diff --git a/util/baremetal/usb/COPYING b/util/baremetal/usb/COPYING new file mode 100644 index 0000000000..0276b7c8da --- /dev/null +++ b/util/baremetal/usb/COPYING @@ -0,0 +1,6 @@ +USB for the bare metal toolkit +Most of this is copyright 2003 Steven James +and Linux Labs http://www.linuxlabs.com + +Licensed under the GNU General Public License. +Portions are borrowed from LinuxBIOS itself and other sources. diff --git a/util/baremetal/usb/Makefile b/util/baremetal/usb/Makefile new file mode 100644 index 0000000000..dda7774897 --- /dev/null +++ b/util/baremetal/usb/Makefile @@ -0,0 +1,68 @@ + +CC = gcc +#INCLUDE = -I/usr/src/linux/include +ARCH = i386 +CFLAGS = -I ../include -I ../include/$(ARCH) -O2 -DDEBUG -DZKERNEL_START=0xfff00000 + +#CFLAGS = -g -O2 $(INCLUDE) +KFLAGS = -g -O2 -D__KERNEL__ -DDEBUG -DKERNEL -DMODULE -I/usr/src/linux/include +LIBS = -lusb + +DEPS = uhci.o usb_scsi_low.o debug.o scsi_cmds.o pci_util.o block_fill_inbuf.o ../lib/baremetal.a + +LD = ld +KERN_LDFLAGS = -r --whole-archive + +PREFIX = /usr +TARGETS = roothub kerntest test + +all: usbboot.elf usb_stream.a + +usbboot.elf: $(DEPS) main.o + ld -defsym HEAPSIZE=0x8000 -T elfImage.lds main.o $(DEPS) -o usbboot.elf + +usb_stream.a: $(DEPS) + ar -cr usb_stream.a $(DEPS) + +test: test.c uhci.[ch] pci_util.o debug.[ch] + $(CC) -g -DDEBUG -DUDEBUG -o test test.c uhci.c pci_util.o debug.c + +kerntest: kernel_test.o + +kernel_test.o: kernel_uhci.o pci_util.o kernel_debug.o kernel_pci_util.o kernel_usb_scsi_low.o kernel_scsi_cmds.o + $(LD) $(KERN_LDFLAGS) -o kernel_test.o kernel_uhci.o kernel_pci_util.o kernel_debug.o kernel_usb_scsi_low.o kernel_scsi_cmds.o + +kernel_uhci.o: uhci.c uhci.h + $(CC) $(KFLAGS) -o kernel_uhci.o -c uhci.c + +kernel_usb_scsi_low.o : usb_scsi_low.c + $(CC) $(KFLAGS) -o kernel_usb_scsi_low.o -c usb_scsi_low.c + +kernel_scsi_cmds.o : scsi_cmds.c + $(CC) $(KFLAGS) -o kernel_scsi_cmds.o -c scsi_cmds.c + +kernel_debug.o: debug.c debug.h + $(CC) $(KFLAGS) -o kernel_debug.o -c debug.c + +kernel_pci_util.o: pci_util.c pci_util.h + $(CC) $(KFLAGS) -o kernel_pci_util.o -c pci_util.c + +roothub: roothub.c pci_util.o + $(CC) $(CFLAGS) -o roothub roothub.c pci_util.o + +strip: $(TARGETS) + strip $(TARGETS) + +install: strip + install -d $(PREFIX)/bin + cp -a $(TARGETS) $(PREFIX)/bin + +clean: + rm -f $(TARGETS) *.o + +distclean: clean + rm -f roothub.tar.gz + +tardist: distclean + tar -C .. --exclude CVS -cvzf roothub.tar.gz thumb + diff --git a/util/baremetal/usb/block_fill_inbuf.c b/util/baremetal/usb/block_fill_inbuf.c new file mode 100644 index 0000000000..9799fe74b6 --- /dev/null +++ b/util/baremetal/usb/block_fill_inbuf.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +//#include + +#include +//#include +#include "uhci.h" + + +typedef struct block_device { + void (*shutdown)(void); + int (*read)(int drive, void *buffer, unsigned int block); + int blocks; + int block_size; +} block_device_t; + +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; + +unsigned char usb_device_address=0; + +/* read a sector or a partial sector */ +extern int ll_read_block(unsigned char device, void *buffer, unsigned int block, unsigned int count); + +extern int block_read_sector(int drive, void * buffer, unsigned int block, int byte_offset, + int n_bytes); + +static unsigned long offset; +static unsigned long partition_start; +static unsigned long partition_end; + +static int init_bytes(void) +{ + int i,res; + int error_count=100; + partition_t part; + unsigned char sense_data[32]; + + // 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) + printk("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) { + printk("Found USB block device %u\n", i); + usb_device_address = i; + break; + } + } + + if(!usb_device_address) { + printk("Massive failure, no suitable USB device!\n"); + return(-1); + } + + + printk("Requesting initial sense data\n"); + request_sense( usb_device_address, sense_data, 32); + PrintSense(sense_data, 32); + +// get_capacity(addr, &block_count, &block_len); +// printk("%u %u byte blocks\n", block_count, block_len); + + res = ll_read_block(usb_device_address, &part, 0, 1); + + printk("ll_read_block returns %d\n", res); + + res=-1; + + for(i=0; i<4; i++) { + printk("%u: boot=%02x, start=%08x length=%08x\n",i, part.entry[i].boot_flag, part.entry[i].lba_start, part.entry[i].lba_len); + if(part.entry[i].boot_flag & 0x80) { + printk("Using partition %u\n", i); + partition_start = part.entry[i].lba_start; + partition_end = partition_start + part.entry[i].lba_len; + res=i; + break; + } + } + + return res; +} + +static void fini_bytes(void) +{ + return; +} + +#define CHUNK_SIZE 16384 +#define BLOCK_PER_CHUNK CHUNK_SIZE/512 + +static unsigned char buffer[CHUNK_SIZE]; +static unsigned int block_num = 0; +static unsigned int first_fill = 1; + +static byte_offset_t usb_read(void *vdest, byte_offset_t offset, byte_offset_t count) +{ + byte_offset_t bytes = 0; + unsigned char *dest = vdest; + + //printk_debug("ide_read count = %x\n", count); + while (bytes < count) { + unsigned int byte_offset, len; + int result; + int i, j; + + /* The block is not cached in memory or frist time called */ + if (block_num != offset / CHUNK_SIZE || first_fill) { + block_num = offset / CHUNK_SIZE; + printk_notice ("."); + ll_read_block(usb_device_address, buffer, partition_start + block_num*BLOCK_PER_CHUNK, BLOCK_PER_CHUNK); + first_fill = 0; +#if 0 + //printk_debug("ide_read offset = %x\n", offset); + //printk_debug("ide_read block_num = %x\n", block_num); + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + printk_debug("%02x ", buffer[i*16 +j]); + } + printk_debug("\n"); + } + + printk_debug("\n"); +#endif + } + + byte_offset = offset % CHUNK_SIZE; + len = CHUNK_SIZE - byte_offset; + if (len > (count - bytes)) { + len = (count - bytes); + } + + memcpy(dest, buffer + byte_offset, len); + + offset += len; + bytes += len; + dest += len; + + } + return bytes; +} + +static byte_offset_t read_bytes(void *vdest, byte_offset_t count) +{ + byte_offset_t len; + + len = usb_read(vdest, offset, count); + if (len > 0) { + offset += len; + } + + return len; +} + +static byte_offset_t skip_bytes(byte_offset_t count) +{ + offset += count; + return count; +} + +struct stream usb_stream __stream = { + .init = init_bytes, + .read = read_bytes, + .skip = skip_bytes, + .fini = fini_bytes, +}; diff --git a/util/baremetal/usb/debug.c b/util/baremetal/usb/debug.c new file mode 100644 index 0000000000..5f95543d7c --- /dev/null +++ b/util/baremetal/usb/debug.c @@ -0,0 +1,403 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James 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 "uhci.h" +#include "debug.h" + +#include + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +void dump_link( link_pointer_t *link, char *prefix) +{ + DPRINTF("%saddr: %p\n", prefix, MEM_ADDR(link->link) ); + DPRINTF("%s raw addr: %p\n", prefix, (link->link) <<4 ); + DPRINTF("%sterminate: %u\n", prefix, link->terminate); + DPRINTF("%squeue: %u\n", prefix, link->queue); + DPRINTF("%sdepth: %u\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(ipacket_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: %u\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: %u\n", prefix, td->active); + DPRINTF("%sdevice_addr: %02x\n", prefix, td->device_addr); + DPRINTF("%sendpoint: %1x\n", prefix, td->endpoint); + DPRINTF("%sdata_toggle: %1u\n", prefix, td->data_toggle); + DPRINTF("%smax_transfer: %u\n", prefix, td->max_transfer); + DPRINTF("%sactual: %u\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(%p):\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(%p):\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: %u\n", prefix, dev->address); + DPRINTF("%sclass: %u\n", prefix, dev->class); + DPRINTF("%ssubclass: %u\n", prefix, dev->subclass); + DPRINTF("%sbulk_in: %u\n", prefix, dev->bulk_in); + DPRINTF("%sbulk_out: %u\n", prefix, dev->bulk_out); + DPRINTF("%sinterrupt: %u\n", prefix, dev->interrupt); + + DPRINTF("%sep toggle:\n", prefix); + for(i=0;itoggle[i]); + + DPRINTF("%sep max_packet:\n", prefix); + for(i=0;imax_packet[i]); +} + +void dump_all_usbdev(char *prefix) +{ + int i; + + for(i=0;ibLength); + DPRINTF("%stype: %02x\n", prefix, des->type); + DPRINTF("%sbcdVersion: %1u%1u\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: %u\n",prefix, des->max_packet); + DPRINTF("%sidVendor: %04x\n", prefix, des->idVendor); + DPRINTF("%sidProduct: %04x\n", prefix, des->idProduct); + DPRINTF("%sbcdDeviceVersion: %u%u\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(%p):\n", prefix, iface); + dump_interface_descriptor(iface, newpre); + + newpre[1] = '\t'; + strcpy(newpre+2, prefix); + + for(i=0; ibNumEndpoints; i++) { + DPRINTF("\n%sEndpoint (%p):\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); +} + diff --git a/util/baremetal/usb/debug.h b/util/baremetal/usb/debug.h new file mode 100644 index 0000000000..3f8c9e33ff --- /dev/null +++ b/util/baremetal/usb/debug.h @@ -0,0 +1,26 @@ +#ifndef DEBUG_H +#define DEBUG_H + +#include "uhci.h" + +#ifndef KERNEL +#include +#define DPRINTF( x...) printk(x) +#else +#define DPRINTF( x...) printk("<1>" x) +#endif + +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_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/util/baremetal/usb/elfImage.lds b/util/baremetal/usb/elfImage.lds new file mode 100644 index 0000000000..99fb818284 --- /dev/null +++ b/util/baremetal/usb/elfImage.lds @@ -0,0 +1,57 @@ + + + + + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +HEAPSIZE = DEFINED(HEAPSIZE) ? HEAPSIZE : 0x8000; + +ENTRY(main) +SECTIONS +{ +/* . = 0x10000 - (elf_note - elf_header); */ + . = 0x30000000; + _text = .; /* Text and read-only data */ + .text . : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x9090 + .rodata (.): { *(.rodata) } + .kstrtab (.): { *(.kstrtab) } + + + . = ALIGN(16); /* Exception table */ + _etext = .; /* End of text section */ + + .data (.): { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + __bss_start = .; /* BSS */ + .bss (.): { + *(.bss) + _ebss = .; + + _heap = .; + . += HEAPSIZE; + _eheap = .; + } + _end = . ; + + _ram_seg = _text; + _eram_seg = _end; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff --git a/util/baremetal/usb/ide_fill_inbuf.c b/util/baremetal/usb/ide_fill_inbuf.c new file mode 100644 index 0000000000..6056148d63 --- /dev/null +++ b/util/baremetal/usb/ide_fill_inbuf.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +/* read a sector or a partial sector */ +extern int ide_read_sector(int drive, void * buffer, unsigned int block, int byte_offset, + int n_bytes); +extern int ide_init(void); + +static unsigned long offset; +static int init_bytes(void) +{ + int i,res; + + printk_debug ("Trying polled ide\n"); + printk_debug ("Waiting for ide disks to spin up\n"); + printk_notice ("This is a hard coded delay and longer than necessary.\n"); + for (i = 0; i < 2; i++) { + printk_notice ("."); + delay(1); + } + printk_info ("\n"); + +#ifdef ONE_TRACK + offset = (ONE_TRACK*512); +#else + offset = 0x7e00; +#endif + res = ide_init(); + delay(1); + return res; +} + +static void fini_bytes(void) +{ + return; +} + +static unsigned char buffer[512]; +static unsigned int block_num = 0; +static unsigned int first_fill = 1; +#ifndef IDE_BOOT_DRIVE +#define IDE_BOOT_DRIVE 0 +#endif +static byte_offset_t ide_read(void *vdest, byte_offset_t offset, byte_offset_t count) +{ + byte_offset_t bytes = 0; + unsigned char *dest = vdest; + + //printk_debug("ide_read count = %x\n", count); + while (bytes < count) { + unsigned int byte_offset, len; + int result; + int i, j; + + /* The block is not cached in memory or frist time called */ + if (block_num != offset / 512 || first_fill) { + block_num = offset / 512; + printk_notice ("."); + ide_read_sector(IDE_BOOT_DRIVE, buffer, block_num, + 0, 512); + first_fill = 0; +#if 0 + //printk_debug("ide_read offset = %x\n", offset); + //printk_debug("ide_read block_num = %x\n", block_num); + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + printk_debug("%02x ", buffer[i*16 +j]); + } + printk_debug("\n"); + } + + printk_debug("\n"); +#endif + } + + byte_offset = offset % 512; + len = 512 - byte_offset; + if (len > (count - bytes)) { + len = (count - bytes); + } + + memcpy(dest, buffer + byte_offset, len); + + offset += len; + bytes += len; + dest += len; + + } + return bytes; +} + +static byte_offset_t read_bytes(void *vdest, byte_offset_t count) +{ + byte_offset_t len; + + len = ide_read(vdest, offset, count); + if (len > 0) { + offset += len; + } + + return len; +} + +static byte_offset_t skip_bytes(byte_offset_t count) +{ + offset += count; + return count; +} + +static struct stream ide_stream __stream = { + .init = init_bytes, + .read = read_bytes, + .skip = skip_bytes, + .fini = fini_bytes, +}; diff --git a/util/baremetal/usb/main.c b/util/baremetal/usb/main.c new file mode 100644 index 0000000000..13f0b97f8e --- /dev/null +++ b/util/baremetal/usb/main.c @@ -0,0 +1,56 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James 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 "uhci.h" +#include "debug.h" +#include +#include + +struct lb_memory *lbmem; +extern int usec_offset; + +extern struct stream usb_stream; + +int main(void) +{ + unsigned char value; + + printk("LinuxLabs USB bootloader\n"); + + outb( 0x30, 0x70); // reset primary boot + outb( 0xff, 0x71); + lbmem = get_lbmem(); + + init_devices(); + uhci_init(); + + usb_stream.init(); + elfboot(&usb_stream, lbmem); + + while(1) { + poll_usb(); +// usec_offset+=10; +// printk("USEC_OFFSET = %d\n", usec_offset); + } + + return(0); +} diff --git a/util/baremetal/usb/pci_util.c b/util/baremetal/usb/pci_util.c new file mode 100644 index 0000000000..f3dd1dd20c --- /dev/null +++ b/util/baremetal/usb/pci_util.c @@ -0,0 +1,62 @@ +#include + + +unsigned long int pci_read_config_long(unsigned long address) +{ + unsigned short res; + + unsigned port = 0xcfc + (address & 0x03); + address &= ~0x03; + + outl(address | 0x80000000 , 0xcf8); + res = inw(port); + + return(res); +} + +unsigned int pci_read_config_word(unsigned long address) +{ + unsigned short res; + + unsigned port = 0xcfc + (address & 0x03); + address &= ~0x03; + + outl(address | 0x80000000 , 0xcf8); + res = inw(port); + + return(res); +} + +unsigned char pci_read_config_byte(unsigned long address) +{ + unsigned char res; + + unsigned port = 0xcfc + (address & 0x03); + address &= ~0x03; + + outl(address | 0x80000000 , 0xcf8); + res = inb(port); + + return(res); +} + +int pci_write_config_word(unsigned long address, unsigned short value) +{ + unsigned port = 0xcfc + (address & 0x03); + + address &= ~0x03; + + outl(address | 0x80000000 , 0xcf8); + outw(value, port); +} + +int pci_write_config_byte(unsigned long address, unsigned char value) +{ + unsigned port = 0xcfc + (address & 0x03); + + address &= ~0x03; + + outl(address | 0x80000000 , 0xcf8); + outb(value, port); +} + diff --git a/util/baremetal/usb/pci_util.h b/util/baremetal/usb/pci_util.h new file mode 100644 index 0000000000..3026f6baac --- /dev/null +++ b/util/baremetal/usb/pci_util.h @@ -0,0 +1,12 @@ +#ifndef PCI_UTIL_H +#define PCI_UTIL_H + +#define PCI_ADDR( b, d, f, i) 0x80000000 | (b<<16) | (d<<11) | (f<<8) | (i) + +unsigned long int pci_read_config_long(unsigned long address); +unsigned int pci_read_config_word(unsigned long address); +unsigned char pci_read_config_byte(unsigned long address); +int pci_write_config_word(unsigned long address, unsigned short value); +int pci_write_config_byte(unsigned long address, unsigned char value); + +#endif diff --git a/util/baremetal/usb/scsi_cmds.c b/util/baremetal/usb/scsi_cmds.c new file mode 100644 index 0000000000..90ad93e86e --- /dev/null +++ b/util/baremetal/usb/scsi_cmds.c @@ -0,0 +1,523 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James 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 "debug.h" + + +#include +#include + +#include "usb_scsi_low.h" + +#ifndef NULL +#define NULL (void *) 0x0 +#endif + +typedef unsigned char devhandle; +devhandle sgh; + +#define ntohl htonl +unsigned int htonl( unsigned int in) +{ + unsigned int out; + int i; + + for(i=0; i<4; i++) { + out <<= 8; + out |= (in & 0xff); + in >>=8; + } + + return(out); +} + +unsigned short htons( unsigned short in) +{ + unsigned short out; + int i; + + for(i=0; i<2; i++) { + out <<= 8; + out |= (in & 0xff); + in >>=8; + } + + return(out); +} + +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 i; + + DPRINTF( "sense data "); + for(i=0;i<32; 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; iadd_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, &rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + + if(ret<0) { + DPRINTF("ERROR: ll_read_block( %u, %p, %u, %u) = %d\n", sgd, buffer, blocknum, count, ret); + PrintSense(sensedat); + } + + 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, &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, &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, &response, sizeof(response), sensedat, sizeof(sensedat) ); + if(ret<0) { + DPRINTF("ERROR:get capacity: %d\n", ret); + PrintSense(sensedat); + } + + + *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, 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, 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, &ReportOpcodesCMD, sizeof(ReportOpcodesCMD_t), SG_DXFER_FROM_DEV, 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, &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, &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); +} + + diff --git a/util/baremetal/usb/scsi_cmds.h b/util/baremetal/usb/scsi_cmds.h new file mode 100644 index 0000000000..26032f796c --- /dev/null +++ b/util/baremetal/usb/scsi_cmds.h @@ -0,0 +1,450 @@ +#include "scsi_low.h" + +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 i; + + DPRINTF("sense data "); + for(i=0;i<32; 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; iadd_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(out,"\n"); + + if(fd->valid) + DPRINTF("(valid) "); + + DPRINTF("Info: %08x\n", ntohl(fd->info)); + + afd = (additional_fixed_data_t *) (sense + 8); + + while(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, &rb, sizeof(rb), SG_DXFER_FROM_DEV, buffer, count * 512, sensedat, sizeof(sensedat)); + 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, &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, &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, &response, sizeof(response), sensedat, sizeof(sensedat) ); + if(ret<0) + perror("get capacity: IOCTL"); + + + *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, qr, sizeof(query_response_t), sensedat, sizeof(sensedat) ); + + if(ret<0) + perror("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, list, sizeof(lun_list_t), sensedat, sizeof(sensedat) ); + + if(ret<0) + perror("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, &ReportOpcodesCMD, sizeof(ReportOpcodesCMD_t), SG_DXFER_FROM_DEV, list, sizeof(report_opcode_result_t), sensedat, sizeof(sensedat) ); + + if(ret<0) + perror("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, &cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + perror("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, &cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) ); + if(ret<0) { + perror("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) { + perror("UnitReady :"); + return(0); + } + + return(1); +} + + diff --git a/util/baremetal/usb/uhci.c b/util/baremetal/usb/uhci.c new file mode 100644 index 0000000000..8525481dfc --- /dev/null +++ b/util/baremetal/usb/uhci.c @@ -0,0 +1,1663 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James 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. + * + ******************************************************************************/ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + + +#include +#include "uhci.h" +#include "debug.h" +#include "pci_util.h" + +#include +#include + +int usec_offset=0; + +int dump_count=0; + +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; +#ifdef __KERNEL__ + dma_addr_t *dma_handle; + + queue_heads = pci_alloc_consistent(NULL, MAX_QUEUEHEAD * sizeof(queue_head_t), &dma_handle); +#endif + + for(i=0; ilink.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; itd_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 = %p) 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); +} + +#define MAX_CONTROLLERS 4 + +link_pointer_t *frame_list[MAX_CONTROLLERS]; +uchar fl_buffer[MAX_CONTROLLERS][8192]; + +//link_pointer_t frame_list[1024]; + +void init_framelist(uchar dev) +{ + int i; +#ifdef __KERNEL__ + dma_addr_t *dma_handle; + + frame_list[dev] = pci_alloc_consistent(NULL, 1024 * sizeof(link_pointer_t ), &dma_handle); + memset(frame_list[dev], 0, 1024 * sizeof(link_pointer_t)); +#else + memset(fl_buffer[dev], 0, 4096); + DPRINTF("raw frame_list is at %p\n", fl_buffer[dev]); + frame_list[dev] = (link_pointer_t *) (((unsigned int)fl_buffer[dev] & ~0xfff) + 0x1000); +#endif + + DPRINTF("frame_list is at %p\n", frame_list[dev]); + + for(i=0;i<1024;i++) + frame_list[dev][i].terminate=1; + +} + + +int num_controllers=0; + +unsigned int hc_base[MAX_CONTROLLERS]; + +#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_clear_stat() +{ + unsigned short value; + + value = inw(USBSTS(0)); + outw(value, USBSTS(0)); +} + +void clear_port_stat(unsigned short port) +{ + unsigned short value; + + value = inw(port); + outw(value, port); +} + +void port_suspend( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value |= 0x1000; + outw( value, port); +dump_uhci(0x38c0); + +} + +void port_wakeup( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x1000; + outw( value, port); + +} + +void port_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); +} + +void port_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 port_disable( unsigned short port) +{ + unsigned short value; + + value = inw(port); + value &= ~0x04; + outw( value, port); +} + +void port_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); +// dump_uhci(0x38c0); +} + +void port_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); +// dump_uhci(0x38c0); +} + +void hc_reset(uchar controller) +{ + DPRINTF("Resetting UHCI\n"); + outw(0x04, USBCMD(controller)); + udelay(20000); + outw(0, USBCMD(controller)); +} + +int hc_stop(void) +{ + unsigned short tmp; + + tmp = inw(USBCMD(0)); + tmp &= ~USBCMDRUN; + outw( tmp, USBCMD(0)); + + while(! (inw(USBSTS(0)) & USBSTSHALTED) ); + outw( USBSTSHALTED, USBSTS(0)); // clear the status + + return(0); +} + +int hc_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 hc_init(uchar dev, uchar function) +{ + unsigned short cmd; + +// hc_base = pci_read_config_word(PCI_ADDR(0, 0x1d, function, 0x20)); + hc_base[num_controllers] = pci_read_config_word(PCI_ADDR(0, dev, function, 0x20)); + hc_base[num_controllers] &= ~1; + + DPRINTF("Found UHCI at %04x\n", hc_base[num_controllers]); + hc_reset(num_controllers); + + // set master + cmd = pci_read_config_word( PCI_ADDR(0, dev, function, 0x04)); + cmd |= 0x04; + pci_write_config_word( PCI_ADDR(0, dev, function, 0x04), cmd); + + if( (unsigned int) frame_list[dev] != ( ( (unsigned int)frame_list[dev]) & ~0x7ff) ) { + DPRINTF("UHCI: grave error, misaligned framelist (%p)\n", frame_list[dev]); + return(-1); + } + + DPRINTF("hc_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)); + + hc_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 = %p\n", dummy_td[dev]); + +// dump_frame_list("sched:"); + +} + +void uhci_init(void) +{ + int i; + + init_td(); + init_qh(); + init_transactions(); + + for(i=0;idepth.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); +} + +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=3; +} + +#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; + + value = inw(hc_base[dev]); + value |=1; + outw( value, hc_base[dev]); + + 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 bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data) +{ + transaction_t *trans; + td_t *td; + int data_len; + uchar *buffer; + int ret; + +#ifdef __KERNEL__ + dma_addr_t dma_handle; + buffer = pci_alloc_consistent(NULL, len , &dma_handle); + + if( !(ep & 0x80)) + memcpy(buffer, data, len); +#else + buffer = data; +#endif + + + trans = _bulk_transfer(devnum, ep, len, buffer); +//#ifdef DEBUG +// dump_transaction(trans, "bulk_transfer:"); +//#endif + schedule_transaction( usb_device[devnum].controller, trans); + ret = wait_transaction(trans); + unlink_transaction( usb_device[devnum].controller, trans); + + if(ret<0) { +#ifdef DEBUG + dump_uhci(hc_base[usb_device[devnum].controller] ); + dump_td(trans->td_list, "failed_bulk_transaction: "); +#endif + free_transaction(trans); + return(-1); + } + + // a shortcut + if(trans->qh->depth.terminate) + data_len = len; + else { + 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); + } + +//#ifdef DEBUG +// dump_td(trans->td_list, "bulk_transfer_success:"); +//#endif + + if(data_len < len) { + DPRINTF("bulk_transfer( dev= %u, ep = %u, len = %u, buffer = %p) = %d:short transaction:\n", devnum, ep, len, data, data_len); + dump_td(trans->td_list, "short_transaction:"); + } + + free_transaction(trans); +#ifdef __KERNEL__ + memcpy(data, buffer, data_len); + pci_free_consistent(NULL, buffer, len, NULL); +#endif + +// DPRINTF("bulk_transfer returning %d\n", data_len); + return(data_len); +} + +int usb_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data) +{ + transaction_t *trans, *cur; + queue_head_t *qh; + td_t *td; + int data_len=0; + int ret; + uchar *buffer; +#ifdef __KERNEL__ + + dma_addr_t dma_handle; + + buffer = pci_alloc_consistent(NULL, wLength, &dma_handle); +// DPRINTF("usb_control_msg: buffer = %p\n", buffer); + if( !(request_type & 0x80)) + memcpy(buffer, data, wLength); +#else + buffer = data; +#endif + + trans = ctrl_msg(devnum, request_type, request, wValue, wIndex, wLength, buffer); + if(!trans) { + DPRINTF("usb_control_msg: ctrl_msg failed!\n"); +#ifdef __KERNEL__ + pci_free_consistent(NULL, buffer, wLength, NULL); +#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); +#ifdef __KERNEL__ + pci_free_consistent(NULL, buffer, wLength, NULL); +#endif + return(ret); + } + +//#ifdef DEBUG +// dump_td(trans->td_list, "success: "); +//#endif + + unlink_transaction( usb_device[devnum].controller, trans); + + // now, see what happened + qh = trans->qh; + cur = trans; + + if(!qh->depth.terminate) { +// handle setup error + + dump_uhci(hc_base); + dump_td(trans->td_list, "qh->depth failed_transaction: "); + + free_transaction(trans); +#ifdef __KERNEL__ + pci_free_consistent(NULL, buffer, wLength, NULL); +#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); + + // now check status phase + if(! qh->depth.terminate) { + } + + free_transaction(trans); + +#ifdef __KERNEL__ + if( (request_type & 0x80)) + memcpy(data, buffer, wLength); + pci_free_consistent(NULL, buffer, wLength, NULL); +#endif + + return(data_len); +} + +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); + usb_device[device].toggle[endpoint]=0; + + return(ret); +} + +inline int device_reset(uchar device) { + 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; jbNbrPorts; 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 %u, Port %04x disconnected\n", addr, i); + // deconfigure + } + } + } + return(devaddr); + +} + +int usb_hub_init( uchar addr) +{ + int i; + int ret; + hub_descriptor_t *desc; + + desc = malloc(sizeof(hub_descriptor_t)); + + memset(desc, 0 , sizeof(hub_descriptor_t)); + + DPRINTF("hub init (%u)\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); +} + +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; + +// will set up whatever device is answering at address 0. +int configure_device(ushort 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++; + int address_retry=5; + uchar buffer[512]; + uchar string[512]; + + desc = (device_descriptor_t *) buffer; + + memset( &usb_device[addr], 0, sizeof(usbdev_t)); + + DPRINTF("New USB device, setting address %u\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; + + +// hc_clear_stat(); +// dump_uhci(hc_base[controller]); + + ret = set_address(addr); + if(ret<0) { + DPRINTF("configure_device: set_address failed!\n"); +// dump_uhci(hc_base[controller]); + } + + if(ret<0) { + DPRINTF("configure_device: can't set address! bailing!\n"); + next_usb_dev--; + return(-1); + } + + 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( conf, ""); + + DPRINTF("Selecting Configuration number %u:\n", conf->bConfigurationValue); + ret = usb_control_msg(addr, 0, SET_CONFIGURATION, conf->bConfigurationValue, 0, 0, NULL); + + for(i=0; ibNumEndpoints;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) + usb_device[addr].interrupt = epd[i].bEndpointAddress; + + if( (epd[i].bmAttributes & 0x03) == 0x02) { + DPRINTF("clear stall on ep=%u\n", epd[i].bEndpointAddress); + clear_stall(addr, epd[i].bEndpointAddress); // to reset data toggle + + if(epd[i].bEndpointAddress & 0x80) + usb_device[addr].bulk_in = epd[i].bEndpointAddress; + else + 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; + } + + printk_info("%02x:%02x:%02x\n", usb_device[addr].class, usb_device[addr].subclass, usb_device[addr].protocol); + + get_string(addr, desc->iManufacturor, sizeof(string), string); + printk_info("Manufacturor: %s\n", string); + + get_string(addr, desc->iProduct, sizeof(string), string); + printk_info("Product: %s\n", string); + + get_string(addr, desc->iSerial, sizeof(string), string); + printk_info("Serial: %s\n", string); + + switch( usb_device[addr].class) { + case 0x09: // hub + usb_hub_init(addr); + break; + + default: + break; + + } + + +// a test! + if(usb_device[addr].class == 0x08 && usb_device[addr].subclass == 0x06 && usb_device[addr].protocol == 0x50) { + uchar sense_data[32]; + unsigned long block_count=0; + unsigned int block_len=0; + partition_t part; + + DPRINTF("Mass storage, bulk only SCSI transparent\n"); + + DPRINTF("Requesting initial sense data\n"); + request_sense( addr, sense_data, 32); + PrintSense(sense_data, 32); + + get_capacity(addr, &block_count, &block_len); + DPRINTF("%u %u byte blocks\n", block_count, block_len); + + ret = ll_read_block(addr, &part, 0, 1); + + DPRINTF("ll_read_block returns %d\n", ret); + + for(i=0; i<4; i++) + DPRINTF("%u: boot=%02x, start=%08x length=%08x\n",i, part.entry[i].boot_flag, part.entry[i].lba_start, part.entry[i].lba_len); + } + + DPRINTF("DEVICE CONFIGURED\n"); +// dump_uhci(hc_base[controller]); + if(port>8) + clear_port_stat(port); +// dump_uhci(hc_base[controller]); + return(addr); +} + +int poll_root_hub(unsigned short port, uchar controller) +{ + ushort value; + int addr=0; + int i; + static int do_over=0; + + value = inw(port); + + if(value & 0x02 || do_over == port) { + do_over=0; + if(value & 0x01 ) { // if port connected + 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"); + } + } + + port_wakeup(port); +// DPRINTF("Wakup %04x\n", port); + + port_reset(port); + udelay(10); + port_enable(port); + + if(!value & 0x01) { + DPRINTF("Device went away!\n"); + return(-1); + } + + addr = configure_device( port, controller, value & 0x100); + + if(addr<0) { + port_disable(port); + udelay(20000); +// port_reset(port); + port_reset_long(port); + port_suspend(port); + do_over=port; + hc_clear_stat(); +dump_uhci(0x38c0); + } + } else { + port_suspend(port); + port_disable(port); + DPRINTF("Port %04x disconnected\n", port); + // wave hands, deconfigure devices on this port! + } + } + + + 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; + + for(i=0; i0, should probably see what was attached! + addr = poll_root_hub(PORTSC1(0), i); + if(addr && !found) + found=addr; + + addr = poll_root_hub(PORTSC2(0), i); + if(addr && !found) + found=addr; + } + + // now poll registered drivers (such as the hub driver + for(i=0;i +#include +#include +#include +#include +#include +#include +#include + + +unsigned long uhci_poll_interval = 50; +unsigned short int_stat=0; + +void uhci_set_timer(unsigned long interval); + +void uhci_kernel_poll( unsigned long data) +{ + unsigned short value; + + poll_usb(); + uhci_set_timer(uhci_poll_interval); + +} + +struct timer_list uhci_timer; + +static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) +{ + + int_stat = inw(0x38c2); + outw( int_stat, 0x38c2); + printk("<1>UHCI interrupt, status = %04x\n", int_stat); + + +} + +void uhci_set_timer(unsigned long interval) +{ + init_timer(&uhci_timer); + uhci_timer.function = uhci_kernel_poll; + uhci_timer.expires = jiffies + interval; + + add_timer( &uhci_timer ); +} + +int __init uhci_module_init(void) +{ +#ifdef TEST + unsigned int value; + + printk("<1>UHCI DUMP:\n"); + dump_uhci(0x38c0); + value = inl(0x38c0 + 0x08); + + value = bus_to_virt(value); + value &= ~0x7ff; + dump_frame_list(value, ""); + return(0); +#endif + + request_irq(16, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci_interrupt); + + printk("<1>UHCI TEST INIT\n"); + init_devices(); + uhci_init(); + uhci_set_timer( uhci_poll_interval); + + return(0); + +} + +void __exit uhci_module_exit(void) +{ + + outw( 0x00, 0x38c4); + + port_suspend(PORTSC1(0)); + port_suspend(PORTSC2(0)); + hc_reset(0); + free_irq(16, uhci_interrupt); + del_timer( &uhci_timer); + +} + +module_init(uhci_module_init); +module_exit(uhci_module_exit); + + + +#endif diff --git a/util/baremetal/usb/uhci.h b/util/baremetal/usb/uhci.h new file mode 100644 index 0000000000..bce5d7ff80 --- /dev/null +++ b/util/baremetal/usb/uhci.h @@ -0,0 +1,334 @@ +#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 + * + */ + +typedef unsigned char uchar; + +#ifndef KERNEL +typedef unsigned short ushort; +#endif + +#define MAX_POLLDEV 10 + +#define MAX_TRANSACTIONS 10 +#define MAX_QUEUEHEAD 255 +#define MAX_TD 1024 + +#ifndef __KERNEL__ +#define virt_to_bus +#define bus_to_virt +#endif + +#ifndef __KERNEL__ +#define LINK_ADDR(x) ((unsigned int)x >> 4) +#define MEM_ADDR(x) (void *) ( ((unsigned int) (x))<<4) +#else +#define LINK_ADDR(x) ( virt_to_bus(x) >> 4) +#define MEM_ADDR(x) (void *) ( bus_to_virt( ((unsigned int) (x)) <<4) ) +#endif + +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; + +#define SETUP_TOKEN 0x2d +#define IN_TOKEN 0x69 +#define OUT_TOKEN 0xe1 + +#define CTRL_RETRIES 3 +#define CONTROL_STS_RETRIES 0 + +// 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 + +// 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 + +// 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 + +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; + +#define MAX_USB_DEV 127 +#define MAX_EP 8 + +typedef struct usbdev { + unsigned short port; + uchar address; + uchar controller; + uchar class; + uchar subclass; + uchar protocol; + uchar bulk_in; + uchar bulk_out; + uchar interrupt; + uchar lowspeed; + uchar toggle[MAX_EP]; + unsigned short max_packet[MAX_EP]; + void *private; +} usbdev_t; + + +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; + +//##################################################### +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); + +extern link_pointer_t *frame_list[]; + +void init_framelist(uchar dev); + +extern unsigned int hc_base[]; +#define USBCMD hc_base +#define USBSTS (hc_base + 0x02) +#define USBINTR (hc_base + 0x04) +#define FRNUM ( hc_base + 0x06) +#define FLBASE ( hc_base + 0x08) +#define SOFMOD ( hc_base + 0x0c) +#define PORTSC1 ( hc_base + 0x10) +#define PORTSC2 ( hc_base + 0x12) + +#define USBCMDRUN 0x01 + +#define USBSTSHALTED 0x20 + +void hc_reset(uchar dev); +int hc_stop(void); +int hc_start(uchar dev); +int hc_init(uchar dev, uchar function); + +extern queue_head_t *sched_queue[]; + +void init_sched(uchar dev); +void uhci_init(void); +int poll_queue_head( queue_head_t *qh); +int wait_queue_complete( queue_head_t *qh); + +extern int next_usb_dev; +usbdev_t usb_device[MAX_USB_DEV]; + + +extern int num_polls; +extern int (*devpoll[MAX_POLLDEV])(uchar); +extern uchar parm[MAX_POLLDEV]; + +void init_devices(void); +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 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); +inline int set_address(uchar address); +inline int clear_stall(uchar device, uchar endpoint); +int configure_device(unsigned short port, uchar controller, unsigned int lowspeed); +#endif diff --git a/util/baremetal/usb/usb_scsi_low.c b/util/baremetal/usb/usb_scsi_low.c new file mode 100644 index 0000000000..42c4bbd939 --- /dev/null +++ b/util/baremetal/usb/usb_scsi_low.c @@ -0,0 +1,151 @@ +/******************************************************************************* + * + * + * Copyright 2003 Steven James 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 "debug.h" +#include "usb_scsi_low.h" + +//#include + +#define SG_DXFER_FROM_DEV -3 +#define SG_DXFER_TO_DEV -2 + +#define REQUEST_SENSE 0x03 + +#define CBW_SIG 43425355 + +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 53425355 + +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 = 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 %u bytes\n", data_len); + ret = bulk_transfer(device, 0x81, data_len, data); +// DPRINTF("scsi_command read %u bytes\n", ret); + if(ret<0 || ret