| Open Sound System |
| Do you have problems with sound/audio application development? Don't panic! Click here for help! |
This file contains the Linux specific USB support routines defined in udi.h
Copyright (C) 4Front Technologies 2005-2007. Released under GPL2 license.
#include "udi.h"
#undef IO_DEBUG
static const udi_usb_devinfo *known_devices = NULL;
#define MAX_DEVICE_SLOTS 20
int udi_usb_trace = 0;
static udi_usb_driver *drv = NULL;
extern void *oss_pmalloc (size_t sz);
struct udi_usb_devc
{
struct usb_device *usb_dev;
const struct usb_device_id *id;
const udi_usb_devinfo *udi_usb_dev;
char *devpath;
int enabled;
int vendor, product, class, subclass;
struct usb_interface *iface;
int iface_number;
char *dev_name;
char *altsetting_labels;
int default_altsetting;
unsigned int altsetting_mask;
udi_usb_driver *drv;
void *client_devc;
int num_altsettings;
struct usb_host_interface *altsetting;
};
struct _udi_endpoint_handle_t
{
unsigned char desc[32];
};
#define MAX_DEVC 32
static udi_usb_devc usb_devc_list[MAX_DEVC] = { {0} };
static int ndevs = 0;
udi_endpoint_handle_t *
udi_open_endpoint (udi_usb_devc * usbdev, void *ep_descr)
{
return (udi_endpoint_handle_t *) ep_descr;
}
void
udi_close_endpoint (udi_endpoint_handle_t * eph)
{
// NOP
}
int
udi_endpoint_get_num (udi_endpoint_handle_t * eph)
{
return eph->desc[2] /* & 0x7f */;
}
#if 1
static void
dump_configs (struct usb_device *dev)
{
int c;
printk ("#configs %d\n", dev->descriptor.bNumConfigurations);
for (c = 0; c < dev->descriptor.bNumConfigurations; c++)
{
int i, j, k;
struct usb_host_config *config = &dev->config[c];
printk ("\tConfig #%d - #interfaces=%d\n", c,
config->desc.bNumInterfaces);
for (j = 0; j < config->desc.bNumInterfaces; j++)
{
struct usb_interface *ifp = config->interface[j];
printk ("\t\tInterface #%d - altsettings=%d\n", j,
ifp->num_altsetting);
for (k = 0; k < ifp->num_altsetting; k++)
{
struct usb_host_interface *alt = &ifp->altsetting[k];
unsigned char *buf = alt->extra;
printk ("\t\t\tAlt setting #%d:\n", k);
for (i = 0; i < alt->extralen; i++)
{
if (!(i % 8))
{
if (i)
printk ("\n");
printk ("\t\t\t%04x: ", i);
}
printk ("%02x ", buf[i]);
}
printk ("\n");
}
}
}
}
#endif
static int
udi_attach_usbdev (struct usb_device *dev,
oss_device_t * osdev,
char *devpath,
struct usb_interface *iface,
const struct usb_device_id *id,
const udi_usb_devinfo * udi_usb_dev)
{
int cfg_num, ep;
udi_usb_devc *devc = &usb_devc_list[ndevs];
struct usb_host_interface *alts;
if (ndevs >= MAX_DEVC)
{
printk ("OSS: Too many USB audio/midi devices\n");
return -ENODEV;
}
if (udi_usb_trace > 1)
printk ("OSS: Attaching USB device %x:%x/%d, class=%x:%x, name=%s\n",
dev->descriptor.idVendor, dev->descriptor.idProduct,
iface->altsetting[0].desc.bInterfaceNumber,
iface->altsetting[0].desc.bInterfaceClass,
iface->altsetting[0].desc.bInterfaceSubClass, udi_usb_dev->name);
devc->usb_dev = dev;
devc->id = id;
devc->udi_usb_dev = udi_usb_dev;
devc->vendor = dev->descriptor.idVendor;
devc->product = dev->descriptor.idProduct;
devc->class = iface->altsetting[0].desc.bInterfaceClass;
devc->subclass = iface->altsetting[0].desc.bInterfaceSubClass;
devc->iface_number = iface->altsetting[0].desc.bInterfaceNumber;
devc->iface = iface;
devc->dev_name = udi_usb_dev->name;
devc->devpath = devpath;
devc->num_altsettings = iface->num_altsetting;
devc->altsetting = iface->altsetting;
alts = &iface->altsetting[devc->num_altsettings - 1];
ep = 0;
if (alts->desc.bNumEndpoints > 1)
ep = 1;
if (udi_usb_trace > 2)
{
int i;
for (i = 0; i < alts->desc.bNumEndpoints; i++)
{
printk ("Endpoint: %02x\n",
alts->endpoint[i].desc.bEndpointAddress);
}
}
cfg_num = 0;
devc->enabled = 1;
devc->drv = drv;
if (udi_usb_trace > 2)
dump_configs (dev);
if ((devc->client_devc = drv->attach (devc, osdev)) == NULL)
{
return -EIO;
}
ndevs++;
usb_set_intfdata (iface, devc);
return 0;
}
static char *prev_devices[32] = { NULL };
static int nprev_devices = 0;
static int
udi_usb_probe (struct usb_interface *iface, const struct usb_device_id *id)
{
int i;
static int ncalls = 0;
oss_device_t *osdev = NULL;
dev_info_t *dip = NULL; // TODO
char nick[32];
int inst = 0;
struct usb_device *dev = interface_to_usbdev (iface);
if (ncalls++ > 100)
return -EIO;
sprintf (nick, "usb%04x%04x-", dev->descriptor.idVendor,
dev->descriptor.idProduct);
Find out how many instances of this device (ID) are already attached.
for (i = 0; i < nprev_devices; i++)
{
if (strcmp (nick, prev_devices[i]) == 0)
{
inst++;
}
}
prev_devices[nprev_devices] = oss_pmalloc (strlen (nick) + 1);
strcpy (prev_devices[nprev_devices], nick);
if (nprev_devices < 32)
nprev_devices++;
if ((osdev = osdev_create (dip, DRV_USB, inst, nick, NULL)) == NULL)
{
return -ENOMEM;
}
osdev_set_owner (osdev, THIS_MODULE);
osdev_set_major (osdev, usb_major);
i = 0;
if (udi_usb_trace > 1)
printk ("\n\nProbing dev=%s id=%x:%x/%d\n", dev->devpath,
dev->descriptor.idVendor,
dev->descriptor.idProduct,
iface->altsetting[0].desc.bInterfaceNumber);
while (i >= 0 && known_devices[i].vendor >= 0)
{
if (dev->descriptor.idVendor == known_devices[i].vendor &&
dev->descriptor.idProduct == known_devices[i].product)
{
int ret;
const udi_usb_devinfo *d = &known_devices[i];
ret = udi_attach_usbdev (dev, osdev, dev->devpath, iface, id, d);
return ret;
}
else
i++;
}
/* Try the "generic" device */
{
int ret;
const udi_usb_devinfo *d = &known_devices[i];
ret = udi_attach_usbdev (dev, osdev, dev->devpath, iface, id, d);
return ret;
}
return -ENODEV;
}
static void
udi_usb_disconnect (struct usb_interface *iface)
{
udi_usb_devc *devc = usb_get_intfdata (iface);
//struct usb_device *dev = interface_to_usbdev (iface);
if (devc == (udi_usb_devc *) - 1)
return;
if (!devc->enabled)
return;
if (udi_usb_trace > 0)
printk ("OSS: Disconnect USB device %x:%x %s\n", devc->vendor,
devc->product, devc->udi_usb_dev->name);
devc->drv->disconnect (devc->client_devc);
devc->enabled = 0;
}
static struct usb_driver oss_usb = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
owner:THIS_MODULE,
#endif
name:"oss_usb",
probe:udi_usb_probe,
disconnect:udi_usb_disconnect,
id_table:udi_usb_table
};
static int udi_usb_installed = 0;
int
udi_attach_usbdriver (oss_device_t * osdev, const udi_usb_devinfo * devlist,
udi_usb_driver * driver)
{
drv = driver;
known_devices = devlist;
return 1;
}
void
udi_unload_usbdriver (oss_device_t * osdev)
{
}
Device access routines
int
udi_usbdev_get_class (udi_usb_devc * usbdev)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return devc->class;
}
int
udi_usbdev_get_subclass (udi_usb_devc * usbdev)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return devc->subclass;
}
int
udi_usbdev_get_vendor (udi_usb_devc * usbdev)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return devc->vendor;
}
int
udi_usbdev_get_product (udi_usb_devc * usbdev)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return devc->product;
}
int
udi_usbdev_get_inum (udi_usb_devc * usbdev)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return devc->iface_number;
}
int
udi_usbdev_set_interface (udi_usb_devc * usbdev, int inum, int altset)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return usb_set_interface (devc->usb_dev, inum, altset);
}
unsigned char *
udi_usbdev_get_endpoint (udi_usb_devc * usbdev, int altsetting, int n,
int *len)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
int num_endpoints;
struct usb_device *dev;
struct usb_host_interface *alts;
struct usb_interface *iface;
dev = devc->usb_dev;
iface = devc->iface;
if (altsetting >= devc->num_altsettings)
return NULL;
alts = &iface->altsetting[altsetting];
num_endpoints = alts->desc.bNumEndpoints;
if (n >= num_endpoints)
return NULL;
*len = alts->endpoint[n].desc.bLength;
return (unsigned char *) &alts->endpoint[n].desc;
}
int
udi_usbdev_get_num_altsettings (udi_usb_devc * usbdev)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return devc->num_altsettings;
}
unsigned char *
udi_usbdev_get_altsetting (udi_usb_devc * usbdev, int n, int *size)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
struct usb_host_interface *alt;
if (n < 0 || n >= devc->num_altsettings)
{
/* printk("udi usb: Bad altsetting %d (%d)\n", n, n >= devc->num_altsettings); */
return NULL;
}
alt = &devc->altsetting[n];
*size = alt->extralen;
return alt->extra;
}
char *
udi_usbdev_get_name (udi_usb_devc * usbdev)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return devc->dev_name == NULL ? "Unknown" : devc->dev_name;
}
char *
udi_usbdev_get_altsetting_labels (udi_usb_devc * usbdev, int if_num, int *default_alt, unsigned int *mask)
{
int i;
*default_alt=1;
*mask=0xffffffff;
if (usbdev->udi_usb_dev == NULL) /* No device definitions available */
{
return NULL;
}
for (i=0;usbdev->udi_usb_dev->altsettings[i].altsetting_labels!=NULL;i++)
if (i==if_num)
{
*default_alt = usbdev->udi_usb_dev->altsettings[i].default_altsetting;
*mask = usbdev->udi_usb_dev->altsettings[i].altsetting_mask;
if (*mask==0)
*mask=0xffffffff;
return usbdev->udi_usb_dev->altsettings[i].altsetting_labels;
}
return NULL; /* Not found */
}
char *
udi_usbdev_get_string (udi_usb_devc * usbdev, int ix)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
static char str[100];
int err;
if (ix == 0)
return NULL;
if ((err = usb_string (devc->usb_dev, ix, str, sizeof (str) - 1)) != 0)
{
return NULL;
}
return str;
}
char *
udi_usbdev_get_devpath (udi_usb_devc * usbdev)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
return devc->devpath;
}
int
udi_usb_snd_control_msg (udi_usb_devc * usbdev, unsigned int endpoint,
unsigned char rq,
unsigned char rqtype,
unsigned short value,
unsigned short index,
void *buf, int len, int timeout)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
int err;
if (!devc->enabled)
return -EPIPE;
if (timeout < 0)
timeout = 0;
#ifdef IO_DEBUG
printk ("Snd %x (%x) rq=%x, rt=%x, v=%x, ix=%x, l=%d %02x %02x\n",
devc->usb_dev,
usb_sndctrlpipe (devc->usb_dev, endpoint),
rq, rqtype, value, index, len, b[0], b[1]);
#endif
err = usb_control_msg (devc->usb_dev,
usb_sndctrlpipe (devc->usb_dev, endpoint),
rq, rqtype, value, index, buf, len, timeout);
#ifdef IO_DEBUG
if (err < 0)
printk ("Usb write error %d\n", err);
#endif
return err;
}
int
udi_usb_rcv_control_msg (udi_usb_devc * usbdev, unsigned int endpoint,
unsigned char rq,
unsigned char rqtype,
unsigned short value,
unsigned short index,
void *buf, int len, int timeout)
{
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
int err;
if (!devc->enabled)
return -EPIPE;
if (timeout < 0)
timeout = 0;
#ifdef IO_DEBUG
printk ("Rcv %x (%x) rq=%x, rt=%x, v=%x, ix=%x, l=%d\n",
devc->usb_dev,
(unsigned int) usb_rcvctrlpipe (devc->usb_dev, endpoint),
rq, rqtype | USB_DIR_IN, value, index, len);
#endif
err = usb_control_msg (devc->usb_dev,
(unsigned int) usb_rcvctrlpipe (devc->usb_dev,
endpoint), rq,
rqtype | USB_DIR_IN, value, index, buf, len,
timeout);
#ifdef IO_DEBUG
if (err < 0)
printk ("Usb read error %d\n", err);
else
printk ("Got %02x %02x\n", b[0], b[1]);
#endif
return err;
}
/* Request stuff */
struct udi_usb_request
{
struct urb *urb;
udi_usb_complete_func_t callback;
void *callback_arg;
int active;
void *data;
};
udi_usb_request_t
* udi_usb_alloc_request (udi_usb_devc * usbdev, udi_endpoint_handle_t * eph,
int nframes, int xfer_type)
{
udi_usb_request_t *rq;
udi_usb_devc *devc = (udi_usb_devc *) usbdev;
if ((rq = kmalloc (sizeof (*rq), GFP_KERNEL)) == NULL)
{
printk ("udi_usb_alloc_request: Out of memory\n");
return NULL;
}
memset (rq, 0, sizeof (*rq));
if ((rq->urb = usb_alloc_urb (nframes, 0)) == NULL)
{
kfree (rq);
printk ("udi_usb_alloc_request: Failed to allocate URB\n");
return NULL;
}
rq->urb->dev = devc->usb_dev;
rq->urb->number_of_packets = nframes;
rq->active = 0;
return rq;
}
void
udi_usb_free_request (udi_usb_request_t * request)
{
if (request == NULL)
return;
udi_usb_cancel_request (request);
usb_free_urb (request->urb);
kfree (request);
}
unsigned char *
udi_usb_request_actdata (udi_usb_request_t * request)
{
return request->data;
}
static void
complete_func (struct urb *urb)
{
udi_usb_request_t *request = urb->context;
request->active = 0;
request->callback (request, request->callback_arg);
}
int
udi_usb_submit_request (udi_usb_request_t * request,
udi_usb_complete_func_t callback, void *callback_arg,
udi_endpoint_handle_t * eph, int xfer_type,
void *data, int data_len)
{
struct urb *urb;
struct usb_device *d;
int i, err;
int endpoint = eph->desc[2] & 0x7f;
if (request == NULL)
return -EINVAL;
urb = request->urb;
d = urb->dev;
request->callback = callback;
request->callback_arg = callback_arg;
request->data = data;
urb->complete = (usb_complete_t) complete_func;
urb->context = request;
urb->transfer_buffer = data;
for (i = 0; i < urb->number_of_packets; i++)
{
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].length = data_len;
urb->iso_frame_desc[i].offset = i * data_len;
}
urb->transfer_buffer_length = urb->actual_length = data_len;
switch (xfer_type)
{
case UDI_USBXFER_ISO_WRITE:
urb->pipe = usb_sndisocpipe (urb->dev, endpoint);
urb->transfer_flags = URB_ISO_ASAP;
urb->interval = 1;
break;
case UDI_USBXFER_ISO_READ:
urb->pipe = usb_rcvisocpipe (urb->dev, endpoint);
urb->transfer_flags = URB_ISO_ASAP;
urb->interval = 1;
break;
case UDI_USBXFER_BULK_READ:
usb_fill_bulk_urb (urb, d, usb_rcvbulkpipe (d, endpoint),
data, data_len,
(usb_complete_t) complete_func, request);
break;
case UDI_USBXFER_INTR_READ:
usb_fill_int_urb (urb, d, usb_rcvintpipe (d, endpoint),
data, data_len,
(usb_complete_t) complete_func, request, 8);
break;
case UDI_USBXFER_BULK_WRITE:
usb_fill_bulk_urb (urb, d, usb_sndbulkpipe (d, endpoint),
data, data_len,
(usb_complete_t) complete_func, request);
break;
default:
printk ("udi usb: Bad xfer type %d\n", xfer_type);
return -EINVAL;
}
#ifdef SLAB_ATOMIC
if ((err = usb_submit_urb (request->urb, SLAB_ATOMIC)) >= 0)
request->active = 1;
#else
Linux 2.6.20 and later don't have SLAB_ATOMIC
if ((err = usb_submit_urb (request->urb, GFP_ATOMIC)) >= 0)
request->active = 1;
#endif
return err;
}
int
udi_usb_request_actlen (udi_usb_request_t * request)
{
return request->urb->actual_length;
}
void
udi_usb_cancel_request (udi_usb_request_t * request)
{
if (request == NULL || !request->active)
return;
usb_kill_urb (request->urb);
}