293 lines
5.3 KiB
C
293 lines
5.3 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.
|
|
*/
|
|
|
|
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;
|
|
}
|