Exemple #1
0
/**
  * @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;
}
Exemple #2
0
/**
  * @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;
}
Exemple #3
0
/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout                       */
uint8_t USB::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit, uint8_t *data_p = NULL, uint16_t nbytes = 0, uint8_t hcnum = 0) {
        unsigned long timeout = millis() + USB_XFER_TIMEOUT;
        //unsigned long timeout2 = timeout;
        uint8_t tmpdata;
        uint8_t rcode = hrSUCCESS;
        uint8_t retry_count = 0;
        uint16_t nak_count = 0;
        USB_OTG_CORE_HANDLE *pdev = coreConfig;
        uint8_t pid = 0;

		pdev->host.hc[hcnum].nak_count = 0;
		pdev->host.hc[hcnum].nak_limit = nak_limit;

        while (timeout > millis()) {
			//regWr(rHXFR, (token | ep)); //launch the transfer
        	if(token == tokSETUP) {
				USBH_CtlSendSetup(pdev, data_p, hcnum);
				//timeout2 = millis() + 10;
        	} else {
        		if(token == tokOUTHS) {
        			pdev->host.hc[hcnum].toggle_out = 0x1;
        			pdev->host.hc[hcnum].ep_is_in = 0;
        			pid = HC_PID_DATA1;
        		} else if (token == tokINHS) {
        			pdev->host.hc[hcnum].toggle_in = 0x1;
        			pdev->host.hc[hcnum].ep_is_in = 1;
        			pid = HC_PID_DATA1;
        		} else if (token == tokIN){
        			pdev->host.hc[hcnum].ep_is_in = 1;
        			pid = (pdev->host.hc[hcnum].toggle_in)? HC_PID_DATA1 : HC_PID_DATA0;
        		} else {
        			pdev->host.hc[hcnum].ep_is_in = 0;
        			pid = (pdev->host.hc[hcnum].toggle_out)? HC_PID_DATA1 : HC_PID_DATA0;
        		}/*
        		if(pid == HC_PID_DATA0) {
        			STM_EVAL_LEDToggle(LED1);
        			delay_ms(1);
        			STM_EVAL_LEDToggle(LED1);
        			delay_ms(1);
        			STM_EVAL_LEDToggle(LED1);
        		}
        		if(pid == HC_PID_DATA1) {
        			STM_EVAL_LEDToggle(LED1);
        			delay_ms(1);
					STM_EVAL_LEDToggle(LED1);
					delay_ms(1);
					STM_EVAL_LEDToggle(LED1);
					delay_ms(1);
					STM_EVAL_LEDToggle(LED1);
        		}*/
    			pdev->host.hc[hcnum].data_pid = pid;
    			pdev->host.hc[hcnum].xfer_buff = data_p;
    			pdev->host.hc[hcnum].xfer_len = nbytes;

    			HCD_SubmitRequest(pdev, hcnum);
        	}

			rcode = USB_ERROR_TRANSFER_TIMEOUT;
			while (timeout > millis()) //wait for transfer completion
			{
				tmpdata = HCD_GetURB_State(pdev, hcnum);	//regRd(rHIRQ);
				if (tmpdata != URB_IDLE) {	//& bmHXFRDNIRQ) {
					//regWr(rHIRQ, bmHXFRDNIRQ); //clear the interrupt
					rcode = 0x00;
					break;
				} 
				if (pdev->host.ConnSts == 0) {
					rcode = hrJERR;
					return rcode;
				}
/*				else {	//todo: because in bt case, there are two in ep (int-in, bulk-in), that we need to check hid/msc case either.
					rcode = HCD_GetHCState(pdev, hcnum);
					if(rcode == hrNAK) {
						nak_count++;
						if (nak_limit && (nak_count == nak_limit))
							return (rcode);
						uint8_t eptype = pdev->host.hc[hcnum].ep_type;
						if (eptype == EP_TYPE_INTR) {
							break;
						} else if ((eptype == EP_TYPE_CTRL) || (eptype == EP_TYPE_BULK)) {					      // re-activate the channel
							USB_OTG_HCCHAR_TypeDef hcchar;
							hcchar.d32 = USB_OTG_READ_REG32(&pdev->regs.HC_REGS[hcnum]->HCCHAR);
							hcchar.b.chen = 1;
							hcchar.b.chdis = 0;
							USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[hcnum]->HCCHAR, hcchar.d32);
							delay_us(200);
						}
					}
				}*/
			}

			//if (rcode != 0x00) //exit if timeout
			//        return ( rcode);

            rcode = HCD_GetHCState(pdev, hcnum);	//(regRd(rHRSL) & 0x0f); //analyze transfer result
			switch (rcode) {
				case hrNAK: 	//todo: if timeout above with nak, we need to consider the next xfer.
					nak_count = pdev->host.hc[hcnum].nak_count;
					if (nak_limit && (nak_count == nak_limit))
						return (rcode);
					break;
				case hrTIMEOUT:
					retry_count++;
					if (retry_count == USB_RETRY_LIMIT)
						return (rcode);
					break;
				default:
					return (rcode);
			}

        }//while( timeout > millis()

        return ( rcode);
}
Exemple #4
0
uint8_t USBPTD_SetupStage(USB_OTG_CORE_HANDLE* pcore, USB_SETUP_REQ* req)
{
	// store for later use from another function
	memcpy(USBPT_LastSetupPacket, pcore->dev.setup_packet, 24);

	// print for monitoring
	USBPT_printf("\b\r\n USBPT:SETUP:");
	for (uint8_t i = 0; i < 8; i++) {
		USBPT_printf(" 0x%02X", USBPT_LastSetupPacket[i]);
	}
	USBPT_printf("\r\n");

	// prepare to be sent to the device
	memcpy(USBPT_Dev->Control.setup.d8, USBPT_LastSetupPacket, 8);

	// set address must be handled explicitly
	if ((req->bmRequest & 0x7F) == (USB_REQ_RECIPIENT_DEVICE | USB_REQ_TYPE_STANDARD) && req->bRequest == USB_REQ_SET_ADDRESS)
	{
		// let the internal code handle it for the device interface
		USBD_StdDevReq(pcore, req);

		// pass it to the downstream device
		USBH_CtlReq_Blocking(&USB_OTG_Core_host, USBPT_Dev, 0, 0, 100);
		USBD_CtlSendStatus(pcore);

		// modifiy our host channel to match
		USBPT_Dev->device_prop.address = (uint8_t)(req->wValue) & 0x7F;
		USBH_Modify_Channel (&USB_OTG_Core_host,
							USBPT_Dev->Control.hc_num_in,
							USBPT_Dev->device_prop.address,
							0,
							0,
							0);
		USBH_Modify_Channel (&USB_OTG_Core_host,
							USBPT_Dev->Control.hc_num_out,
							USBPT_Dev->device_prop.address,
							0,
							0,
							0);

		// modify all other channels to match
		for (uint8_t i = 0; i < USBPTH_MAX_LISTENERS; i++)
		{
			USBPTH_HC_EP_t* pl = &USBPTH_Listeners[i];
			uint8_t hc = pl->hc;
			if (hc != 0 && hc != HC_ERROR) // if listener is actually allocated
			{
				USBH_EpDesc_TypeDef* epDesc = pl->epDesc;
				uint8_t epType = 0;
				if ((epDesc->bmAttributes & USB_EP_TYPE_INTR) == USB_EP_TYPE_INTR) {
					epType = EP_TYPE_INTR;
				}
				else if ((epDesc->bmAttributes & USB_EP_TYPE_INTR) == USB_EP_TYPE_BULK) {
					epType = EP_TYPE_BULK;
				}
				else if ((epDesc->bmAttributes & USB_EP_TYPE_INTR) == USB_EP_TYPE_ISOC) {
					epType = EP_TYPE_ISOC;
				}
				else if ((epDesc->bmAttributes & USB_EP_TYPE_INTR) == USB_EP_TYPE_CTRL) {
					epType = EP_TYPE_CTRL;
				}

				USBH_Modify_Channel(	&USB_OTG_Core_host,
										USBPTH_Listeners[i].hc,
										USBPT_Dev->device_prop.address,
										USBPT_Dev->device_prop.speed,
										epType,
										USBPTH_Listeners[i].epDesc->wMaxPacketSize);
			}
		}
		// note: out direction channels are dynamically allocated only when needed
		// so we don't need to modify those channel addresses

		return USBD_OK;
	}

	// no data means we can just directly relay the data
	if (req->wLength == 0) {
		USBH_CtlReq_Blocking(&USB_OTG_Core_host, USBPT_Dev, 0, 0, 100);
		USBD_CtlSendStatus(pcore);
		return USBD_OK;
	}

	// there is extra data later
	USBPT_CtrlDataLen = req->wLength;
	if (USBPT_CtrlData != 0) free(USBPT_CtrlData);
	USBPT_CtrlData = malloc(USBPT_CtrlDataLen);

	USBH_Status status;

	// wait until previous req is finished
	delay_1ms_cnt = 100;
	while (delay_1ms_cnt > 0 &&
			USBPT_Dev->Control.state != CTRL_COMPLETE &&
			USBPT_Dev->Control.state != CTRL_IDLE &&
			USBPT_Dev->Control.state != CTRL_ERROR &&
			USBPT_Dev->Control.state != CTRL_STALLED);
	{
		status = USBH_HandleControl(&USB_OTG_Core_host, USBPT_Dev);
	}

	// finalize previous ctrl req
	if (USBPT_Dev->RequestState == CMD_WAIT) {
		USBH_CtlReq(&USB_OTG_Core_host, USBPT_Dev, 0 , 0 );
	}

	// prepare new setup
	USBH_SubmitSetupRequest(USBPT_Dev, USBPT_CtrlData, USBPT_CtrlDataLen);
	USBPT_Dev->RequestState = CMD_WAIT;
	USBH_CtlSendSetup (&USB_OTG_Core_host, USBPT_Dev->Control.setup.d8, USBPT_Dev->Control.hc_num_out);
	USBPT_Dev->Control.state = CTRL_SETUP_WAIT;
	USBPT_Dev->Control.timer = HCD_GetCurrentFrame(pcore);
	USBPT_Dev->Control.timeout = 50;

	if ((req->bmRequest & 0x80) == 0)
	{ // H2D
		// we need to obtain the data from EP0_RxReady first
		USBD_CtlPrepareRx (pcore, USBPT_CtrlData, USBPT_CtrlDataLen);
		return USBD_OK;
	}
	else
	{ // D2H

		// wait for request to finish
		delay_1ms_cnt = 100;
		do
		{
			status = USBH_CtlReq(&USB_OTG_Core_host, USBPT_Dev, USBPT_CtrlData , USBPT_CtrlDataLen );
			if (status == USBH_OK || status == USBH_FAIL || status == USBH_STALL || status == USBH_NOT_SUPPORTED) {
				break;
			}
			else
			{
				status = USBH_HandleControl(&USB_OTG_Core_host, USBPT_Dev);
				if (status == USBH_FAIL || status == USBH_STALL || status == USBH_NOT_SUPPORTED) {
					break;
				}
			}
		}
		while (delay_1ms_cnt > 0);

		if (delay_1ms_cnt == 0)
		{
			// timeout
			dbg_printf(DBGMODE_ERR, "USBPT Setup Timed Out \r\n");
			USBD_CtlSendStatus(pcore); // we reply with nothing to simulate a timeout
			return USBH_OK;
		}
		else if (status == USBH_OK)
		{
			// all good, send back the data
			USBD_CtlSendData (pcore, USBPT_CtrlData, USBPT_CtrlDataLen);

			// handle config descriptors specially, we need to know what channels to open based on endpoints
			if ((req->bmRequest & 0x7F) == (USB_REQ_RECIPIENT_DEVICE | USB_REQ_TYPE_STANDARD) &&
					req->bRequest == USB_REQ_GET_DESCRIPTOR &&
					req->wValue == USB_DESC_CONFIGURATION &&
					req->wLength > USB_CONFIGURATION_DESC_SIZE)
			{
				// this is a full length configuration descriptor
				// we need this info to open as many D2H endpoints to channels
				USBH_ParseCfgDesc(&USBPT_Dev->device_prop.Cfg_Desc,
									USBPT_Dev->device_prop.Itf_Desc,
									USBPT_Dev->device_prop.Ep_Desc,
									USBPT_CtrlData,
									USBPT_CtrlDataLen);

				USBPTH_OutEPCnt = 0;
				USBPT_GeneralInDataLen = 0;
				for (uint8_t i = 0; i < USBPT_Dev->device_prop.Cfg_Desc.bNumInterfaces && i < USBH_MAX_NUM_INTERFACES; i++)
				{
					for (uint8_t j = 0; j < USBPT_Dev->device_prop.Itf_Desc[i].bNumEndpoints && j < USBH_MAX_NUM_ENDPOINTS; j++)
					{
						USBH_EpDesc_TypeDef* epDesc = &USBPT_Dev->device_prop.Ep_Desc[i][j];
						for (uint8_t k = 0; k < USBPTH_MAX_LISTENERS; k++)
						{
							if ((epDesc->bEndpointAddress & USB_EP_DIR_MSK) == USB_D2H && USBPTH_Listeners[k].used == 0)
							{
								USBPTH_Listeners[k].epDesc = epDesc;
								uint8_t epType = 0;
								if ((epDesc->bmAttributes & USB_EP_TYPE_INTR) == USB_EP_TYPE_INTR) {
									epType = EP_TYPE_INTR;
								}
								else if ((epDesc->bmAttributes & USB_EP_TYPE_INTR) == USB_EP_TYPE_BULK) {
									epType = EP_TYPE_BULK;
								}
								else if ((epDesc->bmAttributes & USB_EP_TYPE_INTR) == USB_EP_TYPE_ISOC) {
									epType = EP_TYPE_ISOC;
								}
								else if ((epDesc->bmAttributes & USB_EP_TYPE_INTR) == USB_EP_TYPE_CTRL) {
									epType = EP_TYPE_CTRL;
								}

								USBH_Open_Channel(	&USB_OTG_Core_host,
													&(USBPTH_Listeners[k].hc),
													epDesc->bEndpointAddress,
													USBPT_Dev->device_prop.address,
													USBPT_Dev->device_prop.speed,
													epType,
													USBPTH_Listeners[k].epDesc->wMaxPacketSize);

								if (USBPTH_Listeners[k].hc >= 0)
								{
									USBPTH_Listeners[k].used = 1;
									DCD_EP_Open(&USB_OTG_Core_dev, epDesc->bEndpointAddress, epDesc->wMaxPacketSize, epType);

									if (epDesc->wMaxPacketSize > USBPT_GeneralInDataMax) {
										USBPT_GeneralInDataMax = epDesc->wMaxPacketSize;
									}
								}
							}
						}

						if ((epDesc->bEndpointAddress & 0x80) == USB_H2D)
						{
							USBPTH_OutEPCnt++;
						}
					}
				}

				if (USBPT_GeneralInData != 0) free(USBPT_GeneralInData); // release memory if previously allocated
				USBPT_GeneralInData = malloc(USBPT_GeneralInDataMax); // only allocate the memory we need

				if (USBPTH_OutEP != 0) free(USBPTH_OutEP); // release memory if previously allocated
				USBPTH_OutEP = malloc(sizeof(USBH_EpDesc_TypeDef*) * USBPTH_OutEPCnt); // only allocate the memory we need

				uint8_t ec = 0;
				for (uint8_t i = 0; i < USBPT_Dev->device_prop.Cfg_Desc.bNumInterfaces && i < USBH_MAX_NUM_INTERFACES; i++)
				{
					for (uint8_t j = 0; j < USBPT_Dev->device_prop.Itf_Desc[i].bNumEndpoints && j < USBH_MAX_NUM_ENDPOINTS; j++)
					{
						USBH_EpDesc_TypeDef* epDesc = &USBPT_Dev->device_prop.Ep_Desc[i][j];
						if ((epDesc->bEndpointAddress & 0x80) == USB_H2D) {
							// only save the H2D direction endpoints
							USBPTH_OutEP[ec] = epDesc;
							ec++;
						}
					}
				}
			}
			return USBH_OK;
		}
		else
		{
			if (status == USBH_STALL || status == USBH_NOT_SUPPORTED) {
				dbg_printf(DBGMODE_ERR, "USBPT Setup Stalled \r\n");
				USBD_CtlError(pcore , req);
				return USBH_OK;
			}
		}

		return USBD_OK;
	}

	dbg_printf(DBGMODE_ERR, "USBPT Setup Unhandled Error \r\n");
	USBD_CtlError(pcore , req);

	return USBD_OK;
}
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;
}