diff --git a/i2cspi_BaS_gcc/sources/usb.c b/i2cspi_BaS_gcc/sources/usb.c new file mode 100644 index 0000000..cf84b4b --- /dev/null +++ b/i2cspi_BaS_gcc/sources/usb.c @@ -0,0 +1,1646 @@ +/* + * + * Most of this source has been derived from the Linux USB + * project: + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id) + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +/* + * How it works: + * + * Since this is a bootloader, the devices will not be automatic + * (re)configured on hotplug, but after a restart of the USB the + * device should work. + * + * For each transfer (except "Interrupt") we wait for completion. + */ + +#include "config.h" +#include "usb.h" + +#if defined(COLDFIRE) && defined(NETWORK) && defined(LWIP) +#include "../freertos/FreeRTOS.h" +#include "../freertos/task.h" +#include "../freertos/queue.h" +#include "../freertos/semphr.h" +#define USB_POLL_HUB +#ifdef CONFIG_USB_STORAGE +extern int usb_stor_curr_dev; +extern unsigned long usb_1st_disk_drive; +#endif +#endif + +#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined(CONFIG_USB_EHCI) + +#undef USB_DEBUG + +#ifdef USB_DEBUG +#define USB_PRINTF(fmt, args...) board_printf(fmt , ##args) +#else +#define USB_PRINTF(fmt, args...) +#endif + +#define USB_BUFSIZ 512 + +struct hci { + /* ------- common part -------- */ + long handle; /* PCI BIOS */ + const struct pci_device_id *ent; + int usbnum; + /* ---- end of common part ---- */ +}; + +extern void udelay(long usec); + +static struct usb_device *usb_dev; +static int bus_index; +static int dev_index[USB_MAX_BUS]; +static struct hci *controller_priv[USB_MAX_BUS]; +#ifdef USB_POLL_HUB +xQueueHandle queue_poll_hub; +#endif +static int asynch_allowed; +static struct devrequest *setup_packet; + +char usb_started; /* flag for the started/stopped USB status */ + +/********************************************************************** + * some forward declerations... + */ +void usb_scan_devices(void *priv); + +int usb_hub_probe(struct usb_device *dev, int ifnum); +void usb_hub_reset(int index_bus); +static int hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat); + +/*********************************************************************** + * wait_ms + */ +inline void wait_ms(unsigned long ms) +{ + while(ms-- > 0) + udelay(1000); +} + +/*************************************************************************** + * Init USB Device + */ +int usb_init(long handle, const struct pci_device_id *ent) +{ + void *priv; + int res = 0; + if(bus_index >= USB_MAX_BUS) + return(-1); + dev_index[bus_index] = 0; + asynch_allowed = 1; + if(handle && (ent != NULL)) + { + if(usb_mem_init()) + { + usb_started = 0; + return -1; /* out of memoy */ + } + if(usb_dev == NULL) + usb_dev = (struct usb_device *)usb_malloc(sizeof(struct usb_device) * USB_MAX_BUS * USB_MAX_DEVICE); + if(usb_dev == NULL) + { + usb_started = 0; + return -1; /* out of memoy */ + } + } + else /* restart */ + { + int i; + res = 0; + for(i = 0; i < USB_MAX_BUS; i++) + { + if(controller_priv[i] != NULL) + { + long handle = controller_priv[i]->handle; + if(handle) + res |= usb_init(handle, NULL); + } + } + return res; + } + usb_hub_reset(bus_index); + /* init low_level USB */ + Cconws("USB: "); + switch(ent->class) + { +#ifdef CONFIG_USB_UHCI + case PCI_CLASS_SERIAL_USB_UHCI: + res = uhci_usb_lowlevel_init(handle, ent, &priv); + break; +#endif +#ifdef CONFIG_USB_OHCI + case PCI_CLASS_SERIAL_USB_OHCI: + res = ohci_usb_lowlevel_init(handle, ent, &priv); + break; +#endif +#ifdef CONFIG_USB_EHCI + case PCI_CLASS_SERIAL_USB_EHCI: + res = ehci_usb_lowlevel_init(handle, ent, &priv); + break; +#endif + default: res = -1; break; + } + if(!res) + { + /* if lowlevel init is OK, scan the bus for devices + * i.e. search HUBs and configure them */ + if(setup_packet == NULL) + setup_packet = (void *)usb_malloc(sizeof(struct devrequest)); + if(setup_packet == NULL) + { + usb_started = 0; + return -1; /* out of memoy */ + } + Cconws("Scanning bus for devices... "); + controller_priv[bus_index] = (struct hci *)priv; + controller_priv[bus_index]->usbnum = bus_index; + usb_scan_devices(priv); + bus_index++; + usb_started = 1; + return 0; + } + else + { + Cconws("Error, couldn't init Lowlevel part\r\n"); + usb_started = 0; + return -1; + } +} + +/****************************************************************************** + * Stop USB this stops the LowLevel Part and deregisters USB devices. + */ +int usb_stop(void) +{ + int i, res = 0; + if(usb_started) + { + asynch_allowed = 1; + usb_started = 0; + usb_hub_reset(bus_index); + usb_free(setup_packet); + for(i = 0; i < USB_MAX_BUS; i++) + { + struct hci *priv = controller_priv[i]; + if(priv != NULL) + { + switch(priv->ent->class) + { +#ifdef CONFIG_USB_UHCI + case PCI_CLASS_SERIAL_USB_UHCI: + res |= uhci_usb_lowlevel_stop(priv); + break; +#endif +#ifdef CONFIG_USB_OHCI + case PCI_CLASS_SERIAL_USB_OHCI: + res |= ohci_usb_lowlevel_stop(priv); + break; +#endif +#ifdef CONFIG_USB_EHCI + case PCI_CLASS_SERIAL_USB_EHCI: + res |= ehci_usb_lowlevel_stop(priv); + break; +#endif + } + } + } + bus_index = 0; + usb_mem_stop(); + } + return res; +} + +#ifdef CONFIG_USB_INTERRUPT_POLLING + +void usb_event_poll(int interrupt) +{ +#ifdef CONFIG_USB_UHCI + uhci_usb_event_poll(interrupt); +#endif +#ifdef CONFIG_USB_OHCI + ohci_usb_event_poll(interrupt); +#endif +#ifdef CONFIG_USB_EHCI + ehci_usb_event_poll(interrupt); +#endif +} + +#else /* !CONFIG_USB_INTERRUPT_POLLING */ + +void usb_enable_interrupt(int enable) +{ +#ifdef CONFIG_USB_UHCI + uhci_usb_enable_interrupt(enable); +#endif +#ifdef CONFIG_USB_OHCI + ohci_usb_enable_interrupt(enable); +#endif +#ifdef CONFIG_USB_EHCI + ehci_usb_enable_interrupt(enable); +#endif +} + +#endif /* CONFIG_USB_INTERRUPT_POLLING */ + +/* + * disables the asynch behaviour of the control message. This is used for data + * transfers that uses the exclusiv access to the control and bulk messages. + */ +void usb_disable_asynch(int disable) +{ +#if 0 // #ifndef CONFIG_USB_INTERRUPT_POLLING + if(!asynch_allowed && !disable) + { + USB_PRINTF("Enable interrupts\r\n"); + usb_enable_interrupt(1); + } + else if(asynch_allowed && disable) + { + USB_PRINTF("Disable interrupts\r\n"); + usb_enable_interrupt(0); + } +#endif + asynch_allowed = !disable; +} + +/*------------------------------------------------------------------- + * Message wrappers. + * + */ + +/* + * submits an Interrupt Message + */ +int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int interval) +{ + struct hci *priv = (struct hci *)dev->priv_hcd; + switch(priv->ent->class) + { +#ifdef CONFIG_USB_UHCI + case PCI_CLASS_SERIAL_USB_UHCI: + return uhci_submit_int_msg(dev, pipe, buffer, transfer_len, interval); +#endif +#ifdef CONFIG_USB_OHCI + case PCI_CLASS_SERIAL_USB_OHCI: + return ohci_submit_int_msg(dev, pipe, buffer, transfer_len, interval); +#endif +#ifdef CONFIG_USB_EHCI + case PCI_CLASS_SERIAL_USB_EHCI: + return ehci_submit_int_msg(dev, pipe, buffer, transfer_len, interval); +#endif + default: + return -1; + } +} + +/* + * submits a control message and waits for comletion (at least timeout * 1ms) + * If timeout is 0, we don't wait for completion (used as example to set and + * clear keyboards LEDs). For data transfers, (storage transfers) we don't + * allow control messages with 0 timeout, by previousely resetting the flag + * asynch_allowed (usb_disable_asynch(1)). + * returns the transfered length if OK or -1 if error. The transfered length + * and the current status are stored in the dev->act_len and dev->status. + */ +int usb_control_msg(struct usb_device *dev, unsigned int pipe, + unsigned char request, unsigned char requesttype, + unsigned short value, unsigned short index, + void *data, unsigned short size, int timeout) +{ + struct hci *priv = (struct hci *)dev->priv_hcd; + if((timeout == 0) && (!asynch_allowed)) + { + /* request for a asynch control pipe is not allowed */ + return -1; + } + /* set setup command */ + setup_packet->requesttype = requesttype; + setup_packet->request = request; + setup_packet->value = cpu_to_le16(value); + setup_packet->index = cpu_to_le16(index); + setup_packet->length = cpu_to_le16(size); + USB_PRINTF("usb_control_msg: request: 0x%X, requesttype: 0x%X, value 0x%X index 0x%X length 0x%X\r\n", request, requesttype, value, index, size); + switch(priv->ent->class) + { +#ifdef CONFIG_USB_UHCI + case PCI_CLASS_SERIAL_USB_UHCI: + dev->status = USB_ST_NOT_PROC; /* not yet processed */ + uhci_submit_control_msg(dev, pipe, data, size, setup_packet); + break; +#endif +#ifdef CONFIG_USB_OHCI + case PCI_CLASS_SERIAL_USB_OHCI: + dev->status = USB_ST_NOT_PROC; /* not yet processed */ + ohci_submit_control_msg(dev, pipe, data, size, setup_packet); + break; +#endif +#ifdef CONFIG_USB_EHCI + case PCI_CLASS_SERIAL_USB_EHCI: + dev->status = USB_ST_NOT_PROC; /* not yet processed */ + ehci_submit_control_msg(dev, pipe, data, size, setup_packet); + break; +#endif + default: + return -1; + } + if(timeout == 0) + return (int)size; + if(dev->status != 0) + { + /* + * Let's wait a while for the timeout to elapse. + * It has no real use, but it keeps the interface happy. + */ + wait_ms(timeout); + return -1; + } + return dev->act_len; +} + +/*------------------------------------------------------------------- + * submits bulk message, and waits for completion. returns 0 if Ok or + * -1 if Error. + * synchronous behavior + */ +int usb_bulk_msg(struct usb_device *dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) +{ + struct hci *priv = (struct hci *)dev->priv_hcd; + if(len < 0) + return -1; + switch(priv->ent->class) + { +#ifdef CONFIG_USB_UHCI + case PCI_CLASS_SERIAL_USB_UHCI: + dev->status = USB_ST_NOT_PROC; /* not yet processed */ + uhci_submit_bulk_msg(dev, pipe, data, len); + break; +#endif +#ifdef CONFIG_USB_OHCI + case PCI_CLASS_SERIAL_USB_OHCI: + dev->status = USB_ST_NOT_PROC; /* not yet processed */ + ohci_submit_bulk_msg(dev, pipe, data, len); + break; +#endif +#ifdef CONFIG_USB_EHCI + case PCI_CLASS_SERIAL_USB_EHCI: + dev->status = USB_ST_NOT_PROC; /* not yet processed */ + ehci_submit_bulk_msg(dev, pipe, data, len); + break; +#endif + default: + return -1; + } + while(timeout--) + { + if(!((volatile unsigned long)dev->status & USB_ST_NOT_PROC)) + break; + wait_ms(1); + } + *actual_length = dev->act_len; + if(dev->status == 0) + return 0; + else + return -1; +} + + +/*------------------------------------------------------------------- + * Max Packet stuff + */ + +/* + * returns the max packet size, depending on the pipe direction and + * the configurations values + */ +int usb_maxpacket(struct usb_device *dev, unsigned long pipe) +{ + /* direction is out -> use emaxpacket out */ + if((pipe & USB_DIR_IN) == 0) + return dev->epmaxpacketout[((pipe>>15) & 0xf)]; + else + return dev->epmaxpacketin[((pipe>>15) & 0xf)]; +} + +/* The routine usb_set_maxpacket_ep() is extracted from the loop of routine + * usb_set_maxpacket(), because the optimizer of GCC 4.x chokes on this routine + * when it is inlined in 1 single routine. What happens is that the register r3 + * is used as loop-count 'i', but gets overwritten later on. + * This is clearly a compiler bug, but it is easier to workaround it here than + * to update the compiler (Occurs with at least several GCC 4.{1,2},x + * CodeSourcery compilers like e.g. 2007q3, 2008q1, 2008q3 lite editions on ARM) + */ +static void __attribute__((noinline))usb_set_maxpacket_ep(struct usb_device *dev, struct usb_endpoint_descriptor *ep) +{ + int b; + b = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + if((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL) + { + /* Control => bidirectional */ + dev->epmaxpacketout[b] = ep->wMaxPacketSize; + dev->epmaxpacketin[b] = ep->wMaxPacketSize; + USB_PRINTF("##Control EP epmaxpacketout/in[%d] = %d\r\n", b, dev->epmaxpacketin[b]); + } + else + { + if((ep->bEndpointAddress & 0x80) == 0) + { + /* OUT Endpoint */ + if(ep->wMaxPacketSize > dev->epmaxpacketout[b]) + { + dev->epmaxpacketout[b] = ep->wMaxPacketSize; + USB_PRINTF("##EP epmaxpacketout[%d] = %d\r\n", b, dev->epmaxpacketout[b]); + } + } + else + { + /* IN Endpoint */ + if(ep->wMaxPacketSize > dev->epmaxpacketin[b]) + { + dev->epmaxpacketin[b] = ep->wMaxPacketSize; + USB_PRINTF("##EP epmaxpacketin[%d] = %d\r\n", b, dev->epmaxpacketin[b]); + } + } /* if out */ + } /* if control */ +} + +/* + * set the max packed value of all endpoints in the given configuration + */ +int usb_set_maxpacket(struct usb_device *dev) +{ + int i, ii; + for(i = 0; i < dev->config.bNumInterfaces; i++) + for(ii = 0; ii < dev->config.if_desc[i].bNumEndpoints; ii++) + usb_set_maxpacket_ep(dev,&dev->config.if_desc[i].ep_desc[ii]); + return 0; +} + +/******************************************************************************* + * Parse the config, located in buffer, and fills the dev->config structure. + * Note that all little/big endian swapping are done automatically. + */ +int usb_parse_config(struct usb_device *dev, unsigned char *buffer, int cfgno) +{ + struct usb_descriptor_header *head; + int index, ifno, epno, curr_if_num; + int i; + unsigned char *ch; + ifno = -1; + epno = -1; + curr_if_num = -1; + dev->configno = cfgno; + head = (struct usb_descriptor_header *)&buffer[0]; + if(head->bDescriptorType != USB_DT_CONFIG) + { + board_printf(" ERROR: NOT USB_CONFIG_DESC %x\r\n", head->bDescriptorType); + return -1; + } + memcpy(&dev->config, buffer, buffer[0]); + le16_to_cpus(&(dev->config.wTotalLength)); + dev->config.no_of_if = 0; + index = dev->config.bLength; + /* Ok the first entry must be a configuration entry, + * now process the others */ + head = (struct usb_descriptor_header *) &buffer[index]; + while(index + 1 < dev->config.wTotalLength) + { + switch (head->bDescriptorType) + { + case USB_DT_INTERFACE: + if(((struct usb_interface_descriptor *)&buffer[index])->bInterfaceNumber != curr_if_num) + { + /* this is a new interface, copy new desc */ + ifno = dev->config.no_of_if; + dev->config.no_of_if++; + memcpy(&dev->config.if_desc[ifno], &buffer[index], buffer[index]); + dev->config.if_desc[ifno].no_of_ep = 0; + dev->config.if_desc[ifno].num_altsetting = 1; + curr_if_num = dev->config.if_desc[ifno].bInterfaceNumber; + } + else + { + /* found alternate setting for the interface */ + dev->config.if_desc[ifno].num_altsetting++; + } + break; + case USB_DT_ENDPOINT: + epno = dev->config.if_desc[ifno].no_of_ep; + /* found an endpoint */ + dev->config.if_desc[ifno].no_of_ep++; + memcpy(&dev->config.if_desc[ifno].ep_desc[epno], &buffer[index], buffer[index]); + le16_to_cpus(&(dev->config.if_desc[ifno].ep_desc[epno].wMaxPacketSize)); + USB_PRINTF("if %d, ep %d\r\n", ifno, epno); + break; + default: + if(head->bLength == 0) + return 1; + USB_PRINTF("unknown Description Type : %x\r\n", head->bDescriptorType); + { + ch = (unsigned char *)head; + for (i = 0; i < head->bLength; i++) + USB_PRINTF(" %02X", *ch++); + USB_PRINTF("\r\n"); + } + break; + } + index += head->bLength; + head = (struct usb_descriptor_header *)&buffer[index]; + } + return 1; +} + +/*********************************************************************** + * Clears an endpoint + * endp: endpoint number in bits 0-3; + * direction flag in bit 7 (1 = IN, 0 = OUT) + */ +int usb_clear_halt(struct usb_device *dev, int pipe) +{ + int result; + int endp = usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7); + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, USB_CNTL_TIMEOUT * 3); + /* don't clear if failed */ + if(result < 0) + return result; + /* + * NOTE: we do not get status and verify reset was successful + * as some devices are reported to lock up upon this check.. + */ + usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); + /* toggle is reset on clear */ + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0); + return 0; +} + +/********************************************************************** + * get_descriptor type + */ +int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) +{ + int res; + res = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, (type << 8) + index, 0, buf, size, USB_CNTL_TIMEOUT); + return res; +} + +/********************************************************************** + * gets configuration cfgno and store it in the buffer + */ +int usb_get_configuration_no(struct usb_device *dev, unsigned char *buffer, int cfgno) +{ + int result; + unsigned int tmp; + struct usb_config_descriptor *config; + config = (struct usb_config_descriptor *)&buffer[0]; + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 9); + if(result < 9) + { + if(result < 0) + board_printf("unable to get descriptor, error %lX\r\n", dev->status); + else + board_printf("config descriptor too short (expected %i, got %i)\n", 9, result); + return -1; + } + tmp = le16_to_cpu(config->wTotalLength); + if(tmp > USB_BUFSIZ) + { + USB_PRINTF("usb_get_configuration_no: failed to get descriptor - too long: %d\r\n", tmp); + return -1; + } + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, tmp); + USB_PRINTF("get_conf_no %d Result %d, wLength %d\r\n", cfgno, result, tmp); + return result; +} + +/******************************************************************** + * set address of a device to the value in dev->devnum. + * This can only be done by addressing the device via the default address (0) + */ +int usb_set_address(struct usb_device *dev) +{ + int res; + USB_PRINTF("set address %d\r\n", dev->devnum); + res = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS, 0, (dev->devnum), 0, NULL, 0, USB_CNTL_TIMEOUT); + return res; +} + +/******************************************************************** + * set interface number to interface + */ +int usb_set_interface(struct usb_device *dev, int interface, int alternate) +{ + struct usb_interface_descriptor *if_face = NULL; + int ret, i; + for(i = 0; i < dev->config.bNumInterfaces; i++) + { + if(dev->config.if_desc[i].bInterfaceNumber == interface) + { + if_face = &dev->config.if_desc[i]; + break; + } + } + if(!if_face) + { + board_printf("selecting invalid interface %d", interface); + return -1; + } + /* + * We should return now for devices with only one alternate setting. + * According to 9.4.10 of the Universal Serial Bus Specification + * Revision 2.0 such devices can return with a STALL. This results in + * some USB sticks timeouting during initialization and then being + * unusable in U-Boot. + */ + if(if_face->num_altsetting == 1) + return 0; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate, interface, NULL, 0, USB_CNTL_TIMEOUT * 5); + if(ret < 0) + return ret; + return 0; +} + +/******************************************************************** + * set configuration number to configuration + */ +int usb_set_configuration(struct usb_device *dev, int configuration) +{ + int res; + USB_PRINTF("set configuration %d\r\n", configuration); + /* set setup command */ + res = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CNTL_TIMEOUT); + if(res == 0) + { + dev->toggle[0] = 0; + dev->toggle[1] = 0; + return 0; + } + else + return -1; +} + +/******************************************************************** + * set protocol to protocol + */ +int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + protocol, ifnum, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * set idle + */ +int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (duration << 8) | report_id, ifnum, NULL, 0, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get report + */ +int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, + unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get class descriptor + */ +int usb_get_class_descriptor(struct usb_device *dev, int ifnum, + unsigned char type, unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + (type << 8) + id, ifnum, buf, size, USB_CNTL_TIMEOUT); +} + +/******************************************************************** + * get string index in buffer + */ +int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size) +{ + int i; + int result; + for(i = 0; i < 3; ++i) + { + /* some devices are flaky */ + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + index, langid, buf, size, USB_CNTL_TIMEOUT); + if(result > 0) + break; + } + return result; +} + +static void usb_try_string_workarounds(unsigned char *buf, int *length) +{ + int newlength, oldlength = *length; + for(newlength = 2; newlength + 1 < oldlength; newlength += 2) + { + char c = buf[newlength]; + if((c < ' ') || (c >= 127) || buf[newlength + 1]) + break; + } + if(newlength > 2) + { + buf[0] = newlength; + *length = newlength; + } +} + +static int usb_string_sub(struct usb_device *dev, unsigned int langid, unsigned int index, unsigned char *buf) +{ + int rc; + /* Try to read the string descriptor by asking for the maximum + * possible number of bytes */ + rc = usb_get_string(dev, langid, index, buf, 255); + /* If that failed try to read the descriptor length, then + * ask for just that many bytes */ + if(rc < 2) + { + rc = usb_get_string(dev, langid, index, buf, 2); + if(rc == 2) + rc = usb_get_string(dev, langid, index, buf, buf[0]); + } + if(rc >= 2) + { + if (!buf[0] && !buf[1]) + usb_try_string_workarounds(buf, &rc); + /* There might be extra junk at the end of the descriptor */ + if (buf[0] < rc) + rc = buf[0]; + rc = rc - (rc & 1); /* force a multiple of two */ + } + if(rc < 2) + rc = -1; + return rc; +} + +/******************************************************************** + * usb_string: + * Get string index and translate it to ascii. + * returns string length (> 0) or error (< 0) + */ +int usb_string(struct usb_device *dev, int index, char *buf, size_t size) +{ + unsigned char *tbuf; + int err; + unsigned int u, idx; + if(size <= 0 || !buf || !index) + return -1; + buf[0] = 0; + tbuf = (unsigned char *)usb_malloc(USB_BUFSIZ); + if(tbuf == NULL) + { + USB_PRINTF("usb_string: malloc failure\r\n"); + return -1; + } + /* get langid for strings if it's not yet known */ + if(!dev->have_langid) + { + err = usb_string_sub(dev, 0, 0, tbuf); + if(err < 0) + { + USB_PRINTF("error getting string descriptor 0 (error=%lx)\r\n", dev->status); + usb_free(tbuf); + return -1; + } + else if(tbuf[0] < 4) + { + USB_PRINTF("string descriptor 0 too short\r\n"); + usb_free(tbuf); + return -1; + } + else + { + dev->have_langid = -1; + dev->string_langid = tbuf[2] | (tbuf[3] << 8); + /* always use the first langid listed */ + USB_PRINTF("USB device number %d default language ID 0x%x\r\n", dev->devnum, dev->string_langid); + } + } + err = usb_string_sub(dev, dev->string_langid, index, tbuf); + if(err < 0) + { + usb_free(tbuf); + return err; + } + size--; /* leave room for trailing NULL char in output buffer */ + for(idx = 0, u = 2; u < err; u += 2) + { + if(idx >= size) + break; + if(tbuf[u+1]) /* high byte */ + buf[idx++] = '?'; /* non-ASCII character */ + else + buf[idx++] = tbuf[u]; + } + buf[idx] = 0; + err = idx; + usb_free(tbuf); + return err; +} + +/******************************************************************** + * USB device handling: + * the USB device are static allocated [USB_MAX_DEVICE]. + */ + +/* + * Something got disconnected. Get rid of it, and all of its children. + */ +void usb_disconnect(struct usb_device **pdev) +{ + struct usb_device *dev = *pdev; + if(dev != NULL) + { + int i; + USB_PRINTF("USB %d disconnect on device %d\r\n", dev->parent->usbnum, dev->parent->devnum); + USB_PRINTF("USB %d disconnected, device number %d\r\n", dev->usbnum, dev->devnum); + if(dev->deregister != NULL) + dev->deregister(dev); + /* Free up all the children.. */ + for(i = 0; i < USB_MAXCHILDREN; i++) + { + if(dev->children[i] != NULL) + { + USB_PRINTF("USB %d, disconnect children %d\r\n", dev->usbnum, dev->children[i]->devnum); + usb_disconnect(&dev->children[i]); + dev->children[i] = NULL; + } + } + /* Free up the device itself, including its device number */ + if(dev->devnum > 0) + { + dev_index[dev->usbnum]--; + memset(dev, 0, sizeof(struct usb_device)); + dev->devnum = -1; + } + *pdev = NULL; + } +} + +/* returns a pointer to the device with the index [index]. + * if the device is not assigned (dev->devnum==-1) returns NULL + */ +struct usb_device *usb_get_dev_index(int index, int index_bus) +{ + struct usb_device *dev; + if((index_bus >= USB_MAX_BUS) || (index_bus < 0) + || (index >= USB_MAX_DEVICE) || (index < 0)) + return NULL; + dev = &usb_dev[(index_bus * USB_MAX_DEVICE) + index]; + if((controller_priv[index_bus] == NULL) || (dev->devnum == -1)) + return NULL; + return dev; +} + +/* returns a pointer of a new device structure or NULL, if + * no device struct is available + */ +struct usb_device *usb_alloc_new_device(int bus_index, void *priv) +{ + int i, index = dev_index[bus_index]; + struct usb_device *dev; + USB_PRINTF("USB %d new device %d\r\n", bus_index, index); + if(index >= USB_MAX_DEVICE) + { + board_printf("ERROR, too many USB Devices, max=%d\r\n", USB_MAX_DEVICE); + return NULL; + } + /* default Address is 0, real addresses start with 1 */ + dev = &usb_dev[(bus_index * USB_MAX_DEVICE) + index]; + dev->devnum = index + 1; + dev->maxchild = 0; + for(i = 0; i < USB_MAXCHILDREN; dev->children[i++] = NULL); + dev->parent = NULL; + dev->priv_hcd = priv; + dev->usbnum = bus_index; + dev_index[bus_index]++; + return dev; +} + +/* + * By the time we get here, the device has gotten a new device ID + * and is in the default state. We need to identify the thing and + * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. + */ +int usb_new_device(struct usb_device *dev) +{ + int addr, err, tmp; + unsigned char *tmpbuf; +#ifndef CONFIG_LEGACY_USB_INIT_SEQ + struct usb_device_descriptor *desc; + int port = -1; + struct usb_device *parent = dev->parent; + unsigned short portstatus; +#endif + if(dev == NULL) + return 1; + /* We still haven't set the Address yet */ + addr = dev->devnum; + dev->devnum = 0; + tmpbuf = (unsigned char *)usb_malloc(USB_BUFSIZ); + if(tmpbuf == NULL) + { + USB_PRINTF("usb_new_device: malloc failure\r\n"); + return 1; + } +#ifdef CONFIG_LEGACY_USB_INIT_SEQ + /* this is the old and known way of initializing devices, it is + * different than what Windows and Linux are doing. Windows and Linux + * both retrieve 64 bytes while reading the device descriptor + * Several USB stick devices report ERR: CTL_TIMEOUT, caused by an + * invalid header while reading 8 bytes as device descriptor. */ + dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */ + dev->maxpacketsize = PACKET_SIZE_8; + dev->epmaxpacketin[0] = 8; + dev->epmaxpacketout[0] = 8; + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8); + if(err < 8) + { + board_printf("\r\nUSB device not responding, giving up (status=%lX)\r\n", dev->status); + usb_free(tmpbuf); + return 1; + } +#else + /* This is a Windows scheme of initialization sequence, with double + * reset of the device (Linux uses the same sequence) + * Some equipment is said to work only with such init sequence; this + * patch is based on the work by Alan Stern: + * http://sourceforge.net/mailarchive/forum.php? + * thread_id=5729457&forum_id=5398 + */ + /* send 64-byte GET-DEVICE-DESCRIPTOR request. Since the descriptor is + * only 18 bytes long, this will terminate with a short packet. But if + * the maxpacket size is 8 or 16 the device may be waiting to transmit + * some more, or keeps on retransmitting the 8 byte header. */ + desc = (struct usb_device_descriptor *)tmpbuf; + dev->descriptor.bMaxPacketSize0 = 64; /* Start off at 64 bytes */ + /* Default to 64 byte max packet size */ + dev->maxpacketsize = PACKET_SIZE_64; + dev->epmaxpacketin[0] = 64; + dev->epmaxpacketout[0] = 64; + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, 64); + if(err < 0) + { + USB_PRINTF("usb_new_device: usb_get_descriptor() failed\r\n"); + usb_free(tmpbuf); + return 1; + } + dev->descriptor.bMaxPacketSize0 = desc->bMaxPacketSize0; + /* find the port number we're at */ + if(parent) + { + int j; + for(j = 0; j < parent->maxchild; j++) + { + if(parent->children[j] == dev) + { + port = j; + break; + } + } + if(port < 0) + { + board_printf("usb_new_device: cannot locate device's port.\r\n"); + usb_free(tmpbuf); + return 1; + } + /* reset the port for the second time */ + err = hub_port_reset(dev->parent, port, &portstatus); + if(err < 0) + { + board_printf("\r\nCouldn't reset port %i\r\n", port); + usb_free(tmpbuf); + return 1; + } + } +#endif + dev->epmaxpacketin[0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; + switch (dev->descriptor.bMaxPacketSize0) + { + case 8: dev->maxpacketsize = PACKET_SIZE_8; break; + case 16: dev->maxpacketsize = PACKET_SIZE_16; break; + case 32: dev->maxpacketsize = PACKET_SIZE_32; break; + case 64: dev->maxpacketsize = PACKET_SIZE_64; break; + } + dev->devnum = addr; + err = usb_set_address(dev); /* set address */ + if(err < 0) + { + board_printf("\r\nUSB device not accepting new address (error=%lX)\r\n", dev->status); + usb_free(tmpbuf); + return 1; + } + wait_ms(10); /* Let the SET_ADDRESS settle */ + tmp = sizeof(dev->descriptor); + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor)); + if(err < tmp) + { + if(err < 0) + board_printf("unable to get device descriptor (error=%d)\r\n", err); + else + board_printf("USB device descriptor short read (expected %i, got %i)\r\n", tmp, err); + usb_free(tmpbuf); + return 1; + } + /* correct le values */ + le16_to_cpus(&dev->descriptor.bcdUSB); + le16_to_cpus(&dev->descriptor.idVendor); + le16_to_cpus(&dev->descriptor.idProduct); + le16_to_cpus(&dev->descriptor.bcdDevice); + /* only support for one config for now */ + usb_get_configuration_no(dev, &tmpbuf[0], 0); + usb_parse_config(dev, &tmpbuf[0], 0); + usb_set_maxpacket(dev); + /* we set the default configuration here */ + if(usb_set_configuration(dev, dev->config.bConfigurationValue)) + { + board_printf("failed to set default configuration len %d, status %lX\r\n", dev->act_len, dev->status); + usb_free(tmpbuf); + return -1; + } + USB_PRINTF("new device strings: Mfr=%d, Product=%d, SerialNumber=%d\r\n", + dev->descriptor.iManufacturer, dev->descriptor.iProduct, + dev->descriptor.iSerialNumber); + memset(dev->mf, 0, sizeof(dev->mf)); + memset(dev->prod, 0, sizeof(dev->prod)); + memset(dev->serial, 0, sizeof(dev->serial)); + if(dev->descriptor.iManufacturer) + usb_string(dev, dev->descriptor.iManufacturer, dev->mf, sizeof(dev->mf)); + if(dev->descriptor.iProduct) + usb_string(dev, dev->descriptor.iProduct, dev->prod, sizeof(dev->prod)); + if(dev->descriptor.iSerialNumber) + usb_string(dev, dev->descriptor.iSerialNumber, dev->serial, sizeof(dev->serial)); + USB_PRINTF("Manufacturer %s\r\n", dev->mf); + USB_PRINTF("Product %s\r\n", dev->prod); + USB_PRINTF("SerialNumber %s\r\n", dev->serial); + /* now prode if the device is a hub */ + usb_hub_probe(dev, 0); + usb_free(tmpbuf); + return 0; +} + +/* build device Tree */ +void usb_scan_devices(void *priv) +{ + int i; + struct usb_device *dev; + /* first make all devices unknown */ + for(i = 0; i < USB_MAX_DEVICE; i++) + { + memset(&usb_dev[(bus_index * USB_MAX_DEVICE) + i], 0, sizeof(struct usb_device)); + usb_dev[(bus_index * USB_MAX_DEVICE) + i].devnum = -1; + } + dev_index[bus_index] = 0; + /* device 0 is always present (root hub, so let it analyze) */ + dev = usb_alloc_new_device(bus_index, priv); + if(usb_new_device(dev)) + { + Cconws("No USB Device found\r\n"); + USB_PRINTF("No USB Device found\r\n"); + if(dev != NULL) + dev_index[bus_index]--; + } + else + { + kprint("%d USB Device(s) found\r\n", dev_index[bus_index]); + USB_PRINTF("%d USB Device(s) found\r\n", dev_index[bus_index]); + } +#ifndef USB_POLL_HUB + /* insert "driver" if possible */ +#ifdef CONFIG_USB_KEYBOARD + if(drv_usb_kbd_init() < 0) + USB_PRINTF("No USB keyboard found\r\n"); + else + Cconws("USB HID keyboard driver installed\r\n"); +#endif /* CONFIG_USB_KEYBOARD */ +#ifdef CONFIG_USB_MOUSE + if(drv_usb_mouse_init() < 0) + USB_PRINTF("No USB mouse found\r\n"); + else + Cconws("USB HID mouse driver installed\r\n"); +#endif /* CONFIG_USB_MOUSE */ +#endif + USB_PRINTF("Scan end\r\n"); +} + +/**************************************************************************** + * HUB "Driver" + * Probes device for being a hub and configurate it + */ + +#undef USB_HUB_DEBUG + +#ifdef USB_HUB_DEBUG +#ifdef COLDFIRE +#define USB_HUB_PRINTF(fmt, args...) board_printf(fmt , ##args) +#else +#define USB_HUB_PRINTF(fmt, args...) board_printf(fmt , ##args); Crawcin() +#endif /* COLDFIRE */ +#else +#define USB_HUB_PRINTF(fmt, args...) +#endif + +static struct usb_hub_device hub_dev[USB_MAX_BUS][USB_MAX_HUB]; +static int usb_hub_index[USB_MAX_BUS]; +char usb_error_str[256]; + +int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, USB_DT_HUB << 8, 0, data, size, USB_CNTL_TIMEOUT); +} + +int usb_clear_hub_feature(struct usb_device *dev, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, USB_CNTL_TIMEOUT); +} + +int usb_clear_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, USB_CNTL_TIMEOUT); +} + +int usb_set_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, USB_CNTL_TIMEOUT); +} + +int usb_get_hub_status(struct usb_device *dev, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + +int usb_get_port_status(struct usb_device *dev, int port, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, data, sizeof(struct usb_hub_status), USB_CNTL_TIMEOUT); +} + +static void usb_hub_power_on(struct usb_hub_device *hub) +{ + int i; + struct usb_device *dev; + dev = hub->pusb_dev; + /* Enable power to the ports */ + USB_HUB_PRINTF("enabling power on all ports\r\n"); + for (i = 0; i < dev->maxchild; i++) + { + usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + USB_HUB_PRINTF("port %d returns %lX\r\n", i + 1, dev->status); + wait_ms(hub->desc.bPwrOn2PwrGood * 2); + } +} + +void usb_hub_reset(int bus_index) +{ + usb_hub_index[bus_index] = 0; +} + +struct usb_hub_device *usb_hub_allocate(void) +{ + if(usb_hub_index[bus_index] < USB_MAX_HUB) + return &hub_dev[bus_index][usb_hub_index[bus_index]++]; + board_printf("ERROR: USB_MAX_HUB (%d) reached\r\n", USB_MAX_HUB); + return NULL; +} + +#define MAX_TRIES 5 + +static inline char *portspeed(int portstatus) +{ + if(portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) + return "480 Mb/s"; + else if(portstatus & (1 << USB_PORT_FEAT_LOWSPEED)) + return "1.5 Mb/s"; + else + return "12 Mb/s"; +} + +static int hub_port_reset(struct usb_device *dev, int port, unsigned short *portstat) +{ + int tries; + struct usb_port_status portsts; + unsigned short portstatus, portchange; + USB_HUB_PRINTF("hub_port_reset: resetting port %d...\r\n", port + 1); + for(tries = 0; tries < MAX_TRIES; tries++) + { + usb_set_port_feature(dev, port + 1, USB_PORT_FEAT_RESET); +#ifdef USB_POLL_HUB + vTaskDelay((200*configTICK_RATE_HZ)/1000); +#else + wait_ms(200); +#endif + if(usb_get_port_status(dev, port + 1, &portsts) < 0) + { + USB_HUB_PRINTF("get_port_status failed status %lX\r\n", dev->status); + return -1; + } + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + USB_HUB_PRINTF("USB %d portstatus %x, change %x, %s\r\n", dev->usbnum, portstatus, portchange, portspeed(portstatus)); + USB_HUB_PRINTF("STAT_C_CONNECTION = %d STAT_CONNECTION = %d USB_PORT_STAT_ENABLE = %d\r\n", + (portchange & USB_PORT_STAT_C_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_CONNECTION) ? 1 : 0, (portstatus & USB_PORT_STAT_ENABLE) ? 1 : 0); + if((portchange & USB_PORT_STAT_C_CONNECTION) || !(portstatus & USB_PORT_STAT_CONNECTION)) + return -1; + if(portstatus & USB_PORT_STAT_ENABLE) + break; +#ifdef USB_POLL_HUB + vTaskDelay((200*configTICK_RATE_HZ)/1000); +#else + wait_ms(200); +#endif + } + if(tries == MAX_TRIES) + { + USB_HUB_PRINTF("USB %d, cannot enable port %i after %i retries, disabling port.\r\n", dev->usbnum, port + 1, MAX_TRIES); + USB_HUB_PRINTF("Maybe the USB cable is bad?\r\n"); + return -1; + } + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_RESET); + *portstat = portstatus; + return 0; +} + +void usb_hub_port_connect_change(struct usb_device *dev, int port) +{ + struct usb_device *usb; + struct usb_port_status portsts; + unsigned short portstatus, portchange; + /* Check status */ + if(usb_get_port_status(dev, port + 1, &portsts) < 0) + { + USB_HUB_PRINTF("USB %d get_port_status failed\r\n", dev->usbnum); + return; + } + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + USB_HUB_PRINTF("USB %d, portstatus %x, change %x, %s\r\n", dev->usbnum, portstatus, portchange, portspeed(portstatus)); + /* Clear the connection change status */ + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_C_CONNECTION); + /* Disconnect any existing devices under this port */ + if(((!(portstatus & USB_PORT_STAT_CONNECTION)) + && (!(portstatus & USB_PORT_STAT_ENABLE))) || (dev->children[port])) + { + USB_HUB_PRINTF("USB %d port %i disconnected\r\n", dev->usbnum, port + 1); + usb_disconnect(&dev->children[port]); + /* Return now if nothing is connected */ + if(!(portstatus & USB_PORT_STAT_CONNECTION)) + return; + } +#ifdef USB_POLL_HUB + vTaskDelay((200*configTICK_RATE_HZ)/1000); +#else + wait_ms(200); +#endif + /* Reset the port */ + if(hub_port_reset(dev, port, &portstatus) < 0) + { + board_printf("USB %d cannot reset port %i!?\r\n", dev->usbnum, port + 1); + return; + } +#ifdef USB_POLL_HUB + vTaskDelay((200*configTICK_RATE_HZ)/1000); +#else + wait_ms(200); +#endif + /* Allocate a new device struct for it */ + usb = usb_alloc_new_device(dev->usbnum, dev->priv_hcd); + if(portstatus & USB_PORT_STAT_HIGH_SPEED) + usb->speed = USB_SPEED_HIGH; + else if(portstatus & USB_PORT_STAT_LOW_SPEED) + usb->speed = USB_SPEED_LOW; + else + usb->speed = USB_SPEED_FULL; + dev->children[port] = usb; + usb->parent = dev; + /* Run it through the hoops (find a driver, etc) */ + if(usb_new_device(usb)) + { + /* Woops, disable the port */ + USB_HUB_PRINTF("USB %d hub: disabling port %d\r\n", dev->usbnum, port + 1); + usb_clear_port_feature(dev, port + 1, USB_PORT_FEAT_ENABLE); + } +#ifdef USB_POLL_HUB + else + { +#ifdef CONFIG_USB_KEYBOARD + usb_kbd_register(usb); +#endif /* CONFIG_USB_KEYBOARD */ +#ifdef CONFIG_USB_MOUSE + usb_mouse_register(usb); +#endif /* CONFIG_USB_MOUSE */ +#ifdef CONFIG_USB_STORAGE + usb_stor_register(usb); +#endif /* CONFIG_USB_STORAGE */ + } +#endif +} + +static void usb_hub_events(struct usb_device *dev) +{ + int i; + struct usb_hub_device *hub = dev->hub; + if(hub == NULL) + return; + for(i = 0; i < dev->maxchild; i++) + { + struct usb_port_status portsts; + unsigned short portstatus, portchange; + if(usb_get_port_status(dev, i + 1, &portsts) < 0) + { + USB_HUB_PRINTF("get_port_status failed\r\n"); + continue; + } + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); +// USB_HUB_PRINTF("USB %d Port %d Status %X Change %X\r\n", dev->usbnum, i + 1, portstatus, portchange); + if(portchange & USB_PORT_STAT_C_CONNECTION) + { + USB_HUB_PRINTF("USB %d port %d connection change\r\n", dev->usbnum, i + 1); + usb_hub_port_connect_change(dev, i); + } + if(portchange & USB_PORT_STAT_C_ENABLE) + { + USB_HUB_PRINTF("USB %d port %d enable change, status %x\r\n", dev->usbnum, i + 1, portstatus); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); + /* EM interference sometimes causes bad shielded USB + * devices to be shutdown by the hub, this hack enables + * them again. Works at least with mouse driver */ + if(!(portstatus & USB_PORT_STAT_ENABLE) && (portstatus & USB_PORT_STAT_CONNECTION) && ((dev->children[i]))) + { + USB_HUB_PRINTF("USB %d already running port %i disabled by hub (EMI?), re-enabling...\r\n", dev->usbnum, i + 1); + usb_hub_port_connect_change(dev, i); + } + } + if(portstatus & USB_PORT_STAT_SUSPEND) + { + USB_HUB_PRINTF("USB %d port %d suspend change\r\n", dev->usbnum, i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); + } + if(portchange & USB_PORT_STAT_C_OVERCURRENT) + { + USB_HUB_PRINTF("USB %d port %d over-current change\r\n", dev->usbnum, i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); + usb_hub_power_on(hub); + } + if(portchange & USB_PORT_STAT_C_RESET) + { + USB_HUB_PRINTF("USB %d port %d reset change\r\n", dev->usbnum, i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); + } + } /* end for i all ports */ +} + +#ifdef USB_POLL_HUB +void usb_poll_hub_task(void *pvParameters) +{ + int index_bus = 0; + portTickType timeout = configTICK_RATE_HZ/10; + if(pvParameters); + while(1) + { + if(xQueueAltReceive(queue_poll_hub, &index_bus, timeout) == pdPASS) + { + if((index_bus >= 0) && (index_bus < USB_MAX_BUS) && (controller_priv[index_bus] != NULL)) + { + USB_HUB_PRINTF("USB %d event change\r\n", index_bus); +#ifdef CONFIG_USB_INTERRUPT_POLLING + *vblsem = 0; +#endif + usb_hub_events(&usb_dev[index_bus * USB_MAX_DEVICE]); +#ifdef CONFIG_USB_INTERRUPT_POLLING + *vblsem = 1; +#endif + } + } + else /* timeout */ + { + int i; +#ifdef CONFIG_USB_INTERRUPT_POLLING + *vblsem = 0; +#endif + for(i = 0; i < USB_MAX_BUS ; i++) + { + if(controller_priv[i] != NULL) + usb_hub_events(&usb_dev[i * USB_MAX_DEVICE]); + } +#ifdef CONFIG_USB_INTERRUPT_POLLING + *vblsem = 1; +#endif + } + timeout = portMAX_DELAY; + } +} +#endif /* USB_POLL_HUB */ + +int usb_hub_configure(struct usb_device *dev) +{ + unsigned char *buffer, *bitmap; + struct usb_hub_descriptor *descriptor; + struct usb_hub_status *hubsts; + int i; + struct usb_hub_device *hub; + /* "allocate" Hub device */ + hub = usb_hub_allocate(); + dev->hub = hub; + if(hub == NULL) + return -1; + hub->pusb_dev = dev; + buffer = (unsigned char *)usb_malloc(USB_BUFSIZ); + if(buffer == NULL) + { + USB_HUB_PRINTF("usb_hub_configure: malloc failure\r\n"); + return -1; + } + /* Get the the hub descriptor */ + if(usb_get_hub_descriptor(dev, buffer, 4) < 0) + { + USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor, giving up %lX\r\n", dev->status); + usb_free(buffer); + return -1; + } + USB_HUB_PRINTF("bLength:%02X bDescriptorType:%02X bNbrPorts:%02X\r\n", buffer[0], buffer[1], buffer[2]); + descriptor = (struct usb_hub_descriptor *)buffer; + /* silence compiler warning if USB_BUFSIZ is > 256 [= sizeof(char)] */ + i = descriptor->bLength; + if(i > USB_BUFSIZ) + { + USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor - too long: %d\r\n", descriptor->bLength); + usb_free(buffer); + return -1; + } + if(usb_get_hub_descriptor(dev, buffer, descriptor->bLength) < 0) + { + USB_HUB_PRINTF("usb_hub_configure: failed to get hub descriptor 2nd giving up %lX\r\n", dev->status); + usb_free(buffer); + return -1; + } + memcpy((unsigned char *)&hub->desc, buffer, descriptor->bLength); + /* adjust 16bit values */ + hub->desc.wHubCharacteristics = le16_to_cpu(descriptor->wHubCharacteristics); + /* set the bitmap */ + bitmap = (unsigned char *)&hub->desc.DeviceRemovable[0]; + /* devices not removable by default */ + memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); + bitmap = (unsigned char *)&hub->desc.PortPowerCtrlMask[0]; + memset(bitmap, 0xff, (USB_MAXCHILDREN+1+7)/8); /* PowerMask = 1B */ + for(i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) + hub->desc.DeviceRemovable[i] = descriptor->DeviceRemovable[i]; + for(i = 0; i < ((hub->desc.bNbrPorts + 1 + 7)/8); i++) + hub->desc.DeviceRemovable[i] = descriptor->PortPowerCtrlMask[i]; + dev->maxchild = descriptor->bNbrPorts; + USB_HUB_PRINTF("USB %d, %d ports detected\r\n", dev->usbnum, dev->maxchild); + if(dev->maxchild >= 10) + dev->maxchild = 10; + switch(hub->desc.wHubCharacteristics & HUB_CHAR_LPSM) + { + case 0x00: USB_HUB_PRINTF("ganged power switching\r\n"); break; + case 0x01: USB_HUB_PRINTF("individual port power switching\r\n"); break; + case 0x02: + case 0x03: USB_HUB_PRINTF("unknown reserved power switching mode\r\n"); break; + } + if(hub->desc.wHubCharacteristics & HUB_CHAR_COMPOUND) + { + USB_HUB_PRINTF("part of a compound device\r\n"); + } + else + { + USB_HUB_PRINTF("standalone hub\r\n"); + } + switch(hub->desc.wHubCharacteristics & HUB_CHAR_OCPM) + { + case 0x00: USB_HUB_PRINTF("global over-current protection\r\n"); break; + case 0x08: USB_HUB_PRINTF("individual port over-current protection\r\n"); break; + case 0x10: + case 0x18: USB_HUB_PRINTF("no over-current protection\r\n"); break; + } + USB_HUB_PRINTF("power on to power good time: %dms\r\n", descriptor->bPwrOn2PwrGood * 2); + USB_HUB_PRINTF("hub controller current requirement: %dmA\r\n", descriptor->bHubContrCurrent); + for(i = 0; i < dev->maxchild; i++) + { + USB_HUB_PRINTF("USB %d port %d is%s removable\r\n", dev->usbnum, i + 1, hub->desc.DeviceRemovable[(i + 1) / 8] & (1 << ((i + 1) % 8)) ? " not" : ""); + } + if(sizeof(struct usb_hub_status) > USB_BUFSIZ) + { + USB_HUB_PRINTF("usb_hub_configure: failed to get Status - too long: %d\r\n", descriptor->bLength); + usb_free(buffer); + return -1; + } + if(usb_get_hub_status(dev, buffer) < 0) + { + USB_HUB_PRINTF("usb_hub_configure: failed to get Status %lX\r\n", dev->status); + usb_free(buffer); + return -1; + } + hubsts = (struct usb_hub_status *)buffer; + USB_HUB_PRINTF("get_hub_status returned status %X, change %X\r\n", le16_to_cpu(hubsts->wHubStatus), le16_to_cpu(hubsts->wHubChange)); + USB_HUB_PRINTF("local power source is %s\r\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); + USB_HUB_PRINTF("%sover-current condition exists\r\n", (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no "); + usb_hub_power_on(hub); +#ifdef USB_POLL_HUB + if(queue_poll_hub == NULL) + { + queue_poll_hub = xQueueCreate(64, sizeof(int)); + if(queue_poll_hub != NULL) + { + /* Create poll/event task */ + if(xTaskCreate(usb_poll_hub_task, (void *)"USBHub", configMINIMAL_STACK_SIZE, NULL, 16, NULL) != pdPASS) + { + vQueueDelete(queue_poll_hub); + queue_poll_hub = NULL; + } + } + vTaskDelay(configTICK_RATE_HZ); + } + if(queue_poll_hub == NULL) +#endif + usb_hub_events(dev); + usb_free(buffer); + return 0; +} + +int usb_hub_probe(struct usb_device *dev, int ifnum) +{ + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int ret; + iface = &dev->config.if_desc[ifnum]; + /* Is it a hub? */ + if(iface->bInterfaceClass != USB_CLASS_HUB) + return 0; + /* Some hubs have a subclass of 1, which AFAICT according to the */ + /* specs is not defined, but it works */ + if((iface->bInterfaceSubClass != 0) && (iface->bInterfaceSubClass != 1)) + return 0; + /* Multiple endpoints? What kind of mutant ninja-hub is this? */ + if(iface->bNumEndpoints != 1) + return 0; + ep = &iface->ep_desc[0]; + /* Output endpoint? Curiousier and curiousier.. */ + if(!(ep->bEndpointAddress & USB_DIR_IN)) + return 0; + /* If it's not an interrupt endpoint, we'd better punt! */ + if((ep->bmAttributes & 3) != 3) + return 0; + /* We found a hub */ + USB_HUB_PRINTF("USB %d hub found\r\n", dev->usbnum); + ret = usb_hub_configure(dev); + return ret; +} + +#endif /* CONFIG_USB_UHCI || CONFIG_USB_OHCI || CONFIG_USB_EHCI */ + diff --git a/i2cspi_BaS_gcc/sources/usb_kbd.c b/i2cspi_BaS_gcc/sources/usb_kbd.c new file mode 100644 index 0000000..249a50b --- /dev/null +++ b/i2cspi_BaS_gcc/sources/usb_kbd.c @@ -0,0 +1,1329 @@ +/* + * (C) Copyright 2001 + * Denis Peter, MPL AG Switzerland + * + * Part of this source has been derived from the Linux USB + * project. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include "config.h" +#include "usb.h" + +#undef USB_KBD_DEBUG + +#undef USE_COUNTRYCODE + +#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined(CONFIG_USB_EHCI) +#ifdef CONFIG_USB_KEYBOARD + +#ifdef USB_KBD_DEBUG +#define USB_KBD_PRINTF(fmt,args...) board_printf(fmt ,##args) +#else +#define USB_KBD_PRINTF(fmt,args...) +#endif + +typedef struct +{ + long ident; + union + { + long l; + short i[2]; + char c[4]; + } v; +} COOKIE; + +extern void ltoa(char *buf, long n, unsigned long base); +extern void call_ikbdvec(unsigned char code, _IOREC *iorec, void (**ikbdvec)()); +extern int asm_set_ipl(int level); +#ifdef USE_COUNTRYCODE +static int usb_kbd_get_hid_desc(struct usb_device *dev); +#endif + +/* under TOS Repeat keys are build by timer C so infinite (0) or 1000 is a good value */ +#define REPEAT_RATE 0 // 40 /* 40msec -> 25cps */ + +#define MAX_VALUE_LOOKUP 0x90 +#define MAX_VALUE_ATARI 0x75 + +#define NUM_LOCK 0x53 +#define CAPS_LOCK 0x39 +#define SCROLL_LOCK 0x47 + +/* Modifier bits */ +#define LEFT_CNTR 0 +#define LEFT_SHIFT 1 +#define LEFT_ALT 2 +#define LEFT_GUI 3 +#define RIGHT_CNTR 4 +#define RIGHT_SHIFT 5 +#define RIGHT_ALT 6 +#define RIGHT_GUI 7 + +/* HID bCountryCode */ +#define CC_NOT 0 +#define CC_ISO 13 +#define CC_USA 33 +#define CC_FRG 9 +#define CC_FRA 8 +#define CC_UK 32 +#define CC_SPA 25 +#define CC_ITA 14 +#define CC_SWE 29 +#define CC_SWF 27 +#define CC_SWG 28 + +/* Language cookie */ +#define USA 0 /* English */ +#define FRG 1 /* German */ +#define FRA 2 /* French */ +#define UK 3 /* English */ +#define SPA 4 /* Spanish */ +#define ITA 5 /* Italian */ +#define SWE 6 /* Swiss */ +#define SWF 7 /* Swiss French */ +#define SWG 8 /* Swiss German */ + +extern _IOREC *iorec; +extern void (**ikbdvec)(); + +static unsigned char *new; +static unsigned char old[8]; +static unsigned char num_lock; +static unsigned char caps_lock; +static unsigned char scroll_lock; +static unsigned char old_modifier; +static union +{ + struct + { + unsigned reserved1:3; + unsigned force_alt_shift:1; + unsigned right_shift_host:1; + unsigned left_shift_host:1; + unsigned alt_host:1; + unsigned ctrl_host:1; + unsigned key_forced:1; + unsigned reserved2:3; + unsigned altgr_usb:1; + unsigned shift_usb:1; + unsigned altgr_usb_break:1; + unsigned shift_usb_break:1; + } b; + unsigned short s; +} flags; + +#ifdef USE_COUNTRYCODE +struct usb_hid_descriptor +{ + unsigned char bLength; + unsigned char bDescriptorType; /* 0x21 for HID */ + unsigned short bcdHID; /* release number */ + unsigned char bCountryCode; + unsigned char bNumDescriptors; + unsigned char bReportDescriptorType; + unsigned short wDescriptorLength; +} __attribute__ ((packed)); + +static struct usb_hid_descriptor usb_kbd_hid_desc; +#endif + +static unsigned char *leds; +static int kbd_installed; + +static unsigned char usb_kbd_to_atari_scancode[] = +{ +// A(Q) B C D + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x2E, 0x20, +// E F G H I J K L + 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, +// M(,) N O P Q(A) R S T + 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, +// U V W(Z) X Y Z(W) 1 2 + 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x02, 0x03, +// 3 4 5 6 7 8 9 0 + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, +// RET ESC BACK TAB SPACE -()) = [(^) + 0x1C, 0x01, 0x0E, 0x0F, 0x39, 0x0C, 0x0D, 0x1A, +// ]($) \(*) EUR1 ;(M) ' ` ,(;) .(:) + 0x1B, 0x2B, 0x00, 0x27, 0x28, 0x5B, 0x33, 0x34, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0x35, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0x41, 0x42, 0x43, 0x44, 0x62, 0x61, 0x49, 0x4C, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0x4F, 0x52, 0x47, 0x45, 0x53, 0x55, 0x46, 0x4D, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0x4B, 0x50, 0x48, 0x54, 0x65, 0x66, 0x4A, 0x4E, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0x72, 0x6D, 0x6E, 0x6F, 0x6A, 0x6B, 0x6C, 0x67, +// KP8 KP9 KP0 KP. >< APP POWER KP= + 0x68, 0x69, 0x70, 0x71, 0x60, 0x00, 0x00, 0x00, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x60, 0x00, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, +//VolUp VolDn + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x1D, 0x2A, 0x38, 0x56, 0x1D, 0x36, 0x38, 0x57 // virtual codes +}; + +static unsigned char usb_kbd_to_atari_fr_modifier[] = +{ +/* This table can change host SHIFT & ALT states for each scancode, values */ +/* are in hexa : bit 7: 1 for a valid entry */ +/* bit 6: 1 for force CTRL */ +/* bit 5: ALT, bit 4: SHIFT states for the AltGR table */ +/* bit 3: ALT, bit 2: SHIFT states for the Shift table */ +/* bit 1: ALT, bit 0: SHIFT states for the Unshift table */ +// A(Q) B C D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// E F G H I J K L + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// M(,) N O P Q(A) R S T + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// U V W(Z) X Y Z(W) 1 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, +// 3 4 5 6 7 8 9 0 + 0x00, 0xB4, 0xA4, 0x94, 0x00, 0xA5, 0x84, 0xA4, +// RET ESC BACK TAB SPACE -()) = [(^) + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xB4, 0x00, +// ]($) \(*) ;(M) ' ` ,(;) .(:) + 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0x00, 0x00, 0x00, 0x95, 0x00, 0x95, 0x95, 0x00, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// KP8 KP9 KP0 KP. >< APP POWER KP= + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x00, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//VolUp VolDn + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xEA +}; + +static unsigned char usb_kbd_to_atari_fr_unshift[] = +{ +// A(Q) B C D + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x2E, 0x20, +// E F G H I J K L + 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, +// M(,) N O P Q(A) R S T + 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, +// U V W(Z) X Y Z(W) 1 2 + 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x02, 0x03, +// 3 4 5 6 7 8 9 0 + 0x04, 0x05, 0x06, 0x0D, 0x08, 0x0D, 0x0A, 0x0B, +// RET ESC BACK TAB SPACE -()) = [(^) + 0x1C, 0x01, 0x0E, 0x0F, 0x39, 0x0C, 0x35, 0x1A, +// ]($) \(*) ;(M) ' ` ,(;) .(:) + 0x1B, 0x1B, 0x00, 0x27, 0x28, 0x00, 0x33, 0x34, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0x09, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0x41, 0x42, 0x43, 0x44, 0x62, 0x61, 0x62, 0x4C, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0x4F, 0x52, 0x47, 0x45, 0x53, 0x47, 0x46, 0x4D, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0x4B, 0x50, 0x48, 0x00, 0x65, 0x66, 0x4A, 0x4E, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0x72, 0x6D, 0x6E, 0x6F, 0x6A, 0x6B, 0x6C, 0x67, +// KP8 KP9 KP0 KP. >< APP KP= + 0x68, 0x69, 0x70, 0x71, 0x60, 0x00, 0x00, 0x00, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x60, 0x00, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, +//VolUp VolDn + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x1D, 0x2A, 0x38, 0x0F, 0x1D, 0x36, 0x00, 0x0F +}; + +static unsigned char usb_kbd_to_atari_fr_shift[] = +{ +/* Hexa values, 00: unused => use the Unshift table */ +/* FF: invalid => no scancode */ +// A(Q) B C D + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x2E, 0x20, +// E F G H I J K L + 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, +// M(,) N O P Q(A) R S T + 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, +// U V W(Z) X Y Z(W) 1 2 + 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x02, 0x03, +// 3 4 5 6 7 8 9 0 + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, +// RET ESC BACK TAB SPACE -()) = [(^) + 0x1C, 0x01, 0x0E, 0x0F, 0x39, 0x0C, 0x35, 0x1A, +// ]($) \(*) ;(M) ' ` ,(;) .(:) + 0x29, 0xFF, 0x00, 0x27, 0x28, 0x00, 0x33, 0x34, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0x07, 0x3A, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0x5A, 0x5B, 0x5C, 0x5D, 0x62, 0x61, 0x62, 0x4C, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0x4F, 0x52, 0x47, 0x48, 0x53, 0x47, 0x50, 0x4D, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0x4B, 0x50, 0x48, 0x00, 0x65, 0x66, 0x4A, 0x4E, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0x72, 0x6D, 0x6E, 0x6F, 0x6A, 0x6B, 0x6C, 0x67, +// KP8 KP9 KP0 KP. >< APP KP= + 0x68, 0x69, 0x70, 0x71, 0x60, 0x00, 0x00, 0x00, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x60, 0x00, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, +//VolUp VolDn + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x1D, 0x2A, 0x38, 0x0F, 0x1D, 0x36, 0x00, 0x0F +}; + +static unsigned char usb_kbd_to_atari_fr_altgr[] = +{ +/* Hexa values, 00: unused => use the Unshift table */ +/* FF: invalid => no scancode */ +// A(Q) B C D + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// E F G H I J K L + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// M(,) N O P Q(A) R S T + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// U V W(Z) X Y Z(W) 1 2 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, +// 3 4 5 6 7 8 9 0 + 0x2B, 0x1A, 0x1A, 0x2B, 0x29, 0x28, 0x1A, 0x2B, +// RET ESC BACK TAB SPACE -()) = [(^) + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1B, 0x1B, 0xFF, +// ]($) \(*) ;(M) ' ` ,(;) .(:) + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// KP8 KP9 KP0 KP. >< APP KP= + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x62, 0xFF, 0xFF, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0xFF, 0xFF, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +//VolUp VolDn + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF +}; + +static unsigned char usb_kbd_to_atari_de_modifier[] = +{ +/* This table can change host SHIFT & ALT states for each scancode, values */ +/* are in hexa : bit 7: 1 for a valid entry */ +/* bit 6: 1 for force CTRL */ +/* bit 5: ALT, bit 4: SHIFT states for the AltGR table */ +/* bit 3: ALT, bit 2: SHIFT states for the Shift table */ +/* bit 1: ALT, bit 0: SHIFT states for the Unshift table */ +// A(Q) B C D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// E F G H I J K L + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// M(,) N O P Q(A) R S T + 0x00, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, +// U V W(Z) X Y Z(W) 1 2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 3 4 5 6 7 8 9 0 + 0x00, 0x00, 0x00, 0x00, 0xB4, 0xA4, 0xA4, 0xB4, +// RET ESC BACK TAB SPACE -()) = [(^) + 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, +// ]($) \(*) ;(M) ' ` ,(;) .(:) + 0x00, 0x80, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0x00, 0x00, 0x00, 0x81, 0x00, 0x95, 0x81, 0x00, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// KP8 KP9 KP0 KP. >< APP POWER KP= + 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x00, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//VolUp VolDn + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xEA +}; + +static unsigned char usb_kbd_to_atari_de_unshift[] = +{ +// A(Q) B C D + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x2E, 0x20, +// E F G H I J K L + 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, +// M(,) N O P Q(A) R S T + 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, +// U V W(Z) X Y Z(W) 1 2 + 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x02, 0x03, +// 3 4 5 6 7 8 9 0 + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, +// RET ESC BACK TAB SPACE -()) = [(^) + 0x1C, 0x01, 0x0E, 0x0F, 0x39, 0x0C, 0x0D, 0x1A, +// ]($) \(*) ;(M) ' ` ,(;) .(:) + 0x1B, 0x29, 0x00, 0x27, 0x28, 0x29, 0x33, 0x34, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0x35, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0x41, 0x42, 0x43, 0x44, 0x63, 0x64, 0x62, 0x50, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0x61, 0x52, 0x47, 0x48, 0x53, 0x47, 0x50, 0x4D, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0x4B, 0x50, 0x48, 0x54, 0x65, 0x66, 0x4A, 0x4E, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0x72, 0x6D, 0x6E, 0x6F, 0x6A, 0x6B, 0x6C, 0x67, +// KP8 KP9 KP0 KP. >< APP KP= + 0x68, 0x69, 0x70, 0x71, 0x60, 0x00, 0x00, 0x00, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x60, 0x00, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, +//VolUp VolDn + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x1D, 0x2A, 0x38, 0x0F, 0x1D, 0x36, 0x00, 0x0F +}; + +static unsigned char usb_kbd_to_atari_de_shift[] = +{ +/* Hexa values, 00: unused => use the Unshift table */ +/* FF: invalid => no scancode */ +// A(Q) B C D + 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x2E, 0x20, +// E F G H I J K L + 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, +// M(,) N O P Q(A) R S T + 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, +// U V W(Z) X Y Z(W) 1 2 + 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x02, 0x03, +// 3 4 5 6 7 8 9 0 + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, +// RET ESC BACK TAB SPACE -()) = [(^) + 0x1C, 0x01, 0x0E, 0x0F, 0x39, 0x0C, 0x0D, 0x1A, +// ]($) \(*) ;(M) ' ` ,(;) .(:) + 0x1B, 0x0D, 0x00, 0x27, 0x28, 0x34, 0x33, 0x34, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0x35, 0x3A, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0x5A, 0x5B, 0x5C, 0x5D, 0x63, 0x64, 0x62, 0x50, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0x61, 0x52, 0x47, 0x48, 0x53, 0x47, 0x50, 0x4D, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0x4B, 0x50, 0x48, 0x54, 0x65, 0x66, 0x4A, 0x4E, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0x72, 0x6D, 0x6E, 0x6F, 0x6A, 0x6B, 0x6C, 0x67, +// KP8 KP9 KP0 KP. >< APP KP= + 0x68, 0x69, 0x70, 0x71, 0x60, 0x00, 0x00, 0x00, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x60, 0x00, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, +//VolUp VolDn + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x1D, 0x2A, 0x38, 0x0F, 0x1D, 0x36, 0x00, 0x0F +}; + +static unsigned char usb_kbd_to_atari_de_altgr[] = +{ +/* Hexa values, 00: unused => use the Unshift table */ +/* FF: invalid => no scancode */ +// A(Q) B C D + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// E F G H I J K L + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// M(,) N O P Q(A) R S T + 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0xFF, 0xFF, 0xFF, +// U V W(Z) X Y Z(W) 1 2 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// 3 4 5 6 7 8 9 0 + 0xFF, 0xFF, 0xFF, 0xFF, 0x27, 0x27, 0x28, 0x28, +// RET ESC BACK TAB SPACE -()) = [(^) + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0x2B, 0xFF, +// ]($) \(*) ;(M) ' ` ,(;) .(:) + 0x2B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// /(!) CAPS F1 F2 F3 F4 F5 F6 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// F7 F8 F9 F10 F11 F12 PrtSc ScLoc + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +//PAUSE INS HOME PgUp DEL END PgDn -> + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// <- DOWN UP NuLoc KP/ KP* KP- KP+ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, +// ENT KP1 KP2 KP3 KP4 KP5 KP6 KP7 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// KP8 KP9 KP0 KP. >< APP KP= + 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 0xFF, 0xFF, 0xFF, +// F13, F14, F15 F16 F17 F18 F19 F20 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +// F21 F22 F23 F24 EXEC HELP MENU SEL + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x62, 0xFF, 0xFF, +// STOP AGAIN UNDO CUT COPY PASTE FIND MUTE + 0xFF, 0xFF, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +//VolUp VolDn + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +//LCTRL LSHFT LALT LGUI RCTRL RSHFT RALT RGUI + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF +}; + +static COOKIE *get_cookie(long id) +{ + COOKIE *p= *(COOKIE **)0x5a0; + while(p) + { + if(p->ident == id) + return(p); + if(!p->ident) + return((COOKIE *)0); + p++; + } + return((COOKIE *)0); +} + +static void *memscan(void *addr, int c, int size) +{ + unsigned char *p = (unsigned char *)addr; + while(size) + { + if(*p == (char)c) + return(void *)p; + p++; + size--; + } + return(void *)p; +} + +/* forward declaration */ +static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum); + +/* deregistering the keyboard */ +int usb_kbd_deregister(struct usb_device *dev) +{ + dev->irq_handle = NULL; + if(new != NULL) + { + usb_free(new); + new = NULL; + } + if(leds != NULL) + { + usb_free(leds); + leds = NULL; + } + kbd_installed = 0; + USB_KBD_PRINTF("USB KBD deregister\r\n"); + return 1; +} + +/* registering the keyboard */ +int usb_kbd_register(struct usb_device *dev) +{ + if(!kbd_installed && (dev->devnum != -1) && (usb_kbd_probe(dev, 0) == 1)) + { /* Ok, we found a keyboard */ + USB_KBD_PRINTF("USB KBD found (iorec: 0x%x, USB: %d, devnum: %d)\r\n", iorec, dev->usbnum, dev->devnum); + num_lock = caps_lock = scroll_lock = old_modifier = 0; + flags.s = 0; + kbd_installed = 1; + dev->deregister = usb_kbd_deregister; + return 1; + } + /* no USB Keyboard found */ + return -1; +} + +/* search for keyboard and register it if found */ +int drv_usb_kbd_init(void) +{ + int i, j; + if(kbd_installed) + return -1; + /* scan all USB Devices */ + for(j = 0; j < USB_MAX_BUS; j++) + { + for(i = 0; i < USB_MAX_DEVICE; i++) + { + struct usb_device *dev = usb_get_dev_index(i, j); /* get device */ + if(dev == NULL) + break; + if(usb_kbd_register(dev) > 0) + return 1; + } + } + /* no USB Keyboard found */ + return -1; +} + +/************************************************************************** + * Low Level drivers + */ + +/* set the LEDs. Since this is used in the irq routine, the control job + is issued with a timeout of 0. This means, that the job is queued without + waiting for job completion */ + +static void usb_kbd_setled(struct usb_device *dev) +{ + struct usb_interface_descriptor *iface = &dev->config.if_desc[0]; + unsigned char *pleds = (unsigned char *)(((unsigned long)leds + 3) & ~3); + if(scroll_lock != 0) + *pleds = 4; + else + *pleds = 0; + if(caps_lock != 0) + *pleds |= 2; + if(num_lock != 0) + *pleds |= 1; + usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x200, iface->bInterfaceNumber, pleds, 1, 0); +} + +static void usb_kbd_send_code(unsigned char code) +{ + if((iorec != NULL) && (ikbdvec != NULL)) + call_ikbdvec(code, iorec, ikbdvec); +} + +/* Translate the scancode */ +static int usb_kbd_translate(unsigned char scancode, unsigned char modifier, int pressed) +{ + unsigned char keycode = 0, atari_modifier = 0; +#ifdef CONFIG_USB_INTERRUPT_POLLING + int level; +#endif + int type = USA; + USB_KBD_PRINTF("USB KBD scancode: 0x%02x, modifier:0x%02x, pressed: %d\r\n", scancode, modifier, pressed); + flags.b.force_alt_shift = 0; + if(scancode > MAX_VALUE_LOOKUP) + keycode = 0; + else + { + if(scancode == 0x8A) /* LEFT ALT */ + keycode = 0x38; /* Alt Atari */ + else + { + unsigned char *unshift_table = NULL; + unsigned char *shift_table = NULL; + unsigned char *altgr_table = NULL; + unsigned char *modifier_table = NULL; + unsigned long lang = USA; + COOKIE *p = get_cookie('_AKP'); + if(p != NULL) + lang = (p->v.l >> 8) & 0xFF; +#ifdef USE_COUNTRYCODE + switch(usb_kbd_hid_desc.bCountryCode) + { + case CC_USA: lang = USA; break; + case CC_FRG: lang = FRG; break; + case CC_FRA: lang = FRA; break; + case CC_UK: lang = UK; break; + case CC_SPA: lang = SPA; break; + case CC_ITA: lang = ITA; break; + case CC_SWE: lang = SWE; break; + case CC_SWF: lang = SWF; break; + case CC_SWG: lang = SWG; break; + } +#endif + switch(lang) + { + case FRA: + case SWF: + unshift_table = usb_kbd_to_atari_fr_unshift; + shift_table = usb_kbd_to_atari_fr_shift; + altgr_table = usb_kbd_to_atari_fr_altgr; + modifier_table = usb_kbd_to_atari_fr_modifier; + type = FRA; + break; + case FRG: + case SWG: + unshift_table = usb_kbd_to_atari_de_unshift; + shift_table = usb_kbd_to_atari_de_shift; + altgr_table = usb_kbd_to_atari_de_altgr; + modifier_table = usb_kbd_to_atari_de_modifier; + type = FRG; + break; + default: + unshift_table = usb_kbd_to_atari_scancode; + break; + } + if(modifier != old_modifier) + { + if(((modifier & (1 << LEFT_SHIFT)) != 0) || ((modifier & (1 << RIGHT_SHIFT)) != 0)) + { + if(!flags.b.shift_usb) + { + flags.b.shift_usb = 1; + flags.b.shift_usb_break = 1; + } + } + else + flags.b.shift_usb = 0; + if((modifier & (1 << RIGHT_ALT)) != 0) + { + if(!flags.b.altgr_usb) + { + flags.b.altgr_usb = 1; + flags.b.altgr_usb_break = 1; + } + } + else + flags.b.altgr_usb = 0; + old_modifier = modifier; + } + else if(pressed) + { + if(!flags.b.altgr_usb) + flags.b.altgr_usb_break = 0; + if(!flags.b.shift_usb) + flags.b.shift_usb_break = 0; + } + keycode = unshift_table[scancode]; + if((modifier & (1 << LEFT_ALT)) == 0) + { + /* This modifier table can change host SHIFT & ALT states for each scancode, values + are in hexa : bit 7: 1 for a valid entry + bit 6: 1 for force CTRL + bit 5: ALT, bit 4: SHIFT states for the AltGR table + bit 3: ALT, bit 2: SHIFT states for the Shift table + bit 1: ALT, bit 0: SHIFT states for the Unshift table + */ + if(modifier_table != NULL) + atari_modifier = modifier_table[scancode]; + else + atari_modifier = 0; + if((atari_modifier & (1 << 7)) != 0) + flags.b.force_alt_shift = 1; + else + atari_modifier = 0; + if(flags.b.altgr_usb_break) + { + if(altgr_table[scancode]) + { + keycode = altgr_table[scancode]; + if((atari_modifier & (1 << 6)) != 0) + atari_modifier = (atari_modifier >> 4) | (atari_modifier & (1 << 6)); + else + atari_modifier >>= 4; + } + } + else if(flags.b.shift_usb_break) + { + if(shift_table[scancode]) + { + keycode = shift_table[scancode]; + if((atari_modifier & (1 << 6)) != 0) + atari_modifier = (atari_modifier >> 2) | (atari_modifier & (1 << 6)); + else + atari_modifier >>= 2; + } + } + } + } + } + USB_KBD_PRINTF("USB KBD atari-%s keycode:0x%02x, modifier:0x%02x, flags:0x%04x\r\n", (type == FRA) ? "fr" : (type == FRG) ? "de" : "us", keycode, atari_modifier, flags.s); + if(keycode == 0x1D) /* CTRL */ + { + if(pressed) + flags.b.ctrl_host = 1; + else + flags.b.ctrl_host = 0; + } + if(keycode == 0x36) /* RSHIFT */ + { + if(pressed) + flags.b.right_shift_host = 1; + else + flags.b.right_shift_host = 0; + } + if(keycode == 0x2A) /* LSHIFT */ + { + if(pressed) + flags.b.left_shift_host = 1; + else + flags.b.left_shift_host = 0; + } +#ifdef CONFIG_USB_INTERRUPT_POLLING + level = asm_set_ipl(7); /* mask interrupts for use call_ikbdvec() */ +#endif + if(pressed && (flags.b.force_alt_shift)) + { + flags.b.key_forced = 1; + if(((atari_modifier & (1 << 6)) != 0) && flags.b.ctrl_host) + usb_kbd_send_code(0x1D); /* CTRL */ + if((atari_modifier & (1 << 0)) == 0) + { + if(flags.b.left_shift_host) + usb_kbd_send_code(0xAA); /* !LSHIFT */ + if(flags.b.right_shift_host) + usb_kbd_send_code(0xB6); /* !RSHIFT */ + } + else + { + if(!flags.b.left_shift_host) + usb_kbd_send_code(0x2A); /* LSHIFT */ + if(!flags.b.right_shift_host && (keycode != 0x60)) /* < */ + usb_kbd_send_code(0x36); /* RSHIFT */ + } + if((atari_modifier & (1 << 1)) == 0) + { + if(flags.b.alt_host) + usb_kbd_send_code(0xB8); /* !ALT */ + } + else + { + if(!flags.b.alt_host) + usb_kbd_send_code(0x38); /* ALT */ + } + } + if((keycode !=0) && (keycode <= MAX_VALUE_ATARI)) + usb_kbd_send_code(pressed ? keycode : keycode | 0x80); + if(!pressed && (flags.b.force_alt_shift)) + { + flags.b.key_forced = 0; + if(((atari_modifier & (1 << 6)) != 0) && flags.b.ctrl_host) + usb_kbd_send_code(0x9D); /* !CTRL */ + if((atari_modifier & (1 << 0)) == 0) + { + if(flags.b.left_shift_host) + usb_kbd_send_code(0x2A); /* LSHIFT */ + if(flags.b.right_shift_host) + usb_kbd_send_code(0x36); /* RSHIFT */ + } + else + { + if(!flags.b.left_shift_host) + usb_kbd_send_code(0xAA); /* !LSHIFT */ + if(!flags.b.right_shift_host) + usb_kbd_send_code(0xB6); /* !RSHIFT */ + } + if((atari_modifier & (1 << 1)) == 0) + { + if(flags.b.alt_host) + usb_kbd_send_code(0x38); /* ALT */ + } + else + { + if(!flags.b.alt_host) + usb_kbd_send_code(0xB8); /* !ALT */ + } + } +#ifdef CONFIG_USB_INTERRUPT_POLLING + asm_set_ipl(level); +#endif + if(pressed == 1) + { + if(scancode == NUM_LOCK) + { + num_lock = ~num_lock; + return 1; + } + if(scancode == CAPS_LOCK) + { + caps_lock = ~caps_lock; + return 1; + } + if(scancode == SCROLL_LOCK) + { + scroll_lock = ~scroll_lock; + return 1; + } + } + return 0; +} + +/* Interrupt service routine */ +static int usb_kbd_irq(struct usb_device *dev) +{ + int i,res; + if((dev->irq_status != 0) || (dev->irq_act_len != 8)) + { + USB_KBD_PRINTF("USB KBD error %lX, len %d\r\n",dev->irq_status,dev->irq_act_len); + return 1; + } + res = 0; + for(i = 2; i < 8; i++) + { + if(old[i] > 3 && memscan(&new[2], old[i], 6) == &new[8]) + { + res |= usb_kbd_translate(old[i], new[0], 0); + old[0] = new[0]; + } + if(new[i] > 3 && memscan(&old[2], new[i], 6) == &old[8]) + { + res |= usb_kbd_translate(new[i], new[0], 1); + old[0] = new[0]; + } + } + if(new[0] != old[0]) /* modifier change */ + { + unsigned char modifier_change = new[0] ^ old[0]; + if(modifier_change & (1 << LEFT_CNTR)) + res |= usb_kbd_translate(0x88, new[0], (new[0] & (1 << LEFT_CNTR)) ? 1 : 0); + if(modifier_change & (1 << LEFT_SHIFT)) + res |= usb_kbd_translate(0x89, new[0], (new[0] & (1 << LEFT_SHIFT)) ? 1 : 0); + if(modifier_change & (1 << LEFT_ALT)) + res |= usb_kbd_translate(0x8A, new[0], (new[0] & (1 << LEFT_ALT)) ? 1 : 0); + if(modifier_change & (1 << LEFT_GUI)) + res |= usb_kbd_translate(0x8B, new[0], (new[0] & (1 << LEFT_GUI)) ? 1 : 0); + if(modifier_change & (1 << RIGHT_CNTR)) + res |= usb_kbd_translate(0x8C, new[0], (new[0] & (1 << RIGHT_CNTR)) ? 1 : 0); + if(modifier_change & (1 << RIGHT_SHIFT)) + res |= usb_kbd_translate(0x8D, new[0], (new[0] & (1 << RIGHT_SHIFT)) ? 1 : 0); + if(modifier_change & (1 << RIGHT_ALT)) + res |= usb_kbd_translate(0x8E, new[0], (new[0] & (1 << RIGHT_ALT)) ? 1 : 0); + if(modifier_change & (1 << RIGHT_GUI)) + res |= usb_kbd_translate(0x8F, new[0], (new[0] & (1 << RIGHT_GUI)) ? 1 : 0); + } + if(res == 1) + usb_kbd_setled(dev); + memcpy(&old[0], &new[0], 8); + return 1; /* install IRQ Handler again */ +} + +/* probes the USB device dev for keyboard type */ +static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int pipe,maxp; + if(dev->descriptor.bNumConfigurations != 1) + return 0; + iface = &dev->config.if_desc[ifnum]; + if(iface->bInterfaceClass != 3) + return 0; + if(iface->bInterfaceSubClass != 1) + return 0; + if(iface->bInterfaceProtocol != 1) + return 0; + if(iface->bNumEndpoints != 1) + return 0; + ep = &iface->ep_desc[0]; + if(!(ep->bEndpointAddress & 0x80)) + return 0; + if((ep->bmAttributes & 3) != 3) + return 0; + leds = (unsigned char *)usb_malloc(8); + if(leds == NULL) + return 0; + new = (unsigned char *)usb_malloc(8); + if(new == NULL) + { + usb_free(leds); + new = NULL; + return 0; + } + USB_KBD_PRINTF("USB KBD found set protocol...\r\n"); + /* ok, we found a USB Keyboard, install it */ +#ifdef USE_COUNTRYCODE + if(usb_kbd_get_hid_desc(dev) < 0) + usb_kbd_hid_desc.bCountryCode = CC_NOT; +#endif + usb_set_protocol(dev, iface->bInterfaceNumber, 0); + USB_KBD_PRINTF("USB KBD found set idle...\r\n"); + usb_set_idle(dev, iface->bInterfaceNumber, REPEAT_RATE, 0); + memset(&new[0], 0, 8); + memset(&old[0], 0, 8); + pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe); + dev->irq_handle = usb_kbd_irq; + USB_KBD_PRINTF("USB KBD enable interrupt pipe (maxp: %d)...\r\n", maxp); + usb_submit_int_msg(dev, pipe, &new[0], maxp > 8 ? 8 : maxp, ep->bInterval); + return 1; +} + +#ifdef USE_COUNTRYCODE + +/* + * We parse each description item into this structure. Short items data + * values are expanded to 32-bit signed int, long items contain a pointer + * into the data area. + */ + +struct hid_item { + unsigned char format; + unsigned char size; + unsigned char type; + unsigned char tag; + union { + unsigned char u_8; + char s_8; + unsigned short u_16; + short s_16; + unsigned long u_32; + long s_32; + unsigned char *longdata; + } data; +}; + +/* HID report item format */ +#define HID_ITEM_FORMAT_SHORT 0 +#define HID_ITEM_FORMAT_LONG 1 + +/* Special tag indicating long items */ +#define HID_ITEM_TAG_LONG 15 + +#ifdef USB_KBD_DEBUG + +void usb_kbd_display_hid(struct usb_hid_descriptor *hid) +{ + board_printf("USB_HID_DESC:\r\n"); + board_printf(" bLenght 0x%x\r\n",hid->bLength); + board_printf(" bcdHID 0x%x\r\n",hid->bcdHID); + board_printf(" bCountryCode %d\r\n",hid->bCountryCode); + board_printf(" bNumDescriptors 0x%x\r\n",hid->bNumDescriptors); + board_printf(" bReportDescriptorType 0x%x\r\n",hid->bReportDescriptorType); + board_printf(" wDescriptorLength 0x%x\r\n",hid->wDescriptorLength); +} + +/* + * Fetch a report description item from the data stream. We support long + * items, though they are not used yet. + */ + +static int fetch_item(unsigned char *start, unsigned char *end, struct hid_item *item) +{ + if((end - start) > 0) + { + unsigned char b = *start++; + item->type = (b >> 2) & 3; + item->tag = (b >> 4) & 15; + if(item->tag == HID_ITEM_TAG_LONG) + { + item->format = HID_ITEM_FORMAT_LONG; + if((end - start) >= 2) + { + item->size = *start++; + item->tag = *start++; + if((end - start) >= item->size) + { + item->data.longdata = start; + start += item->size; + return item->size; + } + } + } + else + { + item->format = HID_ITEM_FORMAT_SHORT; + item->size = b & 3; + switch(item->size) + { + case 0: + return item->size; + case 1: + if((end - start) >= 1) + { + item->data.u_8 = *start++; + return item->size; + } + break; + case 2: + if((end - start) >= 2) + { + item->data.u_16 = le16_to_cpu(*(unsigned short *)start); + start+=2; + return item->size; + } + case 3: + item->size++; + if((end - start) >= 4) + { + item->data.u_32 = le32_to_cpu(*(unsigned long *)start); + start+=4; + return item->size; + } + } + } + } + return -1; +} + +#endif /* USB_KBD_DEBUG */ + +/* + * HID report descriptor item type (prefix bit 2,3) + */ + +#define HID_ITEM_TYPE_MAIN 0 +#define HID_ITEM_TYPE_GLOBAL 1 +#define HID_ITEM_TYPE_LOCAL 2 +#define HID_ITEM_TYPE_RESERVED 3 +/* + * HID report descriptor main item tags + */ + +#define HID_MAIN_ITEM_TAG_INPUT 8 +#define HID_MAIN_ITEM_TAG_OUTPUT 9 +#define HID_MAIN_ITEM_TAG_FEATURE 11 +#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10 +#define HID_MAIN_ITEM_TAG_END_COLLECTION 12 +/* + * HID report descriptor main item contents + */ + +#define HID_MAIN_ITEM_CONSTANT 0x001 +#define HID_MAIN_ITEM_VARIABLE 0x002 +#define HID_MAIN_ITEM_RELATIVE 0x004 +#define HID_MAIN_ITEM_WRAP 0x008 +#define HID_MAIN_ITEM_NONLINEAR 0x010 +#define HID_MAIN_ITEM_NO_PREFERRED 0x020 +#define HID_MAIN_ITEM_NULL_STATE 0x040 +#define HID_MAIN_ITEM_VOLATILE 0x080 +#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100 + +/* + * HID report descriptor collection item types + */ + +#define HID_COLLECTION_PHYSICAL 0 +#define HID_COLLECTION_APPLICATION 1 +#define HID_COLLECTION_LOGICAL 2 +/* + * HID report descriptor global item tags + */ + +#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4 +#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5 +#define HID_GLOBAL_ITEM_TAG_UNIT 6 +#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7 +#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8 +#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9 +#define HID_GLOBAL_ITEM_TAG_PUSH 10 +#define HID_GLOBAL_ITEM_TAG_POP 11 + +/* + * HID report descriptor local item tags + */ + +#define HID_LOCAL_ITEM_TAG_USAGE 0 +#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1 +#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5 +#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7 +#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8 +#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9 +#define HID_LOCAL_ITEM_TAG_DELIMITER 10 + +#ifdef USB_KBD_DEBUG +static void usb_kbd_show_item(struct hid_item *item) +{ + switch(item->type) + { + case HID_ITEM_TYPE_MAIN: + switch(item->tag) + { + case HID_MAIN_ITEM_TAG_INPUT: board_printf("Main Input"); break; + case HID_MAIN_ITEM_TAG_OUTPUT: board_printf("Main Output"); break; + case HID_MAIN_ITEM_TAG_FEATURE: board_printf("Main Feature"); break; + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: board_printf("Main Begin Collection"); break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: board_printf("Main End Collection"); break; + default: board_printf("Main reserved %d",item->tag); break; + } + break; + case HID_ITEM_TYPE_GLOBAL: + switch(item->tag) + { + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: board_printf("- Global Usage Page"); break; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: board_printf("- Global Logical Minimum"); break; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: board_printf("- Global Logical Maximum"); break; + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: board_printf("- Global physical Minimum"); break; + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: board_printf("- Global physical Maximum"); break; + case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: board_printf("- Global Unit Exponent"); break; + case HID_GLOBAL_ITEM_TAG_UNIT: board_printf("- Global Unit"); break; + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: board_printf("- Global Report Size"); break; + case HID_GLOBAL_ITEM_TAG_REPORT_ID: board_printf("- Global Report ID"); break; + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: board_printf("- Global Report Count"); break; + case HID_GLOBAL_ITEM_TAG_PUSH: board_printf("- Global Push"); break; + case HID_GLOBAL_ITEM_TAG_POP: board_printf("- Global Pop"); break; + default: board_printf("- Global reserved %d",item->tag); break; + } + break; + case HID_ITEM_TYPE_LOCAL: + switch(item->tag) + { + case HID_LOCAL_ITEM_TAG_USAGE: board_printf("-- Local Usage"); break; + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: board_printf("-- Local Usage Minimum"); break; + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: board_printf("-- Local Usage Maximum"); break; + case HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX: board_printf("-- Local Designator Index"); break; + case HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM: board_printf("-- Local Designator Minimum"); break; + case HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM: board_printf("-- Local Designator Maximum"); break; + case HID_LOCAL_ITEM_TAG_STRING_INDEX: board_printf("-- Local String Index"); break; + case HID_LOCAL_ITEM_TAG_STRING_MINIMUM: board_printf("-- Local String Minimum"); break; + case HID_LOCAL_ITEM_TAG_STRING_MAXIMUM: board_printf("-- Local String Maximum"); break; + case HID_LOCAL_ITEM_TAG_DELIMITER: board_printf("-- Local Delimiter"); break; + default: board_printf("-- Local reserved %d",item->tag); break; + } + break; + default: + board_printf("--- reserved %d",item->type); + break; + } + board_printf(" "); + switch(item->size) + { + case 1: board_printf("%d",item->data.u_8); break; + case 2: board_printf("%d",item->data.u_16); break; + case 4: board_printf("%ld",item->data.u_32); break; + } + board_printf("\r\n"); +} +#endif /* USB_KBD_DEBUG */ + +static int usb_kbd_get_hid_desc(struct usb_device *dev) +{ + unsigned char *buffer = (unsigned char *)usb_malloc(256); + struct usb_descriptor_header *head; + struct usb_config_descriptor *config; + int index, len; +#ifdef USB_KBD_DEBUG + int i; + unsigned char *start, *end; + struct hid_item item; +#endif + if(buffer == NULL) + return -1; + if(usb_get_configuration_no(dev, &buffer[0], 0) == -1) + { + usb_free(buffer); + return -1; + } + head = (struct usb_descriptor_header *)&buffer[0]; + if(head->bDescriptorType!=USB_DT_CONFIG) + { + USB_KBD_PRINTF(" ERROR: NOT USB_CONFIG_DESC %x\r\n",head->bDescriptorType); + usb_free(buffer); + return -1; + } + index = head->bLength; + config = (struct usb_config_descriptor *)&buffer[0]; + len = le16_to_cpu(config->wTotalLength); + /* Ok the first entry must be a configuration entry, now process the others */ + head = (struct usb_descriptor_header *)&buffer[index]; + while(index+1 < len) + { + if(head->bDescriptorType == USB_DT_HID) + { + USB_KBD_PRINTF("HID desc found\r\n"); + memcpy(&usb_kbd_hid_desc, &buffer[index],buffer[index]); + le16_to_cpus(&usb_kbd_hid_desc.bcdHID); + le16_to_cpus(&usb_kbd_hid_desc.wDescriptorLength); +#ifdef USB_KBD_DEBUG + usb_kbd_display_hid(&usb_kbd_hid_desc); +#endif + len = 0; + break; + } + index += head->bLength; + head = (struct usb_descriptor_header *)&buffer[index]; + } + if(len > 0) + { + usb_free(buffer); + return -1; + } +#ifdef USB_KBD_DEBUG + len = usb_kbd_hid_desc.wDescriptorLength; + if((index = usb_get_class_descriptor(dev, 0, USB_DT_REPORT, 0, &buffer[0], len)) < 0) + { + USB_KBD_PRINTF("reading report descriptor failed\r\n"); + usb_free(buffer); + return -1; + } + USB_KBD_PRINTF(" report descriptor (size %u, read %d)\r\n", len, index); + start = &buffer[0]; + end = &buffer[len]; + i = 0; + do + { + index = fetch_item(start, end, &item); + i += index; + i++; + if(index >= 0) + usb_kbd_show_item(&item); + start += index; + start++; + } + while(index >= 0); +#endif /* USB_KBD_DEBUG */ + usb_free(buffer); + return 0; +} + +#endif /* USE_COUNTRYCODE */ + +/* + usb_get_report(dev, 0, 0, 1, &new[0], 8); +*/ + +#endif /* CONFIG_USB_KEYBOARD */ +#endif /* CONFIG_USB_UHCI || CONFIG_USB_OHCI || CONFIG_USB_EHCI */ + diff --git a/i2cspi_BaS_gcc/sources/usb_mem.c b/i2cspi_BaS_gcc/sources/usb_mem.c new file mode 100644 index 0000000..4431b39 --- /dev/null +++ b/i2cspi_BaS_gcc/sources/usb_mem.c @@ -0,0 +1,272 @@ +/* + * usb_mem.c + * + * based from Emutos / BDOS + * + * Copyright (c) 2001 Lineo, Inc. + * + * Authors: Karl T. Braun, Martin Doering, Laurent Vogel + * + * This file is distributed under the GPL, version 2 or at your + * option any later version. + */ + +#include "config.h" +#include +#include +#include +#include "usb.h" + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#undef USB_MEM_DEBUG + +#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined(CONFIG_USB_EHCI) + +#ifdef USB_MEM_DEBUG +#define USB_MEM_PRINTF(fmt,args...) board_printf(fmt ,##args) +#else +#define USB_MEM_PRINTF(fmt,args...) +#endif + +#ifdef CONFIG_USB_MEM_NO_CACHE + +extern char __NO_CACHE_MEMORY_BASE[]; +extern char __NO_CACHE_MEMORY_SIZE[]; + +#endif /* CONFIG_USB_MEM_NO_CACHE */ + +static void *usb_buffer; +extern int asm_set_ipl(int level); +extern void *info_fvdi; +extern long offscren_reserved(void); + +/* MD - Memory Descriptor */ + +#define MD struct _md_ + +MD +{ + MD *m_link; + long m_start; + long m_length; +}; + +/* MPB - Memory Partition Block */ + +#define MPB struct _mpb + +MPB +{ + MD *mp_mfl; + MD *mp_mal; + MD *mp_rover; +}; + +#define MAXMD 100 + +static int count_md; +static MD tab_md[MAXMD]; +static MPB pmd; + +static MD *ffit(long amount, MPB *mp) +{ + MD *p,*q,*p1; /* free list is composed of MD's */ + int maxflg; + long maxval; + if(amount != -1) + { + amount += 15; /* 16 bytes alignment */ + amount &= 0xFFFFFFF0; + } + if((q = mp->mp_rover) == 0) /* get rotating pointer */ + return(0) ; + maxval = 0; + maxflg = (amount == -1 ? TRUE : FALSE) ; + p = q->m_link; /* start with next MD */ + do /* search the list for an MD with enough space */ + { + if(p == 0) + { + /* at end of list, wrap back to start */ + q = (MD *) &mp->mp_mfl; /* q => mfl field */ + p = q->m_link; /* p => 1st MD */ + } + if((!maxflg) && (p->m_length >= amount)) + { + /* big enough */ + if(p->m_length == amount) + q->m_link = p->m_link; /* take the whole thing */ + else + { + /* break it up - 1st allocate a new + MD to describe the remainder */ + if(count_md >= MAXMD) + return(0); + p1 = &tab_md[count_md++]; + /* init new MD */ + p1->m_length = p->m_length - amount; + p1->m_start = p->m_start + amount; + p1->m_link = p->m_link; + p->m_length = amount; /* adjust allocated block */ + q->m_link = p1; + } + /* link allocate block into allocated list, + mark owner of block, & adjust rover */ + p->m_link = mp->mp_mal; + mp->mp_mal = p; + mp->mp_rover = (q == (MD *) &mp->mp_mfl ? q->m_link : q); + return(p); /* got some */ + } + else if(p->m_length > maxval) + maxval = p->m_length; + p = ( q=p )->m_link; + } + while(q != mp->mp_rover); + /* return either the max, or 0 (error) */ + if(maxflg) + { + maxval -= 15; /* 16 bytes alignment */ + if(maxval < 0) + maxval = 0; + else + maxval &= 0xFFFFFFF0; + } + return(maxflg ? (MD *) maxval : 0); +} + +static void freeit(MD *m, MPB *mp) +{ + MD *p, *q; + q = 0; + for(p = mp->mp_mfl; p ; p = (q=p) -> m_link) + { + if(m->m_start <= p->m_start) + break; + } + m->m_link = p; + if(q) + q->m_link = m; + else + mp->mp_mfl = m; + if(!mp->mp_rover) + mp->mp_rover = m; + if(p) + { + if(m->m_start + m->m_length == p->m_start) + { /* join to higher neighbor */ + m->m_length += p->m_length; + m->m_link = p->m_link; + if(p == mp->mp_rover) + mp->mp_rover = m; + if(count_md>=0) + count_md--; + } + } + if(q) + { + if(q->m_start + q->m_length == m->m_start) + { /* join to lower neighbor */ + q->m_length += m->m_length; + q->m_link = m->m_link; + if(m == mp->mp_rover) + mp->mp_rover = q; + if(count_md>=0) + count_md--; + } + } +} + +int usb_free(void *addr) +{ + int level; + MD *p, **q; + MPB *mpb; + mpb = &pmd; + if(usb_buffer == NULL) + return(EFAULT); + level = asm_set_ipl(7); + for(p = *(q = &mpb->mp_mal); p; p = *(q = &p->m_link)) + { + if((long)addr == p->m_start) + break; + } + if(!p) + { + asm_set_ipl(level); + return(EFAULT); + } + *q = p->m_link; + freeit(p, mpb); + asm_set_ipl(level); + USB_MEM_PRINTF("usb_free(0x%08X)\r\n", addr); + return(0); +} + +void *usb_malloc(long amount) +{ + int level; + MD *m; + if(usb_buffer == NULL) + return(NULL); + if(amount == -1L) + return((void *)ffit(-1L,&pmd)); + if(amount <= 0 ) + return(0); + if((amount & 1)) + amount++; + level = asm_set_ipl(7); + m = ffit(amount, &pmd); + asm_set_ipl(level); + USB_MEM_PRINTF("usb_malloc(%d) = 0x%08X\r\n", amount, (m == NULL) ? 0 : m->m_start); + if(m == NULL) + return(NULL); + return((void *)m->m_start); +} + +int usb_mem_init(void) +{ + if(usb_buffer != NULL) + return(0); +#ifdef CONFIG_USB_MEM_NO_CACHE + pmd.mp_mfl = pmd.mp_rover = &tab_md[0]; + tab_md[0].m_link = (MD *)NULL; + usb_buffer = (void *)__NO_CACHE_MEMORY_BASE; + tab_md[0].m_start = (long)usb_buffer; + tab_md[0].m_length = (long)__NO_CACHE_MEMORY_SIZE; +#else /* !CONFIG_USB_MEM_NO_CACHE */ +#if 0 + usb_buffer = (void *)offscren_reserved(); + if(usb_buffer == NULL) +#endif + usb_buffer = (void *)Mxalloc(USB_BUFFER_SIZE + 16, 0); /* STRAM - cache in writethough */ + if(usb_buffer == NULL) + return(-1); + pmd.mp_mfl = pmd.mp_rover = &tab_md[0]; + tab_md[0].m_link = (MD *)NULL; + tab_md[0].m_start = ((long)usb_buffer + 15) & ~15; + tab_md[0].m_length = USB_BUFFER_SIZE; +#endif /* CONFIG_USB_MEM_NO_CACHE */ + pmd.mp_mal = (MD *)NULL; + memset(usb_buffer, 0, tab_md[0].m_length); + USB_MEM_PRINTF("USB malloc buffer at 0x%08X size %d\r\n", tab_md[0].m_start, tab_md[0].m_length); + count_md = 1; + return(0); +} + +void usb_mem_stop(void) +{ +#ifndef CONFIG_USB_MEM_NO_CACHE + if(usb_buffer != NULL) + Mfree(usb_buffer); +#endif +} + +#endif /* CONFIG_USB_UHCI || CONFIG_USB_OHCI || CONFIG_USB_EHCI */ + + diff --git a/i2cspi_BaS_gcc/sources/usb_mouse.c b/i2cspi_BaS_gcc/sources/usb_mouse.c new file mode 100644 index 0000000..a722a18 --- /dev/null +++ b/i2cspi_BaS_gcc/sources/usb_mouse.c @@ -0,0 +1,244 @@ +/* + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include "config.h" +#include "usb.h" + +#undef USB_MOUSE_DEBUG + +#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined(CONFIG_USB_EHCI) +#ifdef CONFIG_USB_MOUSE + +#ifdef USB_MOUSE_DEBUG +#define USB_MOUSE_PRINTF(fmt,args...) board_printf(fmt ,##args) +#else +#define USB_MOUSE_PRINTF(fmt,args...) +#endif + +extern void ltoa(char *buf, long n, unsigned long base); +extern void call_mousevec(unsigned char *data, void (**mousevec)(void *)); +extern void call_ikbdvec(unsigned char code, _IOREC *iorec, void (**ikbdvec)()); +extern int asm_set_ipl(int level); + +static unsigned char *new; +static unsigned char old[8]; +static int mouse_installed; + +extern void (**mousevec)(void *); +extern _IOREC *iorec; +extern void (**ikbdvec)(); + +/* forward declaration */ +static int usb_mouse_probe(struct usb_device *dev, unsigned int ifnum); + +/* deregistering the mouse */ +int usb_mouse_deregister(struct usb_device *dev) +{ + dev->irq_handle = NULL; + if(new != NULL) + { + usb_free(new); + new = NULL; + } + mouse_installed = 0; + USB_MOUSE_PRINTF("USB MOUSE deregister\r\n"); + return 1; +} + +/* registering the mouse */ +int usb_mouse_register(struct usb_device *dev) +{ + if(!mouse_installed && (dev->devnum != -1) && (usb_mouse_probe(dev, 0) == 1)) + { /* Ok, we found a mouse */ + USB_MOUSE_PRINTF("USB MOUSE found (USB: %d, devnum: %d)\r\n", dev->usbnum, dev->devnum); + mouse_installed = 1; + dev->deregister = usb_mouse_deregister; + return 1; + } + /* no USB Mouse found */ + return -1; +} + +/* search for mouse and register it if found */ +int drv_usb_mouse_init(void) +{ + int i, j; + if(mouse_installed) + return -1; + /* scan all USB Devices */ + for(j = 0; j < USB_MAX_BUS; j++) + { + for(i = 0; i < USB_MAX_DEVICE; i++) + { + struct usb_device *dev = usb_get_dev_index(i, j); /* get device */ + if(dev == NULL) + break; + if(usb_mouse_register(dev) > 0) + return 1; + } + } + /* no USB Mouse found */ + return -1; +} + +/************************************************************************** + * Low Level drivers + */ +static void usb_kbd_send_code(unsigned char code) +{ + if((iorec != NULL) && (ikbdvec != NULL)) + call_ikbdvec(code, iorec, ikbdvec); +} + +/* Interrupt service routine */ +static int usb_mouse_irq(struct usb_device *dev) +{ +#ifdef CONFIG_USB_INTERRUPT_POLLING + int level; +#endif + int i, change = 0; + if((dev->irq_status != 0) || (dev->irq_act_len < 3) || (dev->irq_act_len > 8)) + { + USB_MOUSE_PRINTF("USB MOUSE error %lX, len %d\r\n", dev->irq_status, dev->irq_act_len); + return 1; + } + for(i = 0; i < dev->irq_act_len; i++) + { + if(new[i] != old[i]) + { + change = 1; + break; + } + } + if(change) + { + char wheel = 0, buttons, old_buttons; + USB_MOUSE_PRINTF("USB MOUSE len:%d %02X %02X %02X %02X %02X %02X\r\n", dev->irq_act_len, new[0], new[1], new[2], new[3], new[4], new[5]); +#ifdef CONFIG_USB_INTERRUPT_POLLING + level = asm_set_ipl(7); /* mask interrupts */ +#endif + if((dev->irq_act_len >= 6) && (new[0] == 1)) /* report-ID */ + { + buttons = new[1]; + old_buttons = old[1]; + new[0] = ((new[1] & 1) << 1) + ((new[1] & 2) >> 1) + 0xF8; + new[1] = new[2]; + new[2] = new[3]; + wheel = new[4]; + } + else /* boot report */ + { + buttons = new[0]; + old_buttons = old[0]; + new[0] = ((new[0] & 1) << 1) + ((new[0] & 2) >> 1) + 0xF8; + if(dev->irq_act_len >= 3) + wheel = new[3]; + } + if((buttons ^ old_buttons) & 4) /* 3rd button */ + { + if(buttons & 4) + { + usb_kbd_send_code(0x72); /* ENTER */ + usb_kbd_send_code(0xF2); + } + } + if(wheel != 0) /* actually like Eiffel */ + { +#define REPEAT_WHEEL 3 + int i; + if(wheel > 0) + { + for(i = 0; i < REPEAT_WHEEL; i++) + { + usb_kbd_send_code(0x48); /* UP */ + usb_kbd_send_code(0xC8); + } + } + else + { + for(i = 0; i < REPEAT_WHEEL; i++) + { + usb_kbd_send_code(0x50); /* DOWN */ + usb_kbd_send_code(0xD0); + } + } + } + if(mousevec != NULL) + call_mousevec(new, mousevec); +#ifdef CONFIG_USB_INTERRUPT_POLLING + asm_set_ipl(level); +#endif + old[0] = new[0]; + old[1] = new[1]; + old[2] = new[2]; + old[3] = new[3]; + old[4] = new[4]; + old[5] = new[5]; + } + return 1; /* install IRQ Handler again */ +} + +/* probes the USB device dev for mouse type */ +static int usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *iface; + struct usb_endpoint_descriptor *ep; + int pipe, maxp; + if(dev->descriptor.bNumConfigurations != 1) + return 0; + iface = &dev->config.if_desc[ifnum]; + if(iface->bInterfaceClass != 3) + return 0; + if(iface->bInterfaceSubClass != 1) + return 0; + if(iface->bInterfaceProtocol != 2) + return 0; + if(iface->bNumEndpoints != 1) + return 0; + ep = &iface->ep_desc[0]; + if(!(ep->bEndpointAddress & 0x80)) + return 0; + if((ep->bmAttributes & 3) != 3) + return 0; + new = (unsigned char *)usb_malloc(8); + if(new == NULL) + return 0; + USB_MOUSE_PRINTF("USB MOUSE found set protocol...\r\n"); + /* ok, we found a USB Mouse, install it */ + pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe); +// if(maxp < 6) +// usb_set_protocol(dev, iface->bInterfaceNumber, 0); /* boot */ +// else + usb_set_protocol(dev, iface->bInterfaceNumber, 1); /* report */ + USB_MOUSE_PRINTF("USB MOUSE found set idle...\r\n"); + usb_set_idle(dev, iface->bInterfaceNumber, 0, 0); /* report infinite */ + memset(&new[0], 0, 8); + memset(&old[0], 0, 8); + dev->irq_handle = usb_mouse_irq; + USB_MOUSE_PRINTF("USB MOUSE enable interrupt pipe (maxp: %d)...\r\n", maxp); + usb_submit_int_msg(dev, pipe, &new[0], maxp > 8 ? 8 : maxp, ep->bInterval); + return 1; +} + +#endif /* CONFIG_USB_MOUSE */ +#endif /* CONFIG_USB_UHCI || CONFIG_USB_OHCI || CONFIG_USB_EHCI */ diff --git a/i2cspi_BaS_gcc/sources/usb_storage.c b/i2cspi_BaS_gcc/sources/usb_storage.c new file mode 100644 index 0000000..6200b73 --- /dev/null +++ b/i2cspi_BaS_gcc/sources/usb_storage.c @@ -0,0 +1,1669 @@ +/* + * Most of this source has been derived from the Linux USB + * project: + * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * (c) 2000 Yggdrasil Computing, Inc. + * + * + * Adapted for U-Boot: + * (C) Copyright 2001 Denis Peter, MPL AG Switzerland + * + * For BBB support (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * BBB support based on /sys/dev/usb/umass.c from + * FreeBSD. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +/* Note: + * Currently only the CBI transport protocoll has been implemented, and it + * is only tested with a TEAC USB Floppy. Other Massstorages with CBI or CB + * transport protocoll may work as well. + */ +/* + * New Note: + * Support for USB Mass Storage Devices (BBB) has been added. It has + * only been tested with USB memory sticks. + */ + +#include "config.h" +#include "usb.h" +#include "scsi.h" + +#define CONFIG_USB_WRITE_ENABLE + +#undef USB_STOR_DEBUG +#undef BBB_COMDAT_TRACE +#undef BBB_XPORT_TRACE + +#if defined(CONFIG_USB_UHCI) || defined(CONFIG_USB_OHCI) || defined(CONFIG_USB_EHCI) +#ifdef CONFIG_USB_STORAGE + +#ifdef USB_STOR_DEBUG +#define USB_STOR_PRINTF(fmt, args...) board_printf(fmt , ##args) +#else +#define USB_STOR_PRINTF(fmt, args...) +#endif + +extern void udelay(long usec); +extern long install_usb_stor(int dev_num, unsigned long part_type, unsigned long part_offset, unsigned long part_size, char *vendor, char *revision, char *product); +extern void uninstall_usb_stor(int dev_num); + +/* direction table -- this indicates the direction of the data + * transfer for each command code -- a 1 indicates input + */ +unsigned char us_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1) + +static unsigned char *usb_stor_buf; +static ccb usb_ccb; +static short drive_ok_usb; + +/* + * CBI style + */ + +#define US_CBI_ADSC 0 + +/* + * BULK only + */ +#define US_BBB_RESET 0xff +#define US_BBB_GET_MAX_LUN 0xfe + +/* Command Block Wrapper */ +typedef struct { + __u32 dCBWSignature; +# define CBWSIGNATURE 0x43425355 + __u32 dCBWTag; + __u32 dCBWDataTransferLength; + __u8 bCBWFlags; +# define CBWFLAGS_OUT 0x00 +# define CBWFLAGS_IN 0x80 + __u8 bCBWLUN; + __u8 bCDBLength; +# define CBWCDBLENGTH 16 + __u8 CBWCDB[CBWCDBLENGTH]; +} umass_bbb_cbw_t; +#define UMASS_BBB_CBW_SIZE 31 +static __u32 CBWTag; + +/* Command Status Wrapper */ +typedef struct { + __u32 dCSWSignature; +# define CSWSIGNATURE 0x53425355 + __u32 dCSWTag; + __u32 dCSWDataResidue; + __u8 bCSWStatus; +# define CSWSTATUS_GOOD 0x0 +# define CSWSTATUS_FAILED 0x1 +# define CSWSTATUS_PHASE 0x2 +} umass_bbb_csw_t; +#define UMASS_BBB_CSW_SIZE 13 + +#define USB_MAX_STOR_DEV 5 +static int usb_max_devs; /* number of highest available usb device */ + +static block_dev_desc_t usb_dev_desc[USB_MAX_STOR_DEV]; + +struct us_data; +typedef int (*trans_cmnd)(ccb *cb, struct us_data *data); +typedef int (*trans_reset)(struct us_data *data); + +struct us_data { + struct usb_device *pusb_dev; /* this usb_device */ + + unsigned int flags; /* from filter initially */ + unsigned char ifnum; /* interface number */ + unsigned char ep_in; /* in endpoint */ + unsigned char ep_out; /* out ....... */ + unsigned char ep_int; /* interrupt . */ + unsigned char subclass; /* as in overview */ + unsigned char protocol; /* .............. */ + unsigned char attention_done; /* force attn on first cmd */ + unsigned short ip_data; /* interrupt data */ + int action; /* what to do */ + int ip_wanted; /* needed */ + int *irq_handle; /* for USB int requests */ + unsigned int irqpipe; /* pipe for release_irq */ + unsigned char irqmaxp; /* max packed for irq Pipe */ + unsigned char irqinterval; /* Intervall for IRQ Pipe */ + ccb *srb; /* current srb */ + trans_reset transport_reset; /* reset routine */ + trans_cmnd transport; /* transport routine */ +}; + +static struct us_data usb_stor[USB_MAX_STOR_DEV]; + +#define USB_STOR_TRANSPORT_GOOD 0 +#define USB_STOR_TRANSPORT_FAILED -1 +#define USB_STOR_TRANSPORT_ERROR -2 + +#define DEFAULT_SECTOR_SIZE 512 +#define GEMDOS_PART_TBL_OFFSET 0x1c6 +#define DOS_PART_TBL_OFFSET 0x1be +#define DOS_PART_MAGIC_OFFSET 0x1fe +#define DOS_PBR_FSTYPE_OFFSET 0x36 +#define DOS_PBR_MEDIA_TYPE_OFFSET 0x15 +#define DOS_MBR 0 +#define DOS_PBR 1 +#define DOS_FS_TYPE_OFFSET 0x36 + +typedef struct gemdos_partition { + unsigned char boot_ind; /* 0x01 - active */ + unsigned char id[3]; /* partition type */ + unsigned char start4[4]; /* starting sector counting from 0 */ + unsigned char size4[4]; /* nr of sectors in partition */ +} gemdos_partition_t; + +typedef struct dos_partition { + unsigned char boot_ind; /* 0x80 - active */ + unsigned char head; /* starting head */ + unsigned char sector; /* starting sector */ + unsigned char cyl; /* starting cylinder */ + unsigned char sys_ind; /* What partition type */ + unsigned char end_head; /* end head */ + unsigned char end_sector; /* end sector */ + unsigned char end_cyl; /* end cylinder */ + unsigned char start4[4]; /* starting sector counting from 0 */ + unsigned char size4[4]; /* nr of sectors in partition */ +} dos_partition_t; + +typedef struct disk_partition { + unsigned long type; + unsigned long start; /* # of first block in partition */ + unsigned long size; /* number of blocks in partition */ + unsigned long blksz; /* block size in bytes */ +} disk_partition_t; + +extern unsigned long swap_long(unsigned long val); +#define le32_to_int(a) (int)swap_long(*(unsigned long *)a) + +int usb_stor_get_info(struct usb_device *dev, struct us_data *us, block_dev_desc_t *dev_desc); +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, struct us_data *ss); +long usb_stor_read(int device, unsigned long blknr, unsigned long blkcnt, void *buffer); +long usb_stor_write(int device, unsigned long blknr, unsigned long blkcnt, const void *buffer); +void uhci_show_temp_int_td(void); + +block_dev_desc_t *usb_stor_get_dev(int index) +{ + return(index < USB_MAX_STOR_DEV) ? &usb_dev_desc[index] : NULL; +} + +void init_part(block_dev_desc_t *dev_desc) +{ + int i; + unsigned char *buffer = (unsigned char *)usb_malloc(DEFAULT_SECTOR_SIZE); + unsigned short *p = (unsigned short *)buffer; + unsigned short sum; + if(buffer == NULL) + return; + if(dev_desc->block_read(dev_desc->dev, 0, 1, (unsigned long *)buffer) != 1) + { + usb_free(buffer); + return; + } +#ifdef USB_STOR_DEBUG + for(i = 0; i < DEFAULT_SECTOR_SIZE; i++) + { + if((i & 15) == 0) + board_printf("\r\n%04X ", i); + board_printf("%02X ", buffer[i]); + if((i & 15) == 15) + { + int k; + for(k = i-15; k <= i; k++) + { + if(buffer[k] < ' ' || buffer[k] >= 127) + board_printf("."); + else + board_printf("%c", buffer[k]); + } + } + } + board_printf("\r\n"); +#endif + sum = 0; + for(i = 0; i < DEFAULT_SECTOR_SIZE/2; i++) + sum += *p++; + if(sum == 0x1234) + { + dev_desc->part_type = PART_TYPE_GEMDOS; + USB_STOR_PRINTF("GEMDOS partition table found\r\n"); + } + else if((buffer[DOS_PART_MAGIC_OFFSET] == 0x55) && (buffer[DOS_PART_MAGIC_OFFSET + 1] == 0xaa)) + { + dev_desc->part_type = PART_TYPE_DOS; + USB_STOR_PRINTF("DOS partition table found\r\n"); + } + usb_free(buffer); +} + +static inline int is_extended(int part_type) +{ + return(part_type == 0x5 || part_type == 0xf || part_type == 0x85); +} + +/* Print a partition that is relative to its Extended partition table + */ +static int get_partition_info_extended(block_dev_desc_t *dev_desc, int ext_part_sector, int relative, int part_num, int which_part, disk_partition_t *info) +{ + int i; + gemdos_partition_t *gpt; + dos_partition_t *pt; + unsigned char *buffer = (unsigned char *)usb_malloc(DEFAULT_SECTOR_SIZE); + if(buffer == NULL) + return -1; + if(dev_desc->block_read(dev_desc->dev, ext_part_sector, 1, (unsigned long *)buffer) != 1) + { + USB_STOR_PRINTF("Can't read partition table on %d:%d\r\n", dev_desc->dev, ext_part_sector); + usb_free(buffer); + return -1; + } + switch(dev_desc->part_type) + { + case PART_TYPE_GEMDOS: + gpt = (gemdos_partition_t *)(buffer + GEMDOS_PART_TBL_OFFSET); + for(i = 0; i < (relative ? 2 : 4); i++, gpt++) + { + unsigned long type = ((unsigned long)gpt->boot_ind << 24) + ((unsigned long)gpt->id[0] << 16) + ((unsigned long)gpt->id[1] << 8) + (unsigned long)gpt->id[2]; + if((type & 0xffffff) == 0x58474D) // XGM + { + int lba_start = (int)*(unsigned long *)gpt->start4; + usb_free(buffer); + return get_partition_info_extended(dev_desc, lba_start, lba_start, part_num, which_part, info); + } + if(((type & 0xffffff) != 0) && (part_num == which_part)) + { + info->type = type; + info->blksz = 512; + info->start = *(unsigned long *)gpt->start4; + info->size = *(unsigned long *)gpt->size4; + USB_STOR_PRINTF("GEMDOS partition at offset 0x%x, size 0x%x, type 0x%x\r\n", info->start, info->size, info->type & 0xffffff); + usb_free(buffer); + return 0; + } + } + break; + case PART_TYPE_DOS: + pt = (dos_partition_t *)(buffer + DOS_PART_TBL_OFFSET); + for(i = 0; i < 4; i++, pt++) + { + /* fdisk does not show the extended partitions that are not in the MBR */ + if((pt->sys_ind != 0) && (part_num == which_part) && (is_extended(pt->sys_ind) == 0)) + { + info->type = (unsigned long)pt->sys_ind; + info->blksz = 512; + info->start = ext_part_sector + le32_to_int(pt->start4); + info->size = le32_to_int(pt->size4); + USB_STOR_PRINTF("DOS partition at offset 0x%x, size 0x%x, type 0x%x %s\r\n", info->start, info->size, pt->sys_ind, (is_extended(pt->sys_ind) ? " Extd" : "")); + usb_free(buffer); + return 0; + } + /* Reverse engr the fdisk part# assignment rule! */ + if((ext_part_sector == 0) || (pt->sys_ind != 0 && !is_extended (pt->sys_ind))) + part_num++; + } + /* Follows the extended partitions */ + pt = (dos_partition_t *)(buffer + DOS_PART_TBL_OFFSET); + for(i = 0; i < 4; i++, pt++) + { + if(is_extended(pt->sys_ind)) + { + int lba_start = le32_to_int(pt->start4) + relative; + usb_free(buffer); + return get_partition_info_extended(dev_desc, lba_start, ext_part_sector == 0 ? lba_start : relative, part_num, which_part, info); + } + } + break; + } + usb_free(buffer); + return -1; +} + +int fat_register_device(block_dev_desc_t *dev_desc, int part_no, unsigned long *part_type, unsigned long *part_offset, unsigned long *part_size) +{ + unsigned char *buffer; + disk_partition_t info; + if(!dev_desc->block_read) + return -1; + buffer = (unsigned char *)usb_malloc(DEFAULT_SECTOR_SIZE); + if(buffer == NULL) + return -1; + /* check if we have a MBR (on floppies we have only a PBR) */ + if(dev_desc->block_read(dev_desc->dev, 0, 1, (unsigned long *)buffer) != 1) + { + USB_STOR_PRINTF("Can't read from device %d\r\n", dev_desc->dev); + usb_free(buffer); + return -1; + } + if(buffer[DOS_PART_MAGIC_OFFSET] != 0x55 || buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) + { + /* no signature found */ + usb_free(buffer); + return -1; + } + /* First we assume, there is a MBR */ + if(!get_partition_info_extended(dev_desc, 0, 0, 1, part_no, &info)) + { + *part_type = info.type; + *part_offset = info.start; + *part_size = info.size; + } + else if(!strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3)) + { + /* ok, we assume we are on a PBR only */ + *part_type = 0; + *part_offset = 0; + *part_size = 0; + } + else + { + USB_STOR_PRINTF("Partition %d not valid on device %d\r\n", part_no, dev_desc->dev); + usb_free(buffer); + return -1; + } + usb_free(buffer); + return 0; +} + +void dev_print(block_dev_desc_t *dev_desc) +{ +#ifdef CONFIG_LBA48 + uint64_t lba512; /* number of blocks if 512bytes block size */ +#else + lbaint_t lba512; +#endif + if(dev_desc->type == DEV_TYPE_UNKNOWN) + { + board_printf("not available\r\n"); + return; + } + board_printf("Vendor: %s Rev: %s Prod: %s\r\n", dev_desc->vendor, dev_desc->revision, dev_desc->product); + board_printf("\r\n"); + if((dev_desc->lba * dev_desc->blksz) > 0L) + { + unsigned long mb, mb_quot, mb_rem, gb, gb_quot, gb_rem; + lbaint_t lba = dev_desc->lba; + lba512 = (lba * (dev_desc->blksz / 512)); + mb = (10 * lba512) / 2048; /* 2048 = (1024 * 1024) / 512 MB */ + /* round to 1 digit */ + mb_quot = mb / 10; + mb_rem = mb - (10 * mb_quot); + gb = mb / 1024; + gb_quot = gb / 10; + gb_rem = gb - (10 * gb_quot); +#ifdef CONFIG_LBA48 + if(dev_desc->lba48) + board_printf("Supports 48-bit addressing\r\n"); +#endif + board_printf("Capacity: %ld.%ld MB = %ld.%ld GB (%ld x %ld)\r\n", mb_quot, mb_rem, gb_quot, gb_rem, (unsigned long)lba, dev_desc->blksz); + } + else + board_printf("Capacity: not available\r\n"); +} + +/******************************************************************************* + * show info on storage devices; 'usb start/init' must be invoked earlier + * as we only retrieve structures populated during devices initialization + */ +int usb_stor_info(void) +{ + int i; + if(usb_max_devs > 0) + { + for(i = 0; i < usb_max_devs; i++) + { + board_printf("Device %d: ", i); + dev_print(&usb_dev_desc[i]); + } + return 0; + } + board_printf("No storage devices\r\n"); + return 1; +} + +/******************************************************************************* + * scan the usb and reports device info to the user + * returns current device or -1 if no + */ +int usb_stor_scan(void) +{ + int i, j; + struct usb_device *dev; + usb_max_devs = 0; + drive_ok_usb = 1; + for(j = 0; j < USB_MAX_BUS; j++) + { + for(i = 0; i < USB_MAX_DEVICE; i++) + { + dev = usb_get_dev_index(i, j); /* get device */ + if(dev == NULL) + break; /* no more devices available */ + usb_stor_register(dev); + } /* for */ + } /* for */ + board_printf("%d Storage Device(s) found\r\n", usb_max_devs); + if(usb_max_devs > 0) + return 0; + return -1; +} + +/******************************************************************************* + * registering usb mass storage device + */ +int usb_stor_register(struct usb_device *dev) +{ + if(!drive_ok_usb) + return -1; + if(usb_max_devs >= USB_MAX_STOR_DEV) + { + board_printf("Max USB Storage Device reached: %d\r\n", usb_max_devs); + return -1; + } + if(usb_max_devs == 0) + { + int i; + for(i = 0; i < USB_MAX_STOR_DEV; i++) + { + memset(&usb_dev_desc[i], 0, sizeof(block_dev_desc_t)); + usb_dev_desc[i].target = 0xff; + usb_dev_desc[i].if_type = IF_TYPE_USB; + usb_dev_desc[i].dev = i; + usb_dev_desc[i].part_type = PART_TYPE_UNKNOWN; + usb_dev_desc[i].block_read = usb_stor_read; + usb_dev_desc[i].block_write = usb_stor_write; + } + } + if(usb_stor_buf == NULL) + usb_stor_buf = (unsigned char *)usb_malloc(512); + if(usb_stor_buf == NULL) + return -1; + memset(usb_stor_buf, 0, sizeof(usb_stor_buf)); + usb_disable_asynch(1); /* asynch transfer not allowed */ + if(usb_storage_probe(dev, 0, &usb_stor[usb_max_devs])) + { + /* ok, it is a storage devices get info and fill it in */ + USB_STOR_PRINTF("USB STORAGE found (USB: %d, devnum: %d)\r\n", dev->usbnum, dev->devnum); + usb_dev_desc[usb_max_devs].priv = (void *)dev; + if(usb_stor_get_info(dev, &usb_stor[usb_max_devs], &usb_dev_desc[usb_max_devs])) + { + block_dev_desc_t *stor_dev; + if((stor_dev = usb_stor_get_dev(usb_max_devs)) != NULL) + { + int part_num = 1; + unsigned long part_type, part_offset, part_size; + while(!fat_register_device(stor_dev, part_num, &part_type, &part_offset, &part_size)) + { + USB_STOR_PRINTF("USB STORAGE install partition dev: %d\r\n", usb_max_devs); + install_usb_stor(usb_max_devs, part_type, part_offset, part_size, stor_dev->vendor, stor_dev->revision, stor_dev->product); + part_num++; + } + } + usb_max_devs++; + dev->deregister = usb_stor_deregister; + usb_disable_asynch(0); /* asynch transfer allowed */ + return 1; + } + else + usb_dev_desc[usb_max_devs].priv = NULL; + } + usb_disable_asynch(0); /* asynch transfer allowed */ + return -1; +} + +/******************************************************************************* + * deregistering usb mass storage device + */ +int usb_stor_deregister(struct usb_device *dev) +{ + int i; + for (i = 0; i <= USB_MAX_STOR_DEV; i++) + { + if(dev == (struct usb_device *)usb_dev_desc[i].priv) + { + memset(&usb_dev_desc[i], 0, sizeof(block_dev_desc_t)); + usb_dev_desc[i].target = 0xff; + usb_dev_desc[i].part_type = PART_TYPE_UNKNOWN; + usb_dev_desc[i].priv = NULL; + usb_max_devs--; + uninstall_usb_stor(i); + USB_STOR_PRINTF("USB STORAGE deregister\r\n"); + return 1; + } + } + return -1; +} + +static int usb_stor_irq(struct usb_device *dev) +{ + struct us_data *us; + us = (struct us_data *)dev->privptr; + if(us->ip_wanted) + us->ip_wanted = 0; + return 0; +} + +#ifdef USB_STOR_DEBUG + +static void usb_show_srb(ccb *pccb) +{ + int i; + board_printf("SRB: len %d datalen 0x%lX\r\n ", pccb->cmdlen, pccb->datalen); + for(i = 0; i < 12; i++) + board_printf("%02X ", pccb->cmd[i]); + board_printf("\r\n"); +} + +static void display_int_status(unsigned long tmp) +{ + board_printf("Status: %s %s %s %s %s %s %s\r\n", + (tmp & USB_ST_ACTIVE) ? "Active" : "", + (tmp & USB_ST_STALLED) ? "Stalled" : "", + (tmp & USB_ST_BUF_ERR) ? "Buffer Error" : "", + (tmp & USB_ST_BABBLE_DET) ? "Babble Det" : "", + (tmp & USB_ST_NAK_REC) ? "NAKed" : "", + (tmp & USB_ST_CRC_ERR) ? "CRC Error" : "", + (tmp & USB_ST_BIT_ERR) ? "Bitstuff Error" : ""); +} + +#endif + +/*********************************************************************** + * Data transfer routines + ***********************************************************************/ + +static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length) +{ + int this_xfer, result, partial, maxtry, stat; + /* determine the maximum packet size for these transfers */ + int max_size = usb_maxpacket(us->pusb_dev, pipe) * 16; + /* while we have data left to transfer */ + while(length) + { + /* calculate how long this will be -- maximum or a remainder */ + this_xfer = length > max_size ? max_size : length; + length -= this_xfer; + /* setup the retry counter */ + maxtry = 10; + /* set up the transfer loop */ + do + { + /* transfer the data */ + USB_STOR_PRINTF("Bulk xfer 0x%x(%d) try #%d\r\n", (unsigned int)buf, this_xfer, 11 - maxtry); + result = usb_bulk_msg(us->pusb_dev, pipe, buf, this_xfer, &partial, USB_CNTL_TIMEOUT * 5); + USB_STOR_PRINTF("bulk_msg returned %d xferred %d/%d\r\n", result, partial, this_xfer); + if(us->pusb_dev->status != 0) + { + /* if we stall, we need to clear it before we go on */ +#ifdef USB_STOR_DEBUG + display_int_status(us->pusb_dev->status); +#endif + if(us->pusb_dev->status & USB_ST_STALLED) + { + USB_STOR_PRINTF("stalled ->clearing endpoint halt for pipe 0x%x\r\n", pipe); + stat = us->pusb_dev->status; + usb_clear_halt(us->pusb_dev, pipe); + us->pusb_dev->status = stat; + if(this_xfer == partial) + { + USB_STOR_PRINTF("bulk transferred with error %X, but data ok\r\n", us->pusb_dev->status); + return 0; + } + else + return result; + } + if(us->pusb_dev->status & USB_ST_NAK_REC) + { + USB_STOR_PRINTF("Device NAKed bulk_msg\r\n"); + return result; + } + USB_STOR_PRINTF("bulk transferred with error"); + if(this_xfer == partial) + { + USB_STOR_PRINTF(" %d, but data ok\r\n", us->pusb_dev->status); + return 0; + } + /* if our try counter reaches 0, bail out */ + USB_STOR_PRINTF(" %d, data %d\r\n", us->pusb_dev->status, partial); + if(!maxtry--) + return result; + } + /* update to show what data was transferred */ + this_xfer -= partial; + buf += partial; + /* continue until this transfer is done */ + } + while (this_xfer); + } + /* if we get here, we're done and successful */ + return 0; +} + +static int usb_stor_BBB_reset(struct us_data *us) +{ + int result; + unsigned int pipe; + /* + * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class) + * + * For Reset Recovery the host shall issue in the following order: + * a) a Bulk-Only Mass Storage Reset + * b) a Clear Feature HALT to the Bulk-In endpoint + * c) a Clear Feature HALT to the Bulk-Out endpoint + * + * This is done in 3 steps. + * + * If the reset doesn't succeed, the device should be port reset. + * + * This comment stolen from FreeBSD's /sys/dev/usb/umass.c. + */ + USB_STOR_PRINTF("BBB_reset\r\n"); + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + US_BBB_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, 0, 0, USB_CNTL_TIMEOUT * 5); + if((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) + { + USB_STOR_PRINTF("RESET:stall\r\n"); + return -1; + } + /* long wait for reset */ + wait_ms(150); + USB_STOR_PRINTF("BBB_reset result %d: status %X reset\r\n", result, us->pusb_dev->status); + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + result = usb_clear_halt(us->pusb_dev, pipe); + /* long wait for reset */ + wait_ms(150); + USB_STOR_PRINTF("BBB_reset result %d: status %X clearing IN endpoint\r\n", result, us->pusb_dev->status); + /* long wait for reset */ + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + result = usb_clear_halt(us->pusb_dev, pipe); + wait_ms(150); + USB_STOR_PRINTF("BBB_reset result %d: status %X clearing OUT endpoint\r\n", result, us->pusb_dev->status); + USB_STOR_PRINTF("BBB_reset done\r\n"); + return 0; +} + +/* FIXME: this reset function doesn't really reset the port, and it + * should. Actually it should probably do what it's doing here, and + * reset the port physically + */ +static int usb_stor_CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + int result; + USB_STOR_PRINTF("CB_reset\r\n"); + memset(cmd, 0xff, sizeof(cmd)); + cmd[0] = SCSI_SEND_DIAG; + cmd[1] = 4; + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, cmd, sizeof(cmd), USB_CNTL_TIMEOUT * 5); + /* long wait for reset */ + wait_ms(1500); + USB_STOR_PRINTF("CB_reset result %d: status %X clearing endpoint halt\r\n", result, us->pusb_dev->status); + usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); + USB_STOR_PRINTF("CB_reset done\r\n"); + return 0; +} + +/* + * Set up the command for a BBB device. Note that the actual SCSI + * command is copied into cbw.CBWCDB. + */ +int usb_stor_BBB_comdat(ccb *srb, struct us_data *us) +{ + int result, actlen, dir_in; + unsigned int pipe; + umass_bbb_cbw_t *cbw = (umass_bbb_cbw_t *)usb_malloc(sizeof(umass_bbb_cbw_t)); + if(cbw == NULL) + { + USB_STOR_PRINTF("usb_stor_BBB_comdat: out of memory\r\n"); + return -1; + } + dir_in = US_DIRECTION(srb->cmd[0]); +#ifdef BBB_COMDAT_TRACE + board_printf("dir %d lun %d cmdlen %d cmd %p datalen %d pdata %p\r\n", dir_in, srb->lun, srb->cmdlen, srb->cmd, srb->datalen, srb->pdata); + if(srb->cmdlen) + { + for(result = 0; result < srb->cmdlen; result++) + board_printf("cmd[%d] %#x ", result, srb->cmd[result]); + board_printf("\r\n"); + } +#endif + /* sanity checks */ + if(!(srb->cmdlen <= CBWCDBLENGTH)) + { + USB_STOR_PRINTF("usb_stor_BBB_comdat: cmdlen too large\r\n"); + usb_free(cbw); + return -1; + } + /* always OUT to the ep */ + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + cbw->dCBWSignature = cpu_to_le32(CBWSIGNATURE); + cbw->dCBWTag = cpu_to_le32(CBWTag++); + cbw->dCBWDataTransferLength = cpu_to_le32(srb->datalen); + cbw->bCBWFlags = (dir_in ? CBWFLAGS_IN : CBWFLAGS_OUT); + cbw->bCBWLUN = srb->lun; + cbw->bCDBLength = srb->cmdlen; + /* copy the command data into the CBW command data buffer */ + /* DST SRC LEN!!! */ + memcpy(&cbw->CBWCDB, srb->cmd, srb->cmdlen); + result = usb_bulk_msg(us->pusb_dev, pipe, cbw, UMASS_BBB_CBW_SIZE, &actlen, USB_CNTL_TIMEOUT * 5); + if(result < 0) + USB_STOR_PRINTF("usb_stor_BBB_comdat:usb_bulk_msg error\r\n"); + usb_free(cbw); + return result; +} + +/* FIXME: we also need a CBI_command which sets up the completion + * interrupt, and waits for it + */ +int usb_stor_CB_comdat(ccb *srb, struct us_data *us) +{ + int result = 0; + int retry = 5; + unsigned int pipe; + unsigned long status; + int dir_in = US_DIRECTION(srb->cmd[0]); + if(dir_in) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + while(retry--) + { + USB_STOR_PRINTF("CBI gets a command: Try %d\r\n", 5 - retry); +#ifdef USB_STOR_DEBUG + usb_show_srb(srb); +#endif + /* let's send the command via the control pipe */ + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev , 0), + US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, us->ifnum, srb->cmd, srb->cmdlen, USB_CNTL_TIMEOUT * 5); + USB_STOR_PRINTF("CB_transport: control msg returned %d, status %X\r\n", result, us->pusb_dev->status); + /* check the return code for the command */ + if(result < 0) + { + if(us->pusb_dev->status & USB_ST_STALLED) + { + status = us->pusb_dev->status; + USB_STOR_PRINTF(" stall during command found, clear pipe\r\n"); + usb_clear_halt(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0)); + us->pusb_dev->status = status; + } + USB_STOR_PRINTF(" error during command %02X Stat = %X\r\n", srb->cmd[0], us->pusb_dev->status); + return result; + } + /* transfer the data payload for this command, if one exists*/ + USB_STOR_PRINTF("CB_transport: control msg returned %d, direction is %s to go 0x%lx\r\n", result, dir_in ? "IN" : "OUT", srb->datalen); + if(srb->datalen) + { + result = us_one_transfer(us, pipe, (char *)srb->pdata, srb->datalen); + USB_STOR_PRINTF("CBI attempted to transfer data, result is %d status %X, len %d\r\n", result, us->pusb_dev->status, us->pusb_dev->act_len); + if(!(us->pusb_dev->status & USB_ST_NAK_REC)) + break; + } /* if(srb->datalen) */ + else + break; + } + /* return result */ + return result; +} + +int usb_stor_CBI_get_status(ccb *srb, struct us_data *us) +{ + int timeout = 1000; + us->ip_wanted = 1; + usb_submit_int_msg(us->pusb_dev, us->irqpipe, (void *) &us->ip_data, us->irqmaxp, us->irqinterval); + while(timeout--) + { + if((volatile int *) us->ip_wanted == 0) + break; + wait_ms(10); + } + if(us->ip_wanted) + { + board_printf("Did not get interrupt on CBI\r\n"); + us->ip_wanted = 0; + return USB_STOR_TRANSPORT_ERROR; + } + USB_STOR_PRINTF("Got interrupt data 0x%x, transfered %d status 0x%lX\r\n", us->ip_data, us->pusb_dev->irq_act_len, us->pusb_dev->irq_status); + /* UFI gives us ASC and ASCQ, like a request sense */ + if(us->subclass == US_SC_UFI) + { + if(srb->cmd[0] == SCSI_REQ_SENSE || srb->cmd[0] == SCSI_INQUIRY) + return USB_STOR_TRANSPORT_GOOD; /* Good */ + else if(us->ip_data) + return USB_STOR_TRANSPORT_FAILED; + else + return USB_STOR_TRANSPORT_GOOD; + } + /* otherwise, we interpret the data normally */ + switch(us->ip_data) + { + case 0x0001: return USB_STOR_TRANSPORT_GOOD; + case 0x0002: return USB_STOR_TRANSPORT_FAILED; + default: return USB_STOR_TRANSPORT_ERROR; + } + return USB_STOR_TRANSPORT_ERROR; +} + +#define USB_TRANSPORT_UNKNOWN_RETRY 5 +#define USB_TRANSPORT_NOT_READY_RETRY 10 + +/* clear a stall on an endpoint - special for BBB devices */ +int usb_stor_BBB_clear_endpt_stall(struct us_data *us, __u8 endpt) +{ + int result; + /* ENDPOINT_HALT = 0, so set value to 0 */ + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endpt, 0, 0, USB_CNTL_TIMEOUT * 5); + return result; +} + +int usb_stor_BBB_transport(ccb *srb, struct us_data *us) +{ + int result, retry; + int dir_in; + int actlen, data_actlen; + unsigned int pipe, pipein, pipeout; +#ifdef BBB_XPORT_TRACE + unsigned char *ptr; + int index; +#endif + umass_bbb_csw_t *csw = (umass_bbb_csw_t *)usb_malloc(sizeof(umass_bbb_csw_t)); + if(csw == NULL) + { + USB_STOR_PRINTF("out of memory\r\n"); + return USB_STOR_TRANSPORT_FAILED; + } + dir_in = US_DIRECTION(srb->cmd[0]); + /* COMMAND phase */ + USB_STOR_PRINTF("COMMAND phase\r\n"); + result = usb_stor_BBB_comdat(srb, us); + if(result < 0) + { + USB_STOR_PRINTF("failed to send CBW status %X\r\n", us->pusb_dev->status); + if(!(us->pusb_dev->status & USB_ST_NOT_PROC)) + usb_stor_BBB_reset(us); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } + wait_ms(5); + pipein = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + pipeout = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + /* DATA phase + error handling */ + data_actlen = 0; + /* no data, go immediately to the STATUS phase */ + if(srb->datalen == 0) + goto st; + USB_STOR_PRINTF("DATA phase\r\n"); + if(dir_in) + pipe = pipein; + else + pipe = pipeout; + result = usb_bulk_msg(us->pusb_dev, pipe, srb->pdata, srb->datalen, &data_actlen, USB_CNTL_TIMEOUT * 5); + /* special handling of STALL in DATA phase */ + if((result < 0) && (us->pusb_dev->status & USB_ST_STALLED)) + { + USB_STOR_PRINTF("DATA:stall\r\n"); + /* clear the STALL on the endpoint */ + result = usb_stor_BBB_clear_endpt_stall(us, dir_in ? us->ep_in : us->ep_out); + if(result >= 0) + /* continue on to STATUS phase */ + goto st; + } + if(result < 0) + { + USB_STOR_PRINTF("usb_bulk_msg error status %X\r\n", us->pusb_dev->status); + usb_stor_BBB_reset(us); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } +#ifdef BBB_XPORT_TRACE + for(index = 0; index < data_actlen; index++) + board_printf("pdata[%d] %#x ", index, srb->pdata[index]); + board_printf("\r\n"); +#endif + /* STATUS phase + error handling */ +st: + retry = 0; +again: + USB_STOR_PRINTF("STATUS phase\r\n"); + result = usb_bulk_msg(us->pusb_dev, pipein, csw, UMASS_BBB_CSW_SIZE, &actlen, USB_CNTL_TIMEOUT * 5); + /* special handling of STALL in STATUS phase */ + if((result < 0) && (retry < 1) && (us->pusb_dev->status & USB_ST_STALLED)) + { + USB_STOR_PRINTF("STATUS:stall\r\n"); + /* clear the STALL on the endpoint */ + result = usb_stor_BBB_clear_endpt_stall(us, us->ep_in); + if(result >= 0 && (retry++ < 1)) + /* do a retry */ + goto again; + } + if(result < 0) + { + USB_STOR_PRINTF("usb_bulk_msg error status %X\r\n", us->pusb_dev->status); + usb_stor_BBB_reset(us); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } +#ifdef BBB_XPORT_TRACE + ptr = (unsigned char *)csw; + for(index = 0; index < UMASS_BBB_CSW_SIZE; index++) + board_printf("ptr[%d] %#x ", index, ptr[index]); + board_printf("\r\n"); +#endif + /* misuse pipe to get the residue */ + pipe = le32_to_cpu(csw->dCSWDataResidue); + if(pipe == 0 && srb->datalen != 0 && srb->datalen - data_actlen != 0) + pipe = srb->datalen - data_actlen; + if(CSWSIGNATURE != le32_to_cpu(csw->dCSWSignature)) + { + USB_STOR_PRINTF("!CSWSIGNATURE\r\n"); + usb_stor_BBB_reset(us); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } + else if((CBWTag - 1) != le32_to_cpu(csw->dCSWTag)) + { + USB_STOR_PRINTF("!Tag\r\n"); + usb_stor_BBB_reset(us); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } + else if(csw->bCSWStatus > CSWSTATUS_PHASE) + { + USB_STOR_PRINTF(">PHASE\r\n"); + usb_stor_BBB_reset(us); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } + else if(csw->bCSWStatus == CSWSTATUS_PHASE) + { + USB_STOR_PRINTF("=PHASE\r\n"); + usb_stor_BBB_reset(us); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } + else if(data_actlen > srb->datalen) + { + USB_STOR_PRINTF("transferred %dB instead of %dB\r\n", data_actlen, srb->datalen); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } + else if(csw->bCSWStatus == CSWSTATUS_FAILED) + { + USB_STOR_PRINTF("FAILED\r\n"); + usb_free(csw); + return USB_STOR_TRANSPORT_FAILED; + } + usb_free(csw); + return result; +} + +int usb_stor_CB_transport(ccb *srb, struct us_data *us) +{ + int result, status; + int retry = 0, notready = 0; + ccb *psrb = (ccb *)usb_malloc(sizeof(ccb)); + if(psrb == NULL) + { + USB_STOR_PRINTF("out of memory\r\n"); + return USB_STOR_TRANSPORT_FAILED; + } + status = USB_STOR_TRANSPORT_GOOD; + /* issue the command */ +do_retry: + result = usb_stor_CB_comdat(srb, us); + USB_STOR_PRINTF("command / Data returned %d, status %X\r\n", result, us->pusb_dev->status); + /* if this is an CBI Protocol, get IRQ */ + if(us->protocol == US_PR_CBI) + { + status = usb_stor_CBI_get_status(srb, us); + /* if the status is error, report it */ + if(status == USB_STOR_TRANSPORT_ERROR) + { + USB_STOR_PRINTF(" USB CBI Command Error\r\n"); + usb_free(psrb); + return status; + } + srb->sense_buf[12] = (unsigned char)(us->ip_data >> 8); + srb->sense_buf[13] = (unsigned char)(us->ip_data & 0xff); + if(!us->ip_data) + { + /* if the status is good, report it */ + if(status == USB_STOR_TRANSPORT_GOOD) + { + USB_STOR_PRINTF(" USB CBI Command Good\r\n"); + usb_free(psrb); + return status; + } + } + } + /* do we have to issue an auto request? */ + /* HERE we have to check the result */ + if((result < 0) && !(us->pusb_dev->status & USB_ST_STALLED)) + { + USB_STOR_PRINTF("ERROR %X\r\n", us->pusb_dev->status); + us->transport_reset(us); + usb_free(psrb); + return USB_STOR_TRANSPORT_ERROR; + } + if((us->protocol == US_PR_CBI) && ((srb->cmd[0] == SCSI_REQ_SENSE) || (srb->cmd[0] == SCSI_INQUIRY))) + { + /* do not issue an autorequest after request sense */ + USB_STOR_PRINTF("No auto request and good\r\n"); + usb_free(psrb); + return USB_STOR_TRANSPORT_GOOD; + } + /* issue an request_sense */ + memset(&psrb->cmd[0], 0, 12); + psrb->cmd[0] = SCSI_REQ_SENSE; + psrb->cmd[1] = srb->lun << 5; + psrb->cmd[4] = 18; + psrb->datalen = 18; + psrb->pdata = &srb->sense_buf[0]; + psrb->cmdlen = 12; + /* issue the command */ + result = usb_stor_CB_comdat(psrb, us); + USB_STOR_PRINTF("auto request returned %d\r\n", result); + /* if this is an CBI Protocol, get IRQ */ + if(us->protocol == US_PR_CBI) + status = usb_stor_CBI_get_status(psrb, us); + if((result < 0) && !(us->pusb_dev->status & USB_ST_STALLED)) + { + USB_STOR_PRINTF(" AUTO REQUEST ERROR %d\r\n", us->pusb_dev->status); + usb_free(psrb); + return USB_STOR_TRANSPORT_ERROR; + } + USB_STOR_PRINTF("autorequest returned 0x%02X 0x%02X 0x%02X 0x%02X\r\n", srb->sense_buf[0], srb->sense_buf[2], srb->sense_buf[12], srb->sense_buf[13]); + /* Check the auto request result */ + if((srb->sense_buf[2] == 0) && (srb->sense_buf[12] == 0) && (srb->sense_buf[13] == 0)) + { + /* ok, no sense */ + usb_free(psrb); + return USB_STOR_TRANSPORT_GOOD; + } + /* Check the auto request result */ + switch(srb->sense_buf[2]) + { + case 0x01: + /* Recovered Error */ + usb_free(psrb); + return USB_STOR_TRANSPORT_GOOD; + case 0x02: /* Not Ready */ + if(notready++ > USB_TRANSPORT_NOT_READY_RETRY) + { + board_printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X 0x%02X (NOT READY)\r\n", srb->cmd[0], srb->sense_buf[0], srb->sense_buf[2], srb->sense_buf[12], srb->sense_buf[13]); + usb_free(psrb); + return USB_STOR_TRANSPORT_FAILED; + } + else + { + wait_ms(100); + goto do_retry; + } + break; + default: + if(retry++ > USB_TRANSPORT_UNKNOWN_RETRY) + { + board_printf("cmd 0x%02X returned 0x%02X 0x%02X 0x%02X 0x%02X\r\n", srb->cmd[0], srb->sense_buf[0], srb->sense_buf[2], srb->sense_buf[12], srb->sense_buf[13]); + usb_free(psrb); + return USB_STOR_TRANSPORT_FAILED; + } + else + goto do_retry; + break; + } + usb_free(psrb); + return USB_STOR_TRANSPORT_FAILED; +} + +static int usb_inquiry(ccb *srb, struct us_data *ss) +{ + int i, retry = 5; + do + { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_INQUIRY; + srb->cmd[4] = 36; + srb->datalen = 36; + srb->cmdlen = 12; + i = ss->transport(srb, ss); + USB_STOR_PRINTF("inquiry returns %d\r\n", i); + switch(i) + { + case USB_STOR_TRANSPORT_GOOD : return 0; + default : + if(ss->pusb_dev->status & USB_ST_NOT_PROC) + return -1; + break; + } + } + while(retry--); + board_printf("error in inquiry\r\n"); + return -1; +} + +static int usb_request_sense(ccb *srb, struct us_data *ss) +{ + char *ptr; + ptr = (char *)srb->pdata; + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_REQ_SENSE; + srb->cmd[4] = 18; + srb->datalen = 18; + srb->pdata = &srb->sense_buf[0]; + srb->cmdlen = 12; + ss->transport(srb, ss); + USB_STOR_PRINTF("Request Sense returned %02X %02X %02X\r\n", srb->sense_buf[2], srb->sense_buf[12], srb->sense_buf[13]); + srb->pdata = (unsigned char *)ptr; + return 0; +} + +static int usb_test_unit_ready(ccb *srb, struct us_data *ss) +{ + int retries = 10; + do + { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_TST_U_RDY; + srb->datalen = 0; + srb->cmdlen = 12; + switch(ss->transport(srb, ss)) + { + case USB_STOR_TRANSPORT_GOOD : return 0; + default : + if(ss->pusb_dev->status & USB_ST_NOT_PROC) + return -1; + break; + } + usb_request_sense(srb, ss); + wait_ms(100); + } + while(retries--); + return -1; +} + +static int usb_read_capacity(ccb *srb, struct us_data *ss) +{ + int retry = 3; + do + { + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_RD_CAPAC; + srb->datalen = 8; + srb->cmdlen = 12; + switch(ss->transport(srb, ss)) + { + case USB_STOR_TRANSPORT_GOOD : return 0; + default : + if(ss->pusb_dev->status & USB_ST_NOT_PROC) + return -1; + break; + } + } + while(retry--); + return -1; +} + +static int usb_read_10(ccb *srb, struct us_data *ss, unsigned long start, unsigned short blocks) +{ + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_READ10; + srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; + srb->cmd[5] = ((unsigned char) (start)) & 0xff; + srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + srb->cmd[8] = (unsigned char) blocks & 0xff; + srb->cmdlen = 12; + USB_STOR_PRINTF("read10: start %lx blocks %x\r\n", start, blocks); + return ss->transport(srb, ss); +} + +#ifdef CONFIG_USB_WRITE_ENABLE +static int usb_write_10(ccb *srb, struct us_data *ss, unsigned long start, unsigned short blocks) +{ + memset(&srb->cmd[0], 0, 12); + srb->cmd[0] = SCSI_WRITE10; + srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff; + srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff; + srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff; + srb->cmd[5] = ((unsigned char) (start)) & 0xff; + srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff; + srb->cmd[8] = (unsigned char) blocks & 0xff; + srb->cmdlen = 12; + USB_STOR_PRINTF("write10: start %lx blocks %x\r\n", start, blocks); + return ss->transport(srb, ss); +} +#endif /* CONFIG_USB_WRITE_ENABLE */ + +#ifdef CONFIG_USB_BIN_FIXUP +/* + * Some USB storage devices queried for SCSI identification data respond with + * binary strings, which if output to the console freeze the terminal. The + * workaround is to modify the vendor and product strings read from such + * device with proper values (as reported by 'usb info'). + * + * Vendor and product length limits are taken from the definition of + * block_dev_desc_t in include/part.h. + */ +static void usb_bin_fixup(struct usb_device_descriptor descriptor, unsigned char vendor[], unsigned char product[]) +{ + const unsigned char max_vendor_len = 40; + const unsigned char max_product_len = 20; + if(descriptor.idVendor == 0x0424 && descriptor.idProduct == 0x223a) + { + strncpy((char *)vendor, "SMSC", max_vendor_len); + strncpy((char *)product, "Flash Media Cntrller", max_product_len); + } +} +#endif /* CONFIG_USB_BIN_FIXUP */ + +#define USB_MAX_READ_BLK 20 + +long usb_stor_read(int device, unsigned long blknr, unsigned long blkcnt, void *buffer) +{ + unsigned long start, blks, buf_addr; + unsigned short smallblks; + struct usb_device *dev; + unsigned char *tmp_buf; + int retry, i, j; + ccb *srb = &usb_ccb; + if(blkcnt == 0) + return -1; + device &= 0xff; + /* Setup device */ + dev = NULL; + for(j = 0; j < USB_MAX_BUS; j++) + { + for(i = 0; i < USB_MAX_DEVICE; i++) + { + dev = usb_get_dev_index(i, j); + if(dev == NULL) + break; + if(dev == (struct usb_device *)usb_dev_desc[device].priv) + { + j = USB_MAX_BUS; + break; + } + } + } + if((dev == NULL) || (dev->privptr == NULL)) + return -1; + usb_disable_asynch(1); /* asynch transfer not allowed */ + srb->lun = usb_dev_desc[device].lun; + buf_addr = (unsigned long)buffer; + start = blknr; + blks = blkcnt; + if(usb_test_unit_ready(srb, (struct us_data *)dev->privptr)) + { + board_printf("Device NOT ready\r\nRequest Sense returned %02X %02X %02X\r\n", srb->sense_buf[2], srb->sense_buf[12], srb->sense_buf[13]); + usb_disable_asynch(0); /* asynch transfer allowed */ + return -2; + } + tmp_buf = (unsigned char *)usb_malloc(USB_MAX_READ_BLK * usb_dev_desc[device].blksz); + if(tmp_buf == NULL) + { + USB_STOR_PRINTF("Read out of memory ERROR\r\n"); + usb_disable_asynch(0); /* asynch transfer allowed */ + return -1; + } + USB_STOR_PRINTF("usb_read: dev %d startblk %lx, blccnt %lx buffer %lx\r\n", device, start, blks, buf_addr); + do + { + /* XXX need some comment here */ + retry = 2; + if(blks > USB_MAX_READ_BLK) + smallblks = USB_MAX_READ_BLK; + else + smallblks = (unsigned short)blks; +retry_it: + srb->datalen = usb_dev_desc[device].blksz * smallblks; + srb->pdata = tmp_buf; + if(usb_read_10(srb, (struct us_data *)dev->privptr, start, smallblks)) + { + USB_STOR_PRINTF("Read ERROR\r\n"); + usb_request_sense(srb, (struct us_data *)dev->privptr); + if(retry--) + goto retry_it; + blkcnt -= blks; + break; + } + memcpy((void *)buf_addr, tmp_buf, srb->datalen); + start += smallblks; + blks -= smallblks; + buf_addr += srb->datalen; + } + while(blks != 0); + usb_free(tmp_buf); + USB_STOR_PRINTF("usb_read: end startblk %lx, blccnt %x buffer %lx\r\n", start, smallblks, buf_addr); + usb_disable_asynch(0); /* asynch transfer allowed */ + return blkcnt; +} + +long usb_stor_write(int device, unsigned long blknr, unsigned long blkcnt, const void *buffer) +{ +#ifdef CONFIG_USB_WRITE_ENABLE + unsigned long start, blks, buf_addr; + unsigned short smallblks; + struct usb_device *dev; + unsigned char *tmp_buf; + int retry, i, j; + ccb *srb = &usb_ccb; + if(blkcnt == 0) + return -1; + device &= 0xff; + /* Setup device */ + dev = NULL; + for(j = 0; j < USB_MAX_BUS; j++) + { + for(i = 0; i < USB_MAX_DEVICE; i++) + { + dev = usb_get_dev_index(i, j); + if(dev == NULL) + break; + if(dev == (struct usb_device *)usb_dev_desc[device].priv) + { + j = USB_MAX_BUS; + break; + } + } + } + if((dev == NULL) || (dev->privptr == NULL)) + return -1; + usb_disable_asynch(1); /* asynch transfer not allowed */ + srb->lun = usb_dev_desc[device].lun; + buf_addr = (unsigned long)buffer; + start = blknr; + blks = blkcnt; + if(usb_test_unit_ready(srb, (struct us_data *)dev->privptr)) + { + board_printf("Device NOT ready\r\nRequest Sense returned %02X %02X %02X\r\n", srb->sense_buf[2], srb->sense_buf[12], srb->sense_buf[13]); + usb_disable_asynch(0); /* asynch transfer allowed */ + return -2; + } + tmp_buf = (unsigned char *)usb_malloc(USB_MAX_READ_BLK * usb_dev_desc[device].blksz); + if(tmp_buf == NULL) + { + USB_STOR_PRINTF("Write out of memory ERROR\r\n"); + usb_disable_asynch(0); /* asynch transfer allowed */ + return -1; + } + USB_STOR_PRINTF("usb_write: dev %d startblk %lx, blccnt %lx buffer %lx\r\n", device, start, blks, buf_addr); + do + { + /* XXX need some comment here */ + retry = 2; + if(blks > USB_MAX_READ_BLK) + smallblks = USB_MAX_READ_BLK; + else + smallblks = (unsigned short)blks; +retry_it: + srb->datalen = usb_dev_desc[device].blksz * smallblks; + srb->pdata = tmp_buf; + memcpy(tmp_buf, (void *)buf_addr, srb->datalen); + if(usb_write_10(srb, (struct us_data *)dev->privptr, start, smallblks)) + { + USB_STOR_PRINTF("Write ERROR\r\n"); + usb_request_sense(srb, (struct us_data *)dev->privptr); + if(retry--) + goto retry_it; + blkcnt -= blks; + break; + } + start += smallblks; + blks -= smallblks; + buf_addr += srb->datalen; + } + while(blks != 0); + usb_free(tmp_buf); + USB_STOR_PRINTF("usb_write: end startblk %lx, blccnt %x buffer %lx\r\n", start, smallblks, buf_addr); + usb_disable_asynch(0); /* asynch transfer allowed */ + return blkcnt; +#else + if(device); + if(blknr); + if(blkcnt); + if(buffer); + return 0; +#endif /* CONFIG_USB_WRITE_ENABLE */ +} + +/* Probe to see if a new device is actually a Storage device */ +int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, struct us_data *ss) +{ + struct usb_interface_descriptor *iface; + int i; + unsigned int flags = 0; + int protocol = 0; + int subclass = 0; + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; +#if 0 + USB_STOR_PRINTF("iVendor 0x%X iProduct 0x%X\r\n", dev->descriptor.idVendor, dev->descriptor.idProduct); + /* this is the place to patch some storage devices */ + if((dev->descriptor.idVendor) == 0x066b && (dev->descriptor.idProduct) == 0x0103) + { + USB_STOR_PRINTF("patched for E-USB\r\n"); + protocol = US_PR_CB; + subclass = US_SC_UFI; /* an assumption */ + } +#endif + if(dev->descriptor.bDeviceClass != 0 || iface->bInterfaceClass != USB_CLASS_MASS_STORAGE + || iface->bInterfaceSubClass < US_SC_MIN || iface->bInterfaceSubClass > US_SC_MAX) + /* if it's not a mass storage, we go no further */ + return 0; + memset(ss, 0, sizeof(struct us_data)); + /* At this point, we know we've got a live one */ + USB_STOR_PRINTF("\r\n\r\nUSB Mass Storage device detected\r\n"); + /* Initialize the us_data structure with some useful info */ + ss->flags = flags; + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->attention_done = 0; + /* If the device has subclass and protocol, then use that. Otherwise, + * take data from the specific interface. + */ + if(subclass) + { + ss->subclass = subclass; + ss->protocol = protocol; + } + else + { + ss->subclass = iface->bInterfaceSubClass; + ss->protocol = iface->bInterfaceProtocol; + } + /* set the handler pointers based on the protocol */ + USB_STOR_PRINTF("Transport: "); + switch(ss->protocol) + { + case US_PR_CB: + USB_STOR_PRINTF("Control/Bulk\r\n"); + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + break; + case US_PR_CBI: + USB_STOR_PRINTF("Control/Bulk/Interrupt\r\n"); + ss->transport = usb_stor_CB_transport; + ss->transport_reset = usb_stor_CB_reset; + break; + case US_PR_BULK: + USB_STOR_PRINTF("Bulk/Bulk/Bulk\r\n"); + ss->transport = usb_stor_BBB_transport; + ss->transport_reset = usb_stor_BBB_reset; + break; + default: + board_printf("USB Storage Transport unknown / not yet implemented\r\n"); + return 0; + break; + } + /* + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * An optional interrupt is OK (necessary for CBI protocol). + * We will ignore any others. + */ + for(i = 0; i < iface->bNumEndpoints; i++) + { + /* is it an BULK endpoint? */ + if((iface->ep_desc[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) + { + if(iface->ep_desc[i].bEndpointAddress & USB_DIR_IN) + ss->ep_in = iface->ep_desc[i].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + else + ss->ep_out = iface->ep_desc[i].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + } + /* is it an interrupt endpoint? */ + if((iface->ep_desc[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + { + ss->ep_int = iface->ep_desc[i].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + ss->irqinterval = iface->ep_desc[i].bInterval; + } + } + USB_STOR_PRINTF("Endpoints In %d Out %d Int %d\r\n", ss->ep_in, ss->ep_out, ss->ep_int); + /* Do some basic sanity checks, and bail if we find a problem */ + if(usb_set_interface(dev, iface->bInterfaceNumber, 0) || !ss->ep_in || !ss->ep_out + || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) + { + USB_STOR_PRINTF("Problems with device\r\n"); + return 0; + } + /* set class specific stuff */ + /* We only handle certain protocols. Currently, these are + * the only ones. + * The SFF8070 accepts the requests used in u-boot + */ + if(ss->subclass != US_SC_UFI && ss->subclass != US_SC_SCSI && ss->subclass != US_SC_8070) + { + board_printf("Sorry, protocol %d not yet supported.\r\n", ss->subclass); + return 0; + } + if(ss->ep_int) + { + /* we had found an interrupt endpoint, prepare irq pipe + * set up the IRQ pipe and handler + */ + ss->irqinterval = (ss->irqinterval > 0) ? ss->irqinterval : 255; + ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); + ss->irqmaxp = usb_maxpacket(dev, ss->irqpipe); + dev->irq_handle = usb_stor_irq; + } + dev->privptr = (void *)ss; + return 1; +} + +int usb_stor_get_info(struct usb_device *dev, struct us_data *ss, block_dev_desc_t *dev_desc) +{ + unsigned char perq, modi; + unsigned long *cap = NULL; + unsigned long *capacity, *blksz; + ccb *pccb = &usb_ccb; + /* for some reasons a couple of devices would not survive this reset */ + if( + /* Sony USM256E */ + (dev->descriptor.idVendor == 0x054c && dev->descriptor.idProduct == 0x019e) + /* USB007 Mini-USB2 Flash Drive */ + || (dev->descriptor.idVendor == 0x066f && dev->descriptor.idProduct == 0x2010) + /* SanDisk Corporation Cruzer Micro 20044318410546613953 */ + || (dev->descriptor.idVendor == 0x0781 && dev->descriptor.idProduct == 0x5151) + /* SanDisk Corporation U3 Cruzer Micro 1/4GB Flash Drive 000016244373FFB4 */ + || (dev->descriptor.idVendor == 0x0781 && dev->descriptor.idProduct == 0x5406) + ) + USB_STOR_PRINTF("usb_stor_get_info: skipping RESET..\r\n"); + else + ss->transport_reset(ss); + pccb->pdata = usb_stor_buf; + dev_desc->target = dev->devnum; + pccb->lun = dev_desc->lun; + USB_STOR_PRINTF(" address %d\r\n", dev_desc->target); + if(usb_inquiry(pccb, ss)) + return -1; + perq = usb_stor_buf[0]; + modi = usb_stor_buf[1]; + if((perq & 0x1f) == 0x1f) + return 0; /* skip unknown devices */ + if((modi & 0x80) == 0x80) + dev_desc->removable = 1; /* drive is removable */ + memcpy(&dev_desc->vendor[0], &usb_stor_buf[8], 8); + memcpy(&dev_desc->product[0], &usb_stor_buf[16], 16); + memcpy(&dev_desc->revision[0], &usb_stor_buf[32], 4); + dev_desc->vendor[8] = 0; + dev_desc->product[16] = 0; + dev_desc->revision[4] = 0; +#ifdef CONFIG_USB_BIN_FIXUP + usb_bin_fixup(dev->descriptor, (uchar *)dev_desc->vendor, (uchar *)dev_desc->product); +#endif /* CONFIG_USB_BIN_FIXUP */ + USB_STOR_PRINTF("ISO Vers %X, Response Data %X\r\n", usb_stor_buf[2], usb_stor_buf[3]); + if(usb_test_unit_ready(pccb, ss)) + { + board_printf("Device NOT ready\r\nRequest Sense returned %02X %02X %02X\r\n", pccb->sense_buf[2], pccb->sense_buf[12], pccb->sense_buf[13]); + if(dev_desc->removable == 1) + { + dev_desc->type = perq; + return 1; + } + return 0; + } + cap = (unsigned long *)usb_malloc(sizeof(unsigned long) * 2); + if(cap == NULL) + return 0; + pccb->pdata = (unsigned char *)&cap[0]; + memset(pccb->pdata, 0, 8); + if(usb_read_capacity(pccb, ss) != 0) + { + board_printf("READ_CAP ERROR\r\n"); + cap[0] = 2880; + cap[1] = 0x200; + } + USB_STOR_PRINTF("Read Capacity returns: 0x%lx, 0x%lx\r\n", cap[0], cap[1]); +#if 0 + if(cap[0] > (0x200000 * 10)) /* greater than 10 GByte */ + cap[0] >>= 16; +#endif + cap[0] = cpu_to_be32(cap[0]); + cap[1] = cpu_to_be32(cap[1]); + /* this assumes bigendian! */ + cap[0] += 1; + capacity = &cap[0]; + blksz = &cap[1]; + USB_STOR_PRINTF("Capacity = 0x%lx, blocksz = 0x%lx\r\n", *capacity, *blksz); + dev_desc->lba = *capacity; + dev_desc->blksz = *blksz; + dev_desc->type = perq; + USB_STOR_PRINTF(" address %d\r\n", dev_desc->target); + USB_STOR_PRINTF("partype: %d\r\n", dev_desc->part_type); + init_part(dev_desc); + USB_STOR_PRINTF("partype: %d\r\n", dev_desc->part_type); + usb_free(cap); + return 1; +} + +#endif /* CONFIG_USB_STORAGE */ +#endif /* CONFIG_USB_UHCI || CONFIG_USB_OHCI || CONFIG_USB_EHCI */