AnsweredAssumed Answered

Composite USB with multiple HID class instances (not working)

Question asked by steveevers on Dec 23, 2015

I have a implemented a composite USB device using the kinetis KSDK_1.3.0 sdk. This device is composed of 1 mass storage (interface=0, in ep=0x83, out ep=4), 1 hid keyboard (interface=1, in ep=0x82), and 1 hid generic (interface=2, in ep=0x85, out ep=0x06). The device enumerates on a linux Host fine. The lsusb for the device is shown below. Mass storage and keyboard interact with the linux Host as expected.

 

I have created a simple Host test app using libusb to communication with the generic hid interface on the composite device (code shown below and attached). This Host program works fine with the Freescale dev_hid_generic_bm_frdmk2ff example code FRDM_K22F dev board. The dev board enumerates on the Linux Host, and the Host libusb program detaches the kernel driver from the generic hid interface and packets can be sent to and received from the generic hid device fine.

 

When using the same program (with the new interface number and endpoint address) in my composite device I get the following error on the Host in response to a libusb_interrupt_transfer() write to the device's generic hid interface output endpoint (0x06) :

 

  retVal: -7, num_bytes_written: 0, Resource temporarily unavailable

 

When sniffing the USB bus using wireshark, I see the USB URB_INTERRUPT out packet being sent (with my packet data) to the device fine on the device output endpoint 6, but instead of receiving a "URB status: Success" in the response URB_INTERRUPT out, I get the following error:

 

  "URB status: No such file or directory (-ENOENT) (-2)"

 

Any suggestions on how to tweak the USB descriptor, configure the Fresscale USB stack, or modify my host libusb application to get composite device working with multiple generic HID class instances would be appreciated ... thanks.

 

 

 

/************************************************************************/

/* freescale usb library Macro changes to build for composite device    */

/* with 1 mass storage and 2 hid class instances                        */

/************************************************************************/

 

  /KSDK_1.3.0/usb/usb_core/device/sources/classes/include/config/usb_hid_config.h

  #define MAX_HID_DEVICE                    (0x02) // default is (0x01)

 

  /KSDK_1.3.0/usb/usb_core/device/sources/classes/include/config/usb_composite_config.h

  #define CONFIG_MAX 3 // default is 2

 

 

  /KSDK_1.3.0/usb/usb_core/device/include/MK22F51212/usb_device_config.h

  #define USBCFG_DEV_MAX_CLASS_OBJECT       (2) // default is (1)

 

/************************************************************************/

/* Host libusb program to communicate with composite device             */

/************************************************************************/

 

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

 

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

 

#include <errno.h>

#include <fcntl.h>

#include <libusb-1.0/libusb.h>

#include <stdbool.h>

#include <stdio.h>

#include <string.h>

#include <unistd.h>

 

//#define FRDM_K22F

 

/* HID parameters */

#define VID_FREESCALE   0x15a2

#if defined(FRDM_K22F)

#define PID_FREESCALE   0x0101

#define WRITE_EP        0x02

#define READ_EP         0x81

#else

#define PID_FREESCALE   0x0200

#define WRITE_EP        0x06

#define READ_EP         0x85

#endif

#define USB_HID_BUF_SZ  16

#define USB_WR_TIMEOUT  1500      // write timeout 500 ms

#define USB_RD_TIMEOUT  500      // read timeout 500 ms

 

 

static uint8_t  bus_num;

static uint8_t  dev_addr;

static uint8_t  lockfile_str[128];

static bool     claimed_interface = false;

static bool     kernel_owns_interface = false;

static int      lockfile_fd;

static struct   libusb_device_handle* dev_handle = NULL;

#if defined(FRDM_K22F)

static int      iterface_num = 0;

#else

static int      iterface_num = 2;

#endif

#define FUNC_ENTRY() printf("%s entry\r\n", __FUNCTION__);

#define FUNC_EXIT() printf("%s exit\r\n", __FUNCTION__);

 

static unsigned char write_report_buf[USB_HID_BUF_SZ];

static unsigned char read_report_buf[USB_HID_BUF_SZ];

 

 

 

static int create_lockfile (uint8_t bus_num, uint8_t dev_addr, uint8_t* lockfile_str);

 

// API

libusb_device_handle* open_usb_device (void);

int close_usb_device (libusb_device_handle* dev_handle);

int32_t write_usb_device (uint8_t *pData, int32_t num_bytes, uint32_t timeout);

int32_t read_usb_device (uint8_t* pData, int32_t numBytes, uint32_t timeout);

 

#define LOCKFILE_BASE "/var/lock/hb_usb_lockfile"

 

static void print_dev_info (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("\n*** Device Descriptor ***\n");

  printf("bLength: %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);

}

 

 

// main program

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

{

  int retVal = 0;

 

  for (int32_t i = 0; i < USB_HID_BUF_SZ; i++) {

    write_report_buf[i] = i;

  }

 

  if ((dev_handle = open_usb_device()) != NULL) {

 

    bool wait_for_exit = true;

    while (wait_for_exit) {

 

#if 1

      char input = getchar();

      if (input == 'x') {

        wait_for_exit = false;

      }

      else

#else

      static int8_t num_packets_to_send = 10;

      sleep(2);

      if (!num_packets_to_send) {

        wait_for_exit = false;

      }

#endif

      {

        // do stuff

        int num_bytes_written = write_usb_device(write_report_buf, USB_HID_BUF_SZ, USB_WR_TIMEOUT);

 

        if (num_bytes_written == USB_HID_BUF_SZ) {

           int num_bytes_read = read_usb_device(read_report_buf, USB_HID_BUF_SZ, USB_RD_TIMEOUT);

 

           if (num_bytes_read == USB_HID_BUF_SZ) {

             printf("read_report_buf[]: \n");

             for (int32_t i = 0; i < USB_HID_BUF_SZ; i++) {

               printf("%d \n", read_report_buf[i]);

             }

             printf("\n");

           }

           else {

             printf("num_bytes_read == USB_HID_BUF_SZ\n");

           }

        }

        else {

          printf("num_bytes_written != USB_HID_BUF_SZ, num_bytes_written = %d\n", num_bytes_written);

        }

 

        for (int32_t i = 0; i < USB_HID_BUF_SZ; i++) {

          write_report_buf[i]++;

        }

      }

    }

 

    close_usb_device(dev_handle);

  }

  else {

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

    retVal = -1;

  }

 

  return retVal;

}

 

 

int32_t write_usb_device (uint8_t *pData, int32_t num_bytes, uint32_t timeout)

{

  int32_t num_bytes_written = 0;

  int32_t retVal;

  int32_t toWrite = num_bytes;

 

  FUNC_ENTRY();

 

  if (!pData || (num_bytes == 0) || !dev_handle) return -1;

 

  do {

 

    retVal = libusb_interrupt_transfer(dev_handle,

                                       WRITE_EP,

                                       pData,

                                       USB_HID_BUF_SZ,

                                       &num_bytes_written,

                                       timeout);

 

    if (retVal < 0) {

      printf("libusb_interrupt_transfer() write failure:\n");

      printf("  retVal: %d, num_bytes_written: %d, %s\n", retVal, num_bytes_written, strerror(errno));

 

      if ((retVal == LIBUSB_ERROR_NO_DEVICE) || (retVal == LIBUSB_ERROR_TIMEOUT)) {

        return retVal;

      }

    }

 

    toWrite -= num_bytes_written;

    pData += num_bytes_written;

 

  } while (toWrite > 0);

 

  printf("num_bytes_written: %d\n", num_bytes_written);

 

  FUNC_EXIT();

 

  return num_bytes_written;

}

 

 

int32_t read_usb_device (uint8_t* pData, int32_t num_bytes, uint32_t timeout)

{

 

  int num_bytes_read;

  int retVal;

 

  FUNC_ENTRY();

 

  if (!pData || (num_bytes == 0) || !dev_handle) return -1;

 

  int32_t packetBytesToRead = num_bytes;

 

  retVal = libusb_interrupt_transfer(dev_handle,

                                     READ_EP,

                                     pData,

                                     USB_HID_BUF_SZ,

                                     &num_bytes_read,

                                     timeout);

 

  if (retVal < 0) {

    printf("libusb_interrupt_transfer() read failure:\n");

    printf("  %s\n", strerror(errno));

 

    if ((retVal == LIBUSB_ERROR_NO_DEVICE) || (retVal == LIBUSB_ERROR_TIMEOUT)) {

      return retVal;

    }

  }

  else if (num_bytes_read != num_bytes) {

    printf("complete buffer not read\n");

  }

 

  return num_bytes_read;

}

 

 

static int create_lockfile (uint8_t bus_num, uint8_t dev_addr, uint8_t* lockfile_str)

{

  int fd;

 

  sprintf((char *)lockfile_str, "%s_%03d_%03d.lock", LOCKFILE_BASE, bus_num, dev_addr);

 

  printf("Lockfile name = %s\n", lockfile_str);

 

  fd = open((const char*)lockfile_str, O_CREAT | O_EXCL);

  if (fd < 0) {

    printf("open() failure:\n");

    printf("  %s\n", strerror(errno));

  }

 

  return fd;

}

 

 

libusb_device_handle* open_usb_device () {

 

  unsigned count, i;

  int retVal = 0;

  libusb_device_handle* handle = NULL;

  struct libusb_device **devs;

  struct libusb_device_descriptor info;

 

  // if the open_usb_device procedure has been already called

  if (dev_handle != NULL) {

    printf("dev_handle != NULL\n");

    return dev_handle;

  }

 

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

  if (libusb_init(NULL) < 0) {

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

    return NULL;

  }

 

  // get list of devices and counts

  count = libusb_get_device_list(NULL, &devs);

  if (count <= 0) {

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

    return NULL;

  }

 

  // 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 == VID_FREESCALE && info.idProduct == PID_FREESCALE) {

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

 

      bus_num = libusb_get_bus_number(devs[i]);

      dev_addr = libusb_get_device_address(devs[i]);

 

      // Create lock file

      if ((lockfile_fd = create_lockfile(bus_num, dev_addr, lockfile_str)) >= 0) {

 

        // Open the device

        if ((retVal = libusb_open(devs[i], &handle)) == 0) {

 

          print_dev_info(devs[i]);

 

          // release kernel driver ownership if necessary

          kernel_owns_interface = false;

          if ((retVal = libusb_kernel_driver_active(handle, iterface_num)) == 1) {

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

  

            if ((retVal = libusb_detach_kernel_driver(handle, iterface_num)) == 0) {

              kernel_owns_interface = true;

            }

            else {

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

            }

          }

          else if (retVal == 0) {

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

          }

          else {

            retVal = -1;

          }

 

          if (retVal != -1) {

 

            // claim the USB HID interface

            if ((retVal = libusb_claim_interface(handle, iterface_num)) == 0) {

 

              printf("libusb_claim_interface() success\n");

              break;

            }

            else {

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

              handle = NULL;

            }

          }

          else {

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

            libusb_close(handle);

            if (lockfile_fd && (remove(lockfile_str) == -1)) printf("remove() failed\n");

            libusb_free_device_list(devs, 1);

            libusb_exit(NULL);

            handle = NULL;

          }

        }

        else {

          printf("libusb_open() failure, retVal: %x\n", retVal);

          if (lockfile_fd && (remove(lockfile_str) == -1)) printf("remove() failed\n");

          libusb_free_device_list(devs, 1);

          libusb_exit(NULL);

          handle = NULL;

        }

      }

      else {

        printf("%s lock file already exists\n", lockfile_str);

        libusb_exit(NULL);

        handle = NULL;

      }

    }

  }

 

  libusb_free_device_list(devs, 1);

 

  return handle;

}

 

 

int close_usb_device ( libusb_device_handle* dev_handle) {

 

  int retVal = 0;

 

  // lockfile

  if (lockfile_fd && (remove(lockfile_str) == -1)) printf("remove() failed\n");

  memset(lockfile_str, 0, sizeof(lockfile_str));

 

  // release the interface

  if (dev_handle && libusb_release_interface(dev_handle, iterface_num) < 0) {

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

  }

 

  // close the device

  if (dev_handle) {

    libusb_close(dev_handle);

    dev_handle = NULL;

  }

  else {

    printf("dev_handle == NULL\n");

  }

 

  // close the libusb library

  libusb_exit(NULL);

 

  return 0;

}

 

/************************************************************************/

/* output from $ lsusb -v                                               */

/************************************************************************/

 

Bus 003 Device 079: ID 15a2:0200 Freescale Semiconductor, Inc.

Device Descriptor:

  bLength                18

  bDescriptorType         1

  bcdUSB               2.00

  bDeviceClass            0 (Defined at Interface level)

  bDeviceSubClass         0

  bDeviceProtocol         0

  bMaxPacketSize0        64

  idVendor           0x15a2 Freescale Semiconductor, Inc.

  idProduct          0x0200

  bcdDevice            2.00

  iManufacturer           1

  iProduct                2

  iSerial                 0

  bNumConfigurations      1

  Configuration Descriptor:

    bLength                 9

    bDescriptorType         2

    wTotalLength           89

    bNumInterfaces          3

    bConfigurationValue     1

    iConfiguration          0

    bmAttributes         0xc0

      Self Powered

    MaxPower              100mA

    Interface Descriptor:

      bLength                 9

      bDescriptorType         4

      bInterfaceNumber        0

      bAlternateSetting       0

      bNumEndpoints           2

      bInterfaceClass         8 Mass Storage

      bInterfaceSubClass      6 SCSI

      bInterfaceProtocol     80 Bulk-Only

      iInterface              0

      Endpoint Descriptor:

        bLength                 7

        bDescriptorType         5

        bEndpointAddress     0x83  EP 3 IN

        bmAttributes            2

          Transfer Type            Bulk

          Synch Type               None

          Usage Type               Data

        wMaxPacketSize     0x0040  1x 64 bytes

        bInterval               0

      Endpoint Descriptor:

        bLength                 7

        bDescriptorType         5

        bEndpointAddress     0x04  EP 4 OUT

        bmAttributes            2

          Transfer Type            Bulk

          Synch Type               None

          Usage Type               Data

        wMaxPacketSize     0x0040  1x 64 bytes

        bInterval               0

    Interface Descriptor:

      bLength                 9

      bDescriptorType         4

      bInterfaceNumber        1

      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.00

          bCountryCode            0 Not supported

          bNumDescriptors         1

          bDescriptorType        34 Report

          wDescriptorLength      63

         Report Descriptors:

           ** UNAVAILABLE **

      Endpoint Descriptor:

        bLength                 7

        bDescriptorType         5

        bEndpointAddress     0x82  EP 2 IN

        bmAttributes            3

          Transfer Type            Interrupt

          Synch Type               None

          Usage Type               Data

        wMaxPacketSize     0x0010  1x 16 bytes

        bInterval               8

    Interface Descriptor:

      bLength                 9

      bDescriptorType         4

      bInterfaceNumber        2

      bAlternateSetting       0

      bNumEndpoints           2

      bInterfaceClass         3 Human Interface Device

      bInterfaceSubClass      0 No Subclass

      bInterfaceProtocol      0 None

      iInterface              0

        HID Device Descriptor:

          bLength                 9

          bDescriptorType        33

          bcdHID               1.00

          bCountryCode            0 Not supported

          bNumDescriptors         1

          bDescriptorType        34 Report

          wDescriptorLength      33

         Report Descriptors:

           ** UNAVAILABLE **

      Endpoint Descriptor:

        bLength                 7

        bDescriptorType         5

        bEndpointAddress     0x85  EP 5 IN

        bmAttributes            3

          Transfer Type            Interrupt

          Synch Type               None

          Usage Type               Data

        wMaxPacketSize     0x0010  1x 16 bytes

        bInterval               8

      Endpoint Descriptor:

        bLength                 7

        bDescriptorType         5

        bEndpointAddress     0x06  EP 6 OUT

        bmAttributes            3

          Transfer Type            Interrupt

          Synch Type               None

          Usage Type               Data

        wMaxPacketSize     0x0010  1x 16 bytes

        bInterval               8

Original Attachment has been moved to: sources.tar.gz

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

Outcomes