AT32F435_437_Firmware_Library/middlewares/usb_drivers/src/usbh_core.c

1242 lines
33 KiB
C

/**
**************************************************************************
* @file usbh_core.c
* @version v2.0.9
* @date 2022-06-28
* @brief usb host driver
**************************************************************************
* Copyright notice & Disclaimer
*
* The software Board Support Package (BSP) that is made available to
* download from Artery official website is the copyrighted work of Artery.
* Artery authorizes customers to use, copy, and distribute the BSP
* software and its related documentation for the purpose of design and
* development in conjunction with Artery microcontrollers. Use of the
* software is governed by this copyright notice and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
* GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
* TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
* STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
* INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
*
**************************************************************************
*/
#include "usbh_core.h"
#include "usb_core.h"
#include "usbh_ctrl.h"
#include "usb_conf.h"
/** @addtogroup AT32F435_437_middlewares_usbh_drivers
* @{
*/
/** @defgroup USBH_drivers_core
* @brief usb host drivers core
* @{
*/
/** @defgroup USBH_core_private_functions
* @{
*/
static void usbh_attached(usbh_core_type *uhost);
static void usbh_enumeration(usbh_core_type *uhost);
static void usbh_class_request(usbh_core_type *uhost);
static void usbh_class(usbh_core_type *uhost);
static void usbh_suspend(usbh_core_type *uhost);
static void usbh_wakeup(usbh_core_type *uhost);
static void usbh_disconnect(usbh_core_type *uhost);
/**
* @brief usb host free channel
* @param uhost: to the structure of usbh_core_type
* @param index: channle number
* @retval none
*/
void usbh_free_channel(usbh_core_type *uhost, uint8_t index)
{
if(index < USB_HOST_CHANNEL_NUM)
{
/* free host channel */
uhost->channel[index] = 0x0;
}
}
/**
* @brief get usb host free channel
* @param uhost: to the structure of usbh_core_type
* @retval channel index
*/
uint16_t usbh_get_free_channel(usbh_core_type *uhost)
{
uint16_t i_index = 0;
for(i_index = 0; i_index < USB_HOST_CHANNEL_NUM; i_index ++)
{
/* find unuse channel */
if((uhost->channel[i_index] & HCH_USED) == 0)
{
/* return channel index */
return i_index;
}
}
return HCH_ERROR;
}
/**
* @brief usb host set toggle
* @param uhost: to the structure of usbh_core_type
* @param hc_num: channel number
* @param toggle: toggle value
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_set_toggle(usbh_core_type *uhost, uint8_t hc_num, uint8_t toggle)
{
if(uhost->hch[hc_num].dir)
{
/* direction in */
uhost->hch[hc_num].toggle_in = toggle;
}
else
{
/* direction out */
uhost->hch[hc_num].toggle_out = toggle;
}
return USB_OK;
}
/**
* @brief usb host in out request
* @param uhost: to the structure of usbh_core_type
* @param hc_num: channel number
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_in_out_request(usbh_core_type *uhost, uint8_t hc_num)
{
usb_sts_type status = USB_OK;
uint32_t n_packet = 0;
uint32_t num_words = 0;
uint32_t tmp;
otg_global_type *usbx = uhost->usb_reg;
otg_hchannel_type *ch = USB_CHL(uhost->usb_reg, hc_num);
/* set usb request block to idle */
uhost->urb_state[hc_num] = URB_IDLE;
uhost->hch[hc_num].state = HCH_IDLE;
/* set usb channel transmit count to zero */
uhost->hch[hc_num].trans_count = 0;
/* check transmit data len */
if(uhost->hch[hc_num].trans_len > 0)
{
/* count how many packet need to send */
n_packet = (uhost->hch[hc_num].trans_len + \
uhost->hch[hc_num].maxpacket - 1) / \
uhost->hch[hc_num].maxpacket;
/* packet count max 256 */
if(n_packet > 256)
{
n_packet = 256;
uhost->hch[hc_num].trans_len = n_packet * uhost->hch[hc_num].maxpacket;
}
}
else
{
/* zero data len */
n_packet = 1;
}
/* direction is in */
if(uhost->hch[hc_num].dir)
{
uhost->hch[hc_num].trans_len = n_packet * uhost->hch[hc_num].maxpacket;
}
/* set transfer information to channel register */
ch->hctsiz = (uhost->hch[hc_num].trans_len & USB_OTG_HCTSIZ_XFERSIZE) |
((n_packet << 19) & USB_OTG_HCTSIZ_PKTCNT) |
((uhost->hch[hc_num].data_pid << 29) & USB_OTG_HCTSIZ_PID);
/* set odd frame */
ch->hcchar_bit.oddfrm = !(OTG_HOST(uhost->usb_reg)->hfnum & 0x1);
/* clear channel disable bit and enable channel */
tmp = ch->hcchar;
tmp &= ~(USB_OTG_HCCHAR_CHDIS);
tmp |= USB_OTG_HCCHAR_CHENA;
ch->hcchar = tmp;
/* channel direction is out and transfer len > 0 */
if((uhost->hch[hc_num].dir == 0) &&
(uhost->hch[hc_num].trans_len > 0 ))
{
switch(uhost->hch[hc_num].ept_type)
{
case EPT_CONTROL_TYPE:
case EPT_BULK_TYPE:
num_words = (uhost->hch[hc_num].trans_len + 3) / 4;
/* non-periodic transfer */
if(num_words > usbx->gnptxsts_bit.nptxfspcavail)
{
usbx->gintmsk_bit.nptxfempmsk = 1;
}
break;
case EPT_ISO_TYPE:
case EPT_INT_TYPE:
num_words = (uhost->hch[hc_num].trans_len + 3) / 4;
/* periodic transfer */
if(num_words > OTG_HOST(usbx)->hptxsts_bit.ptxfspcavil)
{
usbx->gintmsk_bit.ptxfempmsk = 1;
}
break;
default:
break;
}
/* write data to fifo */
usb_write_packet(usbx, uhost->hch[hc_num].trans_buf,
hc_num, uhost->hch[hc_num].trans_len);
}
return status;
}
/**
* @brief usb host interrupt receive request
* @param uhost: to the structure of usbh_core_type
* @param hc_num: channel number
* @param buffer: receive buffer
* @param length: receive length
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_interrupt_recv(usbh_core_type *uhost, uint8_t hc_num,
uint8_t *buffer, uint16_t length)
{
/* set direction is in */
uhost->hch[hc_num].dir = 1;
/* set transfer buffer */
uhost->hch[hc_num].trans_buf = buffer;
/* set transfer len*/
uhost->hch[hc_num].trans_len = length;
if(uhost->hch[hc_num].toggle_in == 0)
{
/* pid: data0 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA0;
}
else
{
/* pid: data1 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA1;
}
return usbh_in_out_request(uhost, hc_num);
}
/**
* @brief usb host interrupt send request
* @param uhost: to the structure of usbh_core_type
* @param hc_num: channel number
* @param buffer: send buffer
* @param length: send length
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_interrupt_send(usbh_core_type *uhost, uint8_t hc_num,
uint8_t *buffer, uint16_t length)
{
/* set direction is out */
uhost->hch[hc_num].dir = 0;
/* set transfer buffer */
uhost->hch[hc_num].trans_buf = buffer;
/* set transfer len*/
uhost->hch[hc_num].trans_len = length;
if(uhost->hch[hc_num].toggle_out == 0)
{
/* pid: data0 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA0;
}
else
{
/* pid: data1 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA1;
}
return usbh_in_out_request(uhost, hc_num);
}
/**
* @brief usb host bulk receive request
* @param uhost: to the structure of usbh_core_type
* @param hc_num: channel number
* @param buffer: receive buffer
* @param length: receive length
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_bulk_recv(usbh_core_type *uhost, uint8_t hc_num,
uint8_t *buffer, uint16_t length)
{
/* set direction is in */
uhost->hch[hc_num].dir = 1;
/* set transfer buffer */
uhost->hch[hc_num].trans_buf = buffer;
/* set transfer len*/
uhost->hch[hc_num].trans_len = length;
if(uhost->hch[hc_num].toggle_in == 0)
{
/* pid: data0 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA0;
}
else
{
/* pid: data1 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA1;
}
return usbh_in_out_request(uhost, hc_num);
}
/**
* @brief usb host bulk send request
* @param uhost: to the structure of usbh_core_type
* @param hc_num: channel number
* @param buffer: receive buffer
* @param length: receive length
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_bulk_send(usbh_core_type *uhost, uint8_t hc_num,
uint8_t *buffer, uint16_t length)
{
/* set direction is out */
uhost->hch[hc_num].dir = 0;
/* set transfer buffer */
uhost->hch[hc_num].trans_buf = buffer;
/* set transfer len*/
uhost->hch[hc_num].trans_len = length;
if(uhost->hch[hc_num].toggle_out == 0)
{
/* pid: data0 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA0;
}
else
{
/* pid: data1 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA1;
}
return usbh_in_out_request(uhost, hc_num);
}
/**
* @brief usb host iso send request
* @param uhost: to the structure of usbh_core_type
* @param hc_num: channel number
* @param buffer: send buffer
* @param length: send length
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_isoc_send(usbh_core_type *uhost, uint8_t hc_num,
uint8_t *buffer, uint16_t length)
{
/* set direction is out */
uhost->hch[hc_num].dir = 0;
/* set transfer buffer */
uhost->hch[hc_num].trans_buf = buffer;
/* set transfer len*/
uhost->hch[hc_num].trans_len = length;
/* pid: data0 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA0;
return usbh_in_out_request(uhost, hc_num);
}
/**
* @brief usb host iso receive request
* @param uhost: to the structure of usbh_core_type
* @param hc_num: channel number
* @param buffer: receive buffer
* @param length: receive length
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_isoc_recv(usbh_core_type *uhost, uint8_t hc_num,
uint8_t *buffer, uint16_t length)
{
/* set direction is in */
uhost->hch[hc_num].dir = 1;
/* set transfer buffer */
uhost->hch[hc_num].trans_buf = buffer;
/* set transfer len*/
uhost->hch[hc_num].trans_len = length;
/* pid: data0 */
uhost->hch[hc_num].data_pid = HCH_PID_DATA0;
return usbh_in_out_request(uhost, hc_num);
}
/**
* @brief usb host cfg default init
* @param uhost: to the structure of usbh_core_type
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_cfg_default_init(usbh_core_type *uhost)
{
/* set global state to idle */
uhost->global_state = USBH_IDLE;
/* enumeration state to get description */
uhost->enum_state = ENUM_GET_MIN_DESC;
/* request state send */
uhost->req_state = CMD_SEND;
/* control transfer state is idle*/
uhost->ctrl.state = CONTROL_IDLE;
/* defaut endpoint 0 max size is 8byte */
uhost->ctrl.ept0_size = 8;
/* default device address is 0 */
uhost->dev.address = 0;
/* default speed is full speed */
uhost->dev.speed = USB_FULL_SPEED_CORE_ID;
uhost->timer = 0;
uhost->ctrl.err_cnt = 0;
/* free all channel */
usbh_free_channel(uhost, uhost->ctrl.hch_in);
usbh_free_channel(uhost, uhost->ctrl.hch_out);
return USB_OK;
}
/**
* @brief usb host enter suspend
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
void usbh_enter_suspend(usbh_core_type *uhost)
{
otg_host_type *host = OTG_HOST(uhost->usb_reg);
uint32_t hprt_val = host->hprt;
hprt_val &= ~(USB_OTG_HPRT_PRTENA | USB_OTG_HPRT_PRTENCHNG |
USB_OTG_HPRT_PRTOVRCACT | USB_OTG_HPRT_PRTCONDET);
/* set port suspend */
host->hprt = hprt_val | USB_OTG_HPRT_PRTSUSP;
/* stop phy clock */
usb_stop_phy_clk(uhost->usb_reg);
}
/**
* @brief usb host resume
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
void usbh_resume(usbh_core_type *uhost)
{
otg_host_type *host = OTG_HOST(uhost->usb_reg);
uint32_t temp = host->hprt;
/* open phy clock */
usb_open_phy_clk(uhost->usb_reg);
/* clear port suspend and set port resume*/
temp &= ~(USB_OTG_HPRT_PRTENA | USB_OTG_HPRT_PRTENCHNG |
USB_OTG_HPRT_PRTOVRCACT | USB_OTG_HPRT_PRTCONDET
| USB_OTG_HPRT_PRTSUSP);
host->hprt = temp | USB_OTG_HPRT_PRTRES;
/* delay 20 ms */
usb_delay_ms(20);
/*clear port resume */
temp = host->hprt;
temp &= ~(USB_OTG_HPRT_PRTENA | USB_OTG_HPRT_PRTENCHNG |
USB_OTG_HPRT_PRTOVRCACT | USB_OTG_HPRT_PRTCONDET
| USB_OTG_HPRT_PRTRES);
host->hprt = temp;
usb_delay_ms(5);
}
/**
* @brief usb host core initialize
* @param uhost: to the structure of usbh_core_type
* @param usb_reg: usb otgfs peripheral global register
* this parameter can be one of the following values:
* OTG1_GLOBAL , OTG2_GLOBAL
* @param class_handler: usb host class handler type pointer
* @param user_handler: usb host user handler type pointer
* @param core_id: usb core select id
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_core_init(usbh_core_type *uhost,
usb_reg_type *usb_reg,
usbh_class_handler_type *class_handler,
usbh_user_handler_type *user_handler,
uint8_t core_id)
{
usb_sts_type status = USB_OK;
uint32_t i_index;
otg_global_type *usbx = usb_reg;
otg_host_type *host = OTG_HOST(usbx);
uhost->usb_reg = usb_reg;
/* host class handler */
uhost->class_handler = class_handler;
uhost->user_handler = user_handler;
/* host user handler */
uhost->user_handler->user_init();
uhost->timer = 0;
/* usb host cfg default init */
usbh_cfg_default_init(uhost);
/* clear host config to default value */
for(i_index = 0; i_index < USB_HOST_CHANNEL_NUM; i_index ++)
{
uhost->err_cnt[i_index] = 0;
uhost->xfer_cnt[i_index] = 0;
uhost->hch_state[i_index] = HCH_IDLE;
uhost->hch[0].maxpacket = 8;
}
/* no device connect */
uhost->conn_sts = 0;
/* disable usb interrupt */
usb_interrupt_disable(usbx);
/* usb global init */
usb_global_init(usbx);
/* set usb host mode */
usb_global_set_mode(usbx, OTG_HOST_MODE);
/* open usb phy clock*/
usb_open_phy_clk(usbx);
/* clock select */
usbh_fsls_clksel(usbx, USB_HCFG_CLK_48M);
/* set support ls and fs device */
host->hcfg_bit.fslssupp = 0;
if(usbx == OTG1_GLOBAL)
{
/* set receive fifo size */
usbx->grxfsiz = USBH_RX_FIFO_SIZE;
/* set non-periodic transmit fifo start address and depth */
usbx->gnptxfsiz_ept0tx_bit.nptxfstaddr = USBH_RX_FIFO_SIZE;
usbx->gnptxfsiz_ept0tx_bit.nptxfdep = USBH_NP_TX_FIFO_SIZE;
/* set periodic transmit fifo start address and depth */
usbx->hptxfsiz_bit.ptxfstaddr = USBH_RX_FIFO_SIZE + USBH_NP_TX_FIFO_SIZE;
usbx->hptxfsiz_bit.ptxfsize = USBH_P_TX_FIFO_SIZE;
}
#ifdef OTG2_GLOBAL
if(usbx == OTG2_GLOBAL)
{
/* set receive fifo size */
usbx->grxfsiz = USBH2_RX_FIFO_SIZE;
/* set non-periodic transmit fifo start address and depth */
usbx->gnptxfsiz_ept0tx_bit.nptxfstaddr = USBH2_RX_FIFO_SIZE;
usbx->gnptxfsiz_ept0tx_bit.nptxfdep = USBH2_NP_TX_FIFO_SIZE;
/* set periodic transmit fifo start address and depth */
usbx->hptxfsiz_bit.ptxfstaddr = USBH2_RX_FIFO_SIZE + USBH2_NP_TX_FIFO_SIZE;
usbx->hptxfsiz_bit.ptxfsize = USBH2_P_TX_FIFO_SIZE;
}
#endif
/* flush tx fifo */
usb_flush_tx_fifo(usbx, 16);
/* flush rx fifo */
usb_flush_rx_fifo(usbx);
/* clear host channel interrut mask and status */
for(i_index = 0; i_index < USB_HOST_CHANNEL_NUM; i_index ++)
{
USB_CHL(usbx, i_index)->hcintmsk = 0;
USB_CHL(usbx, i_index)->hcint = 0xFFFFFFFF;
}
/* power on to this port */
usb_port_power_on(usbx, TRUE);
/* clear global interrupt mask and status */
usbx->gintmsk = 0;
usbx->gintsts = 0xBFFFFFFF;
/* set global interrut mask */
usbx->gintmsk = USB_OTG_SOF_INT | USB_OTG_RXFLVL_INT |
USB_OTG_USBSUSP_INT | USB_OTG_PRT_INT |
USB_OTG_HCH_INT | USB_OTG_INCOMISOIN_INT |
USB_OTG_INCOMPIP_INCOMPISOOUT_INT | USB_OTG_WKUP_INT |
USB_OTG_DISCON_INT;
/* enable usb global interrupt */
usb_interrupt_enable(usbx);
/* active vbus */
usbh_active_vbus(uhost, TRUE);
return status;
}
/**
* @brief usb host open channel
* @param uhost: to the structure of usbh_core_type
* @param chn: host channel number
* @param ept_num: devvice endpoint number
* @param dev_address: device address
* @param type: channel transfer type
* this parameter can be one of the following values:
* - EPT_CONTROL_TYPE
* - EPT_BULK_TYPE
* - EPT_INT_TYPE
* - EPT_ISO_TYPE
* @param maxpacket: support max packe size for this channel
* @param speed: device speed
* this parameter can be one of the following values:
* - USB_PRTSPD_FULL_SPEED
* - USB_PRTSPD_LOW_SPEED
* @param ept_addr: endpoint address
* @retval usb_sts_type
*/
void usbh_hc_open(usbh_core_type *uhost,
uint8_t chn,
uint8_t ept_num,
uint8_t dev_address,
uint8_t type,
uint16_t maxpacket,
uint8_t speed)
{
/* device address */
uhost->hch[chn].address = dev_address;
/* device speed */
uhost->hch[chn].speed = speed;
/* endpoint transfer type */
uhost->hch[chn].ept_type = type;
/* endpoint support maxpacket */
uhost->hch[chn].maxpacket = maxpacket;
/* endpoint direction in or out */
uhost->hch[chn].dir = (ept_num & 0x80)?1:0;;
/* host channel number */
uhost->hch[chn].ch_num = chn;
/* device endpoint number */
uhost->hch[chn].ept_num = ept_num;
/* enable channel */
usb_hc_enable(uhost->usb_reg, chn,
ept_num, dev_address,
type, maxpacket, speed
);
}
/**
* @brief disable host channel
* @param usbx: to select the otgfs peripheral.
* this parameter can be one of the following values:
* - OTG1_GLOBAL
* - OTG2_GLOBAL
* @param chn: channel number
* @retval none
*/
void usbh_ch_disable(usbh_core_type *uhost, uint8_t chn)
{
usb_hch_halt(uhost->usb_reg, chn);
}
/**
* @brief usb host alloc channel
* @param uhost: to the structure of usbh_core_type
* @param ept_addr: endpoint address
* @retval usb_sts_type
*/
uint16_t usbh_alloc_channel(usbh_core_type *uhost, uint8_t ept_addr)
{
/* get one free channel */
uint16_t ch_num = usbh_get_free_channel(uhost);
if(ch_num == HCH_ERROR)
return USB_FAIL;
/* set channel to used */
uhost->channel[ch_num] = HCH_USED | ept_addr;
return ch_num;
}
/**
* @brief usb host get urb status
* @param uhost: to the structure of usbh_core_type
* @param ch_num: channel number
* @retval urb_sts_type: urb status
*/
urb_sts_type usbh_get_urb_status(usbh_core_type *uhost, uint8_t ch_num)
{
return uhost->urb_state[ch_num];
}
/**
* @brief usb wait control setup complete
* @param uhost: to the structure of usbh_core_type
* @param next_ctrl_state: next ctrl state when setup complete
* @param next_enum_state: next enum state when setup complete
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_ctrl_result_check(usbh_core_type *uhost, ctrl_ept0_sts_type next_ctrl_state, uint8_t next_enum_state)
{
usb_sts_type status;
/* control transfer loop */
status = usbh_ctrl_transfer_loop(uhost);
if(status == USB_OK)
{
uhost->ctrl.state = next_ctrl_state;
uhost->enum_state = next_enum_state;
uhost->req_state = CMD_SEND;
}
else if(status == USB_ERROR)
{
uhost->ctrl.state = CONTROL_IDLE;
uhost->req_state = CMD_SEND;
}
else if(status == USB_NOT_SUPPORT)
{
uhost->ctrl.state = next_ctrl_state;
uhost->enum_state = next_enum_state;
uhost->req_state = CMD_SEND;
}
return status;
}
/**
* @brief auto alloc address (1...20)
* @param none
* @retval address (1...20)
*/
uint8_t usbh_alloc_address(void)
{
static uint8_t address = 1;
if(address == 20)
address = 1;
return address ++;
}
/**
* @brief usb host enumeration handler
* @param uhost: to the structure of usbh_core_type
* @retval status: usb_sts_type status
*/
usb_sts_type usbh_enum_handler(usbh_core_type *uhost)
{
usb_sts_type status = USB_WAIT;
switch(uhost->enum_state)
{
case ENUM_IDLE:
break;
case ENUM_GET_MIN_DESC:
/* get description */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_get_device_descriptor(uhost, 8);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_GET_FULL_DESC) == USB_OK)
{
usbh_parse_dev_desc(uhost, uhost->rx_buffer, 8);
/* set new control endpoint maxpacket size */
uhost->ctrl.ept0_size = (uhost->dev).dev_desc.bMaxPacketSize0;
/* enable channel */
usbh_hc_open(uhost, uhost->ctrl.hch_in,0x80,
uhost->dev.address, EPT_CONTROL_TYPE,
uhost->ctrl.ept0_size,
uhost->dev.speed);
/* enable channel */
usbh_hc_open(uhost, uhost->ctrl.hch_out,0x00,
uhost->dev.address, EPT_CONTROL_TYPE,
uhost->ctrl.ept0_size,
uhost->dev.speed);
}
break;
case ENUM_GET_FULL_DESC:
/* get description */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_get_device_descriptor(uhost, 18);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_SET_ADDR) == USB_OK)
{
usbh_parse_dev_desc(uhost, uhost->rx_buffer, 18);
USBH_DEBUG("VID: %xh", uhost->dev.dev_desc.idVendor);
USBH_DEBUG("PID: %xh", uhost->dev.dev_desc.idProduct);
}
break;
case ENUM_SET_ADDR:
/* set device address */
if(uhost->ctrl.state == CONTROL_IDLE)
{
uhost->dev.address = usbh_alloc_address();
USBH_DEBUG("Set Address: %d", uhost->dev.address);
usbh_set_address(uhost, uhost->dev.address);
}
if (usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_GET_CFG) == USB_OK)
{
/* enable channel */
usbh_hc_open(uhost, uhost->ctrl.hch_in,0x80,
uhost->dev.address, EPT_CONTROL_TYPE,
uhost->ctrl.ept0_size,
uhost->dev.speed);
/* enable channel */
usbh_hc_open(uhost, uhost->ctrl.hch_out,0x00,
uhost->dev.address, EPT_CONTROL_TYPE,
uhost->ctrl.ept0_size,
uhost->dev.speed);
}
break;
case ENUM_GET_CFG:
/* get device confiuration */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_get_configure_descriptor(uhost, 9);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_GET_FULL_CFG) == USB_OK)
{
usbh_parse_configure_desc(uhost, uhost->rx_buffer, 9);
}
break;
case ENUM_GET_FULL_CFG:
/* get device confiuration */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_get_configure_descriptor(uhost, uhost->dev.cfg_desc.cfg.wTotalLength);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_GET_MFC_STRING) == USB_OK)
{
usbh_parse_configure_desc(uhost, uhost->rx_buffer, uhost->dev.cfg_desc.cfg.wTotalLength);
}
break;
case ENUM_GET_MFC_STRING:
/* get device mfc string */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_get_sting_descriptor(uhost, uhost->dev.dev_desc.iManufacturer,
uhost->rx_buffer, 0xFF);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_GET_PRODUCT_STRING) == USB_OK)
{
usbh_parse_string_desc(uhost->rx_buffer, uhost->rx_buffer, 0xFF);
uhost->user_handler->user_mfc_string(uhost->rx_buffer);
}
break;
case ENUM_GET_PRODUCT_STRING:
/* get device product string */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_get_sting_descriptor(uhost, uhost->dev.dev_desc.iProduct,
uhost->rx_buffer, 0xFF);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_GET_SERIALNUM_STRING) == USB_OK)
{
usbh_parse_string_desc(uhost->rx_buffer, uhost->rx_buffer, 0xFF);
uhost->user_handler->user_product_string(uhost->rx_buffer);
}
break;
case ENUM_GET_SERIALNUM_STRING:
/* get device serial string */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_get_sting_descriptor(uhost, uhost->dev.dev_desc.iSerialNumber,
uhost->rx_buffer, 0xFF);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_SET_CONFIG) == USB_OK)
{
usbh_parse_string_desc(uhost->rx_buffer, uhost->rx_buffer, 0xFF);
uhost->user_handler->user_serial_string(uhost->rx_buffer);
}
break;
case ENUM_SET_CONFIG:
/* set device config */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_set_configuration(uhost, uhost->dev.cfg_desc.cfg.bConfigurationValue);
}
usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_COMPLETE);
break;
case ENUM_COMPLETE:
/* enum complete */
status = USB_OK;
break;
default:
break;
}
return status;
}
/**
* @brief active vbus.
* @param uhost: to the structure of usbh_core_type
* @param state: vbus state
* @retval none
*/
void usbh_active_vbus(usbh_core_type *uhost, confirm_state state)
{
uhost->user_handler->user_active_vbus(uhost, state);
}
/**
* @brief reset usb port
* @param usbx: to the structure of otg_global_type
* @retval none
*/
void usbh_reset_port(usbh_core_type *uhost)
{
otg_host_type *usb_host = OTG_HOST(uhost->usb_reg);
uint32_t hprt_val = usb_host->hprt;
hprt_val &= ~(USB_OTG_HPRT_PRTENA | USB_OTG_HPRT_PRTENCHNG |
USB_OTG_HPRT_PRTOVRCACT | USB_OTG_HPRT_PRTCONDET);
/* set port reset */
usb_host->hprt = hprt_val | USB_OTG_HPRT_PRTRST;
usb_delay_ms(100);
/* clear port reset */
usb_host->hprt = hprt_val & (~USB_OTG_HPRT_PRTRST);
usb_delay_ms(20);
}
/**
* @brief usb host attached
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
static void usbh_attached(usbh_core_type *uhost)
{
/* get free channel */
uhost->ctrl.hch_in = usbh_alloc_channel(uhost, 0x80);
uhost->ctrl.hch_out = usbh_alloc_channel(uhost, 0x00);
/* user reset callback handler */
uhost->user_handler->user_reset();
/* get device speed */
uhost->dev.speed = OTG_HOST(uhost->usb_reg)->hprt_bit.prtspd;
uhost->global_state = USBH_ENUMERATION;
uhost->user_handler->user_speed(uhost->dev.speed);
/* enable channel */
usbh_hc_open(uhost, uhost->ctrl.hch_in,0x80,
uhost->dev.address, EPT_CONTROL_TYPE,
uhost->ctrl.ept0_size,
uhost->dev.speed);
/* enable channel */
usbh_hc_open(uhost, uhost->ctrl.hch_out,0x00,
uhost->dev.address, EPT_CONTROL_TYPE,
uhost->ctrl.ept0_size,
uhost->dev.speed);
usb_flush_tx_fifo(uhost->usb_reg, 0x10);
usb_flush_rx_fifo(uhost->usb_reg);
/* user attached callback */
uhost->user_handler->user_attached();
}
/**
* @brief usb host enumeration
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
static void usbh_enumeration(usbh_core_type *uhost)
{
/* enumeration process */
if(usbh_enum_handler(uhost) == USB_OK)
{
/* user enumeration done callback */
uhost->user_handler->user_enumeration_done();
uhost->global_state = USBH_USER_HANDLER;
}
}
/**
* @brief usb host class request
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
static void usbh_class_request(usbh_core_type *uhost)
{
usb_sts_type status;
/* class request callback */
status = uhost->class_handler->request_handler((void *)uhost);
if(status == USB_OK)
{
uhost->global_state = USBH_CLASS;
}
else if(status == USB_ERROR || status == USB_FAIL)
{
uhost->global_state = USBH_ERROR_STATE;
}
else if(status == USB_NOT_SUPPORT)
{
uhost->global_state = USBH_ERROR_STATE;
}
}
/**
* @brief usb host class handler
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
static void usbh_class(usbh_core_type *uhost)
{
/* process handler */
if(uhost->class_handler->process_handler((void *)uhost) == USB_OK)
{
}
}
/**
* @brief usb host suspend
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
static void usbh_suspend(usbh_core_type *uhost)
{
/* set device feature */
if(uhost->ctrl.state == CONTROL_IDLE)
{
usbh_set_feature(uhost, 0x01, 0);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_IDLE) == USB_OK)
{
/* enter suspend mode */
usb_delay_ms(3);
usbh_enter_suspend(uhost);
uhost->global_state = USBH_SUSPENDED;
}
}
/**
* @brief usb host wakeup
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
static void usbh_wakeup(usbh_core_type *uhost)
{
/* clear device feature */
if(uhost->ctrl.state == CONTROL_IDLE)
{
/* usb host resume */
usbh_resume(uhost);
usbh_clear_dev_feature(uhost, 0x01, 0);
}
if(usbh_ctrl_result_check(uhost, CONTROL_IDLE, ENUM_IDLE) == USB_OK)
{
uhost->global_state = USBH_CLASS_REQUEST;
USBH_DEBUG("Remote Wakeup");
}
}
/**
* @brief usb host disconnect
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
static void usbh_disconnect(usbh_core_type *uhost)
{
uint8_t i_index = 0;
/* set host to default state */
usbh_cfg_default_init(uhost);
/* free host channel */
for(i_index = 0; i_index < USB_HOST_CHANNEL_NUM; i_index ++)
{
usbh_free_channel(uhost, i_index);
}
/* call class reset handler */
if(uhost->class_handler->reset_handler != NULL)
{
uhost->class_handler->reset_handler(uhost);
}
/* set global state to idle */
uhost->global_state = USBH_IDLE;
/*call user disconnect function */
uhost->user_handler->user_disconnect();
}
/**
* @brief usb host enum loop handler
* @param uhost: to the structure of usbh_core_type
* @retval none
*/
usb_sts_type usbh_loop_handler(usbh_core_type *uhost)
{
usb_sts_type status = USB_FAIL;
if(uhost->conn_sts == 0 &&
uhost->global_state != USBH_IDLE &&
uhost->global_state != USBH_DISCONNECT)
{
uhost->global_state = USBH_IDLE;
}
switch(uhost->global_state)
{
case USBH_IDLE:
if(uhost->conn_sts == 1)
{
uhost->global_state = USBH_PORT_EN;
/* wait stable */
usb_delay_ms(200);
/* port reset */
usbh_reset_port(uhost);
/* user reset */
uhost->user_handler->user_reset();
}
break;
case USBH_PORT_EN:
if(uhost->port_enable)
{
uhost->global_state = USBH_ATTACHED;
usb_delay_ms(50);
}
break;
case USBH_ATTACHED:
usbh_attached(uhost);
break;
case USBH_ENUMERATION:
usbh_enumeration(uhost);
break;
case USBH_USER_HANDLER:
uhost->global_state = USBH_CLASS_REQUEST;
if( uhost->class_handler->init_handler(uhost) == USB_NOT_SUPPORT)
{
uhost->global_state = USBH_UNSUPPORT;
}
break;
case USBH_CLASS_REQUEST:
usbh_class_request(uhost);
break;
case USBH_CLASS:
usbh_class(uhost);
break;
case USBH_SUSPEND:
usbh_suspend(uhost);
break;
case USBH_SUSPENDED:
break;
case USBH_WAKEUP:
usbh_wakeup(uhost);
break;
case USBH_DISCONNECT:
usbh_disconnect(uhost);
break;
case USBH_ERROR_STATE:
usbh_cfg_default_init(uhost);
uhost->class_handler->reset_handler(uhost);
uhost->user_handler->user_reset();
break;
case USBH_UNSUPPORT:
break;
default:
break;
}
return status;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/