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
Hi Steveevers,
For how to make a composite USB device application based on ksdk 1.3 , I would recommend you referring to Composite Device User's Guide in the folder of "C:\Freescale\KSDK_1.3.0\doc\usb", there are steps you may follow to build a custom composite demo .
Hope that helps,
Have a great day,
Kan
NXP Technical Support
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------