Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usb compliance #91

Merged
merged 8 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions bochs/CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,50 @@ We welcome every new contributor !
- Apply standard CPPFLAGS from environment in all makefiles
- Fixed and improved PCI slot config error handling
- Updated Bochs instrumentation examples for new disassembler introduced in Bochs 2.7 release
- USB: SCSI: added the Event Status command (0x4a).
- USB: SCSI: added BX_DEBUG checks to the command sent by the Guest.
- USB: SCSI: added the block_size in the Read Capacity command (0x25).
- USB: SCSI: added some 0x9E/xx commands. ReadCapacity(16).
- USB: UHCI: re-wrote the uhci stack to better support control/bulk reclamation.
- USB: UHCI: check that a reset doesn't clear the CSC bit during a reset, but should after the reset is complete.
- USB: added the ability to change interfaces (ex: BBB to UASP).
- USB: added the Toggle bit check to all controllers/devices. (optional with a #define)
- USB: added the ability to trigger an over-current to all controllers.
- USB: added numberous BX_DEBUG checks to the USB_COMMON emulation to help show bugs in a Guest's device driver.
- USB: added BX_DEBUG if the first requested packet after a reset is not 8 bytes.
- USB: added a BX_DEBUG check to make sure the speed indicator is correct within TDs.
- USB: added BX_DEBUG checks to all request lengths, max packet size, and other length checks.
- USB: return default speed of device/controller combination.
- USB: check that the bochsrc file doesn't specify two different devices for the same port.
- USB: check that the bochsrc file has a super-speed device defined on a correct port number.
- USB: re-wrote how the USB_COMMON passed on packets to allow for zero-length packets to be accepted.
- USB: HID: added multiple mouse modes to have different HID Reports, including a physical report, and an irregular report.
- USB: HID: added the Boot Protocol function.
- USB: HUB: fixed/Added a more accurate DeviceRemovable emulation.
- USB: HUB: Allow USB 1.0 or USB 1.1 emulation (there is a difference).
- USB: HUB: Add bit 0 function to the returned report (change status bit).
- USB: HUB: Some Guests think a NAK on the Interrupt EP is an error. (option to ignore this)
- USB: Floppy: Fixed the CBI/CB configuration descriptor.
- USB: fixed/added USB 2.0 Compliance to all devices. Hub still needs a few additions.
- Floppies (using the CB/CBI protocol) can only be full-speed.
- Fixed the Endpoint Clear Feature request (halted, etc.).
- Added the Endpoint Get Status request to each device.
- Hub power switching emulation.
- Fixed the return/request error on Device Qualifier requests.
- Fixed Device Qualifier and Other Speed requests.
- USB: MSD: fixed USB 2.0 only descriptor emulation. (Device Qualifier, etc.)
- USB: OHCI: fixed the toggle emulation.
- USB: added the ability to catch 0xEE descriptors (Microsoft specific).
- USB: xHCI: added the ability to have more than one model of xHCI hardware. Currently there are two.
- USB: xHCI: added the ability to indicate the port count.
- USB: xHCI: check the USB2 and USB3 port assignments.
- USB: xHCI: added experimental Stream emulation.
- USB: added experimental MSD UASP emulation.
- USB: xHCI: added the (vendor) Dump Controller command (Specific to Bochs).
- USB: xHCI: added checks to the Evaluate Context and Address Device commands.
- USB: xHCI: fixed/updated the Port Status Change Event emulation.
- USB: xHCI: fixed/updated the port bandwidth emulation.
- USB: Many documentation additions.
- Documentation fixes

-------------------------------------------------------------------------
Expand Down
8 changes: 5 additions & 3 deletions bochs/iodev/usb/uhci_core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -550,9 +550,9 @@ void bx_uhci_core_c::write(Bit32u address, Bit32u value, unsigned io_len)
if (value & (1<<11)) hub.usb_port[port].over_current_change = 0;
hub.usb_port[port].reset = (value & (1<<9)) ? 1 : 0;
hub.usb_port[port].resume = (value & (1<<6)) ? 1 : 0;
if (!hub.usb_port[port].enabled && (value & (1<<2)))
if (!hub.usb_port[port].enabled && (value & (1<<2))) {
hub.usb_port[port].enable_changed = 0;
else
} else
if (value & (1<<3)) hub.usb_port[port].enable_changed = 0;
hub.usb_port[port].enabled = (value & (1<<2)) ? 1 : 0;
if (value & (1<<1)) hub.usb_port[port].connect_changed = 0;
Expand Down Expand Up @@ -908,7 +908,7 @@ bool bx_uhci_core_c::DoTransfer(Bit32u address, struct TD *td) {
}

BX_DEBUG(("TD found at address 0x%08X: 0x%08X 0x%08X 0x%08X 0x%08X", address, td->dword0, td->dword1, td->dword2, td->dword3));

// check TD to make sure it is valid
// A max length 0x500 to 0x77E is illegal
if ((maxlen >= 0x500) && (maxlen != 0x7FF)) {
Expand Down Expand Up @@ -1141,6 +1141,8 @@ void bx_uhci_core_c::set_port_device(int port, usb_device_c *dev)
{
usb_device_c *olddev = hub.usb_port[port].device;
if ((dev != NULL) && (olddev == NULL)) {
// make sure we are calling the correct handler for the device
dev->set_event_handler(this, uhci_event_handler, port);
hub.usb_port[port].device = dev;
set_connect_status(port, 1);
} else if ((dev == NULL) && (olddev != NULL)) {
Expand Down
40 changes: 34 additions & 6 deletions bochs/iodev/usb/usb_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ int usb_device_c::handle_packet(USBPacket *p)
break;

default:
BX_ERROR(("Unknown Data state while finding Control In Packet."));
BX_ERROR(("Unknown Data state while finding Control In Packet. (state = %i)", d.setup_state));
goto fail;
}
break;
Expand Down Expand Up @@ -550,7 +550,7 @@ int usb_device_c::handle_packet(USBPacket *p)
break;

default:
BX_ERROR(("Unknown Data state while finding Control Out Packet."));
BX_ERROR(("Unknown Data state while finding Control Out Packet. (state = %i)", d.setup_state));
goto fail;
}
break;
Expand Down Expand Up @@ -695,6 +695,11 @@ int usb_device_c::handle_control_common(int request, int value, int index, int l
}
d.config = value;
d.state = USB_STATE_CONFIGURED;
#if HANDLE_TOGGLE_CONTROL
// we must also clear all of the EP toggle bits
for (int i=0; i<USB_MAX_ENDPOINTS; i++)
set_toggle(i, 0);
#endif
ret = 0;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
Expand Down Expand Up @@ -752,13 +757,36 @@ int usb_device_c::handle_control_common(int request, int value, int index, int l
}
break;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
BX_DEBUG(("EndpointOutRequest | USB_REQ_CLEAR_FEATURE:"));
BX_DEBUG(("EndpointOutRequest | USB_REQ_CLEAR_FEATURE: ep = %d", index & 0x7F));
// Value == 0 == Endpoint Halt (the Guest wants to reset the endpoint)
if (value == 0) { /* clear ep halt */
if (value == USB_ENDPOINT_HALT) {
if ((index & 0x7F) < USB_MAX_ENDPOINTS) {
#if HANDLE_TOGGLE_CONTROL
set_toggle(index & 0x7F, 0);
set_toggle(index, 0);
#endif
ret = 0;
set_halted(index, 0);
ret = 0;
} else {
BX_ERROR(("EndpointOutRequest | USB_REQ_CLEAR_FEATURE: index > ep count: %d", index));
}
} else {
BX_ERROR(("EndpointOutRequest | USB_REQ_CLEAR_FEATURE: Unknown Clear Feature Request found: %d", value));
}
break;
case EndpointOutRequest | USB_REQ_SET_FEATURE:
// with EndpointRequest, The wLength field must be zero
if (length != 0) {
BX_ERROR(("USB_REQ_SET_FEATURE: This type of request requires the wLength field to be zero."));
}
if (value == USB_ENDPOINT_HALT) {
if ((index & 0x7F) < USB_MAX_ENDPOINTS) {
set_halted(index, 1);
ret = 0;
} else {
BX_ERROR(("EndpointOutRequest | USB_REQ_SET_FEATURE: index > ep count: %d", index));
}
} else {
BX_ERROR(("EndpointOutRequest | USB_REQ_SET_FEATURE: Unknown Set Feature Request found: %d", value));
}
break;
// should not have a default: here, so allowing the device's handle_control() to try to execute the request
Expand Down
15 changes: 12 additions & 3 deletions bochs/iodev/usb/usb_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
#define USB_REQ_SET_SEL 0x30
#define USB_REQ_SET_ISO_DELAY 0x31

#define USB_ENDPOINT_HALT 0
#define USB_DEVICE_SELF_POWERED 0
#define USB_DEVICE_REMOTE_WAKEUP 1
#define USB_DEVICE_U1_ENABLE 48
Expand Down Expand Up @@ -188,6 +189,7 @@ typedef struct USBEndPoint {
#if HANDLE_TOGGLE_CONTROL
int toggle; // the current toggle for the endpoint (0, 1, or -1 for xHCI)
#endif
bool halted; // is the current ep halted?
} USBEndPoint;

class BOCHSAPI bx_usbdev_ctl_c : public logfunctions {
Expand Down Expand Up @@ -258,13 +260,20 @@ class BOCHSAPI usb_device_c : public logfunctions {

#if HANDLE_TOGGLE_CONTROL
int get_toggle(const int ep) {
return (ep < USB_MAX_ENDPOINTS) ? d.endpoint_info[ep].toggle : 0;
return ((ep & 0x7F) < USB_MAX_ENDPOINTS) ? d.endpoint_info[(ep & 0x7F)].toggle : 0;
}
void set_toggle(const int ep, const int toggle) {
if (ep < USB_MAX_ENDPOINTS)
d.endpoint_info[ep].toggle = toggle;
if ((ep & 0x7F) < USB_MAX_ENDPOINTS)
d.endpoint_info[(ep & 0x7F)].toggle = toggle;
}
#endif
bool get_halted(const int ep) {
return ((ep & 0x7F) < USB_MAX_ENDPOINTS) ? d.endpoint_info[(ep & 0x7F)].halted : 0;
}
void set_halted(const int ep, const bool halted) {
if ((ep & 0x7F) < USB_MAX_ENDPOINTS)
d.endpoint_info[(ep & 0x7F)].halted = halted;
}

Bit8u get_type() {
return d.type;
Expand Down
34 changes: 20 additions & 14 deletions bochs/iodev/usb/usb_ehci.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ static inline struct EHCIPacket *ehci_container_of_usb_packet(void *ptr)
reinterpret_cast<size_t>(&(static_cast<struct EHCIPacket*>(0)->packet)));
}

int ehci_event_handler(int event, void *ptr, void *dev, int port);

// builtin configuration handling functions

Bit32s usb_ehci_options_parser(const char *context, int num_params, char *params[])
Expand Down Expand Up @@ -525,6 +523,8 @@ void bx_usb_ehci_c::reset_port(int p)
BX_EHCI_THIS hub.usb_port[p].portsc.csc = 0;
}

int ehci_event_handler(int event, void *ptr, void *dev, int port);

void bx_usb_ehci_c::init_device(Bit8u port, bx_list_c *portconf)
{
char pname[BX_PATHNAME_LEN];
Expand Down Expand Up @@ -603,18 +603,18 @@ bool bx_usb_ehci_c::set_connect_status(Bit8u port, bool connected)
} else { // not connected
BX_INFO(("port #%d: device disconnect", port+1));
if (BX_EHCI_THIS hub.usb_port[port].portsc.po) {
BX_EHCI_THIS uhci[port >> 1]->set_port_device(port & 1, NULL);
if ((!BX_EHCI_THIS hub.usb_port[port].owner_change) &&
(BX_EHCI_THIS hub.op_regs.ConfigFlag & 1)) {
BX_EHCI_THIS hub.usb_port[port].portsc.po = 0;
BX_EHCI_THIS hub.usb_port[port].portsc.csc = 1;
}
BX_EHCI_THIS uhci[port >> 1]->set_port_device(port & 1, NULL);
if ((!BX_EHCI_THIS hub.usb_port[port].owner_change) &&
(BX_EHCI_THIS hub.op_regs.ConfigFlag & 1)) {
BX_EHCI_THIS hub.usb_port[port].portsc.po = 0;
BX_EHCI_THIS hub.usb_port[port].portsc.csc = 1;
}
} else {
BX_EHCI_THIS hub.usb_port[port].portsc.ccs = 0;
BX_EHCI_THIS hub.usb_port[port].portsc.ped = 0;
BX_EHCI_THIS queues_rip_device(device, 0);
BX_EHCI_THIS queues_rip_device(device, 1);
device->set_async_mode(0);
BX_EHCI_THIS hub.usb_port[port].portsc.ccs = 0;
BX_EHCI_THIS hub.usb_port[port].portsc.ped = 0;
BX_EHCI_THIS queues_rip_device(device, 0);
BX_EHCI_THIS queues_rip_device(device, 1);
device->set_async_mode(0);
}
if (!BX_EHCI_THIS hub.usb_port[port].owner_change) {
remove_device(port);
Expand Down Expand Up @@ -652,6 +652,11 @@ void bx_usb_ehci_c::change_port_owner(int port)
set_connect_status(port, 1);
}
}
// make sure we are calling the correct handler for the device
if (device != NULL) {
if (BX_EHCI_THIS hub.usb_port[port].portsc.po == 0)
device->set_event_handler(BX_EHCI_THIS_PTR, ehci_event_handler, port);
}
BX_EHCI_THIS hub.usb_port[port].owner_change = 0;
}
}
Expand Down Expand Up @@ -888,6 +893,7 @@ bool bx_usb_ehci_c::write_handler(bx_phy_address addr, unsigned len, void *data,
if (oldfpr && !BX_EHCI_THIS hub.usb_port[port].portsc.fpr) {
BX_EHCI_THIS hub.usb_port[port].portsc.sus = 0;
}
} else if (port == USB_EHCI_PORTS) {
}
}
} else {
Expand Down Expand Up @@ -2224,7 +2230,7 @@ void bx_usb_ehci_c::ehci_frame_timer(void)
t_now = bx_pc_system.time_usec();
usec_elapsed = t_now - BX_EHCI_THIS hub.last_run_usec;
frames = (int)(usec_elapsed / FRAME_TIMER_USEC);

if (BX_EHCI_THIS periodic_enabled() || (BX_EHCI_THIS hub.pstate != EST_INACTIVE)) {
need_timer++;
BX_EHCI_THIS hub.async_stepdown = 0;
Expand Down
55 changes: 37 additions & 18 deletions bochs/iodev/usb/usb_floppy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,22 @@ class bx_usb_floppy_locator_c : public usbdev_locator_c {
// to match your changes.

// Full-speed only
// * USB Mass Storage Class Specification, p4, section 3, states that
// * that a Mass Storage Class device using CB/CBI must be full-speed only.
static Bit8u bx_floppy_dev_descriptor[] = {
0x12, /* u8 bLength; */
0x01, /* u8 bDescriptorType; Device */
0x00, 0x02, /* u16 bcdUSB; v2.0 */
0x01, 0x01, /* u16 bcdUSB; v1.1 */

0x00, /* u8 bDeviceClass; */
0x00, /* u8 bDeviceSubClass; */
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
0x00, /* u8 bDeviceProtocol; */
0x40, /* u8 bMaxPacketSize; 64 Bytes */

/* Vendor and product id are arbitrary. */
0x00, 0x00, /* u16 idVendor; */
0x00, 0x00, /* u16 idProduct; */
0x00, 0x00, /* u16 bcdDevice */
0x00, 0x00, /* u16 bcdDevice; */

0x01, /* u8 iManufacturer; */
0x02, /* u8 iProduct; */
Expand All @@ -120,7 +122,11 @@ static const Bit8u bx_floppy_config_descriptor[] = {
/* one configuration */
0x09, /* u8 bLength; */
0x02, /* u8 bDescriptorType; Configuration */
#if USB_FLOPPY_USE_INTERRUPT
0x27, 0x00, /* u16 wTotalLength; */
#else
0x20, 0x00, /* u16 wTotalLength; */
#endif
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x00, /* u8 iConfiguration; */
Expand Down Expand Up @@ -324,6 +330,7 @@ usb_floppy_device_c::usb_floppy_device_c()
bx_param_bool_c *readonly;
bx_param_enum_c *status, *mode;

// MSC Compliance states that a CB(I) device must be full-speed only
d.speed = d.minspeed = d.maxspeed = USB_SPEED_FULL;
memset((void *) &s, 0, sizeof(s));
strcpy(d.devname, "BOCHS UFI/CBI FLOPPY");
Expand Down Expand Up @@ -456,13 +463,13 @@ bool usb_floppy_device_c::init()
bx_floppy_dev_descriptor[9] = 0x06;
d.vendor_desc = "TEAC ";
d.product_desc = "TEAC FD-05PUW ";
d.serial_num = "3000";
d.serial_num = "3000 ";
} else {
bx_floppy_dev_descriptor[8] = 0x00;
bx_floppy_dev_descriptor[9] = 0x00;
d.vendor_desc = "BOCHS ";
d.product_desc = d.devname;
d.serial_num = "00.10";
d.serial_num = "00.10 ";
}
if (set_inserted(1)) {
sprintf(s.info_txt, "USB floppy: path='%s', mode='%s'", s.fname, s.image_mode);
Expand Down Expand Up @@ -552,6 +559,29 @@ int usb_floppy_device_c::handle_control(int request, int value, int index, int l
}
ret = 0;
break;
case EndpointRequest | USB_REQ_GET_STATUS:
BX_DEBUG(("USB_REQ_GET_STATUS: Endpoint."));
// if the endpoint is currently halted, return bit 0 = 1
if (value == USB_ENDPOINT_HALT) {
int indx = (index & 0x7F);
#if USB_FLOPPY_USE_INTERRUPT
int limit = 3;
#else
int limit = 2;
#endif
if ((indx > 0) && (indx <= limit)) {
data[0] = 0x00 | (get_halted(indx) ? 1 : 0);
data[1] = 0x00;
ret = 2;
} else {
BX_ERROR(("EndpointRequest | USB_REQ_GET_STATUS: index > ep count: %d", index));
goto fail;
}
} else {
BX_ERROR(("EndpointRequest | USB_REQ_SET_FEATURE: Unknown Get Status Request found: %d", value));
goto fail;
}
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch (value >> 8) {
case USB_DT_STRING:
Expand Down Expand Up @@ -579,17 +609,6 @@ int usb_floppy_device_c::handle_control(int request, int value, int index, int l
goto fail;
}
break;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
BX_DEBUG(("USB_REQ_CLEAR_FEATURE:"));
// Value == 0 == Endpoint Halt (the Guest wants to reset the endpoint)
if (value == 0) { /* clear ep halt */
#if HANDLE_TOGGLE_CONTROL
set_toggle(index, 0);
#endif
ret = 0;
} else
goto fail;
break;
case DeviceOutRequest | USB_REQ_SET_SEL:
// Set U1 and U2 System Exit Latency
BX_DEBUG(("SET_SEL (U1 and U2):"));
Expand Down Expand Up @@ -646,13 +665,13 @@ bool usb_floppy_device_c::handle_command(Bit8u *command)
}

#if UFI_DO_INQUIRY_HACK
// to be consistant with real hardware, we need to fail with
// to be consistent with real hardware, we need to fail with
// a STALL twice for any command after the first Inquiry except
// for the inquiry and request_sense commands.
// (I don't know why, and will document further when I know more)
if ((s.fail_count > 0) &&
(s.cur_command != UFI_INQUIRY) && (s.cur_command != UFI_REQUEST_SENSE)) {
BX_DEBUG(("Consistant stall of %d of 2.", 2 - s.fail_count + 1));
BX_INFO(("Consistent stall of %d of 2.", 2 - s.fail_count + 1));
s.fail_count--;
return 0;
}
Expand Down
Loading