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

[cdi-devel] [PATCH 2/2] [usb] No memory error, remove STALL



From: Max Reitz <max@xxxxxxxxxx>

* This is a REALLY ugly patch, though it works. At startup, the
  driver allocates about 1 MB of memory per UHC and uses that for the
  queue heads, the transfer descriptors and the packet buffer maximum
  packet size: 1 kB).
+ STALL is now removed via the ClearEndpointFeature(ENDPOINT_STALL)
  request.

Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
 usb/include/uhci.h |   24 ++++++---
 usb/include/usb.h  |    1 -
 usb/main.c         |   18 +++----
 usb/msd.c          |   45 +++++++++++------
 usb/uhci.c         |  143 ++++++++++++++++++++++++++++-----------------------
 5 files changed, 131 insertions(+), 100 deletions(-)

diff --git a/usb/include/uhci.h b/usb/include/uhci.h
index 240619b..d1a7c61 100644
--- a/usb/include/uhci.h
+++ b/usb/include/uhci.h
@@ -53,17 +53,10 @@
 #define RPORT_DEVICE   0x0001
 
 
-struct uhci {
-    struct hci gen_hci;
-    uint16_t pbase;
-    uintptr_t phys_frame_list;
-    uint32_t* frame_list;
-    int root_ports;
-};
-
 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 {
@@ -103,4 +96,19 @@ struct transfer {
     volatile int error;
 };
 
+struct uhci {
+    struct hci gen_hci;
+    uint16_t 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 uhci_td* transfer_descs;
+    struct uhci_td* phys_transfer_descs;
+    //FIXME Das tut einfach nur weh.
+    void** data_buffers;
+    uintptr_t* phys_data_buffers;
+};
+
 #endif
diff --git a/usb/include/usb.h b/usb/include/usb.h
index 9a94b2e..459a340 100644
--- a/usb/include/usb.h
+++ b/usb/include/usb.h
@@ -251,7 +251,6 @@ struct usb_device {
     struct config_desc* config;
     struct interface_desc* interface;
     struct class_data* classd;
-    int stalled;
     int locked;
     int expects;
     int data_toggle;
diff --git a/usb/main.c b/usb/main.c
index 7250092..b09bfba 100644
--- a/usb/main.c
+++ b/usb/main.c
@@ -52,6 +52,9 @@ static int usb_dev_ids = 1;
 
 static void usb_init(void);
 static void enumerate_hub(struct usb_device* usbdev);
+static void* do_control(struct usb_device* device, int direction, void* buffer,
+    int length, int rtype, int recipient, int request, int value,
+    int index);
 
 #ifdef CDI_STANDALONE
 int main(void)
@@ -119,11 +122,6 @@ int usb_do_packet(struct usb_device* device, struct usb_packet* packet)
     //dprintf("TOD: %s\n", tod_name[tod_short]);
     device->expects = next_data_type[tod_short];
     packet->type &= 0xFF;
-    if (device->stalled) {
-        //TODO: Irgendwie entstallen, evtl.? oO
-        dprintf("Zugriff auf stalled-Gerät verweigert.\n");
-        return USB_STALLED;
-    }
 
     struct usb_packet send_packet = {
         .type      = packet->type,
@@ -152,10 +150,9 @@ int usb_do_packet(struct usb_device* device, struct usb_packet* packet)
             printf("[usb] ENDPOINT %i DES GERÃ?TS %i STALLED!\n",
                 packet->endpoint->endpoint_address,
                 device->id);
-            for (;;) {
-            }
-            device->stalled = 1;
-            break;
+            do_control(device, HOST_TO_DEV | NO_DATA, NULL, 0, STD_REQUEST,
+                REC_ENDPOINT, CLEAR_FEATURE, 0,
+                packet->endpoint->endpoint_address);
         }
     }
     return error;
@@ -171,9 +168,11 @@ static void* do_control(struct usb_device* device, int direction, void* buffer,
 
     no_data = direction & NO_DATA;
     direction &= 0x80;
+
     if ((direction != HOST_TO_DEV) || !length || (buffer == NULL)) {
         buffer = malloc(length);
     }
+
     setup->request_type = direction | (rtype & 0x60) | (recipient & 0x1F);
     setup->request = request;
     setup->value = value;
@@ -315,7 +314,6 @@ static void enum_device(struct usb_device* usbdev)
     int i, id;
 
     //Gerät und EP0 initialisieren
-    usbdev->stalled = 0;
     usbdev->locked = 0;
     usbdev->expects = USB_TOD_SETUP | USB_TOD_COMMAND;
     usbdev->data_toggle = 0;
diff --git a/usb/msd.c b/usb/msd.c
index 6fb8a42..415657e 100644
--- a/usb/msd.c
+++ b/usb/msd.c
@@ -366,6 +366,8 @@ static inline int tsl(volatile int* variable)
     return rval;
 }
 
+#define MAX_ACCESS_BLOCKS 32
+
 static uint32_t msd_read(struct usb_device* usbdev, uint32_t lba,
     uint16_t sectors, void* buffer,
     size_t length);
@@ -379,6 +381,7 @@ static int msd_cdi_read(struct cdi_storage_device* strgdev, uint64_t start,
     int i;
 #endif
     struct usb_device* usbdev = ((struct cdi_msd*) strgdev)->usb_device;
+    int j, bbs;
 
     fdprintf("read(%i, %i)\n", (int) start, (int) count);
     start += ((struct cdi_msd*) strgdev)->offset;
@@ -392,17 +395,23 @@ static int msd_cdi_read(struct cdi_storage_device* strgdev, uint64_t start,
         __asm__ __volatile__ ("hlt");
 #endif
     }
+    for (j = 0; j < count; j += MAX_ACCESS_BLOCKS) {
 #ifdef WAIT_FOR_MSD_READY
-    for (i = 0; !msd_ready(usbdev) && (i < 10); i++) {
-        cdi_sleep_ms(20);
-    }
+        for (i = 0; !msd_ready(usbdev) && (i < 10); i++) {
+            cdi_sleep_ms(20);
+        }
 #endif
-    error = msd_read(usbdev, start, count, buffer, count * bs);
-    if (error != USB_NO_ERROR) {
-        dprintf("Lesefehler 0x%X bei Block %lld.\n", error, start);
-        usbdev->locked = 0;
-        return -1;
+        bbs = (count - j > MAX_ACCESS_BLOCKS) ? MAX_ACCESS_BLOCKS : count - j;
+        _dprintf("/");
+        error = msd_read(usbdev, start + j, bbs, buffer + j * bs, bbs * bs);
+        _dprintf("\\");
+        if (error != USB_NO_ERROR) {
+            dprintf("Lesefehler 0x%X bei Block %lld.\n", error, start + j);
+            usbdev->locked = 0;
+            return -1;
+        }
     }
+    _dprintf("\n");
     usbdev->locked = 0;
     return 0;
 }
@@ -476,6 +485,7 @@ static int msd_cdi_write(struct cdi_storage_device* strgdev, uint64_t start,
     int i;
 #endif
     struct usb_device* usbdev = ((struct cdi_msd*) strgdev)->usb_device;
+    int j, bbs;
 
     fdprintf("write(%i, %i)\n", (int) start, (int) count);
     start += ((struct cdi_msd*) strgdev)->offset;
@@ -489,16 +499,19 @@ static int msd_cdi_write(struct cdi_storage_device* strgdev, uint64_t start,
         __asm__ __volatile__ ("hlt");
 #endif
     }
+    for (j = 0; j < count; j += MAX_ACCESS_BLOCKS) {
 #ifdef WAIT_FOR_MSD_READY
-    for (i = 0; !msd_ready(usbdev) && (i < 10); i++) {
-        cdi_sleep_ms(20);
-    }
+        for (i = 0; !msd_ready(usbdev) && (i < 10); i++) {
+            cdi_sleep_ms(20);
+        }
 #endif
-    error = msd_write(usbdev, start, count, buffer, count * bs);
-    if (error != USB_NO_ERROR) {
-        dprintf("Schreibfehler 0x%X bei Block %i.\n", error, start);
-        usbdev->locked = 0;
-        return -1;
+        bbs = (count - j > MAX_ACCESS_BLOCKS) ? MAX_ACCESS_BLOCKS : count - j;
+        error = msd_write(usbdev, start + j, bbs, buffer + j * bs, bbs * bs);
+        if (error != USB_NO_ERROR) {
+            dprintf("Schreibfehler 0x%X bei Block %i.\n", error, start + j);
+            usbdev->locked = 0;
+            return -1;
+        }
     }
     usbdev->locked = 0;
     return 0;
diff --git a/usb/uhci.c b/usb/uhci.c
index d302364..fb1fdb3 100644
--- a/usb/uhci.c
+++ b/usb/uhci.c
@@ -95,7 +95,6 @@ void uhci_init(struct cdi_device* cdi_hci)
     struct cdi_pci_resource* res;
     int i, size = 0x14;
 
-    //cdi_pci_alloc_ioports(gen_hci->pcidev);
     uhci->pbase = 0;
     for (i = 0; (res = cdi_list_get(gen_hci->pcidev->resources, i)) != NULL;
          i++)
@@ -120,16 +119,45 @@ void uhci_init(struct cdi_device* cdi_hci)
         dprintf("Frame List konnte nicht allociert werden!\n");
         return;
     }
+    if (cdi_alloc_phys_mem(sizeof(*uhci->queue_heads) * 1024,
+            (void**) &uhci->queue_heads,
+            (void**) &uhci->phys_queue_heads) == -1)
+    {
+        dprintf("QH-Speicher konnte nicht allociert werden!\n");
+        return;
+    }
+    if (cdi_alloc_phys_mem(sizeof(*uhci->transfer_descs) * 1024,
+            (void**) &uhci->transfer_descs,
+            (void**) &uhci->phys_transfer_descs) == -1)
+    {
+        dprintf("TD-Speicher konnte nicht allociert werden!\n");
+        return;
+    }
+    //FIXME Das tut dermaÃ?en weh.
+    uhci->data_buffers = malloc(sizeof(void*) * 1024);
+    uhci->phys_data_buffers = malloc(sizeof(uintptr_t) * 1024);
+    for (i = 0; i < 1024; i++) {
+        if (cdi_alloc_phys_mem(1024, &uhci->data_buffers[i],
+                (void**) &uhci->phys_data_buffers[i]) == -1)
+        {
+            dprintf("Paketspeicher konnte nicht allociert werden!\n");
+            return;
+        }
+    }
     cdi_register_irq(gen_hci->pcidev->irq, &uhci_handler, cdi_hci);
     uhci->root_ports = (size - 0x10) >> 1;
-    if (uhci->root_ports > 7) {                         //Laut Linuxkernel ist das so "weird", dass da was nicht stimmen kann...
+    if (uhci->root_ports > 7) { //Laut Linuxkernel ist das so "weird", dass da was nicht stimmen kann...
         uhci->root_ports = 2;
     }
     dprintf("UHC mit I/O 0x%04X (%i Ports) und IRQ %i\n", uhci->pbase,
         uhci->root_ports,
         gen_hci->pcidev->irq);
     for (i = 0; i < 1024; i++) {
-        uhci->frame_list[i] = 1; //Invalid
+        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;
     }
     dprintf("Resetten...\n");
     //HC zurücksetzen
@@ -229,80 +257,65 @@ static volatile int locked = 0;
 static int uhci_do_packet(struct usb_device* usbdev, struct usb_packet* packet)
 {
     struct uhci* uhci = (struct uhci*) usbdev->hci;
-    struct uhci_td* td;
-    struct uhci_qh* qh;
-    uintptr_t ptd, pqh;
-    struct transfer* addr;
-    int timeout;
-    void* data;
-    uintptr_t phys_data;
-
-    int frame = (usbdev->hci->get_frame(usbdev->hci) + 3) & 0x3FF;
-
-    if (cdi_alloc_phys_mem(sizeof(struct uhci_td), (void**) &td,
-            (void**) &ptd) == -1)
-    {
-        return USB_TRIVIAL_ERROR;
+    struct uhci_td td;
+    struct transfer addr;
+    int timeout, frame;
+
+    memset(&td, 0, sizeof(td));
+    td.next = 1; //Invalid
+    td.active = 1;
+    td.ioc = 1;
+    td.data_toggle = usbdev->data_toggle;
+    td.low_speed = usbdev->low_speed;
+    td.errors = 1;
+    td.pid = packet->type;
+    td.device = usbdev->id;
+    td.endpoint = packet->endpoint->endpoint_address & 0x07;
+    td.maxlen = packet->length ? packet->length - 1 : 0x7FF;
+    while (tsl(&locked)) {
+#ifndef CDI_STANDALONE
+        __asm__ __volatile__ ("hlt");
+#endif
     }
-    if (cdi_alloc_phys_mem(sizeof(struct uhci_qh), (void**) &qh,
-            (void**) &pqh) == -1)
-    {
-        return USB_TRIVIAL_ERROR;
+    frame = (cdi_inw(uhci->pbase + UHCI_FRNUM) + 5) & 0x3FF;
+    while (!(uhci->queue_heads[frame].transfer & 1)) {
+        frame++;
+        frame &= 0x3FF;
     }
-
-    if (packet->length == 0) {
-        data = NULL;
-        phys_data = 0;
+    if (!packet->length) {
+        td.buffer = 0;
     } else {
-        if (cdi_alloc_phys_mem(packet->length, &data,
-                (void**) &phys_data) == -1)
-        {
-            return USB_TRIVIAL_ERROR;
-        }
+        td.buffer = uhci->phys_data_buffers[frame];
         if (packet->type != PACKET_IN) {
-            memcpy(data, packet->data, packet->length);
+            memcpy(uhci->data_buffers[frame], packet->data, packet->length);
         }
     }
-
-    while (tsl(&locked)) {
-#ifndef CDI_STANDALONE
-        __asm__ __volatile__ ("hlt");
-#endif
-    }
-    qh->next = 1; //Invalid
-    qh->transfer = ptd;
-    memset(td, 0, sizeof(struct uhci_td));
-    td->next = 1; //Invalid
-    td->active = 1;
-    td->ioc = 1;
-    td->data_toggle = usbdev->data_toggle;
-    td->low_speed = usbdev->low_speed;
-    td->errors = 1;
-    td->pid = packet->type;
-    td->device = usbdev->id;
-    td->endpoint = packet->endpoint->endpoint_address & 0x07;
-    td->maxlen = packet->length ? packet->length - 1 : 0x7FF;
-    td->buffer = phys_data;
-    addr = malloc(sizeof(struct transfer));
-    addr->virt = td;
-    addr->phys = ptd;
-    addr->error = 0xFFFF;
-    cdi_list_push(active_transfers, addr);
-    uhci->frame_list[frame] = pqh | 2;
-    for (timeout = 0; !(qh->transfer & 1) && (timeout < 1000); timeout++) {
+    memcpy(&uhci->transfer_descs[frame], &td, sizeof(td));
+    uhci->queue_heads[frame].transfer =
+        (uintptr_t) &uhci->phys_transfer_descs[frame];
+    addr.virt = &uhci->transfer_descs[frame];
+    addr.phys = (uintptr_t) &uhci->phys_transfer_descs[frame];
+    addr.error = 0xFFFF;
+    cdi_list_push(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)) {
+    while ((timeout < 1000) && (addr.error == 0xFFFF)) {
 #ifndef CDI_STANDALONE
         __asm__ __volatile__ ("hlt");
 #endif
     }
-    uhci->frame_list[frame] = 1;
-    locked = 0;
     if (packet->type == PACKET_IN) {
-        memcpy(packet->data, data, packet->length);
+        memcpy(packet->data, uhci->data_buffers[frame], packet->length);
+    }
+    if (addr.error == 0xFFFF) {
+        addr.error = USB_TIMEOUT;
     }
-    return addr->error;
+    return addr.error;
 }
 
 static void uhci_handler(struct cdi_device* cdi_hci)
@@ -312,7 +325,7 @@ static void uhci_handler(struct cdi_device* cdi_hci)
     struct transfer* addr;
     struct uhci_td* td;
 
-    if (!status) {                         //Also, von hier kommt der IRQ nicht.
+    if (!status) { //Also, von hier kommt der IRQ nicht.
         return;
     }
     if (status & ~0x0001) {
@@ -320,7 +333,7 @@ static void uhci_handler(struct cdi_device* cdi_hci)
     }
     if (status & 0x10) {
         printf("[uhci] SCHWERWIEGENDER FEHLER - HC WIRD ANGEHALTEN\n");
-        cdi_outw(uhci->pbase + UHCI_USBCMD, MAXP | HCRESET);    //FU!
+        cdi_outw(uhci->pbase + UHCI_USBCMD, MAXP | HCRESET); //FU!
     } else {
         for (i = 0; (addr = cdi_list_get(active_transfers, i)) != NULL; i++) {
             td = addr->virt;
-- 
1.6.3.3