/******************************************************************************* * Copyright 2019-2020 Microchip FPGA Embedded Systems Solutions. * * SPDX-License-Identifier: MIT * * PolarFire SoC MSS USB Driver Stack * USB Logical Layer (USB-LL) * USBH driver * * USBH driver implementation: * This source file implements the common functionality of USB host mode * */ #include "mpfs_hal/mss_hal.h" #include "mss_usb_common_cif.h" #include "mss_usb_common_reg_io.h" #include "mss_usb_host.h" #include "mss_usb_host_cif.h" #include "mss_usb_host_reg_io.h" #include "mss_usb_std_def.h" #ifdef __cplusplus extern "C" { #endif #ifdef MSS_USB_HOST_ENABLED /***************************************************************************//** Constant values internally used by USBH driver. */ #define MSS_USB_1ms_DIVISION_VAL 1000u /*In miliSeconds*/ #define MSS_USB_TARGET_RESET_DELAY 20u /*In miliSeconds*/ #define MSS_USB_TARGET_VRISE_DELAY 20u /*In miliSeconds*/ #define MSS_USB_TARGET_SUSPEND_DELAY 20u /*In miliSeconds*/ /***************************************************************************//** Types internally used by USBH driver. */ typedef enum enum_state { ENUM_IDLE, ENUM_GET_DEF_DEV_DESC, ENUM_WAIT_GET_DEF_DEV_DESC, ENUM_RESET_TDEV, ENUM_CLR_RESET_TDEV, ENUM_SET_TDEV_ADDR, ENUM_WAIT_SET_TDEV_ADDR, ENUM_GET_FUL_DEV_DESC, ENUM_WAIT_GET_FUL_DEV_DESC, ENUM_GET_DEF_CONFG_DESC, ENUM_WAIT_GET_DEF_CONFG_DESC, ENUM_GET_FUL_CONFG_DESC, ENUM_WAIT_GET_FUL_CONFG_DESC, ENUM_CHECK_TDEV_CLASS_SUPPORT, ENUM_SUCCESS, ENUM_ERROR, ENUM_PET_SET_CONFIG, ENUM_PET_WAIT_SET_CONFIG } mss_usb_enum_state_t; typedef enum host_state { HOST_IDLE, HOST_ROOT_TARGET_DETECTED, HOST_ROOT_TARGET_RESETING, HOST_ROOT_TARGET_ENUMERATING, HOST_ROOT_TARGET_ALLOCATE_CLASSD, HOST_ROOT_TARGET_SERVICING, HOST_ROOT_TARGET_ERROR } mss_usbh_state_t; typedef enum { TDEV_R, TDEV_RHP1, TDEV_RHP2 } tdev_id_t; /***************************************************************************//** Data structures internally used by USBH driver. */ typedef struct { uint8_t alloc_state; mss_usbh_class_cb_t* class_handle; } class_driver_info_t; /* This structure type is used to read device and config descriptor from pen drive. At a time only one descriptor is operated upon.*/ typedef struct { union { uint8_t da[20]; dev_desc_t ds; }dev_desc; union { /* Def_confg + First Interface Desc+AddressAlign */ uint8_t ca[20]; def_conf_desc_t cs; }conf_desc; int8_t desc_err_code; } tdev_desc_t; /****************************************************************************** Global variable declarations for this file (USBH Driver). */ /* Flags to indicate the Callback Events */ uint8_t volatile gh_tdev_connect_event = 0u; uint8_t volatile gh_tdev_discon_event = 0u; uint8_t volatile gh_cep_cb_event = 0u; /* Track the physically connected number of target devices */ tdev_id_t tdev_idx = TDEV_R; /* * Information about the connected target devices * Index of this array is tdev_idx i.e. physical location on SF2 receptacle. */ static mss_usbh_tdev_info_t g_tdev[1]; int8_t g_tdev_error_code = 0; /* * Holds the Target device descriptor information. * Currently we support only one device. Still an array is used so that future * support for multiple devices would be easy. * MPH:Number of connected target Devices. */ static tdev_desc_t g_tdev_desc[1]; /* General purpose milliseconds count */ volatile uint32_t ms = 0; /* Track current state of the Host */ static mss_usbh_state_t g_host_state = HOST_IDLE; /* Track current state of ENUMERATION FSM */ static mss_usb_enum_state_t g_enum_state = ENUM_IDLE; /* MSS USB has 4 Transmit, 4 Receive and one control Endpoint */ mss_usb_ep_t gh_tx_ep[5]; /*[0] ==> Control EP*/ mss_usb_ep_t gh_rx_ep[5]; /*[0] ==> Empty*/ /* Use this at time of class allocation after device is enumerated * Currently we support only one device.Hence one Class allocation, still an * array is used so that future support for multiple devices would be easy.*/ /* MPH:Number of registered classes */ static class_driver_info_t g_rcd[1]; static volatile uint8_t g_cep_xfr_result = MSS_USB_EP_XFR_SUCCESS; /* User application call-back handler */ static mss_usbh_user_cb_t* g_user_cb; /* Apart from this driver, Class drivers can also do a CEP transfers as per * need. This variable is to differentiate between transfers started by this * driver and CRP transfers started by Class driver. */ static volatile uint8_t g_internal_cep_xfr = 0u; extern volatile uint8_t txep_irq; /****************************************************************************** Private function declarations for this file (USBH driver). */ static void mss_usbh_enum_fsm(void); static void mss_usbh_control_xfr_fsm(void); /* Call-back functions used by Host CIF layer to communicate with this layer */ static void mss_usbh_ep_tx_complete_cb(uint8_t ep_num, uint8_t status); static void mss_usbh_ep_rx_cb(uint8_t ep_num, uint8_t status); static void mss_usbh_cep_cb(uint8_t status); static void mss_usbh_sof_cb(uint32_t frame_number); static void mss_usbh_connect_cb(mss_usb_device_speed_t target_speed, mss_usb_vbus_level_t vbus_level); static void mss_usbh_disconnect_cb(void); static void mss_usbh_vbus_err_cb(mss_usb_vbus_level_t vbus_level); static void mss_usbh_babble_err_cb(void); static void mss_usbh_session_request_cb(void); static void mss_usbh_dma_handler_cb(mss_usb_ep_num_t ep_num, mss_usb_dma_dir_t dma_dir, uint8_t status, uint32_t dma_addr_val); static int8_t host_enum_check_class_support(tdev_id_t tid); static int8_t validate_dev_desc(dev_desc_t* p_desc); static int8_t validate_def_conf_desc(def_conf_desc_t* p_desc); static void mss_usbh_fsm(void); static void mss_usbh_reset_enum_fsm(void); static void mss_usbh_start_enum_fsm(void); static mss_usb_enum_state_t mss_usbh_get_enum_fsm_state(void); static void mss_usbh_handle_discon_event(void); /* Host call-back functions */ mss_usbh_cb_t g_mss_usbh_cb = { mss_usbh_ep_tx_complete_cb, mss_usbh_ep_rx_cb, mss_usbh_cep_cb, mss_usbh_sof_cb, mss_usbh_connect_cb, mss_usbh_disconnect_cb, mss_usbh_vbus_err_cb, mss_usbh_babble_err_cb, mss_usbh_session_request_cb, mss_usbh_dma_handler_cb }; /******************************************************************************* * EXPORTED API Functions *******************************************************************************/ /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ void MSS_USBH_init ( mss_usbh_user_cb_t* app_cb ) { /* * By Default Prepare CEP for HS target. * Actual speed will be found in Enumeration FSM */ g_tdev[TDEV_R].addr = USB_DEFAULT_TARGET_ADDR; g_tdev[TDEV_R].speed = MSS_USB_DEVICE_HS; g_tdev[TDEV_R].state = MSS_USB_NOT_ATTACHED_STATE; g_tdev[TDEV_R].hub_addr = 0u; g_tdev[TDEV_R].hub_port = 0u; g_tdev[TDEV_R].hub_mtt = 0u; g_tdev[TDEV_R].tdev_maxpktsz0 = CEP_MAX_PKT_SIZE; g_tdev[TDEV_R].class_handle = 0u; g_user_cb = app_cb; /* Initialize host core interface layer */ MSS_USBH_CIF_init(); MSS_USBH_configure_control_pipe(TDEV_R); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ int8_t MSS_USBH_register_class_driver ( void* class_handle ) { volatile int8_t value = 0; g_rcd[0].alloc_state = 0u; if ((mss_usbh_class_cb_t*)0 != class_handle) { g_rcd[0].class_handle = (mss_usbh_class_cb_t*)class_handle; value = 0; } else { value = 1; } return(value); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ int8_t MSS_USBH_configure_control_pipe ( uint8_t target_addr ) { tdev_id_t tid = (tdev_id_t)0u; mss_usb_ep_t* cep_ptr = &gh_tx_ep[MSS_USB_CEP]; /* * Find the physical location of Target Device from Assigned address. * This will be used to call the correct call-back function assigned to this * address. * For multiple devices support using a HUB will be must. we intent to assign * non-zero device addresses starting with 1 (This will be HUB).This will be * done by this driver in mss_usbh_enum_fsm() function. This way array indexing * will be easier. But all this only when Multiple devices are supported. * Currently we assign 0x10 as the non-zero address for single connected device * and this will evaluate tdex_idx to 0. */ tid = (tdev_id_t)(target_addr & 0x03u); cep_ptr->tdev_idx = tid; cep_ptr->num = MSS_USB_CEP; cep_ptr->fifo_size = 64u; cep_ptr->max_pkt_size = g_tdev[tid].tdev_maxpktsz0; cep_ptr->num_usb_pkt = 1u; cep_ptr->state = MSS_USB_CEP_IDLE; /* Control Endpoint Config is fixed. NakLimit = MaxVal.*/ cep_ptr->interval = 32768u; MSS_USBH_CIF_cep_configure(cep_ptr); /* Type0:Default target speed */ MSS_USBH_CIF_cep_set_type0_reg(g_tdev[tid].speed); MSS_USBH_CIF_tx_ep_set_target_func_addr(MSS_USB_CEP,target_addr); return (0); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ int8_t MSS_USBH_configure_out_pipe ( uint8_t target_addr, uint8_t outpipe_num, uint8_t target_ep, uint16_t fifo_addr, uint16_t fifo_size, uint16_t max_pkt_size, uint8_t num_usb_pkt, uint8_t dma_enable, mss_usb_dma_channel_t dma_channel, mss_usb_xfr_type_t xfr_type, uint32_t add_zlp, uint32_t interval ) { mss_usb_ep_t* txep_ptr = &gh_tx_ep[outpipe_num]; /* TODO: Error check for all the parameters. */ tdev_id_t tid = (tdev_id_t)0u; tid = (tdev_id_t)(target_addr & 0x03u); txep_ptr->tdev_idx = tid; txep_ptr->num = (mss_usb_ep_num_t)outpipe_num; txep_ptr->dpb_enable = DPB_DISABLE; txep_ptr->fifo_size = fifo_size; txep_ptr->fifo_addr =fifo_addr; txep_ptr->dma_enable =dma_enable; txep_ptr->dma_channel =dma_channel; txep_ptr->max_pkt_size = max_pkt_size; txep_ptr->stall = 0u; txep_ptr->state = MSS_USB_EP_VALID; txep_ptr->xfr_type = xfr_type; txep_ptr->add_zlp = add_zlp; txep_ptr->num_usb_pkt = 1u; txep_ptr->buf_addr = 0u; txep_ptr->xfr_length = 0u; txep_ptr->xfr_count = 0u; txep_ptr->txn_length = 0u; txep_ptr->txn_count = 0u; txep_ptr->disable_ping = 1u; txep_ptr->req_pkt_n = 0u; txep_ptr->interval = interval; /* Configure MP Registers with Target Device informations */ MSS_USBH_CIF_tx_ep_mp_configure(outpipe_num, target_ep, target_addr, g_tdev[tid].hub_addr, g_tdev[tid].hub_port, g_tdev[tid].hub_mtt, g_tdev[tid].speed, interval, xfr_type ); MSS_USBH_CIF_tx_ep_configure(txep_ptr); return (0); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ int8_t MSS_USBH_configure_in_pipe ( uint8_t target_addr, uint8_t inpipe_num, uint8_t target_ep, uint16_t fifo_addr, uint16_t fifo_size, uint16_t max_pkt_size, uint8_t num_usb_pkt, uint8_t dma_enable, mss_usb_dma_channel_t dma_channel, mss_usb_xfr_type_t xfr_type, uint32_t add_zlp, uint32_t interval ) { /* TODO: Error check for all the parameters.*/ tdev_id_t tid = (tdev_id_t)0u; mss_usb_ep_t* rxep_ptr = &gh_rx_ep[inpipe_num]; tid = (tdev_id_t)(target_addr & 0x03u); rxep_ptr->tdev_idx = tid; rxep_ptr->num = (mss_usb_ep_num_t)inpipe_num; rxep_ptr->dpb_enable = DPB_DISABLE; rxep_ptr->fifo_size = fifo_size; rxep_ptr->fifo_addr = fifo_addr; rxep_ptr->dma_enable = dma_enable; rxep_ptr->dma_channel = dma_channel; rxep_ptr->max_pkt_size = max_pkt_size; rxep_ptr->stall = 0u; rxep_ptr->state = MSS_USB_EP_VALID; rxep_ptr->xfr_type = xfr_type; rxep_ptr->add_zlp = add_zlp; rxep_ptr->num_usb_pkt = 1u; rxep_ptr->buf_addr = 0u; rxep_ptr->xfr_length = 0u; rxep_ptr->xfr_count = 0u; rxep_ptr->txn_length = 0u; rxep_ptr->txn_count = 0u; rxep_ptr->disable_ping = 1u; rxep_ptr->req_pkt_n = 0u; rxep_ptr->interval = interval; /* Configure MP Registers with Target Device informations */ MSS_USBH_CIF_rx_ep_mp_configure(inpipe_num, target_ep, target_addr, g_tdev[tid].hub_addr, g_tdev[tid].hub_port, g_tdev[tid].hub_mtt, g_tdev[tid].speed, interval, xfr_type); MSS_USBH_CIF_rx_ep_configure(rxep_ptr); return (0); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ int8_t MSS_USBH_write_out_pipe ( uint8_t target_addr, uint8_t outpipe_num, uint8_t tdev_ep_num, uint16_t maxpktsz, uint8_t* buf, uint32_t length ) { mss_usb_ep_t* txep_ptr = &gh_tx_ep[outpipe_num]; /* TODO: Error check for all the parameters. */ tdev_id_t tid = (tdev_id_t)0u; ASSERT(target_addr); ASSERT(outpipe_num); ASSERT(tdev_ep_num); ASSERT(maxpktsz); ASSERT(buf); if ((target_addr != 0) && (outpipe_num != 0) && (tdev_ep_num != 0) && (maxpktsz != 0u) && (buf != 0u) ) { tid = (tdev_id_t)(target_addr & 0x03u); txep_ptr->tdev_idx = tid; txep_ptr->xfr_length = length; if (length > maxpktsz) { txep_ptr->txn_length = maxpktsz; } else { txep_ptr->txn_length = length; } txep_ptr->xfr_count = 0u; txep_ptr->txn_count = 0u; txep_ptr->buf_addr = buf; txep_ptr->state = MSS_USB_EP_VALID; MSS_USB_CIF_ep_write_pkt(txep_ptr->num, txep_ptr->buf_addr, txep_ptr->dma_enable, txep_ptr->dma_channel, txep_ptr->xfr_type, txep_ptr->xfr_length, txep_ptr->txn_length); return (0); } else { return (1); } } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ int8_t MSS_USBH_read_in_pipe ( uint8_t target_addr, uint8_t inpipe_num, uint8_t tdev_ep_num, uint16_t tdev_ep_maxpktsz, uint8_t* buf, uint32_t length ) { mss_usb_ep_t* rxep_ptr = &gh_rx_ep[inpipe_num]; /* TODO: Error check for all the parameters. */ tdev_id_t tid = (tdev_id_t)0u; ASSERT(target_addr); ASSERT(inpipe_num); ASSERT(tdev_ep_num); ASSERT(tdev_ep_maxpktsz); ASSERT(buf); if ((target_addr != 0) && (inpipe_num != 0) && (tdev_ep_num != 0) && (tdev_ep_maxpktsz != 0u) && (buf != 0u)) { tid = (tdev_id_t)(target_addr & 0x03u); rxep_ptr->tdev_idx = tid; rxep_ptr->xfr_length = length; if (length > tdev_ep_maxpktsz) { rxep_ptr->txn_length = tdev_ep_maxpktsz; } else { rxep_ptr->txn_length = length; } rxep_ptr->xfr_count = 0u; rxep_ptr->txn_count = 0u; rxep_ptr->buf_addr = buf; if ((DMA_ENABLE == rxep_ptr->dma_enable) && (MSS_USB_XFR_BULK == rxep_ptr->xfr_type)) { if (rxep_ptr->xfr_length >= rxep_ptr->max_pkt_size) { MSS_USBH_CIF_rx_ep_set_reqpkt_count(rxep_ptr->num, (rxep_ptr->xfr_length/ rxep_ptr->max_pkt_size)); } MSS_USBH_CIF_rx_ep_set_autoreq(rxep_ptr->num); } if ((rxep_ptr->max_pkt_size <= rxep_ptr->fifo_size) && (rxep_ptr->txn_length <= rxep_ptr->fifo_size)) { MSS_USB_CIF_rx_ep_read_prepare(rxep_ptr->num, rxep_ptr->buf_addr, rxep_ptr->dma_enable, rxep_ptr->dma_channel, rxep_ptr->xfr_type, rxep_ptr->xfr_length); MSS_USBH_CIF_rx_ep_set_reqpkt((mss_usb_ep_num_t)inpipe_num); } return (0); } else { return (1); } } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ mss_usb_state_t MSS_USBH_get_tdev_state ( uint8_t target_addr ) { tdev_id_t tid = (tdev_id_t)0; if (target_addr) { tid = (tdev_id_t)(target_addr & 0x03u); } ASSERT(g_tdev[tid].addr == target_addr); return (g_tdev[tid].state); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ void MSS_USBH_task ( void ) { mss_usbh_control_xfr_fsm(); mss_usbh_enum_fsm(); mss_usbh_fsm(); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ uint8_t MSS_USBH_start_control_xfr ( uint8_t* cbuf_addr, uint8_t* dbuf_addr, uint8_t data_dir, uint32_t data_len /*Add tdev_addr, speed and tdevmaxpktsz as the parameter*/ ) { mss_usb_ep_t* cep_ptr = &gh_tx_ep[MSS_USB_CEP]; ASSERT(cbuf_addr != (uint8_t*)0); ASSERT(dbuf_addr != (uint8_t*)0); ASSERT(!(((uint32_t)cbuf_addr) & 0x00000002U)); ASSERT(!(((uint32_t)dbuf_addr) & 0x00000002U)); ASSERT((data_dir == USB_STD_REQ_DATA_DIR_IN) || (data_dir == USB_STD_REQ_DATA_DIR_OUT)); cep_ptr->buf_addr = dbuf_addr; cep_ptr->cep_cmd_addr = cbuf_addr; cep_ptr->xfr_length = data_len; /* Set the logical MaxPktSize of Host CEP to the MazPktsz of the associated * Target CEP */ /* TODO: Copy from Parameter*/ /* cep_ptr->max_pkt_size = cep_ptr->tdev_addr */ cep_ptr->cep_data_dir = data_dir; cep_ptr->xfr_count = 0u; cep_ptr->txn_count = 0u; cep_ptr->xfr_type = MSS_USB_XFR_CONTROL; g_cep_xfr_result = 0u; MSS_USBH_CIF_load_tx_fifo(MSS_USB_CEP, cbuf_addr, USB_SETUP_PKT_LEN); cep_ptr->state = MSS_USB_CEP_SETUP; MSS_USBH_CIF_cep_set_setuppktrdy(); return (0); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ void MSS_USBH_construct_get_descr_command ( uint8_t* buf, uint8_t xfr_dir, uint8_t req_type, uint8_t recip_type, uint8_t request, uint8_t desc_type, uint8_t strng_idx, uint16_t length ) { /* bmRequestType */ buf[0] = (xfr_dir| req_type| recip_type); /* bRequest */ buf[1] = request; /* wValue-MSB = desc_type */ buf[3] = desc_type; if ((request == USB_STD_REQ_GET_DESCRIPTOR) && (desc_type == USB_STRING_DESCRIPTOR_TYPE)) { /* We support only one LANGID 0x0409. * Refer USB standards for list of all supported LangIDs */ buf[2] = strng_idx; /* wValue-LSB = string_idx */ buf[4] = (uint8_t)0x09u; /* wIndex-LSB = LangID for string Desc */ buf[5] = (uint8_t)0x04u; /* wIndex-MSB */ } else { /* * wValue-LSB = Conf_idx, Field should be used only for Conf_desc or * String Desc. * Since we support only one configuration, we set it to zero for * Conf_desc. For all other descriptors this field must be zero */ buf[2] = 0x00U; /* wIndex-LSB and MSB => other than String Desc, these values must be * zero */ buf[4] = 0x00U; buf[5] = 0x00U; } /* wLength-LSB and MSB, Length of data to be received */ buf[6] = (uint8_t)length; buf[7] = (uint8_t)(length >> 8u); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ mss_usb_ep_state_t MSS_USBH_get_cep_state ( void ) { return (gh_tx_ep[MSS_USB_CEP].state); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ void MSS_USBH_1ms_tick ( void ) { ms++; } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ uint32_t MSS_USBH_get_milis ( void ) { return (ms); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ int8_t MSS_USBH_get_std_dev_descr ( uint8_t* buffer ) { uint8_t command_buf[USB_SETUP_PKT_LEN] = {0}; g_internal_cep_xfr = 1u; /* 0x10 is the fixed non-zero device address we are using */ MSS_USBH_configure_control_pipe(0x10u); memset(command_buf, 0u, USB_SETUP_PKT_LEN *(sizeof(uint8_t))); /* Read first 8 bytes of the Device descriptor */ MSS_USBH_construct_get_descr_command(command_buf, USB_STD_REQ_DATA_DIR_IN, USB_STANDARD_REQUEST, USB_STD_REQ_RECIPIENT_DEVICE, USB_STD_REQ_GET_DESCRIPTOR, USB_DEVICE_DESCRIPTOR_TYPE, 0u, 8u); MSS_USBH_start_control_xfr(command_buf, buffer, USB_STD_REQ_DATA_DIR_IN, 8u); /* Wait for internal CEP transfer to complete */ while ((0u == g_cep_xfr_result) && (g_internal_cep_xfr == 1u)) { ; } if (MSS_USB_EP_XFR_SUCCESS == g_cep_xfr_result) { return 0; } else { return -1; } } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ void MSS_USBH_test_mode ( uint8_t test_case ) { switch (test_case) { case USB_TEST_MODE_SELECTOR_TEST_PACKET: MSS_USB_CIF_start_testpacket(); break; case USB_TEST_MODE_SELECTOR_TEST_J: MSS_USB_CIF_start_testj(); break; case USB_TEST_MODE_SELECTOR_TEST_K: MSS_USB_CIF_start_testk(); break; case USB_TEST_MODE_SELECTOR_TEST_SE0NAK: MSS_USB_CIF_start_testse0nak(); break; case USB_TEST_MODE_SELECTOR_TEST_FORCE_ENA: MSS_USB_CIF_start_forcehost_ena(); break; default: /* Empty default */ break; } } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ void MSS_USBH_suspend ( void ) { /* suspendM enabled. RemoteWakeup not supported. */ MSS_USBH_CIF_bus_suspend(0u); } /****************************************************************************** * See mss_usb_host.h for details of how to use this function. */ void MSS_USBH_resume ( void ) { volatile static uint32_t resume_milis = 0u; resume_milis = MSS_USBH_get_milis(); MSS_USBH_CIF_clr_suspend_bus(); MSS_USBH_CIF_assert_bus_resume(); /* This delay should be at lease 20ms */ while ((MSS_USBH_get_milis() - resume_milis) <= 40u) { ; } MSS_USBH_CIF_clr_bus_resume(); } void MSS_USBH_abort_in_pipe ( mss_usb_ep_num_t inpipe_num ) { MSS_USBH_CIF_rx_ep_clr_reqpkt(inpipe_num); MSS_USBH_CIF_rx_ep_clr_naktimeout_err(inpipe_num); MSS_USBH_CIF_rx_ep_clr_rxstall_err(inpipe_num); MSS_USBH_CIF_rx_ep_clr_retry_err(inpipe_num); if (MSS_USBH_CIF_rx_ep_is_rxpktrdy(inpipe_num)) { MSS_USBH_CIF_rx_ep_flush_fifo_reg(inpipe_num); MSS_USB_CIF_rx_ep_clr_rxpktrdy(inpipe_num); } } void MSS_USBH_abort_out_pipe ( mss_usb_ep_num_t outpipe_num ) { gh_tx_ep[outpipe_num].state = MSS_USB_EP_ABORTED; MSS_USBH_CIF_tx_ep_clr_autoset(outpipe_num); MSS_USBH_CIF_tx_ep_flush_fifo_reg(outpipe_num); MSS_USB_CIF_tx_ep_disable_dma(outpipe_num); MSS_USBH_CIF_tx_ep_clr_retry_err(outpipe_num); MSS_USBH_CIF_tx_ep_clr_rxstall_err(outpipe_num); MSS_USBH_CIF_tx_ep_clr_naktimeout_err(outpipe_num); } /******************************************************************************* * Internal Functions *******************************************************************************/ /* * This function is the main FSM of the USB Host. It handles connect/disconnect * events, initiate enumeration FSM when device is detected and handles allocation * of class driver to the device. */ static void mss_usbh_fsm ( void ) { static uint32_t reset_milis = 0u; mss_usb_enum_state_t enum_st; if (1u == gh_tdev_discon_event) { mss_usbh_handle_discon_event(); gh_tdev_discon_event = 0u; g_host_state = HOST_IDLE; } switch (g_host_state) { case HOST_IDLE: /* * Repeatedly enable session bit so that MUSB sample Idig pin to * detect device connection */ MSS_USB_CIF_start_session(); if (gh_tdev_connect_event) { gh_tdev_connect_event = 0u; g_tdev[tdev_idx].state = MSS_USB_ATTACHED_STATE; reset_milis = MSS_USBH_get_milis(); g_host_state = HOST_ROOT_TARGET_DETECTED; } break; case HOST_ROOT_TARGET_DETECTED: /* Wait for at least 100ms for target power de-bounce as per spec */ if ((MSS_USBH_get_milis() - reset_milis) >= 100u) { MSS_USB_CIF_enable_usbirq(VBUS_ERROR_IRQ_MASK); g_tdev[TDEV_R].state = MSS_USB_POWERED_STATE; reset_milis = MSS_USBH_get_milis(); MSS_USBH_CIF_assert_bus_reset(); /*Reset Root device*/ g_host_state = HOST_ROOT_TARGET_RESETING; } break; case HOST_ROOT_TARGET_RESETING: /* Keep target device in reset, at least for 20ms as per spec */ if ((MSS_USBH_get_milis() - reset_milis) == 20u) { MSS_USBH_CIF_clr_bus_reset(); } else if ((MSS_USBH_get_milis() - reset_milis) >= 40u) { /* If target was operating at FS, check if can work with HS as well */ if ((g_tdev[TDEV_R].speed == MSS_USB_DEVICE_FS) && (MSS_USBH_CIF_is_hs_mode())) { g_tdev[TDEV_R].speed = MSS_USB_DEVICE_HS; } if (0 != g_user_cb->usbh_tdev_attached) { g_user_cb->usbh_tdev_attached(g_tdev[tdev_idx].speed); } /* Configure CEP for the detected speed */ MSS_USBH_configure_control_pipe(TDEV_R); g_tdev[TDEV_R].state = MSS_USB_DEFAULT_STATE; g_host_state = HOST_ROOT_TARGET_ENUMERATING; } break; case HOST_ROOT_TARGET_ENUMERATING: enum_st = mss_usbh_get_enum_fsm_state(); if (ENUM_IDLE == enum_st) { mss_usbh_start_enum_fsm(); } else if (ENUM_SUCCESS == enum_st) { g_tdev[TDEV_R].state = MSS_USB_ADDRESS_STATE; mss_usbh_reset_enum_fsm(); g_host_state = HOST_ROOT_TARGET_ALLOCATE_CLASSD; } else if (ENUM_ERROR == enum_st) { mss_usbh_reset_enum_fsm(); g_tdev[TDEV_R].state = MSS_USB_ATTACHED_STATE; g_host_state = HOST_ROOT_TARGET_ERROR; } else { /* EnumFSM in progress, Do nothing */ } break; case HOST_ROOT_TARGET_ALLOCATE_CLASSD: g_rcd[TDEV_R].class_handle->usbh_class_allocate(g_tdev[TDEV_R].addr); g_host_state = HOST_ROOT_TARGET_SERVICING; if (0 != g_user_cb->usbh_tdev_class_driver_assigned) { g_user_cb->usbh_tdev_class_driver_assigned(); } break; case HOST_ROOT_TARGET_SERVICING: /* Class driver FSM is active now */ break; case HOST_ROOT_TARGET_ERROR: if (0 != g_user_cb->usbh_tdev_not_supported) { g_user_cb->usbh_tdev_not_supported(g_tdev_error_code); } g_host_state = HOST_IDLE; /* * Clear Enum-FSM * Clear Target_info for all targets. * Clear EP0 config * Give Visual Err Indication and remain in this state, * till Disconnect Interrupt occurs or USER wants to give a retry * command. */ break; default: /* Empty default */ break; } } /* * This function handles the enumeration process which includes, device reset, * speed detection, and standard USB request for retrieving Device and Config * Descriptor from the device. */ static void mss_usbh_enum_fsm ( void ) { uint8_t command_buf[USB_SETUP_PKT_LEN] = {0}; static uint32_t reset_milis = 0u; int8_t cd_idx = 0x04; uint8_t dummy[8]; /* * We do not do any assert checks here. * Before Calling this FSM, Calling function should make sure that This FSM * can be executed */ switch(g_enum_state) { case ENUM_IDLE: /* Free running Enum FSM hits here when doing nothing */ break; case ENUM_GET_DEF_DEV_DESC: MSS_USBH_construct_get_descr_command(command_buf, USB_STD_REQ_DATA_DIR_IN, USB_STANDARD_REQUEST, USB_STD_REQ_RECIPIENT_DEVICE, USB_STD_REQ_GET_DESCRIPTOR, USB_DEVICE_DESCRIPTOR_TYPE, 0u, USB_SETUP_PKT_LEN); g_enum_state = ENUM_WAIT_GET_DEF_DEV_DESC; MSS_USBH_start_control_xfr(command_buf, (uint8_t*)&g_tdev_desc[tdev_idx].dev_desc, USB_STD_REQ_DATA_DIR_IN, USB_SETUP_PKT_LEN); break; case ENUM_WAIT_GET_DEF_DEV_DESC: if (g_cep_xfr_result) { if (MSS_USB_EP_XFR_SUCCESS == g_cep_xfr_result) { g_tdev[TDEV_R].tdev_maxpktsz0 = g_tdev_desc[TDEV_R].dev_desc.ds.bMaxPacketSize0; g_enum_state = ENUM_RESET_TDEV; } else { g_enum_state = ENUM_ERROR; } } break; case ENUM_RESET_TDEV: reset_milis = MSS_USBH_get_milis(); MSS_USBH_CIF_assert_bus_reset(); g_enum_state = ENUM_CLR_RESET_TDEV; break; case ENUM_CLR_RESET_TDEV: if ((MSS_USBH_get_milis() - reset_milis) == 20u) { MSS_USBH_CIF_clr_bus_reset(); } else if ((MSS_USBH_get_milis() - reset_milis) >= 40u) { /* Wait for 20ms to let the Target settle down */ g_enum_state = ENUM_SET_TDEV_ADDR; } break; case ENUM_SET_TDEV_ADDR: /* Configure CEP Since Maxpkt0 might have changed. */ MSS_USBH_configure_control_pipe(TDEV_R); memset(command_buf, 0, USB_SETUP_PKT_LEN*(sizeof(uint8_t))); command_buf[1] = USB_STD_REQ_SET_ADDRESS; command_buf[2] = 0x10U; /*New non-zero ADDR*/ reset_milis = MSS_USBH_get_milis(); g_enum_state = ENUM_WAIT_SET_TDEV_ADDR; MSS_USBH_start_control_xfr(command_buf, command_buf, /*Dummy Buf for zld req*/ USB_STD_REQ_DATA_DIR_OUT, 0u); break; case ENUM_WAIT_SET_TDEV_ADDR: if (g_cep_xfr_result) { if (MSS_USB_EP_XFR_SUCCESS == g_cep_xfr_result) { int8_t res; /* SetAddrDelay at least 2ms */ if ((MSS_USBH_get_milis() - reset_milis) >= 5u) { g_tdev[TDEV_R].addr = 0x10U; /* New non-zero ADDR */ MSS_USBH_CIF_tx_ep_set_target_func_addr(gh_tx_ep[MSS_USB_CEP].num, g_tdev[TDEV_R].addr); /* Validate DevDescriptor. Take exception for FS LS * devices */ res = validate_dev_desc((dev_desc_t*)&g_tdev_desc[tdev_idx].dev_desc); g_tdev_desc[tdev_idx].desc_err_code = res; if (0u == res) { g_enum_state = ENUM_GET_FUL_DEV_DESC; } else { g_enum_state = ENUM_ERROR; g_tdev_error_code = res; } } } else { g_tdev[TDEV_R].addr = 0x00u; g_enum_state = ENUM_ERROR; } } break; case ENUM_GET_FUL_DEV_DESC: MSS_USBH_construct_get_descr_command(command_buf, USB_STD_REQ_DATA_DIR_IN, USB_STANDARD_REQUEST, USB_STD_REQ_RECIPIENT_DEVICE, USB_STD_REQ_GET_DESCRIPTOR, USB_DEVICE_DESCRIPTOR_TYPE, 0u, USB_STD_DEVICE_DESCR_LEN); g_enum_state = ENUM_WAIT_GET_FUL_DEV_DESC; MSS_USBH_start_control_xfr(command_buf, (uint8_t*)&g_tdev_desc[tdev_idx].dev_desc, USB_STD_REQ_DATA_DIR_IN, USB_STD_DEVICE_DESCR_LEN); break; case ENUM_WAIT_GET_FUL_DEV_DESC: if (g_cep_xfr_result) { if (MSS_USB_EP_XFR_SUCCESS == g_cep_xfr_result) { /* Support for PET device with VID = 0x1A0A PID = PID=0x0200*/ if ((g_tdev_desc[tdev_idx].dev_desc.ds.idVendor == 0x1A0AU) && (g_tdev_desc[tdev_idx].dev_desc.ds.idProduct == 0x0200U)) { g_enum_state = ENUM_PET_SET_CONFIG; } else { int8_t res; /* Validate DevDescriptor */ res = validate_dev_desc((dev_desc_t*)&g_tdev_desc[tdev_idx].dev_desc); g_tdev_desc[tdev_idx].desc_err_code = res; if (0u == res) { g_enum_state = ENUM_GET_DEF_CONFG_DESC; } else { g_enum_state = ENUM_ERROR; g_tdev_error_code = res; } } } else { g_enum_state = ENUM_ERROR; } } break; case ENUM_GET_DEF_CONFG_DESC: memset(command_buf, 0u, USB_SETUP_PKT_LEN*(sizeof(uint8_t))); MSS_USBH_construct_get_descr_command(command_buf, USB_STD_REQ_DATA_DIR_IN, USB_STANDARD_REQUEST, USB_STD_REQ_RECIPIENT_DEVICE, USB_STD_REQ_GET_DESCRIPTOR, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0x0U, /*stringID*/ USB_STD_CONFIG_DESCR_LEN); g_enum_state = ENUM_WAIT_GET_DEF_CONFG_DESC; MSS_USBH_start_control_xfr(command_buf, (uint8_t*)&g_tdev_desc[tdev_idx].conf_desc, USB_STD_REQ_DATA_DIR_IN, USB_STD_CONFIG_DESCR_LEN); break; case ENUM_WAIT_GET_DEF_CONFG_DESC: if (g_cep_xfr_result) { if (MSS_USB_EP_XFR_SUCCESS == g_cep_xfr_result) { int8_t res; res = validate_def_conf_desc((def_conf_desc_t*)&g_tdev_desc[tdev_idx].conf_desc); g_tdev_desc[tdev_idx].desc_err_code = res; if (0u == res) { g_enum_state = ENUM_GET_FUL_CONFG_DESC; } else { g_enum_state = ENUM_ERROR; g_tdev_error_code = res; } } else { g_enum_state = ENUM_ERROR; } } break; case ENUM_GET_FUL_CONFG_DESC: /* We already read the Standard Config desc of size 9. Now read * additional MSC class specific descriptor data. reading first * 18 bytes is sufficient for supporting MSC class */ memset(command_buf, 0u, USB_SETUP_PKT_LEN*(sizeof(uint8_t))); MSS_USBH_construct_get_descr_command(command_buf, USB_STD_REQ_DATA_DIR_IN, USB_STANDARD_REQUEST, USB_STD_REQ_RECIPIENT_DEVICE, USB_STD_REQ_GET_DESCRIPTOR, USB_CONFIGURATION_DESCRIPTOR_TYPE, 0u, /*stringID*/ 18u); g_enum_state = ENUM_WAIT_GET_FUL_CONFG_DESC; MSS_USBH_start_control_xfr(command_buf, (uint8_t*)&g_tdev_desc[tdev_idx].conf_desc, USB_STD_REQ_DATA_DIR_IN, 18u); break; case ENUM_WAIT_GET_FUL_CONFG_DESC: if (g_cep_xfr_result) { if (MSS_USB_EP_XFR_SUCCESS == g_cep_xfr_result) { g_enum_state = ENUM_CHECK_TDEV_CLASS_SUPPORT; } else { g_enum_state = ENUM_ERROR; } } break; case ENUM_CHECK_TDEV_CLASS_SUPPORT: cd_idx = host_enum_check_class_support(TDEV_R); if (cd_idx < (int8_t)0u) { g_enum_state = ENUM_ERROR; g_tdev_error_code = cd_idx; } /* Max No of classDriver is 3 */ else if (cd_idx < 3u) { g_rcd[cd_idx].alloc_state = 1u; g_tdev[TDEV_R].class_handle = g_rcd[cd_idx].class_handle; g_enum_state = ENUM_SUCCESS; } break; case ENUM_PET_SET_CONFIG: memset(command_buf, 0u, USB_SETUP_PKT_LEN*(sizeof(uint8_t))); command_buf[1] = USB_STD_REQ_SET_CONFIG; command_buf[2] = 0x01u; /* ConfigNum for PET Device */ g_enum_state = ENUM_PET_WAIT_SET_CONFIG; MSS_USBH_start_control_xfr(command_buf, (uint8_t*)dummy, USB_STD_REQ_DATA_DIR_IN, 0u); break; case ENUM_PET_WAIT_SET_CONFIG: if (g_cep_xfr_result) { if (MSS_USB_EP_XFR_SUCCESS == g_cep_xfr_result) { g_enum_state = ENUM_CHECK_TDEV_CLASS_SUPPORT; } else { g_enum_state = ENUM_ERROR; } } break; case ENUM_SUCCESS: break; case ENUM_ERROR: default: ASSERT(0); /* Should never happen */ break; } } /* * This function is the FSM for the control transfer. Once the control transfer * is initiated, this function will monitor it's progress in all the stages and * record the status at the end. */ static void mss_usbh_control_xfr_fsm ( void ) { volatile mss_usb_ep_state_t cep_event_st = MSS_USB_EP_VALID; uint32_t rem_length = 0u; mss_usb_ep_t* cep_ptr = &gh_tx_ep[MSS_USB_CEP]; MSS_USB_CIF_cep_disable_irq(); /* gh_cep_cb_event is critical */ cep_event_st = (mss_usb_ep_state_t)gh_cep_cb_event; gh_cep_cb_event = 0u; MSS_USB_CIF_cep_enable_irq(); MSS_USB_CIF_set_index_reg(MSS_USB_CEP); switch (cep_ptr->state) { case MSS_USB_CEP_IDLE: /* do nothing. Free running CEP-FSM hits here when doing no transfers */ break; case MSS_USB_CEP_SETUP: if (cep_event_st) { if (MSS_USB_EP_TXN_SUCCESS == cep_event_st) { if (0u == cep_ptr->xfr_length) { /* zdl-request is sent. Get the status now */ cep_ptr->state = MSS_USB_CEP_STATUS_AFTER_OUT; MSS_USBH_CIF_cep_set_statuspktrdy_after_out(); } else { rem_length = cep_ptr->xfr_length - cep_ptr->xfr_count; if (rem_length > cep_ptr->max_pkt_size) { cep_ptr->txn_length = cep_ptr->max_pkt_size; } else { cep_ptr->txn_length = rem_length; } if (USB_STD_REQ_DATA_DIR_IN == cep_ptr->cep_data_dir) { cep_ptr->state = MSS_USB_CEP_RX; MSS_USBH_CIF_cep_set_request_in_pkt(); /* One Packet at a time */ } else if (USB_STD_REQ_DATA_DIR_OUT == cep_ptr->cep_data_dir) { MSS_USBH_CIF_cep_write_pkt(cep_ptr); cep_ptr->state = MSS_USB_CEP_TX; MSS_USBH_CIF_cep_set_txpktrdy(); } else { ASSERT(0); /* DataDir not valid */ } } } else { cep_ptr->state = cep_event_st; } } break; case MSS_USB_CEP_RX: if (MSS_USB_EP_TXN_SUCCESS == cep_event_st) { MSS_USBH_CIF_cep_read_pkt(cep_ptr); if (cep_ptr->xfr_count == cep_ptr->xfr_length) { cep_ptr->state = MSS_USB_CEP_STATUS_AFTER_IN; MSS_USBH_CIF_cep_set_statuspktrdy_after_in(); } else { rem_length = cep_ptr->xfr_length - cep_ptr->xfr_count; if (rem_length > cep_ptr->max_pkt_size) { cep_ptr->txn_length = cep_ptr->max_pkt_size; } else { cep_ptr->txn_length = rem_length; } cep_ptr->state = MSS_USB_CEP_RX; MSS_USBH_CIF_cep_set_request_in_pkt(); /*One Packet at a time*/ } } else { cep_ptr->state = cep_event_st; ASSERT(0);/* Flush fifo, if RxpktRdy is set.MUSB:21.2.2 */ } break; case MSS_USB_CEP_TX: if (MSS_USB_EP_TXN_SUCCESS == cep_event_st) { if (cep_ptr->xfr_count == cep_ptr->xfr_length) { cep_ptr->state = MSS_USB_CEP_STATUS_AFTER_OUT; MSS_USBH_CIF_cep_set_statuspktrdy_after_out(); } else { rem_length = cep_ptr->xfr_length - cep_ptr->xfr_count; if (rem_length > cep_ptr->max_pkt_size) { cep_ptr->txn_length = cep_ptr->max_pkt_size; } else { cep_ptr->txn_length = rem_length; } MSS_USBH_CIF_cep_write_pkt(cep_ptr); } } else { cep_ptr->state = cep_event_st; ASSERT(0); /* Error response received in INTR */ } /* TODO: Check if more data needs to Transmitted */ /* Initiate IN status phase. */ break; case MSS_USB_CEP_STATUS_AFTER_IN: if (MSS_USB_EP_TXN_SUCCESS == cep_event_st) { uint8_t this_tdev = cep_ptr->tdev_idx; cep_ptr->state = MSS_USB_EP_XFR_SUCCESS; if ((0u == g_tdev[(cep_ptr->tdev_idx)].class_handle) || (1u == g_internal_cep_xfr)) { g_cep_xfr_result = MSS_USB_EP_XFR_SUCCESS; g_internal_cep_xfr = 0u; } else { if (0 != g_tdev[this_tdev].class_handle->usbh_class_cep_xfr_done) { g_tdev[this_tdev].class_handle-> usbh_class_cep_xfr_done(g_tdev[this_tdev].addr, cep_ptr->state, cep_ptr->xfr_count); } else { ASSERT(0);/* invalid pointer */ } } cep_ptr->state = MSS_USB_CEP_IDLE; } else { cep_ptr->state = cep_event_st; ASSERT(0); } break; case MSS_USB_CEP_STATUS_AFTER_OUT: if (MSS_USB_EP_TXN_SUCCESS == cep_event_st) { uint8_t this_tdev = cep_ptr->tdev_idx; MSS_USBH_CIF_cep_clr_statusRxpktrdy(); cep_ptr->state = MSS_USB_EP_XFR_SUCCESS; if ((0 == g_tdev[(cep_ptr->tdev_idx)].class_handle) || (1u == g_internal_cep_xfr)) { g_cep_xfr_result = MSS_USB_EP_XFR_SUCCESS; } else { if (0 != g_tdev[this_tdev].class_handle->usbh_class_cep_xfr_done) { g_tdev[this_tdev].class_handle-> usbh_class_cep_xfr_done(g_tdev[this_tdev].addr, cep_ptr->state, cep_ptr->xfr_count); } else { ASSERT(0); /* invalid pointer */ } } cep_ptr->state = MSS_USB_CEP_IDLE; } else { cep_ptr->state = cep_event_st; ASSERT(0); } break; case MSS_USB_EP_NAK_TOUT: case MSS_USB_EP_NO_RESPONSE: case MSS_USB_EP_STALL_RCVD: { uint8_t this_tdev = cep_ptr->tdev_idx; if ((0 == g_tdev[(cep_ptr->tdev_idx)].class_handle) || (1u == g_internal_cep_xfr)) { g_cep_xfr_result = cep_ptr->state; g_internal_cep_xfr = 0u; } else { if (0 != g_tdev[this_tdev].class_handle->usbh_class_cep_xfr_done) { g_tdev[this_tdev].class_handle-> usbh_class_cep_xfr_done(g_tdev[this_tdev].addr, cep_ptr->state, cep_ptr->xfr_count); } else { ASSERT(0); /* invalid pointer */ } cep_ptr->state = MSS_USB_CEP_IDLE; } } break; default: { ASSERT(0); /* Invalid CEP state */ break; } } } /* * This function is used to start the enumeration FSM */ static void mss_usbh_start_enum_fsm ( void ) { g_enum_state = ENUM_GET_DEF_DEV_DESC; } /* * This function is used to bring the enum FSM to it's initial IDLE state. */ static void mss_usbh_reset_enum_fsm ( void ) { g_enum_state = ENUM_IDLE; } /* * This function is used to find the current status of the enumeration FSM. The * state information is used to know if the enumeration was successful or not. */ static mss_usb_enum_state_t mss_usbh_get_enum_fsm_state ( void ) { return (g_enum_state); } /* * This Call-back function is executed on completion of current OUT transfer. */ static void mss_usbh_ep_tx_complete_cb ( uint8_t ep_num, uint8_t status ) { uint8_t this_tdev = 0u; uint8_t transfer_complete = 0u; mss_usb_ep_t* txep_ptr = &gh_tx_ep[ep_num]; /* retrieve tdev_idx which transmitted data. */ this_tdev = gh_tx_ep[ep_num].tdev_idx; if (MSS_USB_EP_ABORTED != txep_ptr->state) { txep_ptr->txn_count = txep_ptr->txn_length; txep_ptr->xfr_count += txep_ptr->txn_length; if (0u == status) { if (DMA_ENABLE == txep_ptr->dma_enable) { txep_ptr->state = MSS_USB_EP_XFR_SUCCESS; txep_ptr->xfr_count = MSS_USB_CIF_dma_read_addr(txep_ptr->dma_channel) - ((uint32_t)txep_ptr->buf_addr); ASSERT(txep_ptr->xfr_count == txep_ptr->xfr_length); transfer_complete = 1u; } else { if (MSS_USB_XFR_BULK == txep_ptr->xfr_type) { if (txep_ptr->xfr_count < txep_ptr->xfr_length) { transfer_complete = 0u; } else if (txep_ptr->xfr_count == txep_ptr->xfr_length) { if (ADD_ZLP_TO_XFR == txep_ptr->add_zlp) { if (0u == txep_ptr->txn_count) { transfer_complete = 1u; } else { if (txep_ptr->txn_count == txep_ptr->max_pkt_size) { transfer_complete = 0u; } else if (txep_ptr->txn_count < txep_ptr->max_pkt_size) { transfer_complete = 1u; } } } else /* no zlp */ { transfer_complete = 1u; } } else { /* If xfr_count is more than xfr_lenght then something * has seriously gone bad.*/ ASSERT(0); } } } } else { txep_ptr->state = (mss_usb_ep_state_t)status; transfer_complete = 1u; } if (1u == transfer_complete) { if (0 != g_tdev[this_tdev].class_handle->usbh_class_tx_done) { /* call-back class driver */ if (0u == txep_ptr->xfr_count) { ASSERT(0); } else { g_tdev[this_tdev].class_handle->usbh_class_tx_done(g_tdev[this_tdev].addr, status, txep_ptr->xfr_count); } } else { ASSERT(0); /* Invalid function pointer */ } } else { txep_ptr->txn_count = 0u;/* reset txn_count for next txn */ ASSERT(txep_ptr->xfr_length >= txep_ptr->xfr_count); if ((txep_ptr->xfr_length - txep_ptr->xfr_count) >= txep_ptr->max_pkt_size) { txep_ptr->txn_length = txep_ptr->max_pkt_size; } else { txep_ptr->txn_length = (txep_ptr->xfr_length - txep_ptr->xfr_count); } while (MSS_USB_CIF_is_txepfifo_notempty(txep_ptr->num)) { ; } if ((txep_ptr->max_pkt_size <= txep_ptr->fifo_size) && (txep_ptr->txn_length <= txep_ptr->fifo_size)) { txep_ptr->state = MSS_USB_EP_VALID; MSS_USB_CIF_ep_write_pkt(txep_ptr->num, (txep_ptr->buf_addr + txep_ptr->xfr_count), txep_ptr->dma_enable, txep_ptr->dma_channel, txep_ptr->xfr_type, txep_ptr->xfr_length, txep_ptr->txn_length); } else { ASSERT(0); } } } else { /* The Transmit endpoint generates an endpoint interrupt when previously * initiated TX transfer is aborted.Class driver doesn't need to know this. * Also this driver doesn't need to do anything in this case.*/ } } /* * This Call-back function is executed on completion of current IN transfer. */ static void mss_usbh_ep_rx_cb ( uint8_t ep_num, uint8_t status ) { uint8_t this_tdev = 0u; uint8_t transfer_complete = 0u; uint32_t received_count = 0u; mss_usb_ep_t* rxep_ptr = &gh_rx_ep[ep_num]; /* Retrieve tdev_idx which received data. */ this_tdev = gh_rx_ep[ep_num].tdev_idx; if (status & (RX_EP_OVER_RUN_ERROR | RX_EP_STALL_ERROR | RX_EP_DATA_ERROR | RX_EP_PID_ERROR | RX_EP_ISO_INCOMP_ERROR)) { transfer_complete = 1u; } else { if (MSS_USB_CIF_rx_ep_is_rxpktrdy((mss_usb_ep_num_t)ep_num)) { received_count = (uint32_t)MSS_USB_CIF_rx_ep_read_count((mss_usb_ep_num_t)ep_num); if (DMA_ENABLE == rxep_ptr->dma_enable) { if (MSS_USB_DMA_MODE1 == (MSS_USB_CIF_rx_ep_get_dma_mode((mss_usb_ep_num_t)ep_num))) { uint32_t increamented_addr; /* * This means we are in BULK transfer with DMA mode1. * all the rxmaxP size pkts are received and last short pkt * need to be read without DMA or by switching to mode 0. * After switching mode to 0, this ISR handler is invoked * again. Data packet will be read then. */ /* Read 'short packet' without DMA */ MSS_USB_CIF_dma_stop_xfr(rxep_ptr->dma_channel); MSS_USB_CIF_rx_ep_clr_autoclr((mss_usb_ep_num_t)ep_num); increamented_addr = MSS_USB_CIF_dma_read_addr(rxep_ptr->dma_channel); /* Count number of bytes read so far,since DMA was operating * in m1 with Autoclr.*/ rxep_ptr->xfr_count = (increamented_addr - ((uint32_t)(rxep_ptr->buf_addr))); if (received_count) { MSS_USB_CIF_read_rx_fifo((mss_usb_ep_num_t)ep_num, (rxep_ptr->buf_addr+rxep_ptr->xfr_count), received_count); rxep_ptr->xfr_count += received_count; MSS_USB_CIF_rx_ep_clr_rxpktrdy((mss_usb_ep_num_t)ep_num); } transfer_complete = 1u; } else { MSS_USB_CIF_dma_write_count(rxep_ptr->dma_channel, received_count); MSS_USB_CIF_dma_start_xfr(rxep_ptr->dma_channel); transfer_complete = 2u; /*Upper layer cb will be called from DMA ISR*/ } } else /* dma disable */ { if (received_count) { MSS_USB_CIF_read_rx_fifo((mss_usb_ep_num_t)ep_num, (rxep_ptr->buf_addr+rxep_ptr->xfr_count), received_count); rxep_ptr->txn_count = received_count; rxep_ptr->xfr_count += received_count; MSS_USB_CIF_rx_ep_clr_rxpktrdy((mss_usb_ep_num_t)ep_num); } if (rxep_ptr->xfr_count == rxep_ptr->xfr_length) { transfer_complete = 1u; } else if (rxep_ptr->xfr_count < rxep_ptr->xfr_length) { /* Reset txn_count for next txn */ rxep_ptr->txn_count = 0u; if ((rxep_ptr->xfr_length - rxep_ptr->xfr_count) > rxep_ptr->max_pkt_size) { rxep_ptr->txn_length = rxep_ptr->max_pkt_size; } else { rxep_ptr->txn_length = (rxep_ptr->xfr_length - rxep_ptr->xfr_count); } MSS_USBH_CIF_rx_ep_set_reqpkt((mss_usb_ep_num_t)ep_num); } else { ASSERT(0); } } } } if (1u == transfer_complete) { rxep_ptr->state = (mss_usb_ep_state_t)status; if (0 != g_tdev[this_tdev].class_handle->usbh_class_rx_done) { /* call-back class driver */ /* count = 0 since there was error */ g_tdev[this_tdev].class_handle->usbh_class_rx_done(g_tdev[this_tdev].addr, status, rxep_ptr->xfr_count); } else { ASSERT(0); /* Invalid pointer */ } } } /* * This Call-back function is executed on completion of current CONTROL transfer. */ static void mss_usbh_cep_cb ( uint8_t status ) { gh_cep_cb_event = status; } /* * This Call-back function is executed on receiving SOF interrupt in HOST mode */ static void mss_usbh_sof_cb ( uint32_t frame_number ) { } /* * This Call-back function is executed on detecting the device attach event */ static void mss_usbh_connect_cb ( mss_usb_device_speed_t target_speed, mss_usb_vbus_level_t vbus_level ) { g_tdev[TDEV_R].speed = target_speed; gh_tdev_connect_event = 1u; } /* * This Call-back function is executed on detecting the device detach event */ static void mss_usbh_disconnect_cb ( void ) { gh_tdev_discon_event = 1u; } /* * This Call-back function is executed on detecting a Vbus level error event */ static void mss_usbh_vbus_err_cb ( mss_usb_vbus_level_t vbus_level ) { } /* * This Call-back function is executed on detecting the babble error event */ static void mss_usbh_babble_err_cb ( void ) { /* Not supported yet */ } /* * This Call-back function is executed on detecting the session request event * when the attached device is also a OTG device. */ static void mss_usbh_session_request_cb ( void ) { /* Not supported yet */ } static void mss_usbh_dma_handler_cb ( mss_usb_ep_num_t ep_num, mss_usb_dma_dir_t dma_dir, uint8_t status, uint32_t dma_addr_val ) { mss_usb_ep_t* ep_ptr = 0; uint8_t this_tdev = 0u; if (DMA_XFR_ERROR == status) { ASSERT(0); } else { if (MSS_USB_DMA_READ == dma_dir) /*TX EP*/ { ep_ptr = &gh_tx_ep[ep_num]; /* Retrieve tdev_idx which received data.*/ this_tdev = gh_tx_ep[ep_num].tdev_idx; /* EP interrupt won't happen when short packet is not received */ if ((NO_ZLP_TO_XFR == ep_ptr->add_zlp) && (ep_ptr->xfr_length) && (!(ep_ptr->xfr_length % ep_ptr->max_pkt_size))) { /* Wait till last TxMaxPkt size packet is sent.*/ while (MSS_USB_CIF_tx_ep_is_txpktrdy(ep_num)) { ; } ep_ptr->xfr_count = dma_addr_val - (uint32_t)ep_ptr->buf_addr; ep_ptr->state = MSS_USB_EP_XFR_SUCCESS; if (0 != g_tdev[this_tdev].class_handle->usbh_class_tx_done) { /* Call-back class driver */ g_tdev[this_tdev].class_handle->usbh_class_tx_done(g_tdev[this_tdev].addr, status, ep_ptr->xfr_count); } } else { MSS_USB_CIF_tx_ep_set_txpktrdy(ep_num); } } /* RX EP */ else if (MSS_USB_DMA_WRITE == dma_dir) { ep_ptr = &gh_rx_ep[ep_num]; /* Retrieve tdev_idx which transmitted data.*/ this_tdev = gh_rx_ep[ep_num].tdev_idx; if ((NO_ZLP_TO_XFR == ep_ptr->add_zlp) && (ep_ptr->xfr_length) && (!(ep_ptr->xfr_length % ep_ptr->max_pkt_size))) { MSS_USB_CIF_dma_stop_xfr(ep_ptr->dma_channel); MSS_USBH_CIF_rx_ep_clr_reqpkt((mss_usb_ep_num_t)ep_num); MSS_USB_CIF_rx_ep_clr_autoclr((mss_usb_ep_num_t)ep_num); ep_ptr->xfr_count = dma_addr_val - (uint32_t)ep_ptr->buf_addr; ep_ptr->state = MSS_USB_EP_XFR_SUCCESS; if (MSS_USB_DMA_MODE0 == (MSS_USB_CIF_rx_ep_get_dma_mode(ep_num))) { MSS_USB_CIF_rx_ep_clr_rxpktrdy(ep_num); } if (0 != g_tdev[this_tdev].class_handle->usbh_class_rx_done) { /* Call-back class driver */ g_tdev[this_tdev].class_handle-> usbh_class_rx_done(g_tdev[this_tdev].addr, status, ep_ptr->xfr_count); } } } } } /* * This function checks errors in the received device descriptor. */ static int8_t validate_dev_desc ( dev_desc_t* p_desc ) { int8_t result = 0; if (p_desc->bLength != USB_STD_DEVICE_DESCR_LEN) { result = DEV_DESC_WRONG_LENGTH; } if (p_desc->bDescriptorType != USB_DEVICE_DESCRIPTOR_TYPE) { result = DEV_DESC_WRONG_DESC_TYPE; } if (!((p_desc->bcdUSB != USB_BCD_VERSION_2_0) || (p_desc->bcdUSB != USB_BCD_VERSION_2_1) || (p_desc->bcdUSB != USB_BCD_VERSION_1_1) || (p_desc->bcdUSB != USB_BCD_VERSION_1_0))) { result = DEV_DESC_WRONG_USBBCD; } if (!((p_desc->bMaxPacketSize0 != 8u) || (p_desc->bMaxPacketSize0 != 16u) || (p_desc->bMaxPacketSize0 != 32u) || (p_desc->bMaxPacketSize0 != 64u))) { result = DEV_DESC_WRONG_MAXPKTSZ0; } if ((MSS_USB_DEVICE_HS == g_tdev[TDEV_R].speed)) { if ((p_desc->bcdUSB != USB_BCD_VERSION_2_0) && (p_desc->bcdUSB != USB_BCD_VERSION_2_1)) { result = DEV_DESC_HS_USBBCD_NOT200; } if (p_desc->bMaxPacketSize0 != 64u) { result = DEV_DESC_HS_MAXPKTSZ0_NOT64; } } if ((MSS_USB_DEVICE_LS == g_tdev[TDEV_R].speed)) { if (p_desc->bMaxPacketSize0 != 8u) { result = DEV_DESC_LS_MAXPKTSZ0_NOT8; } } if ((MSS_USB_DEVICE_FS == g_tdev[TDEV_R].speed)) { result = FS_DEV_NOT_SUPPORTED; } /* Need to comment the below section for keyboard and mouse are low speed * device */ #if 0 if ((MSS_USB_DEVICE_LS == g_tdev[TDEV_R].speed)) { result = LS_DEV_NOT_SUPPORTED; } #endif /*0 return value indicates success*/ return (result); } /* * This function checks errors in the received configuration descriptor (9bytes) */ static int8_t validate_def_conf_desc ( def_conf_desc_t* p_desc ) { int8_t result = 0u; if (p_desc->bLength != USB_STD_CONFIG_DESCR_LEN) { result = CONF_DESC_WRONG_LENGTH; } if (p_desc->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) { result = CONF_DESC_WRONG_DESC_TYPE; } if (p_desc->bMaxPower > USB_MAX_BUS_POWER) { result = CONF_DESC_POWER_LIM_EXCEED; } /*0 return value indicates success*/ return (result); } /* * This function executes the sequence of actions when device is disconnected. */ static void mss_usbh_handle_discon_event ( void ) { gh_tdev_discon_event = 0u; tdev_idx = TDEV_R; if (0 != g_user_cb->usbh_tdev_dettached) { g_user_cb->usbh_tdev_dettached(); } if (1u == g_rcd[TDEV_R].alloc_state) { g_rcd[TDEV_R].class_handle->usbh_class_release(g_tdev[TDEV_R].addr); } g_tdev[TDEV_R].addr = USB_DEFAULT_TARGET_ADDR; g_tdev[TDEV_R].speed = MSS_USB_DEVICE_HS; g_tdev[TDEV_R].state = MSS_USB_NOT_ATTACHED_STATE; g_tdev[TDEV_R].hub_addr = 0u; g_tdev[TDEV_R].hub_port = 0u; g_tdev[TDEV_R].hub_mtt = 0u; g_tdev[TDEV_R].tdev_maxpktsz0 = CEP_MAX_PKT_SIZE; g_tdev[TDEV_R].class_handle = 0u; g_tdev_error_code = 0; memset(&g_tdev_desc[TDEV_R], 0u, sizeof(tdev_desc_t)); /* Un-register Class driver */ g_rcd[TDEV_R].alloc_state = 0u; mss_usbh_reset_enum_fsm(); MSS_USB_CIF_enable_usbirq(CONNECT_IRQ_MASK | DISCONNECT_IRQ_MASK); MSS_USBH_configure_control_pipe(USB_DEFAULT_TARGET_ADDR); MSS_USBH_CIF_tx_ep_set_target_func_addr(MSS_USB_CEP,0u); MSS_USBH_CIF_cep_set_type0_reg((mss_usb_device_speed_t)0u); } static int8_t host_enum_check_class_support ( tdev_id_t tid ) { uint8_t i = 0u; /* Max number of registered Class drivers supported is 1 */ for (i = 0u;i < 1u;i++) { /* Value 9 in interface descriptor's bInterfaceClass field indicates * HUB Class device */ if (0x09u == g_tdev_desc[tid].conf_desc.cs.upended_desc[5]) { return (HUB_CLASS_NOT_SUPPORTED); } /* Comparing assigned class driver with the interface descriptor's * bInterfaceClass, bInterfaceSubClass and bInterfaceProtocol * fields*/ if (((uint8_t)(g_rcd[i].class_handle->class_id>>16u) == g_tdev_desc[tid].conf_desc.cs.upended_desc[5]) && ((uint8_t)(g_rcd[i].class_handle->class_id>>8u) == g_tdev_desc[tid].conf_desc.cs.upended_desc[6]) && ((uint8_t)(g_rcd[i].class_handle->class_id) == g_tdev_desc[tid].conf_desc.cs.upended_desc[7])) { /* Class driver is matched and available. */ if (g_rcd[i].alloc_state == 0u) { /* Allocation successful */ return (i); } else { /* Match found but Driver is already assigned */ return (CLASS_DRIVER_NOT_FREE); } } } /* No matching class driver found. */ return (CLASS_DRIVER_NOT_MATCHED); } #endif /* MSS_USB_HOST_ENABLED */ #ifdef __cplusplus } #endif