[Solved] UHCI set address and get descriptor 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
foliagecanine
Member
Member
Posts: 148
Joined: Sun Aug 23, 2020 4:35 pm

[Solved] UHCI set address and get descriptor problems

Post by foliagecanine »

Hello OSDev community,

I've been working on my operating system for a year or more.
Until now, I've been able to get pretty far with just the wiki and forum posts.
I've gotten terminal output, GDT, paging, interrupts, PS/2 keyboard and mouse support, FAT12/16 support, an AHCI driver, and program loading and usermode.

I've started working on USB support (right now I just want to get a mouse working), so I'm working on an UHCI driver since I think it's the simplest (also it's the first one mentioned in Benjamin Lunt's book). I've also read most of the UHCI spec (which is the most understandable spec I've seen from Intel).
I understand how the I/O ports work, queues, transfer descriptors, etc. I have mostly figured out how the different packets work, but I'll get there once I figure this out.

I had a working implementation of the Get Device Descriptor function and the Set Address function before, but since it was basically just merging Ben's code into my OS, I decided to scrap that code and rewrite it. However I (foolishly) did not save a copy of that code. I've rewritten it to be much easier to integrate into the system later and I'm trying to get multiple queues that run at 2^n intervals (as described in Ben's book). However, I've put that on hold for a bit because I can't seem to get what I have to work.

The first problem:

I try to get 8 bytes of the device descriptor, attempt to set the address, and then read all the bytes of the descriptor.
However, when I run my code as it is (see below for links), It succeeds in getting the descriptor the first time, "says" it succeeded with setting the address, and then fails to get the descriptor the second time (times out).
I've determined that the Set Address function fails, and therefore the Get Device Descriptor function is trying to request something from a nonexistent device (I've tested this theory by disabling the Set Address function and getting the descriptor from device address 0, which "works" (see below)).

I've dumped the I/O registers before and after each function, but they always turn up like this:

Code: Select all

USBCMD:		0x00C1
USBSTS:		0x0002
USBINTR:		0x0000
FRNUM:		(varies)
FLBASEADD:	0x00833000
PORTSC1: 	0x0085
PORTSC2: 	0x0080 (shouldn't worry about this register since the device is on port 0 (PORTSC1)
My code should check the TDs for errors, so I doubt it's those.

The second problem:

If I try to get the device descriptor without setting the address, it "works." However, it doesn't seem right:

Code: Select all

Length: 18
Descriptor type: 0x01
USB Version (BCD): 0x0200
Device Class: 0x00
Device Subclass: 0x00
Device Protocol: 0x00
Max Packet Size: 8
VendorID: 0x0106
ProductID: 0x0000
I'm using a QEMU emulated USB mouse:

Code: Select all

qemu-system-i386 -m 512M -cdrom myos.iso -s -serial stdio -usb -device usb-mouse
Which should have a descriptor that says:

Code: Select all

VendorID: 0x0627
ProductID: 0x0001
Could someone please help me find where I've made a mistake?
It's most likely a stupid mistake like a bit not set in a TD or a virtual instead of physical address, but whatever it is I can't figure it out.

Code Links
Main UHCI code: https://github.com/foliagecanine/tritiu ... 386/uhci.c
UHCI Header: https://github.com/foliagecanine/tritiu ... usb/uhci.h
USB Header: https://github.com/foliagecanine/tritiu ... /usb/usb.h
Full repository: https://github.com/foliagecanine/tritium-os

If you want to build it, see the read-me in the repository.

How to test
Download the ISO file.
Run

Code: Select all

qemu-system-i386 -m 512M -cdrom myos.iso -s -serial stdio -usb -device usb-mouse
When it boots up to GRUB, press enter.
It will boot and then say "No valid drive found"
Press shift to get to the debug console.
Type "usb" and press enter.

Thanks ahead of time.
Last edited by foliagecanine on Mon Aug 24, 2020 2:26 pm, edited 1 time in total.
My OS: TritiumOS
https://github.com/foliagecanine/tritium-os
void warranty(laptop_t laptop) { if (laptop.broken) return laptop; }
I don't get it: Why's the warranty void?
User avatar
iman
Member
Member
Posts: 84
Joined: Wed Feb 06, 2019 10:41 am
Libera.chat IRC: ImAn

Re: UHCI set address and get descriptor problems

Post by iman »

foliagecanine wrote:I'm working on an UHCI driver since I think it's the simplest
In my opinion, and as once Ben Lunt said, xHCI programming is more straight forward, then OHCI, UHCI, and finally EHCI.
foliagecanine wrote:I try to get 8 bytes of the device descriptor, attempt to set the address, and then read all the bytes of the descriptor.
I had a look at your uhci.c and io.c, and could not find your mem_read32 and mem_write32 functions, but only inw() and outw().
Maybe I was wrong, but how do you read and write registers?
Iman Abdollahzadeh
Github
Codeberg
foliagecanine
Member
Member
Posts: 148
Joined: Sun Aug 23, 2020 4:35 pm

Re: UHCI set address and get descriptor problems

Post by foliagecanine »

iman wrote:
foliagecanine wrote:I try to get 8 bytes of the device descriptor, attempt to set the address, and then read all the bytes of the descriptor.
I had a look at your uhci.c and io.c, and could not find your mem_read32 and mem_write32 functions, but only inw() and outw().
Maybe I was wrong, but how do you read and write registers?
For UHCI, the registers use Port I/O rather than MMIO.
UHCI is odd in this way, as all the others do it with MMIO.

I've read through OHCI a bit. I guess I'll read XHCI next (and skip EHCI for now).
My OS: TritiumOS
https://github.com/foliagecanine/tritium-os
void warranty(laptop_t laptop) { if (laptop.broken) return laptop; }
I don't get it: Why's the warranty void?
foliagecanine
Member
Member
Posts: 148
Joined: Sun Aug 23, 2020 4:35 pm

Re: UHCI set address and get descriptor problems

Post by foliagecanine »

Well, I found one stupid mistake.
It didn't fix either of the mentioned problems, but at least it narrows it down.

I was calling my uhci_set_address function like this

Code: Select all

uhci_set_address(*this_ctrlr,current_port,devaddr)
but the function is defined as

Code: Select all

bool uhci_set_address(uhci_controller uc, uint8_t dev_address, uint8_t port)
I accidentally swapped the device address and the port. It was trying to set a non-existant device on port 1 to address 0.

Still working on solving the other two problems...

But, I did manage to recreate working code the way I did last time, so I should be able to check the TDs' values to make sure I set all the bits correctly.

EDIT:

It looks like my set_address function works in QEMU

Code: Select all

(qemu) info usb
  Device 0.0, Port 1, Speed 12 Mb/s, Product QEMU USB Mouse
... after uhci_set_address...
(qemu) info usb
  Device 0.1, Port 1, Speed 12 Mb/s, Product QEMU USB Mouse
So it looks like it's just my get descriptor function that's broken right now.
My OS: TritiumOS
https://github.com/foliagecanine/tritium-os
void warranty(laptop_t laptop) { if (laptop.broken) return laptop; }
I don't get it: Why's the warranty void?
foliagecanine
Member
Member
Posts: 148
Joined: Sun Aug 23, 2020 4:35 pm

Re: UHCI set address and get descriptor problems

Post by foliagecanine »

Alright. I solved it!

I did a bunch of dumps of the TDs (since I now had a working implementation to compare to) and found several things.

1) I forgot to add the device address to the IN transfer descriptors.
2) I forgot to subtract 1 from the MAXLEN, so it was breaking the VendorID, and ProductID.

Since this post is mostly just me, I figure I'll add a checklist for those in the future who run into a similar situation

[ ] All queues, TDs, and framelist pointers are physical addresses
[ ] The pointers are valid
[ ] Check to make sure that there's no errors in the TDs or the USBSTS register
[ ] Check that the setup packet is correct
[ ] In the second dword of the TDs, check the lowspeed bit, that the error counter is 3, and the status byte has only the ACTIVE bit set
[ ] In the third dword of the TDs, check the MAXLEN (make sure you've subtracted 1 from the actual amount you want to transfer),
[ ] In the third dword of the TDs, make sure you toggle the Data Toggle every other TD except for the last one, which it should be a 1
[ ] In the third dword of the TDs, make sure you have the correct device address, and that the transfer type is correct (SETUP, IN, OUT)
[ ] Make sure that the framelist pointer is ORed with 2 if it is pointing to a queue.

I've ran into several of these problems on the checklist myself, so I hope this will be helpful to future OSDevers.

EDIT:
Is there a way to rename the topic to say [SOLVED] or something, or is that just for mods?
My OS: TritiumOS
https://github.com/foliagecanine/tritium-os
void warranty(laptop_t laptop) { if (laptop.broken) return laptop; }
I don't get it: Why's the warranty void?
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: UHCI set address and get descriptor problems

Post by nexos »

Edit your first post's title to say solved, and it will be marked as solved.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
foliagecanine
Member
Member
Posts: 148
Joined: Sun Aug 23, 2020 4:35 pm

Re: [Solved] UHCI set address and get descriptor problems

Post by foliagecanine »

Thank you nexos!
My OS: TritiumOS
https://github.com/foliagecanine/tritium-os
void warranty(laptop_t laptop) { if (laptop.broken) return laptop; }
I don't get it: Why's the warranty void?
Post Reply