Ok, so here’s the story. The PICkit2 announces itself as a HID (human interface device). Why? I’m not sure, but I think it’s because that way Microchip avoid the need for a host-side driver. On FreeBSD it shows up as /dev/uhid0 – a “generic” HID. On Windows and the Mac, things are similar.

HID devices exchange report info with the host – the current settings of the controls on the device. A keyboard sends key presses (and releases?). A mouse sends its X, Y, Z (scroll), and button states. Etc. The layout of this report is defined by a report descriptor, which the USB device sends to the host when the device is configured. This descriptor is essentially a small (binary) program in a bytecoded language! The report descriptor parser – really an interpreter – on the host figures what the fields of the report mean, what values they can take, etc.

If this all sounds hideously complicated, it is. This approach has the advantage of generality, but the obvious disadvantage that’s it’s just complicated enough that... but I get ahead of myself. The spec is HIDeously written, confusing, ambiguous, and generally awful.

I wouldn’t care about any of this except that I’m having trouble talking to the PICkit2 from FreeBSD. I seem to be able to read from it but I can’t write to it. And it doesn’t talk unless you talk to it first. ;-)

Its report descriptor defines a report of 64 bytes, and doesn’t associate any meaning with any of these bytes. Unlike a real HID, this is just a buffer of data. The firmware always reads and writes 64 bytes over USB. Fine.

The Microchip host-side (dotNET) code that talks to it always reads and writes 65 bytes, the first one (coming and going) always a zero. No explanation. I thought this might be a reportID, but the report descriptor doesn’t define one, and the firmware on the programmer doesn’t seem to send this extra byte, or expect it. Hmm. This is still a mystery.

Back to FreeBSD. I started to look at the code that parses the report descriptor, and got a bit nervous. I saw things that immediately seemed like bugs. Then I ran a utility (usbhidctl(8)) that prints out what it thinks is in the descriptor. Everythings looks good until the end:

 Total   input size 64 bytes
 Total  output size 0 bytes
 Total feature size 0 bytes

FreeBSD thinks the output report size is 0 bytes, and it forces me to stick with that – ie, opening /dev/uhid0 and trying to write more than 0 bytes will fail!