/** ************************************************************************** * @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; } /** * @} */ /** * @} */ /** * @} */