18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * oxfw.c - a part of driver for OXFW970/971 based devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "oxfw.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define OXFORD_FIRMWARE_ID_ADDRESS	(CSR_REGISTER_BASE + 0x50000)
118c2ecf20Sopenharmony_ci/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define OXFORD_HARDWARE_ID_ADDRESS	(CSR_REGISTER_BASE + 0x90020)
148c2ecf20Sopenharmony_ci#define OXFORD_HARDWARE_ID_OXFW970	0x39443841
158c2ecf20Sopenharmony_ci#define OXFORD_HARDWARE_ID_OXFW971	0x39373100
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define VENDOR_LOUD		0x000ff2
188c2ecf20Sopenharmony_ci#define VENDOR_GRIFFIN		0x001292
198c2ecf20Sopenharmony_ci#define VENDOR_BEHRINGER	0x001564
208c2ecf20Sopenharmony_ci#define VENDOR_LACIE		0x00d04b
218c2ecf20Sopenharmony_ci#define VENDOR_TASCAM		0x00022e
228c2ecf20Sopenharmony_ci#define OUI_STANTON		0x001260
238c2ecf20Sopenharmony_ci#define OUI_APOGEE		0x0003db
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define MODEL_SATELLITE		0x00200f
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define SPECIFIER_1394TA	0x00a02d
288c2ecf20Sopenharmony_ci#define VERSION_AVC		0x010001
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver");
318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
338c2ecf20Sopenharmony_ciMODULE_ALIAS("snd-firewire-speakers");
348c2ecf20Sopenharmony_ciMODULE_ALIAS("snd-scs1x");
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct compat_info {
378c2ecf20Sopenharmony_ci	const char *driver_name;
388c2ecf20Sopenharmony_ci	const char *vendor_name;
398c2ecf20Sopenharmony_ci	const char *model_name;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic bool detect_loud_models(struct fw_unit *unit)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	const char *const models[] = {
458c2ecf20Sopenharmony_ci		"Onyxi",
468c2ecf20Sopenharmony_ci		"Onyx-i",
478c2ecf20Sopenharmony_ci		"Onyx 1640i",
488c2ecf20Sopenharmony_ci		"d.Pro",
498c2ecf20Sopenharmony_ci		"Mackie Onyx Satellite",
508c2ecf20Sopenharmony_ci		"Tapco LINK.firewire 4x6",
518c2ecf20Sopenharmony_ci		"U.420"};
528c2ecf20Sopenharmony_ci	char model[32];
538c2ecf20Sopenharmony_ci	int err;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	err = fw_csr_string(unit->directory, CSR_MODEL,
568c2ecf20Sopenharmony_ci			    model, sizeof(model));
578c2ecf20Sopenharmony_ci	if (err < 0)
588c2ecf20Sopenharmony_ci		return false;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return match_string(models, ARRAY_SIZE(models), model) >= 0;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int name_card(struct snd_oxfw *oxfw)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
668c2ecf20Sopenharmony_ci	const struct compat_info *info;
678c2ecf20Sopenharmony_ci	char vendor[24];
688c2ecf20Sopenharmony_ci	char model[32];
698c2ecf20Sopenharmony_ci	const char *d, *v, *m;
708c2ecf20Sopenharmony_ci	u32 firmware;
718c2ecf20Sopenharmony_ci	int err;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* get vendor name from root directory */
748c2ecf20Sopenharmony_ci	err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
758c2ecf20Sopenharmony_ci			    vendor, sizeof(vendor));
768c2ecf20Sopenharmony_ci	if (err < 0)
778c2ecf20Sopenharmony_ci		goto end;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* get model name from unit directory */
808c2ecf20Sopenharmony_ci	err = fw_csr_string(oxfw->unit->directory, CSR_MODEL,
818c2ecf20Sopenharmony_ci			    model, sizeof(model));
828c2ecf20Sopenharmony_ci	if (err < 0)
838c2ecf20Sopenharmony_ci		goto end;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	err = snd_fw_transaction(oxfw->unit, TCODE_READ_QUADLET_REQUEST,
868c2ecf20Sopenharmony_ci				 OXFORD_FIRMWARE_ID_ADDRESS, &firmware, 4, 0);
878c2ecf20Sopenharmony_ci	if (err < 0)
888c2ecf20Sopenharmony_ci		goto end;
898c2ecf20Sopenharmony_ci	be32_to_cpus(&firmware);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* to apply card definitions */
928c2ecf20Sopenharmony_ci	if (oxfw->entry->vendor_id == VENDOR_GRIFFIN ||
938c2ecf20Sopenharmony_ci	    oxfw->entry->vendor_id == VENDOR_LACIE) {
948c2ecf20Sopenharmony_ci		info = (const struct compat_info *)oxfw->entry->driver_data;
958c2ecf20Sopenharmony_ci		d = info->driver_name;
968c2ecf20Sopenharmony_ci		v = info->vendor_name;
978c2ecf20Sopenharmony_ci		m = info->model_name;
988c2ecf20Sopenharmony_ci	} else {
998c2ecf20Sopenharmony_ci		d = "OXFW";
1008c2ecf20Sopenharmony_ci		v = vendor;
1018c2ecf20Sopenharmony_ci		m = model;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	strcpy(oxfw->card->driver, d);
1058c2ecf20Sopenharmony_ci	strcpy(oxfw->card->mixername, m);
1068c2ecf20Sopenharmony_ci	strcpy(oxfw->card->shortname, m);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	snprintf(oxfw->card->longname, sizeof(oxfw->card->longname),
1098c2ecf20Sopenharmony_ci		 "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d",
1108c2ecf20Sopenharmony_ci		 v, m, firmware >> 20, firmware & 0xffff,
1118c2ecf20Sopenharmony_ci		 fw_dev->config_rom[3], fw_dev->config_rom[4],
1128c2ecf20Sopenharmony_ci		 dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed);
1138c2ecf20Sopenharmony_ciend:
1148c2ecf20Sopenharmony_ci	return err;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic void oxfw_card_free(struct snd_card *card)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct snd_oxfw *oxfw = card->private_data;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (oxfw->has_output || oxfw->has_input)
1228c2ecf20Sopenharmony_ci		snd_oxfw_stream_destroy_duplex(oxfw);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int detect_quirks(struct snd_oxfw *oxfw)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
1288c2ecf20Sopenharmony_ci	struct fw_csr_iterator it;
1298c2ecf20Sopenharmony_ci	int key, val;
1308c2ecf20Sopenharmony_ci	int vendor, model;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/*
1338c2ecf20Sopenharmony_ci	 * Add ALSA control elements for two models to keep compatibility to
1348c2ecf20Sopenharmony_ci	 * old firewire-speaker module.
1358c2ecf20Sopenharmony_ci	 */
1368c2ecf20Sopenharmony_ci	if (oxfw->entry->vendor_id == VENDOR_GRIFFIN)
1378c2ecf20Sopenharmony_ci		return snd_oxfw_add_spkr(oxfw, false);
1388c2ecf20Sopenharmony_ci	if (oxfw->entry->vendor_id == VENDOR_LACIE)
1398c2ecf20Sopenharmony_ci		return snd_oxfw_add_spkr(oxfw, true);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/*
1428c2ecf20Sopenharmony_ci	 * Stanton models supports asynchronous transactions for unique MIDI
1438c2ecf20Sopenharmony_ci	 * messages.
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	if (oxfw->entry->vendor_id == OUI_STANTON) {
1468c2ecf20Sopenharmony_ci		/* No physical MIDI ports. */
1478c2ecf20Sopenharmony_ci		oxfw->midi_input_ports = 0;
1488c2ecf20Sopenharmony_ci		oxfw->midi_output_ports = 0;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci		return snd_oxfw_scs1x_add(oxfw);
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/*
1548c2ecf20Sopenharmony_ci	 * TASCAM FireOne has physical control and requires a pair of additional
1558c2ecf20Sopenharmony_ci	 * MIDI ports.
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci	if (oxfw->entry->vendor_id == VENDOR_TASCAM) {
1588c2ecf20Sopenharmony_ci		oxfw->midi_input_ports++;
1598c2ecf20Sopenharmony_ci		oxfw->midi_output_ports++;
1608c2ecf20Sopenharmony_ci		return 0;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Seek from Root Directory of Config ROM. */
1648c2ecf20Sopenharmony_ci	vendor = model = 0;
1658c2ecf20Sopenharmony_ci	fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
1668c2ecf20Sopenharmony_ci	while (fw_csr_iterator_next(&it, &key, &val)) {
1678c2ecf20Sopenharmony_ci		if (key == CSR_VENDOR)
1688c2ecf20Sopenharmony_ci			vendor = val;
1698c2ecf20Sopenharmony_ci		else if (key == CSR_MODEL)
1708c2ecf20Sopenharmony_ci			model = val;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/*
1748c2ecf20Sopenharmony_ci	 * Mackie Onyx Satellite with base station has a quirk to report a wrong
1758c2ecf20Sopenharmony_ci	 * value in 'dbs' field of CIP header against its format information.
1768c2ecf20Sopenharmony_ci	 */
1778c2ecf20Sopenharmony_ci	if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
1788c2ecf20Sopenharmony_ci		oxfw->wrong_dbs = true;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void do_registration(struct work_struct *work)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
1868c2ecf20Sopenharmony_ci	int err;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (oxfw->registered)
1898c2ecf20Sopenharmony_ci		return;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
1928c2ecf20Sopenharmony_ci			   &oxfw->card);
1938c2ecf20Sopenharmony_ci	if (err < 0)
1948c2ecf20Sopenharmony_ci		return;
1958c2ecf20Sopenharmony_ci	oxfw->card->private_free = oxfw_card_free;
1968c2ecf20Sopenharmony_ci	oxfw->card->private_data = oxfw;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	err = name_card(oxfw);
1998c2ecf20Sopenharmony_ci	if (err < 0)
2008c2ecf20Sopenharmony_ci		goto error;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	err = snd_oxfw_stream_discover(oxfw);
2038c2ecf20Sopenharmony_ci	if (err < 0)
2048c2ecf20Sopenharmony_ci		goto error;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	err = detect_quirks(oxfw);
2078c2ecf20Sopenharmony_ci	if (err < 0)
2088c2ecf20Sopenharmony_ci		goto error;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (oxfw->has_output || oxfw->has_input) {
2118c2ecf20Sopenharmony_ci		err = snd_oxfw_stream_init_duplex(oxfw);
2128c2ecf20Sopenharmony_ci		if (err < 0)
2138c2ecf20Sopenharmony_ci			goto error;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		err = snd_oxfw_create_pcm(oxfw);
2168c2ecf20Sopenharmony_ci		if (err < 0)
2178c2ecf20Sopenharmony_ci			goto error;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		snd_oxfw_proc_init(oxfw);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		err = snd_oxfw_create_midi(oxfw);
2228c2ecf20Sopenharmony_ci		if (err < 0)
2238c2ecf20Sopenharmony_ci			goto error;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		err = snd_oxfw_create_hwdep(oxfw);
2268c2ecf20Sopenharmony_ci		if (err < 0)
2278c2ecf20Sopenharmony_ci			goto error;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	err = snd_card_register(oxfw->card);
2318c2ecf20Sopenharmony_ci	if (err < 0)
2328c2ecf20Sopenharmony_ci		goto error;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	oxfw->registered = true;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return;
2378c2ecf20Sopenharmony_cierror:
2388c2ecf20Sopenharmony_ci	snd_card_free(oxfw->card);
2398c2ecf20Sopenharmony_ci	dev_info(&oxfw->unit->device,
2408c2ecf20Sopenharmony_ci		 "Sound card registration failed: %d\n", err);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int oxfw_probe(struct fw_unit *unit,
2448c2ecf20Sopenharmony_ci		      const struct ieee1394_device_id *entry)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct snd_oxfw *oxfw;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
2498c2ecf20Sopenharmony_ci		return -ENODEV;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* Allocate this independent of sound card instance. */
2528c2ecf20Sopenharmony_ci	oxfw = devm_kzalloc(&unit->device, sizeof(struct snd_oxfw), GFP_KERNEL);
2538c2ecf20Sopenharmony_ci	if (!oxfw)
2548c2ecf20Sopenharmony_ci		return -ENOMEM;
2558c2ecf20Sopenharmony_ci	oxfw->unit = fw_unit_get(unit);
2568c2ecf20Sopenharmony_ci	dev_set_drvdata(&unit->device, oxfw);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	oxfw->entry = entry;
2598c2ecf20Sopenharmony_ci	mutex_init(&oxfw->mutex);
2608c2ecf20Sopenharmony_ci	spin_lock_init(&oxfw->lock);
2618c2ecf20Sopenharmony_ci	init_waitqueue_head(&oxfw->hwdep_wait);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* Allocate and register this sound card later. */
2648c2ecf20Sopenharmony_ci	INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
2658c2ecf20Sopenharmony_ci	snd_fw_schedule_registration(unit, &oxfw->dwork);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return 0;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic void oxfw_bus_reset(struct fw_unit *unit)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (!oxfw->registered)
2758c2ecf20Sopenharmony_ci		snd_fw_schedule_registration(unit, &oxfw->dwork);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	fcp_bus_reset(oxfw->unit);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (oxfw->registered) {
2808c2ecf20Sopenharmony_ci		if (oxfw->has_output || oxfw->has_input) {
2818c2ecf20Sopenharmony_ci			mutex_lock(&oxfw->mutex);
2828c2ecf20Sopenharmony_ci			snd_oxfw_stream_update_duplex(oxfw);
2838c2ecf20Sopenharmony_ci			mutex_unlock(&oxfw->mutex);
2848c2ecf20Sopenharmony_ci		}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		if (oxfw->entry->vendor_id == OUI_STANTON)
2878c2ecf20Sopenharmony_ci			snd_oxfw_scs1x_update(oxfw);
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void oxfw_remove(struct fw_unit *unit)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/*
2968c2ecf20Sopenharmony_ci	 * Confirm to stop the work for registration before the sound card is
2978c2ecf20Sopenharmony_ci	 * going to be released. The work is not scheduled again because bus
2988c2ecf20Sopenharmony_ci	 * reset handler is not called anymore.
2998c2ecf20Sopenharmony_ci	 */
3008c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&oxfw->dwork);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (oxfw->registered) {
3038c2ecf20Sopenharmony_ci		// Block till all of ALSA character devices are released.
3048c2ecf20Sopenharmony_ci		snd_card_free(oxfw->card);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	mutex_destroy(&oxfw->mutex);
3088c2ecf20Sopenharmony_ci	fw_unit_put(oxfw->unit);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic const struct compat_info griffin_firewave = {
3128c2ecf20Sopenharmony_ci	.driver_name = "FireWave",
3138c2ecf20Sopenharmony_ci	.vendor_name = "Griffin",
3148c2ecf20Sopenharmony_ci	.model_name = "FireWave",
3158c2ecf20Sopenharmony_ci};
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic const struct compat_info lacie_speakers = {
3188c2ecf20Sopenharmony_ci	.driver_name = "FWSpeakers",
3198c2ecf20Sopenharmony_ci	.vendor_name = "LaCie",
3208c2ecf20Sopenharmony_ci	.model_name = "FireWire Speakers",
3218c2ecf20Sopenharmony_ci};
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic const struct ieee1394_device_id oxfw_id_table[] = {
3248c2ecf20Sopenharmony_ci	{
3258c2ecf20Sopenharmony_ci		.match_flags  = IEEE1394_MATCH_VENDOR_ID |
3268c2ecf20Sopenharmony_ci				IEEE1394_MATCH_MODEL_ID |
3278c2ecf20Sopenharmony_ci				IEEE1394_MATCH_SPECIFIER_ID |
3288c2ecf20Sopenharmony_ci				IEEE1394_MATCH_VERSION,
3298c2ecf20Sopenharmony_ci		.vendor_id    = VENDOR_GRIFFIN,
3308c2ecf20Sopenharmony_ci		.model_id     = 0x00f970,
3318c2ecf20Sopenharmony_ci		.specifier_id = SPECIFIER_1394TA,
3328c2ecf20Sopenharmony_ci		.version      = VERSION_AVC,
3338c2ecf20Sopenharmony_ci		.driver_data  = (kernel_ulong_t)&griffin_firewave,
3348c2ecf20Sopenharmony_ci	},
3358c2ecf20Sopenharmony_ci	{
3368c2ecf20Sopenharmony_ci		.match_flags  = IEEE1394_MATCH_VENDOR_ID |
3378c2ecf20Sopenharmony_ci				IEEE1394_MATCH_MODEL_ID |
3388c2ecf20Sopenharmony_ci				IEEE1394_MATCH_SPECIFIER_ID |
3398c2ecf20Sopenharmony_ci				IEEE1394_MATCH_VERSION,
3408c2ecf20Sopenharmony_ci		.vendor_id    = VENDOR_LACIE,
3418c2ecf20Sopenharmony_ci		.model_id     = 0x00f970,
3428c2ecf20Sopenharmony_ci		.specifier_id = SPECIFIER_1394TA,
3438c2ecf20Sopenharmony_ci		.version      = VERSION_AVC,
3448c2ecf20Sopenharmony_ci		.driver_data  = (kernel_ulong_t)&lacie_speakers,
3458c2ecf20Sopenharmony_ci	},
3468c2ecf20Sopenharmony_ci	/* Behringer,F-Control Audio 202 */
3478c2ecf20Sopenharmony_ci	{
3488c2ecf20Sopenharmony_ci		.match_flags	= IEEE1394_MATCH_VENDOR_ID |
3498c2ecf20Sopenharmony_ci				  IEEE1394_MATCH_MODEL_ID,
3508c2ecf20Sopenharmony_ci		.vendor_id	= VENDOR_BEHRINGER,
3518c2ecf20Sopenharmony_ci		.model_id	= 0x00fc22,
3528c2ecf20Sopenharmony_ci	},
3538c2ecf20Sopenharmony_ci	/*
3548c2ecf20Sopenharmony_ci	 * Any Mackie(Loud) models (name string/model id):
3558c2ecf20Sopenharmony_ci	 *  Onyx-i series (former models):	0x081216
3568c2ecf20Sopenharmony_ci	 *  Mackie Onyx Satellite:		0x00200f
3578c2ecf20Sopenharmony_ci	 *  Tapco LINK.firewire 4x6:		0x000460
3588c2ecf20Sopenharmony_ci	 *  d.2 pro/d.4 pro (built-in card):	Unknown
3598c2ecf20Sopenharmony_ci	 *  U.420:				Unknown
3608c2ecf20Sopenharmony_ci	 *  U.420d:				Unknown
3618c2ecf20Sopenharmony_ci	 */
3628c2ecf20Sopenharmony_ci	{
3638c2ecf20Sopenharmony_ci		.match_flags	= IEEE1394_MATCH_VENDOR_ID |
3648c2ecf20Sopenharmony_ci				  IEEE1394_MATCH_SPECIFIER_ID |
3658c2ecf20Sopenharmony_ci				  IEEE1394_MATCH_VERSION,
3668c2ecf20Sopenharmony_ci		.vendor_id	= VENDOR_LOUD,
3678c2ecf20Sopenharmony_ci		.specifier_id	= SPECIFIER_1394TA,
3688c2ecf20Sopenharmony_ci		.version	= VERSION_AVC,
3698c2ecf20Sopenharmony_ci	},
3708c2ecf20Sopenharmony_ci	/* TASCAM, FireOne */
3718c2ecf20Sopenharmony_ci	{
3728c2ecf20Sopenharmony_ci		.match_flags	= IEEE1394_MATCH_VENDOR_ID |
3738c2ecf20Sopenharmony_ci				  IEEE1394_MATCH_MODEL_ID,
3748c2ecf20Sopenharmony_ci		.vendor_id	= VENDOR_TASCAM,
3758c2ecf20Sopenharmony_ci		.model_id	= 0x800007,
3768c2ecf20Sopenharmony_ci	},
3778c2ecf20Sopenharmony_ci	/* Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m) */
3788c2ecf20Sopenharmony_ci	{
3798c2ecf20Sopenharmony_ci		.match_flags	= IEEE1394_MATCH_VENDOR_ID |
3808c2ecf20Sopenharmony_ci				  IEEE1394_MATCH_MODEL_ID,
3818c2ecf20Sopenharmony_ci		.vendor_id	= OUI_STANTON,
3828c2ecf20Sopenharmony_ci		.model_id	= 0x001000,
3838c2ecf20Sopenharmony_ci	},
3848c2ecf20Sopenharmony_ci	/* Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d) */
3858c2ecf20Sopenharmony_ci	{
3868c2ecf20Sopenharmony_ci		.match_flags	= IEEE1394_MATCH_VENDOR_ID |
3878c2ecf20Sopenharmony_ci				  IEEE1394_MATCH_MODEL_ID,
3888c2ecf20Sopenharmony_ci		.vendor_id	= OUI_STANTON,
3898c2ecf20Sopenharmony_ci		.model_id	= 0x002000,
3908c2ecf20Sopenharmony_ci	},
3918c2ecf20Sopenharmony_ci	// APOGEE, duet FireWire
3928c2ecf20Sopenharmony_ci	{
3938c2ecf20Sopenharmony_ci		.match_flags	= IEEE1394_MATCH_VENDOR_ID |
3948c2ecf20Sopenharmony_ci				  IEEE1394_MATCH_MODEL_ID,
3958c2ecf20Sopenharmony_ci		.vendor_id	= OUI_APOGEE,
3968c2ecf20Sopenharmony_ci		.model_id	= 0x01dddd,
3978c2ecf20Sopenharmony_ci	},
3988c2ecf20Sopenharmony_ci	{ }
3998c2ecf20Sopenharmony_ci};
4008c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic struct fw_driver oxfw_driver = {
4038c2ecf20Sopenharmony_ci	.driver   = {
4048c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
4058c2ecf20Sopenharmony_ci		.name	= KBUILD_MODNAME,
4068c2ecf20Sopenharmony_ci		.bus	= &fw_bus_type,
4078c2ecf20Sopenharmony_ci	},
4088c2ecf20Sopenharmony_ci	.probe    = oxfw_probe,
4098c2ecf20Sopenharmony_ci	.update   = oxfw_bus_reset,
4108c2ecf20Sopenharmony_ci	.remove   = oxfw_remove,
4118c2ecf20Sopenharmony_ci	.id_table = oxfw_id_table,
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic int __init snd_oxfw_init(void)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	return driver_register(&oxfw_driver.driver);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic void __exit snd_oxfw_exit(void)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	driver_unregister(&oxfw_driver.driver);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cimodule_init(snd_oxfw_init);
4258c2ecf20Sopenharmony_cimodule_exit(snd_oxfw_exit);
426