Saturday, May 22, 2010

Joystick pong (how to write custom usb drivers for the nanoboard)

I've just worked out how to write custom drivers for the nanoboard to extend the capabilities of the platform through different USB devices. It isn't really that difficult for similar devices (you could just copy things across), but for more complicated devices you need to understand how the USB protocol works. The nanoboard USB host driver already takes care of a lot of the details for you. The specific device driver that you are writing only has to indicate if it wants to be a device driver for a particular detected device. The device driver call basically does a test using the data that is passed in. This data comes straight from the interface and device descriptors that were received from the bus. This is sometimes where the problems start. My joystick on Windows isn't recognized as a joystick per se. It's a Human Interface Device without a boot capability. It's working properly in games, but that's about it. The HID protocol isn't passed by it (which should be 04, for joysticks). This causes issues for people writing device drivers, because you can't know what type of device something is.

Also, not all joysticks pass the same ranges of data. The USB HID descriptor does indicate the ranges for USB, but this complicates things a bit for the device driver writer.

Anyway, the sample joystick driver is here and a new version of the pong game is here.
( the joystick driver should be put in the "Library/Software Platform/services/usbhost" directory of Altium designer).

In order to write drivers for other devices, it's helpful to boot into Linux and inspect the descriptors that are sent across the wire. A useful page comes from a guy who has rewired his old joystick to make it USB compatible. Other useful pages are the HID tools page and a page explaining how to use usbmon software to analyze the USB in more detail. Wireshark can also capture things from the USB. (usbmon on my lucid ubuntu seems to have been compiled into the kernel already. Just see if the /sys/kernel/debug/usb tree is there).

You can then use the kernel debug interface to inspect what's going on in the system. Under linux, cd'ing to:
/sys/kernel/debug/usb
will give you a devices file, which is useful to find out how a device is recognized. My joystick is incorrect, because it doesn't provide subclasses or protocols, as can be seen here:

T: Bus=03 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=1.5 MxCh= 0
D: Ver= 1.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
P: Vendor=046d ProdID=c215 Rev= 2.04
S: Manufacturer=Logitech
S: Product=Logitech Extreme 3D
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr= 30mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=00 Prot=00 Driver=usbhid
E: Ad=81(I) Atr=03(Int.) MxPS= 7 Ivl=10ms

Compare that to the mouse (but notice how the same driver is used):
T:  Bus=07 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=1.5 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
P: Vendor=046d ProdID=c018 Rev=43.01
S: Manufacturer=Logitech
S: Product=USB Optical Mouse
C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=usbhid
E: Ad=81(I) Atr=03(Int.) MxPS= 5 Ivl=10ms
Another cool thing to do is to look under:
/sys/kernel/debug/hid
This will show a couple of weird ID's, but those are the currently attached devices. In those directories, you can inspect the rdesc file for each of them. The rdesc file describes the format of the data, so indicates to the host device driver how it should interpret the data stream.

Devices may have multiple interfaces. The keyboard and mouse typically have a 'boot' interface, which allows the devices to be configured in the boot sequence. Notice that under linux, the same 'usbhid' driver takes care of most HID devices. This is because of the rdesc file, indicating the way how the driver should interact with the device. I'm not exactly sure at the moment how the driver would communicate this to the applications or HAL layer above it in a generic way.

Notice that HID are not always strictly input devices. Some HID's have outputs (device input) which allow the setting of LED's, force feedback, etc.

So how does one read these HID files to discover the data format? Let's look at a simpler example, the mouse:
Field(0)
Physical(GenericDesktop.Pointer)
Usage(3)
Button.0001
Button.0002
Button.0003
Logical Minimum(0)
Logical Maximum(1)
Report Size(1)
Report Count(3)
Report Offset(0)
Flags( Variable Absolute )
Field(1)provides
Physical(GenericDesktop.Pointer)
Usage(3)
GenericDesktop.X
GenericDesktop.Y
GenericDesktop.Wheel
Logical Minimum(-127)
Logical Maximum(127)
Report Size(8)
Report Count(3)
Report Offset(8)
Flags( Variable Relative )
The logical minimum and maximum determine the value range. This could also be used to rescale the values if you want a consistent output. The "report Size" is the size in bits of one individual field. "Usage(x)" indicates the number of fields that are being described. Report Count(x) does the same. The Report offset(x) describes the offset of these fields within the entire data stream. So, this mouse protocol uses the three bits in the first byte to describe the state of up to three buttons. It uses 3 bytes after that, indicating X and Y motions and the motions of the wheel.

This is basically how I figured out how my joystick worked. You could also specify your own rdesc / hid file using the HID tools mentioned above. Of course, computers don't need to communicate in ASCII, they'd use the compiled HID information as an array of bytes to convey the same information. The manufacturer and product ID are 16-bit fields that need to be looked up in a file on the host somewhere, if one cares about it.

Update: Here's another version using a lower resolution and lower color palette. Because of the USB code and the other drivers, the generated ROM image is larger than 32k and doesn't fit in local processor memory. This requires the use of a small amount of RAM, which constantly reads in new code to execute whenever required from memory. This memory (SRAM) is the external RAM on the nanoboard, which is shared with the VGA driver. Because the VGA driver gets priority, the game slows down whenever the processor tries to update the frame (because the processor cannot read in code to execute from SRAM memory at the same time the frame is being drawn to the monitor).

2 comments:

288clandestino said...

passando rápido por aqui só pra gritar

BRASIL!!!

bom jogo gerard. saudades sempre, você é uma referência abraço!

Gerard Toonstra said...

aeeeeeeeeeeeeeeeeeeeee