USB2.0 control endpoint stall upon set_configuration request
USB2.0 control endpoint stall upon set_configuration request
I'm working on xhci controller driver for a proprietary OS. I'm having a problem with enumerating USB2 devices. USB3 devices get enumerated without issues. Here's what my driver does:
1. Attach device and a port status change event is posted.
2. if the event is a USB2 attach, reset the port and wait for it to complete the reset.
3. Send an enableSlot command to the controller.
4. if success, send an AddressDevice command to the controller.
5. If success, read the device descriptor and get the maxPacketSize
6. Update the input context and send an EvaluateContext command.
7. If success, read the configuration descriptor.
8. Configure contexts, send ConfigureEndpoint command to the controller.
9. If success, send a SET_CONFIGURATION request to the device.
10. Device responds with a transfer event with a success completion code.
Now, if I attach a USB3 device, all 10 steps complete without issues. However, if I attach a USB2 device, only the first 9 steps complete and the device responds with a STALL on the 10th step.
I have tried more things than I can even list here, including playing with the BSR, sending SET_ADDRESS requests manually, trying other configurations, different devices, sending wrong configurations... They all return a STALL on the status stage trb.
Is there any way to know what is causing the usb2 devices to stall? How can I fix this? Any pointers are much appreciated.
1. Attach device and a port status change event is posted.
2. if the event is a USB2 attach, reset the port and wait for it to complete the reset.
3. Send an enableSlot command to the controller.
4. if success, send an AddressDevice command to the controller.
5. If success, read the device descriptor and get the maxPacketSize
6. Update the input context and send an EvaluateContext command.
7. If success, read the configuration descriptor.
8. Configure contexts, send ConfigureEndpoint command to the controller.
9. If success, send a SET_CONFIGURATION request to the device.
10. Device responds with a transfer event with a success completion code.
Now, if I attach a USB3 device, all 10 steps complete without issues. However, if I attach a USB2 device, only the first 9 steps complete and the device responds with a STALL on the 10th step.
I have tried more things than I can even list here, including playing with the BSR, sending SET_ADDRESS requests manually, trying other configurations, different devices, sending wrong configurations... They all return a STALL on the status stage trb.
Is there any way to know what is causing the usb2 devices to stall? How can I fix this? Any pointers are much appreciated.
- bellezzasolo
- Member
- Posts: 110
- Joined: Sun Feb 20, 2011 2:01 pm
Re: USB2.0 control endpoint stall upon set_configuration req
It could be any number of things. But are you sending a DATA stage TRB? Set Configuration is the first command that doesn't have a data payload, so it doesn't need one.
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS
https://github.com/ChaiSoft/ChaiOS
Re: USB2.0 control endpoint stall upon set_configuration req
No I'm not sending a DATA stage.
- bellezzasolo
- Member
- Posts: 110
- Joined: Sun Feb 20, 2011 2:01 pm
Re: USB2.0 control endpoint stall upon set_configuration req
Without seeing the code it's difficult to say.melef wrote:No I'm not sending a DATA stage.
This is what I have for the procedure:
Code: Select all
static usb_status_t ConfigureDevice(UsbDeviceInfo* device, int CONFIG, uint8_t HubPorts)
{
usb_status_t status = USB_FAIL;
REQUEST_PACKET device_packet;
device_packet.request_type = USB_BM_REQUEST_INPUT | USB_BM_REQUEST_STANDARD | USB_BM_REQUEST_DEVICE;
device_packet.request = USB_BREQUEST_GET_DESCRIPTOR;
device_packet.value = USB_DESCRIPTOR_WVALUE(USB_DESCRIPTOR_CONFIGURATION, CONFIG-1);
device_packet.index = 0;
auto confdesc = GetCompleteDescriptor<usb_configuration_descriptor>(device, device_packet, &GetConfigLength);
if (!confdesc)
return USB_FAIL;
usb_interface_descriptor* defiface = raw_offset<usb_interface_descriptor*>(confdesc, confdesc->bLength);
usb_descriptor* descep = raw_offset<usb_descriptor*>(defiface, defiface->bLength);
usb_endpoint_descriptor** endpointdata = new usb_endpoint_descriptor * [defiface->bNumEndpoints + 1];
int i = 0;
while (raw_diff(descep, confdesc) < confdesc->wTotalLength)
{
if (descep->bDescriptorType == USB_DESCRIPTOR_ENDPOINT)
{
endpointdata[i++] = (usb_endpoint_descriptor*)descep;
if (i == defiface->bNumEndpoints) break;
}
descep = raw_offset<usb_descriptor*>(descep, descep->bLength);
}
endpointdata[defiface->bNumEndpoints] = nullptr;
if (USB_FAILED(status = device->ConfigureEndpoint(endpointdata, 1, 0, 0, HubPorts)))
return status;
//Send set configuration
device_packet.request_type = USB_BM_REQUEST_OUTPUT | USB_BM_REQUEST_STANDARD | USB_BM_REQUEST_DEVICE;
device_packet.request = USB_BREQUEST_SET_CONFIGURATION;
device_packet.value = USB_DESCRIPTOR_WVALUE(0, CONFIG);
device_packet.index = 0;
device_packet.length = 0;
void* devdesc;
if (USB_FAILED(status = device->RequestData(device_packet, (void**)&devdesc)))
return status;
kputs(u"Device Configured\n");
return USB_SUCCESS;
}
The one that caught me out for a while was that for retrieving the descriptor, the index is zero-based, (CONFIG-1), but it's one-base (CONFIG) for SET_CONFIGURATION.
And then it could even be an issue with the endpoint configuration.
Code: Select all
virtual usb_status_t ConfigureEndpoint(usb_endpoint_descriptor** pEndpoints, uint8_t config, uint8_t iface, uint8_t alternate, uint8_t downstreamports)
{
paddr_t incontxt;
size_t epAdd = 1;
size_t highbit = 0;
auto parseep = [](usb_endpoint_descriptor* ep, uint8_t& addr, uint8_t& dirIn)
{
addr = ep->bEndpointAddress & 0xF;
dirIn = ep->bEndpointAddress >> 7;
};
auto BitToSet = [&](usb_endpoint_descriptor* ep)
{
uint8_t addr, dirIn;
parseep(ep, addr, dirIn);
return 2 * addr + dirIn;
};
for (int n = 0; pEndpoints[n]; ++n)
{
auto bitset = BitToSet(pEndpoints[n]);
epAdd |= (1 << bitset);
highbit = bitset > highbit ? bitset : highbit;
}
auto mapin = controller->CreateInputContext(incontxt, epAdd, 0, config, iface, alternate);
mapin->slot.ContextEntries = highbit;
if (downstreamports > 0)
{
mapin->slot.Hub = 1;
mapin->slot.NumberPorts = downstreamports;
}
for (int n = 0; pEndpoints[n]; ++n)
{
auto ep = pEndpoints[n];
uint8_t addr, dirIn;
parseep(ep, addr, dirIn);
auto epct = controller->GetEndpointContext(mapin, addr, dirIn);
epct->MaxPacketSize = ep->wMaxPacketSize & 0x7FF;
auto usbtyp = ep->bmAttributes & USB_EP_ATTR_TYPE_MASK;
epct->EpType = usbtyp | (dirIn << 2);
if (dirIn)
{
auto ring = controller->MakeEventRing(1);
epct->TRDequeuePtr = (uint64_t)get_physical_address(ring->dequeueptr) | 1;
}
auto MaxEsit = epct->MaxPacketSize * (epct->MaxBurstSize + 1);
epct->HID = 1;
switch (usbtyp)
{
case USB_EP_ATTR_TYPE_CONTROL:
break;
case USB_EP_ATTR_TYPE_BULK:
break;
case USB_EP_ATTR_TYPE_INTERRUPT:
epct->CErr = 3;
epct->MaxEsitLow = MaxEsit & 0xFFFF;
epct->MaxEsitHigh = MaxEsit >> 16;
epct->Interval = 11;
epct->AverageTrbLength = 0x400;
break;
case USB_EP_ATTR_TYPE_ISOCHRONOUS:
break;
default:
break;
}
}
//while (1);
//Issue address device command
void* last_command = controller->cmdring.enqueue(create_configureendpoint_command(incontxt, slotid, false));
uint64_t* curevt = (uint64_t*)controller->waitComplete(last_command, 1000);
if (get_trb_completion_code(curevt) != XHCI_COMPLETION_SUCCESS)
{
kprintf(u"Error configuring endpoint: %x\n", get_trb_completion_code(curevt));
return USB_FAIL;
}
return USB_SUCCESS;
}
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS
https://github.com/ChaiSoft/ChaiOS
Re: USB2.0 control endpoint stall upon set_configuration req
The code has gotten very big so I'm gonna start by posting the contexts and other states and if we couldn't see something there then I will post as much as I can from the code. All these states are dumped after the status stage TRB completes with a STALL.
Also, I'm only configuring one endpoint just for simplicity for now.
First, here's the descriptors of a device that fails to enumerate:
Here's the endpoint output context for endpoint 129 after the configure endpoint command:
Here's the output slot context when the status stage completes with a stall:
Here's the control endpoint output context when the status stage completes with stall:
Here's the port state:
Here are the Setup TRB I'm sending for the set_configuration request:
And here's the status TRB:
And here's the transfer event TRB in response to the status:
Also, I'm only configuring one endpoint just for simplicity for now.
First, here's the descriptors of a device that fails to enumerate:
Code: Select all
Device Descriptor:
===================
BcdUSB = 0x200
DeviceClass = 0
DeviceSubClass = 0
DeviceProtocol = 0
MaxPacketSize0 = 64
IdVendor = 8644
IdProduct = 3271
BcdDevice = 4352
StrManufacturer = 1
StrProduct = 2
StrSerialNumber = 3
NumConfigurations = 1
Configuration Descriptor:
---------------------------
Length = 9
Decriptor Type = 2
Total length = 32
Num Interfaces = 1
Configuration Value = 1
configuration = 0
attributes = 128
Max Power = 250
Interface Descriptor:
----------------------
Length = 9
Decriptor Type = 4
Interface Number = 0
Alternate Setting = 0
Number of endpoints = 2
Interace Class = 8
Interface Subclass = 6
Interface protocol = 80
Interface 0
Endpoint Descriptor:
----------------------
Length = 7
Decriptor Type = 5
Endpoint Address = 129
Attributes = 2
Max packet size = 512
Interval = 255
Code: Select all
[xhci]EP State = 0x1
[xhci]MULT = 0x0
[xhci]MaxPStreams = 0x0
[xhci]LSA = 0x0
[xhci]Interval = 0xf
[xhci]MAX esit payload hi = 0x0
[xhci]CERR = 0x3
[xhci]EP type = 0x6
[xhci]HID = 0x0
[xhci]Max burst Size = 0x0
[xhci]Max packet size = 0x200
[xhci]Transfer dq ptr = 0x1c03e001
[xhci]avg Trb length = 0x1000
[xhci]Max esit payload lo = 0x0
Here's the output slot context when the status stage completes with a stall:
Code: Select all
[xhci]Route String = 0x0
[xhci]Speed = 0x3
[xhci]MTT = 0x0
[xhci]HUB = 0x0
[xhci]Context Entries = 0x3
[xhci]Max Exit Latency = 0x0
[xhci]Root hub port number = 0x1
[xhci]Number of ports = 0x0
[xhci]TT Hub Slot ID = 0x0
[xhci]TT Port Number = 0x0
[xhci]TTT = 0x0
[xhci]Interruputer Target = 0x0
[xhci]USB Device Address = 0x4
[xhci]Slot State = 0x3
Code: Select all
[xhci]EP State = 0x2
[xhci]MULT = 0x0
[xhci]MaxPStreams = 0x0
[xhci]LSA = 0x0
[xhci]Interval = 0x0
[xhci]MAX esit payload hi = 0x0
[xhci]CERR = 0x3
[xhci]EP type = 0x4
[xhci]HID = 0x0
[xhci]Max burst Size = 0x0
[xhci]Max packet size = 0x40
[xhci]Transfer dq ptr = 0x1c03d0a1
[xhci]avg Trb length = 0x8
[xhci]Max esit payload lo = 0x0
Code: Select all
Port state:
========
PLS:0
PP:1
CCS:1
PED:1
PR:0
Code: Select all
10900
0
8
841
Code: Select all
0
0
0
1021
Code: Select all
1c03d0a0
0
6000000
4018001
Re: USB2.0 control endpoint stall upon set_configuration req
Stall on the control pipe typically means your messages are wrong or you are sending them with wrong DATA0/1 fields. With XHCI, it can also be that the device is not correctly configured in the device context (particularly if you only have problems with non-USB3 devices).melef wrote: Is there any way to know what is causing the usb2 devices to stall? How can I fix this? Any pointers are much appreciated.
Note that all rules that apply to usb2 still apply with XHCI. The XHCI controller only takes care of the speed conversion, and you need to give the correct parameters to make it operate properly.
Re: USB2.0 control endpoint stall upon set_configuration req
The problem was that I need to set the DIR bit of the status stage trb of the set configuration request to 1. USB3 devices want DIR = 0. I don't know but it works.
- bellezzasolo
- Member
- Posts: 110
- Joined: Sun Feb 20, 2011 2:01 pm
Re: USB2.0 control endpoint stall upon set_configuration req
USB2 specification says (8.5.3.1), which is duplicated in the USB 3 spec (8.12.2.1):melef wrote:The problem was that I need to set the DIR bit of the status stage trb of the set configuration request to 1. USB3 devices want DIR = 0. I don't know but it works.
Taking a look in the xHCI spec (4.11.2.2):Status reporting is always in the function-to-host direction.
Table 4-7 says that the Status Stage direction flag should be OUT only for an IN Data Stage.System software is responsible for ensuring that the Direction (DIR) flag of the Data Stage and Status Stage TRBs are consistent with the USB SETUP Data defined bmRequestType:Data Transfer Direction (DTD) flag and wLength field. Refer to Table 4-7 for mapping.
But here is where I think we reach the critical bit:
So hopping across to USB3 spec 8.8, the direction flag is actually part of the addressing triple, and for the default control endpoint this is zero (=OUT).The Direction (DIR) flag in the Status Stage TRB indicates the direction of the control transfer acknowledgement. For USB2 devices, DIR directly determines the PID that shall be used for the associated USB2 transaction. For USB3 devices, a Status TP is defined which is used for the status stage of all Enhanced SuperSpeed control transfers. Refer to section 8.5 of the USB3 spec for the definition of the SS Status TP Direction flag.
The Direction (DIR) flag in the Data Stage TRB defines the transfer direction for all TRBS in the Data Stage TD. For USB2 devices, DIR directly determines the PID that shall be used for the Data Stage transaction. For USB3 devices, if DIR = OUT a DP is generated with write data, if DIR = IN an ACK TP is generated to request read data from the device.
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS
https://github.com/ChaiSoft/ChaiOS