/* ********************************************************************************************************* * uC/USB-Device * The Embedded USB Device Stack * * Copyright (c) 2021 Semidrive Semiconductor * * All rights reserved. * ********************************************************************************************************* */ /* ********************************************************************************************************* * * USB DEVICE FASTBOOT CLASS * * Filename : usbd_fb.c * Version : V1.00.00 ********************************************************************************************************* */ /* ********************************************************************************************************* * INCLUDE FILES ********************************************************************************************************* */ #include #include #include #include "usbd_fb.h" #include "usbd_fb_os.h" #include /* ********************************************************************************************************* * EXTERNAL C LANGUAGE LINKAGE ********************************************************************************************************* */ /* ********************************************************************************************************* * LOCAL DEFINES ********************************************************************************************************* */ #define USBD_FB_MSG_BUF_LEN 512u #define USBD_FB_VAR_INFO_LEN 128u /* Since Win10 sends fastboot data 1MB by 1MB, let usb controller receive it correspondingly. */ #define USBD_FB_RX_LIMIT (1024 * 1024u) #if CONFIG_OS #define USBD_FB_TRX_TIMEOUT (0) #else #define USBD_FB_TRX_TIMEOUT (1500) #endif #define USBD_FB_COM_NBR_MAX (USBD_FB_CFG_MAX_NBR_DEV * \ USBD_FB_CFG_MAX_NBR_CFG) #define USBD_FB_SUBCLASS_CODE 0x42u #define USBD_FB_PROTOCOL_CODE 3u /* ********************************************************************************************************* * LOCAL CONSTANTS ********************************************************************************************************* */ /* ********************************************************************************************************* * LOCAL DATA TYPES ********************************************************************************************************* */ /* ********************************************************************************************************* * FORWARD DECLARATIONS ********************************************************************************************************* */ typedef struct usbd_fb_ctrl USBD_FB_CTRL; /* ********************************************************************************************************* * FASTBOOT STATES ********************************************************************************************************* */ typedef enum usbd_fb_state { USBD_FB_STATE_NONE = 0, USBD_FB_STATE_INIT, USBD_FB_STATE_CFG } USBD_FB_STATE; typedef enum usbd_fb_comm_state { USBD_FB_COMM_STATE_NONE = 0, USBD_FB_COMM_STATE_RX_WAIT, USBD_FB_COMM_STATE_RX_HANDLE, USBD_FB_COMM_STATE_COMMAND, USBD_FB_COMM_STATE_COMPLETE, USBD_FB_COMM_STATE_ERROR, } USBD_FB_COMM_STATE; /* ********************************************************************************************************** * FASTBOOT EP REQUIREMENTS DATA TYPE ********************************************************************************************************** */ typedef struct usbd_fb_comm { /* ---------- FB COMMUNICATION INFORMATION ----------- */ USBD_FB_CTRL *CtrlPtr; /* Ptr to ctrl information. */ CPU_INT08U DataBulkInEpAddr; CPU_INT08U DataBulkOutEpAddr; USBD_FB_COMM_STATE CommState; /* comm state of the FB device. */ CPU_INT32U RxLen; } USBD_FB_COMM; struct usbd_fb_ctrl { /* ------------- FB CONTROL INFORMATION -------------- */ CPU_INT08U DevNbr; /* FB dev nbr. */ USBD_FB_STATE State; /* FB dev state. */ USBD_FB_COMM *CommPtr; /* FB comm info ptr. */ CPU_INT16U CmdLen; CPU_INT16U VarLen; const USBD_FB_CMD *CmdArray; const USBD_FB_VAR *VarArray; void *DLAddr; CPU_INT32U DLMax; CPU_INT32U DLSize; char *MSG_BufPtr; }; /* ********************************************************************************************************** * LOCAL TABLES ********************************************************************************************************** */ /* ********************************************************************************************************** * LOCAL GLOBAL VARIABLES ********************************************************************************************************** */ /* FB instance array. */ static USBD_FB_CTRL USBD_FBCtrlTbl[USBD_FB_CFG_MAX_NBR_DEV]; static CPU_INT08U USBD_FBCtrlNbrNext; /* FB comm array. */ static USBD_FB_COMM USBD_FBCommTbl[USBD_FB_COM_NBR_MAX]; static CPU_INT16U USBD_FBCommNbrNext; #if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED) static const CPU_INT08U USBD_FB_MsExtPropName[] = { 'L', 0, 'a', 0, 'b', 0, 'e', 0, 'l', 0, 0, 0 }; static const CPU_INT08U USBD_FB_MsExtPropStr[] = { 'F', 0, 'a', 0, 's', 0, 't', 0, 'B', 0, 'o', 0, 'o', 0, 't', 0, ' ', 0, 'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 0 , 0 }; static USBD_MS_OS_EXT_PROPERTY USBD_FB_MsExtProp[] = { { USBD_MS_OS_PROPERTY_TYPE_REG_SZ, USBD_FB_MsExtPropName, sizeof(USBD_FB_MsExtPropName), USBD_FB_MsExtPropStr, sizeof(USBD_FB_MsExtPropStr) } }; #endif /* ********************************************************************************************************* * LOCAL MACRO'S ********************************************************************************************************* */ #define fbd_err(x, ...) ssdk_printf(SSDK_CRIT, "[FB]" x "\r\n", ##__VA_ARGS__) #define fbd_log(x, ...) ssdk_printf(SSDK_INFO, "[FB]" x "\r\n", ##__VA_ARGS__) /* ********************************************************************************************************* * LOCAL FUNCTION PROTOTYPES ********************************************************************************************************* */ static void USBD_FB_Conn ( CPU_INT08U dev_nbr, CPU_INT08U cfg_nbr, void *p_if_class_arg); static void USBD_FB_Disconn ( CPU_INT08U dev_nbr, CPU_INT08U cfg_nbr, void *p_if_class_arg); #if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED) static CPU_INT08U USBD_FB_MS_GetCompatID ( CPU_INT08U dev_nbr, CPU_INT08U *p_sub_compat_id_ix, void *p_if_class_arg); static CPU_INT08U USBD_FB_MS_GetExtPropertyTbl( CPU_INT08U dev_nbr, USBD_MS_OS_EXT_PROPERTY **pp_ext_property_tbl, void *p_if_class_arg); #endif /* ********************************************************************************************************* * FastBoot CLASS DRIVER ********************************************************************************************************* */ USBD_CLASS_DRV USBD_FB_Drv = { USBD_FB_Conn, USBD_FB_Disconn, DEF_NULL, #if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED) .MS_GetCompatID = USBD_FB_MS_GetCompatID, .MS_GetExtPropertyTbl = USBD_FB_MS_GetExtPropertyTbl, #endif }; /* ********************************************************************************************************* * LOCAL CONFIGURATION ERRORS ********************************************************************************************************* */ /* ********************************************************************************************************* ********************************************************************************************************* * GLOBAL FUNCTIONS ********************************************************************************************************* ********************************************************************************************************* */ /* ********************************************************************************************************** * USBD_FB_Init() * * Description : Initialize internal structures and variables used by the Fast Boot Class * Bulk Only Transport. * * Argument(s) : p_err Pointer to variable that will receive the return error code from this function : * * USBD_ERR_NONE FB class successfully initialized. * * Return(s) : None. * * Note(s) : None. ********************************************************************************************************** */ void USBD_FB_Init (USBD_ERR *p_err) { CPU_INT08U ix; USBD_FB_CTRL *p_ctrl; USBD_FB_COMM *p_comm; LIB_ERR err_lib; #if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */ if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */ CPU_SW_EXCEPTION(;); } #endif for (ix = 0u; ix < USBD_FB_CFG_MAX_NBR_DEV; ix++) { /* Init FB class struct. */ p_ctrl = &USBD_FBCtrlTbl[ix]; p_ctrl->State = USBD_FB_STATE_NONE; p_ctrl->CommPtr = (USBD_FB_COMM *)0; p_ctrl->CmdLen = 0; p_ctrl->VarLen = 0; p_ctrl->DLAddr = NULL; p_ctrl->DLMax = 0; p_ctrl->DLSize = 0; p_ctrl->MSG_BufPtr = (char *)Mem_HeapAlloc( USBD_FB_MSG_BUF_LEN, CONFIG_ARCH_CACHE_LINE, NULL, &err_lib); if (err_lib != LIB_MEM_ERR_NONE) { *p_err = USBD_ERR_ALLOC; return; } } for (ix = 0u; ix < USBD_FB_COM_NBR_MAX; ix++) { /* Init FB class EP tbl. */ p_comm = &USBD_FBCommTbl[ix]; p_comm->DataBulkInEpAddr = USBD_EP_ADDR_NONE; p_comm->DataBulkOutEpAddr = USBD_EP_ADDR_NONE; p_comm->CommState = USBD_FB_COMM_STATE_NONE; p_comm->CtrlPtr = (USBD_FB_CTRL *)0; } USBD_FBCtrlNbrNext = 0u; USBD_FBCommNbrNext = 0u; #if CONFIG_OS USBD_FB_OS_Init(p_err); /* Init FB OS layer. */ #else *p_err = USBD_ERR_NONE; #endif return; } /* ********************************************************************************************************* * USBD_FB_Add() * * Description : Add a new instance of the Fast Boot Class. * * Argument(s) : p_err Pointer to variable that will receive the return error code from this function : * * USBD_ERR_NONE FB class instance successfully added. * * USBD_ERR_ALLOC No more class instance structure available. * * Return(s) : Class instance number, if NO error(s). * * USBD_CLASS_NBR_NONE, otherwise. * * Note(s) : none. ********************************************************************************************************* */ CPU_INT08U USBD_FB_Add (USBD_ERR *p_err) { CPU_INT08U fb_nbr; CPU_SR_ALLOC(); #if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */ if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */ CPU_SW_EXCEPTION(0); } #endif CPU_CRITICAL_ENTER(); fb_nbr = USBD_FBCtrlNbrNext; /* Alloc new FB class. */ if (fb_nbr >= USBD_FB_CFG_MAX_NBR_DEV) { CPU_CRITICAL_EXIT(); *p_err = USBD_ERR_FB_INSTANCE_ALLOC; return (USBD_CLASS_NBR_NONE); } USBD_FBCtrlNbrNext++; CPU_CRITICAL_EXIT(); *p_err = USBD_ERR_NONE; return (fb_nbr); } /* ********************************************************************************************************* * USBD_FB_CfgAdd() * * Description : Add an existing FB instance to the specified configuration and device. * * Argument(s) : class_nbr FB instance number. * * dev_nbr Device number. * * cfg_nbr Configuration index to add existing FB interface to. * * p_err Pointer to variable that will receive the return error code from this function: * USBD_ERR_NONE FB class instance successfully added. * USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_nbr'/ * 'dev_nbr'/'intr_en'/'interval'. * USBD_ERR_ALLOC No more class communication structure available. * ---------- RETURNED BY USBD_IF_Add() : ---------- * USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'class_desc'/ * 'class_desc_size'. * USBD_ERR_NULL_PTR Argument 'p_class_drv'/'p_class_drv->Conn'/ * 'p_class_drv->Disconn' passed a NULL * pointer. * USBD_ERR_DEV_INVALID_NBR Invalid device number. * USBD_ERR_DEV_INVALID_STATE Invalid device state. * USBD_ERR_CFG_INVALID_NBR Invalid configuration number. * USBD_ERR_IF_ALLOC Interfaces NOT available. * USBD_ERR_IF_ALT_ALLOC Interface alternate settings NOT available. * * ---------- RETURNED BY USBD_BulkAdd() : ---------- * USBD_ERR_INVALID_ARG Invalid argument(s) passed to 'max_pkt_len'. * * USBD_ERR_DEV_INVALID_NBR Invalid device number. * USBD_ERR_CFG_INVALID_NBR Invalid configuration number. * USBD_ERR_IF_INVALID_NBR Invalid interface number. * USBD_ERR_EP_NONE_AVAIL Physical endpoint NOT available. * USBD_ERR_EP_ALLOC Endpoints NOT available. * * Return(s) : DEF_YES, if FB instance added to USB device configuration successfully. * * DEF_NO, otherwise * * Note(s) : (1) USBD_FB_CfgAdd() basically adds an Interface descriptor and its associated Endpoint * descriptor(s) to the Configuration descriptor. One call to USBD_FB_CfgAdd() builds * the Configuration descriptor corresponding to a FB device with the following format: * * Configuration Descriptor * |-- Interface Descriptor (FB) * |-- Endpoint Descriptor (Bulk OUT) * |-- Endpoint Descriptor (Bulk IN) * * If USBD_FB_CfgAdd() is called several times from the application, it allows to create * multiple instances and multiple configurations. ********************************************************************************************************* */ CPU_BOOLEAN USBD_FB_CfgAdd(CPU_INT08U class_nbr, CPU_INT08U dev_nbr, CPU_INT08U cfg_nbr, USBD_ERR *p_err) { USBD_FB_CTRL *p_ctrl; USBD_FB_COMM *p_comm; USBD_DEV_CFG *p_dev_cfg; CPU_INT08U if_nbr; CPU_INT08U ep_addr; CPU_INT16U comm_nbr; CPU_SR_ALLOC(); #if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) /* ---------------- VALIDATE ARGUMENTS ---------------- */ if (p_err == (USBD_ERR *)0) { /* Validate error ptr. */ CPU_SW_EXCEPTION(DEF_NO); } #endif if (class_nbr >= USBD_FBCtrlNbrNext) { *p_err = USBD_ERR_CLASS_INVALID_NBR; return (DEF_NO); } p_dev_cfg = USBD_DevCfgGet(dev_nbr, p_err); if (*p_err != USBD_ERR_NONE) { return (DEF_NO); } if (p_dev_cfg->SerialNbrStrPtr == (CPU_CHAR *)0) { *p_err = USBD_ERR_INVALID_ARG; return (DEF_NO); } p_ctrl = &USBD_FBCtrlTbl[class_nbr]; CPU_CRITICAL_ENTER(); p_ctrl->DevNbr = dev_nbr; comm_nbr = USBD_FBCommNbrNext; if (comm_nbr >= USBD_FB_COM_NBR_MAX) { USBD_FBCtrlNbrNext--; CPU_CRITICAL_EXIT(); *p_err = USBD_ERR_FB_INSTANCE_ALLOC; return (DEF_NO); } USBD_FBCommNbrNext++; CPU_CRITICAL_EXIT(); p_comm = &USBD_FBCommTbl[comm_nbr]; if_nbr = USBD_IF_Add ( dev_nbr, /* Add FB IF desc to cfg desc. */ cfg_nbr, &USBD_FB_Drv, (void *)p_comm, (void *)0, USBD_CLASS_CODE_VENDOR_SPECIFIC, USBD_FB_SUBCLASS_CODE, USBD_FB_PROTOCOL_CODE, "USB Fast Boot Interface", p_err); if (*p_err != USBD_ERR_NONE) { return (DEF_NO); } ep_addr = USBD_BulkAdd ( dev_nbr, /* Add bulk-IN EP desc. */ cfg_nbr, if_nbr, 0u, DEF_YES, 0u, p_err); if (*p_err != USBD_ERR_NONE) { return (DEF_NO); } p_comm->DataBulkInEpAddr = ep_addr; /* Store bulk-IN EP address. */ ep_addr = USBD_BulkAdd ( dev_nbr, /* Add bulk-OUT EP desc. */ cfg_nbr, if_nbr, 0u, DEF_NO, 0u, p_err); if (*p_err != USBD_ERR_NONE) { return (DEF_NO); } p_comm->DataBulkOutEpAddr = ep_addr; /* Store bulk-OUT EP address. */ CPU_CRITICAL_ENTER(); p_ctrl->State = USBD_FB_STATE_INIT; /* Set class instance to init state. */ p_ctrl->DevNbr = dev_nbr; p_ctrl->CommPtr = (USBD_FB_COMM *)0; CPU_CRITICAL_EXIT(); p_comm->CtrlPtr = p_ctrl; *p_err = USBD_ERR_NONE; return (DEF_YES); } CPU_BOOLEAN USBD_FB_SetDL( CPU_INT08U class_nbr, void *download_addr, CPU_INT32U download_max, USBD_ERR *p_err) { USBD_FB_CTRL *p_ctrl; #if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) if (!p_err) { CPU_SW_EXCEPTION(DEF_NO); } #endif if (!download_addr || !download_max) { *p_err = USBD_ERR_INVALID_ARG; return (DEF_NO); } if (class_nbr >= USBD_FBCtrlNbrNext) { *p_err = USBD_ERR_CLASS_INVALID_NBR; return (DEF_NO); } p_ctrl = &USBD_FBCtrlTbl[class_nbr]; p_ctrl->DLAddr = download_addr; p_ctrl->DLMax = download_max; *p_err = USBD_ERR_NONE; return (DEF_YES); } CPU_BOOLEAN USBD_FB_SetCmdTbl( CPU_INT08U class_nbr, const USBD_FB_CMD *cmd_tbl, CPU_INT16U cmd_len, USBD_ERR *p_err) { CPU_INT16U ix; USBD_FB_CTRL *p_ctrl; CPU_SR_ALLOC(); #if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) if (!p_err) { CPU_SW_EXCEPTION(DEF_NO); } #endif if (!cmd_tbl || !cmd_len) { *p_err = USBD_ERR_INVALID_ARG; return (DEF_NO); } for (ix = 0; ix < cmd_len; ix++) { if (!cmd_tbl[ix].cmd || !*cmd_tbl[ix].cmd || !cmd_tbl[ix].handler) { *p_err = USBD_ERR_INVALID_ARG; return (DEF_NO); } } if (class_nbr >= USBD_FBCtrlNbrNext) { *p_err = USBD_ERR_CLASS_INVALID_NBR; return (DEF_NO); } p_ctrl = &USBD_FBCtrlTbl[class_nbr]; CPU_CRITICAL_ENTER(); p_ctrl->CmdLen = cmd_len; p_ctrl->CmdArray = cmd_tbl; CPU_CRITICAL_EXIT(); *p_err = USBD_ERR_NONE; return (DEF_YES); } CPU_BOOLEAN USBD_FB_SetVarTbl( CPU_INT08U class_nbr, const USBD_FB_VAR *var_tbl, CPU_INT16U var_len, USBD_ERR *p_err) { CPU_INT16U ix; USBD_FB_CTRL *p_ctrl; CPU_SR_ALLOC(); #if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) if (!p_err) { CPU_SW_EXCEPTION(DEF_NO); } #endif if (!var_tbl || !var_len) { *p_err = USBD_ERR_INVALID_ARG; return (DEF_NO); } for (ix = 0; ix < var_len; ix++) { if (!var_tbl[ix].name || !*var_tbl[ix].name || !var_tbl[ix].value || !*var_tbl[ix].value) { *p_err = USBD_ERR_INVALID_ARG; return (DEF_NO); } } if (class_nbr >= USBD_FBCtrlNbrNext) { *p_err = USBD_ERR_CLASS_INVALID_NBR; return (DEF_NO); } p_ctrl = &USBD_FBCtrlTbl[class_nbr]; CPU_CRITICAL_ENTER(); p_ctrl->VarLen = var_len; p_ctrl->VarArray = var_tbl; CPU_CRITICAL_EXIT(); *p_err = USBD_ERR_NONE; return (DEF_YES); } /* ********************************************************************************************************* * USBD_FB_IsConn() * * Description : Get the FB connection state of the device. * * Argument(s) : class_nbr FB instance number. * * Return(s) : DEF_YES, if FB class is connected. * * DEF_NO, otherwise. * * Note(s) : None. ********************************************************************************************************* */ CPU_BOOLEAN USBD_FB_IsConn (CPU_INT08U class_nbr) { USBD_FB_CTRL *p_ctrl; USBD_DEV_STATE state; USBD_ERR err; #if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED) if (class_nbr >= USBD_FBCtrlNbrNext) { return (DEF_NO); } #endif p_ctrl = &USBD_FBCtrlTbl[class_nbr]; if (p_ctrl->CommPtr == (USBD_FB_COMM *)0) { return (DEF_NO); } state = USBD_DevStateGet(p_ctrl->DevNbr, &err); /* Get dev state. */ if ((err == USBD_ERR_NONE ) && /* Return true if dev state is cfg & FB state is cfg. */ (state == USBD_DEV_STATE_CONFIGURED) && (p_ctrl->State == USBD_FB_STATE_CFG )) { return (DEF_YES); } else { return (DEF_NO); } } #if CONFIG_OS /* ********************************************************************************************************** * USBD_FB_TaskHandler() * * Description : This function is used to handle FB transfers. * * Argument(s) : class_nbr FB instance number. * * Return(s) : none. * * Note(s) : none. ********************************************************************************************************** */ void USBD_FB_TaskHandler (CPU_INT08U class_nbr) { USBD_ERR err; USBD_ERR os_err; USBD_FB_CTRL *p_ctrl; USBD_FB_COMM *p_comm; CPU_BOOLEAN conn; CPU_INT32U xfer_len; CPU_DATA ix; unsigned char fb_cls_nbr; USBD_FB_CMD_HDL cmd_hdl; fb_cls_nbr = class_nbr; p_ctrl = &USBD_FBCtrlTbl[class_nbr]; while (DEF_TRUE) { conn = USBD_FB_IsConn(class_nbr); if (conn != DEF_YES) { fbd_log("fastboot: offline!"); /* Wait till FB state and dev state is connected. */ USBD_FB_OS_EnumSignalPend((CPU_INT16U)0, &os_err); } p_comm = p_ctrl->CommPtr; if (p_comm != (USBD_FB_COMM *)0) { xfer_len = USBD_BulkRx (p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, p_ctrl->MSG_BufPtr, USBD_FB_MSG_BUF_LEN, 0, &err); if ((err != USBD_ERR_NONE) || (xfer_len == 0)) { fbd_err("get fastboot command err=%d", err); continue; } if (xfer_len == USBD_FB_MSG_BUF_LEN) p_ctrl->MSG_BufPtr[xfer_len - 1] = 0; else p_ctrl->MSG_BufPtr[xfer_len] = 0; fbd_log("fastboot cmd: %s", p_ctrl->MSG_BufPtr); p_comm->CommState = USBD_FB_COMM_STATE_COMMAND; cmd_hdl = NULL; for (ix = 0; ix < p_ctrl->CmdLen; ix++) { if (!memcmp(p_ctrl->MSG_BufPtr, p_ctrl->CmdArray[ix].cmd, strlen(p_ctrl->CmdArray[ix].cmd))) { cmd_hdl = p_ctrl->CmdArray[ix].handler; break; } } if (cmd_hdl) { cmd_hdl(&fb_cls_nbr, p_ctrl->MSG_BufPtr + strlen(p_ctrl->CmdArray[ix].cmd), p_ctrl->DLAddr, p_ctrl->DLSize); if (p_comm->CommState == USBD_FB_COMM_STATE_COMMAND) { USBD_FB_Fail(&fb_cls_nbr, "unknown reason"); } } else { USBD_FB_Fail(&fb_cls_nbr, "unknown command"); } } } } #else static void USBD_FB_RxDone (CPU_INT08U dev_nbr, CPU_INT08U ep_addr, void *p_buf, CPU_INT32U buf_len, CPU_INT32U xfer_len, void *p_arg, USBD_ERR err) { USBD_FB_COMM *p_comm; p_comm = p_arg; if ((err != USBD_ERR_NONE) || (xfer_len == 0)) { fbd_err("get fastboot command err=%d", err); p_comm->CommState = USBD_FB_COMM_STATE_NONE; return; } p_comm->RxLen = xfer_len; p_comm->CommState = USBD_FB_COMM_STATE_RX_HANDLE; return; } /* ********************************************************************************************************** * USBD_FB_TaskLoop() * * Description : This function is used to handle FB transfers. * * Argument(s) : class_nbr FB instance number. * * Return(s) : none. * * Note(s) : none. ********************************************************************************************************** */ void USBD_FB_TaskLoop (CPU_INT08U class_nbr) { USBD_ERR err; USBD_FB_CTRL *p_ctrl; USBD_FB_COMM *p_comm; CPU_BOOLEAN conn; CPU_INT32U xfer_len; CPU_DATA ix; unsigned char fb_cls_nbr; USBD_FB_CMD_HDL cmd_hdl; fb_cls_nbr = class_nbr; p_ctrl = &USBD_FBCtrlTbl[class_nbr]; conn = USBD_FB_IsConn(class_nbr); if (conn != DEF_YES) { return; } p_comm = p_ctrl->CommPtr; switch (p_comm->CommState) { case USBD_FB_COMM_STATE_NONE: USBD_BulkRxAsync (p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, p_ctrl->MSG_BufPtr, USBD_FB_MSG_BUF_LEN, USBD_FB_RxDone, p_comm, &err); if (err != USBD_ERR_NONE) { fbd_err("get fastboot command err=%d", err); break; } p_comm->CommState = USBD_FB_COMM_STATE_RX_WAIT; break; case USBD_FB_COMM_STATE_RX_HANDLE: xfer_len = p_comm->RxLen; if (xfer_len == USBD_FB_MSG_BUF_LEN) p_ctrl->MSG_BufPtr[xfer_len - 1] = 0; else p_ctrl->MSG_BufPtr[xfer_len] = 0; fbd_log("fastboot cmd: %s", p_ctrl->MSG_BufPtr); p_comm->CommState = USBD_FB_COMM_STATE_COMMAND; cmd_hdl = NULL; for (ix = 0; ix < p_ctrl->CmdLen; ix++) { if (!memcmp(p_ctrl->MSG_BufPtr, p_ctrl->CmdArray[ix].cmd, strlen(p_ctrl->CmdArray[ix].cmd))) { cmd_hdl = p_ctrl->CmdArray[ix].handler; break; } } if (cmd_hdl) { cmd_hdl(&fb_cls_nbr, p_ctrl->MSG_BufPtr + strlen(p_ctrl->CmdArray[ix].cmd), p_ctrl->DLAddr, p_ctrl->DLSize); if (p_comm->CommState == USBD_FB_COMM_STATE_COMMAND) { USBD_FB_Fail(&fb_cls_nbr, "unknown reason"); } } else { USBD_FB_Fail(&fb_cls_nbr, "unknown command"); } p_comm->CommState = USBD_FB_COMM_STATE_NONE; break; default: break; } } #endif /* ********************************************************************************************************** ********************************************************************************************************** * LOCAL FUNCTIONS ********************************************************************************************************** ********************************************************************************************************** */ /* ********************************************************************************************************* * USBD_FB_Conn() * * Description : Notify class that configuration is active. * * Argument(s) : dev_nbr Device number. * * cfg_nbr Configuration index to add the interface to. * * p_if_class_arg Pointer to class argument. * * Return(s) : None. * * Note(s) : None. ********************************************************************************************************* */ static void USBD_FB_Conn (CPU_INT08U dev_nbr, CPU_INT08U cfg_nbr, void *p_if_class_arg) { USBD_FB_COMM *p_comm; #if CONFIG_OS USBD_ERR os_err; #endif CPU_SR_ALLOC(); (void)dev_nbr; (void)cfg_nbr; p_comm = (USBD_FB_COMM *)p_if_class_arg; CPU_CRITICAL_ENTER(); p_comm->CtrlPtr->CommPtr = p_comm; p_comm->CtrlPtr->State = USBD_FB_STATE_CFG; /* Set initial FB state to cfg. */ p_comm->CommState = USBD_FB_COMM_STATE_NONE; /* Set FB comm state to none. */ CPU_CRITICAL_EXIT(); #if CONFIG_OS USBD_FB_OS_EnumSignalPost(&os_err); #endif fbd_log("Conn"); } /* ********************************************************************************************************* * USBD_FB_Disconn() * * Description : Notify class that configuration is not active. * * Argument(s) : dev_nbr Device number. * * cfg_nbr Configuration index to add the interface. * * p_if_class_arg Pointer to class argument. * * Return(s) : None. * * Note(s) : None. ********************************************************************************************************* */ static void USBD_FB_Disconn ( CPU_INT08U dev_nbr, CPU_INT08U cfg_nbr, void *p_if_class_arg) { USBD_FB_COMM *p_comm; CPU_SR_ALLOC(); (void)dev_nbr; (void)cfg_nbr; p_comm = (USBD_FB_COMM *)p_if_class_arg; CPU_CRITICAL_ENTER(); p_comm->CtrlPtr->CommPtr = (USBD_FB_COMM *)0; p_comm->CtrlPtr->State = USBD_FB_STATE_INIT; /* Set FB state to init. */ p_comm->CommState = USBD_FB_COMM_STATE_NONE; /* Set FB comm state to none. */ CPU_CRITICAL_EXIT(); fbd_log("Disconn"); } #if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED) /* ********************************************************************************************************* * USBD_FB_MS_GetCompatID() * * Description : Returns Microsoft descriptor compatible ID. * * Argument(s) : dev_nbr Device number. * * p_sub_compat_id_ix Pointer to variable that will receive subcompatible ID. * * Return(s) : Compatible ID. * * DEF_FAIL, otherwise. * * Note(s) : None. ********************************************************************************************************* */ static CPU_INT08U USBD_FB_MS_GetCompatID (CPU_INT08U dev_nbr, CPU_INT08U *p_sub_compat_id_ix, void *p_if_class_arg) { (void)dev_nbr; (void)p_if_class_arg; *p_sub_compat_id_ix = USBD_MS_OS_SUBCOMPAT_ID_NULL; return (USBD_MS_OS_COMPAT_ID_WINUSB); } /* ********************************************************************************************************* * USBD_FB_MS_GetExtPropertyTbl() * * Description : Returns Microsoft descriptor extended properties table. * * Argument(s) : dev_nbr Device number. * * pp_ext_property_tbl Pointer to variable that will receive the Microsoft extended * properties table. * * Return(s) : Number of Microsoft extended properties in table. * * Note(s) : None. ********************************************************************************************************* */ static CPU_INT08U USBD_FB_MS_GetExtPropertyTbl(CPU_INT08U dev_nbr, USBD_MS_OS_EXT_PROPERTY **pp_ext_property_tbl, void *p_if_class_arg) { (void)dev_nbr; (void)p_if_class_arg; *pp_ext_property_tbl = USBD_FB_MsExtProp; return sizeof(USBD_FB_MsExtProp) / sizeof(USBD_FB_MsExtProp[0]); } #endif void USBD_FB_Ack( unsigned char *fb_cls, const char *code, const char *reason) { USBD_FB_CTRL *p_ctrl; USBD_FB_COMM *p_comm; int str_len; USBD_ERR err; if (!fb_cls || (*fb_cls >= USBD_FBCtrlNbrNext)) { return; } p_ctrl = &USBD_FBCtrlTbl[*fb_cls]; p_comm = p_ctrl->CommPtr; if (!p_comm || (p_comm->CommState != USBD_FB_COMM_STATE_COMMAND)) { return; } if (reason == 0) { reason = ""; } str_len = snprintf((char *)p_ctrl->MSG_BufPtr, MAX_RSP_SIZE, "%s%s", code, reason); if (str_len >= MAX_RSP_SIZE) str_len = MAX_RSP_SIZE - 1; p_comm->CommState = USBD_FB_COMM_STATE_COMPLETE; (void)USBD_BulkTx(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, p_ctrl->MSG_BufPtr, str_len, USBD_FB_TRX_TIMEOUT, DEF_NO, &err); if (err != USBD_ERR_NONE) p_comm->CommState = USBD_FB_COMM_STATE_ERROR; } void USBD_FB_Info(unsigned char *fb_cls, const char *reason) { USBD_FB_CTRL *p_ctrl; USBD_FB_COMM *p_comm; int str_len; USBD_ERR err; if (!fb_cls || (*fb_cls >= USBD_FBCtrlNbrNext)) { return; } p_ctrl = &USBD_FBCtrlTbl[*fb_cls]; p_comm = p_ctrl->CommPtr; if (!reason || !p_comm || (p_comm->CommState != USBD_FB_COMM_STATE_COMMAND)) { return; } str_len = snprintf((char *)p_ctrl->MSG_BufPtr, MAX_RSP_SIZE, "INFO%s", reason); if (str_len >= MAX_RSP_SIZE) str_len = MAX_RSP_SIZE - 1; (void)USBD_BulkTx(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, p_ctrl->MSG_BufPtr, str_len, USBD_FB_TRX_TIMEOUT, DEF_NO, &err); if (err != USBD_ERR_NONE) p_comm->CommState = USBD_FB_COMM_STATE_ERROR; } void USBD_FB_Fail(unsigned char *fb_cls, const char *reason) { USBD_FB_Ack(fb_cls, "FAIL", reason); } void USBD_FB_Okay(unsigned char *fb_cls, const char *info) { USBD_FB_Ack(fb_cls, "OKAY", info); } static void USBD_FB_GetAllVar(unsigned char *fb_cls, USBD_FB_CTRL *p_ctrl) { CPU_DATA ix; char getvar_all[USBD_FB_VAR_INFO_LEN]; for (ix = 0; ix < p_ctrl->VarLen; ix++) { snprintf( getvar_all, USBD_FB_VAR_INFO_LEN, "%s:%s", p_ctrl->VarArray[ix].name, p_ctrl->VarArray[ix].value); USBD_FB_Info(fb_cls, getvar_all); } USBD_FB_Okay(fb_cls, ""); } void USBD_FB_CmdGetVar( unsigned char *fb_cls, const char *arg, void *data, unsigned sz) { USBD_FB_CTRL *p_ctrl; CPU_DATA ix; if (!fb_cls || (*fb_cls >= USBD_FBCtrlNbrNext)) { return; } p_ctrl = &USBD_FBCtrlTbl[*fb_cls]; if (!strcmp("all", arg)) { USBD_FB_GetAllVar(fb_cls, p_ctrl); return; } for (ix = 0; ix < p_ctrl->VarLen; ix++) { if (!strcmp(p_ctrl->VarArray[ix].name, arg)) { USBD_FB_Okay(fb_cls, p_ctrl->VarArray[ix].value); return; } } USBD_FB_Okay(fb_cls, ""); } void USBD_FB_CmdFlash( unsigned char *fb_cls, const char *arg, void *data, unsigned sz) { fbd_log("Flash %s", arg); USBD_FB_Okay(fb_cls, ""); } static CPU_INT32U USBD_FB_ReadLarge(CPU_INT08U dev_nbr, CPU_INT08U ep_addr, void *p_buf, CPU_INT32U buf_len, USBD_ERR *p_err) { CPU_INT32U usbd_rd_limit; CPU_INT32U rd_len; CPU_INT32U ret_len; CPU_INT32U already_rd; usbd_rd_limit = USBD_FB_RX_LIMIT; already_rd = 0; while (buf_len > 0) { rd_len = (buf_len > usbd_rd_limit) ? usbd_rd_limit : buf_len; ret_len = USBD_BulkRx ( dev_nbr, ep_addr, p_buf, rd_len, USBD_FB_TRX_TIMEOUT, p_err); if ((*p_err != USBD_ERR_NONE) || (ret_len != rd_len)) { fbd_err("read large data err=%d", *p_err); return already_rd; } p_buf = (uint8_t *)p_buf + rd_len; buf_len -= rd_len; already_rd += rd_len; } *p_err = USBD_ERR_NONE; return already_rd; } void USBD_FB_CmdDownload( unsigned char *fb_cls, const char *arg, void *data, unsigned sz) { char *tmp; unsigned long len; int str_len; CPU_INT32U rd_len; USBD_FB_CTRL *p_ctrl; USBD_FB_COMM *p_comm; USBD_ERR err; if (!fb_cls || (*fb_cls >= USBD_FBCtrlNbrNext)) { return; } p_ctrl = &USBD_FBCtrlTbl[*fb_cls]; p_comm = p_ctrl->CommPtr; if (!p_comm || (p_comm->CommState != USBD_FB_COMM_STATE_COMMAND)) { return; } p_ctrl->DLSize = 0; errno = 0; len = strtoul(arg, &tmp, 16); if (*tmp || ((len == ULONG_MAX) && errno) || !len) { USBD_FB_Fail(fb_cls, "data len incorrect"); return; } if (len > p_ctrl->DLMax) { USBD_FB_Fail(fb_cls, "data too large"); return; } str_len = snprintf((char *)p_ctrl->MSG_BufPtr, MAX_RSP_SIZE, "DATA%08x", (CPU_INT32U)len); (void)USBD_BulkTx(p_ctrl->DevNbr, p_comm->DataBulkInEpAddr, p_ctrl->MSG_BufPtr, str_len, USBD_FB_TRX_TIMEOUT, DEF_NO, &err); if (err != USBD_ERR_NONE){ p_comm->CommState = USBD_FB_COMM_STATE_ERROR; fbd_err("download tx data err=%d", err); return; } rd_len = USBD_FB_ReadLarge( p_ctrl->DevNbr, p_comm->DataBulkOutEpAddr, p_ctrl->DLAddr, len, &err); if ((err != USBD_ERR_NONE) || (rd_len != len)) { fbd_err("read download data err=%d", err); p_comm->CommState = USBD_FB_COMM_STATE_ERROR; return; } p_ctrl->DLSize = len; USBD_FB_Okay(fb_cls, ""); }