AnsweredAssumed Answered

USB HID keyboard - sending output report for LED control fails

Question asked by steveevers on Dec 17, 2015

I am evaluating the kinetis sample code for a USB HID keyboard (dev_hid_keyboard_frdmk22f_bm_frdmk22f) and having issues sending the device and output report to control keyboard LEDs.

 

The dev board running the sample code enumerates fine. I have modified to device code to report button presses as keyboard events and am able to "type" with using the few buttons on the board by populate to USB HID report with ascii HID codes.

 

The problem I have is when I try to send a output report to the device. To send the output report I am using libusb from a UNIX host to test the device. After the device enumerates on the linux host I run the libusb application below to detach it from the host OS and try to send it an output report to change the device LEDs (see g_report_descriptor entries below)

 

    0x05, 0x08,   /*   USAGE_PAGE (LEDs) */

    0x19, 0x01,   /*   USAGE_MINIMUM (Num Lock) */

    0x29, 0x05,   /*   USAGE_MAXIMUM (Kana) */

    0x91, 0x02,   /*   OUTPUT (Data,Var,Abs) pc->kbd */

    0x95, 0x01,   /*   REPORT_COUNT (1) */

    0x75, 0x03,   /*   REPORT_SIZE (3 */

    0x91, 0x01,   /*   OUTPUT (Cnst,Var,Abs) filupp to byte boundary */

 

After sending a output report to the device from a linux host machine I do in fact receive the application registered callback, USB_App_Class_Callback(), but the data pointer for the report is NULL.

 

The call stack is shown below. I see the report data sent from the host on a USB sniffer. I also see it in the device inside of the _usb_khci_service_tk_dne_intr() in the call stack below inside this methods local event.buffer_ptr buffer pointer. But because USB_Control_Service() never popluates the data (sets it to a NULL pointer) it never makes it way back to the application callback.

 

Anyone have success in working with this sample code and sending it an output report to control the keypad LEDs?

 

*******************************************

***** set report call stack on device *****

*******************************************

 

khci_dev.c::static void _usb_khci_isr(void)

  khci_dev.c::::static void _usb_khci_service_tk_dne_intr(

    usb_khci_dev_state_struct_t* state_ptr)

      event->setup=FALSE

      usb_dev.c::usb_status _usb_device_call_service(

        uint8_t  type,

        usb_event_struct_t* event)

          usb_dev.c::_usb_device_call_service_internal(

            usb_dev_state_struct_t* usb_dev_ptr,

            usb_event_struct_t* event)

              usb_framework.c::USB_Control_Service(

                void* handle,

                usb_event_struct_t* event,

                void* arg)

                uint8_t * data = NULL;

            if (event->setup == TRUE) {

                  appears to copy usb packet info into data

        } else {

            /* class or vendor request */

            usb_fw_ptr->request_notify_callback(

            (usb_setup_struct_t*) usb_fw_ptr->ext_req_to_host,

            &data,&size,usb_fw_ptr->request_notify_param);

 

                      which calls ...

 

                      usb_hid.c::USB_HID_Requests(

                        usb_setup_struct_t* setup_packet,

                        uint8_t * *data,

                        uint32_t *size,

                        void* arg)

                        keyboard.c::USB_App_Class_Callback(

                          uint8_t request,

                          uint16_t value,

                          uint8_t ** data,

                          uint32_t* size,

                          void* arg)

 

                          /* handle the class request */

                          switch(request)

                          {

                          case USB_HID_SET_REPORT_REQUEST:

                            for (index = 0; index < KEYBOARD_BUFF_SIZE; index++)

                            { /* copy the report sent by the host */

                               g_keyboard.rpt_buf[index] = *(*data + index);

 

PROBLEM IS HERE. Since the event->setup was not set to TRUE in the above USB_Control_Service() call data is a NULL pointer.

                            }

                          break;

 

 

**********************************************

***** linux host libusb test application *****

**********************************************

 

 

// gcc -std=c99 my_libusb_dell.c -o my_libusb_dell `pkg-config libusb-1.0 --libs --cflags`

 

// ***** must run as root (sudo)

 

#include <stdbool.h>

#include <stdio.h>

#include <libusb-1.0/libusb.h>

 

 

#define VID_DELL 0x413c

#define HID_DELL 0x2107

 

#define VID_FREESCALE 0x15a2

#define HID_FREESCALE 0x0200

 

void printdev (libusb_device *dev) {

 

  struct libusb_device_descriptor desc;

 

  // Device Descriptor

  int r = libusb_get_device_descriptor(dev, &desc);

  if (r < 0) {

    printf("libusb_get_device_descriptor() failure\n");

    return;

  }

 

  printf("*** Device Descriptor ***\n");

  printf("\nbLength: %d\n", (int)desc.bLength);

  printf("bDescriptorType: %d\n", (int)desc.bDescriptorType);

  printf("bcdUSB: %d\n", (int)desc.bcdUSB);

  printf("bDeviceClass: %d\n", (int)desc.bDeviceClass);

  printf("bDeviceSubClass: %d\n", (int)desc.bDeviceSubClass);

  printf("bDeviceProtocol: %d\n", (int)desc.bDeviceProtocol);

  printf("bMaxPacketSize0: %d\n", (int)desc.bMaxPacketSize0);

  printf("idVendor: %x\n", desc.idVendor);

  printf("idProduct: %x\n", desc.idProduct);

  printf("bcdDevice: %x\n", desc.bcdDevice);

  printf("bNumConfigurations: %d\n", (int)desc.bNumConfigurations);

 

  // Configuration Descriptor

  struct libusb_config_descriptor *config;

  libusb_get_config_descriptor(dev, 0, &config);

 

  printf("  *** Configuration Descriptor ***\n");

  printf("  bLength: %d\n", (int)config->bLength);

  printf("  bDescriptorType: %d\n", (int)config->bDescriptorType);

  printf("  wTotalLength: %d\n", (int)config->wTotalLength);

  printf("  bConfigurationValue: %d\n", (int)config->bConfigurationValue);

  printf("  bmAttributes: %d\n", (int)config->bmAttributes);

  printf("  MaxPower: %d\n", (int)config->MaxPower);

  printf("  bNumInterfaces: %d\n", (int)config->bNumInterfaces);

 

  // Interface Descriptor (Keyboard)

  const struct libusb_interface *inter;

  const struct libusb_interface_descriptor *interdesc;

  const struct libusb_endpoint_descriptor *epdesc;

 

  printf("    *** Interface Descriptors ***\n");

  for (int i = 0; i < (int)config->bNumInterfaces; i++) {

    printf("      *** Interface Descriptor %d ***\n", i);

    inter = &config->interface[i];

    printf("      num_altsetting: %d\n", inter->num_altsetting);

    for (int j = 0; j < inter->num_altsetting; j++) {

 

      interdesc = &inter->altsetting[j];

      printf("      bLength: %d\n", (int)interdesc->bLength);

      printf("      bDescriptorType: %d\n", (int)interdesc->bDescriptorType);

      printf("      bInterfaceNumber: %d\n", (int)interdesc->bInterfaceNumber);

      printf("      Number of endpoints: %d\n", (int)interdesc->bNumEndpoints);

      // Class code (HID code assigned by USB). 0x03

      printf("      bInterfaceClass: %d\n", (int)interdesc->bInterfaceClass);

      printf("      bInterfaceSubClass: %d\n", (int)interdesc->bInterfaceSubClass);

      // Protocol code.

      // 0 None

      // 1 Keyboard

      // 2 Mouse

      printf("      bInterfaceProtocol: %d\n", (int)interdesc->bInterfaceProtocol);

 

      // Endpoint Descriptor (Keyboard)

      printf("      *** Endpoint Descriptors ***\n");

      for (int k = 0; k < (int)interdesc->bNumEndpoints; k++) {

        printf("      *** Endpoint Descriptor %d ***\n", i);

        epdesc = &interdesc->endpoint[k];

        printf("        bLength: %d\n", (int)epdesc->bLength);

        // LIBUSB_DT_ENDPOINT = 0x05

        printf("        bDescriptorType %d\n", (int)epdesc->bDescriptorType);

        printf("        bEndpointAddress: %x\n", (int)epdesc->bEndpointAddress);

        printf("        bmAttributes: %x\n", (int)epdesc->bmAttributes);

        printf("        wMaxPacketSize: %x\n", (int)epdesc->wMaxPacketSize);

        printf("        bInterval: %x\n", (int)epdesc->bInterval);

      }

    }

  }

 

  libusb_free_config_descriptor(config);

}

 

unsigned char cdata[1] =

  {

   0xff,

  };

 

#define FREESCALE

#define  KEYBOARD_BUFF_SIZE    (8)   /* report buffer size */

static unsigned char rpt_buf[KEYBOARD_BUFF_SIZE] = {0, 1, 2, 3, 4, 5, 6, 7};

 

// main program

int main(int argc, char *argv[])

{

  struct libusb_device **devs;

  struct libusb_device_descriptor info;

  struct libusb_device_handle *dev_handle;

  unsigned count, i;

  int rv = 0;

#if defined(FREESCALE)

  uint16_t vendor_id = VID_FREESCALE;

  uint16_t product_id = HID_FREESCALE;

#else

  uint16_t vendor_id = VID_DELL;

  uint16_t product_id = HID_DELL;

#endif

 

  // init USB lib (this is the 1.0 lib)

  if (libusb_init(NULL) < 0) {

    printf("libusb_init() failure\n");

    return 1;

  }

 

  // get list of devices and counts

  count = libusb_get_device_list(NULL, &devs);

  if (count <= 0) {

    printf("libusb_get_device_list() failure\n");

    return 2;

  }

 

  // walk the list, read descriptors, and dump some output from each

  for (i = 0; i < count; i++) {

 

    libusb_get_device_descriptor(devs[i], &info);

    printf("VID=%04x PID=%04x\n", info.idVendor, info.idProduct);

 

    if (info.idVendor == vendor_id && info.idProduct == product_id) {

      printf("match found VID=%04x PID=%04x\n", info.idVendor, info.idProduct);

 

      // Open the device

      if (rv = libusb_open(devs[i], &dev_handle) == 0) {

      // dev_handle = libusb_open_device_with_vid_pid(NULL, vendor_id, product_id);

      //if (dev_handle) {

 

        printdev(devs[i]);

 

        // unhook the kernel's driver on the interface if necessary

#if defined(FREESCALE)

        int iterface_num = 1;

#else

        int iterface_num = 0;

#endif

 

        // detatch kernal driver an obtain interface access

        bool kernel_owns_interface = false;

 

        if ((rv = libusb_kernel_driver_active(dev_handle, iterface_num)) == 1) {

          printf("libusb_kernel_driver_active() - kernel driver is active\n");

 

          if ((rv = libusb_detach_kernel_driver(dev_handle, iterface_num)) == 0) {

            kernel_owns_interface = true;

          }

          else {

            printf("libusb_detach_kernel_driver() failure\n");

          }

        }

        else if (rv == 0) {

          printf("libusb_kernel_driver_active() - kernel driver is not active\n");

        }

        else {

          rv = -1;

          printf("libusb_kernel_driver_active() failure\n");

        }

 

 

        if (rv != -1) {

 

          // claim the USB HID interface

          if ( (rv = libusb_claim_interface(dev_handle, iterface_num)) == 0) {

 

            // enum libusb_endpoint_direction

            // LIBUSB_ENDPOINT_IN  In: device-to-host.

            // LIBUSB_ENDPOINT_OUT   Out: host-to-device.

 

            // enum libusb_endpoint_direction {

            //      /** In: device-to-host */

            //      LIBUSB_ENDPOINT_IN = 0x80,

            //      /** Out: host-to-device */

            //      LIBUSB_ENDPOINT_OUT = 0x00

            // };

 

            // enum    libusb_transfer_type {

            //   LIBUSB_TRANSFER_TYPE_CONTROL = 0,

            //   LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1,

            //   LIBUSB_TRANSFER_TYPE_BULK = 2,

            //   LIBUSB_TRANSFER_TYPE_INTERRUPT = 3 }

 

            // enum    libusb_request_recipient {

            //   LIBUSB_RECIPIENT_DEVICE = 0x00,

            //   LIBUSB_RECIPIENT_INTERFACE = 0x01,

            //   LIBUSB_RECIPIENT_ENDPOINT = 0x02,

            //   LIBUSB_RECIPIENT_OTHER = 0x03 }

 

 

            bool wait_for_exit = true;

            while (wait_for_exit) {

 

              static unsigned char ledValues = 0xff;

              uint8_t bmRequestType = LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT; // 0x20 | 0x01 | 0x00

              uint8_t bRequest = 0x9;           // request type (SET_REPORT)

              uint16_t wValue = 0x200;          // value (0x2 = output report, 00 = report ID)

              uint16_t wIndex = iterface_num;   // 0 for keyboard HID, 1 for composite USBMS / USB Hid Keyboard

 

              printf("rpt_buf[0-7]");

              for (int i = 0; i < 7; i++) {

                printf(" %x", rpt_buf[i]);

              }

              printf("\n");

 

              rv = libusb_control_transfer(dev_handle,

                                           bmRequestType,       // type of write

                                           bRequest,

                                           wValue,

                                           wIndex,

                                           rpt_buf,             // data packet

                                           KEYBOARD_BUFF_SIZE,  // byte count

                                           1000);               // timeout

 

              if (rv > 0) {

                printf("libusb_control_transfer() transferred %d bytes\r\n", rv);

              }

              else {

                printf("libusb_control_transfer() failure: %x\n", rv);

              }

 

              char input = getchar();

              if (input == 'x') {

                wait_for_exit = false;

              }

 

              // use control ep 0 for now

              // rv = libusb_interrupt_transfer...)

            }

 

            // release usb hid interface

            if ( (rv = libusb_release_interface(dev_handle, iterface_num)) < 0) printf("libusb_release_interface() failure: %d\n", rv);

           

            // re-attatch interface to kernel driver if necessary

            if ( kernel_owns_interface && ((rv = libusb_attach_kernel_driver(dev_handle, iterface_num)) < 0)) printf("libusb_attach_kernel_driver() failure: %d\n", rv);

          }

          else {

            printf("libusb_claim_interface() failure: %d\n", rv);

            if ( (rv = libusb_attach_kernel_driver(dev_handle, iterface_num)) < 0) printf("libusb_attach_kernel_driver() failure: %d\n", rv);

          }

        }

 

        libusb_close(dev_handle);

     

      } else {

        printf("libusb_open_device_with_vid_pid() failure\n");

        rv = 1;

      }

    }

  }

 

  libusb_free_device_list(devs, 1);

  libusb_exit(NULL);

 

  return rv;

}

Original Attachment has been moved to: my_libusb_dell.c.zip

Outcomes