1360 lines
47 KiB
C
1360 lines
47 KiB
C
/* -----------------------------------------------------------------------------
|
|
* Copyright (c) 2013-2017 ARM Ltd.
|
|
*
|
|
* This software is provided 'as-is', without any express or implied warranty.
|
|
* In no event will the authors be held liable for any damages arising from
|
|
* the use of this software. Permission is granted to anyone to use this
|
|
* software for any purpose, including commercial applications, and to alter
|
|
* it and redistribute it freely, subject to the following restrictions:
|
|
*
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software in
|
|
* a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
*
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
* misrepresented as being the original software.
|
|
*
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
*
|
|
*
|
|
* $Date: 19. September 2017
|
|
* $Revision: V2.4
|
|
*
|
|
* Driver: Driver_USBD0
|
|
* Configured: via RTE_Device.h configuration file
|
|
* Project: USB Full/Low-Speed Device Driver for ST STM32F1xx
|
|
* --------------------------------------------------------------------------
|
|
* Use the following configuration settings in the middleware component
|
|
* to connect to this driver.
|
|
*
|
|
* Configuration Setting Value
|
|
* --------------------- -----
|
|
* Connect to hardware via Driver_USBD# = 0
|
|
* --------------------------------------------------------------------------
|
|
* Defines used for driver configuration (at compile time):
|
|
*
|
|
* USBD_MAX_ENDPOINT_NUM: defines maximum number of IN/OUT Endpoint pairs
|
|
* that driver will support with Control Endpoint 0
|
|
* not included, this value impacts driver memory
|
|
* requirements
|
|
* - default value: 3
|
|
* - maximum value: 3
|
|
* USBD_FS_VBUS_DETECT: defines if driver supports VBUS detection
|
|
* - default value: 1 (=enabled)
|
|
* -------------------------------------------------------------------------- */
|
|
|
|
/* History:
|
|
* Version 2.4
|
|
* Added support for CMSIS-RTOS2
|
|
* Version 2.3
|
|
* Corrected resume event signaling
|
|
* Version 2.2
|
|
* Corrected initial resume signaling after USB Bus Reset
|
|
* VBUS sensing enabled by default
|
|
* Version 2.1
|
|
* Updated Isochronous transfer
|
|
* Updated IN Endpoint FIFO flush procedure
|
|
* Version 2.0
|
|
* Updated to CMSIS Driver API V2.01
|
|
* Version 1.4
|
|
* Multiple packet read
|
|
* Version 1.3
|
|
* Based on API V1.10 (namespace prefix ARM_ added)
|
|
* Version 1.2
|
|
* Removed include of rl_usb.h header
|
|
* Version 1.0
|
|
* Initial release
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "RTE_Components.h"
|
|
|
|
#if defined(RTE_CMSIS_RTOS2)
|
|
#include "cmsis_os2.h"
|
|
#elif defined(RTE_CMSIS_RTOS)
|
|
#include "cmsis_os.h"
|
|
#endif
|
|
|
|
#include "Driver_USBD.h"
|
|
|
|
#include "stm32f10x.h"
|
|
|
|
#include "GPIO_STM32F10x.h"
|
|
#include "OTG_STM32F10x_cl.h"
|
|
|
|
#ifndef USBD_MAX_ENDPOINT_NUM
|
|
#define USBD_MAX_ENDPOINT_NUM (3U)
|
|
#endif
|
|
#if (USBD_MAX_ENDPOINT_NUM > 3)
|
|
#error Too many Endpoints, maximum IN/OUT Endpoint pairs that this driver supports is 3 !!!
|
|
#endif
|
|
|
|
#ifndef USBD_FS_VBUS_DETECT
|
|
#define USBD_FS_VBUS_DETECT (1U)
|
|
#endif
|
|
|
|
extern uint8_t otg_fs_role;
|
|
|
|
extern void OTG_FS_PinsConfigure (uint8_t pins_mask);
|
|
extern void OTG_FS_PinsUnconfigure (uint8_t pins_mask);
|
|
|
|
|
|
// USBD Driver *****************************************************************
|
|
|
|
#define ARM_USBD_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,4)
|
|
|
|
// Driver Version
|
|
static const ARM_DRIVER_VERSION usbd_driver_version = { ARM_USBD_API_VERSION, ARM_USBD_DRV_VERSION };
|
|
|
|
// Driver Capabilities
|
|
static const ARM_USBD_CAPABILITIES usbd_driver_capabilities = {
|
|
#if (USBD_FS_VBUS_DETECT == 1)
|
|
1U, // VBUS Detection
|
|
1U, // Event VBUS On
|
|
1U, // Event VBUS Off
|
|
#else
|
|
0U, // VBUS Detection
|
|
0U, // Event VBUS On
|
|
0U // Event VBUS Off
|
|
#endif
|
|
};
|
|
|
|
#define OTG (OTG_FS)
|
|
|
|
#define EP_NUM(ep_addr) ((ep_addr) & ARM_USB_ENDPOINT_NUMBER_MASK)
|
|
#define EP_ID(ep_addr) ((EP_NUM(ep_addr) * 2U) + (((ep_addr) >> 7) & 1U))
|
|
|
|
// FIFO sizes in bytes (total available memory for FIFOs is 1.25 kB)
|
|
#ifndef OTG_RX_FIFO_SIZE
|
|
#define OTG_RX_FIFO_SIZE (640U)
|
|
#endif
|
|
#ifndef OTG_TX0_FIFO_SIZE
|
|
#define OTG_TX0_FIFO_SIZE (160U)
|
|
#endif
|
|
#ifndef OTG_TX1_FIFO_SIZE
|
|
#define OTG_TX1_FIFO_SIZE (160U)
|
|
#endif
|
|
#ifndef OTG_TX2_FIFO_SIZE
|
|
#define OTG_TX2_FIFO_SIZE (160U)
|
|
#endif
|
|
#ifndef OTG_TX3_FIFO_SIZE
|
|
#define OTG_TX3_FIFO_SIZE (160U)
|
|
#endif
|
|
|
|
#define OTG_TX_FIFO(n) *((volatile uint32_t *)(OTG_FS_BASE + 0x1000U + (n * 0x1000U)))
|
|
#define OTG_RX_FIFO *((volatile uint32_t *)(OTG_FS_BASE + 0x1000U))
|
|
|
|
#define OTG_DIEPTSIZ(ep_num) *((volatile uint32_t *)(&OTG->DIEPTSIZ0 + (ep_num * 8U)))
|
|
#define OTG_DIEPCTL(ep_num) *((volatile uint32_t *)(&OTG->DIEPCTL0 + (ep_num * 8U)))
|
|
#define OTG_DTXFSTS(ep_num) *((volatile uint32_t *)(&OTG->DTXFSTS0 + (ep_num * 8U)))
|
|
#define OTG_DOEPTSIZ(ep_num) *((volatile uint32_t *)(&OTG->DOEPTSIZ0 + (ep_num * 8U)))
|
|
#define OTG_DOEPCTL(ep_num) *((volatile uint32_t *)(&OTG->DOEPCTL0 + (ep_num * 8U)))
|
|
#define OTG_DIEPINT(ep_num) *((volatile uint32_t *)(&OTG->DIEPINT0 + (ep_num * 8U)))
|
|
#define OTG_DOEPINT(ep_num) *((volatile uint32_t *)(&OTG->DOEPINT0 + (ep_num * 8U)))
|
|
|
|
#define OTG_EP_IN_TYPE(ep_num) ((OTG_DIEPCTL(ep_num) >> 18) & 3U)
|
|
#define OTG_EP_OUT_TYPE(ep_num) ((OTG_DOEPCTL(ep_num) >> 18) & 3U)
|
|
|
|
typedef struct { // Endpoint structure definition
|
|
uint8_t *data;
|
|
uint32_t num;
|
|
uint32_t num_transferred_total;
|
|
uint16_t num_transferring;
|
|
uint16_t max_packet_size;
|
|
uint8_t active;
|
|
uint8_t in_nak;
|
|
uint8_t in_zlp;
|
|
uint8_t in_flush;
|
|
} ENDPOINT_t;
|
|
|
|
static ARM_USBD_SignalDeviceEvent_t SignalDeviceEvent;
|
|
static ARM_USBD_SignalEndpointEvent_t SignalEndpointEvent;
|
|
|
|
static bool hw_powered = false;
|
|
static bool hw_initialized = false;
|
|
static ARM_USBD_STATE usbd_state;
|
|
static uint32_t setup_packet[2]; // Setup packet data
|
|
static volatile uint8_t setup_received; // Setup packet received
|
|
|
|
// Endpoints runtime information
|
|
static volatile ENDPOINT_t ep[(USBD_MAX_ENDPOINT_NUM + 1U) * 2U];
|
|
|
|
// Function prototypes
|
|
static uint16_t USBD_GetFrameNumber (void);
|
|
|
|
|
|
// Auxiliary functions
|
|
|
|
/**
|
|
\fn void USBD_FlushInEpFifo (uint8_t FIFO_num)
|
|
\brief Flush IN Endpoint FIFO
|
|
\param[in] FIFO_num IN Endpoint FIFO number
|
|
- FIFO_num.0..3: IN Endpoint FIFO to Flush
|
|
- FIFO_num.4: All IN Endpoint FIFOs to Flush
|
|
*/
|
|
static void USBD_FlushInEpFifo (uint8_t FIFO_num) {
|
|
|
|
while ((OTG->GRSTCTL & OTG_FS_GRSTCTL_TXFFLSH) != 0U);
|
|
OTG->GRSTCTL = (OTG->GRSTCTL & ~OTG_FS_GRSTCTL_TXFNUM_MSK) | OTG_FS_GRSTCTL_TXFNUM(FIFO_num);
|
|
OTG->GRSTCTL |= OTG_FS_GRSTCTL_TXFFLSH;
|
|
while ((OTG->GRSTCTL & OTG_FS_GRSTCTL_TXFFLSH) != 0U);
|
|
|
|
}
|
|
|
|
/**
|
|
\fn void USBD_Reset (void)
|
|
\brief Reset USB Endpoint settings and variables.
|
|
*/
|
|
static void USBD_Reset (void) {
|
|
uint8_t i;
|
|
uint32_t epctl;
|
|
|
|
// Reset global variables
|
|
setup_packet[0] = 0U;
|
|
setup_packet[1] = 0U;
|
|
setup_received = 0U;
|
|
memset((void *)(ep), 0U, sizeof(ep));
|
|
|
|
// Clear Endpoint mask registers
|
|
OTG->DOEPMSK = 0U;
|
|
OTG->DIEPMSK = 0U;
|
|
|
|
for (i = 1U; i <= USBD_MAX_ENDPOINT_NUM; i++) {
|
|
// Endpoint set NAK
|
|
epctl = OTG_FS_DOEPCTLx_SNAK;
|
|
if ((OTG_DOEPCTL(i) & OTG_FS_DOEPCTLx_EPENA) != 0U) {
|
|
// Disable enabled Endpoint
|
|
epctl |= OTG_FS_DOEPCTLx_EPDIS;
|
|
}
|
|
OTG_DOEPCTL(i) = epctl;
|
|
|
|
// Endpoint set NAK
|
|
epctl = OTG_FS_DIEPCTLx_SNAK;
|
|
if ((OTG_DIEPCTL(i) & OTG_FS_DIEPCTLx_EPENA) != 0U) {
|
|
// Disable enabled Endpoint
|
|
epctl |= OTG_FS_DIEPCTLx_EPDIS;
|
|
}
|
|
OTG_DIEPCTL(i) = epctl;
|
|
|
|
// Clear IN Endpoint interrupts
|
|
OTG_DIEPINT(i) = OTG_FS_DIEPINTx_XFCR |
|
|
OTG_FS_DIEPINTx_EPDISD |
|
|
OTG_FS_DIEPINTx_TOC |
|
|
OTG_FS_DIEPINTx_ITTXFE |
|
|
OTG_FS_DIEPINTx_INEPNE |
|
|
OTG_FS_DIEPINTx_TXFE ;
|
|
|
|
// Clear OUT Endpoint interrupts
|
|
OTG_DOEPINT(i) = OTG_FS_DOEPINTx_XFCR |
|
|
OTG_FS_DOEPINTx_EPDISD |
|
|
OTG_FS_DOEPINTx_STUP |
|
|
OTG_FS_DOEPINTx_OTEPDIS |
|
|
OTG_FS_DOEPINTx_B2BSTUP ;
|
|
}
|
|
|
|
// Flush all IN Endpoint FIFOs
|
|
USBD_FlushInEpFifo (0x10U);
|
|
|
|
// Set device address to 0
|
|
OTG->DCFG = (OTG->DCFG & ~OTG_FS_DCFG_DAD_MSK);
|
|
OTG->DAINTMSK = OTG_FS_DAINT_IEPINT(0U) | // Enable IN Endpoint0 interrupt
|
|
OTG_FS_DAINT_OEPINT(0U); // Enable OUT Endpoint0 interrupt
|
|
|
|
// Enable Setup phase done, OUT Endpoint disabled and OUT transfer complete interrupt
|
|
OTG->DOEPMSK = OTG_FS_DOEPMSK_STUPM |
|
|
OTG_FS_DOEPMSK_EPDM |
|
|
OTG_FS_DOEPMSK_XFRCM ;
|
|
|
|
// Enable In Endpoint disable and IN transfer complete interrupt
|
|
OTG->DIEPMSK = OTG_FS_DIEPMSK_EPDM |
|
|
OTG_FS_DIEPMSK_XFRCM ;
|
|
|
|
// Configure FIFOs
|
|
OTG->GRXFSIZ = OTG_RX_FIFO_SIZE / 4U;
|
|
OTG->DIEPTXF0 = (OTG_RX_FIFO_SIZE / 4U) |
|
|
((OTG_TX0_FIFO_SIZE / 4U) << OTG_FS_DIEPTXFx_INEPTXFD_POS);
|
|
|
|
OTG->DIEPTXF1 = ((OTG_RX_FIFO_SIZE + OTG_TX0_FIFO_SIZE) / 4U) |
|
|
((OTG_TX1_FIFO_SIZE / 4U) << OTG_FS_DIEPTXFx_INEPTXFD_POS);
|
|
|
|
OTG->DIEPTXF2 = ((OTG_RX_FIFO_SIZE + OTG_TX0_FIFO_SIZE + OTG_TX1_FIFO_SIZE) / 4U) |
|
|
((OTG_TX2_FIFO_SIZE / 4U) << OTG_FS_DIEPTXFx_INEPTXFD_POS);
|
|
|
|
OTG->DIEPTXF3 = ((OTG_RX_FIFO_SIZE + OTG_TX0_FIFO_SIZE + OTG_TX1_FIFO_SIZE +
|
|
OTG_TX2_FIFO_SIZE)/ 4U) |
|
|
((OTG_TX3_FIFO_SIZE / 4U) << OTG_FS_DIEPTXFx_INEPTXFD_POS);
|
|
}
|
|
|
|
/**
|
|
\fn void USBD_EndpointReadSet (uint8_t ep_addr)
|
|
\brief Set Endpoint for next read.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
*/
|
|
static void USBD_EndpointReadSet (uint8_t ep_addr) {
|
|
volatile ENDPOINT_t *ptr_ep;
|
|
uint16_t num;
|
|
uint8_t ep_num;
|
|
|
|
ptr_ep = &ep[EP_ID(ep_addr)];
|
|
ep_num = EP_NUM(ep_addr);
|
|
|
|
// Set packet count and transfer size
|
|
if (ptr_ep->num > ptr_ep->max_packet_size) { num = ptr_ep->max_packet_size; }
|
|
else { num = ptr_ep->num; }
|
|
|
|
if (ep_num != 0U) {
|
|
OTG_DOEPTSIZ(ep_num) = (1U << OTG_FS_DOEPTSIZx_PKTCNT_POS ) |
|
|
num ;
|
|
} else {
|
|
OTG_DOEPTSIZ(0U) = (1U << OTG_FS_DOEPTSIZx_PKTCNT_POS ) |
|
|
(3U << OTG_FS_DOEPTSIZ0_STUPCNT_POS) |
|
|
num ;
|
|
}
|
|
// Set correct frame for Isochronous Endpoint
|
|
if (OTG_EP_OUT_TYPE(ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
if ((USBD_GetFrameNumber() & 1U) != 0U) { OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_SEVNFRM; }
|
|
else { OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_SODDFRM; }
|
|
}
|
|
|
|
// Clear NAK and enable Endpoint
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_EPENA | OTG_FS_DOEPCTLx_CNAK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_ReadFromFifo (uint8_t ep_addr, uint16_t num)
|
|
\brief Read data from USB Endpoint.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
\param[in] num number of data bytes to read
|
|
\return number of data bytes read
|
|
*/
|
|
static int32_t USBD_ReadFromFifo (uint8_t ep_addr, uint16_t num) {
|
|
volatile ENDPOINT_t *ptr_ep;
|
|
uint32_t i, residue;
|
|
uint8_t *ptr_dest_8;
|
|
__packed uint32_t *ptr_dest_32;
|
|
volatile uint32_t *ptr_src;
|
|
uint8_t ep_num;
|
|
uint8_t tmp_buf[4];
|
|
|
|
ptr_ep = &ep[EP_ID(ep_addr)];
|
|
ep_num = EP_NUM(ep_addr);
|
|
|
|
// Check if Endpoint is activated and buffer available
|
|
if ((OTG_DOEPCTL(ep_num) & OTG_FS_DOEPCTLx_USBAEP) == 0U) { return 0U; }
|
|
if (ptr_ep->data == 0U) { return 0U; }
|
|
|
|
if (num > ptr_ep->num) { num = ptr_ep->num; }
|
|
|
|
// If Isochronous Endpoint
|
|
if (OTG_EP_OUT_TYPE(ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
// If DATA PID = DATA0 and number of packets in
|
|
// which this payload was received = 1, then data is valid
|
|
if ((OTG_DOEPTSIZ(ep_num) & (OTG_FS_DOEPTSIZx_PKTCNT_MSK | OTG_FS_DOEPTSIZx_RXDPID_MSK)) != 0) { num = 0U; }
|
|
}
|
|
|
|
// Copy data from FIFO
|
|
ptr_src = (volatile uint32_t *)(OTG_FS_BASE + 0x1000U);
|
|
ptr_dest_32 = (__packed uint32_t *)(ptr_ep->data + ptr_ep->num_transferred_total);
|
|
i = num / 4U;
|
|
while (i != 0U) {
|
|
*ptr_dest_32++ = *ptr_src;
|
|
i--;
|
|
}
|
|
ptr_ep->num_transferred_total += num;
|
|
|
|
// If data size is not equal n*4
|
|
residue = num % 4U;
|
|
if (residue != 0U) {
|
|
ptr_dest_8 = (uint8_t *)(ptr_dest_32);
|
|
*((__packed uint32_t *)tmp_buf) = OTG_RX_FIFO;
|
|
for (i = 0U; i < residue; i++) {
|
|
*ptr_dest_8++ = tmp_buf[i];
|
|
}
|
|
}
|
|
|
|
if (num != ptr_ep->max_packet_size) { ptr_ep->num = 0U; }
|
|
else { ptr_ep->num -= num; }
|
|
|
|
return num;
|
|
}
|
|
|
|
/**
|
|
\fn void USBD_WriteToFifo (uint8_t ep_addr)
|
|
\brief Write data to Endpoint FIFO.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
*/
|
|
static void USBD_WriteToFifo (uint8_t ep_addr) {
|
|
volatile ENDPOINT_t *ptr_ep;
|
|
uint8_t ep_num;
|
|
uint16_t num, i;
|
|
volatile uint32_t *ptr_dest;
|
|
__packed uint32_t *ptr_src;
|
|
|
|
ptr_ep = &ep[EP_ID(ep_addr)];
|
|
ep_num = EP_NUM(ep_addr);
|
|
|
|
if (ptr_ep->num > ptr_ep->max_packet_size) { num = ptr_ep->max_packet_size; }
|
|
else { num = ptr_ep->num; }
|
|
|
|
// Check if enough space in FIFO
|
|
if ((OTG_DTXFSTS(ep_num) * 4U) < num) { return; }
|
|
|
|
// Set transfer size and packet count
|
|
OTG_DIEPTSIZ(ep_num) = (1U << OTG_FS_DIEPTSIZx_PKTCNT_POS) |
|
|
(1U << OTG_FS_DIEPTSIZx_MCNT_POS) |
|
|
num ;
|
|
|
|
// Set correct frame for Isochronous Endpoint
|
|
if (OTG_EP_IN_TYPE(ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
if ((USBD_GetFrameNumber() & 1U) != 0U) { OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_SEVNFRM; }
|
|
else { OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_SODDFRM; }
|
|
}
|
|
|
|
// Enable Endpoint and clear NAK
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_EPENA | OTG_FS_DIEPCTLx_CNAK;
|
|
|
|
ptr_src = (__packed uint32_t *)(ptr_ep->data + ptr_ep->num_transferred_total);
|
|
ptr_dest = (volatile uint32_t *)(OTG_FS_BASE + 0x1000U + (ep_num * 0x1000U));
|
|
|
|
ptr_ep->num_transferring = num;
|
|
ptr_ep->num -= num;
|
|
ptr_ep->in_zlp = 0U;
|
|
|
|
// Copy data to FIFO
|
|
i = (num + 3U) >> 2;
|
|
while (i != 0U) {
|
|
*ptr_dest = *ptr_src++;
|
|
i--;
|
|
}
|
|
}
|
|
|
|
|
|
// USBD Driver functions
|
|
|
|
/**
|
|
\fn ARM_DRIVER_VERSION USBD_GetVersion (void)
|
|
\brief Get driver version.
|
|
\return \ref ARM_DRIVER_VERSION
|
|
*/
|
|
static ARM_DRIVER_VERSION USBD_GetVersion (void) { return usbd_driver_version; }
|
|
|
|
/**
|
|
\fn ARM_USBD_CAPABILITIES USBD_GetCapabilities (void)
|
|
\brief Get driver capabilities.
|
|
\return \ref ARM_USBD_CAPABILITIES
|
|
*/
|
|
static ARM_USBD_CAPABILITIES USBD_GetCapabilities (void) { return usbd_driver_capabilities; }
|
|
|
|
/**
|
|
\fn int32_t USBD_Initialize (ARM_USBD_SignalDeviceEvent_t cb_device_event,
|
|
ARM_USBD_SignalEndpointEvent_t cb_endpoint_event)
|
|
\brief Initialize USB Device Interface.
|
|
\param[in] cb_device_event Pointer to \ref ARM_USBD_SignalDeviceEvent
|
|
\param[in] cb_endpoint_event Pointer to \ref ARM_USBD_SignalEndpointEvent
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_Initialize (ARM_USBD_SignalDeviceEvent_t cb_device_event,
|
|
ARM_USBD_SignalEndpointEvent_t cb_endpoint_event) {
|
|
|
|
if (hw_initialized == true) {
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
SignalDeviceEvent = cb_device_event;
|
|
SignalEndpointEvent = cb_endpoint_event;
|
|
|
|
otg_fs_role = ARM_USB_ROLE_DEVICE;
|
|
OTG_FS_PinsConfigure (ARM_USB_PIN_DP | ARM_USB_PIN_DM
|
|
#if (USBD_FS_VBUS_DETECT == 1)
|
|
| ARM_USB_PIN_VBUS
|
|
#endif
|
|
);
|
|
|
|
hw_initialized = true;
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_Uninitialize (void)
|
|
\brief De-initialize USB Device Interface.
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_Uninitialize (void) {
|
|
|
|
OTG_FS_PinsUnconfigure (ARM_USB_PIN_DP | ARM_USB_PIN_DM
|
|
#if (USBD_FS_VBUS_DETECT == 1)
|
|
| ARM_USB_PIN_VBUS
|
|
#endif
|
|
);
|
|
|
|
otg_fs_role = ARM_USB_ROLE_NONE;
|
|
|
|
hw_initialized = false;
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_PowerControl (ARM_POWER_STATE state)
|
|
\brief Control USB Device Interface Power.
|
|
\param[in] state Power state
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_PowerControl (ARM_POWER_STATE state) {
|
|
|
|
switch (state) {
|
|
case ARM_POWER_OFF:
|
|
RCC->AHBENR |= RCC_AHBENR_OTGFSEN; // OTG FS clock enable
|
|
NVIC_DisableIRQ (OTG_FS_IRQn); // Disable interrupt
|
|
NVIC_ClearPendingIRQ (OTG_FS_IRQn); // Clear pending interrupt
|
|
|
|
hw_powered = false; // Clear powered flag
|
|
OTG->DCTL |= OTG_FS_DCTL_SDIS; // Soft disconnect enabled
|
|
OTG->GAHBCFG &= ~OTG_FS_GAHBCFG_GINTMSK; // Disable USB interrupts
|
|
RCC->AHBRSTR |= RCC_AHBRSTR_OTGFSRST; // Reset OTG FS module
|
|
// Reset variables
|
|
setup_received = 0U;
|
|
memset((void *)(&usbd_state), 0, sizeof(usbd_state));
|
|
memset((void *)(ep), 0, sizeof(ep));
|
|
|
|
OTG->GCCFG &= ~OTG_FS_GCCFG_PWRDWN; // Enable PHY power down
|
|
OTG->PCGCCTL |= OTG_FS_PCGCCTL_STPPCLK; // Stop PHY clock
|
|
OTG->GCCFG = 0U; // Reset core configuration
|
|
|
|
RCC->AHBENR &= ~RCC_AHBENR_OTGFSEN; // Disable OTG FS clock
|
|
|
|
break;
|
|
|
|
case ARM_POWER_FULL:
|
|
if (hw_initialized == false) {
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
if (hw_powered == true) {
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
RCC->AHBENR |= RCC_AHBENR_OTGFSEN; // OTG FS clock enable
|
|
RCC->AHBRSTR |= RCC_AHBRSTR_OTGFSRST; // Reset OTG FS module
|
|
osDelay(1U);
|
|
RCC->AHBRSTR &= ~RCC_AHBRSTR_OTGFSRST; // Clear reset of OTG FS module
|
|
osDelay(1U);
|
|
|
|
OTG->GCCFG |= OTG_FS_GCCFG_PWRDWN; // Disable power down
|
|
OTG->GUSBCFG |= OTG_FS_GUSBCFG_PHYSEL; // Full-speed transceiver
|
|
|
|
// Wait until AHB Master state machine is in the idle condition
|
|
while ((OTG->GRSTCTL & OTG_FS_GRSTCTL_AHBIDL) == 0U);
|
|
|
|
OTG->GRSTCTL |= OTG_FS_GRSTCTL_CSRST; // Core soft reset
|
|
while ((OTG->GRSTCTL & OTG_FS_GRSTCTL_CSRST) != 0U);
|
|
osDelay(1U);
|
|
|
|
// Wait until AHB Master state machine is in the idle condition
|
|
while ((OTG->GRSTCTL & OTG_FS_GRSTCTL_AHBIDL) == 0U);
|
|
|
|
OTG->DCTL |= OTG_FS_DCTL_SDIS; // Soft disconnect enabled
|
|
|
|
// Set turnaround time
|
|
OTG->GUSBCFG = ((OTG->GUSBCFG & ~OTG_FS_GUSBCFG_TRDT_MSK) |
|
|
OTG_FS_GUSBCFG_TRDT(15U)) ;
|
|
if (((OTG->GUSBCFG & OTG_FS_GUSBCFG_FDMOD) == 0U) || ((OTG->GUSBCFG & OTG_FS_GUSBCFG_FHMOD) != 0U)) {
|
|
OTG->GUSBCFG &= ~OTG_FS_GUSBCFG_FHMOD; // Clear force host mode
|
|
OTG->GUSBCFG |= OTG_FS_GUSBCFG_FDMOD; // Force device mode
|
|
osDelay(50U);
|
|
}
|
|
|
|
USBD_Reset (); // Reset variables and endpoint settings
|
|
|
|
#if (USBD_FS_VBUS_DETECT == 1)
|
|
OTG->GCCFG |= OTG_FS_GCCFG_VBUSBSEN; // Enable VBUS sensing device "B"
|
|
#else
|
|
OTG->GCCFG |= OTG_FS_GCCFG_NOVBUSSENS; // Disable VBUS sensing
|
|
#endif
|
|
|
|
OTG->DCFG |= OTG_FS_DCFG_DSPD_MSK; // Full Speed
|
|
|
|
OTG->GINTMSK = (OTG_FS_GINTMSK_USBSUSPM | // Unmask interrupts
|
|
OTG_FS_GINTMSK_USBRST |
|
|
OTG_FS_GINTMSK_ENUMDNEM |
|
|
OTG_FS_GINTMSK_RXFLVLM |
|
|
OTG_FS_GINTMSK_IEPINT |
|
|
OTG_FS_GINTMSK_OEPINT |
|
|
#if (USBD_FS_VBUS_DETECT == 1)
|
|
OTG_FS_GINTMSK_SRQIM |
|
|
OTG_FS_GINTMSK_OTGINT |
|
|
#endif
|
|
OTG_FS_GINTMSK_WUIM) ;
|
|
|
|
OTG->GAHBCFG |= (OTG_FS_GAHBCFG_GINTMSK | // Enable interrupts
|
|
OTG_FS_GAHBCFG_TXFELVL) ;
|
|
|
|
hw_powered = true; // Set powered flag
|
|
NVIC_EnableIRQ (OTG_FS_IRQn); // Enable interrupt
|
|
break;
|
|
|
|
default:
|
|
return ARM_DRIVER_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_DeviceConnect (void)
|
|
\brief Connect USB Device.
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_DeviceConnect (void) {
|
|
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
OTG->DCTL &= ~OTG_FS_DCTL_SDIS; // Soft disconnect disabled
|
|
OTG->GCCFG |= OTG_FS_GCCFG_PWRDWN; // Disable power down
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_DeviceDisconnect (void)
|
|
\brief Disconnect USB Device.
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_DeviceDisconnect (void) {
|
|
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
OTG->DCTL |= OTG_FS_DCTL_SDIS; // Soft disconnect enabled
|
|
OTG->GCCFG &= ~OTG_FS_GCCFG_PWRDWN; // Enable power down
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn ARM_USBD_STATE USBD_DeviceGetState (void)
|
|
\brief Get current USB Device State.
|
|
\return Device State \ref ARM_USBD_STATE
|
|
*/
|
|
static ARM_USBD_STATE USBD_DeviceGetState (void) {
|
|
return usbd_state;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_DeviceRemoteWakeup (void)
|
|
\brief Trigger USB Remote Wakeup.
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_DeviceRemoteWakeup (void) {
|
|
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
OTG->DCTL |= OTG_FS_DCTL_RWUSIG; // Remote wakeup signaling
|
|
osDelay(5U);
|
|
OTG->DCTL &= ~OTG_FS_DCTL_RWUSIG;
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_DeviceSetAddress (uint8_t dev_addr)
|
|
\brief Set USB Device Address.
|
|
\param[in] dev_addr Device Address
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_DeviceSetAddress (uint8_t dev_addr) {
|
|
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
OTG->DCFG = (OTG->DCFG & ~OTG_FS_DCFG_DAD_MSK) | OTG_FS_DCFG_DAD(dev_addr);
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_ReadSetupPacket (uint8_t *setup)
|
|
\brief Read setup packet received over Control Endpoint.
|
|
\param[out] setup Pointer to buffer for setup packet
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_ReadSetupPacket (uint8_t *setup) {
|
|
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
if (setup_received == 0U) { return ARM_DRIVER_ERROR; }
|
|
|
|
setup_received = 0U;
|
|
memcpy(setup, setup_packet, 8);
|
|
|
|
if (setup_received != 0U) { // If new setup packet was received while this was being read
|
|
return ARM_DRIVER_ERROR;
|
|
}
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_EndpointConfigure (uint8_t ep_addr,
|
|
uint8_t ep_type,
|
|
uint16_t ep_max_packet_size)
|
|
\brief Configure USB Endpoint.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
\param[in] ep_type Endpoint Type (ARM_USB_ENDPOINT_xxx)
|
|
\param[in] ep_max_packet_size Endpoint Maximum Packet Size
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_EndpointConfigure (uint8_t ep_addr,
|
|
uint8_t ep_type,
|
|
uint16_t ep_max_packet_size) {
|
|
volatile ENDPOINT_t *ptr_ep;
|
|
uint8_t ep_num;
|
|
uint16_t ep_mps;
|
|
bool ep_dir;
|
|
|
|
ep_num = EP_NUM(ep_addr);
|
|
if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; }
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
ptr_ep = &ep[EP_ID(ep_addr)];
|
|
if (ptr_ep->active != 0U) { return ARM_DRIVER_ERROR_BUSY; }
|
|
|
|
ep_dir = (ep_addr & ARM_USB_ENDPOINT_DIRECTION_MASK) == ARM_USB_ENDPOINT_DIRECTION_MASK;
|
|
ep_mps = ep_max_packet_size & ARM_USB_ENDPOINT_MAX_PACKET_SIZE_MASK;
|
|
|
|
// Clear Endpoint transfer and configuration information
|
|
memset((void *)(ptr_ep), 0, sizeof (ENDPOINT_t));
|
|
|
|
// Set maximum packet size to requested
|
|
ptr_ep->max_packet_size = ep_mps;
|
|
|
|
// IN Endpoint
|
|
if (ep_dir != 0U) {
|
|
ptr_ep->in_nak = 0U; // Clear IN Endpoint NAK flag
|
|
|
|
// Configure IN Endpoint
|
|
OTG_DIEPCTL(ep_num) = (ep_num << OTG_FS_DIEPCTLx_TXFNUM_POS) | // FIFO Number
|
|
(ep_type << OTG_FS_DIEPCTLx_EPTYP_POS ) | // Endpoint Type
|
|
ep_mps ; // Max Packet Size
|
|
|
|
// Set DATA0 PID for Interrupt or Bulk Endpoint
|
|
if (ep_type >= ARM_USB_ENDPOINT_BULK) {
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_SD0PID;
|
|
}
|
|
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_USBAEP; // Activate Endpoint
|
|
|
|
if ((OTG_DIEPCTL(ep_num) & OTG_FS_DIEPCTLx_EPENA) != 0U) {
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_EPDIS; // Disable Endpoint
|
|
}
|
|
|
|
// Isochronous IN Endpoint Configuration
|
|
if (OTG_EP_IN_TYPE(ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
OTG->GINTMSK |= OTG_FS_GINTMSK_EOPFM; // Enable End of Periodic Frame Interrupt
|
|
}
|
|
|
|
OTG->DAINTMSK |= (1U << ep_num); // Enable Endpoint interrupt
|
|
|
|
} else {
|
|
// OUT Endpoint
|
|
|
|
// Configure OUT Endpoint
|
|
OTG_DOEPCTL(ep_num) = (ep_type << OTG_FS_DOEPCTLx_EPTYP_POS) | // Endpoint Type
|
|
OTG_FS_DOEPCTLx_SNAK | // Set NAK
|
|
ep_mps ; // Max Packet Size
|
|
|
|
// Set DATA0 PID for Interrupt or Bulk Endpoint
|
|
if (ep_type >= ARM_USB_ENDPOINT_BULK) {
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_SD0PID;
|
|
}
|
|
|
|
// Isochronous OUT Endpoint Configuration
|
|
if (OTG_EP_OUT_TYPE(ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
OTG->GINTMSK |= OTG_FS_GINTMSK_EOPFM; // Enable End of Periodic Frame Interrupt
|
|
}
|
|
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_USBAEP; // Activate Endpoint
|
|
|
|
OTG->DAINTMSK |= (1U << (ep_num + 16)); // Enable Endpoint interrupt
|
|
}
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_EndpointUnconfigure (uint8_t ep_addr)
|
|
\brief Unconfigure USB Endpoint.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_EndpointUnconfigure (uint8_t ep_addr) {
|
|
volatile ENDPOINT_t *ptr_ep;
|
|
uint8_t ep_num, i, IsoEpEnCnt;
|
|
bool ep_dir;
|
|
|
|
ep_num = EP_NUM(ep_addr);
|
|
if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; }
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
ptr_ep = &ep[EP_ID(ep_addr)];
|
|
if (ptr_ep->active != 0U) { return ARM_DRIVER_ERROR_BUSY; }
|
|
|
|
ep_dir = (ep_addr & ARM_USB_ENDPOINT_DIRECTION_MASK) == ARM_USB_ENDPOINT_DIRECTION_MASK;
|
|
|
|
OTG->DAINTMSK &= ~(1U << (ep_num + ((ep_dir ^ 1U) << 4))); // Disable Endpoint interrupt
|
|
|
|
// Clear Endpoint transfer and configuration information
|
|
memset((void *)(ptr_ep), 0, sizeof (ENDPOINT_t));
|
|
|
|
IsoEpEnCnt = 0U;
|
|
|
|
// Count Active Isochronous OUT and IN Endpoints
|
|
if ((OTG_EP_OUT_TYPE(ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS) ||
|
|
(OTG_EP_IN_TYPE (ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS)) {
|
|
for (i = 1U; i <= USBD_MAX_ENDPOINT_NUM; i++) {
|
|
if ((OTG_DOEPCTL(i) & OTG_FS_DOEPCTLx_USBAEP) != 0U) {
|
|
if (OTG_EP_OUT_TYPE(i) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
IsoEpEnCnt++;
|
|
}
|
|
}
|
|
if ((OTG_DIEPCTL(i) & OTG_FS_DIEPCTLx_USBAEP) != 0U) {
|
|
if (OTG_EP_IN_TYPE(i) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
IsoEpEnCnt++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If Last Active Isochronous OUT or IN Endpoint, Disable EOPF
|
|
if (IsoEpEnCnt == 1U) { OTG->GINTMSK &= ~OTG_FS_GINTMSK_EOPFM; }
|
|
}
|
|
|
|
// IN Endpoint
|
|
if (ep_dir != 0U) {
|
|
// Disable Enabled IN Endpoint
|
|
if ((OTG_DIEPCTL(ep_num) & OTG_FS_DIEPCTLx_EPENA) != 0U) {
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_EPDIS;
|
|
}
|
|
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_SNAK; // Set Endpoint NAK
|
|
|
|
// Deactivate Endpoint
|
|
if (ep_num != 0U) { OTG_DIEPCTL(ep_num) &= ~OTG_FS_DIEPCTLx_USBAEP; }
|
|
|
|
} else {
|
|
// OUT Endpoint
|
|
|
|
OTG->DCTL |= OTG_FS_DCTL_SGONAK; // Set Global OUT NAK
|
|
while ((OTG->GINTSTS & OTG_FS_GINTSTS_GONAKEFF) == 0U);
|
|
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_SNAK; // Set Endpoint NAK
|
|
|
|
if (ep_num != 0U) {
|
|
// Disable Enabled OUT Endpoint
|
|
if ((OTG_DOEPCTL(ep_num) & OTG_FS_DOEPCTLx_EPENA) != 0U) {
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_EPDIS;
|
|
while ((OTG_DOEPINT(ep_num) & OTG_FS_DOEPINTx_EPDISD) == 0U);
|
|
}
|
|
OTG_DOEPCTL(ep_num) &= ~OTG_FS_DOEPCTLx_USBAEP; // Deactivate Endpoint
|
|
}
|
|
|
|
OTG->DCTL |= OTG_FS_DCTL_CGONAK; // Clear Global OUT NAK
|
|
}
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_EndpointStall (uint8_t ep_addr, bool stall)
|
|
\brief Set/Clear Stall for USB Endpoint.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
\param[in] stall Operation
|
|
- \b false Clear
|
|
- \b true Set
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_EndpointStall (uint8_t ep_addr, bool stall) {
|
|
volatile ENDPOINT_t *ptr_ep;
|
|
uint8_t ep_num;
|
|
bool ep_dir;
|
|
|
|
ep_num = EP_NUM(ep_addr);
|
|
if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; }
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
ptr_ep = &ep[EP_ID(ep_addr)];
|
|
if (ptr_ep->active != 0U) { return ARM_DRIVER_ERROR_BUSY; }
|
|
|
|
ep_dir = (ep_addr & ARM_USB_ENDPOINT_DIRECTION_MASK) == ARM_USB_ENDPOINT_DIRECTION_MASK;
|
|
|
|
if (stall != 0U) { // Activate STALL
|
|
if (ep_dir != 0U) { // IN Endpoint
|
|
if ((OTG_DIEPCTL(ep_num) & OTG_FS_DIEPCTLx_EPENA) != 0U) {
|
|
// Set flush flag to Flush IN FIFO in Endpoint disabled interrupt
|
|
ptr_ep->in_flush = 1U;
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_STALL | OTG_FS_DIEPCTLx_EPDIS;
|
|
} else {
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_STALL;
|
|
USBD_FlushInEpFifo (ep_num);
|
|
}
|
|
} else { // OUT Endpoint
|
|
OTG->DCTL |= OTG_FS_DCTL_SGONAK; // Set Global OUT NAK
|
|
while ((OTG->GINTSTS & OTG_FS_GINTSTS_GONAKEFF) == 0U);
|
|
|
|
// Stall OUT Endpoint
|
|
if ((OTG_DOEPCTL(ep_num) & OTG_FS_DOEPCTLx_EPENA) != 0U) {
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_STALL | OTG_FS_DOEPCTLx_EPDIS;
|
|
} else {
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_STALL;
|
|
}
|
|
|
|
OTG->DCTL |= OTG_FS_DCTL_CGONAK; // Clear global NAK
|
|
}
|
|
} else { // Clear STALL
|
|
ptr_ep->in_nak = 0U;
|
|
ptr_ep->in_zlp = 0U;
|
|
if (ep_dir != 0U) { // IN Endpoint
|
|
if ((OTG_DIEPCTL(ep_num) & OTG_FS_DIEPCTLx_EPENA) != 0U) { // If Endpoint enabled
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_EPDIS; // Disable Endpoint
|
|
}
|
|
|
|
// Set DATA0 PID for Interrupt and Bulk Endpoint
|
|
if (((OTG_DIEPCTL(ep_num) & OTG_FS_DIEPCTLx_EPTYP_MSK) >> OTG_FS_DIEPCTLx_EPTYP_POS) > 1U) {
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_SD0PID;
|
|
}
|
|
|
|
OTG_DIEPCTL(ep_num) &= ~OTG_FS_DIEPCTLx_STALL; // Clear Stall
|
|
} else { // Clear OUT Endpoint stall
|
|
// Set DATA0 PID for Interrupt and Bulk Endpoint
|
|
if (((OTG_DOEPCTL(ep_num) & OTG_FS_DOEPCTLx_EPTYP_MSK) >> OTG_FS_DOEPCTLx_EPTYP_POS) > 1U) {
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_SD0PID;
|
|
}
|
|
OTG_DOEPCTL(ep_num) &= ~OTG_FS_DOEPCTLx_STALL; // Clear Stall
|
|
}
|
|
}
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_EndpointTransfer (uint8_t ep_addr, uint8_t *data, uint32_t num)
|
|
\brief Read data from or Write data to USB Endpoint.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
\param[out] data Pointer to buffer for data to read or with data to write
|
|
\param[in] num Number of data bytes to transfer
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_EndpointTransfer (uint8_t ep_addr, uint8_t *data, uint32_t num) {
|
|
volatile ENDPOINT_t *ptr_ep;
|
|
uint8_t ep_num;
|
|
bool ep_dir;
|
|
|
|
ep_num = EP_NUM(ep_addr);
|
|
if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; }
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
ptr_ep = &ep[EP_ID(ep_addr)];
|
|
if (ptr_ep->active != 0U) { return ARM_DRIVER_ERROR_BUSY; }
|
|
|
|
ep_dir = (ep_addr & ARM_USB_ENDPOINT_DIRECTION_MASK) == ARM_USB_ENDPOINT_DIRECTION_MASK;
|
|
|
|
ptr_ep->active = 1U;
|
|
|
|
ptr_ep->data = data;
|
|
ptr_ep->num = num;
|
|
ptr_ep->num_transferred_total = 0U;
|
|
ptr_ep->num_transferring = 0U;
|
|
|
|
if (ep_dir != 0U) { // IN Endpoint
|
|
if (OTG_EP_IN_TYPE(ep_num) != ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
if (num == 0U) {
|
|
ptr_ep->in_zlp = 1U; // Send IN ZLP requested
|
|
}
|
|
ptr_ep->in_nak = 1U; // Set IN Endpoint NAK flag
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_CNAK; // Clear NAK
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_SNAK; // Set NAK
|
|
OTG->DIEPMSK |= OTG_FS_DIEPMSK_INEPNEM; // Enable NAK effective interrupt
|
|
}
|
|
} else { // OUT Endpoint
|
|
if (OTG_EP_OUT_TYPE(ep_num) != ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
USBD_EndpointReadSet(ep_addr);
|
|
}
|
|
}
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn uint32_t USBD_EndpointTransferGetResult (uint8_t ep_addr)
|
|
\brief Get result of USB Endpoint transfer.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
\return number of successfully transferred data bytes
|
|
*/
|
|
static uint32_t USBD_EndpointTransferGetResult (uint8_t ep_addr) {
|
|
|
|
if (EP_NUM(ep_addr) > USBD_MAX_ENDPOINT_NUM) { return 0U; }
|
|
|
|
return (ep[EP_ID(ep_addr)].num_transferred_total);
|
|
}
|
|
|
|
/**
|
|
\fn int32_t USBD_EndpointTransferAbort (uint8_t ep_addr)
|
|
\brief Abort current USB Endpoint transfer.
|
|
\param[in] ep_addr Endpoint Address
|
|
- ep_addr.0..3: Address
|
|
- ep_addr.7: Direction
|
|
\return \ref execution_status
|
|
*/
|
|
static int32_t USBD_EndpointTransferAbort (uint8_t ep_addr) {
|
|
volatile ENDPOINT_t *ptr_ep;
|
|
uint8_t ep_num;
|
|
|
|
ep_num = EP_NUM(ep_addr);
|
|
if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; }
|
|
if (hw_powered == false) { return ARM_DRIVER_ERROR; }
|
|
|
|
ptr_ep = &ep[EP_ID(ep_addr)];
|
|
|
|
ptr_ep->num = 0U;
|
|
ptr_ep->in_nak = 0U;
|
|
ptr_ep->in_zlp = 0U;
|
|
|
|
if ((ep_addr & ARM_USB_ENDPOINT_DIRECTION_MASK) != 0U) {
|
|
if ((OTG_DIEPCTL(ep_num) & OTG_FS_DIEPCTLx_EPENA) != 0U) {
|
|
// Set flush flag to Flush IN FIFO in Endpoint disabled interrupt
|
|
ptr_ep->in_flush = 1U;
|
|
|
|
// Disable enabled Endpoint and set NAK
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_EPDIS | OTG_FS_DIEPCTLx_SNAK;
|
|
} else {
|
|
// Endpoint is already disabled. Set NAK
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_SNAK;
|
|
|
|
// Flush IN EP FIFO
|
|
USBD_FlushInEpFifo (ep_num);
|
|
}
|
|
|
|
} else {
|
|
if ((OTG_DOEPCTL(ep_num) & OTG_FS_DOEPCTLx_EPENA) != 0U) {
|
|
// Disable enabled Endpoint and set NAK
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_EPDIS | OTG_FS_DOEPCTLx_SNAK;
|
|
} else {
|
|
// Endpoint is already disabled. Set NAK
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_SNAK;
|
|
}
|
|
}
|
|
|
|
ptr_ep->active = 0U;
|
|
|
|
return ARM_DRIVER_OK;
|
|
}
|
|
|
|
/**
|
|
\fn uint16_t USBD_GetFrameNumber (void)
|
|
\brief Get current USB Frame Number.
|
|
\return Frame Number
|
|
*/
|
|
static uint16_t USBD_GetFrameNumber (void) {
|
|
|
|
if (hw_powered == false) { return 0U; }
|
|
|
|
return ((OTG->DSTS & OTG_FS_DSTS_FNSOF_MSK) >> OTG_FS_DSTS_FNSOF_POS);
|
|
}
|
|
|
|
/**
|
|
\fn void USBD_FS_IRQ (uint32_t gintsts)
|
|
\brief USB Device Interrupt Routine (IRQ).
|
|
*/
|
|
void USBD_FS_IRQ (uint32_t gintsts) {
|
|
volatile ENDPOINT_t *ptr_ep, *ptr_ep_in;
|
|
uint32_t val, msk, ep_int;
|
|
#if (USBD_FS_VBUS_DETECT == 1)
|
|
uint32_t gotgint;
|
|
#endif
|
|
uint16_t num;
|
|
uint8_t ep_num, i;
|
|
static uint32_t IsoInIncomplete = 0U;
|
|
|
|
if ((gintsts & OTG_FS_GINTSTS_USBRST) != 0U) { // Reset interrupt
|
|
OTG->GINTSTS = OTG_FS_GINTSTS_USBRST;
|
|
OTG->GINTMSK |= OTG_FS_GINTMSK_SOFM; // Unmask SOF interrupts (to detect initial SOF)
|
|
usbd_state.active = 0U;
|
|
USBD_Reset();
|
|
SignalDeviceEvent(ARM_USBD_EVENT_RESET);
|
|
}
|
|
|
|
if ((gintsts & OTG_FS_GINTSTS_USBSUSP) != 0U) { // Suspend interrupt
|
|
OTG->GINTSTS = OTG_FS_GINTSTS_USBSUSP;
|
|
OTG->PCGCCTL |= OTG_FS_PCGCCTL_STPPCLK; // Stop PHY clock
|
|
usbd_state.active = 0U;
|
|
SignalDeviceEvent(ARM_USBD_EVENT_SUSPEND);
|
|
}
|
|
|
|
if ((gintsts & OTG_FS_GINTSTS_WKUPINT) != 0U) { // Resume interrupt
|
|
OTG->GINTSTS = OTG_FS_GINTSTS_WKUPINT;
|
|
OTG->PCGCCTL &= ~OTG_FS_PCGCCTL_STPPCLK; // Start PHY clock
|
|
usbd_state.active = 1U;
|
|
SignalDeviceEvent(ARM_USBD_EVENT_RESUME);
|
|
}
|
|
|
|
if ((gintsts & OTG_FS_GINTSTS_ENUMDNE) != 0U) { // Speed enumeration completed
|
|
OTG->GINTSTS = OTG_FS_GINTSTS_ENUMDNE;
|
|
usbd_state.speed = ARM_USB_SPEED_FULL;
|
|
OTG->DCTL |= OTG_FS_DCTL_CGINAK; // Clear global IN NAK
|
|
OTG->DCTL |= OTG_FS_DCTL_CGONAK; // Clear global OUT NAK
|
|
}
|
|
|
|
if ((usbd_state.active == 0U) &&
|
|
(gintsts & OTG_FS_GINTSTS_SOF) != 0U) { // First SOF after Reset
|
|
OTG->GINTMSK &= ~OTG_FS_GINTMSK_SOFM; // Mask SOF interrupts
|
|
OTG->GINTSTS = OTG_FS_GINTSTS_SOF;
|
|
usbd_state.active = 1U;
|
|
SignalDeviceEvent(ARM_USBD_EVENT_RESUME);
|
|
}
|
|
|
|
if ((gintsts & OTG_FS_GINTSTS_RXFLVL) != 0U) { // Receive FIFO interrupt
|
|
val = OTG->GRXSTSP;
|
|
ep_num = val & 0x0FU;
|
|
num = (val >> 4) & 0x7FFU;
|
|
switch ((val >> 17) & 0x0FU) {
|
|
case 6: // Setup packet
|
|
// Read setup packet
|
|
setup_packet[0] = OTG_RX_FIFO;
|
|
setup_packet[1] = OTG_RX_FIFO;
|
|
|
|
// Analyze Setup packet for SetAddress
|
|
if ((setup_packet[0] & 0xFFFFU) == 0x0500U) {
|
|
USBD_DeviceSetAddress((setup_packet[0] >> 16) & 0xFFU);
|
|
}
|
|
setup_received = 1U;
|
|
break;
|
|
case 2: // OUT packet
|
|
USBD_ReadFromFifo(ep_num, num);
|
|
break;
|
|
case 1: // Global OUT NAK
|
|
case 3: // OUT transfer completed
|
|
case 4: // SETUP transaction completed
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// OUT Packet
|
|
if ((gintsts & OTG_FS_GINTSTS_OEPINT) != 0U) {
|
|
msk = (((OTG->DAINT & OTG->DAINTMSK) >> 16) & 0xFFFFU);
|
|
ep_num = 0U;
|
|
do {
|
|
if (((msk >> ep_num) & 1U) != 0U) {
|
|
ep_int = OTG_DOEPINT(ep_num) & OTG->DOEPMSK;
|
|
ptr_ep = &ep[EP_ID(ep_num)];
|
|
if ((ep_int & OTG_FS_DOEPINTx_EPDISD) != 0U) { // If Endpoint disabled
|
|
if (OTG_EP_OUT_TYPE(ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
// Isochronous OUT Endpoint
|
|
// Set packet count and transfer size
|
|
OTG_DOEPTSIZ(ep_num) = (1U << OTG_FS_DOEPTSIZx_PKTCNT_POS) |
|
|
(ptr_ep->max_packet_size);
|
|
|
|
// Set correct frame
|
|
if ((USBD_GetFrameNumber() & 1U) != 0U) { OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_SEVNFRM; }
|
|
else { OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_SODDFRM; }
|
|
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_EPENA | OTG_FS_DOEPCTLx_CNAK;
|
|
}
|
|
OTG_DOEPINT(ep_num) = OTG_FS_DOEPINTx_EPDISD;
|
|
}
|
|
|
|
// Setup phase done interrupt
|
|
if ((ep_int & OTG_FS_DOEPINTx_STUP) != 0U) {
|
|
ptr_ep->num = 0U;
|
|
OTG_DOEPINT(ep_num) = OTG_FS_DOEPINTx_STUP;
|
|
SignalEndpointEvent(ep_num, ARM_USBD_EVENT_SETUP);
|
|
}
|
|
|
|
// Transfer complete interrupt
|
|
if ((ep_int & OTG_FS_DOEPINTx_XFCR) != 0U) {
|
|
OTG_DOEPINT(ep_num) = OTG_FS_DOEPINTx_XFCR;
|
|
if (ptr_ep->num != 0U) {
|
|
if (OTG_EP_OUT_TYPE(ep_num) != ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
USBD_EndpointReadSet(ep_num);
|
|
}
|
|
} else {
|
|
ptr_ep->active = 0U;
|
|
SignalEndpointEvent(ep_num, ARM_USBD_EVENT_OUT);
|
|
}
|
|
}
|
|
}
|
|
ep_num++;
|
|
} while ((msk >> ep_num) != 0U);
|
|
}
|
|
|
|
// IN Packet
|
|
if ((gintsts & OTG_FS_GINTSTS_IEPINT) != 0U) {
|
|
msk = (OTG->DAINT & OTG->DAINTMSK & 0xFFFFU);
|
|
ep_num = 0U;
|
|
do {
|
|
if (((msk >> ep_num) & 1U) != 0U) {
|
|
ep_int = OTG_DIEPINT(ep_num) & OTG->DIEPMSK;
|
|
ptr_ep = &ep[EP_ID(ep_num | ARM_USB_ENDPOINT_DIRECTION_MASK)];
|
|
|
|
if ((ep_int & OTG_FS_DIEPINTx_EPDISD) != 0U) { // If Endpoint disabled
|
|
OTG_DIEPINT(ep_num) = OTG_FS_DIEPINTx_EPDISD;
|
|
if (ptr_ep->in_flush == 1U) {
|
|
// Clear flush flag
|
|
ptr_ep->in_flush = 0U;
|
|
// Flush IN Endpoint FIFO
|
|
USBD_FlushInEpFifo(ep_num);
|
|
} else if (OTG_EP_IN_TYPE(ep_num) == ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
if ((IsoInIncomplete & (1U << ep_num)) != 0U) {
|
|
// Flush IN Endpoint FIFO and write new data if available
|
|
USBD_FlushInEpFifo(ep_num);
|
|
if (ptr_ep->data != NULL) {
|
|
ptr_ep->num += ptr_ep->num_transferring;
|
|
USBD_WriteToFifo(ep_num | ARM_USB_ENDPOINT_DIRECTION_MASK);
|
|
}
|
|
IsoInIncomplete &= ~(1U << ep_num);
|
|
}
|
|
}
|
|
}
|
|
|
|
// IN Endpoint NAK effective
|
|
if ((ep_int & OTG_FS_DIEPINTx_INEPNE) != 0U) {
|
|
if (ptr_ep->in_nak != 0U) {
|
|
ptr_ep->in_nak = 0U;
|
|
val = 0U;
|
|
ptr_ep_in = &ep[0];
|
|
for (i = 0U; i < USBD_MAX_ENDPOINT_NUM; i++) {
|
|
val |= ptr_ep_in->in_nak;
|
|
ptr_ep_in += 2U;
|
|
}
|
|
|
|
// If no more forced NAKs, disable IN NAK effective interrupt
|
|
if (val == 0U) { OTG->DIEPMSK &= ~OTG_FS_DIEPMSK_INEPNEM; }
|
|
|
|
// If Data available, write Data
|
|
if ((ptr_ep->num != 0U) || (ptr_ep->in_zlp != 0U)) {
|
|
USBD_WriteToFifo(ep_num | ARM_USB_ENDPOINT_DIRECTION_MASK);
|
|
}
|
|
}
|
|
OTG_DIEPINT(ep_num) = OTG_FS_DIEPINTx_INEPNE;
|
|
}
|
|
|
|
// Transmit completed
|
|
if ((ep_int & OTG_FS_DIEPINTx_XFCR) != 0U) {
|
|
OTG_DIEPINT(ep_num) = OTG_FS_DIEPINTx_XFCR;
|
|
ptr_ep->num_transferred_total += ptr_ep->num_transferring;
|
|
if (ptr_ep->num == 0U) {
|
|
ptr_ep->data = NULL;
|
|
ptr_ep->active = 0U;
|
|
SignalEndpointEvent(ep_num | ARM_USB_ENDPOINT_DIRECTION_MASK, ARM_USBD_EVENT_IN);
|
|
} else {
|
|
if (OTG_EP_IN_TYPE(ep_num) != ARM_USB_ENDPOINT_ISOCHRONOUS) {
|
|
USBD_WriteToFifo(ep_num | ARM_USB_ENDPOINT_DIRECTION_MASK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ep_num++;
|
|
} while ((msk >> ep_num != 0U));
|
|
}
|
|
|
|
// End of periodic frame
|
|
if ((gintsts & OTG_FS_GINTSTS_EOPF) != 0U) {
|
|
|
|
// Clear interrupt flags
|
|
OTG->GINTSTS = OTG_FS_GINTSTS_EOPF | OTG_FS_GINTSTS_IPXFR | OTG_FS_GINTSTS_IISOIXFR;
|
|
|
|
// Check enabled isochronous OUT Endpoints
|
|
for (ep_num = 1U; ep_num <= USBD_MAX_ENDPOINT_NUM; ep_num++) {
|
|
if (OTG_EP_OUT_TYPE(ep_num) != ARM_USB_ENDPOINT_ISOCHRONOUS) { continue; }
|
|
if ((OTG_DOEPCTL(ep_num) & OTG_FS_DOEPCTLx_USBAEP) == 0U) { continue; }
|
|
|
|
if ((gintsts & OTG_FS_GINTSTS_IPXFR) != 0U) {
|
|
// Incomplete Isochronous OUT transfer
|
|
if ((USBD_GetFrameNumber() & 1U) == ((OTG_DOEPCTL(ep_num) >> OTG_FS_DOEPCTLx_EONUM_POS) & 1U)) {
|
|
if ((OTG_DOEPCTL(ep_num) & OTG_FS_DOEPCTLx_EPENA) != 0U) {
|
|
OTG_DOEPCTL(ep_num) |= OTG_FS_DOEPCTLx_EPDIS;
|
|
}
|
|
}
|
|
} else {
|
|
// Isochronous OUT transfer completed
|
|
if (ep[EP_ID(ep_num)].num != 0U) {
|
|
USBD_EndpointReadSet(ep_num);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check enabled isochronous IN Endpoints
|
|
for (ep_num = 1U; ep_num <= USBD_MAX_ENDPOINT_NUM; ep_num++) {
|
|
if (OTG_EP_IN_TYPE(ep_num) != ARM_USB_ENDPOINT_ISOCHRONOUS) { continue; }
|
|
if ((OTG_DIEPCTL(ep_num) & OTG_FS_DIEPCTLx_USBAEP) == 0U) { continue; }
|
|
|
|
if ((gintsts & OTG_FS_GINTSTS_IISOIXFR) != 0U) {
|
|
if ((OTG_DIEPCTL(ep_num) & OTG_FS_DIEPCTLx_EPENA) != 0U) {
|
|
if ((USBD_GetFrameNumber() & 1U) == ((OTG_DIEPCTL(ep_num) >> OTG_FS_DIEPCTLx_EONUM_POS) & 1U)) {
|
|
IsoInIncomplete |= (1U << ep_num);
|
|
OTG_DIEPCTL(ep_num) |= OTG_FS_DIEPCTLx_EPDIS | OTG_FS_DIEPCTLx_SNAK;
|
|
}
|
|
}
|
|
} else {
|
|
if (ep[EP_ID(ep_num | ARM_USB_ENDPOINT_DIRECTION_MASK)].num != 0U) {
|
|
USBD_WriteToFifo (ep_num | ARM_USB_ENDPOINT_DIRECTION_MASK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if (USBD_FS_VBUS_DETECT == 1)
|
|
if ((gintsts & OTG_FS_GINTSTS_SRQINT) != 0U) {
|
|
OTG->GINTSTS = OTG_FS_GINTSTS_SRQINT;
|
|
usbd_state.vbus = 1U;
|
|
SignalDeviceEvent(ARM_USBD_EVENT_VBUS_ON);
|
|
}
|
|
|
|
if ((gintsts & OTG_FS_GINTSTS_OTGINT) != 0U) {
|
|
gotgint = OTG->GOTGINT;
|
|
OTG->GOTGINT = gotgint;
|
|
if ((gotgint & OTG_FS_GOTGINT_SEDET) != 0U) {
|
|
usbd_state.vbus = 0U;
|
|
usbd_state.speed = 0U;
|
|
usbd_state.active = 0U;
|
|
SignalDeviceEvent(ARM_USBD_EVENT_VBUS_OFF);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ARM_DRIVER_USBD Driver_USBD0 = {
|
|
USBD_GetVersion,
|
|
USBD_GetCapabilities,
|
|
USBD_Initialize,
|
|
USBD_Uninitialize,
|
|
USBD_PowerControl,
|
|
USBD_DeviceConnect,
|
|
USBD_DeviceDisconnect,
|
|
USBD_DeviceGetState,
|
|
USBD_DeviceRemoteWakeup,
|
|
USBD_DeviceSetAddress,
|
|
USBD_ReadSetupPacket,
|
|
USBD_EndpointConfigure,
|
|
USBD_EndpointUnconfigure,
|
|
USBD_EndpointStall,
|
|
USBD_EndpointTransfer,
|
|
USBD_EndpointTransferGetResult,
|
|
USBD_EndpointTransferAbort,
|
|
USBD_GetFrameNumber
|
|
};
|