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
gmahan250
Associate II
Posted on June 19, 2012 at 05:38

Duplicate Post...sorry

tsuneo
Senior
Posted on June 19, 2012 at 11:51

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

CustomHID_Data_Setup() registers the user callback, CustomHID_GetReport_Feature() or CustomHID_SetReport_Feature(), to pInformation->Ctrl_Info.CopyData

RESULT CustomHID_Data_Setup(uint8_t RequestNo)

{

  ...

  ...

  /*** 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;

  }

  ...

  ...

  pInformation->Ctrl_Info.CopyData = CopyRoutine;  // <--- register user callback to stack

  return USB_SUCCESS;

}

For Control write transfer, the stack calls ''CopyData'' function pointer at DataStageOut()

\STM32_USB-FS-Device_Lib_V3.3.0\Libraries\STM32_USB-FS-Device_Driver\src\usb_core.c

void DataStageOut(void)

{

  ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info;

  uint32_t save_rLength;

  save_rLength = pEPinfo->Usb_rLength;

  if (pEPinfo->CopyData && save_rLength)

  {

    uint8_t *Buffer;

    uint32_t Length;

    Length = pEPinfo->PacketSize;

    if (Length > save_rLength)

    {

      Length = save_rLength;

    }

    Buffer = (*pEPinfo->CopyData)(Length);   // <--- user callback is called to get a buffer address

    pEPinfo->Usb_rLength -= Length;

    pEPinfo->Usb_rOffset += Length;

                                             // <--- stack copies data from EP to the buffer

  #ifdef STM32F10X_CL  

    PCD_EP_Read(ENDP0, Buffer, Length);

  #else  

    PMAToUserBufferCopy(Buffer, GetEPRxAddr(ENDP0), Length);

  #endif  /* STM32F10X_CL */

  }

  ...

As you see, the buffer is filled after user callback is called.

Therefore, we have to get another chance at CustomHID_Status_In(), to process data on the buffer.

As of Control Read transfer, the user callback is called from DataStageIn()

\STM32_USB-FS-Device_Lib_V3.3.0\Libraries\STM32_USB-FS-Device_Driver\src\usb_core.c

void DataStageIn(void)

{

  ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info;

  uint32_t save_wLength = pEPinfo->Usb_wLength;

  uint32_t ControlState = pInformation->ControlState;

  uint8_t *DataBuffer;

  uint32_t Length;

  ...

  ...

  Length = pEPinfo->PacketSize;

  ControlState = (save_wLength <= Length) ? LAST_IN_DATA : IN_DATA;

  if (Length > save_wLength)

  {

    Length = save_wLength;

  }

  DataBuffer = (*pEPinfo->CopyData)(Length);    // <---  user callback is called to get the buffer address

                                                // <---  Copy data from the buffer to EP

#ifdef STM32F10X_CL

  PCD_EP_Write (ENDP0, DataBuffer, Length);

#else   

  UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length);

#endif /* STM32F10X_CL */

  ...

Anyway, this implementation is too redundant, as usual of ST.

My recommendation is,

1) At SETUP stage, the stack calls user_SETUP_handler() (combined CustomHID_Data_Setup() and CustomHID_Data_Setup())

2) user_SETUP_handler() interprets the SETUP packet, to know the request. If it doesn't accept the request, this routines returns FALSE.

3-1) For No-data Control transfer, user_SETUP_handler() process the requests and returns TRUE

3-2) For Control Read transfer, user_SETUP_handler() fills a buffer with requested data. And it passes the buffer address and data size over stack parameters. And then, it returns TRUE

3-3) For Control Write transfer, user_SETUP_handler() passes a buffer (greater than wLength) and a function pointer of user callback to the stack. And it returns TRUE. The stack calls this user callback at STATUS stage.

The stack processes the rest of all 🙂

Tsuneo

gmahan250
Associate II
Posted on June 26, 2012 at 02:26

Tsuneo,

Thanks for all the information.  What I didnt realize was that the desired routine is called twice.  Once with the call

(*CopyRoutine)(0);

And then later by the stack using the length parameter to designate which stage its in.

Gary

gmahan250
Associate II
Posted on June 26, 2012 at 06:14

So I am still learning how the STM stack is working.  I come from a background of using LUFA USB stack for AVRs and the Microchip USB Stack for PIC processors.

So I simple did the following in my routine and see the Set Report Data

uint8_t *Joystick_SetReportValue(uint16_t Length)

{

    uint8_t mybuff[64];

    uint32_t numread = 0;

  if (Length == 0)

  {

   

pInformation->Ctrl_Info.Usb_wLength = 1;

    return NULL;

  }

  else

  {

    numread = USB_SIL_Read(EP0_OUT, mybuff);

  }

  return USB_SUCCESS;

}

Question is to what I should set as the length (highlighted in green above).  It seems to be ok as is but may be introducing problems.

Thanks again for all the help.

Gary

tsuneo
Senior
Posted on June 27, 2012 at 06:42

Hi Gary,

Your code is modified as follows,

Tsuneo

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

uint8_t mybuff[64];    // this buffer must be global

RESULT Joystick_Data_Setup(uint8_t RequestNo)

{

  ...

  ...

  /*** SET_REPORT ***/

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

           && RequestNo == SET_REPORT)

  {

    CopyRoutine = Joystick_SetReportValue;

    Request = SET_REPORT;          // <------- add this line to here

  }

  ...

  ...

 

uint8_t *Joystick_SetReportValue(uint16_t Length)

{

  if (Length == 0)

  {

    pInformation->Ctrl_Info.Usb_wLength = 1;

    return NULL;

  }

  else

  {

    return mybuff;

  }

}

void Joystick_Status_In(void)

{

  if (Request == SET_REPORT)         // SET_REPORT completion

  {

    Request = 0;

    //

    // now, stack has filled mybuff[]

    // pInformation->USBwLengths.w holds transferred size

    // process it

    //

  }

}

bluscape
Associate III
Posted on March 01, 2013 at 10:28

Hi Guys,

I have a custom device that has some memory and a micro-controller. Due to cost implications the device cannot have its own USB interface. To access the memory and the micro controller status I use a custom made USB dongle that converts USB commands into SPI commands (I communicate with the memory and micro by means of SPI). The USB dongle has a STM32L151.

I've done a HID implementation using Delphi (HIDKomponente).

The memory device is 512kb and on some PC's I can read the entire in 13seconds and write the entire device in 30seconds but then on other PC it will take 58seconds and 300seconds respectively. The code is at the a point where the speed can't be increased anymore (reduced HID polling interval to 1ms) but 300seconds is WAY TOO LONG and I need an alternative.

As always, I'm trying to do this without having to install USB drivers and do it as fast as possible. But with the inherent delays of HID I do not think I'm going to get the speed I want.

I've been considering maybe using mass storage class. With this I should get quite high transfer rates and again I wont need any drivers. This will work fine fro programming the memory but what about the micro status. Will I be able to read the micro status using mass storage?

If you guys can propose any other USB class with high transfer rates that does not require drivers and can do what I've described above.

Thank you

tsuneo
Senior
Posted on March 01, 2013 at 18:01

> If you guys can propose any other USB class with high transfer rates that does not require drivers and can do what I've described above.

Generic Text-Only printer

Visit to this post on Keil forum for the details.

http://www.keil.com/forum/21682/

.NET Framework System.Printing.PrintQueue makes the host code simple.

See the example on this MSDN page.

http://msdn.microsoft.com/en-us/library/ms552912(v=vs.90).aspx

You may read out from printer's JobStream, too.

Tsuneo

bluscape
Associate III
Posted on March 03, 2013 at 06:54

That looks nice. I don't use .NET, I use Delphi. Can I use Delphi printer? or maybe libusb?

Libusb supports bulk interfaces.

tsuneo
Senior
Posted on March 03, 2013 at 18:01

> I don't use .NET, I use Delphi.

Delphi has supported .NET Framework since Delphi8.

But Embarcadero made .NET support as an option of prism, not for XE3.

> Can I use Delphi printer?

Maybe, but I'm not sure.

You may code it using just Win32 API

HOWTO: Send Raw Data to a Printer by Using the Win32 API

http://support.microsoft.com/kb/138594/en-us

> maybe libusb?

If libusb-Win32 would be acceptable for you, any USB class, including vendor-specific, will do 😉

libusb-Win32 isn't an in-box driver.

Tsuneo

mk299
Associate III
Posted on August 21, 2015 at 22:17

Hi Tsuneo,

did you already extend the Cube framework to also support GetFeature reports and if yes, could you write down the neccessary changes? SetFeature seems to be implemented and working out of the box.

Best regards,

Michael