|  | /* | 
|  | * Copyright (c) 2023 Nordic Semiconductor ASA | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * USB device controller (UDC) driver skeleton | 
|  | * | 
|  | * This is a skeleton for a device controller driver using the UDC API. | 
|  | * Please use it as a starting point for a driver implementation for your | 
|  | * USB device controller. Maintaining a common style, terminology and | 
|  | * abbreviations will allow us to speed up reviews and reduce maintenance. | 
|  | * Copy UDC driver skeleton, remove all unrelated comments and replace the | 
|  | * copyright notice with your own. | 
|  | * | 
|  | * Typically, a driver implementation contains only a single source file, | 
|  | * but the large list of e.g. register definitions should be in a separate | 
|  | * .h file. | 
|  | * | 
|  | * If you want to define a helper macro, check if there is something similar | 
|  | * in include/zephyr/sys/util.h or include/zephyr/usb/usb_ch9.h that you can use. | 
|  | * Please keep all identifiers and logging messages concise and clear. | 
|  | */ | 
|  |  | 
|  | #include "udc_common.h" | 
|  |  | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include <zephyr/kernel.h> | 
|  | #include <zephyr/drivers/usb/udc.h> | 
|  |  | 
|  | #include <zephyr/logging/log.h> | 
|  | LOG_MODULE_REGISTER(udc_skeleton, CONFIG_UDC_DRIVER_LOG_LEVEL); | 
|  |  | 
|  | /* | 
|  | * Structure for holding controller configuration items that can remain in | 
|  | * non-volatile memory. This is usually accessed as | 
|  | *   const struct udc_skeleton_config *config = dev->config; | 
|  | */ | 
|  | struct udc_skeleton_config { | 
|  | size_t num_of_eps; | 
|  | struct udc_ep_config *ep_cfg_in; | 
|  | struct udc_ep_config *ep_cfg_out; | 
|  | void (*make_thread)(const struct device *dev); | 
|  | int speed_idx; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * Structure to hold driver private data. | 
|  | * Note that this is not accessible via dev->data, but as | 
|  | *   struct udc_skeleton_data *priv = udc_get_private(dev); | 
|  | */ | 
|  | struct udc_skeleton_data { | 
|  | struct k_thread thread_data; | 
|  | }; | 
|  |  | 
|  | /* | 
|  | * You can use one thread per driver instance model or UDC driver workqueue, | 
|  | * whichever model suits your needs best. If you decide to use the UDC workqueue, | 
|  | * enable Kconfig option UDC_WORKQUEUE and remove the handler below and | 
|  | * caller from the UDC_SKELETON_DEVICE_DEFINE macro. | 
|  | */ | 
|  | static ALWAYS_INLINE void skeleton_thread_handler(void *const arg) | 
|  | { | 
|  | const struct device *dev = (const struct device *)arg; | 
|  |  | 
|  | LOG_DBG("Driver %p thread started", dev); | 
|  | while (true) { | 
|  | k_msleep(1000); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is called in the context of udc_ep_enqueue() and must | 
|  | * not block. The driver can immediately claim the buffer if the queue is empty, | 
|  | * but usually it is offloaded to a thread or workqueue to handle transfers | 
|  | * in a single location. Please refer to existing driver implementations | 
|  | * for examples. | 
|  | */ | 
|  | static int udc_skeleton_ep_enqueue(const struct device *dev, | 
|  | struct udc_ep_config *const cfg, | 
|  | struct net_buf *buf) | 
|  | { | 
|  | LOG_DBG("%p enqueue %p", dev, buf); | 
|  | udc_buf_put(cfg, buf); | 
|  |  | 
|  | if (cfg->stat.halted) { | 
|  | /* | 
|  | * It is fine to enqueue a transfer for a halted endpoint, | 
|  | * you need to make sure that transfers are retriggered when | 
|  | * the halt is cleared. | 
|  | * | 
|  | * Always use the abbreviation 'ep' for the endpoint address | 
|  | * and 'ep_idx' or 'ep_num' for the endpoint number identifiers. | 
|  | * Although struct udc_ep_config uses address to be unambiguous | 
|  | * in its context. | 
|  | */ | 
|  | LOG_DBG("ep 0x%02x halted", cfg->addr); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is called in the context of udc_ep_dequeue() | 
|  | * and must remove all requests from an endpoint queue | 
|  | * Successful removal should be reported to the higher level with | 
|  | * ECONNABORTED as the request result. | 
|  | * It is up to the request owner to clean up or reuse the buffer. | 
|  | */ | 
|  | static int udc_skeleton_ep_dequeue(const struct device *dev, | 
|  | struct udc_ep_config *const cfg) | 
|  | { | 
|  | unsigned int lock_key; | 
|  | struct net_buf *buf; | 
|  |  | 
|  | lock_key = irq_lock(); | 
|  |  | 
|  | buf = udc_buf_get_all(dev, cfg->addr); | 
|  | if (buf) { | 
|  | udc_submit_ep_event(dev, buf, -ECONNABORTED); | 
|  | } | 
|  |  | 
|  | irq_unlock(lock_key); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Configure and make an endpoint ready for use. | 
|  | * This is called in the context of udc_ep_enable() or udc_ep_enable_internal(), | 
|  | * the latter of which may be used by the driver to enable control endpoints. | 
|  | */ | 
|  | static int udc_skeleton_ep_enable(const struct device *dev, | 
|  | struct udc_ep_config *const cfg) | 
|  | { | 
|  | LOG_DBG("Enable ep 0x%02x", cfg->addr); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Opposite function to udc_skeleton_ep_enable(). udc_ep_disable_internal() | 
|  | * may be used by the driver to disable control endpoints. | 
|  | */ | 
|  | static int udc_skeleton_ep_disable(const struct device *dev, | 
|  | struct udc_ep_config *const cfg) | 
|  | { | 
|  | LOG_DBG("Disable ep 0x%02x", cfg->addr); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Halt endpoint. Halted endpoint should respond with a STALL handshake. */ | 
|  | static int udc_skeleton_ep_set_halt(const struct device *dev, | 
|  | struct udc_ep_config *const cfg) | 
|  | { | 
|  | LOG_DBG("Set halt ep 0x%02x", cfg->addr); | 
|  |  | 
|  | cfg->stat.halted = true; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Opposite to halt endpoint. If there are requests in the endpoint queue, | 
|  | * the next transfer should be prepared. | 
|  | */ | 
|  | static int udc_skeleton_ep_clear_halt(const struct device *dev, | 
|  | struct udc_ep_config *const cfg) | 
|  | { | 
|  | LOG_DBG("Clear halt ep 0x%02x", cfg->addr); | 
|  | cfg->stat.halted = false; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int udc_skeleton_set_address(const struct device *dev, const uint8_t addr) | 
|  | { | 
|  | LOG_DBG("Set new address %u for %p", addr, dev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int udc_skeleton_host_wakeup(const struct device *dev) | 
|  | { | 
|  | LOG_DBG("Remote wakeup from %p", dev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Return actual USB device speed */ | 
|  | static enum udc_bus_speed udc_skeleton_device_speed(const struct device *dev) | 
|  | { | 
|  | struct udc_data *data = dev->data; | 
|  |  | 
|  | return data->caps.hs ? UDC_BUS_SPEED_HS : UDC_BUS_SPEED_FS; | 
|  | } | 
|  |  | 
|  | static int udc_skeleton_enable(const struct device *dev) | 
|  | { | 
|  | LOG_DBG("Enable device %p", dev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int udc_skeleton_disable(const struct device *dev) | 
|  | { | 
|  | LOG_DBG("Enable device %p", dev); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Prepare and configure most of the parts, if the controller has a way | 
|  | * of detecting VBUS activity it should be enabled here. | 
|  | * Only udc_skeleton_enable() makes device visible to the host. | 
|  | */ | 
|  | static int udc_skeleton_init(const struct device *dev) | 
|  | { | 
|  | if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, | 
|  | USB_EP_TYPE_CONTROL, 64, 0)) { | 
|  | LOG_ERR("Failed to enable control endpoint"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, | 
|  | USB_EP_TYPE_CONTROL, 64, 0)) { | 
|  | LOG_ERR("Failed to enable control endpoint"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Shut down the controller completely */ | 
|  | static int udc_skeleton_shutdown(const struct device *dev) | 
|  | { | 
|  | if (udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT)) { | 
|  | LOG_ERR("Failed to disable control endpoint"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | if (udc_ep_disable_internal(dev, USB_CONTROL_EP_IN)) { | 
|  | LOG_ERR("Failed to disable control endpoint"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is called once to initialize the controller and endpoints | 
|  | * capabilities, and register endpoint structures. | 
|  | */ | 
|  | static int udc_skeleton_driver_preinit(const struct device *dev) | 
|  | { | 
|  | const struct udc_skeleton_config *config = dev->config; | 
|  | struct udc_data *data = dev->data; | 
|  | uint16_t mps = 1023; | 
|  | int err; | 
|  |  | 
|  | /* | 
|  | * You do not need to initialize it if your driver does not use | 
|  | * udc_lock_internal() / udc_unlock_internal(), but implements its | 
|  | * own mechanism. | 
|  | */ | 
|  | k_mutex_init(&data->mutex); | 
|  |  | 
|  | data->caps.rwup = true; | 
|  | data->caps.mps0 = UDC_MPS0_64; | 
|  | if (config->speed_idx == 2) { | 
|  | data->caps.hs = true; | 
|  | mps = 1024; | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < config->num_of_eps; i++) { | 
|  | config->ep_cfg_out[i].caps.out = 1; | 
|  | if (i == 0) { | 
|  | config->ep_cfg_out[i].caps.control = 1; | 
|  | config->ep_cfg_out[i].caps.mps = 64; | 
|  | } else { | 
|  | config->ep_cfg_out[i].caps.bulk = 1; | 
|  | config->ep_cfg_out[i].caps.interrupt = 1; | 
|  | config->ep_cfg_out[i].caps.iso = 1; | 
|  | config->ep_cfg_out[i].caps.mps = mps; | 
|  | } | 
|  |  | 
|  | config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; | 
|  | err = udc_register_ep(dev, &config->ep_cfg_out[i]); | 
|  | if (err != 0) { | 
|  | LOG_ERR("Failed to register endpoint"); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < config->num_of_eps; i++) { | 
|  | config->ep_cfg_in[i].caps.in = 1; | 
|  | if (i == 0) { | 
|  | config->ep_cfg_in[i].caps.control = 1; | 
|  | config->ep_cfg_in[i].caps.mps = 64; | 
|  | } else { | 
|  | config->ep_cfg_in[i].caps.bulk = 1; | 
|  | config->ep_cfg_in[i].caps.interrupt = 1; | 
|  | config->ep_cfg_in[i].caps.iso = 1; | 
|  | config->ep_cfg_in[i].caps.mps = mps; | 
|  | } | 
|  |  | 
|  | config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i; | 
|  | err = udc_register_ep(dev, &config->ep_cfg_in[i]); | 
|  | if (err != 0) { | 
|  | LOG_ERR("Failed to register endpoint"); | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | config->make_thread(dev); | 
|  | LOG_INF("Device %p (max. speed %d)", dev, config->speed_idx); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int udc_skeleton_lock(const struct device *dev) | 
|  | { | 
|  | return udc_lock_internal(dev, K_FOREVER); | 
|  | } | 
|  |  | 
|  | static int udc_skeleton_unlock(const struct device *dev) | 
|  | { | 
|  | return udc_unlock_internal(dev); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * UDC API structure. | 
|  | * Note, you do not need to implement basic checks, these are done by | 
|  | * the UDC common layer udc_common.c | 
|  | */ | 
|  | static const struct udc_api udc_skeleton_api = { | 
|  | .lock = udc_skeleton_lock, | 
|  | .unlock = udc_skeleton_unlock, | 
|  | .device_speed = udc_skeleton_device_speed, | 
|  | .init = udc_skeleton_init, | 
|  | .enable = udc_skeleton_enable, | 
|  | .disable = udc_skeleton_disable, | 
|  | .shutdown = udc_skeleton_shutdown, | 
|  | .set_address = udc_skeleton_set_address, | 
|  | .host_wakeup = udc_skeleton_host_wakeup, | 
|  | .ep_enable = udc_skeleton_ep_enable, | 
|  | .ep_disable = udc_skeleton_ep_disable, | 
|  | .ep_set_halt = udc_skeleton_ep_set_halt, | 
|  | .ep_clear_halt = udc_skeleton_ep_clear_halt, | 
|  | .ep_enqueue = udc_skeleton_ep_enqueue, | 
|  | .ep_dequeue = udc_skeleton_ep_dequeue, | 
|  | }; | 
|  |  | 
|  | #define DT_DRV_COMPAT zephyr_udc_skeleton | 
|  |  | 
|  | /* | 
|  | * A UDC driver should always be implemented as a multi-instance | 
|  | * driver, even if your platform does not require it. | 
|  | */ | 
|  | #define UDC_SKELETON_DEVICE_DEFINE(n)						\ | 
|  | K_THREAD_STACK_DEFINE(udc_skeleton_stack_##n, CONFIG_UDC_SKELETON);	\ | 
|  | \ | 
|  | static void udc_skeleton_thread_##n(void *dev, void *arg1, void *arg2)	\ | 
|  | {									\ | 
|  | skeleton_thread_handler(dev);					\ | 
|  | }									\ | 
|  | \ | 
|  | static void udc_skeleton_make_thread_##n(const struct device *dev)	\ | 
|  | {									\ | 
|  | struct udc_skeleton_data *priv = udc_get_private(dev);		\ | 
|  | \ | 
|  | k_thread_create(&priv->thread_data,				\ | 
|  | udc_skeleton_stack_##n,				\ | 
|  | K_THREAD_STACK_SIZEOF(udc_skeleton_stack_##n),	\ | 
|  | udc_skeleton_thread_##n,			\ | 
|  | (void *)dev, NULL, NULL,			\ | 
|  | K_PRIO_COOP(CONFIG_UDC_SKELETON_THREAD_PRIORITY),\ | 
|  | K_ESSENTIAL,					\ | 
|  | K_NO_WAIT);					\ | 
|  | k_thread_name_set(&priv->thread_data, dev->name);		\ | 
|  | }									\ | 
|  | \ | 
|  | static struct udc_ep_config						\ | 
|  | ep_cfg_out[DT_INST_PROP(n, num_bidir_endpoints)];		\ | 
|  | static struct udc_ep_config						\ | 
|  | ep_cfg_in[DT_INST_PROP(n, num_bidir_endpoints)];		\ | 
|  | \ | 
|  | static const struct udc_skeleton_config udc_skeleton_config_##n = {	\ | 
|  | .num_of_eps = DT_INST_PROP(n, num_bidir_endpoints),		\ | 
|  | .ep_cfg_in = ep_cfg_out,					\ | 
|  | .ep_cfg_out = ep_cfg_in,					\ | 
|  | .make_thread = udc_skeleton_make_thread_##n,			\ | 
|  | .speed_idx = DT_ENUM_IDX(DT_DRV_INST(n), maximum_speed),	\ | 
|  | };									\ | 
|  | \ | 
|  | static struct udc_skeleton_data udc_priv_##n = {			\ | 
|  | };									\ | 
|  | \ | 
|  | static struct udc_data udc_data_##n = {					\ | 
|  | .mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex),		\ | 
|  | .priv = &udc_priv_##n,						\ | 
|  | };									\ | 
|  | \ | 
|  | DEVICE_DT_INST_DEFINE(n, udc_skeleton_driver_preinit, NULL,		\ | 
|  | &udc_data_##n, &udc_skeleton_config_##n,		\ | 
|  | POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,	\ | 
|  | &udc_skeleton_api); | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(UDC_SKELETON_DEVICE_DEFINE) |