STM32L4 USB Host communicating with a custom class device with multiple interrupt out endpoints.
Hi all,
I am using the Nucleo-L476RG dev board and am using it as a USB Host to communicate with a custom vendor device which has 4 interrupt IN endpoints and 4 interrupt OUT endpoints.
My code has been generated using CubeMX and I have generated a custom class for this device. The custom class is loosely based on the HID keyboard/mouse class example provided in the package.
I can successfully enumerate my device and initiate my custom class driver. Communication with the 4 IN pipes works fine, however when writing data to the interrupt OUT pipes, my program crashes as soon as I send to the 3rd of 4th endpoint. If I don’t send any data to the 3rd or 4th endpoint my code work fine.
If I USBH_ClosePipe + USBH_FreePipe the out pipe after each transaction, then USBH_AllocPipe + USBH_OpenPipe the corresponding pipe before the transaction, the code works with all 4 out endpoints, but this doesn’t seem like the right thing to do.
By ‘crash’ It’s difficult to tell what it is doing exactly, however my main while loop gets stuck within the MX_USB_HOST_Process function, and the debugger seems to indicate that it’s just stuck in the HAL_HCD_IRQHandler function.
Hoping anyone can provide any suggestions on what I might be missing or if this is expected when dealing with multiple interrupt endpoints?
I'm not particularly knowledgeable in the low level USB details so apologies if it's obvious.
My Class Init Function:
static USBH_StatusTypeDef USBH_DEVICE_InitDevice(USBH_HandleTypeDef *phost) {
interface = USBH_FindInterface(phost, phost->pActiveClass->ClassCode, 0xFF, 0xFF);
USBH_SelectInterface(phost, interface);
phost->pActiveClass->pData = (DEVICE_HandleTypeDef *) USBH_malloc(sizeof(DEVICE_HandleTypeDef));
DEVICE_Handle = (DEVICE_HandleTypeDef *) phost->pActiveClass->pData;
DEVICE_Handle->state = DEVICE_INIT;
//We know what all the addresses are without parsing the descriptor so just allocate them
DEVICE_Handle->InEp[0] = 0x81;
DEVICE_Handle->InEp[1] = 0x83;
DEVICE_Handle->InEp[2] = 0x85;
DEVICE_Handle->InEp[3] = 0x87;
DEVICE_Handle->OutEp[0] = 0x01;
DEVICE_Handle->OutEp[1] = 0x03;
DEVICE_Handle->OutEp[2] = 0x05;
DEVICE_Handle->OutEp[3] = 0x07;
DEVICE_Handle->InEpSize = 0x0020;
DEVICE_Handle->OutEpSize = 0x0020;
//Create a Pipe to each IN end point
for (uint8_t i = 0; i < 4; i++) {
DEVICE_Handle->InPipe[i] = USBH_AllocPipe(phost, DEVICE_Handle->InEp[i]);
USBH_OpenPipe(phost, DEVICE_Handle->InPipe[i], DEVICE_Handle->InEp[i],
phost->device.address, phost->device.speed,
USB_EP_TYPE_INTR, DEVICE_Handle->InEpSize);
USBH_LL_SetToggle(phost, DEVICE_Handle->InPipe[i], 0);
printf("InEPaddr 0x%02x allocated to pipe 0x%02x\n",
DEVICE_Handle->InEp[i], DEVICE_Handle->InPipe[i]);
}
//Create a Pipe to each OUT end point
for (uint8_t i = 0; i < 4; i++) {
DEVICE_Handle->OutPipe[i] = USBH_AllocPipe(phost, DEVICE_Handle->OutEp[i]);
USBH_OpenPipe(phost, DEVICE_Handle->OutPipe[i], DEVICE_Handle->OutEp[i],
phost->device.address, phost->device.speed,
USB_EP_TYPE_INTR, DEVICE_Handle->OutEpSize);
USBH_LL_SetToggle(phost, DEVICE_Handle->OutPipe[i], 0);
printf("OutEPaddr 0x%02x allocated to pipe 0x%02x\n", DEVICE_Handle->OutEp[i], DEVICE_Handle->OutPipe[i]);
}
DEVICE_Handle->state = DEVICE_POLL;
}My SOFProcess Function
static USBH_StatusTypeDef USBH_DEVICE_SOFProcess(USBH_HandleTypeDef *phost) {
DEVICE_HandleTypeDef *DEVICE_Handle = (DEVICE_HandleTypeDef *) phost->pActiveClass->pData;
if (DEVICE_Handle->state == DEVICE_POLL) {
if ((phost->Timer - DEVICE_Handle->InTimer) > 1) {
DEVICE_Handle->state = DEVICE_GET_DATA;
}
if ((phost->Timer - DEVICE_Handle->OutTimer) > 8) {
DEVICE_Handle->state = DEVICE_SEND_DATA;
}
}
return USBH_OK;
}My Process Function:
static USBH_StatusTypeDef USBH_DEVICE_Process(USBH_HandleTypeDef *phost) {
DEVICE_HandleTypeDef *DEVICE_Handle = (DEVICE_HandleTypeDef *) phost->pActiveClass->pData;
switch (DEVICE_Handle->state) {
case DEVICE_INIT:
DEVICE_Handle->Init(phost);
DEVICE_Handle->state = DEVICE_POLL;
break;
case DEVICE_POLL:
/* CHECK IF INPUT DATA HAS BEEN RECEIVED */
for (uint8_t i = 0; i < 4; i++) {
USBH_URBStateTypeDef URB = USBH_LL_GetURBState(phost, DEVICE_Handle->InPipe[i]);
if ((URB == USBH_URB_DONE || URB == USBH_URB_IDLE) && DEVICE[i].inputDataReady == 0) {
DEVICE[i].inputDataReady = 1;
USBH_DEVICE_ParseReport(i, DEVICE[i].inputBuffer);
} else if (URB == USBH_URB_ERROR) {
DEVICE[i].inputDataReady = 1;
printf("[%u]: USBH_URB_ERROR",i);
} else if (URB == USBH_URB_STALL) {
if (USBH_ClrFeature(phost, DEVICE_Handle->InEp[i]) == USBH_OK) {
DEVICE_Handle->state = DEVICE_GET_DATA;
}
printf("[%u]: USBH_URB_STALL",i);
DEVICE[i].inputDataReady = 1;
}
}
/* CHECK IF OUTPUT DATA HAS BEEN SENT */
for (int i = 0; i < 4; i++) {
USBH_URBStateTypeDef URB = USBH_LL_GetURBState(phost, DEVICE_Handle->OutPipe[i]);
if (DEVICE[i].outputDataSent == 0 && (URB == USBH_URB_DONE/* || URB == USBH_URB_IDLE*/)) {
USBH_LL_SetToggle(phost, DEVICE_Handle->OutPipe[i],
1 ^ USBH_LL_GetToggle(phost, DEVICE_Handle->OutPipe[i]));
//USBH_ClosePipe(phost, DEVICE_Handle->OutPipe[i]);
//USBH_FreePipe(phost, DEVICE_Handle->OutPipe[i]);
//DEVICE_Handle->OutPipe[i] = 0x00;
DEVICE[i].outputDataSent = 1;
} else if (URB == USBH_URB_ERROR) {
DEVICE[i].outputDataSent = 1;
printf("[%u]: USBH_URB_ERROR", i);
} else if (URB == USBH_URB_STALL) {
if (USBH_ClrFeature(phost, DEVICE_Handle->OutEp[i]) == USBH_OK) {
DEVICE_Handle->state = DEVICE_SEND_DATA;
}
printf("[%u]: USBH_URB_STALL", i);
DEVICE[i].outputDataSent = 1;
}
}
break;
case DEVICE_SEND_DATA:
//printf("DEVICE_SEND_DATA\n");
for (int i = 0; i < 4; i++) {
//DEVICE_Handle->OutPipe[i] = USBH_AllocPipe(phost, DEVICE_Handle->OutEp[i]);
//USBH_OpenPipe(phost, DEVICE_Handle->OutPipe[i], DEVICE_Handle->OutEp[i],
phost->device.address, phost->device.speed,
USB_EP_TYPE_INTR, DEVICE_Handle->OutEpSize);
//USBH_LL_SetToggle(phost, DEVICE_Handle->OutPipe[i], 0);
USBH_InterruptSendData(phost, DEVICE[i].outputBuffer, 12, DEVICE_Handle->OutPipe[i]);
DEVICE_Handle->state = DEVICE_POLL;
DEVICE_Handle->OutTimer = phost->Timer;
DEVICE[i].outputDataSent = 0;
}
/* no break we want to jump into GET_DATA below */
case DEVICE_GET_DATA:
for (uint8_t i=0;i<4;i++){
USBH_URBStateTypeDef URB = USBH_LL_GetURBState(phost, DEVICE_Handle->InPipe[i]);
if(URB==USBH_URB_IDLE || URB==USBH_URB_DONE){
USBH_InterruptReceiveData(phost, DEVICE[i].inputBuffer,
DEVICE_Handle->InEpSize, DEVICE_Handle->InPipe[i]);
DEVICE[i].inputDataReady = 0;
}
}
DEVICE_Handle->state = DEVICE_POLL;
DEVICE_Handle->InTimer = phost->Timer;
break;
default:
printf("Not meant to be here %u", DEVICE_Handle->state);
break;
}
return USBH_OK;
}