How to set or get Feature Reports

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to set or get Feature Reports

6,456 Views
john8
Contributor III

I am developing USB device firmware using the EVK MIMXRT1060 development board and the NXP USB middleware (MCUXpresso SDK USB Stack Device).

Below is a generic/custom HID descriptor I have created for my USB device. 

The INPUT, OUTPUT report types work fine. However, I am unable to send or receive the FEATURE Report type.

As I understand it feature reports are sent via Endpoint 0 as control configuration data. But it is not clear what API functions I am supposed to use and/or in which events? 

I can find no sample code for get/set feature reports or perhaps I am not looking properly. What am I missing?

USB_DMA_INIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE)
uint8_t g_UsbDeviceHidGenericReportDescriptor[] = {
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x01, // Usage (0x01)
0xA1, 0x01, // Collection (Application)

0x85, 0x01, // Report ID (1) SUCCESS/FAIL - Total size 2B
0x09, 0x00, // Usage (0x00) we dont use usage
0x75, 0x08, // Report Size (8 bits) success/fail/ack/nak/errorcode/etc...
0x95, 0x01, // Report Count (1)
0x81, 0x03, // Input (Data,Var,Abs)

0x85, 0x02, // Report ID (2) General Request - Total size 34B
0x09, 0x00, // Usage (0x00) we dont use usage
0x75, 0x08, // Report Size (8) command (tag)
0x95, 0x01, // Report Count (1)
0x91, 0x03, // Output (Data,Var,Abs)
0x75, 0x08, // Report Size (8 bits)
0x95, 0x20, // Report Count (32)
0x91, 0x03, // Output (Data,Var,Abs)

0x85, 0x03, // Report ID (3) General Response - Total Size 66B
0x09, 0x00, // Usage (0x00) we dont use usage
0x75, 0x08, // Report Size (8) command (tag)
0x95, 0x01, // Report Count (1)
0x81, 0x03, // Input (Data,Var,Abs)
0x75, 0x08, // Report Size (8 bits) Response data
0x95, 0x40, // Report Count (64)
0x81, 0x03, // Input (Data,Var,Abs)

0x85, 0x04, // Report ID (4) Flash a data page - Total size 261B
0x09, 0x00, // Usage (0x00) we dont use usage
0x75, 0x20, // Report Size (32 bits) address at which to burn
0x95, 0x01, // Report Count (1) 4B address - 32bit word
0x91, 0x03, // Output (Data,Var,Abs)
0x75, 0x08, // Report Size (8 bits)
0x96, 0x00, 0x01, // Report Count (256) 256B of data to be burned to flash
0x91, 0x03, // Output (Data,Var,Abs)

// How to transmit/receive this report?

0x85, 0x05, // Report ID (5) SUCCESS/FAIL - Total size 2B
0x09, 0x00, // Usage (0x00) we dont use usage
0x75, 0x08, // Report Size (8 bits)
0x95, 0x08, // Report Count (1)
0xB1, 0x02, // Feature (Data,Var,Abs)

0xC0, // End Collection
};

Labels (3)
0 Kudos
Reply
11 Replies

6,136 Views
john8
Contributor III

I have resolved this.

NXP have no examples on this and their documentation and support is simply useless in this area, so I had no choice but to figure it out myself. Its a pity because the middleware seems to be very good and robust but is let down by a lack of decent documentation and examples. when it comes to the more esoteric HID usages such as UPS and Sensor, one is left to ones own devices to figure out.

I used a USB tool called USB Monitor by HHD Software to help me understand the NXP USB Middleware. I highly recommend you get it if developing USB device firmware. With it I was able to send and receive feature reports easily and then debug the middleware to find out what was going on.

So for those people that may be interested in the future and need an answer on this, the below code shows how to use the NXP USB middleware to GET/SET Feature reports. 

for SET FEATURE REPORT you need to:

1. respond to the kUSB_DeviceHidEventRequestReportBuffer event first, in which you set a  dedicated "feature buffer" that the middleware can use for the control data (endpoint 0) transfer.

2. respond to the kUSB_DeviceHidEventSetReport event, at which point the feature buffer you set previously will hold incoming feature report data.

for GET FEATURE REPORT you need to:

1. respond to kUSB_DeviceHidEventGetReport event, at which point simply set the feature buffer and fill with your outgoing report data. after which it will be sent to the host.

see example code below. bold bits are most relevant:

/* The hid class callback */
usb_status_t USB_DeviceHidGenericCallback(class_handle_t handle, uint32_t event, void *param)
{
   usb_status_t error = kStatus_USB_Error;

   switch (event)
{
case kUSB_DeviceHidEventSendResponse:
   if (s_UsbDeviceComposite->attach)
   {
   }
break;
case kUSB_DeviceHidEventRecvResponse:
   if (s_UsbDeviceComposite->attach)
   {
      error = USB_DeviceHidGenericAction();
   }
   break;

case kUSB_DeviceHidEventGetReport:
   error = USB_DeviceHidGenericGetReport(param);
   break;

case kUSB_DeviceHidEventSetReport:
   error = USB_DeviceHidGenericSetReport(param);
   break;

//This request is used by the middleware to set the buffer for both GET/SET FEATURE reports

case kUSB_DeviceHidEventRequestReportBuffer:
   ((usb_device_hid_report_struct_t *)param)->reportBuffer = (uint8_t *)s_GenericFeatureBuffer;
   ((usb_device_hid_report_struct_t *) param)->reportLength = 64;//sizeof(s_GenericFeatureBuffer);
   error = kStatus_USB_Success;
break;

   case kUSB_DeviceHidEventGetIdle:
   case kUSB_DeviceHidEventGetProtocol:
   case kUSB_DeviceHidEventSetIdle:
   case kUSB_DeviceHidEventSetProtocol:
break;
   default:
   break;
}

   return error;
}

//gets data FROM the host

usb_status_t USB_DeviceHidGenericSetReport(usb_device_hid_report_struct_t *report)
{
usb_status_t usbstatus = kStatus_USB_Error;

if (report == NULL)
   return kStatus_USB_InvalidParameter;

switch (report->reportId)
{

//handle report ID 5 which is a FEATURE report sent to the DEVICE
case 5:

   printf("report length: %x\r\n", report->reportLength);
   printf("set report: %x\r\n", report->reportBuffer[1]); //show second byte of report data
   usbstatus = kStatus_USB_InvalidRequest;
default:
   break;
}

return usbstatus;
}

//gets data TO the HOST

usb_status_t USB_DeviceHidGenericGetReport(usb_device_hid_report_struct_t *report)
{
usb_status_t usbstatus = kStatus_USB_Error;

if (report == NULL)
   return kStatus_USB_InvalidParameter;

//clear our report buffers

memset(s_GenericFeatureBuffer, 0, sizeof(s_GenericFeatureBuffer));
memset(s_GenericBufferIn, 0, sizeof(s_GenericBufferIn));

switch (report->reportId)
{

//report ID 3 is an Input report and so works differently from a feature report

//in that the buffer is initialized at start of the day (in device init)
case 3:
   report->reportBuffer = (uint8_t *)s_GenericBufferIn;
   report->reportLength = sizeof(s_GenericBufferIn);
   report->reportBuffer[0] = 3;
   report->reportBuffer[1] = 0xff;
   report->reportBuffer[2] = 0xaa;
   report->reportBuffer[3] = 3;
   report->reportBuffer[4] = 4;
   report->reportBuffer[5] = 5;
   report->reportBuffer[6] = 6;
   report->reportBuffer[7] = 7;
   report->reportBuffer[8] = 8;
   report->reportBuffer[9] = 9;

   usbstatus = kStatus_USB_Success;
   break;

//handle report ID 5 which is a FEATURE report sent to HOST

case 5:

   //get the feature report buffer and put some data into it
   report->reportBuffer = (uint8_t *)s_GenericFeatureBuffer;
   report->reportLength = sizeof(s_GenericFeatureBuffer);


   s_GenericFeatureBuffer[0] = 5;
   s_GenericFeatureBuffer[1] = 1;
   s_GenericFeatureBuffer[2] = 2;
   s_GenericFeatureBuffer[3] = 3;
   s_GenericFeatureBuffer[4] = 4;
   s_GenericFeatureBuffer[5] = 5;
   s_GenericFeatureBuffer[6] = 6;
   s_GenericFeatureBuffer[7] = 7;
   s_GenericFeatureBuffer[8] = 8;

   //thats it!!! The data will be sent to the host on return from this callback!

   usbstatus = kStatus_USB_Success;
   break;
default:
   break;
}

return usbstatus;
}

0 Kudos
Reply

6,136 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi john,

Thank you for sharing. This is definitely helpful to other people.

Regards,

Jing

0 Kudos
Reply

6,136 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi john,

RT1060 USB stack send all Hid report descriptor data in USB_DeviceGetHidReportDescriptor(). In USB_DeviceHidEvent() it receive GetReport request, but in USB_DeviceHidGenericCallback(), it shows this is a invalid request. You should add your own code in the callback function.
You can trace 'kUSB_DeviceHidEventGetReport' to see them.

Regards

Jing

0 Kudos
Reply

6,136 Views
john8
Contributor III

So are you saying that GetReport and SetReport events are used to Get/Set Feature reports? 

If I a breakpoint there in USB_DeviceHidEvent() GetReport request, it does not break for the feature report. It only breaks for Input reports.

Do you have any sample code on how to use feature reports with the stack?

0 Kudos
Reply

6,136 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi,

There isn't demo or sample code for feature report. But it seems if host send Get Feature Report request, the code should step to here.

Regards,

Jing

0 Kudos
Reply

6,136 Views
john8
Contributor III

This is a VERY poor answer by NXP Support. I think it is disgraceful that you are unable to provide a simple example of how to use the set /get feature report functionality of the USB middleware.

0 Kudos
Reply

6,136 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi john,

Can you send me you code to show your situation? 

Regards,

Jing

0 Kudos
Reply

6,136 Views
john8
Contributor III

Hi,

My test Project is attached.

I have resolved the issue.

please note it uses Segger Embedded Studio (SES) as the IDE, not MCUXpresso. 

You should be able to port it easily or download SES for free.

regards,

0 Kudos
Reply

6,136 Views
jingpan
NXP TechSupport
NXP TechSupport

Hi john,

I'm sorry but USB class is a big family. Our examples can only cover a small part. I've asked AE team if they have example. 

Regards,

Jing

0 Kudos
Reply

6,136 Views
mjbcswitzerland
Specialist V

Hi

Here is a reference from the uTasker project which 'possibly' shows the general method. Since such reports are used by keyboards, mouse and RAW HID there should be various methods to copy too.

static const unsigned char ucRawReport[] = {
 0x06, LITTLE_SHORT_WORD_BYTES(RAWHID_USAGE_PAGE),
 0x0a, LITTLE_SHORT_WORD_BYTES(RAWHID_USAGE),
 0xa1, 0x01,                                                          // collection 0x01
 0x75, 0x08,                                                          // report size = 8 bits
 0x15, 0x00,                                                          // logical minimum = 0
 0x26, 255, 0x00,                                                     // logical maximum = 255
 0x95, HID_RAW_TX_SIZE,                                               // report count
 0x09, 0x01,                                                          // usage
 0x81, 0x02,                                                          // input (array)
 0x95, HID_RAW_RX_SIZE,                                               // report count
 0x09, 0x02,                                                          // usage
 0x91, 0x02,                                                          // output (array)
 0xc0                                                                 // end collection
};

// Control endpoint 0 call-back
//
static int control_callback(unsigned char *ptrData, unsigned short usLength, int iType)
{
    int iRtn = BUFFER_CONSUMED;                                          // default return value if not overwritten
    switch (iType) {
    case SETUP_DATA_RECEPTION:
        {
            USB_SETUP_HEADER *ptrSetup = (USB_SETUP_HEADER *)ptrData;    // interpret the received data as a setup header
            usExpectedData = ptrSetup->wLength[0];                       // the amount of additional data which is expected to arrive from the host belonging to this request
            usExpectedData |= (ptrSetup->wLength[1] << 8);
            if ((ptrSetup->bmRequestType & STANDARD_DEVICE_TO_HOST) != 0) {     // request for information
                usExpectedData = 0;                                      // no data expected to be received by us
                switch (ptrSetup->bRequest) {
                case USB_REQUEST_GET_DESCRIPTOR:                         // standard request
                    if (ptrSetup->wValue[1] == DESCRIPTOR_TYPE_REPORT) {
                        int iHID_Interface = (ptrSetup->wIndex[0] | (ptrSetup->wIndex[1] << 8)); // the interface being requested
                        if (iHID_Interface == USB_HID_RAW_INTERFACE_NUMBER) { // raw interface number
                            fnWrite(USB_control[USB_DEVICE_REF], (unsigned char *)&ucRawReport, sizeof(ucRawReport)); // return directly (non-buffered)
                            break;
                        }
                        iRtn = STALL_ENDPOINT;                          // not supported so stall
                    }
                    break;
                }
            }
        }
        break;
    ....
    }
    return iRtn;
}

Endpoint 0 has a callback where the code can detect setup data reception with request type device to host. A get descriptor  request can be identified with a descriptor type report value (0x22). Finally the interface should match (in multiple interfaces are used) and the report content can be returned.

This may not be the exact report being discussed but any such can be returned by interpreting the exact request value.

Regards

Mark
[uTasker project developer for Kinetis and i.MX RT]

0 Kudos
Reply

6,136 Views
john8
Contributor III

many thanks, but I have seen this. I understand that feature reports are control transfers (endpoint 0) but it was not obvious how to do it using NXP middleware hence my question. There is no 'control' callback in the NXP middleware unlike other USB API's. I have since figured it out though and have posted the answer here for others.

0 Kudos
Reply