nrf52: Add USB device peripheral driver
This commit is contained in:
parent
4b4c63f797
commit
ab05e63175
78
cpu/nrf52/include/nrfusb.h
Normal file
78
cpu/nrf52/include/nrfusb.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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_nrf52_nrfusb NRF usb peripheral implementation
|
||||
* @ingroup cpu_nrf52
|
||||
* @brief Minimal driver for the NRF52840 usb peripheral
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief USB interface functions for the nrf52840 class devices
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*/
|
||||
|
||||
#ifndef NRFUSB_H
|
||||
#define NRFUSB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "periph/usbdev.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Number of USB peripherals on the MCU
|
||||
*/
|
||||
#define NRF_USB_NUM_PERIPH 1
|
||||
|
||||
/**
|
||||
* USB endpoint buffer space
|
||||
*/
|
||||
#define NRF_USB_BUF_SPACE USBDEV_EP_BUF_SPACE
|
||||
|
||||
/**
|
||||
* Number of USB IN and OUT endpoints
|
||||
*/
|
||||
#define NRF_USB_NUM_EP USBDEV_NUM_ENDPOINTS
|
||||
|
||||
/**
|
||||
* @brief nrfusb setup packet state tracker for endpoint 0 handling
|
||||
*/
|
||||
typedef enum {
|
||||
NRFUSB_SETUP_READY, /**< Ready for a new setup request */
|
||||
NRFUSB_SETUP_READ, /**< Read request received */
|
||||
NRFUSB_SETUP_WRITE, /**< Write request received */
|
||||
NRFUSB_SETUP_ACKOUT, /**< Expecting an ACK on the out endpoint */
|
||||
NRFUSB_SETUP_ACKIN, /**< Expecting an ACK on the in endpoint */
|
||||
} nrfusb_setup_state_t;
|
||||
|
||||
/**
|
||||
* @brief nrf usb peripheral device context
|
||||
*/
|
||||
typedef struct {
|
||||
usbdev_t usbdev; /**< Inherited usbdev struct */
|
||||
usbdev_ep_t ep_ins[NRF_USB_NUM_EP]; /**< IN type endpoints */
|
||||
usbdev_ep_t ep_outs[ NRF_USB_NUM_EP]; /**< OUT type endpoints */
|
||||
NRF_USBD_Type *device; /**< Ptr to the device registers */
|
||||
size_t used; /**< Number of bytes from the
|
||||
buffer that are used */
|
||||
uint8_t buffer[NRF_USB_BUF_SPACE]; /**< Buffer space for endpoint data */
|
||||
nrfusb_setup_state_t sstate; /**< Setup request state machine */
|
||||
} nrfusb_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* NRFUSB_H */
|
||||
/** @} */
|
||||
657
cpu/nrf52/periph/usbdev.c
Normal file
657
cpu/nrf52/periph/usbdev.c
Normal file
@ -0,0 +1,657 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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_nrf52_nrfusb
|
||||
* @{
|
||||
* @file
|
||||
* @brief USB interface functions
|
||||
*
|
||||
* @file
|
||||
* @brief Low level USB interface functions for the nrf52840 class
|
||||
* devices
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @}
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "nrfusb.h"
|
||||
#include "periph/usbdev.h"
|
||||
#include "usb.h"
|
||||
#include "usb/descriptor.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static nrfusb_t _usbdevs[NRF_USB_NUM_PERIPH];
|
||||
|
||||
static void _init(usbdev_t *usbdev);
|
||||
static int _get(usbdev_t *usbdev, usbopt_t opt, void *value, size_t max_len);
|
||||
static int _set(usbdev_t *usbdev, usbopt_t opt, const void *value, size_t value_len);
|
||||
static usbdev_ep_t *_new_ep(usbdev_t *dev, usb_ep_type_t type, usb_ep_dir_t dir, size_t buf_len);
|
||||
static void _esr(usbdev_t *usbdev);
|
||||
static void _ep_init(usbdev_ep_t *ep);
|
||||
static int _ep_get(usbdev_ep_t *ep, usbopt_ep_t opt, void *value, size_t max_len);
|
||||
static int _ep_set(usbdev_ep_t *ep, usbopt_ep_t opt, const void *value, size_t value_len);
|
||||
static int _ep_ready(usbdev_ep_t *ep, size_t len);
|
||||
static void _ep_esr(usbdev_ep_t *ep);
|
||||
|
||||
static const usbdev_driver_t _driver = {
|
||||
.init = _init,
|
||||
.new_ep = _new_ep,
|
||||
.get = _get,
|
||||
.set = _set,
|
||||
.esr = _esr,
|
||||
.ep_init = _ep_init,
|
||||
.ep_get = _ep_get,
|
||||
.ep_set = _ep_set,
|
||||
.ep_esr = _ep_esr,
|
||||
.ready = _ep_ready,
|
||||
};
|
||||
|
||||
static inline usbdev_ep_t *_get_ep_in(nrfusb_t *usbdev, unsigned num)
|
||||
{
|
||||
return &usbdev->ep_ins[num];
|
||||
}
|
||||
|
||||
static inline usbdev_ep_t *_get_ep_out(nrfusb_t *usbdev, unsigned num)
|
||||
{
|
||||
return &usbdev->ep_outs[num];
|
||||
}
|
||||
|
||||
static inline usbdev_ep_t *_get_ep(nrfusb_t *usbdev,
|
||||
unsigned num, usb_ep_dir_t dir)
|
||||
{
|
||||
return dir == USB_EP_DIR_IN ? _get_ep_in(usbdev, num)
|
||||
: _get_ep_out(usbdev, num);
|
||||
}
|
||||
static inline void _enable_errata_199(void)
|
||||
{
|
||||
/* Contains the workaround as described in nRF52840 Errata 199 */
|
||||
*(volatile uint32_t *)0x40027C1C = 0x00000082;
|
||||
}
|
||||
|
||||
static inline void _disable_errata_199(void)
|
||||
{
|
||||
/* Contains the workaround as described in nRF52840 Errata 199 */
|
||||
*(volatile uint32_t *)0x40027C1C = 0x00000000;
|
||||
}
|
||||
|
||||
/* Contains the sequence as described in nRF52840 Errata 187 */
|
||||
static inline void poweron(nrfusb_t *usbdev)
|
||||
{
|
||||
/* Apply magic */
|
||||
*(volatile uint32_t *)0x4006EC00 = 0x00009375;
|
||||
*(volatile uint32_t *)0x4006ED14 = 0x00000003;
|
||||
*(volatile uint32_t *)0x4006EC00 = 0x00009375;
|
||||
|
||||
/* Enable the peripheral */
|
||||
usbdev->device->ENABLE = USBD_ENABLE_ENABLE_Msk;
|
||||
/* Waiting for peripheral to enable, this should take a few μs */
|
||||
while (!(usbdev->device->EVENTCAUSE & USBD_EVENTCAUSE_READY_Msk)) {}
|
||||
|
||||
usbdev->device->EVENTCAUSE &= ~USBD_EVENTCAUSE_READY_Msk;
|
||||
|
||||
/* Apply more magic */
|
||||
*(volatile uint32_t *)0x4006EC00 = 0x00009375;
|
||||
*(volatile uint32_t *)0x4006ED14 = 0x00000000;
|
||||
*(volatile uint32_t *)0x4006EC00 = 0x00009375;
|
||||
|
||||
/* Enable peripheral a second time */
|
||||
usbdev->device->ENABLE = USBD_ENABLE_ENABLE_Msk;
|
||||
}
|
||||
|
||||
static inline void poweroff(nrfusb_t *usbdev)
|
||||
{
|
||||
usbdev->device->ENABLE = 0x00;
|
||||
}
|
||||
|
||||
static void usb_attach(nrfusb_t *usbdev)
|
||||
{
|
||||
DEBUG("nrfusb: Enabling pull-up\n");
|
||||
usbdev->device->USBPULLUP = 0x01;
|
||||
}
|
||||
|
||||
static void usb_detach(nrfusb_t *usbdev)
|
||||
{
|
||||
DEBUG("nrfusb: Disabling pull-up\n");
|
||||
usbdev->device->USBPULLUP = 0x00;
|
||||
}
|
||||
|
||||
static void _ep_set_address(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
usbdev->device->EPOUT[ep->num].PTR = (uint32_t)ep->buf;
|
||||
}
|
||||
else {
|
||||
usbdev->device->EPIN[ep->num].PTR = (uint32_t)ep->buf;
|
||||
}
|
||||
}
|
||||
|
||||
static void _copy_setup(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
usb_setup_t *setup = (usb_setup_t*)ep->buf;
|
||||
setup->type = usbdev->device->BMREQUESTTYPE;
|
||||
setup->request = usbdev->device->BREQUEST;
|
||||
setup->value = usbdev->device->WVALUEL | usbdev->device->WVALUEH << 8;
|
||||
setup->index = usbdev->device->WINDEXL | usbdev->device->WINDEXH << 8;
|
||||
setup->length = usbdev->device->WLENGTHL | usbdev->device->WLENGTHH << 8;
|
||||
usbdev->sstate = usb_setup_is_read(setup) ? NRFUSB_SETUP_READ
|
||||
: NRFUSB_SETUP_WRITE;
|
||||
if (setup->request == USB_SETUP_REQ_SET_ADDRESS) {
|
||||
DEBUG("nrfusb: set address call\n");
|
||||
usbdev->sstate = NRFUSB_SETUP_READY;
|
||||
}
|
||||
}
|
||||
|
||||
static int _ep_set_size(usbdev_ep_t *ep)
|
||||
{
|
||||
/* TODO: validate size */
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
usbdev->device->EPOUT[ep->num].MAXCNT = (uint32_t)ep->len;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void _ep_enable(usbdev_ep_t *ep)
|
||||
{
|
||||
DEBUG("Enabling endpoint %u dir %s\n", ep->num, ep->dir == USB_EP_DIR_OUT ? "OUT" : "IN");
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
usbdev->device->EPOUTEN |= 1 << ep->num;
|
||||
}
|
||||
else {
|
||||
usbdev->device->EPINEN |= 1 << ep->num;
|
||||
}
|
||||
}
|
||||
|
||||
static void _ep_disable(usbdev_ep_t *ep)
|
||||
{
|
||||
/* TODO: validate size */
|
||||
DEBUG("disabling endpoint %u dir %s\n", ep->num, ep->dir == USB_EP_DIR_OUT ? "OUT" : "IN");
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
usbdev->device->EPOUTEN &= ~(1 << ep->num);
|
||||
}
|
||||
else {
|
||||
usbdev->device->EPINEN &= ~(1 << ep->num);
|
||||
}
|
||||
}
|
||||
|
||||
static void _ep_set_stall(usbdev_ep_t *ep, usbopt_enable_t enable)
|
||||
{
|
||||
/* TODO: validate size */
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
uint32_t val = (ep->num & USBD_EPSTALL_EP_Msk) |
|
||||
(ep->dir == USB_EP_DIR_IN ? USBD_EPSTALL_IO_Msk : 0) |
|
||||
(enable ? USBD_EPSTALL_STALL_Msk : 0);
|
||||
usbdev->device->EPSTALL = val;
|
||||
}
|
||||
|
||||
static usbopt_enable_t _ep_get_stall(usbdev_ep_t *ep)
|
||||
{
|
||||
/* TODO: validate size */
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
return usbdev->device->HALTED.EPOUT[ep->num] ? USBOPT_ENABLE
|
||||
: USBOPT_DISABLE;
|
||||
}
|
||||
else {
|
||||
return usbdev->device->HALTED.EPIN[ep->num] ? USBOPT_ENABLE
|
||||
: USBOPT_DISABLE;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t _ep_get_available(usbdev_ep_t *ep)
|
||||
{
|
||||
/* TODO: validate size */
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
return usbdev->device->SIZE.EPOUT[ep->num];
|
||||
}
|
||||
else {
|
||||
return usbdev->device->EPIN[ep->num].AMOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
static void _ep_dma_out(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
assert(ep->dir == USB_EP_DIR_OUT);
|
||||
_enable_errata_199();
|
||||
usbdev->device->TASKS_STARTEPOUT[ep->num] = 1;
|
||||
/* Block while waiting for dma to finish */
|
||||
while (!(usbdev->device->EVENTS_ENDEPOUT[ep->num])) {}
|
||||
|
||||
usbdev->device->EVENTS_ENDEPOUT[ep->num] = 0;
|
||||
_disable_errata_199();
|
||||
}
|
||||
|
||||
static void _ep_dma_in(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
assert(ep->dir == USB_EP_DIR_IN);
|
||||
_enable_errata_199();
|
||||
usbdev->device->TASKS_STARTEPIN[ep->num] = 1;
|
||||
/* Block while waiting for dma to finish */
|
||||
while (!(usbdev->device->EVENTS_ENDEPIN[ep->num])) {}
|
||||
|
||||
usbdev->device->EVENTS_ENDEPIN[ep->num] = 0;
|
||||
_disable_errata_199();
|
||||
}
|
||||
|
||||
usbdev_t *usbdev_get_ctx(unsigned num)
|
||||
{
|
||||
assert(num < NRF_USB_NUM_PERIPH);
|
||||
return &_usbdevs[num].usbdev;
|
||||
}
|
||||
|
||||
void usbdev_init_lowlevel(void)
|
||||
{
|
||||
for (size_t i = 0; i < NRF_USB_NUM_PERIPH; i++) {
|
||||
_usbdevs[i].usbdev.driver = &_driver;
|
||||
_usbdevs[i].device = NRF_USBD;
|
||||
}
|
||||
}
|
||||
|
||||
static void _init(usbdev_t *dev)
|
||||
{
|
||||
DEBUG("nrfusb: initializing\n");
|
||||
nrfusb_t *usbdev = (nrfusb_t*)dev;
|
||||
poweron(usbdev);
|
||||
usbdev->used = 0;
|
||||
usbdev->sstate = NRFUSB_SETUP_READY;
|
||||
|
||||
/* Enable a set of interrupts */
|
||||
usbdev->device->INTEN = USBD_INTEN_USBRESET_Msk | USBD_INTEN_EPDATA_Msk;
|
||||
NVIC_EnableIRQ(USBD_IRQn);
|
||||
}
|
||||
|
||||
static int _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 _set(usbdev_t *dev, usbopt_t opt,
|
||||
const void *value, size_t value_len)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)dev;
|
||||
(void)value_len;
|
||||
int res = -ENOTSUP;
|
||||
switch (opt) {
|
||||
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 usbdev_ep_t *_new_ep(usbdev_t *dev, usb_ep_type_t type, usb_ep_dir_t dir, size_t buf_len)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_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 if (type == USB_EP_TYPE_INTERRUPT || type == USB_EP_TYPE_BULK) {
|
||||
/* Find the first unassigned ep with proper dir */
|
||||
for (unsigned idx = 1; idx < NRF_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;
|
||||
DEBUG("nrfusb: Allocated new ep (%d %s)\n", res->num, res->dir == USB_EP_DIR_OUT ? "OUT" : "IN");
|
||||
if (usbdev->used + buf_len < NRF_USB_BUF_SPACE) {
|
||||
res->buf = usbdev->buffer + usbdev->used;
|
||||
res->len = buf_len;
|
||||
if (_ep_set_size(res) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
usbdev->used += buf_len;
|
||||
_ep_set_address(res);
|
||||
res->type = type;
|
||||
res->dev = dev;
|
||||
}
|
||||
else {
|
||||
DEBUG("nrfusb: error allocating buffer space\n");
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _ep_enable_irq(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_IN) {
|
||||
if (ep->num == 0) {
|
||||
usbdev->device->INTENSET = USBD_INTENSET_EP0DATADONE_Msk;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ep->num == 0) {
|
||||
usbdev->device->INTENSET = USBD_INTENSET_EP0SETUP_Msk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _ep_disable_irq(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_IN) {
|
||||
if (ep->num == 0) {
|
||||
usbdev->device->INTENCLR = USBD_INTENCLR_EP0DATADONE_Msk;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ep->num == 0) {
|
||||
usbdev->device->INTENCLR = USBD_INTENCLR_EP0SETUP_Msk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _ep_init(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
_ep_set_size(ep);
|
||||
_ep_set_address(ep);
|
||||
if (ep->num == 0) {
|
||||
usbdev->device->EVENTS_EP0SETUP = 0;
|
||||
}
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
usbdev->device->EVENTS_ENDEPOUT[ep->num] = 0;
|
||||
usbdev->device->EPDATASTATUS = 1 << ep->num;
|
||||
}
|
||||
else {
|
||||
usbdev->device->EPDATASTATUS = 1 << ep->num;
|
||||
}
|
||||
_ep_enable_irq(ep);
|
||||
}
|
||||
|
||||
static int _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("Unhandled get call: 0x%x\n", opt);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _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);
|
||||
_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_ready(ep, 0);
|
||||
res = sizeof(usbopt_enable_t);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG("Unhandled set call: 0x%x\n", opt);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _ep0_ready(usbdev_ep_t *ep, size_t len)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->dir == USB_EP_DIR_IN) {
|
||||
if (len == 0 && usbdev->sstate == NRFUSB_SETUP_WRITE) {
|
||||
usbdev->device->TASKS_EP0STATUS = 1;
|
||||
usbdev->usbdev.epcb(_get_ep_in(usbdev, 0), USBDEV_EVENT_ESR);
|
||||
usbdev->sstate = NRFUSB_SETUP_ACKIN;
|
||||
}
|
||||
else {
|
||||
usbdev->device->EPIN[0].PTR = (uint32_t)ep->buf;
|
||||
usbdev->device->EPIN[0].MAXCNT = (uint32_t)len;
|
||||
usbdev->device->TASKS_STARTEPIN[0] = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* USB_EP_DIR_OUT */
|
||||
if (len == 0) {
|
||||
if (usbdev->sstate == NRFUSB_SETUP_READ) {
|
||||
usbdev->device->TASKS_EP0STATUS = 1;
|
||||
usbdev->sstate = NRFUSB_SETUP_ACKOUT;
|
||||
usbdev->usbdev.epcb(_get_ep_out(usbdev, 0), USBDEV_EVENT_ESR);
|
||||
}
|
||||
else if (usbdev->sstate == NRFUSB_SETUP_READY) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
usbdev->device->TASKS_STARTEPOUT[ep->num] = 1;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int _ep_ready(usbdev_ep_t *ep, size_t len)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
if (ep->num == 0) {
|
||||
/* Endpoint 0 requires special handling as per datasheet sec 6.35.9 */
|
||||
return _ep0_ready(ep, len);
|
||||
}
|
||||
if (ep->dir == USB_EP_DIR_IN) {
|
||||
usbdev->device->EPIN[ep->num].PTR = (uint32_t)ep->buf;
|
||||
usbdev->device->EPIN[ep->num].MAXCNT = (uint32_t)len;
|
||||
_ep_dma_in(ep);
|
||||
}
|
||||
else {
|
||||
/* Write nonzero value to EPOUT to indicate ready */
|
||||
usbdev->device->SIZE.EPOUT[ep->num] = 1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void _esr(usbdev_t *dev)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)dev;
|
||||
if (usbdev->device->EVENTS_USBRESET) {
|
||||
DEBUG("nrfusb: reset condition\n");
|
||||
usbdev->device->EVENTS_USBRESET = 0;
|
||||
usbdev->sstate = NRFUSB_SETUP_READY;
|
||||
usbdev->usbdev.cb(&usbdev->usbdev, USBDEV_EVENT_RESET);
|
||||
usbdev->device->INTENSET = USBD_INTENSET_USBRESET_Msk;
|
||||
}
|
||||
}
|
||||
|
||||
static signed _ep0_esr(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
signed event = -1;
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
if (usbdev->sstate == NRFUSB_SETUP_ACKOUT) {
|
||||
usbdev->sstate = NRFUSB_SETUP_READY;
|
||||
event = USBDEV_EVENT_TR_COMPLETE;
|
||||
}
|
||||
else if (usbdev->device->EVENTS_EP0SETUP) {
|
||||
usbdev->device->EVENTS_EP0SETUP = 0;
|
||||
event = USBDEV_EVENT_TR_COMPLETE;
|
||||
/* Copy setup request info to buffer */
|
||||
_copy_setup(ep);
|
||||
if ((uint8_t)usbdev->device->BREQUEST == 0x05) {
|
||||
event = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (usbdev->sstate == NRFUSB_SETUP_ACKIN) {
|
||||
usbdev->sstate = NRFUSB_SETUP_READY;
|
||||
event = USBDEV_EVENT_TR_COMPLETE;
|
||||
}
|
||||
else if (usbdev->device->EVENTS_EP0DATADONE) {
|
||||
usbdev->device->EVENTS_EP0DATADONE = 0;
|
||||
event = USBDEV_EVENT_TR_COMPLETE;
|
||||
}
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
static void _ep_esr(usbdev_ep_t *ep)
|
||||
{
|
||||
nrfusb_t *usbdev = (nrfusb_t*)ep->dev;
|
||||
signed event = -1;
|
||||
if (ep->num == 0) {
|
||||
event = _ep0_esr(ep);
|
||||
}
|
||||
else {
|
||||
if (ep->dir == USB_EP_DIR_IN) {
|
||||
if (usbdev->device->EPDATASTATUS & 1 << ep->num) {
|
||||
usbdev->device->EPDATASTATUS = 1 << (ep->num);
|
||||
usbdev->device->EVENTS_EPDATA = 0;
|
||||
usbdev->device->INTENSET = USBD_INTENSET_EPDATA_Msk;
|
||||
|
||||
event = USBDEV_EVENT_TR_COMPLETE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (usbdev->device->EPDATASTATUS & 1 << (ep->num + 16)) {
|
||||
/* start dma to transfer payload to memory */
|
||||
_ep_dma_out(ep);
|
||||
|
||||
usbdev->device->EPDATASTATUS = 1 << (ep->num + 16);
|
||||
event = USBDEV_EVENT_TR_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event) {
|
||||
ep->dev->epcb(ep, event);
|
||||
}
|
||||
_ep_enable_irq(ep);
|
||||
}
|
||||
|
||||
void isr_usbd(void)
|
||||
{
|
||||
/* Only one usb peripheral possible at the moment */
|
||||
nrfusb_t *usbdev = &_usbdevs[0];
|
||||
/* Generic USB peripheral events */
|
||||
if (usbdev->device->EVENTS_USBRESET &&
|
||||
(usbdev->device->INTEN & USBD_INTEN_USBRESET_Msk)) {
|
||||
usbdev->device->INTENCLR = USBD_INTENCLR_USBRESET_Msk;
|
||||
usbdev->usbdev.cb(&usbdev->usbdev, USBDEV_EVENT_ESR);
|
||||
}
|
||||
else {
|
||||
/* Endpoint specific isr handling*/
|
||||
/* Endpoint 0 SETUP data received requests */
|
||||
if (usbdev->device->EVENTS_EP0SETUP &&
|
||||
(usbdev->device->INTEN & USBD_INTEN_EP0SETUP_Msk)) {
|
||||
usbdev->usbdev.epcb(_get_ep_out(usbdev, 0), USBDEV_EVENT_ESR);
|
||||
_ep_disable_irq(_get_ep_out(usbdev, 0));
|
||||
}
|
||||
if (usbdev->device->EVENTS_EP0DATADONE &&
|
||||
(usbdev->device->INTEN & USBD_INTEN_EP0DATADONE_Msk)) {
|
||||
usbdev->usbdev.epcb(_get_ep_in(usbdev, 0), USBDEV_EVENT_ESR);
|
||||
_ep_disable_irq(_get_ep_in(usbdev, 0));
|
||||
}
|
||||
if (usbdev->device->EVENTS_EPDATA && usbdev->device->EPDATASTATUS) {
|
||||
usbdev->device->EVENTS_EPDATA = 0;
|
||||
uint32_t epdatastatus = usbdev->device->EPDATASTATUS;
|
||||
while (epdatastatus) {
|
||||
unsigned epnum = bitarithm_lsb(epdatastatus);
|
||||
if (epnum > 16) {
|
||||
usbdev_ep_t *ep = _get_ep_out(usbdev, epnum - 16);
|
||||
if (ep->type != USB_EP_TYPE_NONE) {
|
||||
/* OUT type endpoint */
|
||||
usbdev->usbdev.epcb(ep,
|
||||
USBDEV_EVENT_ESR);
|
||||
}
|
||||
}
|
||||
else {
|
||||
usbdev_ep_t *ep = _get_ep_in(usbdev, epnum);
|
||||
if (ep->type != USB_EP_TYPE_NONE) {
|
||||
usbdev->usbdev.epcb(ep,
|
||||
USBDEV_EVENT_ESR);
|
||||
}
|
||||
}
|
||||
epdatastatus &= ~(1 << epnum);
|
||||
}
|
||||
}
|
||||
}
|
||||
cortexm_isr_end();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user