diff mbox

add 1394 OHCI device

Message ID 1456939927-6071-1-git-send-email-lprosek@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ladi Prosek March 2, 2016, 5:32 p.m. UTC
This patch adds 1394 (FireWire) virtual device support to QEMU.

Signed-off-by: Ladi Prosek <lprosek@redhat.com>
---
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

Comments

Ladi Prosek March 17, 2016, 8:11 a.m. UTC | #1
Just a friendly ping after two weeks of silence.

Thanks,
Ladi
Sameeh Jubran April 4, 2016, 12:48 p.m. UTC | #2
Hello All,

I've tested windows live debugging using the Firewire device and it 
seems to be good.

Test case scenario:
      Kernel-Mode Debugging over a 1394 as detailed in: 
https://msdn.microsoft.com/en-us/library/windows/hardware/ff556866(v=vs.85).aspx.

Setup:
     Two separate VMs, both running Windows 2012 X64. One as Host VM ( 
which runs the debugger ) and the other as Target VM ( which is being 
debugged ).

Qemu Command line:

     On the Host VM:
         -chardev 
socket,id=serial10,path=/tmp/debug-serial2,server,nowait \
         -device ohci-1394,chardev=serial10,idserial10

     On the Target VM:
         -chardev socket,id=serial10,path=/tmp/debug-serial2 \
         -device ohci-1394,chardev=serial10,idserial10

Results:
I've successfully connected to the Target VM using WindbgX64 and ran 
basic debugging functions such as "Break" and "stack trace".
Moreover I've successfully viewed live trace output from virtio-net 
driver while running Iperf test and ping.

Tested-by: Sameeh Jubran <sameeh@daynix.com>

------ Original Message ------
From: "Ladi Prosek" <lprosek@redhat.com>
To: qemu-devel@nongnu.org
Cc: peter.maydell@linaro.org; "Ladi Prosek" <lprosek@redhat.com>; 
itamar@guardicore.com; afaerber@suse.de; itamar.tal4@gmail.com
Sent: 3/17/2016 10:11:33 AM
Subject: Re: [Qemu-devel] [PATCH] add 1394 OHCI device

>Just a friendly ping after two weeks of silence.
>
>Thanks,
>Ladi
>
>
Itamar Tal April 4, 2016, 2:30 p.m. UTC | #3
Hi all,

That's the exactly use case we've intended for when we've wrote this patch
and have tested it as well. On my tests the patch worked for both 32 and 64
bit machines for Windows Server 2003, 2008 and 2012 and it seemed to be
working just fine. I've also benchmarked it against the good old char
device and it was superior on a scale of magnitude.
Feel free to ask me if you encounter any issues and I'll gladly respond.

Itamar,
Guardicore


On Mon, Apr 4, 2016 at 3:48 PM, Sameeh Jubran <sameeh@daynix.com> wrote:

> Hello All,
>
> I've tested windows live debugging using the Firewire device and it seems
> to be good.
>
> Test case scenario:
>      Kernel-Mode Debugging over a 1394 as detailed in:
> https://msdn.microsoft.com/en-us/library/windows/hardware/ff556866(v=vs.85).aspx
> .
>
> Setup:
>     Two separate VMs, both running Windows 2012 X64. One as Host VM (
> which runs the debugger ) and the other as Target VM ( which is being
> debugged ).
>
> Qemu Command line:
>
>     On the Host VM:
>         -chardev socket,id=serial10,path=/tmp/debug-serial2,server,nowait \
>         -device ohci-1394,chardev=serial10,idserial10
>
>     On the Target VM:
>         -chardev socket,id=serial10,path=/tmp/debug-serial2 \
>         -device ohci-1394,chardev=serial10,idserial10
>
> Results:
> I've successfully connected to the Target VM using WindbgX64 and ran basic
> debugging functions such as "Break" and "stack trace".
> Moreover I've successfully viewed live trace output from virtio-net driver
> while running Iperf test and ping.
>
> Tested-by: Sameeh Jubran <sameeh@daynix.com>
>
>
> ------ Original Message ------
> From: "Ladi Prosek" <lprosek@redhat.com>
> To: qemu-devel@nongnu.org
> Cc: peter.maydell@linaro.org; "Ladi Prosek" <lprosek@redhat.com>;
> itamar@guardicore.com; afaerber@suse.de; itamar.tal4@gmail.com
> Sent: 3/17/2016 10:11:33 AM
> Subject: Re: [Qemu-devel] [PATCH] add 1394 OHCI device
>
> Just a friendly ping after two weeks of silence.
>>
>> Thanks,
>> Ladi
>>
>>
>>
>
Ladi Prosek April 5, 2016, 7:01 a.m. UTC | #4
Thank you Sameeh & Itamar!

Peter, it looks like there are people who care about this device and
are willing to maintain it. Would it be enough to merge the patch?

CC Alexander who may be interested as well.

Thanks,
Ladi

On Mon, Apr 4, 2016 at 4:30 PM, Itamar Tal <itamar@guardicore.com> wrote:
> Hi all,
>
> That's the exactly use case we've intended for when we've wrote this patch
> and have tested it as well. On my tests the patch worked for both 32 and 64
> bit machines for Windows Server 2003, 2008 and 2012 and it seemed to be
> working just fine. I've also benchmarked it against the good old char device
> and it was superior on a scale of magnitude.
> Feel free to ask me if you encounter any issues and I'll gladly respond.
>
> Itamar,
> Guardicore
>
>
> On Mon, Apr 4, 2016 at 3:48 PM, Sameeh Jubran <sameeh@daynix.com> wrote:
>>
>> Hello All,
>>
>> I've tested windows live debugging using the Firewire device and it seems
>> to be good.
>>
>> Test case scenario:
>>      Kernel-Mode Debugging over a 1394 as detailed in:
>> https://msdn.microsoft.com/en-us/library/windows/hardware/ff556866(v=vs.85).aspx.
>>
>> Setup:
>>     Two separate VMs, both running Windows 2012 X64. One as Host VM (
>> which runs the debugger ) and the other as Target VM ( which is being
>> debugged ).
>>
>> Qemu Command line:
>>
>>     On the Host VM:
>>         -chardev socket,id=serial10,path=/tmp/debug-serial2,server,nowait
>> \
>>         -device ohci-1394,chardev=serial10,idserial10
>>
>>     On the Target VM:
>>         -chardev socket,id=serial10,path=/tmp/debug-serial2 \
>>         -device ohci-1394,chardev=serial10,idserial10
>>
>> Results:
>> I've successfully connected to the Target VM using WindbgX64 and ran basic
>> debugging functions such as "Break" and "stack trace".
>> Moreover I've successfully viewed live trace output from virtio-net driver
>> while running Iperf test and ping.
>>
>> Tested-by: Sameeh Jubran <sameeh@daynix.com>
>>
>>
>> ------ Original Message ------
>> From: "Ladi Prosek" <lprosek@redhat.com>
>> To: qemu-devel@nongnu.org
>> Cc: peter.maydell@linaro.org; "Ladi Prosek" <lprosek@redhat.com>;
>> itamar@guardicore.com; afaerber@suse.de; itamar.tal4@gmail.com
>> Sent: 3/17/2016 10:11:33 AM
>> Subject: Re: [Qemu-devel] [PATCH] add 1394 OHCI device
>>
>>> Just a friendly ping after two weeks of silence.
>>>
>>> Thanks,
>>> Ladi
>>>
>>>
>>
>
Peter Maydell April 5, 2016, 8:01 a.m. UTC | #5
On 5 April 2016 at 08:01, Ladi Prosek <lprosek@redhat.com> wrote:
> Thank you Sameeh & Itamar!
>
> Peter, it looks like there are people who care about this device and
> are willing to maintain it. Would it be enough to merge the patch?

I have no objections to the patch particularly, but I would
prefer it if you can find a submaintainer willing to deal
with the device and include any related patches in their
pull requests to me.

thanks
-- PMM
diff mbox

Patch

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   <itamar@guardicore.com>
+ * 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