Bochs/bochs/iodev/usb/usb_ehci.h
Volker Ruppert c3b608a3c9 Implemented USB remote wakeup mechanism.
- Extended USB callback definition to support different event types sent from
  device to HC. Currently we only have the two events "async completion" and
  "remote wakeup".
- Implemented event handlers in the UHCI, OHCI and EHCI. For the EHCI version
  we currently have no test case.
- The external USB hub now uses this feature at device connect/disconnect.
  Now the device change on the hub is correctly detected by the guest OS.
- TODO #1: remote wakeup and async completion for the xHCI.
- TODO #2: event handler for the external hub to make a USB device work on
  a chain of hubs.
- TODO #3: the event handler possibly could be used for the packet copy code.
2016-12-10 13:45:49 +00:00

427 lines
13 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Experimental USB EHCI adapter (partly ported from Qemu)
//
// Copyright (C) 2015-2016 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 event_handler(int event, USBPacket *packet, int port);
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;
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);
// EHCI core methods ported from QEMU 1.2.2
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);
bx_bool async_enabled(void);
bx_bool periodic_enabled(void);
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);
void flush_qh(EHCIQueue *q);
int qh_do_overlay(EHCIQueue *q);
int 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