The init infrastructure, found in `init.h`, is currently used by:
- `SYS_INIT`: to call functions before `main`
- `DEVICE_*`: to initialize devices
They are all sorted according to an initialization level + a priority.
`SYS_INIT` calls are really orthogonal to devices, however, the required
function signature requires a `const struct device *dev` as a first
argument. The only reason for that is because the same init machinery is
used by devices, so we have something like:
```c
struct init_entry {
int (*init)(const struct device *dev);
/* only set by DEVICE_*, otherwise NULL */
const struct device *dev;
}
```
As a result, we end up with such weird/ugly pattern:
```c
static int my_init(const struct device *dev)
{
/* always NULL! add ARG_UNUSED to avoid compiler warning */
ARG_UNUSED(dev);
...
}
```
This is really a result of poor internals isolation. This patch proposes
a to make init entries more flexible so that they can accept sytem
initialization calls like this:
```c
static int my_init(void)
{
...
}
```
This is achieved using a union:
```c
union init_function {
/* for SYS_INIT, used when init_entry.dev == NULL */
int (*sys)(void);
/* for DEVICE*, used when init_entry.dev != NULL */
int (*dev)(const struct device *dev);
};
struct init_entry {
/* stores init function (either for SYS_INIT or DEVICE*)
union init_function init_fn;
/* stores device pointer for DEVICE*, NULL for SYS_INIT. Allows
* to know which union entry to call.
*/
const struct device *dev;
}
```
This solution **does not increase ROM usage**, and allows to offer clean
public APIs for both SYS_INIT and DEVICE*. Note that however, init
machinery keeps a coupling with devices.
**NOTE**: This is a breaking change! All `SYS_INIT` functions will need
to be converted to the new signature. See the script offered in the
following commit.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
init: convert SYS_INIT functions to the new signature
Conversion scripted using scripts/utils/migrate_sys_init.py.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
manifest: update projects for SYS_INIT changes
Update modules with updated SYS_INIT calls:
- hal_ti
- lvgl
- sof
- TraceRecorderSource
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
tests: devicetree: devices: adjust test
Adjust test according to the recently introduced SYS_INIT
infrastructure.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
tests: kernel: threads: adjust SYS_INIT call
Adjust to the new signature: int (*init_fn)(void);
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
225 lines
4.9 KiB
C
225 lines
4.9 KiB
C
/*
|
|
* Copyright (c) 2022 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/toolchain/common.h>
|
|
#include <zephyr/sys/slist.h>
|
|
|
|
#include <zephyr/drivers/usb/udc.h>
|
|
#include <zephyr/usb/usbd.h>
|
|
|
|
#include "usbd_device.h"
|
|
#include "usbd_config.h"
|
|
#include "usbd_init.h"
|
|
#include "usbd_ch9.h"
|
|
#include "usbd_class.h"
|
|
#include "usbd_class_api.h"
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(usbd_core, CONFIG_USBD_LOG_LEVEL);
|
|
|
|
static K_KERNEL_STACK_DEFINE(usbd_stack, CONFIG_USBD_THREAD_STACK_SIZE);
|
|
static struct k_thread usbd_thread_data;
|
|
|
|
K_MSGQ_DEFINE(usbd_msgq, sizeof(struct udc_event),
|
|
CONFIG_USBD_MAX_UDC_MSG, sizeof(uint32_t));
|
|
|
|
static int usbd_event_carrier(const struct device *dev,
|
|
const struct udc_event *const event)
|
|
{
|
|
return k_msgq_put(&usbd_msgq, event, K_NO_WAIT);
|
|
}
|
|
|
|
static int event_handler_ep_request(struct usbd_contex *const uds_ctx,
|
|
const struct udc_event *const event)
|
|
{
|
|
struct udc_buf_info *bi;
|
|
int ret;
|
|
|
|
bi = udc_get_buf_info(event->buf);
|
|
|
|
if (USB_EP_GET_IDX(bi->ep) == 0) {
|
|
ret = usbd_handle_ctrl_xfer(uds_ctx, event->buf, bi->err);
|
|
} else {
|
|
ret = usbd_class_handle_xfer(uds_ctx, event->buf, bi->err);
|
|
}
|
|
|
|
if (ret) {
|
|
LOG_ERR("unrecoverable error %d, ep 0x%02x, buf %p",
|
|
ret, bi->ep, event->buf);
|
|
/* TODO: Shutdown USB device gracefully */
|
|
k_panic();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void usbd_class_bcast_event(struct usbd_contex *const uds_ctx,
|
|
struct udc_event *const event)
|
|
{
|
|
struct usbd_config_node *cfg_nd;
|
|
struct usbd_class_node *c_nd;
|
|
|
|
if (!usbd_state_is_configured(uds_ctx)) {
|
|
return;
|
|
}
|
|
|
|
cfg_nd = usbd_config_get_current(uds_ctx);
|
|
if (cfg_nd == NULL) {
|
|
LOG_ERR("Failed to get cfg_nd, despite configured state");
|
|
return;
|
|
}
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&cfg_nd->class_list, c_nd, node) {
|
|
switch (event->type) {
|
|
case UDC_EVT_SUSPEND:
|
|
usbd_class_suspended(c_nd);
|
|
break;
|
|
case UDC_EVT_RESUME:
|
|
usbd_class_resumed(c_nd);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int event_handler_bus_reset(struct usbd_contex *const uds_ctx)
|
|
{
|
|
int ret;
|
|
|
|
LOG_WRN("Bus reset event");
|
|
|
|
usbd_status_suspended(uds_ctx, false);
|
|
ret = udc_set_address(uds_ctx->dev, 0);
|
|
if (ret) {
|
|
LOG_ERR("Failed to set default address after bus reset");
|
|
return ret;
|
|
}
|
|
|
|
ret = usbd_config_set(uds_ctx, 0);
|
|
if (ret) {
|
|
LOG_ERR("Failed to set default state after bus reset");
|
|
return ret;
|
|
}
|
|
|
|
/* There might be pending data stage transfer */
|
|
if (usbd_ep_dequeue(uds_ctx, USB_CONTROL_EP_IN)) {
|
|
LOG_ERR("Failed to dequeue control IN");
|
|
}
|
|
|
|
LOG_INF("Actual device speed %d", udc_device_speed(uds_ctx->dev));
|
|
uds_ctx->ch9_data.state = USBD_STATE_DEFAULT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* TODO: Add event broadcaster to user application */
|
|
static int ALWAYS_INLINE usbd_event_handler(struct usbd_contex *const uds_ctx,
|
|
struct udc_event *const event)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (event->type) {
|
|
case UDC_EVT_VBUS_REMOVED:
|
|
LOG_WRN("VBUS remove event");
|
|
break;
|
|
case UDC_EVT_VBUS_READY:
|
|
LOG_WRN("VBUS detected event");
|
|
break;
|
|
case UDC_EVT_SUSPEND:
|
|
LOG_WRN("SUSPEND event");
|
|
usbd_status_suspended(uds_ctx, true);
|
|
usbd_class_bcast_event(uds_ctx, event);
|
|
break;
|
|
case UDC_EVT_RESUME:
|
|
LOG_WRN("RESUME event");
|
|
usbd_status_suspended(uds_ctx, false);
|
|
usbd_class_bcast_event(uds_ctx, event);
|
|
break;
|
|
case UDC_EVT_SOF:
|
|
usbd_class_bcast_event(uds_ctx, event);
|
|
break;
|
|
case UDC_EVT_RESET:
|
|
LOG_WRN("RESET event");
|
|
ret = event_handler_bus_reset(uds_ctx);
|
|
break;
|
|
case UDC_EVT_EP_REQUEST:
|
|
ret = event_handler_ep_request(uds_ctx, event);
|
|
break;
|
|
case UDC_EVT_ERROR:
|
|
LOG_ERR("Error event");
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void usbd_thread(void)
|
|
{
|
|
struct udc_event event;
|
|
|
|
while (true) {
|
|
k_msgq_get(&usbd_msgq, &event, K_FOREVER);
|
|
|
|
STRUCT_SECTION_FOREACH(usbd_contex, uds_ctx) {
|
|
if (uds_ctx->dev == event.dev) {
|
|
usbd_event_handler(uds_ctx, &event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int usbd_device_init_core(struct usbd_contex *const uds_ctx)
|
|
{
|
|
int ret;
|
|
|
|
ret = udc_init(uds_ctx->dev, usbd_event_carrier);
|
|
if (ret != 0) {
|
|
LOG_ERR("Failed to init device driver");
|
|
return ret;
|
|
}
|
|
|
|
usbd_set_config_value(uds_ctx, 0);
|
|
|
|
ret = usbd_init_configurations(uds_ctx);
|
|
if (ret != 0) {
|
|
udc_shutdown(uds_ctx->dev);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int usbd_device_shutdown_core(struct usbd_contex *const uds_ctx)
|
|
{
|
|
return udc_shutdown(uds_ctx->dev);
|
|
}
|
|
|
|
static int usbd_pre_init(void)
|
|
{
|
|
k_thread_create(&usbd_thread_data, usbd_stack,
|
|
K_KERNEL_STACK_SIZEOF(usbd_stack),
|
|
(k_thread_entry_t)usbd_thread,
|
|
NULL, NULL, NULL,
|
|
K_PRIO_COOP(8), 0, K_NO_WAIT);
|
|
|
|
k_thread_name_set(&usbd_thread_data, "usbd");
|
|
|
|
LOG_DBG("Available USB class nodes:");
|
|
STRUCT_SECTION_FOREACH(usbd_class_node, node) {
|
|
atomic_set(&node->data->state, 0);
|
|
LOG_DBG("\t%p, name %s", node, node->name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(usbd_pre_init, POST_KERNEL, CONFIG_USBD_THREAD_INIT_PRIO);
|