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