efm32: Add usb library. Not linked in yet.

This commit is contained in:
Solomon Peachy 2013-08-04 15:27:39 -04:00
parent c3d05fc576
commit 181c267825
16 changed files with 10505 additions and 0 deletions

View File

@ -0,0 +1,54 @@
================ Revision history ============================================
3.20.0:
- Documentation changes only.
3.0.3:
- Fixed compile time error when using USB stack energy saving on devices with
no endpoints in addition to EP0.
- Fixed bug when repeatedly unplugging/replugging a device USB cable and using
energy saving modes.
3.0.2:
- Bugfix: USBD_Init() may now be called when USB peripheral is in partial
powerdown mode.
- New function USBD_EpIsBusy( int epAddr ) added to API.
- Changed USBD_AbortTransfer() from USBD_AbortTransfer( uint8_t ) to
USBD_AbortTransfer( int ).
- Added configuration of which hw TIMER the stack will use.
3.0.1:
- USBD_Stop() now disables all USB related interrupts correctly.
- Minor documentation changes.
- Added more flexibility for taking advantage of energymodes in USB applications.
- Fixed bug in device stack partial powerdown procedure.
3.0.0:
- Updated to CMSIS_V3, "efm32lib" is now called "emlib"
- This library has been renamed from "efm32usb" to "usb"
- Prefixes changed from efm32_ to em_, to reflect changes in emlib
- Updated software license, see top of every file for details
2.4.1:
- Corrected USBC clock enable for header file updates
2.4.0:
- Added raw transfer API in the host USB stack for internal testing purposes.
- Added USB energy-saving modes in device protocol stack.
2.3.2:
- 19/12-2011: MSD updates
2.3.0:
- 29/11-2011: Initial version of host stack.
- 11/11-2011: Added Doxygen doc for host stack.
2.2.2:
- 18/10-2011: Fixed bug in USBDHAL_AbortEpOut() which in some scenarios would hang.
- 18/10-2011: Added function USBD_Stop(), which disables device stack operation.
2.2.1:
- 23/03-2011: Initial version.
- 15/04-2011: Added host device stack.
- 24/05-2011: First release candidate.
- 06/06-2011: Host driver port plug/unplug code improvements.
- 18/08-2011: Redesign of device abort transfer function.

63
libs/emusb/ReadMe_usb.txt Normal file
View File

@ -0,0 +1,63 @@
================ Energy Micro USB STACK =====================================
This directory, "usb", contains the Energy Micro USB stack for the EFM32
Giant Gecko and Leopard Gecko series of microcontrollers.
Some design guidelines for this library is:
* Follow the guidelines established by ARM's and Energy Micro's adaptation
of the CMSIS (see below) standard
* Be usable as a starting point for developing richer, more target specific
functionality (i.e. copy and modify further)
* Ability to be used as a standalone software component, used by other drivers
that should cover "the most common cases"
* Readability of the code and usability preferred before optimization for speed
and size or covering a particular "narrow" purpose
* As little "cross-dependency" between modules as possible, to enable users to
pick and choose what they want
================ About CMSIS ================================================
The USB APIs are built on top of EFM32 CMSIS headers and em_lib API'S, which
again are based on the Cortex Microcontroller Software Interface Standard.
These files are supplied together with this archive in separate folders.
The library requires basic C99-support. You might have to enable C99 support
in your compiler. Comments are in doxygen compatible format, and can be viewed
online from Energy Micro's web site.
To download updates of this EFM32 CMSIS release, go to
http://www.energymicro.com/downloads
For more information about CMSIS see
http://www.onarm.com
================ File structure ==============================================
inc/ - header files
src/ - source files
================ License =====================================================
See the top of each file for SW license. Basically you are free to use the
Energy Micro code for any project using Energy Micro devices. Parts of the
CMSIS library is copyrighted by ARM Inc. See "License.doc" for ARM's CMSIS
license.
================ Software updates ============================================
Energy Micro continually works to provide updated and improved emlib, usb
stack, example code and other software of use for Energy Micro customers.
Please check the download section of Energy Micro's web site at
http://www.energymicro.com/downloads
for the latest releases, news and updates. If you download and install the
Simplicity Studio application, you will be notified about updates when
available.
(C) Copyright Energy Micro AS, 2012

932
libs/emusb/inc/em_usb.h Normal file
View File

@ -0,0 +1,932 @@
/***************************************************************************//**
* @file
* @brief USB protocol stack library API for EFM32.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#ifndef __EM_USB_H
#define __EM_USB_H
#include "em_device.h"
#include "em_assert.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "usbconfig.h"
#if defined( USB_DEVICE ) || defined( USB_HOST )
#include <string.h>
#include <stddef.h>
#include "em_common.h"
#include "em_int.h"
#if defined( USB_USE_PRINTF )
#include <stdio.h>
#include <stdlib.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __CC_ARM
#pragma anon_unions
#endif
/***************************************************************************//**
* @addtogroup USB
* @brief USB HOST and DEVICE protocol stacks.
* @{
******************************************************************************/
/***************************************************************************//**
* @addtogroup USB_COMMON
* @brief Common parts for both HOST and DEVICE USB stacks, see @ref usb_device
* and @ref usb_host pages for device and host library documentation.
* @{
******************************************************************************/
/* SETUP request, direction of data stage */
#define USB_SETUP_DIR_OUT 0 /**< Setup request data stage OUT direction value. */
#define USB_SETUP_DIR_IN 1 /**< Setup request data stage IN direction value. */
#define USB_SETUP_DIR_MASK 0x80 /**< Setup request data stage direction mask. */
#define USB_SETUP_DIR_D2H 0x80 /**< Setup request data stage IN direction mask. */
#define USB_SETUP_DIR_H2D 0x00 /**< Setup request data stage OUT direction mask. */
/* SETUP request type */
#define USB_SETUP_TYPE_STANDARD 0 /**< Standard setup request value. */
#define USB_SETUP_TYPE_CLASS 1 /**< Class setup request value. */
#define USB_SETUP_TYPE_VENDOR 2 /**< Vendor setup request value. */
#define USB_SETUP_TYPE_STANDARD_MASK 0x00 /**< Standard setup request mask. */
#define USB_SETUP_TYPE_CLASS_MASK 0x20 /**< Class setup request mask. */
#define USB_SETUP_TYPE_VENDOR_MASK 0x40 /**< Vendor setup request mask. */
/* SETUP request recipient */
#define USB_SETUP_RECIPIENT_DEVICE 0 /**< Setup request device recipient value. */
#define USB_SETUP_RECIPIENT_INTERFACE 1 /**< Setup request interface recipient value. */
#define USB_SETUP_RECIPIENT_ENDPOINT 2 /**< Setup request endpoint recipient value. */
#define USB_SETUP_RECIPIENT_OTHER 3 /**< Setup request other recipient value. */
/* SETUP standard request codes for Full Speed devices */
#define GET_STATUS 0 /**< Standard setup request GET_STATUS. */
#define CLEAR_FEATURE 1 /**< Standard setup request CLEAR_FEATURE. */
#define SET_FEATURE 3 /**< Standard setup request SET_FEATURE. */
#define SET_ADDRESS 5 /**< Standard setup request SET_ADDRESS. */
#define GET_DESCRIPTOR 6 /**< Standard setup request GET_DESCRIPTOR. */
#define SET_DESCRIPTOR 7 /**< Standard setup request SET_DESCRIPTOR. */
#define GET_CONFIGURATION 8 /**< Standard setup request GET_CONFIGURATION. */
#define SET_CONFIGURATION 9 /**< Standard setup request SET_CONFIGURATION. */
#define GET_INTERFACE 10 /**< Standard setup request GET_INTERFACE. */
#define SET_INTERFACE 11 /**< Standard setup request SET_INTERFACE. */
#define SYNCH_FRAME 12 /**< Standard setup request SYNCH_FRAME. */
/* SETUP class request codes */
#define USB_HID_GET_REPORT 0x01 /**< HID class setup request GET_REPORT. */
#define USB_HID_GET_IDLE 0x02 /**< HID class setup request GET_IDLE. */
#define USB_HID_SET_REPORT 0x09 /**< HID class setup request SET_REPORT. */
#define USB_HID_SET_IDLE 0x0A /**< HID class setup request SET_IDLE. */
#define USB_HID_SET_PROTOCOL 0x0B /**< HID class setup request SET_PROTOCOL. */
#define USB_CDC_SETLINECODING 0x20 /**< CDC class setup request SET_LINE_CODING. */
#define USB_CDC_GETLINECODING 0x21 /**< CDC class setup request GET_LINE_CODING. */
#define USB_CDC_SETCTRLLINESTATE 0x22 /**< CDC class setup request SET_CONTROL_LINE_STATE. */
#define USB_MSD_BOTRESET 0xFF /**< MSD class setup request Bulk only transfer reset. */
#define USB_MSD_GETMAXLUN 0xFE /**< MSD class setup request Get Max LUN. */
/* SETUP command GET/SET_DESCRIPTOR decriptor types */
#define USB_DEVICE_DESCRIPTOR 1 /**< DEVICE descriptor value. */
#define USB_CONFIG_DESCRIPTOR 2 /**< CONFIGURATION descriptor value. */
#define USB_STRING_DESCRIPTOR 3 /**< STRING descriptor value. */
#define USB_INTERFACE_DESCRIPTOR 4 /**< INTERFACE descriptor value. */
#define USB_ENDPOINT_DESCRIPTOR 5 /**< ENDPOINT descriptor value. */
#define USB_DEVICE_QUALIFIER_DESCRIPTOR 6 /**< DEVICE_QUALIFIER descriptor value. */
#define USB_OTHER_SPEED_CONFIG_DESCRIPTOR 7 /**< OTHER_SPEED_CONFIGURATION descriptor value. */
#define USB_INTERFACE_POWER_DESCRIPTOR 8 /**< INTERFACE_POWER descriptor value. */
#define USB_HUB_DESCRIPTOR 0x29 /**< HUB descriptor value. */
#define USB_HID_DESCRIPTOR 0x21 /**< HID descriptor value. */
#define USB_HID_REPORT_DESCRIPTOR 0x22 /**< HID REPORT descriptor value. */
#define USB_CS_INTERFACE_DESCRIPTOR 0x24 /**< Audio Class-specific Descriptor Type. */
#define USB_DEVICE_DESCSIZE 18 /**< Device descriptor size. */
#define USB_CONFIG_DESCSIZE 9 /**< Configuration descriptor size. */
#define USB_INTERFACE_DESCSIZE 9 /**< Interface descriptor size. */
#define USB_ENDPOINT_DESCSIZE 7 /**< Endpoint descriptor size. */
#define USB_DEVICE_QUALIFIER_DESCSIZE 10 /**< Device qualifier descriptor size. */
#define USB_OTHER_SPEED_CONFIG_DESCSIZE 9 /**< Device other speed configuration descriptor size. */
#define USB_HID_DESCSIZE 9 /**< HID descriptor size. */
#define USB_CDC_HEADER_FND_DESCSIZE 5 /**< CDC Header functional descriptor size. */
#define USB_CDC_CALLMNG_FND_DESCSIZE 5 /**< CDC Call Management functional descriptor size. */
#define USB_CDC_ACM_FND_DESCSIZE 4 /**< CDC Abstract Control Management functional descriptor size.*/
/* Misc. USB definitions */
#define USB_EP0_SIZE 64 /**< The size of endpoint 0. */
#define USB_MAX_EP_SIZE 64 /**< The max size of any full speed endpoint. */
#define USB_EPTYPE_CTRL 0 /**< Endpoint type control. */
#define USB_EPTYPE_ISOC 1 /**< Endpoint type isochron. */
#define USB_EPTYPE_BULK 2 /**< Endpoint type bulk. */
#define USB_EPTYPE_INTR 3 /**< Endpoint type interrupt. */
#define USB_EP_DIR_IN 0x80 /**< Endpoint direction mask. */
#define USB_SETUP_PKT_SIZE 8 /**< Setup request packet size. */
#define USB_EPNUM_MASK 0x0F /**< Endpoint number mask. */
#define USB_LANGID_ENUS 0x0409 /**< English-United States language id. */
#define USB_MAX_DEVICE_ADDRESS 127 /**< Maximum allowable device address. */
#define CONFIG_DESC_BM_REMOTEWAKEUP 0x20 /**< Configuration descriptor attribute macro. */
#define CONFIG_DESC_BM_SELFPOWERED 0x40 /**< Configuration descriptor attribute macro. */
#define CONFIG_DESC_BM_RESERVED_D7 0x80 /**< Configuration descriptor attribute macro. */
#define CONFIG_DESC_BM_TRANSFERTYPE 0x03 /**< Configuration descriptor transfer type bitmask. */
#define CONFIG_DESC_MAXPOWER_mA(x) (((x)+1)/2) /**< Configuration descriptor power macro. */
#define DEVICE_IS_SELFPOWERED 0x0001 /**< Standard request GET_STATUS bitmask. */
#define REMOTE_WAKEUP_ENABLED 0x0002 /**< Standard request GET_STATUS bitmask. */
#define USB_FEATURE_ENDPOINT_HALT 0 /**< Standard request CLEAR/SET_FEATURE bitmask. */
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP 1 /**< Standard request CLEAR/SET_FEATURE bitmask. */
#define HUB_FEATURE_PORT_RESET 4 /**< HUB class request CLEAR/SET_PORT_FEATURE feature selector. */
#define HUB_FEATURE_PORT_POWER 8 /**< HUB class request CLEAR/SET_PORT_FEATURE feature selector. */
#define HUB_FEATURE_C_PORT_CONNECTION 16 /**< HUB class request CLEAR/SET_PORT_FEATURE feature selector. */
#define HUB_FEATURE_C_PORT_RESET 20 /**< HUB class request CLEAR/SET_PORT_FEATURE feature selector. */
#define HUB_FEATURE_PORT_INDICATOR 22 /**< HUB class request CLEAR/SET_PORT_FEATURE feature selector. */
#define USB_CLASS_CDC 2 /**< CDC device/interface class code. */
#define USB_CLASS_CDC_DATA 0x0A /**< CDC Data interface class code. */
#define USB_CLASS_CDC_ACM 2 /**< CDC Abstract Control Model interface subclass code. */
#define USB_CLASS_CDC_HFN 0 /**< CDC class Header Functional Descriptor subtype. */
#define USB_CLASS_CDC_CMNGFN 1 /**< CDC class Call Management Functional Descriptor subtype.*/
#define USB_CLASS_CDC_ACMFN 2 /**< CDC class Abstract Control Management Functional Descriptor subtype.*/
#define USB_CLASS_CDC_UNIONFN 6 /**< CDC class Union Functional Descriptor subtype. */
#define USB_CLASS_HID 3 /**< HID device/interface class code. */
#define USB_CLASS_HID_KEYBOARD 1 /**< HID keyboard interface protocol code. */
#define USB_CLASS_HID_MOUSE 2 /**< HID mouse interface protocol code. */
#define USB_CLASS_HUB 9 /**< HUB device/interface class code. */
#define USB_CLASS_MSD 8 /**< MSD device/interface class code. */
#define USB_CLASS_MSD_BOT_TRANSPORT 0x50 /**< MSD Bulk Only Transport protocol. */
#define USB_CLASS_MSD_SCSI_CMDSET 6 /**< MSD Subclass SCSI transparent command set. */
#define USB_CLASS_MSD_CSW_CMDPASSED 0 /**< MSD BOT Command status wrapper command passed code. */
#define USB_CLASS_MSD_CSW_CMDFAILED 1 /**< MSD BOT Command status wrapper command failed code. */
#define USB_CLASS_MSD_CSW_PHASEERROR 2 /**< MSD BOT Command status wrapper cmd phase error code.*/
#define PORT_FULL_SPEED 1 /**< Full speed return value for USBH_GetPortSpeed(). */
#define PORT_LOW_SPEED 2 /**< Low speed return value for USBH_GetPortSpeed(). */
/** Macro for creating USB compliant UTF-16LE UNICODE string descriptors.
* @n Example: STATIC_CONST_STRING_DESC( iManufacturer, L"Energy Micro AS" );
*/
#define STATIC_CONST_STRING_DESC( name, value ) \
typedef struct \
{ \
uint8_t len; \
uint8_t type; \
wchar_t name[sizeof( value )/2]; \
} __attribute__ ((packed)) _##name; \
EFM32_ALIGN( 4 ) \
EFM32_PACK_START( 1 ) \
static const _##name name __attribute__ ((aligned(4)))= \
{ \
.len = sizeof(value), \
.type = USB_STRING_DESCRIPTOR, \
.name = value \
} \
EFM32_PACK_END()
/** Macro for creating USB compliant language string descriptors.
* @n Example: STATIC_CONST_STRING_DESC_LANGID( langID, 0x04, 0x09 );
*/
#define STATIC_CONST_STRING_DESC_LANGID( name, x, y )\
typedef struct \
{ \
uint8_t len; \
uint8_t type; \
uint8_t name[ 2 ]; \
} __attribute__ ((packed)) _##name; \
EFM32_ALIGN( 4 ) \
EFM32_PACK_START( 1 ) \
static const _##name name __attribute__ ((aligned(4)))= \
{ \
.len = 4, \
.type = USB_STRING_DESCRIPTOR, \
.name = { y, x } \
} \
EFM32_PACK_END()
/** Macro for creating WORD (4 byte) aligned uint8_t array with size which
* is a multiple of WORD size.
* @n Example: @n UBUF( rxBuffer, 37 ); => uint8_t rxBuffer[ 40 ];
*/
#if !defined(__GNUC__)
#define UBUF( x, y ) EFM32_ALIGN( 4 ) uint8_t x[((y)+3)&~3]
#define STATIC_UBUF( x, y ) EFM32_ALIGN( 4 ) static uint8_t x[((y)+3)&~3]
#else
#define UBUF( x, y ) uint8_t x[((y)+3)&~3] __attribute__ ((aligned(4)))
/** Macro for creating WORD (4 byte) aligned static uint8_t arrays with size which
* is a multiple of WORD size.
* @n Example: @n STATIC_UBUF( rxBuffer, 37 ); => static uint8_t rxBuffer[ 40 ];
*/
#define STATIC_UBUF( x, y ) static uint8_t x[((y)+3)&~3] __attribute__ ((aligned(4)))
#endif
/** @brief USB transfer status enumerator. */
typedef enum
{
/* NOTE: Please keep in sync with table errMsg[] in em_usbhal.c */
USB_STATUS_OK = 0, /**< No errors detected. */
USB_STATUS_REQ_ERR = -1, /**< Setup request error. */
USB_STATUS_EP_BUSY = -2, /**< Endpoint is busy. */
USB_STATUS_REQ_UNHANDLED = -3, /**< Setup request not handled. */
USB_STATUS_ILLEGAL = -4, /**< Illegal operation attempted. */
USB_STATUS_EP_STALLED = -5, /**< Endpoint is stalled. */
USB_STATUS_EP_ABORTED = -6, /**< Endpoint transfer was aborted. */
USB_STATUS_EP_ERROR = -7, /**< Endpoint transfer error. */
USB_STATUS_EP_NAK = -8, /**< Endpoint NAK'ed transfer request. */
USB_STATUS_DEVICE_UNCONFIGURED = -9, /**< Device is unconfigured. */
USB_STATUS_DEVICE_SUSPENDED = -10, /**< Device is suspended. */
USB_STATUS_DEVICE_RESET = -11, /**< Device is/was reset. */
USB_STATUS_TIMEOUT = -12, /**< Transfer timeout. */
USB_STATUS_DEVICE_REMOVED = -13, /**< Device was removed. */
USB_STATUS_HC_BUSY = -14, /**< Host channel is busy. */
USB_STATUS_DEVICE_MALFUNCTION = -15, /**< Malfunctioning device attached. */
USB_STATUS_PORT_OVERCURRENT = -16, /**< VBUS shortcircuit/overcurrent failure. */
} USB_Status_TypeDef;
/** @} (end addtogroup USB_COMMON) */
#if defined( USB_DEVICE )
/***************************************************************************//**
* @addtogroup USB_DEVICE
* @brief USB DEVICE protocol stack, see @ref usb_device page for detailed documentation.
* @{
******************************************************************************/
#define USB_PWRSAVE_MODE_OFF 0 /**< No energy saving mode selected. */
#define USB_PWRSAVE_MODE_ONSUSPEND 1 /**< Enter USB power-save mode on suspend. */
#define USB_PWRSAVE_MODE_ONVBUSOFF 2 /**< Enter USB power-save mode when not attached to host. */
#define USB_PWRSAVE_MODE_ENTEREM2 4 /**< Enter EM2 while in power-save mode. */
#define USB_USBC_32kHz_CLK_LFXO 0 /**< Use 32kHz LFXO clock while in powersave mode. */
#define USB_USBC_32kHz_CLK_LFRCO 1 /**< Use 32kHz LFRCO clock while in powersave mode. */
/** @brief USB device state enumerator. */
typedef enum
{
USBD_STATE_NONE = 0, /**< Device state is undefined/unknown. */
USBD_STATE_ATTACHED = 1, /**< Device state is ATTACHED. */
USBD_STATE_POWERED = 2, /**< Device state is POWERED. */
USBD_STATE_DEFAULT = 3, /**< Device state is DEFAULT. */
USBD_STATE_ADDRESSED = 4, /**< Device state is ADDRESSED. */
USBD_STATE_CONFIGURED = 5, /**< Device state is CONFIGURED. */
USBD_STATE_SUSPENDED = 6, /**< Device state is SUSPENDED. */
USBD_STATE_LASTMARKER = 7, /**< Device state enum end marker. */
} USBD_State_TypeDef;
/** @} (end addtogroup USB_DEVICE) */
#endif /* defined( USB_DEVICE ) */
/** @addtogroup USB_COMMON
* @{*/
/** @brief USB Setup request package. */
EFM32_PACK_START( 1 )
typedef struct
{
union
{
struct
{
union
{
struct
{
uint8_t Recipient : 5; /**< Request recipient (device, interface, endpoint or other).*/
uint8_t Type : 2; /**< Request type (standard, class or vendor). */
uint8_t Direction : 1; /**< Transfer direction of SETUP data phase. */
};
uint8_t bmRequestType; /**< Request characteristics. */
};
uint8_t bRequest; /**< Request code. */
uint16_t wValue; /**< Varies according to request. */
uint16_t wIndex; /**< Index or offset, varies according to request. */
uint16_t wLength; /**< Number of bytes to transfer if there is a data stage.*/
};
uint32_t dw[2];
};
} __attribute__ ((packed)) USB_Setup_TypeDef;
EFM32_PACK_END()
/** @brief USB Device Descriptor. */
EFM32_PACK_START( 1 )
typedef struct
{
uint8_t bLength; /**< Size of this descriptor in bytes */
uint8_t bDescriptorType; /**< Constant DEVICE Descriptor Type */
uint16_t bcdUSB; /**< USB Specification Release Number in Binary-Coded
Decimal */
uint8_t bDeviceClass; /**< Class code (assigned by the USB-IF) */
uint8_t bDeviceSubClass; /**< Subclass code (assigned by the USB-IF) */
uint8_t bDeviceProtocol; /**< Protocol code (assigned by the USB-IF) */
uint8_t bMaxPacketSize0; /**< Maximum packet size for endpoint zero */
uint16_t idVendor; /**< Vendor ID (assigned by the USB-IF) */
uint16_t idProduct; /**< Product ID (assigned by the manufacturer) */
uint16_t bcdDevice; /**< Device release number in binary-coded decimal */
uint8_t iManufacturer; /**< Index of string descriptor describing manufacturer*/
uint8_t iProduct; /**< Index of string descriptor describing product */
uint8_t iSerialNumber; /**< Index of string descriptor describing the device
serialnumber */
uint8_t bNumConfigurations; /**< Number of possible configurations */
} __attribute__ ((packed)) USB_DeviceDescriptor_TypeDef;
EFM32_PACK_END()
/** @brief USB Configuration Descriptor. */
EFM32_PACK_START( 1 )
typedef struct
{
uint8_t bLength; /**< Size of this descriptor in bytes */
uint8_t bDescriptorType; /**< Constant CONFIGURATION Descriptor Type */
uint16_t wTotalLength; /**< Total length of data returned for this
configuration. Includes the combined length of all
descriptors (configuration, interface, endpoint,
and class- or vendor-specific) returned for this
configuration. */
uint8_t bNumInterfaces; /**< Number of interfaces supported by this
configuration */
uint8_t bConfigurationValue; /**< Value to use as an argument to the
SetConfiguration request to select this
configuration. */
uint8_t iConfiguration; /**< Index of string descriptor describing this
configuration. */
uint8_t bmAttributes; /**< Configuration characteristics.
@n D7: Reserved (set to one)
@n D6: Self-powered
@n D5: Remote Wakeup
@n D4...0: Reserved (reset to zero) */
uint8_t bMaxPower; /**< Maximum power consumption of the USB device, unit
is 2mA per LSB */
} __attribute__ ((packed)) USB_ConfigurationDescriptor_TypeDef;
EFM32_PACK_END()
/** @brief USB Interface Descriptor. */
EFM32_PACK_START( 1 )
typedef struct
{
uint8_t bLength; /**< Size of this descriptor in bytes. */
uint8_t bDescriptorType; /**< Constant INTERFACE Descriptor Type. */
uint8_t bInterfaceNumber; /**< Number of this interface. Zero-based value
identifying the index in the array of concurrent
interfaces supported by this configuration. */
uint8_t bAlternateSetting; /**< Value used to select this alternate setting for
the interface identified in the prior field. */
uint8_t bNumEndpoints; /**< Number of endpoints used by this interface
(excluding endpoint zero). If this value is zero,
this interface only uses the Default Control Pipe.*/
uint8_t bInterfaceClass; /**< Class code (assigned by the USB-IF). A value
of zero is reserved for future standardization. If
this field is set to FFH, the interface class is
vendor-specific. All other values are reserved for
assignment by the USB-IF. */
uint8_t bInterfaceSubClass; /**< Subclass code (assigned by the USB-IF). These codes
are qualified by the value of the bInterfaceClass
field. If the bInterfaceClass field is reset to
zero, this field must also be reset to zero. If
the bInterfaceClass field is not set to FFH, all
values are reserved forassignment by the USB-IF. */
uint8_t bInterfaceProtocol; /**< Protocol code (assigned by the USB). These codes
are qualified by the value of the bInterfaceClass
and the bInterfaceSubClass fields. If an interface
supports class-specific requests, this code
identifies the protocols that the device uses as
defined by the specification of the device class.
If this field is reset to zero, the device does
not use a class-specific protocol on this
interface. If this field is set to FFH, the device
uses a vendor-specific protocol for this interface*/
uint8_t iInterface; /**< Index of string descriptor describing this
interface. */
} __attribute__ ((packed)) USB_InterfaceDescriptor_TypeDef;
EFM32_PACK_END()
/** @brief USB Endpoint Descriptor. */
EFM32_PACK_START( 1 )
typedef struct
{
uint8_t bLength; /**< Size of this descriptor in bytes */
uint8_t bDescriptorType; /**< Constant ENDPOINT Descriptor Type */
uint8_t bEndpointAddress; /**< The address of the endpoint */
uint8_t bmAttributes; /**< This field describes the endpoint attributes */
uint16_t wMaxPacketSize; /**< Maximum packet size for the endpoint */
uint8_t bInterval; /**< Interval for polling EP for data transfers */
} __attribute__ ((packed)) USB_EndpointDescriptor_TypeDef;
EFM32_PACK_END()
/** @brief USB String Descriptor. */
EFM32_PACK_START( 1 )
typedef struct
{
uint8_t len; /**< Size of this descriptor in bytes. */
uint8_t type; /**< Constant STRING Descriptor Type. */
wchar_t name[]; /**< The string encoded with UTF-16LE UNICODE charset. */
} __attribute__ ((packed)) USB_StringDescriptor_TypeDef;
EFM32_PACK_END()
/** @} (end addtogroup USB_COMMON) */
/*** -------------------- Serial port debug configuration ---------------- ***/
#if defined( DOXY_DOC_ONLY )
/** @addtogroup USB_COMMON
* @{*/
/***************************************************************************//**
* @brief
* Transmit a single char on the debug serial port.
*
* @note
* This function is enabled with \#define DEBUG_USB_API when configuring the
* protocol stack in "usbconfig.h".
* This is convenient when debugging code, no need to remove use of this
* function when debugging has completed.
*
* @param[in] c
* Char to transmit.
*
* @return
* The char transmitted.
******************************************************************************/
int USB_PUTCHAR( char c );
/***************************************************************************//**
* @brief
* Transmit a zero terminated string on the debug serial port.
*
* @note
* This function is enabled with \#define DEBUG_USB_API when configuring the
* protocol stack in "usbconfig.h".
* This is convenient when debugging code, no need to remove use of this
* function when debugging has completed.
*
* @param[in] p
* Pointer to string to transmit.
******************************************************************************/
void USB_PUTS( const char *p );
/***************************************************************************//**
* @brief
* Transmit "printf" formated data on the debug serial port.
*
* @note
* This function is enabled with \#define USB_USE_PRINTF when configuring the
* protocol stack in "usbconfig.h".
* This is convenient when debugging code, no need to remove use of this
* function when debugging has completed.
*
* @param[in] format
* Format string (as in printf). No floating point format support.
******************************************************************************/
int USB_PRINTF( const char *format, ... );
/** @} (end addtogroup USB_COMMON) */
#endif /* defined( DOXY_DOC_ONLY ) */
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/* Hardware constraint, do not change. */
#define MAX_NUM_HOSTCHANNELS 14
#if !defined( USB_SLAVEMODE )
/* In DMA mode the DMA engine use one FIFO ram word for each host channel. */
#define MAX_FIFO_SIZE_INWORDS (512-MAX_NUM_HOSTCHANNELS) /* Unit is 4 bytes */
#else
#define MAX_FIFO_SIZE_INWORDS 512 /* Unit is 4 bytes */
#endif
#if defined ( USER_PUTCHAR )
void USB_Puts( const char *p );
#define USB_PUTS( s ) USB_Puts( s )
#define USB_PUTCHAR( c ) USER_PUTCHAR( c )
#else
#define USB_PUTS( s )
#define USB_PUTCHAR( c )
#endif
#if defined( USB_USE_PRINTF )
/* Use a printf which don't support floating point formatting */
#if defined(__ICCARM__) || defined (__CC_ARM) || defined (__CROSSWORKS_ARM)
#define USB_PRINTF printf
#else
#define USB_PRINTF iprintf
#endif
#else
#define USB_PRINTF(...)
#endif /* defined( USB_USE_PRINTF ) */
#if defined( DEBUG_USB_API )
#define DEBUG_USB_API_PUTS( s ) USB_PUTS( s )
#define DEBUG_USB_API_PUTCHAR( c ) USB_PUTCHAR( c )
#else
#define DEBUG_USB_API_PUTS( s )
#define DEBUG_USB_API_PUTCHAR( c )
#endif /* defined( DEBUG_USB_API ) */
/** @endcond */
/*** -------------------- Common API definitions ------------------------- ***/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#if defined( USB_HOST )
#if defined( NUM_APP_TIMERS )
#define NUM_QTIMERS ( NUM_HC_USED + 2 + NUM_APP_TIMERS + 1 )
#else
#define NUM_QTIMERS ( NUM_HC_USED + 2 + 1 )
#endif
/* + 2 for default ctrl. host ch. 0 & 1, + 1 for host port timer */
#else
#if defined( NUM_APP_TIMERS )
#define NUM_QTIMERS ( NUM_APP_TIMERS )
#else
#define NUM_QTIMERS 0
#endif
#endif /* defined( USB_HOST ) */
/** @endcond */
/** @addtogroup USB_COMMON
* @{*/
/***************************************************************************//**
* @brief
* USB transfer callback function.
*
* @details
* The callback function is called when a transfer has completed. An application
* should check the status, xferred and optionally the remaining parameters
* before deciding if the transfer is usable. In the case where the transfer
* is part of a control request data stage, the callback function should
* return an appropriate @ref USB_Status_TypeDef status.
*
* @param[in] status
* The transfer status. See @ref USB_Status_TypeDef.
*
* @param[in] xferred
* Number of bytes actually transferred.
*
* @param[in] remaining
* Number of bytes not transferred.
*
* @return
* @ref USB_STATUS_OK on success, else an appropriate error code.
******************************************************************************/
typedef int (*USB_XferCompleteCb_TypeDef)( USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining );
/***************************************************************************//**
* @brief
* USBTIMER callback function.
*
* @details
* The callback function is called when an USBTIMER has expired. The callback
* is done with interrupts disabled.
******************************************************************************/
typedef void (*USBTIMER_Callback_TypeDef)( void );
char *USB_GetErrorMsgString( int error );
#if defined( USB_USE_PRINTF )
void USB_PrintErrorMsgString( char *pre, int error );
#else
#define USB_PrintErrorMsgString( pre, error )
#endif
void USBTIMER_DelayMs( uint32_t msec );
void USBTIMER_DelayUs( uint32_t usec );
void USBTIMER_Init( void );
#if ( NUM_QTIMERS > 0 )
void USBTIMER_Start( uint32_t id, uint32_t timeout, USBTIMER_Callback_TypeDef callback );
void USBTIMER_Stop( uint32_t id );
#endif /* ( NUM_QTIMERS > 0 ) */
/** @} (end addtogroup USB_COMMON) */
#if defined( USB_DEVICE )
/** @addtogroup USB_DEVICE
* @{*/
/*** -------------------- DEVICE mode API definitions -------------------- ***/
/***************************************************************************//**
* @brief
* USB Reset callback function.
* @details
* Called whenever USB reset signalling is detected on the USB port.
******************************************************************************/
typedef void (*USBD_UsbResetCb_TypeDef)( void );
/***************************************************************************//**
* @brief
* USB Start Of Frame (SOF) interrupt callback function.
*
* @details
* Called at each SOF interrupt (if enabled),
*
* @param[in] sofNr
* Current frame number. The value rolls over to 0 after 16383 (0x3FFF).
******************************************************************************/
typedef void (*USBD_SofIntCb_TypeDef)( uint16_t sofNr );
/***************************************************************************//**
* @brief
* USB State change callback function.
*
* @details
* Called whenever the device change state.
*
* @param[in] oldState
* The device USB state just leaved. See @ref USBD_State_TypeDef.
*
* @param[in] newState
* New (the current) USB device state. See @ref USBD_State_TypeDef.
******************************************************************************/
typedef void (*USBD_DeviceStateChangeCb_TypeDef)( USBD_State_TypeDef oldState, USBD_State_TypeDef newState );
/***************************************************************************//**
* @brief
* USB power mode callback function.
*
* @details
* Called whenever the device stack needs to query if the device is currently
* self- or bus-powered. Typically when host has issued an @ref GET_STATUS
* setup command.
*
* @return
* True if self-powered, false otherwise.
******************************************************************************/
typedef bool (*USBD_IsSelfPoweredCb_TypeDef)( void );
/***************************************************************************//**
* @brief
* USB setup request callback function.
*
* @details
* Called on each setup request received from host. This gives the application a
* possibility to extend or override standard requests, and to implement class
* or vendor specific requests. Return @ref USB_STATUS_OK if the request is
* handled, return @ref USB_STATUS_REQ_ERR if it is an illegal request or
* return @ref USB_STATUS_REQ_UNHANDLED to pass the request on to the default
* request handler.
*
* @param[in] setup
* Pointer to an USB setup packet. See @ref USB_Setup_TypeDef.
*
* @return
* An appropriate status/error code. See @ref USB_Status_TypeDef.
******************************************************************************/
typedef int (*USBD_SetupCmdCb_TypeDef)( const USB_Setup_TypeDef *setup );
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
struct USBD_Callbacks_TypeDef;
typedef struct USBD_Callbacks_TypeDef const *USBD_Callbacks_TypeDef_Pointer;
/** @endcond */
/** @brief USB Device stack initialization structure.
* @details This structure is passed to @ref USBD_Init() when starting up
* the device. */
typedef struct
{
const USB_DeviceDescriptor_TypeDef *deviceDescriptor; /**< Pointer to a device descriptor. */
const uint8_t *configDescriptor; /**< Pointer to a configuration descriptor. */
const void * const *stringDescriptors; /**< Pointer to an array of string descriptor pointers.*/
const uint8_t numberOfStrings; /**< Number of strings in string descriptor array. */
const uint8_t *bufferingMultiplier; /**< Pointer to an array defining the size of the
endpoint buffers. The size is given in
multiples of endpoint size. Generally a value
of 1 (single) or 2 (double) buffering should be
used. */
USBD_Callbacks_TypeDef_Pointer callbacks; /**< Pointer to struct with callbacks
(@ref USBD_Callbacks_TypeDef). These callbacks
are used by the device stack to signal events
to or query the application. */
const uint32_t reserved; /**< Reserved for future use. */
} USBD_Init_TypeDef;
/** @brief USB Device stack callback structure.
* @details Callback functions used by the device stack to signal events or
* query status to/from the application. See @ref USBD_Init_TypeDef. Assign
* members to NULL if your application don't need a specific callback. */
typedef struct USBD_Callbacks_TypeDef
{
const USBD_UsbResetCb_TypeDef usbReset; /**< Called whenever USB reset signalling is detected
on the USB port. */
const USBD_DeviceStateChangeCb_TypeDef usbStateChange; /**< Called whenever the device change state. */
const USBD_SetupCmdCb_TypeDef setupCmd; /**< Called on each setup request received from host.*/
const USBD_IsSelfPoweredCb_TypeDef isSelfPowered; /**< Called whenever the device stack needs to query
if the device is currently self- or bus-powered.
Applies to devices which can operate in both modes.*/
const USBD_SofIntCb_TypeDef sofInt; /**< Called at each SOF interrupt. If NULL, the device
stack will not enable the SOF interrupt. */
} USBD_Callbacks_TypeDef;
/*** -------------------- DEVICE mode API -------------------------------- ***/
void USBD_AbortAllTransfers( void );
int USBD_AbortTransfer( int epAddr );
void USBD_Connect( void );
void USBD_Disconnect( void );
bool USBD_EpIsBusy( int epAddr );
USBD_State_TypeDef USBD_GetUsbState( void );
const char * USBD_GetUsbStateName( USBD_State_TypeDef state );
int USBD_Init( const USBD_Init_TypeDef *p );
int USBD_Read( int epAddr, void *data, int byteCount, USB_XferCompleteCb_TypeDef callback );
int USBD_RemoteWakeup( void );
bool USBD_SafeToEnterEM2( void );
int USBD_StallEp( int epAddr );
void USBD_Stop( void );
int USBD_UnStallEp( int epAddr );
int USBD_Write( int epAddr, void *data, int byteCount, USB_XferCompleteCb_TypeDef callback );
/** @} (end addtogroup USB_DEVICE) */
#endif /* defined( USB_DEVICE ) */
#if defined( USB_HOST )
/***************************************************************************//**
* @addtogroup USB_HOST
* @brief USB HOST protocol stack, see @ref usb_host page for detailed documentation.
* @{
******************************************************************************/
/*** -------------------- HOST mode API definitions ---------------------- ***/
#define USB_VBUSOVRCUR_PORT_NONE -1 /**< No overcurrent flag functionality. */
#define USB_VBUSOVRCUR_POLARITY_LOW 0 /**< Overcurrent flag pin polarity is low. */
#define USB_VBUSOVRCUR_POLARITY_HIGH 1 /**< Overcurrent flag pin polarity is high. */
/** USB HOST endpoint status enumerator. */
typedef enum
{
H_EP_IDLE = 0, /**< The endpoint is idle. */
H_EP_SETUP = 1, /**< The endpoint is in SETUP stage. */
H_EP_DATA_IN = 2, /**< The endpoint is in DATA IN stage. */
H_EP_DATA_OUT = 3, /**< The endpoint is in DATA OUT stage. */
H_EP_STATUS_IN = 4, /**< The endpoint is in STATUS IN stage. */
H_EP_STATUS_OUT = 5, /**< The endpoint is in STATUS OUT stage. */
} USBH_EpState_TypeDef;
/** @brief USB HOST endpoint status data.
* @details A host application should not manipulate the contents of
* this struct. */
typedef struct
{
USB_Setup_TypeDef setup; /**< A SETUP package. */
uint8_t setupErrCnt; /**< Error counter for SETUP transfers. */
USB_EndpointDescriptor_TypeDef epDesc; /**< Endpoint descriptor. */
struct USBH_Device_TypeDef *parentDevice; /**< The device the endpoint belongs to. */
uint8_t type; /**< Endpoint type. */
uint16_t packetSize; /**< Packet size, current transfer. */
uint8_t hcOut; /**< Host channel number assigned for OUT transfers. */
uint8_t hcIn; /**< Host channel number assigned for IN transfers. */
bool in; /**< Endpoint direction. */
uint8_t toggle; /**< Endpoint data toggle. */
USBH_EpState_TypeDef state; /**< Endpoint state. */
uint8_t addr; /**< Endpoint address. */
uint8_t *buf; /**< Transfer buffer. */
volatile bool xferCompleted; /**< Transfer completion flag. */
USB_Status_TypeDef xferStatus; /**< Transfer status. */
USB_XferCompleteCb_TypeDef xferCompleteCb; /**< Transfer completion callback function. */
uint32_t xferred; /**< Number of bytes transferred. */
uint32_t remaining; /**< Number of bytes remaining. */
uint32_t timeout; /**< Transfer timeout. */
} USBH_Ep_TypeDef;
/** @brief USB HOST device definition.
* @details A host application should not manipulate the contents of
* this struct. */
typedef struct USBH_Device_TypeDef
{
USB_DeviceDescriptor_TypeDef devDesc; /**< The device device descriptor. */
USB_ConfigurationDescriptor_TypeDef confDesc; /**< The device configuration descriptor. */
USB_InterfaceDescriptor_TypeDef itfDesc; /**< The device interface descriptor. */
USBH_Ep_TypeDef ep0; /**< Endpoint 0 status data. */
USBH_Ep_TypeDef *ep; /**< Array of endpoint status data. */
int numEp; /**< Number of endpoints. */
uint8_t addr; /**< The device address. */
uint8_t speed; /**< The device speed (low or full speed). */
} USBH_Device_TypeDef;
/** @brief USB Host stack initialization structure.
* @details This structure is passed to @ref USBH_Init() when starting up the
* device. Max accumulated FIFO size is 2K bytes. */
typedef struct
{
uint32_t rxFifoSize; /**< Number of FIFO bytes set aside for IN endpoints. */
uint32_t nptxFifoSize; /**< Number of FIFO bytes set aside for OUT CTRL/BULK endoints. */
uint32_t ptxFifoSize; /**< Number of FIFO bytes set aside for OUT INTR/ISO endoints. */
uint32_t reserved; /**< Reserved for future use. */
} USBH_Init_TypeDef;
/** Default @ref USBH_Init_TypeDef values, provides reasonable Tx/Rx FIFO
* partitioning. */
/* In DMA mode the total available FIFO space is smaller. */
/* The DMA controller use one FIFO word pr. channel for status. */
/* The unit in the table is byte. */
#define USBH_INIT_DEFAULT \
{ \
MAX_FIFO_SIZE_INWORDS * 2,/* 1024 bytes Rx FIFO size. */ \
MAX_FIFO_SIZE_INWORDS, /* 512 bytes non-periodic Tx FIFO size. */ \
MAX_FIFO_SIZE_INWORDS, /* 512 bytes periodic Tx FIFO size. */ \
0 /* Reserved. */ \
}
/*** -------------------- HOST mode API ---------------------------------- ***/
int USBH_AssignHostChannel( USBH_Ep_TypeDef *ep, uint8_t hcnum );
int USBH_ControlMsg( USBH_Ep_TypeDef *ep, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, void *data, int timeout, USB_XferCompleteCb_TypeDef callback );
int USBH_ControlMsgB( USBH_Ep_TypeDef *ep, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, void *data, int timeout );
bool USBH_DeviceConnected( void );
int USBH_GetConfigurationDescriptorB( USBH_Device_TypeDef *device, void *buf, int len, uint8_t configIndex );
int USBH_GetDeviceDescriptorB( USBH_Device_TypeDef *device, void *buf, int len );
uint8_t USBH_GetPortSpeed( void );
int USBH_GetStringB( USBH_Device_TypeDef *device, uint8_t *buf, int bufLen, uint8_t stringIndex, uint16_t langID );
int USBH_Init( const USBH_Init_TypeDef *p );
int USBH_InitDeviceData( USBH_Device_TypeDef *device, const uint8_t *buf, USBH_Ep_TypeDef *ep, int numEp, uint8_t deviceSpeed );
int USBH_PortReset( void );
int USBH_PortResume( void );
void USBH_PortSuspend( void );
void USBH_PrintString( const char *pre, const USB_StringDescriptor_TypeDef *s, const char *post );
#if defined( USB_USE_PRINTF )
int USBH_PrintConfigurationDescriptor( const USB_ConfigurationDescriptor_TypeDef *config, int maxLen );
int USBH_PrintDeviceDescriptor( const USB_DeviceDescriptor_TypeDef *device );
int USBH_PrintEndpointDescriptor( const USB_EndpointDescriptor_TypeDef *endpoint );
int USBH_PrintInterfaceDescriptor( const USB_InterfaceDescriptor_TypeDef *interface );
#else
#define USBH_PrintConfigurationDescriptor( config, maxLen )
#define USBH_PrintDeviceDescriptor( device )
#define USBH_PrintEndpointDescriptor( endpoint )
#define USBH_PrintInterfaceDescriptor( interface )
#endif /* defined( USB_USE_PRINTF ) */
int USBH_QueryDeviceB( uint8_t *buf, size_t bufsize, uint8_t deviceSpeed );
USB_ConfigurationDescriptor_TypeDef* USBH_QGetConfigurationDescriptor( const uint8_t *buf, int configIndex );
USB_DeviceDescriptor_TypeDef* USBH_QGetDeviceDescriptor( const uint8_t *buf );
USB_EndpointDescriptor_TypeDef* USBH_QGetEndpointDescriptor( const uint8_t *buf, int configIndex, int interfaceIndex, int endpointIndex );
USB_InterfaceDescriptor_TypeDef* USBH_QGetInterfaceDescriptor( const uint8_t *buf, int configIndex, int interfaceIndex );
int USBH_Read( USBH_Ep_TypeDef *ep, void *data, int byteCount, int timeout, USB_XferCompleteCb_TypeDef callback );
int USBH_ReadB( USBH_Ep_TypeDef *ep, void *data, int byteCount, int timeout );
int USBH_SetAddressB( USBH_Device_TypeDef *device, uint8_t deviceAddress );
int USBH_SetAltInterfaceB( USBH_Device_TypeDef *device, uint8_t interfaceIndex, uint8_t alternateSetting );
int USBH_SetConfigurationB( USBH_Device_TypeDef *device, uint8_t configValue );
int USBH_StallEpB( USBH_Ep_TypeDef *ep );
void USBH_Stop( void );
int USBH_UnStallEpB( USBH_Ep_TypeDef *ep );
int USBH_WaitForDeviceConnectionB( uint8_t *buf, int timeoutInSeconds );
int USBH_Write( USBH_Ep_TypeDef *ep, void *data, int byteCount, int timeout, USB_XferCompleteCb_TypeDef callback );
int USBH_WriteB( USBH_Ep_TypeDef *ep, void *data, int byteCount, int timeout );
/** @} (end addtogroup USB_HOST) */
#endif /* defined( USB_HOST ) */
/** @} (end addtogroup USB) */
#ifdef __cplusplus
}
#endif
#endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
#endif /* __EM_USB_H */

222
libs/emusb/inc/em_usbd.h Normal file
View File

@ -0,0 +1,222 @@
/***************************************************************************//**
* @file
* @brief USB protocol stack library API for EFM32.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#ifndef __EM_USBD_H
#define __EM_USBD_H
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_DEVICE )
#ifdef __cplusplus
extern "C" {
#endif
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#if defined( DEBUG_USB_API )
#define DEBUG_TRACE_ABORT( x ) \
{ \
if ( x == USB_STATUS_EP_STALLED ) \
{ DEBUG_USB_API_PUTS( "\nEP cb(), EP stalled" ); } \
else if ( x == USB_STATUS_EP_ABORTED ) \
{ DEBUG_USB_API_PUTS( "\nEP cb(), EP aborted" ); } \
else if ( x == USB_STATUS_DEVICE_UNCONFIGURED ) \
{ DEBUG_USB_API_PUTS( "\nEP cb(), device unconfigured" ); } \
else if ( x == USB_STATUS_DEVICE_SUSPENDED ) \
{ DEBUG_USB_API_PUTS( "\nEP cb(), device suspended" ); } \
else /* ( x == USB_STATUS_DEVICE_RESET ) */ \
{ DEBUG_USB_API_PUTS( "\nEP cb(), device reset" ); } \
}
#else
#define DEBUG_TRACE_ABORT( x )
#endif
extern USBD_Device_TypeDef *dev;
extern volatile bool USBD_poweredDown;
__STATIC_INLINE void USBD_ArmEp0( USBD_Ep_TypeDef *ep );
__STATIC_INLINE void USBD_ArmEpN( USBD_Ep_TypeDef *ep );
__STATIC_INLINE void USBD_AbortEp( USBD_Ep_TypeDef *ep );
void USBD_SetUsbState( USBD_State_TypeDef newState );
int USBDCH9_SetupCmd( USBD_Device_TypeDef *device );
void USBDEP_Ep0Handler( USBD_Device_TypeDef *device );
void USBDEP_EpHandler( uint8_t epAddr );
__STATIC_INLINE void USBD_ActivateAllEps( bool forceIdle )
{
int i;
for ( i = 1; i <= NUM_EP_USED; i++ )
{
USBDHAL_ActivateEp( &dev->ep[ i ], forceIdle );
}
}
__STATIC_INLINE void USBD_ArmEp( USBD_Ep_TypeDef *ep )
{
if ( ep->num == 0 )
{
USBD_ArmEp0( ep );
}
else
{
USBD_ArmEpN( ep );
}
}
__STATIC_INLINE void USBD_ArmEp0( USBD_Ep_TypeDef *ep )
{
if ( ep->in )
{
if ( ep->remaining == 0 ) /* Zero Length Packet? */
{
ep->zlp = 1;
}
#if !defined( USB_SLAVEMODE )
USBDHAL_SetEp0InDmaPtr( ep->buf );
#endif
USBDHAL_StartEp0In( EFM32_MIN( ep->remaining, ep->packetSize ) );
}
else
{
#if !defined( USB_SLAVEMODE )
USBDHAL_SetEp0OutDmaPtr( ep->buf );
#endif
USBDHAL_StartEp0Out( ep->packetSize );
}
}
__STATIC_INLINE void USBD_ArmEpN( USBD_Ep_TypeDef *ep )
{
if ( ep->in )
{
USBDHAL_StartEpIn( ep );
}
else
{
USBDHAL_StartEpOut( ep );
}
}
__STATIC_INLINE void USBD_DeactivateAllEps( USB_Status_TypeDef reason )
{
int i;
USBD_Ep_TypeDef *ep;
for ( i = 1; i <= NUM_EP_USED; i++ )
{
ep = &dev->ep[ i ];
if ( ep->state == D_EP_IDLE )
{
USBDHAL_DeactivateEp( ep );
}
}
USBDHAL_AbortAllTransfers( reason );
}
__STATIC_INLINE USBD_Ep_TypeDef *USBD_GetEpFromAddr( uint8_t epAddr )
{
int epIndex;
USBD_Ep_TypeDef *ep = NULL;
if ( epAddr & USB_SETUP_DIR_MASK )
{
epIndex = dev->inEpAddr2EpIndex[ epAddr & USB_EPNUM_MASK ];
}
else
{
epIndex = dev->outEpAddr2EpIndex[ epAddr & USB_EPNUM_MASK ];
}
if ( epIndex )
{
ep = &dev->ep[ epIndex ];
}
else if ( ( epAddr & USB_EPNUM_MASK ) == 0 )
{
ep = &dev->ep[ 0 ];
}
return ep;
}
__STATIC_INLINE void USBD_ReArmEp0( USBD_Ep_TypeDef *ep )
{
if ( ep->in )
{
USBDHAL_StartEp0In( EFM32_MIN( ep->remaining, ep->packetSize ) );
}
else
{
USBDHAL_StartEp0Out( ep->packetSize );
}
}
__STATIC_INLINE void USBD_AbortEp( USBD_Ep_TypeDef *ep )
{
if ( ep->state == D_EP_IDLE )
{
return;
}
if ( ep->in )
{
USBDHAL_AbortEpIn( ep );
}
else
{
USBDHAL_AbortEpOut( ep );
}
}
/** @endcond */
#ifdef __cplusplus
}
#endif
#endif /* defined( USB_DEVICE ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
#endif /* __EM_USBD_H */

86
libs/emusb/inc/em_usbh.h Normal file
View File

@ -0,0 +1,86 @@
/***************************************************************************//**
* @file
* @brief USB protocol stack library API for EFM32.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#ifndef __EM_USBH_H
#define __EM_USBH_H
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_HOST )
#ifdef __cplusplus
extern "C" {
#endif
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
extern USBH_Hc_TypeDef hcs[];
extern int USBH_attachRetryCount;
extern const USBH_AttachTiming_TypeDef USBH_attachTiming[];
extern USBH_Init_TypeDef USBH_initData;
extern volatile USBH_PortState_TypeDef USBH_portStatus;
USB_Status_TypeDef USBH_CtlSendSetup( USBH_Ep_TypeDef *ep );
USB_Status_TypeDef USBH_CtlSendData( USBH_Ep_TypeDef *ep, uint16_t length );
USB_Status_TypeDef USBH_CtlReceiveData( USBH_Ep_TypeDef *ep, uint16_t length );
#if defined( USB_RAW_API )
int USBH_CtlRxRaw( uint8_t pid, USBH_Ep_TypeDef *ep, void *data, int byteCount );
int USBH_CtlTxRaw( uint8_t pid, USBH_Ep_TypeDef *ep, void *data, int byteCount );
#endif
void USBHEP_EpHandler( USBH_Ep_TypeDef *ep, USB_Status_TypeDef result );
void USBHEP_CtrlEpHandler( USBH_Ep_TypeDef *ep, USB_Status_TypeDef result );
void USBHEP_TransferDone( USBH_Ep_TypeDef *ep, USB_Status_TypeDef result );
__STATIC_INLINE uint16_t USBH_GetFrameNum( void )
{
return USBHHAL_GetFrameNum();
}
__STATIC_INLINE bool USBH_FrameNumIsEven( void )
{
return ( USBHHAL_GetFrameNum() & 1 ) == 0;
}
/** @endcond */
#ifdef __cplusplus
}
#endif
#endif /* defined( USB_HOST ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
#endif /* __EM_USBH_H */

965
libs/emusb/inc/em_usbhal.h Normal file
View File

@ -0,0 +1,965 @@
/***************************************************************************//**
* @file
* @brief USB protocol stack library, low level USB peripheral access.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#ifndef __EM_USBHAL_H
#define __EM_USBHAL_H
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_DEVICE ) || defined( USB_HOST )
#ifdef __cplusplus
extern "C" {
#endif
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#define USB_PID_DATA0 0
#define USB_PID_DATA2 1
#define USB_PID_DATA1 2
#define USB_PID_SETUP 3
#define HPRT_F_SPEED ( 1 << _USB_HPRT_PRTSPD_SHIFT )
#define HPRT_L_SPEED ( 2 << _USB_HPRT_PRTSPD_SHIFT )
#define HCFG_PHYCLK_48MHZ 1
#define HCFG_PHYCLK_6MHZ 2
#define DOEP0_XFERSIZE_PKTCNT_MASK ( _USB_DOEP0TSIZ_XFERSIZE_MASK | \
_USB_DOEP0TSIZ_PKTCNT_MASK )
#define DOEP_XFERSIZE_PKTCNT_MASK ( _USB_DOEP_TSIZ_XFERSIZE_MASK | \
_USB_DOEP_TSIZ_PKTCNT_MASK )
#define DIEP0_XFERSIZE_PKTCNT_MASK ( _USB_DIEP0TSIZ_XFERSIZE_MASK | \
_USB_DIEP0TSIZ_PKTCNT_MASK )
#define DIEP_XFERSIZE_PKTCNT_MASK ( _USB_DIEP_TSIZ_XFERSIZE_MASK | \
_USB_DIEP_TSIZ_PKTCNT_MASK | \
_USB_DIEP_TSIZ_MC_MASK )
#define DIEPCTL_EPTYPE_CONTROL (0 << _USB_DIEP_CTL_EPTYPE_SHIFT )
#define DIEPCTL_EPTYPE_ISOC (1 << _USB_DIEP_CTL_EPTYPE_SHIFT )
#define DIEPCTL_EPTYPE_BULK (2 << _USB_DIEP_CTL_EPTYPE_SHIFT )
#define DIEPCTL_EPTYPE_INTR (3 << _USB_DIEP_CTL_EPTYPE_SHIFT )
#define HCCHAR_EPTYPE_CTRL (0 << _USB_HC_CHAR_EPTYPE_SHIFT )
#define HCCHAR_EPTYPE_ISOC (1 << _USB_HC_CHAR_EPTYPE_SHIFT )
#define HCCHAR_EPTYPE_BULK (2 << _USB_HC_CHAR_EPTYPE_SHIFT )
#define HCCHAR_EPTYPE_INTR (3 << _USB_HC_CHAR_EPTYPE_SHIFT )
#define GRXSTSP_PKTSTS_DEVICE_GOTNAK ( 1 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define GRXSTSP_PKTSTS_DEVICE_DATAOUTRECEIVED ( 2 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define GRXSTSP_PKTSTS_DEVICE_DATAOUTCOMPLETE ( 3 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define GRXSTSP_PKTSTS_DEVICE_SETUPCOMPLETE ( 4 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define GRXSTSP_PKTSTS_DEVICE_SETUPRECEIVED ( 6 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define GRXSTSP_PKTSTS_HOST_DATAINRECEIVED ( 2 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define GRXSTSP_PKTSTS_HOST_DATAINCOMPLETE ( 3 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define GRXSTSP_PKTSTS_HOST_DATATOGGLEERROR ( 5 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define GRXSTSP_PKTSTS_HOST_CHANNELHALTED ( 7 << _USB_GRXSTSP_PKTSTS_SHIFT )
#define DCTL_WO_BITMASK \
( _USB_DCTL_CGOUTNAK_MASK | _USB_DCTL_SGOUTNAK_MASK | \
_USB_DCTL_CGNPINNAK_MASK | _USB_DCTL_SGNPINNAK_MASK )
#define GUSBCFG_WO_BITMASK ( USB_GUSBCFG_CORRUPTTXPKT )
#define DEPCTL_WO_BITMASK \
( USB_DIEP_CTL_CNAK | USB_DIEP_CTL_SNAK | \
USB_DIEP_CTL_SETD0PIDEF | USB_DIEP_CTL_SETD1PIDOF )
#define HPRT_WC_MASK ( USB_HPRT_PRTCONNDET | USB_HPRT_PRTENA | \
USB_HPRT_PRTENCHNG | USB_HPRT_PRTOVRCURRCHNG )
typedef __IO uint32_t USB_FIFO_TypeDef[ 0x1000 / sizeof( uint32_t ) ];
typedef __IO uint32_t USB_DIEPTXF_TypeDef;
#define USB_DINEPS ((USB_DIEP_TypeDef *) &USB->DIEP0CTL )
#define USB_DOUTEPS ((USB_DOEP_TypeDef *) &USB->DOEP0CTL )
#define USB_FIFOS ((USB_FIFO_TypeDef *) &USB->FIFO0D )
#define USB_DIEPTXFS ((USB_DIEPTXF_TypeDef *) &USB->DIEPTXF1 )
void USBHAL_CoreReset( void );
#if defined( USB_DEVICE )
void USBDHAL_AbortAllTransfers( USB_Status_TypeDef reason );
USB_Status_TypeDef USBDHAL_CoreInit( const uint32_t totalRxFifoSize,
const uint32_t totalTxFifoSize );
void USBDHAL_Connect( void );
void USBDHAL_Disconnect( void );
void USBDHAL_AbortAllEps( void );
void USBDHAL_AbortEpIn( USBD_Ep_TypeDef *ep );
void USBDHAL_AbortEpOut( USBD_Ep_TypeDef *ep );
__STATIC_INLINE USB_Status_TypeDef USBDHAL_GetStallStatusEp(
USBD_Ep_TypeDef *ep, uint16_t *halt );
__STATIC_INLINE uint32_t USBDHAL_GetInEpInts( USBD_Ep_TypeDef *ep );
__STATIC_INLINE uint32_t USBDHAL_GetOutEpInts( USBD_Ep_TypeDef *ep );
__STATIC_INLINE void USBDHAL_SetEPDISNAK( USBD_Ep_TypeDef *ep );
#endif /* defined( USB_DEVICE ) */
#if defined( USB_HOST )
USB_Status_TypeDef USBHHAL_CoreInit( const uint32_t rxFifoSize,
const uint32_t nptxFifoSize,
const uint32_t ptxFifoSize );
void USBHHAL_HCHalt( int hcnum, uint32_t hcchar, uint32_t eptype );
void USBHHAL_HCInit( int hcnum );
void USBHHAL_HCStart( int hcnum );
#endif /* defined( USB_HOST ) */
__STATIC_INLINE void USBHAL_DisableGlobalInt( void )
{
USB->GAHBCFG &= ~USB_GAHBCFG_GLBLINTRMSK;
}
__STATIC_INLINE void USBHAL_EnableGlobalInt( void )
{
USB->GAHBCFG |= USB_GAHBCFG_GLBLINTRMSK;
}
__STATIC_INLINE void USBHAL_FlushRxFifo( void )
{
USB->GRSTCTL = USB_GRSTCTL_RXFFLSH;
while ( USB->GRSTCTL & USB_GRSTCTL_RXFFLSH ) {}
}
__STATIC_INLINE void USBHAL_FlushTxFifo( uint8_t fifoNum )
{
USB->GRSTCTL = USB_GRSTCTL_TXFFLSH | ( fifoNum << _USB_GRSTCTL_TXFNUM_SHIFT );
while ( USB->GRSTCTL & USB_GRSTCTL_TXFFLSH ) {}
}
__STATIC_INLINE uint32_t USBHAL_GetCoreInts( void )
{
uint32_t retVal;
retVal = USB->GINTSTS;
retVal &= USB->GINTMSK;
return retVal;
}
__STATIC_INLINE void USBHAL_ReadFifo( uint8_t *dest, uint16_t byteCount )
{
#if 0
int words, i;
#else
int words, loops;
#endif
uint32_t *data;
__IO uint32_t *fifo;
words = (byteCount + 3) / 4;
data = (uint32_t*)dest;
fifo = USB->FIFO0D;
#if 0
for ( i=0; i<words; i++ )
{
*data++ = *fifo;
}
#else
/* Duff's Device ! */
loops = ( words + 7 ) / 8;
switch( words % 8)
{
case 0: do
{
*data++ = *fifo;
case 7: *data++ = *fifo;
case 6: *data++ = *fifo;
case 5: *data++ = *fifo;
case 4: *data++ = *fifo;
case 3: *data++ = *fifo;
case 2: *data++ = *fifo;
case 1: *data++ = *fifo;
} while ( --loops );
}
#endif
}
__STATIC_INLINE void USBHAL_FlushFifo( uint16_t byteCount )
{
int words, loops;
__IO uint32_t *fifo;
words = (byteCount + 3) / 4;
fifo = USB->FIFO0D;
/* Duff's Device ! */
loops = ( words + 7 ) / 8;
switch( words % 8)
{
case 0: do
{
*fifo;
case 7: *fifo;
case 6: *fifo;
case 5: *fifo;
case 4: *fifo;
case 3: *fifo;
case 2: *fifo;
case 1: *fifo;
} while ( --loops );
}
}
__STATIC_INLINE bool USBHAL_VbusIsOn( void )
{
return ( USB->STATUS & USB_STATUS_VREGOS ) != 0;
}
__STATIC_INLINE void USBHAL_WriteFifo( int epnum, uint8_t *src,
uint16_t byteCount )
{
#if 0
int words, i;
#else
int words, loops;
#endif
uint32_t *data;
__IO uint32_t *fifo;
words = (byteCount + 3) / 4;
data = (uint32_t*)src;
fifo = USB_FIFOS[ epnum ];
#if 0
for ( i=0; i<words; i++ )
{
*fifo = *data++;
}
#else
/* Duff's Device ! */
loops = ( words + 7 ) / 8;
switch( words % 8)
{
case 0: do
{
*fifo = *data++;
case 7: *fifo = *data++;
case 6: *fifo = *data++;
case 5: *fifo = *data++;
case 4: *fifo = *data++;
case 3: *fifo = *data++;
case 2: *fifo = *data++;
case 1: *fifo = *data++;
} while ( --loops );
}
#endif
}
#if defined( USB_DEVICE )
__STATIC_INLINE void USBDHAL_ActivateEp( USBD_Ep_TypeDef *ep, bool forceIdle )
{
#define DIEP_MPS_EPTYPE_TXFNUM_MASK ( _USB_DIEP_CTL_MPS_MASK | \
_USB_DIEP_CTL_EPTYPE_MASK | \
_USB_DIEP_CTL_TXFNUM_MASK )
#define DOEP_MPS_EPTYPE_MASK ( _USB_DOEP_CTL_MPS_MASK | \
_USB_DOEP_CTL_EPTYPE_MASK )
uint32_t daintmask, depctl;
if ( forceIdle )
ep->state = D_EP_IDLE;
if ( ep->in )
{
daintmask = ep->mask;
depctl = USB_DINEPS[ ep->num ].CTL & ~DEPCTL_WO_BITMASK;
if ( !( depctl & USB_DIEP_CTL_USBACTEP ) )
{
depctl = ( depctl &
~( DIEP_MPS_EPTYPE_TXFNUM_MASK |
USB_DIEP_CTL_STALL ) ) |
( ep->packetSize << _USB_DIEP_CTL_MPS_SHIFT ) |
( ep->type << _USB_DIEP_CTL_EPTYPE_SHIFT ) |
( ep->txFifoNum << _USB_DIEP_CTL_TXFNUM_SHIFT ) |
USB_DIEP_CTL_SETD0PIDEF |
USB_DIEP_CTL_USBACTEP |
USB_DIEP_CTL_SNAK;
}
else
{
depctl |= USB_DIEP_CTL_SETD0PIDEF;
}
USB_DINEPS[ ep->num ].CTL = depctl;
}
else
{
daintmask = ep->mask << _USB_DAINTMSK_OUTEPMSK0_SHIFT;
depctl = USB_DOUTEPS[ ep->num ].CTL & ~DEPCTL_WO_BITMASK;
if ( !( depctl & USB_DOEP_CTL_USBACTEP ) )
{
depctl = ( depctl &
~( DOEP_MPS_EPTYPE_MASK |
USB_DOEP_CTL_STALL ) ) |
( ep->packetSize << _USB_DOEP_CTL_MPS_SHIFT ) |
( ep->type << _USB_DOEP_CTL_EPTYPE_SHIFT ) |
USB_DOEP_CTL_SETD0PIDEF |
USB_DOEP_CTL_USBACTEP |
USB_DOEP_CTL_SNAK;
}
else
{
depctl |= USB_DOEP_CTL_SETD0PIDEF;
}
USB_DOUTEPS[ ep->num ].CTL = depctl;
}
/* Enable interrupt for this EP */
USB->DAINTMSK |= daintmask;
#undef DIEP_MPS_EPTYPE_TXFNUM_MASK
#undef DOEP_MPS_EPTYPE_MASK
}
__STATIC_INLINE void USBDHAL_ClearRemoteWakeup( void )
{
USB->DCTL &= ~( DCTL_WO_BITMASK | USB_DCTL_RMTWKUPSIG );
}
__STATIC_INLINE void USBDHAL_DeactivateEp( USBD_Ep_TypeDef *ep )
{
uint32_t daintmask;
if ( ep->in )
{
USB_DINEPS[ ep->num ].CTL = 0;
daintmask = ep->mask;
}
else
{
USB_DOUTEPS[ ep->num ].CTL = 0;
daintmask = ep->mask << _USB_DAINTMSK_OUTEPMSK0_SHIFT;
}
/* Disable interrupt for this EP */
USB->DAINTMSK &= ~daintmask;
}
__STATIC_INLINE void USBDHAL_EnableInts( USBD_Device_TypeDef *dev )
{
uint32_t mask;
/* Disable all interrupts. */
USB->GINTMSK = 0;
/* Clear pending interrupts */
USB->GINTSTS = 0xFFFFFFFF;
mask = USB_GINTMSK_USBSUSPMSK |
#if defined( USB_SLAVEMODE )
USB_GINTMSK_RXFLVLMSK |
#endif
USB_GINTMSK_USBRSTMSK |
USB_GINTMSK_ENUMDONEMSK |
USB_GINTMSK_IEPINTMSK |
USB_GINTMSK_OEPINTMSK |
USB_GINTMSK_WKUPINTMSK;
if ( dev->callbacks->sofInt )
{
mask |= USB_GINTMSK_SOFMSK;
}
USB->GINTMSK = mask;
}
__STATIC_INLINE void USBDHAL_EnableUsbResetInt( void )
{
/* Disable all interrupts. */
USB->GINTMSK = 0;
/* Clear pending interrupts */
USB->GINTSTS = 0xFFFFFFFF;
USB->GINTMSK = USB_GINTMSK_USBRSTMSK;
}
__STATIC_INLINE void USBDHAL_Ep0Activate( void )
{
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_CGNPINNAK;
#if !defined( USB_SLAVEMODE )
USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) |
USB_DOEP_CTL_CNAK | USB_DOEP_CTL_EPENA;
#endif
}
__STATIC_INLINE bool USBDHAL_EpIsStalled( USBD_Ep_TypeDef *ep )
{
bool retVal = false;
uint16_t stallStatus;
if ( USBDHAL_GetStallStatusEp( ep, &stallStatus ) == USB_STATUS_OK )
{
retVal = stallStatus & 1 ? true : false;
}
return retVal;
}
#if defined( USB_SLAVEMODE )
__STATIC_INLINE void USBDHAL_FillFifo( USBD_Ep_TypeDef *ep )
{
uint32_t len, words, fifoSpace;
for( ;; )
{
len = EFM32_MIN( ep->remaining, ep->packetSize );
words = (len + 3) / 4;
fifoSpace = USB_DINEPS[ ep->num ].TXFSTS & _USB_DIEP_TXFSTS_SPCAVAIL_MASK;
if ( ( fifoSpace >= words ) && len )
{
USBHAL_WriteFifo( ep->num, ep->buf, len );
ep->xferred += len;
ep->remaining -= len;
ep->buf += len;
if ( ep->num == 0 )
{
break;
}
}
else
{
break;
}
}
if ( len == 0 )
{
/* Disable Tx FIFO empty interrupt */
USB->DIEPEMPMSK &= ~ep->mask;
}
}
#endif
__STATIC_INLINE uint32_t USBDHAL_GetAllInEpInts( void )
{
uint32_t retVal;
retVal = USB->DAINT;
retVal &= USB->DAINTMSK;
return retVal & 0xFFFF;
}
__STATIC_INLINE uint32_t USBDHAL_GetAllOutEpInts( void )
{
uint32_t retVal;
retVal = USB->DAINT;
retVal &= USB->DAINTMSK;
return retVal >> 16;
}
__STATIC_INLINE uint32_t USBDHAL_GetInEpInts( USBD_Ep_TypeDef *ep )
{
uint32_t retVal, msk;
#if defined( USB_SLAVEMODE )
uint32_t emp;
#endif
msk = USB->DIEPMSK;
#if defined( USB_SLAVEMODE )
emp = USB->DIEPEMPMSK;
msk |= ((emp >> ep->num) & 0x1) << 7;
#endif
retVal = USB_DINEPS[ ep->num ].INT;
return retVal & msk;
}
__STATIC_INLINE uint32_t USBDHAL_GetOutEpInts( USBD_Ep_TypeDef *ep )
{
uint32_t retVal;
retVal = USB_DOUTEPS[ ep->num ].INT;
retVal &= USB->DOEPMSK;
return retVal;
}
__STATIC_INLINE USB_Status_TypeDef USBDHAL_GetStallStatusEp(
USBD_Ep_TypeDef *ep, uint16_t *halt )
{
uint32_t depctl, eptype;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( ep->in == true )
{
depctl = USB_DINEPS[ ep->num ].CTL;
eptype = depctl & _USB_DIEP_CTL_EPTYPE_MASK;
if (( eptype == DIEPCTL_EPTYPE_INTR ) || ( eptype == DIEPCTL_EPTYPE_BULK ))
{
*halt = depctl & USB_DIEP_CTL_STALL ? 1 : 0;
retVal = USB_STATUS_OK;
}
}
else
{
depctl = USB_DOUTEPS[ ep->num ].CTL;
eptype = depctl & _USB_DOEP_CTL_EPTYPE_MASK;
if (( eptype == DIEPCTL_EPTYPE_INTR ) || ( eptype == DIEPCTL_EPTYPE_BULK ))
{
*halt = depctl & USB_DOEP_CTL_STALL ? 1 : 0;
retVal = USB_STATUS_OK;
}
}
return retVal;
}
__STATIC_INLINE void USBDHAL_ReenableEp0Setup( USBD_Device_TypeDef *dev )
{
#if !defined( USB_SLAVEMODE )
USB->DOEP0DMAADDR = (uint32_t)dev->setupPkt;
#else
(void)dev;
#endif
USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) | USB_DOEP_CTL_EPENA;
}
__STATIC_INLINE void USBDHAL_SetAddr( uint8_t addr )
{
USB->DCFG = ( USB->DCFG &
~_USB_DCFG_DEVADDR_MASK ) |
(addr << _USB_DCFG_DEVADDR_SHIFT );
}
#if !defined( USB_SLAVEMODE )
__STATIC_INLINE void USBDHAL_SetEp0InDmaPtr( uint8_t* addr )
{
USB->DIEP0DMAADDR = (uint32_t)addr;
}
__STATIC_INLINE void USBDHAL_SetEp0OutDmaPtr( uint8_t* addr )
{
USB->DOEP0DMAADDR = (uint32_t)addr;
}
#endif
__STATIC_INLINE void USBDHAL_SetEPDISNAK( USBD_Ep_TypeDef *ep )
{
if ( ep->in )
{
USB_DINEPS[ ep->num ].CTL = ( USB_DINEPS[ ep->num ].CTL &
~DEPCTL_WO_BITMASK ) |
USB_DIEP_CTL_SNAK |
USB_DIEP_CTL_EPDIS;
}
else
{
USB_DOUTEPS[ ep->num ].CTL = ( USB_DOUTEPS[ ep->num ].CTL &
~DEPCTL_WO_BITMASK ) |
USB_DOEP_CTL_EPENA;
USB_DOUTEPS[ ep->num ].CTL = ( USB_DOUTEPS[ ep->num ].CTL &
~DEPCTL_WO_BITMASK ) |
USB_DOEP_CTL_SNAK |
USB_DOEP_CTL_EPDIS;
}
}
__STATIC_INLINE void USBDHAL_SetRemoteWakeup( void )
{
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_RMTWKUPSIG;
}
__STATIC_INLINE USB_Status_TypeDef USBDHAL_StallEp( USBD_Ep_TypeDef *ep )
{
uint32_t depctl, eptype;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( ep->in == true )
{
depctl = USB_DINEPS[ ep->num ].CTL & ~DEPCTL_WO_BITMASK;
eptype = depctl & _USB_DIEP_CTL_EPTYPE_MASK;
if ( eptype != DIEPCTL_EPTYPE_ISOC )
{
if ( depctl & USB_DIEP_CTL_EPENA )
{
depctl |= USB_DIEP_CTL_EPDIS;
}
USB_DINEPS[ ep->num ].CTL = depctl | USB_DIEP_CTL_STALL;
retVal = USB_STATUS_OK;
}
}
else
{
depctl = USB_DOUTEPS[ ep->num ].CTL & ~DEPCTL_WO_BITMASK;
eptype = depctl & _USB_DOEP_CTL_EPTYPE_MASK;
if ( eptype != DIEPCTL_EPTYPE_ISOC )
{
USB_DOUTEPS[ ep->num ].CTL = depctl | USB_DOEP_CTL_STALL;
retVal = USB_STATUS_OK;
}
}
return retVal;
}
__STATIC_INLINE void USBDHAL_StartEp0In( uint32_t len )
{
USB->DIEP0TSIZ = ( len << _USB_DIEP0TSIZ_XFERSIZE_SHIFT ) |
( 1 << _USB_DIEP0TSIZ_PKTCNT_SHIFT );
USB->DIEP0CTL = ( USB->DIEP0CTL & ~DEPCTL_WO_BITMASK ) |
USB_DIEP_CTL_CNAK | USB_DIEP_CTL_EPENA;
#if defined( USB_SLAVEMODE )
if ( len ) /* Enable Tx FIFO Empty Interrupt */
{
USB->DIEPEMPMSK |= 1;
}
#endif
}
__STATIC_INLINE void USBDHAL_StartEp0Out( uint32_t len )
{
USB->DOEP0TSIZ = ( len << _USB_DOEP0TSIZ_XFERSIZE_SHIFT ) |
( 1 << _USB_DOEP0TSIZ_PKTCNT_SHIFT );
USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) |
USB_DOEP_CTL_CNAK | USB_DOEP_CTL_EPENA;
}
__STATIC_INLINE void USBDHAL_StartEp0Setup( USBD_Device_TypeDef *dev )
{
dev->ep[ 0 ].in = false;
USB->DOEP0TSIZ = 3 << _USB_DOEP0TSIZ_SUPCNT_SHIFT;
#if !defined( USB_SLAVEMODE )
dev->setup = dev->setupPkt;
USB->DOEP0DMAADDR = (uint32_t)dev->setup;
USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) |
USB_DOEP_CTL_CNAK | USB_DOEP_CTL_EPENA;
#endif
}
__STATIC_INLINE void USBDHAL_StartEpIn( USBD_Ep_TypeDef *ep )
{
uint32_t pktcnt, xfersize;
if ( ep->remaining == 0 ) /* ZLP ? */
{
pktcnt = 1;
xfersize = 0;
}
else
{
pktcnt = ( ep->remaining - 1 + ep->packetSize ) / ep->packetSize;
xfersize = ep->remaining;
}
USB_DINEPS[ ep->num ].TSIZ =
( USB_DINEPS[ ep->num ].TSIZ &
~DIEP_XFERSIZE_PKTCNT_MASK ) |
( xfersize << _USB_DIEP_TSIZ_XFERSIZE_SHIFT ) |
( pktcnt << _USB_DIEP_TSIZ_PKTCNT_SHIFT );
#if defined( USB_SLAVEMODE )
if ( ep->remaining ) /* Enable Tx FIFO Empty Interrupt */
{
USB->DIEPEMPMSK |= ep->mask;
}
#else
USB_DINEPS[ ep->num ].DMAADDR = (uint32_t)ep->buf;
#endif
USB_DINEPS[ ep->num ].CTL =
( USB_DINEPS[ ep->num ].CTL & ~DEPCTL_WO_BITMASK ) |
USB_DIEP_CTL_CNAK |
USB_DIEP_CTL_EPENA;
}
__STATIC_INLINE void USBDHAL_StartEpOut( USBD_Ep_TypeDef *ep )
{
uint32_t pktcnt, xfersize;
if ( ep->remaining == 0 ) /* ZLP ? */
{
pktcnt = 1;
xfersize = ep->packetSize;
}
else
{
pktcnt = ( ep->remaining - 1 + ep->packetSize ) / ep->packetSize;
xfersize = pktcnt * ep->packetSize;
}
USB_DOUTEPS[ ep->num ].TSIZ =
( USB_DOUTEPS[ ep->num ].TSIZ &
~DOEP_XFERSIZE_PKTCNT_MASK ) |
( xfersize << _USB_DOEP_TSIZ_XFERSIZE_SHIFT ) |
( pktcnt << _USB_DOEP_TSIZ_PKTCNT_SHIFT );
#if !defined( USB_SLAVEMODE )
ep->hwXferSize = xfersize;
USB_DOUTEPS[ ep->num ].DMAADDR = (uint32_t)ep->buf;
#endif
USB_DOUTEPS[ ep->num ].CTL =
( USB_DOUTEPS[ ep->num ].CTL &
~DEPCTL_WO_BITMASK ) |
USB_DOEP_CTL_CNAK |
USB_DOEP_CTL_EPENA;
}
__STATIC_INLINE USB_Status_TypeDef USBDHAL_UnStallEp( USBD_Ep_TypeDef *ep )
{
uint32_t depctl, eptype;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( ep->in == true )
{
depctl = USB_DINEPS[ ep->num ].CTL & ~DEPCTL_WO_BITMASK;
eptype = depctl & _USB_DIEP_CTL_EPTYPE_MASK;
if (( eptype == DIEPCTL_EPTYPE_INTR ) || ( eptype == DIEPCTL_EPTYPE_BULK ))
{
depctl |= USB_DIEP_CTL_SETD0PIDEF;
depctl &= ~USB_DIEP_CTL_STALL;
USB_DINEPS[ ep->num ].CTL = depctl;
retVal = USB_STATUS_OK;
}
}
else
{
depctl = USB_DOUTEPS[ ep->num ].CTL & ~DEPCTL_WO_BITMASK;
eptype = depctl & _USB_DOEP_CTL_EPTYPE_MASK;
if (( eptype == DIEPCTL_EPTYPE_INTR ) || ( eptype == DIEPCTL_EPTYPE_BULK ))
{
depctl |= USB_DOEP_CTL_SETD0PIDEF;
depctl &= ~USB_DOEP_CTL_STALL;
USB_DOUTEPS[ ep->num ].CTL = depctl;
retVal = USB_STATUS_OK;
}
}
return retVal;
}
#endif /* defined( USB_DEVICE ) */
#if defined( USB_HOST )
__STATIC_INLINE void USBHHAL_HCActivate( int hcnum, uint32_t hcchar, bool intep )
{
uint32_t oddframe;
if ( intep )
{
oddframe = USB->HFNUM & 1;
USB->HC[ hcnum ].CHAR =
( hcchar &
~( USB_HC_CHAR_CHDIS | _USB_HC_CHAR_ODDFRM_MASK ) ) |
/* Schedule INT transfers to start in next frame. */
( oddframe & 1 ? 0 : USB_HC_CHAR_ODDFRM ) |
USB_HC_CHAR_CHENA;
}
else
{
USB->HC[ hcnum ].CHAR = ( hcchar & ~USB_HC_CHAR_CHDIS ) |
USB_HC_CHAR_CHENA;
}
}
__STATIC_INLINE bool USBHHAL_InitializedAndPowered( void )
{
if ( ( USB->ROUTE & USB_ROUTE_PHYPEN ) &&
( USB->HPRT & USB_HPRT_PRTPWR ) )
return true;
return false;
}
__STATIC_INLINE void USBHHAL_EnableInts( void )
{
/* Disable all interrupts. */
USB->GINTMSK = 0;
/* Clear pending OTG interrupts */
USB->GOTGINT = 0xFFFFFFFF;
/* Clear pending interrupts */
USB->GINTSTS = 0xFFFFFFFF;
USB->GINTMSK = USB_GINTMSK_PRTINTMSK |
#if defined( USB_SLAVEMODE )
USB_GINTMSK_RXFLVLMSK |
#endif
USB_GINTMSK_HCHINTMSK |
USB_GINTMSK_DISCONNINTMSK;
}
#if defined( USB_SLAVEMODE )
__STATIC_INLINE void USBHHAL_FillFifo( uint8_t hcnum,
uint32_t fifoStatus,
USBH_Hc_TypeDef *hc )
{
uint16_t words, len;
if ( hc->remaining > 0 )
{
len = EFM32_MIN( hc->remaining, hc->ep->packetSize );
words = ( len + 3 ) / 4;
if ( ( fifoStatus & _USB_GNPTXSTS_NPTXQSPCAVAIL_MASK ) &&
( ( fifoStatus & _USB_GNPTXSTS_NPTXFSPCAVAIL_MASK ) >= words ) )
{
hc->pending = len;
USBHAL_WriteFifo( hcnum, hc->buf, len );
if ( hc->ep->type == USB_EPTYPE_INTR )
{
hc->txPFempIntOn = false;
}
else
{
hc->txNpFempIntOn = false;
}
}
else
{
/* Write to FIFO on "empty" int. */
if ( hc->ep->type == USB_EPTYPE_INTR )
{
USB->GINTMSK |= USB_GINTSTS_PTXFEMP;
hc->txPFempIntOn = true;
}
else
{
USB->GINTMSK |= USB_GINTSTS_NPTXFEMP; /* Write FIFO on "empty" int. */
hc->txNpFempIntOn = true;
}
}
}
}
#endif
__STATIC_INLINE uint16_t USBHHAL_GetFrameNum( void )
{
return USB->HFNUM;
}
__STATIC_INLINE uint32_t USBHHAL_GetHcChar( uint8_t hcnum )
{
return USB->HC[ hcnum ].CHAR;
}
__STATIC_INLINE uint32_t USBHHAL_GetHcInts( uint8_t hcnum )
{
uint32_t retVal;
retVal = USB->HC[ hcnum ].INT;
#if defined( USB_SLAVEMODE )
retVal &= USB->HC[ hcnum ].INTMSK;
#endif
return retVal;
}
__STATIC_INLINE uint32_t USBHHAL_GetHostChannelInts( void )
{
return USB->HAINT;
}
__STATIC_INLINE uint8_t USBHHAL_GetPortSpeed( void )
{
return ( USB->HPRT & _USB_HPRT_PRTSPD_MASK ) >> _USB_HPRT_PRTSPD_SHIFT;
}
__STATIC_INLINE void USBHHAL_PortReset( bool on )
{
if ( on )
{
DEBUG_USB_INT_LO_PUTCHAR( '+' );
USB->HPRT = ( USB->HPRT & ~HPRT_WC_MASK ) | USB_HPRT_PRTRST;
}
else
{
DEBUG_USB_INT_LO_PUTCHAR( '-' );
USB->HPRT &= ~( HPRT_WC_MASK | USB_HPRT_PRTRST );
}
}
__STATIC_INLINE void USBHHAL_PortResume( bool on )
{
if ( on )
{
USB->HPRT = ( USB->HPRT & ~( HPRT_WC_MASK | USB_HPRT_PRTSUSP ) ) |
USB_HPRT_PRTRES;
}
else
{
USB->HPRT &= ~( HPRT_WC_MASK | USB_HPRT_PRTSUSP | USB_HPRT_PRTRES );
}
}
__STATIC_INLINE void USBHHAL_PortSuspend( void )
{
USB->HPRT = ( USB->HPRT & ~HPRT_WC_MASK ) | USB_HPRT_PRTSUSP;
}
__STATIC_INLINE void USBHHAL_VbusOn( bool on )
{
if ( on )
{
USB->HPRT = ( USB->HPRT & ~HPRT_WC_MASK ) | USB_HPRT_PRTPWR;
DEBUG_USB_INT_LO_PUTCHAR( '/' );
}
else
{
USB->HPRT &= ~( HPRT_WC_MASK | USB_HPRT_PRTPWR );
DEBUG_USB_INT_LO_PUTCHAR( '\\' );
}
}
#endif /* defined( USB_HOST ) */
/** @endcond */
#ifdef __cplusplus
}
#endif
#endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
#endif /* __EM_USBHAL_H */

View File

@ -0,0 +1,237 @@
/***************************************************************************//**
* @file
* @brief USB protocol stack library, internal type definitions.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#ifndef __EM_USBTYPES_H
#define __EM_USBTYPES_H
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_DEVICE ) || defined( USB_HOST )
#ifdef __cplusplus
extern "C" {
#endif
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/* Limits imposed by the USB peripheral */
#define NP_RX_QUE_DEPTH 8
#define HP_RX_QUE_DEPTH 8
#define MAX_XFER_LEN 524287L /* 2^19 - 1 bytes */
#define MAX_PACKETS_PR_XFER 1023 /* 2^10 - 1 packets */
#define MAX_NUM_TX_FIFOS 6 /* In addition to EP0 Tx FIFO */
#define MAX_NUM_IN_EPS 6 /* In addition to EP0 */
#define MAX_NUM_OUT_EPS 6 /* In addition to EP0 */
/* Limit imposed by the USB standard */
#define MAX_USB_EP_NUM 15
#if defined( USB_DEVICE )
/* Check power saving modes. */
#ifndef USB_PWRSAVE_MODE
/* Default powersave-mode is OFF. */
#define USB_PWRSAVE_MODE USB_PWRSAVE_MODE_OFF
#else
#if ( USB_PWRSAVE_MODE & \
~( USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ONVBUSOFF | \
USB_PWRSAVE_MODE_ENTEREM2 ) )
#error "Illegal USB powersave mode."
#endif
#endif /* ifndef USB_PWRSAVE_MODE */
/* Check power saving low frequency clock selection. */
#ifndef USB_USBC_32kHz_CLK
/* Default clock source is LFXO. */
#define USB_USBC_32kHz_CLK USB_USBC_32kHz_CLK_LFXO
#else
#if ( ( USB_USBC_32kHz_CLK != USB_USBC_32kHz_CLK_LFXO ) && \
( USB_USBC_32kHz_CLK != USB_USBC_32kHz_CLK_LFRCO ) )
#error "Illegal USB 32kHz powersave clock selection."
#endif
#endif /* ifndef USB_USBC_32kHz_CLK */
#endif /* defined( USB_DEVICE ) */
#if defined( USB_HOST )
/* Check VBUS overcurrent definitions. */
#ifndef USB_VBUSOVRCUR_PORT
#define USB_VBUSOVRCUR_PORT gpioPortE
#define USB_VBUSOVRCUR_PIN 2
#define USB_VBUSOVRCUR_POLARITY USB_VBUSOVRCUR_POLARITY_LOW
#endif
#endif
/* Developer mode debugging macro's */
#if defined( DEBUG_USB_INT_LO )
#define DEBUG_USB_INT_LO_PUTS( s ) USB_PUTS( s )
#define DEBUG_USB_INT_LO_PUTCHAR( c ) USB_PUTCHAR( c )
#else
#define DEBUG_USB_INT_LO_PUTS( s )
#define DEBUG_USB_INT_LO_PUTCHAR( c )
#endif /* defined( DEBUG_USB_INT_LO ) */
#if defined( DEBUG_USB_INT_HI )
#define DEBUG_USB_INT_HI_PUTS( s ) USB_PUTS( s )
#define DEBUG_USB_INT_HI_PUTCHAR( c ) USB_PUTCHAR( c )
#else
#define DEBUG_USB_INT_HI_PUTS( s )
#define DEBUG_USB_INT_HI_PUTCHAR( c )
#endif /* defined( DEBUG_USB_INT_HI ) */
#if defined( USB_HOST )
#if defined( NUM_APP_TIMERS )
#define HOSTPORT_TIMER_INDEX (NUM_APP_TIMERS)
#else
#define HOSTPORT_TIMER_INDEX (0)
#endif
#define HOSTCH_TIMER_INDEX (HOSTPORT_TIMER_INDEX + 1 )
#endif
/* Macros for selecting a hardware timer. */
#define USB_TIMER0 0
#define USB_TIMER1 1
#define USB_TIMER2 2
#define USB_TIMER3 3
#if defined( USB_HOST )
#define HCS_NAK 0x01
#define HCS_STALL 0x02
#define HCS_XACT 0x04
#define HCS_TGLERR 0x08
#define HCS_BABBLE 0x10
#define HCS_TIMEOUT 0x20
#define HCS_COMPLETED 0x40
#define HCS_RETRY 0x80
#endif
#if defined( USB_DEVICE )
typedef enum
{
D_EP_IDLE = 0,
D_EP_TRANSMITTING = 1,
D_EP_RECEIVING = 2,
D_EP_STATUS = 3
} USBD_EpState_TypeDef;
typedef struct
{
bool in;
uint8_t zlp;
uint8_t num;
uint8_t addr;
uint8_t type;
uint8_t txFifoNum;
uint8_t *buf;
uint16_t packetSize;
uint16_t mask;
uint32_t remaining;
uint32_t xferred;
#if !defined( USB_SLAVEMODE )
uint32_t hwXferSize;
#endif
uint32_t fifoSize;
USBD_EpState_TypeDef state;
USB_XferCompleteCb_TypeDef xferCompleteCb;
} USBD_Ep_TypeDef;
typedef struct
{
USB_Setup_TypeDef *setup;
#if !defined( USB_SLAVEMODE )
USB_Setup_TypeDef setupPkt[3];
#else
USB_Setup_TypeDef setupPkt[1];
#endif
uint8_t configurationValue; /* Must be DWORD aligned */
bool remoteWakeupEnabled;
uint8_t numberOfStrings;
USBD_State_TypeDef state;
USBD_State_TypeDef savedState;
USBD_State_TypeDef lastState;
const USB_DeviceDescriptor_TypeDef *deviceDescriptor;
const USB_ConfigurationDescriptor_TypeDef *configDescriptor;
const void * const *stringDescriptors;
const USBD_Callbacks_TypeDef *callbacks;
USBD_Ep_TypeDef ep[ NUM_EP_USED + 1 ];
uint8_t inEpAddr2EpIndex[ MAX_USB_EP_NUM + 1 ];
uint8_t outEpAddr2EpIndex[ MAX_USB_EP_NUM + 1 ];
} USBD_Device_TypeDef;
#endif /* defined( USB_DEVICE ) */
#if defined( USB_HOST )
typedef enum
{
H_PORT_DISCONNECTED = 0,
H_PORT_CONNECTED_DEBOUNCING = 1,
H_PORT_CONNECTED_RESETTING = 2,
H_PORT_CONNECTED = 3,
H_PORT_OVERCURRENT = 4
} USBH_PortState_TypeDef;
typedef struct
{
int debounceTime;
int resetTime;
} USBH_AttachTiming_TypeDef;
typedef struct
{
uint8_t *buf;
int errorCnt;
uint32_t remaining;
uint32_t xferred;
#if defined( USB_SLAVEMODE )
uint32_t pending;
#else
uint32_t hwXferSize;
#endif
uint8_t status;
bool idle;
#if defined( USB_SLAVEMODE )
bool txNpFempIntOn;
bool txPFempIntOn;
#endif
USBH_Ep_TypeDef *ep;
} USBH_Hc_TypeDef;
#endif /* defined( USB_HOST ) */
/** @endcond */
#ifdef __cplusplus
}
#endif
#endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
#endif /* __EM_USBTYPES_H */

1315
libs/emusb/src/em_usbd.c Normal file

File diff suppressed because it is too large Load Diff

532
libs/emusb/src/em_usbdch9.c Normal file
View File

@ -0,0 +1,532 @@
/**************************************************************************//**
* @file
* @brief USB protocol stack library, USB chapter 9 command handler.
* @author Energy Micro AS
* @version 3.20.0
******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_DEVICE )
#include "em_usbtypes.h"
#include "em_usbhal.h"
#include "em_usbd.h"
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
static USB_Status_TypeDef ClearFeature ( USBD_Device_TypeDef *pDev );
static USB_Status_TypeDef GetConfiguration ( USBD_Device_TypeDef *pDev );
static USB_Status_TypeDef GetDescriptor ( USBD_Device_TypeDef *pDev );
static USB_Status_TypeDef GetInterface ( USBD_Device_TypeDef *pDev );
static USB_Status_TypeDef GetStatus ( USBD_Device_TypeDef *pDev );
static USB_Status_TypeDef SetAddress ( USBD_Device_TypeDef *pDev );
static USB_Status_TypeDef SetConfiguration ( USBD_Device_TypeDef *pDev );
static USB_Status_TypeDef SetFeature ( USBD_Device_TypeDef *pDev );
static USB_Status_TypeDef SetInterface ( USBD_Device_TypeDef *pDev );
static uint32_t txBuf;
int USBDCH9_SetupCmd( USBD_Device_TypeDef *device )
{
int status;
USB_Setup_TypeDef *p = device->setup;
/* Vendor unique, Class or Standard setup commands override ? */
if ( device->callbacks->setupCmd )
{
status = device->callbacks->setupCmd( p );
if ( status != USB_STATUS_REQ_UNHANDLED )
{
return status;
}
}
status = USB_STATUS_REQ_ERR;
if ( p->Type == USB_SETUP_TYPE_STANDARD )
{
switch ( p->bRequest )
{
case GET_STATUS:
status = GetStatus( device );
break;
case CLEAR_FEATURE:
status = ClearFeature( device );
break;
case SET_FEATURE:
status = SetFeature( device );
break;
case SET_ADDRESS:
status = SetAddress( device );
break;
case GET_DESCRIPTOR:
status = GetDescriptor( device );
break;
case GET_CONFIGURATION:
status = GetConfiguration( device );
break;
case SET_CONFIGURATION:
status = SetConfiguration( device );
break;
case GET_INTERFACE:
status = GetInterface( device );
break;
case SET_INTERFACE:
status = SetInterface( device );
break;
case SYNCH_FRAME: /* Synch frame is for isochronous endpoints */
case SET_DESCRIPTOR: /* Set descriptor is optional */
default:
break;
}
}
return status;
}
static USB_Status_TypeDef ClearFeature( USBD_Device_TypeDef *pDev )
{
USBD_Ep_TypeDef *ep;
USB_Setup_TypeDef *p = pDev->setup;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( p->wLength != 0 )
{
return USB_STATUS_REQ_ERR;
}
switch ( p->Recipient )
{
case USB_SETUP_RECIPIENT_DEVICE:
if ( ( p->wIndex == 0 ) && /* ITF no. 0 */
( p->wValue == USB_FEATURE_DEVICE_REMOTE_WAKEUP ) &&
( ( pDev->state == USBD_STATE_ADDRESSED ) ||
( pDev->state == USBD_STATE_CONFIGURED ) ) )
{
/* Remote wakeup feature clear */
if ( pDev->configDescriptor->bmAttributes & CONFIG_DESC_BM_REMOTEWAKEUP )
{
/* The device is capable of signalling remote wakeup */
pDev->remoteWakeupEnabled = false;
retVal = USB_STATUS_OK;
}
}
break;
case USB_SETUP_RECIPIENT_ENDPOINT:
ep = USBD_GetEpFromAddr( p->wIndex & 0xFF );
if ( ep )
{
if ( ( ep->num > 0 ) &&
( p->wValue == USB_FEATURE_ENDPOINT_HALT ) &&
( pDev->state == USBD_STATE_CONFIGURED ) )
{
retVal = USBDHAL_UnStallEp( ep );
}
}
}
return retVal;
}
static USB_Status_TypeDef GetConfiguration( USBD_Device_TypeDef *pDev )
{
USB_Setup_TypeDef *p = pDev->setup;
uint8_t *pConfigValue = (uint8_t*)&txBuf;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( ( p->wIndex != 0 ) ||
( p->wLength != 1 ) ||
( p->wValue != 0 ) ||
( p->Direction != USB_SETUP_DIR_IN ) ||
( p->Recipient != USB_SETUP_RECIPIENT_DEVICE ) )
{
return USB_STATUS_REQ_ERR;
}
if ( pDev->state == USBD_STATE_ADDRESSED )
{
*pConfigValue = 0;
USBD_Write( 0, pConfigValue, 1, NULL );
retVal = USB_STATUS_OK;
}
else if ( pDev->state == USBD_STATE_CONFIGURED )
{
USBD_Write( 0, &pDev->configurationValue, 1, NULL );
retVal = USB_STATUS_OK;
}
return retVal;
}
static USB_Status_TypeDef GetDescriptor( USBD_Device_TypeDef *pDev )
{
int index;
uint16_t length = 0;
const void *data = NULL;
USB_Setup_TypeDef *p = pDev->setup;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( ( p->Recipient != USB_SETUP_RECIPIENT_DEVICE ) ||
( p->Direction != USB_SETUP_DIR_IN ) )
{
return USB_STATUS_REQ_ERR;
}
index = p->wValue & 0xFF;
switch ( p->wValue >> 8 )
{
case USB_DEVICE_DESCRIPTOR :
if ( index != 0 )
{
break;
}
data = pDev->deviceDescriptor;
length = pDev->deviceDescriptor->bLength;
break;
case USB_CONFIG_DESCRIPTOR :
if ( index != 0 )
{
break;
}
data = pDev->configDescriptor;
length = pDev->configDescriptor->wTotalLength;
break;
case USB_STRING_DESCRIPTOR :
if ( index < pDev->numberOfStrings )
{
USB_StringDescriptor_TypeDef *s;
s = ((USB_StringDescriptor_TypeDef**)pDev->stringDescriptors)[index];
data = s;
length = s->len;
}
break;
}
if ( length )
{
USBD_Write( 0, (void*)data, EFM32_MIN( length, p->wLength ), NULL );
retVal = USB_STATUS_OK;
}
return retVal;
}
static USB_Status_TypeDef GetInterface( USBD_Device_TypeDef *pDev )
{
USB_Setup_TypeDef *p = pDev->setup;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
uint8_t *pAlternateSetting = (uint8_t*)&txBuf;
/* There is currently support for one interface and no alternate settings */
if ( ( p->wIndex != 0 ) ||
( p->wLength != 1 ) ||
( p->wValue != 0 ) ||
( p->Direction != USB_SETUP_DIR_IN ) ||
( p->Recipient != USB_SETUP_RECIPIENT_INTERFACE ) )
{
return USB_STATUS_REQ_ERR;
}
if ( pDev->state == USBD_STATE_CONFIGURED )
{
*pAlternateSetting = 0;
USBD_Write( 0, pAlternateSetting, 1, NULL );
retVal = USB_STATUS_OK;
}
return retVal;
}
static USB_Status_TypeDef GetStatus( USBD_Device_TypeDef *pDev )
{
USBD_Ep_TypeDef *ep;
USB_Setup_TypeDef *p = pDev->setup;
uint16_t *pStatus = (uint16_t*)&txBuf;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( ( p->wValue != 0 ) ||
( p->wLength != 2 ) ||
( p->Direction != USB_SETUP_DIR_IN ) )
{
return USB_STATUS_REQ_ERR;
}
switch ( p->Recipient )
{
case USB_SETUP_RECIPIENT_DEVICE:
if ( ( p->wIndex == 0 ) &&
( ( pDev->state == USBD_STATE_ADDRESSED ) ||
( pDev->state == USBD_STATE_CONFIGURED ) ) )
{
*pStatus = 0;
/* Remote wakeup feature status */
if ( pDev->remoteWakeupEnabled )
*pStatus |= REMOTE_WAKEUP_ENABLED;
/* Current self/bus power status */
if ( pDev->callbacks->isSelfPowered != NULL )
{
if ( pDev->callbacks->isSelfPowered() )
{
*pStatus |= DEVICE_IS_SELFPOWERED;
}
}
else
{
if ( pDev->configDescriptor->bmAttributes & CONFIG_DESC_BM_SELFPOWERED )
{
*pStatus |= DEVICE_IS_SELFPOWERED;
}
}
USBD_Write( 0, pStatus, 2, NULL );
retVal = USB_STATUS_OK;
}
break;
case USB_SETUP_RECIPIENT_INTERFACE:
if ( ( p->wIndex == 0 ) &&
( ( pDev->state == USBD_STATE_ADDRESSED ) ||
( pDev->state == USBD_STATE_CONFIGURED ) ) )
{
*pStatus = 0;
USBD_Write( 0, pStatus, 2, NULL );
retVal = USB_STATUS_OK;
}
break;
case USB_SETUP_RECIPIENT_ENDPOINT:
ep = USBD_GetEpFromAddr( p->wIndex & 0xFF );
if ( ep )
{
if ( ( ep->num > 0 ) &&
( pDev->state == USBD_STATE_CONFIGURED ) )
{
/* Send 2 bytes w/halt status for endpoint */
retVal = USBDHAL_GetStallStatusEp( ep, pStatus );
if ( retVal == USB_STATUS_OK )
{
USBD_Write( 0, pStatus, 2, NULL );
}
}
}
break;
}
return retVal;
}
static USB_Status_TypeDef SetAddress( USBD_Device_TypeDef *pDev )
{
USB_Setup_TypeDef *p = pDev->setup;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( ( p->wIndex != 0 ) ||
( p->wLength != 0 ) ||
( p->wValue > 127 ) ||
( p->Recipient != USB_SETUP_RECIPIENT_DEVICE ) )
{
return USB_STATUS_REQ_ERR;
}
if ( pDev->state == USBD_STATE_DEFAULT )
{
if ( p->wValue != 0 )
{
USBD_SetUsbState( USBD_STATE_ADDRESSED );
}
USBDHAL_SetAddr( p->wValue );
retVal = USB_STATUS_OK;
}
else if ( pDev->state == USBD_STATE_ADDRESSED )
{
if ( p->wValue == 0 )
{
USBD_SetUsbState( USBD_STATE_DEFAULT );
}
USBDHAL_SetAddr( p->wValue );
retVal = USB_STATUS_OK;
}
return retVal;
}
static USB_Status_TypeDef SetConfiguration( USBD_Device_TypeDef *pDev )
{
USB_Setup_TypeDef *p = pDev->setup;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( ( p->wIndex != 0 ) ||
( p->wLength != 0 ) ||
( (p->wValue>>8) != 0 ) ||
( p->Recipient != USB_SETUP_RECIPIENT_DEVICE ) )
{
return USB_STATUS_REQ_ERR;
}
if ( pDev->state == USBD_STATE_ADDRESSED )
{
if ( ( p->wValue == 0 ) ||
( p->wValue == pDev->configDescriptor->bConfigurationValue ) )
{
pDev->configurationValue = p->wValue;
if ( p->wValue == pDev->configDescriptor->bConfigurationValue )
{
USBD_ActivateAllEps( true );
USBD_SetUsbState( USBD_STATE_CONFIGURED );
}
retVal = USB_STATUS_OK;
}
}
else if ( pDev->state == USBD_STATE_CONFIGURED )
{
if ( ( p->wValue == 0 ) ||
( p->wValue == pDev->configDescriptor->bConfigurationValue ) )
{
pDev->configurationValue = p->wValue;
if ( p->wValue == 0 )
{
USBD_SetUsbState( USBD_STATE_ADDRESSED );
USBD_DeactivateAllEps( USB_STATUS_DEVICE_UNCONFIGURED );
}
else
{
/* Reenable device endpoints, will reset data toggles */
USBD_ActivateAllEps( false );
}
retVal = USB_STATUS_OK;
}
}
return retVal;
}
static USB_Status_TypeDef SetFeature( USBD_Device_TypeDef *pDev )
{
USBD_Ep_TypeDef *ep;
USB_XferCompleteCb_TypeDef callback;
USB_Setup_TypeDef *p = pDev->setup;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
if ( p->wLength != 0 )
{
return USB_STATUS_REQ_ERR;
}
switch ( p->Recipient )
{
case USB_SETUP_RECIPIENT_DEVICE:
if ( ( p->wIndex == 0 ) && /* ITF no. 0 */
( p->wValue == USB_FEATURE_DEVICE_REMOTE_WAKEUP ) &&
( pDev->state == USBD_STATE_CONFIGURED ) )
{
/* Remote wakeup feature set */
if ( pDev->configDescriptor->bmAttributes & CONFIG_DESC_BM_REMOTEWAKEUP )
{
/* The device is capable of signalling remote wakeup */
pDev->remoteWakeupEnabled = true;
retVal = USB_STATUS_OK;
}
}
break;
case USB_SETUP_RECIPIENT_ENDPOINT:
ep = USBD_GetEpFromAddr( p->wIndex & 0xFF );
if ( ep )
{
if ( ( ep->num > 0 ) &&
( p->wValue == USB_FEATURE_ENDPOINT_HALT ) &&
( pDev->state == USBD_STATE_CONFIGURED ) )
{
retVal = USBDHAL_StallEp( ep );
ep->state = D_EP_IDLE;
/* Call end of transfer callback for endpoint */
if ( ( retVal == USB_STATUS_OK ) &&
( ep->state != D_EP_IDLE ) &&
( ep->xferCompleteCb ) )
{
callback = ep->xferCompleteCb;
ep->xferCompleteCb = NULL;
DEBUG_USB_API_PUTS( "\nEP cb(), EP stalled" );
callback( USB_STATUS_EP_STALLED, ep->xferred, ep->remaining);
}
}
}
}
return retVal;
}
static USB_Status_TypeDef SetInterface( USBD_Device_TypeDef *pDev )
{
USB_Setup_TypeDef *p = pDev->setup;
USB_Status_TypeDef retVal = USB_STATUS_REQ_ERR;
/* There is currently support for one interface and no alternate settings */
if ( ( p->wIndex == 0 ) &&
( p->wLength == 0 ) &&
( p->wValue == 0 ) &&
( pDev->state == USBD_STATE_CONFIGURED ) &&
( p->Recipient == USB_SETUP_RECIPIENT_INTERFACE ) )
{
/* Reset data toggles on EP's */
USBD_ActivateAllEps( false );
return USB_STATUS_OK;
}
return retVal;
}
/** @endcond */
#endif /* defined( USB_DEVICE ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */

237
libs/emusb/src/em_usbdep.c Normal file
View File

@ -0,0 +1,237 @@
/**************************************************************************//**
* @file
* @brief USB protocol stack library, USB device endpoint handlers.
* @author Energy Micro AS
* @version 3.20.0
******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_DEVICE )
#include "em_usbtypes.h"
#include "em_usbhal.h"
#include "em_usbd.h"
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/*
* USBDEP_Ep0Handler() is called each time a packet has been transmitted
* or recieved on the default endpoint.
* A state machine navigate us through the phases of a control transfer
* according to "chapter 9" in the USB spec.
*/
void USBDEP_Ep0Handler( USBD_Device_TypeDef *device )
{
int status;
USBD_Ep_TypeDef *ep;
static bool statusIn;
static uint32_t xferred;
static USB_XferCompleteCb_TypeDef callback;
ep = &device->ep[ 0 ];
switch ( ep->state )
{
case D_EP_IDLE:
ep->remaining = 0;
ep->zlp = 0;
callback = NULL;
statusIn = false;
status = USBDCH9_SetupCmd( device );
if ( status == USB_STATUS_REQ_ERR )
{
ep->in = true;
USBDHAL_StallEp( ep ); /* Stall Ep0 IN */
ep->in = false; /* OUT for next SETUP */
USBDHAL_StallEp( ep ); /* Stall Ep0 OUT */
USBDHAL_ReenableEp0Setup( device ); /* Prepare for next SETUP packet*/
}
else /* ( Status == USB_STATUS_OK ) */
{
if ( (ep->state == D_EP_RECEIVING) || (ep->state == D_EP_TRANSMITTING) )
{
callback = ep->xferCompleteCb;
}
if ( ep->state != D_EP_RECEIVING )
{
if ( ep->remaining )
{
/* Data will be sent to host, check if a ZLP must be appended */
if ( ( ep->remaining < device->setup->wLength ) &&
( ep->remaining % ep->packetSize == 0 ) )
{
ep->zlp = 1;
}
}
else
{
/* Prepare for next SETUP packet*/
USBDHAL_ReenableEp0Setup( device );
/* No data stage, a ZLP may have been sent. If not, send one */
xferred = 0;
if ( ep->zlp == 0 )
{
USBD_Write( 0, NULL, 0, NULL ); /* ACK to host */
ep->state = D_EP_STATUS;
}
else
{
ep->state = D_EP_IDLE;
ep->in = false; /* OUT for next SETUP */
}
}
}
}
break;
case D_EP_RECEIVING:
if ( ep->remaining )
{
/* There is more data to receive */
USBD_ReArmEp0( ep );
}
else
{
status = USB_STATUS_OK;
if ( callback != NULL )
{
status = callback( USB_STATUS_OK, ep->xferred, 0 );
callback = NULL;
}
if ( status != USB_STATUS_OK )
{
ep->in = true;
USBDHAL_StallEp( ep ); /* Stall Ep0 IN */
ep->in = false; /* OUT for next SETUP */
USBDHAL_StallEp( ep ); /* Stall Ep0 OUT */
USBDHAL_ReenableEp0Setup( device ); /* Prepare for next SETUP pkt. */
ep->state = D_EP_IDLE;
}
else /* Everything OK, send a ZLP (ACK) to host */
{
USBDHAL_ReenableEp0Setup( device );/* Prepare for next SETUP packet*/
ep->state = D_EP_IDLE; /* USBD_Write() sets state back*/
/* to EP_TRANSMITTING */
USBD_Write( 0, NULL, 0, NULL );
ep->state = D_EP_STATUS;
}
}
break;
case D_EP_TRANSMITTING:
if ( ep->remaining )
{
/* There is more data to transmit */
USBD_ReArmEp0( ep );
}
else
{
/* All data transferred, is a ZLP packet needed ? */
if ( ep->zlp == 1 )
{
xferred = ep->xferred;
ep->state = D_EP_IDLE; /* USBD_Write() sets state back */
/* to EP_TRANSMITTING */
USBD_Write( 0, NULL, 0, NULL ); /* Send ZLP */
ep->zlp = 2;
}
else
{
if ( ep->zlp == 0 )
{
xferred = ep->xferred;
}
ep->state = D_EP_IDLE;
USBD_Read( 0, NULL, 0, NULL ); /* Get ZLP packet (ACK) from host */
statusIn = true;
ep->state = D_EP_STATUS;
}
}
break;
case D_EP_STATUS:
if ( statusIn )
{
USBDHAL_ReenableEp0Setup( device );
}
if ( callback != NULL )
{
callback( USB_STATUS_OK, xferred, 0 );
}
ep->state = D_EP_IDLE;
ep->in = false; /* OUT for next SETUP */
break;
default:
EFM_ASSERT( false );
break;
}
}
/*
* USBDEP_EpHandler() is called each time a packet has been transmitted
* or recieved on an endpoint other than the default endpoint.
*/
void USBDEP_EpHandler( uint8_t epAddr )
{
USB_XferCompleteCb_TypeDef callback;
USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr );
if ( ( ep->state == D_EP_TRANSMITTING ) || ( ep->state == D_EP_RECEIVING ) )
{
ep->state = D_EP_IDLE;
if ( ep->xferCompleteCb )
{
callback = ep->xferCompleteCb;
ep->xferCompleteCb = NULL;
callback( USB_STATUS_OK, ep->xferred, ep->remaining );
}
}
else
{
EFM_ASSERT( false );
}
}
/** @endcond */
#endif /* defined( USB_DEVICE ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */

752
libs/emusb/src/em_usbdint.c Normal file
View File

@ -0,0 +1,752 @@
/**************************************************************************//**
* @file
* @brief USB protocol stack library, USB device peripheral interrupt handlers.
* @author Energy Micro AS
* @version 3.20.0
******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_DEVICE )
#include "em_cmu.h"
#include "em_usbtypes.h"
#include "em_usbhal.h"
#include "em_usbd.h"
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#define HANDLE_INT( x ) if ( status & x ) { Handle_##x(); status &= ~x; }
static void Handle_USB_GINTSTS_ENUMDONE ( void );
static void Handle_USB_GINTSTS_IEPINT ( void );
static void Handle_USB_GINTSTS_OEPINT ( void );
static void Handle_USB_GINTSTS_RESETDET ( void );
#if defined( USB_SLAVEMODE )
static void Handle_USB_GINTSTS_RXFLVL ( void );
#endif
static void Handle_USB_GINTSTS_SOF ( void );
static void Handle_USB_GINTSTS_USBRST ( void );
static void Handle_USB_GINTSTS_USBSUSP ( void );
static void Handle_USB_GINTSTS_WKUPINT ( void );
#if ( USB_PWRSAVE_MODE )
/* Variables and prototypes for USB powerdown (suspend) functionality. */
volatile bool USBD_poweredDown = false;
static bool UsbPowerDown( void );
static bool UsbPowerUp( void );
/* Storage for backing up USB core registers. */
static uint32_t x_USB_GINTMSK;
static uint32_t x_USB_GOTGCTL;
static uint32_t x_USB_GAHBCFG;
static uint32_t x_USB_GUSBCFG;
static uint32_t x_USB_GRXFSIZ;
static uint32_t x_USB_GNPTXFSIZ;
static uint32_t x_USB_DCFG;
static uint32_t x_USB_DCTL;
static uint32_t x_USB_DAINTMSK;
static uint32_t x_USB_DIEPMSK;
static uint32_t x_USB_DOEPMSK;
static uint32_t x_USB_PCGCCTL;
#if ( NUM_EP_USED > 0 )
static uint32_t x_USB_EP_CTL[ NUM_EP_USED ];
static uint32_t x_USB_EP_TSIZ[ NUM_EP_USED ];
static uint32_t x_USB_EP_DMAADDR[ NUM_EP_USED ];
#endif
#if ( NUM_EP_USED > MAX_NUM_TX_FIFOS )
#define FIFO_CNT MAX_NUM_TX_FIFOS
#else
#define FIFO_CNT NUM_EP_USED
#endif
#if ( FIFO_CNT > 0 )
static uint32_t x_USB_DIEPTXFS[ FIFO_CNT ];
#endif
#endif /* if ( USB_PWRSAVE_MODE ) */
/*
* USB_IRQHandler() is the first level handler for the USB peripheral interrupt.
*/
void USB_IRQHandler( void )
{
uint32_t status;
#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF )
bool servedVbusInterrupt = false;
#endif
INT_Disable();
#if ( USB_PWRSAVE_MODE )
if ( USBD_poweredDown )
{
/* Switch USBC clock from 32kHz to HFCLK to be able to read USB */
/* peripheral registers. */
/* If we woke up from EM2, HFCLK is now HFRCO. */
CMU_OscillatorEnable( cmuOsc_HFXO, true, false); /* Prepare HFXO. */
CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV;
while ( !( CMU->STATUS & CMU_STATUS_USBCHFCLKSEL ) ){}
}
#endif /* if ( USB_PWRSAVE_MODE ) */
#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF )
if ( USB->IF )
{
if ( USB->IF & USB_IF_VREGOSH )
{
USB->IFC = USB_IFC_VREGOSH;
if ( USB->STATUS & USB_STATUS_VREGOS )
{
servedVbusInterrupt = true;
DEBUG_USB_INT_LO_PUTS( "\nVboN" );
if ( UsbPowerUp() )
USBDHAL_EnableUsbResetInt();
USBD_SetUsbState( USBD_STATE_POWERED );
}
}
if ( USB->IF & USB_IF_VREGOSL )
{
USB->IFC = USB_IFC_VREGOSL;
if ( !( USB->STATUS & USB_STATUS_VREGOS ) )
{
servedVbusInterrupt = true;
DEBUG_USB_INT_LO_PUTS( "\nVboF" );
USB->GINTMSK = 0;
USB->GINTSTS = 0xFFFFFFFF;
UsbPowerDown();
USBD_SetUsbState( USBD_STATE_NONE );
}
}
}
#endif
status = USBHAL_GetCoreInts();
if ( status == 0 )
{
INT_Enable();
#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF )
if ( !servedVbusInterrupt )
{
DEBUG_USB_INT_LO_PUTS( "\nSinT" );
}
#else
DEBUG_USB_INT_LO_PUTS( "\nSinT" );
#endif
return;
}
HANDLE_INT( USB_GINTSTS_RESETDET )
HANDLE_INT( USB_GINTSTS_WKUPINT )
HANDLE_INT( USB_GINTSTS_USBSUSP )
HANDLE_INT( USB_GINTSTS_SOF )
#if defined( USB_SLAVEMODE )
HANDLE_INT( USB_GINTSTS_RXFLVL )
#endif
HANDLE_INT( USB_GINTSTS_ENUMDONE )
HANDLE_INT( USB_GINTSTS_USBRST )
HANDLE_INT( USB_GINTSTS_IEPINT )
HANDLE_INT( USB_GINTSTS_OEPINT )
INT_Enable();
if ( status != 0 )
{
DEBUG_USB_INT_LO_PUTS( "\nUinT" );
}
}
/*
* Handle port enumeration interrupt. This has nothing to do with normal
* device enumeration.
*/
static void Handle_USB_GINTSTS_ENUMDONE( void )
{
#if ( USB_PWRSAVE_MODE )
UsbPowerUp();
#endif /* if ( USB_PWRSAVE_MODE ) */
USBDHAL_Ep0Activate();
dev->ep[ 0 ].state = D_EP_IDLE;
USBDHAL_EnableInts( dev );
DEBUG_USB_INT_LO_PUTS( "EnumD" );
}
/*
* Handle IN endpoint transfer interrupt.
*/
static void Handle_USB_GINTSTS_IEPINT( void )
{
int epnum;
uint16_t epint;
uint16_t epmask;
uint32_t status;
USBD_Ep_TypeDef *ep;
DEBUG_USB_INT_HI_PUTCHAR( 'i' );
epint = USBDHAL_GetAllInEpInts();
for ( epnum = 0, epmask = 1;
epnum <= MAX_NUM_IN_EPS;
epnum++, epmask <<= 1 )
{
if ( epint & epmask )
{
ep = USBD_GetEpFromAddr( USB_SETUP_DIR_MASK | epnum );
status = USBDHAL_GetInEpInts( ep );
if ( status & USB_DIEP_INT_XFERCOMPL )
{
#if defined( USB_SLAVEMODE )
/* Disable Tx FIFO empty interrupt */
USB->DIEPEMPMSK &= ~epmask;
USB_DINEPS[ epnum ].INT = USB_DIEP_INT_TXFEMP;
status &= ~USB_DIEP_INT_TXFEMP;
#endif
USB_DINEPS[ epnum ].INT = USB_DIEP_INT_XFERCOMPL;
DEBUG_USB_INT_HI_PUTCHAR( 'c' );
if ( epnum == 0 )
{
#if !defined( USB_SLAVEMODE )
if ( ep->remaining > ep->packetSize )
{
ep->remaining -= ep->packetSize;
ep->xferred += ep->packetSize;
}
else
{
ep->xferred += ep->remaining;
ep->remaining = 0;
}
#endif
USBDEP_Ep0Handler( dev );
}
else
{
#if !defined( USB_SLAVEMODE )
ep->xferred = ep->remaining -
( ( USB_DINEPS[ epnum ].TSIZ &
_USB_DIEP_TSIZ_XFERSIZE_MASK ) >>
_USB_DIEP_TSIZ_XFERSIZE_SHIFT );
ep->remaining -= ep->xferred;
#endif
USBDEP_EpHandler( ep->addr );
}
}
#if defined( USB_SLAVEMODE )
if ( status & USB_DIEP_INT_TXFEMP )
{
USB_DINEPS[ epnum ].INT = USB_DIEP_INT_TXFEMP;
if ( ep->state != D_EP_IDLE )
{
DEBUG_USB_INT_HI_PUTCHAR( 'f' );
USBDHAL_FillFifo( ep );
}
}
#endif
}
}
}
/*
* Handle OUT endpoint transfer interrupt.
*/
static void Handle_USB_GINTSTS_OEPINT( void )
{
int epnum;
uint16_t epint;
uint16_t epmask;
uint32_t status;
USBD_Ep_TypeDef *ep;
DEBUG_USB_INT_HI_PUTCHAR( 'o' );
epint = USBDHAL_GetAllOutEpInts();
for ( epnum = 0, epmask = 1;
epnum <= MAX_NUM_OUT_EPS;
epnum++, epmask <<= 1 )
{
if ( epint & epmask )
{
ep = USBD_GetEpFromAddr( epnum );
status = USBDHAL_GetOutEpInts( ep );
if ( status & USB_DOEP_INT_XFERCOMPL )
{
USB_DOUTEPS[ epnum ].INT = USB_DOEP_INT_XFERCOMPL;
DEBUG_USB_INT_HI_PUTCHAR( 'c' );
if ( epnum == 0 )
{
#if !defined( USB_SLAVEMODE )
if ( ep->remaining > ep->packetSize )
{
ep->remaining -= ep->packetSize;
ep->xferred += ep->packetSize;
}
else
{
ep->xferred += ep->remaining;
ep->remaining = 0;
}
#endif
USBDEP_Ep0Handler( dev );
}
else
{
#if !defined( USB_SLAVEMODE )
ep->xferred = ep->hwXferSize -
( ( USB_DOUTEPS[ epnum ].TSIZ & _USB_DOEP_TSIZ_XFERSIZE_MASK ) >>
_USB_DOEP_TSIZ_XFERSIZE_SHIFT );
ep->remaining -= ep->xferred;
#endif
USBDEP_EpHandler( ep->addr );
}
}
/* Setup Phase Done */
if ( status & USB_DOEP_INT_SETUP )
{
DEBUG_USB_INT_LO_PUTS( "\nSP" );
#if !defined( USB_SLAVEMODE )
if ( USB->DOEP0INT & USB_DOEP_INT_BACK2BACKSETUP )
{ /* Back to back setup packets received */
USB->DOEP0INT = USB_DOEP_INT_BACK2BACKSETUP;
DEBUG_USB_INT_LO_PUTS( "B2B" );
dev->setup = (USB_Setup_TypeDef*)
( USB->DOEP0DMAADDR - USB_SETUP_PKT_SIZE );
}
else
{
/* Read SETUP packet counter from hw. */
int supCnt = ( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_SUPCNT_MASK )
>> _USB_DOEP0TSIZ_SUPCNT_SHIFT;
if ( supCnt == 3 )
supCnt = 2;
dev->setup = &dev->setupPkt[ 2 - supCnt ];
}
#endif
USB->DOEP0TSIZ |= 3 << _USB_DOEP0TSIZ_SUPCNT_SHIFT;
USB->DOEP0DMAADDR = (uint32_t)dev->setupPkt;
USB->DOEP0INT = USB_DOEP_INT_SETUP;
USBDEP_Ep0Handler( dev ); /* Call the SETUP process for the EP0 */
}
}
}
}
/*
* Handle USB reset detectet interrupt in suspend mode.
*/
static void Handle_USB_GINTSTS_RESETDET ( void )
{
USB->GINTSTS = USB_GINTSTS_RESETDET;
#if ( USB_PWRSAVE_MODE )
UsbPowerUp();
#endif /* if ( USB_PWRSAVE_MODE ) */
USBD_SetUsbState( USBD_STATE_DEFAULT );
DEBUG_USB_INT_LO_PUTS( "RsuP\n" );
}
#if defined( USB_SLAVEMODE )
/*
* Handle receive FIFO full interrupt.
*/
static void Handle_USB_GINTSTS_RXFLVL( void )
{
USBD_Ep_TypeDef *ep;
uint32_t status, byteCount, count, residue;
DEBUG_USB_INT_HI_PUTCHAR( 'q' );
status = USB->GRXSTSP; /* Get status from top of FIFO */
ep = USBD_GetEpFromAddr( status & _USB_GRXSTSP_CHEPNUM_MASK );
switch ( status & _USB_GRXSTSP_PKTSTS_MASK )
{
case GRXSTSP_PKTSTS_DEVICE_DATAOUTRECEIVED:
byteCount = (status & _USB_GRXSTSP_BCNT_MASK) >> _USB_GRXSTSP_BCNT_SHIFT;
if ( byteCount )
{
DEBUG_USB_INT_HI_PUTCHAR( 'e' );
if ( ep->state != D_EP_IDLE )
{
/* Check for possible buffer overflow */
if ( byteCount > ep->remaining )
{
residue = byteCount - ep->remaining;
}
else
{
residue = 0;
}
count = EFM32_MIN( byteCount, ep->remaining );
USBHAL_ReadFifo( ep->buf, count );
ep->xferred += count;
ep->remaining -= count;
ep->buf += count;
if ( residue )
{
USBHAL_FlushFifo( residue );
}
}
}
break;
case GRXSTSP_PKTSTS_DEVICE_SETUPRECEIVED:
USBHAL_ReadFifo( (uint8_t*)dev->setup, USB_SETUP_PKT_SIZE );
break;
}
}
#endif
/*
* Handle Start Of Frame (SOF) interrupt.
*/
static void Handle_USB_GINTSTS_SOF( void )
{
USB->GINTSTS = USB_GINTSTS_SOF;
if ( dev->callbacks->sofInt )
{
dev->callbacks->sofInt(
( USB->DSTS & _USB_DSTS_SOFFN_MASK ) >> _USB_DSTS_SOFFN_SHIFT );
}
}
/*
* Handle USB port reset interrupt.
*/
static void Handle_USB_GINTSTS_USBRST( void )
{
int i;
DEBUG_USB_INT_LO_PUTS( "ReseT" );
/* Clear Remote Wakeup Signalling */
USB->DCTL &= ~( DCTL_WO_BITMASK | USB_DCTL_RMTWKUPSIG );
USBHAL_FlushTxFifo( 0 );
/* Clear pending interrupts */
for ( i = 0; i <= MAX_NUM_IN_EPS; i++ )
{
USB_DINEPS[ i ].INT = 0xFFFFFFFF;
}
for ( i = 0; i <= MAX_NUM_OUT_EPS; i++ )
{
USB_DOUTEPS[ i ].INT = 0xFFFFFFFF;
}
USB->DAINTMSK = USB_DAINTMSK_INEPMSK0 | USB_DAINTMSK_OUTEPMSK0;
USB->DOEPMSK = USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK;
USB->DIEPMSK = USB_DIEPMSK_XFERCOMPLMSK;
/* Reset Device Address */
USB->DCFG &= ~_USB_DCFG_DEVADDR_MASK;
/* Setup EP0 to receive SETUP packets */
USBDHAL_StartEp0Setup( dev );
USBDHAL_EnableInts( dev );
if ( dev->callbacks->usbReset )
{
dev->callbacks->usbReset();
}
USBD_SetUsbState( USBD_STATE_DEFAULT );
USBDHAL_AbortAllTransfers( USB_STATUS_DEVICE_RESET );
}
/*
* Handle USB port suspend interrupt.
*/
static void Handle_USB_GINTSTS_USBSUSP( void )
{
USBD_State_TypeDef state;
USB->GINTSTS = USB_GINTSTS_USBSUSP;
USBDHAL_AbortAllTransfers( USB_STATUS_DEVICE_SUSPENDED );
DEBUG_USB_INT_LO_PUTS( "\nSusP" );
state = USBD_GetUsbState();
if ( ( state == USBD_STATE_POWERED ) ||
( state == USBD_STATE_DEFAULT ) ||
( state == USBD_STATE_ADDRESSED ) ||
( state == USBD_STATE_CONFIGURED ) )
{
#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONSUSPEND )
UsbPowerDown();
#endif /* if ( USB_PWRSAVE_MODE ) */
USBD_SetUsbState( USBD_STATE_SUSPENDED );
}
}
/*
* Handle USB port wakeup interrupt.
*/
static void Handle_USB_GINTSTS_WKUPINT( void )
{
USB->GINTSTS = USB_GINTSTS_WKUPINT;
#if ( USB_PWRSAVE_MODE )
if ( UsbPowerUp() )
{
USBDHAL_StartEp0Setup( dev );
USBDHAL_Ep0Activate();
}
#endif /* if ( USB_PWRSAVE_MODE ) */
USBD_SetUsbState( dev->savedState );
DEBUG_USB_INT_LO_PUTS( "WkuP\n" );
}
#if ( USB_PWRSAVE_MODE )
/*
* Backup essential USB core registers, and set the core in partial powerdown
* mode. Optionally prepare entry into EM2.
*/
static bool UsbPowerDown( void )
{
#if ( NUM_EP_USED > 0 ) || ( FIFO_CNT > 0 )
int i;
#endif
#if ( NUM_EP_USED > 0 )
int epNum;
USBD_Ep_TypeDef *ep;
#endif
if ( !USBD_poweredDown )
{
USBD_poweredDown = true;
/* Backup USB core registers. */
x_USB_GINTMSK = USB->GINTMSK;
x_USB_GOTGCTL = USB->GOTGCTL;
x_USB_GAHBCFG = USB->GAHBCFG;
x_USB_GUSBCFG = USB->GUSBCFG;
x_USB_GRXFSIZ = USB->GRXFSIZ;
x_USB_GNPTXFSIZ = USB->GNPTXFSIZ;
x_USB_DCFG = USB->DCFG;
x_USB_DCTL = USB->DCTL;
x_USB_DAINTMSK = USB->DAINTMSK;
x_USB_DIEPMSK = USB->DIEPMSK;
x_USB_DOEPMSK = USB->DOEPMSK;
x_USB_PCGCCTL = USB->PCGCCTL;
#if ( NUM_EP_USED > 0 )
for ( i = 0; i < NUM_EP_USED; i++ )
{
ep = &dev->ep[ i+1 ];
epNum = ep->num;
if ( ep->in )
{
x_USB_EP_CTL[ i ] = USB_DINEPS[ epNum ].CTL;
x_USB_EP_TSIZ[ i ] = USB_DINEPS[ epNum ].TSIZ;
x_USB_EP_DMAADDR[ i ] = USB_DINEPS[ epNum ].DMAADDR;
}
else
{
x_USB_EP_CTL[ i ] = USB_DOUTEPS[ epNum ].CTL;
x_USB_EP_TSIZ[ i ] = USB_DOUTEPS[ epNum ].TSIZ;
x_USB_EP_DMAADDR[ i ] = USB_DOUTEPS[ epNum ].DMAADDR;
}
}
#endif
#if ( FIFO_CNT > 0 )
for ( i = 0; i < FIFO_CNT; i++ )
{
x_USB_DIEPTXFS[ i ] = USB_DIEPTXFS[ i ];
}
#endif
/* Prepare for wakeup on resume and reset. */
USB->DCFG = (USB->DCFG & ~_USB_DCFG_RESVALID_MASK) |
(4 << _USB_DCFG_RESVALID_SHIFT);
USB->DCFG |= USB_DCFG_ENA32KHZSUSP;
USB->GINTMSK = USB_GINTMSK_RESETDETMSK | USB_GINTMSK_WKUPINTMSK;
/* Enter partial powerdown mode. */
USB->PCGCCTL |= USB_PCGCCTL_PWRCLMP;
USB->PCGCCTL |= USB_PCGCCTL_RSTPDWNMODULE;
USB->PCGCCTL |= USB_PCGCCTL_STOPPCLK;
#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ENTEREM2 )
/* Enter EM2 on interrupt exit. */
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk;
#endif
/* Switch USBC clock to 32 kHz. */
#if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO )
CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO;
#else
CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO;
#endif
while ( CMU->STATUS & CMU_STATUS_USBCHFCLKSEL ){}
return true;
}
return false;
}
#endif /* if ( USB_PWRSAVE_MODE ) */
#if ( USB_PWRSAVE_MODE )
/*
* Exit USB core partial powerdown mode, restore essential USB core registers.
* Optionally prevent re-entry back to EM2.
*/
static bool UsbPowerUp( void )
{
#if ( NUM_EP_USED > 0 ) || ( FIFO_CNT > 0 )
int i;
#endif
#if ( NUM_EP_USED > 0 )
int epNum;
uint32_t tmp;
USBD_Ep_TypeDef *ep;
#endif
#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF )
if ( USBD_poweredDown && ( USB->STATUS & USB_STATUS_VREGOS ) )
#else
if ( USBD_poweredDown )
#endif
{
USBD_poweredDown = false;
/* Switch CPU core clock to 48MHz */
CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO );
/* Switch USBC clock to HFCLK */
CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_HFCLK );
/* Exit partial powerdown mode. */
USB->PCGCCTL &= ~USB_PCGCCTL_STOPPCLK;
USB->PCGCCTL &= ~(USB_PCGCCTL_PWRCLMP | USB_PCGCCTL_RSTPDWNMODULE);
/* Restore USB core registers. */
USB->GUSBCFG = x_USB_GUSBCFG;
USB->DCFG = x_USB_DCFG;
#if ( FIFO_CNT > 0 )
for ( i = 0; i < FIFO_CNT; i++ )
{
USB_DIEPTXFS[ i ] = x_USB_DIEPTXFS[ i ];
}
#endif
#if ( NUM_EP_USED > 0 )
for ( i = 0; i < NUM_EP_USED; i++ )
{
ep = &dev->ep[ i+1 ];
epNum = ep->num;
if ( ep->in )
{
tmp = ( ep->packetSize << _USB_DIEP_CTL_MPS_SHIFT ) |
( ep->type << _USB_DIEP_CTL_EPTYPE_SHIFT ) |
( ep->txFifoNum << _USB_DIEP_CTL_TXFNUM_SHIFT ) |
USB_DIEP_CTL_USBACTEP |
USB_DIEP_CTL_SNAK;
if ( x_USB_EP_CTL[ i ] & USB_DIEP_CTL_DPIDEOF )
tmp |= USB_DIEP_CTL_SETD1PIDOF;
else
tmp |= USB_DIEP_CTL_SETD0PIDEF;
USB_DINEPS[ epNum ].CTL = tmp;
USB_DINEPS[ epNum ].TSIZ = x_USB_EP_TSIZ[ i ];
USB_DINEPS[ epNum ].DMAADDR = x_USB_EP_DMAADDR[ i ];
}
else
{
if ( x_USB_EP_CTL[ i ] & USB_DOEP_CTL_DPIDEOF )
USB_DOUTEPS[ epNum ].CTL = x_USB_EP_CTL[ i ] | USB_DOEP_CTL_SETD1PIDOF;
else
USB_DOUTEPS[ epNum ].CTL = x_USB_EP_CTL[ i ] | USB_DOEP_CTL_SETD0PIDEF;
USB_DOUTEPS[ epNum ].TSIZ = x_USB_EP_TSIZ[ i ];
USB_DOUTEPS[ epNum ].DMAADDR = x_USB_EP_DMAADDR[ i ];
}
}
#endif
USB->PCGCCTL = x_USB_PCGCCTL;
USB->DOEPMSK = x_USB_DOEPMSK;
USB->DIEPMSK = x_USB_DIEPMSK;
USB->DAINTMSK = x_USB_DAINTMSK;
USB->DCTL = x_USB_DCTL;
USB->GNPTXFSIZ = x_USB_GNPTXFSIZ;
USB->GRXFSIZ = x_USB_GRXFSIZ;
USB->GAHBCFG = x_USB_GAHBCFG;
USB->GOTGCTL = x_USB_GOTGCTL;
USB->GINTMSK = x_USB_GINTMSK;
USB->DCTL |= USB_DCTL_PWRONPRGDONE;
#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ENTEREM2 )
/* Do not reenter EM2 on interrupt exit. */
SCB->SCR &= ~(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk);
#endif
return true;
}
return false;
}
#endif /* if ( USB_PWRSAVE_MODE ) */
/** @endcond */
#endif /* defined( USB_DEVICE ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */

2801
libs/emusb/src/em_usbh.c Normal file

File diff suppressed because it is too large Load Diff

814
libs/emusb/src/em_usbhal.c Normal file
View File

@ -0,0 +1,814 @@
/**************************************************************************//**
* @file
* @brief USB protocol stack library, low level USB peripheral access.
* @author Energy Micro AS
* @version 3.20.0
******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_DEVICE ) || defined( USB_HOST )
#include "em_usbtypes.h"
#include "em_usbhal.h"
#if defined( USB_DEVICE )
#include "em_usbd.h"
#endif
#if defined( USB_HOST )
#include "em_usbh.h"
#endif
#include "em_cmu.h"
#include "em_gpio.h"
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/* NOTE: The sequence of error message strings must agree with the */
/* definition of USB_Status_TypeDef enum. */
static const char * const errMsg[] =
{
[ USB_STATUS_OK ] = "No errors",
[ -USB_STATUS_REQ_ERR ] = "Setup request error",
[ -USB_STATUS_EP_BUSY ] = "Endpoint is busy",
[ -USB_STATUS_REQ_UNHANDLED ] = "Setup request not handled",
[ -USB_STATUS_ILLEGAL ] = "Illegal operation attempted",
[ -USB_STATUS_EP_STALLED ] = "Endpoint is stalled",
[ -USB_STATUS_EP_ABORTED ] = "Transfer aborted",
[ -USB_STATUS_EP_ERROR ] = "Transfer error",
[ -USB_STATUS_EP_NAK ] = "Endpoint NAK",
[ -USB_STATUS_DEVICE_UNCONFIGURED ] = "Device is not configured",
[ -USB_STATUS_DEVICE_SUSPENDED ] = "Device is suspended",
[ -USB_STATUS_DEVICE_RESET ] = "Device has been reset",
[ -USB_STATUS_TIMEOUT ] = "Transfer timeout",
[ -USB_STATUS_DEVICE_REMOVED ] = "Device removed",
[ -USB_STATUS_HC_BUSY ] = "Host channel is busy",
[ -USB_STATUS_DEVICE_MALFUNCTION ] = "Device malfunction",
[ -USB_STATUS_PORT_OVERCURRENT ] = "VBUS overcurrent",
};
/** @endcond */
/***************************************************************************//**
* @brief
* Return an error message string for a given error code.
*
* @param[in] error
* Error code, see \ref USB_Status_TypeDef.
*
* @return
* Error message string pointer.
******************************************************************************/
char *USB_GetErrorMsgString( int error )
{
if ( error >= 0 )
return (char*)errMsg[ 0 ];
return (char*)errMsg[ -error ];
}
#if defined( USB_USE_PRINTF )
/***************************************************************************//**
* @brief
* Format and print a text string given an error code, prepends an optional user
* supplied leader string.
*
* @param[in] pre
* Optional leader string to prepend to error message string.
*
* @param[in] error
* Error code, see \ref USB_Status_TypeDef.
******************************************************************************/
void USB_PrintErrorMsgString( char *pre, int error )
{
if ( pre )
{
USB_PRINTF( "%s", pre );
}
if ( error > USB_STATUS_OK )
{
USB_PRINTF( "%d", error );
}
else
{
USB_PRINTF( "%s", USB_GetErrorMsgString( error ) );
}
}
#endif /* defined( USB_USE_PRINTF ) */
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#if defined( DEBUG_EFM_USER )
static void PrintI( int i )
{
#if !defined ( USER_PUTCHAR )
(void)i;
#else
if ( i >= 10 )
{
PrintI( i / 10 );
}
DEBUG_USB_API_PUTCHAR( ( i % 10 ) + '0' );
#endif
}
void assertEFM( const char *file, int line )
{
#if !defined ( USER_PUTCHAR )
(void)file;
#endif
DEBUG_USB_API_PUTS( "\nASSERT " );
DEBUG_USB_API_PUTS( file );
DEBUG_USB_API_PUTCHAR( ' ' );
PrintI( line );
for(;;){}
}
#endif /* defined( DEBUG_EFM_USER ) */
#if defined ( USER_PUTCHAR )
void USB_Puts( const char *p )
{
while( *p )
USB_PUTCHAR( *p++ );
}
#endif /* defined ( USER_PUTCHAR ) */
void USBHAL_CoreReset( void )
{
USB->PCGCCTL &= ~USB_PCGCCTL_STOPPCLK;
USB->PCGCCTL &= ~(USB_PCGCCTL_PWRCLMP | USB_PCGCCTL_RSTPDWNMODULE);
/* Wait for AHB master IDLE state. */
while ( !( USB->GRSTCTL & USB_GRSTCTL_AHBIDLE ) ) {}
/* Core Soft Reset */
USB->GRSTCTL |= USB_GRSTCTL_CSFTRST;
while ( USB->GRSTCTL & USB_GRSTCTL_CSFTRST ) {}
}
#ifdef USB_DEVICE
void USBDHAL_Connect( void )
{
USB->DCTL &= ~( DCTL_WO_BITMASK | USB_DCTL_SFTDISCON );
}
USB_Status_TypeDef USBDHAL_CoreInit( uint32_t totalRxFifoSize,
uint32_t totalTxFifoSize )
{
uint8_t i, j;
uint16_t start, depth;
USBD_Ep_TypeDef *ep;
CMU_ClockEnable( cmuClock_GPIO, true );
GPIO_PinModeSet( gpioPortF, 5, gpioModePushPull, 0 ); /* Enable VBUSEN pin */
USB->ROUTE = USB_ROUTE_PHYPEN | USB_ROUTE_VBUSENPEN; /* Init PHY */
USBHAL_CoreReset(); /* Reset USB core */
/* Force Device Mode */
USB->GUSBCFG = ( USB->GUSBCFG &
~(GUSBCFG_WO_BITMASK | USB_GUSBCFG_FORCEHSTMODE ) ) |
USB_GUSBCFG_FORCEDEVMODE;
INT_Enable();
USBTIMER_DelayMs( 50 );
INT_Disable();
/* Set device speed */
USB->DCFG = ( USB->DCFG & ~_USB_DCFG_DEVSPD_MASK ) | 3; /* Full speed PHY */
/* Stall on non-zero len status OUT packets (ctrl transfers). */
USB->DCFG |= USB_DCFG_NZSTSOUTHSHK;
/* Set periodic frame interval to 80% */
USB->DCFG &= ~_USB_DCFG_PERFRINT_MASK;
#if !defined( USB_SLAVEMODE )
USB->GAHBCFG = ( USB->GAHBCFG & ~_USB_GAHBCFG_HBSTLEN_MASK ) |
USB_GAHBCFG_DMAEN | USB_GAHBCFG_HBSTLEN_INCR;
#endif
/* Set Rx FIFO size */
USB->GRXFSIZ = ( totalRxFifoSize << _USB_GRXFSIZ_RXFDEP_SHIFT ) &
_USB_GRXFSIZ_RXFDEP_MASK;
start = totalRxFifoSize; /* Set Tx EP0 FIFO size */
depth = dev->ep[ 0 ].fifoSize;
USB->GNPTXFSIZ = ( ( depth << _USB_GNPTXFSIZ_NPTXFINEPTXF0DEP_SHIFT ) &
_USB_GNPTXFSIZ_NPTXFINEPTXF0DEP_MASK ) |
( ( start << _USB_GNPTXFSIZ_NPTXFSTADDR_SHIFT ) &
_USB_GNPTXFSIZ_NPTXFSTADDR_MASK );
/* Set Tx EP FIFO sizes for all IN ep's */
for ( j = 1; j <= MAX_NUM_TX_FIFOS; j++ )
{
for ( i = 1; i <= MAX_NUM_IN_EPS; i++ )
{
ep = USBD_GetEpFromAddr( USB_SETUP_DIR_MASK | i );
if ( ep ) /* Is EP in use ? */
{
if ( ep->txFifoNum == j ) /* Is it correct FIFO number ? */
{
start += depth;
depth = ep->fifoSize;
USB_DIEPTXFS[ ep->txFifoNum - 1 ] =
( depth << _USB_DIEPTXF1_INEPNTXFDEP_SHIFT ) |
( start & _USB_DIEPTXF1_INEPNTXFSTADDR_MASK );
}
}
}
}
if ( totalRxFifoSize + totalTxFifoSize > MAX_FIFO_SIZE_INWORDS )
return USB_STATUS_ILLEGAL;
/* Flush the FIFO's */
USBHAL_FlushTxFifo( 0x10 ); /* All Tx FIFO's */
USBHAL_FlushRxFifo(); /* The Rx FIFO */
/* Disable all device interrupts */
USB->DIEPMSK = 0;
USB->DOEPMSK = 0;
USB->DAINTMSK = 0;
USB->DIEPEMPMSK = 0;
/* Disable all EP's, clear all EP ints. */
for ( i = 0; i <= MAX_NUM_IN_EPS; i++ )
{
USB_DINEPS[ i ].CTL = 0;
USB_DINEPS[ i ].TSIZ = 0;
USB_DINEPS[ i ].INT = 0xFFFFFFFF;
}
for ( i = 0; i <= MAX_NUM_OUT_EPS; i++ )
{
USB_DOUTEPS[ i ].CTL = 0;
USB_DOUTEPS[ i ].TSIZ = 0;
USB_DOUTEPS[ i ].INT = 0xFFFFFFFF;
}
#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF )
/* Enable VREGO sense. */
USB->CTRL |= USB_CTRL_VREGOSEN;
USB->IFC = USB_IFC_VREGOSH | USB_IFC_VREGOSL;
USB->IEN = USB_IFC_VREGOSH | USB_IFC_VREGOSL;
/* Force a VREGO interrupt. */
if ( USB->STATUS & USB_STATUS_VREGOS)
USB->IFS = USB_IFS_VREGOSH;
else
USB->IFS = USB_IFS_VREGOSL;
#endif
return USB_STATUS_OK;
}
void USBDHAL_Disconnect( void )
{
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_SFTDISCON;
}
void USBDHAL_AbortEpIn( USBD_Ep_TypeDef *ep )
{
/* Clear epdis & inepnakeff INT's */
USB_DINEPS[ ep->num ].INT |= USB_DIEP_INT_EPDISBLD |
USB_DIEP_INT_INEPNAKEFF;
/* Enable epdis & inepnakeff INT's */
USB->DIEPMSK |= USB_DIEPMSK_EPDISBLDMSK | USB_DIEPMSK_INEPNAKEFFMSK;
USB_DINEPS[ ep->num ].CTL = ( USB_DINEPS[ ep->num ].CTL &
~DEPCTL_WO_BITMASK ) |
USB_DIEP_CTL_SNAK;
/* Wait for inepnakeff INT */
while ( !( USBDHAL_GetInEpInts( ep ) & USB_DIEP_INT_INEPNAKEFF ) ) {}
USB_DINEPS[ ep->num ].INT = USB_DIEP_INT_INEPNAKEFF;
USB->DIEPMSK &= ~USB_DIEPMSK_INEPNAKEFFMSK;
DEBUG_USB_INT_LO_PUTCHAR( '.' );
USBDHAL_SetEPDISNAK( ep );
/* Wait for epdis INT */
while ( !( USBDHAL_GetInEpInts( ep ) & USB_DIEP_INT_EPDISBLD ) ) {}
USB_DINEPS[ ep->num ].INT = USB_DIEP_INT_EPDISBLD;
USB->DIEPMSK &= ~USB_DIEPMSK_EPDISBLDMSK;
USBHAL_FlushTxFifo( ep->txFifoNum );
/* Clear any interrupts generated by the abort sequence. */
NVIC_ClearPendingIRQ( USB_IRQn );
DEBUG_USB_INT_LO_PUTCHAR( '.' );
}
void USBDHAL_AbortEpOut( USBD_Ep_TypeDef *ep )
{
/* Clear epdis INT's */
USB_DOUTEPS[ ep->num ].INT |= USB_DOEP_INT_EPDISBLD;
/* Clear Global OUT NAK if already set */
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_CGOUTNAK;
USB->GINTMSK |= USB_GINTMSK_GOUTNAKEFFMSK; /* Enable GOUTNAKEFF int */
/* Set Global OUT NAK */
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_SGOUTNAK;
/* Wait for goutnakeff */
while ( !( USB->GINTSTS & USB_GINTSTS_GOUTNAKEFF ) )
{
#if defined( USB_SLAVEMODE )
/* Check if Rx FIFO filled with global OUT NAK status */
if ( USB->GINTSTS & USB_GINTSTS_RXFLVL )
{
USB->GRXSTSP; /* Pop status from top of FIFO */
}
#endif
}
USB->GINTMSK &= ~USB_GINTMSK_GOUTNAKEFFMSK; /* Disable GOUTNAKEFF int */
USB->DOEPMSK |= USB_DOEPMSK_EPDISBLDMSK; /* Enable EPDIS interrupt */
DEBUG_USB_INT_LO_PUTCHAR( ',' );
USBDHAL_SetEPDISNAK( ep ); /* Disable ep */
/* Wait for epdis INT */
while ( !( USBDHAL_GetOutEpInts( ep ) & USB_DOEP_INT_EPDISBLD ) ) {}
USB_DOUTEPS[ ep->num ].INT = USB_DOEP_INT_EPDISBLD;
USB->DOEPMSK &= ~USB_DOEPMSK_EPDISBLDMSK; /* Disable EPDIS interrupt */
/* Clear Global OUT NAK */
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_CGOUTNAK;
/* Clear any interrupts generated by the abort sequence. */
NVIC_ClearPendingIRQ( USB_IRQn );
DEBUG_USB_INT_LO_PUTCHAR( ',' );
}
void USBDHAL_AbortAllEps( void )
{
int i;
USBD_Ep_TypeDef *ep;
uint16_t im, om, inmask=0, outmask=0;
/* Clear epdis & inepnakeff INT's */
for ( i = 1; i <= NUM_EP_USED; i++ )
{
ep = &dev->ep[i];
if ( ep->state != D_EP_IDLE )
{
if ( ep->in )
{
inmask |= ep->mask;
USB_DINEPS[ ep->num ].INT |= USB_DIEP_INT_EPDISBLD |
USB_DIEP_INT_INEPNAKEFF;
}
else
{
outmask |= ep->mask;
USB_DOUTEPS[ ep->num ].INT |= USB_DOEP_INT_EPDISBLD;
}
}
}
if ( inmask )
{
/* Enable epdis & inepnakeff INT's */
USB->DIEPMSK |= USB_DIEPMSK_EPDISBLDMSK | USB_DIEPMSK_INEPNAKEFFMSK;
/* Set NAK on all IN ep's */
im = inmask;
for ( i = 1; i <= NUM_EP_USED; i++ )
{
ep = &dev->ep[i];
if ( im & ep->mask )
{
USB_DINEPS[ ep->num ].CTL = ( USB_DINEPS[ ep->num ].CTL &
~DEPCTL_WO_BITMASK ) |
USB_DIEP_CTL_SNAK;
}
}
}
if ( outmask )
{
/* Clear Global OUT NAK if already set */
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_CGOUTNAK;
USB->GINTMSK |= USB_GINTMSK_GOUTNAKEFFMSK; /* Enable GOUTNAKEFF int */
/* Set Global OUT NAK */
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_SGOUTNAK;
/* Wait for goutnakeff */
while ( !( USB->GINTSTS & USB_GINTSTS_GOUTNAKEFF ) )
{
#if defined( USB_SLAVEMODE )
/* Check if Rx FIFO filled with global OUT NAK status */
if ( USB->GINTSTS & USB_GINTSTS_RXFLVL )
{
USB->GRXSTSP; /* Pop status from top of FIFO */
}
#endif
}
USB->GINTMSK &= ~USB_GINTMSK_GOUTNAKEFFMSK; /* Disable GOUTNAKEFF int */
USB->DOEPMSK |= USB_DOEPMSK_EPDISBLDMSK; /* Enable EPDIS interrupt */
}
if ( inmask )
{
/* Wait for inepnakeff INT on all IN ep's */
im = inmask;
do
{
for ( i = 1; i <= NUM_EP_USED; i++ )
{
ep = &dev->ep[i];
if ( im & ep->mask )
{
if ( USBDHAL_GetInEpInts( ep ) & USB_DIEP_INT_INEPNAKEFF )
{
USB_DINEPS[ ep->num ].INT = USB_DIEP_INT_INEPNAKEFF;
im &= ~ep->mask;
}
}
}
} while ( im );
USB->DIEPMSK &= ~USB_DIEPMSK_INEPNAKEFFMSK;
}
DEBUG_USB_INT_LO_PUTCHAR( '\'' );
/* Disable ep's */
for ( i = 1; i <= NUM_EP_USED; i++ )
{
ep = &dev->ep[i];
if ( ep->state != D_EP_IDLE )
{
USBDHAL_SetEPDISNAK( ep );
}
}
/* Wait for epdis INT */
im = inmask;
om = outmask;
do
{
for ( i = 1; i <= NUM_EP_USED; i++ )
{
ep = &dev->ep[i];
if ( ep->in && ( im & ep->mask ) )
{
if ( USBDHAL_GetInEpInts( ep ) & USB_DIEP_INT_EPDISBLD )
{
USB_DINEPS[ ep->num ].INT = USB_DIEP_INT_EPDISBLD;
im &= ~ep->mask;
}
}
if ( !ep->in && ( om & ep->mask ) )
{
if ( USBDHAL_GetOutEpInts( ep ) & USB_DOEP_INT_EPDISBLD )
{
USB_DOUTEPS[ ep->num ].INT = USB_DOEP_INT_EPDISBLD;
om &= ~ep->mask;
}
}
}
} while ( im || om );
if ( inmask )
{
USB->DIEPMSK &= ~USB_DIEPMSK_EPDISBLDMSK; /* Disable EPDIS interrupt */
USBHAL_FlushTxFifo( 0x10 ); /* Flush all Tx FIFO's */
}
if ( outmask )
{
USB->DOEPMSK &= ~USB_DOEPMSK_EPDISBLDMSK; /* Disable EPDIS interrupt */
/* Clear Global OUT NAK */
USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_CGOUTNAK;
}
DEBUG_USB_INT_LO_PUTCHAR( '\'' );
}
void USBDHAL_AbortAllTransfers( USB_Status_TypeDef reason )
{
int i;
USBD_Ep_TypeDef *ep;
USB_XferCompleteCb_TypeDef callback;
USBDHAL_AbortAllEps();
for ( i = 1; i <= NUM_EP_USED; i++ )
{
ep = &(dev->ep[i]);
if ( ep->state != D_EP_IDLE )
{
ep->state = D_EP_IDLE;
if ( ep->xferCompleteCb )
{
callback = ep->xferCompleteCb;
ep->xferCompleteCb = NULL;
if ( ( dev->lastState == USBD_STATE_CONFIGURED ) &&
( dev->state == USBD_STATE_ADDRESSED ) )
{
USBDHAL_DeactivateEp( ep );
}
DEBUG_TRACE_ABORT( reason );
callback( reason, ep->xferred, ep->remaining );
}
}
}
/* Clear any interrupts generated by the abort sequence. */
NVIC_ClearPendingIRQ( USB_IRQn );
}
#endif /* defined( USB_DEVICE ) */
#if defined( USB_HOST )
USB_Status_TypeDef USBHHAL_CoreInit( uint32_t rxFifoSize,
uint32_t nptxFifoSize,
uint32_t ptxFifoSize )
{
uint8_t i;
rxFifoSize /= 4; /* Convert from byte count to word count. */
nptxFifoSize /= 4;
ptxFifoSize /= 4;
CMU_ClockEnable( cmuClock_GPIO, true );
GPIO_PinModeSet( gpioPortF, 5, gpioModePushPull, 0 ); /* Enable VBUSEN pin */
#if ( USB_VBUSOVRCUR_PORT != USB_VBUSOVRCUR_PORT_NONE )
/* Enable VBUS overcurrent flag pin. */
GPIO_PinModeSet( USB_VBUSOVRCUR_PORT, USB_VBUSOVRCUR_PIN, gpioModeInput, 0 );
#endif
USB->ROUTE = USB_ROUTE_PHYPEN | USB_ROUTE_VBUSENPEN; /* Init PHY */
USBHAL_CoreReset(); /* Reset USB core */
/* Force Host Mode */
USB->GUSBCFG = ( USB->GUSBCFG &
~(GUSBCFG_WO_BITMASK | USB_GUSBCFG_FORCEDEVMODE ) ) |
USB_GUSBCFG_FORCEHSTMODE;
INT_Enable();
USBTIMER_DelayMs( 100 );
INT_Disable();
/* Set 48 MHz PHY clock, FS/LS mode */
USB->HCFG = ( USB->HCFG & ~_USB_HCFG_FSLSPCLKSEL_MASK ) |
( 1 << _USB_HCFG_FSLSPCLKSEL_SHIFT ) |
( USB_HCFG_FSLSSUPP );
#if !defined( USB_SLAVEMODE )
USB->GAHBCFG = ( USB->GAHBCFG & ~_USB_GAHBCFG_HBSTLEN_MASK ) |
USB_GAHBCFG_DMAEN | USB_GAHBCFG_HBSTLEN_INCR;
#endif
/* Set Rx FIFO size */
USB->GRXFSIZ = ( rxFifoSize << _USB_GRXFSIZ_RXFDEP_SHIFT ) &
_USB_GRXFSIZ_RXFDEP_MASK;
/* Set Tx FIFO sizes */
USB->GNPTXFSIZ = ( ( nptxFifoSize <<
_USB_GNPTXFSIZ_NPTXFINEPTXF0DEP_SHIFT ) &
_USB_GNPTXFSIZ_NPTXFINEPTXF0DEP_MASK ) |
( ( rxFifoSize <<
_USB_GNPTXFSIZ_NPTXFSTADDR_SHIFT ) &
_USB_GNPTXFSIZ_NPTXFSTADDR_MASK );
USB->HPTXFSIZ = ( ( ptxFifoSize << _USB_HPTXFSIZ_PTXFSIZE_SHIFT ) &
_USB_HPTXFSIZ_PTXFSIZE_MASK ) |
( ( ( rxFifoSize + nptxFifoSize )
<< _USB_HPTXFSIZ_PTXFSTADDR_SHIFT ) &
_USB_HPTXFSIZ_PTXFSTADDR_MASK );
/* Flush Tx and Rx FIFO's */
USBHAL_FlushTxFifo( 0x10 );
USBHAL_FlushRxFifo();
for ( i = 0; i < MAX_NUM_HOSTCHANNELS; i++ )
{
USB->HC[ i ].CHAR = USB_HC_CHAR_CHDIS; /* Disable channel */
USB->HC[ i ].INT = 0xFFFFFFFF; /* Clear pending interrupts */
}
/* Enable and halt all channels */
for ( i = 0; i < MAX_NUM_HOSTCHANNELS; i++ )
{
USB->HC[ i ].CHAR |= USB_HC_CHAR_CHDIS | USB_HC_CHAR_CHENA;
do
{
#if defined( USB_SLAVEMODE )
USB->GRXSTSP;
__NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
#endif
__NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
}
while ( USB->HC[ i ].CHAR & USB_HC_CHAR_CHENA );
}
/* Disable all interrupts */
for ( i = 0; i < MAX_NUM_HOSTCHANNELS; i++ )
{
USB->HC[ i ].INTMSK = 0;
}
USB->HAINTMSK = 0;
return USB_STATUS_OK;
}
void USBHHAL_HCHalt( int hcnum, uint32_t hcchar, uint32_t eptype )
{
#if !defined( USB_SLAVEMODE )
(void)eptype;
#else
USB->HC[ hcnum ].INTMSK |= USB_HC_INT_CHHLTD;
#endif
hcchar |= USB_HC_CHAR_CHENA | USB_HC_CHAR_CHDIS;
#if defined( USB_SLAVEMODE )
/* Check for space in the request queue to issue the halt. */
if ( eptype == HCCHAR_EPTYPE_CTRL || eptype == HCCHAR_EPTYPE_BULK )
{
if ( ( USB->GNPTXSTS & _USB_GNPTXSTS_NPTXQSPCAVAIL_MASK ) == 0 )
{
hcchar &= ~USB_HC_CHAR_CHENA;
}
}
else
{
if ( ( USB->HPTXSTS & _USB_HPTXSTS_PTXQSPCAVAIL_MASK ) == 0)
{
hcchar &= ~USB_HC_CHAR_CHENA;
}
}
#endif
USB->HC[ hcnum ].CHAR = hcchar;
}
void USBHHAL_HCInit( int hcnum )
{
USBH_Ep_TypeDef *ep;
ep = hcs[ hcnum ].ep;
USB->HC[ hcnum ].INT = 0xFFFFFFFF; /* Clear all interrupt flags */
switch ( ep->type ) /* Enable host channel int. types */
{
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
USB->HC[ hcnum ].INTMSK =
#if !defined( USB_SLAVEMODE )
USB_HC_INT_CHHLTD;
#else
USB_HC_INT_XFERCOMPL |
USB_HC_INT_STALL |
USB_HC_INT_XACTERR |
USB_HC_INT_NAK |
( ep->in ? 0 : USB_HC_INT_ACK ) |
( ep->in ? USB_HC_INT_DATATGLERR : 0 ) |
( ep->in ? USB_HC_INT_BBLERR : 0 );
#endif
break;
case USB_EPTYPE_INTR:
USB->HC[ hcnum ].INTMSK =
#if !defined( USB_SLAVEMODE )
USB_HC_INT_CHHLTD;
#else
USB_HC_INT_XFERCOMPL |
USB_HC_INT_STALL |
USB_HC_INT_XACTERR |
USB_HC_INT_DATATGLERR |
USB_HC_INT_NAK |
( ep->in ? 0 : USB_HC_INT_ACK ) |
USB_HC_INT_FRMOVRUN |
( ep->in ? USB_HC_INT_BBLERR : 0 );
#endif
break;
}
hcs[ hcnum ].errorCnt = 0;
USB->HAINTMSK |= 1 << hcnum; /* Enable host channel interrupt */
USB->HC[ hcnum ].CHAR = /* Program HCCHAR register */
( ep->parentDevice->addr << _USB_HC_CHAR_DEVADDR_SHIFT ) |
( ( ep->addr & USB_EPNUM_MASK ) << _USB_HC_CHAR_EPNUM_SHIFT ) |
( ep->type << _USB_HC_CHAR_EPTYPE_SHIFT ) |
( ep->packetSize << _USB_HC_CHAR_MPS_SHIFT ) |
( ep->in ? USB_HC_CHAR_EPDIR : 0 ) |
( ep->parentDevice->speed ==
HPRT_L_SPEED >> _USB_HPRT_PRTSPD_SHIFT
? USB_HC_CHAR_LSPDDEV : 0 );
}
void USBHHAL_HCStart( int hcnum )
{
USBH_Hc_TypeDef *hc;
uint16_t packets, len;
hc = &hcs[ hcnum ];
hc->status = 0;
hc->idle = false;
#if defined( USB_SLAVEMODE )
hc->pending = 0;
#endif
if ( hc->remaining > 0 )
{
packets = ( hc->remaining + hc->ep->packetSize - 1 ) / hc->ep->packetSize;
}
else
{
packets = 1;
}
if ( hc->ep->in )
{
len = packets * hc->ep->packetSize;
}
else
{
len = hc->remaining;
}
/* Initialize the HCTSIZn register */
#if !defined( USB_SLAVEMODE )
hc->hwXferSize = len;
#endif
USB->HC[ hcnum ].TSIZ =
( ( len << _USB_HC_TSIZ_XFERSIZE_SHIFT ) &
_USB_HC_TSIZ_XFERSIZE_MASK ) |
( ( packets << _USB_HC_TSIZ_PKTCNT_SHIFT ) &
_USB_HC_TSIZ_PKTCNT_MASK ) |
( ( hc->ep->toggle << _USB_HC_TSIZ_PID_SHIFT ) &
_USB_HC_TSIZ_PID_MASK );
USB->HC[ hcnum ].DMAADDR = (uint32_t)hc->buf;
USBHHAL_HCActivate( hcnum,
USB->HC[ hcnum ].CHAR,
hc->ep->type == USB_EPTYPE_INTR );
#if defined( USB_SLAVEMODE )
if ( !hc->ep->in )
{
if ( hc->ep->type == USB_EPTYPE_INTR )
{
USBHHAL_FillFifo( hcnum, USB->HPTXSTS, hc );
}
else
{
USBHHAL_FillFifo( hcnum, USB->GNPTXSTS, hc );
}
}
#endif
}
#endif /* defined( USB_HOST ) */
/** @endcond */
#endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */

212
libs/emusb/src/em_usbhep.c Normal file
View File

@ -0,0 +1,212 @@
/***************************************************************************//**
* @file
* @brief USB protocol stack library, USB host endpoint handlers.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_HOST )
#include "em_usbtypes.h"
#include "em_usbhal.h"
#include "em_usbh.h"
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
/*
* USBHEP_CtrlEpHandler() is called each time a packet has been transmitted
* or recieved on host channels serving a device default endpoint (EP0).
* A state machine navigate us through the phases of a control transfer
* according to "chapter 9" in the USB spec.
*/
void USBHEP_CtrlEpHandler( USBH_Ep_TypeDef *ep, USB_Status_TypeDef result )
{
uint8_t direction;
switch ( ep->state )
{
/* ------- Setup stage ------------------------------------------------ */
case H_EP_SETUP: /* SETUP packet sent successfully */
if ( result == USB_STATUS_OK )
{
direction = ep->setup.bmRequestType & USB_SETUP_DIR_MASK;
/* Check if there is a data stage */
if ( ep->setup.wLength != 0 )
{
if ( direction == USB_SETUP_DIR_D2H ) /* Data direction is IN */
{
USBH_CtlReceiveData( ep, ep->setup.wLength );
ep->state = H_EP_DATA_IN;
break;
}
else /* Data direction is OUT */
{
USBH_CtlSendData( ep, ep->setup.wLength );
ep->state = H_EP_DATA_OUT;
break;
}
}
USBH_CtlReceiveData( ep, 0 ); /* No data stage */
ep->state = H_EP_STATUS_IN; /* Get zero length in status packet */
break;
}
ep->setupErrCnt++;
/*
* After a halt condition is encountered or an error is detected by the
* host, a control endpoint is allowed to recover by accepting the next
* SETUP package. If the next Setup package is not accepted, a device
* reset will ultimately be needed.
*/
if ( ( ep->setupErrCnt == 1 ) && ( ep->timeout != 0 ) )
{
USBH_CtlSendSetup( ep ); /* Retry */
break;
}
USBHEP_TransferDone( ep, result );
break;
/* ------- Data IN stage ---------------------------------------------- */
case H_EP_DATA_IN: /* Data received */
if ( result == USB_STATUS_OK )
{
USBH_CtlSendData( ep, 0 ); /* Send zero length out status packet */
ep->state = H_EP_STATUS_OUT;
break;
}
USBHEP_TransferDone( ep, result );
break;
/* ------- Data OUT stage --------------------------------------------- */
case H_EP_DATA_OUT: /* Data sent */
if ( result == USB_STATUS_OK )
{
USBH_CtlReceiveData( ep, 0 ); /* Get zero length in status packet */
ep->state = H_EP_STATUS_IN;
break;
}
USBHEP_TransferDone( ep, result );
break;
/* ------- Status IN/OUT stage ---------------------------------------- */
case H_EP_STATUS_IN: /* Status packet received */
case H_EP_STATUS_OUT : /* Status packet sent */
USBHEP_TransferDone( ep, result );
break;
case H_EP_IDLE:
break;
}
}
/*
* USBHEP_EpHandler() is called each time a packet has been transmitted
* or recieved on a host channel serving a non-control device endpoint.
*/
void USBHEP_EpHandler( USBH_Ep_TypeDef *ep, USB_Status_TypeDef result )
{
switch ( ep->state )
{
case H_EP_DATA_IN:
case H_EP_DATA_OUT:
USBHEP_TransferDone( ep, result );
break;
default:
case H_EP_IDLE:
break;
}
}
/*
* USBHEP_TransferDone() is called on transfer completion on all types of
* device endpoints.
*/
void USBHEP_TransferDone( USBH_Ep_TypeDef *ep, USB_Status_TypeDef result )
{
int hcnum;
USB_XferCompleteCb_TypeDef callback;
ep->xferCompleted = true;
ep->xferStatus = result;
ep->state = H_EP_IDLE;
if ( ep->type == USB_EPTYPE_CTRL )
{
hcnum = ep->setup.Direction == USB_SETUP_DIR_IN ? ep->hcIn : ep->hcOut;
if ( ep->timeout )
USBTIMER_Stop( ep->hcOut + HOSTCH_TIMER_INDEX );
ep->xferred = hcs[ hcnum ].xferred;
ep->remaining = hcs[ hcnum ].remaining;
hcs[ ep->hcIn ].idle = true;
hcs[ ep->hcOut ].idle = true;
if ( ep->setup.wLength == 0 )
{
ep->xferred = 0;
}
}
else
{
hcnum = ep->in ? ep->hcIn : ep->hcOut;
if ( ep->timeout || ep->type == USB_EPTYPE_INTR )
USBTIMER_Stop( hcnum + HOSTCH_TIMER_INDEX );
hcs[ hcnum ].idle = true;
ep->xferred = hcs[ hcnum ].xferred;
ep->remaining = hcs[ hcnum ].remaining;
}
if ( ep->xferCompleteCb )
{
callback = ep->xferCompleteCb;
ep->xferCompleteCb = NULL;
(callback)( result, ep->xferred, ep->remaining );
}
}
/** @endcond */
#endif /* defined( USB_HOST ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */

899
libs/emusb/src/em_usbhint.c Normal file
View File

@ -0,0 +1,899 @@
/***************************************************************************//**
* @file
* @brief USB protocol stack library, USB host peripheral interrupt handlers.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_HOST )
#include "em_usbtypes.h"
#include "em_usbhal.h"
#include "em_usbh.h"
#if ( USB_VBUSOVRCUR_PORT != USB_VBUSOVRCUR_PORT_NONE )
#include "em_gpio.h"
#endif
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#define HANDLE_INT( x ) if ( status & x ) { Handle_##x(); status &= ~x; }
#define FIFO_TXSTS_HCNUM_MASK 0x78000000
#define FIFO_TXSTS_HCNUM_SHIFT 27
#if defined( USB_SLAVEMODE )
#define TOGGLE_DATA_PID() \
if ( eptype != HCCHAR_EPTYPE_ISOC ) \
{ \
if ( hc->ep->toggle == USB_PID_DATA0 ) \
{ \
hc->ep->toggle = USB_PID_DATA1; \
} \
else if ( hc->ep->toggle == USB_PID_DATA1 ) \
{ \
hc->ep->toggle = USB_PID_DATA0; \
} \
}
#endif
static void Handle_HcInInt( uint8_t hcnum );
static void Handle_HcOutInt( uint8_t hcnum );
static void Handle_USB_GINTSTS_DISCONNINT ( void );
static void Handle_USB_GINTSTS_HCHINT ( void );
static void Handle_USB_GINTSTS_PRTINT ( void );
#if defined( USB_SLAVEMODE )
static void Handle_USB_GINTSTS_NPTXFEMP ( void );
static void Handle_USB_GINTSTS_PTXFEMP ( void );
static void Handle_USB_GINTSTS_RXFLVL ( void );
#endif
/*
* USB_IRQHandler() is the first level handler for the USB peripheral interrupt.
*/
void USB_IRQHandler( void )
{
uint32_t status;
INT_Disable();
status = USBHAL_GetCoreInts();
if ( status == 0 )
{
INT_Enable();
DEBUG_USB_INT_LO_PUTS( "\nSinT" );
return;
}
#if defined( USB_SLAVEMODE )
HANDLE_INT( USB_GINTSTS_RXFLVL )
HANDLE_INT( USB_GINTSTS_NPTXFEMP )
HANDLE_INT( USB_GINTSTS_PTXFEMP )
#endif
HANDLE_INT( USB_GINTSTS_HCHINT )
HANDLE_INT( USB_GINTSTS_PRTINT )
HANDLE_INT( USB_GINTSTS_DISCONNINT )
INT_Enable();
if ( status != 0 )
{
DEBUG_USB_INT_LO_PUTS( "\nUinT" );
}
}
/*
* Handle host channel IN transfer interrupt.
*/
static void Handle_HcInInt( uint8_t hcnum )
{
USBH_Hc_TypeDef *hc;
USB_Status_TypeDef result;
uint32_t status, hcchar, eptype;
#ifdef DEBUG_USB_INT_HI
uint32_t status2;
#endif
hc = &hcs[ hcnum ];
status = USBHHAL_GetHcInts( hcnum );
hcchar = USB->HC[ hcnum ].CHAR;
eptype = hcchar & _USB_HC_CHAR_EPTYPE_MASK;
DEBUG_USB_INT_HI_PUTCHAR( 'i' );
#if !defined( USB_SLAVEMODE )
if ( status & USB_HC_INT_CHHLTD )
{
USB->HC[ hcnum ].INT = 0xFFFFFFFF;
#ifdef DEBUG_USB_INT_HI
status2 = status;
#endif
status &= USB_HC_INT_XFERCOMPL | USB_HC_INT_STALL | USB_HC_INT_XACTERR |
USB_HC_INT_ACK | USB_HC_INT_NAK | USB_HC_INT_DATATGLERR |
USB_HC_INT_BBLERR;
if ( ( status & ( USB_HC_INT_ACK | USB_HC_INT_XFERCOMPL ) ) ==
( USB_HC_INT_ACK | USB_HC_INT_XFERCOMPL ) )
{
DEBUG_USB_INT_HI_PUTCHAR( 'c' );
hc->xferred = hc->hwXferSize -
( ( USB->HC[ hcnum ].TSIZ & _USB_HC_TSIZ_XFERSIZE_MASK ) >>
_USB_HC_TSIZ_XFERSIZE_SHIFT );
hc->remaining -= hc->xferred;
hc->ep->toggle = ( USB->HC[ hcnum ].TSIZ & _USB_HC_TSIZ_PID_MASK ) ==
USB_HC_TSIZ_PID_DATA0 ? USB_PID_DATA0 : USB_PID_DATA1;
result = USB_STATUS_OK;
}
else if ( status & USB_HC_INT_STALL )
{
result = USB_STATUS_EP_STALLED;
DEBUG_USB_INT_LO_PUTS( "StaL" );
}
else if ( status & USB_HC_INT_BBLERR )
{
result = USB_STATUS_EP_ERROR;
DEBUG_USB_INT_LO_PUTS( "BabL" );
}
else if ( ( ( status &
( USB_HC_INT_DATATGLERR | USB_HC_INT_NAK ) )
== USB_HC_INT_DATATGLERR ) &&
( !( hc->status & HCS_TIMEOUT ) ) )
{
/* Toggle error but not nak or timeout */
result = USB_STATUS_EP_ERROR;
DEBUG_USB_INT_LO_PUTS( "TglE" );
hc->errorCnt++;
if ( hc->errorCnt < 3 )
{
USBHHAL_HCStart( hcnum );
return;
}
}
else if ( ( ( status &
( USB_HC_INT_DATATGLERR | USB_HC_INT_NAK |
USB_HC_INT_XACTERR ) )
== USB_HC_INT_XACTERR ) &&
( !( hc->status & HCS_TIMEOUT ) ) )
{
/* Exact error but not toggle err or nak or timeout */
result = USB_STATUS_EP_ERROR;
DEBUG_USB_INT_LO_PUTS( "XacT" );
hc->errorCnt++;
if ( hc->errorCnt < 3 )
{
USBHHAL_HCStart( hcnum );
return;
}
}
else if ( hc->status & HCS_TIMEOUT )
{
DEBUG_USB_INT_HI_PUTCHAR( 't' );
result = USB_STATUS_TIMEOUT;
}
else
{
#ifdef DEBUG_USB_INT_HI
if ( !( ( eptype == HCCHAR_EPTYPE_INTR ) &&
( ( status & USB_HC_INT_NAK ) == USB_HC_INT_NAK ) ) )
{
USB_PRINTF( "0x%08lX", status2 );
}
#endif
return;
}
if ( eptype == HCCHAR_EPTYPE_CTRL )
USBHEP_CtrlEpHandler( hc->ep, result );
else
USBHEP_EpHandler( hc->ep, result );
}
#else /* !defined( USB_SLAVEMODE ) */
if ( status & USB_HC_INT_XFERCOMPL )
{
DEBUG_USB_INT_HI_PUTCHAR( 'c' );
USB->HC[ hcnum ].INT = USB_HC_INT_XFERCOMPL;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_COMPLETED;
TOGGLE_DATA_PID() /* Update data toggle */
}
else if ( status & USB_HC_INT_XACTERR )
{
USB->HC[ hcnum ].INT = USB_HC_INT_XACTERR;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_XACT;
DEBUG_USB_INT_LO_PUTS( "XacT" );
}
else if ( status & USB_HC_INT_BBLERR )
{
USB->HC[ hcnum ].INT = USB_HC_INT_BBLERR;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_BABBLE;
DEBUG_USB_INT_LO_PUTS( "BabL" );
}
else if ( status & USB_HC_INT_STALL )
{
USB->HC[ hcnum ].INT = USB_HC_INT_STALL;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_STALL;
DEBUG_USB_INT_LO_PUTS( "StaL" );
}
else if ( status & USB_HC_INT_NAK )
{
DEBUG_USB_INT_HI_PUTCHAR( 'n' );
USB->HC[ hcnum ].INT = USB_HC_INT_NAK;
if ( ( eptype != HCCHAR_EPTYPE_INTR ) &&
( hc->status == 0 ) )
{
/* Re-activate the channel */
USBHHAL_HCActivate( hcnum, hcchar, false );
}
else
{
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_NAK;
}
}
else if ( status & USB_HC_INT_DATATGLERR )
{
USB->HC[ hcnum ].INT = USB_HC_INT_DATATGLERR;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_TGLERR;
}
else if ( status & USB_HC_INT_CHHLTD )
{
DEBUG_USB_INT_HI_PUTCHAR( 'h' );
USB->HC[ hcnum ].INT = 0xFFFFFFFF;
USB->HC[ hcnum ].INTMSK &= ~USB_HC_INT_CHHLTD;
result = USB_STATUS_EP_ERROR;
if ( hc->status & HCS_STALL )
{
result = USB_STATUS_EP_STALLED;
}
else if ( ( hc->status &
( HCS_NAK | HCS_STALL | HCS_TGLERR |
HCS_BABBLE | HCS_TIMEOUT ) ) == HCS_TGLERR )
{
hc->errorCnt++;
if ( hc->errorCnt < 3 )
{
hc->status |= HCS_RETRY;
}
}
else if ( ( hc->status &
( HCS_TGLERR | HCS_TIMEOUT |
HCS_XACT | HCS_BABBLE ) ) == HCS_XACT )
{
hc->errorCnt++;
if ( hc->errorCnt < 3 )
{
hc->status |= HCS_RETRY;
}
}
else if ( ( hc->status & ( HCS_NAK | HCS_TIMEOUT ) ) == HCS_NAK )
{
if ( eptype == HCCHAR_EPTYPE_INTR )
return;
result = USB_STATUS_EP_NAK;
}
else if ( hc->status & ( HCS_TIMEOUT | HCS_NAK ) )
{
result = USB_STATUS_TIMEOUT;
}
else if ( hc->status == HCS_COMPLETED )
{
result = USB_STATUS_OK;
}
if ( hc->status & HCS_RETRY )
{
USBHHAL_HCStart( hcnum );
}
else
{
if ( eptype == HCCHAR_EPTYPE_CTRL )
USBHEP_CtrlEpHandler( hc->ep, result );
else
USBHEP_EpHandler( hc->ep, result );
}
}
#endif /* !defined( USB_SLAVEMODE ) */
}
/*
* Handle host channel OUT transfer interrupt.
*/
static void Handle_HcOutInt( uint8_t hcnum )
{
USBH_Hc_TypeDef *hc;
USB_Status_TypeDef result;
uint32_t status, hcchar, eptype;
#ifdef DEBUG_USB_INT_HI
uint32_t status2;
#endif
hc = &hcs[ hcnum ];
status = USBHHAL_GetHcInts( hcnum );
hcchar = USB->HC[ hcnum ].CHAR;
eptype = hcchar & _USB_HC_CHAR_EPTYPE_MASK;
DEBUG_USB_INT_HI_PUTCHAR( 'o' );
#if !defined( USB_SLAVEMODE )
if ( status & USB_HC_INT_CHHLTD )
{
USB->HC[ hcnum ].INT = 0xFFFFFFFF;
#ifdef DEBUG_USB_INT_HI
status2 = status;
#endif
status &= USB_HC_INT_XFERCOMPL | USB_HC_INT_STALL | USB_HC_INT_XACTERR |
USB_HC_INT_ACK | USB_HC_INT_NAK;
if ( ( status & ( USB_HC_INT_ACK | USB_HC_INT_XFERCOMPL ) ) ==
( USB_HC_INT_ACK | USB_HC_INT_XFERCOMPL ) )
{
DEBUG_USB_INT_HI_PUTCHAR( 'c' );
hc->xferred = hc->remaining;
hc->remaining = 0;
hc->ep->toggle = ( USB->HC[ hcnum ].TSIZ & _USB_HC_TSIZ_PID_MASK ) ==
USB_HC_TSIZ_PID_DATA0 ? USB_PID_DATA0 : USB_PID_DATA1;
result = USB_STATUS_OK;
}
else if ( status & USB_HC_INT_STALL )
{
result = USB_STATUS_EP_STALLED;
DEBUG_USB_INT_LO_PUTS( "StaL" );
}
else if ( status & USB_HC_INT_XACTERR )
{
DEBUG_USB_INT_LO_PUTS( "XacT" );
if ( status & ( USB_HC_INT_ACK | USB_HC_INT_NAK ) )
{
hc->errorCnt = 0;
USBHHAL_HCStart( hcnum );
return;
}
else
{
hc->errorCnt++;
if ( hc->errorCnt < 3 )
{
USBHHAL_HCStart( hcnum );
return;
}
}
result = USB_STATUS_EP_ERROR;
}
else if ( hc->status & HCS_TIMEOUT )
{
DEBUG_USB_INT_HI_PUTCHAR( 't' );
result = USB_STATUS_TIMEOUT;
}
else
{
#ifdef DEBUG_USB_INT_HI
if ( !( ( eptype == HCCHAR_EPTYPE_INTR ) &&
( ( status & USB_HC_INT_NAK ) == USB_HC_INT_NAK ) ) )
{
USB_PRINTF( "0x%08lX", status2 );
}
#endif
return;
}
if ( eptype == HCCHAR_EPTYPE_CTRL )
USBHEP_CtrlEpHandler( hc->ep, result );
else
USBHEP_EpHandler( hc->ep, result );
}
#else /* #if !defined( USB_SLAVEMODE ) */
if ( status & USB_HC_INT_XFERCOMPL )
{
DEBUG_USB_INT_HI_PUTCHAR( 'c' );
USB->HC[ hcnum ].INT = USB_HC_INT_XFERCOMPL;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_COMPLETED;
}
else if ( status & USB_HC_INT_STALL )
{
USB->HC[ hcnum ].INT = USB_HC_INT_STALL;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_STALL;
DEBUG_USB_INT_LO_PUTS( "StaL" );
}
else if ( status & USB_HC_INT_ACK )
{
DEBUG_USB_INT_HI_PUTCHAR( 'a' );
USB->HC[ hcnum ].INT = USB_HC_INT_ACK;
if ( hc->pending )
{
hc->buf += hc->pending;
hc->xferred += hc->pending;
hc->remaining -= hc->pending;
hc->pending = 0;
TOGGLE_DATA_PID() /* Update data toggle */
if ( eptype == HCCHAR_EPTYPE_INTR )
{
USBHHAL_FillFifo( hcnum, USB->HPTXSTS, hc ); /* There may be more data*/
}
else
{
USBHHAL_FillFifo( hcnum, USB->GNPTXSTS, hc );/* There may be more data*/
}
}
}
else if ( status & USB_HC_INT_NAK )
{
DEBUG_USB_INT_HI_PUTCHAR( 'n' );
USB->HC[ hcnum ].INT = USB_HC_INT_NAK;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_NAK;
}
else if ( status & USB_HC_INT_XACTERR )
{
USB->HC[ hcnum ].INT = USB_HC_INT_XACTERR;
USBHHAL_HCHalt( hcnum, hcchar, eptype );
hc->status |= HCS_XACT;
DEBUG_USB_INT_LO_PUTS( "XacT" );
}
else if ( status & USB_HC_INT_CHHLTD )
{
DEBUG_USB_INT_HI_PUTCHAR( 'h' );
USB->HC[ hcnum ].INT = 0xFFFFFFFF;
USB->HC[ hcnum ].INTMSK &= ~USB_HC_INT_CHHLTD;
result = USB_STATUS_EP_ERROR;
if ( hc->status & HCS_STALL )
{
result = USB_STATUS_EP_STALLED;
}
else if ( hc->status == HCS_XACT )
{
hc->errorCnt++;
if ( hc->errorCnt < 3 )
{
hc->status |= HCS_RETRY;
}
}
else if ( ( hc->status & ( HCS_NAK | HCS_TIMEOUT ) ) == HCS_NAK )
{
if ( eptype == HCCHAR_EPTYPE_INTR )
return;
hc->status |= HCS_RETRY;
}
else if ( hc->status & HCS_TIMEOUT )
{
result = USB_STATUS_TIMEOUT;
}
else if ( hc->status == HCS_COMPLETED )
{
result = USB_STATUS_OK;
}
if ( hc->status & HCS_RETRY )
{
USBHHAL_HCStart( hcnum );
}
else
{
if ( eptype == HCCHAR_EPTYPE_CTRL )
USBHEP_CtrlEpHandler( hc->ep, result );
else
USBHEP_EpHandler( hc->ep, result );
}
}
#endif /* #if !defined( USB_SLAVEMODE ) */
}
/*
* Handle port disconnect interrupt.
*/
static void Handle_USB_GINTSTS_DISCONNINT( void )
{
int i;
uint32_t hcchar;
USB->GINTSTS = USB_GINTSTS_DISCONNINT;
USB->HAINTMSK = 0;
USBH_portStatus = H_PORT_DISCONNECTED;
USBTIMER_Stop( HOSTPORT_TIMER_INDEX );
USBHHAL_PortReset( false );
for ( i=0; i< NUM_HC_USED + 2; i++ )
{
hcchar = USB->HC[ i ].CHAR; /* Halt channel */
USBHHAL_HCHalt( i, hcchar, hcchar & _USB_HC_CHAR_EPTYPE_MASK );
USB->HC[ i ].INT = 0xFFFFFFFF; /* Clear pending interrupts */
if ( !hcs[ i ].idle )
{
USBHEP_TransferDone( hcs[ i ].ep, USB_STATUS_DEVICE_REMOVED );
}
}
DEBUG_USB_INT_LO_PUTS( "\nDisC" );
}
/*
* Handle host channel interrupt. Call IN and OUT transfer handlers as needed.
*/
static void Handle_USB_GINTSTS_HCHINT( void )
{
uint8_t hcnum;
uint32_t hcints, hcmask;
hcints = USBHHAL_GetHostChannelInts();
for ( hcnum = 0, hcmask = 1;
hcnum < NUM_HC_USED + 2;
hcnum++, hcmask <<= 1 )
{
if ( hcints & hcmask )
{
if ( USB->HC[ hcnum ].CHAR & USB_HC_CHAR_EPDIR )
{
Handle_HcInInt( hcnum );
}
else
{
Handle_HcOutInt( hcnum );
}
}
}
}
#if defined( USB_SLAVEMODE )
/*
* Handle non periodic transmit FIFO empty interrupt.
*/
static void Handle_USB_GINTSTS_NPTXFEMP( void )
{
int i;
uint8_t hcnum;
uint32_t fifoStatus;
USBH_Hc_TypeDef *hc;
fifoStatus = USB->GNPTXSTS;
hcnum = ( fifoStatus & FIFO_TXSTS_HCNUM_MASK ) >> FIFO_TXSTS_HCNUM_SHIFT;
hc = &hcs[ hcnum ];
USBHHAL_FillFifo( hcnum, fifoStatus, hc );
DEBUG_USB_INT_LO_PUTS( "NpE" );
for ( i = 0; i < NUM_HC_USED + 2; i++ )
{
if ( hcs[ i ].txNpFempIntOn == true )
return; /* TxFemp int must remain on */
}
USB->GINTMSK &= ~USB_GINTSTS_NPTXFEMP; /* Turn off TxFemp interrupt */
}
#endif
/*
* Callback function for port interrupt state machine.
* Called on timeout of the port timer when a port reset is completed.
*/
static void PortResetComplete( void )
{
if ( USB->HPRT & USB_HPRT_PRTCONNSTS ) /* Is device still connected ? */
{
DEBUG_USB_INT_LO_PUTCHAR( '5' );
}
else
{
USBH_portStatus = H_PORT_DISCONNECTED;
}
USBHHAL_PortReset( false );
}
/*
* Callback function for port interrupt state machine.
* Called on timeout of the port timer connection debounce time has expired.
*/
static void PortDebounceComplete( void )
{
uint32_t hprt;
hprt = USB->HPRT; /* Get port status */
if ( hprt & USB_HPRT_PRTCONNSTS ) /* Is device still connected ? */
{
if ( ( hprt & _USB_HPRT_PRTSPD_MASK ) == HPRT_L_SPEED )
{
DEBUG_USB_INT_LO_PUTCHAR( '3' );
USB->HFIR = 6000;
/* Set 6 MHz PHY clock */
USB->HCFG = ( USB->HCFG & ~_USB_HCFG_FSLSPCLKSEL_MASK ) |
( 2 << _USB_HCFG_FSLSPCLKSEL_SHIFT );
}
else if ( ( hprt & _USB_HPRT_PRTSPD_MASK ) == HPRT_F_SPEED )
{
DEBUG_USB_INT_LO_PUTCHAR( '4' );
USB->HFIR = 48000;
/* Set 48 MHz PHY clock */
USB->HCFG = ( USB->HCFG & ~_USB_HCFG_FSLSPCLKSEL_MASK ) |
( 1 << _USB_HCFG_FSLSPCLKSEL_SHIFT );
}
USBH_portStatus = H_PORT_CONNECTED_RESETTING;
USBTIMER_Start( HOSTPORT_TIMER_INDEX,
USBH_attachTiming[ USBH_attachRetryCount ].resetTime,
PortResetComplete );
USBHHAL_PortReset( true );
}
else
{
USBH_portStatus = H_PORT_DISCONNECTED;
}
}
/*
* Handle port interrupt.
*/
static void Handle_USB_GINTSTS_PRTINT( void )
{
uint32_t hprt;
hprt = USB->HPRT; /* Get port status */
DEBUG_USB_INT_LO_PUTCHAR( '^' );
switch ( USBH_portStatus )
{
case H_PORT_DISCONNECTED:
/***********************/
if ( ( hprt & USB_HPRT_PRTCONNDET ) &&
( hprt & USB_HPRT_PRTCONNSTS ) ) /* Any device connected ? */
{
DEBUG_USB_INT_LO_PUTCHAR( '2' );
USBH_portStatus = H_PORT_CONNECTED_DEBOUNCING;
USBTIMER_Start( HOSTPORT_TIMER_INDEX,
USBH_attachTiming[ USBH_attachRetryCount ].debounceTime,
PortDebounceComplete );
}
break;
case H_PORT_CONNECTED_DEBOUNCING:
/***********************/
DEBUG_USB_INT_LO_PUTCHAR( 'Y' );
break;
case H_PORT_CONNECTED_RESETTING:
/***********************/
if ( ( hprt & USB_HPRT_PRTENCHNG ) && /* Port enable changed ? */
( hprt & USB_HPRT_PRTENA ) && /* Port enabled ? */
( hprt & USB_HPRT_PRTCONNSTS ) ) /* Device still connected ? */
{
DEBUG_USB_INT_LO_PUTCHAR( '6' );
USBH_portStatus = H_PORT_CONNECTED;
}
break;
case H_PORT_CONNECTED:
/***********************/
if ( ( hprt & USB_HPRT_PRTENCHNG ) &&
( !( hprt & USB_HPRT_PRTENA ) ) )
{
DEBUG_USB_INT_LO_PUTCHAR( 'X' );
#if ( USB_VBUSOVRCUR_PORT != USB_VBUSOVRCUR_PORT_NONE )
if ( GPIO_PinInGet( USB_VBUSOVRCUR_PORT, USB_VBUSOVRCUR_PIN ) ==
USB_VBUSOVRCUR_POLARITY )
{
DEBUG_USB_INT_LO_PUTCHAR( '~' );
USBHHAL_PortReset( false );
USBHHAL_VbusOn( false );
USBTIMER_Stop( HOSTPORT_TIMER_INDEX );
USBH_portStatus = H_PORT_OVERCURRENT;
}
#endif
}
break;
case H_PORT_OVERCURRENT:
/***********************/
break;
}
if ( hprt & USB_HPRT_PRTOVRCURRCHNG ) /* Overcurrent change interrupt ? */
{
DEBUG_USB_INT_LO_PUTCHAR( '9' );
}
hprt &= ~HPRT_WC_MASK; /* Mask off all write clear bits */
hprt |= USB_HPRT_PRTCONNDET | USB_HPRT_PRTENCHNG | USB_HPRT_PRTOVRCURRCHNG;
USB->HPRT = hprt; /* Clear all port interrupt flags */
}
#if defined( USB_SLAVEMODE )
/*
* Handle periodic transmit FIFO empty interrupt.
*/
static void Handle_USB_GINTSTS_PTXFEMP( void )
{
int i;
uint8_t hcnum;
uint32_t fifoStatus;
USBH_Hc_TypeDef *hc;
fifoStatus = USB->HPTXSTS;
hcnum = ( fifoStatus & FIFO_TXSTS_HCNUM_MASK ) >> FIFO_TXSTS_HCNUM_SHIFT;
hc = &hcs[ hcnum ];
USBHHAL_FillFifo( hcnum, fifoStatus, hc );
DEBUG_USB_INT_LO_PUTS( "PE" );
for ( i = 0; i < NUM_HC_USED + 2; i++ )
{
if ( hcs[ i ].txPFempIntOn == true )
return; /* TxFemp int must remain on */
}
USB->GINTMSK &= ~USB_GINTSTS_PTXFEMP ; /* Turn off TxFemp interrupt */
}
#endif
#if defined( USB_SLAVEMODE )
/*
* Handle receive FIFO full interrupt.
*/
static void Handle_USB_GINTSTS_RXFLVL( void )
{
uint8_t hcnum;
USBH_Hc_TypeDef *hc;
uint32_t grxstsp, hcchar;
int bytecnt, pid, packetcnt;
/* Disable the Rx Status Queue Level interrupt */
USB->GINTMSK &= ~USB_GINTMSK_RXFLVLMSK;
grxstsp = USB->GRXSTSP;
hcnum = ( grxstsp & _USB_GRXSTSP_CHEPNUM_MASK ) >> _USB_GRXSTSP_CHEPNUM_SHIFT;
hc = &hcs[ hcnum ];
bytecnt = ( grxstsp & _USB_GRXSTSP_BCNT_MASK ) >> _USB_GRXSTSP_BCNT_SHIFT;
pid = ( grxstsp & _USB_GRXSTSP_DPID_MASK ) >> _USB_GRXSTSP_DPID_SHIFT;
packetcnt = ( USB->HC[ hcnum ].TSIZ & _USB_HC_TSIZ_PKTCNT_MASK ) >>
_USB_HC_TSIZ_PKTCNT_SHIFT;
hcchar = USB->HC[ hcnum ].CHAR;
DEBUG_USB_INT_HI_PUTCHAR( 'q' );
switch ( grxstsp & _USB_GRXSTSP_PKTSTS_MASK )
{
case GRXSTSP_PKTSTS_HOST_DATAINRECEIVED:
/* Read the data into the host buffer. */
if ( ( bytecnt > 0 ) && ( hc->buf != NULL ) )
{
USBHAL_ReadFifo( hc->buf, bytecnt );
hc->remaining -= bytecnt;
hc->buf += bytecnt;
hc->xferred += bytecnt;
if ( packetcnt )
{
/* re-activate the channel when more packets are expected */
hcchar |= USB_HC_CHAR_CHENA;
hcchar &= ~USB_HC_CHAR_CHDIS;
USB->HC[ hcnum ].CHAR = hcchar;
}
hc->ep->toggle = pid;
}
DEBUG_USB_INT_HI_PUTS( "IR" );
break;
case GRXSTSP_PKTSTS_HOST_DATAINCOMPLETE:
if ( packetcnt )
{
hc->ep->toggle = pid;
}
DEBUG_USB_INT_HI_PUTS( "IC" );
break;
case GRXSTSP_PKTSTS_HOST_CHANNELHALTED:
case GRXSTSP_PKTSTS_HOST_DATATOGGLEERROR:
default:
break;
}
/* Enable the Rx Status Queue Level interrupt */
USB->GINTMSK |= USB_GINTMSK_RXFLVLMSK;
}
#endif
/** @endcond */
#endif /* defined( USB_HOST ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */

View File

@ -0,0 +1,384 @@
/***************************************************************************//**
* @file
* @brief USB protocol stack library, timer API.
* @author Energy Micro AS
* @version 3.20.0
*******************************************************************************
* @section License
* <b>(C) Copyright 2012 Energy Micro AS, http://www.energymicro.com</b>
*******************************************************************************
*
* 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.
* 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.
* 4. The source and compiled code may only be used on Energy Micro "EFM32"
* microcontrollers and "EFR4" radios.
*
* DISCLAIMER OF WARRANTY/LIMITATION OF REMEDIES: Energy Micro AS has no
* obligation to support this Software. Energy Micro AS is providing the
* Software "AS IS", with no express or implied warranties of any kind,
* including, but not limited to, any implied warranties of merchantability
* or fitness for any particular purpose or warranties against infringement
* of any proprietary rights of a third party.
*
* Energy Micro AS will not be liable for any consequential, incidental, or
* special damages, or any other relief, or for any claim by any third party,
* arising from your use of this Software.
*
*****************************************************************************/
#include "em_device.h"
#if defined( USB_PRESENT ) && ( USB_COUNT == 1 )
#include "em_usb.h"
#if defined( USB_DEVICE ) || defined( USB_HOST )
#include "em_cmu.h"
#include "em_timer.h"
#include "em_usbtypes.h"
#include "em_usbhal.h"
/*
* Use one HW timer to serve n software milisecond timers.
* A timer is, when running, in a linked list of timers.
* A given timers timeout period is the acculmulated timeout
* of all timers preceeding it in the queue.
* This makes timer start (linked list insertion) computing intensive,
* but the checking of the queue at each tick very effective.
* ______ ______ ______
* | | --->| | --->| |
* head --> | | | | | | | |
* |______|--- |______|--- |______|---/ NULL
*/
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
#ifndef USB_TIMER
#define USB_TIMER USB_TIMER0
#endif
#if ( USB_TIMER == USB_TIMER0 ) && ( TIMER_COUNT >= 1 )
#define TIMER TIMER0
#define TIMER_CLK cmuClock_TIMER0
#define TIMER_IRQ TIMER0_IRQn
#define TIMER_IRQHandler TIMER0_IRQHandler
#elif ( USB_TIMER == USB_TIMER1 ) && ( TIMER_COUNT >= 2 )
#define TIMER TIMER1
#define TIMER_CLK cmuClock_TIMER1
#define TIMER_IRQ TIMER1_IRQn
#define TIMER_IRQHandler TIMER1_IRQHandler
#elif ( USB_TIMER == USB_TIMER2 ) && ( TIMER_COUNT >= 3 )
#define TIMER TIMER2
#define TIMER_CLK cmuClock_TIMER2
#define TIMER_IRQ TIMER2_IRQn
#define TIMER_IRQHandler TIMER2_IRQHandler
#elif ( USB_TIMER == USB_TIMER3 ) && ( TIMER_COUNT == 4 )
#define TIMER TIMER3
#define TIMER_CLK cmuClock_TIMER3
#define TIMER_IRQ TIMER3_IRQn
#define TIMER_IRQHandler TIMER3_IRQHandler
#else
#error "Illegal USB TIMER definition"
#endif
typedef struct _timer
{
uint32_t timeout; /* Delta value relative to prev. timer */
struct _timer *next;
USBTIMER_Callback_TypeDef callback;
bool running;
} USBTIMER_Timer_TypeDef;
#if ( NUM_QTIMERS > 0 )
static USBTIMER_Timer_TypeDef timers[ NUM_QTIMERS ];
static USBTIMER_Timer_TypeDef *head = NULL;
#endif
static uint32_t ticksPrMs, ticksPr1us, ticksPr10us, ticksPr100us;
#if ( NUM_QTIMERS > 0 )
static void TimerTick( void );
void TIMER_IRQHandler( void )
{
uint32_t flags;
flags = TIMER_IntGet( TIMER );
if ( flags & TIMER_IF_CC0 )
{
TIMER_IntClear( TIMER, TIMER_IFC_CC0 );
TIMER_CompareSet( TIMER, 0, TIMER_CaptureGet( TIMER, 0 ) + ticksPrMs );
TimerTick();
}
}
#endif /* ( NUM_QTIMERS > 0 ) */
static void DelayTicks( uint16_t ticks )
{
uint16_t startTime;
volatile uint16_t now;
if ( ticks )
{
startTime = TIMER_CounterGet( TIMER );
do
{
now = TIMER_CounterGet(TIMER);
} while ( (uint16_t)( now - startTime ) < ticks );
}
}
/** @endcond */
/** @addtogroup USB_COMMON
* @{*/
/***************************************************************************//**
* @brief
* Active wait millisecond delay function. Can also be used inside
* interrupt handlers.
*
* @param[in] msec
* Number of milliseconds to wait.
******************************************************************************/
void USBTIMER_DelayMs( uint32_t msec )
{
uint64_t totalTicks;
totalTicks = (uint64_t)ticksPrMs * msec;
while ( totalTicks > 20000 )
{
DelayTicks( 20000 );
totalTicks -= 20000;
}
DelayTicks( (uint16_t)totalTicks );
}
/***************************************************************************//**
* @brief
* Active wait microsecond delay function. Can also be used inside
* interrupt handlers.
*
* @param[in] usec
* Number of microseconds to wait.
******************************************************************************/
void USBTIMER_DelayUs( uint32_t usec )
{
uint64_t totalTicks;
totalTicks = (uint64_t)ticksPr1us * usec;
if ( totalTicks == 0 )
{
usec /= 10;
totalTicks = (uint64_t)ticksPr10us * usec;
if ( totalTicks == 0 )
{
usec /= 10;
totalTicks = (uint64_t)ticksPr100us * usec;
}
}
while ( totalTicks > 60000 )
{
DelayTicks( 60000 );
totalTicks -= 60000;
}
DelayTicks( (uint16_t)totalTicks );
}
/***************************************************************************//**
* @brief
* Activate the hardware timer used to pace the 1 millisecond timer system.
*
* @details
* Call this function whenever the HFPERCLK frequency is changed.
* This function is initially called by HOST and DEVICE stack xxxx_Init()
* functions.
******************************************************************************/
void USBTIMER_Init( void )
{
uint32_t freq;
TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT;
TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT;
freq = CMU_ClockFreqGet( cmuClock_HFPER );
ticksPrMs = ( freq + 500 ) / 1000;
ticksPr1us = ( freq + 500000 ) / 1000000;
ticksPr10us = ( freq + 50000 ) / 100000;
ticksPr100us = ( freq + 5000 ) / 10000;
timerCCInit.mode = timerCCModeCompare;
CMU_ClockEnable( TIMER_CLK, true );
TIMER_TopSet( TIMER, 0xFFFF );
TIMER_InitCC( TIMER, 0, &timerCCInit );
TIMER_Init( TIMER, &timerInit );
#if ( NUM_QTIMERS > 0 )
TIMER_IntClear( TIMER, 0xFFFFFFFF );
TIMER_IntEnable( TIMER, TIMER_IEN_CC0 );
TIMER_CompareSet( TIMER, 0, TIMER_CounterGet( TIMER ) + ticksPrMs );
NVIC_ClearPendingIRQ( TIMER_IRQ );
NVIC_EnableIRQ( TIMER_IRQ );
#endif /* ( NUM_QTIMERS > 0 ) */
}
#if ( NUM_QTIMERS > 0 ) || defined( DOXY_DOC_ONLY )
/***************************************************************************//**
* @brief
* Start a timer.
*
* @details
* If the timer is already running, it will be restarted with new timeout.
*
* @param[in] id
* Timer id (0..).
*
* @param[in] timeout
* Number of milliseconds before timer will elapse.
*
* @param[in] callback
* Function to be called on timer elapse, ref. @ref USBTIMER_Callback_TypeDef.
******************************************************************************/
void USBTIMER_Start( uint32_t id, uint32_t timeout,
USBTIMER_Callback_TypeDef callback )
{
uint32_t accumulated;
USBTIMER_Timer_TypeDef *this, **last;
INT_Disable();
if ( timers[ id ].running )
{
USBTIMER_Stop( id );
}
timers[ id ].running = true;
timers[ id ].callback = callback;
timers[ id ].next = NULL;
if ( !head ) /* Queue empty ? */
{
timers[ id ].timeout = timeout;
head = &timers[ id ];
}
else
{
this = head;
last = &head;
accumulated = 0;
/* Do a sorted insert */
while ( this )
{
if ( timeout < accumulated + this->timeout ) /* Insert before "this" ? */
{
timers[ id ].timeout = timeout - accumulated;
timers[ id ].next = this;
*last = &timers[ id ];
this->timeout -= timers[ id ].timeout; /* Adjust timeout */
break;
}
else if ( this->next == NULL ) /* At end of queue ? */
{
timers[ id ].timeout = timeout - accumulated - this->timeout;
this->next = &timers[ id ];
break;
}
accumulated += this->timeout;
last = &this->next;
this = this->next;
}
}
INT_Enable();
}
/***************************************************************************//**
* @brief
* Stop a timer.
*
* @param[in] id
* Timer id (0..).
******************************************************************************/
void USBTIMER_Stop( uint32_t id )
{
USBTIMER_Timer_TypeDef *this, **last;
INT_Disable();
if ( head ) /* Queue empty ? */
{
this = head;
last = &head;
timers[ id ].running = false;
while ( this )
{
if ( this == &timers[ id ] ) /* Correct timer ? */
{
if ( this->next )
{
this->next->timeout += timers[ id ].timeout; /* Adjust timeout */
}
*last = this->next;
break;
}
last = &this->next;
this = this->next;
}
}
INT_Enable();
}
#endif /* ( NUM_QTIMERS > 0 ) */
/** @} (end addtogroup USB_COMMON) */
#if ( NUM_QTIMERS > 0 )
/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
static void TimerTick( void )
{
USBTIMER_Callback_TypeDef cb;
INT_Disable();
if ( head )
{
head->timeout--;
while ( head )
{
if ( head->timeout == 0 )
{
cb = head->callback;
head->running = false;
head = head->next;
/* The callback may place new items in the queue !!! */
if ( cb )
{
(cb)();
}
continue; /* There might be more than one timeout pr. tick */
}
break;
}
}
INT_Enable();
}
/** @endcond */
#endif /* ( NUM_QTIMERS > 0 ) */
#endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */
#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */