/* * Copyright (c) 2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "usbd_device.h" #include "usbd_config.h" #include "usbd_class.h" #include "usbd_ch9.h" #include "usbd_desc.h" #include 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(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_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; }