Sync betaflight to Gitea
This commit is contained in:
+116
@@ -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 */
|
||||
Executable
+96
@@ -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 */
|
||||
+582
@@ -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);
|
||||
}
|
||||
Executable
+271
@@ -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;
|
||||
}
|
||||
+148
@@ -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 */
|
||||
+122
@@ -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 */
|
||||
+97
@@ -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 */
|
||||
+360
@@ -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;
|
||||
}
|
||||
+551
@@ -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;
|
||||
}
|
||||
+232
@@ -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 */
|
||||
}
|
||||
+396
@@ -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 *)§or_num)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)§or_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 *)§or_num)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)§or_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;
|
||||
}
|
||||
+262
@@ -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 */
|
||||
+70
@@ -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 */
|
||||
+102
@@ -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 */
|
||||
+50
@@ -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 */
|
||||
+659
@@ -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 */
|
||||
+693
@@ -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 */
|
||||
}
|
||||
}
|
||||
+181
@@ -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;
|
||||
}
|
||||
+369
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user