Skip to content

Commit 34f42d6

Browse files
jfischer-nonashif
authored andcommitted
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 2d90df4 commit 34f42d6

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
@@ -61,6 +61,8 @@ extern "C" {
6161
*/
6262
#define USB_STRING_DESCRIPTOR_LENGTH(s) (sizeof(s) * 2)
6363

64+
struct usbd_context;
65+
6466
/** Used internally to keep descriptors in order
6567
* @cond INTERNAL_HIDDEN
6668
*/
@@ -92,6 +94,54 @@ struct usbd_str_desc_data {
9294
unsigned int use_hwinfo : 1;
9395
};
9496

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

207-
struct usbd_context;
208-
209257
/**
210258
* @brief Callback type definition for USB device message delivery
211259
*
@@ -243,6 +291,8 @@ struct usbd_context {
243291
sys_slist_t fs_configs;
244292
/** slist to manage High-Speed device configurations */
245293
sys_slist_t hs_configs;
294+
/** dlist to manage vendor requests with recipient device */
295+
sys_dlist_t vreqs;
246296
/** Status of the USB device support */
247297
struct usbd_status status;
248298
/** Pointer to Full-Speed device descriptor */
@@ -618,6 +668,21 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c
618668
.bDescriptorType = USB_DESC_BOS, \
619669
}
620670

671+
/**
672+
* @brief Define a vendor request with recipient device
673+
*
674+
* @param name Vendor request identifier
675+
* @param vcode Vendor request code
676+
* @param vto_host Vendor callback for to-host direction request
677+
* @param vto_dev Vendor callback for to-device direction request
678+
*/
679+
#define USBD_VREQUEST_DEFINE(name, vcode, vto_host, vto_dev) \
680+
static struct usbd_vreq_node name = { \
681+
.code = vcode, \
682+
.to_host = vto_host, \
683+
.to_dev = vto_dev, \
684+
}
685+
621686
/**
622687
* @brief Define USB device support class data
623688
*
@@ -1088,6 +1153,20 @@ int usbd_config_maxpower(struct usbd_context *const uds_ctx,
10881153
*/
10891154
bool usbd_can_detect_vbus(struct usbd_context *const uds_ctx);
10901155

1156+
/**
1157+
* @brief Register an USB vendor request with recipient device
1158+
*
1159+
* The vendor request with the recipient device applies to all configurations
1160+
* within the device.
1161+
*
1162+
* @param[in] uds_ctx Pointer to USB device support context
1163+
* @param[in] vreq_nd Pointer to vendor request node
1164+
*
1165+
* @return 0 on success, other values on fail.
1166+
*/
1167+
int usbd_device_register_vreq(struct usbd_context *const uds_ctx,
1168+
struct usbd_vreq_node *const vreq_nd);
1169+
10911170
/**
10921171
* @}
10931172
*/

subsys/usb/device_next/usbd_ch9.c

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

921+
static int vendor_device_request(struct usbd_context *const uds_ctx,
922+
struct net_buf *const buf)
923+
{
924+
struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx);
925+
struct usbd_vreq_node *vreq_nd;
926+
927+
vreq_nd = usbd_device_get_vreq(uds_ctx, setup->bRequest);
928+
if (vreq_nd == NULL) {
929+
errno = -ENOTSUP;
930+
return 0;
931+
}
932+
933+
if (reqtype_is_to_device(setup) && vreq_nd->to_dev != NULL) {
934+
LOG_ERR("Vendor request 0x%02x to device", setup->bRequest);
935+
errno = vreq_nd->to_dev(uds_ctx, setup, buf);
936+
return 0;
937+
}
938+
939+
if (reqtype_is_to_host(setup) && vreq_nd->to_host != NULL) {
940+
LOG_ERR("Vendor request 0x%02x to host", setup->bRequest);
941+
errno = vreq_nd->to_host(uds_ctx, setup, buf);
942+
return 0;
943+
}
944+
945+
errno = -ENOTSUP;
946+
return 0;
947+
}
948+
921949
static int nonstd_request(struct usbd_context *const uds_ctx,
922950
struct net_buf *const dbuf)
923951
{
@@ -946,7 +974,7 @@ static int nonstd_request(struct usbd_context *const uds_ctx,
946974
ret = usbd_class_control_to_host(c_nd->c_data, setup, dbuf);
947975
}
948976
} else {
949-
errno = -ENOTSUP;
977+
return vendor_device_request(uds_ctx, dbuf);
950978
}
951979

952980
return ret;

subsys/usb/device_next/usbd_core.c

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

254+
usbd_device_unregister_all_vreq(uds_ctx);
255+
254256
return udc_shutdown(uds_ctx->dev);
255257
}
256258

subsys/usb/device_next/usbd_device.c

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

335335
return caps.can_detect_vbus;
336336
}
337+
338+
struct usbd_vreq_node *usbd_device_get_vreq(struct usbd_context *const uds_ctx,
339+
const uint8_t code)
340+
{
341+
struct usbd_vreq_node *vreq_nd;
342+
343+
SYS_DLIST_FOR_EACH_CONTAINER(&uds_ctx->vreqs, vreq_nd, node) {
344+
if (vreq_nd->code == code) {
345+
return vreq_nd;
346+
}
347+
}
348+
349+
return NULL;
350+
}
351+
352+
int usbd_device_register_vreq(struct usbd_context *const uds_ctx,
353+
struct usbd_vreq_node *const vreq_nd)
354+
{
355+
int ret = 0;
356+
357+
usbd_device_lock(uds_ctx);
358+
359+
if (usbd_is_initialized(uds_ctx)) {
360+
ret = -EPERM;
361+
goto error;
362+
}
363+
364+
if (vreq_nd->to_dev == NULL && vreq_nd->to_host == NULL) {
365+
ret = -EINVAL;
366+
goto error;
367+
}
368+
369+
if (!sys_dnode_is_linked(&uds_ctx->vreqs)) {
370+
LOG_DBG("Initialize vendor request list");
371+
sys_dlist_init(&uds_ctx->vreqs);
372+
}
373+
374+
if (sys_dnode_is_linked(&vreq_nd->node)) {
375+
ret = -EALREADY;
376+
goto error;
377+
}
378+
379+
sys_dlist_append(&uds_ctx->vreqs, &vreq_nd->node);
380+
LOG_DBG("Registered vendor request 0x%02x", vreq_nd->code);
381+
382+
error:
383+
usbd_device_unlock(uds_ctx);
384+
return ret;
385+
}
386+
387+
void usbd_device_unregister_all_vreq(struct usbd_context *const uds_ctx)
388+
{
389+
struct usbd_vreq_node *tmp;
390+
sys_dnode_t *node;
391+
392+
if (!sys_dnode_is_linked(&uds_ctx->vreqs)) {
393+
return;
394+
}
395+
396+
while ((node = sys_dlist_get(&uds_ctx->vreqs))) {
397+
tmp = CONTAINER_OF(node, struct usbd_vreq_node, node);
398+
LOG_DBG("Remove vendor request 0x%02x", tmp->code);
399+
}
400+
}

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)