18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * usbusy2y.c - ALSA USB US-428 Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci2005-04-14 Karsten Wiese 68c2ecf20Sopenharmony_ci Version 0.8.7.2: 78c2ecf20Sopenharmony_ci Call snd_card_free() instead of snd_card_free_in_thread() to prevent oops with dead keyboard symptom. 88c2ecf20Sopenharmony_ci Tested ok with kernel 2.6.12-rc2. 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci2004-12-14 Karsten Wiese 118c2ecf20Sopenharmony_ci Version 0.8.7.1: 128c2ecf20Sopenharmony_ci snd_pcm_open for rawusb pcm-devices now returns -EBUSY if called without rawusb's hwdep device being open. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci2004-12-02 Karsten Wiese 158c2ecf20Sopenharmony_ci Version 0.8.7: 168c2ecf20Sopenharmony_ci Use macro usb_maxpacket() for portability. 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci2004-10-26 Karsten Wiese 198c2ecf20Sopenharmony_ci Version 0.8.6: 208c2ecf20Sopenharmony_ci wake_up() process waiting in usx2y_urbs_start() on error. 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci2004-10-21 Karsten Wiese 238c2ecf20Sopenharmony_ci Version 0.8.5: 248c2ecf20Sopenharmony_ci nrpacks is runtime or compiletime configurable now with tested values from 1 to 4. 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci2004-10-03 Karsten Wiese 278c2ecf20Sopenharmony_ci Version 0.8.2: 288c2ecf20Sopenharmony_ci Avoid any possible racing while in prepare callback. 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci2004-09-30 Karsten Wiese 318c2ecf20Sopenharmony_ci Version 0.8.0: 328c2ecf20Sopenharmony_ci Simplified things and made ohci work again. 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci2004-09-20 Karsten Wiese 358c2ecf20Sopenharmony_ci Version 0.7.3: 368c2ecf20Sopenharmony_ci Use usb_kill_urb() instead of deprecated (kernel 2.6.9) usb_unlink_urb(). 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci2004-07-13 Karsten Wiese 398c2ecf20Sopenharmony_ci Version 0.7.1: 408c2ecf20Sopenharmony_ci Don't sleep in START/STOP callbacks anymore. 418c2ecf20Sopenharmony_ci us428 channels C/D not handled just for this version, sorry. 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci2004-06-21 Karsten Wiese 448c2ecf20Sopenharmony_ci Version 0.6.4: 458c2ecf20Sopenharmony_ci Temporarely suspend midi input 468c2ecf20Sopenharmony_ci to sanely call usb_set_interface() when setting format. 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci2004-06-12 Karsten Wiese 498c2ecf20Sopenharmony_ci Version 0.6.3: 508c2ecf20Sopenharmony_ci Made it thus the following rule is enforced: 518c2ecf20Sopenharmony_ci "All pcm substreams of one usx2y have to operate at the same rate & format." 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci2004-04-06 Karsten Wiese 548c2ecf20Sopenharmony_ci Version 0.6.0: 558c2ecf20Sopenharmony_ci Runs on 2.6.5 kernel without any "--with-debug=" things. 568c2ecf20Sopenharmony_ci us224 reported running. 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci2004-01-14 Karsten Wiese 598c2ecf20Sopenharmony_ci Version 0.5.1: 608c2ecf20Sopenharmony_ci Runs with 2.6.1 kernel. 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci2003-12-30 Karsten Wiese 638c2ecf20Sopenharmony_ci Version 0.4.1: 648c2ecf20Sopenharmony_ci Fix 24Bit 4Channel capturing for the us428. 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci2003-11-27 Karsten Wiese, Martin Langer 678c2ecf20Sopenharmony_ci Version 0.4: 688c2ecf20Sopenharmony_ci us122 support. 698c2ecf20Sopenharmony_ci us224 could be tested by uncommenting the sections containing USB_ID_US224 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci2003-11-03 Karsten Wiese 728c2ecf20Sopenharmony_ci Version 0.3: 738c2ecf20Sopenharmony_ci 24Bit support. 748c2ecf20Sopenharmony_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. 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci2003-08-22 Karsten Wiese 778c2ecf20Sopenharmony_ci Version 0.0.8: 788c2ecf20Sopenharmony_ci Removed EZUSB Firmware. First Stage Firmwaredownload is now done by tascam-firmware downloader. 798c2ecf20Sopenharmony_ci See: 808c2ecf20Sopenharmony_ci http://usb-midi-fw.sourceforge.net/tascam-firmware.tar.gz 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci2003-06-18 Karsten Wiese 838c2ecf20Sopenharmony_ci Version 0.0.5: 848c2ecf20Sopenharmony_ci changed to compile with kernel 2.4.21 and alsa 0.9.4 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci2002-10-16 Karsten Wiese 878c2ecf20Sopenharmony_ci Version 0.0.4: 888c2ecf20Sopenharmony_ci compiles again with alsa-current. 898c2ecf20Sopenharmony_ci USB_ISO_ASAP not used anymore (most of the time), instead 908c2ecf20Sopenharmony_ci urb->start_frame is calculated here now, some calls inside usb-driver don't need to happen anymore. 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci To get the best out of this: 938c2ecf20Sopenharmony_ci Disable APM-support in the kernel as APM-BIOS calls (once each second) hard disable interrupt for many precious milliseconds. 948c2ecf20Sopenharmony_ci This helped me much on my slowish PII 400 & PIII 500. 958c2ecf20Sopenharmony_ci ACPI yet untested but might cause the same bad behaviour. 968c2ecf20Sopenharmony_ci Use a kernel with lowlatency and preemptiv patches applied. 978c2ecf20Sopenharmony_ci To autoload snd-usb-midi append a line 988c2ecf20Sopenharmony_ci post-install snd-usb-us428 modprobe snd-usb-midi 998c2ecf20Sopenharmony_ci to /etc/modules.conf. 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci known problems: 1028c2ecf20Sopenharmony_ci sliders, knobs, lights not yet handled except MASTER Volume slider. 1038c2ecf20Sopenharmony_ci "pcm -c 2" doesn't work. "pcm -c 2 -m direct_interleaved" does. 1048c2ecf20Sopenharmony_ci KDE3: "Enable full duplex operation" deadlocks. 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci2002-08-31 Karsten Wiese 1088c2ecf20Sopenharmony_ci Version 0.0.3: audio also simplex; 1098c2ecf20Sopenharmony_ci simplifying: iso urbs only 1 packet, melted structs. 1108c2ecf20Sopenharmony_ci ASYNC_UNLINK not used anymore: no more crashes so far..... 1118c2ecf20Sopenharmony_ci for alsa 0.9 rc3. 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci2002-08-09 Karsten Wiese 1148c2ecf20Sopenharmony_ci Version 0.0.2: midi works with snd-usb-midi, audio (only fullduplex now) with i.e. bristol. 1158c2ecf20Sopenharmony_ci The firmware has been sniffed from win2k us-428 driver 3.09. 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci * Copyright (c) 2002 - 2004 Karsten Wiese 1188c2ecf20Sopenharmony_ci*/ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#include <linux/init.h> 1218c2ecf20Sopenharmony_ci#include <linux/module.h> 1228c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 1238c2ecf20Sopenharmony_ci#include <linux/slab.h> 1248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 1258c2ecf20Sopenharmony_ci#include <linux/usb.h> 1268c2ecf20Sopenharmony_ci#include <sound/core.h> 1278c2ecf20Sopenharmony_ci#include <sound/initval.h> 1288c2ecf20Sopenharmony_ci#include <sound/pcm.h> 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#include <sound/rawmidi.h> 1318c2ecf20Sopenharmony_ci#include "usx2y.h" 1328c2ecf20Sopenharmony_ci#include "usbusx2y.h" 1338c2ecf20Sopenharmony_ci#include "usX2Yhwdep.h" 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>"); 1388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2"); 1398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1408c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604),"NAME_ALLCAPS"(0x8001)(0x8005)(0x8007)}}"); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */ 1438c2ecf20Sopenharmony_cistatic char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */ 1448c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 1478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS"."); 1488c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 1498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS"."); 1508c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 1518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS"."); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int snd_usx2y_card_used[SNDRV_CARDS]; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void usx2y_usb_disconnect(struct usb_device* usb_device, void* ptr); 1578c2ecf20Sopenharmony_cistatic void snd_usx2y_card_private_free(struct snd_card *card); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* 1608c2ecf20Sopenharmony_ci * pipe 4 is used for switching the lamps, setting samplerate, volumes .... 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_cistatic void i_usx2y_out04_int(struct urb *urb) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 1658c2ecf20Sopenharmony_ci if (urb->status) { 1668c2ecf20Sopenharmony_ci int i; 1678c2ecf20Sopenharmony_ci struct usx2ydev *usx2y = urb->context; 1688c2ecf20Sopenharmony_ci for (i = 0; i < 10 && usx2y->as04.urb[i] != urb; i++); 1698c2ecf20Sopenharmony_ci snd_printdd("i_usx2y_out04_int() urb %i status=%i\n", i, urb->status); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci#endif 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void i_usx2y_in04_int(struct urb *urb) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int err = 0; 1778c2ecf20Sopenharmony_ci struct usx2ydev *usx2y = urb->context; 1788c2ecf20Sopenharmony_ci struct us428ctls_sharedmem *us428ctls = usx2y->us428ctls_sharedmem; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci usx2y->in04_int_calls++; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (urb->status) { 1838c2ecf20Sopenharmony_ci snd_printdd("Interrupt Pipe 4 came back with status=%i\n", urb->status); 1848c2ecf20Sopenharmony_ci return; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_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 ?!? 1888c2ecf20Sopenharmony_ci if (us428ctls) { 1898c2ecf20Sopenharmony_ci int diff = -1; 1908c2ecf20Sopenharmony_ci if (-2 == us428ctls->ctl_snapshot_last) { 1918c2ecf20Sopenharmony_ci diff = 0; 1928c2ecf20Sopenharmony_ci memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last)); 1938c2ecf20Sopenharmony_ci us428ctls->ctl_snapshot_last = -1; 1948c2ecf20Sopenharmony_ci } else { 1958c2ecf20Sopenharmony_ci int i; 1968c2ecf20Sopenharmony_ci for (i = 0; i < 21; i++) { 1978c2ecf20Sopenharmony_ci if (usx2y->in04_last[i] != ((char*)usx2y->in04_buf)[i]) { 1988c2ecf20Sopenharmony_ci if (diff < 0) 1998c2ecf20Sopenharmony_ci diff = i; 2008c2ecf20Sopenharmony_ci usx2y->in04_last[i] = ((char*)usx2y->in04_buf)[i]; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci if (0 <= diff) { 2058c2ecf20Sopenharmony_ci int n = us428ctls->ctl_snapshot_last + 1; 2068c2ecf20Sopenharmony_ci if (n >= N_US428_CTL_BUFS || n < 0) 2078c2ecf20Sopenharmony_ci n = 0; 2088c2ecf20Sopenharmony_ci memcpy(us428ctls->ctl_snapshot + n, usx2y->in04_buf, sizeof(us428ctls->ctl_snapshot[0])); 2098c2ecf20Sopenharmony_ci us428ctls->ctl_snapshot_differs_at[n] = diff; 2108c2ecf20Sopenharmony_ci us428ctls->ctl_snapshot_last = n; 2118c2ecf20Sopenharmony_ci wake_up(&usx2y->us428ctls_wait_queue_head); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (usx2y->us04) { 2178c2ecf20Sopenharmony_ci if (0 == usx2y->us04->submitted) 2188c2ecf20Sopenharmony_ci do { 2198c2ecf20Sopenharmony_ci err = usb_submit_urb(usx2y->us04->urb[usx2y->us04->submitted++], GFP_ATOMIC); 2208c2ecf20Sopenharmony_ci } while (!err && usx2y->us04->submitted < usx2y->us04->len); 2218c2ecf20Sopenharmony_ci } else 2228c2ecf20Sopenharmony_ci if (us428ctls && us428ctls->p4out_last >= 0 && us428ctls->p4out_last < N_US428_P4OUT_BUFS) { 2238c2ecf20Sopenharmony_ci if (us428ctls->p4out_last != us428ctls->p4out_sent) { 2248c2ecf20Sopenharmony_ci int j, send = us428ctls->p4out_sent + 1; 2258c2ecf20Sopenharmony_ci if (send >= N_US428_P4OUT_BUFS) 2268c2ecf20Sopenharmony_ci send = 0; 2278c2ecf20Sopenharmony_ci for (j = 0; j < URBS_ASYNC_SEQ && !err; ++j) 2288c2ecf20Sopenharmony_ci if (0 == usx2y->as04.urb[j]->status) { 2298c2ecf20Sopenharmony_ci struct us428_p4out *p4out = us428ctls->p4out + send; // FIXME if more than 1 p4out is new, 1 gets lost. 2308c2ecf20Sopenharmony_ci usb_fill_bulk_urb(usx2y->as04.urb[j], usx2y->dev, 2318c2ecf20Sopenharmony_ci usb_sndbulkpipe(usx2y->dev, 0x04), &p4out->val.vol, 2328c2ecf20Sopenharmony_ci p4out->type == ELT_LIGHT ? sizeof(struct us428_lights) : 5, 2338c2ecf20Sopenharmony_ci i_usx2y_out04_int, usx2y); 2348c2ecf20Sopenharmony_ci err = usb_submit_urb(usx2y->as04.urb[j], GFP_ATOMIC); 2358c2ecf20Sopenharmony_ci us428ctls->p4out_sent = send; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (err) 2428c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "in04_int() usb_submit_urb err=%i\n", err); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci urb->dev = usx2y->dev; 2458c2ecf20Sopenharmony_ci usb_submit_urb(urb, GFP_ATOMIC); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* 2498c2ecf20Sopenharmony_ci * Prepare some urbs 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ciint usx2y_async_seq04_init(struct usx2ydev *usx2y) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int err = 0, 2548c2ecf20Sopenharmony_ci i; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci usx2y->as04.buffer = kmalloc_array(URBS_ASYNC_SEQ, 2578c2ecf20Sopenharmony_ci URB_DATA_LEN_ASYNC_SEQ, GFP_KERNEL); 2588c2ecf20Sopenharmony_ci if (NULL == usx2y->as04.buffer) { 2598c2ecf20Sopenharmony_ci err = -ENOMEM; 2608c2ecf20Sopenharmony_ci } else 2618c2ecf20Sopenharmony_ci for (i = 0; i < URBS_ASYNC_SEQ; ++i) { 2628c2ecf20Sopenharmony_ci if (NULL == (usx2y->as04.urb[i] = usb_alloc_urb(0, GFP_KERNEL))) { 2638c2ecf20Sopenharmony_ci err = -ENOMEM; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci usb_fill_bulk_urb( usx2y->as04.urb[i], usx2y->dev, 2678c2ecf20Sopenharmony_ci usb_sndbulkpipe(usx2y->dev, 0x04), 2688c2ecf20Sopenharmony_ci usx2y->as04.buffer + URB_DATA_LEN_ASYNC_SEQ*i, 0, 2698c2ecf20Sopenharmony_ci i_usx2y_out04_int, usx2y 2708c2ecf20Sopenharmony_ci ); 2718c2ecf20Sopenharmony_ci err = usb_urb_ep_type_check(usx2y->as04.urb[i]); 2728c2ecf20Sopenharmony_ci if (err < 0) 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci return err; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ciint usx2y_in04_init(struct usx2ydev *usx2y) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci if (! (usx2y->in04_urb = usb_alloc_urb(0, GFP_KERNEL))) 2818c2ecf20Sopenharmony_ci return -ENOMEM; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (! (usx2y->in04_buf = kmalloc(21, GFP_KERNEL))) 2848c2ecf20Sopenharmony_ci return -ENOMEM; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci init_waitqueue_head(&usx2y->in04_wait_queue); 2878c2ecf20Sopenharmony_ci usb_fill_int_urb(usx2y->in04_urb, usx2y->dev, usb_rcvintpipe(usx2y->dev, 0x4), 2888c2ecf20Sopenharmony_ci usx2y->in04_buf, 21, 2898c2ecf20Sopenharmony_ci i_usx2y_in04_int, usx2y, 2908c2ecf20Sopenharmony_ci 10); 2918c2ecf20Sopenharmony_ci if (usb_urb_ep_type_check(usx2y->in04_urb)) 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci return usb_submit_urb(usx2y->in04_urb, GFP_KERNEL); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void usx2y_unlinkseq(struct snd_usx2y_async_seq *s) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci int i; 2998c2ecf20Sopenharmony_ci for (i = 0; i < URBS_ASYNC_SEQ; ++i) { 3008c2ecf20Sopenharmony_ci usb_kill_urb(s->urb[i]); 3018c2ecf20Sopenharmony_ci usb_free_urb(s->urb[i]); 3028c2ecf20Sopenharmony_ci s->urb[i] = NULL; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci kfree(s->buffer); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic const struct usb_device_id snd_usx2y_usb_id_table[] = { 3098c2ecf20Sopenharmony_ci { 3108c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_DEVICE, 3118c2ecf20Sopenharmony_ci .idVendor = 0x1604, 3128c2ecf20Sopenharmony_ci .idProduct = USB_ID_US428 3138c2ecf20Sopenharmony_ci }, 3148c2ecf20Sopenharmony_ci { 3158c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_DEVICE, 3168c2ecf20Sopenharmony_ci .idVendor = 0x1604, 3178c2ecf20Sopenharmony_ci .idProduct = USB_ID_US122 3188c2ecf20Sopenharmony_ci }, 3198c2ecf20Sopenharmony_ci { 3208c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_DEVICE, 3218c2ecf20Sopenharmony_ci .idVendor = 0x1604, 3228c2ecf20Sopenharmony_ci .idProduct = USB_ID_US224 3238c2ecf20Sopenharmony_ci }, 3248c2ecf20Sopenharmony_ci { /* terminator */ } 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int usx2y_create_card(struct usb_device *device, 3288c2ecf20Sopenharmony_ci struct usb_interface *intf, 3298c2ecf20Sopenharmony_ci struct snd_card **cardp) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci int dev; 3328c2ecf20Sopenharmony_ci struct snd_card * card; 3338c2ecf20Sopenharmony_ci int err; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci for (dev = 0; dev < SNDRV_CARDS; ++dev) 3368c2ecf20Sopenharmony_ci if (enable[dev] && !snd_usx2y_card_used[dev]) 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 3398c2ecf20Sopenharmony_ci return -ENODEV; 3408c2ecf20Sopenharmony_ci err = snd_card_new(&intf->dev, index[dev], id[dev], THIS_MODULE, 3418c2ecf20Sopenharmony_ci sizeof(struct usx2ydev), &card); 3428c2ecf20Sopenharmony_ci if (err < 0) 3438c2ecf20Sopenharmony_ci return err; 3448c2ecf20Sopenharmony_ci snd_usx2y_card_used[usx2y(card)->card_index = dev] = 1; 3458c2ecf20Sopenharmony_ci card->private_free = snd_usx2y_card_private_free; 3468c2ecf20Sopenharmony_ci usx2y(card)->dev = device; 3478c2ecf20Sopenharmony_ci init_waitqueue_head(&usx2y(card)->prepare_wait_queue); 3488c2ecf20Sopenharmony_ci mutex_init(&usx2y(card)->pcm_mutex); 3498c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&usx2y(card)->midi_list); 3508c2ecf20Sopenharmony_ci strcpy(card->driver, "USB "NAME_ALLCAPS""); 3518c2ecf20Sopenharmony_ci sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); 3528c2ecf20Sopenharmony_ci sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)", 3538c2ecf20Sopenharmony_ci card->shortname, 3548c2ecf20Sopenharmony_ci le16_to_cpu(device->descriptor.idVendor), 3558c2ecf20Sopenharmony_ci le16_to_cpu(device->descriptor.idProduct), 3568c2ecf20Sopenharmony_ci 0,//us428(card)->usbmidi.ifnum, 3578c2ecf20Sopenharmony_ci usx2y(card)->dev->bus->busnum, usx2y(card)->dev->devnum 3588c2ecf20Sopenharmony_ci ); 3598c2ecf20Sopenharmony_ci *cardp = card; 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int usx2y_usb_probe(struct usb_device *device, 3658c2ecf20Sopenharmony_ci struct usb_interface *intf, 3668c2ecf20Sopenharmony_ci const struct usb_device_id *device_id, 3678c2ecf20Sopenharmony_ci struct snd_card **cardp) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci int err; 3708c2ecf20Sopenharmony_ci struct snd_card * card; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci *cardp = NULL; 3738c2ecf20Sopenharmony_ci if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 || 3748c2ecf20Sopenharmony_ci (le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 && 3758c2ecf20Sopenharmony_ci le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 && 3768c2ecf20Sopenharmony_ci le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428)) 3778c2ecf20Sopenharmony_ci return -EINVAL; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci err = usx2y_create_card(device, intf, &card); 3808c2ecf20Sopenharmony_ci if (err < 0) 3818c2ecf20Sopenharmony_ci return err; 3828c2ecf20Sopenharmony_ci if ((err = usx2y_hwdep_new(card, device)) < 0 || 3838c2ecf20Sopenharmony_ci (err = snd_card_register(card)) < 0) { 3848c2ecf20Sopenharmony_ci snd_card_free(card); 3858c2ecf20Sopenharmony_ci return err; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci *cardp = card; 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/* 3928c2ecf20Sopenharmony_ci * new 2.5 USB kernel API 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_cistatic int snd_usx2y_probe(struct usb_interface *intf, const struct usb_device_id *id) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct snd_card *card; 3978c2ecf20Sopenharmony_ci int err; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci err = usx2y_usb_probe(interface_to_usbdev(intf), intf, id, &card); 4008c2ecf20Sopenharmony_ci if (err < 0) 4018c2ecf20Sopenharmony_ci return err; 4028c2ecf20Sopenharmony_ci dev_set_drvdata(&intf->dev, card); 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic void snd_usx2y_disconnect(struct usb_interface *intf) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci usx2y_usb_disconnect(interface_to_usbdev(intf), 4098c2ecf20Sopenharmony_ci usb_get_intfdata(intf)); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, snd_usx2y_usb_id_table); 4138c2ecf20Sopenharmony_cistatic struct usb_driver snd_usx2y_usb_driver = { 4148c2ecf20Sopenharmony_ci .name = "snd-usb-usx2y", 4158c2ecf20Sopenharmony_ci .probe = snd_usx2y_probe, 4168c2ecf20Sopenharmony_ci .disconnect = snd_usx2y_disconnect, 4178c2ecf20Sopenharmony_ci .id_table = snd_usx2y_usb_id_table, 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic void snd_usx2y_card_private_free(struct snd_card *card) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci kfree(usx2y(card)->in04_buf); 4238c2ecf20Sopenharmony_ci usb_free_urb(usx2y(card)->in04_urb); 4248c2ecf20Sopenharmony_ci if (usx2y(card)->us428ctls_sharedmem) 4258c2ecf20Sopenharmony_ci free_pages_exact(usx2y(card)->us428ctls_sharedmem, 4268c2ecf20Sopenharmony_ci sizeof(*usx2y(card)->us428ctls_sharedmem)); 4278c2ecf20Sopenharmony_ci if (usx2y(card)->card_index >= 0 && usx2y(card)->card_index < SNDRV_CARDS) 4288c2ecf20Sopenharmony_ci snd_usx2y_card_used[usx2y(card)->card_index] = 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci/* 4328c2ecf20Sopenharmony_ci * Frees the device. 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_cistatic void usx2y_usb_disconnect(struct usb_device *device, void* ptr) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci if (ptr) { 4378c2ecf20Sopenharmony_ci struct snd_card *card = ptr; 4388c2ecf20Sopenharmony_ci struct usx2ydev *usx2y = usx2y(card); 4398c2ecf20Sopenharmony_ci struct list_head *p; 4408c2ecf20Sopenharmony_ci usx2y->chip_status = USX2Y_STAT_CHIP_HUP; 4418c2ecf20Sopenharmony_ci usx2y_unlinkseq(&usx2y->as04); 4428c2ecf20Sopenharmony_ci usb_kill_urb(usx2y->in04_urb); 4438c2ecf20Sopenharmony_ci snd_card_disconnect(card); 4448c2ecf20Sopenharmony_ci /* release the midi resources */ 4458c2ecf20Sopenharmony_ci list_for_each(p, &usx2y->midi_list) { 4468c2ecf20Sopenharmony_ci snd_usbmidi_disconnect(p); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci if (usx2y->us428ctls_sharedmem) 4498c2ecf20Sopenharmony_ci wake_up(&usx2y->us428ctls_wait_queue_head); 4508c2ecf20Sopenharmony_ci snd_card_free(card); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cimodule_usb_driver(snd_usx2y_usb_driver); 455