diff options
Diffstat (limited to 'src/filo/usb')
| -rw-r--r-- | src/filo/usb/debug_x.c | 433 | ||||
| -rw-r--r-- | src/filo/usb/debug_x.h | 18 | ||||
| -rw-r--r-- | src/filo/usb/ohci.c | 1437 | ||||
| -rw-r--r-- | src/filo/usb/ohci.h | 316 | ||||
| -rw-r--r-- | src/filo/usb/scsi.h | 226 | ||||
| -rw-r--r-- | src/filo/usb/scsi_cmds.c | 512 | ||||
| -rw-r--r-- | src/filo/usb/scsi_cmds.h | 14 | ||||
| -rw-r--r-- | src/filo/usb/uhci.c | 1143 | ||||
| -rw-r--r-- | src/filo/usb/uhci.h | 175 | ||||
| -rw-r--r-- | src/filo/usb/usb.c | 803 | ||||
| -rw-r--r-- | src/filo/usb/usb.h | 435 | ||||
| -rw-r--r-- | src/filo/usb/usb_scsi_low.c | 172 | ||||
| -rw-r--r-- | src/filo/usb/usb_scsi_low.h | 10 | ||||
| -rw-r--r-- | src/filo/usb/usb_x.c | 163 |
14 files changed, 5857 insertions, 0 deletions
diff --git a/src/filo/usb/debug_x.c b/src/filo/usb/debug_x.c new file mode 100644 index 000000000..95a80b0a9 --- /dev/null +++ b/src/filo/usb/debug_x.c @@ -0,0 +1,433 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#include "usb.h" +#include "uhci.h" +#include "debug_x.h" + +//#include <string.h> + + +void dump_link( link_pointer_t *link, char *prefix) +{ + DPRINTF("%saddr: %08x\n", prefix, MEM_ADDR(link->link) ); + DPRINTF("%s raw addr: %04x\n", prefix, (link->link) <<4 ); + DPRINTF("%sterminate: %x\n", prefix, link->terminate); + DPRINTF("%squeue: %x\n", prefix, link->queue); + DPRINTF("%sdepth: %x\n", prefix, link->depth); +} + +void dump_frame_list( link_pointer_t *addr, char *prefix) +{ + int i; + + DPRINTF("%sFRAMELIST:\n",prefix); + + for(i=0;i<10; i++) { + dump_link(addr+i, prefix); + if(addr[i].queue) + dump_queue_head( MEM_ADDR(addr[i].link), ""); + else + dump_td( MEM_ADDR(addr[i].link), ""); + } +} + +void dump_hex(uchar *data, int len, char *prefix) +{ + int i=0; + + while(i<len) { + if(!i%16) { + DPRINTF("\n%s %04x: ", prefix, i); + } + else { + DPRINTF(": "); + } + + DPRINTF("%02x", data[i++]); + } + + DPRINTF("\n"); +} + +void dump_uhci(uint32_t port) +{ +#if 0 + unsigned long value; +#endif + + DPRINTF("HCI at %08x\n", port); +#if 0 + value = inw(port); + DPRINTF("Command: %04x\n", value); + + value = inw(port+2); + DPRINTF("USBSTS: %04x\n", value); + + value = inw(port+4); + DPRINTF("USBINTR: %04x\n", value); + + value = inw(port+6); + DPRINTF("FRNUM: %04x\n", value); + + value = inl(port+8); + DPRINTF("FLBASEADD: %08x\n", value); + + value = inb(port+0x0c); + DPRINTF("SOFMOD: %02x\n", value); + + value = inw(port+0x10); + DPRINTF("PORTSTS1: %04x\n", value); + + value = inw(port+0x12); + DPRINTF("PORTSTS2: %04x\n", value); + +#endif + +} + +void dump_td( td_t *td, char *prefix) +{ + char newpre[64]; + + newpre[0]='\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%sTD(%08x):\n", prefix, td); + + switch(td->packet_type) { + case SETUP_TOKEN: + DPRINTF("%stype: SETUP\n", prefix); + break; + case OUT_TOKEN: + DPRINTF("%stype: OUT\n", prefix); + break; + case IN_TOKEN: + DPRINTF("%stype: IN\n", prefix); + break; + default: + DPRINTF("%stype: INVALID (%02x)\n", prefix, td->packet_type); + break; + } + + DPRINTF("%sretries: %x\n", prefix, td->retrys); + + if(td->isochronous) { + DPRINTF("%sisochronous\n", prefix); + } + + if(td->interrupt) { + DPRINTF("%sIOC\n", prefix); + } + + if(td->lowspeed) { + DPRINTF("%slowspeed\n", prefix); + } + + if(td->detect_short) { + DPRINTF("%sDETECT_SHORT\n", prefix); + } + + DPRINTF("%sactive: %04x\n", prefix, td->active); + DPRINTF("%sdevice_addr: %02x\n", prefix, td->device_addr); + DPRINTF("%sendpoint: %1x\n", prefix, td->endpoint); + DPRINTF("%sdata_toggle: %1x\n", prefix, td->data_toggle); + DPRINTF("%smax_transfer: %x\n", prefix, td->max_transfer); + DPRINTF("%sactual: %x\n", prefix, td->actual); + DPRINTF("%slink:\n", prefix); + + if(td->stall) { + DPRINTF("%sSTALL\n", prefix); + } + + if(td->bitstuff) { + DPRINTF("%sBITSTUFF ERROR\n", prefix); + } + + if(td->crc) { + DPRINTF("%sCRC ERROR\n", prefix); + } + + if(td->nak) { + DPRINTF("%sNAK ERROR\n", prefix); + } + + if(td->babble) { + DPRINTF("%sBABBLE ERROR\n", prefix); + } + + if(td->buffer_error) { + DPRINTF("%sBUFFER ERROR\n", prefix); + } + + if(MEM_ADDR(td->link.link) == td) { + DPRINTF("link loops back to me!\n"); + return; + } + + dump_link(&(td->link), newpre); + if(!td->link.terminate) { + if(td->link.queue) + dump_queue_head(MEM_ADDR(td->link.link), prefix ); + else + dump_td(MEM_ADDR(td->link.link), prefix); + } +} + +void dump_queue_head( queue_head_t *qh, char *prefix) +{ + char newpre[64]; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%sQUEUE HEAD(%x):\n", prefix, qh); + DPRINTF("%sdepth:\n", prefix); + dump_link( &(qh->depth), newpre); + + if(!qh->depth.terminate) { + if(qh->depth.queue) { + dump_queue_head(MEM_ADDR(qh->depth.link), newpre); + } + else { + dump_td( MEM_ADDR(qh->depth.link), newpre); + } + } + + + DPRINTF("%sbredth:\n", prefix); + dump_link( &(qh->bredth), newpre); + if(!qh->bredth.terminate) { + if(qh->bredth.queue) { + dump_queue_head(MEM_ADDR(qh->bredth.link), newpre); + } + else { + dump_td( MEM_ADDR(qh->bredth.link), newpre); + } + } +} + +void dump_transaction( transaction_t *trans, char *prefix) +{ + char newpre[64]; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + + DPRINTF("%s TRANSACTION(%x):\n", prefix, trans); + dump_queue_head( trans->qh, newpre); + + DPRINTF("%s TDs:\n", prefix); + dump_td( trans->td_list, newpre); + + DPRINTF("\n"); + if(trans->next) + dump_transaction(trans->next, prefix); +} + +void dump_usbdev( usbdev_t *dev, char *prefix) +{ + char newpre[64]; + int i; + + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + DPRINTF("%saddress: %x\n", prefix, dev->address); + DPRINTF("%sclass: %x\n", prefix, dev->class); + DPRINTF("%ssubclass: %x\n", prefix, dev->subclass); + DPRINTF("%sbulk_in: %x\n", prefix, dev->bulk_in); + DPRINTF("%sbulk_out: %x\n", prefix, dev->bulk_out); + DPRINTF("%sinterrupt: %x\n", prefix, dev->interrupt); + + DPRINTF("%sep toggle:\n", prefix); + for(i=0;i<MAX_EP;i++) { + DPRINTF("%s%x\n", newpre, dev->toggle[i]); + } + + DPRINTF("%sep max_packet:\n", prefix); + for(i=0;i<MAX_EP;i++) { + DPRINTF("%s%x\n", newpre, dev->max_packet[i]); + } +} + +void dump_all_usbdev(char *prefix) +{ + int i; + + for(i=0;i<MAX_USB_DEV;i++) { + if(usb_device[i].address) { + DPRINTF("%sDEVICE: %x\n", prefix, i); + dump_usbdev( usb_device +i, prefix); + } + } +} + +void dump_device_descriptor( device_descriptor_t *des, char *prefix) +{ + DPRINTF("%sbLength: %02x\n", prefix, des->bLength); + DPRINTF("%stype: %02x\n", prefix, des->type); + DPRINTF("%sbcdVersion: %1x%1x\n", prefix, des->bcdVersion[1], des->bcdVersion[0]); + DPRINTF("%sClass: %02x\n",prefix, des->Class); + DPRINTF("%sSubClass: %02x\n",prefix, des->SubClass); + DPRINTF("%sprotocol: %02x\n",prefix, des->protocol); + DPRINTF("%smax_packet: %x\n",prefix, des->max_packet); + DPRINTF("%sidVendor: %04x\n", prefix, des->idVendor); + DPRINTF("%sidProduct: %04x\n", prefix, des->idProduct); + DPRINTF("%sbcdDeviceVersion: %1x%1x\n", prefix, des->bcdDevice[1], des->bcdDevice[0]); + DPRINTF("%siManufacturor: %02x\n", prefix, des->iManufacturor); + DPRINTF("%siProduct: %02x\n", prefix, des->iProduct); + DPRINTF("%siSerial: %02x\n", prefix, des->iSerial); + DPRINTF("%sbNumConfig: %02x\n", prefix, des->bNumConfig); + +} + +void dump_interface_descriptor( interface_descriptor_t *iface, char *prefix) +{ + + DPRINTF("%sbLength: %02x\n", prefix, iface->bLength); + DPRINTF("%stype: %02x\n", prefix, iface->type); + DPRINTF("%sbInterfaceNumber: %02x\n", prefix, iface->bInterfaceNumber); + DPRINTF("%sbAlternateSetting: %02x\n", prefix, iface->bAlternateSetting); + DPRINTF("%sbNumEndpoints: %02x\n", prefix, iface->bNumEndpoints); + DPRINTF("%sbInterfaceClass: %02x\n", prefix, iface->bInterfaceClass); + DPRINTF("%sbInterfaceSubClass: %02x\n", prefix, iface->bInterfaceSubClass); + DPRINTF("%sbInterfaceProtocol: %02x\n", prefix, iface->bInterfaceProtocol); + DPRINTF("%siInterface: %02x\n", prefix, iface->iInterface); +} + +void dump_endpoint_descriptor( endpoint_descriptor_t *ep, char *prefix) +{ + + DPRINTF("%sbLength: %02x\n", prefix, ep->bLength); + DPRINTF("%stype: %02x\n", prefix, ep->type); + DPRINTF("%sbEndpointAddress: %02x\n", prefix, ep->bEndpointAddress); + DPRINTF("%sbmAttributes: %02x\n", prefix, ep->bmAttributes); + DPRINTF("%swMaxPacketSize: %02x\n", prefix, ep->wMaxPacketSize); + DPRINTF("%sbInterval: %02x\n", prefix, ep->bInterval); +} + +void dump_config_descriptor( uchar *des, char *prefix) // YES uchar * +{ + config_descriptor_t *config; + interface_descriptor_t *iface; + endpoint_descriptor_t *ep; + char newpre[64]; + int i; + + memset(newpre,0,sizeof(newpre)); + newpre[0] = '\t'; + strcpy(newpre+1, prefix); + + config = (config_descriptor_t *) des; + iface = (interface_descriptor_t *) (des + config->bLength); + ep = (endpoint_descriptor_t *) (des + config->bLength + iface->bLength); + + // now, the config itself + DPRINTF("%sbLength: %02x\n", prefix, config->bLength); + DPRINTF("%stype: %02x\n", prefix, config->type); + DPRINTF("%swTotalLength: %04x\n", prefix, config->wTotalLength); + DPRINTF("%sbNumInterfaces: %02x\n", prefix, config->bNumInterfaces); + DPRINTF("%sbConfigurationValue: %02x\n", prefix, config->bConfigurationValue); + DPRINTF("%siConfiguration: %02x\n", prefix, config->iConfiguration); + DPRINTF("%sbmAttributes: %02x\n", prefix, config->bmAttributes); + + DPRINTF("%sbMaxPower: %02x\n", prefix, config->bMaxPower); + + DPRINTF("\n%sInterface(%x):\n", prefix, iface); + dump_interface_descriptor(iface, newpre); + + newpre[1] = '\t'; + strcpy(newpre+2, prefix); + + for(i=0; i<iface->bNumEndpoints; i++) { + DPRINTF("\n%sEndpoint (%x):\n", newpre+1, ep+i); + dump_endpoint_descriptor( ep+i, newpre); + } +} + +// Some control message bmRequestType defines +#define CTRL_DEVICE 0 +#define CONTROL_INTERFACE 1 +#define CONTROL_ENDPOINT 2 +#define CONTROL_OTHER 3 +#define CONTROL_RECIPIENT_MASK 0x1f + +#define CONTROL_TYPE_STD 0 +#define CONTROL_TYPE_CLASS 0x20 +#define CONTROL_CLASS_VENDOR 0x40 +#define CONTROL_CLASS_MASK 0x60 + +#define CONTROL_OUT 0 +#define CONTROL_IN 0x80 +#define CONTROL_DIR_MASK 0x80 + +// bRequest values +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 + +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 + +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 + +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +#define SYNC_FRAME 12 + +// descriptor types +#define DEVICE_DESC 1 +#define CONFIGURATION_DESC 2 +#define STRING_DESC 3 +#define INTERFACE_DESC 4 +#define ENDPOINT_DESC 5 +#define OTHERSPEED_DESC 7 +#define POWER_DESC 8 + +// features +#define FEATURE_HALT 0 +void dump_ctrlmsg( ctrl_msg_t *msg, char *prefix) +{ + DPRINTF("%sbmRequestType: %02x\n", prefix, msg->bmRequestType); + DPRINTF("%sbRequest: %02x\n", prefix, msg->bRequest); + DPRINTF("%swValue: %04x\n", prefix, msg->wValue); + DPRINTF("%swIndex: %04x\n", prefix, msg->wIndex); + DPRINTF("%swLength: %04x\n", prefix, msg->wLength); +} + +#endif diff --git a/src/filo/usb/debug_x.h b/src/filo/usb/debug_x.h new file mode 100644 index 000000000..109daae0e --- /dev/null +++ b/src/filo/usb/debug_x.h @@ -0,0 +1,18 @@ +#ifndef _DEBUG_X_H +#define _DEBUG_X_H + +void dump_hex(uchar *data, int len, char *prefix); +void dump_link( link_pointer_t *link, char *prefix); +void dump_td( td_t *td, char *prefix); +void dump_queue_head( queue_head_t *qh, char *prefix); +void dump_transaction( transaction_t *trans, char *prefix); +void dump_usbdev( usbdev_t *dev, char *prefix); +void dump_uhci(uint32_t port); +//void dump_all_usbdev(char *prefix); +void dump_device_descriptor( device_descriptor_t *des, char *prefix); +void dump_interface_descriptor( interface_descriptor_t *iface, char *prefix); +void dump_endpoint_descriptor( endpoint_descriptor_t *ep, char *prefix); +void dump_config_descriptor( uchar *des, char *prefix); +void dump_ctrlmsg( ctrl_msg_t *msg, char *prefix); + +#endif diff --git a/src/filo/usb/ohci.c b/src/filo/usb/ohci.c new file mode 100644 index 000000000..5c84c6376 --- /dev/null +++ b/src/filo/usb/ohci.c @@ -0,0 +1,1437 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#define DEBUG_TD 0 +#define DEBUG_ED 0 + + +#include "usb.h" +#include "ohci.h" + + +extern int usec_offset; + +ohci_regs_t *ohci_regs; + +// It will clear the enable bit +void ohc_clear_stat(uchar dev) +{ + uint32_t value; + ohci_regs = (ohci_regs_t *)hc_base[dev]; + + value = readl(&ohci_regs->cmdstatus); + writel(value, &ohci_regs->cmdstatus); +} + +void clear_oport_stat(uint32_t port) +{ + uint32_t value; + + value = readl(port); + writel(value, port); +} + +void oport_suspend( uint32_t port) +{ + writel( RH_PS_PSS, port); + +} +void oport_wakeup( uint32_t port) +{ + writel( RH_PS_POCI, port); + +} +#if 0 +void oport_resume( uint32_t port) +{ + uint32_t value; + + value = readl(port); + value |= 0x40; + writel(value, port); + udelay(20000+usec_offset); + value &= ~0x40; + writel(value, port); + + do { + value = readl(port); + } while(value & 0x40); +} +#endif +void oport_enable( uint32_t port) +{ + uint32_t value; + + value = readl(port); + + if((value & RH_PS_CCS)) { // if connected + writel( RH_PS_PES, port); + udelay(10); + writel( RH_PS_PESC, port); // Clear Change bit + } + +} + + + +void oport_disable( uint32_t port) +{ + writel( RH_PS_CCS, port); +} + +void oport_reset(uint32_t port) +{ + + uint32_t value; + + writel( RH_PS_PRS, port); + do { + value = readl( port ); + } while (!(value & RH_PS_PRSC) ); + writel(RH_PS_PRSC, port); //Clear Change bit + +} + +void oport_reset_long(uint32_t port) +{ + oport_reset(port); +} + +#if 0 + +int ohc_stop(uchar dev) +{ + unsigned short tmp; + uint32_t ctl; + + ohci_regs = hc_base[dev]; + + ctl = readl( &ohci_regs->control); + ctl &= ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE); + writel (ctl, &ohci_regs->control); + + return(0); +} +#endif + + +#define MAX_OHCI_TD 32 + +ohci_td_t *_ohci_td; +uint8_t _ohci_td_tag[MAX_OHCI_TD]; //1: used, 0:unused + +void init_ohci_td(){ + _ohci_td = allot2(sizeof(ohci_td_t)*MAX_OHCI_TD, 0x1f); // 32 byte aligna + if(_ohci_td==0) { + printf("init_ohci_td: NOMEM\n"); + } + memset(_ohci_td_tag, 0, sizeof(_ohci_td_tag)); +} + +ohci_td_t *td_alloc(ohci_t *ohci, int memflag){ + int i; + ohci_td_t *td; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==1) continue; + td = &_ohci_td[i]; + memset(td, 0, sizeof(ohci_td_t)); + td->td_dma = (void *)virt_to_phys(td); + _ohci_td_tag[i] = 1; + return td; + } + printf("td_alloc: no free slot\n"); + return 0; +} + +int td_free(ohci_t *ohci, ohci_td_t *td) { + int i; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==0) continue; + if(&_ohci_td[i] == td ) { + _ohci_td_tag[i] = 0; + return 1; + } + } + return 0; +} + + +struct ohci_td * +dma_to_td (struct ohci * hc, void *td_dma) +{ + int i; + ohci_td_t *td; + for(i = 0; i< MAX_OHCI_TD; i++ ) { + if(_ohci_td_tag[i]==0) continue; + td = &_ohci_td[i]; + if(td->td_dma == td_dma ) { + return td; + } + } + printf("dma_to_td: can not find td\n"); + return 0; + +} + +ohci_t _ohci_x[MAX_CONTROLLERS]; + +void ohci_init(void) +{ + init_ohci_td(); +} + +static int ohci_get_current_frame_number (struct usbdev *usb_dev) +{ + ohci_t * ohci = &_ohci_x[usb_dev->controller]; + + return le16_to_cpu (ohci->hcca->frame_no); +} + + + +static u32 roothub_a (struct ohci *hc) + { return readl (&hc->regs->roothub.a); } +static inline u32 roothub_b (struct ohci *hc) + { return readl (&hc->regs->roothub.b); } +static inline u32 roothub_status (struct ohci *hc) + { return readl (&hc->regs->roothub.status); } +static u32 roothub_portstatus (struct ohci *hc, int i) + { return readl (&hc->regs->roothub.portstatus[i]);} + + +#if DEBUG_USB==1 + +#define OHCI_VERBOSE_DEBUG + +# define dbg(...) \ + do { printf(__VA_ARGS__); printf("\n"); } while (0) + +static void urb_print (struct urb * urb, char * str, int small) +{ + unsigned int pipe= urb->pipe; + + if (!urb->dev ) { + dbg("%s URB: no dev", str); + return; + } + +#ifndef OHCI_VERBOSE_DEBUG + if (urb->status != 0) +#endif + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", + str, + ohci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + urb->transfer_flags, + urb->actual_length, + urb->transfer_buffer_length, + urb->status, urb->status); +#ifdef OHCI_VERBOSE_DEBUG +// if (!small) { + int i, len; + + if (usb_pipecontrol (pipe)) { + printf ("ohci.c: cmd(8):"); + for (i = 0; i < 8 ; i++) + printf (" %02x", ((u8 *) urb->setup_packet) [i]); + printf ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printf("ohci.c: data(%d/%d):", + urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe)? + urb->transfer_buffer_length: urb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printf (" %02x", ((u8 *) urb->transfer_buffer) [i]); + printf ("%s stat:%d\n", i < len? "...": "", urb->status); + } +// } +#endif +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t * ohci, char * str) { + int i, j; + u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca->int_table [i]); + if (*ed_p == 0) + continue; + printf ("ohci.c: %s branch int %2d(%2x):", str, i, i); +#if 0 + while (*ed_p != 0 && j--) { + ed_t *ed = dma_to_ed (ohci, le32_to_cpup(ed_p)); + printk (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } +#endif + printf ("\n"); + } +} +static void ohci_dump_intr_mask (char *label, u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} +static void maybe_print_eds (char *label, u32 value) +{ + if (value) + dbg ("%s %08x", label, value); +} +static char *hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} +// dump control and status registers +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + // intrdisable always same as intrenable + // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + u32 temp, ndp, i; + + temp = roothub_a (controller); + if (temp == ~(u32)0) + return; + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = roothub_b (controller); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = roothub_status (controller); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller usb-%x state", controller->regs); + + // dumps some of the state we know about + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca->frame_no); + ohci_dump_roothub (controller, 1); +} +void ohci_dump_x(uchar controller) +{ + ohci_t *ohci; + ohci = &_ohci_x[controller]; + ohci_dump (ohci, 1); +} +#endif + +/* link an ed into one of the HC chains */ + +/* ED is only enqueued and dequeued by HCD + So ep_link may only to be called two times for every device (function) -- ed, one for controled and one for bulked + one ohci may have several controled and bulked. +*/ + +int ep_link (ohci_t * ohci, ed_t * edi) +{ + volatile ed_t * ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { +// debug("ep_link control 21 ed->dma = %x\n", (uint32_t)ed->dma); + writel ((uint32_t)ed->dma, &ohci->regs->ed_controlhead); + } else { +// debug("ep_link control 22 ed->dma = %x\n", (uint32_t)ed->dma); + ohci->ed_controltail->hwNextED = cpu_to_le32 ((uint32_t)ed->dma); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail) { + /* enable control ed list */ + ohci->hc_control |= OHCI_CTRL_CLE; //5 + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + // debug("ep_link control 31 ed->dma = %x\n", (uint32_t)ed->dma); + writel ((uint32_t)ed->dma, &ohci->regs->ed_bulkhead); + } else { + // debug("ep_link control 32 ed->dma = %x\n", (uint32_t)ed->dma); + ohci->ed_bulktail->hwNextED = cpu_to_le32 ((uint32_t)ed->dma); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail) { + /* enable bulk ed list */ + ohci->hc_control |= OHCI_CTRL_BLE; //5 + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +ed_t * ep_add_ed ( + usbdev_t * usb_dev, + unsigned int pipe, + int interval, + int load, + int mem_flags +) +{ + ohci_t * ohci = &_ohci_x[usb_dev->controller]; + ohci_td_t * td; + ed_t * ed; + unsigned long flags; + int i; + + /* We use preallocate ed in ohci struct + numbering rule ??? + */ + i = (usb_pipeendpoint (pipe) << 1) |(usb_pipecontrol (pipe)? 0: usb_pipeout (pipe)); + ed = (ed_t *)&ohci->ed[i]; + +// debug("ep_add_ed: usb_dev port=%x, controller = %d ohci=%x ohci->ed=%x ed=%x ed->dma=%x\n", usb_dev->port,usb_dev->controller, ohci, ohci->ed, ed, ed->dma); + + if (ed->state == ED_NEW) { + ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (ohci, 0); + + ed->hwTailP = cpu_to_le32 ((uint32_t)td->td_dma); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci->ed_cnt++; // we will be used to calcaulate next pipe + +// debug("ep_add_ed 1 td=%x dma=%x ed->dma=%x ed->hwHeadP=%x ed->hwTailP=%x\n", td, td->td_dma, ed->dma, ed->hwHeadP, ed->hwTailP); + + } + + ohci->dev[usb_pipedevice (pipe)] = usb_dev; // marked the ed to this dev + + ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); + +// debug("ep_add_ed: pipe=%x ed_num=%d ed->dma=%x ed->hwInfo=%x ed->hwHeadP=%x ed->hwTailP=%x\n", pipe, i, ed->dma, ed->hwINFO, ed->hwHeadP, ed->hwTailP); + + return ed; +} + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +void +td_fill (ohci_t * ohci, unsigned int info, + void *data, int len, + struct urb * urb, int index) // *data should dma address of buffer +{ + ohci_td_t * td, * td_pt; + urb_priv_t * urb_priv = urb->hcpriv; + + if (index >= urb_priv->length) { + printf("internal OHCI error: TD index > length"); + return; + } + + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = dma_to_td (ohci, + (void *)(le32_to_cpup (&urb_priv->ed->hwTailP) & ~0xf)); + +// debug("td_fill 2 td = %x, dma=%x , ed->hwHeadP=%x, ed->hwTailP=%x \n", td, td->td_dma, urb_priv->ed->hwHeadP, urb_priv->ed->hwTailP ); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->urb = urb; + td->data_dma = data; + if (!len) + data = 0; + + + td->hwINFO = cpu_to_le32 (info); + td->hwCBP = cpu_to_le32 ((uint32_t)data); + if (data) + td->hwBE = cpu_to_le32 ((uint32_t)data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = cpu_to_le32 ((uint32_t)td_pt->td_dma); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; + // debug("td_fill 4 td->td_dma=%x, td->hwINFO=%x\n", td->td_dma, td->hwINFO ); + // debug("td_fill 5 ed->dma=%x, ed->hwHeadP=%x, ed->hwTailP=%x \n", urb_priv->ed->dma, urb_priv->ed->hwHeadP, urb_priv->ed->hwTailP ); +} + +/* prepare all TDs of a transfer */ + +void td_submit_urb (struct urb * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + ohci_t * ohci = (ohci_t *) &_ohci_x[urb->dev->controller]; + void * data; + int data_len = urb->transfer_buffer_length; + int cnt = 0; + u32 info = 0; + unsigned int toggle = 0; + void *setup_buffer; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); + } + + urb_priv->td_cnt = 0; + + if (data_len) { + data = (void *)virt_to_phys(urb->transfer_buffer); + } else + data = 0; + switch (usb_pipetype (urb->pipe)) { + case PIPE_BULK: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); + cnt++; +#if 0 + /* If the transfer size is multiple of the pipe mtu, + * we may need an extra TD to create a empty frame + * Note : another way to check this condition is + * to test if(urb_priv->length > cnt) - Jean II */ + if ((urb->transfer_flags & USB_ZERO_PACKET) && + usb_pipeout (urb->pipe) && + (urb->transfer_buffer_length != 0) && + ((urb->transfer_buffer_length % maxps) == 0)) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), 0, 0, urb, cnt); + cnt++; + } +#endif + +// debug("td_submit_urb 2 -- set OHCI_BLF\n"); + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + break; + + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + setup_buffer = (void *)virt_to_phys(urb->setup_packet); +// debug("td_sumbit_urb 11 setup_buffer = %x\n", setup_buffer); + td_fill (ohci, info, setup_buffer , 8, urb, cnt++); + if (data_len > 0) { + info = usb_pipeout (urb->pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, urb, cnt++); + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, urb, cnt++); +// debug("td_sumbit_urb 11 data = %x\n", data); + +// debug("td_submit_urb 2 -- set OHCI_CLF\n"); + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + break; + + } + if (urb_priv->length != cnt) { + debug("TD LENGTH %d != CNT %d", urb_priv->length, cnt); + } +} + +/* free HCD-private data associated with this URB */ + +void urb_free_priv (struct ohci *hc, urb_priv_t * urb_priv) +{ + int i; + int last = urb_priv->length - 1; + int len; + struct ohci_td *td; + + if (last >= 0) { +#if 0 + /* ISOC, BULK, INTR data buffer starts at td 0 + * CTRL setup starts at td 0 */ + td = urb_priv->td [0]; + + len = td->urb->transfer_buffer_length; + + /* unmap CTRL URB setup */ + if (usb_pipecontrol (td->urb->pipe)) { +// it should be freed in usb_control_msg_x +// forget2((void *)phys_to_virt((uint32_t)td->data_dma)); // 8 bytes + + /* CTRL data buffer starts at td 1 if len > 0 */ + if (len && last > 0) + td = urb_priv->td [1]; + } + + /* unmap data buffer */ + if (len && td->data_dma) { +// Don't need +// forget2((void *)phys_to_virt((uint32_t)td->data_dma)); + } +#endif + + for (i = 0; i <= last; i++) { + td = urb_priv->td [i]; + if (td) + td_free (hc, td); + } + } +#if URB_PRE_ALLOCATE!=1 + forget2((void *)urb_priv); +#endif +} + +/* get a transfer request */ + +int ohci_submit_urb (struct urb * urb) +{ + ohci_t * ohci; + ed_t * ed; + urb_priv_t * urb_priv; + unsigned int pipe = urb->pipe; + int i, size = 0; + int mem_flags = 0; + + if (!urb->dev) + return -ENODEV; + + if (urb->hcpriv) /* urb already in use */ + return -EINVAL; + + + ohci = (ohci_t *) &_ohci_x[urb->dev->controller]; +// printf("ohci_submit_urb: urb->dev port=%x, controller = %d ohci=%x ohci->ed=%x ohci->hcca=%x\n", urb->dev->port,urb->dev->controller, ohci, ohci->ed, ohci->hcca); + +#if DEBUG_USB==1 +// urb_print (urb, "SUB", usb_pipein (pipe)); +#endif + + +#if 0 + /* handle a request to the virtual root hub */ + if (usb_pipedevice (pipe) == ohci->rh.devnum) + return rh_submit_urb (urb); + + /* when controller's hung, permit only roothub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) { + usb_dec_dev_use (urb->dev); + return -ESHUTDOWN; + } +#endif + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { + return -ENOMEM; + } +// debug("ohci_submit_usb: ed->dma=%x\n", ed->dma); + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (urb->transfer_buffer_length - 1) / 4096 + 1; +#if 0 + /* If the transfer size is multiple of the pipe mtu, + * we may need an extra TD to create a empty frame + * Jean II */ + if ((urb->transfer_flags & USB_ZERO_PACKET) && + usb_pipeout (pipe) && + (urb->transfer_buffer_length != 0) && + ((urb->transfer_buffer_length % maxps) == 0)) + size++; +#endif + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (urb->transfer_buffer_length == 0)? 2: + (urb->transfer_buffer_length - 1) / 4096 + 3; + break; + } + + /* allocate the private part of the URB */ +#if URB_PRE_ALLOCATE!=1 + urb_priv = allot2 (sizeof (urb_priv_t) + size * sizeof (ohci_td_t *), 0xff); + if (urb_priv == 0) { + printf("ohci_submit_usb: urb_priv allocated no mem\n"); + return -ENOMEM; + } +#else + urb_priv = ohci->urb_priv; +#endif + memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (ohci_td_t *)); + + /* fill the private part of the URB */ + urb_priv->length = size; + urb_priv->ed = ed; + + /* allocate the TDs (updating hash chains) */ + for (i = 0; i < size; i++) { + urb_priv->td[i] = td_alloc (ohci, 0); + if (!urb_priv->td[i]) { + urb_priv->length = i; + urb_free_priv (ohci, urb_priv); + return -ENOMEM; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (ohci, urb_priv); + return -EINVAL; + } + + urb->actual_length = 0; + urb->hcpriv = urb_priv; + urb->status = USB_ST_URB_PENDING; + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) { + ep_link (ohci, ed); + } + + /* fill the TDs and link it to the ed */ + td_submit_urb (urb); + +#if 0 + /* drive timeouts by SF (messy, but works) */ + writel (OHCI_INTR_SF, &ohci->regs->intrenable); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +#endif + + return 0; +} +/* calculate the transfer length and update the urb */ + +void dl_transfer_length(ohci_td_t * td) +{ + u32 tdINFO, tdBE, tdCBP; + struct urb * urb = td->urb; + urb_priv_t * urb_priv = urb->hcpriv; + + tdINFO = le32_to_cpup (&td->hwINFO); + tdBE = le32_to_cpup (&td->hwBE); + tdCBP = le32_to_cpup (&td->hwCBP); + + + if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == urb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + urb->actual_length += tdBE - (uint32_t)td->data_dma + 1; + else + urb->actual_length += tdCBP - (uint32_t)td->data_dma; + } + + } + +// debug("td->td_dma=%x, urb->actual_length=%d\n", td->td_dma, urb->actual_length); +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +ohci_td_t * dl_reverse_done_list (ohci_t * ohci) +{ + u32 td_list_hc; + ohci_td_t * td_rev = NULL; + ohci_td_t * td_list = NULL; + urb_priv_t * urb_priv = NULL; + uint32_t value; + u32 td_list_hc2; + int timeout = 1000000; //1 second +// unsigned long flags; +// Here need to process across the frame tds + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + td_list_hc2 = readl(&ohci->regs->donehead); +// debug("ohci->hcca->done_head = %x ohci->hcca=%x ohci=%x ohci->regs->donehead=%x\n", td_list_hc, ohci->hcca, ohci, td_list_hc2); + + td_list = dma_to_td (ohci, (void *)td_list_hc); + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + + while(/*(td_list_hc2!=0) || */(td_list->index < urb_priv->length-1) && (timeout>0)) { // wait another update for donehead + // To handle 1. ohci->hcca->donehead !=0 and regs->donehead!=0 + // 2. ohci->hcca->donehead !=0 and regs->donehead ==0 but regs-->donehead will be filled + + ohci->hcca->done_head = 0; + + value = readl(&ohci->regs->intrstatus); + value &= readl(&ohci->regs->intrenable); + + // We need to clear that the bit, otherwise We will not get next return. + if(value & OHCI_INTR_WDH) { + writel(value, &ohci->regs->intrstatus); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +// debug("OHCI_INTR_WDH cleared intrstatus=%x value=%x \n", readl(&ohci->regs->intrstatus), value); + } + while(timeout>0) { // wait for next DONEHEAD_WRITEBACK + value = readl(&ohci->regs->intrstatus); + if(!(value & OHCI_INTR_WDH)) { + udelay(1); + timeout--; + continue; + } else { + break; + } + } + + td_list_hc2 = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + // merge td_list_hc the tail of td_list_hc2 + + if(td_list_hc2!=0) { + + while (td_list_hc2) { + td_list = dma_to_td (ohci, (void *)td_list_hc2); + td_list_hc2 = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + + td_list->hwNextTD = td_list_hc; + + td_list_hc = le32_to_cpup (&ohci->hcca->done_head) & 0xfffffff0; + + td_list = dma_to_td (ohci, (void *)td_list_hc); + } else { + printf("."); + } + + } + + ohci->hcca->done_head = 0; + + value = readl(&ohci->regs->intrstatus); +// debug("OHCI_INTR_WDH value=%x \n", value); + value &= readl(&ohci->regs->intrenable); + +// We need to clear that the bit, otherwise We will not get next return. +// if(value & OHCI_INTR_WDH) { + writel(value, &ohci->regs->intrstatus); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ +// debug("OHCI_INTR_WDH cleared intrstatus=%x value=%x \n", readl(&ohci->regs->intrstatus), value); +// } + +#if 0 + if (value & OHCI_INTR_SO) { + debug("USB Schedule overrun"); + writel (OHCI_INTR_SO, &ohci->regs->intrenable); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + } +#endif + + + while (td_list_hc) { +// debug("td_list_hc = %x\n", td_list_hc); + td_list = dma_to_td (ohci, (void *)td_list_hc); + + if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + debug(" USB-error/status: %x : %x\n", + TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { + if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { + td_list->ed->hwHeadP = + (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); + urb_priv->td_cnt += urb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + return td_list; +} +/*-------------------------------------------------------------------------*/ +/* td done list */ + +void dl_done_list (ohci_t * ohci, ohci_td_t * td_list) +{ + ohci_td_t * td_list_next = NULL; + ed_t * ed; + // int cc = 0; + struct urb * urb; + urb_priv_t * urb_priv; + u32 tdINFO; //, edHeadP, edTailP; + +// unsigned long flags; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + urb = td_list->urb; + urb_priv = urb->hcpriv; + tdINFO = le32_to_cpup (&td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); +#if 0 + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (cc == TD_CC_STALL) + usb_endpoint_halt(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + + if (!(urb->transfer_flags & USB_DISABLE_SPD) + && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + if (++(urb_priv->td_cnt) == urb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK)) + && (urb_priv->state != URB_DEL)) { + urb->status = cc_to_error[cc]; + ohci_return_urb (ohci, urb); + } + else { + dl_del_urb (urb); + } + } + + if (ed->state != ED_NEW) { + edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; + edTailP = le32_to_cpup (&ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } +#endif + + td_list = td_list_next; + } +} + +void ohci_wait_urb_done(struct urb *urb, int timeout) { // timeout usually ==10000 --> 10milisecond + //here need to according the urb or ed type judge the BLF and CLF, We may need one time out in it + // Or need to check intrstatus and see if the hcca->done_head has been filled. + // We need to clear that the bit, otherwise We will get next return. + uint32_t pipe = urb->pipe; + uint32_t value; + usbdev_t *usb_dev = urb->dev; + ohci_t *ohci = &_ohci_x[usb_dev->controller]; + uint32_t type; + while(timeout>0) { +#if 1 + value = readl(&ohci->regs->intrstatus); + if(!(value & OHCI_INTR_WDH)) { + udelay(1); + timeout--; + continue; + } else { + break; + } +#endif + } +#if 1 + while (timeout>0) { + type = usb_pipetype (pipe); + if(type ==PIPE_BULK) { + if( (readl(&ohci->regs->cmdstatus) & OHCI_BLF) == 0) break; + } else if(type == PIPE_CONTROL) { + if( (readl(&ohci->regs->cmdstatus) & OHCI_CLF) == 0) break; + } + udelay(1); // + timeout--; + } +#endif + + +} +void ohci_urb_complete(struct urb *urb) { + + ohci_t *ohci = &_ohci_x[urb->dev->controller]; + // it will clear the done list. and urb's actual_length is updated + dl_done_list (ohci, dl_reverse_done_list (ohci)); + +#if DEBUG_USB==1 + urb_print (urb, "RET", usb_pipein (urb->pipe)); +#endif + + urb_free_priv(ohci, urb->hcpriv); // free the priv and td list + +} +/*-------------------------------------------------------------------*/ +// it will 1. call usb_bulk_msg_x +// 2. call dl_list and find the data return +int ohci_bulk_transfer( uchar devnum, uchar ep, unsigned int data_len, uchar *data) { + int actual_length; + uint32_t t = devnum; + uint32_t pipe = ((ep&0x80)? 0x80:0)|(t<<8)|(3<<30); + t = ep; + pipe |=(t&0xf)<<15; + + usb_bulk_msg_x(&usb_device[devnum], pipe, data, data_len, &actual_length, 10000, ohci_urb_complete); + + return actual_length; + + +} +// it will 1. Call usb_control_msg_x +// 2. call dl_done_list to get the data returned ----> should be packed in one usb_complete_t function +// and assigned that to urb + +int ohci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short +wLength, void *data){ + + uint32_t t = devnum; + uint32_t pipe = ((request_type&0x80)? 0x80:0)|(t<<8)|(2<<30); + return usb_control_msg_x(&usb_device[devnum], pipe, request, request_type, wValue, wIndex, data, wLength, 10000, ohci_urb_complete); + +} + +int ohc_reset(uchar controller) +{ + + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + debug("Resetting OHCI\n"); + ohci_regs = (ohci_regs_t *)hc_base[controller]; + ohci_t *ohci = &_ohci_x[controller]; + +#ifndef __hppa__ + /* PA-RISC doesn't have SMM, but PDC might leave IR set */ + if (readl (&ohci_regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci_regs->cmdstatus); /* request ownership */ + debug("USB HC TakeOver from SMM"); + while (readl (&ohci_regs->control) & OHCI_CTRL_IR) { + mdelay (10); + if (--smm_timeout == 0) { + printf("USB HC TakeOver failed!"); + return -1; + } + } + } +#endif + + debug("USB HC reset_hc usb-%08x: ctrl = 0x%x ;", + hc_base[controller], + readl (&ohci_regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci_regs->control); + + /* Force a state change from USBRESET to USBOPERATIONAL for ALi */ + (void) readl (&ohci_regs->control); /* PCI posting */ + writel (ohci->hc_control = OHCI_USB_OPER, &ohci_regs->control); + + /* HC Reset requires max 10 ms delay */ + writel (OHCI_HCR, &ohci_regs->cmdstatus); + while ((readl (&ohci_regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + printf("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + return 0; +} + + +int ohc_start(uchar controller) { + // unsigned short tmp; + u32 mask; + unsigned int fminterval; + int delaytime; + ohci_regs = (ohci_regs_t *)hc_base[controller]; + ohci_t *ohci = &_ohci_x[controller]; + + debug("Starting OHCI\n"); + + writel (0, &ohci_regs->ed_controlhead); + writel (0, &ohci_regs->ed_bulkhead); + + writel ((uint32_t)ohci->hcca_dma, &ohci_regs->hcca); /* a reset clears this */ //3 + + fminterval = 0x2edf; //6 + writel ((fminterval * 9) / 10, &ohci_regs->periodicstart); // Don't worry, we can disable periodic in contol or let the ED list null + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci_regs->fminterval); + writel (0x628, &ohci_regs->lsthresh); + + /* start controller operations */ + + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + writel (ohci->hc_control, &ohci_regs->control); // PIE and IE is disabled + // DO we need to enable that but leave all ISO ED and INT ED list null??? + + mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + writel (mask, &ohci->regs->intrstatus); + + /* required for AMD-756 and some Mac platforms */ + writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM, &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); + + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + + // POTPGT delay is bits 24-31, in 2 ms units. + delaytime = ((roothub_a (ohci) >> 23) & 0x1fe)*5/2; // for apacer 256 usb 2.0 + NEC 2.0 chip +// delaytime = ((roothub_a (ohci) >> 23) & 0x1fe); + + mdelay (delaytime); + +// printf("delaytime: %d\n", delaytime); + + return(0); +} + + +int ohc_init(struct pci_device *dev) +{ + uint16_t word; + uint32_t dword; + ohci_t *ohci; + ed_t * ed; + int i,j, NDP; + int size; + + pci_read_config_dword(dev, 0x10, &dword); // it will be 4k range + hc_base[num_controllers] = (uint32_t)phys_to_virt(dword); + ohci = &_ohci_x[num_controllers]; + debug("ohc_init num_controllers=%d ohci=%x\n", num_controllers, (uint32_t)ohci); + memset(ohci, 0, sizeof(ohci_t)); + ohci->regs = (ohci_regs_t *)hc_base[num_controllers]; + ohci_regs = ohci->regs; + + ohci->hcca = allot2(sizeof (struct ohci_hcca), 0xff); //1 + if (!ohci->hcca) { + printf("ohc_init: hcca allocated no MEM\n"); + return -ENOMEM; + } + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); + ohci->hcca_dma = (void *)virt_to_phys(ohci->hcca); + + //init ed; + ohci->ed = allot2(sizeof(ed_t)*NUM_EDS,0xf); + if(ohci->ed==0) { + printf("ohci_init: ed allocate no MEM\n"); + } +// debug("ohci->ed = %x\n", ohci->ed); + for(i=0; i<NUM_EDS;i++) { + ed = (ed_t *)&ohci->ed[i]; + ed->dma = (void *)virt_to_phys(ed); +// debug("i=%d, ed dma = %x\n", i, (uint32_t)ed->dma); + ed->state = ED_NEW; + } + +// init urb and urb_priv + ohci->urb = (struct urb *)allot2(sizeof(struct urb),0xff); + if (!ohci->urb) { + printf("ohci_init: urb allocate failed"); + } + memset(ohci->urb, 0, sizeof(urb_t)); + + + /* allocate the private part of the URB */ + size = 4; + ohci->urb_priv = allot2 (sizeof (urb_priv_t) + size * sizeof (ohci_td_t *), 0xff); + if (ohci->urb_priv == 0) { + printf("ohci_init: urb_priv allocated no mem\n"); + } + memset (ohci->urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (ohci_td_t *)); + + // set master + pci_read_config_word(dev, 0x04, &word); + word |= 0x04; + pci_write_config_word(dev, 0x04, word); + + + DPRINTF("Found OHCI at %08x\n", hc_base[num_controllers]); + ohc_reset(num_controllers); + + /* Here should or move to ohc_start + 1. Init HCCA + 2. Init ED and TD ---> in submit_urb + 3. Assign HCCA to ohci_regs->hcca ---> in ohc_init + 4. Set Intr to ohci_regs->intrenable ---> disable that in ohc_init + 5. enable all queue in ohci_regs->control ---> in ep_link and it is called by submit_urb + 6. set peridicstart to 0.9 of frameinterval ---> ohc_start + */ + +// writel( 0, &ohci_regs->intrenable); // no interrupts! //4 +// writel( 0xffffffff, &ohci_regs->intrdisable); + + NDP = readl(&ohci->regs->roothub.a) & 0xff; + for(j=0;j<NDP;j++) { + writel(RH_PS_PSS, &ohci->regs->roothub.portstatus[j]); + } + + /* FIXME this is a second HC reset; why?? */ + writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); + (void)readl (&ohci->regs->intrdisable); /* PCI posting flush */ + mdelay (10); + + ohc_start(num_controllers); + + num_controllers++; + +#if DEBUG_USB==1 +// ohci_dump (ohci, 1); +#endif + +// debug("ohci->ed = %x\n", ohci->ed); + return(0); +} +int poll_o_root_hub(uint32_t port, uchar controller) +{ + uint32_t value; + int addr=0; + int i; + static uint32_t do_over=0; + uint8_t what; + ohci_t *ohci; + + value = readl(port); + + debug("poll_o_root_hub1 v=%08x port = %x, controller = %d\n", value, port, controller); + + if(value == 0xffffffff) return addr; // stupid port + + if((value & RH_PS_CSC) || do_over == port) { + debug("poll_o_root_hub2 v=%08x\t", value); + do_over=0; + if(value & RH_PS_CCS ) { // if port connected + debug("poll_o_root_hub21 v=%08x\t", value); + DPRINTF("Connection on port %04x\n", port); + + writel(value, port); + for(i=0; i<40; i++) { + udelay(10000+usec_offset); + value = readl(port); + if(value & RH_PS_CSC) { + writel(value, port); //Clear Change bit + i=0; + DPRINTF("BOUNCE!\n"); + } + } +// debug("poll_o_root_hub211 v=%08x\t", value); + + oport_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + +// debug("poll_o_root_hub212 v=%08x\t", readl(port)); + oport_reset(port); +// debug("poll_o_root_hub213 v=%08x\t", readl(port)); + mdelay(10); + oport_enable(port); +// debug("poll_o_root_hub214 v=%08x\t", readl(port)); + + if(!(value & RH_PS_CCS)) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & RH_PS_LSDA); + +#if DEBUG_USB==1 + // some one clear enable bit??? why??? It costs me one week to find it out. + ohci = &_ohci_x[controller]; + ohci_dump (ohci, 1); +#endif + +#if 1 +// usb_control_msg(addr, 0x21, 0xff, 0, 0, 0, NULL);// reset device +// mdelay(10); + usb_control_msg(addr, 0xa1, 0xfe, 0, 0, 1, &what); // get MAX L // get MAX LUN +#endif + +// debug("poll_o_root_hub215 v=%08x addr = %d\n", readl(port), addr); + + if(addr<0) { + oport_disable(port); + udelay(20000); +// oport_reset(port); + oport_reset_long(port); + oport_suspend(port); + do_over=port; + ohc_clear_stat(controller); + + } + } else { +// debug("poll_o_root_hub22 v=%08x\t", readl(port)); + oport_suspend(port); + oport_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + return(addr); +} + + + + +#endif diff --git a/src/filo/usb/ohci.h b/src/filo/usb/ohci.h new file mode 100644 index 000000000..0c9c6ad57 --- /dev/null +++ b/src/filo/usb/ohci.h @@ -0,0 +1,316 @@ +#ifdef USB_DISK + +#ifndef _OHCI_H +#define _OHCI_H + +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +// for OHCI + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +struct ed { + u32 hwINFO; + u32 hwTailP; + u32 hwHeadP; + u32 hwNextED; + + struct ed * ed_prev; + u8 int_period; // No use just for aligned + u8 int_branch; // No use just for aligned + u8 int_load; // No uae just for aligned + u8 int_interval; // No use just for aligned + u8 state; + u8 type; + u16 last_iso; // no use just for aligned + struct ed * ed_rm_list; // No use just for aligned + + void * dma; + + u32 unused[3]; +}; +// __attribute((aligned(16))); +typedef struct ed ed_t; + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +struct ohci_td { + u32 hwINFO; + u32 hwCBP; /* Current Buffer Pointer */ + u32 hwNextTD; /* Next TD Pointer */ + u32 hwBE; /* Memory Buffer End Pointer */ + u16 hwPSW[MAXPSW]; + u8 unused; + u8 index; + struct ed * ed; + struct ohci_td * next_dl_td; + struct urb * urb; //defined in usb.h + void * td_dma; + void * data_dma; + u32 unused2[2]; +}; +//__attribute((aligned(32))); /* normally 16, iso needs 32 */ +typedef struct ohci_td ohci_td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + u32 int_table[NUM_INTS]; /* Interrupt ED table */ + u16 frame_no; /* current frame number */ + u16 pad1; /* set to 0 on each frame_no change */ + u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + +#define MAX_ROOT_PORTS 15 + +struct ohci_regs { + /* control and status registers */ + u32 revision; + u32 control; + u32 cmdstatus; + u32 intrstatus; + u32 intrenable; + u32 intrdisable; + /* memory pointers */ + u32 hcca; + u32 ed_periodcurrent; + u32 ed_controlhead; + u32 ed_controlcurrent; + u32 ed_bulkhead; + u32 ed_bulkcurrent; + u32 donehead; + /* frame counters */ + u32 fminterval; + u32 fmremaining; + u32 fmnumber; + u32 periodicstart; + u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + u32 a; + u32 b; + u32 status; + u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); +typedef struct ohci_regs ohci_regs_t; + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) +//| OHCI_CTRL_IE | OHCI_CTRL_PLE + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +typedef struct +{ + ed_t * ed; + u16 length; // number of tds associated with this request + u16 td_cnt; // number of tds already serviced + int state; +#if 0 + wait_queue_head_t * wait; +#endif + ohci_td_t * td[0]; // list pointer to all corresponding TDs associated with this request + +} urb_priv_t; + +#define NUM_EDS 32 /* num of preallocated endpoint descriptors */ + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + void * hcca_dma; + + ohci_regs_t * regs; /* OHCI controller's memory */ + + ed_t * ed_bulktail; /* last endpoint of bulk list */ + ed_t * ed_controltail; /* last endpoint of control list */ + + int intrstatus; + u32 hc_control; /* copy of the hc control reg */ + + uint32_t ed_cnt; + ed_t *ed; // Allocate that from ed_buffer in ohc_init + usbdev_t *dev[NUM_EDS]; + urb_t *urb; // one ohci one urb + urb_priv_t *urb_priv; + struct usb_ctrlrequest *dr; +} ohci_t; + + +extern ohci_t _ohci_x[MAX_CONTROLLERS]; + +#define usb_to_ohci(usb_dev) (&_ohci_x[(usb_dev)->controller]) + +extern ohci_regs_t *ohci_regs; + +void clear_oport_stat(uint32_t port); +int ohc_init(struct pci_device *dev); +int poll_o_root_hub(uint32_t port, uchar controller); + +int ohci_bulk_transfer( uchar devnum, uchar ep, unsigned int data_len, uchar *data); +int ohci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short + wLength, void *data); +void ohci_wait_urb_done(struct urb *urb, int timeout); + +void ohci_init(void); +int ohc_init(struct pci_device *dev); +int ohci_submit_urb (struct urb * urb); +#endif + +#endif diff --git a/src/filo/usb/scsi.h b/src/filo/usb/scsi.h new file mode 100644 index 000000000..7c119a48a --- /dev/null +++ b/src/filo/usb/scsi.h @@ -0,0 +1,226 @@ +/* Copyright (C) 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * This header file contains public constants and structures used by + * the scsi code for linux. + */ + +#ifndef _SCSI_SCSI_H +#define _SCSI_SCSI_H 1 + +//#include <features.h> + +/* + * SCSI opcodes + */ + +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define SET_WINDOW 0x24 +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG 0x3e +#define WRITE_LONG 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME 0x41 +#define READ_TOC 0x43 +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define MOVE_MEDIUM 0xa5 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define WRITE_VERIFY_12 0xae +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define WRITE_LONG_2 0xea + +/* + * Status codes + */ + +#define GOOD 0x00 +#define CHECK_CONDITION 0x01 +#define CONDITION_GOOD 0x02 +#define BUSY 0x04 +#define INTERMEDIATE_GOOD 0x08 +#define INTERMEDIATE_C_GOOD 0x0a +#define RESERVATION_CONFLICT 0x0c +#define COMMAND_TERMINATED 0x11 +#define QUEUE_FULL 0x14 + +#define STATUS_MASK 0x3e + +/* + * SENSE KEYS + */ + +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define BLANK_CHECK 0x08 +#define COPY_ABORTED 0x0a +#define ABORTED_COMMAND 0x0b +#define VOLUME_OVERFLOW 0x0d +#define MISCOMPARE 0x0e + + +/* + * DEVICE TYPES + */ + +#define TYPE_DISK 0x00 +#define TYPE_TAPE 0x01 +#define TYPE_PROCESSOR 0x03 /* HP scanners use this */ +#define TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define TYPE_ROM 0x05 +#define TYPE_SCANNER 0x06 +#define TYPE_MOD 0x07 /* Magneto-optical disk - + * - treated as TYPE_DISK */ +#define TYPE_MEDIUM_CHANGER 0x08 +#define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define TYPE_NO_LUN 0x7f + +/* + * standard mode-select header prepended to all mode-select commands + * + * moved here from cdrom.h -- kraxel + */ + +struct ccs_modesel_head + { + unsigned char _r1; /* reserved. */ + unsigned char medium; /* device-specific medium type. */ + unsigned char _r2; /* reserved. */ + unsigned char block_desc_length; /* block descriptor length. */ + unsigned char density; /* device-specific density code. */ + unsigned char number_blocks_hi; /* number of blocks in this block + desc. */ + unsigned char number_blocks_med; + unsigned char number_blocks_lo; + unsigned char _r3; + unsigned char block_length_hi; /* block length for blocks in this + desc. */ + unsigned char block_length_med; + unsigned char block_length_lo; + }; + +/* + * MESSAGE CODES + */ + +#define COMMAND_COMPLETE 0x00 +#define EXTENDED_MESSAGE 0x01 +#define EXTENDED_MODIFY_DATA_POINTER 0x00 +#define EXTENDED_SDTR 0x01 +#define EXTENDED_EXTENDED_IDENTIFY 0x02 /* SCSI-I only */ +#define EXTENDED_WDTR 0x03 +#define SAVE_POINTERS 0x02 +#define RESTORE_POINTERS 0x03 +#define DISCONNECT 0x04 +#define INITIATOR_ERROR 0x05 +#define ABORT 0x06 +#define MESSAGE_REJECT 0x07 +#define NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define LINKED_CMD_COMPLETE 0x0a +#define LINKED_FLG_CMD_COMPLETE 0x0b +#define BUS_DEVICE_RESET 0x0c + +#define INITIATE_RECOVERY 0x0f /* SCSI-II only */ +#define RELEASE_RECOVERY 0x10 /* SCSI-II only */ + +#define SIMPLE_QUEUE_TAG 0x20 +#define HEAD_OF_QUEUE_TAG 0x21 +#define ORDERED_QUEUE_TAG 0x22 + +/* + * Here are some scsi specific ioctl commands which are sometimes useful. + */ +/* These are a few other constants only used by scsi devices. */ + +#define SCSI_IOCTL_GET_IDLUN 0x5382 + +/* Used to turn on and off tagged queuing for scsi devices. */ + +#define SCSI_IOCTL_TAGGED_ENABLE 0x5383 +#define SCSI_IOCTL_TAGGED_DISABLE 0x5384 + +/* Used to obtain the host number of a device. */ +#define SCSI_IOCTL_PROBE_HOST 0x5385 + +/* Used to get the bus number for a device. */ +#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386 + +#endif /* scsi/scsi.h */ diff --git a/src/filo/usb/scsi_cmds.c b/src/filo/usb/scsi_cmds.c new file mode 100644 index 000000000..bd7b7fa8a --- /dev/null +++ b/src/filo/usb/scsi_cmds.c @@ -0,0 +1,512 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + + +#include "scsi.h" + + +#include "usb_scsi_low.h" + +#ifndef NULL +#define NULL (void *) 0x0 +#endif + +#include "scsi_cmds.h" + +devhandle sgh; + +typedef struct sense_data { + uchar code; + + uchar sense_key:4; + uchar res1:4; + + uchar additional_code; + uchar qualifier; + + uchar res2[3]; + + uchar length; +} __attribute__ ((packed)) sense_data_t; + +typedef struct fixed_sense_data { + uchar code:7; + uchar valid:1; + + uchar obs1; + + uchar sense_key:4; + uchar res1:1; + uchar ili:1; + uchar eom:1; + uchar mark:1; + + unsigned int info; + + uchar add_len; +} __attribute__ ((packed)) fixed_sense_data_t; + +typedef struct additional_fixed_data { + unsigned int info; + + uchar code; + uchar qualifier; + uchar fru; + + uchar specific[3]; +} __attribute__ ((packed)) additional_fixed_data_t; + + +void PrintSense(uchar *sense, int len) +{ + int i; + + DPRINTF( "sense data "); + for(i=0;i<len; i++) { + DPRINTF( ":%02x", sense[i]); + } + DPRINTF("\n\n"); + + if( (sense[0] & 0x7f) >=0x72) { + sense_data_t *sd = (sense_data_t *) sense; + uchar *pos = sense+sizeof(sense_data_t); + uchar remaining = sd->length; + int dlen; + + DPRINTF("code = %02x, key = %1x, additional = %02x, qual = %02x\n", sd->code, sd->sense_key, sd->additional_code, sd->qualifier); + + while(remaining) { + DPRINTF("type = %02x", pos[0]); + dlen = pos[1]; + pos+=2; + remaining -=2; + + for(i=0; i<dlen; i++) + DPRINTF( ": %02x", pos[i]); + + DPRINTF("\n"); + pos+=i; + remaining -=i; + } + + } else { + fixed_sense_data_t *fd = (fixed_sense_data_t *) sense; + uchar remaining = fd->add_len; + additional_fixed_data_t *afd; + + + DPRINTF("code = %02x key = %1x\n", fd->code, fd->sense_key); + if(fd->mark) { + DPRINTF("filemark "); + } + + if(fd->eom) { + DPRINTF(" End Of Media "); + } + + if(fd->ili) { + DPRINTF("Illegal instruction"); + } + + DPRINTF("\n"); + + if(fd->valid) { + DPRINTF( "(valid) "); + } + + DPRINTF( "Info: %08x\n", ntohl(fd->info)); + + afd = (additional_fixed_data_t *) (sense + 8); + +// while(remaining) { + if(remaining) { + DPRINTF("command info = %08x\n", ntohl(afd->info)); + DPRINTF("code = %02x, qual = %02x, fru = %02x\n", afd->code, afd->qualifier, afd->fru); + DPRINTF("sense key data = %02x:%02x:%02x\n\n", afd->specific[2], afd->specific[1], afd->specific[0]); + + afd++; + remaining -= sizeof(additional_fixed_data_t); + } + } + +} + +typedef struct query_response { + uchar type:5; + uchar qualifier:3; + + uchar reserved1:7; + uchar removable:1; + + uchar version; + + uchar ResponseDataFormat:4; // should == 2 + uchar HiSup:1; // report luns cmd supported + uchar NormACA:1; + uchar obsolete:1; + uchar aerc:1; + + uchar AdditionalLength; // length of vendor specific data (beyond 96 bytes) + + uchar reserved2:7; + uchar sccs:1; // have raid controller + + uchar addr16:1; // + uchar obsolete2:2; + uchar MChnger:1; // media changer + uchar MultiP:1; // multi port + uchar vs:1; // ??? + uchar EncServ:1; // enclosure service + uchar BQue:1; // basic command queueing + + uchar vs2:1; + uchar CmdQue:1; // full command queueing + uchar obsolete4:1; + uchar linked:1; + uchar sync:1; + uchar wbus16:1; // + uchar obsolete3:1; + uchar RelAddr:1; // treletive addressing + + char vendor[8]; + char product[16]; + char revision[4]; + char vendor_data[20]; + + uchar ius:1; + uchar qas:1; + uchar clocking:2; // + uchar reserved3:4; + + unsigned short version_desc[8]; + + char reserved4[21]; +} query_response_t; + +typedef struct ReadBlockCMD { + uchar cmd; + + uchar reladdr:1; + uchar reserved:2; + uchar fua:1; // force unit access flush to media + uchar dpo:1; // direct page out, do not cache + uchar reserved2:3; + + unsigned int block_address; + uchar reserved3; + + unsigned short block_count; + + uchar control; +} __attribute__ ((packed)) ReadBlockCMD_t ; + +int ll_read_block(devhandle sgd, char *buffer, int blocknum, int count) +{ + int ret; + ReadBlockCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = READ_10; + rb.block_address = htonl(blocknum); + rb.block_count = htons(count); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + if(ret<0) { + DPRINTF("ERROR: ll_read_block( %x, %x, %x, %x) = %d\n", sgd, buffer, blocknum, count, ret); + PrintSense(sensedat, 32); + } + + return(ret); + +} + +int ll_write_block(devhandle sgd, char *buffer, int blocknum, int count) +{ + int ret; + ReadBlockCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = WRITE_10; + rb.block_address = htonl(blocknum); + rb.block_count = htons(count); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_TO_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + return(ret); +} + +typedef struct ReadLongCMD { + uchar cmd; + + uchar reladdr:1; + uchar correct:1; + uchar reserved:5; + + unsigned int block_address; + uchar reserved3; + + unsigned short length; + + uchar control; +} __attribute__ ((packed)) ReadLongCMD_t ; + +int ll_read_long(devhandle sgd, char *buffer, int blocknum, int size) +{ + int ret; + ReadLongCMD_t rb; + char sensedat[32]; + + memset(&rb,0,sizeof(rb)); + rb.cmd = READ_LONG; + rb.block_address = htonl(blocknum); + rb.length = htons(size); + + ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, size, sensedat, sizeof(sensedat)); + return(ret); +} + +unsigned char ReadCapacityCMD[10] = { READ_CAPACITY, 0, 0,0,0,0, 0,0,0, 0}; + +struct ReadCapacityResponse { + unsigned int block_address; + unsigned int block_length; +}; + +int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len) +{ + int ret; + struct ReadCapacityResponse response; + char sensedat[32]; + + ret = scsi_command(sgd, ReadCapacityCMD, sizeof(ReadCapacityCMD), SG_DXFER_FROM_DEV, (uint8_t *)&response, sizeof(response), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("ERROR:get capacity: %d\n", ret); + PrintSense(sensedat,32); + } + + + *block_count = ntohl(response.block_address) +1; + *blk_len = ntohl(response.block_length); + + return(ret); +} + +#define INQ_REP_LEN 96 +unsigned char InquiryCMD[6] = { INQUIRY, 0, 0, 0, INQ_REP_LEN, 0}; + +int query(devhandle sgd, query_response_t *qr) +{ + int ret; + char sensedat[32]; + + ret = scsi_command(sgd, InquiryCMD, sizeof(InquiryCMD), SG_DXFER_FROM_DEV, (uint8_t *)qr, sizeof(query_response_t), sensedat, sizeof(sensedat) ); + + if(ret<0){ + DPRINTF("query: IOCTL"); + } + + return(ret); +} + +typedef struct lun_list { + unsigned int list_length; + unsigned int reserved; + unsigned long long lun[16]; +} lun_list_t; + +#define REPORT_LUNS 0xa0 +unsigned char ReportLunsCMD[12] = { REPORT_LUNS, 0, 2, 0, 0, 0, 0, 0, 0, 128, 0, 0 }; + +int ReportLUNS(devhandle sgd, lun_list_t *list) +{ + int ret; + char sensedat[32]; + + memset (list, 0, sizeof(lun_list_t)); + ret = scsi_command(sgd, ReportLunsCMD, sizeof(ReportLunsCMD), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(lun_list_t), sensedat, sizeof(sensedat) ); + + if(ret<0) { + DPRINTF("Report Luns: IOCTL"); + } + + list->list_length = ntohl(list->list_length); + + return(ret); +} + +typedef struct command_descriptor { + uchar opcode; + uchar reserved; + unsigned short service_action; + uchar reserved2; + + uchar action_valid:1; + uchar reserved3:7; + + unsigned short cdb_len; +} __attribute__ ((packed)) command_descriptor_t; + +typedef struct report_opcodes_result { + unsigned long length; + + command_descriptor_t command[256]; +} __attribute__ ((packed)) report_opcode_result_t; + + +#define REPORT_OPCODES 0xa3 + +typedef struct report_opcodes_cmd { + uchar cmd; + uchar reserved[5]; + unsigned int reply_len; + uchar reserved2; + uchar control; +} __attribute__ ((packed)) ReportOpcodesCMD_t; + +//ReportOpcodesCMD_t ReportOpcodesCMD = { cmd : REPORT_OPCODES, reply_len: htonl(sizeof(report_opcode_result_t)) }; + +int ReportOpCodes(devhandle sgd, report_opcode_result_t *list) +{ + int ret; + char sensedat[32]; + ReportOpcodesCMD_t ReportOpcodesCMD; + + memset (list, 0, sizeof(report_opcode_result_t)); + ReportOpcodesCMD.cmd = REPORT_OPCODES; + ReportOpcodesCMD.reply_len = htonl( sizeof(report_opcode_result_t)); + + ret = scsi_command(sgd, (uint8_t *)&ReportOpcodesCMD, sizeof(ReportOpcodesCMD_t), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(report_opcode_result_t), sensedat, sizeof(sensedat) ); + + if(ret<0) { + DPRINTF("Report Luns: IOCTL"); + } + + list->length = ntohl(list->length); + + return(ret); +} + + +#define READ_ATTRIBUTE 0x8c +#define VOLUME_LIST 2 +#define PARTITION_LIST 3 + +typedef struct read_attribute_cmd { + uchar cmd; + + uchar action:5; + uchar res:3; + + uchar restricted[3]; + + uchar volume; + uchar res2; + uchar partition; + + ushort attribute; + unsigned int reply_len; + uchar res3; + uchar control; +} __attribute__ ((packed)) ReadAttributeCMD_t; + +int CheckVolumes(devhandle sgd) +{ + int ret; + uchar reply[4]; + uchar sensedat[32]; + ReadAttributeCMD_t cmd; + + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd=READ_ATTRIBUTE; + cmd.action = VOLUME_LIST; + cmd.reply_len = htonl(4); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("Report Volumes: IOCTL"); + return(-1); + } + + if(! reply[0] && !reply[1]) + return(0); + + return(reply[3]); +} + +int CheckPartitions(devhandle sgd) +{ + int ret; + uchar reply[4]; + uchar sensedat[32]; + ReadAttributeCMD_t cmd; + + memset(&cmd,0,sizeof(cmd)); + + cmd.cmd=READ_ATTRIBUTE; + cmd.action = PARTITION_LIST; + cmd.reply_len = htonl(4); + + ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("Report PARTITIONVolumes: IOCTL"); + return(-1); + } + + if(! reply[0] && !reply[1]) + return(0); + + return(reply[3]); +} + +int UnitReady(devhandle sgd) +{ + uchar cmd[6]; + uchar sensedat[32]; + int ret; + + memset(cmd,0,sizeof(cmd)); + + ret = scsi_command(sgd, &cmd, sizeof(cmd), SG_DXFER_FROM_DEV, NULL, 0, sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("UnitReady :"); + return(0); + } + + return(1); +} + + +#endif diff --git a/src/filo/usb/scsi_cmds.h b/src/filo/usb/scsi_cmds.h new file mode 100644 index 000000000..1c64a3bf3 --- /dev/null +++ b/src/filo/usb/scsi_cmds.h @@ -0,0 +1,14 @@ +#ifndef _SCSI_CMDS_H +#define _SCSI_CMDS_H + +#define devhandle uint8_t + +#define uchar uint8_t +#define ushort uint16_t + +void PrintSense(uchar *sense, int len); +int ll_read_block(devhandle sgd, char *buffer, int blocknum, int count); + +int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len); +int UnitReady(uchar sgd); +#endif diff --git a/src/filo/usb/uhci.c b/src/filo/usb/uhci.c new file mode 100644 index 000000000..4ce4b499c --- /dev/null +++ b/src/filo/usb/uhci.c @@ -0,0 +1,1143 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + + +#include "usb.h" +#include "uhci.h" +#include "debug_x.h" + +#define ALLOCATE 1 + +extern int usec_offset; + +int wait_head( queue_head_t *head, int count) +{ + td_t *td; + + + while(!head->depth.terminate) { + td = MEM_ADDR(head->depth.link); + if(!td->active) + return(-1); // queue failed + + if(count) + if(! --count) + return(0); // still active + + udelay(500); // give it some time + } + + return(1); // success +} + +queue_head_t *free_qh; +queue_head_t _queue_heads[MAX_QUEUEHEAD]; +queue_head_t *queue_heads = _queue_heads; + +queue_head_t *new_queue_head(void) +{ + queue_head_t *qh; + + if(!free_qh) + return(NULL); + + qh = free_qh; + free_qh = MEM_ADDR(qh->bredth.link); + + memset(qh,0,sizeof(queue_head_t)); + qh->bredth.terminate = qh->depth.terminate=1; + + return(qh); +} + +void free_queue_head( queue_head_t *qh) +{ + + qh->bredth.link = LINK_ADDR(free_qh); + if(!free_qh) + qh->bredth.terminate=1; + + qh->depth.terminate=1; + free_qh = qh; +} + +void init_qh(void) +{ + int i; + + for(i=0; i<MAX_QUEUEHEAD-1; i++) { + memset(queue_heads+i, 0, sizeof(queue_head_t)); + queue_heads[i].bredth.link = LINK_ADDR( &queue_heads[i+1] ); + queue_heads[i].depth.terminate=1; + } + + queue_heads[MAX_QUEUEHEAD-1].depth.terminate=1; + queue_heads[MAX_QUEUEHEAD-1].bredth.terminate=1; + + free_qh = queue_heads; +} + +td_t *free_td_list; +td_t _tds[MAX_TD]; +td_t *tds = _tds; // indirection added for kernel testing + + +void init_td(void) +{ + int i; + + for(i=0; i<MAX_TD-1; i++) { + memset(tds+i, 0, sizeof(td_t)); + tds[i].link.link = LINK_ADDR( &tds[i+1]); + } + + memset( &tds[MAX_TD-1], 0, sizeof(td_t)); + tds[MAX_TD-1].link.terminate=1; + + free_td_list = tds; +} + +td_t *new_td(void) +{ + td_t *td; + + if(!free_td_list) + return(NULL); + +// DPRINTF("new_td: free_td = %p\n", free_td_list); + td = free_td_list; + + free_td_list = MEM_ADDR( td->link.link); +// DPRINTF("new_td: free_td_list = %p\n", free_td_list); + + memset(td, 0, sizeof(td_t)); + td->link.terminate=1; + +// DPRINTF("new_td: returning %p\n", td); + return(td); +} + +td_t *find_last_td(td_t *td) +{ + td_t *last; + + last = td; + + while(!last->link.terminate) + last = MEM_ADDR(last->link.link); + + return(last); +} + +void free_td( td_t *td) +{ + td_t *last_td; + + last_td = find_last_td(td); + + last_td->link.link = LINK_ADDR(free_td_list); + if(!free_td_list) + last_td->link.terminate=1; + else + last_td->link.terminate=0; + + free_td_list = td; + +} + +link_pointer_t *queue_end( queue_head_t *queue) +{ + link_pointer_t *link; + + link = &(queue->depth); + + while(!link->terminate) + link = MEM_ADDR(link->link); + + return(link); +} + +void add_td( queue_head_t *head, td_t *td) +{ + link_pointer_t *link; + + link = queue_end(head); + + link->link = LINK_ADDR(td); + link->terminate=0; +} + +transaction_t transactions[MAX_TRANSACTIONS]; +transaction_t *free_transactions; + +void init_transactions(void) +{ + int i; + + memset(transactions, 0, sizeof(transactions)); + + for(i=0; i<MAX_TRANSACTIONS-1; i++) + transactions[i].next = &transactions[i+1]; + + free_transactions = transactions; +} + +void free_transaction( transaction_t *trans ) +{ + transaction_t *my_current, *last; + + my_current = trans; + + if(my_current==0) return; + + while(my_current) { + free_td( my_current->td_list ); + free_queue_head( my_current->qh ); + + last = my_current; + my_current = my_current->next; + } + + last->next = free_transactions; + free_transactions = trans; +} + +transaction_t *new_transaction(td_t *td) +{ + transaction_t *trans = free_transactions; + queue_head_t *qh; + + if(!trans) { + DPRINTF("new_transaction( td = %x) failed!\n", td); + return(NULL); + } + + free_transactions = trans->next; + + memset(trans, 0, sizeof(transaction_t)); + + if(td) { + qh = new_queue_head(); + if(!qh) { + free_transaction(trans); + return(NULL); + } + + trans->qh = qh; + trans->td_list = td; + qh->depth.link = LINK_ADDR(td); + qh->depth.terminate = 0; + qh->bredth.terminate=1; + } + + return(trans); +} + +transaction_t *add_transaction( transaction_t *trans, td_t *td) +{ + transaction_t *t1; + + + t1 = new_transaction(td); + if(!t1) + return(NULL); + + trans->next = t1; + trans->qh->bredth.terminate=0; + trans->qh->bredth.link = LINK_ADDR(t1->qh); + trans->qh->bredth.queue=1; + + return(trans); +} + +link_pointer_t *frame_list[MAX_CONTROLLERS]; +#if 0 +uchar fl_buffer[MAX_CONTROLLERS][8192]; +#endif + +void init_framelist(uchar dev) +{ + + int i; +#if 0 + DPRINTF("raw frame_list is at %x\n", fl_buffer[dev]); + frame_list[dev] = (link_pointer_t *) ((bus_to_virt)(((unsigned int)virt_to_bus(fl_buffer[dev]) & ~0xfff) + 0x1000)); +#else + frame_list[dev] = (link_pointer_t *) allot2(sizeof(link_pointer_t)*1024, 0xfff); // 4K alignment + if(frame_list[dev]==0) { + printf("init_framelist: no mem\n"); + } +#endif + memset(frame_list[dev], 0, 1024 * sizeof(link_pointer_t)); + + + DPRINTF("frame_list is at %x\n", frame_list[dev]); + + for(i=0;i<1024;i++) + frame_list[dev][i].terminate=1; + +} + + +extern int num_controllers; + +extern uint32_t hc_base[MAX_CONTROLLERS]; +extern uint8_t hc_type[MAX_CONTROLLERS]; + +void uhc_clear_stat() +{ + unsigned short value; + + value = inw(USBSTS(0)); + outw(value, USBSTS(0)); +} + +void clear_uport_stat(unsigned short port) +{ + unsigned short value; + + value = inw(port); + outw(value, port); +} + +void uport_suspend( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x1000; + outw( value, port); + +} + +void uport_wakeup( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x1000; + outw( value, port); + +} + +#if 0 +void uport_resume( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x40; + outw(value, port); + udelay(20000+usec_offset); + value &= ~0x40; + outw(value, port); + + do { + value = inw(port); + } while(value & 0x40); +} + +#endif +void uport_enable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x04; + outw( value, port); + + do { + value = inw(port); + } while( !(value & 0x04) && (value & 0x01)); + +} + + +void uport_disable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x04; + outw( value, port); +} + +void uport_reset(unsigned short port) +{ + unsigned short value; + int i; + + value = inw(port); + value |= 0x200; + + outw( value, port); + + for(i=0;i<5;i++) + udelay(10000+usec_offset); + + value &= ~0x200; + outw( value, port); + +// DPRINTF("Port %04x reset\n", port); +} + +void uport_reset_long(unsigned short port) +{ + unsigned short value; + int i; + + value = inw(port); + value |= 0x200; + outw( value, port); + + for(i=0; i<20; i++) + udelay(10000); + + value &= ~0x200; + outw( value, port); + +// DPRINTF("Port %04x reset\n", port); +} + +void uhc_reset(uchar controller) +{ + DPRINTF("Resetting UHCI\n"); + outw(0x04, USBCMD(controller)); + udelay(20000); + outw(0, USBCMD(controller)); +} +#if 0 +int uhc_stop(uchar dev) +{ + unsigned short tmp; + + tmp = inw(USBCMD(dev)); + tmp &= ~USBCMDRUN; + outw( tmp, USBCMD(dev)); + + while(! (inw(USBSTS(dev)) & USBSTSHALTED) ); + outw( USBSTSHALTED, USBSTS(dev)); // clear the status + + return(0); +} + +#endif + +int uhc_start(uchar dev) { + unsigned short tmp; + + DPRINTF("Starting UHCI\n"); + + tmp = inw(USBCMD(dev)); + tmp |= USBCMDRUN; + +// tmp |= USBCMD_DEBUG; + outw( tmp, USBCMD(dev)); + + return(0); +} + +int uhc_init(struct pci_device *dev) +{ + int16_t word; + + + pci_read_config_word(dev, 0x20, &word); + hc_base[num_controllers] = word; + hc_base[num_controllers] &= ~1; + + DPRINTF("Found UHCI at %04x\n", hc_base[num_controllers]); + uhc_reset(num_controllers); + + // set master + pci_read_config_word(dev, 0x04, &word); + word |= 0x04; + pci_write_config_word(dev, 0x04, word); + +#if 0 + if( ((unsigned int) virt_to_bus(frame_list[num_controllers])) != ( ( (unsigned int)virt_to_bus(frame_list[num_controllers])) & ~0x7ff) ) { + DPRINTF("UHCI: grave error, misaligned framelist (%x)\n", frame_list[num_controllers]); + return(-1); + } +#endif + + DPRINTF("uhc_init setting framelist to: %08x\n", (unsigned int) virt_to_bus( (frame_list[num_controllers]) )); + outl( (unsigned int) virt_to_bus(frame_list[num_controllers]), FLBASE(num_controllers)); + outw( 0, FRNUM(num_controllers)); + outw( 0, USBINTR(num_controllers)); // no interrupts! + + outw(0x1000, PORTSC1(num_controllers)); + outw(0x1000, PORTSC2(num_controllers)); + + uhc_start(num_controllers); + + dump_uhci(hc_base[num_controllers]); + + num_controllers++; + return(0); +} + +queue_head_t *sched_queue[MAX_CONTROLLERS]; +queue_head_t *term_qh[MAX_CONTROLLERS]; +//td_t *dummy_td[MAX_CONTROLLERS]; +td_t *loop_td[MAX_CONTROLLERS]; + +void init_sched(uchar dev) +{ + int i; + +// dummy_td[dev] = new_td(); + loop_td[dev] = new_td(); + term_qh[dev] = new_queue_head(); + + sched_queue[dev] = new_queue_head(); + sched_queue[dev]->bredth.terminate=0; + sched_queue[dev]->bredth.queue=1; + sched_queue[dev]->bredth.link=LINK_ADDR(term_qh[dev]); + sched_queue[dev]->depth.terminate=1; + + term_qh[dev]->bredth.terminate=1; + term_qh[dev]->depth.link = LINK_ADDR(loop_td[dev]); + term_qh[dev]->depth.terminate=0; + +// dummy_td->link.link = LINK_ADDR(sched_queue); +// dummy_td->link.queue = 1; +// dummy_td->link.depth=1; +// dummy_td->link.terminate=0; +// dummy_td->packet_type = IN_TOKEN; +// dummy_td->max_transfer = 0x7; +// dummy_td->isochronous=1; +// dummy_td->active=1; +// dummy_td->device_addr = 0x7f; +// dummy_td->endpoint=0x01; +// dummy_td->buffer = virt_to_bus(&dummy_td->data[2]); +// dummy_td->retrys=3; + +//dump_hex( (uchar *) dummy_td, sizeof(td_t), "dummy_td "); + + loop_td[dev]->link.link = LINK_ADDR(loop_td[dev]); + loop_td[dev]->link.terminate=0; + loop_td[dev]->link.queue=0; + loop_td[dev]->packet_type = IN_TOKEN; + loop_td[dev]->max_transfer=7; + loop_td[dev]->retrys=0; + loop_td[dev]->device_addr=0x7f; + + for(i=0; i< 1024; i++) { + frame_list[dev][i].link = LINK_ADDR(sched_queue[dev]); + frame_list[dev][i].queue=1; + frame_list[dev][i].terminate=0; +// frame_list[dev][i].terminate=1; + } + + dump_link( frame_list[dev], "frame_list_link: "); +// DPRINTF("dummy_td = %x\n", dummy_td[dev]); + +// dump_frame_list("sched:"); + +} + +void uhci_init(void) +{ + int i; + + init_td(); + init_qh(); + init_transactions(); + + for(i=0;i<MAX_CONTROLLERS; i++) { + if(hc_type[i] == 0x00) { + init_framelist(i); + init_sched(i); + } + } + + // From now should not change num_controllers any more +} + +int poll_queue_head( queue_head_t *qh) +{ + td_t *td; + int strikes=3; + + if(qh->depth.terminate) + return(1); + + while(strikes--) { + if(qh->depth.terminate) + return(1); + + td = MEM_ADDR(qh->depth.link); + + if(td->active) + return(0); + + udelay(1000); + +// if(!td->active) +// return(1); + } + + return(1); +} + +int wait_queue_complete( queue_head_t *qh) +{ + int ret; + int spins=1000; + + while( --spins && !(ret = poll_queue_head(qh))) { + udelay(1500); +// if(!(spins%30)) +// DPRINTF("wait_queue_complete: spin\n"); + } +// DPRINTF("wait_queue_complete: returning %d\n", ret); + + if(!spins) + return(-1); + + return(ret); +} + +#define BULK_DEPTH 1 + +transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + uchar dt; + transaction_t *trans; + td_t *td, *cur, *last; + int remaining = len; + uchar *pos = data; + int max; + uchar type = OUT_TOKEN; + int packet_length; + + + if(ep & 0x80) + type = IN_TOKEN; + + ep &= 0x7f; + + td = cur = last = NULL; + dt = usb_device[devnum].toggle[ep]; + max = usb_device[devnum].max_packet[ep]; + + while(remaining) { + cur = new_td(); + cur->packet_type = type; + cur->data_toggle = dt; + cur->endpoint = ep&0x7f; + cur->device_addr = devnum; + cur->detect_short=1; + cur->active=1; + dt = dt^0x01; + + if(!td){ + td = cur; + } + + if(last) { + last->link.terminate=0; + last->link.link = LINK_ADDR(cur); + } + + cur->buffer = (void *) virt_to_bus(pos); + + if(remaining>max) { + packet_length = max; + } + else { + packet_length = remaining; + } + + cur->max_transfer=packet_length-1; + cur->link.depth = BULK_DEPTH; + + remaining -= packet_length; + pos+= packet_length; + last = cur; + } + +// if( packet_length == max) { // if final packet wasn't short, add a zero packet +// cur = new_td(); +// dt = dt^0x01; +// cur->packet_type = type; +// cur->max_transfer = 0x7ff; // zero length code +// last->link.terminate=0; +// last->link.link = LINK_ADDR(cur); +// +// } + + cur->link.terminate=1; + + trans = new_transaction(td); + usb_device[devnum].toggle[ep] = dt; + + return(trans); +} + +#define DEPTH 0 + +transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data) +{ + td_t *td; + td_t *current_td; + td_t *last_td; + transaction_t *trans; + + ctrl_msg_t *message; + + unsigned char type; + int remaining = wLength; + uchar *pos = data; + uchar dt=1; + +// DPRINTF("ctrl_msg( %02x, %02x, %02x, %04x, %04x, %04x, %p)\n", devnum, request_type, request, wValue, wIndex, wLength, data); +// DPRINTF("%d bytes in payload\n", remaining); +// DPRINTF("lowspeed = %u\n", usb_device[devnum].lowspeed); + last_td = td = new_td(); + + td->packet_type = SETUP_TOKEN; + td->device_addr = devnum & 0x7f; + td->max_transfer = 7; // fixed for setup packets + td->retrys = CTRL_RETRIES; + td->active=1; + td->data_toggle=0; + td->link.depth=DEPTH; + td->detect_short=0; + td->interrupt=1; + td->lowspeed = usb_device[devnum].lowspeed; + +// steal 8 bytes from so-called software area to hole the control message itself + td->buffer = (void *) virt_to_bus(&(td->data[2])); + message = bus_to_virt( (unsigned int) td->buffer); + + message->bmRequestType = request_type; + message->bRequest = request; + message->wValue = wValue; + message->wIndex = wIndex; + message->wLength = wLength; +//dump_hex(td, sizeof(td_t), "ctrl_msg:"); + trans = new_transaction(td); + + if(!trans) { + DPRINTF("ctrl_msg: couldn't allocate a transaction!\n"); + return(NULL); + } + + if(request_type & CONTROL_DIR_MASK) + type = IN_TOKEN; + else + type = OUT_TOKEN; + + while(remaining >0) { + int length; + +// DPRINTF("ctrl_msg loop %d remaining, maxpacket = %u\n", remaining, usb_device[devnum].max_packet[0]); + current_td = new_td(); + + last_td->link.link = LINK_ADDR(current_td); + last_td->link.terminate=0; + last_td->link.queue=0; + last_td->link.depth=DEPTH; + + + current_td->device_addr = devnum & 0x7f; + current_td->retrys = CTRL_RETRIES; + current_td->active=1; + current_td->data_toggle=dt; + current_td->link.depth=DEPTH; + current_td->lowspeed = usb_device[devnum].lowspeed; + current_td->detect_short=1; + + dt = dt^0x01; + + current_td->packet_type = type; +// if(type == IN_TOKEN) +// current_td->detect_short=1; + + if(remaining >usb_device[devnum].max_packet[0]) + length = usb_device[devnum].max_packet[0]; + else + length = remaining; + + current_td->max_transfer = length-1; + current_td->buffer = (void *) virt_to_bus(pos); + remaining -= length; + pos += length; + + last_td = current_td; + } + + current_td = new_td(); + + current_td->device_addr = devnum & 0x7f; + current_td->retrys = CONTROL_STS_RETRIES; + current_td->active=1; + current_td->lowspeed = usb_device[devnum].lowspeed; + + if(type == IN_TOKEN) + current_td->packet_type = OUT_TOKEN; + else + current_td->packet_type = IN_TOKEN; + + current_td->max_transfer=0x7ff; + + current_td->link.terminate=1; + current_td->data_toggle=1; + current_td->link.depth=DEPTH; + + + last_td->link.link = LINK_ADDR(current_td); + last_td->link.terminate=0; + last_td->link.queue=0; + last_td->link.depth=DEPTH; + + return(trans); +} + + +int schedule_transaction( uchar dev, transaction_t *trans) +{ + unsigned short value; + + if(!sched_queue[dev]->depth.terminate) + return(-EBUSY); + + sched_queue[dev]->depth.link = LINK_ADDR(trans->qh); + sched_queue[dev]->depth.terminate = 0; + sched_queue[dev]->depth.queue=1; + + if(hc_type[dev]==0x00) { + value = inw(hc_base[dev]); + value |=1; + outw( value, hc_base[dev]); + } +#if 0 + else if (hc_type[dev]==0x10) { + uint32_t value; + ohci_regs_t *ohci_regs = (ohci_regs_t *) hc_base[dev]; + value = readl(&ohci_regs->control); + value |=OHCI_USB_OPER; + writel( value, &ohci_regs->control); + + } +#endif + + return(0); +} + +int wait_transaction( transaction_t *trans) +{ + queue_head_t *qh; + + qh = trans->qh; + + while(!qh->bredth.terminate) + qh = MEM_ADDR(qh->bredth.link); + + return( wait_queue_complete(qh)); +} + +void unlink_transaction( uchar dev, transaction_t *trans) +{ + sched_queue[dev]->depth.terminate=1; + sched_queue[dev]->depth.link = 0; // just in case +} + +int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + transaction_t *trans; + td_t *td; + int data_len; + int ret; + uchar *buffer; + DPRINTF("bulk_transfer: ep = %x len=%d\n", ep, len); +#if ALLOCATE==1 + buffer = allot2(2048, 0x7ff); + if(buffer==0){ + printf("bulk_transfer: can not allot\n"); + } + memset(buffer,0,2048); +// DPRINTF("bulk_transfer: buffer(virt) = %x buffer(phys) = %x len = %d\n", buffer, virt_to_phys(buffer), len); + + if( !(ep & 0x80)) + memcpy(buffer, data, len); +#else + buffer = data; +#endif + + + trans = _bulk_transfer(devnum, ep, len, buffer); +#if 0 +#ifdef DEBUG + dump_transaction(trans, "bulk_transfer:"); +#endif +#endif + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_bulk_transaction: "); +#endif + unlink_transaction( usb_device[devnum].controller, trans); + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + unlink_transaction( usb_device[devnum].controller, trans); + + data_len=0; + td = trans->td_list; + do { + if(td->active) + break; + + if(td->max_transfer == 0x7ff) + break; + + data_len += td->actual +1; + + if(td->actual < td->max_transfer) // short packet also check for errors here + break; + + if(!td->link.terminate){ + td = MEM_ADDR(td->link.link); + } + else { + td=NULL; + } + } while(td); +#if 0 + +#ifdef DEBUG + dump_td(trans->td_list, "bulk_transfer_success:"); +#endif +#endif + + if(data_len < len) { + DPRINTF("bulk_transfer( dev= %d, ep = %d, len = %d, buffer = %x) = %d:short transaction:\n", devnum, ep, len, data, data_len); + dump_td(trans->td_list, "short_transaction:"); + } + + free_transaction(trans); + +#if ALLOCATE==1 + if( (ep & 0x80)) + memcpy(data, buffer, len); + forget2(buffer); +#endif + + + DPRINTF("bulk_transfer returning %d\n", data_len); + return(data_len); +} + +int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data) +{ + transaction_t *trans; + td_t *td; + int data_len=0; + uchar *buffer; + int ret; + DPRINTF("uhci_control_msg: request_type = %x request = %x wLength=%d\n", request_type, request, wLength); +#if ALLOCATE==1 +// if( (wLength!=0) && (data!=NULL) ) { + buffer = allot2(2048+wLength,0x7ff); + if(buffer==0){ + printf("uhci_control_msg: can not allot\n"); + } + + memset(buffer,0,2048+wLength); + //DPRINTF("uhci_control_msg: buffer(virt) = %x buffer(phys) = %x wLength=%d\n", buffer, virt_to_phys(buffer), wLength); + if( !(request_type & 0x80)) + memcpy(buffer, data, wLength); +// } else { +// buffer=NULL; +// } + +#else + buffer = data; +#endif + + trans = ctrl_msg(devnum, request_type, request, wValue, wIndex, wLength, buffer); + if(!trans) { + DPRINTF("uhci_control_msg: ctrl_msg failed!\n"); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_transaction: "); +#endif + unlink_transaction( usb_device[devnum].controller, trans); + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(ret); + } + +//#ifdef DEBUG +// dump_td(trans->td_list, "success: "); +//#endif + + unlink_transaction( usb_device[devnum].controller, trans); + + // now, see what happened + + if(!trans->qh->depth.terminate) { +// handle setup error + + dump_uhci(hc_base); + dump_td(trans->td_list, "qh->depth failed_transaction: "); + + free_transaction(trans); +#if ALLOCATE==1 + forget2(buffer); +#endif + return(-1); + } + + td = trans->td_list; + + do { + if(td->packet_type != SETUP_TOKEN) + data_len += td->actual; + + if(td->actual < td->max_transfer) // short packet also check for errors here + break; + + if(!td->link.terminate) { + td = MEM_ADDR(td->link.link); + } + else { + td=NULL; + } + } while(td); + + free_transaction(trans); + +#if ALLOCATE==1 + if ( (wLength!=0) && (data!=NULL)){ + if( (request_type & 0x80)) + memcpy(data, buffer, wLength); + forget2(buffer); + } +#endif + + DPRINTF("usb_control_message returning %d\n", data_len); + + return(data_len); +} + + +int poll_u_root_hub(unsigned short port, uchar controller) +{ + ushort value; + int addr=0; + int i; + static int do_over=0; + + value = inw(port); + + debug("poll_u_root_hub1 v=%08x\t", value); + + if(value & 0x02 || do_over == port) { + debug("poll_u_root_hub2 v=%08x\t", value); + do_over=0; + if(value & 0x01 ) { // if port connected + debug("poll_u_root_hub21 v=%08x\t", value); + DPRINTF("Connection on port %04x\n", port); + + outw(value, port); + for(i=0; i<40; i++) { + udelay(10000+usec_offset); + value = inw(port); + if(value & 0x02) { + outw(value, port); + i=0; + DPRINTF("BOUNCE!\n"); + } + } + + uport_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + + uport_reset(port); + udelay(10); + uport_enable(port); + + if(!value & 0x01) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & 0x100); + + if(addr<0) { + uport_disable(port); + udelay(20000); +// uport_reset(port); + uport_reset_long(port); + uport_suspend(port); + do_over=port; + uhc_clear_stat(); +// dump_uhci(0x38c0); + } + } else { + uport_suspend(port); + uport_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + + return(addr); +} + +#endif diff --git a/src/filo/usb/uhci.h b/src/filo/usb/uhci.h new file mode 100644 index 000000000..17370a13d --- /dev/null +++ b/src/filo/usb/uhci.h @@ -0,0 +1,175 @@ +#ifndef _UHCI_H +#define _UHCI_H + +/* + * The link pointer is multi use. Some fields are valid only for some uses. + * In other cases, they must be 0 + * + */ + +#define MAX_POLLDEV 10 + +#define MAX_TRANSACTIONS 10 +#define MAX_QUEUEHEAD 255 +#define MAX_TD 1024 + + +typedef struct link_pointer { + unsigned long terminate:1; + unsigned long queue:1; + unsigned long depth:1; + unsigned long reserved:1; + unsigned long link:28; +} __attribute__((packed)) link_pointer_t; + +extern link_pointer_t *frame_list[]; + +void init_framelist(uchar dev); + + +#define SETUP_TOKEN 0x2d +#define IN_TOKEN 0x69 +#define OUT_TOKEN 0xe1 + +#define CTRL_RETRIES 3 +#define CONTROL_STS_RETRIES 0 + + +// some port features +#define PORT_CONNECTION 0 +#define PORT_ENABLE 1 +#define PORT_SUSPEND 2 +#define PORT_OVER_CURRENT 3 +#define PORT_RESET 4 +#define PORT_POWER 8 +#define PORT_LOW_SPEED 9 +#define C_PORT_CONNECTION 16 +#define C_PORT_ENABLE 17 +#define C_PORT_SUSPEND 18 +#define C_PORT_OVER_CURRENT 19 +#define C_PORT_RESET 20 + +// features +#define FEATURE_HALT 0 + +typedef struct td { + + link_pointer_t link; + + unsigned long actual:11; // actual length + unsigned long reserved2:5; + +// status/error flags + unsigned long res1:1; + unsigned long bitstuff:1; + unsigned long crc:1; + unsigned long nak:1; + unsigned long babble:1; + unsigned long buffer_error:1; + unsigned long stall:1; + unsigned long active:1; + + unsigned long interrupt:1; // interrupt on complete + unsigned long isochronous:1; + unsigned long lowspeed:1; + unsigned long retrys:2; + unsigned long detect_short:1; + unsigned long reserved3:2; + + unsigned long packet_type:8; // one of in (0x69), out (0xe1) or setup (0x2d) + unsigned long device_addr:7; + unsigned long endpoint:4; + unsigned long data_toggle:1; + unsigned long reserved:1; + unsigned long max_transfer:11; // misnamed. Desired length might be better + + void *buffer; + unsigned long data[4]; // free use by driver +} __attribute__((packed)) td_t; + +typedef struct queue_head { + link_pointer_t bredth; // depth must = 0 + link_pointer_t depth; // depth may vary randomly, ignore + unsigned long int udata[2]; +} __attribute__((packed)) queue_head_t; + +typedef struct transaction { + queue_head_t *qh; + td_t *td_list; + struct transaction *next; +} transaction_t; + +//##################################################### +int wait_head( queue_head_t *head, int count); + +extern queue_head_t *free_qh; +extern queue_head_t *queue_heads; + +queue_head_t *new_queue_head(void); +void free_queue_head( queue_head_t *qh); +void init_qh(void); + +extern td_t *free_td_list; +extern td_t *tds; + +void init_td(void); +td_t *new_td(void); +td_t *find_last_td(td_t *td); +void free_td( td_t *td); +link_pointer_t *queue_end( queue_head_t *queue); +void add_td( queue_head_t *head, td_t *td); + +extern transaction_t transactions[MAX_TRANSACTIONS]; +extern transaction_t *free_transactions; + +void init_transactions(void); +void free_transaction( transaction_t *trans ); +transaction_t *new_transaction(td_t *td); +transaction_t *add_transaction( transaction_t *trans, td_t *td); + + +#define USBCMD(x) hc_base[x] +#define USBSTS(x) (hc_base[x] + 0x02) +#define USBINTR(x) (hc_base[x] + 0x04) +#define FRNUM(x) ( hc_base[x] + 0x06) +#define FLBASE(x) ( hc_base[x] + 0x08) +#define SOFMOD(x) ( hc_base[x] + 0x0c) +#define PORTSC1(x) ( hc_base[x] + 0x10) +#define PORTSC2(x) ( hc_base[x] + 0x12) + +#define USBCMDRUN 0x01 +#define USBCMD_DEBUG 0x20 + +#define USBSTSHALTED 0x20 + + +void hc_reset(uchar dev); +int hc_stop(void); +int hc_start(uchar dev); + +extern queue_head_t *sched_queue[]; + +void init_sched(uchar dev); +int poll_queue_head( queue_head_t *qh); +int wait_queue_complete( queue_head_t *qh); + +extern int num_polls; +extern int (*devpoll[MAX_POLLDEV])(uchar); +extern uchar parm[MAX_POLLDEV]; + +transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data); +int schedule_transaction( uchar dev, transaction_t *trans); +int wait_transaction( transaction_t *trans); +void unlink_transaction( uchar dev, transaction_t *trans); +int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data); + + +// defined in uhci.c +int uhc_init(struct pci_device *dev); +void uhci_init(void); +void clear_uport_stat(unsigned short port); +int poll_u_root_hub(unsigned short port, uchar controller); + +#endif diff --git a/src/filo/usb/usb.c b/src/filo/usb/usb.c new file mode 100644 index 000000000..23afe9a71 --- /dev/null +++ b/src/filo/usb/usb.c @@ -0,0 +1,803 @@ +#ifdef USB_DISK + +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + + +#include "usb.h" +#include "uhci.h" +#include "ohci.h" +#include "debug_x.h" + + +#define ALLOCATE 1 + +int usec_offset=0; + +int num_controllers=0; + +uint32_t hc_base[MAX_CONTROLLERS]; +uint8_t hc_type[MAX_CONTROLLERS]; + + +void hci_init(void) +{ + int i; + struct pci_device *dev; + uint8_t prog_if; + + + for(i=0;i<MAX_CONTROLLERS; i++) { + hc_type[i] = 0xff; + } + + /* Find a PCI_SERIAL_USB class device */ + i=0; + num_controllers = 0; + while(num_controllers<MAX_CONTROLLERS) { + dev = pci_find_device(-1, -1, 0x0c03, -1, i); + if(!dev) break; + + prog_if = ((dev->class>>8) & 0xff); + if(prog_if == 0x00 ) { // UHCI + hc_type[num_controllers] = prog_if; + uhc_init(dev); + } + else if(prog_if == 0x10) { // OHCI + hc_type[num_controllers] = prog_if; + ohc_init(dev); + } +#if 0 + else if(prog_if == 0x20) { // EHCI + hc_type[num_controllers] = prog_if; + ehc_init(dev); + } +#endif + i++; + } + // From now should not change num_controllers any more + + uhci_init(); + ohci_init(); +} + + +int next_usb_dev; +usbdev_t usb_device[MAX_USB_DEV]; + +void init_devices(void) +{ + + memset(usb_device,0,sizeof(usb_device)); + usb_device[0].max_packet[0] = 8; + next_usb_dev=2; // 0 for all controller root hub, use MAX_CONTROLLERS instead??? + // do we need have one for every controller ?? or just use hc_base and hc_type instead + // For example 0 --> controller 1 root hub + // 1 --> controller 2 root hub + // 2 --> controller 3 root hub.... +} + + +inline int set_address( uchar address) +{ + int ret; + + ret = usb_control_msg(0, 0, SET_ADDRESS, address, 0, 0, NULL); + + return(ret); +} + +inline int clear_stall(uchar device, uchar endpoint) +{ + int ret; + + ret = usb_control_msg(device, CONTROL_ENDPOINT, CLEAR_FEATURE, FEATURE_HALT, endpoint, 0, NULL); + if(hc_type[device]==0x00) { + usb_device[device].toggle[endpoint]=0; + } + else if(hc_type[device]==0x10) { + usb_settoggle(&usb_device[device], endpoint & 0xf, ((endpoint & 0x80)>>7)^1, 0); + } + + return(ret); +} + +inline int device_reset(uchar device) { + return usb_control_msg(device, 0x21, 0xff, 0, 0, 0, NULL); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// String Descriptors +// +////////////////////////////////////////////////////////////////////////////////////// + +#define STRING_DESCRIPTOR 0x0300 + +int get_string( uchar addr, uchar string, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + ushort lang; + + if(!string) { + strcpy(buffer, "unknown"); + return(0); + } + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, 0, 4, buffer); + real_len = buffer[0]; + if(real_len>len) + real_len = len; + + lang = buffer[2] | buffer[3]<<8; + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, lang, real_len, buffer); + + // de-unicode it! + for(i=0, j=2; j<real_len; i++, j+=2) + buffer[i] = buffer[j]; + + buffer[i]=0; + real_len/=2; + + return(real_len); +} + +int get_string2( uchar addr, uchar string, ushort lang, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, lang, len, buffer); + + real_len = buffer[0]; + if(real_len>len) + real_len = len; + + if(real_len<=4) { + strcpy(buffer, "USB"); + real_len = 3; + buffer[real_len] = 0; + } else { + // de-unicode it! + for(i=0, j=2; j<real_len; i++, j+=2) + buffer[i] = buffer[j]; + + buffer[i]=0; + real_len/=2; + } + + return(real_len); +} +ushort get_lang( uchar addr, uchar string, int len, uchar *buffer) +{ + int ret; + int i,j; + int real_len; + ushort lang; + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, STRING_DESCRIPTOR | string, 0, 4, buffer); + lang = buffer[2] | buffer[3]<<8; + + return lang; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// HUB functions. This will be moved to it's own module soonishly +// +/////////////////////////////////////////////////////////////////////////////////////// + +typedef struct port_charge { + ushort c_port_connection:1; + ushort c_port_enable:1; + ushort c_port_suspend:1; + ushort c_port_over_current:1; + ushort c_port_reset:1; + ushort reserved:11; +} port_change_t; + +typedef struct port_status { + ushort port_connection:1; + ushort port_enable:1; + ushort port_suspend:1; + ushort port_over_current:1; + ushort port_reset:1; + ushort reserved:3; + ushort port_power:1; + ushort port_lowspeed:1; + ushort port_highspeed:1; + ushort port_test:1; + ushort port_indicator:1; +} __attribute__ ((packed)) portstatus_t; + + +typedef struct portstat { + portstatus_t stat; + port_change_t change; +} __attribute__ ((packed)) portstat_t; + +int hub_port_reset( uchar addr, uchar port) +{ + int ret; + int tries=100; + portstat_t status; + + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_RESET, port, 0, NULL); // reset port + + while(tries--) { + udelay(10000); + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, port, 4, &status); + if(!status.change.c_port_reset) + continue; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_RESET, port, 0, NULL); // clear status + return(0); + } + + DPRINTF("hub_port_reset(%x, %x) failed,\n", addr, port); + dump_hex((uint8_t *)&status, 4, "status="); + + return(-1); +} + +int hub_port_resume( uchar addr, uchar port) +{ + int ret; + int tries=100; + portstat_t status; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, PORT_SUSPEND, port, 0, NULL); // reset port + + while(tries--) { + udelay(10000); + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, port, 4, &status); + if(!status.change.c_port_suspend) + continue; + + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_SUSPEND, port, 0, NULL); // clear status + return(0); + } + + return(-1); +} + +int poll_hub(uchar addr) +{ + int i; + int ret; + uchar devaddr=0; + hub_descriptor_t *desc; + portstat_t status; + + DPRINTF("Poll hub (%x)\n", addr); + desc = usb_device[addr].private; + + for(i=1; i<= desc->bNbrPorts; i++) { + ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, i, 4, &status); +// DPRINTF("Get status for port %u returns: %d\n", i, ret); +// dump_hex(&status, 4, "status="); + + if(status.change.c_port_connection) { + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, C_PORT_CONNECTION, i, 0, NULL); // clear status + + if(status.stat.port_connection) { + udelay(desc->bPwrOn2PwrGood * 20000); + + hub_port_resume(addr, i); + + ret = hub_port_reset(addr,i); + udelay(10); + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_ENABLE, i, 0, NULL); // enable port + +// ret = usb_control_msg(addr, 0xa3, GET_STATUS, 0x0, i, 4, &status); +// DPRINTF("*****Get status again for port %u returns: %d\n", i, ret); +// dump_hex(&status, 4, "status="); + + devaddr = configure_device(i, usb_device[addr].controller, status.stat.port_lowspeed); + + // configure + } else { + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_SUSPEND, i, 0, NULL); // suspend port + ret = usb_control_msg(addr, 0x23, CLEAR_FEATURE, PORT_ENABLE, i, 0, NULL); // disable port + DPRINTF("Hub %d, Port %04x disconnected\n", addr, i); + // deconfigure + } + } + } + return(devaddr); + +} + +int usb_hub_init( uchar addr) +{ + int i; + int ret; + hub_descriptor_t *desc; + + desc = allot(sizeof(hub_descriptor_t)); + + memset(desc, 0 , sizeof(hub_descriptor_t)); + + DPRINTF("hub init (%d)\n", addr); + + ret = usb_control_msg(addr, 0xa0, GET_DESCRIPTOR, 0x2900, 0, 8, desc); + ret = usb_control_msg(addr, 0xa0, GET_DESCRIPTOR, 0x2900, 0, desc->bLength, desc); + + usb_device[addr].private = desc; + + for(i=1; i<=desc->bNbrPorts; i++) + ret = usb_control_msg(addr, 0x23, SET_FEATURE, PORT_POWER, i, 0, NULL); // power port + + + // register hub to be polled + + devpoll[num_polls] = poll_hub; + parm[num_polls++] = addr; + + return(0); +} + +extern void ohci_dump_x(uchar controller); + +// will set up whatever device is answering at address 0. +int configure_device(uint32_t port, uchar controller, unsigned int lowspeed) +{ + device_descriptor_t *desc; + config_descriptor_t *conf; + interface_descriptor_t *iface; + endpoint_descriptor_t *epd; + int ret; + int i; + int addr = next_usb_dev++; + uchar buffer[512]; + uchar string[255]; + ushort lang; + uchar x[2]; + + desc = (device_descriptor_t *) buffer; + + memset( &usb_device[addr], 0, sizeof(usbdev_t)); + + printf("New USB device, setting address %d\n", addr); + if(lowspeed) { + usb_device[addr].lowspeed = usb_device[0].lowspeed = 1; + DPRINTF("LOWSPEED\n"); + } else + usb_device[addr].lowspeed = usb_device[0].lowspeed = 0; + + usb_device[0].port = usb_device[addr].port = port; + usb_device[0].controller = usb_device[addr].controller = controller; + usb_device[addr].toggle2[0]=0; + usb_device[addr].toggle2[1]=0; + +// hc_clear_stat(); + + ret = set_address(addr); + if(ret<0) { + DPRINTF("configure_device: set_address failed!\n"); + next_usb_dev--; + return(-1); + } + + mdelay(10); /* Let the SET_ADDRESS settle */ + + usb_device[addr].max_packet[0] = 8; + + + DPRINTF("Fetching device descriptor length\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x100, 0, 8, desc); + + usb_device[addr].max_packet[0] = desc->max_packet; + + DPRINTF("Fetching device descriptor\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x100, 0, desc->bLength, desc); + if(ret < desc->bLength) + return(-1); + + DPRINTF("Fetching config descriptor length\n"); + conf = (config_descriptor_t *) (buffer + sizeof(device_descriptor_t)); + + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x200, 0, 8, conf); + + DPRINTF("Fetching config descriptor\n"); + ret = usb_control_msg(addr, 0x80, GET_DESCRIPTOR, 0x200, 0, conf->wTotalLength, conf); + if(ret < conf->wTotalLength) + return(-1); + + iface = (interface_descriptor_t *) (buffer + sizeof(device_descriptor_t) + conf->bLength); + epd = (endpoint_descriptor_t *) (buffer + conf->bLength + iface->bLength + sizeof(device_descriptor_t)); + + DPRINTF("device:\n"); + dump_device_descriptor( desc, ""); + DPRINTF("config:\n"); + dump_config_descriptor( (uchar *)conf, ""); + + DPRINTF("Selecting Configuration number %x:\n", conf->bConfigurationValue); + ret = usb_control_msg(addr, 0, SET_CONFIGURATION, conf->bConfigurationValue, 0, 0, NULL); + +// mdelay(20); + +#if 0 + usb_control_msg(addr, 0x80, GET_CONFIGURATION, 0, 0, 1 , x); + DPRINTF("Configuration number = %x\n", x[0]); + + usb_control_msg(addr, 0x80, GET_STATUS, 0, addr, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); + + usb_control_msg(addr, 0x81, GET_STATUS, 0, 0, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); +#endif + + for(i=0; i<iface->bNumEndpoints;i++) { + if(!epd[i].bEndpointAddress) { + usb_device[addr].max_packet[ 1 ] = epd[i].wMaxPacketSize & 0x3ff; + } else { + usb_device[addr].max_packet[ epd[i].bEndpointAddress & 0x7f ] = epd[i].wMaxPacketSize & 0x3ff; + } + + if( (epd[i].bmAttributes & 0x03) == 0x01) // interrupt + usb_device[addr].interrupt = epd[i].bEndpointAddress; + + if( (epd[i].bmAttributes & 0x03) == 0x02) { // bulk +#if 0 + DPRINTF("clear stall on ep=%x\n", epd[i].bEndpointAddress); + clear_stall(addr, epd[i].bEndpointAddress); // to reset data toggle + udelay(10); +#endif + +#if 0 + usb_control_msg(addr, 0x82, GET_STATUS, 0, epd[i].bEndpointAddress, 2, x); + DPRINTF("status = %x %x\n", x[0], x[1]); +#endif + + if(epd[i].bEndpointAddress & 0x80){ //in + usb_device[addr].bulk_in = epd[i].bEndpointAddress; + } + else { //out + usb_device[addr].bulk_out = epd[i].bEndpointAddress; + } + } + + } + + // determine device class + if(desc->Class) { + usb_device[addr].class = desc->Class; + usb_device[addr].subclass = desc->SubClass; + usb_device[addr].protocol = desc->protocol; + } else { + usb_device[addr].class = iface->bInterfaceClass; + usb_device[addr].subclass = iface->bInterfaceSubClass; + usb_device[addr].protocol = iface->bInterfaceProtocol; + } + + printf("%02x:%02x:%02x\n", usb_device[addr].class, usb_device[addr].subclass, usb_device[addr].protocol); +#if 0 + get_string(addr, desc->iManufacturor, sizeof(string), string); + printf("Manufacturor: %s\n", string); + + get_string(addr, desc->iProduct, sizeof(string), string); + printf("Product: %s\n", string); + + get_string(addr, desc->iSerial, sizeof(string), string); + printf("Serial: %s\n", string); +#else + lang = get_lang(addr, 0, sizeof(string), string); + + get_string2(addr, desc->iManufacturor, lang, sizeof(string), string); + printf("Manufacturor: %s\n", string); + + get_string2(addr, desc->iProduct, lang,sizeof(string), string); + printf("Product: %s\n", string); + + get_string2(addr, desc->iSerial, lang, sizeof(string), string); + printf("Serial: %s\n", string); +#endif + + switch( usb_device[addr].class) { + case 0x09: // hub + usb_hub_init(addr); + break; + + default: + break; + + } + + DPRINTF("DEVICE CONFIGURED\n"); + + return(addr); +} + +int num_polls=0; +int (*devpoll[MAX_POLLDEV])(uchar); +uchar parm[MAX_POLLDEV]; + +int poll_usb() +{ + int addr; + int found=0; + int i; + int j; + + for(i=0; i<num_controllers; i++) { + debug("poll_usb1 i=%d\t", i); + // if addr >0, should probably see what was attached! + if(hc_type[i]==0x00) { + addr = poll_u_root_hub(PORTSC1(i), i); + if(addr && !found) + found=addr; + + addr = poll_u_root_hub(PORTSC2(i), i); + if(addr && !found) + found=addr; + } + + else if(hc_type[i]==0x10) { + int NDP; + NDP = readl(&ohci_regs->roothub.a) & 0xff; + ohci_regs = (ohci_regs_t *)hc_base[i]; + for(j=0;j<NDP;j++) { + addr = poll_o_root_hub((uint32_t)&ohci_regs->roothub.portstatus[j], i); + if(addr && !found) + found=addr; + } + + } + + } + + // now poll registered drivers (such as the hub driver + for(i=0;i<num_polls; i++) { + debug("poll_usb2 i=%d\t", i); + addr = devpoll[i](parm[i]); + if(addr && !found) + found=addr; + } + + return(found); +} + + +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + uint8_t hc_num = usb_device[devnum].controller; + if(ep&0x80) { + ep = usb_device[devnum].bulk_in; + } else { + ep = usb_device[devnum].bulk_out; + } + + if(hc_type[hc_num] == 0x00) { //UHCI + return uhci_bulk_transfer(devnum, ep, len, data); + } + else if( hc_type[hc_num] == 0x10 ) { //OHCI + return ohci_bulk_transfer(devnum, ep, len, data); + } +#if 0 + else if (hc_type[hc_num] == 0x20 ) { //EHCI + return ehci_bulk_transfer(devnum, ep, len, data); + } +#endif + return 0; +} +int usb_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, + unsigned short wLength, void *data) +{ + + uint8_t hc_num = usb_device[devnum].controller; + + if(hc_type[hc_num] == 0x00) { //UHCI + return uhci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } + else if( hc_type[hc_num] == 0x10 ) { //OHCI + return ohci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } +#if 0 + else if (hc_type[hc_num] == 0x20 ) { //EHCI + return ehci_control_msg(devnum, request_type, request, wValue, wIndex, wLength, data); + } +#endif + return 0; +} + + +struct urb *usb_alloc_urb(int controller) +{ + struct urb *urb; + ohci_t *ohci = NULL; +#if URB_PRE_ALLOCATE!=1 + urb = (struct urb *)allot2(sizeof(struct urb),0xff); + if (!urb) { + printf("usb_alloc_urb: allot2 failed"); + return NULL; + } +#else + if(hc_type[controller] == 0x10) { //OHCI + ohci = &_ohci_x[controller]; + urb = ohci->urb; + } else { + urb = NULL; + } +#endif + + memset(urb, 0, sizeof(*urb)); + + return urb; +} +/** + * usb_free_urb - frees the memory used by a urb + * @urb: pointer to the urb to free + * + * If an urb is created with a call to usb_create_urb() it should be + * cleaned up with a call to usb_free_urb() when the driver is finished + * with it. + */ +void usb_free_urb(struct urb* urb) +{ +#if URB_PRE_ALLOCATE!=1 + if (urb) + forget2(urb); +#endif +} + +void usb_wait_urb_done(struct urb* urb, int timeout) +{ + usbdev_t *usb_dev = urb->dev; + if(hc_type[usb_dev->controller]==0x10) { + ohci_wait_urb_done(urb, timeout); + } + +} + + +int usb_submit_urb(struct urb *urb) +{ + if (urb && urb->dev) { +#if 0 + if(hc_type[urb->dev->controller] == 0x00) { + return uhci_submit_urb(urb); + } else +#endif + if(hc_type[urb->dev->controller] == 0x10) { + return ohci_submit_urb(urb); + } +#if 0 + else if(hc_type[urb->dev->controller] == 0x20) { + return ohci_submit_urb(urb); + } +#endif + return 0; + } + else + return -ENODEV; +} + +// Starts urb and waits for completion or timeout +static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) +{ + int status; + status = usb_submit_urb(urb); + +//for OHCI We will check the BLF and CLF, because HC after processing all td list, it will clear the BLF and CLF + usb_wait_urb_done(urb, timeout); +//Add by LYH to call complete function + if(urb->complete!=0) urb->complete(urb); + + if (actual_length) + *actual_length = urb->actual_length; + + usb_free_urb(urb); + return status; +} +// returns status (negative) or length (positive) +int usb_internal_control_msg(struct usbdev *usb_dev, unsigned int pipe, + struct usb_ctrlrequest *cmd, void *data, int len, int timeout, usb_complete_t complete) +{ + struct urb *urb; + int retv; + int length; + + urb = usb_alloc_urb(usb_dev->controller); + if (!urb) + return -ENOMEM; + + FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, + complete,0); + + retv = usb_start_wait_urb(urb, timeout, &length); + if (retv < 0) + return retv; + else + return length; +} +int usb_control_msg_x(struct usbdev *dev, unsigned int pipe, u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size, int timeout, usb_complete_t complete) +{ + struct usb_ctrlrequest *dr; + int ret; + int controller = dev->controller; + ohci_t *ohci; + +#if URB_PRE_ALLOCATE!=1 + dr = allot2(sizeof(struct usb_ctrlrequest), 0xf); + if (!dr) { + printf("usb_control_msg_x: dr allocate no MEM\n"); + return -ENOMEM; + } +#else + if(hc_type[controller] == 0x10) { //OHCI + ohci = &_ohci_x[controller]; + dr = ohci->dr; + } else { + dr = NULL; + } + +#endif + + dr->bRequestType = requesttype; + dr->bRequest = request; + dr->wValue = cpu_to_le16p(&value); + dr->wIndex = cpu_to_le16p(&index); + dr->wLength = cpu_to_le16p(&size); + + ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout, complete); + +#if URB_PRE_ALLOCATE!=1 + forget2(dr); +#endif + + return ret; +} +int usb_bulk_msg_x(struct usbdev *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout, usb_complete_t complete) +{ + struct urb *urb; + + if (len < 0) + return -EINVAL; + + urb=usb_alloc_urb(usb_dev->controller); + if (!urb) + return -ENOMEM; + + FILL_BULK_URB(urb, usb_dev, pipe, data, len, + complete, 0); + + return usb_start_wait_urb(urb,timeout,actual_length); +} + +#endif diff --git a/src/filo/usb/usb.h b/src/filo/usb/usb.h new file mode 100644 index 000000000..58fd57347 --- /dev/null +++ b/src/filo/usb/usb.h @@ -0,0 +1,435 @@ +#ifndef _USB_H +#define _USB_H + +#define URB_PRE_ALLOCATE 1 + +#define u32 uint32_t +#define u16 uint16_t +#define u8 uint8_t + +#define uchar uint8_t +#define ushort uint16_t +#define EBUSY 1 +#define ENOMEM 12 +#define ENODEV 19 +#define EINVAL 22 +#define EINPROGRESS 115 + +#define LINK_ADDR(x) ( virt_to_bus(x) >> 4) +#define MEM_ADDR(x) (void *) ( bus_to_virt( ((unsigned int) (x)) <<4) ) + +#define MAX_CONTROLLERS 4 + +extern int num_controllers; + +extern uint32_t hc_base[]; +extern uint8_t hc_type[]; + +// Some control message bmRequestType defines +#define CTRL_DEVICE 0 +#define CONTROL_INTERFACE 1 +#define CONTROL_ENDPOINT 2 +#define CONTROL_OTHER 3 +#define CONTROL_RECIPIENT_MASK 0x1f + +#define CONTROL_TYPE_STD 0 +#define CONTROL_TYPE_CLASS 0x20 +#define CONTROL_CLASS_VENDOR 0x40 +#define CONTROL_CLASS_MASK 0x60 + +#define CONTROL_OUT 0 +#define CONTROL_IN 0x80 +#define CONTROL_DIR_MASK 0x80 + +// bRequest values +#define GET_STATUS 0 +#define CLEAR_FEATURE 1 +#define SET_FEATURE 3 +#define SET_ADDRESS 5 + +#define GET_DESCRIPTOR 6 +#define SET_DESCRIPTOR 7 + +#define GET_CONFIGURATION 8 +#define SET_CONFIGURATION 9 + +#define GET_INTERFACE 10 +#define SET_INTERFACE 11 + +#define SYNC_FRAME 12 + +// descriptor types +#define DEVICE_DESC 1 +#define CONFIGURATION_DESC 2 +#define STRING_DESC 3 +#define INTERFACE_DESC 4 +#define ENDPOINT_DESC 5 +#define OTHERSPEED_DESC 7 +#define POWER_DESC 8 + + +typedef struct device_descriptor { + uchar bLength; + uchar type; + + uchar bcdVersion[2]; + uchar Class; + uchar SubClass; + uchar protocol; + uchar max_packet; + + unsigned short idVendor; + unsigned short idProduct; + + uchar bcdDevice[2]; + uchar iManufacturor; + uchar iProduct; + uchar iSerial; + uchar bNumConfig; +} __attribute__((packed)) device_descriptor_t; + +#define GET_DESCRIPTOR 6 + +typedef struct config_descriptor { + uchar bLength; + uchar type; + + unsigned short wTotalLength; + uchar bNumInterfaces; + uchar bConfigurationValue; + uchar iConfiguration; + + uchar bmAttributes; + uchar bMaxPower; +} __attribute__((packed)) config_descriptor_t; + +typedef struct interface_descriptor { + uchar bLength; + uchar type; + + uchar bInterfaceNumber; + uchar bAlternateSetting; + + uchar bNumEndpoints; + uchar bInterfaceClass; + uchar bInterfaceSubClass; + uchar bInterfaceProtocol; + uchar iInterface; +} __attribute__((packed)) interface_descriptor_t; + +typedef struct endpoint_descriptor { + uchar bLength; + uchar type; + + uchar bEndpointAddress; + uchar bmAttributes; + unsigned short wMaxPacketSize; + uchar bInterval; +} __attribute__((packed)) endpoint_descriptor_t; + +typedef struct ctrl_msg { + uchar bmRequestType; + uchar bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +} __attribute__((packed)) ctrl_msg_t; + +// Some descriptors for hubs, will be moved later +typedef struct hub_descriptor { + uchar bLength; + uchar type; + + uchar bNbrPorts; + ushort wHubCharacteristics; + uchar bPwrOn2PwrGood; + uchar bHubCntrCurrent; + + uchar DeviceRemovable; // assume bNbrPorts <=8 + uchar PortPwrCntrMask; +} __attribute__((packed)) hub_descriptor_t; + +#define MAX_USB_DEV 127 +#define MAX_EP 8 + +typedef struct usbdev { + uint32_t port; + uchar address; + uchar controller; + uchar class; + uchar subclass; + uchar protocol; + uchar bulk_in; + uchar bulk_out; + uchar interrupt; + uchar lowspeed; + uint32_t toggle2[2]; //For OHCI + uint32_t halted[2]; + uchar toggle[MAX_EP]; //for UHCI + unsigned short max_packet[MAX_EP]; + void *private; +} usbdev_t; + +// I will use urb as transaction for OHCI to remember the td and ed + +struct urb; +typedef void (*usb_complete_t)(struct urb *); + +struct urb +{ +#if 0 + spinlock_t lock; // lock for the URB +#endif + void *hcpriv; // private data for host controller +#if 0 + struct list_head urb_list; // list pointer to all active urbs + struct urb *next; // pointer to next URB +#endif + struct usbdev *dev; // pointer to associated USB device + unsigned int pipe; // pipe information + int status; // returned status + unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | etc. + void *transfer_buffer; // associated data buffer + void *transfer_dma; // dma addr for transfer_buffer + int transfer_buffer_length; // data buffer length + int actual_length; // actual data buffer length + int bandwidth; // bandwidth for this transfer request (INT or ISO) + unsigned char *setup_packet; // setup packet (control only) + void * setup_dma; // dma addr for setup_packet + // + int start_frame; // start frame (iso/irq only) + int number_of_packets; // number of packets in this request (iso) + int interval; // polling interval (irq only) + int error_count; // number of errors in this transfer (iso only) + int timeout; // timeout (in jiffies) + // + void *context; // context for completion routine + usb_complete_t complete; // pointer to completion routine + // +#if 0 + struct iso_packet_descriptor iso_frame_desc[0]; +#endif +}; + +typedef struct urb urb_t; + +/* + * urb->transfer_flags: + */ +#define USB_DISABLE_SPD 0x0001 +#define URB_SHORT_NOT_OK USB_DISABLE_SPD +#define USB_ISO_ASAP 0x0002 +#define USB_ASYNC_UNLINK 0x0008 +#define USB_QUEUE_BULK 0x0010 +#define USB_NO_FSBR 0x0020 +#define USB_ZERO_PACKET 0x0040 // Finish bulk OUTs always with zero length packet +#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */ + /* ... less overhead for QUEUE_BULK */ +#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD! + + +struct usb_ctrlrequest { + u8 bRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; +} __attribute__ ((packed)); + +/* + * USB-status codes: + * USB_ST* maps to -E* and should go away in the future + */ + +#define USB_ST_NOERROR 0 +#define USB_ST_CRC (-EILSEQ) +#define USB_ST_BITSTUFF (-EPROTO) +#define USB_ST_NORESPONSE (-ETIMEDOUT) /* device not responding/handshaking */ +#define USB_ST_DATAOVERRUN (-EOVERFLOW) +#define USB_ST_DATAUNDERRUN (-EREMOTEIO) +#define USB_ST_BUFFEROVERRUN (-ECOMM) +#define USB_ST_BUFFERUNDERRUN (-ENOSR) +#define USB_ST_INTERNALERROR (-EPROTO) /* unknown error */ +#define USB_ST_SHORT_PACKET (-EREMOTEIO) +#define USB_ST_PARTIAL_ERROR (-EXDEV) /* ISO transfer only partially completed */ +#define USB_ST_URB_KILLED (-ENOENT) /* URB canceled by user */ +#define USB_ST_URB_PENDING (-EINPROGRESS) +#define USB_ST_REMOVED (-ENODEV) /* device not existing or removed */ +#define USB_ST_TIMEOUT (-ETIMEDOUT) /* communication timed out, also in urb->status**/ +#define USB_ST_NOTSUPPORTED (-ENOSYS) +#define USB_ST_BANDWIDTH_ERROR (-ENOSPC) /* too much bandwidth used */ +#define USB_ST_URB_INVALID_ERROR (-EINVAL) /* invalid value/transfer type */ +#define USB_ST_URB_REQUEST_ERROR (-ENXIO) /* invalid endpoint */ +#define USB_ST_STALL (-EPIPE) /* pipe stalled, also in urb->status*/ + +/** + * FILL_CONTROL_URB - macro to help initialize a control urb + * @URB: pointer to the urb to initialize. + * @DEV: pointer to the struct usb_device for this urb. + * @PIPE: the endpoint pipe + * @SETUP_PACKET: pointer to the setup_packet buffer + * @TRANSFER_BUFFER: pointer to the transfer buffer + * @BUFFER_LENGTH: length of the transfer buffer + * @COMPLETE: pointer to the usb_complete_t function + * @CONTEXT: what to set the urb context to. + * + * Initializes a control urb with the proper information needed to submit + * it to a device. This macro is depreciated, the usb_fill_control_urb() + * function should be used instead. + */ +#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \ + do {\ + (URB)->dev=DEV;\ + (URB)->pipe=PIPE;\ + (URB)->setup_packet=SETUP_PACKET;\ + (URB)->transfer_buffer=TRANSFER_BUFFER;\ + (URB)->transfer_buffer_length=BUFFER_LENGTH;\ + (URB)->complete=COMPLETE;\ + (URB)->context=CONTEXT;\ + } while (0) + + +/** + * FILL_BULK_URB - macro to help initialize a bulk urb + * @URB: pointer to the urb to initialize. + * @DEV: pointer to the struct usb_device for this urb. + * @PIPE: the endpoint pipe + * @TRANSFER_BUFFER: pointer to the transfer buffer + * @BUFFER_LENGTH: length of the transfer buffer + * @COMPLETE: pointer to the usb_complete_t function + * @CONTEXT: what to set the urb context to. + * + * Initializes a bulk urb with the proper information needed to submit it + * to a device. This macro is depreciated, the usb_fill_bulk_urb() + * function should be used instead. + */ +#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \ + do {\ + (URB)->dev=DEV;\ + (URB)->pipe=PIPE;\ + (URB)->transfer_buffer=TRANSFER_BUFFER;\ + (URB)->transfer_buffer_length=BUFFER_LENGTH;\ + (URB)->complete=COMPLETE;\ + (URB)->context=CONTEXT;\ + } while (0) + + +/* + * USB directions + */ +#define USB_DIR_OUT 0 /* to device */ +#define USB_DIR_IN 0x80 /* to host */ + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +#define PIPE_ISOCHRONOUS 0 +#define PIPE_INTERRUPT 1 +#define PIPE_CONTROL 2 +#define PIPE_BULK 3 + +#define usb_maxpacket(dev, pipe, out) ((dev)->max_packet[usb_pipeendpoint(pipe)]) +#define usb_packetid(pipe) (((pipe) & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT) + +#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) +#define usb_pipein(pipe) (((pipe) >> 7) & 1) +#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) +#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) +#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) +#define usb_pipedata(pipe) (((pipe) >> 19) & 1) +#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) +#define usb_pipetype(pipe) (((pipe) >> 30) & 3) +#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) +#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT) +#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL) +#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK) + +#define PIPE_DEVEP_MASK 0x0007ff00 + + +/* The D0/D1 toggle bits */ +#define usb_gettoggle(dev, ep, out) (((dev)->toggle2[out] >> (ep)) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle2[out] ^= (1 << (ep))) +static inline void usb_settoggle(struct usbdev *dev, + unsigned int ep, + unsigned int out, + int bit) +{ + dev->toggle2[out] &= ~(1 << ep); + dev->toggle2[out] |= bit << ep; +} + + +/* Endpoint halt control/status */ +#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1) +#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep))) +#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep))) +#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep))) + + +static inline unsigned int __create_pipe(usbdev_t *dev, unsigned int endpoint) +{ + return (dev->address << 8) | (endpoint << 15) | + ((dev->lowspeed == 1) << 26); +} + +static inline unsigned int __default_pipe(struct usbdev *dev) +{ + return ((dev->lowspeed == 1) << 26); +} + +/* Create various pipes... */ +#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#if 0 +#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#endif +#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#if 0 +#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#endif +#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev)) +#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev) | USB_DIR_IN) + + +extern int next_usb_dev; +usbdev_t usb_device[MAX_USB_DEV]; + +void init_devices(void); +void hci_init(void); +int hc_init(struct pci_device *dev); +inline int set_address(uchar address); +inline int clear_stall(uchar device, uchar endpoint); +int poll_usb(); +int configure_device(uint32_t port, uchar controller, unsigned int lowspeed); +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); +int usb_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, + unsigned short wLength, void *data); + +int usb_control_msg_x(struct usbdev *dev, unsigned int pipe, u8 request, u8 requesttype, + u16 value, u16 index, void *data, u16 size, int timeout, usb_complete_t complete); +int usb_bulk_msg_x(struct usbdev *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout, usb_complete_t complete); + +#endif diff --git a/src/filo/usb/usb_scsi_low.c b/src/filo/usb/usb_scsi_low.c new file mode 100644 index 000000000..20afecae5 --- /dev/null +++ b/src/filo/usb/usb_scsi_low.c @@ -0,0 +1,172 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#define uchar uint8_t + +//#include "debug_x.h" +#include "usb_scsi_low.h" + +int usb_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data); + +#define SG_DXFER_FROM_DEV -3 +#define SG_DXFER_TO_DEV -2 + +#define REQUEST_SENSE 0x03 + +#define CBW_SIG 0x43425355 + +typedef struct usb_cbw { + unsigned int signature; + unsigned int tag; + unsigned int transfer_len; // this is exclusive of cbw and csw + + uchar res1:7; + uchar direction:1; // 1 = device to host (read) + + uchar lun:4; + uchar res:4; + + uchar cbw_len:5; // the length of the SCSI command + uchar res3:3; + + uchar scsi_cmd[16]; +} __attribute__ ((packed)) usb_cbw_t; + +#define CSW_SIG 0x53425355 + +typedef struct usb_csw { + unsigned int signature; + unsigned int tag; + unsigned int residue; + uchar status; +} __attribute__ ((packed)) usb_csw_t; + + +int scsi_command( uchar device, unsigned char *cmd, int cmd_len, int direction, unsigned char *data, int data_len, char *sense_data, int sense_len) +{ + usb_cbw_t cbw; + usb_csw_t csw; + int ret; + + memset(&cbw,0,sizeof(usb_cbw_t)); + memset(&csw,0,sizeof(usb_csw_t)); + + cbw.signature = CBW_SIG; + cbw.tag = 777; + + memcpy(cbw.scsi_cmd, cmd, cmd_len); + cbw.cbw_len = cmd_len; + + if(direction == SG_DXFER_FROM_DEV) + cbw.direction=1; + + cbw.transfer_len = data_len; + + ret = usb_bulk_transfer(device, 2, sizeof(cbw), (uchar *) &cbw); + if(ret<0){ + DPRINTF("ERROR:Bulk write:\n"); + } + + if(data_len) { + if(cbw.direction) { + DPRINTF("scsi_command reading %d bytes\n", data_len); + ret = usb_bulk_transfer(device, 0x81, data_len, data); + DPRINTF("scsi_command read %d bytes\n", ret); + if(ret<0 || ret <data_len) { + DPRINTF("ERROR:Bulk read data ret = %d\n", ret); + } + } else { +// DPRINTF("scsi_command writing %u bytes\n", data_len); + ret = usb_bulk_transfer(device, 0x2, data_len, data); +// DPRINTF("scsi_command wrote %u bytes\n", ret); + if(ret<0) { + DPRINTF("ERROR:Bulk write data\n"); + } + } + } + +// DPRINTF("scsi_command fetching csw\n"); + ret = usb_bulk_transfer(device, 0x81, sizeof(csw), (uchar *) &csw); +// DPRINTF("scsi_command csw is %d bytes\n", ret); + if(ret<0 || ret < sizeof(csw)) { + DPRINTF("ERROR: Bulk read CSW ret = %d\n", ret); + return(-1); + } + + if(csw.status) { + DPRINTF("CSW: residue = %08x, status = %02x\n", csw.residue, csw.status); + DPRINTF("Getting sense data\n"); + request_sense( device, sense_data, sense_len); + return(-csw.status); + } + + return(data_len - csw.residue); +} + +int request_sense( uchar device, char *sense_data, int len) +{ + usb_cbw_t cbw; + usb_csw_t csw; + int ret; + + memset(&cbw,0,sizeof(usb_cbw_t)); + memset(&csw,0,sizeof(usb_csw_t)); + + cbw.signature = CBW_SIG; + cbw.tag = 666; + + cbw.scsi_cmd[0] = REQUEST_SENSE; + cbw.scsi_cmd[4] = len; + cbw.cbw_len = 6; + cbw.direction=1; + cbw.transfer_len = len; + + ret = usb_bulk_transfer(device, 2, sizeof(usb_cbw_t), (uchar *) &cbw); + if(ret<0 || ret < sizeof(usb_cbw_t)) { + DPRINTF("ERROR: sense Bulk write ret = %d\n", ret); + } + + ret = usb_bulk_transfer(device, 0x81, len, sense_data); + if(ret<0 || ret < len) { + DPRINTF("ERROR: sense Bulk read data ret = %d\n", ret); + } + + ret = usb_bulk_transfer(device, 0x81, sizeof(usb_csw_t), (uchar *) &csw); + if(ret<0 || ret < sizeof(usb_csw_t)) { + DPRINTF("ERROR: sense Bulk read CSW ret = %d\n", ret); + } + + return(-csw.status); +} + +#endif diff --git a/src/filo/usb/usb_scsi_low.h b/src/filo/usb/usb_scsi_low.h new file mode 100644 index 000000000..773614e43 --- /dev/null +++ b/src/filo/usb/usb_scsi_low.h @@ -0,0 +1,10 @@ +#ifndef _USB_SCSI_LOW_H +#define _USB_SCSI_LOW_H + +#define SG_DXFER_FROM_DEV -3 +#define SG_DXFER_TO_DEV -2 + +int scsi_command( unsigned char device, unsigned char *cmd, int cmd_len, int direction, unsigned char *data, int data_len, char *sense_data, int sense_len); +int request_sense( unsigned char device, char *sense_data, int len); + +#endif diff --git a/src/filo/usb/usb_x.c b/src/filo/usb/usb_x.c new file mode 100644 index 000000000..3da02d81f --- /dev/null +++ b/src/filo/usb/usb_x.c @@ -0,0 +1,163 @@ +#ifdef USB_DISK +/******************************************************************************* + * + * + * Copyright 2003 Steven James <pyro@linuxlabs.com> and + * LinuxLabs http://www.linuxlabs.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************************/ + +#include <etherboot.h> +#include <pci.h> +#include <timer.h> +#include <lib.h> + +#define DEBUG_THIS DEBUG_USB +#include <debug.h> + +#define DPRINTF debug + +#include "usb.h" +#include "scsi_cmds.h" + +struct usbdisk_info_t { + struct controller *ctrl; + uint16_t heads; + uint16_t cylinders; + uint16_t sectors_per_track; + uint8_t model_number[41]; + uint8_t slave; + sector_t sectors; + int address_mode; +#define ADDRESS_MODE_CHS 0 +#define ADDRESS_MODE_LBA 1 +#define ADDRESS_MODE_LBA48 2 +#define ADDRESS_MODE_PACKET 3 + uint32_t hw_sector_size; + unsigned drive_exists : 1; + unsigned slave_absent : 1; + unsigned removable : 1; + + unsigned char usb_device_address; +}; + +struct usbdisk_info_t usbdisk_info; + +#define TEST 0 + +#if TEST==1 +#include "usb_scsi_low.h" +typedef struct partition_entry { + uchar boot_flag; + + uchar chs[7]; + + unsigned int lba_start; + unsigned int lba_len; +} __attribute__ ((packed)) partition_entry_t; + +typedef struct partition { + char loader[446]; + partition_entry_t entry[4]; + char sig[2]; +} __attribute__ ((packed)) partition_t; +#endif + +int usb_probe(int drive) +{ + struct usbdisk_info_t *info = &usbdisk_info; +#if TEST==1 + partition_t part; + unsigned char sense_data[32]; +#endif + int i,res; + int error_count=100; + + printf("LinuxLabs USB bootloader\n"); + +// outb( 0x30, 0x70); // reset primary boot +// outb( 0xff, 0x71); + init_devices(); + hci_init(); + + info->usb_device_address = 0; + // find first usb device + + while(error_count && (res = poll_usb())) // keep polling usb until no more devices are enumerated + if(res<0) + if(!--error_count) + printf("There is a USB device, but it won't init! This is a bad thing.\n"); + + for(i=0; i< next_usb_dev ; i++) { + if(usb_device[i].class == 0x08 && usb_device[i].subclass == 0x06 && usb_device[i].protocol == 0x50) { + printf("Found USB block device %d\n", i); + if(drive==0) { + info->usb_device_address = i; + break; + } + drive--; + } + } + + if(info->usb_device_address == 0) return -1; + + UnitReady(info->usb_device_address); + +#if TEST==1 +//Test + printf("Requesting initial sense data\n"); + request_sense( info->usb_device_address, sense_data, 32); + PrintSense(sense_data, 32); + + res = ll_read_block(info->usb_device_address, (uint8_t *)&part, 0, 1); + + printf("ll_read_block returns %d\n", res); + + res=-1; + + debug("part address (phy) = %x, (virt) = %x\n", (uint32_t) virt_to_phys(&part), (uint32_t)&part); + + for(i=0; i<4; i++) { + printf("%d: boot=%02x, start=%08x length=%08x\n",i, part.entry[i].boot_flag, part.entry[i].lba_start, part.entry[i] +.lba_len); + } + + +#endif + + return 0; +} +int usb_read(int drive, sector_t sector, void *buffer) +{ + struct usbdisk_info_t *info = &usbdisk_info; + int result; + int blocknum = sector; + int i; +// printf("sector= %d\t", blocknum); + result = ll_read_block(info->usb_device_address, buffer,blocknum, 1); +#if 0 + for(i=0;i<128;i++) { + if((i%4)==0) printf("\n %08x:",i*4); + printf(" %08x ",(uint32_t)*((uint32_t *)buffer+i)); + } +#endif + + if(result!=512) return -1; + + return 0; +} +#endif |
