zephyr/subsys/usb/host/usbh_ch9.c
Alexander Kaiser 476a224dfa usb: host: use uint16_t for Language ID
usbh_req_desc() truncates the descriptor id. This problem is most
visible with string descriptor requests, as only then can wIndex be
greater than 0xFF. In particular, this affects commonly used language
IDs such as English (United States), which is 0x0409.

Signed-off-by: Alexander Kaiser <akaiser@urbansky.com>
2024-02-01 10:29:00 +00:00

288 lines
7 KiB
C

/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/usb/usbh.h>
#include <zephyr/usb/usb_ch9.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/net/buf.h>
#include "usbh_device.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(usbh_ch9, CONFIG_USBH_LOG_LEVEL);
/*
* For now we set it to the upper limit defined in Chapter
* "9.2.6.4 Standard Device Requests"
* This will need to be revised and set depending on the request.
*/
#define SETUP_REQ_TIMEOUT 5000U
K_SEM_DEFINE(ch9_req_sync, 0, 1);
static int ch9_req_cb(struct usb_device *const udev, struct uhc_transfer *const xfer)
{
LOG_DBG("Request finished %p, err %d", xfer, xfer->err);
k_sem_give(&ch9_req_sync);
return 0;
}
int usbh_req_setup(struct usb_device *const udev,
const uint8_t bmRequestType,
const uint8_t bRequest,
const uint16_t wValue,
const uint16_t wIndex,
const uint16_t wLength,
struct net_buf *const buf)
{
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;
uint8_t ep = usb_reqtype_is_to_device(&req) ? 0x00 : 0x80;
int ret;
xfer = usbh_xfer_alloc(udev, ep, 0, 64, SETUP_REQ_TIMEOUT, (void *)ch9_req_cb);
if (!xfer) {
return -ENOMEM;
}
memcpy(xfer->setup_pkt, &req, sizeof(req));
__ASSERT((buf != NULL && wLength) || (buf == NULL && !wLength),
"Unresolved conditions for data stage");
if (wLength) {
ret = usbh_xfer_buf_add(udev, xfer, buf);
if (ret) {
goto buf_alloc_err;
}
}
ret = usbh_xfer_enqueue(udev, xfer);
if (ret) {
goto buf_alloc_err;
}
k_sem_take(&ch9_req_sync, K_MSEC(SETUP_REQ_TIMEOUT));
ret = xfer->err;
buf_alloc_err:
usbh_xfer_free(udev, xfer);
return ret;
}
int usbh_req_desc(struct usb_device *const udev,
const uint8_t type, const uint8_t index,
const uint16_t id,
const uint16_t len,
struct net_buf *const buf)
{
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(udev,
bmRequestType, bRequest, wValue, id, len,
buf);
}
int usbh_req_desc_dev(struct usb_device *const udev,
struct usb_device_descriptor *const desc)
{
const uint8_t type = USB_DESC_DEVICE;
const uint16_t wLength = sizeof(struct usb_device_descriptor);
struct net_buf *buf;
int ret;
buf = usbh_xfer_buf_alloc(udev, wLength);
if (!buf) {
return -ENOMEM;
}
ret = usbh_req_desc(udev, type, 0, 0, wLength, buf);
if (ret == 0 && buf->len == wLength) {
memcpy(desc, buf->data, wLength);
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);
}
usbh_xfer_buf_free(udev, buf);
return ret;
}
int usbh_req_desc_cfg(struct usb_device *const udev,
const uint8_t index,
const uint16_t len,
struct usb_cfg_descriptor *const desc)
{
const uint8_t type = USB_DESC_CONFIGURATION;
const uint16_t wLength = len;
struct net_buf *buf;
int ret;
buf = usbh_xfer_buf_alloc(udev, len);
if (!buf) {
return -ENOMEM;
}
ret = usbh_req_desc(udev, type, index, 0, wLength, buf);
if (ret == 0) {
memcpy(desc, buf->data, len);
desc->wTotalLength = sys_le16_to_cpu(desc->wTotalLength);
}
usbh_xfer_buf_free(udev, buf);
return ret;
}
int usbh_req_set_address(struct usb_device *const udev,
const uint8_t addr)
{
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7;
const uint8_t bRequest = USB_SREQ_SET_ADDRESS;
int ret;
ret = usbh_req_setup(udev, bmRequestType, bRequest, addr, 0, 0, NULL);
if (ret == 0) {
udev->addr = addr;
if (addr == 0) {
udev->state = USB_STATE_DEFAULT;
}
if (addr != 0 && udev->state == USB_STATE_DEFAULT) {
udev->state = USB_STATE_ADDRESSED;
}
}
return ret;
}
int usbh_req_set_cfg(struct usb_device *const udev,
const uint8_t cfg)
{
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7;
const uint8_t bRequest = USB_SREQ_SET_CONFIGURATION;
int ret;
/* Ignore the required state change condition for now. */
ret = usbh_req_setup(udev, bmRequestType, bRequest, cfg, 0, 0, NULL);
if (ret == 0) {
udev->actual_cfg = cfg;
if (cfg == 0) {
udev->state = USB_STATE_ADDRESSED;
}
if (cfg != 0 && udev->state == USB_STATE_ADDRESSED) {
udev->state = USB_STATE_CONFIGURED;
}
}
return ret;
}
int usbh_req_get_cfg(struct usb_device *const udev,
uint8_t *const cfg)
{
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_HOST << 7;
const uint8_t bRequest = USB_SREQ_GET_CONFIGURATION;
const uint16_t wLength = 1;
struct net_buf *buf;
int ret;
buf = usbh_xfer_buf_alloc(udev, wLength);
if (!buf) {
return -ENOMEM;
}
ret = usbh_req_setup(udev, bmRequestType, bRequest, 0, 0, wLength, buf);
if (ret == 0 && buf->len == wLength) {
*cfg = buf->data[0];
}
usbh_xfer_buf_free(udev, buf);
return ret;
}
int usbh_req_set_alt(struct usb_device *const udev,
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(udev,
bmRequestType, bRequest, wValue, wIndex, 0,
NULL);
}
int usbh_req_set_sfs_rwup(struct usb_device *const udev)
{
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(udev,
bmRequestType, bRequest, wValue, 0, 0,
NULL);
}
int usbh_req_clear_sfs_rwup(struct usb_device *const udev)
{
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(udev,
bmRequestType, bRequest, wValue, 0, 0,
NULL);
}
int usbh_req_set_hcfs_ppwr(struct usb_device *const udev,
const uint8_t port)
{
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7 |
USB_REQTYPE_TYPE_CLASS << 5 |
USB_REQTYPE_RECIPIENT_OTHER << 0;
const uint8_t bRequest = USB_HCREQ_SET_FEATURE;
const uint16_t wValue = USB_HCFS_PORT_POWER;
const uint16_t wIndex = port;
return usbh_req_setup(udev,
bmRequestType, bRequest, wValue, wIndex, 0,
NULL);
}
int usbh_req_set_hcfs_prst(struct usb_device *const udev,
const uint8_t port)
{
const uint8_t bmRequestType = USB_REQTYPE_DIR_TO_DEVICE << 7 |
USB_REQTYPE_TYPE_CLASS << 5 |
USB_REQTYPE_RECIPIENT_OTHER << 0;
const uint8_t bRequest = USB_HCREQ_SET_FEATURE;
const uint16_t wValue = USB_HCFS_PORT_RESET;
const uint16_t wIndex = port;
return usbh_req_setup(udev,
bmRequestType, bRequest, wValue, wIndex, 0,
NULL);
}