This driver is ported from CoreBoot's libpayload and fixed to work on big endian CPUs (tested on PPC with QEMU). It is enough to support a USB keyboard on an Apple Keylargo OHCI compliant USB host and makes OpenBIOS usable on qemu-system-ppc64 with mac99 machine type as well as allows to emulate PowerMac3,1 better.
Signed-off-by: BALATON Zoltan balaton@eik.bme.hu --- openbios-devel/config/examples/ppc_config.xml | 3 + openbios-devel/drivers/Kconfig | 20 + openbios-devel/drivers/build.xml | 4 + openbios-devel/drivers/pci.c | 18 + openbios-devel/drivers/pci.h | 1 + openbios-devel/drivers/pci_database.c | 18 +- openbios-devel/drivers/pci_database.h | 1 + openbios-devel/drivers/usb.c | 587 ++++++++++++++++ openbios-devel/drivers/usb.h | 357 ++++++++++ openbios-devel/drivers/usbhid.c | 579 ++++++++++++++++ openbios-devel/drivers/usbohci.c | 920 ++++++++++++++++++++++++++ openbios-devel/drivers/usbohci.h | 45 ++ openbios-devel/drivers/usbohci_private.h | 270 ++++++++ openbios-devel/drivers/usbohci_rh.c | 212 ++++++ openbios-devel/include/drivers/pci.h | 1 + openbios-devel/include/drivers/usb.h | 8 + 16 files changed, 3043 insertions(+), 1 deletion(-) create mode 100644 openbios-devel/drivers/usb.c create mode 100644 openbios-devel/drivers/usb.h create mode 100644 openbios-devel/drivers/usbhid.c create mode 100644 openbios-devel/drivers/usbohci.c create mode 100644 openbios-devel/drivers/usbohci.h create mode 100644 openbios-devel/drivers/usbohci_private.h create mode 100644 openbios-devel/drivers/usbohci_rh.c create mode 100644 openbios-devel/include/drivers/usb.h
diff --git a/openbios-devel/config/examples/ppc_config.xml b/openbios-devel/config/examples/ppc_config.xml index 621b65d..4c14eb6 100644 --- a/openbios-devel/config/examples/ppc_config.xml +++ b/openbios-devel/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/openbios-devel/drivers/Kconfig b/openbios-devel/drivers/Kconfig index 7be334b..ce7e7ac 100644 --- a/openbios-devel/drivers/Kconfig +++ b/openbios-devel/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/openbios-devel/drivers/build.xml b/openbios-devel/drivers/build.xml index edec6b5..bd1abd3 100644 --- a/openbios-devel/drivers/build.xml +++ b/openbios-devel/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/openbios-devel/drivers/pci.c b/openbios-devel/drivers/pci.c index ca92d63..ca4de87 100644 --- a/openbios-devel/drivers/pci.c +++ b/openbios-devel/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__) @@ -883,6 +886,21 @@ 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 + pci_addr addr = 0x80000000u | config->dev; + uint16_t cmd; + + cmd = pci_config_read16(addr, PCI_COMMAND); + cmd |= PCI_COMMAND_BUS_MASTER; + pci_config_write16(addr, PCI_COMMAND, cmd); + + ob_usb_ohci_init(config->path, config->assigned[0] & ~0x0000000F); +#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) diff --git a/openbios-devel/drivers/pci.h b/openbios-devel/drivers/pci.h index ab8f184..84a2b2c 100644 --- a/openbios-devel/drivers/pci.h +++ b/openbios-devel/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/openbios-devel/drivers/pci_database.c b/openbios-devel/drivers/pci_database.c index 0bd854a..3b670d3 100644 --- a/openbios-devel/drivers/pci_database.c +++ b/openbios-devel/drivers/pci_database.c @@ -845,6 +845,22 @@ static const pci_subclass_t cpu_subclass[] = { }, };
+static const pci_dev_t usb_devices[] = { + { + PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_KEYL_USB, + "usb", "usb", NULL, + "pci106b,3f\0pciclass,0c0310\0", + 1, 0, 0, + NULL, NULL, + }, + { + 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 +868,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/openbios-devel/drivers/pci_database.h b/openbios-devel/drivers/pci_database.h index 16b385b..7f053d8 100644 --- a/openbios-devel/drivers/pci_database.h +++ b/openbios-devel/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/openbios-devel/drivers/usb.c b/openbios-devel/drivers/usb.c new file mode 100644 index 0000000..c6e3717 --- /dev/null +++ b/openbios-devel/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/openbios-devel/drivers/usb.h b/openbios-devel/drivers/usb.h new file mode 100644 index 0000000..2e23a13 --- /dev/null +++ b/openbios-devel/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/openbios-devel/drivers/usbhid.c b/openbios-devel/drivers/usbhid.c new file mode 100644 index 0000000..a423278 --- /dev/null +++ b/openbios-devel/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/openbios-devel/drivers/usbohci.c b/openbios-devel/drivers/usbohci.c new file mode 100644 index 0000000..58c1423 --- /dev/null +++ b/openbios-devel/drivers/usbohci.c @@ -0,0 +1,920 @@ +/* + * 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/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; +} + +#ifdef CONFIG_USB_PCI +hci_t * +ohci_pci_init (pcidev_t addr) +{ + u32 reg_base; + + /* 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_read_config32 (addr, 0x10) & 0xfffff000; + + return ohci_init((void *)(unsigned long)reg_base); +} +#endif + +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); + 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, phys_addr_t addr) +{ + hci_t *ctrl; + int i; + + usb_debug("ohci_init: %s addr = %x\n", path, addr); + ctrl = ohci_init((void *)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/openbios-devel/drivers/usbohci.h b/openbios-devel/drivers/usbohci.h new file mode 100644 index 0000000..6903328 --- /dev/null +++ b/openbios-devel/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/openbios-devel/drivers/usbohci_private.h b/openbios-devel/drivers/usbohci_private.h new file mode 100644 index 0000000..b3a723e --- /dev/null +++ b/openbios-devel/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/openbios-devel/drivers/usbohci_rh.c b/openbios-devel/drivers/usbohci_rh.c new file mode 100644 index 0000000..55503be --- /dev/null +++ b/openbios-devel/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/openbios-devel/include/drivers/pci.h b/openbios-devel/include/drivers/pci.h index 1f0af6c..2eb5685 100644 --- a/openbios-devel/include/drivers/pci.h +++ b/openbios-devel/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/openbios-devel/include/drivers/usb.h b/openbios-devel/include/drivers/usb.h new file mode 100644 index 0000000..d2d94d2 --- /dev/null +++ b/openbios-devel/include/drivers/usb.h @@ -0,0 +1,8 @@ +#ifndef USB_H +#define USB_H + +int ob_usb_ohci_init(const char *path, phys_addr_t addr); +void ob_usb_hid_add_keyboard(const char *path); +int usb_exit(void); + +#endif /* USB_H */