cancel
Showing results for 
Search instead for 
Did you mean: 

Get and set feature reports with STM32_USB-FS-Device_Lib_V3.3.0?

mk299
Associate III
Posted on May 27, 2012 at 00:32

Hello,

I'm working with STM32 since some weeks now and I'd like to use the Custom-Hid code from the STM32_USB-FS-Device_Lib_V3.3.0 for one of my projects based on STM32L151.

In contrary to the Custom-Hid example I'd like to use feature reports in addition to the input and output reports. In the example code feature reports are also defined, but I was not able to work with them.

I don't understand if, where and how they are processed with the given code. The host will work with HidD_GetFeature/HidD_SetFeature, but what about the device?

It would be great if someone had an idea that could help me.

Best regards and thanks in advance,

Michael

#custom-usb-hid-device
19 REPLIES 19
tsuneo
Senior
Posted on May 28, 2012 at 13:42

Unfortunately, the example doesn't implement Feature request handling.

Here is a snippet for Get_/Set_Report( Feature ) request.

First, CustomHID_Data_Setup() is called by the stack just after Setup stage.

At Data stage, CustomHID_GetReport_Feature() or CustomHID_SetReport_Feature() is called.

For Set_Report, CustomHID_Status_In() is called at Status stage.

\STM32_USB-FS-Device_Lib_V3.3.0\Project\Custom_HID\src\usb_prop.c

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

* Function Name  : CustomHID_Data_Setup

* Description    : Handle the data class specific requests.

* Input          : Request Nb.

* Output         : None.

* Return         : USB_UNSUPPORT or USB_SUCCESS.

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

typedef enum _HID_REPORTS

{

  HID_INPUT   = 1,

  HID_OUTPUT  = 2,

  HID_FEATURE = 3

} HID_REPORTS;

#define HID_INTERFACE_NO    0                       // interface number

                                                    // callback function prototypes

uint8_t *CustomHID_GetReport_Feature(uint16_t Length);

uint8_t *CustomHID_SetReport_Feature(uint16_t Length);

uint8_t Request = 0;                                // completed request at status stage

RESULT CustomHID_Data_Setup(uint8_t RequestNo)

{

  uint8_t *(*CopyRoutine)(uint16_t);

  if (pInformation->USBwIndex != HID_INTERFACE_NO) return USB_UNSUPPORT;    // just for this interface

  CopyRoutine = NULL;

  if ((RequestNo == GET_DESCRIPTOR)

      && (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))

     )

  {

    if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)

    {

      CopyRoutine = CustomHID_GetReportDescriptor;

    }

    else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)

    {

      CopyRoutine = CustomHID_GetHIDDescriptor;

    }

  } /* End of GET_DESCRIPTOR */

  /*** GET_PROTOCOL, GET_REPORT, SET_REPORT ***/

  else if ( (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) )

  {         

    switch( RequestNo )

    {

      case GET_PROTOCOL:

        CopyRoutine = CustomHID_GetProtocolValue;

        break;

      case GET_REPORT:

        switch( pInformation->USBwValue1 )          // report type

        {

          case HID_FEATURE:

            CopyRoutine = CustomHID_GetReport_Feature;

            break;

          case HID_INPUT:

            break;

        }

        break;

      case SET_REPORT:

        switch( pInformation->USBwValue1 )          // report type

        {

          case HID_FEATURE:

            CopyRoutine = CustomHID_SetReport_Feature;

            Request = SET_REPORT;

            break;

          case HID_OUTPUT:

            break;

        }

        break;

      default:

        break;

    }

  }

  if (CopyRoutine == NULL)

  {

    return USB_UNSUPPORT;

  }

  pInformation->Ctrl_Info.CopyData = CopyRoutine;

  pInformation->Ctrl_Info.Usb_wOffset = 0;

  (*CopyRoutine)(0);

  return USB_SUCCESS;

}

// callbacks

uint8_t HID_GetReport_Value[2];

uint8_t *CustomHID_GetReport_Feature(uint16_t Length)

{

  switch ( pInformation->USBwValue0 )               // report ID

  {

    case 1:

    case 2:

    case 3:

    case 4:

    case 5:

    case 6:

    case 7:

      if (Length == 0)

      {

                              // two bytes, report ID and report body

        pInformation->Ctrl_Info.Usb_wLength = 2;

        return NULL;

      } else {

        //

        // here, put value to HID_GetReport_Value depending on report ID

        //

        HID_GetReport_Value[0] = pInformation->USBwValue0;  // report ID

        HID_GetReport_Value[1] = 0;                         // body

        return HID_GetReport_Value;

      }

    default:

      return NULL;

  }

}

uint8_t HID_SetReport_Value[2];

uint8_t *CustomHID_SetReport_Feature(uint16_t Length)

{

  switch ( pInformation->USBwValue0 )               // report ID

  {

    case 1:

    case 2:

    case 3:

    case 4:

    case 5:

    case 6:

    case 7:

      if (Length == 0)

      {

                              // two bytes, report ID and report body

        pInformation->Ctrl_Info.Usb_wLength = 2;

        return NULL;

      } else {

        return HID_SetReport_Value;

      }

    default:

      return NULL;

  }

}

void CustomHID_Status_In(void)

{

  if (Request == SET_REPORT)                        // SET_REPORT completion

  {

    Request = 0;

    //

    // now, stack has filled HID_SetReport_Value

    // HID_SetReport_Value[0] holds report ID

    // HID_SetReport_Value[1], report body

    // process it

    //

  }

}

tsuneo
Senior
Posted on May 28, 2012 at 17:44

For testing of Feature report implementation, Robert Marquardt's SimpleHidWrite is a good test bench on the PC side.

SimpleHidWrite (*1) on Jan Axelson's web page

http://www.lvr.com/files/SimpleHIDWrite3.zip

1) Plug-in your device, and run SimpleHIDWrite.

2) Click ''STM32 Custom HID'' on the top list of the window

3) Type in

- Report ID box - 1 (or 2, 3, ..., 7)

- next box - any number

4) Click ''Set Feature'' button at the bottom

Set_Report( Feature ) request is put to the device

OR

3') Type in

- Report ID box - 1 (or 2, 3, ..., 7)

- next box - left in blank

4') Click ''Get Feature'' button at the bottom

Get_Report( Feature ) request is put to the device. Result is shown on the pane.

(*1) Source code of SimpleHidWrite is included in HIDKomponente.zip (HIDKomponente\DEMOS\Delphi\ReadWriteDemo\)

http://www.soft-gems.net/supplement/download.php?ID=37

Tsuneo

mk299
Associate III
Posted on June 05, 2012 at 09:05

Hello Tsuneo,

thank you very much for the code. I implemented it the way you wrote and it works very well. The adaptation to my needs was no problem.

Also thank you for the link to SimpleHidWrite. I knew Jan's site, but didn't notice that very useful tool before.

Can you recommend a host library for using with C#? It should support interrupt transfers, getting/setting of feature reports and device notification.

Michael

tsuneo
Senior
Posted on June 05, 2012 at 12:35

> Can you recommend a host library for using with C#?

Mike O'brien / HidLibrary

https://github.com/mikeobrien/HidLibrary

This library doesn't have API for Get_Report( Feature ), unless I miss it.

But you may implement it, by referring to the source of WriteFeatureData().

Tsuneo

gafsos
Associate II
Posted on June 07, 2012 at 00:19

This feature will be supported on next library release (V4.0.0) (W34 ??)

mk299
Associate III
Posted on June 11, 2012 at 11:45

Hi Tsuneo,

I've adapted my report descriptor and reading and writing feature reports works fine so far. But if I write something like

PrevXferComplete = 1;
while
(1)
{
if
(PrevXferComplete)
{
Send_Buffer[0] = 0x05;
if
(Send_Buffer[1] == 0x00)
{
Send_Buffer[1] = 0x01;
}
else
{
Send_Buffer[1] = 0x00;
} 
USB_SIL_Write(EP1_IN, (uint8_t*) Send_Buffer, 2); 
SetEPTxValid(ENDP1);
PrevXferComplete = 0;
}
Delay(500000);
}

(copied from stm32l1xx_it.c from the ST library) inside main, EP1_IN_Callback is never called and therefore PrevXferComplete is not set to 1 again. Also the host seems to not receive any data as I can't see anything happening, neither with SimpleHidWrite nor with Mike O'briens HidLibrary/TestHarness. Is there anything special to be considered now with the modified CustomHID_Data_Setup? Michael
mk299
Associate III
Posted on June 11, 2012 at 21:44

Hi again,

it seems that the problem is caused by my report descriptor. When I take that one from the CustomHID example and the following code inside main

PrevXferComplete = 1;
while
(1)
{
if
(PrevXferComplete)
{
SendBuffer[0] = 0x05;
SendBuffer[1]++;
USB_SIL_Write(EP1_IN, (uint8_t*) SendBuffer, 2);
SetEPTxValid(ENDP1);
PrevXferComplete = 0;
}
Delay(750000);
}

I can see how the value increases. This works with SimpleHidWrite as well as with Mike O'briens HidLibrary/TestHarness. But when I change the report descriptor to

#define CUSTOMHID_SIZ_REPORT_DESC 111
const
uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
{
0x06, 0x00, 0xff, 
// USAGE_PAGE (Vendor Defined Page 1)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xa1, 0x01, 
// COLLECTION (Application)
0x15, 0x00, 
// LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, 
// LOGICAL_MAXIMUM (255)
0x75, 0x08, 
// REPORT_SIZE (8)
0x95, 0x01, 
// REPORT_COUNT (1)
0x85, 0x02, 
// REPORT_ID (2)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x02, 
// FEATURE (Data,Var,Abs)
0x85, 0x04, 
// REPORT_ID (4)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x02, 
// FEATURE (Data,Var,Abs)
0x85, 0x08, 
// REPORT_ID (8)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x02, 
// FEATURE (Data,Var,Abs)
0x85, 0x53, 
// REPORT_ID (83)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x02, 
// FEATURE (Data,Var,Abs)
0x85, 0x54, 
// REPORT_ID (84)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x02, 
// FEATURE (Data,Var,Abs)
0x95, 0x04, 
// REPORT_COUNT (4)
0x85, 0x51, 
// REPORT_ID (81)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x00, 
// FEATURE (Data,Ary,Abs)
0x85, 0x52, 
// REPORT_ID (82)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x00, 
// FEATURE (Data,Ary,Abs)
0x95, 0x06, 
// REPORT_COUNT (6)
0x85, 0x01, 
// REPORT_ID (1)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0x81, 0x00, 
// INPUT (Data,Ary,Abs)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0x91, 0x00, 
// OUTPUT (Data,Ary,Abs)
0x85, 0x03, 
// REPORT_ID (3)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x00, 
// FEATURE (Data,Ary,Abs)
0x85, 0x05, 
// REPORT_ID (5)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x00, 
// FEATURE (Data,Ary,Abs)
0x85, 0x50, 
// REPORT_ID (80)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x00, 
// FEATURE (Data,Ary,Abs)
0x85, 0x55, 
// REPORT_ID (85)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x00, 
// FEATURE (Data,Ary,Abs)
0x85, 0x56, 
// REPORT_ID (86)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x00, 
// FEATURE (Data,Ary,Abs)
0x95, 0x0f, 
// REPORT_COUNT (15)
0x85, 0x07, 
// REPORT_ID (7)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xb1, 0x00, 
// FEATURE (Data,Ary,Abs)
0xc0 
// END_COLLECTION
}; 
/* CustomHID_ReportDescriptor */

and SendBuffer[0] = 0x01 inside main/while(1) it doesn't work any more. So there seems to be an error around ReportID 1, which I'd like to use as interrupt input and output. Michael
mk299
Associate III
Posted on June 12, 2012 at 20:31

I found the problem:

- in usb_prop.c I changed EPTxCount and EPRxCount to 7 (both for Endpoint 1) - in usb_desc.c I changed wMaxPacketSize to 0x07 (was: 0x02) on both appearances for (input and output) Now it works fine.
gmahan250
Associate II
Posted on June 19, 2012 at 05:37

Hopefully I am not hijacking this thread but I am trying to do the same thing for the most part.

Tsuneo, you mention the following:

First, CustomHID_Data_Setup() is called by the stack just after Setup stage.

At Data stage, CustomHID_GetReport_Feature() or CustomHID_SetReport_Feature() is called.

For Set_Report, CustomHID_Status_In() is called at Status stage.

What is causing the CustomHID_GetReport_Feature() or CustomHID_SetReport_Feature() to be called in the data stage?

The reason I ask is I have the following code which is VERY similar.

RESULT Joystick_Data_Setup(uint8_t RequestNo)

{

  uint8_t *(*CopyRoutine)(uint16_t);

  uint16_t Length = 0;

  CopyRoutine = NULL;

  if ((RequestNo == GET_DESCRIPTOR)

      && (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))

      && (pInformation->USBwIndex0 == 0))

  {

    if (pInformation->USBwValue1 == REPORT_DESCRIPTOR)

    {

      CopyRoutine = Joystick_GetReportDescriptor;

    }

    else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE)

    {

      CopyRoutine = Joystick_GetHIDDescriptor;

    }

  } /* End of GET_DESCRIPTOR */

  /*** GET_PROTOCOL ***/

  else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))

           && RequestNo == GET_PROTOCOL)

  {

    CopyRoutine = Joystick_GetProtocolValue;

  }

  /*** GET_REPORT ***/

  else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))

           && RequestNo == GET_REPORT)

  {

    CopyRoutine = Joystick_GetReportValue;

  }

  /*** SET_REPORT ***/

  else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))

           && RequestNo == SET_REPORT)

  {

    CopyRoutine = Joystick_SetReportValue;

  }

  if (CopyRoutine == NULL)

  {

    return USB_UNSUPPORT;

  }

  pInformation->Ctrl_Info.CopyData = CopyRoutine;

  pInformation->Ctrl_Info.Usb_wOffset = 0;

  (*CopyRoutine)(Length);

  return USB_SUCCESS;

}

uint8_t *Joystick_SetReportValue(uint16_t Length)

{

    uint8_t mybuff[64];

    uint32_t numread = 0;

    numread = USB_SIL_Read(EP0_OUT, mybuff);

/*

  if (Length == 0)

  {

    pInformation->Ctrl_Info.Usb_wLength = 1;

    return NULL;

  }

  else

  {

    return (uint8_t *)(&ProtocolValue);

  }

*/

  return USB_SUCCESS;

When I look at the mybuff in Joystick_SetReportValue, it contains the data from the setup stage.  I have a breakpoint at the return in Joystick_SetReportValue.

I am sure I am missing it but have been staring at this way too long with no headway.  I do have a USB analyzer and see both the setup and data stages.

Gary