UHCI framelist stuck

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
User avatar
Bonfra
Member
Member
Posts: 270
Joined: Wed Feb 19, 2020 1:08 pm
Libera.chat IRC: Bonfra
Location: Italy

UHCI framelist stuck

Post by Bonfra »

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

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

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

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.
User avatar
Bonfra
Member
Member
Posts: 270
Joined: Wed Feb 19, 2020 1:08 pm
Libera.chat IRC: Bonfra
Location: Italy

Re: UHCI framelist stuck

Post by Bonfra »

this functions takes an array of packets and posts them in the framelist for the controller to execute

Code: Select all

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

		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.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI framelist stuck

Post by BenLunt »

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

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
User avatar
Bonfra
Member
Member
Posts: 270
Joined: Wed Feb 19, 2020 1:08 pm
Libera.chat IRC: Bonfra
Location: Italy

Re: UHCI framelist stuck

Post by Bonfra »

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

$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.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI framelist stuck

Post by BenLunt »

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
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: UHCI framelist stuck

Post by nullplan »

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!
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: UHCI framelist stuck

Post by BenLunt »

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
Post Reply