blob: 0230605c917e452139377d8f9f8a79ef41de81a0 [file] [log] [blame]
Clemens Ladisch3a691b22011-05-11 10:44:51 +02001/*
2 * Apple iSight audio driver
3 *
4 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
5 * Licensed under the terms of the GNU General Public License, version 2.
6 */
7
8#include <linux/delay.h>
9#include <linux/device.h>
10#include <linux/firewire.h>
11#include <linux/firewire-constants.h>
12#include <linux/module.h>
13#include <linux/mod_devicetable.h>
14#include <linux/mutex.h>
15#include <linux/string.h>
16#include <sound/control.h>
17#include <sound/core.h>
18#include <sound/initval.h>
19#include <sound/pcm.h>
20#include <sound/tlv.h>
21#include "lib.h"
22#include "iso-resources.h"
23#include "packets-buffer.h"
24
25#define OUI_APPLE 0x000a27
26#define MODEL_APPLE_ISIGHT 0x000008
27#define SW_ISIGHT_AUDIO 0x000010
28
29#define REG_AUDIO_ENABLE 0x000
30#define AUDIO_ENABLE 0x80000000
31#define REG_DEF_AUDIO_GAIN 0x204
32#define REG_GAIN_RAW_START 0x210
33#define REG_GAIN_RAW_END 0x214
34#define REG_GAIN_DB_START 0x218
35#define REG_GAIN_DB_END 0x21c
36#define REG_SAMPLE_RATE_INQUIRY 0x280
37#define REG_ISO_TX_CONFIG 0x300
38#define SPEED_SHIFT 16
39#define REG_SAMPLE_RATE 0x400
40#define RATE_48000 0x80000000
41#define REG_GAIN 0x500
42#define REG_MUTE 0x504
43
44#define MAX_FRAMES_PER_PACKET 475
45
46#define QUEUE_LENGTH 20
47
48struct isight {
49 struct snd_card *card;
50 struct fw_unit *unit;
51 struct fw_device *device;
52 u64 audio_base;
53 struct fw_address_handler iris_handler;
54 struct snd_pcm_substream *pcm;
55 struct mutex mutex;
56 struct iso_packets_buffer buffer;
57 struct fw_iso_resources resources;
58 struct fw_iso_context *context;
Clemens Ladisch03c29682011-05-11 10:47:30 +020059 bool pcm_active;
Clemens Ladisch3a691b22011-05-11 10:44:51 +020060 bool pcm_running;
61 bool first_packet;
62 int packet_index;
63 u32 total_samples;
64 unsigned int buffer_pointer;
65 unsigned int period_counter;
66 s32 gain_min, gain_max;
67 unsigned int gain_tlv[4];
68};
69
70struct audio_payload {
71 __be32 sample_count;
72 __be32 signature;
73 __be32 sample_total;
74 __be32 reserved;
75 __be16 samples[2 * MAX_FRAMES_PER_PACKET];
76};
77
78MODULE_DESCRIPTION("iSight audio driver");
79MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
80MODULE_LICENSE("GPL v2");
81
82static struct fw_iso_packet audio_packet = {
83 .payload_length = sizeof(struct audio_payload),
84 .interrupt = 1,
85};
86
87static void isight_update_pointers(struct isight *isight, unsigned int count)
88{
89 struct snd_pcm_runtime *runtime = isight->pcm->runtime;
90 unsigned int ptr;
91
92 smp_wmb(); /* update buffer data before buffer pointer */
93
94 ptr = isight->buffer_pointer;
95 ptr += count;
96 if (ptr >= runtime->buffer_size)
97 ptr -= runtime->buffer_size;
98 ACCESS_ONCE(isight->buffer_pointer) = ptr;
99
100 isight->period_counter += count;
101 if (isight->period_counter >= runtime->period_size) {
102 isight->period_counter -= runtime->period_size;
103 snd_pcm_period_elapsed(isight->pcm);
104 }
105}
106
107static void isight_samples(struct isight *isight,
108 const __be16 *samples, unsigned int count)
109{
110 struct snd_pcm_runtime *runtime;
111 unsigned int count1;
112
113 if (!ACCESS_ONCE(isight->pcm_running))
114 return;
115
116 runtime = isight->pcm->runtime;
117 if (isight->buffer_pointer + count <= runtime->buffer_size) {
118 memcpy(runtime->dma_area + isight->buffer_pointer * 4,
119 samples, count * 4);
120 } else {
121 count1 = runtime->buffer_size - isight->buffer_pointer;
122 memcpy(runtime->dma_area + isight->buffer_pointer * 4,
123 samples, count1 * 4);
124 samples += count1 * 2;
125 memcpy(runtime->dma_area, samples, (count - count1) * 4);
126 }
127
128 isight_update_pointers(isight, count);
129}
130
131static void isight_pcm_abort(struct isight *isight)
132{
133 unsigned long flags;
134
Clemens Ladisch03c29682011-05-11 10:47:30 +0200135 if (ACCESS_ONCE(isight->pcm_active)) {
136 snd_pcm_stream_lock_irqsave(isight->pcm, flags);
137 if (snd_pcm_running(isight->pcm))
138 snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN);
139 snd_pcm_stream_unlock_irqrestore(isight->pcm, flags);
140 }
Clemens Ladisch3a691b22011-05-11 10:44:51 +0200141}
142
143static void isight_dropped_samples(struct isight *isight, unsigned int total)
144{
145 struct snd_pcm_runtime *runtime;
146 u32 dropped;
147 unsigned int count1;
148
149 if (!ACCESS_ONCE(isight->pcm_running))
150 return;
151
152 runtime = isight->pcm->runtime;
153 dropped = total - isight->total_samples;
154 if (dropped < runtime->buffer_size) {
155 if (isight->buffer_pointer + dropped <= runtime->buffer_size) {
156 memset(runtime->dma_area + isight->buffer_pointer * 4,
157 0, dropped * 4);
158 } else {
159 count1 = runtime->buffer_size - isight->buffer_pointer;
160 memset(runtime->dma_area + isight->buffer_pointer * 4,
161 0, count1 * 4);
162 memset(runtime->dma_area, 0, (dropped - count1) * 4);
163 }
164 isight_update_pointers(isight, dropped);
165 } else {
166 isight_pcm_abort(isight);
167 }
168}
169
170static void isight_packet(struct fw_iso_context *context, u32 cycle,
171 size_t header_length, void *header, void *data)
172{
173 struct isight *isight = data;
174 const struct audio_payload *payload;
175 unsigned int index, length, count, total;
176 int err;
177
178 if (isight->packet_index < 0)
179 return;
180 index = isight->packet_index;
181 payload = isight->buffer.packets[index].buffer;
182 length = be32_to_cpup(header) >> 16;
183
184 if (likely(length >= 16 &&
185 payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) {
186 count = be32_to_cpu(payload->sample_count);
187 if (likely(count <= (length - 16) / 4)) {
188 total = be32_to_cpu(payload->sample_total);
189 if (unlikely(total != isight->total_samples)) {
190 if (!isight->first_packet)
191 isight_dropped_samples(isight, total);
192 isight->first_packet = false;
193 isight->total_samples = total;
194 }
195
196 isight_samples(isight, payload->samples, count);
197 isight->total_samples += count;
198 }
199 }
200
201 if (++index >= QUEUE_LENGTH)
202 index = 0;
203
204 err = fw_iso_context_queue(isight->context, &audio_packet,
205 &isight->buffer.iso_buffer,
206 isight->buffer.packets[index].offset);
207 if (err < 0) {
208 dev_err(&isight->unit->device, "queueing error: %d\n", err);
209 isight_pcm_abort(isight);
210 isight->packet_index = -1;
211 return;
212 }
213
214 isight->packet_index = index;
215}
216
217static int isight_connect(struct isight *isight)
218{
219 int ch, err, rcode, errors = 0;
220 __be32 value;
221
222retry_after_bus_reset:
223 ch = fw_iso_resources_allocate(&isight->resources,
224 sizeof(struct audio_payload),
225 isight->device->max_speed);
226 if (ch < 0) {
227 err = ch;
228 goto error;
229 }
230
231 value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT));
232 for (;;) {
233 rcode = fw_run_transaction(
234 isight->device->card,
235 TCODE_WRITE_QUADLET_REQUEST,
236 isight->device->node_id,
237 isight->resources.generation,
238 isight->device->max_speed,
239 isight->audio_base + REG_ISO_TX_CONFIG,
240 &value, 4);
241 if (rcode == RCODE_COMPLETE) {
242 return 0;
243 } else if (rcode == RCODE_GENERATION) {
244 fw_iso_resources_free(&isight->resources);
245 goto retry_after_bus_reset;
246 } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
247 err = -EIO;
248 goto err_resources;
249 }
250 msleep(5);
251 }
252
253err_resources:
254 fw_iso_resources_free(&isight->resources);
255error:
256 return err;
257}
258
259static int isight_open(struct snd_pcm_substream *substream)
260{
261 static const struct snd_pcm_hardware hardware = {
262 .info = SNDRV_PCM_INFO_MMAP |
263 SNDRV_PCM_INFO_MMAP_VALID |
264 SNDRV_PCM_INFO_BATCH |
265 SNDRV_PCM_INFO_INTERLEAVED |
266 SNDRV_PCM_INFO_BLOCK_TRANSFER,
267 .formats = SNDRV_PCM_FMTBIT_S16_BE,
268 .rates = SNDRV_PCM_RATE_48000,
269 .rate_min = 48000,
270 .rate_max = 48000,
271 .channels_min = 2,
272 .channels_max = 2,
273 .buffer_bytes_max = 4 * 1024 * 1024,
274 .period_bytes_min = MAX_FRAMES_PER_PACKET * 4,
275 .period_bytes_max = 1024 * 1024,
276 .periods_min = 2,
277 .periods_max = UINT_MAX,
278 };
279 struct isight *isight = substream->private_data;
280
281 substream->runtime->hw = hardware;
282
283 return iso_packets_buffer_init(&isight->buffer, isight->unit,
284 QUEUE_LENGTH,
285 sizeof(struct audio_payload),
286 DMA_FROM_DEVICE);
287}
288
289static int isight_close(struct snd_pcm_substream *substream)
290{
291 struct isight *isight = substream->private_data;
292
293 iso_packets_buffer_destroy(&isight->buffer, isight->unit);
294
295 return 0;
296}
297
298static int isight_hw_params(struct snd_pcm_substream *substream,
299 struct snd_pcm_hw_params *hw_params)
300{
Clemens Ladisch03c29682011-05-11 10:47:30 +0200301 struct isight *isight = substream->private_data;
302 int err;
303
304 err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
305 params_buffer_bytes(hw_params));
306 if (err < 0)
307 return err;
308
309 ACCESS_ONCE(isight->pcm_active) = true;
310
311 return 0;
Clemens Ladisch3a691b22011-05-11 10:44:51 +0200312}
313
314static void isight_stop_streaming(struct isight *isight)
315{
316 __be32 value;
317
318 if (!isight->context)
319 return;
320
321 fw_iso_context_stop(isight->context);
322 fw_iso_context_destroy(isight->context);
323 isight->context = NULL;
324
325 value = 0;
326 snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
327 isight->audio_base + REG_AUDIO_ENABLE,
328 &value, 4);
329
330 fw_iso_resources_free(&isight->resources);
331}
332
333static int isight_hw_free(struct snd_pcm_substream *substream)
334{
335 struct isight *isight = substream->private_data;
336
Clemens Ladisch03c29682011-05-11 10:47:30 +0200337 ACCESS_ONCE(isight->pcm_active) = false;
338
Clemens Ladisch3a691b22011-05-11 10:44:51 +0200339 mutex_lock(&isight->mutex);
340 isight_stop_streaming(isight);
341 mutex_unlock(&isight->mutex);
342
343 return snd_pcm_lib_free_vmalloc_buffer(substream);
344}
345
346static int isight_start_streaming(struct isight *isight)
347{
348 __be32 sample_rate;
349 unsigned int i;
350 int err;
351
352 if (isight->context) {
353 if (isight->packet_index < 0)
354 isight_stop_streaming(isight);
355 else
356 return 0;
357 }
358
359 sample_rate = cpu_to_be32(RATE_48000);
360 err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
361 isight->audio_base + REG_SAMPLE_RATE,
362 &sample_rate, 4);
363 if (err < 0)
364 return err;
365
366 err = isight_connect(isight);
367 if (err < 0)
368 goto error;
369
370 isight->context = fw_iso_context_create(isight->device->card,
371 FW_ISO_CONTEXT_RECEIVE,
372 isight->resources.channel,
373 isight->device->max_speed,
374 4, isight_packet, isight);
375 if (IS_ERR(isight->context)) {
376 err = PTR_ERR(isight->context);
377 isight->context = NULL;
378 goto err_resources;
379 }
380
381 for (i = 0; i < QUEUE_LENGTH; ++i) {
382 err = fw_iso_context_queue(isight->context, &audio_packet,
383 &isight->buffer.iso_buffer,
384 isight->buffer.packets[i].offset);
385 if (err < 0)
386 goto err_context;
387 }
388
389 isight->first_packet = true;
390 isight->packet_index = 0;
391
392 err = fw_iso_context_start(isight->context, -1, 0,
393 FW_ISO_CONTEXT_MATCH_ALL_TAGS/*?*/);
394 if (err < 0)
395 goto err_context;
396
397 return 0;
398
399err_context:
400 fw_iso_context_destroy(isight->context);
401 isight->context = NULL;
402err_resources:
403 fw_iso_resources_free(&isight->resources);
404error:
405 return err;
406}
407
408static int isight_prepare(struct snd_pcm_substream *substream)
409{
410 struct isight *isight = substream->private_data;
411 int err;
412
413 isight->buffer_pointer = 0;
414 isight->period_counter = 0;
415
416 mutex_lock(&isight->mutex);
417 err = isight_start_streaming(isight);
418 mutex_unlock(&isight->mutex);
419
420 return err;
421}
422
423static int isight_trigger(struct snd_pcm_substream *substream, int cmd)
424{
425 struct isight *isight = substream->private_data;
426
427 switch (cmd) {
428 case SNDRV_PCM_TRIGGER_START:
429 ACCESS_ONCE(isight->pcm_running) = true;
430 break;
431 case SNDRV_PCM_TRIGGER_STOP:
432 ACCESS_ONCE(isight->pcm_running) = false;
433 break;
434 default:
435 return -EINVAL;
436 }
437 return 0;
438}
439
440static snd_pcm_uframes_t isight_pointer(struct snd_pcm_substream *substream)
441{
442 struct isight *isight = substream->private_data;
443
444 return ACCESS_ONCE(isight->buffer_pointer);
445}
446
447static int isight_create_pcm(struct isight *isight)
448{
449 static struct snd_pcm_ops ops = {
450 .open = isight_open,
451 .close = isight_close,
452 .ioctl = snd_pcm_lib_ioctl,
453 .hw_params = isight_hw_params,
454 .hw_free = isight_hw_free,
455 .prepare = isight_prepare,
456 .trigger = isight_trigger,
457 .pointer = isight_pointer,
458 .page = snd_pcm_lib_get_vmalloc_page,
459 .mmap = snd_pcm_lib_mmap_vmalloc,
460 };
461 struct snd_pcm *pcm;
462 int err;
463
464 err = snd_pcm_new(isight->card, "iSight", 0, 0, 1, &pcm);
465 if (err < 0)
466 return err;
467 pcm->private_data = isight;
468 strcpy(pcm->name, "iSight");
469 isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
470 isight->pcm->ops = &ops;
471
472 return 0;
473}
474
475static int isight_gain_info(struct snd_kcontrol *ctl,
476 struct snd_ctl_elem_info *info)
477{
478 struct isight *isight = ctl->private_data;
479
480 info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
481 info->count = 1;
482 info->value.integer.min = isight->gain_min;
483 info->value.integer.max = isight->gain_max;
484
485 return 0;
486}
487
488static int isight_gain_get(struct snd_kcontrol *ctl,
489 struct snd_ctl_elem_value *value)
490{
491 struct isight *isight = ctl->private_data;
492 __be32 gain;
493 int err;
494
495 err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
496 isight->audio_base + REG_GAIN, &gain, 4);
497 if (err < 0)
498 return err;
499
500 value->value.integer.value[0] = (s32)be32_to_cpu(gain);
501
502 return 0;
503}
504
505static int isight_gain_put(struct snd_kcontrol *ctl,
506 struct snd_ctl_elem_value *value)
507{
508 struct isight *isight = ctl->private_data;
509 __be32 gain;
510
511 if (value->value.integer.value[0] < isight->gain_min ||
512 value->value.integer.value[0] > isight->gain_max)
513 return -EINVAL;
514
515 gain = cpu_to_be32(value->value.integer.value[0]);
516 return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
517 isight->audio_base + REG_GAIN, &gain, 4);
518}
519
520static int isight_mute_get(struct snd_kcontrol *ctl,
521 struct snd_ctl_elem_value *value)
522{
523 struct isight *isight = ctl->private_data;
524 __be32 mute;
525 int err;
526
527 err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
528 isight->audio_base + REG_MUTE, &mute, 4);
529 if (err < 0)
530 return err;
531
532 value->value.integer.value[0] = !mute;
533
534 return 0;
535}
536
537static int isight_mute_put(struct snd_kcontrol *ctl,
538 struct snd_ctl_elem_value *value)
539{
540 struct isight *isight = ctl->private_data;
541 __be32 mute;
542
543 mute = (__force __be32)!value->value.integer.value[0];
544 return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST,
545 isight->audio_base + REG_MUTE, &mute, 4);
546}
547
548static int isight_create_mixer(struct isight *isight)
549{
550 static const struct snd_kcontrol_new gain_control = {
551 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
552 .name = "Mic Capture Volume",
553 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
554 SNDRV_CTL_ELEM_ACCESS_TLV_READ,
555 .info = isight_gain_info,
556 .get = isight_gain_get,
557 .put = isight_gain_put,
558 };
559 static const struct snd_kcontrol_new mute_control = {
560 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
561 .name = "Mic Capture Switch",
562 .info = snd_ctl_boolean_mono_info,
563 .get = isight_mute_get,
564 .put = isight_mute_put,
565 };
566 __be32 value;
567 struct snd_kcontrol *ctl;
568 int err;
569
570 err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
571 isight->audio_base + REG_GAIN_RAW_START,
572 &value, 4);
573 if (err < 0)
574 return err;
575 isight->gain_min = be32_to_cpu(value);
576
577 err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
578 isight->audio_base + REG_GAIN_RAW_END,
579 &value, 4);
580 if (err < 0)
581 return err;
582 isight->gain_max = be32_to_cpu(value);
583
584 isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX;
585 isight->gain_tlv[1] = 2 * sizeof(unsigned int);
586 err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
587 isight->audio_base + REG_GAIN_DB_START,
588 &value, 4);
589 if (err < 0)
590 return err;
591 isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100;
592 err = snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST,
593 isight->audio_base + REG_GAIN_DB_END,
594 &value, 4);
595 if (err < 0)
596 return err;
597 isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100;
598
599 ctl = snd_ctl_new1(&gain_control, isight);
600 if (ctl)
601 ctl->tlv.p = isight->gain_tlv;
602 err = snd_ctl_add(isight->card, ctl);
603 if (err < 0)
604 return err;
605
606 err = snd_ctl_add(isight->card, snd_ctl_new1(&mute_control, isight));
607 if (err < 0)
608 return err;
609
610 return 0;
611}
612
613static void isight_card_free(struct snd_card *card)
614{
615 struct isight *isight = card->private_data;
616
617 fw_iso_resources_destroy(&isight->resources);
618 fw_unit_put(isight->unit);
619 fw_device_put(isight->device);
620 mutex_destroy(&isight->mutex);
621}
622
623static u64 get_unit_base(struct fw_unit *unit)
624{
625 struct fw_csr_iterator i;
626 int key, value;
627
628 fw_csr_iterator_init(&i, unit->directory);
629 while (fw_csr_iterator_next(&i, &key, &value))
630 if (key == CSR_OFFSET)
631 return CSR_REGISTER_BASE + value * 4;
632 return 0;
633}
634
635static int isight_probe(struct device *unit_dev)
636{
637 struct fw_unit *unit = fw_unit(unit_dev);
638 struct fw_device *fw_dev = fw_parent_device(unit);
639 struct snd_card *card;
640 struct isight *isight;
641 int err;
642
643 err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*isight), &card);
644 if (err < 0)
645 return err;
646 snd_card_set_dev(card, unit_dev);
647
648 isight = card->private_data;
649 isight->card = card;
650 mutex_init(&isight->mutex);
651 isight->unit = fw_unit_get(unit);
652 isight->device = fw_device_get(fw_dev);
653 isight->audio_base = get_unit_base(unit);
654 if (!isight->audio_base) {
655 dev_err(&unit->device, "audio unit base not found\n");
656 err = -ENXIO;
657 goto err_unit;
658 }
659 fw_iso_resources_init(&isight->resources, unit);
660
661 card->private_free = isight_card_free;
662
663 strcpy(card->driver, "iSight");
664 strcpy(card->shortname, "Apple iSight");
665 snprintf(card->longname, sizeof(card->longname),
666 "Apple iSight (GUID %08x%08x) at %s, S%d",
667 fw_dev->config_rom[3], fw_dev->config_rom[4],
668 dev_name(&unit->device), 100 << fw_dev->max_speed);
669 strcpy(card->mixername, "iSight");
670
671 err = isight_create_pcm(isight);
672 if (err < 0)
673 goto error;
674
675 err = isight_create_mixer(isight);
676 if (err < 0)
677 goto error;
678
679 err = snd_card_register(card);
680 if (err < 0)
681 goto error;
682
683 dev_set_drvdata(unit_dev, isight);
684
685 return 0;
686
687err_unit:
688 fw_unit_put(isight->unit);
689 fw_device_put(isight->device);
690 mutex_destroy(&isight->mutex);
691error:
692 snd_card_free(card);
693 return err;
694}
695
696static int isight_remove(struct device *dev)
697{
698 struct isight *isight = dev_get_drvdata(dev);
699
700 snd_card_disconnect(isight->card);
701
702 mutex_lock(&isight->mutex);
703 isight_pcm_abort(isight);
704 isight_stop_streaming(isight);
705 mutex_unlock(&isight->mutex);
706
707 snd_card_free_when_closed(isight->card);
708
709 return 0;
710}
711
712static void isight_bus_reset(struct fw_unit *unit)
713{
714 struct isight *isight = dev_get_drvdata(&unit->device);
715
716 mutex_lock(&isight->mutex);
717 if (fw_iso_resources_update(&isight->resources) < 0) {
718 isight_pcm_abort(isight);
719 isight_stop_streaming(isight);
720 }
721 mutex_unlock(&isight->mutex);
722}
723
724static const struct ieee1394_device_id isight_id_table[] = {
725 {
726 .match_flags = IEEE1394_MATCH_SPECIFIER_ID |
727 IEEE1394_MATCH_VERSION,
728 .specifier_id = OUI_APPLE,
729 .version = SW_ISIGHT_AUDIO,
730 },
731 { }
732};
733MODULE_DEVICE_TABLE(ieee1394, isight_id_table);
734
735static struct fw_driver isight_driver = {
736 .driver = {
737 .owner = THIS_MODULE,
738 .name = KBUILD_MODNAME,
739 .bus = &fw_bus_type,
740 .probe = isight_probe,
741 .remove = isight_remove,
742 },
743 .update = isight_bus_reset,
744 .id_table = isight_id_table,
745};
746
747static int __init alsa_isight_init(void)
748{
749 return driver_register(&isight_driver.driver);
750}
751
752static void __exit alsa_isight_exit(void)
753{
754 driver_unregister(&isight_driver.driver);
755}
756
757module_init(alsa_isight_init);
758module_exit(alsa_isight_exit);