]> git.ipfire.org Git - thirdparty/qemu.git/blobdiff - hw/usb/hcd-ohci.c
include/qemu/osdep.h: Don't include qapi/error.h
[thirdparty/qemu.git] / hw / usb / hcd-ohci.c
index 3d35058b14de592970a9391368b2e0ddc99ebaa5..f3dc26bccaaa5064b0bf2f504833249bf5c3dd0c 100644 (file)
  *  o BIOS work to boot from USB storage
 */
 
+#include "qemu/osdep.h"
 #include "hw/hw.h"
+#include "qapi/error.h"
 #include "qemu/timer.h"
 #include "hw/usb.h"
 #include "hw/pci/pci.h"
 #include "hw/sysbus.h"
 #include "hw/qdev-dma.h"
+#include "trace.h"
 
-//#define DEBUG_OHCI
-/* Dump packet contents.  */
-//#define DEBUG_PACKET
-//#define DEBUG_ISOCH
 /* This causes frames to occur 1000x slower */
 //#define OHCI_TIME_WARP 1
 
-#ifdef DEBUG_OHCI
-#define DPRINTF printf
-#else
-#define DPRINTF(...)
-#endif
-
 /* Number of Downstream Ports on the root hub.  */
 
 #define OHCI_MAX_PORTS 15
@@ -80,13 +73,13 @@ typedef struct {
     uint32_t bulk_head, bulk_cur;
     uint32_t per_cur;
     uint32_t done;
-    int done_count;
+    int32_t done_count;
 
     /* Frame counter partition */
-    uint32_t fsmps:15;
-    uint32_t fit:1;
-    uint32_t fi:14;
-    uint32_t frt:1;
+    uint16_t fsmps;
+    uint8_t fit;
+    uint16_t fi;
+    uint8_t frt;
     uint16_t frame_number;
     uint16_t padding;
     uint32_t pstart;
@@ -111,7 +104,7 @@ typedef struct {
     USBPacket usb_packet;
     uint8_t usb_buf[8192];
     uint32_t async_td;
-    int async_complete;
+    bool async_complete;
 
 } OHCIState;
 
@@ -234,15 +227,15 @@ struct ohci_iso_td {
 #define OHCI_STATUS_OCR       (1<<3)
 #define OHCI_STATUS_SOC       ((1<<6)|(1<<7))
 
-#define OHCI_INTR_SO          (1<<0) /* Scheduling overrun */
-#define OHCI_INTR_WD          (1<<1) /* HcDoneHead writeback */
-#define OHCI_INTR_SF          (1<<2) /* Start of frame */
-#define OHCI_INTR_RD          (1<<3) /* Resume detect */
-#define OHCI_INTR_UE          (1<<4) /* Unrecoverable error */
-#define OHCI_INTR_FNO         (1<<5) /* Frame number overflow */
-#define OHCI_INTR_RHSC        (1<<6) /* Root hub status change */
-#define OHCI_INTR_OC          (1<<30) /* Ownership change */
-#define OHCI_INTR_MIE         (1<<31) /* Master Interrupt Enable */
+#define OHCI_INTR_SO          (1U<<0) /* Scheduling overrun */
+#define OHCI_INTR_WD          (1U<<1) /* HcDoneHead writeback */
+#define OHCI_INTR_SF          (1U<<2) /* Start of frame */
+#define OHCI_INTR_RD          (1U<<3) /* Resume detect */
+#define OHCI_INTR_UE          (1U<<4) /* Unrecoverable error */
+#define OHCI_INTR_FNO         (1U<<5) /* Frame number overflow */
+#define OHCI_INTR_RHSC        (1U<<6) /* Root hub status change */
+#define OHCI_INTR_OC          (1U<<30) /* Ownership change */
+#define OHCI_INTR_MIE         (1U<<31) /* Master Interrupt Enable */
 
 #define OHCI_HCCA_SIZE        0x100
 #define OHCI_HCCA_MASK        0xffffff00
@@ -253,7 +246,7 @@ struct ohci_iso_td {
 #define OHCI_FMI_FSMPS        0xffff0000
 #define OHCI_FMI_FIT          0x80000000
 
-#define OHCI_FR_RT            (1<<31)
+#define OHCI_FR_RT            (1U<<31)
 
 #define OHCI_LS_THRESH        0x628
 
@@ -265,12 +258,12 @@ struct ohci_iso_td {
 #define OHCI_RHA_NOCP         (1<<12)
 #define OHCI_RHA_POTPGT_MASK  0xff000000
 
-#define OHCI_RHS_LPS          (1<<0)
-#define OHCI_RHS_OCI          (1<<1)
-#define OHCI_RHS_DRWE         (1<<15)
-#define OHCI_RHS_LPSC         (1<<16)
-#define OHCI_RHS_OCIC         (1<<17)
-#define OHCI_RHS_CRWE         (1<<31)
+#define OHCI_RHS_LPS          (1U<<0)
+#define OHCI_RHS_OCI          (1U<<1)
+#define OHCI_RHS_DRWE         (1U<<15)
+#define OHCI_RHS_LPSC         (1U<<16)
+#define OHCI_RHS_OCIC         (1U<<17)
+#define OHCI_RHS_CRWE         (1U<<31)
 
 #define OHCI_PORT_CCS         (1<<0)
 #define OHCI_PORT_PES         (1<<1)
@@ -350,7 +343,7 @@ static void ohci_attach(USBPort *port1)
         ohci_set_interrupt(s, OHCI_INTR_RD);
     }
 
-    DPRINTF("usb-ohci: Attached port %d\n", port1->index);
+    trace_usb_ohci_port_attach(port1->index);
 
     if (old_state != port->ctrl) {
         ohci_set_interrupt(s, OHCI_INTR_RHSC);
@@ -375,7 +368,7 @@ static void ohci_detach(USBPort *port1)
         port->ctrl &= ~OHCI_PORT_PES;
         port->ctrl |= OHCI_PORT_PESC;
     }
-    DPRINTF("usb-ohci: Detached port %d\n", port1->index);
+    trace_usb_ohci_port_detach(port1->index);
 
     if (old_state != port->ctrl) {
         ohci_set_interrupt(s, OHCI_INTR_RHSC);
@@ -388,14 +381,14 @@ static void ohci_wakeup(USBPort *port1)
     OHCIPort *port = &s->rhport[port1->index];
     uint32_t intr = 0;
     if (port->ctrl & OHCI_PORT_PSS) {
-        DPRINTF("usb-ohci: port %d: wakeup\n", port1->index);
+        trace_usb_ohci_port_wakeup(port1->index);
         port->ctrl |= OHCI_PORT_PSSC;
         port->ctrl &= ~OHCI_PORT_PSS;
         intr = OHCI_INTR_RHSC;
     }
     /* Note that the controller can be suspended even if this port is not */
     if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
-        DPRINTF("usb-ohci: remote-wakeup: SUSPEND->RESUME\n");
+        trace_usb_ohci_remote_wakeup(s->name);
         /* This is the one state transition the controller can do by itself */
         s->ctl &= ~OHCI_CTL_HCFS;
         s->ctl |= OHCI_USB_RESUME;
@@ -448,15 +441,37 @@ static void ohci_stop_endpoints(OHCIState *ohci)
     }
 }
 
-/* Reset the controller */
-static void ohci_reset(void *opaque)
+static void ohci_roothub_reset(OHCIState *ohci)
 {
-    OHCIState *ohci = opaque;
     OHCIPort *port;
     int i;
 
     ohci_bus_stop(ohci);
-    ohci->ctl = 0;
+    ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
+    ohci->rhdesc_b = 0x0; /* Impl. specific */
+    ohci->rhstatus = 0;
+
+    for (i = 0; i < ohci->num_ports; i++) {
+        port = &ohci->rhport[i];
+        port->ctrl = 0;
+        if (port->port.dev && port->port.dev->attached) {
+            usb_port_reset(&port->port);
+        }
+    }
+    if (ohci->async_td) {
+        usb_cancel_packet(&ohci->usb_packet);
+        ohci->async_td = 0;
+    }
+    ohci_stop_endpoints(ohci);
+}
+
+/* Reset the controller */
+static void ohci_soft_reset(OHCIState *ohci)
+{
+    trace_usb_ohci_reset(ohci->name);
+
+    ohci_bus_stop(ohci);
+    ohci->ctl = (ohci->ctl & OHCI_CTL_IR) | OHCI_USB_SUSPEND;
     ohci->old_ctl = 0;
     ohci->status = 0;
     ohci->intr_status = 0;
@@ -479,25 +494,13 @@ static void ohci_reset(void *opaque)
     ohci->frame_number = 0;
     ohci->pstart = 0;
     ohci->lst = OHCI_LS_THRESH;
+}
 
-    ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
-    ohci->rhdesc_b = 0x0; /* Impl. specific */
-    ohci->rhstatus = 0;
-
-    for (i = 0; i < ohci->num_ports; i++)
-      {
-        port = &ohci->rhport[i];
-        port->ctrl = 0;
-        if (port->port.dev && port->port.dev->attached) {
-            usb_port_reset(&port->port);
-        }
-      }
-    if (ohci->async_td) {
-        usb_cancel_packet(&ohci->usb_packet);
-        ohci->async_td = 0;
-    }
-    ohci_stop_endpoints(ohci);
-    DPRINTF("usb-ohci: Reset %s\n", ohci->name);
+static void ohci_hard_reset(OHCIState *ohci)
+{
+    ohci_soft_reset(ohci);
+    ohci->ctl = 0;
+    ohci_roothub_reset(ohci);
 }
 
 /* Get an array of dwords from main memory */
@@ -619,8 +622,8 @@ static inline int ohci_put_td(OHCIState *ohci,
 static inline int ohci_put_iso_td(OHCIState *ohci,
                                   dma_addr_t addr, struct ohci_iso_td *td)
 {
-    return put_dwords(ohci, addr, (uint32_t *)td, 4 ||
-           put_words(ohci, addr + 16, td->offset, 8));
+    return put_dwords(ohci, addr, (uint32_t *)td, 4) ||
+           put_words(ohci, addr + 16, td->offset, 8);
 }
 
 static inline int ohci_put_hcca(OHCIState *ohci,
@@ -690,10 +693,9 @@ static void ohci_process_lists(OHCIState *ohci, int completion);
 static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
 {
     OHCIState *ohci = container_of(packet, OHCIState, usb_packet);
-#ifdef DEBUG_PACKET
-    DPRINTF("Async packet complete\n");
-#endif
-    ohci->async_complete = 1;
+
+    trace_usb_ohci_async_complete();
+    ohci->async_complete = true;
     ohci_process_lists(ohci, 1);
 }
 
@@ -704,9 +706,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
 {
     int dir;
     size_t len = 0;
-#ifdef DEBUG_ISOCH
     const char *str = NULL;
-#endif
     int pid;
     int ret;
     int i;
@@ -723,7 +723,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     addr = ed->head & OHCI_DPTR_MASK;
 
     if (ohci_read_iso_td(ohci, addr, &iso_td)) {
-        printf("usb-ohci: ISO_TD read error at %x\n", addr);
+        trace_usb_ohci_iso_td_read_failed(addr);
         ohci_die(ohci);
         return 0;
     }
@@ -732,31 +732,25 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     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",
+    trace_usb_ohci_iso_td_head(
            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
+           ohci->frame_number, starting_frame,
+           frame_count, relative_frame_number);
+    trace_usb_ohci_iso_td_head_offset(
+           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]);
 
     if (relative_frame_number < 0) {
-        DPRINTF("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number);
+        trace_usb_ohci_iso_td_relative_frame_number_neg(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);
+        trace_usb_ohci_iso_td_relative_frame_number_big(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);
@@ -775,30 +769,24 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     dir = OHCI_BM(ed->flags, ED_D);
     switch (dir) {
     case OHCI_TD_DIR_IN:
-#ifdef DEBUG_ISOCH
         str = "in";
-#endif
         pid = USB_TOKEN_IN;
         break;
     case OHCI_TD_DIR_OUT:
-#ifdef DEBUG_ISOCH
         str = "out";
-#endif
         pid = USB_TOKEN_OUT;
         break;
     case OHCI_TD_DIR_SETUP:
-#ifdef DEBUG_ISOCH
         str = "setup";
-#endif
         pid = USB_TOKEN_SETUP;
         break;
     default:
-        printf("usb-ohci: Bad direction %d\n", dir);
+        trace_usb_ohci_iso_td_bad_direction(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);
+        trace_usb_ohci_iso_td_bad_bp_be(iso_td.bp, iso_td.be);
         return 1;
     }
 
@@ -808,14 +796,12 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     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);
+        trace_usb_ohci_iso_td_bad_cc_not_accessed(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);
+        trace_usb_ohci_iso_td_bad_cc_overrun(start_offset, next_offset);
         return 1;
     }
 
@@ -875,10 +861,8 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
         ret = ohci->usb_packet.status;
     }
 
-#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
+    trace_usb_ohci_iso_td_so(start_offset, end_offset, start_addr, end_addr,
+                             str, len, ret);
 
     /* Writeback */
     if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
@@ -898,13 +882,13 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
         OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0);
     } else {
         if (ret > (ssize_t) len) {
-            printf("usb-ohci: DataOverrun %d > %zu\n", ret, len);
+            trace_usb_ohci_iso_td_data_overrun(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);
+            trace_usb_ohci_iso_td_data_underrun(ret);
             OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
                         OHCI_CC_DATAUNDERRUN);
         } else {
@@ -918,14 +902,14 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
                 break;
             case USB_RET_NAK:
             case USB_RET_STALL:
-                printf("usb-ohci: got NAK/STALL %d\n", ret);
+                trace_usb_ohci_iso_td_nak(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);
+                trace_usb_ohci_iso_td_bad_response(ret);
                 OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
                             OHCI_CC_UNDEXPETEDPID);
                 break;
@@ -950,6 +934,43 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
     return 1;
 }
 
+#ifdef trace_event_get_state
+static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len)
+{
+    bool print16 = !!trace_event_get_state(TRACE_USB_OHCI_TD_PKT_SHORT);
+    bool printall = !!trace_event_get_state(TRACE_USB_OHCI_TD_PKT_FULL);
+    const int width = 16;
+    int i;
+    char tmp[3 * width + 1];
+    char *p = tmp;
+
+    if (!printall && !print16) {
+        return;
+    }
+
+    for (i = 0; ; i++) {
+        if (i && (!(i % width) || (i == len))) {
+            if (!printall) {
+                trace_usb_ohci_td_pkt_short(msg, tmp);
+                break;
+            }
+            trace_usb_ohci_td_pkt_full(msg, tmp);
+            p = tmp;
+            *p = 0;
+        }
+        if (i == len) {
+            break;
+        }
+
+        p += sprintf(p, " %.2x", buf[i]);
+    }
+}
+#else
+static void ohci_td_pkt(const char *msg, const uint8_t *buf, size_t len)
+{
+}
+#endif
+
 /* Service a transport descriptor.
    Returns nonzero to terminate processing of this endpoint.  */
 
@@ -957,9 +978,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
 {
     int dir;
     size_t len = 0, pktlen = 0;
-#ifdef DEBUG_PACKET
     const char *str = NULL;
-#endif
     int pid;
     int ret;
     int i;
@@ -974,13 +993,11 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
     /* See if this TD has already been submitted to the device.  */
     completion = (addr == ohci->async_td);
     if (completion && !ohci->async_complete) {
-#ifdef DEBUG_PACKET
-        DPRINTF("Skipping async TD\n");
-#endif
+        trace_usb_ohci_td_skip_async();
         return 1;
     }
     if (ohci_read_td(ohci, addr, &td)) {
-        fprintf(stderr, "usb-ohci: TD read error at %x\n", addr);
+        trace_usb_ohci_td_read_error(addr);
         ohci_die(ohci);
         return 0;
     }
@@ -998,25 +1015,19 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
 
     switch (dir) {
     case OHCI_TD_DIR_IN:
-#ifdef DEBUG_PACKET
         str = "in";
-#endif
         pid = USB_TOKEN_IN;
         break;
     case OHCI_TD_DIR_OUT:
-#ifdef DEBUG_PACKET
         str = "out";
-#endif
         pid = USB_TOKEN_OUT;
         break;
     case OHCI_TD_DIR_SETUP:
-#ifdef DEBUG_PACKET
         str = "setup";
-#endif
         pid = USB_TOKEN_SETUP;
         break;
     default:
-        fprintf(stderr, "usb-ohci: Bad direction\n");
+        trace_usb_ohci_td_bad_direction(dir);
         return 1;
     }
     if (td.cbp && td.be) {
@@ -1043,22 +1054,13 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
     }
 
     flag_r = (td.flags & OHCI_TD_R) != 0;
-#ifdef DEBUG_PACKET
-    DPRINTF(" TD @ 0x%.8x %" PRId64 " of %" PRId64
-            " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
-            addr, (int64_t)pktlen, (int64_t)len, str, flag_r, td.cbp, td.be);
-
-    if (pktlen > 0 && dir != OHCI_TD_DIR_IN) {
-        DPRINTF("  data:");
-        for (i = 0; i < pktlen; i++) {
-            printf(" %.2x", ohci->usb_buf[i]);
-        }
-        DPRINTF("\n");
-    }
-#endif
+    trace_usb_ohci_td_pkt_hdr(addr, (int64_t)pktlen, (int64_t)len, str,
+                              flag_r, td.cbp, td.be);
+    ohci_td_pkt("OUT", ohci->usb_buf, pktlen);
+
     if (completion) {
         ohci->async_td = 0;
-        ohci->async_complete = 0;
+        ohci->async_complete = false;
     } else {
         if (ohci->async_td) {
             /* ??? The hardware should allow one active packet per
@@ -1066,9 +1068,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
                This should be sufficient as long as devices respond in a
                timely manner.
             */
-#ifdef DEBUG_PACKET
-            DPRINTF("Too many pending packets\n");
-#endif
+            trace_usb_ohci_td_too_many_pending();
             return 1;
         }
         dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
@@ -1077,9 +1077,8 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
                          OHCI_BM(td.flags, TD_DI) == 0);
         usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
         usb_handle_packet(dev, &ohci->usb_packet);
-#ifdef DEBUG_PACKET
-        DPRINTF("status=%d\n", ohci->usb_packet.status);
-#endif
+        trace_usb_ohci_td_packet_status(ohci->usb_packet.status);
+
         if (ohci->usb_packet.status == USB_RET_ASYNC) {
             usb_device_flush_ep_queue(dev, ep);
             ohci->async_td = addr;
@@ -1098,12 +1097,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
                              DMA_DIRECTION_FROM_DEVICE)) {
                 ohci_die(ohci);
             }
-#ifdef DEBUG_PACKET
-            DPRINTF("  data:");
-            for (i = 0; i < ret; i++)
-                printf(" %.2x", ohci->usb_buf[i]);
-            DPRINTF("\n");
-#endif
+            ohci_td_pkt("IN", ohci->usb_buf, pktlen);
         } else {
             ret = pktlen;
         }
@@ -1137,28 +1131,28 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
             ed->head |= OHCI_ED_C;
     } else {
         if (ret >= 0) {
-            DPRINTF("usb-ohci: Underrun\n");
+            trace_usb_ohci_td_underrun();
             OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN);
         } else {
             switch (ret) {
             case USB_RET_IOERROR:
             case USB_RET_NODEV:
-                DPRINTF("usb-ohci: got DEV ERROR\n");
+                trace_usb_ohci_td_dev_error();
                 OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
                 break;
             case USB_RET_NAK:
-                DPRINTF("usb-ohci: got NAK\n");
+                trace_usb_ohci_td_nak();
                 return 1;
             case USB_RET_STALL:
-                DPRINTF("usb-ohci: got STALL\n");
+                trace_usb_ohci_td_stall();
                 OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL);
                 break;
             case USB_RET_BABBLE:
-                DPRINTF("usb-ohci: got BABBLE\n");
+                trace_usb_ohci_td_babble();
                 OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
                 break;
             default:
-                fprintf(stderr, "usb-ohci: Bad device response %d\n", ret);
+                trace_usb_ohci_td_bad_device_response(ret);
                 OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID);
                 OHCI_SET_BM(td.flags, TD_EC, 3);
                 break;
@@ -1198,7 +1192,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
 
     for (cur = head; cur; cur = next_ed) {
         if (ohci_read_ed(ohci, cur, &ed)) {
-            fprintf(stderr, "usb-ohci: ED read error at %x\n", cur);
+            trace_usb_ohci_ed_read_error(cur);
             ohci_die(ohci);
             return 0;
         }
@@ -1219,16 +1213,15 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
         }
 
         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 "
-                    "h=%u c=%u\n  head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur,
+            trace_usb_ohci_ed_pkt(cur, (ed.head & OHCI_ED_H) != 0,
+                    (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK,
+                    ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK);
+            trace_usb_ohci_ed_pkt_flags(
                     OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN),
                     OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0,
                     (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0,
-                    OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0,
-                    (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK,
-                    ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK);
-#endif
+                    OHCI_BM(ed.flags, ED_MPS));
+
             active = 1;
 
             if ((ed.flags & OHCI_ED_F) == 0) {
@@ -1250,11 +1243,16 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
     return active;
 }
 
-/* Generate a SOF event, and set a timer for EOF */
-static void ohci_sof(OHCIState *ohci)
+/* set a timer for EOF */
+static void ohci_eof_timer(OHCIState *ohci)
 {
     ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     timer_mod(ohci->eof_timer, ohci->sof_time + usb_frame_time);
+}
+/* Set a timer for EOF and generate a SOF event */
+static void ohci_sof(OHCIState *ohci)
+{
+    ohci_eof_timer(ohci);
     ohci_set_interrupt(ohci, OHCI_INTR_SF);
 }
 
@@ -1263,8 +1261,7 @@ 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);
+            trace_usb_ohci_process_lists(ohci->ctrl_head, ohci->ctrl_cur);
         }
         if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
             ohci->ctrl_cur = 0;
@@ -1287,7 +1284,7 @@ static void ohci_frame_boundary(void *opaque)
     struct ohci_hcca hcca;
 
     if (ohci_read_hcca(ohci, ohci->hcca, &hcca)) {
-        fprintf(stderr, "usb-ohci: HCCA read error at %x\n", ohci->hcca);
+        trace_usb_ohci_hcca_read_error(ohci->hcca);
         ohci_die(ohci);
         return;
     }
@@ -1351,19 +1348,14 @@ static void ohci_frame_boundary(void *opaque)
  */
 static int ohci_bus_start(OHCIState *ohci)
 {
-    ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
-                    ohci_frame_boundary,
-                    ohci);
+    trace_usb_ohci_start(ohci->name);
 
-    if (ohci->eof_timer == NULL) {
-        fprintf(stderr, "usb-ohci: %s: timer_new_ns failed\n", ohci->name);
-        ohci_die(ohci);
-        return 0;
-    }
-
-    DPRINTF("usb-ohci: %s: USB Operational\n", ohci->name);
+    /* Delay the first SOF event by one frame time as
+     * linux driver is not ready to receive it and
+     * can meet some race conditions
+     */
 
-    ohci_sof(ohci);
+    ohci_eof_timer(ohci);
 
     return 1;
 }
@@ -1371,9 +1363,8 @@ static int ohci_bus_start(OHCIState *ohci)
 /* Stop sending SOF tokens on the bus */
 static void ohci_bus_stop(OHCIState *ohci)
 {
-    if (ohci->eof_timer)
-        timer_del(ohci->eof_timer);
-    ohci->eof_timer = NULL;
+    trace_usb_ohci_stop(ohci->name);
+    timer_del(ohci->eof_timer);
 }
 
 /* Sets a flag in a port status register but only set it if the port is
@@ -1414,8 +1405,7 @@ static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val)
     val &= OHCI_FMI_FI;
 
     if (val != ohci->fi) {
-        DPRINTF("usb-ohci: %s: FrameInterval = 0x%x (%u)\n",
-            ohci->name, ohci->fi, ohci->fi);
+        trace_usb_ohci_set_frame_interval(ohci->name, ohci->fi, ohci->fi);
     }
 
     ohci->fi = val;
@@ -1447,20 +1437,22 @@ static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
     if (old_state == new_state)
         return;
 
+    trace_usb_ohci_set_ctl(ohci->name, new_state);
     switch (new_state) {
     case OHCI_USB_OPERATIONAL:
         ohci_bus_start(ohci);
         break;
     case OHCI_USB_SUSPEND:
         ohci_bus_stop(ohci);
-        DPRINTF("usb-ohci: %s: USB Suspended\n", ohci->name);
+        /* clear pending SF otherwise linux driver loops in ohci_irq() */
+        ohci->intr_status &= ~OHCI_INTR_SF;
+        ohci_intr_update(ohci);
         break;
     case OHCI_USB_RESUME:
-        DPRINTF("usb-ohci: %s: USB Resume\n", ohci->name);
+        trace_usb_ohci_resume(ohci->name);
         break;
     case OHCI_USB_RESET:
-        ohci_reset(ohci);
-        DPRINTF("usb-ohci: %s: USB Reset\n", ohci->name);
+        ohci_roothub_reset(ohci);
         break;
     }
 }
@@ -1505,7 +1497,7 @@ static void ohci_set_hub_status(OHCIState *ohci, uint32_t val)
 
         for (i = 0; i < ohci->num_ports; i++)
             ohci_port_power(ohci, i, 0);
-        DPRINTF("usb-ohci: powered down all ports\n");
+        trace_usb_ohci_hub_power_down();
     }
 
     if (val & OHCI_RHS_LPSC) {
@@ -1513,7 +1505,7 @@ static void ohci_set_hub_status(OHCIState *ohci, uint32_t val)
 
         for (i = 0; i < ohci->num_ports; i++)
             ohci_port_power(ohci, i, 1);
-        DPRINTF("usb-ohci: powered up all ports\n");
+        trace_usb_ohci_hub_power_up();
     }
 
     if (val & OHCI_RHS_DRWE)
@@ -1545,11 +1537,11 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
     ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES);
 
     if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) {
-        DPRINTF("usb-ohci: port %d: SUSPEND\n", portnum);
+        trace_usb_ohci_port_suspend(portnum);
     }
 
     if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
-        DPRINTF("usb-ohci: port %d: RESET\n", portnum);
+        trace_usb_ohci_port_reset(portnum);
         usb_device_reset(port->port.dev);
         port->ctrl &= ~OHCI_PORT_PRS;
         /* ??? Should this also set OHCI_PORT_PESC.  */
@@ -1577,7 +1569,7 @@ static uint64_t ohci_mem_read(void *opaque,
 
     /* Only aligned reads are allowed on OHCI */
     if (addr & 3) {
-        fprintf(stderr, "usb-ohci: Mis-aligned read\n");
+        trace_usb_ohci_mem_read_unaligned(addr);
         return 0xffffffff;
     } else if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
         /* HcRhPortStatus */
@@ -1683,7 +1675,7 @@ static uint64_t ohci_mem_read(void *opaque,
             break;
 
         default:
-            fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr);
+            trace_usb_ohci_mem_read_bad_offset(addr);
             retval = 0xffffffff;
         }
     }
@@ -1700,7 +1692,7 @@ static void ohci_mem_write(void *opaque,
 
     /* Only aligned reads are allowed on OHCI */
     if (addr & 3) {
-        fprintf(stderr, "usb-ohci: Mis-aligned write\n");
+        trace_usb_ohci_mem_write_unaligned(addr);
         return;
     }
 
@@ -1723,7 +1715,7 @@ static void ohci_mem_write(void *opaque,
         ohci->status |= val;
 
         if (ohci->status & OHCI_STATUS_HCR)
-            ohci_reset(ohci);
+            ohci_soft_reset(ohci);
         break;
 
     case 3: /* HcInterruptStatus */
@@ -1802,7 +1794,7 @@ static void ohci_mem_write(void *opaque,
     case 25: /* HcHReset */
         ohci->hreset = val & ~OHCI_HRESET_FSBIR;
         if (val & OHCI_HRESET_FSBIR)
-            ohci_reset(ohci);
+            ohci_hard_reset(ohci);
         break;
 
     case 26: /* HcHInterruptEnable */
@@ -1814,7 +1806,7 @@ static void ohci_mem_write(void *opaque,
         break;
 
     default:
-        fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr);
+        trace_usb_ohci_mem_write_bad_offset(addr);
         break;
     }
 }
@@ -1846,11 +1838,12 @@ static USBPortOps ohci_port_ops = {
 static USBBusOps ohci_bus_ops = {
 };
 
-static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
-                         int num_ports, dma_addr_t localmem_base,
-                         char *masterbus, uint32_t firstport,
-                         AddressSpace *as)
+static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
+                          int num_ports, dma_addr_t localmem_base,
+                          char *masterbus, uint32_t firstport,
+                          AddressSpace *as, Error **errp)
 {
+    Error *err = NULL;
     int i;
 
     ohci->as = as;
@@ -1867,8 +1860,7 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
             usb_bit_time = 1;
         }
 #endif
-        DPRINTF("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n",
-                usb_frame_time, usb_bit_time);
+        trace_usb_ohci_init_time(usb_frame_time, usb_bit_time);
     }
 
     ohci->num_ports = num_ports;
@@ -1877,10 +1869,13 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
         for(i = 0; i < num_ports; i++) {
             ports[i] = &ohci->rhport[i].port;
         }
-        if (usb_register_companion(masterbus, ports, num_ports,
-                firstport, ohci, &ohci_port_ops,
-                USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) {
-            return -1;
+        usb_register_companion(masterbus, ports, num_ports,
+                               firstport, ohci, &ohci_port_ops,
+                               USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL,
+                               &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
         }
     } else {
         usb_bus_new(&ohci->bus, sizeof(ohci->bus), &ohci_bus_ops, dev);
@@ -1899,9 +1894,9 @@ static int usb_ohci_init(OHCIState *ohci, DeviceState *dev,
     usb_packet_init(&ohci->usb_packet);
 
     ohci->async_td = 0;
-    qemu_register_reset(ohci_reset, ohci);
 
-    return 0;
+    ohci->eof_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                                   ohci_frame_boundary, ohci);
 }
 
 #define TYPE_PCI_OHCI "pci-ohci"
@@ -1926,7 +1921,7 @@ static void ohci_die(OHCIState *ohci)
 {
     OHCIPCIState *dev = container_of(ohci, OHCIPCIState, state);
 
-    fprintf(stderr, "%s: DMA error\n", __func__);
+    trace_usb_ohci_die();
 
     ohci_set_interrupt(ohci, OHCI_INTR_UE);
     ohci_bus_stop(ohci);
@@ -1934,22 +1929,55 @@ static void ohci_die(OHCIState *ohci)
                  PCI_STATUS_DETECTED_PARITY);
 }
 
-static int usb_ohci_initfn_pci(PCIDevice *dev)
+static void usb_ohci_realize_pci(PCIDevice *dev, Error **errp)
 {
+    Error *err = NULL;
     OHCIPCIState *ohci = PCI_OHCI(dev);
 
     dev->config[PCI_CLASS_PROG] = 0x10; /* OHCI */
     dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */
 
-    if (usb_ohci_init(&ohci->state, DEVICE(dev), ohci->num_ports, 0,
-                      ohci->masterbus, ohci->firstport,
-                      pci_get_address_space(dev)) != 0) {
-        return -1;
+    usb_ohci_init(&ohci->state, DEVICE(dev), ohci->num_ports, 0,
+                  ohci->masterbus, ohci->firstport,
+                  pci_get_address_space(dev), &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
     }
-    ohci->state.irq = pci_allocate_irq(dev);
 
+    ohci->state.irq = pci_allocate_irq(dev);
     pci_register_bar(dev, 0, 0, &ohci->state.mem);
-    return 0;
+}
+
+static void usb_ohci_exit(PCIDevice *dev)
+{
+    OHCIPCIState *ohci = PCI_OHCI(dev);
+    OHCIState *s = &ohci->state;
+
+    trace_usb_ohci_exit(s->name);
+    ohci_bus_stop(s);
+
+    if (s->async_td) {
+        usb_cancel_packet(&s->usb_packet);
+        s->async_td = 0;
+    }
+    ohci_stop_endpoints(s);
+
+    if (!ohci->masterbus) {
+        usb_bus_release(&s->bus);
+    }
+
+    timer_del(s->eof_timer);
+    timer_free(s->eof_timer);
+}
+
+static void usb_ohci_reset_pci(DeviceState *d)
+{
+    PCIDevice *dev = PCI_DEVICE(d);
+    OHCIPCIState *ohci = PCI_OHCI(dev);
+    OHCIState *s = &ohci->state;
+
+    ohci_hard_reset(s);
 }
 
 #define TYPE_SYSBUS_OHCI "sysbus-ohci"
@@ -1972,11 +2000,19 @@ static void ohci_realize_pxa(DeviceState *dev, Error **errp)
 
     /* Cannot fail as we pass NULL for masterbus */
     usb_ohci_init(&s->ohci, dev, s->num_ports, s->dma_offset, NULL, 0,
-                  &address_space_memory);
+                  &address_space_memory, &error_abort);
     sysbus_init_irq(sbd, &s->ohci.irq);
     sysbus_init_mmio(sbd, &s->ohci.mem);
 }
 
+static void usb_ohci_reset_sysbus(DeviceState *dev)
+{
+    OHCISysBusState *s = SYSBUS_OHCI(dev);
+    OHCIState *ohci = &s->ohci;
+
+    ohci_hard_reset(ohci);
+}
+
 static Property ohci_pci_properties[] = {
     DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus),
     DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3),
@@ -1984,12 +2020,99 @@ static Property ohci_pci_properties[] = {
     DEFINE_PROP_END_OF_LIST(),
 };
 
+static const VMStateDescription vmstate_ohci_state_port = {
+    .name = "ohci-core/port",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(ctrl, OHCIPort),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static bool ohci_eof_timer_needed(void *opaque)
+{
+    OHCIState *ohci = opaque;
+
+    return timer_pending(ohci->eof_timer);
+}
+
+static const VMStateDescription vmstate_ohci_eof_timer = {
+    .name = "ohci-core/eof-timer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = ohci_eof_timer_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_TIMER_PTR(eof_timer, OHCIState),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static const VMStateDescription vmstate_ohci_state = {
+    .name = "ohci-core",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT64(sof_time, OHCIState),
+        VMSTATE_UINT32(ctl, OHCIState),
+        VMSTATE_UINT32(status, OHCIState),
+        VMSTATE_UINT32(intr_status, OHCIState),
+        VMSTATE_UINT32(intr, OHCIState),
+        VMSTATE_UINT32(hcca, OHCIState),
+        VMSTATE_UINT32(ctrl_head, OHCIState),
+        VMSTATE_UINT32(ctrl_cur, OHCIState),
+        VMSTATE_UINT32(bulk_head, OHCIState),
+        VMSTATE_UINT32(bulk_cur, OHCIState),
+        VMSTATE_UINT32(per_cur, OHCIState),
+        VMSTATE_UINT32(done, OHCIState),
+        VMSTATE_INT32(done_count, OHCIState),
+        VMSTATE_UINT16(fsmps, OHCIState),
+        VMSTATE_UINT8(fit, OHCIState),
+        VMSTATE_UINT16(fi, OHCIState),
+        VMSTATE_UINT8(frt, OHCIState),
+        VMSTATE_UINT16(frame_number, OHCIState),
+        VMSTATE_UINT16(padding, OHCIState),
+        VMSTATE_UINT32(pstart, OHCIState),
+        VMSTATE_UINT32(lst, OHCIState),
+        VMSTATE_UINT32(rhdesc_a, OHCIState),
+        VMSTATE_UINT32(rhdesc_b, OHCIState),
+        VMSTATE_UINT32(rhstatus, OHCIState),
+        VMSTATE_STRUCT_ARRAY(rhport, OHCIState, OHCI_MAX_PORTS, 0,
+                             vmstate_ohci_state_port, OHCIPort),
+        VMSTATE_UINT32(hstatus, OHCIState),
+        VMSTATE_UINT32(hmask, OHCIState),
+        VMSTATE_UINT32(hreset, OHCIState),
+        VMSTATE_UINT32(htest, OHCIState),
+        VMSTATE_UINT32(old_ctl, OHCIState),
+        VMSTATE_UINT8_ARRAY(usb_buf, OHCIState, 8192),
+        VMSTATE_UINT32(async_td, OHCIState),
+        VMSTATE_BOOL(async_complete, OHCIState),
+        VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription*[]) {
+        &vmstate_ohci_eof_timer,
+        NULL
+    }
+};
+
+static const VMStateDescription vmstate_ohci = {
+    .name = "ohci",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(parent_obj, OHCIPCIState),
+        VMSTATE_STRUCT(state, OHCIPCIState, 1, vmstate_ohci_state, OHCIState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static void ohci_pci_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
 
-    k->init = usb_ohci_initfn_pci;
+    k->realize = usb_ohci_realize_pci;
+    k->exit = usb_ohci_exit;
     k->vendor_id = PCI_VENDOR_ID_APPLE;
     k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB;
     k->class_id = PCI_CLASS_SERIAL_USB;
@@ -1997,6 +2120,8 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data)
     dc->desc = "Apple USB Controller";
     dc->props = ohci_pci_properties;
     dc->hotpluggable = false;
+    dc->vmsd = &vmstate_ohci;
+    dc->reset = usb_ohci_reset_pci;
 }
 
 static const TypeInfo ohci_pci_info = {
@@ -2020,6 +2145,7 @@ static void ohci_sysbus_class_init(ObjectClass *klass, void *data)
     set_bit(DEVICE_CATEGORY_USB, dc->categories);
     dc->desc = "OHCI USB Controller";
     dc->props = ohci_sysbus_properties;
+    dc->reset = usb_ohci_reset_sysbus;
 }
 
 static const TypeInfo ohci_sysbus_info = {