135 lines
2.9 KiB
C
135 lines
2.9 KiB
C
/*
|
|
* Copyright (c) 2024 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
#include <zephyr/sys/slist.h>
|
|
#include <zephyr/usb/usbd.h>
|
|
|
|
#include "usbd_device.h"
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(usbd_msg, CONFIG_USBD_LOG_LEVEL);
|
|
|
|
static void msg_work_handler(struct k_work *work);
|
|
static K_WORK_DELAYABLE_DEFINE(msg_work, msg_work_handler);
|
|
static struct k_spinlock ml_lock;
|
|
static sys_slist_t msg_list;
|
|
|
|
struct usbd_msg_pkt {
|
|
sys_snode_t node;
|
|
struct usbd_context *ctx;
|
|
struct usbd_msg msg;
|
|
};
|
|
|
|
K_MEM_SLAB_DEFINE_STATIC(usbd_msg_slab, sizeof(struct usbd_msg_pkt),
|
|
CONFIG_USBD_MSG_SLAB_COUNT, sizeof(void *));
|
|
|
|
static inline void usbd_msg_pub(struct usbd_context *const ctx,
|
|
const struct usbd_msg msg)
|
|
{
|
|
struct usbd_msg_pkt *m_pkt;
|
|
k_spinlock_key_t key;
|
|
|
|
if (k_mem_slab_alloc(&usbd_msg_slab, (void **)&m_pkt, K_NO_WAIT)) {
|
|
LOG_DBG("Failed to allocate message memory");
|
|
return;
|
|
}
|
|
|
|
m_pkt->ctx = ctx;
|
|
m_pkt->msg = msg;
|
|
|
|
key = k_spin_lock(&ml_lock);
|
|
sys_slist_append(&msg_list, &m_pkt->node);
|
|
k_spin_unlock(&ml_lock, key);
|
|
|
|
if (k_work_schedule(&msg_work, K_NO_WAIT) < 0) {
|
|
__ASSERT(false, "Failed to schedule work");
|
|
}
|
|
}
|
|
|
|
static void msg_work_handler(struct k_work *work)
|
|
{
|
|
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
|
|
struct usbd_msg_pkt *m_pkt;
|
|
k_spinlock_key_t key;
|
|
sys_snode_t *node;
|
|
|
|
key = k_spin_lock(&ml_lock);
|
|
node = sys_slist_peek_head(&msg_list);
|
|
k_spin_unlock(&ml_lock, key);
|
|
|
|
__ASSERT(node != NULL, "slist appears to be empty");
|
|
m_pkt = SYS_SLIST_CONTAINER(node, m_pkt, node);
|
|
|
|
if (!usbd_is_initialized(m_pkt->ctx)) {
|
|
LOG_DBG("USB device support is not yet initialized");
|
|
(void)k_work_reschedule(dwork, K_MSEC(CONFIG_USBD_MSG_WORK_DELAY));
|
|
return;
|
|
}
|
|
|
|
key = k_spin_lock(&ml_lock);
|
|
node = sys_slist_get(&msg_list);
|
|
k_spin_unlock(&ml_lock, key);
|
|
|
|
if (node != NULL) {
|
|
m_pkt = SYS_SLIST_CONTAINER(node, m_pkt, node);
|
|
m_pkt->ctx->msg_cb(m_pkt->ctx, &m_pkt->msg);
|
|
k_mem_slab_free(&usbd_msg_slab, (void *)m_pkt);
|
|
}
|
|
|
|
if (!sys_slist_is_empty(&msg_list)) {
|
|
(void)k_work_schedule(dwork, K_NO_WAIT);
|
|
}
|
|
}
|
|
|
|
int usbd_msg_register_cb(struct usbd_context *const uds_ctx,
|
|
const usbd_msg_cb_t cb)
|
|
{
|
|
int ret = 0;
|
|
|
|
usbd_device_lock(uds_ctx);
|
|
|
|
if (uds_ctx->msg_cb != NULL) {
|
|
ret = -EALREADY;
|
|
goto register_cb_exit;
|
|
}
|
|
|
|
uds_ctx->msg_cb = cb;
|
|
|
|
register_cb_exit:
|
|
usbd_device_unlock(uds_ctx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void usbd_msg_pub_simple(struct usbd_context *const ctx,
|
|
const enum usbd_msg_type type, const int status)
|
|
{
|
|
const struct usbd_msg msg = {
|
|
.type = type,
|
|
.status = status,
|
|
};
|
|
|
|
if (ctx->msg_cb != NULL) {
|
|
usbd_msg_pub(ctx, msg);
|
|
}
|
|
}
|
|
|
|
void usbd_msg_pub_device(struct usbd_context *const ctx,
|
|
const enum usbd_msg_type type, const struct device *const dev)
|
|
{
|
|
const struct usbd_msg msg = {
|
|
.type = type,
|
|
.dev = dev,
|
|
};
|
|
|
|
if (ctx->msg_cb != NULL) {
|
|
usbd_msg_pub(ctx, msg);
|
|
}
|
|
}
|