Initial support for USB, UHCI, and USB Keyboards.
authorKevin O'Connor <kevin@koconnor.net>
Tue, 29 Sep 2009 01:32:08 +0000 (21:32 -0400)
committerKevin O'Connor <kevin@koconnor.net>
Tue, 29 Sep 2009 01:32:08 +0000 (21:32 -0400)
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.

15 files changed:
Makefile
src/clock.c
src/config.h
src/kbd.c
src/output.c
src/pci.c
src/pci.h
src/post.c
src/usb-hid.c [new file with mode: 0644]
src/usb-hid.h [new file with mode: 0644]
src/usb-uhci.c [new file with mode: 0644]
src/usb-uhci.h [new file with mode: 0644]
src/usb.c [new file with mode: 0644]
src/usb.h [new file with mode: 0644]
src/util.h

index a933f607e6236cf7c89a07bc2c4a70606e4f92b0..c4016e87f36a68131752e6abecdb2c5e1135de4e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -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
index dd81f9d31163a869c43809da0556766c3924472c..aaa24885b19a36fc546afe166ea3a5855036c108 100644 (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);
index df10bbe4a86d3a27181362741c0e54f71a6837c6..e93b08065ad3b002a0185f60c11126f917c820a6 100644 (file)
 #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)
index 650f1d4e4fdd2a063e83ff2a187cb990b779cf39..6b149409f0ca58a83ee2ef10c282495b53d5dc7f 100644 (file)
--- a/src/kbd.c
+++ b/src/kbd.c
@@ -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) {
index da585b4f35b18f9266cb17ea93db4bf4831b0d15..28cb024bf572e0037dbbb84c1161055d9b84b696 100644 (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);
index bf0e549b9f501e610bed5020870457d1b2cc17bb..e6b74d5347ba4e4f3e692cddf169667e82ffb770 100644 (file)
--- a/src/pci.c
+++ b/src/pci.c
@@ -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);
+}
index 89265840726ef2591261d7432af1707452bceeba..166b775e85e3cc7d2227fce78351dc54ef66ebed 100644 (file)
--- a/src/pci.h
+++ b/src/pci.h
@@ -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)                    \
index 45a319df2f9e074b4f51c449ff6c1061dc040a3b..f72e134aec4b275f4dbac9961de82dcfd7b15955 100644 (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();
diff --git a/src/usb-hid.c b/src/usb-hid.c
new file mode 100644 (file)
index 0000000..7e5b309
--- /dev/null
@@ -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);
+    }
+}
diff --git a/src/usb-hid.h b/src/usb-hid.h
new file mode 100644 (file)
index 0000000..26ca91a
--- /dev/null
@@ -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
diff --git a/src/usb-uhci.c b/src/usb-uhci.c
new file mode 100644 (file)
index 0000000..814e3ec
--- /dev/null
@@ -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;
+}
diff --git a/src/usb-uhci.h b/src/usb-uhci.h
new file mode 100644 (file)
index 0000000..6ad7e15
--- /dev/null
@@ -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
diff --git a/src/usb.c b/src/usb.c
new file mode 100644 (file)
index 0000000..8e14b8e
--- /dev/null
+++ b/src/usb.c
@@ -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;
+        }
+    }
+}
diff --git a/src/usb.h b/src/usb.h
new file mode 100644 (file)
index 0000000..e8d7455
--- /dev/null
+++ b/src/usb.h
@@ -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
index a74bdec13365d370965d0de0ff0952d282af1822..fbcfb79b8a414fa7a0d518f5c754105c8bcd6e09 100644 (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);
 }