Sadly I cannot communicate successfully with the device on real HW. the UHCI initialization phase appears to work as I'm able to enumerate the ports and find the attached device but when I try to send a control packet to set its address (so even before retrieving the default descriptor) the execution flow stops and I'm left waiting for the packets in the UHCI framelist to be processed.
Follow some parts of my code highlighting the process:
Code: Select all
// this functions expects a just resetted port to be present at address 0
void usb_register_device(usb_bus_t* bus)
{
uint64_t addr = alloc_address(bus);
// TODO: real hw hangs inside this function
if(usb_set_address(bus, addr) != USB_TRANSFER_STATUS_OK)
return;
kernel_log("Done\n");
// ....
}
usb_transfer_status_t usb_set_address(const usb_bus_t* bus, uint64_t addr)
{
usb_request_packet_t setup;
setup.type = USB_REQUEST_DIR_HOST_TO_DEVICE | USB_REQUEST_TYPE_STANDARD;
setup.request = USB_REQUEST_SET_ADDRESS;
setup.value = addr;
setup.index = 0;
setup.size = 0;
return usb_transfer_control_out(bus, 0, 0, &setup);
}
usb_transfer_status_t usb_transfer_control_out(const usb_bus_t* bus, uint64_t addr, uint64_t endpoint, void* setup)
{
usb_packet_t packets[2];
memset(packets, 0, sizeof(usb_packet_t) * 2);
packets[0].type = USB_PACKET_TYPE_SETUP;
packets[0].maxlen = 8;
packets[0].buffer = setup;
packets[1].type = USB_PACKET_TYPE_IN;
packets[1].maxlen = 0x800;
packets[1].buffer = NULL;
packets[1].toggle = 1;
return uhci_transfer_packets(bus->hci.data, addr, endpoint, packets, 2);
}
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;
volatile alignas(0x20) transfer_descriptor_t tds[num_packets];
for(size_t i = 0; i < num_packets; i++)
{
tds[i].link = i == num_packets - 1 ? TD_TERMINATE : ((uint32_t)(uint64_t)&tds[i + 1] | TD_DEPTH_FIRST);
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 ? packets[i].maxlen - 1 : 0) << 21) | (endpoint << 15) | (addr << 8);
switch (packets[i].type)
{
case USB_PACKET_TYPE_SETUP: tds[i].maxlen |= TD_PID_SETUP; break;
case USB_PACKET_TYPE_IN: tds[i].maxlen |= TD_PID_IN; break;
case USB_PACKET_TYPE_OUT: tds[i].maxlen |= TD_PID_OUT; break;
}
if(packets[i].toggle)
tds[i].maxlen |= TD_DATA_TOGGLE;
tds[i].bufptr = (uint32_t)(uint64_t)packets[i].buffer;
}
volatile alignas(0x20) queue_head_t qh;
qh.head_link = QH_TERMINATE;
qh.element_link = (uint32_t)(uint64_t)&tds[0];
for(uint32_t i = 0; i < 1024; ++i)
controller->frame_list[i] = FRAMELIST_TERMINATE;
controller->frame_list[0] = (uint32_t)(uint64_t)&qh | FRAMELIST_QH;
sched_run(controller);
while(!(inportw(controller->io + IO_USBSTS) & USBSTS_INT))
asm("pause");
sched_stop(controller);
return USB_TRANSFER_STATUS_OK;
}
Here and here the parts of my repo that contains a less simplified version of the code showed above.
At the moment I'm trying to retrieve various dumps of the data that gets sent in real HW but it's a bit of a slow process not having copy-paste and a really small screen. It's gonna take a while so for the time being I'm posting just this and I'll add in a subsequent post the dumps.
PS:
Since my OS wasn't designed with BOCHS in mind I was only able to test this thing on QEMU, being the only VM I know that emulates UHCI. If you happen to know any other VM that also emulates it please let me know as it would be interesting to know how my code behaves there