XHCI - bulk endpoint problems

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
SanderR
Member
Member
Posts: 73
Joined: Tue Aug 30, 2016 1:31 pm
Libera.chat IRC: SDR

XHCI - bulk endpoint problems

Post by SanderR »

Good afternoon all!

I am writing a 64bit operating system in C, booted with EFI.
My problem is, when I activate the bulk endpoints with CONFIGURE_ENDPOINT, I get status 1 in return, but I do not get a reply on the event ring when I actually try to use the endpoint

First of all, this is the output for lsusb:

Code: Select all

sudo lsusb -v
Bus 004 Device 002: ID 0951:1666 Kingston Technology DataTraveler 100 G3/G4/SE9 G2/50 Kyson
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               3.20
  bDeviceClass            0 [unknown]
  bDeviceSubClass         0 [unknown]
  bDeviceProtocol         0 
  bMaxPacketSize0         9
  idVendor           0x0951 Kingston Technology
  idProduct          0x1666 DataTraveler 100 G3/G4/SE9 G2/50 Kyson
  bcdDevice            1.10
  iManufacturer           2 Kingston
  iProduct                3 DataTraveler 3.0
  iSerial                 4 E0D55EA58B281910C82C0026
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x002c
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              144mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         8 Mass Storage
      bInterfaceSubClass      6 SCSI
      bInterfaceProtocol     80 Bulk-Only
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               0
        bMaxBurst               3
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0400  1x 1024 bytes
        bInterval               0
        bMaxBurst               3

Now I try to trigger "configure endpoint" like this:

Code: Select all

#include "xhci.h"

void xhci_activate_endpoints(XHCIControllerSession *session, USBDevice* device)
{
    if(device->configdesc==NULL)
    {
        //
        // we just focus on the MSD
        return;
    }

    if(device->configdesc->interfacdesc.bNumEndpoints != 2)
    {
        printk("XHCI: het apparaat heeft geen 2 endpoints, activatie overgeslagen!\n");
        return;
    }

    void* ring_in = alloc_page();
    void* ring_out = alloc_page();

    device->infostructures->icc.Aregisters = 0b1100;//0b1100
	// device->infostructures->slotcontext.RootHubPortNumber = device->physical_port_id + 1;
	device->infostructures->slotcontext.ContextEntries = 5;
	// device->infostructures->slotcontext.Speed = portspeed;
	device->infostructures->ep1.LSA = 0;
	device->infostructures->ep1.EPType = XHCI_ENDPOINT_TYPE_BULK_OUT; // Bulk OUT
	device->infostructures->ep1.Cerr = 3;
	device->infostructures->ep1.MaxPacketSize = 1024;
	device->infostructures->ep1.TRDequeuePointerLow = ((uint32_t) (uint64_t) ring_out)>>4 ;
	device->infostructures->ep1.TRDequeuePointerHigh = 0;
	device->infostructures->ep1.DequeueCycleState = XHCI_CRCS_DEFAULT_CYCLE_STATE;
	device->infostructures->ep1.MaxBurstSize = 3;

	device->infostructures->ep2.LSA = 0;
	device->infostructures->ep2.EPType = XHCI_ENDPOINT_TYPE_BULK_IN; // Bulk IN
	device->infostructures->ep2.Cerr = 3;
	device->infostructures->ep2.MaxPacketSize = 1024;
	device->infostructures->ep2.TRDequeuePointerLow = ((uint32_t) (uint64_t) ring_in)>>4 ;
	device->infostructures->ep2.TRDequeuePointerHigh = 0;
	device->infostructures->ep2.DequeueCycleState = XHCI_CRCS_DEFAULT_CYCLE_STATE;
	device->infostructures->ep2.MaxBurstSize = 3;
    
    ConfigureEndpointCommandTRB* trb = (ConfigureEndpointCommandTRB*) xhci_alloc_command_trb(session);
    trb->CycleBit = XHCI_CRCS_DEFAULT_CYCLE_STATE; // Cycle Bit instellen
    trb->TRBType = XHCI_TRB_CONFIGURE_ENDPOINT_COMMAND_TRB_TYPE; // CONGIGURE ENDPOINT Command TRB Type
    trb->SlotID = device->slot_id; // Slot ID van het apparaat inst
    trb->DataBufferPointerLo = (uint32_t)(uint64_t)(device->infostructures);
	trb->DataBufferPointerHi = (uint32_t)0;

    USBRing* in_ring = (USBRing*) alloc_page();
	in_ring->ring_trbs = ring_in;
	in_ring->ring_size = XHCI_COMMAND_RING_SIZE;
	in_ring->enqueue_index = 0;
	in_ring->slot_id = device->slot_id;
	in_ring->endpoint_id = 2;
	in_ring->cycle_state = XHCI_CRCS_DEFAULT_CYCLE_STATE;
	device->ep_ring_in = in_ring;

    USBRing* out_ring = (USBRing*) alloc_page();
	out_ring->ring_trbs = ring_out;
	out_ring->ring_size = XHCI_COMMAND_RING_SIZE;
	out_ring->enqueue_index = 0;
	out_ring->slot_id = device->slot_id;
	out_ring->endpoint_id = 3;
	out_ring->cycle_state = XHCI_CRCS_DEFAULT_CYCLE_STATE;
	device->ep_ring_out = out_ring;

    xhci_thingdong(session, device, (void*)trb, 0, 0);

}
And this is the headerfile:

Code: Select all


typedef struct{
    uint32_t Dregisters;
    uint32_t Aregisters;
    uint32_t reservedA;
    uint32_t reservedB;
    uint32_t reservedC;
    uint32_t reservedD;
    uint32_t reservedE;
    uint8_t ConfigurationValue;
    uint8_t InterfaceNumber;
    uint8_t AlternateSetting;
    uint8_t reservedF;
}__attribute__((packed)) XHCIInputControlContext;

typedef struct{
    uint32_t RouteString:20;
    uint8_t Speed:4;
    uint8_t reservedA:1;
    uint8_t MTT:1;
    uint8_t Hub:1;
    uint8_t ContextEntries:5;

    uint16_t MaxExitLatency;
    uint8_t RootHubPortNumber;
    uint8_t NumberOfPorts;

    uint8_t ParentHubSlotID;
    uint8_t ParentPortNumber;
    uint8_t TTT:2;
    uint8_t reservedB:4;
    uint16_t InterrupterTarget:10;

    uint8_t USBDeviceAddress;
    uint32_t reservedC:19;
    uint8_t SlotState:5;
}__attribute__((packed)) XHCISlotContext;

typedef struct {
    uint8_t EndpointState:3;
    uint8_t reservedA:5;
    uint8_t Mult:2;
    uint8_t MaxPStreams:5;
    uint8_t LSA:1;
    uint8_t Interval;
    uint8_t MaxESITPayloadHigh;

    uint8_t reservedB:1;
    uint8_t Cerr:2;
    uint8_t EPType:3;
    uint8_t reservedC:1;
    uint8_t HID:1;
    uint8_t MaxBurstSize;
    uint16_t MaxPacketSize;

    uint8_t DequeueCycleState:1;
    uint8_t reservedD:3;
    uint32_t TRDequeuePointerLow:28;
    uint32_t TRDequeuePointerHigh;

    uint16_t AverageTRBLength;
    uint16_t MaxESITPayloadLow;

    uint8_t padding[0xC];
}__attribute__((packed)) XHCIEndpointContext;

typedef struct{
    XHCIInputControlContext icc;
    XHCISlotContext slotcontext;
    uint8_t paddingB[0x10];
    XHCIEndpointContext ep0;
    XHCIEndpointContext ep1;
    XHCIEndpointContext ep2;
    XHCIEndpointContext ep3;
    XHCIEndpointContext ep4;
}__attribute__((packed)) XHCIInputContextBuffer;
And this is the code to execute the TRB

Code: Select all

void xhci_send_bulk(XHCIControllerSession *session, USBDevice* device, USBRing *ring, uint64_t data_length, void* data){
    TransferTRB* trb1 = (TransferTRB*) xhci_alloc_trb_ring(ring);
    trb1->DataBufferPointerHi = 0;
    trb1->DataBufferPointerLo = (uint32_t) (uint64_t) data;
    trb1->BlockEventInterrupt = 0;
    trb1->Chainbit = 0;
    trb1->Cyclebit = 1;
    trb1->EvaluateNextTRB = 0;
    trb1->ImmediateData = 0;
    trb1->InterrupterTarget = 0;
    trb1->InterruptOnCompletion = 1;
    trb1->InterruptonShortPacket = 1;
    trb1->NoSnoop = 0;
    trb1->TRBTransferLength = data_length;
    trb1->TRBType = 1;

    xhci_ring_trb_ring(session, device, ring, (void*)trb1);
}
This is the link to the github of the project: https://github.com/AdeRegt/FramboOS

Can someone please tell me where my error is?
I am testing this on real hardware.
Thank you in advantage!
User avatar
BenLunt
Member
Member
Posts: 1029
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: XHCI - bulk endpoint problems

Post by BenLunt »

HI,

Code: Select all

  if(device->configdesc==NULL)
    {
        //
        // we just focus on the MSD
        return;
    }
Up to this point, have you retrieved the Configuration Descriptor? If not, how have you sent the SetConfiguration() request?
Then, without getting the Config Descriptor, you can't know which Interface to set using a SetInterface() request.

The USB won't (shouldn't) do much until you send the SetConfiguration() and SetInterface() requests.

If the MSD is a Super-speed device, there is a good chance that it supports both the BBB and the UASP protocols. You have to tell it which one to use.

In general, a USB device usually only has one Configuration Descriptor. However, that doesn't mean you can assume so. Also, you must get the bConfigurationValue from the Configuration Descriptor and send the SetConfiguration(bConfigurationValue) request.

Then, you must choose an interface. In the listing you provide, the device only supports the BBB interface (under that configuration), however, you still have to tell it to use that interface with a SetInterface(bInterfaceNumber) request.

A quick look of your source code, I don't see where you do any of this.

Ben
- https://www.fysnet.net/the_universal_serial_bus.htm
SanderR
Member
Member
Posts: 73
Joined: Tue Aug 30, 2016 1:31 pm
Libera.chat IRC: SDR

Re: XHCI - bulk endpoint problems

Post by SanderR »

Thank you for your answer!
I just implemented the SET_INTERFACE command as you recommended.
When I execute this command, the system replies with a Stall Error.

The order I do things at: I have a small tread running, who walks over the event ring all the time, and then parsing the items that are in there.
https://github.com/AdeRegt/FramboOS/blo ... _watcher.c
from there it checks if the result TRB is a command completion event (https://github.com/AdeRegt/FramboOS/blo ... on_event.c) or a transfer event (https://github.com/AdeRegt/FramboOS/blo ... er_event.c)
This is the URL to the header: https://github.com/AdeRegt/FramboOS/blo ... ude/xhci.h

Am I missing something?
User avatar
BenLunt
Member
Member
Posts: 1029
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: XHCI - bulk endpoint problems

Post by BenLunt »

You should "activate" the endpoints after you set the configuration/interface.

You currently hard-code many of the items. For example, it looks like you are assuming the endpoints are 1, 2, and 3 respectively. You also assume the max packet size is 1024. If the device is using BBB, most likely the max packet size is 512.

All of this is in the Configuration and Interface (which includes the Endpoint) descriptors. It tells you which endpoint is the Bulk IN and which one is the Bulk OUT. It tells you the max packet size of each endpoint.

Granted, you are probably hard-coding them from the listing you received, hopefully temporarily, but you really do need to get this information from the descriptors, the whole reason USB was ever invented.

It is very difficult to start out like this and test with real hardware. It is time consuming and difficult to narrow down the errors. I highly suggest using an emulator, such as Bochs. With debug set to 1, Bochs will give you many warnings and descriptions of the process being made, making it much easier to narrow down the place where it "stops working".

Ben
Post Reply