/******************************************************************* * * Silicon Lab Bonn USB device driver for EZ-USB Controller * * Author: (C) 2007 Sergey Fourletov (fourl@mail.cern.ch) * * This driver is based on the 2.6.16 version of USB Skeleton driver, * usb test driver and dab usb driver. * */ //#define SILAB_VERSION "1.6 2-Jul-2009" //---- Version --------------------- #define SILAB_VERSION "1.7 29-Jun-2010" //---- Version --------------------- /* * * Supported : usbfs ioctl, file open/read/write/ioctl and various tests. * * Supported vendors: * * Supported devices: Cypress EZ-USB FX2 (CY7C68013) * * Modification: * * 5-Mar-2008 added timeout for Bulk IO, by Sergey * 6-Mar-2008 compatibility with Kernel higher than 2.6.19. by Juergen * 19-Sep-2008 compatibility with Kernel higher than 2.6.24. (scatterlist) * 29-May-2009 compatibility with SLC4 Kernel (2.6.9) * 2-Jul-2009 check the number of endpoints : minimum 2. * 29-Jun-2010 add: USB_ANCHOR_DOWNLOAD and USB_VENDOR_REQUEST * ********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef KERNEL_VERSION #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #endif #include "silab.h" /*-------------------------------------------------------------------------*/ //#define SF_DEBUG 1 //#define SF_DEBUG3 1 // FIXME make these public somewhere; usbdevfs.h? // //------------- silab ioctl ---- struct usbsilab_param1 { // inputs unsigned test_num; /* 0..(TEST_CASES-1) */ unsigned iterations; unsigned length; unsigned vary; unsigned sglen; // outputs struct timeval duration; }; //------------- struct usbsilab_param { // inputs unsigned test_num; /* 0..(TEST_CASES-1) */ unsigned iterations; unsigned length; unsigned vary; unsigned sglen; // outputs struct timeval duration; //---- silab ----- unsigned char *ioBuffer; int sizeBuffer; }; #define USBSILAB_REQUEST _IOWR('U', 100, struct usbsilab_param) /*-------------------------------------------------------------------------*/ #define GENERIC /* let probe() bind using module params */ /* Some devices that can be used for testing will have "real" drivers. * Entries for those need to be enabled here by hand, after disabling * that "real" driver. */ //#define IBOT2 /* grab iBOT2 webcams */ //#define KEYSPAN_19Qi /* grab un-renumerated serial adapter */ #define SILAB #define USB_SILAB_VENDOR_ID 0x5312 #define USB_SILAB_PRODUCT_ID 0x0200 #define USB_SILAB_PRODUCT_ID2 0x0011 #define USB_SILAB_TST_PRODUCT_ID 0x0201 /* Get a minor range for your devices from the usb maintainer */ #define USB_SILAB_MINOR_BASE 233 /* our private defines. if this grows any larger, use your own .h file */ #define MAX_TRANSFER ( PAGE_SIZE - 512 ) #define WRITES_IN_FLIGHT 8 /*-------------------------------------------------------------------------*/ struct usbsilab_info { const char *name; u8 ep_in; /* bulk/intr source */ u8 ep_out; /* bulk/intr sink */ unsigned autoconf : 1; unsigned ctrl_out : 1; unsigned iso : 1; /* try iso in/out */ int alt; }; /* this is accessed only through usbfs ioctl calls. * one ioctl to issue a test ... one lock per device. * tests create other threads if they need them. * urbs and buffers are allocated dynamically, * and data generated deterministically. */ struct usbsilab_dev { struct usb_interface *intf; struct usbsilab_info *info; int in_pipe; int out_pipe; int in_iso_pipe; int out_iso_pipe; struct usb_endpoint_descriptor *iso_in, *iso_out; struct semaphore sem; #define TBUF_SIZE 256 u8 *buf; //-------------- add struct usb_silab ---------------------- .fsv. --------------------------- /* Structure to hold all of our device specific stuff */ struct usb_device * udev; /* the usb device for this device */ struct usb_interface * interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ unsigned char * bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ struct kref kref; }; //============================================================================================ static struct usb_device *silabdev_to_usbdev (struct usbsilab_dev *silab) { return interface_to_usbdev (silab->intf); } /* set up all urbs so they can be used with either bulk or interrupt */ #define INTERRUPT_RATE 1 /* msec/transfer */ #define xprintk(tdev,level,fmt,args...) \ dev_printk(level , &(tdev)->intf->dev , fmt , ## args) #ifdef DEBUG #define DBG(dev,fmt,args...) \ xprintk(dev , KERN_DEBUG , fmt , ## args) #else #define DBG(dev,fmt,args...) \ do { } while (0) #endif /* DEBUG */ #ifdef VERBOSE #define VDBG DBG #else #define VDBG(dev,fmt,args...) \ do { } while (0) #endif /* VERBOSE */ #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) /*#define WARN(dev,fmt,args...) \*/ /* xprintk(dev , KERN_WARNING , fmt , ## args) */ #define INFO(dev,fmt,args...) \ xprintk(dev , KERN_INFO , fmt , ## args) #undef info #define info(format, arg...) printk(KERN_INFO "silab: " format "\n" ,## arg) #define info_0(format, arg...) printk(KERN_INFO "%s: " format "\n" , \ __FILE__ , ## arg) /*-------------------------------------------------------------------------*/ static int ezusb_cpucs (struct usbsilab_dev *dev, int doRun); /*-------------------------------------------------------------------*/ /* re-definition of these functions in kernel 2.6.35 */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) #define usb_buffer_alloc usb_alloc_coherent #define usb_buffer_free usb_free_coherent #endif #ifdef MY_DEBUG static void dump_urb (struct urb *urb) { dbg("urb :%p", urb); dbg("dev :%p", urb->dev); dbg("pipe :%08X", urb->pipe); dbg("status :%d", urb->status); dbg("transfer_flags :%08X", urb->transfer_flags); dbg("transfer_buffer :%p", urb->transfer_buffer); dbg("transfer_buffer_length:%d", urb->transfer_buffer_length); dbg("actual_length :%d", urb->actual_length); dbg("setup_packet :%p", urb->setup_packet); dbg("start_frame :%d", urb->start_frame); dbg("number_of_packets :%d", urb->number_of_packets); dbg("interval :%d", urb->interval); dbg("error_count :%d", urb->error_count); dbg("context :%p", urb->context); dbg("complete :%p", urb->complete); } static void dump_descr (struct usb_device_descriptor *deviceDescriptor) { info ("SlbUsb Device Descriptor:"); info ("-------------------------"); info ("bLength %d", deviceDescriptor->bLength); info ("bDescriptorType 0x%x", deviceDescriptor->bDescriptorType); info ("bcdUSB 0x%x", deviceDescriptor->bcdUSB); info ("bDeviceClass 0x%x", deviceDescriptor->bDeviceClass); info ("bDeviceSubClass 0x%x", deviceDescriptor->bDeviceSubClass); info ("bDeviceProtocol 0x%x", deviceDescriptor->bDeviceProtocol); info ("bMaxPacketSize0 0x%x", deviceDescriptor->bMaxPacketSize0); info ("idVendor 0x%x", deviceDescriptor->idVendor); info ("idProduct 0x%x", deviceDescriptor->idProduct); info ("bcdDevice 0x%x", deviceDescriptor->bcdDevice); info ("iManufacturer 0x%x", deviceDescriptor->iManufacturer); info ("iProduct 0x%x", deviceDescriptor->iProduct); info ("iSerialNumber 0x%x", deviceDescriptor->iSerialNumber); info ("bNumConfigurations 0x%x", deviceDescriptor->bNumConfigurations); } //---------------------------------------------------------------------------- #endif /*-------------------------------------------------------------------*/ static int get_endpoints (struct usbsilab_dev *dev, struct usb_interface *intf) { int tmp; struct usb_host_interface *alt; struct usb_host_endpoint *in, *out; struct usb_host_endpoint *iso_in, *iso_out; struct usb_device *udev; for (tmp = 0; tmp < intf->num_altsetting; tmp++) { unsigned ep; in = out = NULL; iso_in = iso_out = NULL; alt = intf->altsetting + tmp; /* take the first altsetting with in-bulk + out-bulk; * ignore other endpoints and altsetttings. */ for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) { struct usb_host_endpoint *e; e = alt->endpoint + ep; switch (e->desc.bmAttributes) { case USB_ENDPOINT_XFER_BULK: break; case USB_ENDPOINT_XFER_ISOC: if (dev->info->iso) goto try_iso; // FALLTHROUGH default: continue; } if (e->desc.bEndpointAddress & USB_DIR_IN) { if (!in) in = e; } else { if (!out) out = e; } continue; try_iso: if (e->desc.bEndpointAddress & USB_DIR_IN) { if (!iso_in) iso_in = e; } else { if (!iso_out) iso_out = e; } } if ((in && out) || (iso_in && iso_out)) goto found; } return -EINVAL; found: udev = silabdev_to_usbdev (dev); if (alt->desc.bAlternateSetting != 0) { tmp = usb_set_interface (udev, alt->desc.bInterfaceNumber, alt->desc.bAlternateSetting); if (tmp < 0) return tmp; } if (in) { dev->in_pipe = usb_rcvbulkpipe (udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->out_pipe = usb_sndbulkpipe (udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); } if (iso_in) { dev->iso_in = &iso_in->desc; dev->in_iso_pipe = usb_rcvisocpipe (udev, iso_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->iso_out = &iso_out->desc; dev->out_iso_pipe = usb_sndisocpipe (udev, iso_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); } return 0; } /*-------------------------------------------------------------------------*/ /* Support for testing basic non-queued I/O streams. * * These just package urbs as requests that can be easily canceled. * Each urb's data buffer is dynamically allocated; callers can fill * them with non-zero test data (or test for it) when appropriate. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) static void silab_callback (struct urb *urb) #else static void silab_callback (struct urb *urb, struct pt_regs *regs) #endif { complete ((struct completion *) urb->context); } static struct urb *silab_alloc_urb ( struct usb_device *udev, int pipe, unsigned long bytes ) { struct urb *urb; if (bytes < 0) return NULL; urb = usb_alloc_urb (0, GFP_KERNEL); if (!urb) return urb; usb_fill_bulk_urb (urb, udev, pipe, NULL, bytes, silab_callback, NULL); urb->interval = (udev->speed == USB_SPEED_HIGH) ? (INTERRUPT_RATE << 3) : INTERRUPT_RATE; urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; if (usb_pipein (pipe)) urb->transfer_flags |= URB_SHORT_NOT_OK; urb->transfer_buffer = usb_buffer_alloc (udev, bytes, GFP_KERNEL, &urb->transfer_dma); if (!urb->transfer_buffer) { usb_free_urb (urb); urb = NULL; } else memset (urb->transfer_buffer, 0, bytes); return urb; } static unsigned pattern = 0; module_param (pattern, uint, S_IRUGO); // MODULE_PARM_DESC (pattern, "i/o pattern (0 == zeroes)"); static inline void silab_fill_buf (struct urb *urb) { unsigned i; u8 *buf = urb->transfer_buffer; unsigned len = urb->transfer_buffer_length; switch (pattern) { default: // FALLTHROUGH case 0: memset (buf, 0, len); break; case 1: /* mod63 */ for (i = 0; i < len; i++) *buf++ = (u8) (i % 63); break; } } static inline int silab_check_buf (struct urb *urb) { unsigned i; u8 expected; u8 *buf = urb->transfer_buffer; unsigned len = urb->actual_length; for (i = 0; i < len; i++, buf++) { switch (pattern) { /* all-zeroes has no synchronization issues */ case 0: expected = 0; break; /* mod63 stays in sync with short-terminated transfers, * or otherwise when host and gadget agree on how large * each usb transfer request should be. resync is done * with set_interface or set_config. */ case 1: /* mod63 */ expected = i % 63; break; /* always fail unsupported patterns */ default: expected = !*buf; break; } if (*buf == expected) continue; dbg ("buf[%d] = %d (not %d)", i, *buf, expected); return -EINVAL; } return 0; } static void silab_free_urb (struct urb *urb) { usb_buffer_free (urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); usb_free_urb (urb); } static int silab_io ( struct urb *urb, int iterations, int vary, int expected, const char *label ) { struct usb_device *udev = urb->dev; int max = urb->transfer_buffer_length; struct completion completion; int retval = 0; urb->context = &completion; while (retval == 0 && iterations-- > 0) { init_completion (&completion); // if (usb_pipeout (urb->pipe)) silab_fill_buf (urb); if ((retval = usb_submit_urb (urb, GFP_KERNEL)) != 0) break; // info("io:: usb_submit_urb ret=%d , wait ...",retval); /* NOTE: no timeouts; can't be broken out of by interrupt */ wait_for_completion (&completion); //wait_for_completion_timeout (&completion,10*HZ); //-- 10 sec ? retval = urb->status; // info("io:: wait ... done !! ret=%d",retval); urb->dev = udev; /* if (retval == 0 && usb_pipein (urb->pipe)) { retval = silab_check_buf (urb); info("io:: check buf ... done !! ret=%d",retval); } */ if (vary) { int len = urb->transfer_buffer_length; len += vary; len %= max; if (len == 0) len = (vary < max) ? vary : max; urb->transfer_buffer_length = len; } /* FIXME if endpoint halted, clear halt (and log) */ } urb->transfer_buffer_length = max; if (expected != retval) dev_dbg (&udev->dev, "%s failed, iterations left %d, status %d (not %d)\n", label, iterations, retval, expected); return retval; } /*-------------------------------------------------------------------------*/ /* Loop io .fsv. */ /*-------------------------------------------------------------------------*/ static int silab_loop ( struct urb *urb, int iterations, int vary, int expected, const char *label ) { struct usb_device *udev = urb->dev; struct completion completion; int retval = 0; // urb->transfer_buffer_length = max; urb->context = &completion; init_completion (&completion); if (usb_pipeout (urb->pipe)) silab_fill_buf (urb); info("loop pipe ep=0x%x test: IN=0x%x OUT=0x%x" ,usb_pipeendpoint(urb->pipe) ,usb_pipein (urb->pipe),usb_pipeout (urb->pipe)); //.fsv. if ((retval = usb_submit_urb (urb, GFP_KERNEL)) != 0) return retval; info("loop usb_submit_urb ret=%d , wait ...",retval); /* NOTE: no timeouts; can't be broken out of by interrupt */ wait_for_completion (&completion); retval = urb->status; info("loop wait ... done !!! ret=%d max-size=%d realsize=%d " ,retval,urb->transfer_buffer_length,urb->actual_length); /* urb->dev = udev; if (retval == 0 && usb_pipein (urb->pipe)) retval = silab_check_buf (urb); */ /* FIXME if endpoint halted, clear halt (and log) */ if (retval) dev_dbg (&udev->dev, "%s failed, iterations left %d, status %d (not %d)\n", label, iterations, retval, expected); return retval; } /*-------------------------------------------------------------------------*/ /* We use scatterlist primitives to test queued I/O. * Yes, this also tests the scatterlist primitives. */ static void free_sglist (struct scatterlist *sg, int nents) { unsigned i; if (!sg) return; for (i = 0; i < nents; i++) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) if (!sg_page(&sg[i])) continue; kfree (sg_virt(&sg[i])); #else if (!sg [i].page) continue; kfree (page_address (sg [i].page) + sg [i].offset); #endif } kfree (sg); } static struct scatterlist * alloc_sglist (int nents, int max, int vary) { struct scatterlist *sg; unsigned i; unsigned size = max; sg = kmalloc (nents * sizeof *sg, GFP_KERNEL); if (!sg) return NULL; for (i = 0; i < nents; i++) { char *buf; buf = kmalloc (size, GFP_KERNEL); if (!buf) { free_sglist (sg, i); return NULL; } memset (buf, 0, size); /* kmalloc pages are always physically contiguous! */ sg_init_one(&sg[i], buf, size); if (vary) { size += vary; size %= max; if (size == 0) size = (vary < max) ? vary : max; } } return sg; } static int perform_sglist ( struct usb_device *udev, unsigned iterations, int pipe, struct usb_sg_request *req, struct scatterlist *sg, int nents ) { int retval = 0; while (retval == 0 && iterations-- > 0) { retval = usb_sg_init (req, udev, pipe, (udev->speed == USB_SPEED_HIGH) ? (INTERRUPT_RATE << 3) : INTERRUPT_RATE, sg, nents, 0, GFP_KERNEL); if (retval) break; usb_sg_wait (req); retval = req->status; /* FIXME if endpoint halted, clear halt (and log) */ } // FIXME for unlink or fault handling tests, don't report // failure if retval is as we expected ... if (retval) dbg ("perform_sglist failed, iterations left %d, status %d", iterations, retval); return retval; } /*-------------------------------------------------------------------------*/ /* unqueued control message testing * * there's a nice set of device functional requirements in chapter 9 of the * usb 2.0 spec, which we can apply to ANY device, even ones that don't use * special test firmware. * * we know the device is configured (or suspended) by the time it's visible * through usbfs. we can't change that, so we won't test enumeration (which * worked 'well enough' to get here, this time), power management (ditto), * or remote wakeup (which needs human interaction). */ static unsigned realworld = 1; module_param (realworld, uint, 0); MODULE_PARM_DESC (realworld, "clear to demand stricter spec compliance"); static int get_altsetting (struct usbsilab_dev *dev) { struct usb_interface *iface = dev->intf; struct usb_device *udev = interface_to_usbdev (iface); int retval; retval = usb_control_msg (udev, usb_rcvctrlpipe (udev, 0), USB_REQ_GET_INTERFACE, USB_DIR_IN|USB_RECIP_INTERFACE, 0, iface->altsetting [0].desc.bInterfaceNumber, dev->buf, 1, USB_CTRL_GET_TIMEOUT); switch (retval) { case 1: return dev->buf [0]; case 0: retval = -ERANGE; // FALLTHROUGH default: return retval; } } static int set_altsetting (struct usbsilab_dev *dev, int alternate) { struct usb_interface *iface = dev->intf; struct usb_device *udev; if (alternate < 0 || alternate >= 256) return -EINVAL; udev = interface_to_usbdev (iface); return usb_set_interface (udev, iface->altsetting [0].desc.bInterfaceNumber, alternate); } static int is_good_config (char *buf, int len) { struct usb_config_descriptor *config; if (len < sizeof *config) return 0; config = (struct usb_config_descriptor *) buf; switch (config->bDescriptorType) { case USB_DT_CONFIG: case USB_DT_OTHER_SPEED_CONFIG: if (config->bLength != 9) { dbg ("bogus config descriptor length"); return 0; } /* this bit 'must be 1' but often isn't */ if (!realworld && !(config->bmAttributes & 0x80)) { dbg ("high bit of config attributes not set"); return 0; } if (config->bmAttributes & 0x1f) { /* reserved == 0 */ dbg ("reserved config bits set"); return 0; } break; default: return 0; } if (le16_to_cpu(config->wTotalLength) == len) /* read it all */ return 1; if (le16_to_cpu(config->wTotalLength) >= TBUF_SIZE) /* max partial read */ return 1; dbg ("bogus config descriptor read size"); return 0; } /* sanity test for standard requests working with usb_control_mesg() and some * of the utility functions which use it. * * this doesn't test how endpoint halts behave or data toggles get set, since * we won't do I/O to bulk/interrupt endpoints here (which is how to change * halt or toggle). toggle testing is impractical without support from hcds. * * this avoids failing devices linux would normally work with, by not testing * config/altsetting operations for devices that only support their defaults. * such devices rarely support those needless operations. * * NOTE that since this is a sanity test, it's not examining boundary cases * to see if usbcore, hcd, and device all behave right. such testing would * involve varied read sizes and other operation sequences. */ static int ch9_postconfig (struct usbsilab_dev *dev) { struct usb_interface *iface = dev->intf; struct usb_device *udev = interface_to_usbdev (iface); int i, alt, retval; /* [9.2.3] if there's more than one altsetting, we need to be able to * set and get each one. mostly trusts the descriptors from usbcore. */ for (i = 0; i < iface->num_altsetting; i++) { /* 9.2.3 constrains the range here */ alt = iface->altsetting [i].desc.bAlternateSetting; if (alt < 0 || alt >= iface->num_altsetting) { dev_dbg (&iface->dev, "invalid alt [%d].bAltSetting = %d\n", i, alt); } /* [real world] get/set unimplemented if there's only one */ if (realworld && iface->num_altsetting == 1) continue; /* [9.4.10] set_interface */ retval = set_altsetting (dev, alt); if (retval) { dev_dbg (&iface->dev, "can't set_interface = %d, %d\n", alt, retval); return retval; } /* [9.4.4] get_interface always works */ retval = get_altsetting (dev); if (retval != alt) { dev_dbg (&iface->dev, "get alt should be %d, was %d\n", alt, retval); return (retval < 0) ? retval : -EDOM; } } /* [real world] get_config unimplemented if there's only one */ if (!realworld || udev->descriptor.bNumConfigurations != 1) { int expected = udev->actconfig->desc.bConfigurationValue; /* [9.4.2] get_configuration always works * ... although some cheap devices (like one TI Hub I've got) * won't return config descriptors except before set_config. */ retval = usb_control_msg (udev, usb_rcvctrlpipe (udev, 0), USB_REQ_GET_CONFIGURATION, USB_DIR_IN | USB_RECIP_DEVICE, 0, 0, dev->buf, 1, USB_CTRL_GET_TIMEOUT); if (retval != 1 || dev->buf [0] != expected) { dev_dbg (&iface->dev, "get config --> %d %d (1 %d)\n", retval, dev->buf[0], expected); return (retval < 0) ? retval : -EDOM; } } /* there's always [9.4.3] a device descriptor [9.6.1] */ retval = usb_get_descriptor (udev, USB_DT_DEVICE, 0, dev->buf, sizeof udev->descriptor); if (retval != sizeof udev->descriptor) { dev_dbg (&iface->dev, "dev descriptor --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } /* there's always [9.4.3] at least one config descriptor [9.6.3] */ for (i = 0; i < udev->descriptor.bNumConfigurations; i++) { retval = usb_get_descriptor (udev, USB_DT_CONFIG, i, dev->buf, TBUF_SIZE); if (!is_good_config (dev->buf, retval)) { dev_dbg (&iface->dev, "config [%d] descriptor --> %d\n", i, retval); return (retval < 0) ? retval : -EDOM; } // FIXME cross-checking udev->config[i] to make sure usbcore // parsed it right (etc) would be good testing paranoia } /* and sometimes [9.2.6.6] speed dependent descriptors */ if (le16_to_cpu(udev->descriptor.bcdUSB) == 0x0200) { struct usb_qualifier_descriptor *d = NULL; /* device qualifier [9.6.2] */ retval = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0, dev->buf, sizeof (struct usb_qualifier_descriptor)); if (retval == -EPIPE) { if (udev->speed == USB_SPEED_HIGH) { dev_dbg (&iface->dev, "hs dev qualifier --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } /* usb2.0 but not high-speed capable; fine */ } else if (retval != sizeof (struct usb_qualifier_descriptor)) { dev_dbg (&iface->dev, "dev qualifier --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } else d = (struct usb_qualifier_descriptor *) dev->buf; /* might not have [9.6.2] any other-speed configs [9.6.4] */ if (d) { unsigned max = d->bNumConfigurations; for (i = 0; i < max; i++) { retval = usb_get_descriptor (udev, USB_DT_OTHER_SPEED_CONFIG, i, dev->buf, TBUF_SIZE); if (!is_good_config (dev->buf, retval)) { dev_dbg (&iface->dev, "other speed config --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } } } } // FIXME fetch strings from at least the device descriptor /* [9.4.5] get_status always works */ retval = usb_get_status (udev, USB_RECIP_DEVICE, 0, dev->buf); if (retval != 2) { dev_dbg (&iface->dev, "get dev status --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } // FIXME configuration.bmAttributes says if we could try to set/clear // the device's remote wakeup feature ... if we can, test that here retval = usb_get_status (udev, USB_RECIP_INTERFACE, iface->altsetting [0].desc.bInterfaceNumber, dev->buf); if (retval != 2) { dev_dbg (&iface->dev, "get interface status --> %d\n", retval); return (retval < 0) ? retval : -EDOM; } // FIXME get status for each endpoint in the interface return 0; } /*-------------------------------------------------------------------------*/ /* use ch9 requests to test whether: * (a) queues work for control, keeping N subtests queued and * active (auto-resubmit) for M loops through the queue. * (b) protocol stalls (control-only) will autorecover. * it's not like bulk/intr; no halt clearing. * (c) short control reads are reported and handled. * (d) queues are always processed in-order */ struct ctrl_ctx { spinlock_t lock; struct usbsilab_dev *dev; struct completion complete; unsigned count; unsigned pending; int status; struct urb **urb; struct usbsilab_param *param; int last; }; #define NUM_SUBCASES 15 /* how many test subcases here? */ struct subcase { struct usb_ctrlrequest setup; int number; int expected; }; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) static void ctrl_complete (struct urb *urb) #else static void ctrl_complete (struct urb *urb, struct pt_regs *regs) #endif { struct ctrl_ctx *ctx = urb->context; struct usb_ctrlrequest *reqp; struct subcase *subcase; int status = urb->status; reqp = (struct usb_ctrlrequest *)urb->setup_packet; subcase = container_of (reqp, struct subcase, setup); spin_lock (&ctx->lock); ctx->count--; ctx->pending--; /* queue must transfer and complete in fifo order, unless * usb_unlink_urb() is used to unlink something not at the * physical queue head (not tested). */ if (subcase->number > 0) { if ((subcase->number - ctx->last) != 1) { dbg ("subcase %d completed out of order, last %d", subcase->number, ctx->last); status = -EDOM; ctx->last = subcase->number; goto error; } } ctx->last = subcase->number; /* succeed or fault in only one way? */ if (status == subcase->expected) status = 0; /* async unlink for cleanup? */ else if (status != -ECONNRESET) { /* some faults are allowed, not required */ if (subcase->expected > 0 && ( ((urb->status == -subcase->expected /* happened */ || urb->status == 0)))) /* didn't */ status = 0; /* sometimes more than one fault is allowed */ else if (subcase->number == 12 && status == -EPIPE) status = 0; else dbg ("subtest %d error, status %d", subcase->number, status); } /* unexpected status codes mean errors; ideally, in hardware */ if (status) { error: if (ctx->status == 0) { int i; ctx->status = status; info ("control queue %02x.%02x, err %d, %d left", reqp->bRequestType, reqp->bRequest, status, ctx->count); /* FIXME this "unlink everything" exit route should * be a separate test case. */ /* unlink whatever's still pending */ for (i = 1; i < ctx->param->sglen; i++) { struct urb *u = ctx->urb [ (i + subcase->number) % ctx->param->sglen]; if (u == urb || !u->dev) continue; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) spin_unlock(&ctx->lock); #endif status = usb_unlink_urb (u); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) spin_lock(&ctx->lock); #endif switch (status) { case -EINPROGRESS: case -EBUSY: case -EIDRM: continue; default: dbg ("urb unlink --> %d", status); } } status = ctx->status; } } /* resubmit if we need to, else mark this as done */ if ((status == 0) && (ctx->pending < ctx->count)) { if ((status = usb_submit_urb (urb, GFP_ATOMIC)) != 0) { dbg ("can't resubmit ctrl %02x.%02x, err %d", reqp->bRequestType, reqp->bRequest, status); urb->dev = NULL; } else ctx->pending++; } else urb->dev = NULL; /* signal completion when nothing's queued */ if (ctx->pending == 0) complete (&ctx->complete); spin_unlock (&ctx->lock); } static int silab_ctrl_queue (struct usbsilab_dev *dev, struct usbsilab_param *param) { struct usb_device *udev = silabdev_to_usbdev (dev); struct urb **urb; struct ctrl_ctx context; int i; spin_lock_init (&context.lock); context.dev = dev; init_completion (&context.complete); context.count = param->sglen * param->iterations; context.pending = 0; context.status = -ENOMEM; context.param = param; context.last = -1; /* allocate and init the urbs we'll queue. * as with bulk/intr sglists, sglen is the queue depth; it also * controls which subtests run (more tests than sglen) or rerun. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) urb = kcalloc(param->sglen, sizeof(struct urb *), GFP_KERNEL); #else urb = kmalloc (param->sglen * sizeof (struct urb *), GFP_KERNEL); #endif if (!urb) return -ENOMEM; memset (urb, 0, param->sglen * sizeof (struct urb *)); for (i = 0; i < param->sglen; i++) { int pipe = usb_rcvctrlpipe (udev, 0); unsigned len; struct urb *u; struct usb_ctrlrequest req; struct subcase *reqp; int expected = 0; /* requests here are mostly expected to succeed on any * device, but some are chosen to trigger protocol stalls * or short reads. */ memset (&req, 0, sizeof req); req.bRequest = USB_REQ_GET_DESCRIPTOR; req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE; switch (i % NUM_SUBCASES) { case 0: // get device descriptor req.wValue = cpu_to_le16 (USB_DT_DEVICE << 8); len = sizeof (struct usb_device_descriptor); break; case 1: // get first config descriptor (only) req.wValue = cpu_to_le16 ((USB_DT_CONFIG << 8) | 0); len = sizeof (struct usb_config_descriptor); break; case 2: // get altsetting (OFTEN STALLS) req.bRequest = USB_REQ_GET_INTERFACE; req.bRequestType = USB_DIR_IN|USB_RECIP_INTERFACE; // index = 0 means first interface len = 1; expected = EPIPE; break; case 3: // get interface status req.bRequest = USB_REQ_GET_STATUS; req.bRequestType = USB_DIR_IN|USB_RECIP_INTERFACE; // interface 0 len = 2; break; case 4: // get device status req.bRequest = USB_REQ_GET_STATUS; req.bRequestType = USB_DIR_IN|USB_RECIP_DEVICE; len = 2; break; case 5: // get device qualifier (MAY STALL) req.wValue = cpu_to_le16 (USB_DT_DEVICE_QUALIFIER << 8); len = sizeof (struct usb_qualifier_descriptor); if (udev->speed != USB_SPEED_HIGH) expected = EPIPE; break; case 6: // get first config descriptor, plus interface req.wValue = cpu_to_le16 ((USB_DT_CONFIG << 8) | 0); len = sizeof (struct usb_config_descriptor); len += sizeof (struct usb_interface_descriptor); break; case 7: // get interface descriptor (ALWAYS STALLS) req.wValue = cpu_to_le16 (USB_DT_INTERFACE << 8); // interface == 0 len = sizeof (struct usb_interface_descriptor); expected = EPIPE; break; // NOTE: two consecutive stalls in the queue here. // that tests fault recovery a bit more aggressively. case 8: // clear endpoint halt (USUALLY STALLS) req.bRequest = USB_REQ_CLEAR_FEATURE; req.bRequestType = USB_RECIP_ENDPOINT; // wValue 0 == ep halt // wIndex 0 == ep0 (shouldn't halt!) len = 0; pipe = usb_sndctrlpipe (udev, 0); expected = EPIPE; break; case 9: // get endpoint status req.bRequest = USB_REQ_GET_STATUS; req.bRequestType = USB_DIR_IN|USB_RECIP_ENDPOINT; // endpoint 0 len = 2; break; case 10: // trigger short read (EREMOTEIO) req.wValue = cpu_to_le16 ((USB_DT_CONFIG << 8) | 0); len = 1024; expected = -EREMOTEIO; break; // NOTE: two consecutive _different_ faults in the queue. case 11: // get endpoint descriptor (ALWAYS STALLS) req.wValue = cpu_to_le16 (USB_DT_ENDPOINT << 8); // endpoint == 0 len = sizeof (struct usb_interface_descriptor); expected = EPIPE; break; // NOTE: sometimes even a third fault in the queue! case 12: // get string 0 descriptor (MAY STALL) req.wValue = cpu_to_le16 (USB_DT_STRING << 8); // string == 0, for language IDs len = sizeof (struct usb_interface_descriptor); // may succeed when > 4 languages expected = EREMOTEIO; // or EPIPE, if no strings break; case 13: // short read, resembling case 10 req.wValue = cpu_to_le16 ((USB_DT_CONFIG << 8) | 0); // last data packet "should" be DATA1, not DATA0 len = 1024 - udev->descriptor.bMaxPacketSize0; expected = -EREMOTEIO; break; case 14: // short read; try to fill the last packet req.wValue = cpu_to_le16 ((USB_DT_DEVICE << 8) | 0); // device descriptor size == 18 bytes len = udev->descriptor.bMaxPacketSize0; switch (len) { case 8: len = 24; break; case 16: len = 32; break; } expected = -EREMOTEIO; break; default: err ("bogus number of ctrl queue testcases!"); context.status = -EINVAL; goto cleanup; } req.wLength = cpu_to_le16 (len); urb [i] = u = silab_alloc_urb (udev, pipe, len); if (!u) goto cleanup; reqp = usb_buffer_alloc (udev, sizeof *reqp, GFP_KERNEL, &u->setup_dma); if (!reqp) goto cleanup; reqp->setup = req; reqp->number = i % NUM_SUBCASES; reqp->expected = expected; u->setup_packet = (char *) &reqp->setup; /* support for URB_NO_SETUP_DMA_MAP was removed in 2.6.34 */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)) u->transfer_flags |= URB_NO_SETUP_DMA_MAP; #endif u->context = &context; u->complete = ctrl_complete; } /* queue the urbs */ context.urb = urb; spin_lock_irq (&context.lock); for (i = 0; i < param->sglen; i++) { context.status = usb_submit_urb (urb [i], GFP_ATOMIC); if (context.status != 0) { dbg ("can't submit urb[%d], status %d", i, context.status); context.count = context.pending; break; } context.pending++; } spin_unlock_irq (&context.lock); /* FIXME set timer and time out; provide a disconnect hook */ /* wait for the last one to complete */ if (context.pending > 0) wait_for_completion (&context.complete); cleanup: for (i = 0; i < param->sglen; i++) { if (!urb [i]) continue; urb [i]->dev = udev; if (urb [i]->setup_packet) usb_buffer_free (udev, sizeof (struct usb_ctrlrequest), urb [i]->setup_packet, urb [i]->setup_dma); silab_free_urb (urb [i]); } kfree (urb); return context.status; } #undef NUM_SUBCASES /*-------------------------------------------------------------------------*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) static void unlink1_callback (struct urb *urb) #else static void unlink1_callback (struct urb *urb, struct pt_regs *regs) #endif { int status = urb->status; // we "know" -EPIPE (stall) never happens if (!status) status = usb_submit_urb (urb, GFP_ATOMIC); if (status) { urb->status = status; complete ((struct completion *) urb->context); } } static int unlink1 (struct usbsilab_dev *dev, int pipe, int size, int async) { struct urb *urb; struct completion completion; int retval = 0; init_completion (&completion); urb = silab_alloc_urb (silabdev_to_usbdev (dev), pipe, size); if (!urb) return -ENOMEM; urb->context = &completion; urb->complete = unlink1_callback; /* keep the endpoint busy. there are lots of hc/hcd-internal * states, and testing should get to all of them over time. * * FIXME want additional tests for when endpoint is STALLing * due to errors, or is just NAKing requests. */ if ((retval = usb_submit_urb (urb, GFP_KERNEL)) != 0) { dev_dbg (&dev->intf->dev, "submit fail %d\n", retval); return retval; } /* unlinking that should always work. variable delay tests more * hcd states and code paths, even with little other system load. */ msleep (jiffies % (2 * INTERRUPT_RATE)); if (async) { retry: retval = usb_unlink_urb (urb); if (retval == -EBUSY || retval == -EIDRM) { /* we can't unlink urbs while they're completing. * or if they've completed, and we haven't resubmitted. * "normal" drivers would prevent resubmission, but * since we're testing unlink paths, we can't. */ dev_dbg (&dev->intf->dev, "unlink retry\n"); goto retry; } } else usb_kill_urb (urb); if (!(retval == 0 || retval == -EINPROGRESS)) { dev_dbg (&dev->intf->dev, "unlink fail %d\n", retval); return retval; } wait_for_completion (&completion); retval = urb->status; silab_free_urb (urb); if (async) return (retval == -ECONNRESET) ? 0 : retval - 1000; else return (retval == -ENOENT || retval == -EPERM) ? 0 : retval - 2000; } static int unlink_silab (struct usbsilab_dev *dev, int pipe, int len) { int retval = 0; /* test sync and async paths */ retval = unlink1 (dev, pipe, len, 1); if (!retval) retval = unlink1 (dev, pipe, len, 0); return retval; } /*-------------------------------------------------------------------------*/ static int verify_not_halted (int ep, struct urb *urb) { int retval; u16 status; /* shouldn't look or act halted */ retval = usb_get_status (urb->dev, USB_RECIP_ENDPOINT, ep, &status); if (retval < 0) { dbg ("ep %02x couldn't get no-halt status, %d", ep, retval); return retval; } if (status != 0) { dbg ("ep %02x bogus status: %04x != 0", ep, status); return -EINVAL; } retval = silab_io (urb, 1, 0, 0, __FUNCTION__); if (retval != 0) return -EINVAL; return 0; } static int verify_halted (int ep, struct urb *urb) { int retval; u16 status; /* should look and act halted */ retval = usb_get_status (urb->dev, USB_RECIP_ENDPOINT, ep, &status); if (retval < 0) { dbg ("ep %02x couldn't get halt status, %d", ep, retval); return retval; } if (status != 1) { dbg ("ep %02x bogus status: %04x != 1", ep, status); return -EINVAL; } retval = silab_io (urb, 1, 0, -EPIPE, __FUNCTION__); if (retval != -EPIPE) return -EINVAL; retval = silab_io (urb, 1, 0, -EPIPE, "verify_still_halted"); if (retval != -EPIPE) return -EINVAL; return 0; } static int silab_halt (int ep, struct urb *urb) { int retval; /* shouldn't look or act halted now */ retval = verify_not_halted (ep, urb); if (retval < 0) return retval; /* set halt (protocol test only), verify it worked */ retval = usb_control_msg (urb->dev, usb_sndctrlpipe (urb->dev, 0), USB_REQ_SET_FEATURE, USB_RECIP_ENDPOINT, USB_ENDPOINT_HALT, ep, NULL, 0, USB_CTRL_SET_TIMEOUT); if (retval < 0) { dbg ("ep %02x couldn't set halt, %d", ep, retval); return retval; } retval = verify_halted (ep, urb); if (retval < 0) return retval; /* clear halt (tests API + protocol), verify it worked */ retval = usb_clear_halt (urb->dev, urb->pipe); if (retval < 0) { dbg ("ep %02x couldn't clear halt, %d", ep, retval); return retval; } retval = verify_not_halted (ep, urb); if (retval < 0) return retval; /* NOTE: could also verify SET_INTERFACE clear halts ... */ return 0; } static int halt_silab (struct usbsilab_dev *dev) { int ep; int retval = 0; struct urb *urb; urb = silab_alloc_urb (silabdev_to_usbdev (dev), 0, 512); if (urb == NULL) return -ENOMEM; if (dev->in_pipe) { ep = usb_pipeendpoint (dev->in_pipe) | USB_DIR_IN; urb->pipe = dev->in_pipe; retval = silab_halt (ep, urb); if (retval < 0) goto done; } if (dev->out_pipe) { ep = usb_pipeendpoint (dev->out_pipe); urb->pipe = dev->out_pipe; retval = silab_halt (ep, urb); } done: silab_free_urb (urb); return retval; } /*-------------------------------------------------------------------------*/ /* Control OUT tests use the vendor control requests from Intel's * USB 2.0 compliance test device: write a buffer, read it back. * * Intel's spec only _requires_ that it work for one packet, which * is pretty weak. Some HCDs place limits here; most devices will * need to be able to handle more than one OUT data packet. We'll * try whatever we're told to try. */ static int ctrl_out (struct usbsilab_dev *dev, unsigned count, unsigned length, unsigned vary) { unsigned i, j, len, retval; u8 *buf; char *what = "?"; struct usb_device *udev; if (length < 1 || length > 0xffff || vary >= length) return -EINVAL; buf = kmalloc(length, GFP_KERNEL); if (!buf) return -ENOMEM; udev = silabdev_to_usbdev (dev); len = length; retval = 0; /* NOTE: hardware might well act differently if we pushed it * with lots back-to-back queued requests. */ for (i = 0; i < count; i++) { /* write patterned data */ for (j = 0; j < len; j++) buf [j] = i + j; retval = usb_control_msg (udev, usb_sndctrlpipe (udev,0), 0x5b, USB_DIR_OUT|USB_TYPE_VENDOR, 0, 0, buf, len, USB_CTRL_SET_TIMEOUT); if (retval != len) { what = "write"; if (retval >= 0) { INFO(dev, "ctrl_out, wlen %d (expected %d)\n", retval, len); retval = -EBADMSG; } break; } /* read it back -- assuming nothing intervened!! */ retval = usb_control_msg (udev, usb_rcvctrlpipe (udev,0), 0x5c, USB_DIR_IN|USB_TYPE_VENDOR, 0, 0, buf, len, USB_CTRL_GET_TIMEOUT); if (retval != len) { what = "read"; if (retval >= 0) { INFO(dev, "ctrl_out, rlen %d (expected %d)\n", retval, len); retval = -EBADMSG; } break; } /* fail if we can't verify */ for (j = 0; j < len; j++) { if (buf [j] != (u8) (i + j)) { INFO (dev, "ctrl_out, byte %d is %d not %d\n", j, buf [j], (u8) i + j); retval = -EBADMSG; break; } } if (retval < 0) { what = "verify"; break; } len += vary; /* [real world] the "zero bytes IN" case isn't really used. * hardware can easily trip up in this wierd case, since its * status stage is IN, not OUT like other ep0in transfers. */ if (len > length) len = realworld ? 1 : 0; } if (retval < 0) INFO (dev, "ctrl_out %s failed, code %d, count %d\n", what, retval, i); kfree (buf); return retval; } /*-------------------------------------------------------------------------*/ /* ISO tests ... mimics common usage * - buffer length is split into N packets (mostly maxpacket sized) * - multi-buffers according to sglen */ struct iso_context { unsigned count; unsigned pending; spinlock_t lock; struct completion done; unsigned long errors; struct usbsilab_dev *dev; }; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) static void iso_callback (struct urb *urb) #else static void iso_callback (struct urb *urb, struct pt_regs *regs) #endif { struct iso_context *ctx = urb->context; spin_lock(&ctx->lock); ctx->count--; if (urb->error_count > 0) ctx->errors += urb->error_count; if (urb->status == 0 && ctx->count > (ctx->pending - 1)) { int status = usb_submit_urb (urb, GFP_ATOMIC); switch (status) { case 0: goto done; default: dev_dbg (&ctx->dev->intf->dev, "iso resubmit err %d\n", status); /* FALLTHROUGH */ case -ENODEV: /* disconnected */ break; } } silab_free_urb (urb); ctx->pending--; if (ctx->pending == 0) { if (ctx->errors) dev_dbg (&ctx->dev->intf->dev, "iso test, %lu errors\n", ctx->errors); complete (&ctx->done); } done: spin_unlock(&ctx->lock); } static struct urb *iso_alloc_urb ( struct usb_device *udev, int pipe, struct usb_endpoint_descriptor *desc, long bytes ) { struct urb *urb; unsigned i, maxp, packets; if (bytes < 0 || !desc) return NULL; maxp = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); maxp *= 1 + (0x3 & (le16_to_cpu(desc->wMaxPacketSize) >> 11)); packets = (bytes + maxp - 1) / maxp; urb = usb_alloc_urb (packets, GFP_KERNEL); if (!urb) return urb; urb->dev = udev; urb->pipe = pipe; urb->number_of_packets = packets; urb->transfer_buffer_length = bytes; urb->transfer_buffer = usb_buffer_alloc (udev, bytes, GFP_KERNEL, &urb->transfer_dma); if (!urb->transfer_buffer) { usb_free_urb (urb); return NULL; } memset (urb->transfer_buffer, 0, bytes); for (i = 0; i < packets; i++) { /* here, only the last packet will be short */ urb->iso_frame_desc[i].length = min ((unsigned) bytes, maxp); bytes -= urb->iso_frame_desc[i].length; urb->iso_frame_desc[i].offset = maxp * i; } urb->complete = iso_callback; // urb->context = SET BY CALLER urb->interval = 1 << (desc->bInterval - 1); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; return urb; } static int silab_iso_queue (struct usbsilab_dev *dev, struct usbsilab_param *param, int pipe, struct usb_endpoint_descriptor *desc) { struct iso_context context; struct usb_device *udev; unsigned i; unsigned long packets = 0; int status; struct urb *urbs[10]; /* FIXME no limit */ if (param->sglen > 10) return -EDOM; context.count = param->iterations * param->sglen; context.pending = param->sglen; context.errors = 0; context.dev = dev; init_completion (&context.done); spin_lock_init (&context.lock); memset (urbs, 0, sizeof urbs); udev = silabdev_to_usbdev (dev); dev_dbg (&dev->intf->dev, "... iso period %d %sframes, wMaxPacket %04x\n", 1 << (desc->bInterval - 1), (udev->speed == USB_SPEED_HIGH) ? "micro" : "", le16_to_cpu(desc->wMaxPacketSize)); for (i = 0; i < param->sglen; i++) { urbs [i] = iso_alloc_urb (udev, pipe, desc, param->length); if (!urbs [i]) { status = -ENOMEM; goto fail; } packets += urbs[i]->number_of_packets; urbs [i]->context = &context; } packets *= param->iterations; dev_dbg (&dev->intf->dev, "... total %lu msec (%lu packets)\n", (packets * (1 << (desc->bInterval - 1))) / ((udev->speed == USB_SPEED_HIGH) ? 8 : 1), packets); spin_lock_irq (&context.lock); for (i = 0; i < param->sglen; i++) { status = usb_submit_urb (urbs [i], GFP_ATOMIC); if (status < 0) { ERROR (dev, "submit iso[%d], error %d\n", i, status); if (i == 0) { spin_unlock_irq (&context.lock); goto fail; } silab_free_urb (urbs [i]); context.pending--; } } spin_unlock_irq (&context.lock); wait_for_completion (&context.done); return 0; fail: for (i = 0; i < param->sglen; i++) { if (urbs [i]) silab_free_urb (urbs [i]); } return status; } /*-------------------------------------------------------------------------*/ /* FILE IOCTL (from dab) */ /*-------------------------------------------------------------------------*/ //----------------------------------------------------------------------------------------------- static int bulk_io_urb (struct urb *urb) { struct completion completion; int retval = 0; urb->context = &completion; init_completion (&completion); if ((retval = usb_submit_urb (urb, GFP_KERNEL)) != 0) return retval; //wait_for_completion (&completion); if (!wait_for_completion_timeout (&completion,10*HZ)) { //-- 10 sec ?? retval = -ETIMEDOUT; usb_kill_urb(urb); } else { retval = urb->status; } /* FIXME if endpoint halted, clear halt (and log) */ return retval; } //----------------------------------------------------------------------------------------------- // #1 //----------------------------------------------------------------------------------------------- static int file_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int ret=0,i,in_pipe,out_pipe; char *CharDescr; struct urb *urb; struct usbsilab_dev *dev = (struct usbsilab_dev*) file->private_data; struct usb_device *udev = silabdev_to_usbdev (dev); struct usbsilab_param *param = (struct usbsilab_param *)arg; struct usb_interface *intf; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; USBD_INTERFACE_INFORMATION *pInterface = (USBD_INTERFACE_INFORMATION*) arg; //windows PGET_STRING_DESCRIPTOR_IN Input = (PGET_STRING_DESCRIPTOR_IN)arg; pUsb_String_Descriptor StrDescr = (pUsb_String_Descriptor)arg; BULK_IOCTL *bulk_io = (BULK_IOCTL*) arg; VENDOR_REQUEST_IN *vendor_rq = (VENDOR_REQUEST_IN*) arg; BULK_TRANSFER_CONTROL *Control; int u_pipe, req_type; unsigned char *buffer; unsigned char data; //int subminor; //subminor = iminor(inode); //intf = usb_find_interface(&usbsilab_driver, subminor); intf=dev->intf; #ifdef SF_DEBUG3 info("#--------------------> file_ioctl=0x%x",cmd); /* info("# file_ioctl expected= 0x%x 0x%x 0x%x" ,IOCTL_SLBUSB_GET_DEVICE_DESCRIPTOR ,IOCTL_SLBUSB_GET_PIPE_INFO ,USBSILAB_REQUEST); */ /* info("# file_ioctl func=%d sizeof=%d ",param->test_num,sizeof(InterfaceInfo)); info("# dev->bulk_in_endpointAddr=0x%x",dev->bulk_in_endpointAddr); info("# dev->bulk_out_endpointAddr=0x%x",dev->bulk_out_endpointAddr); info("# dev->bulk_in_size=%d",dev->bulk_in_size); info("# dev->in_pipe=0x%x",dev->in_pipe); info("# dev->out_pipe=0x%x",dev->out_pipe); info("matched module params, vend=0x%04x prod=0x%04x", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); */ //dump_descr(&udev->descriptor); #endif switch (cmd) { //.................................... case IOCTL_SLBUSB_GET_DEVICE_DESCRIPTOR: // // inputs - pointer to a buffer in which to place descriptor data // outputs - we put the device descriptor data, if any is returned by the device // in the system buffer and then we set the length inthe Information field // in the Irp, which will then cause the system to copy the buffer back // to the user's buffer // #ifdef SF_DEBUG info("---> FILE_IO: IOCTL_SLBUSB_GET_DEVICE_DESCRIPTOR"); #endif if (copy_to_user((void __user *)arg,&udev->descriptor,sizeof(udev->descriptor))) ret = -EFAULT; #ifdef SF_DEBUG info("* Get Device Descriptor returned %d bytes", sizeof(udev->descriptor)); #endif break; //.................................... case IOCTL_SLBUSB_GET_PIPE_INFO: // // inputs - none // outputs - we copy the interface information structure that we have // stored in our device extension area to the output buffer which // will be reflected to the user mode application by the IOS. // info("---> FILE_IO: IOCTL_SLBUSB_GET_PIPE_INFO"); //== iface_desc = intf->cur_altsetting; info("* the Number of endpoint = %d",iface_desc->desc.bNumEndpoints); pInterface->Length=sizeof(USBD_INTERFACE_INFORMATION) +sizeof(USBD_PIPE_INFORMATION)*iface_desc->desc.bNumEndpoints; info("* pInterface->Length=%d",pInterface->Length); pInterface->Class =iface_desc->desc.bInterfaceClass; pInterface->SubClass =iface_desc->desc.bInterfaceSubClass; pInterface->Protocol =iface_desc->desc.bInterfaceProtocol; pInterface->InterfaceHandle =NULL; //&iface_desc->desc.iInterface; //-- fsv FIXME!! pInterface->NumberOfPipes =iface_desc->desc.bNumEndpoints; /* set up the endpoint information */ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; pInterface->Pipes[i].MaximumPacketSize =endpoint->wMaxPacketSize; pInterface->Pipes[i].EndpointAddress =endpoint->bEndpointAddress; pInterface->Pipes[i].Interval =endpoint->bInterval; pInterface->Pipes[i].PipeType =endpoint->bDescriptorType; pInterface->Pipes[i].MaximumTransferSize = MAX_BULK_IOCTL; // FIXME !! __bulk_ioctl if (endpoint->bEndpointAddress & USB_DIR_IN) { u_pipe = usb_rcvbulkpipe (udev, // void-FIXME !!! endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); } else { u_pipe = usb_sndbulkpipe (udev, // void-FIXME !!! endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); } //pInterface->Pipes[i].PipeHandle = u_pipe; pInterface->Pipes[i].PipeHandle=NULL; /* dev->in_pipe = usb_rcvbulkpipe (udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->out_pipe = usb_sndbulkpipe (udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); pInterface->Pipes[i].PipeHandle = NULL; // =dev->in_pipe; FIXME !!! */ info("* ep=%d bEndpointAddress=0x%02x size=%3d num=%d pipe_handle=0x%x",i ,endpoint->bEndpointAddress, endpoint->wMaxPacketSize ,endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK ,u_pipe); } //Irp->IoStatus.Information = length; //Irp->IoStatus.Status = STATUS_SUCCESS; break; //.................................... case IOCTL_SLBUSB_GET_STRING_DESCRIPTOR: info("---> FILE_IO: IOCTL_SLBUSB_GET_STRING_DESCRIPTOR"); info("* LanguageId=%d Index=%d ",Input->LanguageId,Input->Index); if (udev->product) info("* string-Product=%s len=%ld",udev->product,strlen(udev->product)+1); if (udev->manufacturer) info("* string-Manufact=%s len=%ld",udev->manufacturer ,strlen(udev->manufacturer)+1); if (udev->serial)info("* string-serial=%s len=%ld",udev->serial,strlen(udev->serial)+1); i=Input->Index; StrDescr->bLength=0; StrDescr->bString[0]=0; CharDescr=NULL; switch (i) { case 0: CharDescr=udev->product; break; case 1: CharDescr=udev->manufacturer; break; case 2: CharDescr=udev->serial; break; } if (CharDescr) { if (copy_to_user(StrDescr->bString,CharDescr,strlen(CharDescr)+1)) ret = -EFAULT; //for (i=0;ibString[i]=CharDescr[i]; StrDescr->bLength=strlen(CharDescr)+1; } break; //.................................... case IOCTL_SLBUSB_BULK_WRITE: Control=(BULK_TRANSFER_CONTROL*)bulk_io->Control; if (!Control || !bulk_io) { info("!!!> ERROR::FILE_IO: IOCTL_SLBUSB_BULK_WRITE Control=%p bulk_io=%p",Control,bulk_io); return -EFAULT; } i=Control->pipeNum; #ifdef SF_DEBUG info("---> FILE_IO: IOCTL_SLBUSB_BULK_WRITE pipe=%d len=%d",i,bulk_io->sizeBuffer); #endif iface_desc = intf->cur_altsetting; endpoint = &iface_desc->endpoint[i].desc; out_pipe = usb_sndbulkpipe (udev,endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); #ifdef SF_DEBUG info(" + out bEndpointAddress=0x%x out_pipe=0x%x MaxSize=%d (%d)" ,endpoint->bEndpointAddress,out_pipe,endpoint->wMaxPacketSize ,usb_maxpacket(udev,out_pipe,1)); #endif urb = silab_alloc_urb (udev, out_pipe, bulk_io->sizeBuffer); if (!urb) { ret = -ENOMEM; break; } if (copy_from_user(urb->transfer_buffer,bulk_io->ioBuffer, bulk_io->sizeBuffer)) { ret = -EFAULT; info("!!!!---> bulk_write:: error copy_from_user "); } else ret = bulk_io_urb (urb); if (ret) info("!!!!bulk_write:: ERROR: ---> bulk_io_urb:: ret=%d",ret); /* if (bulk_io->sizeBuffer>1000) for (i=0;i<20;i++) info("to write: i=%d %02x " ,i,*((unsigned char*)urb->transfer_buffer+i)); else { printk("--> to write: "); for (i=0;isizeBuffer;i++) printk(" %x",*((unsigned char*)urb->transfer_buffer+i)); printk(" <--\n"); } */ #ifdef SF_DEBUG info(" + Length:: BUF=%d< ACTUAL=%d ",urb->transfer_buffer_length,urb->actual_length); #endif bulk_io->doneBytes=urb->actual_length; silab_free_urb (urb); break; //....................................##### case IOCTL_SLBUSB_BULK_READ: Control=(BULK_TRANSFER_CONTROL*)bulk_io->Control; if (!Control || !bulk_io) { info("!!!> ERROR::FILE_IO: IOCTL_SLBUSB_BULK_READ Control=%p bulk_io=%p",Control,bulk_io); return -EFAULT; } i=Control->pipeNum; #ifdef SF_DEBUG info("---> FILE_IO: IOCTL_SLBUSB_BULK_READ pipe=%d len=%d",i,bulk_io->sizeBuffer); #endif iface_desc = intf->cur_altsetting; endpoint = &iface_desc->endpoint[i].desc; in_pipe = usb_rcvbulkpipe (udev,endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); #ifdef SF_DEBUG info(" + in bEndpointAddress=0x%x in_pipe=0x%x MaxSise=%d (%d)" ,endpoint->bEndpointAddress,in_pipe,endpoint->wMaxPacketSize ,usb_maxpacket(udev,in_pipe,0)); #endif urb = silab_alloc_urb (udev, in_pipe, bulk_io->sizeBuffer); if (!urb) { ret = -ENOMEM; break; } ret = bulk_io_urb (urb); if (ret) info("!!!!bulk_read:: ERROR: ---> bulk_io_urb:: ret=%d",ret); #ifdef SF_DEBUG info(" + Length:: BUF=%d ACTUAL=%d ",urb->transfer_buffer_length,urb->actual_length); #endif if (ret==0) { if (copy_to_user(bulk_io->ioBuffer, urb->transfer_buffer, urb->actual_length)) ret = -EFAULT; bulk_io->doneBytes=urb->actual_length; } else { bulk_io->doneBytes=0; } silab_free_urb (urb); break; //.................................... case IOCTL_SLBUSB_RESET_CPU: info("********* R E S E T C P U *****************************"); ezusb_cpucs (dev,0); ezusb_cpucs (dev,1); info("********* D O N E *****************************"); break; //========================================================================================= case IOCTL_SLBUSB_VENDOR_REQUEST: info("---> FILE_IO: IOCTL_SLBUSB_VENDOR_REQUEST: \n\t bRequest=0x%x \n\t wValue=0x%x \n\t wIndex=0x%x \n\t wLength=0x%x \n\t direction=0x%x \n\t bData=0x%x", vendor_rq->bRequest, vendor_rq->wValue, vendor_rq->wIndex, vendor_rq->wLength, vendor_rq->direction, vendor_rq->bData); if (vendor_rq->direction) req_type=USB_DIR_IN|USB_TYPE_VENDOR; else req_type=USB_DIR_OUT|USB_TYPE_VENDOR; data=vendor_rq->bData; //info("---> FILE_IO: IOCTL_SLBUSB_VENDOR_REQUEST: req_type=0x%x",req_type); ret = usb_control_msg(udev, usb_sndctrlpipe (udev,0), vendor_rq->bRequest, req_type, vendor_rq->wValue, 0,&data, vendor_rq->wLength, 3000); if (ret==vendor_rq->wLength) ret=0; if (vendor_rq->direction) vendor_rq->bData=data; break; //========================================================================================= case IOCTL_SLBUSB_ANCHOR_DOWNLOAD: info("---> IOCTL_SLBUSB_ANCHOR_DOWNLOAD: size=%d ",bulk_io->sizeControl); if (bulk_io->sizeControl>0xFFFF) ret = -EFAULT; buffer = kmalloc(bulk_io->sizeControl, GFP_KERNEL); if (copy_from_user(buffer,bulk_io->Control,bulk_io->sizeControl)) { info("!!!!---> ANCHOR_DOWNLOAD:: error copy_from_user "); return -EFAULT; } ret = usb_control_msg (udev, usb_sndctrlpipe (udev,0), 0xA0, USB_DIR_OUT|USB_TYPE_VENDOR ,0,0, buffer,bulk_io->sizeControl, 3000); INFO(dev, "ctrl_out, wlen %d (expected %d), req=0x%x \n" ,ret,bulk_io->sizeControl,USB_DIR_OUT|USB_TYPE_VENDOR); if (ret != bulk_io->sizeControl) { if (ret >= 0) { info("!!!!---> ANCHOR_DOWNLOAD:: ERROR: ctrl_out, len=%d (expected %d)\n",ret,bulk_io->sizeControl); } ret=-EFAULT; } else { ret=0; } kfree(buffer); break; //......................................................................................... case USBSILAB_REQUEST: switch (param->test_num) { case 0: info("FILE_IO:: TEST 0: NOP\n"); break; case 1: info("FILE_IO::TEST 1: write %d bytes %u times ", param->length, param->iterations); break; } } return ret; } /*-------------------------------------------------------------------------*/ /* We only have this one interface to user space, through usbfs. * User mode code can scan usbfs to find N different devices (maybe on * different busses) to use when testing, and allocate one thread per * test. So discovery is simplified, and we have no device naming issues. * * Don't use these only as stress/load tests. Use them along with with * other USB bus activity: plugging, unplugging, mousing, mp3 playback, * video capture, and so on. Run different tests at different times, in * different sequences. Nothing here should interact with other devices, * except indirectly by consuming USB bandwidth and CPU resources for test * threads and request completion. But the only way to know that for sure * is to test when HC queues are in use by many devices. */ static int usbsilab_ioctl (struct usb_interface *intf, unsigned int code, void *buf) { struct usbsilab_dev *dev = usb_get_intfdata (intf); struct usb_device *udev = silabdev_to_usbdev (dev); struct usbsilab_param *param = buf; int retval = -EOPNOTSUPP; struct urb *urb; struct scatterlist *sg; struct usb_sg_request req; struct timeval start; unsigned i; // FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is. if (code != USBSILAB_REQUEST) return -EOPNOTSUPP; if (param->iterations <= 0 || param->length < 0 || param->sglen < 0 || param->vary < 0) return -EINVAL; if (down_interruptible (&dev->sem)) return -ERESTARTSYS; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)) // --- SLC4 if (intf->dev.power.power_state.event != PM_EVENT_ON) { up (&dev->sem); return -EHOSTUNREACH; } #endif /* some devices, like ez-usb default devices, need a non-default * altsetting to have any active endpoints. some tests change * altsettings; force a default so most tests don't need to check. */ // fsv if (dev->info->alt >= 0) { if (dev->info->alt < 0) { int res; if (intf->altsetting->desc.bInterfaceNumber) { up (&dev->sem); return -ENODEV; } res = set_altsetting (dev, dev->info->alt); if (res) { dev_err (&intf->dev, "set altsetting to %d failed, %d\n", dev->info->alt, res); up (&dev->sem); return res; } } /* * Just a bunch of test cases that every HCD is expected to handle. * * Some may need specific firmware, though it'd be good to have * one firmware image to handle all the test cases. * * FIXME add more tests! cancel requests, verify the data, control * queueing, concurrent read+write threads, and so on. */ do_gettimeofday (&start); switch (param->test_num) { case 0: dev_dbg (&intf->dev, "TEST 0: NOP\n"); retval = 0; break; /* Simple non-queued bulk I/O tests */ case 1: if (dev->out_pipe == 0) break; //dev_dbg (&intf->dev, // "TEST 1: write %d bytes %u times\n", // param->length, param->iterations); urb = silab_alloc_urb (udev, dev->out_pipe, param->length); if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) retval = silab_io (urb, param->iterations, 0, 0, "test1"); silab_free_urb (urb); break; case 2: if (dev->in_pipe == 0) break; //info ("TEST 2: read %d bytes %u times", // param->length, param->iterations); urb = silab_alloc_urb (udev, dev->in_pipe, param->length); if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) retval = silab_io (urb, param->iterations, 0, 0, "test2"); silab_free_urb (urb); break; //--------------- --------------------------------------------------- /* Simple non-queued bulk I/O tests */ case 21: if (dev->out_pipe == 0) break; //dev_dbg (&intf->dev, // "TEST 21: write %d bytes %u times\n", // param->length, param->iterations); urb = silab_alloc_urb (udev, dev->out_pipe, param->length); if (!urb) { retval = -ENOMEM; break; } if (copy_from_user(urb->transfer_buffer,param->ioBuffer,param->length)) { retval = -ENOMEM; } // FIRMWARE: bulk sink (maybe accepts short writes) retval = silab_io (urb, param->iterations, 0, 0, "test1"); silab_free_urb (urb); break; case 22: if (dev->in_pipe == 0) break; // info ("TEST 22: read %d bytes %u times", // param->length, param->iterations); urb = silab_alloc_urb (udev, dev->in_pipe, param->length); if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) retval = silab_io (urb, param->iterations, 0, 0, "test2"); if (urb->actual_length > param->sizeBuffer) { retval = -ENOMEM; } else { if(copy_to_user(param->ioBuffer, urb->transfer_buffer, urb->actual_length)) { retval = -ENOMEM; } } param->sizeBuffer=urb->actual_length; silab_free_urb (urb); break; //---------------##2 --------------------------------------------------- case 3: if (dev->out_pipe == 0 || param->vary == 0) break; info ("TEST 3: write/%d 0..%d bytes %u times", param->vary, param->length, param->iterations); urb = silab_alloc_urb (udev, dev->out_pipe, param->length); if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) retval = silab_io (urb, param->iterations, param->vary, 0, "test3"); silab_free_urb (urb); break; //--------------- fsv loop ------------------------------------------ case 32: if (dev->out_pipe == 0 || param->vary == 0) break; info("TEST 32: ---------------------------------------"); info(" WRITE/%d 0..%d bytes %u times", param->vary, param->length, param->iterations); urb = silab_alloc_urb (udev, dev->out_pipe, param->length); if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) retval = silab_loop (urb, param->iterations, param->vary, 0, "test3"); silab_free_urb (urb); //----------------- read -------------------------------- if (dev->in_pipe == 0) break; info(" READ %d bytes %u times", param->length, param->iterations); urb = silab_alloc_urb (udev, dev->in_pipe, param->length); if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) retval = silab_loop (urb, param->iterations, 0, 0, "test2"); silab_free_urb (urb); break; break; //--------------------------------------------------------- case 4: if (dev->in_pipe == 0 || param->vary == 0) break; dev_dbg (&intf->dev, "TEST 4: read/%d 0..%d bytes %u times\n", param->vary, param->length, param->iterations); urb = silab_alloc_urb (udev, dev->in_pipe, param->length); if (!urb) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) retval = silab_io (urb, param->iterations, param->vary, 0, "test4"); silab_free_urb (urb); break; /* Queued bulk I/O tests */ case 5: if (dev->out_pipe == 0 || param->sglen == 0) break; dev_dbg (&intf->dev, "TEST 5: write %d sglists %d entries of %d bytes\n", param->iterations, param->sglen, param->length); sg = alloc_sglist (param->sglen, param->length, 0); if (!sg) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) retval = perform_sglist (udev, param->iterations, dev->out_pipe, &req, sg, param->sglen); free_sglist (sg, param->sglen); break; case 6: if (dev->in_pipe == 0 || param->sglen == 0) break; dev_dbg (&intf->dev, "TEST 6: read %d sglists %d entries of %d bytes\n", param->iterations, param->sglen, param->length); sg = alloc_sglist (param->sglen, param->length, 0); if (!sg) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) retval = perform_sglist (udev, param->iterations, dev->in_pipe, &req, sg, param->sglen); free_sglist (sg, param->sglen); break; case 7: if (dev->out_pipe == 0 || param->sglen == 0 || param->vary == 0) break; dev_dbg (&intf->dev, "TEST 7: write/%d %d sglists %d entries 0..%d bytes\n", param->vary, param->iterations, param->sglen, param->length); sg = alloc_sglist (param->sglen, param->length, param->vary); if (!sg) { retval = -ENOMEM; break; } // FIRMWARE: bulk sink (maybe accepts short writes) retval = perform_sglist (udev, param->iterations, dev->out_pipe, &req, sg, param->sglen); free_sglist (sg, param->sglen); break; case 8: if (dev->in_pipe == 0 || param->sglen == 0 || param->vary == 0) break; dev_dbg (&intf->dev, "TEST 8: read/%d %d sglists %d entries 0..%d bytes\n", param->vary, param->iterations, param->sglen, param->length); sg = alloc_sglist (param->sglen, param->length, param->vary); if (!sg) { retval = -ENOMEM; break; } // FIRMWARE: bulk source (maybe generates short writes) retval = perform_sglist (udev, param->iterations, dev->in_pipe, &req, sg, param->sglen); free_sglist (sg, param->sglen); break; /* non-queued sanity tests for control (chapter 9 subset) */ case 9: retval = 0; dev_dbg (&intf->dev, "TEST 9: ch9 (subset) control tests, %d times\n", param->iterations); for (i = param->iterations; retval == 0 && i--; /* NOP */) retval = ch9_postconfig (dev); if (retval) dbg ("ch9 subset failed, iterations left %d", i); break; /* queued control messaging */ case 10: if (param->sglen == 0) break; retval = 0; dev_dbg (&intf->dev, "TEST 10: queue %d control calls, %d times\n", param->sglen, param->iterations); retval = silab_ctrl_queue (dev, param); break; /* simple non-queued unlinks (ring with one urb) */ case 11: if (dev->in_pipe == 0 || !param->length) break; retval = 0; dev_dbg (&intf->dev, "TEST 11: unlink %d reads of %d\n", param->iterations, param->length); for (i = param->iterations; retval == 0 && i--; /* NOP */) retval = unlink_silab (dev, dev->in_pipe, param->length); if (retval) dev_dbg (&intf->dev, "unlink reads failed %d, " "iterations left %d\n", retval, i); break; case 12: if (dev->out_pipe == 0 || !param->length) break; retval = 0; dev_dbg (&intf->dev, "TEST 12: unlink %d writes of %d\n", param->iterations, param->length); for (i = param->iterations; retval == 0 && i--; /* NOP */) retval = unlink_silab (dev, dev->out_pipe, param->length); if (retval) dev_dbg (&intf->dev, "unlink writes failed %d, " "iterations left %d\n", retval, i); break; /* ep halt tests */ case 13: if (dev->out_pipe == 0 && dev->in_pipe == 0) break; retval = 0; dev_dbg (&intf->dev, "TEST 13: set/clear %d halts\n", param->iterations); for (i = param->iterations; retval == 0 && i--; /* NOP */) retval = halt_silab (dev); if (retval) DBG (dev, "halts failed, iterations left %d\n", i); break; /* control write tests */ case 14: if (!dev->info->ctrl_out) break; dev_dbg (&intf->dev, "TEST 14: %d ep0out, %d..%d vary %d\n", param->iterations, realworld ? 1 : 0, param->length, param->vary); retval = ctrl_out (dev, param->iterations, param->length, param->vary); break; /* iso write tests */ case 15: if (dev->out_iso_pipe == 0 || param->sglen == 0) break; dev_dbg (&intf->dev, "TEST 15: write %d iso, %d entries of %d bytes\n", param->iterations, param->sglen, param->length); // FIRMWARE: iso sink retval = silab_iso_queue (dev, param, dev->out_iso_pipe, dev->iso_out); break; /* iso read tests */ case 16: if (dev->in_iso_pipe == 0 || param->sglen == 0) break; dev_dbg (&intf->dev, "TEST 16: read %d iso, %d entries of %d bytes\n", param->iterations, param->sglen, param->length); // FIRMWARE: iso source retval = silab_iso_queue (dev, param, dev->in_iso_pipe, dev->iso_in); break; // FIXME unlink from queue (ring with N urbs) // FIXME scatterlist cancel (needs helper thread) } do_gettimeofday (¶m->duration); param->duration.tv_sec -= start.tv_sec; param->duration.tv_usec -= start.tv_usec; if (param->duration.tv_usec < 0) { param->duration.tv_usec += 1000 * 1000; param->duration.tv_sec -= 1; } up (&dev->sem); return retval; } /*-------------------------------------------------------------------------*/ static unsigned force_interrupt = 0; module_param (force_interrupt, uint, 0); MODULE_PARM_DESC (force_interrupt, "0 = test default; else interrupt"); #ifdef GENERIC static unsigned short vendor; module_param(vendor, ushort, 0); MODULE_PARM_DESC (vendor, "vendor code (from usb-if)"); static unsigned short product; module_param(product, ushort, 0); MODULE_PARM_DESC (product, "product code (from vendor)"); #endif //----------------------------------------------------------------------- // begin from fops //----------------------------------------------------------------------- // extra declaration #define to_silab_dev(d) container_of(d, struct usbsilab_dev, kref) static ssize_t silab_read(struct file *file, char *buffer, size_t count, loff_t *ppos); static ssize_t silab_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos); static int silab_open(struct inode *inode, struct file *file); static int silab_release(struct inode *inode, struct file *file); static struct file_operations silab_fops = { .owner = THIS_MODULE, .read = silab_read, .write = silab_write, .ioctl = file_ioctl, .open = silab_open, .release = silab_release, }; /* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with the driver core */ static struct usb_class_driver silab_class = { .name = "silab%d", .fops = &silab_fops, .minor_base = USB_SILAB_MINOR_BASE, }; static void silab_delete(struct kref *kref) { struct usbsilab_dev *dev = to_silab_dev(kref); usb_put_dev(dev->udev); kfree (dev->bulk_in_buffer); kfree (dev); } //----------------------------------------------------------------------- // end from fops //----------------------------------------------------------------------- static int usbsilab_probe (struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev; struct usbsilab_dev *dev = NULL; struct usbsilab_info *info; char *rtest, *wtest; char *irtest, *iwtest; //------------ fsv silab --------- int retval = -ENOMEM; int i; size_t buffer_size; struct usb_endpoint_descriptor *endpoint; // -- fsv struct usb_host_interface *iface_desc; //------------ udev = interface_to_usbdev (intf); info("*************************************************************"); info("* Silicon Lab Bonn USB device driver for EZ-USB Controller *"); info("* Author: (C) 2007 Sergey Fourletov (fourl@mail.cern.ch) *"); info("*************************************************************"); info("* Version %s ",SILAB_VERSION); info("*************************************************************"); dbg ("* matched module: vend=0x%04x prod=0x%04x", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); //------------ iface_desc = intf->cur_altsetting; info("* the Number of endpoints = %d",iface_desc->desc.bNumEndpoints); if (iface_desc->desc.bNumEndpoints<2) { info("* ERROR !!! the Number of endpoints = %d, needs min 2 !!!!",iface_desc->desc.bNumEndpoints); return -ERANGE; } for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); info("* ep=%d, bEndpointAddress=0x%02x, size=%3ld",i,endpoint->bEndpointAddress,buffer_size); } //------------ end fsv silab --------- #ifdef GENERIC /* specify devices by module parameters? */ if (id->match_flags == 0) { /* vendor match required, product match optional */ if (!vendor || le16_to_cpu(udev->descriptor.idVendor) != (u16)vendor) return -ENODEV; if (product && le16_to_cpu(udev->descriptor.idProduct) != (u16)product) return -ENODEV; dbg ("GENERIC:: matched module params, vend=0x%04x prod=0x%04x", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); } #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) dev = kzalloc(sizeof(*dev), GFP_KERNEL); #else dev = kmalloc (sizeof *dev, GFP_KERNEL); #endif if (!dev) return -ENOMEM; memset (dev, 0, sizeof *dev); kref_init(&dev->kref); sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); info = (struct usbsilab_info *) id->driver_info; dev->info = info; init_MUTEX (&dev->sem); dev->intf = intf; dev->interface = intf; //-- skel fops -- dev->udev = usb_get_dev(interface_to_usbdev(intf)); //-- skel fops -- /* cacheline-aligned scratch for i/o */ if ((dev->buf = kmalloc (TBUF_SIZE, GFP_KERNEL)) == NULL) { kfree (dev); return -ENOMEM; } /* NOTE this doesn't yet test the handful of difference that are * visible with high speed interrupts: bigger maxpacket (1K) and * "high bandwidth" modes (up to 3 packets/uframe). */ rtest = wtest = ""; irtest = iwtest = ""; if (force_interrupt || udev->speed == USB_SPEED_LOW) { if (info->ep_in) { dev->in_pipe = usb_rcvintpipe (udev, info->ep_in); rtest = " intr-in"; } if (info->ep_out) { dev->out_pipe = usb_sndintpipe (udev, info->ep_out); wtest = " intr-out"; } } else { info("* autoconfig = %d in=%d out=%d",info->autoconf,info->ep_in,info->ep_out); if (info->autoconf) { int status; status = get_endpoints (dev, intf); if (status < 0) { dbg ("couldn't get endpoints, %d\n", status); return status; } /* may find bulk or ISO pipes */ } else { if (info->ep_in) dev->in_pipe = usb_rcvbulkpipe (udev,info->ep_in); if (info->ep_out) dev->out_pipe = usb_sndbulkpipe (udev,info->ep_out); } info("def: in_pipe=0x%x ep=%d usb_maxpacket=%d " ,dev->in_pipe, usb_pipeendpoint(dev->in_pipe) ,usb_maxpacket(dev->udev,dev->in_pipe,0)); info("def: out_pipe=0x%x ep=%d usb_maxpacket=%d " ,dev->out_pipe, usb_pipeendpoint(dev->out_pipe) ,usb_maxpacket(dev->udev,dev->out_pipe,1)); if (dev->in_pipe) rtest = " bulk-in"; if (dev->out_pipe) wtest = " bulk-out"; if (dev->in_iso_pipe) irtest = " iso-in"; if (dev->out_iso_pipe) iwtest = " iso-out"; } //------------------- .fvs. from skel ----------- //--- IN-endpoint endpoint = &iface_desc->endpoint[3].desc; //--3 FIXME !! buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); info("+in ep=3, bEndpointAddress=0x%02x size=%ld",endpoint->bEndpointAddress,buffer_size); dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!dev->bulk_in_buffer) { err("Could not allocate bulk_in_buffer"); goto error; } //--- OUT endpoint endpoint = &iface_desc->endpoint[2].desc; //--2 FIXME !! buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); info("+out ep=2, bEndpointAddress=0x%02x size=%ld",endpoint->bEndpointAddress,buffer_size); dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; //------------------------------------------------ /* save our data pointer in this interface device */ usb_set_intfdata (intf, dev); //------------------------------------------------ dev_info (&intf->dev, "%s\n", info->name); dev_info (&intf->dev, "%s speed {control%s%s%s%s%s} tests%s\n", ({ char *tmp; switch (udev->speed) { case USB_SPEED_LOW: tmp = "low"; break; case USB_SPEED_FULL: tmp = "full"; break; case USB_SPEED_HIGH: tmp = "high"; break; default: tmp = "unknown"; break; }; tmp; }), info->ctrl_out ? " in/out" : "", rtest, wtest, irtest, iwtest, info->alt >= 0 ? " (+alt)" : ""); dev_info (&intf->dev, "r/w MAX_TRANSFER=%ld\n",MAX_TRANSFER); dev_info (&intf->dev, "ioctl MAX_BULK_IOCTL=%d\n",MAX_BULK_IOCTL); //-------------- .fsv. /dev/silab0 --------------- /* we can register the device now, as it is ready */ retval = usb_register_dev(intf, &silab_class); if (retval) { /* something prevented us from registering this driver */ err("Not able to get a minor for this device."); usb_set_intfdata(intf, NULL); goto error; } /* let the user know what node this device is now attached to */ info("USB Silab device now attached to USBSilab-%d", intf->minor); return 0; error: if (dev) kref_put(&dev->kref, silab_delete); return retval; //---------------------------------------------------- // return 0; } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) // SL4 static int usbsilab_suspend (struct usb_interface *intf, pm_message_t message) { return 0; } static int usbsilab_resume (struct usb_interface *intf) { return 0; } #endif static void usbsilab_disconnect (struct usb_interface *intf) { struct usbsilab_dev *dev = usb_get_intfdata (intf); down (&dev->sem); usb_set_intfdata (intf, NULL); dev_dbg (&intf->dev, "disconnect\n"); /* give back our minor */ usb_deregister_dev(intf, &silab_class); kfree (dev); } /* Basic testing only needs a device that can source or sink bulk traffic. * Any device can test control transfers (default with GENERIC binding). * * Several entries work with the default EP0 implementation that's built * into EZ-USB chips. There's a default vendor ID which can be overridden * by (very) small config EEPROMS, but otherwise all these devices act * identically until firmware is loaded: only EP0 works. It turns out * to be easy to make other endpoints work, without modifying that EP0 * behavior. For now, we expect that kind of firmware. */ /* fx2 version of ez-usb */ static struct usbsilab_info ez2_info = { .name = "FX2 device", .ep_in = 6, .ep_out = 2, .alt = 1, .autoconf = 0, // use EP2 & EP6 }; /* SILAB fx2 version of ez-usb */ static struct usbsilab_info silab_info = { .name = "SILAB usb FX2 driver", .ep_in = 1, .ep_out = 1, .ep_in = 6, // 0x86 .ep_out = 2, // 0x2 .alt = 1, .autoconf = 0, // use EP2 & EP6 fsv !!! }; /* SILAB TEST fx2 version of ez-usb */ static struct usbsilab_info silab_info_tst = { .name = "SILAB usb test device", /* .ep_in = 6, .ep_out = 2, */ .ep_in = 8, // 0x88 .ep_out = 4, // 0x4 .alt = 1, .autoconf = 0, // use EP4 & EP8 fsv !!! }; static struct usb_device_id id_table [] = { /*-------------------------------------------------------------*/ /* EZ-USB devices which download firmware to replace (or in our * case augment) the default device implementation. */ /* generic EZ-USB FX2 controller (or development board) */ { USB_DEVICE (0x04b4, 0x8613), .driver_info = (unsigned long) &ez2_info, }, /* SILAB generic EZ-USB FX2 controller (or development board) */ { USB_DEVICE (USB_SILAB_VENDOR_ID, USB_SILAB_PRODUCT_ID), .driver_info = (unsigned long) &silab_info, }, /* SILAB generic EZ-USB FX2 controller */ { USB_DEVICE (USB_SILAB_VENDOR_ID, USB_SILAB_PRODUCT_ID2), .driver_info = (unsigned long) &silab_info, }, /* SILAB TEST EZ-USB FX2 */ { USB_DEVICE (USB_SILAB_VENDOR_ID, USB_SILAB_TST_PRODUCT_ID), // 0x0201 .driver_info = (unsigned long) &silab_info_tst, }, /*-------------------------------------------------------------*/ { } }; MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver usbsilab_driver = { .name = "silab", .id_table = id_table, .probe = usbsilab_probe, .ioctl = usbsilab_ioctl, .disconnect = usbsilab_disconnect, #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)) .suspend = usbsilab_suspend, .resume = usbsilab_resume, #endif }; //---------------------------------------------------------------------- // .fsv. begin fops //---------------------------------------------------------------------- /* our private defines. if this grows any larger, use your own .h file */ //#define MAX_TRANSFER ( PAGE_SIZE - 512 ) //#define WRITES_IN_FLIGHT 8 /* Structure to hold all of our device specific stuff */ //struct usb_silab { // struct usb_device * udev; /* the usb device for this device */ // struct usb_interface * interface; /* the interface for this device */ // struct semaphore limit_sem; /* limiting the number of writes in progress */ // unsigned char * bulk_in_buffer; /* the buffer to receive data */ // size_t bulk_in_size; /* the size of the receive buffer */ // __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ // __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ // struct kref kref; //}; //static struct usb_driver silab_driver; static int silab_open(struct inode *inode, struct file *file) { struct usbsilab_dev *dev; struct usb_interface *interface; int subminor; int retval = 0; subminor = iminor(inode); interface = usb_find_interface(&usbsilab_driver, subminor); if (!interface) { err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; goto exit; } dev = usb_get_intfdata(interface); if (!dev) { retval = -ENODEV; goto exit; } /* increment our usage count for the device */ kref_get(&dev->kref); /* save our object in the file's private structure */ file->private_data = dev; // info(" silab_OPEN IN=0x%x OUT=0x%x \n",dev->bulk_in_endpointAddr,dev->bulk_in_endpointAddr); #ifdef SF_DEBUG info(" silab_OPEN name=%s minor=%d",dev->info->name, subminor); #endif exit: return retval; } static int silab_release(struct inode *inode, struct file *file) { struct usbsilab_dev *dev; dev = (struct usbsilab_dev *)file->private_data; if (dev == NULL) return -ENODEV; /* decrement the count on our device */ kref_put(&dev->kref, silab_delete); return 0; } static ssize_t silab_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct usbsilab_dev *dev; int retval = 0; int bytes_read; dev = (struct usbsilab_dev *)file->private_data; // info(" silab_READ IN=0x%x size=%d",dev->bulk_in_endpointAddr,dev->bulk_in_size); //-- do a blocking bulk read to get data from the device retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), dev->bulk_in_buffer, min(dev->bulk_in_size, count), &bytes_read, 10000); //-- if the read was successful, copy the data to userspace if (!retval) { if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read)) retval = -EFAULT; else retval = bytes_read; } return retval; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)) static void silab_write_bulk_callback(struct urb *urb) #else static void silab_write_bulk_callback(struct urb *urb, struct pt_regs *regs) #endif { struct usbsilab_dev *dev; dev = (struct usbsilab_dev *)urb->context; /* sync/async unlink faults aren't errors */ if (urb->status && !(urb->status == -ENOENT || urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); } /* free up our allocated buffer */ usb_buffer_free(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); up(&dev->limit_sem); } static ssize_t silab_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos) { struct usbsilab_dev *dev; int retval = 0; struct urb *urb = NULL; char *buf = NULL; size_t writesize = min(count, (size_t)MAX_TRANSFER); dev = (struct usbsilab_dev *)file->private_data; // info(" silab_WRITE OUT=0x%x size=%d",dev->bulk_out_endpointAddr,writesize); /* verify that we actually have some data to write */ if (count == 0) goto exit; /* limit the number of URBs in flight to stop a user from using up all RAM */ if (down_interruptible(&dev->limit_sem)) { retval = -ERESTARTSYS; goto exit; } /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { retval = -ENOMEM; goto error; } buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; } if (copy_from_user(buf, user_buffer, writesize)) { retval = -EFAULT; goto error; } /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, writesize, silab_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); goto error; } /* release our reference to this urb, the USB core will eventually free it entirely */ usb_free_urb(urb); exit: return writesize; error: usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); usb_free_urb(urb); up(&dev->limit_sem); return retval; } /*---------------------------------------------------------------------- * Modifies the CPUCS register to stop or reset the CPU. * Returns false on error. ----------------------------------------------------------------------*/ static int ezusb_cpucs (struct usbsilab_dev *dev, int doRun) { int status; unsigned char data = doRun ? 0 : 1; //unsigned short addr = 0x600; //-- fx2 -- struct usb_interface *iface = dev->intf; struct usb_device *udev = interface_to_usbdev (iface); //int retval; info("%s", data ? "stop CPU" : "reset CPU"); status = usb_control_msg (udev, usb_sndctrlpipe (udev,0), 0xA0, 0x40, 0xE600, 0, &data, 1, 3000); if (status != 1) { char *mesg = "can't modify CPUCS"; if (status < 0) info ("ezusb_cpucs: ERR %s",mesg); else info ("ezusb_cpucs: OK %s", mesg); return 1; } else return 0; } //---------------------------------------------------------------------- // .fsv end //---------------------------------------------------------------------- /*-------------------------------------------------------------------------*/ static int __init usbsilab_init (void) { #ifdef GENERIC if (vendor) dbg ("params: vend=0x%04x prod=0x%04x", vendor, product); #endif return usb_register (&usbsilab_driver); } module_init (usbsilab_init); static void __exit usbsilab_exit (void) { usb_deregister (&usbsilab_driver); } module_exit (usbsilab_exit); MODULE_DESCRIPTION ("USB Core/HCD Testing Driver"); MODULE_LICENSE ("GPL");