Fork of the USBDevice library by mbed.
Dependents: mouse1 ECE2035_Agar_Shell_ECE2035_Prana usb_device
Fork of USBDevice by
Revision 59:2af474687369, committed 2015-08-13
- Comitter:
- mbed_official
- Date:
- Thu Aug 13 15:46:06 2015 +0100
- Parent:
- 58:f3cad7e6984e
- Commit message:
- Synchronized with git revision 376d6a73e345b728a788041adb166b08cd8d2b95
Full URL: https://github.com/mbedmicro/mbed/commit/376d6a73e345b728a788041adb166b08cd8d2b95/
Silicon Labs - Add support for USBDevice
Changed in this revision
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/inc/em_usb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/inc/em_usb.h Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,999 @@ +/***************************************************************************//** + * @file em_usb.h + * @brief USB protocol stack library API for EFM32. + * @version 3.20.14 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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> +#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. + * @{ + ******************************************************************************/ + +#define SILABS_USB_VID 0x10C4 /**< Silicon Labs Vendor ID, supplied by USB-IF. */ + +/* 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. */ +#define USB_AUDIO_GET_CUR 0x81 /**< Audio class setup request GET_CUR. */ +#define USB_AUDIO_SET_CUR 0x01 /**< Audio class setup request SET_CUR. */ +#define USB_AUDIO_GET_CUR 0x81 /**< Audio class setup request GET_CUR. */ +#define USB_AUDIO_SET_MIN 0x02 /**< Audio class setup request SET_MIN. */ +#define USB_AUDIO_GET_MIN 0x82 /**< Audio class setup request GET_MIN. */ +#define USB_AUDIO_SET_MAX 0x03 /**< Audio class setup request SET_MAX. */ +#define USB_AUDIO_GET_MAX 0x83 /**< Audio class setup request GET_MAX. */ +#define USB_AUDIO_SET_RES 0x04 /**< Audio class setup request SET_RES. */ +#define USB_AUDIO_GET_RES 0x84 /**< Audio class setup request GET_RES. */ +#define USB_AUDIO_SET_MEM 0x05 /**< Audio class setup request SET_MEM. */ +#define USB_AUDIO_GET_MEM 0x85 /**< Audio class setup request GET_MEM. */ +#define USB_AUDIO_GET_STAT 0xFF /**< Audio class setup request GET_STAT. */ + +/* 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_MAX_STRING_DESCRIPTOR_CHARS 126 /**< Maximum STRING descriptor bString length. */ +#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_INTERFACE_ASSOCIATION_DESCRIPTOR 11 /**< INTERFACE_ASSOCIATION descriptor value. */ +#define USB_HID_DESCRIPTOR 0x21 /**< HID descriptor value. */ +#define USB_SMARTCARD_DESCRIPTOR 0x21 /**< Smartcard usb-ccid-specific Descriptor Type. */ +#define USB_HID_REPORT_DESCRIPTOR 0x22 /**< HID REPORT descriptor value. */ +#define USB_CS_INTERFACE_DESCRIPTOR 0x24 /**< Audio Class-specific interface Descriptor Type. */ +#define USB_CS_ENDPOINT_DESCRIPTOR 0x25 /**< Audio Class-specific endpoint Descriptor Type. */ +#define USB_HUB_DESCRIPTOR 0x29 /**< HUB descriptor value. */ +#define USB_CA_HEADER_DESCRIPTOR 1 /**< Audio Class-Specific AC Interface Header descriptor.*/ +#define USB_CA_INPUT_TERMINAL_DESCRIPTOR 2 /**< Audio Class-Specific AC Interface Input Terminal desc. */ +#define USB_CA_OUTPUT_TERMINAL_DESCRIPTOR 3 /**< Audio Class-Specific AC Interface Output Terminal desc.*/ +#define USB_CA_MIXER_UNIT_DESCRIPTOR 4 /**< Audio Class-Specific AC Interface Mixer descriptor.*/ +#define USB_CA_SELECTOR_UNIT_DESCRIPTOR 5 /**< Audio Class-Specific AC Interface Selector desc. */ +#define USB_CA_FEATURE_UNIT_DESCRIPTOR 6 /**< Audio Class-Specific AC Interface Feature desc. */ +#define USB_CA_PROCESSING_UNIT_DESCRIPTOR 7 /**< Audio Class-Specific AC Interface Processing desc.*/ +#define USB_CA_EXTENSION_UNIT_DESCRIPTOR 8 /**< Audio Class-Specific AC Interface Extension desc. */ +#define USB_CA_EP_GENERAL_DESCRIPTOR 1 /**< Audio Class-Specific general descriptor subtype code.*/ +#define USB_CA_AS_GENERAL_DESCRIPTOR 1 /**< Audio Class-Specific AS Interface General descriptor.*/ +#define USB_CA_FORMAT_TYPE_DESCRIPTOR 2 /**< Audio Class-Specific AS Interface Format Type desc. */ + +#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_INTERFACE_ASSOCIATION_DESCSIZE 8 /**< INTERFACE_ASSOCIATION descriptor size. */ +#define USB_HID_DESCSIZE 9 /**< HID descriptor size. */ +#define USB_SMARTCARD_DESCSIZE 54 /**< CCID 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.*/ +#define USB_CA_INPUT_TERMINAL_DESCSIZE 12 /**< Audio Input Terminal descriptor size. */ +#define USB_CA_OUTPUT_TERMINAL_DESCSIZE 9 /**< Audio Output Terminal descriptor size. */ +#define USB_CA_EP_GENERAL_DESCSIZE 7 /**< Audio Class-Specific general descriptor subtype size.*/ +#define USB_CA_AS_GENERAL_DESCSIZE 7 /**< Audio Class-Specific AS Interface General desc size.*/ +#define USB_CA_STD_AS_ENDPOINT_DESCSZIE 9 /**< Audio-class standard audio stream descriptor size.*/ + +/* Misc. USB definitions */ +#define USB_LS_CTRL_EP_MAXSIZE 8 /**< The max size of low speed control endpoints. */ +#define USB_LS_INTR_EP_MAXSIZE 8 /**< The max size of low speed interrupt endpoints. */ +#define USB_FS_CTRL_EP_MAXSIZE 64 /**< The max size of full speed control endpoints. */ +#define USB_FS_INTR_EP_MAXSIZE 64 /**< The max size of full speed interrupt endpoints. */ +#define USB_FS_BULK_EP_MAXSIZE 64 /**< The max size of full speed bulk endpoints. */ +#define USB_FS_ISOC_EP_MAXSIZE 1023 /**< The max size of full speed isochronous endpoints. */ +#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_EPSYNC_NO (0 << 2) /**< Endpoint synchronization type, none. */ +#define USB_EPSYNC_ASYNC (1 << 2) /**< Endpoint synchronization type, asynchronous. */ +#define USB_EPSYNC_ADAPTIVE (2 << 2) /**< Endpoint synchronization type, adaptive. */ +#define USB_EPSYNC_SYNC (3 << 2) /**< Endpoint synchronization type, synchronous. */ +#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 USB_CLASS_AUDIO 1 /**< Audio interface class code. */ +#define USB_CLASS_AUDIO_CONTROL 1 /**< Audio subclass code for control interface. */ +#define USB_CLASS_AUDIO_STREAMING 2 /**< Audio subclass code for streaming interface. */ +#define USB_CLASS_AUDIO_MIDISTREAMING 3 /**< Audio subclass code for midi streaming interface. */ + +/*** Triplet for the device descriptor of a composite device using IAD descriptors. ***/ +#define USB_CLASS_MISCELLANEOUS 0xEF /**< MISCELLANEOUS device class code. */ +#define USB_CLASS_MISC_COMMON_SUBCLASS 2 /**< MISCELLANEOUS Common sub class code. */ +#define USB_CLASS_MISC_IAD_PROTOCOL 1 /**< MISCELLANEOUS Interface Association Descriptor protocol 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(). */ + +#if defined( __GNUC__ ) /* GCC compilers */ +#if defined( __CHAR16_TYPE__ ) +typedef __CHAR16_TYPE__ char16_t; +#else +typedef unsigned short char16_t; +#endif + +#elif defined( __ICCARM__ ) /* IAR compiler */ +#include <uchar.h> + +#elif defined( __CC_ARM ) /* MDK-ARM compiler */ +typedef unsigned short char16_t; +#endif + +/** Macro for creating USB compliant UTF-16LE UNICODE string descriptors. + * @n Example: STATIC_CONST_STRING_DESC( iManufacturer, 'E','n','e','r','g','y',' ','M','i','c','r','o',' ','A','S' ); + * @note The size of the resulting struct will be two byte larger than a USB string + * descriptor. This is to accommodate a terminating null char for the string. + * The value assigned to the 'len' member does not take this into account + * and is therefore correct usb wise. + */ +#define STATIC_CONST_STRING_DESC( _name, ... ) \ +EFM32_PACK_START( 1 ) \ +typedef struct \ +{ \ + uint8_t len; \ + uint8_t type; \ + char16_t name[ 1 + sizeof( (char16_t[]){__VA_ARGS__} ) / 2]; \ +} __attribute__ ((packed)) _##_name; \ +EFM32_PACK_END() \ +EFM32_ALIGN( 4 ) \ +EFM32_PACK_START( 1 ) \ +static const _##_name _name __attribute__ ((aligned(4)))= \ +{ \ + .len = sizeof( _##_name ) - 2, \ + .type = USB_STRING_DESCRIPTOR, \ + .name = {__VA_ARGS__}, \ + .name[ ( ( sizeof( _##_name ) - 2 ) / 2 ) - 1 ] = '\0' \ +} \ +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 ) \ +EFM32_PACK_START( 1 ) \ +typedef struct \ +{ \ + uint8_t len; \ + uint8_t type; \ + uint8_t name[ 2 ]; \ +} __attribute__ ((packed)) _##_name; \ +EFM32_PACK_END() \ +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. */ + char16_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 + +/* The DMA engine use one FIFO ram word for each host channel. */ +#define MAX_HOST_FIFO_SIZE_INWORDS (512-MAX_NUM_HOSTCHANNELS)/*Unit is 4 bytes*/ + +#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 ); + +#ifdef __MBED__ +int USBD_SetAddress( uint8_t addr ); +int USBD_AddEndpoint( int epAddr, int transferType, int maxPacketSize, int bufferMult ); +int USBD_EpIsStalled( int epAddr ); +void USBD_StallEp0( void ); +#endif + +/** @} (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_HOST_FIFO_SIZE_INWORDS * 2,/* 1024 bytes Rx FIFO size. */ \ + MAX_HOST_FIFO_SIZE_INWORDS, /* 512 bytes non-periodic Tx FIFO size. */ \ + MAX_HOST_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 */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/inc/em_usbd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/inc/em_usbd.h Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,206 @@ +/***************************************************************************//** + * @file em_usbd.h + * @brief USB protocol stack library API for EFM32. + * @version 3.20.14 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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; + } + + USBDHAL_SetEp0InDmaPtr( ep->buf ); + USBDHAL_StartEp0In( EFM32_MIN( ep->remaining, ep->packetSize ), + dev->ep0MpsCode ); + } + else + { + USBDHAL_SetEp0OutDmaPtr( ep->buf ); + USBDHAL_StartEp0Out( ep->packetSize, dev->ep0MpsCode ); + } +} + +__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 ), + dev->ep0MpsCode ); + } + else + { + USBDHAL_StartEp0Out( ep->packetSize, dev->ep0MpsCode ); + } +} + +__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 */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/inc/em_usbh.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/inc/em_usbh.h Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,75 @@ +/***************************************************************************//** + * @file em_usbh.h + * @brief USB protocol stack library API for EFM32. + * @version 3.20.14 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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 */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/inc/em_usbhal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/inc/em_usbhal.h Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,757 @@ +/***************************************************************************//** + * @file em_usbhal.h + * @brief USB protocol stack library, low level USB peripheral access. + * @version 3.20.14 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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 DOEPCTL_EPTYPE_CONTROL (0 << _USB_DOEP_CTL_EPTYPE_SHIFT ) +#define DOEPCTL_EPTYPE_ISOC (1 << _USB_DOEP_CTL_EPTYPE_SHIFT ) +#define DOEPCTL_EPTYPE_BULK (2 << _USB_DOEP_CTL_EPTYPE_SHIFT ) +#define DOEPCTL_EPTYPE_INTR (3 << _USB_DOEP_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 ); +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_DisablePhyPins( void ) +{ + USB->ROUTE = _USB_ROUTE_RESETVALUE; +} + +__STATIC_INLINE void USBHAL_DisableUsbInt( void ) +{ + USB->IEN = _USB_IEN_RESETVALUE; +} + +__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 bool USBHAL_VbusIsOn( void ) +{ + return ( USB->STATUS & USB_STATUS_VREGOS ) != 0; +} + +#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 | + 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_EnableUsbResetAndSuspendInt( void ) +{ + /* Disable all interrupts. */ + USB->GINTMSK = 0; + + USB->GINTMSK = USB_GINTMSK_USBRSTMSK | USB_GINTMSK_USBSUSPMSK; +} + +__STATIC_INLINE void USBDHAL_Ep0Activate( uint32_t ep0mps ) +{ + USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_CGNPINNAK; + + USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) + | USB_DOEP0CTL_CNAK | USB_DOEP0CTL_EPENA + | ep0mps; +} + +__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; +} + +__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; + + msk = USB->DIEPMSK; + 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; +#if defined( USB_DOEP0INT_STUPPKTRCVD ) + retVal &= USB->DOEPMSK | USB_DOEP0INT_STUPPKTRCVD; +#else + retVal &= USB->DOEPMSK; +#endif + + 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 == DOEPCTL_EPTYPE_INTR ) || ( eptype == DOEPCTL_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 ) + +{ + USB->DOEP0DMAADDR = (uint32_t)dev->setupPkt; + USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) + | USB_DOEP0CTL_EPENA + | dev->ep0MpsCode; +} + +__STATIC_INLINE void USBDHAL_SetAddr( uint8_t addr ) +{ + USB->DCFG = ( USB->DCFG & + ~_USB_DCFG_DEVADDR_MASK ) | + (addr << _USB_DCFG_DEVADDR_SHIFT ); +} + +__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; +} + +__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, uint32_t ep0mps ) +{ + USB->DIEP0TSIZ = ( len << _USB_DIEP0TSIZ_XFERSIZE_SHIFT ) | + ( 1 << _USB_DIEP0TSIZ_PKTCNT_SHIFT ); + + USB->DIEP0CTL = ( USB->DIEP0CTL & ~DEPCTL_WO_BITMASK ) + | USB_DIEP0CTL_CNAK | USB_DIEP0CTL_EPENA + | ep0mps; +} + +__STATIC_INLINE void USBDHAL_StartEp0Out( uint32_t len, uint32_t ep0mps ) +{ + USB->DOEP0TSIZ = ( len << _USB_DOEP0TSIZ_XFERSIZE_SHIFT ) | + ( 1 << _USB_DOEP0TSIZ_PKTCNT_SHIFT ); + + USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) + | USB_DOEP0CTL_CNAK | USB_DOEP0CTL_EPENA + | ep0mps; +} + +__STATIC_INLINE void USBDHAL_StartEp0Setup( USBD_Device_TypeDef *dev ) +{ + dev->ep[ 0 ].in = false; + +#if defined( USB_DOEP0INT_STUPPKTRCVD ) + USB->DOEP0TSIZ = ( 8*3 << _USB_DOEP0TSIZ_XFERSIZE_SHIFT ) | + ( 1 << _USB_DOEP0TSIZ_PKTCNT_SHIFT ) | + ( 3 << _USB_DOEP0TSIZ_SUPCNT_SHIFT ); +#else + USB->DOEP0TSIZ = 3 << _USB_DOEP0TSIZ_SUPCNT_SHIFT; +#endif + + dev->setup = dev->setupPkt; + USB->DOEP0DMAADDR = (uint32_t)dev->setup; + +#if defined( USB_DOEP0INT_STUPPKTRCVD ) + USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) + | USB_DOEP0CTL_EPENA + | dev->ep0MpsCode; +#else + USB->DOEP0CTL = ( USB->DOEP0CTL & ~DEPCTL_WO_BITMASK ) + | USB_DOEP0CTL_CNAK | USB_DOEP0CTL_EPENA + | dev->ep0MpsCode; +#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 ); + + USB_DINEPS[ ep->num ].DMAADDR = (uint32_t)ep->buf; + 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 ); + + ep->hwXferSize = xfersize; + USB_DOUTEPS[ ep->num ].DMAADDR = (uint32_t)ep->buf; + 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 | + USB_GINTMSK_HCHINTMSK | + USB_GINTMSK_DISCONNINTMSK; +} + +__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; + 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 */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/inc/em_usbtypes.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/inc/em_usbtypes.h Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,230 @@ +/***************************************************************************//** + * @file em_usbtypes.h + * @brief USB protocol stack library, internal type definitions. + * @version 3.20.14 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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 */ +#if defined( _USB_DIEPTXF6_MASK ) + #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 */ + #define MAX_DEVICE_FIFO_SIZE_INWORDS 512U +#else + #define MAX_NUM_TX_FIFOS 3 /* In addition to EP0 Tx FIFO */ + #define MAX_NUM_IN_EPS 3 /* In addition to EP0 */ + #define MAX_NUM_OUT_EPS 3 /* In addition to EP0 */ + #define MAX_DEVICE_FIFO_SIZE_INWORDS 384U +#endif +#define MIN_EP_FIFO_SIZE_INWORDS 16U /* Unit is words (32bit) */ +#define MIN_EP_FIFO_SIZE_INBYTES 64U /* Unit is bytes (8bit) */ + +/* For MCU's without USB host capability. */ +#if !defined( USB_ROUTE_VBUSENPEN ) +#define USB_VBUS_SWITCH_NOT_PRESENT +#endif + +/* 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_EP0_IN_STATUS = 3, + D_EP0_OUT_STATUS = 4 +} 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; + uint32_t hwXferSize; + uint32_t fifoSize; + USBD_EpState_TypeDef state; + USB_XferCompleteCb_TypeDef xferCompleteCb; +} USBD_Ep_TypeDef; + +typedef struct +{ + USB_Setup_TypeDef *setup; + USB_Setup_TypeDef setupPkt[3]; + uint8_t configurationValue; /* Must be DWORD aligned */ + bool remoteWakeupEnabled; + uint8_t numberOfStrings; + uint8_t numberOfInterfaces; + 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 ]; + uint32_t ep0MpsCode; +} 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; + uint32_t hwXferSize; + uint8_t status; + bool idle; + 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 */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/inc/usbconfig.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/inc/usbconfig.h Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,88 @@ +/***************************************************************************//** + * @file usbconfig.h + * @brief USB protocol stack library, application supplied configuration options. + * @version 3.20.12 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef __USBCONFIG_H +#define __USBCONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Compile stack for device mode. */ +#define USB_DEVICE + +/* Maximum number of endpoint used, EP0 excluded. If you change this, you must + also change USBEndpoints_EFM32.h to match. */ +#define NUM_EP_USED 6 + +/* Power management modes. The following can be or'd toghether. See comments in + em_usbd.c under "Energy-saving modes" for more details. + + USB_PWRSAVE_MODE_ONSUSPEND Set USB peripheral in low power mode on suspend + + USB_PWRSAVE_MODE_ONVBUSOFF Set USB peripheral in low power mode when not + attached to a host. While this mode assumes that the internal voltage regulator + is used and that the VREGI pin of the chip is connected to VBUS it should + be safe to use given that VREGOSEN is always enabled. If you disable VREGOSEN + you must turn this off. + + USB_PWRSAVE_MODE_ENTEREM2 Enter EM2 when USB peripheral is in low power mode. + On Mbed this allows the sleep() and deepsleep() calls to enter EM2, but + does not automatically enter any sleep states. Entering EM1 is always allowed. + + Note for Happy Gecko, errata USB_E111: Entering EM2 when both the system clock + (HFCLK) and the USB core clock (USBCCLK) is running on USHFRCO will result in + a lock-up. +*/ +#define USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND|USB_PWRSAVE_MODE_ONVBUSOFF|USB_PWRSAVE_MODE_ENTEREM2) + +/* Use dynamic memory to allocate rx/tx buffers in the HAL. Saves memory + as buffers are only allocated for used endpoints. The system malloc + must return memory that is aligned by 4. + + Note: if you disable this, using isochronous endpoints with packet + sizes that are larger than the maximum for other EP types (64) will + not work. */ +#define USB_USE_DYNAMIC_MEMORY + +/* When the USB peripheral is set in low power mode, it must be clocked by a 32kHz + clock. Both LFXO and LFRCO can be used, but only LFXO guarantee USB specification + compliance. */ +#define USB_USBC_32kHz_CLK USB_USBC_32kHz_CLK_LFXO + +/* Uncomment to get some debugging information. Default value for USER_PUTCHAR + should work for SiLabs Gecko boards. Printf requires a working retarget + implementation for write(). */ +//#define DEBUG_USB_API +//#define USB_USE_PRINTF +//#define USER_PUTCHAR ITM_SendChar +//#define DEBUG_USB_INT_HI +//#define DEBUG_USB_INT_LO + + + +#ifdef __cplusplus +} +#endif + +#endif /* __USBCONFIG_H */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/src/em_usbd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/src/em_usbd.c Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,1438 @@ +/**************************************************************************//** + * @file em_usbd.c + * @brief USB protocol stack library, device API. + * @version 3.20.14 + ****************************************************************************** + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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 */ + +static USBD_Device_TypeDef device; +USBD_Device_TypeDef *dev = &device; + +static uint32_t totalRxFifoSize = 0, totalTxFifoSize = 0; +static int numEps = 0; +static int txFifoNum = 1; + +static void USBD_ResetEndpoints(void); +extern USB_Status_TypeDef USBDHAL_ReconfigureFifos( uint32_t totalRxFifoSize, + uint32_t totalTxFifoSize ); +#ifndef __MBED__ +static const char *stateNames[] = +{ + [ USBD_STATE_NONE ] = "NONE ", + [ USBD_STATE_ATTACHED ] = "ATTACHED ", + [ USBD_STATE_POWERED ] = "POWERED ", + [ USBD_STATE_DEFAULT ] = "DEFAULT ", + [ USBD_STATE_ADDRESSED ] = "ADDRESSED ", + [ USBD_STATE_CONFIGURED ] = "CONFIGURED", + [ USBD_STATE_SUSPENDED ] = "SUSPENDED ", + [ USBD_STATE_LASTMARKER ] = "UNDEFINED " +}; +#endif + +/** @endcond */ + +/***************************************************************************//** + * @brief + * Abort all pending transfers. + * + * @details + * Aborts transfers for all endpoints currently in use. Pending + * transfers on the default endpoint (EP0) are not aborted. + ******************************************************************************/ +void USBD_AbortAllTransfers( void ) +{ + INT_Disable(); + USBDHAL_AbortAllTransfers( USB_STATUS_EP_ABORTED ); + INT_Enable(); +} + +/***************************************************************************//** + * @brief + * Abort a pending transfer on a specific endpoint. + * + * @param[in] epAddr + * The address of the endpoint to abort. + ******************************************************************************/ +int USBD_AbortTransfer( int epAddr ) +{ + USB_XferCompleteCb_TypeDef callback; + USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); + + if ( ep == NULL ) + { + DEBUG_USB_API_PUTS( "\nUSBD_AbortTransfer(), Illegal endpoint" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + if ( ep->num == 0 ) + { + DEBUG_USB_API_PUTS( "\nUSBD_AbortTransfer(), Illegal endpoint" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + INT_Disable(); + if ( ep->state == D_EP_IDLE ) + { + INT_Enable(); + return USB_STATUS_OK; + } + + USBD_AbortEp( ep ); + + 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( USB_STATUS_EP_ABORTED ); + callback( USB_STATUS_EP_ABORTED, ep->xferred, ep->remaining ); + } + + INT_Enable(); + return USB_STATUS_OK; +} + +/***************************************************************************//** + * @brief + * Start USB device operation. + * + * @details + * Device operation is started by connecting a pullup resistor on the + * appropriate USB data line. + ******************************************************************************/ +void USBD_Connect( void ) +{ + INT_Disable(); + USBDHAL_Connect(); + INT_Enable(); +} + +/***************************************************************************//** + * @brief + * Stop USB device operation. + * + * @details + * Device operation is stopped by disconnecting the pullup resistor from the + * appropriate USB data line. Often referred to as a "soft" disconnect. + ******************************************************************************/ +void USBD_Disconnect( void ) +{ + INT_Disable(); + USBDHAL_Disconnect(); + INT_Enable(); +} + +/***************************************************************************//** + * @brief + * Check if an endpoint is busy doing a transfer. + * + * @param[in] epAddr + * The address of the endpoint to check. + * + * @return + * True if endpoint is busy, false otherwise. + ******************************************************************************/ +bool USBD_EpIsBusy( int epAddr ) +{ + USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); + + if ( ep == NULL ) + { + DEBUG_USB_API_PUTS( "\nUSBD_EpIsBusy(), Illegal endpoint" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + if ( ep->state == D_EP_IDLE ) + return false; + + return true; +} + +/***************************************************************************//** + * @brief + * Get current USB device state. + * + * @return + * Device USB state. See @ref USBD_State_TypeDef. + ******************************************************************************/ +USBD_State_TypeDef USBD_GetUsbState( void ) +{ + return dev->state; +} + +/***************************************************************************//** + * @brief + * Get a string naming a device USB state. + * + * @param[in] state + * Device USB state. See @ref USBD_State_TypeDef. + * + * @return + * State name string pointer. + ******************************************************************************/ +const char *USBD_GetUsbStateName( USBD_State_TypeDef state ) +{ + if ( state > USBD_STATE_LASTMARKER ) + state = USBD_STATE_LASTMARKER; + +#ifndef __MBED__ + return stateNames[ state ]; +#else + return NULL; +#endif +} + +/***************************************************************************//** + * @brief + * Initializes USB device hardware and internal protocol stack data structures, + * then connects the data-line (D+ or D-) pullup resistor to signal host that + * enumeration can begin. + * + * @note + * You may later use @ref USBD_Disconnect() and @ref USBD_Connect() to force + * reenumeration. + * + * @param[in] p + * Pointer to device initialization struct. See @ref USBD_Init_TypeDef. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int USBD_Init( const USBD_Init_TypeDef *p ) +{ + USBD_Ep_TypeDef *ep; + +#if !defined( USB_CORECLK_HFRCO ) || !defined( CMU_OSCENCMD_USHFRCOEN ) + /* Devices supporting crystal-less USB can use HFRCO or HFXO as core clock. */ + /* All other devices must use HFXO as core clock. */ + if ( CMU_ClockSelectGet( cmuClock_HF ) != cmuSelect_HFXO ) + { + CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); + } +#endif + +#if !defined( CMU_OSCENCMD_USHFRCOEN ) +#if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) + CMU_OscillatorEnable(cmuOsc_LFXO, true, false); +#else + CMU_OscillatorEnable(cmuOsc_LFRCO, true, false); +#endif + +#else + CMU_ClockEnable(cmuClock_CORELE, true); + /* LFC clock is needed to detect USB suspend when LEMIDLE is activated. */ +#if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) + CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFXO); +#else + CMU_ClockSelectSet(cmuClock_LFC, cmuSelect_LFRCO); +#endif + CMU_ClockEnable(cmuClock_USBLE, true); +#endif + + USBTIMER_Init(); + + memset( dev, 0, sizeof( USBD_Device_TypeDef ) ); + + dev->setup = dev->setupPkt; + dev->state = USBD_STATE_LASTMARKER; + dev->savedState = USBD_STATE_NONE; + dev->lastState = USBD_STATE_NONE; + dev->callbacks = p->callbacks; + dev->remoteWakeupEnabled = false; + + /* Initialize EP0 */ + + ep = &dev->ep[ 0 ]; + ep->in = false; + ep->buf = NULL; + ep->num = 0; + ep->mask = 1; + ep->addr = 0; + ep->type = USB_EPTYPE_CTRL; + ep->txFifoNum = 0; + + /* FIXME! */ + ep->packetSize = 64; + dev->ep0MpsCode = _USB_DOEP0CTL_MPS_64B; + + ep->remaining = 0; + ep->xferred = 0; + ep->state = D_EP_IDLE; + ep->xferCompleteCb = NULL; + ep->fifoSize = ep->packetSize / 4; + + totalTxFifoSize = ep->fifoSize * p->bufferingMultiplier[ 0 ]; + totalRxFifoSize = (ep->fifoSize + 1) * p->bufferingMultiplier[ 0 ]; + + /* Rx-FIFO size: SETUP packets : 4*n + 6 n=#CTRL EP's + * GOTNAK : 1 + * Status info : 2*n n=#OUT EP's (EP0 included) in HW + */ + totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) ); + + INT_Disable(); + + /* Enable USB clock */ + CMU->HFCORECLKEN0 |= CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC; + +#if defined( CMU_OSCENCMD_USHFRCOEN ) + CMU->USHFRCOCONF = CMU_USHFRCOCONF_BAND_48MHZ; + CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_USHFRCO ); + + /* Enable USHFRCO Clock Recovery mode. */ + CMU->USBCRCTRL |= CMU_USBCRCTRL_EN; + + /* Turn on Low Energy Mode (LEM) features. */ + USB->CTRL = USB_CTRL_LEMOSCCTRL_GATE + | USB_CTRL_LEMIDLEEN + | USB_CTRL_LEMPHYCTRL; +#else + CMU_ClockSelectSet( cmuClock_USBC, cmuSelect_HFCLK ); +#endif + + USBHAL_DisableGlobalInt(); + + if ( USBDHAL_CoreInit( totalRxFifoSize, totalTxFifoSize ) == USB_STATUS_OK ) + { + USBDHAL_EnableUsbResetAndSuspendInt(); + USBHAL_EnableGlobalInt(); + NVIC_ClearPendingIRQ( USB_IRQn ); + NVIC_EnableIRQ( USB_IRQn ); + } + else + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Init(), FIFO setup error" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) + if ( USBHAL_VbusIsOn() ) + { + USBD_SetUsbState( USBD_STATE_POWERED ); + } + else +#endif + { + USBD_SetUsbState( USBD_STATE_NONE ); + } + + INT_Enable(); + return USB_STATUS_OK; +} + +/***************************************************************************//** + * @brief + * Start a read (OUT) transfer on an endpoint. + * + * @note + * The transfer buffer length must be a multiple of 4 bytes in length and + * WORD (4 byte) aligned. When allocating the buffer, round buffer length up. + * If it is possible that the host will send more data than your device + * expects, round buffer size up to the next multiple of maxpacket size. + * + * @param[in] epAddr + * Endpoint address. + * + * @param[in] data + * Pointer to transfer data buffer. + * + * @param[in] byteCount + * Transfer length. + * + * @param[in] callback + * Function to be called on transfer completion. Supply NULL if no callback + * is needed. See @ref USB_XferCompleteCb_TypeDef. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int USBD_Read( int epAddr, void *data, int byteCount, + USB_XferCompleteCb_TypeDef callback ) +{ + USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); + + USB_PRINTF("USBD: Read addr %x, data %p, size %d, cb 0x%lx\n", + epAddr, data, byteCount, (uint32_t)callback); + + if ( ep == NULL ) + { + DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal endpoint" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + if ( ( byteCount > MAX_XFER_LEN ) || + ( ( byteCount / ep->packetSize ) > MAX_PACKETS_PR_XFER ) ) + { + DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal transfer size" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + if ( (uint32_t)data & 3 ) + { + DEBUG_USB_API_PUTS( "\nUSBD_Read(), Misaligned data buffer" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + INT_Disable(); + if ( USBDHAL_EpIsStalled( ep ) ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Read(), Endpoint is halted" ); + return USB_STATUS_EP_STALLED; + } + + if ( ep->state != D_EP_IDLE ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Read(), Endpoint is busy" ); + return USB_STATUS_EP_BUSY; + } + + if ( ( ep->num > 0 ) && ( USBD_GetUsbState() != USBD_STATE_CONFIGURED ) ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Read(), Device not configured" ); + return USB_STATUS_DEVICE_UNCONFIGURED; + } + + ep->buf = (uint8_t*)data; + ep->remaining = byteCount; + ep->xferred = 0; + + if ( ep->num == 0 ) + { + ep->in = false; + } + else if ( ep->in != false ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Read(), Illegal EP direction" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + ep->state = D_EP_RECEIVING; + ep->xferCompleteCb = callback; + + USBD_ArmEp( ep ); + INT_Enable(); + return USB_STATUS_OK; +} + +/***************************************************************************//** + * @brief + * Perform a remote wakeup signalling sequence. + * + * @note + * It is the responsibility of the application to ensure that remote wakeup + * is not attempted before the device has been suspended for at least 5 + * miliseconds. This function should not be called from within an interrupt + * handler. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int USBD_RemoteWakeup( void ) +{ + INT_Disable(); + + if ( ( dev->state != USBD_STATE_SUSPENDED ) || + ( dev->remoteWakeupEnabled == false ) ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_RemoteWakeup(), Illegal remote wakeup" ); + return USB_STATUS_ILLEGAL; + } + + USBDHAL_SetRemoteWakeup(); + INT_Enable(); + USBTIMER_DelayMs( 10 ); + INT_Disable(); + USBDHAL_ClearRemoteWakeup(); + INT_Enable(); + return USB_STATUS_OK; +} + +/***************************************************************************//** + * @brief + * Check if it is ok to enter energy mode EM2. + * + * @note + * Before entering EM2 both the USB hardware and the USB stack must be in a + * certain state, this function checks if all conditions for entering EM2 + * is met. + * Refer to the @ref usb_device_powersave section for more information. + * + * @return + * True if ok to enter EM2, false otherwise. + ******************************************************************************/ +bool USBD_SafeToEnterEM2( void ) +{ +#if ( USB_PWRSAVE_MODE ) + return USBD_poweredDown ? true : false; +#else + return false; +#endif +} + +/** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ + +void USBD_SetUsbState( USBD_State_TypeDef newState ) +{ + USBD_State_TypeDef currentState; + + currentState = dev->state; + if ( newState == USBD_STATE_SUSPENDED ) + { + dev->savedState = currentState; + } + + dev->lastState = dev->state; + dev->state = newState; + + if ( ( dev->callbacks->usbStateChange ) && + ( currentState != newState ) ) + { + /* When we transition to a state "lower" than CONFIGURED + * we must reset the endpoint data + */ + if ( (dev->lastState == USBD_STATE_CONFIGURED || + dev->lastState == USBD_STATE_SUSPENDED ) && + dev->state < USBD_STATE_CONFIGURED ) + { + USBD_ResetEndpoints(); + } + + dev->callbacks->usbStateChange( currentState, newState ); + } +} + +/** @endcond */ + +/***************************************************************************//** + * @brief + * Set an endpoint in the stalled (halted) state. + * + * @param[in] epAddr + * The address of the endpoint to stall. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int USBD_StallEp( int epAddr ) +{ + USB_Status_TypeDef retVal; + USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); + + if ( ep == NULL ) + { + DEBUG_USB_API_PUTS( "\nUSBD_StallEp(), Illegal request" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + if ( ep->num == 0 ) + { + DEBUG_USB_API_PUTS( "\nUSBD_StallEp(), Illegal endpoint" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + INT_Disable(); + retVal = USBDHAL_StallEp( ep ); + INT_Enable(); + + if ( retVal != USB_STATUS_OK ) + { + retVal = USB_STATUS_ILLEGAL; + } + + return retVal; +} + +/***************************************************************************//** + * @brief + * Stop USB device stack operation. + * + * @details + * The data-line pullup resistor is turned off, USB interrupts are disabled, + * and finally the USB pins are disabled. + ******************************************************************************/ +void USBD_Stop( void ) +{ + USBD_Disconnect(); + NVIC_DisableIRQ( USB_IRQn ); + USBHAL_DisableGlobalInt(); + USBHAL_DisableUsbInt(); + USBHAL_DisablePhyPins(); + USBD_SetUsbState( USBD_STATE_NONE ); + /* Turn off USB clocks. */ + CMU->HFCORECLKEN0 &= ~(CMU_HFCORECLKEN0_USB | CMU_HFCORECLKEN0_USBC); +} + +/***************************************************************************//** + * @brief + * Reset stall state on a stalled (halted) endpoint. + * + * @param[in] epAddr + * The address of the endpoint to un-stall. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int USBD_UnStallEp( int epAddr ) +{ + USB_Status_TypeDef retVal; + USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); + + if ( ep == NULL ) + { + DEBUG_USB_API_PUTS( "\nUSBD_UnStallEp(), Illegal request" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + if ( ep->num == 0 ) + { + DEBUG_USB_API_PUTS( "\nUSBD_UnStallEp(), Illegal endpoint" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + INT_Disable(); + retVal = USBDHAL_UnStallEp( ep ); + INT_Enable(); + + if ( retVal != USB_STATUS_OK ) + { + retVal = USB_STATUS_ILLEGAL; + } + + return retVal; +} + +/***************************************************************************//** + * @brief + * Start a write (IN) transfer on an endpoint. + * + * @param[in] epAddr + * Endpoint address. + * + * @param[in] data + * Pointer to transfer data buffer. This buffer must be WORD (4 byte) aligned. + * + * @param[in] byteCount + * Transfer length. + * + * @param[in] callback + * Function to be called on transfer completion. Supply NULL if no callback + * is needed. See @ref USB_XferCompleteCb_TypeDef. + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int USBD_Write( int epAddr, void *data, int byteCount, + USB_XferCompleteCb_TypeDef callback ) +{ + USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); + + USB_PRINTF("USBD: Write addr %x, data %p, size %d, cb 0x%lx\n", + epAddr, data, byteCount, (uint32_t)callback); + + if ( ep == NULL ) + { + DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal endpoint" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + if ( ( byteCount > MAX_XFER_LEN ) || + ( ( byteCount / ep->packetSize ) > MAX_PACKETS_PR_XFER ) ) + { + DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal transfer size" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + if ( (uint32_t)data & 3 ) + { + DEBUG_USB_API_PUTS( "\nUSBD_Write(), Misaligned data buffer" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + INT_Disable(); + if ( USBDHAL_EpIsStalled( ep ) ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Write(), Endpoint is halted" ); + return USB_STATUS_EP_STALLED; + } + + if ( ep->state != D_EP_IDLE ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Write(), Endpoint is busy" ); + return USB_STATUS_EP_BUSY; + } + + if ( ( ep->num > 0 ) && ( USBD_GetUsbState() != USBD_STATE_CONFIGURED ) ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Write(), Device not configured" ); + return USB_STATUS_DEVICE_UNCONFIGURED; + } + + ep->buf = (uint8_t*)data; + ep->remaining = byteCount; + ep->xferred = 0; + + if ( ep->num == 0 ) + { + ep->in = true; + } + else if ( ep->in != true ) + { + INT_Enable(); + DEBUG_USB_API_PUTS( "\nUSBD_Write(), Illegal EP direction" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + + ep->state = D_EP_TRANSMITTING; + ep->xferCompleteCb = callback; + + USBD_ArmEp( ep ); + INT_Enable(); + return USB_STATUS_OK; +} + +int USBD_SetAddress(uint8_t addr) +{ + int retVal = USB_STATUS_REQ_ERR; + + if ( dev->state == USBD_STATE_DEFAULT ) + { + if ( addr != 0 ) + { + USBD_SetUsbState( USBD_STATE_ADDRESSED ); + } + USBDHAL_SetAddr( addr ); + retVal = USB_STATUS_OK; + } + else if ( dev->state == USBD_STATE_ADDRESSED ) + { + if ( addr == 0 ) + { + USBD_SetUsbState( USBD_STATE_DEFAULT ); + } + USBDHAL_SetAddr( addr ); + retVal = USB_STATUS_OK; + } + + return retVal; +} + +/***************************************************************************//** + * @brief + * Query the stall state of an endpoint + * + * @param[in] epAddr + * The address of the endpoint to query. + * + * @return + * True if endpoint is stalled, false otherwise + ******************************************************************************/ +int USBD_EpIsStalled(int epAddr) +{ + USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); + + if( !ep ) + { + return false; + } + + return USBDHAL_EpIsStalled(ep); +} + +/***************************************************************************//** + * @brief + * Reset (remove) all client endpoints + * + * @details + * Removes client endpoints, and resets the RX/TX fifos. No endpoints + * other than EP0 can be used until added with @ref USBD_AddEndpoint. + ******************************************************************************/ +static void USBD_ResetEndpoints(void) +{ + USBD_Ep_TypeDef *ep = &dev->ep[0]; + + numEps = 0; + txFifoNum = 1; + + totalTxFifoSize = ep->fifoSize * 1; + totalRxFifoSize = (ep->fifoSize + 1) * 1; + totalRxFifoSize += 10 + 1 + ( 2 * (MAX_NUM_OUT_EPS + 1) ); +} + +/***************************************************************************//** + * @brief + * Add a new endpoint + * + * @param[in] epAddr + * Endpoint address + * + * @param[in] transferType + * Endpoint type, one of @ref USB_EPTYPE_BULK, @ref USB_EPTYPE_INTR or + * @ref USB_EPTYPE_ISOC. + * + * @param[in] maxPacketSize + * Maximum packet size of the new endpoint, in bytes + * + * @param[in] bufferMult + * FIFO buffer size multiplier + * + * @return + * @ref USB_STATUS_OK on success, else an appropriate error code. + ******************************************************************************/ +int USBD_AddEndpoint(int epAddr, int transferType, + int maxPacketSize, int bufferMult) +{ + USBD_Ep_TypeDef *ep; + + numEps++; + + ep = &dev->ep[ numEps ]; + ep->in = ( epAddr & USB_SETUP_DIR_MASK ) != 0; + ep->buf = NULL; + ep->addr = epAddr; + ep->num = ep->addr & USB_EPNUM_MASK; + ep->mask = 1 << ep->num; + ep->type = transferType; + ep->packetSize = maxPacketSize; + ep->remaining = 0; + ep->xferred = 0; + ep->state = D_EP_IDLE; + ep->xferCompleteCb = NULL; + + if ( ep->in ) + { + ep->txFifoNum = txFifoNum++; + ep->fifoSize = ( ( ep->packetSize + 3 ) / 4 ) * bufferMult; + dev->inEpAddr2EpIndex[ ep->num ] = numEps; + totalTxFifoSize += ep->fifoSize; + + if ( ep->num > MAX_NUM_IN_EPS ) + { + DEBUG_USB_API_PUTS( "\nUSBD_AddEndpoint(), Illegal IN EP address" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + } + else + { + ep->fifoSize = ( ( ( ep->packetSize + 3 ) / 4 ) + 1 ) * bufferMult; + dev->outEpAddr2EpIndex[ ep->num ] = numEps; + totalRxFifoSize += ep->fifoSize; + + if ( ep->num > MAX_NUM_OUT_EPS ) + { + DEBUG_USB_API_PUTS( "\nUSBD_AddEndpoint(), Illegal OUT EP address" ); + EFM_ASSERT( false ); + return USB_STATUS_ILLEGAL; + } + } + + USB_PRINTF("USBD: Added endpoint %d to slot %d, in %d, addr 0x%x, type %d, ps %d, fifo %ld (total tx %ld, rx %ld)\n", + ep->num, numEps, ep->in, ep->addr, ep->type, ep->packetSize, ep->fifoSize, + totalTxFifoSize, totalRxFifoSize); + + INT_Disable(); +#if defined( CMU_OSCENCMD_USHFRCOEN ) + /* Happy Gecko workaround: disable LEM GATE mode if using ISOC endpoints. */ + if ( transferType == USB_EPTYPE_ISOC ) + { + USB->CTRL = (USB->CTRL & ~_USB_CTRL_LEMOSCCTRL_MASK) | USB_CTRL_LEMOSCCTRL_NONE; + } +#endif + + int ret = USBDHAL_ReconfigureFifos(totalRxFifoSize, totalTxFifoSize); + INT_Enable(); + + if( ret != USB_STATUS_OK ) { + return ret; + } + + USBDHAL_ActivateEp(ep, false); + + return USB_STATUS_OK; +} + + +/***************************************************************************//** + * @brief + * Set an endpoint0 in the stalled (halted) state. + * + * @details + * Temporarily stalls endpoint 0. Used to signal a failure to respond to + * the host's setup packet. + ******************************************************************************/ +void USBD_StallEp0() +{ + int const epAddr = 0; + USBD_Ep_TypeDef *ep = USBD_GetEpFromAddr( epAddr ); + ep->in = true; + USBDHAL_StallEp( ep ); /* Stall Ep0 IN */ + ep->in = false; /* OUT for next SETUP */ + USBDHAL_StallEp( ep ); /* Stall Ep0 OUT */ +#if !defined( USB_DOEP0INT_STUPPKTRCVD ) + USBDHAL_ReenableEp0Setup( dev ); /* Prepare for next SETUP pkt. */ +#else + USBDHAL_StartEp0Setup( dev ); +#endif + ep->state = D_EP_IDLE; +} + +/******** THE REST OF THE FILE IS DOCUMENTATION ONLY !**********************//** + * @{ + +@page usb_device USB device stack library + + The source files for the USB device stack resides in the usb directory + and follows the naming convention: em_usbd<em>nnn</em>.c/h. + + @li @ref usb_device_intro + @li @ref usb_device_api + @li @ref usb_device_conf + @li @ref usb_device_powersave + @li @ref usb_device_example1 + + +@n @section usb_device_intro Introduction + + The USB device protocol stack provides an API which makes it possible to + create USB devices with a minimum of effort. The device stack supports control, + bulk and interrupt transfers. + + The stack is highly configurable to suit various needs, it does also contain + useful debugging features together with several demonstration projects to + get you started fast. + + We recommend that you read through this documentation, then proceed to build + and test a few example projects before you start designing your own device. + +@n @section usb_device_api The device stack API + + This section contains brief descriptions of the functions in the API. You will + find detailed information on input and output parameters and return values by + clicking on the hyperlinked function names. It is also a good idea to study + the code in the USB demonstration projects. + + Your application code must include one header file: @em em_usb.h. + + All functions defined in the API can be called from within interrupt handlers. + + The USB stack use a hardware timer to keep track of time. TIMER0 is the + default choice, refer to @ref usb_device_conf for other possibilities. + Your application must not use the selected timer. + + <b>Pitfalls:</b>@n + The USB peripheral will fill your receive buffers in quantities of WORD's + (4 bytes). Transmit and receive buffers must be WORD aligned, in + addition when allocating storage for receive buffers, round size up to + next WORD boundary. If it is possible that the host will send more data + than your device expects, round buffer size up to the next multiple of + maxpacket size for the relevant endpoint to avoid data corruption. + + Transmit buffers passed to @htmlonly USBD_Write() @endhtmlonly must be + statically allocated because @htmlonly USBD_Write() @endhtmlonly only + initiates the transfer. When the host decide to actually perform the + transfer, your data must be available. + + @n @ref USBD_Init() @n + This function is called to register your device and all its properties with + the device stack. The application must fill in a @ref USBD_Init_TypeDef + structure prior to calling. Refer to @ref DeviceInitCallbacks for the + optional callback functions defined within this structure. When this + function has been called your device is ready to be enumerated by the USB + host. + + @ref USBD_Read(), @ref USBD_Write() @n + These functions initiate data transfers. + @n @htmlonly USBD_Read() @endhtmlonly initiate a transfer of data @em + from host @em to device (an @em OUT transfer in USB terminology). + @n @htmlonly USBD_Write() @endhtmlonly initiate a transfer of data @em from + device @em to host (an @em IN transfer). + + When the USB host actually performs the transfer, your application will be + notified by means of a callback function which you provide (optionally). + Refer to @ref TransferCallback for details of the callback functionality. + + @ref USBD_AbortTransfer(), @ref USBD_AbortAllTransfers() @n + These functions terminate transfers that are initiated, but has not yet + taken place. If a transfer is initiated with @htmlonly USBD_Read() + or USBD_Write(), @endhtmlonly but the USB host never actually peform + the transfers, these functions will deactivate the transfer setup to make + the USB device endpoint hardware ready for new (and potentially) different + transfers. + + @ref USBD_Connect(), @ref USBD_Disconnect() @n + These functions turns the data-line (D+ or D-) pullup on or off. They can be + used to force reenumeration. It's good practice to delay at least one second + between @htmlonly USBD_Disconnect() and USBD_Connect() @endhtmlonly + to allow the USB host to unload the currently active device driver. + + @ref USBD_EpIsBusy() @n + Check if an endpoint is busy. + + @ref USBD_StallEp(), @ref USBD_UnStallEp() @n + These functions stalls or un-stalls an endpoint. This functionality may not + be needed by your application, but the USB device stack use them in response + to standard setup commands SET_FEATURE and CLEAR_FEATURE. They may be useful + when implementing some USB classes, e.g. a mass storage device use them + extensively. + + @ref USBD_RemoteWakeup() @n + Used in SUSPENDED state (see @ref USB_Status_TypeDef) to signal resume to + host. It's the applications responsibility to adhere to the USB standard + which states that a device can not signal resume before it has been + SUSPENDED for at least 5 ms. The function will also check the configuration + descriptor defined by the application to see if it is legal for the device + to signal resume. + + @ref USBD_GetUsbState() @n + Returns the device USB state (see @ref USBD_State_TypeDef). Refer to + Figure 9-1. "Device State Diagram" in the USB revision 2.0 specification. + + @ref USBD_GetUsbStateName() @n + Returns a text string naming a given USB device state. + + @ref USBD_SafeToEnterEM2() @n + Check if it is ok to enter energy mode EM2. Refer to the + @ref usb_device_powersave section for more information. + + @n @anchor TransferCallback <b>The transfer complete callback function:</b> @n + @n USB_XferCompleteCb_TypeDef() is called when a transfer completes. It is + called with three parameters, the status of the transfer, the number of + bytes transferred and the number of bytes remaining. It may not always be + needed to have a callback on transfer completion, but you should keep in + mind that a transfer may be aborted when you least expect it. A transfer + will be aborted if host stalls the endpoint, if host resets your device, if + host unconfigures your device or if you unplug your device cable and the + device is selfpowered. + @htmlonly USB_XferCompleteCb_TypeDef() @endhtmlonly is also called if your + application use @htmlonly USBD_AbortTransfer() or USBD_AbortAllTransfers() + @endhtmlonly calls. + @note This callback is called from within an interrupt handler with + interrupts disabled. + + @n @anchor DeviceInitCallbacks <b>Optional callbacks passed to the stack via + the @ref USBD_Init() function:</b> @n + @n These callbacks are all optional, and it is up to the application + programmer to decide if the application needs the functionality they + provide. + @note These callbacks are all called from within an interrupt handler + with interrupts disabled. + + USBD_UsbResetCb_TypeDef() is called each time reset signalling is sensed on + the USB wire. + + @n USBD_SofIntCb_TypeDef() is called with framenumber as a parameter on + each SOF interrupt. + + @n USBD_DeviceStateChangeCb_TypeDef() is called whenever the device state + change. Useful for detecting e.g. SUSPENDED state change in order to reduce + current consumption of buspowered devices. The USB HID keyboard example + project has a good example on how to use this callback. + + @n USBD_IsSelfPoweredCb_TypeDef() is called by the device stack when host + queries the device with a standard setup GET_STATUS command to check if the + device is currently selfpowered or buspowered. This feature is only + applicable on selfpowered devices which also works when only buspower is + available. + + @n USBD_SetupCmdCb_TypeDef() is called each time a setup command is + received from host. Use this callback to override or extend the default + handling of standard setup commands, and to implement class or vendor + specific setup commands. The USB HID keyboard example project has a good + example on how to use this callback. + + @n <b>Utility functions:</b> @n + @n USB_PUTCHAR() Transmit a single char on the debug serial port. + @n @n USB_PUTS() Transmit a zero terminated string on the debug serial port. + @n @n USB_PRINTF() Transmit "printf" formated data on the debug serial port. + @n @n USB_GetErrorMsgString() Return an error message string for a given + error code. + @n @n USB_PrintErrorMsgString() Format and print a text string given an + error code, prepends an optional user supplied leader string. + @n @n USBTIMER_DelayMs() Active wait millisecond delay function. Can also be + used inside interrupt handlers. + @n @n USBTIMER_DelayUs() Active wait microsecond delay function. Can also be + used inside interrupt handlers. + @n @n USBTIMER_Init() Initialize the timer system. Called by @htmlonly + USBD_Init(), @endhtmlonly but your application must call it again to + reinitialize whenever you change the HFPERCLK frequency. + @n @n USBTIMER_Start() Start a timer. You can configure the USB device stack + to provide any number of timers. The timers have 1 ms resolution, your + application is notified of timeout by means of a callback. + @n @n USBTIMER_Stop() Stop a timer. + +@n @section usb_device_conf Configuring the device stack + + Your application must provide a header file named @em usbconfig.h. This file + must contain the following \#define's:@n @n + @verbatim +#define USB_DEVICE // Compile the stack for device mode. +#define NUM_EP_USED n // Your application use 'n' endpoints in + // addition to endpoint 0. @endverbatim + + @n @em usbconfig.h may define the following items: @n @n + @verbatim +#define NUM_APP_TIMERS n // Your application needs 'n' timers + +#define DEBUG_USB_API // Turn on API debug diagnostics. + +// Some utility functions in the API needs printf. These +// functions have "print" in their name. This macro enables +// these functions. +#define USB_USE_PRINTF // Enable utility print functions. + +// Define a function for transmitting a single char on the serial port. +extern int RETARGET_WriteChar(char c); +#define USER_PUTCHAR RETARGET_WriteChar + +#define USB_TIMER USB_TIMERn // Select which hardware timer the USB stack + // is allowed to use. Valid values are n=0,1,2... + // corresponding to TIMER0, TIMER1, ... + // If not specified, TIMER0 is used + +#define USB_VBUS_SWITCH_NOT_PRESENT // Hardware does not have a VBUS switch + +#define USB_CORECLK_HFRCO // Devices supporting crystal-less USB can use + // HFRCO as core clock, default is HFXO +@endverbatim + + @n You are strongly encouraged to start application development with DEBUG_USB_API + turned on. When DEBUG_USB_API is turned on and USER_PUTCHAR is defined, useful + debugging information will be output on the development kit serial port. + Compiling with the DEBUG_EFM_USER flag will also enable all asserts + in both @em emlib and in the USB stack. If asserts are enabled and + USER_PUTCHAR defined, assert texts will be output on the serial port. + + You application must include @em retargetserial.c if DEBUG_USB_API is defined + and @em retargetio.c if USB_USE_PRINTF is defined. + These files reside in the @em drivers + directory in the software package for your development board. Refer to + @ref usb_device_powersave for energy-saving mode configurations. + +@n @section usb_device_powersave Energy-saving modes + + The device stack provides two energy saving levels. The first level is to + set the USB peripheral in energy saving mode, the next level is to enter + Energy Mode 2 (EM2). These energy saving modes can be applied when the device + is suspended by the USB host, or when when the device is not connected to a + USB host. + In addition to this an application can use energy modes EM1 and EM2. There + are no restrictions on when EM1 can be entered, EM2 can only be entered + when the USB device is suspended or detached from host. + + Energy-saving modes are selected with a \#define in @em usbconfig.h, default + selection is to not use any energy saving modes.@n @n + @verbatim +#define USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ENTEREM2)@endverbatim + + There are three flags available, the flags can be or'ed together as shown above. + + <b>\#define USB_PWRSAVE_MODE_ONSUSPEND</b>@n Set USB peripheral in low power + mode on suspend. + + <b>\#define USB_PWRSAVE_MODE_ONVBUSOFF</b>@n Set USB peripheral in low power + mode when not attached to a host. This mode assumes that the internal voltage + regulator is used and that the VREGI pin of the chip is connected to VBUS. + This option can not be used with bus-powered devices. + + <b>\#define USB_PWRSAVE_MODE_ENTEREM2</b>@n Enter EM2 when USB peripheral is + in low power mode. + + When the USB peripheral is set in low power mode, it must be clocked by a 32kHz + clock. Both LFXO and LFRCO can be used, but only LFXO guarantee USB specification + compliance. Selection is done with a \#define in @em usbconfig.h.@n @n + @verbatim +#define USB_USBC_32kHz_CLK USB_USBC_32kHz_CLK_LFXO @endverbatim + Two flags are available, <b>USB_USBC_32kHz_CLK_LFXO</b> and + <b>USB_USBC_32kHz_CLK_LFRCO</b>. <b>USB_USBC_32kHz_CLK_LFXO</b> is selected + by default. + + The USB HID keyboard and Mass Storage device example projects demonstrate + different energy-saving modes. + + <b>Example 1:</b> + Leave all energy saving to the stack, the device enters EM2 on suspend and + when detached from host. @n + @verbatim +In usbconfig.h: + +#define USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ONVBUSOFF | USB_PWRSAVE_MODE_ENTEREM2) + @endverbatim + + @n <b>Example 2:</b> + Let the stack control energy saving in the USB periheral but let your + application control energy modes EM1 and EM2. @n + @verbatim +In usbconfig.h: + +#define USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ONVBUSOFF) + +In application code: + +if ( USBD_SafeToEnterEM2() ) + EMU_EnterEM2(true); +else + EMU_EnterEM1(); @endverbatim + +@n @section usb_device_example1 Vendor unique device example application + + This example represents the most simple USB device imaginable. It's purpose + is to turn user LED's on or off under control of vendor unique setup commands. + The device will rely on @em libusb device driver on the host, a host + application @em EFM32-LedApp.exe is bundled with the example. + + The main() is really simple ! @n @n + @verbatim +#include "em_usb.h" + +#include "descriptors.h" + +int main( void ) +{ + BSP_Init(BSP_INIT_DEFAULT); // Initialize DK board register access + CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); + BSP_LedsSet(0); // Turn off all LED's + + ConsoleDebugInit(); // Initialize UART for debug diagnostics + + USB_PUTS( "\nEFM32 USB LED Vendor Unique Device example\n" ); + + USBD_Init( &initstruct ); // GO ! + + //When using a debugger it is pratical to uncomment the following three + //lines to force host to re-enumerate the device. + + //USBD_Disconnect(); + //USBTIMER_DelayMs( 1000 ); + //USBD_Connect(); + + for (;;) {} +} @endverbatim + + @n Configure the device stack in <em>usbconfig.h</em>: @n @n + @verbatim +#define USB_DEVICE // Compile stack for device mode. + +// ************************************************************************** +** ** +** Specify number of endpoints used (in addition to EP0). ** +** ** +***************************************************************************** +#define NUM_EP_USED 0 // EP0 is the only endpoint used. + +// ************************************************************************** +** ** +** Configure serial port debug output. ** +** ** +***************************************************************************** +// Prototype a function for transmitting a single char on the serial port. +extern int RETARGET_WriteChar(char c); +#define USER_PUTCHAR RETARGET_WriteChar + +// Enable debug diagnostics from API functions (illegal input params etc.) +#define DEBUG_USB_API @endverbatim + + @n Define device properties and fill in USB initstruct in + <em>descriptors.h</em>: @n @n + @verbatim +EFM32_ALIGN(4) +static const USB_DeviceDescriptor_TypeDef deviceDesc __attribute__ ((aligned(4))) = +{ + .bLength = USB_DEVICE_DESCSIZE, + .bDescriptorType = USB_DEVICE_DESCRIPTOR, + .bcdUSB = 0x0200, + .bDeviceClass = 0xFF, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = USB_FS_CTRL_EP_MAXSIZE, + .idVendor = 0x10C4, + .idProduct = 0x0001, + .bcdDevice = 0x0000, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1 +}; + +EFM32_ALIGN(4) +static const uint8_t configDesc[] __attribute__ ((aligned(4)))= +{ + // *** Configuration descriptor *** + USB_CONFIG_DESCSIZE, // bLength + USB_CONFIG_DESCRIPTOR, // bDescriptorType + USB_CONFIG_DESCSIZE + // wTotalLength (LSB) + USB_INTERFACE_DESCSIZE, + (USB_CONFIG_DESCSIZE + // wTotalLength (MSB) + USB_INTERFACE_DESCSIZE)>>8, + 1, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + CONFIG_DESC_BM_RESERVED_D7 | // bmAttrib: Self powered + CONFIG_DESC_BM_SELFPOWERED, + CONFIG_DESC_MAXPOWER_mA( 100 ), // bMaxPower: 100 mA + + // *** Interface descriptor *** + USB_INTERFACE_DESCSIZE, // bLength + USB_INTERFACE_DESCRIPTOR, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + NUM_EP_USED, // bNumEndpoints + 0xFF, // bInterfaceClass + 0, // bInterfaceSubClass + 0, // bInterfaceProtocol + 0, // iInterface +}; + +STATIC_CONST_STRING_DESC_LANGID( langID, 0x04, 0x09 ); +STATIC_CONST_STRING_DESC( iManufacturer, 'E','n','e','r','g','y',' ', \ + 'M','i','c','r','o',' ','A','S' ); +STATIC_CONST_STRING_DESC( iProduct , 'V','e','n','d','o','r',' ', \ + 'U','n','i','q','u','e',' ', \ + 'L','E','D',' ', \ + 'D','e','v','i','c','e' ); +STATIC_CONST_STRING_DESC( iSerialNumber, '0','0','0','0','0','0', \ + '0','0','1','2','3','4' ); + +static const void * const strings[] = +{ + &langID, + &iManufacturer, + &iProduct, + &iSerialNumber +}; + +// Endpoint buffer sizes +// 1 = single buffer, 2 = double buffering, 3 = tripple buffering ... +static const uint8_t bufferingMultiplier[ NUM_EP_USED + 1 ] = { 1 }; + +static const USBD_Callbacks_TypeDef callbacks = +{ + .usbReset = NULL, + .usbStateChange = NULL, + .setupCmd = SetupCmd, + .isSelfPowered = NULL, + .sofInt = NULL +}; + +static const USBD_Init_TypeDef initstruct = +{ + .deviceDescriptor = &deviceDesc, + .configDescriptor = configDesc, + .stringDescriptors = strings, + .numberOfStrings = sizeof(strings)/sizeof(void*), + .callbacks = &callbacks, + .bufferingMultiplier = bufferingMultiplier +}; @endverbatim + + @n Now we have to implement vendor unique USB setup commands to control the + LED's (see callbacks variable above). Notice that the buffer variable below is + statically allocated because @htmlonly USBD_Write() @endhtmlonly only + initiates the transfer. When the host actually performs the transfer, the + SetupCmd() function will have returned ! @n @n + + @verbatim +#define VND_GET_LEDS 0x10 +#define VND_SET_LED 0x11 + +static int SetupCmd( const USB_Setup_TypeDef *setup ) +{ + int retVal; + uint16_t leds; + static uint32_t buffer; + uint8_t *pBuffer = (uint8_t*)&buffer; + + retVal = USB_STATUS_REQ_UNHANDLED; + + if ( setup->Type == USB_SETUP_TYPE_VENDOR ) + { + switch ( setup->bRequest ) + { + case VND_GET_LEDS: + // ******************** + *pBuffer = BSP_LedsGet() & 0x1F; + retVal = USBD_Write( 0, pBuffer, setup->wLength, NULL ); + break; + + case VND_SET_LED: + // ******************** + leds = DVK_getLEDs() & 0x1F; + if ( setup->wValue ) + { + leds |= LED0 << setup->wIndex; + } + else + { + leds &= ~( LED0 << setup->wIndex ); + } + BSP_LedsSet( leds ); + retVal = USB_STATUS_OK; + break; + } + } + + return retVal; +}@endverbatim + + * @}**************************************************************************/ + +#endif /* defined( USB_DEVICE ) */ +#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/src/em_usbdep.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/src/em_usbdep.c Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,446 @@ +/**************************************************************************//** + * @file em_usbdep.c + * @brief USB protocol stack library, USB device endpoint handlers. + * @version 3.20.14 + ****************************************************************************** + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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" + +#ifdef USB_USE_PRINTF +static const char *epStatusStr[] = { + "IDLE","TRANS","RECV","IN_S","OUT_S" +}; +#endif + +/** @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. + */ +#if !defined( USB_DOEP0INT_STUPPKTRCVD ) +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 ]; + +#ifdef __MBED__ + + (void)xferred; + (void)statusIn; + (void)status; + + USB_PRINTF("USBDEP: ep0 %s, rem %ld, z %d\n", epStatusStr[ep->state], ep->remaining, ep->zlp); + + 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 ); + } + + USBDHAL_ReenableEp0Setup(device); + } + else + { + device->callbacks->setupCmd(device->setup); + } + +#else /* not __MBED__ */ + + 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_EP0_IN_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_EP0_IN_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_EP0_OUT_STATUS; + } + } + break; + + case D_EP0_IN_STATUS: + case D_EP0_OUT_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; + } +#endif /* __MBED__ */ +} +#endif + +#if defined( USB_DOEP0INT_STUPPKTRCVD ) +void USBDEP_Ep0Handler( USBD_Device_TypeDef *device ) +{ + int status; + USBD_Ep_TypeDef *ep; + static uint32_t xferred; + static USB_XferCompleteCb_TypeDef callback; + +#ifdef __MBED__ + + (void)xferred; + (void)status; + + ep = &device->ep[ 0 ]; + + 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 ); + } + + USBDHAL_StartEp0Setup( dev ); + } + else + { + device->callbacks->setupCmd(device->setup); + } + +#else + + ep = &device->ep[ 0 ]; + + switch ( ep->state ) + { + case D_EP_IDLE: + ep->zlp = 0; + ep->remaining = 0; + callback = NULL; + + 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_StartEp0Setup( dev ); /* 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 + { + /* No data stage, a ZLP may have been sent. If not, send one */ + xferred = 0; + if ( ep->zlp == 0 ) + { + ep->state = D_EP_IDLE; + USBD_Write( 0, NULL, 0, NULL ); /* ACK to host */ + ep->state = D_EP0_IN_STATUS; + } + } + } + } + break; + + case D_EP_RECEIVING: + if ( ep->remaining ) + { + ep->in = false; + 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_StartEp0Setup( dev ); /* Prepare for next SETUP pkt. */ + ep->state = D_EP_IDLE; + } + else + { + + USBDHAL_StartEp0Setup( dev ); /* 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_EP0_IN_STATUS; + } + } + break; + + case D_EP_TRANSMITTING: + if ( ep->remaining ) + { + ep->in = true; + USBD_ReArmEp0( ep ); + } + else + { + 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 */ + ep->state = D_EP0_OUT_STATUS; + } + } + break; + + case D_EP0_IN_STATUS: + if ( ( USB->DOEP0CTL & USB_DOEP0CTL_EPENA ) == 0 ) + { + /* Prepare for more SETUP Packets */ + USBDHAL_StartEp0Setup( dev ); + } + if ( callback != NULL ) + { + callback( USB_STATUS_OK, xferred, 0 ); + } + ep->state = D_EP_IDLE; + ep->in = false; /* OUT for next SETUP */ + break; + + case D_EP0_OUT_STATUS: + USBDHAL_StartEp0Setup( dev ); /* Prepare for more SETUP Packets */ + if ( callback != NULL ) + { + callback( USB_STATUS_OK, xferred, 0 ); + } + ep->state = D_EP_IDLE; + ep->in = false; /* OUT for next SETUP */ + break; + } +#endif /* __MBED__ */ +} +#endif + +/* + * 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 ) */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/src/em_usbdint.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/src/em_usbdint.c Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,946 @@ +/**************************************************************************//** + * @file em_usbdint.c + * @brief USB protocol stack library, USB device peripheral interrupt handlers. + * @version 3.20.14 + ****************************************************************************** + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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" + +#ifdef __MBED__ +extern void usbhal_allow_em2(bool em2_allow); +#endif + +/** @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 ); +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 defined( USB_DOEP0INT_STUPPKTRCVD ) +static void HandleOutEpIntr( uint32_t status, USBD_Ep_TypeDef *ep ); +#else +static void ProcessSetup ( void ); +static void ProcessOepData ( USBD_Ep_TypeDef *ep ); +#endif + +#if ( USB_PWRSAVE_MODE ) +/* Variables and prototypes for USB powerdown (suspend) functionality. */ +static bool UsbPowerDown( void ); +static bool UsbPowerUp( void ); + +volatile bool USBD_poweredDown = false; + +/* Storage for backing up USB core registers. */ +static uint32_t x_USB_GINTMSK; +#if defined(_USB_GOTGCTL_MASK) +static uint32_t x_USB_GOTGCTL; +#endif +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 + +#if ( USB_PWRSAVE_MODE ) +static uint32_t cmuStatus = 0; +#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; + bool servedVbusInterrupt = false; + + INT_Disable(); + +#if ( USB_PWRSAVE_MODE ) + if ( USBD_poweredDown ) + { + /* Switch USBC clock from 32kHz to a 48MHz clock to be able to */ + /* read USB peripheral registers. */ + /* If we woke up from EM2, HFCLK is now HFRCO. */ + + /* Restore clock oscillators.*/ +#if defined( CMU_OSCENCMD_USHFRCOEN ) + if ( ( CMU->STATUS & CMU_STATUS_USHFRCOENS ) == 0 )/*Wakeup from EM2 ?*/ + { + CMU->OSCENCMD = ( cmuStatus + & ( CMU_STATUS_AUXHFRCOENS | CMU_STATUS_HFXOENS ) ) + | CMU_OSCENCMD_USHFRCOEN; + } +#else + if ( ( CMU->STATUS & CMU_STATUS_HFXOENS ) == 0 ) /* Wakeup from EM2 ? */ + { + CMU->OSCENCMD = cmuStatus + & ( CMU_STATUS_AUXHFRCOENS | CMU_STATUS_HFXOENS ); + } +#endif + + /* Select correct USBC clock.*/ +#if defined( CMU_OSCENCMD_USHFRCOEN ) + CMU->CMD = CMU_CMD_USBCCLKSEL_USHFRCO; + while ( ( CMU->STATUS & CMU_STATUS_USBCUSHFRCOSEL ) == 0 ){} +#else + CMU->CMD = CMU_CMD_USBCCLKSEL_HFCLKNODIV; + while ( ( CMU->STATUS & CMU_STATUS_USBCHFCLKSEL ) == 0 ){} +#endif + } +#endif /* if ( USB_PWRSAVE_MODE ) */ + + if ( USB->IF && ( USB->CTRL & USB_CTRL_VREGOSEN ) ) + { + 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 ( USB_PWRSAVE_MODE ) + if ( UsbPowerUp() ) + { + USBDHAL_EnableUsbResetAndSuspendInt(); + } + USBD_SetUsbState( USBD_STATE_POWERED ); +#endif + } + } + + if ( USB->IF & USB_IF_VREGOSL ) + { + USB->IFC = USB_IFC_VREGOSL; + + if ( ( USB->STATUS & USB_STATUS_VREGOS ) == 0 ) + { + servedVbusInterrupt = true; + DEBUG_USB_INT_LO_PUTS( "\nVboF" ); + +#if ( USB_PWRSAVE_MODE ) +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) + if ( !USBD_poweredDown ) + { + USB->GINTMSK = 0; + USB->GINTSTS = 0xFFFFFFFF; + } + + UsbPowerDown(); +#endif + USBD_SetUsbState( USBD_STATE_NONE ); +#endif + } + } + } + + status = USBHAL_GetCoreInts(); + if ( status == 0 ) + { + INT_Enable(); + if ( !servedVbusInterrupt ) + { + DEBUG_USB_INT_LO_PUTS( "\nSinT" ); + } + return; + } + + HANDLE_INT( USB_GINTSTS_RESETDET ) + HANDLE_INT( USB_GINTSTS_WKUPINT ) + HANDLE_INT( USB_GINTSTS_USBSUSP ) + HANDLE_INT( USB_GINTSTS_SOF ) + 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 + + USBDHAL_Ep0Activate( dev->ep0MpsCode ); + 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 ) + { + USB_DINEPS[ epnum ].INT = USB_DIEP_INT_XFERCOMPL; + + DEBUG_USB_INT_HI_PUTCHAR( 'c' ); + + if ( epnum == 0 ) + { + if ( ep->remaining > ep->packetSize ) + { + ep->remaining -= ep->packetSize; + ep->xferred += ep->packetSize; + } + else + { + ep->xferred += ep->remaining; + ep->remaining = 0; + } + USBDEP_Ep0Handler( dev ); + } + else + { + ep->xferred = ep->remaining - + ( ( USB_DINEPS[ epnum ].TSIZ & + _USB_DIEP_TSIZ_XFERSIZE_MASK ) >> + _USB_DIEP_TSIZ_XFERSIZE_SHIFT ); + ep->remaining -= ep->xferred; + + USBDEP_EpHandler( ep->addr ); +#if defined( USB_DOEP0INT_STUPPKTRCVD ) + if ( USB_DINEPS[ ep->num ].INT & USB_DIEP_INT_NAKINTRPT ) + { + USB_DINEPS[ ep->num ].INT = USB_DIEP_INT_NAKINTRPT; + } +#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 defined( USB_DOEP0INT_STUPPKTRCVD ) + HandleOutEpIntr( status, ep ); +#else + if ( status & USB_DOEP_INT_XFERCOMPL ) + { + USB_DOUTEPS[ epnum ].INT = USB_DOEP_INT_XFERCOMPL; + DEBUG_USB_INT_HI_PUTCHAR( 'c' ); + ProcessOepData( ep ); + } + + /* Setup Phase Done */ + if ( status & USB_DOEP0INT_SETUP ) + { + ProcessSetup(); + } +#endif + } + } +} + +#if !defined( USB_DOEP0INT_STUPPKTRCVD ) +static void ProcessOepData( USBD_Ep_TypeDef *ep ) +{ + if ( ep->num == 0 ) + { + +#ifdef __MBED__ + int xfer_size = ep->packetSize - (( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_XFERSIZE_MASK ) + >> _USB_DOEP0TSIZ_XFERSIZE_SHIFT); + int setup_pkt_received = USBDHAL_GetOutEpInts( ep ) & USB_DOEP0INT_SETUP; + + if ( (!setup_pkt_received && xfer_size == 0) || + (setup_pkt_received && xfer_size == 8) ) + { + /* Higher levels need to see the correct transfer amount for ZLPs */ + ep->remaining = 0; + ep->xferred = 0; + } + else + { + /* FIXME - does not work if actual read size > 56 */ + if ( setup_pkt_received ) xfer_size -= 8; + + ep->xferred = xfer_size; + ep->remaining -= xfer_size; + } +#else + 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 + { + ep->xferred = ep->hwXferSize - + ( ( USB_DOUTEPS[ ep->num ].TSIZ & _USB_DOEP_TSIZ_XFERSIZE_MASK )>> + _USB_DOEP_TSIZ_XFERSIZE_SHIFT ); + ep->remaining -= ep->xferred; + USBDEP_EpHandler( ep->addr ); + } +} +#endif + +#if !defined( USB_DOEP0INT_STUPPKTRCVD ) +static void ProcessSetup( void ) +{ + DEBUG_USB_INT_LO_PUTS( "\nS" ); + + if ( USB->DOEP0INT & USB_DOEP0INT_BACK2BACKSETUP ) + { /* Back to back setup packets received */ + USB->DOEP0INT = USB_DOEP0INT_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 ]; + } + USB->DOEP0TSIZ |= 3 << _USB_DOEP0TSIZ_SUPCNT_SHIFT; + USB->DOEP0DMAADDR = (uint32_t)dev->setupPkt; + USB->DOEP0INT = USB_DOEP0INT_SETUP; + + USBDEP_Ep0Handler( dev ); /* Call the SETUP handler for EP0 */ +} +#endif + +/* + * Handle USB reset detected interrupt in suspend mode. + */ +static void Handle_USB_GINTSTS_RESETDET ( void ) +{ +#if ( USB_PWRSAVE_MODE ) + if ( ! USBD_poweredDown ) + { + USB->GINTSTS = USB_GINTSTS_RESETDET; + } + + if ( UsbPowerUp() ) + { + USB->GINTSTS = USB_GINTSTS_RESETDET; + } + +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ONVBUSOFF ) + /* Power down immediately if VBUS is off. */ + if ( ! ( USB->STATUS & USB_STATUS_VREGOS ) ) + { + UsbPowerDown(); + } +#endif + +#else + USB->GINTSTS = USB_GINTSTS_RESETDET; +#endif /* if ( USB_PWRSAVE_MODE ) */ + + if ( USB->STATUS & USB_STATUS_VREGOS ) + { + USBD_SetUsbState( USBD_STATE_DEFAULT ); + } + else + { + USBD_SetUsbState( USBD_STATE_NONE ); + } + DEBUG_USB_INT_LO_PUTS( "RsuP\n" ); +} + +/* + * 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; +#if defined( USB_DOEPMSK_STSPHSERCVDMSK ) + USB->DOEPMSK = USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK + | USB_DOEPMSK_STSPHSERCVDMSK; +#else + USB->DOEPMSK = USB_DOEPMSK_SETUPMSK | USB_DOEPMSK_XFERCOMPLMSK; +#endif + 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" ); + + if ( USBD_GetUsbState() == USBD_STATE_NONE ) + { + USBD_SetUsbState( USBD_STATE_POWERED ); + } + + state = USBD_GetUsbState(); + if ( ( state == USBD_STATE_POWERED ) || + ( state == USBD_STATE_DEFAULT ) || + ( state == USBD_STATE_ADDRESSED ) || + ( state == USBD_STATE_CONFIGURED ) ) + { +#if ( USB_PWRSAVE_MODE ) + UsbPowerDown(); +#endif + USBD_SetUsbState( USBD_STATE_SUSPENDED ); + } +} + +/* + * Handle USB port wakeup interrupt. + */ +static void Handle_USB_GINTSTS_WKUPINT( void ) +{ +#if ( USB_PWRSAVE_MODE ) + if ( ! USBD_poweredDown ) + { + USB->GINTSTS = USB_GINTSTS_WKUPINT; + } + + if ( UsbPowerUp() ) + { + USB->GINTSTS = USB_GINTSTS_WKUPINT; + USBDHAL_StartEp0Setup( dev ); + USBDHAL_Ep0Activate( dev->ep0MpsCode ); + } +#else + USB->GINTSTS = USB_GINTSTS_WKUPINT; +#endif + + 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; + DEBUG_USB_INT_LO_PUTCHAR( '\\' ); + + /* Backup USB core registers. */ + x_USB_GINTMSK = USB->GINTMSK; +#if defined(_USB_GOTGCTL_MASK) + x_USB_GOTGCTL = USB->GOTGCTL; +#endif + 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; + + /* Record current clock settings. */ + cmuStatus = CMU->STATUS; + +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ENTEREM2 ) +#ifndef __MBED__ + /* Enter EM2 on interrupt exit. */ + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk; +#else + usbhal_allow_em2(true); +#endif +#endif + + /* Switch USBC clock to 32 kHz. */ +#if ( USB_USBC_32kHz_CLK == USB_USBC_32kHz_CLK_LFXO ) + CMU->CMD = CMU_CMD_USBCCLKSEL_LFXO; + while ( ( CMU->STATUS & CMU_STATUS_USBCLFXOSEL ) == 0 ){} +#else + CMU->CMD = CMU_CMD_USBCCLKSEL_LFRCO; + while ( ( CMU->STATUS & CMU_STATUS_USBCLFRCOSEL ) == 0 ){} +#endif + + return true; + } + return false; +} +#endif /* if ( USB_PWRSAVE_MODE ) */ + +#if ( USB_PWRSAVE_MODE ) +/* + * Exit USB core partial powerdown mode, restore essential USB core registers. + * Will prevent re-entry back to EM2. + * Returns true if a powerup sequence was performed. + */ +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 ( USBD_poweredDown ) + { + USBD_poweredDown = false; + DEBUG_USB_INT_LO_PUTCHAR( '/' ); + +#if !defined( USB_CORECLK_HFRCO ) || !defined( CMU_OSCENCMD_USHFRCOEN ) + /* Switch HFCLK from HFRCO to HFXO. */ + CMU_ClockSelectSet( cmuClock_HF, cmuSelect_HFXO ); +#endif + + /* Turn off HFRCO when not needed. */ + if ( ( cmuStatus & CMU_STATUS_HFRCOENS ) == 0 ) + { + CMU->OSCENCMD = CMU_OSCENCMD_HFRCODIS; + } + + /* Exit partial powerdown mode. */ + USB->PCGCCTL &= ~USB_PCGCCTL_STOPPCLK; + USB->PCGCCTL &= ~(USB_PCGCCTL_PWRCLMP | USB_PCGCCTL_RSTPDWNMODULE); + + if (( USB->GINTSTS & ( USB_GINTSTS_WKUPINT | USB_GINTSTS_RESETDET ) ) == 0) + { + USB->DCTL = x_USB_DCTL | USB_DCTL_RMTWKUPSIG; + USB->DCTL = x_USB_DCTL; + } + + /* 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; + + tmp = x_USB_EP_CTL[ i ] & + ~( USB_DIEP_CTL_CNAK | USB_DIEP_CTL_SNAK | + USB_DIEP_CTL_SETD0PIDEF | USB_DIEP_CTL_SETD1PIDOF ); + + if ( x_USB_EP_CTL[ i ] & USB_DIEP_CTL_DPIDEOF ) + tmp |= USB_DIEP_CTL_SETD1PIDOF; + else + tmp |= USB_DIEP_CTL_SETD0PIDEF; + + if ( x_USB_EP_CTL[ i ] & USB_DIEP_CTL_NAKSTS ) + tmp |= USB_DIEP_CTL_SNAK; + else + tmp |= USB_DIEP_CTL_CNAK; + + if ( ep->in ) + { + USB_DINEPS[ epNum ].CTL = tmp; + USB_DINEPS[ epNum ].TSIZ = x_USB_EP_TSIZ[ i ]; + USB_DINEPS[ epNum ].DMAADDR = x_USB_EP_DMAADDR[ i ]; + } + else + { + USB_DOUTEPS[ epNum ].CTL = tmp; + 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; +#if defined(_USB_GOTGCTL_MASK) + USB->GOTGCTL = x_USB_GOTGCTL; +#endif + USB->GINTMSK = x_USB_GINTMSK; + + USB->DCTL |= USB_DCTL_PWRONPRGDONE; + +#if ( USB_PWRSAVE_MODE & USB_PWRSAVE_MODE_ENTEREM2 ) +#ifndef __MBED__ + /* Do not reenter EM2 on interrupt exit. */ + SCB->SCR &= ~(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk); +#else + usbhal_allow_em2(false); +#endif +#endif + + return true; + } + return false; +} +#endif /* if ( USB_PWRSAVE_MODE ) */ + +#if defined( USB_DOEP0INT_STUPPKTRCVD ) +static void HandleOutEpIntr( uint32_t status, USBD_Ep_TypeDef *ep ) +{ + uint32_t doeptsiz; + + if ( ep->num == 0 ) + { + if ( status & USB_DOEP0INT_XFERCOMPL ) + { + USB->DOEP0INT = USB_DOEP0INT_XFERCOMPL; + doeptsiz = USB->DOEP0TSIZ; + + if ( ep->state == D_EP_IDLE ) + { + if ( status & USB_DOEP0INT_STUPPKTRCVD ) + { + USB->DOEP0INT = USB_DOEP0INT_STUPPKTRCVD; + } + status = USBDHAL_GetOutEpInts( ep ); + doeptsiz = USB->DOEP0TSIZ; + + if ( status & USB_DOEP0INT_SETUP ) + { +retry: + /* Already started data stage, clear setup */ + USB->DOEP0INT = USB_DOEP0INT_SETUP; + status &= ~USB_DOEP0INT_SETUP; + { + int supCnt = ( doeptsiz & _USB_DOEP0TSIZ_SUPCNT_MASK ) + >> _USB_DOEP0TSIZ_SUPCNT_SHIFT; + + if ( supCnt == 3 ) + supCnt = 2; + + dev->setup = &dev->setupPkt[ 2 - supCnt ]; + } + DEBUG_USB_INT_LO_PUTS( "\nS" ); + USBDEP_Ep0Handler( dev ); + + /* Prepare for more setup packets */ + if ( ep->state == D_EP0_IN_STATUS || ep->state == D_EP_TRANSMITTING ) + { + USBDHAL_StartEp0Setup( dev ); + } + } + else /* xfercompl && idle && !setup */ + { + status = USBDHAL_GetOutEpInts( ep ); + if ( status & USB_DOEP0INT_SETUP ) + goto retry; + USBDHAL_StartEp0Setup( dev ); + } + } + else /* ep0state != EP0_IDLE */ + { +#ifdef __MBED__ + if ( ep->state == D_EP_RECEIVING ) + { + int xfer_size = ep->packetSize - (( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_XFERSIZE_MASK ) + >> _USB_DOEP0TSIZ_XFERSIZE_SHIFT); + int setup_pkt_received = status & USB_DOEP0INT_SETUP; + + if ( (!setup_pkt_received && xfer_size == 0) || + (setup_pkt_received && xfer_size == 8) ) + { + /* Higher levels need to see the correct transfer amount for ZLPs */ + ep->remaining = 0; + ep->xferred = 0; + } + else + { + /* FIXME - does not work if actual read size > 56 */ + if ( setup_pkt_received ) xfer_size -= 8; + + ep->xferred = xfer_size; + ep->remaining -= xfer_size; + } + + USBDEP_Ep0Handler( dev ); + } +#else + if ( ep->state == D_EP_RECEIVING ) + { + if ( ep->remaining > ep->packetSize ) + { + ep->remaining -= ep->packetSize; + ep->xferred += ep->packetSize; + } + else + { + ep->xferred += ep->remaining; + ep->remaining = 0; + } + USBDEP_Ep0Handler( dev ); + } + else if ( ep->state == D_EP0_OUT_STATUS ) + { + USBDEP_Ep0Handler( dev ); + } +#endif + } + } /* if ( status & USB_DOEP0INT_XFERCOMPL ) */ + + if ( status & USB_DOEP0INT_STSPHSERCVD ) + { + USB->DOEP0INT = USB_DOEP0INT_STSPHSERCVD; + } + + if ( status & USB_DOEP0INT_SETUP ) + { + USB->DOEP0INT = USB_DOEP0INT_SETUP; + { + int supCnt = ( USB->DOEP0TSIZ & _USB_DOEP0TSIZ_SUPCNT_MASK ) + >> _USB_DOEP0TSIZ_SUPCNT_SHIFT; + + if ( supCnt == 3 ) + supCnt = 2; + + dev->setup = &dev->setupPkt[ 2 - supCnt ]; + } + DEBUG_USB_INT_LO_PUTS( "\nS" ); + USBDEP_Ep0Handler( dev ); + } + } + else /* epnum != 0 */ + { + if ( status & USB_DOEP_INT_XFERCOMPL ) + { + USB_DOUTEPS[ ep->num ].INT = USB_DOEP_INT_XFERCOMPL; + + ep->xferred = ep->hwXferSize - + ( ( USB_DOUTEPS[ ep->num ].TSIZ & _USB_DOEP_TSIZ_XFERSIZE_MASK )>> + _USB_DOEP_TSIZ_XFERSIZE_SHIFT ); + ep->remaining -= ep->xferred; + + USBDEP_EpHandler( ep->addr ); + } + } +} +#endif + +/** @endcond */ + +#endif /* defined( USB_DEVICE ) */ +#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/src/em_usbhal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/src/em_usbhal.c Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,799 @@ +/**************************************************************************//** + * @file em_usbhal.c + * @brief USB protocol stack library, low level USB peripheral access. + * @version 3.20.14 + ****************************************************************************** + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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 */ + +#define EPABORT_BREAK_LOOP_COUNT 15000 /* Approx. 100 ms */ + +/* 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); + + /* Core Soft Reset */ + USB->GRSTCTL |= USB_GRSTCTL_CSFTRST; + while ( USB->GRSTCTL & USB_GRSTCTL_CSFTRST ) {} + + USBTIMER_DelayUs( 1 ); + + /* Wait for AHB master IDLE state. */ + while ( !( USB->GRSTCTL & USB_GRSTCTL_AHBIDLE ) ) {} +} + +#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; + +#if !defined( USB_VBUS_SWITCH_NOT_PRESENT ) + CMU_ClockEnable( cmuClock_GPIO, true ); + GPIO_PinModeSet( gpioPortF, 5, gpioModePushPull, 0 ); /* Enable VBUSEN pin */ + USB->ROUTE = USB_ROUTE_PHYPEN | USB_ROUTE_VBUSENPEN; /* Enable PHY pins. */ +#else + USB->ROUTE = USB_ROUTE_PHYPEN; /* Enable PHY pins. */ +#endif + + USBHAL_CoreReset(); /* Reset USB core */ + +#if defined( USB_GUSBCFG_FORCEHSTMODE ) + /* Force Device Mode */ + USB->GUSBCFG = ( USB->GUSBCFG & + ~(GUSBCFG_WO_BITMASK | USB_GUSBCFG_FORCEHSTMODE ) ) | + USB_GUSBCFG_FORCEDEVMODE; +#endif + + 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; + + USB->GAHBCFG = ( USB->GAHBCFG & ~_USB_GAHBCFG_HBSTLEN_MASK ) | + USB_GAHBCFG_DMAEN | USB_GAHBCFG_HBSTLEN_INCR; + + /* Ignore frame numbers on ISO transfers. */ + USB->DCTL = ( USB->DCTL & ~DCTL_WO_BITMASK ) | USB_DCTL_IGNRFRMNUM; + + /* Set Rx FIFO size */ + start = EFM32_MAX( totalRxFifoSize, MIN_EP_FIFO_SIZE_INWORDS ); + USB->GRXFSIZ = ( start << _USB_GRXFSIZ_RXFDEP_SHIFT ) & + _USB_GRXFSIZ_RXFDEP_MASK; + + /* Set Tx EP0 FIFO size */ + depth = EFM32_MAX( dev->ep[ 0 ].fifoSize, MIN_EP_FIFO_SIZE_INWORDS ); + 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 = EFM32_MAX( ep->fifoSize, MIN_EP_FIFO_SIZE_INWORDS ); + USB_DIEPTXFS[ ep->txFifoNum - 1 ] = + ( depth << _USB_DIEPTXF1_INEPNTXFDEP_SHIFT ) | + ( start & _USB_DIEPTXF1_INEPNTXFSTADDR_MASK ); + } + } + } + } + + if ( totalRxFifoSize + totalTxFifoSize > MAX_DEVICE_FIFO_SIZE_INWORDS ) + return USB_STATUS_ILLEGAL; + + if ( start > MAX_DEVICE_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_DCTL_SFTDISCON_DEFAULT != 0 ) + USBD_Connect(); +#endif + + /* 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; + + return USB_STATUS_OK; +} + +USB_Status_TypeDef USBDHAL_ReconfigureFifos( uint32_t totalRxFifoSize, + uint32_t totalTxFifoSize ) +{ + uint8_t i, j; + uint16_t start, depth; + USBD_Ep_TypeDef *ep; + + /* Set Rx FIFO size */ + start = EFM32_MAX( totalRxFifoSize, MIN_EP_FIFO_SIZE_INWORDS ); + USB->GRXFSIZ = ( start << _USB_GRXFSIZ_RXFDEP_SHIFT ) & + _USB_GRXFSIZ_RXFDEP_MASK; + + /* Set Tx EP0 FIFO size */ + depth = EFM32_MAX( dev->ep[ 0 ].fifoSize, MIN_EP_FIFO_SIZE_INWORDS ); + 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 = EFM32_MAX( ep->fifoSize, MIN_EP_FIFO_SIZE_INWORDS ); + USB_DIEPTXFS[ ep->txFifoNum - 1 ] = + ( depth << _USB_DIEPTXF1_INEPNTXFDEP_SHIFT ) | + ( start & _USB_DIEPTXF1_INEPNTXFSTADDR_MASK ); + } + } + } + } + + if ( totalRxFifoSize + totalTxFifoSize > MAX_DEVICE_FIFO_SIZE_INWORDS ) + return USB_STATUS_ILLEGAL; + + if ( start > MAX_DEVICE_FIFO_SIZE_INWORDS ) + return USB_STATUS_ILLEGAL; + + /* Flush the FIFO's */ + USBHAL_FlushTxFifo( 0x10 ); /* All Tx FIFO's */ + USBHAL_FlushRxFifo(); /* The Rx FIFO */ + + 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 ) +{ + int cnt; + + /* 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 */ + cnt = EPABORT_BREAK_LOOP_COUNT; + while ( !( USB->GINTSTS & USB_GINTSTS_GOUTNAKEFF ) && cnt ) + { + cnt--; + } + + 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 */ + cnt = EPABORT_BREAK_LOOP_COUNT; + while ( !( USBDHAL_GetOutEpInts( ep ) & USB_DOEP_INT_EPDISBLD ) && cnt ) + { + cnt--; + } + + 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, cnt; + 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 */ + cnt = EPABORT_BREAK_LOOP_COUNT; + while ( !( USB->GINTSTS & USB_GINTSTS_GOUTNAKEFF ) && cnt ) + { + cnt--; + } + 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; + cnt = EPABORT_BREAK_LOOP_COUNT; + 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; + } + } + } + cnt--; + } while ( im && cnt ); + 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; + cnt = EPABORT_BREAK_LOOP_COUNT; + 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; + } + } + } + cnt--; + } while ( ( im || om ) && cnt ); + + 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; + + if ( reason != USB_STATUS_DEVICE_RESET ) + { + 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; /* Enable PHY pins. */ + 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 ); + + USB->GAHBCFG = ( USB->GAHBCFG & ~_USB_GAHBCFG_HBSTLEN_MASK ) | + USB_GAHBCFG_DMAEN | USB_GAHBCFG_HBSTLEN_INCR; + + /* 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 + { + __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 ) +{ + hcchar |= USB_HC_CHAR_CHENA | USB_HC_CHAR_CHDIS; + 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: + case USB_EPTYPE_INTR: + USB->HC[ hcnum ].INTMSK = USB_HC_INT_CHHLTD; + 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 ( 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 */ + hc->hwXferSize = len; + 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 ); +} +#endif /* defined( USB_HOST ) */ + +/** @endcond */ + +#endif /* defined( USB_DEVICE ) || defined( USB_HOST ) */ +#endif /* defined( USB_PRESENT ) && ( USB_COUNT == 1 ) */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/TARGET_Silicon_Labs/src/em_usbtimer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/TARGET_Silicon_Labs/src/em_usbtimer.c Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,381 @@ +/***************************************************************************//** + * @file em_usbtimer.c + * @brief USB protocol stack library, timer API. + * @version 3.20.14 + ******************************************************************************* + * @section License + * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b> + ******************************************************************************* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#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" + +#include "device_peripherals.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 +#error HW platform must define the timer to use for USB +#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 ); + } + + if ( timeout == 0 ) + { + callback(); + INT_Enable(); + return; + } + + 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 ) */
diff -r f3cad7e6984e -r 2af474687369 USBDevice/USBEndpoints.h --- a/USBDevice/USBEndpoints.h Wed Jul 08 14:46:06 2015 +0100 +++ b/USBDevice/USBEndpoints.h Thu Aug 13 15:46:06 2015 +0100 @@ -49,6 +49,8 @@ #include "USBEndpoints_RZ_A1H.h" #elif defined(TARGET_Maxim) #include "USBEndpoints_Maxim.h" +#elif defined(TARGET_EFM32GG_STK3700) || defined(TARGET_EFM32LG_STK3600) || defined(TARGET_EFM32WG_STK3800) || defined(TARGET_EFM32HG_STK3400) +#include "USBEndpoints_EFM32.h" #else #error "Unknown target type" #endif
diff -r f3cad7e6984e -r 2af474687369 USBDevice/USBEndpoints_EFM32.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/USBEndpoints_EFM32.h Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,65 @@ + +#ifndef TARGET_EFM32HG_STK3400 +# define NUMBER_OF_LOGICAL_ENDPOINTS (6) +#else +# define NUMBER_OF_LOGICAL_ENDPOINTS (3) +#endif + +#define NUMBER_OF_PHYSICAL_ENDPOINTS (NUMBER_OF_LOGICAL_ENDPOINTS * 2) +#define NUMBER_OF_ENDPOINTS (NUMBER_OF_PHYSICAL_ENDPOINTS + 2) /* Includes EP0 */ + +#define EP0OUT (0) +#define EP0IN (1) +#define EP1OUT (2) +#define EP1IN (3) +#define EP2OUT (4) +#define EP2IN (5) +#define EP3OUT (6) +#define EP3IN (7) +#ifndef TARGET_EFM32HG_STK3400 +# define EP4OUT (8) +# define EP4IN (9) +# define EP5OUT (10) +# define EP5IN (11) +# define EP6OUT (12) +# define EP6IN (13) +#endif + +#define USB_EP_TO_INDEX(ep) (ep) +#define USB_EP_TO_ADDR(ep) (((ep)>>1) | (((ep) & 1 ) ? 0x80 : 0x00)) +#define USB_ADDR_TO_EP(ep) (((ep)<<1) | (((ep) & 0x80) ? 0x01 : 0x00)) + +/* Maximum Packet sizes */ + +#define MAX_PACKET_SIZE_EP0 64 +#define MAX_PACKET_SIZE_EP1 64 +#define MAX_PACKET_SIZE_EP2 64 +#define MAX_PACKET_SIZE_EP3 64 +#ifndef TARGET_EFM32HG_STK3400 +# define MAX_PACKET_SIZE_EP4 64 +# define MAX_PACKET_SIZE_EP5 64 +# define MAX_PACKET_SIZE_EP6 64 +#endif + +/* Generic endpoints - intended to be portable accross devices */ +/* and be suitable for simple USB devices. */ + +/* Bulk endpoints */ +#define EPBULK_OUT EP2OUT +#define EPBULK_IN EP2IN +#define EPBULK_OUT_callback EP2_OUT_callback +#define EPBULK_IN_callback EP2_IN_callback +/* Interrupt endpoints */ +#define EPINT_OUT EP1OUT +#define EPINT_IN EP1IN +#define EPINT_OUT_callback EP1_OUT_callback +#define EPINT_IN_callback EP1_IN_callback +/* Isochronous endpoints */ +#define EPISO_OUT EP3OUT +#define EPISO_IN EP3IN +#define EPISO_OUT_callback EP3_OUT_callback +#define EPISO_IN_callback EP3_IN_callback + +#define MAX_PACKET_SIZE_EPBULK 64 +#define MAX_PACKET_SIZE_EPINT 64 +#define MAX_PACKET_SIZE_EPISO 1023
diff -r f3cad7e6984e -r 2af474687369 USBDevice/USBHAL_EFM32.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBDevice/USBHAL_EFM32.cpp Thu Aug 13 15:46:06 2015 +0100 @@ -0,0 +1,775 @@ +/* Copyright 2015 Silicon Labs, http://www.silabs.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined TARGET_EFM32GG_STK3700 || \ + defined TARGET_EFM32LG_STK3600 || \ + defined TARGET_EFM32WG_STK3800 || \ + defined TARGET_EFM32HG_STK3400 + +#include "USBHAL.h" +#include "em_usb.h" +#include "em_usbtypes.h" +#include "em_usbhal.h" +#include "em_usbd.h" + +#include "sleepmodes.h" + +enum USBISRCommand { + CMD_HANDLED = 0, + CMD_EP0SETUP, + CMD_EP0IN, + CMD_EP0OUT, + CMD_EP_XFER_COMPLETED, + CMD_SOF, + CMD_BUSRESET, + CMD_SUSPEND_STATE_CHANGED, + CMD_ENUM_END_MARKER +}; + +enum IEPStatus { + NOT_CONFIGURED = 0, + IDLE = 1, + READ_PENDING = 2, + WRITE_PENDING = 3, + READ_COMPLETE = 4, + WRITE_COMPLETE = 5, + FAILED_INVALID = 6, + FAILED_STALLED = 7 +}; + +typedef struct { + IEPStatus status; + uint32_t byte_count; + uint32_t max_packet; + USB_XferCompleteCb_TypeDef intern_cb; + uint8_t *data_buf; +} ep_state_t; + +USBHAL * USBHAL::instance; +static uint8_t ep0setupdata[8]; +static ep_state_t ep_state[NUMBER_OF_ENDPOINTS]; +#ifdef USB_USE_DYNAMIC_MEMORY +static uint8_t ep0in_data_buf[MAX_PACKET_SIZE_EP0] __attribute__ ((aligned (4))); +static uint8_t ep0out_data_buf[MAX_PACKET_SIZE_EP0]; // FIXME: does this need to be this big? +#else +static uint8_t ep_data_buf[NUMBER_OF_ENDPOINTS][64] __attribute__ ((aligned (4))); +#endif + +static void run_cmd(USBISRCommand cmd, uint32_t param); +static void (*isrptr)() = NULL; +static USBISRCommand usb_isrcmd = CMD_HANDLED; +static uint32_t usb_isrcmd_param = 0; + +extern "C" void usbhal_allow_em2(bool allow_em2); + +#ifdef DEBUG_USB_API +#define TRACE(fmt,...) printf("USB: %s: " fmt "\n", __func__, __VA_ARGS__); +#define TRACE_FUNC_IN printf("USB: > %s\n",__func__); +#define TRACE_FUNC_IN_P(fmt, ...) printf("USB: > %s: " fmt "\n", __func__, __VA_ARGS__); +#else +#define TRACE(fmt,...) +#define TRACE_FUNC_IN +#define TRACE_FUNC_IN_P(fmt, ...) +#endif + +static EP_STATUS internEndpointRead(uint8_t ep, uint32_t maxSize); + +static int usbhal_xfer_complete_cb(uint8_t epaddr, USB_Status_TypeDef status, + uint32_t xferred, uint32_t remaining); +static void usbhal_free_buffers(void); + +/* Internal EP transfer complete callbacks */ +#define EPCB(n) static int usbhal_xfer_complete_cb_##n(USB_Status_TypeDef status, \ + uint32_t xferred, uint32_t remaining) { \ + return usbhal_xfer_complete_cb(n, status, xferred, remaining); \ +} +/* ------^ */ +EPCB(EP0OUT) +EPCB(EP0IN) +EPCB(EP1OUT) +EPCB(EP1IN) +EPCB(EP2OUT) +EPCB(EP2IN) +EPCB(EP3OUT) +EPCB(EP3IN) +#ifndef TARGET_EFM32HG_STK3400 +EPCB(EP4OUT) +EPCB(EP4IN) +EPCB(EP5OUT) +EPCB(EP5IN) +EPCB(EP6OUT) +EPCB(EP6IN) +#endif + +static inline bool is_aligned(const void *pointer, size_t byte_count) +{ + return ((uintptr_t)pointer % byte_count == 0); +} + +USBHAL::USBHAL(void) +{ + TRACE_FUNC_IN; + + isrptr = &USBHAL::_usbisr; + + if (instance) { + TRACE("Assert self failed! instance=%p", instance); + abort(); + } + instance = this; + + // When USB is active, we can't go below EM1. This block may + // be dynamically removed/reinstated to allow deeper sleep. + usbhal_allow_em2(false); + + // When in suspend / Vbus off we can go to EM2, but never below + // that as long as USB is being used. Despite the name the call here + // blocks entering modes _below_ EM2, but allows EM2. + blockSleepMode(EM2); + + epCallback[EP0OUT] = NULL; + epCallback[EP0IN ] = NULL; + epCallback[EP1OUT] = &USBHAL::EP1_OUT_callback; + epCallback[EP1IN ] = &USBHAL::EP1_IN_callback; + epCallback[EP2OUT] = &USBHAL::EP2_OUT_callback; + epCallback[EP2IN ] = &USBHAL::EP2_IN_callback; + epCallback[EP3OUT] = &USBHAL::EP3_OUT_callback; + epCallback[EP3IN ] = &USBHAL::EP3_IN_callback; +#ifndef TARGET_EFM32HG_STK3400 + epCallback[EP4OUT] = &USBHAL::EP4_OUT_callback; + epCallback[EP4IN ] = &USBHAL::EP4_IN_callback; + epCallback[EP5OUT] = &USBHAL::EP5_OUT_callback; + epCallback[EP5IN ] = &USBHAL::EP5_IN_callback; + epCallback[EP6OUT] = &USBHAL::EP6_OUT_callback; + epCallback[EP6IN ] = &USBHAL::EP6_IN_callback; +#endif + + memset(ep_state, 0, sizeof(ep_state)); + + ep_state[EP0OUT].intern_cb = usbhal_xfer_complete_cb_EP0OUT; + ep_state[EP0IN ].intern_cb = usbhal_xfer_complete_cb_EP0IN; + ep_state[EP1OUT].intern_cb = usbhal_xfer_complete_cb_EP1OUT; + ep_state[EP1IN ].intern_cb = usbhal_xfer_complete_cb_EP1IN; + ep_state[EP2OUT].intern_cb = usbhal_xfer_complete_cb_EP2OUT; + ep_state[EP2IN ].intern_cb = usbhal_xfer_complete_cb_EP2IN; + ep_state[EP3OUT].intern_cb = usbhal_xfer_complete_cb_EP3OUT; + ep_state[EP3IN ].intern_cb = usbhal_xfer_complete_cb_EP3IN; +#ifndef TARGET_EFM32HG_STK3400 + ep_state[EP4OUT].intern_cb = usbhal_xfer_complete_cb_EP4OUT; + ep_state[EP4IN ].intern_cb = usbhal_xfer_complete_cb_EP4IN; + ep_state[EP5OUT].intern_cb = usbhal_xfer_complete_cb_EP5OUT; + ep_state[EP5IN ].intern_cb = usbhal_xfer_complete_cb_EP5IN; + ep_state[EP6OUT].intern_cb = usbhal_xfer_complete_cb_EP6OUT; + ep_state[EP6IN ].intern_cb = usbhal_xfer_complete_cb_EP6IN; +#endif + +#ifdef USB_USE_DYNAMIC_MEMORY + ep_state[EP0OUT].data_buf = ep0out_data_buf; + ep_state[EP0IN].data_buf = ep0in_data_buf; +#else + for (int i=0 ; i<NUMBER_OF_ENDPOINTS ; i++) { + ep_state[i].data_buf = ep_data_buf[i]; + } +#endif +} + +USBHAL::~USBHAL(void) +{ + TRACE_FUNC_IN; + USBD_AbortAllTransfers(); + USBD_Disconnect(); + usbhal_free_buffers(); + + usbhal_allow_em2(true); + unblockSleepMode(EM2); +} + +extern "C" void usbhal_allow_em2(bool allow_em2) +{ + if (allow_em2) { + // unblockSleepMode is safe to call even if we would unblock + // an already unblocked mode, so no checks here. + unblockSleepMode(EM1); + } else { + blockSleepMode(EM1); + } +} + +static void usbhal_reset_cb(void) +{ + TRACE_FUNC_IN; + run_cmd(CMD_BUSRESET, 0); +} + +#ifdef DEBUG_USB_API +static const char *usbstate[] = { "NONE", "ATTACHED", "POWERED", "DEFAULT", + "ADDRESSED", "CONFIGURED", "SUSPENDED", "???" }; +#endif + +static void usbhal_state_change_cb(USBD_State_TypeDef oldState, + USBD_State_TypeDef newState) +{ + TRACE("state changed %s -> %s", usbstate[oldState], usbstate[newState]); + + if (oldState == USBD_STATE_SUSPENDED) { + run_cmd(CMD_SUSPEND_STATE_CHANGED, 0); + } + + if (newState == USBD_STATE_SUSPENDED) { + run_cmd(CMD_SUSPEND_STATE_CHANGED, 1); + } + + // Should call connectStateChanged from here as well but there is + // no documentation on when to actually do so. (And the implementation + // in USBDevice.cpp is a stub) + + // HACK! Since connectStateChanged is not used, indicate the loss + // off connection by reporting a bus reset. This causes USBDevice + // to realise that at least it's not in CONFIGURED anymore, and + // stop trying to read/write in a busyloop. + if (newState == USBD_STATE_NONE) { + run_cmd(CMD_BUSRESET, 0); + } +} + +static int usbhal_setupcmd_cb(const USB_Setup_TypeDef *setup) +{ + TRACE_FUNC_IN; + if (!setup) { + EFM_ASSERT(false); + return USB_STATUS_REQ_ERR; + } + + memcpy(ep0setupdata, setup, 8); + run_cmd(CMD_EP0SETUP, 0); + + return USB_STATUS_OK; +} + +static void usbhal_sof_cb(uint16_t frameNum) +{ + run_cmd(CMD_SOF, frameNum); +} + +static void usbhal_free_buffers(void) +{ +#ifdef USB_USE_DYNAMIC_MEMORY + TRACE_FUNC_IN; + + for (int i=EP1OUT ; i<NUMBER_OF_ENDPOINTS ; i++ ) { + if (ep_state[i].data_buf) { + free(ep_state[i].data_buf); + ep_state[i].data_buf = NULL; + } + } +#endif +} + +void USBHAL::connect(void) +{ + TRACE_FUNC_IN; + + // Init datastructures must be static - driver will use these even after the init function exits! + + static const uint8_t buffer_multiplier[] = { 1 }; // Mult 1 for control EP + static const USBD_Callbacks_TypeDef usbd_callbacks = { + .usbReset = usbhal_reset_cb, + .usbStateChange = usbhal_state_change_cb, + .setupCmd = usbhal_setupcmd_cb, + .isSelfPowered = NULL, + .sofInt = usbhal_sof_cb + }; + + USBD_Init_TypeDef initdata = { + .deviceDescriptor = NULL, + .configDescriptor = NULL, + .stringDescriptors = NULL, + .numberOfStrings = 0, + .bufferingMultiplier = buffer_multiplier, + .callbacks = &usbd_callbacks, + .reserved = 0 + }; + + int ret = USBD_Init(&initdata); + + TRACE("init = %d, devicedesc = %lx, configdesc = %lx", ret, + (uint32_t) initdata.deviceDescriptor, + (uint32_t) initdata.configDescriptor); + + EFM_ASSERT(ret == USB_STATUS_OK); +} + +void USBHAL::disconnect(void) +{ + TRACE_FUNC_IN; + USBD_Disconnect(); +} + +void USBHAL::configureDevice(void) +{ + TRACE_FUNC_IN; + USBD_SetUsbState(USBD_STATE_CONFIGURED); +} + +void USBHAL::unconfigureDevice(void) +{ + TRACE_FUNC_IN; + USBD_SetUsbState(USBD_STATE_DEFAULT); + usbhal_free_buffers(); +} + +void USBHAL::setAddress(uint8_t address) +{ + TRACE_FUNC_IN_P("addr 0x%x", (unsigned)address); + USBD_SetAddress(address); +} + +void USBHAL::remoteWakeup(void) +{ + TRACE_FUNC_IN; + USBD_RemoteWakeup(); +} + +void USBHAL::EP0setup(uint8_t *buffer) +{ + TRACE_FUNC_IN; + EFM_ASSERT(buffer); + if (buffer) { + memcpy(buffer, ep0setupdata, 8); + } +} + +void USBHAL::EP0read(void) +{ + TRACE_FUNC_IN; + (void)internEndpointRead(0, MAX_PACKET_SIZE_EP0); +} + +void USBHAL::EP0readStage(void) +{ + TRACE_FUNC_IN; + // Not needed +} + +uint32_t USBHAL::EP0getReadResult(uint8_t *buffer) +{ + TRACE_FUNC_IN; + EFM_ASSERT(buffer); + + uint32_t read = 0; + endpointReadResult(0, buffer, &read); + return read; +} + +static int usbhal_xfer_complete_cb(uint8_t ep, USB_Status_TypeDef status, + uint32_t xferred, uint32_t remaining) +{ + TRACE_FUNC_IN_P("ep 0x%x, status %u, xferred %lu, rem %lu", + ep, status, xferred, remaining); + + if (ep >= NUMBER_OF_ENDPOINTS) { + EFM_ASSERT(false); + return USB_STATUS_REQ_ERR; + } + + switch (ep) { + case EP0OUT: + if (ep_state[EP0OUT].status == READ_PENDING) { + ep_state[EP0OUT].status = READ_COMPLETE; + ep_state[EP0OUT].byte_count = xferred; + // drop zlp + if (xferred == 0) { + break; + } + } + run_cmd(CMD_EP0OUT, 0); + break; + + case EP0IN: + run_cmd(CMD_EP0IN, 0); + break; + + default: + bool write = ep & 1; + + if (status == USB_STATUS_OK) { + if (!write && ep_state[ep].status == READ_PENDING) { + ep_state[ep].status = READ_COMPLETE; + ep_state[ep].byte_count = xferred; + } else if (write && ep_state[ep].status == WRITE_PENDING) { + ep_state[ep].status = WRITE_COMPLETE; + } else { + ep_state[ep].status = FAILED_INVALID; + } + } else { + ep_state[ep].status = FAILED_INVALID; + } + + if (ep_state[ep].status != FAILED_INVALID) { + run_cmd(CMD_EP_XFER_COMPLETED, ep); + } + break; + } + + return USB_STATUS_OK; +} + +void USBHAL::EP0write(uint8_t *buffer, uint32_t size) +{ + //TRACE_FUNC_IN_P("buffer %lx, size %lu", (uint32_t) buffer, size); + + int ret; + USB_XferCompleteCb_TypeDef cb = ep_state[EP0IN].intern_cb; + + EFM_ASSERT((buffer != NULL) || (size == 0)); + EFM_ASSERT(size <= MAX_PACKET_SIZE_EP0); + + if (!buffer || size == 0) { + // No callback after writing EP0 ZLP + cb = NULL; + } + + if (buffer && !is_aligned(buffer,4)) { + // Copy unaligned data to write-buffer before USBD_Write + memcpy(ep_state[EP0IN].data_buf, buffer, size); + ret = USBD_Write(0, ep_state[EP0IN].data_buf, size, cb); + } else { + ret = USBD_Write(0, buffer, size, cb); + } + + if (ret != USB_STATUS_OK) { + TRACE("FAILED - ret %d", ret); + } +} + +void USBHAL::EP0stall(void) +{ + TRACE_FUNC_IN; + USBD_StallEp0(); +} + +static EP_STATUS internEndpointRead(uint8_t ep, uint32_t maxSize) +{ + //TRACE_FUNC_IN_P("endpoint 0x%x, size %ld, cb %d", (unsigned)ep, maxSize, useCallback); + + if (ep >= NUMBER_OF_ENDPOINTS) { + EFM_ASSERT(false); + return EP_INVALID; + } + + ep_state[ep].status = READ_PENDING; + + int ret = USBD_Read(USB_EP_TO_ADDR(ep), ep_state[ep].data_buf, maxSize, + ep_state[ep].intern_cb); + + if (ret == USB_STATUS_OK) { + return EP_PENDING; + } else { + TRACE("FAILED - ret %d", ret); + + if (ret == USB_STATUS_EP_STALLED) { + return EP_STALLED; + } else { + return EP_INVALID; + } + } +} + +EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize) +{ + return internEndpointRead(endpoint, maximumSize); +} + +EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t *data, uint32_t *bytesRead) +{ + TRACE_FUNC_IN; + + if (endpoint >= NUMBER_OF_ENDPOINTS) { + EFM_ASSERT(false); + return EP_INVALID; + } + + EFM_ASSERT(data); + EFM_ASSERT(bytesRead); + if (!data || !bytesRead) { + return EP_INVALID; + } + + switch (ep_state[endpoint].status) { + case READ_PENDING: + return EP_PENDING; + + case READ_COMPLETE: + memcpy(data, ep_state[endpoint].data_buf, ep_state[endpoint].byte_count); + *bytesRead = ep_state[endpoint].byte_count; + ep_state[endpoint].status = IDLE; + return EP_COMPLETED; + + case FAILED_STALLED: + ep_state[endpoint].status = IDLE; + return EP_STALLED; + + default: + ep_state[endpoint].status = IDLE; + return EP_INVALID; + } +} + +EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) +{ + TRACE_FUNC_IN_P("endpoint 0x%x, data 0x%lx, size %lu", (unsigned )endpoint, (uint32_t)data, size); + + EFM_ASSERT(endpoint < NUMBER_OF_ENDPOINTS); + EFM_ASSERT(endpoint > EP0IN); + EFM_ASSERT(size <= ep_state[endpoint].max_packet); + EFM_ASSERT(data); + + uint8_t ep = USB_EP_TO_INDEX(endpoint); + + if (endpoint >= NUMBER_OF_ENDPOINTS || endpoint <= EP0IN) { + return EP_INVALID; + } + + if (size > ep_state[endpoint].max_packet) { + return EP_INVALID; + } + + if (!data) { + return EP_INVALID; + } + + memcpy(ep_state[ep].data_buf, data, size); + + ep_state[ep].status = WRITE_PENDING; + int ret = USBD_Write(USB_EP_TO_ADDR(endpoint), ep_state[ep].data_buf, size, ep_state[ep].intern_cb); + + if (ret == USB_STATUS_EP_STALLED) { + ep_state[ep].status = IDLE; + return EP_STALLED; + } else if (ret != USB_STATUS_OK) { + ep_state[ep].status = IDLE; + return EP_INVALID; + } + + return EP_PENDING; +} + +EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint) +{ + if (endpoint >= NUMBER_OF_ENDPOINTS) { + EFM_ASSERT(false); + return EP_INVALID; + } + + switch (ep_state[endpoint].status) { + case WRITE_PENDING: + return EP_PENDING; + + case WRITE_COMPLETE: + ep_state[endpoint].status = IDLE; + return EP_COMPLETED; + + case FAILED_STALLED: + ep_state[endpoint].status = IDLE; + return EP_STALLED; + + default: + ep_state[endpoint].status = IDLE; + return EP_INVALID; + } +} + +void USBHAL::stallEndpoint(uint8_t endpoint) +{ + TRACE_FUNC_IN; + + EFM_ASSERT(endpoint < NUMBER_OF_ENDPOINTS); + EFM_ASSERT((endpoint != EP0OUT) && (endpoint != EP0IN)); + + USBD_StallEp(USB_EP_TO_ADDR(endpoint)); +} + +void USBHAL::unstallEndpoint(uint8_t endpoint) +{ + TRACE_FUNC_IN; + + EFM_ASSERT(endpoint < NUMBER_OF_ENDPOINTS); + EFM_ASSERT((endpoint != EP0OUT) && (endpoint != EP0IN)); + + USBD_UnStallEp(USB_EP_TO_ADDR(endpoint)); +} + +bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t options) +{ + TRACE_FUNC_IN_P("endpoint %d, packetsize %ld, options 0x%lx", endpoint, + maxPacket, options); + + int mult = 1; // RX/TX buffer size multiplier + int type = USB_EPTYPE_INTR; + + if (endpoint >= NUMBER_OF_ENDPOINTS) { + EFM_ASSERT(false); + return false; + } + + if (endpoint == EP0IN || endpoint == EP0OUT) { + EFM_ASSERT(false); + return false; + } + + ep_state[endpoint].max_packet = 0; + + if (endpoint == EPISO_OUT || endpoint == EPISO_IN) { + if (maxPacket > MAX_PACKET_SIZE_EPISO) { + EFM_ASSERT(false); + return false; + } + } else if ((maxPacket > MAX_PACKET_SIZE_EPBULK) || (maxPacket > MAX_PACKET_SIZE_EPINT)) { + EFM_ASSERT(false); + return false; + } + + // USBDevice performs a read right after creating the endpoints, + // before calling configureDevice. The read will fail since + // at that point the device state is still ADDRESSED. Workaround + // is to force configured state here. + // + // This relies on USBDevice to not call realiseEndpoint unless + // it is transitioning to the CONFIGURED state. + USBD_SetUsbState(USBD_STATE_CONFIGURED); + + // Why doesn't this function have a type param? This is silly... + switch (endpoint) { + case EPBULK_OUT: + case EPBULK_IN: + type = USB_EPTYPE_BULK; + mult = 2; + break; + case EPINT_OUT: + case EPINT_IN: + type = USB_EPTYPE_INTR; + mult = 1; + break; + case EPISO_OUT: + case EPISO_IN: + type = USB_EPTYPE_ISOC; + mult = 2; // ? + break; + } + + // Some options force the endpoint to a specific type + if( options & ISOCHRONOUS ) { + type = USB_EPTYPE_ISOC; + mult = 2; // ? + } else if ( options & RATE_FEEDBACK_MODE ) { + // No support for whatever rate feedback is, but for interrupt only + type = USB_EPTYPE_INTR; + mult = 1; + } + +#ifdef USB_USE_DYNAMIC_MEMORY + if (ep_state[endpoint].data_buf) { + free(ep_state[endpoint].data_buf); + } + + ep_state[endpoint].data_buf = (uint8_t *)malloc(maxPacket); + + if (!ep_state[endpoint].data_buf) { + EFM_ASSERT(false); + return false; + } +#endif + + int ret = USBD_AddEndpoint(USB_EP_TO_ADDR(endpoint), type, maxPacket, mult); + + if (ret == USB_STATUS_OK) { + ep_state[endpoint].status = IDLE; + ep_state[endpoint].max_packet = maxPacket; + return true; + } else { + return false; + } +} + +bool USBHAL::getEndpointStallState(unsigned char endpoint) +{ + TRACE_FUNC_IN; + if (endpoint >= NUMBER_OF_ENDPOINTS) { + EFM_ASSERT(false); + return false; + } + return USBD_EpIsStalled(USB_EP_TO_ADDR(endpoint)); +} + +static void run_cmd(USBISRCommand cmd, uint32_t param) +{ + if (usb_isrcmd != CMD_HANDLED || cmd >= CMD_ENUM_END_MARKER) { + EFM_ASSERT(false); + abort(); + } + + usb_isrcmd = cmd; + usb_isrcmd_param = param; + isrptr(); +} + +void USBHAL::_usbisr(void) +{ + EFM_ASSERT(instance); + instance->usbisr(); +} + +void USBHAL::usbisr(void) +{ + //TRACE_FUNC_IN; + + // This "ISR" is used just to route callbacks from SiL USB driver + // callback context (which can not call protected/private USBHAL + // methods), to the actual USBHAL. + + EFM_ASSERT(usb_isrcmd != CMD_HANDLED); + switch (usb_isrcmd) { + case CMD_EP0SETUP: + this->EP0setupCallback(); + break; + case CMD_EP0IN: + this->EP0in(); + break; + case CMD_EP0OUT: + this->EP0out(); + break; + case CMD_BUSRESET: + this->busReset(); + break; + case CMD_EP_XFER_COMPLETED: + if (epCallback[usb_isrcmd_param] && instance) { + (instance->*(epCallback[usb_isrcmd_param]))(); + } + break; + case CMD_SOF: + this->SOF(usb_isrcmd_param); + break; + case CMD_SUSPEND_STATE_CHANGED: + this->suspendStateChanged(usb_isrcmd_param); + break; + default: + EFM_ASSERT(false); + break; + } + usb_isrcmd = CMD_HANDLED; +} +#endif + +// End of file