summaryrefslogtreecommitdiff
path: root/xpp/xpp_usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'xpp/xpp_usb.c')
-rw-r--r--xpp/xpp_usb.c832
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);