/* * This file is part of the unicore-mx project. * * Copyright (C) 2016 Kuldeep Singh Dhaka * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #include "usbh-private.h" usbh_urb_id usbh_transfer_submit(const usbh_transfer *transfer) { /* Check if the device is acceptable or not. */ if (transfer->device == NULL || IS_DEVICE_INVALID(transfer->device)) { LOG_LN("Invalid device provided"); TRANSFER_INVALID(transfer); return USBH_INVALID_URB_ID; } usbh_host *host = transfer->device->host; if (transfer->ep_type == USBH_EP_CONTROL) { if (transfer->length != transfer->setup.wLength) { LOG_LN("for setup, transfer length and wLength should match"); TRANSFER_INVALID(transfer); return USBH_INVALID_URB_ID; } if ((transfer->setup.bmRequestType & USB_REQ_TYPE_DIRECTION) && !transfer->setup.wLength) { /* Control IN with no data do not make sense * and is not valid as per specs! * usb_20.pdf p226 "Figure 8-37. Control Read and Write Sequences" */ LOG_LN("WARN: device-to-host bit set for wLength = 0"); TRANSFER_INVALID(transfer); return USBH_INVALID_URB_ID; } } if (transfer->ep_type == USBH_EP_INTERRUPT || transfer->ep_type == USBH_EP_ISOCHRONOUS) { if (!transfer->interval) { /* Interval = 0 do not make any sense! */ LOG_LN("WARN: interval should be greater than 0 " "for isochronous/interrupt endpoints"); TRANSFER_INVALID(transfer); return USBH_INVALID_URB_ID; } } /* Low speed device capabilities check. */ if (transfer->device->speed == USBH_SPEED_LOW) { usbh_ep_type et = transfer->ep_type; if (et != USBH_EP_CONTROL && et != USBH_EP_INTERRUPT) { LOG_LN("USB low-speed ONLY support control/interrupt endpoint"); TRANSFER_INVALID(transfer); return USBH_INVALID_URB_ID; } } /* Search for a free URB */ usbh_urb *urb = NULL; unsigned i; for (i = 0; i < URB_ARRAY_LENGTH; i++) { usbh_urb *tmp = &host->urbs[i]; if (IS_URB_INVALID(tmp)) { LOGF_LN("urb at offset %"PRIu8" is currently unused", i); urb = tmp; break; } } /* check if got any URB free */ if (urb == NULL) { LOG_LN("WARN: all urb in use"); TRANSFER_NO_RES(transfer); return USBH_INVALID_URB_ID; } /* store the information in URB */ urb->id = host->next_urb_id++; urb->transfer = *transfer; urb->transfer.transferred = 0; urb->timeout_on = transfer->timeout ? (host->last_poll + MS2US(transfer->timeout)) : 0; urb->backend_tag = INVALID_BACKEND_TAG; LOGF_LN("Create URB with id = %"PRIu64, urb->id); host->backend->transfer_submit(host, urb); return urb->id; } void usbh_transfer_cancel(usbh_host *host, usbh_urb_id urb_id) { unsigned i; if (IS_URB_ID_INVALID(urb_id)) { LOG_LN("invalid urb id passed to transfer_cancel"); return; } for (i = 0; i < URB_ARRAY_LENGTH; i++) { usbh_urb *urb = &host->urbs[i]; if (urb->id == urb_id) { LOGF_LN("urb %"PRIu64" cancelled", urb->id); usbh_urb_free(urb, USBH_ERR_CANCEL); return; } } LOGF_LN("WARN: urb with id = %"PRIu64" not found", urb_id); }