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.
# 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 \
$(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
#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
SET_BDA(timer_counter, counter);
+ usb_check_key();
+
// chain to user timer tick INT #0x1c
u32 eax=0, flags;
call16_simpint(0x1c, &eax, &flags);
#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)
SET_BDA(kbd_flag2, flags2);
}
-static void
+void
process_key(u8 key)
{
if (CONFIG_KBD_CALL_INT15_4F) {
}
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);
}
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);
+}
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) \
#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)
pnp_setup();
vga_setup();
+ usb_setup();
kbd_setup();
lpt_setup();
serial_setup();
--- /dev/null
+// 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);
+ }
+}
--- /dev/null
+#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
--- /dev/null
+// 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;
+}
--- /dev/null
+#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
--- /dev/null
+// 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;
+ }
+ }
+}
--- /dev/null
+// 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
} 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();
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);
}