From patchwork Wed Mar 2 17:32:07 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ladi Prosek X-Patchwork-Id: 8484201 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 28328C0553 for ; Wed, 2 Mar 2016 17:33:06 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 835B92037C for ; Wed, 2 Mar 2016 17:32:59 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E08D120351 for ; Wed, 2 Mar 2016 17:32:55 +0000 (UTC) Received: from localhost ([::1]:57973 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1abAdr-0007Kt-9c for patchwork-qemu-devel@patchwork.kernel.org; Wed, 02 Mar 2016 12:32:55 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42975) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1abAdX-00079B-FR for qemu-devel@nongnu.org; Wed, 02 Mar 2016 12:32:41 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1abAdR-00035m-Nx for qemu-devel@nongnu.org; Wed, 02 Mar 2016 12:32:35 -0500 Received: from mx1.redhat.com ([209.132.183.28]:48581) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1abAdR-00035f-Ap for qemu-devel@nongnu.org; Wed, 02 Mar 2016 12:32:29 -0500 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (Postfix) with ESMTPS id 6BF967F081; Wed, 2 Mar 2016 17:32:28 +0000 (UTC) Received: from dhcp-1-107.brq.redhat.com (dhcp-1-127.brq.redhat.com [10.34.1.127]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u22HWPIu018428; Wed, 2 Mar 2016 12:32:25 -0500 From: Ladi Prosek To: qemu-devel@nongnu.org Date: Wed, 2 Mar 2016 18:32:07 +0100 Message-Id: <1456939927-6071-1-git-send-email-lprosek@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.26 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: peter.maydell@linaro.org, Ladi Prosek , itamar@guardicore.com, afaerber@suse.de, itamar.tal4@gmail.com Subject: [Qemu-devel] [PATCH] add 1394 OHCI device X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds 1394 (FireWire) virtual device support to QEMU. Signed-off-by: Ladi Prosek Tested-by: Sameeh Jubran --- 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 + * 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