qemu-devel
[Top][All Lists]
Advanced

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

[Qemu-devel] [PATCH] add 1394 OHCI device


From: Ladi Prosek
Subject: [Qemu-devel] [PATCH] add 1394 OHCI device
Date: Wed, 2 Mar 2016 18:32:07 +0100

This patch adds 1394 (FireWire) virtual device support to QEMU.

Signed-off-by: Ladi Prosek <address@hidden>
---
I am resurrecting this patch sent to the list by Itamar last year. My
motivation is remote Windows kernel debugging - probably the same reason
why this work was started in the first place.

I have not changed anything in the actual logic as I am not familiar
with how the 1394 device works. I have however:

- added type_init & co so the device can actually be instantiated
- refactored the freakishly long hcd_chr_receive into several subroutines
- eliminated all bitfields and refactored everything to use explicit
bit operations
- made the device available on all platforms
- renamed structures and fields to follow the QEMU coding style
(checkpatch.pl is clean)

I am cc'ing Itamar and the folks who provided feedback last year in the
hope of achieving at least some continuity.

 default-configs/pci.mak  |    1 +
 hw/1394/Makefile.objs    |    1 +
 hw/1394/hcd-ohci.c       | 1736 ++++++++++++++++++++++++++++++++++++++++++++++
 hw/1394/hcd-ohci.h       |  131 ++++
 hw/Makefile.objs         |    1 +
 include/hw/pci/pci_ids.h |    3 +
 6 files changed, 1873 insertions(+)
 create mode 100644 hw/1394/Makefile.objs
 create mode 100644 hw/1394/hcd-ohci.c
 create mode 100644 hw/1394/hcd-ohci.h

diff --git a/default-configs/pci.mak b/default-configs/pci.mak
index 4fa9a28..573d55c 100644
--- a/default-configs/pci.mak
+++ b/default-configs/pci.mak
@@ -5,6 +5,7 @@ CONFIG_USB_UHCI=y
 CONFIG_USB_OHCI=y
 CONFIG_USB_EHCI=y
 CONFIG_USB_XHCI=y
+CONFIG_1394_OHCI=y
 CONFIG_NE2000_PCI=y
 CONFIG_EEPRO100_PCI=y
 CONFIG_PCNET_PCI=y
diff --git a/hw/1394/Makefile.objs b/hw/1394/Makefile.objs
new file mode 100644
index 0000000..bddc0e1
--- /dev/null
+++ b/hw/1394/Makefile.objs
@@ -0,0 +1 @@
+common-obj-$(CONFIG_1394_OHCI) += hcd-ohci.o
diff --git a/hw/1394/hcd-ohci.c b/hw/1394/hcd-ohci.c
new file mode 100644
index 0000000..294b9a1
--- /dev/null
+++ b/hw/1394/hcd-ohci.c
@@ -0,0 +1,1736 @@
+/*
+ * FireWire (1394) support
+ *
+ * Copyright (c) 2016 Guardicore
+ * Authors:
+ *  Itamar Tal   <address@hidden>
+ * Originally Written by James Harper
+ *
+ * This is a `bare-bones' implementation of the Firewire 1394 OHCI
+ * for virtual->virtual firewire connections emulation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "hcd-ohci.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "hw/loader.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/char.h"
+#include "qemu/timer.h"
+
+#define OHCI_1394_MMIO_SIZE 0x800
+
+#define HCCONTROL_RESET 16
+#define HCCONTROL_LINK_ENABLE 17
+#define HCCONTROL_LPS 19
+
+#define HCCONTROL_RESET_MASK (1 << (HCCONTROL_RESET))
+#define HCCONTROL_LINK_ENABLE_MASK (1 << (HCCONTROL_LINK_ENABLE))
+#define HCCONTROL_LPS_MASK (1 << (HCCONTROL_LPS))
+
+#define HCD_STATE_UNPLUGGED    0 /* no connection */
+#define HCD_STATE_MAGIC        1 /* waiting for magic */
+#define HCD_STATE_DISCONNECTED 2 /* waiting for link packet */
+#define HCD_STATE_ARBITRATION1 3 /* send bid */
+#define HCD_STATE_ARBITRATION2 4 /* receive bid and compare */
+#define HCD_STATE_CONNECTED    5 /* connected and ready to go */
+
+#define IS_Ax_ACTIVE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] & (1 << 10))
+#define SET_Ax_ACTIVE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] |= (1 << 10))
+#define CLR_Ax_ACTIVE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] &= ~(1 << 10))
+
+#define IS_Ax_DEAD(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] & (1 << 11))
+#define SET_Ax_DEAD(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] |= (1 << 11))
+
+#define IS_Ax_WAKE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] & (1 << 12))
+#define CLR_Ax_WAKE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] &= ~(1 << 12))
+
+#define IS_Ax_RUN(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] & (1 << 15))
+
+#define SET_Ax_EVENT_CODE(n, e) s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x000) >> 2] = ((s->mmio.regs[((0x180 + \
+                                      ((n) * 0x20)) + 0x00) >> 2] & \
+                                      0xFFFFFFE0) | (e))
+
+#define Ax_COMMAND_PTR(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x00C) >> 2])
+#define SET_Ax_COMMAND_PTR(n, c) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x00C) >> 2] = (c))
+
+#define Ax_CONTEXT_CONTROL(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \
+                                      0x00) >> 2])
+
+/* interrupt event flags */
+#define INT_REQ_TX_COMPLETE           0x00000001
+#define INT_RESP_TX_COMPLETE          0x00000002
+#define INT_ARRQ                      0x00000004
+#define INT_ARRS                      0x00000008
+#define INT_RQP_KT                    0x00000010
+#define INT_RSP_KT                    0x00000020
+#define INT_ISOCH_TX                  0x00000040
+#define INT_ISOCH_RX                  0x00000080
+#define INT_POSTED_WRITE_ERR          0x00000100
+#define INT_LOCK_RESP_ERR             0x00000200
+#define INT_SELF_ID_COMPLETE2         0x00008000
+#define INT_SELF_ID_COMPLETE          0x00010000
+#define INT_BUS_RESET                 0x00020000
+#define INT_REG_ACCESS_FAIL           0x00040000
+#define INT_PHY                       0x00080000
+#define INT_CYCLE_SUNCH               0x00100000
+#define INT_CYCLE_64_SECONDS          0x00200000
+#define INT_CYCLE_LOST                0x00400000
+#define INT_CYCLE_INCONSISTENT        0x00800000
+#define INT_UNRECOVERABLE_ERROR       0x01000000
+#define INT_CYCLE_TOO_LONG            0x02000000
+#define INT_PHY_REG_RCVD              0x04000000
+#define INT_ACK_TARDY                 0x08000000
+#define INT_SOFT_INTERRUPT            0x20000000
+#define INT_VENDOR_SPECIFIC           0x40000000
+#define INT_MASTER_INT_ENABLE         0x80000000
+
+/* node ID layout */
+#define NODEID_NODE_NUMBER            0x0000003F
+#define NODEID_BUS_NUMBER             0x0000FFC0
+#define NODEID_CPS                    0x08000000
+#define NODEID_ROOT                   0x40000000
+#define NODEID_ID_VALID               0x80000000
+
+/* PhyControl and its flag layout */
+#define PHY_CONTROL_WR_FLAGS_REG_ADDR 0x0F
+#define PHY_CONTROL_WR_FLAGS_WR_REG   0x40
+#define PHY_CONTROL_WR_FLAGS_RD_REG   0x80
+#define PHY_CONTROL_RD_FLAGS_RD_ADDR  0x0F
+#define PHY_CONTROL_RD_FLAGS_RD_DONE  0x80
+
+typedef union {
+    uint32_t value;
+    struct {
+        uint8_t wr_data;
+        uint8_t wr_flags;
+        uint8_t rd_data;
+        uint8_t rd_flags;
+    };
+} PhyControl;
+
+typedef union {
+    uint32_t value;
+    struct {
+        uint16_t rom_crc_value;
+        uint8_t crc_length;
+        uint8_t info_length;
+    };
+} ConfigROMHdr;
+
+/* SelfIDCount and its field layout */
+#define SELF_ID_COUNT_LO_WORD_SIZE 0x07FC
+#define SELF_ID_COUNT_FLAGS_ERROR  0x80
+
+typedef union {
+    uint32_t value;
+    struct {
+        uint16_t lo_word;
+        uint8_t generation;
+        uint8_t flags;
+    };
+} SelfIDCount;
+
+typedef struct {
+    union {
+       struct {
+           uint32_t context_control;
+           uint32_t context_control_alt;
+       };
+       struct {
+           uint32_t context_control_set;
+           uint32_t context_control_clear;
+       };
+    };
+    uint32_t reserved_08;
+    uint32_t command_ptr;
+    uint32_t reserved_10;
+    uint32_t reserved_14;
+    uint32_t reserved_18;
+    uint32_t reserved_1c;
+} AsyncContext;
+
+typedef union {
+    struct {
+        uint32_t version;
+        uint32_t guid_rom;
+        uint32_t at_retries;
+        union {
+            uint32_t csr_read_data;  /* 00c */
+            uint32_t csr_write_data; /* 00c */
+        };
+        uint32_t csr_compare_data;
+        uint32_t csr_control;
+        ConfigROMHdr config_rom_hdr;
+        uint32_t bus_id;
+        uint32_t bus_options;
+        uint32_t guid_hi;
+        uint32_t guid_lo;
+        uint32_t reserved_002c;
+        uint32_t reserved_0030;
+        uint32_t config_rom_map;
+        uint32_t posted_write_address_lo;
+        uint32_t posted_write_address_hi;
+        uint32_t vendor_id;
+        uint32_t reserved_0044;
+        uint32_t reserved_0048;
+        uint32_t reserved_004c;
+        union {
+            struct {
+                /* read */
+                uint32_t hc_control;
+                uint32_t hc_control_alt;
+            };
+            struct {
+                /* write */
+                uint32_t hc_control_set;
+                uint32_t hc_control_clear;
+            };
+        };
+        uint32_t reserved_0058;
+        uint32_t reserved_005c;
+        uint32_t reserved_0060;
+        uint32_t self_id_buffer;
+        SelfIDCount self_id_count;
+        uint32_t reserved_006c;
+        union {
+            struct {
+                /* read */
+                uint32_t ir_multi_chan_mask_hi;
+                uint32_t ir_multi_chan_mask_hi_alt;
+            };
+            struct {
+                /* write */
+                uint32_t ir_multi_chan_mask_hi_set;
+                uint32_t ir_multi_chan_mask_hi_clear;
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t ir_multi_chan_mask_lo;
+                uint32_t ir_multi_chan_mask_lo_alt;
+            };
+            struct {
+                /* write */
+                uint32_t ir_multi_chan_mask_lo_set;
+                uint32_t ir_multi_chan_mask_lo_clear;
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t int_event;            /* 0080 */
+                uint32_t int_event_masked;     /* 0084 */
+            };
+            struct {
+                /* write */
+                uint32_t int_event_set;        /* 0080 */
+                uint32_t int_event_clear;      /* 0084 */
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t int_mask;
+                uint32_t int_mask_alt;
+            };
+            struct {
+                /* write */
+                uint32_t int_mask_set;
+                uint32_t int_mask_clear;
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t iso_xmit_int_event;
+                uint32_t iso_xmit_int_event_masked;
+            };
+            struct {
+                /* write */
+                uint32_t iso_xmit_int_event_set;
+                uint32_t iso_xmit_int_event_clear;
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t iso_xmit_int_mask;
+                uint32_t iso_xmit_int_mask_alt;
+            };
+            struct {
+                /* write */
+                uint32_t iso_xmit_int_mask_set;
+                uint32_t iso_xmit_int_mask_clear;
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t iso_recv_int_event;
+                uint32_t iso_recv_int_event_masked;
+            };
+            struct {
+                /* write */
+                uint32_t iso_recv_int_event_set;
+                uint32_t iso_recv_int_event_clear;
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t iso_recv_int_mask;
+                uint32_t iso_recv_int_mask_alt;
+            };
+            struct {
+                /* write */
+                uint32_t iso_recv_int_mask_set;
+                uint32_t iso_recv_int_mask_clear;
+            };
+        };
+        uint32_t initial_bandwidth_available;   /* 00B0 */
+        uint32_t initial_channels_available_hi; /* 00B4 */
+        uint32_t initial_channels_available_lo; /* 00B8 */
+        uint32_t reserved_00bc;
+        uint32_t reserved_00c0;
+        uint32_t reserved_00c4;
+        uint32_t reserved_00c8;
+        uint32_t reserved_00dc;
+        uint32_t reserved_00d0;
+        uint32_t reserved_00d4;
+        uint32_t reserved_00d8;
+        uint32_t fairness_control; /* 00dc */
+        union {
+            struct {
+                /* read */
+                uint32_t link_control;       /* 0xe0 */
+                uint32_t link_control_alt;   /* 0xe4 */
+            };
+            struct {
+                /* write */
+                uint32_t link_control_set;   /* 0xe0 */
+                uint32_t link_control_clear; /* 0xe4 */
+            };
+        };
+        uint32_t node_id;                                  /* 00e8 */
+        PhyControl phy_control;                            /* 00ec */
+        uint32_t isochronous_cycle_timer;                  /* 00f0 */
+        uint32_t reserved_00f4;
+        uint32_t reserved_00f8;
+        uint32_t reserved_00fc;
+        union {
+            struct {
+                /* read */
+                uint32_t asynchronous_request_filter_hi;     /* 0100 */
+                uint32_t asynchronous_request_filter_hi_alt; /* 0104 */
+            };
+            struct {
+                /* write */
+                uint32_t asynchronous_request_filter_hi_set;   /* 0100 */
+                uint32_t asynchronous_request_filter_hi_clear; /* 0104 */
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t asynchronous_request_filter_lo;      /* 0108 */
+                uint32_t asynchronous_request_filter_lo_alt;  /* 010c */
+            };
+            struct {
+                /* write */
+                uint32_t asynchronous_request_filter_lo_set;   /* 0108 */
+                uint32_t asynchronous_request_filter_lo_clear; /* 010c */
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t physical_request_filter_hi;          /* 0110 */
+                uint32_t physical_request_filter_hi_alt;      /* 0114 */
+            };
+            struct {
+                /* write */
+                uint32_t physical_request_filter_hi_set;      /* 0110 */
+                uint32_t physical_request_filter_hi_clear;    /* 0114 */
+            };
+        };
+        union {
+            struct {
+                /* read */
+                uint32_t physical_request_filter_lo;          /* 0118 */
+                uint32_t physical_request_filter_lo_alt;      /* 011c */
+            };
+            struct {
+                /* write */
+                uint32_t physical_request_filter_lo_set;      /* 0118 */
+                uint32_t physical_request_filter_lo_clear;    /* 011c */
+            };
+        };
+        uint32_t physical_upper_bound; /* 0120 */
+        uint32_t reserved_0124;
+        uint32_t reserved_0128;
+        uint32_t reserved_012c;
+        uint32_t reserved_0130;
+        uint32_t reserved_0134;
+        uint32_t reserved_0138;
+        uint32_t reserved_013c;
+        uint32_t reserved_0140;
+        uint32_t reserved_0144;
+        uint32_t reserved_0148;
+        uint32_t reserved_014c;
+        uint32_t reserved_0150;
+        uint32_t reserved_0154;
+        uint32_t reserved_0158;
+        uint32_t reserved_015c;
+        uint32_t reserved_0160;
+        uint32_t reserved_0164;
+        uint32_t reserved_0168;
+        uint32_t reserved_016c;
+        uint32_t reserved_0170;
+        uint32_t reserved_0174;
+        uint32_t reserved_0178;
+        uint32_t reserved_017c;
+        AsyncContext async_request_transmit;
+        AsyncContext async_response_transmit;
+        AsyncContext async_request_receive;
+        AsyncContext async_response_receive;
+        /* Isoch stuff */
+    };
+    uint32_t regs[OHCI_1394_MMIO_SIZE >> 2];
+} OHCIDeviceRegs;
+
+/* OHCIPhyState and its register layout */
+#define PHY_REG0_CPS         0x01
+#define PHY_REG0_ROOT        0x02
+#define PHY_REG0_PHYSICAL_ID 0xFC
+#define PHY_REG1_GAP_COUNT   0x3F
+#define PHY_REG1_IBR         0x40
+#define PHY_REG1_RHB         0x80
+#define PHY_REG2_NUM_PORTS   0x0F
+#define PHY_REG2_EXTENDED    0xE0
+#define PHY_REG3_DELAY       0x0F
+#define PHY_REG3_PHY_SPEED   0xE0
+#define PHY_REG4_PWR_CLASS   0x07
+#define PHY_REG4_JITTER      0x38
+#define PHY_REG4_C           0x40
+#define PHY_REG4_L           0x80
+#define PHY_REG5_EMC         0x01
+#define PHY_REG5_EAA         0x02
+#define PHY_REG5_PEI         0x04
+#define PHY_REG5_STOI        0x08
+#define PHY_REG5_CPSI        0x10
+#define PHY_REG5_CTOI        0x20
+#define PHY_REG5_ISBR        0x40
+#define PHY_REG5_RPIE        0x80
+#define PHY_REG7_PORT_SELECT 0x0F
+#define PHY_REG7_PAGE_SELECT 0xE0
+
+typedef union {
+    uint8_t bytes[16];
+    struct {
+        uint8_t reg0;
+        uint8_t reg1;
+        uint8_t reg2;
+        uint8_t reg3;
+        uint8_t reg4;
+        uint8_t reg5;
+        uint8_t reg6;
+        uint8_t reg7;
+    };
+} OHCIPhyState;
+
+/* OHCISelfID and its register layout */
+#define SELF_ID_REG0_M         0x01 /* = 00 */
+#define SELF_ID_REG0_INITIATED 0x02 /* = 02 for root node */
+#define SELF_ID_REG0_P2        0x0C /* = 00 */
+#define SELF_ID_REG0_P1        0x30 /* = 00 */
+#define SELF_ID_REG0_P0        0xC0 /* = C0, maybe 80 when "child" to root */
+#define SELF_ID_REG1_PWR       0x07 /* = 00 */
+#define SELF_ID_REG1_C         0x08 /* = 08 when root */
+#define SELF_ID_REG1_DEL       0x30 /* = 00? */
+#define SELF_ID_REG1_SP        0xC0 /* = 00? */
+#define SELF_ID_REG2_GAP_CNT   0x3F /* = 00? */
+#define SELF_ID_REG2_L         0x40 /* = 40? maybe just when connected */
+#define SELF_ID_REG3_NODE_ID   0x3F
+#define SELF_ID_REG3_TYPE      0xC0 /* = 80 */
+
+typedef union {
+    uint32_t val;
+    struct {
+        uint8_t reg0;
+        uint8_t reg1;
+        uint8_t reg2;
+        uint8_t reg3;
+    };
+} OHCISelfID;
+
+struct OHCI1394State;
+typedef struct OHCI1394State OHCI1394State;
+
+/* HCDAtDB and its flag layout */
+#define HCD_AT_DB_FLAGS_BRANCH    0x000C
+#define HCD_AT_DB_FLAGS_INTERRUPT 0x0030
+#define HCD_AT_DB_FLAGS_PING      0x0080
+#define HCD_AT_DB_FLAGS_KEY       0x0700
+#define HCD_AT_DB_FLAGS_CMD       0xF000
+
+typedef struct {
+    uint16_t req_count;
+    uint16_t flags;
+    uint32_t data_address;
+    uint32_t branch_address;
+    uint16_t timestamp;
+    uint16_t transfer_status;
+} HCDAtDB;
+
+/* HCDArDB and its flag layout */
+#define HCD_AR_DB_FLAGS_BRANCH    0x000C
+#define HCD_AR_DB_FLAGS_INTERRUPT 0x0030
+#define HCD_AR_DB_FLAGS_KEY       0x0700
+#define HCD_AR_DB_FLAGS_STATUS    0x0800
+#define HCD_AR_DB_FLAGS_CMD       0xF000
+
+typedef struct {
+    uint16_t req_count;
+    uint16_t flags;
+    uint32_t data_address;
+    uint32_t branch_address;
+    uint16_t res_count;
+    uint16_t transfer_status;
+} HCDArDB;
+
+typedef struct {
+    OHCI1394State *s;
+    QEMUTimer *timer;
+    uint32_t num; /* base register is 0x180 + num * 0x20 */
+    uint32_t address; /* current address */
+    uint32_t response;
+} HCDTimerState;
+
+struct OHCI1394State {
+    PCIDevice pci_dev;
+    MemoryRegion mmio_bar;
+    OHCIDeviceRegs mmio;
+    HCDTimerState at_req_timer;
+    HCDTimerState at_rsp_timer;
+    OHCIPhyState phy;
+    uint8_t phy_pages[8][8];
+    qemu_irq irq;
+    uint32_t irq_asserted;
+    /* properties from init */
+    CharDriverState *chr;
+    int state;
+    int other_link;
+    uint16_t bid;
+    int root;
+    int bufpos;
+    uint8_t buf[16 + 65536]; /* maximum request size + maximum data size */
+};
+
+#define REG_OFFSET(field)  offsetof(OHCIDeviceRegs, field)
+
+static void hcd_bus_reset(OHCI1394State *s);
+static void hcd_chr_event(void *opaque, int event);
+
+static void
+hcd_check_irq(OHCI1394State *s) {
+    if ((s->mmio.int_mask & 0x80000000) &&
+        (s->mmio.int_event & s->mmio.int_mask)) {
+        if (!s->irq_asserted) {
+            qemu_set_irq(s->irq, 1);
+            s->irq_asserted = 1;
+        }
+    } else {
+        if (s->irq_asserted) {
+            qemu_set_irq(s->irq, 0);
+            s->irq_asserted = 0;
+        }
+    }
+}
+
+static void
+hcd_soft_reset(OHCI1394State *s) {
+    s->mmio.bus_options = 0x00008002; /* 5.11 */
+    s->mmio.hc_control &= 0x00C00000; /* 5.7.2 */
+}
+
+static void
+hcd_hard_reset(OHCI1394State *s) {
+    memset(&s->mmio, 0, sizeof(s->mmio));
+    s->mmio.version = 0x00010010; /* Release 1.1 of OHCI spec */
+    s->mmio.bus_id = 0x31333934; /* 1394 */
+    s->mmio.bus_options = 0x00008002; /* 5.11 */
+    s->mmio.guid_hi = 0x89abcdef;
+    s->mmio.guid_lo = 0x01234567;
+    memset(&s->phy, 0, sizeof(s->phy));
+    s->phy.reg2 = 1 | (s->phy.reg2 & ~PHY_REG2_NUM_PORTS);
+    s->phy.reg4 |= PHY_REG4_L;
+    s->phy.reg4 |= PHY_REG4_C;
+    s->phy_pages[0][0] = 0x08; /* 0xFE; */
+    hcd_soft_reset(s);
+}
+
+static void
+hcd_complete_self_id(OHCI1394State *s) {
+    s->mmio.node_id = (s->root) ? 0 : 1; /* 5.11 */
+    s->mmio.node_id |= (0x3ff << 6);     /* busNumber */
+    if (s->state == HCD_STATE_CONNECTED) {
+        s->mmio.node_id |= NODEID_CPS;
+    }
+    if (s->root) {
+        s->mmio.node_id |= NODEID_ROOT;
+    }
+    s->mmio.node_id |= NODEID_ID_VALID;
+    s->mmio.self_id_count.lo_word &= ~SELF_ID_COUNT_LO_WORD_SIZE;
+    s->mmio.self_id_count.flags &= ~SELF_ID_COUNT_FLAGS_ERROR;
+    if (s->mmio.link_control & 0x00000200) { /* if RcvSelfID */
+        uint32_t tmp = 0;
+        OHCISelfID sid;
+
+        sid.val = 0;
+        sid.reg0 |= SELF_ID_REG0_INITIATED;
+        sid.reg0 |= (0x80 & SELF_ID_REG0_P0);
+        sid.reg1 |= SELF_ID_REG1_C;
+        sid.reg2 |= SELF_ID_REG2_L;
+        sid.reg3 |= (0x80 & SELF_ID_REG3_TYPE);
+        dma_memory_write(&address_space_memory,
+                         s->mmio.self_id_buffer + 4,
+                         &sid.val, 4);
+        sid.val = ~sid.val;
+        dma_memory_write(&address_space_memory,
+                         s->mmio.self_id_buffer + 8,
+                         &sid.val, 4);
+        s->mmio.self_id_count.lo_word += 8;
+
+        if (s->state == HCD_STATE_CONNECTED) {
+            sid.val = 0;
+            sid.reg0 |= SELF_ID_REG0_P0;
+            sid.reg2 |= SELF_ID_REG2_L;
+            sid.reg3 = 1; /* node id */
+            sid.reg3 |= (0x80 & SELF_ID_REG3_TYPE);
+            dma_memory_write(&address_space_memory,
+                             s->mmio.self_id_buffer + 12,
+                             &sid.val, 4);
+            sid.val = ~sid.val;
+            dma_memory_write(&address_space_memory,
+                             s->mmio.self_id_buffer + 16,
+                             &sid.val, 4);
+            s->mmio.self_id_count.lo_word += 8;
+        }
+
+        tmp = (s->mmio.self_id_count.generation << 16) | 1;
+        dma_memory_write(&address_space_memory,
+                         s->mmio.self_id_buffer,
+                         &tmp, 4);
+        s->mmio.self_id_count.lo_word += 4;
+    }
+    s->mmio.int_event |= 0x00018000; /* selfIDcomplete | selfIDcomplete2 */
+    hcd_check_irq(s);
+}
+
+static void
+hcd_async_rx_rsp_packet(OHCI1394State *s, uint8_t *buf, uint32_t size,
+                        uint8_t response) {
+    int num = 3;
+    HCDArDB db;
+    uint32_t data_address = 0;
+    uint32_t status = 0;
+    int state = 0;
+
+    if (size == 0) {
+        return;
+    }
+    SET_Ax_EVENT_CODE(num, response);
+    dma_memory_read(&address_space_memory,
+                    Ax_COMMAND_PTR(num) & 0xFFFFFFF0,
+                    &db, sizeof(db));
+    data_address = db.data_address + db.req_count - db.res_count;
+    while (state != 3) {
+        int write_size;
+
+        db.transfer_status =
+                        s->mmio.async_response_receive.context_control & 
0xFFFF;
+        if (db.res_count == 0) {
+            dma_memory_write(&address_space_memory,
+                             Ax_COMMAND_PTR(num) & 0xFFFFFFF0,
+                             &db, sizeof(db));
+            if (db.branch_address == 0) {
+                CLR_Ax_ACTIVE(num);
+                /* TODO: need to roll back if this happens */
+            }
+            SET_Ax_COMMAND_PTR(num, db.branch_address);
+            dma_memory_read(&address_space_memory,
+                            Ax_COMMAND_PTR(num) & 0xFFFFFFF0,
+                            &db, sizeof(db));
+            data_address = db.data_address + db.req_count - db.res_count;
+        }
+        switch (state) {
+        case 0:
+            if (db.res_count > size) {
+                write_size = size;
+            } else {
+                write_size = db.res_count;
+            }
+            dma_memory_write(&address_space_memory,
+                             data_address,
+                             buf, write_size);
+            db.res_count -= write_size;
+            data_address += write_size;
+            size -= write_size;
+            buf += write_size;
+            if (size == 0) {
+                state = 1;
+            }
+            break;
+        case 1:
+            status = s->mmio.async_response_receive.context_control << 16;
+            db.transfer_status =
+                        s->mmio.async_response_receive.context_control & 
0xFFFF;
+            dma_memory_write(&address_space_memory, data_address, &status, 4);
+            db.res_count -= 4;
+            data_address += 4;
+            dma_memory_write(&address_space_memory,
+                             Ax_COMMAND_PTR(num) & 0xFFFFFFF0,
+                             &db, sizeof(db));
+            state = 2;
+            break;
+        case 2:
+            /* this state exists to go around the loop again and update the db
+            if required */
+            state = 3;
+            break;
+        }
+    }
+    s->mmio.int_event |= (1 << 5);
+    hcd_check_irq(s);
+}
+
+static void
+hcd_async_rx_run(OHCI1394State *s, uint32_t addr) {
+    int num;
+
+    num = (addr & 0x0180) >> 7;
+    SET_Ax_ACTIVE(num);
+}
+
+static void
+hcd_async_rx_stop(OHCI1394State *s, uint32_t addr) {
+    int num;
+
+    num = (addr & 0x0180) >> 7;
+    CLR_Ax_ACTIVE(num);
+}
+
+static void
+hcd_async_rx_wake(OHCI1394State *s, uint32_t addr) {
+    uint32_t address;
+    HCDArDB db;
+    int num;
+
+    num = (addr & 0x0180) >> 7;
+    if (IS_Ax_ACTIVE(num)) {
+        return;
+    }
+    address = s->mmio.regs[(addr >> 2) + 0x00c];
+    dma_memory_read(&address_space_memory,
+                    address & 0xFFFFFFF0,
+                    &db, sizeof(db));
+    if ((db.branch_address & 0x0000000f) != 0) {
+        SET_Ax_ACTIVE(num);
+        SET_Ax_COMMAND_PTR(num, db.branch_address);
+    }
+}
+
+static void
+hcd_at_run(HCDTimerState *t) {
+    OHCI1394State *s = t->s;
+    t->address = Ax_COMMAND_PTR(t->num) & 0xfffffff0;
+    t->response = EVT_TCODE_ERR;
+    SET_Ax_ACTIVE(t->num);
+}
+
+static void
+hcd_at_timer(void *o) {
+    HCDTimerState *t = (HCDTimerState *)o;
+    OHCI1394State *s = t->s;
+    OHCIPacketHeader packet_header;
+    HCDAtDB db;
+
+    if (IS_Ax_DEAD(t->num) || !IS_Ax_RUN(t->num)) {
+        CLR_Ax_WAKE(t->num);
+        CLR_Ax_ACTIVE(t->num);
+        return;
+    }
+    if (!IS_Ax_ACTIVE(t->num)) {
+        if (!IS_Ax_WAKE(t->num)) {
+            return;
+        }
+        CLR_Ax_WAKE(t->num);
+        dma_memory_read(&address_space_memory,
+                        t->address, &db,
+                        sizeof(db));
+        if (!(db.branch_address & 0x0000000f)) {
+            return;
+        }
+        SET_Ax_COMMAND_PTR(t->num, db.branch_address);
+        hcd_at_run(t); /* also sets active */
+    }
+    CLR_Ax_WAKE(t->num);
+    dma_memory_read(&address_space_memory,
+                    t->address, &db,
+                    sizeof(db));
+    if ((db.flags & HCD_AT_DB_FLAGS_CMD) == 0 &&
+        (db.flags & HCD_AT_DB_FLAGS_KEY) == 0) {
+        /* Do nothing */
+    } else if ((db.flags & HCD_AT_DB_FLAGS_CMD) == 0 &&
+        (db.flags & HCD_AT_DB_FLAGS_KEY) == 0x0200) {
+        /* OUTPUT_MORE_Immediate */
+    } else if ((db.flags & HCD_AT_DB_FLAGS_CMD) == 0x1000 &&
+        (db.flags & HCD_AT_DB_FLAGS_KEY) == 0) {
+        /* OUTPUT_LAST */
+    } else if ((db.flags & HCD_AT_DB_FLAGS_CMD) == 0x1000 &&
+        (db.flags & HCD_AT_DB_FLAGS_KEY) == 0x0200) {
+        /* OUTPUT_LAST_Immediate */
+    } else {
+        /* UNKNOWN COMMAND */
+        return ;
+    }
+
+    switch (db.flags & HCD_AT_DB_FLAGS_KEY) {
+    case 0: { /* non-Immediate */
+        uint8_t buf[65536];
+        dma_memory_read(&address_space_memory,
+                        db.data_address, buf,
+                        db.req_count);
+        qemu_chr_fe_write(s->chr, buf, db.req_count);
+        break;
+    }
+    case 0x0200: { /* Immediate */
+        uint32_t data[4];
+        dma_memory_read(&address_space_memory,
+                        t->address + sizeof(db),
+                        data, db.req_count);
+
+        packet_header = *(OHCIPacketHeader *)data;
+        switch (packet_header.flags & OHCI_PACKET_FLAGS_T_CODE) {
+        case 0x00: { /* quadlet write - quadlet format */
+            OHCIReqQuadletPacket at_packet = *(OHCIReqQuadletPacket *)data;
+            qemu_chr_fe_write(s->chr, (uint8_t *)data, sizeof(at_packet));
+            t->response = ACK_PENDING;
+            break;
+        }
+        case 0x10: { /* block write - block write format */
+            qemu_chr_fe_write(s->chr, (uint8_t *)data, db.req_count);
+            t->response = ACK_PENDING;
+            break;
+        }
+        case 0x40: { /* quadlet read - nodata format */
+            qemu_chr_fe_write(s->chr, (uint8_t *)data, db.req_count);
+            t->response = ACK_PENDING;
+            break;
+        }
+        case 0x50: { /* read bytes from target */
+            qemu_chr_fe_write(s->chr, (uint8_t *)data, db.req_count);
+            t->response = ACK_PENDING;
+            break;
+        }
+        case 0xe0: { /* PHY packet */
+            /* probably just configuring the gap count... */
+            t->response = ACK_COMPLETE;
+            /* reset because PHY packet */
+            hcd_bus_reset(s); /* not all PHY packets require reset... */
+            break;
+        }
+        default:
+            break;
+        }
+        break;
+    }
+    default:
+        break;
+    }
+    if ((db.flags & HCD_AT_DB_FLAGS_CMD) == 0) { /* more */
+        if ((db.flags & HCD_AT_DB_FLAGS_KEY) == 0x0200) {
+            t->address += sizeof(db) + sizeof(int32_t) * 4;
+        } else {
+            t->address += sizeof(db);
+        }
+    } else { /* last */
+        if ((db.flags & HCD_AT_DB_FLAGS_INTERRUPT) == 0x0030) {
+            s->mmio.int_event |= (1 << t->num);
+        }
+        SET_Ax_EVENT_CODE(t->num, t->response);
+        db.transfer_status = (uint16_t)Ax_CONTEXT_CONTROL(t->num);
+        dma_memory_write(&address_space_memory, t->address, &db, sizeof(db));
+        if ((db.branch_address & 0x0000000f) == 0) {
+            CLR_Ax_ACTIVE(t->num);
+            return;
+        }
+        SET_Ax_COMMAND_PTR(t->num, db.branch_address);
+        hcd_at_run(t);
+    }
+    timer_mod(t->timer, 0);
+    /* maybe
+    t_now + get_ticks_per_sec() / 100000);
+    100/sec isn't going to be right */
+}
+
+static void
+hcd_bus_reset(OHCI1394State *s) {
+    uint32_t bus_reset_packet[3] = {0x000000e0, 0x00000000, 0x00000000};
+    s->mmio.node_id = (s->mmio.node_id & ~NODEID_BUS_NUMBER) | (0x3ff << 6);
+    s->mmio.node_id &= ~NODEID_CPS;
+    s->mmio.node_id &= ~NODEID_ROOT;
+    s->mmio.node_id &= ~NODEID_ID_VALID;
+    s->mmio.self_id_count.generation++;
+    s->mmio.int_event |= 0x00020000; /* bus reset complete */
+    if (s->state != HCD_STATE_CONNECTED) {
+        s->root = 1;
+    }
+    s->mmio.async_request_transmit.context_control &= 0xFFFFFBFF;
+    s->mmio.async_response_transmit.context_control &= 0xFFFFFBFF;
+    if (s->mmio.async_response_receive.context_control & 0x00008000) {
+        bus_reset_packet[2] |= s->mmio.self_id_count.generation << 16;
+        hcd_async_rx_rsp_packet(s, (uint8_t *)bus_reset_packet,
+                                sizeof(bus_reset_packet), EVT_BUS_RESET);
+    }
+    hcd_complete_self_id(s);
+}
+
+static uint8_t
+hcd_phy_read(OHCI1394State *s, uint8_t reg) {
+    if (reg < 8) {
+        return s->phy.bytes[reg];
+    } else {
+        int page = (s->phy.reg7 & PHY_REG7_PAGE_SELECT) >> 5;
+        return s->phy_pages[page][reg & 7];
+    }
+}
+
+static void
+hcd_phy_write(OHCI1394State *s, uint8_t reg, uint8_t data) {
+    if (reg < 8) {
+        switch (reg) {
+        case 0: /* not allowed? */
+            break;
+        case 1:
+            s->phy.bytes[reg] = data & 0xBF;
+            if (data & 0x40) {
+                hcd_bus_reset(s);
+            }
+            break;
+        case 5:
+            s->phy.bytes[reg] = data & 0xBF;
+            if (data & 0x40) {
+                hcd_bus_reset(s);
+            }
+            break;
+        default:
+            s->phy.bytes[reg] = data;
+            break;
+        }
+    } else {
+        int page = (s->phy.reg7 & PHY_REG7_PAGE_SELECT) >> 5;
+        s->phy_pages[page][reg & 7] = data;
+    }
+}
+
+static uint64_t hcd_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+    uint64_t ret;
+    OHCI1394State *s = (OHCI1394State *)opaque;
+    switch (addr) {
+    case REG_OFFSET(int_event_masked): /* 0084 */
+        ret = s->mmio.int_event & s->mmio.int_mask;
+         break;
+    default:
+        ret = s->mmio.regs[addr >> 2];
+        break;
+    }
+    if (addr != 0x0080) {
+        /* what to do? */
+    }
+    return ret;
+}
+
+static void hcd_mmio_write(void *opaque, hwaddr addr, uint64_t data,
+                           unsigned size)
+{
+    OHCI1394State *s = (OHCI1394State *)opaque;
+    PhyControl phy_control;
+    uint8_t reg_addr;
+
+    if ((addr & 0xFFE0) != 0x0100) {
+        /* what to do? */
+    }
+    switch (addr) {
+    case REG_OFFSET(csr_control):     /* 014 */
+        switch (data & 0x00000003) {
+        case 0: /* BUS_MANAGER_ID */
+            /* TODO: set bus manager somewhere... */
+            s->mmio.csr_read_data = s->mmio.csr_compare_data;
+            s->mmio.csr_control = 0x80000000 | (data & 0x00000003);
+            break;
+        case 1: /* BANDWIDTH_AVAILABLE */
+            /* not actioned */
+            s->mmio.csr_control = 0x80000000 | (data & 0x00000003);
+            break;
+        case 2: /* CHANNELS_AVAILABLE_HI */
+            /* not actioned */
+            s->mmio.csr_control = 0x80000000 | (data & 0x00000003);
+            break;
+        case 3: /* CHANNELS_AVAILABLE_LO */
+            /* not actioned */
+            s->mmio.csr_control = 0x80000000 | (data & 0x00000003);
+            break;
+        }
+        break;
+    case REG_OFFSET(hc_control_set): /* 0050 */
+        data &= 0xE0CF0000;
+        s->mmio.hc_control |= data;
+        if (data & HCCONTROL_RESET_MASK) {
+            /* do a reset */
+            hcd_soft_reset(s);
+        }
+        if (data & HCCONTROL_LINK_ENABLE_MASK) {
+            if ((s->state == HCD_STATE_DISCONNECTED) ||
+                (s->state == HCD_STATE_ARBITRATION1)) {
+                uint32_t buf = 0xFFFFFFFF;
+                qemu_chr_fe_write(s->chr, (uint8_t *)&buf, 4);
+            }
+        }
+        break;
+    case REG_OFFSET(hc_control_clear): /* 0054 */
+        data &= 0xE0CE0000;
+        s->mmio.hc_control &= ~data;
+        break;
+    case REG_OFFSET(ir_multi_chan_mask_hi_set): /* 0070 */
+    case REG_OFFSET(ir_multi_chan_mask_lo_set): /* 0078 */
+        s->mmio.regs[(addr >> 2) & 0xFFFE] |= data;
+        s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] =
+                                          s->mmio.regs[((addr >> 2) & 0xFFFE)];
+        break;
+    case REG_OFFSET(ir_multi_chan_mask_hi_clear): /* 0074 */
+    case REG_OFFSET(ir_multi_chan_mask_lo_clear): /* 007c */
+        s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data;
+        s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] =
+                                          s->mmio.regs[((addr >> 2) & 0xFFFE)];
+        break;
+    case REG_OFFSET(int_event_set): /* 0080 */
+    case REG_OFFSET(int_mask_set): /* 0088 */
+    case REG_OFFSET(iso_xmit_int_mask_set):
+    case REG_OFFSET(iso_recv_int_mask_set):
+    case REG_OFFSET(link_control_set):
+        s->mmio.regs[(addr >> 2) & 0xFFFE] |= data;
+        break;
+    case REG_OFFSET(int_event_clear): /* 0084 */
+    case REG_OFFSET(int_mask_clear): /* 008c */
+    case REG_OFFSET(iso_xmit_int_mask_clear):
+    case REG_OFFSET(iso_recv_int_mask_clear):
+    case REG_OFFSET(link_control_clear):
+        s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data;
+        break;
+    case REG_OFFSET(node_id): /* 00E8 */
+        s->mmio.node_id =
+            (s->mmio.node_id & ~NODEID_BUS_NUMBER) | (data & 
NODEID_BUS_NUMBER);
+        break;
+    case REG_OFFSET(phy_control): /* 00ec */
+        *(uint32_t *)&phy_control = data;
+        reg_addr = phy_control.wr_flags & PHY_CONTROL_WR_FLAGS_REG_ADDR;
+        s->mmio.phy_control.wr_flags = reg_addr |
+            (s->mmio.phy_control.wr_flags & ~PHY_CONTROL_WR_FLAGS_REG_ADDR);
+        if (phy_control.wr_flags & PHY_CONTROL_WR_FLAGS_RD_REG) {
+            s->mmio.phy_control.rd_flags = reg_addr |
+                (s->mmio.phy_control.rd_flags & ~PHY_CONTROL_RD_FLAGS_RD_ADDR);
+            s->mmio.phy_control.rd_data = hcd_phy_read(s, reg_addr);
+            s->mmio.phy_control.rd_flags |= PHY_CONTROL_RD_FLAGS_RD_DONE;
+            s->mmio.int_event |= INT_PHY_REG_RCVD;
+        }
+        if (phy_control.wr_flags & PHY_CONTROL_WR_FLAGS_WR_REG) {
+            hcd_phy_write(s, reg_addr, phy_control.wr_data);
+            s->mmio.phy_control.wr_data = phy_control.wr_data;
+            s->mmio.phy_control.rd_flags &= ~PHY_CONTROL_RD_FLAGS_RD_DONE;
+        }
+        break;
+    case REG_OFFSET(asynchronous_request_filter_hi_set): /* 0x100 */
+    case REG_OFFSET(asynchronous_request_filter_lo_set): /* 0x108 */
+    case REG_OFFSET(physical_request_filter_hi_set): /* 0x110 */
+    case REG_OFFSET(physical_request_filter_lo_set): /* 0x118 */
+        s->mmio.regs[(addr >> 2) & 0xFFFE] |= data;
+        s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] =
+                                          s->mmio.regs[((addr >> 2) & 0xFFFE)];
+        break;
+
+    case REG_OFFSET(asynchronous_request_filter_hi_clear): /* 0x104 */
+    case REG_OFFSET(asynchronous_request_filter_lo_clear): /* 0x10c */
+    case REG_OFFSET(physical_request_filter_hi_clear): /* 0x114 */
+    case REG_OFFSET(physical_request_filter_lo_clear): /* 0x11c */
+        s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data;
+        s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] =
+                                          s->mmio.regs[((addr >> 2) & 0xFFFE)];
+        break;
+    case REG_OFFSET(async_request_transmit.context_control_set):
+    case REG_OFFSET(async_response_transmit.context_control_set):
+        data &= 0x00009000;
+        s->mmio.regs[(addr >> 2) & 0xFFFE] |= data;
+        s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] =
+                                          s->mmio.regs[((addr >> 2) & 0xFFFE)];
+        if (data & 0x00009000) {
+            HCDTimerState *t = NULL;
+            if (addr ==
+                REG_OFFSET(async_request_transmit.context_control_set)) {
+                t = &s->at_req_timer;
+            } else {
+                t = &s->at_rsp_timer;
+            }
+            if (data & 0x00008000) {
+                hcd_at_run(t);
+            }
+            timer_mod(t->timer, 0);
+        }
+        break;
+    case REG_OFFSET(async_request_receive.context_control_set):
+    case REG_OFFSET(async_response_receive.context_control_set):
+        data &= 0x00009000;
+        s->mmio.regs[(addr >> 2) & 0xFFFE] |= data;
+        s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] =
+                                          s->mmio.regs[((addr >> 2) & 0xFFFE)];
+        if (data & 0x00008000) {
+            hcd_async_rx_run(s, addr & 0xFFE0);
+        }
+        if (data & 0x00001000) {
+            hcd_async_rx_wake(s, addr & 0xFFE0);
+        }
+        break;
+    case REG_OFFSET(async_request_transmit.context_control_clear):
+    case REG_OFFSET(async_response_transmit.context_control_clear):
+        s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data;
+        s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] =
+                                          s->mmio.regs[((addr >> 2) & 0xFFFE)];
+        break;
+    case REG_OFFSET(async_request_receive.context_control_clear):
+    case REG_OFFSET(async_response_receive.context_control_clear):
+        data &= 0x00008000;
+        s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data;
+        s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] =
+                                          s->mmio.regs[((addr >> 2) & 0xFFFE)];
+        if (data & 0x00008000) {
+            hcd_async_rx_stop(s, addr & 0xFFE0);
+        }
+        break;
+    default:
+        s->mmio.regs[addr >> 2] = data;
+        break;
+    }
+    hcd_check_irq(s);
+}
+
+static const MemoryRegionOps hcd_mmio_ops = {
+    .read = hcd_mmio_read,
+    .write = hcd_mmio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int hcd_chr_can_receive(void *opaque)
+{
+    if (runstate_check(RUN_STATE_INMIGRATE)) {
+        /* this seems to race with the restore RUN_STATE_INMIGRATE */
+        return 0;
+    } else {
+        return 8192;
+    }
+}
+
+static void hcd_fill_buffer(OHCI1394State *s, const uint8_t **buf, int *len,
+                            int required)
+{
+    int to_copy;
+
+    if (s->bufpos >= required) {
+        return;
+    }
+    if (required - s->bufpos > *len) {
+        to_copy = *len;
+    } else {
+        to_copy = required - s->bufpos;
+    }
+
+    memcpy(s->buf + s->bufpos, *buf, to_copy);
+    *buf += to_copy;
+    *len -= to_copy;
+    s->bufpos += to_copy;
+}
+
+static bool hcd_chr_request_quadlet_write(OHCI1394State *s,
+                                          const uint8_t **buf, int *len)
+{
+    OHCIReqQuadletPacket *req_packet;
+    OHCIRspNoDataPacket rsp_packet;
+
+    hcd_fill_buffer(s, buf, len, sizeof(*req_packet));
+    if (s->bufpos < sizeof(*req_packet)) {
+        /* not enough data yet HCD_STATE_CONNECTED 00 */
+        return false;
+    }
+    if (s->bufpos > sizeof(*req_packet)) {
+        /* overflow HCD_STATE_CONNECTED 00 */
+        return false;
+    }
+    req_packet = (OHCIReqQuadletPacket *)s->buf;
+    dma_memory_write(&address_space_memory,
+                     req_packet->destination_offset_low,
+                     &req_packet->data, sizeof(uint32_t));
+    /* forward to ar or handle here?? */
+    rsp_packet.flags = 0x20; /* t_code */
+    rsp_packet.flags |= (req_packet->flags & OHCI_PACKET_FLAGS_RT);
+    rsp_packet.flags |= (req_packet->flags & OHCI_PACKET_FLAGS_T_LABEL);
+    rsp_packet.r_code = RESP_COMPLETE;
+    rsp_packet.destination_id = req_packet->destination_id ^ 1;
+    rsp_packet.source_id = req_packet->destination_id;
+    qemu_chr_fe_write(s->chr, (uint8_t *)&rsp_packet, sizeof(rsp_packet));
+    s->bufpos = 0;
+    return true;
+}
+
+static bool hcd_chr_request_block_write(OHCI1394State *s,
+                                        const uint8_t **buf, int *len)
+{
+    OHCIReqBlockPacket *req_packet;
+    OHCIRspNoDataPacket rsp_packet;
+
+    hcd_fill_buffer(s, buf, len, sizeof(*req_packet));
+    if (s->bufpos < sizeof(*req_packet)) {
+        /* not enough data yet HCD_STATE_CONNECTED 01 */
+        return false;
+    }
+    req_packet = (OHCIReqBlockPacket *)s->buf;
+    hcd_fill_buffer(s, buf, len,
+                    sizeof(*req_packet) + req_packet->data_length);
+    if (s->bufpos < (sizeof(*req_packet) +
+                      req_packet->data_length)) {
+        /* not enough data yet HCD_STATE_CONNECTED 01 */
+        return false;
+    }
+    if (s->bufpos > (sizeof(*req_packet) +
+                      req_packet->data_length)) {
+        /* overflow HCD_STATE_CONNECTED 01 */
+        return false;
+    }
+    dma_memory_write(&address_space_memory,
+                     req_packet->destination_offset_low,
+                     s->buf + sizeof(*req_packet),
+                     req_packet->data_length);
+    /* forward to ar or handle here?? */
+    rsp_packet.flags = 0x20; /* t_code */
+    rsp_packet.flags |= (req_packet->flags & OHCI_PACKET_FLAGS_RT);
+    rsp_packet.flags |= (req_packet->flags & OHCI_PACKET_FLAGS_T_LABEL);
+    rsp_packet.r_code = RESP_COMPLETE;
+    rsp_packet.destination_id = req_packet->destination_id ^ 1;
+    rsp_packet.source_id = req_packet->destination_id;
+    qemu_chr_fe_write(s->chr, (uint8_t *)&rsp_packet, sizeof(rsp_packet));
+    s->bufpos = 0;
+    return true;
+}
+
+static bool hcd_chr_response_quadlet_write(OHCI1394State *s,
+                                           const uint8_t **buf, int *len)
+{
+    OHCIRspNoDataPacket *rsp_packet;
+
+    hcd_fill_buffer(s, buf, len, sizeof(*rsp_packet));
+    if (s->bufpos < sizeof(*rsp_packet)) {
+        /* not enough data yet HCD_STATE_CONNECTED 02 */
+        return false;
+    }
+    if (s->bufpos > sizeof(*rsp_packet)) {
+        /* overflow HCD_STATE_CONNECTED 02 */
+        return false;
+    }
+    rsp_packet = (OHCIRspNoDataPacket *)s->buf;
+    hcd_async_rx_rsp_packet(s, (uint8_t *)rsp_packet,
+                            sizeof(*rsp_packet), ACK_COMPLETE);
+    s->bufpos = 0;
+    return true;
+}
+
+static bool hcd_chr_request_quadlet_read(OHCI1394State *s,
+                                         const uint8_t **buf, int *len)
+{
+    OHCIReqNoDataPacket *req_nodata_packet;
+    OHCIRspQuadletPacket rsp_quadlet_packet;
+
+    hcd_fill_buffer(s, buf, len, 12);
+    if (s->bufpos < 12) {
+        /* not enough data yet HCD_STATE_CONNECTED 04 */
+        return false;
+    }
+    if (s->bufpos > 12) {
+        /* overflow HCD_STATE_CONNECTED 04 */
+        return false;
+    }
+    req_nodata_packet = (OHCIReqNoDataPacket *)s->buf;
+    /* forward to ar or handle here?? */
+    rsp_quadlet_packet.flags = 0x60; /* t_code */
+    rsp_quadlet_packet.flags |=
+        (req_nodata_packet->flags & OHCI_PACKET_FLAGS_RT);
+    rsp_quadlet_packet.flags |=
+        (req_nodata_packet->flags & OHCI_PACKET_FLAGS_T_LABEL);
+    rsp_quadlet_packet.destination_id = req_nodata_packet->destination_id ^ 1;
+    rsp_quadlet_packet.source_id = req_nodata_packet->destination_id;
+    if (req_nodata_packet->destination_offset_high == 0xFFFF) {
+        if (0xF0000400 ==
+            (req_nodata_packet->destination_offset_low & 0xFFFFFC00)) {
+            uint32_t tmp_addr = s->mmio.config_rom_map +
+               (req_nodata_packet->destination_offset_low & 0x3ff);
+
+            dma_memory_read(&address_space_memory,
+                            tmp_addr,
+                            &rsp_quadlet_packet.data,
+                            sizeof(uint32_t));
+            rsp_quadlet_packet.r_code = RESP_COMPLETE;
+        } else {
+            /* Unknown address */
+            rsp_quadlet_packet.r_code = RESP_ADDRESS_ERROR;
+        }
+    } else if ((req_nodata_packet->destination_offset_high) == 0x0000) {
+        if (dma_memory_read(&address_space_memory,
+                            req_nodata_packet->destination_offset_low,
+                            &rsp_quadlet_packet.data, sizeof(uint32_t))) {
+            rsp_quadlet_packet.r_code = RESP_ADDRESS_ERROR;
+        } else {
+            rsp_quadlet_packet.r_code = RESP_COMPLETE;
+        }
+    } else {
+        /* Unknown address */
+        rsp_quadlet_packet.r_code = RESP_ADDRESS_ERROR;
+    }
+
+    qemu_chr_fe_write(s->chr, (uint8_t *)&rsp_quadlet_packet,
+                      sizeof(rsp_quadlet_packet));
+    s->bufpos = 0;
+    return true;
+}
+
+static bool hcd_chr_request_block_read(OHCI1394State *s,
+                                       const uint8_t **buf, int *len)
+{
+    OHCIReqBlockPacket *req_packet;
+    OHCIRspBlockPacket rsp_packet;
+    void *bounce_buffer;
+
+    hcd_fill_buffer(s, buf, len, sizeof(*req_packet));
+    if (s->bufpos < sizeof(*req_packet)) {
+        /* not enough data yet HCD_STATE_CONNECTED 05 */
+        return false;
+    }
+    if (s->bufpos > sizeof(*req_packet)) {
+        /* overflow HCD_STATE_CONNECTED 05 */
+        return false;
+    }
+    req_packet = (OHCIReqBlockPacket *)s->buf;
+    /* forward to ar or handle here?? */
+    rsp_packet.flags = 0x70; /* t_code */
+    rsp_packet.flags |= (req_packet->flags & OHCI_PACKET_FLAGS_RT);
+    rsp_packet.flags |= (req_packet->flags & OHCI_PACKET_FLAGS_T_LABEL);
+    rsp_packet.destination_id = req_packet->destination_id ^ 1;
+    rsp_packet.source_id = req_packet->destination_id;
+    rsp_packet.data_length = req_packet->data_length;
+
+    bounce_buffer = g_malloc(rsp_packet.data_length);
+    if (req_packet->destination_offset_high == 0xFFFF) {
+        if ((req_packet->destination_offset_low & 0xFFFFFC00) == 0xF0000400) {
+            uint32_t tmp_addr = s->mmio.config_rom_map +
+                      (req_packet->destination_offset_low & 0x3ff);
+            dma_memory_read(&address_space_memory,
+                            tmp_addr,
+                            bounce_buffer,
+                            rsp_packet.data_length);
+            rsp_packet.r_code = RESP_COMPLETE;
+        } else {
+            /* Unknown address */
+            rsp_packet.r_code = RESP_ADDRESS_ERROR;
+        }
+    } else if ((req_packet->destination_offset_high) == 0x0000) {
+        if (dma_memory_read(&address_space_memory,
+                            req_packet->destination_offset_low,
+                            bounce_buffer,
+                            rsp_packet.data_length)) {
+            /* address error */
+            rsp_packet.r_code = RESP_ADDRESS_ERROR;
+        } else {
+            rsp_packet.r_code = RESP_COMPLETE;
+        }
+    } else {
+        /* Unknown address */
+        rsp_packet.r_code = RESP_ADDRESS_ERROR;
+    }
+    qemu_chr_fe_write(s->chr, (uint8_t *)&rsp_packet, sizeof(rsp_packet));
+    if (rsp_packet.r_code == RESP_COMPLETE) {
+        qemu_chr_fe_write(s->chr, bounce_buffer, rsp_packet.data_length);
+    }
+    g_free(bounce_buffer);
+    s->bufpos = 0;
+    return true;
+}
+
+static bool hcd_chr_response_quadlet_read(OHCI1394State *s,
+                                          const uint8_t **buf, int *len)
+{
+    OHCIRspQuadletPacket *rsp_packet;
+
+    hcd_fill_buffer(s, buf, len, sizeof(*rsp_packet));
+    if (s->bufpos < sizeof(*rsp_packet)) {
+        /* not enough data yet HCD_STATE_CONNECTED 4 */
+        return false;
+    }
+    if (s->bufpos > sizeof(*rsp_packet)) {
+        /* overflow HCD_STATE_CONNECTED 4 */
+        return false;
+    }
+    rsp_packet = (OHCIRspQuadletPacket *)s->buf;
+
+    hcd_async_rx_rsp_packet(s, (uint8_t *)rsp_packet,
+                            sizeof(*rsp_packet), ACK_COMPLETE);
+    s->bufpos = 0;
+    return true;
+}
+
+static bool hcd_chr_response_block_read(OHCI1394State *s,
+                                        const uint8_t **buf, int *len)
+{
+    OHCIRspBlockPacket *rsp_packet;
+
+    hcd_fill_buffer(s, buf, len, sizeof(*rsp_packet));
+    if (s->bufpos < sizeof(*rsp_packet)) {
+        /* not enough data yet HCD_STATE_CONNECTED 07 */
+        return false;
+    }
+    rsp_packet = (OHCIRspBlockPacket *)s->buf;
+    hcd_fill_buffer(s, buf, len,
+                    sizeof(*rsp_packet) + rsp_packet->data_length);
+    if (s->bufpos < (sizeof(*rsp_packet) + rsp_packet->data_length)) {
+        /* not enough data yet HCD_STATE_CONNECTED 07 */
+        return false;
+    }
+    if (s->bufpos > (sizeof(*rsp_packet) + rsp_packet->data_length)) {
+        /* overflow HCD_STATE_CONNECTED 07 */
+        return false;
+    }
+    hcd_async_rx_rsp_packet(s, (uint8_t *)rsp_packet,
+                            sizeof(*rsp_packet) + rsp_packet->data_length,
+                            ACK_COMPLETE);
+    s->bufpos = 0;
+    return true;
+}
+
+static void hcd_chr_receive(void *opaque, const uint8_t *buf, int len)
+{
+    OHCI1394State *s = (OHCI1394State *)opaque;
+    uint16_t received_bid;
+    struct timeval tv;
+    OHCIPacketHeader *packet_header;
+
+    while (len) {
+        switch (s->state) {
+        case HCD_STATE_UNPLUGGED:
+            /* restore races with chr event, just fake it here */
+            hcd_chr_event(s, CHR_EVENT_OPENED);
+            break;
+        case HCD_STATE_MAGIC: /* waiting for magic */
+            hcd_fill_buffer(s, &buf, &len, 4);
+            if (s->bufpos < 4) {
+                /* not enough data yet HCD_MAGIC */
+                break;
+            }
+            if (s->bufpos > 4) {
+                /* overflow HCD_MAGIC */
+                break;
+            }
+            if (memcmp(s->buf, "1394", 4) != 0) {
+                /* TODO: what do we do here? drop the connection I suppose */
+                break;
+            } else {
+                s->state = HCD_STATE_DISCONNECTED;
+            }
+            s->bufpos = 0;
+            break;
+        case HCD_STATE_DISCONNECTED:
+            hcd_fill_buffer(s, &buf, &len, 4);
+            if (s->bufpos < 4) {
+                /* not enough data yet HCD_STATE_DISCONNECTED */
+                return;
+            }
+            s->bufpos = 0;
+            if (*(uint32_t *)s->buf != 0xFFFFFFFF) {
+                /* unknown data */
+                break;
+            }
+            s->other_link = 1;
+            /* link change - connected */
+            s->state = HCD_STATE_ARBITRATION1;
+            if (!(s->mmio.hc_control & HCCONTROL_LINK_ENABLE_MASK)) {
+                /* we will progress when our link comes up and the other end
+                sends a bid */
+                break;
+            }
+            /* fall through as we won't go around again because len == 0 */
+        case HCD_STATE_ARBITRATION1:
+            gettimeofday(&tv, NULL);
+            s->bid = 0;
+            s->bid ^= (tv.tv_sec >> 0) & 0xFFFF;
+            s->bid ^= (tv.tv_sec >> 16) & 0xFFFF;
+            s->bid ^= (tv.tv_sec >> 32) & 0xFFFF;
+            s->bid ^= (tv.tv_sec >> 48) & 0xFFFF;
+            s->bid ^= (tv.tv_usec >> 0) & 0xFFFF;
+            s->bid ^= (tv.tv_usec >> 16) & 0xFFFF;
+            s->bid ^= (tv.tv_usec >> 32) & 0xFFFF;
+            s->bid ^= (tv.tv_usec >> 48) & 0xFFFF;
+            s->bid &= 0x7FFF;
+            /* TODO: set high bit based on preference to become root */
+            qemu_chr_fe_write(s->chr, (uint8_t *)&s->bid, 2);
+            s->state = HCD_STATE_ARBITRATION2;
+            break;
+        case HCD_STATE_ARBITRATION2:
+            hcd_fill_buffer(s, &buf, &len, 2);
+            if (s->bufpos < 2) {
+                /* not enough data yet HCD_STATE_ARBITRATION2 */
+                break;
+            }
+            received_bid = *(uint16_t *)s->buf;
+            s->bufpos = 0;
+            if (received_bid == s->bid) {
+                s->state = HCD_STATE_ARBITRATION1;
+                break;
+            } else if (received_bid < s->bid) {
+                s->root = 1;
+                s->state = HCD_STATE_CONNECTED;
+            } else {
+                s->root = 0;
+                s->state = HCD_STATE_CONNECTED;
+            }
+            hcd_bus_reset(s);
+            break;
+        case HCD_STATE_CONNECTED:
+            if (!(s->mmio.hc_control & HCCONTROL_LINK_ENABLE_MASK)) {
+                return;
+            }
+            hcd_fill_buffer(s, &buf, &len, 4);
+            if (s->bufpos < 4) {
+                /* not enough data yet HCD_STATE_CONNECTED */
+                return;
+            }
+            if (*(uint32_t *)s->buf == 0xFFFFFFFE) {
+                /* Reset because link change */
+                s->bufpos = 0;
+                s->state = HCD_STATE_DISCONNECTED;
+                hcd_bus_reset(s);
+                break;
+            }
+            packet_header = (OHCIPacketHeader *)s->buf;
+
+            switch (packet_header->flags & OHCI_PACKET_FLAGS_T_CODE) {
+            case 0x00: /* request - quadlet write */
+                if (!hcd_chr_request_quadlet_write(s, &buf, &len)) {
+                    return;
+                }
+                break;
+            case 0x10: /* request - block write */
+                if (!hcd_chr_request_block_write(s, &buf, &len)) {
+                    return;
+                }
+                break;
+            case 0x20: /* response - quadlet write */
+                if (!hcd_chr_response_quadlet_write(s, &buf, &len)) {
+                    return;
+                }
+                break;
+            case 0x40: /* request - quadlet read */
+                if (!hcd_chr_request_quadlet_read(s, &buf, &len)) {
+                    return;
+                }
+                break;
+            case 0x50: /* request - block read */
+                if (!hcd_chr_request_block_read(s, &buf, &len)) {
+                    return;
+                }
+                break;
+            case 0x60: /* response - quadlet read */
+                if (!hcd_chr_response_quadlet_read(s, &buf, &len)) {
+                    return;
+                }
+                break;
+            case 0x70: /* response - block read */
+                if (!hcd_chr_response_block_read(s, &buf, &len)) {
+                    return;
+                }
+                break;
+            default:
+                /* unknown t_code */
+                break;
+            }
+            return;
+        }
+    }
+}
+
+static void hcd_chr_event(void *opaque, int event)
+{
+    OHCI1394State *s = (OHCI1394State *)opaque;
+
+    if (runstate_check(RUN_STATE_INMIGRATE)) {
+        return;
+    }
+
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        s->state = HCD_STATE_MAGIC;
+        qemu_chr_fe_write(s->chr, (uint8_t *)"1394", 4);
+        if (s->mmio.hc_control & HCCONTROL_LINK_ENABLE_MASK) {
+            uint32_t buf = 0xFFFFFFFF;
+            qemu_chr_fe_write(s->chr, (uint8_t *)&buf, 4);
+            if (s->other_link) {
+                hcd_bus_reset(s);
+            }
+        }
+        break;
+    case CHR_EVENT_CLOSED:
+        s->state = HCD_STATE_UNPLUGGED;
+        s->phy_pages[0][0] = 0x08; /* 0xFE ? */
+        /* TODO: interrupt? */
+        s->phy.reg5 |= PHY_REG5_PEI;
+        s->mmio.int_event |= (1 << 19);
+        hcd_bus_reset(s);
+        break;
+    }
+}
+
+#define TYPE_PCI_1394 "ohci-1394"
+#define PCI_1394(obj) OBJECT_CHECK(OHCI1394State, (obj), TYPE_PCI_1394)
+
+static int
+hcd_pci_init(PCIDevice *pci_dev)
+{
+    OHCI1394State *s = PCI_1394(pci_dev);
+    uint8_t *pci_conf = pci_dev->config;
+
+    pci_set_byte(pci_conf + PCI_CLASS_PROG, 0x10); /* OHCI */
+    pci_set_word(pci_conf + PCI_STATUS,
+                 PCI_STATUS_DEVSEL_MEDIUM | PCI_STATUS_FAST_BACK);
+    pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1);
+    pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08);
+
+    memory_region_init_io(&s->mmio_bar, OBJECT(s), &hcd_mmio_ops, s,
+                          "ohci-1394-mmio", OHCI_1394_MMIO_SIZE);
+    pci_register_bar(&s->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
+                     &s->mmio_bar);
+    s->irq = pci_allocate_irq(&s->pci_dev);
+    s->at_req_timer.s = s;
+    s->at_req_timer.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hcd_at_timer,
+                                         &s->at_req_timer);
+    s->at_req_timer.num = 0;
+    s->at_rsp_timer.s = s;
+    s->at_rsp_timer.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hcd_at_timer,
+                                         &s->at_rsp_timer);
+    s->at_rsp_timer.num = 1;
+    qemu_chr_add_handlers(s->chr, hcd_chr_can_receive, hcd_chr_receive,
+                          hcd_chr_event, s);
+    hcd_hard_reset(s);
+    return 0;
+}
+
+static void
+hcd_pci_exit(PCIDevice *pci_dev)
+{
+    OHCI1394State *s = PCI_1394(pci_dev);
+
+    qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
+
+    timer_del(s->at_rsp_timer.timer);
+    timer_free(s->at_rsp_timer.timer);
+
+    timer_del(s->at_req_timer.timer);
+    timer_free(s->at_req_timer.timer);
+
+    qemu_free_irq(s->irq);
+}
+
+static Property hcd_properties[] = {
+    DEFINE_PROP_CHR("chardev", OHCI1394State, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_pci_hcd = {
+    .name = TYPE_PCI_1394,
+    .version_id = 3,
+    .minimum_version_id = 3,
+    .minimum_version_id_old = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_PCI_DEVICE(pci_dev, OHCI1394State),
+        VMSTATE_UINT32_ARRAY(mmio.regs, OHCI1394State,
+                             OHCI_1394_MMIO_SIZE >> 2),
+        VMSTATE_UINT8_ARRAY(phy.bytes, OHCI1394State, 16),
+        VMSTATE_UINT8_2DARRAY(phy_pages, OHCI1394State, 8, 8),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void
+hcd_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = hcd_pci_init;
+    k->exit = hcd_pci_exit;
+    k->vendor_id = PCI_VENDOR_ID_INTEL;
+    k->device_id = PCI_DEVICE_ID_INTEL_1394_OHCI;
+    k->class_id = PCI_CLASS_SERIAL_1394;
+    dc->desc = "IEEE1394 OpenHCI Host Controller";
+    dc->props = hcd_properties;
+    dc->vmsd = &vmstate_pci_hcd;
+}
+
+static const TypeInfo hcd_info = {
+    .name          = TYPE_PCI_1394,
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(OHCI1394State),
+    .class_init    = hcd_class_init,
+};
+
+static void ohci_1394_register_types(void)
+{
+    type_register_static(&hcd_info);
+}
+
+type_init(ohci_1394_register_types)
diff --git a/hw/1394/hcd-ohci.h b/hw/1394/hcd-ohci.h
new file mode 100644
index 0000000..91222d3
--- /dev/null
+++ b/hw/1394/hcd-ohci.h
@@ -0,0 +1,131 @@
+#ifndef HCD_OHCI_H
+#define HCD_OHCI_H
+
+/*
+ * QEMU IEEE 1394 API
+ *
+ * Copyright (c) 2016 Guardicore
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+
+#define EVT_NO_STATUS  0x00
+#define EVT_UNDERRUN   0x04
+#define EVT_OVERRUN    0x05
+#define EVT_DATA_READ  0x07
+#define EVT_DATA_WRITE 0x08
+#define EVT_BUS_RESET  0x09
+#define EVT_TCODE_ERR  0x0B
+#define EVT_UNKNOWN    0x0E
+#define EVT_FLUSHED    0x0F
+#define ACK_COMPLETE   0x11
+#define ACK_PENDING    0x12
+
+#define RESP_COMPLETE       0x00
+#define RESP_CONFLICT_ERROR 0x40
+#define RESP_DATA_ERROR     0x50
+#define RESP_TYPE_ERROR     0x60
+#define RESP_ADDRESS_ERROR  0x70
+
+/* Layout of the flags field in packet structures below */
+#define OHCI_PACKET_FLAGS_T_CODE     0x000000F0
+#define OHCI_PACKET_FLAGS_RT         0x00000300
+#define OHCI_PACKET_FLAGS_T_LABEL    0x0000FC00
+#define OHCI_PACKET_FLAGS_SPD        0x00070000
+#define OHCI_PACKET_FLAGS_SRC_BUS_ID 0x00800000
+
+typedef union {
+    uint32_t qdata[3];
+    uint32_t flags;
+} OHCIPacketHeader;
+
+typedef union {
+    uint32_t qdata[3];
+    struct {
+        uint32_t flags;
+        uint16_t destination_offset_high;
+        uint16_t destination_id;
+        uint32_t destination_offset_low;
+    };
+} OHCIReqNoDataPacket;
+
+typedef union {
+    uint32_t qdata[3];
+    struct {
+        uint32_t flags;
+        uint16_t destination_offset_high;
+        uint16_t destination_id;
+        uint32_t destination_offset_low;
+        uint32_t data;
+    };
+} OHCIReqQuadletPacket;
+
+typedef union {
+    uint32_t qdata[4];
+    struct {
+        uint32_t flags;
+        uint16_t destination_offset_high;
+        uint16_t destination_id;
+        uint32_t destination_offset_low;
+        uint16_t padding;
+        uint16_t data_length;
+    };
+} OHCIReqBlockPacket;
+
+typedef union {
+    uint32_t qdata[3];
+    struct {
+        uint16_t flags;
+        uint16_t destination_id;
+        uint8_t padding;
+        uint8_t r_code;
+        uint16_t source_id;
+    };
+} OHCIRspNoDataPacket;
+
+typedef union {
+    uint32_t qdata[4];
+    struct {
+        uint16_t flags;
+        uint16_t destination_id;
+        uint8_t padding1;
+        uint8_t r_code;
+        uint16_t source_id;
+        uint32_t padding2;
+        uint32_t data;
+    };
+} OHCIRspQuadletPacket;
+
+typedef union {
+    uint32_t qdata[4];
+    struct {
+        uint16_t flags;
+        uint16_t destination_id;
+        uint8_t padding1;
+        uint8_t r_code;
+        uint16_t source_id;
+        uint32_t padding2;
+        uint16_t padding3;
+        uint16_t data_length;
+    };
+} OHCIRspBlockPacket;
+
+#endif
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 4a07ed4..db012bf 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -27,6 +27,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ssi/
 devices-dirs-$(CONFIG_SOFTMMU) += timer/
 devices-dirs-$(CONFIG_TPM) += tpm/
 devices-dirs-$(CONFIG_SOFTMMU) += usb/
+devices-dirs-$(CONFIG_SOFTMMU) += 1394/
 devices-dirs-$(CONFIG_SOFTMMU) += vfio/
 devices-dirs-$(CONFIG_VIRTIO) += virtio/
 devices-dirs-$(CONFIG_SOFTMMU) += watchdog/
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index db85afa..0ff5335 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -35,6 +35,7 @@
 #define PCI_CLASS_SYSTEM_SDHCI           0x0805
 #define PCI_CLASS_SYSTEM_OTHER           0x0880
 
+#define PCI_CLASS_SERIAL_1394            0x0c00
 #define PCI_CLASS_SERIAL_USB             0x0c03
 #define PCI_CLASS_SERIAL_SMBUS           0x0c05
 
@@ -157,6 +158,8 @@
 
 #define PCI_DEVICE_ID_INTEL_Q35_MCH      0x29c0
 
+#define PCI_DEVICE_ID_INTEL_1394_OHCI    0x7605
+
 #define PCI_VENDOR_ID_XEN                0x5853
 #define PCI_DEVICE_ID_XEN_PLATFORM       0x0001
 
-- 
2.5.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]