1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

sam0_common: Add USB peripheral driver

This commit is contained in:
Koen Zandberg 2019-01-31 22:01:02 +01:00
parent c697a3bfd8
commit d7804823db
No known key found for this signature in database
GPG Key ID: 0895A893E6D2985B
4 changed files with 843 additions and 0 deletions

View File

@ -392,6 +392,17 @@ typedef struct {
uint32_t muxpos; /**< ADC channel pin multiplexer value */
} adc_conf_chan_t;
/**
* @brief USB peripheral parameters
*/
#if defined(USB_INST_NUM) || defined(DOXYGEN)
typedef struct {
gpio_t dm; /**< D- line gpio */
gpio_t dp; /**< D+ line gpio */
gpio_mux_t d_mux; /**< alternate function (mux) for data pins */
UsbDevice *device; /**< ptr to the device registers */
} sam0_common_usb_config_t;
#endif /* USB_INST_NUM */
#ifdef __cplusplus
}

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2019 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup cpu_sam0_common_usb sam0 USB peripheral
* @ingroup cpu_sam0_common
* @brief USB interface functions for the sam0 class devices
*
* @{
*
* @file
* @brief USB interface functions for the sam0 class devices
*
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef SAM_USB_H
#define SAM_USB_H
#include <stdint.h>
#include <stdlib.h>
#include "periph_cpu.h"
#include "periph/usbdev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* USB endpoint buffer space
*/
#define SAM_USB_BUF_SPACE USBDEV_EP_BUF_SPACE
/**
* Number of USB IN and OUT endpoints
*/
#define SAM_USB_NUM_EP USBDEV_NUM_ENDPOINTS
/**
* @brief sam0 usb peripheral device context
*/
typedef struct {
usbdev_t usbdev; /**< Inherited usbdev struct */
const sam0_common_usb_config_t *config; /**< USB peripheral config */
UsbDeviceDescBank banks[2 * SAM_USB_NUM_EP]; /**< Device descriptor banks */
usbdev_ep_t endpoints[2 * SAM_USB_NUM_EP]; /**< Endpoints */
size_t used; /**< Number of bytes from the
buffer that are used */
__attribute__ ((aligned(4)))
uint8_t buffer[SAM_USB_BUF_SPACE]; /**< Buffer space, must be
32-bit aligned */
bool suspended; /**< Suspend active */
} sam0_common_usb_t;
#ifdef __cplusplus
}
#endif
#endif /* SAM_USB_H */
/** @} */

View File

@ -0,0 +1,768 @@
/*
* Copyright (C) 2019 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_sam0_common_usb
* @{
* @file
* @brief Low level USB interface functions for the sam0 class devices
*
* @author Koen Zandberg <koen@bergzand.net>
* @}
*/
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include "cpu.h"
#include "cpu_conf.h"
#include "periph/gpio.h"
#include "periph/usbdev.h"
#include "pm_layered.h"
#include "sam_usb.h"
#include "bitarithm.h"
/**
* Be careful with enabling debug here. As with all timing critical systems it
* is able to interfere with USB functionality and you might see different
* errors than debug disabled
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
/* List of instantiated USB peripherals */
static sam0_common_usb_t _usbdevs[USB_INST_NUM];
/* Forward declaration for the usb device driver */
const usbdev_driver_t driver;
static void _usbdev_ep_init(usbdev_ep_t *ep);
static int _usbdev_ep_ready(usbdev_ep_t *ep, size_t len);
static usbdev_ep_t *_usbdev_new_ep(usbdev_t *dev, usb_ep_type_t type,
usb_ep_dir_t dir, size_t buf_len);
static int _bank_set_size(usbdev_ep_t *ep);
static int _ep_unready(usbdev_ep_t *ep);
static inline unsigned _ep_num(unsigned num, usb_ep_dir_t dir)
{
return 2 * num + (dir == USB_EP_DIR_OUT ? 0 : 1);
}
static inline UsbDeviceDescBank *_bank_from_ep(usbdev_ep_t *ep)
{
sam0_common_usb_t *usbdev = (sam0_common_usb_t *)ep->dev;
return &usbdev->banks[_ep_num(ep->num, ep->dir)];
}
static inline usbdev_ep_t *_get_ep(sam0_common_usb_t *dev,
unsigned num, usb_ep_dir_t dir)
{
return &dev->endpoints[_ep_num(num, dir)];
}
static inline UsbDeviceEndpoint *_ep_reg_from_ep(usbdev_ep_t *ep)
{
sam0_common_usb_t *usbdev = (sam0_common_usb_t *)ep->dev;
return &usbdev->config->device->DeviceEndpoint[ep->num];
}
static inline void _enable_irq(sam0_common_usb_t *dev)
{
dev->config->device->INTENSET.reg = USB_DEVICE_INTENSET_EORST;
if (dev->suspended) {
dev->config->device->INTENSET.reg = USB_DEVICE_INTENSET_WAKEUP;
}
else {
dev->config->device->INTENSET.reg = USB_DEVICE_INTENSET_SUSPEND;
}
}
static inline void _disable_irq(sam0_common_usb_t *dev)
{
dev->config->device->INTENCLR.reg = USB_DEVICE_INTENCLR_EORST |
USB_DEVICE_INTENCLR_SUSPEND |
USB_DEVICE_INTENCLR_WAKEUP;
}
static void _enable_ep_irq_out(sam0_common_usb_t *usbdev,
UsbDeviceEndpoint *ep_reg)
{
ep_reg->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 |
USB_DEVICE_EPINTENSET_STALL0;
if (ep_reg == &usbdev->config->device->DeviceEndpoint[0]) {
ep_reg->EPINTENSET.reg = USB_DEVICE_EPINTENSET_RXSTP;
}
}
static void _enable_ep_irq_in(UsbDeviceEndpoint *ep_reg)
{
ep_reg->EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT1 |
USB_DEVICE_EPINTENSET_STALL1;
}
/* Enable the endpoint specific interrupts */
static void _enable_ep_irq(usbdev_ep_t *ep)
{
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
if (ep->dir == USB_EP_DIR_OUT) {
_enable_ep_irq_out((sam0_common_usb_t *)ep->dev, ep_reg);
}
else {
_enable_ep_irq_in(ep_reg);
}
}
static void _disable_ep_irq_out(sam0_common_usb_t *usbdev,
UsbDeviceEndpoint *ep_reg)
{
DEBUG("Disabling OUT irq\n");
ep_reg->EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT0 |
USB_DEVICE_EPINTENCLR_STALL0;
if (ep_reg == &usbdev->config->device->DeviceEndpoint[0]) {
ep_reg->EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_RXSTP;
}
}
static void _disable_ep_irq_in(UsbDeviceEndpoint *ep_reg)
{
DEBUG("Disabling IN irq\n");
ep_reg->EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT1 |
USB_DEVICE_EPINTENCLR_STALL1;
}
static void _bank_set_address(usbdev_ep_t *ep)
{
UsbDeviceDescBank *bank = _bank_from_ep(ep);
bank->ADDR.reg = (uint32_t)ep->buf;
}
static int _bank_set_size(usbdev_ep_t *ep)
{
UsbDeviceDescBank *bank = _bank_from_ep(ep);
unsigned val = 0x00;
switch (ep->len) {
case 8:
val = 0x0;
break;
case 16:
val = 0x1;
break;
case 32:
val = 0x2;
break;
case 64:
val = 0x3;
break;
case 128:
val = 0x4;
break;
case 256:
val = 0x5;
break;
case 512:
val = 0x6;
break;
case 1023:
val = 0x7;
break;
default:
return -1;
}
bank->PCKSIZE.bit.SIZE = val;
return val;
}
/**
* Checks if one of the endpoints interrupts flags for the OUT direction are
* set
*/
static bool _ep_out_flags_set(UsbDeviceEndpoint *ep_reg)
{
return ep_reg->EPINTFLAG.reg &
ep_reg->EPINTENSET.reg &
(USB_DEVICE_EPINTENSET_TRFAIL0 |
USB_DEVICE_EPINTENSET_TRCPT0 |
USB_DEVICE_EPINTENSET_RXSTP |
USB_DEVICE_EPINTENSET_STALL0);
}
/**
* Checks if one of the endpoints interrupts flags for the IN direction are
* set
*/
static bool _ep_in_flags_set(UsbDeviceEndpoint *ep_reg)
{
return ep_reg->EPINTFLAG.reg &
ep_reg->EPINTENSET.reg &
(USB_DEVICE_EPINTENSET_TRFAIL1 |
USB_DEVICE_EPINTENSET_TRCPT1 |
USB_DEVICE_EPINTENSET_STALL1);
}
static void _set_address(sam0_common_usb_t *dev, uint8_t addr)
{
dev->config->device->DADD.bit.DADD = addr;
/* Only enable the address if it is nonzero */
dev->config->device->DADD.bit.ADDEN = addr ? 1 : 0;
}
static bool _syncbusy_enable(sam0_common_usb_t *dev)
{
return dev->config->device->SYNCBUSY.bit.ENABLE;
}
static bool _syncbusy_swrst(sam0_common_usb_t *dev)
{
return dev->config->device->SYNCBUSY.bit.SWRST;
}
static inline void _poweron(void)
{
#if defined(CPU_FAM_SAMD21)
PM->AHBMASK.reg |= PM_AHBMASK_USB;
PM->APBBMASK.reg |= PM_APBBMASK_USB;
GCLK->CLKCTRL.reg = (uint32_t)(GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN_GCLK0 |
(GCLK_CLKCTRL_ID(USB_GCLK_ID)));
#elif defined(CPU_FAM_SAML21)
MCLK->AHBMASK.reg |= MCLK_AHBMASK_USB;
MCLK->APBBMASK.reg |= MCLK_APBBMASK_USB;
GCLK->PCHCTRL[USB_GCLK_ID].reg = GCLK_PCHCTRL_CHEN |
GCLK_PCHCTRL_GEN_GCLK0;
#else
#error "Unknown CPU family for sam0 common usbdev driver"
#endif
}
static usbdev_ep_t *_usbdev_new_ep(usbdev_t *dev, usb_ep_type_t type,
usb_ep_dir_t dir, size_t buf_len)
{
sam0_common_usb_t *usbdev = (sam0_common_usb_t *)dev;
/* The IP supports all types for all endpoints */
usbdev_ep_t *res = NULL;
/* Always return endpoint 0 for control types */
if (type == USB_EP_TYPE_CONTROL) {
res = _get_ep(usbdev, 0, dir);
res->num = 0;
}
else {
/* Find the first unassigned ep with proper dir */
for (unsigned idx = 1; idx < SAM_USB_NUM_EP && !res; idx++) {
usbdev_ep_t *ep = _get_ep(usbdev, idx, dir);
if (ep->type == USB_EP_TYPE_NONE) {
res = ep;
res->num = idx;
}
}
}
if (res) {
res->dev = dev;
res->dir = dir;
if (usbdev->used + buf_len < SAM_USB_BUF_SPACE) {
res->buf = usbdev->buffer + usbdev->used;
res->len = buf_len;
if (_bank_set_size(res) < 0) {
return NULL;
}
usbdev->used += buf_len;
_bank_set_address(res);
res->type = type;
res->dev = dev;
}
}
return res;
}
static void _block_pm(void)
{
#if defined(CPU_FAM_SAMD21)
pm_block(SAMD21_PM_IDLE_1);
#endif
}
static void _unblock_pm(void)
{
#if defined(CPU_FAM_SAMD21)
pm_unblock(SAMD21_PM_IDLE_1);
#endif
}
static void _setup(sam0_common_usb_t *usbdev,
const sam0_common_usb_config_t *config)
{
usbdev->usbdev.driver = &driver;
usbdev->config = config;
}
void usbdev_init_lowlevel(void)
{
for (size_t i = 0; i < USB_INST_NUM; i++) {
_setup(&_usbdevs[i], &sam_usbdev_config[i]);
}
}
usbdev_t *usbdev_get_ctx(unsigned num)
{
assert(num < USB_INST_NUM);
return &_usbdevs[num].usbdev;
}
static void _usbdev_init(usbdev_t *dev)
{
DEBUG("Initializing sam0 usb peripheral\n");
/* Only one usb device on this board */
sam0_common_usb_t *usbdev = (sam0_common_usb_t *)dev;
usbdev->used = 0;
/* Set GPIO */
gpio_init(usbdev->config->dp, GPIO_IN);
gpio_init(usbdev->config->dm, GPIO_IN);
gpio_init_mux(usbdev->config->dm, usbdev->config->d_mux);
gpio_init_mux(usbdev->config->dp, usbdev->config->d_mux);
_poweron();
/* Reset peripheral */
usbdev->config->device->CTRLA.reg |= USB_CTRLA_SWRST;
while (_syncbusy_swrst(usbdev)) {}
/* Enable USB device */
usbdev->config->device->DESCADD.reg = (uint32_t)&usbdev->banks;
usbdev->config->device->CTRLA.reg |= USB_CTRLA_ENABLE;
while (_syncbusy_enable(usbdev)) {}
/* Callibration values */
usbdev->config->device->PADCAL.reg =
USB_PADCAL_TRANSP((*(uint32_t *)USB_FUSES_TRANSP_ADDR >>
USB_FUSES_TRANSP_Pos)) |
USB_PADCAL_TRANSN((*(uint32_t *)USB_FUSES_TRANSN_ADDR >>
USB_FUSES_TRANSN_Pos)) |
USB_PADCAL_TRIM((*(uint32_t *)USB_FUSES_TRIM_ADDR >>
USB_FUSES_TRIM_Pos));
usbdev->config->device->CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_FS;
_enable_irq(usbdev);
_block_pm();
usbdev->usbdev.cb(&usbdev->usbdev, USBDEV_EVENT_HOST_CONNECT);
/* Interrupt configuration */
NVIC_EnableIRQ(USB_IRQn);
}
static void usb_attach(sam0_common_usb_t *dev)
{
DEBUG("sam_usb: Attaching to host\n");
dev->config->device->CTRLB.reg &= ~USB_DEVICE_CTRLB_DETACH;
}
static void usb_detach(sam0_common_usb_t *dev)
{
DEBUG("sam_usb: detaching to host\n");
dev->config->device->CTRLB.reg |= USB_DEVICE_CTRLB_DETACH;
}
static int _usbdev_get(usbdev_t *usbdev, usbopt_t opt,
void *value, size_t max_len)
{
(void)usbdev;
(void)max_len;
int res = -ENOTSUP;
switch (opt) {
case USBOPT_MAX_VERSION:
assert(max_len == sizeof(usb_version_t));
*(usb_version_t *)value = USB_VERSION_20;
res = sizeof(usb_version_t);
break;
case USBOPT_MAX_SPEED:
assert(max_len == sizeof(usb_speed_t));
*(usb_speed_t *)value = USB_SPEED_FULL;
res = sizeof(usb_speed_t);
break;
default:
DEBUG("Unhandled get call: 0x%x\n", opt);
break;
}
return res;
}
static int _usbdev_set(usbdev_t *dev, usbopt_t opt,
const void *value, size_t value_len)
{
sam0_common_usb_t *usbdev = (sam0_common_usb_t *)dev;
(void)value_len;
int res = -ENOTSUP;
switch (opt) {
case USBOPT_ADDRESS:
assert(value_len == sizeof(uint8_t));
uint8_t addr = (*((uint8_t *)value));
_set_address(usbdev, addr);
break;
case USBOPT_ATTACH:
assert(value_len == sizeof(usbopt_enable_t));
if (*((usbopt_enable_t *)value)) {
usb_attach(usbdev);
}
else {
usb_detach(usbdev);
}
res = sizeof(usbopt_enable_t);
break;
default:
DEBUG("Unhandled set call: 0x%x\n", opt);
break;
}
return res;
}
static void _ep_disable(usbdev_ep_t *ep)
{
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
if (ep->dir == USB_EP_DIR_OUT) {
ep_reg->EPCFG.bit.EPTYPE0 = 0;
}
else {
ep_reg->EPCFG.bit.EPTYPE1 = 0;
}
}
static void _ep_enable(usbdev_ep_t *ep)
{
DEBUG("sam_usb: Enabling endpoint %d, dir %s\n", ep->num,
ep->dir == USB_EP_DIR_OUT ? "OUT" : "IN");
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
uint8_t type = 0;
switch (ep->type) {
case USB_EP_TYPE_CONTROL:
type = 0x01;
break;
case USB_EP_TYPE_ISOCHRONOUS:
type = 0x02;
break;
case USB_EP_TYPE_BULK:
type = 0x03;
break;
case USB_EP_TYPE_INTERRUPT:
type = 0x04;
break;
case default:
/* Must never happen */
assert(false);
}
if (ep->dir == USB_EP_DIR_OUT) {
ep_reg->EPCFG.bit.EPTYPE0 = type;
}
else {
ep_reg->EPCFG.bit.EPTYPE1 = type;
}
}
/*
* sam0 USB interrupt service routine
*/
void isr_usb(void)
{
/* TODO: make a bit more elegant for multi-periph support */
sam0_common_usb_t *dev = &_usbdevs[0];
if (dev->config->device->EPINTSMRY.reg) {
/* Endpoint specific interrupt */
unsigned ep_num = bitarithm_lsb(dev->config->device->EPINTSMRY.reg);
UsbDeviceEndpoint *ep_reg =
&dev->config->device->DeviceEndpoint[ep_num];
if (_ep_in_flags_set(ep_reg)) {
usbdev_ep_t *ep = _get_ep(dev, ep_num, USB_EP_DIR_IN);
_disable_ep_irq_in(ep_reg);
dev->usbdev.epcb(ep, USBDEV_EVENT_ESR);
}
else if (_ep_out_flags_set(ep_reg)) {
usbdev_ep_t *ep = _get_ep(dev, ep_num, USB_EP_DIR_OUT);
_disable_ep_irq_out(dev, ep_reg);
dev->usbdev.epcb(ep, USBDEV_EVENT_ESR);
}
else {
DEBUG("sam_usb: Unhandled EP interrupt\n");
}
}
else {
/* Device interrupt */
_disable_irq(dev);
dev->usbdev.cb(&dev->usbdev, USBDEV_EVENT_ESR);
}
cortexm_isr_end();
}
static void _usbdev_esr(usbdev_t *dev)
{
sam0_common_usb_t *usbdev = (sam0_common_usb_t *)dev;
if (usbdev->config->device->INTFLAG.reg) {
if (usbdev->config->device->INTFLAG.bit.EORST) {
/* Clear flag */
usbdev->config->device->INTFLAG.reg = USB_DEVICE_INTFLAG_EORST;
usbdev->usbdev.cb(&usbdev->usbdev, USBDEV_EVENT_RESET);
usbdev->config->device->INTFLAG.reg = USB_DEVICE_INTFLAG_EORST;
}
else if (usbdev->config->device->INTFLAG.bit.SUSPEND &&
!usbdev->suspended) {
usbdev->config->device->INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP |
USB_DEVICE_INTFLAG_SUSPEND;
usbdev->suspended = true;
usbdev->usbdev.cb(&usbdev->usbdev, USBDEV_EVENT_SUSPEND);
/* Low power modes are available while suspended */
_unblock_pm();
}
else if (usbdev->config->device->INTFLAG.bit.WAKEUP &&
usbdev->suspended) {
usbdev->config->device->INTFLAG.reg = USB_DEVICE_INTFLAG_WAKEUP |
USB_DEVICE_INTFLAG_SUSPEND;
usbdev->suspended = false;
usbdev->usbdev.cb(&usbdev->usbdev, USBDEV_EVENT_RESUME);
/* Device wakeup detected, blocking low power modes */
_block_pm();
}
else {
DEBUG("sam_usb: Unhandled interrupt\n");
}
/* Re-enable the USB IRQ */
_enable_irq(usbdev);
}
}
static inline void _enable_ep_stall_out(UsbDeviceEndpoint *ep_reg)
{
ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
}
static inline void _enable_ep_stall_in(UsbDeviceEndpoint *ep_reg)
{
ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
}
static inline void _disable_ep_stall_out(UsbDeviceEndpoint *ep_reg)
{
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;
}
static inline void _disable_ep_stall_in(UsbDeviceEndpoint *ep_reg)
{
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;
}
static void _ep_set_stall(usbdev_ep_t *ep, usbopt_enable_t enable)
{
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
if (ep->dir == USB_EP_DIR_IN) {
enable ? _enable_ep_stall_in(ep_reg)
: _disable_ep_stall_in(ep_reg);
}
else {
enable ? _enable_ep_stall_out(ep_reg)
: _disable_ep_stall_out(ep_reg);
}
}
usbopt_enable_t _ep_get_stall(usbdev_ep_t *ep)
{
usbopt_enable_t res;
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
if (ep->dir == USB_EP_DIR_IN) {
res = ep_reg->EPSTATUSSET.bit.STALLRQ1
? USBOPT_ENABLE
: USBOPT_DISABLE;
}
else {
res = ep_reg->EPSTATUSSET.bit.STALLRQ0
? USBOPT_ENABLE
: USBOPT_DISABLE;
}
return res;
}
static void _usbdev_ep_init(usbdev_ep_t *ep)
{
_enable_ep_irq(ep);
}
static size_t _ep_get_available(usbdev_ep_t *ep)
{
return _bank_from_ep(ep)->PCKSIZE.bit.BYTE_COUNT;
}
static int _usbdev_ep_get(usbdev_ep_t *ep, usbopt_ep_t opt,
void *value, size_t max_len)
{
(void)max_len;
int res = -ENOTSUP;
switch (opt) {
case USBOPT_EP_STALL:
assert(max_len == sizeof(usbopt_enable_t));
*(usbopt_enable_t *)value = _ep_get_stall(ep);
res = sizeof(usbopt_enable_t);
break;
case USBOPT_EP_AVAILABLE:
assert(max_len == sizeof(size_t));
*(size_t *)value = _ep_get_available(ep);
res = sizeof(size_t);
break;
default:
DEBUG("sam_usb: Unhandled get call: 0x%x\n", opt);
break;
}
return res;
}
static int _usbdev_ep_set(usbdev_ep_t *ep, usbopt_ep_t opt,
const void *value, size_t value_len)
{
(void)value_len;
int res = -ENOTSUP;
switch (opt) {
case USBOPT_EP_ENABLE:
assert(value_len == sizeof(usbopt_enable_t));
if (*((usbopt_enable_t *)value)) {
_ep_enable(ep);
_usbdev_ep_init(ep);
}
else {
_ep_disable(ep);
}
res = sizeof(usbopt_enable_t);
break;
case USBOPT_EP_STALL:
assert(value_len == sizeof(usbopt_enable_t));
_ep_set_stall(ep, *(usbopt_enable_t *)value);
res = sizeof(usbopt_enable_t);
break;
case USBOPT_EP_READY:
assert(value_len == sizeof(usbopt_enable_t));
if (*((usbopt_enable_t *)value)) {
_ep_unready(ep);
}
else {
_usbdev_ep_ready(ep, 0);
}
res = sizeof(usbopt_enable_t);
break;
default:
DEBUG("sam_usb: Unhandled set call: 0x%x\n", opt);
break;
}
return res;
}
static int _ep_unready(usbdev_ep_t *ep)
{
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
if (ep->dir == USB_EP_DIR_IN) {
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
}
else {
ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;
}
return 0;
}
static int _usbdev_ep_ready(usbdev_ep_t *ep, size_t len)
{
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
if (ep->dir == USB_EP_DIR_IN) {
_disable_ep_stall_in(ep_reg);
_bank_from_ep(ep)->PCKSIZE.bit.BYTE_COUNT = len;
ep_reg->EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK1RDY;
}
else {
_disable_ep_stall_out(ep_reg);
ep_reg->EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY;
}
return 0;
}
/**
* Endpoint event handler
*
* Calls the endpoint callback to report the event to the USB stack
*/
static void _usbdev_ep_esr(usbdev_ep_t *ep)
{
UsbDeviceEndpoint *ep_reg = _ep_reg_from_ep(ep);
signed event = -1;
if (ep->dir == USB_EP_DIR_OUT) {
if (ep_reg->EPINTFLAG.bit.TRCPT0) {
ep_reg->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0;
event = USBDEV_EVENT_TR_COMPLETE;
}
else if (ep_reg->EPINTFLAG.bit.RXSTP) {
ep_reg->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP;
event = USBDEV_EVENT_TR_COMPLETE;
}
else if (ep_reg->EPINTFLAG.bit.TRFAIL0) {
ep_reg->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRFAIL0;
event = USBDEV_EVENT_TR_FAIL;
}
else if (ep_reg->EPINTFLAG.bit.STALL0) {
ep_reg->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL0;
event = USBDEV_EVENT_TR_STALL;
}
else {
DEBUG("Unhandled event: EP OUT %u: 0x%x\n", ep->num,
ep_reg->EPINTFLAG.reg);
}
}
else {
if (ep_reg->EPINTFLAG.bit.TRCPT1) {
DEBUG("sam_usb: Transfer IN complete\n");
ep_reg->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1;
event = USBDEV_EVENT_TR_COMPLETE;
}
else if (ep_reg->EPINTFLAG.bit.TRFAIL1) {
ep_reg->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRFAIL1;
event = USBDEV_EVENT_TR_FAIL;
}
else if (ep_reg->EPINTFLAG.bit.STALL1) {
ep_reg->EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL1;
event = USBDEV_EVENT_TR_STALL;
}
else {
DEBUG("Unhandled event: EP IN %u: 0x%x\n", ep->num,
ep_reg->EPINTFLAG.reg);
}
}
if (event >= 0) {
ep->dev->epcb(ep, event);
}
ep->dir == USB_EP_DIR_OUT ? _enable_ep_irq_out((sam0_common_usb_t *)ep->dev,
ep_reg)
: _enable_ep_irq_in(ep_reg);
}
const usbdev_driver_t driver = {
.init = _usbdev_init,
.new_ep = _usbdev_new_ep,
.get = _usbdev_get,
.set = _usbdev_set,
.esr = _usbdev_esr,
.ep_init = _usbdev_ep_init,
.ep_get = _usbdev_ep_get,
.ep_set = _usbdev_ep_set,
.ep_esr = _usbdev_ep_esr,
.ready = _usbdev_ep_ready,
};