qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
@ 2007-10-22 10:19 Arnon Gilboa
  2007-10-22 10:29 ` Itamar Heim
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Arnon Gilboa @ 2007-10-22 10:19 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 337 bytes --]

Hi,

The attached patch adds isochronous transfers support to the OHCI
emulation, similarly to the UHCI patch pushed two weeks ago.

In order to use ohci instead of uhci, replace the following line in
pc.c:
usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);

With:
usb_ohci_init_pci(pci_bus, 3, piix3_devfn + 2);

Any comments?

[-- Attachment #2: usb-ohci-iso.patch --]
[-- Type: application/octet-stream, Size: 15880 bytes --]

diff --git a/qemu-head/hw/usb-ohci.c b/qemu/hw/usb-ohci.c
index 2d5af7d..91da16b 100755
--- a/qemu-head/hw/usb-ohci.c
+++ b/qemu/hw/usb-ohci.c
@@ -32,6 +32,7 @@
 //#define DEBUG_OHCI
 /* Dump packet contents.  */
 //#define DEBUG_PACKET
+//#define DEBUG_ISOCH
 /* This causes frames to occur 1000x slower */
 //#define OHCI_TIME_WARP 1
 
@@ -132,8 +133,8 @@ static void ohci_bus_stop(OHCIState *ohci);
 #define OHCI_ED_S         (1<<13)
 #define OHCI_ED_K         (1<<14)
 #define OHCI_ED_F         (1<<15)
-#define OHCI_ED_MPS_SHIFT 7
-#define OHCI_ED_MPS_MASK  (0xf<<OHCI_ED_FA_SHIFT)
+#define OHCI_ED_MPS_SHIFT 16
+#define OHCI_ED_MPS_MASK  (0x7ff<<OHCI_ED_MPS_SHIFT)
 
 /* Flags in the head field of an Endpoint Desciptor.  */
 #define OHCI_ED_H         1
@@ -152,6 +153,22 @@ static void ohci_bus_stop(OHCIState *ohci);
 #define OHCI_TD_CC_SHIFT  28
 #define OHCI_TD_CC_MASK   (0xf<<OHCI_TD_CC_SHIFT)
 
+/* Bitfields for the first word of an Isochronous Transfer Desciptor.  */
+/* CC & DI - same as in the General Transfer Desciptor */
+#define OHCI_TD_SF_SHIFT  0
+#define OHCI_TD_SF_MASK   (0xffff<<OHCI_TD_SF_SHIFT)
+#define OHCI_TD_FC_SHIFT  24
+#define OHCI_TD_FC_MASK   (7<<OHCI_TD_FC_SHIFT)
+
+/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */
+#define OHCI_TD_PSW_CC_SHIFT 12
+#define OHCI_TD_PSW_CC_MASK  (0xf<<OHCI_TD_PSW_CC_SHIFT)
+#define OHCI_TD_PSW_SIZE_SHIFT 0
+#define OHCI_TD_PSW_SIZE_MASK  (0xfff<<OHCI_TD_PSW_SIZE_SHIFT)
+
+#define OHCI_PAGE_MASK    0xfffff000
+#define OHCI_OFFSET_MASK  0xfff
+
 #define OHCI_DPTR_MASK    0xfffffff0
 
 #define OHCI_BM(val, field) \
@@ -178,6 +195,15 @@ struct ohci_td {
     uint32_t be;
 };
 
+/* Isochronous transfer descriptor */
+struct ohci_iso_td {
+    uint32_t flags;
+    uint32_t bp;
+    uint32_t next;
+    uint32_t be;
+    uint16_t offset[8];
+};
+
 #define USB_HZ                      12000000
 
 /* OHCI Local stuff */
@@ -431,6 +457,11 @@ static inline int ohci_read_td(uint32_t addr, struct ohci_td *td)
     return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
 }
 
+static inline int ohci_read_iso_td(uint32_t addr, struct ohci_iso_td *td)
+{
+    return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
 static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed)
 {
     return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
@@ -441,6 +472,11 @@ static inline int ohci_put_td(uint32_t addr, struct ohci_td *td)
     return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
 }
 
+static inline int ohci_put_iso_td(uint32_t addr, struct ohci_iso_td *td)
+{
+    return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
 /* Read/Write the contents of a TD from/to main memory.  */
 static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
 {
@@ -459,16 +495,270 @@ static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
     cpu_physical_memory_rw(ptr, buf, len - n, write);
 }
 
-static void ohci_process_lists(OHCIState *ohci);
+/* Read/Write the contents of an ISO TD from/to main memory.  */
+static void ohci_copy_iso_td(uint32_t start_addr, uint32_t end_addr,
+                             uint8_t *buf, int len, int write)
+{
+    uint32_t ptr;
+    uint32_t n;
+
+    ptr = start_addr;
+    n = 0x1000 - (ptr & 0xfff);
+    if (n > len)
+        n = len;
+    cpu_physical_memory_rw(ptr, buf, n, write);
+    if (n == len)
+        return;
+    ptr = end_addr & ~0xfffu;
+    buf += n;
+    cpu_physical_memory_rw(ptr, buf, len - n, write);
+}
+
+static void ohci_process_lists(OHCIState *ohci, int completion);
 
-static void ohci_async_complete_packet(USBPacket * packet, void *opaque)
+static void ohci_async_complete_packet(USBPacket *packet, void *opaque)
 {
     OHCIState *ohci = opaque;
 #ifdef DEBUG_PACKET
     dprintf("Async packet complete\n");
 #endif
     ohci->async_complete = 1;
-    ohci_process_lists(ohci);
+    ohci_process_lists(ohci, 1);
+}
+
+#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
+
+static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
+                               int completion)
+{
+    int dir;
+    size_t len = 0;
+    char *str = NULL;
+    int pid;
+    int ret;
+    int i;
+    USBDevice *dev;
+    struct ohci_iso_td iso_td;
+    uint32_t addr;
+    uint16_t starting_frame;
+    int16_t relative_frame_number;
+    int frame_count;
+    uint32_t start_offset, next_offset, end_offset = 0;
+    uint32_t start_addr, end_addr;
+
+    addr = ed->head & OHCI_DPTR_MASK;
+
+    if (!ohci_read_iso_td(addr, &iso_td)) {
+        printf("usb-ohci: ISO_TD read error at %x\n", addr);
+        return 0;
+    }
+
+    starting_frame = OHCI_BM(iso_td.flags, TD_SF);
+    frame_count = OHCI_BM(iso_td.flags, TD_FC);
+    relative_frame_number = USUB(ohci->frame_number, starting_frame); 
+
+#ifdef DEBUG_ISOCH
+    printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "frame_number 0x%.8x starting_frame 0x%.8x\n"
+           "frame_count  0x%.8x relative %d\n"
+           "di 0x%.8x cc 0x%.8x\n",
+           ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK,
+           iso_td.flags, iso_td.bp, iso_td.next, iso_td.be,
+           iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3],
+           iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7],
+           ohci->frame_number, starting_frame, 
+           frame_count, relative_frame_number,         
+           OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC));
+#endif
+
+    if (relative_frame_number < 0) {
+        dprintf("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number);
+        return 1;
+    } else if (relative_frame_number > frame_count) {
+        /* ISO TD expired - retire the TD to the Done Queue and continue with
+           the next ISO TD of the same ED */
+        dprintf("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, 
+               frame_count);
+        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+        ed->head &= ~OHCI_DPTR_MASK;
+        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+        iso_td.next = ohci->done;
+        ohci->done = addr;
+        i = OHCI_BM(iso_td.flags, TD_DI);
+        if (i < ohci->done_count)
+            ohci->done_count = i;
+        ohci_put_iso_td(addr, &iso_td);        
+        return 0;
+    }
+
+    dir = OHCI_BM(ed->flags, ED_D);
+    switch (dir) {
+    case OHCI_TD_DIR_IN:
+        str = "in";
+        pid = USB_TOKEN_IN;
+        break;
+    case OHCI_TD_DIR_OUT:
+        str = "out";
+        pid = USB_TOKEN_OUT;
+        break;
+    case OHCI_TD_DIR_SETUP:
+        str = "setup";
+        pid = USB_TOKEN_SETUP;
+        break;
+    default:
+        printf("usb-ohci: Bad direction %d\n", dir);
+        return 1;
+    }
+
+    if (!iso_td.bp || !iso_td.be) {
+        printf("usb-ohci: ISO_TD bp 0x%.8x be 0x%.8x\n", iso_td.bp, iso_td.be);
+        return 1;
+    }
+
+    start_offset = iso_td.offset[relative_frame_number];
+    next_offset = iso_td.offset[relative_frame_number + 1];
+
+    if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || 
+        ((relative_frame_number < frame_count) && 
+         !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) {
+        printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n",
+               start_offset, next_offset);
+        return 1;
+    }
+
+    if ((relative_frame_number < frame_count) && (start_offset > next_offset)) {
+        printf("usb-ohci: ISO_TD start_offset=0x%.8x > next_offset=0x%.8x\n",
+                start_offset, next_offset);
+        return 1;
+    }
+
+    if ((start_offset & 0x1000) == 0) {
+        start_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+            (start_offset & OHCI_OFFSET_MASK);
+    } else {
+        start_addr = (iso_td.be & OHCI_PAGE_MASK) |
+            (start_offset & OHCI_OFFSET_MASK);
+    }
+
+    if (relative_frame_number < frame_count) {
+        end_offset = next_offset - 1;
+        if ((end_offset & 0x1000) == 0) {
+            end_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+                (end_offset & OHCI_OFFSET_MASK);
+        } else {
+            end_addr = (iso_td.be & OHCI_PAGE_MASK) |
+                (end_offset & OHCI_OFFSET_MASK);
+        }
+    } else {
+        /* Last packet in the ISO TD */
+        end_addr = iso_td.be;
+    }
+
+    if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) {
+        len = (end_addr & OHCI_OFFSET_MASK) + 0x1001
+            - (start_addr & OHCI_OFFSET_MASK);
+    } else {
+        len = end_addr - start_addr + 1;
+    }
+
+    if (len && dir != OHCI_TD_DIR_IN) {
+        ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, len, 0);
+    }
+
+    if (completion) {
+        ret = ohci->usb_packet.len;
+    } else {
+        ret = USB_RET_NODEV;
+        for (i = 0; i < ohci->num_ports; i++) {
+            dev = ohci->rhport[i].port.dev;
+            if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+                continue;
+            ohci->usb_packet.pid = pid;
+            ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
+            ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
+            ohci->usb_packet.data = ohci->usb_buf;
+            ohci->usb_packet.len = len;
+            ohci->usb_packet.complete_cb = ohci_async_complete_packet;
+            ohci->usb_packet.complete_opaque = ohci;
+            ret = dev->handle_packet(dev, &ohci->usb_packet);
+            if (ret != USB_RET_NODEV)
+                break;
+        }
+    
+        if (ret == USB_RET_ASYNC) {
+            return 1;
+        }
+    }
+
+#ifdef DEBUG_ISOCH
+    printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
+           start_offset, end_offset, start_addr, end_addr, str, len, ret);
+#endif
+
+    /* Writeback */
+    if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
+        /* IN transfer succeeded */
+        ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, ret, 1);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                    OHCI_CC_NOERROR);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret);
+    } else if (dir == OHCI_TD_DIR_OUT && ret == len) {
+        /* OUT transfer succeeded */
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                    OHCI_CC_NOERROR);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0);
+    } else {
+        if (ret > len) {
+            printf("usb-ohci: DataOverrun %d > %zu\n", ret, len);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                        OHCI_CC_DATAOVERRUN);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                        len);
+        } else if (ret >= 0) {
+            printf("usb-ohci: DataUnderrun %d\n", ret);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                        OHCI_CC_DATAUNDERRUN);
+        } else {
+            switch (ret) {
+            case USB_RET_NODEV:
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_DEVICENOTRESPONDING);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                            0);
+                break;
+            case USB_RET_NAK:
+            case USB_RET_STALL:
+                printf("usb-ohci: got NAK/STALL %d\n", ret);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_STALL);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                            0);
+                break;
+            default:
+                printf("usb-ohci: Bad device response %d\n", ret);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_UNDEXPETEDPID);
+                break;
+            }
+        }
+    }
+
+    if (relative_frame_number == frame_count) {
+        /* Last data packet of ISO TD - retire the TD to the Done Queue */
+        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR);
+        ed->head &= ~OHCI_DPTR_MASK;
+        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+        iso_td.next = ohci->done;
+        ohci->done = addr;
+        i = OHCI_BM(iso_td.flags, TD_DI);
+        if (i < ohci->done_count)
+            ohci->done_count = i;
+    }
+    ohci_put_iso_td(addr, &iso_td);
+    return 1;
 }
 
 /* Service a transport descriptor.
@@ -671,7 +961,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
 }
 
 /* Service an endpoint list.  Returns nonzero if active TD were found.  */
-static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
+static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
 {
     struct ohci_ed ed;
     uint32_t next_ed;
@@ -702,10 +992,6 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
             continue;
         }
 
-        /* Skip isochronous endpoints.  */
-        if (ed.flags & OHCI_ED_F)
-          continue;
-
         while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
 #ifdef DEBUG_PACKET
             dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u "
@@ -719,8 +1005,14 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
 #endif
             active = 1;
 
-            if (ohci_service_td(ohci, &ed))
-                break;
+            if ((ed.flags & OHCI_ED_F) == 0) {
+                if (ohci_service_td(ohci, &ed))
+                    break;
+            } else {
+                /* Handle isochronous endpoints */
+                if (ohci_service_iso_td(ohci, &ed, completion))
+                    break;
+            }
         }
 
         ohci_put_ed(cur, &ed);
@@ -738,19 +1030,19 @@ static void ohci_sof(OHCIState *ohci)
 }
 
 /* Process Control and Bulk lists.  */
-static void ohci_process_lists(OHCIState *ohci)
+static void ohci_process_lists(OHCIState *ohci, int completion)
 {
     if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
         if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head)
           dprintf("usb-ohci: head %x, cur %x\n", ohci->ctrl_head, ohci->ctrl_cur);
-        if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) {
+        if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
             ohci->ctrl_cur = 0;
             ohci->status &= ~OHCI_STATUS_CLF;
         }
     }
 
     if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
-        if (!ohci_service_ed_list(ohci, ohci->bulk_head)) {
+        if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
             ohci->bulk_cur = 0;
             ohci->status &= ~OHCI_STATUS_BLF;
         }
@@ -770,7 +1062,7 @@ static void ohci_frame_boundary(void *opaque)
         int n;
 
         n = ohci->frame_number & 0x1f;
-        ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]));
+        ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
     }
 
     /* Cancel all pending packets if either of the lists has been disabled.  */
@@ -780,7 +1072,7 @@ static void ohci_frame_boundary(void *opaque)
         ohci->async_td = 0;
     }
     ohci->old_ctl = ohci->ctl;
-    ohci_process_lists(ohci);
+    ohci_process_lists(ohci, 0);
 
     /* Frame boundary, so do EOF stuf here */
     ohci->frt = ohci->fit;
@@ -1206,6 +1498,9 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
         ohci_set_frame_interval(ohci, val);
         break;
 
+    case 15: /* HcFmNumber */
+        break;
+
     case 16: /* HcPeriodicStart */
         ohci->pstart = val & 0xffff;
         break;

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* RE: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
  2007-10-22 10:19 [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support Arnon Gilboa
@ 2007-10-22 10:29 ` Itamar Heim
  2007-10-22 12:38   ` Hetz Ben Hamo
  2007-10-22 12:41   ` Arnon Gilboa
  2007-10-22 10:44 ` Dor Laor
  2007-10-22 11:52 ` Paul Brook
  2 siblings, 2 replies; 9+ messages in thread
From: Itamar Heim @ 2007-10-22 10:29 UTC (permalink / raw)
  To: qemu-devel

So this is isochronous for usb 2.0?

-----Original Message-----
From: qemu-devel-bounces+itamarh=qumranet.com@nongnu.org
[mailto:qemu-devel-bounces+itamarh=qumranet.com@nongnu.org] On Behalf Of
Arnon Gilboa
Sent: Monday, October 22, 2007 12:19 PM
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers
support

Hi,

The attached patch adds isochronous transfers support to the OHCI
emulation, similarly to the UHCI patch pushed two weeks ago.

In order to use ohci instead of uhci, replace the following line in
pc.c:
usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);

With:
usb_ohci_init_pci(pci_bus, 3, piix3_devfn + 2);

Any comments?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
  2007-10-22 10:19 [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support Arnon Gilboa
  2007-10-22 10:29 ` Itamar Heim
@ 2007-10-22 10:44 ` Dor Laor
  2007-10-22 10:50   ` Arnon Gilboa
  2007-10-22 11:52 ` Paul Brook
  2 siblings, 1 reply; 9+ messages in thread
From: Dor Laor @ 2007-10-22 10:44 UTC (permalink / raw)
  To: qemu-devel

Arnon Gilboa wrote:
>
> Hi,
>
> The attached patch adds isochronous transfers support to the OHCI
> emulation, similarly to the UHCI patch pushed two weeks ago.
>
> In order to use ohci instead of uhci, replace the following line in
> pc.c:
> usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
>
> With:
> usb_ohci_init_pci(pci_bus, 3, piix3_devfn + 2);
>
> Any comments?
>
What about making it dynamically set by cmdline?
Dor

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
  2007-10-22 10:44 ` Dor Laor
@ 2007-10-22 10:50   ` Arnon Gilboa
  2007-10-22 11:29     ` Michal Schulz
  0 siblings, 1 reply; 9+ messages in thread
From: Arnon Gilboa @ 2007-10-22 10:50 UTC (permalink / raw)
  To: Dor Laor, qemu-devel

Good idea 

-----Original Message-----
From: qemu-devel-bounces+arnong=qumranet.com@nongnu.org
[mailto:qemu-devel-bounces+arnong=qumranet.com@nongnu.org] On Behalf Of
Dor Laor
Sent: Monday, October 22, 2007 12:45 PM
To: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers
support

Arnon Gilboa wrote:
>
> Hi,
>
> The attached patch adds isochronous transfers support to the OHCI 
> emulation, similarly to the UHCI patch pushed two weeks ago.
>
> In order to use ohci instead of uhci, replace the following line in
> pc.c:
> usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
>
> With:
> usb_ohci_init_pci(pci_bus, 3, piix3_devfn + 2);
>
> Any comments?
>
What about making it dynamically set by cmdline?
Dor

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
  2007-10-22 10:50   ` Arnon Gilboa
@ 2007-10-22 11:29     ` Michal Schulz
  0 siblings, 0 replies; 9+ messages in thread
From: Michal Schulz @ 2007-10-22 11:29 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 370 bytes --]

On Monday 22 October 2007, Arnon Gilboa wrote:

> Good idea

Let me quote my ancient email (sent on this list on 29.03.2007 23:34):

> Hello. I've made small patch for qemu, which adds a new command line
> option - > the "-ohci". It instructs qemu that the OHCI controller should be
> emulated instead of the default UHCI. What do you think about it?

-- 
Michal Schulz

[-- Attachment #2: qemu-ohci.patch --]
[-- Type: text/x-diff, Size: 2536 bytes --]

diff -Naur qemu-0.9.0/hw/pc.c qemu-0.9.0-ohci-patch/hw/pc.c
--- qemu-0.9.0/hw/pc.c	2007-02-06 00:01:54.000000000 +0100
+++ qemu-0.9.0-ohci-patch/hw/pc.c	2007-03-29 15:43:18.000000000 +0200
@@ -695,7 +695,10 @@
     cmos_init(ram_size, boot_device, bs_table);
 
     if (pci_enabled && usb_enabled) {
-        usb_uhci_init(pci_bus, piix3_devfn + 2);
+        if (use_ohci_instead)
+	    usb_ohci_init(pci_bus, 3, -1);
+	else
+            usb_uhci_init(pci_bus, piix3_devfn + 2);
     }
 
     if (pci_enabled && acpi_enabled) {
diff -Naur qemu-0.9.0/vl.c qemu-0.9.0-ohci-patch/vl.c
--- qemu-0.9.0/vl.c	2007-02-06 00:01:54.000000000 +0100
+++ qemu-0.9.0-ohci-patch/vl.c	2007-03-29 15:44:52.000000000 +0200
@@ -155,6 +155,7 @@
 int win2k_install_hack = 0;
 #endif
 int usb_enabled = 0;
+int use_ohci_instead = 0;
 static VLANState *first_vlan;
 int smp_cpus = 1;
 const char *vnc_display;
@@ -6048,6 +6049,7 @@
            "-win2k-hack     use it when installing Windows 2000 to avoid a disk full bug\n"
 #endif
            "-usb            enable the USB driver (will be the default soon)\n"
+	   "-ohci           use the OHCI controller instead of the UHCI\n"
            "-usbdevice name add the host or guest USB device 'name'\n"
 #if defined(TARGET_PPC) || defined(TARGET_SPARC)
            "-g WxH[xDEPTH]  Set the initial graphical resolution and depth\n"
@@ -6198,6 +6200,7 @@
     QEMU_OPTION_kernel_kqemu,
     QEMU_OPTION_win2k_hack,
     QEMU_OPTION_usb,
+    QEMU_OPTION_ohci,
     QEMU_OPTION_usbdevice,
     QEMU_OPTION_smp,
     QEMU_OPTION_vnc,
@@ -6284,6 +6287,7 @@
 
     /* temporary options */
     { "usb", 0, QEMU_OPTION_usb },
+    { "ohci", 0, QEMU_OPTION_ohci },
     { "cirrusvga", 0, QEMU_OPTION_cirrusvga },
     { "no-acpi", 0, QEMU_OPTION_no_acpi },
     { "no-reboot", 0, QEMU_OPTION_no_reboot },
@@ -6917,6 +6921,9 @@
             case QEMU_OPTION_usb:
                 usb_enabled = 1;
                 break;
+	    case QEMU_OPTION_ohci:
+	        use_ohci_instead = 1;
+		break;
             case QEMU_OPTION_usbdevice:
                 usb_enabled = 1;
                 if (usb_devices_index >= MAX_USB_CMDLINE) {
diff -Naur qemu-0.9.0/vl.h qemu-0.9.0-ohci-patch/vl.h
--- qemu-0.9.0/vl.h	2007-02-06 00:01:54.000000000 +0100
+++ qemu-0.9.0-ohci-patch/vl.h	2007-03-29 15:42:19.000000000 +0200
@@ -155,6 +155,7 @@
 extern int kqemu_allowed;
 extern int win2k_install_hack;
 extern int usb_enabled;
+extern int use_ohci_instead;
 extern int smp_cpus;
 extern int no_quit;
 extern int semihosting_enabled;

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
  2007-10-22 10:19 [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support Arnon Gilboa
  2007-10-22 10:29 ` Itamar Heim
  2007-10-22 10:44 ` Dor Laor
@ 2007-10-22 11:52 ` Paul Brook
  2007-10-22 15:04   ` Arnon Gilboa
  2 siblings, 1 reply; 9+ messages in thread
From: Paul Brook @ 2007-10-22 11:52 UTC (permalink / raw)
  To: qemu-devel; +Cc: Arnon Gilboa

On Monday 22 October 2007, Arnon Gilboa wrote:
> Hi,
>
> The attached patch adds isochronous transfers support to the OHCI
> emulation, similarly to the UHCI patch pushed two weeks ago.

> +    uint16_t offset[8];
> +};

> +static inline int ohci_read_iso_td(uint32_t addr, struct ohci_iso_td *td)
> +{
> +    return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
> +}

This is wrong. It will break on big-endian hosts.
set_dwords only DTRT if all the structure fields are 32-bit.

Paul

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
  2007-10-22 10:29 ` Itamar Heim
@ 2007-10-22 12:38   ` Hetz Ben Hamo
  2007-10-22 12:41   ` Arnon Gilboa
  1 sibling, 0 replies; 9+ messages in thread
From: Hetz Ben Hamo @ 2007-10-22 12:38 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 276 bytes --]

Hi Itamar,

On 22/10/2007, Itamar Heim <Itamar.Heim@qumranet.com> wrote:
>
> So this is isochronous for usb 2.0?
>

last I checked, OHCI is for USB 1.x, EHCI was for 2.0.

Thanks,
Hetz
-- 
Skepticism is the lazy person's default position.
my blog (hebrew): http://benhamo.org

[-- Attachment #2: Type: text/html, Size: 672 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
  2007-10-22 10:29 ` Itamar Heim
  2007-10-22 12:38   ` Hetz Ben Hamo
@ 2007-10-22 12:41   ` Arnon Gilboa
  1 sibling, 0 replies; 9+ messages in thread
From: Arnon Gilboa @ 2007-10-22 12:41 UTC (permalink / raw)
  To: qemu-devel

no. uhci & ohci are for usb 1.1. ehci is for usb 2.0 and i have just
started working on its qemu emulation.

-----Original Message-----
From: qemu-devel-bounces+arnong=qumranet.com@nongnu.org
[mailto:qemu-devel-bounces+arnong=qumranet.com@nongnu.org] On Behalf Of
Itamar Heim
Sent: Monday, October 22, 2007 12:30 PM
To: qemu-devel@nongnu.org
Subject: RE: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers
support

So this is isochronous for usb 2.0?

-----Original Message-----
From: qemu-devel-bounces+itamarh=qumranet.com@nongnu.org
[mailto:qemu-devel-bounces+itamarh=qumranet.com@nongnu.org] On Behalf Of
Arnon Gilboa
Sent: Monday, October 22, 2007 12:19 PM
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers
support

Hi,

The attached patch adds isochronous transfers support to the OHCI
emulation, similarly to the UHCI patch pushed two weeks ago.

In order to use ohci instead of uhci, replace the following line in
pc.c:
usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);

With:
usb_ohci_init_pci(pci_bus, 3, piix3_devfn + 2);

Any comments?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* RE: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support
  2007-10-22 11:52 ` Paul Brook
@ 2007-10-22 15:04   ` Arnon Gilboa
  0 siblings, 0 replies; 9+ messages in thread
From: Arnon Gilboa @ 2007-10-22 15:04 UTC (permalink / raw)
  To: Paul Brook, qemu-devel

[-- Attachment #1: Type: text/plain, Size: 833 bytes --]

Thanks, Paul.
The attached updated patch seems to fix this bug.
Comments? 

-----Original Message-----
From: Paul Brook [mailto:paul@codesourcery.com] 
Sent: Monday, October 22, 2007 1:53 PM
To: qemu-devel@nongnu.org
Cc: Arnon Gilboa
Subject: Re: [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers
support

On Monday 22 October 2007, Arnon Gilboa wrote:
> Hi,
>
> The attached patch adds isochronous transfers support to the OHCI 
> emulation, similarly to the UHCI patch pushed two weeks ago.

> +    uint16_t offset[8];
> +};

> +static inline int ohci_read_iso_td(uint32_t addr, struct ohci_iso_td 
> +*td) {
> +    return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2); }

This is wrong. It will break on big-endian hosts.
set_dwords only DTRT if all the structure fields are 32-bit.

Paul

[-- Attachment #2: usb-ohci-iso.patch --]
[-- Type: application/octet-stream, Size: 16869 bytes --]

diff --git a/qemu-head/hw/usb-ohci.c b/qemu/hw/usb-ohci.c
index 2d5af7d..287c45e 100755
--- a/qemu-head/hw/usb-ohci.c
+++ b/qemu/hw/usb-ohci.c
@@ -32,6 +32,7 @@
 //#define DEBUG_OHCI
 /* Dump packet contents.  */
 //#define DEBUG_PACKET
+//#define DEBUG_ISOCH
 /* This causes frames to occur 1000x slower */
 //#define OHCI_TIME_WARP 1
 
@@ -132,8 +133,8 @@ static void ohci_bus_stop(OHCIState *ohci);
 #define OHCI_ED_S         (1<<13)
 #define OHCI_ED_K         (1<<14)
 #define OHCI_ED_F         (1<<15)
-#define OHCI_ED_MPS_SHIFT 7
-#define OHCI_ED_MPS_MASK  (0xf<<OHCI_ED_FA_SHIFT)
+#define OHCI_ED_MPS_SHIFT 16
+#define OHCI_ED_MPS_MASK  (0x7ff<<OHCI_ED_MPS_SHIFT)
 
 /* Flags in the head field of an Endpoint Desciptor.  */
 #define OHCI_ED_H         1
@@ -152,6 +153,22 @@ static void ohci_bus_stop(OHCIState *ohci);
 #define OHCI_TD_CC_SHIFT  28
 #define OHCI_TD_CC_MASK   (0xf<<OHCI_TD_CC_SHIFT)
 
+/* Bitfields for the first word of an Isochronous Transfer Desciptor.  */
+/* CC & DI - same as in the General Transfer Desciptor */
+#define OHCI_TD_SF_SHIFT  0
+#define OHCI_TD_SF_MASK   (0xffff<<OHCI_TD_SF_SHIFT)
+#define OHCI_TD_FC_SHIFT  24
+#define OHCI_TD_FC_MASK   (7<<OHCI_TD_FC_SHIFT)
+
+/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */
+#define OHCI_TD_PSW_CC_SHIFT 12
+#define OHCI_TD_PSW_CC_MASK  (0xf<<OHCI_TD_PSW_CC_SHIFT)
+#define OHCI_TD_PSW_SIZE_SHIFT 0
+#define OHCI_TD_PSW_SIZE_MASK  (0xfff<<OHCI_TD_PSW_SIZE_SHIFT)
+
+#define OHCI_PAGE_MASK    0xfffff000
+#define OHCI_OFFSET_MASK  0xfff
+
 #define OHCI_DPTR_MASK    0xfffffff0
 
 #define OHCI_BM(val, field) \
@@ -178,6 +195,15 @@ struct ohci_td {
     uint32_t be;
 };
 
+/* Isochronous transfer descriptor */
+struct ohci_iso_td {
+    uint32_t flags;
+    uint32_t bp;
+    uint32_t next;
+    uint32_t be;
+    uint16_t offset[8];
+};
+
 #define USB_HZ                      12000000
 
 /* OHCI Local stuff */
@@ -421,6 +447,32 @@ static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
     return 1;
 }
 
+/* Get an array of words from main memory */
+static inline int get_words(uint32_t addr, uint16_t *buf, int num)
+{
+    int i;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
+        *buf = le16_to_cpu(*buf);
+    }
+
+    return 1;
+}
+
+/* Put an array of words in to main memory */
+static inline int put_words(uint32_t addr, uint16_t *buf, int num)
+{
+    int i;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        uint16_t tmp = cpu_to_le16(*buf);
+        cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
+    }
+
+    return 1;
+}
+
 static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed)
 {
     return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
@@ -431,6 +483,12 @@ static inline int ohci_read_td(uint32_t addr, struct ohci_td *td)
     return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
 }
 
+static inline int ohci_read_iso_td(uint32_t addr, struct ohci_iso_td *td)
+{
+    return (get_dwords(addr, (uint32_t *)td, 4) &&
+            get_words(addr + 16, td->offset, 8));
+}
+
 static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed)
 {
     return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
@@ -441,6 +499,12 @@ static inline int ohci_put_td(uint32_t addr, struct ohci_td *td)
     return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
 }
 
+static inline int ohci_put_iso_td(uint32_t addr, struct ohci_iso_td *td)
+{
+    return (put_dwords(addr, (uint32_t *)td, 4) &&
+            put_words(addr + 16, td->offset, 8));
+}
+
 /* Read/Write the contents of a TD from/to main memory.  */
 static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
 {
@@ -459,16 +523,270 @@ static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
     cpu_physical_memory_rw(ptr, buf, len - n, write);
 }
 
-static void ohci_process_lists(OHCIState *ohci);
+/* Read/Write the contents of an ISO TD from/to main memory.  */
+static void ohci_copy_iso_td(uint32_t start_addr, uint32_t end_addr,
+                             uint8_t *buf, int len, int write)
+{
+    uint32_t ptr;
+    uint32_t n;
 
-static void ohci_async_complete_packet(USBPacket * packet, void *opaque)
+    ptr = start_addr;
+    n = 0x1000 - (ptr & 0xfff);
+    if (n > len)
+        n = len;
+    cpu_physical_memory_rw(ptr, buf, n, write);
+    if (n == len)
+        return;
+    ptr = end_addr & ~0xfffu;
+    buf += n;
+    cpu_physical_memory_rw(ptr, buf, len - n, write);
+}
+
+static void ohci_process_lists(OHCIState *ohci, int completion);
+
+static void ohci_async_complete_packet(USBPacket *packet, void *opaque)
 {
     OHCIState *ohci = opaque;
 #ifdef DEBUG_PACKET
     dprintf("Async packet complete\n");
 #endif
     ohci->async_complete = 1;
-    ohci_process_lists(ohci);
+    ohci_process_lists(ohci, 1);
+}
+
+#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
+
+static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
+                               int completion)
+{
+    int dir;
+    size_t len = 0;
+    char *str = NULL;
+    int pid;
+    int ret;
+    int i;
+    USBDevice *dev;
+    struct ohci_iso_td iso_td;
+    uint32_t addr;
+    uint16_t starting_frame;
+    int16_t relative_frame_number;
+    int frame_count;
+    uint32_t start_offset, next_offset, end_offset = 0;
+    uint32_t start_addr, end_addr;
+
+    addr = ed->head & OHCI_DPTR_MASK;
+
+    if (!ohci_read_iso_td(addr, &iso_td)) {
+        printf("usb-ohci: ISO_TD read error at %x\n", addr);
+        return 0;
+    }
+
+    starting_frame = OHCI_BM(iso_td.flags, TD_SF);
+    frame_count = OHCI_BM(iso_td.flags, TD_FC);
+    relative_frame_number = USUB(ohci->frame_number, starting_frame); 
+
+#ifdef DEBUG_ISOCH
+    printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "frame_number 0x%.8x starting_frame 0x%.8x\n"
+           "frame_count  0x%.8x relative %d\n"
+           "di 0x%.8x cc 0x%.8x\n",
+           ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK,
+           iso_td.flags, iso_td.bp, iso_td.next, iso_td.be,
+           iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3],
+           iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7],
+           ohci->frame_number, starting_frame, 
+           frame_count, relative_frame_number,         
+           OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC));
+#endif
+
+    if (relative_frame_number < 0) {
+        dprintf("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number);
+        return 1;
+    } else if (relative_frame_number > frame_count) {
+        /* ISO TD expired - retire the TD to the Done Queue and continue with
+           the next ISO TD of the same ED */
+        dprintf("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, 
+               frame_count);
+        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+        ed->head &= ~OHCI_DPTR_MASK;
+        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+        iso_td.next = ohci->done;
+        ohci->done = addr;
+        i = OHCI_BM(iso_td.flags, TD_DI);
+        if (i < ohci->done_count)
+            ohci->done_count = i;
+        ohci_put_iso_td(addr, &iso_td);        
+        return 0;
+    }
+
+    dir = OHCI_BM(ed->flags, ED_D);
+    switch (dir) {
+    case OHCI_TD_DIR_IN:
+        str = "in";
+        pid = USB_TOKEN_IN;
+        break;
+    case OHCI_TD_DIR_OUT:
+        str = "out";
+        pid = USB_TOKEN_OUT;
+        break;
+    case OHCI_TD_DIR_SETUP:
+        str = "setup";
+        pid = USB_TOKEN_SETUP;
+        break;
+    default:
+        printf("usb-ohci: Bad direction %d\n", dir);
+        return 1;
+    }
+
+    if (!iso_td.bp || !iso_td.be) {
+        printf("usb-ohci: ISO_TD bp 0x%.8x be 0x%.8x\n", iso_td.bp, iso_td.be);
+        return 1;
+    }
+
+    start_offset = iso_td.offset[relative_frame_number];
+    next_offset = iso_td.offset[relative_frame_number + 1];
+
+    if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || 
+        ((relative_frame_number < frame_count) && 
+         !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) {
+        printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n",
+               start_offset, next_offset);
+        return 1;
+    }
+
+    if ((relative_frame_number < frame_count) && (start_offset > next_offset)) {
+        printf("usb-ohci: ISO_TD start_offset=0x%.8x > next_offset=0x%.8x\n",
+                start_offset, next_offset);
+        return 1;
+    }
+
+    if ((start_offset & 0x1000) == 0) {
+        start_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+            (start_offset & OHCI_OFFSET_MASK);
+    } else {
+        start_addr = (iso_td.be & OHCI_PAGE_MASK) |
+            (start_offset & OHCI_OFFSET_MASK);
+    }
+
+    if (relative_frame_number < frame_count) {
+        end_offset = next_offset - 1;
+        if ((end_offset & 0x1000) == 0) {
+            end_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+                (end_offset & OHCI_OFFSET_MASK);
+        } else {
+            end_addr = (iso_td.be & OHCI_PAGE_MASK) |
+                (end_offset & OHCI_OFFSET_MASK);
+        }
+    } else {
+        /* Last packet in the ISO TD */
+        end_addr = iso_td.be;
+    }
+
+    if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) {
+        len = (end_addr & OHCI_OFFSET_MASK) + 0x1001
+            - (start_addr & OHCI_OFFSET_MASK);
+    } else {
+        len = end_addr - start_addr + 1;
+    }
+
+    if (len && dir != OHCI_TD_DIR_IN) {
+        ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, len, 0);
+    }
+
+    if (completion) {
+        ret = ohci->usb_packet.len;
+    } else {
+        ret = USB_RET_NODEV;
+        for (i = 0; i < ohci->num_ports; i++) {
+            dev = ohci->rhport[i].port.dev;
+            if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+                continue;
+            ohci->usb_packet.pid = pid;
+            ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
+            ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
+            ohci->usb_packet.data = ohci->usb_buf;
+            ohci->usb_packet.len = len;
+            ohci->usb_packet.complete_cb = ohci_async_complete_packet;
+            ohci->usb_packet.complete_opaque = ohci;
+            ret = dev->handle_packet(dev, &ohci->usb_packet);
+            if (ret != USB_RET_NODEV)
+                break;
+        }
+    
+        if (ret == USB_RET_ASYNC) {
+            return 1;
+        }
+    }
+
+#ifdef DEBUG_ISOCH
+    printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
+           start_offset, end_offset, start_addr, end_addr, str, len, ret);
+#endif
+
+    /* Writeback */
+    if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
+        /* IN transfer succeeded */
+        ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, ret, 1);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                    OHCI_CC_NOERROR);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret);
+    } else if (dir == OHCI_TD_DIR_OUT && ret == len) {
+        /* OUT transfer succeeded */
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                    OHCI_CC_NOERROR);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0);
+    } else {
+        if (ret > len) {
+            printf("usb-ohci: DataOverrun %d > %zu\n", ret, len);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                        OHCI_CC_DATAOVERRUN);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                        len);
+        } else if (ret >= 0) {
+            printf("usb-ohci: DataUnderrun %d\n", ret);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                        OHCI_CC_DATAUNDERRUN);
+        } else {
+            switch (ret) {
+            case USB_RET_NODEV:
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_DEVICENOTRESPONDING);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                            0);
+                break;
+            case USB_RET_NAK:
+            case USB_RET_STALL:
+                printf("usb-ohci: got NAK/STALL %d\n", ret);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_STALL);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                            0);
+                break;
+            default:
+                printf("usb-ohci: Bad device response %d\n", ret);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_UNDEXPETEDPID);
+                break;
+            }
+        }
+    }
+
+    if (relative_frame_number == frame_count) {
+        /* Last data packet of ISO TD - retire the TD to the Done Queue */
+        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR);
+        ed->head &= ~OHCI_DPTR_MASK;
+        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+        iso_td.next = ohci->done;
+        ohci->done = addr;
+        i = OHCI_BM(iso_td.flags, TD_DI);
+        if (i < ohci->done_count)
+            ohci->done_count = i;
+    }
+    ohci_put_iso_td(addr, &iso_td);
+    return 1;
 }
 
 /* Service a transport descriptor.
@@ -671,7 +989,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
 }
 
 /* Service an endpoint list.  Returns nonzero if active TD were found.  */
-static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
+static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
 {
     struct ohci_ed ed;
     uint32_t next_ed;
@@ -702,10 +1020,6 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
             continue;
         }
 
-        /* Skip isochronous endpoints.  */
-        if (ed.flags & OHCI_ED_F)
-          continue;
-
         while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
 #ifdef DEBUG_PACKET
             dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u "
@@ -719,8 +1033,14 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head)
 #endif
             active = 1;
 
-            if (ohci_service_td(ohci, &ed))
-                break;
+            if ((ed.flags & OHCI_ED_F) == 0) {
+                if (ohci_service_td(ohci, &ed))
+                    break;
+            } else {
+                /* Handle isochronous endpoints */
+                if (ohci_service_iso_td(ohci, &ed, completion))
+                    break;
+            }
         }
 
         ohci_put_ed(cur, &ed);
@@ -738,19 +1058,19 @@ static void ohci_sof(OHCIState *ohci)
 }
 
 /* Process Control and Bulk lists.  */
-static void ohci_process_lists(OHCIState *ohci)
+static void ohci_process_lists(OHCIState *ohci, int completion)
 {
     if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
         if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head)
           dprintf("usb-ohci: head %x, cur %x\n", ohci->ctrl_head, ohci->ctrl_cur);
-        if (!ohci_service_ed_list(ohci, ohci->ctrl_head)) {
+        if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
             ohci->ctrl_cur = 0;
             ohci->status &= ~OHCI_STATUS_CLF;
         }
     }
 
     if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
-        if (!ohci_service_ed_list(ohci, ohci->bulk_head)) {
+        if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
             ohci->bulk_cur = 0;
             ohci->status &= ~OHCI_STATUS_BLF;
         }
@@ -770,7 +1090,7 @@ static void ohci_frame_boundary(void *opaque)
         int n;
 
         n = ohci->frame_number & 0x1f;
-        ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]));
+        ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
     }
 
     /* Cancel all pending packets if either of the lists has been disabled.  */
@@ -780,7 +1100,7 @@ static void ohci_frame_boundary(void *opaque)
         ohci->async_td = 0;
     }
     ohci->old_ctl = ohci->ctl;
-    ohci_process_lists(ohci);
+    ohci_process_lists(ohci, 0);
 
     /* Frame boundary, so do EOF stuf here */
     ohci->frt = ohci->fit;
@@ -1206,6 +1526,9 @@ static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
         ohci_set_frame_interval(ohci, val);
         break;
 
+    case 15: /* HcFmNumber */
+        break;
+
     case 16: /* HcPeriodicStart */
         ohci->pstart = val & 0xffff;
         break;

^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2007-10-22 15:04 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-10-22 10:19 [Qemu-devel] [PATCH, RFC] USB OHCI isochronous transfers support Arnon Gilboa
2007-10-22 10:29 ` Itamar Heim
2007-10-22 12:38   ` Hetz Ben Hamo
2007-10-22 12:41   ` Arnon Gilboa
2007-10-22 10:44 ` Dor Laor
2007-10-22 10:50   ` Arnon Gilboa
2007-10-22 11:29     ` Michal Schulz
2007-10-22 11:52 ` Paul Brook
2007-10-22 15:04   ` Arnon Gilboa

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).