This patch adds USB capabilities to libpayload. It requires some

memalign implementation (eg. the one I sent yesterday).
Features:
 - UHCI controller driver
 - UHCI root hub driver
 - USB MSC (Mass Storage Class) driver
 - skeleton of a USB HID driver
   (requires better interrupt transfer handling, which is TODO)
 - skeleton of a USB hub driver
   (needs several blank spots filled in, eg. power management.
    Again: TODO)

OHCI and EHCI are not supported, though OHCI support should be rather
easy as the stack provides reasonable abstractions (or so I hope). EHCI
will probably be more complicated.

Isochronous transfers (eg. webcams, audio stuff, ...) are not supported.
They can be, but I doubt we'll have a reason for that in the boot
environment.

The MSC driver was tested against a couple of USB flash drives, and
should be reasonably tolerant by now. But I probably underestimate
the amount of bugs present in USB flash drives, so feedback is welcome.

Signed-off-by: Patrick Georgi <patrick.georgi@coresystems.de>
Acked-by: Jordan Crouse <jordan.crouse@amd.com>


git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3560 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
This commit is contained in:
Patrick Georgi 2008-09-02 16:06:22 +00:00
parent 5ccfa1ac79
commit d21f68bbd5
16 changed files with 2427 additions and 0 deletions

View File

@ -132,5 +132,46 @@ config SPEAKER
bool "Support for PC speaker"
default y
config USB
bool "USB Support"
default n
config USB_UHCI
bool "Support for USB UHCI controllers"
depends on USB
help
Select this option if you are going to use USB 1.1 on an Intel based
system.
config USB_OHCI
bool "Support for USB OHCI controllers"
depends on USB
help
Select this option if you are going to use USB 1.1 on an AMD based
system.
NOTE: This option is not (fully) implemented yet
config USB_EHCI
bool "Support for USB EHCI controllers"
depends on USB
help
Select this option if you want to use USB 2.0
NOTE: This option is not (fully) implemented yet
config USB_HID
bool "Support for USB keyboards (broken)"
depends on USB
default n
config USB_HUB
bool "Support for USB hubs (broken)"
depends on USB
default n
config USB_MSC
bool "Support for USB storage"
depends on USB
endmenu

View File

@ -137,6 +137,7 @@ install: lib
prepare:
$(Q)mkdir -p $(obj)/util/kconfig/lxdialog
$(Q)mkdir -p $(obj)/crypto $(obj)/curses $(obj)/drivers/video
$(Q)mkdir -p $(obj)/drivers/usb
$(Q)mkdir -p $(obj)/i386 $(obj)/lib/$(ARCHDIR-y) $(obj)/libc
$(Q)mkdir -p $(src)/lib/$(ARCHDIR-y)

View File

@ -47,3 +47,14 @@ TARGETS-$(CONFIG_VGA_VIDEO_CONSOLE) += drivers/video/vga.o
# Geode console drivers
TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/geode.o
TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/font8x16.o
# USB stack
TARGETS-$(CONFIG_USB) += drivers/usb/usbinit.o
TARGETS-$(CONFIG_USB) += drivers/usb/usb.o
TARGETS-$(CONFIG_USB) += drivers/usb/usb_dev.o
TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o
TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o
TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o
TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o
TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o

View File

@ -0,0 +1,22 @@
- handle error conditions
- handle disconnect more gracefully (ie. make calling layer aware that the device doesn't exist somehow)
- usbhub:
- proper client enumeration (esp. detach)
- change detection
- power management
- handle interrupts more cleverly:
create a new queue for the interrupt with a couple of TD sequences,
- each ending with "breadth first" flag
- linked as a chain
add that queue at the appropriate times in front of the default structure so the max latency is honored
- only one intr chain per framelist item, so it must be arranged appropriately
reads from usb device just look at "invalidated" tds and the results they got
handled tds get reactivated as a ring structure
- added as child of the oldest td
- queue header already dropped the td, so no issue there
this setup ensures that:
- the max latency of the device is honored
- the client knows the right order of the data
- there is no need for an interrupt handler
- but must be polled at least max latency * num tds times -> more tds = less time pressure

View File

@ -0,0 +1,507 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "usb.h"
#include "uhci.h"
#include <arch/virtual.h>
static void uhci_start (hci_t *controller);
static void uhci_stop (hci_t *controller);
static void uhci_reset (hci_t *controller);
static void uhci_shutdown (hci_t *controller);
static int uhci_packet (usbdev_t *dev, int endp, int pid, int toggle,
int length, u8 *data);
static int uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
static int uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq,
int dalen, u8 *data);
#if 0
/* dump uhci */
static void
uhci_dump (hci_t *controller)
{
printf ("dump:\nUSBCMD: %x\n", uhci_reg_read16 (controller, USBCMD));
printf ("USBSTS: %x\n", uhci_reg_read16 (controller, USBSTS));
printf ("USBINTR: %x\n", uhci_reg_read16 (controller, USBINTR));
printf ("FRNUM: %x\n", uhci_reg_read16 (controller, FRNUM));
printf ("FLBASEADD: %x\n", uhci_reg_read32 (controller, FLBASEADD));
printf ("SOFMOD: %x\n", uhci_reg_read8 (controller, SOFMOD));
printf ("PORTSC1: %x\n", uhci_reg_read16 (controller, PORTSC1));
printf ("PORTSC2: %x\n", uhci_reg_read16 (controller, PORTSC2));
}
#endif
static void
td_dump (td_t *td)
{
printf ("%x packet (at %lx) to %x.%x failed\n", td->pid,
virt_to_phys (td), td->dev_addr, td->endp);
printf ("td (counter at %x) returns: ", td->counter);
printf (" bitstuff err: %x, ", td->status_bitstuff_err);
printf (" CRC err: %x, ", td->status_crc_err);
printf (" NAK rcvd: %x, ", td->status_nakrcvd);
printf (" Babble: %x, ", td->status_babble);
printf (" Data Buffer err: %x, ", td->status_databuf_err);
printf (" Stalled: %x, ", td->status_stalled);
printf (" Active: %x\n", td->status_active);
if (td->status_babble)
printf (" Babble because of %s\n",
td->status_bitstuff_err ? "host" : "device");
if (td->status_active)
printf (" still active - timeout?\n");
}
static void
uhci_reset (hci_t *controller)
{
/* reset */
uhci_reg_write16 (controller, USBCMD, 4);
mdelay (50);
uhci_reg_write16 (controller, USBCMD, 0);
mdelay (10);
uhci_reg_write16 (controller, USBCMD, 2);
while ((uhci_reg_read16 (controller, USBCMD) & 2) != 0)
mdelay (1);
uhci_reg_write32 (controller, FLBASEADD,
(u32) virt_to_phys (UHCI_INST (controller)->
framelistptr));
//printf ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
/* disable irqs */
uhci_reg_write16 (controller, USBINTR, 0);
/* reset framelist index */
uhci_reg_write16 (controller, FRNUM, 0);
uhci_reg_mask16 (controller, USBCMD, ~0, 0xc0); // max packets, configure flag
uhci_start (controller);
}
hci_t *
uhci_init (pcidev_t addr)
{
int i;
hci_t *controller = new_controller ();
controller->instance = malloc (sizeof (uhci_t));
controller->start = uhci_start;
controller->stop = uhci_stop;
controller->reset = uhci_reset;
controller->shutdown = uhci_shutdown;
controller->packet = uhci_packet;
controller->bulk = uhci_bulk;
controller->control = uhci_control;
UHCI_INST (controller)->roothub = &(controller->devices[0]);
controller->bus_address = addr;
controller->reg_base = pci_read_config32 (controller->bus_address, 0x20) & ~1; /* ~1 clears the register type indicator that is set to 1 for IO space */
/* kill legacy support handler */
uhci_stop (controller);
mdelay (1);
uhci_reg_write16 (controller, USBSTS, 0x3f);
pci_write_config32 (controller->bus_address, 0xc0, 0x8f00);
UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t *)); /* 4kb aligned to 4kb */
memset (UHCI_INST (controller)->framelistptr, 0,
1024 * sizeof (flistp_t));
UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t));
UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t));
UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t));
UHCI_INST (controller)->qh_intr->headlinkptr.ptr =
virt_to_phys (UHCI_INST (controller)->qh_data);
UHCI_INST (controller)->qh_intr->headlinkptr.queue_head = 1;
UHCI_INST (controller)->qh_intr->elementlinkptr.ptr = 0;
UHCI_INST (controller)->qh_intr->elementlinkptr.terminate = 1;
UHCI_INST (controller)->qh_data->headlinkptr.ptr =
virt_to_phys (UHCI_INST (controller)->qh_last);
UHCI_INST (controller)->qh_data->headlinkptr.queue_head = 1;
UHCI_INST (controller)->qh_data->elementlinkptr.ptr = 0;
UHCI_INST (controller)->qh_data->elementlinkptr.terminate = 1;
UHCI_INST (controller)->qh_last->headlinkptr.ptr = 0;
UHCI_INST (controller)->qh_last->headlinkptr.terminate = 1;
UHCI_INST (controller)->qh_last->elementlinkptr.ptr = 0;
UHCI_INST (controller)->qh_last->elementlinkptr.terminate = 1;
for (i = 0; i < 1024; i++) {
UHCI_INST (controller)->framelistptr[i].ptr =
virt_to_phys (UHCI_INST (controller)->qh_intr);
UHCI_INST (controller)->framelistptr[i].terminate = 0;
UHCI_INST (controller)->framelistptr[i].queue_head = 1;
}
for (i = 1; i < 128; i++) {
init_device_entry (controller, i);
}
controller->devices[0].controller = controller;
controller->devices[0].init = uhci_rh_init;
controller->devices[0].init (&controller->devices[0]);
uhci_reset (controller);
return controller;
}
static void
uhci_shutdown (hci_t *controller)
{
if (controller == 0)
return;
detach_controller (controller);
UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)->
roothub);
uhci_reg_mask16 (controller, USBCMD, 0, 0); // stop work
free (UHCI_INST (controller)->framelistptr);
free (UHCI_INST (controller)->qh_intr);
free (UHCI_INST (controller)->qh_data);
free (UHCI_INST (controller)->qh_last);
free (UHCI_INST (controller));
free (controller);
}
static void
uhci_start (hci_t *controller)
{
uhci_reg_mask16 (controller, USBCMD, ~0, 1); // start work on schedule
}
static void
uhci_stop (hci_t *controller)
{
uhci_reg_mask16 (controller, USBCMD, ~1, 0); // stop work on schedule
}
#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
static td_t *
wait_for_completed_qh (hci_t *controller, qh_t *qh)
{
int timeout = 1000; /* max 30 ms. */
void *current = GET_TD (qh->elementlinkptr.ptr);
while ((qh->elementlinkptr.terminate == 0) && (timeout-- > 0)) {
if (current != GET_TD (qh->elementlinkptr.ptr)) {
current = GET_TD (qh->elementlinkptr.ptr);
timeout = 1000;
}
uhci_reg_mask16 (controller, USBSTS, ~0, 0); // clear resettable registers
udelay (30);
}
return (GET_TD (qh->elementlinkptr.ptr) ==
0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr.ptr));
}
static void
wait_for_completed_td (hci_t *controller, td_t *td)
{
int timeout = 10000;
while ((td->status_active == 1)
&& ((uhci_reg_read16 (controller, USBSTS) & 2) == 0)
&& (timeout-- > 0)) {
uhci_reg_mask16 (controller, USBSTS, ~0, 0); // clear resettable registers
udelay (10);
}
}
static int
maxlen (int size)
{
return (size - 1) & 0x7ff;
}
static int
min (int a, int b)
{
if (a < b)
return a;
else
return b;
}
static int
uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq, int dalen,
unsigned char *data)
{
int endp = 0; /* this is control: always 0 */
int mlen = dev->endpoints[0].maxpacketsize;
int count = (2 + (dalen + mlen - 1) / mlen);
unsigned short req = ((unsigned short *) devreq)[0];
int i;
td_t *tds = memalign (16, sizeof (td_t) * count);
memset (tds, 0, sizeof (td_t) * count);
count--; /* to compensate for 0-indexed array */
for (i = 0; i < count; i++) {
tds[i].ptr = virt_to_phys (&tds[i + 1]);
tds[i].depth_first = 1;
tds[i].terminate = 0;
}
tds[count].ptr = 0;
tds[count].depth_first = 1;
tds[count].terminate = 1;
tds[0].pid = SETUP;
tds[0].dev_addr = dev->address;
tds[0].endp = endp;
tds[0].maxlen = maxlen (drlen);
tds[0].counter = 3;
tds[0].data_toggle = 0;
tds[0].lowspeed = dev->lowspeed;
tds[0].bufptr = virt_to_phys (devreq);
tds[0].status_active = 1;
int toggle = 1;
for (i = 1; i < count; i++) {
tds[i].pid = dir;
tds[i].dev_addr = dev->address;
tds[i].endp = endp;
tds[i].maxlen = maxlen (min (mlen, dalen));
tds[i].counter = 3;
tds[i].data_toggle = toggle;
tds[i].lowspeed = dev->lowspeed;
tds[i].bufptr = virt_to_phys (data);
tds[i].status_active = 1;
toggle ^= 1;
dalen -= mlen;
data += mlen;
}
tds[count].pid = (dir == OUT) ? IN : OUT;
tds[count].dev_addr = dev->address;
tds[count].endp = endp;
tds[count].maxlen = maxlen (0);
tds[count].counter = 0; /* as per linux 2.4.10 */
tds[count].data_toggle = 1;
tds[count].lowspeed = dev->lowspeed, tds[count].bufptr = 0;
tds[count].status_active = 1;
UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
virt_to_phys (tds);
UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
td_t *td = wait_for_completed_qh (dev->controller,
UHCI_INST (dev->controller)->
qh_data);
int result;
if (td == 0) {
result = 0;
} else {
printf ("control packet, req %x\n", req);
td_dump (td);
result = 1;
}
free (tds);
return result;
}
static int
uhci_packet (usbdev_t *dev, int endp, int pid, int toggle, int length,
unsigned char *data)
{
static td_t *td = 0;
if (td == 0)
td = memalign (16, sizeof (td_t));
memset (td, 0, sizeof (td_t));
td->ptr = 0;
td->terminate = 1;
td->queue_head = 0;
td->pid = pid;
td->dev_addr = dev->address;
td->endp = endp & 0xf;
td->maxlen = maxlen (length);
if (pid == SETUP)
td->counter = 3;
else
td->counter = 0;
td->data_toggle = toggle & 1;
td->lowspeed = dev->lowspeed;
td->bufptr = virt_to_phys (data);
td->status_active = 1;
UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
virt_to_phys (td);
UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
wait_for_completed_td (dev->controller, td);
if ((td->status & 0x7f) == 0) {
//printf("successfully sent a %x packet to %x.%x\n",pid, dev->address,endp);
// success
return 0;
} else {
td_dump (td);
return 1;
}
}
static td_t *
create_schedule (int numpackets)
{
if (numpackets == 0)
return 0;
td_t *tds = memalign (16, sizeof (td_t) * numpackets);
memset (tds, 0, sizeof (td_t) * numpackets);
int i;
for (i = 0; i < numpackets; i++) {
tds[i].ptr = virt_to_phys (&tds[i + 1]);
tds[i].terminate = 0;
tds[i].queue_head = 0;
tds[i].depth_first = 1;
}
tds[numpackets - 1].ptr = 0;
tds[numpackets - 1].terminate = 1;
tds[numpackets - 1].queue_head = 0;
tds[numpackets - 1].depth_first = 0;
return tds;
}
static void
fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data,
int *toggle)
{
td->pid = ep->direction;
td->dev_addr = ep->dev->address;
td->endp = ep->endpoint & 0xf;
td->maxlen = maxlen (length);
if (ep->direction == SETUP)
td->counter = 3;
else
td->counter = 0;
td->data_toggle = *toggle & 1;
td->lowspeed = ep->dev->lowspeed;
td->bufptr = virt_to_phys (data);
td->status_active = 1;
*toggle ^= 1;
}
static int
run_schedule (usbdev_t *dev, td_t *td)
{
UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
virt_to_phys (td);
UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
td = wait_for_completed_qh (dev->controller,
UHCI_INST (dev->controller)->qh_data);
if (td == 0) {
return 0;
} else {
td_dump (td);
return 1;
}
}
/* finalize == 1: if data is of packet aligned size, add a zero length packet */
static int
uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
{
int maxpsize = ep->maxpacketsize;
if (maxpsize == 0)
fatal ("MaxPacketSize == 0!!!");
int numpackets = (size + maxpsize - 1 + finalize) / maxpsize;
if (numpackets == 0)
return 0;
td_t *tds = create_schedule (numpackets);
int i = 0, toggle = ep->toggle;
while ((size > 0) || ((size == 0) && (finalize != 0))) {
fill_schedule (&tds[i], ep, min (size, maxpsize), data,
&toggle);
i++;
data += maxpsize;
size -= maxpsize;
}
if (run_schedule (ep->dev, tds) == 1) {
clear_stall (ep);
free (tds);
return 1;
}
ep->toggle = toggle;
free (tds);
return 0;
}
void
uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value)
{
outl (value, ctrl->reg_base + reg);
}
u32
uhci_reg_read32 (hci_t *ctrl, usbreg reg)
{
return inl (ctrl->reg_base + reg);
}
void
uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value)
{
outw (value, ctrl->reg_base + reg);
}
u16
uhci_reg_read16 (hci_t *ctrl, usbreg reg)
{
return inw (ctrl->reg_base + reg);
}
void
uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value)
{
outb (value, ctrl->reg_base + reg);
}
u8
uhci_reg_read8 (hci_t *ctrl, usbreg reg)
{
return inb (ctrl->reg_base + reg);
}
void
uhci_reg_mask32 (hci_t *ctrl, usbreg reg, u32 andmask, u32 ormask)
{
uhci_reg_write32 (ctrl, reg,
(uhci_reg_read32 (ctrl, reg) & andmask) | ormask);
}
void
uhci_reg_mask16 (hci_t *ctrl, usbreg reg, u16 andmask, u16 ormask)
{
uhci_reg_write16 (ctrl, reg,
(uhci_reg_read16 (ctrl, reg) & andmask) | ormask);
}
void
uhci_reg_mask8 (hci_t *ctrl, usbreg reg, u8 andmask, u8 ormask)
{
uhci_reg_write8 (ctrl, reg,
(uhci_reg_read8 (ctrl, reg) & andmask) | ormask);
}

View File

@ -0,0 +1,124 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __UHCI_H
#define __UHCI_H
#include <pci.h>
#include "usb.h"
typedef union {
struct {
unsigned long terminate:1;
unsigned long queue_head:1;
unsigned long:2;
unsigned long ptr_part:28;
};
u32 ptr;
} __attribute__ ((packed)) flistp_t;
typedef struct {
union {
struct {
unsigned long terminate:1;
unsigned long queue_head:1;
unsigned long depth_first:1;
unsigned long:29;
} __attribute__ ((packed));
u32 ptr;
} __attribute__ ((packed));
volatile unsigned long actlen:11;
volatile unsigned long:5;
union {
struct {
unsigned long:1; // bit 0
unsigned long status_bitstuff_err:1;
unsigned long status_crc_err:1;
unsigned long status_nakrcvd:1;
unsigned long status_babble:1;
unsigned long status_databuf_err:1;
unsigned long status_stalled:1;
unsigned long status_active:1; // bit 7
} __attribute__ ((packed));
unsigned char status;
} __attribute__ ((packed));
volatile unsigned long ioc:1; /* interrupt on complete */
volatile unsigned long isochronous:1;
volatile unsigned long lowspeed:1;
volatile unsigned long counter:2;
volatile unsigned long shortpck:1;
volatile unsigned long:2;
unsigned long pid:8;
unsigned long dev_addr:7;
unsigned long endp:4;
unsigned long data_toggle:1;
unsigned long:1;
unsigned long maxlen:11;
u32 bufptr;
} __attribute__ ((packed))
td_t;
typedef struct {
flistp_t headlinkptr;
volatile flistp_t elementlinkptr;
} __attribute__ ((packed))
qh_t;
typedef enum { USBCMD = 0, USBSTS = 2, USBINTR = 4, FRNUM =
6, FLBASEADD = 8, SOFMOD = 0xc, PORTSC1 = 0x10, PORTSC2 =
0x12
} usbreg;
void uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value);
u32 uhci_reg_read32 (hci_t *ctrl, usbreg reg);
void uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value);
u16 uhci_reg_read16 (hci_t *ctrl, usbreg reg);
void uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value);
u8 uhci_reg_read8 (hci_t *ctrl, usbreg reg);
void uhci_reg_mask32 (hci_t *ctrl, usbreg reg, u32 andmask, u32 ormask);
void uhci_reg_mask16 (hci_t *ctrl, usbreg reg, u16 andmask, u16 ormask);
void uhci_reg_mask8 (hci_t *ctrl, usbreg reg, u8 andmask, u8 ormask);
typedef struct uhci {
flistp_t *framelistptr;
qh_t *qh_intr, *qh_data, *qh_last;
usbdev_t *roothub;
} uhci_t;
#define UHCI_INST(controller) ((uhci_t*)((controller)->instance))
hci_t *uhci_init (pcidev_t addr);
void uhci_rh_init (usbdev_t *dev);
#endif

View File

@ -0,0 +1,182 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <libpayload.h>
#include "uhci.h"
typedef struct {
int port[2];
} rh_inst_t;
#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
static void
uhci_rh_enable_port (usbdev_t *dev, int port)
{
u16 value;
hci_t *controller = dev->controller;
if (port == 1)
port = PORTSC1;
else
port = PORTSC2;
uhci_reg_mask16 (controller, port, ~(1 << 12), 0); /* wakeup */
uhci_reg_mask16 (controller, port, ~0, 1 << 9); /* reset */
mdelay (30); // >10ms
uhci_reg_mask16 (controller, port, ~(1 << 9), 0);
mdelay (1); // >5.3us per spec, <3ms because some devices make trouble
uhci_reg_mask16 (controller, port, ~0, 1 << 2); /* enable */
do {
value = uhci_reg_read16 (controller, port);
mdelay (1);
} while (((value & (1 << 2)) == 0) && (value & 0x01));
}
/* disable root hub */
static void
uhci_rh_disable_port (usbdev_t *dev, int port)
{
hci_t *controller = dev->controller;
port = PORTSC2;
if (port == 1)
port = PORTSC1;
uhci_reg_mask16 (controller, port, ~4, 0);
int value;
do {
value = uhci_reg_read16 (controller, port);
mdelay (1);
} while ((value & (1 << 2)) != 0);
}
static void
uhci_rh_scanport (usbdev_t *dev, int port)
{
int portsc, offset;
if (port == 1) {
portsc = PORTSC1;
offset = 0;
} else if (port == 2) {
portsc = PORTSC2;
offset = 1;
} else
return;
int devno = RH_INST (dev)->port[offset];
if (devno != -1) {
dev->controller->devices[devno].destroy (&dev->controller->
devices[devno]);
init_device_entry (dev->controller, devno);
RH_INST (dev)->port[offset] = -1;
}
uhci_reg_mask16 (dev->controller, portsc, ~0, (1 << 3) | (1 << 2)); // clear port state change, enable port
if ((uhci_reg_read16 (dev->controller, portsc) & 1) != 0) {
int newdev;
usbdev_t *newdev_t;
// device attached
uhci_rh_disable_port (dev, port);
uhci_rh_enable_port (dev, port);
int lowspeed =
(uhci_reg_read16 (dev->controller, portsc) >> 8) & 1;
printf ("%sspeed device\n", (lowspeed == 1) ? "low" : "full");
newdev = set_address (dev->controller, lowspeed);
if (newdev == -1)
return;
newdev_t = &dev->controller->devices[newdev];
RH_INST (dev)->port[offset] = newdev;
newdev_t->address = newdev;
newdev_t->hub = dev->address;
newdev_t->port = portsc;
// determine responsible driver
newdev_t->init (newdev_t);
}
}
static int
uhci_rh_report_port_changes (usbdev_t *dev)
{
int stored, real;
stored = (RH_INST (dev)->port[0] == -1);
real = ((uhci_reg_read16 (dev->controller, PORTSC1) & 1) == 0);
if (stored != real)
return 1;
stored = (RH_INST (dev)->port[1] == -1);
real = ((uhci_reg_read16 (dev->controller, PORTSC2) & 1) == 0);
if (stored != real)
return 2;
// maybe detach+attach happened between two scans?
if ((uhci_reg_read16 (dev->controller, PORTSC1) & 2) > 0)
return 1;
if ((uhci_reg_read16 (dev->controller, PORTSC2) & 2) > 0)
return 2;
// no change
return -1;
}
static void
uhci_rh_destroy (usbdev_t *dev)
{
uhci_rh_disable_port (dev, 1);
uhci_rh_disable_port (dev, 2);
free (RH_INST (dev));
}
static void
uhci_rh_poll (usbdev_t *dev)
{
int port;
while ((port = uhci_rh_report_port_changes (dev)) != -1)
uhci_rh_scanport (dev, port);
}
void
uhci_rh_init (usbdev_t *dev)
{
dev->destroy = uhci_rh_destroy;
dev->poll = uhci_rh_poll;
uhci_rh_enable_port (dev, 1);
uhci_rh_enable_port (dev, 2);
dev->data = malloc (sizeof (rh_inst_t));
RH_INST (dev)->port[0] = -1;
RH_INST (dev)->port[1] = -1;
/* we can set them here because a root hub _really_ shouldn't
appear elsewhere */
dev->address = 0;
dev->hub = -1;
dev->port = -1;
}

View File

@ -0,0 +1,350 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "usb.h"
hci_t *usb_hcs = 0;
hci_t *
new_controller ()
{
hci_t *controller = malloc (sizeof (hci_t));
/* atomic */
controller->next = usb_hcs;
usb_hcs = controller;
/* atomic end */
return controller;
}
void
detach_controller (hci_t *controller)
{
if (controller == 0)
return;
if (usb_hcs == controller) {
usb_hcs = controller->next;
} else {
hci_t *it = usb_hcs;
while (it != 0) {
if (it->next == controller) {
it->next = controller->next;
return;
}
}
}
}
/**
* Polls all hubs on all USB controllers, to find out about device changes
*/
void
usb_poll ()
{
if (usb_hcs == 0)
return;
hci_t *controller = usb_hcs;
while (controller != 0) {
int i;
for (i = 0; i < 128; i++) {
if (controller->devices[i].address != -1) {
controller->devices[i].poll (&controller->
devices[i]);
}
}
controller = controller->next;
}
}
void
init_device_entry (hci_t *controller, int i)
{
controller->devices[i].controller = controller;
controller->devices[i].address = -1;
controller->devices[i].hub = -1;
controller->devices[i].port = -1;
controller->devices[i].init = usb_nop_init;
controller->devices[i].init (&controller->devices[i]);
}
void
set_feature (usbdev_t *dev, int endp, int feature, int rtype)
{
dev_req_t dr;
dr.bmRequestType = rtype;
dr.data_dir = host_to_device;
dr.bRequest = SET_FEATURE;
dr.wValue = feature;
dr.wIndex = endp;
dr.wLength = 0;
dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
}
void
get_status (usbdev_t *dev, int intf, int rtype, int len, void *data)
{
dev_req_t dr;
dr.bmRequestType = rtype;
dr.data_dir = device_to_host;
dr.bRequest = GET_STATUS;
dr.wValue = 0;
dr.wIndex = intf;
dr.wLength = len;
dev->controller->control (dev, IN, sizeof (dr), &dr, len, data);
}
u8 *
get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType,
int descIdx, int langID)
{
u8 buf[8];
u8 *result;
dev_req_t dr;
int size;
dr.bmRequestType = bmRequestType;
dr.data_dir = device_to_host; // always like this for descriptors
dr.bRequest = GET_DESCRIPTOR;
dr.wValue = (descType << 8) | descIdx;
dr.wIndex = langID;
dr.wLength = 8;
if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) {
printf ("getting descriptor size (type %x) failed\n",
descType);
}
if (descType == 1) {
device_descriptor_t *dd = (device_descriptor_t *) buf;
printf ("maxPacketSize0: %x\n", dd->bMaxPacketSize0);
if (dd->bMaxPacketSize0 != 0)
dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
}
/* special case for configuration descriptors: they carry all their
subsequent descriptors with them, and keep the entire size at a
different location */
size = buf[0];
if (buf[1] == 2) {
int realsize = ((unsigned short *) (buf + 2))[0];
size = realsize;
}
result = malloc (size);
memset (result, 0, size);
dr.wLength = size;
if (dev->controller->
control (dev, IN, sizeof (dr), &dr, size, result)) {
printf ("getting descriptor (type %x, size %x) failed\n",
descType, size);
}
return result;
}
void
set_configuration (usbdev_t *dev)
{
dev_req_t dr;
dr.bmRequestType = 0;
dr.bRequest = SET_CONFIGURATION;
dr.wValue = dev->configuration[5];
dr.wIndex = 0;
dr.wLength = 0;
dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
}
int
clear_stall (endpoint_t *ep)
{
usbdev_t *dev = ep->dev;
int endp = ep->endpoint;
dev_req_t dr;
dr.bmRequestType = 0;
if (endp != 0) {
dr.req_recp = endp_recp;
}
dr.bRequest = CLEAR_FEATURE;
dr.wValue = ENDPOINT_HALT;
dr.wIndex = endp;
dr.wLength = 0;
dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
return 0;
}
/* returns free address or -1 */
static int
get_free_address (hci_t *controller)
{
int i;
for (i = 1; i < 128; i++) {
if (controller->devices[i].address != i)
return i;
}
printf ("no free address found\n");
return -1; // no free address
}
int
set_address (hci_t *controller, int lowspeed)
{
int adr = get_free_address (controller); // address to set
dev_req_t dr;
configuration_descriptor_t *cd;
device_descriptor_t *dd;
memset (&dr, 0, sizeof (dr));
dr.data_dir = host_to_device;
dr.req_type = standard_type;
dr.req_recp = dev_recp;
dr.bRequest = SET_ADDRESS;
dr.wValue = adr;
dr.wIndex = 0;
dr.wLength = 0;
usbdev_t *dev = &controller->devices[adr];
// dummy values for registering the address
dev->address = 0;
dev->lowspeed = lowspeed;
dev->endpoints[0].dev = dev;
dev->endpoints[0].endpoint = 0;
dev->endpoints[0].maxpacketsize = 8;
dev->endpoints[0].toggle = 0;
dev->endpoints[0].direction = SETUP;
mdelay (50);
if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) {
printf ("set_address failed\n");
return -1;
}
mdelay (50);
dev->address = adr;
dev->descriptor =
get_descriptor (dev,
gen_bmRequestType (device_to_host,
standard_type, dev_recp),
1, 0, 0);
dd = (device_descriptor_t *) dev->descriptor;
printf ("device version: %x.%x\n", dd->bcdUSB >> 8,
dd->bcdUSB & 0xff);
printf ("device has %x configurations\n", dd->bNumConfigurations);
if (dd->bNumConfigurations == 0) {
/* device isn't usable */
printf ("no usable configuration!\n");
dev->address = 0;
return -1;
}
dev->configuration =
get_descriptor (dev,
gen_bmRequestType (device_to_host,
standard_type, dev_recp),
2, 0, 0);
cd = (configuration_descriptor_t *) dev->configuration;
set_configuration (dev);
interface_descriptor_t *interface =
(interface_descriptor_t *) (((char *) cd) + cd->bLength);
{
int i;
int num = cd->bNumInterfaces;
interface_descriptor_t *current = interface;
printf ("device has %x interfaces\n", num);
num = (num > 5) ? 5 : num;
for (i = 0; i < num; i++) {
int j;
printf (" #%x has %x endpoints, interface %x:%x, protocol %x\n", current->bInterfaceNumber, current->bNumEndpoints, current->bInterfaceClass, current->bInterfaceSubClass, current->bInterfaceProtocol);
endpoint_descriptor_t *endp =
(endpoint_descriptor_t *) (((char *) current)
+
current->bLength);
if (interface->bInterfaceClass == 0x3)
endp = (endpoint_descriptor_t *) (((char *) endp) + ((char *) endp)[0]); // ignore HID descriptor
memset (dev->endpoints, 0, sizeof (dev->endpoints));
dev->num_endp = 1; // 0 always exists
dev->endpoints[0].dev = dev;
dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
dev->endpoints[0].direction = SETUP;
dev->endpoints[0].type = CONTROL;
for (j = 1; j <= current->bNumEndpoints; j++) {
static const char *transfertypes[4] =
{ "control", "isochronous", "bulk",
"interrupt"
};
printf (" #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", endp->wMaxPacketSize, transfertypes[endp->bmAttributes]);
endpoint_t *ep =
&dev->endpoints[dev->num_endp++];
ep->dev = dev;
ep->endpoint = endp->bEndpointAddress;
ep->toggle = 0;
ep->maxpacketsize = endp->wMaxPacketSize;
ep->direction =
((endp->bEndpointAddress & 0x80) ==
0) ? OUT : IN;
ep->type = endp->bmAttributes;
endp = (endpoint_descriptor_t
*) (((char *) endp) + endp->bLength);
}
current = (interface_descriptor_t *) endp;
}
}
int class = dd->bDeviceClass;
if (class == 0)
class = interface->bInterfaceClass;
enum { hid_device = 0x3, msc_device = 0x8, hub_device = 0x9 };
printf ("device of class %x found\n", class);
if (class == hub_device) {
printf ("hub found\n");
#ifdef CONFIG_USB_HUB
controller->devices[adr].init = usb_hub_init;
#else
printf ("support not compiled in\n");
#endif
}
if (class == hid_device) {
printf ("HID found\n");
#ifdef CONFIG_USB_HID
controller->devices[adr].init = usb_hid_init;
#else
printf ("support not compiled in\n");
#endif
}
if (class == msc_device) {
printf ("MSC found\n");
#ifdef CONFIG_USB_MSC
controller->devices[adr].init = usb_msc_init;
#else
printf ("support not compiled in\n");
#endif
}
return adr;
}

View File

@ -0,0 +1,224 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __USB_H
#define __USB_H
#include <libpayload.h>
#include <pci.h>
typedef enum { host_to_device = 0, device_to_host = 1 } dev_req_dir;
typedef enum { standard_type = 0, class_type = 1, vendor_type =
2, reserved_type = 3
} dev_req_type;
typedef enum { dev_recp = 0, iface_recp = 1, endp_recp = 2, other_recp = 3
} dev_req_recp;
typedef enum {
GET_STATUS = 0,
CLEAR_FEATURE = 1,
SET_FEATURE = 3,
SET_ADDRESS = 5,
GET_DESCRIPTOR = 6,
SET_DESCRIPTOR = 7,
GET_CONFIGURATION = 8,
SET_CONFIGURATION = 9,
GET_INTERFACE = 10,
SET_INTERFACE = 11,
SYNCH_FRAME = 12
} bRequest_Codes;
typedef enum {
ENDPOINT_HALT = 0,
DEVICE_REMOTE_WAKEUP = 1,
TEST_MODE = 2
} feature_selectors;
typedef struct {
union {
struct {
dev_req_recp req_recp:5;
dev_req_type req_type:2;
dev_req_dir data_dir:1;
} __attribute__ ((packed));
unsigned char bmRequestType;
} __attribute__ ((packed));
unsigned char bRequest;
unsigned short wValue;
unsigned short wIndex;
unsigned short wLength;
} __attribute__ ((packed)) dev_req_t;
struct usbdev_hc;
typedef struct usbdev_hc hci_t;
struct usbdev;
typedef struct usbdev usbdev_t;
typedef enum { SETUP = 0x2d, IN = 0x69, OUT = 0xe1 } pid_t;
typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3
} endpoint_type;
typedef struct {
usbdev_t *dev;
int endpoint;
pid_t direction;
int toggle;
int maxpacketsize;
endpoint_type type;
} endpoint_t;
struct usbdev {
hci_t *controller;
endpoint_t endpoints[32];
int num_endp;
int address; // usb address
int hub; // hub, device is attached to
int port; // port where device is attached
int lowspeed; // 1 if lowspeed device
void *data;
u8 *descriptor;
u8 *configuration;
void (*init) (usbdev_t *dev);
void (*destroy) (usbdev_t *dev);
void (*poll) (usbdev_t *dev);
};
struct usbdev_hc {
struct usbdev_hc *next;
pcidev_t bus_address;
u32 reg_base;
usbdev_t devices[128]; // dev 0 is root hub, 127 is last addressable
void (*start) (hci_t *controller);
void (*stop) (hci_t *controller);
void (*reset) (hci_t *controller);
void (*shutdown) (hci_t *controller);
int (*packet) (usbdev_t *dev, int endp, int pid, int toggle,
int length, u8 *data);
int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize);
int (*control) (usbdev_t *dev, pid_t pid, int dr_length,
void *devreq, int data_length, u8 *data);
void *instance;
};
typedef struct {
unsigned char bDescLength;
unsigned char bDescriptorType;
unsigned char bNbrPorts;
union {
struct {
unsigned long logicalPowerSwitchingMode:2;
unsigned long isCompoundDevice:1;
unsigned long overcurrentProtectionMode:2;
unsigned long ttThinkTime:2;
unsigned long arePortIndicatorsSupported:1;
unsigned long:8;
} __attribute__ ((packed));
unsigned short wHubCharacteristics;
} __attribute__ ((packed));
unsigned char bPowerOn2PwrGood;
unsigned char bHubContrCurrent;
char DeviceRemovable[];
} __attribute__ ((packed)) hub_descriptor_t;
typedef struct {
unsigned char bLength;
unsigned char bDescriptorType;
unsigned short bcdUSB;
unsigned char bDeviceClass;
unsigned char bDeviceSubClass;
unsigned char bDeviceProtocol;
unsigned char bMaxPacketSize0;
unsigned short idVendor;
unsigned short idProduct;
unsigned short bcdDevice;
unsigned char iManufacturer;
unsigned char iProduct;
unsigned char iSerialNumber;
unsigned char bNumConfigurations;
} __attribute__ ((packed)) device_descriptor_t;
typedef struct {
unsigned char bLength;
unsigned char bDescriptorType;
unsigned short wTotalLength;
unsigned char bNumInterfaces;
unsigned char bConfigurationValue;
unsigned char iConfiguration;
unsigned char bmAttributes;
unsigned char bMaxPower;
} __attribute__ ((packed)) configuration_descriptor_t;
typedef struct {
unsigned char bLength;
unsigned char bDescriptorType;
unsigned char bInterfaceNumber;
unsigned char bAlternateSetting;
unsigned char bNumEndpoints;
unsigned char bInterfaceClass;
unsigned char bInterfaceSubClass;
unsigned char bInterfaceProtocol;
unsigned char iInterface;
} __attribute__ ((packed)) interface_descriptor_t;
typedef struct {
unsigned char bLength;
unsigned char bDescriptorType;
unsigned char bEndpointAddress;
unsigned char bmAttributes;
unsigned short wMaxPacketSize;
unsigned char bInterval;
} __attribute__ ((packed)) endpoint_descriptor_t;
hci_t *new_controller (void);
void detach_controller (hci_t *controller);
void usb_poll (void);
void init_device_entry (hci_t *controller, int num);
void set_feature (usbdev_t *dev, int endp, int feature, int rtype);
void get_status (usbdev_t *dev, int endp, int rtype, int len, void *data);
int clear_stall (endpoint_t *ep);
void usb_nop_init (usbdev_t *dev);
void usb_hub_init (usbdev_t *dev);
void usb_hid_init (usbdev_t *dev);
void usb_msc_init (usbdev_t *dev);
int set_address (hci_t *controller, int lowspeed);
u8 *get_descriptor (usbdev_t *dev, unsigned char bmRequestType,
int descType, int descIdx, int langID);
static inline unsigned char
gen_bmRequestType (dev_req_dir dir, dev_req_type type, dev_req_recp recp)
{
return (dir << 7) | (type << 5) | recp;
}
#endif

View File

@ -0,0 +1,55 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "usb.h"
static void
usb_nop_destroy (usbdev_t *dev)
{
if (dev->descriptor != 0)
free (dev->descriptor);
usb_nop_init (dev);
dev->address = -1;
dev->hub = -1;
dev->port = -1;
}
static void
usb_nop_poll (usbdev_t *dev)
{
return;
}
void
usb_nop_init (usbdev_t *dev)
{
dev->descriptor = 0;
dev->destroy = usb_nop_destroy;
dev->poll = usb_nop_poll;
}

View File

@ -0,0 +1,51 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __USBDISK_H
#define __USBDISK_H
#include "usb.h"
/**
* To be implemented by libpayload-client. It's called by the USB stack
* when a new USB storage device is found, so the client has the chance
* to know about it.
*
* @param dev descriptor for the USB storage device
*/
void usbdisk_create (usbdev_t *dev);
/**
* To be implemented by libpayload-client. It's called by the USB stack
* when it finds out that a USB storage device is removed.
*
* @param dev descriptor for the USB storage device
*/
void usbdisk_remove (usbdev_t *dev);
#endif

View File

@ -0,0 +1,137 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "usb.h"
enum { hid_subclass_none = 0, hid_subclass_boot = 1 };
enum { hid_proto_boot = 0, hid_proto_report = 1 };
enum { hid_boot_proto_none = 0, hid_boot_proto_keyboard =
1, hid_boot_proto_mouse = 2
};
static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" };
enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT =
0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb
};
static void
usb_hid_destroy (usbdev_t *dev)
{
}
int keypress;
char keymap[256] = {
-1, -1, -1, -1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'1', '2',
'3', '4', '5', '6', '7', '8', '9', '0', '\n', TERM_ESC,
TERM_BACKSPACE, TERM_TAB, ' ', '-', '=', '[',
']', '\\', -1, ';', '\'', '`', ',', '.', '/', -1, -1, -1, -1, -1, -1,
-1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, TERM_HOME, TERM_PPAGE, -1,
TERM_END, TERM_NPAGE, TERM_RIGHT,
TERM_LEFT, TERM_DOWN, TERM_UP, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
static void
usb_hid_poll (usbdev_t *dev)
{
char buf[8];
static int toggle = 0;
// hardcode to endpoint 1, 8 bytes
dev->controller->packet (dev, 1, IN, toggle, 8, buf);
toggle ^= 1;
// FIXME: manage buf[0]=special keys, too
keypress = keymap[buf[2]];
if ((keypress == -1) && (buf[2] != 0)) {
printf ("%x %x %x %x %x %x %x %x\n", buf[0], buf[1], buf[2],
buf[3], buf[4], buf[5], buf[6], buf[7]);
}
}
int (*oldhook) (void);
int
hookfunc (void)
{
int key;
if (oldhook != 0)
key = oldhook ();
if (key == -1)
key = keypress;
return key;
}
void
usb_hid_init (usbdev_t *dev)
{
configuration_descriptor_t *cd = dev->configuration;
interface_descriptor_t *interface = ((char *) cd) + cd->bLength;
if (interface->bInterfaceSubClass == hid_subclass_boot) {
printf (" supports boot interface..\n");
printf (" it's a %s\n",
boot_protos[interface->bInterfaceProtocol]);
if (interface->bInterfaceProtocol == hid_boot_proto_keyboard) {
printf (" activating...\n");
dev_req_t dr;
// set_protocol(hid_proto_boot)
dr.data_dir = host_to_device;
dr.req_type = class_type;
dr.req_recp = iface_recp;
dr.bRequest = SET_PROTOCOL;
dr.wValue = hid_proto_boot;
dr.wIndex = interface->bInterfaceNumber;
dr.wLength = 0;
dev->controller->control (dev, OUT,
sizeof (dev_req_t), &dr, 0,
0);
// only add here, because we only support boot-keyboard HID devices
// FIXME: make this a real console input driver instead, once the API is there
dev->destroy = usb_hid_destroy;
dev->poll = usb_hid_poll;
oldhook = getkey_hook;
getkey_hook = hookfunc;
}
}
}

View File

@ -0,0 +1,158 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "usb.h"
// assume that host_to_device is overwritten if necessary
#define DR_PORT gen_bmRequestType(host_to_device, class_type, other_recp)
#define PORT_RESET 0x4
#define PORT_POWER 0x8
typedef struct {
int num_ports;
int *ports;
hub_descriptor_t *descriptor;
} usbhub_inst_t;
#define HUB_INST(dev) ((usbhub_inst_t*)(dev)->data)
static void
usb_hub_destroy (usbdev_t *dev)
{
free (HUB_INST (dev)->ports);
free (HUB_INST (dev)->descriptor);
free (HUB_INST (dev));
}
static void
usb_hub_scanport (usbdev_t *dev, int port)
{
int newdev;
unsigned short buf[2];
usbdev_t *newdev_t;
get_status (dev, port, DR_PORT, 4, buf);
int portstatus = ((buf[0] & 1) == 0);
int datastatus = (HUB_INST (dev)->ports[port] == -1);
if (portstatus == datastatus)
return; // no change - FIXME: read right fields for that test
if (!datastatus) {
int devno = HUB_INST (dev)->ports[port];
if (devno == -1)
fatal ("FATAL: illegal devno!\n");
dev->controller->devices[devno].destroy (&dev->controller->
devices[devno]);
init_device_entry (dev->controller, devno);
HUB_INST (dev)->ports[port] = -1;
return;
}
set_feature (dev, port, PORT_RESET, DR_PORT);
mdelay (20);
get_status (dev, port, DR_PORT, 4, buf);
int lowspeed = (buf[0] >> 9) & 1;
newdev = set_address (dev->controller, lowspeed);
if (newdev == -1)
return;
newdev_t = &dev->controller->devices[newdev];
HUB_INST (dev)->ports[port] = newdev;
newdev_t->address = newdev;
newdev_t->hub = dev->address;
newdev_t->port = port;
// determine responsible driver
newdev_t->init (newdev_t);
}
static int
usb_hub_report_port_changes (usbdev_t *dev)
{
int port;
unsigned short buf[2];
for (port = 1; port <= HUB_INST (dev)->num_ports; port++) {
get_status (dev, port, DR_PORT, 4, buf);
// FIXME: proper change detection
int portstatus = ((buf[0] & 1) == 0);
int datastatus = (HUB_INST (dev)->ports[port] == -1);
if (portstatus != datastatus)
return port;
}
// no change
return -1;
}
static void
usb_hub_enable_port (usbdev_t *dev, int port)
{
set_feature (dev, port, PORT_POWER, DR_PORT);
mdelay (20);
}
#if 0
static void
usb_hub_disable_port (usbdev_t *dev, int port)
{
}
#endif
static void
usb_hub_poll (usbdev_t *dev)
{
int port;
while ((port = usb_hub_report_port_changes (dev)) != -1)
usb_hub_scanport (dev, port);
}
void
usb_hub_init (usbdev_t *dev)
{
int i;
dev->destroy = usb_hub_destroy;
dev->poll = usb_hub_poll;
dev->data = malloc (sizeof (usbhub_inst_t));
HUB_INST (dev)->descriptor =
(hub_descriptor_t *) get_descriptor (dev,
gen_bmRequestType
(device_to_host,
class_type, dev_recp),
0x29, 0, 0);
HUB_INST (dev)->num_ports = HUB_INST (dev)->descriptor->bNbrPorts;
HUB_INST (dev)->ports =
malloc (sizeof (int) * (HUB_INST (dev)->num_ports + 1));
for (i = 1; i <= HUB_INST (dev)->num_ports; i++)
HUB_INST (dev)->ports[i] = -1;
for (i = 1; i <= HUB_INST (dev)->num_ports; i++)
usb_hub_enable_port (dev, i);
}

View File

@ -0,0 +1,120 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include "usb.h"
#include "uhci.h"
#include "usbdisk.h"
/**
* Initializes USB controller attached to PCI
*
* @param bus PCI bus number
* @param dev PCI device id at bus
* @param func function id of the controller
*/
int
usb_controller_initialize (int bus, int dev, int func)
{
u32 class;
u32 devclass;
u32 prog_if;
pcidev_t addr;
u32 pciid;
addr = PCI_DEV (bus, dev, func);
class = pci_read_config32 (addr, 8);
pciid = pci_read_config32 (addr, 0);
devclass = class >> 16;
prog_if = (class >> 8) & 0xff;
/* enable busmaster */
#define PCI_COMMAND 4
#define PCI_COMMAND_MASTER 4
pci_write_config32 (addr, PCI_COMMAND,
pci_read_config32 (addr,
PCI_COMMAND) |
PCI_COMMAND_MASTER);
if (devclass == 0xc03) {
printf ("%02x:%02x.%x %04x:%04x.%d ", 0, dev, func,
pciid >> 16, pciid & 0xFFFF, func);
if (prog_if == 0) {
printf ("UHCI controller\n");
#ifdef CONFIG_USB_UHCI
uhci_init (addr);
usb_poll ();
usb_poll ();
#else
printf ("Not supported.\n");
#endif
}
if (prog_if == 0x10) {
printf ("OHCI controller\n");
#ifdef CONFIG_USB_OHCI
// ohci_init(addr);
#else
printf ("Not supported.\n");
#endif
}
if (prog_if == 0x20) {
printf ("EHCI controller\n");
#ifdef CONFIG_USB_EHCI
// ehci_init(addr);
#else
printf ("Not supported.\n");
#endif
}
}
return 0;
}
/**
* Initialize all USB controllers attached to PCI.
*/
int
usb_initialize (void)
{
int bus, dev, func;
for (bus = 0; bus < 256; bus++)
for (dev = 0; dev < 32; dev++)
for (func = 0; func < 8; func++)
usb_controller_initialize (bus, dev, func);
return 0;
}
int
usb_exit (void)
{
return 0;
}

View File

@ -0,0 +1,397 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <arch/endian.h>
#include "usb.h"
#include "usbmsc.h"
#include "usbdisk.h"
enum {
msc_subclass_rbc = 0x1,
msc_subclass_mmc2 = 0x2,
msc_subclass_qic157 = 0x3,
msc_subclass_ufi = 0x4,
msc_subclass_sff8070i = 0x5,
msc_subclass_scsitrans = 0x6
};
static const char *msc_subclass_strings[7] = {
"(none)",
"RBC",
"MMC-2",
"QIC-157",
"UFI",
"SFF-8070i",
"SCSI transparent"
};
enum {
msc_proto_cbi_wcomp = 0x0,
msc_proto_cbi_wocomp = 0x1,
msc_proto_bulk_only = 0x50
};
static const char *msc_protocol_strings[0x51] = {
"Control/Bulk/Interrupt protocol (with command completion interrupt)",
"Control/Bulk/Interrupt protocol (with no command completion interrupt)",
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
"Bulk-Only Transport"
};
static void
usb_msc_destroy (usbdev_t *dev)
{
usbdisk_remove (dev);
free (dev->data);
dev->data = 0;
}
static void
usb_msc_poll (usbdev_t *dev)
{
}
const int DEV_RESET = 0xff;
const int GET_MAX_LUN = 0xfe;
const unsigned int cbw_signature = 0x43425355;
const unsigned int csw_signature = 0x53425355;
typedef struct {
unsigned int dCBWSignature;
unsigned int dCBWTag;
unsigned int dCBWDataTransferLength;
unsigned char bmCBWFlags;
unsigned long bCBWLUN:4;
unsigned long:4;
unsigned long bCBWCBLength:5;
unsigned long:3;
unsigned char CBWCB[31 - 15];
} __attribute__ ((packed))
cbw_t;
typedef struct {
unsigned int dCSWSignature;
unsigned int dCSWTag;
unsigned int dCSWDataResidue;
unsigned char bCSWStatus;
} __attribute__ ((packed))
csw_t;
static void
reset_transport (usbdev_t *dev)
{
dev_req_t dr;
memset (&dr, 0, sizeof (dr));
dr.bmRequestType = 0;
dr.data_dir = host_to_device;
#ifndef QEMU
dr.req_type = class_type;
dr.req_recp = iface_recp;
#endif
dr.bRequest = DEV_RESET;
dr.wValue = 0;
dr.wIndex = 0;
dr.wLength = 0;
dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
clear_stall (MSC_INST (dev)->bulk_in);
clear_stall (MSC_INST (dev)->bulk_out);
}
/* device may stall this command, so beware! */
static int
get_max_luns (usbdev_t *dev)
{
unsigned char luns = 75;
dev_req_t dr;
dr.bmRequestType = 0;
dr.data_dir = device_to_host;
#ifndef QEMU
dr.req_type = class_type;
dr.req_recp = iface_recp;
#endif
dr.bRequest = GET_MAX_LUN;
dr.wValue = 0;
dr.wIndex = 0;
dr.wLength = 1;
if (dev->controller->control (dev, IN, sizeof (dr), &dr, 1, &luns)) {
luns = 0; // assume only 1 lun if req fails
}
return luns;
}
int tag;
int lun = 0;
static void
wrap_cbw (cbw_t *cbw, int datalen, cbw_direction dir, const u8 *cmd,
int cmdlen)
{
memset (cbw, 0, sizeof (cbw_t));
cbw->dCBWSignature = cbw_signature;
cbw->dCBWTag = tag++;
cbw->bCBWLUN = lun; // static value per device
cbw->dCBWDataTransferLength = datalen;
cbw->bmCBWFlags = dir;
memcpy (cbw->CBWCB, cmd, sizeof (cbw->CBWCB));
cbw->bCBWCBLength = cmdlen;
}
static void
get_csw (endpoint_t *ep, csw_t *csw)
{
ep->dev->controller->bulk (ep, sizeof (csw_t), (u8 *) csw, 1);
}
static int
execute_command (usbdev_t *dev, cbw_direction dir, const u8 *cb, int cblen,
u8 *buf, int buflen)
{
cbw_t cbw;
csw_t csw;
int always_succeed = 0;
if ((cb[0] == 0x1b) && (cb[4] == 1)) { //start command, always succeed
always_succeed = 1;
}
wrap_cbw (&cbw, buflen, dir, cb, cblen);
if (dev->controller->
bulk (MSC_INST (dev)->bulk_out, sizeof (cbw), (u8 *) &cbw, 0)) {
clear_stall (MSC_INST (dev)->bulk_out);
return 1;
}
mdelay (10);
if (dir == cbw_direction_data_in) {
if (dev->controller->
bulk (MSC_INST (dev)->bulk_in, buflen, buf, 0)) {
clear_stall (MSC_INST (dev)->bulk_in);
return 1;
}
} else {
if (dev->controller->
bulk (MSC_INST (dev)->bulk_out, buflen, buf, 0)) {
clear_stall (MSC_INST (dev)->bulk_out);
return 1;
}
}
get_csw (MSC_INST (dev)->bulk_in, &csw);
if (always_succeed == 1) {
// return success, regardless of message
return 0;
}
if (csw.bCSWStatus == 2) {
// phase error, reset transport
reset_transport (dev);
return 1;
}
if (csw.bCSWStatus == 0) {
// no error, exit
return 0;
}
// error "check condition" or reserved error
return 1;
}
typedef struct {
unsigned char command; //0
unsigned char res1; //1
unsigned int block; //2-5
unsigned char res2; //6
unsigned short numblocks; //7-8
unsigned char res3; //9 - the block is 10 bytes long
} __attribute__ ((packed)) cmdblock_t;
typedef struct {
unsigned char command; //0
unsigned char res1; //1
unsigned char res2; //2
unsigned char res3; //3
unsigned char lun; //4
unsigned char res4; //5
} __attribute__ ((packed)) cmdblock6_t;
/**
* Reads or writes a number of sequential blocks on a USB storage device.
* As it uses the READ(10) SCSI-2 command, it's limited to storage devices
* of at most 2TB. It assumes sectors of 512 bytes.
*
* @param dev device to access
* @param start first sector to access
* @param n number of sectors to access
* @param dir direction of access: cbw_direction_data_in == read, cbw_direction_data_out == write
* @param buf buffer to read into or write from. Must be at least n*512 bytes
* @return 0 on success, 1 on failure
*/
int
readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir, u8 *buf)
{
cmdblock_t cb;
memset (&cb, 0, sizeof (cb));
if (dir == cbw_direction_data_in) {
// read
cb.command = 0x28;
} else {
// write
cb.command = 0x2a;
}
cb.block = ntohl (start);
cb.numblocks = ntohw (n);
return execute_command (dev, dir, (u8 *) &cb, sizeof (cb), buf,
n * 512);
}
static int
test_unit_ready (usbdev_t *dev)
{
cmdblock6_t cb;
memset (&cb, 0, sizeof (cb)); // full initialization for T-U-R
return execute_command (dev, cbw_direction_data_out, (u8 *) &cb,
sizeof (cb), 0, 0);
}
static int
spin_up (usbdev_t *dev)
{
cmdblock6_t cb;
memset (&cb, 0, sizeof (cb));
cb.command = 0x1b;
cb.lun = 1;
return execute_command (dev, cbw_direction_data_out, (u8 *) &cb,
sizeof (cb), 0, 0);
}
static void
read_capacity (usbdev_t *dev)
{
cmdblock_t cb;
memset (&cb, 0, sizeof (cb));
cb.command = 0x25; // read capacity
u8 buf[8];
int count = 0;
while ((count++ < 20)
&&
(execute_command
(dev, cbw_direction_data_in, (u8 *) &cb, sizeof (cb), buf,
8) == 1));
if (count >= 20) {
// still not successful, assume 2tb in 512byte sectors, which is just the same garbage as any other number, but probably reasonable.
printf ("assuming 2TB in 512byte sectors as READ CAPACITY didn't answer.\n");
MSC_INST (dev)->numblocks = 0xffffffff;
MSC_INST (dev)->blocksize = 512;
} else {
MSC_INST (dev)->numblocks = ntohl (*(u32 *) buf) + 1;
MSC_INST (dev)->blocksize = ntohl (*(u32 *) (buf + 4));
}
printf (" has %d blocks sized %db\n", MSC_INST (dev)->numblocks,
MSC_INST (dev)->blocksize);
}
void
usb_msc_init (usbdev_t *dev)
{
int i, timeout;
dev->destroy = usb_msc_destroy;
dev->poll = usb_msc_poll;
configuration_descriptor_t *cd =
(configuration_descriptor_t *) dev->configuration;
interface_descriptor_t *interface =
(interface_descriptor_t *) (((char *) cd) + cd->bLength);
printf (" it uses %s command set\n",
msc_subclass_strings[interface->bInterfaceSubClass]);
printf (" it uses %s protocol\n",
msc_protocol_strings[interface->bInterfaceProtocol]);
if ((interface->bInterfaceProtocol != 0x50)
|| (interface->bInterfaceSubClass != 6)) {
/* Other protocols, such as ATAPI don't seem to be very popular. looks like ATAPI would be really easy to add, if necessary. */
printf (" Only SCSI over Bulk is supported.\n");
return;
}
dev->data = malloc (sizeof (usbmsc_inst_t));
MSC_INST (dev)->bulk_in = 0;
MSC_INST (dev)->bulk_out = 0;
for (i = 1; i <= dev->num_endp; i++) {
if (dev->endpoints[i].endpoint == 0)
continue;
if (dev->endpoints[i].type != BULK)
continue;
if ((dev->endpoints[i].direction == IN)
&& (MSC_INST (dev)->bulk_in == 0))
MSC_INST (dev)->bulk_in = &dev->endpoints[i];
if ((dev->endpoints[i].direction == OUT)
&& (MSC_INST (dev)->bulk_out == 0))
MSC_INST (dev)->bulk_out = &dev->endpoints[i];
}
if (MSC_INST (dev)->bulk_in == 0)
fatal ("couldn't find bulk-in endpoint");
if (MSC_INST (dev)->bulk_out == 0)
fatal ("couldn't find bulk-out endpoint");
printf (" using endpoint %x as in, %x as out\n",
MSC_INST (dev)->bulk_in->endpoint,
MSC_INST (dev)->bulk_out->endpoint);
printf (" has %d luns\n", get_max_luns (dev) + 1);
printf (" Waiting for device to become ready... ");
timeout = 10;
while (test_unit_ready (dev) && --timeout) {
mdelay (100);
printf (".");
}
if (test_unit_ready (dev)) {
printf ("timeout. Device not ready. Still trying...\n");
} else {
printf ("ok.\n");
}
printf (" spin up");
for (i = 0; i < 30; i++) {
printf (".");
if (!spin_up (dev)) {
printf (" OK.");
break;
}
mdelay (100);
}
printf ("\n");
read_capacity (dev);
usbdisk_create (dev);
}

View File

@ -0,0 +1,47 @@
/*
* This file is part of the libpayload project.
*
* Copyright (C) 2008 coresystems GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __USBMSC_H
#define __USBMSC_H
typedef struct {
unsigned int blocksize;
unsigned int numblocks;
endpoint_t *bulk_in;
endpoint_t *bulk_out;
} usbmsc_inst_t;
#define MSC_INST(dev) ((usbmsc_inst_t*)(dev)->data)
typedef enum { cbw_direction_data_in = 0x80, cbw_direction_data_out = 0
} cbw_direction;
int readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir,
u8 *buf);
#endif