1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdio.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/sys/__assert.h>
10 #include <zephyr/sys/slist.h>
11 #include <zephyr/usb/usbd.h>
12 
13 #include "usbd_device.h"
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(usbd_msg, CONFIG_USBD_LOG_LEVEL);
17 
18 static void msg_work_handler(struct k_work *work);
19 static K_WORK_DELAYABLE_DEFINE(msg_work, msg_work_handler);
20 static struct k_spinlock ml_lock;
21 static sys_slist_t msg_list;
22 
23 struct usbd_msg_pkt {
24 	sys_snode_t node;
25 	struct usbd_context *ctx;
26 	struct usbd_msg msg;
27 };
28 
29 K_MEM_SLAB_DEFINE_STATIC(usbd_msg_slab, sizeof(struct usbd_msg_pkt),
30 			 CONFIG_USBD_MSG_SLAB_COUNT, sizeof(void *));
31 
usbd_msg_pub(struct usbd_context * const ctx,const struct usbd_msg msg)32 static inline void usbd_msg_pub(struct usbd_context *const ctx,
33 				const struct usbd_msg msg)
34 {
35 	struct usbd_msg_pkt *m_pkt;
36 	k_spinlock_key_t key;
37 
38 	if (k_mem_slab_alloc(&usbd_msg_slab, (void **)&m_pkt, K_NO_WAIT)) {
39 		LOG_DBG("Failed to allocate message memory");
40 		return;
41 	}
42 
43 	m_pkt->ctx = ctx;
44 	m_pkt->msg = msg;
45 
46 	key = k_spin_lock(&ml_lock);
47 	sys_slist_append(&msg_list, &m_pkt->node);
48 	k_spin_unlock(&ml_lock, key);
49 
50 	if (k_work_schedule(&msg_work, K_NO_WAIT) < 0) {
51 		__ASSERT(false, "Failed to schedule work");
52 	}
53 }
54 
msg_work_handler(struct k_work * work)55 static void msg_work_handler(struct k_work *work)
56 {
57 	struct k_work_delayable *dwork = k_work_delayable_from_work(work);
58 	struct usbd_msg_pkt *m_pkt;
59 	k_spinlock_key_t key;
60 	sys_snode_t *node;
61 
62 	key = k_spin_lock(&ml_lock);
63 	node = sys_slist_peek_head(&msg_list);
64 	k_spin_unlock(&ml_lock, key);
65 
66 	__ASSERT(node != NULL, "slist appears to be empty");
67 	m_pkt = SYS_SLIST_CONTAINER(node, m_pkt, node);
68 
69 	if (!usbd_is_initialized(m_pkt->ctx)) {
70 		LOG_DBG("USB device support is not yet initialized");
71 		(void)k_work_reschedule(dwork, K_MSEC(CONFIG_USBD_MSG_WORK_DELAY));
72 		return;
73 	}
74 
75 	key = k_spin_lock(&ml_lock);
76 	node = sys_slist_get(&msg_list);
77 	k_spin_unlock(&ml_lock, key);
78 
79 	if (node != NULL) {
80 		m_pkt = SYS_SLIST_CONTAINER(node, m_pkt, node);
81 		m_pkt->ctx->msg_cb(m_pkt->ctx, &m_pkt->msg);
82 		k_mem_slab_free(&usbd_msg_slab, (void *)m_pkt);
83 	}
84 
85 	if (!sys_slist_is_empty(&msg_list)) {
86 		(void)k_work_schedule(dwork, K_NO_WAIT);
87 	}
88 }
89 
usbd_msg_register_cb(struct usbd_context * const uds_ctx,const usbd_msg_cb_t cb)90 int usbd_msg_register_cb(struct usbd_context *const uds_ctx,
91 			 const usbd_msg_cb_t cb)
92 {
93 	int ret = 0;
94 
95 	usbd_device_lock(uds_ctx);
96 
97 	if (uds_ctx->msg_cb != NULL) {
98 		ret = -EALREADY;
99 		goto register_cb_exit;
100 	}
101 
102 	uds_ctx->msg_cb = cb;
103 
104 register_cb_exit:
105 	usbd_device_unlock(uds_ctx);
106 
107 	return ret;
108 }
109 
usbd_msg_pub_simple(struct usbd_context * const ctx,const enum usbd_msg_type type,const int status)110 void usbd_msg_pub_simple(struct usbd_context *const ctx,
111 			 const enum usbd_msg_type type, const int status)
112 {
113 	const struct usbd_msg msg = {
114 		.type = type,
115 		.status = status,
116 	};
117 
118 	if (ctx->msg_cb != NULL) {
119 		usbd_msg_pub(ctx, msg);
120 	}
121 }
122 
usbd_msg_pub_device(struct usbd_context * const ctx,const enum usbd_msg_type type,const struct device * const dev)123 void usbd_msg_pub_device(struct usbd_context *const ctx,
124 			 const enum usbd_msg_type type, const struct device *const dev)
125 {
126 	const struct usbd_msg msg = {
127 		.type = type,
128 		.dev = dev,
129 	};
130 
131 	if (ctx->msg_cb != NULL) {
132 		usbd_msg_pub(ctx, msg);
133 	}
134 }
135