diff options
author | Chanho Park <chanho61.park@samsung.com> | 2014-12-10 15:42:55 +0900 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-12-10 15:42:55 +0900 |
commit | 0d6a2f7e595218b5632ba7005128470e65138951 (patch) | |
tree | 596b09930ef1538e6606450e2d8b88ec2e296a9b /roms | |
parent | 16b1353a36171ae06d63fd309f4772dbfb1da113 (diff) | |
download | qemu-0d6a2f7e595218b5632ba7005128470e65138951.tar.gz qemu-0d6a2f7e595218b5632ba7005128470e65138951.tar.bz2 qemu-0d6a2f7e595218b5632ba7005128470e65138951.zip |
Imported Upstream version 2.2.0upstream/2.2.1upstream/2.2.0
Diffstat (limited to 'roms')
33 files changed, 3399 insertions, 224 deletions
diff --git a/roms/openbios/arch/ppc/qemu/methods.c b/roms/openbios/arch/ppc/qemu/methods.c index 8219cff90..fd993daa9 100644 --- a/roms/openbios/arch/ppc/qemu/methods.c +++ b/roms/openbios/arch/ppc/qemu/methods.c @@ -25,6 +25,7 @@ #include "qemu/qemu.h" #include "libopenbios/ofmem.h" #include "arch/ppc/processor.h" +#include "drivers/usb.h" /************************************************************************/ /* RTAS (run-time abstraction services) */ @@ -112,6 +113,7 @@ DECLARE_NODE( ciface, 0, 0, "+/openprom/client-services" ); static void ciface_quiesce( unsigned long args[], unsigned long ret[] ) { + usb_exit(); #if 0 unsigned long msr; /* This seems to be the correct thing to do - but I'm not sure */ diff --git a/roms/openbios/arch/ppc/qemu/tree.fs b/roms/openbios/arch/ppc/qemu/tree.fs index 1b9c2934f..1ed838397 100644 --- a/roms/openbios/arch/ppc/qemu/tree.fs +++ b/roms/openbios/arch/ppc/qemu/tree.fs @@ -12,7 +12,8 @@ include config.fs \ ------------------------------------------------------------- " /" find-device - +\ Apple calls the root node device-tree +" device-tree" device-name [IFDEF] CONFIG_PPC64 2 [ELSE] 1 [THEN] encode-int " #address-cells" property 1 encode-int " #size-cells" property h# 05f5e100 encode-int " clock-frequency" property diff --git a/roms/openbios/arch/sparc64/openbios.c b/roms/openbios/arch/sparc64/openbios.c index 9cc1ac223..3a3614652 100644 --- a/roms/openbios/arch/sparc64/openbios.c +++ b/roms/openbios/arch/sparc64/openbios.c @@ -116,6 +116,20 @@ sparc64_reset_all(void) : : "r" (val), "r" (addr) : "memory"); } +/* PCI Target Address Space Register (see UltraSPARC IIi User's Manual + section 19.3.0.4) */ +#define PBM_PCI_TARGET_AS 0x2028 +#define PBM_PCI_TARGET_AS_CD_ENABLE 0x40 + +static void +sparc64_set_tas_register(unsigned long val) +{ + unsigned long addr = APB_SPECIAL_BASE + PBM_PCI_TARGET_AS; + + asm("stxa %0, [%1] 0x15\n\t" + : : "r" (val), "r" (addr) : "memory"); +} + static void cpu_generic_init(const struct cpudef *cpu, uint32_t clock_frequency) { unsigned long iu_version; @@ -574,6 +588,10 @@ arch_init( void ) modules_init(); #ifdef CONFIG_DRIVER_PCI ob_pci_init(); + + /* Set TAS register to match the virtual-dma properties + set during sabre configure */ + sparc64_set_tas_register(PBM_PCI_TARGET_AS_CD_ENABLE); #endif nvconf_init(); device_end(); diff --git a/roms/openbios/config/examples/ppc_config.xml b/roms/openbios/config/examples/ppc_config.xml index 621b65d9c..4c14eb6c8 100644 --- a/roms/openbios/config/examples/ppc_config.xml +++ b/roms/openbios/config/examples/ppc_config.xml @@ -80,3 +80,6 @@ <option name="CONFIG_DRIVER_ESCC" type="boolean" value="true"/> <option name="CONFIG_DRIVER_FW_CFG" type="boolean" value="true"/> <option name="CONFIG_FW_CFG_ADDR" type="integer" value="0xf0000510"/> + <option name="CONFIG_DRIVER_USB" type="boolean" value="true"/> + <option name="CONFIG_DEBUG_USB" type="boolean" value="false"/> + <option name="CONFIG_USB_HID" type="boolean" value="true"/> diff --git a/roms/openbios/drivers/Kconfig b/roms/openbios/drivers/Kconfig index 7be334b1c..3bebc0293 100644 --- a/roms/openbios/drivers/Kconfig +++ b/roms/openbios/drivers/Kconfig @@ -36,4 +36,24 @@ config DEBUG_IDE help Debug IDE driver +config DRIVER_USB + bool "USB Support" + default n + help + If you want to be able to use USB devices, enable this option. + +config DEBUG_USB + depends DRIVER_USB + bool "Debug USB driver" + default n + help + Debug USB driver + +config USB_HID + depends DRIVER_USB + bool "USB driver for HID devices" + default n + help + If you want to be able to use USB keyboard, enable this option. + endmenu diff --git a/roms/openbios/drivers/build.xml b/roms/openbios/drivers/build.xml index edec6b512..bd1abd358 100644 --- a/roms/openbios/drivers/build.xml +++ b/roms/openbios/drivers/build.xml @@ -22,6 +22,10 @@ <object source="pc_serial.c" condition="DRIVER_PC_SERIAL"/> <object source="escc.c" condition="DRIVER_ESCC"/> <object source="fw_cfg.c" condition="DRIVER_FW_CFG"/> + <object source="usb.c" condition="DRIVER_USB"/> + <object source="usbhid.c" condition="USB_HID"/> + <object source="usbohci.c" condition="DRIVER_USB"/> + <object source="usbohci_rh.c" condition="DRIVER_USB"/> </library> <dictionary name="openbios" target="forth"> diff --git a/roms/openbios/drivers/ide.c b/roms/openbios/drivers/ide.c index 0e0f0cf24..327c64a40 100644 --- a/roms/openbios/drivers/ide.c +++ b/roms/openbios/drivers/ide.c @@ -1526,6 +1526,10 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) u32 props[8]; struct ide_channel *chan; + /* IDE ports on Macs are numbered from 3. + * Also see comments in macio.c:openpic_init() */ + current_channel = 3; + for (i = 0; i < nb_channels; i++, current_channel++) { chan = malloc(sizeof(struct ide_channel)); @@ -1574,7 +1578,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) dnode = find_dev(nodebuff); - set_property(dnode, "compatible", "heathrow-ata", 13); + set_property(dnode, "compatible", (is_oldworld() ? + "heathrow-ata" : "keylargo-ata"), 13); props[0] = 0x00000526; props[1] = 0x00000085; diff --git a/roms/openbios/drivers/macio.c b/roms/openbios/drivers/macio.c index 00d714990..f54bc86dc 100644 --- a/roms/openbios/drivers/macio.c +++ b/roms/openbios/drivers/macio.c @@ -27,20 +27,31 @@ #define NW_IO_NVRAM_SIZE 0x00004000 #define NW_IO_NVRAM_OFFSET 0xfff04000 -#define NW_IO_NVRAM_SHIFT 1 #define IO_OPENPIC_SIZE 0x00040000 #define IO_OPENPIC_OFFSET 0x00040000 static char *nvram; +static int macio_nvram_shift(void) +{ + int nvram_flat; + + if (is_oldworld()) + return OW_IO_NVRAM_SHIFT; + + nvram_flat = fw_cfg_read_i32(FW_CFG_PPC_NVRAM_FLAT); + return nvram_flat ? 0 : 1; +} + int macio_get_nvram_size(void) { + int shift = macio_nvram_shift(); if (is_oldworld()) - return OW_IO_NVRAM_SIZE >> OW_IO_NVRAM_SHIFT; + return OW_IO_NVRAM_SIZE >> shift; else - return NW_IO_NVRAM_SIZE >> NW_IO_NVRAM_SHIFT; + return NW_IO_NVRAM_SIZE >> shift; } static unsigned long macio_nvram_offset(void) @@ -123,14 +134,9 @@ void macio_nvram_put(char *buf) { int i; - unsigned int it_shift; + unsigned int it_shift = macio_nvram_shift(); - if (is_oldworld()) - it_shift = OW_IO_NVRAM_SHIFT; - else - it_shift = NW_IO_NVRAM_SHIFT; - - for (i=0; i< arch_nvram_size() ; i++) + for (i=0; i < arch_nvram_size(); i++) nvram[i << it_shift] = buf[i]; #ifdef DUMP_NVRAM printk("new nvram:\n"); @@ -142,12 +148,7 @@ void macio_nvram_get(char *buf) { int i; - unsigned int it_shift; - - if (is_oldworld()) - it_shift = OW_IO_NVRAM_SHIFT; - else - it_shift = NW_IO_NVRAM_SHIFT; + unsigned int it_shift = macio_nvram_shift(); for (i=0; i< arch_nvram_size(); i++) buf[i] = nvram[i << it_shift]; @@ -161,7 +162,6 @@ macio_nvram_get(char *buf) static void openpic_init(const char *path, phys_addr_t addr) { - phandle_t target_node; phandle_t dnode; int props[2]; char buf[128]; @@ -186,42 +186,6 @@ openpic_init(const char *path, phys_addr_t addr) set_int_property(dnode, "clock-frequency", 4166666); fword("finish-device"); - - u32 *interrupt_map; - int len, i; - - /* patch in interrupt parent */ - dnode = find_dev(buf); - - target_node = find_dev("/pci/mac-io"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/escc/ch-a"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/escc/ch-b"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/ata-1"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/ata-2"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/ata-3"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci/mac-io/via-cuda"); - set_int_property(target_node, "interrupt-parent", dnode); - - target_node = find_dev("/pci"); - set_int_property(target_node, "interrupt-parent", dnode); - - interrupt_map = (u32 *)get_property(target_node, "interrupt-map", &len); - for (i = 0; i < 8; i++) { - interrupt_map[(i * 7) + PCI_INT_MAP_PIC_HANDLE] = (u32)dnode; - } - set_property(target_node, "interrupt-map", (char *)interrupt_map, len); } DECLARE_NODE(ob_macio, INSTALL_OPEN, sizeof(int), "Tmac-io"); @@ -311,7 +275,7 @@ ob_macio_keylargo_init(const char *path, phys_addr_t addr) /* The NewWorld NVRAM is not located in the MacIO device */ macio_nvram_init("", 0); escc_init(path, addr); - macio_ide_init(path, addr, 3); + macio_ide_init(path, addr, 2); openpic_init(path, addr); ob_unin_init(); } diff --git a/roms/openbios/drivers/pc_serial.c b/roms/openbios/drivers/pc_serial.c index f63fcaa47..a638e1f99 100644 --- a/roms/openbios/drivers/pc_serial.c +++ b/roms/openbios/drivers/pc_serial.c @@ -142,22 +142,19 @@ pc_serial_close(void) static void pc_serial_open(unsigned long *address) { - int len; - phandle_t ph; - unsigned long *prop; - - fword("my-self"); - fword("ihandle>phandle"); - ph = (phandle_t)POP(); - prop = (unsigned long *)get_property(ph, "address", &len); - *address = *prop; - RET ( -1 ); } +static void +pc_serial_init(unsigned long *address) +{ + *address = POP(); +} + DECLARE_UNNAMED_NODE(pc_serial, INSTALL_OPEN, sizeof(unsigned long)); NODE_METHODS(pc_serial) = { + { "init", pc_serial_init }, { "open", pc_serial_open }, { "close", pc_serial_close }, { "read", pc_serial_read }, @@ -177,6 +174,10 @@ ob_pc_serial_init(const char *path, const char *dev_name, uint64_t base, push_str(nodebuff); fword("find-device"); + PUSH(offset); + PUSH(find_package_method("init", get_cur_dev())); + fword("execute"); + push_str("serial"); fword("device-type"); @@ -191,10 +192,12 @@ ob_pc_serial_init(const char *path, const char *dev_name, uint64_t base, push_str("reg"); fword("property"); +#if !defined(CONFIG_SPARC64) PUSH(offset); fword("encode-int"); push_str("address"); fword("property"); +#endif #if defined(CONFIG_SPARC64) set_int_property(get_cur_dev(), "interrupts", 1); diff --git a/roms/openbios/drivers/pci.c b/roms/openbios/drivers/pci.c index ca92d63bf..3260354be 100644 --- a/roms/openbios/drivers/pci.c +++ b/roms/openbios/drivers/pci.c @@ -34,6 +34,9 @@ #include "cuda.h" #include "macio.h" #endif +#ifdef CONFIG_DRIVER_USB +#include "drivers/usb.h" +#endif #if defined (CONFIG_DEBUG_PCI) # define PCI_DPRINTF(format, ...) printk(format, ## __VA_ARGS__) @@ -414,62 +417,6 @@ static void pci_set_bus_range(const pci_config_t *config) set_property(dev, "bus-range", (char *)props, 2 * sizeof(props[0])); } -static void pci_host_set_interrupt_map(const pci_config_t *config) -{ -/* XXX We currently have a hook in the MPIC init code to fill in its handle. - * If you want to have interrupt maps for your PCI host bus, add your - * architecture to the #if and make your bridge detect code fill in its - * handle too. - * - * It would be great if someone clever could come up with a more universal - * mechanism here. - */ -#if defined(CONFIG_PPC) - phandle_t dev = get_cur_dev(); - u32 props[7 * 8]; - int i; - -#if defined(CONFIG_PPC) - /* Oldworld macs do interrupt maps differently */ - if(!is_newworld()) - return; -#endif - - for (i = 0; i < (7*8); i+=7) { - props[i+PCI_INT_MAP_PCI0] = 0; - props[i+PCI_INT_MAP_PCI1] = 0; - props[i+PCI_INT_MAP_PCI2] = 0; - props[i+PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1 - props[i+PCI_INT_MAP_PIC_HANDLE] = 0; // gets patched in later - props[i+PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7]; - props[i+PCI_INT_MAP_PIC_POL] = 3; - } - set_property(dev, "interrupt-map", (char *)props, 7 * 8 * sizeof(props[0])); - - props[PCI_INT_MAP_PCI0] = 0; - props[PCI_INT_MAP_PCI1] = 0; - props[PCI_INT_MAP_PCI2] = 0; - props[PCI_INT_MAP_PCI_INT] = 0x7; - - set_property(dev, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); -#elif defined(CONFIG_SPARC64) - phandle_t dev = get_cur_dev(); - uint32_t props[5]; - - props[0] = 0x000001fe; - props[1] = 0x020003f8; - props[2] = 1; - props[3] = find_dev("/"); - props[4] = 0x2b; - set_property(dev, "interrupt-map", (char *)props, 5 * sizeof(props[0])); - - props[0] = 0x000001ff; - props[1] = 0xffffffff; - props[2] = 3; - set_property(dev, "interrupt-map-mask", (char *)props, 3 * sizeof(props[0])); -#endif -} - static void pci_host_set_reg(phandle_t phandle) { phandle_t dev = phandle; @@ -536,7 +483,6 @@ int host_config_cb(const pci_config_t *config) //XXX this overrides "reg" property pci_host_set_reg(get_cur_dev()); pci_host_set_ranges(config); - pci_host_set_interrupt_map(config); return 0; } @@ -553,6 +499,9 @@ static int sabre_configure(phandle_t dev) sizeof(props[0])); set_property(dev, "#virtual-dma-addr-cells", (char *)props, sizeof(props[0])); + + set_property(dev, "no-streaming-cache", (char *)props, 0); + props[0] = 0x000007f0; props[1] = 0x000007ee; props[2] = 0x000007ef; @@ -841,10 +790,14 @@ int ebus_config_cb(const pci_config_t *config) { #ifdef CONFIG_DRIVER_EBUS phandle_t dev = get_cur_dev(); - uint32_t props[5]; + uint32_t props[12]; + int ncells; + int i; + uint32_t mask; + int flags, space_code; - props[0] = 0x000001fe; - props[1] = 0x020003f8; + props[0] = 0x14; + props[1] = 0x3f8; props[2] = 1; props[3] = find_dev("/"); props[4] = 0x2b; @@ -855,14 +808,37 @@ int ebus_config_cb(const pci_config_t *config) props[2] = 3; set_property(dev, "interrupt-map-mask", (char *)props, 3 * sizeof(props[0])); + /* Build ranges property from the BARs */ + ncells = 0; + for (i = 0; i < 6; i++) { + /* consider only bars with non-zero region size */ + if (!config->sizes[i]) + continue; + + pci_decode_pci_addr(config->assigned[i], + &flags, &space_code, &mask); + + props[ncells++] = PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)); + props[ncells++] = 0x0; + + ncells += pci_encode_phys_addr(props + ncells, + flags, space_code, config->dev, + PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)), + 0); + + props[ncells++] = config->sizes[i]; + } + + set_property(dev, "ranges", (char *)props, ncells * sizeof(props[0])); + #ifdef CONFIG_DRIVER_FLOPPY ob_floppy_init(config->path, "fdthree", 0x3f0ULL, 0); #endif #ifdef CONFIG_DRIVER_PC_SERIAL - ob_pc_serial_init(config->path, "su", arch->io_base, 0x3f8ULL, 0); + ob_pc_serial_init(config->path, "su", (PCI_BASE_ADDR_1 | 0ULL) << 32, 0x3f8ULL, 0); #endif #ifdef CONFIG_DRIVER_PC_KBD - ob_pc_kbd_init(config->path, "kb_ps2", arch->io_base, 0x60ULL, 0); + ob_pc_kbd_init(config->path, "kb_ps2", (PCI_BASE_ADDR_1 | 0ULL) << 32, 0x60ULL, 0); #endif #endif return 0; @@ -883,6 +859,14 @@ int i82378_config_cb(const pci_config_t *config) return 0; } +int usb_ohci_config_cb(const pci_config_t *config) +{ +#ifdef CONFIG_DRIVER_USB + ob_usb_ohci_init(config->path, 0x80000000 | config->dev); +#endif + return 0; +} + static void ob_pci_add_properties(phandle_t phandle, pci_addr addr, const pci_dev_t *pci_dev, const pci_config_t *config, int num_bars) @@ -933,10 +917,7 @@ static void ob_pci_add_properties(phandle_t phandle, OLDWORLD(set_int_property(dev, "AAPL,interrupts", config->irq_line)); #if defined(CONFIG_SPARC64) - /* direct mapping bssnn (Bus, Slot, interrupt Number */ - set_int_property(get_cur_dev(), "interrupts", - ((((config->dev >> 11) << 2) - + config->irq_pin - 1) & 0x1f)); + set_int_property(dev, "interrupts", config->irq_pin); #else NEWWORLD(set_int_property(dev, "interrupts", config->irq_pin)); #endif @@ -1375,6 +1356,132 @@ static void ob_configure_pci_device(const char* parent_path, } } +static void ob_pci_set_available(phandle_t host, unsigned long mem_base, unsigned long io_base) +{ + /* Create an available property for both memory and IO space */ + uint32_t props[10]; + int ncells; + + ncells = 0; + ncells += pci_encode_phys_addr(props + ncells, 0, MEMORY_SPACE_32, 0, 0, mem_base); + ncells += pci_encode_size(props + ncells, arch->mem_len - mem_base); + ncells += pci_encode_phys_addr(props + ncells, 0, IO_SPACE, 0, 0, io_base); + ncells += pci_encode_size(props + ncells, arch->io_len - io_base); + + set_property(host, "available", (char *)props, ncells * sizeof(props[0])); +} + +/* Convert device/irq pin to interrupt property */ +#define SUN4U_INTERRUPT(dev, irq_pin) \ + ((((dev >> 11) << 2) + irq_pin - 1) & 0x1f) + +static void ob_pci_host_set_interrupt_map(phandle_t host) +{ + phandle_t dnode = 0; + u32 props[128]; + int i; + +#if defined(CONFIG_PPC) + phandle_t target_node; + + /* Oldworld macs do interrupt maps differently */ + if (!is_newworld()) + return; + + dnode = dt_iterate_type(0, "open-pic"); + if (dnode) { + /* patch in openpic interrupt-parent properties */ + target_node = find_dev("/pci/mac-io"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc/ch-a"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc/ch-b"); + set_int_property(target_node, "interrupt-parent", dnode); + + /* QEMU only emulates 2 of the 3 ata buses currently */ + /* On a new world Mac these are not numbered but named by the + * ATA version they support. Thus we have: ata-3, ata-3, ata-4 + * On g3beige they all called just ide. + * We take ata-3 and ata-4 which seems to work for both + * at least for clients we care about */ + target_node = find_dev("/pci/mac-io/ata-3"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/ata-4"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/via-cuda"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci"); + set_int_property(target_node, "interrupt-parent", dnode); + + /* openpic interrupt mapping */ + for (i = 0; i < (7*8); i += 7) { + props[i + PCI_INT_MAP_PCI0] = 0; + props[i + PCI_INT_MAP_PCI1] = 0; + props[i + PCI_INT_MAP_PCI2] = 0; + props[i + PCI_INT_MAP_PCI_INT] = (i / 7) + 1; // starts at PINA=1 + props[i + PCI_INT_MAP_PIC_HANDLE] = dnode; + props[i + PCI_INT_MAP_PIC_INT] = arch->irqs[i / 7]; + props[i + PCI_INT_MAP_PIC_POL] = 3; + } + set_property(host, "interrupt-map", (char *)props, 7 * 8 * sizeof(props[0])); + + props[PCI_INT_MAP_PCI0] = 0; + props[PCI_INT_MAP_PCI1] = 0; + props[PCI_INT_MAP_PCI2] = 0; + props[PCI_INT_MAP_PCI_INT] = 0x7; + + set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); + } +#elif defined(CONFIG_SPARC64) + int ncells, len; + u32 *val, addr; + char *reg; + + /* Set interrupt-map for PCI devices with an interrupt pin present */ + ncells = 0; + + PUSH(host); + fword("child"); + dnode = POP(); + while (dnode) { + if (get_int_property(dnode, "interrupts", &len)) { + reg = get_property(dnode, "reg", &len); + if (reg) { + val = (u32 *)reg; + + for (i = 0; i < (len / sizeof(u32)); i += 5) { + addr = val[i]; + + /* Device address is in 1st 32-bit word of encoded PCI address for config space */ + if (!(addr & 0x03000000)) { + ncells += pci_encode_phys_addr(props + ncells, 0, 0, addr, 0, 0); + props[ncells++] = 1; /* always interrupt pin 1 for QEMU */ + props[ncells++] = host; + props[ncells++] = SUN4U_INTERRUPT(addr, 1); + } + } + } + } + + PUSH(dnode); + fword("peer"); + dnode = POP(); + } + set_property(host, "interrupt-map", (char *)props, ncells * sizeof(props[0])); + + props[0] = 0x0000f800; + props[1] = 0x0; + props[2] = 0x0; + props[3] = 7; + set_property(host, "interrupt-map-mask", (char *)props, 4 * sizeof(props[0])); +#endif +} + int ob_pci_init(void) { int bus, devnum, fn; @@ -1382,7 +1489,7 @@ int ob_pci_init(void) unsigned long mem_base, io_base; pci_config_t config = {}; /* host bridge */ - phandle_t phandle_host; + phandle_t phandle_host = 0; PCI_DPRINTF("Initializing PCI host bridge...\n"); @@ -1430,6 +1537,11 @@ int ob_pci_init(void) break; } + /* create available attributes for the PCI bridge */ + ob_pci_set_available(phandle_host, mem_base, io_base); + + /* configure the host bridge interrupt map */ + ob_pci_host_set_interrupt_map(phandle_host); device_end(); diff --git a/roms/openbios/drivers/pci.h b/roms/openbios/drivers/pci.h index ab8f18437..84a2b2cf6 100644 --- a/roms/openbios/drivers/pci.h +++ b/roms/openbios/drivers/pci.h @@ -7,6 +7,7 @@ #define PCI_COMMAND 0x04 #define PCI_COMMAND_IO 0x01 #define PCI_COMMAND_MEMORY 0x02 +#define PCI_COMMAND_BUS_MASTER 0x04 #define PCI_STATUS 0x06 /* 16 bits */ #define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ diff --git a/roms/openbios/drivers/pci_database.c b/roms/openbios/drivers/pci_database.c index 0bd854a51..0070a78ba 100644 --- a/roms/openbios/drivers/pci_database.c +++ b/roms/openbios/drivers/pci_database.c @@ -845,6 +845,24 @@ static const pci_subclass_t cpu_subclass[] = { }, }; +static const pci_dev_t usb_devices[] = { +#if defined(CONFIG_QEMU) + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_KEYL_USB, + "usb", "usb", NULL, + "pci106b,3f\0pciclass,0c0310\0", + 1, 0, 0, + NULL, NULL, + }, +#endif + { + 0xFFFF, 0xFFFF, + NULL, NULL, NULL, NULL, + -1, -1, -1, + NULL, NULL, + }, +}; + static const pci_iface_t usb_iface[] = { { 0x00, "UHCI USB controller", NULL, @@ -852,7 +870,7 @@ static const pci_iface_t usb_iface[] = { }, { 0x10, "OHCI USB controller", NULL, - NULL, NULL, NULL, + usb_devices, &usb_ohci_config_cb, NULL, }, { 0x20, "EHCI USB controller", NULL, diff --git a/roms/openbios/drivers/pci_database.h b/roms/openbios/drivers/pci_database.h index 16b385bf1..7f053d808 100644 --- a/roms/openbios/drivers/pci_database.h +++ b/roms/openbios/drivers/pci_database.h @@ -38,6 +38,7 @@ extern int sabre_config_cb(const pci_config_t *config); extern int bridge_config_cb(const pci_config_t *config); extern int ebus_config_cb(const pci_config_t *config); extern int i82378_config_cb(const pci_config_t *config); +extern int usb_ohci_config_cb(const pci_config_t *config); static inline int pci_compat_len(const pci_dev_t *dev) { diff --git a/roms/openbios/drivers/tcx.fs b/roms/openbios/drivers/tcx.fs index e1ec5ad70..af8991fd0 100644 --- a/roms/openbios/drivers/tcx.fs +++ b/roms/openbios/drivers/tcx.fs @@ -72,54 +72,53 @@ fcode-version3 \ Registers \ -h# 0 constant tcx-off1 -h# 10000 constant /tcx-off1 +h# 0 constant tcx-off-rom +h# 10000 constant /tcx-off-rom -h# 200000 constant tcx-off-dac -h# 4000 constant /tcx-off-dac-24 -h# 4 constant /tcx-off-dac-8 +h# 200000 constant tcx-off-cmap +h# 4000 constant /tcx-off-cmap-24 +h# 4 constant /tcx-off-cmap-8 -h# 240000 constant tcx-off3 -h# 4000 constant /tcx-off3-24 -h# 4 constant /tcx-off3-8 +h# 240000 constant tcx-off-dhc +h# 4000 constant /tcx-off-dhc-24 +h# 4 constant /tcx-off-dhc-8 -h# 280000 constant tcx-off4 -h# 8000 constant /tcx-off4-24 -h# 1 constant /tcx-off4-8 +h# 280000 constant tcx-off-alt +h# 8000 constant /tcx-off-alt-24 +h# 1 constant /tcx-off-alt-8 -h# 301000 constant tcx-off5-24 -h# 300000 constant tcx-off5-8 -h# 1000 constant /tcx-off5-24 -h# 81c constant /tcx-off5-8 +h# 301000 constant tcx-off-thc-24 +h# 300000 constant tcx-off-thc-8 +h# 1000 constant /tcx-off-thc-24 +h# 81c constant /tcx-off-thc-8 -h# 700000 constant tcx-off6 -h# 1000 constant /tcx-off6 +h# 701000 constant tcx-off-tec +h# 1000 constant /tcx-off-tec -h# 800000 constant tcx-off-fb -h# 100000 constant /tcx-off-fb +h# 800000 constant tcx-off-dfb8 +h# 100000 constant /tcx-off-dfb8 -h# 2000000 constant tcx-off8 -h# 400000 constant /tcx-off8-24 -h# 1 constant /tcx-off8-8 +h# 2000000 constant tcx-off-dfb24 +h# 400000 constant /tcx-off-dfb24-24 +h# 1 constant /tcx-off-dfb24-8 -h# 4000000 constant tcx-off9 -h# 400000 constant /tcx-off9-24 -h# 1 constant /tcx-off9-8 +h# 4000000 constant tcx-off-stip +h# 800000 constant /tcx-off-stip -h# 6000000 constant tcx-off10 -h# 800000 constant /tcx-off10 +h# 6000000 constant tcx-off-blit +h# 800000 constant /tcx-off-blit -h# a000000 constant tcx-off11 -h# 400000 constant /tcx-off11-24 -h# 1 constant /tcx-off11-8 +h# a000000 constant tcx-off-rdfb32 +h# 400000 constant /tcx-off-rdfb32-24 +h# 1 constant /tcx-off-rdfb32-8 -h# c000000 constant tcx-off12 -h# 800000 constant /tcx-off12-24 -h# 1 constant /tcx-off12-8 +h# c000000 constant tcx-off-rstip +h# 800000 constant /tcx-off-rstip-24 +h# 1 constant /tcx-off-rstip-8 -h# e000000 constant tcx-off13 -h# 800000 constant /tcx-off13-24 -h# 1 constant /tcx-off13-8 +h# e000000 constant tcx-off-rblit +h# 800000 constant /tcx-off-rblit-24 +h# 1 constant /tcx-off-rblit-8 : >tcx-reg-spec ( offset size -- encoded-reg ) >r 0 my-address d+ my-space encode-phys r> encode-int encode+ @@ -127,37 +126,37 @@ h# 1 constant /tcx-off13-8 : tcx-8bit-reg \ WARNING: order is important (at least to Solaris) - tcx-off-fb /tcx-off-fb >tcx-reg-spec - tcx-off8 /tcx-off8-8 >tcx-reg-spec encode+ - tcx-off9 /tcx-off9-8 >tcx-reg-spec encode+ - tcx-off10 /tcx-off10 >tcx-reg-spec encode+ - tcx-off11 /tcx-off11-8 >tcx-reg-spec encode+ - tcx-off12 /tcx-off12-8 >tcx-reg-spec encode+ - tcx-off13 /tcx-off13-8 >tcx-reg-spec encode+ - tcx-off6 /tcx-off6 >tcx-reg-spec encode+ - tcx-off-dac /tcx-off-dac-8 >tcx-reg-spec encode+ - tcx-off5-8 /tcx-off5-8 >tcx-reg-spec encode+ - tcx-off1 /tcx-off1 >tcx-reg-spec encode+ - tcx-off3 /tcx-off3-8 >tcx-reg-spec encode+ - tcx-off4 /tcx-off4-8 >tcx-reg-spec encode+ + tcx-off-dfb8 /tcx-off-dfb8 >tcx-reg-spec + tcx-off-dfb24 /tcx-off-dfb24-8 >tcx-reg-spec encode+ + tcx-off-stip /tcx-off-stip >tcx-reg-spec encode+ + tcx-off-blit /tcx-off-blit >tcx-reg-spec encode+ + tcx-off-rdfb32 /tcx-off-rdfb32-8 >tcx-reg-spec encode+ + tcx-off-rstip /tcx-off-rstip-8 >tcx-reg-spec encode+ + tcx-off-rblit /tcx-off-rblit-8 >tcx-reg-spec encode+ + tcx-off-tec /tcx-off-tec >tcx-reg-spec encode+ + tcx-off-cmap /tcx-off-cmap-8 >tcx-reg-spec encode+ + tcx-off-thc-8 /tcx-off-thc-8 >tcx-reg-spec encode+ + tcx-off-rom /tcx-off-rom >tcx-reg-spec encode+ + tcx-off-dhc /tcx-off-dhc-8 >tcx-reg-spec encode+ + tcx-off-alt /tcx-off-alt-8 >tcx-reg-spec encode+ " reg" property ; : tcx-24bit-reg \ WARNING: order is important (at least to Solaris) - tcx-off-fb /tcx-off-fb >tcx-reg-spec - tcx-off8 /tcx-off8-24 >tcx-reg-spec encode+ - tcx-off9 /tcx-off9-24 >tcx-reg-spec encode+ - tcx-off10 /tcx-off10 >tcx-reg-spec encode+ - tcx-off11 /tcx-off11-24 >tcx-reg-spec encode+ - tcx-off12 /tcx-off12-24 >tcx-reg-spec encode+ - tcx-off13 /tcx-off13-24 >tcx-reg-spec encode+ - tcx-off6 /tcx-off6 >tcx-reg-spec encode+ - tcx-off-dac /tcx-off-dac-24 >tcx-reg-spec encode+ - tcx-off5-24 /tcx-off5-24 >tcx-reg-spec encode+ - tcx-off1 /tcx-off1 >tcx-reg-spec encode+ - tcx-off3 /tcx-off3-24 >tcx-reg-spec encode+ - tcx-off4 /tcx-off4-24 >tcx-reg-spec encode+ + tcx-off-dfb8 /tcx-off-dfb8 >tcx-reg-spec + tcx-off-dfb24 /tcx-off-dfb24-24 >tcx-reg-spec encode+ + tcx-off-stip /tcx-off-stip >tcx-reg-spec encode+ + tcx-off-blit /tcx-off-blit >tcx-reg-spec encode+ + tcx-off-rdfb32 /tcx-off-rdfb32-24 >tcx-reg-spec encode+ + tcx-off-rstip /tcx-off-rstip-24 >tcx-reg-spec encode+ + tcx-off-rblit /tcx-off-rblit-24 >tcx-reg-spec encode+ + tcx-off-tec /tcx-off-tec >tcx-reg-spec encode+ + tcx-off-cmap /tcx-off-cmap-24 >tcx-reg-spec encode+ + tcx-off-thc-24 /tcx-off-thc-24 >tcx-reg-spec encode+ + tcx-off-rom /tcx-off-rom >tcx-reg-spec encode+ + tcx-off-dhc /tcx-off-dhc-24 >tcx-reg-spec encode+ + tcx-off-alt /tcx-off-alt-24 >tcx-reg-spec encode+ " reg" property ; @@ -198,11 +197,11 @@ headerless \ : dac-map - tcx-off-dac /tcx-dac do-map-in to tcx-dac + tcx-off-cmap /tcx-dac do-map-in to tcx-dac ; : fb-map - tcx-off-fb h# c0000 do-map-in to fb-addr + tcx-off-dfb8 h# c0000 do-map-in to fb-addr ; : map-regs @@ -228,7 +227,11 @@ headerless fb-addr to frame-buffer-adr default-font set-font - frame-buffer-adr encode-int " address" property + \ Sun TCX adapters don't have an address property, but it is useful for + \ OpenBIOS developers. Unfortunately NetBSD SPARC32 has a bug that causes + \ it to fail initialising TCX if the address property is present; so work + \ around this by adding an underscore prefix + frame-buffer-adr encode-int " _address" property openbios-video-width openbios-video-height over char-width / over char-height / fb8-install @@ -240,11 +243,11 @@ headerless \ Handle differences between 8-bit/24-bit mode depth-bits 8 = if tcx-8bit-reg - /tcx-off-dac-8 to /tcx-dac + /tcx-off-cmap-8 to /tcx-dac " true" encode-string " tcx-8-bit" property else tcx-24bit-reg - /tcx-off-dac-24 to /tcx-dac + /tcx-off-cmap-24 to /tcx-dac \ Even with a 24-bit enabled TCX card, the control plane is \ used in 8-bit mode. So force the video subsystem into 8-bit @@ -266,7 +269,7 @@ headerless openbios-video-width encode-int " width" property line-bytes encode-int " linebytes" property - 5 encode-int 0 encode-int encode+ " intr" property + h# 39 encode-int 0 encode-int encode+ " intr" property 5 encode-int " interrupts" property ['] qemu-tcx-driver-install is-install diff --git a/roms/openbios/drivers/usb.c b/roms/openbios/drivers/usb.c new file mode 100644 index 000000000..c6e37174e --- /dev/null +++ b/roms/openbios/drivers/usb.c @@ -0,0 +1,587 @@ +/* + * Driver for USB ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2008-2010 coresystems GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" +#include "drivers/usb.h" +#include "usb.h" +#include "timer.h" +#include "libc/byteorder.h" + +hci_t *usb_hcs = 0; + +static void usb_nop_init (usbdev_t *dev); + +static void +usb_nop_destroy (usbdev_t *dev) +{ + if (dev->descriptor != 0) + free (dev->descriptor); + usb_nop_init (dev); + dev->address = -1; + dev->hub = -1; + dev->port = -1; +} + +static void +usb_nop_poll (usbdev_t *dev) +{ + return; +} + +static void +usb_nop_init (usbdev_t *dev) +{ + dev->descriptor = 0; + dev->destroy = usb_nop_destroy; + dev->poll = usb_nop_poll; +} + +hci_t * +new_controller (void) +{ + hci_t *controller = malloc (sizeof (hci_t)); + + if (controller) { + /* atomic */ + controller->next = usb_hcs; + usb_hcs = controller; + /* atomic end */ + } + + return controller; +} + +void +detach_controller (hci_t *controller) +{ + if (controller == NULL) + return; + if (usb_hcs == controller) { + usb_hcs = controller->next; + } else { + hci_t *it = usb_hcs; + while (it != NULL) { + if (it->next == controller) { + it->next = controller->next; + return; + } + it = it->next; + } + } +} + +/** + * Shut down all controllers + */ +int +usb_exit (void) +{ + while (usb_hcs != NULL) { + usb_hcs->shutdown(usb_hcs); + } + return 0; +} + +/** + * Polls all hubs on all USB controllers, to find out about device changes + */ +void +usb_poll (void) +{ + if (usb_hcs == 0) + return; + hci_t *controller = usb_hcs; + while (controller != NULL) { + int i; + for (i = 0; i < 128; i++) { + if (controller->devices[i] != 0) { + controller->devices[i]->poll (controller->devices[i]); + } + } + controller = controller->next; + } +} + +void +init_device_entry (hci_t *controller, int i) +{ + if (controller->devices[i] != 0) + usb_debug("warning: device %d reassigned?\n", i); + controller->devices[i] = malloc(sizeof(usbdev_t)); + controller->devices[i]->controller = controller; + controller->devices[i]->address = -1; + controller->devices[i]->hub = -1; + controller->devices[i]->port = -1; + controller->devices[i]->init = usb_nop_init; + controller->devices[i]->init (controller->devices[i]); +} + +void +set_feature (usbdev_t *dev, int endp, int feature, int rtype) +{ + dev_req_t dr; + + dr.bmRequestType = rtype; + dr.data_dir = host_to_device; + dr.bRequest = SET_FEATURE; + dr.wValue = __cpu_to_le16(feature); + dr.wIndex = __cpu_to_le16(endp); + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); +} + +void +get_status (usbdev_t *dev, int intf, int rtype, int len, void *data) +{ + dev_req_t dr; + + dr.bmRequestType = rtype; + dr.data_dir = device_to_host; + dr.bRequest = GET_STATUS; + dr.wValue = 0; + dr.wIndex = __cpu_to_le16(intf); + dr.wLength = __cpu_to_le16(len); + dev->controller->control (dev, IN, sizeof (dr), &dr, len, data); +} + +u8 * +get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType, + int descIdx, int langID) +{ + u8 buf[8]; + u8 *result; + dev_req_t dr; + int size; + + dr.bmRequestType = bmRequestType; + dr.data_dir = device_to_host; // always like this for descriptors + dr.bRequest = GET_DESCRIPTOR; + dr.wValue = __cpu_to_le16((descType << 8) | descIdx); + dr.wIndex = __cpu_to_le16(langID); + dr.wLength = __cpu_to_le16(8); + if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) { + usb_debug ("getting descriptor size (type %x) failed\n", + descType); + } + + if (descType == 1) { + device_descriptor_t *dd = (device_descriptor_t *) buf; + usb_debug ("maxPacketSize0: %x\n", dd->bMaxPacketSize0); + if (dd->bMaxPacketSize0 != 0) + dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0; + } + + /* special case for configuration descriptors: they carry all their + subsequent descriptors with them, and keep the entire size at a + different location */ + size = buf[0]; + if (buf[1] == 2) { + int realsize = __le16_to_cpu(((unsigned short *) (buf + 2))[0]); + size = realsize; + } + result = malloc (size); + memset (result, 0, size); + dr.wLength = __cpu_to_le16(size); + if (dev->controller-> + control (dev, IN, sizeof (dr), &dr, size, result)) { + usb_debug ("getting descriptor (type %x, size %x) failed\n", + descType, size); + } + + return result; +} + +void +set_configuration (usbdev_t *dev) +{ + dev_req_t dr; + + dr.bmRequestType = 0; + dr.bRequest = SET_CONFIGURATION; + dr.wValue = __cpu_to_le16(dev->configuration[5]); + dr.wIndex = 0; + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); +} + +int +clear_feature (usbdev_t *dev, int endp, int feature, int rtype) +{ + dev_req_t dr; + + dr.bmRequestType = rtype; + dr.data_dir = host_to_device; + dr.bRequest = CLEAR_FEATURE; + dr.wValue = __cpu_to_le16(feature); + dr.wIndex = __cpu_to_le16(endp); + dr.wLength = 0; + return dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0); +} + +int +clear_stall (endpoint_t *ep) +{ + usbdev_t *dev = ep->dev; + int endp = ep->endpoint; + int rtype = gen_bmRequestType (host_to_device, standard_type, + endp ? endp_recp : dev_recp); + + int ret = clear_feature (dev, endp, ENDPOINT_HALT, rtype); + ep->toggle = 0; + return ret; +} + +/* returns free address or -1 */ +static int +get_free_address (hci_t *controller) +{ + int i; + for (i = 1; i < 128; i++) { + if (controller->devices[i] == 0) + return i; + } + usb_debug ("no free address found\n"); + return -1; // no free address +} + +int +generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr) +{ + int adr = get_free_address (controller); // address to set + dev_req_t dr; + + memset (&dr, 0, sizeof (dr)); + dr.data_dir = host_to_device; + dr.req_type = standard_type; + dr.req_recp = dev_recp; + dr.bRequest = SET_ADDRESS; + dr.wValue = __cpu_to_le16(adr); + dr.wIndex = 0; + dr.wLength = 0; + + init_device_entry(controller, adr); + usbdev_t *dev = controller->devices[adr]; + // dummy values for registering the address + dev->address = 0; + dev->hub = hubaddr; + dev->port = hubport; + dev->speed = speed; + dev->endpoints[0].dev = dev; + dev->endpoints[0].endpoint = 0; + dev->endpoints[0].maxpacketsize = 8; + dev->endpoints[0].toggle = 0; + dev->endpoints[0].direction = SETUP; + mdelay (50); + if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) { + return -1; + } + mdelay (50); + + return adr; +} + +/* Normalize bInterval to log2 of microframes */ +static int +usb_decode_interval(const int speed, const endpoint_type type, const unsigned char bInterval) +{ +#define LOG2(a) ((sizeof(unsigned) << 3) - __builtin_clz(a) - 1) + switch (speed) { + case LOW_SPEED: + switch (type) { + case ISOCHRONOUS: case INTERRUPT: + return LOG2(bInterval) + 3; + default: + return 0; + } + case FULL_SPEED: + switch (type) { + case ISOCHRONOUS: + return (bInterval - 1) + 3; + case INTERRUPT: + return LOG2(bInterval) + 3; + default: + return 0; + } + case HIGH_SPEED: + switch (type) { + case ISOCHRONOUS: case INTERRUPT: + return bInterval - 1; + default: + return LOG2(bInterval); + } + case SUPER_SPEED: + switch (type) { + case ISOCHRONOUS: case INTERRUPT: + return bInterval - 1; + default: + return 0; + } + default: + return 0; + } +#undef LOG2 +} + +static int +set_address (hci_t *controller, int speed, int hubport, int hubaddr) +{ + int adr = controller->set_address(controller, speed, hubport, hubaddr); + if (adr < 0 || !controller->devices[adr]) { + usb_debug ("set_address failed\n"); + return -1; + } + configuration_descriptor_t *cd; + device_descriptor_t *dd; + + usbdev_t *dev = controller->devices[adr]; + dev->address = adr; + dev->hub = hubaddr; + dev->port = hubport; + dev->speed = speed; + dev->descriptor = get_descriptor (dev, gen_bmRequestType + (device_to_host, standard_type, dev_recp), 1, 0, 0); + dd = (device_descriptor_t *) dev->descriptor; + + usb_debug ("* found device (0x%04x:0x%04x, USB %x.%x)", + __le16_to_cpu(dd->idVendor), __le16_to_cpu(dd->idProduct), + __le16_to_cpu(dd->bcdUSB) >> 8, __le16_to_cpu(dd->bcdUSB) & 0xff); + dev->quirks = USB_QUIRK_NONE; + + usb_debug ("\ndevice has %x configurations\n", dd->bNumConfigurations); + if (dd->bNumConfigurations == 0) { + /* device isn't usable */ + usb_debug ("... no usable configuration!\n"); + dev->address = 0; + return -1; + } + + dev->configuration = get_descriptor (dev, gen_bmRequestType + (device_to_host, standard_type, dev_recp), 2, 0, 0); + cd = (configuration_descriptor_t *) dev->configuration; + interface_descriptor_t *interface = + (interface_descriptor_t *) (((char *) cd) + cd->bLength); + { + int i; + int num = cd->bNumInterfaces; + interface_descriptor_t *current = interface; + usb_debug ("device has %x interfaces\n", num); + if (num > 1) { + usb_debug ("\nNOTICE: This driver defaults to using the first interface.\n" + "This might be the wrong choice and lead to limited functionality\n" + "of the device.\n"); + /* we limit to the first interface, as there was no need to + * implement something else for the time being. If you need + * it, see the SetInterface and GetInterface functions in + * the USB specification, and adapt appropriately. + */ + num = (num > 1) ? 1 : num; + } + for (i = 0; i < num; i++) { + int j; + usb_debug (" #%x has %x endpoints, interface %x:%x, protocol %x\n", + current->bInterfaceNumber, current->bNumEndpoints, current->bInterfaceClass, current->bInterfaceSubClass, current->bInterfaceProtocol); + endpoint_descriptor_t *endp = + (endpoint_descriptor_t *) (((char *) current) + + current->bLength); + /* Skip any non-endpoint descriptor */ + if (endp->bDescriptorType != 0x05) + endp = (endpoint_descriptor_t *)(((char *)endp) + ((char *)endp)[0]); + + memset (dev->endpoints, 0, sizeof (dev->endpoints)); + dev->num_endp = 1; // 0 always exists + dev->endpoints[0].dev = dev; + dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0; + dev->endpoints[0].direction = SETUP; + dev->endpoints[0].type = CONTROL; + dev->endpoints[0].interval = usb_decode_interval(dev->speed, CONTROL, endp->bInterval); + for (j = 1; j <= current->bNumEndpoints; j++) { +#ifdef CONFIG_DEBUG_USB + static const char *transfertypes[4] = { + "control", "isochronous", "bulk", "interrupt" + }; + usb_debug (" #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", __le16_to_cpu(endp->wMaxPacketSize), transfertypes[endp->bmAttributes]); +#endif + endpoint_t *ep = + &dev->endpoints[dev->num_endp++]; + ep->dev = dev; + ep->endpoint = endp->bEndpointAddress; + ep->toggle = 0; + ep->maxpacketsize = __le16_to_cpu(endp->wMaxPacketSize); + ep->direction = + ((endp->bEndpointAddress & 0x80) == + 0) ? OUT : IN; + ep->type = endp->bmAttributes; + ep->interval = usb_decode_interval(dev->speed, ep->type, endp->bInterval); + endp = (endpoint_descriptor_t + *) (((char *) endp) + endp->bLength); + } + current = (interface_descriptor_t *) endp; + } + } + + if (controller->finish_device_config && + controller->finish_device_config(dev)) + return adr; /* Device isn't configured correctly, + only control transfers may work. */ + + set_configuration(dev); + + int class = dd->bDeviceClass; + if (class == 0) + class = interface->bInterfaceClass; + + usb_debug(", class: "); + switch (class) { + case audio_device: + usb_debug("audio\n"); + break; + case comm_device: + usb_debug("communication\n"); + break; + case hid_device: + usb_debug ("HID\n"); +#ifdef CONFIG_USB_HID + controller->devices[adr]->init = usb_hid_init; + return adr; +#else + usb_debug ("NOTICE: USB HID support not compiled in\n"); +#endif + break; + case physical_device: + usb_debug("physical\n"); + break; + case imaging_device: + usb_debug("camera\n"); + break; + case printer_device: + usb_debug("printer\n"); + break; + case msc_device: + usb_debug ("MSC\n"); +#ifdef CONFIG_USB_MSC + controller->devices[adr]->init = usb_msc_init; + return adr; +#else + usb_debug ("NOTICE: USB MSC support not compiled in\n"); +#endif + break; + case hub_device: + usb_debug ("hub\n"); +#ifdef CONFIG_USB_HUB + controller->devices[adr]->init = usb_hub_init; + return adr; +#else + usb_debug ("NOTICE: USB hub support not compiled in.\n"); +#endif + break; + case cdc_device: + usb_debug("CDC\n"); + break; + case ccid_device: + usb_debug("smartcard / CCID\n"); + break; + case security_device: + usb_debug("content security\n"); + break; + case video_device: + usb_debug("video\n"); + break; + case healthcare_device: + usb_debug("healthcare\n"); + break; + case diagnostic_device: + usb_debug("diagnostic\n"); + break; + case wireless_device: + usb_debug("wireless\n"); + break; + default: + usb_debug("unsupported class %x\n", class); + break; + } + controller->devices[adr]->init = usb_generic_init; + return adr; +} + +/* + * Should be called by the hub drivers whenever a physical detach occurs + * and can be called by usb class drivers if they are unsatisfied with a + * malfunctioning device. + */ +void +usb_detach_device(hci_t *controller, int devno) +{ + /* check if device exists, as we may have + been called yet by the usb class driver */ + if (controller->devices[devno]) { + controller->devices[devno]->destroy (controller->devices[devno]); + free(controller->devices[devno]); + controller->devices[devno] = NULL; + if (controller->destroy_device) + controller->destroy_device(controller, devno); + } +} + +int +usb_attach_device(hci_t *controller, int hubaddress, int port, int speed) +{ +#ifdef CONFIG_USB_DEBUG + static const char* speeds[] = { "full", "low", "high" }; + usb_debug ("%sspeed device\n", (speed <= 2) ? speeds[speed] : "invalid value - no"); +#endif + int newdev = set_address (controller, speed, port, hubaddress); + if (newdev == -1) + return -1; + usbdev_t *newdev_t = controller->devices[newdev]; + // determine responsible driver - current done in set_address + newdev_t->init (newdev_t); + /* init() may have called usb_detach_device() yet, so check */ + return controller->devices[newdev] ? newdev : -1; +} + +static void +usb_generic_destroy (usbdev_t *dev) +{ + if (usb_generic_remove) + usb_generic_remove(dev); +} + +void +usb_generic_init (usbdev_t *dev) +{ + dev->data = NULL; + dev->destroy = usb_generic_destroy; + + if (usb_generic_create) + usb_generic_create(dev); +} diff --git a/roms/openbios/drivers/usb.h b/roms/openbios/drivers/usb.h new file mode 100644 index 000000000..2e23a1370 --- /dev/null +++ b/roms/openbios/drivers/usb.h @@ -0,0 +1,357 @@ +/* + * Driver for USB ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2008 coresystems GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __USB_H +#define __USB_H +#include <drivers/pci.h> + +typedef enum { host_to_device = 0, device_to_host = 1 } dev_req_dir; +typedef enum { standard_type = 0, class_type = 1, vendor_type = + 2, reserved_type = 3 +} dev_req_type; +typedef enum { dev_recp = 0, iface_recp = 1, endp_recp = 2, other_recp = 3 +} dev_req_recp; + +typedef enum { + GET_STATUS = 0, + CLEAR_FEATURE = 1, + SET_FEATURE = 3, + SET_ADDRESS = 5, + GET_DESCRIPTOR = 6, + SET_DESCRIPTOR = 7, + GET_CONFIGURATION = 8, + SET_CONFIGURATION = 9, + GET_INTERFACE = 10, + SET_INTERFACE = 11, + SYNCH_FRAME = 12 +} bRequest_Codes; + +typedef enum { + ENDPOINT_HALT = 0, + DEVICE_REMOTE_WAKEUP = 1, + TEST_MODE = 2 +} feature_selectors; + +enum { + audio_device = 0x01, + comm_device = 0x02, + hid_device = 0x03, + physical_device = 0x05, + imaging_device = 0x06, + printer_device = 0x07, + msc_device = 0x08, + hub_device = 0x09, + cdc_device = 0x0a, + ccid_device = 0x0b, + security_device = 0x0d, + video_device = 0x0e, + healthcare_device = 0x0f, + diagnostic_device = 0xdc, + wireless_device = 0xe0, + misc_device = 0xef, +}; + +enum { hid_subclass_none = 0, hid_subclass_boot = 1 }; + +enum { + hid_boot_proto_none = 0, + hid_boot_proto_keyboard = 1, + hid_boot_proto_mouse = 2 +}; + +typedef struct { + union { + struct { +#ifdef CONFIG_BIG_ENDIAN + dev_req_dir data_dir:1; + dev_req_type req_type:2; + dev_req_recp req_recp:5; +#else + dev_req_recp req_recp:5; + dev_req_type req_type:2; + dev_req_dir data_dir:1; +#endif + } __attribute__ ((packed)); + unsigned char bmRequestType; + } __attribute__ ((packed)); + unsigned char bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +} __attribute__ ((packed)) dev_req_t; + +struct usbdev_hc; +typedef struct usbdev_hc hci_t; + +struct usbdev; +typedef struct usbdev usbdev_t; + +typedef enum { SETUP, IN, OUT } direction_t; +typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3 +} endpoint_type; + +typedef struct { + usbdev_t *dev; + int endpoint; + direction_t direction; + int toggle; + int maxpacketsize; + endpoint_type type; + int interval; /* expressed as binary logarithm of the number + of microframes (i.e. t = 125us * 2^interval) */ +} endpoint_t; + +enum { FULL_SPEED = 0, LOW_SPEED = 1, HIGH_SPEED = 2, SUPER_SPEED = 3 }; + +struct usbdev { + hci_t *controller; + endpoint_t endpoints[32]; + int num_endp; + int address; // usb address + int hub; // hub, device is attached to + int port; // port where device is attached + int speed; // 1: lowspeed, 0: fullspeed, 2: highspeed + u32 quirks; // quirks field. got to love usb + void *data; + u8 *descriptor; + u8 *configuration; + void (*init) (usbdev_t *dev); + void (*destroy) (usbdev_t *dev); + void (*poll) (usbdev_t *dev); +}; + +typedef enum { OHCI = 0, UHCI = 1, EHCI = 2, XHCI = 3} hc_type; + +struct usbdev_hc { + hci_t *next; + u32 reg_base; + hc_type type; + usbdev_t *devices[128]; // dev 0 is root hub, 127 is last addressable + + /* start(): Resume operation. */ + void (*start) (hci_t *controller); + /* stop(): Stop operation but keep controller initialized. */ + void (*stop) (hci_t *controller); + /* reset(): Perform a controller reset. The controller needs to + be (re)initialized afterwards to work (again). */ + void (*reset) (hci_t *controller); + /* init(): Initialize a (previously reset) controller + to a working state. */ + void (*init) (hci_t *controller); + /* shutdown(): Stop operation, detach host controller and shutdown + this driver instance. After calling shutdown() any + other usage of this hci_t* is invalid. */ + void (*shutdown) (hci_t *controller); + + int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize); + int (*control) (usbdev_t *dev, direction_t pid, int dr_length, + void *devreq, int data_length, u8 *data); + void* (*create_intr_queue) (endpoint_t *ep, int reqsize, int reqcount, int reqtiming); + void (*destroy_intr_queue) (endpoint_t *ep, void *queue); + u8* (*poll_intr_queue) (void *queue); + void *instance; + + /* set_address(): Tell the usb device its address and + return it. xHCI controllers want to + do this by themself. Also, the usbdev + structure has to be allocated and + initialized. */ + int (*set_address) (hci_t *controller, int speed, int hubport, int hubaddr); + /* finish_device_config(): Another hook for xHCI, + returns 0 on success. */ + int (*finish_device_config) (usbdev_t *dev); + /* destroy_device(): Finally, destroy all structures that + were allocated during set_address() + and finish_device_config(). */ + void (*destroy_device) (hci_t *controller, int devaddr); +}; + +typedef struct { + unsigned char bDescLength; + unsigned char bDescriptorType; + unsigned char bNbrPorts; + union { + struct { +#ifdef CONFIG_BIG_ENDIAN + unsigned long:8; + unsigned long arePortIndicatorsSupported:1; + unsigned long ttThinkTime:2; + unsigned long overcurrentProtectionMode:2; + unsigned long isCompoundDevice:1; + unsigned long logicalPowerSwitchingMode:2; +#else + unsigned long logicalPowerSwitchingMode:2; + unsigned long isCompoundDevice:1; + unsigned long overcurrentProtectionMode:2; + unsigned long ttThinkTime:2; + unsigned long arePortIndicatorsSupported:1; + unsigned long:8; +#endif + } __attribute__ ((packed)); + unsigned short wHubCharacteristics; + } __attribute__ ((packed)); + unsigned char bPowerOn2PwrGood; + unsigned char bHubContrCurrent; + char DeviceRemovable[]; +} __attribute__ ((packed)) hub_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdUSB; + unsigned char bDeviceClass; + unsigned char bDeviceSubClass; + unsigned char bDeviceProtocol; + unsigned char bMaxPacketSize0; + unsigned short idVendor; + unsigned short idProduct; + unsigned short bcdDevice; + unsigned char iManufacturer; + unsigned char iProduct; + unsigned char iSerialNumber; + unsigned char bNumConfigurations; +} __attribute__ ((packed)) device_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; + unsigned char bMaxPower; +} __attribute__ ((packed)) configuration_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; +} __attribute__ ((packed)) interface_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bEndpointAddress; + unsigned char bmAttributes; + unsigned short wMaxPacketSize; + unsigned char bInterval; +} __attribute__ ((packed)) endpoint_descriptor_t; + +typedef struct { + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdHID; + unsigned char bCountryCode; + unsigned char bNumDescriptors; + unsigned char bReportDescriptorType; + unsigned short wReportDescriptorLength; +} __attribute__ ((packed)) hid_descriptor_t; + +hci_t *new_controller (void); +void detach_controller (hci_t *controller); +void usb_poll (void); +void init_device_entry (hci_t *controller, int num); + +void set_feature (usbdev_t *dev, int endp, int feature, int rtype); +void get_status (usbdev_t *dev, int endp, int rtype, int len, void *data); +void set_configuration (usbdev_t *dev); +int clear_feature (usbdev_t *dev, int endp, int feature, int rtype); +int clear_stall (endpoint_t *ep); + +void usb_hub_init (usbdev_t *dev); +void usb_hid_init (usbdev_t *dev); +void usb_msc_init (usbdev_t *dev); +void usb_generic_init (usbdev_t *dev); + +u8 *get_descriptor (usbdev_t *dev, unsigned char bmRequestType, + int descType, int descIdx, int langID); + +static inline unsigned char +gen_bmRequestType (dev_req_dir dir, dev_req_type type, dev_req_recp recp) +{ + return (dir << 7) | (type << 5) | recp; +} + +/* default "set address" handler */ +int generic_set_address (hci_t *controller, int speed, int hubport, int hubaddr); + +void usb_detach_device(hci_t *controller, int devno); +int usb_attach_device(hci_t *controller, int hubaddress, int port, int speed); + +u32 usb_quirk_check(u16 vendor, u16 device); +int usb_interface_check(u16 vendor, u16 device); + +#define USB_QUIRK_MSC_FORCE_PROTO_SCSI (1 << 0) +#define USB_QUIRK_MSC_FORCE_PROTO_ATAPI (1 << 1) +#define USB_QUIRK_MSC_FORCE_PROTO_UFI (1 << 2) +#define USB_QUIRK_MSC_FORCE_PROTO_RBC (1 << 3) +#define USB_QUIRK_MSC_FORCE_TRANS_BBB (1 << 4) +#define USB_QUIRK_MSC_FORCE_TRANS_CBI (1 << 5) +#define USB_QUIRK_MSC_FORCE_TRANS_CBI_I (1 << 6) +#define USB_QUIRK_MSC_NO_TEST_UNIT_READY (1 << 7) +#define USB_QUIRK_MSC_SHORT_INQUIRY (1 << 8) +#define USB_QUIRK_TEST (1 << 31) +#define USB_QUIRK_NONE 0 + +#ifdef CONFIG_DEBUG_USB +#define usb_debug(fmt, args...) do { printk(fmt , ##args); } while (0) +#else +#define usb_debug(fmt, args...) +#endif + +/** + * To be implemented by libpayload-client. It's called by the USB stack + * when a new USB device is found which isn't claimed by a built in driver, + * so the client has the chance to know about it. + * + * @param dev descriptor for the USB device + */ +void __attribute__((weak)) usb_generic_create (usbdev_t *dev); + +/** + * To be implemented by libpayload-client. It's called by the USB stack + * when it finds out that a USB device is removed which wasn't claimed by a + * built in driver. + * + * @param dev descriptor for the USB device + */ +void __attribute__((weak)) usb_generic_remove (usbdev_t *dev); + +#endif diff --git a/roms/openbios/drivers/usbhid.c b/roms/openbios/drivers/usbhid.c new file mode 100644 index 000000000..a423278a8 --- /dev/null +++ b/roms/openbios/drivers/usbhid.c @@ -0,0 +1,579 @@ +/* + * Driver for HID devices ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2008-2010 coresystems GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include <libc/string.h> +#include "libc/byteorder.h" +#include "libc/vsprintf.h" +#include "drivers/usb.h" +#include "usb.h" + +DECLARE_UNNAMED_NODE(usb_kbd, INSTALL_OPEN, sizeof(int)); + +static void +keyboard_open(int *idx) +{ + RET(-1); +} + +static void +keyboard_close(int *idx) +{ +} + +static void keyboard_read(void); + +NODE_METHODS( usb_kbd ) = { + { "open", keyboard_open }, + { "close", keyboard_close }, + { "read", keyboard_read }, +}; + +#ifdef CONFIG_USB_DEBUG +static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" }; +#endif +typedef enum { hid_proto_boot = 0, hid_proto_report = 1 } hid_proto; +enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT = + 0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb +}; + +typedef union { + struct { + u8 modifiers; + u8 repeats; + u8 keys[6]; + }; + u8 buffer[8]; +} usb_hid_keyboard_event_t; + +typedef struct { + void* queue; + hid_descriptor_t *descriptor; + + usb_hid_keyboard_event_t previous; + int lastkeypress; + int repeat_delay; +} usbhid_inst_t; + +#define HID_INST(dev) ((usbhid_inst_t*)(dev)->data) + +static void +usb_hid_destroy (usbdev_t *dev) +{ + if (HID_INST(dev)->queue) { + int i; + for (i = 0; i <= dev->num_endp; i++) { + if (dev->endpoints[i].endpoint == 0) + continue; + if (dev->endpoints[i].type != INTERRUPT) + continue; + if (dev->endpoints[i].direction != IN) + continue; + break; + } + dev->controller->destroy_intr_queue( + &dev->endpoints[i], HID_INST(dev)->queue); + HID_INST(dev)->queue = NULL; + } + free (dev->data); +} + +/* keybuffer is global to all USB keyboards */ +static int keycount; +#define KEYBOARD_BUFFER_SIZE 16 +static short keybuffer[KEYBOARD_BUFFER_SIZE]; + +const char *countries[36][2] = { + { "unknown", "us" }, + { "Arabic", "ae" }, + { "Belgian", "be" }, + { "Canadian-Bilingual", "ca" }, + { "Canadian-French", "ca" }, + { "Czech Republic", "cz" }, + { "Danish", "dk" }, + { "Finnish", "fi" }, + { "French", "fr" }, + { "German", "de" }, + { "Greek", "gr" }, + { "Hebrew", "il" }, + { "Hungary", "hu" }, + { "International (ISO)", "iso" }, + { "Italian", "it" }, + { "Japan (Katakana)", "jp" }, + { "Korean", "us" }, + { "Latin American", "us" }, + { "Netherlands/Dutch", "nl" }, + { "Norwegian", "no" }, + { "Persian (Farsi)", "ir" }, + { "Poland", "pl" }, + { "Portuguese", "pt" }, + { "Russia", "ru" }, + { "Slovakia", "sl" }, + { "Spanish", "es" }, + { "Swedish", "se" }, + { "Swiss/French", "ch" }, + { "Swiss/German", "ch" }, + { "Switzerland", "ch" }, + { "Taiwan", "tw" }, + { "Turkish-Q", "tr" }, + { "UK", "uk" }, + { "US", "us" }, + { "Yugoslavia", "yu" }, + { "Turkish-F", "tr" }, + /* 36 - 255: Reserved */ +}; + + + +struct layout_maps { + const char *country; + const short map[4][0x80]; +}; + +static const struct layout_maps *map; + +#define KEY_BREAK 0x101 /* Not on PC KBD */ +#define KEY_DOWN 0x102 /* Down arrow key */ +#define KEY_UP 0x103 /* Up arrow key */ +#define KEY_LEFT 0x104 /* Left arrow key */ +#define KEY_RIGHT 0x105 /* Right arrow key */ +#define KEY_HOME 0x106 /* home key */ +#define KEY_BACKSPACE 0x107 /* not on pc */ +#define KEY_F0 0x108 /* function keys; 64 reserved */ +#define KEY_F(n) (KEY_F0 + (n)) + +#define KEY_DC 0x14a /* delete character */ +#define KEY_IC 0x14b /* insert char or enter ins mode */ + +#define KEY_NPAGE 0x152 /* next page */ +#define KEY_PPAGE 0x153 /* previous page */ + +#define KEY_ENTER 0x157 /* enter or send (unreliable) */ + +#define KEY_PRINT 0x15a /* print/copy */ + +#define KEY_END 0x166 /* end key */ + +static const struct layout_maps keyboard_layouts[] = { +// #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US +{ .country = "us", .map = { + { /* No modifier */ + -1, -1, -1, -1, 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + /* 0x10 */ + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', + /* 0x20 */ + '3', '4', '5', '6', '7', '8', '9', '0', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ';', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Shift modifier */ + -1, -1, -1, -1, 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + /* 0x10 */ + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', + /* 0x20 */ + '#', '$', '%', '^', '&', '*', '(', ')', + '\n', '\e', '\b', '\t', ' ', '_', '+', '{', + /* 0x30 */ + '}', '|', -1, ':', '"', '~', '<', '>', + '?', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Alt */ + -1, -1, -1, -1, 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + /* 0x10 */ + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', + /* 0x20 */ + '3', '4', '5', '6', '7', '8', '9', '0', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ';', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Shift+Alt modifier */ + -1, -1, -1, -1, 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + /* 0x10 */ + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', + /* 0x20 */ + '#', '$', '%', '^', '&', '*', '(', ')', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ':', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + } +}}, +//#endif +}; + +#define MOD_SHIFT (1 << 0) +#define MOD_ALT (1 << 1) +#define MOD_CTRL (1 << 2) + +static void usb_hid_keyboard_queue(int ch) { + /* ignore key presses if buffer full */ + if (keycount < KEYBOARD_BUFFER_SIZE) + keybuffer[keycount++] = ch; +} + +#define KEYBOARD_REPEAT_MS 30 +#define INITIAL_REPEAT_DELAY 10 +#define REPEAT_DELAY 2 + +static void +usb_hid_process_keyboard_event(usbhid_inst_t *const inst, + const usb_hid_keyboard_event_t *const current) +{ + const usb_hid_keyboard_event_t *const previous = &inst->previous; + + int i, keypress = 0, modifiers = 0; + + if (current->modifiers & 0x01) /* Left-Ctrl */ modifiers |= MOD_CTRL; + if (current->modifiers & 0x02) /* Left-Shift */ modifiers |= MOD_SHIFT; + if (current->modifiers & 0x04) /* Left-Alt */ modifiers |= MOD_ALT; + if (current->modifiers & 0x08) /* Left-GUI */ ; + if (current->modifiers & 0x10) /* Right-Ctrl */ modifiers |= MOD_CTRL; + if (current->modifiers & 0x20) /* Right-Shift */ modifiers |= MOD_SHIFT; + if (current->modifiers & 0x40) /* Right-AltGr */ modifiers |= MOD_ALT; + if (current->modifiers & 0x80) /* Right-GUI */ ; + + /* Did the event change at all? */ + if (inst->lastkeypress && + !memcmp(current, previous, sizeof(*current))) { + /* No. Then it's a key repeat event. */ + if (inst->repeat_delay) { + inst->repeat_delay--; + } else { + usb_hid_keyboard_queue(inst->lastkeypress); + inst->repeat_delay = REPEAT_DELAY; + } + + return; + } + + inst->lastkeypress = 0; + + for (i=0; i<6; i++) { + int j; + int skip = 0; + // No more keys? skip + if (current->keys[i] == 0) + return; + + for (j=0; j<6; j++) { + if (current->keys[i] == previous->keys[j]) { + skip = 1; + break; + } + } + if (skip) + continue; + + + /* Mask off MOD_CTRL */ + keypress = map->map[modifiers & 0x03][current->keys[i]]; + + if (modifiers & MOD_CTRL) { + switch (keypress) { + case 'a' ... 'z': + keypress &= 0x1f; + break; + default: + continue; + } + } + + if (keypress == -1) { + /* Debug: Print unknown keys */ + usb_debug ("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n", + current->modifiers, current->repeats, + current->keys[0], current->keys[1], + current->keys[2], current->keys[3], + current->keys[4], current->keys[5], i); + + /* Unknown key? Try next one in the queue */ + continue; + } + + usb_hid_keyboard_queue(keypress); + + /* Remember for authentic key repeat */ + inst->lastkeypress = keypress; + inst->repeat_delay = INITIAL_REPEAT_DELAY; + } +} + +static void +usb_hid_poll (usbdev_t *dev) +{ + usb_hid_keyboard_event_t current; + const u8 *buf; + + while ((buf=dev->controller->poll_intr_queue (HID_INST(dev)->queue))) { + memcpy(¤t.buffer, buf, 8); + usb_hid_process_keyboard_event(HID_INST(dev), ¤t); + HID_INST(dev)->previous = current; + } +} + +static void +usb_hid_set_idle (usbdev_t *dev, interface_descriptor_t *interface, u16 duration) +{ + dev_req_t dr; + dr.data_dir = host_to_device; + dr.req_type = class_type; + dr.req_recp = iface_recp; + dr.bRequest = SET_IDLE; + dr.wValue = __cpu_to_le16((duration >> 2) << 8); + dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber); + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0); +} + +static void +usb_hid_set_protocol (usbdev_t *dev, interface_descriptor_t *interface, hid_proto proto) +{ + dev_req_t dr; + dr.data_dir = host_to_device; + dr.req_type = class_type; + dr.req_recp = iface_recp; + dr.bRequest = SET_PROTOCOL; + dr.wValue = __cpu_to_le16(proto); + dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber); + dr.wLength = 0; + dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0); +} + +static int usb_hid_set_layout (const char *country) +{ + /* FIXME should be per keyboard */ + int i; + + for (i=0; i<sizeof(keyboard_layouts)/sizeof(keyboard_layouts[0]); i++) { + if (strncmp(keyboard_layouts[i].country, country, + strlen(keyboard_layouts[i].country))) + continue; + + /* Found, changing keyboard layout */ + map = &keyboard_layouts[i]; + usb_debug(" Keyboard layout '%s'\n", map->country); + return 0; + } + + usb_debug(" Keyboard layout '%s' not found, using '%s'\n", + country, map->country); + + /* Nothing found, not changed */ + return -1; +} + +void +usb_hid_init (usbdev_t *dev) +{ + configuration_descriptor_t *cd = (configuration_descriptor_t*)dev->configuration; + interface_descriptor_t *interface = (interface_descriptor_t*)(((char *) cd) + cd->bLength); + + if (interface->bInterfaceSubClass == hid_subclass_boot) { + u8 countrycode = 0; + usb_debug (" supports boot interface..\n"); + usb_debug (" it's a %s\n", + boot_protos[interface->bInterfaceProtocol]); + switch (interface->bInterfaceProtocol) { + case hid_boot_proto_keyboard: + dev->data = malloc (sizeof (usbhid_inst_t)); + if (!dev->data) { + printk("Not enough memory for USB HID device.\n"); + return; + } + memset(&HID_INST(dev)->previous, 0x00, + sizeof(HID_INST(dev)->previous)); + usb_debug (" configuring...\n"); + usb_hid_set_protocol(dev, interface, hid_proto_boot); + usb_hid_set_idle(dev, interface, KEYBOARD_REPEAT_MS); + usb_debug (" activating...\n"); +#if 0 + HID_INST (dev)->descriptor = + (hid_descriptor_t *) + get_descriptor(dev, gen_bmRequestType + (device_to_host, standard_type, iface_recp), + 0x21, 0, 0); + countrycode = HID_INST(dev)->descriptor->bCountryCode; +#endif + /* 35 countries defined: */ + if (countrycode > 35) + countrycode = 0; + usb_debug (" Keyboard has %s layout (country code %02x)\n", + countries[countrycode][0], countrycode); + + /* Set keyboard layout accordingly */ + usb_hid_set_layout(countries[countrycode][1]); + + // only add here, because we only support boot-keyboard HID devices + dev->destroy = usb_hid_destroy; + dev->poll = usb_hid_poll; + int i; + for (i = 0; i <= dev->num_endp; i++) { + if (dev->endpoints[i].endpoint == 0) + continue; + if (dev->endpoints[i].type != INTERRUPT) + continue; + if (dev->endpoints[i].direction != IN) + continue; + break; + } + usb_debug (" found endpoint %x for interrupt-in\n", i); + /* 20 buffers of 8 bytes, for every 10 msecs */ + HID_INST(dev)->queue = dev->controller->create_intr_queue (&dev->endpoints[i], 8, 20, 10); + keycount = 0; + usb_debug (" configuration done.\n"); + break; + default: + usb_debug("NOTICE: HID interface protocol %d%s not supported.\n", + interface->bInterfaceProtocol, + (interface->bInterfaceProtocol == hid_boot_proto_mouse ? + " (USB mouse)" : "")); + break; + } + } +} + +static int usbhid_havechar (void) +{ + return (keycount != 0); +} + +static int usbhid_getchar (void) +{ + short ret; + + if (keycount == 0) + return 0; + ret = keybuffer[0]; + memmove(keybuffer, keybuffer + 1, --keycount); + + return (int)ret; +} + +/* ( addr len -- actual ) */ +static void keyboard_read(void) +{ + char *addr; + int len, key, i; + + usb_poll(); + len=POP(); + addr=(char *)cell2pointer(POP()); + + for (i = 0; i < len; i++) { + if (!usbhid_havechar()) + break; + key = usbhid_getchar(); + *addr++ = (char)key; + } + PUSH(i); +} + +void ob_usb_hid_add_keyboard(const char *path) +{ + char name[128]; + phandle_t aliases; + + snprintf(name, sizeof(name), "%s/keyboard", path); + usb_debug("Found keyboard at %s\n", name); + REGISTER_NAMED_NODE(usb_kbd, name); + + push_str(name); + fword("find-device"); + + push_str("keyboard"); + fword("device-type"); + + aliases = find_dev("/aliases"); + set_property(aliases, "adb-keyboard", name, strlen(name) + 1); +} diff --git a/roms/openbios/drivers/usbohci.c b/roms/openbios/drivers/usbohci.c new file mode 100644 index 000000000..774164b0b --- /dev/null +++ b/roms/openbios/drivers/usbohci.c @@ -0,0 +1,926 @@ +/* + * Driver for USB OHCI ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2010 Patrick Georgi + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +//#define USB_DEBUG_ED + +#include "config.h" +#include <asm/io.h> +#include <libopenbios/ofmem.h> +#include "timer.h" +#include "drivers/pci.h" +#include "pci.h" +#include <drivers/usb.h> +#include "usbohci_private.h" +#include "usbohci.h" + +static void ohci_start (hci_t *controller); +static void ohci_stop (hci_t *controller); +static void ohci_reset (hci_t *controller); +static void ohci_shutdown (hci_t *controller); +static int ohci_bulk (endpoint_t *ep, int size, u8 *data, int finalize); +static int ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, + int dalen, u8 *data); +static void* ohci_create_intr_queue (endpoint_t *ep, int reqsize, int reqcount, int reqtiming); +static void ohci_destroy_intr_queue (endpoint_t *ep, void *queue); +static u8* ohci_poll_intr_queue (void *queue); +static void ohci_process_done_queue(ohci_t *ohci, int spew_debug); + +#ifdef USB_DEBUG_ED +static void +dump_td (td_t *cur) +{ + usb_debug("+---------------------------------------------------+\n"); + if (((__le32_to_cpu(cur->config) & (3UL << 19)) >> 19) == 0) + usb_debug("|..[SETUP]..........................................|\n"); + else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 2) + usb_debug("|..[IN].............................................|\n"); + else if (((__le32_to_cpu(cur->config) & (3UL << 8)) >> 8) == 1) + usb_debug("|..[OUT]............................................|\n"); + else + usb_debug("|..[]...............................................|\n"); + usb_debug("|:|============ OHCI TD at [0x%08lx] ==========|:|\n", virt_to_phys(cur)); + usb_debug("|:| ERRORS = [%ld] | CONFIG = [0x%08x] | |:|\n", + 3 - ((__le32_to_cpu(cur->config) & (3UL << 26)) >> 26), __le32_to_cpu(cur->config)); + usb_debug("|:+-----------------------------------------------+:|\n"); + usb_debug("|:| C | Condition Code | [%02ld] |:|\n", + (__le32_to_cpu(cur->config) & (0xFUL << 28)) >> 28); + usb_debug("|:| O | Direction/PID | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (3UL << 19)) >> 19); + usb_debug("|:| N | Buffer Rounding | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (1UL << 18)) >> 18); + usb_debug("|:| F | Delay Intterrupt | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (7UL << 21)) >> 21); + usb_debug("|:| I | Data Toggle | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (3UL << 24)) >> 24); + usb_debug("|:| G | Error Count | [%ld] |:|\n", + (__le32_to_cpu(cur->config) & (3UL << 26)) >> 26); + usb_debug("|:+-----------------------------------------------+:|\n"); + usb_debug("|:| Current Buffer Pointer [0x%08x] |:|\n", __le32_to_cpu(cur->current_buffer_pointer)); + usb_debug("|:+-----------------------------------------------+:|\n"); + usb_debug("|:| Next TD [0x%08x] |:|\n", __le32_to_cpu(cur->next_td)); + usb_debug("|:+-----------------------------------------------+:|\n"); + usb_debug("|:| Current Buffer End [0x%08x] |:|\n", __le32_to_cpu(cur->buffer_end)); + usb_debug("|:|-----------------------------------------------|:|\n"); + usb_debug("|...................................................|\n"); + usb_debug("+---------------------------------------------------+\n"); +} + +static void +dump_ed (ed_t *cur) +{ + td_t *tmp_td = NULL; + usb_debug("+===================================================+\n"); + usb_debug("| ############# OHCI ED at [0x%08lx] ########### |\n", virt_to_phys(cur)); + usb_debug("+---------------------------------------------------+\n"); + usb_debug("| Next Endpoint Descriptor [0x%08lx] |\n", __le32_to_cpu(cur->next_ed) & ~0xFUL); + usb_debug("+---------------------------------------------------+\n"); + usb_debug("| | @ 0x%08x : |\n", __le32_to_cpu(cur->config)); + usb_debug("| C | Maximum Packet Length | [%04ld] |\n", + ((__le32_to_cpu(cur->config) & (0x3fffUL << 16)) >> 16)); + usb_debug("| O | Function Address | [%04d] |\n", + __le32_to_cpu(cur->config) & 0x7F); + usb_debug("| N | Endpoint Number | [%02ld] |\n", + (__le32_to_cpu(cur->config) & (0xFUL << 7)) >> 7); + usb_debug("| F | Endpoint Direction | [%ld] |\n", + ((__le32_to_cpu(cur->config) & (3UL << 11)) >> 11)); + usb_debug("| I | Endpoint Speed | [%ld] |\n", + ((__le32_to_cpu(cur->config) & (1UL << 13)) >> 13)); + usb_debug("| G | Skip | [%ld] |\n", + ((__le32_to_cpu(cur->config) & (1UL << 14)) >> 14)); + usb_debug("| | Format | [%ld] |\n", + ((__le32_to_cpu(cur->config) & (1UL << 15)) >> 15)); + usb_debug("+---------------------------------------------------+\n"); + usb_debug("| TD Queue Tail Pointer [0x%08lx] |\n", + __le32_to_cpu(cur->tail_pointer) & ~0xFUL); + usb_debug("+---------------------------------------------------+\n"); + usb_debug("| TD Queue Head Pointer [0x%08lx] |\n", + __le32_to_cpu(cur->head_pointer) & ~0xFUL); + usb_debug("| CarryToggleBit [%d] Halted [%d] |\n", + (u16)(__le32_to_cpu(cur->head_pointer) & 0x2UL)>>1, (u16)(__le32_to_cpu(cur->head_pointer) & 0x1UL)); + + tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(cur->head_pointer) & ~0xFUL)); + if ((__le32_to_cpu(cur->head_pointer) & ~0xFUL) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL)) { + usb_debug("|:::::::::::::::::: OHCI TD CHAIN ::::::::::::::::::|\n"); + while (virt_to_phys(tmp_td) != (__le32_to_cpu(cur->tail_pointer) & ~0xFUL)) + { + dump_td(tmp_td); + tmp_td = (td_t *)phys_to_virt((__le32_to_cpu(tmp_td->next_td) & ~0xFUL)); + } + usb_debug("|:::::::::::::::: EOF OHCI TD CHAIN ::::::::::::::::|\n"); + usb_debug("+---------------------------------------------------+\n"); + } else { + usb_debug("+---------------------------------------------------+\n"); + } +} +#endif + +static void +ohci_reset (hci_t *controller) +{ + if (controller == NULL) + return; + + OHCI_INST(controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset); + mdelay(2); /* wait 2ms */ + OHCI_INST(controller)->opreg->HcControl = 0; + mdelay(10); /* wait 10ms */ +} + +static void +ohci_reinit (hci_t *controller) +{ +} + +hci_t * +ohci_init (void *bar) +{ + int i; + + hci_t *controller = new_controller (); + + if (!controller) { + printk("Could not create USB controller instance.\n"); + return NULL; + } + + controller->instance = malloc (sizeof (ohci_t)); + if(!controller->instance) { + printk("Not enough memory creating USB controller instance.\n"); + return NULL; + } + + controller->type = OHCI; + + controller->start = ohci_start; + controller->stop = ohci_stop; + controller->reset = ohci_reset; + controller->init = ohci_reinit; + controller->shutdown = ohci_shutdown; + controller->bulk = ohci_bulk; + controller->control = ohci_control; + controller->set_address = generic_set_address; + controller->finish_device_config = NULL; + controller->destroy_device = NULL; + controller->create_intr_queue = ohci_create_intr_queue; + controller->destroy_intr_queue = ohci_destroy_intr_queue; + controller->poll_intr_queue = ohci_poll_intr_queue; + for (i = 0; i < 128; i++) { + controller->devices[i] = 0; + } + init_device_entry (controller, 0); + OHCI_INST (controller)->roothub = controller->devices[0]; + + controller->reg_base = (u32)(unsigned long)bar; + OHCI_INST (controller)->opreg = (opreg_t*)phys_to_virt(controller->reg_base); + usb_debug("OHCI Version %x.%x\n", + (READ_OPREG(OHCI_INST(controller), HcRevision) >> 4) & 0xf, + READ_OPREG(OHCI_INST(controller), HcRevision) & 0xf); + + if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) == USBReset) { + /* cold boot */ + OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~RemoteWakeupConnected); + OHCI_INST (controller)->opreg->HcFmInterval = + __cpu_to_le32((11999 * FrameInterval) | ((((11999 - 210)*6)/7) * FSLargestDataPacket)); + /* TODO: right value for PowerOnToPowerGoodTime ? */ + OHCI_INST (controller)->opreg->HcRhDescriptorA = + __cpu_to_le32(NoPowerSwitching | NoOverCurrentProtection | (10 * PowerOnToPowerGoodTime)); + OHCI_INST (controller)->opreg->HcRhDescriptorB = __cpu_to_le32(0 * DeviceRemovable); + udelay(100); /* TODO: reset asserting according to USB spec */ + } else if ((READ_OPREG(OHCI_INST(controller), HcControl) & HostControllerFunctionalStateMask) != USBOperational) { + OHCI_INST (controller)->opreg->HcControl = + __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl) & ~HostControllerFunctionalStateMask) + | USBResume); + udelay(100); /* TODO: resume time according to USB spec */ + } + int interval = OHCI_INST (controller)->opreg->HcFmInterval; + + OHCI_INST (controller)->opreg->HcCommandStatus = __cpu_to_le32(HostControllerReset); + udelay (10); /* at most 10us for reset to complete. State must be set to Operational within 2ms (5.1.1.4) */ + OHCI_INST (controller)->opreg->HcFmInterval = interval; + ofmem_posix_memalign((void **)&(OHCI_INST (controller)->hcca), 256, 256); + memset((void*)OHCI_INST (controller)->hcca, 0, 256); + + usb_debug("HCCA addr %p\n", OHCI_INST(controller)->hcca); + /* Initialize interrupt table. */ + u32 *const intr_table = OHCI_INST(controller)->hcca->HccaInterruptTable; + ed_t *const periodic_ed; + ofmem_posix_memalign((void **)&periodic_ed, sizeof(ed_t), sizeof(ed_t)); + memset((void *)periodic_ed, 0, sizeof(*periodic_ed)); + for (i = 0; i < 32; ++i) + intr_table[i] = __cpu_to_le32(virt_to_phys(periodic_ed)); + OHCI_INST (controller)->periodic_ed = periodic_ed; + + OHCI_INST (controller)->opreg->HcHCCA = __cpu_to_le32(virt_to_phys(OHCI_INST(controller)->hcca)); + /* Make sure periodic schedule is enabled. */ + OHCI_INST (controller)->opreg->HcControl |= __cpu_to_le32(PeriodicListEnable); + OHCI_INST (controller)->opreg->HcControl &= __cpu_to_le32(~IsochronousEnable); // unused by this driver + // disable everything, contrary to what OHCI spec says in 5.1.1.4, as we don't need IRQs + OHCI_INST (controller)->opreg->HcInterruptEnable = __cpu_to_le32(1<<31); + OHCI_INST (controller)->opreg->HcInterruptDisable = __cpu_to_le32(~(1<<31)); + OHCI_INST (controller)->opreg->HcInterruptStatus = __cpu_to_le32(~0); + OHCI_INST (controller)->opreg->HcPeriodicStart = + __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcFmInterval) & FrameIntervalMask) / 10 * 9); + OHCI_INST (controller)->opreg->HcControl = __cpu_to_le32((READ_OPREG(OHCI_INST(controller), HcControl) + & ~HostControllerFunctionalStateMask) | USBOperational); + + mdelay(100); + + controller->devices[0]->controller = controller; + controller->devices[0]->init = ohci_rh_init; + controller->devices[0]->init (controller->devices[0]); + return controller; +} + +hci_t * +ohci_pci_init (pci_addr addr) +{ + u32 reg_base; + uint16_t cmd; + + cmd = pci_config_read16(addr, PCI_COMMAND); + cmd |= PCI_COMMAND_BUS_MASTER; + pci_config_write16(addr, PCI_COMMAND, cmd); + + /* regarding OHCI spec, Appendix A, BAR_OHCI register description, Table A-4 + * BASE ADDRESS only [31-12] bits. All other usually 0, but not all. + * OHCI mandates MMIO, so bit 0 is clear */ + reg_base = pci_config_read32 (addr, PCI_BASE_ADDR_0) & 0xfffff000; + + return ohci_init((void *)(unsigned long)reg_base); +} + +static void +ohci_shutdown (hci_t *controller) +{ + if (controller == 0) + return; + detach_controller (controller); + ohci_stop(controller); + OHCI_INST (controller)->roothub->destroy (OHCI_INST (controller)-> + roothub); + controller->reset (controller); + free ((void *)OHCI_INST (controller)->periodic_ed); + free (OHCI_INST (controller)); + free (controller); +} + +static void +ohci_start (hci_t *controller) +{ +// TODO: turn on all operation of OHCI, but assume that it's initialized. +} + +static void +ohci_stop (hci_t *controller) +{ +// TODO: turn off all operation of OHCI +} + +static int +wait_for_ed(usbdev_t *dev, ed_t *head, int pages) +{ + usb_debug("Waiting for %d pages on dev %p with head %p\n", pages, dev, head); + /* wait for results */ + /* TOTEST: how long to wait? + * give 2s per TD (2 pages) plus another 2s for now + */ + int timeout = pages*1000 + 2000; + while (((__le32_to_cpu(head->head_pointer) & ~3) != __le32_to_cpu(head->tail_pointer)) && + !(__le32_to_cpu(head->head_pointer) & 1) && + ((__le32_to_cpu((((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3)))->config) + & TD_CC_MASK) >= TD_CC_NOACCESS) && timeout--) { + /* don't log every ms */ + if (!(timeout % 100)) + usb_debug("intst: %x; ctrl: %x; cmdst: %x; head: %x -> %x, tail: %x, condition: %x\n", + READ_OPREG(OHCI_INST(dev->controller), HcInterruptStatus), + READ_OPREG(OHCI_INST(dev->controller), HcControl), + READ_OPREG(OHCI_INST(dev->controller), HcCommandStatus), + __le32_to_cpu(head->head_pointer), + __le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->next_td), + __le32_to_cpu(head->tail_pointer), + (__le32_to_cpu(((td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~3))->config) & TD_CC_MASK) >> TD_CC_SHIFT); + mdelay(1); + } + if (timeout < 0) + usb_debug("Error: ohci: endpoint " + "descriptor processing timed out.\n"); + /* Clear the done queue. */ + ohci_process_done_queue(OHCI_INST(dev->controller), 1); + + if (__le32_to_cpu(head->head_pointer) & 1) { + usb_debug("HALTED!\n"); + return 1; + } + return 0; +} + +static void +ohci_free_ed (ed_t *const head) +{ + /* In case the transfer canceled, we have to free unprocessed TDs. */ + while ((__le32_to_cpu(head->head_pointer) & ~0x3) != __le32_to_cpu(head->tail_pointer)) { + /* Save current TD pointer. */ + td_t *const cur_td = + (td_t*)phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3); + /* Advance head pointer. */ + head->head_pointer = cur_td->next_td; + /* Free current TD. */ + free((void *)cur_td); + } + + /* Always free the dummy TD */ + if ((__le32_to_cpu(head->head_pointer) & ~0x3) == __le32_to_cpu(head->tail_pointer)) + free(phys_to_virt(__le32_to_cpu(head->head_pointer) & ~0x3)); + /* and the ED. */ + free((void *)head); +} + +static int +ohci_control (usbdev_t *dev, direction_t dir, int drlen, void *devreq, int dalen, + unsigned char *data) +{ + td_t *cur; + + // pages are specified as 4K in OHCI, so don't use getpagesize() + int first_page = (unsigned long)data / 4096; + int last_page = (unsigned long)(data+dalen-1)/4096; + if (last_page < first_page) last_page = first_page; + int pages = (dalen==0)?0:(last_page - first_page + 1); + + /* First TD. */ + td_t *const first_td; + ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t)); + memset((void *)first_td, 0, sizeof(*first_td)); + cur = first_td; + + cur->config = __cpu_to_le32(TD_DIRECTION_SETUP | + TD_DELAY_INTERRUPT_NOINTR | + TD_TOGGLE_FROM_TD | + TD_TOGGLE_DATA0 | + TD_CC_NOACCESS); + cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(devreq)); + cur->buffer_end = __cpu_to_le32(virt_to_phys((char *)devreq + drlen - 1)); + + while (pages > 0) { + /* One more TD. */ + td_t *const next; + ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t)); + memset((void *)next, 0, sizeof(*next)); + /* Linked to the previous. */ + cur->next_td = __cpu_to_le32(virt_to_phys(next)); + /* Advance to the new TD. */ + cur = next; + + cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | + TD_DELAY_INTERRUPT_NOINTR | + TD_TOGGLE_FROM_ED | + TD_CC_NOACCESS); + cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); + pages--; + int consumed = (4096 - ((unsigned long)data % 4096)); + if (consumed >= dalen) { + // end of data is within same page + cur->buffer_end = __cpu_to_le32(virt_to_phys(data + dalen - 1)); + dalen = 0; + /* assert(pages == 0); */ + } else { + dalen -= consumed; + data += consumed; + pages--; + int second_page_size = dalen; + if (dalen > 4096) { + second_page_size = 4096; + } + cur->buffer_end = __cpu_to_le32(virt_to_phys(data + second_page_size - 1)); + dalen -= second_page_size; + data += second_page_size; + } + } + + /* One more TD. */ + td_t *const next_td; + ofmem_posix_memalign((void **)&next_td, sizeof(td_t), sizeof(td_t)); + memset((void *)next_td, 0, sizeof(*next_td)); + /* Linked to the previous. */ + cur->next_td = __cpu_to_le32(virt_to_phys(next_td)); + /* Advance to the new TD. */ + cur = next_td; + cur->config = __cpu_to_le32((dir == IN ? TD_DIRECTION_OUT : TD_DIRECTION_IN) | + TD_DELAY_INTERRUPT_ZERO | /* Write done head after this TD. */ + TD_TOGGLE_FROM_TD | + TD_TOGGLE_DATA1 | + TD_CC_NOACCESS); + cur->current_buffer_pointer = 0; + cur->buffer_end = 0; + + /* Final dummy TD. */ + td_t *const final_td; + ofmem_posix_memalign((void **)&final_td, sizeof(td_t), sizeof(td_t)); + memset((void *)final_td, 0, sizeof(*final_td)); + /* Linked to the previous. */ + cur->next_td = __cpu_to_le32(virt_to_phys(final_td)); + + /* Data structures */ + ed_t *head; + ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t)); + memset((void*)head, 0, sizeof(*head)); + head->config = __cpu_to_le32((dev->address << ED_FUNC_SHIFT) | + (0 << ED_EP_SHIFT) | + (OHCI_FROM_TD << ED_DIR_SHIFT) | + (dev->speed?ED_LOWSPEED:0) | + (dev->endpoints[0].maxpacketsize << ED_MPS_SHIFT)); + head->tail_pointer = __cpu_to_le32(virt_to_phys(final_td)); + head->head_pointer = __cpu_to_le32(virt_to_phys(first_td)); + + usb_debug("ohci_control(): doing transfer with %x. first_td at %x\n", + __le32_to_cpu(head->config) & ED_FUNC_MASK, __le32_to_cpu(head->head_pointer)); +#ifdef USB_DEBUG_ED + dump_ed(head); +#endif + + /* activate schedule */ + OHCI_INST(dev->controller)->opreg->HcControlHeadED = __cpu_to_le32(virt_to_phys(head)); + OHCI_INST(dev->controller)->opreg->HcControl |= __cpu_to_le32(ControlListEnable); + OHCI_INST(dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(ControlListFilled); + + int failure = wait_for_ed(dev, head, + (dalen==0)?0:(last_page - first_page + 1)); + /* Wait some frames before and one after disabling list access. */ + mdelay(4); + OHCI_INST(dev->controller)->opreg->HcControl &= __cpu_to_le32(~ControlListEnable); + mdelay(1); + + /* free memory */ + ohci_free_ed(head); + + return failure; +} + +/* finalize == 1: if data is of packet aligned size, add a zero length packet */ +static int +ohci_bulk (endpoint_t *ep, int dalen, u8 *data, int finalize) +{ + int i; + usb_debug("bulk: %x bytes from %p, finalize: %x, maxpacketsize: %x\n", dalen, data, finalize, ep->maxpacketsize); + + td_t *cur, *next; + + // pages are specified as 4K in OHCI, so don't use getpagesize() + int first_page = (unsigned long)data / 4096; + int last_page = (unsigned long)(data+dalen-1)/4096; + if (last_page < first_page) last_page = first_page; + int pages = (dalen==0)?0:(last_page - first_page + 1); + int td_count = (pages+1)/2; + + if (finalize && ((dalen % ep->maxpacketsize) == 0)) { + td_count++; + } + + /* First TD. */ + td_t *const first_td; + ofmem_posix_memalign((void **)&first_td, sizeof(td_t), sizeof(td_t)); + memset((void *)first_td, 0, sizeof(*first_td)); + cur = next = first_td; + + for (i = 0; i < td_count; ++i) { + /* Advance to next TD. */ + cur = next; + cur->config = __cpu_to_le32((ep->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | + TD_DELAY_INTERRUPT_NOINTR | + TD_TOGGLE_FROM_ED | + TD_CC_NOACCESS); + cur->current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); + pages--; + if (dalen == 0) { + /* magic TD for empty packet transfer */ + cur->current_buffer_pointer = 0; + cur->buffer_end = 0; + /* assert((pages == 0) && finalize); */ + } + int consumed = (4096 - ((unsigned long)data % 4096)); + if (consumed >= dalen) { + // end of data is within same page + cur->buffer_end = __cpu_to_le32(virt_to_phys(data + dalen - 1)); + dalen = 0; + /* assert(pages == finalize); */ + } else { + dalen -= consumed; + data += consumed; + pages--; + int second_page_size = dalen; + if (dalen > 4096) { + second_page_size = 4096; + } + cur->buffer_end = __cpu_to_le32(virt_to_phys(data + second_page_size - 1)); + dalen -= second_page_size; + data += second_page_size; + } + /* One more TD. */ + ofmem_posix_memalign((void **)&next, sizeof(td_t), sizeof(td_t)); + memset((void *)next, 0, sizeof(*next)); + /* Linked to the previous. */ + cur->next_td = __cpu_to_le32(virt_to_phys(next)); + } + + /* Write done head after last TD. */ + cur->config &= __cpu_to_le32(~TD_DELAY_INTERRUPT_MASK); + /* Advance to final, dummy TD. */ + cur = next; + + /* Data structures */ + ed_t *head; + ofmem_posix_memalign((void **)&head, sizeof(ed_t), sizeof(ed_t)); + memset((void*)head, 0, sizeof(*head)); + head->config = __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) | + ((ep->endpoint & 0xf) << ED_EP_SHIFT) | + (((ep->direction==IN)?OHCI_IN:OHCI_OUT) << ED_DIR_SHIFT) | + (ep->dev->speed?ED_LOWSPEED:0) | + (ep->maxpacketsize << ED_MPS_SHIFT)); + head->tail_pointer = __cpu_to_le32(virt_to_phys(cur)); + head->head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle?ED_TOGGLE:0)); + + usb_debug("doing bulk transfer with %x(%x). first_td at %lx, last %lx\n", + __le32_to_cpu(head->config) & ED_FUNC_MASK, + (__le32_to_cpu(head->config) & ED_EP_MASK) >> ED_EP_SHIFT, + virt_to_phys(first_td), virt_to_phys(cur)); + + /* activate schedule */ + OHCI_INST(ep->dev->controller)->opreg->HcBulkHeadED = __cpu_to_le32(virt_to_phys(head)); + OHCI_INST(ep->dev->controller)->opreg->HcControl |= __cpu_to_le32(BulkListEnable); + OHCI_INST(ep->dev->controller)->opreg->HcCommandStatus = __cpu_to_le32(BulkListFilled); + + int failure = wait_for_ed(ep->dev, head, + (dalen==0)?0:(last_page - first_page + 1)); + /* Wait some frames before and one after disabling list access. */ + mdelay(4); + OHCI_INST(ep->dev->controller)->opreg->HcControl &= __cpu_to_le32(~BulkListEnable); + mdelay(1); + + ep->toggle = __le32_to_cpu(head->head_pointer) & ED_TOGGLE; + + /* free memory */ + ohci_free_ed(head); + + if (failure) { + /* try cleanup */ + clear_stall(ep); + } + + return failure; +} + + +struct _intr_queue; + +struct _intrq_td { + volatile td_t td; + u8 *data; + struct _intrq_td *next; + struct _intr_queue *intrq; +}; + +struct _intr_queue { + volatile ed_t ed; + struct _intrq_td *head; + struct _intrq_td *tail; + u8 *data; + int reqsize; + endpoint_t *endp; + unsigned int remaining_tds; + int destroy; +}; + +typedef struct _intrq_td intrq_td_t; +typedef struct _intr_queue intr_queue_t; + +#define INTRQ_TD_FROM_TD(x) ((intrq_td_t *)x) + +static void +ohci_fill_intrq_td(intrq_td_t *const td, intr_queue_t *const intrq, + u8 *const data) +{ + memset(td, 0, sizeof(*td)); + td->td.config = __cpu_to_le32(TD_QUEUETYPE_INTR | + (intrq->endp->direction == IN ? TD_DIRECTION_IN : TD_DIRECTION_OUT) | + TD_DELAY_INTERRUPT_ZERO | + TD_TOGGLE_FROM_ED | + TD_CC_NOACCESS); + td->td.current_buffer_pointer = __cpu_to_le32(virt_to_phys(data)); + td->td.buffer_end = __cpu_to_le32(virt_to_phys(data) + intrq->reqsize - 1); + td->intrq = intrq; + td->data = data; +} + +/* create and hook-up an intr queue into device schedule */ +static void * +ohci_create_intr_queue(endpoint_t *const ep, const int reqsize, + const int reqcount, const int reqtiming) +{ + int i; + intrq_td_t *first_td = NULL, *last_td = NULL; + + if (reqsize > 4096) + return NULL; + + intr_queue_t *const intrq; + ofmem_posix_memalign((void **)&intrq, sizeof(intrq->ed), sizeof(*intrq)); + memset(intrq, 0, sizeof(*intrq)); + intrq->data = (u8 *)malloc(reqcount * reqsize); + intrq->reqsize = reqsize; + intrq->endp = ep; + + /* Create #reqcount TDs. */ + u8 *cur_data = intrq->data; + for (i = 0; i < reqcount; ++i) { + intrq_td_t *const td; + ofmem_posix_memalign((void **)&td, sizeof(td->td), sizeof(*td)); + ++intrq->remaining_tds; + ohci_fill_intrq_td(td, intrq, cur_data); + cur_data += reqsize; + if (!first_td) + first_td = td; + else + last_td->td.next_td = __cpu_to_le32(virt_to_phys(&td->td)); + last_td = td; + } + + /* Create last, dummy TD. */ + intrq_td_t *dummy_td; + ofmem_posix_memalign((void **)&dummy_td, sizeof(dummy_td->td), sizeof(*dummy_td)); + memset(dummy_td, 0, sizeof(*dummy_td)); + dummy_td->intrq = intrq; + if (last_td) + last_td->td.next_td = __cpu_to_le32(virt_to_phys(&dummy_td->td)); + last_td = dummy_td; + + /* Initialize ED. */ + intrq->ed.config = __cpu_to_le32((ep->dev->address << ED_FUNC_SHIFT) | + ((ep->endpoint & 0xf) << ED_EP_SHIFT) | + (((ep->direction == IN) ? OHCI_IN : OHCI_OUT) << ED_DIR_SHIFT) | + (ep->dev->speed ? ED_LOWSPEED : 0) | + (ep->maxpacketsize << ED_MPS_SHIFT)); + intrq->ed.tail_pointer = __cpu_to_le32(virt_to_phys(last_td)); + intrq->ed.head_pointer = __cpu_to_le32(virt_to_phys(first_td) | (ep->toggle ? ED_TOGGLE : 0)); + +#ifdef USB_DEBUG_ED + dump_ed(&intrq->ed); +#endif + /* Insert ED into periodic table. */ + int nothing_placed = 1; + ohci_t *const ohci = OHCI_INST(ep->dev->controller); + u32 *const intr_table = ohci->hcca->HccaInterruptTable; + const u32 dummy_ptr = __cpu_to_le32(virt_to_phys(ohci->periodic_ed)); + for (i = 0; i < 32; i += reqtiming) { + /* Advance to the next free position. */ + while ((i < 32) && (intr_table[i] != dummy_ptr)) ++i; + if (i < 32) { + usb_debug("Placed endpoint %lx to %d\n", virt_to_phys(&intrq->ed), i); + intr_table[i] = __cpu_to_le32(virt_to_phys(&intrq->ed)); + nothing_placed = 0; + } + } + if (nothing_placed) { + usb_debug("Error: Failed to place ohci interrupt endpoint " + "descriptor into periodic table: no space left\n"); + ohci_destroy_intr_queue(ep, intrq); + return NULL; + } + + return intrq; +} + +/* remove queue from device schedule, dropping all data that came in */ +static void +ohci_destroy_intr_queue(endpoint_t *const ep, void *const q_) +{ + intr_queue_t *const intrq = (intr_queue_t *)q_; + + int i; + + /* Remove interrupt queue from periodic table. */ + ohci_t *const ohci = OHCI_INST(ep->dev->controller); + u32 *const intr_table = ohci->hcca->HccaInterruptTable; + for (i=0; i < 32; ++i) { + if (intr_table[i] == __cpu_to_le32(virt_to_phys(intrq))) + intr_table[i] = __cpu_to_le32(virt_to_phys(ohci->periodic_ed)); + } + /* Wait for frame to finish. */ + mdelay(1); + + /* Free unprocessed TDs. */ + while ((__le32_to_cpu(intrq->ed.head_pointer) & ~0x3) != __le32_to_cpu(intrq->ed.tail_pointer)) { + td_t *const cur_td = (td_t *)phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3); + intrq->ed.head_pointer = cur_td->next_td; + free(INTRQ_TD_FROM_TD(cur_td)); + --intrq->remaining_tds; + } + /* Free final, dummy TD. */ + free(phys_to_virt(__le32_to_cpu(intrq->ed.head_pointer) & ~0x3)); + /* Free data buffer. */ + free(intrq->data); + + /* Free TDs already fetched from the done queue. */ + ohci_process_done_queue(ohci, 1); + while (intrq->head) { + intrq_td_t *const cur_td = (intrq_td_t *const )__le32_to_cpu(intrq->head); + intrq->head = intrq->head->next; + free(cur_td); + --intrq->remaining_tds; + } + + /* Mark interrupt queue to be destroyed. + ohci_process_done_queue() will free the remaining TDs + and finish the interrupt queue off once all TDs are gone. */ + intrq->destroy = 1; + + /* Save data toggle. */ + ep->toggle = __le32_to_cpu(intrq->ed.head_pointer) & ED_TOGGLE; +} + +/* read one intr-packet from queue, if available. extend the queue for new input. + return NULL if nothing new available. + Recommended use: while (data=poll_intr_queue(q)) process(data); + */ +static u8 * +ohci_poll_intr_queue(void *const q_) +{ + intr_queue_t *const intrq = (intr_queue_t *)q_; + + u8 *data = NULL; + + /* Process done queue first, then check if we have work to do. */ + ohci_process_done_queue(OHCI_INST(intrq->endp->dev->controller), 0); + + if (intrq->head) { + /* Save pointer to processed TD and advance. */ + intrq_td_t *const cur_td = intrq->head; + intrq->head = cur_td->next; + + /* Return data buffer of this TD. */ + data = cur_td->data; + + /* Requeue this TD (i.e. copy to dummy and requeue as dummy). */ + intrq_td_t *const dummy_td = + INTRQ_TD_FROM_TD(phys_to_virt(__le32_to_cpu(intrq->ed.tail_pointer))); + ohci_fill_intrq_td(dummy_td, intrq, data); + /* Reset all but intrq pointer (i.e. init as dummy). */ + memset(cur_td, 0, sizeof(*cur_td)); + cur_td->intrq = intrq; + /* Insert into interrupt queue as dummy. */ + dummy_td->td.next_td = __le32_to_cpu(virt_to_phys(&cur_td->td)); + intrq->ed.tail_pointer = __le32_to_cpu(virt_to_phys(&cur_td->td)); + } + + return data; +} + +static void +ohci_process_done_queue(ohci_t *const ohci, const int spew_debug) +{ + int i, j; + + /* Temporary queue of interrupt queue TDs (to reverse order). */ + intrq_td_t *temp_tdq = NULL; + + /* Check if done head has been written. */ + if (!(READ_OPREG(ohci, HcInterruptStatus) & WritebackDoneHead)) + return; + /* Fetch current done head. + Lsb is only interesting for hw interrupts. */ + u32 phys_done_queue = __le32_to_cpu(ohci->hcca->HccaDoneHead) & ~1; + /* Tell host controller, he may overwrite the done head pointer. */ + ohci->opreg->HcInterruptStatus = __cpu_to_le32(WritebackDoneHead); + + i = 0; + /* Process done queue (it's in reversed order). */ + while (phys_done_queue) { + td_t *const done_td = (td_t *)phys_to_virt(phys_done_queue); + + /* Advance pointer to next TD. */ + phys_done_queue = __le32_to_cpu(done_td->next_td); + + switch (__le32_to_cpu(done_td->config) & TD_QUEUETYPE_MASK) { + case TD_QUEUETYPE_ASYNC: + /* Free processed async TDs. */ + free((void *)done_td); + break; + case TD_QUEUETYPE_INTR: { + intrq_td_t *const td = INTRQ_TD_FROM_TD(done_td); + intr_queue_t *const intrq = td->intrq; + /* Check if the corresponding interrupt + queue is still beeing processed. */ + if (intrq->destroy) { + /* Free this TD, and */ + free(td); + --intrq->remaining_tds; + /* the interrupt queue if it has no more TDs. */ + if (!intrq->remaining_tds) + free(intrq); + usb_debug("Freed TD from orphaned interrupt " + "queue, %d TDs remain.\n", + intrq->remaining_tds); + } else { + /* Save done TD to be processed. */ + td->next = temp_tdq; + temp_tdq = td; + } + break; + } + default: + break; + } + ++i; + } + if (spew_debug) + usb_debug("Processed %d done TDs.\n", i); + + j = 0; + /* Process interrupt queue TDs in right order. */ + while (temp_tdq) { + /* Save pointer of current TD and advance. */ + intrq_td_t *const cur_td = temp_tdq; + temp_tdq = temp_tdq->next; + + /* The interrupt queue for the current TD. */ + intr_queue_t *const intrq = cur_td->intrq; + /* Append to interrupt queue. */ + if (!intrq->head) { + /* First element. */ + intrq->head = intrq->tail = cur_td; + } else { + /* Insert at tail. */ + intrq->tail->next = cur_td; + intrq->tail = cur_td; + } + /* It's always the last element. */ + cur_td->next = NULL; + ++j; + } + if (spew_debug) + usb_debug("processed %d done tds, %d intr tds thereof.\n", i, j); +} + +int ob_usb_ohci_init (const char *path, uint32_t addr) +{ + hci_t *ctrl; + int i; + + usb_debug("ohci_init: %s addr = %x\n", path, addr); + ctrl = ohci_pci_init(addr); + if (!ctrl) + return 0; + + /* Init ports */ + usb_poll(); + + /* Look for a keyboard */ + for (i = 0; i < 128; i++) { + if (ctrl->devices[i] && ctrl->devices[i]->configuration) { + configuration_descriptor_t *cd; + interface_descriptor_t *intf; + + cd = (configuration_descriptor_t *)ctrl->devices[i]->configuration; + intf = (interface_descriptor_t *)(ctrl->devices[i]->configuration + cd->bLength); + usb_debug("Device at port %d is class %d\n", i, intf->bInterfaceClass); + if (intf->bInterfaceClass == hid_device && + intf->bInterfaceSubClass == hid_subclass_boot && + intf->bInterfaceProtocol == hid_boot_proto_keyboard ) { + break; + } + } + } + if ( i < 128 ) + ob_usb_hid_add_keyboard(path); + + return 1; +} diff --git a/roms/openbios/drivers/usbohci.h b/roms/openbios/drivers/usbohci.h new file mode 100644 index 000000000..690332871 --- /dev/null +++ b/roms/openbios/drivers/usbohci.h @@ -0,0 +1,45 @@ +/* + * Driver for USB OHCI ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2010 Patrick Georgi + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __OHCI_H +#define __OHCI_H + +#include "config.h" +#include "usbohci_private.h" + +hci_t *ohci_pci_init (u32 addr); +hci_t *ohci_init (void *bar); + +void ohci_rh_init (usbdev_t *dev); + +#endif diff --git a/roms/openbios/drivers/usbohci_private.h b/roms/openbios/drivers/usbohci_private.h new file mode 100644 index 000000000..b3a723e21 --- /dev/null +++ b/roms/openbios/drivers/usbohci_private.h @@ -0,0 +1,270 @@ +/* + * Driver for USB OHCI ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2010 Patrick Georgi + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __OHCI_PRIVATE_H +#define __OHCI_PRIVATE_H + +#include "libc/byteorder.h" +#include "usb.h" + +#define READ_OPREG(ohci, field) (__le32_to_cpu((ohci)->opreg->field)) +#define MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit)) + + // FIXME: fake + typedef enum { CMD} reg; + + enum { + NumberDownstreamPorts = 1<<0, + PowerSwitchingMode = 1<<8, + NoPowerSwitching = 1<<9, + DeviceType = 1<<10, + OverCurrentProtectionMode = 1<<11, + NoOverCurrentProtection = 1<<12, + PowerOnToPowerGoodTime = 1<<24 + } HcRhDescriptorAReg; + + enum { + NumberDownstreamPortsMask = MASK(0, 8), + PowerOnToPowerGoodTimeMask = MASK(24, 8) + } HcRhDescriptorAMask; + + enum { + DeviceRemovable = 1<<0, + PortPowerControlMask = 1<<16 + } HcRhDescriptorBReg; + + enum { + CurrentConnectStatus = 1<<0, + PortEnableStatus = 1<<1, + PortSuspendStatus = 1<<2, + PortOverCurrentIndicator = 1<<3, + PortResetStatus = 1<<4, + PortPowerStatus = 1<<8, + LowSpeedDeviceAttached = 1<<9, + ConnectStatusChange = 1<<16, + PortEnableStatusChange = 1<<17, + PortSuspendStatusChange = 1<<18, + PortOverCurrentIndicatorChange = 1<<19, + PortResetStatusChange = 1<<20 + } HcRhPortStatusRead; + enum { + ClearPortEnable = 1<<0, + SetPortEnable = 1<<1, + SetPortSuspend = 1<<2, + ClearSuspendStatus = 1<<3, + SetPortReset = 1<<4, + SetPortPower = 1<<8, + ClearPortPower = 1<<9, + } HcRhPortStatusSet; + + enum { + LocalPowerStatus = 1<<0, + OverCurrentIndicator = 1<<1, + DeviceRemoteWakeupEnable = 1<<15, + LocalPowerStatusChange = 1<<16, + OverCurrentIndicatorChange = 1<<17, + ClearRemoteWakeupEnable = 1<<31 + } HcRhStatusReg; + + enum { + FrameInterval = 1<<0, + FSLargestDataPacket = 1<<16, + FrameIntervalToggle = 1<<31 + } HcFmIntervalOffset; + enum { + FrameIntervalMask = MASK(0, 14), + FSLargestDataPacketMask = MASK(16, 15), + FrameIntervalToggleMask = MASK(31, 1) + } HcFmIntervalMask; + + enum { + ControlBulkServiceRatio = 1<<0, + PeriodicListEnable = 1<<2, + IsochronousEnable = 1<<3, + ControlListEnable = 1<<4, + BulkListEnable = 1<<5, + HostControllerFunctionalState = 1<<6, + InterruptRouting = 1<<8, + RemoteWakeupConnected = 1<<9, + RemoteWakeupEnable = 1<<10 + } HcControlReg; + + enum { + ControlBulkServiceRatioMask = MASK(0, 2), + HostControllerFunctionalStateMask = MASK(6, 2) + } HcControlMask; + + enum { + USBReset = 0*HostControllerFunctionalState, + USBResume = 1*HostControllerFunctionalState, + USBOperational = 2*HostControllerFunctionalState, + USBSuspend = 3*HostControllerFunctionalState + }; + + enum { + HostControllerReset = 1<<0, + ControlListFilled = 1<<1, + BulkListFilled = 1<<2, + OwnershipChangeRequest = 1<<3, + SchedulingOverrunCount = 1<<16 + } HcCommandStatusReg; + + enum { + SchedulingOverrunCountMask = MASK(16, 2) + } HcCommandStatusMask; + + enum { + FrameRemaining = 1<<0, + FrameRemainingToggle = 1<<31 + } HcFmRemainingReg; + + enum { + SchedulingOverrung = 1<<0, + WritebackDoneHead = 1<<1, + StartofFrame = 1<<2, + ResumeDetected = 1<<3, + UnrecoverableError = 1<<4, + FrameNumberOverflow = 1<<5, + RootHubStatusChange = 1<<6, + OwnershipChange = 1<<30 + } HcInterruptStatusReg; + + typedef struct { + // Control and Status Partition + volatile u32 HcRevision; + volatile u32 HcControl; + volatile u32 HcCommandStatus; + volatile u32 HcInterruptStatus; + volatile u32 HcInterruptEnable; + volatile u32 HcInterruptDisable; + + // Memory Pointer Partition + volatile u32 HcHCCA; + volatile u32 HcPeriodCurrentED; + volatile u32 HcControlHeadED; + volatile u32 HcControlCurrentED; + volatile u32 HcBulkHeadED; + volatile u32 HcBulkCurrentED; + volatile u32 HcDoneHead; + + // Frame Counter Partition + volatile u32 HcFmInterval; + volatile u32 HcFmRemaining; + volatile u32 HcFmNumber; + volatile u32 HcPeriodicStart; + volatile u32 HcLSThreshold; + + // Root Hub Partition + volatile u32 HcRhDescriptorA; + volatile u32 HcRhDescriptorB; + volatile u32 HcRhStatus; + /* all bits in HcRhPortStatus registers are R/WC, so + _DO NOT_ use |= to set the bits, + this clears the entire state */ + volatile u32 HcRhPortStatus[]; + } __attribute__ ((packed)) opreg_t; + + typedef struct { /* should be 256 bytes according to spec */ + u32 HccaInterruptTable[32]; + volatile u16 HccaFrameNumber; + volatile u16 HccaPad1; + volatile u32 HccaDoneHead; + u8 reserved[116]; /* pad according to spec */ + u8 what[4]; /* really pad to 256 as spec only covers 252 */ + } __attribute__ ((packed)) hcca_t; + + typedef volatile struct { + u32 config; + u32 tail_pointer; + u32 head_pointer; + u32 next_ed; + } __attribute__ ((packed)) ed_t; +#define ED_HALTED 1 +#define ED_TOGGLE 2 + +#define ED_FUNC_SHIFT 0 +#define ED_FUNC_MASK MASK(0, 7) +#define ED_EP_SHIFT 7 +#define ED_EP_MASK MASK(7, 4) +#define ED_DIR_SHIFT 11 +#define ED_DIR_MASK MASK(11, 2) +#define ED_LOWSPEED (1 << 13) +#define ED_MPS_SHIFT 16 + + typedef volatile struct { + u32 config; + u32 current_buffer_pointer; + u32 next_td; + u32 buffer_end; + } __attribute__ ((packed)) td_t; +/* + * Bits 0 through 17 of .config won't be interpreted by the host controller + * (HC) and, after processing the TD, the HC has to ensure those bits have + * the same state as before. So we are free to use those bits for our own + * purpose. + */ +#define TD_QUEUETYPE_SHIFT 0 +#define TD_QUEUETYPE_MASK MASK(TD_QUEUETYPE_SHIFT, 2) +#define TD_QUEUETYPE_ASYNC (0 << TD_QUEUETYPE_SHIFT) +#define TD_QUEUETYPE_INTR (1 << TD_QUEUETYPE_SHIFT) + +#define TD_DIRECTION_SHIFT 19 +#define TD_DIRECTION_MASK MASK(TD_DIRECTION_SHIFT, 2) +#define TD_DIRECTION_SETUP OHCI_SETUP << TD_DIRECTION_SHIFT +#define TD_DIRECTION_IN OHCI_IN << TD_DIRECTION_SHIFT +#define TD_DIRECTION_OUT OHCI_OUT << TD_DIRECTION_SHIFT +#define TD_DELAY_INTERRUPT_SHIFT 21 +#define TD_DELAY_INTERRUPT_MASK MASK(TD_DELAY_INTERRUPT_SHIFT, 3) +#define TD_DELAY_INTERRUPT_ZERO 0 +#define TD_DELAY_INTERRUPT_NOINTR (7 << TD_DELAY_INTERRUPT_SHIFT) +#define TD_TOGGLE_DATA0 0 +#define TD_TOGGLE_DATA1 (1 << 24) +#define TD_TOGGLE_FROM_ED 0 +#define TD_TOGGLE_FROM_TD (1 << 25) +#define TD_CC_SHIFT 28 +#define TD_CC_MASK MASK(TD_CC_SHIFT, 4) +#define TD_CC_NOERR 0 +#define TD_CC_NOACCESS (14 << TD_CC_SHIFT) /* the lower of the two values, so "no access" can be tested with >= */ + +#define OHCI_INST(controller) ((ohci_t*)((controller)->instance)) + + typedef struct ohci { + opreg_t *opreg; + hcca_t *hcca; + usbdev_t *roothub; + ed_t *periodic_ed; + } ohci_t; + + typedef enum { OHCI_SETUP=0, OHCI_OUT=1, OHCI_IN=2, OHCI_FROM_TD=3 } ohci_pid_t; + +#endif diff --git a/roms/openbios/drivers/usbohci_rh.c b/roms/openbios/drivers/usbohci_rh.c new file mode 100644 index 000000000..55503be61 --- /dev/null +++ b/roms/openbios/drivers/usbohci_rh.c @@ -0,0 +1,212 @@ +/* + * Driver for USB OHCI Root Hubs ported from CoreBoot + * + * Copyright (C) 2014 BALATON Zoltan + * + * This file was part of the libpayload project. + * + * Copyright (C) 2010 Patrick Georgi + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" +#include "timer.h" +#include "usbohci_private.h" +#include "usbohci.h" + +typedef struct { + int numports; + int *port; +} rh_inst_t; + +#define RH_INST(dev) ((rh_inst_t*)(dev)->data) + +static void +ohci_rh_enable_port (usbdev_t *dev, int port) +{ + /* Reset RH port should hold 50ms with pulses of at least 10ms and + * gaps of at most 3ms (usb20 spec 7.1.7.5). + * After reset, the port will be enabled automatically (ohci spec + * 7.4.4). + */ + int total_delay = 100; /* 100 * 500us == 50ms */ + while (total_delay > 0) { + if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) + & CurrentConnectStatus)) + return; + + /* start reset */ + OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = + __cpu_to_le32(SetPortReset); + int timeout = 200; /* timeout after 200 * 500us == 100ms */ + while ((READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) + & PortResetStatus) + && timeout--) { + udelay(500); total_delay--; + } + if (READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) + & PortResetStatus) { + usb_debug("Warning: root-hub port reset timed out.\n"); + break; + } + if ((200-timeout) < 20) { + usb_debug("Warning: port reset too short: %dms; " + "should be at least 10ms.\n", + (200-timeout)/2); + total_delay = 0; /* can happen on QEMU */ + } + /* clear reset status change */ + OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] = + __cpu_to_le32(PortResetStatusChange); + usb_debug ("rh port reset finished after %dms.\n", (200-timeout)/2); + } +} + +/* disable root hub */ +static void +ohci_rh_disable_port (usbdev_t *dev, int port) +{ + OHCI_INST (dev->controller)->opreg->HcRhPortStatus[port] = + __cpu_to_le32(ClearPortEnable); // disable port + int timeout = 50; /* timeout after 50 * 100us == 5ms */ + while ((READ_OPREG(OHCI_INST (dev->controller), HcRhPortStatus[port]) + & PortEnableStatus) + && timeout--) { + udelay(100); + } +} + +static void +ohci_rh_scanport (usbdev_t *dev, int port) +{ + if (port >= RH_INST(dev)->numports) { + usb_debug("Invalid port %d\n", port); + return; + } + + /* device registered, and device change logged, so something must have happened */ + if (RH_INST (dev)->port[port] != -1) { + usb_detach_device(dev->controller, RH_INST (dev)->port[port]); + RH_INST (dev)->port[port] = -1; + } + + /* no device attached + previously registered devices are detached, nothing left to do */ + if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & CurrentConnectStatus)) + return; + + // clear port state change + OHCI_INST(dev->controller)->opreg->HcRhPortStatus[port] = __cpu_to_le32(ConnectStatusChange); + ohci_rh_enable_port (dev, port); + + mdelay(100); // wait for signal to stabilize + + if (!(READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & PortEnableStatus)) { + usb_debug ("port enable failed\n"); + return; + } + + int speed = (READ_OPREG(OHCI_INST(dev->controller), HcRhPortStatus[port]) & LowSpeedDeviceAttached) != 0; + RH_INST (dev)->port[port] = usb_attach_device(dev->controller, dev->address, port, speed); +} + +static int +ohci_rh_report_port_changes (usbdev_t *dev) +{ + ohci_t *const ohcic = OHCI_INST (dev->controller); + + int i; + + for (i = 0; i < RH_INST(dev)->numports; i++) { + // maybe detach+attach happened between two scans? + if (READ_OPREG(ohcic, HcRhPortStatus[i]) & ConnectStatusChange) { + ohcic->opreg->HcRhPortStatus[i] = __cpu_to_le32(ConnectStatusChange); + usb_debug("attachment change on port %d\n", i); + return i; + } + } + + // no change + return -1; +} + +static void +ohci_rh_destroy (usbdev_t *dev) +{ + int i; + for (i = 0; i < RH_INST (dev)->numports; i++) + ohci_rh_disable_port (dev, i); + free (RH_INST (dev)); +} + +static void +ohci_rh_poll (usbdev_t *dev) +{ + ohci_t *const ohcic = OHCI_INST (dev->controller); + + int port; + + /* Check if anything changed. */ + if (!(READ_OPREG(ohcic, HcInterruptStatus) & RootHubStatusChange)) + return; + ohcic->opreg->HcInterruptStatus = __cpu_to_le32(RootHubStatusChange); + usb_debug("root hub status change\n"); + + /* Scan ports with changed connection status. */ + while ((port = ohci_rh_report_port_changes (dev)) != -1) + ohci_rh_scanport (dev, port); +} + +void +ohci_rh_init (usbdev_t *dev) +{ + int i; + + dev->destroy = ohci_rh_destroy; + dev->poll = ohci_rh_poll; + + dev->data = malloc (sizeof (rh_inst_t)); + if (!dev->data) { + printk("Not enough memory for OHCI RH.\n"); + return; + } + + RH_INST (dev)->numports = READ_OPREG(OHCI_INST(dev->controller), HcRhDescriptorA) & NumberDownstreamPortsMask; + RH_INST (dev)->port = malloc(sizeof(int) * RH_INST (dev)->numports); + usb_debug("%d ports registered\n", RH_INST (dev)->numports); + + for (i = 0; i < RH_INST (dev)->numports; i++) { + ohci_rh_enable_port (dev, i); + RH_INST (dev)->port[i] = -1; + } + + /* we can set them here because a root hub _really_ shouldn't + appear elsewhere */ + dev->address = 0; + dev->hub = -1; + dev->port = -1; + + usb_debug("rh init done\n"); +} diff --git a/roms/openbios/include/arch/common/fw_cfg.h b/roms/openbios/include/arch/common/fw_cfg.h index bd4808bbd..df44c2e89 100644 --- a/roms/openbios/include/arch/common/fw_cfg.h +++ b/roms/openbios/include/arch/common/fw_cfg.h @@ -46,6 +46,7 @@ #define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) #define FW_CFG_PPC_NVRAM_ADDR (FW_CFG_ARCH_LOCAL + 0x08) #define FW_CFG_PPC_BUSFREQ (FW_CFG_ARCH_LOCAL + 0x09) +#define FW_CFG_PPC_NVRAM_FLAT (FW_CFG_ARCH_LOCAL + 0x0a) #define FW_CFG_INVALID 0xffff diff --git a/roms/openbios/include/drivers/pci.h b/roms/openbios/include/drivers/pci.h index 1f0af6c9f..2eb5685d3 100644 --- a/roms/openbios/include/drivers/pci.h +++ b/roms/openbios/include/drivers/pci.h @@ -188,6 +188,7 @@ extern const pci_arch_t *arch; #define PCI_DEVICE_ID_APPLE_UNI_N_PCI 0x001f #define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 #define PCI_DEVICE_ID_APPLE_UNI_N_KEYL 0x0022 +#define PCI_DEVICE_ID_APPLE_KEYL_USB 0x003f #define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b #define PCI_VENDOR_ID_SUN 0x108e diff --git a/roms/openbios/include/drivers/usb.h b/roms/openbios/include/drivers/usb.h new file mode 100644 index 000000000..143ed27bc --- /dev/null +++ b/roms/openbios/include/drivers/usb.h @@ -0,0 +1,8 @@ +#ifndef USB_H +#define USB_H + +int ob_usb_ohci_init(const char *path, uint32_t addr); +void ob_usb_hid_add_keyboard(const char *path); +int usb_exit(void); + +#endif /* USB_H */ diff --git a/roms/openbios/libopenbios/ofmem_common.c b/roms/openbios/libopenbios/ofmem_common.c index 157ca8741..3b8ca152f 100644 --- a/roms/openbios/libopenbios/ofmem_common.c +++ b/roms/openbios/libopenbios/ofmem_common.c @@ -634,8 +634,8 @@ ucell ofmem_claim( ucell addr, ucell size, ucell align ) } else { if( align < PAGE_SIZE ) align = PAGE_SIZE; - phys = ofmem_claim_phys_( addr, size, align, 0, ofmem_arch_get_phys_top(), 1 /* reverse */ ); - virt = ofmem_claim_virt_( addr, size, align, 0, get_ram_size(), 1 /* reverse */ ); + phys = ofmem_claim_phys_( -1, size, align, 0, ofmem_arch_get_phys_top(), 1 /* reverse */ ); + virt = ofmem_claim_virt_( phys, size, 0, 0, 0, 0 ); if( phys == -1 || virt == -1 ) { OFMEM_TRACE("ofmem_claim failed\n"); return -1; diff --git a/roms/seabios/.version b/roms/seabios/.version index 88fc6a6d5..0bd308b59 100644 --- a/roms/seabios/.version +++ b/roms/seabios/.version @@ -1 +1 @@ -rel-1.7.5-0-ge51488c +rel-1.7.5.1-0-g8936dbb diff --git a/roms/seabios/Makefile b/roms/seabios/Makefile index 78b598eee..7c2b33c3a 100644 --- a/roms/seabios/Makefile +++ b/roms/seabios/Makefile @@ -112,8 +112,8 @@ endif # Do a whole file compile by textually including all C code. define whole-compile @echo " Compiling whole program $3" -$(Q)printf '$(foreach i,$2,#include "$(CURDIR)/$i"\n)' > $3.tmp.c -$(Q)$(CC) $1 $(CFLAGSWHOLE) -c $3.tmp.c -o $3 +$(Q)printf '$(foreach i,$2,#include "$i"\n)' > $3.tmp.c +$(Q)$(CC) -I. $1 $(CFLAGSWHOLE) -c $3.tmp.c -o $3 endef %.strip.o: %.o diff --git a/roms/seabios/src/boot.c b/roms/seabios/src/boot.c index 133e2063f..97de89cfa 100644 --- a/roms/seabios/src/boot.c +++ b/roms/seabios/src/boot.c @@ -145,7 +145,7 @@ int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun) // Find scsi drive - for example: /pci@i0cf8/scsi@5/channel@0/disk@1,0 char desc[256], *p; p = build_pci_path(desc, sizeof(desc), "*", pci); - snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%d,%d", target, lun); + snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%x,%x", target, lun); return find_prio(desc); } @@ -189,7 +189,7 @@ int bootprio_find_pci_rom(struct pci_device *pci, int instance) char desc[256], *p; p = build_pci_path(desc, sizeof(desc), "*", pci); if (instance) - snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance); + snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance); return find_prio(desc); } @@ -201,7 +201,7 @@ int bootprio_find_named_rom(const char *name, int instance) char desc[256], *p; p = desc + snprintf(desc, sizeof(desc), "/rom@%s", name); if (instance) - snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance); + snprintf(p, desc+sizeof(desc)-p, ":rom%x", instance); return find_prio(desc); } @@ -224,7 +224,7 @@ int bootprio_find_usb(struct usbdevice_s *usbdev, int lun) char desc[256], *p; p = build_pci_path(desc, sizeof(desc), "usb", usbdev->hub->cntl->pci); p = build_usb_path(p, desc+sizeof(desc)-p, usbdev->hub); - snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%d" + snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%x" , usbdev->port+1, lun); int ret = find_prio(desc); if (ret >= 0) diff --git a/roms/seabios/src/fw/pciinit.c b/roms/seabios/src/fw/pciinit.c index 2e6382f98..0e5d51b91 100644 --- a/roms/seabios/src/fw/pciinit.c +++ b/roms/seabios/src/fw/pciinit.c @@ -635,6 +635,36 @@ pci_region_create_entry(struct pci_bus *bus, struct pci_device *dev, return entry; } +static int pci_bus_hotplug_support(struct pci_bus *bus) +{ + u8 pcie_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_EXP); + u8 shpc_cap; + + if (pcie_cap) { + u16 pcie_flags = pci_config_readw(bus->bus_dev->bdf, + pcie_cap + PCI_EXP_FLAGS); + u8 port_type = ((pcie_flags & PCI_EXP_FLAGS_TYPE) >> + (__builtin_ffs(PCI_EXP_FLAGS_TYPE) - 1)); + u8 downstream_port = (port_type == PCI_EXP_TYPE_DOWNSTREAM) || + (port_type == PCI_EXP_TYPE_ROOT_PORT); + /* + * PCI Express SPEC, 7.8.2: + * Slot Implemented – When Set, this bit indicates that the Link + * HwInit associated with this Port is connected to a slot (as + * compared to being connected to a system-integrated device or + * being disabled). + * This bit is valid for Downstream Ports. This bit is undefined + * for Upstream Ports. + */ + u16 slot_implemented = pcie_flags & PCI_EXP_FLAGS_SLOT; + + return downstream_port && slot_implemented; + } + + shpc_cap = pci_find_capability(bus->bus_dev, PCI_CAP_ID_SHPC); + return !!shpc_cap; +} + static int pci_bios_check_devices(struct pci_bus *busses) { dprintf(1, "PCI: check devices\n"); @@ -677,7 +707,7 @@ static int pci_bios_check_devices(struct pci_bus *busses) continue; struct pci_bus *parent = &busses[pci_bdf_to_bus(s->bus_dev->bdf)]; int type; - u8 shpc_cap = pci_find_capability(s->bus_dev, PCI_CAP_ID_SHPC); + int hotplug_support = pci_bus_hotplug_support(s); for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) { u64 align = (type == PCI_REGION_TYPE_IO) ? PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN; @@ -686,7 +716,7 @@ static int pci_bios_check_devices(struct pci_bus *busses) if (pci_region_align(&s->r[type]) > align) align = pci_region_align(&s->r[type]); u64 sum = pci_region_sum(&s->r[type]); - if (!sum && shpc_cap) + if (!sum && hotplug_support) sum = align; /* reserve min size for hot-plug */ u64 size = ALIGN(sum, align); int is64 = pci_bios_bridge_region_is64(&s->r[type], diff --git a/roms/seabios/src/hw/megasas.c b/roms/seabios/src/hw/megasas.c index a5dc14fcf..b2a65e48b 100644 --- a/roms/seabios/src/hw/megasas.c +++ b/roms/seabios/src/hw/megasas.c @@ -357,6 +357,10 @@ init_megasas(struct pci_device *pci) u32 iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_2) & PCI_BASE_ADDRESS_IO_MASK; + if (!iobase) + iobase = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) + & PCI_BASE_ADDRESS_IO_MASK; + dprintf(1, "found MegaRAID SAS at %02x:%02x.%x, io @ %x\n", pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf), iobase); diff --git a/roms/seabios/src/hw/usb-ehci.c b/roms/seabios/src/hw/usb-ehci.c index 9d9427b6c..17a7e8b28 100644 --- a/roms/seabios/src/hw/usb-ehci.c +++ b/roms/seabios/src/hw/usb-ehci.c @@ -372,7 +372,7 @@ ehci_desc2pipe(struct ehci_pipe *pipe, struct usbdevice_s *usbdev struct ehci_pipe *hpipe = container_of( hubdev->defpipe, struct ehci_pipe, pipe); if (hpipe->pipe.speed == USB_HIGHSPEED) - pipe->qh.info2 |= ((usbdev->port << QH_HUBPORT_SHIFT) + pipe->qh.info2 |= (((usbdev->port+1) << QH_HUBPORT_SHIFT) | (hpipe->pipe.devaddr << QH_HUBADDR_SHIFT)); else pipe->qh.info2 = hpipe->qh.info2; diff --git a/roms/seabios/src/stacks.c b/roms/seabios/src/stacks.c index 6bcb31940..beccc0fa6 100644 --- a/roms/seabios/src/stacks.c +++ b/roms/seabios/src/stacks.c @@ -287,7 +287,7 @@ thread_init(void) int threads_during_optionroms(void) { - return CONFIG_THREADS && ThreadControl == 2; + return CONFIG_THREADS && ThreadControl == 2 && in_post(); } // Switch to next thread stack. diff --git a/roms/seabios/vgasrc/vgabios.c b/roms/seabios/vgasrc/vgabios.c index 400e29675..e87b7ebff 100644 --- a/roms/seabios/vgasrc/vgabios.c +++ b/roms/seabios/vgasrc/vgabios.c @@ -891,15 +891,15 @@ handle_1011(struct bregs *regs) { if (CONFIG_VGA_STDVGA_PORTS) { switch (regs->al) { - case 0x00: handle_101100(regs); break; - case 0x01: handle_101101(regs); break; - case 0x02: handle_101102(regs); break; - case 0x03: handle_101103(regs); break; - case 0x04: handle_101104(regs); break; - case 0x10: handle_101110(regs); break; - case 0x11: handle_101111(regs); break; - case 0x12: handle_101112(regs); break; - case 0x14: handle_101114(regs); break; + case 0x00: handle_101100(regs); return; + case 0x01: handle_101101(regs); return; + case 0x02: handle_101102(regs); return; + case 0x03: handle_101103(regs); return; + case 0x04: handle_101104(regs); return; + case 0x10: handle_101110(regs); return; + case 0x11: handle_101111(regs); return; + case 0x12: handle_101112(regs); return; + case 0x14: handle_101114(regs); return; } } switch (regs->al) { |