Initial support for USB, UHCI, and USB Keyboards.

This adds preliminary support for USB controllers and keyboards.
Add support for UHCI controllers.
Add support for "HID" USB keyboards.
Also, fix bug in hexdump() - len need not be power of 4.
This commit is contained in:
Kevin O'Connor 2009-09-28 21:32:08 -04:00
parent 6aee52dd45
commit 114592f000
15 changed files with 1060 additions and 6 deletions

View File

@ -13,7 +13,8 @@ OUT=out/
# Source files
SRCBOTH=output.c util.c block.c floppy.c ata.c misc.c mouse.c kbd.c pci.c \
serial.c clock.c pic.c cdrom.c ps2port.c smp.c resume.c \
pnpbios.c pirtable.c vgahooks.c pmm.c ramdisk.c
pnpbios.c pirtable.c vgahooks.c pmm.c ramdisk.c \
usb.c usb-uhci.c usb-hid.c
SRC16=$(SRCBOTH) system.c disk.c apm.c pcibios.c font.c
SRC32=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \
acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \
@ -136,7 +137,7 @@ $(OUT)rom32.o: $(OUT)code32.o $(OUT)romlayout32.lds
$(Q)$(LD) -r -T $(OUT)romlayout32.lds $< -o $@
$(OUT)rom.o: $(OUT)rom16.o $(OUT)rom32.o $(OUT)rombios16.lds $(OUT)rombios.lds
@echo " Linking $@ (version \"$(VERSION)\")"
@echo " Linking $@"
$(Q)$(LD) -T $(OUT)rombios16.lds $(OUT)rom16.o -R $(OUT)rom32.o -o $(OUT)rom16.reloc.o
$(Q)$(STRIP) $(OUT)rom16.reloc.o -o $(OUT)rom16.final.o
$(Q)$(OBJCOPY) --adjust-vma 0xf0000 $(OUT)rom16.o $(OUT)rom16.moved.o

View File

@ -12,6 +12,7 @@
#include "pic.h" // eoi_pic1
#include "bregs.h" // struct bregs
#include "biosvar.h" // GET_GLOBAL
#include "usb-hid.h" // usb_check_key
// RTC register flags
#define RTC_A_UIP 0x80
@ -435,6 +436,8 @@ handle_08()
SET_BDA(timer_counter, counter);
usb_check_key();
// chain to user timer tick INT #0x1c
u32 eax=0, flags;
call16_simpint(0x1c, &eax, &flags);

View File

@ -28,6 +28,12 @@
#define CONFIG_DRIVES 1
// Support floppy drive access
#define CONFIG_FLOPPY 1
// Support USB devices
#define CONFIG_USB 1
// Support USB UHCI controllers
#define CONFIG_USB_UHCI 1
// Support USB keyboards
#define CONFIG_USB_KEYBOARD 1
// Support for IDE disk code
#define CONFIG_ATA 1
// Use 32bit PIO accesses on ATA (minor optimization on PCI transfers)

View File

@ -622,7 +622,7 @@ __process_key(u8 scancode)
SET_BDA(kbd_flag2, flags2);
}
static void
void
process_key(u8 key)
{
if (CONFIG_KBD_CALL_INT15_4F) {

View File

@ -296,10 +296,10 @@ printf(const char *fmt, ...)
}
void
hexdump(void *d, int len)
hexdump(const void *d, int len)
{
int count=0;
while (len) {
while (len > 0) {
if (count % 8 == 0) {
putc(0, '\n');
puthex(0, count*4, 8);

View File

@ -178,3 +178,11 @@ pci_find_class(u16 classid)
}
return -1;
}
void
pci_set_bus_master(u16 bdf)
{
u16 val = pci_config_readw(bdf, PCI_COMMAND);
val |= PCI_COMMAND_MASTER;
pci_config_writew(bdf, PCI_COMMAND, val);
}

View File

@ -29,6 +29,7 @@ u8 pci_config_readb(u16 bdf, u32 addr);
int pci_find_vga();
int pci_find_device(u16 vendid, u16 devid);
int pci_find_class(u16 classid);
void pci_set_bus_master(u16 bdf);
int pci_next(int bdf, int *pmax);
#define foreachpci(BDF, MAX) \

View File

@ -19,6 +19,7 @@
#include "bregs.h" // struct bregs
#include "mptable.h" // mptable_init
#include "boot.h" // IPL
#include "usb.h" // usb_setup
void
__set_irq(int vector, void *loc)
@ -177,6 +178,7 @@ post()
pnp_setup();
vga_setup();
usb_setup();
kbd_setup();
lpt_setup();
serial_setup();

185
src/usb-hid.c Normal file
View File

@ -0,0 +1,185 @@
// Code for handling USB Human Interface Devices (HID).
//
// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "util.h" // dprintf
#include "usb-hid.h" // usb_keyboard_setup
#include "config.h" // CONFIG_*
#include "usb.h" // usb_ctrlrequest
#include "biosvar.h" // GET_GLOBAL
void *keyboard_pipe VAR16VISIBLE;
/****************************************************************
* Setup
****************************************************************/
static int
set_protocol(u32 endp, u16 val)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
req.bRequest = HID_REQ_SET_PROTOCOL;
req.wValue = val;
req.wIndex = 0;
req.wLength = 0;
return send_default_control(endp, &req, NULL);
}
static int
set_idle(u32 endp, u8 val)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
req.bRequest = HID_REQ_SET_IDLE;
req.wValue = val<<8;
req.wIndex = 0;
req.wLength = 0;
return send_default_control(endp, &req, NULL);
}
int
usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface, int imax)
{
if (! CONFIG_USB_KEYBOARD)
return -1;
if (keyboard_pipe)
return -1;
dprintf(2, "usb_keyboard_setup %x\n", endp);
struct usb_endpoint_descriptor *epdesc = (void*)&iface[1];
for (;;) {
if ((void*)epdesc >= (void*)iface + imax
|| epdesc->bDescriptorType == USB_DT_INTERFACE) {
dprintf(1, "No keyboard intr in?\n");
return -1;
}
if (epdesc->bDescriptorType == USB_DT_ENDPOINT
&& (epdesc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN
&& ((epdesc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_INT)
&& epdesc->wMaxPacketSize == 8)
break;
epdesc = (void*)epdesc + epdesc->bLength;
}
u32 inendp = mkendp(endp2cntl(endp), endp2devaddr(endp)
, epdesc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK
, endp2speed(endp), epdesc->wMaxPacketSize);
// Enable "boot" protocol.
int ret = set_protocol(endp, 1);
if (ret)
return -1;
// Only send reports on a new key event.
ret = set_idle(endp, 0);
if (ret)
return -1;
void *pipe = alloc_intr_pipe(inendp, epdesc->bInterval);
if (!pipe)
return -1;
keyboard_pipe = pipe;
return 0;
}
void
usb_keyboard_setup()
{
if (! CONFIG_USB_KEYBOARD)
return;
keyboard_pipe = NULL;
}
/****************************************************************
* Keyboard events
****************************************************************/
static u16 KeyToScanCode[] VAR16 = {
0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x0030, 0x002e, 0x0020,
0x0012, 0x0021, 0x0022, 0x0023, 0x0017, 0x0024, 0x0025, 0x0026,
0x0032, 0x0031, 0x0018, 0x0019, 0x0010, 0x0013, 0x001f, 0x0014,
0x0016, 0x002f, 0x0011, 0x002d, 0x0015, 0x002c, 0x0002, 0x0003,
0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b,
0x001c, 0x0001, 0x000e, 0x000f, 0x0039, 0x000c, 0x000d, 0x001a,
0x001b, 0x002b, 0x0000, 0x0027, 0x0028, 0x0029, 0x0033, 0x0034,
0x0035, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040,
0x0041, 0x0042, 0x0043, 0x0044, 0x0057, 0x0058, 0xe037, 0x0046,
0xe11d, 0xe052, 0xe047, 0xe049, 0xe053, 0xe04f, 0xe051, 0xe04d,
0xe04b, 0xe050, 0xe048, 0x0045, 0xe035, 0x0037, 0x004a, 0x004e,
0xe01c, 0x004f, 0x0050, 0x0051, 0x004b, 0x004c, 0x004d, 0x0047,
0x0048, 0x0049, 0x0052, 0x0053
};
static u16 ModifierToScanCode[] VAR16 = {
//lcntl, lshift, lalt, lgui, rcntl, rshift, ralt, rgui
0x001d, 0x002a, 0x0038, 0xe05b, 0xe01d, 0x0036, 0xe038, 0xe05c
};
struct keyevent {
u8 modifiers;
u8 reserved;
u8 keys[6];
};
static void
prockeys(u16 keys)
{
if (keys > 0xff) {
u8 key = keys>>8;
if (key == 0xe1) {
// Pause key
process_key(0xe1);
process_key(0x1d | (keys & 0x80));
process_key(0x45 | (keys & 0x80));
return;
}
process_key(key);
}
process_key(keys);
}
static void
handle_key(struct keyevent *data)
{
dprintf(5, "Got key %x %x\n", data->modifiers, data->keys[0]);
// XXX
int i;
for (i=0; i<8; i++)
if (data->modifiers & (1<<i))
prockeys(GET_GLOBAL(ModifierToScanCode[i]));
for (i=0; i<ARRAY_SIZE(data->keys); i++) {
u8 key = data->keys[i];
if (key >= ARRAY_SIZE(KeyToScanCode))
continue;
key = GET_GLOBAL(KeyToScanCode[key]);
if (!key)
continue;
prockeys(key);
}
for (i=0; i<8; i++)
if (data->modifiers & (1<<i))
prockeys(GET_GLOBAL(ModifierToScanCode[i]) | 0x80);
}
void
usb_check_key()
{
if (! CONFIG_USB_KEYBOARD)
return;
void *pipe = GET_GLOBAL(keyboard_pipe);
if (!pipe)
return;
for (;;) {
struct keyevent data;
int ret = usb_poll_intr(pipe, &data);
if (ret)
break;
handle_key(&data);
}
}

27
src/usb-hid.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef __USB_HID_H
#define __USB_HID_H
// usb-hid.c
struct usb_interface_descriptor;
int usb_keyboard_init(u32 endp, struct usb_interface_descriptor *iface
, int imax);
void usb_keyboard_setup();
void usb_check_key();
/****************************************************************
* hid flags
****************************************************************/
#define USB_INTERFACE_SUBCLASS_BOOT 1
#define USB_INTERFACE_PROTOCOL_KEYBOARD 1
#define USB_INTERFACE_PROTOCOL_MOUSE 2
#define HID_REQ_GET_REPORT 0x01
#define HID_REQ_GET_IDLE 0x02
#define HID_REQ_GET_PROTOCOL 0x03
#define HID_REQ_SET_REPORT 0x09
#define HID_REQ_SET_IDLE 0x0A
#define HID_REQ_SET_PROTOCOL 0x0B
#endif // ush-hid.h

299
src/usb-uhci.c Normal file
View File

@ -0,0 +1,299 @@
// Code for handling UHCI USB controllers.
//
// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "util.h" // dprintf
#include "pci.h" // pci_bdf_to_bus
#include "config.h" // CONFIG_*
#include "ioport.h" // outw
#include "usb-uhci.h" // USBLEGSUP
#include "pci_regs.h" // PCI_BASE_ADDRESS_4
#include "usb.h" // struct usb_s
#include "farptr.h" // GET_FLATPTR
#include "biosvar.h" // GET_GLOBAL
static void
reset_uhci(struct usb_s *cntl)
{
// XXX - don't reset if not needed.
// Reset PIRQ and SMI
pci_config_writew(cntl->bdf, USBLEGSUP, USBLEGSUP_RWC);
// Reset the HC
outw(USBCMD_HCRESET, cntl->iobase + USBCMD);
udelay(5);
// Disable interrupts and commands (just to be safe).
outw(0, cntl->iobase + USBINTR);
outw(0, cntl->iobase + USBCMD);
}
static void
configure_uhci(struct usb_s *cntl)
{
// Allocate ram for schedule storage
struct uhci_td *term_td = malloc_high(sizeof(*term_td));
struct uhci_framelist *fl = memalign_high(sizeof(*fl), sizeof(*fl));
struct uhci_qh *data_qh = malloc_low(sizeof(*data_qh));
struct uhci_qh *term_qh = malloc_high(sizeof(*term_qh));
if (!term_td || !fl || !data_qh || !term_qh) {
dprintf(1, "No ram for uhci init");
return;
}
// Work around for PIIX errata
memset(term_td, 0, sizeof(*term_td));
term_td->link = UHCI_PTR_TERM;
term_td->token = (uhci_explen(0) | (0x7f << TD_TOKEN_DEVADDR_SHIFT)
| USB_PID_IN);
memset(term_qh, 0, sizeof(*term_qh));
term_qh->element = (u32)term_td;
term_qh->link = UHCI_PTR_TERM;
// Setup primary queue head.
memset(data_qh, 0, sizeof(*data_qh));
data_qh->element = UHCI_PTR_TERM;
data_qh->link = (u32)term_qh | UHCI_PTR_QH;
cntl->qh = data_qh;
// Set schedule to point to primary queue head
int i;
for (i=0; i<ARRAY_SIZE(fl->links); i++) {
fl->links[i] = (u32)data_qh | UHCI_PTR_QH;
}
// Set the frame length to the default: 1 ms exactly
outb(USBSOF_DEFAULT, cntl->iobase + USBSOF);
// Store the frame list base address
outl((u32)fl->links, cntl->iobase + USBFLBASEADD);
// Set the current frame number
outw(0, cntl->iobase + USBFRNUM);
}
static void
start_uhci(struct usb_s *cntl)
{
// Mark as configured and running with a 64-byte max packet.
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, cntl->iobase + USBCMD);
}
// Find any devices connected to the root hub.
static int
check_ports(struct usb_s *cntl)
{
u16 port1 = inw(cntl->iobase + USBPORTSC1);
u16 port2 = inw(cntl->iobase + USBPORTSC2);
if (!((port1 & USBPORTSC_CCS) || (port2 & USBPORTSC_CCS)))
// No devices
return 0;
// reset ports
if (port1 & USBPORTSC_CCS)
outw(USBPORTSC_PR, cntl->iobase + USBPORTSC1);
if (port2 & USBPORTSC_CCS)
outw(USBPORTSC_PR, cntl->iobase + USBPORTSC2);
mdelay(10);
outw(0, cntl->iobase + USBPORTSC1);
outw(0, cntl->iobase + USBPORTSC2);
mdelay(10);
// Configure ports
int totalcount = 0;
port1 = inw(cntl->iobase + USBPORTSC1);
if (port1 & USBPORTSC_CCS) {
outw(USBPORTSC_PE, cntl->iobase + USBPORTSC1);
int count = configure_usb_device(cntl, !!(port1 & USBPORTSC_LSDA));
if (! count)
outw(0, cntl->iobase + USBPORTSC1);
totalcount += count;
}
port2 = inw(cntl->iobase + USBPORTSC2);
if (port2 & USBPORTSC_CCS) {
outw(USBPORTSC_PE, cntl->iobase + USBPORTSC2);
int count = configure_usb_device(cntl, !!(port2 & USBPORTSC_LSDA));
if (! count)
outw(0, cntl->iobase + USBPORTSC2);
totalcount += count;
}
return totalcount;
}
int
uhci_init(struct usb_s *cntl)
{
if (! CONFIG_USB_UHCI)
return 0;
cntl->iobase = (pci_config_readl(cntl->bdf, PCI_BASE_ADDRESS_4)
& PCI_BASE_ADDRESS_IO_MASK);
dprintf(3, "UHCI init on dev %02x:%02x.%x (io=%x)\n"
, pci_bdf_to_bus(cntl->bdf), pci_bdf_to_dev(cntl->bdf)
, pci_bdf_to_fn(cntl->bdf), cntl->iobase);
pci_set_bus_master(cntl->bdf);
reset_uhci(cntl);
configure_uhci(cntl);
start_uhci(cntl);
int count = check_ports(cntl);
if (! count) {
// XXX - no devices; free data structures.
return 0;
}
return count;
}
static int
wait_qh(struct uhci_qh *qh)
{
// XXX - 500ms just a guess
u64 end = calc_future_tsc(500);
for (;;) {
if (qh->element & UHCI_PTR_TERM)
return 0;
if (rdtscll() > end) {
dprintf(1, "Timeout on wait_qh %p\n", qh);
return -1;
}
cpu_relax();
}
}
int
uhci_control(u32 endp, int dir, const void *cmd, int cmdsize
, void *data, int datasize)
{
if (! CONFIG_USB_UHCI)
return 0;
dprintf(5, "uhci_control %x\n", endp);
struct usb_s *cntl = endp2cntl(endp);
int maxpacket = endp2maxsize(endp);
int lowspeed = endp2speed(endp);
int devaddr = endp2devaddr(endp) | (endp2ep(endp) << 7);
// Setup transfer descriptors
int count = 2 + DIV_ROUND_UP(datasize, maxpacket);
struct uhci_td *tds = malloc_tmphigh(sizeof(*tds) * count);
tds[0].link = (u32)&tds[1] | UHCI_PTR_DEPTH;
tds[0].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0)
| TD_CTRL_ACTIVE);
tds[0].token = (uhci_explen(cmdsize) | (devaddr << TD_TOKEN_DEVADDR_SHIFT)
| USB_PID_SETUP);
tds[0].buffer = (void*)cmd;
int toggle = TD_TOKEN_TOGGLE;
int i;
for (i=1; i<count-1; i++) {
tds[i].link = (u32)&tds[i+1] | UHCI_PTR_DEPTH;
tds[i].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0)
| TD_CTRL_ACTIVE);
int len = (i == count-2 ? (datasize - (i-1)*maxpacket) : maxpacket);
tds[i].token = (uhci_explen(len) | toggle
| (devaddr << TD_TOKEN_DEVADDR_SHIFT)
| (dir ? USB_PID_IN : USB_PID_OUT));
tds[i].buffer = data + (i-1) * maxpacket;
toggle ^= TD_TOKEN_TOGGLE;
}
tds[i].link = UHCI_PTR_TERM;
tds[i].status = (uhci_maxerr(0) | (lowspeed ? TD_CTRL_LS : 0)
| TD_CTRL_ACTIVE);
tds[i].token = (uhci_explen(0) | TD_TOKEN_TOGGLE
| (devaddr << TD_TOKEN_DEVADDR_SHIFT)
| (dir ? USB_PID_OUT : USB_PID_IN));
tds[i].buffer = 0;
// Transfer data
struct uhci_qh *data_qh = cntl->qh;
data_qh->element = (u32)&tds[0];
int ret = wait_qh(data_qh);
if (ret)
// XXX - leak tds
return ret;
// XXX - free(tds);
return 0;
}
void *
uhci_alloc_intr_pipe(u32 endp, int period)
{
if (! CONFIG_USB_UHCI)
return NULL;
dprintf(7, "uhci_alloc_intr_pipe %x %d\n", endp, period);
struct usb_s *cntl = endp2cntl(endp);
int maxpacket = endp2maxsize(endp);
int lowspeed = endp2speed(endp);
int devaddr = endp2devaddr(endp) | (endp2ep(endp) << 7);
// XXX - just grab 20 for now.
int count = 20;
struct uhci_qh *qh = malloc_low(sizeof(*qh));
struct uhci_td *tds = malloc_low(sizeof(*tds) * count);
if (!qh || !tds)
return NULL;
if (maxpacket > sizeof(tds[0].data))
// XXX - free qh/tds
return NULL;
qh->element = (u32)tds;
int toggle = 0;
int i;
for (i=0; i<count; i++) {
tds[i].link = (i==count-1 ? (u32)&tds[0] : (u32)&tds[i+1]);
tds[i].status = (uhci_maxerr(3) | (lowspeed ? TD_CTRL_LS : 0)
| TD_CTRL_ACTIVE);
tds[i].token = (uhci_explen(maxpacket) | toggle
| (devaddr << TD_TOKEN_DEVADDR_SHIFT)
| USB_PID_IN);
tds[i].buffer = &tds[i].data;
toggle ^= TD_TOKEN_TOGGLE;
}
qh->next_td = &tds[0];
// XXX - need schedule - just add to primary list for now.
struct uhci_qh *data_qh = cntl->qh;
qh->link = data_qh->link;
data_qh->link = (u32)qh | UHCI_PTR_QH;
return qh;
}
int
uhci_poll_intr(void *pipe, void *data)
{
ASSERT16();
if (! CONFIG_USB_UHCI)
return -1;
struct uhci_qh *qh = pipe;
struct uhci_td *td = GET_FLATPTR(qh->next_td);
u32 status = GET_FLATPTR(td->status);
u32 token = GET_FLATPTR(td->token);
if (status & TD_CTRL_ACTIVE)
// No intrs found.
return -1;
// XXX - check for errors.
// Copy data.
memcpy_far(GET_SEG(SS), data
, FLATPTR_TO_SEG(td->data), (void*)FLATPTR_TO_OFFSET(td->data)
, uhci_expected_length(token));
// Reenable this td.
u32 next = GET_FLATPTR(td->link);
SET_FLATPTR(td->status, (uhci_maxerr(0) | (status & TD_CTRL_LS)
| TD_CTRL_ACTIVE));
SET_FLATPTR(qh->next_td, (void*)(next & ~UHCI_PTR_BITS));
return 0;
}

131
src/usb-uhci.h Normal file
View File

@ -0,0 +1,131 @@
#ifndef __USB_UHCI_H
#define __USB_UHCI_H
// usb-uhci.c
struct usb_s;
int uhci_init(struct usb_s *cntl);
int uhci_control(u32 endp, int dir, const void *cmd, int cmdsize
, void *data, int datasize);
void *uhci_alloc_intr_pipe(u32 endp, int period);
int uhci_poll_intr(void *pipe, void *data);
/****************************************************************
* uhci structs and flags
****************************************************************/
/* USB port status and control registers */
#define USBPORTSC1 16
#define USBPORTSC2 18
#define USBPORTSC_CCS 0x0001 /* Current Connect Status
* ("device present") */
#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
#define USBPORTSC_PE 0x0004 /* Port Enable */
#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
#define USBPORTSC_DPLUS 0x0010 /* D+ high (line status) */
#define USBPORTSC_DMINUS 0x0020 /* D- high (line status) */
#define USBPORTSC_RD 0x0040 /* Resume Detect */
#define USBPORTSC_RES1 0x0080 /* reserved, always 1 */
#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
#define USBPORTSC_PR 0x0200 /* Port Reset */
/* Legacy support register */
#define USBLEGSUP 0xc0
#define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */
/* Command register */
#define USBCMD 0
#define USBCMD_RS 0x0001 /* Run/Stop */
#define USBCMD_HCRESET 0x0002 /* Host reset */
#define USBCMD_GRESET 0x0004 /* Global reset */
#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
#define USBCMD_FGR 0x0010 /* Force Global Resume */
#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
/* Status register */
#define USBSTS 2
#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
#define USBSTS_RD 0x0004 /* Resume Detect */
#define USBSTS_HSE 0x0008 /* Host System Error: PCI problems */
#define USBSTS_HCPE 0x0010 /* Host Controller Process Error:
* the schedule is buggy */
#define USBSTS_HCH 0x0020 /* HC Halted */
/* Interrupt enable register */
#define USBINTR 4
#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
#define USBFRNUM 6
#define USBFLBASEADD 8
#define USBSOF 12
#define USBSOF_DEFAULT 64 /* Frame length is exactly 1 ms */
struct uhci_framelist {
u32 links[1024];
} PACKED;
#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */
#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */
#define TD_CTRL_C_ERR_SHIFT 27
#define TD_CTRL_LS (1 << 26) /* Low Speed Device */
#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */
#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */
#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */
#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */
#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */
#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */
#define TD_CTRL_NAK (1 << 19) /* NAK Received */
#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */
#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */
#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */
#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \
TD_CTRL_BABBLE | TD_CTRL_CRCTIME | \
TD_CTRL_BITSTUFF)
#define uhci_maxerr(err) ((err) << TD_CTRL_C_ERR_SHIFT)
#define TD_TOKEN_DEVADDR_SHIFT 8
#define TD_TOKEN_TOGGLE_SHIFT 19
#define TD_TOKEN_TOGGLE (1 << 19)
#define TD_TOKEN_EXPLEN_SHIFT 21
#define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n-1 */
#define TD_TOKEN_PID_MASK 0xFF
#define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \
TD_TOKEN_EXPLEN_SHIFT)
#define uhci_expected_length(token) ((((token) >> TD_TOKEN_EXPLEN_SHIFT) + \
1) & TD_TOKEN_EXPLEN_MASK)
struct uhci_td {
u32 link;
u32 status;
u32 token;
void *buffer;
// Software fields
u32 data[4];
} PACKED;
struct uhci_qh {
u32 link;
u32 element;
// Software fields
struct uhci_td *next_td;
u32 reserved;
} PACKED;
#define UHCI_PTR_BITS 0x000F
#define UHCI_PTR_TERM 0x0001
#define UHCI_PTR_QH 0x0002
#define UHCI_PTR_DEPTH 0x0004
#define UHCI_PTR_BREADTH 0x0000
#endif // usb-uhci.h

206
src/usb.c Normal file
View File

@ -0,0 +1,206 @@
// Main code for handling USB controllers and devices.
//
// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "util.h" // dprintf
#include "pci.h" // foreachpci
#include "config.h" // CONFIG_*
#include "pci_regs.h" // PCI_CLASS_REVISION
#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
#include "usb-uhci.h" // uhci_init
#include "usb-hid.h" // usb_keyboard_setup
#include "usb.h" // struct usb_s
struct usb_s USBControllers[16] VAR16VISIBLE;
static int
send_control(u32 endp, int dir, const void *cmd, int cmdsize
, void *data, int datasize)
{
return uhci_control(endp, dir, cmd, cmdsize, data, datasize);
}
void *
alloc_intr_pipe(u32 endp, int period)
{
return uhci_alloc_intr_pipe(endp, period);
}
int
usb_poll_intr(void *pipe, void *data)
{
return uhci_poll_intr(pipe, data);
}
int
send_default_control(u32 endp, const struct usb_ctrlrequest *req, void *data)
{
return send_control(endp, req->bRequestType & USB_DIR_IN
, req, sizeof(*req), data, req->wLength);
}
// Get the first 8 bytes of the device descriptor.
static int
get_device_info8(struct usb_device_descriptor *dinfo, u32 endp)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
req.bRequest = USB_REQ_GET_DESCRIPTOR;
req.wValue = USB_DT_DEVICE<<8;
req.wIndex = 0;
req.wLength = 8;
return send_default_control(endp, &req, dinfo);
}
static struct usb_config_descriptor *
get_device_config(u32 endp)
{
struct usb_config_descriptor cfg;
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
req.bRequest = USB_REQ_GET_DESCRIPTOR;
req.wValue = USB_DT_CONFIG<<8;
req.wIndex = 0;
req.wLength = sizeof(cfg);
int ret = send_default_control(endp, &req, &cfg);
if (ret)
return NULL;
void *config = malloc_tmphigh(cfg.wTotalLength);
if (!config)
return NULL;
req.wLength = cfg.wTotalLength;
ret = send_default_control(endp, &req, config);
if (ret)
return NULL;
//hexdump(config, cfg.wTotalLength);
return config;
}
static u32
set_address(u32 endp)
{
dprintf(3, "set_address %x\n", endp);
struct usb_s *cntl = endp2cntl(endp);
if (cntl->maxaddr >= USB_MAXADDR)
return 0;
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
req.bRequest = USB_REQ_SET_ADDRESS;
req.wValue = cntl->maxaddr + 1;
req.wIndex = 0;
req.wLength = 0;
int ret = send_default_control(endp, &req, NULL);
if (ret)
return 0;
mdelay(2);
cntl->maxaddr++;
return mkendp(cntl, cntl->maxaddr, 0, endp2speed(endp), endp2maxsize(endp));
}
static int
set_configuration(u32 endp, u16 val)
{
struct usb_ctrlrequest req;
req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
req.bRequest = USB_REQ_SET_CONFIGURATION;
req.wValue = val;
req.wIndex = 0;
req.wLength = 0;
return send_default_control(endp, &req, NULL);
}
// Called for every found device - see if a driver is available for
// this device and do setup if so.
int
configure_usb_device(struct usb_s *cntl, int lowspeed)
{
dprintf(1, "config_usb: %p %d\n", cntl, lowspeed);
// Get device info
u32 endp = mkendp(cntl, 0, 0, lowspeed, 8);
struct usb_device_descriptor dinfo;
int ret = get_device_info8(&dinfo, endp);
if (ret)
return 0;
dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%02x\n"
, dinfo.bcdUSB, dinfo.bDeviceClass, dinfo.bDeviceSubClass
, dinfo.bDeviceProtocol, dinfo.bMaxPacketSize0);
if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64)
return 0;
endp = mkendp(cntl, 0, 0, lowspeed, dinfo.bMaxPacketSize0);
// Get configuration
struct usb_config_descriptor *config = get_device_config(endp);
if (!config)
return 0;
// Determine if a driver exists for this device - only look at the
// first interface of the first configuration.
struct usb_interface_descriptor *iface = (void*)(&config[1]);
if (iface->bInterfaceClass != USB_CLASS_HID
|| iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT
|| iface->bInterfaceProtocol != USB_INTERFACE_PROTOCOL_KEYBOARD)
// Not a "boot" keyboard
goto fail;
// Set the address and configure device.
endp = set_address(endp);
if (!endp)
goto fail;
ret = set_configuration(endp, config->bConfigurationValue);
if (ret)
goto fail;
// Configure driver.
ret = usb_keyboard_init(endp, iface, ((void*)config + config->wTotalLength
- (void*)iface));
if (ret)
goto fail;
// XXX - free(config);
return 1;
fail:
// XXX - free(config);
return 0;
}
void
usb_setup()
{
if (! CONFIG_USB)
return;
dprintf(3, "init usb\n");
usb_keyboard_setup();
// Look for USB controllers
int count = 0;
int bdf, max;
foreachpci(bdf, max) {
u32 code = pci_config_readl(bdf, PCI_CLASS_REVISION) >> 8;
if (code >> 8 != PCI_CLASS_SERIAL_USB)
continue;
struct usb_s *cntl = &USBControllers[count];
cntl->bdf = bdf;
int devcount = 0;
if (code == PCI_CLASS_SERIAL_USB_UHCI)
devcount = uhci_init(cntl);
if (devcount > 0) {
// Success
count++;
if (count >= ARRAY_SIZE(USBControllers))
break;
}
}
}

181
src/usb.h Normal file
View File

@ -0,0 +1,181 @@
// USB functions and data.
#ifndef __USB_H
#define __USB_H
// Local information for a usb controller.
struct usb_s {
u16 bdf;
u16 iobase;
u8 maxaddr;
void *qh;
};
extern struct usb_s USBControllers[];
#define USB_MAXADDR 127
// usb.c
void usb_setup();
int configure_usb_device(struct usb_s *cntl, int lowspeed);
struct usb_ctrlrequest;
int send_default_control(u32 endp, const struct usb_ctrlrequest *req
, void *data);
void *alloc_intr_pipe(u32 endp, int period);
int usb_poll_intr(void *pipe, void *data);
/****************************************************************
* endpoint definition
****************************************************************/
static inline u32
mkendp(struct usb_s *cntl, u8 devaddr, u8 ep, u8 lowspeed, u8 maxsize)
{
u8 bus = cntl-USBControllers;
u8 size = __ffs(maxsize);
return (size<<25) | (lowspeed<<24) | (bus<<16) | (devaddr<<8) | ep;
}
static inline u8 endp2ep(u32 endp) {
return endp;
}
static inline u8 endp2devaddr(u32 endp) {
return endp>>8;
}
static inline struct usb_s *endp2cntl(u32 endp) {
u8 bus = endp>>16;
return &USBControllers[bus];
}
static inline u8 endp2speed(u32 endp) {
return (endp>>24) & 1;
}
static inline u8 endp2maxsize(u32 endp) {
return 1 << (endp>>25);
}
/****************************************************************
* usb structs and flags
****************************************************************/
#define USB_PID_OUT 0xe1
#define USB_PID_IN 0x69
#define USB_PID_SETUP 0x2d
#define USB_DIR_OUT 0 /* to device */
#define USB_DIR_IN 0x80 /* to host */
#define USB_TYPE_MASK (0x03 << 5)
#define USB_TYPE_STANDARD (0x00 << 5)
#define USB_TYPE_CLASS (0x01 << 5)
#define USB_TYPE_VENDOR (0x02 << 5)
#define USB_TYPE_RESERVED (0x03 << 5)
#define USB_RECIP_MASK 0x1f
#define USB_RECIP_DEVICE 0x00
#define USB_RECIP_INTERFACE 0x01
#define USB_RECIP_ENDPOINT 0x02
#define USB_RECIP_OTHER 0x03
#define USB_REQ_GET_STATUS 0x00
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_DESCRIPTOR 0x07
#define USB_REQ_GET_CONFIGURATION 0x08
#define USB_REQ_SET_CONFIGURATION 0x09
#define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
struct usb_ctrlrequest {
u8 bRequestType;
u8 bRequest;
u16 wValue;
u16 wIndex;
u16 wLength;
} PACKED;
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
#define USB_DT_DEVICE_QUALIFIER 0x06
#define USB_DT_OTHER_SPEED_CONFIG 0x07
struct usb_device_descriptor {
u8 bLength;
u8 bDescriptorType;
u16 bcdUSB;
u8 bDeviceClass;
u8 bDeviceSubClass;
u8 bDeviceProtocol;
u8 bMaxPacketSize0;
u16 idVendor;
u16 idProduct;
u16 bcdDevice;
u8 iManufacturer;
u8 iProduct;
u8 iSerialNumber;
u8 bNumConfigurations;
} PACKED;
#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
#define USB_CLASS_AUDIO 1
#define USB_CLASS_COMM 2
#define USB_CLASS_HID 3
#define USB_CLASS_PHYSICAL 5
#define USB_CLASS_STILL_IMAGE 6
#define USB_CLASS_PRINTER 7
#define USB_CLASS_MASS_STORAGE 8
#define USB_CLASS_HUB 9
struct usb_config_descriptor {
u8 bLength;
u8 bDescriptorType;
u16 wTotalLength;
u8 bNumInterfaces;
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
} PACKED;
struct usb_interface_descriptor {
u8 bLength;
u8 bDescriptorType;
u8 bInterfaceNumber;
u8 bAlternateSetting;
u8 bNumEndpoints;
u8 bInterfaceClass;
u8 bInterfaceSubClass;
u8 bInterfaceProtocol;
u8 iInterface;
} PACKED;
struct usb_endpoint_descriptor {
u8 bLength;
u8 bDescriptorType;
u8 bEndpointAddress;
u8 bmAttributes;
u16 wMaxPacketSize;
u8 bInterval;
} PACKED;
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
#define USB_ENDPOINT_DIR_MASK 0x80
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
#endif // usb.h

View File

@ -173,11 +173,12 @@ void __debug_isr(const char *fname);
} while (0)
#define debug_stub(regs) \
__debug_stub((regs), __LINE__, __func__)
void hexdump(void *d, int len);
void hexdump(const void *d, int len);
// kbd.c
void kbd_setup();
void handle_15c2(struct bregs *regs);
void process_key(u8 key);
// mouse.c
void mouse_setup();
@ -277,6 +278,9 @@ static inline void *malloc_high(u32 size) {
static inline void *malloc_fseg(u32 size) {
return zone_malloc(&ZoneFSeg, size, MALLOC_MIN_ALIGN);
}
static inline void *malloc_tmphigh(u32 size) {
return zone_malloc(&ZoneTmpHigh, size, MALLOC_MIN_ALIGN);
}
static inline void *memalign_tmphigh(u32 align, u32 size) {
return zone_malloc(&ZoneTmpHigh, size, align);
}