You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2053 lines
61 KiB
C
2053 lines
61 KiB
C
4 years ago
|
/**
|
||
|
* \file
|
||
|
*
|
||
|
* \brief SAM USB HPL
|
||
|
*
|
||
|
* Copyright (c) 2015-2018 Microchip Technology Inc. and its subsidiaries.
|
||
|
*
|
||
|
* \asf_license_start
|
||
|
*
|
||
|
* \page License
|
||
|
*
|
||
|
* Subject to your compliance with these terms, you may use Microchip
|
||
|
* software and any derivatives exclusively with Microchip products.
|
||
|
* It is your responsibility to comply with third party license terms applicable
|
||
|
* to your use of third party software (including open source software) that
|
||
|
* may accompany Microchip software.
|
||
|
*
|
||
|
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
|
||
|
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
|
||
|
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
|
||
|
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
|
||
|
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
|
||
|
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
|
||
|
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
|
||
|
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
|
||
|
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
|
||
|
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
|
||
|
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
|
||
|
*
|
||
|
* \asf_license_stop
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <compiler.h>
|
||
|
#include <hal_atomic.h>
|
||
|
#include <hpl_usb.h>
|
||
|
#include <hpl_usb_device.h>
|
||
|
|
||
|
#include <hpl_usb_config.h>
|
||
|
#include <string.h>
|
||
|
#include <utils_assert.h>
|
||
|
|
||
|
#define hri_usbdevice_is_syncing(a, b) hri_usb_is_syncing(a, b)
|
||
|
#define hri_usbdevice_wait_for_sync(a, b) hri_usb_wait_for_sync(a, b)
|
||
|
#define hri_usbdevice_write_CTRLA_reg(a, b) hri_usb_write_CTRLA_reg(a, b)
|
||
|
#define hri_usbdevice_write_DESCADD_reg(a, b) hri_usb_write_DESCADD_reg(a, b)
|
||
|
#define hri_usbdevice_clear_CTRLA_ENABLE_bit(a) hri_usb_clear_CTRLA_ENABLE_bit(a)
|
||
|
#define hri_usbdevice_get_CTRLA_reg(a, b) hri_usb_get_CTRLA_reg(a, b)
|
||
|
#define hri_usbdevice_get_SYNCBUSY_reg(a, b) hri_usb_get_SYNCBUSY_reg(a, b)
|
||
|
#define hri_usbdevice_read_CTRLA_reg(a) hri_usb_read_CTRLA_reg(a)
|
||
|
#define hri_usbdevice_read_FSMSTATUS_FSMSTATE_bf(a) hri_usb_read_FSMSTATUS_FSMSTATE_bf(a)
|
||
|
|
||
|
/**
|
||
|
* \brief Dummy callback function
|
||
|
* \return Always false.
|
||
|
*/
|
||
|
static bool _dummy_func_no_return(uint32_t unused0, uint32_t unused1)
|
||
|
{
|
||
|
(void)unused0;
|
||
|
(void)unused1;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Load USB calibration value from NVM
|
||
|
*/
|
||
|
static void _usb_load_calib(void)
|
||
|
{
|
||
|
#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
|
||
|
Usb * hw = USB;
|
||
|
uint32_t pad_transn
|
||
|
= (*((uint32_t *)(NVMCTRL_OTP4) + (NVM_USB_PAD_TRANSN_POS / 32)) >> (NVM_USB_PAD_TRANSN_POS % 32))
|
||
|
& ((1 << NVM_USB_PAD_TRANSN_SIZE) - 1);
|
||
|
uint32_t pad_transp
|
||
|
= (*((uint32_t *)(NVMCTRL_OTP4) + (NVM_USB_PAD_TRANSP_POS / 32)) >> (NVM_USB_PAD_TRANSP_POS % 32))
|
||
|
& ((1 << NVM_USB_PAD_TRANSP_SIZE) - 1);
|
||
|
uint32_t pad_trim = (*((uint32_t *)(NVMCTRL_OTP4) + (NVM_USB_PAD_TRIM_POS / 32)) >> (NVM_USB_PAD_TRIM_POS % 32))
|
||
|
& ((1 << NVM_USB_PAD_TRIM_SIZE) - 1);
|
||
|
if (pad_transn == 0x1F) {
|
||
|
pad_transn = 5;
|
||
|
}
|
||
|
if (pad_transp == 0x1F) {
|
||
|
pad_transp = 29;
|
||
|
}
|
||
|
if (pad_trim == 0x7) {
|
||
|
pad_trim = 5;
|
||
|
}
|
||
|
|
||
|
hw->DEVICE.PADCAL.reg = USB_PADCAL_TRANSN(pad_transn) | USB_PADCAL_TRANSP(pad_transp) | USB_PADCAL_TRIM(pad_trim);
|
||
|
|
||
|
hw->DEVICE.QOSCTRL.bit.CQOS = 3;
|
||
|
hw->DEVICE.QOSCTRL.bit.DQOS = 3;
|
||
|
}
|
||
|
|
||
|
/** \name USB clock source management */
|
||
|
/*@{*/
|
||
|
|
||
|
/** USB clock is generated by DFLL. */
|
||
|
#define USB_CLK_SRC_DFLL 0
|
||
|
|
||
|
/** USB clock is generated by DPLL. */
|
||
|
#define USB_CLK_SRC_DPLL 1
|
||
|
|
||
|
/** Uses DFLL as USB clock source. */
|
||
|
#define CONF_USB_D_CLK_SRC USB_CLK_SRC_DFLL
|
||
|
|
||
|
/** Retry for USB remote wakeup sending. */
|
||
|
#define CONF_USB_RMT_WKUP_RETRY 5
|
||
|
|
||
|
/**
|
||
|
* \brief Wait DPLL clock to be ready
|
||
|
*/
|
||
|
static inline void _usb_d_dev_wait_dpll_rdy(void)
|
||
|
{
|
||
|
#define DPLL_READY_FLAG (SYSCTRL_DPLLSTATUS_ENABLE | SYSCTRL_DPLLSTATUS_CLKRDY | SYSCTRL_DPLLSTATUS_LOCK)
|
||
|
while (hri_sysctrl_get_DPLLSTATUS_reg(SYSCTRL, DPLL_READY_FLAG) != DPLL_READY_FLAG)
|
||
|
;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Wait DFLL clock to be ready
|
||
|
*/
|
||
|
static inline void _usb_d_dev_wait_dfll_rdy(void)
|
||
|
{
|
||
|
#define DFLL_READY_FLAG (SYSCTRL_PCLKSR_DFLLRDY | SYSCTRL_PCLKSR_DFLLLCKF | SYSCTRL_PCLKSR_DFLLLCKC)
|
||
|
/* In USB recovery mode the status is not checked */
|
||
|
if (!(SYSCTRL->DFLLCTRL.reg & SYSCTRL_DFLLCTRL_USBCRM)) {
|
||
|
while (hri_sysctrl_get_PCLKSR_reg(SYSCTRL, DFLL_READY_FLAG) != DFLL_READY_FLAG)
|
||
|
;
|
||
|
} else {
|
||
|
while (hri_sysctrl_get_PCLKSR_reg(SYSCTRL, SYSCTRL_PCLKSR_DFLLRDY) != SYSCTRL_PCLKSR_DFLLRDY)
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Wait USB source clock to be ready
|
||
|
* \param[in] clk_src Clock source, could be \ref USB_CLK_SRC_DFLL or
|
||
|
* \ref USB_CLK_SRC_DPLL.
|
||
|
*/
|
||
|
static inline void _usb_d_dev_wait_clk_rdy(const uint8_t clk_src)
|
||
|
{
|
||
|
if (clk_src == USB_CLK_SRC_DFLL) {
|
||
|
_usb_d_dev_wait_dfll_rdy();
|
||
|
} else if (clk_src == USB_CLK_SRC_DPLL) {
|
||
|
_usb_d_dev_wait_dpll_rdy();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*@}*/
|
||
|
|
||
|
/** \name USB general settings */
|
||
|
/*@{*/
|
||
|
|
||
|
/** Increase the value to be aligned. */
|
||
|
#define _usb_align_up(val) (((val)&0x3) ? (((val) + 4 - ((val)&0x3))) : (val))
|
||
|
|
||
|
/** Check if the buffer is in RAM (can DMA), or cache needed
|
||
|
* \param[in] a Buffer start address.
|
||
|
* \param[in] s Buffer size, in number of bytes.
|
||
|
* \return \c true If the buffer is in RAM.
|
||
|
*/
|
||
|
#define _IN_RAM(a, s) ((0x20000000 <= (uint32_t)(a)) && (((uint32_t)(a) + (s)) < (0x20000000 + 0x00008000)))
|
||
|
|
||
|
/** Check if the address should be placed in RAM. */
|
||
|
#define _usb_is_addr4dma(addr, size) _IN_RAM((addr), (size))
|
||
|
|
||
|
/** Check if the address is 32-bit aligned. */
|
||
|
#define _usb_is_aligned(val) (((uint32_t)(val)&0x3) == 0)
|
||
|
/*@}*/
|
||
|
|
||
|
/* Cache static configurations.
|
||
|
* By default, all OUT endpoint have 64 bytes cache. */
|
||
|
#ifndef CONF_USB_EP0_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP0_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP0_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP0_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP1_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP1_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP1_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP1_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP2_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP2_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP2_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP2_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP3_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP3_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP3_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP3_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP4_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP4_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP4_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP4_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP5_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP5_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP5_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP5_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP6_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP6_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP6_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP6_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP7_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP7_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP7_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP7_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP8_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP8_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP8_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP8_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP9_CACHE
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#define CONF_USB_EP9_CACHE 64
|
||
|
#endif
|
||
|
|
||
|
#ifndef CONF_USB_EP9_I_CACHE
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define CONF_USB_EP9_I_CACHE 0
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP0_CACHE
|
||
|
static uint32_t _usb_ep0_cache[_usb_align_up(CONF_USB_EP0_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep0_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define _usb_ep0_i_cache NULL
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP1_CACHE && CONF_USB_D_MAX_EP_N >= 1
|
||
|
static uint32_t _usb_ep1_cache[_usb_align_up(CONF_USB_EP1_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep1_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP1_I_CACHE && CONF_USB_D_MAX_EP_N >= 1
|
||
|
static uint32_t _usb_ep1_i_cache[_usb_align_up(CONF_USB_EP1_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep1_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP2_CACHE && CONF_USB_D_MAX_EP_N >= 2
|
||
|
static uint32_t _usb_ep2_cache[_usb_align_up(CONF_USB_EP2_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep2_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP2_I_CACHE && CONF_USB_D_MAX_EP_N >= 2
|
||
|
static uint32_t _usb_ep2_i_cache[_usb_align_up(CONF_USB_EP2_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep2_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP3_CACHE && CONF_USB_D_MAX_EP_N >= 3
|
||
|
static uint32_t _usb_ep3_cache[_usb_align_up(CONF_USB_EP3_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep3_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP3_I_CACHE && CONF_USB_D_MAX_EP_N >= 3
|
||
|
static uint32_t _usb_ep3_i_cache[_usb_align_up(CONF_USB_EP3_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep3_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP4_CACHE && CONF_USB_D_MAX_EP_N >= 4
|
||
|
static uint32_t _usb_ep4_cache[_usb_align_up(CONF_USB_EP4_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep4_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP4_I_CACHE && CONF_USB_D_MAX_EP_N >= 4
|
||
|
static uint32_t _usb_ep4_i_cache[_usb_align_up(CONF_USB_EP4_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep4_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP5_CACHE && CONF_USB_D_MAX_EP_N >= 5
|
||
|
static uint32_t _usb_ep5_cache[_usb_align_up(CONF_USB_EP5_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep5_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP5_I_CACHE && CONF_USB_D_MAX_EP_N >= 5
|
||
|
static uint32_t _usb_ep5_i_cache[_usb_align_up(CONF_USB_EP5_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep5_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP6_CACHE && CONF_USB_D_MAX_EP_N >= 6
|
||
|
static uint32_t _usb_ep6_cache[_usb_align_up(CONF_USB_EP6_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep6_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP6_I_CACHE && CONF_USB_D_MAX_EP_N >= 6
|
||
|
static uint32_t _usb_ep6_i_cache[_usb_align_up(CONF_USB_EP6_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep6_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP7_CACHE && CONF_USB_D_MAX_EP_N >= 7
|
||
|
static uint32_t _usb_ep7_cache[_usb_align_up(CONF_USB_EP7_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep7_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP7_I_CACHE && CONF_USB_D_MAX_EP_N >= 7
|
||
|
static uint32_t _usb_ep7_i_cache[_usb_align_up(CONF_USB_EP7_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep7_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP8_CACHE && CONF_USB_D_MAX_EP_N >= 8
|
||
|
static uint32_t _usb_ep8_cache[_usb_align_up(CONF_USB_EP8_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep8_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP8_I_CACHE && CONF_USB_D_MAX_EP_N >= 8
|
||
|
static uint32_t _usb_ep8_i_cache[_usb_align_up(CONF_USB_EP8_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep8_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for OUT transactions (none-control) or SETUP/IN/OUT
|
||
|
* transactions (control). */
|
||
|
#if CONF_USB_EP9_CACHE && CONF_USB_D_MAX_EP_N >= 9
|
||
|
static uint32_t _usb_ep9_cache[_usb_align_up(CONF_USB_EP9_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep9_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Endpoint cache buffer for IN transactions (none-control). */
|
||
|
#if CONF_USB_EP9_I_CACHE && CONF_USB_D_MAX_EP_N >= 9
|
||
|
static uint32_t _usb_ep9_i_cache[_usb_align_up(CONF_USB_EP9_I_CACHE) / 4];
|
||
|
#else
|
||
|
#define _usb_ep9_i_cache NULL
|
||
|
#endif
|
||
|
|
||
|
/** Access endpoint cache buffer for OUT transactions (none-control) or
|
||
|
* SETUP/IN/OUT transactions (control). */
|
||
|
#define _USB_EP_CACHE(n) ((void *)_usb_ep##n##_cache)
|
||
|
|
||
|
/** Access endpoint cache buffer for IN transactions (none-control). */
|
||
|
#define _USB_EP_I_CACHE(n) ((void *)_usb_ep##n##_i_cache)
|
||
|
|
||
|
/** The configuration settings for one of the endpoint hardware. */
|
||
|
struct _usb_ep_cfg_item {
|
||
|
/* Endpoint cache buffer for OUT transactions (none-control) or
|
||
|
* SETUP/IN/OUT transactions (control). */
|
||
|
void *cache;
|
||
|
/* endpoint cache buffer for IN transactions (none-control). */
|
||
|
void *i_cache;
|
||
|
/* Cache buffer size for OUT transactions (none-control) or
|
||
|
* SETUP/IN/OUT transactions (control). */
|
||
|
uint16_t size;
|
||
|
/* Cache buffer size for IN transactions (none-control). */
|
||
|
uint16_t i_size;
|
||
|
};
|
||
|
|
||
|
/** Build the endpoint configuration settings for one endpoint. */
|
||
|
#define _USB_EP_CFG_ITEM(n) \
|
||
|
{ \
|
||
|
_USB_EP_CACHE(n), _USB_EP_I_CACHE(n), CONF_USB_EP##n##_CACHE, CONF_USB_EP##n##_I_CACHE, \
|
||
|
}
|
||
|
|
||
|
/** The configuration settings for all endpoint. */
|
||
|
static const struct _usb_ep_cfg_item _usb_ep_cfgs[] = {_USB_EP_CFG_ITEM(0)
|
||
|
#if CONF_USB_D_MAX_EP_N >= 1
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(1)
|
||
|
#endif
|
||
|
#if CONF_USB_D_MAX_EP_N >= 2
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(2)
|
||
|
#endif
|
||
|
#if CONF_USB_D_MAX_EP_N >= 3
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(3)
|
||
|
#endif
|
||
|
#if CONF_USB_D_MAX_EP_N >= 4
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(4)
|
||
|
#endif
|
||
|
#if CONF_USB_D_MAX_EP_N >= 5
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(5)
|
||
|
#endif
|
||
|
#if CONF_USB_D_MAX_EP_N >= 6
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(6)
|
||
|
#endif
|
||
|
#if CONF_USB_D_MAX_EP_N >= 7
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(7)
|
||
|
#endif
|
||
|
#if CONF_USB_D_MAX_EP_N >= 8
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(8)
|
||
|
#endif
|
||
|
#if CONF_USB_D_MAX_EP_N >= 9
|
||
|
,
|
||
|
_USB_EP_CFG_ITEM(9)
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
/** \name HW specific settings and implements */
|
||
|
/*@{*/
|
||
|
|
||
|
/** Number of endpoints supported. */
|
||
|
#define USB_D_N_EP (1 + CONF_USB_D_NUM_EP_SP * 2)
|
||
|
|
||
|
/** HPL USB device endpoint struct. */
|
||
|
struct _usb_d_dev_ep {
|
||
|
/** Pointer to transaction buffer. */
|
||
|
uint8_t *trans_buf;
|
||
|
/** Transaction size. */
|
||
|
uint32_t trans_size;
|
||
|
/** Transaction transferred count. */
|
||
|
uint32_t trans_count;
|
||
|
|
||
|
/** Pointer to cache buffer, must be aligned. */
|
||
|
uint8_t *cache;
|
||
|
|
||
|
/** Endpoint size. */
|
||
|
uint16_t size;
|
||
|
/** Endpoint address. */
|
||
|
uint8_t ep;
|
||
|
/** Feature flags. */
|
||
|
union {
|
||
|
/** Interpreted by bit fields. */
|
||
|
struct {
|
||
|
/** EPCFG.ETYPE. */
|
||
|
uint8_t eptype : 3;
|
||
|
/** Stall status. */
|
||
|
uint8_t is_stalled : 1;
|
||
|
/** Transaction auto ZLP. */
|
||
|
uint8_t need_zlp : 1;
|
||
|
/** Transaction with cache */
|
||
|
uint8_t use_cache : 1;
|
||
|
/** Endpoint is busy. */
|
||
|
uint8_t is_busy : 1;
|
||
|
/** Transaction direction. */
|
||
|
uint8_t dir : 1;
|
||
|
} bits;
|
||
|
uint8_t u8;
|
||
|
} flags;
|
||
|
};
|
||
|
|
||
|
/** Check if the endpoint is used. */
|
||
|
#define _usb_d_dev_ep_is_used(ept) ((ept)->ep != 0xFF)
|
||
|
|
||
|
/** Check if the endpoint is busy doing transactions. */
|
||
|
#define _usb_d_dev_ep_is_busy(ept) ((ept)->flags.bits.is_busy)
|
||
|
|
||
|
/** Check if the endpoint is control endpoint. */
|
||
|
#define _usb_d_dev_ep_is_ctrl(ept) ((ept)->flags.bits.eptype == USB_D_EPTYPE_CTRL)
|
||
|
|
||
|
/** Check if the endpoint transactions are IN. */
|
||
|
#define _usb_d_dev_ep_is_in(ept) ((ept)->flags.bits.dir)
|
||
|
|
||
|
/** Interrupt flags for SETUP transaction. */
|
||
|
#define USB_D_SETUP_INT_FLAGS (USB_DEVICE_EPINTFLAG_RXSTP)
|
||
|
|
||
|
/** Interrupt flags for BANK1 transactions. */
|
||
|
#define USB_D_BANK1_INT_FLAGS (USB_DEVICE_EPINTFLAG_TRCPT1 | USB_DEVICE_EPINTFLAG_TRFAIL1 | USB_DEVICE_EPINTFLAG_STALL1)
|
||
|
|
||
|
/** Interrupt flags for BANK0 transactions. */
|
||
|
#define USB_D_BANK0_INT_FLAGS (USB_DEVICE_EPINTFLAG_TRCPT0 | USB_DEVICE_EPINTFLAG_TRFAIL0 | USB_DEVICE_EPINTFLAG_STALL0)
|
||
|
|
||
|
/** Interrupt flags for SETUP/IN/OUT transactions. */
|
||
|
#define USB_D_ALL_INT_FLAGS (0x7F)
|
||
|
|
||
|
/** Interrupt flags for WAKEUP event. */
|
||
|
#define USB_D_WAKEUP_INT_FLAGS (USB_DEVICE_INTFLAG_UPRSM | USB_DEVICE_INTFLAG_EORSM | USB_DEVICE_INTFLAG_WAKEUP)
|
||
|
|
||
|
/** Interrupt flags for SUSPEND event. */
|
||
|
#define USB_D_SUSPEND_INT_FLAGS (USB_DEVICE_INTFLAG_LPMSUSP | USB_DEVICE_INTFLAG_SUSPEND)
|
||
|
|
||
|
/** Max data bytes for a single DMA transfer. */
|
||
|
#define USB_D_DEV_TRANS_MAX 8192 /* 14-bits, uses 13-bits. */
|
||
|
|
||
|
/** Endpoint type setting to disable. */
|
||
|
#define USB_D_EPTYPE_DISABLE 0
|
||
|
|
||
|
/** Endpoint type setting to work as control endpoint. */
|
||
|
#define USB_D_EPTYPE_CTRL 1
|
||
|
|
||
|
/** Endpoint type setting to work as isochronous endpoint. */
|
||
|
#define USB_D_EPTYPE_ISOCH 2
|
||
|
|
||
|
/** Endpoint type setting to work as interrupt endpoint. */
|
||
|
#define USB_D_EPTYPE_INT 3
|
||
|
|
||
|
/** Endpoint type setting to work as bulk endpoint. */
|
||
|
#define USB_D_EPTYPE_BULK 4
|
||
|
|
||
|
/** Endpoint type setting for dual bank endpoint. */
|
||
|
#define USB_D_EPTYPE_DUAL 5
|
||
|
|
||
|
/** EPCFG register value for control endpoints. */
|
||
|
#define USB_D_EPCFG_CTRL 0x11
|
||
|
|
||
|
/** HPL USB device struct. */
|
||
|
struct _usb_d_dev {
|
||
|
/** Callbacks of USB device. */
|
||
|
struct _usb_d_dev_callbacks callbacks;
|
||
|
/** Endpoint transaction callbacks. */
|
||
|
struct _usb_d_dev_ep_callbacks ep_callbacks;
|
||
|
/** Endpoints (ep0 + others). */
|
||
|
struct _usb_d_dev_ep ep[USB_D_N_EP];
|
||
|
};
|
||
|
|
||
|
/** Private data for SAM0 USB peripheral.
|
||
|
*/
|
||
|
typedef struct _usb_d_dev_prvt {
|
||
|
/** USB device descriptor table for peripheral to work. */
|
||
|
UsbDeviceDescriptor desc_table[CONF_USB_D_MAX_EP_N + 1];
|
||
|
} usb_d_dev_prvt_t;
|
||
|
|
||
|
/*@}*/
|
||
|
|
||
|
/** USB device driver instance. */
|
||
|
static struct _usb_d_dev dev_inst;
|
||
|
|
||
|
/** USB device driver private data instance. */
|
||
|
static struct _usb_d_dev_prvt prvt_inst;
|
||
|
|
||
|
static void _usb_d_dev_reset_epts(void);
|
||
|
|
||
|
static void _usb_d_dev_trans_done(struct _usb_d_dev_ep *ept, const int32_t status);
|
||
|
static void _usb_d_dev_trans_stop(struct _usb_d_dev_ep *ept, bool dir, const int32_t code);
|
||
|
|
||
|
static void _usb_d_dev_in_next(struct _usb_d_dev_ep *ept, bool isr);
|
||
|
static void _usb_d_dev_out_next(struct _usb_d_dev_ep *ept, bool isr);
|
||
|
|
||
|
static inline void _usb_d_dev_trans_setup(struct _usb_d_dev_ep *ept);
|
||
|
|
||
|
/** \brief ACK the endpoint interrupt
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] flags Interrupt flags.
|
||
|
*/
|
||
|
static inline void _usbd_ep_int_ack(uint8_t epn, uint32_t flags)
|
||
|
{
|
||
|
hri_usbendpoint_clear_EPINTFLAG_reg(USB, epn, flags);
|
||
|
}
|
||
|
|
||
|
/** \brief Enable the endpoint interrupt
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] flags Interrupt flags.
|
||
|
*/
|
||
|
static inline void _usbd_ep_int_en(uint8_t epn, uint32_t flags)
|
||
|
{
|
||
|
hri_usbendpoint_set_EPINTEN_reg(USB, epn, flags);
|
||
|
}
|
||
|
|
||
|
/** \brief Disable the endpoint interrupt
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] flags Interrupt flags.
|
||
|
*/
|
||
|
static inline void _usbd_ep_int_dis(uint8_t epn, uint32_t flags)
|
||
|
{
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(USB, epn, flags);
|
||
|
}
|
||
|
|
||
|
/** \brief Check if endpoint is control endpoint
|
||
|
* \param[in] epn Endpoint number.
|
||
|
*/
|
||
|
static inline bool _usbd_ep_is_ctrl(uint8_t epn)
|
||
|
{
|
||
|
return (hri_usbendpoint_read_EPCFG_reg(USB, epn) == USB_D_EPCFG_CTRL);
|
||
|
}
|
||
|
|
||
|
/** \brief Set endpoint stall
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] st Stall status.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_stall(uint8_t epn, uint8_t bank_n, bool st)
|
||
|
{
|
||
|
if (st) {
|
||
|
hri_usbendpoint_set_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n));
|
||
|
} else {
|
||
|
hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** \brief Check if the endpoint is stalled
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \return \c true if it's stalled.
|
||
|
*/
|
||
|
static inline bool _usbd_ep_is_stalled(uint8_t epn, uint8_t bank_n)
|
||
|
{
|
||
|
Usb *hw = USB;
|
||
|
return (hri_usbendpoint_read_EPSTATUS_reg(hw, epn) & (USB_DEVICE_EPSTATUS_STALLRQ0 << bank_n));
|
||
|
}
|
||
|
|
||
|
/** \brief Check if stall has been sent from the endpoint
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \return \c true if it's sent.
|
||
|
*/
|
||
|
static inline bool _usbd_ep_is_stall_sent(uint8_t epn, uint8_t bank_n)
|
||
|
{
|
||
|
Usb *hw = USB;
|
||
|
return (hri_usbendpoint_read_EPINTFLAG_reg(hw, epn) & (USB_DEVICE_EPINTFLAG_STALL0 << bank_n));
|
||
|
}
|
||
|
|
||
|
/** \brief ACK endpoint STALL interrupt
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
*/
|
||
|
static inline void _usbd_ep_ack_stall(uint8_t epn, uint8_t bank_n)
|
||
|
{
|
||
|
_usbd_ep_int_ack(epn, (USB_DEVICE_EPINTFLAG_STALL0 << bank_n));
|
||
|
}
|
||
|
|
||
|
/** \brief Enable/disable endpoint STALL interrupt
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] en \c true to enable, \c false to disable.
|
||
|
*/
|
||
|
static inline void _usbd_ep_int_stall_en(uint8_t epn, uint8_t bank_n, const bool en)
|
||
|
{
|
||
|
if (en) {
|
||
|
_usbd_ep_int_en(epn, USB_DEVICE_EPINTFLAG_STALL0 << bank_n);
|
||
|
} else {
|
||
|
_usbd_ep_int_dis(epn, USB_DEVICE_EPINTFLAG_STALL0 << bank_n);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** \brief Stop SETUP transactions
|
||
|
* \param[in] epn Endpoint number.
|
||
|
*/
|
||
|
static inline void _usbd_ep_stop_setup(uint8_t epn)
|
||
|
{
|
||
|
hri_usbendpoint_clear_EPINTEN_RXSTP_bit(USB, epn);
|
||
|
}
|
||
|
|
||
|
/** \brief Check if SETUP packet is ready in cache
|
||
|
* \param[in] epn Endpoint number.
|
||
|
*/
|
||
|
static inline bool _usbd_ep_is_setup(uint8_t epn)
|
||
|
{
|
||
|
return hri_usbendpoint_get_EPINTFLAG_reg(USB, epn, USB_DEVICE_EPINTFLAG_RXSTP);
|
||
|
}
|
||
|
|
||
|
/** \brief ACK endpoint SETUP interrupt
|
||
|
* \param[in] epn Endpoint number.
|
||
|
*/
|
||
|
static inline void _usbd_ep_ack_setup(uint8_t epn)
|
||
|
{
|
||
|
_usbd_ep_int_ack(epn, USB_DEVICE_EPINTFLAG_RXSTP);
|
||
|
}
|
||
|
|
||
|
/** \brief Set endpoint toggle value
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] tgl Toggle value.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_toggle(uint8_t epn, uint8_t bank_n, uint8_t tgl)
|
||
|
{
|
||
|
if (tgl) {
|
||
|
hri_usbendpoint_set_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_DTGLOUT << bank_n));
|
||
|
} else {
|
||
|
hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, (USB_DEVICE_EPSTATUS_DTGLOUT << bank_n));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** \brief ACK IN/OUT complete interrupt
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
*/
|
||
|
static inline void _usbd_ep_ack_io_cpt(uint8_t epn, uint8_t bank_n)
|
||
|
{
|
||
|
_usbd_ep_int_ack(epn, USB_DEVICE_EPINTFLAG_TRCPT0 << bank_n);
|
||
|
}
|
||
|
|
||
|
/** \brief Set DMA buffer used for bank data
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] addr DMA buffer address to set.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_buf(uint8_t epn, uint8_t bank_n, uint32_t addr)
|
||
|
{
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
|
||
|
bank->ADDR.reg = addr;
|
||
|
}
|
||
|
|
||
|
/** \brief Set bank count for IN transactions
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] count Data count for IN.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_in_count(uint8_t epn, uint8_t bank_n, uint16_t count)
|
||
|
{
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
|
||
|
bank->PCKSIZE.bit.MULTI_PACKET_SIZE = count;
|
||
|
}
|
||
|
|
||
|
/** \brief Set bank size for IN transactions
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] size Data size for IN.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_in_size(uint8_t epn, uint8_t bank_n, uint16_t size)
|
||
|
{
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
|
||
|
bank->PCKSIZE.bit.BYTE_COUNT = size;
|
||
|
}
|
||
|
|
||
|
/** \brief Set bank count for OUT transaction
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] count Data count for OUT.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_out_count(uint8_t epn, uint8_t bank_n, uint16_t count)
|
||
|
{
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
|
||
|
bank->PCKSIZE.bit.BYTE_COUNT = count;
|
||
|
}
|
||
|
|
||
|
/** \brief Set bank size for OUT transactions
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] size Data size for OUT.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_out_size(uint8_t epn, uint8_t bank_n, uint16_t size)
|
||
|
{
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
|
||
|
bank->PCKSIZE.bit.MULTI_PACKET_SIZE = size;
|
||
|
}
|
||
|
|
||
|
/** Set bank size and count for IN transactions
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] size Data size.
|
||
|
* \param[in] count Initial data count.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_in_trans(uint8_t epn, uint8_t bank_n, uint32_t size, uint32_t count)
|
||
|
{
|
||
|
_usbd_ep_set_in_size(epn, bank_n, size);
|
||
|
_usbd_ep_set_in_count(epn, bank_n, count);
|
||
|
}
|
||
|
|
||
|
/** \brief Set bank size and count for OUT transaction
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] size Data size.
|
||
|
* \param[in] count Initial data count.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_out_trans(uint8_t epn, uint8_t bank_n, uint32_t size, uint32_t count)
|
||
|
{
|
||
|
_usbd_ep_set_out_size(epn, bank_n, size);
|
||
|
_usbd_ep_set_out_count(epn, bank_n, count);
|
||
|
}
|
||
|
|
||
|
/** \brief Clear bank status
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
*/
|
||
|
static inline void _usbd_ep_clear_bank_status(uint8_t epn, uint8_t bank_n)
|
||
|
{
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[bank_n];
|
||
|
bank->STATUS_BK.reg = 0;
|
||
|
}
|
||
|
|
||
|
/** Set IN ready for IN transactions
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] rdy Set to \c true to indicate IN packet ready to TX.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_in_rdy(uint8_t epn, uint8_t bank_n, const bool rdy)
|
||
|
{
|
||
|
if (rdy) {
|
||
|
hri_usbendpoint_set_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n);
|
||
|
} else {
|
||
|
hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** \brief Set bank ready for OUT transactions
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] bank_n Endpoint bank number.
|
||
|
* \param[in] rdy Set to \c true to indicate OUT bank ready to RX.
|
||
|
*/
|
||
|
static inline void _usbd_ep_set_out_rdy(uint8_t epn, uint8_t bank_n, const bool rdy)
|
||
|
{
|
||
|
if (rdy) {
|
||
|
hri_usbendpoint_clear_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n);
|
||
|
} else {
|
||
|
hri_usbendpoint_set_EPSTATUS_reg(USB, epn, USB_DEVICE_EPSTATUS_BK0RDY << bank_n);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Convert USB endpoint size to HW PCKSIZE.SIZE
|
||
|
* \param[in] n Number of bytes of endpoint size.
|
||
|
*/
|
||
|
static inline uint8_t _usbd_ep_pcksize_size(uint16_t n)
|
||
|
{
|
||
|
return (
|
||
|
(n > 512)
|
||
|
? 7
|
||
|
: ((n > 256) ? 6 : ((n > 128) ? 5 : ((n > 64) ? 4 : ((n > 32) ? 3 : ((n > 16) ? 2 : ((n > 8) ? 1 : 0)))))));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Obtain endpoint descriptor pointer
|
||
|
* \param[in] epn Endpoint number.
|
||
|
* \param[in] dir Endpoint direction.
|
||
|
*/
|
||
|
static inline struct _usb_d_dev_ep *_usb_d_dev_ept(uint8_t epn, bool dir)
|
||
|
{
|
||
|
uint8_t ep_index = (epn == 0) ? 0 : (dir ? (epn + CONF_USB_D_MAX_EP_N) : epn);
|
||
|
return &dev_inst.ep[ep_index];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles USB SOF interrupt
|
||
|
*/
|
||
|
static inline void _usb_d_dev_sof(void)
|
||
|
{
|
||
|
/* ACK SOF interrupt. */
|
||
|
hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_SOF);
|
||
|
dev_inst.callbacks.sof();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles USB LPM Suspend interrupt
|
||
|
*/
|
||
|
static inline void _usb_d_dev_lpmsusp(void)
|
||
|
{
|
||
|
uint8_t i;
|
||
|
uint32_t lpm_variable = 0;
|
||
|
|
||
|
/* ACK LPMSUSP interrupt. */
|
||
|
hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_SUSPEND_INT_FLAGS);
|
||
|
/* Change interrupt masks */
|
||
|
hri_usbdevice_clear_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS);
|
||
|
hri_usbdevice_set_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS);
|
||
|
|
||
|
/* Find LPM data */
|
||
|
for (i = 0; i < CONF_USB_D_MAX_EP_N; i++) {
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[i].DeviceDescBank[0];
|
||
|
if (bank->EXTREG.bit.SUBPID == 0x3) {
|
||
|
/* Save LPM variable */
|
||
|
lpm_variable = bank->EXTREG.bit.VARIABLE;
|
||
|
/* Clear */
|
||
|
bank->EXTREG.reg = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
dev_inst.callbacks.event(USB_EV_LPM_SUSPEND, lpm_variable);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles USB RAM Error interrupt
|
||
|
*/
|
||
|
static inline void _usb_d_dev_ramerr(void)
|
||
|
{
|
||
|
hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_RAMACER);
|
||
|
dev_inst.callbacks.event(USB_EV_ERROR, 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles USB resume/wakeup interrupts
|
||
|
*/
|
||
|
static inline void _usb_d_dev_wakeup(void)
|
||
|
{
|
||
|
hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_WAKEUP_INT_FLAGS);
|
||
|
hri_usbdevice_clear_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS);
|
||
|
hri_usbdevice_set_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS);
|
||
|
|
||
|
_usb_d_dev_wait_clk_rdy(CONF_USB_D_CLK_SRC);
|
||
|
dev_inst.callbacks.event(USB_EV_WAKEUP, 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles USB signal reset interrupt
|
||
|
*/
|
||
|
static inline void _usb_d_dev_reset(void)
|
||
|
{
|
||
|
/* EP0 will not be reseted by USB RESET, disable manually. */
|
||
|
hri_usbendpoint_write_EPCFG_reg(USB, 0, 0);
|
||
|
|
||
|
hri_usbdevice_clear_INTFLAG_reg(USB, USB_DEVICE_INTFLAG_EORST);
|
||
|
hri_usbdevice_clear_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS);
|
||
|
hri_usbdevice_set_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS);
|
||
|
|
||
|
_usb_d_dev_reset_epts();
|
||
|
dev_inst.callbacks.event(USB_EV_RESET, 0);
|
||
|
}
|
||
|
|
||
|
static inline void _usb_d_dev_suspend(void)
|
||
|
{
|
||
|
hri_usbdevice_clear_INTFLAG_reg(USB, USB_D_SUSPEND_INT_FLAGS);
|
||
|
hri_usbdevice_clear_INTEN_reg(USB, USB_D_SUSPEND_INT_FLAGS);
|
||
|
hri_usbdevice_set_INTEN_reg(USB, USB_D_WAKEUP_INT_FLAGS);
|
||
|
|
||
|
dev_inst.callbacks.event(USB_EV_SUSPEND, 0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles USB non-endpoint interrupt
|
||
|
*/
|
||
|
static inline bool _usb_d_dev_handle_nep(void)
|
||
|
{
|
||
|
bool rc = true;
|
||
|
uint16_t flags = hri_usbdevice_read_INTFLAG_reg(USB);
|
||
|
flags &= hri_usbdevice_read_INTEN_reg(USB);
|
||
|
|
||
|
if (flags & USB_DEVICE_INTFLAG_SOF) {
|
||
|
_usb_d_dev_sof();
|
||
|
return true;
|
||
|
}
|
||
|
if (flags & USB_DEVICE_INTFLAG_LPMSUSP) {
|
||
|
_usb_d_dev_lpmsusp();
|
||
|
} else if (flags & USB_DEVICE_INTFLAG_RAMACER) {
|
||
|
_usb_d_dev_ramerr();
|
||
|
} else if (flags & USB_D_WAKEUP_INT_FLAGS) {
|
||
|
_usb_d_dev_wakeup();
|
||
|
} else if (flags & USB_DEVICE_INTFLAG_EORST) {
|
||
|
_usb_d_dev_reset();
|
||
|
} else if (flags & USB_DEVICE_INTFLAG_SUSPEND) {
|
||
|
_usb_d_dev_suspend();
|
||
|
} else {
|
||
|
rc = false;
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Prepare next IN transactions
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
* \param[in] isr Invoked from ISR.
|
||
|
*/
|
||
|
static void _usb_d_dev_in_next(struct _usb_d_dev_ep *ept, bool isr)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[0];
|
||
|
uint16_t trans_count = isr ? bank[1].PCKSIZE.bit.BYTE_COUNT : 0;
|
||
|
uint16_t trans_next;
|
||
|
uint16_t last_pkt = trans_count & ((ept->size == 1023) ? ept->size : (ept->size - 1));
|
||
|
uint8_t inten = 0;
|
||
|
bool is_ctrl = _usb_d_dev_ep_is_ctrl(ept);
|
||
|
|
||
|
if (isr) {
|
||
|
_usbd_ep_ack_io_cpt(epn, 1);
|
||
|
}
|
||
|
|
||
|
ept->trans_count += trans_count;
|
||
|
/* Send more data. */
|
||
|
if (ept->trans_count < ept->trans_size) {
|
||
|
trans_next = ept->trans_size - ept->trans_count;
|
||
|
if (ept->flags.bits.use_cache) {
|
||
|
if (trans_next > ept->size) {
|
||
|
trans_next = ept->size;
|
||
|
}
|
||
|
memcpy(ept->cache, &ept->trans_buf[ept->trans_count], trans_next);
|
||
|
_usbd_ep_set_buf(epn, 1, (uint32_t)ept->cache);
|
||
|
} else {
|
||
|
if (trans_next > USB_D_DEV_TRANS_MAX) {
|
||
|
trans_next = USB_D_DEV_TRANS_MAX;
|
||
|
}
|
||
|
_usbd_ep_set_buf(epn, 1, (uint32_t)&ept->trans_buf[ept->trans_count]);
|
||
|
}
|
||
|
_usbd_ep_set_in_trans(epn, 1, trans_next, 0);
|
||
|
goto _in_tx_exec;
|
||
|
} else if (ept->flags.bits.need_zlp) {
|
||
|
ept->flags.bits.need_zlp = 0;
|
||
|
_usbd_ep_set_in_trans(epn, 1, 0, 0);
|
||
|
goto _in_tx_exec;
|
||
|
}
|
||
|
/* Complete. */
|
||
|
if (is_ctrl) {
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK1_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRCPT0);
|
||
|
} else {
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK1_INT_FLAGS);
|
||
|
}
|
||
|
|
||
|
/* No ping-pong, so ask more data without background transfer. */
|
||
|
if (last_pkt == ept->size) {
|
||
|
ept->flags.bits.is_busy = 0;
|
||
|
if (dev_inst.ep_callbacks.more(ept->ep, ept->trans_count)) {
|
||
|
/* More data added. */
|
||
|
return;
|
||
|
}
|
||
|
ept->flags.bits.is_busy = 1;
|
||
|
}
|
||
|
/* Finish normally. */
|
||
|
_usb_d_dev_trans_done(ept, USB_TRANS_DONE);
|
||
|
return;
|
||
|
|
||
|
_in_tx_exec:
|
||
|
if (!isr) {
|
||
|
if (is_ctrl) {
|
||
|
/* Control endpoint: SETUP or OUT will abort IN transaction.
|
||
|
* SETUP: terminate the IN without any notification. Trigger
|
||
|
* SETUP callback.
|
||
|
* OUT NAK: terminate IN.
|
||
|
*/
|
||
|
inten = USB_D_BANK1_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL0;
|
||
|
} else {
|
||
|
/* Initialize normal IN transaction. */
|
||
|
inten = USB_D_BANK1_INT_FLAGS;
|
||
|
}
|
||
|
hri_usbendpoint_set_EPINTEN_reg(hw, epn, inten);
|
||
|
}
|
||
|
_usbd_ep_set_in_rdy(epn, 1, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Prepare next OUT transactions
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
* \param[in] isr Invoked from ISR.
|
||
|
*/
|
||
|
static void _usb_d_dev_out_next(struct _usb_d_dev_ep *ept, bool isr)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
UsbDeviceDescBank *bank = &prvt_inst.desc_table[epn].DeviceDescBank[0];
|
||
|
uint16_t trans_size = isr ? bank->PCKSIZE.bit.MULTI_PACKET_SIZE : 0;
|
||
|
uint16_t last_trans = isr ? bank->PCKSIZE.bit.BYTE_COUNT : 0;
|
||
|
uint16_t size_mask = (ept->size == 1023) ? 1023 : (ept->size - 1);
|
||
|
uint16_t last_pkt = last_trans & size_mask;
|
||
|
uint16_t trans_next;
|
||
|
uint8_t inten;
|
||
|
bool is_ctrl = _usb_d_dev_ep_is_ctrl(ept);
|
||
|
|
||
|
if (isr) {
|
||
|
_usbd_ep_ack_io_cpt(epn, 0);
|
||
|
}
|
||
|
|
||
|
/* If cache is used, copy data to buffer. */
|
||
|
if (ept->flags.bits.use_cache && ept->trans_size) {
|
||
|
uint16_t buf_remain = ept->trans_size - ept->trans_count;
|
||
|
memcpy(&ept->trans_buf[ept->trans_count], ept->cache, (buf_remain > last_pkt) ? last_pkt : buf_remain);
|
||
|
}
|
||
|
|
||
|
/* Force wait ZLP */
|
||
|
if (ept->trans_size == 0 && ept->flags.bits.need_zlp) {
|
||
|
ept->flags.bits.need_zlp = 0;
|
||
|
ept->flags.bits.use_cache = 1;
|
||
|
_usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache);
|
||
|
_usbd_ep_set_out_trans(epn, 0, ept->size, 0);
|
||
|
goto _out_rx_exec;
|
||
|
} else if (isr && last_pkt < ept->size) {
|
||
|
/* Short packet. */
|
||
|
ept->flags.bits.need_zlp = 0;
|
||
|
ept->trans_count += last_trans;
|
||
|
} else {
|
||
|
/* Full packets. */
|
||
|
ept->trans_count += trans_size;
|
||
|
|
||
|
/* Wait more data */
|
||
|
if (ept->trans_count < ept->trans_size) {
|
||
|
/* Continue OUT */
|
||
|
trans_next = ept->trans_size - ept->trans_count;
|
||
|
if (ept->flags.bits.use_cache) {
|
||
|
/* Expect single packet each time. */
|
||
|
if (trans_next > ept->size) {
|
||
|
trans_next = ept->size;
|
||
|
}
|
||
|
_usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache);
|
||
|
} else {
|
||
|
/* Multiple packets each time. */
|
||
|
if (trans_next > ept->size) {
|
||
|
if (trans_next > USB_D_DEV_TRANS_MAX) {
|
||
|
trans_next = USB_D_DEV_TRANS_MAX;
|
||
|
} else {
|
||
|
/* Must expect multiple of ep size. */
|
||
|
trans_next -= trans_next & size_mask;
|
||
|
}
|
||
|
} else if (trans_next < ept->size) {
|
||
|
/* Last un-aligned packet should be cached. */
|
||
|
ept->flags.bits.use_cache = 1;
|
||
|
}
|
||
|
_usbd_ep_set_buf(epn, 0, (uint32_t)&ept->trans_buf[ept->trans_count]);
|
||
|
}
|
||
|
_usbd_ep_set_out_trans(epn, 0, trans_next, 0);
|
||
|
goto _out_rx_exec;
|
||
|
}
|
||
|
}
|
||
|
/* Finish normally. */
|
||
|
if (is_ctrl) {
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK0_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL1);
|
||
|
} else {
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_BANK0_INT_FLAGS);
|
||
|
}
|
||
|
/* Use ep0 out cache for next setup packets */
|
||
|
if (0 == epn) {
|
||
|
_usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache);
|
||
|
}
|
||
|
_usb_d_dev_trans_done(ept, USB_TRANS_DONE);
|
||
|
return;
|
||
|
|
||
|
_out_rx_exec:
|
||
|
if (!isr) {
|
||
|
if (is_ctrl) {
|
||
|
/* Initialize control OUT transaction. */
|
||
|
|
||
|
/* Control transfer: SETUP or IN request will abort the
|
||
|
* OUT transactions.
|
||
|
* SETUP: terminate OUT without any notification.
|
||
|
* Trigger SETUP notification.
|
||
|
* IN NAK: finish OUT normally. Notify data done.
|
||
|
*/
|
||
|
_usbd_ep_clear_bank_status(epn, 1);
|
||
|
/* Detect OUT, SETUP, NAK IN */
|
||
|
inten = USB_D_BANK0_INT_FLAGS | USB_DEVICE_EPINTFLAG_TRFAIL1;
|
||
|
} else {
|
||
|
/* Initialize normal OUT transaction. */
|
||
|
inten = USB_D_BANK0_INT_FLAGS;
|
||
|
}
|
||
|
hri_usbendpoint_set_EPINTEN_reg(hw, epn, inten);
|
||
|
}
|
||
|
_usbd_ep_set_out_rdy(epn, 0, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles setup received interrupt
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
*/
|
||
|
static void _usb_d_dev_handle_setup(struct _usb_d_dev_ep *ept)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
bool is_ctrl = _usb_d_dev_ep_is_ctrl(ept);
|
||
|
|
||
|
if (!is_ctrl) {
|
||
|
/* Should never be here! */
|
||
|
_usbd_ep_ack_setup(epn);
|
||
|
_usbd_ep_stop_setup(epn);
|
||
|
return;
|
||
|
}
|
||
|
/* Control transfer:
|
||
|
* SETUP transaction will terminate IN/OUT transaction,
|
||
|
* and start new transaction with received SETUP packet.
|
||
|
*/
|
||
|
if (_usb_d_dev_ep_is_busy(ept)) {
|
||
|
ept->flags.bits.is_busy = 0;
|
||
|
|
||
|
/* Stop transfer on either direction. */
|
||
|
_usbd_ep_set_in_rdy(epn, 1, false);
|
||
|
_usbd_ep_set_out_rdy(epn, 0, false);
|
||
|
}
|
||
|
ept->flags.bits.is_stalled = 0;
|
||
|
|
||
|
/* Clear status and notify SETUP */
|
||
|
_usbd_ep_clear_bank_status(epn, 0);
|
||
|
_usbd_ep_clear_bank_status(epn, 1);
|
||
|
_usbd_ep_int_ack(epn, USB_D_BANK0_INT_FLAGS | USB_D_BANK1_INT_FLAGS);
|
||
|
_usbd_ep_int_dis(epn, USB_D_BANK0_INT_FLAGS | USB_D_BANK1_INT_FLAGS);
|
||
|
/* Invoke callback. */
|
||
|
dev_inst.ep_callbacks.setup(ept->ep);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles stall sent interrupt
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
* \param[in] bank_n Bank number.
|
||
|
*/
|
||
|
static void _usb_d_dev_handle_stall(struct _usb_d_dev_ep *ept, const uint8_t bank_n)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
/* Clear interrupt enable. Leave status there for status check. */
|
||
|
_usbd_ep_int_stall_en(epn, bank_n, false);
|
||
|
dev_inst.ep_callbacks.done(ept->ep, USB_TRANS_STALL, ept->trans_count);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles transaction fail interrupt
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
* \param[in] bank_n Bank number.
|
||
|
*/
|
||
|
static void _usb_d_dev_handle_trfail(struct _usb_d_dev_ep *ept, const uint8_t bank_n)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
const uint8_t fail[2] = {USB_DEVICE_EPINTFLAG_TRFAIL0, USB_DEVICE_EPINTFLAG_TRFAIL1};
|
||
|
UsbDeviceDescBank *bank = prvt_inst.desc_table[epn].DeviceDescBank;
|
||
|
uint8_t eptype
|
||
|
= bank_n ? hri_usbendpoint_read_EPCFG_EPTYPE1_bf(hw, epn) : hri_usbendpoint_read_EPCFG_EPTYPE0_bf(hw, epn);
|
||
|
bool is_ctrl = _usb_d_dev_ep_is_ctrl(ept);
|
||
|
USB_DEVICE_STATUS_BK_Type st;
|
||
|
st.reg = bank[bank_n].STATUS_BK.reg;
|
||
|
|
||
|
if ((eptype == USB_D_EPTYPE_ISOCH) && st.bit.CRCERR) {
|
||
|
bank[bank_n].STATUS_BK.bit.CRCERR = 0;
|
||
|
hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]);
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]);
|
||
|
_usb_d_dev_trans_stop(ept, bank_n, USB_TRANS_ERROR);
|
||
|
} else if (st.bit.ERRORFLOW) {
|
||
|
bank[bank_n].STATUS_BK.bit.ERRORFLOW = 0;
|
||
|
hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]);
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]);
|
||
|
/* Abort control transfer. */
|
||
|
if (is_ctrl && _usb_d_dev_ep_is_busy(ept)) {
|
||
|
if (bank_n != _usb_d_dev_ep_is_in(ept)) {
|
||
|
_usb_d_dev_trans_stop(ept, _usb_d_dev_ep_is_in(ept), USB_TRANS_DONE);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
_usbd_ep_clear_bank_status(epn, bank_n);
|
||
|
hri_usbendpoint_clear_EPINTFLAG_reg(hw, epn, fail[bank_n]);
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(hw, epn, fail[bank_n]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Analyze flags for setup transaction
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
* \param[in] flags Endpoint interrupt flags.
|
||
|
*/
|
||
|
static inline void _usb_d_dev_trans_setup_isr(struct _usb_d_dev_ep *ept, const uint8_t flags)
|
||
|
{
|
||
|
/*
|
||
|
* SETPU is automatically ACKed by hardware
|
||
|
* OUT & IN should be set to NAK when checking SETUP
|
||
|
* No need to check OUT & IN status.
|
||
|
*/
|
||
|
if (flags & USB_DEVICE_EPINTFLAG_RXSTP) {
|
||
|
_usb_d_dev_handle_setup(ept);
|
||
|
} else if (flags & USB_DEVICE_EPINTFLAG_STALL1) {
|
||
|
_usb_d_dev_handle_stall(ept, 1);
|
||
|
} else if (flags & USB_DEVICE_EPINTFLAG_STALL0) {
|
||
|
_usb_d_dev_handle_stall(ept, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Analyze flags for IN transactions
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
* \param[in] flags Endpoint interrupt flags.
|
||
|
*/
|
||
|
static inline void _usb_d_dev_trans_in_isr(struct _usb_d_dev_ep *ept, const uint8_t flags)
|
||
|
{
|
||
|
/*
|
||
|
* Check IN flags
|
||
|
* If control endpoint, SETUP & OUT is checked to see if abort
|
||
|
*/
|
||
|
if (flags & USB_DEVICE_EPINTFLAG_STALL1) {
|
||
|
_usb_d_dev_handle_stall(ept, 1);
|
||
|
} else if (flags & USB_DEVICE_EPINTFLAG_TRFAIL1) {
|
||
|
_usb_d_dev_handle_trfail(ept, 1);
|
||
|
} else if (flags & USB_DEVICE_EPINTFLAG_TRCPT1) {
|
||
|
_usb_d_dev_in_next(ept, true);
|
||
|
} else if (_usb_d_dev_ep_is_ctrl(ept)) {
|
||
|
/* Check OUT NAK
|
||
|
* Check SETUP
|
||
|
*/
|
||
|
if (flags & USB_DEVICE_EPINTFLAG_TRFAIL0) {
|
||
|
_usb_d_dev_handle_trfail(ept, 0);
|
||
|
} else if (flags & USB_DEVICE_EPINTFLAG_RXSTP) {
|
||
|
_usb_d_dev_handle_setup(ept);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Analyze flags for OUT transactions
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
* \param[in] flags Endpoint interrupt flags.
|
||
|
*/
|
||
|
static inline void _usb_d_dev_trans_out_isr(struct _usb_d_dev_ep *ept, const uint8_t flags)
|
||
|
{
|
||
|
/*
|
||
|
* Check OUT flags.
|
||
|
* If control endpoint, SETUP & IN NAK is checked to see if abort
|
||
|
*/
|
||
|
if (flags & USB_DEVICE_EPINTFLAG_STALL0) {
|
||
|
_usb_d_dev_handle_stall(ept, 0);
|
||
|
} else if (flags & USB_DEVICE_EPINTFLAG_TRFAIL0) {
|
||
|
_usb_d_dev_handle_trfail(ept, 0);
|
||
|
} else if (flags & USB_DEVICE_EPINTFLAG_TRCPT0) {
|
||
|
_usb_d_dev_out_next(ept, true);
|
||
|
} else if (_usb_d_dev_ep_is_ctrl(ept)) {
|
||
|
/* Check IN NAK
|
||
|
* Check SETUP
|
||
|
*/
|
||
|
if (flags & USB_DEVICE_EPINTFLAG_TRFAIL1) {
|
||
|
_usb_d_dev_handle_trfail(ept, 1);
|
||
|
} else if (flags & USB_DEVICE_EPINTFLAG_RXSTP) {
|
||
|
_usb_d_dev_handle_setup(ept);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Handles the endpoint interrupts.
|
||
|
* \param[in] epint Endpoint interrupt summary (by bits).
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
*/
|
||
|
static inline void _usb_d_dev_handle_eps(uint32_t epint, struct _usb_d_dev_ep *ept)
|
||
|
{
|
||
|
Usb *hw = USB;
|
||
|
|
||
|
uint8_t flags, mask;
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
|
||
|
if (!(epint & (1u << epn))) {
|
||
|
return;
|
||
|
}
|
||
|
flags = hw->DEVICE.DeviceEndpoint[epn].EPINTFLAG.reg;
|
||
|
mask = hw->DEVICE.DeviceEndpoint[epn].EPINTENSET.reg;
|
||
|
flags &= mask;
|
||
|
if (flags) {
|
||
|
if ((ept->flags.bits.eptype == 0x1) && !_usb_d_dev_ep_is_busy(ept)) {
|
||
|
_usb_d_dev_trans_setup_isr(ept, flags);
|
||
|
} else if (_usb_d_dev_ep_is_in(ept)) {
|
||
|
_usb_d_dev_trans_in_isr(ept, flags);
|
||
|
} else {
|
||
|
_usb_d_dev_trans_out_isr(ept, flags);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief USB device interrupt handler
|
||
|
* \param[in] unused The parameter is not used
|
||
|
*/
|
||
|
static void _usb_d_dev_handler(void)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t i;
|
||
|
|
||
|
uint16_t epint = hw->DEVICE.EPINTSMRY.reg;
|
||
|
if (0 == epint) {
|
||
|
if (_usb_d_dev_handle_nep()) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
/* Handle endpoints */
|
||
|
for (i = 0; i < USB_D_N_EP; i++) {
|
||
|
struct _usb_d_dev_ep *ept = &dev_inst.ep[i];
|
||
|
if (ept->ep == 0xFF) {
|
||
|
continue;
|
||
|
}
|
||
|
_usb_d_dev_handle_eps(epint, ept);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Reset all endpoint software instances
|
||
|
*/
|
||
|
static void _usb_d_dev_reset_epts(void)
|
||
|
{
|
||
|
uint8_t i;
|
||
|
for (i = 0; i < USB_D_N_EP; i++) {
|
||
|
_usb_d_dev_trans_done(&dev_inst.ep[i], USB_TRANS_RESET);
|
||
|
dev_inst.ep[i].ep = 0xFF;
|
||
|
dev_inst.ep[i].flags.u8 = 0;
|
||
|
}
|
||
|
memset(prvt_inst.desc_table, 0, sizeof(UsbDeviceDescriptor) * (CONF_USB_D_MAX_EP_N + 1));
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_init(void)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t speed = CONF_USB_D_SPEED;
|
||
|
const uint8_t spdconf[4] = {
|
||
|
USB_DEVICE_CTRLB_SPDCONF(1), /* LS */
|
||
|
USB_DEVICE_CTRLB_SPDCONF(0), /* FS */
|
||
|
0,
|
||
|
0 /* Reserved */
|
||
|
};
|
||
|
|
||
|
if (!hri_usbdevice_is_syncing(hw, USB_SYNCBUSY_SWRST)) {
|
||
|
if (hri_usbdevice_get_CTRLA_reg(hw, USB_CTRLA_ENABLE)) {
|
||
|
hri_usbdevice_clear_CTRLA_ENABLE_bit(hw);
|
||
|
hri_usbdevice_wait_for_sync(hw, USB_SYNCBUSY_ENABLE);
|
||
|
}
|
||
|
hri_usbdevice_write_CTRLA_reg(hw, USB_CTRLA_SWRST);
|
||
|
}
|
||
|
hri_usbdevice_wait_for_sync(hw, USB_SYNCBUSY_SWRST);
|
||
|
|
||
|
dev_inst.callbacks.sof = (_usb_d_dev_sof_cb_t)_dummy_func_no_return;
|
||
|
dev_inst.callbacks.event = (_usb_d_dev_event_cb_t)_dummy_func_no_return;
|
||
|
|
||
|
dev_inst.ep_callbacks.setup = (_usb_d_dev_ep_cb_setup_t)_dummy_func_no_return;
|
||
|
dev_inst.ep_callbacks.more = (_usb_d_dev_ep_cb_more_t)_dummy_func_no_return;
|
||
|
dev_inst.ep_callbacks.done = (_usb_d_dev_ep_cb_done_t)_dummy_func_no_return;
|
||
|
|
||
|
_usb_d_dev_reset_epts();
|
||
|
|
||
|
_usb_load_calib();
|
||
|
|
||
|
hri_usbdevice_write_CTRLA_reg(hw, USB_CTRLA_RUNSTDBY);
|
||
|
hri_usbdevice_write_DESCADD_reg(hw, (uint32_t)prvt_inst.desc_table);
|
||
|
hri_usbdevice_write_CTRLB_reg(hw, spdconf[speed] | USB_DEVICE_CTRLB_DETACH);
|
||
|
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_deinit(void)
|
||
|
{
|
||
|
Usb *hw = USB;
|
||
|
|
||
|
while (_usb_d_dev_disable() < 0)
|
||
|
;
|
||
|
|
||
|
hri_usbdevice_write_CTRLA_reg(hw, USB_CTRLA_SWRST);
|
||
|
|
||
|
NVIC_DisableIRQ(USB_IRQn);
|
||
|
NVIC_ClearPendingIRQ(USB_IRQn);
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_enable(void)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t ctrla;
|
||
|
|
||
|
if (hri_usbdevice_get_SYNCBUSY_reg(hw, (USB_SYNCBUSY_ENABLE | USB_SYNCBUSY_SWRST))) {
|
||
|
return -USB_ERR_DENIED;
|
||
|
}
|
||
|
ctrla = hri_usbdevice_read_CTRLA_reg(hw);
|
||
|
if ((ctrla & USB_CTRLA_ENABLE) == 0) {
|
||
|
hri_usbdevice_write_CTRLA_reg(hw, ctrla | USB_CTRLA_ENABLE);
|
||
|
}
|
||
|
|
||
|
NVIC_EnableIRQ(USB_IRQn);
|
||
|
|
||
|
hri_usbdevice_set_INTEN_reg(hw,
|
||
|
USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST | USB_DEVICE_INTENSET_RAMACER
|
||
|
| USB_D_SUSPEND_INT_FLAGS);
|
||
|
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_disable(void)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t ctrla;
|
||
|
|
||
|
if (hri_usbdevice_get_SYNCBUSY_reg(hw, (USB_SYNCBUSY_ENABLE | USB_SYNCBUSY_SWRST))) {
|
||
|
return -USB_ERR_DENIED;
|
||
|
}
|
||
|
|
||
|
ctrla = hri_usbdevice_read_CTRLA_reg(hw);
|
||
|
if (ctrla & USB_CTRLA_ENABLE) {
|
||
|
hri_usbdevice_write_CTRLA_reg(hw, ctrla & ~USB_CTRLA_ENABLE);
|
||
|
}
|
||
|
NVIC_DisableIRQ(USB_IRQn);
|
||
|
|
||
|
hri_usbdevice_clear_INTEN_reg(hw,
|
||
|
USB_DEVICE_INTENSET_SOF | USB_DEVICE_INTENSET_EORST | USB_DEVICE_INTENSET_RAMACER
|
||
|
| USB_D_SUSPEND_INT_FLAGS | USB_D_WAKEUP_INT_FLAGS);
|
||
|
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_attach(void)
|
||
|
{
|
||
|
hri_usbdevice_clear_CTRLB_DETACH_bit(USB);
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_detach(void)
|
||
|
{
|
||
|
hri_usbdevice_set_CTRLB_DETACH_bit(USB);
|
||
|
}
|
||
|
|
||
|
#ifndef USB_FSMSTATUS_FSMSTATE_ON
|
||
|
#define USB_FSMSTATUS_FSMSTATE_ON USB_FSMSTATUS_FSMSTATE(2ul)
|
||
|
#endif
|
||
|
void _usb_d_dev_send_remotewakeup(void)
|
||
|
{
|
||
|
uint32_t retry = CONF_USB_RMT_WKUP_RETRY;
|
||
|
_usb_d_dev_wait_clk_rdy(CONF_USB_D_CLK_SRC);
|
||
|
while ((USB_FSMSTATUS_FSMSTATE_ON != hri_usbdevice_read_FSMSTATUS_FSMSTATE_bf(USB)) && (retry--)) {
|
||
|
USB->DEVICE.CTRLB.bit.UPRSM = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
enum usb_speed _usb_d_dev_get_speed(void)
|
||
|
{
|
||
|
uint8_t sp = (enum usb_speed)hri_usbdevice_read_STATUS_SPEED_bf(USB);
|
||
|
const enum usb_speed speed[2] = {USB_SPEED_FS, USB_SPEED_LS};
|
||
|
|
||
|
return speed[sp];
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_set_address(uint8_t addr)
|
||
|
{
|
||
|
hri_usbdevice_write_DADD_reg(USB, USB_DEVICE_DADD_ADDEN | USB_DEVICE_DADD_DADD(addr));
|
||
|
}
|
||
|
|
||
|
uint8_t _usb_d_dev_get_address(void)
|
||
|
{
|
||
|
uint8_t addr = hri_usbdevice_read_DADD_DADD_bf(USB);
|
||
|
return addr;
|
||
|
}
|
||
|
|
||
|
uint16_t _usb_d_dev_get_frame_n(void)
|
||
|
{
|
||
|
uint16_t fn = hri_usbdevice_read_FNUM_FNUM_bf(USB);
|
||
|
return fn;
|
||
|
}
|
||
|
|
||
|
uint8_t _usb_d_dev_get_uframe_n(void)
|
||
|
{
|
||
|
uint8_t ufn = hri_usbdevice_read_FNUM_MFNUM_bf(USB);
|
||
|
return ufn;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Start a setup transaction
|
||
|
* \param[in] ept Endpoint information.
|
||
|
*/
|
||
|
static inline void _usb_d_dev_trans_setup(struct _usb_d_dev_ep *ept)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
|
||
|
_usbd_ep_set_buf(epn, 0, (uint32_t)ept->cache);
|
||
|
_usbd_ep_set_out_trans(epn, 0, ept->size, 0);
|
||
|
|
||
|
hri_usbendpoint_clear_EPSTATUS_reg(hw, epn, USB_DEVICE_EPSTATUS_STALLRQ(0x3) | USB_DEVICE_EPSTATUS_BK1RDY);
|
||
|
_usbd_ep_set_out_rdy(epn, 0, false);
|
||
|
|
||
|
hri_usbendpoint_set_EPINTEN_reg(hw, epn, USB_D_SETUP_INT_FLAGS);
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_ep0_init(const uint8_t max_pkt_siz)
|
||
|
{
|
||
|
return _usb_d_dev_ep_init(0, USB_EP_XTYPE_CTRL, max_pkt_siz);
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_ep_init(const uint8_t ep, const uint8_t attr, const uint16_t max_pkt_siz)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ep);
|
||
|
bool dir = USB_EP_GET_DIR(ep);
|
||
|
struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
|
||
|
|
||
|
uint8_t ep_type = attr & USB_EP_XTYPE_MASK;
|
||
|
const struct _usb_ep_cfg_item *pcfg = &_usb_ep_cfgs[epn];
|
||
|
|
||
|
if (epn > CONF_USB_D_MAX_EP_N) {
|
||
|
return -USB_ERR_PARAM;
|
||
|
}
|
||
|
if (ept->ep != 0xFF) {
|
||
|
return -USB_ERR_REDO;
|
||
|
}
|
||
|
if (ep_type == USB_EP_XTYPE_CTRL) {
|
||
|
struct _usb_d_dev_ep *ept_in = _usb_d_dev_ept(epn, !dir);
|
||
|
if (ept_in->ep != 0xFF) {
|
||
|
return -USB_ERR_REDO;
|
||
|
}
|
||
|
if (pcfg->cache == NULL) {
|
||
|
return -USB_ERR_FUNC;
|
||
|
}
|
||
|
}
|
||
|
if ((dir ? pcfg->i_cache : pcfg->cache) && ((dir ? pcfg->i_size : pcfg->size) < max_pkt_siz)) {
|
||
|
return -USB_ERR_FUNC;
|
||
|
}
|
||
|
|
||
|
/* Initialize EP n settings */
|
||
|
ept->cache = (uint8_t *)(dir ? pcfg->i_cache : pcfg->cache);
|
||
|
ept->size = max_pkt_siz;
|
||
|
ept->flags.u8 = (ep_type + 1);
|
||
|
ept->ep = ep;
|
||
|
|
||
|
return USB_OK;
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_ep_deinit(uint8_t ep)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t epn = USB_EP_GET_N(ep);
|
||
|
bool dir = USB_EP_GET_DIR(ep);
|
||
|
struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
|
||
|
|
||
|
if (epn > CONF_USB_D_MAX_EP_N || !_usb_d_dev_ep_is_used(ept)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Finish pending transactions. */
|
||
|
_usb_d_dev_trans_stop(ept, dir, USB_TRANS_RESET);
|
||
|
|
||
|
/* Disable the endpoint. */
|
||
|
if (_usb_d_dev_ep_is_ctrl(ept)) {
|
||
|
hw->DEVICE.DeviceEndpoint[ep].EPCFG.reg = 0;
|
||
|
} else if (USB_EP_GET_DIR(ep)) {
|
||
|
hw->DEVICE.DeviceEndpoint[USB_EP_GET_N(ep)].EPCFG.reg &= ~USB_DEVICE_EPCFG_EPTYPE1_Msk;
|
||
|
} else {
|
||
|
hw->DEVICE.DeviceEndpoint[ep].EPCFG.reg &= ~USB_DEVICE_EPCFG_EPTYPE0_Msk;
|
||
|
}
|
||
|
ept->flags.u8 = 0;
|
||
|
ept->ep = 0xFF;
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_ep_enable(const uint8_t ep)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t epn = USB_EP_GET_N(ep);
|
||
|
bool dir = USB_EP_GET_DIR(ep);
|
||
|
struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
|
||
|
uint8_t epcfg = hri_usbendpoint_read_EPCFG_reg(hw, epn);
|
||
|
UsbDeviceDescBank * bank;
|
||
|
|
||
|
if (epn > CONF_USB_D_MAX_EP_N || !_usb_d_dev_ep_is_used(ept)) {
|
||
|
return -USB_ERR_PARAM;
|
||
|
}
|
||
|
|
||
|
bank = prvt_inst.desc_table[epn].DeviceDescBank;
|
||
|
if (ept->flags.bits.eptype == USB_D_EPTYPE_CTRL) {
|
||
|
if (epcfg & (USB_DEVICE_EPCFG_EPTYPE1_Msk | USB_DEVICE_EPCFG_EPTYPE0_Msk)) {
|
||
|
return -USB_ERR_REDO;
|
||
|
}
|
||
|
hri_usbendpoint_write_EPCFG_reg(hw, epn, USB_D_EPCFG_CTRL);
|
||
|
bank[0].PCKSIZE.reg = USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE(ept->size)
|
||
|
| USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size));
|
||
|
bank[1].PCKSIZE.reg
|
||
|
= USB_DEVICE_PCKSIZE_BYTE_COUNT(ept->size) | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size));
|
||
|
/* By default, control endpoint accept SETUP and NAK all other token. */
|
||
|
_usbd_ep_set_out_rdy(epn, 0, false);
|
||
|
_usbd_ep_set_in_rdy(epn, 1, false);
|
||
|
|
||
|
_usbd_ep_clear_bank_status(epn, 0);
|
||
|
_usbd_ep_clear_bank_status(epn, 1);
|
||
|
|
||
|
/* Enable SETUP reception for control endpoint. */
|
||
|
_usb_d_dev_trans_setup(ept);
|
||
|
|
||
|
} else if (dir) {
|
||
|
if (epcfg & USB_DEVICE_EPCFG_EPTYPE1_Msk) {
|
||
|
return -USB_ERR_REDO;
|
||
|
}
|
||
|
epcfg |= USB_DEVICE_EPCFG_EPTYPE1(ept->flags.bits.eptype);
|
||
|
hri_usbendpoint_write_EPCFG_reg(hw, epn, epcfg);
|
||
|
|
||
|
bank[1].PCKSIZE.reg
|
||
|
= USB_DEVICE_PCKSIZE_BYTE_COUNT(ept->size) | USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size));
|
||
|
|
||
|
/* By default, IN endpoint will NAK all token. */
|
||
|
_usbd_ep_set_in_rdy(epn, 1, false);
|
||
|
_usbd_ep_clear_bank_status(epn, 1);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (epcfg & USB_DEVICE_EPCFG_EPTYPE0_Msk) {
|
||
|
return -USB_ERR_REDO;
|
||
|
}
|
||
|
epcfg |= USB_DEVICE_EPCFG_EPTYPE0(ept->flags.bits.eptype);
|
||
|
hri_usbendpoint_write_EPCFG_reg(hw, epn, epcfg);
|
||
|
|
||
|
bank[0].PCKSIZE.reg = USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE(ept->size)
|
||
|
| USB_DEVICE_PCKSIZE_SIZE(_usbd_ep_pcksize_size(ept->size));
|
||
|
|
||
|
/* By default, OUT endpoint will NAK all token. */
|
||
|
_usbd_ep_set_out_rdy(epn, 0, false);
|
||
|
_usbd_ep_clear_bank_status(epn, 0);
|
||
|
}
|
||
|
|
||
|
return USB_OK;
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_ep_disable(const uint8_t ep)
|
||
|
{
|
||
|
Usb * hw = USB;
|
||
|
uint8_t epn = USB_EP_GET_N(ep);
|
||
|
bool dir = USB_EP_GET_DIR(ep);
|
||
|
struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
|
||
|
|
||
|
_usb_d_dev_trans_stop(ept, dir, USB_TRANS_RESET);
|
||
|
if (_usb_d_dev_ep_is_ctrl(ept)) {
|
||
|
hri_usbendpoint_clear_EPINTEN_reg(hw, epn, USB_D_ALL_INT_FLAGS);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Get endpoint stall status
|
||
|
* \param[in] ept Pointer to endpoint information.
|
||
|
* \param[in] dir Endpoint direction.
|
||
|
* \return Stall status.
|
||
|
* \retval \c true Endpoint is stalled.
|
||
|
* \retval \c false Endpoint is not stalled.
|
||
|
*/
|
||
|
static inline int32_t _usb_d_dev_ep_stall_get(struct _usb_d_dev_ep *ept, bool dir)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
return _usbd_ep_is_stalled(epn, dir);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Set endpoint stall
|
||
|
* \param[in, out] ept Pointer to endpoint information.
|
||
|
* \param[in] dir Endpoint direction.
|
||
|
* \return Always 0, success.
|
||
|
*/
|
||
|
static inline int32_t _usb_d_dev_ep_stall_set(struct _usb_d_dev_ep *ept, bool dir)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
_usbd_ep_set_stall(epn, dir, true);
|
||
|
_usbd_ep_int_en(epn, USB_DEVICE_EPINTFLAG_STALL0 << dir);
|
||
|
ept->flags.bits.is_stalled = 1;
|
||
|
/* In stall interrupt abort the transfer. */
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Clear endpoint stall
|
||
|
* \param[in, out] ept Pointer to endpoint information.
|
||
|
* \param[in] dir Endpoint direction.
|
||
|
* \return Always 0, success.
|
||
|
*/
|
||
|
static inline int32_t _usb_d_dev_ep_stall_clr(struct _usb_d_dev_ep *ept, bool dir)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
bool is_stalled = _usbd_ep_is_stalled(epn, dir);
|
||
|
if (!is_stalled) {
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
_usbd_ep_set_stall(epn, dir, false);
|
||
|
_usbd_ep_int_dis(epn, USB_DEVICE_EPINTFLAG_STALL0 << dir);
|
||
|
if (_usbd_ep_is_stall_sent(epn, dir)) {
|
||
|
_usbd_ep_ack_stall(epn, dir);
|
||
|
_usbd_ep_set_toggle(epn, dir, 0);
|
||
|
}
|
||
|
if (_usb_d_dev_ep_is_ctrl(ept)) {
|
||
|
if ((hri_usbendpoint_read_EPSTATUS_reg(USB, epn) & USB_DEVICE_EPSTATUS_STALLRQ_Msk) == 0) {
|
||
|
ept->flags.bits.is_stalled = 0;
|
||
|
}
|
||
|
} else {
|
||
|
ept->flags.bits.is_stalled = 0;
|
||
|
}
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_ep_stall(const uint8_t ep, const enum usb_ep_stall_ctrl ctrl)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ep);
|
||
|
bool dir = USB_EP_GET_DIR(ep);
|
||
|
struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
|
||
|
int32_t rc;
|
||
|
|
||
|
if (epn > CONF_USB_D_MAX_EP_N) {
|
||
|
return -USB_ERR_PARAM;
|
||
|
}
|
||
|
|
||
|
if (USB_EP_STALL_SET == ctrl) {
|
||
|
rc = _usb_d_dev_ep_stall_set(ept, dir);
|
||
|
} else if (USB_EP_STALL_CLR == ctrl) {
|
||
|
rc = _usb_d_dev_ep_stall_clr(ept, dir);
|
||
|
} else {
|
||
|
rc = _usb_d_dev_ep_stall_get(ept, dir);
|
||
|
}
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Finish the transaction and invoke callback
|
||
|
* \param[in, out] ept Pointer to endpoint information.
|
||
|
* \param[in] code Information code passed.
|
||
|
*/
|
||
|
static void _usb_d_dev_trans_done(struct _usb_d_dev_ep *ept, const int32_t code)
|
||
|
{
|
||
|
if (!(_usb_d_dev_ep_is_used(ept) && _usb_d_dev_ep_is_busy(ept))) {
|
||
|
return;
|
||
|
}
|
||
|
ept->flags.bits.is_busy = 0;
|
||
|
dev_inst.ep_callbacks.done(ept->ep, code, ept->trans_count);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief Terminate the transaction with specific status code
|
||
|
* \param[in, out] ept Pointer to endpoint information.
|
||
|
* \param[in] dir Endpoint direction.
|
||
|
* \param[in] code Information code passed.
|
||
|
*/
|
||
|
static void _usb_d_dev_trans_stop(struct _usb_d_dev_ep *ept, bool dir, const int32_t code)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ept->ep);
|
||
|
;
|
||
|
const uint8_t intflags[2] = {USB_D_BANK0_INT_FLAGS, USB_D_BANK1_INT_FLAGS};
|
||
|
if (!(_usb_d_dev_ep_is_used(ept) && _usb_d_dev_ep_is_busy(ept))) {
|
||
|
return;
|
||
|
}
|
||
|
/* Stop transfer */
|
||
|
if (dir) {
|
||
|
/* NAK IN */
|
||
|
_usbd_ep_set_in_rdy(epn, 1, false);
|
||
|
} else {
|
||
|
/* NAK OUT */
|
||
|
_usbd_ep_set_out_rdy(epn, 0, false);
|
||
|
}
|
||
|
_usbd_ep_int_ack(epn, intflags[dir]);
|
||
|
_usbd_ep_int_dis(epn, intflags[dir]);
|
||
|
_usb_d_dev_trans_done(ept, code);
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_ep_read_req(const uint8_t ep, uint8_t *req_buf)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ep);
|
||
|
UsbDeviceDescBank *bank = prvt_inst.desc_table[epn].DeviceDescBank;
|
||
|
uint32_t addr = bank[0].ADDR.reg;
|
||
|
uint16_t bytes = bank[0].PCKSIZE.bit.BYTE_COUNT;
|
||
|
|
||
|
if (epn > CONF_USB_D_MAX_EP_N || !req_buf) {
|
||
|
return -USB_ERR_PARAM;
|
||
|
}
|
||
|
if (!_usbd_ep_is_ctrl(epn)) {
|
||
|
return -USB_ERR_FUNC;
|
||
|
}
|
||
|
if (!_usbd_ep_is_setup(epn)) {
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
memcpy(req_buf, (void *)addr, 8);
|
||
|
_usbd_ep_ack_setup(epn);
|
||
|
|
||
|
return bytes;
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_ep_trans(const struct usb_d_transfer *trans)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(trans->ep);
|
||
|
bool dir = USB_EP_GET_DIR(trans->ep);
|
||
|
struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
|
||
|
|
||
|
uint16_t size_mask = (ept->size == 1023) ? 1023 : (ept->size - 1);
|
||
|
bool size_n_aligned = (trans->size & size_mask);
|
||
|
|
||
|
bool use_cache = false;
|
||
|
|
||
|
volatile hal_atomic_t flags;
|
||
|
|
||
|
if (epn > CONF_USB_D_MAX_EP_N) {
|
||
|
return -USB_ERR_PARAM;
|
||
|
}
|
||
|
|
||
|
/* Cases that needs cache:
|
||
|
* 1. Buffer not in RAM (cache all).
|
||
|
* 2. IN/OUT with unaligned buffer (cache all).
|
||
|
* 3. OUT with unaligned packet size (cache last packet).
|
||
|
* 4. OUT size < 8 (sub-case for 3).
|
||
|
*/
|
||
|
if (!_usb_is_addr4dma(trans->buf, trans->size) || (!_usb_is_aligned(trans->buf))
|
||
|
|| (!dir && (trans->size < ept->size))) {
|
||
|
if (!ept->cache) {
|
||
|
return -USB_ERR_FUNC;
|
||
|
}
|
||
|
/* Use cache all the time. */
|
||
|
use_cache = true;
|
||
|
}
|
||
|
if (!dir && size_n_aligned) {
|
||
|
if (!ept->cache) {
|
||
|
return -USB_ERR_PARAM;
|
||
|
}
|
||
|
/* Set 'use_cache' on last packet. */
|
||
|
}
|
||
|
|
||
|
/* Check halt */
|
||
|
if (ept->flags.bits.is_stalled) {
|
||
|
return USB_HALTED;
|
||
|
}
|
||
|
|
||
|
/* Try to start transactions. */
|
||
|
|
||
|
atomic_enter_critical(&flags);
|
||
|
if (_usb_d_dev_ep_is_busy(ept)) {
|
||
|
atomic_leave_critical(&flags);
|
||
|
return USB_BUSY;
|
||
|
}
|
||
|
ept->flags.bits.is_busy = 1;
|
||
|
atomic_leave_critical(&flags);
|
||
|
|
||
|
/* Copy transaction information. */
|
||
|
ept->trans_buf = trans->buf;
|
||
|
ept->trans_size = trans->size;
|
||
|
ept->trans_count = 0;
|
||
|
|
||
|
ept->flags.bits.dir = dir;
|
||
|
ept->flags.bits.use_cache = use_cache;
|
||
|
ept->flags.bits.need_zlp = (trans->zlp && (!size_n_aligned));
|
||
|
|
||
|
if (dir) {
|
||
|
_usb_d_dev_in_next(ept, false);
|
||
|
} else {
|
||
|
_usb_d_dev_out_next(ept, false);
|
||
|
}
|
||
|
|
||
|
return ERR_NONE;
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_ep_abort(const uint8_t ep)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ep);
|
||
|
bool dir = USB_EP_GET_DIR(ep);
|
||
|
struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
|
||
|
if (epn > CONF_USB_D_MAX_EP_N) {
|
||
|
return;
|
||
|
}
|
||
|
_usb_d_dev_trans_stop(ept, dir, USB_TRANS_ABORT);
|
||
|
}
|
||
|
|
||
|
int32_t _usb_d_dev_ep_get_status(const uint8_t ep, struct usb_d_trans_status *stat)
|
||
|
{
|
||
|
uint8_t epn = USB_EP_GET_N(ep);
|
||
|
bool dir = USB_EP_GET_DIR(ep);
|
||
|
struct _usb_d_dev_ep *ept = _usb_d_dev_ept(epn, dir);
|
||
|
bool busy, stall;
|
||
|
|
||
|
if (epn > CONF_USB_D_MAX_EP_N) {
|
||
|
return USB_ERR_PARAM;
|
||
|
}
|
||
|
busy = ept->flags.bits.is_busy;
|
||
|
stall = ept->flags.bits.is_stalled;
|
||
|
if (stat) {
|
||
|
stat->stall = stall;
|
||
|
stat->busy = busy;
|
||
|
stat->setup = USB->DEVICE.DeviceEndpoint[epn].EPINTFLAG.bit.RXSTP;
|
||
|
stat->dir = ept->flags.bits.dir;
|
||
|
stat->size = ept->trans_size;
|
||
|
stat->count = ept->trans_count;
|
||
|
stat->ep = ep;
|
||
|
stat->xtype = ept->flags.bits.eptype - 1;
|
||
|
}
|
||
|
if (stall) {
|
||
|
return USB_HALTED;
|
||
|
}
|
||
|
if (busy) {
|
||
|
return USB_BUSY;
|
||
|
}
|
||
|
return USB_OK;
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_register_callback(const enum usb_d_cb_type type, const FUNC_PTR func)
|
||
|
{
|
||
|
FUNC_PTR f = (func == NULL) ? (FUNC_PTR)_dummy_func_no_return : (FUNC_PTR)func;
|
||
|
if (type == USB_D_CB_EVENT) {
|
||
|
dev_inst.callbacks.event = (_usb_d_dev_event_cb_t)f;
|
||
|
} else if (type == USB_D_CB_SOF) {
|
||
|
dev_inst.callbacks.sof = (_usb_d_dev_sof_cb_t)f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void _usb_d_dev_register_ep_callback(const enum usb_d_dev_ep_cb_type type, const FUNC_PTR func)
|
||
|
{
|
||
|
FUNC_PTR f = (func == NULL) ? (FUNC_PTR)_dummy_func_no_return : (FUNC_PTR)func;
|
||
|
if (type == USB_D_DEV_EP_CB_SETUP) {
|
||
|
dev_inst.ep_callbacks.setup = (_usb_d_dev_ep_cb_setup_t)f;
|
||
|
} else if (type == USB_D_DEV_EP_CB_MORE) {
|
||
|
dev_inst.ep_callbacks.more = (_usb_d_dev_ep_cb_more_t)f;
|
||
|
} else if (type == USB_D_DEV_EP_CB_DONE) {
|
||
|
dev_inst.ep_callbacks.done = (_usb_d_dev_ep_cb_done_t)f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* \brief USB interrupt handler
|
||
|
*/
|
||
|
void USB_Handler(void)
|
||
|
{
|
||
|
|
||
|
_usb_d_dev_handler();
|
||
|
}
|