This patch adds USB capabilities to libpayload. It requires some
memalign implementation (eg. the one I sent yesterday).
Features:
 - UHCI controller driver
 - UHCI root hub driver
 - USB MSC (Mass Storage Class) driver
 - skeleton of a USB HID driver
   (requires better interrupt transfer handling, which is TODO)
 - skeleton of a USB hub driver
   (needs several blank spots filled in, eg. power management.
    Again: TODO)
OHCI and EHCI are not supported, though OHCI support should be rather
easy as the stack provides reasonable abstractions (or so I hope). EHCI
will probably be more complicated.
Isochronous transfers (eg. webcams, audio stuff, ...) are not supported.
They can be, but I doubt we'll have a reason for that in the boot
environment.
The MSC driver was tested against a couple of USB flash drives, and
should be reasonably tolerant by now. But I probably underestimate
the amount of bugs present in USB flash drives, so feedback is welcome.
Signed-off-by: Patrick Georgi <patrick.georgi@coresystems.de>
Acked-by: Jordan Crouse <jordan.crouse@amd.com>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3560 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
			
			
This commit is contained in:
		@@ -132,5 +132,46 @@ config SPEAKER
 | 
				
			|||||||
	bool "Support for PC speaker"
 | 
						bool "Support for PC speaker"
 | 
				
			||||||
	default y
 | 
						default y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config USB
 | 
				
			||||||
 | 
						bool "USB Support"
 | 
				
			||||||
 | 
						default n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config USB_UHCI
 | 
				
			||||||
 | 
						bool "Support for USB UHCI controllers"
 | 
				
			||||||
 | 
						depends on USB
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  Select this option if you are going to use USB 1.1 on an Intel based
 | 
				
			||||||
 | 
						  system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config USB_OHCI
 | 
				
			||||||
 | 
						bool "Support for USB OHCI controllers"
 | 
				
			||||||
 | 
						depends on USB
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  Select this option if you are going to use USB 1.1 on an AMD based
 | 
				
			||||||
 | 
						  system.
 | 
				
			||||||
 | 
						  NOTE: This option is not (fully) implemented yet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config USB_EHCI
 | 
				
			||||||
 | 
						bool "Support for USB EHCI controllers"
 | 
				
			||||||
 | 
						depends on USB
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  Select this option if you want to use USB 2.0
 | 
				
			||||||
 | 
						  NOTE: This option is not (fully) implemented yet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config USB_HID
 | 
				
			||||||
 | 
						bool "Support for USB keyboards (broken)"
 | 
				
			||||||
 | 
						depends on USB
 | 
				
			||||||
 | 
						default n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config USB_HUB
 | 
				
			||||||
 | 
						bool "Support for USB hubs (broken)"
 | 
				
			||||||
 | 
						depends on USB
 | 
				
			||||||
 | 
						default n
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config USB_MSC
 | 
				
			||||||
 | 
						bool "Support for USB storage"
 | 
				
			||||||
 | 
						depends on USB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
endmenu
 | 
					endmenu
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -137,6 +137,7 @@ install: lib
 | 
				
			|||||||
prepare:
 | 
					prepare:
 | 
				
			||||||
	$(Q)mkdir -p $(obj)/util/kconfig/lxdialog
 | 
						$(Q)mkdir -p $(obj)/util/kconfig/lxdialog
 | 
				
			||||||
	$(Q)mkdir -p $(obj)/crypto $(obj)/curses $(obj)/drivers/video
 | 
						$(Q)mkdir -p $(obj)/crypto $(obj)/curses $(obj)/drivers/video
 | 
				
			||||||
 | 
						$(Q)mkdir -p $(obj)/drivers/usb
 | 
				
			||||||
	$(Q)mkdir -p $(obj)/i386 $(obj)/lib/$(ARCHDIR-y) $(obj)/libc 
 | 
						$(Q)mkdir -p $(obj)/i386 $(obj)/lib/$(ARCHDIR-y) $(obj)/libc 
 | 
				
			||||||
	$(Q)mkdir -p $(src)/lib/$(ARCHDIR-y)
 | 
						$(Q)mkdir -p $(src)/lib/$(ARCHDIR-y)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,3 +47,14 @@ TARGETS-$(CONFIG_VGA_VIDEO_CONSOLE) += drivers/video/vga.o
 | 
				
			|||||||
# Geode console drivers
 | 
					# Geode console drivers
 | 
				
			||||||
TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/geode.o
 | 
					TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/geode.o
 | 
				
			||||||
TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/font8x16.o
 | 
					TARGETS-$(CONFIG_GEODE_VIDEO_CONSOLE) += drivers/video/font8x16.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# USB stack
 | 
				
			||||||
 | 
					TARGETS-$(CONFIG_USB) += drivers/usb/usbinit.o
 | 
				
			||||||
 | 
					TARGETS-$(CONFIG_USB) += drivers/usb/usb.o
 | 
				
			||||||
 | 
					TARGETS-$(CONFIG_USB) += drivers/usb/usb_dev.o
 | 
				
			||||||
 | 
					TARGETS-$(CONFIG_USB_HUB) += drivers/usb/usbhub.o
 | 
				
			||||||
 | 
					TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci.o
 | 
				
			||||||
 | 
					TARGETS-$(CONFIG_USB_UHCI) += drivers/usb/uhci_rh.o
 | 
				
			||||||
 | 
					TARGETS-$(CONFIG_USB_HID) += drivers/usb/usbhid.o
 | 
				
			||||||
 | 
					TARGETS-$(CONFIG_USB_MSC) += drivers/usb/usbmsc.o
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										22
									
								
								payloads/libpayload/drivers/usb/TODO
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								payloads/libpayload/drivers/usb/TODO
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					- handle error conditions
 | 
				
			||||||
 | 
					- handle disconnect more gracefully (ie. make calling layer aware that the device doesn't exist somehow)
 | 
				
			||||||
 | 
					- usbhub:
 | 
				
			||||||
 | 
					  - proper client enumeration (esp. detach)
 | 
				
			||||||
 | 
					  - change detection
 | 
				
			||||||
 | 
					  - power management
 | 
				
			||||||
 | 
					- handle interrupts more cleverly:
 | 
				
			||||||
 | 
					  create a new queue for the interrupt with a couple of TD sequences,
 | 
				
			||||||
 | 
					    - each ending with "breadth first" flag
 | 
				
			||||||
 | 
					    - linked as a chain
 | 
				
			||||||
 | 
					  add that queue at the appropriate times in front of the default structure so the max latency is honored
 | 
				
			||||||
 | 
					    - only one intr chain per framelist item, so it must be arranged appropriately
 | 
				
			||||||
 | 
					  reads from usb device just look at "invalidated" tds and the results they got
 | 
				
			||||||
 | 
					  handled tds get reactivated as a ring structure
 | 
				
			||||||
 | 
					    - added as child of the oldest td
 | 
				
			||||||
 | 
					    - queue header already dropped the td, so no issue there
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  this setup ensures that:
 | 
				
			||||||
 | 
					  - the max latency of the device is honored
 | 
				
			||||||
 | 
					  - the client knows the right order of the data
 | 
				
			||||||
 | 
					  - there is no need for an interrupt handler
 | 
				
			||||||
 | 
					    - but must be polled at least max latency * num tds times -> more tds = less time pressure
 | 
				
			||||||
							
								
								
									
										507
									
								
								payloads/libpayload/drivers/usb/uhci.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										507
									
								
								payloads/libpayload/drivers/usb/uhci.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,507 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					#include "uhci.h"
 | 
				
			||||||
 | 
					#include <arch/virtual.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void uhci_start (hci_t *controller);
 | 
				
			||||||
 | 
					static void uhci_stop (hci_t *controller);
 | 
				
			||||||
 | 
					static void uhci_reset (hci_t *controller);
 | 
				
			||||||
 | 
					static void uhci_shutdown (hci_t *controller);
 | 
				
			||||||
 | 
					static int uhci_packet (usbdev_t *dev, int endp, int pid, int toggle,
 | 
				
			||||||
 | 
								int length, u8 *data);
 | 
				
			||||||
 | 
					static int uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize);
 | 
				
			||||||
 | 
					static int uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq,
 | 
				
			||||||
 | 
								 int dalen, u8 *data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					/* dump uhci */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_dump (hci_t *controller)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						printf ("dump:\nUSBCMD: %x\n", uhci_reg_read16 (controller, USBCMD));
 | 
				
			||||||
 | 
						printf ("USBSTS: %x\n", uhci_reg_read16 (controller, USBSTS));
 | 
				
			||||||
 | 
						printf ("USBINTR: %x\n", uhci_reg_read16 (controller, USBINTR));
 | 
				
			||||||
 | 
						printf ("FRNUM: %x\n", uhci_reg_read16 (controller, FRNUM));
 | 
				
			||||||
 | 
						printf ("FLBASEADD: %x\n", uhci_reg_read32 (controller, FLBASEADD));
 | 
				
			||||||
 | 
						printf ("SOFMOD: %x\n", uhci_reg_read8 (controller, SOFMOD));
 | 
				
			||||||
 | 
						printf ("PORTSC1: %x\n", uhci_reg_read16 (controller, PORTSC1));
 | 
				
			||||||
 | 
						printf ("PORTSC2: %x\n", uhci_reg_read16 (controller, PORTSC2));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					td_dump (td_t *td)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						printf ("%x packet (at %lx) to %x.%x failed\n", td->pid,
 | 
				
			||||||
 | 
							virt_to_phys (td), td->dev_addr, td->endp);
 | 
				
			||||||
 | 
						printf ("td (counter at %x) returns: ", td->counter);
 | 
				
			||||||
 | 
						printf (" bitstuff err: %x, ", td->status_bitstuff_err);
 | 
				
			||||||
 | 
						printf (" CRC err: %x, ", td->status_crc_err);
 | 
				
			||||||
 | 
						printf (" NAK rcvd: %x, ", td->status_nakrcvd);
 | 
				
			||||||
 | 
						printf (" Babble: %x, ", td->status_babble);
 | 
				
			||||||
 | 
						printf (" Data Buffer err: %x, ", td->status_databuf_err);
 | 
				
			||||||
 | 
						printf (" Stalled: %x, ", td->status_stalled);
 | 
				
			||||||
 | 
						printf (" Active: %x\n", td->status_active);
 | 
				
			||||||
 | 
						if (td->status_babble)
 | 
				
			||||||
 | 
							printf (" Babble because of %s\n",
 | 
				
			||||||
 | 
								td->status_bitstuff_err ? "host" : "device");
 | 
				
			||||||
 | 
						if (td->status_active)
 | 
				
			||||||
 | 
							printf (" still active - timeout?\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_reset (hci_t *controller)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* reset */
 | 
				
			||||||
 | 
						uhci_reg_write16 (controller, USBCMD, 4);
 | 
				
			||||||
 | 
						mdelay (50);
 | 
				
			||||||
 | 
						uhci_reg_write16 (controller, USBCMD, 0);
 | 
				
			||||||
 | 
						mdelay (10);
 | 
				
			||||||
 | 
						uhci_reg_write16 (controller, USBCMD, 2);
 | 
				
			||||||
 | 
						while ((uhci_reg_read16 (controller, USBCMD) & 2) != 0)
 | 
				
			||||||
 | 
							mdelay (1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhci_reg_write32 (controller, FLBASEADD,
 | 
				
			||||||
 | 
								  (u32) virt_to_phys (UHCI_INST (controller)->
 | 
				
			||||||
 | 
										      framelistptr));
 | 
				
			||||||
 | 
						//printf ("framelist at %p\n",UHCI_INST(controller)->framelistptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* disable irqs */
 | 
				
			||||||
 | 
						uhci_reg_write16 (controller, USBINTR, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* reset framelist index */
 | 
				
			||||||
 | 
						uhci_reg_write16 (controller, FRNUM, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, USBCMD, ~0, 0xc0);	// max packets, configure flag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhci_start (controller);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hci_t *
 | 
				
			||||||
 | 
					uhci_init (pcidev_t addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						hci_t *controller = new_controller ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						controller->instance = malloc (sizeof (uhci_t));
 | 
				
			||||||
 | 
						controller->start = uhci_start;
 | 
				
			||||||
 | 
						controller->stop = uhci_stop;
 | 
				
			||||||
 | 
						controller->reset = uhci_reset;
 | 
				
			||||||
 | 
						controller->shutdown = uhci_shutdown;
 | 
				
			||||||
 | 
						controller->packet = uhci_packet;
 | 
				
			||||||
 | 
						controller->bulk = uhci_bulk;
 | 
				
			||||||
 | 
						controller->control = uhci_control;
 | 
				
			||||||
 | 
						UHCI_INST (controller)->roothub = &(controller->devices[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						controller->bus_address = addr;
 | 
				
			||||||
 | 
						controller->reg_base = pci_read_config32 (controller->bus_address, 0x20) & ~1;	/* ~1 clears the register type indicator that is set to 1 for IO space */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* kill legacy support handler */
 | 
				
			||||||
 | 
						uhci_stop (controller);
 | 
				
			||||||
 | 
						mdelay (1);
 | 
				
			||||||
 | 
						uhci_reg_write16 (controller, USBSTS, 0x3f);
 | 
				
			||||||
 | 
						pci_write_config32 (controller->bus_address, 0xc0, 0x8f00);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UHCI_INST (controller)->framelistptr = memalign (0x1000, 1024 * sizeof (flistp_t *));	/* 4kb aligned to 4kb */
 | 
				
			||||||
 | 
						memset (UHCI_INST (controller)->framelistptr, 0,
 | 
				
			||||||
 | 
							1024 * sizeof (flistp_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_intr = memalign (16, sizeof (qh_t));
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_data = memalign (16, sizeof (qh_t));
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_last = memalign (16, sizeof (qh_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_intr->headlinkptr.ptr =
 | 
				
			||||||
 | 
							virt_to_phys (UHCI_INST (controller)->qh_data);
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_intr->headlinkptr.queue_head = 1;
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_intr->elementlinkptr.ptr = 0;
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_intr->elementlinkptr.terminate = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_data->headlinkptr.ptr =
 | 
				
			||||||
 | 
							virt_to_phys (UHCI_INST (controller)->qh_last);
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_data->headlinkptr.queue_head = 1;
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_data->elementlinkptr.ptr = 0;
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_data->elementlinkptr.terminate = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_last->headlinkptr.ptr = 0;
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_last->headlinkptr.terminate = 1;
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_last->elementlinkptr.ptr = 0;
 | 
				
			||||||
 | 
						UHCI_INST (controller)->qh_last->elementlinkptr.terminate = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < 1024; i++) {
 | 
				
			||||||
 | 
							UHCI_INST (controller)->framelistptr[i].ptr =
 | 
				
			||||||
 | 
								virt_to_phys (UHCI_INST (controller)->qh_intr);
 | 
				
			||||||
 | 
							UHCI_INST (controller)->framelistptr[i].terminate = 0;
 | 
				
			||||||
 | 
							UHCI_INST (controller)->framelistptr[i].queue_head = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for (i = 1; i < 128; i++) {
 | 
				
			||||||
 | 
							init_device_entry (controller, i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						controller->devices[0].controller = controller;
 | 
				
			||||||
 | 
						controller->devices[0].init = uhci_rh_init;
 | 
				
			||||||
 | 
						controller->devices[0].init (&controller->devices[0]);
 | 
				
			||||||
 | 
						uhci_reset (controller);
 | 
				
			||||||
 | 
						return controller;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_shutdown (hci_t *controller)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (controller == 0)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						detach_controller (controller);
 | 
				
			||||||
 | 
						UHCI_INST (controller)->roothub->destroy (UHCI_INST (controller)->
 | 
				
			||||||
 | 
											  roothub);
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, USBCMD, 0, 0);	// stop work
 | 
				
			||||||
 | 
						free (UHCI_INST (controller)->framelistptr);
 | 
				
			||||||
 | 
						free (UHCI_INST (controller)->qh_intr);
 | 
				
			||||||
 | 
						free (UHCI_INST (controller)->qh_data);
 | 
				
			||||||
 | 
						free (UHCI_INST (controller)->qh_last);
 | 
				
			||||||
 | 
						free (UHCI_INST (controller));
 | 
				
			||||||
 | 
						free (controller);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_start (hci_t *controller)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, USBCMD, ~0, 1);	// start work on schedule
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_stop (hci_t *controller)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, USBCMD, ~1, 0);	// stop work on schedule
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GET_TD(x) ((void*)(((unsigned int)(x))&~0xf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static td_t *
 | 
				
			||||||
 | 
					wait_for_completed_qh (hci_t *controller, qh_t *qh)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int timeout = 1000;	/* max 30 ms. */
 | 
				
			||||||
 | 
						void *current = GET_TD (qh->elementlinkptr.ptr);
 | 
				
			||||||
 | 
						while ((qh->elementlinkptr.terminate == 0) && (timeout-- > 0)) {
 | 
				
			||||||
 | 
							if (current != GET_TD (qh->elementlinkptr.ptr)) {
 | 
				
			||||||
 | 
								current = GET_TD (qh->elementlinkptr.ptr);
 | 
				
			||||||
 | 
								timeout = 1000;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							uhci_reg_mask16 (controller, USBSTS, ~0, 0);	// clear resettable registers
 | 
				
			||||||
 | 
							udelay (30);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return (GET_TD (qh->elementlinkptr.ptr) ==
 | 
				
			||||||
 | 
							0) ? 0 : GET_TD (phys_to_virt (qh->elementlinkptr.ptr));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					wait_for_completed_td (hci_t *controller, td_t *td)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int timeout = 10000;
 | 
				
			||||||
 | 
						while ((td->status_active == 1)
 | 
				
			||||||
 | 
						       && ((uhci_reg_read16 (controller, USBSTS) & 2) == 0)
 | 
				
			||||||
 | 
						       && (timeout-- > 0)) {
 | 
				
			||||||
 | 
							uhci_reg_mask16 (controller, USBSTS, ~0, 0);	// clear resettable registers
 | 
				
			||||||
 | 
							udelay (10);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					maxlen (int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (size - 1) & 0x7ff;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					min (int a, int b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (a < b)
 | 
				
			||||||
 | 
							return a;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return b;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					uhci_control (usbdev_t *dev, pid_t dir, int drlen, void *devreq, int dalen,
 | 
				
			||||||
 | 
						      unsigned char *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int endp = 0;		/* this is control: always 0 */
 | 
				
			||||||
 | 
						int mlen = dev->endpoints[0].maxpacketsize;
 | 
				
			||||||
 | 
						int count = (2 + (dalen + mlen - 1) / mlen);
 | 
				
			||||||
 | 
						unsigned short req = ((unsigned short *) devreq)[0];
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						td_t *tds = memalign (16, sizeof (td_t) * count);
 | 
				
			||||||
 | 
						memset (tds, 0, sizeof (td_t) * count);
 | 
				
			||||||
 | 
						count--;		/* to compensate for 0-indexed array */
 | 
				
			||||||
 | 
						for (i = 0; i < count; i++) {
 | 
				
			||||||
 | 
							tds[i].ptr = virt_to_phys (&tds[i + 1]);
 | 
				
			||||||
 | 
							tds[i].depth_first = 1;
 | 
				
			||||||
 | 
							tds[i].terminate = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tds[count].ptr = 0;
 | 
				
			||||||
 | 
						tds[count].depth_first = 1;
 | 
				
			||||||
 | 
						tds[count].terminate = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tds[0].pid = SETUP;
 | 
				
			||||||
 | 
						tds[0].dev_addr = dev->address;
 | 
				
			||||||
 | 
						tds[0].endp = endp;
 | 
				
			||||||
 | 
						tds[0].maxlen = maxlen (drlen);
 | 
				
			||||||
 | 
						tds[0].counter = 3;
 | 
				
			||||||
 | 
						tds[0].data_toggle = 0;
 | 
				
			||||||
 | 
						tds[0].lowspeed = dev->lowspeed;
 | 
				
			||||||
 | 
						tds[0].bufptr = virt_to_phys (devreq);
 | 
				
			||||||
 | 
						tds[0].status_active = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int toggle = 1;
 | 
				
			||||||
 | 
						for (i = 1; i < count; i++) {
 | 
				
			||||||
 | 
							tds[i].pid = dir;
 | 
				
			||||||
 | 
							tds[i].dev_addr = dev->address;
 | 
				
			||||||
 | 
							tds[i].endp = endp;
 | 
				
			||||||
 | 
							tds[i].maxlen = maxlen (min (mlen, dalen));
 | 
				
			||||||
 | 
							tds[i].counter = 3;
 | 
				
			||||||
 | 
							tds[i].data_toggle = toggle;
 | 
				
			||||||
 | 
							tds[i].lowspeed = dev->lowspeed;
 | 
				
			||||||
 | 
							tds[i].bufptr = virt_to_phys (data);
 | 
				
			||||||
 | 
							tds[i].status_active = 1;
 | 
				
			||||||
 | 
							toggle ^= 1;
 | 
				
			||||||
 | 
							dalen -= mlen;
 | 
				
			||||||
 | 
							data += mlen;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tds[count].pid = (dir == OUT) ? IN : OUT;
 | 
				
			||||||
 | 
						tds[count].dev_addr = dev->address;
 | 
				
			||||||
 | 
						tds[count].endp = endp;
 | 
				
			||||||
 | 
						tds[count].maxlen = maxlen (0);
 | 
				
			||||||
 | 
						tds[count].counter = 0;	/* as per linux 2.4.10 */
 | 
				
			||||||
 | 
						tds[count].data_toggle = 1;
 | 
				
			||||||
 | 
						tds[count].lowspeed = dev->lowspeed, tds[count].bufptr = 0;
 | 
				
			||||||
 | 
						tds[count].status_active = 1;
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
 | 
				
			||||||
 | 
							virt_to_phys (tds);
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
 | 
				
			||||||
 | 
						td_t *td = wait_for_completed_qh (dev->controller,
 | 
				
			||||||
 | 
										  UHCI_INST (dev->controller)->
 | 
				
			||||||
 | 
										  qh_data);
 | 
				
			||||||
 | 
						int result;
 | 
				
			||||||
 | 
						if (td == 0) {
 | 
				
			||||||
 | 
							result = 0;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							printf ("control packet, req %x\n", req);
 | 
				
			||||||
 | 
							td_dump (td);
 | 
				
			||||||
 | 
							result = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						free (tds);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					uhci_packet (usbdev_t *dev, int endp, int pid, int toggle, int length,
 | 
				
			||||||
 | 
						     unsigned char *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static td_t *td = 0;
 | 
				
			||||||
 | 
						if (td == 0)
 | 
				
			||||||
 | 
							td = memalign (16, sizeof (td_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset (td, 0, sizeof (td_t));
 | 
				
			||||||
 | 
						td->ptr = 0;
 | 
				
			||||||
 | 
						td->terminate = 1;
 | 
				
			||||||
 | 
						td->queue_head = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						td->pid = pid;
 | 
				
			||||||
 | 
						td->dev_addr = dev->address;
 | 
				
			||||||
 | 
						td->endp = endp & 0xf;
 | 
				
			||||||
 | 
						td->maxlen = maxlen (length);
 | 
				
			||||||
 | 
						if (pid == SETUP)
 | 
				
			||||||
 | 
							td->counter = 3;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							td->counter = 0;
 | 
				
			||||||
 | 
						td->data_toggle = toggle & 1;
 | 
				
			||||||
 | 
						td->lowspeed = dev->lowspeed;
 | 
				
			||||||
 | 
						td->bufptr = virt_to_phys (data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						td->status_active = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
 | 
				
			||||||
 | 
							virt_to_phys (td);
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
 | 
				
			||||||
 | 
						wait_for_completed_td (dev->controller, td);
 | 
				
			||||||
 | 
						if ((td->status & 0x7f) == 0) {
 | 
				
			||||||
 | 
							//printf("successfully sent a %x packet to %x.%x\n",pid, dev->address,endp);
 | 
				
			||||||
 | 
							// success
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							td_dump (td);
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static td_t *
 | 
				
			||||||
 | 
					create_schedule (int numpackets)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (numpackets == 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						td_t *tds = memalign (16, sizeof (td_t) * numpackets);
 | 
				
			||||||
 | 
						memset (tds, 0, sizeof (td_t) * numpackets);
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						for (i = 0; i < numpackets; i++) {
 | 
				
			||||||
 | 
							tds[i].ptr = virt_to_phys (&tds[i + 1]);
 | 
				
			||||||
 | 
							tds[i].terminate = 0;
 | 
				
			||||||
 | 
							tds[i].queue_head = 0;
 | 
				
			||||||
 | 
							tds[i].depth_first = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tds[numpackets - 1].ptr = 0;
 | 
				
			||||||
 | 
						tds[numpackets - 1].terminate = 1;
 | 
				
			||||||
 | 
						tds[numpackets - 1].queue_head = 0;
 | 
				
			||||||
 | 
						tds[numpackets - 1].depth_first = 0;
 | 
				
			||||||
 | 
						return tds;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					fill_schedule (td_t *td, endpoint_t *ep, int length, unsigned char *data,
 | 
				
			||||||
 | 
						       int *toggle)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						td->pid = ep->direction;
 | 
				
			||||||
 | 
						td->dev_addr = ep->dev->address;
 | 
				
			||||||
 | 
						td->endp = ep->endpoint & 0xf;
 | 
				
			||||||
 | 
						td->maxlen = maxlen (length);
 | 
				
			||||||
 | 
						if (ep->direction == SETUP)
 | 
				
			||||||
 | 
							td->counter = 3;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							td->counter = 0;
 | 
				
			||||||
 | 
						td->data_toggle = *toggle & 1;
 | 
				
			||||||
 | 
						td->lowspeed = ep->dev->lowspeed;
 | 
				
			||||||
 | 
						td->bufptr = virt_to_phys (data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						td->status_active = 1;
 | 
				
			||||||
 | 
						*toggle ^= 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					run_schedule (usbdev_t *dev, td_t *td)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.ptr =
 | 
				
			||||||
 | 
							virt_to_phys (td);
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.queue_head = 0;
 | 
				
			||||||
 | 
						UHCI_INST (dev->controller)->qh_data->elementlinkptr.terminate = 0;
 | 
				
			||||||
 | 
						td = wait_for_completed_qh (dev->controller,
 | 
				
			||||||
 | 
									    UHCI_INST (dev->controller)->qh_data);
 | 
				
			||||||
 | 
						if (td == 0) {
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							td_dump (td);
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* finalize == 1: if data is of packet aligned size, add a zero length packet */
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					uhci_bulk (endpoint_t *ep, int size, u8 *data, int finalize)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int maxpsize = ep->maxpacketsize;
 | 
				
			||||||
 | 
						if (maxpsize == 0)
 | 
				
			||||||
 | 
							fatal ("MaxPacketSize == 0!!!");
 | 
				
			||||||
 | 
						int numpackets = (size + maxpsize - 1 + finalize) / maxpsize;
 | 
				
			||||||
 | 
						if (numpackets == 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						td_t *tds = create_schedule (numpackets);
 | 
				
			||||||
 | 
						int i = 0, toggle = ep->toggle;
 | 
				
			||||||
 | 
						while ((size > 0) || ((size == 0) && (finalize != 0))) {
 | 
				
			||||||
 | 
							fill_schedule (&tds[i], ep, min (size, maxpsize), data,
 | 
				
			||||||
 | 
								       &toggle);
 | 
				
			||||||
 | 
							i++;
 | 
				
			||||||
 | 
							data += maxpsize;
 | 
				
			||||||
 | 
							size -= maxpsize;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (run_schedule (ep->dev, tds) == 1) {
 | 
				
			||||||
 | 
							clear_stall (ep);
 | 
				
			||||||
 | 
							free (tds);
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ep->toggle = toggle;
 | 
				
			||||||
 | 
						free (tds);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						outl (value, ctrl->reg_base + reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u32
 | 
				
			||||||
 | 
					uhci_reg_read32 (hci_t *ctrl, usbreg reg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return inl (ctrl->reg_base + reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						outw (value, ctrl->reg_base + reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u16
 | 
				
			||||||
 | 
					uhci_reg_read16 (hci_t *ctrl, usbreg reg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return inw (ctrl->reg_base + reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						outb (value, ctrl->reg_base + reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u8
 | 
				
			||||||
 | 
					uhci_reg_read8 (hci_t *ctrl, usbreg reg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return inb (ctrl->reg_base + reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					uhci_reg_mask32 (hci_t *ctrl, usbreg reg, u32 andmask, u32 ormask)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uhci_reg_write32 (ctrl, reg,
 | 
				
			||||||
 | 
								  (uhci_reg_read32 (ctrl, reg) & andmask) | ormask);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					uhci_reg_mask16 (hci_t *ctrl, usbreg reg, u16 andmask, u16 ormask)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uhci_reg_write16 (ctrl, reg,
 | 
				
			||||||
 | 
								  (uhci_reg_read16 (ctrl, reg) & andmask) | ormask);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					uhci_reg_mask8 (hci_t *ctrl, usbreg reg, u8 andmask, u8 ormask)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uhci_reg_write8 (ctrl, reg,
 | 
				
			||||||
 | 
								 (uhci_reg_read8 (ctrl, reg) & andmask) | ormask);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										124
									
								
								payloads/libpayload/drivers/usb/uhci.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								payloads/libpayload/drivers/usb/uhci.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __UHCI_H
 | 
				
			||||||
 | 
					#define __UHCI_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pci.h>
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef union {
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							unsigned long terminate:1;
 | 
				
			||||||
 | 
							unsigned long queue_head:1;
 | 
				
			||||||
 | 
							unsigned long:2;
 | 
				
			||||||
 | 
							unsigned long ptr_part:28;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						u32 ptr;
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) flistp_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								unsigned long terminate:1;
 | 
				
			||||||
 | 
								unsigned long queue_head:1;
 | 
				
			||||||
 | 
								unsigned long depth_first:1;
 | 
				
			||||||
 | 
								unsigned long:29;
 | 
				
			||||||
 | 
							} __attribute__ ((packed));
 | 
				
			||||||
 | 
							u32 ptr;
 | 
				
			||||||
 | 
						} __attribute__ ((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						volatile unsigned long actlen:11;
 | 
				
			||||||
 | 
						volatile unsigned long:5;
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								unsigned long:1;	// bit 0
 | 
				
			||||||
 | 
								unsigned long status_bitstuff_err:1;
 | 
				
			||||||
 | 
								unsigned long status_crc_err:1;
 | 
				
			||||||
 | 
								unsigned long status_nakrcvd:1;
 | 
				
			||||||
 | 
								unsigned long status_babble:1;
 | 
				
			||||||
 | 
								unsigned long status_databuf_err:1;
 | 
				
			||||||
 | 
								unsigned long status_stalled:1;
 | 
				
			||||||
 | 
								unsigned long status_active:1;	// bit 7
 | 
				
			||||||
 | 
							} __attribute__ ((packed));
 | 
				
			||||||
 | 
							unsigned char status;
 | 
				
			||||||
 | 
						} __attribute__ ((packed));
 | 
				
			||||||
 | 
						volatile unsigned long ioc:1;	/* interrupt on complete */
 | 
				
			||||||
 | 
						volatile unsigned long isochronous:1;
 | 
				
			||||||
 | 
						volatile unsigned long lowspeed:1;
 | 
				
			||||||
 | 
						volatile unsigned long counter:2;
 | 
				
			||||||
 | 
						volatile unsigned long shortpck:1;
 | 
				
			||||||
 | 
						volatile unsigned long:2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned long pid:8;
 | 
				
			||||||
 | 
						unsigned long dev_addr:7;
 | 
				
			||||||
 | 
						unsigned long endp:4;
 | 
				
			||||||
 | 
						unsigned long data_toggle:1;
 | 
				
			||||||
 | 
						unsigned long:1;
 | 
				
			||||||
 | 
						unsigned long maxlen:11;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						u32 bufptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} __attribute__ ((packed))
 | 
				
			||||||
 | 
					     td_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     typedef struct {
 | 
				
			||||||
 | 
						     flistp_t headlinkptr;
 | 
				
			||||||
 | 
						     volatile flistp_t elementlinkptr;
 | 
				
			||||||
 | 
					     } __attribute__ ((packed))
 | 
				
			||||||
 | 
					     qh_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     typedef enum { USBCMD = 0, USBSTS = 2, USBINTR = 4, FRNUM =
 | 
				
			||||||
 | 
							     6, FLBASEADD = 8, SOFMOD = 0xc, PORTSC1 = 0x10, PORTSC2 =
 | 
				
			||||||
 | 
							     0x12
 | 
				
			||||||
 | 
					     } usbreg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     void uhci_reg_write32 (hci_t *ctrl, usbreg reg, u32 value);
 | 
				
			||||||
 | 
					     u32 uhci_reg_read32 (hci_t *ctrl, usbreg reg);
 | 
				
			||||||
 | 
					     void uhci_reg_write16 (hci_t *ctrl, usbreg reg, u16 value);
 | 
				
			||||||
 | 
					     u16 uhci_reg_read16 (hci_t *ctrl, usbreg reg);
 | 
				
			||||||
 | 
					     void uhci_reg_write8 (hci_t *ctrl, usbreg reg, u8 value);
 | 
				
			||||||
 | 
					     u8 uhci_reg_read8 (hci_t *ctrl, usbreg reg);
 | 
				
			||||||
 | 
					     void uhci_reg_mask32 (hci_t *ctrl, usbreg reg, u32 andmask, u32 ormask);
 | 
				
			||||||
 | 
					     void uhci_reg_mask16 (hci_t *ctrl, usbreg reg, u16 andmask, u16 ormask);
 | 
				
			||||||
 | 
					     void uhci_reg_mask8 (hci_t *ctrl, usbreg reg, u8 andmask, u8 ormask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     typedef struct uhci {
 | 
				
			||||||
 | 
						     flistp_t *framelistptr;
 | 
				
			||||||
 | 
						     qh_t *qh_intr, *qh_data, *qh_last;
 | 
				
			||||||
 | 
						     usbdev_t *roothub;
 | 
				
			||||||
 | 
					     } uhci_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define UHCI_INST(controller) ((uhci_t*)((controller)->instance))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     hci_t *uhci_init (pcidev_t addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     void uhci_rh_init (usbdev_t *dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										182
									
								
								payloads/libpayload/drivers/usb/uhci_rh.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								payloads/libpayload/drivers/usb/uhci_rh.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,182 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libpayload.h>
 | 
				
			||||||
 | 
					#include "uhci.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						int port[2];
 | 
				
			||||||
 | 
					} rh_inst_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RH_INST(dev) ((rh_inst_t*)(dev)->data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_rh_enable_port (usbdev_t *dev, int port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u16 value;
 | 
				
			||||||
 | 
						hci_t *controller = dev->controller;
 | 
				
			||||||
 | 
						if (port == 1)
 | 
				
			||||||
 | 
							port = PORTSC1;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							port = PORTSC2;
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, port, ~(1 << 12), 0);	/* wakeup */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, port, ~0, 1 << 9);	/* reset */
 | 
				
			||||||
 | 
						mdelay (30);		// >10ms
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, port, ~(1 << 9), 0);
 | 
				
			||||||
 | 
						mdelay (1);		// >5.3us per spec, <3ms because some devices make trouble
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, port, ~0, 1 << 2);	/* enable */
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							value = uhci_reg_read16 (controller, port);
 | 
				
			||||||
 | 
							mdelay (1);
 | 
				
			||||||
 | 
						} while (((value & (1 << 2)) == 0) && (value & 0x01));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* disable root hub */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_rh_disable_port (usbdev_t *dev, int port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						hci_t *controller = dev->controller;
 | 
				
			||||||
 | 
						port = PORTSC2;
 | 
				
			||||||
 | 
						if (port == 1)
 | 
				
			||||||
 | 
							port = PORTSC1;
 | 
				
			||||||
 | 
						uhci_reg_mask16 (controller, port, ~4, 0);
 | 
				
			||||||
 | 
						int value;
 | 
				
			||||||
 | 
						do {
 | 
				
			||||||
 | 
							value = uhci_reg_read16 (controller, port);
 | 
				
			||||||
 | 
							mdelay (1);
 | 
				
			||||||
 | 
						} while ((value & (1 << 2)) != 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_rh_scanport (usbdev_t *dev, int port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int portsc, offset;
 | 
				
			||||||
 | 
						if (port == 1) {
 | 
				
			||||||
 | 
							portsc = PORTSC1;
 | 
				
			||||||
 | 
							offset = 0;
 | 
				
			||||||
 | 
						} else if (port == 2) {
 | 
				
			||||||
 | 
							portsc = PORTSC2;
 | 
				
			||||||
 | 
							offset = 1;
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						int devno = RH_INST (dev)->port[offset];
 | 
				
			||||||
 | 
						if (devno != -1) {
 | 
				
			||||||
 | 
							dev->controller->devices[devno].destroy (&dev->controller->
 | 
				
			||||||
 | 
												 devices[devno]);
 | 
				
			||||||
 | 
							init_device_entry (dev->controller, devno);
 | 
				
			||||||
 | 
							RH_INST (dev)->port[offset] = -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						uhci_reg_mask16 (dev->controller, portsc, ~0, (1 << 3) | (1 << 2));	// clear port state change, enable port
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((uhci_reg_read16 (dev->controller, portsc) & 1) != 0) {
 | 
				
			||||||
 | 
							int newdev;
 | 
				
			||||||
 | 
							usbdev_t *newdev_t;
 | 
				
			||||||
 | 
							// device attached
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							uhci_rh_disable_port (dev, port);
 | 
				
			||||||
 | 
							uhci_rh_enable_port (dev, port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int lowspeed =
 | 
				
			||||||
 | 
								(uhci_reg_read16 (dev->controller, portsc) >> 8) & 1;
 | 
				
			||||||
 | 
							printf ("%sspeed device\n", (lowspeed == 1) ? "low" : "full");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							newdev = set_address (dev->controller, lowspeed);
 | 
				
			||||||
 | 
							if (newdev == -1)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							newdev_t = &dev->controller->devices[newdev];
 | 
				
			||||||
 | 
							RH_INST (dev)->port[offset] = newdev;
 | 
				
			||||||
 | 
							newdev_t->address = newdev;
 | 
				
			||||||
 | 
							newdev_t->hub = dev->address;
 | 
				
			||||||
 | 
							newdev_t->port = portsc;
 | 
				
			||||||
 | 
							// determine responsible driver
 | 
				
			||||||
 | 
							newdev_t->init (newdev_t);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					uhci_rh_report_port_changes (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int stored, real;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stored = (RH_INST (dev)->port[0] == -1);
 | 
				
			||||||
 | 
						real = ((uhci_reg_read16 (dev->controller, PORTSC1) & 1) == 0);
 | 
				
			||||||
 | 
						if (stored != real)
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stored = (RH_INST (dev)->port[1] == -1);
 | 
				
			||||||
 | 
						real = ((uhci_reg_read16 (dev->controller, PORTSC2) & 1) == 0);
 | 
				
			||||||
 | 
						if (stored != real)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// maybe detach+attach happened between two scans?
 | 
				
			||||||
 | 
						if ((uhci_reg_read16 (dev->controller, PORTSC1) & 2) > 0)
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						if ((uhci_reg_read16 (dev->controller, PORTSC2) & 2) > 0)
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// no change
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_rh_destroy (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uhci_rh_disable_port (dev, 1);
 | 
				
			||||||
 | 
						uhci_rh_disable_port (dev, 2);
 | 
				
			||||||
 | 
						free (RH_INST (dev));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					uhci_rh_poll (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int port;
 | 
				
			||||||
 | 
						while ((port = uhci_rh_report_port_changes (dev)) != -1)
 | 
				
			||||||
 | 
							uhci_rh_scanport (dev, port);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					uhci_rh_init (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dev->destroy = uhci_rh_destroy;
 | 
				
			||||||
 | 
						dev->poll = uhci_rh_poll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhci_rh_enable_port (dev, 1);
 | 
				
			||||||
 | 
						uhci_rh_enable_port (dev, 2);
 | 
				
			||||||
 | 
						dev->data = malloc (sizeof (rh_inst_t));
 | 
				
			||||||
 | 
						RH_INST (dev)->port[0] = -1;
 | 
				
			||||||
 | 
						RH_INST (dev)->port[1] = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* we can set them here because a root hub _really_ shouldn't
 | 
				
			||||||
 | 
						   appear elsewhere */
 | 
				
			||||||
 | 
						dev->address = 0;
 | 
				
			||||||
 | 
						dev->hub = -1;
 | 
				
			||||||
 | 
						dev->port = -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										350
									
								
								payloads/libpayload/drivers/usb/usb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								payloads/libpayload/drivers/usb/usb.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,350 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <config.h>
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hci_t *usb_hcs = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hci_t *
 | 
				
			||||||
 | 
					new_controller ()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						hci_t *controller = malloc (sizeof (hci_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* atomic */
 | 
				
			||||||
 | 
						controller->next = usb_hcs;
 | 
				
			||||||
 | 
						usb_hcs = controller;
 | 
				
			||||||
 | 
						/* atomic end */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return controller;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					detach_controller (hci_t *controller)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (controller == 0)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						if (usb_hcs == controller) {
 | 
				
			||||||
 | 
							usb_hcs = controller->next;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							hci_t *it = usb_hcs;
 | 
				
			||||||
 | 
							while (it != 0) {
 | 
				
			||||||
 | 
								if (it->next == controller) {
 | 
				
			||||||
 | 
									it->next = controller->next;
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Polls all hubs on all USB controllers, to find out about device changes
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					usb_poll ()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (usb_hcs == 0)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						hci_t *controller = usb_hcs;
 | 
				
			||||||
 | 
						while (controller != 0) {
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
							for (i = 0; i < 128; i++) {
 | 
				
			||||||
 | 
								if (controller->devices[i].address != -1) {
 | 
				
			||||||
 | 
									controller->devices[i].poll (&controller->
 | 
				
			||||||
 | 
												     devices[i]);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							controller = controller->next;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					init_device_entry (hci_t *controller, int i)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						controller->devices[i].controller = controller;
 | 
				
			||||||
 | 
						controller->devices[i].address = -1;
 | 
				
			||||||
 | 
						controller->devices[i].hub = -1;
 | 
				
			||||||
 | 
						controller->devices[i].port = -1;
 | 
				
			||||||
 | 
						controller->devices[i].init = usb_nop_init;
 | 
				
			||||||
 | 
						controller->devices[i].init (&controller->devices[i]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					set_feature (usbdev_t *dev, int endp, int feature, int rtype)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dev_req_t dr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dr.bmRequestType = rtype;
 | 
				
			||||||
 | 
						dr.data_dir = host_to_device;
 | 
				
			||||||
 | 
						dr.bRequest = SET_FEATURE;
 | 
				
			||||||
 | 
						dr.wValue = feature;
 | 
				
			||||||
 | 
						dr.wIndex = endp;
 | 
				
			||||||
 | 
						dr.wLength = 0;
 | 
				
			||||||
 | 
						dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					get_status (usbdev_t *dev, int intf, int rtype, int len, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dev_req_t dr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dr.bmRequestType = rtype;
 | 
				
			||||||
 | 
						dr.data_dir = device_to_host;
 | 
				
			||||||
 | 
						dr.bRequest = GET_STATUS;
 | 
				
			||||||
 | 
						dr.wValue = 0;
 | 
				
			||||||
 | 
						dr.wIndex = intf;
 | 
				
			||||||
 | 
						dr.wLength = len;
 | 
				
			||||||
 | 
						dev->controller->control (dev, IN, sizeof (dr), &dr, len, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u8 *
 | 
				
			||||||
 | 
					get_descriptor (usbdev_t *dev, unsigned char bmRequestType, int descType,
 | 
				
			||||||
 | 
							int descIdx, int langID)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u8 buf[8];
 | 
				
			||||||
 | 
						u8 *result;
 | 
				
			||||||
 | 
						dev_req_t dr;
 | 
				
			||||||
 | 
						int size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dr.bmRequestType = bmRequestType;
 | 
				
			||||||
 | 
						dr.data_dir = device_to_host;	// always like this for descriptors
 | 
				
			||||||
 | 
						dr.bRequest = GET_DESCRIPTOR;
 | 
				
			||||||
 | 
						dr.wValue = (descType << 8) | descIdx;
 | 
				
			||||||
 | 
						dr.wIndex = langID;
 | 
				
			||||||
 | 
						dr.wLength = 8;
 | 
				
			||||||
 | 
						if (dev->controller->control (dev, IN, sizeof (dr), &dr, 8, buf)) {
 | 
				
			||||||
 | 
							printf ("getting descriptor size (type %x) failed\n",
 | 
				
			||||||
 | 
								descType);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (descType == 1) {
 | 
				
			||||||
 | 
							device_descriptor_t *dd = (device_descriptor_t *) buf;
 | 
				
			||||||
 | 
							printf ("maxPacketSize0: %x\n", dd->bMaxPacketSize0);
 | 
				
			||||||
 | 
							if (dd->bMaxPacketSize0 != 0)
 | 
				
			||||||
 | 
								dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* special case for configuration descriptors: they carry all their
 | 
				
			||||||
 | 
						   subsequent descriptors with them, and keep the entire size at a
 | 
				
			||||||
 | 
						   different location */
 | 
				
			||||||
 | 
						size = buf[0];
 | 
				
			||||||
 | 
						if (buf[1] == 2) {
 | 
				
			||||||
 | 
							int realsize = ((unsigned short *) (buf + 2))[0];
 | 
				
			||||||
 | 
							size = realsize;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						result = malloc (size);
 | 
				
			||||||
 | 
						memset (result, 0, size);
 | 
				
			||||||
 | 
						dr.wLength = size;
 | 
				
			||||||
 | 
						if (dev->controller->
 | 
				
			||||||
 | 
						    control (dev, IN, sizeof (dr), &dr, size, result)) {
 | 
				
			||||||
 | 
							printf ("getting descriptor (type %x, size %x) failed\n",
 | 
				
			||||||
 | 
								descType, size);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					set_configuration (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dev_req_t dr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dr.bmRequestType = 0;
 | 
				
			||||||
 | 
						dr.bRequest = SET_CONFIGURATION;
 | 
				
			||||||
 | 
						dr.wValue = dev->configuration[5];
 | 
				
			||||||
 | 
						dr.wIndex = 0;
 | 
				
			||||||
 | 
						dr.wLength = 0;
 | 
				
			||||||
 | 
						dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					clear_stall (endpoint_t *ep)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						usbdev_t *dev = ep->dev;
 | 
				
			||||||
 | 
						int endp = ep->endpoint;
 | 
				
			||||||
 | 
						dev_req_t dr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dr.bmRequestType = 0;
 | 
				
			||||||
 | 
						if (endp != 0) {
 | 
				
			||||||
 | 
							dr.req_recp = endp_recp;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dr.bRequest = CLEAR_FEATURE;
 | 
				
			||||||
 | 
						dr.wValue = ENDPOINT_HALT;
 | 
				
			||||||
 | 
						dr.wIndex = endp;
 | 
				
			||||||
 | 
						dr.wLength = 0;
 | 
				
			||||||
 | 
						dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* returns free address or -1 */
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					get_free_address (hci_t *controller)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						for (i = 1; i < 128; i++) {
 | 
				
			||||||
 | 
							if (controller->devices[i].address != i)
 | 
				
			||||||
 | 
								return i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printf ("no free address found\n");
 | 
				
			||||||
 | 
						return -1;		// no free address
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					set_address (hci_t *controller, int lowspeed)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int adr = get_free_address (controller);	// address to set
 | 
				
			||||||
 | 
						dev_req_t dr;
 | 
				
			||||||
 | 
						configuration_descriptor_t *cd;
 | 
				
			||||||
 | 
						device_descriptor_t *dd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memset (&dr, 0, sizeof (dr));
 | 
				
			||||||
 | 
						dr.data_dir = host_to_device;
 | 
				
			||||||
 | 
						dr.req_type = standard_type;
 | 
				
			||||||
 | 
						dr.req_recp = dev_recp;
 | 
				
			||||||
 | 
						dr.bRequest = SET_ADDRESS;
 | 
				
			||||||
 | 
						dr.wValue = adr;
 | 
				
			||||||
 | 
						dr.wIndex = 0;
 | 
				
			||||||
 | 
						dr.wLength = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						usbdev_t *dev = &controller->devices[adr];
 | 
				
			||||||
 | 
						// dummy values for registering the address
 | 
				
			||||||
 | 
						dev->address = 0;
 | 
				
			||||||
 | 
						dev->lowspeed = lowspeed;
 | 
				
			||||||
 | 
						dev->endpoints[0].dev = dev;
 | 
				
			||||||
 | 
						dev->endpoints[0].endpoint = 0;
 | 
				
			||||||
 | 
						dev->endpoints[0].maxpacketsize = 8;
 | 
				
			||||||
 | 
						dev->endpoints[0].toggle = 0;
 | 
				
			||||||
 | 
						dev->endpoints[0].direction = SETUP;
 | 
				
			||||||
 | 
						mdelay (50);
 | 
				
			||||||
 | 
						if (dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0)) {
 | 
				
			||||||
 | 
							printf ("set_address failed\n");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mdelay (50);
 | 
				
			||||||
 | 
						dev->address = adr;
 | 
				
			||||||
 | 
						dev->descriptor =
 | 
				
			||||||
 | 
							get_descriptor (dev,
 | 
				
			||||||
 | 
									gen_bmRequestType (device_to_host,
 | 
				
			||||||
 | 
											   standard_type, dev_recp),
 | 
				
			||||||
 | 
									1, 0, 0);
 | 
				
			||||||
 | 
						dd = (device_descriptor_t *) dev->descriptor;
 | 
				
			||||||
 | 
						printf ("device version: %x.%x\n", dd->bcdUSB >> 8,
 | 
				
			||||||
 | 
							dd->bcdUSB & 0xff);
 | 
				
			||||||
 | 
						printf ("device has %x configurations\n", dd->bNumConfigurations);
 | 
				
			||||||
 | 
						if (dd->bNumConfigurations == 0) {
 | 
				
			||||||
 | 
							/* device isn't usable */
 | 
				
			||||||
 | 
							printf ("no usable configuration!\n");
 | 
				
			||||||
 | 
							dev->address = 0;
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dev->configuration =
 | 
				
			||||||
 | 
							get_descriptor (dev,
 | 
				
			||||||
 | 
									gen_bmRequestType (device_to_host,
 | 
				
			||||||
 | 
											   standard_type, dev_recp),
 | 
				
			||||||
 | 
									2, 0, 0);
 | 
				
			||||||
 | 
						cd = (configuration_descriptor_t *) dev->configuration;
 | 
				
			||||||
 | 
						set_configuration (dev);
 | 
				
			||||||
 | 
						interface_descriptor_t *interface =
 | 
				
			||||||
 | 
							(interface_descriptor_t *) (((char *) cd) + cd->bLength);
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int i;
 | 
				
			||||||
 | 
							int num = cd->bNumInterfaces;
 | 
				
			||||||
 | 
							interface_descriptor_t *current = interface;
 | 
				
			||||||
 | 
							printf ("device has %x interfaces\n", num);
 | 
				
			||||||
 | 
							num = (num > 5) ? 5 : num;
 | 
				
			||||||
 | 
							for (i = 0; i < num; i++) {
 | 
				
			||||||
 | 
								int j;
 | 
				
			||||||
 | 
								printf (" #%x has %x endpoints, interface %x:%x, protocol %x\n", current->bInterfaceNumber, current->bNumEndpoints, current->bInterfaceClass, current->bInterfaceSubClass, current->bInterfaceProtocol);
 | 
				
			||||||
 | 
								endpoint_descriptor_t *endp =
 | 
				
			||||||
 | 
									(endpoint_descriptor_t *) (((char *) current)
 | 
				
			||||||
 | 
												   +
 | 
				
			||||||
 | 
												   current->bLength);
 | 
				
			||||||
 | 
								if (interface->bInterfaceClass == 0x3)
 | 
				
			||||||
 | 
									endp = (endpoint_descriptor_t *) (((char *) endp) + ((char *) endp)[0]);	// ignore HID descriptor
 | 
				
			||||||
 | 
								memset (dev->endpoints, 0, sizeof (dev->endpoints));
 | 
				
			||||||
 | 
								dev->num_endp = 1;	// 0 always exists
 | 
				
			||||||
 | 
								dev->endpoints[0].dev = dev;
 | 
				
			||||||
 | 
								dev->endpoints[0].maxpacketsize = dd->bMaxPacketSize0;
 | 
				
			||||||
 | 
								dev->endpoints[0].direction = SETUP;
 | 
				
			||||||
 | 
								dev->endpoints[0].type = CONTROL;
 | 
				
			||||||
 | 
								for (j = 1; j <= current->bNumEndpoints; j++) {
 | 
				
			||||||
 | 
									static const char *transfertypes[4] =
 | 
				
			||||||
 | 
										{ "control", "isochronous", "bulk",
 | 
				
			||||||
 | 
										"interrupt"
 | 
				
			||||||
 | 
									};
 | 
				
			||||||
 | 
									printf ("   #%x: Endpoint %x (%s), max packet size %x, type %s\n", j, endp->bEndpointAddress & 0x7f, ((endp->bEndpointAddress & 0x80) != 0) ? "in" : "out", endp->wMaxPacketSize, transfertypes[endp->bmAttributes]);
 | 
				
			||||||
 | 
									endpoint_t *ep =
 | 
				
			||||||
 | 
										&dev->endpoints[dev->num_endp++];
 | 
				
			||||||
 | 
									ep->dev = dev;
 | 
				
			||||||
 | 
									ep->endpoint = endp->bEndpointAddress;
 | 
				
			||||||
 | 
									ep->toggle = 0;
 | 
				
			||||||
 | 
									ep->maxpacketsize = endp->wMaxPacketSize;
 | 
				
			||||||
 | 
									ep->direction =
 | 
				
			||||||
 | 
										((endp->bEndpointAddress & 0x80) ==
 | 
				
			||||||
 | 
										 0) ? OUT : IN;
 | 
				
			||||||
 | 
									ep->type = endp->bmAttributes;
 | 
				
			||||||
 | 
									endp = (endpoint_descriptor_t
 | 
				
			||||||
 | 
										*) (((char *) endp) + endp->bLength);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								current = (interface_descriptor_t *) endp;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int class = dd->bDeviceClass;
 | 
				
			||||||
 | 
						if (class == 0)
 | 
				
			||||||
 | 
							class = interface->bInterfaceClass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum { hid_device = 0x3, msc_device = 0x8, hub_device = 0x9 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf ("device of class %x found\n", class);
 | 
				
			||||||
 | 
						if (class == hub_device) {
 | 
				
			||||||
 | 
							printf ("hub found\n");
 | 
				
			||||||
 | 
					#ifdef CONFIG_USB_HUB
 | 
				
			||||||
 | 
							controller->devices[adr].init = usb_hub_init;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							printf ("support not compiled in\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (class == hid_device) {
 | 
				
			||||||
 | 
							printf ("HID found\n");
 | 
				
			||||||
 | 
					#ifdef CONFIG_USB_HID
 | 
				
			||||||
 | 
							controller->devices[adr].init = usb_hid_init;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							printf ("support not compiled in\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (class == msc_device) {
 | 
				
			||||||
 | 
							printf ("MSC found\n");
 | 
				
			||||||
 | 
					#ifdef CONFIG_USB_MSC
 | 
				
			||||||
 | 
							controller->devices[adr].init = usb_msc_init;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							printf ("support not compiled in\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return adr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										224
									
								
								payloads/libpayload/drivers/usb/usb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								payloads/libpayload/drivers/usb/usb.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,224 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __USB_H
 | 
				
			||||||
 | 
					#define __USB_H
 | 
				
			||||||
 | 
					#include <libpayload.h>
 | 
				
			||||||
 | 
					#include <pci.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum { host_to_device = 0, device_to_host = 1 } dev_req_dir;
 | 
				
			||||||
 | 
					typedef enum { standard_type = 0, class_type = 1, vendor_type =
 | 
				
			||||||
 | 
							2, reserved_type = 3
 | 
				
			||||||
 | 
					} dev_req_type;
 | 
				
			||||||
 | 
					typedef enum { dev_recp = 0, iface_recp = 1, endp_recp = 2, other_recp = 3
 | 
				
			||||||
 | 
					} dev_req_recp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
						GET_STATUS = 0,
 | 
				
			||||||
 | 
						CLEAR_FEATURE = 1,
 | 
				
			||||||
 | 
						SET_FEATURE = 3,
 | 
				
			||||||
 | 
						SET_ADDRESS = 5,
 | 
				
			||||||
 | 
						GET_DESCRIPTOR = 6,
 | 
				
			||||||
 | 
						SET_DESCRIPTOR = 7,
 | 
				
			||||||
 | 
						GET_CONFIGURATION = 8,
 | 
				
			||||||
 | 
						SET_CONFIGURATION = 9,
 | 
				
			||||||
 | 
						GET_INTERFACE = 10,
 | 
				
			||||||
 | 
						SET_INTERFACE = 11,
 | 
				
			||||||
 | 
						SYNCH_FRAME = 12
 | 
				
			||||||
 | 
					} bRequest_Codes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
						ENDPOINT_HALT = 0,
 | 
				
			||||||
 | 
						DEVICE_REMOTE_WAKEUP = 1,
 | 
				
			||||||
 | 
						TEST_MODE = 2
 | 
				
			||||||
 | 
					} feature_selectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								dev_req_recp req_recp:5;
 | 
				
			||||||
 | 
								dev_req_type req_type:2;
 | 
				
			||||||
 | 
								dev_req_dir data_dir:1;
 | 
				
			||||||
 | 
							} __attribute__ ((packed));
 | 
				
			||||||
 | 
							unsigned char bmRequestType;
 | 
				
			||||||
 | 
						} __attribute__ ((packed));
 | 
				
			||||||
 | 
						unsigned char bRequest;
 | 
				
			||||||
 | 
						unsigned short wValue;
 | 
				
			||||||
 | 
						unsigned short wIndex;
 | 
				
			||||||
 | 
						unsigned short wLength;
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) dev_req_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct usbdev_hc;
 | 
				
			||||||
 | 
					typedef struct usbdev_hc hci_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct usbdev;
 | 
				
			||||||
 | 
					typedef struct usbdev usbdev_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum { SETUP = 0x2d, IN = 0x69, OUT = 0xe1 } pid_t;
 | 
				
			||||||
 | 
					typedef enum { CONTROL = 0, ISOCHRONOUS = 1, BULK = 2, INTERRUPT = 3
 | 
				
			||||||
 | 
					} endpoint_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						usbdev_t *dev;
 | 
				
			||||||
 | 
						int endpoint;
 | 
				
			||||||
 | 
						pid_t direction;
 | 
				
			||||||
 | 
						int toggle;
 | 
				
			||||||
 | 
						int maxpacketsize;
 | 
				
			||||||
 | 
						endpoint_type type;
 | 
				
			||||||
 | 
					} endpoint_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct usbdev {
 | 
				
			||||||
 | 
						hci_t *controller;
 | 
				
			||||||
 | 
						endpoint_t endpoints[32];
 | 
				
			||||||
 | 
						int num_endp;
 | 
				
			||||||
 | 
						int address;		// usb address
 | 
				
			||||||
 | 
						int hub;		// hub, device is attached to
 | 
				
			||||||
 | 
						int port;		// port where device is attached
 | 
				
			||||||
 | 
						int lowspeed;		// 1 if lowspeed device
 | 
				
			||||||
 | 
						void *data;
 | 
				
			||||||
 | 
						u8 *descriptor;
 | 
				
			||||||
 | 
						u8 *configuration;
 | 
				
			||||||
 | 
						void (*init) (usbdev_t *dev);
 | 
				
			||||||
 | 
						void (*destroy) (usbdev_t *dev);
 | 
				
			||||||
 | 
						void (*poll) (usbdev_t *dev);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct usbdev_hc {
 | 
				
			||||||
 | 
						struct usbdev_hc *next;
 | 
				
			||||||
 | 
						pcidev_t bus_address;
 | 
				
			||||||
 | 
						u32 reg_base;
 | 
				
			||||||
 | 
						usbdev_t devices[128];	// dev 0 is root hub, 127 is last addressable
 | 
				
			||||||
 | 
						void (*start) (hci_t *controller);
 | 
				
			||||||
 | 
						void (*stop) (hci_t *controller);
 | 
				
			||||||
 | 
						void (*reset) (hci_t *controller);
 | 
				
			||||||
 | 
						void (*shutdown) (hci_t *controller);
 | 
				
			||||||
 | 
						int (*packet) (usbdev_t *dev, int endp, int pid, int toggle,
 | 
				
			||||||
 | 
							       int length, u8 *data);
 | 
				
			||||||
 | 
						int (*bulk) (endpoint_t *ep, int size, u8 *data, int finalize);
 | 
				
			||||||
 | 
						int (*control) (usbdev_t *dev, pid_t pid, int dr_length,
 | 
				
			||||||
 | 
								void *devreq, int data_length, u8 *data);
 | 
				
			||||||
 | 
						void *instance;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned char bDescLength;
 | 
				
			||||||
 | 
						unsigned char bDescriptorType;
 | 
				
			||||||
 | 
						unsigned char bNbrPorts;
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								unsigned long logicalPowerSwitchingMode:2;
 | 
				
			||||||
 | 
								unsigned long isCompoundDevice:1;
 | 
				
			||||||
 | 
								unsigned long overcurrentProtectionMode:2;
 | 
				
			||||||
 | 
								unsigned long ttThinkTime:2;
 | 
				
			||||||
 | 
								unsigned long arePortIndicatorsSupported:1;
 | 
				
			||||||
 | 
								unsigned long:8;
 | 
				
			||||||
 | 
							} __attribute__ ((packed));
 | 
				
			||||||
 | 
							unsigned short wHubCharacteristics;
 | 
				
			||||||
 | 
						} __attribute__ ((packed));
 | 
				
			||||||
 | 
						unsigned char bPowerOn2PwrGood;
 | 
				
			||||||
 | 
						unsigned char bHubContrCurrent;
 | 
				
			||||||
 | 
						char DeviceRemovable[];
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) hub_descriptor_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned char bLength;
 | 
				
			||||||
 | 
						unsigned char bDescriptorType;
 | 
				
			||||||
 | 
						unsigned short bcdUSB;
 | 
				
			||||||
 | 
						unsigned char bDeviceClass;
 | 
				
			||||||
 | 
						unsigned char bDeviceSubClass;
 | 
				
			||||||
 | 
						unsigned char bDeviceProtocol;
 | 
				
			||||||
 | 
						unsigned char bMaxPacketSize0;
 | 
				
			||||||
 | 
						unsigned short idVendor;
 | 
				
			||||||
 | 
						unsigned short idProduct;
 | 
				
			||||||
 | 
						unsigned short bcdDevice;
 | 
				
			||||||
 | 
						unsigned char iManufacturer;
 | 
				
			||||||
 | 
						unsigned char iProduct;
 | 
				
			||||||
 | 
						unsigned char iSerialNumber;
 | 
				
			||||||
 | 
						unsigned char bNumConfigurations;
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) device_descriptor_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned char bLength;
 | 
				
			||||||
 | 
						unsigned char bDescriptorType;
 | 
				
			||||||
 | 
						unsigned short wTotalLength;
 | 
				
			||||||
 | 
						unsigned char bNumInterfaces;
 | 
				
			||||||
 | 
						unsigned char bConfigurationValue;
 | 
				
			||||||
 | 
						unsigned char iConfiguration;
 | 
				
			||||||
 | 
						unsigned char bmAttributes;
 | 
				
			||||||
 | 
						unsigned char bMaxPower;
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) configuration_descriptor_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned char bLength;
 | 
				
			||||||
 | 
						unsigned char bDescriptorType;
 | 
				
			||||||
 | 
						unsigned char bInterfaceNumber;
 | 
				
			||||||
 | 
						unsigned char bAlternateSetting;
 | 
				
			||||||
 | 
						unsigned char bNumEndpoints;
 | 
				
			||||||
 | 
						unsigned char bInterfaceClass;
 | 
				
			||||||
 | 
						unsigned char bInterfaceSubClass;
 | 
				
			||||||
 | 
						unsigned char bInterfaceProtocol;
 | 
				
			||||||
 | 
						unsigned char iInterface;
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) interface_descriptor_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned char bLength;
 | 
				
			||||||
 | 
						unsigned char bDescriptorType;
 | 
				
			||||||
 | 
						unsigned char bEndpointAddress;
 | 
				
			||||||
 | 
						unsigned char bmAttributes;
 | 
				
			||||||
 | 
						unsigned short wMaxPacketSize;
 | 
				
			||||||
 | 
						unsigned char bInterval;
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) endpoint_descriptor_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hci_t *new_controller (void);
 | 
				
			||||||
 | 
					void detach_controller (hci_t *controller);
 | 
				
			||||||
 | 
					void usb_poll (void);
 | 
				
			||||||
 | 
					void init_device_entry (hci_t *controller, int num);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_feature (usbdev_t *dev, int endp, int feature, int rtype);
 | 
				
			||||||
 | 
					void get_status (usbdev_t *dev, int endp, int rtype, int len, void *data);
 | 
				
			||||||
 | 
					int clear_stall (endpoint_t *ep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void usb_nop_init (usbdev_t *dev);
 | 
				
			||||||
 | 
					void usb_hub_init (usbdev_t *dev);
 | 
				
			||||||
 | 
					void usb_hid_init (usbdev_t *dev);
 | 
				
			||||||
 | 
					void usb_msc_init (usbdev_t *dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int set_address (hci_t *controller, int lowspeed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u8 *get_descriptor (usbdev_t *dev, unsigned char bmRequestType,
 | 
				
			||||||
 | 
							    int descType, int descIdx, int langID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline unsigned char
 | 
				
			||||||
 | 
					gen_bmRequestType (dev_req_dir dir, dev_req_type type, dev_req_recp recp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (dir << 7) | (type << 5) | recp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										55
									
								
								payloads/libpayload/drivers/usb/usb_dev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								payloads/libpayload/drivers/usb/usb_dev.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_nop_destroy (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (dev->descriptor != 0)
 | 
				
			||||||
 | 
							free (dev->descriptor);
 | 
				
			||||||
 | 
						usb_nop_init (dev);
 | 
				
			||||||
 | 
						dev->address = -1;
 | 
				
			||||||
 | 
						dev->hub = -1;
 | 
				
			||||||
 | 
						dev->port = -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_nop_poll (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					usb_nop_init (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dev->descriptor = 0;
 | 
				
			||||||
 | 
						dev->destroy = usb_nop_destroy;
 | 
				
			||||||
 | 
						dev->poll = usb_nop_poll;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								payloads/libpayload/drivers/usb/usbdisk.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								payloads/libpayload/drivers/usb/usbdisk.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __USBDISK_H
 | 
				
			||||||
 | 
					#define __USBDISK_H
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * To be implemented by libpayload-client. It's called by the USB stack
 | 
				
			||||||
 | 
					 * when a new USB storage device is found, so the client has the chance
 | 
				
			||||||
 | 
					 * to know about it.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param dev descriptor for the USB storage device
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void usbdisk_create (usbdev_t *dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * To be implemented by libpayload-client. It's called by the USB stack
 | 
				
			||||||
 | 
					 * when it finds out that a USB storage device is removed.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param dev descriptor for the USB storage device
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void usbdisk_remove (usbdev_t *dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										137
									
								
								payloads/libpayload/drivers/usb/usbhid.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								payloads/libpayload/drivers/usb/usbhid.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum { hid_subclass_none = 0, hid_subclass_boot = 1 };
 | 
				
			||||||
 | 
					enum { hid_proto_boot = 0, hid_proto_report = 1 };
 | 
				
			||||||
 | 
					enum { hid_boot_proto_none = 0, hid_boot_proto_keyboard =
 | 
				
			||||||
 | 
							1, hid_boot_proto_mouse = 2
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" };
 | 
				
			||||||
 | 
					enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT =
 | 
				
			||||||
 | 
							0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_hid_destroy (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int keypress;
 | 
				
			||||||
 | 
					char keymap[256] = {
 | 
				
			||||||
 | 
						-1, -1, -1, -1, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
 | 
				
			||||||
 | 
						'l',
 | 
				
			||||||
 | 
						'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
 | 
				
			||||||
 | 
						'1', '2',
 | 
				
			||||||
 | 
						'3', '4', '5', '6', '7', '8', '9', '0', '\n', TERM_ESC,
 | 
				
			||||||
 | 
						TERM_BACKSPACE, TERM_TAB, ' ', '-', '=', '[',
 | 
				
			||||||
 | 
						']', '\\', -1, ';', '\'', '`', ',', '.', '/', -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, TERM_HOME, TERM_PPAGE, -1,
 | 
				
			||||||
 | 
						TERM_END, TERM_NPAGE, TERM_RIGHT,
 | 
				
			||||||
 | 
						TERM_LEFT, TERM_DOWN, TERM_UP, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
						-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_hid_poll (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char buf[8];
 | 
				
			||||||
 | 
						static int toggle = 0;
 | 
				
			||||||
 | 
						// hardcode to endpoint 1, 8 bytes
 | 
				
			||||||
 | 
						dev->controller->packet (dev, 1, IN, toggle, 8, buf);
 | 
				
			||||||
 | 
						toggle ^= 1;
 | 
				
			||||||
 | 
						// FIXME: manage buf[0]=special keys, too
 | 
				
			||||||
 | 
						keypress = keymap[buf[2]];
 | 
				
			||||||
 | 
						if ((keypress == -1) && (buf[2] != 0)) {
 | 
				
			||||||
 | 
							printf ("%x %x %x %x %x %x %x %x\n", buf[0], buf[1], buf[2],
 | 
				
			||||||
 | 
								buf[3], buf[4], buf[5], buf[6], buf[7]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int (*oldhook) (void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					hookfunc (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int key;
 | 
				
			||||||
 | 
						if (oldhook != 0)
 | 
				
			||||||
 | 
							key = oldhook ();
 | 
				
			||||||
 | 
						if (key == -1)
 | 
				
			||||||
 | 
							key = keypress;
 | 
				
			||||||
 | 
						return key;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					usb_hid_init (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configuration_descriptor_t *cd = dev->configuration;
 | 
				
			||||||
 | 
						interface_descriptor_t *interface = ((char *) cd) + cd->bLength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (interface->bInterfaceSubClass == hid_subclass_boot) {
 | 
				
			||||||
 | 
							printf ("  supports boot interface..\n");
 | 
				
			||||||
 | 
							printf ("  it's a %s\n",
 | 
				
			||||||
 | 
								boot_protos[interface->bInterfaceProtocol]);
 | 
				
			||||||
 | 
							if (interface->bInterfaceProtocol == hid_boot_proto_keyboard) {
 | 
				
			||||||
 | 
								printf ("  activating...\n");
 | 
				
			||||||
 | 
								dev_req_t dr;
 | 
				
			||||||
 | 
								// set_protocol(hid_proto_boot)
 | 
				
			||||||
 | 
								dr.data_dir = host_to_device;
 | 
				
			||||||
 | 
								dr.req_type = class_type;
 | 
				
			||||||
 | 
								dr.req_recp = iface_recp;
 | 
				
			||||||
 | 
								dr.bRequest = SET_PROTOCOL;
 | 
				
			||||||
 | 
								dr.wValue = hid_proto_boot;
 | 
				
			||||||
 | 
								dr.wIndex = interface->bInterfaceNumber;
 | 
				
			||||||
 | 
								dr.wLength = 0;
 | 
				
			||||||
 | 
								dev->controller->control (dev, OUT,
 | 
				
			||||||
 | 
											  sizeof (dev_req_t), &dr, 0,
 | 
				
			||||||
 | 
											  0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// only add here, because we only support boot-keyboard HID devices
 | 
				
			||||||
 | 
								// FIXME: make this a real console input driver instead, once the API is there
 | 
				
			||||||
 | 
								dev->destroy = usb_hid_destroy;
 | 
				
			||||||
 | 
								dev->poll = usb_hid_poll;
 | 
				
			||||||
 | 
								oldhook = getkey_hook;
 | 
				
			||||||
 | 
								getkey_hook = hookfunc;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										158
									
								
								payloads/libpayload/drivers/usb/usbhub.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								payloads/libpayload/drivers/usb/usbhub.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,158 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// assume that host_to_device is overwritten if necessary
 | 
				
			||||||
 | 
					#define DR_PORT gen_bmRequestType(host_to_device, class_type, other_recp)
 | 
				
			||||||
 | 
					#define PORT_RESET 0x4
 | 
				
			||||||
 | 
					#define PORT_POWER 0x8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						int num_ports;
 | 
				
			||||||
 | 
						int *ports;
 | 
				
			||||||
 | 
						hub_descriptor_t *descriptor;
 | 
				
			||||||
 | 
					} usbhub_inst_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HUB_INST(dev) ((usbhub_inst_t*)(dev)->data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_hub_destroy (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						free (HUB_INST (dev)->ports);
 | 
				
			||||||
 | 
						free (HUB_INST (dev)->descriptor);
 | 
				
			||||||
 | 
						free (HUB_INST (dev));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_hub_scanport (usbdev_t *dev, int port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int newdev;
 | 
				
			||||||
 | 
						unsigned short buf[2];
 | 
				
			||||||
 | 
						usbdev_t *newdev_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get_status (dev, port, DR_PORT, 4, buf);
 | 
				
			||||||
 | 
						int portstatus = ((buf[0] & 1) == 0);
 | 
				
			||||||
 | 
						int datastatus = (HUB_INST (dev)->ports[port] == -1);
 | 
				
			||||||
 | 
						if (portstatus == datastatus)
 | 
				
			||||||
 | 
							return;		// no change - FIXME: read right fields for that test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!datastatus) {
 | 
				
			||||||
 | 
							int devno = HUB_INST (dev)->ports[port];
 | 
				
			||||||
 | 
							if (devno == -1)
 | 
				
			||||||
 | 
								fatal ("FATAL: illegal devno!\n");
 | 
				
			||||||
 | 
							dev->controller->devices[devno].destroy (&dev->controller->
 | 
				
			||||||
 | 
												 devices[devno]);
 | 
				
			||||||
 | 
							init_device_entry (dev->controller, devno);
 | 
				
			||||||
 | 
							HUB_INST (dev)->ports[port] = -1;
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_feature (dev, port, PORT_RESET, DR_PORT);
 | 
				
			||||||
 | 
						mdelay (20);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get_status (dev, port, DR_PORT, 4, buf);
 | 
				
			||||||
 | 
						int lowspeed = (buf[0] >> 9) & 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						newdev = set_address (dev->controller, lowspeed);
 | 
				
			||||||
 | 
						if (newdev == -1)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						newdev_t = &dev->controller->devices[newdev];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						HUB_INST (dev)->ports[port] = newdev;
 | 
				
			||||||
 | 
						newdev_t->address = newdev;
 | 
				
			||||||
 | 
						newdev_t->hub = dev->address;
 | 
				
			||||||
 | 
						newdev_t->port = port;
 | 
				
			||||||
 | 
						// determine responsible driver
 | 
				
			||||||
 | 
						newdev_t->init (newdev_t);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					usb_hub_report_port_changes (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int port;
 | 
				
			||||||
 | 
						unsigned short buf[2];
 | 
				
			||||||
 | 
						for (port = 1; port <= HUB_INST (dev)->num_ports; port++) {
 | 
				
			||||||
 | 
							get_status (dev, port, DR_PORT, 4, buf);
 | 
				
			||||||
 | 
							// FIXME: proper change detection
 | 
				
			||||||
 | 
							int portstatus = ((buf[0] & 1) == 0);
 | 
				
			||||||
 | 
							int datastatus = (HUB_INST (dev)->ports[port] == -1);
 | 
				
			||||||
 | 
							if (portstatus != datastatus)
 | 
				
			||||||
 | 
								return port;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// no change
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_hub_enable_port (usbdev_t *dev, int port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						set_feature (dev, port, PORT_POWER, DR_PORT);
 | 
				
			||||||
 | 
						mdelay (20);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_hub_disable_port (usbdev_t *dev, int port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_hub_poll (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int port;
 | 
				
			||||||
 | 
						while ((port = usb_hub_report_port_changes (dev)) != -1)
 | 
				
			||||||
 | 
							usb_hub_scanport (dev, port);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					usb_hub_init (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						dev->destroy = usb_hub_destroy;
 | 
				
			||||||
 | 
						dev->poll = usb_hub_poll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev->data = malloc (sizeof (usbhub_inst_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						HUB_INST (dev)->descriptor =
 | 
				
			||||||
 | 
							(hub_descriptor_t *) get_descriptor (dev,
 | 
				
			||||||
 | 
											     gen_bmRequestType
 | 
				
			||||||
 | 
											     (device_to_host,
 | 
				
			||||||
 | 
											      class_type, dev_recp),
 | 
				
			||||||
 | 
											     0x29, 0, 0);
 | 
				
			||||||
 | 
						HUB_INST (dev)->num_ports = HUB_INST (dev)->descriptor->bNbrPorts;
 | 
				
			||||||
 | 
						HUB_INST (dev)->ports =
 | 
				
			||||||
 | 
							malloc (sizeof (int) * (HUB_INST (dev)->num_ports + 1));
 | 
				
			||||||
 | 
						for (i = 1; i <= HUB_INST (dev)->num_ports; i++)
 | 
				
			||||||
 | 
							HUB_INST (dev)->ports[i] = -1;
 | 
				
			||||||
 | 
						for (i = 1; i <= HUB_INST (dev)->num_ports; i++)
 | 
				
			||||||
 | 
							usb_hub_enable_port (dev, i);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										120
									
								
								payloads/libpayload/drivers/usb/usbinit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								payloads/libpayload/drivers/usb/usbinit.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <config.h>
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					#include "uhci.h"
 | 
				
			||||||
 | 
					#include "usbdisk.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Initializes USB controller attached to PCI
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param bus PCI bus number
 | 
				
			||||||
 | 
					 * @param dev PCI device id at bus
 | 
				
			||||||
 | 
					 * @param func function id of the controller
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					usb_controller_initialize (int bus, int dev, int func)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 class;
 | 
				
			||||||
 | 
						u32 devclass;
 | 
				
			||||||
 | 
						u32 prog_if;
 | 
				
			||||||
 | 
						pcidev_t addr;
 | 
				
			||||||
 | 
						u32 pciid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						addr = PCI_DEV (bus, dev, func);
 | 
				
			||||||
 | 
						class = pci_read_config32 (addr, 8);
 | 
				
			||||||
 | 
						pciid = pci_read_config32 (addr, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						devclass = class >> 16;
 | 
				
			||||||
 | 
						prog_if = (class >> 8) & 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* enable busmaster */
 | 
				
			||||||
 | 
					#define PCI_COMMAND 4
 | 
				
			||||||
 | 
					#define PCI_COMMAND_MASTER 4
 | 
				
			||||||
 | 
						pci_write_config32 (addr, PCI_COMMAND,
 | 
				
			||||||
 | 
								    pci_read_config32 (addr,
 | 
				
			||||||
 | 
										       PCI_COMMAND) |
 | 
				
			||||||
 | 
								    PCI_COMMAND_MASTER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (devclass == 0xc03) {
 | 
				
			||||||
 | 
							printf ("%02x:%02x.%x %04x:%04x.%d ", 0, dev, func,
 | 
				
			||||||
 | 
								pciid >> 16, pciid & 0xFFFF, func);
 | 
				
			||||||
 | 
							if (prog_if == 0) {
 | 
				
			||||||
 | 
								printf ("UHCI controller\n");
 | 
				
			||||||
 | 
					#ifdef CONFIG_USB_UHCI
 | 
				
			||||||
 | 
								uhci_init (addr);
 | 
				
			||||||
 | 
								usb_poll ();
 | 
				
			||||||
 | 
								usb_poll ();
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
								printf ("Not supported.\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (prog_if == 0x10) {
 | 
				
			||||||
 | 
								printf ("OHCI controller\n");
 | 
				
			||||||
 | 
					#ifdef CONFIG_USB_OHCI
 | 
				
			||||||
 | 
								// ohci_init(addr);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
								printf ("Not supported.\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (prog_if == 0x20) {
 | 
				
			||||||
 | 
								printf ("EHCI controller\n");
 | 
				
			||||||
 | 
					#ifdef CONFIG_USB_EHCI
 | 
				
			||||||
 | 
								// ehci_init(addr);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
								printf ("Not supported.\n");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Initialize all USB controllers attached to PCI.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					usb_initialize (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int bus, dev, func;
 | 
				
			||||||
 | 
						for (bus = 0; bus < 256; bus++)
 | 
				
			||||||
 | 
							for (dev = 0; dev < 32; dev++)
 | 
				
			||||||
 | 
								for (func = 0; func < 8; func++)
 | 
				
			||||||
 | 
									usb_controller_initialize (bus, dev, func);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					usb_exit (void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										397
									
								
								payloads/libpayload/drivers/usb/usbmsc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								payloads/libpayload/drivers/usb/usbmsc.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,397 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <arch/endian.h>
 | 
				
			||||||
 | 
					#include "usb.h"
 | 
				
			||||||
 | 
					#include "usbmsc.h"
 | 
				
			||||||
 | 
					#include "usbdisk.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						msc_subclass_rbc = 0x1,
 | 
				
			||||||
 | 
						msc_subclass_mmc2 = 0x2,
 | 
				
			||||||
 | 
						msc_subclass_qic157 = 0x3,
 | 
				
			||||||
 | 
						msc_subclass_ufi = 0x4,
 | 
				
			||||||
 | 
						msc_subclass_sff8070i = 0x5,
 | 
				
			||||||
 | 
						msc_subclass_scsitrans = 0x6
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static const char *msc_subclass_strings[7] = {
 | 
				
			||||||
 | 
						"(none)",
 | 
				
			||||||
 | 
						"RBC",
 | 
				
			||||||
 | 
						"MMC-2",
 | 
				
			||||||
 | 
						"QIC-157",
 | 
				
			||||||
 | 
						"UFI",
 | 
				
			||||||
 | 
						"SFF-8070i",
 | 
				
			||||||
 | 
						"SCSI transparent"
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
						msc_proto_cbi_wcomp = 0x0,
 | 
				
			||||||
 | 
						msc_proto_cbi_wocomp = 0x1,
 | 
				
			||||||
 | 
						msc_proto_bulk_only = 0x50
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static const char *msc_protocol_strings[0x51] = {
 | 
				
			||||||
 | 
						"Control/Bulk/Interrupt protocol (with command completion interrupt)",
 | 
				
			||||||
 | 
						"Control/Bulk/Interrupt protocol (with no command completion interrupt)",
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
						"Bulk-Only Transport"
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_msc_destroy (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						usbdisk_remove (dev);
 | 
				
			||||||
 | 
						free (dev->data);
 | 
				
			||||||
 | 
						dev->data = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					usb_msc_poll (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const int DEV_RESET = 0xff;
 | 
				
			||||||
 | 
					const int GET_MAX_LUN = 0xfe;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unsigned int cbw_signature = 0x43425355;
 | 
				
			||||||
 | 
					const unsigned int csw_signature = 0x53425355;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned int dCBWSignature;
 | 
				
			||||||
 | 
						unsigned int dCBWTag;
 | 
				
			||||||
 | 
						unsigned int dCBWDataTransferLength;
 | 
				
			||||||
 | 
						unsigned char bmCBWFlags;
 | 
				
			||||||
 | 
						unsigned long bCBWLUN:4;
 | 
				
			||||||
 | 
						unsigned long:4;
 | 
				
			||||||
 | 
						unsigned long bCBWCBLength:5;
 | 
				
			||||||
 | 
						unsigned long:3;
 | 
				
			||||||
 | 
						unsigned char CBWCB[31 - 15];
 | 
				
			||||||
 | 
					} __attribute__ ((packed))
 | 
				
			||||||
 | 
					     cbw_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     typedef struct {
 | 
				
			||||||
 | 
						     unsigned int dCSWSignature;
 | 
				
			||||||
 | 
						     unsigned int dCSWTag;
 | 
				
			||||||
 | 
						     unsigned int dCSWDataResidue;
 | 
				
			||||||
 | 
						     unsigned char bCSWStatus;
 | 
				
			||||||
 | 
					     } __attribute__ ((packed))
 | 
				
			||||||
 | 
					     csw_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     static void
 | 
				
			||||||
 | 
					       reset_transport (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dev_req_t dr;
 | 
				
			||||||
 | 
						memset (&dr, 0, sizeof (dr));
 | 
				
			||||||
 | 
						dr.bmRequestType = 0;
 | 
				
			||||||
 | 
						dr.data_dir = host_to_device;
 | 
				
			||||||
 | 
					#ifndef QEMU
 | 
				
			||||||
 | 
						dr.req_type = class_type;
 | 
				
			||||||
 | 
						dr.req_recp = iface_recp;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						dr.bRequest = DEV_RESET;
 | 
				
			||||||
 | 
						dr.wValue = 0;
 | 
				
			||||||
 | 
						dr.wIndex = 0;
 | 
				
			||||||
 | 
						dr.wLength = 0;
 | 
				
			||||||
 | 
						dev->controller->control (dev, OUT, sizeof (dr), &dr, 0, 0);
 | 
				
			||||||
 | 
						clear_stall (MSC_INST (dev)->bulk_in);
 | 
				
			||||||
 | 
						clear_stall (MSC_INST (dev)->bulk_out);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* device may stall this command, so beware! */
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					get_max_luns (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned char luns = 75;
 | 
				
			||||||
 | 
						dev_req_t dr;
 | 
				
			||||||
 | 
						dr.bmRequestType = 0;
 | 
				
			||||||
 | 
						dr.data_dir = device_to_host;
 | 
				
			||||||
 | 
					#ifndef QEMU
 | 
				
			||||||
 | 
						dr.req_type = class_type;
 | 
				
			||||||
 | 
						dr.req_recp = iface_recp;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						dr.bRequest = GET_MAX_LUN;
 | 
				
			||||||
 | 
						dr.wValue = 0;
 | 
				
			||||||
 | 
						dr.wIndex = 0;
 | 
				
			||||||
 | 
						dr.wLength = 1;
 | 
				
			||||||
 | 
						if (dev->controller->control (dev, IN, sizeof (dr), &dr, 1, &luns)) {
 | 
				
			||||||
 | 
							luns = 0;	// assume only 1 lun if req fails
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return luns;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int tag;
 | 
				
			||||||
 | 
					int lun = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					wrap_cbw (cbw_t *cbw, int datalen, cbw_direction dir, const u8 *cmd,
 | 
				
			||||||
 | 
						  int cmdlen)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						memset (cbw, 0, sizeof (cbw_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cbw->dCBWSignature = cbw_signature;
 | 
				
			||||||
 | 
						cbw->dCBWTag = tag++;
 | 
				
			||||||
 | 
						cbw->bCBWLUN = lun;	// static value per device
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cbw->dCBWDataTransferLength = datalen;
 | 
				
			||||||
 | 
						cbw->bmCBWFlags = dir;
 | 
				
			||||||
 | 
						memcpy (cbw->CBWCB, cmd, sizeof (cbw->CBWCB));
 | 
				
			||||||
 | 
						cbw->bCBWCBLength = cmdlen;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					get_csw (endpoint_t *ep, csw_t *csw)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ep->dev->controller->bulk (ep, sizeof (csw_t), (u8 *) csw, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					execute_command (usbdev_t *dev, cbw_direction dir, const u8 *cb, int cblen,
 | 
				
			||||||
 | 
							 u8 *buf, int buflen)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cbw_t cbw;
 | 
				
			||||||
 | 
						csw_t csw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int always_succeed = 0;
 | 
				
			||||||
 | 
						if ((cb[0] == 0x1b) && (cb[4] == 1)) {	//start command, always succeed
 | 
				
			||||||
 | 
							always_succeed = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						wrap_cbw (&cbw, buflen, dir, cb, cblen);
 | 
				
			||||||
 | 
						if (dev->controller->
 | 
				
			||||||
 | 
						    bulk (MSC_INST (dev)->bulk_out, sizeof (cbw), (u8 *) &cbw, 0)) {
 | 
				
			||||||
 | 
							clear_stall (MSC_INST (dev)->bulk_out);
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mdelay (10);
 | 
				
			||||||
 | 
						if (dir == cbw_direction_data_in) {
 | 
				
			||||||
 | 
							if (dev->controller->
 | 
				
			||||||
 | 
							    bulk (MSC_INST (dev)->bulk_in, buflen, buf, 0)) {
 | 
				
			||||||
 | 
								clear_stall (MSC_INST (dev)->bulk_in);
 | 
				
			||||||
 | 
								return 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (dev->controller->
 | 
				
			||||||
 | 
							    bulk (MSC_INST (dev)->bulk_out, buflen, buf, 0)) {
 | 
				
			||||||
 | 
								clear_stall (MSC_INST (dev)->bulk_out);
 | 
				
			||||||
 | 
								return 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						get_csw (MSC_INST (dev)->bulk_in, &csw);
 | 
				
			||||||
 | 
						if (always_succeed == 1) {
 | 
				
			||||||
 | 
							// return success, regardless of message
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (csw.bCSWStatus == 2) {
 | 
				
			||||||
 | 
							// phase error, reset transport
 | 
				
			||||||
 | 
							reset_transport (dev);
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (csw.bCSWStatus == 0) {
 | 
				
			||||||
 | 
							// no error, exit
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// error "check condition" or reserved error
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned char command;	//0
 | 
				
			||||||
 | 
						unsigned char res1;	//1
 | 
				
			||||||
 | 
						unsigned int block;	//2-5
 | 
				
			||||||
 | 
						unsigned char res2;	//6
 | 
				
			||||||
 | 
						unsigned short numblocks;	//7-8
 | 
				
			||||||
 | 
						unsigned char res3;	//9 - the block is 10 bytes long
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) cmdblock_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned char command;	//0
 | 
				
			||||||
 | 
						unsigned char res1;	//1
 | 
				
			||||||
 | 
						unsigned char res2;	//2
 | 
				
			||||||
 | 
						unsigned char res3;	//3
 | 
				
			||||||
 | 
						unsigned char lun;	//4
 | 
				
			||||||
 | 
						unsigned char res4;	//5
 | 
				
			||||||
 | 
					} __attribute__ ((packed)) cmdblock6_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Reads or writes a number of sequential blocks on a USB storage device.
 | 
				
			||||||
 | 
					 * As it uses the READ(10) SCSI-2 command, it's limited to storage devices
 | 
				
			||||||
 | 
					 * of at most 2TB. It assumes sectors of 512 bytes.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param dev device to access
 | 
				
			||||||
 | 
					 * @param start first sector to access
 | 
				
			||||||
 | 
					 * @param n number of sectors to access
 | 
				
			||||||
 | 
					 * @param dir direction of access: cbw_direction_data_in == read, cbw_direction_data_out == write
 | 
				
			||||||
 | 
					 * @param buf buffer to read into or write from. Must be at least n*512 bytes
 | 
				
			||||||
 | 
					 * @return 0 on success, 1 on failure
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir, u8 *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cmdblock_t cb;
 | 
				
			||||||
 | 
						memset (&cb, 0, sizeof (cb));
 | 
				
			||||||
 | 
						if (dir == cbw_direction_data_in) {
 | 
				
			||||||
 | 
							// read
 | 
				
			||||||
 | 
							cb.command = 0x28;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// write
 | 
				
			||||||
 | 
							cb.command = 0x2a;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cb.block = ntohl (start);
 | 
				
			||||||
 | 
						cb.numblocks = ntohw (n);
 | 
				
			||||||
 | 
						return execute_command (dev, dir, (u8 *) &cb, sizeof (cb), buf,
 | 
				
			||||||
 | 
									n * 512);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					test_unit_ready (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cmdblock6_t cb;
 | 
				
			||||||
 | 
						memset (&cb, 0, sizeof (cb));	// full initialization for T-U-R
 | 
				
			||||||
 | 
						return execute_command (dev, cbw_direction_data_out, (u8 *) &cb,
 | 
				
			||||||
 | 
									sizeof (cb), 0, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					spin_up (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cmdblock6_t cb;
 | 
				
			||||||
 | 
						memset (&cb, 0, sizeof (cb));
 | 
				
			||||||
 | 
						cb.command = 0x1b;
 | 
				
			||||||
 | 
						cb.lun = 1;
 | 
				
			||||||
 | 
						return execute_command (dev, cbw_direction_data_out, (u8 *) &cb,
 | 
				
			||||||
 | 
									sizeof (cb), 0, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					read_capacity (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						cmdblock_t cb;
 | 
				
			||||||
 | 
						memset (&cb, 0, sizeof (cb));
 | 
				
			||||||
 | 
						cb.command = 0x25;	// read capacity
 | 
				
			||||||
 | 
						u8 buf[8];
 | 
				
			||||||
 | 
						int count = 0;
 | 
				
			||||||
 | 
						while ((count++ < 20)
 | 
				
			||||||
 | 
						       &&
 | 
				
			||||||
 | 
						       (execute_command
 | 
				
			||||||
 | 
							(dev, cbw_direction_data_in, (u8 *) &cb, sizeof (cb), buf,
 | 
				
			||||||
 | 
							 8) == 1));
 | 
				
			||||||
 | 
						if (count >= 20) {
 | 
				
			||||||
 | 
							// still not successful, assume 2tb in 512byte sectors, which is just the same garbage as any other number, but probably reasonable.
 | 
				
			||||||
 | 
							printf ("assuming 2TB in 512byte sectors as READ CAPACITY didn't answer.\n");
 | 
				
			||||||
 | 
							MSC_INST (dev)->numblocks = 0xffffffff;
 | 
				
			||||||
 | 
							MSC_INST (dev)->blocksize = 512;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							MSC_INST (dev)->numblocks = ntohl (*(u32 *) buf) + 1;
 | 
				
			||||||
 | 
							MSC_INST (dev)->blocksize = ntohl (*(u32 *) (buf + 4));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printf ("  has %d blocks sized %db\n", MSC_INST (dev)->numblocks,
 | 
				
			||||||
 | 
							MSC_INST (dev)->blocksize);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					usb_msc_init (usbdev_t *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i, timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev->destroy = usb_msc_destroy;
 | 
				
			||||||
 | 
						dev->poll = usb_msc_poll;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						configuration_descriptor_t *cd =
 | 
				
			||||||
 | 
							(configuration_descriptor_t *) dev->configuration;
 | 
				
			||||||
 | 
						interface_descriptor_t *interface =
 | 
				
			||||||
 | 
							(interface_descriptor_t *) (((char *) cd) + cd->bLength);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf ("  it uses %s command set\n",
 | 
				
			||||||
 | 
							msc_subclass_strings[interface->bInterfaceSubClass]);
 | 
				
			||||||
 | 
						printf ("  it uses %s protocol\n",
 | 
				
			||||||
 | 
							msc_protocol_strings[interface->bInterfaceProtocol]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((interface->bInterfaceProtocol != 0x50)
 | 
				
			||||||
 | 
						    || (interface->bInterfaceSubClass != 6)) {
 | 
				
			||||||
 | 
							/* Other protocols, such as ATAPI don't seem to be very popular. looks like ATAPI would be really easy to add, if necessary. */
 | 
				
			||||||
 | 
							printf ("  Only SCSI over Bulk is supported.\n");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev->data = malloc (sizeof (usbmsc_inst_t));
 | 
				
			||||||
 | 
						MSC_INST (dev)->bulk_in = 0;
 | 
				
			||||||
 | 
						MSC_INST (dev)->bulk_out = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 1; i <= dev->num_endp; i++) {
 | 
				
			||||||
 | 
							if (dev->endpoints[i].endpoint == 0)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							if (dev->endpoints[i].type != BULK)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
							if ((dev->endpoints[i].direction == IN)
 | 
				
			||||||
 | 
							    && (MSC_INST (dev)->bulk_in == 0))
 | 
				
			||||||
 | 
								MSC_INST (dev)->bulk_in = &dev->endpoints[i];
 | 
				
			||||||
 | 
							if ((dev->endpoints[i].direction == OUT)
 | 
				
			||||||
 | 
							    && (MSC_INST (dev)->bulk_out == 0))
 | 
				
			||||||
 | 
								MSC_INST (dev)->bulk_out = &dev->endpoints[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (MSC_INST (dev)->bulk_in == 0)
 | 
				
			||||||
 | 
							fatal ("couldn't find bulk-in endpoint");
 | 
				
			||||||
 | 
						if (MSC_INST (dev)->bulk_out == 0)
 | 
				
			||||||
 | 
							fatal ("couldn't find bulk-out endpoint");
 | 
				
			||||||
 | 
						printf ("  using endpoint %x as in, %x as out\n",
 | 
				
			||||||
 | 
							MSC_INST (dev)->bulk_in->endpoint,
 | 
				
			||||||
 | 
							MSC_INST (dev)->bulk_out->endpoint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf ("  has %d luns\n", get_max_luns (dev) + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf ("  Waiting for device to become ready... ");
 | 
				
			||||||
 | 
						timeout = 10;
 | 
				
			||||||
 | 
						while (test_unit_ready (dev) && --timeout) {
 | 
				
			||||||
 | 
							mdelay (100);
 | 
				
			||||||
 | 
							printf (".");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (test_unit_ready (dev)) {
 | 
				
			||||||
 | 
							printf ("timeout. Device not ready. Still trying...\n");
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							printf ("ok.\n");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						printf ("  spin up");
 | 
				
			||||||
 | 
						for (i = 0; i < 30; i++) {
 | 
				
			||||||
 | 
							printf (".");
 | 
				
			||||||
 | 
							if (!spin_up (dev)) {
 | 
				
			||||||
 | 
								printf (" OK.");
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							mdelay (100);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						printf ("\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						read_capacity (dev);
 | 
				
			||||||
 | 
						usbdisk_create (dev);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										47
									
								
								payloads/libpayload/drivers/usb/usbmsc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								payloads/libpayload/drivers/usb/usbmsc.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This file is part of the libpayload project.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2008 coresystems GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					 * modification, are permitted provided that the following conditions
 | 
				
			||||||
 | 
					 * are met:
 | 
				
			||||||
 | 
					 * 1. Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					 * 2. Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					 *    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					 *    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					 * 3. The name of the author may not be used to endorse or promote products
 | 
				
			||||||
 | 
					 *    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 | 
				
			||||||
 | 
					 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
				
			||||||
 | 
					 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
				
			||||||
 | 
					 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 | 
				
			||||||
 | 
					 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
				
			||||||
 | 
					 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
				
			||||||
 | 
					 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
				
			||||||
 | 
					 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
				
			||||||
 | 
					 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
				
			||||||
 | 
					 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
				
			||||||
 | 
					 * SUCH DAMAGE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __USBMSC_H
 | 
				
			||||||
 | 
					#define __USBMSC_H
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						unsigned int blocksize;
 | 
				
			||||||
 | 
						unsigned int numblocks;
 | 
				
			||||||
 | 
						endpoint_t *bulk_in;
 | 
				
			||||||
 | 
						endpoint_t *bulk_out;
 | 
				
			||||||
 | 
					} usbmsc_inst_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MSC_INST(dev) ((usbmsc_inst_t*)(dev)->data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum { cbw_direction_data_in = 0x80, cbw_direction_data_out = 0
 | 
				
			||||||
 | 
					} cbw_direction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int readwrite_blocks (usbdev_t *dev, int start, int n, cbw_direction dir,
 | 
				
			||||||
 | 
							      u8 *buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		Reference in New Issue
	
	Block a user