OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 5:50 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: USB2.0 control endpoint stall upon set_configuration request
PostPosted: Mon Apr 03, 2023 8:07 am 
Offline

Joined: Mon Apr 03, 2023 7:34 am
Posts: 4
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.


Top
 Profile  
 
 Post subject: Re: USB2.0 control endpoint stall upon set_configuration req
PostPosted: Tue Apr 04, 2023 12:40 am 
Offline
Member
Member
User avatar

Joined: Sun Feb 20, 2011 2:01 pm
Posts: 110
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


Top
 Profile  
 
 Post subject: Re: USB2.0 control endpoint stall upon set_configuration req
PostPosted: Tue Apr 04, 2023 7:27 am 
Offline

Joined: Mon Apr 03, 2023 7:34 am
Posts: 4
No I'm not sending a DATA stage.


Top
 Profile  
 
 Post subject: Re: USB2.0 control endpoint stall upon set_configuration req
PostPosted: Tue Apr 04, 2023 8:42 am 
Offline
Member
Member
User avatar

Joined: Sun Feb 20, 2011 2:01 pm
Posts: 110
melef wrote:
No I'm not sending a DATA stage.

Without seeing the code it's difficult to say.

This is what I have for the procedure:
Code:
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;
}


Set_Configuration is USB_BM_REQUEST_OUTPUT, something very simple that might potentially get overlooked.

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:
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


Top
 Profile  
 
 Post subject: Re: USB2.0 control endpoint stall upon set_configuration req
PostPosted: Tue Apr 04, 2023 1:13 pm 
Offline

Joined: Mon Apr 03, 2023 7:34 am
Posts: 4
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:
Code:
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


Here's the endpoint output context for endpoint 129 after the configure endpoint command:
Code:
[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:
[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   


Here's the control endpoint output context when the status stage completes with stall:
Code:
[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


Here's the port state:
Code:
Port state:
========
PLS:0
PP:1
CCS:1
PED:1
PR:0


Here are the Setup TRB I'm sending for the set_configuration request:
Code:
10900
0
8
841


And here's the status TRB:
Code:
0
0
0
1021


And here's the transfer event TRB in response to the status:
Code:
1c03d0a0
0
6000000
4018001


Top
 Profile  
 
 Post subject: Re: USB2.0 control endpoint stall upon set_configuration req
PostPosted: Tue Apr 04, 2023 2:48 pm 
Offline
Member
Member

Joined: Wed Oct 01, 2008 1:55 pm
Posts: 3191
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.


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).

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.


Top
 Profile  
 
 Post subject: Re: USB2.0 control endpoint stall upon set_configuration req
PostPosted: Wed Apr 19, 2023 2:53 pm 
Offline

Joined: Mon Apr 03, 2023 7:34 am
Posts: 4
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.


Top
 Profile  
 
 Post subject: Re: USB2.0 control endpoint stall upon set_configuration req
PostPosted: Thu Apr 20, 2023 2:12 am 
Offline
Member
Member
User avatar

Joined: Sun Feb 20, 2011 2:01 pm
Posts: 110
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.

USB2 specification says (8.5.3.1), which is duplicated in the USB 3 spec (8.12.2.1):
Quote:
Status reporting is always in the function-to-host direction.


Taking a look in the xHCI spec (4.11.2.2):
Quote:
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.

Table 4-7 says that the Status Stage direction flag should be OUT only for an IN Data Stage.
Image

But here is where I think we reach the critical bit:
Quote:
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.


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).

_________________
Whoever said you can't do OS development on Windows?
https://github.com/ChaiSoft/ChaiOS


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot], DotBot [Bot] and 64 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group