6ba530e461
Asynchronous transfers are not yet working properly (tested with USB disk device in high speed mode). That's why the support for "speed:high" is still disabled. Since we have no test case for the isosynchronous mode, I have not yet ported the main code for it.
427 lines
13 KiB
C++
427 lines
13 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Experimental USB EHCI adapter (partly ported from Qemu)
|
|
//
|
|
// Copyright (C) 2015 The Bochs Project
|
|
//
|
|
// Copyright(c) 2008 Emutex Ltd. (address@hidden)
|
|
// Copyright(c) 2011-2012 Red Hat, Inc.
|
|
//
|
|
// Red Hat Authors:
|
|
// Gerd Hoffmann <kraxel@redhat.com>
|
|
// Hans de Goede <hdegoede@redhat.com>
|
|
//
|
|
// EHCI project was started by Mark Burkley, with contributions by
|
|
// Niels de Vos. David S. Ahern continued working on it. Kevin Wolf,
|
|
// Jan Kiszka and Vincent Palatin contributed bugfixes.
|
|
//
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef BX_IODEV_USB_EHCI_H
|
|
#define BX_IODEV_USB_EHCI_H
|
|
|
|
#if BX_USE_USB_EHCI_SMF
|
|
# define BX_EHCI_THIS theUSB_EHCI->
|
|
# define BX_EHCI_THIS_PTR theUSB_EHCI
|
|
#else
|
|
# define BX_EHCI_THIS this->
|
|
# define BX_EHCI_THIS_PTR this
|
|
#endif
|
|
|
|
#define USB_EHCI_PORTS 6
|
|
|
|
/* EHCI spec version 1.0 Section 3.3
|
|
*/
|
|
typedef struct EHCIitd {
|
|
Bit32u next;
|
|
|
|
Bit32u transact[8];
|
|
#define ITD_XACT_ACTIVE (1 << 31)
|
|
#define ITD_XACT_DBERROR (1 << 30)
|
|
#define ITD_XACT_BABBLE (1 << 29)
|
|
#define ITD_XACT_XACTERR (1 << 28)
|
|
#define ITD_XACT_LENGTH_MASK 0x0fff0000
|
|
#define ITD_XACT_LENGTH_SH 16
|
|
#define ITD_XACT_IOC (1 << 15)
|
|
#define ITD_XACT_PGSEL_MASK 0x00007000
|
|
#define ITD_XACT_PGSEL_SH 12
|
|
#define ITD_XACT_OFFSET_MASK 0x00000fff
|
|
|
|
Bit32u bufptr[7];
|
|
#define ITD_BUFPTR_MASK 0xfffff000
|
|
#define ITD_BUFPTR_SH 12
|
|
#define ITD_BUFPTR_EP_MASK 0x00000f00
|
|
#define ITD_BUFPTR_EP_SH 8
|
|
#define ITD_BUFPTR_DEVADDR_MASK 0x0000007f
|
|
#define ITD_BUFPTR_DEVADDR_SH 0
|
|
#define ITD_BUFPTR_DIRECTION (1 << 11)
|
|
#define ITD_BUFPTR_MAXPKT_MASK 0x000007ff
|
|
#define ITD_BUFPTR_MAXPKT_SH 0
|
|
#define ITD_BUFPTR_MULT_MASK 0x00000003
|
|
#define ITD_BUFPTR_MULT_SH 0
|
|
} EHCIitd;
|
|
|
|
/* EHCI spec version 1.0 Section 3.4
|
|
*/
|
|
typedef struct EHCIsitd {
|
|
Bit32u next; // Standard next link pointer
|
|
Bit32u epchar;
|
|
#define SITD_EPCHAR_IO (1 << 31)
|
|
#define SITD_EPCHAR_PORTNUM_MASK 0x7f000000
|
|
#define SITD_EPCHAR_PORTNUM_SH 24
|
|
#define SITD_EPCHAR_HUBADD_MASK 0x007f0000
|
|
#define SITD_EPCHAR_HUBADDR_SH 16
|
|
#define SITD_EPCHAR_EPNUM_MASK 0x00000f00
|
|
#define SITD_EPCHAR_EPNUM_SH 8
|
|
#define SITD_EPCHAR_DEVADDR_MASK 0x0000007f
|
|
|
|
Bit32u uframe;
|
|
#define SITD_UFRAME_CMASK_MASK 0x0000ff00
|
|
#define SITD_UFRAME_CMASK_SH 8
|
|
#define SITD_UFRAME_SMASK_MASK 0x000000ff
|
|
|
|
Bit32u results;
|
|
#define SITD_RESULTS_IOC (1 << 31)
|
|
#define SITD_RESULTS_PGSEL (1 << 30)
|
|
#define SITD_RESULTS_TBYTES_MASK 0x03ff0000
|
|
#define SITD_RESULTS_TYBYTES_SH 16
|
|
#define SITD_RESULTS_CPROGMASK_MASK 0x0000ff00
|
|
#define SITD_RESULTS_CPROGMASK_SH 8
|
|
#define SITD_RESULTS_ACTIVE (1 << 7)
|
|
#define SITD_RESULTS_ERR (1 << 6)
|
|
#define SITD_RESULTS_DBERR (1 << 5)
|
|
#define SITD_RESULTS_BABBLE (1 << 4)
|
|
#define SITD_RESULTS_XACTERR (1 << 3)
|
|
#define SITD_RESULTS_MISSEDUF (1 << 2)
|
|
#define SITD_RESULTS_SPLITXSTATE (1 << 1)
|
|
|
|
Bit32u bufptr[2];
|
|
#define SITD_BUFPTR_MASK 0xfffff000
|
|
#define SITD_BUFPTR_CURROFF_MASK 0x00000fff
|
|
#define SITD_BUFPTR_TPOS_MASK 0x00000018
|
|
#define SITD_BUFPTR_TPOS_SH 3
|
|
#define SITD_BUFPTR_TCNT_MASK 0x00000007
|
|
|
|
Bit32u backptr; // Standard next link pointer
|
|
} EHCIsitd;
|
|
|
|
/* EHCI spec version 1.0 Section 3.5
|
|
*/
|
|
typedef struct EHCIqtd {
|
|
Bit32u next; // Standard next link pointer
|
|
Bit32u altnext; // Standard next link pointer
|
|
Bit32u token;
|
|
#define QTD_TOKEN_DTOGGLE (1 << 31)
|
|
#define QTD_TOKEN_TBYTES_MASK 0x7fff0000
|
|
#define QTD_TOKEN_TBYTES_SH 16
|
|
#define QTD_TOKEN_IOC (1 << 15)
|
|
#define QTD_TOKEN_CPAGE_MASK 0x00007000
|
|
#define QTD_TOKEN_CPAGE_SH 12
|
|
#define QTD_TOKEN_CERR_MASK 0x00000c00
|
|
#define QTD_TOKEN_CERR_SH 10
|
|
#define QTD_TOKEN_PID_MASK 0x00000300
|
|
#define QTD_TOKEN_PID_SH 8
|
|
#define QTD_TOKEN_ACTIVE (1 << 7)
|
|
#define QTD_TOKEN_HALT (1 << 6)
|
|
#define QTD_TOKEN_DBERR (1 << 5)
|
|
#define QTD_TOKEN_BABBLE (1 << 4)
|
|
#define QTD_TOKEN_XACTERR (1 << 3)
|
|
#define QTD_TOKEN_MISSEDUF (1 << 2)
|
|
#define QTD_TOKEN_SPLITXSTATE (1 << 1)
|
|
#define QTD_TOKEN_PING (1 << 0)
|
|
|
|
Bit32u bufptr[5]; // Standard buffer pointer
|
|
#define QTD_BUFPTR_MASK 0xfffff000
|
|
#define QTD_BUFPTR_SH 12
|
|
} EHCIqtd;
|
|
|
|
/* EHCI spec version 1.0 Section 3.6
|
|
*/
|
|
typedef struct EHCIqh {
|
|
Bit32u next; // Standard next link pointer
|
|
|
|
/* endpoint characteristics */
|
|
Bit32u epchar;
|
|
#define QH_EPCHAR_RL_MASK 0xf0000000
|
|
#define QH_EPCHAR_RL_SH 28
|
|
#define QH_EPCHAR_C (1 << 27)
|
|
#define QH_EPCHAR_MPLEN_MASK 0x07FF0000
|
|
#define QH_EPCHAR_MPLEN_SH 16
|
|
#define QH_EPCHAR_H (1 << 15)
|
|
#define QH_EPCHAR_DTC (1 << 14)
|
|
#define QH_EPCHAR_EPS_MASK 0x00003000
|
|
#define QH_EPCHAR_EPS_SH 12
|
|
#define EHCI_QH_EPS_FULL 0
|
|
#define EHCI_QH_EPS_LOW 1
|
|
#define EHCI_QH_EPS_HIGH 2
|
|
#define EHCI_QH_EPS_RESERVED 3
|
|
|
|
#define QH_EPCHAR_EP_MASK 0x00000f00
|
|
#define QH_EPCHAR_EP_SH 8
|
|
#define QH_EPCHAR_I (1 << 7)
|
|
#define QH_EPCHAR_DEVADDR_MASK 0x0000007f
|
|
#define QH_EPCHAR_DEVADDR_SH 0
|
|
|
|
/* endpoint capabilities */
|
|
Bit32u epcap;
|
|
#define QH_EPCAP_MULT_MASK 0xc0000000
|
|
#define QH_EPCAP_MULT_SH 30
|
|
#define QH_EPCAP_PORTNUM_MASK 0x3f800000
|
|
#define QH_EPCAP_PORTNUM_SH 23
|
|
#define QH_EPCAP_HUBADDR_MASK 0x007f0000
|
|
#define QH_EPCAP_HUBADDR_SH 16
|
|
#define QH_EPCAP_CMASK_MASK 0x0000ff00
|
|
#define QH_EPCAP_CMASK_SH 8
|
|
#define QH_EPCAP_SMASK_MASK 0x000000ff
|
|
#define QH_EPCAP_SMASK_SH 0
|
|
|
|
Bit32u current_qtd; // Standard next link pointer
|
|
Bit32u next_qtd; // Standard next link pointer
|
|
Bit32u altnext_qtd;
|
|
#define QH_ALTNEXT_NAKCNT_MASK 0x0000001e
|
|
#define QH_ALTNEXT_NAKCNT_SH 1
|
|
|
|
Bit32u token; // Same as QTD token
|
|
Bit32u bufptr[5]; // Standard buffer pointer
|
|
#define BUFPTR_CPROGMASK_MASK 0x000000ff
|
|
#define BUFPTR_FRAMETAG_MASK 0x0000001f
|
|
#define BUFPTR_SBYTES_MASK 0x00000fe0
|
|
#define BUFPTR_SBYTES_SH 5
|
|
} EHCIqh;
|
|
|
|
typedef struct EHCIPacket EHCIPacket;
|
|
typedef struct EHCIQueue EHCIQueue;
|
|
|
|
typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead;
|
|
|
|
typedef struct {
|
|
int frame_timer_index;
|
|
|
|
Bit8u usbsts_pending;
|
|
Bit32u usbsts_frindex;
|
|
EHCIQueueHead aqueues;
|
|
EHCIQueueHead pqueues;
|
|
Bit32u pstate, astate;
|
|
/* which address to look at next */
|
|
Bit32u a_fetch_addr;
|
|
Bit32u p_fetch_addr;
|
|
|
|
Bit64u last_run_usec;
|
|
Bit32u async_stepdown;
|
|
|
|
struct {
|
|
Bit8u CapLength;
|
|
Bit8u Reserved;
|
|
Bit16u HciVersion;
|
|
Bit32u HcsParams;
|
|
Bit32u HccParams;
|
|
} cap_regs;
|
|
|
|
struct {
|
|
struct {
|
|
Bit8u itc;
|
|
bx_bool iaad;
|
|
bx_bool ase;
|
|
bx_bool pse;
|
|
bx_bool hcreset;
|
|
bx_bool rs;
|
|
} UsbCmd;
|
|
struct {
|
|
bx_bool ass;
|
|
bx_bool pss;
|
|
bx_bool recl;
|
|
bx_bool hchalted;
|
|
Bit8u inti;
|
|
} UsbSts;
|
|
Bit8u UsbIntr;
|
|
Bit32u FrIndex;
|
|
Bit32u CtrlDsSegment;
|
|
Bit32u PeriodicListBase;
|
|
Bit32u AsyncListAddr;
|
|
Bit32u ConfigFlag;
|
|
} op_regs;
|
|
|
|
struct {
|
|
// our data
|
|
usb_device_c *device; // device connected to this port
|
|
bx_bool owner_change;
|
|
struct {
|
|
bx_bool woe;
|
|
bx_bool wde;
|
|
bx_bool wce;
|
|
Bit8u ptc;
|
|
Bit8u pic;
|
|
bx_bool po;
|
|
bx_bool pp;
|
|
Bit8u ls;
|
|
bx_bool pr;
|
|
bx_bool sus;
|
|
bx_bool fpr;
|
|
bx_bool occ;
|
|
bx_bool oca;
|
|
bx_bool pec;
|
|
bx_bool ped;
|
|
bx_bool csc;
|
|
bx_bool ccs;
|
|
} portsc;
|
|
} usb_port[USB_EHCI_PORTS];
|
|
|
|
} bx_usb_ehci_t;
|
|
|
|
enum async_state {
|
|
EHCI_ASYNC_NONE = 0,
|
|
EHCI_ASYNC_INITIALIZED,
|
|
EHCI_ASYNC_INFLIGHT,
|
|
EHCI_ASYNC_FINISHED,
|
|
};
|
|
|
|
struct EHCIPacket {
|
|
EHCIQueue *queue;
|
|
QTAILQ_ENTRY(EHCIPacket) next;
|
|
|
|
EHCIqtd qtd; /* copy of current QTD (being worked on) */
|
|
Bit32u qtdaddr; /* address QTD read from */
|
|
|
|
USBPacket packet;
|
|
int pid;
|
|
Bit32u tbytes;
|
|
enum async_state async;
|
|
int usb_status;
|
|
};
|
|
|
|
struct EHCIQueue {
|
|
bx_usb_ehci_t *ehci;
|
|
QTAILQ_ENTRY(EHCIQueue) next;
|
|
Bit32u seen;
|
|
Bit64u ts;
|
|
int async;
|
|
|
|
/* cached data from guest - needs to be flushed
|
|
* when guest removes an entry (doorbell, handshake sequence)
|
|
*/
|
|
EHCIqh qh; /* copy of current QH (being worked on) */
|
|
Bit32u qhaddr; /* address QH read from */
|
|
Bit32u qtdaddr; /* address QTD read from */
|
|
usb_device_c *dev;
|
|
QTAILQ_HEAD(, EHCIPacket) packets;
|
|
};
|
|
|
|
class bx_usb_ehci_c : public bx_devmodel_c, public bx_pci_device_stub_c {
|
|
public:
|
|
bx_usb_ehci_c();
|
|
virtual ~bx_usb_ehci_c();
|
|
virtual void init(void);
|
|
virtual void reset(unsigned);
|
|
virtual void register_state(void);
|
|
virtual void after_restore_state(void);
|
|
virtual Bit32u pci_read_handler(Bit8u address, unsigned io_len);
|
|
virtual void pci_write_handler(Bit8u address, Bit32u value, unsigned io_len);
|
|
|
|
void async_complete_packet(USBPacket *packet);
|
|
|
|
static const char *usb_param_handler(bx_param_string_c *param, int set,
|
|
const char *oldval, const char *val, int maxlen);
|
|
|
|
private:
|
|
bx_uhci_core_c *uhci[3];
|
|
bx_usb_ehci_t hub;
|
|
Bit8u devfunc;
|
|
Bit8u device_change;
|
|
int rt_conf_id;
|
|
Bit8u *device_buffer;
|
|
Bit32u maxframes;
|
|
|
|
void reset_hc(void);
|
|
void reset_port(int);
|
|
|
|
static void init_device(Bit8u port, bx_list_c *portconf);
|
|
static void remove_device(Bit8u port);
|
|
static void set_connect_status(Bit8u port, int type, bx_bool connected);
|
|
static void change_port_owner(int port);
|
|
|
|
// methods ported from QEMU EHCI
|
|
void update_irq(void);
|
|
void raise_irq(Bit8u intr);
|
|
void commit_irq(void);
|
|
void update_halt(void);
|
|
|
|
void set_state(int async, int state);
|
|
int get_state(int async);
|
|
void set_fetch_addr(int async, Bit32u addr);
|
|
Bit32u get_fetch_addr(int async);
|
|
|
|
EHCIPacket *alloc_packet(EHCIQueue *q);
|
|
void free_packet(EHCIPacket *p);
|
|
EHCIQueue *alloc_queue(Bit32u addr, int async);
|
|
int cancel_queue(EHCIQueue *q);
|
|
int reset_queue(EHCIQueue *q);
|
|
void free_queue(EHCIQueue *q, const char *warn);
|
|
EHCIQueue *find_queue_by_qh(Bit32u addr, int async);
|
|
void queues_rip_unused(int async);
|
|
void queues_rip_unseen(int async);
|
|
void queues_rip_device(usb_device_c *dev, int async);
|
|
void queues_rip_all(int async);
|
|
|
|
usb_device_c *find_device(Bit8u addr);
|
|
int get_dwords(Bit32u addr, Bit32u *buf, int num);
|
|
int put_dwords(Bit32u addr, Bit32u *buf, int num);
|
|
void flush_qh(EHCIQueue *q);
|
|
int qh_do_overlay(EHCIQueue *q);
|
|
int init_transfer(EHCIPacket *p);
|
|
void finish_transfer(EHCIQueue *q, int status);
|
|
void execute_complete(EHCIQueue *q);
|
|
int execute(EHCIPacket *p);
|
|
int process_itd(EHCIitd *itd, Bit32u addr);
|
|
|
|
int state_waitlisthead(int async);
|
|
int state_fetchentry(int async);
|
|
EHCIQueue *state_fetchqh(int async);
|
|
int state_fetchitd(int async);
|
|
int state_fetchsitd(int async);
|
|
int state_advqueue(EHCIQueue *q);
|
|
int state_fetchqtd(EHCIQueue *q);
|
|
int state_horizqh(EHCIQueue *q);
|
|
int fill_queue(EHCIPacket *p);
|
|
int state_execute(EHCIQueue *q);
|
|
int state_executing(EHCIQueue *q);
|
|
int state_writeback(EHCIQueue *q);
|
|
|
|
void advance_state(int async);
|
|
void advance_async_state(void);
|
|
void advance_periodic_state(void);
|
|
void update_frindex(int frames);
|
|
|
|
// EHCI frame timer
|
|
static void ehci_frame_handler(void *);
|
|
void ehci_frame_timer(void);
|
|
|
|
#if BX_USE_USB_EHCI_SMF
|
|
static bx_bool read_handler(bx_phy_address addr, unsigned len, void *data, void *param);
|
|
static bx_bool write_handler(bx_phy_address addr, unsigned len, void *data, void *param);
|
|
#else
|
|
bx_bool read_handler(bx_phy_address addr, unsigned len, void *data, void *param);
|
|
bx_bool write_handler(bx_phy_address addr, unsigned len, void *data, void *param);
|
|
#endif
|
|
|
|
static void runtime_config_handler(void *);
|
|
void runtime_config(void);
|
|
};
|
|
|
|
#endif // BX_IODEV_USB_EHCI_H
|