123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639 |
- /*!
- \file usbh_ctrl.c
- \brief this file implements the functions for the control transmit process
- \version 2014-12-26, V1.0.0, firmware for GD32F10x
- \version 2017-06-20, V2.0.0, firmware for GD32F10x
- \version 2018-07-31, V2.1.0, firmware for GD32F10x
- */
- /*
- Copyright (c) 2018, GigaDevice Semiconductor Inc.
- All rights reserved.
- Redistribution and use in source and binary forms, with or without modification,
- are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- 3. Neither the name of the copyright holder nor the names of its contributors
- may be used to endorse or promote products derived from this software without
- specific prior written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- OF SUCH DAMAGE.
- */
- #include "usbh_core.h"
- #include "usbh_std.h"
- #include "usbh_ctrl.h"
- uint8_t ctrl_polling_handle_flag = 0U;
- uint8_t ctrl_setup_wait_flag = 0U;
- uint8_t ctrl_data_wait_flag = 0U;
- uint8_t ctrl_status_wait_flag = 0U;
- static uint16_t timeout = 0U;
- static void ctrl_idle_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
- static void ctrl_setup_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
- static void ctrl_data_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
- static void ctrl_status_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
- static void ctrl_error_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
- static void ctrl_stalled_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
- static void ctrl_complete_handle (usb_core_handle_struct *pudev, usbh_host_struct *puhost, usbh_state_handle_struct *pustate);
- /* the ctrl state handle function array */
- void (*ctrl_state_handle[]) (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- usbh_state_handle_struct *pustate) =
- {
- ctrl_idle_handle,
- ctrl_setup_handle,
- ctrl_data_handle,
- ctrl_status_handle,
- ctrl_error_handle,
- ctrl_stalled_handle,
- ctrl_complete_handle,
- };
- /* the ctrl state handle table */
- state_table_struct ctrl_handle_table[CTRL_HANDLE_TABLE_SIZE] =
- {
- /* the current state the current event the next state the event function */
- {CTRL_IDLE, CTRL_EVENT_SETUP, CTRL_SETUP, only_state_move },
- {CTRL_SETUP, CTRL_EVENT_DATA, CTRL_DATA, only_state_move },
- {CTRL_SETUP, CTRL_EVENT_STATUS, CTRL_STATUS, only_state_move },
- {CTRL_SETUP, CTRL_EVENT_ERROR, CTRL_ERROR, only_state_move },
- {CTRL_DATA, CTRL_EVENT_STATUS, CTRL_STATUS, only_state_move },
- {CTRL_DATA, CTRL_EVENT_ERROR, CTRL_ERROR, only_state_move },
- {CTRL_DATA, CTRL_EVENT_STALLED, CTRL_STALLED, only_state_move },
- {CTRL_STATUS, CTRL_EVENT_COMPLETE, CTRL_COMPLETE, only_state_move },
- {CTRL_STATUS, CTRL_EVENT_ERROR, CTRL_ERROR, only_state_move },
- {CTRL_STATUS, CTRL_EVENT_STALLED, CTRL_STALLED, only_state_move },
- {CTRL_ERROR, GO_TO_UP_STATE_EVENT, UP_STATE, goto_up_state_fun },
- {CTRL_STALLED, GO_TO_UP_STATE_EVENT, UP_STATE, goto_up_state_fun },
- {CTRL_COMPLETE, GO_TO_UP_STATE_EVENT, UP_STATE, goto_up_state_fun },
- };
- /*!
- \brief the polling function of CTRL state
- \param[in] pudev: pointer to usb device
- \param[in] puhost: pointer to usb host
- \param[in] pustate: pointer to usb state driver
- \param[out] none
- \retval none
- */
- usbh_status_enum ctrl_state_polling_fun (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- void *pustate)
- {
- usbh_status_enum exe_state = USBH_BUSY;
- usbh_state_handle_struct *p_state;
- p_state = (usbh_state_handle_struct *)pustate;
- /* if first enter this function, begin the ctrl state */
- if (0U == ctrl_polling_handle_flag) {
- ctrl_polling_handle_flag = 1U;
- scd_table_push(p_state);
- scd_state_move(p_state, CTRL_IDLE);
- }
- /* base on the current state to handle the ctrl state */
- scd_begin(p_state, CTRL_FSM_ID);
- ctrl_state_handle[p_state->usbh_current_state](pudev, puhost, p_state);
- /* determine the control transfer whether to complete */
- switch (puhost->usbh_backup_state.ctrl_backup_state) {
- case CTRL_COMPLETE:
- ctrl_polling_handle_flag = 0U;
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_IDLE;
- exe_state = USBH_OK;
- break;
- case CTRL_STALLED:
- ctrl_polling_handle_flag = 0U;
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_IDLE;
- exe_state = USBH_NOT_SUPPORTED;
- break;
- case CTRL_ERROR:
- ctrl_polling_handle_flag = 0U;
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_IDLE;
- exe_state = USBH_FAIL;
- break;
- default:
- exe_state = USBH_BUSY;
- break;
- }
- return exe_state;
- }
- /*!
- \brief the handle function of CTRL_IDLE state
- \param[in] pudev: pointer to usb device
- \param[in] puhost: pointer to usb host
- \param[in] pustate: pointer to usb state driver
- \param[out] none
- \retval none
- */
- static void ctrl_idle_handle (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- usbh_state_handle_struct *pustate)
- {
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_IDLE;
- scd_event_handle(pudev, puhost, pustate, CTRL_EVENT_SETUP, pustate->usbh_current_state);
- }
- /*!
- \brief the handle function of CTRL_SETUP state
- \param[in] pudev: pointer to usb device
- \param[in] puhost: pointer to usb host
- \param[in] pustate: pointer to usb state driver
- \param[out] none
- \retval none
- */
- static void ctrl_setup_handle (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- usbh_state_handle_struct *pustate)
- {
- urb_state_enum urb_status = URB_IDLE;
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_SETUP;
- if (0U == ctrl_setup_wait_flag) {
- ctrl_setup_wait_flag = 1U;
- /* send a setup packet */
- usbh_ctltx_setup (pudev,
- puhost->control.setup.data,
- puhost->control.hc_out_num);
- } else {
- urb_status = hcd_urb_state_get(pudev, puhost->control.hc_out_num);
- /* case setup packet sent successfully */
- if (URB_DONE == urb_status) {
- /* check if there is a data stage */
- if (0U != puhost->control.setup.b.wLength) {
- ctrl_setup_wait_flag = 0U;
- timeout = DATA_STAGE_TIMEOUT;
- scd_event_handle(pudev, puhost, pustate, CTRL_EVENT_DATA, pustate->usbh_current_state);
- /* no data stage */
- } else {
- timeout = NODATA_STAGE_TIMEOUT;
- ctrl_setup_wait_flag = 0U;
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_STATUS,
- pustate->usbh_current_state);
- }
- /* set the delay timer to enable timeout for data stage completion */
- puhost->control.timer = (uint16_t)USB_CURRENT_FRAME_GET();
- } else if (URB_ERROR == urb_status) {
- ctrl_setup_wait_flag = 0U;
- scd_event_handle(pudev, puhost, pustate, CTRL_EVENT_ERROR, pustate->usbh_current_state);
- } else {
- /* no operation */
- }
- }
- }
- /*!
- \brief the handle function of CTRL_DATA state
- \param[in] pudev: pointer to usb device
- \param[in] puhost: pointer to usb host
- \param[in] pustate: pointer to usb state driver
- \param[out] none
- \retval none
- */
- static void ctrl_data_handle (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- usbh_state_handle_struct *pustate)
- {
- uint8_t direction;
- urb_state_enum urb_status = URB_IDLE;
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_DATA;
- direction = (puhost->control.setup.b.bmRequestType & USB_DIR_MASK);
- if (USB_DIR_IN == direction) {
- if (0U == ctrl_data_wait_flag) {
- ctrl_data_wait_flag = 1U;
- /* issue an IN token */
- usbh_xfer(pudev,
- puhost->control.buff,
- puhost->control.hc_in_num,
- puhost->control.length);
- } else {
- urb_status = hcd_urb_state_get(pudev, puhost->control.hc_in_num);
- /* check is data packet transfered successfully */
- switch (urb_status) {
- case URB_DONE:
- ctrl_data_wait_flag = 0U;
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_STATUS,
- pustate->usbh_current_state);
- break;
- case URB_STALL:
- ctrl_data_wait_flag = 0U;
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_STALLED,
- pustate->usbh_current_state);
- break;
- case URB_ERROR:
- ctrl_data_wait_flag = 0U;
- /* device error */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_ERROR,
- pustate->usbh_current_state);
- break;
- default:
- if (((uint16_t)USB_CURRENT_FRAME_GET() - puhost->control.timer) > timeout) {
- ctrl_data_wait_flag = 0U;
- /* timeout for in transfer */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_ERROR,
- pustate->usbh_current_state);
- }
- break;
- }
- }
- } else {
- if (0U == ctrl_data_wait_flag) {
- ctrl_data_wait_flag = 1U;
- /* start data out transfer (only one data packet)*/
- pudev->host.host_channel[puhost->control.hc_out_num].data_tg_out = 1U;
- usbh_xfer(pudev,
- puhost->control.buff,
- puhost->control.hc_out_num,
- puhost->control.length);
- } else {
- urb_status = hcd_urb_state_get(pudev, puhost->control.hc_out_num);
- switch (urb_status) {
- case URB_DONE:
- ctrl_data_wait_flag = 0U;
- /* if the setup pkt is sent successful, then change the state */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_STATUS,
- pustate->usbh_current_state);
- break;
- case URB_STALL:
- ctrl_data_wait_flag = 0U;
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_STALLED,
- pustate->usbh_current_state);
- break;
- case URB_NOTREADY:
- /* nack received from device */
- ctrl_data_wait_flag = 0U;
- break;
- case URB_ERROR:
- ctrl_data_wait_flag = 0U;
- /* device error */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_ERROR,
- pustate->usbh_current_state);
- break;
- default:
- break;
- }
- }
- }
- }
- /*!
- \brief the handle function of CTRL_STATUS state
- \param[in] pudev: pointer to usb device
- \param[in] puhost: pointer to usb host
- \param[in] pustate: pointer to usb state driver
- \param[out] none
- \retval none
- */
- static void ctrl_status_handle (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- usbh_state_handle_struct *pustate)
- {
- uint8_t direction;
- urb_state_enum urb_status = URB_IDLE;
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_STATUS;
- /* get the transfer direction in the data state, but the transfer direction in the status state is opposite */
- direction = (puhost->control.setup.b.bmRequestType & USB_DIR_MASK);
- if (USB_DIR_OUT == direction) {
- /* handle status in */
- if (0U == ctrl_status_wait_flag) {
- ctrl_status_wait_flag = 1U;
- usbh_xfer (pudev, 0U, puhost->control.hc_in_num, 0U);
- } else {
- urb_status = hcd_urb_state_get(pudev, puhost->control.hc_in_num);
- switch (urb_status) {
- case URB_DONE:
- ctrl_status_wait_flag = 0U;
- /* handle URB_DONE status */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_COMPLETE,
- pustate->usbh_current_state);
- break;
- case URB_ERROR:
- ctrl_status_wait_flag = 0U;
- /* handle URB_STALL status*/
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_ERROR,
- pustate->usbh_current_state);
- break;
- case URB_STALL:
- ctrl_status_wait_flag = 0U;
- /* handle URB_STALL status */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_STALLED,
- pustate->usbh_current_state);
- break;
- default:
- if (((uint16_t)USB_CURRENT_FRAME_GET() - puhost->control.timer) > timeout) {
- ctrl_status_wait_flag = 0U;
- /* handle timeout */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_ERROR,
- pustate->usbh_current_state);
- }
- break;
- }
- }
- } else {
- /* handle status out */
- if (0U == ctrl_status_wait_flag) {
- ctrl_status_wait_flag = 1U;
- pudev->host.host_channel[puhost->control.hc_out_num].data_tg_out ^= 1U;
- usbh_xfer (pudev, 0U, puhost->control.hc_out_num, 0U);
- } else {
- urb_status = hcd_urb_state_get(pudev, puhost->control.hc_out_num);
- switch (urb_status) {
- case URB_DONE:
- ctrl_status_wait_flag = 0U;
- /* handle URB_DONE status */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_COMPLETE,
- pustate->usbh_current_state);
- break;
- case URB_NOTREADY:
- /* handle URB_NOTREADY status */
- ctrl_status_wait_flag = 0U;
- break;
- case URB_ERROR:
- ctrl_status_wait_flag = 0U;
- /* handle URB_ERROR status */
- scd_event_handle(pudev,
- puhost,
- pustate,
- CTRL_EVENT_ERROR,
- pustate->usbh_current_state);
- break;
- default:
- break;
- }
- }
- }
- }
- /*!
- \brief the handle function of CTRL_ERROR state
- \param[in] pudev: pointer to usb device
- \param[in] puhost: pointer to usb host
- \param[in] pustate: pointer to usb state driver
- \param[out] none
- \retval none
- */
- static void ctrl_error_handle (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- usbh_state_handle_struct *pustate)
- {
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_ERROR;
- if (++puhost->control.error_count <= USBH_MAX_ERROR_COUNT) {
- /* do the transmission again, starting from setup packet */
- scd_event_handle(pudev, puhost, pustate, CTRL_EVENT_SETUP, pustate->usbh_current_state);
- } else {
- scd_event_handle(pudev, puhost, pustate, GO_TO_UP_STATE_EVENT, pustate->usbh_current_state);
- }
- }
- /*!
- \brief the handle function of CTRL_STALLED state
- \param[in] pudev: pointer to usb device
- \param[in] puhost: pointer to usb host
- \param[in] pustate: pointer to usb state driver
- \param[out] none
- \retval none
- */
- static void ctrl_stalled_handle (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- usbh_state_handle_struct *pustate)
- {
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_STALLED;
- scd_event_handle(pudev, puhost, pustate, GO_TO_UP_STATE_EVENT, pustate->usbh_current_state);
- }
- /*!
- \brief the handle function of CTRL_COMPLETE state
- \param[in] pudev: pointer to usb device
- \param[in] puhost: pointer to usb host
- \param[in] pustate: pointer to usb state driver
- \param[out] none
- \retval none
- */
- static void ctrl_complete_handle (usb_core_handle_struct *pudev,
- usbh_host_struct *puhost,
- usbh_state_handle_struct *pustate)
- {
- puhost->usbh_backup_state.ctrl_backup_state = CTRL_COMPLETE;
- scd_event_handle(pudev, puhost, pustate, GO_TO_UP_STATE_EVENT, pustate->usbh_current_state);
- }
- /*!
- \brief send datas from the host channel
- \param[in] pudev: pointer to usb device
- \param[in] buf: data buffer address to send datas
- \param[in] hc_num: the number of the host channel
- \param[in] len: length of the send data
- \param[out] none
- \retval host operation status
- */
- usbh_status_enum usbh_xfer (usb_core_handle_struct *pudev,
- uint8_t *buf,
- uint8_t hc_num,
- uint16_t len)
- {
- usb_hostchannel_struct *puhc = &pudev->host.host_channel[hc_num];
- puhc->xfer_buff = buf;
- puhc->xfer_len = len;
- switch (puhc->endp_type) {
- case USB_EPTYPE_CTRL:
- if (0U == puhc->endp_in) {
- if (0U == len) {
- /* for status out stage, length = 0, status out pid = 1 */
- puhc->data_tg_out = 1U;
- }
- /* set the data toggle bit as per the flag */
- if (0U == puhc->data_tg_out) {
- /* put the pid 0 */
- puhc->DPID = HC_PID_DATA0;
- } else {
- /* put the pid 1 */
- puhc->DPID = HC_PID_DATA1;
- }
- } else {
- puhc->DPID = HC_PID_DATA1;
- }
- break;
- case USB_EPTYPE_ISOC:
- puhc->DPID = HC_PID_DATA0;
- break;
- case USB_EPTYPE_BULK:
- if (0U == puhc->endp_in) {
- /* set the data toggle bit as per the flag */
- if (0U == puhc->data_tg_out) {
- /* put the pid 0 */
- puhc->DPID = HC_PID_DATA0;
- } else {
- /* put the pid 1 */
- puhc->DPID = HC_PID_DATA1;
- }
- } else {
- if (0U == puhc->data_tg_in) {
- puhc->DPID = HC_PID_DATA0;
- } else {
- puhc->DPID = HC_PID_DATA1;
- }
- }
- break;
- case USB_EPTYPE_INTR:
- if (0U == puhc->endp_in) {
- if (0U == puhc->data_tg_out) {
- puhc->DPID = HC_PID_DATA0;
- } else {
- puhc->DPID = HC_PID_DATA1;
- }
- /* toggle data pid */
- puhc->data_tg_out ^= 1U;
- } else {
- if (0U == puhc->data_tg_in) {
- puhc->DPID = HC_PID_DATA0;
- } else {
- puhc->DPID = HC_PID_DATA1;
- }
- /* toggle data pid */
- puhc->data_tg_in ^= 1U;
- }
- break;
- default:
- break;
- }
- hcd_submit_request (pudev, hc_num);
- return USBH_OK;
- }
- /*!
- \brief send the setup packet to the device
- \param[in] pudev: pointer to usb device
- \param[in] buf: buffer pointer from which the data will be send to device
- \param[in] hc_num: host channel number
- \param[out] none
- \retval host operation status
- */
- usbh_status_enum usbh_ctltx_setup (usb_core_handle_struct *pudev, uint8_t *buf, uint8_t hc_num)
- {
- usb_hostchannel_struct *puhc = &pudev->host.host_channel[hc_num];
- puhc->DPID = HC_PID_SETUP;
- puhc->xfer_buff = buf;
- puhc->xfer_len = USBH_SETUP_PACKET_SIZE;
- return (usbh_status_enum)hcd_submit_request (pudev, hc_num);
- }
- /*!
- \brief this function prepare a hc and start a transfer
- \param[in] pudev: pointer to usb device
- \param[in] channel_num: host channel number which is in (0..7)
- \param[out] none
- \retval host operation status
- */
- uint32_t hcd_submit_request (usb_core_handle_struct *pudev, uint8_t channel_num)
- {
- usb_hostchannel_struct *puhc = &pudev->host.host_channel[channel_num];
- puhc->urb_state = URB_IDLE;
- puhc->xfer_count = 0U;
- return (uint32_t)usb_hostchannel_startxfer(pudev, channel_num);
- }
|