Newer
Older
/*
* Most of this source has been derived from the Linux USB
* project:
* (C) Copyright Linus Torvalds 1999
* (C) Copyright Johannes Erdfelt 1999-2001
* (C) Copyright Andreas Gal 1999
* (C) Copyright Gregory P. Smith 1999
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000 (kernel hotplug, usb_device_id)
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
*
* Adapted for U-Boot:
* (C) Copyright 2001 Denis Peter, MPL AG Switzerland
* SPDX-License-Identifier: GPL-2.0+
*/
/*
* How it works:
*
* Since this is a bootloader, the devices will not be automatic
* (re)configured on hotplug, but after a restart of the USB the
* device should work.
*
* For each transfer (except "Interrupt") we wait for completion.
*/
#include <common.h>
#include <command.h>
#include <memalign.h>
#include <linux/ctype.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
char usb_started; /* flag for the started/stopped USB status */
#ifndef CONFIG_DM_USB
static struct usb_device usb_dev[USB_MAX_DEVICE];
static int dev_index;
#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT
#define CONFIG_USB_MAX_CONTROLLER_COUNT 1
#endif
/***************************************************************************
* Init USB Device
*/
int usb_init(void)
{
void *ctrl;
struct usb_device *dev;
int i, start_index = 0;
Hans de Goede
committed
int controllers_initialized = 0;
dev_index = 0;
asynch_allowed = 1;
/* first make all devices unknown */
for (i = 0; i < USB_MAX_DEVICE; i++) {
memset(&usb_dev[i], 0, sizeof(struct usb_device));
usb_dev[i].devnum = -1;
}
for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) {
/* init low_level USB */
printf("USB%d: ", i);
ret = usb_lowlevel_init(i, USB_INIT_HOST, &ctrl);
if (ret == -ENODEV) { /* No such device. */
puts("Port not available.\n");
Hans de Goede
committed
controllers_initialized++;
continue;
}
if (ret) { /* Other error. */
puts("lowlevel init failed\n");
continue;
}
/*
* lowlevel init is OK, now scan the bus for devices
* i.e. search HUBs and configure them
*/
Hans de Goede
committed
controllers_initialized++;
start_index = dev_index;
printf("scanning bus %d for devices... ", i);
ret = usb_alloc_new_device(ctrl, &dev);
if (ret)
/*
* device 0 is always present
* (root hub, so let it analyze)
*/
ret = usb_new_device(dev);
if (ret)
usb_free_device(dev->controller);
if (start_index == dev_index) {
puts("No USB Device found\n");
printf("%d USB Device(s) found\n",
dev_index - start_index);
usb_started = 1;
debug("scan end\n");
/* if we were not able to find at least one working bus, bail out */
Hans de Goede
committed
if (controllers_initialized == 0)
puts("USB error: all controllers failed lowlevel init\n");
return usb_started ? 0 : -ENODEV;
}
/******************************************************************************
* Stop USB this stops the LowLevel Part and deregisters USB devices.
*/
int usb_stop(void)
{
if (usb_started) {
asynch_allowed = 1;
usb_started = 0;
usb_hub_reset();
for (i = 0; i < CONFIG_USB_MAX_CONTROLLER_COUNT; i++) {
if (usb_lowlevel_stop(i))
printf("failed to stop USB controller %d\n", i);
}
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/******************************************************************************
* Detect if a USB device has been plugged or unplugged.
*/
int usb_detect_change(void)
{
int i, j;
int change = 0;
for (j = 0; j < USB_MAX_DEVICE; j++) {
for (i = 0; i < usb_dev[j].maxchild; i++) {
struct usb_port_status status;
if (usb_get_port_status(&usb_dev[j], i + 1,
&status) < 0)
/* USB request failed */
continue;
if (le16_to_cpu(status.wPortChange) &
USB_PORT_STAT_C_CONNECTION)
change++;
}
}
return change;
}
/*
* disables the asynch behaviour of the control message. This is used for data
* transfers that uses the exclusiv access to the control and bulk messages.
* Returns the old value so it can be restored later.
#endif /* !CONFIG_DM_USB */
/*-------------------------------------------------------------------
* Message wrappers.
*
*/
/*
* submits an Interrupt Message
*/
int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int transfer_len, int interval)
return submit_int_msg(dev, pipe, buffer, transfer_len, interval);
}
/*
* submits a control message and waits for comletion (at least timeout * 1ms)
* If timeout is 0, we don't wait for completion (used as example to set and
* clear keyboards LEDs). For data transfers, (storage transfers) we don't
* allow control messages with 0 timeout, by previousely resetting the flag
* asynch_allowed (usb_disable_asynch(1)).
* returns the transferred length if OK or -1 if error. The transferred length
* and the current status are stored in the dev->act_len and dev->status.
*/
int usb_control_msg(struct usb_device *dev, unsigned int pipe,
unsigned char request, unsigned char requesttype,
unsigned short value, unsigned short index,
void *data, unsigned short size, int timeout)
{
ALLOC_CACHE_ALIGN_BUFFER(struct devrequest, setup_packet, 1);
if ((timeout == 0) && (!asynch_allowed)) {
/* request for a asynch control pipe is not allowed */
setup_packet->requesttype = requesttype;
setup_packet->request = request;
setup_packet->value = cpu_to_le16(value);
setup_packet->index = cpu_to_le16(index);
setup_packet->length = cpu_to_le16(size);
debug("usb_control_msg: request: 0x%X, requesttype: 0x%X, " \
"value 0x%X index 0x%X length 0x%X\n",
request, requesttype, value, index, size);
dev->status = USB_ST_NOT_PROC; /*not yet processed */
err = submit_control_msg(dev, pipe, data, size, setup_packet);
if (err < 0)
return err;
/*
* Wait for status to update until timeout expires, USB driver
* interrupt handler may set the status when the USB operation has
* been completed.
*/
while (timeout--) {
if (!((volatile unsigned long)dev->status & USB_ST_NOT_PROC))
break;
if (dev->status)
return -1;
}
/*-------------------------------------------------------------------
* submits bulk message, and waits for completion. returns 0 if Ok or
* synchronous behavior
*/
int usb_bulk_msg(struct usb_device *dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
{
if (len < 0)
dev->status = USB_ST_NOT_PROC; /*not yet processed */
if (submit_bulk_msg(dev, pipe, data, len) < 0)
while (timeout--) {
if (!((volatile unsigned long)dev->status & USB_ST_NOT_PROC))
*actual_length = dev->act_len;
if (dev->status == 0)
}
/*-------------------------------------------------------------------
* Max Packet stuff
*/
/*
* returns the max packet size, depending on the pipe direction and
* the configurations values
*/
int usb_maxpacket(struct usb_device *dev, unsigned long pipe)
/* direction is out -> use emaxpacket out */
if ((pipe & USB_DIR_IN) == 0)
return dev->epmaxpacketout[((pipe>>15) & 0xf)];
return dev->epmaxpacketin[((pipe>>15) & 0xf)];
/*
* The routine usb_set_maxpacket_ep() is extracted from the loop of routine
* usb_set_maxpacket(), because the optimizer of GCC 4.x chokes on this routine
* when it is inlined in 1 single routine. What happens is that the register r3
* is used as loop-count 'i', but gets overwritten later on.
* This is clearly a compiler bug, but it is easier to workaround it here than
* to update the compiler (Occurs with at least several GCC 4.{1,2},x
* CodeSourcery compilers like e.g. 2007q3, 2008q1, 2008q3 lite editions on ARM)
*
* NOTE: Similar behaviour was observed with GCC4.6 on ARMv5.
usb_set_maxpacket_ep(struct usb_device *dev, int if_idx, int ep_idx)
{
int b;
u16 ep_wMaxPacketSize;
ep = &dev->config.if_desc[if_idx].ep_desc[ep_idx];
b = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
ep_wMaxPacketSize = get_unaligned(&ep->wMaxPacketSize);
if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_CONTROL) {
/* Control => bidirectional */
dev->epmaxpacketout[b] = ep_wMaxPacketSize;
dev->epmaxpacketin[b] = ep_wMaxPacketSize;
debug("##Control EP epmaxpacketout/in[%d] = %d\n",
b, dev->epmaxpacketin[b]);
} else {
if ((ep->bEndpointAddress & 0x80) == 0) {
/* OUT Endpoint */
if (ep_wMaxPacketSize > dev->epmaxpacketout[b]) {
dev->epmaxpacketout[b] = ep_wMaxPacketSize;
debug("##EP epmaxpacketout[%d] = %d\n",
b, dev->epmaxpacketout[b]);
}
} else {
/* IN Endpoint */
if (ep_wMaxPacketSize > dev->epmaxpacketin[b]) {
dev->epmaxpacketin[b] = ep_wMaxPacketSize;
debug("##EP epmaxpacketin[%d] = %d\n",
b, dev->epmaxpacketin[b]);
}
} /* if out */
} /* if control */
}
/*
* set the max packed value of all endpoints in the given configuration
*/
static int usb_set_maxpacket(struct usb_device *dev)
int i, ii;
for (i = 0; i < dev->config.desc.bNumInterfaces; i++)
for (ii = 0; ii < dev->config.if_desc[i].desc.bNumEndpoints; ii++)
return 0;
}
/*******************************************************************************
* Parse the config, located in buffer, and fills the dev->config structure.
* Note that all little/big endian swapping are done automatically.
* (wTotalLength has already been swapped and sanitized when it was read.)
static int usb_parse_config(struct usb_device *dev,
unsigned char *buffer, int cfgno)
Bartlomiej Sieka
committed
int index, ifno, epno, curr_if_num;
u16 ep_wMaxPacketSize;
struct usb_interface *if_desc = NULL;
Bartlomiej Sieka
committed
ifno = -1;
epno = -1;
curr_if_num = -1;
dev->configno = cfgno;
head = (struct usb_descriptor_header *) &buffer[0];
if (head->bDescriptorType != USB_DT_CONFIG) {
printf(" ERROR: NOT USB_CONFIG_DESC %x\n",
head->bDescriptorType);
if (head->bLength != USB_DT_CONFIG_SIZE) {
printf("ERROR: Invalid USB CFG length (%d)\n", head->bLength);
}
memcpy(&dev->config, head, USB_DT_CONFIG_SIZE);
Bartlomiej Sieka
committed
dev->config.no_of_if = 0;
/* Ok the first entry must be a configuration entry,
* now process the others */
Bartlomiej Sieka
committed
head = (struct usb_descriptor_header *) &buffer[index];
while (index + 1 < dev->config.desc.wTotalLength && head->bLength) {
switch (head->bDescriptorType) {
case USB_DT_INTERFACE:
if (head->bLength != USB_DT_INTERFACE_SIZE) {
printf("ERROR: Invalid USB IF length (%d)\n",
head->bLength);
break;
}
if (index + USB_DT_INTERFACE_SIZE >
dev->config.desc.wTotalLength) {
puts("USB IF descriptor overflowed buffer!\n");
break;
}
if (((struct usb_interface_descriptor *) \
head)->bInterfaceNumber != curr_if_num) {
/* this is a new interface, copy new desc */
ifno = dev->config.no_of_if;
if (ifno >= USB_MAXINTERFACES) {
puts("Too many USB interfaces!\n");
/* try to go on with what we have */
if_desc = &dev->config.if_desc[ifno];
memcpy(if_desc, head,
USB_DT_INTERFACE_SIZE);
if_desc->no_of_ep = 0;
if_desc->num_altsetting = 1;
if_desc->desc.bInterfaceNumber;
} else {
/* found alternate setting for the interface */
if (ifno >= 0) {
if_desc = &dev->config.if_desc[ifno];
if_desc->num_altsetting++;
}
}
break;
case USB_DT_ENDPOINT:
if (head->bLength != USB_DT_ENDPOINT_SIZE &&
head->bLength != USB_DT_ENDPOINT_AUDIO_SIZE) {
printf("ERROR: Invalid USB EP length (%d)\n",
head->bLength);
break;
}
if (index + head->bLength >
dev->config.desc.wTotalLength) {
puts("USB EP descriptor overflowed buffer!\n");
break;
}
if (ifno < 0) {
puts("Endpoint descriptor out of order!\n");
break;
}
epno = dev->config.if_desc[ifno].no_of_ep;
if_desc = &dev->config.if_desc[ifno];
printf("Interface %d has too many endpoints!\n",
if_desc->desc.bInterfaceNumber);
if_desc->no_of_ep++;
memcpy(&if_desc->ep_desc[epno], head,
USB_DT_ENDPOINT_SIZE);
Loading
Loading full blame...