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