diff options
Diffstat (limited to 'xpp/xpp_usb.c')
-rw-r--r-- | xpp/xpp_usb.c | 832 |
1 files changed, 408 insertions, 424 deletions
diff --git a/xpp/xpp_usb.c b/xpp/xpp_usb.c index 5d34554..73f351c 100644 --- a/xpp/xpp_usb.c +++ b/xpp/xpp_usb.c @@ -20,34 +20,6 @@ * */ #include <linux/version.h> - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -# warning "This module is tested only with 2.6 kernels" -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) -# undef USB_FIELDS_MISSING -#else -# define USB_FIELDS_MISSING - -# define USB_MAX_STRING 128 -# define USB_GET_STRING(udev,field,buf) \ - do { \ - if((udev)->descriptor.field) { \ - char tmp[USB_MAX_STRING]; \ - if(usb_string((udev), (udev)->descriptor.field, tmp, sizeof(tmp)) > 0) \ - snprintf((buf), USB_MAX_STRING, "%s", tmp); \ - } \ - } while(0); -# define USB_GET_IFACE_NAME(udev,iface,buf) \ - do { \ - if((iface)->desc.iInterface) { \ - char tmp[USB_MAX_STRING]; \ - if(usb_string((udev), (iface)->desc.iInterface, tmp, sizeof(tmp)) > 0) \ - snprintf((buf), USB_MAX_STRING, "%s", tmp); \ - } \ - } while(0); -#endif - #include <linux/kernel.h> #include <linux/errno.h> #include <linux/module.h> @@ -64,6 +36,7 @@ #include "xpd.h" #include "xproto.h" #include "xbus-core.h" +#include "xframe_queue.h" #ifdef DEBUG #include "card_fxs.h" #include "card_fxo.h" @@ -74,10 +47,22 @@ static const char rcsid[] = "$Id$"; DEF_PARM(int, print_dbg, 0, 0644, "Print DBG statements"); /* must be before zap_debug.h */ DEF_PARM(int, usb1, 0, 0644, "Allow using USB 1.1 interfaces"); -DEF_PARM_BOOL(rx_tasklet, 1, 0644, "Use receive tasklets"); +DEF_PARM(uint, tx_sluggish, 2000, 0644, "A sluggish transmit (usec)"); +DEF_PARM(uint, drop_pcm_after, 6, 0644, "Number of consecutive tx_sluggish to drop a PCM frame"); #include "zap_debug.h" + +#define XUSB_PRINTK(level, xusb, fmt, ...) \ + printk(KERN_ ## level "%s-%s: xusb-%d (%s) [%s]: " fmt, #level, \ + THIS_MODULE->name, (xusb)->index, xusb->path, xusb->serial, ## __VA_ARGS__) + +#define XUSB_DBG(bits, xusb, fmt, ...) \ + ((void)((print_dbg & (DBG_ ## bits)) && XUSB_PRINTK(DEBUG, xusb, "%s: " fmt, __FUNCTION__, ## __VA_ARGS__))) +#define XUSB_ERR(xusb, fmt, ...) XUSB_PRINTK(ERR, xusb, fmt, ## __VA_ARGS__) +#define XUSB_NOTICE(xusb, fmt, ...) XUSB_PRINTK(NOTICE, xusb, fmt, ## __VA_ARGS__) +#define XUSB_INFO(xusb, fmt, ...) XUSB_PRINTK(INFO, xusb, fmt, ## __VA_ARGS__) + /* FIXME: A flag that was deprecated at some point, and rather useless */ /* anyway. Only used in the code or-ed to other flags */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) @@ -90,6 +75,33 @@ DEF_PARM_BOOL(rx_tasklet, 1, 0644, "Use receive tasklets"); #define PROC_USBXPP_SUMMARY "xpp_usb" #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +# warning "This module is tested only with 2.6 kernels" +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) +# undef USB_FIELDS_MISSING +#else +# define USB_FIELDS_MISSING + +# define USB_MAX_STRING 128 +# define USB_GET_STRING(udev,field,buf) \ + do { \ + if((udev)->descriptor.field) { \ + char tmp[USB_MAX_STRING]; \ + if(usb_string((udev), (udev)->descriptor.field, tmp, sizeof(tmp)) > 0) \ + snprintf((buf), USB_MAX_STRING, "%s", tmp); \ + } \ + } while(0); +# define USB_GET_IFACE_NAME(udev,iface,buf) \ + do { \ + if((iface)->desc.iInterface) { \ + char tmp[USB_MAX_STRING]; \ + if(usb_string((udev), (iface)->desc.iInterface, tmp, sizeof(tmp)) > 0) \ + snprintf((buf), USB_MAX_STRING, "%s", tmp); \ + } \ + } while(0); +#endif + #ifdef DEBUG_PCM_TIMING static cycles_t stamp_last_pcm_read; static cycles_t accumulate_diff; @@ -103,12 +115,21 @@ struct xusb_endpoint { usb_complete_t callback; }; -static int xusb_xframe_send(xbus_t *xbus, xframe_t *xframe); +enum xusb_dir { + XUSB_RECV = 0, + XUSB_SEND = 1, +}; + +static int xframe_send_pcm(xbus_t *xbus, xframe_t *xframe); +static int xframe_send_cmd(xbus_t *xbus, xframe_t *xframe); +static xframe_t *alloc_xframe(xbus_t *xbus, gfp_t flags); +static void free_xframe(xbus_t *xbus, xframe_t *frm); -static xbus_ops_t xusb_ops = { - .xframe_send = xusb_xframe_send, - .xframe_new = NULL, // Default allocator - .xframe_free = NULL, // Default deallocator +static struct xbus_ops xusb_ops = { + .xframe_send_pcm = xframe_send_pcm, + .xframe_send_cmd = xframe_send_cmd, + .alloc_xframe = alloc_xframe, + .free_xframe = free_xframe, }; enum { @@ -139,58 +160,63 @@ static struct xusb_counters { #define MAX_PENDING_WRITES 100 -enum xusb_dir { - XUSB_RECV = 0, - XUSB_SEND = 1, -}; +static KMEM_CACHE_T *xusb_cache = NULL; -/* Receive Tasklets */ -#define MAX_RECV_QUEUE 100 +typedef struct xusb xusb_t; -struct xframe_queue { - struct list_head head; - unsigned int count; - unsigned int worst_count; - unsigned int overflows; - spinlock_t lock; +/* + * A uframe is our low level representation of a frame. + * + * It contains the metadata for the usb stack (a urb) + * and the metadata for the xbus-core (an xframe) + * as well as pointing to the data (transfer_buffer, transfer_buffer_length) + * directionality (send/receive) and ownership (xusb). + */ +struct uframe { + unsigned long uframe_magic; +#define UFRAME_MAGIC 654321L + struct urb urb; + xframe_t xframe; + size_t transfer_buffer_length; + void *transfer_buffer; /* max XFRAME_DATASIZE */ + xusb_t *xusb; }; -static void xframe_queue_init(struct xframe_queue *q); -static int xframe_enqueue(struct xframe_queue *q, xframe_t *xframe); -static xframe_t *xframe_dequeue(struct xframe_queue *q); -static void receive_tasklet_func(unsigned long data); +#define urb_to_uframe(urb) container_of(urb, struct uframe, urb) +#define xframe_to_uframe(xframe) container_of(xframe, struct uframe, xframe) +#define xusb_of(xbus) ((xusb_t *)((xbus)->transport.priv)) + +#define USEC_BUCKET 100 /* usec */ +#define NUM_BUCKETS 15 +#define BUCKET_START (500/USEC_BUCKET) /* skip uninteresting */ /* * USB XPP Bus (a USB Device) */ -typedef struct xpp_usb_bus { - xbus_t *xbus; +struct xusb { + uint xbus_num; struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ unsigned char minor; /* the starting minor number for this device */ + uint index; + char path[XBUS_DESCLEN]; /* a unique path */ struct xusb_model_info *model_info; struct xusb_endpoint endpoints[2]; /* RECV/SEND endpoints */ - struct urb *read_urb; - - /* Receive tasklet */ - struct xframe_queue receive_queue; - struct tasklet_struct receive_tasklet; - int cpu_rcv_intr[NR_CPUS]; - int cpu_rcv_tasklet[NR_CPUS]; - - struct completion write_finished; /* wait for the write to finish */ - int present; /* if the device is not disconnected */ - int reading; /* is the read_urb reading (listening) */ atomic_t pending_writes; /* submited but not out yet */ struct semaphore sem; /* locks this structure */ - int counters[XUSB_COUNTER_MAX]; + int counters[XUSB_COUNTER_MAX]; /* metrics */ struct timeval last_tx; unsigned int max_tx_delay; + uint usb_tx_delay[NUM_BUCKETS]; + uint sluggish_debounce; + bool drop_next_pcm; /* due to sluggishness */ + atomic_t pcm_tx_drops; + atomic_t usb_sluggish_count; #ifdef USB_FIELDS_MISSING /* storage for missing strings in old kernels */ @@ -205,7 +231,7 @@ typedef struct xpp_usb_bus { const char *interface_name; #endif -} xusb_t; +}; static spinlock_t xusb_lock = SPIN_LOCK_UNLOCKED; static xusb_t *xusb_array[MAX_BUSES] = {}; @@ -216,18 +242,6 @@ static unsigned bus_count = 0; static DECLARE_MUTEX (disconnect_sem); /* - * Function Prototypes - */ -#if 0 -static ssize_t xusb_read (struct file *file, char *buffer, size_t count, loff_t *ppos); -static ssize_t xusb_write (struct file *file, const char *buffer, size_t count, loff_t *ppos); -static int xusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); -static int xusb_open (struct inode *inode, struct file *file); -static int xusb_release (struct inode *inode, struct file *file); -static void xusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs); -#endif - -/* * AsteriskNow kernel has backported the "lean" callback from 2.6.20 * to 2.6.19 without any macro to notify of this fact -- how lovely. * Debian-Etch and Centos5 are using 2.6.18 for now (lucky for us). @@ -239,37 +253,112 @@ static void xusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs); #define USB_PASS_CB(u) struct urb *u #endif -static void xpp_urb_delete(struct urb *urb); -static struct urb *xpp_urb_new(xusb_t *dev, enum xusb_dir dir, size_t size); static void xpp_send_callback(USB_PASS_CB(urb)); static void xpp_receive_callback(USB_PASS_CB(urb)); - static int xusb_probe (struct usb_interface *interface, const struct usb_device_id *id); static void xusb_disconnect (struct usb_interface *interface); static int xusb_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); /*------------------------------------------------------------------*/ -static int xusb_xframe_send(xbus_t *xbus, xframe_t *xframe) +/* + * Updates the urb+xframe metadata from the uframe information. + */ +static void uframe_recompute(struct uframe *uframe, enum xusb_dir dir) +{ + struct urb *urb = &uframe->urb; + xusb_t *xusb = uframe->xusb; + struct usb_device *udev = xusb->udev; + struct xusb_endpoint *xusb_ep = &xusb->endpoints[dir]; + unsigned int ep_addr = xusb_ep->ep_addr; + usb_complete_t urb_cb = xusb_ep->callback; + unsigned int epnum = ep_addr & USB_ENDPOINT_NUMBER_MASK; + int pipe = usb_pipein(ep_addr) + ? usb_rcvbulkpipe(udev, epnum) + : usb_sndbulkpipe(udev, epnum); + + BUG_ON(uframe->uframe_magic != UFRAME_MAGIC); + usb_fill_bulk_urb(urb, udev, pipe, uframe->transfer_buffer, uframe->transfer_buffer_length, urb_cb, uframe); + urb->transfer_flags = (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK); +} + +static xframe_t *alloc_xframe(xbus_t *xbus, gfp_t gfp_flags) +{ + struct uframe *uframe; + xusb_t *xusb; + void *p; + int size; + static int rate_limit; + + BUG_ON(!xbus); + xusb = xusb_of(xbus); + BUG_ON(!xusb); + if(!xusb->present) { + if((rate_limit++ % 1003) == 0) + XUSB_ERR(xusb, + "abort allocations during device disconnect (%d)\n", rate_limit); + return NULL; + } + size = min(xusb->endpoints[XUSB_SEND].max_size, xusb->endpoints[XUSB_RECV].max_size); + uframe = kmem_cache_alloc(xusb_cache, gfp_flags); + if(!uframe) { + if((rate_limit++ % 1003) == 0) + XUSB_ERR(xusb, "frame allocation failed (%d)\n", rate_limit); + return NULL; + } + usb_init_urb(&uframe->urb); + p = usb_buffer_alloc(xusb->udev, size, gfp_flags, &uframe->urb.transfer_dma); + if(!p) { + if((rate_limit++ % 1003) == 0) + XUSB_ERR(xusb, "buffer allocation failed (%d)\n", rate_limit); + kfree(uframe); + return NULL; + } + uframe->uframe_magic = UFRAME_MAGIC; + uframe->transfer_buffer_length = size; + uframe->transfer_buffer = p; + uframe->xusb = xusb; + xframe_init(xbus, &uframe->xframe, uframe->transfer_buffer, uframe->transfer_buffer_length, uframe); + return &uframe->xframe; +} + +void free_xframe(xbus_t *xbus, xframe_t *xframe) +{ + struct uframe *uframe = xframe_to_uframe(xframe); + struct urb *urb = &uframe->urb; + + BUG_ON(xbus->transport.priv != uframe->xusb); + //XUSB_INFO(uframe->xusb, "frame_free\n"); + usb_buffer_free(urb->dev, uframe->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + memset(uframe, 0, sizeof(*uframe)); + kmem_cache_free(xusb_cache, uframe); +} + +/*------------------------------------------------------------------*/ + +/* + * Actuall frame sending -- both PCM and commands. + */ +static int do_send_xframe(xbus_t *xbus, xframe_t *xframe) { - xusb_t *xusb; struct urb *urb; + xusb_t *xusb; int ret = 0; - size_t size; - struct xusb_endpoint *xusb_ep; + struct uframe *uframe; + static int rate_limit; BUG_ON(!xframe); - BUG_ON(!xbus); - xusb = xbus->priv; + BUG_ON(xframe->xframe_magic != XFRAME_MAGIC); + xusb = xusb_of(xbus); BUG_ON(!xusb); if(!xusb->present) { - static int rate_limit; - - if((rate_limit++ % 5000) == 0) - XBUS_ERR(xbus, "USB device not present. Dropping packets (#%d).\n", - rate_limit); + if((rate_limit++ % 1003) == 0) + XUSB_ERR(xusb, + "abort do_send_xframe during device disconnect (%d)\n", rate_limit); ret = -ENODEV; - goto freepack; + goto failure; } /* * If something really bad happend, do not overflow the USB stack @@ -278,209 +367,126 @@ static int xusb_xframe_send(xbus_t *xbus, xframe_t *xframe) static int rate_limit; if((rate_limit++ % 5000) == 0) - XBUS_ERR(xbus, "USB device is totaly stuck. Dropping packets (#%d).\n", - rate_limit); + XUSB_ERR(xusb, + "USB device is totaly stuck. Dropping packets (#%d).\n", + rate_limit); ret = -ENODEV; - goto freepack; - } - size = XFRAME_LEN(xframe); - xusb_ep = &xusb->endpoints[XUSB_SEND]; - urb = xpp_urb_new(xusb, XUSB_SEND, size); - if (!urb) { - ERR("No free urbs available\n"); - ret = -ENOMEM; - goto freepack; + goto failure; } - - /* FIXME: FIXME: FIXME: we use copy+free until low-level drivers allocate memory themselves */ - - memcpy(urb->transfer_buffer, xframe->packets, size); - + uframe = xframe->priv; + BUG_ON(!uframe); + BUG_ON(uframe->uframe_magic != UFRAME_MAGIC); + uframe_recompute(uframe, XUSB_SEND); + urb = &uframe->urb; + BUG_ON(!urb); + /* update urb length */ + urb->transfer_buffer_length = XFRAME_LEN(xframe); + do_gettimeofday(&xframe->tv_submitted); ret = usb_submit_urb(urb, GFP_ATOMIC); if(ret < 0) { static int rate_limit; if((rate_limit++ % 1000) < 5) - ERR("%s: failed submit_urb: %d\n", __FUNCTION__, ret); - xpp_urb_delete(urb); + XUSB_ERR(xusb, "failed submit_urb: %d\n", ret); ret = -EBADF; - goto freepack; + goto failure; } - if (print_dbg) - dump_xframe("USB_FRAME_SEND", xbus, xframe); - do_gettimeofday(&xusb->last_tx); +// if (print_dbg) +// dump_xframe("USB_FRAME_SEND", xbus, xframe); atomic_inc(&xusb->pending_writes); -freepack: - xbus->ops->xframe_free(xbus, xframe); // FIXME: eventually will be done in the urb callback - if(ret < 0) - XUSB_COUNTER(xusb, TX_ERRORS)++; + return 0; +failure: + XUSB_COUNTER(xusb, TX_ERRORS)++; + FREE_SEND_XFRAME(xbus, xframe); /* return to pool */ return ret; } -static void xpp_urb_delete(struct urb *urb) -{ - BUG_ON(!urb); - // DBG(GENERAL, "%s: (%d) %p %X", __FUNCTION__, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); - usb_buffer_free (urb->dev, urb->transfer_buffer_length, - urb->transfer_buffer, - urb->transfer_dma); - usb_free_urb(urb); -} - -static struct urb *xpp_urb_new(xusb_t *xusb, enum xusb_dir dir, size_t size) +/* + * PCM wrapper + */ +static int xframe_send_pcm(xbus_t *xbus, xframe_t *xframe) { - struct usb_device *udev = xusb->udev; - struct xusb_endpoint *xusb_ep = &xusb->endpoints[dir]; - unsigned int ep_addr = xusb_ep->ep_addr; - usb_complete_t urb_cb = xusb_ep->callback; - struct urb *urb; - unsigned char *buffer; /* the buffer to send data */ - unsigned int epnum = ep_addr & USB_ENDPOINT_NUMBER_MASK; - int pipe = usb_pipein(ep_addr) - ? usb_rcvbulkpipe(udev, epnum) - : usb_sndbulkpipe(udev, epnum); - - if(size > xusb_ep->max_size) - return NULL; - urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!urb) { - err("No free urbs available"); - return NULL; - } + xusb_t *xusb; - /* on some platforms using this kind of buffer alloc - * call eliminates a dma "bounce buffer". - * - * NOTE: you'd normally want i/o buffers that hold - * more than one packet, so that i/o delays between - * packets don't hurt throughput. (Probably applies only to isochronous - * transfers) - */ - urb->transfer_flags = (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK); - buffer = usb_buffer_alloc(udev, size, GFP_ATOMIC, &urb->transfer_dma); - // DBG(GENERAL, "(%d) %p / %x", size, buffer, urb->transfer_dma); - if (!buffer) { - err("Couldn't allocate buffer"); - usb_free_urb(urb); - return NULL; + BUG_ON(!xbus); + BUG_ON(!xframe); + xusb = xusb_of(xbus); + BUG_ON(!xusb); + if(xusb->drop_next_pcm) { + FREE_SEND_XFRAME(xbus, xframe); /* return to pool */ + xusb->drop_next_pcm = 0; + return -EIO; } - usb_fill_bulk_urb(urb, udev, pipe, buffer, size, urb_cb, xusb); - return urb; + return do_send_xframe(xbus, xframe); } -/*------------------------- Receive Tasklet Handling ---------------*/ - -static void xframe_queue_init(struct xframe_queue *q) +/* + * commands wrapper + */ +static int xframe_send_cmd(xbus_t *xbus, xframe_t *xframe) { - memset(q, 0, sizeof(*q)); - spin_lock_init(&q->lock); - INIT_LIST_HEAD(&q->head); + BUG_ON(!xbus); + BUG_ON(!xframe); + //XBUS_INFO(xbus, "%s:\n", __FUNCTION__); + return do_send_xframe(xbus, xframe); } -static int xframe_enqueue(struct xframe_queue *q, xframe_t *xframe) +/* + * get a urb from the receive_pool and submit it on the read endpoint. + */ +static bool xusb_listen(xusb_t *xusb) { - unsigned long flags; - int ret = 1; + xbus_t *xbus = get_xbus(xusb->xbus_num); + xframe_t *xframe; + struct uframe *uframe; + int ret = 0; - spin_lock_irqsave(&q->lock, flags); - if(q->count >= MAX_RECV_QUEUE) { - q->overflows++; - ret = 0; + BUG_ON(!xbus); + xframe = ALLOC_RECV_XFRAME(xbus); + if(!xframe) { + XBUS_ERR(xbus, "Empty receive_pool\n"); goto out; } - if(++q->count > q->worst_count) - q->worst_count = q->count; - list_add_tail(&xframe->frame_list, &q->head); -out: - spin_unlock_irqrestore(&q->lock, flags); - return ret; -} - -static xframe_t *xframe_dequeue(struct xframe_queue *q) -{ - unsigned long flags; - xframe_t *frm = NULL; - struct list_head *h; - - spin_lock_irqsave(&q->lock, flags); - if(list_empty(&q->head)) + uframe = xframe_to_uframe(xframe); + uframe_recompute(uframe, XUSB_RECV); + ret = usb_submit_urb(&uframe->urb, GFP_ATOMIC); + if(ret < 0) { + XBUS_ERR(xbus, "Failed to submit a receive urb\n"); + FREE_RECV_XFRAME(xbus, xframe); goto out; - h = q->head.next; - list_del(h); - q->count--; - frm = list_entry(h, xframe_t, frame_list); -out: - spin_unlock_irqrestore(&q->lock, flags); - return frm; -} - -static void xusb_handle_xframe(xbus_t *xbus, xframe_t *xframe) -{ - xusb_t *xusb; - int cpu = smp_processor_id(); - - BUG_ON(!xbus); - xusb = xbus->priv; - BUG_ON(!xusb); - xusb->cpu_rcv_intr[cpu]++; - if(!xframe_enqueue(&xusb->receive_queue, xframe)) { - xbus->ops->xframe_free(xbus, xframe); - return; } - tasklet_schedule(&xusb->receive_tasklet); -} - -static void receive_tasklet_func(unsigned long data) -{ - xusb_t *xusb = (xusb_t *)data; - xbus_t *xbus; - xframe_t *xframe = NULL; - int cpu = smp_processor_id(); - - BUG_ON(!xusb); - xbus = xusb->xbus; - BUG_ON(!xbus); - xusb->cpu_rcv_tasklet[cpu]++; - while((xframe = xframe_dequeue(&xusb->receive_queue)) != NULL) - xframe_receive(xbus, xframe); -} - -static void init_receive_tasklet(xusb_t *xusb) -{ - xframe_queue_init(&xusb->receive_queue); - tasklet_init(&xusb->receive_tasklet, receive_tasklet_func, (unsigned long)xusb); + ret = 1; +out: + put_xbus(xbus); + return ret; } /*------------------------- XPP USB Bus Handling -------------------*/ -#define XUSB_MODEL(interface, ep_in,ep_out,type,str) \ - { \ - .iface_num = (interface), \ - .in = { .ep_addr = (ep_in) }, \ - .out = { .ep_addr = (ep_out) }, \ - .bus_type = (type), \ - .desc = (str) \ - } +enum XUSB_MODELS { + MODEL_FPGA_XPD +}; static const struct xusb_model_info { const char *desc; int iface_num; struct xusb_endpoint in; struct xusb_endpoint out; - xbus_type_t bus_type; } model_table[] = { - XUSB_MODEL(0, 0x86, 0x02, FIRMWARE_XPP, "FPGA_XPD"), + [MODEL_FPGA_XPD] { + .iface_num = 0, + .in = { .ep_addr = 0x86 }, + .out = { .ep_addr = 0x02 }, + .desc = "FPGA_XPD" + }, }; /* table of devices that work with this driver */ static const struct usb_device_id xusb_table [] = { -// { USB_DEVICE(0x04B4, 0x8613) }, // default of cypress - { USB_DEVICE(0xE4E4, 0x2211), .driver_info=(kernel_ulong_t)&model_table[0] }, // OLD FPGA - { USB_DEVICE(0xE4E4, 0x1132), .driver_info=(kernel_ulong_t)&model_table[0] }, // FPGA_FXS - { USB_DEVICE(0xE4E4, 0x1142), .driver_info=(kernel_ulong_t)&model_table[0] }, // FPGA_1141 - { USB_DEVICE(0xE4E4, 0x1152), .driver_info=(kernel_ulong_t)&model_table[0] }, // FPGA_1151 - /* "Gadget Zero" firmware runs under Linux */ - //{ USB_DEVICE(0x0525, 0xa4a0) }, + { USB_DEVICE(0xE4E4, 0x1132), .driver_info=(kernel_ulong_t)&model_table[MODEL_FPGA_XPD] }, // FPGA_FXS + { USB_DEVICE(0xE4E4, 0x1142), .driver_info=(kernel_ulong_t)&model_table[MODEL_FPGA_XPD] }, // FPGA_1141 + { USB_DEVICE(0xE4E4, 0x1152), .driver_info=(kernel_ulong_t)&model_table[MODEL_FPGA_XPD] }, // FPGA_1151 + { USB_DEVICE(0xE4E4, 0x1162), .driver_info=(kernel_ulong_t)&model_table[MODEL_FPGA_XPD] }, // FPGA_1161 { } /* Terminating entry */ }; @@ -580,7 +586,7 @@ static int set_endpoints(xusb_t *xusb, struct usb_host_interface *iface_desc, st ep_addr = endpoint->bEndpointAddress; if(!BULK_ENDPOINT(endpoint)) { - DBG(GENERAL, "endpoint 0x%x is not bulk: mbAttributes=0x%X\n", + DBG(DEVICES, "endpoint 0x%x is not bulk: mbAttributes=0x%X\n", ep_addr, endpoint->bmAttributes); continue; } @@ -605,10 +611,10 @@ static int set_endpoints(xusb_t *xusb, struct usb_host_interface *iface_desc, st } } if (!xusb->endpoints[XUSB_RECV].ep_addr || !xusb->endpoints[XUSB_SEND].ep_addr) { - ERR("Couldn't find bulk-in or bulk-out endpoints\n"); + XUSB_ERR(xusb, "Couldn't find bulk-in or bulk-out endpoints\n"); return 0; } - DBG(GENERAL, "in=0x%02X out=0x%02X\n", xusb->endpoints[XUSB_RECV].ep_addr, xusb->endpoints[XUSB_SEND].ep_addr); + DBG(DEVICES, "in=0x%02X out=0x%02X\n", xusb->endpoints[XUSB_RECV].ep_addr, xusb->endpoints[XUSB_SEND].ep_addr); return 1; } @@ -626,18 +632,15 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i struct xusb_model_info *model_info = (struct xusb_model_info*)id->driver_info; struct proc_dir_entry *procsummary = NULL; xbus_t *xbus = NULL; - struct xusb_endpoint *xusb_ep; unsigned long flags; int retval = -ENOMEM; - size_t xframe_size; int i; - DBG(GENERAL, "New XUSB device MODEL=%s bus_type=%d\n", model_info->desc, model_info->bus_type); + DBG(DEVICES, "New XUSB device MODEL=%s\n", model_info->desc); if(iface_desc->desc.bInterfaceNumber != model_info->iface_num) { - DBG(GENERAL, "Skip interface #%d != #%d\n", + DBG(DEVICES, "Skip interface #%d != #%d\n", iface_desc->desc.bInterfaceNumber, model_info->iface_num); - retval = -ENODEV; - goto probe_failed; + return -ENODEV; } /* The USB stack before 2.6.10 seems to be a bit shoddy. It seems that when being called @@ -669,6 +672,8 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i init_MUTEX (&xusb->sem); atomic_set(&xusb->pending_writes, 0); + atomic_set(&xusb->pcm_tx_drops, 0); + atomic_set(&xusb->usb_sluggish_count, 0); xusb->udev = udev; xusb->interface = interface; xusb->model_info = model_info; @@ -677,17 +682,6 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i retval = -ENODEV; goto probe_failed; } - xusb_ep = &xusb->endpoints[XUSB_RECV]; - - /* Receive tasklets */ - init_receive_tasklet(xusb); - - xusb->read_urb = xpp_urb_new(xusb, XUSB_RECV, xusb_ep->max_size); - if (!xusb->read_urb) { - ERR("No free urbs available\n"); - retval = -ENOMEM; - goto probe_failed; - } #ifndef USB_FIELDS_MISSING xusb->serial = udev->serial; xusb->manufacturer = udev->manufacturer; @@ -699,8 +693,8 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i USB_GET_STRING(udev, iProduct, xusb->product); USB_GET_IFACE_NAME(udev, iface_desc, xusb->interface_name); #endif - INFO("XUSB: manufacturer=[%s] product=[%s] serial=[%s] interface=[%s]\n", - xusb->manufacturer, xusb->product, xusb->serial, xusb->interface_name); + INFO("XUSB: %s -- %s -- %s\n", + xusb->manufacturer, xusb->product, xusb->interface_name); /* allow device read, write and ioctl */ xusb->present = 1; @@ -717,17 +711,12 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i xusb->minor = interface->minor; /* let the user know what node this device is now attached to */ - DBG(GENERAL, "USB XPP device now attached to minor %d\n", xusb->minor); - - /* Allocate high level structures */ - xframe_size = min(xusb->endpoints[XUSB_SEND].max_size , xusb->endpoints[XUSB_RECV].max_size); - xbus = xbus_new(&xusb_ops, xframe_size); + DBG(DEVICES, "USB XPP device now attached to minor %d\n", xusb->minor); + xbus = xbus_new(&xusb_ops, min(xusb->endpoints[XUSB_SEND].max_size, xusb->endpoints[XUSB_RECV].max_size), xusb); if(!xbus) { retval = -ENOMEM; goto probe_failed; } - xbus->bus_type = model_info->bus_type; - spin_lock_irqsave(&xusb_lock, flags); for(i = 0; i < MAX_BUSES; i++) { if(xusb_array[i] == NULL) @@ -739,18 +728,13 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i retval = -ENOMEM; goto probe_failed; } - { - char path[XBUS_DESCLEN]; - - usb_make_path(udev, path, XBUS_DESCLEN); // May trunacte... ignore - snprintf(xbus->busdesc, XBUS_DESCLEN, "%s", path); - } + usb_make_path(udev, xusb->path, XBUS_DESCLEN); // May trunacte... ignore + snprintf(xbus->busdesc, XBUS_DESCLEN, "%s", xusb->path); if(xusb->serial && xusb->serial[0]) - snprintf(xbus->serialnum, SERIALNUM_SIZE, "usb:%s", xusb->serial); - DBG(GENERAL, "GOT XPP USB BUS #%d: %s (type=%d)\n", i, xbus->busdesc, xbus->bus_type); - + snprintf(xbus->label, LABEL_SIZE, "usb:%s", xusb->serial); + xusb->index = i; xusb_array[i] = xusb; - + XUSB_DBG(DEVICES, xusb, "GOT XPP USB BUS: %s\n", xbus->busdesc); #ifdef CONFIG_PROC_FS DBG(PROC, "Creating proc entry " PROC_USBXPP_SUMMARY " in bus proc dir.\n"); @@ -765,20 +749,14 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i procsummary->owner = THIS_MODULE; #endif bus_count++; - retval = usb_submit_urb(xusb->read_urb, GFP_ATOMIC); - if(retval < 0) { - ERR("%s: Failed to submit the receive URB errno=%d\n", __FUNCTION__, retval); - } - xusb->xbus = xbus; - xbus->priv = xusb; + xusb->xbus_num = xbus->num; + xusb_listen(xusb); xbus_activate(xbus); return retval; probe_failed: ERR("Failed to initialize xpp usb bus: %d\n", retval); usb_set_intfdata (interface, NULL); if(xusb) { - if(xusb->read_urb) - xpp_urb_delete(xusb->read_urb); if(xusb->minor) // passed registration phase usb_deregister_dev(interface, &xusb_class); kfree(xusb); @@ -812,13 +790,12 @@ static void xusb_disconnect(struct usb_interface *interface) int minor; int i; - DBG(GENERAL, "CALLED\n"); + DBG(DEVICES, "CALLED\n"); /* prevent races with open() */ down (&disconnect_sem); xusb = usb_get_intfdata (interface); - usb_set_intfdata (interface, NULL); - xbus = xusb->xbus; + xbus = get_xbus(xusb->xbus_num); /* find our xusb */ for(i = 0; i < MAX_BUSES; i++) { @@ -830,184 +807,196 @@ static void xusb_disconnect(struct usb_interface *interface) #ifdef CONFIG_PROC_FS if(xbus->proc_xbus_dir) { - XBUS_DBG(GENERAL, xbus, "Remove proc_entry: " PROC_USBXPP_SUMMARY "\n"); + XBUS_DBG(PROC, xbus, "Remove proc_entry: " PROC_USBXPP_SUMMARY "\n"); remove_proc_entry(PROC_USBXPP_SUMMARY, xbus->proc_xbus_dir); } #endif - tasklet_kill(&xusb->receive_tasklet); - xusb->present = 0; + /* + * put_xbus() would be called during xbus_disconnect() + */ xbus_disconnect(xbus); // Blocking until fully deactivated! + xusb->present = 0; + usb_set_intfdata (interface, NULL); down (&xusb->sem); - minor = xusb->minor; /* give back our minor */ usb_deregister_dev (interface, &xusb_class); - /* terminate an ongoing read */ - /* terminate an ongoing write */ - // FIXME: Does it really kill pending URB's? - - if(xusb->read_urb) - xpp_urb_delete(xusb->read_urb); - up (&xusb->sem); - DBG(GENERAL, "Semaphore released\n"); - + DBG(DEVICES, "Semaphore released\n"); kfree(xusb); up (&disconnect_sem); - DBG(GENERAL, "XUSB #%d now disconnected\n", minor); + XUSB_INFO(xusb, "now disconnected\n"); } static void xpp_send_callback(USB_PASS_CB(urb)) { - xusb_t *xusb = (xusb_t *)urb->context; - xbus_t *xbus = xusb->xbus; - struct timeval now; - long usec_diff; + struct uframe *uframe = urb_to_uframe(urb); + xframe_t *xframe = &uframe->xframe; + xusb_t *xusb = uframe->xusb; + xbus_t *xbus = get_xbus(xusb->xbus_num); + struct timeval now; + long usec; + int writes = atomic_read(&xusb->pending_writes); + int i; - BUG_ON(!xbus); + if(!xbus) { + XUSB_ERR(xusb, "Sent URB does not belong to a valid xbus anymore...\n"); + return; + } //flip_parport_bit(6); atomic_dec(&xusb->pending_writes); do_gettimeofday(&now); - usec_diff = - (now.tv_sec - xusb->last_tx.tv_sec)*1000*1000 + - (now.tv_usec - xusb->last_tx.tv_usec); - if(usec_diff > xusb->max_tx_delay) - xusb->max_tx_delay = usec_diff; - if(unlikely(usec_diff > 500)) { - static int rate_limit; - - if((rate_limit++ % 5003) == 0) - XBUS_NOTICE(xbus, "Slagish USB. %ld usec to transmit a frame\n", - usec_diff); - } + xusb->last_tx = xframe->tv_submitted; + usec = usec_diff(&now, &xframe->tv_submitted); + if(usec > xusb->max_tx_delay) + xusb->max_tx_delay = usec; + i = usec / USEC_BUCKET; + if(i >= NUM_BUCKETS) + i = NUM_BUCKETS - 1; + xusb->usb_tx_delay[i]++; + if(unlikely(usec > tx_sluggish)) { + atomic_inc(&xusb->usb_sluggish_count); + if(xusb->sluggish_debounce++ > drop_pcm_after) { + static int rate_limit; + + if((rate_limit++ % 1003) == 500) /* skip first messages */ + XUSB_NOTICE(xusb, + "Sluggish USB. Dropping next PCM frame (pending_writes=%d)\n", + writes); + atomic_inc(&xusb->pcm_tx_drops); + xusb->drop_next_pcm = 1; + xusb->sluggish_debounce = 0; + } + } else + xusb->sluggish_debounce = 0; /* sync/async unlink faults aren't errors */ if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET)) { static int rate_limit; if((rate_limit++ % 1000) < 10) - DBG(GENERAL, "nonzero read bulk status received: %d\n", urb->status); + XUSB_ERR(xusb, + "nonzero write bulk status received: %d (pending_writes=%d)\n", + urb->status, writes); XUSB_COUNTER(xusb, TX_ERRORS)++; - } - xpp_urb_delete(urb); - if(!xusb->present) { - ERR("A packet from non-connected device?\n"); - return; - } - /* allow device read, write and ioctl */ - XUSB_COUNTER(xusb, TX_FRAMES)++; + } else + XUSB_COUNTER(xusb, TX_FRAMES)++; + FREE_SEND_XFRAME(xbus, xframe); + if(!xusb->present) + XUSB_ERR(xusb, "A urb from non-connected device?\n"); + put_xbus(xbus); } static void xpp_receive_callback(USB_PASS_CB(urb)) { - xusb_t *xusb = (xusb_t *)urb->context; - xbus_t *xbus; - xpacket_t *pack; - xframe_t *xframe; + struct uframe *uframe = urb_to_uframe(urb); + xframe_t *xframe = &uframe->xframe; + xusb_t *xusb = uframe->xusb; + xbus_t *xbus = get_xbus(xusb->xbus_num); size_t size; - int retval; bool do_resubmit = 1; bool is_inuse = 0; - struct timeval now; - do_gettimeofday(&now); - BUG_ON(!xusb); - xbus = xusb->xbus; + //flip_parport_bit(7); if(!xbus) { - NOTICE("spurious URB\n"); + XUSB_ERR(xusb, "Received URB does not belong to a valid xbus anymore...\n"); return; } - //flip_parport_bit(7); if (urb->status) { DBG(GENERAL, "nonzero read bulk status received: %d\n", urb->status); XUSB_COUNTER(xusb, RX_ERRORS)++; - /* Free old URB, allocate a fresh one */ - if(xusb->read_urb) - xpp_urb_delete(xusb->read_urb); - xusb->read_urb = xpp_urb_new(xusb, XUSB_RECV, xusb->endpoints[XUSB_RECV].max_size); - if (!xusb->read_urb) { - ERR("URB allocation failed\n"); - do_resubmit = 0;; - } - goto end; + goto err; } - if(!down_read_trylock(&xbus->in_use)) { - ERR("%s: xbus is going down\n", __FUNCTION__); + if(!XBUS_GET(xbus)) { + XUSB_ERR(xusb, "Dropping urb. Is shutting down.\n"); do_resubmit = 0; - goto end; + goto err; } is_inuse = 1; if(!xusb->present) { - ERR("A packet from non-connected device?\n"); + XUSB_ERR(xusb, "A packet from non-connected device?\n"); do_resubmit = 0; - goto end; + goto err; } size = urb->actual_length; if(size == 0) { static int rate_limit; if((rate_limit++ % 5003) == 0) - XBUS_NOTICE(xbus, "Received a zero length URBs (%d)\n", rate_limit); + XUSB_NOTICE(xusb, "Received a zero length URBs (%d)\n", rate_limit); XUSB_COUNTER(xusb, RCV_ZERO_LEN)++; - goto end; - } - xframe = xbus->ops->xframe_new(xbus, GFP_ATOMIC); - if(!xframe) { - ERR("%s: Not enough memory for packets. Dropping\n", __FUNCTION__); - goto end; + goto err; } atomic_set(&xframe->frame_len, size); - pack = (xpacket_t *)xframe->packets; - memcpy(xframe->packets, urb->transfer_buffer, size); - xframe->tv_received = now; + do_gettimeofday(&xframe->tv_received); - if (print_dbg) - dump_xframe("USB_FRAME_RECEIVE", xbus, xframe); +// if (print_dbg) +// dump_xframe("USB_FRAME_RECEIVE", xbus, xframe); XUSB_COUNTER(xusb, RX_FRAMES)++; - // Send UP - if(rx_tasklet) - xusb_handle_xframe(xbus, xframe); - else - xframe_receive(xbus, xframe); + /* Send UP */ + xbus_receive_xframe(xbus, xframe); end: if(is_inuse) - up_read(&xbus->in_use); - if(do_resubmit) { - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval < 0) { - ERR("failed re-submitting read urb, error %d\n", retval); - return; - } - } + XBUS_PUT(xbus); + if(do_resubmit) + xusb_listen(xusb); + put_xbus(xbus); + return; +err: + FREE_RECV_XFRAME(xbus, xframe); + goto end; } /*------------------------- Initialization -------------------------*/ +static void xpp_usb_cleanup(void) +{ + if(xusb_cache) { + kmem_cache_destroy(xusb_cache); + xusb_cache = NULL; + } +} + int __init xpp_usb_init(void) { - int result; + int ret; //xusb_t *xusb; - INFO("revision %s\n", XPP_VERSION); + INFO("revision %s [sizeof(uframe)=%d]\n", XPP_VERSION, sizeof(struct uframe)); + xusb_cache = kmem_cache_create("xusb_cache", + sizeof(xframe_t) + XFRAME_DATASIZE, + 0, 0, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) + NULL, +#endif + NULL); + if(!xusb_cache) { + ret = -ENOMEM; + goto failure; + } /* register this driver with the USB subsystem */ - result = usb_register(&xusb_driver); - if (result) { - ERR("usb_register failed. Error number %d\n", result); - return result; + ret = usb_register(&xusb_driver); + if (ret) { + ERR("usb_register failed. Error number %d\n", ret); + goto failure; } return 0; +failure: + xpp_usb_cleanup(); + return ret; } -void __exit xpp_usb_cleanup(void) +void __exit xpp_usb_shutdown(void) { DBG(GENERAL, "\n"); /* deregister this driver with the USB subsystem */ usb_deregister(&xusb_driver); + xpp_usb_cleanup(); } @@ -1021,6 +1010,8 @@ static int xusb_read_proc(char *page, char **start, off_t off, int count, int *e int i; //unsigned long stamp = jiffies; xusb_t *xusb = data; + uint usb_tx_delay[NUM_BUCKETS]; + const int mark_limit = tx_sluggish/USEC_BUCKET; if(!xusb) goto out; @@ -1033,12 +1024,8 @@ static int xusb_read_proc(char *page, char **start, off_t off, int count, int *e len += sprintf(page + len, "USB: manufacturer=%s\n", xusb->manufacturer); len += sprintf(page + len, "USB: product=%s\n", xusb->product); len += sprintf(page + len, "USB: serial=%s\n", xusb->serial); - len += sprintf(page + len, "Minor: %d\n" - "Model Info: Bus Type=%d (%s)\n", - xusb->minor, - xusb->model_info->bus_type, - xusb->model_info->desc - ); + len += sprintf(page + len, "Minor: %d\nModel Info: %s\n", + xusb->minor, xusb->model_info->desc); len += sprintf(page + len, "Endpoints:\n" "\tIn: 0x%02X - Size: %d)\n" "\tOut: 0x%02X - Size: %d)\n", @@ -1050,25 +1037,22 @@ static int xusb_read_proc(char *page, char **start, off_t off, int count, int *e len += sprintf(page + len, "\npending_writes=%d\n", atomic_read(&xusb->pending_writes)); len += sprintf(page + len, "max_tx_delay=%d\n", xusb->max_tx_delay); xusb->max_tx_delay = 0; - if(rx_tasklet) { - len += sprintf(page + len, - "receive_queue: count = %5d, worst = %5d, overflows = %5d\n", - xusb->receive_queue.count, - xusb->receive_queue.worst_count, - xusb->receive_queue.overflows); - xusb->receive_queue.worst_count = 0; - // xusb->receive_queue.overflows = 0; - } - len += sprintf(page + len, "\ncpu_rcv_intr: "); - for_each_online_cpu(i) - len += sprintf(page + len, "%5d ", xusb->cpu_rcv_intr[i]); - len += sprintf(page + len, "\ncpu_rcv_tasklet: "); - for_each_online_cpu(i) - len += sprintf(page + len, "%5d ", xusb->cpu_rcv_tasklet[i]); - len += sprintf(page + len, "\n"); #ifdef DEBUG_PCM_TIMING len += sprintf(page + len, "\nstamp_last_pcm_read=%lld accumulate_diff=%lld\n", stamp_last_pcm_read, accumulate_diff); #endif + memcpy(usb_tx_delay, xusb->usb_tx_delay, sizeof(usb_tx_delay)); + len += sprintf(page + len, "usb_tx_delay[%d,%d,%d]: ", + USEC_BUCKET, BUCKET_START, NUM_BUCKETS); + for(i = BUCKET_START; i < NUM_BUCKETS; i++) { + len += sprintf(page + len, "%6d ", + usb_tx_delay[i]); + if(i == mark_limit) + len += sprintf(page + len, "| "); + } + len += sprintf(page + len, "\nPCM_TX_DROPS: %5d (sluggish: %d)\n", + atomic_read(&xusb->pcm_tx_drops), + atomic_read(&xusb->usb_sluggish_count) + ); len += sprintf(page + len, "\nCOUNTERS:\n"); for(i = 0; i < XUSB_COUNTER_MAX; i++) { len += sprintf(page + len, "\t%-15s = %d\n", xusb_counters[i].name, xusb->counters[i]); @@ -1094,10 +1078,10 @@ out: -MODULE_DESCRIPTION("XPP USB Driver"); +MODULE_DESCRIPTION("XPP USB Transport Driver"); MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>"); MODULE_LICENSE("GPL"); MODULE_VERSION(XPP_VERSION); module_init(xpp_usb_init); -module_exit(xpp_usb_cleanup); +module_exit(xpp_usb_shutdown); |