summaryrefslogtreecommitdiffstats
path: root/src/filo/usb
diff options
context:
space:
mode:
Diffstat (limited to 'src/filo/usb')
-rw-r--r--src/filo/usb/debug_x.c433
-rw-r--r--src/filo/usb/debug_x.h18
-rw-r--r--src/filo/usb/ohci.c1437
-rw-r--r--src/filo/usb/ohci.h316
-rw-r--r--src/filo/usb/scsi.h226
-rw-r--r--src/filo/usb/scsi_cmds.c512
-rw-r--r--src/filo/usb/scsi_cmds.h14
-rw-r--r--src/filo/usb/uhci.c1143
-rw-r--r--src/filo/usb/uhci.h175
-rw-r--r--src/filo/usb/usb.c803
-rw-r--r--src/filo/usb/usb.h435
-rw-r--r--src/filo/usb/usb_scsi_low.c172
-rw-r--r--src/filo/usb/usb_scsi_low.h10
-rw-r--r--src/filo/usb/usb_x.c163
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 (&regs->revision) & 0xff;
+ if (temp != 0x10)
+ dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+ temp = readl (&regs->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 (&regs->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 (&regs->intrstatus));
+ ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
+ // intrdisable always same as intrenable
+ // ohci_dump_intr_mask ("intrdisable", readl (&regs->intrdisable));
+
+ maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
+
+ maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
+ maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
+
+ maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
+ maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
+
+ maybe_print_eds ("donehead", readl (&regs->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