cancel
Showing results for 
Search instead for 
Did you mean: 

usbh hid custom device issues

jhgorse
Associate III
Posted on August 09, 2016 at 06:36

Hello,

I am trying to get a custom HID device to work on my STM32F429ZI board using the USB Host driver from the latest CubeMX for F4. I have successfully received boot mode keyboard data with this driver. The intended device requires report mode as far as I can tell. The device enumerates fine and with a few modifications we make it through to the usbh_hid.cUSBH_HID_Process() state machine. As described

http://www.keil.com/forum/22428/stm32f4-usb-host-hid-application/

by Tsuneo Chinzei, I've added some states so that we can send data over the interrupt out EP then receive data from the interrupt in EP.


static
USBH_StatusTypeDef USBH_HID_Process(USBH_HandleTypeDef *phost)

{


USBH_StatusTypeDef status = USBH_OK;

HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData;

USBH_URBStateTypeDef urb_state;

static
uint8_t _buf[61];


switch
(HID_Handle->state)

{

case
HID_INIT:

// HID_Handle->Init(phost);

memset
(_buf, 0, 
sizeof
(_buf));

_buf[0] = 2;

HID_Handle->state = HID_IDLE;

case
HID_IDLE:

// 7.2 Class-Specific Requests

if
(USBH_HID_GetReport (phost, 
// 10100001 - D2H, CLASS REQ

0x01, 
// Type 1 in, 2 out, 3 feature, ff res

0, 
// Id

HID_Handle->pData,

HID_Handle->length) == USBH_OK)

{

printf
(
''USBH_HID_GetReport(phost, type=0x1, Id=0, len=0x%x, HID_Handle->pData=0x%x 0x%x)\n''
,

HID_Handle->length,HID_Handle->pData[0],HID_Handle->pData[1]);


// fifo_write(&HID_Handle->fifo, HID_Handle->pData, HID_Handle->length);

HID_Handle->state = HID_SYNC;

}


break
;


case
HID_SYNC:


/* Sync with start of Even Frame */

if
(phost->Timer & 1)

{

HID_Handle->state = HID_SEND_DATA;

}

#if (USBH_USE_OS == 1)

osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);

#endif 

break
;


case
HID_SEND_DATA:

memset
(HID_Handle->pData, 0, HID_Handle->length);

HID_Handle->pData[0] = 2;


USBH_InterruptSendData(phost,

HID_Handle->pData,

HID_Handle->length,

HID_Handle->OutPipe);


HID_Handle->state = HID_POLL_SEND;

// HID_Handle->timer = phost->Timer;

// HID_Handle->DataReady = 0;

break
;


case
HID_POLL_SEND:


if
(USBH_LL_GetURBState(phost , HID_Handle->OutPipe) == USBH_URB_DONE)

{

HID_Handle->state = HID_GET_DATA;

}

else
if
(USBH_LL_GetURBState(phost , HID_Handle->OutPipe) == USBH_URB_STALL) 
/* IN Endpoint Stalled */

{

printf
(
''HID_POLL_SEND stalled''
);

/* Issue Clear Feature on interrupt IN endpoint */

if
(USBH_ClrFeature(phost,

HID_Handle->ep_addr) == USBH_OK)

{

/* Change state to issue next OUT token */

HID_Handle->state = HID_SYNC;

}

}

break
;


case
HID_GET_DATA:


USBH_InterruptReceiveData(phost, 

HID_Handle->pData,

HID_Handle->length,

HID_Handle->InPipe);


HID_Handle->state = HID_POLL_GET;

HID_Handle->timer = phost->Timer;

HID_Handle->DataReady = 0;

break
;


case
HID_POLL_GET:


if
(USBH_LL_GetURBState(phost, HID_Handle->InPipe) == USBH_URB_DONE)

{

if
(HID_Handle->DataReady == 0)

{

// fifo_write(&HID_Handle->fifo, HID_Handle->pData, HID_Handle->length);

HID_Handle->DataReady = 1;

USBH_HID_EventCallback(phost);

#if (USBH_USE_OS == 1)

osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);

#endif 

}

}

else
if
(USBH_LL_GetURBState(phost , HID_Handle->InPipe) == USBH_URB_STALL) 
/* IN Endpoint Stalled */

{

printf
(
''HID_POLL_GET stalled''
);

/* Issue Clear Feature on interrupt IN endpoint */

if
(USBH_ClrFeature(phost,

HID_Handle->ep_addr) == USBH_OK)

{

/* Change state to issue next IN token */

HID_Handle->state = HID_GET_DATA;

}

} 



break
;


default
:

break
;

}

return
status;

}


/**

* @brief USBH_HID_SOFProcess 

* The function is for managing the SOF Process 

* @param phost: Host handle

* @retval USBH Status

*/

static
USBH_StatusTypeDef USBH_HID_SOFProcess(USBH_HandleTypeDef *phost)

{

HID_HandleTypeDef *HID_Handle = (HID_HandleTypeDef *) phost->pActiveClass->pData;


if
(HID_Handle->state == HID_POLL_GET)

{

if
(( phost->Timer - HID_Handle->timer) >= HID_Handle->poll)

{

HID_Handle->state = HID_SYNC; 
// TODO: Send then get?

#if (USBH_USE_OS == 1)

osMessagePut ( phost->os_event, USBH_URB_EVENT, 0);

#endif 

}

}

return
USBH_OK;

}

Sending appears to always work, though the buffer never changes in the receive interrupt and it always says the URB status is IDLE. For reference, this is a working example of what I want written in a handful of lines in ruby using the HID API library:


require 
''hid_api''

# http://www.signalus/oss/hidapi/


handle 
=
HidApi::hid_open(
0x05AC
, 
0x024F
, 
0
)


buf 
=
Array.new(
61
) { |i| (
0
) };

buf[
0
] 
=
0x2


while
1
do

handle.write(buf)

rbuf 
=
handle.read_timeout(buf.length,
1000
)

print
rbuf.read_bytes(
61
).unpack(
''C*''
), 
''\n''

sleep(
0.2
)

end


HidApi::hid_close(handle)

How can I get this simple HID use case to work with the CubeMX USB Host driver? Cheers, Joe #hid #!cubemx #usbh
8 REPLIES 8
jhgorse
Associate III
Posted on August 09, 2016 at 14:56

Thank you,rygelxvi, this helps with an intermittent inability to get through this HID_IDLE state which is an optional thing for my device.

I came across an issue in which the URB was USBH_URB_NOTREADY in

USBH_HandleControl() at usbh_ctlreq.c. I just added handlers for that in both of the WAIT states to go back to the command before it.

case
CTRL_DATA_IN_WAIT:
...
else
if
(URB_Status == USBH_URB_NOTREADY)
{
/* Device not ready. Try again. */
phost->Control.state = CTRL_DATA_IN;
}
break
;
/// And
case
CTRL_DATA_OUT_WAIT:
URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_out); 
...
else
if
(URB_Status == USBH_URB_NOTREADY)
{ 
/* Nack received from device */
phost->Control.state = CTRL_DATA_OUT;
} 

I am not sure how correct those are. Now the issue is that when I try to

USBH_InterruptReceiveData(phost, 
_buf,
sizeof
(_buf),
HID_Handle->InPipe);

The URB state is alwaysUSBH_URB_IDLE after this call.

Earlier, I received one packet from my device. Though after that it has not repeated the success. What do I need to do to receive data over the

USBH_InterruptReceiveData()?

Cheers, Joe
jhgorse
Associate III
Posted on August 09, 2016 at 15:52

0690X00000605IpQAI.png

jhgorse
Associate III
Posted on August 11, 2016 at 05:36

I have tried nearly every imaginable combination of the provided Read/Write functions with varying degrees of fragility and general failure.

Writes I have tried in various sequences. Each was tried standalone:

// Seems to put stuff into the tx fifo
USB_WritePacket(USB_OTG_HS,
(uint8_t *)_buf, 
// data
1, 
// ch_ep_num
64, 
// Length
0); 
// DMA
// Also seems to get the raw data to tx fifo
USBH_InterruptSendData(phost,
_buf,
sizeof
(_buf),
HID_Handle->OutPipe);
// Something totally different, executes, but fails to perform intended function
if
(USBH_HID_SetReport (phost, 
// H2D, CLASS REQ
0x02, 
// Type 1 in, 2 out, 3 feature, ff res
1, 
// Id
_buf,
sizeof
(_buf)) == USBH_OK)
{
HID_Handle->state = HID_SYNC;
}

Reads. Also independently used. Sometimes with HID_SYNC, sometimes without.

case
HID_SYNC:
if
(phost->Timer & 1) 
/* Sync with start of Even Frame */
{
HID_Handle->state = HID_GET_DATA;
}
break
;
case
HID_GET_DATA:
if
(USBH_HID_GetReport (phost, 
// 10100001 - D2H, CLASS REQ
0x01, 
// Type 1 in, 2 out, 3 feature, ff res
0, 
// Id
HID_Handle->pData,
HID_Handle->length) == USBH_OK)
{
printf
(
''USBH_HID_GetReport(phost, type=0x1, Id=0, len=0x%x, HID_Handle->pData=0x%x 0x%x)
''
,
HID_Handle->length,HID_Handle->pData[0],HID_Handle->pData[1]);
fifo_write(&HID_Handle->fifo, HID_Handle->pData, HID_Handle->length);
HID_Handle->state = HID_IDLE;
}
// Interrupt Read, never works
USBH_InterruptReceiveData(phost,
HID_Handle->pData,
HID_Handle->length,
HID_Handle->InPipe);
HID_Handle->state = HID_POLL_GET;
// Direct read fifo, does nothing
dest = USB_ReadPacket(USB_OTG_HS,
HID_Handle->pData, 
// dest buffer
HID_Handle->length); 
// Length
if
(dest != NULL) {
fifo_write(&HID_Handle->fifo, HID_Handle->pData, HID_Handle->length);
HID_Handle->DataReady = 1;
USBH_HID_EventCallback(phost);
HID_Handle->timer = phost->Timer;
HID_Handle->DataReady = 0;
HID_Handle->state = HID_IDLE;
}
break
;

The only one that sometimes gets a packet or two of sensor values out of the device is the ''

USBH_HID_GetReport()

'' block when it is run first thing, without putting the ''0x02 x01 ...'' 61 byte string out there. I read the HID API library for their hid_read() and hid_write() functionality and I don't see any magic in the write/read cycle I've proposed and implemented previously. For reference, the project is here:

https://github.com/jhgorse/STM32F429ZITx_cube_usbh_hid

https://github.com/jhgorse/STM32F429ZITx_cube_usbh_hid

Cheers, Joe
jhgorse
Associate III
Posted on August 11, 2016 at 05:42

These are the report descriptors from the device:

code 
const
hid_report_descriptor HIDREPORTDESC =
{
0x06, 0x00, 0xff, 
// USAGE_PAGE (Vendor Defined Page 1)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0xa1, 0x01, 
// COLLECTION (Application)
//#define IN_CONTROL 0xFE
//#define IN_CONTROL_SIZE 8
0x85, IN_CONTROL, 
// Report ID
0x95, IN_CONTROL_SIZE, 
// REPORT_COUNT ()
0x75, 0x08, 
// REPORT_SIZE (8)
0x26, 0xff, 0x00, 
// LOGICAL_MAXIMUM (255)
0x15, 0x00, 
// LOGICAL_MINIMUM (0)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0x81, 0x02, 
// INPUT (Data,Var,Abs)
//#define OUT_CONTROL 0xFD
//#define OUT_CONTROL_SIZE 8
0x85, OUT_CONTROL, 
// Report ID
0x95, OUT_CONTROL_SIZE, 
// REPORT_COUNT ()
0x75, 0x08, 
// REPORT_SIZE (8)
0x26, 0xff, 0x00, 
// LOGICAL_MAXIMUM (255)
0x15, 0x00, 
// LOGICAL_MINIMUM (0)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0x91, 0x02, 
// OUTPUT (Data,Var,Abs)
//#define IN_DATA 0x01
//#define IN_DATA_SIZE 60
0x85, IN_DATA, 
// Report ID
0x95, IN_DATA_SIZE, 
// REPORT_COUNT ()
0x75, 0x08, 
// REPORT_SIZE (8)
0x26, 0xff, 0x00, 
// LOGICAL_MAXIMUM (255)
0x15, 0x00, 
// LOGICAL_MINIMUM (0)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0x81, 0x02, 
// INPUT (Data,Var,Abs)
//#define OUT_DATA 0x02
//#define OUT_DATA_SIZE 60
0x85, OUT_DATA, 
// Report ID
0x95, OUT_DATA_SIZE, 
// REPORT_COUNT ()
0x75, 0x08, 
// REPORT_SIZE (8)
0x26, 0xff, 0x00, 
// LOGICAL_MAXIMUM (255)
0x15, 0x00, 
// LOGICAL_MINIMUM (0)
0x09, 0x01, 
// USAGE (Vendor Usage 1)
0x91, 0x02, 
// OUTPUT (Data,Var,Abs)
0xC0 
// end Application Collection
};

jhgorse
Associate III
Posted on August 12, 2016 at 07:11

My issue was with the DATA0/DATA1 toggle. It needed to toggle in order to continue operation past the first OUT/IN sequence over the Interrupt Ep 1. This is solved now.

Cheers,

Joe

P.S. The TotalPhase Beagle 12 made all the difference in the world for learning the USB spec by actually watching working devices and exploring that way rather than the endless texts written about it.

P.P.S. Microsoft Sharepoint was not meant to do this. Please use something with better performance at scale, like the Usenet Newsgroups, PHPBB, dialup bulletin boards, and so on.
Posted on April 29, 2018 at 21:55

Hi Joseph,

thanks for this thread, I am also struggling with HID host for another HID device (CP2110 USB-HID converter which I have to access via USB in order to communicate with a medical device). What does toggling the DATA0 and DATA1 mean? Also, would you mind dropping a line about what modifications did you have to do in order to pass the USBH_HID_Process state machine? I have done some modifications but the program then freezes.

Thanks for your time.

Posted on May 02, 2018 at 16:58

Use Wireshark or Beagle to snoop the transmission and decode the packets. This will show you if you are toggling DATA0/DATA1 or not. Good luck.