usb: add initial USB host support
This is initial support. Necessary to test the UHC driver API, for the USB support test implementations, and upcoming USBIP support. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
This commit is contained in:
parent
bb7ab1e0f2
commit
7c1ef35027
161
include/zephyr/usb/usbh.h
Normal file
161
include/zephyr/usb/usbh.h
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief New experimental USB device stack APIs and structures
|
||||
*
|
||||
* This file contains the USB device stack APIs and structures.
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBH_H_
|
||||
#define ZEPHYR_INCLUDE_USBH_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/net/buf.h>
|
||||
#include <zephyr/sys/dlist.h>
|
||||
#include <zephyr/drivers/usb/uhc.h>
|
||||
#include <zephyr/linker/linker-defs.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USB HOST Core Layer API
|
||||
* @defgroup _usb_host_core_api USB Host Core API
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* USB host support runtime context
|
||||
*/
|
||||
struct usbh_contex {
|
||||
/** Name of the USB device */
|
||||
const char *name;
|
||||
/** Access mutex */
|
||||
struct k_mutex mutex;
|
||||
/** Pointer to UHC device struct */
|
||||
const struct device *dev;
|
||||
/** peripheral list */
|
||||
sys_dlist_t peripherals;
|
||||
};
|
||||
|
||||
#define USBH_CONTROLLER_DEFINE(device_name, uhc_dev) \
|
||||
static STRUCT_SECTION_ITERABLE(usbh_contex, device_name) = { \
|
||||
.name = STRINGIFY(device_name), \
|
||||
.mutex = Z_MUTEX_INITIALIZER(device_name.mutex), \
|
||||
.dev = uhc_dev, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB host peripheral structure
|
||||
*/
|
||||
struct usbh_peripheral {
|
||||
/** Peripheral dlist node */
|
||||
sys_dnode_t node;
|
||||
/** Peripheral address */
|
||||
uint8_t addr;
|
||||
/** Detected speed (TBD) */
|
||||
uint8_t speed;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class Code
|
||||
*/
|
||||
struct usbh_class_code {
|
||||
/** Device Class Code */
|
||||
uint8_t dclass;
|
||||
/** Class Subclass Code */
|
||||
uint8_t sub;
|
||||
/** Class Protocol Code */
|
||||
uint8_t proto;
|
||||
/** Reserved */
|
||||
uint8_t reserved;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief USB host class data and class instance API
|
||||
*/
|
||||
struct usbh_class_data {
|
||||
/** Class code supported by this instance */
|
||||
struct usbh_class_code code;
|
||||
|
||||
/** Initialization of the class implementation */
|
||||
/* int (*init)(struct usbh_contex *const uhs_ctx); */
|
||||
/** Request completion event handler */
|
||||
int (*request)(struct usbh_contex *const uhs_ctx,
|
||||
struct uhc_transfer *const xfer, int err);
|
||||
/** Device connected handler */
|
||||
int (*connected)(struct usbh_contex *const uhs_ctx);
|
||||
/** Device removed handler */
|
||||
int (*removed)(struct usbh_contex *const uhs_ctx);
|
||||
/** Bus remote wakeup handler */
|
||||
int (*rwup)(struct usbh_contex *const uhs_ctx);
|
||||
/** Bus suspended handler */
|
||||
int (*suspended)(struct usbh_contex *const uhs_ctx);
|
||||
/** Bus resumed handler */
|
||||
int (*resumed)(struct usbh_contex *const uhs_ctx);
|
||||
};
|
||||
|
||||
/**
|
||||
*/
|
||||
#define USBH_DEFINE_CLASS(name) \
|
||||
static STRUCT_SECTION_ITERABLE(usbh_class_data, name)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize the USB host support;
|
||||
*
|
||||
* @param[in] uhs_ctx Pointer to USB host support context
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbh_init(struct usbh_contex *uhs_ctx);
|
||||
|
||||
/**
|
||||
* @brief Enable the USB host support and class instances
|
||||
*
|
||||
* This function enables the USB host support.
|
||||
*
|
||||
* @param[in] uhs_ctx Pointer to USB host support context
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbh_enable(struct usbh_contex *uhs_ctx);
|
||||
|
||||
/**
|
||||
* @brief Disable the USB host support
|
||||
*
|
||||
* This function disables the USB host support.
|
||||
*
|
||||
* @param[in] uhs_ctx Pointer to USB host support context
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbh_disable(struct usbh_contex *uhs_ctx);
|
||||
|
||||
/**
|
||||
* @brief Shutdown the USB host support
|
||||
*
|
||||
* This function completely disables the USB host support.
|
||||
*
|
||||
* @param[in] uhs_ctx Pointer to USB host support context
|
||||
*
|
||||
* @return 0 on success, other values on fail.
|
||||
*/
|
||||
int usbh_shutdown(struct usbh_contex *const uhs_ctx);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBH_H_ */
|
|
@ -56,6 +56,8 @@ source "subsys/sd/Kconfig"
|
|||
|
||||
source "subsys/usb/device_next/Kconfig"
|
||||
|
||||
source "subsys/usb/host/Kconfig"
|
||||
|
||||
source "subsys/dfu/Kconfig"
|
||||
|
||||
source "subsys/random/Kconfig"
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
|
||||
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK device)
|
||||
add_subdirectory_ifdef(CONFIG_USB_DEVICE_STACK_NEXT device_next)
|
||||
add_subdirectory_ifdef(CONFIG_USB_HOST_STACK host)
|
||||
add_subdirectory_ifdef(CONFIG_USBC_STACK usb_c)
|
||||
|
|
18
subsys/usb/host/CMakeLists.txt
Normal file
18
subsys/usb/host/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
zephyr_library_sources(
|
||||
usbh_ch9.c
|
||||
usbh_core.c
|
||||
usbh_api.c
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_USBH_SHELL
|
||||
usbh_shell.c
|
||||
)
|
||||
|
||||
zephyr_linker_sources(DATA_SECTIONS usbh_data.ld)
|
43
subsys/usb/host/Kconfig
Normal file
43
subsys/usb/host/Kconfig
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig USB_HOST_STACK
|
||||
bool "USB host stack [EXPERIMENTAL]"
|
||||
select EXPERIMENTAL
|
||||
select UHC_DRIVER
|
||||
help
|
||||
New experimental USB host stack.
|
||||
|
||||
if USB_HOST_STACK
|
||||
|
||||
module = USBH
|
||||
module-str = usbh
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
config USBH_SHELL
|
||||
bool "USB host shell"
|
||||
default y
|
||||
depends on SHELL
|
||||
help
|
||||
Shell commands for USB host support.
|
||||
|
||||
config USBH_INIT_PRIO
|
||||
int
|
||||
default 90
|
||||
help
|
||||
USB host thread initialization priority level.
|
||||
|
||||
config USBH_STACK_SIZE
|
||||
int "USB host stack thread stack size"
|
||||
default 1024
|
||||
help
|
||||
USB host stack thread stack size in bytes.
|
||||
|
||||
config USBH_MAX_UHC_MSG
|
||||
int "Maximum number of UHC events"
|
||||
default 10
|
||||
help
|
||||
Maximum number of USB host controller events that can be queued.
|
||||
|
||||
endif # USB_HOST_STACK
|
103
subsys/usb/host/usbh_api.c
Normal file
103
subsys/usb/host/usbh_api.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include "usbh_internal.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(uhs_api, CONFIG_USBH_LOG_LEVEL);
|
||||
|
||||
int usbh_init(struct usbh_contex *uhs_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
k_mutex_lock(&uhs_ctx->mutex, K_FOREVER);
|
||||
|
||||
if (!device_is_ready(uhs_ctx->dev)) {
|
||||
LOG_ERR("USB host controller is not ready");
|
||||
ret = -ENODEV;
|
||||
goto init_exit;
|
||||
}
|
||||
|
||||
if (uhc_is_initialized(uhs_ctx->dev)) {
|
||||
LOG_WRN("USB host controller is already initialized");
|
||||
ret = -EALREADY;
|
||||
goto init_exit;
|
||||
}
|
||||
|
||||
ret = usbh_init_device_intl(uhs_ctx);
|
||||
|
||||
init_exit:
|
||||
k_mutex_unlock(&uhs_ctx->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbh_enable(struct usbh_contex *uhs_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
k_mutex_lock(&uhs_ctx->mutex, K_FOREVER);
|
||||
|
||||
if (!uhc_is_initialized(uhs_ctx->dev)) {
|
||||
LOG_WRN("USB host controller is not initialized");
|
||||
ret = -EPERM;
|
||||
goto enable_exit;
|
||||
}
|
||||
|
||||
if (uhc_is_enabled(uhs_ctx->dev)) {
|
||||
LOG_WRN("USB host controller is already enabled");
|
||||
ret = -EALREADY;
|
||||
goto enable_exit;
|
||||
}
|
||||
|
||||
ret = uhc_enable(uhs_ctx->dev);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to enable controller");
|
||||
goto enable_exit;
|
||||
}
|
||||
|
||||
enable_exit:
|
||||
k_mutex_unlock(&uhs_ctx->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbh_disable(struct usbh_contex *uhs_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!uhc_is_enabled(uhs_ctx->dev)) {
|
||||
LOG_WRN("USB host controller is already disabled");
|
||||
return 0;
|
||||
}
|
||||
|
||||
k_mutex_lock(&uhs_ctx->mutex, K_FOREVER);
|
||||
|
||||
ret = uhc_disable(uhs_ctx->dev);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to disable USB controller");
|
||||
}
|
||||
|
||||
k_mutex_unlock(&uhs_ctx->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usbh_shutdown(struct usbh_contex *const uhs_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
k_mutex_lock(&uhs_ctx->mutex, K_FOREVER);
|
||||
|
||||
ret = uhc_shutdown(uhs_ctx->dev);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to shutdown USB device");
|
||||
}
|
||||
|
||||
k_mutex_unlock(&uhs_ctx->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
172
subsys/usb/host/usbh_ch9.c
Normal file
172
subsys/usb/host/usbh_ch9.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/drivers/usb/uhc.h>
|
||||
#include <zephyr/usb/usb_ch9.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/net/buf.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbh_ch9, CONFIG_USBH_LOG_LEVEL);
|
||||
|
||||
#define SETUP_REQ_TIMEOUT 1000U
|
||||
|
||||
int usbh_req_setup(const struct device *dev,
|
||||
const uint8_t addr,
|
||||
const uint8_t bmRequestType,
|
||||
const uint8_t bRequest,
|
||||
const uint16_t wValue,
|
||||
const uint16_t wIndex,
|
||||
const uint16_t wLength,
|
||||
uint8_t *const data)
|
||||
{
|
||||
struct usb_setup_packet req = {
|
||||
.bmRequestType = bmRequestType,
|
||||
.bRequest = bRequest,
|
||||
.wValue = sys_cpu_to_le16(wValue),
|
||||
.wIndex = sys_cpu_to_le16(wIndex),
|
||||
.wLength = sys_cpu_to_le16(wLength),
|
||||
};
|
||||
struct uhc_transfer *xfer;
|
||||
struct net_buf *buf;
|
||||
uint8_t ep = usb_reqtype_is_to_device(&req) ? 0x00 : 0x80;
|
||||
int ret;
|
||||
|
||||
xfer = uhc_xfer_alloc(dev, addr, ep, 0, 64, SETUP_REQ_TIMEOUT, NULL);
|
||||
if (!xfer) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf = uhc_xfer_buf_alloc(dev, xfer, sizeof(req));
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto buf_alloc_err;
|
||||
}
|
||||
|
||||
net_buf_add_mem(buf, &req, sizeof(req));
|
||||
|
||||
if (wLength) {
|
||||
buf = uhc_xfer_buf_alloc(dev, xfer, wLength);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto buf_alloc_err;
|
||||
}
|
||||
|
||||
if (usb_reqtype_is_to_device(&req) && data != NULL) {
|
||||
net_buf_add_mem(buf, data, wLength);
|
||||
}
|
||||
}
|
||||
|
||||
buf = uhc_xfer_buf_alloc(dev, xfer, 0);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto buf_alloc_err;
|
||||
}
|
||||
|
||||
return uhc_ep_enqueue(dev, xfer);
|
||||
|
||||
buf_alloc_err:
|
||||
uhc_xfer_free(dev, xfer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbh_req_desc(const struct device *dev,
|
||||
const uint8_t addr,
|
||||
const uint8_t type, const uint8_t index,
|
||||
const uint8_t id,
|
||||
const uint16_t len)
|
||||
{
|
||||
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_HOST << 7;
|
||||
const uint8_t bRequest = USB_SREQ_GET_DESCRIPTOR;
|
||||
const uint16_t wValue = (type << 8) | index;
|
||||
|
||||
return usbh_req_setup(dev, addr,
|
||||
bmRequestType, bRequest, wValue, id, len,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int usbh_req_desc_dev(const struct device *dev,
|
||||
const uint8_t addr)
|
||||
{
|
||||
const uint8_t type = USB_DESC_DEVICE;
|
||||
const uint16_t wLength = 18;
|
||||
|
||||
return usbh_req_desc(dev, addr, type, 0, 0, wLength);
|
||||
}
|
||||
|
||||
int usbh_req_desc_cfg(const struct device *dev,
|
||||
const uint8_t addr,
|
||||
const uint8_t index,
|
||||
const uint16_t len)
|
||||
{
|
||||
const uint8_t type = USB_DESC_CONFIGURATION;
|
||||
|
||||
return usbh_req_desc(dev, addr, type, index, 0, len);
|
||||
}
|
||||
|
||||
int usbh_req_set_address(const struct device *dev,
|
||||
const uint8_t addr, const uint8_t new)
|
||||
{
|
||||
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7;
|
||||
const uint8_t bRequest = USB_SREQ_SET_ADDRESS;
|
||||
|
||||
return usbh_req_setup(dev, addr,
|
||||
bmRequestType, bRequest, new, 0, 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int usbh_req_set_cfg(const struct device *dev,
|
||||
const uint8_t addr, const uint8_t new)
|
||||
{
|
||||
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7;
|
||||
const uint8_t bRequest = USB_SREQ_SET_CONFIGURATION;
|
||||
|
||||
return usbh_req_setup(dev, addr,
|
||||
bmRequestType, bRequest, new, 0, 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int usbh_req_set_alt(const struct device *dev,
|
||||
const uint8_t addr, const uint8_t iface,
|
||||
const uint8_t alt)
|
||||
{
|
||||
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7 |
|
||||
USB_REQTYPE_RECIPIENT_INTERFACE;
|
||||
const uint8_t bRequest = USB_SREQ_SET_INTERFACE;
|
||||
const uint16_t wValue = alt;
|
||||
const uint16_t wIndex = iface;
|
||||
|
||||
return usbh_req_setup(dev, addr,
|
||||
bmRequestType, bRequest, wValue, wIndex, 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int usbh_req_set_sfs_rwup(const struct device *dev,
|
||||
const uint8_t addr)
|
||||
{
|
||||
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7;
|
||||
const uint8_t bRequest = USB_SREQ_SET_FEATURE;
|
||||
const uint16_t wValue = USB_SFS_REMOTE_WAKEUP;
|
||||
|
||||
return usbh_req_setup(dev, addr,
|
||||
bmRequestType, bRequest, wValue, 0, 0,
|
||||
NULL);
|
||||
}
|
||||
|
||||
int usbh_req_clear_sfs_rwup(const struct device *dev,
|
||||
const uint8_t addr)
|
||||
{
|
||||
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7;
|
||||
const uint8_t bRequest = USB_SREQ_CLEAR_FEATURE;
|
||||
const uint16_t wValue = USB_SFS_REMOTE_WAKEUP;
|
||||
|
||||
return usbh_req_setup(dev, addr,
|
||||
bmRequestType, bRequest, wValue, 0, 0,
|
||||
NULL);
|
||||
}
|
52
subsys/usb/host/usbh_ch9.h
Normal file
52
subsys/usb/host/usbh_ch9.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBH_CH9_H
|
||||
#define ZEPHYR_INCLUDE_USBH_CH9_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/usb/usbh.h>
|
||||
|
||||
int usbh_req_setup(const struct device *dev,
|
||||
const uint8_t addr,
|
||||
const uint8_t bmRequestType,
|
||||
const uint8_t bRequest,
|
||||
const uint16_t wValue,
|
||||
const uint16_t wIndex,
|
||||
const uint16_t wLength,
|
||||
uint8_t *const data);
|
||||
|
||||
int usbh_req_desc(const struct device *dev,
|
||||
const uint8_t addr,
|
||||
const uint8_t type, const uint8_t index,
|
||||
const uint8_t id,
|
||||
const uint16_t len);
|
||||
|
||||
int usbh_req_desc_dev(const struct device *dev,
|
||||
const uint8_t addr);
|
||||
|
||||
int usbh_req_desc_cfg(const struct device *dev,
|
||||
const uint8_t addr,
|
||||
const uint8_t index,
|
||||
const uint16_t len);
|
||||
|
||||
int usbh_req_set_alt(const struct device *dev,
|
||||
const uint8_t addr, const uint8_t iface,
|
||||
const uint8_t alt);
|
||||
|
||||
int usbh_req_set_address(const struct device *dev,
|
||||
const uint8_t addr, const uint8_t new);
|
||||
|
||||
int usbh_req_set_cfg(const struct device *dev,
|
||||
const uint8_t addr, const uint8_t new);
|
||||
|
||||
int usbh_req_set_sfs_rwup(const struct device *dev,
|
||||
const uint8_t addr);
|
||||
|
||||
int usbh_req_clear_sfs_rwup(const struct device *dev,
|
||||
const uint8_t addr);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBH_CH9_H */
|
160
subsys/usb/host/usbh_core.c
Normal file
160
subsys/usb/host/usbh_core.c
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include "usbh_internal.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(uhs, CONFIG_USBH_LOG_LEVEL);
|
||||
|
||||
static K_KERNEL_STACK_DEFINE(usbh_stack, CONFIG_USBH_STACK_SIZE);
|
||||
static struct k_thread usbh_thread_data;
|
||||
|
||||
/* TODO */
|
||||
static struct usbh_class_data *class_data;
|
||||
|
||||
K_MSGQ_DEFINE(usbh_msgq, sizeof(struct uhc_event),
|
||||
CONFIG_USBH_MAX_UHC_MSG, sizeof(uint32_t));
|
||||
|
||||
static int usbh_event_carrier(const struct device *dev,
|
||||
const struct uhc_event *const event)
|
||||
{
|
||||
return k_msgq_put(&usbh_msgq, event, K_NO_WAIT);
|
||||
}
|
||||
|
||||
static int event_ep_request(struct usbh_contex *const ctx,
|
||||
struct uhc_event *const event)
|
||||
{
|
||||
struct uhc_transfer *xfer = event->xfer;
|
||||
const struct device *dev = ctx->dev;
|
||||
|
||||
if (class_data && class_data->request) {
|
||||
return class_data->request(ctx, event->xfer, event->status);
|
||||
}
|
||||
|
||||
while (!k_fifo_is_empty(&xfer->done)) {
|
||||
struct net_buf *buf;
|
||||
|
||||
buf = net_buf_get(&xfer->done, K_NO_WAIT);
|
||||
if (buf) {
|
||||
LOG_HEXDUMP_INF(buf->data, buf->len, "buf");
|
||||
uhc_xfer_buf_free(dev, buf);
|
||||
}
|
||||
}
|
||||
|
||||
return uhc_xfer_free(dev, xfer);
|
||||
}
|
||||
|
||||
static int ALWAYS_INLINE usbh_event_handler(struct usbh_contex *const ctx,
|
||||
struct uhc_event *const event)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (event->type) {
|
||||
case UHC_EVT_DEV_CONNECTED_LS:
|
||||
case UHC_EVT_DEV_CONNECTED_FS:
|
||||
case UHC_EVT_DEV_CONNECTED_HS:
|
||||
LOG_DBG("Device connected event");
|
||||
if (class_data && class_data->connected) {
|
||||
ret = class_data->connected(ctx);
|
||||
}
|
||||
break;
|
||||
case UHC_EVT_DEV_REMOVED:
|
||||
LOG_DBG("Device removed event");
|
||||
if (class_data && class_data->removed) {
|
||||
ret = class_data->removed(ctx);
|
||||
}
|
||||
break;
|
||||
case UHC_EVT_RESETED:
|
||||
LOG_DBG("Bus reseted");
|
||||
/* TODO */
|
||||
if (class_data && class_data->removed) {
|
||||
ret = class_data->removed(ctx);
|
||||
}
|
||||
break;
|
||||
case UHC_EVT_SUSPENDED:
|
||||
LOG_DBG("Bus suspended");
|
||||
if (class_data && class_data->suspended) {
|
||||
ret = class_data->suspended(ctx);
|
||||
}
|
||||
break;
|
||||
case UHC_EVT_RESUMED:
|
||||
LOG_DBG("Bus resumed");
|
||||
if (class_data && class_data->resumed) {
|
||||
ret = class_data->resumed(ctx);
|
||||
}
|
||||
break;
|
||||
case UHC_EVT_RWUP:
|
||||
LOG_DBG("RWUP event");
|
||||
if (class_data && class_data->rwup) {
|
||||
ret = class_data->rwup(ctx);
|
||||
}
|
||||
break;
|
||||
case UHC_EVT_EP_REQUEST:
|
||||
event_ep_request(ctx, event);
|
||||
break;
|
||||
case UHC_EVT_ERROR:
|
||||
LOG_DBG("Error event");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usbh_thread(const struct device *dev)
|
||||
{
|
||||
struct uhc_event event;
|
||||
|
||||
while (true) {
|
||||
k_msgq_get(&usbh_msgq, &event, K_FOREVER);
|
||||
|
||||
STRUCT_SECTION_FOREACH(usbh_contex, uhs_ctx) {
|
||||
if (uhs_ctx->dev == event.dev) {
|
||||
usbh_event_handler(uhs_ctx, &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int usbh_init_device_intl(struct usbh_contex *const uhs_ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = uhc_init(uhs_ctx->dev, usbh_event_carrier);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Failed to init device driver");
|
||||
return ret;
|
||||
}
|
||||
|
||||
STRUCT_SECTION_FOREACH(usbh_class_data, cdata) {
|
||||
LOG_DBG("class data %p", cdata);
|
||||
/* TODO */
|
||||
class_data = cdata;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uhs_pre_init(const struct device *unused)
|
||||
{
|
||||
k_thread_create(&usbh_thread_data, usbh_stack,
|
||||
K_KERNEL_STACK_SIZEOF(usbh_stack),
|
||||
(k_thread_entry_t)usbh_thread,
|
||||
NULL, NULL, NULL,
|
||||
K_PRIO_COOP(9), 0, K_NO_WAIT);
|
||||
|
||||
k_thread_name_set(&usbh_thread_data, "usbh");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(uhs_pre_init, POST_KERNEL, CONFIG_USBH_INIT_PRIO);
|
2
subsys/usb/host/usbh_data.ld
Normal file
2
subsys/usb/host/usbh_data.ld
Normal file
|
@ -0,0 +1,2 @@
|
|||
ITERABLE_SECTION_RAM(usbh_contex, 4)
|
||||
ITERABLE_SECTION_RAM(usbh_class_data, 4)
|
15
subsys/usb/host/usbh_internal.h
Normal file
15
subsys/usb/host/usbh_internal.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_INCLUDE_USBH_INTERNAL_H
|
||||
#define ZEPHYR_INCLUDE_USBH_INTERNAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zephyr/usb/usbh.h>
|
||||
|
||||
int usbh_init_device_intl(struct usbh_contex *const uhs_ctx);
|
||||
|
||||
#endif /* ZEPHYR_INCLUDE_USBH_INTERNAL_H */
|
599
subsys/usb/host/usbh_shell.c
Normal file
599
subsys/usb/host/usbh_shell.c
Normal file
|
@ -0,0 +1,599 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/usb/usbh.h>
|
||||
|
||||
#include "usbh_internal.h"
|
||||
#include "usbh_ch9.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(usbh_shell, CONFIG_USBH_LOG_LEVEL);
|
||||
|
||||
#define FOOBAZ_VREQ_OUT 0x5b
|
||||
#define FOOBAZ_VREQ_IN 0x5c
|
||||
|
||||
USBH_CONTROLLER_DEFINE(uhs_ctx, DEVICE_DT_GET(DT_NODELABEL(zephyr_uhc0)));
|
||||
|
||||
const static struct shell *ctx_shell;
|
||||
|
||||
static void print_dev_desc(const struct shell *sh,
|
||||
const struct usb_device_descriptor *const desc)
|
||||
{
|
||||
shell_print(sh, "bLength\t\t\t%u", desc->bLength);
|
||||
shell_print(sh, "bDescriptorType\t\t%u", desc->bDescriptorType);
|
||||
shell_print(sh, "bcdUSB\t\t\t%x", desc->bcdUSB);
|
||||
shell_print(sh, "bDeviceClass\t\t%u", desc->bDeviceClass);
|
||||
shell_print(sh, "bDeviceSubClass\t\t%u", desc->bDeviceSubClass);
|
||||
shell_print(sh, "bDeviceProtocol\t\t%u", desc->bDeviceProtocol);
|
||||
shell_print(sh, "bMaxPacketSize0\t\t%u", desc->bMaxPacketSize0);
|
||||
shell_print(sh, "idVendor\t\t%x", desc->idVendor);
|
||||
shell_print(sh, "idProduct\t\t%x", desc->idProduct);
|
||||
shell_print(sh, "bcdDevice\t\t%x", desc->bcdDevice);
|
||||
shell_print(sh, "iManufacturer\t\t%u", desc->iManufacturer);
|
||||
shell_print(sh, "iProduct\t\t%u", desc->iProduct);
|
||||
shell_print(sh, "iSerial\t\t\t%u", desc->iSerialNumber);
|
||||
shell_print(sh, "bNumConfigurations\t%u", desc->bNumConfigurations);
|
||||
}
|
||||
|
||||
static void print_cfg_desc(const struct shell *sh,
|
||||
const struct usb_cfg_descriptor *const desc)
|
||||
{
|
||||
shell_print(sh, "bLength\t\t\t%u", desc->bLength);
|
||||
shell_print(sh, "bDescriptorType\t\t%u", desc->bDescriptorType);
|
||||
shell_print(sh, "wTotalLength\t\t%x", desc->wTotalLength);
|
||||
shell_print(sh, "bNumInterfaces\t\t%u", desc->bNumInterfaces);
|
||||
shell_print(sh, "bConfigurationValue\t%u", desc->bConfigurationValue);
|
||||
shell_print(sh, "iConfiguration\t\t%u", desc->iConfiguration);
|
||||
shell_print(sh, "bmAttributes\t\t%02x", desc->bmAttributes);
|
||||
shell_print(sh, "bMaxPower\t\t%u mA", desc->bMaxPower * 2);
|
||||
}
|
||||
|
||||
static void print_desc(const struct shell *sh, const struct net_buf *const buf)
|
||||
{
|
||||
struct usb_desc_header *head = (void *)buf->data;
|
||||
|
||||
switch (head->bDescriptorType) {
|
||||
case USB_DESC_DEVICE: {
|
||||
struct usb_device_descriptor *desc = (void *)buf->data;
|
||||
|
||||
if (buf->len < sizeof(struct usb_device_descriptor)) {
|
||||
shell_hexdump(ctx_shell, buf->data, buf->len);
|
||||
break;
|
||||
}
|
||||
|
||||
desc->bcdUSB = sys_le16_to_cpu(desc->bcdUSB);
|
||||
desc->idVendor = sys_le16_to_cpu(desc->idVendor);
|
||||
desc->idProduct = sys_le16_to_cpu(desc->idProduct);
|
||||
desc->bcdDevice = sys_le16_to_cpu(desc->bcdDevice);
|
||||
print_dev_desc(sh, desc);
|
||||
break;
|
||||
}
|
||||
case USB_DESC_CONFIGURATION: {
|
||||
struct usb_cfg_descriptor *desc = (void *)buf->data;
|
||||
|
||||
if (buf->len < sizeof(struct usb_cfg_descriptor)) {
|
||||
shell_hexdump(ctx_shell, buf->data, buf->len);
|
||||
break;
|
||||
}
|
||||
|
||||
desc->wTotalLength = sys_le16_to_cpu(desc->wTotalLength);
|
||||
print_cfg_desc(sh, desc);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
shell_hexdump(ctx_shell, buf->data, buf->len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int bazfoo_request(struct usbh_contex *const ctx,
|
||||
struct uhc_transfer *const xfer,
|
||||
int err)
|
||||
{
|
||||
const struct device *dev = ctx->dev;
|
||||
|
||||
shell_info(ctx_shell, "host: transfer finished %p, err %d", xfer, err);
|
||||
|
||||
while (!k_fifo_is_empty(&xfer->done)) {
|
||||
struct net_buf *buf;
|
||||
|
||||
buf = net_buf_get(&xfer->done, K_NO_WAIT);
|
||||
if (buf) {
|
||||
/*
|
||||
* FIXME: We don not distinguish the context
|
||||
* of the request and always try to print it
|
||||
* as descriptor first. If it is not a known descriptor,
|
||||
* we show a hexdump in any case.
|
||||
* This is just simple enough for first steps and will
|
||||
* be revised with coming peripheral device management.
|
||||
*/
|
||||
if (xfer->ep == USB_CONTROL_EP_IN) {
|
||||
print_desc(ctx_shell, buf);
|
||||
} else {
|
||||
shell_hexdump(ctx_shell, buf->data, buf->len);
|
||||
}
|
||||
|
||||
uhc_xfer_buf_free(dev, buf);
|
||||
}
|
||||
}
|
||||
|
||||
return uhc_xfer_free(dev, xfer);
|
||||
}
|
||||
|
||||
static int bazfoo_connected(struct usbh_contex *const uhs_ctx)
|
||||
{
|
||||
shell_info(ctx_shell, "host: USB device connected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bazfoo_removed(struct usbh_contex *const uhs_ctx)
|
||||
{
|
||||
shell_info(ctx_shell, "host: USB device removed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bazfoo_rwup(struct usbh_contex *const uhs_ctx)
|
||||
{
|
||||
shell_info(ctx_shell, "host: Bus remote wakeup event");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bazfoo_suspended(struct usbh_contex *const uhs_ctx)
|
||||
{
|
||||
shell_info(ctx_shell, "host: Bus suspended");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bazfoo_resumed(struct usbh_contex *const uhs_ctx)
|
||||
{
|
||||
shell_info(ctx_shell, "host: Bus resumed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
USBH_DEFINE_CLASS(bazfoo) = {
|
||||
.request = bazfoo_request,
|
||||
.connected = bazfoo_connected,
|
||||
.removed = bazfoo_removed,
|
||||
.rwup = bazfoo_rwup,
|
||||
.suspended = bazfoo_suspended,
|
||||
.resumed = bazfoo_resumed,
|
||||
};
|
||||
|
||||
static uint8_t vreq_test_buf[1024];
|
||||
|
||||
static int cmd_bulk(const struct shell *sh, size_t argc, char **argv)
|
||||
{
|
||||
struct uhc_transfer *xfer;
|
||||
struct net_buf *buf;
|
||||
uint8_t addr;
|
||||
uint8_t ep;
|
||||
size_t len;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
ep = strtol(argv[2], NULL, 16);
|
||||
len = MIN(sizeof(vreq_test_buf), strtol(argv[3], NULL, 10));
|
||||
|
||||
xfer = uhc_xfer_alloc(uhs_ctx.dev, addr, ep, 0, 512, 10, NULL);
|
||||
if (!xfer) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
buf = uhc_xfer_buf_alloc(uhs_ctx.dev, xfer, len);
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (USB_EP_DIR_IS_OUT(ep)) {
|
||||
net_buf_add_mem(buf, vreq_test_buf, len);
|
||||
}
|
||||
|
||||
return uhc_ep_enqueue(uhs_ctx.dev, xfer);
|
||||
}
|
||||
|
||||
static int cmd_vendor_in(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
const uint8_t bmRequestType = (USB_REQTYPE_DIR_TO_HOST << 7) |
|
||||
(USB_REQTYPE_TYPE_VENDOR << 5);
|
||||
const uint8_t bRequest = FOOBAZ_VREQ_IN;
|
||||
const uint16_t wValue = 0x0000;
|
||||
uint16_t wLength;
|
||||
uint8_t addr;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
wLength = MIN(sizeof(vreq_test_buf), strtol(argv[2], NULL, 10));
|
||||
|
||||
return usbh_req_setup(uhs_ctx.dev, addr,
|
||||
bmRequestType, bRequest, wValue, 0, wLength,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static int cmd_vendor_out(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
const uint8_t bmRequestType = (USB_REQTYPE_DIR_TO_DEVICE << 7) |
|
||||
(USB_REQTYPE_TYPE_VENDOR << 5);
|
||||
const uint8_t bRequest = FOOBAZ_VREQ_OUT;
|
||||
const uint16_t wValue = 0x0000;
|
||||
uint16_t wLength;
|
||||
uint8_t addr;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
wLength = MIN(sizeof(vreq_test_buf), strtol(argv[2], NULL, 10));
|
||||
|
||||
for (int i = 0; i < wLength; i++) {
|
||||
vreq_test_buf[i] = i;
|
||||
}
|
||||
|
||||
return usbh_req_setup(uhs_ctx.dev, addr,
|
||||
bmRequestType, bRequest, wValue, 0, wLength,
|
||||
vreq_test_buf);
|
||||
}
|
||||
|
||||
static int cmd_desc_device(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t addr;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
|
||||
err = usbh_req_desc_dev(uhs_ctx.dev, addr);
|
||||
if (err) {
|
||||
shell_print(sh, "host: Failed to request device descriptor");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_desc_config(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t addr;
|
||||
uint8_t cfg;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
cfg = strtol(argv[2], NULL, 10);
|
||||
|
||||
/* TODO: cfg is ignored, add to usbh_req_desc_cfg */
|
||||
err = usbh_req_desc_cfg(uhs_ctx.dev, addr, cfg, 128);
|
||||
if (err) {
|
||||
shell_print(sh, "host: Failed to request configuration descriptor");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_desc_string(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
const uint8_t type = USB_DESC_STRING;
|
||||
uint8_t addr;
|
||||
uint8_t id;
|
||||
uint8_t idx;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
id = strtol(argv[2], NULL, 10);
|
||||
idx = strtol(argv[3], NULL, 10);
|
||||
|
||||
err = usbh_req_desc(uhs_ctx.dev, addr, type, idx, id, 128);
|
||||
if (err) {
|
||||
shell_print(sh, "host: Failed to request configuration descriptor");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_feature_set_halt(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t addr;
|
||||
uint8_t ep;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
ep = strtol(argv[2], NULL, 16);
|
||||
|
||||
/* TODO: add usbh_req_set_sfs_halt(uhs_ctx.dev, 0); */
|
||||
err = usbh_req_set_sfs_rwup(uhs_ctx.dev, addr);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to set halt feature");
|
||||
} else {
|
||||
shell_print(sh, "host: Device 0x%02x, ep 0x%02x halt feature set",
|
||||
addr, ep);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_feature_clear_rwup(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t addr;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
|
||||
err = usbh_req_clear_sfs_rwup(uhs_ctx.dev, addr);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to clear rwup feature");
|
||||
} else {
|
||||
shell_print(sh, "host: Device 0x%02x, rwup feature cleared", addr);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_feature_set_rwup(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t addr;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
|
||||
err = usbh_req_set_sfs_rwup(uhs_ctx.dev, addr);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to set rwup feature");
|
||||
} else {
|
||||
shell_print(sh, "host: Device 0x%02x, rwup feature set", addr);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_device_config(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t addr;
|
||||
uint8_t cfg;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
cfg = strtol(argv[2], NULL, 10);
|
||||
|
||||
err = usbh_req_set_cfg(uhs_ctx.dev, addr, cfg);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to set configuration");
|
||||
} else {
|
||||
shell_print(sh, "host: Device 0x%02x, new configuration %u",
|
||||
addr, cfg);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_device_interface(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t addr;
|
||||
uint8_t iface;
|
||||
uint8_t alt;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
iface = strtol(argv[2], NULL, 10);
|
||||
alt = strtol(argv[3], NULL, 10);
|
||||
|
||||
err = usbh_req_set_alt(uhs_ctx.dev, addr, iface, alt);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to set interface alternate");
|
||||
} else {
|
||||
shell_print(sh, "host: Device 0x%02x, new %u alternate %u",
|
||||
addr, iface, alt);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_device_address(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
uint8_t addr;
|
||||
int err;
|
||||
|
||||
addr = strtol(argv[1], NULL, 10);
|
||||
|
||||
err = usbh_req_set_address(uhs_ctx.dev, 0, addr);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to set address");
|
||||
} else {
|
||||
shell_print(sh, "host: New device address is 0x%02x", addr);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_bus_suspend(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = uhc_bus_suspend(uhs_ctx.dev);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to perform bus suspend %d", err);
|
||||
} else {
|
||||
shell_print(sh, "host: USB bus suspended");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_bus_resume(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = uhc_bus_resume(uhs_ctx.dev);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to perform bus resume %d", err);
|
||||
} else {
|
||||
shell_print(sh, "host: USB bus resumed");
|
||||
}
|
||||
|
||||
err = uhc_sof_enable(uhs_ctx.dev);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to start SoF generator %d", err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_bus_reset(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = uhc_bus_reset(uhs_ctx.dev);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to perform bus reset %d", err);
|
||||
} else {
|
||||
shell_print(sh, "host: USB bus reseted");
|
||||
}
|
||||
|
||||
err = uhc_sof_enable(uhs_ctx.dev);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to start SoF generator %d", err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_usbh_init(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
ctx_shell = sh;
|
||||
|
||||
err = usbh_init(&uhs_ctx);
|
||||
if (err == -EALREADY) {
|
||||
shell_error(sh, "host: USB host already initialized");
|
||||
} else if (err) {
|
||||
shell_error(sh, "host: Failed to initialize %d", err);
|
||||
} else {
|
||||
shell_print(sh, "host: USB host initialized");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_usbh_enable(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usbh_enable(&uhs_ctx);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to enable USB host support");
|
||||
} else {
|
||||
shell_print(sh, "host: USB host enabled");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_usbh_disable(const struct shell *sh,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = usbh_disable(&uhs_ctx);
|
||||
if (err) {
|
||||
shell_error(sh, "host: Failed to disable USB host support");
|
||||
} else {
|
||||
shell_print(sh, "host: USB host disabled");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(desc_cmds,
|
||||
SHELL_CMD_ARG(device, NULL, "<address>",
|
||||
cmd_desc_device, 2, 0),
|
||||
SHELL_CMD_ARG(configuration, NULL, "<address> <index>",
|
||||
cmd_desc_config, 3, 0),
|
||||
SHELL_CMD_ARG(string, NULL, "<address> <id> <index>",
|
||||
cmd_desc_string, 4, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(feature_set_cmds,
|
||||
SHELL_CMD_ARG(rwup, NULL, "<address>",
|
||||
cmd_feature_set_rwup, 2, 0),
|
||||
SHELL_CMD_ARG(halt, NULL, "<address> <endpoint>",
|
||||
cmd_feature_set_halt, 3, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(feature_clear_cmds,
|
||||
SHELL_CMD_ARG(rwup, NULL, "<address>",
|
||||
cmd_feature_clear_rwup, 2, 0),
|
||||
SHELL_CMD_ARG(halt, NULL, "<address> <endpoint>",
|
||||
cmd_feature_set_halt, 3, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(device_cmds,
|
||||
SHELL_CMD_ARG(address, NULL, "<address>",
|
||||
cmd_device_address, 2, 0),
|
||||
SHELL_CMD_ARG(config, NULL, "<address> <config>",
|
||||
cmd_device_config, 3, 0),
|
||||
SHELL_CMD_ARG(interface, NULL, "<address> <interface> <alternate>",
|
||||
cmd_device_interface, 4, 0),
|
||||
SHELL_CMD_ARG(descriptor, &desc_cmds, "descriptor request",
|
||||
NULL, 1, 0),
|
||||
SHELL_CMD_ARG(feature-set, &feature_set_cmds, "feature selector",
|
||||
NULL, 1, 0),
|
||||
SHELL_CMD_ARG(feature-clear, &feature_clear_cmds, "feature selector",
|
||||
NULL, 1, 0),
|
||||
SHELL_CMD_ARG(vendor_in, NULL, "<address> <length>",
|
||||
cmd_vendor_in, 3, 0),
|
||||
SHELL_CMD_ARG(vendor_out, NULL, "<address> <length>",
|
||||
cmd_vendor_out, 3, 0),
|
||||
SHELL_CMD_ARG(bulk, NULL, "<address> <endpoint> <length>",
|
||||
cmd_bulk, 4, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(bus_cmds,
|
||||
SHELL_CMD_ARG(suspend, NULL, "[nono]",
|
||||
cmd_bus_suspend, 1, 0),
|
||||
SHELL_CMD_ARG(resume, NULL, "[nono]",
|
||||
cmd_bus_resume, 1, 0),
|
||||
SHELL_CMD_ARG(reset, NULL, "[nono]",
|
||||
cmd_bus_reset, 1, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(sub_usbh_cmds,
|
||||
SHELL_CMD_ARG(init, NULL, "[none]",
|
||||
cmd_usbh_init, 1, 0),
|
||||
SHELL_CMD_ARG(enable, NULL, "[none]",
|
||||
cmd_usbh_enable, 1, 0),
|
||||
SHELL_CMD_ARG(disable, NULL, "[none]",
|
||||
cmd_usbh_disable, 1, 0),
|
||||
SHELL_CMD_ARG(bus, &bus_cmds, "bus commands",
|
||||
NULL, 1, 0),
|
||||
SHELL_CMD_ARG(device, &device_cmds, "device commands",
|
||||
NULL, 1, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_CMD_REGISTER(usbh, &sub_usbh_cmds, "USBH commands", NULL);
|
Loading…
Reference in a new issue