[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cdi-devel] [PATCH 4/6] UHCI



From: Max Reitz <max@xxxxxxxxxx>

+ Added USB universal host controller driver

Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
 uhci/hcd.c                   |  215 ++++++++++++++++++++++++++++++++++++
 uhci/include/uhci-hcd.h      |   39 +++++++
 uhci/include/uhci-init.h     |   31 +++++
 uhci/include/uhci-transfer.h |   30 +++++
 uhci/include/uhci.h          |  138 +++++++++++++++++++++++
 uhci/init.c                  |  249 ++++++++++++++++++++++++++++++++++++++++++
 uhci/main.c                  |   34 ++++++
 uhci/transfer.c              |   91 +++++++++++++++
 8 files changed, 827 insertions(+), 0 deletions(-)
 create mode 100644 uhci/hcd.c
 create mode 100644 uhci/include/uhci-hcd.h
 create mode 100644 uhci/include/uhci-init.h
 create mode 100644 uhci/include/uhci-transfer.h
 create mode 100644 uhci/include/uhci.h
 create mode 100644 uhci/init.c
 create mode 100644 uhci/main.c
 create mode 100644 uhci/transfer.c

diff --git a/uhci/hcd.c b/uhci/hcd.c
new file mode 100644
index 0000000..bda4715
--- /dev/null
+++ b/uhci/hcd.c
@@ -0,0 +1,215 @@
+/*****************************************************************************
+* Copyright (c) 2009 Max Reitz                                               *
+*                                                                            *
+* Permission is hereby granted,  free of charge,  to any  person obtaining a *
+* copy of this software and associated documentation files (the "Software"), *
+* to deal in the Software without restriction,  including without limitation *
+* the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+* and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+* Software is furnished to do so, subject to the following conditions:       *
+*                                                                            *
+* The above copyright notice and this permission notice shall be included in *
+* all copies or substantial portions of the Software.                        *
+*                                                                            *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+* IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+* FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+* LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+* FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+* DEALINGS IN THE SOFTWARE.                                                  *
+*****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cdi/io.h>
+#include <cdi/lists.h>
+#include <cdi/mempool.h>
+#include <cdi/usb.h>
+#include <cdi/usb-hcd.h>
+
+#include "uhci.h"
+#include "uhci-hcd.h"
+
+extern cdi_list_t uhci_active_transfers;
+
+static void reset_device(struct cdi_usb_device *device);
+
+static void find_devices(struct cdi_usb_hc *device)
+{
+    struct uhci *uhci = (struct uhci *)device;
+    struct cdi_usb_device *dev;
+    int i;
+
+    d0printf("Scanning root ports\n");
+    cdi_outw(uhci->pbase + UHCI_USBCMD, USB_RUN);
+    for (i = 0; i < uhci->root_ports; i++) {
+        if (!(cdi_inw(uhci->pbase + UHCI_RPORTS + 2 * i) & RPORT_DEVICE)) {
+            continue;
+        }
+        dev = malloc(sizeof(struct cdi_usb_device));
+        dev->hc = device;
+        dev->id = 0;
+        dev->port = i;
+        dev->speed = (cdi_inw(uhci->pbase + UHCI_RPORTS + 2 * i) &
+                RPORT_LOSPD) ? CDI_USB_LOW_SPEED : CDI_USB_FULL_SPEED;
+        dev->hub = NULL;
+        dev->reset = &reset_device;
+        cdi_usb_device_init(dev);
+    }
+}
+
+static void activate_device(struct cdi_usb_device *device)
+{
+    struct uhci *uhci = (struct uhci *)device->hc;
+
+    if (device->hub != NULL) {
+        return;
+    }
+
+    d1printf("Activating device on port %i.\n", device->port);
+    cdi_outw(uhci->pbase + UHCI_RPORTS + 2 * device->port,
+             RPORT_ENABLE | RPORT_CSC);
+    cdi_sleep_ms(20);
+}
+
+static void reset_device(struct cdi_usb_device *device)
+{
+    struct uhci *uhci = (struct uhci *)device->hc;
+
+    if (device->hub != NULL) {
+        return;
+    }
+
+    d1printf("Resetting device on port %i.\n", device->port);
+    cdi_outw(uhci->pbase + UHCI_RPORTS + 2 * device->port,
+             RPORT_ENABLE | RPORT_RESET | RPORT_CSC);
+    cdi_sleep_ms(20);
+}
+
+static void add_pipe(struct cdi_usb_pipe *pipe)
+{
+    //Macht nix, weil das bei UHCI nicht nötig ist
+    pipe = NULL; //Aber GCC können wir trotzdem glücklich machen
+}
+
+static inline int _tsl(volatile int *variable)
+{
+    int rval;
+    rval = *variable;
+    *variable = 1;
+    return rval;
+}
+
+static volatile int locked = 0;
+
+static cdi_usb_status_t do_packet(struct cdi_usb_packet *packet)
+{
+    struct cdi_usb_device *usbdev = packet->pipe->device;
+    struct uhci *uhci = (struct uhci *)usbdev->hc;
+    struct uhci_td td;
+    struct transfer addr;
+    int timeout, frame;
+
+    void *buf;
+    uintptr_t pbuf;
+
+    memset(&td, 0, sizeof(td));
+    td.next = 1; //Invalid
+    td.active = 1;
+    td.ioc = 1;
+    if (packet->use_toggle == TOGGLE_0) {
+        packet->pipe->data_toggle = 0;
+    } else if (packet->use_toggle == TOGGLE_1) {
+        packet->pipe->data_toggle = 1;
+    }
+    td.data_toggle = packet->pipe->data_toggle;
+    packet->pipe->data_toggle ^= 1;
+    td.low_speed = !!(usbdev->speed == CDI_USB_LOW_SPEED);
+    td.errors = 1;
+    td.pid = packet->type;
+    td.device = usbdev->id;
+    td.endpoint = packet->pipe->endpoint->endpoint_address & 0x07;
+    td.maxlen = packet->length ? packet->length - 1 : 0x7FF;
+    while (_tsl(&locked));
+    frame = (cdi_inw(uhci->pbase + UHCI_FRNUM) + 5) & 0x3FF;
+    while (!(uhci->queue_heads[frame].transfer & 1)) {
+        frame++;
+        frame &= 0x3FF;
+    }
+
+    if (mempool_get(uhci->buffers, &buf, &pbuf) < 0) {
+        return CDI_USB_TRIVIAL_ERROR;
+    }
+
+    addr.virt = buf;
+    addr.phys = pbuf;
+    addr.error = 0xFFFF;
+
+    buf = (void *)((uintptr_t)buf + sizeof(td));
+    pbuf += sizeof(td);
+
+    if (!packet->length) {
+        td.buffer = 0;
+    } else {
+        td.buffer = pbuf;
+        if (packet->type != CDI_USB_PACKET_IN) {
+            memcpy(buf, packet->data, packet->length);
+        }
+    }
+
+    memcpy(addr.virt, &td, sizeof(td));
+
+
+    if (uhci_active_transfers == NULL) {
+        uhci_active_transfers = cdi_list_create();
+    }
+    uhci->queue_heads[frame].transfer = addr.phys;
+    cdi_list_push(uhci_active_transfers, &addr);
+
+    locked = 0;
+    for (timeout = 0; !(uhci->queue_heads[frame].transfer & 1) &&
+         (timeout < 1000); timeout++)
+    {
+        cdi_sleep_ms(1);
+    }
+    while ((timeout < 1000) && (addr.error == 0xFFFF)) {
+#ifndef CDI_STANDALONE
+        __asm__ __volatile__ ("hlt");
+#endif
+    }
+    if (packet->type == CDI_USB_PACKET_IN) {
+        memcpy(packet->data, buf, packet->length);
+    }
+    if (addr.error == 0xFFFF) {
+        addr.error = CDI_USB_TIMEOUT;
+    }
+
+    mempool_put(uhci->buffers, addr.virt);
+
+    return addr.error;
+}
+
+static cdi_usb_status_t do_packets(struct cdi_usb_packet *packets,
+    size_t num_packets)
+{
+    size_t i;
+    cdi_usb_status_t cond = CDI_USB_NO_ERROR;
+
+    //TODO
+    for (i = 0; (i < num_packets) && !cond; i++) {
+        packets[i].condition = do_packet(&packets[i]);
+        cond |= packets[i].condition;
+    }
+
+    return cond;
+}
+
+
+const struct uhci_functions uhci_functions = {
+    .find_devices = &find_devices,
+    .activate_device = &activate_device,
+    .send_packets = &do_packets,
+    .add_pipe = &add_pipe
+};
diff --git a/uhci/include/uhci-hcd.h b/uhci/include/uhci-hcd.h
new file mode 100644
index 0000000..46e4093
--- /dev/null
+++ b/uhci/include/uhci-hcd.h
@@ -0,0 +1,39 @@
+/*****************************************************************************
+* Copyright (c) 2009 Max Reitz                                               *
+*                                                                            *
+* Permission is hereby granted,  free of charge,  to any  person obtaining a *
+* copy of this software and associated documentation files (the "Software"), *
+* to deal in the Software without restriction,  including without limitation *
+* the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+* and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+* Software is furnished to do so, subject to the following conditions:       *
+*                                                                            *
+* The above copyright notice and this permission notice shall be included in *
+* all copies or substantial portions of the Software.                        *
+*                                                                            *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+* IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+* FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+* LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+* FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+* DEALINGS IN THE SOFTWARE.                                                  *
+*****************************************************************************/
+
+#ifndef UHCI_HCD_H
+#define UHCI_HCD_H
+
+#include <cdi/usb.h>
+#include <cdi/usb-hcd.h>
+
+struct uhci_functions {
+    void (*find_devices)(struct cdi_usb_hc *device);
+    void (*activate_device)(struct cdi_usb_device *device);
+    cdi_usb_status_t (*send_packets)(struct cdi_usb_packet *packets,
+        size_t num_packets);
+    void (*add_pipe)(struct cdi_usb_pipe *pipe);
+};
+
+cdi_list_t uhci_find_devices(struct cdi_usb_hc *device);
+
+#endif
diff --git a/uhci/include/uhci-init.h b/uhci/include/uhci-init.h
new file mode 100644
index 0000000..7b357bc
--- /dev/null
+++ b/uhci/include/uhci-init.h
@@ -0,0 +1,31 @@
+/*****************************************************************************
+* Copyright (c) 2009 Max Reitz                                               *
+*                                                                            *
+* Permission is hereby granted,  free of charge,  to any  person obtaining a *
+* copy of this software and associated documentation files (the "Software"), *
+* to deal in the Software without restriction,  including without limitation *
+* the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+* and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+* Software is furnished to do so, subject to the following conditions:       *
+*                                                                            *
+* The above copyright notice and this permission notice shall be included in *
+* all copies or substantial portions of the Software.                        *
+*                                                                            *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+* IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+* FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+* LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+* FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+* DEALINGS IN THE SOFTWARE.                                                  *
+*****************************************************************************/
+
+#ifndef UHCI_INIT_H
+#define UHCI_INIT_H
+
+#include "uhci.h"
+
+void find_and_init_uhcis(void);
+void uhci_init(struct cdi_device *dev);
+
+#endif
diff --git a/uhci/include/uhci-transfer.h b/uhci/include/uhci-transfer.h
new file mode 100644
index 0000000..53b46b0
--- /dev/null
+++ b/uhci/include/uhci-transfer.h
@@ -0,0 +1,30 @@
+/*****************************************************************************
+* Copyright (c) 2009 Max Reitz                                               *
+*                                                                            *
+* Permission is hereby granted,  free of charge,  to any  person obtaining a *
+* copy of this software and associated documentation files (the "Software"), *
+* to deal in the Software without restriction,  including without limitation *
+* the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+* and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+* Software is furnished to do so, subject to the following conditions:       *
+*                                                                            *
+* The above copyright notice and this permission notice shall be included in *
+* all copies or substantial portions of the Software.                        *
+*                                                                            *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+* IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+* FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+* LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+* FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+* DEALINGS IN THE SOFTWARE.                                                  *
+*****************************************************************************/
+
+#ifndef UHCI_TRANSFER_H
+#define UHCI_TRANSFER_H
+
+#include <cdi/misc.h>
+
+void uhci_handler(struct cdi_device *cdi_hci);
+
+#endif
diff --git a/uhci/include/uhci.h b/uhci/include/uhci.h
new file mode 100644
index 0000000..678b2a1
--- /dev/null
+++ b/uhci/include/uhci.h
@@ -0,0 +1,138 @@
+/*****************************************************************************
+* Copyright (c) 2009 Max Reitz                                               *
+*                                                                            *
+* Permission is hereby granted,  free of charge,  to any  person obtaining a *
+* copy of this software and associated documentation files (the "Software"), *
+* to deal in the Software without restriction,  including without limitation *
+* the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+* and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+* Software is furnished to do so, subject to the following conditions:       *
+*                                                                            *
+* The above copyright notice and this permission notice shall be included in *
+* all copies or substantial portions of the Software.                        *
+*                                                                            *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+* IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+* FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+* LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+* FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+* DEALINGS IN THE SOFTWARE.                                                  *
+*****************************************************************************/
+
+#ifndef UHCI_H
+#define UHCI_H
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <cdi/mempool.h>
+#include <cdi/misc.h>
+#include <cdi/pci.h>
+#include <cdi/usb-hcd.h>
+
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define d1printf(...) printf("[uhci] " __VA_ARGS__)
+#define _d1printf(...) printf(__VA_ARGS__)
+#else
+#define d1printf(...)
+#define _d1printf(...)
+#endif
+
+#define d0printf(...) printf("[uhci] " __VA_ARGS__)
+#define _d0printf(...) printf(__VA_ARGS__)
+
+
+#define UHCI_USBCMD    0x00 //w
+#define UHCI_USBSTS    0x02 //w
+#define UHCI_USBINTR   0x04 //w
+#define UHCI_FRNUM     0x06 //w
+#define UHCI_FRBASEADD 0x08 //l
+#define UHCI_SOFMOD    0x0C //b
+#define UHCI_RPORTS    0x10
+#define UHCI_PORTSC1   0x10 //w
+#define UHCI_PORTSC2   0x12 //w
+
+#define MAXP           0x0080
+#define CONFIGURE      0x0040
+#define GLOB_SUSP_MODE 0x0008
+#define GRESET         0x0004
+#define HCRESET        0x0002
+#define USB_RUN        0x0001
+
+#define RPORT_RESET    0x0200
+#define RPORT_LOSPD    0x0100 //Low speed device attached
+#define RPORT_ENABLE   0x0004
+#define RPORT_CSC      0x0002 //Connect status change
+#define RPORT_DEVICE   0x0001
+
+//Legacy support
+#define UHCI_PCI_LEGSUP 0xC0 //w
+
+#define UHCI_LEGSUP_STATUS 0x8F00 //Statusbits, die mittels schreiben einer 1
+                                  //gelöscht werden
+#define UHCI_LEGSUP_NO_CHG 0x5040 //Bits, die nicht verändert werden können
+                                  //(RO bzw. reserviert)
+#define UHCI_LEGSUP_PIRQ   0x2000 //Interrupt wird als PCI-IRQ ausgeführt
+
+
+struct uhci_qh {
+    volatile uint32_t next;
+    volatile uint32_t transfer;
+    uint32_t align[2]; //Fürs korrekte Alignment (an 16 Bytes)
+} __attribute__((packed));
+
+struct uhci_td {
+    volatile uint32_t next;
+
+    unsigned trans_len : 11;
+    unsigned rsvd0 : 6;
+    unsigned bitstuff_err : 1;
+    unsigned crc_time_err : 1;
+    unsigned nak : 1;
+    unsigned babble : 1;
+    unsigned buf_err : 1;
+    unsigned stalled_err : 1;
+    unsigned active : 1;
+    unsigned ioc : 1;
+    unsigned isochronous : 1;
+    unsigned low_speed : 1;
+    unsigned errors : 2;
+    unsigned spd : 1;
+    unsigned rsvd1 : 2;
+
+    unsigned pid : 8;
+    unsigned device : 7;
+    unsigned endpoint : 4;
+    unsigned data_toggle : 1;
+    unsigned rsvd2 : 1;
+    unsigned maxlen : 11;
+
+    uint32_t buffer;
+
+    uint32_t user[4];
+} __attribute__((packed));
+
+struct transfer {
+    void *virt;
+    uintptr_t phys;
+    volatile int error;
+};
+
+struct uhci {
+    struct cdi_usb_hc cdidev;
+    struct cdi_pci_device *pcidev;
+    int pbase;
+    uintptr_t phys_frame_list;
+    uint32_t *frame_list;
+    int root_ports;
+    struct uhci_qh *queue_heads;
+    struct uhci_qh *phys_queue_heads;
+
+    struct mempool *buffers;
+};
+
+#endif
diff --git a/uhci/init.c b/uhci/init.c
new file mode 100644
index 0000000..4405e01
--- /dev/null
+++ b/uhci/init.c
@@ -0,0 +1,249 @@
+/*****************************************************************************
+* Copyright (c) 2009 Max Reitz                                               *
+*                                                                            *
+* Permission is hereby granted,  free of charge,  to any  person obtaining a *
+* copy of this software and associated documentation files (the "Software"), *
+* to deal in the Software without restriction,  including without limitation *
+* the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+* and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+* Software is furnished to do so, subject to the following conditions:       *
+*                                                                            *
+* The above copyright notice and this permission notice shall be included in *
+* all copies or substantial portions of the Software.                        *
+*                                                                            *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+* IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+* FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+* LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+* FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+* DEALINGS IN THE SOFTWARE.                                                  *
+*****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cdi/io.h>
+#include <cdi/lists.h>
+#include <cdi/mempool.h>
+#include <cdi/misc.h>
+#include <cdi/pci.h>
+#include <cdi/usb-hcd.h>
+
+#include "uhci.h"
+#include "uhci-hcd.h"
+#include "uhci-init.h"
+#include "uhci-transfer.h"
+
+extern const struct uhci_functions uhci_functions;
+extern int init_uhci(void);
+
+#define DRIVER_NAME "uhcd"
+
+static struct cdi_usb_hcd driver = {
+    .drv = {
+        .type          = CDI_USB_HCD,
+        .name          = DRIVER_NAME,
+        .devices       = NULL,
+        .init_device   = &uhci_init,
+        .remove_device = NULL,
+        .init          = &init_uhci,
+        .destroy       = NULL
+    }
+};
+
+CDI_DRIVER(DRIVER_NAME, driver)
+
+void find_and_init_uhcis(void)
+{
+    struct uhci *hci;
+    char *name;
+    int uhci_counter = 0;
+    struct cdi_pci_device *dev;
+
+    cdi_usb_hcd_init(&driver);
+
+    cdi_list_t pci_devices = cdi_list_create();
+    cdi_pci_get_all_devices(pci_devices);
+    for (int i = 0; (dev = cdi_list_get(pci_devices, i)) != NULL; i++) {
+        if ((dev->class_id != 0x0C) || (dev->subclass_id != 0x03)
+            || (dev->interface_id != 0x00))
+        {
+            cdi_pci_device_destroy(dev);
+        } else {
+            hci = malloc(sizeof(struct uhci));
+            hci->cdidev.dev.type = CDI_USB_HCD;
+            name = malloc(7);
+            sprintf(name, "uhci%i", uhci_counter++);
+            hci->cdidev.dev.name = (const char *)name;
+            hci->cdidev.speed = CDI_USB_LOW_SPEED | CDI_USB_FULL_SPEED;
+            hci->pcidev = dev;
+            cdi_list_push(driver.drv.devices, hci);
+        }
+    }
+
+    cdi_list_destroy(pci_devices);
+
+    driver.find_devices = uhci_functions.find_devices;
+    driver.activate_device = uhci_functions.activate_device;
+    driver.send_packets = uhci_functions.send_packets;
+    driver.add_pipe = uhci_functions.add_pipe;
+}
+
+void uhci_init(struct cdi_device *dev)
+{
+    struct uhci *uhci = (struct uhci *)dev;
+    struct cdi_pci_resource *res;
+    int size = 0, request_size, cmd, intr, i;
+#ifdef CDI_PCI_DIRECT_ACCESS
+    int legsup;
+#endif
+
+    d0printf("Trying to init %s at %02x:%02x.%02x...\n", uhci->cdidev.dev.name,
+        uhci->pcidev->bus, uhci->pcidev->dev, uhci->pcidev->function);
+
+    uhci->pbase = 0;
+    for (i = 0; (res = cdi_list_get(uhci->pcidev->resources, i)) != NULL; i++)
+    {
+        if (res->type == CDI_PCI_IOPORTS) {
+            size = res->length ? res->length : 0x14;
+            if (cdi_ioports_alloc(res->start, size) == -1) {
+                d0printf("Cannot allocate I/O ports.\n");
+                return;
+            }
+            uhci->pbase = res->start;
+            break;
+        }
+    }
+    if (!uhci->pbase) {
+        d0printf("No I/O space found.\n");
+        return;
+    }
+    request_size =
+            // Maximale Paketlänge (Full Speed) ist 1023, Alignment au�erdem
+            1024 +
+            // Transferdeskriptor
+            sizeof(struct uhci_td);
+
+    uhci->buffers = mempool_create(8192, request_size);
+    if (uhci->buffers == NULL) {
+        d0printf("Cannot create mempool.\n");
+        return;
+    }
+
+    if (cdi_alloc_phys_mem(sizeof(*uhci->queue_heads) * 1024,
+        (void **)&uhci->queue_heads, (void **)&uhci->phys_queue_heads) == -1)
+    {
+        d0printf("Cannot allocate QH memory.\n");
+        return;
+    }
+
+    if (cdi_alloc_phys_mem(4096, (void **)&uhci->frame_list,
+        (void **)&uhci->phys_frame_list) == -1)
+    {
+        d0printf("Cannot allocate frame list.\n");
+        return;
+    }
+
+#ifdef CDI_PCI_DIRECT_ACCESS
+    cdi_pci_config_outw(uhci->pcidev, 4, 0x05);
+#endif
+
+    cdi_register_irq(uhci->pcidev->irq, &uhci_handler, dev);
+    uhci->root_ports = (size - 0x10) >> 1;
+    //Erstmal testen, wie viele wir genau haben...
+    for (i = 2; i < uhci->root_ports; i++) {
+        if (!(cdi_inw(uhci->pbase + UHCI_RPORTS + i * 2) & 0x0080) ||
+              (cdi_inw(uhci->pbase + UHCI_RPORTS + i * 2) == 0xFFFF))
+        {
+            uhci->root_ports = i;
+            break;
+        }
+    }
+    //Mehr als sieben Rootports, klingt lustig
+    if (uhci->root_ports > 7) {
+        uhci->root_ports = 2;
+    }
+
+    d1printf("I/O 0x%04X (%i ports), IRQ %i\n", uhci->pbase, uhci->root_ports,
+        uhci->pcidev->irq);
+
+#ifdef CDI_PCI_DIRECT_ACCESS
+    legsup = cdi_pci_config_inw(uhci->pcidev, UHCI_PCI_LEGSUP);
+#endif
+    cmd = cdi_inw(uhci->pbase + UHCI_USBCMD);
+    intr = cdi_inw(uhci->pbase + UHCI_USBINTR);
+
+#ifdef CDI_PCI_DIRECT_ACCESS
+    //Wenn der HC läuft, das Configurebit gesetzt ist, der nicht runtergefahren
+    //ist oder dieses Interrupt-enable-Bit gesetzt ist, dann war wohl schon
+    //jemand dran.
+    //(oder wenn der Legacy Support aktiviert ist, das heiÃ?t, wenn in LEGSUP
+    //irgendein Bit gesetzt ist, dass nicht RO/reserviert oder ein Statusbit
+    //ist)
+    if ((legsup & ~(UHCI_LEGSUP_STATUS | UHCI_LEGSUP_NO_CHG)) ||
+         (cmd & USB_RUN) || !(cmd & CONFIGURE) || !(cmd & GLOB_SUSP_MODE) ||
+         (intr & 0x0002))
+    {
+        //Na, wer hat denn da am HC rumgefummelt. Bööööses BIOS. Oder SMM. Oder
+        //wer auch immer.
+        //Alle LEGSUP-Statusbits löschen
+        cdi_pci_config_outw(uhci->pcidev, UHCI_PCI_LEGSUP,
+                            UHCI_LEGSUP_STATUS);
+#endif
+        //Resetten
+        cdi_outw(uhci->pbase + UHCI_USBCMD, HCRESET);
+        //Warten, bis der Reset beendet ist
+        for (i = 0; (cdi_inw(uhci->pbase + UHCI_USBCMD) & HCRESET) &&
+             (i < 50); i++)
+        {
+            cdi_sleep_ms(10);
+        }
+        if (i == 50) {
+            d1printf("Reset not finished...\n");
+        }
+
+        //Interrupts erstmal deaktivieren
+        cdi_outw(uhci->pbase + UHCI_USBINTR, 0);
+        //Den gesamten HC ebenso
+        cdi_outw(uhci->pbase + UHCI_USBCMD, 0);
+
+        //Alle Ports "runterfahren"
+        for (int port = 0; port < uhci->root_ports; port++) {
+            cdi_outw(uhci->pbase + UHCI_RPORTS + (port * 2), 0);
+        }
+#ifdef CDI_PCI_DIRECT_ACCESS
+    }
+#endif
+
+    for (i = 0; i < 1024; i++) {
+        uhci->queue_heads[i].next = 1; //Invalid
+        uhci->queue_heads[i].transfer = 1; //Invalid
+    }
+    for (i = 0; i < 1024; i++) {
+        uhci->frame_list[i] = (uintptr_t)&uhci->phys_queue_heads[i] | 2;
+    }
+
+    //Standardwert, jede Millisekunde ein Frame
+    cdi_outb(uhci->pbase + UHCI_SOFMOD, 0x40);
+    //Unsere Frameliste eintragen
+    cdi_outl(uhci->pbase + UHCI_FRBASEADD, uhci->phys_frame_list);
+    //Frame zurücksetzen
+    cdi_outw(uhci->pbase + UHCI_FRNUM, 0);
+#ifdef CDI_PCI_DIRECT_ACCESS
+    //Hier setzen wir das PIRQ-Bit und deaktivieren sämtlichen Legacy Support.
+    cdi_pci_config_outw(uhci->pcidev, UHCI_PCI_LEGSUP, UHCI_LEGSUP_PIRQ);
+#else
+    d0printf("Failed to deactivate USB legacy support.\n");
+#endif
+
+    //HC starten
+    cdi_outw(uhci->pbase + UHCI_USBCMD, USB_RUN | CONFIGURE | MAXP);
+    //Alle Interrupts aktivieren
+    cdi_outw(uhci->pbase + UHCI_USBINTR, 0xF);
+
+    //Connect status change löschen
+    for (i = 0; i < uhci->root_ports; i++) {
+        cdi_outw(uhci->pbase + UHCI_RPORTS + i * 2, RPORT_CSC);
+    }
+}
diff --git a/uhci/main.c b/uhci/main.c
new file mode 100644
index 0000000..8c5fb07
--- /dev/null
+++ b/uhci/main.c
@@ -0,0 +1,34 @@
+/*****************************************************************************
+* Copyright (c) 2009 Max Reitz                                               *
+*                                                                            *
+* Permission is hereby granted,  free of charge,  to any  person obtaining a *
+* copy of this software and associated documentation files (the "Software"), *
+* to deal in the Software without restriction,  including without limitation *
+* the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+* and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+* Software is furnished to do so, subject to the following conditions:       *
+*                                                                            *
+* The above copyright notice and this permission notice shall be included in *
+* all copies or substantial portions of the Software.                        *
+*                                                                            *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+* IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+* FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+* LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+* FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+* DEALINGS IN THE SOFTWARE.                                                  *
+*****************************************************************************/
+
+#include <cdi/misc.h>
+
+#include "uhci-init.h"
+
+int init_uhci()
+{
+    cdi_init();
+
+    find_and_init_uhcis();
+
+    return 0;
+}
diff --git a/uhci/transfer.c b/uhci/transfer.c
new file mode 100644
index 0000000..33c60bf
--- /dev/null
+++ b/uhci/transfer.c
@@ -0,0 +1,91 @@
+/*****************************************************************************
+* Copyright (c) 2009 Max Reitz                                               *
+*                                                                            *
+* Permission is hereby granted,  free of charge,  to any  person obtaining a *
+* copy of this software and associated documentation files (the "Software"), *
+* to deal in the Software without restriction,  including without limitation *
+* the rights to use,  copy, modify, merge, publish,  distribute, sublicense, *
+* and/or sell copies  of the  Software,  and to permit  persons to whom  the *
+* Software is furnished to do so, subject to the following conditions:       *
+*                                                                            *
+* The above copyright notice and this permission notice shall be included in *
+* all copies or substantial portions of the Software.                        *
+*                                                                            *
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
+* IMPLIED, INCLUDING  BUT NOT  LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
+* FITNESS FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT.  IN NO EVENT SHALL *
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
+* LIABILITY,  WHETHER IN AN ACTION OF CONTRACT,  TORT OR OTHERWISE,  ARISING *
+* FROM,  OUT OF  OR IN CONNECTION  WITH THE  SOFTWARE  OR THE  USE OR  OTHER *
+* DEALINGS IN THE SOFTWARE.                                                  *
+*****************************************************************************/
+
+#include <stdio.h>
+
+#include <cdi/io.h>
+#include <cdi/lists.h>
+#include <cdi/misc.h>
+
+#include "uhci.h"
+
+cdi_list_t uhci_active_transfers = NULL;
+
+void uhci_handler(struct cdi_device *cdi_hci)
+{
+    struct uhci *uhci = (struct uhci *)cdi_hci;
+    int status = cdi_inw(uhci->pbase + UHCI_USBSTS), i;
+    struct transfer *addr;
+    struct uhci_td *td;
+
+    if (!status) {
+        //Also, von hier kommt der IRQ nicht.
+        return;
+    }
+    //Ist Bit 0 gesetzt, dann ist das ein normaler IOC (Interrupt on
+    //completion), bei Bit 1 ist ein Fehler aufgetreten, aber das werden wir ja
+    //unten sehen.
+    if (status & ~0x0003) {
+        d1printf("Unerwarteter IRQ 0x%04X von %s\n", status, cdi_hci->name);
+    }
+    if (status & 0x10) {
+        d0printf("SCHWERWIEGENDER FEHLER - HC WIRD ANGEHALTEN\n");
+        cdi_outw(uhci->pbase + UHCI_USBCMD, MAXP | HCRESET); //FU!
+    } else {
+        for (i = 0; (addr = cdi_list_get(uhci_active_transfers, i)) != NULL;
+            i++)
+        {
+            td = addr->virt;
+            if (td->active) {
+                continue;
+            }
+            addr->error = CDI_USB_NO_ERROR;
+            cdi_list_remove(uhci_active_transfers, i--);
+            if (td->stalled_err) {
+                d0printf("ENDPOINT STALLED\n");
+                addr->error |= CDI_USB_STALLED;
+            }
+            if (td->buf_err) {
+                d1printf("Pufferüberlauf oder Underrun\n");
+                addr->error |= CDI_USB_BUFFER_ERROR;
+            }
+            if (td->babble) {
+                d1printf("Da war ein Gerät wohl sehr gesprächig: Babble.\n");
+                addr->error |= CDI_USB_BABBLE;
+            }
+            if (td->nak) {
+                d1printf("NAK empfangen\n");
+                addr->error |= CDI_USB_NAK;
+            }
+            if (td->crc_time_err) {
+                d1printf("CRC-Fehler oder Timeout\n");
+                addr->error |= CDI_USB_CRC | CDI_USB_TIMEOUT;
+            }
+            if (td->bitstuff_err) {
+                d1printf("Bitstufffehler\n");
+                addr->error |= CDI_USB_BITSTUFF;
+            }
+            //TODO: free(td) - dazu wäre eine CDI-Funktion nützlich
+        }
+    }
+    cdi_outw(uhci->pbase + UHCI_USBSTS, status);
+}
-- 
1.6.4.2