www.pudn.com > Linux2410_device.rar > usbd.c
/* * linux/drivers/usbd/usbd.c.c - USB Device Core Layer * * Copyright (c) 2000, 2001, 2002 Lineo * Copyright (c) 2001 Hewlett Packard * * By: * Stuart Lynne, * Tom Rushworth , * Bruce Balden * * Changes copyright (c) 2003 MontaVista Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * Notes... * * 1. The order of loading must be: * * usb-device * usb-function(s) * usb-bus(s) * * 2. Loading usb-function modules allows them to register with the usb-device * layer. This makes their usb configuration descriptors available. Currently * function modules cannot be loaded after a bus module has been loaded. * * 3. Loading usb-bus modules causes them to register with the usb-device * layer and then enable their upstream port. This in turn will cause the * upstream host to enumerate this device. Note that bus modules can create * multiple devices, one per physical interface. * * 4. The usb-device layer provides a default configuration for endpoint 0 for * each device. * * 5. Additional configurations are added to support the configuration * function driver when entering the configured state. * * 6. The usb-device maintains a list of configurations from the loaded * function drivers. It will provide this to the USB HOST as part of the * enumeration process and will add someof them as the active configuration as * per the USB HOST instructions. * * 6. Two types of bus interface modules can be implemented: simple and compound. * * 7. Simple bus interface modules can only support a single function module. The * first function module is used. * * 8. Compound bus interface modules can support multiple functions. When implemented * these will use all available function modules. * */ #include #include #include "usbd-export.h" #include "usbd-build.h" #include "usbd-module.h" MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION ("USB Device Core Support"); USBD_MODULE_INFO ("usbdcore 0.1"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbd.h" #include "usbd-debug.h" #include "usbd-func.h" #include "usbd-bus.h" #include "usbd-inline.h" #include "hotplug.h" #define MAX_INTERFACES 2 /* Module Parameters ************************************************************************* */ MODULE_PARM (maxstrings, "i"); MODULE_PARM (dbg, "s"); MODULE_PARM_DESC (maxstrings, "Maximum number of strings to allow (0..255)"); MODULE_PARM_DESC (dbg, "debug parameters"); int maxstrings = 20; static char *dbg = NULL; /* Debug switches (module parameter "dbg=...") *********************************************** */ int dbgflg_usbdcore_init; int dbgflg_usbdcore_ep0; int dbgflg_usbdcore_rx; int dbgflg_usbdcore_tx; int dbgflg_usbdcore_usbe; int dbgflg_usbdcore_urbmem; static debug_option dbg_table[] = { {&dbgflg_usbdcore_init, NULL, "init", "initialization and termination"}, {&dbgflg_usbdcore_ep0, NULL, "ep0", "End Point 0 (setup) packet handling"}, {&dbgflg_usbdcore_rx, NULL, "rx", "USB RX (host->device) handling"}, {&dbgflg_usbdcore_tx, NULL, "tx", "USB TX (device->host) handling"}, {&dbgflg_usbdcore_usbe, NULL, "usbe", "USB events"}, {&dbgflg_usbdcore_urbmem, NULL, "mem", "URB memory allocation"}, #if 0 {NULL, NULL, "hot", "hotplug actions"}, {NULL, NULL, "mon", "USB conection monitor"}, #endif {NULL, NULL, NULL, NULL} }; #define dbg_init(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_init,lvl,fmt,##args) #define dbg_ep0(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_ep0,lvl,fmt,##args) #define dbg_rx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_rx,lvl,fmt,##args) #define dbg_tx(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_tx,lvl,fmt,##args) #define dbg_usbe(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_usbe,lvl,fmt,##args) #define dbg_urbmem(lvl,fmt,args...) dbgPRINT(dbgflg_usbdcore_urbmem,lvl,fmt,##args) /* Global variables ************************************************************************** */ struct usb_string_descriptor **usb_strings; int usb_devices; extern struct usb_function_driver ep0_driver; int registered_functions; int registered_devices; EXPORT_SYMBOL(usbd_device_events); char *usbd_device_events[] = { "DEVICE_UNKNOWN", "DEVICE_INIT", "DEVICE_CREATE", "DEVICE_HUB_CONFIGURED", "DEVICE_RESET", "DEVICE_ADDRESS_ASSIGNED", "DEVICE_CONFIGURED", "DEVICE_SET_INTERFACE", "DEVICE_SET_FEATURE", "DEVICE_CLEAR_FEATURE", "DEVICE_DE_CONFIGURED", "DEVICE_BUS_INACTIVE", "DEVICE_BUS_ACTIVITY", "DEVICE_POWER_INTERRUPTION", "DEVICE_HUB_RESET", "DEVICE_DESTROY", "DEVICE_FUNCTION_PRIVATE", }; EXPORT_SYMBOL(usbd_device_states); char *usbd_device_states[] = { "STATE_INIT", "STATE_CREATED", "STATE_ATTACHED", "STATE_POWERED", "STATE_DEFAULT", "STATE_ADDRESSED", "STATE_CONFIGURED", "STATE_UNKNOWN", }; EXPORT_SYMBOL(usbd_device_requests); char *usbd_device_requests[] = { "GET STATUS", // 0 "CLEAR FEATURE", // 1 "RESERVED", // 2 "SET FEATURE", // 3 "RESERVED", // 4 "SET ADDRESS", // 5 "GET DESCRIPTOR", // 6 "SET DESCRIPTOR", // 7 "GET CONFIGURATION", // 8 "SET CONFIGURATION", // 9 "GET INTERFACE", // 10 "SET INTERFACE", // 11 "SYNC FRAME", // 12 }; EXPORT_SYMBOL(usbd_device_descriptors); char *usbd_device_descriptors[] = { "UNKNOWN", // 0 "DEVICE", // 1 "CONFIG", // 2 "STRING", // 3 "INTERFACE", // 4 "ENDPOINT", // 5 "DEVICE QUALIFIER", // 6 "OTHER SPEED", // 7 "INTERFACE POWER", // 8 }; EXPORT_SYMBOL(usbd_device_status); char *usbd_device_status[] = { "USBD_OPENING", "USBD_OK", "USBD_SUSPENDED", "USBD_CLOSING", }; /* List support functions ******************************************************************** */ LIST_HEAD (function_drivers); // list of all registered function modules LIST_HEAD (devices); // list of all registered devices static inline struct usb_function_driver *list_entry_func (const struct list_head *le) { return list_entry (le, struct usb_function_driver, drivers); } static inline struct usb_device_instance *list_entry_device (const struct list_head *le) { return list_entry (le, struct usb_device_instance, devices); } /* Support Functions ************************************************************************* */ /** * usbd_hotplug - call a hotplug script * @action: hotplug action */ EXPORT_SYMBOL(usbd_hotplug); int usbd_hotplug (char *interface, char *action) { #ifdef CONFIG_HOTPLUG dbg_init (1, "agent: usbd interface: %s action: %s", interface, action); return hotplug ("usbd", interface, action); #else return (0); #endif } /* Descriptor support functions ************************************************************** */ /** * usbd_get_string - find and return a string descriptor * @index: string index to return * * Find an indexed string and return a pointer to a it. */ EXPORT_SYMBOL(usbd_get_string); struct usb_string_descriptor *usbd_get_string (__u8 index) { if (index >= maxstrings) { return NULL; } return usb_strings[index]; } /** * usbd_cancel_urb - cancel an urb being sent * @urb: pointer to an urb structure * * Used by a USB Function driver to cancel an urb that is being * sent. */ EXPORT_SYMBOL(usbd_cancel_urb); int usbd_cancel_urb (struct usb_device_instance *device, struct urb *urb) { dbg_tx (3, "%p", urb); if (!device->bus->driver->ops->cancel_urb) { // XXX should we do usbd_dealloc_urb(urb); return 0; } //urb->device->urbs_queued++; return device->bus->driver->ops->cancel_urb (urb); } /* Access to device descriptor functions ***************************************************** */ /* * * usbd_device_function_instance - find a function instance for this device * @device: * @configuration: index to configuration, 0 - N-1 * * Get specifed device configuration. Index should be bConfigurationValue-1. */ EXPORT_SYMBOL(usbd_device_function_instance); struct usb_function_instance *usbd_device_function_instance (struct usb_device_instance *device, unsigned int port) { //dbg_init(2,"device: %p port: %d functions: %d", device, port, device->functions); if (port >= device->functions) { dbg_init (0, "configuration out of range: %d %d", port, device->functions); return NULL; } return device->function_instance_array + port; } /* * * usbd_device_configuration_instance - find a configuration instance for this device * @device: * @configuration: index to configuration, 0 - N-1 * * Get specifed device configuration. Index should be bConfigurationValue-1. */ static struct usb_configuration_instance *usbd_device_configuration_instance (struct usb_device_instance *device, unsigned int port, unsigned int configuration) { struct usb_function_instance *function_instance; struct usb_function_driver *function_driver; // XXX configuration = configuration ? configuration - 1 : 0; if (!(function_instance = usbd_device_function_instance (device, port))) { dbg_init (0, "function_instance NULL"); return NULL; } if (!(function_driver = function_instance->function_driver)) { dbg_init (0, "function_driver NULL"); return NULL; } if (configuration >= function_driver->configurations) { dbg_init (0, "configuration out of range: %d %d %d", port, configuration, function_driver->configurations); return NULL; } return function_driver->configuration_instance_array + configuration; } /* * * usbd_device_interface_instance * @device: * @configuration: index to configuration, 0 - N-1 * @interface: index to interface * * Return the specified interface descriptor for the specified device. */ EXPORT_SYMBOL(usbd_device_interface_instance); struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *device, int port, int configuration, int interface) { struct usb_configuration_instance *configuration_instance; if ((configuration_instance = usbd_device_configuration_instance (device, port, configuration)) == NULL) { return NULL; } if (interface >= configuration_instance->interfaces) { return NULL; } return configuration_instance->interface_instance_array + interface; } /* * * usbd_device_alternate_descriptor_list * @device: * @configuration: index to configuration, 0 - N-1 * @interface: index to interface * @alternate: alternate setting * * Return the specified alternate descriptor for the specified device. */ EXPORT_SYMBOL(usbd_device_alternate_instance); struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *device, int port, int configuration, int interface, int alternate) { struct usb_interface_instance *interface_instance; if ((interface_instance = usbd_device_interface_instance (device, port, configuration, interface)) == NULL) { return NULL; } if (alternate >= interface_instance->alternates) { return NULL; } return interface_instance->alternates_instance_array + alternate; } /* * * usbd_device_device_descriptor * @device: which device * @configuration: index to configuration, 0 - N-1 * @port: which port * * Return the specified configuration descriptor for the specified device. */ EXPORT_SYMBOL(usbd_device_device_descriptor); struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) { struct usb_function_instance *function_instance; if (!(function_instance = usbd_device_function_instance (device, port))) { return NULL; } if (!function_instance->function_driver || !function_instance->function_driver->device_descriptor) { return NULL; } return (function_instance->function_driver->device_descriptor); } /** * usbd_device_configuration_descriptor * @device: which device * @port: which port * @configuration: index to configuration, 0 - N-1 * * Return the specified configuration descriptor for the specified device. */ EXPORT_SYMBOL(usbd_device_configuration_descriptor); struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct usb_device_instance *device, int port, int configuration) { struct usb_configuration_instance *configuration_instance; if (!(configuration_instance = usbd_device_configuration_instance (device, port, configuration))) { return NULL; } return (configuration_instance->configuration_descriptor); } /** * usbd_device_interface_descriptor * @device: which device * @port: which port * @configuration: index to configuration, 0 - N-1 * @interface: index to interface * @alternate: alternate setting * * Return the specified interface descriptor for the specified device. */ EXPORT_SYMBOL(usbd_device_interface_descriptor); struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate) { struct usb_interface_instance *interface_instance; if (!(interface_instance = usbd_device_interface_instance (device, port, configuration, interface))) { return NULL; } if ((alternate < 0) || (alternate >= interface_instance->alternates)) { return NULL; } return (interface_instance->alternates_instance_array[alternate].interface_descriptor); } EXPORT_SYMBOL(usbd_device_class_report_descriptor_index); struct usb_class_report_descriptor * usbd_device_class_report_descriptor_index( struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) { struct usb_alternate_instance *alternate_instance; if (!(alternate_instance = usbd_device_alternate_instance(device, port, configuration, interface, alternate))) { return NULL; } if (index >= alternate_instance->classes) { return NULL; } return *(alternate_instance->reports_descriptor_array+index); } /** * usbd_device_class_descriptor_index * @device: which device * @port: which port * @configuration: index to configuration, 0 - N-1 * @interface: index to interface * @index: which index * * Return the specified class descriptor for the specified device. */ EXPORT_SYMBOL(usbd_device_class_descriptor_index); struct usb_class_descriptor *usbd_device_class_descriptor_index (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) { struct usb_alternate_instance *alternate_instance; if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { return NULL; } if (index >= alternate_instance->classes) { return NULL; } return *(alternate_instance->classes_descriptor_array + index); } /** * usbd_device_endpoint_descriptor_index * @device: which device * @port: which port * @configuration: index to configuration, 0 - N-1 * @interface: index to interface * @alternate: index setting * @index: which index * * Return the specified endpoint descriptor for the specified device. */ EXPORT_SYMBOL(usbd_device_endpoint_descriptor_index); struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) { struct usb_alternate_instance *alternate_instance; if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { return NULL; } if (index >= alternate_instance->endpoints) { return NULL; } return *(alternate_instance->endpoints_descriptor_array + index); } /** * usbd_device_endpoint_transfersize * @device: which device * @port: which port * @configuration: index to configuration, 0 - N-1 * @interface: index to interface * @index: which index * * Return the specified endpoint transfer size; */ EXPORT_SYMBOL(usbd_device_endpoint_transfersize); int usbd_device_endpoint_transfersize (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int index) { struct usb_alternate_instance *alternate_instance; if (!(alternate_instance = usbd_device_alternate_instance (device, port, configuration, interface, alternate))) { return 0; } if (index >= alternate_instance->endpoints) { return 0; } return *(alternate_instance->endpoint_transfersize_array + index); } /** * usbd_device_endpoint_descriptor * @device: which device * @port: which port * @configuration: index to configuration, 0 - N-1 * @interface: index to interface * @alternate: alternate setting * @endpoint: which endpoint * * Return the specified endpoint descriptor for the specified device. */ EXPORT_SYMBOL(usbd_device_endpoint_descriptor); struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *device, int port, int configuration, int interface, int alternate, int endpoint) { struct usb_endpoint_descriptor *endpoint_descriptor; int i; for (i = 0; !(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, port, configuration, interface, alternate, i)); i++) { if (endpoint_descriptor->bEndpointAddress == endpoint) { return endpoint_descriptor; } } return NULL; } /** * usbd_alloc_urb_data - allocate urb data buffer * @urb: pointer to urb * @len: size of buffer to allocate * * Allocate a data buffer for the specified urb structure. */ EXPORT_SYMBOL(usbd_alloc_urb_data); int usbd_alloc_urb_data (struct urb *urb, int len) { len += 2; urb->buffer_length = len; urb->actual_length = 0; if (len == 0) { dbg_urbmem (0, "len == zero"); return 0; } if (urb->endpoint && urb->endpoint->endpoint_address && urb->function_instance && urb->function_instance->function_driver->ops->alloc_urb_data) { dbg_urbmem (0, "urb->function->ops used"); return urb->function_instance->function_driver->ops->alloc_urb_data (urb, len); } return !(urb->buffer = ckmalloc (len, GFP_ATOMIC)); } /** * usbd_alloc_urb - allocate an URB appropriate for specified endpoint * @device: device instance * @function_instance: device instance * @endpoint: endpoint * @length: size of transfer buffer to allocate * * Allocate an urb structure. The usb device urb structure is used to * contain all data associated with a transfer, including a setup packet for * control transfers. * * NOTE: endpoint_address MUST contain a direction flag. */ EXPORT_SYMBOL(usbd_alloc_urb); struct urb *usbd_alloc_urb (struct usb_device_instance *device, struct usb_function_instance *function_instance, __u8 endpoint_address, int length) { int i; for (i = 0; i < device->bus->driver->max_endpoints; i++) { struct usb_endpoint_instance *endpoint = device->bus->endpoint_array + i; dbg_urbmem (2, "i=%d epa=%d want %d", i, endpoint->endpoint_address, endpoint_address); if (endpoint->endpoint_address == endpoint_address) { struct urb *urb; dbg_urbmem (2, "[%d]: endpoint: %p %02x length: %d", i, endpoint, endpoint->endpoint_address, length); if (!(urb = ckmalloc (sizeof (struct urb), GFP_ATOMIC))) { dbg_urbmem (0, "ckmalloc(%u,GFP_ATOMIC) failed", sizeof (struct urb)); return NULL; } urb->endpoint = endpoint; urb->device = device; urb->function_instance = function_instance; urb_link_init (&urb->link); if (length) { if (usbd_alloc_urb_data (urb, length)) { dbg_urbmem (0, "usbd_alloc_urb_data(%u,GFP_ATOMIC) failed", length); kfree (urb); return NULL; } } else { urb->buffer_length = urb->actual_length = 0; } dbg_urbmem(1, "[%d] urb: %p len: %d", endpoint_address, urb, length); return urb; } } dbg_urbmem (0, "can't find endpoint #%02x!", endpoint_address); for (i = 0; i < device->bus->driver->max_endpoints; i++) { struct usb_endpoint_instance *endpoint = device->bus->endpoint_array + i; dbg_urbmem (0, "i=%d epa=%d want %d", i, endpoint->endpoint_address, endpoint_address); if (endpoint->endpoint_address == endpoint_address) { dbg_urbmem (0, "found it, but too late"); break; } } return NULL; } /** * usbd_dealloc_urb - deallocate an URB and associated buffer * @urb: pointer to an urb structure * * Deallocate an urb structure and associated data. */ EXPORT_SYMBOL(usbd_dealloc_urb); void usbd_dealloc_urb (struct urb *urb) { dbg_urbmem(1, "[%d] urb: %p len: %d", urb->endpoint->endpoint_address, urb, urb->buffer_length); if (urb) { if (urb->buffer) { if (urb->function_instance && urb->function_instance->function_driver->ops->dealloc_urb_data) { urb->function_instance->function_driver->ops->dealloc_urb_data (urb); } else { kfree (urb->buffer); } } kfree (urb); } } /** * usbd_device_event - called to respond to various usb events * @device: pointer to struct device * @event: event to respond to * * Used by a Bus driver to indicate an event. */ EXPORT_SYMBOL(usbd_device_event_irq); void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data) { struct urb *urb; usb_device_state_t state; if (!device || !device->bus) { dbg_usbe (1, "(%p,%d) NULL device or device->bus", device, event); return; } state = device->device_state; dbg_usbe (3, "-------------------------------------------------------------------------------------"); dbg_usbe (1,"%s", USBD_DEVICE_EVENTS(event)); switch (event) { case DEVICE_UNKNOWN: break; case DEVICE_INIT: device->device_state = STATE_INIT; break; case DEVICE_CREATE: device->device_state = STATE_ATTACHED; break; case DEVICE_HUB_CONFIGURED: device->device_state = STATE_POWERED; break; case DEVICE_RESET: device->device_state = STATE_DEFAULT; device->address = 0; break; case DEVICE_ADDRESS_ASSIGNED: device->device_state = STATE_ADDRESSED; break; case DEVICE_CONFIGURED: device->device_state = STATE_CONFIGURED; break; case DEVICE_DE_CONFIGURED: device->device_state = STATE_ADDRESSED; break; case DEVICE_BUS_INACTIVE: if (device->status != USBD_CLOSING) { device->status = USBD_SUSPENDED; } break; case DEVICE_BUS_ACTIVITY: if (device->status != USBD_CLOSING) { device->status = USBD_OK; } break; case DEVICE_SET_INTERFACE: break; case DEVICE_SET_FEATURE: break; case DEVICE_CLEAR_FEATURE: break; case DEVICE_POWER_INTERRUPTION: device->device_state = STATE_POWERED; break; case DEVICE_HUB_RESET: device->device_state = STATE_ATTACHED; break; case DEVICE_DESTROY: device->device_state = STATE_UNKNOWN; break; case DEVICE_FUNCTION_PRIVATE: break; } dbg_usbe (3, "%s event: %d oldstate: %d newstate: %d status: %d address: %d", device->name, event, state, device->device_state, device->status, device->address);; // tell the bus interface driver if (device->bus && device->bus->driver->ops->device_event) { dbg_usbe (3, "calling bus->event"); device->bus->driver->ops->device_event (device, event, data); } #if 0 if (device->function_instance_array && (device->function_instance_array + port)->function_driver->ops->event) { dbg_usbe (3, "calling function->event"); (device->function_instance_array + port)->function_driver->ops->event (device, event); } #endif if (device->ep0 && device->ep0->function_driver->ops->event) { dbg_usbe (3, "calling ep0->event"); device->ep0->function_driver->ops->event (device, event, data); } #if 0 // tell the bus interface driver if (device->bus && device->bus->driver->ops->device_event) { dbg_usbe (3, "calling bus->event"); device->bus->driver->ops->device_event (device, event); } #endif // dbg_usbe(0, "FINISHED - NO FUNCTION BH"); // return; // XXX // queue an urb to endpoint zero dbg_usbe(1,"queueing event urb"); if ((urb = usbd_alloc_urb (device, device->function_instance_array, 0, 0))) { urb->event = event; urb->data = data; urb_append_irq (&(urb->endpoint->events), urb); usbd_schedule_function_bh (device); } dbg_usbe (5, "FINISHED"); return; } EXPORT_SYMBOL(usbd_device_event); void usbd_device_event (struct usb_device_instance *device, usb_device_event_t event, int data) { unsigned long flags; local_irq_save (flags); usbd_device_event_irq (device, event, data); local_irq_restore (flags); } /** * usbd_endpoint_halted * @device: point to struct usb_device_instance * @endpoint: endpoint to check * * Return non-zero if endpoint is halted. */ EXPORT_SYMBOL(usbd_endpoint_halted); int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint) { if (!device->bus->driver->ops->endpoint_halted) { return -EINVAL; } return device->bus->driver->ops->endpoint_halted (device, endpoint); } /** * usbd_device_feature - set usb device feature * @device: point to struct usb_device_instance * @endpoint: which endpoint * @feature: feature value * * Return non-zero if endpoint is halted. */ EXPORT_SYMBOL(usbd_device_feature); int usbd_device_feature (struct usb_device_instance *device, int endpoint, int feature) { if (!device->bus->driver->ops->device_feature) { return -EINVAL; } return device->bus->driver->ops->device_feature (device, endpoint, feature); } #ifdef CONFIG_USBD_PROCFS /* Proc Filesystem *************************************************************************** */ /* * * dohex * */ static void dohexdigit (char *cp, unsigned char val) { if (val < 0xa) { *cp = val + '0'; } else if ((val >= 0x0a) && (val <= 0x0f)) { *cp = val - 0x0a + 'a'; } } /* * * dohex * */ static void dohexval (char *cp, unsigned char val) { dohexdigit (cp++, val >> 4); dohexdigit (cp++, val & 0xf); } /* * * dump_descriptor */ static int dump_descriptor (char *buf, char *sp, int num) { int len = 0; while (sp && num--) { dohexval (buf, *sp++); buf += 2; *buf++ = ' '; len += 3; } len++; *buf = '\n'; return len; } /* * * usbd_device_proc_read - implement proc file system read. * @file * @buf * @count * @pos * * Standard proc file system read function. * * We let upper layers iterate for us, *pos will indicate which device to return * statistics for. */ static ssize_t usbd_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos) { unsigned long page; int len = 0; int index; struct list_head *lhd; struct usb_function_driver *function_driver; struct usb_device_instance *device; // get a page, max 4095 bytes of data... if (!(page = get_free_page (GFP_KERNEL))) { return -ENOMEM; } len = 0; index = (*pos)++; if (index == 0) { len += sprintf ((char *) page + len, "usb-device list\n"); } //read_lock(&usb_device_rwlock); if (index == 1) { list_for_each (lhd, &function_drivers) { function_driver = list_entry_func (lhd); if (function_driver->configuration_instance_array) { #if 0 { int configuration; // max 4095 bytes of data... len += sprintf ((char *) page + len, "Function: %s", function_driver->name); len += sprintf ((char *) page + len, "\tDescriptors: %d", function_driver->configurations); len += sprintf ((char *) page + len, "\n"); for (configuration = 0; configuration < function_driver->configurations; configuration++) { int interface; struct usb_configuration_description *configuration = &function_driver->configuration_description[configuration]; len += sprintf ((char *) page + len, "\tConfig: %d %s\n", configuration, configuration->iConfiguration); for (interface = 0; interface < configuration->interfaces; interface++) { int alternate; struct usb_interface_instance *interface = &configuration->interface_instance_array[interface]; int endpoint; struct usb_interface_description *interface = &configuration->interface_list[interface]; len += sprintf ((char *) page + len, "\t\tInterface: %d\n", interface); for (endpoint = 0; endpoint < interface->endpoints; endpoint++) { // XXX struct usb_endpoint_description *endpoint = &interface->endpoint_list[endpoint]; len += sprintf ((char *) page + len, "\t\t\tEndpoint: %d %d\n", endpoint, endpoint->bEndpointAddress); } } } break; } #endif len += sprintf ((char *) page + len, "\nDevice descriptor "); len += dump_descriptor ((char *) page + len, (char *) function_driver->device_descriptor, sizeof (struct usb_device_descriptor)); { int configuration; struct usb_configuration_instance *configuration_instance_array = function_driver->configuration_instance_array; for (configuration = 0; configuration < function_driver->configurations; configuration++) { int interface; struct usb_configuration_descriptor *configuration_descriptor = configuration_instance_array[configuration].configuration_descriptor; len += sprintf ((char *) page + len, "\nConfiguration descriptor [%d ] ", configuration); len += dump_descriptor ((char *) page + len, (char *) configuration_descriptor, sizeof (struct usb_configuration_descriptor)); for (interface = 0; interface < configuration_instance_array[configuration].interfaces; interface++) { int alternate; //int class; struct usb_interface_instance *interface_instance = configuration_instance_array[configuration].interface_instance_array + interface; dbg_init (1, "interface: %d:%d alternates: %d", configuration, interface, interface_instance->alternates); for (alternate = 0; alternate < interface_instance->alternates; alternate++) { int endpoint; int class; struct usb_alternate_instance *alternate_instance = interface_instance->alternates_instance_array + alternate; struct usb_interface_descriptor *interface_descriptor = alternate_instance->interface_descriptor; dbg_init (1, "alternate: %d:%d:%d classes: %d", configuration, interface, alternate, alternate_instance->classes); len += sprintf ((char *) page + len, "\nInterface descriptor [%d:%d:%d ] ", configuration, interface + 1, alternate); len += dump_descriptor ((char *) page + len, (char *) interface_descriptor, sizeof (struct usb_interface_descriptor)); for (class = 0; class < alternate_instance->classes; class++) { struct usb_class_descriptor *class_descriptor = alternate_instance->classes_descriptor_array[class]; len += sprintf ((char *) page + len, "Class descriptor [%d:%d:%d ] ", configuration, interface + 1, class); len += dump_descriptor ((char *) page + len, (char *) class_descriptor, *(char *) class_descriptor); } dbg_init (1, "alternate: %d:%d:%d endpoints: %d", configuration, interface, alternate, alternate_instance->endpoints); for (endpoint = 0; endpoint < alternate_instance->endpoints; endpoint++) { struct usb_endpoint_descriptor *endpoint_descriptor = alternate_instance->endpoints_descriptor_array[endpoint]; dbg_init (1, "endpoint: %d:%d:%d:%d", configuration, interface, alternate, endpoint); len += sprintf ((char *) page + len, "Endpoint descriptor [%d:%d:%d:%d] ", configuration, interface + 1, alternate, endpoint); len += dump_descriptor ((char *) page + len, (char *) endpoint_descriptor, sizeof (struct usb_endpoint_descriptor)); } } } } break; } } } } else if (index == 2) { int i; int k; struct usb_string_descriptor *string_descriptor; len += sprintf ((char *) page + len, "\n\n"); if ((string_descriptor = usbd_get_string (0)) != NULL) { len += sprintf ((char *) page + len, "String [%2d] ", 0); for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) { len += sprintf ((char *) page + len, "%02x %02x ", string_descriptor->wData[k] >> 8, string_descriptor->wData[k] & 0xff); len++; } len += sprintf ((char *) page + len, "\n"); } for (i = 1; i < maxstrings; i++) { if ((string_descriptor = usbd_get_string (i)) != NULL) { len += sprintf((char *)page+len, "String [%2d:%2d] ", i, string_descriptor->bLength); // bLength = sizeof(struct usb_string_descriptor) + 2*strlen(str)-2; for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) { *(char *) (page + len) = (char) string_descriptor->wData[k]; len++; } len += sprintf ((char *) page + len, "\n"); } } } else if (index == 3) { list_for_each (lhd, &devices) { device = list_entry_device (lhd); len += sprintf ((char *) page + len, "\n\nBus: %s", device->bus->driver->name); if (device->ep0) { len += sprintf ((char *) page + len, " ep0: %s", device->ep0->function_driver->name); } if (device->function_instance_array) { len += sprintf ((char *) page + len, " func: %s", device->function_instance_array->function_driver->name); } len += sprintf ((char *) page + len, " state: %s", usbd_device_states[device->device_state]); len += sprintf ((char *) page + len, " addr: %d", device->address); len += sprintf ((char *) page + len, " conf: %d", device->configuration); len += sprintf ((char *) page + len, " int: %d", device->interface); len += sprintf ((char *) page + len, " alt: %d", device->alternate); len += sprintf ((char *) page + len, "\n"); break; } len += sprintf ((char *) page + len, "\n"); } //read_unlock(&usb_device_rwlock); if (len > count) { len = -EINVAL; } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { len = -EFAULT; } free_page (page); return len; } static struct file_operations usbd_device_proc_operations_functions = { read:usbd_device_proc_read_functions, }; /* * * usbd_device_proc_read - implement proc file system read. * @file: xx * @buf: xx * @count: xx * @pos: xx * * Standard proc file system read function. * * We let upper layers iterate for us, *pos will indicate which device to return * statistics for. */ static ssize_t usbd_device_proc_read_devices (struct file *file, char *buf, size_t count, loff_t * pos) { unsigned long page; int len = 0; int index; struct list_head *lhd; struct usb_device_instance *device; // get a page, max 4095 bytes of data... if (!(page = get_free_page (GFP_KERNEL))) { return -ENOMEM; } len = 0; index = (*pos)++; if (index == 0) { len += sprintf ((char *) page + len, "usb-device list\n"); } //read_lock(&usb_device_rwlock); if (index > 0) { list_for_each (lhd, &devices) { int configuration; device = list_entry_device (lhd); if (--index == 0) { // max 4095 bytes of data... len += sprintf ((char *) page + len, "Device: %s", device->bus->driver->name); len += sprintf ((char *) page + len, "\n"); len += sprintf ((char *) page + len, "%20s[ ] ", "Device:"); len += dump_descriptor ((char *) page + len, (char *) device->device_descriptor, sizeof (struct usb_device_descriptor)); break; } // iterate across configurations for this device for (configuration = 0; index > 0; configuration++) { int interface; struct usb_configuration_descriptor *configuration_descriptor; if (!(configuration_descriptor = usbd_device_configuration_descriptor (device, 0, configuration))) { break; } if (--index == 0) { // max 4095 bytes of data... len += sprintf ((char *) page + len, "%20s[%2d ] ", "Configuration:", configuration); len += dump_descriptor ((char *) page + len, (char *) configuration_descriptor, sizeof (struct usb_configuration_descriptor)); break; } // iterate across interfaces for this configurations for (interface = 0; index > 0; interface++) { int alternate; struct usb_interface_instance *interface_instance; if (!(interface_instance = usbd_device_interface_instance (device, 0, configuration, interface))) { break; } // itererate across alternates for this interafce for (alternate = 0; index > 0; alternate++) { int endpoint; struct usb_interface_descriptor *interface_descriptor; if (!(interface_descriptor = usbd_device_interface_descriptor (device, 0, configuration, interface, alternate))) { break; } if (--index == 0) { // max 4095 bytes of data... len += sprintf ((char *) page + len, "%20s[%2d:%2d ] ", "Interface", configuration, interface); len += dump_descriptor ((char *) page + len, (char *) interface_descriptor, sizeof (struct usb_interface_descriptor)); break; } // iterate across endpoints for this interface for (endpoint = 0; index > 0; endpoint++) { struct usb_endpoint_descriptor *endpoint_descriptor; if (!(endpoint_descriptor = usbd_device_endpoint_descriptor_index (device, 0, configuration, interface, alternate, endpoint))) { break; } if (--index == 0) { len += sprintf ((char *) page + len, "%20s[%2d:%2d:%2d] ", "Endpoint", configuration, interface, endpoint); len += dump_descriptor ((char *) page + len, (char *) endpoint_descriptor, sizeof (struct usb_endpoint_descriptor)); break; } } } } } } } if (index > 0) { int i; for (i = 1; i < maxstrings; i++) { struct usb_string_descriptor *string_descriptor; if ((string_descriptor = usbd_get_string (i)) != NULL) { if (--index == 0) { int k; len += sprintf ((char *) page + len, "%20s[%2d] ", "String", i); // bLength = sizeof(struct usb_string_descriptor) + 2*strlen(str)-2; for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) { *(char *) (page + len) = (char) string_descriptor->wData[k]; len++; } len += sprintf ((char *) page + len, "\n"); } } } } //read_unlock(&usb_device_rwlock); if (len > count) { len = -EINVAL; } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { len = -EFAULT; } free_page (page); return len; } static struct file_operations usbd_device_proc_operations_devices = { read:usbd_device_proc_read_devices, }; #endif /* Module init ******************************************************************************* */ unsigned intset (unsigned int a, unsigned b) { return (a ? a : b); } char *strset (char *s, char *d) { return ((s && strlen (s) ? s : d)); } static int __init usbd_device_init (void) { #if 0 debug_option *op; #endif printk (KERN_INFO "usbdcore: %s (dbg=\"%s\")\n", __usbd_module_info, dbg ? dbg : ""); #if 0 if (NULL != (op = find_debug_option (dbg_table, "hot"))) { op->sub_table = usbd_hotplug_get_dbg_table (); } if (NULL != (op = find_debug_option (dbg_table, "mon"))) { op->sub_table = usbd_monitor_get_dbg_table (); } #endif if (0 != scan_debug_options ("usb-device", dbg_table, dbg)) { return (-EINVAL); } #ifdef CONFIG_USBD_PROCFS { struct proc_dir_entry *p; // create proc filesystem entries if ((p = create_proc_entry ("usb-functions", 0, 0)) == NULL) return -ENOMEM; p->proc_fops = &usbd_device_proc_operations_functions; if ((p = create_proc_entry ("usb-devices", 0, 0)) == NULL) return -ENOMEM; p->proc_fops = &usbd_device_proc_operations_devices; } #endif return 0; } static void __exit usbd_device_exit (void) { printk (KERN_INFO "usbdcore: %s exiting\n", __usbd_module_info); #ifdef CONFIG_USBD_PROCFS // remove proc filesystem entry remove_proc_entry ("usb-functions", NULL); remove_proc_entry ("usb-devices", NULL); #endif } module_init (usbd_device_init); module_exit (usbd_device_exit);