Author Topic: Need a USB guru! "USB device cannot start (code 10)"  (Read 5276 times)

0 Members and 1 Guest are viewing this topic.

Offline metalliqaz

  • * Maker
  • Thread Starter
  • Posts: 4951
  • Location: the Making Stuff subforum
  • Leopold fanboy
Need a USB guru! "USB device cannot start (code 10)"
« on: Sun, 16 March 2014, 09:17:17 »
Hey guys, I hope someone who really knows their USB HID stuff can help me out with this one.  Soarer maybe? :)

I'm adding NKRO input to my project.  In order to cooperate nicely with other parts of the design, I want to use a layout like this:

4 endpoints:
  Boot keyboard
  Boot mouse
  Media keys
  NKRO keyboard   <- this is new

So I've added the new endpoint, and I'm using this as my descriptor:

Code: [Select]
const USB_Descriptor_HIDReport_Datatype_t PROGMEM NkroReport[] =
{
HID_RI_USAGE_PAGE(8, 0x01),
HID_RI_USAGE(8, 0x07), // keypad instead of keyboard (0x06), I've tried both
HID_RI_COLLECTION(8, 0x01),
HID_RI_USAGE_PAGE(8, 0x07),
HID_RI_USAGE_MINIMUM(8, 0x00),
HID_RI_USAGE_MAXIMUM(8, 0x7F),
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x7F),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_END_COLLECTION(0)
};

It's a standard 16-byte bit-field.  I thought this was pretty established as the best way to do NKRO.

The idea is that I will always use the Boot keyboard for LEDs and modifiers. Also use the Boot keyboard for alpha keys when set to Boot protocol.  Then, when in report protocol, use the NKRO keyboard for alphas.  I have not added this yet.  Right now, I'm just trying to get the NKRO keyboard to be recognized.

With the new layout of endpoints, everything else works, but the 4th endpoint is marked as failed in Windows.  I get "USB device cannot start (code 10)"

Does anyone know why?

Edit: for typos
« Last Edit: Sun, 16 March 2014, 09:20:02 by metalliqaz »

Offline metalliqaz

  • * Maker
  • Thread Starter
  • Posts: 4951
  • Location: the Making Stuff subforum
  • Leopold fanboy
Re: Need a USB guru! "USB device cannot start (code 10)"
« Reply #1 on: Sun, 16 March 2014, 12:18:20 »
Well, I decided to put what I was doing on the back burner for a minute, and I played around with my normal configuration, with one small change.

Instead of the usual 6-byte array for keyboard reports, I extended it to 12 bytes.  I understand this isn't boot protocol compliant, but I just wanted to see how it worked.

To my surprise, the keyboard did nothing.  Windows didn't detect any errors, or throw any warnings, but no keys are recognized.  It's just dead.  I know the firmware is happy because special function keys still work (I could hit the Fn combination to return to boot mode, for instance).

I didn't think Windows would be so picky about this stuff.  I'm really wondering how you guys get NKRO to work.  (I'm talking to Hasu and Soarer, basically)



Edit: Ignore that stufff.  It was a coding error.  Works with extended buffer... sort of.
« Last Edit: Sun, 16 March 2014, 12:28:57 by metalliqaz »

Offline metalliqaz

  • * Maker
  • Thread Starter
  • Posts: 4951
  • Location: the Making Stuff subforum
  • Leopold fanboy
Re: Need a USB guru! "USB device cannot start (code 10)"
« Reply #2 on: Sun, 16 March 2014, 12:35:59 »
So I just took a look at the code for TMK.  Looks like Hasu defines two complete keyboards... one is a Boot keyboard and one is similar to a boot keyboard but with the array replaced with a bit field like the one I posted.  What isn't clear to me is how the software knows which interface to use.  Ideally, we would want the normal keyboard when talking to a BIOS (BootProtocol), but the NKRO keyboard when talking to the OS (ReportProtocol).

I'm digging through USB HID 1.11, not exactly clear how we are expected to change protocols at run-time.

Edit:  Okay, I'm pretty sure I've figured out how the "Boot protocol" is supposed to work.  Really a pretty nice design.  I'm going to go implement it.
« Last Edit: Sun, 16 March 2014, 12:57:17 by metalliqaz »

Offline hasu

  • Posts: 3472
  • Location: Tokyo, Japan
  • @tmk
    • tmk keyboard firmware project
Re: Need a USB guru! "USB device cannot start (code 10)"
« Reply #3 on: Sun, 16 March 2014, 13:34:41 »
I think REPORT_COUNT should be 0x80. Not sure this is a cause of your problem.

In tmk to use NKRO keyboard you need to switch explicitly with special key combination while Soarer's switches between Boot and NKRO automatically IIRC.

BTW, when you change endpoint configuration you will need to remove drivers from Device Manager to get Windows recognize new configuration. This made me irritated when I developed firmware in WIndows.

Offline Grendel

  • Posts: 462
  • Location: OR, USA
    • Firmware for Costar Replacement Controllers
Re: Need a USB guru! "USB device cannot start (code 10)"
« Reply #4 on: Sun, 16 March 2014, 14:39:54 »
BTW, when you change endpoint configuration you will need to remove drivers from Device Manager to get Windows recognize new configuration. This made me irritated when I developed firmware in WIndows.

^ Fell into that trap before too.
Currently using: RK-9000WH/GR, CMS QFXT w/ Ghost Squid
- I'm game !

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Need a USB guru! "USB device cannot start (code 10)"
« Reply #5 on: Sun, 16 March 2014, 16:32:47 »
I put the LEDs into my NKRO report, but I probably don't need to (now that I run both at the same time).

I don't switch as such, but the report descriptor for the Boot endpoint tells the host that there are no keys iin it. When in Boot protocol mode I inhibit output from anything other than the Boot endpoint.

This is my NKRO descriptor (with the Media key and the Linux bug fixes removed, since they complicate it)...

Code: [Select]
#define USB_FIRST_KEY_BIT 1
#define USB_LAST_KEY_BIT 0xA4
#define USB_NUM_KEY_BITS (USB_LAST_KEY_BIT - USB_FIRST_KEY_BIT + 1)
#define USB_NUM_KEY_BIT_BYTES ((USB_NUM_KEY_BITS + 7) / 8)
#define USB_NUM_PADDING_KEY_BITS (8 * USB_NUM_KEY_BIT_BYTES - USB_NUM_KEY_BITS)


static uint8_t PROGMEM keyboard_hid_report_desc[] = {
    0x05, 0x01,          // Usage Page (Generic Desktop),
    0x09, 0x06,          // Usage (Keyboard),
    0xA1, 0x01,          // Collection (Application),

    0x75, 0x01,          //   Report Size (1),
    0x95, 0x08,          //   Report Count (8),
    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),
    0x81, 0x02,          //   Input (Data, Variable, Absolute), ;Modifier byte

    0x95, 0x05,          //   Report Count (5),
    0x75, 0x01,          //   Report Size (1),
    0x05, 0x08,          //   Usage Page (LEDs),
    0x19, 0x01,          //   Usage Minimum (1),
    0x29, 0x05,          //   Usage Maximum (5),
    0x91, 0x02,          //   Output (Data, Variable, Absolute), ;LED report

    0x95, 0x01,          //   Report Count (1),
    0x75, 0x03,          //   Report Size (3),
    0x91, 0x03,          //   Output (Constant),                 ;LED report padding

    0x75, 0x01, //   Report Size (1),
    0x95, USB_NUM_KEY_BITS, //   Report Count (),
    0x05, 0x07, //   Usage Page (Key Codes),
    0x19, USB_FIRST_KEY_BIT,    //   Usage Minimum (),
    0x29, USB_LAST_KEY_BIT,     //   Usage Maximum (),
    0x15, 0x00, //   Logical Minimum (0),
    0x25, 0x01, //   Logical Maximum (1),
    0x81, 0x02, //   Input (Data, Variable, Absolute), ;keys bit array

#if USB_NUM_PADDING_KEY_BITS
    0x95, USB_NUM_PADDING_KEY_BITS, //   Report Count (),
    0x75, 0x01, //   Report Size (1),
    0x81, 0x03, //   Input (Constant),                 ;Padding
#endif

    0xC0,                 // End Collection
};
« Last Edit: Sun, 16 March 2014, 16:37:10 by Soarer »

Offline metalliqaz

  • * Maker
  • Thread Starter
  • Posts: 4951
  • Location: the Making Stuff subforum
  • Leopold fanboy
Re: Need a USB guru! "USB device cannot start (code 10)"
« Reply #6 on: Sun, 16 March 2014, 18:27:41 »
Hey guys, thanks for the help!!!  Hasu, you were right about the count, thanks.

After reading the HID 1.11 spec a couple of times, I finally figured out what we're supposed to do.  I'm supporting NKRO now with only one Keyboard Endpoint.

The spec says that the report can be as long as you want, but in boot mode, the first 8 bytes have to conform to the default boot keyboard report.  You can do this no matter how your report is actually described.  So I simply lay them both on top of each other!

My descriptor is copied from the boot keyboard spec, but with the 6-byte array replaced with the 16-byte bitfield from my first post.  I don't have two keyboard endpoints, just the one.
Code: [Select]
HID_RI_USAGE_PAGE(8, 0x01),
HID_RI_USAGE(8, 0x06),
HID_RI_COLLECTION(8, 0x01),
HID_RI_USAGE_PAGE(8, 0x07),
HID_RI_USAGE_MINIMUM(8, 0xE0),
HID_RI_USAGE_MAXIMUM(8, 0xE7),
HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_REPORT_COUNT(8, 0x08),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_INPUT(8, HID_IOF_CONSTANT),

HID_RI_USAGE_PAGE(8, 0x08),
HID_RI_USAGE_MINIMUM(8, 0x01),
HID_RI_USAGE_MAXIMUM(8, 0x05),
HID_RI_REPORT_COUNT(8, 0x05),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE),
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_REPORT_SIZE(8, 0x03),
HID_RI_OUTPUT(8, HID_IOF_CONSTANT),

HID_RI_LOGICAL_MINIMUM(8, 0x00),
HID_RI_LOGICAL_MAXIMUM(8, 0x01),
HID_RI_USAGE_PAGE(8, 0x07),
HID_RI_USAGE_MINIMUM(8, 0x00),
HID_RI_USAGE_MAXIMUM(8, 0x7F),
HID_RI_REPORT_COUNT(8, 0x80),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
HID_RI_END_COLLECTION(0)

Here it is as a C structure:

Code: [Select]
typedef struct
{
uint8_t Modifier;
uint8_t Reserved;
uint8_t KeyCode[16];
} ATTR_PACKED USB_KeyboardModReport_Data_t;

The trick is, I fill out the KeyCode array differently depending on how the host has called SetReport.  If it is in boot mode, the first 6 bytes are filled in as scancodes.  If it is in report mode, I fill in the 16 bytes as if they were a 128-bit packed field.

My CALLBACK_HID_Device_CreateHIDReport (I'm using LUFA) is like this, simplified:

Code: [Select]
if (HIDInterfaceInfo->State.UsingReportProtocol)
{
// fill in NKRO bits
}
else
{
// fill in boot keyboard bytes
}

I'm using it right now, so it seems to be working.  I just need to find an old PC to attempt BIOS compatibility...