Skip to content

Commit b00b5ca

Browse files
committed
usb: device_next: support vendor request with recipient device
Allow the user to register a vendor request node identified by the vendor code (bRequest) and containing two callbacks to handle the vendor request. The device stack uses the vendor request node to call the vendor request callbacks when it receives a request of type Vendor, recipient Device, and bRequest value equal to the vendor code. Signed-off-by: Johann Fischer <[email protected]>
1 parent 152644c commit b00b5ca

File tree

5 files changed

+196
-3
lines changed

5 files changed

+196
-3
lines changed

include/zephyr/usb/usbd.h

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ extern "C" {
5959
*/
6060
#define USB_STRING_DESCRIPTOR_LENGTH(s) (sizeof(s) * 2)
6161

62+
struct usbd_context;
63+
6264
/** Used internally to keep descriptors in order
6365
* @cond INTERNAL_HIDDEN
6466
*/
@@ -89,6 +91,54 @@ struct usbd_str_desc_data {
8991
unsigned int use_hwinfo : 1;
9092
};
9193

94+
/**
95+
* USBD vendor request node
96+
*
97+
* Vendor request node is identified by the vendor code and is used to register
98+
* callbacks to handle the vendor request with the receiving device.
99+
* When the device stack receives a request with type Vendor and recipient
100+
* Device, and bRequest value equal to the vendor request code, it will call
101+
* the vendor callbacks depending on the direction of the request.
102+
*
103+
* Example callback code fragment:
104+
*
105+
* @code{.c}
106+
* static int foo_to_host_cb(const struct usbd_context *const ctx,
107+
* const struct usb_setup_packet *const setup,
108+
* struct net_buf *const buf)
109+
* {
110+
* if (setup->wIndex == WEBUSB_REQ_GET_URL) {
111+
* uint8_t index = USB_GET_DESCRIPTOR_INDEX(setup->wValue);
112+
*
113+
* if (index != SAMPLE_WEBUSB_LANDING_PAGE) {
114+
* return -ENOTSUP;
115+
* }
116+
*
117+
* net_buf_add_mem(buf, &webusb_origin_url,
118+
* MIN(net_buf_tailroom(buf), sizeof(webusb_origin_url)));
119+
*
120+
* return 0;
121+
* }
122+
*
123+
* return -ENOTSUP;
124+
* }
125+
* @endcode
126+
*/
127+
struct usbd_vreq_node {
128+
/** Node information for the dlist */
129+
sys_dnode_t node;
130+
/** Vendor code (bRequest value) */
131+
const uint8_t code;
132+
/** Vendor request callback for device-to-host direction */
133+
int (*to_host)(const struct usbd_context *const ctx,
134+
const struct usb_setup_packet *const setup,
135+
struct net_buf *const buf);
136+
/** Vendor request callback for host-to-device direction */
137+
int (*to_dev)(const struct usbd_context *const ctx,
138+
const struct usb_setup_packet *const setup,
139+
const struct net_buf *const buf);
140+
};
141+
92142
/**
93143
* USBD BOS Device Capability descriptor data
94144
*/
@@ -199,8 +249,6 @@ struct usbd_status {
199249
enum usbd_speed speed : 2;
200250
};
201251

202-
struct usbd_context;
203-
204252
/**
205253
* @brief Callback type definition for USB device message delivery
206254
*
@@ -238,6 +286,8 @@ struct usbd_context {
238286
sys_slist_t fs_configs;
239287
/** slist to manage High-Speed device configurations */
240288
sys_slist_t hs_configs;
289+
/** dlist to manage vendor requests with recipient device */
290+
sys_dlist_t vreqs;
241291
/** Status of the USB device support */
242292
struct usbd_status status;
243293
/** Pointer to Full-Speed device descriptor */
@@ -597,6 +647,21 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c
597647
.bDescriptorType = USB_DESC_BOS, \
598648
}
599649

650+
/**
651+
* @brief Define a vendor request with recipient device
652+
*
653+
* @param name Vendor request identifier
654+
* @param vcode Vendor request code
655+
* @param vto_host Vendor callback for to-host direction request
656+
* @param vto_dev Vendor callback for to-device direction request
657+
*/
658+
#define USBD_VREQUEST_DEFINE(name, vcode, vto_host, vto_dev) \
659+
static struct usbd_vreq_node name = { \
660+
.code = vcode, \
661+
.to_host = vto_host, \
662+
.to_dev = vto_dev, \
663+
}
664+
600665
/**
601666
* @brief Define USB device support class data
602667
*
@@ -1056,6 +1121,20 @@ int usbd_config_maxpower(struct usbd_context *const uds_ctx,
10561121
*/
10571122
bool usbd_can_detect_vbus(struct usbd_context *const uds_ctx);
10581123

1124+
/**
1125+
* @brief Register an USB vendor request with recipient device
1126+
*
1127+
* The vendor request with the recipient device applies to all configurations
1128+
* within the device.
1129+
*
1130+
* @param[in] uds_ctx Pointer to USB device support context
1131+
* @param[in] vreq_nd Pointer to vendor request node
1132+
*
1133+
* @return 0 on success, other values on fail.
1134+
*/
1135+
int usbd_device_register_vreq(struct usbd_context *const uds_ctx,
1136+
struct usbd_vreq_node *const vreq_nd);
1137+
10591138
/**
10601139
* @}
10611140
*/

subsys/usb/device_next/usbd_ch9.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,34 @@ static int std_request_to_host(struct usbd_context *const uds_ctx,
913913
return ret;
914914
}
915915

916+
static int vendor_device_request(struct usbd_context *const uds_ctx,
917+
struct net_buf *const buf)
918+
{
919+
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
920+
struct usbd_vreq_node *vreq_nd;
921+
922+
vreq_nd = usbd_device_get_vreq(uds_ctx, setup->bRequest);
923+
if (vreq_nd == NULL) {
924+
errno = -ENOTSUP;
925+
return 0;
926+
}
927+
928+
if (reqtype_is_to_device(setup) && vreq_nd->to_dev != NULL) {
929+
LOG_ERR("Vendor request 0x%02x to device", setup->bRequest);
930+
errno = vreq_nd->to_dev(uds_ctx, setup, buf);
931+
return 0;
932+
}
933+
934+
if (reqtype_is_to_host(setup) && vreq_nd->to_host != NULL) {
935+
LOG_ERR("Vendor request 0x%02x to host", setup->bRequest);
936+
errno = vreq_nd->to_host(uds_ctx, setup, buf);
937+
return 0;
938+
}
939+
940+
errno = -ENOTSUP;
941+
return 0;
942+
}
943+
916944
static int nonstd_request(struct usbd_context *const uds_ctx,
917945
struct net_buf *const dbuf)
918946
{
@@ -941,7 +969,7 @@ static int nonstd_request(struct usbd_context *const uds_ctx,
941969
ret = usbd_class_control_to_host(c_nd->c_data, setup, dbuf);
942970
}
943971
} else {
944-
errno = -ENOTSUP;
972+
return vendor_device_request(uds_ctx, dbuf);
945973
}
946974

947975
return ret;

subsys/usb/device_next/usbd_core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ int usbd_device_shutdown_core(struct usbd_context *const uds_ctx)
249249
LOG_ERR("Failed to cleanup descriptors, %d", ret);
250250
}
251251

252+
usbd_device_unregister_all_vreq(uds_ctx);
253+
252254
return udc_shutdown(uds_ctx->dev);
253255
}
254256

subsys/usb/device_next/usbd_device.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,67 @@ bool usbd_can_detect_vbus(struct usbd_context *const uds_ctx)
310310

311311
return caps.can_detect_vbus;
312312
}
313+
314+
struct usbd_vreq_node *usbd_device_get_vreq(struct usbd_context *const uds_ctx,
315+
const uint8_t code)
316+
{
317+
struct usbd_vreq_node *vreq_nd;
318+
319+
SYS_DLIST_FOR_EACH_CONTAINER(&uds_ctx->vreqs, vreq_nd, node) {
320+
if (vreq_nd->code == code) {
321+
return vreq_nd;
322+
}
323+
}
324+
325+
return NULL;
326+
}
327+
328+
int usbd_device_register_vreq(struct usbd_context *const uds_ctx,
329+
struct usbd_vreq_node *const vreq_nd)
330+
{
331+
int ret = 0;
332+
333+
usbd_device_lock(uds_ctx);
334+
335+
if (usbd_is_initialized(uds_ctx)) {
336+
ret = -EPERM;
337+
goto error;
338+
}
339+
340+
if (vreq_nd->to_dev == NULL && vreq_nd->to_host == NULL) {
341+
ret = -EINVAL;
342+
goto error;
343+
}
344+
345+
if (!sys_dnode_is_linked(&uds_ctx->vreqs)) {
346+
LOG_DBG("Initialize vendor request list");
347+
sys_dlist_init(&uds_ctx->vreqs);
348+
}
349+
350+
if (sys_dnode_is_linked(&vreq_nd->node)) {
351+
ret = -EALREADY;
352+
goto error;
353+
}
354+
355+
sys_dlist_append(&uds_ctx->vreqs, &vreq_nd->node);
356+
LOG_DBG("Registered vendor request 0x%02x", vreq_nd->code);
357+
358+
error:
359+
usbd_device_unlock(uds_ctx);
360+
return ret;
361+
}
362+
363+
void usbd_device_unregister_all_vreq(struct usbd_context *const uds_ctx)
364+
{
365+
struct usbd_vreq_node *tmp;
366+
sys_dnode_t *node;
367+
368+
if (!sys_dnode_is_linked(&uds_ctx->vreqs)) {
369+
return;
370+
}
371+
372+
while ((node = sys_dlist_get(&uds_ctx->vreqs))) {
373+
tmp = CONTAINER_OF(node, struct usbd_vreq_node, node);
374+
LOG_DBG("Remove vendor request 0x%02x", tmp->code);
375+
}
376+
}

subsys/usb/device_next/usbd_device.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,26 @@
1010
#include <zephyr/drivers/usb/udc.h>
1111
#include <zephyr/usb/usbd.h>
1212

13+
/**
14+
* @brief Get vendor request node
15+
*
16+
* Get vendor request node from internal vendor request list.
17+
*
18+
* @param[in] ctx Pointer to USB device support context
19+
* @param[in] code Vendor request code
20+
*
21+
* @return pointer to vendor request node or NULL if not found.
22+
*/
23+
struct usbd_vreq_node *usbd_device_get_vreq(struct usbd_context *const uds_ctx,
24+
const uint8_t code);
25+
26+
/**
27+
* @brief Unregister all registered vendor request
28+
*
29+
* @param[in] uds_ctx Pointer to a device context
30+
*/
31+
void usbd_device_unregister_all_vreq(struct usbd_context *const uds_ctx);
32+
1333
/**
1434
* @brief Get device descriptor bNumConfigurations value
1535
*

0 commit comments

Comments
 (0)