162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * usbusx2y.c - ALSA USB US-428 Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci2005-04-14 Karsten Wiese 662306a36Sopenharmony_ci Version 0.8.7.2: 762306a36Sopenharmony_ci Call snd_card_free() instead of snd_card_free_in_thread() to prevent oops with dead keyboard symptom. 862306a36Sopenharmony_ci Tested ok with kernel 2.6.12-rc2. 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci2004-12-14 Karsten Wiese 1162306a36Sopenharmony_ci Version 0.8.7.1: 1262306a36Sopenharmony_ci snd_pcm_open for rawusb pcm-devices now returns -EBUSY if called without rawusb's hwdep device being open. 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci2004-12-02 Karsten Wiese 1562306a36Sopenharmony_ci Version 0.8.7: 1662306a36Sopenharmony_ci Use macro usb_maxpacket() for portability. 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci2004-10-26 Karsten Wiese 1962306a36Sopenharmony_ci Version 0.8.6: 2062306a36Sopenharmony_ci wake_up() process waiting in usx2y_urbs_start() on error. 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci2004-10-21 Karsten Wiese 2362306a36Sopenharmony_ci Version 0.8.5: 2462306a36Sopenharmony_ci nrpacks is runtime or compiletime configurable now with tested values from 1 to 4. 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci2004-10-03 Karsten Wiese 2762306a36Sopenharmony_ci Version 0.8.2: 2862306a36Sopenharmony_ci Avoid any possible racing while in prepare callback. 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci2004-09-30 Karsten Wiese 3162306a36Sopenharmony_ci Version 0.8.0: 3262306a36Sopenharmony_ci Simplified things and made ohci work again. 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci2004-09-20 Karsten Wiese 3562306a36Sopenharmony_ci Version 0.7.3: 3662306a36Sopenharmony_ci Use usb_kill_urb() instead of deprecated (kernel 2.6.9) usb_unlink_urb(). 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci2004-07-13 Karsten Wiese 3962306a36Sopenharmony_ci Version 0.7.1: 4062306a36Sopenharmony_ci Don't sleep in START/STOP callbacks anymore. 4162306a36Sopenharmony_ci us428 channels C/D not handled just for this version, sorry. 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci2004-06-21 Karsten Wiese 4462306a36Sopenharmony_ci Version 0.6.4: 4562306a36Sopenharmony_ci Temporarely suspend midi input 4662306a36Sopenharmony_ci to sanely call usb_set_interface() when setting format. 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci2004-06-12 Karsten Wiese 4962306a36Sopenharmony_ci Version 0.6.3: 5062306a36Sopenharmony_ci Made it thus the following rule is enforced: 5162306a36Sopenharmony_ci "All pcm substreams of one usx2y have to operate at the same rate & format." 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci2004-04-06 Karsten Wiese 5462306a36Sopenharmony_ci Version 0.6.0: 5562306a36Sopenharmony_ci Runs on 2.6.5 kernel without any "--with-debug=" things. 5662306a36Sopenharmony_ci us224 reported running. 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci2004-01-14 Karsten Wiese 5962306a36Sopenharmony_ci Version 0.5.1: 6062306a36Sopenharmony_ci Runs with 2.6.1 kernel. 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci2003-12-30 Karsten Wiese 6362306a36Sopenharmony_ci Version 0.4.1: 6462306a36Sopenharmony_ci Fix 24Bit 4Channel capturing for the us428. 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci2003-11-27 Karsten Wiese, Martin Langer 6762306a36Sopenharmony_ci Version 0.4: 6862306a36Sopenharmony_ci us122 support. 6962306a36Sopenharmony_ci us224 could be tested by uncommenting the sections containing USB_ID_US224 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci2003-11-03 Karsten Wiese 7262306a36Sopenharmony_ci Version 0.3: 7362306a36Sopenharmony_ci 24Bit support. 7462306a36Sopenharmony_ci "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. 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci2003-08-22 Karsten Wiese 7762306a36Sopenharmony_ci Version 0.0.8: 7862306a36Sopenharmony_ci Removed EZUSB Firmware. First Stage Firmwaredownload is now done by tascam-firmware downloader. 7962306a36Sopenharmony_ci See: 8062306a36Sopenharmony_ci http://usb-midi-fw.sourceforge.net/tascam-firmware.tar.gz 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci2003-06-18 Karsten Wiese 8362306a36Sopenharmony_ci Version 0.0.5: 8462306a36Sopenharmony_ci changed to compile with kernel 2.4.21 and alsa 0.9.4 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci2002-10-16 Karsten Wiese 8762306a36Sopenharmony_ci Version 0.0.4: 8862306a36Sopenharmony_ci compiles again with alsa-current. 8962306a36Sopenharmony_ci USB_ISO_ASAP not used anymore (most of the time), instead 9062306a36Sopenharmony_ci urb->start_frame is calculated here now, some calls inside usb-driver don't need to happen anymore. 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci To get the best out of this: 9362306a36Sopenharmony_ci Disable APM-support in the kernel as APM-BIOS calls (once each second) hard disable interrupt for many precious milliseconds. 9462306a36Sopenharmony_ci This helped me much on my slowish PII 400 & PIII 500. 9562306a36Sopenharmony_ci ACPI yet untested but might cause the same bad behaviour. 9662306a36Sopenharmony_ci Use a kernel with lowlatency and preemptiv patches applied. 9762306a36Sopenharmony_ci To autoload snd-usb-midi append a line 9862306a36Sopenharmony_ci post-install snd-usb-us428 modprobe snd-usb-midi 9962306a36Sopenharmony_ci to /etc/modules.conf. 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci known problems: 10262306a36Sopenharmony_ci sliders, knobs, lights not yet handled except MASTER Volume slider. 10362306a36Sopenharmony_ci "pcm -c 2" doesn't work. "pcm -c 2 -m direct_interleaved" does. 10462306a36Sopenharmony_ci KDE3: "Enable full duplex operation" deadlocks. 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci2002-08-31 Karsten Wiese 10762306a36Sopenharmony_ci Version 0.0.3: audio also simplex; 10862306a36Sopenharmony_ci simplifying: iso urbs only 1 packet, melted structs. 10962306a36Sopenharmony_ci ASYNC_UNLINK not used anymore: no more crashes so far..... 11062306a36Sopenharmony_ci for alsa 0.9 rc3. 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci2002-08-09 Karsten Wiese 11362306a36Sopenharmony_ci Version 0.0.2: midi works with snd-usb-midi, audio (only fullduplex now) with i.e. bristol. 11462306a36Sopenharmony_ci The firmware has been sniffed from win2k us-428 driver 3.09. 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci * Copyright (c) 2002 - 2004 Karsten Wiese 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#include <linux/init.h> 12062306a36Sopenharmony_ci#include <linux/module.h> 12162306a36Sopenharmony_ci#include <linux/moduleparam.h> 12262306a36Sopenharmony_ci#include <linux/slab.h> 12362306a36Sopenharmony_ci#include <linux/interrupt.h> 12462306a36Sopenharmony_ci#include <linux/usb.h> 12562306a36Sopenharmony_ci#include <sound/core.h> 12662306a36Sopenharmony_ci#include <sound/initval.h> 12762306a36Sopenharmony_ci#include <sound/pcm.h> 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#include <sound/rawmidi.h> 13062306a36Sopenharmony_ci#include "usx2y.h" 13162306a36Sopenharmony_ci#include "usbusx2y.h" 13262306a36Sopenharmony_ci#include "usX2Yhwdep.h" 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciMODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>"); 13562306a36Sopenharmony_ciMODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2"); 13662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ 13962306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ 14062306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 14362306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS"."); 14462306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 14562306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS"."); 14662306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 14762306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS"."); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int snd_usx2y_card_used[SNDRV_CARDS]; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void snd_usx2y_card_private_free(struct snd_card *card); 15262306a36Sopenharmony_cistatic void usx2y_unlinkseq(struct snd_usx2y_async_seq *s); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * pipe 4 is used for switching the lamps, setting samplerate, volumes .... 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic void i_usx2y_out04_int(struct urb *urb) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 16062306a36Sopenharmony_ci if (urb->status) { 16162306a36Sopenharmony_ci int i; 16262306a36Sopenharmony_ci struct usx2ydev *usx2y = urb->context; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for (i = 0; i < 10 && usx2y->as04.urb[i] != urb; i++) 16562306a36Sopenharmony_ci ; 16662306a36Sopenharmony_ci snd_printdd("%s urb %i status=%i\n", __func__, i, urb->status); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci#endif 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void i_usx2y_in04_int(struct urb *urb) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int err = 0; 17462306a36Sopenharmony_ci struct usx2ydev *usx2y = urb->context; 17562306a36Sopenharmony_ci struct us428ctls_sharedmem *us428ctls = usx2y->us428ctls_sharedmem; 17662306a36Sopenharmony_ci struct us428_p4out *p4out; 17762306a36Sopenharmony_ci int i, j, n, diff, send; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci usx2y->in04_int_calls++; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (urb->status) { 18262306a36Sopenharmony_ci snd_printdd("Interrupt Pipe 4 came back with status=%i\n", urb->status); 18362306a36Sopenharmony_ci return; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci // printk("%i:0x%02X ", 8, (int)((unsigned char*)usx2y->in04_buf)[8]); Master volume shows 0 here if fader is at max during boot ?!? 18762306a36Sopenharmony_ci if (us428ctls) { 18862306a36Sopenharmony_ci diff = -1; 18962306a36Sopenharmony_ci if (us428ctls->ctl_snapshot_last == -2) { 19062306a36Sopenharmony_ci diff = 0; 19162306a36Sopenharmony_ci memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last)); 19262306a36Sopenharmony_ci us428ctls->ctl_snapshot_last = -1; 19362306a36Sopenharmony_ci } else { 19462306a36Sopenharmony_ci for (i = 0; i < 21; i++) { 19562306a36Sopenharmony_ci if (usx2y->in04_last[i] != ((char *)usx2y->in04_buf)[i]) { 19662306a36Sopenharmony_ci if (diff < 0) 19762306a36Sopenharmony_ci diff = i; 19862306a36Sopenharmony_ci usx2y->in04_last[i] = ((char *)usx2y->in04_buf)[i]; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci if (diff >= 0) { 20362306a36Sopenharmony_ci n = us428ctls->ctl_snapshot_last + 1; 20462306a36Sopenharmony_ci if (n >= N_US428_CTL_BUFS || n < 0) 20562306a36Sopenharmony_ci n = 0; 20662306a36Sopenharmony_ci memcpy(us428ctls->ctl_snapshot + n, usx2y->in04_buf, sizeof(us428ctls->ctl_snapshot[0])); 20762306a36Sopenharmony_ci us428ctls->ctl_snapshot_differs_at[n] = diff; 20862306a36Sopenharmony_ci us428ctls->ctl_snapshot_last = n; 20962306a36Sopenharmony_ci wake_up(&usx2y->us428ctls_wait_queue_head); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (usx2y->us04) { 21462306a36Sopenharmony_ci if (!usx2y->us04->submitted) { 21562306a36Sopenharmony_ci do { 21662306a36Sopenharmony_ci err = usb_submit_urb(usx2y->us04->urb[usx2y->us04->submitted++], GFP_ATOMIC); 21762306a36Sopenharmony_ci } while (!err && usx2y->us04->submitted < usx2y->us04->len); 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci if (us428ctls && us428ctls->p4out_last >= 0 && us428ctls->p4out_last < N_US428_P4OUT_BUFS) { 22162306a36Sopenharmony_ci if (us428ctls->p4out_last != us428ctls->p4out_sent) { 22262306a36Sopenharmony_ci send = us428ctls->p4out_sent + 1; 22362306a36Sopenharmony_ci if (send >= N_US428_P4OUT_BUFS) 22462306a36Sopenharmony_ci send = 0; 22562306a36Sopenharmony_ci for (j = 0; j < URBS_ASYNC_SEQ && !err; ++j) { 22662306a36Sopenharmony_ci if (!usx2y->as04.urb[j]->status) { 22762306a36Sopenharmony_ci p4out = us428ctls->p4out + send; // FIXME if more than 1 p4out is new, 1 gets lost. 22862306a36Sopenharmony_ci usb_fill_bulk_urb(usx2y->as04.urb[j], usx2y->dev, 22962306a36Sopenharmony_ci usb_sndbulkpipe(usx2y->dev, 0x04), &p4out->val.vol, 23062306a36Sopenharmony_ci p4out->type == ELT_LIGHT ? sizeof(struct us428_lights) : 5, 23162306a36Sopenharmony_ci i_usx2y_out04_int, usx2y); 23262306a36Sopenharmony_ci err = usb_submit_urb(usx2y->as04.urb[j], GFP_ATOMIC); 23362306a36Sopenharmony_ci us428ctls->p4out_sent = send; 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (err) 24262306a36Sopenharmony_ci snd_printk(KERN_ERR "in04_int() usb_submit_urb err=%i\n", err); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci urb->dev = usx2y->dev; 24562306a36Sopenharmony_ci usb_submit_urb(urb, GFP_ATOMIC); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * Prepare some urbs 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ciint usx2y_async_seq04_init(struct usx2ydev *usx2y) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int err = 0, i; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (WARN_ON(usx2y->as04.buffer)) 25662306a36Sopenharmony_ci return -EBUSY; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci usx2y->as04.buffer = kmalloc_array(URBS_ASYNC_SEQ, 25962306a36Sopenharmony_ci URB_DATA_LEN_ASYNC_SEQ, GFP_KERNEL); 26062306a36Sopenharmony_ci if (!usx2y->as04.buffer) { 26162306a36Sopenharmony_ci err = -ENOMEM; 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci for (i = 0; i < URBS_ASYNC_SEQ; ++i) { 26462306a36Sopenharmony_ci usx2y->as04.urb[i] = usb_alloc_urb(0, GFP_KERNEL); 26562306a36Sopenharmony_ci if (!usx2y->as04.urb[i]) { 26662306a36Sopenharmony_ci err = -ENOMEM; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci usb_fill_bulk_urb(usx2y->as04.urb[i], usx2y->dev, 27062306a36Sopenharmony_ci usb_sndbulkpipe(usx2y->dev, 0x04), 27162306a36Sopenharmony_ci usx2y->as04.buffer + URB_DATA_LEN_ASYNC_SEQ * i, 0, 27262306a36Sopenharmony_ci i_usx2y_out04_int, usx2y); 27362306a36Sopenharmony_ci err = usb_urb_ep_type_check(usx2y->as04.urb[i]); 27462306a36Sopenharmony_ci if (err < 0) 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci if (err) 27962306a36Sopenharmony_ci usx2y_unlinkseq(&usx2y->as04); 28062306a36Sopenharmony_ci return err; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciint usx2y_in04_init(struct usx2ydev *usx2y) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci int err; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (WARN_ON(usx2y->in04_urb)) 28862306a36Sopenharmony_ci return -EBUSY; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci usx2y->in04_urb = usb_alloc_urb(0, GFP_KERNEL); 29162306a36Sopenharmony_ci if (!usx2y->in04_urb) { 29262306a36Sopenharmony_ci err = -ENOMEM; 29362306a36Sopenharmony_ci goto error; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci usx2y->in04_buf = kmalloc(21, GFP_KERNEL); 29762306a36Sopenharmony_ci if (!usx2y->in04_buf) { 29862306a36Sopenharmony_ci err = -ENOMEM; 29962306a36Sopenharmony_ci goto error; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci init_waitqueue_head(&usx2y->in04_wait_queue); 30362306a36Sopenharmony_ci usb_fill_int_urb(usx2y->in04_urb, usx2y->dev, usb_rcvintpipe(usx2y->dev, 0x4), 30462306a36Sopenharmony_ci usx2y->in04_buf, 21, 30562306a36Sopenharmony_ci i_usx2y_in04_int, usx2y, 30662306a36Sopenharmony_ci 10); 30762306a36Sopenharmony_ci if (usb_urb_ep_type_check(usx2y->in04_urb)) { 30862306a36Sopenharmony_ci err = -EINVAL; 30962306a36Sopenharmony_ci goto error; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci return usb_submit_urb(usx2y->in04_urb, GFP_KERNEL); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci error: 31462306a36Sopenharmony_ci kfree(usx2y->in04_buf); 31562306a36Sopenharmony_ci usb_free_urb(usx2y->in04_urb); 31662306a36Sopenharmony_ci usx2y->in04_buf = NULL; 31762306a36Sopenharmony_ci usx2y->in04_urb = NULL; 31862306a36Sopenharmony_ci return err; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void usx2y_unlinkseq(struct snd_usx2y_async_seq *s) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int i; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci for (i = 0; i < URBS_ASYNC_SEQ; ++i) { 32662306a36Sopenharmony_ci if (!s->urb[i]) 32762306a36Sopenharmony_ci continue; 32862306a36Sopenharmony_ci usb_kill_urb(s->urb[i]); 32962306a36Sopenharmony_ci usb_free_urb(s->urb[i]); 33062306a36Sopenharmony_ci s->urb[i] = NULL; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci kfree(s->buffer); 33362306a36Sopenharmony_ci s->buffer = NULL; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic const struct usb_device_id snd_usx2y_usb_id_table[] = { 33762306a36Sopenharmony_ci { 33862306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_DEVICE, 33962306a36Sopenharmony_ci .idVendor = 0x1604, 34062306a36Sopenharmony_ci .idProduct = USB_ID_US428 34162306a36Sopenharmony_ci }, 34262306a36Sopenharmony_ci { 34362306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_DEVICE, 34462306a36Sopenharmony_ci .idVendor = 0x1604, 34562306a36Sopenharmony_ci .idProduct = USB_ID_US122 34662306a36Sopenharmony_ci }, 34762306a36Sopenharmony_ci { 34862306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_DEVICE, 34962306a36Sopenharmony_ci .idVendor = 0x1604, 35062306a36Sopenharmony_ci .idProduct = USB_ID_US224 35162306a36Sopenharmony_ci }, 35262306a36Sopenharmony_ci { /* terminator */ } 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, snd_usx2y_usb_id_table); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int usx2y_create_card(struct usb_device *device, 35762306a36Sopenharmony_ci struct usb_interface *intf, 35862306a36Sopenharmony_ci struct snd_card **cardp) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci int dev; 36162306a36Sopenharmony_ci struct snd_card *card; 36262306a36Sopenharmony_ci int err; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci for (dev = 0; dev < SNDRV_CARDS; ++dev) 36562306a36Sopenharmony_ci if (enable[dev] && !snd_usx2y_card_used[dev]) 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 36862306a36Sopenharmony_ci return -ENODEV; 36962306a36Sopenharmony_ci err = snd_card_new(&intf->dev, index[dev], id[dev], THIS_MODULE, 37062306a36Sopenharmony_ci sizeof(struct usx2ydev), &card); 37162306a36Sopenharmony_ci if (err < 0) 37262306a36Sopenharmony_ci return err; 37362306a36Sopenharmony_ci snd_usx2y_card_used[usx2y(card)->card_index = dev] = 1; 37462306a36Sopenharmony_ci card->private_free = snd_usx2y_card_private_free; 37562306a36Sopenharmony_ci usx2y(card)->dev = device; 37662306a36Sopenharmony_ci init_waitqueue_head(&usx2y(card)->prepare_wait_queue); 37762306a36Sopenharmony_ci init_waitqueue_head(&usx2y(card)->us428ctls_wait_queue_head); 37862306a36Sopenharmony_ci mutex_init(&usx2y(card)->pcm_mutex); 37962306a36Sopenharmony_ci INIT_LIST_HEAD(&usx2y(card)->midi_list); 38062306a36Sopenharmony_ci strcpy(card->driver, "USB "NAME_ALLCAPS""); 38162306a36Sopenharmony_ci sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); 38262306a36Sopenharmony_ci sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)", 38362306a36Sopenharmony_ci card->shortname, 38462306a36Sopenharmony_ci le16_to_cpu(device->descriptor.idVendor), 38562306a36Sopenharmony_ci le16_to_cpu(device->descriptor.idProduct), 38662306a36Sopenharmony_ci 0,//us428(card)->usbmidi.ifnum, 38762306a36Sopenharmony_ci usx2y(card)->dev->bus->busnum, usx2y(card)->dev->devnum); 38862306a36Sopenharmony_ci *cardp = card; 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void snd_usx2y_card_private_free(struct snd_card *card) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct usx2ydev *usx2y = usx2y(card); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci kfree(usx2y->in04_buf); 39762306a36Sopenharmony_ci usb_free_urb(usx2y->in04_urb); 39862306a36Sopenharmony_ci if (usx2y->us428ctls_sharedmem) 39962306a36Sopenharmony_ci free_pages_exact(usx2y->us428ctls_sharedmem, 40062306a36Sopenharmony_ci US428_SHAREDMEM_PAGES); 40162306a36Sopenharmony_ci if (usx2y->card_index >= 0 && usx2y->card_index < SNDRV_CARDS) 40262306a36Sopenharmony_ci snd_usx2y_card_used[usx2y->card_index] = 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void snd_usx2y_disconnect(struct usb_interface *intf) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci struct snd_card *card; 40862306a36Sopenharmony_ci struct usx2ydev *usx2y; 40962306a36Sopenharmony_ci struct list_head *p; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci card = usb_get_intfdata(intf); 41262306a36Sopenharmony_ci if (!card) 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci usx2y = usx2y(card); 41562306a36Sopenharmony_ci usx2y->chip_status = USX2Y_STAT_CHIP_HUP; 41662306a36Sopenharmony_ci usx2y_unlinkseq(&usx2y->as04); 41762306a36Sopenharmony_ci usb_kill_urb(usx2y->in04_urb); 41862306a36Sopenharmony_ci snd_card_disconnect(card); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* release the midi resources */ 42162306a36Sopenharmony_ci list_for_each(p, &usx2y->midi_list) { 42262306a36Sopenharmony_ci snd_usbmidi_disconnect(p); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci if (usx2y->us428ctls_sharedmem) 42562306a36Sopenharmony_ci wake_up(&usx2y->us428ctls_wait_queue_head); 42662306a36Sopenharmony_ci snd_card_free(card); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int snd_usx2y_probe(struct usb_interface *intf, 43062306a36Sopenharmony_ci const struct usb_device_id *id) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct usb_device *device = interface_to_usbdev(intf); 43362306a36Sopenharmony_ci struct snd_card *card; 43462306a36Sopenharmony_ci int err; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 || 43762306a36Sopenharmony_ci (le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 && 43862306a36Sopenharmony_ci le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 && 43962306a36Sopenharmony_ci le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428)) 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci err = usx2y_create_card(device, intf, &card); 44362306a36Sopenharmony_ci if (err < 0) 44462306a36Sopenharmony_ci return err; 44562306a36Sopenharmony_ci err = usx2y_hwdep_new(card, device); 44662306a36Sopenharmony_ci if (err < 0) 44762306a36Sopenharmony_ci goto error; 44862306a36Sopenharmony_ci err = snd_card_register(card); 44962306a36Sopenharmony_ci if (err < 0) 45062306a36Sopenharmony_ci goto error; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci dev_set_drvdata(&intf->dev, card); 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci error: 45662306a36Sopenharmony_ci snd_card_free(card); 45762306a36Sopenharmony_ci return err; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic struct usb_driver snd_usx2y_usb_driver = { 46162306a36Sopenharmony_ci .name = "snd-usb-usx2y", 46262306a36Sopenharmony_ci .probe = snd_usx2y_probe, 46362306a36Sopenharmony_ci .disconnect = snd_usx2y_disconnect, 46462306a36Sopenharmony_ci .id_table = snd_usx2y_usb_id_table, 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_cimodule_usb_driver(snd_usx2y_usb_driver); 467