2021-05-22 16:41:09 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021-2022 Nordic Semiconductor ASA
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zephyr/kernel.h>
|
|
|
|
#include <zephyr/net/buf.h>
|
|
|
|
#include <zephyr/sys/byteorder.h>
|
|
|
|
#include <zephyr/sys/__assert.h>
|
|
|
|
#include <zephyr/usb/usb_ch9.h>
|
|
|
|
#include "udc_common.h"
|
|
|
|
|
|
|
|
#include <zephyr/logging/log.h>
|
2022-12-19 17:11:01 +01:00
|
|
|
#if defined(CONFIG_UDC_DRIVER_LOG_LEVEL)
|
2021-05-22 16:41:09 +02:00
|
|
|
#define UDC_COMMON_LOG_LEVEL CONFIG_UDC_DRIVER_LOG_LEVEL
|
|
|
|
#else
|
|
|
|
#define UDC_COMMON_LOG_LEVEL LOG_LEVEL_NONE
|
|
|
|
#endif
|
|
|
|
LOG_MODULE_REGISTER(udc, CONFIG_UDC_DRIVER_LOG_LEVEL);
|
|
|
|
|
|
|
|
static inline void udc_buf_destroy(struct net_buf *buf);
|
|
|
|
|
|
|
|
NET_BUF_POOL_VAR_DEFINE(udc_ep_pool,
|
|
|
|
CONFIG_UDC_BUF_COUNT, CONFIG_UDC_BUF_POOL_SIZE,
|
|
|
|
sizeof(struct udc_buf_info), udc_buf_destroy);
|
|
|
|
|
|
|
|
#define USB_EP_LUT_IDX(ep) (USB_EP_DIR_IS_IN(ep) ? (ep & BIT_MASK(4)) + 16 : \
|
|
|
|
ep & BIT_MASK(4))
|
|
|
|
|
|
|
|
void udc_set_suspended(const struct device *dev, const bool value)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
|
|
|
|
if (value == udc_is_suspended(dev)) {
|
|
|
|
LOG_WRN("Spurious suspend/resume event");
|
|
|
|
}
|
|
|
|
|
|
|
|
atomic_set_bit_to(&data->status, UDC_STATUS_SUSPENDED, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct udc_ep_config *udc_get_ep_cfg(const struct device *dev, const uint8_t ep)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
|
|
|
|
return data->ep_lut[USB_EP_LUT_IDX(ep)];
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_register_ep(const struct device *dev, struct udc_ep_config *const cfg)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
uint8_t idx;
|
|
|
|
|
|
|
|
if (udc_is_initialized(dev)) {
|
|
|
|
return -EACCES;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = USB_EP_LUT_IDX(cfg->addr);
|
|
|
|
__ASSERT_NO_MSG(idx < ARRAY_SIZE(data->ep_lut));
|
|
|
|
|
|
|
|
data->ep_lut[idx] = cfg;
|
|
|
|
k_fifo_init(&cfg->fifo);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct net_buf *udc_buf_get(const struct device *dev, const uint8_t ep,
|
|
|
|
const bool pending)
|
|
|
|
{
|
|
|
|
struct udc_ep_config *ep_cfg;
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
ep_cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (ep_cfg == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = net_buf_get(&ep_cfg->fifo, K_NO_WAIT);
|
|
|
|
if (buf != NULL) {
|
|
|
|
ep_cfg->stat.pending = 0;
|
|
|
|
} else {
|
|
|
|
if (pending) {
|
|
|
|
ep_cfg->stat.pending = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct net_buf *udc_buf_get_all(const struct device *dev, const uint8_t ep)
|
|
|
|
{
|
|
|
|
struct udc_ep_config *ep_cfg;
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
ep_cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (ep_cfg == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = k_fifo_get(&ep_cfg->fifo, K_NO_WAIT);
|
|
|
|
if (!buf) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_DBG("ep 0x%02x dequeue %p", ep, buf);
|
|
|
|
for (struct net_buf *n = buf; !k_fifo_is_empty(&ep_cfg->fifo); n = n->frags) {
|
|
|
|
n->frags = k_fifo_get(&ep_cfg->fifo, K_NO_WAIT);
|
|
|
|
LOG_DBG("|-> %p ", n->frags);
|
|
|
|
if (n->frags == NULL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct net_buf *udc_buf_peek(const struct device *dev, const uint8_t ep,
|
|
|
|
const bool pending)
|
|
|
|
{
|
|
|
|
struct udc_ep_config *ep_cfg;
|
|
|
|
struct net_buf *buf = NULL;
|
|
|
|
|
|
|
|
ep_cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (ep_cfg != NULL) {
|
|
|
|
buf = k_fifo_peek_head(&ep_cfg->fifo);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf == NULL && pending) {
|
|
|
|
ep_cfg->stat.pending = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf != NULL) {
|
|
|
|
ep_cfg->stat.pending = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void udc_buf_put(struct udc_ep_config *const ep_cfg,
|
|
|
|
struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
net_buf_put(&ep_cfg->fifo, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void udc_ep_buf_set_setup(struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
struct udc_buf_info *bi = udc_get_buf_info(buf);
|
|
|
|
|
|
|
|
bi->setup = 1;
|
|
|
|
bi->data = 0;
|
|
|
|
bi->status = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool udc_ep_buf_has_zlp(const struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
const struct udc_buf_info *bi = udc_get_buf_info(buf);
|
|
|
|
|
|
|
|
return bi->zlp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void udc_ep_buf_clear_zlp(const struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
struct udc_buf_info *bi = udc_get_buf_info(buf);
|
|
|
|
|
|
|
|
bi->zlp = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_submit_event(const struct device *dev,
|
|
|
|
const enum udc_event_type type,
|
|
|
|
const int status,
|
|
|
|
struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
struct udc_event drv_evt = {
|
|
|
|
.type = type,
|
|
|
|
.buf = buf,
|
|
|
|
.status = status,
|
|
|
|
.dev = dev,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!udc_is_initialized(dev)) {
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data->event_cb(dev, &drv_evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8_t ep_attrib_get_transfer(uint8_t attributes)
|
|
|
|
{
|
|
|
|
return attributes & USB_EP_TRANSFER_TYPE_MASK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ep_check_config(const struct device *dev,
|
|
|
|
const struct udc_ep_config *const cfg,
|
|
|
|
const uint8_t ep,
|
|
|
|
const uint8_t attributes,
|
|
|
|
const uint16_t mps,
|
|
|
|
const uint8_t interval)
|
|
|
|
{
|
|
|
|
bool dir_is_in = USB_EP_DIR_IS_IN(ep);
|
|
|
|
bool dir_is_out = USB_EP_DIR_IS_OUT(ep);
|
|
|
|
|
|
|
|
LOG_DBG("cfg d:%c|%c t:%c|%c|%c|%c, mps %u",
|
|
|
|
cfg->caps.in ? 'I' : '-',
|
|
|
|
cfg->caps.out ? 'O' : '-',
|
|
|
|
cfg->caps.iso ? 'S' : '-',
|
|
|
|
cfg->caps.bulk ? 'B' : '-',
|
|
|
|
cfg->caps.interrupt ? 'I' : '-',
|
|
|
|
cfg->caps.control ? 'C' : '-',
|
|
|
|
cfg->caps.mps);
|
|
|
|
|
|
|
|
if (dir_is_out && !cfg->caps.out) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dir_is_in && !cfg->caps.in) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mps > cfg->caps.mps) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ep_attrib_get_transfer(attributes)) {
|
|
|
|
case USB_EP_TYPE_BULK:
|
|
|
|
if (!cfg->caps.bulk) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USB_EP_TYPE_INTERRUPT:
|
|
|
|
if (!cfg->caps.interrupt) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USB_EP_TYPE_ISO:
|
|
|
|
if (!cfg->caps.iso) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case USB_EP_TYPE_CONTROL:
|
|
|
|
if (!cfg->caps.control) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ep_update_mps(const struct device *dev,
|
|
|
|
const struct udc_ep_config *const cfg,
|
|
|
|
const uint8_t attributes,
|
|
|
|
uint16_t *const mps)
|
|
|
|
{
|
|
|
|
struct udc_device_caps caps = udc_caps(dev);
|
|
|
|
const uint16_t spec_iso_mps = caps.hs ? 1024 : 1023;
|
|
|
|
const uint16_t spec_int_mps = caps.hs ? 1024 : 64;
|
|
|
|
const uint16_t spec_bulk_mps = caps.hs ? 512 : 64;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO: It does not take into account the actual speed of the
|
|
|
|
* bus after the RESET. Should be fixed/improved when the driver
|
|
|
|
* for high speed controller are ported.
|
|
|
|
*/
|
|
|
|
switch (ep_attrib_get_transfer(attributes)) {
|
|
|
|
case USB_EP_TYPE_BULK:
|
|
|
|
*mps = MIN(cfg->caps.mps, spec_bulk_mps);
|
|
|
|
break;
|
|
|
|
case USB_EP_TYPE_INTERRUPT:
|
|
|
|
*mps = MIN(cfg->caps.mps, spec_int_mps);
|
|
|
|
break;
|
|
|
|
case USB_EP_TYPE_ISO:
|
|
|
|
*mps = MIN(cfg->caps.mps, spec_iso_mps);
|
|
|
|
break;
|
|
|
|
case USB_EP_TYPE_CONTROL:
|
|
|
|
*mps = 64U;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_try_config(const struct device *dev,
|
|
|
|
const uint8_t ep,
|
|
|
|
const uint8_t attributes,
|
|
|
|
uint16_t *const mps,
|
|
|
|
const uint8_t interval)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_ep_config *cfg;
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (cfg == NULL) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
ret = ep_check_config(dev, cfg, ep, attributes, *mps, interval);
|
|
|
|
if (ret == true && *mps == 0U) {
|
|
|
|
ep_update_mps(dev, cfg, attributes, mps);
|
|
|
|
}
|
|
|
|
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return (ret == false) ? -ENOTSUP : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_enable_internal(const struct device *dev,
|
|
|
|
const uint8_t ep,
|
|
|
|
const uint8_t attributes,
|
|
|
|
const uint16_t mps,
|
|
|
|
const uint8_t interval)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_ep_config *cfg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (cfg == NULL) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->stat.enabled) {
|
|
|
|
LOG_ERR("ep 0x%02x already enabled", cfg->addr);
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ep_check_config(dev, cfg, ep, attributes, mps, interval)) {
|
|
|
|
LOG_ERR("Endpoint 0x%02x validation failed", cfg->addr);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg->attributes = attributes;
|
|
|
|
cfg->mps = mps;
|
|
|
|
cfg->interval = interval;
|
|
|
|
|
|
|
|
cfg->stat.odd = 0;
|
|
|
|
cfg->stat.halted = 0;
|
|
|
|
cfg->stat.pending = 0;
|
|
|
|
cfg->stat.data1 = false;
|
|
|
|
ret = api->ep_enable(dev, cfg);
|
|
|
|
cfg->stat.enabled = ret ? false : true;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_enable(const struct device *dev,
|
|
|
|
const uint8_t ep,
|
|
|
|
const uint8_t attributes,
|
|
|
|
const uint16_t mps,
|
|
|
|
const uint8_t interval)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (ep == USB_CONTROL_EP_OUT || ep == USB_CONTROL_EP_IN) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_enabled(dev)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto ep_enable_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = udc_ep_enable_internal(dev, ep, attributes, mps, interval);
|
|
|
|
|
|
|
|
ep_enable_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_disable_internal(const struct device *dev, const uint8_t ep)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_ep_config *cfg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (cfg == NULL) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg->stat.enabled) {
|
|
|
|
LOG_ERR("ep 0x%02x already disabled", cfg->addr);
|
|
|
|
return -EALREADY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = api->ep_disable(dev, cfg);
|
|
|
|
cfg->stat.enabled = ret ? cfg->stat.enabled : false;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_disable(const struct device *dev, const uint8_t ep)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (ep == USB_CONTROL_EP_OUT || ep == USB_CONTROL_EP_IN) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_initialized(dev)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto ep_disable_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = udc_ep_disable_internal(dev, ep);
|
|
|
|
|
|
|
|
ep_disable_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_set_halt(const struct device *dev, const uint8_t ep)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_ep_config *cfg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_enabled(dev)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto ep_set_halt_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (cfg == NULL) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto ep_set_halt_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg->stat.enabled) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto ep_set_halt_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ep_attrib_get_transfer(cfg->attributes) == USB_EP_TYPE_ISO) {
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
goto ep_set_halt_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = api->ep_set_halt(dev, cfg);
|
|
|
|
|
|
|
|
ep_set_halt_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_clear_halt(const struct device *dev, const uint8_t ep)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_ep_config *cfg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_enabled(dev)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto ep_clear_halt_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (cfg == NULL) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto ep_clear_halt_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg->stat.enabled) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto ep_clear_halt_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ep_attrib_get_transfer(cfg->attributes) == USB_EP_TYPE_ISO) {
|
|
|
|
ret = -ENOTSUP;
|
|
|
|
goto ep_clear_halt_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = api->ep_clear_halt(dev, cfg);
|
|
|
|
if (ret == 0) {
|
|
|
|
cfg->stat.halted = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_clear_halt_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_flush(const struct device *dev, const uint8_t ep)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_ep_config *cfg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_enabled(dev)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto ep_flush_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (cfg == NULL) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto ep_flush_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = api->ep_flush(dev, cfg);
|
|
|
|
|
|
|
|
ep_flush_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void udc_debug_ep_enqueue(const struct device *dev,
|
|
|
|
struct udc_ep_config *const cfg)
|
|
|
|
{
|
|
|
|
struct udc_buf_info *bi;
|
|
|
|
struct net_buf *buf;
|
|
|
|
sys_slist_t list;
|
|
|
|
|
|
|
|
list.head = k_fifo_peek_head(&cfg->fifo);
|
|
|
|
list.tail = k_fifo_peek_tail(&cfg->fifo);
|
|
|
|
if (list.head == NULL) {
|
|
|
|
LOG_DBG("ep 0x%02x queue is empty", cfg->addr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_DBG("[de]queue ep 0x%02x:", cfg->addr);
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&list, buf, node) {
|
|
|
|
bi = udc_get_buf_info(buf);
|
|
|
|
LOG_DBG("|-> %p (%u) ->", buf, buf->size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_enqueue(const struct device *dev, struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_ep_config *cfg;
|
|
|
|
struct udc_buf_info *bi;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_enabled(dev)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto ep_enqueue_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
bi = udc_get_buf_info(buf);
|
|
|
|
if (bi->ep == USB_CONTROL_EP_OUT) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto ep_enqueue_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = udc_get_ep_cfg(dev, bi->ep);
|
|
|
|
if (cfg == NULL) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto ep_enqueue_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_DBG("Queue ep 0x%02x %p len %u", cfg->addr, buf,
|
|
|
|
USB_EP_DIR_IS_IN(cfg->addr) ? buf->len : buf->size);
|
|
|
|
|
|
|
|
bi->setup = 0;
|
|
|
|
ret = api->ep_enqueue(dev, cfg, buf);
|
|
|
|
|
|
|
|
ep_enqueue_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_dequeue(const struct device *dev, const uint8_t ep)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_ep_config *cfg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_initialized(dev)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto ep_dequeue_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
cfg = udc_get_ep_cfg(dev, ep);
|
|
|
|
if (cfg == NULL) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto ep_dequeue_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->stat.enabled || cfg->stat.halted) {
|
|
|
|
LOG_INF("ep 0x%02x is not halted|disabled", cfg->addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (UDC_COMMON_LOG_LEVEL == LOG_LEVEL_DBG) {
|
|
|
|
udc_debug_ep_enqueue(dev, cfg);
|
|
|
|
}
|
|
|
|
|
2022-12-08 17:55:02 +01:00
|
|
|
if (k_fifo_is_empty(&cfg->fifo)) {
|
|
|
|
ret = 0;
|
|
|
|
} else {
|
|
|
|
ret = api->ep_dequeue(dev, cfg);
|
|
|
|
}
|
2021-05-22 16:41:09 +02:00
|
|
|
|
|
|
|
ep_dequeue_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct net_buf *udc_ep_buf_alloc(const struct device *dev,
|
|
|
|
const uint8_t ep,
|
|
|
|
const size_t size)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct net_buf *buf = NULL;
|
|
|
|
struct udc_buf_info *bi;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
buf = net_buf_alloc_len(&udc_ep_pool, size, K_NO_WAIT);
|
|
|
|
if (!buf) {
|
|
|
|
LOG_ERR("Failed to allocate net_buf %zd", size);
|
|
|
|
goto ep_alloc_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
bi = udc_get_buf_info(buf);
|
|
|
|
memset(bi, 0, sizeof(struct udc_buf_info));
|
|
|
|
bi->ep = ep;
|
|
|
|
LOG_DBG("Allocate net_buf, ep 0x%02x, size %zd", ep, size);
|
|
|
|
|
|
|
|
ep_alloc_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct net_buf *udc_ctrl_alloc(const struct device *dev,
|
|
|
|
const uint8_t ep,
|
|
|
|
const size_t size)
|
|
|
|
{
|
|
|
|
/* TODO: for now just pass to udc_buf_alloc() */
|
|
|
|
return udc_ep_buf_alloc(dev, ep, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void udc_buf_destroy(struct net_buf *buf)
|
|
|
|
{
|
|
|
|
/* Adjust level and use together with the log in udc_ep_buf_alloc() */
|
|
|
|
LOG_DBG("destroy %p", buf);
|
|
|
|
net_buf_destroy(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ep_buf_free(const struct device *dev, struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
net_buf_unref(buf);
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum udc_bus_speed udc_device_speed(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
enum udc_bus_speed speed = UDC_BUS_UNKNOWN;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_enabled(dev)) {
|
|
|
|
goto device_speed_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (api->device_speed) {
|
|
|
|
speed = api->device_speed(dev);
|
|
|
|
} else {
|
|
|
|
/* TODO: Shall we track connected status in UDC? */
|
|
|
|
speed = UDC_BUS_SPEED_FS;
|
|
|
|
}
|
|
|
|
|
|
|
|
device_speed_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return speed;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_enable(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_initialized(dev)) {
|
|
|
|
ret = -EPERM;
|
|
|
|
goto udc_enable_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (udc_is_enabled(dev)) {
|
|
|
|
ret = -EALREADY;
|
|
|
|
goto udc_enable_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->stage = CTRL_PIPE_STAGE_SETUP;
|
|
|
|
|
|
|
|
ret = api->enable(dev);
|
|
|
|
if (ret == 0) {
|
|
|
|
atomic_set_bit(&data->status, UDC_STATUS_ENABLED);
|
|
|
|
}
|
|
|
|
|
|
|
|
udc_enable_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_disable(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (!udc_is_enabled(dev)) {
|
|
|
|
ret = -EALREADY;
|
|
|
|
goto udc_disable_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = api->disable(dev);
|
|
|
|
atomic_clear_bit(&data->status, UDC_STATUS_ENABLED);
|
|
|
|
|
|
|
|
udc_disable_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_init(const struct device *dev, udc_event_cb_t event_cb)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (event_cb == NULL) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (udc_is_initialized(dev)) {
|
|
|
|
ret = -EALREADY;
|
|
|
|
goto udc_init_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->event_cb = event_cb;
|
|
|
|
|
|
|
|
ret = api->init(dev);
|
|
|
|
if (ret == 0) {
|
|
|
|
atomic_set_bit(&data->status, UDC_STATUS_INITIALIZED);
|
|
|
|
}
|
|
|
|
|
|
|
|
udc_init_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_shutdown(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct udc_api *api = dev->api;
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
api->lock(dev);
|
|
|
|
|
|
|
|
if (udc_is_enabled(dev)) {
|
|
|
|
ret = -EBUSY;
|
|
|
|
goto udc_shutdown_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!udc_is_initialized(dev)) {
|
|
|
|
ret = -EALREADY;
|
|
|
|
goto udc_shutdown_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = api->shutdown(dev);
|
|
|
|
atomic_clear_bit(&data->status, UDC_STATUS_INITIALIZED);
|
|
|
|
|
|
|
|
udc_shutdown_error:
|
|
|
|
api->unlock(dev);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ALWAYS_INLINE
|
|
|
|
struct net_buf *udc_ctrl_alloc_stage(const struct device *dev,
|
|
|
|
struct net_buf *const parent,
|
|
|
|
const uint8_t ep,
|
|
|
|
const size_t size)
|
|
|
|
{
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
buf = udc_ctrl_alloc(dev, ep, size);
|
|
|
|
if (buf == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parent) {
|
|
|
|
net_buf_frag_add(parent, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct net_buf *udc_ctrl_alloc_data(const struct device *dev,
|
|
|
|
struct net_buf *const setup,
|
|
|
|
const uint8_t ep)
|
|
|
|
{
|
|
|
|
size_t size = udc_data_stage_length(setup);
|
|
|
|
struct udc_buf_info *bi;
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
buf = udc_ctrl_alloc_stage(dev, setup, ep, size);
|
|
|
|
if (buf) {
|
|
|
|
bi = udc_get_buf_info(buf);
|
|
|
|
bi->data = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct net_buf *udc_ctrl_alloc_status(const struct device *dev,
|
|
|
|
struct net_buf *const parent,
|
|
|
|
const uint8_t ep)
|
|
|
|
{
|
|
|
|
size_t size = (ep == USB_CONTROL_EP_OUT) ? 64 : 0;
|
|
|
|
struct udc_buf_info *bi;
|
|
|
|
struct net_buf *buf;
|
|
|
|
|
|
|
|
buf = udc_ctrl_alloc_stage(dev, parent, ep, size);
|
|
|
|
if (buf) {
|
|
|
|
bi = udc_get_buf_info(buf);
|
|
|
|
bi->status = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ctrl_submit_s_out_status(const struct device *dev,
|
|
|
|
struct net_buf *const dout)
|
|
|
|
{
|
|
|
|
struct udc_buf_info *bi = udc_get_buf_info(dout);
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
struct net_buf *buf;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
bi->data = true;
|
|
|
|
net_buf_frag_add(data->setup, dout);
|
|
|
|
|
|
|
|
buf = udc_ctrl_alloc_status(dev, dout, USB_CONTROL_EP_IN);
|
|
|
|
if (buf == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return udc_submit_event(dev, UDC_EVT_EP_REQUEST, ret, data->setup);
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ctrl_submit_s_in_status(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
struct net_buf *buf;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!udc_ctrl_stage_is_data_in(dev)) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate buffer for data stage IN */
|
|
|
|
buf = udc_ctrl_alloc_data(dev, data->setup, USB_CONTROL_EP_IN);
|
|
|
|
if (buf == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return udc_submit_event(dev, UDC_EVT_EP_REQUEST, ret, data->setup);
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ctrl_submit_s_status(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
struct net_buf *buf;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Allocate buffer for possible status IN */
|
|
|
|
buf = udc_ctrl_alloc_status(dev, data->setup, USB_CONTROL_EP_IN);
|
|
|
|
if (buf == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return udc_submit_event(dev, UDC_EVT_EP_REQUEST, ret, data->setup);
|
|
|
|
}
|
|
|
|
|
|
|
|
int udc_ctrl_submit_status(const struct device *dev,
|
|
|
|
struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
struct udc_buf_info *bi = udc_get_buf_info(buf);
|
|
|
|
|
|
|
|
bi->status = true;
|
|
|
|
|
|
|
|
return udc_submit_event(dev, UDC_EVT_EP_REQUEST, 0, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool udc_ctrl_stage_is_data_out(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
|
|
|
|
return data->stage == CTRL_PIPE_STAGE_DATA_OUT ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool udc_ctrl_stage_is_data_in(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
|
|
|
|
return data->stage == CTRL_PIPE_STAGE_DATA_IN ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool udc_ctrl_stage_is_status_out(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
|
|
|
|
return data->stage == CTRL_PIPE_STAGE_STATUS_OUT ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool udc_ctrl_stage_is_status_in(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
|
|
|
|
return data->stage == CTRL_PIPE_STAGE_STATUS_IN ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool udc_ctrl_stage_is_no_data(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
|
|
|
|
return data->stage == CTRL_PIPE_STAGE_NO_DATA ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool udc_data_stage_to_host(const struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
struct usb_setup_packet *setup = (void *)buf->data;
|
|
|
|
|
|
|
|
return USB_REQTYPE_GET_DIR(setup->bmRequestType);
|
|
|
|
}
|
|
|
|
|
|
|
|
void udc_ctrl_update_stage(const struct device *dev,
|
|
|
|
struct net_buf *const buf)
|
|
|
|
{
|
|
|
|
struct udc_buf_info *bi = udc_get_buf_info(buf);
|
|
|
|
struct udc_device_caps caps = udc_caps(dev);
|
|
|
|
uint8_t next_stage = CTRL_PIPE_STAGE_ERROR;
|
|
|
|
struct udc_data *data = dev->data;
|
|
|
|
|
|
|
|
__ASSERT(USB_EP_GET_IDX(bi->ep) == 0,
|
|
|
|
"0x%02x is not a control endpoint", bi->ep);
|
|
|
|
|
|
|
|
if (bi->setup && bi->ep == USB_CONTROL_EP_OUT) {
|
|
|
|
uint16_t length = udc_data_stage_length(buf);
|
|
|
|
|
|
|
|
data->setup = buf;
|
|
|
|
|
|
|
|
if (data->stage != CTRL_PIPE_STAGE_SETUP) {
|
|
|
|
LOG_INF("Sequence %u not completed", data->stage);
|
|
|
|
data->stage = CTRL_PIPE_STAGE_SETUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup Stage has been completed (setup packet received),
|
|
|
|
* regardless of the previous stage, this is now being reset.
|
|
|
|
* Next state depends on wLength and the direction bit (D7).
|
|
|
|
*/
|
|
|
|
if (length == 0) {
|
|
|
|
/*
|
|
|
|
* No Data Stage, next is Status Stage
|
|
|
|
* complete sequence: s->status
|
|
|
|
*/
|
|
|
|
LOG_DBG("s->(status)");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_NO_DATA;
|
|
|
|
} else if (udc_data_stage_to_host(buf)) {
|
|
|
|
/*
|
|
|
|
* Next is Data Stage (to host / IN)
|
|
|
|
* complete sequence: s->in->status
|
|
|
|
*/
|
|
|
|
LOG_DBG("s->(in)");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_DATA_IN;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Next is Data Stage (to device / OUT)
|
|
|
|
* complete sequence: s->out->status
|
|
|
|
*/
|
|
|
|
LOG_DBG("s->(out)");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_DATA_OUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (bi->ep == USB_CONTROL_EP_OUT) {
|
|
|
|
if (data->stage == CTRL_PIPE_STAGE_DATA_OUT) {
|
|
|
|
/*
|
|
|
|
* Next sequence is Status Stage if request is okay,
|
|
|
|
* (IN ZLP status to host)
|
|
|
|
*/
|
|
|
|
next_stage = CTRL_PIPE_STAGE_STATUS_IN;
|
|
|
|
} else if (data->stage == CTRL_PIPE_STAGE_STATUS_OUT) {
|
|
|
|
/*
|
|
|
|
* End of a sequence: s->in->status,
|
|
|
|
* We should check the length here because we always
|
|
|
|
* submit a OUT request with the minimum length
|
|
|
|
* of the control endpoint.
|
|
|
|
*/
|
|
|
|
if (buf->len == 0) {
|
|
|
|
LOG_DBG("s-in-status");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_SETUP;
|
|
|
|
} else {
|
|
|
|
LOG_WRN("ZLP expected");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_ERROR;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LOG_ERR("Cannot determine the next stage");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else { /* if (bi->ep == USB_CONTROL_EP_IN) */
|
|
|
|
if (data->stage == CTRL_PIPE_STAGE_STATUS_IN) {
|
|
|
|
/*
|
|
|
|
* End of a sequence: setup->out->in
|
|
|
|
*/
|
|
|
|
LOG_DBG("s-out-status");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_SETUP;
|
|
|
|
} else if (data->stage == CTRL_PIPE_STAGE_DATA_IN) {
|
|
|
|
/*
|
|
|
|
* Data IN stage completed, next sequence
|
|
|
|
* is Status Stage (OUT ZLP status to device).
|
|
|
|
* over-engineered controllers can send status
|
|
|
|
* on their own, skip this state then.
|
|
|
|
*/
|
|
|
|
if (caps.out_ack) {
|
|
|
|
LOG_DBG("s-in->[status]");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_SETUP;
|
|
|
|
} else {
|
|
|
|
LOG_DBG("s-in->(status)");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_STATUS_OUT;
|
|
|
|
}
|
|
|
|
} else if (data->stage == CTRL_PIPE_STAGE_NO_DATA) {
|
|
|
|
/*
|
|
|
|
* End of a sequence (setup->in)
|
|
|
|
* Previous NO Data stage was completed and
|
|
|
|
* we confirmed it with an IN ZLP.
|
|
|
|
*/
|
|
|
|
LOG_DBG("s-status");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_SETUP;
|
|
|
|
} else {
|
|
|
|
LOG_ERR("Cannot determine the next stage");
|
|
|
|
next_stage = CTRL_PIPE_STAGE_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (next_stage == data->stage) {
|
|
|
|
LOG_WRN("State not changed!");
|
|
|
|
}
|
|
|
|
|
|
|
|
data->stage = next_stage;
|
|
|
|
}
|
|
|
|
|
2022-12-19 17:11:01 +01:00
|
|
|
#if defined(CONFIG_UDC_WORKQUEUE)
|
2021-05-22 16:41:09 +02:00
|
|
|
K_KERNEL_STACK_DEFINE(udc_work_q_stack, CONFIG_UDC_WORKQUEUE_STACK_SIZE);
|
|
|
|
|
|
|
|
struct k_work_q udc_work_q;
|
|
|
|
|
|
|
|
static int udc_work_q_init(const struct device *dev)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
|
|
|
|
k_work_queue_start(&udc_work_q,
|
|
|
|
udc_work_q_stack,
|
|
|
|
K_KERNEL_STACK_SIZEOF(udc_work_q_stack),
|
|
|
|
CONFIG_UDC_WORKQUEUE_PRIORITY, NULL);
|
|
|
|
k_thread_name_set(&udc_work_q.thread, "udc_work_q");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SYS_INIT(udc_work_q_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|
|
|
|
#endif
|