/** * \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; }