xref: /openbmc/linux/sound/usb/usx2y/usbusx2y.c (revision 8ebc80a25f9d9bf7a8e368b266d5b740c485c362)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
3d13a8f6dSChristophe JAILLET  * usbusx2y.c - ALSA USB US-428 Driver
41da177e4SLinus Torvalds  *
5230cd5e2SKarsten Wiese 2005-04-14 Karsten Wiese
6230cd5e2SKarsten Wiese 	Version 0.8.7.2:
7230cd5e2SKarsten Wiese 	Call snd_card_free() instead of snd_card_free_in_thread() to prevent oops with dead keyboard symptom.
8230cd5e2SKarsten Wiese 	Tested ok with kernel 2.6.12-rc2.
9230cd5e2SKarsten Wiese 
101da177e4SLinus Torvalds 2004-12-14 Karsten Wiese
111da177e4SLinus Torvalds 	Version 0.8.7.1:
121da177e4SLinus Torvalds 	snd_pcm_open for rawusb pcm-devices now returns -EBUSY if called without rawusb's hwdep device being open.
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds 2004-12-02 Karsten Wiese
151da177e4SLinus Torvalds 	Version 0.8.7:
161da177e4SLinus Torvalds 	Use macro usb_maxpacket() for portability.
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds 2004-10-26 Karsten Wiese
191da177e4SLinus Torvalds 	Version 0.8.6:
20bae3ce49STakashi Iwai 	wake_up() process waiting in usx2y_urbs_start() on error.
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds 2004-10-21 Karsten Wiese
231da177e4SLinus Torvalds 	Version 0.8.5:
241da177e4SLinus Torvalds 	nrpacks is runtime or compiletime configurable now with tested values from 1 to 4.
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds 2004-10-03 Karsten Wiese
271da177e4SLinus Torvalds 	Version 0.8.2:
281da177e4SLinus Torvalds 	Avoid any possible racing while in prepare callback.
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds 2004-09-30 Karsten Wiese
311da177e4SLinus Torvalds 	Version 0.8.0:
321da177e4SLinus Torvalds 	Simplified things and made ohci work again.
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds 2004-09-20 Karsten Wiese
351da177e4SLinus Torvalds 	Version 0.7.3:
361da177e4SLinus Torvalds 	Use usb_kill_urb() instead of deprecated (kernel 2.6.9) usb_unlink_urb().
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds 2004-07-13 Karsten Wiese
391da177e4SLinus Torvalds 	Version 0.7.1:
401da177e4SLinus Torvalds 	Don't sleep in START/STOP callbacks anymore.
411da177e4SLinus Torvalds 	us428 channels C/D not handled just for this version, sorry.
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 2004-06-21 Karsten Wiese
441da177e4SLinus Torvalds 	Version 0.6.4:
451da177e4SLinus Torvalds 	Temporarely suspend midi input
461da177e4SLinus Torvalds 	to sanely call usb_set_interface() when setting format.
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds 2004-06-12 Karsten Wiese
491da177e4SLinus Torvalds 	Version 0.6.3:
501da177e4SLinus Torvalds 	Made it thus the following rule is enforced:
51bae3ce49STakashi Iwai 	"All pcm substreams of one usx2y have to operate at the same rate & format."
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds 2004-04-06 Karsten Wiese
541da177e4SLinus Torvalds 	Version 0.6.0:
551da177e4SLinus Torvalds 	Runs on 2.6.5 kernel without any "--with-debug=" things.
561da177e4SLinus Torvalds 	us224 reported running.
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 2004-01-14 Karsten Wiese
591da177e4SLinus Torvalds 	Version 0.5.1:
601da177e4SLinus Torvalds 	Runs with 2.6.1 kernel.
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 2003-12-30 Karsten Wiese
631da177e4SLinus Torvalds 	Version 0.4.1:
641da177e4SLinus Torvalds 	Fix 24Bit 4Channel capturing for the us428.
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds 2003-11-27 Karsten Wiese, Martin Langer
671da177e4SLinus Torvalds 	Version 0.4:
681da177e4SLinus Torvalds 	us122 support.
691da177e4SLinus Torvalds 	us224 could be tested by uncommenting the sections containing USB_ID_US224
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 2003-11-03 Karsten Wiese
721da177e4SLinus Torvalds 	Version 0.3:
731da177e4SLinus Torvalds 	24Bit support.
741da177e4SLinus Torvalds 	"arecord -D hw:1 -c 2 -r 48000 -M -f S24_3LE|aplay -D hw:1 -c 2 -r 48000 -M -f S24_3LE" works.
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 2003-08-22 Karsten Wiese
771da177e4SLinus Torvalds 	Version 0.0.8:
781da177e4SLinus Torvalds 	Removed EZUSB Firmware. First Stage Firmwaredownload is now done by tascam-firmware downloader.
791da177e4SLinus Torvalds 	See:
801da177e4SLinus Torvalds 	http://usb-midi-fw.sourceforge.net/tascam-firmware.tar.gz
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 2003-06-18 Karsten Wiese
831da177e4SLinus Torvalds 	Version 0.0.5:
841da177e4SLinus Torvalds 	changed to compile with kernel 2.4.21 and alsa 0.9.4
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 2002-10-16 Karsten Wiese
871da177e4SLinus Torvalds 	Version 0.0.4:
881da177e4SLinus Torvalds 	compiles again with alsa-current.
891da177e4SLinus Torvalds 	USB_ISO_ASAP not used anymore (most of the time), instead
901da177e4SLinus Torvalds 	urb->start_frame is calculated here now, some calls inside usb-driver don't need to happen anymore.
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds 	To get the best out of this:
931da177e4SLinus Torvalds 	Disable APM-support in the kernel as APM-BIOS calls (once each second) hard disable interrupt for many precious milliseconds.
941da177e4SLinus Torvalds 	This helped me much on my slowish PII 400 & PIII 500.
951da177e4SLinus Torvalds 	ACPI yet untested but might cause the same bad behaviour.
961da177e4SLinus Torvalds 	Use a kernel with lowlatency and preemptiv patches applied.
971da177e4SLinus Torvalds 	To autoload snd-usb-midi append a line
981da177e4SLinus Torvalds 		post-install snd-usb-us428 modprobe snd-usb-midi
991da177e4SLinus Torvalds 	to /etc/modules.conf.
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	known problems:
1021da177e4SLinus Torvalds 	sliders, knobs, lights not yet handled except MASTER Volume slider.
1031da177e4SLinus Torvalds 	"pcm -c 2" doesn't work. "pcm -c 2 -m direct_interleaved" does.
1041da177e4SLinus Torvalds 	KDE3: "Enable full duplex operation" deadlocks.
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 2002-08-31 Karsten Wiese
1071da177e4SLinus Torvalds 	Version 0.0.3: audio also simplex;
1081da177e4SLinus Torvalds 	simplifying: iso urbs only 1 packet, melted structs.
1091da177e4SLinus Torvalds 	ASYNC_UNLINK not used anymore: no more crashes so far.....
1101da177e4SLinus Torvalds 	for alsa 0.9 rc3.
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 2002-08-09 Karsten Wiese
1131da177e4SLinus Torvalds 	Version 0.0.2: midi works with snd-usb-midi, audio (only fullduplex now) with i.e. bristol.
1141da177e4SLinus Torvalds 	The firmware has been sniffed from win2k us-428 driver 3.09.
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds  *   Copyright (c) 2002 - 2004 Karsten Wiese
1171da177e4SLinus Torvalds  */
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds #include <linux/init.h>
1201da177e4SLinus Torvalds #include <linux/module.h>
1211da177e4SLinus Torvalds #include <linux/moduleparam.h>
1225a0e3ad6STejun Heo #include <linux/slab.h>
1231da177e4SLinus Torvalds #include <linux/interrupt.h>
1241da177e4SLinus Torvalds #include <linux/usb.h>
1251da177e4SLinus Torvalds #include <sound/core.h>
1261da177e4SLinus Torvalds #include <sound/initval.h>
1271da177e4SLinus Torvalds #include <sound/pcm.h>
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds #include <sound/rawmidi.h>
1301da177e4SLinus Torvalds #include "usx2y.h"
1311da177e4SLinus Torvalds #include "usbusx2y.h"
1321da177e4SLinus Torvalds #include "usX2Yhwdep.h"
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");
135230cd5e2SKarsten Wiese MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2");
1361da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
1391da177e4SLinus Torvalds static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
140a67ff6a5SRusty Russell static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds module_param_array(index, int, NULL, 0444);
1431da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");
1441da177e4SLinus Torvalds module_param_array(id, charp, NULL, 0444);
1451da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS".");
1461da177e4SLinus Torvalds module_param_array(enable, bool, NULL, 0444);
1471da177e4SLinus Torvalds MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS".");
1481da177e4SLinus Torvalds 
149bae3ce49STakashi Iwai static int snd_usx2y_card_used[SNDRV_CARDS];
1501da177e4SLinus Torvalds 
151bae3ce49STakashi Iwai static void snd_usx2y_card_private_free(struct snd_card *card);
15202d382afSTakashi Iwai static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s);
1531da177e4SLinus Torvalds 
154*8c48c26fSMurad Masimov #ifdef USX2Y_NRPACKS_VARIABLE
155*8c48c26fSMurad Masimov int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */
156*8c48c26fSMurad Masimov module_param(nrpacks, int, 0444);
157*8c48c26fSMurad Masimov MODULE_PARM_DESC(nrpacks, "Number of packets per URB.");
158*8c48c26fSMurad Masimov #endif
159*8c48c26fSMurad Masimov 
1601da177e4SLinus Torvalds /*
1611da177e4SLinus Torvalds  * pipe 4 is used for switching the lamps, setting samplerate, volumes ....
1621da177e4SLinus Torvalds  */
i_usx2y_out04_int(struct urb * urb)163bae3ce49STakashi Iwai static void i_usx2y_out04_int(struct urb *urb)
1641da177e4SLinus Torvalds {
1651da177e4SLinus Torvalds #ifdef CONFIG_SND_DEBUG
1661da177e4SLinus Torvalds 	if (urb->status) {
1671da177e4SLinus Torvalds 		int i;
168bae3ce49STakashi Iwai 		struct usx2ydev *usx2y = urb->context;
1694c0a58efSTakashi Iwai 
1704c0a58efSTakashi Iwai 		for (i = 0; i < 10 && usx2y->as04.urb[i] != urb; i++)
1714c0a58efSTakashi Iwai 			;
172a829dd5bSTakashi Iwai 		snd_printdd("%s urb %i status=%i\n", __func__, i, urb->status);
1731da177e4SLinus Torvalds 	}
1741da177e4SLinus Torvalds #endif
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
i_usx2y_in04_int(struct urb * urb)177bae3ce49STakashi Iwai static void i_usx2y_in04_int(struct urb *urb)
1781da177e4SLinus Torvalds {
1791da177e4SLinus Torvalds 	int			err = 0;
180bae3ce49STakashi Iwai 	struct usx2ydev		*usx2y = urb->context;
181bae3ce49STakashi Iwai 	struct us428ctls_sharedmem	*us428ctls = usx2y->us428ctls_sharedmem;
182a829dd5bSTakashi Iwai 	struct us428_p4out *p4out;
183a829dd5bSTakashi Iwai 	int i, j, n, diff, send;
1841da177e4SLinus Torvalds 
185bae3ce49STakashi Iwai 	usx2y->in04_int_calls++;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	if (urb->status) {
1881da177e4SLinus Torvalds 		snd_printdd("Interrupt Pipe 4 came back with status=%i\n", urb->status);
1891da177e4SLinus Torvalds 		return;
1901da177e4SLinus Torvalds 	}
1911da177e4SLinus Torvalds 
192bae3ce49STakashi Iwai 	//	printk("%i:0x%02X ", 8, (int)((unsigned char*)usx2y->in04_buf)[8]); Master volume shows 0 here if fader is at max during boot ?!?
1931da177e4SLinus Torvalds 	if (us428ctls) {
194a829dd5bSTakashi Iwai 		diff = -1;
195a829dd5bSTakashi Iwai 		if (us428ctls->ctl_snapshot_last == -2) {
1961da177e4SLinus Torvalds 			diff = 0;
197bae3ce49STakashi Iwai 			memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last));
198bae3ce49STakashi Iwai 			us428ctls->ctl_snapshot_last = -1;
1991da177e4SLinus Torvalds 		} else {
2001da177e4SLinus Torvalds 			for (i = 0; i < 21; i++) {
201bae3ce49STakashi Iwai 				if (usx2y->in04_last[i] != ((char *)usx2y->in04_buf)[i]) {
2021da177e4SLinus Torvalds 					if (diff < 0)
2031da177e4SLinus Torvalds 						diff = i;
204bae3ce49STakashi Iwai 					usx2y->in04_last[i] = ((char *)usx2y->in04_buf)[i];
2051da177e4SLinus Torvalds 				}
2061da177e4SLinus Torvalds 			}
2071da177e4SLinus Torvalds 		}
208a829dd5bSTakashi Iwai 		if (diff >= 0) {
209a829dd5bSTakashi Iwai 			n = us428ctls->ctl_snapshot_last + 1;
210bae3ce49STakashi Iwai 			if (n >= N_US428_CTL_BUFS || n < 0)
2111da177e4SLinus Torvalds 				n = 0;
212bae3ce49STakashi Iwai 			memcpy(us428ctls->ctl_snapshot + n, usx2y->in04_buf, sizeof(us428ctls->ctl_snapshot[0]));
213bae3ce49STakashi Iwai 			us428ctls->ctl_snapshot_differs_at[n] = diff;
214bae3ce49STakashi Iwai 			us428ctls->ctl_snapshot_last = n;
215bae3ce49STakashi Iwai 			wake_up(&usx2y->us428ctls_wait_queue_head);
2161da177e4SLinus Torvalds 		}
2171da177e4SLinus Torvalds 	}
2181da177e4SLinus Torvalds 
219bae3ce49STakashi Iwai 	if (usx2y->us04) {
220a829dd5bSTakashi Iwai 		if (!usx2y->us04->submitted) {
2213a755ec2SHannes Eder 			do {
222bae3ce49STakashi Iwai 				err = usb_submit_urb(usx2y->us04->urb[usx2y->us04->submitted++], GFP_ATOMIC);
223bae3ce49STakashi Iwai 			} while (!err && usx2y->us04->submitted < usx2y->us04->len);
224a829dd5bSTakashi Iwai 		}
225a829dd5bSTakashi Iwai 	} else {
226bae3ce49STakashi Iwai 		if (us428ctls && us428ctls->p4out_last >= 0 && us428ctls->p4out_last < N_US428_P4OUT_BUFS) {
227bae3ce49STakashi Iwai 			if (us428ctls->p4out_last != us428ctls->p4out_sent) {
228a829dd5bSTakashi Iwai 				send = us428ctls->p4out_sent + 1;
229bae3ce49STakashi Iwai 				if (send >= N_US428_P4OUT_BUFS)
2301da177e4SLinus Torvalds 					send = 0;
231a829dd5bSTakashi Iwai 				for (j = 0; j < URBS_ASYNC_SEQ && !err; ++j) {
232a829dd5bSTakashi Iwai 					if (!usx2y->as04.urb[j]->status) {
233a829dd5bSTakashi Iwai 						p4out = us428ctls->p4out + send;	// FIXME if more than 1 p4out is new, 1 gets lost.
234bae3ce49STakashi Iwai 						usb_fill_bulk_urb(usx2y->as04.urb[j], usx2y->dev,
235bae3ce49STakashi Iwai 								  usb_sndbulkpipe(usx2y->dev, 0x04), &p4out->val.vol,
236bae3ce49STakashi Iwai 								  p4out->type == ELT_LIGHT ? sizeof(struct us428_lights) : 5,
237bae3ce49STakashi Iwai 								  i_usx2y_out04_int, usx2y);
238bae3ce49STakashi Iwai 						err = usb_submit_urb(usx2y->as04.urb[j], GFP_ATOMIC);
239bae3ce49STakashi Iwai 						us428ctls->p4out_sent = send;
2401da177e4SLinus Torvalds 						break;
2411da177e4SLinus Torvalds 					}
2421da177e4SLinus Torvalds 				}
2431da177e4SLinus Torvalds 			}
244a829dd5bSTakashi Iwai 		}
245a829dd5bSTakashi Iwai 	}
2461da177e4SLinus Torvalds 
247d3d579f8STakashi Iwai 	if (err)
248bae3ce49STakashi Iwai 		snd_printk(KERN_ERR "in04_int() usb_submit_urb err=%i\n", err);
2491da177e4SLinus Torvalds 
250bae3ce49STakashi Iwai 	urb->dev = usx2y->dev;
2511da177e4SLinus Torvalds 	usb_submit_urb(urb, GFP_ATOMIC);
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds /*
2551da177e4SLinus Torvalds  * Prepare some urbs
2561da177e4SLinus Torvalds  */
usx2y_async_seq04_init(struct usx2ydev * usx2y)257bae3ce49STakashi Iwai int usx2y_async_seq04_init(struct usx2ydev *usx2y)
2581da177e4SLinus Torvalds {
2594c0a58efSTakashi Iwai 	int	err = 0, i;
2601da177e4SLinus Torvalds 
26102d382afSTakashi Iwai 	if (WARN_ON(usx2y->as04.buffer))
26202d382afSTakashi Iwai 		return -EBUSY;
26302d382afSTakashi Iwai 
264bae3ce49STakashi Iwai 	usx2y->as04.buffer = kmalloc_array(URBS_ASYNC_SEQ,
265bae3ce49STakashi Iwai 					   URB_DATA_LEN_ASYNC_SEQ, GFP_KERNEL);
266a829dd5bSTakashi Iwai 	if (!usx2y->as04.buffer) {
2671da177e4SLinus Torvalds 		err = -ENOMEM;
268a829dd5bSTakashi Iwai 	} else {
269bae3ce49STakashi Iwai 		for (i = 0; i < URBS_ASYNC_SEQ; ++i) {
270a829dd5bSTakashi Iwai 			usx2y->as04.urb[i] = usb_alloc_urb(0, GFP_KERNEL);
271a829dd5bSTakashi Iwai 			if (!usx2y->as04.urb[i]) {
2721da177e4SLinus Torvalds 				err = -ENOMEM;
2731da177e4SLinus Torvalds 				break;
2741da177e4SLinus Torvalds 			}
275bae3ce49STakashi Iwai 			usb_fill_bulk_urb(usx2y->as04.urb[i], usx2y->dev,
276bae3ce49STakashi Iwai 					  usb_sndbulkpipe(usx2y->dev, 0x04),
277bae3ce49STakashi Iwai 					  usx2y->as04.buffer + URB_DATA_LEN_ASYNC_SEQ * i, 0,
2784c0a58efSTakashi Iwai 					  i_usx2y_out04_int, usx2y);
279bae3ce49STakashi Iwai 			err = usb_urb_ep_type_check(usx2y->as04.urb[i]);
2801f100349STakashi Iwai 			if (err < 0)
2811f100349STakashi Iwai 				break;
2821da177e4SLinus Torvalds 		}
283a829dd5bSTakashi Iwai 	}
28402d382afSTakashi Iwai 	if (err)
28502d382afSTakashi Iwai 		usx2y_unlinkseq(&usx2y->as04);
2861da177e4SLinus Torvalds 	return err;
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds 
usx2y_in04_init(struct usx2ydev * usx2y)289bae3ce49STakashi Iwai int usx2y_in04_init(struct usx2ydev *usx2y)
2901da177e4SLinus Torvalds {
29102d382afSTakashi Iwai 	int err;
29202d382afSTakashi Iwai 
29302d382afSTakashi Iwai 	if (WARN_ON(usx2y->in04_urb))
29402d382afSTakashi Iwai 		return -EBUSY;
29502d382afSTakashi Iwai 
296a829dd5bSTakashi Iwai 	usx2y->in04_urb = usb_alloc_urb(0, GFP_KERNEL);
29702d382afSTakashi Iwai 	if (!usx2y->in04_urb) {
29802d382afSTakashi Iwai 		err = -ENOMEM;
29902d382afSTakashi Iwai 		goto error;
30002d382afSTakashi Iwai 	}
3011da177e4SLinus Torvalds 
302a829dd5bSTakashi Iwai 	usx2y->in04_buf = kmalloc(21, GFP_KERNEL);
30302d382afSTakashi Iwai 	if (!usx2y->in04_buf) {
30402d382afSTakashi Iwai 		err = -ENOMEM;
30502d382afSTakashi Iwai 		goto error;
30602d382afSTakashi Iwai 	}
3071da177e4SLinus Torvalds 
308bae3ce49STakashi Iwai 	init_waitqueue_head(&usx2y->in04_wait_queue);
309bae3ce49STakashi Iwai 	usb_fill_int_urb(usx2y->in04_urb, usx2y->dev, usb_rcvintpipe(usx2y->dev, 0x4),
310bae3ce49STakashi Iwai 			 usx2y->in04_buf, 21,
311bae3ce49STakashi Iwai 			 i_usx2y_in04_int, usx2y,
3121da177e4SLinus Torvalds 			 10);
31302d382afSTakashi Iwai 	if (usb_urb_ep_type_check(usx2y->in04_urb)) {
31402d382afSTakashi Iwai 		err = -EINVAL;
31502d382afSTakashi Iwai 		goto error;
31602d382afSTakashi Iwai 	}
317bae3ce49STakashi Iwai 	return usb_submit_urb(usx2y->in04_urb, GFP_KERNEL);
31802d382afSTakashi Iwai 
31902d382afSTakashi Iwai  error:
32002d382afSTakashi Iwai 	kfree(usx2y->in04_buf);
32102d382afSTakashi Iwai 	usb_free_urb(usx2y->in04_urb);
32202d382afSTakashi Iwai 	usx2y->in04_buf = NULL;
32302d382afSTakashi Iwai 	usx2y->in04_urb = NULL;
32402d382afSTakashi Iwai 	return err;
3251da177e4SLinus Torvalds }
3261da177e4SLinus Torvalds 
usx2y_unlinkseq(struct snd_usx2y_async_seq * s)327bae3ce49STakashi Iwai static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s)
3281da177e4SLinus Torvalds {
3291da177e4SLinus Torvalds 	int	i;
3304c0a58efSTakashi Iwai 
331bae3ce49STakashi Iwai 	for (i = 0; i < URBS_ASYNC_SEQ; ++i) {
33202d382afSTakashi Iwai 		if (!s->urb[i])
33302d382afSTakashi Iwai 			continue;
334bae3ce49STakashi Iwai 		usb_kill_urb(s->urb[i]);
335bae3ce49STakashi Iwai 		usb_free_urb(s->urb[i]);
336bae3ce49STakashi Iwai 		s->urb[i] = NULL;
3371da177e4SLinus Torvalds 	}
338bae3ce49STakashi Iwai 	kfree(s->buffer);
33902d382afSTakashi Iwai 	s->buffer = NULL;
3401da177e4SLinus Torvalds }
3411da177e4SLinus Torvalds 
342bae3ce49STakashi Iwai static const struct usb_device_id snd_usx2y_usb_id_table[] = {
3431da177e4SLinus Torvalds 	{
3441da177e4SLinus Torvalds 		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
3451da177e4SLinus Torvalds 		.idVendor =	0x1604,
3461da177e4SLinus Torvalds 		.idProduct =	USB_ID_US428
3471da177e4SLinus Torvalds 	},
3481da177e4SLinus Torvalds 	{
3491da177e4SLinus Torvalds 		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
3501da177e4SLinus Torvalds 		.idVendor =	0x1604,
3511da177e4SLinus Torvalds 		.idProduct =	USB_ID_US122
3521da177e4SLinus Torvalds 	},
3531da177e4SLinus Torvalds 	{
3541da177e4SLinus Torvalds 		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
3551da177e4SLinus Torvalds 		.idVendor =	0x1604,
3561da177e4SLinus Torvalds 		.idProduct =	USB_ID_US224
3571da177e4SLinus Torvalds 	},
3581da177e4SLinus Torvalds 	{ /* terminator */ }
3591da177e4SLinus Torvalds };
3604c0a58efSTakashi Iwai MODULE_DEVICE_TABLE(usb, snd_usx2y_usb_id_table);
3611da177e4SLinus Torvalds 
usx2y_create_card(struct usb_device * device,struct usb_interface * intf,struct snd_card ** cardp)362bae3ce49STakashi Iwai static int usx2y_create_card(struct usb_device *device,
363874b8d42STakashi Iwai 			     struct usb_interface *intf,
364874b8d42STakashi Iwai 			     struct snd_card **cardp)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds 	int		dev;
367bbe85bbdSTakashi Iwai 	struct snd_card *card;
368bd7dd77cSTakashi Iwai 	int err;
369bd7dd77cSTakashi Iwai 
3701da177e4SLinus Torvalds 	for (dev = 0; dev < SNDRV_CARDS; ++dev)
371bae3ce49STakashi Iwai 		if (enable[dev] && !snd_usx2y_card_used[dev])
3721da177e4SLinus Torvalds 			break;
3731da177e4SLinus Torvalds 	if (dev >= SNDRV_CARDS)
37451721f70STakashi Iwai 		return -ENODEV;
375874b8d42STakashi Iwai 	err = snd_card_new(&intf->dev, index[dev], id[dev], THIS_MODULE,
376bae3ce49STakashi Iwai 			   sizeof(struct usx2ydev), &card);
377bd7dd77cSTakashi Iwai 	if (err < 0)
37851721f70STakashi Iwai 		return err;
379bae3ce49STakashi Iwai 	snd_usx2y_card_used[usx2y(card)->card_index = dev] = 1;
380bae3ce49STakashi Iwai 	card->private_free = snd_usx2y_card_private_free;
381bae3ce49STakashi Iwai 	usx2y(card)->dev = device;
382bae3ce49STakashi Iwai 	init_waitqueue_head(&usx2y(card)->prepare_wait_queue);
38364a06f19STakashi Iwai 	init_waitqueue_head(&usx2y(card)->us428ctls_wait_queue_head);
384bae3ce49STakashi Iwai 	mutex_init(&usx2y(card)->pcm_mutex);
385bae3ce49STakashi Iwai 	INIT_LIST_HEAD(&usx2y(card)->midi_list);
3861da177e4SLinus Torvalds 	strcpy(card->driver, "USB "NAME_ALLCAPS"");
3871da177e4SLinus Torvalds 	sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
3881da177e4SLinus Torvalds 	sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",
3891da177e4SLinus Torvalds 		card->shortname,
3901da177e4SLinus Torvalds 		le16_to_cpu(device->descriptor.idVendor),
3911da177e4SLinus Torvalds 		le16_to_cpu(device->descriptor.idProduct),
3921da177e4SLinus Torvalds 		0,//us428(card)->usbmidi.ifnum,
393a829dd5bSTakashi Iwai 		usx2y(card)->dev->bus->busnum, usx2y(card)->dev->devnum);
39451721f70STakashi Iwai 	*cardp = card;
39551721f70STakashi Iwai 	return 0;
3961da177e4SLinus Torvalds }
3971da177e4SLinus Torvalds 
snd_usx2y_card_private_free(struct snd_card * card)398bae3ce49STakashi Iwai static void snd_usx2y_card_private_free(struct snd_card *card)
3991da177e4SLinus Torvalds {
400a829dd5bSTakashi Iwai 	struct usx2ydev *usx2y = usx2y(card);
401a829dd5bSTakashi Iwai 
402a829dd5bSTakashi Iwai 	kfree(usx2y->in04_buf);
403a829dd5bSTakashi Iwai 	usb_free_urb(usx2y->in04_urb);
404a829dd5bSTakashi Iwai 	if (usx2y->us428ctls_sharedmem)
405a829dd5bSTakashi Iwai 		free_pages_exact(usx2y->us428ctls_sharedmem,
4064e268db7STakashi Iwai 				 US428_SHAREDMEM_PAGES);
407a829dd5bSTakashi Iwai 	if (usx2y->card_index >= 0 && usx2y->card_index < SNDRV_CARDS)
408a829dd5bSTakashi Iwai 		snd_usx2y_card_used[usx2y->card_index] = 0;
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
snd_usx2y_disconnect(struct usb_interface * intf)4112ac7a12eSTakashi Iwai static void snd_usx2y_disconnect(struct usb_interface *intf)
4121da177e4SLinus Torvalds {
413a829dd5bSTakashi Iwai 	struct snd_card *card;
414a829dd5bSTakashi Iwai 	struct usx2ydev *usx2y;
4151da177e4SLinus Torvalds 	struct list_head *p;
4164c0a58efSTakashi Iwai 
4172ac7a12eSTakashi Iwai 	card = usb_get_intfdata(intf);
4182ac7a12eSTakashi Iwai 	if (!card)
419a829dd5bSTakashi Iwai 		return;
420a829dd5bSTakashi Iwai 	usx2y = usx2y(card);
421bae3ce49STakashi Iwai 	usx2y->chip_status = USX2Y_STAT_CHIP_HUP;
422bae3ce49STakashi Iwai 	usx2y_unlinkseq(&usx2y->as04);
423bae3ce49STakashi Iwai 	usb_kill_urb(usx2y->in04_urb);
424cb432379STakashi Iwai 	snd_card_disconnect(card);
425a829dd5bSTakashi Iwai 
4261da177e4SLinus Torvalds 	/* release the midi resources */
427bae3ce49STakashi Iwai 	list_for_each(p, &usx2y->midi_list) {
428ee733397SClemens Ladisch 		snd_usbmidi_disconnect(p);
4291da177e4SLinus Torvalds 	}
430bae3ce49STakashi Iwai 	if (usx2y->us428ctls_sharedmem)
431bae3ce49STakashi Iwai 		wake_up(&usx2y->us428ctls_wait_queue_head);
432e07605d8STakashi Iwai 	snd_card_free_when_closed(card);
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
snd_usx2y_probe(struct usb_interface * intf,const struct usb_device_id * id)4352ac7a12eSTakashi Iwai static int snd_usx2y_probe(struct usb_interface *intf,
4362ac7a12eSTakashi Iwai 			   const struct usb_device_id *id)
4372ac7a12eSTakashi Iwai {
4382ac7a12eSTakashi Iwai 	struct usb_device *device = interface_to_usbdev(intf);
4392ac7a12eSTakashi Iwai 	struct snd_card *card;
4402ac7a12eSTakashi Iwai 	int err;
4412ac7a12eSTakashi Iwai 
442*8c48c26fSMurad Masimov #ifdef USX2Y_NRPACKS_VARIABLE
443*8c48c26fSMurad Masimov 	if (nrpacks < 0 || nrpacks > USX2Y_NRPACKS_MAX)
444*8c48c26fSMurad Masimov 		return -EINVAL;
445*8c48c26fSMurad Masimov #endif
446*8c48c26fSMurad Masimov 
4472ac7a12eSTakashi Iwai 	if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 ||
4482ac7a12eSTakashi Iwai 	    (le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 &&
4492ac7a12eSTakashi Iwai 	     le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 &&
4502ac7a12eSTakashi Iwai 	     le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428))
4512ac7a12eSTakashi Iwai 		return -EINVAL;
4522ac7a12eSTakashi Iwai 
4532ac7a12eSTakashi Iwai 	err = usx2y_create_card(device, intf, &card);
4542ac7a12eSTakashi Iwai 	if (err < 0)
4552ac7a12eSTakashi Iwai 		return err;
4562ac7a12eSTakashi Iwai 	err = usx2y_hwdep_new(card, device);
4572ac7a12eSTakashi Iwai 	if (err < 0)
4582ac7a12eSTakashi Iwai 		goto error;
4592ac7a12eSTakashi Iwai 	err = snd_card_register(card);
4602ac7a12eSTakashi Iwai 	if (err < 0)
4612ac7a12eSTakashi Iwai 		goto error;
4622ac7a12eSTakashi Iwai 
4632ac7a12eSTakashi Iwai 	dev_set_drvdata(&intf->dev, card);
4642ac7a12eSTakashi Iwai 	return 0;
4652ac7a12eSTakashi Iwai 
4662ac7a12eSTakashi Iwai  error:
4672ac7a12eSTakashi Iwai 	snd_card_free(card);
4682ac7a12eSTakashi Iwai 	return err;
4692ac7a12eSTakashi Iwai }
4702ac7a12eSTakashi Iwai 
4712ac7a12eSTakashi Iwai static struct usb_driver snd_usx2y_usb_driver = {
4722ac7a12eSTakashi Iwai 	.name =		"snd-usb-usx2y",
4732ac7a12eSTakashi Iwai 	.probe =	snd_usx2y_probe,
4742ac7a12eSTakashi Iwai 	.disconnect =	snd_usx2y_disconnect,
4752ac7a12eSTakashi Iwai 	.id_table =	snd_usx2y_usb_id_table,
4762ac7a12eSTakashi Iwai };
477bae3ce49STakashi Iwai module_usb_driver(snd_usx2y_usb_driver);
478