summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/.cvsignore5
-rw-r--r--drivers/usb/CREDITS154
-rw-r--r--drivers/usb/Config.in30
-rw-r--r--drivers/usb/Makefile88
-rw-r--r--drivers/usb/README.kbd65
-rw-r--r--drivers/usb/README.ohci26
-rw-r--r--drivers/usb/README.ohci_hcd112
-rw-r--r--drivers/usb/audio.c126
-rw-r--r--drivers/usb/hub.c422
-rw-r--r--drivers/usb/hub.h80
-rw-r--r--drivers/usb/inits.h6
-rw-r--r--drivers/usb/keyboard.c226
-rw-r--r--drivers/usb/keymap.c50
-rw-r--r--drivers/usb/maps/.cvsignore5
-rw-r--r--drivers/usb/maps/fixup.map31
-rw-r--r--drivers/usb/maps/serial.map370
-rw-r--r--drivers/usb/maps/usb.map233
-rw-r--r--drivers/usb/mkmap83
-rw-r--r--drivers/usb/mouse.c293
-rw-r--r--drivers/usb/ohci-debug.c178
-rw-r--r--drivers/usb/ohci-hcd.c1489
-rw-r--r--drivers/usb/ohci-hcd.h404
-rw-r--r--drivers/usb/ohci-root-hub.c604
-rw-r--r--drivers/usb/ohci-root-hub.h71
-rw-r--r--drivers/usb/ohci.c1669
-rw-r--r--drivers/usb/ohci.h349
-rw-r--r--drivers/usb/restart38
-rw-r--r--drivers/usb/stopusb7
-rw-r--r--drivers/usb/uhci-debug.c168
-rw-r--r--drivers/usb/uhci.c1213
-rw-r--r--drivers/usb/uhci.h229
-rw-r--r--drivers/usb/usb-debug.c127
-rw-r--r--drivers/usb/usb.c673
-rw-r--r--drivers/usb/usb.h376
34 files changed, 10000 insertions, 0 deletions
diff --git a/drivers/usb/.cvsignore b/drivers/usb/.cvsignore
new file mode 100644
index 000000000..b566130de
--- /dev/null
+++ b/drivers/usb/.cvsignore
@@ -0,0 +1,5 @@
+.depend
+.*.flags
+conmakehash
+consolemap_deftbl.c
+uni_hash.tbl
diff --git a/drivers/usb/CREDITS b/drivers/usb/CREDITS
new file mode 100644
index 000000000..6cd211b22
--- /dev/null
+++ b/drivers/usb/CREDITS
@@ -0,0 +1,154 @@
+Credits for the Simple Linux USB Driver:
+
+The following people have contributed to this code (in alphabetical
+order by last name). I'm sure this list should be longer, its
+difficult to maintain, add yourself with a patch if desired.
+
+ Alan Cox <alan@lxorguk.ukuu.org.uk>
+ Johannes Erdfelt <jerdfelt@sventech.com>
+ ham <ham@unsuave.com>
+ Bradley M Keryan <keryan@andrew.cmu.edu>
+ Vojtech Pavlik <vojtech@twilight.ucw.cz>
+ Gregory P. Smith <greg@electricrain.com>
+ Linus Torvalds <torvalds@transmeta.com>
+ Roman Weissgaerber <weissg@vienna.at>
+ <Kazuki.Yasumatsu@fujixerox.co.jp>
+
+Special thanks to:
+
+ Inaky Perez Gonzalez <inaky@peloncho.fis.ucm.es> for starting the
+ Linux USB driver effort and writing much of the larger uusbd driver.
+ Much has been learned from that effort.
+
+ The NetBSD & FreeBSD USB developers. For being on the Linux USB list
+ and offering suggestions and sharing implementation experiences.
+
+Additional thanks to the following companies and people for donations
+of hardware, support, time and development (this is from the original
+THANKS file in Inaky's driver):
+
+ The following corporations have helped us in the development
+of Linux USB / UUSBD:
+
+ - USAR Systems provided us with one of their excellent USB
+ Evaluation Kits. It allows us to test the Linux-USB driver
+ for compilance with the latest USB specification. USAR
+ Systems recognized the importance of an up-to-date open
+ Operating System and supports this project with
+ Hardware. Thanks!.
+
+ - Thanks to Intel Corporation for their precious help.
+
+ - We teamed up with Cherry to make Linux the first OS with
+ built-in USB support. Cherry is one of the biggest keyboard
+ makers in the world.
+
+ - CMD Technology, Inc. sponsored us kindly donating a CSA-6700
+ PCI-to-USB Controller Board to test the OHCI implementation.
+
+ - Due to their support to us, Keytronic can be sure that they
+ will sell keyboards to some of the 3 million (at least)
+ Linux users.
+
+ - Many thanks to ing büro h doran [http://www.ibhdoran.com]!
+ It was almost imposible to get a PC backplate USB connector
+ for the motherboard here at Europe (mine, home-made, was
+ quite lowsy :). Now I know where to adquire nice USB stuff!
+
+ - Genius Germany donated a USB mouse to test the mouse boot
+ protocol. They've also donated a F-23 digital joystick and a
+ NetMouse Pro. Thanks!
+
+ - AVM GmbH Berlin is supporting the development of the Linux
+ USB driver for the AVM ISDN Controller B1 USB. AVM is a
+ leading manufacturer for active and passive ISDN Controllers
+ and CAPI 2.0-based software. The active design of the AVM B1
+ is open for all OS platforms, including Linux.
+
+ - Thanks to Y-E Data, Inc. for donating their FlashBuster-U
+ USB Floppy Disk Drive, so we could test the bulk transfer
+ code.
+
+ - Many thanks to Logitech for contributing a three axis USB
+ mouse.
+
+ Logitech designs, manufactures and markets
+ Human Interface Devices, having a long history and
+ experience in making devices such as keyboards, mice,
+ trackballs, cameras, loudspeakers and control devices for
+ gaming and professional use.
+
+ Being a recognized vendor and seller for all these devices,
+ they have donated USB mice, a joystick and a scanner, as a
+ way to acknowledge the importance of Linux and to allow
+ Logitech customers to enjoy support in their favorite
+ operating systems and all Linux users to use Logitech and
+ other USB hardware.
+
+ Logitech is official sponsor of the Linux Conference on
+ Feb. 11th 1999 in Vienna, where we'll will present the
+ current state of the Linux USB effort.
+
+ - CATC has provided means to uncover dark corners of the UHCI
+ inner workings with a USB Inspector.
+
+ - Thanks to Entrega for providing PCI to USB cards, hubs and
+ converter products for development.
+
+
+ And thanks go to (hey! in no particular order :)
+
+ - Oren Tirosh <orenti@hishome.net>, for standing so patiently
+ all my doubts'bout USB and giving lots of cool ideas.
+
+ - Jochen Karrer <karrer@wpfd25.physik.uni-wuerzburg.de>, for
+ pointing out mortal bugs and giving advice.
+
+ - Edmund Humemberger <ed@atnet.at>, for it's great work on
+ public relationships and general management stuff for the
+ Linux-USB effort.
+
+ - Alberto Menegazzi <flash@flash.iol.it> is starting the
+ documentation for the UUSBD. Go for it!
+
+ - Ric Klaren <ia_ric@cs.utwente.nl> for doing nice
+ introductory documents (compiting with Alberto's :).
+
+ - Christian Groessler <cpg@aladdin.de>, for it's help on those
+ itchy bits ... :)
+
+ - Paul MacKerras for polishing OHCI and pushing me harder for
+ the iMac support, giving improvements and enhancements.
+
+ - Fernando Herrera <fherrera@eurielec.etsit.upm.es> has taken
+ charge of composing, maintaining and feeding the
+ long-awaited, unique and marvelous UUSBD FAQ! Tadaaaa!!!
+
+ - Rasca Gmelch <thron@gmx.de> has revived the raw driver and
+ pointed bugs, as well as started the uusbd-utils package.
+
+ - Peter Dettori <dettori@ozy.dec.com> is unconvering bugs like
+ crazy, as well as making cool suggestions, great :)
+
+ - All the Free Software and Linux community, the FSF & the GNU
+ project, the MIT X consortium, the TeX people ... everyone!
+ You know who you are!
+
+ - Big thanks to Richard Stallman for creating Emacs!
+
+ - The people at the linux-usb mailing list, for reading so
+ many messages :) Ok, no more kidding; for all your advices!
+
+ - All the people at the USB Implementors Forum for their
+ help and assistance.
+
+ - Nathan Myers <ncm@cantrip.org>, for his advice! (hope you
+ liked Cibeles' party).
+
+ - Linus Torvalds, for starting, developing and managing Linux.
+
+ - Mike Smith, Craig Keithley, Thierry Giron and Janet Schank
+ for convincing me USB Standard hubs are not that standard
+ and that's good to allow for vendor specific quirks on the
+ standard hub driver.
+
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in
new file mode 100644
index 000000000..632e3d08a
--- /dev/null
+++ b/drivers/usb/Config.in
@@ -0,0 +1,30 @@
+#
+# USB device configuration
+#
+# NOTE NOTE NOTE! This is still considered extremely experimental.
+# Right now hubs, mice and keyboards work - at least with UHCI.
+# But that may be more a lucky coincidence than anything else..
+#
+# This was all developed modularly, but I've been lazy in cleaning
+# it up, so right now they are all bools.
+#
+mainmenu_option next_comment
+comment 'USB drivers - not for the faint of heart'
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'Support for USB (EXPERIMENTAL!)' CONFIG_USB
+ if [ ! "$CONFIG_USB" = "n" ]; then
+ bool 'UHCI (intel PIIX4 and others) support?' CONFIG_USB_UHCI
+ bool 'OHCI (compaq and some others) support?' CONFIG_USB_OHCI
+ bool 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support?' CONFIG_USB_OHCI_HCD
+ if [ "$CONFIG_USB_OHCI_HCD" = "y" ]; then
+ bool 'OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB
+ fi
+
+ bool 'USB mouse support' CONFIG_USB_MOUSE
+ bool 'USB keyboard support' CONFIG_USB_KBD
+ bool 'USB audio parsing support' CONFIG_USB_AUDIO
+ fi
+fi
+
+endmenu
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
new file mode 100644
index 000000000..6c6e7b091
--- /dev/null
+++ b/drivers/usb/Makefile
@@ -0,0 +1,88 @@
+#
+# Makefile for the kernel usb device drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+# This isn't actually supported yet. Don't try to use it.
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := usb.a
+M_OBJS :=
+L_OBJS :=
+LX_OBJS :=
+USBX_OBJS := usb.o hub.o usb-debug.o
+
+ifeq ($(CONFIG_USB_MOUSE),y)
+ USBX_OBJS += mouse.o
+endif
+
+ifeq ($(CONFIG_USB_KBD),y)
+ USBX_OBJS += keyboard.o keymap.o
+endif
+
+ifeq ($(CONFIG_USB_AUDIO),y)
+ USBX_OBJS += audio.o
+endif
+
+ifeq ($(CONFIG_USB), y)
+ L_OBJS += $(USBX_OBJS)
+endif
+
+ifeq ($(CONFIG_USB_UHCI),y)
+ ifeq ($(CONFIG_USB), y)
+ L_OBJS += uhci.o uhci-debug.o
+ else
+ ifeq ($(CONFIG_USB),m)
+ M_OBJS += usb-uhci.o
+ MIX_OBJS += $(USBX_OBJS)
+ endif
+ endif
+endif
+
+ifeq ($(CONFIG_USB_OHCI),y)
+ ifeq ($(CONFIG_USB), y)
+ L_OBJS += ohci.o ohci-debug.o
+ else
+ ifeq ($(CONFIG_USB),m)
+ USBO_OBJS += ohci.o ohci-debug.o
+ M_OBJS += usb-ohci.o
+ MIX_OBJS += $(USBX_OBJS)
+ endif
+ endif
+endif
+
+ifeq ($(CONFIG_USB_OHCI_HCD),y)
+ ifeq ($(CONFIG_USB), y)
+ L_OBJS += ohci-hcd.o ohci-root-hub.o
+ else
+ ifeq ($(CONFIG_USB),m)
+ USBO_OBJS += ohci-hcd.o ohci-root-hub.o
+ M_OBJS += usb-ohci-hcd.o
+ MIX_OBJS += $(USBX_OBJS)
+ endif
+ endif
+endif
+include $(TOPDIR)/Rules.make
+
+keymap.o: keymap.c
+
+keymap.c: maps/serial.map maps/usb.map maps/fixup.map
+ ./mkmap > $@
+
+usb-uhci.o: uhci.o uhci-debug.o $(USBX_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ uhci.o uhci-debug.o $(USBX_OBJS)
+
+usb-ohci.o: ohci.o ohci-debug.o $(USBX_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o $(USBX_OBJS)
+
+usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o $(USBX_OBJS)
+ $(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o $(USBX_OBJS)
+
diff --git a/drivers/usb/README.kbd b/drivers/usb/README.kbd
new file mode 100644
index 000000000..84a77d90f
--- /dev/null
+++ b/drivers/usb/README.kbd
@@ -0,0 +1,65 @@
+This is a simple USB keyboard driver written from Linus'
+USB driver (started with Greg's usb-0.03b.tar.gz source
+tree)
+
+It works fine with my BTC keyboard but I'm still investigating
+trouble with my MS keyboard (trouble starts with an inability
+to set into boot protocol mode, though, this very well could
+be all due to crappy hardware).
+
+Anyway, I would appreciate you taking a look if you have
+any USB keyboards lying around. Oh also, I'm doing this on
+UHCI so sorry if it breaks with OHCI.
+
+-ham
+
+
+
+Keyboard patch
+--------------
+
+Instead of using the multiple keyboard patch and then running into all
+of the kernel version problems that the current Linux-USB project has
+had, I'm just mapping the USB keycodes to the standard AT-101 keycodes
+and sending them directly to "handle_scancode".
+
+This may or may not be considered a hack. Anyway it is effective, and
+I think safe, and allows USB keyboards to coexist with a serial
+keyboard (oh yeah, one side effect is that you can for example hold
+down the control key on the serial keyboard and press "a" on the USB
+keyboard and you get Control-a like with Windows USB) and works
+fine for console and X.
+
+You do need to make a *tiny* patch the kernel source tree so that the
+function "handle_scancode" is exported from keyboard.c though.
+
+ $ cd /usr/src/linux
+ $ patch -p0 < kbd.patch
+
+And, of course, then, you need to rebuild and install the kernel.
+
+** [Vojtech]: Alternately, just 'insmod kbd-stub', if you don't want
+to use the keyboard and are too lazy to patch the kernel.
+
+Keyboard map
+------------
+
+I'm including a stupid utility "mkmap" which generates the USB->serial
+keymap. It takes in maps/serial.map (the current serial keymap,
+generated by "dumpkeys"), maps/usb.map (the USB keymap), and
+maps/fixup.map (fixes for e0 keys and misc.) and spits out keymap.c
+Anyway, it is not beautiful but should serve its purpose for the
+moment.
+
+Other changes
+-------------
+uhci.c:
+ * added a context value to the irq callback function
+ (this is exactly like the "dev_id" field to request_irq)
+ * played with uhci_reset_port to get better hot-plug results
+ (eg. do a wait_ms(200) before calling uhci_reset_port)
+usb.c:
+ * disconnect all devices after uhci-control thread is killed
+ * skip over the HID descriptor
+ * disconnect the high-level driver in usb_disconnect
+
diff --git a/drivers/usb/README.ohci b/drivers/usb/README.ohci
new file mode 100644
index 000000000..7a544eb33
--- /dev/null
+++ b/drivers/usb/README.ohci
@@ -0,0 +1,26 @@
+May 09, 1999 16:25:58
+
+Cool, things are working "well" now. (I'm not getting oops's from the
+OHCI code anyways.. ;). I can attach a usb hub and mouse in any
+possible arrangement of the two and they get configured properly.
+
+You can see that the mouse Interrupt transfers are occuring and being
+acknowledged because /proc/interrupts usb-ohci goes up accordingly with
+mouse movements/events. That means the TD at least returns some data
+and requeues itself.
+
+Device attach/detach from the root hub is not working well. Currently
+every interrupt checks for root hub status changes and frame number
+overflow interrupts are enabled. This means you shouldn't have to
+wait more than 32-33 seconds for the change to occur, less if there is
+other activity. (due to checking in the WDH caused interrupts)
+My OHCI controller [SiS 5598 motherboard] doesn't seem to play well
+with the RHSC interrupt so it has been disabled. The ohci_timer
+should be polling but it not currently working, I haven't had time to
+look into that problem.
+
+However, when I tried telling X to use /dev/psaux for the mouse my
+machine locked up...
+
+- greg@electricrain.com
+
diff --git a/drivers/usb/README.ohci_hcd b/drivers/usb/README.ohci_hcd
new file mode 100644
index 000000000..53bd35c95
--- /dev/null
+++ b/drivers/usb/README.ohci_hcd
@@ -0,0 +1,112 @@
+
+The OHCI HCD layer is a simple but nearly complete implementation of what the
+USB people would call a HCD for the OHCI.
+ (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled)
+It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree).
+The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers.
+
+- Roman Weissgaerber <weissg@vienna.at>
+
+
+ * v2.1 1999/05/09 ep_addr correction, code cleanup
+ * v0.2.0 1999/05/04
+ * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers)
+ * virtual root hub is now an option,
+ * memory allocation based on kmalloc and kfree now, simple Bus error handling,
+ * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion
+ *
+ * from Linus Torvalds (uhci.c): APM (not tested); hub, usb_device, bus and related stuff
+ * from Greg Smith (ohci.c): better reset ohci-controller handling, hub
+ *
+ * v0.1.0 1999/04/27 initial release
+
+to remove the module try:
+killall root-hub
+:
+rmmod usb-ohci-hcd
+
+Features:
+- virtual root hub, all basic hub descriptors and commands (state: complete)
+ this is an option now (v0.2.0)
+ #define CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB)
+ default is without.
+ (at the moment: the Virtual Root Hub option is not recommended)
+
+ files: ohci-root-hub.c, ohci-root-hub.h
+
+
+- Endpoint Descriptor (ED) handling more static approach
+ (EDs should be allocated in parallel to the SET CONFIGURATION command and they live
+ as long as the function (device) is alive or another configuration is choosen.
+ In the HCD layer the EDs has to be allocated manually either by calling a subroutine
+ or by sending a USB root hub vendor specific command to the virtual root hub.
+ At the alternate linux usb stack EDs will be added (allocated) at their first use.
+
+ files: ohci-hcd.c ohci-hcd.h
+ routines: (do not use for drivers, use the top layer alternate usb commands instead)
+
+ int usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr1,
+ int interval, int load, f_handler handler, int ep_size, int speed)
+ adds an endpoint, (if the endpoint already exists some parameters will be updated)
+
+ int usb_ohci_rm_ep(struct usb_ohci_ed *ed, struct ohci * ohci)
+ removes an endpoint and all pending TDs of that EP
+
+ usb_ohci_rm_function( struct ohci * ohci, union ep_addr_ ep_addr)
+ removes all Endpoints of a function (device)
+
+- Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers
+ The HCD takes care of TDs and EDs memory allocation whereas the upper layers (UBSD ...) has
+ to take care of buffer allocation.
+ files: ohci-hcd.c ohci-hcd.h
+
+ There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL):
+
+ int ohci_trans_req(struct ohci * ohci, int ep_addr, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1)
+
+ CTRL: ctrl, ctrl_len ... cmd buffer
+ data, data_len ... data buffer (in or out)
+ INT, BULK: ctrl = NULL, ctrl_len=0,
+ data, data_len ... data buffer (in or out)
+ ISO: tbd
+
+ There is no buffer reinsertion done by the internal HCD function.
+ (The interface layer does this for a INT-pipe on request.)
+ If you want a transfer then you have to
+ provide buffers by sending ohci_trans_req requests. As they are queued as TDs on an ED
+ you can send as many as you like. They should come back by the callback f_handler in
+ the same order (for each endpoint, not globally) If an error occurs all
+ queued transfers of an endpoint will return unsent. They will be marked with an error status.
+
+ e.g double-buffering for int transfers:
+
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
+
+ and when a data0 packet returns by the callback f_handler requeue it:
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data0, data0_len, 0,0)
+ and when a data1 packet returns by the callback f_handler requeue it:
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data1, data1_len, 0,0)
+
+ lw0, lw1 are private fields for upper layers for ids or fine grained handlers.
+ The alternate usb uses them for dev_id and usb_device_irq handler.
+
+
+- Done list handling: returns the requests (callback f_handler in ED) and does
+ some error handling, root-hub request dequeuing
+ (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0))
+
+ep_addr union or int is for addressing devices&endpoints:
+__u8 ep_addr.bep.ep ... bit 3..0 endpoint address
+ bit 4 0
+ bit 6,5 type: eg. 10 CTRL, 11 BULK, 01 INT, 00 ISO
+ bit 7 direction 1 IN, 0 OUT
+
+__u8 ep_addr.bep.fa ... bit 6..0 function address
+ bit 7 0
+
+(__u8 ep_addr.bep.hc ... host controller nr) not used
+(__u8 ep_addr.bep.host ... host nr) not used
+
+
+
diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c
new file mode 100644
index 000000000..8b0c9d15c
--- /dev/null
+++ b/drivers/usb/audio.c
@@ -0,0 +1,126 @@
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include "usb.h"
+
+static int usb_audio_probe(struct usb_device *dev);
+static void usb_audio_disconnect(struct usb_device *dev);
+static LIST_HEAD(usb_audio_list);
+
+struct usb_audio
+{
+ struct usb_device *dev;
+ struct list_head list;
+};
+
+static struct usb_driver usb_audio_driver =
+{
+ "audio",
+ usb_audio_probe,
+ usb_audio_disconnect,
+ {NULL, NULL}
+};
+
+
+static int usb_audio_irq(int state, void *buffer, void *dev_id)
+{
+ struct usb_audio *aud = (struct usb_audio*) dev_id;
+ return 1;
+}
+
+static int usb_audio_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_audio *aud;
+
+ int i;
+ int na=0;
+
+ interface = &dev->config[0].interface[0];
+
+ for(i=0;i<dev->config[0].bNumInterfaces;i++)
+ {
+ int x;
+
+ endpoint = &interface->endpoint[i];
+
+ if(interface->bInterfaceClass != 1)
+ continue;
+
+ printk(KERN_INFO "USB audio device detected.\n");
+
+ switch(interface->bInterfaceSubClass)
+ {
+ case 0x01:
+ printk(KERN_INFO "audio: Control device.\n");
+ break;
+ case 0x02:
+ printk(KERN_INFO "audio: streaming.\n");
+ break;
+ case 0x03:
+ printk(KERN_INFO "audio: nonstreaming.\n");
+ break;
+ }
+ na++;
+ }
+
+ if(na==0)
+ return -1;
+
+ aud = kmalloc(sizeof(struct usb_audio), GFP_KERNEL);
+ if(aud)
+ {
+ memset(aud, 0, sizeof(*aud));
+ aud->dev = dev;
+ dev->private = aud;
+
+ endpoint = &interface->endpoint[0];
+
+// usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+// usb_set_protocol(dev, 0);
+// usb_set_idle(dev, 0, 0);
+
+ usb_request_irq(dev,
+ usb_rcvctrlpipe(dev, endpoint->bEndpointAddress),
+ usb_audio_irq,
+ endpoint->bInterval,
+ aud);
+
+ list_add(&aud->list, &usb_audio_list);
+ }
+ return 0;
+}
+
+static void usb_audio_disconnect(struct usb_device *dev)
+{
+ struct usb_audio *aud = (struct usb_audio*) dev->private;
+ if(aud)
+ {
+ dev->private = NULL;
+ list_del(&aud->list);
+ kfree(aud);
+ }
+ printk(KERN_INFO "USB audio driver removed.\n");
+}
+
+int usb_audio_init(void)
+{
+ usb_register(&usb_audio_driver);
+ return 0;
+}
+
+/*
+ * Support functions for parsing
+ */
+
+void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data)
+{
+}
+
+void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data)
+{
+}
+
diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c
new file mode 100644
index 000000000..16789b944
--- /dev/null
+++ b/drivers/usb/hub.c
@@ -0,0 +1,422 @@
+/*
+ * USB hub driver.
+ *
+ * This is horrible, it knows about the UHCI driver
+ * internals, but it's just meant as a rough example,
+ * let's do the virtualization later when this works.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999 Johannes Erdfelt
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+#include "uhci.h"
+#include "hub.h"
+
+extern struct usb_operations uhci_device_operations;
+
+/* Wakes up khubd */
+static struct wait_queue *usb_hub_wait = NULL;
+static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
+
+/* List of hubs needing servicing */
+static struct list_head hub_event_list;
+
+/* PID of khubd */
+static int khubd_pid = 0;
+
+/*
+ * A irq handler returns non-zero to indicate to
+ * the low-level driver that it wants to be re-activated,
+ * or zero to say "I'm done".
+ */
+static int hub_irq(int status, void *__buffer, void *dev_id)
+{
+ struct usb_hub *hub = dev_id;
+ unsigned long flags;
+
+ if (waitqueue_active(&usb_hub_wait)) {
+ /* Add the hub to the event queue */
+ spin_lock_irqsave(&hub_event_lock, flags);
+ if (hub->event_list.next == &hub->event_list) {
+ list_add(&hub->event_list, &hub_event_list);
+ /* Wake up khubd */
+ wake_up(&usb_hub_wait);
+ }
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+ }
+
+ return 1;
+}
+
+static void usb_hub_configure(struct usb_hub *hub)
+{
+ struct usb_device *dev = hub->dev;
+ unsigned char hubdescriptor[8], buf[4];
+ int charac, i;
+
+ usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+
+ if (usb_get_hub_descriptor(dev, hubdescriptor, 8))
+ return;
+
+ hub->nports = dev->maxchild = hubdescriptor[2];
+ printk("hub: %d-port%s detected\n", hub->nports,
+ (hub->nports == 1) ? "" : "s");
+
+ charac = (hubdescriptor[4] << 8) + hubdescriptor[3];
+ switch (charac & HUB_CHAR_LPSM) {
+ case 0x00:
+ printk("hub: ganged power switching\n");
+ break;
+ case 0x01:
+ printk("hub: individual port power switching\n");
+ break;
+ case 0x02:
+ case 0x03:
+ printk("hub: unknown reserved power switching mode\n");
+ break;
+ }
+
+ if (charac & HUB_CHAR_COMPOUND)
+ printk("hub: part of a compound device\n");
+ else
+ printk("hub: standalone hub\n");
+
+ switch (charac & HUB_CHAR_OCPM) {
+ case 0x00:
+ printk("hub: global over current protection\n");
+ break;
+ case 0x08:
+ printk("hub: individual port over current protection\n");
+ break;
+ case 0x10:
+ case 0x18:
+ printk("hub: no over current protection\n");
+ break;
+ }
+
+ printk("hub: power on to power good time: %dms\n",
+ hubdescriptor[5] * 2);
+
+ printk("hub: hub controller current requirement: %dmA\n",
+ hubdescriptor[6]);
+
+ for (i = 0; i < dev->maxchild; i++)
+ printk("hub: port %d is%s removable\n", i + 1,
+ hubdescriptor[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8))
+ ? " not" : "");
+
+ if (usb_get_hub_status(dev, buf))
+ return;
+
+ printk("hub: local power source is %s\n",
+ (buf[0] & 1) ? "lost (inactive)" : "good");
+
+ printk("hub: %sover current condition exists\n",
+ (buf[0] & 2) ? "" : "no ");
+
+#if 0
+ for (i = 0; i < hub->nports; i++) {
+ int portstat, portchange;
+ unsigned char portstatus[4];
+
+ if (usb_get_port_status(dev, i + 1, portstatus))
+ return;
+ portstat = (portstatus[1] << 8) + portstatus[0];
+ portchange = (portstatus[3] << 8) + portstatus[2];
+
+ printk("hub: port %d status\n", i + 1);
+ printk("hub: %sdevice present\n", (portstat & 1) ? "" : "no ");
+ printk("hub: %s\n", (portstat & 2) ? "enabled" : "disabled");
+ printk("hub: %ssuspended\n", (portstat & 4) ? "" : "not ");
+ printk("hub: %sover current\n", (portstat & 8) ? "" : "not ");
+ printk("hub: has %spower\n", (portstat & 0x100) ? "" : "no ");
+ printk("hub: %s speed\n", (portstat & 0x200) ? "low" : "full");
+ }
+#endif
+
+ /* Enable power to the ports */
+ printk("enabling power on all ports\n");
+ for (i = 0; i < hub->nports; i++)
+ usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
+}
+
+static int hub_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_hub *hub;
+
+ /* We don't handle multi-config hubs */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -1;
+
+ /* We don't handle multi-interface hubs */
+ if (dev->config[0].bNumInterfaces != 1)
+ return -1;
+
+ interface = &dev->config[0].interface[0];
+
+ /* Is it a hub? */
+ if (interface->bInterfaceClass != 9)
+ return -1;
+ if ((interface->bInterfaceSubClass != 0) &&
+ (interface->bInterfaceSubClass != 1))
+ return -1;
+
+ /* Multiple endpoints? What kind of mutant ninja-hub is this? */
+ if (interface->bNumEndpoints != 1)
+ return -1;
+
+ endpoint = &interface->endpoint[0];
+
+ /* Output endpoint? Curiousier and curiousier.. */
+ if (!(endpoint->bEndpointAddress & 0x80))
+ return -1;
+
+ /* If it's not an interrupt endpoint, we'd better punt! */
+ if ((endpoint->bmAttributes & 3) != 3)
+ return -1;
+
+ /* We found a hub */
+ printk("USB hub found\n");
+
+ if ((hub = kmalloc(sizeof(*hub), GFP_KERNEL)) == NULL) {
+ printk("couldn't kmalloc hub struct\n");
+ return -1;
+ }
+
+ memset(hub, 0, sizeof(*hub));
+
+ dev->private = hub;
+
+ INIT_LIST_HEAD(&hub->event_list);
+ hub->dev = dev;
+
+ usb_hub_configure(hub);
+
+ usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), hub_irq, endpoint->bInterval, hub);
+
+ /* Wake up khubd */
+ wake_up(&usb_hub_wait);
+
+ return 0;
+}
+
+static void hub_disconnect(struct usb_device *dev)
+{
+ struct usb_hub *hub = dev->private;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hub_event_lock, flags);
+
+ /* Delete it and then reset it */
+ list_del(&hub->event_list);
+ INIT_LIST_HEAD(&hub->event_list);
+
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+
+ /* Free the memory */
+ kfree(hub);
+}
+
+static void usb_hub_port_connect_change(struct usb_device *hub, int port)
+{
+ struct usb_device *usb;
+ unsigned char buf[4];
+ unsigned short portstatus, portchange;
+
+ usb_disconnect(&hub->children[port]);
+
+ usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
+
+ wait_ms(50); /* FIXME: This is from the *BSD stack, thanks! :) */
+
+ if (usb_get_port_status(hub, port + 1, buf)) {
+ printk("get_port_status failed\n");
+ return;
+ }
+
+ portstatus = *((unsigned short *)buf + 0);
+ portchange = *((unsigned short *)buf + 1);
+
+ if ((!(portstatus & USB_PORT_STAT_CONNECTION)) &&
+ (!(portstatus & USB_PORT_STAT_ENABLE))) {
+ /* We're done now, we already disconnected the device */
+ /* printk("not connected/enabled\n"); */
+ return;
+ }
+
+ usb = hub->bus->op->allocate(hub);
+ if (!usb) {
+ printk("couldn't allocate usb_device\n");
+ return;
+ }
+
+ usb_connect(usb);
+
+ usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0;
+
+ hub->children[port] = usb;
+
+ usb_new_device(usb);
+}
+
+static void usb_hub_events(void)
+{
+ unsigned long flags;
+ unsigned char buf[4];
+ unsigned short portstatus, portchange;
+ int i;
+ struct list_head *next, *tmp, *head = &hub_event_list;
+ struct usb_device *dev;
+ struct usb_hub *hub;
+
+ spin_lock_irqsave(&hub_event_lock, flags);
+
+ tmp = head->next;
+ while (tmp != head) {
+ hub = list_entry(tmp, struct usb_hub, event_list);
+ dev = hub->dev;
+
+ next = tmp->next;
+
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+
+ for (i = 0; i < hub->nports; i++) {
+ if (usb_get_port_status(dev, i + 1, buf)) {
+ printk("get_port_status failed\n");
+ continue;
+ }
+
+ portstatus = *((unsigned short *)buf + 0);
+ portchange = *((unsigned short *)buf + 1);
+
+ if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ printk("hub: port %d connection change\n", i + 1);
+
+ usb_clear_port_feature(dev, i + 1,
+ USB_PORT_FEAT_C_CONNECTION);
+
+ usb_hub_port_connect_change(dev, i);
+ }
+
+ if (portchange & USB_PORT_STAT_C_ENABLE) {
+ printk("hub: port %d enable change\n", i + 1);
+ usb_clear_port_feature(dev, i + 1,
+ USB_PORT_FEAT_C_ENABLE);
+ }
+
+ if (portchange & USB_PORT_STAT_C_SUSPEND)
+ printk("hub: port %d suspend change\n", i + 1);
+
+ if (portchange & USB_PORT_STAT_C_OVERCURRENT)
+ printk("hub: port %d over-current change\n", i + 1);
+
+ if (portchange & USB_PORT_STAT_C_RESET) {
+ printk("hub: port %d reset change\n", i + 1);
+ usb_clear_port_feature(dev, i + 1,
+ USB_PORT_FEAT_C_RESET);
+ }
+
+#if 0
+ if (!portchange)
+ continue;
+
+ if (usb_get_port_status(dev, i + 1, buf))
+ return;
+
+ portstatus = (buf[1] << 8) + buf[0];
+ portchange = (buf[3] << 8) + buf[2];
+
+ printk("hub: port %d status\n", i + 1);
+ printk("hub: %sdevice present\n", (portstatus & 1) ? "" : "no ");
+ printk("hub: %s\n", (portstatus & 2) ? "enabled" : "disabled");
+ printk("hub: %ssuspended\n", (portstatus & 4) ? "" : "not ");
+ printk("hub: %sover current\n", (portstatus & 8) ? "" : "not ");
+ printk("hub: has %spower\n", (portstatus & 0x100) ? "" : "no ");
+ printk("hub: %s speed\n", (portstatus & 0x200) ? "low" : "full");
+#endif
+ }
+ tmp = next;
+#if 0
+ wait_ms(1000);
+#endif
+ }
+
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+}
+
+static int usb_hub_thread(void *__hub)
+{
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources
+ */
+ printk("usb_hub_thread at %p\n", &usb_hub_thread);
+ exit_mm(current);
+ exit_files(current);
+ exit_fs(current);
+
+ /* Setup a nice name */
+ strcpy(current->comm, "khubd");
+
+ /* Send me a signal to get me die (for debugging) */
+ do {
+ interruptible_sleep_on(&usb_hub_wait);
+ usb_hub_events();
+ } while (!signal_pending(current));
+
+ printk("usb_hub_thread exiting\n");
+
+ return 0;
+}
+
+static struct usb_driver hub_driver = {
+ "hub",
+ hub_probe,
+ hub_disconnect,
+ { NULL, NULL }
+};
+
+/*
+ * This should be a separate module.
+ */
+int hub_init(void)
+{
+ int pid;
+
+ INIT_LIST_HEAD(&hub_event_list);
+
+ usb_register(&hub_driver);
+ pid = kernel_thread(usb_hub_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (pid >= 0) {
+ khubd_pid = pid;
+ return 0;
+ }
+
+ /* Fall through if kernel_thread failed */
+ usb_deregister(&hub_driver);
+
+ return 0;
+}
+
+void hub_cleanup(void)
+{
+ if (khubd_pid >= 0)
+ kill_proc(khubd_pid, SIGINT, 1);
+
+ usb_deregister(&hub_driver);
+}
diff --git a/drivers/usb/hub.h b/drivers/usb/hub.h
new file mode 100644
index 000000000..d015c5a33
--- /dev/null
+++ b/drivers/usb/hub.h
@@ -0,0 +1,80 @@
+#ifndef __LINUX_HUB_H
+#define __LINUX_HUB_H
+
+#include <linux/list.h>
+
+/*
+ * Hub feature numbers
+ */
+#define C_HUB_LOCAL_POWER 0
+#define C_HUB_OVER_CURRENT 1
+
+/*
+ * Port feature numbers
+ */
+#define USB_PORT_FEAT_ENABLE 1
+#define USB_PORT_FEAT_SUSPEND 2
+#define USB_PORT_FEAT_OVER_CURRENT 3
+#define USB_PORT_FEAT_RESET 4
+#define USB_PORT_FEAT_POWER 8
+#define USB_PORT_FEAT_LOWSPEED 9
+#define USB_PORT_FEAT_C_CONNECTION 16
+#define USB_PORT_FEAT_C_ENABLE 17
+#define USB_PORT_FEAT_C_SUSPEND 18
+#define USB_PORT_FEAT_C_OVER_CURRENT 19
+#define USB_PORT_FEAT_C_RESET 20
+
+/* wPortStatus */
+#define USB_PORT_STAT_CONNECTION 0x0001
+#define USB_PORT_STAT_ENABLE 0x0002
+#define USB_PORT_STAT_SUSPEND 0x0004
+#define USB_PORT_STAT_OVERCURRENT 0x0008
+#define USB_PORT_STAT_RESET 0x0010
+#define USB_PORT_STAT_POWER 0x0100
+#define USB_PORT_STAT_LOW_SPEED 0x0200
+
+/* wPortChange */
+#define USB_PORT_STAT_C_CONNECTION 0x0001
+#define USB_PORT_STAT_C_ENABLE 0x0002
+#define USB_PORT_STAT_C_SUSPEND 0x0004
+#define USB_PORT_STAT_C_OVERCURRENT 0x0008
+#define USB_PORT_STAT_C_RESET 0x0010
+
+/* Characteristics */
+#define HUB_CHAR_LPSM 0x0003
+#define HUB_CHAR_COMPOUND 0x0004
+#define HUB_CHAR_OCPM 0x0018
+
+struct usb_device;
+
+typedef enum {
+ USB_PORT_UNPOWERED = 0, /* Default state */
+ USB_PORT_POWERED, /* When we've put power to it */
+ USB_PORT_ENABLED, /* When it's been enabled */
+ USB_PORT_DISABLED, /* If it's been disabled */
+ USB_PORT_ADMINDISABLED, /* Forced down */
+} usb_hub_port_state;
+
+struct usb_hub_port {
+ usb_hub_port_state cstate; /* Configuration state */
+
+ struct usb_device *child; /* Device attached to this port */
+
+ struct usb_hub *parent; /* Parent hub */
+};
+
+struct usb_hub {
+ /* Device structure */
+ struct usb_device *dev;
+
+ /* Temporary event list */
+ struct list_head event_list;
+
+ /* Number of ports on the hub */
+ int nports;
+
+ struct usb_hub_port ports[0]; /* Dynamically allocated */
+};
+
+#endif
+
diff --git a/drivers/usb/inits.h b/drivers/usb/inits.h
new file mode 100644
index 000000000..81979ce39
--- /dev/null
+++ b/drivers/usb/inits.h
@@ -0,0 +1,6 @@
+int bp_mouse_init(void);
+int usb_kbd_init(void);
+int usb_audio_init(void);
+int hub_init(void);
+void hub_cleanup(void);
+void usb_mouse_cleanup(void);
diff --git a/drivers/usb/keyboard.c b/drivers/usb/keyboard.c
new file mode 100644
index 000000000..c60c812a5
--- /dev/null
+++ b/drivers/usb/keyboard.c
@@ -0,0 +1,226 @@
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/kbd_ll.h>
+#include "usb.h"
+
+#define PCKBD_PRESSED 0x00
+#define PCKBD_RELEASED 0x80
+#define PCKBD_NEEDS_E0 0x80
+
+#define USBKBD_MODIFIER_BASE 120
+#define USBKBD_KEYCODE_OFFSET 2
+#define USBKBD_KEYCODE_COUNT 6
+
+#define USBKBD_VALID_KEYCODE(key) ((unsigned char)(key) > 3)
+#define USBKBD_FIND_KEYCODE(down, key, count) \
+ ((unsigned char*) memscan((down), (key), (count)) < ((down) + (count)))
+
+#define USBKBD_REPEAT_DELAY (HZ / 4)
+#define USBKBD_REPEAT_RATE (HZ / 20)
+
+struct usb_keyboard
+{
+ struct usb_device *dev;
+ unsigned long down[2];
+ unsigned char repeat_key;
+ struct timer_list repeat_timer;
+ struct list_head list;
+};
+
+extern unsigned char usb_kbd_map[];
+
+static int usb_kbd_probe(struct usb_device *dev);
+static void usb_kbd_disconnect(struct usb_device *dev);
+static void usb_kbd_repeat(unsigned long dummy);
+
+static LIST_HEAD(usb_kbd_list);
+
+static struct usb_driver usb_kbd_driver =
+{
+ "keyboard",
+ usb_kbd_probe,
+ usb_kbd_disconnect,
+ {NULL, NULL}
+};
+
+
+static void
+usb_kbd_handle_key(unsigned char key, int down)
+{
+ int scancode = (int) usb_kbd_map[key];
+ if(scancode)
+ {
+ if(scancode & PCKBD_NEEDS_E0)
+ {
+ handle_scancode(0xe0, 1);
+ }
+ handle_scancode((scancode & ~PCKBD_NEEDS_E0), down);
+ }
+}
+
+static void
+usb_kbd_repeat(unsigned long dev_id)
+{
+ struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id;
+
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+
+ if(kbd->repeat_key)
+ {
+ usb_kbd_handle_key(kbd->repeat_key, 1);
+
+ /* reset repeat timer */
+ kbd->repeat_timer.function = usb_kbd_repeat;
+ kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_RATE;
+ kbd->repeat_timer.data = (unsigned long) kbd;
+ kbd->repeat_timer.prev = NULL;
+ kbd->repeat_timer.next = NULL;
+ add_timer(&kbd->repeat_timer);
+ }
+
+ restore_flags(flags);
+}
+
+static int
+usb_kbd_irq(int state, void *buffer, void *dev_id)
+{
+ struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id;
+ unsigned long *down = (unsigned long*) buffer;
+
+ if(kbd->down[0] != down[0] || kbd->down[1] != down[1])
+ {
+ unsigned char *olddown, *newdown;
+ unsigned char modsdelta, key;
+ int i;
+
+ /* handle modifier change */
+ modsdelta = (*(unsigned char*) down ^ *(unsigned char*) kbd->down);
+ if(modsdelta)
+ {
+ for(i = 0; i < 8; i++)
+ {
+ if(modsdelta & 0x01)
+ {
+ int pressed = (*(unsigned char*) down >> i) & 0x01;
+ usb_kbd_handle_key(
+ i + USBKBD_MODIFIER_BASE,
+ pressed);
+ }
+ modsdelta >>= 1;
+ }
+ }
+
+ olddown = (unsigned char*) kbd->down + USBKBD_KEYCODE_OFFSET;
+ newdown = (unsigned char*) down + USBKBD_KEYCODE_OFFSET;
+
+ /* handle released keys */
+ for(i = 0; i < USBKBD_KEYCODE_COUNT; i++)
+ {
+ key = olddown[i];
+ if(USBKBD_VALID_KEYCODE(key)
+ && !USBKBD_FIND_KEYCODE(newdown, key, USBKBD_KEYCODE_COUNT))
+ {
+ usb_kbd_handle_key(key, 0);
+ }
+ }
+
+ /* handle pressed keys */
+ kbd->repeat_key = 0;
+ for(i = 0; i < USBKBD_KEYCODE_COUNT; i++)
+ {
+ key = newdown[i];
+ if(USBKBD_VALID_KEYCODE(key)
+ && !USBKBD_FIND_KEYCODE(olddown, key, USBKBD_KEYCODE_COUNT))
+ {
+ usb_kbd_handle_key(key, 1);
+ kbd->repeat_key = key;
+ }
+ }
+
+ /* set repeat timer if any keys were pressed */
+ if(kbd->repeat_key)
+ {
+ del_timer(&kbd->repeat_timer);
+ kbd->repeat_timer.function = usb_kbd_repeat;
+ kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_DELAY;
+ kbd->repeat_timer.data = (unsigned long) kbd;
+ kbd->repeat_timer.prev = NULL;
+ kbd->repeat_timer.next = NULL;
+ add_timer(&kbd->repeat_timer);
+ }
+
+ kbd->down[0] = down[0];
+ kbd->down[1] = down[1];
+ }
+
+ return 1;
+}
+
+static int
+usb_kbd_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_keyboard *kbd;
+
+ interface = &dev->config[0].interface[0];
+ endpoint = &interface->endpoint[0];
+
+ if(interface->bInterfaceClass != 3
+ || interface->bInterfaceSubClass != 1
+ || interface->bInterfaceProtocol != 1)
+ {
+ return -1;
+ }
+
+ printk(KERN_INFO "USB HID boot protocol keyboard detected.\n");
+
+ kbd = kmalloc(sizeof(struct usb_keyboard), GFP_KERNEL);
+ if(kbd)
+ {
+ memset(kbd, 0, sizeof(*kbd));
+ kbd->dev = dev;
+ dev->private = kbd;
+
+ usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+ usb_set_protocol(dev, 0);
+ usb_set_idle(dev, 0, 0);
+
+ usb_request_irq(dev,
+ usb_rcvctrlpipe(dev, endpoint->bEndpointAddress),
+ usb_kbd_irq,
+ endpoint->bInterval,
+ kbd);
+
+ list_add(&kbd->list, &usb_kbd_list);
+ }
+
+ return 0;
+}
+
+static void
+usb_kbd_disconnect(struct usb_device *dev)
+{
+ struct usb_keyboard *kbd = (struct usb_keyboard*) dev->private;
+ if(kbd)
+ {
+ dev->private = NULL;
+ list_del(&kbd->list);
+ del_timer(&kbd->repeat_timer);
+ kfree(kbd);
+ }
+
+ printk(KERN_INFO "USB HID boot protocol keyboard removed.\n");
+}
+
+int
+usb_kbd_init(void)
+{
+ usb_register(&usb_kbd_driver);
+ return 0;
+}
diff --git a/drivers/usb/keymap.c b/drivers/usb/keymap.c
new file mode 100644
index 000000000..16c7b28f3
--- /dev/null
+++ b/drivers/usb/keymap.c
@@ -0,0 +1,50 @@
+unsigned char usb_kbd_map[256] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x2e, 0x20,
+ 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26,
+
+ 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14,
+ 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03,
+
+ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x1c, 0x01, 0xd3, 0x0f, 0x39, 0x0c, 0x0d, 0x1a,
+
+ 0x1b, 0x2b, 0x00, 0x27, 0x28, 0x29, 0x33, 0x34,
+ 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
+
+ 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xb7, 0x46,
+ 0x00, 0xd2, 0xc7, 0xc9, 0x63, 0xcf, 0xd1, 0xcd,
+
+ 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e,
+ 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47,
+
+ 0x48, 0x49, 0x52, 0x53, 0x00, 0x6d, 0x00, 0x00,
+ 0xbd, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/drivers/usb/maps/.cvsignore b/drivers/usb/maps/.cvsignore
new file mode 100644
index 000000000..b566130de
--- /dev/null
+++ b/drivers/usb/maps/.cvsignore
@@ -0,0 +1,5 @@
+.depend
+.*.flags
+conmakehash
+consolemap_deftbl.c
+uni_hash.tbl
diff --git a/drivers/usb/maps/fixup.map b/drivers/usb/maps/fixup.map
new file mode 100644
index 000000000..fc5d1ed7f
--- /dev/null
+++ b/drivers/usb/maps/fixup.map
@@ -0,0 +1,31 @@
+# misc fixes
+keycode 0 = Pause
+keycode 29 = Control
+keycode 99 = Remove
+keycode 42 = Shift
+keycode 54 = Shift_R
+keycode 109 = Application
+
+# E0 keys (or'ed with 0x80)
+keycode 156 = KP_Enter
+keycode 157 = Control_R
+keycode 181 = KP_Divide
+keycode 183 = Print_Screen
+keycode 184 = Alt_R
+keycode 189 = F13
+keycode 190 = F14
+keycode 193 = F17
+keycode 198 = Break
+keycode 199 = Home
+keycode 200 = Up
+keycode 201 = Prior
+keycode 203 = Left
+keycode 205 = Right
+keycode 207 = End
+keycode 208 = Down
+keycode 209 = Next
+keycode 210 = Insert
+keycode 211 = Delete
+keycode 219 = Window
+keycode 220 = Window_R
+keycode 221 = Menu
diff --git a/drivers/usb/maps/serial.map b/drivers/usb/maps/serial.map
new file mode 100644
index 000000000..e421a0d23
--- /dev/null
+++ b/drivers/usb/maps/serial.map
@@ -0,0 +1,370 @@
+keymaps 0-2,4-6,8-9,12
+keycode 1 = Escape
+ alt keycode 1 = Meta_Escape
+ shift alt keycode 1 = Meta_Escape
+ control alt keycode 1 = Meta_Escape
+keycode 2 = one exclam
+ alt keycode 2 = Meta_one
+ shift alt keycode 2 = Meta_exclam
+keycode 3 = two at at nul nul
+ alt keycode 3 = Meta_two
+ shift alt keycode 3 = Meta_at
+ control alt keycode 3 = Meta_nul
+keycode 4 = three numbersign
+ control keycode 4 = Escape
+ alt keycode 4 = Meta_three
+ shift alt keycode 4 = Meta_numbersign
+keycode 5 = four dollar dollar Control_backslash
+ alt keycode 5 = Meta_four
+ shift alt keycode 5 = Meta_dollar
+ control alt keycode 5 = Meta_Control_backslash
+keycode 6 = five percent
+ control keycode 6 = Control_bracketright
+ alt keycode 6 = Meta_five
+ shift alt keycode 6 = Meta_percent
+keycode 7 = six asciicircum
+ control keycode 7 = Control_asciicircum
+ alt keycode 7 = Meta_six
+ shift alt keycode 7 = Meta_asciicircum
+keycode 8 = seven ampersand braceleft Control_underscore
+ alt keycode 8 = Meta_seven
+ shift alt keycode 8 = Meta_ampersand
+ control alt keycode 8 = Meta_Control_underscore
+keycode 9 = eight asterisk bracketleft Delete
+ alt keycode 9 = Meta_eight
+ shift alt keycode 9 = Meta_asterisk
+ control alt keycode 9 = Meta_Delete
+keycode 10 = nine parenleft bracketright
+ alt keycode 10 = Meta_nine
+ shift alt keycode 10 = Meta_parenleft
+keycode 11 = zero parenright braceright
+ alt keycode 11 = Meta_zero
+ shift alt keycode 11 = Meta_parenright
+keycode 12 = minus underscore backslash Control_underscore Control_underscore
+ alt keycode 12 = Meta_minus
+ shift alt keycode 12 = Meta_underscore
+ control alt keycode 12 = Meta_Control_underscore
+keycode 13 = equal plus
+ alt keycode 13 = Meta_equal
+ shift alt keycode 13 = Meta_plus
+keycode 14 = Delete
+ alt keycode 14 = Meta_Delete
+ shift alt keycode 14 = Meta_Delete
+ control alt keycode 14 = Meta_Delete
+keycode 15 = Tab
+ alt keycode 15 = Meta_Tab
+ shift alt keycode 15 = Meta_Tab
+ control alt keycode 15 = Meta_Tab
+keycode 16 = q
+keycode 17 = w
+keycode 18 = e
+keycode 19 = r
+keycode 20 = t
+keycode 21 = y
+keycode 22 = u
+keycode 23 = i
+keycode 24 = o
+keycode 25 = p
+keycode 26 = bracketleft braceleft
+ control keycode 26 = Escape
+ alt keycode 26 = Meta_bracketleft
+ shift alt keycode 26 = Meta_braceleft
+keycode 27 = bracketright braceright asciitilde Control_bracketright
+ alt keycode 27 = Meta_bracketright
+ shift alt keycode 27 = Meta_braceright
+ control alt keycode 27 = Meta_Control_bracketright
+keycode 28 = Return
+ alt keycode 28 = Meta_Control_m
+keycode 29 = Control
+keycode 30 = a
+keycode 31 = s
+keycode 32 = d
+keycode 33 = f
+keycode 34 = g
+keycode 35 = h
+keycode 36 = j
+keycode 37 = k
+keycode 38 = l
+keycode 39 = semicolon colon
+ alt keycode 39 = Meta_semicolon
+ shift alt keycode 39 = Meta_colon
+keycode 40 = apostrophe quotedbl
+ control keycode 40 = Control_g
+ alt keycode 40 = Meta_apostrophe
+ shift alt keycode 40 = Meta_quotedbl
+keycode 41 = grave asciitilde
+ control keycode 41 = nul
+ alt keycode 41 = Meta_grave
+ shift alt keycode 41 = Meta_asciitilde
+keycode 42 = Shift
+keycode 43 = backslash bar
+ control keycode 43 = Control_backslash
+ alt keycode 43 = Meta_backslash
+ shift alt keycode 43 = Meta_bar
+keycode 44 = z
+keycode 45 = x
+keycode 46 = c
+keycode 47 = v
+keycode 48 = b
+keycode 49 = n
+keycode 50 = m
+keycode 51 = comma less
+ alt keycode 51 = Meta_comma
+ shift alt keycode 51 = Meta_less
+keycode 52 = period greater
+ alt keycode 52 = Meta_period
+ shift alt keycode 52 = Meta_greater
+keycode 53 = slash question
+ control keycode 53 = Delete
+ alt keycode 53 = Meta_slash
+ shift alt keycode 53 = Meta_question
+keycode 54 = Shift
+keycode 55 = KP_Multiply
+ altgr keycode 55 = Hex_C
+keycode 56 = Alt
+keycode 57 = space
+ control keycode 57 = nul
+ alt keycode 57 = Meta_space
+ shift alt keycode 57 = Meta_space
+ control alt keycode 57 = Meta_nul
+keycode 58 = Caps_Lock
+keycode 59 = F1 F13 Console_13 F25
+ alt keycode 59 = Console_1
+ control alt keycode 59 = Console_1
+keycode 60 = F2 F14 Console_14 F26
+ alt keycode 60 = Console_2
+ control alt keycode 60 = Console_2
+keycode 61 = F3 F15 Console_15 F27
+ alt keycode 61 = Console_3
+ control alt keycode 61 = Console_3
+keycode 62 = F4 F16 Console_16 F28
+ alt keycode 62 = Console_4
+ control alt keycode 62 = Console_4
+keycode 63 = F5 F17 Console_17 F29
+ alt keycode 63 = Console_5
+ control alt keycode 63 = Console_5
+keycode 64 = F6 F18 Console_18 F30
+ alt keycode 64 = Console_6
+ control alt keycode 64 = Console_6
+keycode 65 = F7 F19 Console_19 F31
+ alt keycode 65 = Console_7
+ control alt keycode 65 = Console_7
+keycode 66 = F8 F20 Console_20 F32
+ alt keycode 66 = Console_8
+ control alt keycode 66 = Console_8
+keycode 67 = F9 F21 Console_21 F33
+ alt keycode 67 = Console_9
+ control alt keycode 67 = Console_9
+keycode 68 = F10 F22 Console_22 F34
+ alt keycode 68 = Console_10
+ control alt keycode 68 = Console_10
+keycode 69 = Num_Lock
+ altgr keycode 69 = Hex_E
+keycode 70 = Scroll_Lock Show_Memory Show_Registers Show_State
+ alt keycode 70 = Scroll_Lock
+keycode 71 = KP_7
+ altgr keycode 71 = Hex_7
+ alt keycode 71 = Ascii_7
+keycode 72 = KP_8
+ altgr keycode 72 = Hex_8
+ alt keycode 72 = Ascii_8
+keycode 73 = KP_9
+ altgr keycode 73 = Hex_9
+ alt keycode 73 = Ascii_9
+keycode 74 = KP_Subtract
+keycode 75 = KP_4
+ altgr keycode 75 = Hex_4
+ alt keycode 75 = Ascii_4
+keycode 76 = KP_5
+ altgr keycode 76 = Hex_5
+ alt keycode 76 = Ascii_5
+keycode 77 = KP_6
+ altgr keycode 77 = Hex_6
+ alt keycode 77 = Ascii_6
+keycode 78 = KP_Add
+keycode 79 = KP_1
+ altgr keycode 79 = Hex_1
+ alt keycode 79 = Ascii_1
+keycode 80 = KP_2
+ altgr keycode 80 = Hex_2
+ alt keycode 80 = Ascii_2
+keycode 81 = KP_3
+ altgr keycode 81 = Hex_3
+ alt keycode 81 = Ascii_3
+keycode 82 = KP_0
+ altgr keycode 82 = Hex_0
+ alt keycode 82 = Ascii_0
+keycode 83 = KP_Period
+ altgr control keycode 83 = Boot
+ control alt keycode 83 = Boot
+keycode 84 = Last_Console
+keycode 85 =
+keycode 86 = less greater bar
+ alt keycode 86 = Meta_less
+ shift alt keycode 86 = Meta_greater
+keycode 87 = F11 F23 Console_23 F35
+ alt keycode 87 = Console_11
+ control alt keycode 87 = Console_11
+keycode 88 = F12 F24 Console_24 F36
+ alt keycode 88 = Console_12
+ control alt keycode 88 = Console_12
+keycode 89 =
+keycode 90 =
+keycode 91 =
+keycode 92 =
+keycode 93 =
+keycode 94 =
+keycode 95 =
+keycode 96 = KP_Enter
+keycode 97 = Control
+keycode 98 = KP_Divide
+ altgr keycode 98 = Hex_B
+keycode 99 = Control_backslash
+ alt keycode 99 = Meta_Control_backslash
+ shift alt keycode 99 = Meta_Control_backslash
+ control alt keycode 99 = Meta_Control_backslash
+keycode 100 = AltGr
+keycode 101 = Break
+keycode 102 = Find
+keycode 103 = Up
+ alt keycode 103 = KeyboardSignal
+keycode 104 = Prior
+ shift keycode 104 = Scroll_Backward
+keycode 105 = Left
+ alt keycode 105 = Decr_Console
+keycode 106 = Right
+ alt keycode 106 = Incr_Console
+keycode 107 = Select
+keycode 108 = Down
+keycode 109 = Next
+ shift keycode 109 = Scroll_Forward
+keycode 110 = Insert
+keycode 111 = Remove
+ altgr control keycode 111 = Boot
+ control alt keycode 111 = Boot
+keycode 112 = Macro
+ altgr control keycode 112 = VoidSymbol
+ shift alt keycode 112 = VoidSymbol
+keycode 113 = F13
+ altgr control keycode 113 = VoidSymbol
+ shift alt keycode 113 = VoidSymbol
+keycode 114 = F14
+ altgr control keycode 114 = VoidSymbol
+ shift alt keycode 114 = VoidSymbol
+keycode 115 = Help
+ altgr control keycode 115 = VoidSymbol
+ shift alt keycode 115 = VoidSymbol
+keycode 116 = Do
+ altgr control keycode 116 = VoidSymbol
+ shift alt keycode 116 = VoidSymbol
+keycode 117 = F17
+ altgr control keycode 117 = VoidSymbol
+ shift alt keycode 117 = VoidSymbol
+keycode 118 = KP_MinPlus
+ altgr control keycode 118 = VoidSymbol
+ shift alt keycode 118 = VoidSymbol
+keycode 119 = Pause
+keycode 120 =
+keycode 121 =
+keycode 122 =
+keycode 123 =
+keycode 124 =
+keycode 125 =
+keycode 126 =
+keycode 127 =
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to 'À'
+compose '`' 'a' to 'à'
+compose '\'' 'A' to 'Á'
+compose '\'' 'a' to 'á'
+compose '^' 'A' to 'Â'
+compose '^' 'a' to 'â'
+compose '~' 'A' to 'Ã'
+compose '~' 'a' to 'ã'
+compose '"' 'A' to 'Ä'
+compose '"' 'a' to 'ä'
+compose 'O' 'A' to 'Å'
+compose 'o' 'a' to 'å'
+compose '0' 'A' to 'Å'
+compose '0' 'a' to 'å'
+compose 'A' 'A' to 'Å'
+compose 'a' 'a' to 'å'
+compose 'A' 'E' to 'Æ'
+compose 'a' 'e' to 'æ'
+compose ',' 'C' to 'Ç'
+compose ',' 'c' to 'ç'
+compose '`' 'E' to 'È'
+compose '`' 'e' to 'è'
+compose '\'' 'E' to 'É'
+compose '\'' 'e' to 'é'
+compose '^' 'E' to 'Ê'
+compose '^' 'e' to 'ê'
+compose '"' 'E' to 'Ë'
+compose '"' 'e' to 'ë'
+compose '`' 'I' to 'Ì'
+compose '`' 'i' to 'ì'
+compose '\'' 'I' to 'Í'
+compose '\'' 'i' to 'í'
+compose '^' 'I' to 'Î'
+compose '^' 'i' to 'î'
+compose '"' 'I' to 'Ï'
+compose '"' 'i' to 'ï'
+compose '-' 'D' to 'Ð'
+compose '-' 'd' to 'ð'
+compose '~' 'N' to 'Ñ'
+compose '~' 'n' to 'ñ'
+compose '`' 'O' to 'Ò'
+compose '`' 'o' to 'ò'
+compose '\'' 'O' to 'Ó'
+compose '\'' 'o' to 'ó'
+compose '^' 'O' to 'Ô'
+compose '^' 'o' to 'ô'
+compose '~' 'O' to 'Õ'
+compose '~' 'o' to 'õ'
+compose '"' 'O' to 'Ö'
+compose '"' 'o' to 'ö'
+compose '/' 'O' to 'Ø'
+compose '/' 'o' to 'ø'
+compose '`' 'U' to 'Ù'
+compose '`' 'u' to 'ù'
+compose '\'' 'U' to 'Ú'
+compose '\'' 'u' to 'ú'
+compose '^' 'U' to 'Û'
+compose '^' 'u' to 'û'
+compose '"' 'U' to 'Ü'
+compose '"' 'u' to 'ü'
+compose '\'' 'Y' to 'Ý'
+compose '\'' 'y' to 'ý'
+compose 'T' 'H' to 'Þ'
+compose 't' 'h' to 'þ'
+compose 's' 's' to 'ß'
+compose '"' 'y' to 'ÿ'
+compose 's' 'z' to 'ß'
+compose 'i' 'j' to 'ÿ'
diff --git a/drivers/usb/maps/usb.map b/drivers/usb/maps/usb.map
new file mode 100644
index 000000000..e05c71cd8
--- /dev/null
+++ b/drivers/usb/maps/usb.map
@@ -0,0 +1,233 @@
+# USB kernel keymap.
+keymaps 0-2,4-5,8,12
+
+keycode 4 = a
+ altgr keycode 30 = Hex_A
+keycode 5 = b
+ altgr keycode 48 = Hex_B
+keycode 6 = c
+ altgr keycode 46 = Hex_C
+keycode 7 = d
+ altgr keycode 32 = Hex_D
+keycode 8 = e
+ altgr keycode 18 = Hex_E
+keycode 9 = f
+ altgr keycode 33 = Hex_F
+keycode 10 = g
+keycode 11 = h
+keycode 12 = i
+keycode 13 = j
+keycode 14 = k
+keycode 15 = l
+keycode 16 = m
+keycode 17 = n
+keycode 18 = o
+keycode 19 = p
+keycode 20 = q
+keycode 21 = r
+keycode 22 = s
+keycode 23 = t
+keycode 24 = u
+keycode 25 = v
+keycode 26 = w
+keycode 27 = x
+keycode 28 = y
+keycode 29 = z
+keycode 30 = one exclam
+ alt keycode 2 = Meta_one
+keycode 31 = two at
+ control keycode 3 = nul
+ shift control keycode 3 = nul
+ alt keycode 3 = Meta_two
+keycode 32 = three numbersign
+ control keycode 4 = Escape
+ alt keycode 4 = Meta_three
+keycode 33 = four dollar
+ control keycode 5 = Control_backslash
+ alt keycode 5 = Meta_four
+keycode 34 = five percent
+ control keycode 6 = Control_bracketright
+ alt keycode 6 = Meta_five
+keycode 35 = six asciicircum
+ control keycode 7 = Control_asciicircum
+ alt keycode 7 = Meta_six
+keycode 36 = seven ampersand
+ control keycode 8 = Control_underscore
+ alt keycode 8 = Meta_seven
+keycode 37 = eight asterisk
+ control keycode 9 = Delete
+ alt keycode 9 = Meta_eight
+keycode 38 = nine parenleft
+ alt keycode 10 = Meta_nine
+keycode 39 = zero parenright
+ alt keycode 11 = Meta_zero
+keycode 40 = Return
+ alt keycode 28 = Meta_Control_m
+keycode 41 = Escape Escape
+ alt keycode 1 = Meta_Escape
+keycode 42 = Delete Delete
+ control keycode 14 = BackSpace
+ alt keycode 14 = Meta_Delete
+keycode 43 = Tab Tab
+ alt keycode 15 = Meta_Tab
+keycode 44 = space space
+ control keycode 57 = nul
+ alt keycode 57 = Meta_space
+keycode 45 = minus underscore backslash
+ control keycode 12 = Control_underscore
+ shift control keycode 12 = Control_underscore
+ alt keycode 12 = Meta_minus
+keycode 46 = equal plus
+ alt keycode 13 = Meta_equal
+keycode 47 = bracketleft braceleft
+ control keycode 26 = Escape
+ alt keycode 26 = Meta_bracketleft
+keycode 48 = bracketright braceright asciitilde
+ control keycode 27 = Control_bracketright
+ alt keycode 27 = Meta_bracketright
+keycode 49 = backslash bar
+ control keycode 43 = Control_backslash
+ alt keycode 43 = Meta_backslash
+keycode 50 =
+keycode 51 = semicolon colon
+ alt keycode 39 = Meta_semicolon
+keycode 52 = apostrophe quotedbl
+ control keycode 40 = Control_g
+ alt keycode 40 = Meta_apostrophe
+keycode 53 = grave asciitilde
+ control keycode 41 = nul
+ alt keycode 41 = Meta_grave
+keycode 54 = comma less
+ alt keycode 51 = Meta_comma
+keycode 55 = period greater
+ control keycode 52 = Compose
+ alt keycode 52 = Meta_period
+keycode 56 = slash question
+ control keycode 53 = Delete
+ alt keycode 53 = Meta_slash
+keycode 57 = Caps_Lock
+keycode 58 = F1 F11 Console_13
+ control keycode 59 = F1
+ alt keycode 59 = Console_1
+ control alt keycode 59 = Console_1
+keycode 59 = F2 F12 Console_14
+ control keycode 60 = F2
+ alt keycode 60 = Console_2
+ control alt keycode 60 = Console_2
+keycode 60 = F3 F13 Console_15
+ control keycode 61 = F3
+ alt keycode 61 = Console_3
+ control alt keycode 61 = Console_3
+keycode 61 = F4 F14 Console_16
+ control keycode 62 = F4
+ alt keycode 62 = Console_4
+ control alt keycode 62 = Console_4
+keycode 62 = F5 F15 Console_17
+ control keycode 63 = F5
+ alt keycode 63 = Console_5
+ control alt keycode 63 = Console_5
+keycode 63 = F6 F16 Console_18
+ control keycode 64 = F6
+ alt keycode 64 = Console_6
+ control alt keycode 64 = Console_6
+keycode 64 = F7 F17 Console_19
+ control keycode 65 = F7
+ alt keycode 65 = Console_7
+ control alt keycode 65 = Console_7
+keycode 65 = F8 F18 Console_20
+ control keycode 66 = F8
+ alt keycode 66 = Console_8
+ control alt keycode 66 = Console_8
+keycode 66 = F9 F19 Console_21
+ control keycode 67 = F9
+ alt keycode 67 = Console_9
+ control alt keycode 67 = Console_9
+keycode 67 = F10 F20 Console_22
+ control keycode 68 = F10
+ alt keycode 68 = Console_10
+ control alt keycode 68 = Console_10
+keycode 68 = F11 F11 Console_23
+ control keycode 87 = F11
+ alt keycode 87 = Console_11
+ control alt keycode 87 = Console_11
+keycode 69 = F12 F12 Console_24
+ control keycode 88 = F12
+ alt keycode 88 = Console_12
+ control alt keycode 88 = Console_12
+keycode 70 = Print_Screen
+keycode 71 = Scroll_Lock Show_Memory Show_Registers
+ control keycode 70 = Show_State
+ alt keycode 70 = Scroll_Lock
+keycode 72 = Pause
+keycode 73 = Insert
+keycode 74 = Home
+keycode 75 = Prior
+ shift keycode 104 = Scroll_Backward
+keycode 76 = Remove
+# altgr control keycode 111 = Boot
+ control alt keycode 111 = Boot
+keycode 77 = End
+keycode 78 = Next
+ shift keycode 109 = Scroll_Forward
+keycode 79 = Right
+ alt keycode 106 = Incr_Console
+keycode 80 = Left
+ alt keycode 105 = Decr_Console
+keycode 81 = Down
+keycode 82 = Up
+keycode 83 = Num_Lock
+ shift keycode 69 = Bare_Num_Lock
+keycode 84 = KP_Divide
+keycode 85 = KP_Multiply
+keycode 86 = KP_Subtract
+keycode 87 = KP_Add
+keycode 88 = KP_Enter
+keycode 89 = KP_1
+ alt keycode 79 = Ascii_1
+ altgr keycode 79 = Hex_1
+keycode 90 = KP_2
+ alt keycode 80 = Ascii_2
+ altgr keycode 80 = Hex_2
+keycode 91 = KP_3
+ alt keycode 81 = Ascii_3
+ altgr keycode 81 = Hex_3
+keycode 92 = KP_4
+ alt keycode 75 = Ascii_4
+ altgr keycode 75 = Hex_4
+keycode 93 = KP_5
+ alt keycode 76 = Ascii_5
+ altgr keycode 76 = Hex_5
+keycode 94 = KP_6
+ alt keycode 77 = Ascii_6
+ altgr keycode 77 = Hex_6
+keycode 95 = KP_7
+ alt keycode 71 = Ascii_7
+ altgr keycode 71 = Hex_7
+keycode 96 = KP_8
+ alt keycode 72 = Ascii_8
+ altgr keycode 72 = Hex_8
+keycode 97 = KP_9
+ alt keycode 73 = Ascii_9
+ altgr keycode 73 = Hex_9
+keycode 98 = KP_0
+ alt keycode 82 = Ascii_0
+ altgr keycode 82 = Hex_0
+keycode 99 = KP_Period
+# altgr control keycode 83 = Boot
+ control alt keycode 83 = Boot
+keycode 100 =
+keycode 101 = Application
+keycode 102 =
+keycode 103 =
+keycode 104 = F13
+keycode 105 = F14
+
+# modifiers
+keycode 120 = Control
+keycode 121 = Shift
+keycode 122 = Alt
+keycode 123 = Window
+keycode 124 = Control_R
+keycode 125 = Shift_R
+keycode 126 = Alt_R
+keycode 127 = Window_R
diff --git a/drivers/usb/mkmap b/drivers/usb/mkmap
new file mode 100644
index 000000000..35808f227
--- /dev/null
+++ b/drivers/usb/mkmap
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+
+($ME = $0) =~ s|.*/||;
+
+$file = "maps/serial.map";
+$line = 1;
+open(PC, $file) || die("$!");
+while(<PC>)
+{
+ if(/^\s*keycode\s+(\d+)\s*=\s*(\S+)/)
+ {
+ my($idx) = int($1);
+ my($sym) = $2;
+ if(defined($map{uc($sym)}))
+ {
+ # print STDERR "$file:$line: warning: `$sym' redefined\n";
+ }
+ $map{uc($sym)} = $idx;
+ }
+ $line++;
+}
+close(PC);
+
+$file = "maps/fixup.map";
+$line = 1;
+open(FIXUP, $file) || die("$!");
+while(<FIXUP>)
+{
+ if(/^\s*keycode\s+(\d+)\s*=\s*/)
+ {
+ my($idx) = int($1);
+ for $sym (split(/\s+/, $'))
+ {
+ $map{uc($sym)} = $idx;
+ }
+ }
+ $line++;
+}
+close(FIXUP);
+
+$file = "maps/usb.map";
+$line = 1;
+open(USB, $file) || die("$!");
+while(<USB>)
+{
+ if(/^\s*keycode\s+(\d+)\s*=\s*/)
+ {
+ my($idx) = int($1);
+ for $sym (split(/\s+/, $'))
+ {
+ my($val) = $map{uc($sym)};
+ $map[$idx] = $val;
+ if(!defined($val))
+ {
+ print STDERR "$file:$line: warning: `$sym' undefined\n";
+ }
+ else
+ {
+ last;
+ }
+ }
+ }
+ $line++;
+}
+close(USB);
+
+print "unsigned char usb_kbd_map[256] = \n{\n";
+for($x = 0; $x < 32; $x++)
+{
+ if($x && !($x % 2))
+ {
+ print "\n";
+ }
+ print " ";
+ for($y = 0; $y < 8; $y++)
+ {
+ my($idx) = $x * 8 + $y;
+ print sprintf(" 0x%02x,",
+ int(defined($map[$idx]) ? $map[$idx]:0));
+ }
+ print "\n";
+}
+print "};\n";
diff --git a/drivers/usb/mouse.c b/drivers/usb/mouse.c
new file mode 100644
index 000000000..4d346c41a
--- /dev/null
+++ b/drivers/usb/mouse.c
@@ -0,0 +1,293 @@
+/*
+ * USB HID boot protocol mouse support based on MS BusMouse driver, psaux
+ * driver, and Linus's skeleton USB mouse driver. Fixed up a lot by Linus.
+ *
+ * Brad Keryan 4/3/1999
+ *
+ * version 0.20: Linus rewrote read_mouse() to do PS/2 and do it
+ * correctly. Events are added together, not queued, to keep the rodent sober.
+ *
+ * version 0.02: Hmm, the mouse seems drunk because I'm queueing the events.
+ * This is wrong: when an application (like X or gpm) reads the mouse device,
+ * it wants to find out the mouse's current position, not its recent history.
+ * The button thing turned out to be UHCI not flipping data toggle, so half the
+ * packets were thrown out.
+ *
+ * version 0.01: Switched over to busmouse protocol, and changed the minor
+ * number to 32 (same as uusbd's hidbp driver). Buttons work more sanely now,
+ * but it still doesn't generate button events unless you move the mouse.
+ *
+ * version 0.0: Driver emulates a PS/2 mouse, stealing /dev/psaux (sorry, I
+ * know that's not very nice). Moving in the X and Y axes works. Buttons don't
+ * work right yet: X sees a lot of MotionNotify/ButtonPress/ButtonRelease
+ * combos when you hold down a button and drag the mouse around. Probably has
+ * some additional bugs on an SMP machine.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define USB_MOUSE_MINOR 32
+
+struct mouse_state {
+ unsigned char buttons; /* current button state */
+ long dx; /* dx, dy, dz are change since last read */
+ long dy;
+ long dz;
+ int present; /* this mouse is plugged in */
+ int active; /* someone is has this mouse's device open */
+ int ready; /* the mouse has changed state since the last read */
+ struct wait_queue *wait; /* for polling */
+ struct fasync_struct *fasync;
+ /* later, add a list here to support multiple mice */
+ /* but we will also need a list of file pointers to identify it */
+};
+
+static struct mouse_state static_mouse_state;
+
+spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED;
+
+static int mouse_irq(int state, void *__buffer, void *dev_id)
+{
+ signed char *data = __buffer;
+ /* finding the mouse is easy when there's only one */
+ struct mouse_state *mouse = &static_mouse_state;
+
+ /* if a mouse moves with no one listening, do we care? no */
+ if(!mouse->active)
+ return 1;
+
+ /* if the USB mouse sends an interrupt, then something noteworthy
+ must have happened */
+ mouse->buttons = data[0] & 0x07;
+ mouse->dx += data[1]; /* data[] is signed, so this works */
+ mouse->dy -= data[2]; /* y-axis is reversed */
+ mouse->dz += data[3];
+ mouse->ready = 1;
+
+ add_mouse_randomness((mouse->buttons << 24) + (mouse->dz << 16 ) +
+ (mouse->dy << 8) + mouse->dx);
+
+ wake_up_interruptible(&mouse->wait);
+ if (mouse->fasync)
+ kill_fasync(mouse->fasync, SIGIO);
+
+ return 1;
+}
+
+static int fasync_mouse(int fd, struct file *filp, int on)
+{
+ int retval;
+ struct mouse_state *mouse = &static_mouse_state;
+
+ retval = fasync_helper(fd, filp, on, &mouse->fasync);
+ if (retval < 0)
+ return retval;
+ return 0;
+}
+
+static int release_mouse(struct inode * inode, struct file * file)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ fasync_mouse(-1, file, 0);
+ if (--mouse->active)
+ return 0;
+ return 0;
+}
+
+static int open_mouse(struct inode * inode, struct file * file)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ if (!mouse->present)
+ return -EINVAL;
+ if (mouse->active++)
+ return 0;
+ /* flush state */
+ mouse->buttons = mouse->dx = mouse->dy = mouse->dz = 0;
+ return 0;
+}
+
+static ssize_t write_mouse(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+/*
+ * Look like a PS/2 mouse, please..
+ *
+ * The PS/2 protocol is fairly strange, but
+ * oh, well, it's at least common..
+ */
+static ssize_t read_mouse(struct file * file, char * buffer, size_t count, loff_t *ppos)
+{
+ int retval = 0;
+ static int state = 0;
+ struct mouse_state *mouse = &static_mouse_state;
+
+ if (count) {
+ mouse->ready = 0;
+ switch (state) {
+ case 0: { /* buttons and sign */
+ int buttons = mouse->buttons;
+ mouse->buttons = 0;
+ if (mouse->dx < 0)
+ buttons |= 0x10;
+ if (mouse->dy < 0)
+ buttons |= 0x20;
+ put_user(buttons, buffer);
+ buffer++;
+ retval++;
+ state = 1;
+ if (!--count)
+ break;
+ }
+ case 1: { /* dx */
+ int dx = mouse->dx;
+ mouse->dx = 0;
+ put_user(dx, buffer);
+ buffer++;
+ retval++;
+ state = 2;
+ if (!--count)
+ break;
+ }
+ case 2: { /* dy */
+ int dy = mouse->dy;
+ mouse->dy = 0;
+ put_user(dy, buffer);
+ buffer++;
+ retval++;
+ state = 0;
+ }
+ break;
+ }
+ }
+ return retval;
+}
+
+static unsigned int mouse_poll(struct file *file, poll_table * wait)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ poll_wait(file, &mouse->wait, wait);
+ if (mouse->ready)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+struct file_operations usb_mouse_fops = {
+ NULL, /* mouse_seek */
+ read_mouse,
+ write_mouse,
+ NULL, /* mouse_readdir */
+ mouse_poll, /* mouse_poll */
+ NULL, /* mouse_ioctl */
+ NULL, /* mouse_mmap */
+ open_mouse,
+ NULL, /* flush */
+ release_mouse,
+ NULL,
+ fasync_mouse,
+};
+
+static struct miscdevice usb_mouse = {
+ USB_MOUSE_MINOR, "USB mouse", &usb_mouse_fops
+};
+
+static int mouse_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct mouse_state *mouse = &static_mouse_state;
+
+ /* We don't handle multi-config mice */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -1;
+
+ /* We don't handle multi-interface mice */
+ if (dev->config[0].bNumInterfaces != 1)
+ return -1;
+
+ /* Is it a mouse interface? */
+ interface = &dev->config[0].interface[0];
+ if (interface->bInterfaceClass != 3)
+ return -1;
+ if (interface->bInterfaceSubClass != 1)
+ return -1;
+ if (interface->bInterfaceProtocol != 2)
+ return -1;
+
+ /* Multiple endpoints? What kind of mutant ninja-mouse is this? */
+ if (interface->bNumEndpoints != 1)
+ return -1;
+
+ endpoint = &interface->endpoint[0];
+
+ /* Output endpoint? Curiousier and curiousier.. */
+ if (!(endpoint->bEndpointAddress & 0x80))
+ return -1;
+
+ /* If it's not an interrupt endpoint, we'd better punt! */
+ if ((endpoint->bmAttributes & 3) != 3)
+ return -1;
+
+ printk("USB mouse found\n");
+
+ usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+
+ usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), mouse_irq, endpoint->bInterval, NULL);
+
+ mouse->present = 1;
+ return 0;
+}
+
+static void mouse_disconnect(struct usb_device *dev)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ /* this might need work */
+ mouse->present = 0;
+}
+
+static struct usb_driver mouse_driver = {
+ "mouse",
+ mouse_probe,
+ mouse_disconnect,
+ { NULL, NULL }
+};
+
+int usb_mouse_init(void)
+{
+ struct mouse_state *mouse = &static_mouse_state;
+
+ misc_register(&usb_mouse);
+
+ mouse->present = mouse->active = 0;
+ mouse->wait = NULL;
+ mouse->fasync = NULL;
+
+ usb_register(&mouse_driver);
+ printk(KERN_INFO "USB HID boot protocol mouse registered.\n");
+ return 0;
+}
+
+void usb_mouse_cleanup(void)
+{
+ /* this, too, probably needs work */
+ usb_deregister(&mouse_driver);
+ misc_deregister(&usb_mouse);
+}
diff --git a/drivers/usb/ohci-debug.c b/drivers/usb/ohci-debug.c
new file mode 100644
index 000000000..1510bb9da
--- /dev/null
+++ b/drivers/usb/ohci-debug.c
@@ -0,0 +1,178 @@
+/*
+ * OHCI debugging code. It's gross.
+ *
+ * (C) Copyright 1999 Gregory P. Smith
+ */
+
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include "ohci.h"
+
+void show_ohci_status(struct ohci *ohci)
+{
+ struct ohci_regs regs;
+ int i;
+
+ regs.revision = readl(&ohci->regs->revision);
+ regs.control = readl(&ohci->regs->control);
+ regs.cmdstatus = readl(&ohci->regs->cmdstatus);
+ regs.intrstatus = readl(&ohci->regs->intrstatus);
+ regs.intrenable = readl(&ohci->regs->intrenable);
+ regs.hcca = readl(&ohci->regs->hcca);
+ regs.ed_periodcurrent = readl(&ohci->regs->ed_periodcurrent);
+ regs.ed_controlhead = readl(&ohci->regs->ed_controlhead);
+ regs.ed_controlcurrent = readl(&ohci->regs->ed_controlcurrent);
+ regs.ed_bulkhead = readl(&ohci->regs->ed_bulkhead);
+ regs.ed_bulkcurrent = readl(&ohci->regs->ed_bulkcurrent);
+ regs.current_donehead = readl(&ohci->regs->current_donehead);
+ regs.fminterval = readl(&ohci->regs->fminterval);
+ regs.fmremaining = readl(&ohci->regs->fmremaining);
+ regs.fmnumber = readl(&ohci->regs->fmnumber);
+ regs.periodicstart = readl(&ohci->regs->periodicstart);
+ regs.lsthresh = readl(&ohci->regs->lsthresh);
+ regs.roothub.a = readl(&ohci->regs->roothub.a);
+ regs.roothub.b = readl(&ohci->regs->roothub.b);
+ regs.roothub.status = readl(&ohci->regs->roothub.status);
+ for (i=0; i<MAX_ROOT_PORTS; ++i)
+ regs.roothub.portstatus[i] = readl(&ohci->regs->roothub.portstatus[i]);
+
+ printk(KERN_DEBUG " ohci revision = %x\n", regs.revision);
+ printk(KERN_DEBUG " ohci control = %x\n", regs.control);
+ printk(KERN_DEBUG " ohci cmdstatus = %x\n", regs.cmdstatus);
+ printk(KERN_DEBUG " ohci intrstatus = %x\n", regs.intrstatus);
+ printk(KERN_DEBUG " ohci intrenable = %x\n", regs.intrenable);
+
+ printk(KERN_DEBUG " ohci hcca = %x\n", regs.hcca);
+ printk(KERN_DEBUG " ohci ed_pdcur = %x\n", regs.ed_periodcurrent);
+ printk(KERN_DEBUG " ohci ed_ctrlhead = %x\n", regs.ed_controlhead);
+ printk(KERN_DEBUG " ohci ed_ctrlcur = %x\n", regs.ed_controlcurrent);
+ printk(KERN_DEBUG " ohci ed_bulkhead = %x\n", regs.ed_bulkhead);
+ printk(KERN_DEBUG " ohci ed_bulkcur = %x\n", regs.ed_bulkcurrent);
+ printk(KERN_DEBUG " ohci curdonehead = %x\n", regs.current_donehead);
+
+ printk(KERN_DEBUG " ohci fminterval = %x\n", regs.fminterval);
+ printk(KERN_DEBUG " ohci fmremaining = %x\n", regs.fmremaining);
+ printk(KERN_DEBUG " ohci fmnumber = %x\n", regs.fmnumber);
+ printk(KERN_DEBUG " ohci pdstart = %x\n", regs.periodicstart);
+ printk(KERN_DEBUG " ohci lsthresh = %x\n", regs.lsthresh);
+
+ printk(KERN_DEBUG " ohci roothub.a = %x\n", regs.roothub.a);
+ printk(KERN_DEBUG " ohci roothub.b = %x\n", regs.roothub.b);
+ printk(KERN_DEBUG " ohci root status = %x\n", regs.roothub.status);
+ printk(KERN_DEBUG " roothub.port0 = %x\n", regs.roothub.portstatus[0]);
+ printk(KERN_DEBUG " roothub.port1 = %x\n", regs.roothub.portstatus[1]);
+} /* show_ohci_status() */
+
+
+void show_ohci_ed(struct ohci_ed *ed)
+{
+ int stat = ed->status;
+ int skip = (stat & OHCI_ED_SKIP);
+ int mps = (stat & OHCI_ED_MPS) >> 16;
+ int isoc = (stat & OHCI_ED_F_ISOC);
+ int low_speed = (stat & OHCI_ED_S_LOW);
+ int dir = (stat & OHCI_ED_D);
+ int endnum = (stat & OHCI_ED_EN) >> 7;
+ int funcaddr = (stat & OHCI_ED_FA);
+ int halted = (ed->_head_td & 1);
+ int toggle = (ed->_head_td & 2) >> 1;
+
+ printk(KERN_DEBUG " ohci ED:\n");
+ printk(KERN_DEBUG " status = 0x%x\n", stat);
+ printk(KERN_DEBUG " %sMPS %d%s%s%s%s tc%d e%d fa%d\n",
+ skip ? "Skip " : "",
+ mps,
+ isoc ? "Isoc. " : "",
+ low_speed ? " LowSpd" : "",
+ (dir == OHCI_ED_D_IN) ? " Input" :
+ (dir == OHCI_ED_D_OUT) ? " Output" : "",
+ halted ? " Halted" : "",
+ toggle,
+ endnum,
+ funcaddr);
+ printk(KERN_DEBUG " tail_td = 0x%x\n", ed->tail_td);
+ printk(KERN_DEBUG " head_td = 0x%x\n", ed_head_td(ed));
+ printk(KERN_DEBUG " next_ed = 0x%x\n", ed->next_ed);
+} /* show_ohci_ed() */
+
+
+void show_ohci_td(struct ohci_td *td)
+{
+ int td_round = td->info & OHCI_TD_ROUND;
+ int td_dir = td->info & OHCI_TD_D;
+ int td_int_delay = (td->info & OHCI_TD_IOC_DELAY) >> 21;
+ int td_toggle = (td->info & OHCI_TD_DT) >> 24;
+ int td_errcnt = td_errorcount(*td);
+ int td_cc = OHCI_TD_CC_GET(td->info);
+
+ printk(KERN_DEBUG " ohci TD hardware fields:\n");
+ printk(KERN_DEBUG " info = 0x%x\n", td->info);
+ printk(KERN_DEBUG " %s%s%s%d %s\n",
+ td_round ? "Rounding " : "",
+ (td_dir == OHCI_TD_D_IN) ? "Input " :
+ (td_dir == OHCI_TD_D_OUT) ? "Output " :
+ (td_dir == OHCI_TD_D_SETUP) ? "Setup " : "",
+ "IntDelay ", td_int_delay,
+ (td_toggle < 2) ? " " :
+ (td_toggle & 1) ? "Data1 " : "Data0 ");
+ printk(KERN_DEBUG " %s%d %s0x%x, %sAccessed, %sActive\n",
+ "ErrorCnt ", td_errcnt,
+ "ComplCode ", td_cc,
+ td_cc_accessed(*td) ? "" : "Not ",
+ td_active(*td) ? "" : "Not ");
+
+ printk(KERN_DEBUG " cur_buf = 0x%x\n", td->cur_buf);
+ printk(KERN_DEBUG " next_td = 0x%x\n", td->next_td);
+ printk(KERN_DEBUG " buf_end = 0x%x\n", td->buf_end);
+ printk(KERN_DEBUG " ohci TD driver fields:\n");
+ printk(KERN_DEBUG " data = %p\n", td->data);
+ printk(KERN_DEBUG " dev_id = %p\n", td->dev_id);
+ printk(KERN_DEBUG " ed = %p\n", td->ed);
+ if (td->data != NULL) {
+ unsigned char *d = td->data;
+ printk(KERN_DEBUG " DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7] );
+ }
+} /* show_ohci_td() */
+
+
+void show_ohci_device(struct ohci_device *dev)
+{
+ int idx;
+ printk(KERN_DEBUG " ohci_device usb = %p\n", dev->usb);
+ printk(KERN_DEBUG " ohci_device ohci = %p\n", dev->ohci);
+ printk(KERN_DEBUG " ohci_device ohci_hcca = %p\n", dev->hcca);
+ for (idx=0; idx<3 /*NUM_EDS*/; ++idx) {
+ printk(KERN_DEBUG " [ed num %d] ", idx);
+ show_ohci_ed(&dev->ed[idx]);
+ }
+ for (idx=0; idx<3 /*NUM_TDS*/; ++idx) {
+ printk(KERN_DEBUG " [td num %d] ", idx);
+ show_ohci_td(&dev->td[idx]);
+ }
+ printk(KERN_DEBUG " ohci_device data\n ");
+ for (idx=0; idx<4; ++idx) {
+ printk(KERN_DEBUG " %08lx", dev->data[idx]);
+ }
+ printk(KERN_DEBUG "\n");
+} /* show_ohci_device() */
+
+
+void show_ohci_hcca(struct ohci_hcca *hcca)
+{
+ int idx;
+
+ printk(KERN_DEBUG " ohci_hcca\n");
+
+ for (idx=0; idx<NUM_INTS; idx++) {
+ printk(KERN_DEBUG " int_table[%2d] == %p\n", idx, hcca->int_table +idx);
+ }
+
+ printk(KERN_DEBUG " frame_no == %d\n", hcca->frame_no);
+ printk(KERN_DEBUG " donehead == 0x%08x\n", hcca->donehead);
+} /* show_ohci_hcca() */
+
+
+/* vim:sw=8
+ */
diff --git a/drivers/usb/ohci-hcd.c b/drivers/usb/ohci-hcd.c
new file mode 100644
index 000000000..da3ef6657
--- /dev/null
+++ b/drivers/usb/ohci-hcd.c
@@ -0,0 +1,1489 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ *
+ * The OHCI HCD layer is a simple but nearly complete implementation of what the
+ * USB people would call a HCD for the OHCI.
+ * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled)
+ * The layer on top of it, is for interfacing to the alternate-usb device-drivers.
+ *
+ * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ]
+ * [ Open Host Controller Interface driver for USB. ]
+ * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ]
+ * [ (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> ]
+ * [ $Log: ohci.c,v $ ]
+ * [ Revision 1.1 1999/04/05 08:32:30 greg ]
+ *
+ *
+ * v2.1 1999/05/09 ep_addr correction, code clean up
+ * v2.0 1999/05/04
+ * virtual root hub is now an option,
+ * memory allocation based on kmalloc and kfree now, Bus error handling,
+ * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion
+ *
+ * from Linus Torvalds (uhci.c) (APM not tested; hub, usb_device, bus and related stuff)
+ * from Greg Smith (ohci.c) (reset controller handling, hub)
+ *
+ * v1.0 1999/04/27 initial release
+ * ohci-hcd.c
+ */
+
+/* #define OHCI_DBG */ /* printk some debug information */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+
+#include <asm/spinlock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "usb.h"
+#include "ohci-hcd.h"
+#include "inits.h"
+
+
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int handle_apm_event(apm_event_t event);
+static int apm_resume = 0;
+#endif
+
+
+
+static struct wait_queue *control_wakeup;
+static struct wait_queue *root_hub = NULL;
+
+static __u8 cc_to_status[16] = { /* mapping of the OHCI CC to the UHCI status codes; first guess */
+/* Activ, Stalled, Data Buffer Err, Babble Detected : NAK recvd, CRC/Timeout, Bitstuff, reservd */
+/* No Error */ 0x00,
+/* CRC Error */ 0x04,
+/* Bit Stuff */ 0x02,
+/* Data Togg */ 0x40,
+/* Stall */ 0x40,
+/* DevNotResp */ 0x04,
+/* PIDCheck */ 0x04,
+/* UnExpPID */ 0x40,
+/* DataOver */ 0x20,
+/* DataUnder */ 0x20,
+/* reservd */ 0x40,
+/* reservd */ 0x40,
+/* BufferOver */ 0x20,
+/* BuffUnder */ 0x20,
+/* Not Access */ 0x80,
+/* Not Access */ 0x80
+ };
+
+
+/********
+ **** Interface functions
+ ***********************************************/
+
+static int sohci_int_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1)
+{
+
+ struct ohci * ohci = ohci_in;
+ usb_device_irq handler=(void *) lw0;
+ void *dev_id = (void *) lw1;
+ int ret;
+
+ OHCI_DEBUG({ int i; printk("USB HC IRQ <<<: %x: data(%d):", ep_addr, data_len);)
+ OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
+ OHCI_DEBUG( printk(" ret_status: %x\n", status); })
+
+ ret = handler(cc_to_status[status & 0xf], data, dev_id);
+ if(ret == 0) return 0; /* 0 .. do not requeue */
+ if(status > 0) return -1; /* error occured do not requeue ? */
+ ohci_trans_req(ohci, ep_addr, 0, NULL, data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); /* requeue int request */
+ return 0;
+}
+
+static int sohci_ctrl_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw)
+{
+ *(int * )lw0 = status;
+ wake_up(&control_wakeup);
+
+ OHCI_DEBUG( { int i; printk("USB HC CTRL<<<: %x: ctrl(%d):", ep_addr, ctrl_len);)
+ OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) ctrl)[i]);)
+ OHCI_DEBUG( printk(" data(%d):", data_len);)
+ OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
+ OHCI_DEBUG( printk(" ret_status: %x\n", status); })
+ return 0;
+}
+
+static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+{
+ struct ohci * ohci = usb_dev->bus->hcpriv;
+ union ep_addr_ ep_addr;
+
+ ep_addr.iep = 0;
+ ep_addr.bep.ep = ((pipe >> 15) & 0x0f) /* endpoint address */
+ | (pipe & 0x80) /* direction */
+ | (1 << 5); /* type = int*/
+ ep_addr.bep.fa = ((pipe >> 8) & 0x7f); /* device address */
+
+ OHCI_DEBUG( printk("USB HC IRQ >>>: %x: every %d ms\n", ep_addr.iep, period);)
+
+ usb_ohci_add_ep(ohci, ep_addr.iep, period, 1, sohci_int_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01);
+
+ ohci_trans_req(ohci, ep_addr.iep, 0, NULL, ((struct ohci_device *) usb_dev->hcpriv)->data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id);
+ return 0;
+}
+
+
+static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len)
+{
+ struct wait_queue wait = { current, NULL };
+ struct ohci * ohci = usb_dev->bus->hcpriv;
+ int status;
+ union ep_addr_ ep_addr;
+
+ ep_addr.iep = 0;
+ ep_addr.bep.ep = ((pipe >> 15) & 0x0f) /* endpoint address */
+ | (pipe & 0x80) /* direction */
+ | (1 << 6); /* type = ctrl*/
+ ep_addr.bep.fa = ((pipe >> 8) & 0x7f); /* device address */
+
+ status = 0xf; /* CC not Accessed */
+ OHCI_DEBUG( { int i; printk("USB HC CTRL>>>: %x: ctrl(%d):", ep_addr.iep, 8);)
+ OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) cmd)[i]);)
+ OHCI_DEBUG( printk(" data(%d):", len);)
+ OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
+ OHCI_DEBUG( printk("\n"); })
+
+ usb_ohci_add_ep(ohci, ep_addr.iep, 0, 1, sohci_ctrl_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01);
+
+ current->state = TASK_UNINTERRUPTIBLE;
+ add_wait_queue(&control_wakeup, &wait);
+
+ ohci_trans_req(ohci, ep_addr.iep, 8, cmd, data, len, (__OHCI_BAG) &status, 0);
+
+ schedule_timeout(HZ/10);
+
+ remove_wait_queue(&control_wakeup, &wait);
+
+ OHCI_DEBUG(printk("USB HC status::: %x\n", cc_to_status[status & 0x0f]);)
+
+ return cc_to_status[status & 0x0f];
+}
+
+
+static int sohci_usb_deallocate(struct usb_device *usb_dev) {
+ struct ohci_device *dev = usb_to_ohci(usb_dev);
+ union ep_addr_ ep_addr;
+
+ ep_addr.iep = 0;
+
+ OHCI_DEBUG(printk("USB HC dealloc %x\n", usb_dev->devnum);)
+
+ /* wait_ms(20); */
+
+ if(usb_dev->devnum >=0) {
+ ep_addr.bep.fa = usb_dev->devnum;
+ usb_ohci_rm_function(((struct ohci_device *)usb_dev->hcpriv)->ohci, ep_addr.iep);
+ }
+
+ USB_FREE(dev);
+ USB_FREE(usb_dev);
+
+ return 0;
+}
+
+static struct usb_device *sohci_usb_allocate(struct usb_device *parent) {
+
+ struct usb_device *usb_dev;
+ struct ohci_device *dev;
+
+
+ USB_ALLOC(usb_dev, sizeof(*usb_dev));
+ if (!usb_dev)
+ return NULL;
+
+ memset(usb_dev, 0, sizeof(*usb_dev));
+
+ USB_ALLOC(dev, sizeof(*dev));
+ if (!dev) {
+ USB_FREE(usb_dev);
+ return NULL;
+ }
+
+ /* Initialize "dev" */
+ memset(dev, 0, sizeof(*dev));
+
+ usb_dev->hcpriv = dev;
+ dev->usb = usb_dev;
+
+ usb_dev->parent = parent;
+
+ if (parent) {
+ usb_dev->bus = parent->bus;
+ dev->ohci = usb_to_ohci(parent)->ohci;
+ }
+ return usb_dev;
+}
+
+struct usb_operations sohci_device_operations = {
+ sohci_usb_allocate,
+ sohci_usb_deallocate,
+ sohci_control_msg,
+ sohci_request_irq,
+};
+
+
+/******
+ *** ED handling functions
+ ************************************/
+
+
+
+/*
+ * search for the right place to insert an interrupt ed into the int tree
+ * do some load ballancing
+ * */
+
+static int usb_ohci_int_ballance(struct ohci * ohci, int interval, int load) {
+
+ int i,j;
+
+ j = 0; /* search for the least loaded interrupt endpoint branch of all 32 branches */
+ for(i=0; i< 32; i++) if(ohci->ohci_int_load[j] > ohci->ohci_int_load[i]) j=i;
+
+ if(interval < 1) interval = 1;
+ if(interval > 32) interval = 32;
+ for(i= 0; ((interval >> i) > 1 ); interval &= (0xfffe << i++ )); /* interval = 2^int(ld(interval)) */
+
+
+ for(i=j%interval; i< 32; i+=interval) ohci->ohci_int_load[i] += load;
+ j = interval + (j % interval);
+
+ OHCI_DEBUG(printk("USB HC new int ed on pos : %x \n",j);)
+
+ return j;
+}
+
+/* get the ed from the endpoint / device adress */
+
+struct usb_ohci_ed * ohci_find_ep(struct ohci *ohci, unsigned int ep_addr_in) {
+
+union ep_addr_ ep_addr;
+struct usb_ohci_ed *tmp;
+unsigned int mask;
+
+mask = 0;
+ep_addr.iep = ep_addr_in;
+
+#ifdef VROOTHUB
+ if(ep_addr.bep.fa == ohci->root_hub_funct_addr) {
+ if((ep_addr.bep.ep & 0x0f) == 0)
+ return &ohci->ed_rh_ep0; /* root hub ep0 */
+ else
+ return &ohci->ed_rh_epi; /* root hub int ep */
+ }
+#endif
+
+ tmp = ohci->ed_func_ep0[ep_addr.bep.fa];
+ mask = ((((ep_addr.bep.ep >> 5) & 0x03)==2)?0x7f:0xff);
+ ep_addr.bep.ep &= mask; /* mask out direction of ctrl ep */
+
+ while (tmp != NULL) {
+ if (tmp->ep_addr.iep == ep_addr.iep)
+ return tmp;
+ tmp = tmp->ed_list;
+ }
+ return NULL;
+}
+
+spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED;
+/* add a new endpoint ep_addr */
+struct usb_ohci_ed *usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr_in, int interval, int load, f_handler handler, int ep_size, int speed) {
+
+ struct usb_ohci_ed * ed;
+ struct usb_ohci_td * td;
+ union ep_addr_ ep_addr;
+
+
+ int int_junk;
+
+ struct usb_ohci_ed *tmp;
+
+ ep_addr.iep = ep_addr_in ;
+ ep_addr.bep.ep &= ((((ep_addr.bep.ep >> 5) & 0x03)==2)?0x7f:0xff); /* mask out direction of ctrl ep */
+
+ spin_lock(&usb_ed_lock);
+
+ tmp = ohci_find_ep(ohci, ep_addr.iep);
+ if (tmp != NULL) {
+
+#ifdef VROOTHUB
+ if(ep_addr.bep.fa == ohci->root_hub_funct_addr) {
+ if((ep_addr.bep.ep & 0x0f) != 0) { /* root hub int ep */
+ ohci->ed_rh_epi.handler = handler;
+ ohci_init_rh_int_timer(ohci, interval);
+ }
+ else { /* root hub ep0 */
+ ohci->ed_rh_ep0.handler = handler;
+ }
+ }
+
+ else
+#endif
+
+ {
+ tmp->hw.info = ep_addr.bep.fa | ((ep_addr.bep.ep & 0xf) <<7)
+
+ | (((ep_addr.bep.ep & 0x60) == 0)? 0x8000 : 0)
+ | (speed << 13)
+ | ep_size <<16;
+
+ tmp->handler = handler;
+ }
+ spin_unlock(&usb_ed_lock);
+ return tmp; /* ed already in use */
+ }
+
+
+ OHCI_ALLOC(td, sizeof(td)); /* dummy td; end of td list for ed */
+ OHCI_ALLOC(ed, sizeof(ed));
+ td->prev_td = NULL;
+
+ ed->hw.tail_td = virt_to_bus(&td->hw);
+ ed->hw.head_td = ed->hw.tail_td;
+ ed->hw.info = ep_addr.bep.fa | ((ep_addr.bep.ep & 0xf) <<7)
+ /* | ((ep_addr.bep.port & 0x80)? 0x1000 : 0x0800 ) */
+ | (((ep_addr.bep.ep & 0x60) == 0)? 0x8000 : 0)
+ | (speed << 13)
+ | ep_size <<16;
+
+ ed->handler = handler;
+
+ switch((ep_addr.bep.ep >> 5) & 0x03) {
+ case CTRL:
+ ed->hw.next_ed = 0;
+ if(ohci->ed_controltail == NULL) {
+ writel(virt_to_bus(&ed->hw), &ohci->regs->ed_controlhead);
+ }
+ else {
+ ohci->ed_controltail->hw.next_ed = virt_to_bus(&ed->hw);
+ }
+ ed->ed_prev = ohci->ed_controltail;
+ ohci->ed_controltail = ed;
+ break;
+ case BULK:
+ ed->hw.next_ed = 0;
+ if(ohci->ed_bulktail == NULL) {
+ writel(virt_to_bus(&ed->hw), &ohci->regs->ed_bulkhead);
+ }
+ else {
+ ohci->ed_bulktail->hw.next_ed = virt_to_bus(&ed->hw);
+ }
+ ed->ed_prev = ohci->ed_bulktail;
+ ohci->ed_bulktail = ed;
+ break;
+ case INT:
+ int_junk = usb_ohci_int_ballance(ohci, interval, load);
+ ed->hw.next_ed = ohci->hc_area->ed[int_junk].next_ed;
+ ohci->hc_area->ed[int_junk].next_ed = virt_to_bus(&ed->hw);
+ ed->ed_prev = (struct usb_ohci_ed *) &ohci->hc_area->ed[int_junk];
+ break;
+ case ISO:
+ ed->hw.next_ed = 0;
+ ohci->ed_isotail->hw.next_ed = virt_to_bus(&ed->hw);
+ ed->ed_prev = ohci->ed_isotail;
+ ohci->ed_isotail = ed;
+ break;
+ }
+ ed->ep_addr = ep_addr;
+
+ /* Add it to the "hash"-table of known endpoint descriptors */
+
+ ed->ed_list = ohci->ed_func_ep0[ed->ep_addr.bep.fa];
+ ohci->ed_func_ep0[ed->ep_addr.bep.fa] = ed;
+
+ spin_unlock(&usb_ed_lock);
+
+ OHCI_DEBUG(printk("USB HC new ed %x: %x :", ep_addr.iep, (unsigned int ) ed); )
+ OHCI_DEBUG({ int i; for( i= 0; i<8 ;i++) printk(" %4x", ((unsigned int *) ed)[i]) ; printk("\n"); }; )
+ return 0;
+}
+
+/*****
+ * Request the removal of an endpoint
+ *
+ * put the ep on the rm_list and request a stop of the bulk or ctrl list
+ * real removal is done at the next start of frame hardware interrupt
+ */
+int usb_ohci_rm_ep(struct ohci * ohci, struct usb_ohci_ed *ed)
+{
+ unsigned int flags;
+ struct usb_ohci_ed *tmp;
+
+ OHCI_DEBUG(printk("USB HC remove ed %x: %x :\n", ed->ep_addr.iep, (unsigned int ) ed); )
+
+ spin_lock_irqsave(&usb_ed_lock, flags);
+
+ tmp = ohci->ed_func_ep0[ed->ep_addr.bep.fa];
+ if (tmp == NULL) {
+ spin_unlock_irqrestore(&usb_ed_lock, flags);
+ return 0;
+ }
+
+ if(tmp == ed) {
+ ohci->ed_func_ep0[ed->ep_addr.bep.fa] = ed->ed_list;
+ }
+ else {
+ while (tmp->ed_list != ed) {
+ if (tmp->ed_list == NULL) {
+ spin_unlock_irqrestore(&usb_ed_lock, flags);
+ return 0;
+ }
+ tmp = tmp->ed_list;
+ }
+ tmp->ed_list = ed->ed_list;
+ }
+ ed->ed_list = ohci->ed_rm_list;
+ ohci->ed_rm_list = ed;
+ ed->hw.info |= OHCI_ED_SKIP;
+
+ switch((ed->ep_addr.bep.ep >> 5) & 0x03) {
+ case CTRL:
+ writel_mask(~(0x01<<4), &ohci->regs->control); /* stop CTRL list */
+ break;
+ case BULK:
+ writel_mask(~(0x01<<5), &ohci->regs->control); /* stop BULK list */
+ break;
+ }
+
+
+ writel( OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */
+
+ spin_unlock_irqrestore(&usb_ed_lock, flags);
+
+ return 1;
+}
+
+/* we have requested to stop the bulk or CTRL list,
+ * now we can remove the eds on the rm_list */
+
+static int ohci_rm_eds(struct ohci * ohci) {
+
+ unsigned int flags;
+ struct usb_ohci_ed *ed;
+ struct usb_ohci_ed *ed_tmp;
+ struct usb_ohci_td *td;
+ __u32 td_hw_tmp;
+ __u32 td_hw;
+
+ spin_lock_irqsave(&usb_ed_lock, flags);
+
+ ed = ohci->ed_rm_list;
+
+ while (ed != NULL) {
+
+ switch((ed->ep_addr.bep.ep >> 5) & 0x03) {
+ case CTRL:
+ if(ed->ed_prev == NULL) {
+ writel(ed->hw.next_ed, &ohci->regs->ed_controlhead);
+ }
+ else {
+ ed->ed_prev->hw.next_ed = ed->hw.next_ed;
+ }
+ if(ohci->ed_controltail == ed) {
+ ohci->ed_controltail = ed->ed_prev;
+ }
+ break;
+ case BULK:
+ if(ed->ed_prev == NULL) {
+ writel(ed->hw.next_ed, &ohci->regs->ed_bulkhead);
+ }
+ else {
+ ed->ed_prev->hw.next_ed = ed->hw.next_ed;
+ }
+ if(ohci->ed_bulktail == ed) {
+ ohci->ed_bulktail = ed->ed_prev;
+ }
+ break;
+ case INT:
+ ed->ed_prev->hw.next_ed = ed->hw.next_ed;
+ break;
+ case ISO:
+ ed->ed_prev->hw.next_ed = ed->hw.next_ed;
+ if(ohci->ed_isotail == ed) {
+ ohci->ed_isotail = ed->ed_prev;
+ }
+ break;
+ }
+
+ if(ed->hw.next_ed != 0) ((struct usb_ohci_ed *) bus_to_virt(ed->hw.next_ed))->ed_prev = ed->ed_prev;
+
+
+/* tds directly connected to ed */
+
+ td_hw = ed->hw.head_td & 0xfffffff0;
+ while(td_hw != 0) {
+ td = bus_to_virt(td_hw);
+ td_hw_tmp = td_hw;
+ td_hw = td->hw.next_td;
+ OHCI_FREE(td); /* free pending tds */
+ if(td_hw_tmp == ed->hw.tail_td) break;
+
+ }
+
+ /* mark TDs on the hc done list (if there are any) */
+ td_hw = readl(&ohci->regs->donehead) & 0xfffffff0;
+ while(td_hw != 0) {
+ td = bus_to_virt(td_hw);
+ td_hw = td->hw.next_td;
+ if(td->ep == ed) td->ep = 0;
+ }
+
+ /* mark TDs on the hcca done list (if there are any) */
+ td_hw = ohci->hc_area->hcca.done_head & 0xfffffff0 ;
+
+ while(td_hw != 0) {
+ td = bus_to_virt(td_hw);
+ td_hw = td->hw.next_td;
+ if(td->ep == ed) td->ep = 0;
+ }
+
+ ed_tmp = ed;
+ ed = ed->ed_list;
+ OHCI_FREE(ed_tmp); /* free ed */
+ }
+ writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
+ writel(0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */
+ writel_set((0x01<<4), &ohci->regs->control); /* start CTRL u. (BULK list) */
+
+ spin_unlock_irqrestore(&usb_ed_lock, flags);
+
+ ohci->ed_rm_list = NULL;
+ OHCI_DEBUG(printk("USB HC after rm ed control: %4x intrstat: %4x intrenable: %4x\n", readl(&ohci->regs->control),readl(&ohci->regs->intrstatus),readl(&ohci->regs->intrenable));)
+
+
+ return 0;
+}
+
+/* remove all endpoints of a function (device) */
+int usb_ohci_rm_function( struct ohci * ohci, unsigned int ep_addr_in)
+{
+ struct usb_ohci_ed *ed;
+ struct usb_ohci_ed *tmp;
+ union ep_addr_ ep_addr;
+
+
+
+ ep_addr.iep = ep_addr_in;
+
+ for(ed = ohci->ed_func_ep0[ep_addr.bep.fa]; ed != NULL;) {
+ tmp = ed;
+ ed = ed->ed_list;
+ usb_ohci_rm_ep(ohci, tmp);
+ }
+
+
+
+ return 1;
+}
+
+
+
+
+
+/******
+ *** TD handling functions
+ ************************************/
+
+
+#define FILL_TD(TD_PT, HANDLER, INFO, DATA, LEN, LW0, LW1) \
+ td_pt = (TD_PT); \
+ td_pt1 = (struct usb_ohci_td *) bus_to_virt(usb_ep->hw.tail_td); \
+ td_pt1->ep = usb_ep; \
+ td_pt1->handler = (HANDLER); \
+ td_pt1->buffer_start = (DATA); \
+ td_pt1->lw0 = (LW0); \
+ td_pt1->lw1 = (LW1); \
+ td_pt1->hw.info = (INFO); \
+ td_pt1->hw.cur_buf = virt_to_bus(DATA); \
+ td_pt1->hw.buf_end = td_pt1->hw.cur_buf + (LEN) - 1; \
+ td_pt1->hw.next_td = virt_to_bus(td_pt); \
+ usb_ep->hw.tail_td = virt_to_bus(td_pt); \
+ td_pt->prev_td = td_pt1; \
+ td_pt->hw.next_td = 0
+
+spinlock_t usb_req_lock = SPIN_LOCK_UNLOCKED;
+
+int ohci_trans_req(struct ohci * ohci, unsigned int ep_addr, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) {
+
+ int ed_type;
+ unsigned int flags;
+ struct usb_ohci_td *td_pt;
+ struct usb_ohci_td *td_pt1;
+ struct usb_ohci_td *td_pt_a1, *td_pt_a2, *td_pt_a3;
+ struct usb_ohci_ed *usb_ep;
+ f_handler handler;
+
+
+ td_pt_a1 =NULL;
+ td_pt_a2 =NULL;
+ td_pt_a3 =NULL;
+
+ usb_ep = ohci_find_ep(ohci, ep_addr);
+ if(usb_ep == NULL ) return -1; /* not known ep */
+
+ handler = usb_ep->handler;
+
+#ifdef VROOTHUB
+ if(usb_ep == &ohci->ed_rh_ep0) { /* root hub ep 0 control endpoint */
+ root_hub_control_msg(ohci, 8, ctrl, data, data_len, lw0, lw1, handler);
+ return 0;
+ }
+
+ if(usb_ep == &ohci->ed_rh_epi) { /* root hub interrupt endpoint */
+
+ root_hub_int_req(ohci, 8, ctrl, data, data_len, lw0, lw1, handler);
+ return 0;
+ }
+#endif
+ /* struct usb_ohci_ed * usb_ep = usb_ohci_add_ep(pipe, ohci, interval, 1); */
+
+ ed_type = ((((union ep_addr_)ep_addr).bep.ep >> 5) & 0x07);
+
+ switch(ed_type) {
+ case BULK_IN:
+ case BULK_OUT:
+ case INT_IN:
+ case INT_OUT:
+ OHCI_ALLOC(td_pt_a1, sizeof(td_pt_a1));
+ break;
+
+ case CTRL_IN:
+ case CTRL_OUT:
+ OHCI_ALLOC(td_pt_a1, sizeof(td_pt_a1));
+ OHCI_ALLOC(td_pt_a3, sizeof(td_pt_a3));
+ if(data_len > 0) {
+ OHCI_ALLOC(td_pt_a2, sizeof(td_pt_a2));
+ }
+ break;
+
+ case ISO_IN:
+ case ISO_OUT:
+
+ }
+
+ spin_lock_irqsave(&usb_req_lock, flags);
+
+ switch(ed_type) {
+ case BULK_IN:
+ FILL_TD( td_pt_a1, handler, TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1 );
+ writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+ break;
+
+ case BULK_OUT:
+ FILL_TD( td_pt_a1, handler, TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1 );
+ writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+ break;
+
+ case INT_IN:
+ FILL_TD( td_pt_a1, handler, TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1 );
+ break;
+
+ case INT_OUT:
+ FILL_TD( td_pt_a1, handler, TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1 );
+ break;
+
+ case CTRL_IN:
+ FILL_TD( td_pt_a1, NULL, TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0 );
+ if(data_len > 0) {
+ FILL_TD( td_pt_a2, NULL, TD_CC | TD_R | TD_DP_IN | TD_T_DATA1, data, data_len, 0, 0 );
+ }
+ FILL_TD( td_pt_a3, handler, TD_CC | TD_DP_OUT | TD_T_DATA1, NULL, 0, lw0, lw1 );
+ writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+ break;
+
+ case CTRL_OUT:
+ FILL_TD( td_pt_a1, NULL, TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0 );
+ if(data_len > 0) {
+ FILL_TD( td_pt_a2, NULL, TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1, data, data_len, 0, 0 );
+ }
+ FILL_TD( td_pt_a3, handler, TD_CC | TD_DP_IN | TD_T_DATA1, NULL, 0, lw0, lw1 );
+ writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+ break;
+
+ case ISO_IN:
+ case ISO_OUT:
+ break;
+ }
+
+
+
+
+ td_pt1 = (struct usb_ohci_td *) bus_to_virt(usb_ep->hw.tail_td);
+
+
+ if(td_pt_a3 != NULL) td_pt_a3->prev_td = NULL;
+ else if (td_pt_a2 != NULL) td_pt_a2->prev_td = NULL;
+ else if (td_pt_a1 != NULL) td_pt_a1->prev_td = NULL;
+
+ spin_unlock_irqrestore(&usb_req_lock, flags);
+ return 0;
+}
+
+
+/******
+ *** Done List handling functions
+ ************************************/
+
+/* replies to the request have to be on a FIFO basis so
+ * we reverse the reversed done-list */
+
+static struct usb_ohci_td * ohci_reverse_done_list(struct ohci * ohci) {
+
+ __u32 td_list_hc;
+ struct usb_ohci_td * td_list = NULL;
+ struct usb_ohci_td * td_rev = NULL;
+
+ td_list_hc = ohci->hc_area->hcca.done_head & 0xfffffff0;
+ ohci->hc_area->hcca.done_head = 0;
+
+ while(td_list_hc) {
+
+ td_list = (struct usb_ohci_td *) bus_to_virt(td_list_hc);
+ td_list->next_dl_td = td_rev;
+
+ td_rev = td_list;
+ td_list_hc = td_list->hw.next_td & 0xfffffff0;
+ }
+ return td_list;
+}
+
+/* all done requests are replied here */
+static int usb_ohci_done_list(struct ohci * ohci) {
+
+ struct usb_ohci_td * td = NULL;
+ struct usb_ohci_td * td_list;
+ struct usb_ohci_td * td_list_next = NULL;
+ struct usb_ohci_td * td_err = NULL;
+ __u32 td_hw;
+
+
+ td_list = ohci_reverse_done_list(ohci);
+
+ while(td_list) {
+ td_list_next = td_list->next_dl_td;
+ td = td_list;
+
+ if(td->ep == NULL) { /* removed ep */
+ OHCI_FREE(td_list);
+ break;
+ }
+
+ /* the HC halts an ED if an error occurs; put all pendings TDs of an halted ED on the
+ * done list; they are marked with an 0xf CC_error code
+ */
+
+ if(TD_CC_GET(td_list->hw.info) != TD_CC_NOERROR) { /* on error move all pending tds of an ed into the done list */
+ printk("******* USB BUS error %x @ep %x\n", TD_CC_GET(td_list->hw.info), td_list->ep->ep_addr.iep);
+ td_err= td_list;
+ td_hw = td_list->ep->hw.head_td & 0xfffffff0;
+ while(td_hw != 0) {
+ if(td_hw == td_list->ep->hw.tail_td) break;
+ td = bus_to_virt(td_hw);
+ td_err->next_dl_td = td;
+ td_err= td;
+ td_hw = td->hw.next_td;
+ }
+ td_list->ep->hw.head_td = td_list->ep->hw.tail_td;
+ td->next_dl_td = td_list_next;
+ td_list_next = td_list->next_dl_td;
+
+ }
+ /* send the reply */
+ if(td_list->handler != NULL) {
+ if(td_list->prev_td == NULL) {
+ td_list->handler((void *) ohci,
+ td_list->ep->ep_addr.iep,
+ 0,
+ NULL,
+ td_list->buffer_start,
+ td_list->hw.buf_end-virt_to_bus(td_list->buffer_start)+1,
+ TD_CC_GET(td_list->hw.info),
+ td_list->lw0,
+ td_list->lw1);
+ OHCI_FREE(td_list);
+ }
+ else {
+ if(td_list->prev_td->prev_td == NULL) { /* cntrl 2 Transactions dataless */
+ td_list->handler((void *) ohci,
+ td_list->ep->ep_addr.iep,
+ td_list->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->buffer_start)+1,
+ td_list->prev_td->buffer_start,
+ NULL,
+ 0,
+ (TD_CC_GET(td_list->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->hw.info) : TD_CC_GET(td_list->hw.info),
+ td_list->lw0,
+ td_list->lw1);
+ OHCI_FREE(td_list->prev_td);
+ OHCI_FREE(td_list);
+ }
+ else { /* cntrl 3 Transactions */
+ td_list->handler((void *) ohci,
+ td_list->ep->ep_addr.iep,
+ td_list->prev_td->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->prev_td->buffer_start)+1,
+ td_list->prev_td->prev_td->buffer_start,
+ td_list->prev_td->buffer_start,
+ td_list->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->buffer_start)+1,
+ (TD_CC_GET(td_list->prev_td->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->prev_td->hw.info)
+ : (TD_CC_GET(td_list->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->hw.info) : TD_CC_GET(td_list->hw.info),
+ td_list->lw0,
+ td_list->lw1);
+ OHCI_FREE(td_list->prev_td->prev_td);
+ OHCI_FREE(td_list->prev_td);
+ OHCI_FREE(td_list);
+
+ }
+ }
+
+ }
+ td_list = td_list_next;
+ }
+ return 0;
+}
+
+
+
+/******
+ *** HC functions
+ ************************************/
+
+
+
+void reset_hc(struct ohci *ohci) {
+ int retries = 5;
+ int timeout = 30;
+ int fminterval;
+
+ if(readl(&ohci->regs->control) & 0x100) { /* SMM owns the HC */
+ writel(0x08, &ohci->regs->cmdstatus); /* request ownership */
+ printk("USB HC TakeOver from SMM\n");
+ do {
+ wait_ms(100);
+ if(--retries) {
+ printk("USB HC TakeOver timed out!\n");
+ break;
+ }
+ }
+ while(readl(&ohci->regs->control) & 0x100);
+ }
+
+ writel((1<<31), &ohci->regs->intrdisable); /* Disable HC interrupts */
+ OHCI_DEBUG(printk("USB HC reset_hc: %x ; retries: %d\n", readl(&ohci->regs->control), 5-retries);)
+ fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
+ writel(1, &ohci->regs->cmdstatus); /* HC Reset */
+ while ((readl(&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */
+ if (--timeout == 0) {
+ printk("USB HC reset timed out!\n");
+ return;
+ }
+ udelay(1);
+ }
+ /* set the timing */
+ fminterval |= (((fminterval -210) * 6)/7)<<16;
+ writel(fminterval, &ohci->regs->fminterval);
+ writel(((fminterval&0x3fff)*9)/10, &ohci->regs->periodicstart);
+}
+
+
+/*
+ * Reset and start an OHCI controller
+ */
+void start_hc(struct ohci *ohci)
+{
+ /* int fminterval; */
+ unsigned int mask;
+ /* fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
+ reset_hc(ohci); */
+
+
+ writel(virt_to_bus(&ohci->hc_area->hcca), &ohci->regs->hcca); /* a reset clears this */
+
+ /* Choose the interrupts we care about now, others later on demand */
+ mask = OHCI_INTR_MIE | OHCI_INTR_WDH;
+ /* | OHCI_INTR_SO | OHCI_INTR_UE |OHCI_INTR_RHSC |OHCI_INTR_SF|
+ OHCI_INTR_FNO */
+
+
+
+ writel((0x00), &ohci->regs->control); /* USB Reset BUS */
+ wait_ms(10);
+
+ writel((0x97), &ohci->regs->control); /* USB Operational */
+
+ writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */
+ wait_ms(50);
+
+ OHCI_DEBUG(printk("USB HC rstart_hc_operational: %x\n", readl(&ohci->regs->control)); )
+ OHCI_DEBUG(printk("USB HC roothubstata: %x \n", readl( &(ohci->regs->roothub.a) )); )
+ OHCI_DEBUG(printk("USB HC roothubstatb: %x \n", readl( &(ohci->regs->roothub.b) )); )
+ OHCI_DEBUG(printk("USB HC roothubstatu: %x \n", readl( &(ohci->regs->roothub.status) )); )
+ OHCI_DEBUG(printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) )); )
+ OHCI_DEBUG(printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) )); )
+
+ /* control_wakeup = NULL; */
+ writel(mask, &ohci->regs->intrenable);
+ writel(mask, &ohci->regs->intrstatus);
+
+#ifdef VROOTHUB
+ {
+
+ struct usb_device * usb_dev;
+ struct ohci_device *dev;
+
+ usb_dev = sohci_usb_allocate(ohci->root_hub->usb);
+ dev = usb_dev->hcpriv;
+
+ dev->ohci = ohci;
+
+ usb_connect(usb_dev);
+
+ ohci->root_hub->usb->children[0] = usb_dev;
+
+ usb_new_device(usb_dev);
+ }
+#endif
+
+
+
+}
+
+
+
+
+static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r)
+{
+ struct ohci *ohci = __ohci;
+ struct ohci_regs *regs = ohci->regs;
+
+ int ints;
+
+
+ if((ohci->hc_area->hcca.done_head != 0) && !(ohci->hc_area->hcca.done_head & 0x01)) {
+ ints = OHCI_INTR_WDH;
+ }
+ else {
+ if((ints = (readl(&regs->intrstatus) & readl(&regs->intrenable))) == 0)
+ return;
+ }
+
+ ohci->intrstatus |= ints;
+ OHCI_DEBUG(printk("USB HC interrupt: %x (%x) \n", ints, readl(&ohci->regs->intrstatus));)
+
+ /* ints &= ~(OHCI_INTR_WDH); WH Bit will be set by done list subroutine */
+ /* if(ints & OHCI_INTR_FNO) {
+ writel(OHCI_INTR_FNO, &regs->intrstatus);
+ if (waitqueue_active(&ohci_tasks)) wake_up(&ohci_tasks);
+ } */
+
+ if(ints & OHCI_INTR_WDH) {
+ writel(OHCI_INTR_WDH, &regs->intrdisable);
+ ohci->intrstatus &= (~OHCI_INTR_WDH);
+ usb_ohci_done_list(ohci); /* prepare out channel list */
+ writel(OHCI_INTR_WDH, &ohci->regs->intrstatus);
+ writel(OHCI_INTR_WDH, &ohci->regs->intrenable);
+
+ }
+
+ if(ints & OHCI_INTR_SF) {
+ writel(OHCI_INTR_SF, &regs->intrdisable);
+ writel(OHCI_INTR_SF, &ohci->regs->intrstatus);
+ ohci->intrstatus &= (~OHCI_INTR_SF);
+ if(ohci->ed_rm_list != NULL) {
+ ohci_rm_eds(ohci);
+ }
+ }
+#ifndef VROOTHUB
+ if(ints & OHCI_INTR_RHSC) {
+ writel(OHCI_INTR_RHSC, &regs->intrdisable);
+ writel(OHCI_INTR_RHSC, &ohci->regs->intrstatus);
+ wake_up(&root_hub);
+
+ }
+ #endif
+
+ writel(OHCI_INTR_MIE, &regs->intrenable);
+
+}
+
+#ifndef VROOTHUB
+/*
+ * This gets called if the connect status on the root
+ * hub (and the root hub only) changes.
+ */
+static void ohci_connect_change(struct ohci *ohci, unsigned int port_nr)
+{
+ struct usb_device *usb_dev;
+ struct ohci_device *dev;
+ OHCI_DEBUG(printk("uhci_connect_change: called for %d stat %x\n", port_nr,readl(&ohci->regs->roothub.portstatus[port_nr]) );)
+
+ /*
+ * Even if the status says we're connected,
+ * the fact that the status bits changed may
+ * that we got disconnected and then reconnected.
+ *
+ * So start off by getting rid of any old devices..
+ */
+ usb_disconnect(&ohci->root_hub->usb->children[port_nr]);
+
+ if(!(readl(&ohci->regs->roothub.portstatus[port_nr]) & RH_PS_CCS)) {
+ writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[port_nr]);
+ return; /* nothing connected */
+ }
+ /*
+ * Ok, we got a new connection. Allocate a device to it,
+ * and find out what it wants to do..
+ */
+ usb_dev = sohci_usb_allocate(ohci->root_hub->usb);
+ dev = usb_dev->hcpriv;
+ dev->ohci = ohci;
+ usb_connect(dev->usb);
+ ohci->root_hub->usb->children[port_nr] = usb_dev;
+ wait_ms(200); /* wait for powerup */
+ /* reset port/device */
+ writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[port_nr]); /* reset port */
+ while(!(readl( &ohci->regs->roothub.portstatus[port_nr]) & RH_PS_PRSC)) wait_ms(10); /* reset active ? */
+ writel(RH_PS_PES, &ohci->regs->roothub.portstatus[port_nr]); /* enable port */
+ wait_ms(10);
+ /* Get speed information */
+ usb_dev->slow = (readl( &ohci->regs->roothub.portstatus[port_nr]) & RH_PS_LSDA) ? 1 : 0;
+
+ /*
+ * Ok, all the stuff specific to the root hub has been done.
+ * The rest is generic for any new USB attach, regardless of
+ * hub type.
+ */
+ usb_new_device(usb_dev);
+}
+#endif
+
+
+
+/*
+ * Allocate the resources required for running an OHCI controller.
+ * Host controller interrupts must not be running while calling this
+ * function.
+ *
+ * The mem_base parameter must be the usable -virtual- address of the
+ * host controller's memory mapped I/O registers.
+ *
+ * This is where OHCI triumphs over UHCI, because good is dumb.
+ * Note how much simpler this function is than in uhci.c.
+ *
+ * OHCI hardware takes care of most of the scheduling of different
+ * transfer types with the correct prioritization for us.
+ */
+
+
+static struct ohci *alloc_ohci(void* mem_base)
+{
+ int i,j;
+ struct ohci *ohci;
+ struct ohci_hc_area *hc_area;
+ struct usb_bus *bus;
+ struct ohci_device *dev;
+ struct usb_device *usb;
+
+ /*
+ * Here we allocate some dummy EDs as well as the
+ * OHCI host controller communications area. The HCCA is just
+ * a nice pool of memory with pointers to endpoint descriptors
+ * for the different interrupts.
+ *
+ * The first page of memory contains the HCCA and ohci structure
+ */
+ hc_area = (struct ohci_hc_area *) __get_free_pages(GFP_KERNEL, 1);
+ if (!hc_area)
+ return NULL;
+ memset(hc_area, 0, sizeof(*hc_area));
+ ohci = &hc_area->ohci;
+ ohci->irq = -1;
+ ohci->regs = mem_base;
+
+ ohci->hc_area = hc_area;
+ /* Tell the controller where the HCCA is */
+ writel(virt_to_bus(&hc_area->hcca), &ohci->regs->hcca);
+
+
+ /*
+ * Initialize the ED polling "tree", full tree;
+ * dummy eds ed[i] (hc should skip them)
+ * i == 0 is the end of the iso list;
+ * 1 is the 1ms node,
+ * 2,3 2ms nodes,
+ * 4,5,6,7 4ms nodes,
+ * 8 ... 15 8ms nodes,
+ * 16 ... 31 16ms nodes,
+ * 32 ... 63 32ms nodes
+ * Sequenzes:
+ * 32-16- 8-4-2-1-0
+ * 33-17- 9-5-3-1-0
+ * 34-18-10-6-2-1-0
+ * 35-19-11-7-3-1-0
+ * 36-20-12-4-2-1-0
+ * 37-21-13-5-3-1-0
+ * 38-22-14-6-2-1-0
+ * 39-23-15-7-3-1-0
+ * 40-24- 8-4-2-1-0
+ * 41-25- 9-5-3-1-0
+ * 42-26-10-6-2-1-0
+ * : :
+ * 63-31-15-7-3-1-0
+ */
+ hc_area->ed[ED_ISO].info |= OHCI_ED_SKIP; /* place holder, so skip it */
+ hc_area->ed[ED_ISO].next_ed = 0x0000; /* end of iso list */
+
+ hc_area->ed[1].next_ed = virt_to_bus(&(hc_area->ed[ED_ISO]));
+ hc_area->ed[1].info |= OHCI_ED_SKIP; /* place holder, skip it */
+
+ j=1;
+ for (i = 2; i < (NUM_INTS * 2); i++) {
+ if (i >= NUM_INTS)
+ hc_area->hcca.int_table[i - NUM_INTS] = virt_to_bus(&(hc_area->ed[i]));
+
+ if( i == j*4) j *= 2;
+ hc_area->ed[i].next_ed = virt_to_bus(&(hc_area->ed[j+ i%j]));
+ hc_area->ed[i].info |= OHCI_ED_SKIP; /* place holder, skip it */
+ }
+
+
+ /*
+ * for load ballancing of the interrupt branches
+ */
+ for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0;
+
+ /*
+ * Store the end of control and bulk list eds. So, we know where we can add
+ * elements to these lists.
+ */
+ ohci->ed_isotail = (struct usb_ohci_ed *) &(hc_area->ed[ED_ISO]);
+ ohci->ed_controltail = NULL;
+ ohci->ed_bulktail = NULL;
+
+ /*
+ * Tell the controller where the control and bulk lists are
+ * The lists are empty now.
+ */
+ writel(0, &ohci->regs->ed_controlhead);
+ writel(0, &ohci->regs->ed_bulkhead);
+
+
+ USB_ALLOC(bus, sizeof(*bus));
+ if (!bus)
+ return NULL;
+
+ memset(bus, 0, sizeof(*bus));
+
+ ohci->bus = bus;
+ bus->hcpriv = (void *) ohci;
+ bus->op = &sohci_device_operations;
+
+
+ usb = sohci_usb_allocate(NULL);
+ if (!usb)
+ return NULL;
+
+ dev = ohci->root_hub = usb_to_ohci(usb);
+
+ usb->bus = bus;
+ /* bus->root_hub = ohci_to_usb(ohci->root_hub); */
+ dev->ohci = ohci;
+
+ /* Initialize the root hub */
+
+ usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff;
+ usb_init_root_hub(usb);
+
+ return ohci;
+}
+
+
+/*
+ * De-allocate all resources..
+ */
+
+static void release_ohci(struct ohci *ohci)
+{
+ int i;
+ union ep_addr_ ep_addr;
+ ep_addr.iep = 0;
+
+ OHCI_DEBUG(printk("USB HC release ohci \n");)
+
+ if (ohci->irq >= 0) {
+ free_irq(ohci->irq, ohci);
+ ohci->irq = -1;
+ }
+
+ /* stop hc */
+ writel(OHCI_USB_SUSPEND, &ohci->regs->control);
+
+ /* deallocate all EDs and TDs */
+ for(i=0; i < 128; i ++) {
+ ep_addr.bep.fa = i;
+ usb_ohci_rm_function(ohci, ep_addr.iep);
+ }
+ ohci_rm_eds(ohci); /* remove eds */
+
+ /* disconnect all devices */
+ if(ohci->root_hub)
+ for(i = 0; i < ohci->root_hub->usb->maxchild; i++)
+ usb_disconnect(ohci->root_hub->usb->children + i);
+
+ USB_FREE(ohci->root_hub->usb);
+ USB_FREE(ohci->root_hub);
+ USB_FREE(ohci->bus);
+
+ /* unmap the IO address space */
+ iounmap(ohci->regs);
+
+
+ free_pages((unsigned int) ohci->hc_area, 1);
+
+}
+
+
+void cleanup_drivers(void);
+
+static int ohci_roothub_thread(void * __ohci)
+{
+ struct ohci *ohci = (struct ohci *)__ohci;
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources..
+ */
+ printk("ohci_roothub_thread at %p\n", &ohci_roothub_thread);
+ exit_mm(current);
+ exit_files(current);
+ exit_fs(current);
+
+
+ strcpy(current->comm, "root-hub");
+
+
+ start_hc(ohci);
+ writel( 0x10000, &ohci->regs->roothub.status);
+ wait_ms(50); /* root hub power on */
+ do {
+#ifdef CONFIG_APM
+ if (apm_resume) {
+ apm_resume = 0;
+ start_hc(ohci);
+ continue;
+ }
+#endif
+
+ OHCI_DEBUG(printk("USB RH tasks: int: %x\n", ohci->intrstatus); )
+#ifndef VROOTHUB
+ /* if (ohci->intrstatus & OHCI_INTR_RHSC) */
+ {
+ int port_nr;
+ for(port_nr=0; port_nr< ohci->root_hub->usb->maxchild; port_nr++)
+ if(readl(&ohci->regs->roothub.portstatus[port_nr]) & (RH_PS_CSC | RH_PS_PRSC)) {
+ ohci_connect_change(ohci, port_nr);
+ writel(0xffff0000, &ohci->regs->roothub.portstatus[port_nr]);
+ }
+ ohci->intrstatus &= ~(OHCI_INTR_RHSC);
+ writel(OHCI_INTR_RHSC, &ohci->regs->intrenable);
+ }
+#endif
+
+ interruptible_sleep_on(&root_hub);
+
+ } while (!signal_pending(current));
+
+#ifdef VROOTHUB
+ ohci_del_rh_int_timer(ohci);
+#endif
+
+
+ cleanup_drivers();
+ /* reset_hc(ohci); */
+
+ release_ohci(ohci);
+ MOD_DEC_USE_COUNT;
+
+ printk("ohci_control_thread exiting\n");
+
+ return 0;
+}
+
+
+
+
+/*
+ * Increment the module usage count, start the control thread and
+ * return success.
+ */
+static int found_ohci(int irq, void* mem_base)
+{
+ int retval;
+ struct ohci *ohci;
+ OHCI_DEBUG(printk("USB HC found ohci: irq= %d membase= %x \n", irq, (int)mem_base);)
+ /* Allocate the running OHCI structures */
+ ohci = alloc_ohci(mem_base);
+ if (!ohci) {
+ return -ENOMEM;
+ }
+
+ reset_hc(ohci);
+
+ retval = -EBUSY;
+ if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "ohci-usb", ohci) == 0) {
+ int pid;
+
+ MOD_INC_USE_COUNT;
+ ohci->irq = irq;
+
+ pid = kernel_thread(ohci_roothub_thread, ohci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (pid >= 0)
+ return 0;
+
+
+ MOD_DEC_USE_COUNT;
+ retval = pid;
+ }
+
+ release_ohci(ohci);
+ return retval;
+}
+
+static int start_ohci(struct pci_dev *dev)
+{
+ unsigned int mem_base = dev->base_address[0];
+
+ /* If its OHCI, its memory */
+ if (mem_base & PCI_BASE_ADDRESS_SPACE_IO)
+ return -ENODEV;
+
+ /* Get the memory address and map it for IO */
+ mem_base &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ /*
+ * FIXME ioremap_nocache isn't implemented on all CPUs (such
+ * as the Alpha) [?] What should I use instead...
+ *
+ * The iounmap() is done on in release_ohci.
+ */
+ mem_base = (unsigned int) ioremap_nocache(mem_base, 4096);
+
+ if (!mem_base) {
+ printk("Error mapping OHCI memory\n");
+ return -EFAULT;
+ }
+
+ return found_ohci(dev->irq, (void *) mem_base);
+}
+
+
+
+#ifdef CONFIG_APM
+static int handle_apm_event(apm_event_t event)
+{
+ static int down = 0;
+
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ if (down) {
+ printk(KERN_DEBUG "ohci: received extra suspend event\n");
+ break;
+ }
+ down = 1;
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ if (!down) {
+ printk(KERN_DEBUG "ohci: received bogus resume event\n");
+ break;
+ }
+ down = 0;
+ if (waitqueue_active(&root_hub)) {
+ apm_resume = 1;
+ wake_up(&root_hub);
+ }
+ break;
+ }
+ return 0;
+}
+#endif
+
+
+ int usb_mouse_init(void);
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_APM
+ apm_unregister_callback(&handle_apm_event);
+#endif
+}
+
+#define ohci_hcd_init init_module
+
+#endif
+
+#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310
+#define PCI_CLASS_SERIAL_USB_OHCI_PG 0x10
+
+
+int ohci_hcd_init(void)
+{
+ int retval;
+ struct pci_dev *dev = NULL;
+
+ retval = -ENODEV;
+
+ dev = NULL;
+ while((dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev))) { /* OHCI */
+ retval = start_ohci(dev);
+ if (retval < 0) break;
+
+
+#ifdef CONFIG_USB_MOUSE
+ usb_mouse_init();
+#endif
+#ifdef CONFIG_USB_KBD
+ usb_kbd_init();
+#endif
+ hub_init();
+#ifdef CONFIG_USB_AUDIO
+ usb_audio_init();
+#endif
+#ifdef CONFIG_APM
+ apm_register_callback(&handle_apm_event);
+#endif
+
+ return 0;
+ }
+ return retval;
+}
+
+void cleanup_drivers(void)
+{
+ hub_cleanup();
+#ifdef CONFIG_USB_MOUSE
+ usb_mouse_cleanup();
+#endif
+}
+
diff --git a/drivers/usb/ohci-hcd.h b/drivers/usb/ohci-hcd.h
new file mode 100644
index 000000000..aa782bb1d
--- /dev/null
+++ b/drivers/usb/ohci-hcd.h
@@ -0,0 +1,404 @@
+ /*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ *
+ * The OHCI HCD layer is a simple but nearly complete implementation of what the
+ * USB people would call a HCD for the OHCI.
+ * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled)
+ * The layer on top of it, is for interfacing to the alternate-usb device-drivers.
+ *
+ * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ]
+ * [ Open Host Controller Interface driver for USB. ]
+ * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ]
+ * [ (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com> ]
+ * [ $Log: ohci.c,v $ ]
+ * [ Revision 1.1 1999/04/05 08:32:30 greg ]
+ *
+ *
+ * v2.1 1999/05/09 ep_addr correction, code clean up
+ * v2.0 1999/05/04
+ * v1.0 1999/04/27
+ * ohci-hcd.h
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_OHCI_VROOTHUB
+#define VROOTHUB
+#endif
+/* enables virtual root hub
+ * (root hub will be managed by the hub controller
+ * hub.c of the alternate usb driver)
+ * last time I did more testing without virtual root hub
+ * -> the virtual root hub could be more unstable now */
+
+
+
+#ifdef OHCI_DBG
+#define OHCI_DEBUG(X) X
+#else
+#define OHCI_DEBUG(X)
+#endif
+
+/* for readl writel functions */
+#include <linux/list.h>
+#include <asm/io.h>
+
+/* for ED and TD structures */
+
+typedef void * __OHCI_BAG;
+typedef int (*f_handler )(void * ohci, unsigned int ep_addr, int cmd_len, void *cmd, void *data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1);
+
+
+
+struct ep_address {
+ __u8 ep; /* bit 7: IN/-OUT, 6,5: type 10..CTRL 00..ISO 11..BULK 10..INT, 3..0: ep nr */
+ __u8 fa; /* function address */
+ __u8 hc;
+ __u8 host;
+};
+
+union ep_addr_ {
+ unsigned int iep;
+ struct ep_address bep;
+};
+
+/*
+ * ED and TD descriptors has to be 16-byte aligned
+ */
+struct ohci_hw_ed {
+ __u32 info;
+ __u32 tail_td; /* TD Queue tail pointer */
+ __u32 head_td; /* TD Queue head pointer */
+ __u32 next_ed; /* Next ED */
+} __attribute((aligned(16)));
+
+
+struct usb_ohci_ed {
+ struct ohci_hw_ed hw;
+ /* struct ohci * ohci; */
+ f_handler handler;
+ union ep_addr_ ep_addr;
+ struct usb_ohci_ed *ed_list;
+ struct usb_ohci_ed *ed_prev;
+} __attribute((aligned(32)));
+
+ /* OHCI Hardware fields */
+struct ohci_hw_td {
+ __u32 info;
+ __u32 cur_buf; /* Current Buffer Pointer */
+ __u32 next_td; /* Next TD Pointer */
+ __u32 buf_end; /* Memory Buffer End Pointer */
+} __attribute((aligned(16)));
+
+/* TD info field */
+#define TD_CC 0xf0000000
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x04)
+#define TD_EC 0x0C000000
+#define TD_T 0x03000000
+#define TD_T_DATA0 0x02000000
+#define TD_T_DATA1 0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R 0x00040000
+#define TD_DI 0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP 0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN 0x00100000
+#define TD_DP_OUT 0x00080000
+
+/* CC Codes */
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+#define TD_UNEXPECTEDPID 0x07
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+#define TD_NOTACCESSED 0x0F
+
+
+
+struct usb_ohci_td {
+ struct ohci_hw_td hw;
+ void * buffer_start;
+ f_handler handler;
+ struct usb_ohci_td *prev_td;
+ struct usb_ohci_ed *ep;
+ struct usb_ohci_td *next_dl_td;
+ __OHCI_BAG lw0;
+ __OHCI_BAG lw1;
+} __attribute((aligned(32)));
+
+
+
+/* TD types */
+#define BULK 0x03
+#define INT 0x01
+#define CTRL 0x02
+#define ISO 0x00
+/* TD types with direction */
+#define BULK_IN 0x07
+#define BULK_OUT 0x03
+#define INT_IN 0x05
+#define INT_OUT 0x01
+#define CTRL_IN 0x06
+#define CTRL_OUT 0x02
+#define ISO_IN 0x04
+#define ISO_OUT 0x00
+
+struct ohci_rep_td {
+ int cmd_len;
+ void * cmd;
+ void * data;
+ int data_len;
+ f_handler handler;
+ struct ohci_rep_td *next_td;
+ int ep_addr;
+ __OHCI_BAG lw0;
+ __OHCI_BAG lw1;
+ __u32 status;
+} __attribute((aligned(32)));
+
+#define OHCI_ED_SKIP (1 << 14)
+#define OHCI_ED_MPS (0x7ff << 16)
+#define OHCI_ED_F_NORM (0)
+#define OHCI_ED_F_ISOC (1 << 15)
+#define OHCI_ED_S_LOW (1 << 13)
+#define OHCI_ED_S_HIGH (0)
+#define OHCI_ED_D (3 << 11)
+#define OHCI_ED_D_IN (2 << 11)
+#define OHCI_ED_D_OUT (1 << 11)
+#define OHCI_ED_EN (0xf << 7)
+#define OHCI_ED_FA (0x7f)
+
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of. It must be 256-byte aligned.
+ */
+#define NUM_INTS 32 /* part of the OHCI standard */
+struct ohci_hcca {
+ __u32 int_table[NUM_INTS]; /* Interrupt ED table */
+ __u16 frame_no; /* current frame number */
+ __u16 pad1; /* set to 0 on each frame_no change */
+ __u32 done_head; /* info returned for an interrupt */
+ u8 reserved_for_hc[116];
+} __attribute((aligned(256)));
+
+
+
+#define ED_INT_1 1
+#define ED_INT_2 2
+#define ED_INT_4 4
+#define ED_INT_8 8
+#define ED_INT_16 16
+#define ED_INT_32 32
+#define ED_CONTROL 64
+#define ED_BULK 65
+#define ED_ISO 0 /* same as 1ms interrupt queue */
+
+
+/*
+ * This is the maximum number of root hub ports. I don't think we'll
+ * ever see more than two as that's the space available on an ATX
+ * motherboard's case, but it could happen. The OHCI spec allows for
+ * up to 15... (which is insane!)
+ *
+ * Although I suppose several "ports" could be connected directly to
+ * internal laptop devices such as a keyboard, mouse, camera and
+ * serial/parallel ports. hmm... That'd be neat.
+ */
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region. This is Memory Mapped I/O. You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+ /* control and status registers */
+ __u32 revision;
+ __u32 control;
+ __u32 cmdstatus;
+ __u32 intrstatus;
+ __u32 intrenable;
+ __u32 intrdisable;
+ /* memory pointers */
+ __u32 hcca;
+ __u32 ed_periodcurrent;
+ __u32 ed_controlhead;
+ __u32 ed_controlcurrent;
+ __u32 ed_bulkhead;
+ __u32 ed_bulkcurrent;
+ __u32 donehead;
+ /* frame counters */
+ __u32 fminterval;
+ __u32 fmremaining;
+ __u32 fmnumber;
+ __u32 periodicstart;
+ __u32 lsthresh;
+ /* Root hub ports */
+ struct ohci_roothub_regs {
+ __u32 a;
+ __u32 b;
+ __u32 status;
+ __u32 portstatus[MAX_ROOT_PORTS];
+ } roothub;
+} __attribute((aligned(32)));
+
+
+/*
+ * Read a MMIO register and re-write it after ANDing with (m)
+ */
+#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) )
+
+/*
+ * Read a MMIO register and re-write it after ORing with (b)
+ */
+#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) )
+
+/*
+ * cmdstatus register */
+#define OHCI_CLF 0x02
+#define OHCI_BLF 0x04
+
+/*
+ * Interrupt register masks
+ */
+#define OHCI_INTR_SO (1)
+#define OHCI_INTR_WDH (1 << 1)
+#define OHCI_INTR_SF (1 << 2)
+#define OHCI_INTR_RD (1 << 3)
+#define OHCI_INTR_UE (1 << 4)
+#define OHCI_INTR_FNO (1 << 5)
+#define OHCI_INTR_RHSC (1 << 6)
+#define OHCI_INTR_OC (1 << 30)
+#define OHCI_INTR_MIE (1 << 31)
+
+/*
+ * Control register masks
+ */
+#define OHCI_USB_OPER (2 << 6)
+#define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+
+struct ohci {
+ int irq;
+ struct ohci_regs *regs; /* OHCI controller's memory */
+ struct ohci_hc_area *hc_area; /* hcca, int ed-tree, ohci itself .. */
+ int root_hub_funct_addr; /* Address of Root Hub endpoint */
+ int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load ballancing)*/
+ struct usb_ohci_ed * ed_rm_list; /* list of all endpoints to be removed */
+ struct usb_ohci_ed * ed_bulktail; /* last endpoint of bulk list */
+ struct usb_ohci_ed * ed_controltail; /* last endpoint of control list */
+ struct usb_ohci_ed * ed_isotail; /* last endpoint of iso list */
+ struct ohci_device * root_hub;
+ struct usb_ohci_ed ed_rh_ep0;
+ struct usb_ohci_ed ed_rh_epi;
+ struct ohci_rep_td *td_rh_epi;
+ int intrstatus;
+ struct usb_ohci_ed *ed_func_ep0[128]; /* "hash"-table for ep to ed mapping */
+ struct ohci_rep_td *repl_queue; /* for internal requests */
+ int rh_int_interval;
+ int rh_int_timer;
+ struct usb_bus *bus;
+
+
+};
+
+/*
+ * Warning: This constant must not be so large as to cause the
+ * ohci_device structure to exceed one 4096 byte page. Or "weird
+ * things will happen" as the alloc_ohci() function assumes that
+ * its less than one page at the moment. (FIXME)
+ */
+#define NUM_TDS 4 /* num of preallocated transfer descriptors */
+#define NUM_EDS 80 /* num of preallocated endpoint descriptors */
+
+struct ohci_hc_area {
+
+ struct ohci_hcca hcca; /* OHCI mem. mapped IO area 256 Bytes*/
+
+ struct ohci_hw_ed ed[NUM_EDS]; /* Endpoint Descriptors 80 * 16 : 1280 Bytes */
+ struct ohci_hw_td td[NUM_TDS]; /* Transfer Descriptors 2 * 32 : 64 Bytes */
+ struct ohci ohci;
+
+};
+struct ohci_device {
+ struct usb_device *usb;
+ struct ohci *ohci;
+ unsigned long data[16];
+};
+
+#define ohci_to_usb(uhci) ((ohci)->usb)
+#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv)
+
+/* Debugging code */
+/*void show_ed(struct ohci_ed *ed);
+void show_td(struct ohci_td *td);
+void show_status(struct ohci *ohci); */
+
+/* hcd */
+int ohci_trans_req(struct ohci * ohci, unsigned int ep_addr, int cmd_len, void *cmd, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1);
+struct usb_ohci_ed *usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr, int interval, int load, f_handler handler, int ep_size, int speed);
+int usb_ohci_rm_function(struct ohci * ohci, unsigned int ep_addr);
+int usb_ohci_rm_ep(struct ohci * ohci, struct usb_ohci_ed *ed);
+struct usb_ohci_ed * ohci_find_ep(struct ohci *ohci, unsigned int ep_addr_in);
+
+/* roothub */
+int ohci_del_rh_int_timer(struct ohci * ohci);
+int ohci_init_rh_int_timer(struct ohci * ohci, int interval);
+int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler);
+int root_hub_send_irq(struct ohci * ohci, void * data, int data_len );
+int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler);
+int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler);
+int send_replies(struct ohci * ohci);
+
+
+
+
+/* Root-Hub Register info */
+
+#define RH_PS_CCS 0x00000001
+#define RH_PS_PES 0x00000002
+#define RH_PS_PSS 0x00000004
+#define RH_PS_POCI 0x00000008
+#define RH_PS_PRS 0x00000010
+#define RH_PS_PPS 0x00000100
+#define RH_PS_LSDA 0x00000200
+#define RH_PS_CSC 0x00010000
+#define RH_PS_PESC 0x00020000
+#define RH_PS_PSSC 0x00040000
+#define RH_PS_OCIC 0x00080000
+#define RH_PS_PRSC 0x00100000
+
+
+#ifdef OHCI_DBG
+#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d\n", -- __ohci_free_cnt)
+#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("OHCI ALLO: %d\n", ++ __ohci_free_cnt)
+#define USB_FREE(x) kfree(x); printk("USB FREE: %d\n", -- __ohci_free1_cnt)
+#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("USB ALLO: %d\n", ++ __ohci_free1_cnt)
+static int __ohci_free_cnt = 0;
+static int __ohci_free1_cnt = 0;
+#else
+#define OHCI_FREE(x) kfree(x)
+#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL)
+#define USB_FREE(x) kfree(x)
+#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL)
+#endif
+
diff --git a/drivers/usb/ohci-root-hub.c b/drivers/usb/ohci-root-hub.c
new file mode 100644
index 000000000..6d1018eff
--- /dev/null
+++ b/drivers/usb/ohci-root-hub.c
@@ -0,0 +1,604 @@
+/*
+ * HCD (OHCI) Virtual Root Hub for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at)
+ *
+ * The Root Hub is build into the HC (UHCI or OHCI) hardware.
+ * This piece of code lets it look like it resides on the usb
+ * like the other hubs.
+ * (for anyone who wants to do a control operation on the root hub)
+ *
+ * v2.1 1999/05/09
+ * v2.0 1999/05/04
+ * v1.0 1999/04/27
+ * ohci-root-hub.c
+ *
+ */
+
+
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+
+#include "usb.h"
+#include "ohci-hcd.h"
+
+#ifdef VROOTHUB
+
+#include "ohci-root-hub.h"
+
+
+static __u8 root_hub_dev_des[] =
+{
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x00, /* __u16 bcdUSB; v1.0 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __u16 idVendor; */
+ 0x00,
+ 0x00, /* __u16 idProduct; */
+ 0x00,
+ 0x00, /* __u16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x00, /* __u8 iProduct; */
+ 0x00, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __u16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 0x01, /* __u8 bConfigurationValue; */
+ 0x00, /* __u8 iConfiguration; */
+ 0x40, /* __u8 bmAttributes;
+ Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+ 0x00, /* __u8 MaxPower; */
+
+ /* interface */
+ 0x09, /* __u8 if_bLength; */
+ 0x04, /* __u8 if_bDescriptorType; Interface */
+ 0x00, /* __u8 if_bInterfaceNumber; */
+ 0x00, /* __u8 if_bAlternateSetting; */
+ 0x01, /* __u8 if_bNumEndpoints; */
+ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 if_bInterfaceSubClass; */
+ 0x00, /* __u8 if_bInterfaceProtocol; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x40, /* __u16 ep_wMaxPacketSize; 64 Bytes */
+ 0x00,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+/*
+For OHCI we need just the 2nd Byte, so we
+don't need this constant byte-array
+
+static __u8 root_hub_hub_des[] =
+{
+ 0x00, * __u8 bLength; *
+ 0x29, * __u8 bDescriptorType; Hub-descriptor *
+ 0x02, * __u8 bNbrPorts; *
+ 0x00, * __u16 wHubCharacteristics; *
+ 0x00,
+ 0x01, * __u8 bPwrOn2pwrGood; 2ms *
+ 0x00, * __u8 bHubContrCurrent; 0 mA *
+ 0x00, * __u8 DeviceRemovable; *** 8 Ports max *** *
+ 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** *
+};
+*/
+
+
+int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int leni, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler)
+{
+
+ __u32 stat;
+ __u32 rep_handler;
+ int req_reply=0;
+ union ep_addr_ ep_addr;
+ union ep_addr_ ep_addr_ret;
+ __u8 * cmd = rh_cmd;
+ __u8 * data = rh_data;
+ int i;
+ int len =leni;
+
+ __u8 bmRequestType = cmd[0];
+ __u8 bRequest = cmd[1];
+ __u16 wValue = cmd[3] << 8 | cmd [2];
+ __u16 wIndex = cmd[5] << 8 | cmd [4];
+ __u16 wLength = cmd[7] << 8 | cmd [6];
+printk("USB root hub: adr: %8x cmd(%8x): ", ohci->root_hub_funct_addr, 8);
+for(i=0;i<8;i++)
+ printk("%2x", ((char *)rh_cmd)[i]);
+
+printk(" ; \n");
+
+ ep_addr_ret.iep = 0;
+ ep_addr_ret.bep.fa = ohci->root_hub_funct_addr;
+ ep_addr_ret.bep.ep = (bmRequestType & 0x80) | 0x40;
+
+ switch (bmRequestType | bRequest << 8) {
+ /* Request Destination:
+ without flags: Device,
+ RH_INTERFACE: interface,
+ RH_ENDPOINT: endpoint,
+ RH_CLASS means HUB here,
+ RH_OTHER | RH_CLASS almost ever means HUB_PORT here
+ */
+
+ case RH_GET_STATUS:
+ len = 2;
+ data[0] = 0x01;
+ data[1] = 0x00;
+ req_reply = RH_ACK;
+ break;
+ case RH_GET_STATUS | RH_INTERFACE:
+ len = 2;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ req_reply = RH_ACK;
+ break;
+ case RH_GET_STATUS | RH_ENDPOINT:
+ len = 2;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ req_reply = RH_ACK;
+ break;
+ case RH_GET_STATUS | RH_CLASS: /* HUB_STATUS */
+ stat = readl(&ohci->regs->roothub.status) & 0x7fff7fff; /* bit 31 u. 15 has other meaning */
+ data[0] = stat & 0xff;
+ data[1] = (stat >> 8) & 0xff;
+ data[2] = (stat >> 16) & 0xff;
+ data[3] = (stat >> 24) & 0xff;
+ len = 4;
+ req_reply = RH_ACK;
+ break;
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS: /* PORT_STATUS */
+ stat = readl(&ohci->regs->roothub.portstatus[wIndex-1]);
+ data[0] = stat & 0xff;
+ data[1] = (stat >> 8) & 0xff;
+ data[2] = (stat >> 16) & 0xff;
+ data[3] = (stat >> 24) & 0xff;
+ len = 4;
+ req_reply = RH_ACK;
+ printk("rh: stat %4x wIndex %4x;\n", stat , wIndex);
+ break;
+
+ case RH_CLEAR_FEATURE:
+ switch (wValue) {
+ case (RH_DEVICE_REMOTE_WAKEUP):
+ default:
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL):
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ default:
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ switch (wValue) {
+ /* case (RH_C_HUB_LOCAL_POWER): OHCI says: no switching of this one */
+ case (RH_C_HUB_OVER_CURRENT):
+ writel(RH_PS_OCIC, &ohci->regs->roothub.status);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ default:
+ }
+ break;
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_ENABLE):
+ writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_SUSPEND):
+ writel(RH_PS_POCI, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_POWER):
+ writel(RH_PS_LSDA, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_CONNECTION):
+ writel(RH_PS_CSC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_ENABLE):
+ writel(RH_PS_PESC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_SUSPEND):
+ writel(RH_PS_PSSC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_OVER_CURRENT):
+ writel(RH_PS_OCIC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_RESET):
+ writel(RH_PS_PRSC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ /*
+ case (RH_PORT_CONNECTION):
+ case (RH_PORT_OVER_CURRENT):
+ case (RH_PORT_RESET):
+ case (RH_PORT_LOW_SPEED):
+ */
+ default:
+ }
+ break;
+ case RH_SET_FEATURE:
+ switch (wValue) {
+ case (RH_DEVICE_REMOTE_WAKEUP):
+ default:
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL):
+ default:
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_CLASS:
+ switch (wValue) {
+ /* case (RH_C_HUB_LOCAL_POWER): Root Hub has no local Power
+ case (RH_C_HUB_OVER_CURRENT): */
+ default:
+ }
+ break;
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_SUSPEND):
+ writel(RH_PS_PSS, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_RESET):
+ if((readl(&ohci->regs->roothub.portstatus[wIndex-1]) &1) != 0) /* BUG IN HUP CODE *********/
+ writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_POWER):
+ writel(RH_PS_PPS, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_ENABLE):
+ writel(RH_PS_PES, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ /*
+ case (RH_PORT_CONNECTION):
+ case (RH_PORT_OVER_CURRENT):
+ case (RH_PORT_LOW_SPEED):
+ case (RH_C_PORT_CONNECTION):
+ case (RH_C_PORT_ENABLE):
+ case (RH_C_PORT_SUSPEND):
+ case (RH_C_PORT_OVER_CURRENT):
+ case (RH_C_PORT_RESET):
+ */
+ default:
+ }
+ break;
+
+ case RH_SET_ADDRESS:
+ ohci->root_hub_funct_addr = wValue;
+ /* ohci->ed_func_ep0[wValue] = &ohci->ed_rh_ep0;
+ ohci->ed_func_ep0[wValue]->ep_addr.bep.fa = wValue;
+ ohci->ed_func_ep0[wValue]->ed_list = &ohci->ed_rh_epi; */
+ ohci->ed_rh_epi.ed_list = NULL;
+ ohci->ed_rh_epi.ep_addr.bep.fa = wValue;
+ ohci->ed_rh_epi.ep_addr.bep.ep = 0xa1; /* Int in port 1 */
+ ohci->ed_func_ep0[0]= NULL;
+ len = 0;
+ req_reply = RH_ACK;
+ break;
+
+ case RH_GET_DESCRIPTOR:
+ switch ((wValue & 0xff00) >> 8) {
+ case (0x01): /* device descriptor */
+ len = min(sizeof(root_hub_dev_des), wLength);
+ memcpy(data, root_hub_dev_des, len);
+ req_reply = RH_ACK;
+ break;
+ case (0x02): /* configuration descriptor */
+ len = min(sizeof(root_hub_config_des), wLength);
+ memcpy(data, root_hub_config_des, len);
+ req_reply = RH_ACK;
+ break;
+ case (0x03): /* string descriptors */
+ default:
+ }
+ break;
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ data[1] = 0x29;
+ stat = readl(&ohci->regs->roothub.a);
+ data[2] = stat & 0xff; /* number of ports */
+ data[0] = (data[2] / 8) * 2 + 9; /* length of descriptor */
+ if(data[0] > wLength) {
+ req_reply = RH_REQ_ERR;
+ break;
+ }
+ data[3] = (stat >> 8) & 0xff;
+ data[4] = (stat >> 16) & 0xff;
+ data[5] = (stat >> 24) & 0xff;
+ data[6] = 0; /* Root Hub needs no current from bus */
+ stat = readl(&ohci->regs->roothub.b);
+ if(data[2] <= 8) { /* less than 8 Ports */
+ data[7] = stat & 0xff;
+ data[8] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1 ?, stat >> 16 for USB Rev. 1.0 */
+ }
+ else {
+ data[7] = stat & 0xff;
+ data[8] = (stat >> 8) & 0xff;
+ data[9] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 16 for USB Rev. 1.0 */
+ data[10] = (stat >> 24) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 24 for USB Rev. 1.0 */
+ }
+ len = data[0];
+ req_reply = RH_ACK;
+ break;
+
+ case RH_SET_DESCRIPTOR:
+ break;
+
+ case RH_GET_CONFIGURATION:
+ len = 1;
+ data[0] = 0x01;
+ req_reply = RH_ACK;
+ break;
+
+ case RH_SET_CONFIGURATION: /* start it up */
+ writel( 0x10000, &ohci->regs->roothub.status);
+ /*writel( OHCI_INTR_RHSC, &ohci->regs->intrenable);*/
+ len = 0;
+ req_reply = RH_ACK;
+ break;
+ /* Optional or meaningless requests
+ case RH_GET_STATE | RH_OTHER | RH_CLASS:
+ case RH_GET_INTERFACE | RH_INTERFACE:
+ case RH_SET_INTERFACE | RH_INTERFACE:
+ case RH_SYNC_FRAME | RH_ENDPOINT:
+ */
+
+ /* Vendor Requests, we are the vendor!
+ Will the USB-Consortium give us a Vendor Id
+ for a virtual hub-device :-) ?
+ We could use these requests for configuration purposes on the HCD Driver, not used in the altenate usb !*/
+
+ case RH_SET_FEATURE | RH_VENDOR: /* remove all endpoints of device wIndex = Dev << 8 */
+ switch(wValue) {
+ case RH_REMOVE_EP:
+ ep_addr.iep = 0;
+ ep_addr.bep.ep = wIndex & 0xff;
+ ep_addr.bep.fa = (wIndex << 8) & 0xff00;
+ usb_ohci_rm_function(ohci, ep_addr.iep);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ }
+ break;
+ case RH_SET_FEATURE | RH_ENDPOINT | RH_VENDOR: /* remove endpoint wIndex = Dev << 8 | EP */
+ switch(wValue) {
+ case RH_REMOVE_EP:
+ ep_addr.iep = 0;
+ ep_addr.bep.ep = wIndex & 0xff;
+ ep_addr.bep.fa = (wIndex << 8) & 0xff00;
+ usb_ohci_rm_ep(ohci, ohci_find_ep(ohci, ep_addr.iep));
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ }
+ break;
+ case RH_SET_EP | RH_ENDPOINT | RH_VENDOR:
+ ep_addr.bep.ep = data[0];
+ ep_addr.bep.fa = data[1];
+ ep_addr.bep.hc = data[2];
+ ep_addr.bep.host = data[3];
+ rep_handler = data[7] << 24 |data[6] << 16 | data[5] << 8 | data[4];
+ /* struct usb_ohci_ed *usb_ohci_add_ep(union ep_addr_ ep_addr,
+ struct ohci * ohci, int interval, int load, int (*handler)(int, void*), int ep_size, int speed) */
+ usb_ohci_add_ep(ohci, ep_addr.iep, data[8], data[9], (f_handler) rep_handler, data[11] << 8 | data[10] , data[12]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+
+ default:
+ }
+ printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) ));
+ printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) ));
+
+ /* if (req_reply == RH_ACK) len; */
+ queue_reply(ohci, ep_addr_ret.iep, 8, rh_cmd, data, len, lw0, lw1, handler);
+ return 0;
+}
+
+int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler){
+
+ struct ohci_rep_td *td;
+ struct ohci_rep_td *tmp;
+ union ep_addr_ ep_addr;
+
+ td = kmalloc(sizeof(td),GFP_KERNEL);
+ tmp = ohci->td_rh_epi;
+ td->next_td = NULL;
+ if(tmp == NULL) { /* queue td */
+ ohci->td_rh_epi = td;
+ }
+ else {
+ while(tmp->next_td != NULL) tmp = tmp->next_td;
+ tmp->next_td = td;
+ }
+ ep_addr.iep = 0;
+ ep_addr.bep.fa = ohci->root_hub_funct_addr;
+ ep_addr.bep.ep = 0xA1; /* INT IN EP endpoint 1 */
+ td->cmd_len = 0;
+ td->cmd = NULL;
+ td->data = data;
+ td->data_len = data_len;
+ td->handler = handler;
+ td->next_td = NULL;
+ td->ep_addr = ep_addr.iep;
+ td->lw0 = lw0;
+ td->lw1 = lw1;
+ ohci_init_rh_int_timer(ohci, 255);
+ return 0;
+}
+
+
+/* prepare Interrupt pipe transaction data; HUP INTERRUPT ENDPOINT */
+int root_hub_send_irq(struct ohci * ohci, void * rh_data, int rh_len ) {
+
+ int num_ports;
+ int i;
+ int ret;
+ int len;
+
+ __u8 * data = rh_data;
+
+ num_ports = readl(&ohci->regs->roothub.a) & 0xff;
+ data[0] = (readl(&ohci->regs->roothub.status) & 0x00030000)>0?1:0;
+ ret = data[0];
+
+ for(i=0; i < num_ports; i++) {
+ data[i/8] |= ((readl(&ohci->regs->roothub.portstatus[i]) & 0x001f0000)>0?1:0) << ((i+1) % 8);
+ ret += data[i/8];
+ }
+ len = i/8 + 1;
+
+ if (ret > 0) return len;
+
+ return RH_NACK;
+}
+
+
+
+
+
+static struct timer_list rh_int_timer;
+
+/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
+static void rh_int_timer_do(unsigned long ptr) {
+ int len;
+ int interval;
+ struct ohci * ohci = (struct ohci *) ptr;
+ struct ohci_rep_td *td = ohci->td_rh_epi;
+
+ if(td != NULL) { /* if ther is a TD handle the INT request */
+
+ len = root_hub_send_irq(ohci, td->data, td->data_len );
+ if(len > 0) {
+ ohci->td_rh_epi = td->next_td;
+ td->next_td = ohci->repl_queue;
+ ohci->repl_queue = td;
+ send_replies(ohci);
+ }
+ }
+ interval = ohci->rh_int_interval;
+ init_timer(& rh_int_timer);
+ rh_int_timer.function = rh_int_timer_do;
+ rh_int_timer.data = (unsigned long) ohci;
+ rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000;
+ add_timer(&rh_int_timer);
+}
+
+/* Root Hub INTs are polled by this timer */
+int ohci_init_rh_int_timer(struct ohci * ohci, int interval) {
+
+ if(!(ohci->rh_int_timer)) {
+ ohci->rh_int_timer = 1;
+ ohci->rh_int_interval = interval;
+ init_timer(& rh_int_timer);
+ rh_int_timer.function = rh_int_timer_do;
+ rh_int_timer.data = (unsigned long) ohci;
+ rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000;
+ add_timer(&rh_int_timer);
+ }
+ return 0;
+}
+
+int ohci_del_rh_int_timer(struct ohci * ohci) {
+ del_timer(&rh_int_timer);
+ return 0;
+}
+/* for root hub replies, queue the reply, (it will be sent immediately now) */
+
+int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler) {
+
+ struct ohci_rep_td *td;
+ int status = 0;
+
+printk("queue_reply ep: %x len: %x\n", ep_addr, len);
+td = kmalloc(sizeof(td), GFP_KERNEL);
+
+ if (len < 0) { status = len; len = 0;}
+ td->cmd_len = cmd_len;
+ td->cmd = cmd;
+ td->data = data;
+ td->data_len = len;
+ td->handler = handler;
+ td->next_td = ohci->repl_queue; ohci->repl_queue = td;
+ td->ep_addr = ep_addr;
+ td->lw0 = lw0;
+ td->lw1 = lw1;
+ td->status = status;
+ send_replies(ohci);
+return 0;
+}
+
+/* for root hub replies; send the reply */
+int send_replies(struct ohci * ohci) {
+ struct ohci_rep_td *td;
+ struct ohci_rep_td *tmp;
+
+ td = ohci->repl_queue; ohci->repl_queue = NULL;
+ while ( td != NULL) {
+ td->handler((void *) ohci, td->ep_addr,td->cmd_len,td->cmd, td->data, td->data_len, td->status, td->lw0, td->lw1);
+ tmp = td;
+ td = td->next_td;
+
+ kfree(tmp);
+ }
+ return 0;
+}
+
+#endif
diff --git a/drivers/usb/ohci-root-hub.h b/drivers/usb/ohci-root-hub.h
new file mode 100644
index 000000000..8f1e80998
--- /dev/null
+++ b/drivers/usb/ohci-root-hub.h
@@ -0,0 +1,71 @@
+/*
+ * HCD (OHCI) Virtual Root Hub Protocol for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at)
+ *
+ * The Root Hub is build into the HC (UHCI or OHCI) hardware.
+ * This piece of code lets it look like it resides on the bus
+ * like the other hubs.
+ * (for anyone who wants to do a control operation on the root hub)
+ *
+ * v1.0 1999/04/27
+ * ohci-root-hub.h
+ *
+ */
+
+/* destination of request */
+#define RH_INTERFACE 0x01
+#define RH_ENDPOINT 0x02
+#define RH_OTHER 0x03
+
+#define RH_CLASS 0x20
+#define RH_VENDOR 0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS 0x0080
+#define RH_CLEAR_FEATURE 0x0100
+#define RH_SET_FEATURE 0x0300
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_DESCRIPTOR 0x0700
+#define RH_GET_CONFIGURATION 0x0880
+#define RH_SET_CONFIGURATION 0x0900
+#define RH_GET_STATE 0x0280
+#define RH_GET_INTERFACE 0x0A80
+#define RH_SET_INTERFACE 0x0B00
+#define RH_SYNC_FRAME 0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP 0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION 0x00
+#define RH_PORT_ENABLE 0x01
+#define RH_PORT_SUSPEND 0x02
+#define RH_PORT_OVER_CURRENT 0x03
+#define RH_PORT_RESET 0x04
+#define RH_PORT_POWER 0x08
+#define RH_PORT_LOW_SPEED 0x09
+#define RH_C_PORT_CONNECTION 0x10
+#define RH_C_PORT_ENABLE 0x11
+#define RH_C_PORT_SUSPEND 0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET 0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER 0x00
+#define RH_C_HUB_OVER_CURRENT 0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP 0x00
+#define RH_ENDPOINT_STALL 0x01
+
+/* Our Vendor Specific feature */
+#define RH_REMOVE_EP 0x00
+
+
+#define RH_ACK 0x01
+#define RH_REQ_ERR -1
+#define RH_NACK 0x00
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
diff --git a/drivers/usb/ohci.c b/drivers/usb/ohci.c
new file mode 100644
index 000000000..b12d0114c
--- /dev/null
+++ b/drivers/usb/ohci.c
@@ -0,0 +1,1669 @@
+/*
+ * Open Host Controller Interface driver for USB.
+ *
+ * (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com>
+ *
+ * This is the "other" host controller interface for USB. You will
+ * find this on many non-Intel based motherboards, and of course the
+ * Mac. As Linus hacked his UHCI driver together first, I modeled
+ * this after his.. (it should be obvious)
+ *
+ * From the programming standpoint the OHCI interface seems a little
+ * prettier and potentially less CPU intensive. This remains to be
+ * proven. In reality, I don't believe it'll make one darn bit of
+ * difference. USB v1.1 is a slow bus by today's standards.
+ *
+ * OHCI hardware takes care of most of the scheduling of different
+ * transfer types with the correct prioritization for us.
+ *
+ * To get started in USB, I used the "Universal Serial Bus System
+ * Architecture" book by Mindshare, Inc. It was a reasonable introduction
+ * and overview of USB and the two dominant host controller interfaces
+ * however you're better off just reading the real specs available
+ * from www.usb.org as you'll need them to get enough detailt to
+ * actually implement a HCD. The book has many typos and omissions
+ * Beware, the specs are the victim of a committee.
+ *
+ * This code was written with Guinness on the brain, xsnow on the desktop
+ * and Orbital, Orb, Enya & Massive Attack on the CD player. What a life! ;)
+ *
+ * No filesystems were harmed in the development of this code.
+ *
+ * $Id: ohci.c,v 1.26 1999/05/11 07:34:47 greg Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+
+#include <asm/spinlock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "ohci.h"
+#include "inits.h"
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int handle_apm_event(apm_event_t event);
+static int apm_resume = 0;
+#endif
+
+static struct wait_queue *ohci_configure = NULL;
+
+#ifdef OHCI_TIMER
+static struct timer_list ohci_timer; /* timer for root hub polling */
+#endif
+
+
+static int ohci_td_result(struct ohci_device *dev, struct ohci_td *td)
+{
+ unsigned int status;
+
+ status = td->info & OHCI_TD_CC;
+
+ /* TODO Debugging code for TD failures goes here */
+
+ return status;
+} /* ohci_td_result() */
+
+
+static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Add a TD to the end of the TD list on a given ED. If td->next_td
+ * points to any more TDs, they will be added as well (naturally).
+ * Otherwise td->next_td must be 0.
+ *
+ * The SKIP flag will be cleared after this function.
+ *
+ * Important! This function needs locking and atomicity as it works
+ * in parallel with the HC's DMA. Locking ohci_edtd_lock while using
+ * the function is a must.
+ *
+ * This function can be called by the interrupt handler.
+ */
+static void ohci_add_td_to_ed(struct ohci_td *td, struct ohci_ed *ed)
+{
+ /* don't let the HC pull anything from underneath us */
+ ed->status |= OHCI_ED_SKIP;
+
+ if (ed_head_td(ed) == 0) { /* empty list, put it on the head */
+ set_ed_head_td(ed, virt_to_bus(td));
+ ed->tail_td = 0;
+ } else {
+ struct ohci_td *tail, *head;
+ head = (ed_head_td(ed) == 0) ? NULL : bus_to_virt(ed_head_td(ed));
+ tail = (ed->tail_td == 0) ? NULL : bus_to_virt(ed->tail_td);
+ if (!tail) { /* no tail, single element list */
+ td->next_td = head->next_td;
+ head->next_td = virt_to_bus(td);
+ ed->tail_td = virt_to_bus(td);
+ } else { /* append to the list */
+ td->next_td = tail->next_td;
+ tail->next_td = virt_to_bus(td);
+ ed->tail_td = virt_to_bus(td);
+ }
+ }
+
+ /* save the ED link in each of the TDs added */
+ td->ed = ed;
+ while (td->next_td != 0) {
+ td = bus_to_virt(td->next_td);
+ td->ed = ed;
+ }
+
+ /* turn off the SKIP flag */
+ ed->status &= ~OHCI_ED_SKIP;
+} /* ohci_add_td_to_ed() */
+
+
+inline void ohci_start_control(struct ohci *ohci)
+{
+ /* tell the HC to start processing the control list */
+ writel(OHCI_CMDSTAT_CLF, &ohci->regs->cmdstatus);
+}
+
+inline void ohci_start_bulk(struct ohci *ohci)
+{
+ /* tell the HC to start processing the bulk list */
+ writel(OHCI_CMDSTAT_BLF, &ohci->regs->cmdstatus);
+}
+
+inline void ohci_start_periodic(struct ohci *ohci)
+{
+ /* enable processing periodc transfers starting next frame */
+ writel_set(OHCI_USB_PLE, &ohci->regs->control);
+}
+
+inline void ohci_start_isoc(struct ohci *ohci)
+{
+ /* enable processing isoc. transfers starting next frame */
+ writel_set(OHCI_USB_IE, &ohci->regs->control);
+}
+
+/*
+ * Add an ED to the hardware register ED list pointed to by hw_listhead_p
+ */
+static void ohci_add_ed_to_hw(struct ohci_ed *ed, void* hw_listhead_p)
+{
+ __u32 listhead;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ohci_edtd_lock, flags);
+
+ listhead = readl(hw_listhead_p);
+
+ /* if the list is not empty, insert this ED at the front */
+ /* XXX should they go on the end? */
+ if (listhead) {
+ ed->next_ed = listhead;
+ }
+
+ /* update the hardware listhead pointer */
+ writel(virt_to_bus(ed), hw_listhead_p);
+
+ spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+} /* ohci_add_ed() */
+
+
+/*
+ * Put another control ED on the controller's list
+ */
+void ohci_add_control_ed(struct ohci *ohci, struct ohci_ed *ed)
+{
+ ohci_add_ed_to_hw(ed, &ohci->regs->ed_controlhead);
+ ohci_start_control(ohci);
+} /* ohci_add_control_ed() */
+
+
+#if 0
+/*
+ * Put another control ED on the controller's list
+ */
+void ohci_add_periodic_ed(struct ohci *ohci, struct ohci_ed *ed, int period)
+{
+ ohci_add_ed_to_hw(ed, /* XXX */);
+ ohci_start_periodic(ohci);
+} /* ohci_add_control_ed() */
+#endif
+
+
+/*
+ * Remove an ED from the HC list whos bus headpointer is pointed to
+ * by hw_listhead_p
+ *
+ * Note that the SKIP bit is left on in the removed ED.
+ */
+void ohci_remove_ed_from_hw(struct ohci_ed *ed, __u32* hw_listhead_p)
+{
+ unsigned long flags;
+ struct ohci_ed *cur;
+ __u32 bus_ed = virt_to_bus(ed);
+ __u32 bus_cur;
+
+ if (ed == NULL || !bus_ed)
+ return;
+
+ /* tell the controller this skip ED */
+ ed->status |= OHCI_ED_SKIP;
+
+ bus_cur = readl(hw_listhead_p);
+
+ if (bus_cur == 0)
+ return; /* the list is already empty */
+
+ cur = bus_to_virt(bus_cur);
+
+ spin_lock_irqsave(&ohci_edtd_lock, flags);
+
+ /* if its the head ED, move the head */
+ if (bus_cur == bus_ed) {
+ writel(cur->next_ed, hw_listhead_p);
+ } else if (cur->next_ed != 0) {
+ struct ohci_ed *prev;
+
+ /* walk the list and unlink the ED if found */
+ for (;;) {
+ prev = cur;
+ cur = bus_to_virt(cur->next_ed);
+
+ if (virt_to_bus(cur) == bus_ed) {
+ /* unlink from the list */
+ prev->next_ed = cur->next_ed;
+ break;
+ }
+
+ if (cur->next_ed == 0)
+ break;
+ }
+ }
+
+ /* clear any links from the ED for safety */
+ ed->next_ed = 0;
+
+ spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+} /* ohci_remove_ed_from_hw() */
+
+/*
+ * Remove an ED from the controller's control list. Note that the SKIP bit
+ * is left on in the removed ED.
+ */
+inline void ohci_remove_control_ed(struct ohci *ohci, struct ohci_ed *ed)
+{
+ ohci_remove_ed_from_hw(ed, &ohci->regs->ed_controlhead);
+}
+
+/*
+ * Remove an ED from the controller's bulk list. Note that the SKIP bit
+ * is left on in the removed ED.
+ */
+inline void ohci_remove_bulk_ed(struct ohci *ohci, struct ohci_ed *ed)
+{
+ ohci_remove_ed_from_hw(ed, &ohci->regs->ed_bulkhead);
+}
+
+
+/*
+ * Remove a TD from the given EDs TD list.
+ */
+static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed)
+{
+ unsigned long flags;
+ struct ohci_td *head_td;
+
+ if ((td == NULL) || (ed == NULL))
+ return;
+
+ spin_lock_irqsave(&ohci_edtd_lock, flags);
+
+ if (ed_head_td(ed) == 0)
+ return;
+
+ /* set the "skip me bit" in this ED */
+ ed->status |= OHCI_ED_SKIP;
+
+ /* XXX Assuming this list will never be circular */
+
+ head_td = bus_to_virt(ed_head_td(ed));
+ if (virt_to_bus(td) == ed_head_td(ed)) {
+ /* It's the first TD, remove it. */
+ set_ed_head_td(ed, head_td->next_td);
+ } else {
+ struct ohci_td *prev_td, *cur_td;
+
+ /* FIXME: collapse this into a nice simple loop :) */
+ if (head_td->next_td != 0) {
+ prev_td = head_td;
+ cur_td = bus_to_virt(head_td->next_td);
+ for (;;) {
+ if (td == cur_td) {
+ /* remove it */
+ prev_td->next_td = cur_td->next_td;
+ break;
+ }
+ if (cur_td->next_td == 0)
+ break;
+ prev_td = cur_td;
+ cur_td = bus_to_virt(cur_td->next_td);
+ }
+ }
+ }
+
+ td->next_td = 0; /* remove the TDs links */
+ td->ed = NULL;
+
+ /* TODO return this TD to the pool of free TDs */
+
+ /* unset the "skip me bit" in this ED */
+ ed->status &= ~OHCI_ED_SKIP;
+
+ spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+} /* ohci_remove_td_from_ed() */
+
+
+/*
+ * Get a pointer (virtual) to an available TD from the given device's
+ * pool.
+ *
+ * Return NULL if none are left.
+ */
+static struct ohci_td *ohci_get_free_td(struct ohci_device *dev)
+{
+ int idx;
+
+ for (idx=0; idx < NUM_TDS; idx++) {
+ if (!td_allocated(dev->td[idx])) {
+ struct ohci_td *new_td = &dev->td[idx];
+ /* zero out the TD */
+ memset(new_td, 0, sizeof(*new_td));
+ /* mark the new TDs as unaccessed */
+ new_td->info = OHCI_TD_CC_NEW;
+ /* mark it as allocated */
+ allocate_td(new_td);
+ return new_td;
+ }
+ }
+
+ printk("usb-ohci error: unable to allocate a TD\n");
+ return NULL;
+} /* ohci_get_free_td() */
+
+
+/*
+ * Initialize a TD
+ *
+ * dir = OHCI_TD_D_IN, OHCI_TD_D_OUT, or OHCI_TD_D_SETUP
+ * toggle = TOGGLE_AUTO, TOGGLE_DATA0, TOGGLE_DATA1
+ */
+inline struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, __u32 flags, void *data, __u32 len, void *dev_id, usb_device_irq completed)
+{
+ /* hardware fields */
+ td->info = OHCI_TD_CC_NEW |
+ (dir & OHCI_TD_D) |
+ (toggle & OHCI_TD_DT) |
+ flags;
+ td->cur_buf = (data == NULL) ? 0 : virt_to_bus(data);
+ td->buf_end = (len == 0) ? 0 : td->cur_buf + len - 1;
+
+ /* driver fields */
+ td->data = data;
+ td->dev_id = dev_id;
+ td->completed = completed;
+
+ return td;
+} /* ohci_fill_new_td() */
+
+
+/**********************************
+ * OHCI interrupt list operations *
+ **********************************/
+
+/*
+ * Request an interrupt handler for one "pipe" of a USB device.
+ * (this function is pretty minimal right now)
+ *
+ * At the moment this is only good for input interrupts. (ie: for a
+ * mouse or keyboard)
+ *
+ * Period is desired polling interval in ms. The closest, shorter
+ * match will be used. Powers of two from 1-32 are supported by OHCI.
+ */
+static int ohci_request_irq(struct usb_device *usb, unsigned int pipe,
+ usb_device_irq handler, int period, void *dev_id)
+{
+ struct ohci_device *dev = usb_to_ohci(usb);
+ struct ohci_td *td;
+ struct ohci_ed *interrupt_ed; /* endpoint descriptor for this irq */
+
+ /*
+ * Pick a good frequency endpoint based on the requested period
+ */
+ interrupt_ed = &dev->ohci->root_hub->ed[ms_to_ed_int(period)];
+
+ /*
+ * Set the max packet size, device speed, endpoint number, usb
+ * device number (function address), and type of TD.
+ *
+ * FIXME: Isochronous transfers need a pool of special 32 byte
+ * TDs (32 byte aligned) in order to be supported.
+ */
+ interrupt_ed->status = \
+ ed_set_maxpacket(usb_maxpacket(pipe)) |
+ ed_set_speed(usb_pipeslow(pipe)) |
+ usb_pipe_endpdev(pipe) |
+ OHCI_ED_F_NORM;
+
+ td = ohci_get_free_td(dev);
+ /* FIXME: check for NULL */
+
+ /* Fill in the TD */
+ ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)),
+ TOGGLE_AUTO,
+ OHCI_TD_ROUND,
+ dev->data, DATA_BUF_LEN,
+ dev_id, handler);
+ /*
+ * TODO: be aware that OHCI won't advance out of the 4kb
+ * page cur_buf started in. It'll wrap around to the start
+ * of the page... annoying or useful? you decide.
+ *
+ * We should make sure dev->data doesn't cross a page...
+ */
+
+ /* FIXME: this just guarantees that its the end of the list */
+ td->next_td = 0;
+
+ /* Linus did this. see asm/system.h; scary concept... I don't
+ * know if its needed here or not but it won't hurt. */
+ wmb();
+
+ /*
+ * Put the TD onto our ED
+ */
+ {
+ unsigned long flags;
+ spin_lock_irqsave(&ohci_edtd_lock, flags);
+ ohci_add_td_to_ed(td, interrupt_ed);
+ spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+ }
+
+#if 0
+ /* Assimilate the new ED into the collective */
+ /*
+ * When dynamic ED allocation is done, this call will be
+ * useful. For now, the correct ED already on the
+ * controller's proper periodic ED lists was chosen above.
+ */
+ ohci_add_periodic_ed(dev->ohci, interrupt_ed, period);
+#else
+ /* enable periodic (interrupt) transfers on the HC */
+ ohci_start_periodic(dev->ohci);
+#endif
+
+ return 0;
+} /* ohci_request_irq() */
+
+
+/*
+ * Control thread operations:
+ */
+static struct wait_queue *control_wakeup;
+
+/*
+ * This is the handler that gets called when a control transaction
+ * completes.
+ *
+ * This function is called from the interrupt handler.
+ */
+static int ohci_control_completed(int stats, void *buffer, void *dev_id)
+{
+ wake_up(&control_wakeup);
+ return 0;
+} /* ohci_control_completed() */
+
+
+/*
+ * Send or receive a control message on a "pipe"
+ *
+ * The cmd parameter is a pointer to the 8 byte setup command to be
+ * sent. FIXME: This is a devrequest in usb.h. The function
+ * should be updated to accept a devrequest* instead of void*..
+ *
+ * A control message contains:
+ * - The command itself
+ * - An optional data phase (if len > 0)
+ * - Status complete phase
+ */
+static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, void *cmd, void *data, int len)
+{
+ struct ohci_device *dev = usb_to_ohci(usb);
+ /*
+ * ideally dev->ed should be linked into the root hub's
+ * control_ed list and used instead of just using it directly.
+ * This could present a problem as is with more than one
+ * device. (but who wants to use a keyboard AND a mouse
+ * anyways? ;)
+ */
+ struct ohci_ed *control_ed = &dev->ohci->root_hub->ed[ED_CONTROL];
+ struct ohci_td *setup_td, *data_td, *status_td;
+ struct wait_queue wait = { current, NULL };
+
+#if 0
+ printk(KERN_DEBUG "entering ohci_control_msg %p (ohci_dev: %p) pipe 0x%x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len);
+#endif
+
+ /*
+ * Set the max packet size, device speed, endpoint number, usb
+ * device number (function address), and type of TD.
+ *
+ */
+ control_ed->status = \
+ ed_set_maxpacket(usb_maxpacket(pipe)) |
+ ed_set_speed(usb_pipeslow(pipe)) |
+ usb_pipe_endpdev(pipe) |
+ OHCI_ED_F_NORM;
+
+ /*
+ * Build the control TD
+ */
+
+ /* get a TD to send this control message with */
+ setup_td = ohci_get_free_td(dev);
+ /* TODO check for NULL */
+
+ /*
+ * Set the not accessed condition code, allow odd sized data,
+ * and set the data transfer type to SETUP. Setup DATA always
+ * uses a DATA0 packet.
+ *
+ * The setup packet contains a devrequest (usb.h) which
+ * will always be 8 bytes long. FIXME: the cmd parameter
+ * should be a pointer to one of these instead of a void* !!!
+ */
+ ohci_fill_new_td(setup_td, OHCI_TD_D_SETUP, TOGGLE_DATA0,
+ OHCI_TD_IOC_OFF,
+ cmd, 8, /* cmd is always 8 bytes long */
+ NULL, NULL);
+
+ /* allocate the next TD */
+ data_td = ohci_get_free_td(dev); /* TODO check for NULL */
+
+ /* link to the next TD */
+ setup_td->next_td = virt_to_bus(data_td);
+
+ if (len > 0) {
+
+ /* build the Control DATA TD, it starts with a DATA1. */
+ ohci_fill_new_td(data_td, td_set_dir_out(usb_pipeout(pipe)),
+ TOGGLE_DATA1,
+ OHCI_TD_ROUND | OHCI_TD_IOC_OFF,
+ data, len,
+ NULL, NULL);
+
+ /*
+ * XXX we should check that the data buffer doesn't
+ * cross a 4096 byte boundary. If so, it needs to be
+ * copied into a single 4096 byte aligned area for the
+ * OHCI's TD logic to see it all, or multiple TDs need
+ * to be made for each page.
+ *
+ * It's not likely a control transfer will run into
+ * this problem.. (famous last words)
+ */
+
+ status_td = ohci_get_free_td(dev); /* TODO check for NULL */
+ data_td->next_td = virt_to_bus(status_td);
+ } else {
+ status_td = data_td; /* no data_td, use it for status */
+ }
+
+ /* The control status packet always uses a DATA1 */
+ ohci_fill_new_td(status_td,
+ td_set_dir_in(usb_pipeout(pipe) | (len == 0)),
+ TOGGLE_DATA1,
+ 0,
+ NULL, 0,
+ NULL, ohci_control_completed);
+ status_td->next_td = 0; /* end of TDs */
+
+ /*
+ * Start the control transaction..
+ */
+ current->state = TASK_UNINTERRUPTIBLE;
+ add_wait_queue(&control_wakeup, &wait);
+
+ /*
+ * Add the chain of 2-3 control TDs to the control ED's TD list
+ */
+ {
+ unsigned long flags;
+ spin_lock_irqsave(&ohci_edtd_lock, flags);
+ ohci_add_td_to_ed(setup_td, control_ed);
+ spin_unlock_irqrestore(&ohci_edtd_lock, flags);
+ }
+
+#if 0
+ /* complete transaction debugging output (before) */
+ printk(KERN_DEBUG " Control ED %lx:\n", virt_to_bus(control_ed));
+ show_ohci_ed(control_ed);
+ printk(KERN_DEBUG " Setup TD %lx:\n", virt_to_bus(setup_td));
+ show_ohci_td(setup_td);
+ if (data_td != status_td) {
+ printk(KERN_DEBUG " Data TD %lx:\n", virt_to_bus(data_td));
+ show_ohci_td(data_td);
+ }
+ printk(KERN_DEBUG " Status TD %lx:\n", virt_to_bus(status_td));
+ show_ohci_td(status_td);
+#endif
+
+ /* Give the ED to the HC */
+ ohci_add_control_ed(dev->ohci, control_ed);
+
+ /* FIXME:
+ * this should really check to see that the transaction completed.
+ */
+ schedule_timeout(HZ/10);
+
+ remove_wait_queue(&control_wakeup, &wait);
+
+#if 0
+ /* complete transaction debugging output (after) */
+ printk(KERN_DEBUG " (after) Control ED:\n");
+ show_ohci_ed(control_ed);
+ printk(KERN_DEBUG " (after) Setup TD:\n");
+ show_ohci_td(setup_td);
+ if (data_td != status_td) {
+ printk(KERN_DEBUG " (after) Data TD:\n");
+ show_ohci_td(data_td);
+ }
+ printk(KERN_DEBUG " (after) Status TD:\n");
+ show_ohci_td(status_td);
+#endif
+
+ /* clean up incase it failed */
+ /* XXX only do this if their ed pointer still points to control_ed
+ * incase they've been reclaimed and used by something else
+ * already. -greg */
+ ohci_remove_td_from_ed(setup_td, control_ed);
+ ohci_remove_td_from_ed(data_td, control_ed);
+ ohci_remove_td_from_ed(status_td, control_ed);
+
+ /* remove the control ED */
+ ohci_remove_control_ed(dev->ohci, control_ed);
+
+#if 0
+ printk(KERN_DEBUG "leaving ohci_control_msg\n");
+#endif
+
+ return ohci_td_result(dev, status_td);
+} /* ohci_control_msg() */
+
+
+/*
+ * Allocate a new USB device to be attached to an OHCI controller
+ */
+static struct usb_device *ohci_usb_allocate(struct usb_device *parent)
+{
+ struct usb_device *usb_dev;
+ struct ohci_device *dev;
+
+ /*
+ * Allocate the generic USB device
+ */
+ usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL);
+ if (!usb_dev)
+ return NULL;
+
+ memset(usb_dev, 0, sizeof(*usb_dev));
+
+ /*
+ * Allocate an OHCI device (EDs and TDs for this device)
+ */
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ kfree(usb_dev);
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ /*
+ * Link them together
+ */
+ usb_dev->hcpriv = dev;
+ dev->usb = usb_dev;
+
+ /*
+ * Link the device to its parent (hub, etc..) if any.
+ */
+ usb_dev->parent = parent;
+
+ if (parent) {
+ usb_dev->bus = parent->bus;
+ dev->ohci = usb_to_ohci(parent)->ohci;
+ }
+
+ return usb_dev;
+} /* ohci_usb_allocate() */
+
+
+/*
+ * Free a usb device.
+ *
+ * TODO This function needs to take better care of the EDs and TDs, etc.
+ */
+static int ohci_usb_deallocate(struct usb_device *usb_dev)
+{
+ kfree(usb_to_ohci(usb_dev));
+ kfree(usb_dev);
+ return 0;
+}
+
+
+/*
+ * functions for the generic USB driver
+ */
+struct usb_operations ohci_device_operations = {
+ ohci_usb_allocate,
+ ohci_usb_deallocate,
+ ohci_control_msg,
+ ohci_request_irq,
+};
+
+
+/*
+ * Reset an OHCI controller. Returns >= 0 on success.
+ *
+ * Afterwards the HC will be in the "suspend" state which prevents you
+ * from writing to some registers. Bring it to the operational state
+ * ASAP.
+ */
+static int reset_hc(struct ohci *ohci)
+{
+ int timeout = 1000; /* prevent an infinite loop */
+
+#if 0
+ printk(KERN_DEBUG "usb-ohci: resetting HC %p\n", ohci);
+#endif
+
+ writel(~0x0, &ohci->regs->intrdisable); /* Disable HC interrupts */
+ writel(1, &ohci->regs->cmdstatus); /* HC Reset */
+ writel_mask(0x3f, &ohci->regs->control); /* move to UsbReset state */
+
+ while ((readl(&ohci->regs->cmdstatus) & OHCI_CMDSTAT_HCR) != 0) {
+ if (!--timeout) {
+ printk("usb-ohci: USB HC reset timed out!\n");
+ return -1;
+ }
+ udelay(1);
+ }
+
+ printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci);
+
+ return 0;
+} /* reset_hc() */
+
+
+/*
+ * Reset and start an OHCI controller. Returns >= 0 on success.
+ */
+static int start_hc(struct ohci *ohci)
+{
+ int ret = 0;
+ int fminterval;
+
+ fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
+#if 0
+ printk(KERN_DEBUG "entering start_hc %p\n", ohci);
+#endif
+
+ if (reset_hc(ohci) < 0)
+ return -1;
+
+ /* restore registers cleared by the reset */
+ writel(virt_to_bus(ohci->root_hub->hcca), &ohci->regs->hcca);
+
+ /*
+ * XXX Should fminterval also be set here?
+ * The spec suggests 0x2edf [11,999]. (FIXME: make this a constant)
+ */
+ fminterval |= (0x2edf << 16);
+ writel(fminterval, &ohci->regs->fminterval);
+ /* Start periodic transfers at 90% of fminterval (fmremaining
+ * counts down; this will put them in the first 10% of the
+ * frame). */
+ writel((0x2edf*9)/10, &ohci->regs->periodicstart);
+
+ /*
+ * FNO (frame number overflow) could be enabled... they
+ * occur every 32768 frames (every 32-33 seconds). This is
+ * useful for debugging and as a bus heartbeat. -greg
+ */
+ /* Choose the interrupts we care about */
+ writel( OHCI_INTR_MIE | /* OHCI_INTR_RHSC | */
+ OHCI_INTR_WDH | OHCI_INTR_FNO,
+ &ohci->regs->intrenable);
+
+ /* Enter the USB Operational state & start the frames a flowing.. */
+ writel_set(OHCI_USB_OPER, &ohci->regs->control);
+
+ /* Enable control lists */
+ writel_set(OHCI_USB_IE | OHCI_USB_CLE | OHCI_USB_BLE, &ohci->regs->control);
+
+ /* Turn on power to the root hub ports (thanks Roman!) */
+ writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status );
+
+ printk("usb-ohci: host controller operational\n");
+
+ return ret;
+} /* start_hc() */
+
+
+/*
+ * Reset a root hub port
+ */
+static void ohci_reset_port(struct ohci *ohci, unsigned int port)
+{
+ int status;
+
+ /* Don't allow overflows. */
+ if (port >= MAX_ROOT_PORTS) {
+ printk("usb-ohci: bad port #%d in ohci_reset_port\n", port);
+ port = MAX_ROOT_PORTS-1;
+ }
+
+ writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); /* Reset */
+
+ /*
+ * Wait for the reset to complete.
+ */
+ wait_ms(10);
+
+ /* check port status to see that the reset completed */
+ status = readl(&ohci->regs->roothub.portstatus[port]);
+ if (status & PORT_PRS) {
+ /* reset failed, try harder? */
+ printk("usb-ohci: port %d reset failed, retrying\n", port);
+ writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]);
+ wait_ms(50);
+ }
+
+ /* TODO we might need to re-enable the port here or is that
+ * done elsewhere? */
+
+} /* ohci_reset_port */
+
+
+/*
+ * This gets called if the connect status on the root hub changes.
+ */
+static void ohci_connect_change(struct ohci * ohci, int port)
+{
+ struct usb_device *usb_dev;
+ struct ohci_device *dev;
+ /* memory I/O address of the port status register */
+ void *portaddr = &ohci->regs->roothub.portstatus[port];
+ int portstatus;
+
+ printk(KERN_DEBUG "ohci_connect_change(%p, %d)\n", ohci, port);
+
+ /*
+ * Because of the status change we have to forget
+ * everything we think we know about the device
+ * on this root hub port. It may have changed.
+ */
+ usb_disconnect(ohci->root_hub->usb->children + port);
+
+ portstatus = readl(portaddr);
+
+ /* disable the port if nothing is connected */
+ if (!(portstatus & PORT_CCS)) {
+ writel(PORT_CCS, portaddr);
+ return;
+ }
+
+ /*
+ * Allocate a device for the new thingy that's been attached
+ */
+ usb_dev = ohci_usb_allocate(ohci->root_hub->usb);
+ dev = usb_dev->hcpriv;
+
+ dev->ohci = ohci;
+
+ usb_connect(dev->usb);
+
+ /* link it into the bus's device tree */
+ ohci->root_hub->usb->children[port] = usb_dev;
+
+ wait_ms(200); /* wait for powerup; XXX is this needed? */
+ ohci_reset_port(ohci, port);
+
+ /* Get information on speed by using LSD */
+ usb_dev->slow = readl(portaddr) & PORT_LSDA ? 1 : 0;
+
+ /*
+ * Do generic USB device tree processing on the new device.
+ */
+ usb_new_device(usb_dev);
+
+} /* ohci_connect_change() */
+
+
+/*
+ * This gets called when the root hub configuration
+ * has changed. Just go through each port, seeing if
+ * there is something interesting happening.
+ */
+static void ohci_check_configuration(struct ohci *ohci)
+{
+ struct ohci_regs *regs = ohci->regs;
+ int num = 0;
+ int maxport = readl(&ohci->regs->roothub) & 0xff;
+
+#if 1
+ printk(KERN_DEBUG "entering ohci_check_configuration %p\n", ohci);
+#endif
+
+ do {
+ if (readl(&regs->roothub.portstatus[num]) & PORT_CSC) {
+ /* reset the connect status change bit */
+ writel(PORT_CSC, &regs->roothub.portstatus[num]);
+ /* check the port for a nifty device */
+ ohci_connect_change(ohci, num);
+ }
+ } while (++num < maxport);
+
+#if 0
+ printk(KERN_DEBUG "leaving ohci_check_configuration %p\n", ohci);
+#endif
+} /* ohci_check_configuration() */
+
+
+
+/*
+ * Check root hub port status and wake the control thread up if
+ * anything has changed.
+ *
+ * This function is called from the interrupt handler.
+ */
+static void ohci_root_hub_events(struct ohci *ohci)
+{
+ if (waitqueue_active(&ohci_configure)) {
+ int num = 0;
+ int maxport = ohci->root_hub->usb->maxchild;
+
+ do {
+ if (readl(&ohci->regs->roothub.portstatus[num]) &
+ PORT_CSC) {
+ if (waitqueue_active(&ohci_configure))
+ wake_up(&ohci_configure);
+ return;
+ }
+ } while (++num < maxport);
+ }
+} /* ohci_root_hub_events() */
+
+
+/*
+ * The done list is in reverse order; we need to process TDs in the
+ * order they were finished (FIFO). This function builds the FIFO
+ * list using the next_dl_td pointer.
+ *
+ * This function originally by Roman Weissgaerber (weissg@vienna.at)
+ *
+ * This function is called from the interrupt handler.
+ */
+static struct ohci_td * ohci_reverse_donelist(struct ohci * ohci)
+{
+ __u32 td_list_hc;
+ struct ohci_hcca *hcca = ohci->root_hub->hcca;
+ struct ohci_td *td_list = NULL;
+ struct ohci_td *td_rev = NULL;
+
+ td_list_hc = hcca->donehead & 0xfffffff0;
+ hcca->donehead = 0;
+
+ while(td_list_hc) {
+ td_list = (struct ohci_td *) bus_to_virt(td_list_hc);
+ td_list->next_dl_td = td_rev;
+
+ td_rev = td_list;
+ td_list_hc = td_list->next_td & 0xfffffff0;
+ }
+
+ return td_list;
+} /* ohci_reverse_donelist() */
+
+
+/*
+ * Collect this interrupt's goodies off of the list of finished TDs
+ * that the OHCI controller is kind enough to setup for us.
+ *
+ * This function is called from the interrupt handler.
+ */
+static void ohci_reap_donelist(struct ohci *ohci)
+{
+ struct ohci_td *td; /* used for walking the list */
+
+ spin_lock(&ohci_edtd_lock);
+
+ /* create the FIFO ordered donelist */
+ td = ohci_reverse_donelist(ohci);
+
+ while (td != NULL) {
+ struct ohci_td *next_td = td->next_dl_td;
+
+ /* FIXME: munge td->info into a future standard status format */
+ /* Check if TD should be re-queued */
+ if ((td->completed != NULL) &&
+ (td->completed(OHCI_TD_CC_GET(td->info), td->data, td->dev_id)))
+ {
+ /* Mark the TD as active again:
+ * Set the not accessed condition code
+ * FIXME: should this reset OHCI_TD_ERRCNT?
+ */
+ td->info |= OHCI_TD_CC_NEW;
+
+ /* point it back to the start of the data buffer */
+ td->cur_buf = virt_to_bus(td->data);
+
+ /* XXX disabled for debugging reasons right now.. */
+ /* insert it back on its ED */
+ ohci_add_td_to_ed(td, td->ed);
+ } else {
+ /* return it to the pool of free TDs */
+ ohci_free_td(td);
+ }
+
+ td = next_td;
+ }
+
+ spin_unlock(&ohci_edtd_lock);
+} /* ohci_reap_donelist() */
+
+
+#if 0
+static int in_int = 0;
+#endif
+/*
+ * Get annoyed at the controller for bothering us.
+ * This pretty much follows the OHCI v1.0a spec, section 5.3.
+ */
+static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r)
+{
+ struct ohci *ohci = __ohci;
+ struct ohci_regs *regs = ohci->regs;
+ struct ohci_hcca *hcca = ohci->root_hub->hcca;
+ __u32 status, context;
+
+#if 0
+ /* for debugging to keep IRQs from running away. */
+ if (in_int >= 2)
+ return;
+ ++in_int;
+ return;
+#endif
+
+ /* Save the status of the interrupts that are enabled */
+ status = readl(&regs->intrstatus);
+ status &= readl(&regs->intrenable);
+
+
+ /* make context = the interrupt status bits that we care about */
+ if (hcca->donehead != 0) {
+ context = OHCI_INTR_WDH; /* hcca donehead needs processing */
+ if (hcca->donehead & 1) {
+ context |= status; /* other status change to check */
+ }
+ } else {
+ context = status;
+ if (!context) {
+ /* TODO increment a useless interrupt counter here */
+ return;
+ }
+ }
+
+ /* Disable HC interrupts */
+ writel(OHCI_INTR_MIE, &regs->intrdisable);
+
+ /* Process the done list */
+ if (context & OHCI_INTR_WDH) {
+ /* See which TD's completed.. */
+ ohci_reap_donelist(ohci);
+
+ /* reset the done queue and tell the controller */
+ hcca->donehead = 0;
+ writel(OHCI_INTR_WDH, &regs->intrstatus);
+
+ context &= ~OHCI_INTR_WDH; /* mark this as checked */
+ }
+
+ /* Process any root hub status changes */
+ if (context & OHCI_INTR_RHSC) {
+ /* Wake the thread to process root hub events */
+ if (waitqueue_active(&ohci_configure))
+ wake_up(&ohci_configure);
+
+ writel(OHCI_INTR_RHSC, &regs->intrstatus);
+ /*
+ * Don't unset RHSC in context; it should be disabled.
+ * The control thread will re-enable it after it has
+ * checked the root hub status.
+ */
+ } else {
+ /* check the root hub status anyways. Some controllers
+ * might not generate the interrupt properly. (?) */
+ ohci_root_hub_events(ohci);
+ }
+
+ /* Check those "other" pesky bits */
+ if (context & (OHCI_INTR_FNO)) {
+ writel(OHCI_INTR_FNO, &regs->intrstatus);
+ context &= ~OHCI_INTR_FNO; /* mark this as checked */
+ }
+ if (context & OHCI_INTR_SO) {
+ writel(OHCI_INTR_SO, &regs->intrstatus);
+ context &= ~OHCI_INTR_SO; /* mark this as checked */
+ }
+ if (context & OHCI_INTR_RD) {
+ writel(OHCI_INTR_RD, &regs->intrstatus);
+ context &= ~OHCI_INTR_RD; /* mark this as checked */
+ }
+ if (context & OHCI_INTR_UE) {
+ /* FIXME: need to have the control thread reset the
+ * controller now and keep a count of unrecoverable
+ * errors. If there are too many, it should just shut
+ * the broken controller down entirely. */
+ writel(OHCI_INTR_UE, &regs->intrstatus);
+ context &= ~OHCI_INTR_UE; /* mark this as checked */
+ }
+ if (context & OHCI_INTR_OC) {
+ writel(OHCI_INTR_OC, &regs->intrstatus);
+ context &= ~OHCI_INTR_OC; /* mark this as checked */
+ }
+
+ /* Mask out any remaining unprocessed interrupts so we don't
+ * get any more of them. */
+ if (context & ~OHCI_INTR_MIE) {
+ writel(context, &regs->intrdisable);
+ }
+
+ /* Re-enable HC interrupts */
+ writel(OHCI_INTR_MIE, &regs->intrenable);
+
+} /* ohci_interrupt() */
+
+
+/*
+ * Allocate the resources required for running an OHCI controller.
+ * Host controller interrupts must not be running while calling this
+ * function or the penguins will get angry.
+ *
+ * The mem_base parameter must be the usable -virtual- address of the
+ * host controller's memory mapped I/O registers.
+ */
+static struct ohci *alloc_ohci(void* mem_base)
+{
+ int i;
+ struct ohci *ohci;
+ struct usb_bus *bus;
+ struct ohci_device *dev;
+ struct usb_device *usb;
+
+#if 0
+ printk(KERN_DEBUG "entering alloc_ohci %p\n", mem_base);
+#endif
+
+ ohci = kmalloc(sizeof(*ohci), GFP_KERNEL);
+ if (!ohci)
+ return NULL;
+
+ memset(ohci, 0, sizeof(*ohci));
+
+ ohci->irq = -1;
+ ohci->regs = mem_base;
+ INIT_LIST_HEAD(&ohci->interrupt_list);
+
+ bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return NULL;
+
+ memset(bus, 0, sizeof(*bus));
+
+ ohci->bus = bus;
+ bus->hcpriv = ohci;
+ bus->op = &ohci_device_operations;
+
+ /*
+ * Allocate the USB device structure and root hub.
+ *
+ * Here we allocate our own root hub and TDs as well as the
+ * OHCI host controller communications area. The HCCA is just
+ * a nice pool of memory with pointers to endpoint descriptors
+ * for the different interrupts.
+ */
+ usb = ohci_usb_allocate(NULL);
+ if (!usb)
+ return NULL;
+
+ dev = ohci->root_hub = usb_to_ohci(usb);
+
+ usb->bus = bus;
+
+ /* Initialize the root hub */
+ dev->ohci = ohci; /* link back to the controller */
+
+ /*
+ * Allocate the Host Controller Communications Area on a 256
+ * byte boundary. XXX take the easy way out and just grab a
+ * page as that's guaranteed to have a nice boundary.
+ */
+ dev->hcca = (struct ohci_hcca *) __get_free_page(GFP_KERNEL);
+
+ /* Tell the controller where the HCCA is */
+ writel(virt_to_bus(dev->hcca), &ohci->regs->hcca);
+
+#if 0
+ printk(KERN_DEBUG "usb-ohci: HCCA allocated at %p (bus %p)\n", dev->hcca, (void*)virt_to_bus(dev->hcca));
+#endif
+
+ /* Get the number of ports on the root hub */
+ usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff;
+ if (usb->maxchild > MAX_ROOT_PORTS) {
+ printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS);
+ usb->maxchild = MAX_ROOT_PORTS;
+ }
+ if (usb->maxchild < 1) {
+ printk("usb-ohci: Less than one root hub port? Impossible!\n");
+ usb->maxchild = 1;
+ }
+ printk("usb-ohci: %d root hub ports found\n", usb->maxchild);
+
+ /*
+ * Initialize the ED polling "tree" (for simplicity's sake in
+ * this driver many nodes in the tree will be identical)
+ */
+ dev->ed[ED_INT_32].next_ed = virt_to_bus(&dev->ed[ED_INT_16]);
+ dev->ed[ED_INT_16].next_ed = virt_to_bus(&dev->ed[ED_INT_8]);
+ dev->ed[ED_INT_8].next_ed = virt_to_bus(&dev->ed[ED_INT_4]);
+ dev->ed[ED_INT_4].next_ed = virt_to_bus(&dev->ed[ED_INT_2]);
+ dev->ed[ED_INT_2].next_ed = virt_to_bus(&dev->ed[ED_INT_1]);
+
+ /*
+ * Initialize the polling table to call interrupts at the
+ * intended intervals.
+ */
+ dev->hcca->int_table[0] = virt_to_bus(&dev->ed[ED_INT_32]);
+ for (i = 1; i < NUM_INTS; i++) {
+ if (i & 1)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_16]);
+ else if (i & 2)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_8]);
+ else if (i & 4)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_4]);
+ else if (i & 8)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_2]);
+ else if (i & 16)
+ dev->hcca->int_table[i] =
+ virt_to_bus(&dev->ed[ED_INT_1]);
+ }
+
+ /*
+ * Tell the controller where the control and bulk lists are
+ * The lists start out empty.
+ */
+ writel(0, &ohci->regs->ed_controlhead);
+ writel(0, &ohci->regs->ed_bulkhead);
+ /*
+ writel(virt_to_bus(&dev->ed[ED_CONTROL]), &ohci->regs->ed_controlhead);
+ writel(virt_to_bus(&dev->ed[ED_BULK]), &ohci->regs->ed_bulkhead);
+ */
+
+#if 0
+ printk(KERN_DEBUG "alloc_ohci(): controller\n");
+ show_ohci_status(ohci);
+#endif
+
+#if 0
+ printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci);
+#endif
+
+ return ohci;
+} /* alloc_ohci() */
+
+
+/*
+ * De-allocate all resoueces..
+ */
+static void release_ohci(struct ohci *ohci)
+{
+ printk(KERN_DEBUG "entering release_ohci %p\n", ohci);
+
+#ifdef OHCI_TIMER
+ /* stop our timer */
+ del_timer(&ohci_timer);
+#endif
+ if (ohci->irq >= 0) {
+ free_irq(ohci->irq, ohci);
+ ohci->irq = -1;
+ }
+
+ /* stop all OHCI interrupts */
+ writel(~0x0, &ohci->regs->intrdisable);
+
+ if (ohci->root_hub) {
+ /* ensure that HC is stopped before releasing the HCCA */
+ writel(OHCI_USB_SUSPEND, &ohci->regs->control);
+ free_page((unsigned long) ohci->root_hub->hcca);
+ kfree(ohci->root_hub);
+ ohci->root_hub->hcca = NULL;
+ ohci->root_hub = NULL;
+ }
+
+ /* unmap the IO address space */
+ iounmap(ohci->regs);
+
+ kfree(ohci);
+
+ MOD_DEC_USE_COUNT;
+
+ /* If the ohci itself were dynamic we'd free it here */
+
+ printk(KERN_DEBUG "usb-ohci: HC resources released.\n");
+} /* release_ohci() */
+
+
+/*
+ * USB OHCI control thread
+ */
+static int ohci_control_thread(void * __ohci)
+{
+ struct ohci *ohci = (struct ohci *)__ohci;
+
+ /*
+ * I'm unfamiliar with the SMP kernel locking.. where should
+ * this be released and what does it do? -greg
+ */
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all of our resources..
+ */
+ printk("ohci_control_thread code at %p\n", &ohci_control_thread);
+ exit_mm(current);
+ exit_files(current);
+ exit_fs(current);
+
+ strcpy(current->comm, "ohci-control");
+
+ /*
+ * Damn the torpedoes, full speed ahead
+ */
+ if (start_hc(ohci) < 0) {
+ printk("usb-ohci: failed to start the controller\n");
+ release_ohci(ohci);
+ printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci);
+ return 0;
+ }
+
+ for(;;) {
+ siginfo_t info;
+ int unsigned long signr;
+
+ wait_ms(200);
+
+ /* check the root hub configuration for changes. */
+ ohci_check_configuration(ohci);
+
+ /* re-enable root hub status change interrupts. */
+#if 0
+ writel(OHCI_INTR_RHSC, &ohci->regs->intrenable);
+#endif
+
+ printk(KERN_DEBUG "ohci-control thread sleeping\n");
+ interruptible_sleep_on(&ohci_configure);
+#ifdef CONFIG_APM
+ if (apm_resume) {
+ apm_resume = 0;
+ if (start_hc(ohci) < 0)
+ break;
+ continue;
+ }
+#endif
+
+ /*
+ * If we were woken up by a signal, see if its useful,
+ * otherwise exit.
+ */
+ if (signal_pending(current)) {
+ /* sending SIGUSR1 makes us print out some info */
+ spin_lock_irq(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if(signr == SIGUSR1) {
+ /* FIXME: have it do a full ed/td queue dump */
+ printk(KERN_DEBUG "OHCI status dump:\n");
+ show_ohci_status(ohci);
+ } else {
+ /* unknown signal, exit the thread */
+ break;
+ }
+ }
+ } /* for (;;) */
+
+ reset_hc(ohci);
+ release_ohci(ohci);
+
+ printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci);
+
+ return 0;
+} /* ohci_control_thread() */
+
+
+#ifdef CONFIG_APM
+static int handle_apm_event(apm_event_t event)
+{
+ static int down = 0;
+
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ if (down) {
+ printk(KERN_DEBUG "usb-ohci: received extra suspend event\n");
+ break;
+ }
+ down = 1;
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ if (!down) {
+ printk(KERN_DEBUG "usb-ohci: received bogus resume event\n");
+ break;
+ }
+ down = 0;
+ if (waitqueue_active(&ohci_configure)) {
+ apm_resume = 1;
+ wake_up(&ohci_configure);
+ }
+ break;
+ }
+ return 0;
+} /* handle_apm_event() */
+#endif
+
+
+#ifdef OHCI_TIMER
+/*
+ * Inspired by Iñaky's driver. This function is a timer routine that
+ * is called OHCI_TIMER_FREQ times per second. It polls the root hub
+ * for status changes as on my system things are acting a bit odd at
+ * the moment..
+ */
+static void ohci_timer_func (unsigned long ohci_ptr)
+{
+ struct ohci *ohci = (struct ohci*)ohci_ptr;
+
+ ohci_root_hub_events(ohci);
+
+ /* press the snooze button... */
+ mod_timer(&ohci_timer, jiffies + (OHCI_TIMER_FREQ*HZ));
+} /* ohci_timer_func() */
+#endif
+
+
+/*
+ * Increment the module usage count, start the control thread and
+ * return success if the controller is good.
+ */
+static int found_ohci(int irq, void* mem_base)
+{
+ int retval;
+ struct ohci *ohci;
+
+#if 0
+ printk(KERN_DEBUG "entering found_ohci %d %p\n", irq, mem_base);
+#endif
+
+ /* Allocate the running OHCI structures */
+ ohci = alloc_ohci(mem_base);
+ if (!ohci) {
+ return -ENOMEM;
+ }
+
+#ifdef OHCI_TIMER
+ init_timer(&ohci_timer);
+ ohci_timer.expires = jiffies + (OHCI_TIMER_FREQ*HZ);
+ ohci_timer.data = (unsigned long)ohci;
+ ohci_timer.function = ohci_timer_func;
+#endif
+
+ retval = -EBUSY;
+ if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) {
+ int pid;
+
+ ohci->irq = irq;
+
+#if 0
+ printk(KERN_DEBUG "usb-ohci: starting ohci-control thread\n");
+#endif
+
+ /* fork off the handler */
+ pid = kernel_thread(ohci_control_thread, ohci,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (pid >= 0) {
+ return 0;
+ }
+
+ retval = pid;
+ } else {
+ printk("usb-ohci: Couldn't allocate interrupt %d\n", irq);
+ }
+ release_ohci(ohci);
+
+#if 0
+ printk(KERN_DEBUG "leaving found_ohci %d %p\n", irq, mem_base);
+#endif
+
+ return retval;
+} /* found_ohci() */
+
+
+/*
+ * If this controller is for real, map the IO memory and proceed
+ */
+static int init_ohci(struct pci_dev *dev)
+{
+ unsigned long mem_base = dev->base_address[0];
+
+ /* If its OHCI, its memory */
+ if (mem_base & PCI_BASE_ADDRESS_SPACE_IO)
+ return -ENODEV;
+
+ /* Get the memory address and map it for IO */
+ mem_base &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ /* no interrupt won't work... */
+ if (dev->irq == 0) {
+ printk("usb-ohci: no irq assigned? check your BIOS settings.\n");
+ return -ENODEV;
+ }
+
+ /*
+ * FIXME ioremap_nocache isn't implemented on all CPUs (such
+ * as the Alpha) [?] What should I use instead...
+ *
+ * The iounmap() is done on in release_ohci.
+ */
+ mem_base = (unsigned long) ioremap_nocache(mem_base, 4096);
+
+ if (!mem_base) {
+ printk("Error mapping OHCI memory\n");
+ return -EFAULT;
+ }
+ MOD_INC_USE_COUNT;
+
+ if (found_ohci(dev->irq, (void *) mem_base) < 0) {
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ return 0;
+} /* init_ohci() */
+
+#ifdef MODULE
+/*
+ * Clean up when unloading the module
+ */
+void cleanup_module(void)
+{
+#ifdef CONFIG_APM
+ apm_unregister_callback(&handle_apm_event);
+#endif
+#ifdef CONFIG_USB_MOUSE
+ usb_mouse_cleanup();
+#endif
+ printk("usb-ohci: module unloaded\n");
+}
+
+#define ohci_init init_module
+
+#endif
+
+
+/* TODO this should be named following Linux convention and go in pci.h */
+#define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010)
+
+/*
+ * Search the PCI bus for an OHCI USB controller and set it up
+ *
+ * If anyone wants multiple controllers this will need to be
+ * updated.. Right now, it just picks the first one it finds.
+ */
+int ohci_init(void)
+{
+ int retval;
+ struct pci_dev *dev = NULL;
+ /*u8 type;*/
+
+ if (sizeof(struct ohci_device) > 4096) {
+ printk("usb-ohci: struct ohci_device to large\n");
+ return -ENODEV;
+ }
+
+ printk("OHCI USB Driver loading\n");
+
+ retval = -ENODEV;
+ for (;;) {
+ /* Find an OHCI USB controller */
+ dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev);
+ if (!dev)
+ break;
+
+ /* Verify that its OpenHCI by checking for MMIO */
+ /* pci_read_config_byte(dev, PCI_CLASS_PROG, &type);
+ if (!type)
+ continue; */
+
+ /* Ok, set it up */
+ retval = init_ohci(dev);
+ if (retval < 0)
+ continue;
+
+ /* TODO check module params here to determine what to load */
+
+#ifdef CONFIG_USB_MOUSE
+ usb_mouse_init();
+#endif
+#ifdef CONFIG_USB_KBD
+ usb_kbd_init();
+#endif
+ hub_init();
+#ifdef CONFIG_USB_AUDIO
+ usb_audio_init();
+#endif
+#ifdef CONFIG_APM
+ apm_register_callback(&handle_apm_event);
+#endif
+
+ return 0; /* no error */
+ }
+ return retval;
+} /* ohci_init */
+
+/* vim:sw=8
+ */
diff --git a/drivers/usb/ohci.h b/drivers/usb/ohci.h
new file mode 100644
index 000000000..8714cd2e8
--- /dev/null
+++ b/drivers/usb/ohci.h
@@ -0,0 +1,349 @@
+#ifndef __LINUX_OHCI_H
+#define __LINUX_OHCI_H
+
+/*
+ * Open Host Controller Interface data structures and defines.
+ *
+ * (C) Copyright 1999 Gregory P. Smith <greg@electricrain.com>
+ *
+ * $Id: ohci.h,v 1.15 1999/05/09 23:25:49 greg Exp $
+ */
+
+#include <linux/list.h>
+#include <asm/io.h>
+
+#include "usb.h"
+
+struct ohci_ed;
+
+/*
+ * Each TD must be aligned on a 16-byte boundary. From the OHCI v1.0 spec
+ * it does not state that TDs must be contiguious in memory (due to the
+ * use of the next_td field). This gives us extra room at the end of a
+ * TD for our own driver specific data.
+ *
+ * This structure's size must be a multiple of 16 bytes. ?? no way, I
+ * don't see why. Alignment should be all that matters.
+ */
+struct ohci_td {
+ /* OHCI Hardware fields */
+ __u32 info; /* TD status & type flags */
+ __u32 cur_buf; /* Current Buffer Pointer (bus address) */
+ __u32 next_td; /* Next TD Pointer (bus address) */
+ __u32 buf_end; /* Memory Buffer End Pointer (bus address) */
+
+ /* Driver specific fields */
+ struct ohci_ed *ed; /* address of the ED this TD is on */
+ struct ohci_td *next_dl_td; /* used during donelist processing */
+ void *data; /* virt. address of the the buffer */
+ usb_device_irq completed; /* Completion handler routine */
+ int allocated; /* boolean: is this TD allocated? */
+
+ /* User or Device class driver specific fields */
+ void *dev_id; /* user defined pointer passed to irq handler */
+} __attribute((aligned(16)));
+
+#define OHCI_TD_ROUND (1 << 18) /* buffer rounding bit */
+#define OHCI_TD_D (3 << 19) /* direction of xfer: */
+#define OHCI_TD_D_IN (2 << 19)
+#define OHCI_TD_D_OUT (1 << 19)
+#define OHCI_TD_D_SETUP (0 << 19)
+#define td_set_dir_in(d) ((d) ? OHCI_TD_D_IN : OHCI_TD_D_OUT )
+#define td_set_dir_out(d) ((d) ? OHCI_TD_D_OUT : OHCI_TD_D_IN )
+#define OHCI_TD_IOC_DELAY (7 << 21) /* frame delay allowed before int. */
+#define OHCI_TD_IOC_OFF (OHCI_TD_IOC_DELAY) /* no interrupt on complete */
+#define OHCI_TD_DT (3 << 24) /* data toggle bits */
+#define TOGGLE_AUTO (0 << 24) /* automatic (from the ED) */
+#define TOGGLE_DATA0 (2 << 24) /* force Data0 */
+#define TOGGLE_DATA1 (3 << 24) /* force Data1 */
+#define td_force_toggle(b) (((b) | 2) << 24)
+#define OHCI_TD_ERRCNT (3 << 26) /* error count */
+#define td_errorcount(td) (((td).info >> 26) & 3)
+#define OHCI_TD_CC (0xf << 28) /* condition code */
+#define OHCI_TD_CC_GET(td_i) (((td_i) >> 28) & 0xf)
+#define OHCI_TD_CC_NEW (OHCI_TD_CC) /* set this on all unaccessed TDs! */
+#define td_cc_notaccessed(td) (((td).info >> 29) == 7)
+#define td_cc_accessed(td) (((td).info >> 29) != 7)
+#define td_cc_noerror(td) ((((td).info) & OHCI_TD_CC) == 0)
+#define td_active(td) (!td_cc_noerror((td)) && (td_errorcount((td)) < 3))
+#define td_done(td) (td_cc_noerror((td)) || (td_errorcount((td)) == 3))
+
+#define td_allocated(td) ((td).allocated)
+#define allocate_td(td) ((td)->allocated = 1)
+#define ohci_free_td(td) ((td)->allocated = 0)
+
+
+/*
+ * The endpoint descriptors also requires 16-byte alignment
+ */
+struct ohci_ed {
+ /* OHCI hardware fields */
+ __u32 status;
+ __u32 tail_td; /* TD Queue tail pointer */
+ __u32 _head_td; /* TD Queue head pointer, toggle carry & halted bits */
+ __u32 next_ed; /* Next ED */
+} __attribute((aligned(16)));
+
+/* get the head_td */
+#define ed_head_td(ed) ((ed)->_head_td & 0xfffffff0)
+
+/* save the carry flag while setting the head_td */
+#define set_ed_head_td(ed, td) ((ed)->_head_td = (td) | ((ed)->_head_td & 3))
+
+#define OHCI_ED_SKIP (1 << 14)
+#define OHCI_ED_MPS (0x7ff << 16)
+/* FIXME: should cap at the USB max packet size [0x4ff] */
+#define ed_set_maxpacket(s) (((s) << 16) & OHCI_ED_MPS)
+#define OHCI_ED_F_NORM (0)
+#define OHCI_ED_F_ISOC (1 << 15)
+#define ed_set_type_isoc(i) ((i) ? OHCI_ED_F_ISOC : OHCI_ED_F_NORM)
+#define OHCI_ED_S_LOW (1 << 13)
+#define OHCI_ED_S_HIGH (0)
+#define ed_set_speed(s) ((s) ? OHCI_ED_S_LOW : OHCI_ED_S_HIGH)
+#define OHCI_ED_D (3 << 11)
+#define OHCI_ED_D_IN (2 << 11)
+#define OHCI_ED_D_OUT (1 << 11)
+#define ed_set_dir_in(d) ((d) ? OHCI_ED_D_IN : OHCI_ED_D_OUT)
+#define ed_set_dir_out(d) ((d) ? OHCI_ED_D_OUT : OHCI_ED_D_IN)
+#define OHCI_ED_EN (0xf << 7)
+#define OHCI_ED_FA (0x7f)
+
+
+/* NOTE: bits 27-31 of the status dword are reserved for the driver */
+/*
+ * We'll use this status flag for to mark if an ED is in use by the
+ * driver or not. If the bit is set, it is used.
+ *
+ * FIXME: implement this!
+ */
+#define ED_USED (1 << 31)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of. It must be 256-byte aligned.
+ */
+#define NUM_INTS 32 /* part of the OHCI standard */
+struct ohci_hcca {
+ __u32 int_table[NUM_INTS]; /* Interrupt ED table */
+ __u16 frame_no; /* current frame number */
+ __u16 pad1; /* set to 0 on each frame_no change */
+ __u32 donehead; /* info returned for an interrupt */
+ u8 reserved_for_hc[116];
+} __attribute((aligned(256)));
+
+/*
+ * The TD entries here are pre-allocated as Linus did with his simple
+ * UHCI implementation. With the current state of this driver that
+ * shouldn't be a problem. However if someone ever connects 127
+ * supported devices to this driver and tries to use them all at once:
+ * a) They're insane!
+ * b) They should code in dynamic allocation
+ */
+struct ohci;
+
+/*
+ * Warning: These constants must not be so large as to cause the
+ * ohci_device structure to exceed one 4096 byte page. Or "weird
+ * things will happen" as the alloc_ohci() function assumes that
+ * its less than one page. (FIXME)
+ */
+#define NUM_TDS 32 /* num of preallocated transfer descriptors */
+#define DATA_BUF_LEN 16 /* num of unsigned long's for the data buf */
+
+/*
+ * For this "simple" driver we only support a single ED for each
+ * polling rate.
+ *
+ * Later on this driver should be extended to use a full tree of EDs
+ * so that there can be 32 different 32ms polling frames, etc.
+ * Individual devices shouldn't need as many as the root hub in that
+ * case; complicating how things are done at the moment.
+ *
+ * Bulk and Control transfers hang off of their own ED lists.
+ */
+#define NUM_EDS 16 /* num of preallocated endpoint descriptors */
+
+#define ohci_to_usb(ohci) ((ohci)->usb)
+#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv)
+
+/* The usb_device must be first! */
+struct ohci_device {
+ struct usb_device *usb;
+
+ struct ohci *ohci;
+ struct ohci_hcca *hcca; /* OHCI mem. mapped IO area */
+
+ struct ohci_ed ed[NUM_EDS]; /* Endpoint Descriptors */
+ struct ohci_td td[NUM_TDS]; /* Transfer Descriptors */
+
+ unsigned long data[DATA_BUF_LEN];
+};
+
+/* .... */
+
+#define ED_INT_1 0
+#define ED_INT_2 1
+#define ED_INT_4 2
+#define ED_INT_8 3
+#define ED_INT_16 4
+#define ED_INT_32 5
+#define ED_CONTROL 6
+#define ED_BULK 7
+#define ED_ISO ED_INT_1 /* same as 1ms interrupt queue */
+#define ED_FIRST_AVAIL 8 /* first non-reserved ED */
+
+/*
+ * Given a period p in ms, convert it to the closest endpoint
+ * interrupt frequency; rounding down. This is a gross macro.
+ * Feel free to toss it for actual code. (gasp!)
+ */
+#define ms_to_ed_int(p) \
+ ((p >= 32) ? ED_INT_32 : \
+ ((p & 16) ? ED_INT_16 : \
+ ((p & 8) ? ED_INT_8 : \
+ ((p & 4) ? ED_INT_4 : \
+ ((p & 2) ? ED_INT_2 : \
+ ED_INT_1))))) /* hmm.. scheme or lisp anyone? */
+
+/*
+ * This is the maximum number of root hub ports. I don't think we'll
+ * ever see more than two as that's the space available on an ATX
+ * motherboard's case, but it could happen. The OHCI spec allows for
+ * up to 15... (which is insane!)
+ *
+ * Although I suppose several "ports" could be connected directly to
+ * internal laptop devices such as a keyboard, mouse, camera and
+ * serial/parallel ports. hmm... That'd be neat.
+ */
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region. This is Memory Mapped I/O. You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+ /* control and status registers */
+ __u32 revision;
+ __u32 control;
+ __u32 cmdstatus;
+ __u32 intrstatus;
+ __u32 intrenable;
+ __u32 intrdisable;
+ /* memory pointers */
+ __u32 hcca;
+ __u32 ed_periodcurrent;
+ __u32 ed_controlhead;
+ __u32 ed_controlcurrent;
+ __u32 ed_bulkhead;
+ __u32 ed_bulkcurrent;
+ __u32 current_donehead; /* The driver should get this from the HCCA */
+ /* frame counters */
+ __u32 fminterval;
+ __u32 fmremaining;
+ __u32 fmnumber;
+ __u32 periodicstart;
+ __u32 lsthresh;
+ /* Root hub ports */
+ struct ohci_roothub_regs {
+ __u32 a;
+ __u32 b;
+ __u32 status;
+ __u32 portstatus[MAX_ROOT_PORTS];
+ } roothub;
+} __attribute((aligned(32)));
+
+/*
+ * Read a MMIO register and re-write it after ANDing with (m)
+ */
+#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) )
+
+/*
+ * Read a MMIO register and re-write it after ORing with (b)
+ */
+#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) )
+
+
+#define PORT_CCS (1) /* port current connect status */
+#define PORT_PES (1 << 1) /* port enable status */
+#define PORT_PSS (1 << 2) /* port suspend status */
+#define PORT_POCI (1 << 3) /* port overcurrent indicator */
+#define PORT_PRS (1 << 4) /* port reset status */
+#define PORT_PPS (1 << 8) /* port power status */
+#define PORT_LSDA (1 << 9) /* port low speed dev. attached */
+#define PORT_CSC (1 << 16) /* port connect status change */
+#define PORT_PESC (1 << 17) /* port enable status change */
+#define PORT_PSSC (1 << 18) /* port suspend status change */
+#define PORT_OCIC (1 << 19) /* port over current indicator chg */
+#define PORT_PRSC (1 << 20) /* port reset status change */
+
+/*
+ * Root Hub status register masks
+ */
+#define OHCI_ROOT_LPS (1) /* turn off root hub ports power */
+#define OHCI_ROOT_OCI (1 << 1) /* Overcurrent Indicator */
+#define OHCI_ROOT_DRWE (1 << 15) /* Device remote wakeup enable */
+#define OHCI_ROOT_LPSC (1 << 16) /* turn on root hub ports power */
+#define OHCI_ROOT_OCIC (1 << 17) /* Overcurrent indicator change */
+#define OHCI_ROOT_CRWE (1 << 31) /* Clear RemoteWakeupEnable */
+
+/*
+ * Interrupt register masks
+ */
+#define OHCI_INTR_SO (1)
+#define OHCI_INTR_WDH (1 << 1)
+#define OHCI_INTR_SF (1 << 2)
+#define OHCI_INTR_RD (1 << 3)
+#define OHCI_INTR_UE (1 << 4)
+#define OHCI_INTR_FNO (1 << 5)
+#define OHCI_INTR_RHSC (1 << 6)
+#define OHCI_INTR_OC (1 << 30)
+#define OHCI_INTR_MIE (1 << 31)
+
+/*
+ * Control register masks
+ */
+#define OHCI_USB_OPER (2 << 6)
+#define OHCI_USB_SUSPEND (3 << 6)
+#define OHCI_USB_PLE (1 << 2) /* Periodic (interrupt) list enable */
+#define OHCI_USB_IE (1 << 3) /* Isochronous list enable */
+#define OHCI_USB_CLE (1 << 4) /* Control list enable */
+#define OHCI_USB_BLE (1 << 5) /* Bulk list enable */
+
+/*
+ * Command status register masks
+ */
+#define OHCI_CMDSTAT_HCR (1)
+#define OHCI_CMDSTAT_CLF (1 << 1)
+#define OHCI_CMDSTAT_BLF (1 << 2)
+#define OHCI_CMDSTAT_OCR (1 << 3)
+#define OHCI_CMDSTAT_SOC (3 << 16)
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+struct ohci {
+ int irq;
+ struct ohci_regs *regs; /* OHCI controller's memory */
+ struct usb_bus *bus;
+ struct ohci_device *root_hub; /* Root hub & controller */
+ struct list_head interrupt_list; /* List of interrupt active TDs for this OHCI */
+};
+
+#define OHCI_TIMER
+#define OHCI_TIMER_FREQ (1) /* frequency of OHCI status checks */
+
+/* Debugging code */
+void show_ohci_ed(struct ohci_ed *ed);
+void show_ohci_td(struct ohci_td *td);
+void show_ohci_status(struct ohci *ohci);
+void show_ohci_device(struct ohci_device *dev);
+void show_ohci_hcca(struct ohci_hcca *hcca);
+
+#endif
+/* vim:sw=8
+ */
diff --git a/drivers/usb/restart b/drivers/usb/restart
new file mode 100644
index 000000000..98ca05367
--- /dev/null
+++ b/drivers/usb/restart
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+ME=`basename $0`
+
+#UMOD=`lsmod | grep '^bp-mouse' | grep -v grep`
+#if test "$UMOD"; then
+# echo "$ME: removing bp-mouse.o"
+# if ! rmmod bp-mouse; then
+# echo "$ME: cannot remove bp-mouse.o"
+# exit 1
+# fi
+#fi
+
+UPID=`ps aux | grep ohci-control | grep -v grep | awk '{print $2}'`
+if test "$UPID"; then
+ echo "$ME: killing $UPID"
+ kill $UPID
+fi
+
+UMOD=`lsmod | grep '^usb-ohci' | grep -v grep`
+if test "$UMOD"; then
+ echo "$ME: removing usb-ohci.o"
+ sleep 1
+ if ! rmmod usb-ohci; then
+ echo "$ME: cannot remove usb-ohci.o"
+ exit 1
+ fi
+fi
+
+dmesg -c > /dev/null
+
+echo "$ME: starting usb-ohci.o"
+insmod -m usb-ohci.o > usb-ohci.map
+
+sleep 1
+UPID=`ps aux | grep ohci-control | grep -v grep | awk '{print $2}'`
+if test "$UPID"; then echo "$ME: ohci-control is pid $UPID" ; fi
+
diff --git a/drivers/usb/stopusb b/drivers/usb/stopusb
new file mode 100644
index 000000000..1dc46980b
--- /dev/null
+++ b/drivers/usb/stopusb
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+killall ohci-control
+
+sleep 2
+
+rmmod usb-ohci
diff --git a/drivers/usb/uhci-debug.c b/drivers/usb/uhci-debug.c
new file mode 100644
index 000000000..fd2aba6da
--- /dev/null
+++ b/drivers/usb/uhci-debug.c
@@ -0,0 +1,168 @@
+/*
+ * UHCI-specific debugging code. Invaluable when something
+ * goes wrong, but don't get in my face.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ */
+
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include "uhci.h"
+
+void show_td(struct uhci_td * td)
+{
+ printk("%08x ", td->link);
+ printk("%se%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
+ ((td->status >> 29) & 1) ? "SPD " : "",
+ ((td->status >> 27) & 3),
+ ((td->status >> 26) & 1) ? "LS " : "",
+ ((td->status >> 25) & 1) ? "IOS " : "",
+ ((td->status >> 24) & 1) ? "IOC " : "",
+ ((td->status >> 23) & 1) ? "Active " : "",
+ ((td->status >> 22) & 1) ? "Stalled " : "",
+ ((td->status >> 21) & 1) ? "DataBufErr " : "",
+ ((td->status >> 20) & 1) ? "Babble " : "",
+ ((td->status >> 19) & 1) ? "NAK " : "",
+ ((td->status >> 18) & 1) ? "CRC/Timeo " : "",
+ ((td->status >> 17) & 1) ? "BitStuff " : "",
+ td->status & 0x7ff);
+ printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x ",
+ td->info >> 21,
+ ((td->info >> 19) & 1) ? "DT " : "",
+ (td->info >> 15) & 15,
+ (td->info >> 8) & 127,
+ td->info & 0xff);
+ printk("(buf=%08x)\n", td->buffer);
+}
+
+static void show_sc(int port, unsigned short status)
+{
+ printk(" stat%d = %04x %s%s%s%s%s%s%s%s\n",
+ port,
+ status,
+ (status & (1 << 12)) ? " PortSuspend" : "",
+ (status & (1 << 9)) ? " PortReset" : "",
+ (status & (1 << 8)) ? " LowSpeed" : "",
+ (status & 0x40) ? " ResumeDetect" : "",
+ (status & 0x08) ? " EnableChange" : "",
+ (status & 0x04) ? " PortEnabled" : "",
+ (status & 0x02) ? " ConnectChange" : "",
+ (status & 0x01) ? " PortConnected" : "");
+}
+
+void show_status(struct uhci *uhci)
+{
+ unsigned int io_addr = uhci->io_addr;
+ unsigned short usbcmd, usbstat, usbint, usbfrnum;
+ unsigned int flbaseadd;
+ unsigned char sof;
+ unsigned short portsc1, portsc2;
+
+ usbcmd = inw(io_addr + 0);
+ usbstat = inw(io_addr + 2);
+ usbint = inw(io_addr + 4);
+ usbfrnum = inw(io_addr + 6);
+ flbaseadd = inl(io_addr + 8);
+ sof = inb(io_addr + 12);
+ portsc1 = inw(io_addr + 16);
+ portsc2 = inw(io_addr + 18);
+
+ printk(" usbcmd = %04x %s%s%s%s%s%s%s%s\n",
+ usbcmd,
+ (usbcmd & 0x80) ? " Maxp64" : " Maxp32",
+ (usbcmd & 0x40) ? " CF" : "",
+ (usbcmd & 0x20) ? " SWDBG" : "",
+ (usbcmd & 0x10) ? " FGR" : "",
+ (usbcmd & 0x08) ? " EGSM" : "",
+ (usbcmd & 0x04) ? " GRESET" : "",
+ (usbcmd & 0x02) ? " HCRESET" : "",
+ (usbcmd & 0x01) ? " RS" : "");
+
+ printk(" usbstat = %04x %s%s%s%s%s%s\n",
+ usbstat,
+ (usbstat & 0x20) ? " HCHalted" : "",
+ (usbstat & 0x10) ? " HostControllerProcessError" : "",
+ (usbstat & 0x08) ? " HostSystemError" : "",
+ (usbstat & 0x04) ? " ResumeDetect" : "",
+ (usbstat & 0x02) ? " USBError" : "",
+ (usbstat & 0x01) ? " USBINT" : "");
+
+ printk(" usbint = %04x\n", usbint);
+ printk(" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, 0xfff & (4*(unsigned int)usbfrnum));
+ printk(" flbaseadd = %08x\n", flbaseadd);
+ printk(" sof = %02x\n", sof);
+ show_sc(1, portsc1);
+ show_sc(2, portsc2);
+}
+
+#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x))
+
+struct uhci_td * uhci_link_to_td(unsigned int link)
+{
+ if (link & 1)
+ return NULL;
+
+ return bus_to_virt(link & ~15);
+}
+
+void show_queue(struct uhci_qh *qh)
+{
+ struct uhci_td *td;
+ int i = 0;
+
+#if 0
+ printk(" link = %p, element = %p\n", qh->link, qh->element);
+#endif
+ if(!qh->element) {
+ printk(" td 0 = NULL\n");
+ return;
+ }
+
+ for(td = uhci_link_to_td(qh->element); td;
+ td = uhci_link_to_td(td->link)) {
+ printk(" td %d = %p\n", i++, td);
+ printk(" ");
+ show_td(td);
+ }
+}
+
+int is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh)
+{
+ int j;
+
+ for (j = 0; j < UHCI_MAXQH; j++)
+ if (qh == uhci->root_hub->qh + j)
+ return 1;
+
+ return 0;
+}
+
+static const char *qh_names[] = {"isochronous", "interrupt2", "interrupt4",
+ "interrupt8", "interrupt16", "interrupt32",
+ "interrupt64", "interrupt128", "interrupt256",
+ "control", "bulk0", "bulk1", "bulk2", "bulk3",
+ "unused", "unused"};
+
+void show_queues(struct uhci *uhci)
+{
+ int i;
+ struct uhci_qh *qh;
+
+ for (i = 0; i < UHCI_MAXQH; ++i) {
+ printk(" %s:\n", qh_names[i]);
+#if 0
+ printk(" qh #%d, %p\n", i, virt_to_bus(uhci->root_hub->qh + i));
+ show_queue(uhci->root_hub->qh + i);
+#endif
+
+ qh = uhci_link_to_qh(uhci->root_hub->qh[i].link);
+ for (; qh; qh = uhci_link_to_qh(qh->link)) {
+ if (is_skeleton_qh(uhci, qh))
+ break;
+
+ show_queue(qh);
+ }
+ }
+}
+
diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c
new file mode 100644
index 000000000..3d8ccdb7d
--- /dev/null
+++ b/drivers/usb/uhci.c
@@ -0,0 +1,1213 @@
+/*
+ * Universal Host Controller Interface driver for USB.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ *
+ * Intel documents this fairly well, and as far as I know there
+ * are no royalties or anything like that, but even so there are
+ * people who decided that they want to do the same thing in a
+ * completely different way.
+ *
+ * Oh, well. The intel version is the more common by far. As such,
+ * that's the one I care about right now.
+ *
+ * WARNING! The USB documentation is downright evil. Most of it
+ * is just crap, written by a committee. You're better off ignoring
+ * most of it, the important stuff is:
+ * - the low-level protocol (fairly simple but lots of small details)
+ * - working around the horridness of the rest
+ */
+
+/* 4/4/1999 added data toggle for interrupt pipes -keryan */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+
+#include <asm/spinlock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "uhci.h"
+#include "inits.h"
+
+#ifdef CONFIG_APM
+#include <linux/apm_bios.h>
+static int handle_apm_event(apm_event_t event);
+static int apm_resume = 0;
+#endif
+
+#define compile_assert(x) do { switch (0) { case 1: case !(x): } } while (0)
+
+static struct wait_queue *uhci_configure = NULL;
+
+/*
+ * Return the result of a TD..
+ */
+static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td)
+{
+ unsigned int status;
+
+ status = (td->status >> 16) & 0xff;
+
+ /* Some debugging code */
+ if (status) {
+ int i = 10;
+ struct uhci_td *tmp = dev->control_td;
+ printk("uhci_td_result() failed with status %d\n", status);
+ show_status(dev->uhci);
+ do {
+ show_td(tmp);
+ tmp++;
+ if (!--i)
+ break;
+ } while (tmp <= td);
+ }
+ return status;
+}
+
+/*
+ * Inserts a td into qh list at the top.
+ *
+ * Careful about atomicity: even on UP this
+ * requires a locked access due to the concurrent
+ * DMA engine.
+ *
+ * NOTE! This assumes that first->last is a valid
+ * list of TD's with the proper backpointers set
+ * up and all..
+ */
+static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *first, struct uhci_td *last)
+{
+ unsigned int link = qh->element;
+ unsigned int new = 4 | virt_to_bus(first);
+
+ for (;;) {
+ unsigned char success;
+
+ last->link = link;
+ first->backptr = &qh->element;
+ asm volatile("lock ; cmpxchg %4,%2 ; sete %0"
+ :"=q" (success), "=a" (link)
+ :"m" (qh->element), "1" (link), "r" (new)
+ :"memory");
+ if (success) {
+ /* Was there a successor entry? Fix it's backpointer.. */
+ if ((link & 1) == 0) {
+ struct uhci_td *next = bus_to_virt(link & ~15);
+ next->backptr = &last->link;
+ }
+ break;
+ }
+ }
+}
+
+static inline void uhci_insert_td_in_qh(struct uhci_qh *qh, struct uhci_td *td)
+{
+ uhci_insert_tds_in_qh(qh, td, td);
+}
+
+static void uhci_insert_qh(struct uhci_qh *qh, struct uhci_qh *newqh)
+{
+ newqh->link = qh->link;
+ qh->link = virt_to_bus(newqh) | 2;
+}
+
+static void uhci_remove_qh(struct uhci_qh *qh, struct uhci_qh *remqh)
+{
+ unsigned int remphys = virt_to_bus(remqh);
+ struct uhci_qh *lqh = qh;
+
+ while ((lqh->link & ~0xF) != remphys) {
+ if (lqh->link & 1)
+ break;
+
+ lqh = bus_to_virt(lqh->link & ~0xF);
+ }
+
+ if (lqh->link & 1) {
+ printk("couldn't find qh in chain!\n");
+ return;
+ }
+
+ lqh->link = remqh->link;
+}
+
+/*
+ * Removes td from qh if present.
+ *
+ * NOTE! We keep track of both forward and back-pointers,
+ * so this should be trivial, right?
+ *
+ * Wrong. While all TD insert/remove operations are synchronous
+ * on the CPU, the UHCI controller can (and does) play with the
+ * very first forward pointer. So we need to validate the backptr
+ * before we change it, so that we don't by mistake reset the QH
+ * head to something old.
+ */
+static void uhci_remove_td(struct uhci_td *td)
+{
+ unsigned int *backptr = td->backptr;
+ unsigned int link = td->link;
+ unsigned int me;
+
+ if (!backptr)
+ return;
+
+ td->backptr = NULL;
+
+ /*
+ * This is the easy case: the UHCI will never change "td->link",
+ * so we can always just look at that and fix up the backpointer
+ * of any next element..
+ */
+ if (!(link & 1)) {
+ struct uhci_td *next = bus_to_virt(link & ~15);
+ next->backptr = backptr;
+ }
+
+ /*
+ * The nasty case is "backptr->next", which we need to
+ * update to "link" _only_ if "backptr" still points
+ * to us (it may not: maybe backptr is a QH->element
+ * pointer and the UHCI has changed the value).
+ */
+ me = virt_to_bus(td) | (0xe & *backptr);
+ asm volatile("lock ; cmpxchg %0,%1"
+ :
+ :"r" (link), "m" (*backptr), "a" (me)
+ :"memory");
+}
+
+static struct uhci_qh *uhci_qh_allocate(struct uhci_device *dev)
+{
+ struct uhci_qh *qh;
+ int inuse;
+
+ qh = dev->qh;
+ for (; (inuse = test_and_set_bit(0, &qh->inuse)) != 0 && qh < &dev->qh[UHCI_MAXQH]; qh++)
+ ;
+
+ if (!inuse)
+ return(qh);
+
+ printk("ran out of qh's for dev %p\n", dev);
+ return(NULL);
+}
+
+static void uhci_qh_deallocate(struct uhci_qh *qh)
+{
+ if (qh->element != 1)
+ printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element);
+
+ qh->element = 1;
+ qh->link = 1;
+
+ clear_bit(0, &qh->inuse);
+}
+
+static struct uhci_td *uhci_td_allocate(struct uhci_device *dev)
+{
+ struct uhci_td *td;
+ int inuse;
+
+ td = dev->td;
+ for (; (inuse = test_and_set_bit(0, &td->inuse)) != 0 && td < &dev->td[UHCI_MAXTD]; td++)
+ ;
+
+ if (!inuse)
+ return(td);
+
+ printk("ran out of td's for dev %p\n", dev);
+ return(NULL);
+}
+
+/*
+ * This MUST only be called when it has been removed from a QH already (or
+ * the QH has been removed from the skeleton
+ */
+static void uhci_td_deallocate(struct uhci_td *td)
+{
+ td->link = 1;
+
+ clear_bit(0, &td->inuse);
+}
+
+/*
+ * UHCI interrupt list operations..
+ */
+static spinlock_t irqlist_lock = SPIN_LOCK_UNLOCKED;
+
+static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td, usb_device_irq completed, void *dev_id)
+{
+ unsigned long flags;
+
+ td->completed = completed;
+ td->dev_id = dev_id;
+
+ spin_lock_irqsave(&irqlist_lock, flags);
+ list_add(&td->irq_list, &uhci->interrupt_list);
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+}
+
+static void uhci_remove_irq_list(struct uhci_td *td)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&irqlist_lock, flags);
+ list_del(&td->irq_list);
+ spin_unlock_irqrestore(&irqlist_lock, flags);
+}
+
+/*
+ * Request a interrupt handler..
+ */
+static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ struct uhci_td *td = uhci_td_allocate(dev);
+ struct uhci_qh *interrupt_qh = uhci_qh_allocate(dev);
+
+ unsigned int destination, status;
+
+ /* Destination: pipe destination with INPUT */
+ destination = (pipe & 0x0007ff00) | 0x69;
+
+ /* Status: slow/fast, Interrupt, Active, Short Packet Detect Infinite Errors */
+ status = (pipe & (1 << 26)) | (1 << 24) | (1 << 23) | (1 << 29) | (0 << 27);
+
+ if(interrupt_qh->element != 1)
+ printk("interrupt_qh->element = 0x%x\n",
+ interrupt_qh->element);
+
+ td->link = 1;
+ td->status = status; /* In */
+ td->info = destination | (7 << 21); /* 8 bytes of data */
+ td->buffer = virt_to_bus(dev->data);
+ td->qh = interrupt_qh;
+ interrupt_qh->skel = &dev->uhci->root_hub->skel_int8_qh;
+
+ uhci_add_irq_list(dev->uhci, td, handler, dev_id);
+
+ uhci_insert_td_in_qh(interrupt_qh, td);
+
+ /* Add it into the skeleton */
+ uhci_insert_qh(&dev->uhci->root_hub->skel_int8_qh, interrupt_qh);
+ return 0;
+}
+
+/*
+ * Control thread operations: we just mark the last TD
+ * in a control thread as an interrupt TD, and wake up
+ * the front-end on completion.
+ *
+ * We need to remove the TD from the lists (both interrupt
+ * list and TD lists) by hand if something bad happens!
+ */
+static struct wait_queue *control_wakeup;
+
+static int uhci_control_completed(int status, void *buffer, void *dev_id)
+{
+ wake_up(&control_wakeup);
+ return 0; /* Don't re-instate */
+}
+
+/* td points to the last td in the list, which interrupts on completion */
+static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last)
+{
+ struct wait_queue wait = { current, NULL };
+ struct uhci_qh *ctrl_qh = uhci_qh_allocate(dev);
+ struct uhci_td *curtd;
+
+ current->state = TASK_UNINTERRUPTIBLE;
+ add_wait_queue(&control_wakeup, &wait);
+
+ uhci_add_irq_list(dev->uhci, last, uhci_control_completed, NULL);
+
+ /* FIXME: This is kinda kludged */
+ /* Walk the TD list and update the QH pointer */
+ {
+ int maxcount = 100;
+
+ curtd = first;
+ do {
+ curtd->qh = ctrl_qh;
+ if (curtd->link & 1)
+ break;
+
+ curtd = bus_to_virt(curtd->link & ~0xF);
+ if (!--maxcount) {
+ printk("runaway tds!\n");
+ break;
+ }
+ } while (1);
+ }
+
+ uhci_insert_tds_in_qh(ctrl_qh, first, last);
+
+ /* Add it into the skeleton */
+ uhci_insert_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh);
+
+ schedule_timeout(HZ/10);
+
+ remove_wait_queue(&control_wakeup, &wait);
+
+ /* Clean up in case it failed.. */
+ uhci_remove_irq_list(last);
+
+#if 0
+ printk("Looking for tds [%p, %p]\n", dev->control_td, td);
+#endif
+
+ /* Remove it from the skeleton */
+ uhci_remove_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh);
+
+ uhci_qh_deallocate(ctrl_qh);
+
+ return uhci_td_result(dev, last);
+}
+
+/*
+ * Send or receive a control message on a pipe.
+ *
+ * Note that the "pipe" structure is set up to map
+ * easily to the uhci destination fields.
+ *
+ * A control message is built up from three parts:
+ * - The command itself
+ * - [ optional ] data phase
+ * - Status complete phase
+ *
+ * The data phase can be an arbitrary number of TD's
+ * although we currently had better not have more than
+ * 29 TD's here (we have 31 TD's allocated for control
+ * operations, and two of them are used for command and
+ * status).
+ *
+ * 29 TD's is a minimum of 232 bytes worth of control
+ * information, that's just ridiculously high. Most
+ * control messages have just a few bytes of data.
+ */
+static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ struct uhci_td *first, *td, *prevtd;
+ unsigned long destination, status;
+ int ret;
+
+ if (len > usb_maxpacket(usb_dev->maxpacketsize) * 29)
+ printk("Warning, too much data for a control packet, crashing\n");
+
+ first = td = uhci_td_allocate(dev);
+
+ /* The "pipe" thing contains the destination in bits 8--18, 0x2D is SETUP */
+ destination = (pipe & 0x0007ff00) | 0x2D;
+
+ /* Status: slow/fast, Active, Short Packet Detect Three Errors */
+ status = (pipe & (1 << 26)) | (1 << 23) | (1 << 29) | (3 << 27);
+
+ /*
+ * Build the TD for the control request
+ */
+ td->status = status; /* Try forever */
+ td->info = destination | (7 << 21); /* 8 bytes of data */
+ td->buffer = virt_to_bus(cmd);
+
+ /*
+ * If direction is "send", change the frame from SETUP (0x2D)
+ * to OUT (0xE1). Else change it from SETUP to IN (0x69)
+ */
+ destination ^= (0x2D ^ 0x69); /* SETUP -> IN */
+ if (usb_pipeout(pipe))
+ destination ^= (0xE1 ^ 0x69); /* IN -> OUT */
+
+ prevtd = td;
+ td = uhci_td_allocate(dev);
+ prevtd->link = 4 | virt_to_bus(td);
+
+ /*
+ * Build the DATA TD's
+ */
+ while (len > 0) {
+ /* Build the TD for control status */
+ int pktsze = len;
+ int maxsze = usb_maxpacket(pipe);
+
+ if (pktsze > maxsze)
+ pktsze = maxsze;
+
+ /* Alternate Data0/1 (start with Data1) */
+ destination ^= 1 << 19;
+
+ td->status = status; /* Status */
+ td->info = destination | ((pktsze-1) << 21); /* pktsze bytes of data */
+ td->buffer = virt_to_bus(data);
+ td->backptr = &prevtd->link;
+
+ prevtd = td;
+ td = uhci_td_allocate(dev);
+ prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */
+
+ data += maxsze;
+ len -= maxsze;
+ }
+
+ /*
+ * Build the final TD for control status
+ */
+ destination ^= (0xE1 ^ 0x69); /* OUT -> IN */
+ destination |= 1 << 19; /* End in Data1 */
+
+ td->link = 1; /* Terminate */
+ td->status = status | (1 << 24); /* IOC */
+ td->info = destination | (0x7ff << 21); /* 0 bytes of data */
+ td->buffer = 0;
+ td->backptr = &prevtd->link;
+
+ /* Start it up.. */
+ ret = uhci_run_control(dev, first, td);
+
+ {
+ int maxcount = 100;
+ struct uhci_td *curtd = first;
+ unsigned int nextlink;
+
+ do {
+ nextlink = curtd->link;
+ uhci_remove_td(curtd);
+ uhci_td_deallocate(curtd);
+ if (nextlink & 1) /* Tail? */
+ break;
+
+ curtd = bus_to_virt(nextlink & ~0xF);
+ if (!--maxcount) {
+ printk("runaway td's!?\n");
+ break;
+ }
+ } while (1);
+ }
+
+ return ret;
+}
+
+static struct usb_device *uhci_usb_allocate(struct usb_device *parent)
+{
+ struct usb_device *usb_dev;
+ struct uhci_device *dev;
+ int i;
+
+ usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL);
+ if (!usb_dev)
+ return NULL;
+
+ memset(usb_dev, 0, sizeof(*usb_dev));
+
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ usb_destroy_configuration(usb_dev);
+ kfree(usb_dev);
+ return NULL;
+ }
+
+ /* Initialize "dev" */
+ memset(dev, 0, sizeof(*dev));
+
+ usb_dev->hcpriv = dev;
+ dev->usb = usb_dev;
+
+ usb_dev->parent = parent;
+
+ if (parent) {
+ usb_dev->bus = parent->bus;
+ dev->uhci = usb_to_uhci(parent)->uhci;
+ }
+
+ /* Reset the QH's and TD's */
+ for (i = 0; i < UHCI_MAXQH; i++) {
+ dev->qh[i].link = 1;
+ dev->qh[i].element = 1;
+ dev->qh[i].inuse = 0;
+ }
+
+ for (i = 0; i < UHCI_MAXTD; i++) {
+ dev->td[i].link = 1;
+ dev->td[i].inuse = 0;
+ }
+
+ return usb_dev;
+}
+
+static int uhci_usb_deallocate(struct usb_device *usb_dev)
+{
+ struct uhci_device *dev = usb_to_uhci(usb_dev);
+ int i;
+
+ /* There are UHCI_MAXTD preallocated tds */
+ for (i = 0; i < UHCI_MAXTD; ++i) {
+ struct uhci_td *td = dev->td + i;
+
+ /* And remove it from the irq list, if it's active */
+ if (td->status & (1 << 23))
+ uhci_remove_irq_list(td);
+
+ if (td->inuse)
+ uhci_remove_td(td);
+ }
+
+ /* Remove the td from any queues */
+ for (i = 0; i < UHCI_MAXQH; ++i) {
+ struct uhci_qh *qh = dev->qh + i;
+
+ if (qh->inuse)
+ uhci_remove_qh(qh->skel, qh);
+ }
+
+ kfree(dev);
+ usb_destroy_configuration(usb_dev);
+ kfree(usb_dev);
+
+ return 0;
+}
+
+struct usb_operations uhci_device_operations = {
+ uhci_usb_allocate,
+ uhci_usb_deallocate,
+ uhci_control_msg,
+ uhci_request_irq,
+};
+
+/*
+ * This is just incredibly fragile. The timings must be just
+ * right, and they aren't really documented very well.
+ *
+ * Note the short delay between disabling reset and enabling
+ * the port..
+ */
+static void uhci_reset_port(unsigned int port)
+{
+ unsigned short status;
+
+ status = inw(port);
+ outw(status | USBPORTSC_PR, port); /* reset port */
+ wait_ms(10);
+ outw(status & ~USBPORTSC_PR, port);
+ udelay(5);
+
+ status = inw(port);
+ outw(status | USBPORTSC_PE, port); /* enable port */
+ wait_ms(10);
+
+ status = inw(port);
+ if(!(status & USBPORTSC_PE)) {
+ outw(status | USBPORTSC_PE, port); /* one more try at enabling port */
+ wait_ms(50);
+ }
+
+}
+
+
+/*
+ * This gets called if the connect status on the root
+ * hub (and the root hub only) changes.
+ */
+static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned int nr)
+{
+ struct usb_device *usb_dev;
+ struct uhci_device *dev;
+ unsigned short status;
+
+ printk("uhci_connect_change: called for %d\n", nr);
+
+ /*
+ * Even if the status says we're connected,
+ * the fact that the status bits changed may
+ * that we got disconnected and then reconnected.
+ *
+ * So start off by getting rid of any old devices..
+ */
+ usb_disconnect(&uhci->root_hub->usb->children[nr]);
+
+ status = inw(port);
+
+ /* If we have nothing connected, then clear change status and disable the port */
+ status = (status & ~USBPORTSC_PE) | USBPORTSC_PEC;
+ if (!(status & USBPORTSC_CCS)) {
+ outw(status, port);
+ return;
+ }
+
+ /*
+ * Ok, we got a new connection. Allocate a device to it,
+ * and find out what it wants to do..
+ */
+ usb_dev = uhci_usb_allocate(uhci->root_hub->usb);
+ dev = usb_dev->hcpriv;
+
+ dev->uhci = uhci;
+
+ usb_connect(usb_dev);
+
+ uhci->root_hub->usb->children[nr] = usb_dev;
+
+ wait_ms(200); /* wait for powerup */
+ uhci_reset_port(port);
+
+ /* Get speed information */
+ usb_dev->slow = (inw(port) & USBPORTSC_LSDA) ? 1 : 0;
+
+ /*
+ * Ok, all the stuff specific to the root hub has been done.
+ * The rest is generic for any new USB attach, regardless of
+ * hub type.
+ */
+ usb_new_device(usb_dev);
+}
+
+/*
+ * This gets called when the root hub configuration
+ * has changed. Just go through each port, seeing if
+ * there is something interesting happening.
+ */
+static void uhci_check_configuration(struct uhci *uhci)
+{
+ unsigned int io_addr = uhci->io_addr + USBPORTSC1;
+ int maxchild = uhci->root_hub->usb->maxchild;
+ int nr = 0;
+
+ do {
+ unsigned short status = inw(io_addr);
+
+ if (status & USBPORTSC_CSC)
+ uhci_connect_change(uhci, io_addr, nr);
+
+ nr++; io_addr += 2;
+ } while (nr < maxchild);
+}
+
+static void uhci_interrupt_notify(struct uhci *uhci)
+{
+ struct list_head *head = &uhci->interrupt_list;
+ struct list_head *tmp;
+
+ spin_lock(&irqlist_lock);
+ tmp = head->next;
+ while (tmp != head) {
+ struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list);
+ struct list_head *next;
+
+ next = tmp->next;
+
+ if (!(td->status & (1 << 23))) { /* No longer active? */
+ /* remove from IRQ list */
+ __list_del(tmp->prev, next);
+ INIT_LIST_HEAD(tmp);
+ if (td->completed(td->status, bus_to_virt(td->buffer), td->dev_id)) {
+ struct uhci_qh *interrupt_qh = td->qh;
+
+ list_add(&td->irq_list, &uhci->interrupt_list);
+ td->info ^= 1 << 19; /* toggle between data0 and data1 */
+ td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */
+
+ /* Remove then readd? Is that necessary */
+ uhci_remove_td(td);
+ uhci_insert_td_in_qh(interrupt_qh, td);
+ }
+ /* If completed wants to not reactivate, then it's */
+ /* responsible for free'ing the TD's and QH's */
+ /* or another function (such as run_control) */
+ }
+ tmp = next;
+ }
+ spin_unlock(&irqlist_lock);
+}
+
+/*
+ * Check port status - Connect Status Change - for
+ * each of the attached ports (defaults to two ports,
+ * but at least in theory there can be more of them).
+ *
+ * Wake up the configurator if something happened, we
+ * can't really do much at interrupt time.
+ */
+static void uhci_root_hub_events(struct uhci *uhci, unsigned int io_addr)
+{
+ if (waitqueue_active(&uhci_configure)) {
+ int ports = uhci->root_hub->usb->maxchild;
+ io_addr += USBPORTSC1;
+ do {
+ if (inw(io_addr) & USBPORTSC_CSC) {
+ wake_up(&uhci_configure);
+ return;
+ }
+ io_addr += 2;
+ } while (--ports > 0);
+ }
+}
+
+static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs)
+{
+ struct uhci *uhci = __uhci;
+ unsigned int io_addr = uhci->io_addr;
+ unsigned short status;
+
+ /*
+ * Read the interrupt status, and write it back to clear the interrupt cause
+ */
+ status = inw(io_addr + USBSTS);
+ outw(status, io_addr + USBSTS);
+
+ /* Walk the list of pending TD's to see which ones completed.. */
+ uhci_interrupt_notify(uhci);
+
+ /* Check if there are any events on the root hub.. */
+ uhci_root_hub_events(uhci, io_addr);
+}
+
+/*
+ * We init one packet, and mark it just IOC and _not_
+ * active. Which will result in no actual USB traffic,
+ * but _will_ result in an interrupt every second.
+ *
+ * Which is exactly what we want.
+ */
+static void uhci_init_ticktd(struct uhci *uhci)
+{
+ struct uhci_device *dev = uhci->root_hub;
+ struct uhci_td *td = uhci_td_allocate(dev);
+
+ td->link = 1;
+ td->status = (1 << 24); /* interrupt on completion */
+ td->info = (15 << 21) | 0x7f69; /* (ignored) input packet, 16 bytes, device 127 */
+ td->buffer = 0;
+ td->qh = NULL;
+
+ uhci->fl->frame[0] = virt_to_bus(td);
+}
+
+static void reset_hc(struct uhci *uhci)
+{
+ unsigned int io_addr = uhci->io_addr;
+
+ /* Global reset for 50ms */
+ outw(USBCMD_GRESET, io_addr+USBCMD);
+ wait_ms(50);
+ outw(0, io_addr+USBCMD);
+ wait_ms(10);
+}
+
+static void start_hc(struct uhci *uhci)
+{
+ unsigned int io_addr = uhci->io_addr;
+ int timeout = 1000;
+
+ uhci_init_ticktd(uhci);
+
+ /*
+ * Reset the HC - this will force us to get a
+ * new notification of any already connected
+ * ports due to the virtual disconnect that it
+ * implies.
+ */
+ outw(USBCMD_HCRESET, io_addr + USBCMD);
+ while (inw(io_addr + USBCMD) & USBCMD_HCRESET) {
+ if (!--timeout) {
+ printk("USBCMD_HCRESET timed out!\n");
+ break;
+ }
+ }
+
+ outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
+ outw(0, io_addr + USBFRNUM);
+ outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD);
+
+ /* Run and mark it configured with a 64-byte max packet */
+ outw(USBCMD_RS | USBCMD_CF, io_addr + USBCMD);
+}
+
+/*
+ * Allocate a frame list, and four regular queues.
+ *
+ * The hardware doesn't really know any difference
+ * in the queues, but the order does matter for the
+ * protocols higher up. The order is:
+ *
+ * - any isochronous events handled before any
+ * of the queues. We don't do that here, because
+ * we'll create the actual TD entries on demand.
+ * - The first queue is the "interrupt queue".
+ * - The second queue is the "control queue".
+ * - The third queue is "bulk data".
+ *
+ * We could certainly have multiple queues of the same
+ * type, and maybe we should. We could have per-device
+ * queues, for example. We begin small.
+ */
+static struct uhci *alloc_uhci(unsigned int io_addr)
+{
+ int i;
+ struct uhci *uhci;
+ struct usb_bus *bus;
+ struct uhci_device *dev;
+ struct usb_device *usb;
+
+ uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
+ if (!uhci)
+ return NULL;
+
+ memset(uhci, 0, sizeof(*uhci));
+
+ uhci->irq = -1;
+ uhci->io_addr = io_addr;
+ INIT_LIST_HEAD(&uhci->interrupt_list);
+
+ /* We need exactly one page (per UHCI specs), how convenient */
+ uhci->fl = (void *)__get_free_page(GFP_KERNEL);
+
+ bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return NULL;
+
+ memset(bus, 0, sizeof(*bus));
+
+ uhci->bus = bus;
+ bus->hcpriv = uhci;
+ bus->op = &uhci_device_operations;
+
+ /*
+ * We allocate a 8kB area for the UHCI hub. The area
+ * is described by the uhci_device structure, and basically
+ * contains everything needed for normal operation.
+ *
+ * The first page is the actual device descriptor for the
+ * hub.
+ *
+ * The second page is used for the frame list.
+ */
+ usb = uhci_usb_allocate(NULL);
+ if (!usb)
+ return NULL;
+
+ dev = uhci->root_hub = usb_to_uhci(usb);
+
+ usb->bus = bus;
+
+ /* Initialize the root hub */
+ /* UHCI specs says devices must have 2 ports, but goes on to say */
+ /* they may have more but give no way to determine how many they */
+ /* have, so default to 2 */
+ usb->maxchild = 2;
+ usb_init_root_hub(usb);
+
+ /*
+ * Initialize the queues. They all start out empty,
+ * linked to each other in the proper order.
+ */
+ for (i = 1 ; i < 9; i++) {
+ dev->qh[i].link = 2 | virt_to_bus(&dev->skel_control_qh);
+ dev->qh[i].element = 1;
+ }
+
+ dev->skel_control_qh.link = 2 | virt_to_bus(&dev->skel_bulk0_qh);
+ dev->skel_control_qh.element = 1;
+
+ dev->skel_bulk0_qh.link = 2 | virt_to_bus(&dev->skel_bulk1_qh);
+ dev->skel_bulk0_qh.element = 1;
+
+ dev->skel_bulk1_qh.link = 2 | virt_to_bus(&dev->skel_bulk2_qh);
+ dev->skel_bulk1_qh.element = 1;
+
+ dev->skel_bulk2_qh.link = 2 | virt_to_bus(&dev->skel_bulk3_qh);
+ dev->skel_bulk2_qh.element = 1;
+
+ dev->skel_bulk3_qh.link = 1;
+ dev->skel_bulk3_qh.element = 1;
+
+ /*
+ * Fill the frame list: make all entries point to
+ * the proper interrupt queue.
+ *
+ * This is probably silly, but it's a simple way to
+ * scatter the interrupt queues in a way that gives
+ * us a reasonable dynamic range for irq latencies.
+ */
+ for (i = 0; i < 1024; i++) {
+ struct uhci_qh * irq = &dev->skel_int2_qh;
+ if (i & 1) {
+ irq++;
+ if (i & 2) {
+ irq++;
+ if (i & 4) {
+ irq++;
+ if (i & 8) {
+ irq++;
+ if (i & 16) {
+ irq++;
+ if (i & 32) {
+ irq++;
+ if (i & 64) {
+ irq++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ uhci->fl->frame[i] = 2 | virt_to_bus(irq);
+ }
+
+ return uhci;
+}
+
+
+/*
+ * De-allocate all resources..
+ */
+static void release_uhci(struct uhci *uhci)
+{
+ if (uhci->irq >= 0) {
+ free_irq(uhci->irq, uhci);
+ uhci->irq = -1;
+ }
+
+#if 0
+ if (uhci->root_hub) {
+ uhci_usb_deallocate(uhci_to_usb(uhci->root_hub));
+ uhci->root_hub = NULL;
+ }
+#endif
+
+ if (uhci->fl) {
+ free_page((unsigned long)uhci->fl);
+ uhci->fl = NULL;
+ }
+
+ kfree(uhci->bus);
+ kfree(uhci);
+}
+
+void cleanup_drivers(void);
+
+static int uhci_control_thread(void * __uhci)
+{
+ struct uhci *uhci = (struct uhci *)__uhci;
+
+ lock_kernel();
+ request_region(uhci->io_addr, 32, "usb-uhci");
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources..
+ */
+ printk("uhci_control_thread at %p\n", &uhci_control_thread);
+ exit_mm(current);
+ exit_files(current);
+ exit_fs(current);
+
+ strcpy(current->comm, "uhci-control");
+
+ /*
+ * Ok, all systems are go..
+ */
+ start_hc(uhci);
+ for(;;) {
+ siginfo_t info;
+ int unsigned long signr;
+
+ interruptible_sleep_on(&uhci_configure);
+#ifdef CONFIG_APM
+ if (apm_resume) {
+ apm_resume = 0;
+ start_hc(uhci);
+ continue;
+ }
+#endif
+ uhci_check_configuration(uhci);
+
+ if(signal_pending(current)) {
+ /* sending SIGUSR1 makes us print out some info */
+ spin_lock_irq(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if(signr == SIGUSR1) {
+ printk("UHCI queue dump:\n");
+ show_queues(uhci);
+ } else {
+ break;
+ }
+ }
+ }
+
+#if 0
+ if(uhci->root_hub)
+ for(i = 0; i < uhci->root_hub->usb->maxchild; i++)
+ usb_disconnect(uhci->root_hub->usb->children + i);
+#endif
+
+ cleanup_drivers();
+
+ reset_hc(uhci);
+ release_region(uhci->io_addr, 32);
+
+ release_uhci(uhci);
+ MOD_DEC_USE_COUNT;
+
+ printk("uhci_control_thread exiting\n");
+
+ return 0;
+}
+
+/*
+ * If we've successfully found a UHCI, now is the time to increment the
+ * module usage count, start the control thread, and return success..
+ */
+static int found_uhci(int irq, unsigned int io_addr)
+{
+ int retval;
+ struct uhci *uhci;
+
+ uhci = alloc_uhci(io_addr);
+ if (!uhci)
+ return -ENOMEM;
+
+ reset_hc(uhci);
+
+ retval = -EBUSY;
+ if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb", uhci) == 0) {
+ int pid;
+
+ MOD_INC_USE_COUNT;
+ uhci->irq = irq;
+ pid = kernel_thread(uhci_control_thread, uhci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (pid >= 0)
+ return 0;
+
+ MOD_DEC_USE_COUNT;
+ retval = pid;
+ }
+ release_uhci(uhci);
+ return retval;
+}
+
+static int start_uhci(struct pci_dev *dev)
+{
+ int i;
+
+ /* Search for the IO base address.. */
+ for (i = 0; i < 6; i++) {
+ unsigned int io_addr = dev->base_address[i];
+
+ /* IO address? */
+ if (!(io_addr & 1))
+ continue;
+
+ io_addr &= PCI_BASE_ADDRESS_IO_MASK;
+
+ /* Is it already in use? */
+ if (check_region(io_addr, 32))
+ break;
+
+ return found_uhci(dev->irq, io_addr);
+ }
+ return -1;
+}
+
+#ifdef CONFIG_APM
+static int handle_apm_event(apm_event_t event)
+{
+ static int down = 0;
+
+ switch (event) {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ if (down) {
+ printk(KERN_DEBUG "uhci: received extra suspend event\n");
+ break;
+ }
+ down = 1;
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ if (!down) {
+ printk(KERN_DEBUG "uhci: received bogus resume event\n");
+ break;
+ }
+ down = 0;
+ if (waitqueue_active(&uhci_configure)) {
+ apm_resume = 1;
+ wake_up(&uhci_configure);
+ }
+ break;
+ }
+ return 0;
+}
+#endif
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_APM
+ apm_unregister_callback(&handle_apm_event);
+#endif
+}
+
+#define uhci_init init_module
+
+#endif
+
+int uhci_init(void)
+{
+ int retval;
+ struct pci_dev *dev = NULL;
+ u8 type;
+
+ retval = -ENODEV;
+ for (;;) {
+ dev = pci_find_class(PCI_CLASS_SERIAL_USB<<8, dev);
+ if (!dev)
+ break;
+ /* Is it UHCI */
+ pci_read_config_byte(dev, PCI_CLASS_PROG, &type);
+ if(type != 0)
+ continue;
+ /* Ok set it up */
+ retval = start_uhci(dev);
+ if (retval < 0)
+ continue;
+
+#ifdef CONFIG_USB_MOUSE
+ usb_mouse_init();
+#endif
+#ifdef CONFIG_USB_KBD
+ usb_kbd_init();
+#endif
+ hub_init();
+#ifdef CONFIG_USB_AUDIO
+ usb_audio_init();
+#endif
+#ifdef CONFIG_APM
+ apm_register_callback(&handle_apm_event);
+#endif
+
+ return 0;
+ }
+ return retval;
+}
+
+void cleanup_drivers(void)
+{
+ hub_cleanup();
+#ifdef CONFIG_USB_MOUSE
+ usb_mouse_cleanup();
+#endif
+}
diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h
new file mode 100644
index 000000000..f063356ac
--- /dev/null
+++ b/drivers/usb/uhci.h
@@ -0,0 +1,229 @@
+#ifndef __LINUX_UHCI_H
+#define __LINUX_UHCI_H
+
+#include <linux/list.h>
+
+#include "usb.h"
+
+/*
+ * Universal Host Controller Interface data structures and defines
+ */
+
+/* Command register */
+#define USBCMD 0
+#define USBCMD_RS 0x0001 /* Run/Stop */
+#define USBCMD_HCRESET 0x0002 /* Host reset */
+#define USBCMD_GRESET 0x0004 /* Global reset */
+#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */
+#define USBCMD_FGR 0x0010 /* Force Global Resume */
+#define USBCMD_SWDBG 0x0020 /* SW Debug mode */
+#define USBCMD_CF 0x0040 /* Config Flag (sw only) */
+#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */
+
+/* Status register */
+#define USBSTS 2
+#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */
+#define USBSTS_ERROR 0x0002 /* Interrupt due to error */
+#define USBSTS_RD 0x0004 /* Resume Detect */
+#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */
+#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */
+#define USBSTS_HCH 0x0020 /* HC Halted */
+
+/* Interrupt enable register */
+#define USBINTR 4
+#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */
+#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */
+#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */
+#define USBINTR_SP 0x0008 /* Short packet interrupt enable */
+
+#define USBFRNUM 6
+#define USBFLBASEADD 8
+#define USBSOF 12
+
+/* USB port status and control registers */
+#define USBPORTSC1 16
+#define USBPORTSC2 18
+#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */
+#define USBPORTSC_CSC 0x0002 /* Connect Status Change */
+#define USBPORTSC_PE 0x0004 /* Port Enable */
+#define USBPORTSC_PEC 0x0008 /* Port Enable Change */
+#define USBPORTSC_LS 0x0030 /* Line Status */
+#define USBPORTSC_RD 0x0040 /* Resume Detect */
+#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */
+#define USBPORTSC_PR 0x0200 /* Port Reset */
+#define USBPORTSC_SUSP 0x1000 /* Suspend */
+
+struct uhci_qh {
+ unsigned int link; /* Next queue */
+ unsigned int element; /* Queue element pointer */
+ int inuse; /* Inuse? */
+ struct uhci_qh *skel; /* Skeleton head */
+} __attribute__((aligned(16)));
+
+struct uhci_framelist {
+ unsigned int frame[1024];
+} __attribute__((aligned(4096)));
+
+/*
+ * The documentation says "4 words for hardware, 4 words for software".
+ *
+ * That's silly, the hardware doesn't care. The hardware only cares that
+ * the hardware words are 16-byte aligned, and we can have any amount of
+ * sw space after the TD entry as far as I can tell.
+ *
+ * But let's just go with the documentation, at least for 32-bit machines.
+ * On 64-bit machines we probably want to take advantage of the fact that
+ * hw doesn't really care about the size of the sw-only area.
+ *
+ * Alas, not anymore, we have more than 4 words of software, woops
+ */
+struct uhci_td {
+ /* Hardware fields */
+ __u32 link;
+ __u32 status;
+ __u32 info;
+ __u32 buffer;
+
+ /* Software fields */
+ struct list_head irq_list; /* Active interrupt list.. */
+ usb_device_irq completed; /* Completion handler routine */
+ unsigned int *backptr; /* Where to remove this from.. */
+ void *dev_id;
+ int inuse; /* Inuse? */
+ struct uhci_qh *qh;
+} __attribute__((aligned(32)));
+
+/*
+ * Note the alignment requirements of the entries
+ *
+ * Each UHCI device has pre-allocated QH and TD entries.
+ * You can use more than the pre-allocated ones, but I
+ * don't see you usually needing to.
+ */
+struct uhci;
+
+#define UHCI_MAXTD 64
+
+#define UHCI_MAXQH 16
+
+/* The usb device part must be first! */
+struct uhci_device {
+ struct usb_device *usb;
+
+ struct uhci *uhci;
+ struct uhci_qh qh[UHCI_MAXQH]; /* These are the "common" qh's for each device */
+ struct uhci_td td[UHCI_MAXTD];
+
+ unsigned long data[16];
+};
+
+#define uhci_to_usb(uhci) ((uhci)->usb)
+#define usb_to_uhci(usb) ((struct uhci_device *)(usb)->hcpriv)
+
+/*
+ * The root hub pre-allocated QH's and TD's have
+ * some special global uses..
+ */
+#define control_td td /* Td's 0-30 */
+/* This is only for the root hub's TD list */
+#define tick_td td[31]
+
+/*
+ * There are various standard queues. We set up several different
+ * queues for each of the three basic queue types: interrupt,
+ * control, and bulk.
+ *
+ * - There are various different interrupt latencies: ranging from
+ * every other USB frame (2 ms apart) to every 256 USB frames (ie
+ * 256 ms apart). Make your choice according to how obnoxious you
+ * want to be on the wire, vs how critical latency is for you.
+ * - The control list is done every frame.
+ * - There are 4 bulk lists, so that up to four devices can have a
+ * bulk list of their own and when run concurrently all four lists
+ * will be be serviced.
+ *
+ * This is a bit misleading, there are various interrupt latencies, but they
+ * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the
+ * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor
+ * problem
+ *
+ * In the case of the root hub, these QH's are just head's of qh's. Don't
+ * be scared, it kinda makes sense. Look at this wonderful picture care of
+ * Linus:
+ *
+ * generic-iso-QH -> dev1-iso-QH -> generic-irq-QH -> dev1-irq-QH -> ...
+ * | | | |
+ * End dev1-iso-TD1 End dev1-irq-TD1
+ * |
+ * dev1-iso-TD2
+ * |
+ * ....
+ *
+ * This may vary a bit (the UHCI docs don't explicitly say you can put iso
+ * transfers in QH's and all of their pictures don't have that either) but
+ * other than that, that is what we're doing now
+ *
+ * To keep with Linus' nomenclature, this is called the qh skeleton. These
+ * labels (below) are only signficant to the root hub's qh's
+ */
+#define skel_iso_qh qh[0]
+
+#define skel_int2_qh qh[1]
+#define skel_int4_qh qh[2]
+#define skel_int8_qh qh[3]
+#define skel_int16_qh qh[4]
+#define skel_int32_qh qh[5]
+#define skel_int64_qh qh[6]
+#define skel_int128_qh qh[7]
+#define skel_int256_qh qh[8]
+
+#define skel_control_qh qh[9]
+
+#define skel_bulk0_qh qh[10]
+#define skel_bulk1_qh qh[11]
+#define skel_bulk2_qh qh[12]
+#define skel_bulk3_qh qh[13]
+
+/*
+ * These are significant to the devices allocation of QH's
+ */
+#if 0
+#define iso_qh qh[0]
+#define int_qh qh[1] /* We have 2 "common" interrupt QH's */
+#define control_qh qh[3]
+#define bulk_qh qh[4] /* We have 4 "common" bulk QH's */
+#define extra_qh qh[8] /* The rest, anything goes */
+#endif
+
+/*
+ * This describes the full uhci information.
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs.
+ */
+struct uhci {
+ int irq;
+ unsigned int io_addr;
+
+ struct usb_bus *bus;
+
+#if 0
+ /* These are "standard" QH's for the entire bus */
+ struct uhci_qh qh[UHCI_MAXQH];
+#endif
+ struct uhci_device *root_hub; /* Root hub device descriptor.. */
+
+ struct uhci_framelist *fl; /* Frame list */
+ struct list_head interrupt_list; /* List of interrupt-active TD's for this uhci */
+};
+
+/* needed for the debugging code */
+struct uhci_td *uhci_link_to_td(unsigned int element);
+
+/* Debugging code */
+void show_td(struct uhci_td * td);
+void show_status(struct uhci *uhci);
+void show_queues(struct uhci *uhci);
+
+#endif
+
diff --git a/drivers/usb/usb-debug.c b/drivers/usb/usb-debug.c
new file mode 100644
index 000000000..86d08cd78
--- /dev/null
+++ b/drivers/usb/usb-debug.c
@@ -0,0 +1,127 @@
+/*
+ * debug.c - USB debug helper routines.
+ *
+ * I just want these out of the way where they aren't in your
+ * face, but so that you can still use them..
+ */
+#include <linux/kernel.h>
+
+#include "usb.h"
+
+static void usb_show_endpoint(struct usb_endpoint_descriptor *endpoint)
+{
+ usb_show_endpoint_descriptor(endpoint);
+}
+
+static void usb_show_interface(struct usb_interface_descriptor *interface)
+{
+ int i;
+
+ usb_show_interface_descriptor(interface);
+ for (i = 0 ; i < interface->bNumEndpoints; i++)
+ usb_show_endpoint(interface->endpoint + i);
+}
+
+static void usb_show_config(struct usb_config_descriptor *config)
+{
+ int i;
+
+ usb_show_config_descriptor(config);
+ for (i = 0 ; i < config->bNumInterfaces; i++)
+ usb_show_interface(config->interface + i);
+}
+
+void usb_show_device(struct usb_device *dev)
+{
+ int i;
+
+ usb_show_device_descriptor(&dev->descriptor);
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+ usb_show_config(dev->config + i);
+}
+
+
+/*
+ * Parse and show the different USB descriptors.
+ */
+void usb_show_device_descriptor(struct usb_device_descriptor *desc)
+{
+ printk(" USB version %x.%02x\n", desc->bcdUSB >> 8, desc->bcdUSB & 0xff);
+ printk(" Vendor: %04x\n", desc->idVendor);
+ printk(" Product: %04x\n", desc->idProduct);
+ printk(" Configurations: %d\n", desc->bNumConfigurations);
+
+ printk(" Device Class: %d\n", desc->bDeviceClass);
+ switch (desc->bDeviceClass) {
+ case 0:
+ printk(" Per-interface classes\n");
+ break;
+ case 9:
+ printk(" Hub device class\n");
+ break;
+ case 0xff:
+ printk(" Vendor class\n");
+ break;
+ default:
+ printk(" Unknown class\n");
+ }
+}
+
+void usb_show_config_descriptor(struct usb_config_descriptor * desc)
+{
+ printk("Configuration:\n");
+ printk(" bLength = %4d%s\n", desc->bLength,
+ desc->bLength == 9 ? "" : " (!!!)");
+ printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
+ printk(" wTotalLength = %04x\n", desc->wTotalLength);
+ printk(" bNumInterfaces = %02x\n", desc->bNumInterfaces);
+ printk(" bConfigurationValue = %02x\n", desc->bConfigurationValue);
+ printk(" iConfiguration = %02x\n", desc->iConfiguration);
+ printk(" bmAttributes = %02x\n", desc->bmAttributes);
+ printk(" MaxPower = %4dmA\n", desc->MaxPower * 2);
+}
+
+void usb_show_interface_descriptor(struct usb_interface_descriptor * desc)
+{
+ printk(" Interface:\n");
+ printk(" bLength = %4d%s\n", desc->bLength,
+ desc->bLength == 9 ? "" : " (!!!)");
+ printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
+ printk(" bInterfaceNumber = %02x\n", desc->bInterfaceNumber);
+ printk(" bAlternateSetting = %02x\n", desc->bAlternateSetting);
+ printk(" bNumEndpoints = %02x\n", desc->bNumEndpoints);
+ printk(" bInterfaceClass = %02x\n", desc->bInterfaceClass);
+ printk(" bInterfaceSubClass = %02x\n", desc->bInterfaceSubClass);
+ printk(" bInterfaceProtocol = %02x\n", desc->bInterfaceProtocol);
+ printk(" iInterface = %02x\n", desc->iInterface);
+}
+
+void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor * desc)
+{
+ char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" };
+ printk(" Endpoint:\n");
+ printk(" bLength = %4d%s\n", desc->bLength,
+ desc->bLength == 7 ? "" : " (!!!)");
+ printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
+ printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress,
+ (desc->bEndpointAddress & 0x80) ? "in" : "out");
+ printk(" bmAttributes = %02x (%s)\n", desc->bmAttributes,
+ EndpointType[3 & desc->bmAttributes]);
+ printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize);
+ printk(" bInterval = %02x\n", desc->bInterval);
+}
+
+void usb_show_hub_descriptor(struct usb_hub_descriptor * desc)
+{
+ int len = 7;
+ unsigned char *ptr = (unsigned char *) desc;
+
+ printk("Interface:");
+ while (len) {
+ printk(" %02x", *ptr);
+ ptr++; len--;
+ }
+ printk("\n");
+}
+
+
diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c
new file mode 100644
index 000000000..f5e89ea89
--- /dev/null
+++ b/drivers/usb/usb.c
@@ -0,0 +1,673 @@
+/*
+ * driver/usb/usb.c
+ *
+ * (C) Copyright Linus Torvalds 1999
+ *
+ * NOTE! This is not actually a driver at all, rather this is
+ * just a collection of helper routines that implement the
+ * generic USB things that the real drivers can use..
+ *
+ * Think of this as a "USB library" rather than anything else.
+ * It should be considered a slave, with no callbacks. Callbacks
+ * are evil.
+ */
+
+/*
+ * Table 9-2
+ *
+ * Offset Field Size Value Desc
+ * 0 bmRequestType 1 Bitmap D7: Direction
+ * 0 = Host-to-device
+ * 1 = Device-to-host
+ * D6..5: Type
+ * 0 = Standard
+ * 1 = Class
+ * 2 = Vendor
+ * 3 = Reserved
+ * D4..0: Recipient
+ * 0 = Device
+ * 1 = Interface
+ * 2 = Endpoint
+ * 3 = Other
+ * 4..31 = Reserved
+ * 1 bRequest 1 Value Specific request (9-3)
+ * 2 wValue 2 Value Varies
+ * 4 wIndex 2 Index/Offset Varies
+ * 6 wLength 2 Count Bytes for data
+ */
+
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/malloc.h>
+
+#include "usb.h"
+
+/*
+ * We have a per-interface "registered driver" list.
+ */
+static LIST_HEAD(usb_driver_list);
+
+int usb_register(struct usb_driver *new_driver)
+{
+ /* Add it to the list of known drivers */
+ list_add(&new_driver->driver_list, &usb_driver_list);
+
+ /*
+ * We should go through all existing devices, and see if any of
+ * them would be acceptable to the new driver.. Let's do that
+ * in version 2.0.
+ */
+ return 0;
+}
+
+void usb_deregister(struct usb_driver *driver)
+{
+ list_del(&driver->driver_list);
+}
+
+/*
+ * This entrypoint gets called for each new device.
+ *
+ * We now walk the list of registered USB drivers,
+ * looking for one that will accept this device as
+ * his..
+ */
+void usb_device_descriptor(struct usb_device *dev)
+{
+ struct list_head *tmp = usb_driver_list.next;
+
+ while (tmp != &usb_driver_list) {
+ struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
+ tmp = tmp->next;
+ if (driver->probe(dev))
+ continue;
+ dev->driver = driver;
+ return;
+ }
+
+ /*
+ * Ok, no driver accepted the device, so show the info
+ * for debugging..
+ */
+ printk("Unknown new USB device:\n");
+ usb_show_device(dev);
+}
+
+/*
+ * Parse the fairly incomprehensible output of
+ * the USB configuration data, and build up the
+ * USB device database.
+ */
+static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desctype, unsigned char descindex)
+{
+ int parsed = 0;
+ int n_len;
+ unsigned short n_desc;
+
+ for (;;) {
+ int i;
+
+ if (len < descindex)
+ return -1;
+ n_desc = *(unsigned short *)ptr;
+ n_len = n_desc & 0xff;
+
+ if (n_desc == ((desctype << 8) + descindex))
+ break;
+
+ if (((n_desc >> 8)&0xFF) == desctype &&
+ n_len > descindex)
+ {
+ printk("bug: oversized descriptor.\n");
+ break;
+ }
+
+ if (n_len < 2 || n_len > len)
+ {
+ printk("Short descriptor.\n");
+ return -1;
+ }
+ printk(
+ "Expected descriptor %02X/%02X, got %02X/%02X - skipping\n",
+ desctype, descindex,
+ (n_desc >> 8) & 0xFF, n_desc & 0xFF);
+ for (i = 0 ; i < n_len; i++)
+ printk(" %d %02x\n", i, ptr[i]);
+ len -= n_len;
+ ptr += n_len;
+ parsed += n_len;
+ }
+
+ printk("Found %02X:%02X\n",
+ desctype, descindex);
+ return parsed;
+}
+
+/*
+ * Parse the even more incomprehensible mess made of the USB spec
+ * by USB audio having private magic to go with it.
+ */
+
+static int usb_check_descriptor(unsigned char *ptr, int len, unsigned char desctype)
+{
+ int n_len = ptr[0];
+
+ if (n_len < 2 || n_len > len)
+ {
+ printk("Short descriptor.\n");
+ return -1;
+ }
+
+ if (ptr[1] == desctype)
+ return 0;
+
+ return -1;
+}
+
+
+static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *ptr, int len)
+{
+ int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, 7);
+ int i;
+
+ if (parsed < 0)
+ return parsed;
+ memcpy(endpoint, ptr + parsed, ptr[parsed]);
+
+ parsed += ptr[parsed];
+ len -= ptr[parsed];
+
+ while((i = usb_check_descriptor(ptr+parsed, len, 0x25))>=0)
+ {
+ usb_audio_endpoint(endpoint, ptr+parsed+i);
+ len -= ptr[parsed+i];
+ parsed += ptr[parsed+i];
+ }
+
+ return parsed;// + ptr[parsed];
+}
+
+static int usb_parse_interface(struct usb_device *dev, struct usb_interface_descriptor *interface, unsigned char *ptr, int len)
+{
+ int i;
+ int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, 9);
+ int retval;
+
+ if (parsed < 0)
+ return parsed;
+
+ memcpy(interface, ptr + parsed, *ptr);
+ len -= ptr[parsed];
+ parsed += ptr[parsed];
+
+ while((i=usb_check_descriptor(ptr+parsed, len, 0x24))>=0)
+ {
+ usb_audio_interface(interface, ptr+parsed+i);
+ len -= ptr[parsed+i];
+ parsed += ptr[parsed+i];
+ }
+
+ if (interface->bNumEndpoints > USB_MAXENDPOINTS)
+ {
+ printk(KERN_WARNING "usb: too many endpoints.\n");
+ return -1;
+ }
+
+ interface->endpoint = (struct usb_endpoint_descriptor *)
+ kmalloc(interface->bNumEndpoints * sizeof(struct usb_endpoint_descriptor), GFP_KERNEL);
+ if(interface->endpoint==NULL)
+ {
+ printk(KERN_WARNING "usb: out of memory.\n");
+ return -1;
+ }
+ memset(interface->endpoint, 0, interface->bNumEndpoints*sizeof(struct usb_endpoint_descriptor));
+
+ for (i = 0; i < interface->bNumEndpoints; i++) {
+// if(((USB_DT_HID << 8) | 9) == *(unsigned short*)(ptr + parsed)) {
+// parsed += 9; /* skip over the HID descriptor for now */
+// len -= 9;
+// }
+ retval = usb_parse_endpoint(dev, interface->endpoint + i, ptr + parsed, len);
+ if (retval < 0) return retval;
+ parsed += retval;
+ len -= retval;
+ }
+ return parsed;
+}
+
+static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor *config, unsigned char *ptr, int len)
+{
+ int i;
+ int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9);
+
+ if (parsed < 0)
+ return parsed;
+
+ memcpy(config, ptr + parsed, *ptr);
+ len -= *ptr;
+ parsed += *ptr;
+
+ if (config->bNumInterfaces > USB_MAXINTERFACES)
+ {
+ printk(KERN_WARNING "usb: too many interfaces.\n");
+ return -1;
+
+ }
+
+ config->interface = (struct usb_interface_descriptor *)
+ kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL);
+ if(config->interface==NULL)
+ {
+ printk(KERN_WARNING "usb: out of memory.\n");
+ return -1;
+ }
+ memset(config->interface, 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor));
+
+ for (i = 0; i < config->bNumInterfaces; i++) {
+ int retval = usb_parse_interface(dev, config->interface + i, ptr + parsed, len);
+ if (retval < 0)
+ return parsed; // HACK
+// return retval;
+ parsed += retval;
+ len -= retval;
+ }
+ return parsed;
+}
+
+int usb_parse_configuration(struct usb_device *dev, void *__buf, int bytes)
+{
+ int i;
+ unsigned char *ptr = __buf;
+
+ if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
+ {
+ printk(KERN_WARNING "usb: too many configurations.\n");
+ return -1;
+ }
+
+ dev->config = (struct usb_config_descriptor *)
+ kmalloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor), GFP_KERNEL);
+ if(dev->config==NULL)
+ {
+ printk(KERN_WARNING "usb: out of memory.\n");
+ return -1;
+ }
+ memset(dev->config, 0, dev->descriptor.bNumConfigurations*sizeof(struct usb_config_descriptor));
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+ int retval = usb_parse_config(dev, dev->config + i, ptr, bytes);
+ if (retval < 0)
+ return retval;
+ ptr += retval;
+ bytes += retval;
+ }
+ return 0;
+}
+
+void usb_destroy_configuration(struct usb_device *dev)
+{
+ int c, i;
+ struct usb_config_descriptor *cf;
+ struct usb_interface_descriptor *ifp;
+
+ if(dev->config==NULL)
+ return;
+ for(c=0;c<dev->descriptor.bNumConfigurations;c++)
+ {
+ cf=&dev->config[c];
+ if(cf->interface==NULL)
+ break;
+ for(i=0;i<cf->bNumInterfaces;i++)
+ {
+ ifp=&cf->interface[i];
+ if(ifp->endpoint==NULL)
+ break;
+ kfree(ifp->endpoint);
+ }
+ kfree(cf->interface);
+ }
+ kfree(dev->config);
+}
+
+void usb_init_root_hub(struct usb_device *dev)
+{
+ dev->devnum = -1;
+ dev->slow = 0;
+}
+
+/*
+ * Something got disconnected. Get rid of it, and all of its children.
+ */
+void usb_disconnect(struct usb_device **pdev)
+{
+ struct usb_device * dev = *pdev;
+
+ if (dev) {
+ int i;
+
+ *pdev = NULL;
+
+ printk("USB disconnect on device %d\n", dev->devnum);
+
+ if(dev->driver) dev->driver->disconnect(dev);
+
+ /* Free up all the children.. */
+ for (i = 0; i < USB_MAXCHILDREN; i++) {
+ struct usb_device **child = dev->children + i;
+ usb_disconnect(child);
+ }
+
+ /* Free up the device itself, including its device number */
+ if (dev->devnum > 0)
+ clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
+ dev->bus->op->deallocate(dev);
+ }
+}
+
+
+/*
+ * Connect a new USB device. This basically just initializes
+ * the USB device information and sets up the topology - it's
+ * up to the low-level driver to reset the port and actually
+ * do the setup (the upper levels don't know how to do that).
+ */
+void usb_connect(struct usb_device *dev)
+{
+ int devnum;
+
+ dev->descriptor.bMaxPacketSize0 = 8; /* XXX fixed 8 bytes for now */
+
+ devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
+ if (devnum < 128) {
+ set_bit(devnum, dev->bus->devmap.devicemap);
+ dev->devnum = devnum;
+ }
+}
+
+/*
+ * These are the actual routines to send
+ * and receive control messages.
+ */
+int usb_set_address(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = 0;
+ dr.request = USB_REQ_SET_ADDRESS;
+ dr.value = dev->devnum;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0);
+}
+
+int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
+{
+ devrequest dr;
+
+ dr.requesttype = 0x80;
+ dr.request = USB_REQ_GET_DESCRIPTOR;
+ dr.value = (type << 8) + index;
+ dr.index = 0;
+ dr.length = size;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
+}
+
+int usb_get_device_descriptor(struct usb_device *dev)
+{
+ return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor));
+}
+
+int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HUB | 0x80;
+ dr.request = USB_REQ_GET_DESCRIPTOR;
+ dr.value = (USB_DT_HUB << 8);
+ dr.index = 0;
+ dr.length = size;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, size);
+}
+
+int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_PORT;
+ dr.request = USB_REQ_CLEAR_FEATURE;
+ dr.value = feature;
+ dr.index = port;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_set_port_feature(struct usb_device *dev, int port, int feature)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_PORT;
+ dr.request = USB_REQ_SET_FEATURE;
+ dr.value = feature;
+ dr.index = port;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_get_hub_status(struct usb_device *dev, void *data)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HUB | 0x80;
+ dr.request = USB_REQ_GET_STATUS;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 4;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+}
+
+int usb_get_port_status(struct usb_device *dev, int port, void *data)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_PORT | 0x80;
+ dr.request = USB_REQ_GET_STATUS;
+ dr.value = 0;
+ dr.index = port;
+ dr.length = 4;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
+}
+
+int usb_get_protocol(struct usb_device *dev)
+{
+ unsigned char buf[8];
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HIDD | 0x80;
+ dr.request = USB_REQ_GET_PROTOCOL;
+ dr.value = 0;
+ dr.index = 1;
+ dr.length = 1;
+
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1))
+ return -1;
+
+ return buf[0];
+}
+
+int usb_set_protocol(struct usb_device *dev, int protocol)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HIDD;
+ dr.request = USB_REQ_SET_PROTOCOL;
+ dr.value = protocol;
+ dr.index = 1;
+ dr.length = 0;
+
+ if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
+ return -1;
+
+ return 0;
+}
+
+/* keyboards want a nonzero duration according to HID spec, but
+ mice should use infinity (0) -keryan */
+int usb_set_idle(struct usb_device *dev, int duration, int report_id)
+{
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HIDD;
+ dr.request = USB_REQ_SET_IDLE;
+ dr.value = (duration << 8) | report_id;
+ dr.index = 1;
+ dr.length = 0;
+
+ if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
+ return -1;
+
+ return 0;
+}
+
+int usb_set_configuration(struct usb_device *dev, int configuration)
+{
+ devrequest dr;
+
+ dr.requesttype = 0;
+ dr.request = USB_REQ_SET_CONFIGURATION;
+ dr.value = configuration;
+ dr.index = 0;
+ dr.length = 0;
+
+ if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
+ return -1;
+
+ return 0;
+}
+
+int usb_get_report(struct usb_device *dev)
+{
+ unsigned char buf[8];
+ devrequest dr;
+
+ dr.requesttype = USB_RT_HIDD | 0x80;
+ dr.request = USB_REQ_GET_REPORT;
+ dr.value = 0x100;
+ dr.index = 1;
+ dr.length = 3;
+
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 3))
+ return -1;
+
+ return buf[0];
+}
+
+int usb_get_configuration(struct usb_device *dev)
+{
+ unsigned int cfgno,size;
+ unsigned char buffer[400];
+ unsigned char * bufptr;
+
+ bufptr=buffer;
+ for (cfgno=0;cfgno<dev->descriptor.bNumConfigurations;cfgno++) {
+ /* Get the first 8 bytes - guaranteed */
+ if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8))
+ return -1;
+
+ /* Get the full buffer */
+ size = *(unsigned short *)(bufptr+2);
+ if (bufptr+size > buffer+sizeof(buffer))
+ {
+ printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size);
+ size = buffer+sizeof(buffer)-bufptr;
+ }
+ if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, size))
+ return -1;
+
+ /* Prepare for next configuration */
+ bufptr+=size;
+ }
+ return usb_parse_configuration(dev, buffer, size);
+}
+
+/*
+ * By the time we get here, the device has gotten a new device ID
+ * and is in the default state. We need to identify the thing and
+ * get the ball rolling..
+ */
+void usb_new_device(struct usb_device *dev)
+{
+ int addr, i;
+
+ printk("USB new device connect, assigned device number %d\n",
+ dev->devnum);
+
+ dev->maxpacketsize = 0; /* Default to 8 byte max packet size */
+
+ addr = dev->devnum;
+ dev->devnum = 0;
+
+ /* Slow devices */
+ for (i = 0; i < 5; i++) {
+ if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8))
+ break;
+
+ printk("get_descriptor failed, waiting\n");
+ wait_ms(200);
+ }
+ if (i == 5) {
+ printk("giving up\n");
+ return;
+ }
+
+#if 0
+ printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0);
+#endif
+ switch (dev->descriptor.bMaxPacketSize0) {
+ case 8: dev->maxpacketsize = 0; break;
+ case 16: dev->maxpacketsize = 1; break;
+ case 32: dev->maxpacketsize = 2; break;
+ case 64: dev->maxpacketsize = 3; break;
+ }
+#if 0
+ printk("dev->mps: %d\n", dev->maxpacketsize);
+#endif
+
+ dev->devnum = addr;
+
+ if (usb_set_address(dev)) {
+ printk("Unable to set address\n");
+ /* FIXME: We should disable the port */
+ return;
+ }
+
+ wait_ms(10); /* Let the SET_ADDRESS settle */
+
+ if (usb_get_device_descriptor(dev)) {
+ printk("Unable to get device descriptor\n");
+ return;
+ }
+
+ if (usb_get_configuration(dev)) {
+ printk("Unable to get configuration\n");
+ return;
+ }
+
+#if 0
+ printk("Vendor: %X\n", dev->descriptor.idVendor);
+ printk("Product: %X\n", dev->descriptor.idProduct);
+#endif
+
+ usb_device_descriptor(dev);
+}
+
+int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
+{
+ return dev->bus->op->request_irq(dev, pipe, handler, period, dev_id);
+}
+
diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h
new file mode 100644
index 000000000..10379ef88
--- /dev/null
+++ b/drivers/usb/usb.h
@@ -0,0 +1,376 @@
+#ifndef __LINUX_USB_H
+#define __LINUX_USB_H
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+
+static __inline__ void wait_ms(unsigned int ms)
+{
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1 + ms * HZ / 1000);
+}
+
+
+typedef struct {
+ unsigned char requesttype;
+ unsigned char request;
+ unsigned short value;
+ unsigned short index;
+ unsigned short length;
+} devrequest;
+
+/*
+ * Class codes
+ */
+#define USB_CLASS_HUB 9
+
+/*
+ * Descriptor types
+ */
+#define USB_DT_DEVICE 0x01
+#define USB_DT_CONFIG 0x02
+#define USB_DT_STRING 0x03
+#define USB_DT_INTERFACE 0x04
+#define USB_DT_ENDPOINT 0x05
+
+#define USB_DT_HUB 0x29
+#define USB_DT_HID 0x21
+
+/*
+ * Standard requests
+ */
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+/* 0x02 is reserved */
+#define USB_REQ_SET_FEATURE 0x03
+/* 0x04 is reserved */
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+
+/*
+ * HIDD requests
+ */
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_GET_IDLE 0x02
+#define USB_REQ_GET_PROTOCOL 0x03
+#define USB_REQ_SET_REPORT 0x09
+#define USB_REQ_SET_IDLE 0x0A
+#define USB_REQ_SET_PROTOCOL 0x0B
+
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+/*
+ * Request target types.
+ */
+#define USB_RT_DEVICE 0x00
+#define USB_RT_INTERFACE 0x01
+#define USB_RT_ENDPOINT 0x02
+
+#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
+#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
+
+#define USB_RT_HIDD (USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+
+/*
+ * USB device number allocation bitmap. There's one bitmap
+ * per USB tree.
+ */
+struct usb_devmap {
+ unsigned long devicemap[128 / (8*sizeof(unsigned long))];
+};
+
+/*
+ * This is a USB device descriptor.
+ *
+ * USB device information
+ *
+ */
+
+#define USB_MAXCONFIG 8
+#define USB_MAXINTERFACES 32
+#define USB_MAXENDPOINTS 32
+
+struct usb_device_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u16 bcdUSB;
+ __u8 bDeviceClass;
+ __u8 bDeviceSubClass;
+ __u8 bDeviceProtocol;
+ __u8 bMaxPacketSize0;
+ __u16 idVendor;
+ __u16 idProduct;
+ __u16 bcdDevice;
+ __u8 iManufacturer;
+ __u8 iProduct;
+ __u8 iSerialNumber;
+ __u8 bNumConfigurations;
+};
+
+/* Endpoint descriptor */
+struct usb_endpoint_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bEndpointAddress;
+ __u8 bmAttributes;
+ __u16 wMaxPacketSize;
+ __u8 bInterval;
+ void *audio;
+};
+
+/* Interface descriptor */
+struct usb_interface_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bInterfaceNumber;
+ __u8 bAlternateSetting;
+ __u8 bNumEndpoints;
+ __u8 bInterfaceClass;
+ __u8 bInterfaceSubClass;
+ __u8 bInterfaceProtocol;
+ __u8 iInterface;
+
+ struct usb_endpoint_descriptor *endpoint;
+ void *audio;
+};
+
+/* Configuration descriptor information.. */
+struct usb_config_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u16 wTotalLength;
+ __u8 bNumInterfaces;
+ __u8 bConfigurationValue;
+ __u8 iConfiguration;
+ __u8 bmAttributes;
+ __u8 MaxPower;
+
+ struct usb_interface_descriptor *interface;
+};
+
+/* String descriptor */
+struct usb_string_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+};
+
+/* Hub descriptor */
+struct usb_hub_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+ __u8 bNbrPorts;
+ __u16 wHubCharacteristics;
+ __u8 bPwrOn2PwrGood;
+ __u8 bHubContrCurrent;
+ /* DeviceRemovable and PortPwrCtrlMask want to be variable-length
+ bitmaps that hold max 256 entries, but for now they're ignored */
+ __u8 filler;
+};
+
+struct usb_device;
+
+struct usb_driver {
+ const char * name;
+ int (*probe)(struct usb_device *);
+ void (*disconnect)(struct usb_device *);
+ struct list_head driver_list;
+};
+
+/*
+ * Pointer to a device endpoint interrupt function -greg
+ * Parameters:
+ * int status - This needs to be defined. Right now each HCD
+ * passes different transfer status bits back. Don't use it
+ * until we come up with a common meaning.
+ * void *buffer - This is a pointer to the data used in this
+ * USB transfer.
+ * void *dev_id - This is a user defined pointer set when the IRQ
+ * is requested that is passed back.
+ */
+typedef int (*usb_device_irq)(int, void *, void *);
+
+struct usb_operations {
+ struct usb_device *(*allocate)(struct usb_device *);
+ int (*deallocate)(struct usb_device *);
+ int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int);
+ int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *);
+};
+
+/*
+ * Allocated per bus we have
+ */
+struct usb_bus {
+ struct usb_devmap devmap; /* Device map */
+ struct usb_operations *op; /* Operations (specific to the HC) */
+ struct usb_device *root_hub; /* Root hub */
+ void *hcpriv; /* Host Controller private data */
+};
+
+
+#define USB_MAXCHILDREN (8)
+
+struct usb_device {
+ int devnum; /* Device number on USB bus */
+ int slow; /* Slow device? */
+ int maxpacketsize; /* Maximum packet size */
+
+ struct usb_bus *bus; /* Bus we're apart of */
+ struct usb_driver *driver; /* Driver */
+ struct usb_device_descriptor descriptor; /* Descriptor */
+ struct usb_config_descriptor *config; /* All of the configs */
+ struct usb_device *parent;
+
+ /*
+ * Child devices - these can be either new devices
+ * (if this is a hub device), or different instances
+ * of this same device.
+ *
+ * Each instance needs its own set of data structuctures.
+ */
+
+ int maxchild; /* Number of ports if hub */
+ struct usb_device *children[USB_MAXCHILDREN];
+
+ void *hcpriv; /* Host Controller private data */
+ void *private; /* Upper layer private data */
+};
+
+extern int usb_register(struct usb_driver *);
+extern void usb_deregister(struct usb_driver *);
+
+extern int usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *);
+
+extern void usb_init_root_hub(struct usb_device *dev);
+extern void usb_connect(struct usb_device *dev);
+extern void usb_disconnect(struct usb_device **);
+extern void usb_device_descriptor(struct usb_device *dev);
+
+extern int usb_parse_configuration(struct usb_device *dev, void *buf, int len);
+
+/*
+ * Calling this entity a "pipe" is glorifying it. A USB pipe
+ * is something embarrassingly simple: it basically consists
+ * of the following information:
+ * - device number (7 bits)
+ * - endpoint number (4 bits)
+ * - current Data0/1 state (1 bit)
+ * - direction (1 bit)
+ * - speed (1 bit)
+ * - max packet size (2 bits: 8, 16, 32 or 64)
+ * - pipe type (2 bits: control, interrupt, bulk, isochronous)
+ *
+ * That's 18 bits. Really. Nothing more. And the USB people have
+ * documented these eighteen bits as some kind of glorious
+ * virtual data structure.
+ *
+ * Let's not fall in that trap. We'll just encode it as a simple
+ * unsigned int. The encoding is:
+ *
+ * - device: bits 8-14
+ * - endpoint: bits 15-18
+ * - Data0/1: bit 19
+ * - direction: bit 7 (0 = Host-to-Device, 1 = Device-to-Host)
+ * - speed: bit 26 (0 = High, 1 = Low Speed)
+ * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64)
+ * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk)
+ *
+ * Why? Because it's arbitrary, and whatever encoding we select is really
+ * up to us. This one happens to share a lot of bit positions with the UCHI
+ * specification, so that much of the uhci driver can just mask the bits
+ * appropriately.
+ */
+
+#define usb_maxpacket(pipe) (8 << ((pipe) & 3))
+#define usb_packetid(pipe) (((pipe) & 0x80) ? 0x69 : 0xE1)
+
+#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f)
+#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf)
+#define usb_pipedata(pipe) (((pipe) >> 19) & 1)
+#define usb_pipeout(pipe) (((pipe) & 0x80) == 0)
+#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)
+
+#define usb_pipetype(pipe) (((pipe) >> 30) & 3)
+#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == 0)
+#define usb_pipeint(pipe) (usb_pipetype((pipe)) == 1)
+#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == 2)
+#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == 3)
+
+#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff)
+
+static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
+{
+ return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26) | dev->maxpacketsize;
+}
+
+static inline unsigned int __default_pipe(struct usb_device *dev)
+{
+ return (dev->slow << 26);
+}
+
+/* Create control pipes.. */
+#define usb_sndctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint) | 0x80)
+#define usb_snddefctrl(dev) ((2 << 30) | __default_pipe(dev))
+#define usb_rcvdefctrl(dev) ((2 << 30) | __default_pipe(dev) | 0x80)
+
+/* Create .. */
+
+/*
+ * Send and receive control messages..
+ */
+void usb_new_device(struct usb_device *dev);
+int usb_set_address(struct usb_device *dev);
+int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned
+char descindex, void *buf, int size);
+int usb_get_device_descriptor(struct usb_device *dev);
+int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size);
+int usb_clear_port_feature(struct usb_device *dev, int port, int feature);
+int usb_set_port_feature(struct usb_device *dev, int port, int feature);
+int usb_get_hub_status(struct usb_device *dev, void *data);
+int usb_get_port_status(struct usb_device *dev, int port, void *data);
+int usb_get_protocol(struct usb_device *dev);
+int usb_set_protocol(struct usb_device *dev, int protocol);
+int usb_set_idle(struct usb_device *dev, int duration, int report_id);
+int usb_set_configuration(struct usb_device *dev, int configuration);
+int usb_get_report(struct usb_device *dev);
+
+/*
+ * Debugging helpers..
+ */
+void usb_show_device_descriptor(struct usb_device_descriptor *);
+void usb_show_config_descriptor(struct usb_config_descriptor *);
+void usb_show_interface_descriptor(struct usb_interface_descriptor *);
+void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *);
+void usb_show_hub_descriptor(struct usb_hub_descriptor *);
+void usb_show_device(struct usb_device *);
+
+/*
+ * Audio parsing helpers
+ */
+
+#ifdef CONFIG_USB_AUDIO
+void usb_audio_interface(struct usb_interface_descriptor *, u8 *);
+void usb_audio_endpoint(struct usb_endpoint_descriptor *, u8 *);
+#else
+extern inline void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data) {}
+extern inline void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data) {}
+#endif
+
+#endif
+