/* * Copyright (c) 2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include "usbh_device.h" #include 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 uint8_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); }