diff options
Diffstat (limited to 'cfe/cfe/usb/usbd.c')
-rw-r--r-- | cfe/cfe/usb/usbd.c | 1215 |
1 files changed, 1215 insertions, 0 deletions
diff --git a/cfe/cfe/usb/usbd.c b/cfe/cfe/usb/usbd.c new file mode 100644 index 0000000..3e77049 --- /dev/null +++ b/cfe/cfe/usb/usbd.c @@ -0,0 +1,1215 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * USB device layer File: usbd.c + * + * This module deals with devices (things connected to USB buses) + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#ifndef _CFE_ +#include <stdio.h> +#include <time.h> +#include <memory.h> +#include <stdint.h> +#include "usbhack.h" +#else +#include "lib_types.h" +#include "lib_string.h" +#include "lib_printf.h" +#include "cfe_timer.h" +#endif + +#include "lib_malloc.h" +#include "lib_queue.h" +#include "usbchap9.h" +#include "usbd.h" + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +int usb_noisy = 0; + + +/* ********************************************************************* + * usb_create_pipe(dev,epaddr,mps,flags) + * + * Create a pipe, causing the corresponding endpoint to + * be created in the host controller driver. Pipes form the + * basic "handle" for unidirectional communications with a + * USB device. + * + * Input parameters: + * dev - device we're talking about + * epaddr - endpoint address open, usually from the endpoint + * descriptor + * mps - maximum packet size understood by the device + * flags - flags for this pipe (UP_xxx flags) + * + * Return value: + * <0 if error + * 0 if ok + ********************************************************************* */ + +int usb_create_pipe(usbdev_t *dev,int epaddr,int mps,int flags) +{ + usbpipe_t *pipe; + int pipeidx; + + pipeidx = USB_EPADDR_TO_IDX(epaddr); + + if (dev->ud_pipes[pipeidx] != NULL) { + printf("Trying to create a pipe that was already created!\n"); + return 0; + } + + pipe = KMALLOC(sizeof(usbpipe_t),0); + + if (!pipe) return -1; + + pipe->up_flags = flags; + pipe->up_num = pipeidx; + pipe->up_mps = mps; + pipe->up_dev = dev; + if (dev->ud_flags & UD_FLAG_LOWSPEED) flags |= UP_TYPE_LOWSPEED; + pipe->up_hwendpoint = UBEPTCREATE(dev->ud_bus, + dev->ud_address, + USB_ENDPOINT_ADDRESS(epaddr), + mps, + flags); + + dev->ud_pipes[pipeidx] = pipe; + + return 0; +} + +/* ********************************************************************* + * usb_open_pipe(dev,epdesc) + * + * Open a pipe given an endpoint descriptor - this is the + * normal way pipes get open, since you've just selected a + * configuration and have the descriptors handy with all + * the information you need. + * + * Input parameters: + * dev - device we're talking to + * epdesc - endpoint descriptor + * + * Return value: + * <0 if error + * else endpoint/pipe number (from descriptor) + ********************************************************************* */ + +int usb_open_pipe(usbdev_t *dev,usb_endpoint_descr_t *epdesc) +{ + int res; + int flags = 0; + + if (USB_ENDPOINT_DIR_IN(epdesc->bEndpointAddress)) flags |= UP_TYPE_IN; + else flags |= UP_TYPE_OUT; + + switch (epdesc->bmAttributes & USB_ENDPOINT_TYPE_MASK) { + case USB_ENDPOINT_TYPE_CONTROL: + flags |= UP_TYPE_CONTROL; + break; + case USB_ENDPOINT_TYPE_ISOCHRONOUS: + flags |= UP_TYPE_ISOC; + break; + case USB_ENDPOINT_TYPE_BULK: + flags |= UP_TYPE_BULK; + break; + case USB_ENDPOINT_TYPE_INTERRUPT: + flags |= UP_TYPE_INTR; + break; + } + + res = usb_create_pipe(dev, + epdesc->bEndpointAddress, + GETUSBFIELD(epdesc,wMaxPacketSize), + flags); + + if (res < 0) return res; + + return epdesc->bEndpointAddress; +} + + +/* ********************************************************************* + * usb_destroy_pipe(dev,epaddr) + * + * Close(destroy) an open pipe and remove endpoint descriptor + * + * Input parameters: + * dev - device we're talking to + * epaddr - pipe to close + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_destroy_pipe(usbdev_t *dev,int epaddr) +{ + usbpipe_t *pipe; + int pipeidx; + + pipeidx = USB_EPADDR_TO_IDX(epaddr); + + pipe = dev->ud_pipes[pipeidx]; + if (!pipe) return; + + if (dev->ud_pipes[pipeidx]) { + UBEPTDELETE(dev->ud_bus, + dev->ud_pipes[pipeidx]->up_hwendpoint); + } + + KFREE(dev->ud_pipes[pipeidx]); + dev->ud_pipes[pipeidx] = NULL; +} + +/* ********************************************************************* + * usb_destroy_device(dev) + * + * Delete an entire USB device, closing its pipes and freeing + * the device data structure + * + * Input parameters: + * dev - device to destroy + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_destroy_device(usbdev_t *dev) +{ + int idx; + + for (idx = 0; idx < UD_MAX_PIPES; idx++) { + if (dev->ud_pipes[idx]) { + UBEPTDELETE(dev->ud_bus, + dev->ud_pipes[idx]->up_hwendpoint); + KFREE(dev->ud_pipes[idx]); + } + } + + dev->ud_bus->ub_devices[dev->ud_address] = NULL; + + KFREE(dev); +} + + +/* ********************************************************************* + * usb_create_device(bus,lowspeed) + * + * Create a new USB device. This device will be set to + * communicate on address zero (default address) and will be + * ready for basic stuff so we can figure out what it is. + * The control pipe will be open, so you can start requesting + * descriptors right away. + * + * Input parameters: + * bus - bus to create device on + * lowspeed - true if it's a lowspeed device (the hubs tell + * us these things) + * + * Return value: + * usb device structure, or NULL + ********************************************************************* */ + +usbdev_t *usb_create_device(usbbus_t *bus,int lowspeed) +{ + usbdev_t *dev; + int pipeflags; + + /* + * Create the device structure. + */ + + dev = KMALLOC(sizeof(usbdev_t),0); + memset(dev,0,sizeof(usbdev_t)); + + dev->ud_bus = bus; + dev->ud_address = 0; /* default address */ + dev->ud_parent = NULL; + dev->ud_flags = 0; + + /* + * Adjust things based on the target device speed + */ + + pipeflags = UP_TYPE_CONTROL; + if (lowspeed) { + pipeflags |= UP_TYPE_LOWSPEED; + dev->ud_flags |= UD_FLAG_LOWSPEED; + } + + /* + * Create the control pipe. + */ + + usb_create_pipe(dev,0, + USB_CONTROL_ENDPOINT_MIN_SIZE, + pipeflags); + + return dev; +} + +/* ********************************************************************* + * usb_make_request(dev,epaddr,buf,len,flags) + * + * Create a template request structure with basic fields + * ready to go. A shorthand routine. + * + * Input parameters: + * dev- device we're talking to + * epaddr - endpoint address, from usb_open_pipe() + * buf,length - user buffer and buffer length + * flags - transfer direction, etc. (UR_xxx flags) + * + * Return value: + * usbreq_t pointer, or NULL + ********************************************************************* */ + +usbreq_t *usb_make_request(usbdev_t *dev,int epaddr,uint8_t *buf,int length,int flags) +{ + usbreq_t *ur; + usbpipe_t *pipe; + int pipeidx; + + pipeidx = USB_EPADDR_TO_IDX(epaddr); + + pipe = dev->ud_pipes[pipeidx]; + + if (pipe == NULL) return NULL; + + ur = KMALLOC(sizeof(usbreq_t),0); + memset(ur,0,sizeof(usbreq_t)); + + ur->ur_dev = dev; + ur->ur_pipe = pipe; + ur->ur_buffer = buf; + ur->ur_length = length; + ur->ur_flags = flags; + ur->ur_callback = NULL; + + return ur; + +} + +/* ********************************************************************* + * usb_poll(bus) + * + * Handle device-driver polling - simply vectors to host controller + * driver. + * + * Input parameters: + * bus - bus structure + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_poll(usbbus_t *bus) +{ + UBINTR(bus); +} + +/* ********************************************************************* + * usb_daemon(bus) + * + * Polls for topology changes and initiates a bus scan if + * necessary. + * + * Input parameters: + * bus - bus to watch + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_daemon(usbbus_t *bus) +{ + /* + * Just see if someone flagged a need for a scan here + * and start the bus scan if necessary. + * + * The actual scanning is a hub function, starting at the + * root hub, so the code for that is over there. + */ + + if (bus->ub_flags & UB_FLG_NEEDSCAN) { + bus->ub_flags &= ~UB_FLG_NEEDSCAN; + usb_scan(bus); + } +} + +/* ********************************************************************* + * usb_cancel_request(ur) + * + * Cancel a pending usb transfer request. + * + * Input parameters: + * ur - request to cancel + * + * Return value: + * 0 if ok + * else error (could not find request) + ********************************************************************* */ + +int usb_cancel_request(usbreq_t *ur) +{ + printf("usb_cancel_request is not implemented.\n"); + return 0; +} + +/* ********************************************************************* + * usb_free_request(ur) + * + * Return a transfer request to the free pool. + * + * Input parameters: + * ur - request to return + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_free_request(usbreq_t *ur) +{ + if (ur->ur_inprogress) { + printf("Yow! Tried to free a request that was in progress!\n"); + return; + } + KFREE(ur); +} + +/* ********************************************************************* + * usb_delay_ms(bus,ms) + * + * Wait a while, calling the polling routine as we go. + * + * Input parameters: + * bus - bus we're talking to + * ms - how long to wait + * + * Return value: + * nothing + ********************************************************************* */ + + +void usb_delay_ms(usbbus_t *bus,int ms) +{ +#ifdef _CFE_ + cfe_sleep(1+((ms*CFE_HZ)/1000)); +#else + mydelay(ms); +#endif +} + +/* ********************************************************************* + * usb_queue_request(ur) + * + * Call the transfer handler in the host controller driver to + * set up a transfer descriptor + * + * Input parameters: + * ur - request to queue + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +int usb_queue_request(usbreq_t *ur) +{ + int res; + + ur->ur_inprogress = 1; + ur->ur_xferred = 0; + res = UBXFER(ur->ur_dev->ud_bus, + ur->ur_pipe->up_hwendpoint, + ur); + return res; +} + +/* ********************************************************************* + * usb_wait_request(ur) + * + * Wait until a request completes, calling the polling routine + * as we wait. + * + * Input parameters: + * ur - request to wait for + * + * Return value: + * request status + ********************************************************************* */ + +int usb_wait_request(usbreq_t *ur) +{ + while ((volatile int) (ur->ur_inprogress)) { + usb_poll(ur->ur_dev->ud_bus); + } + + return ur->ur_status; +} + +/* ********************************************************************* + * usb_sync_request(ur) + * + * Synchronous request - call usb_queue and then usb_wait + * + * Input parameters: + * ur - request to submit + * + * Return value: + * status of request + ********************************************************************* */ + +int usb_sync_request(usbreq_t *ur) +{ + usb_queue_request(ur); + return usb_wait_request(ur); +} + +/* ********************************************************************* + * usb_simple_request(dev,reqtype,bRequest,wValue,wIndex) + * + * Handle a simple USB control pipe request. These are OUT + * requests with no data phase. + * + * Input parameters: + * dev - device we're talking to + * reqtype - request type (bmRequestType) for descriptor + * wValue - wValue for descriptor + * wIndex - wIndex for descriptor + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +int usb_simple_request(usbdev_t *dev,uint8_t reqtype,int bRequest,int wValue,int wIndex) +{ +#if 0 + uint8_t *requestbuf; + usb_device_request_t *req; + usbreq_t *ur; + int res; + + requestbuf = KMALLOC(32,0); + + req = (usb_device_request_t *) requestbuf; + + req->bmRequestType = reqtype; + req->bRequest = bRequest; + PUTUSBFIELD(req,wValue,wValue); + PUTUSBFIELD(req,wIndex,wIndex); + PUTUSBFIELD(req,wLength,0); + + if (usb_noisy > 1) printf("Request: "); + ur = usb_make_request(dev,0,requestbuf,sizeof(usb_device_request_t),UR_FLAG_SETUP); + res = usb_sync_request(ur); + usb_free_request(ur); + + if (res == 4) { /* STALL on control pipe */ + if (usb_noisy > 1) printf("STALL\n"); + usb_clear_stall(dev,dev->ud_pipes[0]->up_num); + return -1; + } + + res = ur->ur_xferred; + if (usb_noisy > 1) printf("Result %d\n",res); + + if (usb_noisy > 1) printf("Status: "); + ur = usb_make_request(dev,0,requestbuf,0,UR_FLAG_STATUS_IN); + res = usb_sync_request(ur); + usb_free_request(ur); + if (usb_noisy > 1) printf("Result %d\n",res); + + if (res == 4) { /* STALL */ + if (usb_noisy > 1) printf("STALL\n"); + usb_clear_stall(dev,dev->ud_pipes[0]->up_num); + return -1; + } + + KFREE(requestbuf); + + return 0; +#else + return usb_std_request(dev,reqtype,bRequest,wValue,wIndex,NULL,0); +#endif + +} + + +/* ********************************************************************* + * usb_set_configuration(dev,config) + * + * Set the current configuration for a USB device. + * + * Input parameters: + * dev - device we're talking to + * config - bConfigValue for the device + * + * Return value: + * request status + ********************************************************************* */ + +int usb_set_configuration(usbdev_t *dev,int config) +{ + int res; + + res = usb_simple_request(dev,0x00,USB_REQUEST_SET_CONFIGURATION,config,0); + + return res; +} + + +/* ********************************************************************* + * usb_new_address(bus) + * + * Return the next available address for the specified bus + * + * Input parameters: + * bus - bus to assign an address for + * + * Return value: + * new address, <0 if error + ********************************************************************* */ + +int usb_new_address(usbbus_t *bus) +{ + int idx; + + for (idx = 1; idx < USB_MAX_DEVICES; idx++) { + if (bus->ub_devices[idx] == NULL) return idx; + } + + return -1; +} + +/* ********************************************************************* + * usb_set_address(dev,address) + * + * Set the address of a device. This also puts the device + * in the master device table for the bus and reconfigures the + * address of the control pipe. + * + * Input parameters: + * dev - device we're talking to + * address - new address (1..127) + * + * Return value: + * request status + ********************************************************************* */ + +int usb_set_address(usbdev_t *dev,int address) +{ + int res; + int idx; + usbpipe_t *pipe; + + res = usb_simple_request(dev,0x00,USB_REQUEST_SET_ADDRESS,address,0); + + if (res == 0) { + dev->ud_bus->ub_devices[address] = dev; + dev->ud_address = address; + for (idx = 0; idx < UD_MAX_PIPES; idx++) { + pipe = dev->ud_pipes[idx]; + if (pipe && pipe->up_hwendpoint) { + UBEPTSETADDR(dev->ud_bus,pipe->up_hwendpoint,address); + } + } + } + + return res; +} + +/* ********************************************************************* + * usb_set_ep0mps(dev,mps) + * + * Set the maximum packet size of endpoint zero (mucks with the + * endpoint in the host controller) + * + * Input parameters: + * dev - device we're talking to + * mps - max packet size for endpoint zero + * + * Return value: + * request status + ********************************************************************* */ + +int usb_set_ep0mps(usbdev_t *dev,int mps) +{ + usbpipe_t *pipe; + + pipe = dev->ud_pipes[0]; + if (pipe && pipe->up_hwendpoint) { + UBEPTSETMPS(dev->ud_bus,pipe->up_hwendpoint,mps); + } + if (pipe) { + pipe->up_mps = mps; + } + + return 0; +} + +/* ********************************************************************* + * usb_clear_stall(dev,epaddr) + * + * Clear a stall condition on the specified pipe + * + * Input parameters: + * dev - device we're talking to + * epaddr - endpoint address + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ +int usb_clear_stall(usbdev_t *dev,int epaddr) +{ + uint8_t *requestbuf; + usb_device_request_t *req; + usbreq_t *ur; + int res; + int pipeidx; + + /* + * Clear the stall in the hardware. + */ + + pipeidx = USB_EPADDR_TO_IDX(epaddr); + + UBEPTCLEARTOGGLE(dev->ud_bus,dev->ud_pipes[pipeidx]->up_hwendpoint); + + /* + * Do the "clear stall" request. Note that we should do this + * without calling usb_simple_request, since usb_simple_request + * may itself stall. + */ + + requestbuf = KMALLOC(32,0); + + req = (usb_device_request_t *) requestbuf; + + req->bmRequestType = 0x02; + req->bRequest = USB_REQUEST_CLEAR_FEATURE; + PUTUSBFIELD(req,wValue,0); /* ENDPOINT_HALT */ + PUTUSBFIELD(req,wIndex,epaddr); + PUTUSBFIELD(req,wLength,0); + + ur = usb_make_request(dev,0,requestbuf, + sizeof(usb_device_request_t), + UR_FLAG_SETUP); + res = usb_sync_request(ur); + usb_free_request(ur); + ur = usb_make_request(dev,0,requestbuf,0,UR_FLAG_STATUS_IN); + res = usb_sync_request(ur); + usb_free_request(ur); + + KFREE(requestbuf); + + return 0; +} + + + +/* ********************************************************************* + * usb_std_request(dev,bmRequestType,bRequest,wValue, + * wIndex,buffer,length) + * + * Do a standard control request on the control pipe, + * with the appropriate setup, data, and status phases. + * + * Input parameters: + * dev - dev we're talking to + * bmRequestType,bRequest,wValue,wIndex - fields for the + * USB request structure + * buffer - user buffer + * length - length of user buffer + * + * Return value: + * number of bytes transferred + ********************************************************************* */ + +int usb_std_request(usbdev_t *dev,uint8_t bmRequestType, + uint8_t bRequest,uint16_t wValue, + uint16_t wIndex,uint8_t *buffer,int length) +{ + usbpipe_t *pipe = dev->ud_pipes[0]; + usbreq_t *ur; + int res; + usb_device_request_t *req; + uint8_t *databuf = NULL; + + req = KMALLOC(32,0); + + if ((buffer != NULL) && (length !=0)) { + databuf = KMALLOC(length,0); + if (!(bmRequestType & USBREQ_DIR_IN)) { + memcpy(databuf,buffer,length); + } + else { + memset(databuf,0,length); + } + } + + req->bmRequestType = bmRequestType; + req->bRequest = bRequest; + PUTUSBFIELD(req,wValue,wValue); + PUTUSBFIELD(req,wIndex,wIndex); + PUTUSBFIELD(req,wLength,length); + + ur = usb_make_request(dev,0,(uint8_t *)req,sizeof(usb_device_request_t),UR_FLAG_SETUP); + res = usb_sync_request(ur); + usb_free_request(ur); + + if (length != 0) { + if (bmRequestType & USBREQ_DIR_IN) { + ur = usb_make_request(dev,0,databuf,length,UR_FLAG_IN); + } + else { + ur = usb_make_request(dev,0,databuf,length,UR_FLAG_OUT); + } + + res = usb_sync_request(ur); + + if (res == 4) { /* STALL */ + usb_clear_stall(dev,pipe->up_num); + usb_free_request(ur); + if (databuf) KFREE(databuf); + KFREE(req); + return 0; + } + + length = ur->ur_xferred; + usb_free_request(ur); + } + + if ((length != 0) && (databuf != NULL) && (bmRequestType & USBREQ_DIR_IN)) { + memcpy(buffer,databuf,length); + } + + if (bmRequestType & USBREQ_DIR_IN) { + ur = usb_make_request(dev,0,(uint8_t *)req,0,UR_FLAG_STATUS_OUT); + } + else { + ur = usb_make_request(dev,0,(uint8_t *)req,0,UR_FLAG_STATUS_IN); + } + + res = usb_sync_request(ur); + usb_free_request(ur); + + if (res == 4) { /* STALL */ + usb_clear_stall(dev,pipe->up_num); + if (databuf) KFREE(databuf); + KFREE(req); + return 0; + } + + if (databuf) KFREE(databuf); + KFREE(req); + + return length; +} + + + + +/* ********************************************************************* + * usb_get_descriptor(dev,reqtype,dsctype,dscidx,respbuf,buflen) + * + * Request a descriptor from the device. + * + * Input parameters: + * dev - device we're talking to + * reqtype - bmRequestType field for descriptor we want + * dsctype - descriptor type we want + * dscidx - index of descriptor we want (often zero) + * respbuf - response buffer + * buflen - length of response buffer + * + * Return value: + * number of bytes transferred + ********************************************************************* */ + +int usb_get_descriptor(usbdev_t *dev,uint8_t reqtype,int dsctype,int dscidx, + uint8_t *respbuf,int buflen) +{ + return usb_std_request(dev, + reqtype,USB_REQUEST_GET_DESCRIPTOR, + USB_DESCRIPTOR_TYPEINDEX(dsctype,dscidx), + 0, + respbuf,buflen); +} + +/* ********************************************************************* + * usb_get_string(dev,id,buf,maxlen) + * + * Request a string from the device, converting it from + * unicode to ascii (brutally). + * + * Input parameters: + * dev - device we're talking to + * id - string ID + * buf - buffer to receive string (null terminated) + * maxlen - length of buffer + * + * Return value: + * number of characters in returned string + ********************************************************************* */ + +int usb_get_string(usbdev_t *dev,int id,char *buf,int maxlen) +{ + int amtcopy; + uint8_t *respbuf; + int idx; + usb_string_descr_t *sdscr; + + respbuf = KMALLOC(maxlen*2+2,0); + sdscr = (usb_string_descr_t *) respbuf; + + /* + * First time just get the header of the descriptor so we can + * get the string length + */ + + amtcopy = usb_get_descriptor(dev,USBREQ_DIR_IN,USB_STRING_DESCRIPTOR_TYPE,id, + respbuf,2); + + /* + * now do it again to get the whole string. + */ + + if (maxlen > sdscr->bLength) maxlen = sdscr->bLength; + + amtcopy = usb_get_descriptor(dev,USBREQ_DIR_IN,USB_STRING_DESCRIPTOR_TYPE,id, + respbuf,maxlen); + + *buf = '\0'; + amtcopy = sdscr->bLength - 2; + if (amtcopy <= 0) return amtcopy; + + for (idx = 0; idx < amtcopy; idx+=2) { + *buf++ = sdscr->bString[idx]; + } + + *buf = '\0'; + + KFREE(respbuf); + + return amtcopy; +} + + + +/* ********************************************************************* + * usb_get_device_descriptor(dev,dscr,smallflg) + * + * Request the device descriptor for the device. This is often + * the first descriptor requested, so it needs to be done in + * stages so we can find out how big the control pipe is. + * + * Input parameters: + * dev - device we're talking to + * dscr - pointer to buffer to receive descriptor + * smallflg - TRUE to request just 8 bytes. + * + * Return value: + * number of bytes copied + ********************************************************************* */ + +int usb_get_device_descriptor(usbdev_t *dev,usb_device_descr_t *dscr,int smallflg) +{ + int res; + uint8_t *respbuf; + int amtcopy; + + /* + * Smallflg truncates the request 8 bytes. We need to do this for + * the very first transaction to a USB device in order to determine + * the size of its control pipe. Bad things will happen if you + * try to retrieve more data than the control pipe will hold. + * + * So, be conservative at first and get the first 8 bytes of the + * descriptor. Byte 7 is bMaxPacketSize0, the size of the control + * pipe. Then you can go back and submit a bigger request for + * everything else. + */ + + amtcopy = smallflg ? USB_CONTROL_ENDPOINT_MIN_SIZE : sizeof(usb_device_descr_t); + + respbuf = KMALLOC(64,0); + res = usb_get_descriptor(dev,USBREQ_DIR_IN,USB_DEVICE_DESCRIPTOR_TYPE,0,respbuf,amtcopy); + memcpy(dscr,respbuf,amtcopy); + KFREE(respbuf); + return res; + +} + +/* ********************************************************************* + * usb_get_config_descriptor(dev,dscr,idx,maxlen) + * + * Request the configuration descriptor from the device. + * + * Input parameters: + * dev - device we're talking to + * dscr - descriptor buffer (receives data from device) + * idx - index of config we want (usually zero) + * maxlen - total size of buffer to receive descriptor + * + * Return value: + * number of bytes copied + ********************************************************************* */ + +int usb_get_config_descriptor(usbdev_t *dev,usb_config_descr_t *dscr,int idx,int maxlen) +{ + int res; + uint8_t *respbuf; + + respbuf = KMALLOC(maxlen,0); + res = usb_get_descriptor(dev,USBREQ_DIR_IN, + USB_CONFIGURATION_DESCRIPTOR_TYPE,idx, + respbuf,maxlen); + memcpy(dscr,respbuf,maxlen); + KFREE(respbuf); + return res; + +} + + + +/* ********************************************************************* + * usb_get_device_status(dev,status) + * + * Request status from the device (status descriptor) + * + * Input parameters: + * dev - device we're talking to + * status - receives device_status structure + * + * Return value: + * number of bytes returned + ********************************************************************* */ + +int usb_get_device_status(usbdev_t *dev,usb_device_status_t *status) +{ + return usb_std_request(dev, + USBREQ_DIR_IN, + 0, + 0, + 0, + (uint8_t *) status, + sizeof(usb_device_status_t)); +} + + +/* ********************************************************************* + * usb_complete_request(ur,status) + * + * Called when a usb request completes - pass status to + * caller and call the callback if there is one. + * + * Input parameters: + * ur - usbreq_t to complete + * status - completion status + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_complete_request(usbreq_t *ur,int status) +{ + ur->ur_status = status; + ur->ur_inprogress = 0; + if (ur->ur_callback) (*(ur->ur_callback))(ur); +} + + +/* ********************************************************************* + * usb_initroot(bus) + * + * Initialize the root hub for the bus - we need to do this + * each time a bus is configured. + * + * Input parameters: + * bus - bus to initialize + * + * Return value: + * nothing + ********************************************************************* */ + +void usb_initroot(usbbus_t *bus) +{ + usbdev_t *dev; + usb_driver_t *drv; + int addr; + int res; + uint8_t *buf; + int len; + usb_config_descr_t cfgdescr; + + /* + * Create a device for the root hub. + */ + + dev = usb_create_device(bus,0); + bus->ub_roothub = dev; + + /* + * Get the device descriptor. Make sure it's a hub. + */ + + res = usb_get_device_descriptor(dev,&(dev->ud_devdescr),TRUE); + + if (dev->ud_devdescr.bDeviceClass != USB_DEVICE_CLASS_HUB) { + printf("Error! Root device is not a hub!\n"); + return; + } + + /* + * Set up the max packet size for the control endpoint, + * then get the rest of the descriptor. + */ + + usb_set_ep0mps(dev,dev->ud_devdescr.bMaxPacketSize0); + res = usb_get_device_descriptor(dev,&(dev->ud_devdescr),FALSE); + + /* + * Obtain a new address and set the address of the + * root hub to this address. + */ + + addr = usb_new_address(dev->ud_bus); + res = usb_set_address(dev,addr); + + /* + * Get the configuration descriptor and all the + * associated interface and endpoint descriptors. + */ + + res = usb_get_config_descriptor(dev,&cfgdescr,0, + sizeof(usb_config_descr_t)); + if (res != sizeof(usb_config_descr_t)) { + printf("[a]usb_get_config_descriptor returns %d\n",res); + } + + len = GETUSBFIELD(&cfgdescr,wTotalLength); + buf = KMALLOC(len,0); + + res = usb_get_config_descriptor(dev,(usb_config_descr_t *)buf,0,len); + if (res != len) { + printf("[b]usb_get_config_descriptor returns %d\n",res); + } + + dev->ud_cfgdescr = (usb_config_descr_t *) buf; + + /* + * Select the configuration. Not really needed for our poor + * imitation root hub, but it's the right thing to do. + */ + + usb_set_configuration(dev,cfgdescr.bConfigurationValue); + + /* + * Find the driver for this. It had better be the hub + * driver. + */ + + drv = usb_find_driver(dev); + + /* + * Call the attach method. + */ + + (*(drv->udrv_attach))(dev,drv); + + /* + * Hub should now be operational. + */ + +} + + +/* ********************************************************************* + * usb_find_cfg_descr(dev,dtype,idx) + * + * Find a configuration descriptor - we retrieved all the config + * descriptors during discovery, this lets us dig out the one + * we want. + * + * Input parameters: + * dev - device we are talking to + * dtype - descriptor type to find + * idx - index of descriptor if there's more than one + * + * Return value: + * pointer to descriptor or NULL if not found + ********************************************************************* */ + +void *usb_find_cfg_descr(usbdev_t *dev,int dtype,int idx) +{ + uint8_t *endptr; + uint8_t *ptr; + usb_config_descr_t *cfgdscr; + + if (dev->ud_cfgdescr == NULL) return NULL; + + ptr = (uint8_t *) dev->ud_cfgdescr; + endptr = ptr + GETUSBFIELD((dev->ud_cfgdescr),wTotalLength); + + while (ptr < endptr) { + + cfgdscr = (usb_config_descr_t *) ptr; + + if (cfgdscr->bDescriptorType == dtype) { + if (idx == 0) return (void *) ptr; + else idx--; + } + + ptr += cfgdscr->bLength; + + } + + return NULL; +} |