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