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.c239
1 files changed, 157 insertions, 82 deletions
diff --git a/xpp/xpp_usb.c b/xpp/xpp_usb.c
index c147285..368902d 100644
--- a/xpp/xpp_usb.c
+++ b/xpp/xpp_usb.c
@@ -51,6 +51,7 @@ 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");
#include "zap_debug.h"
@@ -81,7 +82,7 @@ struct xusb_endpoint {
static int xusb_xframe_send(xbus_t *xbus, xframe_t *xframe);
-xbus_ops_t xusb_ops = {
+static xbus_ops_t xusb_ops = {
.xframe_send = xusb_xframe_send,
.xframe_new = NULL, // Default allocator
.xframe_free = NULL, // Default deallocator
@@ -120,6 +121,22 @@ enum xusb_dir {
XUSB_SEND = 1,
};
+/* Receive Tasklets */
+#define MAX_RECV_QUEUE 100
+
+struct xframe_queue {
+ struct list_head head;
+ unsigned int count;
+ unsigned int worst_count;
+ unsigned int overflows;
+ spinlock_t lock;
+};
+
+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);
+
/*
* USB XPP Bus (a USB Device)
*/
@@ -134,6 +151,12 @@ typedef struct xpp_usb_bus {
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 */
@@ -189,73 +212,6 @@ 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);
/*------------------------------------------------------------------*/
-#if 0
-#ifndef DEBUG
-
-#define packet_debug(m, x, p)
-
-#else
-
-/* FIXME FIXME FIXME: should move code to card methods */
-static void packet_debug(const char msg[], xusb_t *xusb, xpacket_t *pack)
-{
- char title[XBUS_DESCLEN];
-
- if(pack->opcode == XPROTO_NAME(GLOBAL,PCM_READ)) {
-#ifdef DEBUG_PCM_TIMING
- /*
- * DEBUG: high-res timing of PCM_READ to PCM_WRITE
- */
- stamp_last_pcm_read = get_cycles();
-#endif
-#if 0
- // fill_beep((u_char *)&PACKET_FIELD(pack, PCM_READS, pcm), 2); // Debugging BEEP
- static int rate_limit;
- if((rate_limit++ % 1000) < 10)
- dump_packet("USB RECEIVE PCM", pack, print_dbg);
-#endif
- return;
- } else if(pack->opcode == XPROTO_NAME(GLOBAL,PCM_WRITE)) {
-#ifdef DEBUG_PCM_TIMING
- /*
- * DEBUG: high-res timing of PCM_READ to PCM_WRITE
- */
- cycles_t diff = get_cycles() - stamp_last_pcm_read;
- accumulate_diff += diff;
-#endif
-#if 0
- static int rate_limit;
- if((rate_limit++ % 1000) < 10)
- dump_packet("USB SEND PCM", pack, print_dbg);
-#endif
- return;
- } else if(pack->opcode == XPROTO_NAME(GLOBAL, REGISTER_REQUEST)) {
- reg_cmd_t *regcmd;
-
- regcmd = &RPACKET_FIELD(pack, GLOBAL, REGISTER_REQUEST, reg_cmd);
- if(REG_FIELD(regcmd, regnum) == 0x06) /* ignore SLIC_QUERY */
- return;
- if(REG_FIELD(regcmd, regnum) == DAA_VBAT_REGISTER) /* ignore DAA_QUERY */
- return;
- if(REG_FIELD(regcmd, regnum) == 0x30) { /* ignore BRI query */
-#if 0
- static int rate_limit;
- if((rate_limit++ % 1000) < 10)
- dump_packet("BRI STATE REG", pack, print_dbg);
-#endif
- return;
- }
-#if 0
- } else if(pack->opcode == XPROTO_NAME(FXS, REGISTER_REPLY)) {
- return;
-#endif
- }
- snprintf(title, XBUS_DESCLEN, "%s: %s", msg, xusb->xbus->busname);
- dump_packet(title, pack, print_dbg);
-}
-
-#endif
-#endif
static int xusb_xframe_send(xbus_t *xbus, xframe_t *xframe)
{
@@ -375,6 +331,89 @@ static struct urb *xpp_urb_new(xusb_t *xusb, enum xusb_dir dir, size_t size)
return urb;
}
+/*------------------------- Receive Tasklet Handling ---------------*/
+
+static void xframe_queue_init(struct xframe_queue *q)
+{
+ memset(q, 0, sizeof(*q));
+ spin_lock_init(&q->lock);
+ INIT_LIST_HEAD(&q->head);
+}
+
+static int xframe_enqueue(struct xframe_queue *q, xframe_t *xframe)
+{
+ unsigned long flags;
+ int ret = 1;
+
+ spin_lock_irqsave(&q->lock, flags);
+ if(q->count >= MAX_RECV_QUEUE) {
+ q->overflows++;
+ ret = 0;
+ 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))
+ 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);
+}
+
/*------------------------- XPP USB Bus Handling -------------------*/
#define XUSB_MODEL(ep_in,ep_out,type,str) \
@@ -463,20 +502,24 @@ static struct usb_class_driver xusb_class = {
* If it is 64, it means we have a USB1 controller. By default we do not
* support it and just fail the probe of the device. However if the user
* has set usb1=1, we continue and just put a notice.
+ *
+ * Returns true if all OK, false otherwise.
*/
-int is_usb1_endpoint_size(int ep_addr, int max_packet_size, const char *type)
+static int check_usb1(struct usb_endpoint_descriptor *endpoint)
{
- if(max_packet_size >= sizeof(xpacket_t))
- return 0;
+ const char *msg = (usb_pipein(endpoint->bEndpointAddress))?"input":"output";
+
+ if(endpoint->wMaxPacketSize >= sizeof(xpacket_t))
+ return 1;
if(usb1) {
NOTICE("USB1 endpoint detected: USB %s endpoint 0x%X support only wMaxPacketSize=%d.\n",
- type, ep_addr, max_packet_size);
- return 0;
+ msg, endpoint->bEndpointAddress, endpoint->wMaxPacketSize);
+ return 1;
}
NOTICE("USB1 endpoint detected. Device disabled. To enable: usb1=1, and read docs. (%s, endpoint %d, size %d).\n",
- type, ep_addr, max_packet_size);
- return 1;
+ msg, endpoint->bEndpointAddress, endpoint->wMaxPacketSize);
+ return 0;
}
/*
@@ -508,7 +551,7 @@ static int set_endpoints(xusb_t *xusb, struct usb_interface *interface, struct x
}
if(usb_pipein(ep_addr)) { // Input
if(ep_addr == model_info->in.ep_addr) {
- if (is_usb1_endpoint_size(ep_addr, endpoint->wMaxPacketSize, "input"))
+ if (!check_usb1(endpoint))
return 0;
xusb_ep = &xusb->endpoints[XUSB_RECV];
xusb_ep->ep_addr = ep_addr;
@@ -517,7 +560,7 @@ static int set_endpoints(xusb_t *xusb, struct usb_interface *interface, struct x
}
} else { // Output
if(ep_addr == model_info->out.ep_addr) {
- if (is_usb1_endpoint_size(ep_addr, endpoint->wMaxPacketSize, "output"))
+ if (!check_usb1(endpoint))
return 0;
xusb_ep = &xusb->endpoints[XUSB_SEND];
xusb_ep->ep_addr = ep_addr;
@@ -550,9 +593,10 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i
struct xusb_endpoint *xusb_ep;
unsigned long flags;
int retval = -ENOMEM;
+ size_t xframe_size;
int i;
- INFO("New XUSB device MODEL=%s bus_type=%d\n", model_info->desc, model_info->bus_type);
+ DBG(GENERAL, "New XUSB device MODEL=%s bus_type=%d\n", model_info->desc, model_info->bus_type);
/* The USB stack before 2.6.10 seems to be a bit shoddy. It seems that when being called
* from the probe we may already have the lock to udev (the Usb DEVice). Thus we call
@@ -592,12 +636,17 @@ static int xusb_probe(struct usb_interface *interface, const struct usb_device_i
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;
}
+ DBG(GENERAL, "XUSB: product=%s manufacturer=%s serial=%s\n", udev->product, udev->manufacturer, udev->serial);
/* allow device read, write and ioctl */
xusb->present = 1;
@@ -614,16 +663,16 @@ 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 */
- INFO("USB XPP device now attached to minor %d\n", xusb->minor);
+ DBG(GENERAL, "USB XPP device now attached to minor %d\n", xusb->minor);
/* Allocate high level structures */
- xbus = xbus_new(&xusb_ops);
+ xframe_size = min(xusb->endpoints[XUSB_SEND].max_size , xusb->endpoints[XUSB_RECV].max_size);
+ xbus = xbus_new(&xusb_ops, xframe_size);
if(!xbus) {
retval = -ENOMEM;
goto probe_failed;
}
xbus->bus_type = model_info->bus_type;
- xbus->max_packet_size = min(xusb->endpoints[XUSB_SEND].max_size , xusb->endpoints[XUSB_RECV].max_size);
spin_lock_irqsave(&xusb_lock, flags);
for(i = 0; i < MAX_BUSES; i++) {
@@ -730,6 +779,7 @@ static void xusb_disconnect(struct usb_interface *interface)
remove_proc_entry(PROC_USBXPP_SUMMARY, xbus->proc_xbus_dir);
}
#endif
+ tasklet_kill(&xusb->receive_tasklet);
xusb->present = 0;
xbus_disconnect(xbus); // Blocking until fully deactivated!
@@ -805,7 +855,9 @@ static void xpp_receive_callback(USB_PASS_CB(urb))
int retval;
bool do_resubmit = 1;
bool is_inuse = 0;
+ struct timeval now;
+ do_gettimeofday(&now);
BUG_ON(!xusb);
xbus = xusb->xbus;
if(!xbus) {
@@ -854,12 +906,16 @@ static void xpp_receive_callback(USB_PASS_CB(urb))
atomic_set(&xframe->frame_len, size);
pack = (xpacket_t *)xframe->packets;
memcpy(xframe->packets, urb->transfer_buffer, size);
+ xframe->tv_received = now;
if (print_dbg)
dump_xframe("USB_FRAME_RECEIVE", xbus, xframe);
XUSB_COUNTER(xusb, RX_FRAMES)++;
// Send UP
- xframe_receive(xbus, xframe);
+ if(rx_tasklet)
+ xusb_handle_xframe(xbus, xframe);
+ else
+ xframe_receive(xbus, xframe);
end:
if(is_inuse)
up_read(&xbus->in_use);
@@ -919,6 +975,9 @@ static int xusb_read_proc(char *page, char **start, off_t off, int count, int *e
xusb->udev->bus->busnum,
xusb->udev->devnum
);
+ len += sprintf(page + len, "USB: manufacturer=%s\n", xusb->udev->manufacturer);
+ len += sprintf(page + len, "USB: product=%s\n", xusb->udev->product);
+ len += sprintf(page + len, "USB: serial=%s\n", xusb->udev->serial);
len += sprintf(page + len, "Minor: %d\n"
"Model Info: Bus Type=%d (%s)\n",
xusb->minor,
@@ -936,6 +995,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