/* * Copyright (c) 2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "usbd_device.h" #include "usbd_class.h" #include "usbd_ch9.h" #include "usbd_desc.h" #include "usbd_endpoint.h" #include LOG_MODULE_REGISTER(usbd_ep, CONFIG_USBD_LOG_LEVEL); int usbd_ep_enable(const struct device *dev, const struct usb_ep_descriptor *const ed, uint32_t *const ep_bm) { int ret; ret = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, ed->wMaxPacketSize, ed->bInterval); if (ret == 0) { usbd_ep_bm_set(ep_bm, ed->bEndpointAddress); } return ret; } int usbd_ep_disable(const struct device *dev, const uint8_t ep, uint32_t *const ep_bm) { int ret; ret = udc_ep_disable(dev, ep); if (ret) { return ret; } usbd_ep_bm_clear(ep_bm, ep); ret = udc_ep_dequeue(dev, ep); if (ret) { return ret; } k_yield(); return ret; } static void usbd_ep_ctrl_set_zlp(struct usbd_context *const uds_ctx, struct net_buf *const buf) { struct usb_setup_packet *setup = usbd_get_setup_pkt(uds_ctx); size_t min_len = MIN(setup->wLength, buf->len); if (buf->len == 0) { return; } /* * Set ZLP flag when host asks for a bigger length and the * last chunk is wMaxPacketSize long, to indicate the last * packet. */ if (setup->wLength > min_len && !(min_len % USB_CONTROL_EP_MPS)) { /* * Transfer length is less as requested by wLength and * is multiple of wMaxPacketSize. */ LOG_DBG("add ZLP, wLength %u buf length %zu", setup->wLength, min_len); udc_ep_buf_set_zlp(buf); } } /* * All the functions below are part of public USB device support API. */ int usbd_ep_ctrl_enqueue(struct usbd_context *const uds_ctx, struct net_buf *const buf) { struct udc_buf_info *bi; bi = udc_get_buf_info(buf); if (USB_EP_GET_IDX(bi->ep)) { /* Not a control endpoint */ return -EINVAL; } if (USB_EP_DIR_IS_IN(bi->ep)) { if (usbd_is_suspended(uds_ctx)) { LOG_ERR("device is suspended"); return -EPERM; } usbd_ep_ctrl_set_zlp(uds_ctx, buf); } return udc_ep_enqueue(uds_ctx->dev, buf); } struct net_buf *usbd_ep_buf_alloc(const struct usbd_class_data *const c_data, const uint8_t ep, const size_t size) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); return udc_ep_buf_alloc(uds_ctx->dev, ep, size); } int usbd_ep_enqueue(const struct usbd_class_data *const c_data, struct net_buf *const buf) { struct usbd_context *uds_ctx = usbd_class_get_ctx(c_data); struct udc_buf_info *bi = udc_get_buf_info(buf); if (USB_EP_DIR_IS_IN(bi->ep)) { if (usbd_is_suspended(uds_ctx)) { return -EPERM; } } bi->owner = (void *)c_data; return udc_ep_enqueue(uds_ctx->dev, buf); } int usbd_ep_buf_free(struct usbd_context *const uds_ctx, struct net_buf *buf) { return udc_ep_buf_free(uds_ctx->dev, buf); } int usbd_ep_dequeue(struct usbd_context *const uds_ctx, const uint8_t ep) { return udc_ep_dequeue(uds_ctx->dev, ep); } int usbd_ep_set_halt(struct usbd_context *const uds_ctx, const uint8_t ep) { struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data; int ret; ret = udc_ep_set_halt(uds_ctx->dev, ep); if (ret) { LOG_WRN("Set halt 0x%02x failed", ep); return ret; } usbd_ep_bm_set(&ch9_data->ep_halt, ep); return ret; } int usbd_ep_clear_halt(struct usbd_context *const uds_ctx, const uint8_t ep) { struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data; int ret; ret = udc_ep_clear_halt(uds_ctx->dev, ep); if (ret) { LOG_WRN("Clear halt 0x%02x failed", ep); return ret; } usbd_ep_bm_clear(&ch9_data->ep_halt, ep); return ret; } bool usbd_ep_is_halted(struct usbd_context *const uds_ctx, const uint8_t ep) { struct usbd_ch9_data *ch9_data = &uds_ctx->ch9_data; return usbd_ep_bm_is_set(&ch9_data->ep_halt, ep); }