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

[cdi-devel] [PATCH] usb: OHCI driver



From: Max Reitz <max@xxxxxxxxxx>

+ OHCI driver works on QEMU

Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
 usb/include/ohci.h |   35 ++++++++--
 usb/include/usb.h  |    1 +
 usb/main.c         |    4 +
 usb/msd.c          |    2 +
 usb/ohci.c         |  199 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 usb/uhci.c         |    4 +-
 6 files changed, 233 insertions(+), 12 deletions(-)

diff --git a/usb/include/ohci.h b/usb/include/ohci.h
index 5f6ad01..4c896b9 100644
--- a/usb/include/ohci.h
+++ b/usb/include/ohci.h
@@ -25,6 +25,8 @@
 
 #include <stdint.h>
 
+#include "cdi/lists.h"
+
 #include "mempool.h"
 #include "usb.h"
 
@@ -174,6 +176,8 @@ struct ohci {
 
     struct mempool* ed_pool;
     struct mempool* transfer_pool;
+
+    cdi_list_t ed_list;
 };
 
 #define OHC_ED_DIR_TD  0 //Richtung im TD definiert
@@ -190,10 +194,22 @@ struct ohci_ed {
     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
-    uint32_t td_queue_head; //Nächster 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
@@ -208,15 +224,22 @@ struct ohci_td {
                      //warten soll
     unsigned toggle : 2; //Datatoggle (00b oder 01b - Wert muss aus dem ED
                          //ermittelt werden, 10b = DATA0, 11b = DATA1)
-    unsigned error : 2; //Anzahl der aufgetretenen Fehler - bei 11b wird der
-                        //Status im "condition"-Feld gespeichert
-    unsigned condition : 4; //Status
-    uint32_t current_buffer_pointer; //Pointer zu den Daten (bei 0 wurden alle
-                                     //Daten übertragen)
+    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);
 
diff --git a/usb/include/usb.h b/usb/include/usb.h
index 896afc7..3882601 100644
--- a/usb/include/usb.h
+++ b/usb/include/usb.h
@@ -25,6 +25,7 @@
 
 #include <stdint.h>
 
+#include "cdi/lists.h"
 #include "cdi/misc.h"
 #include "cdi/pci.h"
 
diff --git a/usb/main.c b/usb/main.c
index 8861fcf..cfab9a5 100644
--- a/usb/main.c
+++ b/usb/main.c
@@ -349,6 +349,8 @@ static void enum_device(struct usb_device* usbdev)
     //Resetten
     usbdev->reset(usbdev);
 
+    usbdev->hci->add_pipe(usbdev->ep0);
+
     //Erste acht Bytes des Device-Descriptors abrufen und die maximale Paketgrö�e von EP0 feststellen
     dev_desc =
         do_control(usbdev, DEV_TO_HOST, NULL, 8, STD_REQUEST, REC_DEVICE,
@@ -369,6 +371,8 @@ static void enum_device(struct usb_device* usbdev)
         0);
     usbdev->id = id;
 
+    usbdev->hci->add_pipe(usbdev->ep0);
+
     //Den ganzen Device-Descriptor einlesen
     dev_desc =
         do_control(usbdev, DEV_TO_HOST, NULL, sizeof(*dev_desc), STD_REQUEST,
diff --git a/usb/msd.c b/usb/msd.c
index 4410f37..f91df4d 100644
--- a/usb/msd.c
+++ b/usb/msd.c
@@ -195,6 +195,8 @@ void register_msd(struct usb_device* usbdev)
         dprintf("Nicht genügend Endpoints gefunden.\n");
         return;
     }
+    usbdev->hci->add_pipe(msc->bulk_in);
+    usbdev->hci->add_pipe(msc->bulk_out);
     if (!msd_get_capacity(usbdev, &bs, &bc)) {
         strgdev->block_size = 0;
         strgdev->block_count = 0;
diff --git a/usb/ohci.c b/usb/ohci.c
index e4de271..e697fae 100644
--- a/usb/ohci.c
+++ b/usb/ohci.c
@@ -23,6 +23,7 @@
 #include <string.h>
 
 #include "cdi/io.h"
+#include "cdi/lists.h"
 #include "cdi/misc.h"
 #include "cdi/pci.h"
 
@@ -133,8 +134,8 @@ void ohci_init(struct cdi_device* cdi_hci)
         return;
     }
 
-    ohci->transfer_pool = mempool_create(32 * (1023 + sizeof(struct ohci_td)),
-        1023 + sizeof(struct ohci_td));
+    ohci->transfer_pool = mempool_create(32 * (1024 + sizeof(struct ohci_td)),
+        1024 + sizeof(struct ohci_td));
     if (ohci->transfer_pool == NULL) {
         dprintf("Transfer-Speicherpool konnte nicht erzeugt werden!\n");
         return;
@@ -224,6 +225,8 @@ void ohci_init(struct cdi_device* cdi_hci)
         ohci->memory->hc_rh_port_status[i] |= OHC_RP_CCS; //Deaktivieren
 
     }
+    ohci->ed_list = cdi_list_create();
+
     gen_hci->find_devices = &get_devices;
     gen_hci->activate_device = &activate_device;
     gen_hci->do_packet = &ohci_do_packet;
@@ -234,7 +237,117 @@ void ohci_init(struct cdi_device* cdi_hci)
 
 static int ohci_do_packet(struct usb_packet* packet)
 {
-    return USB_STALLED;
+    struct ohci_ed_desc* edsc;
+    struct ohci_td_desc* tdsc, * otdsc;
+    int i, cond;
+    struct ohci* ohci = (struct ohci*) packet->pipe->device->hci;
+    struct ohci_td* vtd;
+    uintptr_t ptd;
+
+    for (i = 0; (edsc = cdi_list_get(ohci->ed_list, i)) != NULL; i++) {
+        if ((edsc->function == packet->pipe->device->id) &&
+            (edsc->endpoint == packet->pipe->endpoint->endpoint_address))
+        {
+            break;
+        }
+    }
+    if (edsc == NULL) {
+        return USB_TIMEOUT; //Genau das würde passieren
+    }
+    if (mempool_get(ohci->transfer_pool, (void**) &vtd, &ptd) == -1) {
+        return USB_TRIVIAL_ERROR;
+    }
+    if ((packet->data != NULL) && (packet->type != PACKET_IN)) {
+        memcpy((void*) vtd + sizeof(struct ohci_td), packet->data,
+            packet->length);
+    }
+    vtd->rounding = 1; //Warum nicht
+    switch (packet->type) {
+        case PACKET_SETUP:
+            vtd->direction = OHC_TD_DIR_SETUP;
+            break;
+        case PACKET_IN:
+            vtd->direction = OHC_TD_DIR_IN;
+            break;
+        case PACKET_OUT:
+            vtd->direction = OHC_TD_DIR_OUT;
+            break;
+        default:
+            mempool_put(ohci->transfer_pool, vtd);
+            return USB_TRIVIAL_ERROR; //Hm, passt nicht ganz
+    }
+    vtd->di = 0;
+    vtd->toggle = 0x2 | packet->pipe->data_toggle;
+    vtd->error = 0;
+    vtd->condition = 15;
+    if ((packet->data == NULL) || !packet->length) {
+        vtd->current_buffer_pointer = 0;
+        vtd->buffer_end = 0;
+    } else {
+        vtd->current_buffer_pointer = ptd + sizeof(struct ohci_td);
+        vtd->buffer_end = ptd + sizeof(struct ohci_td) + packet->length - 1;
+    }
+    vtd->next_td = 0;
+    tdsc = malloc(sizeof(*tdsc));
+    if (tdsc == NULL) {
+        mempool_put(ohci->transfer_pool, vtd);
+        return USB_TRIVIAL_ERROR;
+    }
+    tdsc->virt = vtd;
+    tdsc->phys = ptd;
+    tdsc->endpoint = edsc;
+    if (cdi_list_size(edsc->transfers)) {
+        otdsc = cdi_list_get(edsc->transfers, cdi_list_size(
+                edsc->transfers) - 1);
+        otdsc->virt->next_td = ptd;
+    }
+    cdi_list_insert(edsc->transfers, cdi_list_size(edsc->transfers), tdsc);
+    if (!edsc->virt->td_queue_head) {
+        edsc->virt->td_queue_head = ptd;
+    }
+    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;
+    }
+    while (vtd->condition == 15) {
+        cdi_sleep_ms(1);
+    }
+    cond = vtd->condition;
+    if ((packet->data != NULL) && (packet->type == PACKET_IN)) {
+        memcpy(packet->data, (void*) vtd + sizeof(struct ohci_td),
+            packet->length);
+    }
+    if (vtd->next_td == 0) {
+        edsc->virt->td_queue_head = 0;
+    }
+    mempool_put(ohci->transfer_pool, vtd);
+    switch (cond) {
+        case 0x00:
+            return USB_NO_ERROR;
+        case 0x01:
+            return USB_CRC;
+        case 0x02:
+            return USB_BITSTUFF;
+        case 0x03: //Datatoggle-Fehler
+            return USB_CRC;
+        case 0x04:
+            return USB_STALLED;
+        case 0x05:
+            return USB_TIMEOUT;
+        case 0x06: //PID-Fehler
+        case 0x07: //Ebenso
+            return USB_CRC;
+        case 0x08:
+            return USB_BABBLE;
+        case 0x09: //Data underrun
+            return USB_CRC;
+        case 0x0C: //Buffer overrun
+        case 0x0D: //Buffer underrun
+            return USB_BUFFER_ERROR;
+        default: //Ã?hm...
+            return USB_CRC;
+    }
 }
 
 static cdi_list_t get_devices(struct hci* gen_hci)
@@ -282,12 +395,73 @@ static void ohci_reset_device(struct usb_device* device)
 
 static void ohci_establish_pipe(struct usb_pipe* pipe)
 {
+    struct ohci* ohci = (struct ohci*) pipe->device->hci;
+    struct ohci_ed* ved;
+    uintptr_t ped;
+    struct ohci_ed_desc* dsc;
+    //TODO Mehr als Control und Bulk
+    int iscontrol = 0;
+
+    if (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->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) {
+        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);
+
+    dprintf(
+        "%s-Endpoint %i (Gerät %i) hinzugefügt (%s)\n",
+        iscontrol ? "Control" : "Bulk", ved->endpoint, ved->function,
+        (ved->direction ==
+         OHC_ED_DIR_TD) ? "IN/OUT/SETUP" : ((ved->direction ==
+                                             OHC_ED_DIR_IN) ? "IN" : "OUT"));
+
+    if (iscontrol) {
+        ohci->memory->hc_control_head_ed = ped;
+    } else {
+        ohci->memory->hc_bulk_head_ed = ped;
+    }
 }
 
 static void ohci_handler(struct cdi_device* dev)
 {
     struct ohci* ohci = (struct ohci*) ((struct cdi_hci*) dev)->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;
 
@@ -296,12 +470,29 @@ static void ohci_handler(struct cdi_device* dev)
     }
     if (status & OHC_INT_UE) {
         dprintf("Schwerer HC-Fehler.\n");
-        //TODO HC anhalten
+        ohci->memory->hc_command_status |= OHC_CMST_RESET;
         done |= OHC_INT_UE;
     }
     if (status & OHC_INT_SF) { //Ã?hm... Na ja.
         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);
+                    break;
+                }
+            }
+            if (tdsc != NULL) { //Gefunden
+                break;
+            }
+        }
+    }
 
     if (status & ~done) {
         dprintf("Nicht behandelter Interrupt: 0x%08X\n", status & ~done);
diff --git a/usb/uhci.c b/usb/uhci.c
index cb7b857..405a879 100644
--- a/usb/uhci.c
+++ b/usb/uhci.c
@@ -116,8 +116,8 @@ void uhci_init(struct cdi_device* cdi_hci)
         return;
     }
     request_size =
-        // Maximale Paketlänge (Full Speed)
-        1023 +
+        // Maximale Paketlänge (Full Speed) ist 1023, Alignment au�erdem
+        1024 +
         // Transferdeskriptor
         sizeof(struct uhci_td);
 
-- 
1.6.3.3