diff options
Diffstat (limited to 'tmk_core/protocol/arm_atsam/usb')
34 files changed, 13068 insertions, 0 deletions
diff --git a/tmk_core/protocol/arm_atsam/usb/compiler.h b/tmk_core/protocol/arm_atsam/usb/compiler.h new file mode 100644 index 0000000000..7d8350896b --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/compiler.h @@ -0,0 +1,1177 @@ +/** + * \file + * + * \brief Commonly used includes, types and macros. + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef UTILS_COMPILER_H_INCLUDED +#define UTILS_COMPILER_H_INCLUDED + +/** + * \defgroup group_sam0_utils Compiler abstraction layer and code utilities + * + * Compiler abstraction layer and code utilities for Cortex-M0+ based Atmel SAM devices. + * This module provides various abstraction layers and utilities to make code compatible between different compilers. + * + * @{ + */ + +#if (defined __ICCARM__) +# include <intrinsics.h> +#endif + +#include <stddef.h> +//#include <parts.h> +//#include <status_codes.h> +//#include <preprocessor.h> +//#include <io.h> + +#ifndef __ASSEMBLY__ + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +/** + * \def UNUSED + * \brief Marking \a v as a unused parameter or value. + */ +#define UNUSED(v) (void)(v) + +/** + * \def barrier + * \brief Memory barrier + */ +#ifdef __GNUC__ +# define barrier() asm volatile("" ::: "memory") +#else +# define barrier() asm ("") +#endif + +/** + * \brief Emit the compiler pragma \a arg. + * + * \param[in] arg The pragma directive as it would appear after \e \#pragma + * (i.e. not stringified). + */ +#define COMPILER_PRAGMA(arg) _Pragma(#arg) + +/** + * \def COMPILER_PACK_SET(alignment) + * \brief Set maximum alignment for subsequent struct and union definitions to \a alignment. + */ +#define COMPILER_PACK_SET(alignment) COMPILER_PRAGMA(pack(alignment)) + +/** + * \def COMPILER_PACK_RESET() + * \brief Set default alignment for subsequent struct and union definitions. + */ +#define COMPILER_PACK_RESET() COMPILER_PRAGMA(pack()) + + +/** + * \brief Set aligned boundary. + */ +#if (defined __GNUC__) || (defined __CC_ARM) +# define COMPILER_ALIGNED(a) __attribute__((__aligned__(a))) +#elif (defined __ICCARM__) +# define COMPILER_ALIGNED(a) COMPILER_PRAGMA(data_alignment = a) +#endif + +/** + * \brief Set word-aligned boundary. + */ +#if (defined __GNUC__) || defined(__CC_ARM) +#define COMPILER_WORD_ALIGNED __attribute__((__aligned__(4))) +#elif (defined __ICCARM__) +#define COMPILER_WORD_ALIGNED COMPILER_PRAGMA(data_alignment = 4) +#endif + +/** + * \def __always_inline + * \brief The function should always be inlined. + * + * This annotation instructs the compiler to ignore its inlining + * heuristics and inline the function no matter how big it thinks it + * becomes. + */ +#if defined(__CC_ARM) +# define __always_inline __forceinline +#elif (defined __GNUC__ && __GNUC__ <= 6) +# define __always_inline __attribute__((__always_inline__)) +#elif (defined __ICCARM__) +# define __always_inline _Pragma("inline=forced") +#endif + +/** + * \def __no_inline + * \brief The function should never be inlined + * + * This annotation instructs the compiler to ignore its inlining + * heuristics and not inline the function no matter how small it thinks it + * becomes. + */ +#if defined(__CC_ARM) +# define __no_inline __attribute__((noinline)) +#elif (defined __GNUC__) +# define __no_inline __attribute__((noinline)) +#elif (defined __ICCARM__) +# define __no_inline _Pragma("inline=never") +#endif + + +/** \brief This macro is used to test fatal errors. + * + * The macro tests if the expression is false. If it is, a fatal error is + * detected and the application hangs up. If \c TEST_SUITE_DEFINE_ASSERT_MACRO + * is defined, a unit test version of the macro is used, to allow execution + * of further tests after a false expression. + * + * \param[in] expr Expression to evaluate and supposed to be nonzero. + */ +#if defined(_ASSERT_ENABLE_) +# if defined(TEST_SUITE_DEFINE_ASSERT_MACRO) +# include "unit_test/suite.h" +# else +# undef TEST_SUITE_DEFINE_ASSERT_MACRO +# define Assert(expr) \ + {\ + if (!(expr)) asm("BKPT #0");\ + } +# endif +#else +# define Assert(expr) ((void) 0) +#endif + +/* Define WEAK attribute */ +#if defined ( __CC_ARM ) +# define WEAK __attribute__ ((weak)) +#elif defined ( __ICCARM__ ) +# define WEAK __weak +#elif defined ( __GNUC__ ) +# define WEAK __attribute__ ((weak)) +#endif + +/* Define NO_INIT attribute */ +#if defined ( __CC_ARM ) +# define NO_INIT __attribute__((zero_init)) +#elif defined ( __ICCARM__ ) +# define NO_INIT __no_init +#elif defined ( __GNUC__ ) +# define NO_INIT __attribute__((section(".no_init"))) +#endif + +//#include "interrupt.h" + +/** \name Usual Types + * @{ */ +#ifndef __cplusplus +# if !defined(__bool_true_false_are_defined) +typedef unsigned char bool; +# endif +#endif +typedef uint16_t le16_t; +typedef uint16_t be16_t; +typedef uint32_t le32_t; +typedef uint32_t be32_t; +typedef uint32_t iram_size_t; +/** @} */ + +/** \name Aliasing Aggregate Types + * @{ */ + +/** 16-bit union. */ +typedef union +{ + int16_t s16; + uint16_t u16; + int8_t s8[2]; + uint8_t u8[2]; +} Union16; + +/** 32-bit union. */ +typedef union +{ + int32_t s32; + uint32_t u32; + int16_t s16[2]; + uint16_t u16[2]; + int8_t s8[4]; + uint8_t u8[4]; +} Union32; + +/** 64-bit union. */ +typedef union +{ + int64_t s64; + uint64_t u64; + int32_t s32[2]; + uint32_t u32[2]; + int16_t s16[4]; + uint16_t u16[4]; + int8_t s8[8]; + uint8_t u8[8]; +} Union64; + +/** Union of pointers to 64-, 32-, 16- and 8-bit unsigned integers. */ +typedef union +{ + int64_t *s64ptr; + uint64_t *u64ptr; + int32_t *s32ptr; + uint32_t *u32ptr; + int16_t *s16ptr; + uint16_t *u16ptr; + int8_t *s8ptr; + uint8_t *u8ptr; +} UnionPtr; + +/** Union of pointers to volatile 64-, 32-, 16- and 8-bit unsigned integers. */ +typedef union +{ + volatile int64_t *s64ptr; + volatile uint64_t *u64ptr; + volatile int32_t *s32ptr; + volatile uint32_t *u32ptr; + volatile int16_t *s16ptr; + volatile uint16_t *u16ptr; + volatile int8_t *s8ptr; + volatile uint8_t *u8ptr; +} UnionVPtr; + +/** Union of pointers to constant 64-, 32-, 16- and 8-bit unsigned integers. */ +typedef union +{ + const int64_t *s64ptr; + const uint64_t *u64ptr; + const int32_t *s32ptr; + const uint32_t *u32ptr; + const int16_t *s16ptr; + const uint16_t *u16ptr; + const int8_t *s8ptr; + const uint8_t *u8ptr; +} UnionCPtr; + +/** Union of pointers to constant volatile 64-, 32-, 16- and 8-bit unsigned integers. */ +typedef union +{ + const volatile int64_t *s64ptr; + const volatile uint64_t *u64ptr; + const volatile int32_t *s32ptr; + const volatile uint32_t *u32ptr; + const volatile int16_t *s16ptr; + const volatile uint16_t *u16ptr; + const volatile int8_t *s8ptr; + const volatile uint8_t *u8ptr; +} UnionCVPtr; + +/** Structure of pointers to 64-, 32-, 16- and 8-bit unsigned integers. */ +typedef struct +{ + int64_t *s64ptr; + uint64_t *u64ptr; + int32_t *s32ptr; + uint32_t *u32ptr; + int16_t *s16ptr; + uint16_t *u16ptr; + int8_t *s8ptr; + uint8_t *u8ptr; +} StructPtr; + +/** Structure of pointers to volatile 64-, 32-, 16- and 8-bit unsigned integers. */ +typedef struct +{ + volatile int64_t *s64ptr; + volatile uint64_t *u64ptr; + volatile int32_t *s32ptr; + volatile uint32_t *u32ptr; + volatile int16_t *s16ptr; + volatile uint16_t *u16ptr; + volatile int8_t *s8ptr; + volatile uint8_t *u8ptr; +} StructVPtr; + +/** Structure of pointers to constant 64-, 32-, 16- and 8-bit unsigned integers. */ +typedef struct +{ + const int64_t *s64ptr; + const uint64_t *u64ptr; + const int32_t *s32ptr; + const uint32_t *u32ptr; + const int16_t *s16ptr; + const uint16_t *u16ptr; + const int8_t *s8ptr; + const uint8_t *u8ptr; +} StructCPtr; + +/** Structure of pointers to constant volatile 64-, 32-, 16- and 8-bit unsigned integers. */ +typedef struct +{ + const volatile int64_t *s64ptr; + const volatile uint64_t *u64ptr; + const volatile int32_t *s32ptr; + const volatile uint32_t *u32ptr; + const volatile int16_t *s16ptr; + const volatile uint16_t *u16ptr; + const volatile int8_t *s8ptr; + const volatile uint8_t *u8ptr; +} StructCVPtr; + +/** @} */ + +#endif /* #ifndef __ASSEMBLY__ */ + +/** \name Usual Constants + * @{ */ +//kmod #define DISABLE 0 +//kmod #define ENABLE 1 + +#ifndef __cplusplus +# if !defined(__bool_true_false_are_defined) +# define false 0 +# define true 1 +# endif +#endif +/** @} */ + +#ifndef __ASSEMBLY__ + +/** \name Optimization Control + * @{ */ + +/** + * \def likely(exp) + * \brief The expression \a exp is likely to be true + */ +#if !defined(likely) || defined(__DOXYGEN__) +# define likely(exp) (exp) +#endif + +/** + * \def unlikely(exp) + * \brief The expression \a exp is unlikely to be true + */ +#if !defined(unlikely) || defined(__DOXYGEN__) +# define unlikely(exp) (exp) +#endif + +/** + * \def is_constant(exp) + * \brief Determine if an expression evaluates to a constant value. + * + * \param[in] exp Any expression + * + * \return true if \a exp is constant, false otherwise. + */ +#if (defined __GNUC__) || (defined __CC_ARM) +# define is_constant(exp) __builtin_constant_p(exp) +#else +# define is_constant(exp) (0) +#endif + +/** @} */ + +/** \name Bit-Field Handling + * @{ */ + +/** \brief Reads the bits of a value specified by a given bit-mask. + * + * \param[in] value Value to read bits from. + * \param[in] mask Bit-mask indicating bits to read. + * + * \return Read bits. + */ +#define Rd_bits( value, mask) ((value) & (mask)) + +/** \brief Writes the bits of a C lvalue specified by a given bit-mask. + * + * \param[in] lvalue C lvalue to write bits to. + * \param[in] mask Bit-mask indicating bits to write. + * \param[in] bits Bits to write. + * + * \return Resulting value with written bits. + */ +#define Wr_bits(lvalue, mask, bits) ((lvalue) = ((lvalue) & ~(mask)) |\ + ((bits ) & (mask))) + +/** \brief Tests the bits of a value specified by a given bit-mask. + * + * \param[in] value Value of which to test bits. + * \param[in] mask Bit-mask indicating bits to test. + * + * \return \c 1 if at least one of the tested bits is set, else \c 0. + */ +#define Tst_bits( value, mask) (Rd_bits(value, mask) != 0) + +/** \brief Clears the bits of a C lvalue specified by a given bit-mask. + * + * \param[in] lvalue C lvalue of which to clear bits. + * \param[in] mask Bit-mask indicating bits to clear. + * + * \return Resulting value with cleared bits. + */ +#define Clr_bits(lvalue, mask) ((lvalue) &= ~(mask)) + +/** \brief Sets the bits of a C lvalue specified by a given bit-mask. + * + * \param[in] lvalue C lvalue of which to set bits. + * \param[in] mask Bit-mask indicating bits to set. + * + * \return Resulting value with set bits. + */ +#define Set_bits(lvalue, mask) ((lvalue) |= (mask)) + +/** \brief Toggles the bits of a C lvalue specified by a given bit-mask. + * + * \param[in] lvalue C lvalue of which to toggle bits. + * \param[in] mask Bit-mask indicating bits to toggle. + * + * \return Resulting value with toggled bits. + */ +#define Tgl_bits(lvalue, mask) ((lvalue) ^= (mask)) + +/** \brief Reads the bit-field of a value specified by a given bit-mask. + * + * \param[in] value Value to read a bit-field from. + * \param[in] mask Bit-mask indicating the bit-field to read. + * + * \return Read bit-field. + */ +#define Rd_bitfield( value, mask) (Rd_bits( value, mask) >> ctz(mask)) + +/** \brief Writes the bit-field of a C lvalue specified by a given bit-mask. + * + * \param[in] lvalue C lvalue to write a bit-field to. + * \param[in] mask Bit-mask indicating the bit-field to write. + * \param[in] bitfield Bit-field to write. + * + * \return Resulting value with written bit-field. + */ +#define Wr_bitfield(lvalue, mask, bitfield) (Wr_bits(lvalue, mask, (uint32_t)(bitfield) << ctz(mask))) + +/** @} */ + + +/** \name Zero-Bit Counting + * + * Under GCC, __builtin_clz and __builtin_ctz behave like macros when + * applied to constant expressions (values known at compile time), so they are + * more optimized than the use of the corresponding assembly instructions and + * they can be used as constant expressions e.g. to initialize objects having + * static storage duration, and like the corresponding assembly instructions + * when applied to non-constant expressions (values unknown at compile time), so + * they are more optimized than an assembly periphrasis. Hence, clz and ctz + * ensure a possible and optimized behavior for both constant and non-constant + * expressions. + * + * @{ */ + +/** \brief Counts the leading zero bits of the given value considered as a 32-bit integer. + * + * \param[in] u Value of which to count the leading zero bits. + * + * \return The count of leading zero bits in \a u. + */ +#if (defined __GNUC__) || (defined __CC_ARM) +# define clz(u) ((u) ? __builtin_clz(u) : 32) +#else +# define clz(u) (((u) == 0) ? 32 : \ + ((u) & (1ul << 31)) ? 0 : \ + ((u) & (1ul << 30)) ? 1 : \ + ((u) & (1ul << 29)) ? 2 : \ + ((u) & (1ul << 28)) ? 3 : \ + ((u) & (1ul << 27)) ? 4 : \ + ((u) & (1ul << 26)) ? 5 : \ + ((u) & (1ul << 25)) ? 6 : \ + ((u) & (1ul << 24)) ? 7 : \ + ((u) & (1ul << 23)) ? 8 : \ + ((u) & (1ul << 22)) ? 9 : \ + ((u) & (1ul << 21)) ? 10 : \ + ((u) & (1ul << 20)) ? 11 : \ + ((u) & (1ul << 19)) ? 12 : \ + ((u) & (1ul << 18)) ? 13 : \ + ((u) & (1ul << 17)) ? 14 : \ + ((u) & (1ul << 16)) ? 15 : \ + ((u) & (1ul << 15)) ? 16 : \ + ((u) & (1ul << 14)) ? 17 : \ + ((u) & (1ul << 13)) ? 18 : \ + ((u) & (1ul << 12)) ? 19 : \ + ((u) & (1ul << 11)) ? 20 : \ + ((u) & (1ul << 10)) ? 21 : \ + ((u) & (1ul << 9)) ? 22 : \ + ((u) & (1ul << 8)) ? 23 : \ + ((u) & (1ul << 7)) ? 24 : \ + ((u) & (1ul << 6)) ? 25 : \ + ((u) & (1ul << 5)) ? 26 : \ + ((u) & (1ul << 4)) ? 27 : \ + ((u) & (1ul << 3)) ? 28 : \ + ((u) & (1ul << 2)) ? 29 : \ + ((u) & (1ul << 1)) ? 30 : \ + 31) +#endif + +/** \brief Counts the trailing zero bits of the given value considered as a 32-bit integer. + * + * \param[in] u Value of which to count the trailing zero bits. + * + * \return The count of trailing zero bits in \a u. + */ +#if (defined __GNUC__) || (defined __CC_ARM) +# define ctz(u) ((u) ? __builtin_ctz(u) : 32) +#else +# define ctz(u) ((u) & (1ul << 0) ? 0 : \ + (u) & (1ul << 1) ? 1 : \ + (u) & (1ul << 2) ? 2 : \ + (u) & (1ul << 3) ? 3 : \ + (u) & (1ul << 4) ? 4 : \ + (u) & (1ul << 5) ? 5 : \ + (u) & (1ul << 6) ? 6 : \ + (u) & (1ul << 7) ? 7 : \ + (u) & (1ul << 8) ? 8 : \ + (u) & (1ul << 9) ? 9 : \ + (u) & (1ul << 10) ? 10 : \ + (u) & (1ul << 11) ? 11 : \ + (u) & (1ul << 12) ? 12 : \ + (u) & (1ul << 13) ? 13 : \ + (u) & (1ul << 14) ? 14 : \ + (u) & (1ul << 15) ? 15 : \ + (u) & (1ul << 16) ? 16 : \ + (u) & (1ul << 17) ? 17 : \ + (u) & (1ul << 18) ? 18 : \ + (u) & (1ul << 19) ? 19 : \ + (u) & (1ul << 20) ? 20 : \ + (u) & (1ul << 21) ? 21 : \ + (u) & (1ul << 22) ? 22 : \ + (u) & (1ul << 23) ? 23 : \ + (u) & (1ul << 24) ? 24 : \ + (u) & (1ul << 25) ? 25 : \ + (u) & (1ul << 26) ? 26 : \ + (u) & (1ul << 27) ? 27 : \ + (u) & (1ul << 28) ? 28 : \ + (u) & (1ul << 29) ? 29 : \ + (u) & (1ul << 30) ? 30 : \ + (u) & (1ul << 31) ? 31 : \ + 32) +#endif + +/** @} */ + + +/** \name Bit Reversing + * @{ */ + +/** \brief Reverses the bits of \a u8. + * + * \param[in] u8 U8 of which to reverse the bits. + * + * \return Value resulting from \a u8 with reversed bits. + */ +#define bit_reverse8(u8) ((U8)(bit_reverse32((U8)(u8)) >> 24)) + +/** \brief Reverses the bits of \a u16. + * + * \param[in] u16 U16 of which to reverse the bits. + * + * \return Value resulting from \a u16 with reversed bits. + */ +#define bit_reverse16(u16) ((uint16_t)(bit_reverse32((uint16_t)(u16)) >> 16)) + +/** \brief Reverses the bits of \a u32. + * + * \param[in] u32 U32 of which to reverse the bits. + * + * \return Value resulting from \a u32 with reversed bits. + */ +#define bit_reverse32(u32) __RBIT(u32) + +/** \brief Reverses the bits of \a u64. + * + * \param[in] u64 U64 of which to reverse the bits. + * + * \return Value resulting from \a u64 with reversed bits. + */ +#define bit_reverse64(u64) ((uint64_t)(((uint64_t)bit_reverse32((uint64_t)(u64) >> 32)) |\ + ((uint64_t)bit_reverse32((uint64_t)(u64)) << 32))) + +/** @} */ + + +/** \name Alignment + * @{ */ + +/** \brief Tests alignment of the number \a val with the \a n boundary. + * + * \param[in] val Input value. + * \param[in] n Boundary. + * + * \return \c 1 if the number \a val is aligned with the \a n boundary, else \c 0. + */ +#define Test_align(val, n) (!Tst_bits( val, (n) - 1 ) ) + +/** \brief Gets alignment of the number \a val with respect to the \a n boundary. + * + * \param[in] val Input value. + * \param[in] n Boundary. + * + * \return Alignment of the number \a val with respect to the \a n boundary. + */ +#define Get_align(val, n) ( Rd_bits( val, (n) - 1 ) ) + +/** \brief Sets alignment of the lvalue number \a lval to \a alg with respect to the \a n boundary. + * + * \param[in] lval Input/output lvalue. + * \param[in] n Boundary. + * \param[in] alg Alignment. + * + * \return New value of \a lval resulting from its alignment set to \a alg with respect to the \a n boundary. + */ +#define Set_align(lval, n, alg) ( Wr_bits(lval, (n) - 1, alg) ) + +/** \brief Aligns the number \a val with the upper \a n boundary. + * + * \param[in] val Input value. + * \param[in] n Boundary. + * + * \return Value resulting from the number \a val aligned with the upper \a n boundary. + */ +#define Align_up( val, n) (((val) + ((n) - 1)) & ~((n) - 1)) + +/** \brief Aligns the number \a val with the lower \a n boundary. + * + * \param[in] val Input value. + * \param[in] n Boundary. + * + * \return Value resulting from the number \a val aligned with the lower \a n boundary. + */ +#define Align_down(val, n) ( (val) & ~((n) - 1)) + +/** @} */ + + +/** \name Mathematics + * + * The same considerations as for clz and ctz apply here but GCC does not + * provide built-in functions to access the assembly instructions abs, min and + * max and it does not produce them by itself in most cases, so two sets of + * macros are defined here: + * - Abs, Min and Max to apply to constant expressions (values known at + * compile time); + * - abs, min and max to apply to non-constant expressions (values unknown at + * compile time), abs is found in stdlib.h. + * + * @{ */ + +/** \brief Takes the absolute value of \a a. + * + * \param[in] a Input value. + * + * \return Absolute value of \a a. + * + * \note More optimized if only used with values known at compile time. + */ +#define Abs(a) (((a) < 0 ) ? -(a) : (a)) + +#ifndef __cplusplus +/** \brief Takes the minimal value of \a a and \a b. + * + * \param[in] a Input value. + * \param[in] b Input value. + * + * \return Minimal value of \a a and \a b. + * + * \note More optimized if only used with values known at compile time. + */ +#define Min(a, b) (((a) < (b)) ? (a) : (b)) + +/** \brief Takes the maximal value of \a a and \a b. + * + * \param[in] a Input value. + * \param[in] b Input value. + * + * \return Maximal value of \a a and \a b. + * + * \note More optimized if only used with values known at compile time. + */ +#define Max(a, b) (((a) > (b)) ? (a) : (b)) + +/** \brief Takes the minimal value of \a a and \a b. + * + * \param[in] a Input value. + * \param[in] b Input value. + * + * \return Minimal value of \a a and \a b. + * + * \note More optimized if only used with values unknown at compile time. + */ +#define min(a, b) Min(a, b) + +/** \brief Takes the maximal value of \a a and \a b. + * + * \param[in] a Input value. + * \param[in] b Input value. + * + * \return Maximal value of \a a and \a b. + * + * \note More optimized if only used with values unknown at compile time. + */ +#define max(a, b) Max(a, b) +#endif + +/** @} */ + + +/** \brief Calls the routine at address \a addr. + * + * It generates a long call opcode. + * + * For example, `Long_call(0x80000000)' generates a software reset on a UC3 if + * it is invoked from the CPU supervisor mode. + * + * \param[in] addr Address of the routine to call. + * + * \note It may be used as a long jump opcode in some special cases. + */ +#define Long_call(addr) ((*(void (*)(void))(addr))()) + + +/** \name MCU Endianism Handling + * ARM is MCU little endian. + * + * @{ */ +#define BE16(x) swap16(x) +#define LE16(x) (x) + +#define le16_to_cpu(x) (x) +#define cpu_to_le16(x) (x) +#define LE16_TO_CPU(x) (x) +#define CPU_TO_LE16(x) (x) + +#define be16_to_cpu(x) swap16(x) +#define cpu_to_be16(x) swap16(x) +#define BE16_TO_CPU(x) swap16(x) +#define CPU_TO_BE16(x) swap16(x) + +#define le32_to_cpu(x) (x) +#define cpu_to_le32(x) (x) +#define LE32_TO_CPU(x) (x) +#define CPU_TO_LE32(x) (x) + +#define be32_to_cpu(x) swap32(x) +#define cpu_to_be32(x) swap32(x) +#define BE32_TO_CPU(x) swap32(x) +#define CPU_TO_BE32(x) swap32(x) +/** @} */ + + +/** \name Endianism Conversion + * + * The same considerations as for clz and ctz apply here but GCC's + * __builtin_bswap_32 and __builtin_bswap_64 do not behave like macros when + * applied to constant expressions, so two sets of macros are defined here: + * - Swap16, Swap32 and Swap64 to apply to constant expressions (values known + * at compile time); + * - swap16, swap32 and swap64 to apply to non-constant expressions (values + * unknown at compile time). + * + * @{ */ + +/** \brief Toggles the endianism of \a u16 (by swapping its bytes). + * + * \param[in] u16 U16 of which to toggle the endianism. + * + * \return Value resulting from \a u16 with toggled endianism. + * + * \note More optimized if only used with values known at compile time. + */ +#define Swap16(u16) ((uint16_t)(((uint16_t)(u16) >> 8) |\ + ((uint16_t)(u16) << 8))) + +/** \brief Toggles the endianism of \a u32 (by swapping its bytes). + * + * \param[in] u32 U32 of which to toggle the endianism. + * + * \return Value resulting from \a u32 with toggled endianism. + * + * \note More optimized if only used with values known at compile time. + */ +#define Swap32(u32) ((uint32_t)(((uint32_t)Swap16((uint32_t)(u32) >> 16)) |\ + ((uint32_t)Swap16((uint32_t)(u32)) << 16))) + +/** \brief Toggles the endianism of \a u64 (by swapping its bytes). + * + * \param[in] u64 U64 of which to toggle the endianism. + * + * \return Value resulting from \a u64 with toggled endianism. + * + * \note More optimized if only used with values known at compile time. + */ +#define Swap64(u64) ((uint64_t)(((uint64_t)Swap32((uint64_t)(u64) >> 32)) |\ + ((uint64_t)Swap32((uint64_t)(u64)) << 32))) + +/** \brief Toggles the endianism of \a u16 (by swapping its bytes). + * + * \param[in] u16 U16 of which to toggle the endianism. + * + * \return Value resulting from \a u16 with toggled endianism. + * + * \note More optimized if only used with values unknown at compile time. + */ +#define swap16(u16) Swap16(u16) + +/** \brief Toggles the endianism of \a u32 (by swapping its bytes). + * + * \param[in] u32 U32 of which to toggle the endianism. + * + * \return Value resulting from \a u32 with toggled endianism. + * + * \note More optimized if only used with values unknown at compile time. + */ +#if (defined __GNUC__) +# define swap32(u32) ((uint32_t)__builtin_bswap32((uint32_t)(u32))) +#else +# define swap32(u32) Swap32(u32) +#endif + +/** \brief Toggles the endianism of \a u64 (by swapping its bytes). + * + * \param[in] u64 U64 of which to toggle the endianism. + * + * \return Value resulting from \a u64 with toggled endianism. + * + * \note More optimized if only used with values unknown at compile time. + */ +#if (defined __GNUC__) +# define swap64(u64) ((uint64_t)__builtin_bswap64((uint64_t)(u64))) +#else +# define swap64(u64) ((uint64_t)(((uint64_t)swap32((uint64_t)(u64) >> 32)) |\ + ((uint64_t)swap32((uint64_t)(u64)) << 32))) +#endif + +/** @} */ + + +/** \name Target Abstraction + * + * @{ */ + +#define _GLOBEXT_ extern /**< extern storage-class specifier. */ +#define _CONST_TYPE_ const /**< const type qualifier. */ +#define _MEM_TYPE_SLOW_ /**< Slow memory type. */ +#define _MEM_TYPE_MEDFAST_ /**< Fairly fast memory type. */ +#define _MEM_TYPE_FAST_ /**< Fast memory type. */ + +#define memcmp_ram2ram memcmp /**< Target-specific memcmp of RAM to RAM. */ +#define memcmp_code2ram memcmp /**< Target-specific memcmp of RAM to NVRAM. */ +#define memcpy_ram2ram memcpy /**< Target-specific memcpy from RAM to RAM. */ +#define memcpy_code2ram memcpy /**< Target-specific memcpy from NVRAM to RAM. */ + +/** @} */ + +/** + * \brief Calculate \f$ \left\lceil \frac{a}{b} \right\rceil \f$ using + * integer arithmetic. + * + * \param[in] a An integer + * \param[in] b Another integer + * + * \return (\a a / \a b) rounded up to the nearest integer. + */ +#define div_ceil(a, b) (((a) + (b) - 1) / (b)) + +#endif /* #ifndef __ASSEMBLY__ */ +#ifdef __ICCARM__ +/** \name Compiler Keywords + * + * Port of some keywords from GCC to IAR Embedded Workbench. + * + * @{ */ + +#define __asm__ asm +#define __inline__ inline +#define __volatile__ + +/** @} */ + +#endif + +#define FUNC_PTR void * +/** + * \def unused + * \brief Marking \a v as a unused parameter or value. + */ +#define unused(v) do { (void)(v); } while(0) + +/* Define RAMFUNC attribute */ +#if defined ( __CC_ARM ) /* Keil uVision 4 */ +# define RAMFUNC __attribute__ ((section(".ramfunc"))) +#elif defined ( __ICCARM__ ) /* IAR Ewarm 5.41+ */ +# define RAMFUNC __ramfunc +#elif defined ( __GNUC__ ) /* GCC CS3 2009q3-68 */ +# define RAMFUNC __attribute__ ((section(".ramfunc"))) +#endif + +/* Define OPTIMIZE_HIGH attribute */ +#if defined ( __CC_ARM ) /* Keil uVision 4 */ +# define OPTIMIZE_HIGH _Pragma("O3") +#elif defined ( __ICCARM__ ) /* IAR Ewarm 5.41+ */ +# define OPTIMIZE_HIGH _Pragma("optimize=high") +#elif defined ( __GNUC__ ) /* GCC CS3 2009q3-68 */ +# define OPTIMIZE_HIGH __attribute__((optimize("s"))) +#endif +//kmod #define PASS 0 +//kmod #define FAIL 1 +//kmod #define LOW 0 +//kmod #define HIGH 1 + +typedef int8_t S8 ; //!< 8-bit signed integer. +typedef uint8_t U8 ; //!< 8-bit unsigned integer. +typedef int16_t S16; //!< 16-bit signed integer. +typedef uint16_t U16; //!< 16-bit unsigned integer. +typedef int32_t S32; //!< 32-bit signed integer. +typedef uint32_t U32; //!< 32-bit unsigned integer. +typedef int64_t S64; //!< 64-bit signed integer. +typedef uint64_t U64; //!< 64-bit unsigned integer. +typedef float F32; //!< 32-bit floating-point number. +typedef double F64; //!< 64-bit floating-point number. + +#define MSB(u16) (((U8 *)&(u16))[1]) //!< Most significant byte of \a u16. +#define LSB(u16) (((U8 *)&(u16))[0]) //!< Least significant byte of \a u16. + +#define MSH(u32) (((U16 *)&(u32))[1]) //!< Most significant half-word of \a u32. +#define LSH(u32) (((U16 *)&(u32))[0]) //!< Least significant half-word of \a u32. +#define MSB0W(u32) (((U8 *)&(u32))[3]) //!< Most significant byte of 1st rank of \a u32. +#define MSB1W(u32) (((U8 *)&(u32))[2]) //!< Most significant byte of 2nd rank of \a u32. +#define MSB2W(u32) (((U8 *)&(u32))[1]) //!< Most significant byte of 3rd rank of \a u32. +#define MSB3W(u32) (((U8 *)&(u32))[0]) //!< Most significant byte of 4th rank of \a u32. +#define LSB3W(u32) MSB0W(u32) //!< Least significant byte of 4th rank of \a u32. +#define LSB2W(u32) MSB1W(u32) //!< Least significant byte of 3rd rank of \a u32. +#define LSB1W(u32) MSB2W(u32) //!< Least significant byte of 2nd rank of \a u32. +#define LSB0W(u32) MSB3W(u32) //!< Least significant byte of 1st rank of \a u32. + +#define MSW(u64) (((U32 *)&(u64))[1]) //!< Most significant word of \a u64. +#define LSW(u64) (((U32 *)&(u64))[0]) //!< Least significant word of \a u64. +#define MSH0(u64) (((U16 *)&(u64))[3]) //!< Most significant half-word of 1st rank of \a u64. +#define MSH1(u64) (((U16 *)&(u64))[2]) //!< Most significant half-word of 2nd rank of \a u64. +#define MSH2(u64) (((U16 *)&(u64))[1]) //!< Most significant half-word of 3rd rank of \a u64. +#define MSH3(u64) (((U16 *)&(u64))[0]) //!< Most significant half-word of 4th rank of \a u64. +#define LSH3(u64) MSH0(u64) //!< Least significant half-word of 4th rank of \a u64. +#define LSH2(u64) MSH1(u64) //!< Least significant half-word of 3rd rank of \a u64. +#define LSH1(u64) MSH2(u64) //!< Least significant half-word of 2nd rank of \a u64. +#define LSH0(u64) MSH3(u64) //!< Least significant half-word of 1st rank of \a u64. +#define MSB0D(u64) (((U8 *)&(u64))[7]) //!< Most significant byte of 1st rank of \a u64. +#define MSB1D(u64) (((U8 *)&(u64))[6]) //!< Most significant byte of 2nd rank of \a u64. +#define MSB2D(u64) (((U8 *)&(u64))[5]) //!< Most significant byte of 3rd rank of \a u64. +#define MSB3D(u64) (((U8 *)&(u64))[4]) //!< Most significant byte of 4th rank of \a u64. +#define MSB4D(u64) (((U8 *)&(u64))[3]) //!< Most significant byte of 5th rank of \a u64. +#define MSB5D(u64) (((U8 *)&(u64))[2]) //!< Most significant byte of 6th rank of \a u64. +#define MSB6D(u64) (((U8 *)&(u64))[1]) //!< Most significant byte of 7th rank of \a u64. +#define MSB7D(u64) (((U8 *)&(u64))[0]) //!< Most significant byte of 8th rank of \a u64. +#define LSB7D(u64) MSB0D(u64) //!< Least significant byte of 8th rank of \a u64. +#define LSB6D(u64) MSB1D(u64) //!< Least significant byte of 7th rank of \a u64. +#define LSB5D(u64) MSB2D(u64) //!< Least significant byte of 6th rank of \a u64. +#define LSB4D(u64) MSB3D(u64) //!< Least significant byte of 5th rank of \a u64. +#define LSB3D(u64) MSB4D(u64) //!< Least significant byte of 4th rank of \a u64. +#define LSB2D(u64) MSB5D(u64) //!< Least significant byte of 3rd rank of \a u64. +#define LSB1D(u64) MSB6D(u64) //!< Least significant byte of 2nd rank of \a u64. +#define LSB0D(u64) MSB7D(u64) //!< Least significant byte of 1st rank of \a u64. + +#define LSB0(u32) LSB0W(u32) //!< Least significant byte of 1st rank of \a u32. +#define LSB1(u32) LSB1W(u32) //!< Least significant byte of 2nd rank of \a u32. +#define LSB2(u32) LSB2W(u32) //!< Least significant byte of 3rd rank of \a u32. +#define LSB3(u32) LSB3W(u32) //!< Least significant byte of 4th rank of \a u32. +#define MSB3(u32) MSB3W(u32) //!< Most significant byte of 4th rank of \a u32. +#define MSB2(u32) MSB2W(u32) //!< Most significant byte of 3rd rank of \a u32. +#define MSB1(u32) MSB1W(u32) //!< Most significant byte of 2nd rank of \a u32. +#define MSB0(u32) MSB0W(u32) //!< Most significant byte of 1st rank of \a u32. + +#if defined(__ICCARM__) +#define SHORTENUM __packed +#elif defined(__GNUC__) +#define SHORTENUM __attribute__((packed)) +#endif + +/* No operation */ +#if defined(__ICCARM__) +#define nop() __no_operation() +#elif defined(__GNUC__) +#define nop() (__NOP()) +#endif + +#define FLASH_DECLARE(x) const x +#define FLASH_EXTERN(x) extern const x +#define PGM_READ_BYTE(x) *(x) +#define PGM_READ_WORD(x) *(x) +#define MEMCPY_ENDIAN memcpy +#define PGM_READ_BLOCK(dst, src, len) memcpy((dst), (src), (len)) + +/*Defines the Flash Storage for the request and response of MAC*/ +#define CMD_ID_OCTET (0) + +/* Converting of values from CPU endian to little endian. */ +#define CPU_ENDIAN_TO_LE16(x) (x) +#define CPU_ENDIAN_TO_LE32(x) (x) +#define CPU_ENDIAN_TO_LE64(x) (x) + +/* Converting of values from little endian to CPU endian. */ +#define LE16_TO_CPU_ENDIAN(x) (x) +#define LE32_TO_CPU_ENDIAN(x) (x) +#define LE64_TO_CPU_ENDIAN(x) (x) + +/* Converting of constants from little endian to CPU endian. */ +#define CLE16_TO_CPU_ENDIAN(x) (x) +#define CLE32_TO_CPU_ENDIAN(x) (x) +#define CLE64_TO_CPU_ENDIAN(x) (x) + +/* Converting of constants from CPU endian to little endian. */ +#define CCPU_ENDIAN_TO_LE16(x) (x) +#define CCPU_ENDIAN_TO_LE32(x) (x) +#define CCPU_ENDIAN_TO_LE64(x) (x) + +#define ADDR_COPY_DST_SRC_16(dst, src) ((dst) = (src)) +#define ADDR_COPY_DST_SRC_64(dst, src) ((dst) = (src)) + +/** + * @brief Converts a 64-Bit value into a 8 Byte array + * + * @param[in] value 64-Bit value + * @param[out] data Pointer to the 8 Byte array to be updated with 64-Bit value + * @ingroup apiPalApi + */ +static inline void convert_64_bit_to_byte_array(uint64_t value, uint8_t *data) +{ + uint8_t index = 0; + + while (index < 8) + { + data[index++] = value & 0xFF; + value = value >> 8; + } +} + +/** + * @brief Converts a 16-Bit value into a 2 Byte array + * + * @param[in] value 16-Bit value + * @param[out] data Pointer to the 2 Byte array to be updated with 16-Bit value + * @ingroup apiPalApi + */ +static inline void convert_16_bit_to_byte_array(uint16_t value, uint8_t *data) +{ + data[0] = value & 0xFF; + data[1] = (value >> 8) & 0xFF; +} + +/* Converts a 16-Bit value into a 2 Byte array */ +static inline void convert_spec_16_bit_to_byte_array(uint16_t value, uint8_t *data) +{ + data[0] = value & 0xFF; + data[1] = (value >> 8) & 0xFF; +} + +/* Converts a 16-Bit value into a 2 Byte array */ +static inline void convert_16_bit_to_byte_address(uint16_t value, uint8_t *data) +{ + data[0] = value & 0xFF; + data[1] = (value >> 8) & 0xFF; +} + +/* + * @brief Converts a 2 Byte array into a 16-Bit value + * + * @param data Specifies the pointer to the 2 Byte array + * + * @return 16-Bit value + * @ingroup apiPalApi + */ +static inline uint16_t convert_byte_array_to_16_bit(uint8_t *data) +{ + return (data[0] | ((uint16_t)data[1] << 8)); +} + +/* Converts a 4 Byte array into a 32-Bit value */ +static inline uint32_t convert_byte_array_to_32_bit(uint8_t *data) +{ + union + { + uint32_t u32; + uint8_t u8[4]; + } long_addr; + + uint8_t index; + + for (index = 0; index < 4; index++) + { + long_addr.u8[index] = *data++; + } + + return long_addr.u32; +} + +/** + * @brief Converts a 8 Byte array into a 64-Bit value + * + * @param data Specifies the pointer to the 8 Byte array + * + * @return 64-Bit value + * @ingroup apiPalApi + */ +static inline uint64_t convert_byte_array_to_64_bit(uint8_t *data) +{ + union + { + uint64_t u64; + uint8_t u8[8]; + } long_addr; + + uint8_t index; + + for (index = 0; index < 8; index++) + { + long_addr.u8[index] = *data++; + } + + return long_addr.u64; +} + +/** @} */ + +#endif /* UTILS_COMPILER_H_INCLUDED */ diff --git a/tmk_core/protocol/arm_atsam/usb/conf_usb.h b/tmk_core/protocol/arm_atsam/usb/conf_usb.h new file mode 100644 index 0000000000..c91caffe02 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/conf_usb.h @@ -0,0 +1,168 @@ +/** + * \file + * + * \brief USB configuration file + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _CONF_USB_H_ +#define _CONF_USB_H_ + +#include "compiler.h" +#include "udi_device_conf.h" + +#define UDI_CDC_DEFAULT_RATE 115200 +#define UDI_CDC_DEFAULT_STOPBITS CDC_STOP_BITS_1 +#define UDI_CDC_DEFAULT_PARITY CDC_PAR_NONE +#define UDI_CDC_DEFAULT_DATABITS 8 + +//! Device definition (mandatory) +#define USB_DEVICE_VENDOR_ID VENDOR_ID +#define USB_DEVICE_PRODUCT_ID PRODUCT_ID +#define USB_DEVICE_VERSION DEVICE_VER +#define USB_DEVICE_POWER 500 // Consumption on Vbus line (mA) +#define USB_DEVICE_ATTR (USB_CONFIG_ATTR_REMOTE_WAKEUP|USB_CONFIG_ATTR_BUS_POWERED) +// (USB_CONFIG_ATTR_REMOTE_WAKEUP|USB_CONFIG_ATTR_BUS_POWERED) +// (USB_CONFIG_ATTR_REMOTE_WAKEUP|USB_CONFIG_ATTR_SELF_POWERED) +// (USB_CONFIG_ATTR_SELF_POWERED) +// (USB_CONFIG_ATTR_BUS_POWERED) + +//! USB Device string definitions (Optional) +#define USB_DEVICE_MANUFACTURE_NAME MANUFACTURER +#define USB_DEVICE_PRODUCT_NAME PRODUCT +#define USB_DEVICE_SERIAL_NAME SERIAL_NUM + +//Comment out USB_DEVICE_SERIAL_USE_BOOTLOADER_SERIAL to prevent ROM lookup of factory programmed serial number +#define USB_DEVICE_SERIAL_USE_BOOTLOADER_SERIAL + +/** + * Device speeds support + * @{ + */ +//! To define a Low speed device +//#define USB_DEVICE_LOW_SPEED + +//! To authorize the High speed +#if (UC3A3||UC3A4) +//#define USB_DEVICE_HS_SUPPORT +#elif (SAM3XA||SAM3U) +//#define USB_DEVICE_HS_SUPPORT +#endif +//@} + +/** + * USB Device Callbacks definitions (Optional) + * @{ + */ +#define UDC_VBUS_EVENT(b_vbus_high) +#define UDC_SOF_EVENT() main_sof_action() +#define UDC_SUSPEND_EVENT() main_suspend_action() +#define UDC_RESUME_EVENT() main_resume_action() +//! Mandatory when USB_DEVICE_ATTR authorizes remote wakeup feature +#define UDC_REMOTEWAKEUP_ENABLE() main_remotewakeup_enable() +#define UDC_REMOTEWAKEUP_DISABLE() main_remotewakeup_disable() +//! When a extra string descriptor must be supported +//! other than manufacturer, product and serial string +// #define UDC_GET_EXTRA_STRING() +//@} + +//@} + + +/** + * USB Interface Configuration + * @{ + */ +/** + * Configuration of HID Keyboard interface + * @{ + */ +//! Interface callback definition +#ifdef KBD +#define UDI_HID_KBD_ENABLE_EXT() main_kbd_enable() +#define UDI_HID_KBD_DISABLE_EXT() main_kbd_disable() +//#define UDI_HID_KBD_CHANGE_LED(value) ui_kbd_led(value) +#endif + +#ifdef NKRO +#define UDI_HID_NKRO_ENABLE_EXT() main_nkro_enable() +#define UDI_HID_NKRO_DISABLE_EXT() main_nkro_disable() +//#define UDI_HID_NKRO_CHANGE_LED(value) ui_kbd_led(value) +#endif + +#ifdef EXK +#define UDI_HID_EXK_ENABLE_EXT() main_exk_enable() +#define UDI_HID_EXK_DISABLE_EXT() main_exk_disable() +#endif + +#ifdef CON +#define UDI_HID_CON_ENABLE_EXT() main_con_enable() +#define UDI_HID_CON_DISABLE_EXT() main_con_disable() +#endif + +#ifdef MOU +#define UDI_HID_MOU_ENABLE_EXT() main_mou_enable() +#define UDI_HID_MOU_DISABLE_EXT() main_mou_disable() +#endif + +#ifdef RAW +#define UDI_HID_RAW_ENABLE_EXT() main_raw_enable() +#define UDI_HID_RAW_DISABLE_EXT() main_raw_disable() +#endif + + +//@} +//@} + + +/** + * USB Device Driver Configuration + * @{ + */ +//@} + +//! The includes of classes and other headers must be done at the end of this file to avoid compile error +#include "udi_hid_kbd_conf.h" +#include "usb_main.h" +#include "ui.h" + +#endif // _CONF_USB_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/main_usb.c b/tmk_core/protocol/arm_atsam/usb/main_usb.c new file mode 100644 index 0000000000..0f676ab639 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/main_usb.c @@ -0,0 +1,132 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "samd51j18a.h" +#include "conf_usb.h" +#include "udd.h" + +uint8_t keyboard_protocol = 1; + +void main_suspend_action(void) +{ + ui_powerdown(); +} + +void main_resume_action(void) +{ + ui_wakeup(); +} + +void main_sof_action(void) +{ + ui_process(udd_get_frame_number()); +} + +void main_remotewakeup_enable(void) +{ + ui_wakeup_enable(); +} + +void main_remotewakeup_disable(void) +{ + ui_wakeup_disable(); +} + +#ifdef KBD +volatile bool main_b_kbd_enable = false; +bool main_kbd_enable(void) +{ + main_b_kbd_enable = true; + return true; +} + +void main_kbd_disable(void) +{ + main_b_kbd_enable = false; +} +#endif + +#ifdef NKRO +volatile bool main_b_nkro_enable = false; +bool main_nkro_enable(void) +{ + main_b_nkro_enable = true; + return true; +} + +void main_nkro_disable(void) +{ + main_b_nkro_enable = false; +} +#endif + +#ifdef EXK +volatile bool main_b_exk_enable = false; +bool main_exk_enable(void) +{ + main_b_exk_enable = true; + return true; +} + +void main_exk_disable(void) +{ + main_b_exk_enable = false; +} +#endif + +#ifdef CON +volatile bool main_b_con_enable = false; +bool main_con_enable(void) +{ + main_b_con_enable = true; + return true; +} + +void main_con_disable(void) +{ + main_b_con_enable = false; +} +#endif + +#ifdef MOU +volatile bool main_b_mou_enable = false; +bool main_mou_enable(void) +{ + main_b_mou_enable = true; + return true; +} + +void main_mou_disable(void) +{ + main_b_mou_enable = false; +} +#endif + +#ifdef RAW +volatile bool main_b_raw_enable = false; +bool main_raw_enable(void) +{ + main_b_raw_enable = true; + return true; +} + +void main_raw_disable(void) +{ + main_b_raw_enable = false; +} +#endif + diff --git a/tmk_core/protocol/arm_atsam/usb/status_codes.h b/tmk_core/protocol/arm_atsam/usb/status_codes.h new file mode 100644 index 0000000000..f56d2faed1 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/status_codes.h @@ -0,0 +1,158 @@ +/** + * \file + * + * \brief Status code definitions. + * + * This file defines various status codes returned by functions, + * indicating success or failure as well as what kind of failure. + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef STATUS_CODES_H_INCLUDED +#define STATUS_CODES_H_INCLUDED + +#include <stdint.h> + +/** + * \defgroup group_sam0_utils_status_codes Status Codes + * + * \ingroup group_sam0_utils + * + * @{ + */ + +/** Mask to retrieve the error category of a status code. */ +#define STATUS_CATEGORY_MASK 0xF0 + +/** Mask to retrieve the error code within the category of a status code. */ +#define STATUS_ERROR_MASK 0x0F + +/** Status code error categories. */ +enum status_categories { + STATUS_CATEGORY_OK = 0x00, + STATUS_CATEGORY_COMMON = 0x10, + STATUS_CATEGORY_ANALOG = 0x30, + STATUS_CATEGORY_COM = 0x40, + STATUS_CATEGORY_IO = 0x50, +}; + +/** + * Status code that may be returned by shell commands and protocol + * implementations. + * + * \note Any change to these status codes and the corresponding + * message strings is strictly forbidden. New codes can be added, + * however, but make sure that any message string tables are updated + * at the same time. + */ +enum status_code { + STATUS_OK = STATUS_CATEGORY_OK | 0x00, + STATUS_VALID_DATA = STATUS_CATEGORY_OK | 0x01, + STATUS_NO_CHANGE = STATUS_CATEGORY_OK | 0x02, + STATUS_ABORTED = STATUS_CATEGORY_OK | 0x04, + STATUS_BUSY = STATUS_CATEGORY_OK | 0x05, + STATUS_SUSPEND = STATUS_CATEGORY_OK | 0x06, + + STATUS_ERR_IO = STATUS_CATEGORY_COMMON | 0x00, + STATUS_ERR_REQ_FLUSHED = STATUS_CATEGORY_COMMON | 0x01, + STATUS_ERR_TIMEOUT = STATUS_CATEGORY_COMMON | 0x02, + STATUS_ERR_BAD_DATA = STATUS_CATEGORY_COMMON | 0x03, + STATUS_ERR_NOT_FOUND = STATUS_CATEGORY_COMMON | 0x04, + STATUS_ERR_UNSUPPORTED_DEV = STATUS_CATEGORY_COMMON | 0x05, + STATUS_ERR_NO_MEMORY = STATUS_CATEGORY_COMMON | 0x06, + STATUS_ERR_INVALID_ARG = STATUS_CATEGORY_COMMON | 0x07, + STATUS_ERR_BAD_ADDRESS = STATUS_CATEGORY_COMMON | 0x08, + STATUS_ERR_BAD_FORMAT = STATUS_CATEGORY_COMMON | 0x0A, + STATUS_ERR_BAD_FRQ = STATUS_CATEGORY_COMMON | 0x0B, + STATUS_ERR_DENIED = STATUS_CATEGORY_COMMON | 0x0c, + STATUS_ERR_ALREADY_INITIALIZED = STATUS_CATEGORY_COMMON | 0x0d, + STATUS_ERR_OVERFLOW = STATUS_CATEGORY_COMMON | 0x0e, + STATUS_ERR_NOT_INITIALIZED = STATUS_CATEGORY_COMMON | 0x0f, + + STATUS_ERR_SAMPLERATE_UNAVAILABLE = STATUS_CATEGORY_ANALOG | 0x00, + STATUS_ERR_RESOLUTION_UNAVAILABLE = STATUS_CATEGORY_ANALOG | 0x01, + + STATUS_ERR_BAUDRATE_UNAVAILABLE = STATUS_CATEGORY_COM | 0x00, + STATUS_ERR_PACKET_COLLISION = STATUS_CATEGORY_COM | 0x01, + STATUS_ERR_PROTOCOL = STATUS_CATEGORY_COM | 0x02, + + STATUS_ERR_PIN_MUX_INVALID = STATUS_CATEGORY_IO | 0x00, +}; +typedef enum status_code status_code_genare_t; + +/** + Status codes used by MAC stack. + */ +enum status_code_wireless { + //STATUS_OK = 0, //!< Success + ERR_IO_ERROR = -1, //!< I/O error + ERR_FLUSHED = -2, //!< Request flushed from queue + ERR_TIMEOUT = -3, //!< Operation timed out + ERR_BAD_DATA = -4, //!< Data integrity check failed + ERR_PROTOCOL = -5, //!< Protocol error + ERR_UNSUPPORTED_DEV = -6, //!< Unsupported device + ERR_NO_MEMORY = -7, //!< Insufficient memory + ERR_INVALID_ARG = -8, //!< Invalid argument + ERR_BAD_ADDRESS = -9, //!< Bad address + ERR_BUSY = -10, //!< Resource is busy + ERR_BAD_FORMAT = -11, //!< Data format not recognized + ERR_NO_TIMER = -12, //!< No timer available + ERR_TIMER_ALREADY_RUNNING = -13, //!< Timer already running + ERR_TIMER_NOT_RUNNING = -14, //!< Timer not running + + /** + * \brief Operation in progress + * + * This status code is for driver-internal use when an operation + * is currently being performed. + * + * \note Drivers should never return this status code to any + * callers. It is strictly for internal use. + */ + OPERATION_IN_PROGRESS = -128, +}; + +typedef enum status_code_wireless status_code_t; + +/** @} */ + +#endif /* STATUS_CODES_H_INCLUDED */ diff --git a/tmk_core/protocol/arm_atsam/usb/udc.c b/tmk_core/protocol/arm_atsam/usb/udc.c new file mode 100644 index 0000000000..12444d3059 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udc.c @@ -0,0 +1,1164 @@ +/** + * \file + * + * \brief USB Device Controller (UDC) + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "conf_usb.h" +#include "usb_protocol.h" +#include "udd.h" +#include "udc_desc.h" +#include "udi_device_conf.h" +#include "udi.h" +#include "udc.h" +#include "md_bootloader.h" + +/** + * \ingroup udc_group + * \defgroup udc_group_interne Implementation of UDC + * + * Internal implementation + * @{ + */ + +//! \name Internal variables to manage the USB device +//! @{ + +//! Device status state (see enum usb_device_status in usb_protocol.h) +static le16_t udc_device_status; + +COMPILER_WORD_ALIGNED +//! Device interface setting value +static uint8_t udc_iface_setting = 0; + +//! Device Configuration number selected by the USB host +COMPILER_WORD_ALIGNED +static uint8_t udc_num_configuration = 0; + +//! Pointer on the selected speed device configuration +static udc_config_speed_t UDC_DESC_STORAGE *udc_ptr_conf; + +//! Pointer on interface descriptor used by SETUP request. +static usb_iface_desc_t UDC_DESC_STORAGE *udc_ptr_iface; + +//! @} + + +//! \name Internal structure to store the USB device main strings +//! @{ + +/** + * \brief Language ID of USB device (US ID by default) + */ +COMPILER_WORD_ALIGNED +static UDC_DESC_STORAGE usb_str_lgid_desc_t udc_string_desc_languageid = { + .desc.bLength = sizeof(usb_str_lgid_desc_t), + .desc.bDescriptorType = USB_DT_STRING, + .string = {LE16(USB_LANGID_EN_US)} +}; + +/** + * \brief USB device manufacture name storage + * String is allocated only if USB_DEVICE_MANUFACTURE_NAME is declared + * by usb application configuration + */ +#ifdef USB_DEVICE_MANUFACTURE_NAME +static uint8_t udc_string_manufacturer_name[] = USB_DEVICE_MANUFACTURE_NAME; +#define USB_DEVICE_MANUFACTURE_NAME_SIZE (sizeof(udc_string_manufacturer_name)-1) +#else +#define USB_DEVICE_MANUFACTURE_NAME_SIZE 0 +#endif + +/** + * \brief USB device product name storage + * String is allocated only if USB_DEVICE_PRODUCT_NAME is declared + * by usb application configuration + */ +#ifdef USB_DEVICE_PRODUCT_NAME +static uint8_t udc_string_product_name[] = USB_DEVICE_PRODUCT_NAME; +#define USB_DEVICE_PRODUCT_NAME_SIZE (sizeof(udc_string_product_name)-1) +#else +#define USB_DEVICE_PRODUCT_NAME_SIZE 0 +#endif + +#if defined USB_DEVICE_SERIAL_NAME +#define USB_DEVICE_SERIAL_NAME_SIZE (sizeof(USB_DEVICE_SERIAL_NAME)-1) +#else +#define USB_DEVICE_SERIAL_NAME_SIZE 0 +#endif + +uint8_t usb_device_serial_name_size = 0; +#if defined USB_DEVICE_SERIAL_USE_BOOTLOADER_SERIAL +uint8_t bootloader_serial_number[BOOTLOADER_SERIAL_MAX_SIZE+1]=""; +#endif +static const uint8_t *udc_get_string_serial_name(void) +{ +#if defined USB_DEVICE_SERIAL_USE_BOOTLOADER_SERIAL + uint32_t serial_ptrloc = (uint32_t)&_srom - 4; + uint32_t serial_address = *(uint32_t *)serial_ptrloc; //Address of bootloader's serial number if available + + if (serial_address != 0xFFFFFFFF && serial_address < serial_ptrloc) //Check for factory programmed serial address + { + if ((serial_address & 0xFF) % 4 == 0) //Check alignment + { + uint16_t *serial_use = (uint16_t *)(serial_address); //Point to address of string in rom + uint8_t serial_length = 0; + + while ((*(serial_use + serial_length) > 32 && *(serial_use + serial_length) < 127) && + serial_length < BOOTLOADER_SERIAL_MAX_SIZE) + { + bootloader_serial_number[serial_length] = *(serial_use + serial_length) & 0xFF; + serial_length++; + } + bootloader_serial_number[serial_length] = 0; + + usb_device_serial_name_size = serial_length; + + return bootloader_serial_number; //Use serial programmed into bootloader rom + } + } +#endif + + usb_device_serial_name_size = USB_DEVICE_SERIAL_NAME_SIZE; + +#if defined USB_DEVICE_SERIAL_NAME + return (const uint8_t *)USB_DEVICE_SERIAL_NAME; //Use serial supplied by keyboard's config.h +#else + return 0; //No serial supplied +#endif +} + +/** + * \brief USB device string descriptor + * Structure used to transfer ASCII strings to USB String descriptor structure. + */ +#ifndef BOOTLOADER_SERIAL_MAX_SIZE +#define BOOTLOADER_SERIAL_MAX_SIZE 0 +#endif //BOOTLOADER_SERIAL_MAX_SIZE +struct udc_string_desc_t { + usb_str_desc_t header; + le16_t string[Max(Max(Max(USB_DEVICE_MANUFACTURE_NAME_SIZE, \ + USB_DEVICE_PRODUCT_NAME_SIZE), USB_DEVICE_SERIAL_NAME_SIZE), \ + BOOTLOADER_SERIAL_MAX_SIZE)]; +}; +COMPILER_WORD_ALIGNED +static UDC_DESC_STORAGE struct udc_string_desc_t udc_string_desc = { + .header.bDescriptorType = USB_DT_STRING +}; +//! @} + +usb_iface_desc_t UDC_DESC_STORAGE *udc_get_interface_desc(void) +{ + return udc_ptr_iface; +} + +/** + * \brief Returns a value to check the end of USB Configuration descriptor + * + * \return address after the last byte of USB Configuration descriptor + */ +static usb_conf_desc_t UDC_DESC_STORAGE *udc_get_eof_conf(void) +{ + return (UDC_DESC_STORAGE usb_conf_desc_t *) ((uint8_t *) + udc_ptr_conf->desc + + le16_to_cpu(udc_ptr_conf->desc->wTotalLength)); +} + +#if (0!=USB_DEVICE_MAX_EP) +/** + * \brief Search specific descriptor in global interface descriptor + * + * \param desc Address of interface descriptor + * or previous specific descriptor found + * \param desc_id Descriptor ID to search + * + * \return address of specific descriptor found + * \return NULL if it is the end of global interface descriptor + */ +static usb_conf_desc_t UDC_DESC_STORAGE *udc_next_desc_in_iface(usb_conf_desc_t + UDC_DESC_STORAGE * desc, uint8_t desc_id) +{ + usb_conf_desc_t UDC_DESC_STORAGE *ptr_eof_desc; + + ptr_eof_desc = udc_get_eof_conf(); + // Go to next descriptor + desc = (UDC_DESC_STORAGE usb_conf_desc_t *) ((uint8_t *) desc + + desc->bLength); + // Check the end of configuration descriptor + while (ptr_eof_desc > desc) { + // If new interface descriptor is found, + // then it is the end of the current global interface descriptor + if (USB_DT_INTERFACE == desc->bDescriptorType) { + break; // End of global interface descriptor + } + if (desc_id == desc->bDescriptorType) { + return desc; // Specific descriptor found + } + // Go to next descriptor + desc = (UDC_DESC_STORAGE usb_conf_desc_t *) ((uint8_t *) desc + + desc->bLength); + } + return NULL; // No specific descriptor found +} +#endif + +/** + * \brief Search an interface descriptor + * This routine updates the internal pointer udc_ptr_iface. + * + * \param iface_num Interface number to find in Configuration Descriptor + * \param setting_num Setting number of interface to find + * + * \return 1 if found or 0 if not found + */ +static bool udc_update_iface_desc(uint8_t iface_num, uint8_t setting_num) +{ + usb_conf_desc_t UDC_DESC_STORAGE *ptr_end_desc; + + if (0 == udc_num_configuration) { + return false; + } + + if (iface_num >= udc_ptr_conf->desc->bNumInterfaces) { + return false; + } + + // Start at the beginning of configuration descriptor + udc_ptr_iface = (UDC_DESC_STORAGE usb_iface_desc_t *) + udc_ptr_conf->desc; + + // Check the end of configuration descriptor + ptr_end_desc = udc_get_eof_conf(); + while (ptr_end_desc > + (UDC_DESC_STORAGE usb_conf_desc_t *) udc_ptr_iface) { + if (USB_DT_INTERFACE == udc_ptr_iface->bDescriptorType) { + // A interface descriptor is found + // Check interface and alternate setting number + if ((iface_num == udc_ptr_iface->bInterfaceNumber) && + (setting_num == + udc_ptr_iface->bAlternateSetting)) { + return true; // Interface found + } + } + // Go to next descriptor + udc_ptr_iface = (UDC_DESC_STORAGE usb_iface_desc_t *) ( + (uint8_t *) udc_ptr_iface + + udc_ptr_iface->bLength); + } + return false; // Interface not found +} + +/** + * \brief Disables an usb device interface (UDI) + * This routine call the UDI corresponding to interface number + * + * \param iface_num Interface number to disable + * + * \return 1 if it is done or 0 if interface is not found + */ +static bool udc_iface_disable(uint8_t iface_num) +{ + udi_api_t UDC_DESC_STORAGE *udi_api; + + // Select first alternate setting of the interface + // to update udc_ptr_iface before call iface->getsetting() + if (!udc_update_iface_desc(iface_num, 0)) { + return false; + } + + // Select the interface with the current alternate setting + udi_api = udc_ptr_conf->udi_apis[iface_num]; + +#if (0!=USB_DEVICE_MAX_EP) + if (!udc_update_iface_desc(iface_num, udi_api->getsetting())) { + return false; + } + + // Start at the beginning of interface descriptor + { + usb_ep_desc_t UDC_DESC_STORAGE *ep_desc; + ep_desc = (UDC_DESC_STORAGE usb_ep_desc_t *) udc_ptr_iface; + while (1) { + // Search Endpoint descriptor included in global interface descriptor + ep_desc = (UDC_DESC_STORAGE usb_ep_desc_t *) + udc_next_desc_in_iface((UDC_DESC_STORAGE + usb_conf_desc_t *) + ep_desc, USB_DT_ENDPOINT); + if (NULL == ep_desc) { + break; + } + // Free the endpoint used by the interface + udd_ep_free(ep_desc->bEndpointAddress); + } + } +#endif + + // Disable interface + udi_api->disable(); + return true; +} + +/** + * \brief Enables an usb device interface (UDI) + * This routine calls the UDI corresponding + * to the interface and setting number. + * + * \param iface_num Interface number to enable + * \param setting_num Setting number to enable + * + * \return 1 if it is done or 0 if interface is not found + */ +static bool udc_iface_enable(uint8_t iface_num, uint8_t setting_num) +{ + // Select the interface descriptor + if (!udc_update_iface_desc(iface_num, setting_num)) { + return false; + } + +#if (0!=USB_DEVICE_MAX_EP) + usb_ep_desc_t UDC_DESC_STORAGE *ep_desc; + + // Start at the beginning of the global interface descriptor + ep_desc = (UDC_DESC_STORAGE usb_ep_desc_t *) udc_ptr_iface; + while (1) { + // Search Endpoint descriptor included in the global interface descriptor + ep_desc = (UDC_DESC_STORAGE usb_ep_desc_t *) + udc_next_desc_in_iface((UDC_DESC_STORAGE + usb_conf_desc_t *) ep_desc, + USB_DT_ENDPOINT); + if (NULL == ep_desc) + break; + // Alloc the endpoint used by the interface + if (!udd_ep_alloc(ep_desc->bEndpointAddress, + ep_desc->bmAttributes, + le16_to_cpu + (ep_desc->wMaxPacketSize))) { + return false; + } + } +#endif + // Enable the interface + return udc_ptr_conf->udi_apis[iface_num]->enable(); +} + +/*! \brief Start the USB Device stack + */ +void udc_start(void) +{ + udd_enable(); +} + +/*! \brief Stop the USB Device stack + */ +void udc_stop(void) +{ + udd_disable(); + udc_reset(); +} + +/** + * \brief Reset the current configuration of the USB device, + * This routines can be called by UDD when a RESET on the USB line occurs. + */ +void udc_reset(void) +{ + uint8_t iface_num; + + if (udc_num_configuration) { + for (iface_num = 0; + iface_num < udc_ptr_conf->desc->bNumInterfaces; + iface_num++) { + udc_iface_disable(iface_num); + } + } + udc_num_configuration = 0; +#if (USB_CONFIG_ATTR_REMOTE_WAKEUP \ + == (USB_DEVICE_ATTR & USB_CONFIG_ATTR_REMOTE_WAKEUP)) + if (CPU_TO_LE16(USB_DEV_STATUS_REMOTEWAKEUP) & udc_device_status) { + // Remote wakeup is enabled then disable it + UDC_REMOTEWAKEUP_DISABLE(); + } +#endif + udc_device_status = +#if (USB_DEVICE_ATTR & USB_CONFIG_ATTR_SELF_POWERED) + CPU_TO_LE16(USB_DEV_STATUS_SELF_POWERED); +#else + CPU_TO_LE16(USB_DEV_STATUS_BUS_POWERED); +#endif +} + +void udc_sof_notify(void) +{ + uint8_t iface_num; + + if (udc_num_configuration) { + for (iface_num = 0; + iface_num < udc_ptr_conf->desc->bNumInterfaces; + iface_num++) { + if (udc_ptr_conf->udi_apis[iface_num]->sof_notify != NULL) { + udc_ptr_conf->udi_apis[iface_num]->sof_notify(); + } + } + } +} + +/** + * \brief Standard device request to get device status + * + * \return true if success + */ +static bool udc_req_std_dev_get_status(void) +{ + if (udd_g_ctrlreq.req.wLength != sizeof(udc_device_status)) { + return false; + } + + udd_set_setup_payload( (uint8_t *) & udc_device_status, + sizeof(udc_device_status)); + return true; +} + +#if (0!=USB_DEVICE_MAX_EP) +/** + * \brief Standard endpoint request to get endpoint status + * + * \return true if success + */ +static bool udc_req_std_ep_get_status(void) +{ + static le16_t udc_ep_status; + + if (udd_g_ctrlreq.req.wLength != sizeof(udc_ep_status)) { + return false; + } + + udc_ep_status = udd_ep_is_halted(udd_g_ctrlreq.req. + wIndex & 0xFF) ? CPU_TO_LE16(USB_EP_STATUS_HALTED) : 0; + + udd_set_setup_payload( (uint8_t *) & udc_ep_status, + sizeof(udc_ep_status)); + return true; +} +#endif + +/** + * \brief Standard device request to change device status + * + * \return true if success + */ +static bool udc_req_std_dev_clear_feature(void) +{ + if (udd_g_ctrlreq.req.wLength) { + return false; + } + + if (udd_g_ctrlreq.req.wValue == USB_DEV_FEATURE_REMOTE_WAKEUP) { + udc_device_status &= CPU_TO_LE16(~(uint32_t)USB_DEV_STATUS_REMOTEWAKEUP); +#if (USB_CONFIG_ATTR_REMOTE_WAKEUP \ + == (USB_DEVICE_ATTR & USB_CONFIG_ATTR_REMOTE_WAKEUP)) + UDC_REMOTEWAKEUP_DISABLE(); +#endif + return true; + } + return false; +} + +#if (0!=USB_DEVICE_MAX_EP) +/** + * \brief Standard endpoint request to clear endpoint feature + * + * \return true if success + */ +static bool udc_req_std_ep_clear_feature(void) +{ + if (udd_g_ctrlreq.req.wLength) { + return false; + } + + if (udd_g_ctrlreq.req.wValue == USB_EP_FEATURE_HALT) { + return udd_ep_clear_halt(udd_g_ctrlreq.req.wIndex & 0xFF); + } + return false; +} +#endif + +/** + * \brief Standard device request to set a feature + * + * \return true if success + */ +static bool udc_req_std_dev_set_feature(void) +{ + if (udd_g_ctrlreq.req.wLength) { + return false; + } + + switch (udd_g_ctrlreq.req.wValue) { + + case USB_DEV_FEATURE_REMOTE_WAKEUP: +#if (USB_CONFIG_ATTR_REMOTE_WAKEUP \ + == (USB_DEVICE_ATTR & USB_CONFIG_ATTR_REMOTE_WAKEUP)) + udc_device_status |= CPU_TO_LE16(USB_DEV_STATUS_REMOTEWAKEUP); + UDC_REMOTEWAKEUP_ENABLE(); + return true; +#else + return false; +#endif + +#ifdef USB_DEVICE_HS_SUPPORT + case USB_DEV_FEATURE_TEST_MODE: + if (!udd_is_high_speed()) { + break; + } + if (udd_g_ctrlreq.req.wIndex & 0xff) { + break; + } + // Unconfigure the device, terminating all ongoing requests + udc_reset(); + switch ((udd_g_ctrlreq.req.wIndex >> 8) & 0xFF) { + case USB_DEV_TEST_MODE_J: + udd_g_ctrlreq.callback = udd_test_mode_j; + return true; + + case USB_DEV_TEST_MODE_K: + udd_g_ctrlreq.callback = udd_test_mode_k; + return true; + + case USB_DEV_TEST_MODE_SE0_NAK: + udd_g_ctrlreq.callback = udd_test_mode_se0_nak; + return true; + + case USB_DEV_TEST_MODE_PACKET: + udd_g_ctrlreq.callback = udd_test_mode_packet; + return true; + + case USB_DEV_TEST_MODE_FORCE_ENABLE: // Only for downstream facing hub ports + default: + break; + } + break; +#endif + default: + break; + } + return false; +} + +/** + * \brief Standard endpoint request to halt an endpoint + * + * \return true if success + */ +#if (0!=USB_DEVICE_MAX_EP) +static bool udc_req_std_ep_set_feature(void) +{ + if (udd_g_ctrlreq.req.wLength) { + return false; + } + if (udd_g_ctrlreq.req.wValue == USB_EP_FEATURE_HALT) { + udd_ep_abort(udd_g_ctrlreq.req.wIndex & 0xFF); + return udd_ep_set_halt(udd_g_ctrlreq.req.wIndex & 0xFF); + } + return false; +} +#endif + +/** + * \brief Change the address of device + * Callback called at the end of request set address + */ +static void udc_valid_address(void) +{ + udd_set_address(udd_g_ctrlreq.req.wValue & 0x7F); +} + +/** + * \brief Standard device request to set device address + * + * \return true if success + */ +static bool udc_req_std_dev_set_address(void) +{ + if (udd_g_ctrlreq.req.wLength) { + return false; + } + + // The address must be changed at the end of setup request after the handshake + // then we use a callback to change address + udd_g_ctrlreq.callback = udc_valid_address; + return true; +} + +/** + * \brief Standard device request to get device string descriptor + * + * \return true if success + */ +static bool udc_req_std_dev_get_str_desc(void) +{ + uint8_t i; + const uint8_t *str; + uint8_t str_length = 0; + + // Link payload pointer to the string corresponding at request + switch (udd_g_ctrlreq.req.wValue & 0xff) { + case 0: + udd_set_setup_payload((uint8_t *) &udc_string_desc_languageid, + sizeof(udc_string_desc_languageid)); + break; + +#ifdef USB_DEVICE_MANUFACTURE_NAME + case 1: + str_length = USB_DEVICE_MANUFACTURE_NAME_SIZE; + str = udc_string_manufacturer_name; + break; +#endif +#ifdef USB_DEVICE_PRODUCT_NAME + case 2: + str_length = USB_DEVICE_PRODUCT_NAME_SIZE; + str = udc_string_product_name; + break; +#endif + case 3: + str = udc_get_string_serial_name(); + str_length = usb_device_serial_name_size; + break; + default: +#ifdef UDC_GET_EXTRA_STRING + if (UDC_GET_EXTRA_STRING()) { + break; + } +#endif + return false; + } + + if (str_length) { + for(i = 0; i < str_length; i++) { + udc_string_desc.string[i] = cpu_to_le16((le16_t)str[i]); + } + + udc_string_desc.header.bLength = 2 + (str_length) * 2; + udd_set_setup_payload( + (uint8_t *) &udc_string_desc, + udc_string_desc.header.bLength); + } + + return true; +} + +/** + * \brief Standard device request to get descriptors about USB device + * + * \return true if success + */ +static bool udc_req_std_dev_get_descriptor(void) +{ + uint8_t conf_num; + + conf_num = udd_g_ctrlreq.req.wValue & 0xff; + + // Check descriptor ID + switch ((uint8_t) (udd_g_ctrlreq.req.wValue >> 8)) { + case USB_DT_DEVICE: + // Device descriptor requested +#ifdef USB_DEVICE_HS_SUPPORT + if (!udd_is_high_speed()) { + udd_set_setup_payload( + (uint8_t *) udc_config.confdev_hs, + udc_config.confdev_hs->bLength); + } else +#endif + { + udd_set_setup_payload( + (uint8_t *) udc_config.confdev_lsfs, + udc_config.confdev_lsfs->bLength); + } + break; + + case USB_DT_CONFIGURATION: + // Configuration descriptor requested +#ifdef USB_DEVICE_HS_SUPPORT + if (udd_is_high_speed()) { + // HS descriptor + if (conf_num >= udc_config.confdev_hs->bNumConfigurations) { + return false; + } + udd_set_setup_payload( + (uint8_t *)udc_config.conf_hs[conf_num].desc, + le16_to_cpu(udc_config.conf_hs[conf_num].desc->wTotalLength)); + } else +#endif + { + // FS descriptor + if (conf_num >= udc_config.confdev_lsfs->bNumConfigurations) { + return false; + } + udd_set_setup_payload( + (uint8_t *)udc_config.conf_lsfs[conf_num].desc, + le16_to_cpu(udc_config.conf_lsfs[conf_num].desc->wTotalLength)); + } + ((usb_conf_desc_t *) udd_g_ctrlreq.payload)->bDescriptorType = + USB_DT_CONFIGURATION; + break; + +#ifdef USB_DEVICE_HS_SUPPORT + case USB_DT_DEVICE_QUALIFIER: + // Device qualifier descriptor requested + udd_set_setup_payload( (uint8_t *) udc_config.qualifier, + udc_config.qualifier->bLength); + break; + + case USB_DT_OTHER_SPEED_CONFIGURATION: + // Other configuration descriptor requested + if (!udd_is_high_speed()) { + // HS descriptor + if (conf_num >= udc_config.confdev_hs->bNumConfigurations) { + return false; + } + udd_set_setup_payload( + (uint8_t *)udc_config.conf_hs[conf_num].desc, + le16_to_cpu(udc_config.conf_hs[conf_num].desc->wTotalLength)); + } else { + // FS descriptor + if (conf_num >= udc_config.confdev_lsfs->bNumConfigurations) { + return false; + } + udd_set_setup_payload( + (uint8_t *)udc_config.conf_lsfs[conf_num].desc, + le16_to_cpu(udc_config.conf_lsfs[conf_num].desc->wTotalLength)); + } + ((usb_conf_desc_t *) udd_g_ctrlreq.payload)->bDescriptorType = + USB_DT_OTHER_SPEED_CONFIGURATION; + break; +#endif + + case USB_DT_BOS: + // Device BOS descriptor requested + if (udc_config.conf_bos == NULL) { + return false; + } + udd_set_setup_payload( (uint8_t *) udc_config.conf_bos, + udc_config.conf_bos->wTotalLength); + break; + + case USB_DT_STRING: + // String descriptor requested + if (!udc_req_std_dev_get_str_desc()) { + return false; + } + break; + + default: + // Unknown descriptor requested + return false; + } + // if the descriptor is larger than length requested, then reduce it + if (udd_g_ctrlreq.req.wLength < udd_g_ctrlreq.payload_size) { + udd_g_ctrlreq.payload_size = udd_g_ctrlreq.req.wLength; + } + return true; +} + +/** + * \brief Standard device request to get configuration number + * + * \return true if success + */ +static bool udc_req_std_dev_get_configuration(void) +{ + if (udd_g_ctrlreq.req.wLength != 1) { + return false; + } + + udd_set_setup_payload(&udc_num_configuration,1); + return true; +} + +/** + * \brief Standard device request to enable a configuration + * + * \return true if success + */ +static bool udc_req_std_dev_set_configuration(void) +{ + uint8_t iface_num; + + // Check request length + if (udd_g_ctrlreq.req.wLength) { + return false; + } + // Authorize configuration only if the address is valid + if (!udd_getaddress()) { + return false; + } + // Check the configuration number requested +#ifdef USB_DEVICE_HS_SUPPORT + if (udd_is_high_speed()) { + // HS descriptor + if ((udd_g_ctrlreq.req.wValue & 0xFF) > + udc_config.confdev_hs->bNumConfigurations) { + return false; + } + } else +#endif + { + // FS descriptor + if ((udd_g_ctrlreq.req.wValue & 0xFF) > + udc_config.confdev_lsfs->bNumConfigurations) { + return false; + } + } + + // Reset current configuration + udc_reset(); + + // Enable new configuration + udc_num_configuration = udd_g_ctrlreq.req.wValue & 0xFF; + if (udc_num_configuration == 0) { + return true; // Default empty configuration requested + } + // Update pointer of the configuration descriptor +#ifdef USB_DEVICE_HS_SUPPORT + if (udd_is_high_speed()) { + // HS descriptor + udc_ptr_conf = &udc_config.conf_hs[udc_num_configuration - 1]; + } else +#endif + { + // FS descriptor + udc_ptr_conf = &udc_config.conf_lsfs[udc_num_configuration - 1]; + } + // Enable all interfaces of the selected configuration + for (iface_num = 0; iface_num < udc_ptr_conf->desc->bNumInterfaces; + iface_num++) { + if (!udc_iface_enable(iface_num, 0)) { + return false; + } + } + return true; +} + +/** + * \brief Standard interface request + * to get the alternate setting number of an interface + * + * \return true if success + */ +static bool udc_req_std_iface_get_setting(void) +{ + uint8_t iface_num; + udi_api_t UDC_DESC_STORAGE *udi_api; + + if (udd_g_ctrlreq.req.wLength != 1) { + return false; // Error in request + } + if (!udc_num_configuration) { + return false; // The device is not is configured state yet + } + + // Check the interface number included in the request + iface_num = udd_g_ctrlreq.req.wIndex & 0xFF; + if (iface_num >= udc_ptr_conf->desc->bNumInterfaces) { + return false; + } + + // Select first alternate setting of the interface to update udc_ptr_iface + // before call iface->getsetting() + if (!udc_update_iface_desc(iface_num, 0)) { + return false; + } + // Get alternate setting from UDI + udi_api = udc_ptr_conf->udi_apis[iface_num]; + udc_iface_setting = udi_api->getsetting(); + + // Link value to payload pointer of request + udd_set_setup_payload(&udc_iface_setting,1); + return true; +} + +/** + * \brief Standard interface request + * to set an alternate setting of an interface + * + * \return true if success + */ +static bool udc_req_std_iface_set_setting(void) +{ + uint8_t iface_num, setting_num; + + if (udd_g_ctrlreq.req.wLength) { + return false; // Error in request + } + if (!udc_num_configuration) { + return false; // The device is not is configured state yet + } + + iface_num = udd_g_ctrlreq.req.wIndex & 0xFF; + setting_num = udd_g_ctrlreq.req.wValue & 0xFF; + + // Disable current setting + if (!udc_iface_disable(iface_num)) { + return false; + } + + // Enable new setting + return udc_iface_enable(iface_num, setting_num); +} + +/** + * \brief Main routine to manage the standard USB SETUP request + * + * \return true if the request is supported + */ +static bool udc_reqstd(void) +{ + if (Udd_setup_is_in()) { + // GET Standard Requests + if (udd_g_ctrlreq.req.wLength == 0) { + return false; // Error for USB host + } + + if (USB_REQ_RECIP_DEVICE == Udd_setup_recipient()) { + // Standard Get Device request + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_GET_STATUS: + return udc_req_std_dev_get_status(); + case USB_REQ_GET_DESCRIPTOR: + return udc_req_std_dev_get_descriptor(); + case USB_REQ_GET_CONFIGURATION: + return udc_req_std_dev_get_configuration(); + default: + break; + } + } + + if (USB_REQ_RECIP_INTERFACE == Udd_setup_recipient()) { + // Standard Get Interface request + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_GET_INTERFACE: + return udc_req_std_iface_get_setting(); + default: + break; + } + } +#if (0!=USB_DEVICE_MAX_EP) + if (USB_REQ_RECIP_ENDPOINT == Udd_setup_recipient()) { + // Standard Get Endpoint request + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_GET_STATUS: + return udc_req_std_ep_get_status(); + default: + break; + } + } +#endif + } else { + // SET Standard Requests + if (USB_REQ_RECIP_DEVICE == Udd_setup_recipient()) { + // Standard Set Device request + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_SET_ADDRESS: + return udc_req_std_dev_set_address(); + case USB_REQ_CLEAR_FEATURE: + return udc_req_std_dev_clear_feature(); + case USB_REQ_SET_FEATURE: + return udc_req_std_dev_set_feature(); + case USB_REQ_SET_CONFIGURATION: + return udc_req_std_dev_set_configuration(); + case USB_REQ_SET_DESCRIPTOR: + /* Not supported (defined as optional by the USB 2.0 spec) */ + break; + default: + break; + } + } + + if (USB_REQ_RECIP_INTERFACE == Udd_setup_recipient()) { + // Standard Set Interface request + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_SET_INTERFACE: + return udc_req_std_iface_set_setting(); + default: + break; + } + } +#if (0!=USB_DEVICE_MAX_EP) + if (USB_REQ_RECIP_ENDPOINT == Udd_setup_recipient()) { + // Standard Set Endpoint request + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_CLEAR_FEATURE: + return udc_req_std_ep_clear_feature(); + case USB_REQ_SET_FEATURE: + return udc_req_std_ep_set_feature(); + default: + break; + } + } +#endif + } + return false; +} + +/** + * \brief Send the SETUP interface request to UDI + * + * \return true if the request is supported + */ +static bool udc_req_iface(void) +{ + uint8_t iface_num; + udi_api_t UDC_DESC_STORAGE *udi_api; + + if (0 == udc_num_configuration) { + return false; // The device is not is configured state yet + } + // Check interface number + iface_num = udd_g_ctrlreq.req.wIndex & 0xFF; + if (iface_num >= udc_ptr_conf->desc->bNumInterfaces) { + return false; + } + + //* To update udc_ptr_iface with the selected interface in request + // Select first alternate setting of interface to update udc_ptr_iface + // before calling udi_api->getsetting() + if (!udc_update_iface_desc(iface_num, 0)) { + return false; + } + // Select the interface with the current alternate setting + udi_api = udc_ptr_conf->udi_apis[iface_num]; + if (!udc_update_iface_desc(iface_num, udi_api->getsetting())) { + return false; + } + + // Send the SETUP request to the UDI corresponding to the interface number + return udi_api->setup(); +} + +/** + * \brief Send the SETUP interface request to UDI + * + * \return true if the request is supported + */ +static bool udc_req_ep(void) +{ + uint8_t iface_num; + udi_api_t UDC_DESC_STORAGE *udi_api; + + if (0 == udc_num_configuration) { + return false; // The device is not is configured state yet + } + // Send this request on all enabled interfaces + iface_num = udd_g_ctrlreq.req.wIndex & 0xFF; + for (iface_num = 0; iface_num < udc_ptr_conf->desc->bNumInterfaces; + iface_num++) { + // Select the interface with the current alternate setting + udi_api = udc_ptr_conf->udi_apis[iface_num]; + if (!udc_update_iface_desc(iface_num, udi_api->getsetting())) { + return false; + } + + // Send the SETUP request to the UDI + if (udi_api->setup()) { + return true; + } + } + return false; +} + +/** + * \brief Main routine to manage the USB SETUP request. + * + * This function parses a USB SETUP request and submits an appropriate + * response back to the host or, in the case of SETUP OUT requests + * with data, sets up a buffer for receiving the data payload. + * + * The main standard requests defined by the USB 2.0 standard are handled + * internally. The interface requests are sent to UDI, and the specific request + * sent to a specific application callback. + * + * \return true if the request is supported, else the request is stalled by UDD + */ +bool udc_process_setup(void) +{ + // By default no data (receive/send) and no callbacks registered + udd_g_ctrlreq.payload_size = 0; + udd_g_ctrlreq.callback = NULL; + udd_g_ctrlreq.over_under_run = NULL; + + if (Udd_setup_is_in()) { + if (udd_g_ctrlreq.req.wLength == 0) { + return false; // Error from USB host + } + } + + // If standard request then try to decode it in UDC + if (Udd_setup_type() == USB_REQ_TYPE_STANDARD) { + if (udc_reqstd()) { + return true; + } + } + + // If interface request then try to decode it in UDI + if (Udd_setup_recipient() == USB_REQ_RECIP_INTERFACE) { + if (udc_req_iface()) { + return true; + } + } + + // If endpoint request then try to decode it in UDI + if (Udd_setup_recipient() == USB_REQ_RECIP_ENDPOINT) { + if (udc_req_ep()) { + return true; + } + } + + // Here SETUP request unknown by UDC and UDIs +#ifdef USB_DEVICE_SPECIFIC_REQUEST + // Try to decode it in specific callback + return USB_DEVICE_SPECIFIC_REQUEST(); // Ex: Vendor request,... +#else + return false; +#endif +} + +//! @} diff --git a/tmk_core/protocol/arm_atsam/usb/udc.h b/tmk_core/protocol/arm_atsam/usb/udc.h new file mode 100644 index 0000000000..33335d1869 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udc.h @@ -0,0 +1,260 @@ +/** + * \file + * + * \brief Interface of the USB Device Controller (UDC) + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDC_H_ +#define _UDC_H_ + +#include "conf_usb.h" +#include "usb_protocol.h" +#include "udc_desc.h" +#include "udd.h" + +#if USB_DEVICE_VENDOR_ID == 0 +# error USB_DEVICE_VENDOR_ID cannot be equal to 0 +#endif + +#if USB_DEVICE_PRODUCT_ID == 0 +# error USB_DEVICE_PRODUCT_ID cannot be equal to 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup usb_device_group + * \defgroup udc_group USB Device Controller (UDC) + * + * The UDC provides a high-level abstraction of the usb device. + * You can use these functions to control the main device state + * (start/attach/wakeup). + * + * \section USB_DEVICE_CONF USB Device Custom configuration + * The following USB Device configuration must be included in the conf_usb.h + * file of the application. + * + * USB_DEVICE_VENDOR_ID (Word)<br> + * Vendor ID provided by USB org (ATMEL 0x03EB). + * + * USB_DEVICE_PRODUCT_ID (Word)<br> + * Product ID (Referenced in usb_atmel.h). + * + * USB_DEVICE_MAJOR_VERSION (Byte)<br> + * Major version of the device + * + * USB_DEVICE_MINOR_VERSION (Byte)<br> + * Minor version of the device + * + * USB_DEVICE_MANUFACTURE_NAME (string)<br> + * ASCII name for the manufacture + * + * USB_DEVICE_PRODUCT_NAME (string)<br> + * ASCII name for the product + * + * USB_DEVICE_SERIAL_NAME (string)<br> + * ASCII name to enable and set a serial number + * + * USB_DEVICE_POWER (Numeric)<br> + * (unit mA) Maximum device power + * + * USB_DEVICE_ATTR (Byte)<br> + * USB attributes available: + * - USB_CONFIG_ATTR_SELF_POWERED + * - USB_CONFIG_ATTR_REMOTE_WAKEUP + * Note: if remote wake enabled then defines remotewakeup callbacks, + * see Table 5-2. External API from UDC - Callback + * + * USB_DEVICE_LOW_SPEED (Only defined)<br> + * Force the USB Device to run in low speed + * + * USB_DEVICE_HS_SUPPORT (Only defined)<br> + * Authorize the USB Device to run in high speed + * + * USB_DEVICE_MAX_EP (Byte)<br> + * Define the maximum endpoint number used by the USB Device.<br> + * This one is already defined in UDI default configuration. + * Ex: + * - When endpoint control 0x00, endpoint 0x01 and + * endpoint 0x82 is used then USB_DEVICE_MAX_EP=2 + * - When only endpoint control 0x00 is used then USB_DEVICE_MAX_EP=0 + * - When endpoint 0x01 and endpoint 0x81 is used then USB_DEVICE_MAX_EP=1<br> + * (configuration not possible on USBB interface) + * @{ + */ + +/** + * \brief Authorizes the VBUS event + * + * \return true, if the VBUS monitoring is possible. + * + * \section udc_vbus_monitoring VBus monitoring used cases + * + * The VBus monitoring is used only for USB SELF Power application. + * + * - By default the USB device is automatically attached when Vbus is high + * or when USB is start for devices without internal Vbus monitoring. + * conf_usb.h file does not contains define USB_DEVICE_ATTACH_AUTO_DISABLE. + * \code //#define USB_DEVICE_ATTACH_AUTO_DISABLE \endcode + * + * - Add custom VBUS monitoring. conf_usb.h file contains define + * USB_DEVICE_ATTACH_AUTO_DISABLE: + * \code #define USB_DEVICE_ATTACH_AUTO_DISABLE \endcode + * User C file contains: + * \code + // Authorize VBUS monitoring + if (!udc_include_vbus_monitoring()) { + // Implement custom VBUS monitoring via GPIO or other + } + Event_VBUS_present() // VBUS interrupt or GPIO interrupt or other + { + // Attach USB Device + udc_attach(); + } +\endcode + * + * - Case of battery charging. conf_usb.h file contains define + * USB_DEVICE_ATTACH_AUTO_DISABLE: + * \code #define USB_DEVICE_ATTACH_AUTO_DISABLE \endcode + * User C file contains: + * \code + Event VBUS present() // VBUS interrupt or GPIO interrupt or .. + { + // Authorize battery charging, but wait key press to start USB. + } + Event Key press() + { + // Stop batteries charging + // Start USB + udc_attach(); + } +\endcode + */ +static inline bool udc_include_vbus_monitoring(void) +{ + return udd_include_vbus_monitoring(); +} + +/*! \brief Start the USB Device stack + */ +void udc_start(void); + +/*! \brief Stop the USB Device stack + */ +void udc_stop(void); + +/** + * \brief Attach device to the bus when possible + * + * \warning If a VBus control is included in driver, + * then it will attach device when an acceptable Vbus + * level from the host is detected. + */ +static inline void udc_attach(void) +{ + udd_attach(); +} + +/** + * \brief Detaches the device from the bus + * + * The driver must remove pull-up on USB line D- or D+. + */ +static inline void udc_detach(void) +{ + udd_detach(); +} + +/*! \brief The USB driver sends a resume signal called \e "Upstream Resume" + * This is authorized only when the remote wakeup feature is enabled by host. + */ +inline void udc_remotewakeup(void) +{ + udd_send_remotewakeup(); +} + +/** + * \brief Returns a pointer on the current interface descriptor + * + * \return pointer on the current interface descriptor. + */ +usb_iface_desc_t UDC_DESC_STORAGE *udc_get_interface_desc(void); + +//@} + +/** + * \ingroup usb_group + * \defgroup usb_device_group USB Stack Device + * + * This module includes USB Stack Device implementation. + * The stack is divided in three parts: + * - USB Device Controller (UDC) provides USB chapter 9 compliance + * - USB Device Interface (UDI) provides USB Class compliance + * - USB Device Driver (UDD) provides USB Driver for each Atmel MCU + + * Many USB Device applications can be implemented on Atmel MCU. + * Atmel provides many application notes for different applications: + * - AVR4900, provides general information about Device Stack + * - AVR4901, explains how to create a new class + * - AVR4902, explains how to create a composite device + * - AVR49xx, all device classes provided in ASF have an application note + * + * A basic USB knowledge is required to understand the USB Device + * Class application notes (HID,MS,CDC,PHDC,...). + * Then, to create an USB device with + * only one class provided by ASF, refer directly to the application note + * corresponding to this USB class. The USB Device application note for + * New Class and Composite is dedicated to advanced USB users. + * + * @{ + */ + +//! @} + +#ifdef __cplusplus +} +#endif + +#endif // _UDC_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udc_desc.h b/tmk_core/protocol/arm_atsam/usb/udc_desc.h new file mode 100644 index 0000000000..9cab03dcb3 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udc_desc.h @@ -0,0 +1,135 @@ +/** + * \file + * + * \brief Common API for USB Device Interface + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDC_DESC_H_ +#define _UDC_DESC_H_ + +#include "conf_usb.h" +#include "usb_protocol.h" +#include "udi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup udc_group + * \defgroup udc_desc_group USB Device Descriptor + * + * @{ + */ + +/** + * \brief Defines the memory's location of USB descriptors + * + * By default the Descriptor is stored in RAM + * (UDC_DESC_STORAGE is defined empty). + * + * If you have need to free RAM space, + * it is possible to put descriptor in flash in following case: + * - USB driver authorize flash transfer (USBB on UC3 and USB on Mega) + * - USB Device is not high speed (UDC no need to change USB descriptors) + * + * For UC3 application used "const". + * + * For Mega application used "code". + */ +#define UDC_DESC_STORAGE + // Descriptor storage in internal RAM +#if (defined UDC_DATA_USE_HRAM_SUPPORT) +#if defined(__GNUC__) +#define UDC_DATA(x) COMPILER_WORD_ALIGNED __attribute__((__section__(".data_hram0"))) +#define UDC_BSS(x) COMPILER_ALIGNED(x) __attribute__((__section__(".bss_hram0"))) +#elif defined(__ICCAVR32__) +#define UDC_DATA(x) COMPILER_ALIGNED(x) __data32 +#define UDC_BSS(x) COMPILER_ALIGNED(x) __data32 +#endif +#else +#define UDC_DATA(x) COMPILER_ALIGNED(x) +#define UDC_BSS(x) COMPILER_ALIGNED(x) +#endif + + + +/** + * \brief Configuration descriptor and UDI link for one USB speed + */ +typedef struct { + //! USB configuration descriptor + usb_conf_desc_t UDC_DESC_STORAGE *desc; + //! Array of UDI API pointer + udi_api_t UDC_DESC_STORAGE *UDC_DESC_STORAGE * udi_apis; +} udc_config_speed_t; + + +/** + * \brief All information about the USB Device + */ +typedef struct { + //! USB device descriptor for low or full speed + usb_dev_desc_t UDC_DESC_STORAGE *confdev_lsfs; + //! USB configuration descriptor and UDI API pointers for low or full speed + udc_config_speed_t UDC_DESC_STORAGE *conf_lsfs; +#ifdef USB_DEVICE_HS_SUPPORT + //! USB device descriptor for high speed + usb_dev_desc_t UDC_DESC_STORAGE *confdev_hs; + //! USB device qualifier, only use in high speed mode + usb_dev_qual_desc_t UDC_DESC_STORAGE *qualifier; + //! USB configuration descriptor and UDI API pointers for high speed + udc_config_speed_t UDC_DESC_STORAGE *conf_hs; +#endif + usb_dev_bos_desc_t UDC_DESC_STORAGE *conf_bos; +} udc_config_t; + +//! Global variables of USB Device Descriptor and UDI links +extern UDC_DESC_STORAGE udc_config_t udc_config; + +//@} + +#ifdef __cplusplus +} +#endif +#endif // _UDC_DESC_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udd.h b/tmk_core/protocol/arm_atsam/usb/udd.h new file mode 100644 index 0000000000..b580e58479 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udd.h @@ -0,0 +1,396 @@ +/** + * \file + * + * \brief Common API for USB Device Drivers (UDD) + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDD_H_ +#define _UDD_H_ + +#include "usb_protocol.h" +#include "udc_desc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup usb_device_group + * \defgroup udd_group USB Device Driver (UDD) + * + * The UDD driver provides a low-level abstraction of the device + * controller hardware. Most events coming from the hardware such as + * interrupts, which may cause the UDD to call into the UDC and UDI. + * + * @{ + */ + +//! \brief Endpoint identifier +typedef uint8_t udd_ep_id_t; + +//! \brief Endpoint transfer status +//! Returned in parameters of callback register via udd_ep_run routine. +typedef enum { + UDD_EP_TRANSFER_OK = 0, + UDD_EP_TRANSFER_ABORT = 1, +} udd_ep_status_t; + +/** + * \brief Global variable to give and record information of the setup request management + * + * This global variable allows to decode and response a setup request. + * It can be updated by udc_process_setup() from UDC or *setup() from UDIs. + */ +typedef struct { + //! Data received in USB SETUP packet + //! Note: The swap of "req.wValues" from uin16_t to le16_t is done by UDD. + usb_setup_req_t req; + + //! Point to buffer to send or fill with data following SETUP packet + //! This buffer must be word align for DATA IN phase (use prefix COMPILER_WORD_ALIGNED for buffer) + uint8_t *payload; + + //! Size of buffer to send or fill, and content the number of byte transfered + uint16_t payload_size; + + //! Callback called after reception of ZLP from setup request + void (*callback) (void); + + //! Callback called when the buffer given (.payload) is full or empty. + //! This one return false to abort data transfer, or true with a new buffer in .payload. + bool(*over_under_run) (void); +} udd_ctrl_request_t; +extern udd_ctrl_request_t udd_g_ctrlreq; + +//! Return true if the setup request \a udd_g_ctrlreq indicates IN data transfer +#define Udd_setup_is_in() \ + (USB_REQ_DIR_IN == (udd_g_ctrlreq.req.bmRequestType & USB_REQ_DIR_MASK)) + +//! Return true if the setup request \a udd_g_ctrlreq indicates OUT data transfer +#define Udd_setup_is_out() \ + (USB_REQ_DIR_OUT == (udd_g_ctrlreq.req.bmRequestType & USB_REQ_DIR_MASK)) + +//! Return the type of the SETUP request \a udd_g_ctrlreq. \see usb_reqtype. +#define Udd_setup_type() \ + (udd_g_ctrlreq.req.bmRequestType & USB_REQ_TYPE_MASK) + +//! Return the recipient of the SETUP request \a udd_g_ctrlreq. \see usb_recipient +#define Udd_setup_recipient() \ + (udd_g_ctrlreq.req.bmRequestType & USB_REQ_RECIP_MASK) + +/** + * \brief End of halt callback function type. + * Registered by routine udd_ep_wait_stall_clear() + * Callback called when endpoint stall is cleared. + */ +typedef void (*udd_callback_halt_cleared_t) (void); + +/** + * \brief End of transfer callback function type. + * Registered by routine udd_ep_run() + * Callback called by USB interrupt after data transfer or abort (reset,...). + * + * \param status UDD_EP_TRANSFER_OK, if transfer is complete + * \param status UDD_EP_TRANSFER_ABORT, if transfer is aborted + * \param n number of data transfered + */ +typedef void (*udd_callback_trans_t) (udd_ep_status_t status, + iram_size_t nb_transfered, udd_ep_id_t ep); + +/** + * \brief Authorizes the VBUS event + * + * \return true, if the VBUS monitoring is possible. + */ +bool udd_include_vbus_monitoring(void); + +/** + * \brief Enables the USB Device mode + */ +void udd_enable(void); + +/** + * \brief Disables the USB Device mode + */ +void udd_disable(void); + +/** + * \brief Attach device to the bus when possible + * + * \warning If a VBus control is included in driver, + * then it will attach device when an acceptable Vbus + * level from the host is detected. + */ +void udd_attach(void); + +/** + * \brief Detaches the device from the bus + * + * The driver must remove pull-up on USB line D- or D+. + */ +void udd_detach(void); + +/** + * \brief Test whether the USB Device Controller is running at high + * speed or not. + * + * \return \c true if the Device is running at high speed mode, otherwise \c false. + */ +bool udd_is_high_speed(void); + +/** + * \brief Changes the USB address of device + * + * \param address New USB address + */ +void udd_set_address(uint8_t address); + +/** + * \brief Returns the USB address of device + * + * \return USB address + */ +uint8_t udd_getaddress(void); + +/** + * \brief Returns the current start of frame number + * + * \return current start of frame number. + */ +uint16_t udd_get_frame_number(void); + +/** + * \brief Returns the current micro start of frame number + * + * \return current micro start of frame number required in high speed mode. + */ +uint16_t udd_get_micro_frame_number(void); + +/*! \brief The USB driver sends a resume signal called Upstream Resume + */ +void udd_send_remotewakeup(void); + +/** + * \brief Load setup payload + * + * \param payload Pointer on payload + * \param payload_size Size of payload + */ +void udd_set_setup_payload( uint8_t *payload, uint16_t payload_size ); + + +/** + * \name Endpoint Management + * + * The following functions allow drivers to create and remove + * endpoints, as well as set, clear and query their "halted" and + * "wedged" states. + */ +//@{ + +#if (USB_DEVICE_MAX_EP != 0) + +/** + * \brief Configures and enables an endpoint + * + * \param ep Endpoint number including direction (USB_EP_DIR_IN/USB_EP_DIR_OUT). + * \param bmAttributes Attributes of endpoint declared in the descriptor. + * \param MaxEndpointSize Endpoint maximum size + * + * \return \c 1 if the endpoint is enabled, otherwise \c 0. + */ +bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes, + uint16_t MaxEndpointSize); + +/** + * \brief Disables an endpoint + * + * \param ep Endpoint number including direction (USB_EP_DIR_IN/USB_EP_DIR_OUT). + */ +void udd_ep_free(udd_ep_id_t ep); + +/** + * \brief Check if the endpoint \a ep is halted. + * + * \param ep The ID of the endpoint to check. + * + * \return \c 1 if \a ep is halted, otherwise \c 0. + */ +bool udd_ep_is_halted(udd_ep_id_t ep); + +/** + * \brief Set the halted state of the endpoint \a ep + * + * After calling this function, any transaction on \a ep will result + * in a STALL handshake being sent. Any pending transactions will be + * performed first, however. + * + * \param ep The ID of the endpoint to be halted + * + * \return \c 1 if \a ep is halted, otherwise \c 0. + */ +bool udd_ep_set_halt(udd_ep_id_t ep); + +/** + * \brief Clear the halted state of the endpoint \a ep + * + * After calling this function, any transaction on \a ep will + * be handled normally, i.e. a STALL handshake will not be sent, and + * the data toggle sequence will start at DATA0. + * + * \param ep The ID of the endpoint to be un-halted + * + * \return \c 1 if function was successfully done, otherwise \c 0. + */ +bool udd_ep_clear_halt(udd_ep_id_t ep); + +/** + * \brief Registers a callback to call when endpoint halt is cleared + * + * \param ep The ID of the endpoint to use + * \param callback NULL or function to call when endpoint halt is cleared + * + * \warning if the endpoint is not halted then the \a callback is called immediately. + * + * \return \c 1 if the register is accepted, otherwise \c 0. + */ +bool udd_ep_wait_stall_clear(udd_ep_id_t ep, + udd_callback_halt_cleared_t callback); + +/** + * \brief Allows to receive or send data on an endpoint + * + * The driver uses a specific DMA USB to transfer data + * from internal RAM to endpoint, if this one is available. + * When the transfer is finished or aborted (stall, reset, ...), the \a callback is called. + * The \a callback returns the transfer status and eventually the number of byte transfered. + * Note: The control endpoint is not authorized. + * + * \param ep The ID of the endpoint to use + * \param b_shortpacket Enabled automatic short packet + * \param buf Buffer on Internal RAM to send or fill. + * It must be align, then use COMPILER_WORD_ALIGNED. + * \param buf_size Buffer size to send or fill + * \param callback NULL or function to call at the end of transfer + * + * \warning About \a b_shortpacket, for IN endpoint it means that a short packet + * (or a Zero Length Packet) will be sent to the USB line to properly close the usb + * transfer at the end of the data transfer. + * For Bulk and Interrupt OUT endpoint, it will automatically stop the transfer + * at the end of the data transfer (received short packet). + * + * \return \c 1 if function was successfully done, otherwise \c 0. + */ +bool udd_ep_run(udd_ep_id_t ep, bool b_shortpacket, + uint8_t *buf, iram_size_t buf_size, + udd_callback_trans_t callback); +/** + * \brief Aborts transfer on going on endpoint + * + * If a transfer is on going, then it is stopped and + * the callback registered is called to signal the end of transfer. + * Note: The control endpoint is not authorized. + * + * \param ep Endpoint to abort + */ +void udd_ep_abort(udd_ep_id_t ep); + +#endif + +//@} + + +/** + * \name High speed test mode management + * + * The following functions allow the device to jump to a specific test mode required in high speed mode. + */ +//@{ +void udd_test_mode_j(void); +void udd_test_mode_k(void); +void udd_test_mode_se0_nak(void); +void udd_test_mode_packet(void); +//@} + + +/** + * \name UDC callbacks to provide for UDD + * + * The following callbacks are used by UDD. + */ +//@{ + +/** + * \brief Decodes and manages a setup request + * + * The driver call it when a SETUP packet is received. + * The \c udd_g_ctrlreq contains the data of SETUP packet. + * If this callback accepts the setup request then it must + * return \c 1 and eventually update \c udd_g_ctrlreq to send or receive data. + * + * \return \c 1 if the request is accepted, otherwise \c 0. + */ +extern bool udc_process_setup(void); + +/** + * \brief Reset the UDC + * + * The UDC must reset all configuration. + */ +extern void udc_reset(void); + +/** + * \brief To signal that a SOF is occurred + * + * The UDC must send the signal to all UDIs enabled + */ +extern void udc_sof_notify(void); + +//@} + +//@} + +#ifdef __cplusplus +} +#endif +#endif // _UDD_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udi.h b/tmk_core/protocol/arm_atsam/usb/udi.h new file mode 100644 index 0000000000..9e4d4baf7f --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi.h @@ -0,0 +1,133 @@ +/** + * \file + * + * \brief Common API for USB Device Interface + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDI_H_ +#define _UDI_H_ + +#include "conf_usb.h" +#include "usb_protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup usb_device_group + * \defgroup udi_group USB Device Interface (UDI) + * The UDI provides a common API for all classes, + * and this is used by UDC for the main control of USB Device interface. + * @{ + */ + +/** + * \brief UDI API. + * + * The callbacks within this structure are called only by + * USB Device Controller (UDC) + * + * The udc_get_interface_desc() can be use by UDI to know the interface descriptor + * selected by UDC. + */ +typedef struct { + /** + * \brief Enable the interface. + * + * This function is called when the host selects a configuration + * to which this interface belongs through a Set Configuration + * request, and when the host selects an alternate setting of + * this interface through a Set Interface request. + * + * \return \c 1 if function was successfully done, otherwise \c 0. + */ + bool(*enable) (void); + + /** + * \brief Disable the interface. + * + * This function is called when this interface is currently + * active, and + * - the host selects any configuration through a Set + * Configuration request, or + * - the host issues a USB reset, or + * - the device is detached from the host (i.e. Vbus is no + * longer present) + */ + void (*disable) (void); + + /** + * \brief Handle a control request directed at an interface. + * + * This function is called when this interface is currently + * active and the host sends a SETUP request + * with this interface as the recipient. + * + * Use udd_g_ctrlreq to decode and response to SETUP request. + * + * \return \c 1 if this interface supports the SETUP request, otherwise \c 0. + */ + bool(*setup) (void); + + /** + * \brief Returns the current setting of the selected interface. + * + * This function is called when UDC when know alternate setting of selected interface. + * + * \return alternate setting of selected interface + */ + uint8_t(*getsetting) (void); + + /** + * \brief To signal that a SOF is occurred + */ + void(*sof_notify) (void); +} udi_api_t; + +//@} + +#ifdef __cplusplus +} +#endif +#endif // _UDI_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udi_cdc.c b/tmk_core/protocol/arm_atsam/usb/udi_cdc.c new file mode 100644 index 0000000000..5f3c289e81 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_cdc.c @@ -0,0 +1,1380 @@ +/** + * \file + * + * \brief USB Device Communication Device Class (CDC) interface. + * + * Copyright (c) 2009-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "samd51j18a.h" +#include "conf_usb.h" +#include "usb_protocol.h" +#include "usb_protocol_cdc.h" +#include "udd.h" +#include "udc.h" +#include "udi_cdc.h" +#include <string.h> +#include "udi_cdc_conf.h" +#include "udi_device_conf.h" +#include "stdarg.h" +#include "tmk_core/protocol/arm_atsam/clks.h" + +#ifdef CDC + +#ifdef UDI_CDC_LOW_RATE +# ifdef USB_DEVICE_HS_SUPPORT +# define UDI_CDC_TX_BUFFERS (UDI_CDC_DATA_EPS_HS_SIZE) +# define UDI_CDC_RX_BUFFERS (UDI_CDC_DATA_EPS_HS_SIZE) +# else +# define UDI_CDC_TX_BUFFERS (UDI_CDC_DATA_EPS_FS_SIZE) +# define UDI_CDC_RX_BUFFERS (UDI_CDC_DATA_EPS_FS_SIZE) +# endif +#else +# ifdef USB_DEVICE_HS_SUPPORT +# define UDI_CDC_TX_BUFFERS (UDI_CDC_DATA_EPS_HS_SIZE) +# define UDI_CDC_RX_BUFFERS (UDI_CDC_DATA_EPS_HS_SIZE) +# else +# define UDI_CDC_TX_BUFFERS (5*UDI_CDC_DATA_EPS_FS_SIZE) +# define UDI_CDC_RX_BUFFERS (5*UDI_CDC_DATA_EPS_FS_SIZE) +# endif +#endif + +#ifndef UDI_CDC_TX_EMPTY_NOTIFY +# define UDI_CDC_TX_EMPTY_NOTIFY(port) +#endif + +/** + * \ingroup udi_cdc_group + * \defgroup udi_cdc_group_udc Interface with USB Device Core (UDC) + * + * Structures and functions required by UDC. + * + * @{ + */ +bool udi_cdc_comm_enable(void); +void udi_cdc_comm_disable(void); +bool udi_cdc_comm_setup(void); +bool udi_cdc_data_enable(void); +void udi_cdc_data_disable(void); +bool udi_cdc_data_setup(void); +uint8_t udi_cdc_getsetting(void); +void udi_cdc_data_sof_notify(void); +UDC_DESC_STORAGE udi_api_t udi_api_cdc_comm = { + .enable = udi_cdc_comm_enable, + .disable = udi_cdc_comm_disable, + .setup = udi_cdc_comm_setup, + .getsetting = udi_cdc_getsetting, + .sof_notify = NULL +}; +UDC_DESC_STORAGE udi_api_t udi_api_cdc_data = { + .enable = udi_cdc_data_enable, + .disable = udi_cdc_data_disable, + .setup = udi_cdc_data_setup, + .getsetting = udi_cdc_getsetting, + .sof_notify = udi_cdc_data_sof_notify, +}; +//@} + +/** + * \ingroup udi_cdc_group + * \defgroup udi_cdc_group_internal Implementation of UDI CDC + * + * Class internal implementation + * @{ + */ + +/** + * \name Internal routines + */ +//@{ + +/** + * \name Routines to control serial line + */ +//@{ + +/** + * \brief Returns the port number corresponding at current setup request + * + * \return port number + */ +static uint8_t udi_cdc_setup_to_port(void); + +/** + * \brief Sends line coding to application + * + * Called after SETUP request when line coding data is received. + */ +static void udi_cdc_line_coding_received(void); + +/** + * \brief Records new state + * + * \param port Communication port number to manage + * \param b_set State is enabled if true, else disabled + * \param bit_mask Field to process (see CDC_SERIAL_STATE_ defines) + */ +static void udi_cdc_ctrl_state_change(uint8_t port, bool b_set, le16_t bit_mask); + +/** + * \brief Check and eventually notify the USB host of new state + * + * \param port Communication port number to manage + * \param ep Port communication endpoint + */ +static void udi_cdc_ctrl_state_notify(uint8_t port, udd_ep_id_t ep); + +/** + * \brief Ack sent of serial state message + * Callback called after serial state message sent + * + * \param status UDD_EP_TRANSFER_OK, if transfer finished + * \param status UDD_EP_TRANSFER_ABORT, if transfer aborted + * \param n number of data transfered + */ +static void udi_cdc_serial_state_msg_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep); + +//@} + +/** + * \name Routines to process data transfer + */ +//@{ + +/** + * \brief Enable the reception of data from the USB host + * + * The value udi_cdc_rx_trans_sel indicate the RX buffer to fill. + * + * \param port Communication port number to manage + * + * \return \c 1 if function was successfully done, otherwise \c 0. + */ +static bool udi_cdc_rx_start(uint8_t port); + +/** + * \brief Update rx buffer management with a new data + * Callback called after data reception on USB line + * + * \param status UDD_EP_TRANSFER_OK, if transfer finish + * \param status UDD_EP_TRANSFER_ABORT, if transfer aborted + * \param n number of data received + */ +static void udi_cdc_data_received(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep); + +/** + * \brief Ack sent of tx buffer + * Callback called after data transfer on USB line + * + * \param status UDD_EP_TRANSFER_OK, if transfer finished + * \param status UDD_EP_TRANSFER_ABORT, if transfer aborted + * \param n number of data transfered + */ +static void udi_cdc_data_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep); + +/** + * \brief Send buffer on line or wait a SOF event + * + * \param port Communication port number to manage + */ +static void udi_cdc_tx_send(uint8_t port); + +//@} + +//@} + +/** + * \name Information about configuration of communication line + */ +//@{ +COMPILER_WORD_ALIGNED +static usb_cdc_line_coding_t udi_cdc_line_coding[UDI_CDC_PORT_NB]; +static bool udi_cdc_serial_state_msg_ongoing[UDI_CDC_PORT_NB]; +static volatile le16_t udi_cdc_state[UDI_CDC_PORT_NB]; +COMPILER_WORD_ALIGNED static usb_cdc_notify_serial_state_t uid_cdc_state_msg[UDI_CDC_PORT_NB]; + +//! Status of CDC COMM interfaces +static volatile uint8_t udi_cdc_nb_comm_enabled = 0; +//@} + +/** + * \name Variables to manage RX/TX transfer requests + * Two buffers for each sense are used to optimize the speed. + */ +//@{ + +//! Status of CDC DATA interfaces +static volatile uint8_t udi_cdc_nb_data_enabled = 0; +static volatile bool udi_cdc_data_running = false; +//! Buffer to receive data +COMPILER_WORD_ALIGNED static uint8_t udi_cdc_rx_buf[UDI_CDC_PORT_NB][2][UDI_CDC_RX_BUFFERS]; +//! Data available in RX buffers +static volatile uint16_t udi_cdc_rx_buf_nb[UDI_CDC_PORT_NB][2]; +//! Give the current RX buffer used (rx0 if 0, rx1 if 1) +static volatile uint8_t udi_cdc_rx_buf_sel[UDI_CDC_PORT_NB]; +//! Read position in current RX buffer +static volatile uint16_t udi_cdc_rx_pos[UDI_CDC_PORT_NB]; +//! Signal a transfer on-going +static volatile bool udi_cdc_rx_trans_ongoing[UDI_CDC_PORT_NB]; + +//! Define a transfer halted +#define UDI_CDC_TRANS_HALTED 2 + +//! Buffer to send data +COMPILER_WORD_ALIGNED static uint8_t udi_cdc_tx_buf[UDI_CDC_PORT_NB][2][UDI_CDC_TX_BUFFERS]; +//! Data available in TX buffers +static uint16_t udi_cdc_tx_buf_nb[UDI_CDC_PORT_NB][2]; +//! Give current TX buffer used (tx0 if 0, tx1 if 1) +static volatile uint8_t udi_cdc_tx_buf_sel[UDI_CDC_PORT_NB]; +//! Value of SOF during last TX transfer +static uint16_t udi_cdc_tx_sof_num[UDI_CDC_PORT_NB]; +//! Signal a transfer on-going +static volatile bool udi_cdc_tx_trans_ongoing[UDI_CDC_PORT_NB]; +//! Signal that both buffer content data to send +static volatile bool udi_cdc_tx_both_buf_to_send[UDI_CDC_PORT_NB]; + +//@} + +bool udi_cdc_comm_enable(void) +{ + uint8_t port; + uint8_t iface_comm_num; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; + udi_cdc_nb_comm_enabled = 0; +//#else +// if (udi_cdc_nb_comm_enabled > UDI_CDC_PORT_NB) { +// udi_cdc_nb_comm_enabled = 0; +// } +// port = udi_cdc_nb_comm_enabled; +//#endif + + // Initialize control signal management + udi_cdc_state[port] = CPU_TO_LE16(0); + + uid_cdc_state_msg[port].header.bmRequestType = + USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS | + USB_REQ_RECIP_INTERFACE; + uid_cdc_state_msg[port].header.bNotification = USB_REQ_CDC_NOTIFY_SERIAL_STATE; + uid_cdc_state_msg[port].header.wValue = LE16(0); + + /* + switch (port) { + #define UDI_CDC_PORT_TO_IFACE_COMM(index, unused) \ + case index: \ + iface_comm_num = UDI_CDC_COMM_IFACE_NUMBER_##index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_PORT_TO_IFACE_COMM, ~) + #undef UDI_CDC_PORT_TO_IFACE_COMM + default: + iface_comm_num = UDI_CDC_COMM_IFACE_NUMBER_0; + break; + } + */ + iface_comm_num = UDI_CDC_COMM_IFACE_NUMBER_0; + + uid_cdc_state_msg[port].header.wIndex = LE16(iface_comm_num); + uid_cdc_state_msg[port].header.wLength = LE16(2); + uid_cdc_state_msg[port].value = CPU_TO_LE16(0); + + udi_cdc_line_coding[port].dwDTERate = CPU_TO_LE32(UDI_CDC_DEFAULT_RATE); + udi_cdc_line_coding[port].bCharFormat = UDI_CDC_DEFAULT_STOPBITS; + udi_cdc_line_coding[port].bParityType = UDI_CDC_DEFAULT_PARITY; + udi_cdc_line_coding[port].bDataBits = UDI_CDC_DEFAULT_DATABITS; + // Call application callback + // to initialize memories or indicate that interface is enabled +#if 0 + UDI_CDC_SET_CODING_EXT(port,(&udi_cdc_line_coding[port])); + if (!UDI_CDC_ENABLE_EXT(port)) { + return false; + } +#endif + udi_cdc_nb_comm_enabled++; + return true; +} + +bool udi_cdc_data_enable(void) +{ + uint8_t port; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; + udi_cdc_nb_data_enabled = 0; +//#else +// if (udi_cdc_nb_data_enabled > UDI_CDC_PORT_NB) { +// udi_cdc_nb_data_enabled = 0; +// } +// port = udi_cdc_nb_data_enabled; +//#endif + + // Initialize TX management + udi_cdc_tx_trans_ongoing[port] = false; + udi_cdc_tx_both_buf_to_send[port] = false; + udi_cdc_tx_buf_sel[port] = 0; + udi_cdc_tx_buf_nb[port][0] = 0; + udi_cdc_tx_buf_nb[port][1] = 0; + udi_cdc_tx_sof_num[port] = 0; + udi_cdc_tx_send(port); + + // Initialize RX management + udi_cdc_rx_trans_ongoing[port] = false; + udi_cdc_rx_buf_sel[port] = 0; + udi_cdc_rx_buf_nb[port][0] = 0; + udi_cdc_rx_buf_nb[port][1] = 0; + udi_cdc_rx_pos[port] = 0; + if (!udi_cdc_rx_start(port)) { + return false; + } + udi_cdc_nb_data_enabled++; + if (udi_cdc_nb_data_enabled == UDI_CDC_PORT_NB) { + udi_cdc_data_running = true; + } + return true; +} + +void udi_cdc_comm_disable(void) +{ + Assert(udi_cdc_nb_comm_enabled != 0); + udi_cdc_nb_comm_enabled--; +} + +void udi_cdc_data_disable(void) +{ +// uint8_t port; + + Assert(udi_cdc_nb_data_enabled != 0); + udi_cdc_nb_data_enabled--; +// port = udi_cdc_nb_data_enabled; +// UDI_CDC_DISABLE_EXT(port); + udi_cdc_data_running = false; +} + +bool udi_cdc_comm_setup(void) +{ + uint8_t port = udi_cdc_setup_to_port(); + + if (Udd_setup_is_in()) { + // GET Interface Requests + if (Udd_setup_type() == USB_REQ_TYPE_CLASS) { + // Requests Class Interface Get + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_CDC_GET_LINE_CODING: + // Get configuration of CDC line + if (sizeof(usb_cdc_line_coding_t) != + udd_g_ctrlreq.req.wLength) + return false; // Error for USB host + udd_g_ctrlreq.payload = + (uint8_t *) & + udi_cdc_line_coding[port]; + udd_g_ctrlreq.payload_size = + sizeof(usb_cdc_line_coding_t); + return true; + } + } + } + if (Udd_setup_is_out()) { + // SET Interface Requests + if (Udd_setup_type() == USB_REQ_TYPE_CLASS) { + // Requests Class Interface Set + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_CDC_SET_LINE_CODING: + // Change configuration of CDC line + if (sizeof(usb_cdc_line_coding_t) != + udd_g_ctrlreq.req.wLength) + return false; // Error for USB host + udd_g_ctrlreq.callback = + udi_cdc_line_coding_received; + udd_g_ctrlreq.payload = + (uint8_t *) & + udi_cdc_line_coding[port]; + udd_g_ctrlreq.payload_size = + sizeof(usb_cdc_line_coding_t); + return true; + case USB_REQ_CDC_SET_CONTROL_LINE_STATE: + // According cdc spec 1.1 chapter 6.2.14 +// UDI_CDC_SET_DTR_EXT(port, (0 != +// (udd_g_ctrlreq.req.wValue +// & CDC_CTRL_SIGNAL_DTE_PRESENT))); +// UDI_CDC_SET_RTS_EXT(port, (0 != +// (udd_g_ctrlreq.req.wValue +// & CDC_CTRL_SIGNAL_ACTIVATE_CARRIER))); + return true; + } + } + } + return false; // request Not supported +} + +bool udi_cdc_data_setup(void) +{ + return false; // request Not supported +} + +uint8_t udi_cdc_getsetting(void) +{ + return 0; // CDC don't have multiple alternate setting +} + +void udi_cdc_data_sof_notify(void) +{ + static uint8_t port_notify = 0; + + // A call of udi_cdc_data_sof_notify() is done for each port + udi_cdc_tx_send(port_notify); + /* +#if UDI_CDC_PORT_NB != 1 // To optimize code + port_notify++; + if (port_notify >= UDI_CDC_PORT_NB) { + port_notify = 0; + } +#endif + */ +} + + +//------------------------------------------------- +//------- Internal routines to control serial line + +static uint8_t udi_cdc_setup_to_port(void) +{ + uint8_t port; + + /* + switch (udd_g_ctrlreq.req.wIndex & 0xFF) { +#define UDI_CDC_IFACE_COMM_TO_PORT(iface, unused) \ + case UDI_CDC_COMM_IFACE_NUMBER_##iface: \ + port = iface; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_IFACE_COMM_TO_PORT, ~) +#undef UDI_CDC_IFACE_COMM_TO_PORT + default: + port = 0; + break; + } + */ + port = 0; + + return port; +} + +static void udi_cdc_line_coding_received(void) +{ + uint8_t port = udi_cdc_setup_to_port(); + UNUSED(port); + +// UDI_CDC_SET_CODING_EXT(port, (&udi_cdc_line_coding[port])); +} + +static void udi_cdc_ctrl_state_change(uint8_t port, bool b_set, le16_t bit_mask) +{ + udd_ep_id_t ep_comm; + uint32_t irqflags; //irqflags_t + + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + // Update state + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + if (b_set) { + udi_cdc_state[port] |= bit_mask; + } else { + udi_cdc_state[port] &= ~(unsigned)bit_mask; + } + __DMB(); + __set_PRIMASK(irqflags); + + /* + // Send it if possible and state changed + switch (port) { +#define UDI_CDC_PORT_TO_COMM_EP(index, unused) \ + case index: \ + ep_comm = UDI_CDC_COMM_EP_##index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_PORT_TO_COMM_EP, ~) +#undef UDI_CDC_PORT_TO_COMM_EP + default: + ep_comm = UDI_CDC_COMM_EP_0; + break; + } + */ + ep_comm = UDI_CDC_COMM_EP_0; + + udi_cdc_ctrl_state_notify(port, ep_comm); +} + + +static void udi_cdc_ctrl_state_notify(uint8_t port, udd_ep_id_t ep) +{ +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + // Send it if possible and state changed + if ((!udi_cdc_serial_state_msg_ongoing[port]) + && (udi_cdc_state[port] != uid_cdc_state_msg[port].value)) { + // Fill notification message + uid_cdc_state_msg[port].value = udi_cdc_state[port]; + // Send notification message + udi_cdc_serial_state_msg_ongoing[port] = + udd_ep_run(ep, + false, + (uint8_t *) & uid_cdc_state_msg[port], + sizeof(uid_cdc_state_msg[0]), + udi_cdc_serial_state_msg_sent); + } +} + + +static void udi_cdc_serial_state_msg_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep) +{ + uint8_t port; + UNUSED(n); + UNUSED(status); + + /* + switch (ep) { +#define UDI_CDC_GET_PORT_FROM_COMM_EP(iface, unused) \ + case UDI_CDC_COMM_EP_##iface: \ + port = iface; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_GET_PORT_FROM_COMM_EP, ~) +#undef UDI_CDC_GET_PORT_FROM_COMM_EP + default: + port = 0; + break; + } + */ + port = 0; + + udi_cdc_serial_state_msg_ongoing[port] = false; + + // For the irregular signals like break, the incoming ring signal, + // or the overrun error state, this will reset their values to zero + // and again will not send another notification until their state changes. + udi_cdc_state[port] &= ~(CDC_SERIAL_STATE_BREAK | + CDC_SERIAL_STATE_RING | + CDC_SERIAL_STATE_FRAMING | + CDC_SERIAL_STATE_PARITY | CDC_SERIAL_STATE_OVERRUN); + uid_cdc_state_msg[port].value &= ~(CDC_SERIAL_STATE_BREAK | + CDC_SERIAL_STATE_RING | + CDC_SERIAL_STATE_FRAMING | + CDC_SERIAL_STATE_PARITY | CDC_SERIAL_STATE_OVERRUN); + // Send it if possible and state changed + udi_cdc_ctrl_state_notify(port, ep); +} + +//------------------------------------------------- +//------- Internal routines to process data transfer + +static bool udi_cdc_rx_start(uint8_t port) +{ + uint32_t irqflags; //irqflags_t + uint8_t buf_sel_trans; + udd_ep_id_t ep; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + buf_sel_trans = udi_cdc_rx_buf_sel[port]; + if (udi_cdc_rx_trans_ongoing[port] || + (udi_cdc_rx_pos[port] < udi_cdc_rx_buf_nb[port][buf_sel_trans])) { + // Transfer already on-going or current buffer no empty + __DMB(); + __set_PRIMASK(irqflags); + return false; + } + + // Change current buffer + udi_cdc_rx_pos[port] = 0; + udi_cdc_rx_buf_sel[port] = (buf_sel_trans==0)?1:0; + + // Start transfer on RX + udi_cdc_rx_trans_ongoing[port] = true; + __DMB(); + __set_PRIMASK(irqflags); + + if (udi_cdc_multi_is_rx_ready(port)) { +// UDI_CDC_RX_NOTIFY(port); + } + + /* + // Send the buffer with enable of short packet + switch (port) { +#define UDI_CDC_PORT_TO_DATA_EP_OUT(index, unused) \ + case index: \ + ep = UDI_CDC_DATA_EP_OUT_##index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_PORT_TO_DATA_EP_OUT, ~) +#undef UDI_CDC_PORT_TO_DATA_EP_OUT + default: + ep = UDI_CDC_DATA_EP_OUT_0; + break; + } + */ + ep = UDI_CDC_DATA_EP_OUT_0; + + return udd_ep_run(ep, + true, + udi_cdc_rx_buf[port][buf_sel_trans], + UDI_CDC_RX_BUFFERS, + udi_cdc_data_received); +} + +static void udi_cdc_data_received(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep) +{ + uint8_t buf_sel_trans; + uint8_t port; + + /* + switch (ep) { +#define UDI_CDC_DATA_EP_OUT_TO_PORT(index, unused) \ + case UDI_CDC_DATA_EP_OUT_##index: \ + port = index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_DATA_EP_OUT_TO_PORT, ~) +#undef UDI_CDC_DATA_EP_OUT_TO_PORT + default: + port = 0; + break; + } + */ + port = 0; + + if (UDD_EP_TRANSFER_OK != status) { + // Abort reception + return; + } + + buf_sel_trans = (udi_cdc_rx_buf_sel[port]==0)?1:0; + + if (!n) { + udd_ep_run( ep, + true, + udi_cdc_rx_buf[port][buf_sel_trans], + UDI_CDC_RX_BUFFERS, + udi_cdc_data_received); + return; + } + + udi_cdc_rx_buf_nb[port][buf_sel_trans] = n; + udi_cdc_rx_trans_ongoing[port] = false; + udi_cdc_rx_start(port); +} + +static void udi_cdc_data_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep) +{ + uint8_t port; + UNUSED(n); + + /* + switch (ep) { +#define UDI_CDC_DATA_EP_IN_TO_PORT(index, unused) \ + case UDI_CDC_DATA_EP_IN_##index: \ + port = index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_DATA_EP_IN_TO_PORT, ~) +#undef UDI_CDC_DATA_EP_IN_TO_PORT + default: + port = 0; + break; + } + */ + port = 0; + + if (UDD_EP_TRANSFER_OK != status) { + // Abort transfer + return; + } + + udi_cdc_tx_buf_nb[port][(udi_cdc_tx_buf_sel[port]==0)?1:0] = 0; + udi_cdc_tx_both_buf_to_send[port] = false; + udi_cdc_tx_trans_ongoing[port] = false; + + if (n != 0) { + UDI_CDC_TX_EMPTY_NOTIFY(port); + } + + udi_cdc_tx_send(port); +} + +static void udi_cdc_tx_send(uint8_t port) +{ + uint32_t irqflags; //irqflags_t + uint8_t buf_sel_trans; + bool b_short_packet; + udd_ep_id_t ep; + static uint16_t sof_zlp_counter = 0; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + if (udi_cdc_tx_trans_ongoing[port]) { + return; // Already on going or wait next SOF to send next data + } + if (udd_is_high_speed()) { + if (udi_cdc_tx_sof_num[port] == udd_get_micro_frame_number()) { + return; // Wait next SOF to send next data + } + }else{ + if (udi_cdc_tx_sof_num[port] == udd_get_frame_number()) { + return; // Wait next SOF to send next data + } + } + + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + buf_sel_trans = udi_cdc_tx_buf_sel[port]; + if (udi_cdc_tx_buf_nb[port][buf_sel_trans] == 0) { + sof_zlp_counter++; + if (((!udd_is_high_speed()) && (sof_zlp_counter < 100)) + || (udd_is_high_speed() && (sof_zlp_counter < 800))) { + __DMB(); + __set_PRIMASK(irqflags); + return; + } + } + sof_zlp_counter = 0; + + if (!udi_cdc_tx_both_buf_to_send[port]) { + // Send current Buffer + // and switch the current buffer + udi_cdc_tx_buf_sel[port] = (buf_sel_trans==0)?1:0; + }else{ + // Send the other Buffer + // and no switch the current buffer + buf_sel_trans = (buf_sel_trans==0)?1:0; + } + udi_cdc_tx_trans_ongoing[port] = true; + __DMB(); + __set_PRIMASK(irqflags); + + b_short_packet = (udi_cdc_tx_buf_nb[port][buf_sel_trans] != UDI_CDC_TX_BUFFERS); + if (b_short_packet) { + if (udd_is_high_speed()) { + udi_cdc_tx_sof_num[port] = udd_get_micro_frame_number(); + }else{ + udi_cdc_tx_sof_num[port] = udd_get_frame_number(); + } + }else{ + udi_cdc_tx_sof_num[port] = 0; // Force next transfer without wait SOF + } + + /* + // Send the buffer with enable of short packet + switch (port) { +#define UDI_CDC_PORT_TO_DATA_EP_IN(index, unused) \ + case index: \ + ep = UDI_CDC_DATA_EP_IN_##index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_PORT_TO_DATA_EP_IN, ~) +#undef UDI_CDC_PORT_TO_DATA_EP_IN + default: + ep = UDI_CDC_DATA_EP_IN_0; + break; + } + */ + ep = UDI_CDC_DATA_EP_IN_0; + + udd_ep_run( ep, + b_short_packet, + udi_cdc_tx_buf[port][buf_sel_trans], + udi_cdc_tx_buf_nb[port][buf_sel_trans], + udi_cdc_data_sent); +} + +//--------------------------------------------- +//------- Application interface + +void udi_cdc_ctrl_signal_dcd(bool b_set) +{ + udi_cdc_ctrl_state_change(0, b_set, CDC_SERIAL_STATE_DCD); +} + +void udi_cdc_ctrl_signal_dsr(bool b_set) +{ + udi_cdc_ctrl_state_change(0, b_set, CDC_SERIAL_STATE_DSR); +} + +void udi_cdc_signal_framing_error(void) +{ + udi_cdc_ctrl_state_change(0, true, CDC_SERIAL_STATE_FRAMING); +} + +void udi_cdc_signal_parity_error(void) +{ + udi_cdc_ctrl_state_change(0, true, CDC_SERIAL_STATE_PARITY); +} + +void udi_cdc_signal_overrun(void) +{ + udi_cdc_ctrl_state_change(0, true, CDC_SERIAL_STATE_OVERRUN); +} + +void udi_cdc_multi_ctrl_signal_dcd(uint8_t port, bool b_set) +{ + udi_cdc_ctrl_state_change(port, b_set, CDC_SERIAL_STATE_DCD); +} + +void udi_cdc_multi_ctrl_signal_dsr(uint8_t port, bool b_set) +{ + udi_cdc_ctrl_state_change(port, b_set, CDC_SERIAL_STATE_DSR); +} + +void udi_cdc_multi_signal_framing_error(uint8_t port) +{ + udi_cdc_ctrl_state_change(port, true, CDC_SERIAL_STATE_FRAMING); +} + +void udi_cdc_multi_signal_parity_error(uint8_t port) +{ + udi_cdc_ctrl_state_change(port, true, CDC_SERIAL_STATE_PARITY); +} + +void udi_cdc_multi_signal_overrun(uint8_t port) +{ + udi_cdc_ctrl_state_change(port, true, CDC_SERIAL_STATE_OVERRUN); +} + +iram_size_t udi_cdc_multi_get_nb_received_data(uint8_t port) +{ + uint32_t irqflags; //irqflags_t + uint16_t pos; + iram_size_t nb_received; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + pos = udi_cdc_rx_pos[port]; + nb_received = udi_cdc_rx_buf_nb[port][udi_cdc_rx_buf_sel[port]] - pos; + __DMB(); + __set_PRIMASK(irqflags); + return nb_received; +} + +iram_size_t udi_cdc_get_nb_received_data(void) +{ + return udi_cdc_multi_get_nb_received_data(0); +} + +bool udi_cdc_multi_is_rx_ready(uint8_t port) +{ + return (udi_cdc_multi_get_nb_received_data(port) > 0); +} + +bool udi_cdc_is_rx_ready(void) +{ + return udi_cdc_multi_is_rx_ready(0); +} + +int udi_cdc_multi_getc(uint8_t port) +{ + uint32_t irqflags; //irqflags_t + int rx_data = 0; + bool b_databit_9; + uint16_t pos; + uint8_t buf_sel; + bool again; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + b_databit_9 = (9 == udi_cdc_line_coding[port].bDataBits); + +udi_cdc_getc_process_one_byte: + // Check available data + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + pos = udi_cdc_rx_pos[port]; + buf_sel = udi_cdc_rx_buf_sel[port]; + again = pos >= udi_cdc_rx_buf_nb[port][buf_sel]; + __DMB(); + __set_PRIMASK(irqflags); + while (again) { + if (!udi_cdc_data_running) { + return 0; + } + goto udi_cdc_getc_process_one_byte; + } + + // Read data + rx_data |= udi_cdc_rx_buf[port][buf_sel][pos]; + udi_cdc_rx_pos[port] = pos+1; + + udi_cdc_rx_start(port); + + if (b_databit_9) { + // Receive MSB + b_databit_9 = false; + rx_data = rx_data << 8; + goto udi_cdc_getc_process_one_byte; + } + return rx_data; +} + +int udi_cdc_getc(void) +{ + return udi_cdc_multi_getc(0); +} + +iram_size_t udi_cdc_multi_read_buf(uint8_t port, void* buf, iram_size_t size) +{ + uint32_t irqflags; //irqflags_t + uint8_t *ptr_buf = (uint8_t *)buf; + iram_size_t copy_nb; + uint16_t pos; + uint8_t buf_sel; + bool again; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + +udi_cdc_read_buf_loop_wait: + // Check available data + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); pos = udi_cdc_rx_pos[port]; + buf_sel = udi_cdc_rx_buf_sel[port]; + again = pos >= udi_cdc_rx_buf_nb[port][buf_sel]; + __DMB(); + __set_PRIMASK(irqflags); + while (again) { + if (!udi_cdc_data_running) { + return size; + } + goto udi_cdc_read_buf_loop_wait; + } + + // Read data + copy_nb = udi_cdc_rx_buf_nb[port][buf_sel] - pos; + if (copy_nb>size) { + copy_nb = size; + } + memcpy(ptr_buf, &udi_cdc_rx_buf[port][buf_sel][pos], copy_nb); + udi_cdc_rx_pos[port] += copy_nb; + ptr_buf += copy_nb; + size -= copy_nb; + udi_cdc_rx_start(port); + + if (size) { + goto udi_cdc_read_buf_loop_wait; + } + return 0; +} + +static iram_size_t udi_cdc_multi_read_no_polling(uint8_t port, void* buf, iram_size_t size) +{ + uint8_t *ptr_buf = (uint8_t *)buf; + iram_size_t nb_avail_data; + uint16_t pos; + uint8_t buf_sel; + uint32_t irqflags; //irqflags_t + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + //Data interface not started... exit + if (!udi_cdc_data_running) { + return 0; + } + + //Get number of available data + // Check available data + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + pos = udi_cdc_rx_pos[port]; + buf_sel = udi_cdc_rx_buf_sel[port]; + nb_avail_data = udi_cdc_rx_buf_nb[port][buf_sel] - pos; + __DMB(); + __set_PRIMASK(irqflags); + //If the buffer contains less than the requested number of data, + //adjust read size + if(nb_avail_data<size) { + size = nb_avail_data; + } + if(size>0) { + memcpy(ptr_buf, &udi_cdc_rx_buf[port][buf_sel][pos], size); + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + udi_cdc_rx_pos[port] += size; + __DMB(); + __set_PRIMASK(irqflags); + ptr_buf += size; + udi_cdc_rx_start(port); + } + return(nb_avail_data); +} + +iram_size_t udi_cdc_read_no_polling(void* buf, iram_size_t size) +{ + return udi_cdc_multi_read_no_polling(0, buf, size); +} + +iram_size_t udi_cdc_read_buf(void* buf, iram_size_t size) +{ + return udi_cdc_multi_read_buf(0, buf, size); +} + +iram_size_t udi_cdc_multi_get_free_tx_buffer(uint8_t port) +{ + uint32_t irqflags; //irqflags_t + iram_size_t buf_sel_nb, retval; + uint8_t buf_sel; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + buf_sel = udi_cdc_tx_buf_sel[port]; + buf_sel_nb = udi_cdc_tx_buf_nb[port][buf_sel]; + if (buf_sel_nb == UDI_CDC_TX_BUFFERS) { + if ((!udi_cdc_tx_trans_ongoing[port]) + && (!udi_cdc_tx_both_buf_to_send[port])) { + /* One buffer is full, but the other buffer is not used. + * (not used = transfer on-going) + * then move to the other buffer to store data */ + udi_cdc_tx_both_buf_to_send[port] = true; + udi_cdc_tx_buf_sel[port] = (buf_sel == 0)? 1 : 0; + buf_sel_nb = 0; + } + } + retval = UDI_CDC_TX_BUFFERS - buf_sel_nb; + __DMB(); + __set_PRIMASK(irqflags); + return retval; +} + +iram_size_t udi_cdc_get_free_tx_buffer(void) +{ + return udi_cdc_multi_get_free_tx_buffer(0); +} + +bool udi_cdc_multi_is_tx_ready(uint8_t port) +{ + return (udi_cdc_multi_get_free_tx_buffer(port) != 0); +} + +bool udi_cdc_is_tx_ready(void) +{ + return udi_cdc_multi_is_tx_ready(0); +} + +int udi_cdc_multi_putc(uint8_t port, int value) +{ + uint32_t irqflags; //irqflags_t + bool b_databit_9; + uint8_t buf_sel; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + b_databit_9 = (9 == udi_cdc_line_coding[port].bDataBits); + +udi_cdc_putc_process_one_byte: + // Check available space + if (!udi_cdc_multi_is_tx_ready(port)) { + if (!udi_cdc_data_running) { + return false; + } + goto udi_cdc_putc_process_one_byte; + } + + // Write value + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + buf_sel = udi_cdc_tx_buf_sel[port]; + udi_cdc_tx_buf[port][buf_sel][udi_cdc_tx_buf_nb[port][buf_sel]++] = value; + __DMB(); + __set_PRIMASK(irqflags); + + if (b_databit_9) { + // Send MSB + b_databit_9 = false; + value = value >> 8; + goto udi_cdc_putc_process_one_byte; + } + return true; +} + +int udi_cdc_putc(int value) +{ + return udi_cdc_multi_putc(0, value); +} + +iram_size_t udi_cdc_multi_write_buf(uint8_t port, const void* buf, iram_size_t size) +{ + uint32_t irqflags; //irqflags_t + uint8_t buf_sel; + uint16_t buf_nb; + iram_size_t copy_nb; + uint8_t *ptr_buf = (uint8_t *)buf; + +//#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +//#endif + + if (9 == udi_cdc_line_coding[port].bDataBits) { + size *=2; + } + + udi_cdc_write_buf_loop_wait: + + // Check available space + if (!udi_cdc_multi_is_tx_ready(port)) { + if (!udi_cdc_data_running) { + return size; + } + goto udi_cdc_write_buf_loop_wait; + } + + // Write values + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + buf_sel = udi_cdc_tx_buf_sel[port]; + buf_nb = udi_cdc_tx_buf_nb[port][buf_sel]; + copy_nb = UDI_CDC_TX_BUFFERS - buf_nb; + if (copy_nb > size) { + copy_nb = size; + } + memcpy(&udi_cdc_tx_buf[port][buf_sel][buf_nb], ptr_buf, copy_nb); + udi_cdc_tx_buf_nb[port][buf_sel] = buf_nb + copy_nb; + __DMB(); + __set_PRIMASK(irqflags); + + // Update buffer pointer + ptr_buf = ptr_buf + copy_nb; + size -= copy_nb; + + if (size) { + goto udi_cdc_write_buf_loop_wait; + } + + return 0; +} + +iram_size_t udi_cdc_write_buf(const void* buf, iram_size_t size) +{ + return udi_cdc_multi_write_buf(0, buf, size); +} + +#define MAX_PRINT 256 +#define CDC_SEND_INTERVAL 2 +uint32_t cdc_tx_send_time_next; + +void CDC_send(void) +{ + while (CLK_get_ms() < cdc_tx_send_time_next); + udi_cdc_tx_send(0); + cdc_tx_send_time_next = CLK_get_ms() + CDC_SEND_INTERVAL; +} + +uint32_t CDC_print(char *printbuf) +{ + uint32_t count=0; + char *buf = printbuf; + char c; + + if (CLK_get_ms() < 5000) return 0; + + while ((c = *buf++) != 0 && !(count >= MAX_PRINT)) + { + count++; + if (!udi_cdc_is_tx_ready()) return 0; + udi_cdc_putc(c); + if (count >= UDI_CDC_TX_BUFFERS) + { + count = 0; + CDC_send(); + } + } + if (count) + { + CDC_send(); + } + return 1; +} + +char printbuf[CDC_PRINTBUF_SIZE]; + +int CDC_printf(const char *_Format, ...) +{ + va_list va; //Variable argument list variable + int result; + + va_start(va, _Format); //Initialize the variable argument list + result = vsnprintf(printbuf, CDC_PRINTBUF_SIZE, _Format, va); + va_end(va); + + CDC_print(printbuf); + + return result; +} + +//global "inbuf" if desired +inbuf_t inbuf; + +uint32_t CDC_input_buf(inbuf_t inbuf, uint32_t inbuf_size) +{ + int RXChar; + int entered = 0; + + if (!udi_cdc_is_rx_ready()) return 0; + udi_cdc_get_nb_received_data(); + RXChar = udi_cdc_getc(); + + if (RXChar) + { + switch (RXChar) + { + case '\t': //tab - repeat last + inbuf.count=inbuf.lastcount; + inbuf.buf[inbuf.count+1] = 0; + CDC_print(inbuf.buf); + break; + case '\r': //enter + inbuf.buf[inbuf.count]=0; + inbuf.lastcount = inbuf.count; + inbuf.count = 0; + entered = 1; + break; + case '\b': //backspace + if (inbuf.count > 0) { + inbuf.count -= 1; + CDC_print("\b \b\0"); + } + else + CDC_print("\a\0"); + break; + default: + if ((RXChar >= 32) && (RXChar <= 126)) + { + if (inbuf.count < inbuf_size-1) + { + inbuf.buf[inbuf.count] = RXChar; + inbuf.buf[inbuf.count+1] = 0; + CDC_print(&inbuf.buf[inbuf.count]); + inbuf.count += 1; + } + else + CDC_print("\a\0"); + } + break; + } + RXChar = 0; + } + return entered; +} + +uint32_t CDC_input() +{ + return CDC_input_buf(inbuf, CDC_INBUF_SIZE); +} + +void CDC_init(void) +{ + inbuf.count = 0; + inbuf.lastcount = 0; + printbuf[0] = 0; + cdc_tx_send_time_next = CLK_get_ms() + CDC_SEND_INTERVAL; +} + +#else //CDC line 62 + +char printbuf[CDC_PRINTBUF_SIZE]; + +void CDC_send(void) +{ + return; +} + +uint32_t CDC_print(char *printbuf) +{ + return 0; +} + +int CDC_printf(const char *_Format, ...) +{ + return 0; +} + +inbuf_t inbuf; + +uint32_t CDC_input(void) +{ + return 0; +} + +void CDC_init(void) +{ + inbuf.count = 0; + inbuf.lastcount = 0; + printbuf[0]=0; +} + +#endif //CDC line 62 + +//@} diff --git a/tmk_core/protocol/arm_atsam/usb/udi_cdc.h b/tmk_core/protocol/arm_atsam/usb/udi_cdc.h new file mode 100644 index 0000000000..86077ce53b --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_cdc.h @@ -0,0 +1,376 @@ +/** + * \file + * + * \brief USB Device Communication Device Class (CDC) interface definitions. + * + * Copyright (c) 2009-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDI_CDC_H_ +#define _UDI_CDC_H_ + +#ifdef CDC + +#include "conf_usb.h" +#include "usb_protocol.h" +#include "usb_protocol_cdc.h" +#include "udd.h" +#include "udc_desc.h" +#include "udi.h" + +// Check the number of port +#ifndef UDI_CDC_PORT_NB +# define UDI_CDC_PORT_NB 1 +#endif +#if (UDI_CDC_PORT_NB > 1) +# error UDI_CDC_PORT_NB must be at most 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup udi_cdc_group_udc + * @{ + */ + +//! Global structure which contains standard UDI API for UDC +extern UDC_DESC_STORAGE udi_api_t udi_api_cdc_comm; +extern UDC_DESC_STORAGE udi_api_t udi_api_cdc_data; +//@} + +//#define CDC_ACM_SIZE 64 see usb_protocol_cdc.h +//#define CDC_RX_SIZE 64 + +//! CDC communication endpoints size for all speeds +#define UDI_CDC_COMM_EP_SIZE CDC_ACM_SIZE +//! CDC data endpoints size for FS speed (8B, 16B, 32B, 64B) +#define UDI_CDC_DATA_EPS_FS_SIZE CDC_RX_SIZE + +//@} + +/** + * \ingroup udi_group + * \defgroup udi_cdc_group USB Device Interface (UDI) for Communication Class Device (CDC) + * + * Common APIs used by high level application to use this USB class. + * + * These routines are used to transfer and control data + * to/from USB CDC endpoint. + * + * See \ref udi_cdc_quickstart. + * @{ + */ + +/** + * \name Interface for application with single CDC interface support + */ +//@{ + +/** + * \brief Notify a state change of DCD signal + * + * \param b_set DCD is enabled if true, else disabled + */ +void udi_cdc_ctrl_signal_dcd(bool b_set); + +/** + * \brief Notify a state change of DSR signal + * + * \param b_set DSR is enabled if true, else disabled + */ +void udi_cdc_ctrl_signal_dsr(bool b_set); + +/** + * \brief Notify a framing error + */ +void udi_cdc_signal_framing_error(void); + +/** + * \brief Notify a parity error + */ +void udi_cdc_signal_parity_error(void); + +/** + * \brief Notify a overrun + */ +void udi_cdc_signal_overrun(void); + +/** + * \brief Gets the number of byte received + * + * \return the number of data available + */ +iram_size_t udi_cdc_get_nb_received_data(void); + +/** + * \brief This function checks if a character has been received on the CDC line + * + * \return \c 1 if a byte is ready to be read. + */ +bool udi_cdc_is_rx_ready(void); + +/** + * \brief Waits and gets a value on CDC line + * + * \return value read on CDC line + */ +int udi_cdc_getc(void); + +/** + * \brief Reads a RAM buffer on CDC line + * + * \param buf Values read + * \param size Number of value read + * + * \return the number of data remaining + */ +iram_size_t udi_cdc_read_buf(void* buf, iram_size_t size); + +/** + * \brief Non polling reads of a up to 'size' data from CDC line + * + * \param port Communication port number to manage + * \param buf Buffer where to store read data + * \param size Maximum number of data to read (size of buffer) + * + * \return the number of data effectively read + */ +iram_size_t udi_cdc_read_no_polling(void* buf, iram_size_t size); + +/** + * \brief Gets the number of free byte in TX buffer + * + * \return the number of free byte in TX buffer + */ +iram_size_t udi_cdc_get_free_tx_buffer(void); + +/** + * \brief This function checks if a new character sent is possible + * The type int is used to support scanf redirection from compiler LIB. + * + * \return \c 1 if a new character can be sent + */ +bool udi_cdc_is_tx_ready(void); + +/** + * \brief Puts a byte on CDC line + * The type int is used to support printf redirection from compiler LIB. + * + * \param value Value to put + * + * \return \c 1 if function was successfully done, otherwise \c 0. + */ +int udi_cdc_putc(int value); + +/** + * \brief Writes a RAM buffer on CDC line + * + * \param buf Values to write + * \param size Number of value to write + * + * \return the number of data remaining + */ +iram_size_t udi_cdc_write_buf(const void* buf, iram_size_t size); +//@} + +/** + * \name Interface for application with multi CDC interfaces support + */ +//@{ + +/** + * \brief Notify a state change of DCD signal + * + * \param port Communication port number to manage + * \param b_set DCD is enabled if true, else disabled + */ +void udi_cdc_multi_ctrl_signal_dcd(uint8_t port, bool b_set); + +/** + * \brief Notify a state change of DSR signal + * + * \param port Communication port number to manage + * \param b_set DSR is enabled if true, else disabled + */ +void udi_cdc_multi_ctrl_signal_dsr(uint8_t port, bool b_set); + +/** + * \brief Notify a framing error + * + * \param port Communication port number to manage + */ +void udi_cdc_multi_signal_framing_error(uint8_t port); + +/** + * \brief Notify a parity error + * + * \param port Communication port number to manage + */ +void udi_cdc_multi_signal_parity_error(uint8_t port); + +/** + * \brief Notify a overrun + * + * \param port Communication port number to manage + */ +void udi_cdc_multi_signal_overrun(uint8_t port); + +/** + * \brief Gets the number of byte received + * + * \param port Communication port number to manage + * + * \return the number of data available + */ +iram_size_t udi_cdc_multi_get_nb_received_data(uint8_t port); + +/** + * \brief This function checks if a character has been received on the CDC line + * + * \param port Communication port number to manage + * + * \return \c 1 if a byte is ready to be read. + */ +bool udi_cdc_multi_is_rx_ready(uint8_t port); + +/** + * \brief Waits and gets a value on CDC line + * + * \param port Communication port number to manage + * + * \return value read on CDC line + */ +int udi_cdc_multi_getc(uint8_t port); + +/** + * \brief Reads a RAM buffer on CDC line + * + * \param port Communication port number to manage + * \param buf Values read + * \param size Number of values read + * + * \return the number of data remaining + */ +iram_size_t udi_cdc_multi_read_buf(uint8_t port, void* buf, iram_size_t size); + +/** + * \brief Gets the number of free byte in TX buffer + * + * \param port Communication port number to manage + * + * \return the number of free byte in TX buffer + */ +iram_size_t udi_cdc_multi_get_free_tx_buffer(uint8_t port); + +/** + * \brief This function checks if a new character sent is possible + * The type int is used to support scanf redirection from compiler LIB. + * + * \param port Communication port number to manage + * + * \return \c 1 if a new character can be sent + */ +bool udi_cdc_multi_is_tx_ready(uint8_t port); + +/** + * \brief Puts a byte on CDC line + * The type int is used to support printf redirection from compiler LIB. + * + * \param port Communication port number to manage + * \param value Value to put + * + * \return \c 1 if function was successfully done, otherwise \c 0. + */ +int udi_cdc_multi_putc(uint8_t port, int value); + +/** + * \brief Writes a RAM buffer on CDC line + * + * \param port Communication port number to manage + * \param buf Values to write + * \param size Number of value to write + * + * \return the number of data remaining + */ +iram_size_t udi_cdc_multi_write_buf(uint8_t port, const void* buf, iram_size_t size); +//@} + +#define CDC_PRINTBUF_SIZE 256 +extern char printbuf[CDC_PRINTBUF_SIZE]; + +#define CDC_INBUF_SIZE 256 + +typedef struct { + uint32_t count; + uint32_t lastcount; + char buf[CDC_INBUF_SIZE]; +} inbuf_t; + +#else //CDC + +// keep these to accommodate calls if remaining +#define CDC_PRINTBUF_SIZE 1 +extern char printbuf[CDC_PRINTBUF_SIZE]; + +#define CDC_INBUF_SIZE 1 + +typedef struct { + uint32_t count; + uint32_t lastcount; + char buf[CDC_INBUF_SIZE]; +} inbuf_t; + +extern inbuf_t inbuf; + +#endif //CDC + +uint32_t CDC_print(char *printbuf); +int CDC_printf(const char *_Format, ...); +uint32_t CDC_input(void); +void CDC_init(void); + +#ifdef __cplusplus +} +#endif + +#endif // _UDI_CDC_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udi_cdc_conf.h b/tmk_core/protocol/arm_atsam/usb/udi_cdc_conf.h new file mode 100644 index 0000000000..2db61fab54 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_cdc_conf.h @@ -0,0 +1,72 @@ +/** + * \file + * + * \brief Default CDC configuration for a USB Device with a single interface + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDI_CDC_CONF_H_ +#define _UDI_CDC_CONF_H_ + +#include "usb_protocol_cdc.h" +#include "conf_usb.h" +#include "udi_device_conf.h" + +#ifndef UDI_CDC_PORT_NB +#define UDI_CDC_PORT_NB 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define UDI_CDC_DATA_EP_IN_0 ((CDC_TX_ENDPOINT) | (USB_EP_DIR_IN)) //TX +#define UDI_CDC_DATA_EP_OUT_0 ((CDC_RX_ENDPOINT) | (USB_EP_DIR_OUT)) // RX +#define UDI_CDC_COMM_EP_0 ((CDC_ACM_ENDPOINT) | (USB_EP_DIR_IN)) // Notify endpoint + +#define UDI_CDC_COMM_IFACE_NUMBER_0 (CDC_STATUS_INTERFACE) +#define UDI_CDC_DATA_IFACE_NUMBER_0 (CDC_DATA_INTERFACE) + +#ifdef __cplusplus +} +#endif +#endif // _UDI_CDC_CONF_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udi_device_conf.h b/tmk_core/protocol/arm_atsam/usb/udi_device_conf.h new file mode 100644 index 0000000000..1e82b9eccb --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_device_conf.h @@ -0,0 +1,781 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _UDI_DEVICE_CONF_H_ +#define _UDI_DEVICE_CONF_H_ + +#include "udi_device_epsize.h" +#include "usb_protocol.h" +#include "compiler.h" +#include "usb_protocol_hid.h" + +#define DEVICE_CLASS 0 +#define DEVICE_SUBCLASS 0 +#define DEVICE_PROTOCOL 0 + +#define KBD + +//#define MOUSE_ENABLE //rules.mk +#ifdef MOUSE_ENABLE +#define MOU +#endif + +//#define EXTRAKEY_ENABLE //rules.mk +#ifdef EXTRAKEY_ENABLE +#define EXK +#endif + +//#define RAW_ENABLE //rules.mk +#ifdef RAW_ENABLE +#define RAW +#endif + +//#define CONSOLE_ENABLE //rules.mk +#ifdef CONSOLE_ENABLE +#define CON +#endif + +//#define NKRO_ENABLE //rules.mk +#ifdef NKRO_ENABLE +#define NKRO +#endif + +//#define MIDI_ENABLE //deferred implementation +//#ifdef MIDI_ENABLE +//#define MIDI +//#endif + +//#define VIRTSER_ENABLE //rules.mk +#ifdef VIRTSER_ENABLE +#define CDC +//because CDC uses IAD (interface association descriptor +//per USB Interface Association Descriptor Device Class Code and Use Model 7/23/2003 Rev 1.0) +#undef DEVICE_CLASS +#define DEVICE_CLASS 0xEF +#undef DEVICE_SUBCLASS +#define DEVICE_SUBCLASS 0x02 +#undef DEVICE_PROTOCOL +#define DEVICE_PROTOCOL 0x01 +#endif + +/* number of interfaces */ +#define NEXT_INTERFACE_0 0 + +#ifdef KBD +#define KEYBOARD_INTERFACE NEXT_INTERFACE_0 +#define NEXT_INTERFACE_1 (KEYBOARD_INTERFACE + 1) +#define UDI_HID_KBD_IFACE_NUMBER KEYBOARD_INTERFACE +#else +#define NEXT_INTERFACE_1 NEXT_INTERFACE_0 +#endif + +// It is important that the Raw HID interface is at a constant +// interface number, to support Linux/OSX platforms and chrome.hid +// If Raw HID is enabled, let it be always 1. +#ifdef RAW +#define RAW_INTERFACE NEXT_INTERFACE_1 +#define NEXT_INTERFACE_2 (RAW_INTERFACE + 1) +#else +#define NEXT_INTERFACE_2 NEXT_INTERFACE_1 +#endif + +#ifdef MOU +#define MOUSE_INTERFACE NEXT_INTERFACE_2 +#define UDI_HID_MOU_IFACE_NUMBER MOUSE_INTERFACE +#define NEXT_INTERFACE_3 (MOUSE_INTERFACE + 1) +#else +#define NEXT_INTERFACE_3 NEXT_INTERFACE_2 +#endif + +#ifdef EXK +#define EXTRAKEY_INTERFACE NEXT_INTERFACE_3 +#define NEXT_INTERFACE_4 (EXTRAKEY_INTERFACE + 1) +#define UDI_HID_EXK_IFACE_NUMBER EXTRAKEY_INTERFACE +#else +#define NEXT_INTERFACE_4 NEXT_INTERFACE_3 +#endif + +#ifdef CON +#define CON_INTERFACE NEXT_INTERFACE_4 +#define NEXT_INTERFACE_5 (CON_INTERFACE + 1) +#define UDI_HID_CON_IFACE_NUMBER CON_INTERFACE +#else +#define NEXT_INTERFACE_5 NEXT_INTERFACE_4 +#endif + +#ifdef NKRO +#define NKRO_INTERFACE NEXT_INTERFACE_5 +#define NEXT_INTERFACE_6 (NKRO_INTERFACE + 1) +#define UDI_HID_NKRO_IFACE_NUMBER NKRO_INTERFACE +#else +#define NEXT_INTERFACE_6 NEXT_INTERFACE_5 +#endif + +#ifdef MIDI +#define AC_INTERFACE NEXT_INTERFACE_6 +#define AS_INTERFACE (AC_INTERFACE + 1) +#define NEXT_INTERFACE_7 (AS_INTERFACE + 1) +#else +#define NEXT_INTERFACE_7 NEXT_INTERFACE_6 +#endif + +#ifdef CDC +#define CCI_INTERFACE NEXT_INTERFACE_7 +#define CDI_INTERFACE (CCI_INTERFACE + 1) +#define NEXT_INTERFACE_8 (CDI_INTERFACE + 1) +#define CDC_STATUS_INTERFACE CCI_INTERFACE +#define CDC_DATA_INTERFACE CDI_INTERFACE +#else +#define NEXT_INTERFACE_8 NEXT_INTERFACE_7 +#endif + +/* nubmer of interfaces */ +#define TOTAL_INTERFACES NEXT_INTERFACE_8 +#define USB_DEVICE_NB_INTERFACE TOTAL_INTERFACES + + +// ********************************************************************** +// Endopoint number and size +// ********************************************************************** +#define USB_DEVICE_EP_CTRL_SIZE 8 + +#define NEXT_IN_EPNUM_0 1 +#define NEXT_OUT_EPNUM_0 1 + +#ifdef KBD +#define KEYBOARD_IN_EPNUM NEXT_IN_EPNUM_0 +#define UDI_HID_KBD_EP_IN KEYBOARD_IN_EPNUM +#define NEXT_IN_EPNUM_1 (KEYBOARD_IN_EPNUM + 1) +#define UDI_HID_KBD_EP_SIZE KEYBOARD_EPSIZE +#define KBD_POLLING_INTERVAL 10 +#ifndef UDI_HID_KBD_STRING_ID +#define UDI_HID_KBD_STRING_ID 0 +#endif +#else +#define NEXT_IN_EPNUM_1 NEXT_IN_EPNUM_0 +#endif + +#ifdef MOU +#define MOUSE_IN_EPNUM NEXT_IN_EPNUM_1 +#define NEXT_IN_EPNUM_2 (MOUSE_IN_EPNUM + 1) +#define UDI_HID_MOU_EP_IN MOUSE_IN_EPNUM +#define UDI_HID_MOU_EP_SIZE MOUSE_EPSIZE +#define MOU_POLLING_INTERVAL 10 +#ifndef UDI_HID_MOU_STRING_ID +#define UDI_HID_MOU_STRING_ID 0 +#endif +#else +#define NEXT_IN_EPNUM_2 NEXT_IN_EPNUM_1 +#endif + +#ifdef EXK +#define EXTRAKEY_IN_EPNUM NEXT_IN_EPNUM_2 +#define UDI_HID_EXK_EP_IN EXTRAKEY_IN_EPNUM +#define NEXT_IN_EPNUM_3 (EXTRAKEY_IN_EPNUM + 1) +#define UDI_HID_EXK_EP_SIZE EXTRAKEY_EPSIZE +#define EXTRAKEY_POLLING_INTERVAL 10 +#ifndef UDI_HID_EXK_STRING_ID +#define UDI_HID_EXK_STRING_ID 0 +#endif +#else +#define NEXT_IN_EPNUM_3 NEXT_IN_EPNUM_2 +#endif + +#ifdef RAW +#define RAW_IN_EPNUM NEXT_IN_EPNUM_3 +#define UDI_HID_RAW_EP_IN RAW_IN_EPNUM +#define NEXT_IN_EPNUM_4 (RAW_IN_EPNUM + 1) +#define RAW_OUT_EPNUM NEXT_OUT_EPNUM_0 +#define UDI_HID_RAW_EP_OUT RAW_OUT_EPNUM +#define NEXT_OUT_EPNUM_1 (RAW_OUT_EPNUM + 1) +#define RAW_POLLING_INTERVAL 1 +#ifndef UDI_HID_RAW_STRING_ID +#define UDI_HID_RAW_STRING_ID 0 +#endif +#else +#define NEXT_IN_EPNUM_4 NEXT_IN_EPNUM_3 +#define NEXT_OUT_EPNUM_1 NEXT_OUT_EPNUM_0 +#endif + +#ifdef CON +#define CON_IN_EPNUM NEXT_IN_EPNUM_4 +#define UDI_HID_CON_EP_IN CON_IN_EPNUM +#define NEXT_IN_EPNUM_5 (CON_IN_EPNUM + 1) +#define CON_OUT_EPNUM NEXT_OUT_EPNUM_1 +#define UDI_HID_CON_EP_OUT CON_OUT_EPNUM +#define NEXT_OUT_EPNUM_2 (CON_OUT_EPNUM + 1) +#define CON_POLLING_INTERVAL 1 +#ifndef UDI_HID_CON_STRING_ID +#define UDI_HID_CON_STRING_ID 0 +#endif +#else +#define NEXT_IN_EPNUM_5 NEXT_IN_EPNUM_4 +#define NEXT_OUT_EPNUM_2 NEXT_OUT_EPNUM_1 +#endif + +#ifdef NKRO +#define NKRO_IN_EPNUM NEXT_IN_EPNUM_5 +#define UDI_HID_NKRO_EP_IN NKRO_IN_EPNUM +#define NEXT_IN_EPNUM_6 (NKRO_IN_EPNUM + 1) +#define UDI_HID_NKRO_EP_SIZE NKRO_EPSIZE +#define NKRO_POLLING_INTERVAL 1 +#ifndef UDI_HID_NKRO_STRING_ID +#define UDI_HID_NKRO_STRING_ID 0 +#endif +#else +#define NEXT_IN_EPNUM_6 NEXT_IN_EPNUM_5 +#endif + +#ifdef MIDI +#define MIDI_STREAM_IN_EPNUM NEXT_IN_EPNUM_6 +#define NEXT_IN_EPNUM_7 (MIDI_STREAM_IN_EPNUM + 1) +#define MIDI_STREAM_OUT_EPNUM NEXT_OUT_EPNUM_2 +#define NEXT_OUT_EPNUM_3 (MIDI_STREAM_OUT_EPNUM + 1) +#define MIDI_POLLING_INTERVAL 5 +#else +#define NEXT_IN_EPNUM_7 NEXT_IN_EPNUM_6 +#define NEXT_OUT_EPNUM_3 NEXT_OUT_EPNUM_2 +#endif + +#ifdef CDC +#define CDC_NOTIFICATION_EPNUM NEXT_IN_EPNUM_7 +#define CDC_ACM_ENDPOINT CDC_NOTIFICATION_EPNUM +#define CDC_TX_ENDPOINT (CDC_NOTIFICATION_EPNUM + 1) +#define NEXT_IN_EPNUM_8 (CDC_TX_ENDPOINT + 1) + +#define CDC_OUT_EPNUM NEXT_OUT_EPNUM_3 +#define CDC_RX_ENDPOINT CDC_OUT_EPNUM +#define NEXT_OUT_EPNUM_4 (CDC_OUT_EPNUM + 1) + +#define CDC_ACM_SIZE CDC_NOTIFICATION_EPSIZE +#define CDC_RX_SIZE CDC_EPSIZE //KFSMOD was 64 +#define CDC_TX_SIZE CDC_RX_SIZE +#define CDC_ACM_POLLING_INTERVAL 255 +#define CDC_EP_INTERVAL_STATUS CDC_ACM_POLLING_INTERVAL +#define CDC_DATA_POLLING_INTERVAL 5 +#define CDC_EP_INTERVAL_DATA CDC_DATA_POLLING_INTERVAL +#define CDC_STATUS_NAME L"Virtual Serial Port - Status" +#define CDC_DATA_NAME L"Virtual Serial Port - Data" +#else +#define NEXT_IN_EPNUM_8 NEXT_IN_EPNUM_7 +#define NEXT_OUT_EPNUM_4 NEXT_OUT_EPNUM_3 +#endif + +#define TOTAL_OUT_EP NEXT_OUT_EPNUM_4 +#define TOTAL_IN_EP NEXT_IN_EPNUM_8 +#define USB_DEVICE_MAX_EP (max(NEXT_OUT_EPNUM_4, NEXT_IN_EPNUM_8)) + +#if USB_DEVICE_MAX_EP > 8 +#error "There are not enough available endpoints to support all functions. Remove some in the rules.mk file.(MOUSEKEY, EXTRAKEY, CONSOLE, NKRO, MIDI, VIRTSER)" +#endif + + +// ********************************************************************** +// KBD Descriptor structure and content +// ********************************************************************** +#ifdef KBD + +COMPILER_PACK_SET(1) + +typedef struct { + usb_iface_desc_t iface; + usb_hid_descriptor_t hid; + usb_ep_desc_t ep; +} udi_hid_kbd_desc_t; + +typedef struct { + uint8_t array[59]; +} udi_hid_kbd_report_desc_t; + +#define UDI_HID_KBD_DESC {\ + .iface.bLength = sizeof(usb_iface_desc_t),\ + .iface.bDescriptorType = USB_DT_INTERFACE,\ + .iface.bInterfaceNumber = UDI_HID_KBD_IFACE_NUMBER,\ + .iface.bAlternateSetting = 0,\ + .iface.bNumEndpoints = 1,\ + .iface.bInterfaceClass = HID_CLASS,\ + .iface.bInterfaceSubClass = HID_SUB_CLASS_BOOT,\ + .iface.bInterfaceProtocol = HID_PROTOCOL_KEYBOARD,\ + .iface.iInterface = UDI_HID_KBD_STRING_ID,\ + .hid.bLength = sizeof(usb_hid_descriptor_t),\ + .hid.bDescriptorType = USB_DT_HID,\ + .hid.bcdHID = LE16(USB_HID_BDC_V1_11),\ + .hid.bCountryCode = USB_HID_NO_COUNTRY_CODE,\ + .hid.bNumDescriptors = USB_HID_NUM_DESC,\ + .hid.bRDescriptorType = USB_DT_HID_REPORT,\ + .hid.wDescriptorLength = LE16(sizeof(udi_hid_kbd_report_desc_t)),\ + .ep.bLength = sizeof(usb_ep_desc_t),\ + .ep.bDescriptorType = USB_DT_ENDPOINT,\ + .ep.bEndpointAddress = UDI_HID_KBD_EP_IN | USB_EP_DIR_IN,\ + .ep.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep.wMaxPacketSize = LE16(UDI_HID_KBD_EP_SIZE),\ + .ep.bInterval = KBD_POLLING_INTERVAL,\ +} + +//set report buffer (from host) +extern uint8_t udi_hid_kbd_report_set; + +//report buffer (to host) +#define UDI_HID_KBD_REPORT_SIZE 8 +extern uint8_t udi_hid_kbd_report[UDI_HID_KBD_REPORT_SIZE]; + +COMPILER_PACK_RESET() + +#endif //KBD + +// ********************************************************************** +// EXK Descriptor structure and content +// ********************************************************************** +#ifdef EXK + +COMPILER_PACK_SET(1) + +typedef struct { + usb_iface_desc_t iface; + usb_hid_descriptor_t hid; + usb_ep_desc_t ep; +} udi_hid_exk_desc_t; + +typedef struct { + uint8_t array[54]; +} udi_hid_exk_report_desc_t; + +#define UDI_HID_EXK_DESC {\ + .iface.bLength = sizeof(usb_iface_desc_t),\ + .iface.bDescriptorType = USB_DT_INTERFACE,\ + .iface.bInterfaceNumber = UDI_HID_EXK_IFACE_NUMBER,\ + .iface.bAlternateSetting = 0,\ + .iface.bNumEndpoints = 1,\ + .iface.bInterfaceClass = HID_CLASS,\ + .iface.bInterfaceSubClass = HID_SUB_CLASS_BOOT,\ + .iface.bInterfaceProtocol = HID_PROTOCOL_GENERIC,\ + .iface.iInterface = UDI_HID_EXK_STRING_ID,\ + .hid.bLength = sizeof(usb_hid_descriptor_t),\ + .hid.bDescriptorType = USB_DT_HID,\ + .hid.bcdHID = LE16(USB_HID_BDC_V1_11),\ + .hid.bCountryCode = USB_HID_NO_COUNTRY_CODE,\ + .hid.bNumDescriptors = USB_HID_NUM_DESC,\ + .hid.bRDescriptorType = USB_DT_HID_REPORT,\ + .hid.wDescriptorLength = LE16(sizeof(udi_hid_exk_report_desc_t)),\ + .ep.bLength = sizeof(usb_ep_desc_t),\ + .ep.bDescriptorType = USB_DT_ENDPOINT,\ + .ep.bEndpointAddress = UDI_HID_EXK_EP_IN | USB_EP_DIR_IN,\ + .ep.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep.wMaxPacketSize = LE16(UDI_HID_EXK_EP_SIZE),\ + .ep.bInterval = EXTRAKEY_POLLING_INTERVAL,\ +} + +//set report buffer (from host) +extern uint8_t udi_hid_exk_report_set; + +//report buffer +#define UDI_HID_EXK_REPORT_SIZE 3 + +typedef union { + struct { + uint8_t report_id; + uint16_t report_data; + } desc; + uint8_t raw[UDI_HID_EXK_REPORT_SIZE]; +} udi_hid_exk_report_t; + +extern udi_hid_exk_report_t udi_hid_exk_report; + +COMPILER_PACK_RESET() + +#endif //EXK + +// ********************************************************************** +// NKRO Descriptor structure and content +// ********************************************************************** +#ifdef NKRO + +COMPILER_PACK_SET(1) + +typedef struct { + usb_iface_desc_t iface; + usb_hid_descriptor_t hid; + usb_ep_desc_t ep; +} udi_hid_nkro_desc_t; + +typedef struct { + uint8_t array[57]; +} udi_hid_nkro_report_desc_t; + +#define UDI_HID_NKRO_DESC {\ + .iface.bLength = sizeof(usb_iface_desc_t),\ + .iface.bDescriptorType = USB_DT_INTERFACE,\ + .iface.bInterfaceNumber = UDI_HID_NKRO_IFACE_NUMBER,\ + .iface.bAlternateSetting = 0,\ + .iface.bNumEndpoints = 1,\ + .iface.bInterfaceClass = HID_CLASS,\ + .iface.bInterfaceSubClass = HID_SUB_CLASS_NOBOOT,\ + .iface.bInterfaceProtocol = HID_PROTOCOL_KEYBOARD,\ + .iface.iInterface = UDI_HID_NKRO_STRING_ID,\ + .hid.bLength = sizeof(usb_hid_descriptor_t),\ + .hid.bDescriptorType = USB_DT_HID,\ + .hid.bcdHID = LE16(USB_HID_BDC_V1_11),\ + .hid.bCountryCode = USB_HID_NO_COUNTRY_CODE,\ + .hid.bNumDescriptors = USB_HID_NUM_DESC,\ + .hid.bRDescriptorType = USB_DT_HID_REPORT,\ + .hid.wDescriptorLength = LE16(sizeof(udi_hid_nkro_report_desc_t)),\ + .ep.bLength = sizeof(usb_ep_desc_t),\ + .ep.bDescriptorType = USB_DT_ENDPOINT,\ + .ep.bEndpointAddress = UDI_HID_NKRO_EP_IN | USB_EP_DIR_IN,\ + .ep.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep.wMaxPacketSize = LE16(UDI_HID_NKRO_EP_SIZE),\ + .ep.bInterval = NKRO_POLLING_INTERVAL,\ +} + +//set report buffer +extern uint8_t udi_hid_nkro_report_set; + +//report buffer +#define UDI_HID_NKRO_REPORT_SIZE 32 +extern uint8_t udi_hid_nkro_report[UDI_HID_NKRO_REPORT_SIZE]; + +COMPILER_PACK_RESET() + +#endif //NKRO + +// ********************************************************************** +// MOU Descriptor structure and content +// ********************************************************************** +#ifdef MOU + +COMPILER_PACK_SET(1) + +typedef struct { + usb_iface_desc_t iface; + usb_hid_descriptor_t hid; + usb_ep_desc_t ep; +} udi_hid_mou_desc_t; + +typedef struct { + uint8_t array[77];//MOU PDS +} udi_hid_mou_report_desc_t; + +#define UDI_HID_MOU_DESC {\ + .iface.bLength = sizeof(usb_iface_desc_t),\ + .iface.bDescriptorType = USB_DT_INTERFACE,\ + .iface.bInterfaceNumber = MOUSE_INTERFACE,\ + .iface.bAlternateSetting = 0,\ + .iface.bNumEndpoints = 1,\ + .iface.bInterfaceClass = HID_CLASS,\ + .iface.bInterfaceSubClass = HID_SUB_CLASS_BOOT,\ + .iface.bInterfaceProtocol = HID_PROTOCOL_MOUSE,\ + .iface.iInterface = UDI_HID_MOU_STRING_ID,\ + .hid.bLength = sizeof(usb_hid_descriptor_t),\ + .hid.bDescriptorType = USB_DT_HID,\ + .hid.bcdHID = LE16(USB_HID_BDC_V1_11),\ + .hid.bCountryCode = USB_HID_NO_COUNTRY_CODE,\ + .hid.bNumDescriptors = USB_HID_NUM_DESC,\ + .hid.bRDescriptorType = USB_DT_HID_REPORT,\ + .hid.wDescriptorLength = LE16(sizeof(udi_hid_mou_report_desc_t)),\ + .ep.bLength = sizeof(usb_ep_desc_t),\ + .ep.bDescriptorType = USB_DT_ENDPOINT,\ + .ep.bEndpointAddress = UDI_HID_MOU_EP_IN | USB_EP_DIR_IN,\ + .ep.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep.wMaxPacketSize = LE16(UDI_HID_MOU_EP_SIZE),\ + .ep.bInterval = MOU_POLLING_INTERVAL,\ +} + +//no set report buffer + +//report buffer +#define UDI_HID_MOU_REPORT_SIZE 5 //MOU PDS +extern uint8_t udi_hid_mou_report[UDI_HID_MOU_REPORT_SIZE]; + +COMPILER_PACK_RESET() + +#endif //MOU + +// ********************************************************************** +// RAW Descriptor structure and content +// ********************************************************************** +#ifdef RAW + +COMPILER_PACK_SET(1) + +typedef struct { + usb_iface_desc_t iface; + usb_hid_descriptor_t hid; + usb_ep_desc_t ep_out; + usb_ep_desc_t ep_in; +} udi_hid_raw_desc_t; + +typedef struct { + uint8_t array[27]; +} udi_hid_raw_report_desc_t; + +#define UDI_HID_RAW_DESC {\ + .iface.bLength = sizeof(usb_iface_desc_t),\ + .iface.bDescriptorType = USB_DT_INTERFACE,\ + .iface.bInterfaceNumber = RAW_INTERFACE,\ + .iface.bAlternateSetting = 0,\ + .iface.bNumEndpoints = 2,\ + .iface.bInterfaceClass = HID_CLASS,\ + .iface.bInterfaceSubClass = HID_SUB_CLASS_NOBOOT,\ + .iface.bInterfaceProtocol = HID_SUB_CLASS_NOBOOT,\ + .iface.iInterface = UDI_HID_RAW_STRING_ID,\ + .hid.bLength = sizeof(usb_hid_descriptor_t),\ + .hid.bDescriptorType = USB_DT_HID,\ + .hid.bcdHID = LE16(USB_HID_BDC_V1_11),\ + .hid.bCountryCode = USB_HID_NO_COUNTRY_CODE,\ + .hid.bNumDescriptors = USB_HID_NUM_DESC,\ + .hid.bRDescriptorType = USB_DT_HID_REPORT,\ + .hid.wDescriptorLength = LE16(sizeof(udi_hid_raw_report_desc_t)),\ + .ep_out.bLength = sizeof(usb_ep_desc_t),\ + .ep_out.bDescriptorType = USB_DT_ENDPOINT,\ + .ep_out.bEndpointAddress = UDI_HID_RAW_EP_OUT | USB_EP_DIR_OUT,\ + .ep_out.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep_out.wMaxPacketSize = LE16(RAW_EPSIZE),\ + .ep_out.bInterval = RAW_POLLING_INTERVAL,\ + .ep_in.bLength = sizeof(usb_ep_desc_t),\ + .ep_in.bDescriptorType = USB_DT_ENDPOINT,\ + .ep_in.bEndpointAddress = UDI_HID_RAW_EP_IN | USB_EP_DIR_IN,\ + .ep_in.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep_in.wMaxPacketSize = LE16(RAW_EPSIZE),\ + .ep_in.bInterval = RAW_POLLING_INTERVAL,\ +} + +#define UDI_HID_RAW_REPORT_SIZE RAW_EPSIZE + +extern uint8_t udi_hid_raw_report_set[UDI_HID_RAW_REPORT_SIZE]; + +//report buffer +extern uint8_t udi_hid_raw_report[UDI_HID_RAW_REPORT_SIZE]; + +COMPILER_PACK_RESET() + +#endif //RAW + +// ********************************************************************** +// CON Descriptor structure and content +// ********************************************************************** +#ifdef CON + +COMPILER_PACK_SET(1) + +typedef struct { + usb_iface_desc_t iface; + usb_hid_descriptor_t hid; + usb_ep_desc_t ep_out; + usb_ep_desc_t ep_in; +} udi_hid_con_desc_t; + +typedef struct { + uint8_t array[34]; +} udi_hid_con_report_desc_t; + +#define UDI_HID_CON_DESC {\ + .iface.bLength = sizeof(usb_iface_desc_t),\ + .iface.bDescriptorType = USB_DT_INTERFACE,\ + .iface.bInterfaceNumber = UDI_HID_CON_IFACE_NUMBER,\ + .iface.bAlternateSetting = 0,\ + .iface.bNumEndpoints = 2,\ + .iface.bInterfaceClass = HID_CLASS,\ + .iface.bInterfaceSubClass = HID_SUB_CLASS_NOBOOT,\ + .iface.bInterfaceProtocol = HID_SUB_CLASS_NOBOOT,\ + .iface.iInterface = UDI_HID_CON_STRING_ID,\ + .hid.bLength = sizeof(usb_hid_descriptor_t),\ + .hid.bDescriptorType = USB_DT_HID,\ + .hid.bcdHID = LE16(USB_HID_BDC_V1_11),\ + .hid.bCountryCode = USB_HID_NO_COUNTRY_CODE,\ + .hid.bNumDescriptors = USB_HID_NUM_DESC,\ + .hid.bRDescriptorType = USB_DT_HID_REPORT,\ + .hid.wDescriptorLength = LE16(sizeof(udi_hid_con_report_desc_t)),\ + .ep_out.bLength = sizeof(usb_ep_desc_t),\ + .ep_out.bDescriptorType = USB_DT_ENDPOINT,\ + .ep_out.bEndpointAddress = UDI_HID_CON_EP_OUT | USB_EP_DIR_OUT,\ + .ep_out.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep_out.wMaxPacketSize = LE16(CONSOLE_EPSIZE),\ + .ep_out.bInterval = CON_POLLING_INTERVAL,\ + .ep_in.bLength = sizeof(usb_ep_desc_t),\ + .ep_in.bDescriptorType = USB_DT_ENDPOINT,\ + .ep_in.bEndpointAddress = UDI_HID_CON_EP_IN | USB_EP_DIR_IN,\ + .ep_in.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep_in.wMaxPacketSize = LE16(CONSOLE_EPSIZE),\ + .ep_in.bInterval = CON_POLLING_INTERVAL,\ +} + +#define UDI_HID_CON_REPORT_SIZE CONSOLE_EPSIZE + +extern uint8_t udi_hid_con_report_set[UDI_HID_CON_REPORT_SIZE]; + +//report buffer +extern uint8_t udi_hid_con_report[UDI_HID_CON_REPORT_SIZE]; + +COMPILER_PACK_RESET() + +#endif //CON + +// ********************************************************************** +// CDC Descriptor structure and content +// ********************************************************************** +#ifdef CDC + +COMPILER_PACK_SET(1) + +typedef struct { + uint8_t bFunctionLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + le16_t bcdCDC; +} usb_cdc_hdr_desc_t; + +typedef struct { + uint8_t bFunctionLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bmCapabilities; + uint8_t bDataInterface; +} usb_cdc_call_mgmt_desc_t; + +typedef struct { + uint8_t bFunctionLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bmCapabilities; +} usb_cdc_acm_desc_t; + +typedef struct { + uint8_t bFunctionLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bMasterInterface; + uint8_t bSlaveInterface0; +} usb_cdc_union_desc_t; + +typedef struct { + usb_association_desc_t iaface; + usb_iface_desc_t iface_c; + usb_cdc_hdr_desc_t fd; + usb_cdc_call_mgmt_desc_t mfd; + usb_cdc_acm_desc_t acmd; + usb_cdc_union_desc_t ufd; + usb_ep_desc_t ep_c; + usb_iface_desc_t iface_d; + usb_ep_desc_t ep_tx; + usb_ep_desc_t ep_rx; +} udi_cdc_desc_t; + +#define CDC_DESCRIPTOR {\ + .iaface.bLength = sizeof(usb_association_desc_t),\ + .iaface.bDescriptorType = USB_DT_IAD,\ + .iaface.bFirstInterface = CDC_STATUS_INTERFACE,\ + .iaface.bInterfaceCount = 2,\ + .iaface.bFunctionClass = CDC_CLASS_DEVICE,\ + .iaface.bFunctionSubClass = CDC_SUBCLASS_ACM,\ + .iaface.bFunctionProtocol = CDC_PROTOCOL_V25TER,\ + .iaface.iFunction = 0,\ + .iface_c.bLength = sizeof(usb_iface_desc_t),\ + .iface_c.bDescriptorType = USB_DT_INTERFACE,\ + .iface_c.bInterfaceNumber = CDC_STATUS_INTERFACE,\ + .iface_c.bAlternateSetting = 0,\ + .iface_c.bNumEndpoints = 1,\ + .iface_c.bInterfaceClass = 0x02,\ + .iface_c.bInterfaceSubClass = 0x02,\ + .iface_c.bInterfaceProtocol = CDC_PROTOCOL_V25TER,\ + .iface_c.iInterface = 0,\ + .fd.bFunctionLength = sizeof(usb_cdc_hdr_desc_t),\ + .fd.bDescriptorType = CDC_CS_INTERFACE,\ + .fd.bDescriptorSubtype = CDC_SCS_HEADER,\ + .fd.bcdCDC = 0x0110,\ + .mfd.bFunctionLength = sizeof(usb_cdc_call_mgmt_desc_t),\ + .mfd.bDescriptorType = CDC_CS_INTERFACE,\ + .mfd.bDescriptorSubtype = CDC_SCS_CALL_MGMT,\ + .mfd.bmCapabilities = CDC_CALL_MGMT_SUPPORTED,\ + .mfd.bDataInterface = CDC_DATA_INTERFACE,\ + .acmd.bFunctionLength = sizeof(usb_cdc_acm_desc_t),\ + .acmd.bDescriptorType = CDC_CS_INTERFACE,\ + .acmd.bDescriptorSubtype = CDC_SCS_ACM,\ + .acmd.bmCapabilities = CDC_ACM_SUPPORT_LINE_REQUESTS,\ + .ufd.bFunctionLength = sizeof(usb_cdc_union_desc_t),\ + .ufd.bDescriptorType = CDC_CS_INTERFACE,\ + .ufd.bDescriptorSubtype = CDC_SCS_UNION,\ + .ufd.bMasterInterface = CDC_STATUS_INTERFACE,\ + .ufd.bSlaveInterface0 = CDC_DATA_INTERFACE,\ + .ep_c.bLength = sizeof(usb_ep_desc_t),\ + .ep_c.bDescriptorType = USB_DT_ENDPOINT,\ + .ep_c.bEndpointAddress = CDC_ACM_ENDPOINT | USB_EP_DIR_IN,\ + .ep_c.bmAttributes = USB_EP_TYPE_INTERRUPT,\ + .ep_c.wMaxPacketSize = LE16(CDC_ACM_SIZE),\ + .ep_c.bInterval = CDC_EP_INTERVAL_STATUS,\ + .iface_d.bLength = sizeof(usb_iface_desc_t),\ + .iface_d.bDescriptorType = USB_DT_INTERFACE,\ + .iface_d.bInterfaceNumber = CDC_DATA_INTERFACE,\ + .iface_d.bAlternateSetting = 0,\ + .iface_d.bNumEndpoints = 2,\ + .iface_d.bInterfaceClass = CDC_CLASS_DATA,\ + .iface_d.bInterfaceSubClass = 0,\ + .iface_d.bInterfaceProtocol = 0,\ + .iface_d.iInterface = 0,\ + .ep_rx.bLength = sizeof(usb_ep_desc_t),\ + .ep_rx.bDescriptorType = USB_DT_ENDPOINT,\ + .ep_rx.bEndpointAddress = CDC_RX_ENDPOINT | USB_EP_DIR_OUT,\ + .ep_rx.bmAttributes = USB_EP_TYPE_BULK,\ + .ep_rx.wMaxPacketSize = LE16(CDC_RX_SIZE),\ + .ep_rx.bInterval = CDC_EP_INTERVAL_DATA,\ + .ep_tx.bLength = sizeof(usb_ep_desc_t),\ + .ep_tx.bDescriptorType = USB_DT_ENDPOINT,\ + .ep_tx.bEndpointAddress = CDC_TX_ENDPOINT | USB_EP_DIR_IN,\ + .ep_tx.bmAttributes = USB_EP_TYPE_BULK,\ + .ep_tx.wMaxPacketSize = LE16(CDC_TX_SIZE),\ + .ep_tx.bInterval = CDC_EP_INTERVAL_DATA,\ +} + +COMPILER_PACK_RESET() + +#endif //CDC + +// ********************************************************************** +// CONFIGURATION Descriptor structure and content +// ********************************************************************** +COMPILER_PACK_SET(1) + +typedef struct { + usb_conf_desc_t conf; +#ifdef KBD + udi_hid_kbd_desc_t hid_kbd; +#endif +#ifdef MOU + udi_hid_mou_desc_t hid_mou; +#endif +#ifdef EXK + udi_hid_exk_desc_t hid_exk; +#endif +#ifdef RAW + udi_hid_raw_desc_t hid_raw; +#endif +#ifdef CON + udi_hid_con_desc_t hid_con; +#endif +#ifdef NKRO + udi_hid_nkro_desc_t hid_nkro; +#endif +#ifdef MIDI + udi_hid_midi_desc_t hid_midi; +#endif +#ifdef CDC + udi_cdc_desc_t cdc_serial; +#endif +} udc_desc_t; + +COMPILER_PACK_RESET() + +#endif //_UDI_DEVICE_CONF_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udi_device_epsize.h b/tmk_core/protocol/arm_atsam/usb/udi_device_epsize.h new file mode 100644 index 0000000000..96d03c2869 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_device_epsize.h @@ -0,0 +1,32 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _UDI_DEVICE_EPSIZE_H_ +#define _UDI_DEVICE_EPSIZE_H_ + +#define KEYBOARD_EPSIZE 8 +#define MOUSE_EPSIZE 8 +#define EXTRAKEY_EPSIZE 8 +#define RAW_EPSIZE 64 +#define CONSOLE_EPSIZE 32 +#define NKRO_EPSIZE 32 +#define MIDI_STREAM_EPSIZE 64 +#define CDC_NOTIFICATION_EPSIZE 8 +#define CDC_EPSIZE 16 + +#endif //_UDI_DEVICE_EPSIZE_H_ + diff --git a/tmk_core/protocol/arm_atsam/usb/udi_hid.c b/tmk_core/protocol/arm_atsam/usb/udi_hid.c new file mode 100644 index 0000000000..131b7a0ece --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_hid.c @@ -0,0 +1,162 @@ +/** + * \file + * + * \brief USB Device Human Interface Device (HID) interface. + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "conf_usb.h" +#include "usb_protocol.h" +#include "udd.h" +#include "udc.h" +#include "udi_hid.h" + + +/** + * \ingroup udi_hid_group + * \defgroup udi_hid_group_internal Implementation of HID common library + * @{ + */ + +/** + * \brief Send the specific descriptors requested by SETUP request + * + * \retval true if the descriptor is supported + */ +static bool udi_hid_reqstdifaceget_descriptor(uint8_t *report_desc); + +bool udi_hid_setup( uint8_t *rate, uint8_t *protocol, uint8_t *report_desc, bool (*setup_report)(void) ) +{ + if (Udd_setup_is_in()) { + // Requests Interface GET + if (Udd_setup_type() == USB_REQ_TYPE_STANDARD) { + // Requests Standard Interface Get + switch (udd_g_ctrlreq.req.bRequest) { + + case USB_REQ_GET_DESCRIPTOR: + return udi_hid_reqstdifaceget_descriptor(report_desc); + } + } + if (Udd_setup_type() == USB_REQ_TYPE_CLASS) { + // Requests Class Interface Get + switch (udd_g_ctrlreq.req.bRequest) { + + case USB_REQ_HID_GET_REPORT: + return setup_report(); + + case USB_REQ_HID_GET_IDLE: + udd_g_ctrlreq.payload = rate; + udd_g_ctrlreq.payload_size = 1; + return true; + + case USB_REQ_HID_GET_PROTOCOL: + udd_g_ctrlreq.payload = protocol; + udd_g_ctrlreq.payload_size = 1; + return true; + } + } + } + if (Udd_setup_is_out()) { + // Requests Interface SET + if (Udd_setup_type() == USB_REQ_TYPE_CLASS) { + // Requests Class Interface Set + switch (udd_g_ctrlreq.req.bRequest) { + + case USB_REQ_HID_SET_REPORT: + return setup_report(); + + case USB_REQ_HID_SET_IDLE: + *rate = udd_g_ctrlreq.req.wValue >> 8; + return true; + + case USB_REQ_HID_SET_PROTOCOL: + if (0 != udd_g_ctrlreq.req.wLength) + return false; + *protocol = udd_g_ctrlreq.req.wValue; + return true; + } + } + } + return false; // Request not supported +} + +//--------------------------------------------- +//------- Internal routines + +static bool udi_hid_reqstdifaceget_descriptor(uint8_t *report_desc) +{ + usb_hid_descriptor_t UDC_DESC_STORAGE *ptr_hid_desc; + + // Get the USB descriptor which is located after the interface descriptor + // This descriptor must be the HID descriptor + ptr_hid_desc = (usb_hid_descriptor_t UDC_DESC_STORAGE *) ((uint8_t *) + udc_get_interface_desc() + sizeof(usb_iface_desc_t)); + if (USB_DT_HID != ptr_hid_desc->bDescriptorType) + return false; + + // The SETUP request can ask for: + // - an USB_DT_HID descriptor + // - or USB_DT_HID_REPORT descriptor + // - or USB_DT_HID_PHYSICAL descriptor + if (USB_DT_HID == (uint8_t) (udd_g_ctrlreq.req.wValue >> 8)) { + // USB_DT_HID descriptor requested then send it + udd_g_ctrlreq.payload = (uint8_t *) ptr_hid_desc; + udd_g_ctrlreq.payload_size = + min(udd_g_ctrlreq.req.wLength, + ptr_hid_desc->bLength); + return true; + } + // The HID_X descriptor requested must correspond to report type + // included in the HID descriptor + if (ptr_hid_desc->bRDescriptorType == + (uint8_t) (udd_g_ctrlreq.req.wValue >> 8)) { + // Send HID Report descriptor given by high level + udd_g_ctrlreq.payload = report_desc; + udd_g_ctrlreq.payload_size = + min(udd_g_ctrlreq.req.wLength, + le16_to_cpu(ptr_hid_desc->wDescriptorLength)); + return true; + } + return false; +} + +//@} diff --git a/tmk_core/protocol/arm_atsam/usb/udi_hid.h b/tmk_core/protocol/arm_atsam/usb/udi_hid.h new file mode 100644 index 0000000000..0edb09c1c3 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_hid.h @@ -0,0 +1,85 @@ +/** + * \file + * + * \brief USB Device Human Interface Device (HID) interface definitions. + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDI_HID_H_ +#define _UDI_HID_H_ + +#include "conf_usb.h" +#include "usb_protocol.h" +#include "usb_protocol_hid.h" +#include "udd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \ingroup udi_group + * \defgroup udi_hid_group USB Device Interface (UDI) for Human Interface Device (HID) + * + * Common library for all Human Interface Device (HID) implementation. + * + * @{ + */ + +/** + * \brief Decode HID setup request + * + * \param rate Pointer on rate of current HID interface + * \param protocol Pointer on protocol of current HID interface + * \param report_desc Pointer on report descriptor of current HID interface + * \param set_report Pointer on set_report callback of current HID interface + * + * \return \c 1 if function was successfully done, otherwise \c 0. + */ +bool udi_hid_setup( uint8_t *rate, uint8_t *protocol, uint8_t *report_desc, bool (*setup_report)(void) ); + +//@} + +#ifdef __cplusplus +} +#endif +#endif // _UDI_HID_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c new file mode 100644 index 0000000000..c263ac4aa1 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.c @@ -0,0 +1,992 @@ +/** + * \file + * + * \brief USB Device Human Interface Device (HID) keyboard interface. + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "samd51j18a.h" +#include "d51_util.h" +#include "conf_usb.h" +#include "usb_protocol.h" +#include "udd.h" +#include "udc.h" +#include "udi_device_conf.h" +#include "udi_hid.h" +#include "udi_hid_kbd.h" +#include <string.h> +#include "report.h" + +//*************************************************************************** +// KBD +//*************************************************************************** +#ifdef KBD + +bool udi_hid_kbd_enable(void); +void udi_hid_kbd_disable(void); +bool udi_hid_kbd_setup(void); +uint8_t udi_hid_kbd_getsetting(void); + +UDC_DESC_STORAGE udi_api_t udi_api_hid_kbd = { + .enable = (bool(*)(void))udi_hid_kbd_enable, + .disable = (void (*)(void))udi_hid_kbd_disable, + .setup = (bool(*)(void))udi_hid_kbd_setup, + .getsetting = (uint8_t(*)(void))udi_hid_kbd_getsetting, + .sof_notify = NULL, +}; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_kbd_rate; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_kbd_protocol; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_kbd_report_set; + +bool udi_hid_kbd_b_report_valid; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_kbd_report[UDI_HID_KBD_REPORT_SIZE]; + +volatile bool udi_hid_kbd_b_report_trans_ongoing; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_kbd_report_trans[UDI_HID_KBD_REPORT_SIZE]; + +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE udi_hid_kbd_report_desc_t udi_hid_kbd_report_desc = { + { + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x06, // Usage (Keyboard) + 0xA1, 0x01, // Collection (Application) + 0x05, 0x07, // Usage Page (Keyboard) + 0x19, 0xE0, // Usage Minimum (224) + 0x29, 0xE7, // Usage Maximum (231) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x81, 0x01, // Input (Constant) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0x65, // Usage Maximum (101) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x65, // Logical Maximum (101) + 0x75, 0x08, // Report Size (8) + 0x95, 0x06, // Report Count (6) + 0x81, 0x00, // Input (Data, Array) + 0x05, 0x08, // Usage Page (LED) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x05, // Usage Maximum (5) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x05, // Report Count (5) + 0x91, 0x02, // Output (Data, Variable, Absolute) + 0x95, 0x03, // Report Count (3) + 0x91, 0x01, // Output (Constant) + 0xC0 // End Collection + } +}; + +static bool udi_hid_kbd_setreport(void); + +static void udi_hid_kbd_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep); + +static void udi_hid_kbd_setreport_valid(void); + +bool udi_hid_kbd_enable(void) +{ + // Initialize internal values + udi_hid_kbd_rate = 0; + udi_hid_kbd_protocol = 0; + udi_hid_kbd_b_report_trans_ongoing = false; + memset(udi_hid_kbd_report, 0, UDI_HID_KBD_REPORT_SIZE); + udi_hid_kbd_b_report_valid = false; + return UDI_HID_KBD_ENABLE_EXT(); +} + +void udi_hid_kbd_disable(void) +{ + UDI_HID_KBD_DISABLE_EXT(); +} + +bool udi_hid_kbd_setup(void) +{ + return udi_hid_setup(&udi_hid_kbd_rate, + &udi_hid_kbd_protocol, + (uint8_t *) &udi_hid_kbd_report_desc, + udi_hid_kbd_setreport); +} + +uint8_t udi_hid_kbd_getsetting(void) +{ + return 0; +} + +static bool udi_hid_kbd_setreport(void) +{ + if ((USB_HID_REPORT_TYPE_OUTPUT == (udd_g_ctrlreq.req.wValue >> 8)) + && (0 == (0xFF & udd_g_ctrlreq.req.wValue)) + && (1 == udd_g_ctrlreq.req.wLength)) { + // Report OUT type on report ID 0 from USB Host + udd_g_ctrlreq.payload = &udi_hid_kbd_report_set; + udd_g_ctrlreq.callback = udi_hid_kbd_setreport_valid; + udd_g_ctrlreq.payload_size = 1; + return true; + } + return false; +} + +bool udi_hid_kbd_send_report(void) +{ + if (!main_b_kbd_enable) { + return false; + } + + if (udi_hid_kbd_b_report_trans_ongoing) { + return false; + } + + memcpy(udi_hid_kbd_report_trans, udi_hid_kbd_report, UDI_HID_KBD_REPORT_SIZE); + udi_hid_kbd_b_report_valid = false; + udi_hid_kbd_b_report_trans_ongoing = + udd_ep_run(UDI_HID_KBD_EP_IN | USB_EP_DIR_IN, + false, + udi_hid_kbd_report_trans, + UDI_HID_KBD_REPORT_SIZE, + udi_hid_kbd_report_sent); + + return udi_hid_kbd_b_report_trans_ongoing; +} + +static void udi_hid_kbd_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep) +{ + UNUSED(status); + UNUSED(nb_sent); + UNUSED(ep); + udi_hid_kbd_b_report_trans_ongoing = false; + if (udi_hid_kbd_b_report_valid) { + udi_hid_kbd_send_report(); + } +} + +static void udi_hid_kbd_setreport_valid(void) +{ + //UDI_HID_KBD_CHANGE_LED(udi_hid_kbd_report_set); +} + +#endif //KBD + +//******************************************************************************************** +// NKRO Keyboard +//******************************************************************************************** +#ifdef NKRO + +bool udi_hid_nkro_enable(void); +void udi_hid_nkro_disable(void); +bool udi_hid_nkro_setup(void); +uint8_t udi_hid_nkro_getsetting(void); + +UDC_DESC_STORAGE udi_api_t udi_api_hid_nkro = { + .enable = (bool(*)(void))udi_hid_nkro_enable, + .disable = (void (*)(void))udi_hid_nkro_disable, + .setup = (bool(*)(void))udi_hid_nkro_setup, + .getsetting = (uint8_t(*)(void))udi_hid_nkro_getsetting, + .sof_notify = NULL, +}; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_nkro_rate; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_nkro_protocol; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_nkro_report_set; + +bool udi_hid_nkro_b_report_valid; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_nkro_report[UDI_HID_NKRO_REPORT_SIZE]; + +volatile bool udi_hid_nkro_b_report_trans_ongoing; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_nkro_report_trans[UDI_HID_NKRO_REPORT_SIZE]; + +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE udi_hid_nkro_report_desc_t udi_hid_nkro_report_desc = { + { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application) - Keyboard, + + //Mods + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x81, 0x02, // Input (Data, Variable, Absolute), + + //LED Report + 0x75, 0x01, // Report Size (1), + 0x95, 0x05, // Report Count (5), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), + + //LED Report Padding + 0x75, 0x03, // Report Size (3), + 0x95, 0x01, // Report Count (1), + 0x91, 0x03, // Output (Constant), + + //Main keys + 0x75, 0x01, // Report Size (1), + 0x95, 0xF8, // Report Count (248), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, 0xF7, // Usage Maximum (247), + 0x81, 0x02, // Input (Data, Variable, Absolute, Bitfield), + 0xc0, // End Collection - Keyboard + } +}; + +static bool udi_hid_nkro_setreport(void); +static void udi_hid_nkro_setreport_valid(void); +static void udi_hid_nkro_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep); + +bool udi_hid_nkro_enable(void) +{ + // Initialize internal values + udi_hid_nkro_rate = 0; + udi_hid_nkro_protocol = 0; + udi_hid_nkro_b_report_trans_ongoing = false; + memset(udi_hid_nkro_report, 0, UDI_HID_NKRO_REPORT_SIZE); + udi_hid_nkro_b_report_valid = false; + return UDI_HID_NKRO_ENABLE_EXT(); +} + +void udi_hid_nkro_disable(void) +{ + UDI_HID_NKRO_DISABLE_EXT(); +} + +bool udi_hid_nkro_setup(void) +{ + return udi_hid_setup(&udi_hid_nkro_rate, + &udi_hid_nkro_protocol, + (uint8_t *) &udi_hid_nkro_report_desc, + udi_hid_nkro_setreport); +} + +uint8_t udi_hid_nkro_getsetting(void) +{ + return 0; +} + +//keyboard receives LED report here +static bool udi_hid_nkro_setreport(void) +{ + if ((USB_HID_REPORT_TYPE_OUTPUT == (udd_g_ctrlreq.req.wValue >> 8)) + && (0 == (0xFF & udd_g_ctrlreq.req.wValue)) + && (1 == udd_g_ctrlreq.req.wLength)) { + // Report OUT type on report ID 0 from USB Host + udd_g_ctrlreq.payload = &udi_hid_nkro_report_set; + udd_g_ctrlreq.callback = udi_hid_nkro_setreport_valid; //must call routine to transform setreport to LED state + udd_g_ctrlreq.payload_size = 1; + return true; + } + return false; +} + +bool udi_hid_nkro_send_report(void) +{ + if (!main_b_nkro_enable) { + return false; + } + + if (udi_hid_nkro_b_report_trans_ongoing) { + return false; + } + + memcpy(udi_hid_nkro_report_trans, udi_hid_nkro_report, UDI_HID_NKRO_REPORT_SIZE); + udi_hid_nkro_b_report_valid = false; + udi_hid_nkro_b_report_trans_ongoing = + udd_ep_run(UDI_HID_NKRO_EP_IN | USB_EP_DIR_IN, + false, + udi_hid_nkro_report_trans, + UDI_HID_NKRO_REPORT_SIZE, + udi_hid_nkro_report_sent); + + return udi_hid_nkro_b_report_trans_ongoing; +} + +static void udi_hid_nkro_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep) +{ + UNUSED(status); + UNUSED(nb_sent); + UNUSED(ep); + udi_hid_nkro_b_report_trans_ongoing = false; + if (udi_hid_nkro_b_report_valid) { + udi_hid_nkro_send_report(); + } +} + +static void udi_hid_nkro_setreport_valid(void) +{ + //UDI_HID_NKRO_CHANGE_LED(udi_hid_nkro_report_set); +} + +#endif //NKRO + +//******************************************************************************************** +// EXK (extra-keys) SYS-CTRL Keyboard +//******************************************************************************************** +#ifdef EXK + +bool udi_hid_exk_enable(void); +void udi_hid_exk_disable(void); +bool udi_hid_exk_setup(void); +uint8_t udi_hid_exk_getsetting(void); + +UDC_DESC_STORAGE udi_api_t udi_api_hid_exk = { + .enable = (bool(*)(void))udi_hid_exk_enable, + .disable = (void (*)(void))udi_hid_exk_disable, + .setup = (bool(*)(void))udi_hid_exk_setup, + .getsetting = (uint8_t(*)(void))udi_hid_exk_getsetting, + .sof_notify = NULL, +}; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_exk_rate; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_exk_protocol; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_exk_report_set; + +bool udi_hid_exk_b_report_valid; + +COMPILER_WORD_ALIGNED +udi_hid_exk_report_t udi_hid_exk_report; + +static bool udi_hid_exk_b_report_trans_ongoing; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_exk_report_trans[UDI_HID_EXK_REPORT_SIZE]; + +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE udi_hid_exk_report_desc_t udi_hid_exk_report_desc = { + { + // System Control Collection (8 bits) + + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x80, // Usage (System Control), + 0xA1, 0x01, // Collection (Application), + 0x85, REPORT_ID_SYSTEM, // Report ID (2) (System), + 0x16, 0x01, 0x00, // Logical Minimum (1), + 0x26, 0x03, 0x00, // Logical Maximum (3), + 0x1A, 0x81, 0x00, // Usage Minimum (81) (System Power Down), + 0x2A, 0x83, 0x00, // Usage Maximum (83) (System Wake Up), + 0x75, 0x10, // Report Size (16), + 0x95, 0x01, // Report Count (1), + 0x81, 0x00, // Input (Data, Array), + 0xC0, // End Collection - System Control + + // Consumer Control Collection - Media Keys (16 bits) + + 0x05, 0x0C, // Usage Page (Consumer), + 0x09, 0x01, // Usage (Consumer Control), + 0xA1, 0x01, // Collection (Application), + 0x85, REPORT_ID_CONSUMER, // Report ID (3) (Consumer), + 0x16, 0x01, 0x00, // Logical Minimum (1), + 0x26, 0x9C, 0x02, // Logical Maximum (668), + 0x1A, 0x01, 0x00, // Usage Minimum (1), + 0x2A, 0x9C, 0x02, // Usage Maximum (668), + 0x75, 0x10, // Report Size (16), + 0x95, 0x01, // Report Count (1), + 0x81, 0x00, // Input (Data, Array), + 0xC0, // End Collection - Consumer Control + } +}; + +static bool udi_hid_exk_setreport(void); + +static void udi_hid_exk_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep); + +static void udi_hid_exk_setreport_valid(void); + +bool udi_hid_exk_enable(void) +{ + // Initialize internal values + udi_hid_exk_rate = 0; + udi_hid_exk_protocol = 0; + udi_hid_exk_b_report_trans_ongoing = false; + memset(udi_hid_exk_report.raw, 0, UDI_HID_EXK_REPORT_SIZE); + udi_hid_exk_b_report_valid = false; + return UDI_HID_EXK_ENABLE_EXT(); +} + +void udi_hid_exk_disable(void) +{ + UDI_HID_EXK_DISABLE_EXT(); +} + +bool udi_hid_exk_setup(void) +{ + return udi_hid_setup(&udi_hid_exk_rate, + &udi_hid_exk_protocol, + (uint8_t *) &udi_hid_exk_report_desc, + udi_hid_exk_setreport); +} + +uint8_t udi_hid_exk_getsetting(void) +{ + return 0; +} + +static bool udi_hid_exk_setreport(void) +{ + if ((USB_HID_REPORT_TYPE_OUTPUT == (udd_g_ctrlreq.req.wValue >> 8)) + && (0 == (0xFF & udd_g_ctrlreq.req.wValue)) + && (1 == udd_g_ctrlreq.req.wLength)) { + // Report OUT type on report ID 0 from USB Host + udd_g_ctrlreq.payload = &udi_hid_exk_report_set; + udd_g_ctrlreq.callback = udi_hid_exk_setreport_valid; + udd_g_ctrlreq.payload_size = 1; + return true; + } + return false; +} + +bool udi_hid_exk_send_report(void) +{ + if (!main_b_exk_enable) { + return false; + } + + if (udi_hid_exk_b_report_trans_ongoing) { + return false; + } + + memcpy(udi_hid_exk_report_trans, udi_hid_exk_report.raw, UDI_HID_EXK_REPORT_SIZE); + udi_hid_exk_b_report_valid = false; + udi_hid_exk_b_report_trans_ongoing = + udd_ep_run(UDI_HID_EXK_EP_IN | USB_EP_DIR_IN, + false, + udi_hid_exk_report_trans, + UDI_HID_EXK_REPORT_SIZE, + udi_hid_exk_report_sent); + + return udi_hid_exk_b_report_trans_ongoing; +} + +static void udi_hid_exk_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep) +{ + UNUSED(status); + UNUSED(nb_sent); + UNUSED(ep); + udi_hid_exk_b_report_trans_ongoing = false; + if (udi_hid_exk_b_report_valid) { + udi_hid_exk_send_report(); + } +} + +static void udi_hid_exk_setreport_valid(void) +{ + +} + +#endif //EXK + +//******************************************************************************************** +// MOU Mouse +//******************************************************************************************** +#ifdef MOU + +bool udi_hid_mou_enable(void); +void udi_hid_mou_disable(void); +bool udi_hid_mou_setup(void); +uint8_t udi_hid_mou_getsetting(void); + +UDC_DESC_STORAGE udi_api_t udi_api_hid_mou = { + .enable = (bool(*)(void))udi_hid_mou_enable, + .disable = (void (*)(void))udi_hid_mou_disable, + .setup = (bool(*)(void))udi_hid_mou_setup, + .getsetting = (uint8_t(*)(void))udi_hid_mou_getsetting, + .sof_notify = NULL, +}; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_mou_rate; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_mou_protocol; + +//COMPILER_WORD_ALIGNED +//uint8_t udi_hid_mou_report_set; //No set report + +bool udi_hid_mou_b_report_valid; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_mou_report[UDI_HID_MOU_REPORT_SIZE]; + +static bool udi_hid_mou_b_report_trans_ongoing; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_mou_report_trans[UDI_HID_MOU_REPORT_SIZE]; + +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE udi_hid_mou_report_desc_t udi_hid_mou_report_desc = { + { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x02, // Usage (Mouse), + 0xA1, 0x01, // Collection (Application), + 0x09, 0x01, // Usage (Pointer), + 0xA1, 0x00, // Collection (Physical), + 0x05, 0x09, // Usage Page (Buttons), + 0x19, 0x01, // Usage Minimum (01), + 0x29, 0x05, // Usage Maximun (05), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;5 button bits + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x81, 0x01, // Input (Constant), ;3 bit padding, + + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x30, // Usage (X), + 0x09, 0x31, // Usage (Y), + 0x15, 0x81, // Logical Minimum (-127), + 0x25, 0x7F, // Logical Maximum (127), + 0x95, 0x02, // Report Count (2), + 0x75, 0x08, // Report Size (8), + 0x81, 0x06, // Input (Data, Variable, Relative), ;2 position bytes (X & Y), + + 0x09, 0x38, // Usage (Wheel), + 0x15, 0x81, // Logical Minimum (-127), + 0x25, 0x7F, // Logical Maximum (127), + 0x95, 0x01, // Report Count (1), + 0x75, 0x08, // Report Size (8), + 0x81, 0x06, // Input (Data, Variable, Relative), + + 0x05, 0x0C, // Usage Page (Consumer), + 0x0A, 0x38, 0x02, // Usage (AC Pan (Horizontal wheel)), + 0x15, 0x81, // Logical Minimum (-127), + 0x25, 0x7F, // Logical Maximum (127), + 0x95, 0x01, // Report Count (1), + 0x75, 0x08, // Report Size (8), + 0x81, 0x06, // Input (Data, Variable, Relative), + + 0xC0, // End Collection, + 0xC0, // End Collection + } +}; + +static void udi_hid_mou_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep); + +bool udi_hid_mou_enable(void) +{ + // Initialize internal values + udi_hid_mou_rate = 0; + udi_hid_mou_protocol = 0; + udi_hid_mou_b_report_trans_ongoing = false; + memset(udi_hid_mou_report, 0, UDI_HID_MOU_REPORT_SIZE); + udi_hid_mou_b_report_valid = false; + return UDI_HID_MOU_ENABLE_EXT(); +} + +void udi_hid_mou_disable(void) +{ + UDI_HID_MOU_DISABLE_EXT(); +} + +bool udi_hid_mou_setup(void) +{ + return udi_hid_setup(&udi_hid_mou_rate, + &udi_hid_mou_protocol, + (uint8_t *) &udi_hid_mou_report_desc, + NULL); +} + +uint8_t udi_hid_mou_getsetting(void) +{ + return 0; +} + +bool udi_hid_mou_send_report(void) +{ + if (!main_b_mou_enable) { + return false; + } + + if (udi_hid_mou_b_report_trans_ongoing) { + return false; + } + + memcpy(udi_hid_mou_report_trans, udi_hid_mou_report, UDI_HID_MOU_REPORT_SIZE); + udi_hid_mou_b_report_valid = false; + udi_hid_mou_b_report_trans_ongoing = + udd_ep_run(UDI_HID_MOU_EP_IN | USB_EP_DIR_IN, + false, + udi_hid_mou_report_trans, + UDI_HID_MOU_REPORT_SIZE, + udi_hid_mou_report_sent); + + return udi_hid_mou_b_report_trans_ongoing; +} + +static void udi_hid_mou_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep) +{ + UNUSED(status); + UNUSED(nb_sent); + UNUSED(ep); + udi_hid_mou_b_report_trans_ongoing = false; + if (udi_hid_mou_b_report_valid) { + udi_hid_mou_send_report(); + } +} + +#endif //MOU + +//******************************************************************************************** +// RAW +//******************************************************************************************** +#ifdef RAW + +bool udi_hid_raw_enable(void); +void udi_hid_raw_disable(void); +bool udi_hid_raw_setup(void); +uint8_t udi_hid_raw_getsetting(void); + +UDC_DESC_STORAGE udi_api_t udi_api_hid_raw = { + .enable = (bool(*)(void))udi_hid_raw_enable, + .disable = (void (*)(void))udi_hid_raw_disable, + .setup = (bool(*)(void))udi_hid_raw_setup, + .getsetting = (uint8_t(*)(void))udi_hid_raw_getsetting, + .sof_notify = NULL, +}; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_raw_rate; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_raw_protocol; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_raw_report_set[UDI_HID_RAW_REPORT_SIZE]; + +static bool udi_hid_raw_b_report_valid; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_raw_report[UDI_HID_RAW_REPORT_SIZE]; + +static bool udi_hid_raw_b_report_trans_ongoing; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_raw_report_trans[UDI_HID_RAW_REPORT_SIZE]; + +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE udi_hid_raw_report_desc_t udi_hid_raw_report_desc = { + { + 0x06, // Usage Page (Vendor Defined) + 0xFF, 0xFF, + 0x0A, // Usage (Mouse) + 0xFF, 0xFF, + 0xA1, 0x01, // Collection (Application) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0xFF, // Logical Maximum (255) + 0x95, 0x40, // Report Count + 0x09, 0x01, // Usage (Input) + 0x81, 0x02, // Input (Data + 0x95, 0x40, // Report Count + 0x09, 0x02, // Usage (Output) + 0x91, 0x02, // Output (Data + 0xC0, // End Collection - Consumer Control + } +}; + +static bool udi_hid_raw_setreport(void); +static void udi_hid_raw_setreport_valid(void); + +static void udi_hid_raw_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep); + +bool udi_hid_raw_enable(void) +{ + // Initialize internal values + udi_hid_raw_rate = 0; + udi_hid_raw_protocol = 0; + udi_hid_raw_b_report_trans_ongoing = false; + memset(udi_hid_raw_report, 0, UDI_HID_RAW_REPORT_SIZE); + udi_hid_raw_b_report_valid = false; + return UDI_HID_RAW_ENABLE_EXT(); +} + +void udi_hid_raw_disable(void) +{ + UDI_HID_RAW_DISABLE_EXT(); +} + +bool udi_hid_raw_setup(void) +{ + return udi_hid_setup(&udi_hid_raw_rate, + &udi_hid_raw_protocol, + (uint8_t *) &udi_hid_raw_report_desc, + udi_hid_raw_setreport); +} + +uint8_t udi_hid_raw_getsetting(void) +{ + return 0; +} + +static bool udi_hid_raw_setreport(void) +{ + if ((USB_HID_REPORT_TYPE_OUTPUT == (udd_g_ctrlreq.req.wValue >> 8)) + && (0 == (0xFF & udd_g_ctrlreq.req.wValue)) + && (UDI_HID_RAW_REPORT_SIZE == udd_g_ctrlreq.req.wLength)) { + // Report OUT type on report ID 0 from USB Host + udd_g_ctrlreq.payload = udi_hid_raw_report_set; + udd_g_ctrlreq.callback = udi_hid_raw_setreport_valid; //must call routine to transform setreport to LED state + udd_g_ctrlreq.payload_size = UDI_HID_RAW_REPORT_SIZE; + return true; + } + return false; +} + +bool udi_hid_raw_send_report(void) +{ + if (!main_b_raw_enable) { + return false; + } + + if (udi_hid_raw_b_report_trans_ongoing) { + return false; + } + + memcpy(udi_hid_raw_report_trans, udi_hid_raw_report,UDI_HID_RAW_REPORT_SIZE); + udi_hid_raw_b_report_valid = false; + udi_hid_raw_b_report_trans_ongoing = + udd_ep_run(UDI_HID_RAW_EP_IN | USB_EP_DIR_IN, + false, + udi_hid_raw_report_trans, + UDI_HID_RAW_REPORT_SIZE, + udi_hid_raw_report_sent); + + return udi_hid_raw_b_report_trans_ongoing; +} + +static void udi_hid_raw_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep) +{ + UNUSED(status); + UNUSED(nb_sent); + UNUSED(ep); + udi_hid_raw_b_report_trans_ongoing = false; + if (udi_hid_raw_b_report_valid) { + udi_hid_raw_send_report(); + } +} + +static void udi_hid_raw_setreport_valid(void) +{ + +} + +#endif //RAW + +//******************************************************************************************** +// CON +//******************************************************************************************** +#ifdef CON + +bool udi_hid_con_enable(void); +void udi_hid_con_disable(void); +bool udi_hid_con_setup(void); +uint8_t udi_hid_con_getsetting(void); + +UDC_DESC_STORAGE udi_api_t udi_api_hid_con = { + .enable = (bool(*)(void))udi_hid_con_enable, + .disable = (void (*)(void))udi_hid_con_disable, + .setup = (bool(*)(void))udi_hid_con_setup, + .getsetting = (uint8_t(*)(void))udi_hid_con_getsetting, + .sof_notify = NULL, +}; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_con_rate; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_con_protocol; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_con_report_set[UDI_HID_CON_REPORT_SIZE]; + +bool udi_hid_con_b_report_valid; + +COMPILER_WORD_ALIGNED +uint8_t udi_hid_con_report[UDI_HID_CON_REPORT_SIZE]; + +volatile bool udi_hid_con_b_report_trans_ongoing; + +COMPILER_WORD_ALIGNED +static uint8_t udi_hid_con_report_trans[UDI_HID_CON_REPORT_SIZE]; + +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE udi_hid_con_report_desc_t udi_hid_con_report_desc = { + { + 0x06, 0x31, 0xFF, // Vendor Page (PJRC Teensy compatible) + 0x09, 0x74, // Vendor Usage (PJRC Teensy compatible) + 0xA1, 0x01, // Collection (Application) + 0x09, 0x75, // Usage (Vendor) + 0x15, 0x00, // Logical Minimum (0x00) + 0x26, 0xFF, 0x00, // Logical Maximum (0x00FF) + 0x95, CONSOLE_EPSIZE, // Report Count + 0x75, 0x08, // Report Size (8) + 0x81, 0x02, // Input (Data) + 0x09, 0x76, // Usage (Vendor) + 0x15, 0x00, // Logical Minimum (0x00) + 0x26, 0xFF, 0x00, // Logical Maximum (0x00FF) + 0x95, CONSOLE_EPSIZE, // Report Count + 0x75, 0x08, // Report Size (8) + 0x91, 0x02, // Output (Data) + 0xC0, // End Collection + } +}; + +static bool udi_hid_con_setreport(void); +static void udi_hid_con_setreport_valid(void); + +static void udi_hid_con_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep); + +bool udi_hid_con_enable(void) +{ + // Initialize internal values + udi_hid_con_rate = 0; + udi_hid_con_protocol = 0; + udi_hid_con_b_report_trans_ongoing = false; + memset(udi_hid_con_report, 0, UDI_HID_CON_REPORT_SIZE); + udi_hid_con_b_report_valid = false; + return UDI_HID_CON_ENABLE_EXT(); +} + +void udi_hid_con_disable(void) +{ + UDI_HID_CON_DISABLE_EXT(); +} + +bool udi_hid_con_setup(void) +{ + return udi_hid_setup(&udi_hid_con_rate, + &udi_hid_con_protocol, + (uint8_t *) &udi_hid_con_report_desc, + udi_hid_con_setreport); +} + +uint8_t udi_hid_con_getsetting(void) +{ + return 0; +} + +static bool udi_hid_con_setreport(void) +{ + if ((USB_HID_REPORT_TYPE_OUTPUT == (udd_g_ctrlreq.req.wValue >> 8)) + && (0 == (0xFF & udd_g_ctrlreq.req.wValue)) + && (UDI_HID_CON_REPORT_SIZE == udd_g_ctrlreq.req.wLength)) { + udd_g_ctrlreq.payload = udi_hid_con_report_set; + udd_g_ctrlreq.callback = udi_hid_con_setreport_valid; + udd_g_ctrlreq.payload_size = UDI_HID_CON_REPORT_SIZE; + return true; + } + return false; +} + +bool udi_hid_con_send_report(void) +{ + if (!main_b_con_enable) { + return false; + } + + if (udi_hid_con_b_report_trans_ongoing) { + return false; + } + + memcpy(udi_hid_con_report_trans, udi_hid_con_report,UDI_HID_CON_REPORT_SIZE); + udi_hid_con_b_report_valid = false; + udi_hid_con_b_report_trans_ongoing = + udd_ep_run(UDI_HID_CON_EP_IN | USB_EP_DIR_IN, + false, + udi_hid_con_report_trans, + UDI_HID_CON_REPORT_SIZE, + udi_hid_con_report_sent); + + return udi_hid_con_b_report_trans_ongoing; +} + +static void udi_hid_con_report_sent(udd_ep_status_t status, iram_size_t nb_sent, udd_ep_id_t ep) +{ + UNUSED(status); + UNUSED(nb_sent); + UNUSED(ep); + udi_hid_con_b_report_trans_ongoing = false; + if (udi_hid_con_b_report_valid) { + udi_hid_con_send_report(); + } +} + +static void udi_hid_con_setreport_valid(void) +{ + +} + +#endif //CON diff --git a/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.h b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.h new file mode 100644 index 0000000000..e442919a9b --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd.h @@ -0,0 +1,122 @@ +/** + * \file + * + * \brief USB Device Human Interface Device (HID) keyboard interface. + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDC_HID_KBD_H_ +#define _UDC_HID_KBD_H_ + +#include "udc_desc.h" +#include "udi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//****************************************************************************** +// Keyboard interface definitions +//****************************************************************************** +#ifdef KBD +extern UDC_DESC_STORAGE udi_api_t udi_api_hid_kbd; +extern bool udi_hid_kbd_b_report_valid; +extern volatile bool udi_hid_kbd_b_report_trans_ongoing; +extern uint8_t udi_hid_kbd_report_set; +bool udi_hid_kbd_send_report(void); +#endif //KBD + +//******************************************************************************************** +// NKRO Keyboard +//******************************************************************************************** +#ifdef NKRO +extern UDC_DESC_STORAGE udi_api_t udi_api_hid_nkro; +extern bool udi_hid_nkro_b_report_valid; +extern volatile bool udi_hid_nkro_b_report_trans_ongoing; +bool udi_hid_nkro_send_report(void); +#endif //NKRO + +//******************************************************************************************** +// SYS-CTRL interface +//******************************************************************************************** +#ifdef EXK +extern UDC_DESC_STORAGE udi_api_t udi_api_hid_exk; +extern bool udi_hid_exk_b_report_valid; +extern uint8_t udi_hid_exk_report_set; +bool udi_hid_exk_send_report(void); +#endif //EXK + +//******************************************************************************************** +// CON Console +//******************************************************************************************** +#ifdef CON +extern UDC_DESC_STORAGE udi_api_t udi_api_hid_con; +extern bool udi_hid_con_b_report_valid; +extern uint8_t udi_hid_con_report_set[UDI_HID_CON_REPORT_SIZE]; +extern volatile bool udi_hid_con_b_report_trans_ongoing; +bool udi_hid_con_send_report(void); +#endif //CON + +//******************************************************************************************** +// MOU Mouse +//******************************************************************************************** +#ifdef MOU +extern UDC_DESC_STORAGE udi_api_t udi_api_hid_mou; +extern bool udi_hid_mou_b_report_valid; +bool udi_hid_mou_send_report(void); +#endif //MOU + +//******************************************************************************************** +// RAW Raw +//******************************************************************************************** +#ifdef RAW +extern UDC_DESC_STORAGE udi_api_t udi_api_hid_raw; +bool udi_hid_raw_send_report(void); +#endif //RAW + +//@} + +#ifdef __cplusplus +} +#endif + +#endif // _UDC_HID_KBD_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_conf.h b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_conf.h new file mode 100644 index 0000000000..db5db17ed5 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_conf.h @@ -0,0 +1,60 @@ +/** + * \file + * + * \brief Default HID keyboard configuration for a USB Device + * with a single interface HID keyboard + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UDI_HID_KBD_CONF_H_ +#define _UDI_HID_KBD_CONF_H_ + +/** + * \addtogroup udi_hid_keyboard_group_single_desc + * @{ + */ + +#include "udi_device_conf.h" + +#include "udi_hid_kbd.h" + +#endif // _UDI_HID_KBD_CONF_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_desc.c b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_desc.c new file mode 100644 index 0000000000..2d6e35e254 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/udi_hid_kbd_desc.c @@ -0,0 +1,185 @@ +/** + * \file + * + * \brief Default descriptors for a USB Device + * with a single interface HID keyboard + * + * Copyright (c) 2009-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#include "conf_usb.h" +#include "usb_protocol.h" +#include "udc_desc.h" +#include "udi_device_conf.h" +#include "udi_hid_kbd.h" +#include "udi_cdc.h" + +/** + * \ingroup udi_hid_keyboard_group + * \defgroup udi_hid_keyboard_group_single_desc USB device descriptors for a single interface + * + * The following structures provide the USB device descriptors required + * for USB Device with a single interface HID keyboard. + * + * It is ready to use and do not require more definition. + * @{ + */ + +//! USB Device Descriptor +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE usb_dev_desc_t udc_device_desc = { + .bLength = sizeof(usb_dev_desc_t), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = LE16(USB_V2_0), + .bDeviceClass = DEVICE_CLASS, + .bDeviceSubClass = DEVICE_SUBCLASS, + .bDeviceProtocol = DEVICE_PROTOCOL, + .bMaxPacketSize0 = USB_DEVICE_EP_CTRL_SIZE, + .idVendor = LE16(USB_DEVICE_VENDOR_ID), + .idProduct = LE16(USB_DEVICE_PRODUCT_ID), + .bcdDevice = LE16(USB_DEVICE_VERSION), +#ifdef USB_DEVICE_MANUFACTURE_NAME + .iManufacturer = 1, +#else + .iManufacturer = 0, // No manufacture string +#endif +#ifdef USB_DEVICE_PRODUCT_NAME + .iProduct = 2, +#else + .iProduct = 0, // No product string +#endif +#if (defined USB_DEVICE_SERIAL_NAME || defined USB_DEVICE_GET_SERIAL_NAME_POINTER) + .iSerialNumber = 3, +#else + .iSerialNumber = 0, // No serial string +#endif + .bNumConfigurations = 1 +}; + +#if 0 +#ifdef USB_DEVICE_HS_SUPPORT +//! USB Device Qualifier Descriptor for HS +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE usb_dev_qual_desc_t udc_device_qual = { + .bLength = sizeof(usb_dev_qual_desc_t), + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + .bcdUSB = LE16(USB_V2_0), + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = USB_DEVICE_EP_CTRL_SIZE, + .bNumConfigurations = 1 +}; +#endif +#endif + +//! USB Device Configuration Descriptor filled for FS and HS +COMPILER_WORD_ALIGNED +UDC_DESC_STORAGE udc_desc_t udc_desc = { + .conf.bLength = sizeof(usb_conf_desc_t), + .conf.bDescriptorType = USB_DT_CONFIGURATION, + .conf.wTotalLength = LE16(sizeof(udc_desc_t)), + .conf.bNumInterfaces = USB_DEVICE_NB_INTERFACE, + .conf.bConfigurationValue = 1, + .conf.iConfiguration = 0, + .conf.bmAttributes = /* USB_CONFIG_ATTR_MUST_SET | */ USB_DEVICE_ATTR, + .conf.bMaxPower = USB_CONFIG_MAX_POWER(USB_DEVICE_POWER), +#ifdef KBD + .hid_kbd = UDI_HID_KBD_DESC, +#endif +#ifdef RAW + .hid_raw = UDI_HID_RAW_DESC, +#endif +#ifdef MOU + .hid_mou = UDI_HID_MOU_DESC, +#endif +#ifdef EXK + .hid_exk = UDI_HID_EXK_DESC, +#endif +#ifdef CON + .hid_con = UDI_HID_CON_DESC, +#endif +#ifdef NKRO + .hid_nkro = UDI_HID_NKRO_DESC, +#endif +#ifdef CDC + .cdc_serial = CDC_DESCRIPTOR, +#endif +}; + +UDC_DESC_STORAGE udi_api_t *udi_apis[USB_DEVICE_NB_INTERFACE] = { + #ifdef KBD + &udi_api_hid_kbd, + #endif + #ifdef RAW + &udi_api_hid_raw, + #endif + #ifdef MOU + &udi_api_hid_mou, + #endif + #ifdef EXK + &udi_api_hid_exk, + #endif + #ifdef CON + &udi_api_hid_con, + #endif + #ifdef NKRO + &udi_api_hid_nkro, + #endif + #ifdef CDC + &udi_api_cdc_comm, &udi_api_cdc_data, + #endif +}; + +//! Add UDI with USB Descriptors FS & HS +UDC_DESC_STORAGE udc_config_speed_t udc_config_fshs[1] = {{ + .desc = (usb_conf_desc_t UDC_DESC_STORAGE*)&udc_desc, + .udi_apis = udi_apis, +}}; + +//! Add all information about USB Device in global structure for UDC +UDC_DESC_STORAGE udc_config_t udc_config = { + .confdev_lsfs = &udc_device_desc, + .conf_lsfs = udc_config_fshs, +}; + +//@} +//@} diff --git a/tmk_core/protocol/arm_atsam/usb/ui.c b/tmk_core/protocol/arm_atsam/usb/ui.c new file mode 100644 index 0000000000..70a6191098 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/ui.c @@ -0,0 +1,104 @@ +/** + * \file + * + * \brief User Interface + * + * Copyright (c) 2014-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef ARM_MATH_CM4 + #define ARM_MATH_CM4 +#endif + +#undef LITTLE_ENDIAN //redefined in samd51j18a.h +#include "samd51j18a.h" +#include "ui.h" + +//! Sequence process running each \c SEQUENCE_PERIOD ms +#define SEQUENCE_PERIOD 150 + +#if 0 +/* Interrupt on "pin change" from push button to do wakeup on USB + * Note: + * This interrupt is enable when the USB host enable remote wakeup feature + * This interrupt wakeup the CPU if this one is in idle mode + */ +static void ui_wakeup_handler(void) +{ + /* It is a wakeup then send wakeup USB */ + udc_remotewakeup(); +} +#endif + +void ui_init(void) +{ + +} + +void ui_powerdown(void) +{ + +} + +void ui_wakeup_enable(void) +{ + +} + +void ui_wakeup_disable(void) +{ + +} + +void ui_wakeup(void) +{ + +} + +void ui_process(uint16_t framenumber) +{ + +} + +void ui_kbd_led(uint8_t value) +{ + +} diff --git a/tmk_core/protocol/arm_atsam/usb/ui.h b/tmk_core/protocol/arm_atsam/usb/ui.h new file mode 100644 index 0000000000..d1c767d457 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/ui.h @@ -0,0 +1,76 @@ +/** + * \file + * + * \brief Common User Interface for HID Keyboard application + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _UI_H_ +#define _UI_H_ + +//! \brief Initializes the user interface +void ui_init(void); + +//! \brief Enters the user interface in power down mode +void ui_powerdown(void); + +//! \brief Enables the asynchronous interrupts of the user interface +void ui_wakeup_enable(void); + +//! \brief Disables the asynchronous interrupts of the user interface +void ui_wakeup_disable(void); + +//! \brief Exits the user interface of power down mode +void ui_wakeup(void); + +/*! \brief This process is called each 1ms + * It is called only if the USB interface is enabled. + * + * \param framenumber Current frame number + */ +void ui_process(uint16_t framenumber); + +/*! \brief Turn on or off the keyboard LEDs + */ +void ui_kbd_led(uint8_t value); + +#endif // _UI_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/usb.c b/tmk_core/protocol/arm_atsam/usb/usb.c new file mode 100644 index 0000000000..d30d76dd11 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb.c @@ -0,0 +1,1144 @@ +/** + * \file + * + * \brief SAM USB Driver. + * + * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#define DEVICE_MODE_ONLY true +#define SAMD11 DEVICE_MODE_ONLY + +#ifndef ARM_MATH_CM4 + #define ARM_MATH_CM4 +#endif + +#include "compiler.h" +#undef LITTLE_ENDIAN //redefined in samd51j18a.h +#include "samd51j18a.h" +#include <stdbool.h> +#include <string.h> +#include "arm_math.h" +#include "status_codes.h" +#include "usb.h" + +/** Fields definition from a LPM TOKEN */ +#define USB_LPM_ATTRIBUT_BLINKSTATE_MASK (0xF << 0) +#define USB_LPM_ATTRIBUT_HIRD_MASK (0xF << 4) +#define USB_LPM_ATTRIBUT_REMOTEWAKE_MASK (1 << 8) +#define USB_LPM_ATTRIBUT_BLINKSTATE(value) ((value & 0xF) << 0) +#define USB_LPM_ATTRIBUT_HIRD(value) ((value & 0xF) << 4) +#define USB_LPM_ATTRIBUT_REMOTEWAKE(value) ((value & 1) << 8) +#define USB_LPM_ATTRIBUT_BLINKSTATE_L1 USB_LPM_ATTRIBUT_BLINKSTATE(1) + +/** + * \brief Mask selecting the index part of an endpoint address + */ +#define USB_EP_ADDR_MASK 0x0f + +/** + * \brief Endpoint transfer direction is IN + */ +#define USB_EP_DIR_IN 0x80 + +/** + * \brief Endpoint transfer direction is OUT + */ +#define USB_EP_DIR_OUT 0x00 + +/** + * \name USB SRAM data containing pipe descriptor table + * The content of the USB SRAM can be : + * - modified by USB hardware interface to update pipe status. + * Thereby, it is read by software. + * - modified by USB software to control pipe. + * Thereby, it is read by hardware. + * This data section is volatile. + * + * @{ + */ +COMPILER_PACK_SET(1) +COMPILER_WORD_ALIGNED +union { + UsbDeviceDescriptor usb_endpoint_table[USB_EPT_NUM]; +} usb_descriptor_table; +COMPILER_PACK_RESET() +/** @} */ + +/** + * \brief Local USB module instance + */ +static struct usb_module *_usb_instances; + +/* Device LPM callback variable */ +static uint32_t device_callback_lpm_wakeup_enable; + +/** + * \brief Device endpoint callback parameter variable, used to transfer info to UDD wrapper layer + */ +static struct usb_endpoint_callback_parameter ep_callback_para; + +/** + * \internal USB Device IRQ Mask Bits Map + */ +static const uint16_t _usb_device_irq_bits[USB_DEVICE_CALLBACK_N] = { + USB_DEVICE_INTFLAG_SOF, + USB_DEVICE_INTFLAG_EORST, + USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_EORSM | USB_DEVICE_INTFLAG_UPRSM, + USB_DEVICE_INTFLAG_RAMACER, + USB_DEVICE_INTFLAG_SUSPEND, + USB_DEVICE_INTFLAG_LPMNYET, + USB_DEVICE_INTFLAG_LPMSUSP, +}; + +/** + * \internal USB Device IRQ Mask Bits Map + */ +static const uint8_t _usb_endpoint_irq_bits[USB_DEVICE_EP_CALLBACK_N] = { + USB_DEVICE_EPINTFLAG_TRCPT_Msk, + USB_DEVICE_EPINTFLAG_TRFAIL_Msk, + USB_DEVICE_EPINTFLAG_RXSTP, + USB_DEVICE_EPINTFLAG_STALL_Msk +}; + +/** + * \brief Registers a USB device callback + * + * Registers a callback function which is implemented by the user. + * + * \note The callback must be enabled by \ref usb_device_enable_callback, + * in order for the interrupt handler to call it when the conditions for the + * callback type is met. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] callback_type Callback type given by an enum + * \param[in] callback_func Pointer to callback function + * + * \return Status of the registration operation. + * \retval STATUS_OK The callback was registered successfully. + */ +enum status_code usb_device_register_callback(struct usb_module *module_inst, + enum usb_device_callback callback_type, + usb_device_callback_t callback_func) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(callback_func); + + /* Register callback function */ + module_inst->device_callback[callback_type] = callback_func; + + /* Set the bit corresponding to the callback_type */ + module_inst->device_registered_callback_mask |= _usb_device_irq_bits[callback_type]; + + return STATUS_OK; +} + +/** + * \brief Unregisters a USB device callback + * + * Unregisters an asynchronous callback implemented by the user. Removing it + * from the internal callback registration table. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] callback_type Callback type given by an enum + * + * \return Status of the de-registration operation. + * \retval STATUS_OK The callback was unregistered successfully. + */ +enum status_code usb_device_unregister_callback(struct usb_module *module_inst, + enum usb_device_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module_inst); + + /* Unregister callback function */ + module_inst->device_callback[callback_type] = NULL; + + /* Clear the bit corresponding to the callback_type */ + module_inst->device_registered_callback_mask &= ~_usb_device_irq_bits[callback_type]; + + return STATUS_OK; +} + +/** + * \brief Enables USB device callback generation for a given type. + * + * Enables asynchronous callbacks for a given logical type. + * This must be called before USB device generate callback events. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] callback_type Callback type given by an enum + * + * \return Status of the callback enable operation. + * \retval STATUS_OK The callback was enabled successfully. + */ +enum status_code usb_device_enable_callback(struct usb_module *module_inst, + enum usb_device_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* clear related flag */ + module_inst->hw->DEVICE.INTFLAG.reg = _usb_device_irq_bits[callback_type]; + + /* Enable callback */ + module_inst->device_enabled_callback_mask |= _usb_device_irq_bits[callback_type]; + + module_inst->hw->DEVICE.INTENSET.reg = _usb_device_irq_bits[callback_type]; + + return STATUS_OK; +} + +/** + * \brief Disables USB device callback generation for a given type. + * + * Disables asynchronous callbacks for a given logical type. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] callback_type Callback type given by an enum + * + * \return Status of the callback disable operation. + * \retval STATUS_OK The callback was disabled successfully. + */ +enum status_code usb_device_disable_callback(struct usb_module *module_inst, + enum usb_device_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* Disable callback */ + module_inst->device_enabled_callback_mask &= ~_usb_device_irq_bits[callback_type]; + + module_inst->hw->DEVICE.INTENCLR.reg = _usb_device_irq_bits[callback_type]; + + return STATUS_OK; +} + +/** + * \brief Registers a USB device endpoint callback + * + * Registers a callback function which is implemented by the user. + * + * \note The callback must be enabled by \ref usb_device_endpoint_enable_callback, + * in order for the interrupt handler to call it when the conditions for the + * callback type is met. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] ep_num Endpoint to configure + * \param[in] callback_type Callback type given by an enum + * \param[in] callback_func Pointer to callback function + * + * \return Status of the registration operation. + * \retval STATUS_OK The callback was registered successfully. + */ +enum status_code usb_device_endpoint_register_callback( + struct usb_module *module_inst, uint8_t ep_num, + enum usb_device_endpoint_callback callback_type, + usb_device_endpoint_callback_t callback_func) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(ep_num < USB_EPT_NUM); + Assert(callback_func); + + /* Register callback function */ + module_inst->device_endpoint_callback[ep_num][callback_type] = callback_func; + + /* Set the bit corresponding to the callback_type */ + module_inst->device_endpoint_registered_callback_mask[ep_num] |= _usb_endpoint_irq_bits[callback_type]; + + return STATUS_OK; +} + +/** + * \brief Unregisters a USB device endpoint callback + * + * Unregisters an callback implemented by the user. Removing it + * from the internal callback registration table. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] ep_num Endpoint to configure + * \param[in] callback_type Callback type given by an enum + * + * \return Status of the de-registration operation. + * \retval STATUS_OK The callback was unregistered successfully. + */ +enum status_code usb_device_endpoint_unregister_callback( + struct usb_module *module_inst, uint8_t ep_num, + enum usb_device_endpoint_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(ep_num < USB_EPT_NUM); + + /* Unregister callback function */ + module_inst->device_endpoint_callback[ep_num][callback_type] = NULL; + + /* Clear the bit corresponding to the callback_type */ + module_inst->device_endpoint_registered_callback_mask[ep_num] &= ~_usb_endpoint_irq_bits[callback_type]; + + return STATUS_OK; +} + +/** + * \brief Enables USB device endpoint callback generation for a given type. + * + * Enables callbacks for a given logical type. + * This must be called before USB device pipe generate callback events. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] ep Endpoint to configure + * \param[in] callback_type Callback type given by an enum + * + * \return Status of the callback enable operation. + * \retval STATUS_OK The callback was enabled successfully. + */ +enum status_code usb_device_endpoint_enable_callback( + struct usb_module *module_inst, uint8_t ep, + enum usb_device_endpoint_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + Assert(ep_num < USB_EPT_NUM); + + /* Enable callback */ + module_inst->device_endpoint_enabled_callback_mask[ep_num] |= _usb_endpoint_irq_bits[callback_type]; + + if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_TRCPT) { + if (ep_num == 0) { // control endpoint + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1; + } else if (ep & USB_EP_DIR_IN) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT1; + } else { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0; + } + } + + if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL) { + if (ep_num == 0) { // control endpoint + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRFAIL0 | USB_DEVICE_EPINTENSET_TRFAIL1; + } else if (ep & USB_EP_DIR_IN) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRFAIL1; + } else { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRFAIL0; + } + } + + if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_RXSTP) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_RXSTP; + } + + if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_STALL) { + if (ep & USB_EP_DIR_IN) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_STALL1; + } else { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_STALL0; + } + } + + return STATUS_OK; +} + +/** + * \brief Disables USB device endpoint callback generation for a given type. + * + * Disables callbacks for a given logical type. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] ep Endpoint to configure + * \param[in] callback_type Callback type given by an enum + * + * \return Status of the callback disable operation. + * \retval STATUS_OK The callback was disabled successfully. + */ +enum status_code usb_device_endpoint_disable_callback( + struct usb_module *module_inst, uint8_t ep, + enum usb_device_endpoint_callback callback_type) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + Assert(ep_num < USB_EPT_NUM); + + /* Enable callback */ + module_inst->device_endpoint_enabled_callback_mask[ep_num] &= ~_usb_endpoint_irq_bits[callback_type]; + + if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_TRCPT) { + if (ep_num == 0) { // control endpoint + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT0 | USB_DEVICE_EPINTENCLR_TRCPT1; + } else if (ep & USB_EP_DIR_IN) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT1; + } else { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT0; + } + } + + if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL) { + if (ep_num == 0) { // control endpoint + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRFAIL0 | USB_DEVICE_EPINTENCLR_TRFAIL1; + } else if (ep & USB_EP_DIR_IN) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRFAIL1; + } else { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRFAIL0; + } + } + + if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_RXSTP) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_RXSTP; + } + + if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_STALL) { + if (ep & USB_EP_DIR_IN) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_STALL1; + } else { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_STALL0; + } + } + + return STATUS_OK; +} + +/** + * \brief Initializes an USB device endpoint configuration structure to defaults. + * + * Initializes a given USB device endpoint configuration structure to a + * set of known default values. This function should be called on all new + * instances of these configuration structures before being modified by the + * user application. + * + * The default configuration is as follows: + * \li endpoint address is 0 + * \li endpoint size is 8 bytes + * \li auto_zlp is false + * \li endpoint type is control + * + * \param[out] ep_config Configuration structure to initialize to default values + */ +void usb_device_endpoint_get_config_defaults(struct usb_device_endpoint_config *ep_config) +{ + /* Sanity check arguments */ + Assert(ep_config); + + /* Write default config to config struct */ + ep_config->ep_address = 0; + ep_config->ep_size = USB_ENDPOINT_8_BYTE; + ep_config->auto_zlp = false; + ep_config->ep_type = USB_DEVICE_ENDPOINT_TYPE_CONTROL; +} + +/** + * \brief Writes an USB device endpoint configuration to the hardware module. + * + * Writes out a given configuration of an USB device endpoint + * configuration to the hardware module. If the pipe is already configured, + * the new configuration will replace the existing one. + * + * \param[in] module_inst Pointer to USB software instance struct + * \param[in] ep_config Configuration settings for the endpoint + * + * \return Status of the device endpoint configuration operation + * \retval STATUS_OK The device endpoint was configured successfully + * \retval STATUS_ERR_DENIED The endpoint address is already configured + */ +enum status_code usb_device_endpoint_set_config(struct usb_module *module_inst, + struct usb_device_endpoint_config *ep_config) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(ep_config); + + uint8_t ep_num = ep_config->ep_address & USB_EP_ADDR_MASK; + uint8_t ep_bank = (ep_config->ep_address & USB_EP_DIR_IN) ? 1 : 0; + + switch (ep_config->ep_type) { + case USB_DEVICE_ENDPOINT_TYPE_DISABLE: + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0) | USB_DEVICE_EPCFG_EPTYPE1(0); + return STATUS_OK; + + case USB_DEVICE_ENDPOINT_TYPE_CONTROL: + if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE0_Msk) == 0 && \ + (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE1_Msk) == 0) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(1) | USB_DEVICE_EPCFG_EPTYPE1(1); + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY; + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY; + } else { + return STATUS_ERR_DENIED; + } + if (true == ep_config->auto_zlp) { + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.reg |= USB_DEVICE_PCKSIZE_AUTO_ZLP; + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.reg |= USB_DEVICE_PCKSIZE_AUTO_ZLP; + } else { + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.reg &= ~USB_DEVICE_PCKSIZE_AUTO_ZLP; + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.reg &= ~USB_DEVICE_PCKSIZE_AUTO_ZLP; + } + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.bit.SIZE = ep_config->ep_size; + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.SIZE = ep_config->ep_size; + return STATUS_OK; + + case USB_DEVICE_ENDPOINT_TYPE_ISOCHRONOUS: + if (ep_bank) { + if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE1_Msk) == 0){ + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE1(2); + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY; + } else { + return STATUS_ERR_DENIED; + } + } else { + if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE0_Msk) == 0){ + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE0(2); + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY; + } else { + return STATUS_ERR_DENIED; + } + } + break; + + case USB_DEVICE_ENDPOINT_TYPE_BULK: + if (ep_bank) { + if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE1_Msk) == 0){ + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE1(3); + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY; + } else { + return STATUS_ERR_DENIED; + } + } else { + if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE0_Msk) == 0){ + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE0(3); + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY; + } else { + return STATUS_ERR_DENIED; + } + } + break; + + case USB_DEVICE_ENDPOINT_TYPE_INTERRUPT: + if (ep_bank) { + if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE1_Msk) == 0){ + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE1(4); + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY; + } else { + return STATUS_ERR_DENIED; + } + } else { + if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE0_Msk) == 0){ + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE0(4); + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY; + } else { + return STATUS_ERR_DENIED; + } + } + break; + + default: + break; + } + + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[ep_bank].PCKSIZE.bit.SIZE = ep_config->ep_size; + + if (true == ep_config->auto_zlp) { + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[ep_bank].PCKSIZE.reg |= USB_DEVICE_PCKSIZE_AUTO_ZLP; + } else { + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[ep_bank].PCKSIZE.reg &= ~USB_DEVICE_PCKSIZE_AUTO_ZLP; + } + + return STATUS_OK; +} + +/** + * \brief Check if current endpoint is configured + * + * \param module_inst Pointer to USB software instance struct + * \param ep Endpoint address (direction & number) + * + * \return \c true if endpoint is configured and ready to use + */ +bool usb_device_endpoint_is_configured(struct usb_module *module_inst, uint8_t ep) +{ + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + uint8_t flag; + + if (ep & USB_EP_DIR_IN) { + flag = (uint8_t)(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.bit.EPTYPE1); + } else { + flag = (uint8_t)(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.bit.EPTYPE0); + } + return ((enum usb_device_endpoint_type)(flag) != USB_DEVICE_ENDPOINT_TYPE_DISABLE); +} + + +/** + * \brief Abort ongoing job on the endpoint + * + * \param module_inst Pointer to USB software instance struct + * \param ep Endpoint address + */ +void usb_device_endpoint_abort_job(struct usb_module *module_inst, uint8_t ep) +{ + uint8_t ep_num; + ep_num = ep & USB_EP_ADDR_MASK; + + // Stop transfer + if (ep & USB_EP_DIR_IN) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY; + // Eventually ack a transfer occur during abort + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1; + } else { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY; + // Eventually ack a transfer occur during abort + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0; + } +} + +/** + * \brief Check if endpoint is halted + * + * \param module_inst Pointer to USB software instance struct + * \param ep Endpoint address + * + * \return \c true if the endpoint is halted + */ +bool usb_device_endpoint_is_halted(struct usb_module *module_inst, uint8_t ep) +{ + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + + if (ep & USB_EP_DIR_IN) { + return (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUS.reg & USB_DEVICE_EPSTATUSSET_STALLRQ1); + } else { + return (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUS.reg & USB_DEVICE_EPSTATUSSET_STALLRQ0); + } +} + +/** + * \brief Halt the endpoint (send STALL) + * + * \param module_inst Pointer to USB software instance struct + * \param ep Endpoint address + */ +void usb_device_endpoint_set_halt(struct usb_module *module_inst, uint8_t ep) +{ + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + + // Stall endpoint + if (ep & USB_EP_DIR_IN) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1; + } else { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0; + } +} + +/** + * \brief Clear endpoint halt state + * + * \param module_inst Pointer to USB software instance struct + * \param ep Endpoint address + */ +void usb_device_endpoint_clear_halt(struct usb_module *module_inst, uint8_t ep) +{ + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + + if (ep & USB_EP_DIR_IN) { + if (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUS.reg & USB_DEVICE_EPSTATUSSET_STALLRQ1) { + // Remove stall request + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1; + if (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_STALL1) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL1; + // The Stall has occurred, then reset data toggle + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_DTGLIN; + } + } + } else { + if (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUS.reg & USB_DEVICE_EPSTATUSSET_STALLRQ0) { + // Remove stall request + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0; + if (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_STALL0) { + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL0; + // The Stall has occurred, then reset data toggle + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_DTGLOUT; + } + } + } +} + +/** + * \brief Start write buffer job on a endpoint + * + * \param module_inst Pointer to USB module instance + * \param ep_num Endpoint number + * \param pbuf Pointer to buffer + * \param buf_size Size of buffer + * + * \return Status of procedure + * \retval STATUS_OK Job started successfully + * \retval STATUS_ERR_DENIED Endpoint is not ready + */ +enum status_code usb_device_endpoint_write_buffer_job(struct usb_module *module_inst,uint8_t ep_num, + uint8_t* pbuf, uint32_t buf_size) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(ep_num < USB_EPT_NUM); + + uint8_t flag; + flag = (uint8_t)(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.bit.EPTYPE1); + if ((enum usb_device_endpoint_type)(flag) == USB_DEVICE_ENDPOINT_TYPE_DISABLE) { + return STATUS_ERR_DENIED; + }; + + /* get endpoint configuration from setting register */ + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].ADDR.reg = (uint32_t)pbuf; + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.MULTI_PACKET_SIZE = 0; + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.BYTE_COUNT = buf_size; + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK1RDY; + + return STATUS_OK; +} + +/** + * \brief Start read buffer job on a endpoint + * + * \param module_inst Pointer to USB module instance + * \param ep_num Endpoint number + * \param pbuf Pointer to buffer + * \param buf_size Size of buffer + * + * \return Status of procedure + * \retval STATUS_OK Job started successfully + * \retval STATUS_ERR_DENIED Endpoint is not ready + */ +enum status_code usb_device_endpoint_read_buffer_job(struct usb_module *module_inst,uint8_t ep_num, + uint8_t* pbuf, uint32_t buf_size) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(ep_num < USB_EPT_NUM); + + uint8_t flag; + flag = (uint8_t)(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.bit.EPTYPE0); + if ((enum usb_device_endpoint_type)(flag) == USB_DEVICE_ENDPOINT_TYPE_DISABLE) { + return STATUS_ERR_DENIED; + }; + + /* get endpoint configuration from setting register */ + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].ADDR.reg = (uint32_t)pbuf; + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = buf_size; + usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0; + module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY; + + return STATUS_OK; +} + +/** + * \brief Start setup packet read job on a endpoint + * + * \param module_inst Pointer to USB device module instance + * \param pbuf Pointer to buffer + * + * \return Status of procedure + * \retval STATUS_OK Job started successfully + * \retval STATUS_ERR_DENIED Endpoint is not ready + */ +enum status_code usb_device_endpoint_setup_buffer_job(struct usb_module *module_inst, + uint8_t* pbuf) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* get endpoint configuration from setting register */ + usb_descriptor_table.usb_endpoint_table[0].DeviceDescBank[0].ADDR.reg = (uint32_t)pbuf; + usb_descriptor_table.usb_endpoint_table[0].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 8; + usb_descriptor_table.usb_endpoint_table[0].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0; + module_inst->hw->DEVICE.DeviceEndpoint[0].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY; + + return STATUS_OK; +} + +static void _usb_device_interrupt_handler(void) +{ + uint16_t ep_inst; + uint16_t flags, flags_run; + ep_inst = _usb_instances->hw->DEVICE.EPINTSMRY.reg; + + /* device interrupt */ + if (0 == ep_inst) { + int i; + + /* get interrupt flags */ + flags = _usb_instances->hw->DEVICE.INTFLAG.reg; + flags_run = flags & + _usb_instances->device_enabled_callback_mask & + _usb_instances->device_registered_callback_mask; + + for (i = 0; i < USB_DEVICE_CALLBACK_N; i ++) { + if (flags & _usb_device_irq_bits[i]) { + _usb_instances->hw->DEVICE.INTFLAG.reg = + _usb_device_irq_bits[i]; + } + if (flags_run & _usb_device_irq_bits[i]) { + if (i == USB_DEVICE_CALLBACK_LPMSUSP) { + device_callback_lpm_wakeup_enable = + usb_descriptor_table.usb_endpoint_table[0].DeviceDescBank[0].EXTREG.bit.VARIABLE + & USB_LPM_ATTRIBUT_REMOTEWAKE_MASK; + } + (_usb_instances->device_callback[i])(_usb_instances, &device_callback_lpm_wakeup_enable); + } + } + + } else { + /* endpoint interrupt */ + + for (uint8_t i = 0; i < USB_EPT_NUM; i++) { + + if (ep_inst & (1 << i)) { + flags = _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg; + flags_run = flags & + _usb_instances->device_endpoint_enabled_callback_mask[i] & + _usb_instances->device_endpoint_registered_callback_mask[i]; + + // endpoint transfer stall interrupt + if (flags & USB_DEVICE_EPINTFLAG_STALL_Msk) { + if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_STALL1) { + _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL1; + ep_callback_para.endpoint_address = USB_EP_DIR_IN | i; + } else if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_STALL0) { + _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL0; + ep_callback_para.endpoint_address = USB_EP_DIR_OUT | i; + } + + if (flags_run & USB_DEVICE_EPINTFLAG_STALL_Msk) { + (_usb_instances->device_endpoint_callback[i][USB_DEVICE_ENDPOINT_CALLBACK_STALL])(_usb_instances,&ep_callback_para); + } + return; + } + + // endpoint received setup interrupt + if (flags & USB_DEVICE_EPINTFLAG_RXSTP) { + _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP; + if(_usb_instances->device_endpoint_enabled_callback_mask[i] & _usb_endpoint_irq_bits[USB_DEVICE_ENDPOINT_CALLBACK_RXSTP]) { + ep_callback_para.received_bytes = (uint16_t)(usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT); + (_usb_instances->device_endpoint_callback[i][USB_DEVICE_ENDPOINT_CALLBACK_RXSTP])(_usb_instances,&ep_callback_para); + } + return; + } + + // endpoint transfer complete interrupt + if (flags & USB_DEVICE_EPINTFLAG_TRCPT_Msk) { + if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRCPT1) { + _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1; + ep_callback_para.endpoint_address = USB_EP_DIR_IN | i; + ep_callback_para.sent_bytes = (uint16_t)(usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[1].PCKSIZE.bit.BYTE_COUNT); + + } else if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRCPT0) { + _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0; + ep_callback_para.endpoint_address = USB_EP_DIR_OUT | i; + ep_callback_para.received_bytes = (uint16_t)(usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT); + ep_callback_para.out_buffer_size = (uint16_t)(usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE); + } + if(flags_run & USB_DEVICE_EPINTFLAG_TRCPT_Msk) { + (_usb_instances->device_endpoint_callback[i][USB_DEVICE_ENDPOINT_CALLBACK_TRCPT])(_usb_instances,&ep_callback_para); + } + return; + } + + // endpoint transfer fail interrupt + if (flags & USB_DEVICE_EPINTFLAG_TRFAIL_Msk) { + if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRFAIL1) { + _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRFAIL1; + if (usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[1].STATUS_BK.reg & USB_DEVICE_STATUS_BK_ERRORFLOW) { + usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[1].STATUS_BK.reg &= ~USB_DEVICE_STATUS_BK_ERRORFLOW; + } + ep_callback_para.endpoint_address = USB_EP_DIR_IN | i; + if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRCPT1) { + return; + } + } else if(_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRFAIL0) { + _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRFAIL0; + if (usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].STATUS_BK.reg & USB_DEVICE_STATUS_BK_ERRORFLOW) { + usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].STATUS_BK.reg &= ~USB_DEVICE_STATUS_BK_ERRORFLOW; + } + ep_callback_para.endpoint_address = USB_EP_DIR_OUT | i; + if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRCPT0) { + return; + } + } + + if(flags_run & USB_DEVICE_EPINTFLAG_TRFAIL_Msk) { + (_usb_instances->device_endpoint_callback[i][USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL])(_usb_instances,&ep_callback_para); + } + return; + } + } + } + } +} + +/** + * \brief Enable the USB module peripheral + * + * \param module_inst pointer to USB module instance + */ +void usb_enable(struct usb_module *module_inst) +{ + Assert(module_inst); + Assert(module_inst->hw); + + module_inst->hw->DEVICE.CTRLA.reg |= USB_CTRLA_ENABLE; + while (module_inst->hw->DEVICE.SYNCBUSY.reg == USB_SYNCBUSY_ENABLE); +} + +/** + * \brief Disable the USB module peripheral + * + * \param module_inst pointer to USB module instance + */ +void usb_disable(struct usb_module *module_inst) +{ + Assert(module_inst); + Assert(module_inst->hw); + + module_inst->hw->DEVICE.INTENCLR.reg = USB_DEVICE_INTENCLR_MASK; + module_inst->hw->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_MASK; + module_inst->hw->DEVICE.CTRLA.reg &= ~USB_CTRLA_ENABLE; + while (module_inst->hw->DEVICE.SYNCBUSY.reg == USB_SYNCBUSY_ENABLE); +} + +/** + * \brief Interrupt handler for the USB module. + */ +void USB_0_Handler(void) +{ + if (_usb_instances->hw->DEVICE.CTRLA.bit.MODE) { + + } else { + /*device mode ISR */ + _usb_device_interrupt_handler(); + } +} + +void USB_1_Handler(void) +{ + _usb_device_interrupt_handler(); +} + +void USB_2_Handler(void) +{ + _usb_device_interrupt_handler(); +} + +void USB_3_Handler(void) +{ + _usb_device_interrupt_handler(); +} + +/** + * \brief Get the default USB module settings + * + * \param[out] module_config Configuration structure to initialize to default values + */ +void usb_get_config_defaults(struct usb_config *module_config) +{ + Assert(module_config); + + /* Sanity check arguments */ + Assert(module_config); + /* Write default configuration to config struct */ + module_config->select_host_mode = 0; + module_config->run_in_standby = 1; + module_config->source_generator = 0; + module_config->speed_mode = USB_SPEED_FULL; +} + +#define NVM_USB_PAD_TRANSN_POS 45 +#define NVM_USB_PAD_TRANSN_SIZE 5 +#define NVM_USB_PAD_TRANSP_POS 50 +#define NVM_USB_PAD_TRANSP_SIZE 5 +#define NVM_USB_PAD_TRIM_POS 55 +#define NVM_USB_PAD_TRIM_SIZE 3 + +/** + * \brief Initializes USB module instance + * + * Enables the clock and initializes the USB module, based on the given + * configuration values. + * + * \param[in,out] module_inst Pointer to the software module instance struct + * \param[in] hw Pointer to the USB hardware module + * \param[in] module_config Pointer to the USB configuration options struct + * + * \return Status of the initialization procedure. + * + * \retval STATUS_OK The module was initialized successfully + */ + +#define GCLK_USB 10 + +enum status_code usb_init(struct usb_module *module_inst, Usb *const hw, + struct usb_config *module_config) +{ + /* Sanity check arguments */ + Assert(hw); + Assert(module_inst); + Assert(module_config); + + uint32_t i,j; + uint32_t pad_transn, pad_transp, pad_trim; + + Gclk *pgclk = GCLK; + Mclk *pmclk = MCLK; + Port *pport = PORT; + Oscctrl *posc = OSCCTRL; + + _usb_instances = module_inst; + + /* Associate the software module instance with the hardware module */ + module_inst->hw = hw; + + //setup peripheral and synchronous bus clocks to USB + pmclk->AHBMASK.bit.USB_ = 1; + pmclk->APBBMASK.bit.USB_ = 1; + + /* Set up the USB DP/DN pins */ + pport->Group[0].PMUX[12].reg = 0x77; //PA24, PA25, function column H for USB D-, D+ + pport->Group[0].PINCFG[24].bit.PMUXEN = 1; + pport->Group[0].PINCFG[25].bit.PMUXEN = 1; + pport->Group[1].PMUX[11].bit.PMUXE = 7; //PB22, function column H for USB SOF_1KHz output + pport->Group[1].PINCFG[22].bit.PMUXEN = 1; + + //configure and enable DFLL for USB clock recovery mode at 48MHz + posc->DFLLCTRLA.bit.ENABLE = 0; + while (posc->DFLLSYNC.bit.ENABLE); + while (posc->DFLLSYNC.bit.DFLLCTRLB); + posc->DFLLCTRLB.bit.USBCRM = 1; + while (posc->DFLLSYNC.bit.DFLLCTRLB); + posc->DFLLCTRLB.bit.MODE = 1; + while (posc->DFLLSYNC.bit.DFLLCTRLB); + posc->DFLLCTRLB.bit.QLDIS = 0; + while (posc->DFLLSYNC.bit.DFLLCTRLB); + posc->DFLLCTRLB.bit.CCDIS = 1; + posc->DFLLMUL.bit.MUL = 0xbb80; //4800 x 1KHz + while (posc->DFLLSYNC.bit.DFLLMUL); + posc->DFLLCTRLA.bit.ENABLE = 1; + while (posc->DFLLSYNC.bit.ENABLE); + + /* Setup clock for module */ + pgclk->PCHCTRL[GCLK_USB].bit.GEN = 0; + pgclk->PCHCTRL[GCLK_USB].bit.CHEN = 1; + + /* Reset */ + hw->DEVICE.CTRLA.bit.SWRST = 1; + while (hw->DEVICE.SYNCBUSY.bit.SWRST) { + /* Sync wait */ + } + + /* Change QOS values to have the best performance and correct USB behaviour */ + USB->DEVICE.QOSCTRL.bit.CQOS = 2; + USB->DEVICE.QOSCTRL.bit.DQOS = 2; + + /* Load Pad Calibration */ + + pad_transn = (USB_FUSES_TRANSN_ADDR >> USB_FUSES_TRANSN_Pos) & USB_FUSES_TRANSN_Msk; + if (pad_transn == 0x1F) { + pad_transn = 5; + } + + hw->DEVICE.PADCAL.bit.TRANSN = pad_transn; + + pad_transp = (USB_FUSES_TRANSP_ADDR >> USB_FUSES_TRANSP_Pos) & USB_FUSES_TRANSP_Msk; + if (pad_transp == 0x1F) { + pad_transp = 29; + } + + hw->DEVICE.PADCAL.bit.TRANSP = pad_transp; + + pad_trim = (USB_FUSES_TRIM_ADDR >> USB_FUSES_TRIM_Pos) & USB_FUSES_TRIM_Msk; + if (pad_trim == 0x07) { + pad_trim = 3; + } + + hw->DEVICE.PADCAL.bit.TRIM = pad_trim; + + /* Set the configuration */ + hw->DEVICE.CTRLA.bit.MODE = module_config->select_host_mode; + hw->DEVICE.CTRLA.bit.RUNSTDBY = module_config->run_in_standby; + hw->DEVICE.DESCADD.reg = (uint32_t)(&usb_descriptor_table.usb_endpoint_table[0]); + if (USB_SPEED_FULL == module_config->speed_mode) { + module_inst->hw->DEVICE.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_FS_Val; + } else if(USB_SPEED_LOW == module_config->speed_mode) { + module_inst->hw->DEVICE.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_LS_Val; + } + + memset((uint8_t *)(&usb_descriptor_table.usb_endpoint_table[0]), 0, + sizeof(usb_descriptor_table.usb_endpoint_table)); + + /* device callback related */ + for (i = 0; i < USB_DEVICE_CALLBACK_N; i++) { + module_inst->device_callback[i] = NULL; + } + for (i = 0; i < USB_EPT_NUM; i++) { + for(j = 0; j < USB_DEVICE_EP_CALLBACK_N; j++) { + module_inst->device_endpoint_callback[i][j] = NULL; + } + } + module_inst->device_registered_callback_mask = 0; + module_inst->device_enabled_callback_mask = 0; + for (j = 0; j < USB_EPT_NUM; j++) { + module_inst->device_endpoint_registered_callback_mask[j] = 0; + module_inst->device_endpoint_enabled_callback_mask[j] = 0; + } + + /* Enable interrupts for this USB module */ + NVIC_EnableIRQ(USB_0_IRQn); + NVIC_EnableIRQ(USB_2_IRQn); + NVIC_EnableIRQ(USB_3_IRQn); + + return STATUS_OK; +} + diff --git a/tmk_core/protocol/arm_atsam/usb/usb.h b/tmk_core/protocol/arm_atsam/usb/usb.h new file mode 100644 index 0000000000..9a452881a7 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb.h @@ -0,0 +1,492 @@ +/** + * \file + * + * \brief SAM USB Driver + * + * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#ifndef USB_H_INCLUDED +#define USB_H_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup asfdoc_sam0_usb_group SAM Universal Serial Bus (USB) + * + * The Universal Serial Bus (USB) module complies with the USB 2.1 specification. + * + * The following peripherals are used by this module: + * - USB (Universal Serial Bus) + * + * The following devices can use this module: + * - Atmel | SMART SAM D51 + * + * The USB module covers following mode: + * \if USB_DEVICE_MODE + * - USB Device Mode + * \endif + * \if USB_HOST_MODE + * - USB Host Mode + * \endif + * + * The USB module covers following speed: + * \if USB_HS_MODE + * - USB High Speed (480Mbit/s) + * \endif + * - USB Full Speed (12Mbit/s) + * \if USB_LS_MODE + * - USB Low Speed (1.5Mbit/s) + * \endif + * + * \if USB_LPM_MODE + * The USB module supports Link Power Management (LPM-L1) protocol. + * \endif + * + * USB support needs whole set of enumeration process, to make the device + * recognizable and usable. The USB driver is designed to interface to the + * USB Stack in Atmel Software Framework (ASF). + * + * \if USB_DEVICE_MODE + * \section asfdoc_sam0_usb_device USB Device Mode + * The ASF USB Device Stack has defined the USB Device Driver (UDD) interface, + * to support USB device operations. The USB module device driver complies with + * this interface, so that the USB Device Stack can work based on the + * USB module. + * + * Refer to <a href="http://www.atmel.com/images/doc8360.pdf"> + * "ASF - USB Device Stack"</a> for more details. + * \endif + * + * \if USB_HOST_MODE + * \section adfdoc_sam0_usb_host USB Host Mode + * The ASF USB Host Stack has defined the USB Host Driver (UHD) interface, + * to support USB host operations. The USB module host driver complies with + * this interface, so that the USB Host Stack can work based on the USB module. + * + * Refer to <a href="http://www.atmel.com/images/doc8486.pdf"> + * "ASF - USB Host Stack"</a> for more details. + * \endif + */ + +/** Enum for the speed status for the USB module */ +enum usb_speed { + USB_SPEED_LOW, + USB_SPEED_FULL, +}; + +/** Enum for the possible callback types for the USB in host module */ +enum usb_host_callback { + USB_HOST_CALLBACK_SOF, + USB_HOST_CALLBACK_RESET, + USB_HOST_CALLBACK_WAKEUP, + USB_HOST_CALLBACK_DNRSM, + USB_HOST_CALLBACK_UPRSM, + USB_HOST_CALLBACK_RAMACER, + USB_HOST_CALLBACK_CONNECT, + USB_HOST_CALLBACK_DISCONNECT, + USB_HOST_CALLBACK_N, +}; + +/** Enum for the possible callback types for the USB pipe in host module */ +enum usb_host_pipe_callback { + USB_HOST_PIPE_CALLBACK_TRANSFER_COMPLETE, + USB_HOST_PIPE_CALLBACK_ERROR, + USB_HOST_PIPE_CALLBACK_SETUP, + USB_HOST_PIPE_CALLBACK_STALL, + USB_HOST_PIPE_CALLBACK_N, +}; + +/** + * \brief Host pipe types. + */ +enum usb_host_pipe_type { + USB_HOST_PIPE_TYPE_DISABLE, + USB_HOST_PIPE_TYPE_CONTROL, + USB_HOST_PIPE_TYPE_ISO, + USB_HOST_PIPE_TYPE_BULK, + USB_HOST_PIPE_TYPE_INTERRUPT, + USB_HOST_PIPE_TYPE_EXTENDED, +}; + +/** + * \brief Host pipe token types. + */ +enum usb_host_pipe_token { + USB_HOST_PIPE_TOKEN_SETUP, + USB_HOST_PIPE_TOKEN_IN, + USB_HOST_PIPE_TOKEN_OUT, +}; + +/** + * \brief Enumeration for the possible callback types for the USB in device module + */ +enum usb_device_callback { + USB_DEVICE_CALLBACK_SOF, + USB_DEVICE_CALLBACK_RESET, + USB_DEVICE_CALLBACK_WAKEUP, + USB_DEVICE_CALLBACK_RAMACER, + USB_DEVICE_CALLBACK_SUSPEND, + USB_DEVICE_CALLBACK_LPMNYET, + USB_DEVICE_CALLBACK_LPMSUSP, + USB_DEVICE_CALLBACK_N, +}; + +/** + * \brief Enumeration for the possible callback types for the USB endpoint in device module + */ +enum usb_device_endpoint_callback { + USB_DEVICE_ENDPOINT_CALLBACK_TRCPT, + USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL, + USB_DEVICE_ENDPOINT_CALLBACK_RXSTP, + USB_DEVICE_ENDPOINT_CALLBACK_STALL, + USB_DEVICE_EP_CALLBACK_N, +}; + +/** + * \brief Device Endpoint types. + */ +enum usb_device_endpoint_type { + USB_DEVICE_ENDPOINT_TYPE_DISABLE, + USB_DEVICE_ENDPOINT_TYPE_CONTROL, + USB_DEVICE_ENDPOINT_TYPE_ISOCHRONOUS, + USB_DEVICE_ENDPOINT_TYPE_BULK, + USB_DEVICE_ENDPOINT_TYPE_INTERRUPT, +}; + +/** + * \brief Endpoint Size + */ +enum usb_endpoint_size { + USB_ENDPOINT_8_BYTE, + USB_ENDPOINT_16_BYTE, + USB_ENDPOINT_32_BYTE, + USB_ENDPOINT_64_BYTE, + USB_ENDPOINT_128_BYTE, + USB_ENDPOINT_256_BYTE, + USB_ENDPOINT_512_BYTE, + USB_ENDPOINT_1023_BYTE, +}; + +/** + * \brief Link Power Management Handshake. + */ +enum usb_device_lpm_mode { + USB_DEVICE_LPM_NOT_SUPPORT, + USB_DEVICE_LPM_ACK, + USB_DEVICE_LPM_NYET, +}; + +/** + * \brief Module structure + */ +struct usb_module; + +/** + * \name Host Callback Functions Types + * @{ + */ +typedef void (*usb_host_callback_t)(struct usb_module *module_inst); +typedef void (*usb_host_pipe_callback_t)(struct usb_module *module_inst, void *); +/** @} */ + +/** + * \name Device Callback Functions Types + * @{ + */ +typedef void (*usb_device_callback_t)(struct usb_module *module_inst, void* pointer); +typedef void (*usb_device_endpoint_callback_t)(struct usb_module *module_inst, void* pointer); +/** @} */ + +/** USB configurations */ +struct usb_config { + /** \c true for host, \c false for device. */ + bool select_host_mode; + /** When \c true the module is enabled during standby. */ + bool run_in_standby; + /** Generic Clock Generator source channel. */ + // enum gclk_generator source_generator; + uint8_t source_generator; + /** Speed mode */ + //enum usb_speed speed_mode; + uint8_t speed_mode; +}; + +/** + * \brief USB software module instance structure. + * + * USB software module instance structure, used to retain software state + * information of an associated hardware module instance. + * + */ +struct usb_module { + /** Hardware module pointer of the associated USB peripheral. */ + Usb *hw; + + /** Array to store device related callback functions */ + usb_device_callback_t device_callback[USB_DEVICE_CALLBACK_N]; + usb_device_endpoint_callback_t device_endpoint_callback[USB_EPT_NUM][USB_DEVICE_EP_CALLBACK_N]; + /** Bit mask for device callbacks registered */ + uint16_t device_registered_callback_mask; + /** Bit mask for device callbacks enabled */ + uint16_t device_enabled_callback_mask; + /** Bit mask for device endpoint callbacks registered */ + uint8_t device_endpoint_registered_callback_mask[USB_EPT_NUM]; + /** Bit mask for device endpoint callbacks enabled */ + uint8_t device_endpoint_enabled_callback_mask[USB_EPT_NUM]; +}; + +/** USB device endpoint configurations */ +struct usb_device_endpoint_config { + /** device address */ + uint8_t ep_address; + /** endpoint size */ + enum usb_endpoint_size ep_size; + /** automatic zero length packet mode, \c true to enable */ + bool auto_zlp; + /** type of endpoint with Bank */ + enum usb_device_endpoint_type ep_type; +}; + +/** USB device endpoint callback status parameter structure */ +struct usb_endpoint_callback_parameter { + uint16_t received_bytes; + uint16_t sent_bytes; + uint16_t out_buffer_size; + uint8_t endpoint_address; +}; + +void usb_enable(struct usb_module *module_inst); +void usb_disable(struct usb_module *module_inst); + +/** + * \brief Get the status of USB module's state machine + * + * \param module_inst Pointer to USB module instance + */ +static inline uint8_t usb_get_state_machine_status(struct usb_module *module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + return module_inst->hw->DEVICE.FSMSTATUS.reg; +} + +void usb_get_config_defaults(struct usb_config *module_config); +enum status_code usb_init(struct usb_module *module_inst, Usb *const hw, + struct usb_config *module_config); + +/** + * \brief Attach USB device to the bus + * + * \param module_inst Pointer to USB device module instance + */ +static inline void usb_device_attach(struct usb_module *module_inst) +{ + module_inst->hw->DEVICE.CTRLB.reg &= ~USB_DEVICE_CTRLB_DETACH; +} + +/** + * \brief Detach USB device from the bus + * + * \param module_inst Pointer to USB device module instance + */ +static inline void usb_device_detach(struct usb_module *module_inst) +{ + module_inst->hw->DEVICE.CTRLB.reg |= USB_DEVICE_CTRLB_DETACH; +} + +/** + * \brief Get the speed mode of USB device + * + * \param module_inst Pointer to USB device module instance + * \return USB Speed mode (\ref usb_speed). + */ +static inline enum usb_speed usb_device_get_speed(struct usb_module *module_inst) +{ + if (!(module_inst->hw->DEVICE.STATUS.reg & USB_DEVICE_STATUS_SPEED_Msk)) { + return USB_SPEED_FULL; + } else { + return USB_SPEED_LOW; + } +} + +/** + * \brief Get the address of USB device + * + * \param module_inst Pointer to USB device module instance + * \return USB device address value. + */ +static inline uint8_t usb_device_get_address(struct usb_module *module_inst) +{ + return ((uint8_t)(module_inst->hw->DEVICE.DADD.bit.DADD)); +} + +/** + * \brief Set the speed mode of USB device + * + * \param module_inst Pointer to USB device module instance + * \param address USB device address value + */ +static inline void usb_device_set_address(struct usb_module *module_inst, uint8_t address) +{ + module_inst->hw->DEVICE.DADD.reg = USB_DEVICE_DADD_ADDEN | address; +} + +/** + * \brief Get the frame number of USB device + * + * \param module_inst Pointer to USB device module instance + * \return USB device frame number value. + */ +static inline uint16_t usb_device_get_frame_number(struct usb_module *module_inst) +{ + return ((uint16_t)(module_inst->hw->DEVICE.FNUM.bit.FNUM)); +} + +/** + * \brief Get the micro-frame number of USB device + * + * \param module_inst Pointer to USB device module instance + * \return USB device micro-frame number value. + */ +static inline uint16_t usb_device_get_micro_frame_number(struct usb_module *module_inst) +{ + return ((uint16_t)(module_inst->hw->DEVICE.FNUM.reg)); +} + +/** + * \brief USB device send the resume wakeup + * + * \param module_inst Pointer to USB device module instance + */ +static inline void usb_device_send_remote_wake_up(struct usb_module *module_inst) +{ + module_inst->hw->DEVICE.CTRLB.reg |= USB_DEVICE_CTRLB_UPRSM; +} + +/** + * \brief USB device set the LPM mode + * + * \param module_inst Pointer to USB device module instance + * \param lpm_mode LPM mode + */ +static inline void usb_device_set_lpm_mode(struct usb_module *module_inst, + enum usb_device_lpm_mode lpm_mode) +{ + module_inst->hw->DEVICE.CTRLB.bit.LPMHDSK = lpm_mode; +} + +/** + * \name USB Device Callback Management + * @{ + */ +enum status_code usb_device_register_callback(struct usb_module *module_inst, + enum usb_device_callback callback_type, + usb_device_callback_t callback_func); +enum status_code usb_device_unregister_callback(struct usb_module *module_inst, + enum usb_device_callback callback_type); +enum status_code usb_device_enable_callback(struct usb_module *module_inst, + enum usb_device_callback callback_type); +enum status_code usb_device_disable_callback(struct usb_module *module_inst, + enum usb_device_callback callback_type); +/** @} */ + +/** + * \name USB Device Endpoint Configuration + * @{ + */ +void usb_device_endpoint_get_config_defaults(struct usb_device_endpoint_config *ep_config); +enum status_code usb_device_endpoint_set_config(struct usb_module *module_inst, + struct usb_device_endpoint_config *ep_config); +bool usb_device_endpoint_is_configured(struct usb_module *module_inst, uint8_t ep); +/** @} */ + +/** + * \name USB Device Endpoint Callback Management + * @{ + */ +enum status_code usb_device_endpoint_register_callback( + struct usb_module *module_inst, uint8_t ep_num, + enum usb_device_endpoint_callback callback_type, + usb_device_endpoint_callback_t callback_func); +enum status_code usb_device_endpoint_unregister_callback( + struct usb_module *module_inst, uint8_t ep_num, + enum usb_device_endpoint_callback callback_type); +enum status_code usb_device_endpoint_enable_callback( + struct usb_module *module_inst, uint8_t ep, + enum usb_device_endpoint_callback callback_type); +enum status_code usb_device_endpoint_disable_callback( + struct usb_module *module_inst, uint8_t ep, + enum usb_device_endpoint_callback callback_type); +/** @} */ + +/** + * \name USB Device Endpoint Job Management + * @{ + */ +enum status_code usb_device_endpoint_write_buffer_job(struct usb_module *module_inst,uint8_t ep_num, + uint8_t* pbuf, uint32_t buf_size); +enum status_code usb_device_endpoint_read_buffer_job(struct usb_module *module_inst,uint8_t ep_num, + uint8_t* pbuf, uint32_t buf_size); +enum status_code usb_device_endpoint_setup_buffer_job(struct usb_module *module_inst, + uint8_t* pbuf); +void usb_device_endpoint_abort_job(struct usb_module *module_inst, uint8_t ep); +/** @} */ + +/** + * \name USB Device Endpoint Operations + * @{ + */ + +bool usb_device_endpoint_is_halted(struct usb_module *module_inst, uint8_t ep); +void usb_device_endpoint_set_halt(struct usb_module *module_inst, uint8_t ep); +void usb_device_endpoint_clear_halt(struct usb_module *module_inst, uint8_t ep); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* USB_H_INCLUDED */ diff --git a/tmk_core/protocol/arm_atsam/usb/usb2422.c b/tmk_core/protocol/arm_atsam/usb/usb2422.c new file mode 100644 index 0000000000..ac19bf4ea0 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb2422.c @@ -0,0 +1,409 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "arm_atsam_protocol.h" +#include <string.h> + +Usb2422 USB2422_shadow; +unsigned char i2c0_buf[34]; + +const uint16_t MFRNAME[] = { 'M','a','s','s','d','r','o','p',' ','I','n','c','.' }; //Massdrop Inc. +const uint16_t PRDNAME[] = { 'M','a','s','s','d','r','o','p',' ','H','u','b' }; //Massdrop Hub +#ifndef MD_BOOTLOADER +//Serial number reported stops before first found space character or at last found character +const uint16_t SERNAME[] = { 'U','n','a','v','a','i','l','a','b','l','e' }; //Unavailable +#else +//In production, this field is found, modified, and offset noted as the last 32-bit word in the bootloader space +//The offset allows the application to use the factory programmed serial (which may differ from the physical serial label) +//Serial number reported stops before first found space character or when max size is reached +__attribute__((__aligned__(4))) +const uint16_t SERNAME[BOOTLOADER_SERIAL_MAX_SIZE] = { 'M','D','H','U','B','B','O','O','T','L','0','0','0','0','0','0','0','0','0','0' }; +//NOTE: Serial replacer will not write a string longer than given here as a precaution, so give enough +// space as needed and adjust BOOTLOADER_SERIAL_MAX_SIZE to match amount given +#endif //MD_BOOTLOADER + +uint8_t usb_host_port; + +#ifndef MD_BOOTLOADER + +uint8_t usb_extra_state; +uint8_t usb_extra_manual; +uint8_t usb_gcr_auto; + +#endif //MD_BOOTLOADER + +uint16_t adc_extra; + +void USB_write2422_block(void) +{ + unsigned char *dest = i2c0_buf; + unsigned char *src; + unsigned char *base = (unsigned char *)&USB2422_shadow; + + DBGC(DC_USB_WRITE2422_BLOCK_BEGIN); + + for (src = base; src < base + 256; src += 32) + { + dest[0] = src - base; + dest[1] = 32; + memcpy(&dest[2], src, 32); + i2c0_transmit(USB2422_ADDR, dest, 34, 50000); + SERCOM0->I2CM.CTRLB.bit.CMD = 0x03; + while (SERCOM0->I2CM.SYNCBUSY.bit.SYSOP) { DBGC(DC_USB_WRITE2422_BLOCK_SYNC_SYSOP); } + CLK_delay_us(100); + } + + DBGC(DC_USB_WRITE2422_BLOCK_COMPLETE); +} + +void USB2422_init(void) +{ + Gclk *pgclk = GCLK; + Mclk *pmclk = MCLK; + Port *pport = PORT; + Oscctrl *posc = OSCCTRL; + Usb *pusb = USB; + + DBGC(DC_USB2422_INIT_BEGIN); + + while ((v_5v = adc_get(ADC_5V)) < ADC_5V_START_LEVEL) { DBGC(DC_USB2422_INIT_WAIT_5V_LOW); } + + //setup peripheral and synchronous bus clocks to USB + pgclk->PCHCTRL[10].bit.GEN = 0; + pgclk->PCHCTRL[10].bit.CHEN = 1; + pmclk->AHBMASK.bit.USB_ = 1; + pmclk->APBBMASK.bit.USB_ = 1; + + //setup port pins for D-, D+, and SOF_1KHZ + pport->Group[0].PMUX[12].reg = 0x77; //PA24, PA25, function column H for USB D-, D+ + pport->Group[0].PINCFG[24].bit.PMUXEN = 1; + pport->Group[0].PINCFG[25].bit.PMUXEN = 1; + pport->Group[1].PMUX[11].bit.PMUXE = 7; //PB22, function column H for USB SOF_1KHz output + pport->Group[1].PINCFG[22].bit.PMUXEN = 1; + + //configure and enable DFLL for USB clock recovery mode at 48MHz + posc->DFLLCTRLA.bit.ENABLE = 0; + while (posc->DFLLSYNC.bit.ENABLE) { DBGC(DC_USB2422_INIT_OSC_SYNC_DISABLING); } + while (posc->DFLLSYNC.bit.DFLLCTRLB) { DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_1); } + posc->DFLLCTRLB.bit.USBCRM = 1; + while (posc->DFLLSYNC.bit.DFLLCTRLB) { DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_2); } + posc->DFLLCTRLB.bit.MODE = 1; + while (posc->DFLLSYNC.bit.DFLLCTRLB) { DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_3); } + posc->DFLLCTRLB.bit.QLDIS = 0; + while (posc->DFLLSYNC.bit.DFLLCTRLB) { DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLCTRLB_4); } + posc->DFLLCTRLB.bit.CCDIS = 1; + posc->DFLLMUL.bit.MUL = 0xBB80; //4800 x 1KHz + while (posc->DFLLSYNC.bit.DFLLMUL) { DBGC(DC_USB2422_INIT_OSC_SYNC_DFLLMUL); } + posc->DFLLCTRLA.bit.ENABLE = 1; + while (posc->DFLLSYNC.bit.ENABLE) { DBGC(DC_USB2422_INIT_OSC_SYNC_ENABLING); } + + pusb->DEVICE.CTRLA.bit.SWRST = 1; + while (pusb->DEVICE.SYNCBUSY.bit.SWRST) { DBGC(DC_USB2422_INIT_USB_SYNC_SWRST); } + while (pusb->DEVICE.CTRLA.bit.SWRST) { DBGC(DC_USB2422_INIT_USB_WAIT_SWRST); } + //calibration from factory presets + pusb->DEVICE.PADCAL.bit.TRANSN = (USB_FUSES_TRANSN_ADDR >> USB_FUSES_TRANSN_Pos) & USB_FUSES_TRANSN_Msk; + pusb->DEVICE.PADCAL.bit.TRANSP = (USB_FUSES_TRANSP_ADDR >> USB_FUSES_TRANSP_Pos) & USB_FUSES_TRANSP_Msk; + pusb->DEVICE.PADCAL.bit.TRIM = (USB_FUSES_TRIM_ADDR >> USB_FUSES_TRIM_Pos) & USB_FUSES_TRIM_Msk; + //device mode, enabled + pusb->DEVICE.CTRLB.bit.SPDCONF = 0; //full speed + pusb->DEVICE.CTRLA.bit.MODE = 0; + pusb->DEVICE.CTRLA.bit.ENABLE = 1; + while (pusb->DEVICE.SYNCBUSY.bit.ENABLE) { DBGC(DC_USB2422_INIT_USB_SYNC_ENABLING); } + + pusb->DEVICE.QOSCTRL.bit.DQOS = 2; + pusb->DEVICE.QOSCTRL.bit.CQOS = 2; + + pport->Group[USB2422_HUB_ACTIVE_GROUP].PINCFG[USB2422_HUB_ACTIVE_PIN].bit.INEN = 1; + + i2c0_init(); //IC2 clk must be high at USB2422 reset release time to signal SMB configuration + + sr_exp_data.bit.HUB_CONNECT = 1; //connect signal + sr_exp_data.bit.HUB_RESET_N = 1; //reset high + SR_EXP_WriteData(); + + CLK_delay_us(100); + +#ifndef MD_BOOTLOADER + + usb_extra_manual = 0; + usb_gcr_auto = 1; + +#endif //MD_BOOTLOADER + + DBGC(DC_USB2422_INIT_COMPLETE); +} + +void USB_reset(void) +{ + DBGC(DC_USB_RESET_BEGIN); + + //pulse reset for at least 1 usec + sr_exp_data.bit.HUB_RESET_N = 0; //reset low + SR_EXP_WriteData(); + CLK_delay_us(1); + sr_exp_data.bit.HUB_RESET_N = 1; //reset high to run + SR_EXP_WriteData(); + CLK_delay_us(1); + + DBGC(DC_USB_RESET_COMPLETE); +} + +void USB_configure(void) +{ + Usb2422 *pusb2422 = &USB2422_shadow; + memset(pusb2422, 0, sizeof(Usb2422)); + + uint16_t *serial_use = (uint16_t *)SERNAME; //Default to use SERNAME from this file + uint8_t serial_length = sizeof(SERNAME) / sizeof(uint16_t); //Default to use SERNAME from this file +#ifndef MD_BOOTLOADER + uint32_t serial_ptrloc = (uint32_t)&_srom - 4; +#else //MD_BOOTLOADER + uint32_t serial_ptrloc = (uint32_t)&_erom - 4; +#endif //MD_BOOTLOADER + uint32_t serial_address = *(uint32_t *)serial_ptrloc; //Address of bootloader's serial number if available + + DBGC(DC_USB_CONFIGURE_BEGIN); + + if (serial_address != 0xFFFFFFFF && serial_address < serial_ptrloc) //Check for factory programmed serial address + { + if ((serial_address & 0xFF) % 4 == 0) //Check alignment + { + serial_use = (uint16_t *)(serial_address); + serial_length = 0; + while ((*(serial_use + serial_length) > 32 && *(serial_use + serial_length) < 127) && + serial_length < BOOTLOADER_SERIAL_MAX_SIZE) + { + serial_length++; + DBGC(DC_USB_CONFIGURE_GET_SERIAL); + } + } + } + + //configure Usb2422 registers + pusb2422->VID.reg = 0x04D8; // from Microchip 4/19/2018 + pusb2422->PID.reg = 0xEEC5; // from Microchip 4/19/2018 = Massdrop, Inc. USB Hub + pusb2422->DID.reg = 0x0101; // BCD 01.01 + pusb2422->CFG1.bit.SELF_BUS_PWR = 1; // self powered for now + pusb2422->CFG1.bit.HS_DISABLE = 1; // full or high speed + //pusb2422->CFG2.bit.COMPOUND = 0; // compound device + pusb2422->CFG3.bit.STRING_EN = 1; // strings enabled + //pusb2422->NRD.bit.PORT2_NR = 0; // MCU is non-removable + pusb2422->MAXPB.reg = 20; // 0mA + pusb2422->HCMCB.reg = 20; // 0mA + pusb2422->MFRSL.reg = sizeof(MFRNAME) / sizeof(uint16_t); + pusb2422->PRDSL.reg = sizeof(PRDNAME) / sizeof(uint16_t); + pusb2422->SERSL.reg = serial_length; + memcpy(pusb2422->MFRSTR, MFRNAME, sizeof(MFRNAME)); + memcpy(pusb2422->PRDSTR, PRDNAME, sizeof(PRDNAME)); + memcpy(pusb2422->SERSTR, serial_use, serial_length * sizeof(uint16_t)); + //pusb2422->BOOSTUP.bit.BOOST=3; //upstream port + //pusb2422->BOOSTDOWN.bit.BOOST1=0; // extra port + //pusb2422->BOOSTDOWN.bit.BOOST2=2; //MCU is close + pusb2422->STCD.bit.USB_ATTACH = 1; + USB_write2422_block(); + + adc_extra = 0; + + DBGC(DC_USB_CONFIGURE_COMPLETE); +} + +uint16_t USB_active(void) +{ + return (PORT->Group[USB2422_HUB_ACTIVE_GROUP].IN.reg & (1 << USB2422_HUB_ACTIVE_PIN)) != 0; +} + +void USB_set_host_by_voltage(void) +{ + //UP is upstream device (HOST) + //DN1 is downstream device (EXTRA) + //DN2 is keyboard (KEYB) + + DBGC(DC_USB_SET_HOST_BY_VOLTAGE_BEGIN); + + usb_host_port = USB_HOST_PORT_UNKNOWN; +#ifndef MD_BOOTLOADER + usb_extra_state = USB_EXTRA_STATE_UNKNOWN; +#endif //MD_BOOTLOADER + sr_exp_data.bit.SRC_1 = 1; //USBC-1 available for test + sr_exp_data.bit.SRC_2 = 1; //USBC-2 available for test + sr_exp_data.bit.E_UP_N = 1; //HOST disable + sr_exp_data.bit.E_DN1_N = 1; //EXTRA disable + sr_exp_data.bit.E_VBUS_1 = 0; //USBC-1 disable full power I/O + sr_exp_data.bit.E_VBUS_2 = 0; //USBC-2 disable full power I/O + + SR_EXP_WriteData(); + + CLK_delay_ms(250); + + while ((v_5v = adc_get(ADC_5V)) < ADC_5V_START_LEVEL) { DBGC(DC_USB_SET_HOST_5V_LOW_WAITING); } + + v_con_1 = adc_get(ADC_CON1); + v_con_2 = adc_get(ADC_CON2); + + v_con_1_boot = v_con_1; + v_con_2_boot = v_con_2; + + if (v_con_1 > v_con_2) + { + sr_exp_data.bit.S_UP = 0; //HOST to USBC-1 + sr_exp_data.bit.S_DN1 = 1; //EXTRA to USBC-2 + sr_exp_data.bit.SRC_1 = 1; //HOST on USBC-1 + sr_exp_data.bit.SRC_2 = 0; //EXTRA available on USBC-2 + + sr_exp_data.bit.E_VBUS_1 = 1; //USBC-1 enable full power I/O + sr_exp_data.bit.E_VBUS_2 = 0; //USBC-2 disable full power I/O + + SR_EXP_WriteData(); + + sr_exp_data.bit.E_UP_N = 0; //HOST enable + + SR_EXP_WriteData(); + + usb_host_port = USB_HOST_PORT_1; + } + else + { + sr_exp_data.bit.S_UP = 1; //EXTRA to USBC-1 + sr_exp_data.bit.S_DN1 = 0; //HOST to USBC-2 + sr_exp_data.bit.SRC_1 = 0; //EXTRA available on USBC-1 + sr_exp_data.bit.SRC_2 = 1; //HOST on USBC-2 + + sr_exp_data.bit.E_VBUS_1 = 0; //USBC-1 disable full power I/O + sr_exp_data.bit.E_VBUS_2 = 1; //USBC-2 enable full power I/O + + SR_EXP_WriteData(); + + sr_exp_data.bit.E_UP_N = 0; //HOST enable + + SR_EXP_WriteData(); + + usb_host_port = USB_HOST_PORT_2; + } + +#ifndef MD_BOOTLOADER + usb_extra_state = USB_EXTRA_STATE_DISABLED; +#endif //MD_BOOTLOADER + + USB_reset(); + USB_configure(); + + DBGC(DC_USB_SET_HOST_BY_VOLTAGE_COMPLETE); +} + +uint8_t USB2422_Port_Detect_Init(void) +{ + uint32_t port_detect_retry_ms; + uint32_t tmod; + + DBGC(DC_PORT_DETECT_INIT_BEGIN); + + USB_set_host_by_voltage(); + + port_detect_retry_ms = CLK_get_ms() + PORT_DETECT_RETRY_INTERVAL; + + while (!USB_active()) + { + tmod = CLK_get_ms() % PORT_DETECT_RETRY_INTERVAL; + + if (v_con_1 > v_con_2) //Values updated from USB_set_host_by_voltage(); + { + //1 flash for port 1 detected + if (tmod > 500 && tmod < 600) { DBG_LED_ON; } + else { DBG_LED_OFF; } + } + else if (v_con_2 > v_con_1) //Values updated from USB_set_host_by_voltage(); + { + //2 flash for port 2 detected + if (tmod > 500 && tmod < 600) { DBG_LED_ON; } + else if (tmod > 700 && tmod < 800) { DBG_LED_ON; } + else { DBG_LED_OFF; } + } + + if (CLK_get_ms() > port_detect_retry_ms) + { + DBGC(DC_PORT_DETECT_INIT_FAILED); + return 0; + } + } + + DBGC(DC_PORT_DETECT_INIT_COMPLETE); + + return 1; +} + +#ifndef MD_BOOTLOADER + +void USB_ExtraSetState(uint8_t state) +{ + uint8_t state_save = state; + + if (state == USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) + state = USB_EXTRA_STATE_DISABLED; + + if (usb_host_port == USB_HOST_PORT_1) sr_exp_data.bit.E_VBUS_2 = state; + else if (usb_host_port == USB_HOST_PORT_2) sr_exp_data.bit.E_VBUS_1 = state; + else return; + + sr_exp_data.bit.E_DN1_N = !state; + SR_EXP_WriteData(); + + usb_extra_state = state_save; + + if (usb_extra_state == USB_EXTRA_STATE_ENABLED) CDC_print("USB: Extra enabled\r\n"); + else if (usb_extra_state == USB_EXTRA_STATE_DISABLED) + { + CDC_print("USB: Extra disabled\r\n"); + if (led_animation_breathing) gcr_breathe = gcr_desired; + } + else if (usb_extra_state == USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) CDC_print("USB: Extra disabled until replug\r\n"); + else CDC_print("USB: Extra state unknown\r\n"); +} + +void USB_HandleExtraDevice(void) +{ + uint16_t adcval; + + if (usb_host_port == USB_HOST_PORT_1) adcval = adc_get(ADC_CON2); + else if (usb_host_port == USB_HOST_PORT_2) adcval = adc_get(ADC_CON1); + else return; + + adc_extra = adc_extra * 0.9 + adcval * 0.1; + + //Check for a forced disable state (such as overload prevention) + if (usb_extra_state == USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) + { + //Detect unplug and reset state to disabled + if (adc_extra > USB_EXTRA_ADC_THRESHOLD) usb_extra_state = USB_EXTRA_STATE_DISABLED; + + return; //Return even if unplug detected + } + + if (usb_extra_manual) + { + if (usb_extra_state == USB_EXTRA_STATE_DISABLED) + USB_ExtraSetState(USB_EXTRA_STATE_ENABLED); + + return; + } + + //dpf("a %i %i\r\n",adcval, adc_extra); + if (usb_extra_state == USB_EXTRA_STATE_DISABLED && adc_extra < USB_EXTRA_ADC_THRESHOLD) USB_ExtraSetState(USB_EXTRA_STATE_ENABLED); + else if (usb_extra_state == USB_EXTRA_STATE_ENABLED && adc_extra > USB_EXTRA_ADC_THRESHOLD) USB_ExtraSetState(USB_EXTRA_STATE_DISABLED); +} + +#endif //MD_BOOTLOADER + diff --git a/tmk_core/protocol/arm_atsam/usb/usb2422.h b/tmk_core/protocol/arm_atsam/usb/usb2422.h new file mode 100644 index 0000000000..85ad596a62 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb2422.h @@ -0,0 +1,404 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _USB2422_H_ +#define _USB2422_H_ + +#define REV_USB2422 0x100 + +#define USB2422_ADDR 0x58 //I2C device address, one instance + +#define USB2422_HUB_ACTIVE_GROUP 0 //PA +#define USB2422_HUB_ACTIVE_PIN 18 //18 + +/* -------- USB2422_VID : (USB2422L Offset: 0x00) (R/W 16) Vendor ID -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint16_t VID_LSB : 8; + uint16_t VID_MSB : 8; + } bit; /*!< Structure used for bit access */ + uint16_t reg; /*!< Type used for register access */ +} USB2422_VID_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_PID : (USB2422L Offset: 0x02) (R/W 16) Product ID -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint16_t PID_LSB : 8; + uint16_t PID_MSB : 8; + } bit; /*!< Structure used for bit access */ + uint16_t reg; /*!< Type used for register access */ +} USB2422_PID_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_DID : (USB2422L Offset: 0x04) (R/W 16) Device ID -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint16_t DID_LSB : 8; + uint16_t DID_MSB : 8; + } bit; /*!< Structure used for bit access */ + uint16_t reg; /*!< Type used for register access */ +} USB2422_DID_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_CFG1 : (USB2422L Offset: 0x06) (R/W 8) Configuration Data Byte 1-------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t PORT_PWR : 1; + uint8_t CURRENT_SNS : 2; + uint8_t EOP_DISABLE : 1; + uint8_t MTT_ENABLE : 1; + uint8_t HS_DISABLE :1; + uint8_t :1; + uint8_t SELF_BUS_PWR : 1; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_CFG1_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_CFG2 : (USB2422L Offset: 0x07) (R/W 8) Configuration Data Byte 2-------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t : 3; + uint8_t COMPOUND : 1; + uint8_t OC_TIMER :2; + uint8_t :1; + uint8_t DYNAMIC : 1; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_CFG2_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_CFG3 : (USB2422L Offset: 0x08) (R/W 16) Configuration Data Byte 3-------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t STRING_EN : 1; + uint8_t :2; + uint8_t PRTMAP_EN :1; + uint8_t : 4; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_CFG3_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_NRD : (USB2422L Offset: 0x09) (R/W 8) Non Removable Device -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t : 5; + uint8_t PORT2_NR :1; + uint8_t PORT1_NR :1; + uint8_t : 1; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_NRD_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_PDS : (USB2422L Offset: 0x0A) (R/W 8) Port Diable for Self-Powered Operation -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t : 1; + uint8_t PORT1_DIS :1; + uint8_t PORT2_DIS :1; + uint8_t : 5; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_PDS_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_PDB : (USB2422L Offset: 0x0B) (R/W 8) Port Diable for Bus-Powered Operation -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t : 1; + uint8_t PORT1_DIS :1; + uint8_t PORT2_DIS :1; + uint8_t : 5; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_PDB_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_MAXPS : (USB2422L Offset: 0x0C) (R/W 8) Max Power for Self-Powered Operation -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t MAX_PWR_SP : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_MAXPS_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_MAXPB : (USB2422L Offset: 0x0D) (R/W 8) Max Power for Bus-Powered Operation -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t MAX_PWR_BP : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_MAXPB_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_HCMCS : (USB2422L Offset: 0x0E) (R/W 8) Hub Controller Max Current for Self-Powered Operation -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t HC_MAX_C_SP : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_HCMCS_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_HCMCB : (USB2422L Offset: 0x0F) (R/W 8) Hub Controller Max Current for Bus-Powered Operation -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t HC_MAX_C_BP : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_HCMCB_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_PWRT : (USB2422L Offset: 0x10) (R/W 8) Power On Time -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t POWER_ON_TIME : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_PWRT_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_LANGID LSB : (USB2422L Offset: 0x11) (R/W 16) Language ID -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t LANGID_LSB : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_LANGID_LSB_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_LANGID MSB : (USB2422L Offset: 0x12) (R/W 16) Language ID -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t LANGID_MSB : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_LANGID_MSB_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + + +/* -------- USB2422_MFRSL : (USB2422L Offset: 0x13) (R/W 8) Manufacturer String Length -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t MFR_STR_LEN : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_MFRSL_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_PRDSL : (USB2422L Offset: 0x14) (R/W 8) Product String Length -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t PRD_STR_LEN : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_PRDSL_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_SERSL : (USB2422L Offset: 0x15) (R/W 8) Serial String Length -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t SER_STR_LEN : 8; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_SERSL_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_MFRSTR : (USB2422L Offset: 0x16-53) (R/W 8) Maufacturer String -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef uint16_t USB2422_MFRSTR_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_PRDSTR : (USB2422L Offset: 0x54-91) (R/W 8) Product String -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef uint16_t USB2422_PRDSTR_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_SERSTR : (USB2422L Offset: 0x92-CF) (R/W 8) Serial String -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef uint16_t USB2422_SERSTR_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_BCEN : (USB2422L Offset: 0xD0) (R/W 8) Battery Charging Enable -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t : 1; + uint8_t PORT1_BCE :1; + uint8_t PORT2_BCE :1; + uint8_t : 5; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_BCEN_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_BOOSTUP : (USB2422L Offset: 0xF6) (R/W 8) Boost Upstream -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t BOOST :2; + uint8_t : 6; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_BOOSTUP_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_BOOSTDOWN : (USB2422L Offset: 0xF8) (R/W 8) Boost Downstream -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t BOOST1 :2; + uint8_t BOOST2 :2; + uint8_t : 4; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_BOOSTDOWN_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_PRTSP : (USB2422L Offset: 0xFA) (R/W 8) Port Swap -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t : 1; + uint8_t PORT1_SP :1; + uint8_t PORT2_SP :1; + uint8_t : 5; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_PRTSP_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/* -------- USB2422_PRTR12 : (USB2422L Offset: 0xFB) (R/W 8) Port 1/2 Remap -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t PORT1_REMAP: 4; + uint8_t PORT2_REMAP: 4; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_PRTR12_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ +#define USB2422_PRTR12_DISABLE 0 +#define USB2422_PRT12_P2TOL1 1 +#define USB2422_PRT12_P2XTOL2 2 +#define USB2422_PRT12_P1TOL1 1 +#define USB2422_PRT12_P1XTOL2 2 + +/* -------- USB2422_STCD : (USB2422L Offset: 0xFF) (R/W 8) Status Command -------- */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef union { + struct { + uint8_t USB_ATTACH: 1; + uint8_t RESET: 1; + uint8_t INTF_PWRDN: 1; + uint8_t : 5; + } bit; /*!< Structure used for bit access */ + uint8_t reg; /*!< Type used for register access */ +} USB2422_STCD_Type; +#endif /* !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) */ + +/** \brief USB2422 device hardware registers */ +#if !(defined(__ASSEMBLY__) || defined(__IAR_SYSTEMS_ASM__)) +typedef struct { + USB2422_VID_Type VID; /**< \brief Offset: 0x00*/ + USB2422_PID_Type PID; /**< \brief Offset: 0x02*/ + USB2422_DID_Type DID; /**< \brief Offset: 0x04*/ + USB2422_CFG1_Type CFG1; /**< \brief Offset: 0x06*/ + USB2422_CFG2_Type CFG2; /**< \brief Offset: 0x07*/ + USB2422_CFG3_Type CFG3; /**< \brief Offset: 0x08*/ + USB2422_NRD_Type NRD; /**< \brief Offset: 0x09*/ + USB2422_PDS_Type PDS; /**< \brief Offset: 0x0A*/ + USB2422_PDB_Type PDB; /**< \brief Offset: 0x0B*/ + USB2422_MAXPS_Type MAXPS; /**< \brief Offset: 0x0C*/ + USB2422_MAXPB_Type MAXPB; /**< \brief Offset: 0x0D*/ + USB2422_HCMCS_Type HCMCS; /**< \brief Offset: 0x0E*/ + USB2422_HCMCB_Type HCMCB; /**< \brief Offset: 0x0F*/ + USB2422_PWRT_Type PWRT; /**< \brief Offset: 0x10*/ + USB2422_LANGID_LSB_Type LANGID_LSB; /**< \brief Offset: 0x11*/ + USB2422_LANGID_MSB_Type LANGID_MSB; /**< \brief Offset: 0x12*/ + USB2422_MFRSL_Type MFRSL; /**< \brief Offset: 0x13*/ + USB2422_PRDSL_Type PRDSL; /**< \brief Offset: 0x14*/ + USB2422_SERSL_Type SERSL; /**< \brief Offset: 0x15*/ + USB2422_MFRSTR_Type MFRSTR[31]; /**< \brief Offset: 0x16*/ + USB2422_PRDSTR_Type PRDSTR[31]; /**< \brief Offset: 0x54*/ + USB2422_SERSTR_Type SERSTR[31]; /**< \brief Offset: 0x92*/ + USB2422_BCEN_Type BCEN; /**< \brief Offset: 0xD0*/ + uint8_t Reserved1[0x25]; + USB2422_BOOSTUP_Type BOOSTUP; /**< \brief Offset: 0xF6*/ + uint8_t Reserved2[0x1]; + USB2422_BOOSTDOWN_Type BOOSTDOWN; /**< \brief Offset: 0xF8*/ + uint8_t Reserved3[0x1]; + USB2422_PRTSP_Type PRTSP; /**< \brief Offset: 0xFA*/ + USB2422_PRTR12_Type PRTR12; /**< \brief Offset: 0xFB*/ + uint8_t Reserved4[0x3]; + USB2422_STCD_Type STCD; /**< \brief Offset: 0xFF*/ +} Usb2422; +#endif + +#define PORT_DETECT_RETRY_INTERVAL 2000 + +#define USB_EXTRA_ADC_THRESHOLD 900 + +#define USB_EXTRA_STATE_DISABLED 0 +#define USB_EXTRA_STATE_ENABLED 1 +#define USB_EXTRA_STATE_UNKNOWN 2 +#define USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG 3 + +#define USB_HOST_PORT_1 0 +#define USB_HOST_PORT_2 1 +#define USB_HOST_PORT_UNKNOWN 2 + +extern uint8_t usb_host_port; +extern uint8_t usb_extra_state; +extern uint8_t usb_extra_manual; +extern uint8_t usb_gcr_auto; + +void USB2422_init(void); +void USB_reset(void); +void USB_configure(void); +uint16_t USB_active(void); +void USB_set_host_by_voltage(void); +uint16_t adc_get(uint8_t muxpos); +uint8_t USB2422_Port_Detect_Init(void); +void USB_HandleExtraDevice(void); +void USB_ExtraSetState(uint8_t state); + +#endif //_USB2422_H_ + diff --git a/tmk_core/protocol/arm_atsam/usb/usb_atmel.h b/tmk_core/protocol/arm_atsam/usb/usb_atmel.h new file mode 100644 index 0000000000..7febdc9ece --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb_atmel.h @@ -0,0 +1,190 @@ +/** + * \file + * + * \brief All USB VIDs and PIDs from Atmel USB applications + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _USB_ATMEL_H_ +#define _USB_ATMEL_H_ + +/** + * \defgroup usb_group USB Stack + * + * This stack includes the USB Device Stack, USB Host Stack and common + * definitions. + * @{ + */ + +//! @} + +/** + * \ingroup usb_group + * \defgroup usb_atmel_ids_group Atmel USB Identifiers + * + * This module defines Atmel PID and VIDs constants. + * + * @{ + */ + +//! \name Vendor Identifier assigned by USB org to ATMEL +#define USB_VID_ATMEL 0x03EB + +//! \name Product Identifier assigned by ATMEL to AVR applications +//! @{ + +//! \name The range from 2000h to 20FFh is reserved to the old PID for C51, MEGA, and others. +//! @{ +#define USB_PID_ATMEL_MEGA_HIDGENERIC 0x2013 +#define USB_PID_ATMEL_MEGA_HIDKEYBOARD 0x2017 +#define USB_PID_ATMEL_MEGA_CDC 0x2018 +#define USB_PID_ATMEL_MEGA_AUDIO_IN 0x2019 +#define USB_PID_ATMEL_MEGA_MS 0x201A +#define USB_PID_ATMEL_MEGA_AUDIO_IN_OUT 0x201B +#define USB_PID_ATMEL_MEGA_HIDMOUSE 0x201C +#define USB_PID_ATMEL_MEGA_HIDMOUSE_CERTIF_U4 0x201D +#define USB_PID_ATMEL_MEGA_CDC_MULTI 0x201E +#define USB_PID_ATMEL_MEGA_MS_HIDMS_HID_USBKEY 0x2022 +#define USB_PID_ATMEL_MEGA_MS_HIDMS_HID_STK525 0x2023 +#define USB_PID_ATMEL_MEGA_MS_2 0x2029 +#define USB_PID_ATMEL_MEGA_MS_HIDMS 0x202A +#define USB_PID_ATMEL_MEGA_MS_3 0x2032 +#define USB_PID_ATMEL_MEGA_LIBUSB 0x2050 +//! @} + +//! \name The range 2100h to 21FFh is reserved to PIDs for AVR Tools. +//! @{ +#define USB_PID_ATMEL_XPLAINED 0x2122 +#define USB_PID_ATMEL_XMEGA_USB_ZIGBIT_2_4GHZ 0x214A +#define USB_PID_ATMEL_XMEGA_USB_ZIGBIT_SUBGHZ 0x214B +//! @} + +//! \name The range 2300h to 23FFh is reserved to PIDs for demo from ASF1.7=> +//! @{ +#define USB_PID_ATMEL_UC3_ENUM 0x2300 +#define USB_PID_ATMEL_UC3_MS 0x2301 +#define USB_PID_ATMEL_UC3_MS_SDRAM_LOADER 0x2302 +#define USB_PID_ATMEL_UC3_EVK1100_CTRLPANEL 0x2303 +#define USB_PID_ATMEL_UC3_HID 0x2304 +#define USB_PID_ATMEL_UC3_EVK1101_CTRLPANEL_HID 0x2305 +#define USB_PID_ATMEL_UC3_EVK1101_CTRLPANEL_HID_MS 0x2306 +#define USB_PID_ATMEL_UC3_CDC 0x2307 +#define USB_PID_ATMEL_UC3_AUDIO_MICRO 0x2308 +#define USB_PID_ATMEL_UC3_CDC_DEBUG 0x2310 // Virtual Com (debug interface) on EVK11xx +#define USB_PID_ATMEL_UC3_AUDIO_SPEAKER_MICRO 0x2311 +#define USB_PID_ATMEL_UC3_CDC_MSC 0x2312 +//! @} + +//! \name The range 2400h to 24FFh is reserved to PIDs for ASF applications +//! @{ +#define USB_PID_ATMEL_ASF_HIDMOUSE 0x2400 +#define USB_PID_ATMEL_ASF_HIDKEYBOARD 0x2401 +#define USB_PID_ATMEL_ASF_HIDGENERIC 0x2402 +#define USB_PID_ATMEL_ASF_MSC 0x2403 +#define USB_PID_ATMEL_ASF_CDC 0x2404 +#define USB_PID_ATMEL_ASF_PHDC 0x2405 +#define USB_PID_ATMEL_ASF_HIDMTOUCH 0x2406 +#define USB_PID_ATMEL_ASF_MSC_HIDMOUSE 0x2420 +#define USB_PID_ATMEL_ASF_MSC_HIDS_CDC 0x2421 +#define USB_PID_ATMEL_ASF_MSC_HIDKEYBOARD 0x2422 +#define USB_PID_ATMEL_ASF_VENDOR_CLASS 0x2423 +#define USB_PID_ATMEL_ASF_MSC_CDC 0x2424 +#define USB_PID_ATMEL_ASF_TWO_CDC 0x2425 +#define USB_PID_ATMEL_ASF_SEVEN_CDC 0x2426 +#define USB_PID_ATMEL_ASF_XPLAIN_BC_POWERONLY 0x2430 +#define USB_PID_ATMEL_ASF_XPLAIN_BC_TERMINAL 0x2431 +#define USB_PID_ATMEL_ASF_XPLAIN_BC_TOUCH 0x2432 +#define USB_PID_ATMEL_ASF_AUDIO_SPEAKER 0x2433 +#define USB_PID_ATMEL_ASF_XMEGA_B1_XPLAINED 0x2434 +//! @} + +//! \name The range 2F00h to 2FFFh is reserved to official PIDs for AVR bootloaders +//! Note, !!!! don't use this range for demos or examples !!!! +//! @{ +#define USB_PID_ATMEL_DFU_ATXMEGA64C3 0x2FD6 +#define USB_PID_ATMEL_DFU_ATXMEGA128C3 0x2FD7 +#define USB_PID_ATMEL_DFU_ATXMEGA16C4 0x2FD8 +#define USB_PID_ATMEL_DFU_ATXMEGA32C4 0x2FD9 +#define USB_PID_ATMEL_DFU_ATXMEGA256C3 0x2FDA +#define USB_PID_ATMEL_DFU_ATXMEGA384C3 0x2FDB +#define USB_PID_ATMEL_DFU_ATUCL3_L4 0x2FDC +#define USB_PID_ATMEL_DFU_ATXMEGA64A4U 0x2FDD +#define USB_PID_ATMEL_DFU_ATXMEGA128A4U 0x2FDE + +#define USB_PID_ATMEL_DFU_ATXMEGA64B3 0x2FDF +#define USB_PID_ATMEL_DFU_ATXMEGA128B3 0x2FE0 +#define USB_PID_ATMEL_DFU_ATXMEGA64B1 0x2FE1 +#define USB_PID_ATMEL_DFU_ATXMEGA256A3BU 0x2FE2 +#define USB_PID_ATMEL_DFU_ATXMEGA16A4U 0x2FE3 +#define USB_PID_ATMEL_DFU_ATXMEGA32A4U 0x2FE4 +#define USB_PID_ATMEL_DFU_ATXMEGA64A3U 0x2FE5 +#define USB_PID_ATMEL_DFU_ATXMEGA128A3U 0x2FE6 +#define USB_PID_ATMEL_DFU_ATXMEGA192A3U 0x2FE7 +#define USB_PID_ATMEL_DFU_ATXMEGA64A1U 0x2FE8 +#define USB_PID_ATMEL_DFU_ATUC3D 0x2FE9 +#define USB_PID_ATMEL_DFU_ATXMEGA128B1 0x2FEA +#define USB_PID_ATMEL_DFU_AT32UC3C 0x2FEB +#define USB_PID_ATMEL_DFU_ATXMEGA256A3U 0x2FEC +#define USB_PID_ATMEL_DFU_ATXMEGA128A1U 0x2FED +#define USB_PID_ATMEL_DFU_ATMEGA8U2 0x2FEE +#define USB_PID_ATMEL_DFU_ATMEGA16U2 0x2FEF +#define USB_PID_ATMEL_DFU_ATMEGA32U2 0x2FF0 +#define USB_PID_ATMEL_DFU_AT32UC3A3 0x2FF1 +#define USB_PID_ATMEL_DFU_ATMEGA32U6 0x2FF2 +#define USB_PID_ATMEL_DFU_ATMEGA16U4 0x2FF3 +#define USB_PID_ATMEL_DFU_ATMEGA32U4 0x2FF4 +#define USB_PID_ATMEL_DFU_AT32AP7200 0x2FF5 +#define USB_PID_ATMEL_DFU_AT32UC3B 0x2FF6 +#define USB_PID_ATMEL_DFU_AT90USB82 0x2FF7 +#define USB_PID_ATMEL_DFU_AT32UC3A 0x2FF8 +#define USB_PID_ATMEL_DFU_AT90USB64 0x2FF9 +#define USB_PID_ATMEL_DFU_AT90USB162 0x2FFA +#define USB_PID_ATMEL_DFU_AT90USB128 0x2FFB +// 2FFCh to 2FFFh used by C51 family products +//! @} + +//! @} + +//! @} + + +#endif // _USB_ATMEL_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/usb_device_udd.c b/tmk_core/protocol/arm_atsam/usb/usb_device_udd.c new file mode 100644 index 0000000000..b31256df7c --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb_device_udd.c @@ -0,0 +1,1097 @@ +/** + * \file + * + * \brief USB Device wrapper layer for compliance with common driver UDD + * + * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#include "samd51j18a.h" +#include <string.h> +#include <stdlib.h> + +// Get USB device configuration +#include "conf_usb.h" +#include "udd.h" +#include "usb.h" +#include "status_codes.h" + +/** + * \ingroup usb_device_group + * \defgroup usb_device_udd_group USB Device Driver Implement (UDD) + * USB low-level driver for USB device mode + * @{ + */ +// Check USB device configuration +#ifdef USB_DEVICE_HS_SUPPORT +# error The High speed mode is not supported on this part, please remove USB_DEVICE_HS_SUPPORT in conf_usb.h +#endif + +//Note: This driver is adapted for SAMD51 + +#ifndef UDC_REMOTEWAKEUP_LPM_ENABLE +#define UDC_REMOTEWAKEUP_LPM_ENABLE() +#endif +#ifndef UDC_REMOTEWAKEUP_LPM_DISABLE +#define UDC_REMOTEWAKEUP_LPM_DISABLE() +#endif +#ifndef UDC_SUSPEND_LPM_EVENT +#define UDC_SUSPEND_LPM_EVENT() +#endif + +/* for debug text */ +#ifdef USB_DEBUG +# define dbg_print printf +#else +# define dbg_print(...) +#endif + +/** Maximum size of a transfer in multi-packet mode */ +#define UDD_ENDPOINT_MAX_TRANS ((8*1024)-1) + +/** USB software device instance structure */ +struct usb_module usb_device; + +/** + * \name Clock management + * + * @{ + */ + +#define UDD_CLOCK_GEN 0 + +static inline void udd_wait_clock_ready(void) +{ + +} + +/** + * \name Power management + * + * @{ + */ +#define udd_sleep_mode(arg) +/** @} */ + +/** + * \name Control endpoint low level management routine. + * + * This function performs control endpoint management. + * It handles the SETUP/DATA/HANDSHAKE phases of a control transaction. + * + * @{ + */ + +/** + * \brief Buffer to store the data received on control endpoint (SETUP/OUT endpoint 0) + * + * Used to avoid a RAM buffer overflow in case of the payload buffer + * is smaller than control endpoint size + */ +UDC_BSS(4) +uint8_t udd_ctrl_buffer[USB_DEVICE_EP_CTRL_SIZE]; + +/** Bit definitions about endpoint control state machine for udd_ep_control_state */ +typedef enum { + UDD_EPCTRL_SETUP = 0, //!< Wait a SETUP packet + UDD_EPCTRL_DATA_OUT = 1, //!< Wait a OUT data packet + UDD_EPCTRL_DATA_IN = 2, //!< Wait a IN data packet + UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP = 3, //!< Wait a IN ZLP packet + UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP = 4, //!< Wait a OUT ZLP packet + UDD_EPCTRL_STALL_REQ = 5, //!< STALL enabled on IN & OUT packet +} udd_ctrl_ep_state_t; + +/** Global variable to give and record information of the set up request management */ +udd_ctrl_request_t udd_g_ctrlreq; + +/** State of the endpoint control management */ +static udd_ctrl_ep_state_t udd_ep_control_state; + +/** Total number of data received/sent during data packet phase with previous payload buffers */ +static uint16_t udd_ctrl_prev_payload_nb_trans; + +/** Number of data received/sent to/from udd_g_ctrlreq.payload buffer */ +static uint16_t udd_ctrl_payload_nb_trans; + +/** @} */ + +/** + * \name Management of bulk/interrupt/isochronous endpoints + * + * The UDD manages the data transfer on endpoints: + * - Start data transfer on endpoint with USB Device DMA + * - Send a ZLP packet if requested + * - Call callback registered to signal end of transfer + * The transfer abort and stall feature are supported. + * + * @{ + */ + +/** + * \brief Buffer to store the data received on bulk/interrupt endpoints + * + * Used to avoid a RAM buffer overflow in case of the user buffer + * is smaller than endpoint size + * + * \warning The protected interrupt endpoint size is 512 bytes maximum. + * \warning The isochronous and endpoint is not protected by this system and + * the user must always use a buffer corresponding at endpoint size. + */ + +#if (defined USB_DEVICE_LOW_SPEED) +UDC_BSS(4) uint8_t udd_ep_out_cache_buffer[USB_DEVICE_MAX_EP][8]; +#elif (defined USB_DEVICE_HS_SUPPORT) +UDC_BSS(4) uint8_t udd_ep_out_cache_buffer[USB_DEVICE_MAX_EP][512]; +#else +UDC_BSS(4) uint8_t udd_ep_out_cache_buffer[USB_DEVICE_MAX_EP][64]; +#endif + +/** Structure definition about job registered on an endpoint */ +typedef struct { + union { + //! Callback to call at the end of transfer + udd_callback_trans_t call_trans; + //! Callback to call when the endpoint halt is cleared + udd_callback_halt_cleared_t call_nohalt; + }; + //! Buffer located in internal RAM to send or fill during job + uint8_t *buf; + //! Size of buffer to send or fill + iram_size_t buf_size; + //! Total number of data transferred on endpoint + iram_size_t nb_trans; + //! Endpoint size + uint16_t ep_size; + //! A job is registered on this endpoint + uint8_t busy:1; + //! A short packet is requested for this job on endpoint IN + uint8_t b_shortpacket:1; + //! The cache buffer is currently used on endpoint OUT + uint8_t b_use_out_cache_buffer:1; +} udd_ep_job_t; + +/** Array to register a job on bulk/interrupt/isochronous endpoint */ +static udd_ep_job_t udd_ep_job[2 * USB_DEVICE_MAX_EP]; + +/** @} */ + +/** + * \brief Get the detailed job by endpoint number + * \param[in] ep Endpoint Address + * \retval pointer to an udd_ep_job_t structure instance + */ +static udd_ep_job_t* udd_ep_get_job(udd_ep_id_t ep) +{ + if ((ep == 0) || (ep == 0x80)) { + return NULL; + } else { + return &udd_ep_job[(2 * (ep & USB_EP_ADDR_MASK) + ((ep & USB_EP_DIR_IN) ? 1 : 0)) - 2]; + } +} + +/** + * \brief Endpoint IN process, continue to send packets or zero length packet + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void udd_ep_trans_in_next(void* pointer) +{ + struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer; + udd_ep_id_t ep = ep_callback_para->endpoint_address; + uint16_t ep_size, nb_trans; + uint16_t next_trans; + udd_ep_id_t ep_num; + udd_ep_job_t *ptr_job; + + ptr_job = udd_ep_get_job(ep); + ep_num = ep & USB_EP_ADDR_MASK; + + ep_size = ptr_job->ep_size; + /* Update number of data transferred */ + nb_trans = ep_callback_para->sent_bytes; + ptr_job->nb_trans += nb_trans; + + /* Need to send other data */ + if (ptr_job->nb_trans != ptr_job->buf_size) { + next_trans = ptr_job->buf_size - ptr_job->nb_trans; + if (UDD_ENDPOINT_MAX_TRANS < next_trans) { + /* The USB hardware support a maximum + * transfer size of UDD_ENDPOINT_MAX_TRANS Bytes */ + next_trans = UDD_ENDPOINT_MAX_TRANS -(UDD_ENDPOINT_MAX_TRANS % ep_size); + } + /* Need ZLP, if requested and last packet is not a short packet */ + ptr_job->b_shortpacket = ptr_job->b_shortpacket && (0 == (next_trans % ep_size)); + usb_device_endpoint_write_buffer_job(&usb_device,ep_num,&ptr_job->buf[ptr_job->nb_trans],next_trans); + return; + } + + /* Need to send a ZLP after all data transfer */ + if (ptr_job->b_shortpacket) { + ptr_job->b_shortpacket = false; + /* Start new transfer */ + usb_device_endpoint_write_buffer_job(&usb_device,ep_num,&ptr_job->buf[ptr_job->nb_trans],0); + return; + } + + /* Job complete then call callback */ + ptr_job->busy = false; + if (NULL != ptr_job->call_trans) { + ptr_job->call_trans(UDD_EP_TRANSFER_OK, ptr_job->nb_trans, ep); + } +} + +/** + * \brief Endpoint OUT process, continue to receive packets or zero length packet + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void udd_ep_trans_out_next(void* pointer) +{ + struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer; + udd_ep_id_t ep = ep_callback_para->endpoint_address; + uint16_t ep_size, nb_trans; + uint16_t next_trans; + udd_ep_id_t ep_num; + udd_ep_job_t *ptr_job; + + ptr_job = udd_ep_get_job(ep); + ep_num = ep & USB_EP_ADDR_MASK; + + ep_size = ptr_job->ep_size; + /* Update number of data transferred */ + nb_trans = ep_callback_para->received_bytes; + + /* Can be necessary to copy data receive from cache buffer to user buffer */ + if (ptr_job->b_use_out_cache_buffer) { + memcpy(&ptr_job->buf[ptr_job->nb_trans], udd_ep_out_cache_buffer[ep_num - 1], ptr_job->buf_size % ep_size); + } + + /* Update number of data transferred */ + ptr_job->nb_trans += nb_trans; + if (ptr_job->nb_trans > ptr_job->buf_size) { + ptr_job->nb_trans = ptr_job->buf_size; + } + + /* If all previous data requested are received and user buffer not full + * then need to receive other data */ + if ((nb_trans == ep_callback_para->out_buffer_size) && (ptr_job->nb_trans != ptr_job->buf_size)) { + next_trans = ptr_job->buf_size - ptr_job->nb_trans; + if (UDD_ENDPOINT_MAX_TRANS < next_trans) { + /* The USB hardware support a maximum transfer size + * of UDD_ENDPOINT_MAX_TRANS Bytes */ + next_trans = UDD_ENDPOINT_MAX_TRANS - (UDD_ENDPOINT_MAX_TRANS % ep_size); + } else { + next_trans -= next_trans % ep_size; + } + + if (next_trans < ep_size) { + /* Use the cache buffer for Bulk or Interrupt size endpoint */ + ptr_job->b_use_out_cache_buffer = true; + usb_device_endpoint_read_buffer_job(&usb_device,ep_num,udd_ep_out_cache_buffer[ep_num - 1],ep_size); + } else { + usb_device_endpoint_read_buffer_job(&usb_device,ep_num,&ptr_job->buf[ptr_job->nb_trans],next_trans); + } + return; + } + + /* Job complete then call callback */ + ptr_job->busy = false; + if (NULL != ptr_job->call_trans) { + ptr_job->call_trans(UDD_EP_TRANSFER_OK, ptr_job->nb_trans, ep); + } +} + +/** + * \brief Endpoint Transfer Complete callback function, to do the next transfer depends on the direction(IN or OUT) + * \param[in] module_inst Pointer to USB module instance + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void udd_ep_transfer_process(struct usb_module *module_inst, void* pointer) +{ + struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer; + udd_ep_id_t ep = ep_callback_para->endpoint_address; + + if (ep & USB_EP_DIR_IN) { + udd_ep_trans_in_next(pointer); + } else { + udd_ep_trans_out_next(pointer); + } +} + +void udd_ep_abort(udd_ep_id_t ep) +{ + udd_ep_job_t *ptr_job; + + usb_device_endpoint_abort_job(&usb_device, ep); + + /* Job complete then call callback */ + ptr_job = udd_ep_get_job(ep); + if (!ptr_job->busy) { + return; + } + ptr_job->busy = false; + if (NULL != ptr_job->call_trans) { + /* It can be a Transfer or stall callback */ + ptr_job->call_trans(UDD_EP_TRANSFER_ABORT, ptr_job->nb_trans, ep); + } +} + +bool udd_is_high_speed(void) +{ + return false; +} + +uint16_t udd_get_frame_number(void) +{ + return usb_device_get_frame_number(&usb_device); +} + +uint16_t udd_get_micro_frame_number(void) +{ + return usb_device_get_micro_frame_number(&usb_device); +} + +void udd_ep_free(udd_ep_id_t ep) +{ + struct usb_device_endpoint_config config_ep; + usb_device_endpoint_get_config_defaults(&config_ep); + + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + udd_ep_abort(ep); + + config_ep.ep_address = ep; + config_ep.ep_type = USB_DEVICE_ENDPOINT_TYPE_DISABLE; + usb_device_endpoint_set_config(&usb_device, &config_ep); + usb_device_endpoint_unregister_callback(&usb_device,ep_num,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT); + usb_device_endpoint_disable_callback(&usb_device,ep,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT); +} + +bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes, uint16_t MaxEndpointSize) +{ + struct usb_device_endpoint_config config_ep; + usb_device_endpoint_get_config_defaults(&config_ep); + + config_ep.ep_address = ep; + + if(MaxEndpointSize <= 8) { + config_ep.ep_size = USB_ENDPOINT_8_BYTE; + } else if(MaxEndpointSize <= 16) { + config_ep.ep_size = USB_ENDPOINT_16_BYTE; + } else if(MaxEndpointSize <= 32) { + config_ep.ep_size = USB_ENDPOINT_32_BYTE; + } else if(MaxEndpointSize <= 64) { + config_ep.ep_size = USB_ENDPOINT_64_BYTE; + } else if(MaxEndpointSize <= 128) { + config_ep.ep_size = USB_ENDPOINT_128_BYTE; + } else if(MaxEndpointSize <= 256) { + config_ep.ep_size = USB_ENDPOINT_256_BYTE; + } else if(MaxEndpointSize <= 512) { + config_ep.ep_size = USB_ENDPOINT_512_BYTE; + } else if(MaxEndpointSize <= 1023) { + config_ep.ep_size = USB_ENDPOINT_1023_BYTE; + } else { + return false; + } + udd_ep_job_t *ptr_job = udd_ep_get_job(ep); + ptr_job->ep_size = MaxEndpointSize; + + bmAttributes = bmAttributes & USB_EP_TYPE_MASK; + + /* Check endpoint type */ + if(USB_EP_TYPE_ISOCHRONOUS == bmAttributes) { + config_ep.ep_type = USB_DEVICE_ENDPOINT_TYPE_ISOCHRONOUS; + } else if (USB_EP_TYPE_BULK == bmAttributes) { + config_ep.ep_type = USB_DEVICE_ENDPOINT_TYPE_BULK; + } else if (USB_EP_TYPE_INTERRUPT == bmAttributes) { + config_ep.ep_type = USB_DEVICE_ENDPOINT_TYPE_INTERRUPT; + } else { + return false; + } + + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + + if (STATUS_OK != usb_device_endpoint_set_config(&usb_device, &config_ep)) { + return false; + } + usb_device_endpoint_register_callback(&usb_device,ep_num,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT,udd_ep_transfer_process); + usb_device_endpoint_enable_callback(&usb_device,ep,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT); + usb_device_endpoint_enable_callback(&usb_device,ep,USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL); + + return true; +} + +bool udd_ep_is_halted(udd_ep_id_t ep) +{ + return usb_device_endpoint_is_halted(&usb_device, ep); +} + +bool udd_ep_set_halt(udd_ep_id_t ep) +{ + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + + if (USB_DEVICE_MAX_EP < ep_num) { + return false; + } + + usb_device_endpoint_set_halt(&usb_device, ep); + + udd_ep_abort(ep); + return true; +} + +bool udd_ep_clear_halt(udd_ep_id_t ep) +{ + udd_ep_job_t *ptr_job; + uint8_t ep_num = ep & USB_EP_ADDR_MASK; + + if (USB_DEVICE_MAX_EP < ep_num) { + return false; + } + ptr_job = udd_ep_get_job(ep); + + usb_device_endpoint_clear_halt(&usb_device, ep); + + /* If a job is register on clear halt action then execute callback */ + if (ptr_job->busy == true) { + ptr_job->busy = false; + ptr_job->call_nohalt(); + } + + return true; +} + +bool udd_ep_wait_stall_clear(udd_ep_id_t ep, udd_callback_halt_cleared_t callback) +{ + udd_ep_id_t ep_num; + udd_ep_job_t *ptr_job; + + ep_num = ep & USB_EP_ADDR_MASK; + if (USB_DEVICE_MAX_EP < ep_num) { + return false; + } + + ptr_job = udd_ep_get_job(ep); + if (ptr_job->busy == true) { + return false; /* Job already on going */ + } + + /* Wait clear halt endpoint */ + if (usb_device_endpoint_is_halted(&usb_device, ep)) { + /* Endpoint halted then registers the callback */ + ptr_job->busy = true; + ptr_job->call_nohalt = callback; + return true; + } else if (usb_device_endpoint_is_configured(&usb_device, ep)) { + callback(); /* Endpoint not halted then call directly callback */ + return true; + } else { + return false; + } +} + +/** + * \brief Control Endpoint stall sending data + */ +static void udd_ctrl_stall_data(void) +{ + udd_ep_control_state = UDD_EPCTRL_STALL_REQ; + + usb_device_endpoint_set_halt(&usb_device, USB_EP_DIR_IN); + usb_device_endpoint_clear_halt(&usb_device, USB_EP_DIR_OUT); +} + +bool udd_ep_run(udd_ep_id_t ep, bool b_shortpacket, uint8_t *buf, iram_size_t buf_size, udd_callback_trans_t callback) +{ + udd_ep_id_t ep_num; + udd_ep_job_t *ptr_job; + uint32_t irqflags; + + ep_num = ep & USB_EP_ADDR_MASK; + + if ((USB_DEVICE_MAX_EP < ep_num) || (udd_ep_is_halted(ep))) { + return false; + } + + ptr_job = udd_ep_get_job(ep); + + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + + if (ptr_job->busy == true) { + __DMB(); + __set_PRIMASK(irqflags); + return false; /* Job already on going */ + } + + ptr_job->busy = true; + __DMB(); + __set_PRIMASK(irqflags); + + /* No job running, set up a new one */ + ptr_job->buf = buf; + ptr_job->buf_size = buf_size; + ptr_job->nb_trans = 0; + ptr_job->call_trans = callback; + ptr_job->b_shortpacket = b_shortpacket; + ptr_job->b_use_out_cache_buffer = false; + + /* Initialize value to simulate a empty transfer */ + uint16_t next_trans; + + if (ep & USB_EP_DIR_IN) { + if (0 != ptr_job->buf_size) { + next_trans = ptr_job->buf_size; + if (UDD_ENDPOINT_MAX_TRANS < next_trans) { + next_trans = UDD_ENDPOINT_MAX_TRANS - (UDD_ENDPOINT_MAX_TRANS % ptr_job->ep_size); + } + ptr_job->b_shortpacket = ptr_job->b_shortpacket && (0 == (next_trans % ptr_job->ep_size)); + } else if (true == ptr_job->b_shortpacket) { + ptr_job->b_shortpacket = false; /* avoid to send zero length packet again */ + next_trans = 0; + } else { + ptr_job->busy = false; + if (NULL != ptr_job->call_trans) { + ptr_job->call_trans(UDD_EP_TRANSFER_OK, 0, ep); + } + return true; + } + return (STATUS_OK == + usb_device_endpoint_write_buffer_job(&usb_device, + ep_num,&ptr_job->buf[0],next_trans)); + } else { + if (0 != ptr_job->buf_size) { + next_trans = ptr_job->buf_size; + if (UDD_ENDPOINT_MAX_TRANS < next_trans) { + /* The USB hardware support a maximum transfer size + * of UDD_ENDPOINT_MAX_TRANS Bytes */ + next_trans = UDD_ENDPOINT_MAX_TRANS - + (UDD_ENDPOINT_MAX_TRANS % ptr_job->ep_size); + } else { + next_trans -= next_trans % ptr_job->ep_size; + } + if (next_trans < ptr_job->ep_size) { + ptr_job->b_use_out_cache_buffer = true; + return (STATUS_OK == + usb_device_endpoint_read_buffer_job(&usb_device, ep_num, + udd_ep_out_cache_buffer[ep_num - 1], + ptr_job->ep_size)); + } else { + return (STATUS_OK == + usb_device_endpoint_read_buffer_job(&usb_device, ep_num, + &ptr_job->buf[0],next_trans)); + } + } else { + ptr_job->busy = false; + if (NULL != ptr_job->call_trans) { + ptr_job->call_trans(UDD_EP_TRANSFER_OK, 0, ep); + } + return true; + } + } +} + +void udd_set_address(uint8_t address) +{ + usb_device_set_address(&usb_device,address); +} + +uint8_t udd_getaddress(void) +{ + return usb_device_get_address(&usb_device); +} + +void udd_send_remotewakeup(void) +{ + uint32_t try = 5; + udd_wait_clock_ready(); + udd_sleep_mode(UDD_STATE_IDLE); + while(2 != usb_get_state_machine_status(&usb_device) && try --) { + usb_device_send_remote_wake_up(&usb_device); + } +} + +void udd_set_setup_payload( uint8_t *payload, uint16_t payload_size ) +{ + udd_g_ctrlreq.payload = payload; + udd_g_ctrlreq.payload_size = payload_size; +} + +/** + * \brief Control Endpoint translate the data in buffer into Device Request Struct + */ +static void udd_ctrl_fetch_ram(void) +{ + udd_g_ctrlreq.req.bmRequestType = udd_ctrl_buffer[0]; + udd_g_ctrlreq.req.bRequest = udd_ctrl_buffer[1]; + udd_g_ctrlreq.req.wValue = ((uint16_t)(udd_ctrl_buffer[3]) << 8) + udd_ctrl_buffer[2]; + udd_g_ctrlreq.req.wIndex = ((uint16_t)(udd_ctrl_buffer[5]) << 8) + udd_ctrl_buffer[4]; + udd_g_ctrlreq.req.wLength = ((uint16_t)(udd_ctrl_buffer[7]) << 8) + udd_ctrl_buffer[6]; +} + +/** + * \brief Control Endpoint send out zero length packet + */ +static void udd_ctrl_send_zlp_in(void) +{ + udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP; + usb_device_endpoint_setup_buffer_job(&usb_device,udd_ctrl_buffer); + usb_device_endpoint_write_buffer_job(&usb_device,0,udd_g_ctrlreq.payload,0); +} + +/** + * \brief Process control endpoint IN transaction + */ +static void udd_ctrl_in_sent(void) +{ + static bool b_shortpacket = false; + uint16_t nb_remain; + + nb_remain = udd_g_ctrlreq.payload_size - udd_ctrl_payload_nb_trans; + + if (0 == nb_remain) { + /* All content of current buffer payload are sent Update number of total data sending by previous payload buffer */ + udd_ctrl_prev_payload_nb_trans += udd_ctrl_payload_nb_trans; + if ((udd_g_ctrlreq.req.wLength == udd_ctrl_prev_payload_nb_trans) || b_shortpacket) { + /* All data requested are transferred or a short packet has been sent, then it is the end of data phase. + * Generate an OUT ZLP for handshake phase */ + udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP; + usb_device_endpoint_setup_buffer_job(&usb_device,udd_ctrl_buffer); + return; + } + /* Need of new buffer because the data phase is not complete */ + if ((!udd_g_ctrlreq.over_under_run) || (!udd_g_ctrlreq.over_under_run())) { + /* Under run then send zlp on IN + * Here nb_remain=0, this allows to send a IN ZLP */ + } else { + /* A new payload buffer is given */ + udd_ctrl_payload_nb_trans = 0; + nb_remain = udd_g_ctrlreq.payload_size; + } + } + + /* Continue transfer and send next data */ + if (nb_remain >= USB_DEVICE_EP_CTRL_SIZE) { + nb_remain = USB_DEVICE_EP_CTRL_SIZE; + b_shortpacket = false; + } else { + b_shortpacket = true; + } + + /* Link payload buffer directly on USB hardware */ + usb_device_endpoint_write_buffer_job(&usb_device,0,udd_g_ctrlreq.payload + udd_ctrl_payload_nb_trans,nb_remain); + + udd_ctrl_payload_nb_trans += nb_remain; +} + +/** + * \brief Process control endpoint OUT transaction + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void udd_ctrl_out_received(void* pointer) +{ + struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer; + + uint16_t nb_data; + nb_data = ep_callback_para->received_bytes; /* Read data received during OUT phase */ + + if (udd_g_ctrlreq.payload_size < (udd_ctrl_payload_nb_trans + nb_data)) { + /* Payload buffer too small */ + nb_data = udd_g_ctrlreq.payload_size - udd_ctrl_payload_nb_trans; + } + + memcpy((uint8_t *) (udd_g_ctrlreq.payload + udd_ctrl_payload_nb_trans), udd_ctrl_buffer, nb_data); + udd_ctrl_payload_nb_trans += nb_data; + + if ((USB_DEVICE_EP_CTRL_SIZE != nb_data) || \ + (udd_g_ctrlreq.req.wLength <= (udd_ctrl_prev_payload_nb_trans + udd_ctrl_payload_nb_trans))) { + /* End of reception because it is a short packet + * or all data are transferred */ + + /* Before send ZLP, call intermediate callback + * in case of data receive generate a stall */ + udd_g_ctrlreq.payload_size = udd_ctrl_payload_nb_trans; + if (NULL != udd_g_ctrlreq.over_under_run) { + if (!udd_g_ctrlreq.over_under_run()) { + /* Stall ZLP */ + udd_ep_control_state = UDD_EPCTRL_STALL_REQ; + /* Stall all packets on IN & OUT control endpoint */ + udd_ep_set_halt(0); + /* Ack reception of OUT to replace NAK by a STALL */ + return; + } + } + /* Send IN ZLP to ACK setup request */ + udd_ctrl_send_zlp_in(); + return; + } + + if (udd_g_ctrlreq.payload_size == udd_ctrl_payload_nb_trans) { + /* Overrun then request a new payload buffer */ + if (!udd_g_ctrlreq.over_under_run) { + /* No callback available to request a new payload buffer + * Stall ZLP */ + udd_ep_control_state = UDD_EPCTRL_STALL_REQ; + /* Stall all packets on IN & OUT control endpoint */ + udd_ep_set_halt(0); + return; + } + if (!udd_g_ctrlreq.over_under_run()) { + /* No new payload buffer delivered + * Stall ZLP */ + udd_ep_control_state = UDD_EPCTRL_STALL_REQ; + /* Stall all packets on IN & OUT control endpoint */ + udd_ep_set_halt(0); + return; + } + /* New payload buffer available + * Update number of total data received */ + udd_ctrl_prev_payload_nb_trans += udd_ctrl_payload_nb_trans; + + /* Reinitialize reception on payload buffer */ + udd_ctrl_payload_nb_trans = 0; + } + usb_device_endpoint_read_buffer_job(&usb_device,0,udd_ctrl_buffer,USB_DEVICE_EP_CTRL_SIZE); +} + +/** + * \internal + * \brief Endpoint 0 (control) SETUP received callback + * \param[in] module_inst pointer to USB module instance + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void _usb_ep0_on_setup(struct usb_module *module_inst, void* pointer) +{ + struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer; + + if (UDD_EPCTRL_SETUP != udd_ep_control_state) { + if (NULL != udd_g_ctrlreq.callback) { + udd_g_ctrlreq.callback(); + } + udd_ep_control_state = UDD_EPCTRL_SETUP; + } + if ( 8 != ep_callback_para->received_bytes) { + udd_ctrl_stall_data(); + return; + } else { + udd_ctrl_fetch_ram(); + if (false == udc_process_setup()) { + udd_ctrl_stall_data(); + return; + } else if (Udd_setup_is_in()) { + udd_ctrl_prev_payload_nb_trans = 0; + udd_ctrl_payload_nb_trans = 0; + udd_ep_control_state = UDD_EPCTRL_DATA_IN; + usb_device_endpoint_read_buffer_job(&usb_device,0,udd_ctrl_buffer,USB_DEVICE_EP_CTRL_SIZE); + udd_ctrl_in_sent(); + } else { + if(0 == udd_g_ctrlreq.req.wLength) { + udd_ctrl_send_zlp_in(); + return; + } else { + udd_ctrl_prev_payload_nb_trans = 0; + udd_ctrl_payload_nb_trans = 0; + udd_ep_control_state = UDD_EPCTRL_DATA_OUT; + /* Initialize buffer size and enable OUT bank */ + usb_device_endpoint_read_buffer_job(&usb_device,0,udd_ctrl_buffer,USB_DEVICE_EP_CTRL_SIZE); + } + } + } +} + +/** + * \brief Control Endpoint Process when underflow condition has occurred + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void udd_ctrl_underflow(void* pointer) +{ + struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer; + + if (UDD_EPCTRL_DATA_OUT == udd_ep_control_state) { + /* Host want to stop OUT transaction + * then stop to wait OUT data phase and wait IN ZLP handshake */ + udd_ctrl_send_zlp_in(); + } else if (UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP == udd_ep_control_state) { + /* A OUT handshake is waiting by device, + * but host want extra IN data then stall extra IN data */ + usb_device_endpoint_set_halt(&usb_device, ep_callback_para->endpoint_address); + } +} + +/** + * \brief Control Endpoint Process when overflow condition has occurred + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void udd_ctrl_overflow(void* pointer) +{ + struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer; + + if (UDD_EPCTRL_DATA_IN == udd_ep_control_state) { + /* Host want to stop IN transaction + * then stop to wait IN data phase and wait OUT ZLP handshake */ + udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP; + } else if (UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP == udd_ep_control_state) { + /* A IN handshake is waiting by device, + * but host want extra OUT data then stall extra OUT data and following status stage */ + usb_device_endpoint_set_halt(&usb_device, ep_callback_para->endpoint_address); + } +} + +/** + * \internal + * \brief Control endpoint transfer fail callback function + * \param[in] module_inst Pointer to USB module instance + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void _usb_ep0_on_tansfer_fail(struct usb_module *module_inst, void* pointer) +{ + struct usb_endpoint_callback_parameter *ep_callback_para = (struct usb_endpoint_callback_parameter*)pointer; + + if(ep_callback_para->endpoint_address & USB_EP_DIR_IN) { + udd_ctrl_underflow(pointer); + } else { + udd_ctrl_overflow(pointer); + } +} + +/** + * \internal + * \brief Control endpoint transfer complete callback function + * \param[in] module_inst Pointer to USB module instance + * \param[in] pointer Pointer to the endpoint transfer status parameter struct from driver layer. + */ +static void _usb_ep0_on_tansfer_ok(struct usb_module *module_inst, void *pointer) +{ + if (UDD_EPCTRL_DATA_OUT == udd_ep_control_state) { /* handshake Out for status stage */ + udd_ctrl_out_received(pointer); + } else if (UDD_EPCTRL_DATA_IN == udd_ep_control_state) { /* handshake In for status stage */ + udd_ctrl_in_sent(); + } else { + if (NULL != udd_g_ctrlreq.callback) { + udd_g_ctrlreq.callback(); + } + udd_ep_control_state = UDD_EPCTRL_SETUP; + } +} + +/** + * \brief Enable Control Endpoint + * \param[in] module_inst Pointer to USB module instance + */ +static void udd_ctrl_ep_enable(struct usb_module *module_inst) +{ + /* USB Device Endpoint0 Configuration */ + struct usb_device_endpoint_config config_ep0; + + usb_device_endpoint_get_config_defaults(&config_ep0); + config_ep0.ep_size = (enum usb_endpoint_size)(32 - clz(((uint32_t)Min(Max(USB_DEVICE_EP_CTRL_SIZE, 8), 1024) << 1) - 1) - 1 - 3); + usb_device_endpoint_set_config(module_inst,&config_ep0); + + usb_device_endpoint_setup_buffer_job(module_inst,udd_ctrl_buffer); + + usb_device_endpoint_register_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_RXSTP, _usb_ep0_on_setup ); + usb_device_endpoint_register_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT,_usb_ep0_on_tansfer_ok ); + usb_device_endpoint_register_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL,_usb_ep0_on_tansfer_fail ); + usb_device_endpoint_enable_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_RXSTP); + usb_device_endpoint_enable_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_TRCPT); + usb_device_endpoint_enable_callback(module_inst,0,USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL); + +#ifdef USB_DEVICE_LPM_SUPPORT + // Enable LPM feature + usb_device_set_lpm_mode(module_inst, USB_DEVICE_LPM_ACK); +#endif + + udd_ep_control_state = UDD_EPCTRL_SETUP; +} + +/** + * \internal + * \brief Control endpoint Suspend callback function + * \param[in] module_inst Pointer to USB module instance + * \param[in] pointer Pointer to the callback parameter from driver layer. + */ +static void _usb_on_suspend(struct usb_module *module_inst, void *pointer) +{ + usb_device_disable_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND); + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP); + udd_sleep_mode(UDD_STATE_SUSPEND); +#ifdef UDC_SUSPEND_EVENT + UDC_SUSPEND_EVENT(); +#endif +} + +#ifdef USB_DEVICE_LPM_SUPPORT +static void _usb_device_lpm_suspend(struct usb_module *module_inst, void *pointer) +{ + dbg_print("LPM_SUSP\n"); + + uint32_t *lpm_wakeup_enable; + lpm_wakeup_enable = (uint32_t *)pointer; + + usb_device_disable_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP); + usb_device_disable_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND); + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP); + +//#warning Here the sleep mode must be choose to have a DFLL startup time < bmAttribut.HIRD + udd_sleep_mode(UDD_STATE_SUSPEND_LPM); // Enter in LPM SUSPEND mode + if ((*lpm_wakeup_enable)) { + UDC_REMOTEWAKEUP_LPM_ENABLE(); + } + if (!(*lpm_wakeup_enable)) { + UDC_REMOTEWAKEUP_LPM_DISABLE(); + } + UDC_SUSPEND_LPM_EVENT(); +} +#endif + +/** + * \internal + * \brief Control endpoint SOF callback function + * \param[in] module_inst Pointer to USB module instance + * \param[in] pointer Pointer to the callback parameter from driver layer. + */ +static void _usb_on_sof_notify(struct usb_module *module_inst, void *pointer) +{ + udc_sof_notify(); +#ifdef UDC_SOF_EVENT + UDC_SOF_EVENT(); +#endif +} + +/** + * \internal + * \brief Control endpoint Reset callback function + * \param[in] module_inst Pointer to USB module instance + * \param[in] pointer Pointer to the callback parameter from driver layer. + */ +static void _usb_on_bus_reset(struct usb_module *module_inst, void *pointer) +{ + // Reset USB Device Stack Core + udc_reset(); + usb_device_set_address(module_inst,0); + udd_ctrl_ep_enable(module_inst); +} + +/** + * \internal + * \brief Control endpoint Wakeup callback function + * \param[in] module_inst Pointer to USB module instance + * \param[in] pointer Pointer to the callback parameter from driver layer. + */ +static void _usb_on_wakeup(struct usb_module *module_inst, void *pointer) +{ + udd_wait_clock_ready(); + + usb_device_disable_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP); + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND); +#ifdef USB_DEVICE_LPM_SUPPORT + usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP, _usb_device_lpm_suspend); + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP); +#endif + udd_sleep_mode(UDD_STATE_IDLE); +#ifdef UDC_RESUME_EVENT + UDC_RESUME_EVENT(); +#endif +} + +void udd_detach(void) +{ + usb_device_detach(&usb_device); + udd_sleep_mode(UDD_STATE_SUSPEND); +} + +void udd_attach(void) +{ + udd_sleep_mode(UDD_STATE_IDLE); + usb_device_attach(&usb_device); + + usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND, _usb_on_suspend); + usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_SOF, _usb_on_sof_notify); + usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_RESET, _usb_on_bus_reset); + usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP, _usb_on_wakeup); + + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_SUSPEND); + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_SOF); + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_RESET); + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_WAKEUP); +#ifdef USB_DEVICE_LPM_SUPPORT + usb_device_register_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP, _usb_device_lpm_suspend); + usb_device_enable_callback(&usb_device, USB_DEVICE_CALLBACK_LPMSUSP); +#endif +} + +void udd_enable(void) +{ + uint32_t irqflags; + + /* To avoid USB interrupt before end of initialization */ + irqflags = __get_PRIMASK(); + __disable_irq(); + __DMB(); + + struct usb_config config_usb; + + /* USB Module configuration */ + usb_get_config_defaults(&config_usb); + config_usb.source_generator = UDD_CLOCK_GEN; + usb_init(&usb_device, USB, &config_usb); + + /* USB Module Enable */ + usb_enable(&usb_device); + + /* Check clock after enable module, request the clock */ + udd_wait_clock_ready(); + + udd_sleep_mode(UDD_STATE_SUSPEND); + + // No VBus detect, assume always high +#ifndef USB_DEVICE_ATTACH_AUTO_DISABLE + udd_attach(); +#endif + + __DMB(); + __set_PRIMASK(irqflags); +} + +void udd_disable(void) +{ + udd_detach(); + + udd_sleep_mode(UDD_STATE_OFF); +} +/** @} */ diff --git a/tmk_core/protocol/arm_atsam/usb/usb_main.h b/tmk_core/protocol/arm_atsam/usb/usb_main.h new file mode 100644 index 0000000000..76ced474dc --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb_main.h @@ -0,0 +1,103 @@ +/** + * \file + * + * \brief Declaration of main function used by HID keyboard example + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +//Enters the application in low power mode +//Callback called when USB host sets USB line in suspend state +void main_suspend_action(void); + +//Called by UDD when the USB line exit of suspend state +void main_resume_action(void); + +//Called when a start of frame is received on USB line +void main_sof_action(void); + +//Called by UDC when USB Host request to enable remote wakeup +void main_remotewakeup_enable(void); + +//Called by UDC when USB Host request to disable remote wakeup +void main_remotewakeup_disable(void); + + +#ifdef KBD +extern volatile bool main_b_kbd_enable; +bool main_kbd_enable(void); +void main_kbd_disable(void); +#endif //KBD + +#ifdef NKRO +extern volatile bool main_b_nkro_enable; +bool main_nkro_enable(void); +void main_nkro_disable(void); +#endif //NKRO + +#ifdef EXK +extern volatile bool main_b_exk_enable; +bool main_exk_enable(void); +void main_exk_disable(void); +#endif //EXK + +#ifdef CON +extern volatile bool main_b_con_enable; +bool main_con_enable(void); +void main_con_disable(void); +#endif //CON + +#ifdef MOU +extern volatile bool main_b_mou_enable; +bool main_mou_enable(void); +void main_mou_disable(void); +#endif //MOU + +#ifdef RAW +extern volatile bool main_b_raw_enable; +bool main_raw_enable(void); +void main_raw_disable(void); +#endif //RAW + +#endif // _MAIN_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/usb_protocol.h b/tmk_core/protocol/arm_atsam/usb/usb_protocol.h new file mode 100644 index 0000000000..892a7d3a5a --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb_protocol.h @@ -0,0 +1,498 @@ +/** + * \file + * + * \brief USB protocol definitions. + * + * This file contains the USB definitions and data structures provided by the + * USB 2.0 specification. + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _USB_PROTOCOL_H_ +#define _USB_PROTOCOL_H_ + +#include "usb_atmel.h" + +/** + * \ingroup usb_group + * \defgroup usb_protocol_group USB Protocol Definitions + * + * This module defines constants and data structures provided by the USB + * 2.0 specification. + * + * @{ + */ + +//! Value for field bcdUSB +#define USB_V2_0 0x0200 //!< USB Specification version 2.00 +#define USB_V2_1 0x0201 //!< USB Specification version 2.01 + +/*! \name Generic definitions (Class, subclass and protocol) + */ +//! @{ +#define NO_CLASS 0x00 +#define CLASS_VENDOR_SPECIFIC 0xFF +#define NO_SUBCLASS 0x00 +#define NO_PROTOCOL 0x00 +//! @} + +//! \name IAD (Interface Association Descriptor) constants +//! @{ +#define CLASS_IAD 0xEF +#define SUB_CLASS_IAD 0x02 +#define PROTOCOL_IAD 0x01 +//! @} + +/** + * \brief USB request data transfer direction (bmRequestType) + */ +#define USB_REQ_DIR_OUT (0<<7) //!< Host to device +#define USB_REQ_DIR_IN (1<<7) //!< Device to host +#define USB_REQ_DIR_MASK (1<<7) //!< Mask + +/** + * \brief USB request types (bmRequestType) + */ +#define USB_REQ_TYPE_STANDARD (0<<5) //!< Standard request +#define USB_REQ_TYPE_CLASS (1<<5) //!< Class-specific request +#define USB_REQ_TYPE_VENDOR (2<<5) //!< Vendor-specific request +#define USB_REQ_TYPE_MASK (3<<5) //!< Mask + +/** + * \brief USB recipient codes (bmRequestType) + */ +#define USB_REQ_RECIP_DEVICE (0<<0) //!< Recipient device +#define USB_REQ_RECIP_INTERFACE (1<<0) //!< Recipient interface +#define USB_REQ_RECIP_ENDPOINT (2<<0) //!< Recipient endpoint +#define USB_REQ_RECIP_OTHER (3<<0) //!< Recipient other +#define USB_REQ_RECIP_MASK (0x1F) //!< Mask + +/** + * \brief Standard USB requests (bRequest) + */ +enum usb_reqid { + USB_REQ_GET_STATUS = 0, + USB_REQ_CLEAR_FEATURE = 1, + USB_REQ_SET_FEATURE = 3, + USB_REQ_SET_ADDRESS = 5, + USB_REQ_GET_DESCRIPTOR = 6, + USB_REQ_SET_DESCRIPTOR = 7, + USB_REQ_GET_CONFIGURATION = 8, + USB_REQ_SET_CONFIGURATION = 9, + USB_REQ_GET_INTERFACE = 10, + USB_REQ_SET_INTERFACE = 11, + USB_REQ_SYNCH_FRAME = 12, +}; + +/** + * \brief Standard USB device status flags + * + */ +enum usb_device_status { + USB_DEV_STATUS_BUS_POWERED = 0, + USB_DEV_STATUS_SELF_POWERED = 1, + USB_DEV_STATUS_REMOTEWAKEUP = 2 +}; + +/** + * \brief Standard USB Interface status flags + * + */ +enum usb_interface_status { + USB_IFACE_STATUS_RESERVED = 0 +}; + +/** + * \brief Standard USB endpoint status flags + * + */ +enum usb_endpoint_status { + USB_EP_STATUS_HALTED = 1, +}; + +/** + * \brief Standard USB device feature flags + * + * \note valid for SetFeature request. + */ +enum usb_device_feature { + USB_DEV_FEATURE_REMOTE_WAKEUP = 1, //!< Remote wakeup enabled + USB_DEV_FEATURE_TEST_MODE = 2, //!< USB test mode + USB_DEV_FEATURE_OTG_B_HNP_ENABLE = 3, + USB_DEV_FEATURE_OTG_A_HNP_SUPPORT = 4, + USB_DEV_FEATURE_OTG_A_ALT_HNP_SUPPORT = 5 +}; + +/** + * \brief Test Mode possible on HS USB device + * + * \note valid for USB_DEV_FEATURE_TEST_MODE request. + */ +enum usb_device_hs_test_mode { + USB_DEV_TEST_MODE_J = 1, + USB_DEV_TEST_MODE_K = 2, + USB_DEV_TEST_MODE_SE0_NAK = 3, + USB_DEV_TEST_MODE_PACKET = 4, + USB_DEV_TEST_MODE_FORCE_ENABLE = 5, +}; + +/** + * \brief Standard USB endpoint feature/status flags + */ +enum usb_endpoint_feature { + USB_EP_FEATURE_HALT = 0, +}; + +/** + * \brief Standard USB Test Mode Selectors + */ +enum usb_test_mode_selector { + USB_TEST_J = 0x01, + USB_TEST_K = 0x02, + USB_TEST_SE0_NAK = 0x03, + USB_TEST_PACKET = 0x04, + USB_TEST_FORCE_ENABLE = 0x05, +}; + +/** + * \brief Standard USB descriptor types + */ +enum usb_descriptor_type { + USB_DT_DEVICE = 1, + USB_DT_CONFIGURATION = 2, + USB_DT_STRING = 3, + USB_DT_INTERFACE = 4, + USB_DT_ENDPOINT = 5, + USB_DT_DEVICE_QUALIFIER = 6, + USB_DT_OTHER_SPEED_CONFIGURATION = 7, + USB_DT_INTERFACE_POWER = 8, + USB_DT_OTG = 9, + USB_DT_IAD = 0x0B, + USB_DT_BOS = 0x0F, + USB_DT_DEVICE_CAPABILITY = 0x10, +}; + +/** + * \brief USB Device Capability types + */ +enum usb_capability_type { + USB_DC_USB20_EXTENSION = 0x02, +}; + +/** + * \brief USB Device Capability - USB 2.0 Extension + * To fill bmAttributes field of usb_capa_ext_desc_t structure. + */ +enum usb_capability_extension_attr { + USB_DC_EXT_LPM = 0x00000002, +}; + +#define HIRD_50_US 0 +#define HIRD_125_US 1 +#define HIRD_200_US 2 +#define HIRD_275_US 3 +#define HIRD_350_US 4 +#define HIRD_425_US 5 +#define HIRD_500_US 6 +#define HIRD_575_US 7 +#define HIRD_650_US 8 +#define HIRD_725_US 9 +#define HIRD_800_US 10 +#define HIRD_875_US 11 +#define HIRD_950_US 12 +#define HIRD_1025_US 13 +#define HIRD_1100_US 14 +#define HIRD_1175_US 15 + +/** Fields definition from a LPM TOKEN */ +#define USB_LPM_ATTRIBUT_BLINKSTATE_MASK (0xF << 0) +#define USB_LPM_ATTRIBUT_FIRD_MASK (0xF << 4) +#define USB_LPM_ATTRIBUT_REMOTEWAKE_MASK (1 << 8) +#define USB_LPM_ATTRIBUT_BLINKSTATE(value) ((value & 0xF) << 0) +#define USB_LPM_ATTRIBUT_FIRD(value) ((value & 0xF) << 4) +#define USB_LPM_ATTRIBUT_REMOTEWAKE(value) ((value & 1) << 8) +#define USB_LPM_ATTRIBUT_BLINKSTATE_L1 USB_LPM_ATTRIBUT_BLINKSTATE(1) + +/** + * \brief Standard USB endpoint transfer types + */ +enum usb_ep_type { + USB_EP_TYPE_CONTROL = 0x00, + USB_EP_TYPE_ISOCHRONOUS = 0x01, + USB_EP_TYPE_BULK = 0x02, + USB_EP_TYPE_INTERRUPT = 0x03, + USB_EP_TYPE_MASK = 0x03, +}; + +/** + * \brief Standard USB language IDs for string descriptors + */ +enum usb_langid { + USB_LANGID_EN_US = 0x0409, //!< English (United States) +}; + +/** + * \brief Mask selecting the index part of an endpoint address + */ +#define USB_EP_ADDR_MASK 0x0f + +//! \brief USB address identifier +typedef uint8_t usb_add_t; + +/** + * \brief Endpoint transfer direction is IN + */ +#define USB_EP_DIR_IN 0x80 + +/** + * \brief Endpoint transfer direction is OUT + */ +#define USB_EP_DIR_OUT 0x00 + +//! \brief Endpoint identifier +typedef uint8_t usb_ep_t; + +/** + * \brief Maximum length in bytes of a USB descriptor + * + * The maximum length of a USB descriptor is limited by the 8-bit + * bLength field. + */ +#define USB_MAX_DESC_LEN 255 + +/* + * 2-byte alignment requested for all USB structures. + */ +COMPILER_PACK_SET(1) + +/** + * \brief A USB Device SETUP request + * + * The data payload of SETUP packets always follows this structure. + */ +typedef struct { + uint8_t bmRequestType; + uint8_t bRequest; + le16_t wValue; + le16_t wIndex; + le16_t wLength; +} usb_setup_req_t; + +/** + * \brief Standard USB device descriptor structure + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + le16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + le16_t idVendor; + le16_t idProduct; + le16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} usb_dev_desc_t; + +/** + * \brief Standard USB device qualifier descriptor structure + * + * This descriptor contains information about the device when running at + * the "other" speed (i.e. if the device is currently operating at high + * speed, this descriptor can be used to determine what would change if + * the device was operating at full speed.) + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + le16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint8_t bNumConfigurations; + uint8_t bReserved; +} usb_dev_qual_desc_t; + +/** + * \brief USB Device BOS descriptor structure + * + * The BOS descriptor (Binary device Object Store) defines a root + * descriptor that is similar to the configuration descriptor, and is + * the base descriptor for accessing a family of related descriptors. + * A host can read a BOS descriptor and learn from the wTotalLength field + * the entire size of the device-level descriptor set, or it can read in + * the entire BOS descriptor set of device capabilities. + * The host accesses this descriptor using the GetDescriptor() request. + * The descriptor type in the GetDescriptor() request is set to BOS. + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + le16_t wTotalLength; + uint8_t bNumDeviceCaps; +} usb_dev_bos_desc_t; + + +/** + * \brief USB Device Capabilities - USB 2.0 Extension Descriptor structure + * + * Defines the set of USB 1.1-specific device level capabilities. + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + le32_t bmAttributes; +} usb_dev_capa_ext_desc_t; + +/** + * \brief USB Device LPM Descriptor structure + * + * The BOS descriptor and capabilities descriptors for LPM. + */ +typedef struct { + usb_dev_bos_desc_t bos; + usb_dev_capa_ext_desc_t capa_ext; +} usb_dev_lpm_desc_t; + +/** + * \brief Standard USB Interface Association Descriptor structure + */ +typedef struct { + uint8_t bLength; //!< size of this descriptor in bytes + uint8_t bDescriptorType; //!< INTERFACE descriptor type + uint8_t bFirstInterface; //!< Number of interface + uint8_t bInterfaceCount; //!< value to select alternate setting + uint8_t bFunctionClass; //!< Class code assigned by the USB + uint8_t bFunctionSubClass;//!< Sub-class code assigned by the USB + uint8_t bFunctionProtocol;//!< Protocol code assigned by the USB + uint8_t iFunction; //!< Index of string descriptor +} usb_association_desc_t; + + +/** + * \brief Standard USB configuration descriptor structure + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + le16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} usb_conf_desc_t; + + +#define USB_CONFIG_ATTR_MUST_SET (1 << 7) //!< Must always be set +#define USB_CONFIG_ATTR_BUS_POWERED (0 << 6) //!< Bus-powered +#define USB_CONFIG_ATTR_SELF_POWERED (1 << 6) //!< Self-powered +#define USB_CONFIG_ATTR_REMOTE_WAKEUP (1 << 5) //!< remote wakeup supported + +#define USB_CONFIG_MAX_POWER(ma) (((ma) + 1) / 2) //!< Max power in mA + +/** + * \brief Standard USB association descriptor structure + */ +typedef struct { + uint8_t bLength; //!< Size of this descriptor in bytes + uint8_t bDescriptorType; //!< Interface descriptor type + uint8_t bFirstInterface; //!< Number of interface + uint8_t bInterfaceCount; //!< value to select alternate setting + uint8_t bFunctionClass; //!< Class code assigned by the USB + uint8_t bFunctionSubClass; //!< Sub-class code assigned by the USB + uint8_t bFunctionProtocol; //!< Protocol code assigned by the USB + uint8_t iFunction; //!< Index of string descriptor +} usb_iad_desc_t; + +/** + * \brief Standard USB interface descriptor structure + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} usb_iface_desc_t; + +/** + * \brief Standard USB endpoint descriptor structure + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + le16_t wMaxPacketSize; + uint8_t bInterval; +} usb_ep_desc_t; + + +/** + * \brief A standard USB string descriptor structure + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; +} usb_str_desc_t; + +typedef struct { + usb_str_desc_t desc; + le16_t string[1]; +} usb_str_lgid_desc_t; + +COMPILER_PACK_RESET() + +//! @} + +#endif /* _USB_PROTOCOL_H_ */ diff --git a/tmk_core/protocol/arm_atsam/usb/usb_protocol_cdc.h b/tmk_core/protocol/arm_atsam/usb/usb_protocol_cdc.h new file mode 100644 index 0000000000..479f25d4e3 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb_protocol_cdc.h @@ -0,0 +1,193 @@ +/** + * \file + * + * \brief USB Communication Device Class (CDC) protocol definitions + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ +#ifndef _USB_PROTOCOL_CDC_H_ +#define _USB_PROTOCOL_CDC_H_ + +#include "compiler.h" + +#ifdef CDC + +#define CDC_CLASS_DEVICE 0x02 //!< USB Communication Device Class +#define CDC_CLASS_COMM 0x02 //!< CDC Communication Class Interface +#define CDC_CLASS_DATA 0x0A //!< CDC Data Class Interface + +#define CDC_SUBCLASS_DLCM 0x01 //!< Direct Line Control Model +#define CDC_SUBCLASS_ACM 0x02 //!< Abstract Control Model +#define CDC_SUBCLASS_TCM 0x03 //!< Telephone Control Model +#define CDC_SUBCLASS_MCCM 0x04 //!< Multi-Channel Control Model +#define CDC_SUBCLASS_CCM 0x05 //!< CAPI Control Model +#define CDC_SUBCLASS_ETH 0x06 //!< Ethernet Networking Control Model +#define CDC_SUBCLASS_ATM 0x07 //!< ATM Networking Control Model + +#define CDC_PROTOCOL_V25TER 0x01 //!< Common AT commands + +#define CDC_PROTOCOL_I430 0x30 //!< ISDN BRI +#define CDC_PROTOCOL_HDLC 0x31 //!< HDLC +#define CDC_PROTOCOL_TRANS 0x32 //!< Transparent +#define CDC_PROTOCOL_Q921M 0x50 //!< Q.921 management protocol +#define CDC_PROTOCOL_Q921 0x51 //!< Q.931 [sic] Data link protocol +#define CDC_PROTOCOL_Q921TM 0x52 //!< Q.921 TEI-multiplexor +#define CDC_PROTOCOL_V42BIS 0x90 //!< Data compression procedures +#define CDC_PROTOCOL_Q931 0x91 //!< Euro-ISDN protocol control +#define CDC_PROTOCOL_V120 0x92 //!< V.24 rate adaption to ISDN +#define CDC_PROTOCOL_CAPI20 0x93 //!< CAPI Commands +#define CDC_PROTOCOL_HOST 0xFD //!< Host based driver + +#define CDC_PROTOCOL_PUFD 0xFE + +#define CDC_CS_INTERFACE 0x24 //!< Interface Functional Descriptor +#define CDC_CS_ENDPOINT 0x25 //!< Endpoint Functional Descriptor + +#define CDC_SCS_HEADER 0x00 //!< Header Functional Descriptor +#define CDC_SCS_CALL_MGMT 0x01 //!< Call Management +#define CDC_SCS_ACM 0x02 //!< Abstract Control Management +#define CDC_SCS_UNION 0x06 //!< Union Functional Descriptor + +#define USB_REQ_CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define USB_REQ_CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define USB_REQ_CDC_SET_COMM_FEATURE 0x02 +#define USB_REQ_CDC_GET_COMM_FEATURE 0x03 +#define USB_REQ_CDC_CLEAR_COMM_FEATURE 0x04 +#define USB_REQ_CDC_SET_AUX_LINE_STATE 0x10 +#define USB_REQ_CDC_SET_HOOK_STATE 0x11 +#define USB_REQ_CDC_PULSE_SETUP 0x12 +#define USB_REQ_CDC_SEND_PULSE 0x13 +#define USB_REQ_CDC_SET_PULSE_TIME 0x14 +#define USB_REQ_CDC_RING_AUX_JACK 0x15 +#define USB_REQ_CDC_SET_LINE_CODING 0x20 +#define USB_REQ_CDC_GET_LINE_CODING 0x21 +#define USB_REQ_CDC_SET_CONTROL_LINE_STATE 0x22 +#define USB_REQ_CDC_SEND_BREAK 0x23 +#define USB_REQ_CDC_SET_RINGER_PARMS 0x30 +#define USB_REQ_CDC_GET_RINGER_PARMS 0x31 +#define USB_REQ_CDC_SET_OPERATION_PARMS 0x32 +#define USB_REQ_CDC_GET_OPERATION_PARMS 0x33 +#define USB_REQ_CDC_SET_LINE_PARMS 0x34 +#define USB_REQ_CDC_GET_LINE_PARMS 0x35 +#define USB_REQ_CDC_DIAL_DIGITS 0x36 +#define USB_REQ_CDC_SET_UNIT_PARAMETER 0x37 +#define USB_REQ_CDC_GET_UNIT_PARAMETER 0x38 +#define USB_REQ_CDC_CLEAR_UNIT_PARAMETER 0x39 +#define USB_REQ_CDC_GET_PROFILE 0x3A +#define USB_REQ_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define USB_REQ_CDC_SET_ETHERNET_POWER_MANAGEMENT_PATTERNFILTER 0x41 +#define USB_REQ_CDC_GET_ETHERNET_POWER_MANAGEMENT_PATTERNFILTER 0x42 +#define USB_REQ_CDC_SET_ETHERNET_PACKET_FILTER 0x43 +#define USB_REQ_CDC_GET_ETHERNET_STATISTIC 0x44 +#define USB_REQ_CDC_SET_ATM_DATA_FORMAT 0x50 +#define USB_REQ_CDC_GET_ATM_DEVICE_STATISTICS 0x51 +#define USB_REQ_CDC_SET_ATM_DEFAULT_VC 0x52 +#define USB_REQ_CDC_GET_ATM_VC_STATISTICS 0x53 +// Added bNotification codes according cdc spec 1.1 chapter 6.3 +#define USB_REQ_CDC_NOTIFY_RING_DETECT 0x09 +#define USB_REQ_CDC_NOTIFY_SERIAL_STATE 0x20 +#define USB_REQ_CDC_NOTIFY_CALL_STATE_CHANGE 0x28 +#define USB_REQ_CDC_NOTIFY_LINE_STATE_CHANGE 0x29 + + +#define CDC_CALL_MGMT_SUPPORTED (1 << 0) +#define CDC_CALL_MGMT_OVER_DCI (1 << 1) +#define CDC_ACM_SUPPORT_FEATURE_REQUESTS (1 << 0) +#define CDC_ACM_SUPPORT_LINE_REQUESTS (1 << 1) +#define CDC_ACM_SUPPORT_SENDBREAK_REQUESTS (1 << 2) +#define CDC_ACM_SUPPORT_NOTIFY_REQUESTS (1 << 3) + +#pragma pack(push,1) +typedef struct { + le32_t dwDTERate; + uint8_t bCharFormat; + uint8_t bParityType; + uint8_t bDataBits; +} usb_cdc_line_coding_t; +#pragma pack(pop) + +enum cdc_char_format { + CDC_STOP_BITS_1 = 0, //!< 1 stop bit + CDC_STOP_BITS_1_5 = 1, //!< 1.5 stop bits + CDC_STOP_BITS_2 = 2, //!< 2 stop bits +}; + +enum cdc_parity { + CDC_PAR_NONE = 0, //!< No parity + CDC_PAR_ODD = 1, //!< Odd parity + CDC_PAR_EVEN = 2, //!< Even parity + CDC_PAR_MARK = 3, //!< Parity forced to 0 (space) + CDC_PAR_SPACE = 4, //!< Parity forced to 1 (mark) +}; + + +typedef struct { + uint16_t value; +} usb_cdc_control_signal_t; + +#define CDC_CTRL_SIGNAL_ACTIVATE_CARRIER (1 << 1) +#define CDC_CTRL_SIGNAL_DTE_PRESENT (1 << 0) + + +typedef struct { + uint8_t bmRequestType; + uint8_t bNotification; + le16_t wValue; + le16_t wIndex; + le16_t wLength; +} usb_cdc_notify_msg_t; + +typedef struct { + usb_cdc_notify_msg_t header; + le16_t value; +} usb_cdc_notify_serial_state_t; + +#define CDC_SERIAL_STATE_DCD CPU_TO_LE16((1<<0)) +#define CDC_SERIAL_STATE_DSR CPU_TO_LE16((1<<1)) +#define CDC_SERIAL_STATE_BREAK CPU_TO_LE16((1<<2)) +#define CDC_SERIAL_STATE_RING CPU_TO_LE16((1<<3)) +#define CDC_SERIAL_STATE_FRAMING CPU_TO_LE16((1<<4)) +#define CDC_SERIAL_STATE_PARITY CPU_TO_LE16((1<<5)) +#define CDC_SERIAL_STATE_OVERRUN CPU_TO_LE16((1<<6)) + +#endif + +#endif // _USB_PROTOCOL_CDC_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/usb_protocol_hid.h b/tmk_core/protocol/arm_atsam/usb/usb_protocol_hid.h new file mode 100644 index 0000000000..c482e9c069 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb_protocol_hid.h @@ -0,0 +1,319 @@ +/** + * \file + * + * \brief USB Human Interface Device (HID) protocol definitions. + * + * Copyright (c) 2009-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a> + */ + +#ifndef _USB_PROTOCOL_HID_H_ +#define _USB_PROTOCOL_HID_H_ + +/** + * \ingroup usb_protocol_group + * \defgroup usb_hid_protocol USB Human Interface Device (HID) + * protocol definitions + * \brief USB Human Interface Device (HID) protocol definitions + * + * @{ + */ + +//! \name Possible Class value +//@{ +#define HID_CLASS 0x03 +//@} + +//! \name Possible SubClass value +//@{ +//! Interface subclass NO support BOOT protocol +#define HID_SUB_CLASS_NOBOOT 0x00 +//! Interface subclass support BOOT protocol +#define HID_SUB_CLASS_BOOT 0x01 +//@} + +//! \name Possible protocol value +//@{ +//! Protocol generic standard +#define HID_PROTOCOL_GENERIC 0x00 +//! Protocol keyboard standard +#define HID_PROTOCOL_KEYBOARD 0x01 +//! Protocol mouse standard +#define HID_PROTOCOL_MOUSE 0x02 +//@} + + +//! \brief Hid USB requests (bRequest) +enum usb_reqid_hid { + USB_REQ_HID_GET_REPORT = 0x01, + USB_REQ_HID_GET_IDLE = 0x02, + USB_REQ_HID_GET_PROTOCOL = 0x03, + USB_REQ_HID_SET_REPORT = 0x09, + USB_REQ_HID_SET_IDLE = 0x0A, + USB_REQ_HID_SET_PROTOCOL = 0x0B, +}; + +//! \brief HID USB descriptor types +enum usb_descriptor_type_hid { + USB_DT_HID = 0x21, + USB_DT_HID_REPORT = 0x22, + USB_DT_HID_PHYSICAL = 0x23, +}; + +//! \brief HID Type for report descriptor +enum usb_hid_item_report_type { + USB_HID_ITEM_REPORT_TYPE_MAIN = 0, + USB_HID_ITEM_REPORT_TYPE_GLOBAL = 1, + USB_HID_ITEM_REPORT_TYPE_LOCAL = 2, + USB_HID_ITEM_REPORT_TYPE_LONG = 3, +}; + +//! \brief HID report type +enum usb_hid_report_type { + USB_HID_REPORT_TYPE_INPUT = 1, + USB_HID_REPORT_TYPE_OUTPUT = 2, + USB_HID_REPORT_TYPE_FEATURE = 3, +}; + + +//! \brief HID protocol +enum usb_hid_protocol { + USB_HID_PROCOTOL_BOOT = 0, + USB_HID_PROCOTOL_REPORT = 1, +}; + +COMPILER_PACK_SET(1) + +//! \brief HID Descriptor +typedef struct { + uint8_t bLength; //!< Size of this descriptor in bytes + uint8_t bDescriptorType; //!< HID descriptor type + le16_t bcdHID; //!< Binary Coded Decimal Spec. release + uint8_t bCountryCode; //!< Hardware target country + uint8_t bNumDescriptors; //!< Number of HID class descriptors to follow + uint8_t bRDescriptorType; //!< Report descriptor type + le16_t wDescriptorLength; //!< Total length of Report descriptor +} usb_hid_descriptor_t; + +COMPILER_PACK_RESET() + + //! \name HID Report type + //! Used by SETUP_HID_GET_REPORT & SETUP_HID_SET_REPORT + //! @{ +#define REPORT_TYPE_INPUT 0x01 +#define REPORT_TYPE_OUTPUT 0x02 +#define REPORT_TYPE_FEATURE 0x03 + //! @} + + //! \name Constants of field DESCRIPTOR_HID + //! @{ +//! Numeric expression identifying the HID Class +//! Specification release (here V1.11) +#define USB_HID_BDC_V1_11 0x0111 +//! Numeric expression specifying the number of class descriptors +//! Note: Always at least one i.e. Report descriptor. +#define USB_HID_NUM_DESC 0x01 + + //! \name Country code + //! @{ +#define USB_HID_NO_COUNTRY_CODE 0 // Not Supported +#define USB_HID_COUNTRY_ARABIC 1 // Arabic +#define USB_HID_COUNTRY_BELGIAN 2 // Belgian +#define USB_HID_COUNTRY_CANADIAN_BILINGUAL 3 // Canadian-Bilingual +#define USB_HID_COUNTRY_CANADIAN_FRENCH 4 // Canadian-French +#define USB_HID_COUNTRY_CZECH_REPUBLIC 5 // Czech Republic +#define USB_HID_COUNTRY_DANISH 6 // Danish +#define USB_HID_COUNTRY_FINNISH 7 // Finnish +#define USB_HID_COUNTRY_FRENCH 8 // French +#define USB_HID_COUNTRY_GERMAN 9 // German +#define USB_HID_COUNTRY_GREEK 10 // Greek +#define USB_HID_COUNTRY_HEBREW 11 // Hebrew +#define USB_HID_COUNTRY_HUNGARY 12 // Hungary +#define USB_HID_COUNTRY_INTERNATIONAL_ISO 13 // International (ISO) +#define USB_HID_COUNTRY_ITALIAN 14 // Italian +#define USB_HID_COUNTRY_JAPAN_KATAKANA 15 // Japan (Katakana) +#define USB_HID_COUNTRY_KOREAN 16 // Korean +#define USB_HID_COUNTRY_LATIN_AMERICAN 17 // Latin American +#define USB_HID_COUNTRY_NETHERLANDS_DUTCH 18 // Netherlands/Dutch +#define USB_HID_COUNTRY_NORWEGIAN 19 // Norwegian +#define USB_HID_COUNTRY_PERSIAN_FARSI 20 // Persian (Farsi) +#define USB_HID_COUNTRY_POLAND 21 // Poland +#define USB_HID_COUNTRY_PORTUGUESE 22 // Portuguese +#define USB_HID_COUNTRY_RUSSIA 23 // Russia +#define USB_HID_COUNTRY_SLOVAKIA 24 // Slovakia +#define USB_HID_COUNTRY_SPANISH 25 // Spanish +#define USB_HID_COUNTRY_SWEDISH 26 // Swedish +#define USB_HID_COUNTRY_SWISS_FRENCH 27 // Swiss/French +#define USB_HID_COUNTRY_SWISS_GERMAN 28 // Swiss/German +#define USB_HID_COUNTRY_SWITZERLAND 29 // Switzerland +#define USB_HID_COUNTRY_TAIWAN 30 // Taiwan +#define USB_HID_COUNTRY_TURKISH_Q 31 // Turkish-Q +#define USB_HID_COUNTRY_UK 32 // UK +#define USB_HID_COUNTRY_US 33 // US +#define USB_HID_COUNTRY_YUGOSLAVIA 34 // Yugoslavia +#define USB_HID_COUNTRY_TURKISH_F 35 // Turkish-F + //! @} + //! @} +//! @} + + +//! \name HID KEYS values +//! @{ +#define HID_A 0x04 +#define HID_B 0x05 +#define HID_C 0x06 +#define HID_D 0x07 +#define HID_E 0x08 +#define HID_F 0x09 +#define HID_G 0x0A +#define HID_H 0x0B +#define HID_I 0x0C +#define HID_J 0x0D +#define HID_K 0x0E +#define HID_L 0x0F +#define HID_M 0x10 +#define HID_N 0x11 +#define HID_O 0x12 +#define HID_P 0x13 +#define HID_Q 0x14 +#define HID_R 0x15 +#define HID_S 0x16 +#define HID_T 0x17 +#define HID_U 0x18 +#define HID_V 0x19 +#define HID_W 0x1A +#define HID_X 0x1B +#define HID_Y 0x1C +#define HID_Z 0x1D +#define HID_1 30 +#define HID_2 31 +#define HID_3 32 +#define HID_4 33 +#define HID_5 34 +#define HID_6 35 +#define HID_7 36 +#define HID_8 37 +#define HID_9 38 +#define HID_0 39 +#define HID_ENTER 40 +#define HID_ESCAPE 41 +#define HID_BACKSPACE 42 +#define HID_TAB 43 +#define HID_SPACEBAR 44 +#define HID_UNDERSCORE 45 +#define HID_PLUS 46 +#define HID_OPEN_BRACKET 47 // { +#define HID_CLOSE_BRACKET 48 // } +#define HID_BACKSLASH 49 +#define HID_ASH 50 // # ~ +#define HID_COLON 51 // ; : +#define HID_QUOTE 52 // ' " +#define HID_TILDE 53 +#define HID_COMMA 54 +#define HID_DOT 55 +#define HID_SLASH 56 +#define HID_CAPS_LOCK 57 +#define HID_F1 58 +#define HID_F2 59 +#define HID_F3 60 +#define HID_F4 61 +#define HID_F5 62 +#define HID_F6 63 +#define HID_F7 64 +#define HID_F8 65 +#define HID_F9 66 +#define HID_F10 67 +#define HID_F11 68 +#define HID_F12 69 +#define HID_PRINTSCREEN 70 +#define HID_SCROLL_LOCK 71 +#define HID_PAUSE 72 +#define HID_INSERT 73 +#define HID_HOME 74 +#define HID_PAGEUP 75 +#define HID_DELETE 76 +#define HID_END 77 +#define HID_PAGEDOWN 78 +#define HID_RIGHT 79 +#define HID_LEFT 80 +#define HID_DOWN 81 +#define HID_UP 82 +#define HID_KEYPAD_NUM_LOCK 83 +#define HID_KEYPAD_DIVIDE 84 +#define HID_KEYPAD_AT 85 +#define HID_KEYPAD_MULTIPLY 85 +#define HID_KEYPAD_MINUS 86 +#define HID_KEYPAD_PLUS 87 +#define HID_KEYPAD_ENTER 88 +#define HID_KEYPAD_1 89 +#define HID_KEYPAD_2 90 +#define HID_KEYPAD_3 91 +#define HID_KEYPAD_4 92 +#define HID_KEYPAD_5 93 +#define HID_KEYPAD_6 94 +#define HID_KEYPAD_7 95 +#define HID_KEYPAD_8 96 +#define HID_KEYPAD_9 97 +#define HID_KEYPAD_0 98 + + //! \name HID modifier values + //! @{ +#define HID_MODIFIER_NONE 0x00 +#define HID_MODIFIER_LEFT_CTRL 0x01 +#define HID_MODIFIER_LEFT_SHIFT 0x02 +#define HID_MODIFIER_LEFT_ALT 0x04 +#define HID_MODIFIER_LEFT_UI 0x08 +#define HID_MODIFIER_RIGHT_CTRL 0x10 +#define HID_MODIFIER_RIGHT_SHIFT 0x20 +#define HID_MODIFIER_RIGHT_ALT 0x40 +#define HID_MODIFIER_RIGHT_UI 0x80 + //! @} +//! @} + +//! \name HID KEYS values +//! @{ +#define HID_LED_NUM_LOCK (1<<0) +#define HID_LED_CAPS_LOCK (1<<1) +#define HID_LED_SCROLL_LOCK (1<<2) +#define HID_LED_COMPOSE (1<<3) +#define HID_LED_KANA (1<<4) +//! @} + +#endif // _USB_PROTOCOL_HID_H_ diff --git a/tmk_core/protocol/arm_atsam/usb/usb_util.c b/tmk_core/protocol/arm_atsam/usb/usb_util.c new file mode 100644 index 0000000000..58b349362e --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb_util.c @@ -0,0 +1,59 @@ +#include "samd51j18a.h" +#include "string.h" +#include "usb_util.h" + +char digit(int d, int radix) +{ + if (d < 10) + { + return d + '0'; + } + else + { + return d - 10 + 'A'; + } +} + +int UTIL_ltoa_radix(int64_t value, char *dest, int radix) +{ + int64_t original = value; //save original value + char buf[25] = ""; + int c = sizeof(buf)-1; + int last = c; + int d; + int size; + + if (value < 0) //if it's negative, take the absolute value + value = -value; + + do //write least significant digit of value that's left + { + d = (value % radix); + buf[--c] = digit(d, radix); + value /= radix; + } while (value); + + if (original < 0) + buf[--c] = '-'; + + size = last - c + 1; //includes null at end + memcpy(dest, &buf[c], last - c + 1); + + return (size - 1); //without null termination +} + +int UTIL_ltoa(int64_t value, char *dest) +{ + return UTIL_ltoa_radix(value, dest, 10); +} + +int UTIL_itoa(int value, char *dest) +{ + return UTIL_ltoa_radix((int64_t)value, dest, 10); +} + +int UTIL_utoa(uint32_t value, char *dest) +{ + return UTIL_ltoa_radix((int64_t)value, dest, 10); +} + diff --git a/tmk_core/protocol/arm_atsam/usb/usb_util.h b/tmk_core/protocol/arm_atsam/usb/usb_util.h new file mode 100644 index 0000000000..2134d5d279 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/usb/usb_util.h @@ -0,0 +1,10 @@ +#ifndef _USB_UTIL_H_ +#define _USB_UTIL_H_ + +int UTIL_ltoa_radix(int64_t value, char *dest, int radix); +int UTIL_ltoa(int64_t value, char *dest); +int UTIL_itoa(int value, char *dest); +int UTIL_utoa(uint32_t value, char *dest); + +#endif //_USB_UTIL_H_ + |