osmo-asf4-dfu/usb/device/usbdc.c

1028 lines
24 KiB
C

/**
* \file
*
* \brief USB Device Stack Core Layer Implementation.
*
* Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
#include "usbdc.h"
#define USBDC_VERSION 0x00000001u
/**
* \brief USB Device Core Sof Handler
*/
struct usbdc_sof_handler {
struct usbdc_sof_handler *next;
usbdc_sof_cb_t cb;
};
/**
* \brief USB Device Core Request Handler
*/
struct usbdc_req_handler {
struct usbdc_req_handler *next;
usbdc_req_cb_t cb;
};
/**
* \brief USB Device Core Change Handler
*/
struct usbdc_change_handler {
struct usbdc_change_handler *next;
usbdc_change_cb_t cb;
};
/**
* \brief USB Device Core Handler
*/
struct usbdc_handlers {
struct list_descriptor sof_list;
struct list_descriptor req_list;
struct list_descriptor change_list;
};
/**
* \brief USB Device Core Driver Structure
*/
struct usbdc_driver {
/** Pointer to descriptions of descriptors. */
struct usbdc_descriptors desces;
/** Callback handlers. */
struct usbdc_handlers handlers;
/** list of function drivers. */
struct list_descriptor func_list;
/** Control buffer. */
uint8_t *ctrl_buf;
/** Device status. */
uint16_t status;
/** Device state. */
uint8_t state;
/** Configuration value. */
uint8_t cfg_value;
/** Control endpoint size. */
uint8_t ctrl_size;
/** Alternate interface used map */
uint8_t ifc_alt_map;
};
/**
* \brief USB Device Core Driver Instance
*/
static struct usbdc_driver usbdc;
/**
* \brief Process the GetDeviceDescriptor request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_dev_desc(const uint8_t ep, struct usb_req *req)
{
uint8_t *dev_desc = NULL;
uint16_t length = req->wLength;
if (length > 0x12) {
length = 0x12;
}
#if CONF_USBD_HS_SP
if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
dev_desc = usb_find_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, USB_DT_DEVICE);
} else {
/* Obtain descriptor from FS descriptors */
}
#endif
if (!dev_desc) {
dev_desc = usb_find_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, USB_DT_DEVICE);
}
if (!dev_desc) {
return false;
}
if (ERR_NONE != usbdc_xfer(ep, dev_desc, length, false)) {
return false;
}
return true;
}
/**
* \brief Process the GetConfigurationDescriptor request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_cfg_desc(const uint8_t ep, struct usb_req *req)
{
uint8_t *cfg_desc = NULL;
uint16_t total_len;
uint16_t length = req->wLength;
uint8_t index = req->wValue & 0x00FF;
bool need_zlp = !(length & (usbdc.ctrl_size - 1));
#if CONF_USBD_HS_SP
if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
cfg_desc = usb_find_cfg_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, index + 1);
} else {
/* Obtain descriptor from FS descriptors */
}
#endif
if (!cfg_desc) {
cfg_desc = usb_find_cfg_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, index + 1);
}
if (NULL == cfg_desc) {
return false;
}
total_len = usb_cfg_desc_total_len(cfg_desc);
if (length <= total_len) {
need_zlp = false;
} else {
length = total_len;
}
if (ERR_NONE != usbdc_xfer(ep, cfg_desc, length, need_zlp)) {
return false;
}
return true;
}
/**
* \brief Process the GetStringDescriptor request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_str_desc(const uint8_t ep, struct usb_req *req)
{
uint8_t *str_desc;
uint16_t length = req->wLength;
uint8_t index = req->wValue & 0x00FF;
bool need_zlp = !(length & (usbdc.ctrl_size - 1));
/* All string are in default descriptors block: FS/LS */
str_desc = usb_find_str_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, index);
if (NULL == str_desc) {
return false;
}
if (length <= str_desc[0]) {
need_zlp = false;
} else {
length = str_desc[0];
}
if (ERR_NONE != usbdc_xfer(ep, str_desc, length, need_zlp)) {
return false;
}
return true;
}
#if CONF_USBD_HS_SP
/**
* \brief Process the GetDeviceQualifierDescriptor request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_devqual_desc(const uint8_t ep, struct usb_req *req)
{
uint8_t *dev_desc = NULL;
uint16_t length = req->wLength;
if (length > 0x12) {
length = 0x12;
}
if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
dev_desc = usb_find_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, USB_DT_DEVICE_QUALIFIER);
}
if (!dev_desc) {
dev_desc = usb_find_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, USB_DT_DEVICE_QUALIFIER);
}
if (!dev_desc) {
return false;
}
if (ERR_NONE != usbdc_xfer(ep, dev_desc, length, false)) {
return false;
}
return true;
}
/**
* \brief Process the GetOtherSpeedConfigurationDescriptor request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_othspdcfg_desc(const uint8_t ep, struct usb_req *req)
{
uint8_t *cfg_desc = NULL;
uint16_t total_len;
uint16_t length = req->wLength;
uint8_t index = req->wValue & 0x00FF;
bool need_zlp = !(length & (usbdc.ctrl_size - 1));
if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
cfg_desc = usb_find_othspdcfg_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, index + 1);
} else {
/* Obtain descriptor from FS descriptors */
}
if (!cfg_desc) {
cfg_desc = usb_find_othspdcfg_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, index + 1);
}
if (NULL == cfg_desc) {
return false;
}
total_len = usb_cfg_desc_total_len(cfg_desc);
if (length <= total_len) {
need_zlp = false;
} else {
length = total_len;
}
if (ERR_NONE != usbdc_xfer(ep, cfg_desc, length, need_zlp)) {
return false;
}
return true;
}
#endif
/**
* \brief Process the GetDescriptor request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_desc_req(const uint8_t ep, struct usb_req *req)
{
uint8_t type = (uint8_t)(req->wValue >> 8);
switch (type) {
case USB_DT_DEVICE:
return usbdc_get_dev_desc(ep, req);
case USB_DT_CONFIG:
return usbdc_get_cfg_desc(ep, req);
#if CONF_USBD_HS_SP
case USB_DT_DEVICE_QUALIFIER:
return usbdc_get_devqual_desc(ep, req);
case USB_DT_OTHER_SPEED_CONFIG:
return usbdc_get_othspdcfg_desc(ep, req);
#endif
case USB_DT_STRING:
return usbdc_get_str_desc(ep, req);
default:
break;
}
return false;
}
/**
* \brief Process the GetStatus request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_status_req(const uint8_t ep, const struct usb_req *req)
{
int32_t st;
(void)ep;
switch (req->bmRequestType & USB_REQT_RECIP_MASK) {
case USB_REQT_RECIP_DEVICE:
case USB_REQT_RECIP_INTERFACE:
st = 0;
break;
case USB_REQT_RECIP_ENDPOINT:
st = usb_d_ep_halt(req->wIndex & 0xFF, USB_EP_HALT_GET);
if (st < 0) {
return false;
}
st = st & 0x1;
break;
default:
return false;
}
memcpy(usbdc.ctrl_buf, &st, 2);
usbdc_xfer(ep, usbdc.ctrl_buf, 2, false);
return true;
}
/**
* \brief Process the standard Get Interface
* \param[in] req Point to usb request struct.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_interface(struct usb_req *req)
{
struct usbdf_driver *func = (struct usbdf_driver *)usbdc.func_list.head;
int32_t rc;
if (!(usbdc.ifc_alt_map & (1 << req->wIndex))) {
/* Return 0 if alternate is not used */
usbdc.ctrl_buf[0] = 0;
usbdc_xfer(0, usbdc.ctrl_buf, 1, false);
return true;
}
/* Check function drivers only if alternate is used */
while (NULL != func) {
if (0 > (rc = func->ctrl(func, USBDF_GET_IFACE, req))) {
func = func->next;
} else {
usbdc.ctrl_buf[0] = (uint8_t)rc;
usbdc_xfer(0, usbdc.ctrl_buf, 1, false);
return true;
}
}
return false;
}
/**
* \brief Process the standard Get request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_get_req(const uint8_t ep, struct usb_req *req)
{
switch (req->bRequest) {
case USB_REQ_GET_DESC:
return usbdc_get_desc_req(ep, req);
case USB_REQ_GET_CONFIG:
*(uint8_t *)usbdc.ctrl_buf = usbdc.cfg_value;
usbdc_xfer(ep, usbdc.ctrl_buf, 1, false);
return true;
case USB_REQ_GET_STATUS:
return usbdc_get_status_req(ep, req);
case USB_REQ_GET_INTERFACE:
return usbdc_get_interface(req);
default:
return false;
}
}
/**
* \brief Process the standard ClearFeature request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_clear_ftr_req(const uint8_t ep, const struct usb_req *req)
{
(void)ep;
switch (req->bmRequestType & USB_REQT_RECIP_MASK) {
case USB_REQT_RECIP_ENDPOINT:
if (req->wLength != 0) {
return false;
}
usb_d_ep_halt(req->wIndex & 0xFF, USB_EP_HALT_CLR);
usbdc_xfer(ep, NULL, 0, true);
return true;
default:
return false;
}
}
/**
* \brief Process the standard SetFeature request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_set_ftr_req(const uint8_t ep, const struct usb_req *req)
{
(void)ep;
switch (req->bmRequestType & USB_REQT_RECIP_MASK) {
case USB_REQT_RECIP_ENDPOINT:
if (req->wLength != 0) {
return false;
}
usb_d_ep_halt(req->wIndex & 0xFF, USB_EP_HALT_SET);
usbdc_xfer(ep, NULL, 0, true);
return true;
default:
return false;
}
}
/**
* \brief Unconfig, close all interfaces
*/
static void usbdc_unconfig(void)
{
struct usbdf_driver *func = (struct usbdf_driver *)usbdc.func_list.head;
while (NULL != func) {
func->ctrl(func, USBDF_DISABLE, NULL);
func = func->next;
}
}
/**
* \brief Apply Set Configuration Value
* \param[in] cfg_value Configuration Value
* \retval true Set configuration OK.
* \retval false Request error.
*/
static bool usbdc_set_config(uint8_t cfg_value)
{
struct usbd_descriptors desc;
struct usbdf_driver * func;
uint8_t * cfg_desc = NULL;
uint16_t total_len;
uint8_t last_iface = 0xFF;
if (cfg_value == 0) {
usbdc_unconfig();
return true;
}
#if CONF_USBD_HS_SP
if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
cfg_desc = usb_find_cfg_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, cfg_value);
} else {
/* Obtain descriptor from FS descriptors */
}
#endif
if (!cfg_desc) {
cfg_desc = usb_find_cfg_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, cfg_value);
}
if (NULL == cfg_desc) {
return false;
}
total_len = usb_cfg_desc_total_len(cfg_desc);
desc.eod = cfg_desc + total_len;
desc.sod = usb_find_desc(cfg_desc, desc.eod, USB_DT_INTERFACE);
while (NULL != desc.sod) {
/* Apply very first alternate setting (must be 0) of the interface */
if (last_iface != desc.sod[2] /* bInterfaceNumber */) {
last_iface = desc.sod[2];
func = (struct usbdf_driver *)usbdc.func_list.head;
while (NULL != func) {
if (func->ctrl(func, USBDF_ENABLE, &desc)) {
func = func->next;
} else {
break;
}
}
}
desc.sod = usb_desc_next(desc.sod);
desc.sod = usb_find_desc(desc.sod, desc.eod, USB_DT_INTERFACE);
}
return true;
}
/**
* \brief Apply the USB device address
* \param[in] addr address to be set.
*/
static void usbdc_set_address(uint8_t addr)
{
usb_d_set_address(addr);
}
/**
* \brief Process the standard Set Interface
* \param[in] alt_set Alternate Setting.
* \param[in] ifc_id Interface Index.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_set_interface(uint16_t alt_set, uint16_t ifc_id)
{
struct usbd_descriptors desc;
struct usbdf_driver * func;
uint8_t * ifc = NULL;
#if CONF_USBD_HS_SP
if (usb_d_get_speed() == USB_SPEED_HS && usbdc.desces.hs) {
ifc = usb_find_cfg_desc(usbdc.desces.hs->sod, usbdc.desces.hs->eod, usbdc.cfg_value);
} else {
/* Obtain descriptor from FS descriptors */
}
#endif
if (!ifc) {
ifc = usb_find_cfg_desc(usbdc.desces.ls_fs->sod, usbdc.desces.ls_fs->eod, usbdc.cfg_value);
}
if (NULL == ifc) {
return false;
}
desc.sod = ifc;
desc.eod = ifc + usb_cfg_desc_total_len(ifc);
if (NULL == (ifc = usb_find_desc(desc.sod, desc.eod, USB_DT_INTERFACE))) {
return false;
}
while (ifc[2] != ifc_id || ifc[3] != alt_set) {
desc.sod = usb_desc_next(desc.sod);
ifc = usb_find_desc(desc.sod, desc.eod, USB_DT_INTERFACE);
if (NULL == ifc) {
return false;
}
}
desc.sod = ifc;
func = (struct usbdf_driver *)usbdc.func_list.head;
while (NULL != func) {
if (func->ctrl(func, USBDF_DISABLE, &desc)) {
func = func->next;
} else if (ERR_NONE == func->ctrl(func, USBDF_ENABLE, &desc)) {
if (alt_set) {
/* Alternate settings are used from now on */
usbdc.ifc_alt_map |= 1 << ifc_id;
}
usbdc_xfer(0, NULL, 0, 0);
return true;
} else {
return false;
}
}
return false;
}
/**
* \brief Process the standard Set request
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_set_req(const uint8_t ep, struct usb_req *req)
{
switch (req->bRequest) {
case USB_REQ_SET_ADDRESS:
return (ERR_NONE == usbdc_xfer(ep, NULL, 0, true));
case USB_REQ_SET_CONFIG:
if (!usbdc_set_config(req->wValue)) {
return false;
}
return (ERR_NONE == usbdc_xfer(ep, NULL, 0, true));
case USB_REQ_CLEAR_FTR:
return usbdc_clear_ftr_req(ep, req);
case USB_REQ_SET_FTR:
return usbdc_set_ftr_req(ep, req);
case USB_REQ_SET_INTERFACE:
return usbdc_set_interface(req->wValue, req->wIndex);
default:
return false;
}
}
/** Invoke all registered SOF callbacks. */
static void usbdc_sof_notify(void)
{
struct usbdc_sof_handler *sof = (struct usbdc_sof_handler *)usbdc.handlers.sof_list.head;
while (sof != NULL) {
if (NULL != sof->cb) {
sof->cb();
}
sof = sof->next;
}
}
/** Invoke all registered Change notification callbacks. */
static void usbdc_change_notify(enum usbdc_change_type change, uint32_t value)
{
struct usbdc_change_handler *cg = (struct usbdc_change_handler *)usbdc.handlers.change_list.head;
while (cg != NULL) {
if (NULL != cg->cb) {
cg->cb(change, value);
}
cg = cg->next;
}
}
/** Invoke all registered request callbacks until request handled. */
static int32_t usbdc_request_handler(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
{
struct usbdc_req_handler *h = (struct usbdc_req_handler *)usbdc.handlers.req_list.head;
int32_t rc;
while (h != NULL) {
if (NULL != h->cb) {
rc = h->cb(ep, req, stage);
if (0 == rc) {
return true;
} else if (ERR_NOT_FOUND != rc) {
return -1;
}
}
h = h->next;
}
return false;
}
/**
* \brief Callback invoked on USB device SOF
*/
static void usbd_sof_cb(void)
{
usbdc_sof_notify();
}
/**
* \brief Callback invoked when control request is received
* \param[in] ep Endpoint address.
* \param[in] req Pointer to the request.
* \return Operation status.
* \retval true Request is handled OK.
* \retval false Request not supported.
*/
static bool usbdc_cb_ctl_req(const uint8_t ep, struct usb_req *req)
{
switch (usbdc_request_handler(ep, req, USB_SETUP_STAGE)) {
case true:
return true;
case -1:
return false;
default:
break;
}
// STD request handling
switch (req->bmRequestType & (USB_REQT_TYPE_MASK | USB_REQT_DIR_IN)) {
case USB_REQT_TYPE_STANDARD:
return usbdc_set_req(ep, req);
case (USB_REQT_TYPE_STANDARD | USB_REQT_DIR_IN):
return usbdc_get_req(ep, req);
default:
return false;
}
}
/**
* \brief When control status stage is end
* \param[in] req Pointer to the request.
*/
static void usbdc_ctrl_status_end(const struct usb_req *req)
{
if (req->bmRequestType != USB_REQT_TYPE_STANDARD) {
return;
}
switch (req->bRequest) {
case USB_REQ_SET_CONFIG:
usbdc.cfg_value = req->wValue;
usbdc.state = req->wValue ? USBD_S_CONFIG : USBD_S_ADDRESS;
usbdc_change_notify(USBDC_C_STATE, usbdc.state);
break;
case USB_REQ_SET_ADDRESS:
usbdc_set_address(req->wValue);
usbdc.state = req->wValue ? USBD_S_ADDRESS : USBD_S_DEFAULT;
usbdc_change_notify(USBDC_C_STATE, usbdc.state);
break;
default:
break;
}
}
/**
* \brief When control data stage is end
* \param[in] req Pointer to the request.
*/
static bool usbdc_ctrl_data_end(struct usb_req *req)
{
usbdc_request_handler(0, req, USB_DATA_STAGE);
return false;
}
/**
* \brief Callback invoked when control data done or status done
* \param[in] ep Endpoint number with direction on bit 8.
* \param[in] code Status code.
* \param[in] req Pointer to the control setup request.
* \return Data has error or not.
* \retval true There is data error, protocol error.
* \retval false There is no data error.
*/
static bool usbdc_cb_ctl_done(const uint8_t ep, const enum usb_xfer_code code, struct usb_req *req)
{
(void)ep;
switch (code) {
case USB_XFER_DONE:
usbdc_ctrl_status_end(req);
break;
case USB_XFER_DATA:
return usbdc_ctrl_data_end(req);
default:
break;
}
return false;
}
/**
* \brief USB Device Core Reset
*/
void usbdc_reset(void)
{
usbdc_unconfig();
usbdc.state = USBD_S_DEFAULT;
usbdc.cfg_value = 0;
usbdc.ifc_alt_map = 0;
// Setup EP0
usb_d_ep_deinit(0);
usb_d_ep0_init(usbdc.ctrl_size);
usb_d_ep_register_callback(0, USB_D_EP_CB_SETUP, (FUNC_PTR)usbdc_cb_ctl_req);
usb_d_ep_register_callback(0, USB_D_EP_CB_XFER, (FUNC_PTR)usbdc_cb_ctl_done);
usb_d_ep_enable(0);
}
/**
* \brief Callback invoked on USB device events
* \param[in] ev Event code.
* \param[in] param Event parameter for event handling.
*/
static void usbd_event_cb(const enum usb_event ev, const uint32_t param)
{
(void)param;
switch (ev) {
case USB_EV_VBUS:
usbdc_change_notify(USBDC_C_CONN, param);
break;
case USB_EV_RESET:
usbdc_reset();
break;
default:
break;
}
}
/**
* \brief Issue USB device transfer
*/
int32_t usbdc_xfer(uint8_t ep, uint8_t *buf, uint32_t size, bool zlp)
{
struct usb_d_transfer xfer = {(uint8_t *)buf, size, ep, zlp};
return usb_d_ep_transfer(&xfer);
}
/**
* \brief Register the handler
*/
void usbdc_register_handler(enum usbdc_handler_type type, const struct usbdc_handler *h)
{
switch (type) {
case USBDC_HDL_SOF:
list_insert_at_end(&usbdc.handlers.sof_list, (void *)h);
break;
case USBDC_HDL_REQ:
list_insert_at_end(&usbdc.handlers.req_list, (void *)h);
break;
case USBDC_HDL_CHANGE:
list_insert_at_end(&usbdc.handlers.change_list, (void *)h);
break;
default:
break;
}
}
/**
* \brief Unregister the handler
*/
void usbdc_unregister_handler(enum usbdc_handler_type type, const struct usbdc_handler *h)
{
switch (type) {
case USBDC_HDL_SOF:
list_delete_element(&usbdc.handlers.sof_list, (void *)h);
break;
case USBDC_HDL_REQ:
list_delete_element(&usbdc.handlers.req_list, (void *)h);
break;
case USBDC_HDL_CHANGE:
list_delete_element(&usbdc.handlers.change_list, (void *)h);
break;
default:
break;
}
}
/**
* \brief Initialize the USB device core driver
*/
int32_t usbdc_init(uint8_t *ctrl_buf)
{
ASSERT(ctrl_buf);
int32_t rc;
rc = usb_d_init();
if (rc < 0) {
return rc;
}
memset(&usbdc, 0, sizeof(usbdc));
usbdc.ctrl_buf = ctrl_buf;
usb_d_register_callback(USB_D_CB_SOF, (FUNC_PTR)usbd_sof_cb);
usb_d_register_callback(USB_D_CB_EVENT, (FUNC_PTR)usbd_event_cb);
return 0;
}
/**
* \brief De-initialize the USB device core driver
*/
int32_t usbdc_deinit(void)
{
usb_d_deinit();
return 0;
}
/**
* \brief Register/unregister function support of a USB device function
*
* Must be invoked when USB device is stopped.
*/
void usbdc_register_function(struct usbdf_driver *func)
{
list_insert_at_end(&usbdc.func_list, func);
}
/**
* \brief Unregister function support of a USB device function
*
* Must be invoked when USB device is stopped.
*/
void usbdc_unregister_function(struct usbdf_driver *func)
{
list_delete_element(&usbdc.func_list, func);
}
/**
* \brief Validate the descriptor
*/
int32_t usbdc_validate_desces(struct usbd_descriptors *desces)
{
uint8_t *sod, *eod;
if (desces == NULL) {
return ERR_NOT_FOUND;
}
sod = usb_find_desc(desces->sod, desces->eod, USB_DT_DEVICE);
if (sod == NULL) {
return ERR_BAD_DATA;
}
sod = usb_find_desc(desces->sod, desces->eod, USB_DT_CONFIG);
if (sod == NULL) {
return ERR_BAD_DATA;
}
eod = sod + usb_cfg_desc_total_len(sod);
if (eod > desces->eod) {
return ERR_BAD_DATA;
}
return 0;
}
/**
* \brief Validate the descriptor
*/
int32_t usbdc_check_desces(struct usbdc_descriptors *desces)
{
#if CONF_USBD_HS_SP
int32_t rc;
if (desces->hs == NULL && desces->ls_fs == NULL) {
return ERR_NOT_FOUND;
}
if (desces->hs) {
rc = usbdc_validate_desces(desces->hs);
if (rc < 0) {
return rc;
}
}
#endif
return usbdc_validate_desces(desces->ls_fs);
}
/**
* \brief Start the USB device driver with specific descriptors set
*/
int32_t usbdc_start(struct usbd_descriptors *desces)
{
if (usbdc.state >= USBD_S_POWER) {
return ERR_BUSY;
}
if (desces) {
usbdc.desces.ls_fs = desces;
#if CONF_USBD_HS_SP
usbdc.desces.hs = &desces[1];
#endif
} else {
return ERR_BAD_DATA;
}
usbdc.ctrl_size = desces->sod[7];
usbdc.state = USBD_S_POWER;
usb_d_enable();
return ERR_NONE;
}
/**
* \brief Stop the USB device driver
*/
int32_t usbdc_stop(void)
{
usb_d_disable();
usbdc.state = USBD_S_OFF;
return ERR_NONE;
}
/**
* \brief Attach the USB device to host
*/
void usbdc_attach(void)
{
usb_d_attach();
}
/**
* \brief Detach the USB device from host
*/
void usbdc_detach(void)
{
usb_d_detach();
}
/**
* \brief Send remote wakeup to host
*/
void usbdc_remotewakeup(void)
{
usb_d_send_remotewakeup();
usbdc.state = USBD_S_POWER;
}
/**
* \brief Return USB Device endpoint0 buffer
*/
uint8_t *usbdc_get_ctrl_buffer(void)
{
return usbdc.ctrl_buf;
}
/**
* \brief Return current USB state
*/
uint8_t usbdc_get_state(void)
{
if (usbdc.state & USBD_S_SUSPEND) {
return USBD_S_SUSPEND;
}
return usbdc.state;
}
/**
* \brief Return version
*/
uint32_t usbdc_get_version(void)
{
return USBDC_VERSION;
}