More work on enumerating cheap POS mice, arrrrgh.

Added timeout checks and retry functionality to control transaction
state machine. But some times the device just won't talk, so we flash
the fault LED slowly to give the user some clue about what happened.
USG_1.0
Robert Fisk 9 years ago
parent 474b95e475
commit a5f4e52e02

@ -190,6 +190,8 @@
#define USBH_MAX_ERROR_COUNT 2 #define USBH_MAX_ERROR_COUNT 2
#define USBH_DEVICE_ADDRESS 1 #define USBH_DEVICE_ADDRESS 1
#define USBH_CTRL_TRANSACTION_TIMEOUT_MS 100
/** /**
* @} * @}
@ -413,8 +415,8 @@ typedef struct
uint8_t pipe_out; uint8_t pipe_out;
uint8_t pipe_size; uint8_t pipe_size;
uint8_t *buff; uint8_t *buff;
uint32_t timer;
uint16_t length; uint16_t length;
uint16_t timer;
USB_Setup_TypeDef setup; USB_Setup_TypeDef setup;
CTRL_StateTypeDef state; CTRL_StateTypeDef state;
uint8_t errorcount; uint8_t errorcount;

@ -152,6 +152,8 @@ USBH_StatusTypeDef USBH_DeInit(USBH_HandleTypeDef *phost)
USBH_LL_Stop(phost); USBH_LL_Stop(phost);
} }
USBH_LL_DeInit(phost);
return USBH_OK; return USBH_OK;
} }
@ -187,6 +189,7 @@ static USBH_StatusTypeDef DeInitStateMachine(USBH_HandleTypeDef *phost)
phost->device.address = USBH_ADDRESS_DEFAULT; phost->device.address = USBH_ADDRESS_DEFAULT;
phost->device.speed = USBH_SPEED_FULL; phost->device.speed = USBH_SPEED_FULL;
phost->device.is_connected = 0;
return USBH_OK; return USBH_OK;
} }
@ -840,7 +843,7 @@ USBH_StatusTypeDef USBH_LL_Connect (USBH_HandleTypeDef *phost)
} }
else if (phost->gState == HOST_DEV_WAIT_FOR_ATTACHMENT) else if (phost->gState == HOST_DEV_WAIT_FOR_ATTACHMENT)
{ {
//On the first boot after a power cycle with a low-speed device pre-attached, //On a cold boot with a low-speed device pre-attached,
//we get a second port-connected interrupt!??? //we get a second port-connected interrupt!???
//So go back and do the port reset again... //So go back and do the port reset again...
phost->gState = HOST_IDLE; phost->gState = HOST_IDLE;

@ -603,15 +603,14 @@ static USBH_StatusTypeDef USBH_HandleControl (USBH_HandleTypeDef *phost)
{ {
case CTRL_SETUP: case CTRL_SETUP:
/* send a SETUP packet */ /* send a SETUP packet */
phost->Control.timer = phost->Timer;
USBH_CtlSendSetup(phost, USBH_CtlSendSetup(phost,
(uint8_t *)phost->Control.setup.d8 , (uint8_t *)phost->Control.setup.d8 ,
phost->Control.pipe_out); phost->Control.pipe_out);
phost->Control.state = CTRL_SETUP_WAIT; phost->Control.state = CTRL_SETUP_WAIT;
break; break;
case CTRL_SETUP_WAIT: case CTRL_SETUP_WAIT:
URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_out); URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_out);
/* case SETUP packet sent successfully */ /* case SETUP packet sent successfully */
if(URB_Status == USBH_URB_DONE) if(URB_Status == USBH_URB_DONE)
@ -647,22 +646,24 @@ static USBH_StatusTypeDef USBH_HandleControl (USBH_HandleTypeDef *phost)
phost->Control.state = CTRL_STATUS_IN; phost->Control.state = CTRL_STATUS_IN;
} }
} }
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if(URB_Status == USBH_URB_ERROR) else if(URB_Status == USBH_URB_ERROR)
{ {
phost->Control.state = CTRL_ERROR; phost->Control.state = CTRL_ERROR;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if (URB_Status == USBH_URB_NOTREADY) else if (URB_Status == USBH_URB_NOTREADY)
{ {
//Some mice cause a transaction error interrupt (HCINT_TXERR) at this point, //Some mice cause a transaction error interrupt (HCINT_TXERR) at this point.
//so we retry and hope for the best! //Other mice just NAK our transaction.
phost->Control.state = CTRL_SETUP; //Either way we need to retry, but keep our original timeout so we don't wait forever.
USBH_CtlSendSetup(phost,
(uint8_t *)phost->Control.setup.d8 ,
phost->Control.pipe_out);
}
if ((int32_t)(phost->Timer - phost->Control.timer) >= USBH_CTRL_TRANSACTION_TIMEOUT_MS)
{
phost->Control.state = CTRL_ERROR;
} }
break; break;
@ -678,171 +679,139 @@ static USBH_StatusTypeDef USBH_HandleControl (USBH_HandleTypeDef *phost)
break; break;
case CTRL_DATA_IN_WAIT: case CTRL_DATA_IN_WAIT:
URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_in); URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_in);
/* check is DATA packet transferred successfully */ /* check is DATA packet transferred successfully */
if (URB_Status == USBH_URB_DONE) if (URB_Status == USBH_URB_DONE)
{ {
phost->Control.state = CTRL_STATUS_OUT; phost->Control.state = CTRL_STATUS_OUT;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
/* manage error cases*/ /* manage error cases*/
if (URB_Status == USBH_URB_STALL) if (URB_Status == USBH_URB_STALL)
{ {
/* In stall case, return to previous machine state*/ //Retry transaction
status = USBH_NOT_SUPPORTED; phost->Control.state = CTRL_ERROR;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if (URB_Status == USBH_URB_ERROR) else if (URB_Status == USBH_URB_ERROR)
{ {
/* Device error */ /* Device error */
phost->Control.state = CTRL_ERROR; phost->Control.state = CTRL_ERROR;
#if (USBH_USE_OS == 1) }
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif if ((int32_t)(phost->Timer - phost->Control.timer) >= USBH_CTRL_TRANSACTION_TIMEOUT_MS)
{
phost->Control.state = CTRL_ERROR;
} }
break; break;
case CTRL_DATA_OUT: case CTRL_DATA_OUT:
phost->Control.timer = phost->Timer;
USBH_CtlSendData (phost, USBH_CtlSendData (phost,
phost->Control.buff, phost->Control.buff,
phost->Control.length , phost->Control.length ,
phost->Control.pipe_out, phost->Control.pipe_out,
1); 1);
phost->Control.timer = phost->Timer;
phost->Control.state = CTRL_DATA_OUT_WAIT; phost->Control.state = CTRL_DATA_OUT_WAIT;
break; break;
case CTRL_DATA_OUT_WAIT: case CTRL_DATA_OUT_WAIT:
URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_out); URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_out);
if (URB_Status == USBH_URB_DONE) if (URB_Status == USBH_URB_DONE)
{ /* If the Setup Pkt is sent successful, then change the state */ { /* If the Setup Pkt is sent successful, then change the state */
phost->Control.state = CTRL_STATUS_IN; phost->Control.state = CTRL_STATUS_IN;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
/* handle error cases */ /* handle error cases */
else if (URB_Status == USBH_URB_STALL) else if (URB_Status == USBH_URB_STALL)
{ {
/* In stall case, return to previous machine state*/ //Retry transaction
phost->Control.state = CTRL_STALLED; phost->Control.state = CTRL_ERROR;
status = USBH_NOT_SUPPORTED;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if (URB_Status == USBH_URB_NOTREADY) else if (URB_Status == USBH_URB_NOTREADY)
{ {
/* Nack received from device */ /* Nack received from device */
phost->Control.state = CTRL_DATA_OUT; phost->Control.state = CTRL_DATA_OUT;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if (URB_Status == USBH_URB_ERROR) else if (URB_Status == USBH_URB_ERROR)
{ {
/* device error */ /* device error */
phost->Control.state = CTRL_ERROR; phost->Control.state = CTRL_ERROR;
status = USBH_FAIL; status = USBH_FAIL;
}
#if (USBH_USE_OS == 1) if ((int32_t)(phost->Timer - phost->Control.timer) >= USBH_CTRL_TRANSACTION_TIMEOUT_MS)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); {
#endif phost->Control.state = CTRL_ERROR;
} }
break; break;
case CTRL_STATUS_IN: case CTRL_STATUS_IN:
/* Send 0 bytes out packet */ /* Send 0 bytes out packet */
phost->Control.timer = phost->Timer;
USBH_CtlReceiveData (phost, USBH_CtlReceiveData (phost,
0, 0,
0, 0,
phost->Control.pipe_in); phost->Control.pipe_in);
phost->Control.timer = phost->Timer;
phost->Control.state = CTRL_STATUS_IN_WAIT; phost->Control.state = CTRL_STATUS_IN_WAIT;
break; break;
case CTRL_STATUS_IN_WAIT: case CTRL_STATUS_IN_WAIT:
URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_in); URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_in);
if ( URB_Status == USBH_URB_DONE) if ( URB_Status == USBH_URB_DONE)
{ /* Control transfers completed, Exit the State Machine */ { /* Control transfers completed, Exit the State Machine */
phost->Control.state = CTRL_COMPLETE; phost->Control.state = CTRL_COMPLETE;
status = USBH_OK; status = USBH_OK;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if (URB_Status == USBH_URB_ERROR) else if (URB_Status == USBH_URB_ERROR)
{ {
phost->Control.state = CTRL_ERROR; phost->Control.state = CTRL_ERROR;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if(URB_Status == USBH_URB_STALL) else if(URB_Status == USBH_URB_STALL)
{ {
/* Control transfers completed, Exit the State Machine */ //Retry transaction
status = USBH_NOT_SUPPORTED; phost->Control.state = CTRL_ERROR;
}
#if (USBH_USE_OS == 1) if ((int32_t)(phost->Timer - phost->Control.timer) >= USBH_CTRL_TRANSACTION_TIMEOUT_MS)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); {
#endif phost->Control.state = CTRL_ERROR;
} }
break; break;
case CTRL_STATUS_OUT: case CTRL_STATUS_OUT:
phost->Control.timer = phost->Timer;
USBH_CtlSendData (phost, USBH_CtlSendData (phost,
0, 0,
0, 0,
phost->Control.pipe_out, phost->Control.pipe_out,
1); 1);
phost->Control.timer = phost->Timer;
phost->Control.state = CTRL_STATUS_OUT_WAIT; phost->Control.state = CTRL_STATUS_OUT_WAIT;
break; break;
case CTRL_STATUS_OUT_WAIT: case CTRL_STATUS_OUT_WAIT:
URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_out); URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_out);
if (URB_Status == USBH_URB_DONE) if (URB_Status == USBH_URB_DONE)
{ {
status = USBH_OK; status = USBH_OK;
phost->Control.state = CTRL_COMPLETE; phost->Control.state = CTRL_COMPLETE;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if (URB_Status == USBH_URB_NOTREADY) else if (URB_Status == USBH_URB_NOTREADY)
{ {
phost->Control.state = CTRL_STATUS_OUT; phost->Control.state = CTRL_STATUS_OUT;
#if (USBH_USE_OS == 1)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0);
#endif
} }
else if (URB_Status == USBH_URB_ERROR) else if (URB_Status == USBH_URB_ERROR)
{ {
phost->Control.state = CTRL_ERROR; phost->Control.state = CTRL_ERROR;
}
#if (USBH_USE_OS == 1) if ((int32_t)(phost->Timer - phost->Control.timer) >= USBH_CTRL_TRANSACTION_TIMEOUT_MS)
osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); {
#endif phost->Control.state = CTRL_ERROR;
} }
break; break;

@ -201,12 +201,6 @@ void Downstream_HostUserCallback(USBH_HandleTypeDef *phost, uint8_t id)
return; return;
} }
//Called from main()
if (id == HOST_USER_UNRECOVERED_ERROR)
{
DOWNSTREAM_STATEMACHINE_FREAKOUT;
return;
}
//Called from main() //Called from main()
if (id == HOST_USER_CLASS_ACTIVE) if (id == HOST_USER_CLASS_ACTIVE)
@ -263,8 +257,10 @@ void Downstream_HostUserCallback(USBH_HandleTypeDef *phost, uint8_t id)
return; return;
} }
//Called from main(): //Called from main():
if (id == HOST_USER_CLASS_FAILED) if ((id == HOST_USER_CLASS_FAILED) ||
(id == HOST_USER_UNRECOVERED_ERROR)) //Probably due to a crappy device that won't enumerate!
{ {
//Unsupported device classes will cause a slow fault flash. //Unsupported device classes will cause a slow fault flash.
//This is distinct from the fast freakout flash caused by internal errors or attacks. //This is distinct from the fast freakout flash caused by internal errors or attacks.
@ -273,5 +269,6 @@ void Downstream_HostUserCallback(USBH_HandleTypeDef *phost, uint8_t id)
DownstreamState = STATE_ERROR; DownstreamState = STATE_ERROR;
return; return;
} }
} }

@ -85,7 +85,7 @@ void HAL_HCD_MspDeInit(HCD_HandleTypeDef* hhcd)
PA11 ------> USB_OTG_FS_DM PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP PA12 ------> USB_OTG_FS_DP
*/ */
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12); //HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);
/* Peripheral interrupt Deinit*/ /* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(OTG_FS_IRQn); HAL_NVIC_DisableIRQ(OTG_FS_IRQn);

Loading…
Cancel
Save