OSDev.org

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

All times are UTC - 6 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: UHCI framelist stuck
PostPosted: Thu Nov 03, 2022 9:20 am 
Offline
Member
Member
User avatar

Joined: Wed Feb 19, 2020 1:08 pm
Posts: 270
Location: Italy
The scheduler of the UHCI controller freezes at a specific index in the frame list.
After initializing the controller and checking which ports have something connected, I try to retrieve the device descriptor. I tried with only the problematic packets in the framelist to have a cleaner environment to inspect: these are the transfer descriptors present in the framelist:
Code:
print tds[0]
$1 = {link = 0x1, flags = 0x80002d, maxlen = 0xe00000, bufptr = 0x9fa00}
print tds[1]
$2 = {link = 0x1, flags = 0x800069, maxlen = 0xe80000, bufptr = 0x9fa20}

reading the IO_FRNUM register, it executes 0, goes to 1 and every subsequent read is always a 1.
The setup packet is the following:
Code:
print &setup
$3 = (usb_request_packet_t *) 0x9fa00
print setup
$4 = {type = 0x80, request = 0x6, value = 0x100, index = 0x0, size = 0x12}
print packets[0]
$5 = {type = USB_PACKET_TYPE_SETUP, maxlen = 0x8, toggle = 0x0, buffer = 0x9fa00}

and the next packet(s) just point to the device descriptor
Code:
print packets[1]
$7 = {type = USB_PACKET_TYPE_IN, maxlen = 0x8, toggle = 0x1, buffer = 0x9fa20}
print packets[2]
$8 = {type = USB_PACKET_TYPE_IN, maxlen = 0x8, toggle = 0x0, buffer = 0x9fa28}
...

Anyway, no point in showing them all and later the USB_PACKET_TYPE_OUT since it never reaches that packet. Are these data malformed in some way?
next post I'll put some meaningful code (keep in mind for the moment its mainly a collage of things I found on some repos)

_________________
Regards, Bonfra.


Top
 Profile  
 
 Post subject: Re: UHCI framelist stuck
PostPosted: Thu Nov 03, 2022 9:24 am 
Offline
Member
Member
User avatar

Joined: Wed Feb 19, 2020 1:08 pm
Posts: 270
Location: Italy
this functions takes an array of packets and posts them in the framelist for the controller to execute
Code:
usb_transfer_status_t transfer_packets(void* data, uint64_t addr, uint64_t endpoint, const usb_packet_t* packets, size_t num_packets)
    uhci_controller_t* controller = data;
    for(uint32_t i = 0; i < 1024; ++i)
        controller->frame_list[i] = FRAMELIST_TERMINATE;

    volatile alignas(0x20) transfer_descriptor_t tds[num_packets];

    for(size_t i = 0; i < num_packets; i++)
    {
        tds[i].link = TD_TERMINATE;
        tds[i].flags = TD_STATUS_ACTIVE;

        if(i == num_packets - 1)
            tds[i].flags |= TD_IOC;

        if(port_status(controller, addr) == USB_PORT_STATUS_CONNECT_LOW_SPEED)
            tds[i].flags |= TD_LOW_SPEED;
       
        tds[i].maxlen = ((packets[i].maxlen - 1) << 21) | (endpoint << 15) | (addr << 8);
       
        switch (packets[i].type)
        {
        case USB_PACKET_TYPE_SETUP: tds[i].flags |= TD_PID_SETUP; break;
        case USB_PACKET_TYPE_IN: tds[i].flags |= TD_PID_IN; break;
        case USB_PACKET_TYPE_OUT: tds[i].flags |= TD_PID_OUT; break;
        }

        if(packets[i].toggle)
            tds[i].maxlen |= TD_DATA_TOGGLE;

        tds[i].bufptr = (uint32_t)(uint64_t)packets[i].buffer;
        controller->frame_list[i] = (uint32_t)(uint64_t)&tds[i];
    }

    sched_run(controller);

    while(!(inportw(controller->io + IO_USBSTS) & USBSTS_INT)) // stuck here with inportw(controller->io + IO_FRNUM) == 1
        asm("pause");

    sched_stop(controller);

    return USB_TRANSFER_STATUS_OK;
}


and here the code that builds the packets
Code:
      alignas(0x20) usb_device_descriptor_t descriptor;

      alignas(0x20) usb_request_packet_t setup;
      setup.type = USB_REQUEST_DIR_DEVICE_TO_HOST | USB_REQUEST_TYPE_STANDARD;
      setup.request = USB_REQUEST_GET_DESCRIPTOR;
      setup.value = USB_DESCRIPTOR_DEVICE << 8;
      setup.index = 0;
      setup.size = sizeof(usb_device_descriptor_t);

      alignas(0x20) usb_packet_t packets[2 + setup.size / 8];
      memset(packets, 0, sizeof(packets));

      packets[0].type = USB_PACKET_TYPE_SETUP;
      packets[0].maxlen = 8;
      packets[0].buffer = &setup;

      int toggle = 1;
      int pid = 1;
      for(int i = 0; i < setup.size; i += 8)
      {
         packets[pid].type = USB_PACKET_TYPE_IN;
         packets[pid].maxlen = 8;
         packets[pid].buffer = (void*)&descriptor + i;
         packets[pid].toggle = toggle;
         toggle = toggle ? 0 : 1;
         pid++;
      }

      packets[pid].type = USB_PACKET_TYPE_OUT;
      packets[pid].maxlen = 0x800;
      packets[pid].buffer = (void*)0x80000000;
      packets[pid].toggle = 1;

       hci.driver->transfer_packets(hci.data, 0, 0, packets, pid);

_________________
Regards, Bonfra.


Top
 Profile  
 
 Post subject: Re: UHCI framelist stuck
PostPosted: Thu Nov 03, 2022 5:55 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
Bonfra wrote:
The scheduler of the UHCI controller freezes at a specific index in the frame list.
After initializing the controller and checking which ports have something connected, I try to retrieve the device descriptor. I tried with only the problematic packets in the framelist to have a cleaner environment to inspect: these are the transfer descriptors present in the framelist:
Code:
print tds[0]
$1 = {link = 0x1, flags = 0x80002d, maxlen = 0xe00000, bufptr = 0x9fa00}
print tds[1]
$2 = {link = 0x1, flags = 0x800069, maxlen = 0xe80000, bufptr = 0x9fa20}
As for the 'flags' DWORD, you should set the C_ERR field to 3. Also, shouldn't the 2D and 69 be in the 'maxlen' DWORD, not the 'flags' DWORD?
Is the device a low-speed device? If so, the low-speed bit in the 'flags' DWORD should be set. The SPD bit should be set in the DATA IN packet.
Also, if System Management is still active, writing to physical address 0x9FA00 and above is quite dangerous. You should probably try a different address.

Bonfra wrote:
reading the IO_FRNUM register, it executes 0, goes to 1 and every subsequent read is always a 1.
This means that the RUN/STOP bit in the Command register is now zero and the HCHalted bit should now be set. This is due to an error.
Since your 'maxlen' DWORD doesn't have the PID field set (the 2D and 69 above), this is probably the error.

Ben
- https://www.fysnet.net/the_universal_serial_bus.htm


Top
 Profile  
 
 Post subject: Re: UHCI framelist stuck
PostPosted: Fri Nov 04, 2022 7:35 am 
Offline
Member
Member
User avatar

Joined: Wed Feb 19, 2020 1:08 pm
Posts: 270
Location: Italy
BenLunt wrote:
Also, shouldn't the 2D and 69 be in the 'maxlen' DWORD, not the 'flags' DWORD?

it really was this... how could I've not noticed :[ yup just doing this returns a meaningful descriptor
Code:
$1 = {length = 0x12, type = 0x1, usb_release_num = 0x200, class = 0x0, subclass = 0x0, protocol = 0x0, max_packet_size = 0x8, vendor = 0x46f4, product = 0x1, dev_release_num = 0x0, manufacturer_index = 0x1, product_index = 0x2, serial_number_index = 0x3, num_configurations = 0x1}

BenLunt wrote:
The SPD bit should be set in the DATA IN packet.

Isn't this for interrupt stuff if the packet is shorter than expected? how is it useful in this context? (other than detecting wrong responses)

_________________
Regards, Bonfra.


Top
 Profile  
 
 Post subject: Re: UHCI framelist stuck
PostPosted: Fri Nov 04, 2022 3:54 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
Bonfra wrote:
BenLunt wrote:
The SPD bit should be set in the DATA IN packet.

Isn't this for interrupt stuff if the packet is shorter than expected? how is it useful in this context? (other than detecting wrong responses)

In this particular case, the SPD bit doesn't need to be set. However, in general, it should.

For example, if you have a Transfer Descriptor (TD) with a single SETUP, five INs, and a STATUS packet, what happens if the second IN packet is short?
If you don't enable the SPD, the controller will try to execute the remaining INs even though there is no more data to receive, finally (maybe) making it to the STATUS packet.
If you enable the SPD and set up your TD correctly, the controller will skip the remaining INs and go directly to the STATUS packet, automatically.

Place the SETUP and all INs in the 'depth' first direction within the Queue, then place the STATUS in the 'Horizontal' direction in the Queue. At the first Short Packet, the controller will stop executing in the depth first and move to the horizontal pointer, which should now be your STATUS packet. All remaining INs in the depth direction will be, for lack of a better term, discarded.

This makes it really easy when you retrieve an unknown length of data from the pipe. i.e.: There is no header telling you how much data you can retrieve.

Ben


Top
 Profile  
 
 Post subject: Re: UHCI framelist stuck
PostPosted: Fri Nov 04, 2022 11:33 pm 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
BenLunt wrote:
This makes it really easy when you retrieve an unknown length of data from the pipe. i.e.: There is no header telling you how much data you can retrieve.
But that happens very rarely, doesn't it? All the standard descriptors have a fixed length or else tell you the total length at a know offset. Are there any classes that have variable-length descriptors without a total length field?

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: UHCI framelist stuck
PostPosted: Sat Nov 05, 2022 8:37 am 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
nullplan wrote:
BenLunt wrote:
This makes it really easy when you retrieve an unknown length of data from the pipe. i.e.: There is no header telling you how much data you can retrieve.
But that happens very rarely, doesn't it? All the standard descriptors have a fixed length or else tell you the total length at a know offset. Are there any classes that have variable-length descriptors without a total length field?

Descriptors, not usually. But there is much more to USB than Descriptors. :-)

If your framework already has the idea of short packets at the Descriptor level, then when you get to Bulk and Interrupt pipes, it is a simple task to allow for short packets, letting the controller move on to the next item with no interaction with software because of the short packet.

If memory serves, one instance that I can think of is the Request Sense request. You are expecting 18 bytes, but can and usually receive a few less. But that comes from memory, so I may be thinking of something else.

Ben


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

All times are UTC - 6 hours


Who is online

Users browsing this forum: belliash, Bing [Bot] and 68 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