2018-07-06 16:16:47 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018, Nordic Semiconductor ASA
|
|
|
|
* Copyright (c) 2018 Sundar Subramaniyan <sundar.subramaniyan@gmail.com>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file usb_dc_nrfx.c
|
|
|
|
* @brief Nordic USB device controller driver
|
|
|
|
*
|
|
|
|
* The driver implements the interface between the USBD peripheral
|
|
|
|
* driver from nrfx package and the operating system.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <soc.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <kernel.h>
|
2020-01-25 05:35:03 -06:00
|
|
|
#include <drivers/usb/usb_dc.h>
|
2018-07-06 16:16:47 +02:00
|
|
|
#include <usb/usb_device.h>
|
2019-06-25 15:53:47 -04:00
|
|
|
#include <drivers/clock_control.h>
|
2019-01-15 10:27:20 +01:00
|
|
|
#include <drivers/clock_control/nrf_clock_control.h>
|
2018-07-06 16:16:47 +02:00
|
|
|
#include <nrfx_usbd.h>
|
2020-08-10 13:50:26 +02:00
|
|
|
#include <nrfx_power.h>
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
#define LOG_LEVEL CONFIG_USB_DRIVER_LOG_LEVEL
|
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(usb_nrfx);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2020-04-17 17:06:59 -07:00
|
|
|
/* USB device controller access from devicetree */
|
|
|
|
#define DT_DRV_COMPAT nordic_nrf_usbd
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
/**
|
|
|
|
* @brief nRF USBD peripheral states
|
|
|
|
*/
|
|
|
|
enum usbd_periph_state {
|
|
|
|
USBD_DETACHED,
|
|
|
|
USBD_ATTACHED,
|
|
|
|
USBD_POWERED,
|
|
|
|
USBD_SUSPENDED,
|
2019-02-11 12:25:00 +01:00
|
|
|
USBD_RESUMED,
|
2018-07-06 16:16:47 +02:00
|
|
|
USBD_DEFAULT,
|
|
|
|
USBD_ADDRESS_SET,
|
|
|
|
USBD_CONFIGURED,
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Endpoint event types.
|
|
|
|
*/
|
2019-01-04 16:20:30 +01:00
|
|
|
enum usbd_ep_event_type {
|
2018-07-06 16:16:47 +02:00
|
|
|
EP_EVT_SETUP_RECV,
|
|
|
|
EP_EVT_RECV_REQ,
|
|
|
|
EP_EVT_RECV_COMPLETE,
|
|
|
|
EP_EVT_WRITE_COMPLETE,
|
|
|
|
};
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
/**
|
|
|
|
* @brief USBD peripheral event types.
|
|
|
|
*/
|
|
|
|
enum usbd_event_type {
|
|
|
|
USBD_EVT_POWER,
|
|
|
|
USBD_EVT_EP,
|
|
|
|
USBD_EVT_RESET,
|
|
|
|
USBD_EVT_SOF,
|
|
|
|
USBD_EVT_REINIT
|
|
|
|
};
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Endpoint configuration.
|
|
|
|
*
|
|
|
|
* @param cb Endpoint callback.
|
|
|
|
* @param max_sz Max packet size supported by endpoint.
|
|
|
|
* @param en Enable/Disable flag.
|
|
|
|
* @param addr Endpoint address.
|
2020-04-06 17:08:14 +02:00
|
|
|
* @param type Endpoint transfer type.
|
2018-07-06 16:16:47 +02:00
|
|
|
*/
|
|
|
|
struct nrf_usbd_ep_cfg {
|
|
|
|
usb_dc_ep_callback cb;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t max_sz;
|
2018-07-06 16:16:47 +02:00
|
|
|
bool en;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t addr;
|
2020-04-06 17:08:14 +02:00
|
|
|
enum usb_dc_ep_transfer_type type;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
};
|
|
|
|
|
2020-12-07 05:53:28 -08:00
|
|
|
struct usbd_mem_block {
|
|
|
|
void *data;
|
|
|
|
};
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
/**
|
|
|
|
* @brief Endpoint buffer
|
|
|
|
*
|
|
|
|
* @param len Remaining length to be read/written.
|
|
|
|
* @param block Mempool block, for freeing up buffer after use.
|
|
|
|
* @param data Pointer to the data buffer for the endpoint.
|
|
|
|
* @param curr Pointer to the current offset in the endpoint buffer.
|
|
|
|
*/
|
|
|
|
struct nrf_usbd_ep_buf {
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t len;
|
2020-12-07 05:53:28 -08:00
|
|
|
struct usbd_mem_block block;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t *data;
|
|
|
|
uint8_t *curr;
|
2018-07-06 16:16:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Endpoint context
|
|
|
|
*
|
|
|
|
* @param cfg Endpoint configuration
|
|
|
|
* @param buf Endpoint buffer
|
2019-07-03 11:50:46 +02:00
|
|
|
* @param read_complete A flag indicating that DMA read operation
|
|
|
|
* has been completed.
|
|
|
|
* @param read_pending A flag indicating that the Host has requested
|
|
|
|
* a data transfer.
|
|
|
|
* @param write_in_progress A flag indicating that write operation has
|
|
|
|
* been scheduled.
|
2020-10-20 13:58:08 +02:00
|
|
|
* @param trans_zlp Flag required for Control IN Endpoint. It
|
|
|
|
* indicates that ZLP is required to end data
|
|
|
|
* stage of the control request.
|
2018-07-06 16:16:47 +02:00
|
|
|
*/
|
|
|
|
struct nrf_usbd_ep_ctx {
|
|
|
|
struct nrf_usbd_ep_cfg cfg;
|
|
|
|
struct nrf_usbd_ep_buf buf;
|
|
|
|
volatile bool read_complete;
|
|
|
|
volatile bool read_pending;
|
2018-11-13 16:11:52 +01:00
|
|
|
volatile bool write_in_progress;
|
2020-10-20 13:58:08 +02:00
|
|
|
bool trans_zlp;
|
2018-07-06 16:16:47 +02:00
|
|
|
};
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
/**
|
|
|
|
* @brief Endpoint event structure
|
|
|
|
*
|
|
|
|
* @param ep Endpoint control block pointer
|
|
|
|
* @param evt_type Event type
|
|
|
|
*/
|
|
|
|
struct usbd_ep_event {
|
|
|
|
struct nrf_usbd_ep_ctx *ep;
|
|
|
|
enum usbd_ep_event_type evt_type;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Power event structure
|
|
|
|
*
|
|
|
|
* @param state New USBD peripheral state.
|
|
|
|
*/
|
|
|
|
struct usbd_pwr_event {
|
|
|
|
enum usbd_periph_state state;
|
|
|
|
};
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
/**
|
|
|
|
* @brief Endpoint USB event
|
|
|
|
* Used by ISR to send events to work handler
|
|
|
|
*
|
|
|
|
* @param node Used by the kernel for FIFO management
|
|
|
|
* @param block Mempool block pointer for freeing up after use
|
2019-01-04 16:20:30 +01:00
|
|
|
* @param evt Event data field
|
|
|
|
* @param evt_type Type of event that has occurred from the USBD peripheral
|
2018-07-06 16:16:47 +02:00
|
|
|
*/
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event {
|
2018-07-06 16:16:47 +02:00
|
|
|
sys_snode_t node;
|
2020-12-07 05:53:28 -08:00
|
|
|
struct usbd_mem_block block;
|
2018-07-06 16:16:47 +02:00
|
|
|
union {
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_ep_event ep_evt;
|
|
|
|
struct usbd_pwr_event pwr_evt;
|
|
|
|
} evt;
|
|
|
|
enum usbd_event_type evt_type;
|
2018-07-06 16:16:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2020-12-23 13:34:47 +01:00
|
|
|
* @brief Fifo element slab
|
2018-07-06 16:16:47 +02:00
|
|
|
* Used for allocating fifo elements to pass from ISR to work handler
|
|
|
|
* TODO: The number of FIFO elements is an arbitrary number now but it should
|
|
|
|
* be derived from the theoretical number of backlog events possible depending
|
|
|
|
* on the number of endpoints configured.
|
|
|
|
*/
|
2020-12-23 13:34:47 +01:00
|
|
|
#define FIFO_ELEM_SZ sizeof(struct usbd_event)
|
2018-10-16 08:30:22 -04:00
|
|
|
#define FIFO_ELEM_ALIGN sizeof(unsigned int)
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2020-12-23 13:34:47 +01:00
|
|
|
K_MEM_SLAB_DEFINE(fifo_elem_slab, FIFO_ELEM_SZ,
|
|
|
|
CONFIG_USB_NRFX_EVT_QUEUE_SIZE, FIFO_ELEM_ALIGN);
|
2019-07-03 12:09:15 +02:00
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
/** Number of IN Endpoints configured (including control) */
|
2020-04-17 17:06:59 -07:00
|
|
|
#define CFG_EPIN_CNT (DT_INST_PROP(0, num_in_endpoints) + \
|
|
|
|
DT_INST_PROP(0, num_bidir_endpoints))
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
/** Number of OUT Endpoints configured (including control) */
|
2020-04-17 17:06:59 -07:00
|
|
|
#define CFG_EPOUT_CNT (DT_INST_PROP(0, num_out_endpoints) + \
|
|
|
|
DT_INST_PROP(0, num_bidir_endpoints))
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
/** Number of ISO IN Endpoints */
|
2020-04-17 17:06:59 -07:00
|
|
|
#define CFG_EP_ISOIN_CNT DT_INST_PROP(0, num_isoin_endpoints)
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
/** Number of ISO OUT Endpoints */
|
2020-04-17 17:06:59 -07:00
|
|
|
#define CFG_EP_ISOOUT_CNT DT_INST_PROP(0, num_isoout_endpoints)
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
/** ISO endpoint index */
|
|
|
|
#define EP_ISOIN_INDEX CFG_EPIN_CNT
|
|
|
|
#define EP_ISOOUT_INDEX (CFG_EPIN_CNT + CFG_EP_ISOIN_CNT + CFG_EPOUT_CNT)
|
|
|
|
|
2019-07-03 13:37:26 +02:00
|
|
|
#define EP_BUF_MAX_SZ 64UL
|
|
|
|
#define ISO_EP_BUF_MAX_SZ 1024UL
|
|
|
|
|
2020-12-23 13:15:04 +01:00
|
|
|
/**
|
|
|
|
* @brief Output endpoint buffers
|
|
|
|
* Used as buffers for the endpoints' data transfer
|
|
|
|
* Max buffers size possible: 1536 Bytes (8 EP * 64B + 1 ISO * 1024B)
|
|
|
|
*/
|
|
|
|
static uint8_t ep_out_bufs[CFG_EPOUT_CNT][EP_BUF_MAX_SZ]
|
|
|
|
__aligned(sizeof(uint32_t));
|
|
|
|
static uint8_t ep_isoout_bufs[CFG_EP_ISOOUT_CNT][ISO_EP_BUF_MAX_SZ]
|
|
|
|
__aligned(sizeof(uint32_t));
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
/** Total endpoints configured */
|
|
|
|
#define CFG_EP_CNT (CFG_EPIN_CNT + CFG_EP_ISOIN_CNT + \
|
|
|
|
CFG_EPOUT_CNT + CFG_EP_ISOOUT_CNT)
|
|
|
|
|
|
|
|
/**
|
2019-01-04 16:20:30 +01:00
|
|
|
* @brief USBD control structure
|
2018-07-06 16:16:47 +02:00
|
|
|
*
|
2019-01-04 16:20:30 +01:00
|
|
|
* @param status_cb Status callback for USB DC notifications
|
2020-10-20 13:28:02 +02:00
|
|
|
* @param setup Setup packet for Control requests
|
2020-02-13 14:03:29 +01:00
|
|
|
* @param hfxo_cli Onoff client used to control HFXO
|
|
|
|
* @param hfxo_mgr Pointer to onoff manager associated with HFXO.
|
|
|
|
* @param clk_requested Flag used to protect against double stop.
|
2018-07-06 16:16:47 +02:00
|
|
|
* @param attached USBD Attached flag
|
|
|
|
* @param ready USBD Ready flag set after pullup
|
|
|
|
* @param usb_work USBD work item
|
2018-11-13 16:11:52 +01:00
|
|
|
* @param drv_lock Mutex for thread-safe nrfx driver use
|
2018-07-06 16:16:47 +02:00
|
|
|
* @param ep_ctx Endpoint contexts
|
2019-01-04 16:20:30 +01:00
|
|
|
* @param ctrl_read_len State of control read operation (EP0).
|
2018-07-06 16:16:47 +02:00
|
|
|
*/
|
|
|
|
struct nrf_usbd_ctx {
|
|
|
|
usb_dc_status_callback status_cb;
|
2020-10-20 13:28:02 +02:00
|
|
|
struct usb_setup_packet setup;
|
2020-02-13 14:03:29 +01:00
|
|
|
struct onoff_client hfxo_cli;
|
|
|
|
struct onoff_manager *hfxo_mgr;
|
|
|
|
atomic_t clk_requested;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
bool attached;
|
|
|
|
bool ready;
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
struct k_work usb_work;
|
|
|
|
struct k_mutex drv_lock;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
struct nrf_usbd_ep_ctx ep_ctx[CFG_EP_CNT];
|
2018-11-28 17:19:12 +01:00
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
uint16_t ctrl_read_len;
|
2018-07-06 16:16:47 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-07-22 08:56:55 +02:00
|
|
|
/* FIFO used for queuing up events from ISR. */
|
|
|
|
K_FIFO_DEFINE(usbd_evt_fifo);
|
|
|
|
|
|
|
|
/* Work queue used for handling the ISR events (i.e. for notifying the USB
|
|
|
|
* device stack, for executing the endpoints callbacks, etc.) out of the ISR
|
|
|
|
* context.
|
|
|
|
* The system work queue cannot be used for this purpose as it might be used in
|
|
|
|
* applications for scheduling USB transfers and this could lead to a deadlock
|
|
|
|
* when the USB device stack would not be notified about certain event because
|
|
|
|
* of a system work queue item waiting for a USB transfer to be finished.
|
|
|
|
*/
|
|
|
|
static struct k_work_q usbd_work_queue;
|
2020-07-31 12:29:38 -07:00
|
|
|
static K_KERNEL_STACK_DEFINE(usbd_work_queue_stack,
|
2019-07-22 08:56:55 +02:00
|
|
|
CONFIG_USB_NRFX_WORK_QUEUE_STACK_SIZE);
|
2019-01-28 16:22:05 -06:00
|
|
|
|
|
|
|
|
|
|
|
static struct nrf_usbd_ctx usbd_ctx = {
|
|
|
|
.attached = false,
|
|
|
|
.ready = false,
|
|
|
|
};
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
static inline struct nrf_usbd_ctx *get_usbd_ctx(void)
|
|
|
|
{
|
|
|
|
return &usbd_ctx;
|
|
|
|
}
|
|
|
|
|
2019-03-13 11:36:04 +01:00
|
|
|
static inline bool dev_attached(void)
|
|
|
|
{
|
|
|
|
return get_usbd_ctx()->attached;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool dev_ready(void)
|
|
|
|
{
|
|
|
|
return get_usbd_ctx()->ready;
|
|
|
|
}
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
static inline nrfx_usbd_ep_t ep_addr_to_nrfx(uint8_t ep)
|
|
|
|
{
|
|
|
|
return (nrfx_usbd_ep_t)ep;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint8_t nrfx_addr_to_ep(nrfx_usbd_ep_t ep)
|
|
|
|
{
|
|
|
|
return (uint8_t)ep;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static inline bool ep_is_valid(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2020-07-02 13:41:54 +02:00
|
|
|
uint8_t ep_num = USB_EP_GET_IDX(ep);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
if (NRF_USBD_EPIN_CHECK(ep)) {
|
2019-07-02 00:55:54 +02:00
|
|
|
if (unlikely(ep_num == NRF_USBD_EPISO_FIRST)) {
|
2018-07-06 16:16:47 +02:00
|
|
|
if (CFG_EP_ISOIN_CNT == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ep_num >= CFG_EPIN_CNT) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2019-07-02 00:55:54 +02:00
|
|
|
if (unlikely(ep_num == NRF_USBD_EPISO_FIRST)) {
|
2018-07-06 16:16:47 +02:00
|
|
|
if (CFG_EP_ISOOUT_CNT == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ep_num >= CFG_EPOUT_CNT) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static struct nrf_usbd_ep_ctx *endpoint_ctx(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ctx *ctx;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t ep_num;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
if (!ep_is_valid(ep)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx = get_usbd_ctx();
|
|
|
|
ep_num = NRF_USBD_EP_NR_GET(ep);
|
|
|
|
|
|
|
|
if (NRF_USBD_EPIN_CHECK(ep)) {
|
|
|
|
if (unlikely(NRF_USBD_EPISO_CHECK(ep))) {
|
|
|
|
return &ctx->ep_ctx[EP_ISOIN_INDEX];
|
|
|
|
} else {
|
|
|
|
return &ctx->ep_ctx[ep_num];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (unlikely(NRF_USBD_EPISO_CHECK(ep))) {
|
|
|
|
return &ctx->ep_ctx[EP_ISOOUT_INDEX];
|
|
|
|
} else {
|
|
|
|
return &ctx->ep_ctx[CFG_EPIN_CNT +
|
|
|
|
CFG_EP_ISOIN_CNT +
|
|
|
|
ep_num];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static struct nrf_usbd_ep_ctx *in_endpoint_ctx(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
return endpoint_ctx(NRF_USBD_EPIN(ep));
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static struct nrf_usbd_ep_ctx *out_endpoint_ctx(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
return endpoint_ctx(NRF_USBD_EPOUT(ep));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Schedule USBD event processing.
|
|
|
|
*
|
|
|
|
* Should be called after usbd_evt_put().
|
|
|
|
*/
|
|
|
|
static inline void usbd_work_schedule(void)
|
|
|
|
{
|
2019-07-22 08:56:55 +02:00
|
|
|
k_work_submit_to_queue(&usbd_work_queue, &get_usbd_ctx()->usb_work);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Free previously allocated USBD event.
|
|
|
|
*
|
|
|
|
* Should be called after usbd_evt_get().
|
|
|
|
*
|
|
|
|
* @param Pointer to the USBD event structure.
|
|
|
|
*/
|
2019-01-04 16:20:30 +01:00
|
|
|
static inline void usbd_evt_free(struct usbd_event *ev)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2020-12-23 13:34:47 +01:00
|
|
|
k_mem_slab_free(&fifo_elem_slab, (void **)&ev->block.data);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Enqueue USBD event.
|
|
|
|
*
|
|
|
|
* @param Pointer to the previously allocated and filled event structure.
|
|
|
|
*/
|
2019-01-04 16:20:30 +01:00
|
|
|
static inline void usbd_evt_put(struct usbd_event *ev)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2019-07-22 08:56:55 +02:00
|
|
|
k_fifo_put(&usbd_evt_fifo, ev);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Get next enqueued USBD event if present.
|
|
|
|
*/
|
2019-01-04 16:20:30 +01:00
|
|
|
static inline struct usbd_event *usbd_evt_get(void)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2019-07-22 08:56:55 +02:00
|
|
|
return k_fifo_get(&usbd_evt_fifo, K_NO_WAIT);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Drop all enqueued events.
|
|
|
|
*/
|
|
|
|
static inline void usbd_evt_flush(void)
|
|
|
|
{
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event *ev;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
do {
|
|
|
|
ev = usbd_evt_get();
|
|
|
|
if (ev) {
|
|
|
|
usbd_evt_free(ev);
|
|
|
|
}
|
|
|
|
} while (ev != NULL);
|
|
|
|
}
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
/**
|
|
|
|
* @brief Allocate USBD event.
|
|
|
|
*
|
|
|
|
* This function should be called prior to usbd_evt_put().
|
|
|
|
*
|
|
|
|
* @returns Pointer to the allocated event or NULL if there was no space left.
|
|
|
|
*/
|
|
|
|
static inline struct usbd_event *usbd_evt_alloc(void)
|
|
|
|
{
|
|
|
|
struct usbd_event *ev;
|
2020-12-07 05:53:28 -08:00
|
|
|
struct usbd_mem_block block;
|
2019-01-04 16:20:30 +01:00
|
|
|
|
2020-12-23 13:34:47 +01:00
|
|
|
if (k_mem_slab_alloc(&fifo_elem_slab,
|
|
|
|
(void **)&block.data, K_NO_WAIT)) {
|
2019-01-04 16:20:30 +01:00
|
|
|
LOG_ERR("USBD event allocation failed!");
|
|
|
|
|
2019-12-09 14:06:44 +01:00
|
|
|
/*
|
2019-07-03 11:50:46 +02:00
|
|
|
* Allocation may fail if workqueue thread is starved or event
|
|
|
|
* queue size is too small (CONFIG_USB_NRFX_EVT_QUEUE_SIZE).
|
|
|
|
* Wipe all events, free the space and schedule
|
|
|
|
* reinitialization.
|
2019-01-04 16:20:30 +01:00
|
|
|
*/
|
|
|
|
usbd_evt_flush();
|
|
|
|
|
2020-12-23 13:34:47 +01:00
|
|
|
if (k_mem_slab_alloc(&fifo_elem_slab, (void **)&block.data, K_NO_WAIT)) {
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("USBD event memory corrupted");
|
2019-01-04 16:20:30 +01:00
|
|
|
__ASSERT_NO_MSG(0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev = (struct usbd_event *)block.data;
|
|
|
|
ev->block = block;
|
|
|
|
ev->evt_type = USBD_EVT_REINIT;
|
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev = (struct usbd_event *)block.data;
|
|
|
|
ev->block = block;
|
|
|
|
|
|
|
|
return ev;
|
|
|
|
}
|
|
|
|
|
2020-12-17 18:02:59 +01:00
|
|
|
static void submit_dc_power_event(enum usbd_periph_state state)
|
|
|
|
{
|
|
|
|
struct usbd_event *ev = usbd_evt_alloc();
|
|
|
|
|
|
|
|
if (!ev) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev->evt_type = USBD_EVT_POWER;
|
|
|
|
ev->evt.pwr_evt.state = state;
|
|
|
|
|
|
|
|
usbd_evt_put(ev);
|
|
|
|
|
|
|
|
if (usbd_ctx.attached) {
|
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if CONFIG_USB_NRFX_ATTACHED_EVENT_DELAY
|
|
|
|
static void attached_evt_delay_handler(struct k_timer *timer)
|
|
|
|
{
|
|
|
|
LOG_DBG("ATTACHED event delay done");
|
|
|
|
submit_dc_power_event(USBD_ATTACHED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static K_TIMER_DEFINE(delay_timer, attached_evt_delay_handler, NULL);
|
|
|
|
#endif
|
|
|
|
|
2020-08-10 13:50:26 +02:00
|
|
|
static void usb_dc_power_event_handler(nrfx_power_usb_evt_t event)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2019-01-04 16:20:30 +01:00
|
|
|
enum usbd_periph_state new_state;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
switch (event) {
|
2020-08-10 13:50:26 +02:00
|
|
|
case NRFX_POWER_USB_EVT_DETECTED:
|
2020-12-17 18:02:59 +01:00
|
|
|
#if !CONFIG_USB_NRFX_ATTACHED_EVENT_DELAY
|
2019-01-04 16:20:30 +01:00
|
|
|
new_state = USBD_ATTACHED;
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
2020-12-17 18:02:59 +01:00
|
|
|
#else
|
|
|
|
LOG_DBG("ATTACHED event delayed");
|
|
|
|
k_timer_start(&delay_timer,
|
|
|
|
K_MSEC(CONFIG_USB_NRFX_ATTACHED_EVENT_DELAY),
|
|
|
|
K_NO_WAIT);
|
|
|
|
return;
|
|
|
|
#endif
|
2020-08-10 13:50:26 +02:00
|
|
|
case NRFX_POWER_USB_EVT_READY:
|
2019-01-04 16:20:30 +01:00
|
|
|
new_state = USBD_POWERED;
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
2020-08-10 13:50:26 +02:00
|
|
|
case NRFX_POWER_USB_EVT_REMOVED:
|
2019-01-04 16:20:30 +01:00
|
|
|
new_state = USBD_DETACHED;
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
default:
|
2020-08-10 13:50:26 +02:00
|
|
|
LOG_ERR("Unknown USB power event %d", event);
|
2018-07-06 16:16:47 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-17 18:02:59 +01:00
|
|
|
submit_dc_power_event(new_state);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
2020-02-13 14:03:29 +01:00
|
|
|
/* Stopping HFXO, algorithm supports case when stop comes before clock is
|
|
|
|
* started. In that case, it is stopped from the callback context.
|
2018-07-06 16:16:47 +02:00
|
|
|
*/
|
2020-02-13 14:03:29 +01:00
|
|
|
static int hfxo_stop(struct nrf_usbd_ctx *ctx)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2020-02-13 14:03:29 +01:00
|
|
|
if (atomic_cas(&ctx->clk_requested, 1, 0)) {
|
|
|
|
return onoff_cancel_or_release(ctx->hfxo_mgr, &ctx->hfxo_cli);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
2020-02-13 14:03:29 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2020-02-13 14:03:29 +01:00
|
|
|
static int hfxo_start(struct nrf_usbd_ctx *ctx)
|
|
|
|
{
|
|
|
|
if (atomic_cas(&ctx->clk_requested, 0, 1)) {
|
|
|
|
sys_notify_init_spinwait(&ctx->hfxo_cli.notify);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2020-02-13 14:03:29 +01:00
|
|
|
return onoff_request(ctx->hfxo_mgr, &ctx->hfxo_cli);
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-31 12:05:15 +01:00
|
|
|
return 0;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usbd_enable_endpoints(struct nrf_usbd_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
int i;
|
|
|
|
|
2019-01-22 15:00:47 +01:00
|
|
|
for (i = 0; i < CFG_EPIN_CNT; i++) {
|
2018-07-06 16:16:47 +02:00
|
|
|
ep_ctx = in_endpoint_ctx(i);
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
|
|
|
|
if (ep_ctx->cfg.en) {
|
|
|
|
nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep_ctx->cfg.addr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-22 15:00:47 +01:00
|
|
|
if (CFG_EP_ISOIN_CNT) {
|
|
|
|
ep_ctx = in_endpoint_ctx(NRF_USBD_EPIN(8));
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
|
|
|
|
if (ep_ctx->cfg.en) {
|
|
|
|
nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep_ctx->cfg.addr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < CFG_EPOUT_CNT; i++) {
|
2018-07-06 16:16:47 +02:00
|
|
|
ep_ctx = out_endpoint_ctx(i);
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
|
|
|
|
if (ep_ctx->cfg.en) {
|
|
|
|
nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep_ctx->cfg.addr));
|
|
|
|
}
|
|
|
|
}
|
2019-01-22 15:00:47 +01:00
|
|
|
|
|
|
|
if (CFG_EP_ISOOUT_CNT) {
|
|
|
|
ep_ctx = out_endpoint_ctx(NRF_USBD_EPOUT(8));
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
|
|
|
|
if (ep_ctx->cfg.en) {
|
|
|
|
nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep_ctx->cfg.addr));
|
|
|
|
}
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
/**
|
|
|
|
* @brief Reset endpoint state.
|
|
|
|
*
|
|
|
|
* Resets the internal logic state for a given endpoint.
|
|
|
|
*
|
|
|
|
* @param[in] ep_cts Endpoint structure control block
|
|
|
|
*/
|
|
|
|
static void ep_ctx_reset(struct nrf_usbd_ep_ctx *ep_ctx)
|
|
|
|
{
|
|
|
|
ep_ctx->buf.data = ep_ctx->buf.block.data;
|
|
|
|
ep_ctx->buf.curr = ep_ctx->buf.data;
|
2018-11-29 11:12:22 -08:00
|
|
|
ep_ctx->buf.len = 0U;
|
2018-11-13 16:11:52 +01:00
|
|
|
|
2020-05-13 07:11:06 -07:00
|
|
|
/* Abort ongoing write operation. */
|
|
|
|
if (ep_ctx->write_in_progress) {
|
|
|
|
nrfx_usbd_ep_abort(ep_addr_to_nrfx(ep_ctx->cfg.addr));
|
|
|
|
}
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx->read_complete = true;
|
|
|
|
ep_ctx->read_pending = false;
|
|
|
|
ep_ctx->write_in_progress = false;
|
2020-10-20 13:58:08 +02:00
|
|
|
ep_ctx->trans_zlp = false;
|
2018-11-13 16:11:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Initialize all endpoint structures.
|
|
|
|
*
|
|
|
|
* Endpoint buffers are allocated during the first call of this function.
|
|
|
|
* This function may also be called again on every USB reset event
|
|
|
|
* to reinitialize the state of all endpoints.
|
|
|
|
*/
|
|
|
|
static int eps_ctx_init(void)
|
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t i;
|
2018-11-13 16:11:52 +01:00
|
|
|
|
2018-11-29 11:12:22 -08:00
|
|
|
for (i = 0U; i < CFG_EPIN_CNT; i++) {
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx = in_endpoint_ctx(i);
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
ep_ctx_reset(ep_ctx);
|
|
|
|
}
|
|
|
|
|
2018-11-29 11:12:22 -08:00
|
|
|
for (i = 0U; i < CFG_EPOUT_CNT; i++) {
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx = out_endpoint_ctx(i);
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
|
|
|
|
if (!ep_ctx->buf.block.data) {
|
2020-12-23 13:15:04 +01:00
|
|
|
ep_ctx->buf.block.data = ep_out_bufs[i];
|
2018-11-13 16:11:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx_reset(ep_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CFG_EP_ISOIN_CNT) {
|
|
|
|
ep_ctx = in_endpoint_ctx(NRF_USBD_EPIN(8));
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
ep_ctx_reset(ep_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CFG_EP_ISOOUT_CNT) {
|
2020-12-23 13:15:04 +01:00
|
|
|
BUILD_ASSERT(CFG_EP_ISOOUT_CNT <= 1);
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx = out_endpoint_ctx(NRF_USBD_EPOUT(8));
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
|
|
|
|
if (!ep_ctx->buf.block.data) {
|
2020-12-23 13:15:04 +01:00
|
|
|
ep_ctx->buf.block.data = ep_isoout_bufs[0];
|
2018-11-13 16:11:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx_reset(ep_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void eps_ctx_uninit(void)
|
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t i;
|
2018-11-13 16:11:52 +01:00
|
|
|
|
2018-11-29 11:12:22 -08:00
|
|
|
for (i = 0U; i < CFG_EPIN_CNT; i++) {
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx = in_endpoint_ctx(i);
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
memset(ep_ctx, 0, sizeof(*ep_ctx));
|
|
|
|
}
|
|
|
|
|
2018-11-29 11:12:22 -08:00
|
|
|
for (i = 0U; i < CFG_EPOUT_CNT; i++) {
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx = out_endpoint_ctx(i);
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
memset(ep_ctx, 0, sizeof(*ep_ctx));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CFG_EP_ISOIN_CNT) {
|
|
|
|
ep_ctx = in_endpoint_ctx(NRF_USBD_EPIN(8));
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
memset(ep_ctx, 0, sizeof(*ep_ctx));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CFG_EP_ISOOUT_CNT) {
|
|
|
|
ep_ctx = out_endpoint_ctx(NRF_USBD_EPOUT(8));
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
memset(ep_ctx, 0, sizeof(*ep_ctx));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
static inline void usbd_work_process_pwr_events(struct usbd_pwr_event *pwr_evt)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2019-01-04 16:20:30 +01:00
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
2020-02-13 14:03:29 +01:00
|
|
|
int err;
|
2019-01-04 16:20:30 +01:00
|
|
|
|
|
|
|
switch (pwr_evt->state) {
|
2018-07-06 16:16:47 +02:00
|
|
|
case USBD_ATTACHED:
|
2019-04-02 11:35:24 +02:00
|
|
|
if (!nrfx_usbd_is_enabled()) {
|
|
|
|
LOG_DBG("USB detected");
|
|
|
|
nrfx_usbd_enable();
|
2020-02-13 14:03:29 +01:00
|
|
|
err = hfxo_start(ctx);
|
|
|
|
__ASSERT_NO_MSG(err >= 0);
|
2019-04-02 11:35:24 +02:00
|
|
|
}
|
2019-01-31 12:05:15 +01:00
|
|
|
|
2019-03-13 11:36:04 +01:00
|
|
|
/* No callback here.
|
|
|
|
* Stack will be notified when the peripheral is ready.
|
|
|
|
*/
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case USBD_POWERED:
|
|
|
|
usbd_enable_endpoints(ctx);
|
|
|
|
nrfx_usbd_start(true);
|
|
|
|
ctx->ready = true;
|
2019-01-04 16:20:30 +01:00
|
|
|
|
2019-03-13 11:36:04 +01:00
|
|
|
LOG_DBG("USB Powered");
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
if (ctx->status_cb) {
|
|
|
|
ctx->status_cb(USB_DC_CONNECTED, NULL);
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case USBD_DETACHED:
|
|
|
|
ctx->ready = false;
|
|
|
|
nrfx_usbd_disable();
|
2020-02-13 14:03:29 +01:00
|
|
|
err = hfxo_stop(ctx);
|
|
|
|
__ASSERT_NO_MSG(err >= 0);
|
2019-01-04 16:20:30 +01:00
|
|
|
|
2019-03-13 11:36:04 +01:00
|
|
|
LOG_DBG("USB Removed");
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
if (ctx->status_cb) {
|
|
|
|
ctx->status_cb(USB_DC_DISCONNECTED, NULL);
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
|
2019-02-11 12:25:00 +01:00
|
|
|
case USBD_SUSPENDED:
|
2019-03-13 11:36:04 +01:00
|
|
|
if (dev_ready()) {
|
|
|
|
nrfx_usbd_suspend();
|
|
|
|
LOG_DBG("USB Suspend state");
|
2019-02-11 12:25:00 +01:00
|
|
|
|
2019-03-13 11:36:04 +01:00
|
|
|
if (ctx->status_cb) {
|
|
|
|
ctx->status_cb(USB_DC_SUSPEND, NULL);
|
|
|
|
}
|
2019-02-11 12:25:00 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USBD_RESUMED:
|
2019-03-13 11:36:04 +01:00
|
|
|
if (ctx->status_cb && dev_ready()) {
|
|
|
|
LOG_DBG("USB resume");
|
2019-02-11 12:25:00 +01:00
|
|
|
ctx->status_cb(USB_DC_RESUME, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void usbd_work_process_setup(struct nrf_usbd_ep_ctx *ep_ctx)
|
|
|
|
{
|
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
|
|
|
__ASSERT(ep_ctx->cfg.type == USB_DC_EP_CONTROL,
|
|
|
|
"Invalid event on CTRL EP.");
|
|
|
|
|
|
|
|
struct usb_setup_packet *usbd_setup;
|
|
|
|
|
|
|
|
/* SETUP packets are handled by USBD hardware.
|
|
|
|
* For compatibility with the USB stack,
|
|
|
|
* SETUP packet must be reassembled.
|
|
|
|
*/
|
|
|
|
usbd_setup = (struct usb_setup_packet *)ep_ctx->buf.data;
|
|
|
|
memset(usbd_setup, 0, sizeof(struct usb_setup_packet));
|
2019-11-07 22:07:47 +01:00
|
|
|
usbd_setup->bmRequestType = nrf_usbd_setup_bmrequesttype_get(NRF_USBD);
|
|
|
|
usbd_setup->bRequest = nrf_usbd_setup_brequest_get(NRF_USBD);
|
|
|
|
usbd_setup->wValue = nrf_usbd_setup_wvalue_get(NRF_USBD);
|
|
|
|
usbd_setup->wIndex = nrf_usbd_setup_windex_get(NRF_USBD);
|
|
|
|
usbd_setup->wLength = nrf_usbd_setup_wlength_get(NRF_USBD);
|
2018-07-06 16:16:47 +02:00
|
|
|
ep_ctx->buf.len = sizeof(struct usb_setup_packet);
|
|
|
|
|
2020-10-20 13:28:02 +02:00
|
|
|
/* Copy setup packet to driver internal structure */
|
|
|
|
memcpy(&usbd_ctx.setup, usbd_setup, sizeof(struct usb_setup_packet));
|
|
|
|
|
2020-05-21 10:45:45 +02:00
|
|
|
LOG_DBG("SETUP: bR:0x%02x bmRT:0x%02x wV:0x%04x wI:0x%04x wL:%d",
|
2020-05-27 11:26:57 -05:00
|
|
|
(uint32_t)usbd_setup->bRequest,
|
|
|
|
(uint32_t)usbd_setup->bmRequestType,
|
|
|
|
(uint32_t)usbd_setup->wValue,
|
|
|
|
(uint32_t)usbd_setup->wIndex,
|
|
|
|
(uint32_t)usbd_setup->wLength);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
/* Inform the stack. */
|
|
|
|
ep_ctx->cfg.cb(ep_ctx->cfg.addr, USB_DC_EP_SETUP);
|
|
|
|
|
2018-11-28 17:19:12 +01:00
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
|
|
|
|
2021-05-12 11:17:06 +02:00
|
|
|
if (usb_reqtype_is_to_device(usbd_setup) && usbd_setup->wLength) {
|
2020-05-21 10:45:45 +02:00
|
|
|
ctx->ctrl_read_len = usbd_setup->wLength;
|
|
|
|
/* Allow data chunk on EP0 OUT */
|
2018-07-06 16:16:47 +02:00
|
|
|
nrfx_usbd_setup_data_clear();
|
2018-11-28 17:19:12 +01:00
|
|
|
} else {
|
2019-03-26 19:57:45 -06:00
|
|
|
ctx->ctrl_read_len = 0U;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void usbd_work_process_recvreq(struct nrf_usbd_ctx *ctx,
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx)
|
|
|
|
{
|
|
|
|
if (!ep_ctx->read_pending) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!ep_ctx->read_complete) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx->read_pending = false;
|
|
|
|
ep_ctx->read_complete = false;
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_lock(&ctx->drv_lock, K_FOREVER);
|
2018-07-06 16:16:47 +02:00
|
|
|
NRFX_USBD_TRANSFER_OUT(transfer, ep_ctx->buf.data,
|
|
|
|
ep_ctx->cfg.max_sz);
|
|
|
|
nrfx_err_t err = nrfx_usbd_ep_transfer(
|
|
|
|
ep_addr_to_nrfx(ep_ctx->cfg.addr), &transfer);
|
|
|
|
if (err != NRFX_SUCCESS) {
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("nRF USBD transfer error (OUT): 0x%02x", err);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
static inline void usbd_work_process_ep_events(struct usbd_ep_event *ep_evt)
|
|
|
|
{
|
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx = ep_evt->ep;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
__ASSERT_NO_MSG(ep_ctx);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
switch (ep_evt->evt_type) {
|
|
|
|
case EP_EVT_SETUP_RECV:
|
|
|
|
usbd_work_process_setup(ep_ctx);
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
case EP_EVT_RECV_REQ:
|
|
|
|
usbd_work_process_recvreq(ctx, ep_ctx);
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
case EP_EVT_RECV_COMPLETE:
|
|
|
|
ep_ctx->cfg.cb(ep_ctx->cfg.addr,
|
|
|
|
USB_DC_EP_DATA_OUT);
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
case EP_EVT_WRITE_COMPLETE:
|
2020-10-20 13:58:08 +02:00
|
|
|
if (ep_ctx->cfg.type == USB_DC_EP_CONTROL &&
|
|
|
|
!ep_ctx->trans_zlp) {
|
|
|
|
/* Trigger the hardware to perform
|
|
|
|
* status stage, but only if there is
|
|
|
|
* no ZLP required.
|
|
|
|
*/
|
2019-01-04 16:20:30 +01:00
|
|
|
k_mutex_lock(&ctx->drv_lock, K_FOREVER);
|
|
|
|
nrfx_usbd_setup_clear();
|
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
2019-01-04 16:20:30 +01:00
|
|
|
ep_ctx->cfg.cb(ep_ctx->cfg.addr,
|
|
|
|
USB_DC_EP_DATA_IN);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
static void usbd_event_transfer_ctrl(nrfx_usbd_evt_t const *const p_event)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx =
|
|
|
|
endpoint_ctx(p_event->data.eptransfer.ep);
|
|
|
|
|
|
|
|
if (NRF_USBD_EPIN_CHECK(p_event->data.eptransfer.ep)) {
|
|
|
|
switch (p_event->data.eptransfer.status) {
|
2018-10-16 08:30:22 -04:00
|
|
|
case NRFX_USBD_EP_OK: {
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event *ev = usbd_evt_alloc();
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
if (!ev) {
|
|
|
|
return;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx->write_in_progress = false;
|
2019-01-04 16:20:30 +01:00
|
|
|
ev->evt_type = USBD_EVT_EP;
|
|
|
|
ev->evt.ep_evt.evt_type = EP_EVT_WRITE_COMPLETE;
|
|
|
|
ev->evt.ep_evt.ep = ep_ctx;
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
LOG_DBG("ctrl write complete");
|
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
default: {
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
|
2018-10-16 08:30:22 -04:00
|
|
|
p_event->data.eptransfer.status,
|
|
|
|
p_event->data.eptransfer.ep);
|
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (p_event->data.eptransfer.status) {
|
2018-10-16 08:30:22 -04:00
|
|
|
case NRFX_USBD_EP_WAITING: {
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event *ev = usbd_evt_alloc();
|
|
|
|
|
|
|
|
if (!ev) {
|
|
|
|
return;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
LOG_DBG("ctrl read request");
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
ep_ctx->read_pending = true;
|
2019-01-04 16:20:30 +01:00
|
|
|
ev->evt_type = USBD_EVT_EP;
|
|
|
|
ev->evt.ep_evt.evt_type = EP_EVT_RECV_REQ;
|
|
|
|
ev->evt.ep_evt.ep = ep_ctx;
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
case NRFX_USBD_EP_OK: {
|
2018-11-28 17:19:12 +01:00
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event *ev = usbd_evt_alloc();
|
|
|
|
|
|
|
|
if (!ev) {
|
|
|
|
return;
|
|
|
|
}
|
2019-03-14 13:25:35 -05:00
|
|
|
nrfx_usbd_ep_status_t err_code;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
ev->evt_type = USBD_EVT_EP;
|
|
|
|
ev->evt.ep_evt.evt_type = EP_EVT_RECV_COMPLETE;
|
|
|
|
ev->evt.ep_evt.ep = ep_ctx;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
err_code = nrfx_usbd_ep_status_get(
|
|
|
|
p_event->data.eptransfer.ep, &ep_ctx->buf.len);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-03-14 13:25:35 -05:00
|
|
|
if (err_code != NRFX_USBD_EP_OK) {
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("_ep_status_get failed! Code: %d",
|
2018-10-16 08:30:22 -04:00
|
|
|
err_code);
|
|
|
|
__ASSERT_NO_MSG(0);
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
2018-10-16 08:30:22 -04:00
|
|
|
LOG_DBG("ctrl read done: %d", ep_ctx->buf.len);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-11-28 17:19:12 +01:00
|
|
|
if (ctx->ctrl_read_len > ep_ctx->buf.len) {
|
|
|
|
ctx->ctrl_read_len -= ep_ctx->buf.len;
|
2020-05-21 10:45:45 +02:00
|
|
|
/* Allow next data chunk on EP0 OUT */
|
2018-11-28 17:19:12 +01:00
|
|
|
nrfx_usbd_setup_data_clear();
|
|
|
|
} else {
|
2019-03-26 19:57:45 -06:00
|
|
|
ctx->ctrl_read_len = 0U;
|
2018-11-28 17:19:12 +01:00
|
|
|
}
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: {
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
|
2018-10-16 08:30:22 -04:00
|
|
|
p_event->data.eptransfer.status,
|
|
|
|
p_event->data.eptransfer.ep);
|
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
static void usbd_event_transfer_data(nrfx_usbd_evt_t const *const p_event)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx =
|
|
|
|
endpoint_ctx(p_event->data.eptransfer.ep);
|
|
|
|
|
|
|
|
if (NRF_USBD_EPIN_CHECK(p_event->data.eptransfer.ep)) {
|
|
|
|
switch (p_event->data.eptransfer.status) {
|
2018-10-16 08:30:22 -04:00
|
|
|
case NRFX_USBD_EP_OK: {
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event *ev = usbd_evt_alloc();
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
if (!ev) {
|
|
|
|
return;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("write complete, ep 0x%02x",
|
2020-05-27 11:26:57 -05:00
|
|
|
(uint32_t)p_event->data.eptransfer.ep);
|
2019-01-04 16:20:30 +01:00
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx->write_in_progress = false;
|
2019-01-04 16:20:30 +01:00
|
|
|
ev->evt_type = USBD_EVT_EP;
|
|
|
|
ev->evt.ep_evt.evt_type = EP_EVT_WRITE_COMPLETE;
|
|
|
|
ev->evt.ep_evt.ep = ep_ctx;
|
2018-10-16 08:30:22 -04:00
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2020-05-13 07:11:06 -07:00
|
|
|
case NRFX_USBD_EP_ABORTED: {
|
|
|
|
LOG_DBG("Endpoint 0x%02x write aborted",
|
|
|
|
p_event->data.eptransfer.ep);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
default: {
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
|
2018-10-16 08:30:22 -04:00
|
|
|
p_event->data.eptransfer.status,
|
|
|
|
p_event->data.eptransfer.ep);
|
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
switch (p_event->data.eptransfer.status) {
|
2018-10-16 08:30:22 -04:00
|
|
|
case NRFX_USBD_EP_WAITING: {
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event *ev = usbd_evt_alloc();
|
|
|
|
|
|
|
|
if (!ev) {
|
|
|
|
return;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("read request, ep 0x%02x",
|
2020-05-27 11:26:57 -05:00
|
|
|
(uint32_t)p_event->data.eptransfer.ep);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
ep_ctx->read_pending = true;
|
2019-01-04 16:20:30 +01:00
|
|
|
ev->evt_type = USBD_EVT_EP;
|
|
|
|
ev->evt.ep_evt.evt_type = EP_EVT_RECV_REQ;
|
|
|
|
ev->evt.ep_evt.ep = ep_ctx;
|
|
|
|
|
|
|
|
usbd_evt_put(ev);
|
2018-10-16 08:30:22 -04:00
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
case NRFX_USBD_EP_OK: {
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event *ev = usbd_evt_alloc();
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
if (!ev) {
|
|
|
|
return;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-11-07 22:07:47 +01:00
|
|
|
ep_ctx->buf.len = nrf_usbd_ep_amount_get(NRF_USBD,
|
2018-10-16 08:30:22 -04:00
|
|
|
p_event->data.eptransfer.ep);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("read complete, ep 0x%02x, len %d",
|
2020-05-27 11:26:57 -05:00
|
|
|
(uint32_t)p_event->data.eptransfer.ep,
|
2018-10-16 08:30:22 -04:00
|
|
|
ep_ctx->buf.len);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
ev->evt_type = USBD_EVT_EP;
|
|
|
|
ev->evt.ep_evt.evt_type = EP_EVT_RECV_COMPLETE;
|
|
|
|
ev->evt.ep_evt.ep = ep_ctx;
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
default: {
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x",
|
2018-07-06 16:16:47 +02:00
|
|
|
p_event->data.eptransfer.status,
|
|
|
|
p_event->data.eptransfer.ep);
|
2018-10-16 08:30:22 -04:00
|
|
|
}
|
|
|
|
break;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief nRFx USBD driver event handler function.
|
|
|
|
*/
|
2018-10-16 08:30:22 -04:00
|
|
|
static void usbd_event_handler(nrfx_usbd_evt_t const *const p_event)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
2019-03-14 14:26:50 +01:00
|
|
|
struct usbd_event evt = {0};
|
2019-02-11 12:25:00 +01:00
|
|
|
bool put_evt = false;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
switch (p_event->type) {
|
|
|
|
case NRFX_USBD_EVT_SUSPEND:
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("SUSPEND state detected");
|
2019-02-11 12:25:00 +01:00
|
|
|
evt.evt_type = USBD_EVT_POWER;
|
|
|
|
evt.evt.pwr_evt.state = USBD_SUSPENDED;
|
|
|
|
put_evt = true;
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
case NRFX_USBD_EVT_RESUME:
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("RESUMING from suspend");
|
2019-02-11 12:25:00 +01:00
|
|
|
evt.evt_type = USBD_EVT_POWER;
|
|
|
|
evt.evt.pwr_evt.state = USBD_RESUMED;
|
|
|
|
put_evt = true;
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
case NRFX_USBD_EVT_WUREQ:
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("RemoteWU initiated");
|
2020-08-17 11:01:22 +02:00
|
|
|
evt.evt_type = USBD_EVT_POWER;
|
|
|
|
evt.evt.pwr_evt.state = USBD_RESUMED;
|
|
|
|
put_evt = true;
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
case NRFX_USBD_EVT_RESET:
|
2019-02-11 12:25:00 +01:00
|
|
|
evt.evt_type = USBD_EVT_RESET;
|
|
|
|
put_evt = true;
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
case NRFX_USBD_EVT_SOF:
|
2019-02-11 12:25:00 +01:00
|
|
|
if (IS_ENABLED(CONFIG_USB_DEVICE_SOF)) {
|
|
|
|
evt.evt_type = USBD_EVT_SOF;
|
|
|
|
put_evt = true;
|
2019-01-04 16:20:30 +01:00
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case NRFX_USBD_EVT_EPTRANSFER:
|
|
|
|
ep_ctx = endpoint_ctx(p_event->data.eptransfer.ep);
|
|
|
|
switch (ep_ctx->cfg.type) {
|
|
|
|
case USB_DC_EP_CONTROL:
|
|
|
|
usbd_event_transfer_ctrl(p_event);
|
|
|
|
break;
|
|
|
|
case USB_DC_EP_BULK:
|
|
|
|
case USB_DC_EP_INTERRUPT:
|
|
|
|
usbd_event_transfer_data(p_event);
|
|
|
|
break;
|
|
|
|
case USB_DC_EP_ISOCHRONOUS:
|
|
|
|
usbd_event_transfer_data(p_event);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NRFX_USBD_EVT_SETUP: {
|
|
|
|
nrfx_usbd_setup_t drv_setup;
|
|
|
|
|
|
|
|
nrfx_usbd_setup_get(&drv_setup);
|
2021-05-12 11:17:06 +02:00
|
|
|
if ((drv_setup.bRequest != USB_SREQ_SET_ADDRESS)
|
|
|
|
|| (USB_REQTYPE_GET_TYPE(drv_setup.bmRequestType)
|
|
|
|
!= USB_REQTYPE_TYPE_STANDARD)) {
|
2018-07-06 16:16:47 +02:00
|
|
|
/* SetAddress is habdled by USBD hardware.
|
|
|
|
* No software action required.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx =
|
|
|
|
endpoint_ctx(NRF_USBD_EPOUT(0));
|
2019-02-11 12:25:00 +01:00
|
|
|
|
|
|
|
evt.evt_type = USBD_EVT_EP;
|
|
|
|
evt.evt.ep_evt.ep = ep_ctx;
|
|
|
|
evt.evt.ep_evt.evt_type = EP_EVT_SETUP_RECV;
|
|
|
|
put_evt = true;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2019-02-11 12:25:00 +01:00
|
|
|
|
|
|
|
if (put_evt) {
|
|
|
|
struct usbd_event *ev;
|
|
|
|
|
|
|
|
ev = usbd_evt_alloc();
|
|
|
|
if (!ev) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ev->evt_type = evt.evt_type;
|
|
|
|
ev->evt = evt.evt;
|
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
static inline void usbd_reinit(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
nrfx_err_t err;
|
|
|
|
|
2020-08-10 13:50:26 +02:00
|
|
|
nrfx_power_usbevt_disable();
|
2019-01-04 16:20:30 +01:00
|
|
|
nrfx_usbd_disable();
|
|
|
|
nrfx_usbd_uninit();
|
|
|
|
|
|
|
|
usbd_evt_flush();
|
2020-08-10 13:50:26 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
ret = eps_ctx_init();
|
|
|
|
__ASSERT_NO_MSG(ret == 0);
|
|
|
|
|
2020-08-10 13:50:26 +02:00
|
|
|
nrfx_power_usbevt_enable();
|
2019-01-04 16:20:30 +01:00
|
|
|
err = nrfx_usbd_init(usbd_event_handler);
|
|
|
|
|
|
|
|
if (err != NRFX_SUCCESS) {
|
2020-08-10 13:50:26 +02:00
|
|
|
LOG_DBG("nRF USBD driver reinit failed. Code: %d", err);
|
2019-01-04 16:20:30 +01:00
|
|
|
__ASSERT_NO_MSG(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-17 14:37:11 +01:00
|
|
|
/**
|
|
|
|
* @brief funciton to generate fake receive request for
|
|
|
|
* ISO OUT EP.
|
|
|
|
*
|
|
|
|
* ISO OUT endpoint does not generate irq by itself and reading
|
|
|
|
* from ISO OUT ep is sunchronized with SOF frame. For more details
|
|
|
|
* refer to Nordic usbd specification.
|
|
|
|
*/
|
|
|
|
static void usbd_sof_trigger_iso_read(void)
|
|
|
|
{
|
|
|
|
struct usbd_event *ev;
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(NRFX_USBD_EPOUT8);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
LOG_ERR("There is no ISO ep");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ep_ctx->cfg.en) {
|
|
|
|
/* Dissect receive request
|
|
|
|
* if the iso OUT ep is enabled
|
|
|
|
*/
|
|
|
|
ep_ctx->read_pending = true;
|
|
|
|
ep_ctx->read_complete = true;
|
|
|
|
ev = usbd_evt_alloc();
|
|
|
|
if (!ev) {
|
|
|
|
LOG_ERR("Failed to alloc evt");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ev->evt_type = USBD_EVT_EP;
|
|
|
|
ev->evt.ep_evt.evt_type = EP_EVT_RECV_REQ;
|
|
|
|
ev->evt.ep_evt.ep = ep_ctx;
|
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
} else {
|
|
|
|
LOG_DBG("Endpoint is not enabled");
|
|
|
|
}
|
|
|
|
}
|
2019-01-04 16:20:30 +01:00
|
|
|
|
|
|
|
/* Work handler */
|
|
|
|
static void usbd_work_handler(struct k_work *item)
|
|
|
|
{
|
|
|
|
struct nrf_usbd_ctx *ctx;
|
|
|
|
struct usbd_event *ev;
|
|
|
|
|
|
|
|
ctx = CONTAINER_OF(item, struct nrf_usbd_ctx, usb_work);
|
|
|
|
|
|
|
|
while ((ev = usbd_evt_get()) != NULL) {
|
2019-03-13 11:36:04 +01:00
|
|
|
if (!dev_ready() && ev->evt_type != USBD_EVT_POWER) {
|
|
|
|
/* Drop non-power events when cable is detached. */
|
2019-12-09 13:59:27 +01:00
|
|
|
usbd_evt_free(ev);
|
2019-03-13 11:36:04 +01:00
|
|
|
continue;
|
|
|
|
}
|
2019-01-04 16:20:30 +01:00
|
|
|
|
|
|
|
switch (ev->evt_type) {
|
|
|
|
case USBD_EVT_EP:
|
|
|
|
if (!ctx->attached) {
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("not attached, EP 0x%02x event dropped",
|
2020-05-27 11:26:57 -05:00
|
|
|
(uint32_t)ev->evt.ep_evt.ep->cfg.addr);
|
2019-01-04 16:20:30 +01:00
|
|
|
}
|
|
|
|
usbd_work_process_ep_events(&ev->evt.ep_evt);
|
|
|
|
break;
|
|
|
|
case USBD_EVT_POWER:
|
|
|
|
usbd_work_process_pwr_events(&ev->evt.pwr_evt);
|
|
|
|
break;
|
|
|
|
case USBD_EVT_RESET:
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("USBD reset event");
|
2019-01-04 16:20:30 +01:00
|
|
|
k_mutex_lock(&ctx->drv_lock, K_FOREVER);
|
|
|
|
eps_ctx_init();
|
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
|
|
|
|
|
|
|
if (ctx->status_cb) {
|
|
|
|
ctx->status_cb(USB_DC_RESET, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USBD_EVT_SOF:
|
2020-01-17 14:37:11 +01:00
|
|
|
usbd_sof_trigger_iso_read();
|
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
if (ctx->status_cb) {
|
|
|
|
ctx->status_cb(USB_DC_SOF, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USBD_EVT_REINIT: {
|
2019-05-29 14:07:47 +02:00
|
|
|
/*
|
|
|
|
* Reinitialize the peripheral after queue
|
|
|
|
* overflow.
|
|
|
|
*/
|
2019-01-04 16:20:30 +01:00
|
|
|
LOG_ERR("USBD event queue full!");
|
|
|
|
usbd_reinit();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("Unknown USBD event: %"PRId16, ev->evt_type);
|
2019-01-04 16:20:30 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
usbd_evt_free(ev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
int usb_dc_attach(void)
|
|
|
|
{
|
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
|
|
|
nrfx_err_t err;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (ctx->attached) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_init(&ctx->drv_lock);
|
2020-02-13 14:03:29 +01:00
|
|
|
ctx->hfxo_mgr =
|
2020-09-04 12:32:19 +02:00
|
|
|
z_nrf_clock_control_get_onoff(
|
2020-09-07 13:21:14 +02:00
|
|
|
COND_CODE_1(NRF_CLOCK_HAS_HFCLK192M,
|
2020-09-04 12:32:19 +02:00
|
|
|
(CLOCK_CONTROL_NRF_SUBSYS_HF192M),
|
|
|
|
(CLOCK_CONTROL_NRF_SUBSYS_HF)));
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2020-04-17 17:06:59 -07:00
|
|
|
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
|
2018-07-06 16:16:47 +02:00
|
|
|
nrfx_isr, nrfx_usbd_irq_handler, 0);
|
|
|
|
|
|
|
|
err = nrfx_usbd_init(usbd_event_handler);
|
|
|
|
|
|
|
|
if (err != NRFX_SUCCESS) {
|
2020-05-27 11:26:57 -05:00
|
|
|
LOG_DBG("nRF USBD driver init failed. Code: %d", (uint32_t)err);
|
2018-07-06 16:16:47 +02:00
|
|
|
return -EIO;
|
|
|
|
}
|
2020-08-10 13:50:26 +02:00
|
|
|
nrfx_power_usbevt_enable();
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
ret = eps_ctx_init();
|
|
|
|
if (ret == 0) {
|
|
|
|
ctx->attached = true;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-07-22 08:56:55 +02:00
|
|
|
if (!k_fifo_is_empty(&usbd_evt_fifo)) {
|
2019-01-28 16:22:05 -06:00
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
|
2020-08-10 13:50:26 +02:00
|
|
|
if (nrfx_power_usbstatus_get() != NRFX_POWER_USB_STATE_DISCONNECTED) {
|
2019-04-02 11:35:24 +02:00
|
|
|
/* USBDETECTED event is be generated on cable attachment and
|
|
|
|
* when cable is already attached during reset, but not when
|
|
|
|
* the peripheral is re-enabled.
|
|
|
|
* When USB-enabled bootloader is used, target application
|
|
|
|
* will not receive this event and it needs to be generated
|
|
|
|
* again here.
|
|
|
|
*/
|
2020-08-10 13:50:26 +02:00
|
|
|
usb_dc_power_event_handler(NRFX_POWER_USB_EVT_DETECTED);
|
2019-03-25 19:39:26 +09:00
|
|
|
}
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
return ret;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int usb_dc_detach(void)
|
|
|
|
{
|
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_lock(&ctx->drv_lock, K_FOREVER);
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
usbd_evt_flush();
|
2018-11-13 16:11:52 +01:00
|
|
|
eps_ctx_uninit();
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-07-02 01:10:58 +02:00
|
|
|
if (nrfx_usbd_is_enabled()) {
|
|
|
|
nrfx_usbd_disable();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nrfx_usbd_is_initialized()) {
|
|
|
|
nrfx_usbd_uninit();
|
|
|
|
}
|
|
|
|
|
2020-02-13 14:03:29 +01:00
|
|
|
(void)hfxo_stop(ctx);
|
2020-08-10 13:50:26 +02:00
|
|
|
nrfx_power_usbevt_disable();
|
2018-11-05 11:40:50 +02:00
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
ctx->attached = false;
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2019-01-31 12:05:15 +01:00
|
|
|
|
|
|
|
return 0;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int usb_dc_reset(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("USBD Reset");
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
ret = usb_dc_detach();
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = usb_dc_attach();
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_set_address(const uint8_t addr)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ctx *ctx;
|
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Nothing to do here. The USBD HW already takes care of initiating
|
|
|
|
* STATUS stage. Just double check the address for sanity.
|
|
|
|
*/
|
2020-05-27 11:26:57 -05:00
|
|
|
__ASSERT(addr == (uint8_t)NRF_USBD->USBADDR, "USB Address incorrect!");
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
ctx = get_usbd_ctx();
|
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("Address set to: %d", addr);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data *const ep_cfg)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t ep_idx = NRF_USBD_EP_NR_GET(ep_cfg->ep_addr);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("ep 0x%02x, mps %d, type %d", ep_cfg->ep_addr, ep_cfg->ep_mps,
|
2018-07-06 16:16:47 +02:00
|
|
|
ep_cfg->ep_type);
|
|
|
|
|
|
|
|
if ((ep_cfg->ep_type == USB_DC_EP_CONTROL) && ep_idx) {
|
2018-10-16 08:30:22 -04:00
|
|
|
LOG_ERR("invalid endpoint configuration");
|
2018-07-06 16:16:47 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!NRF_USBD_EP_VALIDATE(ep_cfg->ep_addr)) {
|
2018-10-16 08:30:22 -04:00
|
|
|
LOG_ERR("invalid endpoint index/address");
|
2018-07-06 16:16:47 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ep_cfg->ep_type == USB_DC_EP_ISOCHRONOUS) &&
|
2018-10-16 08:30:22 -04:00
|
|
|
(!NRF_USBD_EPISO_CHECK(ep_cfg->ep_addr))) {
|
|
|
|
LOG_WRN("invalid endpoint type");
|
2018-07-06 16:16:47 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-16 08:30:22 -04:00
|
|
|
int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data *const ep_cfg)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO:
|
|
|
|
* For ISO endpoints, application has to use EPIN/OUT 8
|
|
|
|
* but right now there's no standard way of knowing the
|
|
|
|
* ISOIN/ISOOUT endpoint number in advance to configure
|
|
|
|
* accordingly. So either this needs to be chosen in the
|
|
|
|
* menuconfig in application area or perhaps in device tree
|
|
|
|
* at compile time or introduce a new API to read the endpoint
|
|
|
|
* configuration at runtime before configuring them.
|
|
|
|
*/
|
|
|
|
ep_ctx = endpoint_ctx(ep_cfg->ep_addr);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx->cfg.addr = ep_cfg->ep_addr;
|
|
|
|
ep_ctx->cfg.type = ep_cfg->ep_type;
|
|
|
|
ep_ctx->cfg.max_sz = ep_cfg->ep_mps;
|
|
|
|
|
2020-01-24 09:54:41 +01:00
|
|
|
if (!NRF_USBD_EPISO_CHECK(ep_cfg->ep_addr)) {
|
|
|
|
if ((ep_cfg->ep_mps & (ep_cfg->ep_mps - 1)) != 0U) {
|
|
|
|
LOG_ERR("EP max packet size must be a power of 2");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-10-30 15:14:53 +01:00
|
|
|
}
|
2020-01-24 09:54:41 +01:00
|
|
|
|
2018-10-30 15:14:53 +01:00
|
|
|
nrfx_usbd_ep_max_packet_size_set(ep_addr_to_nrfx(ep_cfg->ep_addr),
|
|
|
|
ep_cfg->ep_mps);
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_set_stall(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ep_ctx->cfg.type) {
|
|
|
|
case USB_DC_EP_CONTROL:
|
|
|
|
nrfx_usbd_setup_stall();
|
|
|
|
break;
|
|
|
|
case USB_DC_EP_BULK:
|
|
|
|
case USB_DC_EP_INTERRUPT:
|
|
|
|
nrfx_usbd_ep_stall(ep_addr_to_nrfx(ep));
|
|
|
|
break;
|
|
|
|
case USB_DC_EP_ISOCHRONOUS:
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_ERR("STALL unsupported on ISO endpoint");
|
2018-07-06 16:16:47 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-11-29 11:12:22 -08:00
|
|
|
ep_ctx->buf.len = 0U;
|
2018-07-06 16:16:47 +02:00
|
|
|
ep_ctx->buf.curr = ep_ctx->buf.data;
|
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("STALL on EP 0x%02x", ep);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_clear_stall(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-09-14 14:20:57 +02:00
|
|
|
if (NRF_USBD_EPISO_CHECK(ep)) {
|
|
|
|
/* ISO transactions do not support a handshake phase. */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-03-02 10:52:25 +01:00
|
|
|
nrfx_usbd_ep_dtoggle_clear(ep_addr_to_nrfx(ep));
|
2018-07-06 16:16:47 +02:00
|
|
|
nrfx_usbd_ep_stall_clear(ep_addr_to_nrfx(ep));
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("Unstall on EP 0x%02x", ep);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_halt(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
return usb_dc_ep_set_stall(ep);
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_is_stalled(const uint8_t ep, uint8_t *const stalled)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-07-02 01:15:33 +02:00
|
|
|
if (!stalled) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
*stalled = (uint8_t) nrfx_usbd_ep_stall_check(ep_addr_to_nrfx(ep));
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_enable(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-09-14 14:20:57 +02:00
|
|
|
if (!NRF_USBD_EPISO_CHECK(ep)) {
|
|
|
|
/* ISO transactions for full-speed device do not support
|
|
|
|
* toggle sequencing and should only send DATA0 PID.
|
|
|
|
*/
|
|
|
|
nrfx_usbd_ep_dtoggle_clear(ep_addr_to_nrfx(ep));
|
2020-11-05 14:41:24 +01:00
|
|
|
/** Endpoint is enabled on SetInterface request.
|
|
|
|
* This should also clear EP's halt status.
|
|
|
|
*/
|
|
|
|
nrfx_usbd_ep_stall_clear(ep_addr_to_nrfx(ep));
|
2020-09-14 14:20:57 +02:00
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
if (ep_ctx->cfg.en) {
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("EP enable: 0x%02x", ep);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
ep_ctx->cfg.en = true;
|
|
|
|
|
|
|
|
/* Defer the endpoint enable if USBD is not ready yet. */
|
|
|
|
if (dev_ready()) {
|
|
|
|
nrfx_usbd_ep_enable(ep_addr_to_nrfx(ep));
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_disable(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ep_ctx->cfg.en) {
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("EP disable: 0x%02x", ep);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
nrfx_usbd_ep_disable(ep_addr_to_nrfx(ep));
|
2021-04-19 15:42:30 +02:00
|
|
|
/* Clear write_in_progress as nrfx_usbd_ep_disable()
|
|
|
|
* terminates endpoint transaction.
|
|
|
|
*/
|
|
|
|
ep_ctx->write_in_progress = false;
|
|
|
|
ep_ctx_reset(ep_ctx);
|
2018-07-06 16:16:47 +02:00
|
|
|
ep_ctx->cfg.en = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_flush(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-11-29 11:12:22 -08:00
|
|
|
ep_ctx->buf.len = 0U;
|
2018-07-06 16:16:47 +02:00
|
|
|
ep_ctx->buf.curr = ep_ctx->buf.data;
|
|
|
|
|
|
|
|
nrfx_usbd_transfer_out_drop(ep_addr_to_nrfx(ep));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data,
|
|
|
|
const uint32_t data_len, uint32_t *const ret_bytes)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("ep_write: ep 0x%02x, len %d", ep, data_len);
|
2018-07-06 16:16:47 +02:00
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
2019-07-03 11:50:46 +02:00
|
|
|
int result = 0;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NRF_USBD_EPOUT_CHECK(ep)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-06-26 09:38:34 +02:00
|
|
|
if (!ep_ctx->cfg.en) {
|
|
|
|
LOG_ERR("Endpoint 0x%02x is not enabled", ep);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_lock(&ctx->drv_lock, K_FOREVER);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
/* USBD driver does not allow scheduling multiple DMA transfers
|
|
|
|
* for one EP at a time. Next USB transfer on this endpoint can be
|
|
|
|
* triggered after the completion of previous one.
|
2018-07-06 16:16:47 +02:00
|
|
|
*/
|
2018-11-13 16:11:52 +01:00
|
|
|
if (ep_ctx->write_in_progress) {
|
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2018-07-06 16:16:47 +02:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
2020-10-20 13:58:08 +02:00
|
|
|
/** Clear the ZLP flag if current write is ZLP. After the ZLP will be
|
|
|
|
* send the driver will perform status stage.
|
|
|
|
*/
|
|
|
|
if (!data_len && ep_ctx->trans_zlp) {
|
|
|
|
ep_ctx->trans_zlp = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** If writing to a Control Endpoint there might be a need to transfer
|
|
|
|
* ZLP. If the Hosts asks for more data that the device may return and
|
|
|
|
* the last packet is wMaxPacketSize long. The driver must send ZLP.
|
|
|
|
* For consistance with the Zephyr USB stack sending ZLP must be issued
|
|
|
|
* from the stack level. Making trans_zlp flag true results in blocking
|
|
|
|
* the driver from starting setup stage without required ZLP.
|
|
|
|
*/
|
|
|
|
if (ep_ctx->cfg.type == USB_DC_EP_CONTROL) {
|
|
|
|
if (data_len && usbd_ctx.setup.wLength > data_len &&
|
|
|
|
!(data_len % ep_ctx->cfg.max_sz)) {
|
|
|
|
ep_ctx->trans_zlp = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-06 16:16:47 +02:00
|
|
|
/* Setup stage is handled by hardware.
|
|
|
|
* Detect the setup stage initiated by the stack
|
|
|
|
* and perform appropriate action.
|
|
|
|
*/
|
|
|
|
if ((ep_ctx->cfg.type == USB_DC_EP_CONTROL)
|
2018-10-16 08:30:22 -04:00
|
|
|
&& (nrfx_usbd_last_setup_dir_get() != ep)) {
|
2018-07-06 16:16:47 +02:00
|
|
|
nrfx_usbd_setup_clear();
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2018-07-06 16:16:47 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2018-11-13 16:11:52 +01:00
|
|
|
|
|
|
|
ep_ctx->write_in_progress = true;
|
2020-10-20 09:43:58 +02:00
|
|
|
NRFX_USBD_TRANSFER_IN(transfer, data, data_len, 0);
|
2018-07-06 16:16:47 +02:00
|
|
|
nrfx_err_t err = nrfx_usbd_ep_transfer(ep_addr_to_nrfx(ep), &transfer);
|
|
|
|
|
|
|
|
if (err != NRFX_SUCCESS) {
|
2018-11-13 16:11:52 +01:00
|
|
|
ep_ctx->write_in_progress = false;
|
2020-10-20 09:43:58 +02:00
|
|
|
if (ret_bytes) {
|
|
|
|
*ret_bytes = 0;
|
|
|
|
}
|
2018-11-13 16:11:52 +01:00
|
|
|
result = -EIO;
|
2020-05-27 11:26:57 -05:00
|
|
|
LOG_ERR("nRF USBD write error: %d", (uint32_t)err);
|
2020-10-20 09:43:58 +02:00
|
|
|
} else {
|
|
|
|
if (ret_bytes) {
|
|
|
|
*ret_bytes = data_len;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
|
|
|
return result;
|
2018-07-06 16:16:47 +02:00
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data, uint32_t max_data_len,
|
|
|
|
uint32_t *read_bytes)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t bytes_to_copy;
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NRF_USBD_EPIN_CHECK(ep)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data && max_data_len) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-06-26 09:38:34 +02:00
|
|
|
if (!ep_ctx->cfg.en) {
|
|
|
|
LOG_ERR("Endpoint 0x%02x is not enabled", ep);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_lock(&ctx->drv_lock, K_FOREVER);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-02-11 17:14:19 +00:00
|
|
|
bytes_to_copy = MIN(max_data_len, ep_ctx->buf.len);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
if (!data && !max_data_len) {
|
|
|
|
if (read_bytes) {
|
|
|
|
*read_bytes = ep_ctx->buf.len;
|
|
|
|
}
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2018-07-06 16:16:47 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data, ep_ctx->buf.curr, bytes_to_copy);
|
|
|
|
|
|
|
|
ep_ctx->buf.curr += bytes_to_copy;
|
|
|
|
ep_ctx->buf.len -= bytes_to_copy;
|
|
|
|
if (read_bytes) {
|
|
|
|
*read_bytes = bytes_to_copy;
|
|
|
|
}
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2018-07-06 16:16:47 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_read_continue(uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
|
|
|
|
|
|
|
if (!dev_attached() || !dev_ready()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NRF_USBD_EPIN_CHECK(ep)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-06-26 09:38:34 +02:00
|
|
|
if (!ep_ctx->cfg.en) {
|
|
|
|
LOG_ERR("Endpoint 0x%02x is not enabled", ep);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_lock(&ctx->drv_lock, K_FOREVER);
|
2018-07-06 16:16:47 +02:00
|
|
|
if (!ep_ctx->buf.len) {
|
|
|
|
ep_ctx->buf.curr = ep_ctx->buf.data;
|
|
|
|
ep_ctx->read_complete = true;
|
|
|
|
|
|
|
|
if (ep_ctx->read_pending) {
|
2019-01-04 16:20:30 +01:00
|
|
|
struct usbd_event *ev = usbd_evt_alloc();
|
|
|
|
|
|
|
|
if (!ev) {
|
2020-05-20 16:00:06 +02:00
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2019-01-04 16:20:30 +01:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2018-07-06 16:16:47 +02:00
|
|
|
|
2019-01-04 16:20:30 +01:00
|
|
|
ev->evt_type = USBD_EVT_EP;
|
|
|
|
ev->evt.ep_evt.ep = ep_ctx;
|
|
|
|
ev->evt.ep_evt.evt_type = EP_EVT_RECV_REQ;
|
2018-07-06 16:16:47 +02:00
|
|
|
usbd_evt_put(ev);
|
|
|
|
usbd_work_schedule();
|
|
|
|
}
|
|
|
|
}
|
2018-11-13 16:11:52 +01:00
|
|
|
k_mutex_unlock(&ctx->drv_lock);
|
2018-07-06 16:16:47 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_read(const uint8_t ep, uint8_t *const data,
|
|
|
|
const uint32_t max_data_len, uint32_t *const read_bytes)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
2019-05-29 14:07:47 +02:00
|
|
|
LOG_DBG("ep_read: ep 0x%02x, maxlen %d", ep, max_data_len);
|
2018-07-06 16:16:47 +02:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data && !max_data_len) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = usb_dc_ep_read_continue(ep);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx->cfg.cb = cb;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-06 14:37:44 +02:00
|
|
|
void usb_dc_set_status_callback(const usb_dc_status_callback cb)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
get_usbd_ctx()->status_cb = cb;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int usb_dc_ep_mps(const uint8_t ep)
|
2018-07-06 16:16:47 +02:00
|
|
|
{
|
|
|
|
struct nrf_usbd_ep_ctx *ep_ctx;
|
|
|
|
|
|
|
|
if (!dev_attached()) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_ctx = endpoint_ctx(ep);
|
|
|
|
if (!ep_ctx) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ep_ctx->cfg.max_sz;
|
|
|
|
}
|
2019-02-11 12:25:00 +01:00
|
|
|
|
|
|
|
int usb_dc_wakeup_request(void)
|
|
|
|
{
|
|
|
|
bool res = nrfx_usbd_wakeup_req();
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2020-08-10 13:50:26 +02:00
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int usb_init(const struct device *arg)
|
2020-08-10 13:50:26 +02:00
|
|
|
{
|
2020-10-22 17:08:32 +02:00
|
|
|
struct nrf_usbd_ctx *ctx = get_usbd_ctx();
|
|
|
|
|
2020-09-02 16:41:23 +02:00
|
|
|
#ifdef CONFIG_HAS_HW_NRF_USBREG
|
2020-08-10 13:50:26 +02:00
|
|
|
/* Use CLOCK/POWER priority for compatibility with other series where
|
|
|
|
* USB events are handled by CLOCK interrupt handler.
|
|
|
|
*/
|
2020-09-02 16:41:23 +02:00
|
|
|
IRQ_CONNECT(USBREGULATOR_IRQn,
|
|
|
|
DT_IRQ(DT_INST(0, nordic_nrf_clock), priority),
|
|
|
|
nrfx_isr, nrfx_usbreg_irq_handler, 0);
|
2020-08-10 13:50:26 +02:00
|
|
|
irq_enable(USBREGULATOR_IRQn);
|
|
|
|
#endif
|
|
|
|
|
2020-10-21 17:17:21 +02:00
|
|
|
static const nrfx_power_config_t power_config = {
|
|
|
|
.dcdcen = IS_ENABLED(CONFIG_SOC_DCDC_NRF52X) ||
|
|
|
|
IS_ENABLED(CONFIG_SOC_DCDC_NRF53X_APP),
|
|
|
|
#if NRFX_POWER_SUPPORTS_DCDCEN_VDDH
|
|
|
|
.dcdcenhv = IS_ENABLED(CONFIG_SOC_DCDC_NRF53X_HV),
|
|
|
|
#endif
|
|
|
|
};
|
2020-09-02 16:41:23 +02:00
|
|
|
|
|
|
|
static const nrfx_power_usbevt_config_t usbevt_config = {
|
2020-08-10 13:50:26 +02:00
|
|
|
.handler = usb_dc_power_event_handler
|
|
|
|
};
|
|
|
|
|
2020-09-02 16:41:23 +02:00
|
|
|
/* Ignore the return value, as NRFX_ERROR_ALREADY_INITIALIZED is not
|
|
|
|
* a problem here.
|
|
|
|
*/
|
|
|
|
(void)nrfx_power_init(&power_config);
|
|
|
|
nrfx_power_usbevt_init(&usbevt_config);
|
2020-08-10 13:50:26 +02:00
|
|
|
|
2021-03-31 10:31:30 -05:00
|
|
|
k_work_queue_start(&usbd_work_queue,
|
|
|
|
usbd_work_queue_stack,
|
|
|
|
K_KERNEL_STACK_SIZEOF(usbd_work_queue_stack),
|
|
|
|
CONFIG_SYSTEM_WORKQUEUE_PRIORITY, NULL);
|
2020-10-22 17:08:32 +02:00
|
|
|
|
|
|
|
k_work_init(&ctx->usb_work, usbd_work_handler);
|
|
|
|
|
2020-08-10 13:50:26 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-22 17:08:32 +02:00
|
|
|
SYS_INIT(usb_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|