blob: c836c271ed37f44981ed297b9ddd3aa46c7e5eeb [file] [log] [blame]
/*
* 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.
*/
int usbd_device_set_bcd(struct usbd_contex *const uds_ctx,
const uint16_t bcd)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_bcd_exit;
}
desc->bcdUSB = sys_cpu_to_le16(bcd);
set_bcd_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_vid(struct usbd_contex *const uds_ctx,
const uint16_t vid)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_vid_exit;
}
desc->idVendor = sys_cpu_to_le16(vid);
set_vid_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_pid(struct usbd_contex *const uds_ctx,
const uint16_t pid)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_pid_exit;
}
desc->idProduct = sys_cpu_to_le16(pid);
set_pid_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_class(struct usbd_contex *const uds_ctx,
const uint8_t value)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_class_exit;
}
desc->bDeviceClass = value;
set_class_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_subclass(struct usbd_contex *const uds_ctx,
const uint8_t value)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_subclass_exit;
}
desc->bDeviceSubClass = value;
set_subclass_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_device_set_proto(struct usbd_contex *const uds_ctx,
const uint8_t value)
{
struct usb_device_descriptor *desc = uds_ctx->desc;
int ret = 0;
usbd_device_lock(uds_ctx);
if (usbd_is_enabled(uds_ctx)) {
ret = -EALREADY;
goto set_proto_exit;
}
desc->bDeviceProtocol = value;
set_proto_exit:
usbd_device_unlock(uds_ctx);
return ret;
}
int usbd_wakeup_request(struct usbd_contex *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_ERR("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_contex *uds_ctx)
{
return uds_ctx->status.suspended;
}
int usbd_init(struct usbd_contex *const uds_ctx)
{
int ret;
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);
return ret;
}
int usbd_enable(struct usbd_contex *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_contex *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_contex *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;
}