| /* | |
| * (C) Copyright 2003 | |
| * Gerry Hamel, geh@ti.com, Texas Instruments | |
| * | |
| * Based on | |
| * linux/drivers/usbd/usbd.c.c - USB Device Core Layer | |
| * | |
| * Copyright (c) 2000, 2001, 2002 Lineo | |
| * Copyright (c) 2001 Hewlett Packard | |
| * | |
| * By: | |
| * Stuart Lynne <sl@lineo.com>, | |
| * Tom Rushworth <tbr@lineo.com>, | |
| * Bruce Balden <balden@lineo.com> | |
| * | |
| * 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. | |
| * | |
| */ | |
| #include <malloc.h> | |
| #include "usbdcore.h" | |
| #define MAX_INTERFACES 2 | |
| int maxstrings = 20; | |
| /* Global variables ************************************************************************** */ | |
| struct usb_string_descriptor **usb_strings; | |
| int usb_devices; | |
| extern struct usb_function_driver ep0_driver; | |
| int registered_functions; | |
| int registered_devices; | |
| 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", | |
| }; | |
| char *usbd_device_states[] = { | |
| "STATE_INIT", | |
| "STATE_CREATED", | |
| "STATE_ATTACHED", | |
| "STATE_POWERED", | |
| "STATE_DEFAULT", | |
| "STATE_ADDRESSED", | |
| "STATE_CONFIGURED", | |
| "STATE_UNKNOWN", | |
| }; | |
| 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 */ | |
| }; | |
| 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 */ | |
| }; | |
| char *usbd_device_status[] = { | |
| "USBD_OPENING", | |
| "USBD_OK", | |
| "USBD_SUSPENDED", | |
| "USBD_CLOSING", | |
| }; | |
| /* 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. | |
| */ | |
| struct usb_string_descriptor *usbd_get_string (__u8 index) | |
| { | |
| if (index >= maxstrings) { | |
| return NULL; | |
| } | |
| return usb_strings[index]; | |
| } | |
| /* Access to device descriptor functions ***************************************************** */ | |
| /* * | |
| * 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) | |
| { | |
| /* XXX */ | |
| configuration = configuration ? configuration - 1 : 0; | |
| if (configuration >= device->configurations) { | |
| return NULL; | |
| } | |
| return device->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. | |
| */ | |
| 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. | |
| */ | |
| 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. | |
| */ | |
| struct usb_device_descriptor *usbd_device_device_descriptor (struct usb_device_instance *device, int port) | |
| { | |
| return (device->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. | |
| */ | |
| 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. | |
| */ | |
| 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); | |
| } | |
| /** | |
| * 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. | |
| */ | |
| 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; | |
| */ | |
| 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. | |
| */ | |
| 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_endpoint_halted | |
| * @device: point to struct usb_device_instance | |
| * @endpoint: endpoint to check | |
| * | |
| * Return non-zero if endpoint is halted. | |
| */ | |
| int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint) | |
| { | |
| return (device->status == USB_STATUS_HALT); | |
| } | |
| /** | |
| * usbd_rcv_complete - complete a receive | |
| * @endpoint: | |
| * @len: | |
| * @urb_bad: | |
| * | |
| * Called from rcv interrupt to complete. | |
| */ | |
| void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad) | |
| { | |
| if (endpoint) { | |
| struct urb *rcv_urb; | |
| /*usbdbg("len: %d urb: %p\n", len, endpoint->rcv_urb); */ | |
| /* if we had an urb then update actual_length, dispatch if neccessary */ | |
| if ((rcv_urb = endpoint->rcv_urb)) { | |
| /*usbdbg("actual: %d buffer: %d\n", */ | |
| /*rcv_urb->actual_length, rcv_urb->buffer_length); */ | |
| /* check the urb is ok, are we adding data less than the packetsize */ | |
| if (!urb_bad && (len <= endpoint->rcv_packetSize)) { | |
| /*usbdbg("updating actual_length by %d\n",len); */ | |
| /* increment the received data size */ | |
| rcv_urb->actual_length += len; | |
| } else { | |
| usberr(" RECV_ERROR actual: %d buffer: %d urb_bad: %d\n", | |
| rcv_urb->actual_length, rcv_urb->buffer_length, urb_bad); | |
| rcv_urb->actual_length = 0; | |
| rcv_urb->status = RECV_ERROR; | |
| } | |
| } else { | |
| usberr("no rcv_urb!"); | |
| } | |
| } else { | |
| usberr("no endpoint!"); | |
| } | |
| } | |
| /** | |
| * usbd_tx_complete - complete a transmit | |
| * @endpoint: | |
| * @resetart: | |
| * | |
| * Called from tx interrupt to complete. | |
| */ | |
| void usbd_tx_complete (struct usb_endpoint_instance *endpoint) | |
| { | |
| if (endpoint) { | |
| struct urb *tx_urb; | |
| /* if we have a tx_urb advance or reset, finish if complete */ | |
| if ((tx_urb = endpoint->tx_urb)) { | |
| int sent = endpoint->last; | |
| endpoint->sent += sent; | |
| endpoint->last -= sent; | |
| if( (endpoint->tx_urb->actual_length - endpoint->sent) <= 0 ) { | |
| tx_urb->actual_length = 0; | |
| endpoint->sent = 0; | |
| endpoint->last = 0; | |
| /* Remove from active, save for re-use */ | |
| urb_detach(tx_urb); | |
| urb_append(&endpoint->done, tx_urb); | |
| /*usbdbg("done->next %p, tx_urb %p, done %p", */ | |
| /* endpoint->done.next, tx_urb, &endpoint->done); */ | |
| endpoint->tx_urb = first_urb_detached(&endpoint->tx); | |
| if( endpoint->tx_urb ) { | |
| endpoint->tx_queue--; | |
| usbdbg("got urb from tx list"); | |
| } | |
| if( !endpoint->tx_urb ) { | |
| /*usbdbg("taking urb from done list"); */ | |
| endpoint->tx_urb = first_urb_detached(&endpoint->done); | |
| } | |
| if( !endpoint->tx_urb ) { | |
| usbdbg("allocating new urb for tx_urb"); | |
| endpoint->tx_urb = usbd_alloc_urb(tx_urb->device, endpoint); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /* URB linked list functions ***************************************************** */ | |
| /* | |
| * Initialize an urb_link to be a single element list. | |
| * If the urb_link is being used as a distinguished list head | |
| * the list is empty when the head is the only link in the list. | |
| */ | |
| void urb_link_init (urb_link * ul) | |
| { | |
| if (ul) { | |
| ul->prev = ul->next = ul; | |
| } | |
| } | |
| /* | |
| * Detach an urb_link from a list, and set it | |
| * up as a single element list, so no dangling | |
| * pointers can be followed, and so it can be | |
| * joined to another list if so desired. | |
| */ | |
| void urb_detach (struct urb *urb) | |
| { | |
| if (urb) { | |
| urb_link *ul = &urb->link; | |
| ul->next->prev = ul->prev; | |
| ul->prev->next = ul->next; | |
| urb_link_init (ul); | |
| } | |
| } | |
| /* | |
| * Return the first urb_link in a list with a distinguished | |
| * head "hd", or NULL if the list is empty. This will also | |
| * work as a predicate, returning NULL if empty, and non-NULL | |
| * otherwise. | |
| */ | |
| urb_link *first_urb_link (urb_link * hd) | |
| { | |
| urb_link *nx; | |
| if (NULL != hd && NULL != (nx = hd->next) && nx != hd) { | |
| /* There is at least one element in the list */ | |
| /* (besides the distinguished head). */ | |
| return (nx); | |
| } | |
| /* The list is empty */ | |
| return (NULL); | |
| } | |
| /* | |
| * Return the first urb in a list with a distinguished | |
| * head "hd", or NULL if the list is empty. | |
| */ | |
| struct urb *first_urb (urb_link * hd) | |
| { | |
| urb_link *nx; | |
| if (NULL == (nx = first_urb_link (hd))) { | |
| /* The list is empty */ | |
| return (NULL); | |
| } | |
| return (p2surround (struct urb, link, nx)); | |
| } | |
| /* | |
| * Detach and return the first urb in a list with a distinguished | |
| * head "hd", or NULL if the list is empty. | |
| * | |
| */ | |
| struct urb *first_urb_detached (urb_link * hd) | |
| { | |
| struct urb *urb; | |
| if ((urb = first_urb (hd))) { | |
| urb_detach (urb); | |
| } | |
| return urb; | |
| } | |
| /* | |
| * Append an urb_link (or a whole list of | |
| * urb_links) to the tail of another list | |
| * of urb_links. | |
| */ | |
| void urb_append (urb_link * hd, struct urb *urb) | |
| { | |
| if (hd && urb) { | |
| urb_link *new = &urb->link; | |
| /* This allows the new urb to be a list of urbs, */ | |
| /* with new pointing at the first, but the link */ | |
| /* must be initialized. */ | |
| /* Order is important here... */ | |
| urb_link *pul = hd->prev; | |
| new->prev->next = hd; | |
| hd->prev = new->prev; | |
| new->prev = pul; | |
| pul->next = new; | |
| } | |
| } | |
| /* URB create/destroy functions ***************************************************** */ | |
| /** | |
| * usbd_alloc_urb - allocate an URB appropriate for specified endpoint | |
| * @device: device instance | |
| * @endpoint: endpoint | |
| * | |
| * 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. | |
| */ | |
| struct urb *usbd_alloc_urb (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint) | |
| { | |
| struct urb *urb; | |
| if( !(urb = (struct urb*)malloc(sizeof(struct urb))) ) { | |
| usberr(" F A T A L: malloc(%u) FAILED!!!!", sizeof(struct urb)); | |
| return NULL; | |
| } | |
| /* Fill in known fields */ | |
| memset(urb, 0, sizeof(struct urb)); | |
| urb->endpoint = endpoint; | |
| urb->device = device; | |
| urb->buffer = (u8*)urb->buffer_data; | |
| urb->buffer_length = sizeof(urb->buffer_data); | |
| urb_link_init (&urb->link); | |
| return urb; | |
| } | |
| /** | |
| * usbd_dealloc_urb - deallocate an URB and associated buffer | |
| * @urb: pointer to an urb structure | |
| * | |
| * Deallocate an urb structure and associated data. | |
| */ | |
| void usbd_dealloc_urb (struct urb *urb) | |
| { | |
| if (urb) { | |
| free (urb); | |
| } | |
| } | |
| /* Event signaling functions ***************************************************** */ | |
| /** | |
| * 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. | |
| */ | |
| void usbd_device_event_irq (struct usb_device_instance *device, usb_device_event_t event, int data) | |
| { | |
| usb_device_state_t state; | |
| if (!device || !device->bus) { | |
| usberr("(%p,%d) NULL device or device->bus", device, event); | |
| return; | |
| } | |
| state = device->device_state; | |
| usbinfo("%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; | |
| default: | |
| usbdbg("event %d - not handled",event); | |
| break; | |
| } | |
| /*usbdbg("%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->event ) { | |
| /* usbdbg("calling device->event"); */ | |
| device->event(device, event, data); | |
| } | |
| } |