zephyr/drivers/usb/uhc/uhc_common.c

359 lines
6.5 KiB
C

/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/usb/usb_ch9.h>
#include "uhc_common.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(uhc, CONFIG_UHC_DRIVER_LOG_LEVEL);
K_MEM_SLAB_DEFINE_STATIC(uhc_xfer_pool, sizeof(struct uhc_transfer),
CONFIG_UHC_XFER_COUNT, sizeof(void *));
NET_BUF_POOL_VAR_DEFINE(uhc_ep_pool,
CONFIG_UHC_BUF_COUNT, CONFIG_UHC_BUF_POOL_SIZE,
0, NULL);
int uhc_submit_event(const struct device *dev,
const enum uhc_event_type type,
const int status)
{
struct uhc_data *data = dev->data;
struct uhc_event drv_evt = {
.type = type,
.status = status,
.dev = dev,
};
if (!uhc_is_initialized(dev)) {
return -EPERM;
}
return data->event_cb(dev, &drv_evt);
}
void uhc_xfer_return(const struct device *dev,
struct uhc_transfer *const xfer,
const int err)
{
struct uhc_data *data = dev->data;
struct uhc_event drv_evt = {
.type = UHC_EVT_EP_REQUEST,
.xfer = xfer,
.dev = dev,
};
sys_dlist_remove(&xfer->node);
xfer->queued = 0;
xfer->err = err;
data->event_cb(dev, &drv_evt);
}
struct uhc_transfer *uhc_xfer_get_next(const struct device *dev)
{
struct uhc_data *data = dev->data;
struct uhc_transfer *xfer;
sys_dnode_t *node;
/* Draft, WIP */
node = sys_dlist_peek_head(&data->ctrl_xfers);
if (node == NULL) {
node = sys_dlist_peek_head(&data->bulk_xfers);
}
return (node == NULL) ? NULL : SYS_DLIST_CONTAINER(node, xfer, node);
}
int uhc_xfer_append(const struct device *dev,
struct uhc_transfer *const xfer)
{
struct uhc_data *data = dev->data;
sys_dlist_append(&data->ctrl_xfers, &xfer->node);
return 0;
}
struct net_buf *uhc_xfer_buf_alloc(const struct device *dev,
const size_t size)
{
return net_buf_alloc_len(&uhc_ep_pool, size, K_NO_WAIT);
}
void uhc_xfer_buf_free(const struct device *dev, struct net_buf *const buf)
{
net_buf_unref(buf);
}
struct uhc_transfer *uhc_xfer_alloc(const struct device *dev,
const uint8_t addr,
const uint8_t ep,
const uint8_t attrib,
const uint16_t mps,
const uint16_t timeout,
void *const udev,
void *const cb)
{
const struct uhc_api *api = dev->api;
struct uhc_transfer *xfer = NULL;
api->lock(dev);
if (!uhc_is_initialized(dev)) {
goto xfer_alloc_error;
}
LOG_DBG("Allocate xfer, ep 0x%02x attrib 0x%02x cb %p",
ep, attrib, cb);
if (k_mem_slab_alloc(&uhc_xfer_pool, (void **)&xfer, K_NO_WAIT)) {
LOG_ERR("Failed to allocate transfer");
goto xfer_alloc_error;
}
memset(xfer, 0, sizeof(struct uhc_transfer));
xfer->addr = addr;
xfer->ep = ep;
xfer->attrib = attrib;
xfer->mps = mps;
xfer->timeout = timeout;
xfer->udev = udev;
xfer->cb = cb;
xfer_alloc_error:
api->unlock(dev);
return xfer;
}
struct uhc_transfer *uhc_xfer_alloc_with_buf(const struct device *dev,
const uint8_t addr,
const uint8_t ep,
const uint8_t attrib,
const uint16_t mps,
const uint16_t timeout,
void *const udev,
void *const cb,
size_t size)
{
struct uhc_transfer *xfer;
struct net_buf *buf;
buf = uhc_xfer_buf_alloc(dev, size);
if (buf == NULL) {
return NULL;
}
xfer = uhc_xfer_alloc(dev, addr, ep, attrib, mps, timeout, udev, cb);
if (xfer == NULL) {
net_buf_unref(buf);
return NULL;
}
xfer->buf = buf;
return xfer;
}
int uhc_xfer_free(const struct device *dev, struct uhc_transfer *const xfer)
{
const struct uhc_api *api = dev->api;
int ret = 0;
api->lock(dev);
if (xfer->queued) {
ret = -EBUSY;
LOG_ERR("Transfer is still queued");
goto xfer_free_error;
}
k_mem_slab_free(&uhc_xfer_pool, (void *)xfer);
xfer_free_error:
api->unlock(dev);
return ret;
}
int uhc_xfer_buf_add(const struct device *dev,
struct uhc_transfer *const xfer,
struct net_buf *buf)
{
const struct uhc_api *api = dev->api;
int ret = 0;
api->lock(dev);
if (xfer->queued) {
ret = -EBUSY;
} else {
xfer->buf = buf;
}
api->unlock(dev);
return ret;
}
int uhc_ep_enqueue(const struct device *dev, struct uhc_transfer *const xfer)
{
const struct uhc_api *api = dev->api;
int ret;
api->lock(dev);
if (!uhc_is_initialized(dev)) {
ret = -EPERM;
goto ep_enqueue_error;
}
xfer->queued = 1;
ret = api->ep_enqueue(dev, xfer);
if (ret) {
xfer->queued = 0;
}
ep_enqueue_error:
api->unlock(dev);
return ret;
}
int uhc_ep_dequeue(const struct device *dev, struct uhc_transfer *const xfer)
{
const struct uhc_api *api = dev->api;
int ret;
api->lock(dev);
if (!uhc_is_initialized(dev)) {
ret = -EPERM;
goto ep_dequeue_error;
}
ret = api->ep_dequeue(dev, xfer);
xfer->queued = 0;
ep_dequeue_error:
api->unlock(dev);
return ret;
}
int uhc_enable(const struct device *dev)
{
const struct uhc_api *api = dev->api;
struct uhc_data *data = dev->data;
int ret;
api->lock(dev);
if (!uhc_is_initialized(dev)) {
ret = -EPERM;
goto uhc_enable_error;
}
if (uhc_is_enabled(dev)) {
ret = -EALREADY;
goto uhc_enable_error;
}
ret = api->enable(dev);
if (ret == 0) {
atomic_set_bit(&data->status, UHC_STATUS_ENABLED);
}
uhc_enable_error:
api->unlock(dev);
return ret;
}
int uhc_disable(const struct device *dev)
{
const struct uhc_api *api = dev->api;
struct uhc_data *data = dev->data;
int ret;
api->lock(dev);
if (!uhc_is_enabled(dev)) {
ret = -EALREADY;
goto uhc_disable_error;
}
ret = api->disable(dev);
atomic_clear_bit(&data->status, UHC_STATUS_ENABLED);
uhc_disable_error:
api->unlock(dev);
return ret;
}
int uhc_init(const struct device *dev, uhc_event_cb_t event_cb)
{
const struct uhc_api *api = dev->api;
struct uhc_data *data = dev->data;
int ret;
if (event_cb == NULL) {
return -EINVAL;
}
api->lock(dev);
if (uhc_is_initialized(dev)) {
ret = -EALREADY;
goto uhc_init_error;
}
data->event_cb = event_cb;
sys_dlist_init(&data->ctrl_xfers);
sys_dlist_init(&data->bulk_xfers);
ret = api->init(dev);
if (ret == 0) {
atomic_set_bit(&data->status, UHC_STATUS_INITIALIZED);
}
uhc_init_error:
api->unlock(dev);
return ret;
}
int uhc_shutdown(const struct device *dev)
{
const struct uhc_api *api = dev->api;
struct uhc_data *data = dev->data;
int ret;
api->lock(dev);
if (uhc_is_enabled(dev)) {
ret = -EBUSY;
goto uhc_shutdown_error;
}
if (!uhc_is_initialized(dev)) {
ret = -EALREADY;
goto uhc_shutdown_error;
}
ret = api->shutdown(dev);
atomic_clear_bit(&data->status, UHC_STATUS_INITIALIZED);
uhc_shutdown_error:
api->unlock(dev);
return ret;
}