124 lines
3.6 KiB
C
124 lines
3.6 KiB
C
/*
|
|
* This file is part of the unicore-mx project.
|
|
*
|
|
* Copyright (C) 2016 Kuldeep Singh Dhaka <kuldeepdhaka9@gmail.com>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#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);
|
|
}
|