zephyr/subsys/usb/device_next/usbd_device.c

337 lines
6.7 KiB
C

/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/usb/udc.h>
#include <zephyr/usb/usbd.h>
#include "usbd_device.h"
#include "usbd_config.h"
#include "usbd_class.h"
#include "usbd_ch9.h"
#include "usbd_desc.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(usbd_dev, CONFIG_USBD_LOG_LEVEL);
/*
* All the functions below are part of public USB device support API.
*/
enum usbd_speed usbd_bus_speed(const struct usbd_context *const uds_ctx)
{
return uds_ctx->status.speed;
}
enum usbd_speed usbd_caps_speed(const struct usbd_context *const uds_ctx)
{
struct udc_device_caps caps = udc_caps(uds_ctx->dev);
/* For now, either high speed is supported or not. */
if (caps.hs) {
return USBD_SPEED_HS;
}
return USBD_SPEED_FS;
}
static struct usb_device_descriptor *
get_device_descriptor(struct usbd_context *const uds_ctx,
const enum usbd_speed speed)
{
switch (speed) {
case USBD_SPEED_FS:
return uds_ctx->fs_desc;
case USBD_SPEED_HS:
return uds_ctx->hs_desc;
default:
__ASSERT(false, "Not supported speed");
return NULL;
}
}
int usbd_device_set_bcd_usb(struct usbd_context *const uds_ctx,
const enum usbd_speed speed, const uint16_t bcd)
{
struct usb_device_descriptor *desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_bcd_exit;
}
desc = get_device_descriptor(uds_ctx, speed);
desc->bcdUSB = sys_cpu_to_le16(bcd);
set_bcd_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_vid(struct usbd_context *const uds_ctx,
const uint16_t vid)
{
struct usb_device_descriptor *fs_desc, *hs_desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_vid_exit;
}
fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
fs_desc->idVendor = sys_cpu_to_le16(vid);
hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
hs_desc->idVendor = sys_cpu_to_le16(vid);
set_vid_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_pid(struct usbd_context *const uds_ctx,
const uint16_t pid)
{
struct usb_device_descriptor *fs_desc, *hs_desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_pid_exit;
}
fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
fs_desc->idProduct = sys_cpu_to_le16(pid);
hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
hs_desc->idProduct = sys_cpu_to_le16(pid);
set_pid_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_bcd_device(struct usbd_context *const uds_ctx,
const uint16_t bcd)
{
struct usb_device_descriptor *fs_desc, *hs_desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_bcd_device_exit;
}
fs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_FS);
fs_desc->bcdDevice = sys_cpu_to_le16(bcd);
hs_desc = get_device_descriptor(uds_ctx, USBD_SPEED_HS);
hs_desc->bcdDevice = sys_cpu_to_le16(bcd);
set_bcd_device_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_code_triple(struct usbd_context *const uds_ctx,
const enum usbd_speed speed,
const uint8_t base_class,
const uint8_t subclass, const uint8_t protocol)
{
struct usb_device_descriptor *desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_code_triple_exit;
}
desc = get_device_descriptor(uds_ctx, speed);
desc->bDeviceClass = base_class;
desc->bDeviceSubClass = subclass;
desc->bDeviceProtocol = protocol;
set_code_triple_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_wakeup_request(struct usbd_context *const uds_ctx)
{
struct udc_device_caps caps = udc_caps(uds_ctx->dev);
int ret = 0;
usbd_device_lock(uds_ctx);
if (!caps.rwup) {
LOG_ERR("Remote wakeup feature not supported");
ret = -ENOTSUP;
goto wakeup_request_error;
}
if (!uds_ctx->status.rwup || !usbd_is_suspended(uds_ctx)) {
LOG_WRN("Remote wakeup feature not enabled or not suspended");
ret = -EACCES;
goto wakeup_request_error;
}
ret = udc_host_wakeup(uds_ctx->dev);
wakeup_request_error:
usbd_device_unlock(uds_ctx);
return ret;
}
bool usbd_is_suspended(struct usbd_context *uds_ctx)
{
return uds_ctx->status.suspended;
}
int usbd_init(struct usbd_context *const uds_ctx)
{
int ret;
/*
* Lock the scheduler to ensure that the context is not preempted
* before it is fully initialized.
*/
k_sched_lock();
usbd_device_lock(uds_ctx);
if (uds_ctx->dev == NULL) {
ret = -ENODEV;
goto init_exit;
}
if (usbd_is_initialized(uds_ctx)) {
LOG_WRN("USB device support is already initialized");
ret = -EALREADY;
goto init_exit;
}
if (!device_is_ready(uds_ctx->dev)) {
LOG_ERR("USB device controller is not ready");
ret = -ENODEV;
goto init_exit;
}
ret = usbd_device_init_core(uds_ctx);
if (ret) {
goto init_exit;
}
memset(&uds_ctx->ch9_data, 0, sizeof(struct usbd_ch9_data));
uds_ctx->status.initialized = true;
init_exit:
usbd_device_unlock(uds_ctx);
k_sched_unlock();
return ret;
}
int usbd_enable(struct usbd_context *const uds_ctx)
{
int ret;
usbd_device_lock(uds_ctx);
if (!usbd_is_initialized(uds_ctx)) {
LOG_WRN("USB device support is not initialized");
ret = -EPERM;
goto enable_exit;
}
if (usbd_is_enabled(uds_ctx)) {
LOG_WRN("USB device support is already enabled");
ret = -EALREADY;
goto enable_exit;
}
ret = udc_enable(uds_ctx->dev);
if (ret != 0) {
LOG_ERR("Failed to enable controller");
goto enable_exit;
}
ret = usbd_init_control_pipe(uds_ctx);
if (ret != 0) {
udc_disable(uds_ctx->dev);
goto enable_exit;
}
uds_ctx->status.enabled = true;
enable_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_disable(struct usbd_context *const uds_ctx)
{
int ret;
if (!usbd_is_enabled(uds_ctx)) {
LOG_WRN("USB device support is already disabled");
return -EALREADY;
}
usbd_device_lock(uds_ctx);
ret = usbd_config_set(uds_ctx, 0);
if (ret) {
LOG_ERR("Failed to reset configuration");
}
ret = udc_disable(uds_ctx->dev);
if (ret) {
LOG_ERR("Failed to disable USB device");
}
uds_ctx->status.enabled = false;
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_shutdown(struct usbd_context *const uds_ctx)
{
int ret;
usbd_device_lock(uds_ctx);
/* TODO: control request dequeue ? */
ret = usbd_device_shutdown_core(uds_ctx);
if (ret) {
LOG_ERR("Failed to shutdown USB device");
}
uds_ctx->status.initialized = false;
usbd_device_unlock(uds_ctx);
return 0;
}
bool usbd_can_detect_vbus(struct usbd_context *const uds_ctx)
{
const struct udc_device_caps caps = udc_caps(uds_ctx->dev);
return caps.can_detect_vbus;
}