Quelle usbusx2y.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* usbusx2y . c - ALSA USB US - 428 Driver
*
2005 - 04 - 14 Karsten Wiese
Version 0 . 8 . 7 . 2 :
Call snd_card_free ( ) instead of snd_card_free_in_thread ( ) to prevent oops with dead keyboard symptom .
Tested ok with kernel 2 . 6 . 12 - rc2 .
2004 - 12 - 14 Karsten Wiese
Version 0 . 8 . 7 . 1 :
snd_pcm_open for rawusb pcm - devices now returns - EBUSY if called without rawusb ' s hwdep device being open .
2004 - 12 - 02 Karsten Wiese
Version 0 . 8 . 7 :
Use macro usb_maxpacket ( ) for portability .
2004 - 10 - 26 Karsten Wiese
Version 0 . 8 . 6 :
wake_up ( ) process waiting in usx2y_urbs_start ( ) on error .
2004 - 10 - 21 Karsten Wiese
Version 0 . 8 . 5 :
nrpacks is runtime or compiletime configurable now with tested values from 1 to 4 .
2004 - 10 - 03 Karsten Wiese
Version 0 . 8 . 2 :
Avoid any possible racing while in prepare callback .
2004 - 09 - 30 Karsten Wiese
Version 0 . 8 . 0 :
Simplified things and made ohci work again .
2004 - 09 - 20 Karsten Wiese
Version 0 . 7 . 3 :
Use usb_kill_urb ( ) instead of deprecated ( kernel 2 . 6 . 9 ) usb_unlink_urb ( ) .
2004 - 07 - 13 Karsten Wiese
Version 0 . 7 . 1 :
Don ' t sleep in START / STOP callbacks anymore .
us428 channels C / D not handled just for this version , sorry .
2004 - 06 - 21 Karsten Wiese
Version 0 . 6 . 4 :
Temporarely suspend midi input
to sanely call usb_set_interface ( ) when setting format .
2004 - 06 - 12 Karsten Wiese
Version 0 . 6 . 3 :
Made it thus the following rule is enforced :
" All pcm substreams of one usx2y have to operate at the same rate & format . "
2004 - 04 - 06 Karsten Wiese
Version 0 . 6 . 0 :
Runs on 2 . 6 . 5 kernel without any " - - with - debug = " things .
us224 reported running .
2004 - 01 - 14 Karsten Wiese
Version 0 . 5 . 1 :
Runs with 2 . 6 . 1 kernel .
2003 - 12 - 30 Karsten Wiese
Version 0 . 4 . 1 :
Fix 24 Bit 4 Channel capturing for the us428 .
2003 - 11 - 27 Karsten Wiese , Martin Langer
Version 0 . 4 :
us122 support .
us224 could be tested by uncommenting the sections containing USB_ID_US224
2003 - 11 - 03 Karsten Wiese
Version 0 . 3 :
24 Bit support .
" 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 .
2003 - 08 - 22 Karsten Wiese
Version 0 . 0 . 8 :
Removed EZUSB Firmware . First Stage Firmwaredownload is now done by tascam - firmware downloader .
See :
http : //usb-midi-fw.sourceforge.net/tascam-firmware.tar.gz
2003 - 06 - 18 Karsten Wiese
Version 0 . 0 . 5 :
changed to compile with kernel 2 . 4 . 21 and alsa 0 . 9 . 4
2002 - 10 - 16 Karsten Wiese
Version 0 . 0 . 4 :
compiles again with alsa - current .
USB_ISO_ASAP not used anymore ( most of the time ) , instead
urb - > start_frame is calculated here now , some calls inside usb - driver don ' t need to happen anymore .
To get the best out of this :
Disable APM - support in the kernel as APM - BIOS calls ( once each second ) hard disable interrupt for many precious milliseconds .
This helped me much on my slowish PII 400 & PIII 500 .
ACPI yet untested but might cause the same bad behaviour .
Use a kernel with lowlatency and preemptiv patches applied .
To autoload snd - usb - midi append a line
post - install snd - usb - us428 modprobe snd - usb - midi
to / etc / modules . conf .
known problems :
sliders , knobs , lights not yet handled except MASTER Volume slider .
" pcm - c 2 " doesn ' t work . " pcm - c 2 - m direct_interleaved " does .
KDE3 : " Enable full duplex operation " deadlocks .
2002 - 08 - 31 Karsten Wiese
Version 0 . 0 . 3 : audio also simplex ;
simplifying : iso urbs only 1 packet , melted structs .
ASYNC_UNLINK not used anymore : no more crashes so far . . . . .
for alsa 0 . 9 rc3 .
2002 - 08 - 09 Karsten Wiese
Version 0 . 0 . 2 : midi works with snd - usb - midi , audio ( only fullduplex now ) with i . e . bristol .
The firmware has been sniffed from win2k us - 428 driver 3 . 09 .
* Copyright ( c ) 2002 - 2004 Karsten Wiese
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/rawmidi.h>
#include "usx2y.h"
#include "usbusx2y.h"
#include "usX2Yhwdep.h"
MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>" );
MODULE_DESCRIPTION("TASCAM " NAME_ALLCAPS" Version 0.8.7.2" );
MODULE_LICENSE("GPL" );
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
module_param_array(index, int , NULL, 0444 );
MODULE_PARM_DESC(index, "Index value for " NAME_ALLCAPS"." );
module_param_array(id, charp, NULL, 0444 );
MODULE_PARM_DESC(id, "ID string for " NAME_ALLCAPS"." );
module_param_array(enable, bool , NULL, 0444 );
MODULE_PARM_DESC(enable, "Enable " NAME_ALLCAPS"." );
static int snd_usx2y_card_used[SNDRV_CARDS];
static void snd_usx2y_card_private_free(struct snd_card *card);
static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s);
#ifdef USX2Y_NRPACKS_VARIABLE
int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */
module_param(nrpacks, int , 0444 );
MODULE_PARM_DESC(nrpacks, "Number of packets per URB." );
#endif
/*
* pipe 4 is used for switching the lamps , setting samplerate , volumes . . . .
*/
static void i_usx2y_out04_int(struct urb *urb)
{
#ifdef CONFIG_SND_DEBUG
if (urb->status) {
int i;
struct usx2ydev *usx2y = urb->context;
for (i = 0 ; i < 10 && usx2y->as04.urb[i] != urb; i++)
;
dev_dbg(&urb->dev->dev, "%s urb %i status=%i\n" , __func__, i, urb->status);
}
#endif
}
static void i_usx2y_in04_int(struct urb *urb)
{
int err = 0 ;
struct usx2ydev *usx2y = urb->context;
struct us428ctls_sharedmem *us428ctls = usx2y->us428ctls_sharedmem;
struct us428_p4out *p4out;
int i, j, n, diff, send;
usx2y->in04_int_calls++;
if (urb->status) {
dev_dbg(&urb->dev->dev, "Interrupt Pipe 4 came back with status=%i\n" , urb->status);
return ;
}
if (us428ctls) {
diff = -1 ;
if (us428ctls->ctl_snapshot_last == -2 ) {
diff = 0 ;
memcpy(usx2y->in04_last, usx2y->in04_buf, sizeof (usx2y->in04_last));
us428ctls->ctl_snapshot_last = -1 ;
} else {
for (i = 0 ; i < 21 ; i++) {
if (usx2y->in04_last[i] != ((char *)usx2y->in04_buf)[i]) {
if (diff < 0 )
diff = i;
usx2y->in04_last[i] = ((char *)usx2y->in04_buf)[i];
}
}
}
if (diff >= 0 ) {
n = us428ctls->ctl_snapshot_last + 1 ;
if (n >= N_US428_CTL_BUFS || n < 0 )
n = 0 ;
memcpy(us428ctls->ctl_snapshot + n, usx2y->in04_buf, sizeof (us428ctls->ctl_snapshot[0 ]));
us428ctls->ctl_snapshot_differs_at[n] = diff;
us428ctls->ctl_snapshot_last = n;
wake_up(&usx2y->us428ctls_wait_queue_head);
}
}
if (usx2y->us04) {
if (!usx2y->us04->submitted) {
do {
err = usb_submit_urb(usx2y->us04->urb[usx2y->us04->submitted++], GFP_ATOMIC);
} while (!err && usx2y->us04->submitted < usx2y->us04->len);
}
} else {
if (us428ctls && us428ctls->p4out_last >= 0 && us428ctls->p4out_last < N_US428_P4OUT_BUFS) {
if (us428ctls->p4out_last != us428ctls->p4out_sent) {
send = us428ctls->p4out_sent + 1 ;
if (send >= N_US428_P4OUT_BUFS)
send = 0 ;
for (j = 0 ; j < URBS_ASYNC_SEQ && !err; ++j) {
if (!usx2y->as04.urb[j]->status) {
p4out = us428ctls->p4out + send; // FIXME if more than 1 p4out is new, 1 gets lost.
usb_fill_bulk_urb(usx2y->as04.urb[j], usx2y->dev,
usb_sndbulkpipe(usx2y->dev, 0 x04), &p4out->val.vol,
p4out->type == ELT_LIGHT ? sizeof (struct us428_lights) : 5 ,
i_usx2y_out04_int, usx2y);
err = usb_submit_urb(usx2y->as04.urb[j], GFP_ATOMIC);
us428ctls->p4out_sent = send;
break ;
}
}
}
}
}
if (err)
dev_err(&urb->dev->dev, "in04_int() usb_submit_urb err=%i\n" , err);
urb->dev = usx2y->dev;
usb_submit_urb(urb, GFP_ATOMIC);
}
/*
* Prepare some urbs
*/
int usx2y_async_seq04_init(struct usx2ydev *usx2y)
{
int err = 0 , i;
if (WARN_ON(usx2y->as04.buffer))
return -EBUSY;
usx2y->as04.buffer = kmalloc_array(URBS_ASYNC_SEQ,
URB_DATA_LEN_ASYNC_SEQ, GFP_KERNEL);
if (!usx2y->as04.buffer) {
err = -ENOMEM;
} else {
for (i = 0 ; i < URBS_ASYNC_SEQ; ++i) {
usx2y->as04.urb[i] = usb_alloc_urb(0 , GFP_KERNEL);
if (!usx2y->as04.urb[i]) {
err = -ENOMEM;
break ;
}
usb_fill_bulk_urb(usx2y->as04.urb[i], usx2y->dev,
usb_sndbulkpipe(usx2y->dev, 0 x04),
usx2y->as04.buffer + URB_DATA_LEN_ASYNC_SEQ * i, 0 ,
i_usx2y_out04_int, usx2y);
err = usb_urb_ep_type_check(usx2y->as04.urb[i]);
if (err < 0 )
break ;
}
}
if (err)
usx2y_unlinkseq(&usx2y->as04);
return err;
}
int usx2y_in04_init(struct usx2ydev *usx2y)
{
int err;
if (WARN_ON(usx2y->in04_urb))
return -EBUSY;
usx2y->in04_urb = usb_alloc_urb(0 , GFP_KERNEL);
if (!usx2y->in04_urb) {
err = -ENOMEM;
goto error;
}
usx2y->in04_buf = kmalloc(21 , GFP_KERNEL);
if (!usx2y->in04_buf) {
err = -ENOMEM;
goto error;
}
init_waitqueue_head(&usx2y->in04_wait_queue);
usb_fill_int_urb(usx2y->in04_urb, usx2y->dev, usb_rcvintpipe(usx2y->dev, 0 x4),
usx2y->in04_buf, 21 ,
i_usx2y_in04_int, usx2y,
10 );
if (usb_urb_ep_type_check(usx2y->in04_urb)) {
err = -EINVAL;
goto error;
}
return usb_submit_urb(usx2y->in04_urb, GFP_KERNEL);
error:
kfree(usx2y->in04_buf);
usb_free_urb(usx2y->in04_urb);
usx2y->in04_buf = NULL;
usx2y->in04_urb = NULL;
return err;
}
static void usx2y_unlinkseq(struct snd_usx2y_async_seq *s)
{
int i;
for (i = 0 ; i < URBS_ASYNC_SEQ; ++i) {
if (!s->urb[i])
continue ;
usb_kill_urb(s->urb[i]);
usb_free_urb(s->urb[i]);
s->urb[i] = NULL;
}
kfree(s->buffer);
s->buffer = NULL;
}
static const struct usb_device_id snd_usx2y_usb_id_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = 0 x1604,
.idProduct = USB_ID_US428
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = 0 x1604,
.idProduct = USB_ID_US122
},
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = 0 x1604,
.idProduct = USB_ID_US224
},
{ /* terminator */ }
};
MODULE_DEVICE_TABLE(usb, snd_usx2y_usb_id_table);
static int usx2y_create_card(struct usb_device *device,
struct usb_interface *intf,
struct snd_card **cardp)
{
int dev;
struct snd_card *card;
int err;
for (dev = 0 ; dev < SNDRV_CARDS; ++dev)
if (enable[dev] && !snd_usx2y_card_used[dev])
break ;
if (dev >= SNDRV_CARDS)
return -ENODEV;
err = snd_card_new(&intf->dev, index[dev], id[dev], THIS_MODULE,
sizeof (struct usx2ydev), &card);
if (err < 0 )
return err;
snd_usx2y_card_used[usx2y(card)->card_index = dev] = 1 ;
card->private_free = snd_usx2y_card_private_free;
usx2y(card)->dev = device;
init_waitqueue_head(&usx2y(card)->prepare_wait_queue);
init_waitqueue_head(&usx2y(card)->us428ctls_wait_queue_head);
mutex_init(&usx2y(card)->pcm_mutex);
INIT_LIST_HEAD(&usx2y(card)->midi_list);
strscpy(card->driver, "USB " NAME_ALLCAPS"" );
sprintf(card->shortname, "TASCAM " NAME_ALLCAPS"" );
sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)" ,
card->shortname,
le16_to_cpu(device->descriptor.idVendor),
le16_to_cpu(device->descriptor.idProduct),
0 ,//us428(card)->usbmidi.ifnum,
usx2y(card)->dev->bus->busnum, usx2y(card)->dev->devnum);
*cardp = card;
return 0 ;
}
static void snd_usx2y_card_private_free(struct snd_card *card)
{
struct usx2ydev *usx2y = usx2y(card);
kfree(usx2y->in04_buf);
usb_free_urb(usx2y->in04_urb);
if (usx2y->us428ctls_sharedmem)
free_pages_exact(usx2y->us428ctls_sharedmem,
US428_SHAREDMEM_PAGES);
if (usx2y->card_index >= 0 && usx2y->card_index < SNDRV_CARDS)
snd_usx2y_card_used[usx2y->card_index] = 0 ;
}
static void snd_usx2y_disconnect(struct usb_interface *intf)
{
struct snd_card *card;
struct usx2ydev *usx2y;
struct list_head *p;
card = usb_get_intfdata(intf);
if (!card)
return ;
usx2y = usx2y(card);
usx2y->chip_status = USX2Y_STAT_CHIP_HUP;
usx2y_unlinkseq(&usx2y->as04);
usb_kill_urb(usx2y->in04_urb);
snd_card_disconnect(card);
/* release the midi resources */
list_for_each(p, &usx2y->midi_list) {
snd_usbmidi_disconnect(p);
}
if (usx2y->us428ctls_sharedmem)
wake_up(&usx2y->us428ctls_wait_queue_head);
snd_card_free_when_closed(card);
}
static int snd_usx2y_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *device = interface_to_usbdev(intf);
struct snd_card *card;
int err;
#ifdef USX2Y_NRPACKS_VARIABLE
if (nrpacks < 0 || nrpacks > USX2Y_NRPACKS_MAX)
return -EINVAL;
#endif
if (le16_to_cpu(device->descriptor.idVendor) != 0 x1604 ||
(le16_to_cpu(device->descriptor.idProduct) != USB_ID_US122 &&
le16_to_cpu(device->descriptor.idProduct) != USB_ID_US224 &&
le16_to_cpu(device->descriptor.idProduct) != USB_ID_US428))
return -EINVAL;
err = usx2y_create_card(device, intf, &card);
if (err < 0 )
return err;
err = usx2y_hwdep_new(card, device);
if (err < 0 )
goto error;
err = snd_card_register(card);
if (err < 0 )
goto error;
dev_set_drvdata(&intf->dev, card);
return 0 ;
error:
snd_card_free(card);
return err;
}
static struct usb_driver snd_usx2y_usb_driver = {
.name = "snd-usb-usx2y" ,
.probe = snd_usx2y_probe,
.disconnect = snd_usx2y_disconnect,
.id_table = snd_usx2y_usb_id_table,
};
module_usb_driver(snd_usx2y_usb_driver);
Messung V0.5 in Prozent C=85 H=89 G=86
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691