/** * @brief USBH_HandleControl * Handles the USB control transfer state machine * @param phost: Host Handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_HandleControl (USBH_HandleTypeDef *phost) { uint8_t direction; USBH_StatusTypeDef status = USBH_BUSY; USBH_URBStateTypeDef URB_Status = USBH_URB_IDLE; switch (phost->Control.state) { case CTRL_SETUP: /* send a SETUP packet */ USBH_CtlSendSetup (phost, (uint8_t *)phost->Control.setup.d8 , phost->Control.pipe_out); phost->Control.state = CTRL_SETUP_WAIT; break; case CTRL_SETUP_WAIT: URB_Status = USBH_LL_GetURBState(phost, phost->Control.pipe_out); /* case SETUP packet sent successfully */ if(URB_Status == USBH_URB_DONE) { direction = (phost->Control.setup.b.bmRequestType & USB_REQ_DIR_MASK); /* check if there is a data stage */ if (phost->Control.setup.b.wLength.w != 0 ) { if (direction == USB_D2H) { /* Data Direction is IN */ phost->Control.state = CTRL_DATA_IN; } else { /* Data Direction is OUT */ phost->Control.state = CTRL_DATA_OUT; } } /* No DATA stage */ else { /* If there is No Data Transfer Stage */ if (direction == USB_D2H) { /* Data Direction is IN */ phost->Control.state = CTRL_STATUS_OUT; } else { /* Data Direction is OUT */ 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) { phost->Control.state = CTRL_ERROR; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } break; case CTRL_DATA_IN: /* Issue an IN token */ phost->Control.timer = phost->Timer; USBH_CtlReceiveData(phost, phost->Control.buff, phost->Control.length, phost->Control.pipe_in); phost->Control.state = CTRL_DATA_IN_WAIT; break; case CTRL_DATA_IN_WAIT: URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_in); /* check is DATA packet transfered successfully */ if (URB_Status == USBH_URB_DONE) { phost->Control.state = CTRL_STATUS_OUT; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } /* manage error cases*/ if (URB_Status == USBH_URB_STALL) { /* In stall case, return to previous machine state*/ status = USBH_NOT_SUPPORTED; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } else if (URB_Status == USBH_URB_ERROR) { /* Device error */ phost->Control.state = CTRL_ERROR; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } break; case CTRL_DATA_OUT: USBH_CtlSendData (phost, phost->Control.buff, phost->Control.length , phost->Control.pipe_out, 1); phost->Control.timer = phost->Timer; phost->Control.state = CTRL_DATA_OUT_WAIT; break; case CTRL_DATA_OUT_WAIT: URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_out); if (URB_Status == USBH_URB_DONE) { /* If the Setup Pkt is sent successful, then change the state */ phost->Control.state = CTRL_STATUS_IN; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } /* handle error cases */ else if (URB_Status == USBH_URB_STALL) { /* In stall case, return to previous machine state*/ phost->Control.state = CTRL_STALLED; 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) { /* Nack received from device */ 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) { /* device error */ phost->Control.state = CTRL_ERROR; status = USBH_FAIL; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } break; case CTRL_STATUS_IN: /* Send 0 bytes out packet */ USBH_CtlReceiveData (phost, 0, 0, phost->Control.pipe_in); phost->Control.timer = phost->Timer; phost->Control.state = CTRL_STATUS_IN_WAIT; break; case CTRL_STATUS_IN_WAIT: URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_in); if ( URB_Status == USBH_URB_DONE) { /* Control transfers completed, Exit the State Machine */ phost->Control.state = CTRL_COMPLETE; status = USBH_OK; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } else if (URB_Status == USBH_URB_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) { /* Control transfers completed, Exit the State Machine */ status = USBH_NOT_SUPPORTED; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } break; case CTRL_STATUS_OUT: USBH_CtlSendData (phost, 0, 0, phost->Control.pipe_out, 1); phost->Control.timer = phost->Timer; phost->Control.state = CTRL_STATUS_OUT_WAIT; break; case CTRL_STATUS_OUT_WAIT: URB_Status = USBH_LL_GetURBState(phost , phost->Control.pipe_out); if (URB_Status == USBH_URB_DONE) { status = USBH_OK; 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) { 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) { phost->Control.state = CTRL_ERROR; #if (USBH_USE_OS == 1) osMessagePut ( phost->os_event, USBH_CONTROL_EVENT, 0); #endif } break; case CTRL_ERROR: /* After a halt condition is encountered or an error is detected by the host, a control endpoint is allowed to recover by accepting the next Setup PID; i.e., recovery actions via some other pipe are not required for control endpoints. For the Default Control Pipe, a device reset will ultimately be required to clear the halt or error condition if the next Setup PID is not accepted. */ if (++ phost->Control.errorcount <= USBH_MAX_ERROR_COUNT) { /* try to recover control */ USBH_LL_Stop(phost); /* Do the transmission again, starting from SETUP Packet */ phost->Control.state = CTRL_SETUP; phost->RequestState = CMD_SEND; } else { phost->Control.errorcount = 0; USBH_ErrLog("Control error"); status = USBH_FAIL; } break; default: break; } return status; }
/** * @brief USBH_HandleControl * Handles the USB control transfer state machine * @param pdev: Selected device * @retval Status */ USBH_Status USBH_HandleControl (USB_OTG_CORE_HANDLE *pdev, USBH_HOST *phost) { uint8_t direction; static uint16_t timeout = 0; USBH_Status status = USBH_OK; URB_STATE URB_Status = URB_IDLE; phost->Control.status = CTRL_START; switch (phost->Control.state) { case CTRL_SETUP: /* send a SETUP packet */ USBH_CtlSendSetup (pdev, phost->Control.setup.d8 , phost->Control.hc_num_out); phost->Control.state = CTRL_SETUP_WAIT; break; case CTRL_SETUP_WAIT: URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out); /* case SETUP packet sent successfully */ if(URB_Status == URB_DONE) { direction = (phost->Control.setup.b.bmRequestType & USB_REQ_DIR_MASK); /* check if there is a data stage */ if (phost->Control.setup.b.wLength.w != 0 ) { timeout = DATA_STAGE_TIMEOUT; if (direction == USB_D2H) { /* Data Direction is IN */ phost->Control.state = CTRL_DATA_IN; } else { /* Data Direction is OUT */ phost->Control.state = CTRL_DATA_OUT; } } /* No DATA stage */ else { timeout = NODATA_STAGE_TIMEOUT; /* If there is No Data Transfer Stage */ if (direction == USB_D2H) { /* Data Direction is IN */ phost->Control.state = CTRL_STATUS_OUT; } else { /* Data Direction is OUT */ phost->Control.state = CTRL_STATUS_IN; } } /* Set the delay timer to enable timeout for data stage completion */ phost->Control.timer = HCD_GetCurrentFrame(pdev); } else if(URB_Status == URB_ERROR) { phost->Control.state = CTRL_ERROR; phost->Control.status = CTRL_XACTERR; } break; case CTRL_DATA_IN: /* Issue an IN token */ USBH_CtlReceiveData(pdev, phost->Control.buff, phost->Control.length, phost->Control.hc_num_in); phost->Control.state = CTRL_DATA_IN_WAIT; break; case CTRL_DATA_IN_WAIT: URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_in); /* check is DATA packet transfered successfully */ if (URB_Status == URB_DONE) { phost->Control.state = CTRL_STATUS_OUT; } /* manage error cases*/ if (URB_Status == URB_STALL) { /* In stall case, return to previous machine state*/ phost->gState = phost->gStateBkp; } else if (URB_Status == URB_ERROR) { /* Device error */ phost->Control.state = CTRL_ERROR; } else if ((HCD_GetCurrentFrame(pdev)- phost->Control.timer) > timeout) { /* timeout for IN transfer */ phost->Control.state = CTRL_ERROR; } break; case CTRL_DATA_OUT: /* Start DATA out transfer (only one DATA packet)*/ pdev->host.hc[phost->Control.hc_num_out].toggle_out = 1; USBH_CtlSendData (pdev, phost->Control.buff, phost->Control.length , phost->Control.hc_num_out); phost->Control.state = CTRL_DATA_OUT_WAIT; break; case CTRL_DATA_OUT_WAIT: URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out); if (URB_Status == URB_DONE) { /* If the Setup Pkt is sent successful, then change the state */ phost->Control.state = CTRL_STATUS_IN; } /* handle error cases */ else if (URB_Status == URB_STALL) { /* In stall case, return to previous machine state*/ phost->gState = phost->gStateBkp; phost->Control.state = CTRL_STALLED; } else if (URB_Status == URB_NOTREADY) { /* Nack received from device */ phost->Control.state = CTRL_DATA_OUT; } else if (URB_Status == URB_ERROR) { /* device error */ phost->Control.state = CTRL_ERROR; } break; case CTRL_STATUS_IN: /* Send 0 bytes out packet */ USBH_CtlReceiveData (pdev, 0, 0, phost->Control.hc_num_in); phost->Control.state = CTRL_STATUS_IN_WAIT; break; case CTRL_STATUS_IN_WAIT: URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_in); if ( URB_Status == URB_DONE) { /* Control transfers completed, Exit the State Machine */ phost->gState = phost->gStateBkp; phost->Control.state = CTRL_COMPLETE; } else if (URB_Status == URB_ERROR) { phost->Control.state = CTRL_ERROR; } else if((HCD_GetCurrentFrame(pdev)\ - phost->Control.timer) > timeout) { phost->Control.state = CTRL_ERROR; } else if(URB_Status == URB_STALL) { /* Control transfers completed, Exit the State Machine */ phost->gState = phost->gStateBkp; phost->Control.status = CTRL_STALL; status = USBH_NOT_SUPPORTED; } break; case CTRL_STATUS_OUT: pdev->host.hc[phost->Control.hc_num_out].toggle_out ^= 1; USBH_CtlSendData (pdev, 0, 0, phost->Control.hc_num_out); phost->Control.state = CTRL_STATUS_OUT_WAIT; break; case CTRL_STATUS_OUT_WAIT: URB_Status = HCD_GetURB_State(pdev , phost->Control.hc_num_out); if (URB_Status == URB_DONE) { phost->gState = phost->gStateBkp; phost->Control.state = CTRL_COMPLETE; } else if (URB_Status == URB_NOTREADY) { phost->Control.state = CTRL_STATUS_OUT; } else if (URB_Status == URB_ERROR) { phost->Control.state = CTRL_ERROR; } break; case CTRL_ERROR: /* After a halt condition is encountered or an error is detected by the host, a control endpoint is allowed to recover by accepting the next Setup PID; i.e., recovery actions via some other pipe are not required for control endpoints. For the Default Control Pipe, a device reset will ultimately be required to clear the halt or error condition if the next Setup PID is not accepted. */ if (++ phost->Control.errorcount <= USBH_MAX_ERROR_COUNT) { /* Do the transmission again, starting from SETUP Packet */ phost->Control.state = CTRL_SETUP; } else { phost->Control.status = CTRL_FAIL; phost->gState = phost->gStateBkp; status = USBH_FAIL; } break; default: break; } return status; }
static int _usb_ctrl_request_handle(USB_OTG_CORE_HANDLE *pdev , USBH_HOST *phost, wifi_usb_adapter_t *adapter) { wifi_usb_ctrl_request_t *ctl_req = &adapter->usb_ctrl_req; unsigned char direction; static unsigned short timeout = 0; URB_STATE URB_Status = URB_IDLE; int status = 1; phost->Control.status = CTRL_START; switch (ctl_req->state) { case CTRL_IDLE: status = 0; /* indicate the state machine could be blocked */ break; case CTRL_SETUP: /* Send a SETUP packet. */ pdev->host.hc[ctl_req->hc_num_out].toggle_out = 0; USBH_CtlSendSetup(pdev, ctl_req->ctrlreq.d8, ctl_req->hc_num_out); ctl_req->state = CTRL_SETUP_WAIT; break; case CTRL_SETUP_WAIT: URB_Status = HCD_GetURB_State(pdev , ctl_req->hc_num_out); ctl_req->status = URB_Status; if (URB_Status == URB_DONE) { direction = (ctl_req->ctrlreq.b.bmRequestType & USB_REQ_DIR_MASK); /* check if there is a data stage. */ if (ctl_req->ctrlreq.b.wLength.w != 0) { timeout = CTRL_DATA_STAGE_TIMEOUT; if (direction == USB_D2H) { /* Data Direction is IN. */ ctl_req->state = CTRL_DATA_IN; } else { /* Data Direction is OUT. */ ctl_req->state = CTRL_DATA_OUT; } } /* No DATA stage. */ else { timeout = CTRL_NODATA_STAGE_TIMEOUT; /* If there is No Data Transfer Stage. */ if (direction == USB_D2H) { /* Data Direction is IN. */ ctl_req->state = CTRL_STATUS_OUT; } else { /* Data Direction is OUT. */ ctl_req->state = CTRL_STATUS_IN; } } /* Set the delay timer to enable timeout for data stage completion. */ ctl_req->timer = HCD_GetCurrentFrame(pdev); } else if (URB_Status == URB_ERROR) { ctl_req->state = CTRL_ERROR; /* To be add. */ } break; case CTRL_DATA_IN: /* Issue an IN token. */ USBH_CtlReceiveData(pdev, ctl_req->buffer, ctl_req->length, ctl_req->hc_num_in); ctl_req->state = CTRL_DATA_IN_WAIT; break; case CTRL_DATA_IN_WAIT: USB_OTG_BSP_uDelay(200); URB_Status = HCD_GetURB_State(pdev , ctl_req->hc_num_in); ctl_req->status = URB_Status; /* check is DATA packet transfered successfully. */ if (URB_Status == URB_DONE) { ctl_req->state = CTRL_STATUS_OUT; } /* manage error cases. */ else if (URB_Status == URB_STALL) { ctl_req->state = CTRL_ERROR; } else if (URB_Status == URB_ERROR) { /* Device error. */ ctl_req->state = CTRL_ERROR; } break; case CTRL_DATA_OUT: /* Start DATA out transfer (only one DATA packet). */ USB_OTG_BSP_uDelay(200); pdev->host.hc[ctl_req->hc_num_out].toggle_out ^= 1; USBH_CtlSendData(pdev, ctl_req->buffer, ctl_req->length , ctl_req->hc_num_out); ctl_req->state = CTRL_DATA_OUT_WAIT; break; case CTRL_DATA_OUT_WAIT: URB_Status = HCD_GetURB_State(pdev, ctl_req->hc_num_out); ctl_req->status = URB_Status; if (URB_Status == URB_DONE) { /* If the Setup Pkt is sent successful, then change the state. */ ctl_req->state = CTRL_STATUS_IN; } /* handle error cases. */ else if (URB_Status == URB_STALL) { ctl_req->state = CTRL_ERROR; } else if (URB_Status == URB_NOTREADY) { /* Nack received from device. */ ctl_req->state = CTRL_DATA_OUT; } else if (URB_Status == URB_ERROR) { /* device error */ ctl_req->state = CTRL_ERROR; } break; case CTRL_STATUS_IN: /* Send 0 bytes out packet. */ USB_OTG_BSP_uDelay(200); USBH_CtlReceiveData(pdev, 0, 0, ctl_req->hc_num_in); ctl_req->state = CTRL_STATUS_IN_WAIT; break; case CTRL_STATUS_IN_WAIT: URB_Status = HCD_GetURB_State(pdev, ctl_req->hc_num_in); ctl_req->status = URB_Status; if (URB_Status == URB_DONE) { /* Control transfers completed, Exit the State Machine */ ctl_req->state = CTRL_IDLE; _usb_control_complete(adapter); } else if (URB_Status == URB_ERROR) { ctl_req->state = CTRL_ERROR; } else if (URB_Status == URB_STALL) { ctl_req->state = URB_STALL; /* NOTICE: here maybe a error to be fixed!!! */ } break; case CTRL_STATUS_OUT: USB_OTG_BSP_uDelay(200); pdev->host.hc[ctl_req->hc_num_out].toggle_out ^= 1; USBH_CtlSendData(pdev, 0, 0, ctl_req->hc_num_out); ctl_req->state = CTRL_STATUS_OUT_WAIT; break; case CTRL_STATUS_OUT_WAIT: URB_Status = HCD_GetURB_State(pdev, ctl_req->hc_num_out); ctl_req->status = URB_Status; if (URB_Status == URB_DONE) { ctl_req->state = CTRL_IDLE; _usb_control_complete(adapter); } else if (URB_Status == URB_NOTREADY) { ctl_req->state = CTRL_STATUS_OUT; } else if (URB_Status == URB_ERROR) { ctl_req->state = CTRL_ERROR; } break; case CTRL_ERROR: DBGPRINT(WHED_DEBUG_ERROR, "PANIC(%s - %d): %s - control urb failed(%d), bRequestType = 0x%x, bRequest = 0x%x, wValue = 0x%x, wIndex = 0x%x, wLength = 0x%x.\n", __FILE__, __LINE__, __FUNCTION__, ctl_req->status, ctl_req->ctrlreq.b.bmRequestType, ctl_req->ctrlreq.b.bRequest, ctl_req->ctrlreq.b.wValue.w, ctl_req->ctrlreq.b.wIndex.w, ctl_req->ctrlreq.b.wLength.w); if (ctl_req->retry) { ctl_req->retry--; /* Do the transmission again, starting from SETUP Packet. */ ctl_req->state = CTRL_SETUP; } else { ctl_req->state = CTRL_IDLE; _usb_control_complete(adapter); } break; default: break; } timeout = timeout; /* avoid compiler's warning */ return status; }