USB Keyboard Boot Protocol [solved]

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
spiff
Rank 1
Rank 1
Posts: 24
Joined: Tue Apr 17, 2007 1:00 am
Location: Virum, Denmark
Contact:

USB Keyboard Boot Protocol [solved]

Post by spiff » Wed Jun 13, 2007 12:32 am

Hello all,

I'm trying to make an updated version of my C64 USB keyboard, and decided I want to try and implement the boot protocol (thereby allowing the keyboard to be used by the BIOS before the OS is started).

It seems I will need to use the standard report descriptor (instead of my slightly hacked one). Also, I apparently need to support the SET_PROTOCOL and GET_PROTOCOL HID requests. I assume I can just do this in usbFunctionSetup(), and if I my report descriptor conforms to the boot protocol, I wouldn't need to handle it in any special way (since both protocols would be identical).

How do I handle the 1 byte OUTPUT-report (Setting the status of the LEDs)? In which function should this be handled?

Are there any other things I need to be aware of when trying to convert my device to support the boot protocol?

Thanks in advance
Last edited by spiff on Fri Jun 15, 2007 3:30 pm, edited 1 time in total.

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Wed Jun 13, 2007 4:07 pm

I have not attempted to implement the boot protocol myself, but as far as I understand it, all you need to do is to use the standardized report descriptors.

New requets (such as SET and GET_PROTOCOL) must be implemented in usbFunctionSetup(). If a request has a data phase, you must expect that usbFunctionRead() or usbFunctionWrite() are called afterwards. Status must be communicated in global variables between usbFunctionSetup() and usbFunctionRead/Write().

spiff
Rank 1
Rank 1
Posts: 24
Joined: Tue Apr 17, 2007 1:00 am
Location: Virum, Denmark
Contact:

Post by spiff » Thu Jun 14, 2007 8:47 pm

christian wrote:New requets (such as SET and GET_PROTOCOL) must be implemented in usbFunctionSetup(). If a request has a data phase, you must expect that usbFunctionRead() or usbFunctionWrite() are called afterwards. Status must be communicated in global variables between usbFunctionSetup() and usbFunctionRead/Write().


Thanks for the reply. I have been experimenting a bit with this, but I am stuck.

I tried implementing this simple write-function for testing:

Code: Select all

uchar usbFunctionWrite(uchar *data, uchar len) {
   PORTD^=0x02;
   return 1;
}


I have a led attatched to PORTD bit 1, which should be toggled when the function is called.

But when i enable the write function (setting USB_CFG_IMPLEMENT_FN_WRITE to 1) i get an error message from Windows when plugging in my device. The message is: USB Device Not Recognized. This is even if I don't return 0xFF from usbFunctionSetup().

However, if I put the LED toggling code in my usbFunctionSetup, in the part that handles the USBRQ_HID_SET_REPORT, I can see that this really gets called whenever i change the state of the LEDs (Caps, Num, Scroll-lock).

What could be causing these problems when I enable the write function?[/code]

Bob
Posts: 17
Joined: Sun Jun 10, 2007 7:10 pm
Location: Melbourne

Post by Bob » Fri Jun 15, 2007 9:52 am

I don't know if this helps but there are 2 tables I found for boot compliant keyboards.

This first code is a table I've seen used on the PIC18F based USB keyboard but should also be useful AVR-USB.

Code: Select all

    0x05, 0x01,  // Usage Page (Generic Desktop),
    0x09, 0x06,  // Usage (Keyboard),
    0xA1, 0x01,  // Collection (Application),
    0x05, 0x07,  //   Usage Page (Key Codes);
    0x19, 0xE0,  //   Usage Minimum (224),
    0x29, 0xE7,  //   Usage Maximum (231),
    0x15, 0x00,  //   Logical Minimum (0),
    0x25, 0x01,  //   Logical Maximum (1),
    0x75, 0x01,  //   Report Size (1),
    0x95, 0x08,  //   Report Count (8),
    0x81, 0x02,  //   Input (Data, Variable, Absolute),   ; Modifier byte
    0x95, 0x01,  //   Report Count (1),
    0x75, 0x08,  //   Report Size (8),
  #0x81, 0x01,  //   Input (Constant),                   ; Reserved byte
    0x95, 0x05,  //   Report Count (5),
    0x75, 0x01,  //   Report Size (1),
    0x05, 0x08,  //   Usage Page (Page# for LEDs),
    0x19, 0x01,  //   Usage Minimum (1),
    0x29, 0x05,  //   Usage Maxmimum (5),
    0x91, 0x02,  //   Output (Data, Variable, Absolute),  ; LED report
    0x95, 0x01,  //   Report Count (1),
    0x75, 0x03,  //   Report Size (3),
  #0x91, 0x01,  //   Output (Constant),                  ; LED report padding
    0x95, 0x06,  //   Report Count (6),
    0x75, 0x08,  //   Report Size (8),
    0x15, 0x00,  //   Logical Minimum (0),
    0x25, 0x65,  //   Logical Maximum (101),
    0x05, 0x07,  //   Usage Page (Key Codes),
    0x19, 0x00,  //   Usage Minimum (0),
    0x29, 0x65,  //   Usage Maximum (101),
    0x81, 0x00,  //   Input (Data, Array),                ; Key arrays (6 bytes)
    0xC0           // End Collection


This second table is an example table that is included with the HID desciptor tool.

Code: Select all

    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
  #0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    0x95, 0x05,                    //   REPORT_COUNT (5)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
    0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
  #0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0                           // END_COLLECTION



The only differences with the above tables is the input and output formats, note the # where there is a difference.
Hope this helps,

Bob.

spiff
Rank 1
Rank 1
Posts: 24
Joined: Tue Apr 17, 2007 1:00 am
Location: Virum, Denmark
Contact:

usbWrite does not work.

Post by spiff » Fri Jun 15, 2007 10:55 am

Thanks for the reply.

Bob wrote:This second table is an example table that is included with the HID desciptor tool.


For my experiments so far, I have been using the one that is included with the HID descriptor tool. I actually wanted to rearrange it so the full INPUT report is first in the descriptor, then the OUTPUT report, but it turns out the size of the descriptor is the same (63 bytes), so I don't think there is any reason to use anything other than the default boot descriptor.

My device works fine with this descriptor. I can type text on the keyboard and Windows accepts the data. Using USBsnoop I can also see that an output report is sent to my device when I use my other keyboard to change the state of the caps-, num-, and scroll lock, so it seems the report descriptor is OK, as far as telling the host that it wants reports for the LEDs.

But trying to handle these reports create problems. The setup package is received correctly in usbFunctionSetup(), with a USBRQ_HID_SET_REPORT.

Reading the documentation it seems I need to return 0xFF in this case and implement the usbFunctionWrite() to handle the actual data. But when I enable the write-function in usbconfig.h my device is no longer detected by Windows when I plug it in.

It seems like the usbFunctionWrite() is used for handling some of the setup packages that appear when the device is first plugged in. When compiling without usbFunctionWrite() some default handling is used, which works correctly. But I thought the usbFunctionWrite would only be called if usbFunctionSetup returned 0xFF. Strange.

spiff
Rank 1
Rank 1
Posts: 24
Joined: Tue Apr 17, 2007 1:00 am
Location: Virum, Denmark
Contact:

Working!

Post by spiff » Fri Jun 15, 2007 3:29 pm

I managed to get this working.

It turned out that the problem was related to using an older version of the Obdev stack. :oops:

I had simply used the one already in my source directory with my C64 keyboard firmware. I decided to give it a try and downloaded the newest version, and after making appropriate updates to usbconfig.h it now seems to be working :D

Thanks for any help anyways. The new version will be released soon :wink:

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Fri Jun 15, 2007 7:38 pm

I'm glad to read that it works now!

Although it's interesting that the old driver did not work with usbFunctionWrite(). I know about no bug which caused usbFunctionWrite() to fail in any previous versions.

Maybe your extensions moved the RAM layout around and the alignment requirements for the USB receive buffer was not met. We have dropped this requirement in the latest version.

spiff
Rank 1
Rank 1
Posts: 24
Joined: Tue Apr 17, 2007 1:00 am
Location: Virum, Denmark
Contact:

Post by spiff » Fri Jun 15, 2007 9:45 pm

christian wrote:Maybe your extensions moved the RAM layout around and the alignment requirements for the USB receive buffer was not met. We have dropped this requirement in the latest version.


This is certainly possible. I had seen the discussion about the alignment problem in the header file, but have not done anything to check or fix it. But the important thing is that it works now :D

Post Reply