| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * oxfw.c - a part of driver for OXFW970/971 based devices | 
|  | 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 "oxfw.h" | 
|  | 9 |  | 
|  | 10 | #define OXFORD_FIRMWARE_ID_ADDRESS	(CSR_REGISTER_BASE + 0x50000) | 
|  | 11 | /* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ | 
|  | 12 |  | 
|  | 13 | #define OXFORD_HARDWARE_ID_ADDRESS	(CSR_REGISTER_BASE + 0x90020) | 
|  | 14 | #define OXFORD_HARDWARE_ID_OXFW970	0x39443841 | 
|  | 15 | #define OXFORD_HARDWARE_ID_OXFW971	0x39373100 | 
|  | 16 |  | 
|  | 17 | #define VENDOR_LOUD		0x000ff2 | 
|  | 18 | #define VENDOR_GRIFFIN		0x001292 | 
|  | 19 | #define VENDOR_BEHRINGER	0x001564 | 
|  | 20 | #define VENDOR_LACIE		0x00d04b | 
|  | 21 | #define VENDOR_TASCAM		0x00022e | 
|  | 22 | #define OUI_STANTON		0x001260 | 
|  | 23 | #define OUI_APOGEE		0x0003db | 
|  | 24 |  | 
|  | 25 | #define MODEL_SATELLITE		0x00200f | 
|  | 26 |  | 
|  | 27 | #define SPECIFIER_1394TA	0x00a02d | 
|  | 28 | #define VERSION_AVC		0x010001 | 
|  | 29 |  | 
|  | 30 | MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver"); | 
|  | 31 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | 
|  | 32 | MODULE_LICENSE("GPL v2"); | 
|  | 33 | MODULE_ALIAS("snd-firewire-speakers"); | 
|  | 34 | MODULE_ALIAS("snd-scs1x"); | 
|  | 35 |  | 
|  | 36 | struct compat_info { | 
|  | 37 | const char *driver_name; | 
|  | 38 | const char *vendor_name; | 
|  | 39 | const char *model_name; | 
|  | 40 | }; | 
|  | 41 |  | 
|  | 42 | static bool detect_loud_models(struct fw_unit *unit) | 
|  | 43 | { | 
|  | 44 | const char *const models[] = { | 
|  | 45 | "Onyxi", | 
|  | 46 | "Onyx-i", | 
|  | 47 | "Onyx 1640i", | 
|  | 48 | "d.Pro", | 
|  | 49 | "Mackie Onyx Satellite", | 
|  | 50 | "Tapco LINK.firewire 4x6", | 
|  | 51 | "U.420"}; | 
|  | 52 | char model[32]; | 
|  | 53 | int err; | 
|  | 54 |  | 
|  | 55 | err = fw_csr_string(unit->directory, CSR_MODEL, | 
|  | 56 | model, sizeof(model)); | 
|  | 57 | if (err < 0) | 
|  | 58 | return false; | 
|  | 59 |  | 
|  | 60 | return match_string(models, ARRAY_SIZE(models), model) >= 0; | 
|  | 61 | } | 
|  | 62 |  | 
|  | 63 | static int name_card(struct snd_oxfw *oxfw) | 
|  | 64 | { | 
|  | 65 | struct fw_device *fw_dev = fw_parent_device(oxfw->unit); | 
|  | 66 | const struct compat_info *info; | 
|  | 67 | char vendor[24]; | 
|  | 68 | char model[32]; | 
|  | 69 | const char *d, *v, *m; | 
|  | 70 | u32 firmware; | 
|  | 71 | int err; | 
|  | 72 |  | 
|  | 73 | /* get vendor name from root directory */ | 
|  | 74 | err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR, | 
|  | 75 | vendor, sizeof(vendor)); | 
|  | 76 | if (err < 0) | 
|  | 77 | goto end; | 
|  | 78 |  | 
|  | 79 | /* get model name from unit directory */ | 
|  | 80 | err = fw_csr_string(oxfw->unit->directory, CSR_MODEL, | 
|  | 81 | model, sizeof(model)); | 
|  | 82 | if (err < 0) | 
|  | 83 | goto end; | 
|  | 84 |  | 
|  | 85 | err = snd_fw_transaction(oxfw->unit, TCODE_READ_QUADLET_REQUEST, | 
|  | 86 | OXFORD_FIRMWARE_ID_ADDRESS, &firmware, 4, 0); | 
|  | 87 | if (err < 0) | 
|  | 88 | goto end; | 
|  | 89 | be32_to_cpus(&firmware); | 
|  | 90 |  | 
|  | 91 | /* to apply card definitions */ | 
|  | 92 | if (oxfw->entry->vendor_id == VENDOR_GRIFFIN || | 
|  | 93 | oxfw->entry->vendor_id == VENDOR_LACIE) { | 
|  | 94 | info = (const struct compat_info *)oxfw->entry->driver_data; | 
|  | 95 | d = info->driver_name; | 
|  | 96 | v = info->vendor_name; | 
|  | 97 | m = info->model_name; | 
|  | 98 | } else { | 
|  | 99 | d = "OXFW"; | 
|  | 100 | v = vendor; | 
|  | 101 | m = model; | 
|  | 102 | } | 
|  | 103 |  | 
|  | 104 | strcpy(oxfw->card->driver, d); | 
|  | 105 | strcpy(oxfw->card->mixername, m); | 
|  | 106 | strcpy(oxfw->card->shortname, m); | 
|  | 107 |  | 
|  | 108 | snprintf(oxfw->card->longname, sizeof(oxfw->card->longname), | 
|  | 109 | "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d", | 
|  | 110 | v, m, firmware >> 20, firmware & 0xffff, | 
|  | 111 | fw_dev->config_rom[3], fw_dev->config_rom[4], | 
|  | 112 | dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed); | 
|  | 113 | end: | 
|  | 114 | return err; | 
|  | 115 | } | 
|  | 116 |  | 
|  | 117 | static void oxfw_free(struct snd_oxfw *oxfw) | 
|  | 118 | { | 
|  | 119 | unsigned int i; | 
|  | 120 |  | 
|  | 121 | snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); | 
|  | 122 | if (oxfw->has_output) | 
|  | 123 | snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); | 
|  | 124 |  | 
|  | 125 | fw_unit_put(oxfw->unit); | 
|  | 126 |  | 
|  | 127 | for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { | 
|  | 128 | kfree(oxfw->tx_stream_formats[i]); | 
|  | 129 | kfree(oxfw->rx_stream_formats[i]); | 
|  | 130 | } | 
|  | 131 |  | 
|  | 132 | kfree(oxfw->spec); | 
|  | 133 | mutex_destroy(&oxfw->mutex); | 
|  | 134 | kfree(oxfw); | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | /* | 
|  | 138 | * This module releases the FireWire unit data after all ALSA character devices | 
|  | 139 | * are released by applications. This is for releasing stream data or finishing | 
|  | 140 | * transactions safely. Thus at returning from .remove(), this module still keep | 
|  | 141 | * references for the unit. | 
|  | 142 | */ | 
|  | 143 | static void oxfw_card_free(struct snd_card *card) | 
|  | 144 | { | 
|  | 145 | oxfw_free(card->private_data); | 
|  | 146 | } | 
|  | 147 |  | 
|  | 148 | static int detect_quirks(struct snd_oxfw *oxfw) | 
|  | 149 | { | 
|  | 150 | struct fw_device *fw_dev = fw_parent_device(oxfw->unit); | 
|  | 151 | struct fw_csr_iterator it; | 
|  | 152 | int key, val; | 
|  | 153 | int vendor, model; | 
|  | 154 |  | 
|  | 155 | /* | 
|  | 156 | * Add ALSA control elements for two models to keep compatibility to | 
|  | 157 | * old firewire-speaker module. | 
|  | 158 | */ | 
|  | 159 | if (oxfw->entry->vendor_id == VENDOR_GRIFFIN) | 
|  | 160 | return snd_oxfw_add_spkr(oxfw, false); | 
|  | 161 | if (oxfw->entry->vendor_id == VENDOR_LACIE) | 
|  | 162 | return snd_oxfw_add_spkr(oxfw, true); | 
|  | 163 |  | 
|  | 164 | /* | 
|  | 165 | * Stanton models supports asynchronous transactions for unique MIDI | 
|  | 166 | * messages. | 
|  | 167 | */ | 
|  | 168 | if (oxfw->entry->vendor_id == OUI_STANTON) { | 
|  | 169 | /* No physical MIDI ports. */ | 
|  | 170 | oxfw->midi_input_ports = 0; | 
|  | 171 | oxfw->midi_output_ports = 0; | 
|  | 172 |  | 
|  | 173 | return snd_oxfw_scs1x_add(oxfw); | 
|  | 174 | } | 
|  | 175 |  | 
|  | 176 | /* | 
|  | 177 | * TASCAM FireOne has physical control and requires a pair of additional | 
|  | 178 | * MIDI ports. | 
|  | 179 | */ | 
|  | 180 | if (oxfw->entry->vendor_id == VENDOR_TASCAM) { | 
|  | 181 | oxfw->midi_input_ports++; | 
|  | 182 | oxfw->midi_output_ports++; | 
|  | 183 | return 0; | 
|  | 184 | } | 
|  | 185 |  | 
|  | 186 | /* Seek from Root Directory of Config ROM. */ | 
|  | 187 | vendor = model = 0; | 
|  | 188 | fw_csr_iterator_init(&it, fw_dev->config_rom + 5); | 
|  | 189 | while (fw_csr_iterator_next(&it, &key, &val)) { | 
|  | 190 | if (key == CSR_VENDOR) | 
|  | 191 | vendor = val; | 
|  | 192 | else if (key == CSR_MODEL) | 
|  | 193 | model = val; | 
|  | 194 | } | 
|  | 195 |  | 
|  | 196 | /* | 
|  | 197 | * Mackie Onyx Satellite with base station has a quirk to report a wrong | 
|  | 198 | * value in 'dbs' field of CIP header against its format information. | 
|  | 199 | */ | 
|  | 200 | if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE) | 
|  | 201 | oxfw->wrong_dbs = true; | 
|  | 202 |  | 
|  | 203 | return 0; | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | static void do_registration(struct work_struct *work) | 
|  | 207 | { | 
|  | 208 | struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work); | 
|  | 209 | int i; | 
|  | 210 | int err; | 
|  | 211 |  | 
|  | 212 | if (oxfw->registered) | 
|  | 213 | return; | 
|  | 214 |  | 
|  | 215 | err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0, | 
|  | 216 | &oxfw->card); | 
|  | 217 | if (err < 0) | 
|  | 218 | return; | 
|  | 219 |  | 
|  | 220 | err = name_card(oxfw); | 
|  | 221 | if (err < 0) | 
|  | 222 | goto error; | 
|  | 223 |  | 
|  | 224 | err = snd_oxfw_stream_discover(oxfw); | 
|  | 225 | if (err < 0) | 
|  | 226 | goto error; | 
|  | 227 |  | 
|  | 228 | err = detect_quirks(oxfw); | 
|  | 229 | if (err < 0) | 
|  | 230 | goto error; | 
|  | 231 |  | 
|  | 232 | err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream); | 
|  | 233 | if (err < 0) | 
|  | 234 | goto error; | 
|  | 235 | if (oxfw->has_output) { | 
|  | 236 | err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream); | 
|  | 237 | if (err < 0) | 
|  | 238 | goto error; | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | err = snd_oxfw_create_pcm(oxfw); | 
|  | 242 | if (err < 0) | 
|  | 243 | goto error; | 
|  | 244 |  | 
|  | 245 | snd_oxfw_proc_init(oxfw); | 
|  | 246 |  | 
|  | 247 | err = snd_oxfw_create_midi(oxfw); | 
|  | 248 | if (err < 0) | 
|  | 249 | goto error; | 
|  | 250 |  | 
|  | 251 | err = snd_oxfw_create_hwdep(oxfw); | 
|  | 252 | if (err < 0) | 
|  | 253 | goto error; | 
|  | 254 |  | 
|  | 255 | err = snd_card_register(oxfw->card); | 
|  | 256 | if (err < 0) | 
|  | 257 | goto error; | 
|  | 258 |  | 
|  | 259 | /* | 
|  | 260 | * After registered, oxfw instance can be released corresponding to | 
|  | 261 | * releasing the sound card instance. | 
|  | 262 | */ | 
|  | 263 | oxfw->card->private_free = oxfw_card_free; | 
|  | 264 | oxfw->card->private_data = oxfw; | 
|  | 265 | oxfw->registered = true; | 
|  | 266 |  | 
|  | 267 | return; | 
|  | 268 | error: | 
|  | 269 | snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); | 
|  | 270 | if (oxfw->has_output) | 
|  | 271 | snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); | 
|  | 272 | for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; ++i) { | 
|  | 273 | kfree(oxfw->tx_stream_formats[i]); | 
|  | 274 | oxfw->tx_stream_formats[i] = NULL; | 
|  | 275 | kfree(oxfw->rx_stream_formats[i]); | 
|  | 276 | oxfw->rx_stream_formats[i] = NULL; | 
|  | 277 | } | 
|  | 278 | snd_card_free(oxfw->card); | 
|  | 279 | kfree(oxfw->spec); | 
|  | 280 | oxfw->spec = NULL; | 
|  | 281 | dev_info(&oxfw->unit->device, | 
|  | 282 | "Sound card registration failed: %d\n", err); | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | static int oxfw_probe(struct fw_unit *unit, | 
|  | 286 | const struct ieee1394_device_id *entry) | 
|  | 287 | { | 
|  | 288 | struct snd_oxfw *oxfw; | 
|  | 289 |  | 
|  | 290 | if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit)) | 
|  | 291 | return -ENODEV; | 
|  | 292 |  | 
|  | 293 | /* Allocate this independent of sound card instance. */ | 
|  | 294 | oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL); | 
|  | 295 | if (oxfw == NULL) | 
|  | 296 | return -ENOMEM; | 
|  | 297 |  | 
|  | 298 | oxfw->entry = entry; | 
|  | 299 | oxfw->unit = fw_unit_get(unit); | 
|  | 300 | dev_set_drvdata(&unit->device, oxfw); | 
|  | 301 |  | 
|  | 302 | mutex_init(&oxfw->mutex); | 
|  | 303 | spin_lock_init(&oxfw->lock); | 
|  | 304 | init_waitqueue_head(&oxfw->hwdep_wait); | 
|  | 305 |  | 
|  | 306 | /* Allocate and register this sound card later. */ | 
|  | 307 | INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration); | 
|  | 308 | snd_fw_schedule_registration(unit, &oxfw->dwork); | 
|  | 309 |  | 
|  | 310 | return 0; | 
|  | 311 | } | 
|  | 312 |  | 
|  | 313 | static void oxfw_bus_reset(struct fw_unit *unit) | 
|  | 314 | { | 
|  | 315 | struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); | 
|  | 316 |  | 
|  | 317 | if (!oxfw->registered) | 
|  | 318 | snd_fw_schedule_registration(unit, &oxfw->dwork); | 
|  | 319 |  | 
|  | 320 | fcp_bus_reset(oxfw->unit); | 
|  | 321 |  | 
|  | 322 | if (oxfw->registered) { | 
|  | 323 | mutex_lock(&oxfw->mutex); | 
|  | 324 |  | 
|  | 325 | snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream); | 
|  | 326 | if (oxfw->has_output) | 
|  | 327 | snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream); | 
|  | 328 |  | 
|  | 329 | mutex_unlock(&oxfw->mutex); | 
|  | 330 |  | 
|  | 331 | if (oxfw->entry->vendor_id == OUI_STANTON) | 
|  | 332 | snd_oxfw_scs1x_update(oxfw); | 
|  | 333 | } | 
|  | 334 | } | 
|  | 335 |  | 
|  | 336 | static void oxfw_remove(struct fw_unit *unit) | 
|  | 337 | { | 
|  | 338 | struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); | 
|  | 339 |  | 
|  | 340 | /* | 
|  | 341 | * Confirm to stop the work for registration before the sound card is | 
|  | 342 | * going to be released. The work is not scheduled again because bus | 
|  | 343 | * reset handler is not called anymore. | 
|  | 344 | */ | 
|  | 345 | cancel_delayed_work_sync(&oxfw->dwork); | 
|  | 346 |  | 
|  | 347 | if (oxfw->registered) { | 
|  | 348 | /* No need to wait for releasing card object in this context. */ | 
|  | 349 | snd_card_free_when_closed(oxfw->card); | 
|  | 350 | } else { | 
|  | 351 | /* Don't forget this case. */ | 
|  | 352 | oxfw_free(oxfw); | 
|  | 353 | } | 
|  | 354 | } | 
|  | 355 |  | 
|  | 356 | static const struct compat_info griffin_firewave = { | 
|  | 357 | .driver_name = "FireWave", | 
|  | 358 | .vendor_name = "Griffin", | 
|  | 359 | .model_name = "FireWave", | 
|  | 360 | }; | 
|  | 361 |  | 
|  | 362 | static const struct compat_info lacie_speakers = { | 
|  | 363 | .driver_name = "FWSpeakers", | 
|  | 364 | .vendor_name = "LaCie", | 
|  | 365 | .model_name = "FireWire Speakers", | 
|  | 366 | }; | 
|  | 367 |  | 
|  | 368 | static const struct ieee1394_device_id oxfw_id_table[] = { | 
|  | 369 | { | 
|  | 370 | .match_flags  = IEEE1394_MATCH_VENDOR_ID | | 
|  | 371 | IEEE1394_MATCH_MODEL_ID | | 
|  | 372 | IEEE1394_MATCH_SPECIFIER_ID | | 
|  | 373 | IEEE1394_MATCH_VERSION, | 
|  | 374 | .vendor_id    = VENDOR_GRIFFIN, | 
|  | 375 | .model_id     = 0x00f970, | 
|  | 376 | .specifier_id = SPECIFIER_1394TA, | 
|  | 377 | .version      = VERSION_AVC, | 
|  | 378 | .driver_data  = (kernel_ulong_t)&griffin_firewave, | 
|  | 379 | }, | 
|  | 380 | { | 
|  | 381 | .match_flags  = IEEE1394_MATCH_VENDOR_ID | | 
|  | 382 | IEEE1394_MATCH_MODEL_ID | | 
|  | 383 | IEEE1394_MATCH_SPECIFIER_ID | | 
|  | 384 | IEEE1394_MATCH_VERSION, | 
|  | 385 | .vendor_id    = VENDOR_LACIE, | 
|  | 386 | .model_id     = 0x00f970, | 
|  | 387 | .specifier_id = SPECIFIER_1394TA, | 
|  | 388 | .version      = VERSION_AVC, | 
|  | 389 | .driver_data  = (kernel_ulong_t)&lacie_speakers, | 
|  | 390 | }, | 
|  | 391 | /* Behringer,F-Control Audio 202 */ | 
|  | 392 | { | 
|  | 393 | .match_flags	= IEEE1394_MATCH_VENDOR_ID | | 
|  | 394 | IEEE1394_MATCH_MODEL_ID, | 
|  | 395 | .vendor_id	= VENDOR_BEHRINGER, | 
|  | 396 | .model_id	= 0x00fc22, | 
|  | 397 | }, | 
|  | 398 | /* | 
|  | 399 | * Any Mackie(Loud) models (name string/model id): | 
|  | 400 | *  Onyx-i series (former models):	0x081216 | 
|  | 401 | *  Mackie Onyx Satellite:		0x00200f | 
|  | 402 | *  Tapco LINK.firewire 4x6:		0x000460 | 
|  | 403 | *  d.2 pro:				Unknown | 
|  | 404 | *  d.4 pro:				Unknown | 
|  | 405 | *  U.420:				Unknown | 
|  | 406 | *  U.420d:				Unknown | 
|  | 407 | */ | 
|  | 408 | { | 
|  | 409 | .match_flags	= IEEE1394_MATCH_VENDOR_ID | | 
|  | 410 | IEEE1394_MATCH_SPECIFIER_ID | | 
|  | 411 | IEEE1394_MATCH_VERSION, | 
|  | 412 | .vendor_id	= VENDOR_LOUD, | 
|  | 413 | .specifier_id	= SPECIFIER_1394TA, | 
|  | 414 | .version	= VERSION_AVC, | 
|  | 415 | }, | 
|  | 416 | /* TASCAM, FireOne */ | 
|  | 417 | { | 
|  | 418 | .match_flags	= IEEE1394_MATCH_VENDOR_ID | | 
|  | 419 | IEEE1394_MATCH_MODEL_ID, | 
|  | 420 | .vendor_id	= VENDOR_TASCAM, | 
|  | 421 | .model_id	= 0x800007, | 
|  | 422 | }, | 
|  | 423 | /* Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m) */ | 
|  | 424 | { | 
|  | 425 | .match_flags	= IEEE1394_MATCH_VENDOR_ID | | 
|  | 426 | IEEE1394_MATCH_MODEL_ID, | 
|  | 427 | .vendor_id	= OUI_STANTON, | 
|  | 428 | .model_id	= 0x001000, | 
|  | 429 | }, | 
|  | 430 | /* Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d) */ | 
|  | 431 | { | 
|  | 432 | .match_flags	= IEEE1394_MATCH_VENDOR_ID | | 
|  | 433 | IEEE1394_MATCH_MODEL_ID, | 
|  | 434 | .vendor_id	= OUI_STANTON, | 
|  | 435 | .model_id	= 0x002000, | 
|  | 436 | }, | 
|  | 437 | // APOGEE, duet FireWire | 
|  | 438 | { | 
|  | 439 | .match_flags	= IEEE1394_MATCH_VENDOR_ID | | 
|  | 440 | IEEE1394_MATCH_MODEL_ID, | 
|  | 441 | .vendor_id	= OUI_APOGEE, | 
|  | 442 | .model_id	= 0x01dddd, | 
|  | 443 | }, | 
|  | 444 | { } | 
|  | 445 | }; | 
|  | 446 | MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table); | 
|  | 447 |  | 
|  | 448 | static struct fw_driver oxfw_driver = { | 
|  | 449 | .driver   = { | 
|  | 450 | .owner	= THIS_MODULE, | 
|  | 451 | .name	= KBUILD_MODNAME, | 
|  | 452 | .bus	= &fw_bus_type, | 
|  | 453 | }, | 
|  | 454 | .probe    = oxfw_probe, | 
|  | 455 | .update   = oxfw_bus_reset, | 
|  | 456 | .remove   = oxfw_remove, | 
|  | 457 | .id_table = oxfw_id_table, | 
|  | 458 | }; | 
|  | 459 |  | 
|  | 460 | static int __init snd_oxfw_init(void) | 
|  | 461 | { | 
|  | 462 | return driver_register(&oxfw_driver.driver); | 
|  | 463 | } | 
|  | 464 |  | 
|  | 465 | static void __exit snd_oxfw_exit(void) | 
|  | 466 | { | 
|  | 467 | driver_unregister(&oxfw_driver.driver); | 
|  | 468 | } | 
|  | 469 |  | 
|  | 470 | module_init(snd_oxfw_init); | 
|  | 471 | module_exit(snd_oxfw_exit); |