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:
parent
86b50988be
commit
7041926aac
@ -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:
|
||||
|
@ -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));
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
512
bochs/iodev/usb/usb_uasp.cc
Normal 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
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user