From c91b2cd29653da525d1a37cf680f4a75b2d3b208 Mon Sep 17 00:00:00 2001 From: tzafrir Date: Fri, 14 Sep 2007 22:29:36 +0000 Subject: New xpp release (Xorcom rev 4648): * New firmware protocol version: 2.8 . * New firmwares fix input ports offhook. * Cleanup INFO() messages during module loading. * USB: Receive queue with TASKLETS [r4600]. Controlled by rx_tasklet parameter to xpp_usb module (can be changed in runtime). * The pcm_tasklet parameter in xpp module is deprecated: - Does not actually do anything. - If set during module loading, shows an ERR() message. - Also appears in /proc/xpp/sync * FXS: Hardware DTMF detection by default, can be disabled by setting dtmf_detection=0 parameter to xpd_fxs. PCM is muted when DTMF key is pressed. * zapconf: - Can now generate users.conf compatible with asterisk-gui. - Optional command-line arguments denoting which files to generate. Possible values are 'zaptel', 'zapata' and 'users'. - Defaults to creating zaptel and zapata. git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@3019 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- xpp/xpp_usb.c | 239 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 157 insertions(+), 82 deletions(-) (limited to 'xpp/xpp_usb.c') 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 -- cgit v1.2.3