Sync betaflight to Gitea

This commit is contained in:
2026-08-03 16:37:10 +08:00
commit ad3163cf91
4712 changed files with 3301380 additions and 0 deletions
@@ -0,0 +1,116 @@
/*!
\file usbh_hid_core.h
\brief header file for the usbh_hid_core.c
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_HID_CORE_H
#define USBH_HID_CORE_H
#include "usb_hid.h"
#include "usbh_enum.h"
#include "usbh_transc.h"
#define HID_MIN_POLL 10U /*!< HID minimum polling */
#define HID_REPORT_SIZE 16U /*!< HID report size */
#define HID_QUEUE_SIZE 10U /*!< HID queue size */
#define USB_HID_DESC_SIZE 9U /*!< HID descriptor size */
/* states for HID state machine */
typedef enum {
HID_INIT = 0U, /*!< HID init state */
HID_IDLE, /*!< HID idle state */
HID_SEND_DATA, /*!< HID send data state */
HID_BUSY, /*!< HID busy state */
HID_GET_DATA, /*!< HID get data state */
HID_SYNC, /*!< HID synchronous state */
HID_POLL, /*!< HID polling state */
HID_ERROR /*!< HID error state */
} hid_state;
/* state types of HID control request */
typedef enum {
HID_REQ_INIT = 0U, /*!< HID init request */
HID_REQ_IDLE, /*!< HID idle request */
HID_REQ_GET_REPORT_DESC, /*!< get report descriptor request */
HID_REQ_GET_HID_DESC, /*!< get HID descriptor request */
HID_REQ_SET_IDLE, /*!< set idle request */
HID_REQ_SET_PROTOCOL, /*!< set protocol request */
HID_REQ_SET_REPORT /*!< set report request */
} hid_ctlstate;
/* HID types */
typedef enum {
HID_MOUSE = 0x01U, /*!< HID mouse type */
HID_KEYBOARD = 0x02U, /*!< HID keyboard type */
HID_UNKNOWN = 0xFFU /*!< unknown type */
} hid_type;
typedef struct {
uint8_t *buf; /*!< data FIFO buff pointer */
uint16_t head; /*!< data FIFO header */
uint16_t tail; /*!< data FIFO tail */
uint16_t size; /*!< data FIFO size */
uint8_t lock; /*!< data FIFO lock */
} data_fifo;
/* structure for HID process */
typedef struct _hid_process {
uint8_t pipe_in; /*!< pipe IN */
uint8_t pipe_out; /*!< pipe OUT */
uint8_t ep_addr; /*!< endpoint address */
uint8_t ep_in; /*!< endpoint IN */
uint8_t ep_out; /*!< endpoint OUT */
uint8_t *pdata; /*!< HID data pointer */
__IO uint8_t data_ready; /*!< HID data ready */
uint16_t len; /*!< HID data length */
uint16_t poll; /*!< HID polling */
__IO uint32_t timer; /*!< HID timer */
usb_desc_hid hid_desc; /*!< HID descriptor */
hid_state state; /*!< HID state structure */
hid_ctlstate ctl_state; /*!< control request state structure */
usbh_status (*init)(usb_core_driver *udev, usbh_host *uhost);
usbh_status (*decode)(uint8_t *data);
} usbh_hid_handler;
extern usbh_class usbh_hid;
/* function declarations */
/* set HID report */
usbh_status usbh_set_report(usb_core_driver *udev, \
usbh_host *uhost, \
uint8_t report_type, \
uint8_t report_ID, \
uint8_t report_len, \
uint8_t *report_buf);
#endif /* USBH_HID_CORE_H */
@@ -0,0 +1,96 @@
/*!
\file usbh_standard_hid.h
\brief header file for usbh_standard_hid.c
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_STANDARD_HID_H
#define USBH_STANDARD_HID_H
#include "usbh_hid_core.h"
//#define AZERTY_KEYBOARD
#define QWERTY_KEYBOARD
#define MOUSE_BUTTON_1 0x01U /*!< mouse button 1 */
#define MOUSE_BUTTON_2 0x02U /*!< mouse button 2 */
#define MOUSE_BUTTON_3 0x04U /*!< mouse button 3 */
#define KBD_LEFT_CTRL 0x01U /*!< keyboard left ctrl key */
#define KBD_LEFT_SHIFT 0x02U /*!< keyboard left shift key */
#define KBD_LEFT_ALT 0x04U /*!< keyboard left alt key */
#define KBD_LEFT_GUI 0x08U /*!< keyboard left gui key */
#define KBD_RIGHT_CTRL 0x10U /*!< keyboard right ctrl key */
#define KBD_RIGHT_SHIFT 0x20U /*!< keyboard right shift key */
#define KBD_RIGHT_ALT 0x40U /*!< keyboard right alt key */
#define KBD_RIGHT_GUI 0x80U /*!< keyboard right gui key */
#define KBD_PRESSED_MAX_NUM 6U /*!< keyboard pressed maximum number */
typedef struct _mouse_report_data {
uint8_t x; /*!< X coordinate value */
uint8_t y; /*!< Y coordinate value */
uint8_t buttons[3]; /*!< button buff */
} mouse_report_data;
typedef struct {
uint8_t state; /*!< keyboard state */
uint8_t lctrl; /*!< keyboard left ctrl */
uint8_t lshift; /*!< keyboard left shift */
uint8_t lalt; /*!< keyboard left alt */
uint8_t lgui; /*!< keyboard left gui */
uint8_t rctrl; /*!< keyboard right ctrl */
uint8_t rshift; /*!< keyboard right shift */
uint8_t ralt; /*!< keyboard right alt */
uint8_t rgui; /*!< keyboard right gui */
uint8_t keys[6]; /*!< keyboard keys buff */
} hid_keybd_info;
/* function declarations */
/* initialize mouse */
void usr_mouse_init(void);
/* process mouse data */
void usr_mouse_process_data(mouse_report_data *data);
/* initialize mouse function */
usbh_status usbh_hid_mouse_init(usb_core_driver *udev, usbh_host *uhost);
/* decode mouse information */
usbh_status usbh_hid_mouse_decode(uint8_t *data);
/* initialize keyboard */
void usr_keybrd_init(void);
/* process keyboard data */
void usr_keybrd_process_data(uint8_t pbuf);
/* initialize the keyboard function */
usbh_status usbh_hid_keybrd_init(usb_core_driver *udev, usbh_host *uhost);
/* decode keyboard information */
usbh_status usbh_hid_keybrd_decode(uint8_t *data);
#endif /* USBH_STANDARD_HID_H */
@@ -0,0 +1,582 @@
/*!
\file usbh_hid_core.c
\brief USB host HID class driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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 <string.h>
#include "usbh_pipe.h"
#include "usbh_hid_core.h"
#include "usbh_standard_hid.h"
/* local function prototypes ('static') */
static void usbh_hiddesc_parse(usb_desc_hid *hid_desc, uint8_t *buf);
static void usbh_hid_itf_deinit(usbh_host *uhost);
static usbh_status usbh_hid_itf_init(usbh_host *uhost);
static usbh_status usbh_hid_class_req(usbh_host *uhost);
static usbh_status usbh_hid_handle(usbh_host *uhost);
static usbh_status usbh_hid_reportdesc_get(usbh_host *uhost, uint16_t len);
static usbh_status usbh_hid_sof(usbh_host *uhost);
static usbh_status usbh_hid_desc_get(usbh_host *uhost, uint16_t len);
static usbh_status usbh_set_idle(usbh_host *uhost, uint8_t duration, uint8_t report_ID);
static usbh_status usbh_set_protocol(usbh_host *uhost, uint8_t protocol);
usbh_class usbh_hid = {
USB_HID_CLASS,
usbh_hid_itf_init,
usbh_hid_itf_deinit,
usbh_hid_class_req,
usbh_hid_handle,
usbh_hid_sof
};
/*!
\brief get report
\param[in] uhost: pointer to USB host
\param[in] report_type: duration for HID set idle request
\param[in] report_ID: targeted report ID for HID set idle request
\param[in] report_len: length of data report to be send
\param[in] report_buf: report buffer
\param[out] none
\retval operation status
*/
usbh_status usbh_get_report(usbh_host *uhost, \
uint8_t report_type, \
uint8_t report_ID, \
uint8_t report_len, \
uint8_t *report_buf)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_ITF | USB_REQTYPE_CLASS,
.bRequest = GET_REPORT,
.wValue = (report_type << 8) | report_ID,
.wIndex = 0U,
.wLength = report_len
};
usbh_ctlstate_config(uhost, report_buf, report_len);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set report
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[in] report_type: duration for HID set idle request
\param[in] report_ID: targeted report ID for HID set idle request
\param[in] report_len: length of data report to be send
\param[in] report_buf: report buffer
\param[out] none
\retval operation status
*/
usbh_status usbh_set_report(usb_core_driver *udev, \
usbh_host *uhost, \
uint8_t report_type, \
uint8_t report_ID, \
uint8_t report_len, \
uint8_t *report_buf)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_CLASS,
.bRequest = SET_REPORT,
.wValue = (report_type << 8) | report_ID,
.wIndex = 0U,
.wLength = report_len
};
usbh_ctlstate_config(uhost, report_buf, report_len);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief deinitialize the host pipes used for the HID class
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
void usbh_hid_itf_deinit(usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
if(0x00U != hid->pipe_in) {
usb_pipe_halt(uhost->data, hid->pipe_in);
usbh_pipe_free(uhost->data, hid->pipe_in);
/* reset the pipe as free */
hid->pipe_in = 0U;
}
if(0x00U != hid->pipe_out) {
usb_pipe_halt(uhost->data, hid->pipe_out);
usbh_pipe_free(uhost->data, hid->pipe_out);
/* reset the channel as free */
hid->pipe_out = 0U;
}
}
/*!
\brief return device type
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval hid_type
*/
hid_type usbh_hid_device_type_get(usb_core_driver *udev, usbh_host *uhost)
{
hid_type type = HID_UNKNOWN;
uint8_t interface_protocol;
if(HOST_CLASS_HANDLER == uhost->cur_state) {
interface_protocol = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].itf_desc.bInterfaceProtocol;
if(USB_HID_PROTOCOL_KEYBOARD == interface_protocol) {
type = HID_KEYBOARD;
} else {
if(USB_HID_PROTOCOL_MOUSE == interface_protocol) {
type = HID_MOUSE;
}
}
}
return type;
}
/*!
\brief return HID device poll time
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval poll time (ms)
*/
uint8_t usbh_hid_poll_interval_get(usb_core_driver *udev, usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
if((HOST_CLASS_ENUM == uhost->cur_state) || \
(HOST_USER_INPUT == uhost->cur_state) || \
(HOST_CLASS_CHECK == uhost->cur_state) || \
(HOST_CLASS_HANDLER == uhost->cur_state)) {
return (uint8_t)(hid->poll);
} else {
return 0U;
}
}
/*!
\brief initialize the hid class
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_itf_init(usbh_host *uhost)
{
uint8_t num = 0U, ep_num = 0U, interface = 0U;
usbh_status status = USBH_BUSY;
interface = usbh_interface_find(&uhost->dev_prop, USB_HID_CLASS, USB_HID_SUBCLASS_BOOT_ITF, 0xFFU);
if(0xFFU == interface) {
uhost->usr_cb->dev_not_supported();
status = USBH_FAIL;
} else {
usbh_interface_select(&uhost->dev_prop, interface);
static usbh_hid_handler hid_handler;
memset((void *)&hid_handler, 0U, sizeof(usbh_hid_handler));
hid_handler.state = HID_ERROR;
uint8_t itf_protocol = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].itf_desc.bInterfaceProtocol;
if(USB_HID_PROTOCOL_KEYBOARD == itf_protocol) {
hid_handler.init = usbh_hid_keybrd_init;
hid_handler.decode = usbh_hid_keybrd_decode;
} else if(USB_HID_PROTOCOL_MOUSE == itf_protocol) {
hid_handler.init = usbh_hid_mouse_init;
hid_handler.decode = usbh_hid_mouse_decode;
} else {
status = USBH_FAIL;
}
hid_handler.state = HID_INIT;
hid_handler.ctl_state = HID_REQ_INIT;
hid_handler.ep_addr = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].ep_desc[0].bEndpointAddress;
hid_handler.len = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].ep_desc[0].wMaxPacketSize;
hid_handler.poll = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].ep_desc[0].bInterval;
if(hid_handler.poll < HID_MIN_POLL) {
hid_handler.poll = HID_MIN_POLL;
}
/* check for available number of endpoints */
/* find the number of endpoints in the interface descriptor */
/* choose the lower number in order not to overrun the buffer allocated */
ep_num = USB_MIN(uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].itf_desc.bNumEndpoints, USBH_MAX_EP_NUM);
/* decode endpoint IN and OUT address from interface descriptor */
for(num = 0U; num < ep_num; num++) {
usb_desc_ep *ep_desc = &uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].ep_desc[num];
uint8_t ep_addr = ep_desc->bEndpointAddress;
if(ep_addr & 0x80U) {
hid_handler.ep_in = ep_addr;
hid_handler.pipe_in = usbh_pipe_allocate(uhost->data, ep_addr);
/* open channel for IN endpoint */
usbh_pipe_create(uhost->data, \
&uhost->dev_prop, \
hid_handler.pipe_in, \
USB_EPTYPE_INTR, \
hid_handler.len);
usbh_pipe_toggle_set(uhost->data, hid_handler.pipe_in, 0U);
} else {
hid_handler.ep_out = ep_addr;
hid_handler.pipe_out = usbh_pipe_allocate(uhost->data, ep_addr);
/* open channel for OUT endpoint */
usbh_pipe_create(uhost->data, \
&uhost->dev_prop, \
hid_handler.pipe_out, \
USB_EPTYPE_INTR, \
hid_handler.len);
usbh_pipe_toggle_set(uhost->data, hid_handler.pipe_out, 0U);
}
}
uhost->active_class->class_data = (void *)&hid_handler;
status = USBH_OK;
}
return status;
}
/*!
\brief handle HID class requests for HID class
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_class_req(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
usbh_status class_req_status = USBH_BUSY;
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
/* handle HID control state machine */
switch(hid->ctl_state) {
case HID_REQ_INIT:
case HID_REQ_GET_HID_DESC:
/* get HID descriptor */
if(USBH_OK == usbh_hid_desc_get(uhost, USB_HID_DESC_SIZE)) {
usbh_hiddesc_parse(&hid->hid_desc, uhost->dev_prop.data);
hid->ctl_state = HID_REQ_GET_REPORT_DESC;
}
break;
case HID_REQ_GET_REPORT_DESC:
/* get report descriptor */
if(USBH_OK == usbh_hid_reportdesc_get(uhost, hid->hid_desc.wDescriptorLength)) {
hid->ctl_state = HID_REQ_SET_IDLE;
}
break;
case HID_REQ_SET_IDLE:
class_req_status = usbh_set_idle(uhost, 0U, 0U);
/* set idle */
if(USBH_OK == class_req_status) {
hid->ctl_state = HID_REQ_SET_PROTOCOL;
} else {
if(USBH_NOT_SUPPORTED == class_req_status) {
hid->ctl_state = HID_REQ_SET_PROTOCOL;
}
}
break;
case HID_REQ_SET_PROTOCOL:
/* set protocol */
if(USBH_OK == usbh_set_protocol(uhost, 0U)) {
hid->ctl_state = HID_REQ_IDLE;
/* all requests performed */
status = USBH_OK;
}
break;
case HID_REQ_IDLE:
default:
break;
}
return status;
}
/*!
\brief manage state machine for HID data transfers
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_handle(usbh_host *uhost)
{
usbh_status status = USBH_OK;
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
switch(hid->state) {
case HID_INIT:
hid->init(uhost->data, uhost);
hid->state = HID_IDLE;
break;
case HID_IDLE:
hid->state = HID_SYNC;
status = USBH_OK;
break;
case HID_SYNC:
/* sync with start of even frame */
if(1U == usb_frame_even(uhost->data)) {
hid->state = HID_GET_DATA;
}
break;
case HID_GET_DATA:
usbh_data_recev(uhost->data, hid->pdata, hid->pipe_in, hid->len);
hid->state = HID_POLL;
hid->timer = usb_curframe_get(uhost->data);
hid->data_ready = 0U;
break;
case HID_POLL:
if(URB_DONE == usbh_urbstate_get(uhost->data, hid->pipe_in)) {
if(0U == hid->data_ready) {
hid->data_ready = 1U;
hid->decode(hid->pdata);
}
} else {
/* check IN endpoint STALL status */
if(URB_STALL == usbh_urbstate_get(uhost->data, hid->pipe_in)) {
/* issue clear feature on interrupt IN endpoint */
if(USBH_OK == (usbh_clrfeature(uhost, hid->ep_addr, hid->pipe_in))) {
/* change state to issue next IN token */
hid->state = HID_GET_DATA;
}
}
}
break;
default:
break;
}
return status;
}
/*!
\brief send get report descriptor command to the device
\param[in] uhost: pointer to USB host
\param[in] len: HID report descriptor length
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_reportdesc_get(usbh_host *uhost, uint16_t len)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_ITF | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_REPORT),
.wIndex = 0U,
.wLength = len
};
usbh_ctlstate_config(uhost, uhost->dev_prop.data, len);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief managing the SOF process
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_sof(usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
if(HID_POLL == hid->state) {
uint32_t frame_count = usb_curframe_get(uhost->data);
if((frame_count > hid->timer) && ((frame_count - hid->timer) >= hid->poll)) {
hid->state = HID_GET_DATA;
} else if((frame_count < hid->timer) && ((frame_count + 0x3FFFU - hid->timer) >= hid->poll)) {
hid->state = HID_GET_DATA;
} else {
/* no operation */
}
}
return USBH_OK;
}
/*!
\brief send the command of get HID descriptor to the device
\param[in] uhost: pointer to USB host
\param[in] len: HID descriptor length
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_desc_get(usbh_host *uhost, uint16_t len)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_ITF | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_HID),
.wIndex = 0U,
.wLength = len
};
usbh_ctlstate_config(uhost, uhost->dev_prop.data, len);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set idle state
\param[in] uhost: pointer to USB host
\param[in] duration: duration for HID set idle request
\param[in] report_ID: targeted report ID for HID set idle request
\param[out] none
\retval operation status
*/
static usbh_status usbh_set_idle(usbh_host *uhost, uint8_t duration, uint8_t report_ID)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_CLASS,
.bRequest = SET_IDLE,
.wValue = (duration << 8) | report_ID,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set protocol state
\param[in] uhost: pointer to USB host
\param[in] protocol: boot/report protocol
\param[out] none
\retval operation status
*/
static usbh_status usbh_set_protocol(usbh_host *uhost, uint8_t protocol)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_CLASS,
.bRequest = SET_PROTOCOL,
.wValue = !protocol,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief parse the HID descriptor
\param[in] hid_desc: pointer to HID descriptor
\param[in] buf: pointer to buffer where the source descriptor is available
\param[out] none
\retval none
*/
static void usbh_hiddesc_parse(usb_desc_hid *hid_desc, uint8_t *buf)
{
hid_desc->header.bLength = *(uint8_t *)(buf + 0U);
hid_desc->header.bDescriptorType = *(uint8_t *)(buf + 1U);
hid_desc->bcdHID = BYTE_SWAP(buf + 2U);
hid_desc->bCountryCode = *(uint8_t *)(buf + 4U);
hid_desc->bNumDescriptors = *(uint8_t *)(buf + 5U);
hid_desc->bDescriptorType = *(uint8_t *)(buf + 6U);
hid_desc->wDescriptorLength = BYTE_SWAP(buf + 7U);
}
@@ -0,0 +1,271 @@
/*!
\file usbh_standard_hid.c
\brief USB host HID keyboard and mouse driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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_standard_hid.h"
mouse_report_data mouse_info;
hid_keybd_info keybd_info;
__ALIGN_BEGIN uint8_t hid_mouse_info[8] __ALIGN_END = {0U};
__ALIGN_BEGIN uint32_t keybd_report_data[2] __ALIGN_END;
/* local constants */
static const uint8_t kbd_codes[] = {
0, 0, 0, 0, 31, 50, 48, 33,
19, 34, 35, 36, 24, 37, 38, 39, /* 0x00 - 0x0F */
52, 51, 25, 26, 17, 20, 32, 21,
23, 49, 18, 47, 22, 46, 2, 3, /* 0x10 - 0x1F */
4, 5, 6, 7, 8, 9, 10, 11,
43, 110, 15, 16, 61, 12, 13, 27, /* 0x20 - 0x2F */
28, 29, 42, 40, 41, 1, 53, 54,
55, 30, 112, 113, 114, 115, 116, 117, /* 0x30 - 0x3F */
118, 119, 120, 121, 122, 123, 124, 125,
126, 75, 80, 85, 76, 81, 86, 89, /* 0x40 - 0x4F */
79, 84, 83, 90, 95, 100, 105, 106,
108, 93, 98, 103, 92, 97, 102, 91, /* 0x50 - 0x5F */
96, 101, 99, 104, 45, 129, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6F */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7F */
0, 0, 0, 0, 0, 107, 0, 56,
0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0 - 0xCF */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0 - 0xDF */
58, 44, 60, 127, 64, 57, 62, 128 /* 0xE0 - 0xE7 */
};
#ifdef QWERTY_KEYBOARD
static const int8_t kbd_key[] = {
'\0', '`', '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', '\0', '\r',
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u',
'i', 'o', 'p', '[', ']', '\\',
'\0', 'a', 's', 'd', 'f', 'g', 'h', 'j',
'k', 'l', ';', '\'', '\0', '\n',
'\0', '\0', 'z', 'x', 'c', 'v', 'b', 'n',
'm', ',', '.', '/', '\0', '\0',
'\0', '\0', '\0', ' ', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\r', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '7', '4', '1',
'\0', '/', '8', '5', '2',
'0', '*', '9', '6', '3',
'.', '-', '+', '\0', '\n', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0'
};
static const int8_t kbd_key_shift[] = {
'\0', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
'_', '+', '\0', '\0', '\0', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U',
'I', 'O', 'P', '{', '}', '|', '\0', 'A', 'S', 'D', 'F', 'G',
'H', 'J', 'K', 'L', ':', '"', '\0', '\n', '\0', '\0', 'Z', 'X',
'C', 'V', 'B', 'N', 'M', '<', '>', '?', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
};
#else
static const int8_t kbd_key[] = {
'\0', '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'-', '=', '\0', '\r', '\t', 'a', 'z', 'e', 'r', 't', 'y', 'u',
'i', 'o', 'p', '[', ']', '\\', '\0', 'q', 's', 'd', 'f', 'g',
'h', 'j', 'k', 'l', 'm', '\0', '\0', '\n', '\0', '\0', 'w', 'x',
'c', 'v', 'b', 'n', ',', ';', ':', '!', '\0', '\0', '\0', '\0',
'\0', ' ', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\r', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '7', '4', '1', '\0', '/',
'8', '5', '2', '0', '*', '9', '6', '3', '.', '-', '+', '\0',
'\n', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
};
static const int8_t kbd_key_shift[] = {
'\0', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_',
'+', '\0', '\0', '\0', 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O',
'P', '{', '}', '*', '\0', 'Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K',
'L', 'M', '%', '\0', '\n', '\0', '\0', 'W', 'X', 'C', 'V', 'B', 'N',
'?', '.', '/', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
};
#endif /* QWERTY_KEYBOARD */
/*!
\brief initialize the mouse function
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
usbh_status usbh_hid_mouse_init(usb_core_driver *udev, usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
mouse_info.x = 0U;
mouse_info.y = 0U;
mouse_info.buttons[0] = 0U;
mouse_info.buttons[1] = 0U;
mouse_info.buttons[2] = 0U;
if(hid->len > sizeof(hid_mouse_info)) {
hid->len = sizeof(hid_mouse_info);
}
hid->pdata = (uint8_t *)(void *)hid_mouse_info;
usr_mouse_init();
return USBH_OK;
}
/*!
\brief decode mouse information
\param[in] data: pointer to input data
\param[out] none
\retval operation status
*/
usbh_status usbh_hid_mouse_decode(uint8_t *data)
{
mouse_info.buttons[0] = data[0] & MOUSE_BUTTON_1;
mouse_info.buttons[1] = data[0] & MOUSE_BUTTON_2;
mouse_info.buttons[2] = data[0] & MOUSE_BUTTON_3;
mouse_info.x = data[1];
mouse_info.y = data[2];
/* handle mouse data position */
usr_mouse_process_data(&mouse_info);
return USBH_FAIL;
}
/*!
\brief initialize the keyboard function
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
usbh_status usbh_hid_keybrd_init(usb_core_driver *udev, usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
keybd_info.lctrl = keybd_info.lshift = 0U;
keybd_info.lalt = keybd_info.lgui = 0U;
keybd_info.rctrl = keybd_info.rshift = 0U;
keybd_info.ralt = keybd_info.rgui = 0U;
for(uint32_t x = 0U; x < (sizeof(keybd_report_data) / sizeof(uint32_t)); x++) {
keybd_report_data[x] = 0U;
}
if(hid->len > (sizeof(keybd_report_data) / sizeof(uint32_t))) {
hid->len = (sizeof(keybd_report_data) / sizeof(uint32_t));
}
hid->pdata = (uint8_t *)(void *)keybd_report_data;
/* call user initialization*/
usr_keybrd_init();
return USBH_OK;
}
/*!
\brief get ASCII code
\param[in] info: keyboard information
\param[out] none
\retval output
*/
uint8_t usbh_hid_ascii_code_get(hid_keybd_info *info)
{
uint8_t output = 0U;
if((1U == info->lshift) || (info->rshift)) {
output = kbd_key_shift[kbd_codes[info->keys[0]]];
} else {
output = kbd_key[kbd_codes[info->keys[0]]];
}
return output;
}
/*!
\brief decode keyboard information
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
usbh_status usbh_hid_keybrd_decode(uint8_t *data)
{
uint8_t output = 0U;
keybd_info.lshift = data[0] & KBD_LEFT_SHIFT;
keybd_info.rshift = data[0] & KBD_RIGHT_SHIFT;
keybd_info.keys[0] = data[2];
if(keybd_info.lshift || keybd_info.rshift) {
output = kbd_key_shift[kbd_codes[keybd_info.keys[0]]];
} else {
output = kbd_key[kbd_codes[keybd_info.keys[0]]];
}
if(0U != output) {
usr_keybrd_process_data(output);
}
return USBH_OK;
}
@@ -0,0 +1,148 @@
/*!
\file usbh_msc_bbb.h
\brief header file for usbh_msc_bbb.c
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_MSC_BBB_H
#define USBH_MSC_BBB_H
#include "msc_bbb.h"
#include "usbh_core.h"
#define USBH_MSC_BBB_CBW_TAG 0x20304050U /*!< MSC BBB CBW tag */
#define USBH_MSC_CSW_MAX_LENGTH 63U /*!< MSC CSW maximum length */
#define USBH_MSC_SEND_CSW_DISABLE 0U /*!< MSC send CSW disable */
#define USBH_MSC_SEND_CSW_ENABLE 1U /*!< MSC send CSW enable */
#define USBH_MSC_DIR_IN 0U /*!< MSC data transfer IN */
#define USBH_MSC_DIR_OUT 1U /*!< MSC data transfer OUT */
#define USBH_MSC_BOTH_DIR 2U /*!< MSC data transfer IN/OUT */
#define USBH_MSC_PAGE_LENGTH 512U /*!< MSC memory page length */
#define CBW_CB_LENGTH 16U /*!< MSC CBW CB length */
#define CBW_LENGTH 10U /*!< MSC CBW length */
#define CBW_LENGTH_TEST_UNIT_READY 0U /*!< MSC CBW test unit ready length */
#define MAX_BULK_STALL_COUNT_LIMIT 0x04U /*!< if STALL is seen on bulk
endpoint continously, this means
that device and host has phase error
hence a reset is needed */
typedef union {
msc_bbb_cbw field; /*!< MSC BBB CBW structure */
uint8_t CBWArray[31]; /*!< MSC BBB CBW array buff */
}usbh_cbw_pkt;
typedef union {
msc_bbb_csw field; /*!< MSC BBB CSW structure */
uint8_t CSWArray[13]; /*!< MSC BBB CSW array buff */
}usbh_csw_pkt;
enum usbh_msc_state {
USBH_MSC_BBB_INIT_STATE = 0U, /*!< MSC BBB init state */
USBH_MSC_BBB_RESET, /*!< MSC BBB reset state */
USBH_MSC_GET_MAX_LUN, /*!< MSC init state */
USBH_MSC_TEST_UNIT_READY, /*!< MSC test unit ready state */
USBH_MSC_READ_CAPACITY10, /*!< MSC read capacity10 state */
USBH_MSC_MODE_SENSE6, /*!< MSC sense6 mode state */
USBH_MSC_REQUEST_SENSE, /*!< MSC request sense state */
USBH_MSC_BBB_USB_TRANSFERS, /*!< MSC BBB transfers state */
USBH_MSC_DEFAULT_APPLI_STATE, /*!< MSC default application state */
USBH_MSC_CTRL_ERROR_STATE, /*!< MSC control error state */
USBH_MSC_UNRECOVERED_STATE /*!< MSC unrecovered state */
};
/* MSC BBB status types */
typedef enum {
BBB_OK = 0U, /*!< MSC BBB OK status */
BBB_FAIL, /*!< MSC BBB fail status */
BBB_PHASE_ERROR, /*!< MSC BBB phase error status */
BBB_BUSY /*!< MSC BBB busy status */
} bbb_status;
/* MSC BBB command state types */
typedef enum {
BBB_CMD_IDLE = 0U, /*!< MSC BBB command idle state */
BBB_CMD_SEND, /*!< MSC BBB command send state */
BBB_CMD_WAIT /*!< MSC BBB command wait state */
} bbb_cmd_state;
/* CSW status definitions */
typedef enum {
BBB_CSW_CMD_PASSED = 0U, /*!< MSC BBB CSW command passed status */
BBB_CSW_CMD_FAILED, /*!< MSC BBB CSW command failed status */
BBB_CSW_PHASE_ERROR /*!< MSC BBB CSW phase error status */
} bbb_csw_status;
/* MSC BBB state types */
typedef enum {
BBB_SEND_CBW = 1U, /*!< MSC BBB send CBW state */
BBB_SEND_CBW_WAIT, /*!< MSC BBB send CBW wait state */
BBB_DATA_IN, /*!< MSC BBB data IN state */
BBB_DATA_IN_WAIT, /*!< MSC BBB data IN wait state */
BBB_DATA_OUT, /*!< MSC BBB data OUT state */
BBB_DATA_OUT_WAIT, /*!< MSC BBB data OUT wait state */
BBB_RECEIVE_CSW, /*!< MSC BBB receive CSW state */
BBB_RECEIVE_CSW_WAIT, /*!< MSC BBB receive CSW wait state */
BBB_ERROR_IN, /*!< MSC BBB error IN state */
BBB_ERROR_OUT, /*!< MSC BBB error OUT state */
BBB_UNRECOVERED_ERROR /*!< MSC BBB unrecovered error state */
} bbb_state;
typedef struct {
uint8_t *pbuf; /*!< MSC BBB data buff pointer */
uint32_t data[16]; /*!< MSC BBB data buff */
bbb_state state; /*!< MSC BBB state */
bbb_state prev_state; /*!< MSC BBB previous state */
bbb_cmd_state cmd_state; /*!< MSC BBB command state */
usbh_cbw_pkt cbw; /*!< MSC CBW pocket structure */
usbh_csw_pkt csw; /*!< MSC CSW pocket structure */
} bbb_handle;
/* function declarations */
/* initialize the mass storage parameters */
void usbh_msc_bbb_init(usbh_host *uhost);
/* manage the different states of BBB transfer and updates the status to upper layer */
usbh_status usbh_msc_bbb_process(usbh_host *uhost, uint8_t lun);
/* manages the different error handling for STALL */
usbh_status usbh_msc_bbb_abort(usbh_host *uhost, uint8_t direction);
/* reset MSC BBB request structure */
usbh_status usbh_msc_bbb_reset(usbh_host *uhost);
/* decode the CSW received by the device and updates the same to upper layer */
bbb_csw_status usbh_msc_csw_decode(usbh_host *uhost);
#endif /* USBH_MSC_BBB_H */
@@ -0,0 +1,122 @@
/*!
\file usbh_core.h
\brief header file for the usbh_core.c
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_MSC_CORE_H
#define USBH_MSC_CORE_H
#include "usb_msc.h"
#include "usbh_msc_scsi.h"
#include "usbh_msc_bbb.h"
#define MSC_MAX_SUPPORTED_LUN 2U
/* MSC state types */
typedef enum {
MSC_INIT = 0U, /*!< MSC init state */
MSC_IDLE, /*!< MSC idle state */
MSC_TEST_UNIT_READY, /*!< MSC test unit ready state */
MSC_READ_CAPACITY10, /*!< MSC read capacity10 state */
MSC_READ_INQUIRY, /*!< MSC read inquiry state */
MSC_REQUEST_SENSE, /*!< MSC request sense state */
MSC_READ, /*!< MSC read state */
MSC_WRITE, /*!< MSC write state */
MSC_UNRECOVERED_ERROR, /*!< MSC unrecovered state */
MSC_PERIODIC_CHECK /*!< MSC periodic check state */
} msc_state;
/* MSC error types */
typedef enum {
MSC_OK = 0U, /*!< MSC no error */
MSC_NOT_READY, /*!< MSC not ready */
MSC_ERROR /*!< MSC error */
} msc_error;
/* MSC request types */
typedef enum {
MSC_REQ_IDLE = 0U, /*!< MSC idle request state */
MSC_REQ_RESET, /*!< MSC reset request state */
MSC_REQ_GET_MAX_LUN, /*!< MSC get maximum LUN request state */
MSC_REQ_ERROR /*!< MSC error request state */
} msc_req_state;
/* structure for LUN */
typedef struct {
msc_state state; /*!< MSC LUN state */
msc_error error; /*!< MSC LUN error */
msc_scsi_sense sense; /*!< MSC SCSI sense */
scsi_capacity capacity; /*!< MSC SCSI capacity */
scsi_std_inquiry_data inquiry; /*!< MSC SCSI standard inquiry data */
usbh_status prev_ready_state; /*!< MSC previous ready state */
uint8_t state_changed; /*!< MSC state changed */
} msc_lun;
/* structure for MSC process */
typedef struct _msc_process {
uint8_t pipe_in; /*!< MSC pipe IN */
uint8_t pipe_out; /*!< MSC pipe OUT */
uint8_t ep_in; /*!< MSC endpoint IN */
uint8_t ep_out; /*!< MSC endpoint OUT */
uint16_t ep_size_in; /*!< MSC endpoint IN size */
uint16_t ep_size_out; /*!< MSC endpoint OUT size */
uint8_t cur_lun; /*!< MSC current LUN */
uint16_t rw_lun; /*!< MSC review LUN */
uint32_t max_lun; /*!< MSC maximum LUN */
msc_state state; /*!< MSC state */
msc_error error; /*!< MSC error */
msc_req_state req_state; /*!< MSC request state */
msc_req_state prev_req_state; /*!< MSC previous request state */
bbb_handle bbb; /*!< MSC BBB correlation parameter handle */
msc_lun unit[MSC_MAX_SUPPORTED_LUN]; /*!< MSC LUN unit buff */
uint32_t timer; /*!< MSC read/write timer */
} usbh_msc_handler;
extern usbh_class usbh_msc;
/* function declarations */
/* get MSC logic unit information */
usbh_status usbh_msc_lun_info_get(usbh_host *uhost, uint8_t lun, msc_lun *info);
/* MSC read interface */
usbh_status usbh_msc_read(usbh_host *uhost, \
uint8_t lun, \
uint32_t address, \
uint8_t *pbuf, \
uint32_t length);
/* MSC write interface */
usbh_status usbh_msc_write(usbh_host *uhost, \
uint8_t lun, \
uint32_t address, \
uint8_t *pbuf, \
uint32_t length);
#endif /* USBH_MSC_CORE_H */
@@ -0,0 +1,97 @@
/*!
\file usbh_msc_scsi.h
\brief header file for usbh_msc_scsi.c
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_MSC_SCSI_H
#define USBH_MSC_SCSI_H
#include "msc_scsi.h"
#include "usbh_enum.h"
#define DESC_REQUEST_SENSE 00U /*!< sense request descriptor */
#define ALLOCATION_LENGTH_REQUEST_SENSE 63U /*!< sense request allocation length */
#define XFER_LEN_MODE_SENSE6 63U /*!< MSC sense6 mode transfer length */
#define MASK_MODE_SENSE_WRITE_PROTECT 0x80U /*!< sense write protect mask mode */
#define MODE_SENSE_PAGE_CONTROL_FIELD 0x00U /*!< sense page control field mode */
#define MODE_SENSE_PAGE_CODE 0x3FU /*!< sense page code mode */
#define DISK_WRITE_PROTECTED 0x01U /*!< disk write protected */
/* capacity data */
typedef struct {
uint32_t block_nbr; /*!< MSC SCSI block number */
uint16_t block_size; /*!< MSC SCSI block size */
} scsi_capacity;
/* inquiry data */
typedef struct {
uint8_t peripheral_qualifier; /*!< MSC SCSI standard peripheral qualifier */
uint8_t device_type; /*!< MSC SCSI standard inquiry device types */
uint8_t removable_media; /*!< MSC SCSI standard inquiry removable media */
uint8_t vendor_id[9]; /*!< MSC SCSI standard inquiry vendor id buff */
uint8_t product_id[17]; /*!< MSC SCSI standard inquiry product id buff */
uint8_t revision_id[5]; /*!< MSC SCSI standard inquiry revision id buff */
} scsi_std_inquiry_data;
typedef struct {
uint32_t msc_capacity; /*!< MSC capacity */
uint32_t msc_sense_key; /*!< MSC sense key */
uint16_t msc_page_len; /*!< MSC memory page length */
uint8_t msc_write_protect; /*!< MSC write protect */
}usbh_msc_parameter;
/* function declarations */
/* send 'inquiry' command to the device */
usbh_status usbh_msc_scsi_inquiry(usbh_host *uhost, uint8_t lun, scsi_std_inquiry_data *inquiry);
/* send 'test unit ready' command to the device */
usbh_status usbh_msc_test_unitready(usbh_host *uhost, uint8_t lun);
/* send the read capacity command to the device */
usbh_status usbh_msc_read_capacity10(usbh_host *uhost, uint8_t lun, scsi_capacity *capacity);
/* send the mode sense6 command to the device */
usbh_status usbh_msc_mode_sense6(usbh_host *uhost, uint8_t lun);
/* send the request sense command to the device */
usbh_status usbh_msc_request_sense(usbh_host *uhost, uint8_t lun, msc_scsi_sense *sense_data);
/* send the write10 command to the device */
usbh_status usbh_msc_write10(usbh_host *uhost, \
uint8_t lun, \
uint8_t *data_buf, \
uint32_t addr, \
uint32_t byte_num);
/* send the read10 command to the device */
usbh_status usbh_msc_read10(usbh_host *uhost, \
uint8_t lun, \
uint8_t *data_buf, \
uint32_t addr, \
uint32_t byte_num);
#endif /* USBH_MSC_SCSI_H */
@@ -0,0 +1,360 @@
/*!
\file usbh_msc_bbb.c
\brief USB MSC BBB protocol related functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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_pipe.h"
#include "usbh_transc.h"
#include "usbh_msc_core.h"
#include "usbh_msc_bbb.h"
/*!
\brief initialize the mass storage parameters
\param[in] uhost: pointer to USB host handler
\param[out] none
\retval none
*/
void usbh_msc_bbb_init(usbh_host *uhost)
{
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
msc->bbb.cbw.field.dCBWSignature = BBB_CBW_SIGNATURE;
msc->bbb.cbw.field.dCBWTag = USBH_MSC_BBB_CBW_TAG;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_SEND;
}
/*!
\brief manage the different states of BBB transfer and updates the status to upper layer
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_bbb_process(usbh_host *uhost, uint8_t lun)
{
bbb_csw_status csw_status = BBB_CSW_CMD_FAILED;
usbh_status status = USBH_BUSY;
usbh_status error = USBH_BUSY;
usb_urb_state urb_status = URB_IDLE;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.state) {
case BBB_SEND_CBW:
msc->bbb.cbw.field.bCBWLUN = lun;
msc->bbb.state = BBB_SEND_CBW_WAIT;
/* send CBW */
usbh_data_send(uhost->data, \
msc->bbb.cbw.CBWArray, \
msc->pipe_out, \
BBB_CBW_LENGTH);
break;
case BBB_SEND_CBW_WAIT:
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_out);
if(URB_DONE == urb_status) {
if(0U != msc->bbb.cbw.field.dCBWDataTransferLength) {
if(USB_TRX_IN == (msc->bbb.cbw.field.bmCBWFlags & USB_TRX_MASK)) {
msc->bbb.state = BBB_DATA_IN;
} else {
msc->bbb.state = BBB_DATA_OUT;
}
} else {
msc->bbb.state = BBB_RECEIVE_CSW;
}
} else if(URB_NOTREADY == urb_status) {
msc->bbb.state = BBB_SEND_CBW;
} else {
if(URB_STALL == urb_status) {
msc->bbb.state = BBB_ERROR_OUT;
}
}
break;
case BBB_DATA_IN:
usbh_data_recev(uhost->data, \
msc->bbb.pbuf, \
msc->pipe_in, \
msc->ep_size_in);
msc->bbb.state = BBB_DATA_IN_WAIT;
break;
case BBB_DATA_IN_WAIT:
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_in);
/* BBB DATA IN stage */
if(URB_DONE == urb_status) {
if(msc->bbb.cbw.field.dCBWDataTransferLength > msc->ep_size_in) {
msc->bbb.pbuf += msc->ep_size_in;
msc->bbb.cbw.field.dCBWDataTransferLength -= msc->ep_size_in;
} else {
msc->bbb.cbw.field.dCBWDataTransferLength = 0U;
}
if(msc->bbb.cbw.field.dCBWDataTransferLength > 0U) {
usbh_data_recev(uhost->data, \
msc->bbb.pbuf, \
msc->pipe_in, \
msc->ep_size_in);
} else {
msc->bbb.state = BBB_RECEIVE_CSW;
}
} else if(URB_STALL == urb_status) {
/* this is data stage STALL condition */
msc->bbb.state = BBB_ERROR_IN;
} else {
/* no operation */
}
break;
case BBB_DATA_OUT:
usbh_data_send(uhost->data, \
msc->bbb.pbuf, \
msc->pipe_out, \
msc->ep_size_out);
msc->bbb.state = BBB_DATA_OUT_WAIT;
break;
case BBB_DATA_OUT_WAIT:
/* BBB DATA OUT stage */
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_out);
if(URB_DONE == urb_status) {
if(msc->bbb.cbw.field.dCBWDataTransferLength > msc->ep_size_out) {
msc->bbb.pbuf += msc->ep_size_out;
msc->bbb.cbw.field.dCBWDataTransferLength -= msc->ep_size_out;
} else {
msc->bbb.cbw.field.dCBWDataTransferLength = 0U; /* reset this value and keep in same state */
}
if(msc->bbb.cbw.field.dCBWDataTransferLength > 0U) {
usbh_data_send(uhost->data, \
msc->bbb.pbuf, \
msc->pipe_out, \
msc->ep_size_out);
} else {
msc->bbb.state = BBB_RECEIVE_CSW;
}
} else if(URB_NOTREADY == urb_status) {
msc->bbb.state = BBB_DATA_OUT;
} else if(URB_STALL == urb_status) {
msc->bbb.state = BBB_ERROR_OUT;
} else {
/* no operation */
}
break;
case BBB_RECEIVE_CSW:
/* BBB CSW stage */
usbh_data_recev(uhost->data, \
msc->bbb.csw.CSWArray, \
msc->pipe_in, \
BBB_CSW_LENGTH);
msc->bbb.state = BBB_RECEIVE_CSW_WAIT;
break;
case BBB_RECEIVE_CSW_WAIT:
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_in);
/* decode CSW */
if(URB_DONE == urb_status) {
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_SEND;
csw_status = usbh_msc_csw_decode(uhost);
if(BBB_CSW_CMD_PASSED == csw_status) {
status = USBH_OK;
} else {
status = USBH_FAIL;
}
} else if(URB_STALL == urb_status) {
msc->bbb.state = BBB_ERROR_IN;
} else {
/* no operation */
}
break;
case BBB_ERROR_IN:
error = usbh_msc_bbb_abort(uhost, USBH_MSC_DIR_IN);
if(USBH_OK == error) {
msc->bbb.state = BBB_RECEIVE_CSW;
} else if(USBH_UNRECOVERED_ERROR == status) {
/* this means that there is a STALL error limit, do reset recovery */
msc->bbb.state = BBB_UNRECOVERED_ERROR;
} else {
/* no operation */
}
break;
case BBB_ERROR_OUT:
status = usbh_msc_bbb_abort(uhost, USBH_MSC_DIR_OUT);
if(USBH_OK == status) {
uint8_t toggle = usbh_pipe_toggle_get(uhost->data, msc->pipe_out);
usbh_pipe_toggle_set(uhost->data, msc->pipe_out, 1U - toggle);
usbh_pipe_toggle_set(uhost->data, msc->pipe_in, 0U);
msc->bbb.state = BBB_ERROR_IN;
} else {
if(USBH_UNRECOVERED_ERROR == status) {
msc->bbb.state = BBB_UNRECOVERED_ERROR;
}
}
break;
case BBB_UNRECOVERED_ERROR:
status = usbh_msc_bbb_reset(uhost);
if(USBH_OK == status) {
msc->bbb.state = BBB_SEND_CBW;
}
break;
default:
break;
}
return status;
}
/*!
\brief manages the different error handling for stall
\param[in] uhost: pointer to USB host handler
\param[in] direction: data IN or OUT
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_bbb_abort(usbh_host *uhost, uint8_t direction)
{
usbh_status status = USBH_BUSY;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(direction) {
case USBH_MSC_DIR_IN :
/* send clrfeature command on bulk IN endpoint */
status = usbh_clrfeature(uhost, \
msc->ep_in, \
msc->pipe_in);
break;
case USBH_MSC_DIR_OUT :
/*send clrfeature command on bulk OUT endpoint */
status = usbh_clrfeature(uhost, \
msc->ep_out, \
msc->pipe_out);
break;
default:
break;
}
return status;
}
/*!
\brief reset MSC BBB transfer
\param[in] uhost: pointer to USB host handler
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_bbb_reset(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_REQTYPE_CLASS | USB_RECPTYPE_ITF,
.bRequest = BBB_RESET,
.wValue = 0U,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief decode the CSW received by the device and updates the same to upper layer
\param[in] uhost: pointer to USB host
\param[out] none
\retval on success USBH_MSC_OK, on failure USBH_MSC_FAIL
\notes
Refer to USB Mass-Storage Class: BBB (www.usb.org)
6.3.1 Valid CSW Conditions :
The host shall consider the CSW valid when:
1. dCSWSignature is equal to 53425355h
2. the CSW is 13 (Dh) bytes in length,
3. dCSWTag matches the dCBWTag from the corresponding CBW.
*/
bbb_csw_status usbh_msc_csw_decode(usbh_host *uhost)
{
bbb_csw_status status = BBB_CSW_CMD_FAILED;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
/* checking if the transfer length is different than 13 */
if(BBB_CSW_LENGTH != usbh_xfercount_get(uhost->data, msc->pipe_in)) {
status = BBB_CSW_PHASE_ERROR;
} else {
/* CSW length is correct */
/* check validity of the CSW Signature and CSWStatus */
if(BBB_CSW_SIGNATURE == msc->bbb.csw.field.dCSWSignature) {
/* check condition 1. dCSWSignature is equal to 53425355h */
if(msc->bbb.csw.field.dCSWTag == msc->bbb.cbw.field.dCBWTag) {
/* check condition 3. dCSWTag matches the dCBWTag from the corresponding CBW */
if(0U == msc->bbb.csw.field.bCSWStatus) {
status = BBB_CSW_CMD_PASSED;
} else if(1U == msc->bbb.csw.field.bCSWStatus) {
status = BBB_CSW_CMD_FAILED;
} else if(2U == msc->bbb.csw.field.bCSWStatus) {
status = BBB_CSW_PHASE_ERROR;
} else {
/* no operation */
}
}
} else {
/* if the CSW signature is not valid, we shall return the phase error to
upper layers for reset recovery */
status = BBB_CSW_PHASE_ERROR;
}
}
return status;
}
@@ -0,0 +1,551 @@
/*!
\file usbh_core.c
\brief USB MSC(mass storage device) class driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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_msc_core.h"
#include "usbh_pipe.h"
#include "usbh_transc.h"
#include <string.h>
/* local function prototypes ('static') */
static void usbh_msc_itf_deinit(usbh_host *uhost);
static usbh_status usbh_msc_itf_init(usbh_host *uhost);
static usbh_status usbh_msc_req(usbh_host *uhost);
static usbh_status usbh_msc_handle(usbh_host *uhost);
static usbh_status usbh_msc_maxlun_get(usbh_host *uhost, uint8_t *maxlun);
static usbh_status usbh_msc_rdwr_process(usbh_host *uhost, uint8_t lun);
usbh_class usbh_msc = {
USB_CLASS_MSC,
usbh_msc_itf_init,
usbh_msc_itf_deinit,
usbh_msc_req,
usbh_msc_handle
};
/*!
\brief get MSC logic unit information
\param[in] uhost: pointer to USB host
\param[in] lun: logic unit number
\param[in] info: pointer to logic unit information
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_lun_info_get(usbh_host *uhost, uint8_t lun, msc_lun *info)
{
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
if(HOST_CLASS_HANDLER == uhost->cur_state) {
memcpy(info, &msc->unit[lun], sizeof(msc_lun));
return USBH_OK;
} else {
return USBH_FAIL;
}
}
/*!
\brief MSC read interface
\param[in] uhost: pointer to USB host
\param[in] lun: logic unit number
\param[in] address: address to be read
\param[in] pbuf: pointer to user buffer
\param[in] length: length to be read
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_read(usbh_host *uhost, \
uint8_t lun, \
uint32_t address, \
uint8_t *pbuf, \
uint32_t length)
{
uint32_t timeout = 0U;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
if((0U == udev->host.connect_status) || \
(HOST_CLASS_HANDLER != uhost->cur_state) || \
(MSC_IDLE != msc->unit[lun].state)) {
return USBH_FAIL;
}
msc->state = MSC_READ;
msc->unit[lun].state = MSC_READ;
msc->rw_lun = lun;
usbh_msc_read10(uhost, lun, pbuf, address, length);
timeout = uhost->control.timer;
while(USBH_BUSY == usbh_msc_rdwr_process(uhost, lun)) {
if(((uhost->control.timer - timeout) > (10000U * length)) || (0U == udev->host.connect_status)) {
msc->state = MSC_IDLE;
return USBH_FAIL;
}
}
msc->state = MSC_IDLE;
return USBH_OK;
}
/*!
\brief MSC write interface
\param[in] uhost: pointer to USB host
\param[in] lun: logic unit number
\param[in] address: address to be written
\param[in] pbuf: pointer to user buffer
\param[in] length: length to be written
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_write(usbh_host *uhost, \
uint8_t lun, \
uint32_t address, \
uint8_t *pbuf, \
uint32_t length)
{
uint32_t timeout = 0U;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
if((0U == udev->host.connect_status) || \
(HOST_CLASS_HANDLER != uhost->cur_state) || \
(MSC_IDLE != msc->unit[lun].state)) {
return USBH_FAIL;
}
msc->state = MSC_WRITE;
msc->unit[lun].state = MSC_WRITE;
msc->rw_lun = lun;
usbh_msc_write10(uhost, lun, pbuf, address, length);
timeout = uhost->control.timer;
while(USBH_BUSY == usbh_msc_rdwr_process(uhost, lun)) {
if(((uhost->control.timer - timeout) > (10000U * length)) || (0U == udev->host.connect_status)) {
msc->state = MSC_IDLE;
return USBH_FAIL;
}
}
msc->state = MSC_IDLE;
return USBH_OK;
}
/*!
\brief de-initialize interface by freeing host channels allocated to interface
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static void usbh_msc_itf_deinit(usbh_host *uhost)
{
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
if(msc->pipe_out) {
usb_pipe_halt(uhost->data, msc->pipe_out);
usbh_pipe_free(uhost->data, msc->pipe_out);
msc->pipe_out = 0U;
}
if(msc->pipe_in) {
usb_pipe_halt(uhost->data, msc->pipe_in);
usbh_pipe_free(uhost->data, msc->pipe_in);
msc->pipe_in = 0U;
}
}
/*!
\brief interface initialization for MSC class
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_itf_init(usbh_host *uhost)
{
usbh_status status = USBH_OK;
uint8_t interface = usbh_interface_find(&uhost->dev_prop, MSC_CLASS, USB_MSC_SUBCLASS_SCSI, MSC_PROTOCOL);
if(0xFFU == interface) {
uhost->usr_cb->dev_not_supported();
status = USBH_FAIL;
} else {
static usbh_msc_handler msc_handler;
memset((void *)&msc_handler, 0U, sizeof(usbh_msc_handler));
uhost->active_class->class_data = (void *)&msc_handler;
usbh_interface_select(&uhost->dev_prop, interface);
usb_desc_ep *ep_desc = &uhost->dev_prop.cfg_desc_set.itf_desc_set[interface][0].ep_desc[0];
if(ep_desc->bEndpointAddress & 0x80U) {
msc_handler.ep_in = ep_desc->bEndpointAddress;
msc_handler.ep_size_in = ep_desc->wMaxPacketSize;
} else {
msc_handler.ep_out = ep_desc->bEndpointAddress;
msc_handler.ep_size_out = ep_desc->wMaxPacketSize;
}
ep_desc = &uhost->dev_prop.cfg_desc_set.itf_desc_set[interface][0].ep_desc[1];
if(ep_desc->bEndpointAddress & 0x80U) {
msc_handler.ep_in = ep_desc->bEndpointAddress;
msc_handler.ep_size_in = ep_desc->wMaxPacketSize;
} else {
msc_handler.ep_out = ep_desc->bEndpointAddress;
msc_handler.ep_size_out = ep_desc->wMaxPacketSize;
}
msc_handler.state = MSC_INIT;
msc_handler.error = MSC_OK;
msc_handler.req_state = MSC_REQ_IDLE;
msc_handler.pipe_out = usbh_pipe_allocate(uhost->data, msc_handler.ep_out);
msc_handler.pipe_in = usbh_pipe_allocate(uhost->data, msc_handler.ep_in);
usbh_msc_bbb_init(uhost);
/* open the new channels */
usbh_pipe_create(uhost->data, \
&uhost->dev_prop, \
msc_handler.pipe_out, \
USB_EPTYPE_BULK, \
msc_handler.ep_size_out);
usbh_pipe_create(uhost->data, \
&uhost->dev_prop, \
msc_handler.pipe_in, \
USB_EPTYPE_BULK, \
msc_handler.ep_size_in);
usbh_pipe_toggle_set(uhost->data, msc_handler.pipe_out, 0U);
usbh_pipe_toggle_set(uhost->data, msc_handler.pipe_in, 0U);
}
return status;
}
/*!
\brief initialize the MSC state machine
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_req(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->req_state) {
case MSC_REQ_IDLE:
case MSC_REQ_GET_MAX_LUN:
/* issue Get_MaxLun request */
status = usbh_msc_maxlun_get(uhost, (uint8_t *)&msc->max_lun);
if(USBH_OK == status) {
msc->max_lun = ((uint8_t)msc->max_lun > MSC_MAX_SUPPORTED_LUN) ? MSC_MAX_SUPPORTED_LUN : (uint8_t)msc->max_lun + 1U;
for(uint8_t i = 0U; i < msc->max_lun; i++) {
msc->unit[i].prev_ready_state = USBH_FAIL;
msc->unit[i].state_changed = 0U;
}
} else {
if(USBH_NOT_SUPPORTED == status) {
msc->max_lun = 0U;
status = USBH_OK;
}
}
break;
case MSC_REQ_ERROR:
/* issue clearfeature request */
if(USBH_OK == usbh_clrfeature(uhost, 0x00U, uhost->control.pipe_out_num)) {
msc->req_state = msc->prev_req_state;
}
break;
default:
break;
}
return status;
}
/*!
\brief MSC state machine handler
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_handle(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
uint8_t scsi_status = USBH_BUSY;
uint8_t ready_status = USBH_BUSY;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->state) {
case MSC_INIT:
if(msc->cur_lun < msc->max_lun) {
msc->unit[msc->cur_lun].error = MSC_NOT_READY;
switch(msc->unit[msc->cur_lun].state) {
case MSC_INIT:
msc->unit[msc->cur_lun].state = MSC_READ_INQUIRY;
msc->timer = uhost->control.timer;
break;
case MSC_READ_INQUIRY:
scsi_status = usbh_msc_scsi_inquiry(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].inquiry);
if(USBH_OK == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_TEST_UNIT_READY;
} else if(USBH_FAIL == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_ERROR;
}
}
break;
case MSC_TEST_UNIT_READY:
/* issue SCSI command TestUnitReady */
ready_status = usbh_msc_test_unitready(uhost, msc->cur_lun);
if(USBH_OK == ready_status) {
if(USBH_OK != msc->unit[msc->cur_lun].prev_ready_state) {
msc->unit[msc->cur_lun].state_changed = 1U;
} else {
msc->unit[msc->cur_lun].state_changed = 0U;
}
msc->unit[msc->cur_lun].state = MSC_READ_CAPACITY10;
msc->unit[msc->cur_lun].error = MSC_OK;
msc->unit[msc->cur_lun].prev_ready_state = USBH_OK;
} else if(USBH_FAIL == ready_status) {
if(USBH_FAIL != msc->unit[msc->cur_lun].prev_ready_state) {
msc->unit[msc->cur_lun].state_changed = 1U;
} else {
msc->unit[msc->cur_lun].state_changed = 0U;
}
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
msc->unit[msc->cur_lun].error = MSC_NOT_READY;
msc->unit[msc->cur_lun].prev_ready_state = USBH_FAIL;
} else {
if(USBH_UNRECOVERED_ERROR == ready_status) {
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_ERROR;
}
}
break;
case MSC_READ_CAPACITY10:
/* issue READ_CAPACITY10 SCSI command */
scsi_status = usbh_msc_read_capacity10(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].capacity);
if(USBH_OK == scsi_status) {
if(1U == msc->unit[msc->cur_lun].state_changed) {
}
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_OK;
msc->cur_lun ++;
} else if(USBH_FAIL == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_ERROR;
}
}
break;
case MSC_REQUEST_SENSE:
/* issue RequestSense SCSI command for retrieving error code */
scsi_status = usbh_msc_request_sense(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].sense);
if(USBH_OK == scsi_status) {
if((UNIT_ATTENTION == msc->unit[msc->cur_lun].sense.SenseKey) || (NOT_READY == msc->unit[msc->cur_lun].sense.SenseKey)) {
if((uhost->control.timer - msc->timer) < 10000U) {
msc->unit[msc->cur_lun].state = MSC_TEST_UNIT_READY;
break;
}
}
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->cur_lun++;
} else if(USBH_FAIL == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_UNRECOVERED_ERROR;
} else {
if(MSC_UNRECOVERED_ERROR == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_ERROR;
}
}
break;
case MSC_UNRECOVERED_ERROR:
msc->cur_lun++;
break;
default:
break;
}
} else {
msc->cur_lun = 0U;
msc->state = MSC_IDLE;
}
break;
case MSC_IDLE:
uhost->usr_cb->dev_user_app();
status = USBH_OK;
break;
default:
break;
}
return status;
}
/*!
\brief get max lun of the mass storage device
\param[in] uhost: pointer to USB host
\param[in] maxlun: pointer to max lun
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_maxlun_get(usbh_host *uhost, uint8_t *maxlun)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_REQTYPE_CLASS | USB_RECPTYPE_ITF,
.bRequest = BBB_GET_MAX_LUN,
.wValue = 0U,
.wIndex = 0U,
.wLength = 1U
};
usbh_ctlstate_config(uhost, maxlun, 1U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief get max lun of the mass storage device
\param[in] uhost: pointer to USB host
\param[in] lun: logic unit number
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_rdwr_process(usbh_host *uhost, uint8_t lun)
{
usbh_status error = USBH_BUSY;
usbh_status scsi_status = USBH_BUSY;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
/* switch msc req state machine */
switch(msc->unit[lun].state) {
case MSC_READ:
scsi_status = usbh_msc_read10(uhost, lun, NULL, 0U, 0U);
if(USBH_OK == scsi_status) {
msc->unit[lun].state = MSC_IDLE;
error = USBH_OK;
} else if(USBH_FAIL == scsi_status) {
msc->unit[lun].state = MSC_REQUEST_SENSE;
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
error = USBH_FAIL;
}
}
break;
case MSC_WRITE:
scsi_status = usbh_msc_write10(uhost, lun, NULL, 0U, 0U);
if(USBH_OK == scsi_status) {
msc->unit[lun].state = MSC_IDLE;
error = USBH_OK;
} else if(USBH_FAIL == scsi_status) {
msc->unit[lun].state = MSC_REQUEST_SENSE;
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
error = USBH_FAIL;
}
}
break;
case MSC_REQUEST_SENSE:
scsi_status = usbh_msc_request_sense(uhost, lun, &msc->unit[lun].sense);
if(USBH_OK == scsi_status) {
msc->unit[lun].state = MSC_IDLE;
msc->unit[lun].error = MSC_ERROR;
error = USBH_FAIL;
}
if(USBH_FAIL == scsi_status) {
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
error = USBH_FAIL;
}
}
break;
default:
break;
}
return error;
}
@@ -0,0 +1,232 @@
/*!
\file usbh_msc_fatfs.c
\brief USB MSC host FATFS related functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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 "diskio.h"
#include "usbh_msc_core.h"
static volatile DSTATUS state = STA_NOINIT; /* disk status */
extern usbh_host usb_host_msc;
/*!
\brief initialize the disk drive
\param[in] drv: physical drive number (0)
\param[out] none
\retval operation status
*/
DSTATUS disk_initialize(BYTE drv)
{
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
if(udev->host.connect_status) {
state &= ~STA_NOINIT;
}
return state;
}
/*!
\brief get disk status
\param[in] drv: physical drive number (0)
\param[out] none
\retval operation status
*/
DSTATUS disk_status(BYTE drv)
{
if(drv) {
return STA_NOINIT; /* supports only single drive */
}
return state;
}
/*!
\brief read sectors
\param[in] drv: physical drive number (0)
\param[in] buff: pointer to the data buffer to store read data
\param[in] sector: start sector number (LBA)
\param[in] count: sector count (1..255)
\param[out] none
\retval operation status
*/
DRESULT disk_read(BYTE drv, BYTE *buff, DWORD sector, UINT count)
{
BYTE status = USBH_OK;
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
if(drv || (!count)) {
return RES_PARERR;
}
if(state & STA_NOINIT) {
return RES_NOTRDY;
}
if(udev->host.connect_status) {
do {
status = usbh_msc_read(&usb_host_msc, drv, sector, buff, count);
if(!udev->host.connect_status) {
return RES_ERROR;
}
} while(USBH_BUSY == status);
}
if(USBH_OK == status) {
return RES_OK;
}
return RES_ERROR;
}
#if _READONLY == 0U
/*!
\brief write sectors
\param[in] drv: physical drive number (0)
\param[in] buff: pointer to the data buffer to store read data
\param[in] sector: start sector number (LBA)
\param[in] count: sector count (1..255)
\param[out] none
\retval operation status
*/
DRESULT disk_write(BYTE drv, const BYTE *buff, DWORD sector, UINT count)
{
BYTE status = USBH_OK;
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
if((!count) || drv) {
return RES_PARERR;
}
if(state & STA_NOINIT) {
return RES_NOTRDY;
}
if(state & STA_PROTECT) {
return RES_WRPRT;
}
if(udev->host.connect_status) {
do {
status = usbh_msc_write(&usb_host_msc, drv, sector, (BYTE *)buff, count);
if(!udev->host.connect_status) {
return RES_ERROR;
}
} while(USBH_BUSY == status);
}
if(USBH_OK == status) {
return RES_OK;
}
return RES_ERROR;
}
#endif /* _READONLY == 0 */
/*!
\brief I/O control function
\param[in] drv: physical drive number (0)
\param[in] ctrl: control code
\param[in] buff: pointer to the data buffer to store read data
\param[out] none
\retval operation status
*/
DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void *buff)
{
DRESULT res = RES_OK;
msc_lun info;
if(drv) {
return RES_PARERR;
}
res = RES_ERROR;
if(state & STA_NOINIT) {
return RES_NOTRDY;
}
switch(ctrl) {
/* make sure that no pending write process */
case CTRL_SYNC:
res = RES_OK;
break;
/* get number of sectors on the disk (dword) */
case GET_SECTOR_COUNT:
if(USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, drv, &info)) {
*(DWORD *)buff = (DWORD)info.capacity.block_nbr;
res = RES_OK;
}
break;
/* get r/w sector size (word) */
case GET_SECTOR_SIZE:
if(USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, drv, &info)) {
*(WORD *)buff = (DWORD)info.capacity.block_size;
res = RES_OK;
}
break;
/* get erase block size in unit of sector (dword) */
case GET_BLOCK_SIZE:
*(DWORD *)buff = 512U;
break;
default:
res = RES_PARERR;
break;
}
return res;
}
/*!
\brief get fat time
\param[in] none
\param[out] none
\retval time value
*/
DWORD get_fattime(void)
{
return ((DWORD)(2019U - 1980U) << 25) /* year 2019 */
| ((DWORD)1U << 21) /* month 1 */
| ((DWORD)1U << 16) /* day 1 */
| ((DWORD)0U << 11) /* hour 0 */
| ((DWORD)0U << 5) /* min 0 */
| ((DWORD)0U >> 1); /* sec 0 */
}
@@ -0,0 +1,396 @@
/*!
\file usbh_msc_scsi.c
\brief USB MSC SCSI commands implementation
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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_msc_core.h"
#include "usbh_msc_scsi.h"
/*!
\brief send 'Inquiry' command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] inquiry: pointer to the inquiry structure
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_scsi_inquiry(usbh_host *uhost, uint8_t lun, scsi_std_inquiry_data *inquiry)
{
usbh_status error = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the cbw and relevant field*/
msc->bbb.cbw.field.dCBWDataTransferLength = STANDARD_INQUIRY_DATA_LEN;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_INQUIRY;
msc->bbb.cbw.field.CBWCB[1] = (lun << 5);
msc->bbb.cbw.field.CBWCB[4] = 0x24U;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
error = USBH_BUSY;
break;
case BBB_CMD_WAIT:
error = usbh_msc_bbb_process(uhost, lun);
if(USBH_OK == error) {
memset(inquiry, 0U, sizeof(scsi_std_inquiry_data));
/* assign inquiry data */
inquiry->device_type = msc->bbb.pbuf[0] & 0x1FU;
inquiry->peripheral_qualifier = msc->bbb.pbuf[0] >> 5;
if(0x80U == ((uint32_t)msc->bbb.pbuf[1] & 0x80U)) {
inquiry->removable_media = 1U;
} else {
inquiry->removable_media = 0U;
}
memcpy(inquiry->vendor_id, &msc->bbb.pbuf[8], 8U);
memcpy(inquiry->product_id, &msc->bbb.pbuf[16], 16U);
memcpy(inquiry->revision_id, &msc->bbb.pbuf[32], 4U);
}
break;
default:
break;
}
return error;
}
/*!
\brief send 'Test unit ready' command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_test_unitready(usbh_host *uhost, uint8_t lun)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = CBW_LENGTH_TEST_UNIT_READY;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_OUT;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_TEST_UNIT_READY;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
break;
default:
break;
}
return status;
}
/*!
\brief send the read capacity command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] capacity: pointer to SCSI capacity
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_read_capacity10(usbh_host *uhost, uint8_t lun, scsi_capacity *capacity)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = READ_CAPACITY10_DATA_LEN;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_READ_CAPACITY10;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
if(USBH_OK == status) {
capacity->block_nbr = msc->bbb.pbuf[3] | \
((uint32_t)msc->bbb.pbuf[2] << 8) | \
((uint32_t)msc->bbb.pbuf[1] << 16) | \
((uint32_t)msc->bbb.pbuf[0] << 24);
capacity->block_size = (uint16_t)(msc->bbb.pbuf[7] | ((uint32_t)msc->bbb.pbuf[6] << 8));
}
break;
default:
break;
}
return status;
}
/*!
\brief send the mode sense6 command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_mode_sense6(usbh_host *uhost, uint8_t lun)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = XFER_LEN_MODE_SENSE6;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_MODE_SENSE6;
msc->bbb.cbw.field.CBWCB[2] = MODE_SENSE_PAGE_CONTROL_FIELD | MODE_SENSE_PAGE_CODE;
msc->bbb.cbw.field.CBWCB[4] = XFER_LEN_MODE_SENSE6;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
if(USBH_OK == status) {
if(msc->bbb.data[2] & MASK_MODE_SENSE_WRITE_PROTECT) {
} else {
}
}
break;
default:
break;
}
return status;
}
/*!
\brief send the Request Sense command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] sense_data: pointer to sense data
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_request_sense(usbh_host *uhost, uint8_t lun, msc_scsi_sense *sense_data)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = ALLOCATION_LENGTH_REQUEST_SENSE;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_REQUEST_SENSE;
msc->bbb.cbw.field.CBWCB[1] = (lun << 5);
msc->bbb.cbw.field.CBWCB[4] = ALLOCATION_LENGTH_REQUEST_SENSE;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
if(USBH_OK == status) {
/* get sense data */
sense_data->SenseKey = msc->bbb.pbuf[2] & 0x0FU;
sense_data->ASC = msc->bbb.pbuf[12];
sense_data->ASCQ = msc->bbb.pbuf[13];
}
break;
default:
break;
}
return status;
}
/*!
\brief send the write10 command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] data_buf: data buffer contains the data to write
\param[in] addr: address to which the data will be written
\param[in] sector_num: number of sector to be written
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_write10(usbh_host *uhost, uint8_t lun, uint8_t *data_buf, uint32_t addr, uint32_t sector_num)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
msc->bbb.cbw.field.dCBWDataTransferLength = sector_num * msc->unit[lun].capacity.block_size;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_OUT;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_WRITE10;
/* logical block address */
msc->bbb.cbw.field.CBWCB[2] = (((uint8_t *)&addr)[3]);
msc->bbb.cbw.field.CBWCB[3] = (((uint8_t *)&addr)[2]);
msc->bbb.cbw.field.CBWCB[4] = (((uint8_t *)&addr)[1]);
msc->bbb.cbw.field.CBWCB[5] = (((uint8_t *)&addr)[0]);
/* transfer length */
msc->bbb.cbw.field.CBWCB[7] = (((uint8_t *)&sector_num)[1]);
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)&sector_num)[0]);
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = data_buf;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
break;
default:
break;
}
return status;
}
/*!
\brief send the read10 command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] data_buf: data buffer contains the data to write
\param[in] addr: address to which the data will be read
\param[in] sector_num: number of sector to be read
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_read10(usbh_host *uhost, uint8_t lun, uint8_t *data_buf, uint32_t addr, uint32_t sector_num)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = sector_num * msc->unit[lun].capacity.block_size;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_READ10;
/* logical block address */
msc->bbb.cbw.field.CBWCB[2] = (((uint8_t *)&addr)[3]);
msc->bbb.cbw.field.CBWCB[3] = (((uint8_t *)&addr)[2]);
msc->bbb.cbw.field.CBWCB[4] = (((uint8_t *)&addr)[1]);
msc->bbb.cbw.field.CBWCB[5] = (((uint8_t *)&addr)[0]);
/* transfer length */
msc->bbb.cbw.field.CBWCB[7] = (((uint8_t *)&sector_num)[1]);
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)&sector_num)[0]);
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = data_buf;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
break;
default:
break;
}
return status;
}
@@ -0,0 +1,262 @@
/*!
\file usbh_core.h
\brief USB host core state machine header file
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_CORE_H
#define USBH_CORE_H
#include "usbh_conf.h"
#include "drv_usb_host.h"
#define MSC_CLASS 0x08U /*!< USB MSC class */
#define HID_CLASS 0x03U /*!< USB HID class */
#define MSC_PROTOCOL 0x50U /*!< USB MSC protocol */
#define CBI_PROTOCOL 0x01U /*!< USB CBI protocol */
#define USBH_MAX_ERROR_COUNT 3U /*!< USBH maximum error count */
#define USBH_DEV_ADDR_DEFAULT 0U /*!< USBH device address default */
#define USBH_DEV_ADDR 1U /*!< USBH device address */
typedef enum {
USBH_OK = 0U, /*!< USB host OK status */
USBH_BUSY, /*!< USB host busy status */
USBH_FAIL, /*!< USB host fail status */
USBH_NOT_SUPPORTED, /*!< USB host not support status */
USBH_UNRECOVERED_ERROR, /*!< USB host unrecovered error status */
USBH_SPEED_UNKNOWN_ERROR, /*!< USB host speed unknown error status */
USBH_APPLY_DEINIT /*!< USB host apply deinit status */
} usbh_status;
/* USB host global operation state */
typedef enum {
HOST_DEFAULT = 0U, /*!< USB host global operation default state */
HOST_DETECT_DEV_SPEED, /*!< USB host global operation detect device speed state */
HOST_DEV_CONNECT, /*!< USB host global operation device connect state */
HOST_DEV_DETACHED, /*!< USB host global operation device detached state */
HOST_DEV_ENUM, /*!< USB host global operation device enumeration state */
HOST_PWR_FEATURE_SET, /*!< USB host global operation setting power feature state */
HOST_CLASS_CHECK, /*!< USB host global operation class check state */
HOST_CLASS_ENUM, /*!< USB host global operation class enumeration state */
HOST_CLASS_HANDLER, /*!< USB host global operation class handler state */
HOST_USER_INPUT, /*!< USB host global operation user input state */
HOST_SUSPEND, /*!< USB host global operation suspend state */
HOST_WAKEUP, /*!< USB host global operation wakeup state */
HOST_ERROR /*!< USB host global operation error state */
} usb_host_state;
/* USB host enumeration state */
typedef enum {
ENUM_DEFAULT = 0U, /*!< USB host enumeration default state */
ENUM_GET_DEV_DESC, /*!< USB host enumeration get device descriptor state */
ENUM_SET_ADDR, /*!< USB host enumeration set address state */
ENUM_GET_CFG_DESC, /*!< USB host enumeration get configuration descriptor state */
ENUM_GET_CFG_DESC_SET, /*!< USB host enumeration set configuration descriptor state */
ENUM_GET_STR_DESC, /*!< USB host enumeration get string descriptor state */
#ifdef USB_MTP
ENUM_GET_MTP_STR, /*!< USB host enumeration get MTP string state */
#endif /* USB_MTP */
ENUM_SET_CONFIGURATION, /*!< USB host enumeration set configuration state */
ENUM_DEV_CONFIGURED /*!< USB host enumeration device configured state */
} usbh_enum_state;
/* USB host control transfer state */
typedef enum {
CTL_IDLE = 0U, /*!< USB host control transfer idle state */
CTL_SETUP, /*!< USB host control transfer SETUP state */
CTL_SETUP_WAIT, /*!< USB host control transfer SETUP wait state */
CTL_DATA_IN, /*!< USB host control transfer data IN state */
CTL_DATA_IN_WAIT, /*!< USB host control transfer data IN wait state */
CTL_DATA_OUT, /*!< USB host control transfer data OUT state */
CTL_DATA_OUT_WAIT, /*!< USB host control transfer data OUT wait state */
CTL_STATUS_IN, /*!< USB host control transfer status IN state */
CTL_STATUS_IN_WAIT, /*!< USB host control transfer status IN wait state */
CTL_STATUS_OUT, /*!< USB host control transfer status OUT state */
CTL_STATUS_OUT_WAIT, /*!< USB host control transfer status OUT wait state */
CTL_ERROR, /*!< USB host control transfer error state */
CTL_FINISH /*!< USB host control transfer finish state */
} usbh_ctl_state;
/* user action state */
typedef enum {
USR_IN_NO_RESP = 0U, /*!< user action IN no response state */
USR_IN_RESP_OK = 1U /*!< user action IN response OK state */
} usbh_user_status;
/* USB host wakeup mode */
typedef enum {
NORMAL_WORK = 0U,
GENERAL_WAKEUP = 1U,
REMOTE_WAKEUP = 2
} usbh_wakeup_mode;
/* control transfer information */
typedef struct _usbh_control {
uint8_t pipe_in_num; /*!< the number of IN pipe*/
uint8_t pipe_out_num; /*!< the number of OUT pipe */
uint8_t max_len; /*!< maximum length of control transfer */
uint8_t error_count; /*!< the count of control error */
uint8_t *buf; /*!< control transfer buffer */
uint16_t ctl_len; /*!< the length of control transfer */
__IO uint32_t timer; /*!< control transfer timer */
usb_setup setup; /*!< control transfer SETUP packet */
usbh_ctl_state ctl_state; /*!< host control transfer state */
} usbh_control;
/* USB interface descriptor set */
typedef struct _usb_desc_itf_set {
usb_desc_itf itf_desc; /*!< USB interface descriptor */
usb_desc_ep ep_desc[USBH_MAX_EP_NUM]; /*!< USB endpoint descriptor */
} usb_desc_itf_set;
/* USB configure descriptor set */
typedef struct _usb_desc_cfg_set {
usb_desc_config cfg_desc; /*!< USB configuration descriptor */
usb_desc_itf_set itf_desc_set[USBH_MAX_INTERFACES_NUM][USBH_MAX_ALT_SETTING]; /*!< USB interface descriptor set*/
} usb_desc_cfg_set;
/* USB device property */
typedef struct {
uint8_t data[USBH_DATA_BUF_MAX_LEN]; /*!< if DMA is used, the data array must be located in the first position */
uint8_t cur_itf; /*!< USB device current interface */
uint8_t addr; /*!< USB device address */
uint32_t speed; /*!< USB device speed */
usb_desc_dev dev_desc; /*!< USB device descriptor */
usb_desc_cfg_set cfg_desc_set; /*!< USB interface descriptor set */
#if (1U == USBH_CFG_DESC_KEEP)
uint8_t cfgdesc_rawdata[USBH_CFGSET_MAX_LEN]; /*!< USB configuration descriptor raw data*/
#endif /* (1U == USBH_CFG_DESC_KEEP) */
} usb_dev_prop;
struct _usbh_host;
/* device class callbacks */
typedef struct {
uint8_t class_code; /*!< USB class type */
usbh_status (*class_init)(struct _usbh_host *phost);
void (*class_deinit)(struct _usbh_host *phost);
usbh_status (*class_requests)(struct _usbh_host *phost);
usbh_status (*class_machine)(struct _usbh_host *phost);
usbh_status (*class_sof)(struct _usbh_host *uhost);
void *class_data; /*!< USB class data pointer */
} usbh_class;
/* user callbacks */
typedef struct {
void (*dev_init)(void);
void (*dev_deinit)(void);
void (*dev_attach)(void);
void (*dev_reset)(void);
void (*dev_detach)(void);
void (*dev_over_currented)(void);
void (*dev_speed_detected)(uint32_t dev_speed);
void (*dev_devdesc_assigned)(void *dev_desc);
void (*dev_address_set)(void);
void (*dev_cfgdesc_assigned)(usb_desc_config *cfg_desc, \
usb_desc_itf *itf_desc, \
usb_desc_ep *ep_desc);
void (*dev_mfc_str)(void *mfc_str);
void (*dev_prod_str)(void *prod_str);
void (*dev_seral_str)(void *serial_str);
void (*dev_enumerated)(void);
usbh_user_status (*dev_user_input)(void);
int (*dev_user_app)(void);
void (*dev_not_supported)(void);
void (*dev_error)(void);
} usbh_user_cb;
/* host information */
typedef struct _usbh_host {
usb_host_state cur_state; /*!< host state machine value */
usb_host_state backup_state; /*!< backup of previous state machine value */
usbh_enum_state enum_state; /*!< enumeration state machine */
usbh_control control; /*!< USB host control state machine */
usb_dev_prop dev_prop; /*!< USB device property */
usbh_class *uclass[USBH_MAX_SUPPORTED_CLASS]; /*!< USB host supported class */
usbh_class *active_class; /*!< USB active class */
usbh_user_cb *usr_cb; /*!< USB user callback */
uint8_t class_num; /*!< USB class number */
void *data; /*!< used for... */
uint8_t suspend_flag; /*!< host suspend flag */
uint8_t dev_supp_remote_wkup; /*!< record device remote wakeup function */
usbh_wakeup_mode wakeup_mode; /*!< record wakeup mode */
} usbh_host;
/*!
\brief get USB URB state
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval URB state
*/
static inline usb_urb_state usbh_urbstate_get(usb_core_driver *udev, uint8_t pp_num)
{
return udev->host.pipe[pp_num].urb_state;
}
/*!
\brief get USB transfer data count
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval transfer data count
*/
static inline uint32_t usbh_xfercount_get(usb_core_driver *udev, uint8_t pp_num)
{
return udev->host.backup_xfercount[pp_num];
}
/* function declarations */
/* USB host stack initializations */
void usbh_init(usbh_host *uhost, usb_core_driver *udev, usb_core_enum usb_core, usbh_user_cb *user_cb);
/* USB host register device class */
usbh_status usbh_class_register(usbh_host *uhost, usbh_class *puclass);
/* de-initialize USB host */
usbh_status usbh_deinit(usbh_host *uhost);
/* USB host core main state machine process */
void usbh_core_task(usbh_host *uhost);
/* handle the error on USB host side */
void usbh_error_handler(usbh_host *uhost, usbh_status err_type);
#endif /* USBH_CORE_H */
@@ -0,0 +1,70 @@
/*!
\file usbh_enum.h
\brief USB host mode USB enumeration header file
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_ENUM_H
#define USBH_ENUM_H
#include "usbh_core.h"
/* function declarations */
/* configure USB control status parameters */
void usbh_ctlstate_config(usbh_host *uhost, uint8_t *buf, uint16_t len);
/* get device descriptor from the USB device */
usbh_status usbh_devdesc_get(usbh_host *uhost, uint8_t len);
/* get configuration descriptor from the USB device */
usbh_status usbh_cfgdesc_get(usbh_host *uhost, uint16_t len);
/* get string descriptor from the USB device */
usbh_status usbh_strdesc_get(usbh_host *uhost,uint8_t str_index, uint8_t *buf, uint16_t len);
/* set the address to the connected device */
usbh_status usbh_setaddress(usbh_host *uhost, uint8_t dev_addr);
/* set the configuration value to the connected device */
usbh_status usbh_setcfg(usbh_host *uhost, uint16_t config);
/* set the interface value to the connected device */
usbh_status usbh_setinterface(usbh_host *uhost, uint8_t itf_num, uint8_t alter_setting);
/* set or enable a specific device feature */
usbh_status usbh_setdevfeature(usbh_host *uhost, uint8_t feature_selector, uint16_t windex);
/* clear or disable a specific device feature */
usbh_status usbh_clrdevfeature(usbh_host *uhost, uint8_t feature_selector, uint16_t windex);
/* clear or disable a specific feature */
usbh_status usbh_clrfeature(usbh_host *uhost, uint8_t ep_addr, uint8_t pp_num);
/* get the next descriptor header */
usb_desc_header *usbh_nextdesc_get(uint8_t *pbuf, uint16_t *ptr);
/* select an interface */
usbh_status usbh_interface_select(usb_dev_prop *udev, uint8_t interface);
/* find the interface index for a specific class */
uint8_t usbh_interface_find(usb_dev_prop *udev, uint8_t main_class, uint8_t sub_class, uint8_t protocol);
/* find the interface index for a specific class interface and alternate setting number */
uint8_t usbh_interfaceindex_find(usb_dev_prop *udev, uint8_t interface_number, uint8_t alt_settings);
#endif /* USBH_ENUM_H */
@@ -0,0 +1,102 @@
/*!
\file usbh_pipe.h
\brief USB host mode pipe header file
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_PIPE_H
#define USBH_PIPE_H
#include "usbh_core.h"
/* host pipe maximum */
#define HP_MAX 8U
/* host pipe status */
#define HP_OK 0x0000U /*!< host pipe status */
#define HP_USED 0x8000U /*!< host pipe used status */
#define HP_ERROR 0xFFFFU /*!< host pipe error status */
#define HP_USED_MASK 0x7FFFU /*!< host pipe used mask */
/*!
\brief set toggle for a pipe
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[in] toggle: toggle (0/1)
\param[out] none
\retval none
*/
__STATIC_INLINE void usbh_pipe_toggle_set(usb_core_driver *udev, uint8_t pp_num, uint8_t toggle)
{
if(udev->host.pipe[pp_num].ep.dir) {
udev->host.pipe[pp_num].data_toggle_in = toggle;
} else {
udev->host.pipe[pp_num].data_toggle_out = toggle;
}
}
/*!
\brief get toggle flag of pipe
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval toggle flag
*/
__STATIC_INLINE uint8_t usbh_pipe_toggle_get(usb_core_driver *udev, uint8_t pp_num)
{
if(udev->host.pipe[pp_num].ep.dir) {
return udev->host.pipe[pp_num].data_toggle_in;
} else {
return udev->host.pipe[pp_num].data_toggle_out;
}
}
/* function declarations */
/* create a pipe */
uint8_t usbh_pipe_create(usb_core_driver *udev, \
usb_dev_prop *dev, \
uint8_t pp_num, \
uint8_t ep_type, \
uint16_t ep_mpl);
/* modify a pipe */
uint8_t usbh_pipe_update(usb_core_driver *udev, \
uint8_t pp_num, \
uint8_t dev_addr, \
uint32_t dev_speed, \
uint16_t ep_mpl);
/* allocate a new pipe */
uint8_t usbh_pipe_allocate(usb_core_driver *udev, uint8_t ep_addr);
/* free a pipe */
uint8_t usbh_pipe_free(usb_core_driver *udev, uint8_t pp_num);
/* delete all USB host pipe */
uint8_t usbh_pipe_delete(usb_core_driver *udev);
#endif /* USBH_PIPE_H */
@@ -0,0 +1,50 @@
/*!
\file usbh_transc.h
\brief USB host mode transactions header file
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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.
*/
#ifndef USBH_TRANSC_H
#define USBH_TRANSC_H
#include "usbh_core.h"
/* function declarations */
/* send the SETUP packet to the USB device */
usbh_status usbh_ctlsetup_send(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num);
/* send a data packet to the USB device */
usbh_status usbh_data_send(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num, uint16_t len);
/* receive a data packet from the USB device */
usbh_status usbh_data_recev(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num, uint16_t len);
/* USB control transfer handler */
usbh_status usbh_ctl_handler(usbh_host *uhost);
#endif /* USBH_TRANSC_H */
@@ -0,0 +1,659 @@
/*!
\file usbh_core.c
\brief USB host core state machine driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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 "drv_usb_hw.h"
#include "usbh_pipe.h"
#include "usbh_enum.h"
#include "usbh_core.h"
#include "drv_usbh_int.h"
/* local function prototypes ('static') */
static uint8_t usb_ev_sof(usbh_host *uhost);
static uint8_t usb_ev_connect(usbh_host *uhost);
static uint8_t usb_ev_disconnect(usbh_host *uhost);
static usbh_status usbh_enum_task(usbh_host *uhost);
#if USBFS_LOW_POWER || USBHS_LOW_POWER
static void usb_hwp_suspend(usb_core_driver *udev);
static void usb_hwp_resume(usb_core_driver *udev);
#endif /* USBFS_LOW_POWER || USBHS_LOW_POWER */
usbh_ev_cb usbh_int_op = {
usb_ev_connect,
usb_ev_disconnect,
usb_ev_sof
};
usbh_ev_cb *usbh_int_fop = &usbh_int_op;
/*!
\brief USB host stack initializations
\param[in] uhost: pointer to USB host
\param[in] udev: pointer to USB device instance
\param[in] usb_core: USB core type
\param[in] user_cb: pointer to user callback
\param[out] none
\retval none
*/
void usbh_init(usbh_host *uhost, usb_core_driver *udev, usb_core_enum usb_core, usbh_user_cb *user_cb)
{
/* link driver to the stack */
udev->host.data = (void *)uhost;
uhost->data = (void *)udev;
/* host deinitialization */
usbh_deinit(uhost);
uhost->usr_cb = user_cb;
udev->host.connect_status = 0U;
for(uint8_t i = 0U; i < USBFS_MAX_TX_FIFOS; i++) {
udev->host.pipe[i].err_count = 0U;
udev->host.pipe[i].pp_status = PIPE_IDLE;
udev->host.backup_xfercount[i] = 0U;
}
udev->host.pipe[0].ep.mps = 8U;
usb_basic_init(&udev->bp, &udev->regs, usb_core);
#ifndef DUAL_ROLE_MODE_ENABLED
usb_globalint_disable(&udev->regs);
usb_core_init(udev->bp, &udev->regs);
#ifndef USE_OTG_MODE
usb_curmode_set(&udev->regs, HOST_MODE);
#endif /* USE_OTG_MODE */
usb_host_init(udev);
usb_globalint_enable(&udev->regs);
#endif /* DUAL_ROLE_MODE_ENABLED */
/* upon initialize call usr call back */
uhost->usr_cb->dev_init();
}
/*!
\brief USB host register device class
\param[in] uhost: pointer to USB host instance
\param[in] uclass: pointer to USB device class
\param[out] none
\retval operation status
*/
usbh_status usbh_class_register(usbh_host *uhost, usbh_class *uclass)
{
usbh_status status = USBH_OK;
if(NULL != uclass) {
if(uhost->class_num < USBH_MAX_SUPPORTED_CLASS) {
uhost->uclass[uhost->class_num++] = uclass;
} else {
status = USBH_FAIL;
}
} else {
status = USBH_FAIL;
}
return status;
}
/*!
\brief deinitialize USB host
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
usbh_status usbh_deinit(usbh_host *uhost)
{
usb_core_driver *udev = (usb_core_driver *)uhost->data;
/* software initialize */
uhost->cur_state = HOST_DEFAULT;
uhost->backup_state = HOST_DEFAULT;
uhost->enum_state = ENUM_DEFAULT;
uhost->control.ctl_state = CTL_IDLE;
uhost->control.max_len = USB_FS_EP0_MAX_LEN;
uhost->dev_prop.addr = USBH_DEV_ADDR_DEFAULT;
uhost->dev_prop.speed = PORT_SPEED_FULL;
uhost->dev_prop.cur_itf = 0xFFU;
usbh_pipe_free(udev, uhost->control.pipe_in_num);
usbh_pipe_free(udev, uhost->control.pipe_out_num);
return USBH_OK;
}
/*!
\brief USB host core main state machine process
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
void usbh_core_task(usbh_host *uhost)
{
volatile usbh_status status = USBH_FAIL;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
/* check for host port events */
if(((0U == udev->host.connect_status) || (0U == udev->host.port_enabled)) && (HOST_DEFAULT != uhost->cur_state)) {
if(HOST_DEV_DETACHED != uhost->cur_state) {
uhost->cur_state = HOST_DEV_DETACHED;
}
}
switch(uhost->cur_state) {
case HOST_DEFAULT:
if(udev->host.connect_status) {
uhost->cur_state = HOST_DETECT_DEV_SPEED;
usb_mdelay(100U);
usb_port_reset(udev);
uhost->usr_cb->dev_reset();
}
break;
case HOST_DETECT_DEV_SPEED:
if(udev->host.port_enabled) {
uhost->cur_state = HOST_DEV_CONNECT;
uhost->dev_prop.speed = usb_curspeed_get(udev);
uhost->usr_cb->dev_speed_detected(uhost->dev_prop.speed);
usb_mdelay(50U);
}
break;
case HOST_DEV_CONNECT:
uhost->usr_cb->dev_attach();
uhost->control.pipe_out_num = usbh_pipe_allocate(udev, 0x00U);
uhost->control.pipe_in_num = usbh_pipe_allocate(udev, 0x80U);
/* open IN control pipe */
usbh_pipe_create(udev, \
&uhost->dev_prop, \
uhost->control.pipe_in_num, \
USB_EPTYPE_CTRL, \
(uint16_t)uhost->control.max_len);
/* open OUT control pipe */
usbh_pipe_create(udev, \
&uhost->dev_prop, \
uhost->control.pipe_out_num, \
USB_EPTYPE_CTRL, \
(uint16_t)uhost->control.max_len);
uhost->cur_state = HOST_DEV_ENUM;
break;
case HOST_DEV_ENUM:
/* check for enumeration status */
if(USBH_OK == usbh_enum_task(uhost)) {
/* the function shall return USBH_OK when full enumeration is complete */
/* user callback for end of device basic enumeration */
uhost->usr_cb->dev_enumerated();
#if USBFS_LOW_POWER || USBHS_LOW_POWER
uhost->cur_state = HOST_SUSPEND;
/* judge device remote wakeup function */
if((uhost->dev_prop.cfg_desc_set.cfg_desc.bmAttributes) & (1U << 5)) {
uhost->dev_supp_remote_wkup = 1U;
} else {
uhost->dev_supp_remote_wkup = 0U;
}
#else
uhost->cur_state = HOST_PWR_FEATURE_SET;
#endif /* USBFS_LOW_POWER || USBHS_LOW_POWER */
}
break;
case HOST_PWR_FEATURE_SET:
if((uhost->dev_prop.cfg_desc_set.cfg_desc.bmAttributes) & (1U << 5)) {
if(USBH_OK == usbh_setdevfeature(uhost, FEATURE_SELECTOR_REMOTEWAKEUP, 0U)) {
uhost->cur_state = HOST_CLASS_CHECK;
}
} else {
uhost->cur_state = HOST_CLASS_CHECK;
}
break;
case HOST_CLASS_CHECK:
if(0U == uhost->class_num) {
uhost->cur_state = HOST_ERROR;
} else {
uhost->active_class = NULL;
uint8_t itf_class = uhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].itf_desc.bInterfaceClass;
for(uint8_t index = 0U; index < uhost->class_num; index++) {
if((uhost->uclass[index]->class_code == itf_class) || (0xFFU == itf_class)) {
uhost->active_class = uhost->uclass[index];
}
}
if(NULL != uhost->active_class) {
uhost->cur_state = HOST_USER_INPUT;
} else {
uhost->cur_state = HOST_ERROR;
}
}
break;
case HOST_USER_INPUT:
/* the function should return user response true to move to class state */
if(USR_IN_RESP_OK == uhost->usr_cb->dev_user_input()) {
if((USBH_OK == uhost->active_class->class_init(uhost))) {
uhost->cur_state = HOST_CLASS_ENUM;
}
}
break;
#if USBFS_LOW_POWER || USBHS_LOW_POWER
case HOST_SUSPEND:
if(uhost->dev_supp_remote_wkup) {
/* send set feature command*/
if(USBH_OK == usbh_setdevfeature(uhost, FEATURE_SELECTOR_REMOTEWAKEUP, 0U)) {
usb_hwp_suspend(udev);
usb_mdelay(20U);
uhost->suspend_flag = 1U;
uhost->usr_cb->dev_user_input();
/* MCU enter deep-sleep*/
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_DISABLE, WFI_CMD);
uhost->cur_state = HOST_WAKEUP;
}
} else {
/* host suspend */
usb_hwp_suspend(udev);
usb_mdelay(20U);
uhost->suspend_flag = 1U;
uhost->usr_cb->dev_user_input();
/* MCU enter deep-sleep */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_DISABLE, WFI_CMD);
uhost->cur_state = HOST_WAKEUP;
}
break;
case HOST_WAKEUP:
/* judge suspend status */
if(0U == uhost->suspend_flag) {
usb_hwp_resume(udev);
usb_mdelay(500U);
if(uhost->dev_supp_remote_wkup) {
if(USBH_OK == usbh_clrdevfeature(uhost, FEATURE_SELECTOR_DEV, 0U)) {
/* user callback for initialization */
uhost->usr_cb->dev_init();
uhost->cur_state = HOST_CLASS_CHECK;
}
} else {
uhost->cur_state = HOST_CLASS_CHECK;
}
}
break;
#endif /* USBFS_LOW_POWER || USBHS_LOW_POWER */
case HOST_CLASS_ENUM:
/* process class standard control requests state machine */
status = uhost->active_class->class_requests(uhost);
if(USBH_OK == status) {
uhost->cur_state = HOST_CLASS_HANDLER;
} else {
usbh_error_handler(uhost, status);
}
break;
case HOST_CLASS_HANDLER:
/* process class state machine */
status = uhost->active_class->class_machine(uhost);
usbh_error_handler(uhost, status);
break;
case HOST_ERROR:
/* deinitialize host for new enumeration */
usbh_deinit(uhost);
uhost->usr_cb->dev_deinit();
if(NULL != uhost->active_class) {
uhost->active_class->class_deinit(uhost);
}
break;
case HOST_DEV_DETACHED:
/* manage user disconnect operations*/
uhost->usr_cb->dev_detach();
/* re-initialize host for new enumeration */
usbh_deinit(uhost);
uhost->usr_cb->dev_deinit();
if(NULL != uhost->active_class) {
uhost->active_class->class_deinit(uhost);
}
usbh_pipe_delete(udev);
uhost->cur_state = HOST_DEFAULT;
break;
default:
break;
}
}
/*!
\brief handle the error on USB host side
\param[in] uhost: pointer to USB host
\param[in] err_type: type of error or busy/OK state
\param[out] none
\retval none
*/
void usbh_error_handler(usbh_host *uhost, usbh_status err_type)
{
/* error unrecovered or not supported device speed */
if((USBH_SPEED_UNKNOWN_ERROR == err_type) || (USBH_UNRECOVERED_ERROR == err_type)) {
uhost->usr_cb->dev_error();
uhost->cur_state = HOST_ERROR;
} else if(USBH_APPLY_DEINIT == err_type) {
uhost->cur_state = HOST_ERROR;
/* user callback for initialization */
uhost->usr_cb->dev_init();
} else {
/* no operation */
}
}
/*!
\brief USB SOF event function from the interrupt
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static uint8_t usb_ev_sof(usbh_host *uhost)
{
/* update timer variable */
uhost->control.timer++;
/* this callback could be used to implement a scheduler process */
if(NULL != uhost->active_class) {
if(NULL != uhost->active_class->class_sof) {
uhost->active_class->class_sof(uhost);
}
}
return 0U;
}
/*!
\brief USB connect event function from the interrupt
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static uint8_t usb_ev_connect(usbh_host *uhost)
{
usb_core_driver *udev = (usb_core_driver *)uhost->data;
udev->host.connect_status = 1U;
return 0U;
}
/*!
\brief USB disconnect event function from the interrupt
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static uint8_t usb_ev_disconnect(usbh_host *uhost)
{
usb_core_driver *udev = (usb_core_driver *)uhost->data;
udev->host.connect_status = 0U;
return 0U;
}
/*!
\brief handle the USB enumeration task
\param[in] uhost: pointer to host
\param[out] none
\retval none
*/
static usbh_status usbh_enum_task(usbh_host *uhost)
{
uint8_t str_buf[512];
usbh_status status = USBH_BUSY;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
static uint8_t index_mfc_str = 0U, index_prod_str = 0U, index_serial_str = 0U;
switch(uhost->enum_state) {
case ENUM_DEFAULT:
/* get device descriptor for only 1st 8 bytes : to get ep0 max packet size */
if(USBH_OK == usbh_devdesc_get(uhost, 8U)) {
uhost->control.max_len = uhost->dev_prop.dev_desc.bMaxPacketSize0;
/* modify control channels configuration for maximum packet size */
usbh_pipe_update(udev, \
uhost->control.pipe_out_num, \
0U, 0U, \
(uint16_t)uhost->control.max_len);
usbh_pipe_update(udev, \
uhost->control.pipe_in_num, \
0U, 0U, \
(uint16_t)uhost->control.max_len);
uhost->enum_state = ENUM_GET_DEV_DESC;
}
break;
case ENUM_GET_DEV_DESC:
/* get full device descriptor */
if(USBH_OK == usbh_devdesc_get(uhost, USB_DEV_DESC_LEN)) {
uhost->usr_cb->dev_devdesc_assigned(&uhost->dev_prop.dev_desc);
index_mfc_str = uhost->dev_prop.dev_desc.iManufacturer;
index_prod_str = uhost->dev_prop.dev_desc.iProduct;
index_serial_str = uhost->dev_prop.dev_desc.iSerialNumber;
uhost->enum_state = ENUM_SET_ADDR;
}
break;
case ENUM_SET_ADDR:
/* set address */
if(USBH_OK == usbh_setaddress(uhost, USBH_DEV_ADDR)) {
usb_mdelay(2U);
uhost->dev_prop.addr = USBH_DEV_ADDR;
/* user callback for device address assigned */
uhost->usr_cb->dev_address_set();
/* modify control channels to update device address */
usbh_pipe_update(udev, \
uhost->control.pipe_in_num, \
uhost->dev_prop.addr, \
0U, 0U);
usbh_pipe_update(udev, \
uhost->control.pipe_out_num, \
uhost->dev_prop.addr, \
0U, 0U);
uhost->enum_state = ENUM_GET_CFG_DESC;
}
break;
case ENUM_GET_CFG_DESC:
/* get standard configuration descriptor */
if(USBH_OK == usbh_cfgdesc_get(uhost, USB_CFG_DESC_LEN)) {
uhost->enum_state = ENUM_GET_CFG_DESC_SET;
}
break;
case ENUM_GET_CFG_DESC_SET:
/* get full configure descriptor (config, interface, endpoints) */
if(USBH_OK == usbh_cfgdesc_get(uhost, uhost->dev_prop.cfg_desc_set.cfg_desc.wTotalLength)) {
/* user callback for configuration descriptors available */
uhost->usr_cb->dev_cfgdesc_assigned(&uhost->dev_prop.cfg_desc_set.cfg_desc, \
&uhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].itf_desc, \
&uhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].ep_desc[0]);
uhost->enum_state = ENUM_GET_STR_DESC;
}
break;
case ENUM_GET_STR_DESC:
if(index_mfc_str) {
if(USBH_OK == usbh_strdesc_get(uhost, \
uhost->dev_prop.dev_desc.iManufacturer, \
str_buf, \
0xFFU)) {
/* user callback for manufacturing string */
uhost->usr_cb->dev_mfc_str(str_buf);
index_mfc_str = 0U;
}
} else {
if(index_prod_str) {
/* check that product string is available */
if(USBH_OK == usbh_strdesc_get(uhost, \
uhost->dev_prop.dev_desc.iProduct, \
str_buf, \
0xFFU)) {
uhost->usr_cb->dev_prod_str(str_buf);
index_prod_str = 0U;
}
} else {
if(index_serial_str) {
if(USBH_OK == usbh_strdesc_get(uhost, \
uhost->dev_prop.dev_desc.iSerialNumber, \
str_buf, \
0xFFU)) {
uhost->usr_cb->dev_seral_str(str_buf);
uhost->enum_state = ENUM_SET_CONFIGURATION;
index_serial_str = 0U;
}
} else {
uhost->enum_state = ENUM_SET_CONFIGURATION;
}
}
}
break;
case ENUM_SET_CONFIGURATION:
if(USBH_OK == usbh_setcfg(uhost, (uint16_t)uhost->dev_prop.cfg_desc_set.cfg_desc.bConfigurationValue)) {
uhost->enum_state = ENUM_DEV_CONFIGURED;
}
break;
case ENUM_DEV_CONFIGURED:
status = USBH_OK;
break;
default:
break;
}
return status;
}
#if USBFS_LOW_POWER || USBHS_LOW_POWER
/*!
\brief handles the USB resume from suspend mode
\param[in] udev: pointer to selected USB device
\param[out] none
\retval none
*/
static void usb_hwp_resume(usb_core_driver *udev)
{
__IO uint32_t hprt = 0U;
/* switch-on the clocks */
*udev->regs.PWRCLKCTL &= ~PWRCLKCTL_SUCLK;
*udev->regs.PWRCLKCTL &= ~PWRCLKCTL_SHCLK;
hprt = usb_port_read(udev);
hprt &= ~HPCS_PSP;
hprt |= HPCS_PREM;
*udev->regs.HPCS = hprt;
usb_mdelay(20U);
hprt &= ~HPCS_PREM;
*udev->regs.HPCS = hprt;
}
/*!
\brief handles the USB enter to suspend mode
\param[in] udev: pointer to selected USB device
\param[out] none
\retval none
*/
static void usb_hwp_suspend(usb_core_driver *udev)
{
__IO uint32_t hprt = 0U;
hprt = usb_port_read(udev);
hprt |= HPCS_PSP;
*udev->regs.HPCS = hprt;
/* switch-off the clocks */
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SUCLK;
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SHCLK;
}
#endif /* USBFS_LOW_POWER || USBHS_LOW_POWER */
@@ -0,0 +1,693 @@
/*!
\file usbh_enum.c
\brief USB host mode enumeration driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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_pipe.h"
#include "usbh_transc.h"
#include "usbh_enum.h"
/* local function prototypes ('static') */
static void usbh_devdesc_parse(usb_desc_dev *dev_desc, uint8_t *buf, uint16_t len);
static void usbh_cfgdesc_parse(usb_desc_config *cfg_desc, uint8_t *buf);
static void usbh_cfgset_parse(usb_dev_prop *udev, uint8_t *buf);
static void usbh_itfdesc_parse(usb_desc_itf *itf_desc, uint8_t *buf);
static void usbh_epdesc_parse(usb_desc_ep *ep_desc, uint8_t *buf);
static void usbh_strdesc_parse(uint8_t *psrc, uint8_t *pdest, uint16_t len);
/*!
\brief configure USB control status parameters
\param[in] uhost: pointer to USB host
\param[in] buf: control transfer data buffer pointer
\param[in] len: length of the data buffer
\param[out] none
\retval none
*/
void usbh_ctlstate_config(usbh_host *uhost, uint8_t *buf, uint16_t len)
{
/* prepare the transactions */
uhost->control.buf = buf;
uhost->control.ctl_len = len;
uhost->control.ctl_state = CTL_SETUP;
}
/*!
\brief get device descriptor from the USB device
\param[in] uhost: pointer to USB host
\param[in] len: length of the descriptor
\param[out] none
\retval operation status
*/
usbh_status usbh_devdesc_get(usbh_host *uhost, uint8_t len)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_DEV),
.wIndex = 0U,
.wLength = len
};
usbh_ctlstate_config(uhost, uhost->dev_prop.data, (uint16_t)len);
}
status = usbh_ctl_handler(uhost);
if(USBH_OK == status) {
/* commands successfully sent and response received */
usbh_devdesc_parse(&uhost->dev_prop.dev_desc, uhost->dev_prop.data, (uint16_t)len);
}
return status;
}
/*!
\brief get configuration descriptor from the USB device
\param[in] uhost: pointer to USB host
\param[in] len: length of the descriptor
\param[out] none
\retval operation status
*/
usbh_status usbh_cfgdesc_get(usbh_host *uhost, uint16_t len)
{
uint8_t *pdata = NULL;
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
#if (USBH_CFG_DESC_KEEP == 1U)
pdata = uhost->dev_prop.cfgdesc_rawdata;
#else
pdata = uhost->dev_prop.data;
#endif /* (USBH_CFG_DESC_KEEP == 1U) */
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_CONFIG),
.wIndex = 0U,
.wLength = len
};
usbh_ctlstate_config(uhost, pdata, len);
}
status = usbh_ctl_handler(uhost);
if(USBH_OK == status) {
if(len <= USB_CFG_DESC_LEN) {
usbh_cfgdesc_parse(&uhost->dev_prop.cfg_desc_set.cfg_desc, pdata);
} else {
usbh_cfgset_parse(&uhost->dev_prop, pdata);
}
}
return status;
}
/*!
\brief get string descriptor from the USB device
\param[in] uhost: pointer to USB host
\param[in] str_index: index for the string descriptor
\param[in] buf: buffer pointer to the string descriptor
\param[in] len: length of the descriptor
\param[out] none
\retval operation status
*/
usbh_status usbh_strdesc_get(usbh_host *uhost, \
uint8_t str_index, \
uint8_t *buf, \
uint16_t len)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_STR) | str_index,
.wIndex = 0x0409U,
.wLength = len
};
usbh_ctlstate_config(uhost, uhost->dev_prop.data, len);
}
status = usbh_ctl_handler(uhost);
if(USBH_OK == status) {
/* commands successfully sent and response received */
usbh_strdesc_parse(uhost->dev_prop.data, buf, len);
}
return status;
}
/*!
\brief set the address to the connected device
\param[in] uhost: pointer to USB host
\param[in] dev_addr: device address to assign
\param[out] none
\retval operation status
*/
usbh_status usbh_setaddress(usbh_host *uhost, uint8_t dev_addr)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_SET_ADDRESS,
.wValue = (uint16_t)dev_addr,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set the configuration value to the connected device
\param[in] uhost: pointer to USB host
\param[in] config_index: configuration value
\param[out] none
\retval operation status
*/
usbh_status usbh_setcfg(usbh_host *uhost, uint16_t config_index)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_SET_CONFIGURATION,
.wValue = config_index,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set the interface value to the connected device
\param[in] uhost: pointer to USB host
\param[in] itf_num: interface number
\param[in] set: alternated setting value
\param[out] none
\retval operation status
*/
usbh_status usbh_setinterface(usbh_host *uhost, uint8_t itf_num, uint8_t set)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_STRD,
.bRequest = USB_SET_INTERFACE,
.wValue = set,
.wIndex = itf_num,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set the interface value to the connected device
\param[in] uhost: pointer to USB host
\param[in] feature_selector: feature selector
\param[in] windex: index value
\param[out] none
\retval operation status
*/
usbh_status usbh_setdevfeature(usbh_host *uhost, uint8_t feature_selector, uint16_t windex)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_SET_FEATURE,
.wValue = feature_selector,
.wIndex = windex,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief clear the interface value to the connected device
\param[in] uhost: pointer to USB host
\param[in] feature_selector: feature selector
\param[in] windex: index value
\param[out] none
\retval operation status
*/
usbh_status usbh_clrdevfeature(usbh_host *uhost, uint8_t feature_selector, uint16_t windex)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_CLEAR_FEATURE,
.wValue = feature_selector,
.wIndex = windex,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief clear or disable a specific feature
\param[in] uhost: pointer to USB host
\param[in] ep_addr: endpoint address
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
usbh_status usbh_clrfeature(usbh_host *uhost, uint8_t ep_addr, uint8_t pp_num)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_EP | USB_REQTYPE_STRD,
.bRequest = USB_CLEAR_FEATURE,
.wValue = FEATURE_SELECTOR_EP,
.wIndex = ep_addr,
.wLength = 0U
};
if(EP_ID(ep_addr) == udev->host.pipe[pp_num].ep.num) {
usbh_pipe_toggle_set(udev, pp_num, 0U);
} else {
return USBH_FAIL;
}
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief get the next descriptor header
\param[in] pbuf: pointer to buffer where the configuration descriptor set is available
\param[in] ptr: data pointer inside the configuration descriptor set
\param[out] none
\retval return descriptor header
*/
usb_desc_header *usbh_nextdesc_get(uint8_t *pbuf, uint16_t *ptr)
{
usb_desc_header *pnext;
*ptr += ((usb_desc_header *)pbuf)->bLength;
pnext = (usb_desc_header *)((uint8_t *)pbuf + ((usb_desc_header *)pbuf)->bLength);
return (pnext);
}
/*!
\brief get the next descriptor header
\param[in] udev: pointer to device property
\param[in] interface: interface number
\param[out] none
\retval operation status
*/
usbh_status usbh_interface_select(usb_dev_prop *udev, uint8_t interface)
{
usbh_status status = USBH_OK;
if(interface < udev->cfg_desc_set.cfg_desc.bNumInterfaces) {
udev->cur_itf = interface;
} else {
status = USBH_FAIL;
}
return status;
}
/*!
\brief find the interface index for a specific class
\param[in] udev: pointer to device property
\param[in] main_class: class code
\param[in] sub_class: subclass code
\param[in] protocol: protocol code
\param[out] none
\retval interface index in the configuration structure
\note interface index 0xFF means interface index not found
*/
uint8_t usbh_interface_find(usb_dev_prop *udev, uint8_t main_class, uint8_t sub_class, uint8_t protocol)
{
usb_desc_itf *pif;
uint8_t if_ix = 0U;
pif = (usb_desc_itf *)0U;
while(if_ix < udev->cfg_desc_set.cfg_desc.bNumInterfaces) {
pif = &udev->cfg_desc_set.itf_desc_set[if_ix][0].itf_desc;
if(((pif->bInterfaceClass == main_class) || (0xFFU == main_class)) && \
((pif->bInterfaceSubClass == sub_class) || (0xFFU == sub_class)) && \
((pif->bInterfaceProtocol == protocol) || (0xFFU == protocol))) {
return if_ix;
}
if_ix++;
}
return 0xFFU;
}
/*!
\brief find the interface index for a specific class interface and alternate setting number
\param[in] udev: pointer to device property
\param[in] interface_number: interface number
\param[in] alt_settings: alternate setting number
\param[out] none
\retval interface index in the configuration structure
\note interface index 0xFF means interface index not found
*/
uint8_t usbh_interfaceindex_find(usb_dev_prop *udev, uint8_t interface_number, uint8_t alt_settings)
{
usb_desc_itf *pif;
uint8_t if_ix = 0U;
pif = (usb_desc_itf *)0U;
while(if_ix < USBH_MAX_INTERFACES_NUM) {
pif = &udev->cfg_desc_set.itf_desc_set[if_ix][alt_settings].itf_desc;
if((pif->bInterfaceNumber == interface_number) && (pif->bAlternateSetting == alt_settings)) {
return if_ix;
}
if_ix++;
}
return 0xFFU;
}
/*!
\brief parse the device descriptor
\param[in] dev_desc: pointer to USB device descriptor buffer
\param[in] buf: pointer to the source descriptor buffer
\param[in] len: length of the descriptor
\param[out] none
\retval none
*/
static void usbh_devdesc_parse(usb_desc_dev *dev_desc, uint8_t *buf, uint16_t len)
{
*dev_desc = (usb_desc_dev) {
.header = {
.bLength = *(uint8_t *)(buf + 0U),
.bDescriptorType = *(uint8_t *)(buf + 1U)
},
.bcdUSB = BYTE_SWAP(buf + 2U),
.bDeviceClass = *(uint8_t *)(buf + 4U),
.bDeviceSubClass = *(uint8_t *)(buf + 5U),
.bDeviceProtocol = *(uint8_t *)(buf + 6U),
.bMaxPacketSize0 = *(uint8_t *)(buf + 7U)
};
if(len > 8U) {
/* for 1st time after device connection, host may issue only 8 bytes for device descriptor length */
dev_desc->idVendor = BYTE_SWAP(buf + 8U);
dev_desc->idProduct = BYTE_SWAP(buf + 10U);
dev_desc->bcdDevice = BYTE_SWAP(buf + 12U);
dev_desc->iManufacturer = *(uint8_t *)(buf + 14U);
dev_desc->iProduct = *(uint8_t *)(buf + 15U);
dev_desc->iSerialNumber = *(uint8_t *)(buf + 16U);
dev_desc->bNumberConfigurations = *(uint8_t *)(buf + 17U);
}
}
/*!
\brief parse the configuration descriptor
\param[in] cfg_desc: pointer to USB configuration descriptor buffer
\param[in] buf: pointer to the source descriptor buffer
\param[out] none
\retval none
*/
static void usbh_cfgdesc_parse(usb_desc_config *cfg_desc, uint8_t *buf)
{
/* parse configuration descriptor */
*cfg_desc = (usb_desc_config) {
.header = {
.bLength = *(uint8_t *)(buf + 0U),
.bDescriptorType = *(uint8_t *)(buf + 1U),
},
.wTotalLength = BYTE_SWAP(buf + 2U),
.bNumInterfaces = *(uint8_t *)(buf + 4U),
.bConfigurationValue = *(uint8_t *)(buf + 5U),
.iConfiguration = *(uint8_t *)(buf + 6U),
.bmAttributes = *(uint8_t *)(buf + 7U),
.bMaxPower = *(uint8_t *)(buf + 8U)
};
}
/*!
\brief parse the configuration descriptor set
\param[in] udev: pointer to device property
\param[in] buf: pointer to the source descriptor buffer
\param[out] none
\retval none
*/
static void usbh_cfgset_parse(usb_dev_prop *udev, uint8_t *buf)
{
usb_desc_ep *ep = NULL;
usb_desc_itf_set *itf = NULL;
usb_desc_itf itf_value;
usb_desc_config *cfg = NULL;
usb_desc_header *pdesc = (usb_desc_header *)buf;
uint8_t itf_index = 0U, ep_index = 0U, alt_setting = 0U;
uint8_t pre_itf_index = 0U;
uint16_t ptr = 0U;
/* parse configuration descriptor */
usbh_cfgdesc_parse(&udev->cfg_desc_set.cfg_desc, buf);
cfg = &udev->cfg_desc_set.cfg_desc;
ptr = USB_CFG_DESC_LEN;
if(cfg->bNumInterfaces > USBH_MAX_INTERFACES_NUM) {
return;
}
while(ptr < cfg->wTotalLength) {
pdesc = usbh_nextdesc_get((uint8_t *)pdesc, &ptr);
if(USB_DESCTYPE_ITF == pdesc->bDescriptorType) {
itf_index = *(((uint8_t *)pdesc) + 2U);
if(pre_itf_index != itf_index) {
alt_setting = 0U;
}
itf = &udev->cfg_desc_set.itf_desc_set[itf_index][alt_setting];
alt_setting++;
if((*((uint8_t *)pdesc + 3U)) < 3U) {
usbh_itfdesc_parse(&itf_value, (uint8_t *)pdesc);
/* parse endpoint descriptors relative to the current interface */
if(itf_value.bNumEndpoints > USBH_MAX_EP_NUM) {
return;
}
usbh_itfdesc_parse(&itf->itf_desc, (uint8_t *)&itf_value);
/* store the previous interface index */
pre_itf_index = itf_index;
if(0U == itf_value.bNumEndpoints) {
continue;
}
for(ep_index = 0U; ep_index < itf_value.bNumEndpoints;) {
pdesc = usbh_nextdesc_get((void *)pdesc, &ptr);
if(USB_DESCTYPE_EP == pdesc->bDescriptorType) {
ep = &itf->ep_desc[ep_index];
usbh_epdesc_parse(ep, (uint8_t *)pdesc);
ep_index++;
}
}
}
}
}
}
/*!
\brief parse the interface descriptor
\param[in] itf_desc: pointer to USB interface descriptor buffer
\param[in] buf: pointer to the source descriptor buffer
\param[out] none
\retval none
*/
static void usbh_itfdesc_parse(usb_desc_itf *itf_desc, uint8_t *buf)
{
*itf_desc = (usb_desc_itf) {
.header = {
.bLength = *(uint8_t *)(buf + 0U),
.bDescriptorType = *(uint8_t *)(buf + 1U),
},
.bInterfaceNumber = *(uint8_t *)(buf + 2U),
.bAlternateSetting = *(uint8_t *)(buf + 3U),
.bNumEndpoints = *(uint8_t *)(buf + 4U),
.bInterfaceClass = *(uint8_t *)(buf + 5U),
.bInterfaceSubClass = *(uint8_t *)(buf + 6U),
.bInterfaceProtocol = *(uint8_t *)(buf + 7U),
.iInterface = *(uint8_t *)(buf + 8U)
};
}
/*!
\brief parse the endpoint descriptor
\param[in] ep_desc: pointer to USB endpoint descriptor buffer
\param[in] buf: pointer to the source descriptor buffer
\param[out] none
\retval none
*/
static void usbh_epdesc_parse(usb_desc_ep *ep_desc, uint8_t *buf)
{
*ep_desc = (usb_desc_ep) {
.header = {
.bLength = *(uint8_t *)(buf + 0U),
.bDescriptorType = *(uint8_t *)(buf + 1U)
},
.bEndpointAddress = *(uint8_t *)(buf + 2U),
.bmAttributes = *(uint8_t *)(buf + 3U),
.wMaxPacketSize = BYTE_SWAP(buf + 4U),
.bInterval = *(uint8_t *)(buf + 6U)
};
}
/*!
\brief parse the string descriptor
\param[in] psrc: source pointer containing the descriptor data
\param[in] pdest: destination address pointer
\param[in] len: length of the descriptor
\param[out] none
\retval none
*/
static void usbh_strdesc_parse(uint8_t *psrc, uint8_t *pdest, uint16_t len)
{
uint16_t str_len = 0U, index = 0U;
/* the Unicode string descriptor is not NULL-terminated. The string length is
* computed by subtracting two from the value of the first byte of the descriptor.
*/
/* check which is lower size, the size of string or the length of bytes read from the device */
if(USB_DESCTYPE_STR == psrc[1]) {
/* make sure the descriptor is string type */
/* psrc[0] contains size of descriptor, subtract 2 to get the length of string */
str_len = USB_MIN((uint16_t)psrc[0] - 2U, len);
psrc += 2U; /* adjust the offset ignoring the string length and descriptor type */
for(index = 0U; index < str_len; index += 2U) {
/* copy only the string and ignore the Unicode id, hence add the source */
*pdest = psrc[index];
pdest++;
}
*pdest = 0U; /* mark end of string */
}
}
@@ -0,0 +1,181 @@
/*!
\file usbh_pipe.c
\brief USB host mode pipe operation driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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_pipe.h"
/* local function prototypes ('static') */
static uint16_t usbh_freepipe_get(usb_core_driver *udev);
/*!
\brief create a pipe
\param[in] udev: pointer to USB core instance
\param[in] dev: USB device
\param[in] pp_num: pipe number
\param[in] ep_type: endpoint type
\param[in] ep_mpl: endpoint max packet length
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_create(usb_core_driver *udev, \
usb_dev_prop *dev, \
uint8_t pp_num, \
uint8_t ep_type, \
uint16_t ep_mpl)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
pp->dev_addr = dev->addr;
pp->dev_speed = dev->speed;
pp->ep.type = ep_type;
pp->ep.mps = ep_mpl;
if((USB_EPTYPE_BULK == pp->ep.type) || (USB_EPTYPE_CTRL == pp->ep.type)) {
pp->supp_ping = (uint8_t)(pp->dev_speed == PORT_SPEED_HIGH);
}
usb_pipe_init(udev, pp_num);
return HP_OK;
}
/*!
\brief modify a pipe
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[in] dev_addr: device address
\param[in] dev_speed: device speed
\param[in] ep_mpl: endpoint max packet length
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_update(usb_core_driver *udev, \
uint8_t pp_num, \
uint8_t dev_addr, \
uint32_t dev_speed, \
uint16_t ep_mpl)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
if((pp->dev_addr != dev_addr) && (dev_addr)) {
pp->dev_addr = dev_addr;
}
if((pp->dev_speed != dev_speed) && (dev_speed)) {
pp->dev_speed = dev_speed;
if((USB_EPTYPE_BULK == pp->ep.type) || (USB_EPTYPE_CTRL == pp->ep.type)) {
pp->supp_ping = (uint8_t)(pp->dev_speed == PORT_SPEED_HIGH);
}
}
if((pp->ep.mps != ep_mpl) && (ep_mpl)) {
pp->ep.mps = ep_mpl;
}
usb_pipe_init(udev, pp_num);
return HP_OK;
}
/*!
\brief allocate a new pipe
\param[in] udev: pointer to USB core instance
\param[in] ep_addr: endpoint address
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_allocate(usb_core_driver *udev, uint8_t ep_addr)
{
uint16_t pp_num = usbh_freepipe_get(udev);
if(HP_ERROR != pp_num) {
udev->host.pipe[pp_num].in_used = 1U;
udev->host.pipe[pp_num].ep.dir = EP_DIR(ep_addr);
udev->host.pipe[pp_num].ep.num = EP_ID(ep_addr);
}
return (uint8_t)pp_num;
}
/*!
\brief free a pipe
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_free(usb_core_driver *udev, uint8_t pp_num)
{
if(pp_num < HP_MAX) {
udev->host.pipe[pp_num].in_used = 0U;
}
return USBH_OK;
}
/*!
\brief delete all USB host pipe
\param[in] udev: pointer to USB core instance
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_delete(usb_core_driver *udev)
{
uint8_t pp_num = 0U;
for(pp_num = 2U; pp_num < HP_MAX; pp_num++) {
udev->host.pipe[pp_num] = (usb_pipe) {0};
}
return USBH_OK;
}
/*!
\brief get a free pipe number for allocation
\param[in] udev: pointer to USB core instance
\param[out] none
\retval operation status
*/
static uint16_t usbh_freepipe_get(usb_core_driver *udev)
{
uint8_t pp_num = 0U;
for(pp_num = 0U; pp_num < HP_MAX; pp_num++) {
if(0U == udev->host.pipe[pp_num].in_used) {
return (uint16_t)pp_num;
}
}
return HP_ERROR;
}
@@ -0,0 +1,369 @@
/*!
\file usbh_transc.c
\brief USB host mode transactions driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, GigaDevice Semiconductor Inc.
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_pipe.h"
#include "usbh_transc.h"
/* local function prototypes ('static') */
static usb_urb_state usbh_urb_wait(usbh_host *uhost, uint8_t pp_num, uint32_t wait_time);
static void usbh_setup_transc(usbh_host *uhost);
static void usbh_data_in_transc(usbh_host *uhost);
static void usbh_data_out_transc(usbh_host *uhost);
static void usbh_status_in_transc(usbh_host *uhost);
static void usbh_status_out_transc(usbh_host *uhost);
static uint32_t usbh_request_submit(usb_core_driver *udev, uint8_t pp_num);
/*!
\brief send the SETUP packet to the USB device
\param[in] udev: pointer to USB core instance
\param[in] buf: data buffer which will be sent to USB device
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
usbh_status usbh_ctlsetup_send(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
pp->DPID = PIPE_DPID_SETUP;
pp->xfer_buf = buf;
pp->xfer_len = USB_SETUP_PACKET_LEN;
return (usbh_status)usbh_request_submit(udev, pp_num);
}
/*!
\brief send a data packet to the USB device
\param[in] udev: pointer to USB core instance
\param[in] buf: data buffer which will be sent to USB device
\param[in] pp_num: pipe number
\param[in] len: length of the data to be sent
\param[out] none
\retval operation status
*/
usbh_status usbh_data_send(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num, uint16_t len)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
pp->xfer_buf = buf;
pp->xfer_len = len;
switch(pp->ep.type) {
case USB_EPTYPE_CTRL:
if(0U == len) {
pp->data_toggle_out = 1U;
}
pp->DPID = PIPE_DPID[pp->data_toggle_out];
break;
case USB_EPTYPE_INTR:
pp->DPID = PIPE_DPID[pp->data_toggle_out];
pp->data_toggle_out ^= 1U;
break;
case USB_EPTYPE_BULK:
pp->DPID = PIPE_DPID[pp->data_toggle_out];
break;
case USB_EPTYPE_ISOC:
pp->DPID = PIPE_DPID[0];
break;
default:
break;
}
usbh_request_submit(udev, pp_num);
return USBH_OK;
}
/*!
\brief receive a data packet from the USB device
\param[in] udev: pointer to USB core instance
\param[in] buf: data buffer which will be received from USB device
\param[in] pp_num: pipe number
\param[in] len: length of the data to be received
\param[out] none
\retval operation status
*/
usbh_status usbh_data_recev(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num, uint16_t len)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
pp->xfer_buf = buf;
pp->xfer_len = len;
switch(pp->ep.type) {
case USB_EPTYPE_CTRL:
pp->DPID = PIPE_DPID[1];
break;
case USB_EPTYPE_INTR:
pp->DPID = PIPE_DPID[pp->data_toggle_in];
/* toggle DATA PID */
pp->data_toggle_in ^= 1U;
break;
case USB_EPTYPE_BULK:
pp->DPID = PIPE_DPID[pp->data_toggle_in];
break;
case USB_EPTYPE_ISOC:
pp->DPID = PIPE_DPID[0];
break;
default:
break;
}
usbh_request_submit(udev, pp_num);
return USBH_OK;
}
/*!
\brief USB control transfer handler
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
usbh_status usbh_ctl_handler(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
switch(uhost->control.ctl_state) {
case CTL_SETUP:
usbh_setup_transc(uhost);
break;
case CTL_DATA_IN:
usbh_data_in_transc(uhost);
break;
case CTL_DATA_OUT:
usbh_data_out_transc(uhost);
break;
case CTL_STATUS_IN:
usbh_status_in_transc(uhost);
break;
case CTL_STATUS_OUT:
usbh_status_out_transc(uhost);
break;
case CTL_FINISH:
uhost->control.ctl_state = CTL_IDLE;
status = USBH_OK;
break;
case CTL_ERROR:
if(++uhost->control.error_count <= USBH_MAX_ERROR_COUNT) {
/* do the transmission again, starting from SETUP packet */
uhost->control.ctl_state = CTL_SETUP;
} else {
status = USBH_FAIL;
}
break;
default:
break;
}
return status;
}
/*!
\brief wait for USB URB(USB request block) state
\param[in] uhost: pointer to USB host
\param[in] pp_num: pipe number
\param[in] wait_time: wait time
\param[out] none
\retval USB URB state
*/
static usb_urb_state usbh_urb_wait(usbh_host *uhost, uint8_t pp_num, uint32_t wait_time)
{
uint32_t timeout = 0U;
usb_urb_state urb_status = URB_IDLE;
timeout = uhost->control.timer;
while(URB_DONE != (urb_status = usbh_urbstate_get(uhost->data, pp_num))) {
if(URB_NOTREADY == urb_status) {
break;
} else if(URB_STALL == urb_status) {
uhost->control.ctl_state = CTL_SETUP;
break;
} else if(URB_ERROR == urb_status) {
uhost->control.ctl_state = CTL_ERROR;
break;
} else if((wait_time > 0U) && ((uhost->control.timer - timeout) > wait_time)) {
/* timeout for IN transfer */
uhost->control.ctl_state = CTL_ERROR;
break;
} else {
/* no operation, just wait */
}
}
return urb_status;
}
/*!
\brief USB SETUP transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_setup_transc(usbh_host *uhost)
{
/* send a SETUP packet */
usbh_ctlsetup_send(uhost->data, \
uhost->control.setup.data, \
uhost->control.pipe_out_num);
if(URB_DONE == usbh_urb_wait(uhost, uhost->control.pipe_out_num, 0U)) {
uint8_t dir = (uhost->control.setup.req.bmRequestType & USB_TRX_MASK);
if(uhost->control.setup.req.wLength) {
if(USB_TRX_IN == dir) {
uhost->control.ctl_state = CTL_DATA_IN;
} else {
uhost->control.ctl_state = CTL_DATA_OUT;
}
} else {
if(USB_TRX_IN == dir) {
uhost->control.ctl_state = CTL_STATUS_OUT;
} else {
uhost->control.ctl_state = CTL_STATUS_IN;
}
}
}
}
/*!
\brief USB data IN transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_data_in_transc(usbh_host *uhost)
{
usbh_data_recev(uhost->data, \
uhost->control.buf, \
uhost->control.pipe_in_num, \
uhost->control.ctl_len);
if(URB_DONE == usbh_urb_wait(uhost, uhost->control.pipe_in_num, DATA_STAGE_TIMEOUT)) {
uhost->control.ctl_state = CTL_STATUS_OUT;
}
}
/*!
\brief USB data OUT transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_data_out_transc(usbh_host *uhost)
{
usbh_pipe_toggle_set(uhost->data, uhost->control.pipe_out_num, 1U);
usbh_data_send(uhost->data, \
uhost->control.buf, \
uhost->control.pipe_out_num, \
uhost->control.ctl_len);
if(URB_DONE == usbh_urb_wait(uhost, uhost->control.pipe_out_num, DATA_STAGE_TIMEOUT)) {
uhost->control.ctl_state = CTL_STATUS_IN;
}
}
/*!
\brief USB status IN transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_status_in_transc(usbh_host *uhost)
{
uint8_t pp_num = uhost->control.pipe_in_num;
usbh_data_recev(uhost->data, NULL, pp_num, 0U);
if(URB_DONE == usbh_urb_wait(uhost, pp_num, NODATA_STAGE_TIMEOUT)) {
uhost->control.ctl_state = CTL_FINISH;
}
}
/*!
\brief USB status OUT transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_status_out_transc(usbh_host *uhost)
{
uint8_t pp_num = uhost->control.pipe_out_num;
usbh_data_send(uhost->data, NULL, pp_num, 0U);
if(URB_DONE == usbh_urb_wait(uhost, pp_num, NODATA_STAGE_TIMEOUT)) {
uhost->control.ctl_state = CTL_FINISH;
}
}
/*!
\brief prepare a pipe and start a transfer
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
static uint32_t usbh_request_submit(usb_core_driver *udev, uint8_t pp_num)
{
udev->host.pipe[pp_num].urb_state = URB_IDLE;
udev->host.pipe[pp_num].xfer_count = 0U;
if(1U == udev->host.pipe[pp_num].do_ping) {
(void)usb_pipe_ping(udev, (uint8_t)pp_num);
return USB_OK;
}
return (uint32_t)usb_pipe_xfer(udev, pp_num);
}