USB emulation update

Added examples for new options.
 - xhci: model= option
 - xhci: allow the user to select number of ports
 - usb disk: proto= option (bbb or uasp)
Added xhci primary stream support.
 (Secondary stream support is not included)
Added (experimental) usb disk protocol UASP for super- and high-speed usb disk devices.
  (high-speed uasp support is not thoroughly tested)
xhci: fixed "change event" function
scsi: fixed bug in command 0x25
scsi: added command 9E/10
This commit is contained in:
Benjamin David Lunt 2023-03-10 11:37:08 -07:00 committed by GitHub
parent 86b50988be
commit 7041926aac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1814 additions and 537 deletions

View File

@ -546,7 +546,7 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, Bit8u cmd_len, i
goto fail;
}
switch (command) {
case 0x0:
case 0x00:
BX_DEBUG(("Test Unit Ready"));
if (!inserted)
goto notready;
@ -974,11 +974,11 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, Bit8u cmd_len, i
outbuf[0] = (Bit8u)((nb_sectors >> 24) & 0xff);
outbuf[1] = (Bit8u)((nb_sectors >> 16) & 0xff);
outbuf[2] = (Bit8u)((nb_sectors >> 8) & 0xff);
outbuf[3] = (Bit8u)(nb_sectors & 0xff);
outbuf[4] = 0;
outbuf[5] = 0;
outbuf[6] = (block_size >> 8);
outbuf[7] = 0;
outbuf[3] = (Bit8u) (nb_sectors & 0xff);
outbuf[4] = (Bit8u) ((block_size >> 24) & 0xff);
outbuf[5] = (Bit8u) ((block_size >> 16) & 0xff);
outbuf[6] = (Bit8u) ((block_size >> 8) & 0xff);
outbuf[7] = (Bit8u) (block_size & 0xff);
r->buf_len = 8;
} else {
notready:
@ -1016,6 +1016,7 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, Bit8u cmd_len, i
r->async_mode = async;
break;
case 0x35:
case 0x91:
BX_DEBUG(("Synchronise cache (sector " FMT_LL "d, count %d)", lba, len));
// TODO: flush cache
break;
@ -1112,6 +1113,52 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, Bit8u cmd_len, i
}
break;
// The 0x9E command uses a service action code (ex: 0x9E/0x10)
case 0x9E:
switch (buf[1] & 0x1F) { // service action code
case 0x10: // Read Capacity(16)
BX_DEBUG(("Read Capacity 16"));
memset(outbuf, 0, 32);
if (type == SCSIDEV_TYPE_CDROM) {
nb_sectors = max_lba;
} else {
nb_sectors = hdimage->hd_size / block_size;
nb_sectors--;
}
/* Returned value is the address of the last sector. */
if (nb_sectors) {
// 64-bit lba
outbuf[ 0] = (Bit8u)((nb_sectors >> 56) & 0xff);
outbuf[ 1] = (Bit8u)((nb_sectors >> 48) & 0xff);
outbuf[ 2] = (Bit8u)((nb_sectors >> 40) & 0xff);
outbuf[ 3] = (Bit8u)((nb_sectors >> 32) & 0xff);
outbuf[ 4] = (Bit8u)((nb_sectors >> 24) & 0xff);
outbuf[ 5] = (Bit8u)((nb_sectors >> 16) & 0xff);
outbuf[ 6] = (Bit8u)((nb_sectors >> 8) & 0xff);
outbuf[ 7] = (Bit8u) (nb_sectors & 0xff);
// 32-bit block size
outbuf[ 8] = (Bit8u) ((block_size >> 24) & 0xff);
outbuf[ 9] = (Bit8u) ((block_size >> 16) & 0xff);
outbuf[10] = (Bit8u) ((block_size >> 8) & 0xff);
outbuf[11] = (Bit8u) (block_size & 0xff);
// protection
outbuf[12] = 0;
// exponent/one or more physical blocks per logical block
outbuf[13] = 0;
// lowest aligned logical block address
outbuf[14] = 0;
outbuf[15] = 0;
// bytes 16 through 31 are reserved (zero'd above)
r->buf_len = (len < 32) ? len : 32;
} else {
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY);
return 0;
}
break;
default:
BX_ERROR(("Unknown SCSI command (0x9E/%02X)", buf[1] & 0x1F));
}
break;
default:
BX_ERROR(("Unknown SCSI command (0x%02X)", buf[0]));
fail:

View File

@ -448,7 +448,7 @@ int usb_device_c::handle_packet(USBPacket *p)
#if HANDLE_TOGGLE_CONTROL
// manage our toggle bit
if ((p->toggle > -1) && (p->toggle != get_toggle(p->devep))) {
BX_ERROR(("DATA IN: Packet Toggle indicator doesn't match Device Toggle indicator. %d != %d", p->toggle, get_toggle(p->devep)));
BX_ERROR(("DATA IN EP%d: Packet Toggle indicator doesn't match Device Toggle indicator. %d != %d", p->devep, p->toggle, get_toggle(p->devep)));
goto fail;
}
set_toggle(p->devep, get_toggle(p->devep) ^ 1); // toggle the bit
@ -525,7 +525,7 @@ int usb_device_c::handle_packet(USBPacket *p)
#if HANDLE_TOGGLE_CONTROL
// manage our toggle bit
if ((p->toggle > -1) && (p->toggle != get_toggle(p->devep))) {
BX_ERROR(("DATA OUT: Packet Toggle indicator doesn't match Device Toggle indicator. %d != %d", p->toggle, get_toggle(p->devep)));
BX_ERROR(("DATA OUT EP%d: Packet Toggle indicator doesn't match Device Toggle indicator. %d != %d", p->devep, p->toggle, get_toggle(p->devep)));
goto fail;
}
set_toggle(p->devep, get_toggle(p->devep) ^ 1); // toggle the bit
@ -687,22 +687,33 @@ int usb_device_c::handle_control_common(int request, int value, int index, int l
}
break;
case InterfaceRequest | USB_REQ_GET_INTERFACE:
// Ben TODO: If the device is not in the configured state, this request should stall
BX_DEBUG(("USB_REQ_GET_INTERFACE:"));
// with InterfaceRequest, the wValue field must be zero and wLength field must be 1
if ((value != 0) || (length != 1)) {
BX_ERROR(("USB_REQ_GET_INTERFACE: This type of request requires the wValue field to be zero and wLength field to be one."));
}
data[0] = d.iface;
ret = 1;
// all our devices only have one interface, and that value must be zero
// if we ever add a device that has more than one interface (a video cam ?), we will need to modify this
if (index == 0) {
data[0] = d.alt_iface;
ret = 1;
}
break;
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
// Ben TODO: If the device is not in the configured state, this request should stall
BX_DEBUG(("USB_REQ_SET_INTERFACE: value=%d", value));
// with InterfaceRequest, the wIndex and wLength fields must be zero
if ((index != 0) || (length != 0)) {
BX_ERROR(("USB_REQ_SET_INTERFACE: This type of request requires the wIndex and wLength fields to be zero."));
}
d.iface = value;
ret = 0;
// all our devices only have one interface, and that value must be zero
// if we ever add a device that has more than one interface (a video cam ?), we will need to modify this
if ((index == 0) && (value <= d.alt_iface_max)) {
d.alt_iface = value; // alternate interface
handle_iface_change(value); // let the device know we changed the interface number
ret = 0;
}
break;
// should not have a default: here, so allowing the device's handle_control() to try to execute the request
}
@ -717,7 +728,7 @@ void usb_device_c::register_state(bx_list_c *parent)
bx_list_c *list = new bx_list_c(parent, "d", "Common USB Device State");
BXRS_DEC_PARAM_FIELD(list, addr, d.addr);
BXRS_DEC_PARAM_FIELD(list, config, d.config);
BXRS_DEC_PARAM_FIELD(list, interface, d.iface);
BXRS_DEC_PARAM_FIELD(list, interface, d.alt_iface);
BXRS_DEC_PARAM_FIELD(list, state, d.state);
BXRS_DEC_PARAM_FIELD(list, remote_wakeup, d.remote_wakeup);
register_state_specific(parent);
@ -755,13 +766,13 @@ void usb_device_c::usb_dump_packet(Bit8u *data, int size, int bus, int dev_addr,
}
// safety catch
if (size > 4096) {
BX_DEBUG(("packet hexdump with irregular size: %u (truncating to 64 bytes)", size));
if (size > 8192) {
BX_DEBUG(("packet hexdump with irregular size: %u (truncating to 8192 bytes)", size));
}
// safety catch (only dump up to 512 bytes per packet so to not fill the log file)
if (size > 512)
size = 512;
// safety catch (only dump up to 8192 bytes per packet so to not fill the log file)
if (size > 8192)
size = 8192;
if (getonoff(LOGLEV_DEBUG) == ACT_REPORT) {
BX_DEBUG(("packet hexdump (%d bytes)", size));

View File

@ -115,6 +115,7 @@
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
#define USB_REQ_SET_SEL 0x30
#define USB_REQ_SET_ISO_DELAY 0x31
#define USB_DEVICE_SELF_POWERED 0
#define USB_DEVICE_REMOTE_WAKEUP 1
@ -162,6 +163,7 @@ struct USBPacket {
USBCallback *complete_cb;
void *complete_dev;
usb_device_c *dev;
int strm_pid; // stream primary id
};
typedef struct USBAsync {
@ -211,6 +213,7 @@ public:
virtual void handle_reset() {}
virtual int handle_control(int request, int value, int index, int length, Bit8u *data) { return -1; }
virtual int handle_data(USBPacket *p) { return 0; }
virtual void handle_iface_change(int iface) {}
void register_state(bx_list_c *parent);
virtual void register_state_specific(bx_list_c *parent) {}
virtual void after_restore_state() {}
@ -231,7 +234,7 @@ public:
}
// return information for the specified ep of the current device
#define USB_MAX_ENDPOINTS 4 // we currently don't use more than 4 endpoints (ep0, ep1, ep2, and ep3)
#define USB_MAX_ENDPOINTS 5 // we currently don't use more than 5 endpoints (ep0, ep1, ep2, ep3, and ep4)
int get_mps(const int ep) {
return (ep < USB_MAX_ENDPOINTS) ? d.endpoint_info[ep].max_packet_size : 0;
}
@ -252,6 +255,9 @@ public:
Bit8u get_type() {
return d.type;
}
Bit8u get_aIface() {
return d.alt_iface;
}
Bit8u get_address() {return d.addr;}
void set_async_mode(bool async) { d.async_mode = async; }
@ -275,7 +281,8 @@ protected:
int speed;
Bit8u addr;
Bit8u config;
Bit8u iface;
Bit8u alt_iface;
Bit8u alt_iface_max;
char devname[32];
USBEndPoint endpoint_info[USB_MAX_ENDPOINTS];

View File

@ -1432,12 +1432,13 @@ int bx_usb_ehci_c::execute(EHCIPacket *p)
#endif
p->packet.complete_cb = ehci_event_handler;
p->packet.complete_dev = BX_EHCI_THIS_PTR;
p->packet.strm_pid = 0; // if UASP is used, this must be 0
p->async = EHCI_ASYNC_INITIALIZED;
}
ret = p->queue->dev->handle_packet(&p->packet);
BX_DEBUG(("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
BX_DEBUG(("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d",
p->queue->qhaddr, p->queue->qh.next, p->queue->qtdaddr, p->pid,
p->packet.len, p->tbytes, endp, ret));
@ -2079,7 +2080,10 @@ void bx_usb_ehci_c::advance_async_state(void)
default:
/* this should only be due to a developer mistake */
BX_PANIC(("Bad asynchronous state %d. Resetting to active", BX_EHCI_THIS hub.astate));
// Ben: I commented this line due to the fact that after a USB_RET_ASYNC return,
// then a usb_packet_complete(p), the event handler is setting the astate to
// EST_EXECUTE instead of EST_ACTIVE ???
//BX_PANIC(("Bad asynchronous state %d. Resetting to active", BX_EHCI_THIS hub.astate));
BX_EHCI_THIS set_state(async, EST_ACTIVE);
}
}

View File

@ -470,6 +470,7 @@ bool usb_floppy_device_c::init()
sprintf(s.info_txt, "USB floppy: media not present");
}
d.connected = 1;
d.alt_iface_max = 0;
s.did_inquiry_fail = 0;
s.fail_count = 0;

View File

@ -792,7 +792,7 @@ bool usb_hid_device_c::init()
* in the configuration, simply uncomment this line. I use
* it when I am working on this emulation.
*/
LOG_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
//LOG_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
if ((d.type == USB_HID_TYPE_MOUSE) ||
(d.type == USB_HID_TYPE_TABLET)) {
@ -850,6 +850,7 @@ bool usb_hid_device_c::init()
}
}
d.connected = 1;
d.alt_iface_max = 0;
return 1;
}

View File

@ -283,6 +283,7 @@ bool usb_hub_device_c::init()
}
sprintf(hub.info_txt, "%d-port USB hub", hub.n_ports);
d.connected = 1;
d.alt_iface_max = 0;
return 1;
}
@ -740,8 +741,10 @@ Bit64s usb_hub_device_c::hub_param_handler(bx_param_c *param, bool set, Bit64s v
} else {
BX_PANIC(("usb_param_handler called with unexpected parameter '%s'", param->get_name()));
}
} else {
BX_PANIC(("hub_param_handler: external hub not found"));
// hub == NULL. We shouldn't call BX_PANIC with a NULL pointer.
// #define BX_PANIC(x) (LOG_THIS /* = hub-> */ panic) x
//} else {
// BX_PANIC(("hub_param_handler: external hub not found"));
}
}
return val;

View File

@ -140,7 +140,7 @@ static const Bit8u bx_msd_config_descriptor[] = {
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
0x00, /* u8 if_bAlternateSetting; */
MSD_PROTO_BBB, /* u8 if_bAlternateSetting; */
0x02, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
@ -150,7 +150,7 @@ static const Bit8u bx_msd_config_descriptor[] = {
/* Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
0x80 | MSD_BBB_DATAIN_EP, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x40, 0x00, /* u16 ep_wMaxPacketSize; 64 */
0x00, /* u8 ep_bInterval; */
@ -158,7 +158,7 @@ static const Bit8u bx_msd_config_descriptor[] = {
/* Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
0x00 | MSD_BBB_DATAOUT_EP, /* u8 ep_bEndpointAddress; OUT Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x40, 0x00, /* u16 ep_wMaxPacketSize; 64 */
0x00 /* u8 ep_bInterval; */
@ -179,7 +179,7 @@ static const Bit8u bx_msd_dev_descriptor2[] = {
0x00, 0x00, /* u16 idVendor; */
0x00, 0x00, /* u16 idProduct; */
0x00, 0x01, /* u16 bcdDevice */
0x01, /* u8 iManufacturer; */
0x02, /* u8 iProduct; */
0x03, /* u8 iSerialNumber; */
@ -187,7 +187,7 @@ static const Bit8u bx_msd_dev_descriptor2[] = {
};
// High-speed
static const Bit8u bx_msd_config_descriptor2[] = {
static Bit8u bx_msd_config_descriptor2[] = {
/* one configuration */
0x09, /* u8 bLength; */
@ -196,18 +196,18 @@ static const Bit8u bx_msd_config_descriptor2[] = {
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x00, /* u8 iConfiguration; */
0xc0, /* u8 bmAttributes;
0x80, /* u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* u8 MaxPower; */
/* one interface */
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
0x00, /* u8 if_bAlternateSetting; */
MSD_PROTO_BBB, /* u8 if_bAlternateSetting; */
0x02, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
@ -217,7 +217,7 @@ static const Bit8u bx_msd_config_descriptor2[] = {
/* Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
0x80 | MSD_BBB_DATAIN_EP, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
0x00, /* u8 ep_bInterval; */
@ -225,10 +225,83 @@ static const Bit8u bx_msd_config_descriptor2[] = {
/* Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
0x00 | MSD_BBB_DATAOUT_EP, /* u8 ep_bEndpointAddress; OUT Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
0x00 /* u8 ep_bInterval; */
0x00, /* u8 ep_bInterval; */
/***** If UASP is requested, the rest of this descriptor
* will be returned, else, the descriptor stops here *****/
/* alt interface 1 */
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
MSD_PROTO_UASP, /* u8 if_bAlternateSetting; */
0x04, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
0x62, /* u8 if_bInterfaceProtocol; UASP */
0x00, /* u8 if_iInterface; */
/***** Command Out Pipe *****/
/* Alt Int 1: Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x00 | MSD_UASP_COMMAND, /* u8 ep_bEndpointAddress; Out Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
0x00, /* u8 ep_bInterval; */
/* Alt Int 1: Bulk-In Pipe usage */
0x04, /* u8 bLength; */
0x24, /* u8 bDescriptorType; Function */
MSD_UASP_COMMAND, /* u8 bPipeUsage; Command Out pipe */
0x00, /* u8 breserved; */
/***** Status In Pipe *****/
/* Alt Int 1: Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x80 | MSD_UASP_STATUS, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
0x00, /* u8 ep_bInterval; */
/* Alt Int 1: Bulk-In Pipe usage */
0x04, /* u8 bLength; */
0x24, /* u8 bDescriptorType; Function */
MSD_UASP_STATUS, /* u8 bPipeUsage; Status In pipe */
0x00, /* u8 breserved; */
/***** Data In Pipe *****/
/* Alt Int 1: Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x80 | MSD_UASP_DATAIN, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
0x00, /* u8 ep_bInterval; */
/* Alt Int 1: Bulk-In Pipe usage */
0x04, /* u8 bLength; */
0x24, /* u8 bDescriptorType; Function */
MSD_UASP_DATAIN, /* u8 bPipeUsage; Data In pipe */
0x00, /* u8 breserved; */
/***** Data Out Pipe *****/
/* Alt Int 1: Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x00 | MSD_UASP_DATAOUT, /* u8 ep_bEndpointAddress; OUT Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x02, /* u16 ep_wMaxPacketSize; 512 */
0x00, /* u8 ep_bInterval; */
/* Alt Int 1: Bulk-Out Pipe usage */
0x04, /* u8 bLength; */
0x24, /* u8 bDescriptorType; Function */
MSD_UASP_DATAOUT, /* u8 bPipeUsage; Data Out pipe */
0x00, /* u8 breserved; */
};
// Super-speed
@ -253,11 +326,14 @@ static const Bit8u bx_msd_dev_descriptor3[] = {
0x01 /* u8 bNumConfigurations; */
};
static const Bit8u bx_msd_config_descriptor3[] = {
static Bit8u bx_msd_config_descriptor3[] = {
/* one configuration */
0x09, /* u8 bLength; */
0x02, /* u8 bDescriptorType; Configuration */
0x2C, 0x00, /* u16 wTotalLength; */
/* this length will be modified depending if UASP is requested (0x002C or 0x0079) */
0x2C, 0x00, /* u16 wTotalLength; */
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x00, /* u8 iConfiguration; */
@ -266,48 +342,149 @@ static const Bit8u bx_msd_config_descriptor3[] = {
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x3F, /* u8 MaxPower; */
0x12, /* u8 MaxPower; */
/* one interface */
/* alt interface 0 */
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
0x00, /* u8 if_bAlternateSetting; */
MSD_PROTO_BBB, /* u8 if_bAlternateSetting; */
0x02, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
0x00, /* u8 if_iInterface; */
/* Bulk-In endpoint */
/* Alt Int 0: Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
0x80 | MSD_BBB_DATAIN_EP, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
0x00, /* u8 ep_bInterval; */
/* Bulk-In companion descriptor */
/* Alt Int 0: Bulk-In companion descriptor */
0x06, /* u8 epc_bLength; */
0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
0x0F, /* u8 epc_bMaxPerBurst; */
0x00, /* u8 epc_bmAttributes; */
0x00, 0x00, /* u16 epc_reserved; */
/* Bulk-Out endpoint */
/* Alt Int 0: Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
0x00 | MSD_BBB_DATAOUT_EP, /* u8 ep_bEndpointAddress; OUT Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
0x00, /* u8 ep_bInterval; */
/* Bulk-Out companion descriptor */
/* Alt Int 0: Bulk-Out companion descriptor */
0x06, /* u8 epc_bLength; */
0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
0x0F, /* u8 epc_bMaxPerBurst; */
0x00, /* u8 epc_bmAttributes; */
0x00, 0x00 /* u16 epc_reserved; */
0x00, 0x00, /* u16 epc_reserved; */
/***** If UASP is requested, the rest of this descriptor
* will be returned, else, the descriptor stops here *****/
/* alt interface 1 */
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
MSD_PROTO_UASP, /* u8 if_bAlternateSetting; */
0x04, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
0x62, /* u8 if_bInterfaceProtocol; UASP */
0x00, /* u8 if_iInterface; */
/***** Command Out Pipe *****/
/* Alt Int 1: Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x00 | MSD_UASP_COMMAND, /* u8 ep_bEndpointAddress; Out Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
0x00, /* u8 ep_bInterval; */
/* Alt Int 1: Bulk-Out companion descriptor */
0x06, /* u8 epc_bLength; */
0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
0x00, /* u8 epc_bMaxPerBurst; */
0x00, /* u8 epc_bmAttributes; no streams */
0x00, 0x00, /* u16 epc_reserved; */
/* Alt Int 1: Bulk-In Pipe usage */
0x04, /* u8 bLength; */
0x24, /* u8 bDescriptorType; Function */
MSD_UASP_COMMAND, /* u8 bPipeUsage; Command Out pipe */
0x00, /* u8 breserved; */
/***** Status In Pipe *****/
/* Alt Int 1: Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x80 | MSD_UASP_STATUS, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
0x00, /* u8 ep_bInterval; */
/* Alt Int 1: Bulk-In companion descriptor */
0x06, /* u8 epc_bLength; */
0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
0x0F, /* u8 epc_bMaxPerBurst; */
UASP_MAX_STREAMS, /* u8 epc_bmAttributes; 2^x max streams */
0x00, 0x00, /* u16 epc_reserved; */
/* Alt Int 1: Bulk-In Pipe usage */
0x04, /* u8 bLength; */
0x24, /* u8 bDescriptorType; Function */
MSD_UASP_STATUS, /* u8 bPipeUsage; Status In pipe */
0x00, /* u8 breserved; */
/***** Data In Pipe *****/
/* Alt Int 1: Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x80 | MSD_UASP_DATAIN, /* u8 ep_bEndpointAddress; IN Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
0x00, /* u8 ep_bInterval; */
/* Alt Int 1: Bulk-In companion descriptor */
0x06, /* u8 epc_bLength; */
0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
0x0F, /* u8 epc_bMaxPerBurst; */
UASP_MAX_STREAMS, /* u8 epc_bmAttributes; 2^x max streams */
0x00, 0x00, /* u16 epc_reserved; */
/* Alt Int 1: Bulk-In Pipe usage */
0x04, /* u8 bLength; */
0x24, /* u8 bDescriptorType; Function */
MSD_UASP_DATAIN, /* u8 bPipeUsage; Data In pipe */
0x00, /* u8 breserved; */
/***** Data Out Pipe *****/
/* Alt Int 1: Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x00 | MSD_UASP_DATAOUT, /* u8 ep_bEndpointAddress; OUT Endpoint */
0x02, /* u8 ep_bmAttributes; Bulk */
0x00, 0x04, /* u16 ep_wMaxPacketSize; 1024 */
0x00, /* u8 ep_bInterval; */
/* Alt Int 1: Bulk-Out companion descriptor */
0x06, /* u8 epc_bLength; */
0x30, /* u8 epc_bDescriptorType; Endpoint Companion */
0x0F, /* u8 epc_bMaxPerBurst; */
UASP_MAX_STREAMS, /* u8 epc_bmAttributes; 2^x max streams */
0x00, 0x00, /* u16 epc_reserved; */
/* Alt Int 1: Bulk-Out Pipe usage */
0x04, /* u8 bLength; */
0x24, /* u8 bDescriptorType; Function */
MSD_UASP_DATAOUT, /* u8 bPipeUsage; Data Out pipe */
0x00, /* u8 breserved; */
};
// BOS Descriptor
@ -479,18 +656,36 @@ bool usb_msd_device_c::set_option(const char *option)
} else {
BX_ERROR(("Option 'sect_size' is only valid for USB disks"));
}
} else if (!strncmp(option, "proto:", 6)) {
if (!strcmp(option+6, "uasp")) {
s.proto = MSD_PROTO_UASP;
} else if (!strcmp(option+6, "bbb")) {
s.proto = MSD_PROTO_BBB;
} else {
BX_ERROR(("Unknown option '%s' for proto:", option+6));
}
return 1;
}
return 0;
}
bool usb_msd_device_c::init()
{
unsigned i;
/* If you wish to set DEBUG=report in the code, instead of
* in the configuration, simply uncomment this line. I use
* it when I am working on this emulation.
*/
//LOG_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
LOG_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
// check to make sure correct speed is used if the proto is uasp
if ((s.proto == MSD_PROTO_UASP) && (d.speed < USB_SPEED_HIGH)) {
BX_ERROR(("UASP selected on a non-uasp speed device."));
s.proto = MSD_PROTO_BBB;
}
d.alt_iface_max = 0;
if (d.type == USB_MSD_TYPE_DISK) {
if (strlen(s.fname) > 0) {
s.hdimage = DEV_hdimage_init_image(s.image_mode, 0, s.journal);
@ -504,7 +699,7 @@ bool usb_msd_device_c::init()
s.hdimage->sect_size = s.sect_size;
}
if (s.hdimage->open(s.fname) < 0) {
BX_ERROR(("could not open hard drive image file '%s'", s.fname));
BX_PANIC(("could not open hard drive image file '%s'", s.fname));
return 0;
} else {
s.scsi_dev = new scsi_device_t(s.hdimage, 0, usb_msd_command_complete, (void*)this);
@ -512,7 +707,7 @@ bool usb_msd_device_c::init()
sprintf(s.info_txt, "USB HD: path='%s', mode='%s', sect_size=%d", s.fname,
s.image_mode, s.hdimage->sect_size);
} else {
BX_ERROR(("USB HD: disk image not specified"));
BX_PANIC(("USB HD: disk image not specified"));
return 0;
}
} else if (d.type == USB_MSD_TYPE_CDROM) {
@ -528,28 +723,36 @@ bool usb_msd_device_c::init()
if (getonoff(LOGLEV_DEBUG) == ACT_REPORT) {
s.scsi_dev->set_debug_mode();
}
if (get_speed() == USB_SPEED_SUPER) {
if (d.speed == USB_SPEED_SUPER) {
d.dev_descriptor = bx_msd_dev_descriptor3;
d.config_descriptor = bx_msd_config_descriptor3;
d.device_desc_size = sizeof(bx_msd_dev_descriptor3);
d.config_desc_size = sizeof(bx_msd_config_descriptor3);
d.endpoint_info[USB_CONTROL_EP].max_packet_size = 512; // Control ep0
d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
d.endpoint_info[1].max_packet_size = 1024; // In ep1
d.endpoint_info[1].max_burst_size = 15;
d.endpoint_info[2].max_packet_size = 1024; // Out ep2
d.endpoint_info[2].max_burst_size = 15;
// we need to set the length of the descriptor per the protocol used
if (s.proto == MSD_PROTO_UASP) {
* (Bit16u *) &bx_msd_config_descriptor3[2] =
d.config_desc_size = sizeof(bx_msd_config_descriptor3);
d.alt_iface_max = 1; // allow alt interface 0 through 1
} else {
* (Bit16u *) &bx_msd_config_descriptor3[2] =
d.config_desc_size = 0x002C;
}
// initialize the bbb's endpoint data
handle_iface_change(MSD_PROTO_BBB);
} else if (get_speed() == USB_SPEED_HIGH) {
d.dev_descriptor = bx_msd_dev_descriptor2;
d.config_descriptor = bx_msd_config_descriptor2;
d.device_desc_size = sizeof(bx_msd_dev_descriptor2);
d.config_desc_size = sizeof(bx_msd_config_descriptor2);
d.endpoint_info[USB_CONTROL_EP].max_packet_size = 64; // Control ep0
d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
d.endpoint_info[1].max_packet_size = 512; // In ep1
d.endpoint_info[1].max_burst_size = 0;
d.endpoint_info[2].max_packet_size = 512; // Out ep2
d.endpoint_info[2].max_burst_size = 0;
// we need to set the length of the descriptor per the protocol used
if (s.proto == MSD_PROTO_UASP) {
* (Bit16u *) &bx_msd_config_descriptor2[2] =
d.config_desc_size = sizeof(bx_msd_config_descriptor2);
d.alt_iface_max = 1; // allow alt interface 0 through 1
} else {
* (Bit16u *) &bx_msd_config_descriptor2[2] =
d.config_desc_size = 0x0020;
}
// initialize the bbb's endpoint data
handle_iface_change(MSD_PROTO_BBB);
} else { // USB_SPEED_FULL
d.dev_descriptor = bx_msd_dev_descriptor;
d.config_descriptor = bx_msd_config_descriptor;
@ -557,15 +760,21 @@ bool usb_msd_device_c::init()
d.config_desc_size = sizeof(bx_msd_config_descriptor);
d.endpoint_info[USB_CONTROL_EP].max_packet_size = 64; // Control ep0
d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
d.endpoint_info[1].max_packet_size = 64; // In ep1
d.endpoint_info[1].max_burst_size = 0;
d.endpoint_info[2].max_packet_size = 64; // Out ep2
d.endpoint_info[2].max_burst_size = 0;
d.endpoint_info[MSD_BBB_DATAIN_EP].max_packet_size = 64; // In ep
d.endpoint_info[MSD_BBB_DATAIN_EP].max_burst_size = 0;
d.endpoint_info[MSD_BBB_DATAOUT_EP].max_packet_size = 64; // Out ep
d.endpoint_info[MSD_BBB_DATAOUT_EP].max_burst_size = 0;
}
d.serial_num = s.scsi_dev->get_serial_number();
s.mode = USB_MSDM_CBW;
d.connected = 1;
s.status_changed = 0;
// initialize the uasp stream data
for (i=0; i<UASP_MAX_STREAMS_N; i++) {
s.uasp_request[i].mode = 0;
}
return 1;
}
@ -617,7 +826,7 @@ int usb_msd_device_c::handle_control(int request, int value, int index, int leng
goto fail;
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
BX_DEBUG(("USB_REQ_SET_FEATURE:"));
BX_DEBUG(("USB_REQ_SET_FEATURE: %d", value));
switch (value) {
case USB_DEVICE_REMOTE_WAKEUP:
case USB_DEVICE_U1_ENABLE:
@ -633,7 +842,7 @@ int usb_msd_device_c::handle_control(int request, int value, int index, int leng
switch (value >> 8) {
case USB_DT_STRING:
BX_DEBUG(("USB_REQ_GET_DESCRIPTOR: String"));
switch(value & 0xFF) {
switch (value & 0xFF) {
case 0xEE:
// Microsoft OS Descriptor check
// We don't support this check, so fail
@ -705,14 +914,25 @@ int usb_msd_device_c::handle_control(int request, int value, int index, int leng
set_toggle(index, 0);
#endif
ret = 0;
} else
} else {
BX_ERROR(("Bad value for clear feature: %d", value));
goto fail;
}
break;
case DeviceOutRequest | USB_REQ_SET_SEL:
// Set U1 and U2 System Exit Latency
BX_DEBUG(("SET_SEL (U1 and U2):"));
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_ISO_DELAY:
// Set Isochronous Delay (USB 3.0+)
BX_DEBUG(("USB_REQ_SET_ISO_DELAY: %d", value));
if ((index == 0) && (length == 0)) { // index and length must be zero
// value = setting (ignored)
ret = 0;
} else
goto fail;
break;
// Class specific requests
case InterfaceOutClassRequest | MassStorageReset:
case MassStorageReset:
@ -740,12 +960,61 @@ int usb_msd_device_c::handle_control(int request, int value, int index, int leng
return ret;
}
void usb_msd_device_c::handle_iface_change(int iface)
{
if (d.speed == USB_SPEED_SUPER) {
d.endpoint_info[USB_CONTROL_EP].max_packet_size = 512; // Control ep0
d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
if (iface == MSD_PROTO_BBB) {
// initialize the bbb's endpoint data
d.endpoint_info[MSD_BBB_DATAIN_EP].max_packet_size = 1024; // In ep
d.endpoint_info[MSD_BBB_DATAIN_EP].max_burst_size = 15;
d.endpoint_info[MSD_BBB_DATAOUT_EP].max_packet_size = 1024; // Out ep
d.endpoint_info[MSD_BBB_DATAOUT_EP].max_burst_size = 15;
} else if (iface == MSD_PROTO_UASP) {
d.endpoint_info[MSD_UASP_COMMAND].max_packet_size = 1024;
d.endpoint_info[MSD_UASP_COMMAND].max_burst_size = 0;
d.endpoint_info[MSD_UASP_STATUS].max_packet_size = 1024;
d.endpoint_info[MSD_UASP_STATUS].max_burst_size = 15;
d.endpoint_info[MSD_UASP_DATAIN].max_packet_size = 1024;
d.endpoint_info[MSD_UASP_DATAIN].max_burst_size = 15;
d.endpoint_info[MSD_UASP_DATAOUT].max_packet_size = 1024;
d.endpoint_info[MSD_UASP_DATAOUT].max_burst_size = 15;
} else {
BX_ERROR(("Unknown interface number: %d", iface));
}
// ben
} else if (d.speed == USB_SPEED_HIGH) {
d.endpoint_info[USB_CONTROL_EP].max_packet_size = 64; // Control ep0
d.endpoint_info[USB_CONTROL_EP].max_burst_size = 0;
if (iface == MSD_PROTO_BBB) {
// initialize the bbb's endpoint data
d.endpoint_info[MSD_BBB_DATAIN_EP].max_packet_size = 512; // In ep
d.endpoint_info[MSD_BBB_DATAIN_EP].max_burst_size = 0;
d.endpoint_info[MSD_BBB_DATAOUT_EP].max_packet_size = 512; // Out ep
d.endpoint_info[MSD_BBB_DATAOUT_EP].max_burst_size = 0;
} else if (iface == MSD_PROTO_UASP) {
d.endpoint_info[MSD_UASP_COMMAND].max_packet_size = 512;
d.endpoint_info[MSD_UASP_COMMAND].max_burst_size = 0;
d.endpoint_info[MSD_UASP_STATUS].max_packet_size = 512;
d.endpoint_info[MSD_UASP_STATUS].max_burst_size = 0;
d.endpoint_info[MSD_UASP_DATAIN].max_packet_size = 512;
d.endpoint_info[MSD_UASP_DATAIN].max_burst_size = 0;
d.endpoint_info[MSD_UASP_DATAOUT].max_packet_size = 512;
d.endpoint_info[MSD_UASP_DATAOUT].max_burst_size = 0;
} else {
BX_ERROR(("Unknown interface number: %d", iface));
}
}
}
int usb_msd_device_c::handle_data(USBPacket *p)
{
struct usb_msd_cbw cbw;
int ret = 0;
Bit8u devep = p->devep;
Bit8u *data = p->data;
Bit8u aIface = get_aIface();
int len = p->len;
bool was_status = false; // so don't dump the status packet twice
@ -754,145 +1023,157 @@ int usb_msd_device_c::handle_data(USBPacket *p)
BX_DEBUG(("EP%d transfer length (%d) is greater than Max Packet Size (%d).", p->devep, p->len, get_mps(p->devep)));
}
switch (p->pid) {
case USB_TOKEN_OUT:
usb_dump_packet(data, len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_BULK, false, true);
if (devep != 2)
goto fail;
// are we are doing bbb interface?
if (aIface == MSD_PROTO_BBB) {
switch (p->pid) {
case USB_TOKEN_OUT:
usb_dump_packet(data, len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_BULK, false, true);
if (devep != MSD_BBB_DATAOUT_EP)
goto fail;
switch (s.mode) {
case USB_MSDM_CBW:
if (len != 31) {
BX_ERROR(("bad CBW len (%d)", len));
goto fail;
}
memcpy(&cbw, data, 31);
if (dtoh32(cbw.sig) != 0x43425355) {
BX_ERROR(("bad signature %08X", dtoh32(cbw.sig)));
goto fail;
}
BX_DEBUG(("command on LUN %d", cbw.lun));
s.tag = dtoh32(cbw.tag);
s.data_len = dtoh32(cbw.data_len);
if (s.data_len == 0) {
s.mode = USB_MSDM_CSW;
} else if (cbw.flags & 0x80) {
s.mode = USB_MSDM_DATAIN;
} else {
s.mode = USB_MSDM_DATAOUT;
}
BX_DEBUG(("command tag 0x%X flags %02X cmd_len %d data_len %d",
s.tag, cbw.flags, cbw.cmd_len, s.data_len));
s.residue = 0;
s.scsi_dev->scsi_send_command(s.tag, cbw.cmd, cbw.cmd_len, cbw.lun, d.async_mode);
if (s.residue == 0) {
if (s.mode == USB_MSDM_DATAIN) {
s.scsi_dev->scsi_read_data(s.tag);
} else if (s.mode == USB_MSDM_DATAOUT) {
s.scsi_dev->scsi_write_data(s.tag);
switch (s.mode) {
case USB_MSDM_CBW:
if (len != 31) {
BX_ERROR(("bad CBW len (%d)", len));
goto fail;
}
}
ret = len;
break;
case USB_MSDM_DATAOUT:
BX_DEBUG(("data out %d/%d", len, s.data_len));
if (len > (int)s.data_len)
goto fail;
s.usb_buf = data;
s.usb_len = len;
while (s.usb_len && s.scsi_len) {
copy_data();
}
if (s.residue && s.usb_len) {
s.data_len -= s.usb_len;
if (s.data_len == 0)
memcpy(&cbw, data, 31);
if (dtoh32(cbw.sig) != 0x43425355) {
BX_ERROR(("bad signature %08X", dtoh32(cbw.sig)));
goto fail;
}
BX_DEBUG(("command on LUN %d", cbw.lun));
s.tag = dtoh32(cbw.tag);
s.data_len = dtoh32(cbw.data_len);
if (s.data_len == 0) {
s.mode = USB_MSDM_CSW;
s.usb_len = 0;
}
if (s.usb_len) {
} else if (cbw.flags & 0x80) {
s.mode = USB_MSDM_DATAIN;
} else {
s.mode = USB_MSDM_DATAOUT;
}
BX_DEBUG(("command tag 0x%X flags %02X cmd_len %d data_len %d",
s.tag, cbw.flags, cbw.cmd_len, s.data_len));
s.residue = 0;
s.scsi_dev->scsi_send_command(s.tag, cbw.cmd, cbw.cmd_len, cbw.lun, d.async_mode);
if (s.residue == 0) {
if (s.mode == USB_MSDM_DATAIN) {
s.scsi_dev->scsi_read_data(s.tag);
} else if (s.mode == USB_MSDM_DATAOUT) {
s.scsi_dev->scsi_write_data(s.tag);
}
}
ret = len;
break;
case USB_MSDM_DATAOUT:
BX_DEBUG(("data out %d/%d", len, s.data_len));
if (len > (int)s.data_len)
goto fail;
s.usb_buf = data;
s.usb_len = len;
while (s.usb_len && s.scsi_len) {
copy_data();
}
if (s.residue && s.usb_len) {
s.data_len -= s.usb_len;
if (s.data_len == 0)
s.mode = USB_MSDM_CSW;
s.usb_len = 0;
}
if (s.usb_len) {
BX_DEBUG(("deferring packet %p", p));
usb_defer_packet(p, this);
s.packet = p;
ret = USB_RET_ASYNC;
} else {
ret = len;
}
break;
default:
BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_OUT: (0x%02X)", s.mode));
goto fail;
}
break;
case USB_TOKEN_IN:
if (devep != MSD_BBB_DATAIN_EP)
goto fail;
switch (s.mode) {
case USB_MSDM_DATAOUT:
if (s.data_len != 0 || len < 13)
goto fail;
BX_DEBUG(("deferring packet %p", p));
usb_defer_packet(p, this);
s.packet = p;
ret = USB_RET_ASYNC;
} else {
ret = len;
}
break;
break;
default:
BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_OUT: (0x%02X)", s.mode));
goto fail;
}
break;
case USB_MSDM_CSW:
BX_DEBUG(("command status %d tag 0x%x, len %d",
s.result, s.tag, len));
if (len < 13)
return ret;
case USB_TOKEN_IN:
if (devep != 1)
goto fail;
send_status(p);
s.mode = USB_MSDM_CBW;
was_status = true;
ret = 13;
break;
switch (s.mode) {
case USB_MSDM_DATAOUT:
if (s.data_len != 0 || len < 13)
case USB_MSDM_DATAIN:
BX_DEBUG(("data in %d/%d", len, s.data_len));
if (len > (int)s.data_len)
len = s.data_len;
s.usb_buf = data;
s.usb_len = len;
while (s.usb_len && s.scsi_len) {
copy_data();
}
if (s.residue && s.usb_len) {
s.data_len -= s.usb_len;
memset(s.usb_buf, 0, s.usb_len);
if (s.data_len == 0)
s.mode = USB_MSDM_CSW;
s.usb_len = 0;
}
if (s.usb_len) {
BX_DEBUG(("deferring packet %p", p));
usb_defer_packet(p, this);
s.packet = p;
ret = USB_RET_ASYNC;
} else {
ret = len;
}
break;
default:
BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_IN: (0x%02X)", s.mode));
goto fail;
BX_DEBUG(("deferring packet %p", p));
usb_defer_packet(p, this);
s.packet = p;
ret = USB_RET_ASYNC;
break;
}
if (!was_status && (ret > 0)) usb_dump_packet(data, ret, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
break;
case USB_MSDM_CSW:
BX_DEBUG(("command status %d tag 0x%x, len %d",
s.result, s.tag, len));
if (len < 13)
return ret;
send_status(p);
s.mode = USB_MSDM_CBW;
was_status = true;
ret = 13;
break;
case USB_MSDM_DATAIN:
BX_DEBUG(("data in %d/%d", len, s.data_len));
if (len > (int)s.data_len)
len = s.data_len;
s.usb_buf = data;
s.usb_len = len;
while (s.usb_len && s.scsi_len) {
copy_data();
}
if (s.residue && s.usb_len) {
s.data_len -= s.usb_len;
memset(s.usb_buf, 0, s.usb_len);
if (s.data_len == 0)
s.mode = USB_MSDM_CSW;
s.usb_len = 0;
}
if (s.usb_len) {
BX_DEBUG(("deferring packet %p", p));
usb_defer_packet(p, this);
s.packet = p;
ret = USB_RET_ASYNC;
} else {
ret = len;
}
break;
default:
BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_IN: (0x%02X)", s.mode));
goto fail;
}
if (!was_status && (ret > 0)) usb_dump_packet(data, ret, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
break;
default:
BX_ERROR(("USB MSD handle_data: bad token"));
fail:
d.stall = 1;
ret = USB_RET_STALL;
break;
}
default:
BX_ERROR(("USB MSD handle_data: bad token"));
fail:
d.stall = 1;
ret = USB_RET_STALL;
break;
}
// must be uasp interface
} else if (aIface == MSD_PROTO_UASP) {
ret = uasp_handle_data(p);
} else {
BX_ERROR(("Unknown interface number: %d", aIface));
d.stall = 1;
ret = USB_RET_STALL;
}
return ret;
}
@ -943,52 +1224,60 @@ void usb_msd_device_c::usb_msd_command_complete(void *this_ptr, int reason, Bit3
void usb_msd_device_c::command_complete(int reason, Bit32u tag, Bit32u arg)
{
USBPacket *p = s.packet;
Bit8u aIface = get_aIface();
if (tag != s.tag) {
BX_ERROR(("usb-msd_command_complete: unexpected SCSI tag 0x%x", tag));
}
if (reason == SCSI_REASON_DONE) {
BX_DEBUG(("command complete %d", arg));
s.residue = s.data_len;
s.result = arg != 0;
if (s.packet) {
if ((s.data_len == 0) && (s.mode == USB_MSDM_DATAOUT)) {
send_status(p);
s.mode = USB_MSDM_CBW;
} else if (s.mode == USB_MSDM_CSW) {
send_status(p);
s.mode = USB_MSDM_CBW;
} else {
if (s.data_len) {
s.data_len -= s.usb_len;
if (s.mode == USB_MSDM_DATAIN)
memset(s.usb_buf, 0, s.usb_len);
s.usb_len = 0;
// if bulk/bulk/bulk
if (aIface == MSD_PROTO_BBB) {
if (tag != s.tag) {
BX_ERROR(("usb-msd_command_complete: unexpected SCSI tag 0x%x", tag));
}
if (reason == SCSI_REASON_DONE) {
BX_DEBUG(("command complete %d", arg));
s.residue = s.data_len;
s.result = arg != 0;
if (s.packet) {
if ((s.data_len == 0) && (s.mode == USB_MSDM_DATAOUT)) {
send_status(p);
s.mode = USB_MSDM_CBW;
} else if (s.mode == USB_MSDM_CSW) {
send_status(p);
s.mode = USB_MSDM_CBW;
} else {
if (s.data_len) {
s.data_len -= s.usb_len;
if (s.mode == USB_MSDM_DATAIN)
memset(s.usb_buf, 0, s.usb_len);
s.usb_len = 0;
}
if (s.data_len == 0)
s.mode = USB_MSDM_CSW;
}
if (s.data_len == 0)
s.mode = USB_MSDM_CSW;
}
s.packet = NULL;
usb_packet_complete(p);
} else if (s.data_len == 0) {
s.mode = USB_MSDM_CSW;
}
return;
}
s.scsi_len = arg;
s.scsi_buf = s.scsi_dev->scsi_get_buf(tag);
if (p) {
if ((s.scsi_len > 0) && (s.mode == USB_MSDM_DATAIN)) {
usb_dump_packet(s.scsi_buf, p->len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_BULK, false, true);
}
copy_data();
if (s.usb_len == 0) {
BX_DEBUG(("packet complete %p", p));
if (s.packet != NULL) {
s.packet = NULL;
usb_packet_complete(p);
} else if (s.data_len == 0) {
s.mode = USB_MSDM_CSW;
}
return;
}
s.scsi_len = arg;
s.scsi_buf = s.scsi_dev->scsi_get_buf(tag);
if (p) {
if ((s.scsi_len > 0) && (s.mode == USB_MSDM_DATAIN)) {
usb_dump_packet(s.scsi_buf, p->len, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
}
copy_data();
if (s.usb_len == 0) {
BX_DEBUG(("packet complete %p", p));
if (s.packet != NULL) {
s.packet = NULL;
usb_packet_complete(p);
}
}
}
// else if uasp
} else if (aIface == MSD_PROTO_UASP) {
uasp_command_complete(reason, tag, arg);
}
}

View File

@ -30,6 +30,36 @@ class device_image_t;
class cdrom_base_c;
class scsi_device_t;
#define UASP_MAX_STREAMS 5
#define UASP_MAX_STREAMS_N (1 << (UASP_MAX_STREAMS + 1))
// protocol
#define MSD_PROTO_BBB 0 // bulk only
#define MSD_PROTO_UASP 1 // UASP protocol (uses streams)
// BBB endpoint numbers (must remain consecutive)
#define MSD_BBB_DATAIN_EP 1
#define MSD_BBB_DATAOUT_EP 2
// UASP endpoint numbers (must remain consecutive)
#define MSD_UASP_COMMAND 1
#define MSD_UASP_STATUS 2
#define MSD_UASP_DATAIN 3
#define MSD_UASP_DATAOUT 4
typedef struct UASPRequest {
Bit32u mode; // current direction and other flags
Bit32u data_len; // count of bytes requested (from command requested)
Bit32u residue; // = data_len - actual_len
Bit32u scsi_len; //
Bit8u *scsi_buf; //
Bit32u usb_len; //
Bit8u *usb_buf; //
int result; //
Bit32u tag; // tag number from command ui
USBPacket *p; //
USBPacket *status; //
} UASPRequest;
class usb_msd_device_c : public usb_device_c {
public:
usb_msd_device_c(const char *devname);
@ -44,6 +74,7 @@ public:
virtual void handle_reset();
virtual int handle_control(int request, int value, int index, int length, Bit8u *data);
virtual int handle_data(USBPacket *p);
virtual void handle_iface_change(int iface);
virtual void register_state_specific(bx_list_c *parent);
virtual void cancel_packet(USBPacket *p);
bool set_inserted(bool value);
@ -60,6 +91,7 @@ private:
struct {
// members set in constructor / init
char *image_mode;
int proto; // MSD_PROTO_BBB (default), MSD_PROTO_UASP (uses streams)
device_image_t *hdimage;
cdrom_base_c *cdrom;
scsi_device_t *scsi_dev;
@ -84,11 +116,27 @@ private:
Bit8u *scsi_buf;
Bit8u *usb_buf;
USBPacket *packet;
// UASP (w/streams)
UASPRequest uasp_request[UASP_MAX_STREAMS_N];
} s;
static const char *cdrom_path_handler(bx_param_string_c *param, bool set,
const char *oldval, const char *val, int maxlen);
static Bit64s cdrom_status_handler(bx_param_c *param, bool set, Bit64s val);
int uasp_handle_data(USBPacket *p);
void uasp_initialize_request(int tag);
UASPRequest *uasp_find_request(Bit32u tag);
Bit32u get_data_len(const struct S_UASP_INPUT *input, Bit8u *buf);
const struct S_UASP_INPUT *uasp_get_info(Bit8u command, Bit8u serv_action);
int uasp_do_stall(UASPRequest *req);
int uasp_do_command(USBPacket *p);
int uasp_process_request(USBPacket *p, int index);
int uasp_do_data(UASPRequest *req, USBPacket *p);
int uasp_do_ready(UASPRequest *req, USBPacket *p);
int uasp_do_status(UASPRequest *req, USBPacket *p);
void uasp_copy_data(UASPRequest *req);
void uasp_command_complete(int reason, Bit32u tag, Bit32u arg);
};
#endif

View File

@ -216,6 +216,7 @@ bool usb_printer_device_c::init()
} else {
BX_ERROR(("USB printer: missing output file"));
}
d.alt_iface_max = 0;
return 0;
}

512
bochs/iodev/usb/usb_uasp.cc Normal file
View File

@ -0,0 +1,512 @@
/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
// Experimental USB Attached SCSI Protocol support (UASP)
//
// Copyright (C) 2023 Benjamin D Lunt (fys [at] fysnet [dot] net)
// Copyright (C) 2023 The Bochs Project
//
// 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
/////////////////////////////////////////////////////////////////////////
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE
#include "iodev.h"
#if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
#include "usb_common.h"
#include "hdimage/cdrom.h"
#include "hdimage/hdimage.h"
#include "scsi_device.h"
#include "usb_msd.h"
#define LOG_THIS
// Information Unit Types
// byte[0] in uasp command and status????
#define IU_CMD 1
#define IU_SENSE 3
#define IU_RESP 4
#define IU_TMF 5
#define IU_RRDY 6
#define IU_WRDY 7
#define IU_CMD_LEN 32
#define IU_SENSE_LEN 16
#define IU_RESP_LEN 8
#define IU_TMF_LEN 16
#define IU_RRDY_LEN 4
#define IU_WRDY_LEN 4
// Task Attribute
#define UASP_TASK_SIMPLE 0
#define UASP_TASK_HOQ 1
#define UASP_TASK_ORDERED 2
#define UASP_TASK_ACA 4
#define UASP_GET_ACTIVE(m) (((m) & 1) > 0) // is the request active?
#define UASP_SET_ACTIVE(m) ((m) > 0) // clearing/setting active needs to clear everything else
#define UASP_GET_STATUS(m) (((m) & 2) > 0) // have we sent the RRIU or WRIU?
#define UASP_SET_STATUS(m) ((m) | 2) // set that we have sent the RRIU or WRIU
#define UASP_GET_CMND(m) (((m) & 4) > 0) // have we received and processed the command yet?
#define UASP_SET_CMND(m) ((m) | 4) // set that we have received and processed the command
#define UASP_GET_COMPLETE(m) (((m) & 8) > 0) // is the datain/dataout transfer complete?
#define UASP_SET_COMPLETE(m) ((m) | 8) // set that the datain/dataout transfer is complete
#define UASP_GET_DIR(m) (((m) & 0x0000FF00) >> 8)
#define UASP_SET_DIR(m,d) (((m) & ~0x0000FF00) | ((d) << 8))
struct S_UASP_COMMAND {
Bit8u id; // Information Unit Type
Bit8u rsvd0; //
Bit16u tag; // big endian
Bit8u prio_attr; // Task Attribute
Bit8u rsvd1; //
Bit8u len; // len (if more than 16 bytes) (i.e.: if command len is 20 bytes, this field is 4) (bottom 2 bits are reserved)
Bit8u rsvd2; //
Bit64u lun; // eight byte lun
Bit8u com_block[16]; // command block
};
struct S_UASP_STATUS {
Bit8u id; // usually IU_SENSE ?
Bit8u rsvd0;
Bit16u tag; // big endian
Bit16u stat_qual; // big endian
Bit8u status;
Bit8u resv1[7];
Bit16u len; // big endian
Bit8u sense[18];
};
#define UASP_FROM_COMMAND ((Bit32u) -1)
#define U_NONE 0
#define U_SRV_ACT (1<<0)
#define U_IS_LBA (1<<1)
struct S_UASP_INPUT {
Bit8u command; // command byte
Bit8u serv_action; // command/service action
Bit8u cmd_len; // length of the command buf
Bit8u direction; // in or out
Bit8u flags; // see S_UASP_FLAGS_*
Bit32u data_len; // if the command is fixed at a count of bytes, this holds that count (next two members are ignored)
int offset; // byte offset of data length requested
int size; // size in bytes of this field
};
int usb_msd_device_c::uasp_handle_data(USBPacket *p)
{
int ret = 0;
Bit8u *data = p->data;
int len = p->len;
int index = p->strm_pid; // high-speed device will be zero. super-speed device will have the stream id.
BX_INFO(("uasp_handle_data(): %X ep=%d index=%d len=%d", p->pid, p->devep, index, len));
switch (p->pid) {
case USB_TOKEN_OUT:
if (p->devep == MSD_UASP_DATAOUT) { // Bulk out pipe
if ((index < 0) || (index > UASP_MAX_STREAMS_N))
goto fail;
ret = uasp_process_request(p, index);
if (ret == USB_RET_ASYNC)
usb_defer_packet(p, this);
} else if (p->devep == MSD_UASP_COMMAND) { // Command pipe
ret = uasp_do_command(p);
}
break;
case USB_TOKEN_IN:
// TODO: these are the same
if ((p->devep == MSD_UASP_DATAIN) || // Bulk in pipe
(p->devep == MSD_UASP_STATUS)) { // Status pipe
if ((index < 0) || (index > UASP_MAX_STREAMS_N))
goto fail;
ret = uasp_process_request(p, index);
if (ret == USB_RET_ASYNC)
usb_defer_packet(p, this);
}
break;
default:
BX_ERROR(("USB MSD (UASP) handle_data: bad token"));
fail:
d.stall = 1;
ret = USB_RET_STALL;
}
return ret;
}
void usb_msd_device_c::uasp_initialize_request(int index)
{
UASPRequest *req = &s.uasp_request[index];
req->mode = UASP_SET_ACTIVE(1); // setting to 1 clears other flags
req->data_len = 0;
req->result = 0;
req->tag = 0;
req->scsi_len = 0;
req->status = NULL;
req->p = NULL;
d.stall = 0;
}
UASPRequest *usb_msd_device_c::uasp_find_request(Bit32u tag)
{
int i;
for (i=0; i<UASP_MAX_STREAMS_N; i++) {
if (UASP_GET_ACTIVE(s.uasp_request[i].mode) && (s.uasp_request[i].tag == tag))
return &s.uasp_request[i];
}
return NULL;
}
const struct S_UASP_INPUT bx_uasp_info_array[] = {
// cmd, serv, cmd_len, direction, flags, data_len, (offset, size)
// SPC-4 commands
{ 0x12, 0x00, 6, USB_TOKEN_IN, U_NONE, UASP_FROM_COMMAND, 3, 2 }, // INQUIRY
{ 0x15, 0x00, 6, USB_TOKEN_OUT, U_NONE, UASP_FROM_COMMAND, 4, 1 }, // MODE_SELECT_6
{ 0x55, 0x00, 10, USB_TOKEN_OUT, U_NONE, UASP_FROM_COMMAND, 7, 2 }, // MODE_SELECT_10
{ 0x1A, 0x00, 6, USB_TOKEN_IN, U_NONE, UASP_FROM_COMMAND, 4, 1 }, // MODE_SENSE_6
{ 0x5A, 0x00, 10, USB_TOKEN_IN, U_NONE, UASP_FROM_COMMAND, 7, 2 }, // MODE_SENSE_10
{ 0xA0, 0x00, 12, USB_TOKEN_IN, U_NONE, UASP_FROM_COMMAND, 6, 4 }, // REPORT LUNS
{ 0x03, 0x00, 6, USB_TOKEN_IN, U_NONE, UASP_FROM_COMMAND, 4, 1 }, // REQUEST_SENSE
{ 0x1D, 0x00, 6, USB_TOKEN_OUT, U_NONE, UASP_FROM_COMMAND, 3, 2 }, // SEND_DIAGNOSTIC
{ 0x00, 0x00, 6, 0, U_NONE, 0, }, // TEST_UNIT_READY
// SBC-3 commands
{ 0x08, 0x00, 6, USB_TOKEN_IN, U_IS_LBA, UASP_FROM_COMMAND, 2, 2 }, // READ_6
{ 0x28, 0x00, 10, USB_TOKEN_IN, U_IS_LBA, UASP_FROM_COMMAND, 7, 2 }, // READ_10
{ 0xA8, 0x00, 12, USB_TOKEN_IN, U_IS_LBA, UASP_FROM_COMMAND, 6, 4 }, // READ_12
{ 0x88, 0x00, 16, USB_TOKEN_IN, U_IS_LBA, UASP_FROM_COMMAND, 10, 4 }, // READ_16
{ 0x0A, 0x00, 6, USB_TOKEN_OUT, U_IS_LBA, UASP_FROM_COMMAND, 2, 2 }, // WRITE_6
{ 0x2A, 0x00, 10, USB_TOKEN_OUT, U_IS_LBA, UASP_FROM_COMMAND, 7, 2 }, // WRITE_10
{ 0xAA, 0x00, 12, USB_TOKEN_OUT, U_IS_LBA, UASP_FROM_COMMAND, 6, 4 }, // WRITE_12
{ 0x8A, 0x00, 16, USB_TOKEN_IN, U_IS_LBA, UASP_FROM_COMMAND, 10, 4 }, // WRITE_16
{ 0x1E, 0x00, 6, 0, U_NONE, 0, }, // PREVENT_ALLOW_MEDIUM_REMOVAL
{ 0x25, 0x00, 10, USB_TOKEN_IN, U_NONE, 8, }, // READ_CAPACITY_10
{ 0x9E, 0x10, 16, USB_TOKEN_IN, U_SRV_ACT, UASP_FROM_COMMAND, 10, 4 }, // READ_CAPACITY_16
{ 0x1B, 0x00, 6, 0, U_NONE, 0, }, // START_STOP_UNIT
{ 0x2F, 0x00, 10, 0, U_NONE, 0, }, // VERIFY_10
{ 0xAF, 0x00, 12, 0, U_NONE, 0, }, // VERIFY_12
{ 0x8F, 0x00, 16, 0, U_NONE, 0, }, // VERIFY_16
{ 0x04, 0x00, 6, 0, U_NONE, 0, }, // FORMAT_UNIT
{ 0x35, 0x00, 10, 0, U_NONE, 0, }, // SYNCHRONIZE CACHE_10
{ 0x91, 0x00, 16, 0, U_NONE, 0, }, // SYNCHRONIZE CACHE_16
// end marker
{ 0xFF, }
};
Bit32u usb_msd_device_c::get_data_len(const struct S_UASP_INPUT *input, Bit8u *buf)
{
Bit32u ret = 0;
// the input offset and size fields point to the commands "data_len" field
switch (input->size) {
case 1: // byte
ret = * (Bit8u *) &buf[input->offset];
break;
case 2: // word
ret = * (Bit16u *) &buf[input->offset];
ret = bx_bswap16(ret);
break;
case 4: // dword
ret = * (Bit32u *) &buf[input->offset];
ret = bx_bswap32(ret);
break;
}
// is lba instead of bytes?
if (input->flags & U_IS_LBA)
ret *= 512; // TODO: get size of a sector/block
return ret;
}
// get information about the command requested
const struct S_UASP_INPUT *usb_msd_device_c::uasp_get_info(Bit8u command, Bit8u serv_action)
{
int i = 0;
do {
if (bx_uasp_info_array[i].command == command) {
if (bx_uasp_info_array[i].flags & U_SRV_ACT) {
if (bx_uasp_info_array[i].serv_action == serv_action)
return &bx_uasp_info_array[i];
} else
return &bx_uasp_info_array[i];
}
i++;
} while (bx_uasp_info_array[i].command != 0xFF);
BX_ERROR(("uasp_get_info: Unknown command found: %02X/%02X", command, serv_action));
return NULL;
}
int usb_msd_device_c::uasp_do_stall(UASPRequest *req)
{
USBPacket *p;
p = req->p;
if (p) {
req->p = NULL;
p->len = USB_RET_STALL;
usb_packet_complete(p);
}
p = req->status;
if (p) {
req->status = NULL;
p->len = USB_RET_STALL;
usb_packet_complete(p);
}
req->mode = UASP_SET_ACTIVE(0);
d.stall = 1;
return USB_RET_STALL;
}
int usb_msd_device_c::uasp_do_command(USBPacket *p)
{
struct S_UASP_COMMAND *ui = (struct S_UASP_COMMAND *) p->data;
const struct S_UASP_INPUT *input;
Bit8u lun = (Bit8u) (ui->lun >> 56);
int index = (get_speed() == USB_SPEED_HIGH) ? 0 : bx_bswap16(ui->tag);
UASPRequest *req = &s.uasp_request[index];
usb_dump_packet(p->data, p->len, 0, p->devaddr, USB_DIR_OUT | p->devep, USB_TRANS_TYPE_BULK, false, true);
if (ui->id == IU_CMD) {
if ((ui->prio_attr & 0x7) == UASP_TASK_SIMPLE) {
if (!UASP_GET_ACTIVE(req->mode))
uasp_initialize_request(index);
// get information about the command requested
input = uasp_get_info(ui->com_block[0], ui->com_block[1] & 0x1F);
// if unknown command, stall
if (input == NULL)
return uasp_do_stall(req);
// get the count of bytes requested, command length, etc.
req->tag = bx_bswap16(ui->tag);
req->mode = UASP_SET_DIR(req->mode, input->direction);
req->data_len = (input->data_len == UASP_FROM_COMMAND) ? get_data_len(input, ui->com_block) : input->data_len;
BX_INFO(("uasp command id %d, tag 0x%04X, command 0x%X, len = %d, data_len = %d", ui->id, req->tag, ui->com_block[0], p->len, req->data_len));
s.scsi_dev->scsi_send_command(req->tag, ui->com_block, input->cmd_len, lun, d.async_mode);
if (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) {
s.scsi_dev->scsi_read_data(req->tag);
} else if (UASP_GET_DIR(req->mode) == USB_TOKEN_OUT) {
s.scsi_dev->scsi_write_data(req->tag);
}
// if a high-speed device, we need to send the status ready ui
if ((get_speed() == USB_SPEED_HIGH) && req->status) {
USBPacket *s = req->status;
s->len = uasp_do_ready(req, s);
req->status = NULL;
usb_packet_complete(s);
}
req->mode = UASP_SET_CMND(req->mode); // mark that we have sent the command
return p->len;
} else
BX_ERROR(("uasp: unknown/unsupported task attribute. %d", (ui->prio_attr & 0x7)));
} else
BX_ERROR(("uasp: unknown id on command pipe. %d", ui->id));
return 0;
}
// if we have already done the command and there is data
// waiting, we can process the packet request.
// else ret USB_RET_ASYNC
int usb_msd_device_c::uasp_process_request(USBPacket *p, int index)
{
UASPRequest *req = &s.uasp_request[index];
int len = p->len;
if (!UASP_GET_ACTIVE(req->mode))
uasp_initialize_request(index);
// if it is the status pipe
if (p->devep == MSD_UASP_STATUS) {
// TODO: check to see if the direction is in
if (UASP_GET_COMPLETE(req->mode)) {
return uasp_do_status(req, p);
} else {
if ((get_speed() == USB_SPEED_HIGH) && UASP_GET_CMND(req->mode) && !UASP_GET_STATUS(req->mode)) {
return uasp_do_ready(req, p);
} else {
req->status = p;
return USB_RET_ASYNC;
}
}
}
// have we received the Command yet?
if (!UASP_GET_CMND(req->mode)) {
req->p = p;
return USB_RET_ASYNC;
}
// check to make sure the direction is correct
if (p->pid != UASP_GET_DIR(req->mode)) {
BX_INFO(("Found packet with wrong direction."));
uasp_do_stall(req);
}
// do the transfer for this packet
len = uasp_do_data(req, p);
BX_INFO(("uasp: (0) data: transferred %d bytes", len));
return len;
}
int usb_msd_device_c::uasp_do_data(UASPRequest *req, USBPacket *p)
{
int len = p->len;
// TODO: if (dir != req->dir) error
if (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) {
BX_INFO(("data in %d/%d/%d", len, req->data_len, req->scsi_len));
} else if (UASP_GET_DIR(req->mode) == USB_TOKEN_OUT) {
BX_INFO(("data out %d/%d/%d", len, req->data_len, req->scsi_len));
}
if (len > (int) req->data_len)
len = req->data_len;
req->usb_buf = p->data;
req->usb_len = len;
while (req->usb_len && req->scsi_len) {
uasp_copy_data(req);
}
if (req->residue && req->usb_len) {
req->data_len -= req->usb_len;
memset(req->usb_buf, 0, req->usb_len);
req->usb_len = 0;
}
usb_dump_packet(p->data, len, 0, p->devaddr, ((UASP_GET_DIR(req->mode) == USB_TOKEN_IN) ? USB_DIR_IN : USB_DIR_OUT) | p->devep, USB_TRANS_TYPE_BULK, false, true);
return len;
}
int usb_msd_device_c::uasp_do_ready(UASPRequest *req, USBPacket *p)
{
struct S_UASP_STATUS *status;
// do the RRIU or WRIU
status = (struct S_UASP_STATUS *) p->data;
status->id = (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) ? IU_RRDY : IU_WRDY;
status->rsvd0 = 0;
status->tag = bx_bswap16((Bit16u) req->tag);
usb_dump_packet(p->data, IU_RRDY_LEN, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
req->mode = UASP_SET_STATUS(req->mode);
return IU_RRDY_LEN;
}
int usb_msd_device_c::uasp_do_status(UASPRequest *req, USBPacket *p)
{
struct S_UASP_STATUS *status;
// do the status
BX_INFO(("uasp: Sending Status:"));
status = (struct S_UASP_STATUS *) p->data;
memset(status, 0, 16);
status->id = IU_SENSE;
status->tag = bx_bswap16((Bit16u) req->tag);
status->status = 0; // good return
status->len = bx_bswap16(0); // no sense data
usb_dump_packet(p->data, IU_SENSE_LEN, 0, p->devaddr, USB_DIR_IN | p->devep, USB_TRANS_TYPE_BULK, false, true);
req->mode = UASP_SET_ACTIVE(0);
// return the size of the status block
return IU_SENSE_LEN;
}
void usb_msd_device_c::uasp_copy_data(UASPRequest *req)
{
Bit32u len = req->usb_len;
if (len > req->scsi_len)
len = req->scsi_len;
if (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) {
memcpy(req->usb_buf, req->scsi_buf, len);
} else {
memcpy(req->scsi_buf, req->usb_buf, len);
}
req->usb_len -= len;
req->scsi_len -= len;
req->usb_buf += len;
req->scsi_buf += len;
req->data_len -= len;
if (req->scsi_len == 0) {
if (UASP_GET_DIR(req->mode) == USB_TOKEN_IN) {
s.scsi_dev->scsi_read_data(req->tag);
} else {
s.scsi_dev->scsi_write_data(req->tag);
}
}
}
void usb_msd_device_c::uasp_command_complete(int reason, Bit32u tag, Bit32u arg)
{
USBPacket *p;
UASPRequest *req = uasp_find_request(tag);
BX_INFO(("uasp_command_complete: reason %d, arg %d, tag 0x%04X", reason, arg, tag));
if (req == NULL)
return;
if (reason == SCSI_REASON_DONE) {
req->residue = req->data_len;
req->result = arg != 0;
req->mode = UASP_SET_COMPLETE(req->mode); // mark that are transfer is complete
// do the status
p = req->status;
if (p) {
p->len = uasp_do_status(req, p);
BX_INFO(("uasp: status: transferred %d bytes (residue = %d)", p->len, req->residue));
req->status = NULL;
usb_packet_complete(p);
}
return;
}
// reason == SCSI_REASON_DATA
req->scsi_len = arg;
req->scsi_buf = s.scsi_dev->scsi_get_buf(tag);
p = req->p;
if (p) {
p->len = uasp_do_data(req, p);
BX_INFO(("uasp: (1) data: transferred %d bytes", p->len));
//if (req->usb_len == 0) {
BX_DEBUG(("packet complete 0x%p", p));
req->p = NULL;
usb_packet_complete(p);
//}
}
}
#endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB

File diff suppressed because it is too large Load Diff

View File

@ -45,19 +45,30 @@
#define OPS_REGS_OFFSET 0x20
// Change this to 0.95, 0.96, 1.00, 1.10, according to the desired effects (LINK chain bit, etc)
// (the NEC/Renesas uPD720202 is v1.00. If you change this version, and use a NEC/Renesas specific,
// (the NEC/Renesas uPD720202 is v1.00. If you change this version and use a NEC/Renesas specific,
// driver the emulation may have undefined results)
// (also, the emulation for v1.10 is untested. I don't have a test-system that uses that version)
#define VERSION_MAJOR 0x01
#define VERSION_MINOR 0x10
#define VERSION_MINOR 0x00
// HCSPARAMS1
#define MAX_SLOTS 32 // (1 based)
#define INTERRUPTERS 8 //
#define USB_XHCI_PORTS 4 // Port Registers, each supporting USB3 or USB2 (0x08 = uPD720201, 0x04 = uPD720202)
#if (USB_XHCI_PORTS != 4)
#error "USB_XHCI_PORTS must equal 4"
// Each controller supports its own number of ports.
// Note: USB_XHCI_PORTS should be defined as twice the amount of sockets wanted.
// ie.: Typically each physical port (socket) has two defined port register sets. One for USB3, one for USB2.
// Only one paired port type may be used at a time.
// If we support two sockets, we need four ports. (In our emulation) the first half will be USB3, the last half will be USB2.
// With a count of four, if Port1 is used, then Port3 must be vacant. If Port2 is used, then Port4 must be vacant.
// The uPD720202 supports 2 sockets (4 port register sets).
// The uPD720201 supports 4 sockets (8 port register sets).
// You can change USB_XHCI_PORTS below to another value (must be an even number).
#define USB_XHCI_PORTS 4 // Default number of port Registers, each supporting USB3 or USB2 (0x08 = uPD720201, 0x04 = uPD720202)
#define USB_XHCI_PORTS_MAX 10 // we don't support more than this many ports
#if ((USB_XHCI_PORTS < 2) || (USB_XHCI_PORTS > USB_XHCI_PORTS_MAX) || ((USB_XHCI_PORTS & 1) == 1))
#error "USB_XHCI_PORTS must be at least 2 and no more than USB_XHCI_PORTS_MAX and must be an even number."
#endif
// HCSPARAMS2
@ -83,10 +94,16 @@
#define SEC_DOMAIN_BAND 1 // version 0.96 and below only (MUST BE 1 in v1.00+)
#define STOPPED_EDTLA 0
#define CONT_FRAME_ID 0
#define MAX_PSA_SIZE 0x05
#define MAX_PSA_SIZE 0x05 // 2^(5+1) = 63 Primary Streams (first one is reserved)
#define MAX_PSA_SIZE_NUM (1 << (MAX_PSA_SIZE + 1))
#define EXT_CAPS_OFFSET 0x500
#define EXT_CAPS_SIZE 144
// doorbell masks
#define PSA_MAX_SIZE_NUM(m) (1UL << ((m) + 1))
#define PSA_PRIMARY_MASK(d, m) (((d) >> 16) & ((1 << ((m) + 1)) - 1))
#define PSA_SECONDARY_MASK(d, m) (((d) >> 16) >> ((m) + 1))
// HCCPARAMS2 (v1.10+)
#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
#define U3_ENTRY_CAP 0
@ -141,16 +158,6 @@
# error "NO_SSD_SUPPORT must be used with in version 1.10 and above"
#endif
// Each controller supports its own number of ports. We must adhere to that for now.
// (The Extended Capabilities register set is hardcoded for this as of now.)
// As long as BX_N_USB_XHCI_PORTS was defined as greater than what we have now, then
// we are fine.
// Note: BX_N_USB_XHCI_PORTS should have been defined as twice the amount of ports wanted.
// ie.: Each physical port (socket) has two defined port register sets. One for USB3, one for USB2
// Only one port type may be used at a time. Socket0 or Socket1, not both. If Socket0 is used, then
// Socket1 must be vacant.
#define BX_N_USB_XHCI_PORTS 4
// xHCI speed values
#define XHCI_SPEED_FULL 1
#define XHCI_SPEED_LOW 2
@ -160,6 +167,26 @@
#define USB2 0
#define USB3 1
// Port Status Change Bits
#define PSCEG_CSC (1<<0)
#define PSCEG_PEC (1<<1)
#define PSCEG_WRC (1<<2)
#define PSCEG_OCC (1<<3)
#define PSCEG_PRC (1<<4)
#define PSCEG_PLC (1<<5)
#define PSCEG_CEC (1<<6)
// Extended Capabilities: Protocol
struct XHCI_PROTOCOL {
Bit8u id;
Bit8u next;
Bit16u version;
Bit8u name[4];
Bit8u start_index;
Bit8u count;
Bit16u flags;
};
// our saved ring members
struct RING_MEMBERS {
struct {
@ -213,6 +240,16 @@ struct EP_CONTEXT {
unsigned average_trb_len;
};
struct STREAM_CONTEXT {
bool valid; // is this context valid
Bit64u tr_dequeue_pointer;
bool dcs;
int sct;
#if ((VERSION_MAJOR == 1) && (VERSION_MINOR >= 0x10))
Bit32u stopped_EDTLA;
#endif
};
struct HC_SLOT_CONTEXT {
bool enabled;
struct SLOT_CONTEXT slot_context;
@ -224,6 +261,7 @@ struct HC_SLOT_CONTEXT {
bool rcs;
bool retry;
int retry_counter;
struct STREAM_CONTEXT stream[MAX_PSA_SIZE_NUM]; // first one is reserved
} ep_context[32]; // first one is ignored by controller.
};
@ -322,7 +360,14 @@ struct TRB {
Bit32u command;
};
enum {
XHCI_HC_uPD720202, // Renesas/NEC uPD720202 (2 sockets) (default)
XHCI_HC_uPD720201 // Renesas/NEC uPD720201 (4 sockets)
};
typedef struct {
Bit32u HostController;
unsigned int n_ports;
struct XHCI_CAP_REGS {
Bit32u HcCapLength;
@ -419,7 +464,7 @@ typedef struct {
usb_device_c *device; // device connected to this port
bool is_usb3; // set if usb3 port, cleared if usb2 port.
bool has_been_reset; // set if the port has been reset aftet powered up.
bool needs_psce; // needs a port status change event on port powered.
Bit8u psceg; // current port status change event
struct {
bool wpr; // 1 bit Warm Port Reset = 0b RW or RsvdZ
@ -483,7 +528,7 @@ typedef struct {
Bit8u hirdd; // 4 bit host initiated resume duration deep
Bit32u RsvdP; // 18 bit reserved and preseved = 0x00000000 RW
} porthlpmc;
} usb_port[USB_XHCI_PORTS];
} usb_port[USB_XHCI_PORTS_MAX];
// Extended Caps Registers
Bit8u extended_caps[EXT_CAPS_SIZE];
@ -523,6 +568,13 @@ typedef struct {
struct HC_SLOT_CONTEXT slots[MAX_SLOTS]; // first one is ignored by controller.
struct RING_MEMBERS ring_members;
// filled at runtime with ex: { USB3, USB3, USB2, USB2 };
//Bit8u port_speed_allowed[USB_XHCI_PORTS_MAX];
// four speeds of: 'reserved' + a port count of bytes rounded up to and 8 byte size (ie: 8, 16, 24, 32 bytes each speed)
Bit8u port_band_width[4 * ((1 + USB_XHCI_PORTS_MAX) + 8)]; // + 8 gives us ample room for a boundary of 8-byte entries per speed
// the port's paired port num. i.e., with 4 ports, 1 is paired with 3, 2 is paired with 4
int paired_portnum[USB_XHCI_PORTS_MAX];
} bx_usb_xhci_t;
// Version 3.0.23.0 of the Renesas uPD720202 driver, even though the card is
@ -572,11 +624,14 @@ private:
static int broadcast_speed(const int slot);
static int broadcast_packet(USBPacket *p, const int port);
static Bit8u get_psceg(const int port);
static void xhci_timer_handler(void *);
void xhci_timer(void);
static void process_transfer_ring(const int slot, const int ep);
static Bit64u process_transfer_ring(const int slot, const int ep, Bit64u ring_addr, bool *rcs, const int primary_sid);
static void process_command_ring(void);
static void get_stream_info(struct STREAM_CONTEXT *context, const Bit64u address, const int index);
static void put_stream_info(struct STREAM_CONTEXT *context, const Bit64u address, const int index);
static void write_event_TRB(const unsigned interrupter, const Bit64u parameter, const Bit32u status,
const Bit32u command, const bool fire_int);
static Bit32u NEC_verification(const Bit64u parameter);
@ -587,16 +642,19 @@ private:
static void update_ep_context(const int slot, const int ep);
static void dump_slot_context(const Bit32u *context, const int slot);
static void dump_ep_context(const Bit32u *context, const int slot, const int ep);
static void dump_stream_context(const Bit32u *context);
static void copy_slot_from_buffer(struct SLOT_CONTEXT *slot_context, const Bit8u *buffer);
static void copy_ep_from_buffer(struct EP_CONTEXT *ep_context, const Bit8u *buffer);
static void copy_slot_to_buffer(Bit32u *buffer, const int slot);
static void copy_ep_to_buffer(Bit32u *buffer, const int slot, const int ep);
static int validate_slot_context(const struct SLOT_CONTEXT *slot_context);
static void copy_stream_from_buffer(struct STREAM_CONTEXT *context, const Bit8u *buffer);
static void copy_stream_to_buffer(Bit8u *buffer, const struct STREAM_CONTEXT *context);
static int validate_slot_context(const struct SLOT_CONTEXT *slot_context, const int trb_command, const int slot);
static int validate_ep_context(const struct EP_CONTEXT *ep_context, const int trb_command, const Bit32u a_flags, int port_num, int ep_num);
static int create_unique_address(const int slot);
static int send_set_address(const int addr, const int port_num, const int slot);
static void dump_xhci_core(const int slots, const int eps);
static void dump_xhci_core(const unsigned int slots, const unsigned int eps);
#if BX_USE_USB_XHCI_SMF
static bool read_handler(bx_phy_address addr, unsigned len, void *data, void *param);