466 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * usbusx2y.c - ALSA USB US-428 Driver4 *52005-04-14 Karsten Wiese6 Version 0.8.7.2:7 Call snd_card_free() instead of snd_card_free_in_thread() to prevent oops with dead keyboard symptom.8 Tested ok with kernel 2.6.12-rc2.9 102004-12-14 Karsten Wiese11 Version 0.8.7.1:12 snd_pcm_open for rawusb pcm-devices now returns -EBUSY if called without rawusb's hwdep device being open.13 142004-12-02 Karsten Wiese15 Version 0.8.7:16 Use macro usb_maxpacket() for portability.17 182004-10-26 Karsten Wiese19 Version 0.8.6:20 wake_up() process waiting in usx2y_urbs_start() on error.21 222004-10-21 Karsten Wiese23 Version 0.8.5:24 nrpacks is runtime or compiletime configurable now with tested values from 1 to 4.25 262004-10-03 Karsten Wiese27 Version 0.8.2:28 Avoid any possible racing while in prepare callback.29 302004-09-30 Karsten Wiese31 Version 0.8.0:32 Simplified things and made ohci work again.33 342004-09-20 Karsten Wiese35 Version 0.7.3:36 Use usb_kill_urb() instead of deprecated (kernel 2.6.9) usb_unlink_urb().37 382004-07-13 Karsten Wiese39 Version 0.7.1:40 Don't sleep in START/STOP callbacks anymore.41 us428 channels C/D not handled just for this version, sorry.42 432004-06-21 Karsten Wiese44 Version 0.6.4:45 Temporarely suspend midi input46 to sanely call usb_set_interface() when setting format.47 482004-06-12 Karsten Wiese49 Version 0.6.3:50 Made it thus the following rule is enforced:51 "All pcm substreams of one usx2y have to operate at the same rate & format."52 532004-04-06 Karsten Wiese54 Version 0.6.0:55 Runs on 2.6.5 kernel without any "--with-debug=" things.56 us224 reported running.57 582004-01-14 Karsten Wiese59 Version 0.5.1:60 Runs with 2.6.1 kernel.61 622003-12-30 Karsten Wiese63 Version 0.4.1:64 Fix 24Bit 4Channel capturing for the us428.65 662003-11-27 Karsten Wiese, Martin Langer67 Version 0.4:68 us122 support.69 us224 could be tested by uncommenting the sections containing USB_ID_US22470 712003-11-03 Karsten Wiese72 Version 0.3:73 24Bit support.74 "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.75 762003-08-22 Karsten Wiese77 Version 0.0.8:78 Removed EZUSB Firmware. First Stage Firmwaredownload is now done by tascam-firmware downloader.79 See:80 http://usb-midi-fw.sourceforge.net/tascam-firmware.tar.gz81 822003-06-18 Karsten Wiese83 Version 0.0.5:84 changed to compile with kernel 2.4.21 and alsa 0.9.485 862002-10-16 Karsten Wiese87 Version 0.0.4:88 compiles again with alsa-current.89 USB_ISO_ASAP not used anymore (most of the time), instead90 urb->start_frame is calculated here now, some calls inside usb-driver don't need to happen anymore.91 92 To get the best out of this:93 Disable APM-support in the kernel as APM-BIOS calls (once each second) hard disable interrupt for many precious milliseconds.94 This helped me much on my slowish PII 400 & PIII 500.95 ACPI yet untested but might cause the same bad behaviour.96 Use a kernel with lowlatency and preemptiv patches applied.97 To autoload snd-usb-midi append a line98 post-install snd-usb-us428 modprobe snd-usb-midi99 to /etc/modules.conf.100 101 known problems:102 sliders, knobs, lights not yet handled except MASTER Volume slider.103 "pcm -c 2" doesn't work. "pcm -c 2 -m direct_interleaved" does.104 KDE3: "Enable full duplex operation" deadlocks.105 1062002-08-31 Karsten Wiese107 Version 0.0.3: audio also simplex;108 simplifying: iso urbs only 1 packet, melted structs.109 ASYNC_UNLINK not used anymore: no more crashes so far.....110 for alsa 0.9 rc3.111 1122002-08-09 Karsten Wiese113 Version 0.0.2: midi works with snd-usb-midi, audio (only fullduplex now) with i.e. bristol.114 The firmware has been sniffed from win2k us-428 driver 3.09.115 116 * Copyright (c) 2002 - 2004 Karsten Wiese117 */118 119#include <linux/init.h>120#include <linux/module.h>121#include <linux/moduleparam.h>122#include <linux/slab.h>123#include <linux/interrupt.h>124#include <linux/usb.h>125#include <sound/core.h>126#include <sound/initval.h>127#include <sound/pcm.h>128 129#include <sound/rawmidi.h>130#include "usx2y.h"131#include "usbusx2y.h"132#include "usX2Yhwdep.h"133 134MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");135MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2");136MODULE_LICENSE("GPL");137 138static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */139static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */140static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */141 142module_param_array(index, int, NULL, 0444);143MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS".");144module_param_array(id, charp, NULL, 0444);145MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS".");146module_param_array(enable, bool, NULL, 0444);147MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS".");148 149static int snd_usx2y_card_used[SNDRV_CARDS];150 151static void snd_usx2y_card_private_free(struct snd_card *card);152static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s);153 154/*155 * pipe 4 is used for switching the lamps, setting samplerate, volumes ....156 */157static void i_usx2y_out04_int(struct urb *urb)158{159#ifdef CONFIG_SND_DEBUG160 if (urb->status) {161 int i;162 struct usx2ydev *usx2y = urb->context;163 164 for (i = 0; i < 10 && usx2y->as04.urb[i] != urb; i++)165 ;166 dev_dbg(&urb->dev->dev, "%s urb %i status=%i\n", __func__, i, urb->status);167 }168#endif169}170 171static void i_usx2y_in04_int(struct urb *urb)172{173 int err = 0;174 struct usx2ydev *usx2y = urb->context;175 struct us428ctls_sharedmem *us428ctls = usx2y->us428ctls_sharedmem;176 struct us428_p4out *p4out;177 int i, j, n, diff, send;178 179 usx2y->in04_int_calls++;180 181 if (urb->status) {182 dev_dbg(&urb->dev->dev, "Interrupt Pipe 4 came back with status=%i\n", urb->status);183 return;184 }185 186 if (us428ctls) {187 diff = -1;188 if (us428ctls->ctl_snapshot_last == -2) {189 diff = 0;190 memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof(usx2y->in04_last));191 us428ctls->ctl_snapshot_last = -1;192 } else {193 for (i = 0; i < 21; i++) {194 if (usx2y->in04_last[i] != ((char *)usx2y->in04_buf)[i]) {195 if (diff < 0)196 diff = i;197 usx2y->in04_last[i] = ((char *)usx2y->in04_buf)[i];198 }199 }200 }201 if (diff >= 0) {202 n = us428ctls->ctl_snapshot_last + 1;203 if (n >= N_US428_CTL_BUFS || n < 0)204 n = 0;205 memcpy(us428ctls->ctl_snapshot + n, usx2y->in04_buf, sizeof(us428ctls->ctl_snapshot[0]));206 us428ctls->ctl_snapshot_differs_at[n] = diff;207 us428ctls->ctl_snapshot_last = n;208 wake_up(&usx2y->us428ctls_wait_queue_head);209 }210 }211 212 if (usx2y->us04) {213 if (!usx2y->us04->submitted) {214 do {215 err = usb_submit_urb(usx2y->us04->urb[usx2y->us04->submitted++], GFP_ATOMIC);216 } while (!err && usx2y->us04->submitted < usx2y->us04->len);217 }218 } else {219 if (us428ctls && us428ctls->p4out_last >= 0 && us428ctls->p4out_last < N_US428_P4OUT_BUFS) {220 if (us428ctls->p4out_last != us428ctls->p4out_sent) {221 send = us428ctls->p4out_sent + 1;222 if (send >= N_US428_P4OUT_BUFS)223 send = 0;224 for (j = 0; j < URBS_ASYNC_SEQ && !err; ++j) {225 if (!usx2y->as04.urb[j]->status) {226 p4out = us428ctls->p4out + send; // FIXME if more than 1 p4out is new, 1 gets lost.227 usb_fill_bulk_urb(usx2y->as04.urb[j], usx2y->dev,228 usb_sndbulkpipe(usx2y->dev, 0x04), &p4out->val.vol,229 p4out->type == ELT_LIGHT ? sizeof(struct us428_lights) : 5,230 i_usx2y_out04_int, usx2y);231 err = usb_submit_urb(usx2y->as04.urb[j], GFP_ATOMIC);232 us428ctls->p4out_sent = send;233 break;234 }235 }236 }237 }238 }239 240 if (err)241 dev_err(&urb->dev->dev, "in04_int() usb_submit_urb err=%i\n", err);242 243 urb->dev = usx2y->dev;244 usb_submit_urb(urb, GFP_ATOMIC);245}246 247/*248 * Prepare some urbs249 */250int usx2y_async_seq04_init(struct usx2ydev *usx2y)251{252 int err = 0, i;253 254 if (WARN_ON(usx2y->as04.buffer))255 return -EBUSY;256 257 usx2y->as04.buffer = kmalloc_array(URBS_ASYNC_SEQ,258 URB_DATA_LEN_ASYNC_SEQ, GFP_KERNEL);259 if (!usx2y->as04.buffer) {260 err = -ENOMEM;261 } else {262 for (i = 0; i < URBS_ASYNC_SEQ; ++i) {263 usx2y->as04.urb[i] = usb_alloc_urb(0, GFP_KERNEL);264 if (!usx2y->as04.urb[i]) {265 err = -ENOMEM;266 break;267 }268 usb_fill_bulk_urb(usx2y->as04.urb[i], usx2y->dev,269 usb_sndbulkpipe(usx2y->dev, 0x04),270 usx2y->as04.buffer + URB_DATA_LEN_ASYNC_SEQ * i, 0,271 i_usx2y_out04_int, usx2y);272 err = usb_urb_ep_type_check(usx2y->as04.urb[i]);273 if (err < 0)274 break;275 }276 }277 if (err)278 usx2y_unlinkseq(&usx2y->as04);279 return err;280}281 282int usx2y_in04_init(struct usx2ydev *usx2y)283{284 int err;285 286 if (WARN_ON(usx2y->in04_urb))287 return -EBUSY;288 289 usx2y->in04_urb = usb_alloc_urb(0, GFP_KERNEL);290 if (!usx2y->in04_urb) {291 err = -ENOMEM;292 goto error;293 }294 295 usx2y->in04_buf = kmalloc(21, GFP_KERNEL);296 if (!usx2y->in04_buf) {297 err = -ENOMEM;298 goto error;299 }300 301 init_waitqueue_head(&usx2y->in04_wait_queue);302 usb_fill_int_urb(usx2y->in04_urb, usx2y->dev, usb_rcvintpipe(usx2y->dev, 0x4),303 usx2y->in04_buf, 21,304 i_usx2y_in04_int, usx2y,305 10);306 if (usb_urb_ep_type_check(usx2y->in04_urb)) {307 err = -EINVAL;308 goto error;309 }310 return usb_submit_urb(usx2y->in04_urb, GFP_KERNEL);311 312 error:313 kfree(usx2y->in04_buf);314 usb_free_urb(usx2y->in04_urb);315 usx2y->in04_buf = NULL;316 usx2y->in04_urb = NULL;317 return err;318}319 320static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s)321{322 int i;323 324 for (i = 0; i < URBS_ASYNC_SEQ; ++i) {325 if (!s->urb[i])326 continue;327 usb_kill_urb(s->urb[i]);328 usb_free_urb(s->urb[i]);329 s->urb[i] = NULL;330 }331 kfree(s->buffer);332 s->buffer = NULL;333}334 335static const struct usb_device_id snd_usx2y_usb_id_table[] = {336 {337 .match_flags = USB_DEVICE_ID_MATCH_DEVICE,338 .idVendor = 0x1604,339 .idProduct = USB_ID_US428340 },341 {342 .match_flags = USB_DEVICE_ID_MATCH_DEVICE,343 .idVendor = 0x1604,344 .idProduct = USB_ID_US122345 },346 {347 .match_flags = USB_DEVICE_ID_MATCH_DEVICE,348 .idVendor = 0x1604,349 .idProduct = USB_ID_US224350 },351 { /* terminator */ }352};353MODULE_DEVICE_TABLE(usb, snd_usx2y_usb_id_table);354 355static int usx2y_create_card(struct usb_device *device,356 struct usb_interface *intf,357 struct snd_card **cardp)358{359 int dev;360 struct snd_card *card;361 int err;362 363 for (dev = 0; dev < SNDRV_CARDS; ++dev)364 if (enable[dev] && !snd_usx2y_card_used[dev])365 break;366 if (dev >= SNDRV_CARDS)367 return -ENODEV;368 err = snd_card_new(&intf->dev, index[dev], id[dev], THIS_MODULE,369 sizeof(struct usx2ydev), &card);370 if (err < 0)371 return err;372 snd_usx2y_card_used[usx2y(card)->card_index = dev] = 1;373 card->private_free = snd_usx2y_card_private_free;374 usx2y(card)->dev = device;375 init_waitqueue_head(&usx2y(card)->prepare_wait_queue);376 init_waitqueue_head(&usx2y(card)->us428ctls_wait_queue_head);377 mutex_init(&usx2y(card)->pcm_mutex);378 INIT_LIST_HEAD(&usx2y(card)->midi_list);379 strcpy(card->driver, "USB "NAME_ALLCAPS"");380 sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");381 sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)",382 card->shortname,383 le16_to_cpu(device->descriptor.idVendor),384 le16_to_cpu(device->descriptor.idProduct),385 0,//us428(card)->usbmidi.ifnum,386 usx2y(card)->dev->bus->busnum, usx2y(card)->dev->devnum);387 *cardp = card;388 return 0;389}390 391static void snd_usx2y_card_private_free(struct snd_card *card)392{393 struct usx2ydev *usx2y = usx2y(card);394 395 kfree(usx2y->in04_buf);396 usb_free_urb(usx2y->in04_urb);397 if (usx2y->us428ctls_sharedmem)398 free_pages_exact(usx2y->us428ctls_sharedmem,399 US428_SHAREDMEM_PAGES);400 if (usx2y->card_index >= 0 && usx2y->card_index < SNDRV_CARDS)401 snd_usx2y_card_used[usx2y->card_index] = 0;402}403 404static void snd_usx2y_disconnect(struct usb_interface *intf)405{406 struct snd_card *card;407 struct usx2ydev *usx2y;408 struct list_head *p;409 410 card = usb_get_intfdata(intf);411 if (!card)412 return;413 usx2y = usx2y(card);414 usx2y->chip_status = USX2Y_STAT_CHIP_HUP;415 usx2y_unlinkseq(&usx2y->as04);416 usb_kill_urb(usx2y->in04_urb);417 snd_card_disconnect(card);418 419 /* release the midi resources */420 list_for_each(p, &usx2y->midi_list) {421 snd_usbmidi_disconnect(p);422 }423 if (usx2y->us428ctls_sharedmem)424 wake_up(&usx2y->us428ctls_wait_queue_head);425 snd_card_free(card);426}427 428static int snd_usx2y_probe(struct usb_interface *intf,429 const struct usb_device_id *id)430{431 struct usb_device *device = interface_to_usbdev(intf);432 struct snd_card *card;433 int err;434 435 if (le16_to_cpu(device->descriptor.idVendor) != 0x1604 ||436 (le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 &&437 le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 &&438 le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428))439 return -EINVAL;440 441 err = usx2y_create_card(device, intf, &card);442 if (err < 0)443 return err;444 err = usx2y_hwdep_new(card, device);445 if (err < 0)446 goto error;447 err = snd_card_register(card);448 if (err < 0)449 goto error;450 451 dev_set_drvdata(&intf->dev, card);452 return 0;453 454 error:455 snd_card_free(card);456 return err;457}458 459static struct usb_driver snd_usx2y_usb_driver = {460 .name = "snd-usb-usx2y",461 .probe = snd_usx2y_probe,462 .disconnect = snd_usx2y_disconnect,463 .id_table = snd_usx2y_usb_id_table,464};465module_usb_driver(snd_usx2y_usb_driver);466