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