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

[cdi-devel] [PATCH v3 2/5] OHCI



From: Max Reitz <max@xxxxxxxxxx>

+ Added USB open host controller driver

Signed-off-by: Max Reitz <max@xxxxxxxxxx>
Signed-off-by: Kevin Wolf <kevin@xxxxxxxxxx>
---
 ohci/hcd.c                   |  315 ++++++++++++++++++++++++++++++++++++++++++
 ohci/include/ohci-hcd.h      |   39 +++++
 ohci/include/ohci-init.h     |   31 ++++
 ohci/include/ohci-transfer.h |   30 ++++
 ohci/include/ohci.h          |  283 +++++++++++++++++++++++++++++++++++++
 ohci/init.c                  |  241 ++++++++++++++++++++++++++++++++
 ohci/main.c                  |   34 +++++
 ohci/transfer.c              |   85 +++++++++++
 8 files changed, 1058 insertions(+), 0 deletions(-)
 create mode 100644 ohci/hcd.c
 create mode 100644 ohci/include/ohci-hcd.h
 create mode 100644 ohci/include/ohci-init.h
 create mode 100644 ohci/include/ohci-transfer.h
 create mode 100644 ohci/include/ohci.h
 create mode 100644 ohci/init.c
 create mode 100644 ohci/main.c
 create mode 100644 ohci/transfer.c

diff --git a/ohci/hcd.c b/ohci/hcd.c
new file mode 100644
index 0000000..ca337c2
--- /dev/null
+++ b/ohci/hcd.c
@@ -0,0 +1,315 @@
+/*****************************************************************************
+* 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 "ohci.h"
+#include "ohci-hcd.h"
+
+static void reset_device(struct cdi_usb_device *device);
+
+static void find_devices(struct cdi_usb_hc *device)
+{
+    struct ohci *ohci = (struct ohci *)device;
+    struct cdi_usb_device *dev;
+    int i;
+
+    d0printf("Scanning root ports\n");
+    for (i = 0; i < ohci->root_ports; i++) {
+        if (!(ohci->memory->hc_rh_port_status[i] & OHC_RP_CCS)) {
+            continue;
+        }
+        dev = malloc(sizeof(struct cdi_usb_device));
+        dev->hc = device;
+        dev->id = 0;
+        dev->port = i;
+        dev->speed = (ohci->memory->hc_rh_port_status[i] & OHC_RP_LSDA) ?
+            CDI_USB_LOW_SPEED : CDI_USB_FULL_SPEED;
+        dev->reset = &reset_device;
+        cdi_usb_device_init(dev);
+    }
+}
+
+static void activate_device(struct cdi_usb_device *device)
+{
+    struct ohci *ohci = (struct ohci *)device->hc;
+
+    if (device->hub != NULL) {
+        return;
+    }
+
+    d1printf("Activating device on port %i.\n", device->port);
+    ohci->memory->hc_rh_port_status[device->port] |= OHC_RP_PES;
+    cdi_sleep_ms(20);
+}
+
+static void reset_device(struct cdi_usb_device *device)
+{
+    struct ohci *ohci = (struct ohci *)device->hc;
+
+    if (device->hub != NULL) {
+        return;
+    }
+
+    d1printf("Resetting device on port %i.\n", device->port);
+    ohci->memory->hc_rh_port_status[device->port] |= OHC_RP_PRS;
+    cdi_sleep_ms(20);
+}
+
+static void add_pipe(struct cdi_usb_pipe *pipe)
+{
+    struct ohci *ohci = (struct ohci *)pipe->device->hc;
+    struct ohci_ed *ved;
+    uintptr_t ped;
+    struct ohci_ed_desc *dsc;
+    //TODO Mehr als Control und Bulk
+    int iscontrol = 0;
+
+    if (cdi_mempool_get(ohci->ed_pool, (void **)&ved, &ped) == -1) {
+        return;
+    }
+    ved->function = pipe->device->id;
+    ved->endpoint = pipe->endpoint->endpoint_address;
+    if (ved->endpoint == 0) {
+        //EP0
+        ved->direction = OHC_ED_DIR_TD;
+        iscontrol = 1;
+    } else if (pipe->endpoint->endpoint_address & 0x80) {
+        ved->direction = OHC_ED_DIR_IN;
+    } else {
+        ved->direction = OHC_ED_DIR_OUT;
+    }
+    ved->low_speed = !!(pipe->device->speed == CDI_USB_LOW_SPEED);
+    ved->skip = 0;
+    ved->format = 0;
+    ved->mps = pipe->endpoint->max_packet_size;
+    ved->td_queue_tail = 0;
+    ved->td_queue_head = 0;
+    dsc = cdi_list_get(ohci->ed_list, 0);
+    if (dsc == NULL) {
+        ved->next_ed = 0;
+    } else {
+        ved->next_ed = dsc->phys;
+    }
+    dsc = malloc(sizeof(*dsc));
+    if (dsc == NULL) {
+        cdi_mempool_put(ohci->ed_pool, ved);
+        return;
+    }
+    dsc->virt = ved;
+    dsc->phys = ped;
+    dsc->function = pipe->device->id;
+    dsc->endpoint = pipe->endpoint->endpoint_address;
+    dsc->type = iscontrol ? USB_CONTROL : USB_BULK;
+    dsc->transfers = cdi_list_create();
+    cdi_list_push(ohci->ed_list, dsc);
+
+    if (iscontrol) {
+        ohci->memory->hc_control_head_ed = ped;
+    } else {
+        ohci->memory->hc_bulk_head_ed = ped;
+    }
+}
+
+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_packets(struct cdi_usb_packet *packets,
+    size_t num_packets)
+{
+    struct ohci_ed_desc *edsc;
+    struct ohci_td_desc *tdsc, *otdsc;
+    int i, j, cond, toggle, done_packets;
+    struct ohci *ohci = (struct ohci *)packets[0].pipe->device->hc;
+    struct ohci_td *vtd[num_packets];
+    uintptr_t ptd[num_packets];
+
+    for (i = 0; (edsc = cdi_list_get(ohci->ed_list, i)) != NULL; i++) {
+        if ((edsc->function == packets[0].pipe->device->id) &&
+             (edsc->endpoint == packets[0].pipe->endpoint->endpoint_address))
+        {
+            break;
+        }
+    }
+    if (edsc == NULL) {
+        return CDI_USB_TIMEOUT; //Genau das würde passieren
+    }
+
+    toggle = packets[0].pipe->data_toggle;
+    for (i = 0; i < (int)num_packets; i++) {
+        if (cdi_mempool_get(ohci->transfer_pool, (void **)&vtd[i], &ptd[i]) == -1)
+        {
+            return CDI_USB_TRIVIAL_ERROR;
+        }
+        if ((packets[i].data != NULL) &&
+            (packets[i].type != CDI_USB_PACKET_IN))
+        {
+            memcpy((void *)((uintptr_t)vtd[i] + sizeof(struct ohci_td)),
+                    packets[i].data, packets[i].length);
+        }
+        vtd[i]->rounding = 1; //Warum nicht
+        switch (packets[i].type) {
+            case CDI_USB_PACKET_SETUP:
+                vtd[i]->direction = OHC_TD_DIR_SETUP;
+                break;
+            case CDI_USB_PACKET_IN:
+                vtd[i]->direction = OHC_TD_DIR_IN;
+                break;
+            case CDI_USB_PACKET_OUT:
+                vtd[i]->direction = OHC_TD_DIR_OUT;
+                break;
+                default: //Hm, passt nicht ganz
+                    for (j = 0; j <= i; j++) {
+                        cdi_mempool_put(ohci->transfer_pool, vtd[j]);
+                    }
+                    return CDI_USB_TRIVIAL_ERROR;
+        }
+        vtd[i]->di = 0;
+        if (packets[i].use_toggle == TOGGLE_0) {
+            toggle = 0;
+        } else if (packets[i].use_toggle == TOGGLE_1) {
+            toggle = 1;
+        }
+        vtd[i]->toggle = 0x2 | toggle;
+        toggle ^= 1;
+        vtd[i]->error = 0;
+        vtd[i]->condition = 15;
+        if ((packets[i].data == NULL) || !packets[i].length) {
+            vtd[i]->current_buffer_pointer = 0;
+            vtd[i]->buffer_end = 0;
+        } else {
+            vtd[i]->current_buffer_pointer = ptd[i] + sizeof(struct ohci_td);
+            vtd[i]->buffer_end = ptd[i] +
+                    sizeof(struct ohci_td) + packets[i].length - 1;
+        }
+        vtd[i]->next_td = 0;
+        tdsc = malloc(sizeof(*tdsc));
+        if (tdsc == NULL) {
+            for (j = 0; j <= i; j++) {
+                cdi_mempool_put(ohci->transfer_pool, vtd[j]);
+            }
+            return CDI_USB_TRIVIAL_ERROR;
+        }
+        tdsc->virt = vtd[i];
+        tdsc->phys = ptd[i];
+        tdsc->endpoint = edsc;
+
+        if (cdi_list_size(edsc->transfers)) {
+            otdsc = cdi_list_get(edsc->transfers, 0);
+            otdsc->virt->next_td = ptd[i];
+        }
+        if (!edsc->virt->td_queue_head) {
+            edsc->virt->td_queue_head = ptd[i];
+        }
+
+        cdi_list_push(edsc->transfers, tdsc);
+    }
+    packets[0].pipe->data_toggle = toggle;
+    if (edsc->type == USB_CONTROL) {
+        ohci->memory->hc_command_status |= OHC_CMST_CLF;
+    } else if (edsc->type == USB_BULK) {
+        ohci->memory->hc_command_status |= OHC_CMST_BLF;
+    }
+    done_packets = 0;
+    cond = CDI_USB_NO_ERROR;
+    while (done_packets < (int)num_packets) {
+        cdi_sleep_ms(1);
+        for (i = 0; i < (int)num_packets; i++) {
+            if (vtd[i]->condition != 15) {
+                done_packets++;
+                switch (vtd[i]->condition) {
+                    case 0x00:
+                        packets[i].condition = CDI_USB_NO_ERROR;
+                        break;
+                    case 0x02:
+                        packets[i].condition = CDI_USB_BITSTUFF;
+                        break;
+                    case 0x04:
+                        packets[i].condition = CDI_USB_STALLED;
+                        for (j = 0; j < (int)num_packets; j++) {
+                            //Allet, was nicht fertig ist, ist STALLed
+                            vtd[j]->condition = 0x04;
+                        }
+                        break;
+                    case 0x05:
+                        packets[i].condition = CDI_USB_TIMEOUT;
+                        break;
+                    case 0x08:
+                        packets[i].condition = CDI_USB_BABBLE;
+                        break;
+                    case 0x0C:
+                    case 0x0D:
+                        packets[i].condition = CDI_USB_BUFFER_ERROR;
+                        break;
+                    default:
+                        packets[i].condition = CDI_USB_CRC;
+                        break;
+                }
+                cond |= packets[i].condition;
+                vtd[i]->condition = 15; //Nicht nochmal überprüfen
+            }
+        }
+    }
+    for (i = 0; i < (int)num_packets; i++) {
+        if ((packets[i].data != NULL) &&
+            (packets[i].type == CDI_USB_PACKET_IN))
+        {
+            memcpy(packets[i].data, (void *)((uintptr_t)vtd[i] +
+                    sizeof(struct ohci_td)), packets[i].length);
+        }
+        if (vtd[i]->next_td == 0) {
+            edsc->virt->td_queue_head = 0;
+        }
+        cdi_mempool_put(ohci->transfer_pool, vtd[i]);
+    }
+    if (cond & CDI_USB_STALLED) {
+        //Transferliste für diesen Endpoint leeren
+        edsc->virt->td_queue_head = 0;
+        while ((tdsc = cdi_list_pop(edsc->transfers)) != NULL) {
+            free(tdsc);
+        }
+        edsc->virt->td_queue_tail = 0; //Weitermachen
+    }
+    return cond;
+}
+
+
+const struct ohci_functions ohci_functions = {
+    .find_devices = &find_devices,
+    .activate_device = &activate_device,
+    .send_packets = &do_packets,
+    .add_pipe = &add_pipe
+};
diff --git a/ohci/include/ohci-hcd.h b/ohci/include/ohci-hcd.h
new file mode 100644
index 0000000..ccb6345
--- /dev/null
+++ b/ohci/include/ohci-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 ohci_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 ohci_find_devices(struct cdi_usb_hc *device);
+
+#endif
diff --git a/ohci/include/ohci-init.h b/ohci/include/ohci-init.h
new file mode 100644
index 0000000..2f19d2c
--- /dev/null
+++ b/ohci/include/ohci-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 OHCI_INIT_H
+#define OHCI_INIT_H
+
+#include "ohci.h"
+
+void find_and_init_ohcis(void);
+void ohci_init(struct cdi_device *dev);
+
+#endif
diff --git a/ohci/include/ohci-transfer.h b/ohci/include/ohci-transfer.h
new file mode 100644
index 0000000..a6dc6be
--- /dev/null
+++ b/ohci/include/ohci-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 OHCI_TRANSFER_H
+#define OHCI_TRANSFER_H
+
+#include <cdi/misc.h>
+
+void ohci_handler(struct cdi_device *cdi_hci);
+
+#endif
diff --git a/ohci/include/ohci.h b/ohci/include/ohci.h
new file mode 100644
index 0000000..4936960
--- /dev/null
+++ b/ohci/include/ohci.h
@@ -0,0 +1,283 @@
+/*****************************************************************************
+* 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 OHCI_H
+#define OHCI_H
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <cdi/misc.h>
+#include <cdi/pci.h>
+#include <cdi/usb-hcd.h>
+
+#include "ohci.h"
+
+
+//#define DEBUG
+
+#ifdef DEBUG
+#define d1printf(...) printf("[ohci] " __VA_ARGS__)
+#define _d1printf(...) printf(__VA_ARGS__)
+#else
+#define d1printf(...)
+#define _d1printf(...)
+#endif
+
+#define d0printf(...) printf("[ohci] " __VA_ARGS__)
+#define _d0printf(...) printf(__VA_ARGS__)
+
+
+#define OHC_USB_RESET       0x00000000
+#define OHC_USB_RESUME      0x00000040
+#define OHC_USB_OPERATIONAL 0x00000080
+#define OHC_USB_SUSPEND     0x000000C0
+
+#define OHC_CTRL_CBSR  0x00000003 //Verhältnis zwischen Control und Bulk
+                                 //(cbsr+1 zu 1)
+#define OHC_CTRL_PLE   0x00000004 //Periodische Transfers aktivieren
+#define OHC_CTRL_IE    0x00000008 //Isochronous-Transfers aktivieren
+#define OHC_CTRL_CLE   0x00000010 //Control-Transfers aktivieren
+#define OHC_CTRL_BLE   0x00000020 //Bulk-Transfers aktivieren
+#define OHC_CTRL_HCFS  0x000000C0 //Status des HCs
+#define OHC_CTRL_IR    0x00000100 //Wenn gesetzt, dann werden IRQs zum SMB
+                                  //geleitet
+#define OHC_CTRL_RWC   0x00000200 //Remote wakeup untersetützt
+#define OHC_CTRL_RW    0x00000400 //Remote wakeup aktivieren
+
+#define OHC_CMST_RESET 0x00000001 //Reset
+#define OHC_CMST_CLF   0x00000002 //Control list filled (muss gesetzt werden,
+                                  //wenn ein Eintrag zur Controlliste
+                                  //hinzugefügt wird)
+#define OHC_CMST_BLF   0x00000004 //Bulk list filled (muss gesetzt werden, wenn
+                                  //ein Eintrag zur Bulkliste hinzugefügt wird)
+#define OHC_CMST_OCR   0x00000008 //Ownership change request
+#define OHC_CMST_SOC   0x00030000 //Scheduling overrun count
+
+#define OHC_INT_SO     0x00000001 //Scheduling overrun
+#define OHC_INT_WDH    0x00000002 //Write back done head
+#define OHC_INT_SF     0x00000004 //Start of frame (wird bei SOF gesetzt)
+#define OHC_INT_RD     0x00000008 //Resume detected (wird bei einem von einem
+                                  //Gerät gesendeten Resume gesetzt)
+#define OHC_INT_UE     0x00000010 //Unrecoverable error (wird bei einem HC-
+                                  //Fehler gesetzt, der nichts mit USB zu tun
+                                  //hat)
+#define OHC_INT_FNO    0x00000020 //Frame number overflow (wird gesetzt, wenn
+                                  //die Framenummer grö�er als 32767 und
+                                  //umgebrochen wird)
+#define OHC_INT_RHSC   0x00000040 //Root hub status change (wird gesetzt, wenn
+                                  //sich irgendwas am Roothub geändert hat)
+#define OHC_INT_OC     0x40000000 //Ownership change
+#define OHC_INT_MIE    0x80000000 //(De-)Aktiviert Interrupts
+
+#define OHC_RHA_NDP    0x000000FF //Anzahl an Rootports (1 bis 15)
+#define OHC_RHA_PSM    0x00000100 //Gesetzt, wenn man die Stromzufuhr zu
+                                  //einzelnen Ports unabhängig setzen kann
+#define OHC_RHA_NPS    0x00000200 //Gelöscht, wenn man die Stromzufuhr zu
+                                  //einzelnen Ports unabhängig setzen kann
+#define OHC_RHA_DT     0x00000400 //Immer 0, zeigt so an, dass der Roothub kein
+                                  //Compound device ist.
+#define OHC_RHA_OCPM   0x00000800 //Gesetzt, wenn �berspannungen für jeden Port
+                                  //einzeln gemeldet werden
+#define OHC_RHA_NOCP   0x00001000 //Gesetzt, wenn keine Ã?berspannungen erkannt
+                                  //bzw. verhindert werden können
+#define OHC_RHA_POTPGT 0xFF000000 //Power on to power good time (potpgt * 2)
+
+#define OHC_RHS_LPS    0x00000001 //R: 0
+                                  //W: Strom abdrehen
+#define OHC_RHS_OCI    0x00000002 //R: Unspezifische Ã?berspannung
+#define OHC_RHS_DRWE   0x00008000 //R: Irgendwas mit Remote Wakeup Enable
+                                  //W: Das aktivieren
+#define OHC_RHS_LPSC   0x00010000 //R: 0
+                                  //W: Strom andrehen
+#define OHC_RHS_OCIC   0x00020000 //R: Bei Ã?nderungen von OCI gesetzt
+                                  //W: Feld löschen
+#define OHC_RHS_CRWE   0x80000000 //W: Remote Wakeup Enable deaktivieren
+
+#define OHC_RP_CCS     0x00000001 //R: Gerät angeschlossen
+                                  //W: Deaktiviert den Port
+#define OHC_RP_PES     0x00000002 //R: Port aktiviert
+                                  //W: Aktiviert den Port
+#define OHC_RP_PSS     0x00000004 //R: Port schläft
+                                  //W: Schläfert den Port ein
+#define OHC_RP_POCI    0x00000008 //R: Ã?berspannung
+                                  //W: Port aufwecken
+#define OHC_RP_PRS     0x00000010 //R: Reset läuft
+                                  //W: Reset treiben
+#define OHC_RP_PPS     0x00000100 //R: Strom an
+                                  //W: Strom andrehen
+#define OHC_RP_LSDA    0x00000200 //R: Low-Speed-Gerät
+                                  //W: Strom abdrehen
+#define OHC_RP_CSC     0x00010000 //R: Gerät an- oder abgezogen
+                                  //W: Bit löschen
+#define OHC_RP_PESC    0x00020000 //R: Port (de-)aktiviert
+                                  //W: Bit löschen
+#define OHC_RP_PSSC    0x00040000 //R: Port wurde geweckt
+                                  //W: Bit löschen
+#define OHC_RP_OCIC    0x00080000 //R: �berspannungsbit verändert
+                                  //W: Bit löschen
+#define OHC_RP_PRSC    0x00100000 //R: Reset beendet
+                                  //W: Bit löschen
+
+struct ohci_registers
+{
+    uint32_t hc_revision; //Revision
+    volatile uint32_t hc_control;
+    volatile uint32_t hc_command_status;
+    volatile uint32_t hc_interrupt_status;
+    volatile uint32_t hc_interrupt_enable;
+    volatile uint32_t hc_interrupt_disable;
+    uint32_t hc_hcca; //Adresse des HCCA
+    volatile uint32_t hc_period_current_ed; //Adresse des aktuellen
+                                           //Endpointdescriptors der
+                                           //periodischen Transfers
+    uint32_t hc_control_head_ed; //Adresse des ersten Endpointdescriptors der
+                                //Control-Transfers
+    volatile uint32_t hc_control_current_ed; //Adresse des aktuellen
+                                            //Endpointdescriptors der Control-
+                                            //Transfers
+    uint32_t hc_bulk_head_ed; //Adresse des ersten Endpointdescriptors der
+                             //Bulk-Transfers
+    volatile uint32_t hc_bulk_current_ed; //Adresse des aktuellen
+                                         //Endpointdescriptors der Bulk-
+                                         //Transfers
+    volatile uint32_t hc_done_head; //Adresse des zuletzt abgearbeiteten
+                                   //Transferdeskriptors, der der Done-Liste
+                                   //hinzugefügt wurde
+    uint32_t hc_fm_interval; //Gibt die Länge eines Frames und die maximale
+                            //Paketgrö�e an
+    volatile uint32_t hc_fm_remaining; //Verbleibende Takte des aktuellen
+                                       //Frames
+    volatile uint32_t hc_fm_number; //Nummer des aktuellen Frames
+    uint32_t hc_periodic_start; //10% von HcFmInterval, wenn HcFmInterval
+                                //diesen Wert erreicht, dann haben periodische
+                                //Transfers Vorrang
+    uint32_t hc_ls_threshold; //Besser nicht ändern
+    uint32_t hc_rh_descriptor_a;
+    uint32_t hc_rh_descriptor_b;
+    volatile uint32_t hc_rh_status;
+    volatile uint32_t hc_rh_port_status[];
+} __attribute__((packed));
+
+struct ohci_legsup
+{
+    uint32_t hce_control;
+    uint32_t hce_input;
+    uint32_t hce_output;
+    uint32_t hce_status;
+} __attribute__((packed));
+
+struct ohci_hcca
+{
+    uint32_t interrupt_table[32]; //Zeiger zu Interrupt-EDs
+    uint16_t frame_number; //Die Nummer des aktuellen Frames
+    uint16_t pad; //Wird auf 0 gesetzt, wenn frame_number neu geschrieben wird
+                 //(Ausrichten an DWord-Grenzen vermutlich)
+    uint32_t done_head;
+    uint8_t rsvd[116];
+} __attribute__((packed));
+
+struct ohci
+{
+    struct cdi_usb_hc cdidev;
+    struct cdi_pci_device *pcidev;
+
+    struct ohci_registers *memory;
+    struct ohci_legsup *legsup;
+    int root_ports;
+    struct ohci_hcca *hcca;
+
+    struct cdi_mempool *ed_pool;
+    struct cdi_mempool *transfer_pool;
+
+    cdi_list_t ed_list;
+};
+
+#define OHC_ED_DIR_TD  0 //Richtung im TD definiert
+#define OHC_ED_DIR_OUT 1
+#define OHC_ED_DIR_IN  2
+
+struct ohci_ed
+{
+    unsigned function : 7; //USB-Adresse des Geräts
+    unsigned endpoint : 4; //Nummer des Endpoints
+    unsigned direction : 2; //Richtung des Transfers
+    unsigned low_speed : 1;
+    unsigned skip : 1;
+    unsigned format : 1; //Bei Isochronous gesetzt
+    unsigned mps : 11; //Maximale Paketgrö�e
+    unsigned user : 5; //Für uns frei verfügbar
+    uint32_t td_queue_tail; //Letzter TD in der Warteschlange
+    volatile uint32_t td_queue_head; //Nächster TD in der Warteschlange
+    uint32_t next_ed; //Nächster ED
+} __attribute__((packed));
+
+struct ohci_ed_desc
+{
+    struct ohci_ed *virt;
+    uintptr_t phys;
+    int function;
+    int endpoint;
+    cdi_list_t transfers;
+    enum
+    {
+        USB_CONTROL,
+        USB_BULK
+    } type;
+} __attribute__((packed));
+
+#define OHC_TD_DIR_SETUP 0
+#define OHC_TD_DIR_OUT   1
+#define OHC_TD_DIR_IN    2
+
+struct ohci_td
+{
+    unsigned user : 18; //Für uns frei verfügbar
+    unsigned rounding : 1; //Wenn gesetzt, dann sind zu kleine Pakete nicht so
+                          //schlimm
+    unsigned direction : 2; //Richtung/Typ des Transfers (nur gültig, wenn das
+                           //"direction"-Feld im ED 00b oder 11b ist)
+    unsigned di : 3; //Gibt an, wie viele Frames der HC vor Auslösen eines IRQs
+                    //warten soll
+    unsigned toggle : 2; //Datatoggle (00b oder 01b - Wert muss aus dem ED
+                        //ermittelt werden, 10b = DATA0, 11b = DATA1)
+    volatile unsigned error : 2; //Anzahl der aufgetretenen Fehler - bei 11b
+                                //wird der Status im "condition"-Feld
+                                //gespeichert.
+    volatile unsigned condition : 4; //Status
+    volatile uint32_t current_buffer_pointer; //Pointer zu den Daten (bei 0
+                                             //wurden alle Daten übertragen)
+    uint32_t next_td; //Nächster TD
+    uint32_t buffer_end; //Letztes Datenbyte des Puffers
+} __attribute__((packed));
+
+struct ohci_td_desc
+{
+    struct ohci_td *virt;
+    uintptr_t phys;
+    struct ohci_ed_desc *endpoint;
+} __attribute__((packed));
+
+struct cdi_driver *init_ohcd(void);
+void ohci_init(struct cdi_device *cdi_hci);
+
+#endif
diff --git a/ohci/init.c b/ohci/init.c
new file mode 100644
index 0000000..338a1ac
--- /dev/null
+++ b/ohci/init.c
@@ -0,0 +1,241 @@
+/*****************************************************************************
+* 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 <string.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 "ohci.h"
+#include "ohci-hcd.h"
+#include "ohci-init.h"
+#include "ohci-transfer.h"
+
+extern const struct ohci_functions ohci_functions;
+extern int init_ohci(void);
+
+#define DRIVER_NAME "ohcd"
+
+static struct cdi_usb_hcd driver = {
+    .drv = {
+        .type          = CDI_USB_HCD,
+        .name          = DRIVER_NAME,
+        .devices       = NULL,
+        .init_device   = &ohci_init,
+        .remove_device = NULL,
+        .init          = &init_ohci,
+        .destroy       = NULL
+    }
+};
+
+CDI_DRIVER(DRIVER_NAME, driver)
+
+void find_and_init_ohcis(void)
+{
+    struct ohci *hci;
+    char *name;
+    int ohci_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 != 0x10))
+        {
+            cdi_pci_device_destroy(dev);
+        } else {
+            hci = malloc(sizeof(struct ohci));
+            hci->cdidev.dev.type = CDI_USB_HCD;
+            name = malloc(7);
+            sprintf(name, "ohci%i", ohci_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 = ohci_functions.find_devices;
+    driver.activate_device = ohci_functions.activate_device;
+    driver.send_packets = ohci_functions.send_packets;
+    driver.add_pipe = ohci_functions.add_pipe;
+}
+
+void ohci_init(struct cdi_device *dev)
+{
+    struct ohci *ohci = (struct ohci *)dev;
+    struct cdi_pci_resource *res;
+    int i;
+    uintptr_t phys_hcca;
+
+    ohci->memory = NULL;
+    for (i = 0; (res = cdi_list_get(ohci->pcidev->resources, i)) != NULL;
+        i++)
+    {
+        if (res->type == CDI_PCI_MEMORY) {
+            ohci->memory = cdi_alloc_phys_addr(res->length, res->start);
+            if (ohci->memory == NULL) {
+                d0printf("Wasn't able to allocate memory.\n");
+                return;
+            }
+            ohci->legsup = (void *)((uintptr_t)ohci->memory + 0x100);
+            break;
+        }
+    }
+    if (ohci->memory == NULL) {
+        d1printf("Memory not found!\n");
+        return;
+    }
+    d1printf("Memory @ 0x%08X\n", (uintptr_t)ohci->memory);
+    if ((ohci->memory->hc_revision & 0xFF) != 0x10) {
+        d0printf("This OHCI might be incompatible with this driver"
+            "(HCI %i.%i, HCD 1.0)\n", (ohci->memory->hc_revision & 0xF0) >> 4,
+            ohci->memory->hc_revision & 0xF);
+    }
+    if (cdi_alloc_phys_mem(sizeof(*ohci->hcca), (void **)&ohci->hcca,
+        (void **)&phys_hcca) == -1)
+    {
+        d0printf("Wasn't able to allocate HCCA.\n");
+        return;
+    }
+    memset(ohci->hcca, 0, sizeof(*ohci->hcca));
+
+    d1printf("IRQ: %i\n", ohci->pcidev->irq);
+    cdi_register_irq(ohci->pcidev->irq, &ohci_handler, dev);
+
+    ohci->ed_pool = cdi_mempool_create(64 * sizeof(struct ohci_ed),
+                                   sizeof(struct ohci_ed));
+    if (ohci->ed_pool == NULL) {
+        d0printf("Wasn't able to create ED memory pool!\n");
+        return;
+    }
+
+    ohci->transfer_pool = cdi_mempool_create(48 * (1024 + sizeof(struct ohci_td)),
+                                         1024 + sizeof(struct ohci_td));
+    if (ohci->transfer_pool == NULL) {
+        d0printf("Wasn't able to create transfer memory pool!\n");
+        return;
+    }
+
+    ohci->memory->hc_interrupt_disable = OHC_INT_MIE;
+
+    if (ohci->memory->hc_control & OHC_CTRL_IR) {
+        d1printf("Kicking out SMM... ");
+        //Ã?nderung des "Besitzers" beantragen
+        ohci->memory->hc_command_status |= OHC_CMST_OCR;
+        for (i = 0; (ohci->memory->hc_control & OHC_CTRL_IR) && (i < 1000);
+            i++)
+        {
+            cdi_sleep_ms(1);
+        }
+        if (i < 100) {
+            _d1printf("Went fine.\n");
+        } else {
+            _d1printf("Idiot.\n");
+            ohci->memory->hc_control &= ~OHC_CTRL_IR;
+        }
+    } else if ((ohci->memory->hc_control & OHC_CTRL_CBSR) != OHC_USB_RESET) {
+        d1printf("BIOS active\n");
+        if ((ohci->memory->hc_control & OHC_CTRL_CBSR) != OHC_USB_OPERATIONAL)
+        {
+            d1printf("Activating RESUME...\n");
+            ohci->memory->hc_control =
+                (ohci->memory->hc_control & ~OHC_CTRL_CBSR) | OHC_USB_RESUME;
+            cdi_sleep_ms(10);
+        }
+    } else {
+        //Nix, weder BIOS noch SMM
+        cdi_sleep_ms(10); //Reset abwarten
+    }
+
+    if (ohci->legsup->hce_control & 1) {
+        d1printf("USB legacy support activated, but that won't last for"
+            "long...\n");
+    }
+    //Harhar, that's mine now!
+    ohci->legsup->hce_control = 0;
+
+    ohci->memory->hc_command_status |= OHC_CMST_RESET;
+    //Normalerweise sollte man den Wert von hc_fm_interval von vor dem Reset
+    //wiederherstellen, aber ich finde das Schwachsinn. Die Werte sind so in
+    //Ordnung, wie sie jetzt sind.
+
+    cdi_sleep_ms(3); //10 µs für den Reset und 2 ms für Resume
+    if ((ohci->memory->hc_control & OHC_CTRL_CBSR) == OHC_USB_SUSPEND) {
+        //Boah...
+        d1printf("Reset not finished yet...\n");
+        ohci->memory->hc_control =
+            (ohci->memory->hc_control & ~OHC_CTRL_CBSR) | OHC_USB_RESUME;
+        cdi_sleep_ms(10);
+    }
+
+    ohci->memory->hc_interrupt_disable = OHC_INT_MIE;
+
+    ohci->memory->hc_hcca = phys_hcca;
+    ohci->memory->hc_interrupt_status = 0xFFFFFFFF;
+    ohci->memory->hc_interrupt_enable =
+        OHC_INT_SO   | //Scheduling overrun
+        OHC_INT_WDH  | //Write back done head
+        OHC_INT_RD   | //Resume detected
+        OHC_INT_UE   | //Schwerer Fehler
+        OHC_INT_RHSC | //Root hub status change
+        OHC_INT_MIE;
+    //Eigentlich sollte man alle Transfers aktivieren. Aber wir wollen ja noch
+    //gar keine periodischen Transfers...
+    //Periodisches Zeug deaktivieren
+    ohci->memory->hc_control &= ~(OHC_CTRL_PLE | OHC_CTRL_IE);
+    //Control und Bulk aktivieren
+    ohci->memory->hc_control |= OHC_CTRL_CLE | OHC_CTRL_BLE;
+    //Merkwürdigerweise soll das jetzt nicht auf 10 %, sondern auf 90 % gesetzt
+    //werden. Aber das klingt total bescheuert, weil nicht erklärt wird, wann
+    //man es denn auf 10 % setzen soll. Deshalb machen wir das gleich.
+    ohci->memory->hc_periodic_start = 0x4B0;
+
+    d1printf("Activating HC.\n");
+
+    //DO IT, FAGGOT!
+    ohci->memory->hc_control =
+        (ohci->memory->hc_control & ~OHC_CTRL_CBSR) | OHC_USB_OPERATIONAL;
+
+    ohci->memory->hc_rh_status |= OHC_RHS_LPSC; //Strom andrehen
+    ohci->root_ports = ohci->memory->hc_rh_descriptor_a & OHC_RHA_NDP;
+    cdi_sleep_ms((ohci->memory->hc_rh_descriptor_a & OHC_RHA_POTPGT) >> 23);
+
+    d0printf("Found %i root ports.\n", ohci->root_ports);
+
+    for (i = 0; i < ohci->root_ports; i++) {
+        ohci->memory->hc_rh_port_status[i] |= OHC_RP_CCS; //Deaktivieren
+    }
+
+    ohci->ed_list = cdi_list_create();
+}
diff --git a/ohci/main.c b/ohci/main.c
new file mode 100644
index 0000000..4b6cd67
--- /dev/null
+++ b/ohci/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 "ohci-init.h"
+
+int init_ohci()
+{
+    cdi_init();
+
+    find_and_init_ohcis();
+
+    return 0;
+}
diff --git a/ohci/transfer.c b/ohci/transfer.c
new file mode 100644
index 0000000..24f7c67
--- /dev/null
+++ b/ohci/transfer.c
@@ -0,0 +1,85 @@
+/*****************************************************************************
+* 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/misc.h>
+
+#include "ohci.h"
+
+void ohci_handler(struct cdi_device *cdi_hci)
+{
+    struct ohci *ohci = (struct ohci *)cdi_hci;
+    struct ohci_ed_desc *edsc;
+    struct ohci_td_desc *tdsc;
+    uint32_t status, done = 0;
+    int i, j;
+    uintptr_t phys;
+
+    status = ohci->memory->hc_interrupt_status;
+    if (!status) {
+        return; //Wohl nicht für uns
+    }
+
+    if (status & OHC_INT_RHSC) {
+        done |= OHC_INT_RHSC;
+    }
+    if (status & OHC_INT_UE) {
+        d0printf("Fatal HC error.\n");
+        ohci->memory->hc_command_status |= OHC_CMST_RESET;
+        done |= OHC_INT_UE;
+    }
+    if (status & OHC_INT_SF) {
+        //Ã?hm... Na ja. Manchmal fragt man sich, ob sich der HC so einsam
+        //fühlt, dass er IRQs feuert, obwohl sie deaktiviert sind.
+        done |= OHC_INT_SF;
+    }
+    if (status & OHC_INT_WDH) {
+        //Paket gesendet
+        done |= OHC_INT_WDH;
+        phys = ohci->hcca->done_head;
+        for (i = 0; (edsc = cdi_list_get(ohci->ed_list, i)) != NULL; i++) {
+            for (j = 0; (tdsc = cdi_list_get(edsc->transfers, j)) != NULL; j++)
+            {
+                if (tdsc->phys == phys) {
+                    //Das ist der fertige Transfer
+                    cdi_list_remove(edsc->transfers, j);
+                    free(tdsc);
+                    break;
+                }
+            }
+            if (tdsc != NULL) {
+                //Gefunden
+                break;
+            }
+        }
+    }
+
+    if (status & ~done) {
+        d1printf("Non-handled interrupt: 0x%08X\n", status & ~done);
+    }
+
+    ohci->memory->hc_interrupt_status = status;
+}
-- 
1.6.0.2