Daniel Johnson
Daniel Johnson

Reputation: 351

Generic keyboard emulation using Arduino Leonardo

I am trying to use an Arduino Leonardo for keyboard emulation since that is one of its advertised features. I have been playing with the Leonardo and wrote a very simple sketch based off the Blink example.

// Pin 13 has an LED connected on most Arduino boards.
int led = 13;

// The setup routine runs once when you press reset:
void setup()
{
    // Initialize the digital pin as an output.
    pinMode(led, OUTPUT);
}

// The loop routine runs over and over again forever:
void loop()
{
    delay(5000);              // Wait for five seconds
    digitalWrite(led, HIGH);  // Turn the LED on (HIGH is the voltage level)
    delay(100);               // Wait for a secondA
    digitalWrite(led, LOW);   // Turn the LED off by making the voltage LOW
    Keyboard.write('A');      // Write an A using keyboard emulator
}

Now this example works great on my Windows XP machine. It is recognized as a generic HID keyboard and will type an 'A' every five seconds.

The targeted environment is running DOS so to my knowledge I need to get the Arduino Leonardo recognized by the BIOS on this machine, which it is not and it does not type the 'A' in DOS.

I have been making modifications to the USB device descriptor of the Leonardo, trying to make it match a Dell L100 as much as possible. The modifications I've made haven't helped, and I suspect it is because the Leonardo is a composite device which exposes the serial communication, keyboard emulation, and mouse emulation on the same USB port. The source code for the USB descriptors/configuration is available to be edited and is located at:

The following is a dump from USB Device Viewer for the Leonardo:

[Port3]  :  USB Composite Device

          ---===>Device Information<===---
English product name: "Arduino Leonardo"

ConnectionStatus:
Current Config Value:              0x01  -> Device Bus Speed: Full
Device Address:                    0x03
Open Pipes:                           4

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x81  -> Direction: IN - EndpointID: 1
bmAttributes:                      0x03  -> Interrupt Transfer Type
wMaxPacketSize:                  0x0010 = 0x10 bytes
bInterval:                         0x40

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x02  -> Direction: OUT - EndpointID: 2
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x00

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x83  -> Direction: IN - EndpointID: 3
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x00

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x84  -> Direction: IN - EndpointID: 4
bmAttributes:                      0x03  -> Interrupt Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x01

          ===>Device Descriptor<===
bLength:                           0x12
bDescriptorType:                   0x01
bcdUSB:                          0x0110
bDeviceClass:                      0x00
*!*ERROR: device class should be Multi-interface Function 0xEF
          When IAD descriptor is used
bDeviceSubClass:                   0x00
*!*ERROR: device SubClass should be USB Common Sub Class 2
          When IAD descriptor is used
bDeviceProtocol:                   0x00
*!*ERROR: device Protocol should be USB IAD Protocol 1
          When IAD descriptor is used
bMaxPacketSize0:                   0x40 = (64) Bytes
idVendor:                        0x2341 = Vendor ID not listed with USB.org as of 03-19-2008
idProduct:                       0x8036
bcdDevice:                       0x0100
iManufacturer:                     0x01
     English (United States)  "Arduino LLC"
iProduct:                          0x02
     English (United States)  "Arduino Leonardo"
iSerialNumber:                     0x00
bNumConfigurations:                0x01

          ===>Configuration Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x02
wTotalLength:                    0x0064  -> Validated
bNumInterfaces:                    0x03
bConfigurationValue:               0x01
iConfiguration:                    0x00
bmAttributes:                      0x80  -> Bus Powered
MaxPower:                          0xFA = 500 mA

          ===>IAD Descriptor<===
bLength:                           0x08
bDescriptorType:                   0x0B
bFirstInterface:                   0x00
bInterfaceCount:                   0x02
bFunctionClass:                    0x02  -> This is Communications (CDC Control) USB Device Interface Class
bFunctionSubClass:                 0x01
bFunctionProtocol:                 0x01
iFunction:                         0x00

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x00
bAlternateSetting:                 0x00
bNumEndpoints:                     0x01
bInterfaceClass:                   0x02  -> This is Communications (CDC Control) USB Device Interface Class
bInterfaceSubClass:                0x01
bInterfaceProtocol:                0x00
CAUTION:  This may be an invalid bInterfaceProtocol
iInterface:                        0x00
  -> This is a Communications (CDC Control) USB Device Interface Class

          ===>Descriptor Hex Dump<===
bLength:                           0x05
bDescriptorType:                   0x24
05 24 00 10 01
  -> This is a Communications (CDC Control) USB Device Interface Class

          ===>Descriptor Hex Dump<===
bLength:                           0x05
bDescriptorType:                   0x24
05 24 01 01 01
  -> This is a Communications (CDC Control) USB Device Interface Class

          ===>Descriptor Hex Dump<===
bLength:                           0x04
bDescriptorType:                   0x24
04 24 02 06
  -> This is a Communications (CDC Control) USB Device Interface Class

          ===>Descriptor Hex Dump<===
bLength:                           0x05
bDescriptorType:                   0x24
05 24 06 00 01

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x81  -> Direction: IN - EndpointID: 1
bmAttributes:                      0x03  -> Interrupt Transfer Type
wMaxPacketSize:                  0x0010 = 0x10 bytes
bInterval:                         0x40

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x01
bAlternateSetting:                 0x00
bNumEndpoints:                     0x02
bInterfaceClass:                   0x0A  -> This is a CDC Data USB Device Interface Class
bInterfaceSubClass:                0x01
bInterfaceProtocol:                0x00
CAUTION:  This may be an invalid bInterfaceProtocol
iInterface:                        0x00

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x02  -> Direction: OUT - EndpointID: 2
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x00

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x83  -> Direction: IN - EndpointID: 3
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x00

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x02
bAlternateSetting:                 0x00
bNumEndpoints:                     0x01
bInterfaceClass:                   0x03  -> HID Interface Class
bInterfaceSubClass:                0x01
bInterfaceProtocol:                0x00
CAUTION:  This may be an invalid bInterfaceProtocol
iInterface:                        0x00

          ===>HID Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x21
bcdHID:                          0x0101
bCountryCode:                      0x00
bNumDescriptors:                   0x01
bDescriptorType:                   0x22
wDescriptorLength:               0x0065

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x84  -> Direction: IN - EndpointID: 4
bmAttributes:                      0x03  -> Interrupt Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                         0x01

How could I get this Leonardo recognized by a BIOS as a generic keyboard?

Upvotes: 6

Views: 10792

Answers (2)

darBis
darBis

Reputation: 11

I might be mistaken, but if you want your device to be recognized during boot, its bInterfaceSubClass should be 0x01 (boot device subclass).

In source files, as you have pointed out, sub class is set to 0x00 everywhere.

You may try to set the subclass in all usages of the D_INTERFACE macro from file USBCore.h to 0x01.

I'm not an Arduino user. I was only once playing with HID keyboard emulation on AVR using V-USB. You can try to find detailed information about usb descriptors at USB.org in documentation. But it's quite a pile of pages to read.

Here is part od HID descriptor dump of my keyboard, notice values in Interface Descriptor:

Bus 005 Device 002: ID 046d:c30e Logitech, Inc. UltraX Keyboard (Y-BL49)
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x046d Logitech, Inc.
  idProduct          0xc30e UltraX Keyboard (Y-BL49)
  bcdDevice            1.80
  iManufacturer           1 Logitech
  iProduct                2 HID compliant keyboard
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           59
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      1 Boot Interface Subclass
      bInterfaceProtocol      1 Keyboard
      iInterface              0
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      62

The dump was made using "lsusb -v" as root under Linux.

Upvotes: 1

Nia
Nia

Reputation: 36

Arduino does not support the boot protocol. I have started using Teensy (which is Arduino compatible), and it works pretty well.

Upvotes: 2

Related Questions