-#\r
-# ReactOS USB-Cromwell Drivers\r
-#\r
-\r
-PATH_TO_TOP = ../../..\r
-\r
-include $(PATH_TO_TOP)/rules.mak\r
-\r
-DRIVERS = core host uhci\r
-\r
-all: $(DRIVERS)\r
-\r
-depends: \r
-\r
-implib: $(DRIVERS:%=%_implib)\r
-\r
-clean: $(DRIVERS:%=%_clean)\r
-\r
-install: $(DRIVERS:%=%_install)\r
-\r
-bootcd: $(DRIVERS:%=%_bootcd)\r
-\r
-.PHONY: all depends implib clean install bootcd\r
-\r
-\r
-#\r
-# USB DRIVERS\r
-#\r
-$(DRIVERS): %:\r
- $(MAKE) -C $*\r
-\r
-$(DRIVERS:%=%_implib): %_implib:\r
- $(MAKE) -C $* implib\r
-\r
-$(DRIVERS:%=%_clean): %_clean:\r
- $(MAKE) -C $* clean\r
-\r
-$(DRIVERS:%=%_install): %_install:\r
- $(MAKE) -C $* install\r
-\r
-$(DRIVERS:%=%_bootcd): %_bootcd:\r
- $(MAKE) -C $* bootcd\r
-\r
-.PHONY: $(DRIVERS) $(DRIVERS:%=%_implib) $(DRIVERS:%=%_clean) $(DRIVERS:%=%_install) $(DRIVERS:%=%_bootcd)\r
-\r
-\r
-etags:\r
- find . -name "*.[ch]" -print | etags --language=c -\r
-\r
-# EOF\r
+#
+# ReactOS USB-Cromwell Drivers
+#
+
+PATH_TO_TOP = ../../..
+
+include $(PATH_TO_TOP)/rules.mak
+
+DRIVERS = core host uhci
+
+all: $(DRIVERS)
+
+depends:
+
+implib: $(DRIVERS:%=%_implib)
+
+clean: $(DRIVERS:%=%_clean)
+
+install: $(DRIVERS:%=%_install)
+
+bootcd: $(DRIVERS:%=%_bootcd)
+
+.PHONY: all depends implib clean install bootcd
+
+
+#
+# USB DRIVERS
+#
+$(DRIVERS): %:
+ $(MAKE) -C $*
+
+$(DRIVERS:%=%_implib): %_implib:
+ $(MAKE) -C $* implib
+
+$(DRIVERS:%=%_clean): %_clean:
+ $(MAKE) -C $* clean
+
+$(DRIVERS:%=%_install): %_install:
+ $(MAKE) -C $* install
+
+$(DRIVERS:%=%_bootcd): %_bootcd:
+ $(MAKE) -C $* bootcd
+
+.PHONY: $(DRIVERS) $(DRIVERS:%=%_implib) $(DRIVERS:%=%_clean) $(DRIVERS:%=%_install) $(DRIVERS:%=%_bootcd)
+
+
+etags:
+ find . -name "*.[ch]" -print | etags --language=c -
+
+# EOF
-/* \r
- * buffer_simple.c -- replacement for usb/core/buffer.c\r
- *\r
- * (c) Georg Acher, georg@acher.org\r
- *\r
- */\r
-\r
-#include "../usb_wrapper.h"\r
-#define __KERNEL__\r
-#define CONFIG_PCI\r
-#include "hcd.h"\r
-\r
-/*------------------------------------------------------------------------*/ \r
-int hcd_buffer_create (struct usb_hcd *hcd)\r
-{\r
- return 0;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void hcd_buffer_destroy (struct usb_hcd *hcd)\r
-{\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void *hcd_buffer_alloc (\r
- struct usb_bus *bus,\r
- size_t size,\r
- int mem_flags,\r
- dma_addr_t *dma\r
-)\r
-{\r
- return kmalloc(size,0);\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void hcd_buffer_free (\r
- struct usb_bus *bus,\r
- size_t size,\r
- void *addr,\r
- dma_addr_t dma\r
-)\r
-{\r
- kfree(addr);\r
-}\r
-\r
+/*
+ * buffer_simple.c -- replacement for usb/core/buffer.c
+ *
+ * (c) Georg Acher, georg@acher.org
+ *
+ */
+
+#include "../usb_wrapper.h"
+#define __KERNEL__
+#define CONFIG_PCI
+#include "hcd.h"
+
+/*------------------------------------------------------------------------*/
+int hcd_buffer_create (struct usb_hcd *hcd)
+{
+ return 0;
+}
+/*------------------------------------------------------------------------*/
+void hcd_buffer_destroy (struct usb_hcd *hcd)
+{
+}
+/*------------------------------------------------------------------------*/
+void *hcd_buffer_alloc (
+ struct usb_bus *bus,
+ size_t size,
+ int mem_flags,
+ dma_addr_t *dma
+)
+{
+ return kmalloc(size,0);
+}
+/*------------------------------------------------------------------------*/
+void hcd_buffer_free (
+ struct usb_bus *bus,
+ size_t size,
+ void *addr,
+ dma_addr_t dma
+)
+{
+ kfree(addr);
+}
+
-#if 0\r
-#include <linux/usb.h>\r
-#include <linux/module.h>\r
-#include <linux/init.h>\r
-#include <linux/slab.h>\r
-#include <asm/byteorder.h>\r
-#else\r
-#include "../usb_wrapper.h"\r
-#endif\r
-\r
-#define USB_MAXALTSETTING 128 /* Hard limit */\r
-#define USB_MAXENDPOINTS 30 /* Hard limit */\r
-\r
-/* these maximums are arbitrary */\r
-#define USB_MAXCONFIG 8\r
-#define USB_ALTSETTINGALLOC 4\r
-#define USB_MAXINTERFACES 32\r
-\r
-static int usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char *buffer, int size)\r
-{\r
- struct usb_descriptor_header *header;\r
- unsigned char *begin;\r
- int parsed = 0, len, numskipped;\r
-\r
- header = (struct usb_descriptor_header *)buffer;\r
-\r
- /* Everything should be fine being passed into here, but we sanity */\r
- /* check JIC */\r
- if (header->bLength > size) {\r
- err("ran out of descriptors parsing");\r
- return -1;\r
- }\r
- \r
- if (header->bDescriptorType != USB_DT_ENDPOINT) {\r
- warn("unexpected descriptor 0x%X, expecting endpoint, 0x%X",\r
- header->bDescriptorType, USB_DT_ENDPOINT);\r
- return parsed;\r
- }\r
-\r
- if (header->bLength == USB_DT_ENDPOINT_AUDIO_SIZE)\r
- memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_AUDIO_SIZE);\r
- else\r
- memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_SIZE);\r
- \r
- le16_to_cpus(&endpoint->desc.wMaxPacketSize);\r
-\r
- buffer += header->bLength;\r
- size -= header->bLength;\r
- parsed += header->bLength;\r
-\r
- /* Skip over the rest of the Class Specific or Vendor Specific */\r
- /* descriptors */\r
- begin = buffer;\r
- numskipped = 0;\r
- while (size >= sizeof(struct usb_descriptor_header)) {\r
- header = (struct usb_descriptor_header *)buffer;\r
-\r
- if (header->bLength < 2) {\r
- err("invalid descriptor length of %d", header->bLength);\r
- return -1;\r
- }\r
-\r
- /* If we find another "proper" descriptor then we're done */\r
- if ((header->bDescriptorType == USB_DT_ENDPOINT) ||\r
- (header->bDescriptorType == USB_DT_INTERFACE) ||\r
- (header->bDescriptorType == USB_DT_CONFIG) ||\r
- (header->bDescriptorType == USB_DT_DEVICE))\r
- break;\r
-\r
- dbg("skipping descriptor 0x%X",\r
- header->bDescriptorType);\r
- numskipped++;\r
-\r
- buffer += header->bLength;\r
- size -= header->bLength;\r
- parsed += header->bLength;\r
- }\r
- if (numskipped)\r
- dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);\r
-\r
- /* Copy any unknown descriptors into a storage area for drivers */\r
- /* to later parse */\r
- len = (int)(buffer - begin);\r
- if (!len) {\r
- endpoint->extra = NULL;\r
- endpoint->extralen = 0;\r
- return parsed;\r
- }\r
-\r
- endpoint->extra = kmalloc(len, GFP_KERNEL);\r
-\r
- if (!endpoint->extra) {\r
- err("couldn't allocate memory for endpoint extra descriptors");\r
- endpoint->extralen = 0;\r
- return parsed;\r
- }\r
-\r
- memcpy(endpoint->extra, begin, len);\r
- endpoint->extralen = len;\r
-\r
- return parsed;\r
-}\r
-\r
-static int usb_parse_interface(struct usb_interface *interface, unsigned char *buffer, int size)\r
-{\r
- int i, len, numskipped, retval, parsed = 0;\r
- struct usb_descriptor_header *header;\r
- struct usb_host_interface *ifp;\r
- unsigned char *begin;\r
-\r
- interface->act_altsetting = 0;\r
- interface->num_altsetting = 0;\r
- interface->max_altsetting = USB_ALTSETTINGALLOC;\r
- device_initialize(&interface->dev);\r
-\r
- interface->altsetting = kmalloc(sizeof(*interface->altsetting) * interface->max_altsetting,\r
- GFP_KERNEL);\r
- \r
- if (!interface->altsetting) {\r
- err("couldn't kmalloc interface->altsetting");\r
- return -1;\r
- }\r
-\r
- while (size > 0) {\r
- struct usb_interface_descriptor *d;\r
- \r
- if (interface->num_altsetting >= interface->max_altsetting) {\r
- struct usb_host_interface *ptr;\r
- int oldmas;\r
-\r
- oldmas = interface->max_altsetting;\r
- interface->max_altsetting += USB_ALTSETTINGALLOC;\r
- if (interface->max_altsetting > USB_MAXALTSETTING) {\r
- warn("too many alternate settings (incr %d max %d)\n",\r
- USB_ALTSETTINGALLOC, USB_MAXALTSETTING);\r
- return -1;\r
- }\r
-\r
- ptr = kmalloc(sizeof(*ptr) * interface->max_altsetting, GFP_KERNEL);\r
- if (ptr == NULL) {\r
- err("couldn't kmalloc interface->altsetting");\r
- return -1;\r
- }\r
- memcpy(ptr, interface->altsetting, sizeof(*interface->altsetting) * oldmas);\r
- kfree(interface->altsetting);\r
- interface->altsetting = ptr;\r
- }\r
-\r
- ifp = interface->altsetting + interface->num_altsetting;\r
- ifp->endpoint = NULL;\r
- ifp->extra = NULL;\r
- ifp->extralen = 0;\r
- interface->num_altsetting++;\r
-\r
- memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE);\r
-\r
- /* Skip over the interface */\r
- buffer += ifp->desc.bLength;\r
- parsed += ifp->desc.bLength;\r
- size -= ifp->desc.bLength;\r
-\r
- begin = buffer;\r
- numskipped = 0;\r
-\r
- /* Skip over any interface, class or vendor descriptors */\r
- while (size >= sizeof(struct usb_descriptor_header)) {\r
- header = (struct usb_descriptor_header *)buffer;\r
-\r
- if (header->bLength < 2) {\r
- err("invalid descriptor length of %d", header->bLength);\r
- return -1;\r
- }\r
-\r
- /* If we find another "proper" descriptor then we're done */\r
- if ((header->bDescriptorType == USB_DT_INTERFACE) ||\r
- (header->bDescriptorType == USB_DT_ENDPOINT) ||\r
- (header->bDescriptorType == USB_DT_CONFIG) ||\r
- (header->bDescriptorType == USB_DT_DEVICE))\r
- break;\r
-\r
- numskipped++;\r
-\r
- buffer += header->bLength;\r
- parsed += header->bLength;\r
- size -= header->bLength;\r
- }\r
-\r
- if (numskipped)\r
- dbg("skipped %d class/vendor specific interface descriptors", numskipped);\r
-\r
- /* Copy any unknown descriptors into a storage area for */\r
- /* drivers to later parse */\r
- len = (int)(buffer - begin);\r
- if (len) {\r
- ifp->extra = kmalloc(len, GFP_KERNEL);\r
-\r
- if (!ifp->extra) {\r
- err("couldn't allocate memory for interface extra descriptors");\r
- ifp->extralen = 0;\r
- return -1;\r
- }\r
- memcpy(ifp->extra, begin, len);\r
- ifp->extralen = len;\r
- }\r
-\r
- /* Did we hit an unexpected descriptor? */\r
- header = (struct usb_descriptor_header *)buffer;\r
- if ((size >= sizeof(struct usb_descriptor_header)) &&\r
- ((header->bDescriptorType == USB_DT_CONFIG) ||\r
- (header->bDescriptorType == USB_DT_DEVICE)))\r
- return parsed;\r
-\r
- if (ifp->desc.bNumEndpoints > USB_MAXENDPOINTS) {\r
- warn("too many endpoints");\r
- return -1;\r
- }\r
-\r
- ifp->endpoint = (struct usb_host_endpoint *)\r
- kmalloc(ifp->desc.bNumEndpoints *\r
- sizeof(struct usb_host_endpoint), GFP_KERNEL);\r
- if (!ifp->endpoint) {\r
- err("out of memory");\r
- return -1; \r
- }\r
-\r
- memset(ifp->endpoint, 0, ifp->desc.bNumEndpoints *\r
- sizeof(struct usb_host_endpoint));\r
- \r
- for (i = 0; i < ifp->desc.bNumEndpoints; i++) {\r
- header = (struct usb_descriptor_header *)buffer;\r
-\r
- if (header->bLength > size) {\r
- err("ran out of descriptors parsing");\r
- return -1;\r
- }\r
- \r
- retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size);\r
- if (retval < 0)\r
- return retval;\r
-\r
- buffer += retval;\r
- parsed += retval;\r
- size -= retval;\r
- }\r
-\r
- /* We check to see if it's an alternate to this one */\r
- d = (struct usb_interface_descriptor *)buffer;\r
- if (size < USB_DT_INTERFACE_SIZE\r
- || d->bDescriptorType != USB_DT_INTERFACE\r
- || !d->bAlternateSetting)\r
- return parsed;\r
- }\r
-\r
- return parsed;\r
-}\r
-\r
-int usb_parse_configuration(struct usb_host_config *config, char *buffer)\r
-{\r
- int i, retval, size;\r
- struct usb_descriptor_header *header;\r
-\r
- memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);\r
- le16_to_cpus(&config->desc.wTotalLength);\r
- size = config->desc.wTotalLength;\r
-\r
- if (config->desc.bNumInterfaces > USB_MAXINTERFACES) {\r
- warn("too many interfaces");\r
- return -1;\r
- }\r
-\r
- config->interface = (struct usb_interface *)\r
- kmalloc(config->desc.bNumInterfaces *\r
- sizeof(struct usb_interface), GFP_KERNEL);\r
- dbg("kmalloc IF %p, numif %i", config->interface, config->desc.bNumInterfaces);\r
- if (!config->interface) {\r
- err("out of memory");\r
- return -1; \r
- }\r
-\r
- memset(config->interface, 0,\r
- config->desc.bNumInterfaces * sizeof(struct usb_interface));\r
-\r
- buffer += config->desc.bLength;\r
- size -= config->desc.bLength;\r
- \r
- config->extra = NULL;\r
- config->extralen = 0;\r
-\r
- for (i = 0; i < config->desc.bNumInterfaces; i++) {\r
- int numskipped, len;\r
- char *begin;\r
-\r
- /* Skip over the rest of the Class Specific or Vendor */\r
- /* Specific descriptors */\r
- begin = buffer;\r
- numskipped = 0;\r
- while (size >= sizeof(struct usb_descriptor_header)) {\r
- header = (struct usb_descriptor_header *)buffer;\r
-\r
- if ((header->bLength > size) || (header->bLength < 2)) {\r
- err("invalid descriptor length of %d", header->bLength);\r
- return -1;\r
- }\r
-\r
- /* If we find another "proper" descriptor then we're done */\r
- if ((header->bDescriptorType == USB_DT_ENDPOINT) ||\r
- (header->bDescriptorType == USB_DT_INTERFACE) ||\r
- (header->bDescriptorType == USB_DT_CONFIG) ||\r
- (header->bDescriptorType == USB_DT_DEVICE))\r
- break;\r
-\r
- dbg("skipping descriptor 0x%X", header->bDescriptorType);\r
- numskipped++;\r
-\r
- buffer += header->bLength;\r
- size -= header->bLength;\r
- }\r
- if (numskipped)\r
- dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);\r
-\r
- /* Copy any unknown descriptors into a storage area for */\r
- /* drivers to later parse */\r
- len = (int)(buffer - begin);\r
- if (len) {\r
- if (config->extralen) {\r
- warn("extra config descriptor");\r
- } else {\r
- config->extra = kmalloc(len, GFP_KERNEL);\r
- if (!config->extra) {\r
- err("couldn't allocate memory for config extra descriptors");\r
- config->extralen = 0;\r
- return -1;\r
- }\r
-\r
- memcpy(config->extra, begin, len);\r
- config->extralen = len;\r
- }\r
- }\r
-\r
- retval = usb_parse_interface(config->interface + i, buffer, size);\r
- if (retval < 0)\r
- return retval;\r
-\r
- buffer += retval;\r
- size -= retval;\r
- }\r
-\r
- return size;\r
-}\r
-\r
-// hub-only!! ... and only exported for reset/reinit path.\r
-// otherwise used internally on disconnect/destroy path\r
-void usb_destroy_configuration(struct usb_device *dev)\r
-{\r
- int c, i, j, k;\r
- \r
- if (!dev->config)\r
- return;\r
-\r
- if (dev->rawdescriptors) {\r
- for (i = 0; i < dev->descriptor.bNumConfigurations; i++)\r
- kfree(dev->rawdescriptors[i]);\r
-\r
- kfree(dev->rawdescriptors);\r
- }\r
-\r
- for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {\r
- struct usb_host_config *cf = &dev->config[c];\r
-\r
- if (!cf->interface)\r
- break;\r
-\r
- for (i = 0; i < cf->desc.bNumInterfaces; i++) {\r
- struct usb_interface *ifp =\r
- &cf->interface[i];\r
- \r
- if (!ifp->altsetting)\r
- break;\r
-\r
- for (j = 0; j < ifp->num_altsetting; j++) {\r
- struct usb_host_interface *as =\r
- &ifp->altsetting[j];\r
- \r
- if(as->extra) {\r
- kfree(as->extra);\r
- }\r
-\r
- if (!as->endpoint)\r
- break;\r
- \r
- for(k = 0; k < as->desc.bNumEndpoints; k++) {\r
- if(as->endpoint[k].extra) {\r
- kfree(as->endpoint[k].extra);\r
- }\r
- } \r
- kfree(as->endpoint);\r
- }\r
-\r
- kfree(ifp->altsetting);\r
- }\r
- kfree(cf->interface);\r
- }\r
- kfree(dev->config);\r
-}\r
-\r
-\r
-// hub-only!! ... and only in reset path, or usb_new_device()\r
-// (used by real hubs and virtual root hubs)\r
-int usb_get_configuration(struct usb_device *dev)\r
-{\r
- int result;\r
- unsigned int cfgno, length;\r
- unsigned char *buffer;\r
- unsigned char *bigbuffer;\r
- struct usb_config_descriptor *desc;\r
-\r
- if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) {\r
- warn("too many configurations");\r
- return -EINVAL;\r
- }\r
-\r
- if (dev->descriptor.bNumConfigurations < 1) {\r
- warn("not enough configurations");\r
- return -EINVAL;\r
- }\r
-\r
- dev->config = (struct usb_host_config *)\r
- kmalloc(dev->descriptor.bNumConfigurations *\r
- sizeof(struct usb_host_config), GFP_KERNEL);\r
- if (!dev->config) {\r
- err("out of memory");\r
- return -ENOMEM; \r
- }\r
- memset(dev->config, 0, dev->descriptor.bNumConfigurations *\r
- sizeof(struct usb_host_config));\r
-\r
- dev->rawdescriptors = (char **)kmalloc(sizeof(char *) *\r
- dev->descriptor.bNumConfigurations, GFP_KERNEL);\r
- if (!dev->rawdescriptors) {\r
- err("out of memory");\r
- return -ENOMEM;\r
- }\r
-\r
- buffer = kmalloc(8, GFP_KERNEL);\r
- if (!buffer) {\r
- err("unable to allocate memory for configuration descriptors");\r
- return -ENOMEM;\r
- }\r
- desc = (struct usb_config_descriptor *)buffer;\r
-\r
- for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {\r
- /* We grab the first 8 bytes so we know how long the whole */\r
- /* configuration is */\r
- result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);\r
- if (result < 8) {\r
- if (result < 0)\r
- err("unable to get descriptor");\r
- else {\r
- err("config descriptor too short (expected %i, got %i)", 8, result);\r
- result = -EINVAL;\r
- }\r
- goto err;\r
- }\r
-\r
- /* Get the full buffer */\r
- length = le16_to_cpu(desc->wTotalLength);\r
-\r
- bigbuffer = kmalloc(length, GFP_KERNEL);\r
- if (!bigbuffer) {\r
- err("unable to allocate memory for configuration descriptors");\r
- result = -ENOMEM;\r
- goto err;\r
- }\r
-\r
- /* Now that we know the length, get the whole thing */\r
- result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);\r
- if (result < 0) {\r
- err("couldn't get all of config descriptors");\r
- kfree(bigbuffer);\r
- goto err;\r
- } \r
- \r
- if (result < length) {\r
- err("config descriptor too short (expected %i, got %i)", length, result);\r
- result = -EINVAL;\r
- kfree(bigbuffer);\r
- goto err;\r
- }\r
-\r
- dev->rawdescriptors[cfgno] = bigbuffer;\r
-\r
- result = usb_parse_configuration(&dev->config[cfgno], bigbuffer);\r
- if (result > 0)\r
- dbg("descriptor data left");\r
- else if (result < 0) {\r
- result = -EINVAL;\r
- goto err;\r
- }\r
- }\r
-\r
- kfree(buffer);\r
- return 0;\r
-err:\r
- kfree(buffer);\r
- dev->descriptor.bNumConfigurations = cfgno;\r
- return result;\r
-}\r
-\r
+#if 0
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/byteorder.h>
+#else
+#include "../usb_wrapper.h"
+#endif
+
+#define USB_MAXALTSETTING 128 /* Hard limit */
+#define USB_MAXENDPOINTS 30 /* Hard limit */
+
+/* these maximums are arbitrary */
+#define USB_MAXCONFIG 8
+#define USB_ALTSETTINGALLOC 4
+#define USB_MAXINTERFACES 32
+
+static int usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char *buffer, int size)
+{
+ struct usb_descriptor_header *header;
+ unsigned char *begin;
+ int parsed = 0, len, numskipped;
+
+ header = (struct usb_descriptor_header *)buffer;
+
+ /* Everything should be fine being passed into here, but we sanity */
+ /* check JIC */
+ if (header->bLength > size) {
+ err("ran out of descriptors parsing");
+ return -1;
+ }
+
+ if (header->bDescriptorType != USB_DT_ENDPOINT) {
+ warn("unexpected descriptor 0x%X, expecting endpoint, 0x%X",
+ header->bDescriptorType, USB_DT_ENDPOINT);
+ return parsed;
+ }
+
+ if (header->bLength == USB_DT_ENDPOINT_AUDIO_SIZE)
+ memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_AUDIO_SIZE);
+ else
+ memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_SIZE);
+
+ le16_to_cpus(&endpoint->desc.wMaxPacketSize);
+
+ buffer += header->bLength;
+ size -= header->bLength;
+ parsed += header->bLength;
+
+ /* Skip over the rest of the Class Specific or Vendor Specific */
+ /* descriptors */
+ begin = buffer;
+ numskipped = 0;
+ while (size >= sizeof(struct usb_descriptor_header)) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if (header->bLength < 2) {
+ err("invalid descriptor length of %d", header->bLength);
+ return -1;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
+ (header->bDescriptorType == USB_DT_INTERFACE) ||
+ (header->bDescriptorType == USB_DT_CONFIG) ||
+ (header->bDescriptorType == USB_DT_DEVICE))
+ break;
+
+ dbg("skipping descriptor 0x%X",
+ header->bDescriptorType);
+ numskipped++;
+
+ buffer += header->bLength;
+ size -= header->bLength;
+ parsed += header->bLength;
+ }
+ if (numskipped)
+ dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
+
+ /* Copy any unknown descriptors into a storage area for drivers */
+ /* to later parse */
+ len = (int)(buffer - begin);
+ if (!len) {
+ endpoint->extra = NULL;
+ endpoint->extralen = 0;
+ return parsed;
+ }
+
+ endpoint->extra = kmalloc(len, GFP_KERNEL);
+
+ if (!endpoint->extra) {
+ err("couldn't allocate memory for endpoint extra descriptors");
+ endpoint->extralen = 0;
+ return parsed;
+ }
+
+ memcpy(endpoint->extra, begin, len);
+ endpoint->extralen = len;
+
+ return parsed;
+}
+
+static int usb_parse_interface(struct usb_interface *interface, unsigned char *buffer, int size)
+{
+ int i, len, numskipped, retval, parsed = 0;
+ struct usb_descriptor_header *header;
+ struct usb_host_interface *ifp;
+ unsigned char *begin;
+
+ interface->act_altsetting = 0;
+ interface->num_altsetting = 0;
+ interface->max_altsetting = USB_ALTSETTINGALLOC;
+ device_initialize(&interface->dev);
+
+ interface->altsetting = kmalloc(sizeof(*interface->altsetting) * interface->max_altsetting,
+ GFP_KERNEL);
+
+ if (!interface->altsetting) {
+ err("couldn't kmalloc interface->altsetting");
+ return -1;
+ }
+
+ while (size > 0) {
+ struct usb_interface_descriptor *d;
+
+ if (interface->num_altsetting >= interface->max_altsetting) {
+ struct usb_host_interface *ptr;
+ int oldmas;
+
+ oldmas = interface->max_altsetting;
+ interface->max_altsetting += USB_ALTSETTINGALLOC;
+ if (interface->max_altsetting > USB_MAXALTSETTING) {
+ warn("too many alternate settings (incr %d max %d)\n",
+ USB_ALTSETTINGALLOC, USB_MAXALTSETTING);
+ return -1;
+ }
+
+ ptr = kmalloc(sizeof(*ptr) * interface->max_altsetting, GFP_KERNEL);
+ if (ptr == NULL) {
+ err("couldn't kmalloc interface->altsetting");
+ return -1;
+ }
+ memcpy(ptr, interface->altsetting, sizeof(*interface->altsetting) * oldmas);
+ kfree(interface->altsetting);
+ interface->altsetting = ptr;
+ }
+
+ ifp = interface->altsetting + interface->num_altsetting;
+ ifp->endpoint = NULL;
+ ifp->extra = NULL;
+ ifp->extralen = 0;
+ interface->num_altsetting++;
+
+ memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE);
+
+ /* Skip over the interface */
+ buffer += ifp->desc.bLength;
+ parsed += ifp->desc.bLength;
+ size -= ifp->desc.bLength;
+
+ begin = buffer;
+ numskipped = 0;
+
+ /* Skip over any interface, class or vendor descriptors */
+ while (size >= sizeof(struct usb_descriptor_header)) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if (header->bLength < 2) {
+ err("invalid descriptor length of %d", header->bLength);
+ return -1;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header->bDescriptorType == USB_DT_INTERFACE) ||
+ (header->bDescriptorType == USB_DT_ENDPOINT) ||
+ (header->bDescriptorType == USB_DT_CONFIG) ||
+ (header->bDescriptorType == USB_DT_DEVICE))
+ break;
+
+ numskipped++;
+
+ buffer += header->bLength;
+ parsed += header->bLength;
+ size -= header->bLength;
+ }
+
+ if (numskipped)
+ dbg("skipped %d class/vendor specific interface descriptors", numskipped);
+
+ /* Copy any unknown descriptors into a storage area for */
+ /* drivers to later parse */
+ len = (int)(buffer - begin);
+ if (len) {
+ ifp->extra = kmalloc(len, GFP_KERNEL);
+
+ if (!ifp->extra) {
+ err("couldn't allocate memory for interface extra descriptors");
+ ifp->extralen = 0;
+ return -1;
+ }
+ memcpy(ifp->extra, begin, len);
+ ifp->extralen = len;
+ }
+
+ /* Did we hit an unexpected descriptor? */
+ header = (struct usb_descriptor_header *)buffer;
+ if ((size >= sizeof(struct usb_descriptor_header)) &&
+ ((header->bDescriptorType == USB_DT_CONFIG) ||
+ (header->bDescriptorType == USB_DT_DEVICE)))
+ return parsed;
+
+ if (ifp->desc.bNumEndpoints > USB_MAXENDPOINTS) {
+ warn("too many endpoints");
+ return -1;
+ }
+
+ ifp->endpoint = (struct usb_host_endpoint *)
+ kmalloc(ifp->desc.bNumEndpoints *
+ sizeof(struct usb_host_endpoint), GFP_KERNEL);
+ if (!ifp->endpoint) {
+ err("out of memory");
+ return -1;
+ }
+
+ memset(ifp->endpoint, 0, ifp->desc.bNumEndpoints *
+ sizeof(struct usb_host_endpoint));
+
+ for (i = 0; i < ifp->desc.bNumEndpoints; i++) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if (header->bLength > size) {
+ err("ran out of descriptors parsing");
+ return -1;
+ }
+
+ retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size);
+ if (retval < 0)
+ return retval;
+
+ buffer += retval;
+ parsed += retval;
+ size -= retval;
+ }
+
+ /* We check to see if it's an alternate to this one */
+ d = (struct usb_interface_descriptor *)buffer;
+ if (size < USB_DT_INTERFACE_SIZE
+ || d->bDescriptorType != USB_DT_INTERFACE
+ || !d->bAlternateSetting)
+ return parsed;
+ }
+
+ return parsed;
+}
+
+int usb_parse_configuration(struct usb_host_config *config, char *buffer)
+{
+ int i, retval, size;
+ struct usb_descriptor_header *header;
+
+ memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
+ le16_to_cpus(&config->desc.wTotalLength);
+ size = config->desc.wTotalLength;
+
+ if (config->desc.bNumInterfaces > USB_MAXINTERFACES) {
+ warn("too many interfaces");
+ return -1;
+ }
+
+ config->interface = (struct usb_interface *)
+ kmalloc(config->desc.bNumInterfaces *
+ sizeof(struct usb_interface), GFP_KERNEL);
+ dbg("kmalloc IF %p, numif %i", config->interface, config->desc.bNumInterfaces);
+ if (!config->interface) {
+ err("out of memory");
+ return -1;
+ }
+
+ memset(config->interface, 0,
+ config->desc.bNumInterfaces * sizeof(struct usb_interface));
+
+ buffer += config->desc.bLength;
+ size -= config->desc.bLength;
+
+ config->extra = NULL;
+ config->extralen = 0;
+
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ int numskipped, len;
+ char *begin;
+
+ /* Skip over the rest of the Class Specific or Vendor */
+ /* Specific descriptors */
+ begin = buffer;
+ numskipped = 0;
+ while (size >= sizeof(struct usb_descriptor_header)) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if ((header->bLength > size) || (header->bLength < 2)) {
+ err("invalid descriptor length of %d", header->bLength);
+ return -1;
+ }
+
+ /* If we find another "proper" descriptor then we're done */
+ if ((header->bDescriptorType == USB_DT_ENDPOINT) ||
+ (header->bDescriptorType == USB_DT_INTERFACE) ||
+ (header->bDescriptorType == USB_DT_CONFIG) ||
+ (header->bDescriptorType == USB_DT_DEVICE))
+ break;
+
+ dbg("skipping descriptor 0x%X", header->bDescriptorType);
+ numskipped++;
+
+ buffer += header->bLength;
+ size -= header->bLength;
+ }
+ if (numskipped)
+ dbg("skipped %d class/vendor specific endpoint descriptors", numskipped);
+
+ /* Copy any unknown descriptors into a storage area for */
+ /* drivers to later parse */
+ len = (int)(buffer - begin);
+ if (len) {
+ if (config->extralen) {
+ warn("extra config descriptor");
+ } else {
+ config->extra = kmalloc(len, GFP_KERNEL);
+ if (!config->extra) {
+ err("couldn't allocate memory for config extra descriptors");
+ config->extralen = 0;
+ return -1;
+ }
+
+ memcpy(config->extra, begin, len);
+ config->extralen = len;
+ }
+ }
+
+ retval = usb_parse_interface(config->interface + i, buffer, size);
+ if (retval < 0)
+ return retval;
+
+ buffer += retval;
+ size -= retval;
+ }
+
+ return size;
+}
+
+// hub-only!! ... and only exported for reset/reinit path.
+// otherwise used internally on disconnect/destroy path
+void usb_destroy_configuration(struct usb_device *dev)
+{
+ int c, i, j, k;
+
+ if (!dev->config)
+ return;
+
+ if (dev->rawdescriptors) {
+ for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
+ kfree(dev->rawdescriptors[i]);
+
+ kfree(dev->rawdescriptors);
+ }
+
+ for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
+ struct usb_host_config *cf = &dev->config[c];
+
+ if (!cf->interface)
+ break;
+
+ for (i = 0; i < cf->desc.bNumInterfaces; i++) {
+ struct usb_interface *ifp =
+ &cf->interface[i];
+
+ if (!ifp->altsetting)
+ break;
+
+ for (j = 0; j < ifp->num_altsetting; j++) {
+ struct usb_host_interface *as =
+ &ifp->altsetting[j];
+
+ if(as->extra) {
+ kfree(as->extra);
+ }
+
+ if (!as->endpoint)
+ break;
+
+ for(k = 0; k < as->desc.bNumEndpoints; k++) {
+ if(as->endpoint[k].extra) {
+ kfree(as->endpoint[k].extra);
+ }
+ }
+ kfree(as->endpoint);
+ }
+
+ kfree(ifp->altsetting);
+ }
+ kfree(cf->interface);
+ }
+ kfree(dev->config);
+}
+
+
+// hub-only!! ... and only in reset path, or usb_new_device()
+// (used by real hubs and virtual root hubs)
+int usb_get_configuration(struct usb_device *dev)
+{
+ int result;
+ unsigned int cfgno, length;
+ unsigned char *buffer;
+ unsigned char *bigbuffer;
+ struct usb_config_descriptor *desc;
+
+ if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) {
+ warn("too many configurations");
+ return -EINVAL;
+ }
+
+ if (dev->descriptor.bNumConfigurations < 1) {
+ warn("not enough configurations");
+ return -EINVAL;
+ }
+
+ dev->config = (struct usb_host_config *)
+ kmalloc(dev->descriptor.bNumConfigurations *
+ sizeof(struct usb_host_config), GFP_KERNEL);
+ if (!dev->config) {
+ err("out of memory");
+ return -ENOMEM;
+ }
+ memset(dev->config, 0, dev->descriptor.bNumConfigurations *
+ sizeof(struct usb_host_config));
+
+ dev->rawdescriptors = (char **)kmalloc(sizeof(char *) *
+ dev->descriptor.bNumConfigurations, GFP_KERNEL);
+ if (!dev->rawdescriptors) {
+ err("out of memory");
+ return -ENOMEM;
+ }
+
+ buffer = kmalloc(8, GFP_KERNEL);
+ if (!buffer) {
+ err("unable to allocate memory for configuration descriptors");
+ return -ENOMEM;
+ }
+ desc = (struct usb_config_descriptor *)buffer;
+
+ for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
+ /* We grab the first 8 bytes so we know how long the whole */
+ /* configuration is */
+ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8);
+ if (result < 8) {
+ if (result < 0)
+ err("unable to get descriptor");
+ else {
+ err("config descriptor too short (expected %i, got %i)", 8, result);
+ result = -EINVAL;
+ }
+ goto err;
+ }
+
+ /* Get the full buffer */
+ length = le16_to_cpu(desc->wTotalLength);
+
+ bigbuffer = kmalloc(length, GFP_KERNEL);
+ if (!bigbuffer) {
+ err("unable to allocate memory for configuration descriptors");
+ result = -ENOMEM;
+ goto err;
+ }
+
+ /* Now that we know the length, get the whole thing */
+ result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length);
+ if (result < 0) {
+ err("couldn't get all of config descriptors");
+ kfree(bigbuffer);
+ goto err;
+ }
+
+ if (result < length) {
+ err("config descriptor too short (expected %i, got %i)", length, result);
+ result = -EINVAL;
+ kfree(bigbuffer);
+ goto err;
+ }
+
+ dev->rawdescriptors[cfgno] = bigbuffer;
+
+ result = usb_parse_configuration(&dev->config[cfgno], bigbuffer);
+ if (result > 0)
+ dbg("descriptor data left");
+ else if (result < 0) {
+ result = -EINVAL;
+ goto err;
+ }
+ }
+
+ kfree(buffer);
+ return 0;
+err:
+ kfree(buffer);
+ dev->descriptor.bNumConfigurations = cfgno;
+ return result;
+}
+
-/*\r
- * (C) Copyright David Brownell 2000-2002\r
- * \r
- * This program is free software; you can redistribute it and/or modify it\r
- * under the terms of the GNU General Public License as published by the\r
- * Free Software Foundation; either version 2 of the License, or (at your\r
- * option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful, but\r
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\r
- * for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software Foundation,\r
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
- */\r
-\r
-#if 0\r
-#include <linux/config.h>\r
-\r
-#ifdef CONFIG_USB_DEBUG\r
- #define DEBUG\r
-#else\r
- #undef DEBUG\r
-#endif\r
-\r
-#include <linux/kernel.h>\r
-#include <linux/module.h>\r
-#include <linux/pci.h>\r
-#include <asm/io.h>\r
-#include <asm/irq.h>\r
-#include <linux/usb.h>\r
-#include "hcd.h"\r
-#else\r
-#define DEBUG\r
-#include "../usb_wrapper.h"\r
-#include "hcd.h"\r
-#endif\r
-\r
-\r
-/* PCI-based HCs are normal, but custom bus glue should be ok */\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* configure so an HC device and id are always provided */\r
-/* always called with process context; sleeping is OK */\r
-\r
-/**\r
- * usb_hcd_pci_probe - initialize PCI-based HCDs\r
- * @dev: USB Host Controller being probed\r
- * @id: pci hotplug id connecting controller to HCD framework\r
- * Context: !in_interrupt()\r
- *\r
- * Allocates basic PCI resources for this USB host controller, and\r
- * then invokes the start() method for the HCD associated with it\r
- * through the hotplug entry's driver_data.\r
- *\r
- * Store this function in the HCD's struct pci_driver as probe().\r
- */\r
-int STDCALL usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)\r
-{\r
- struct hc_driver *driver;\r
- PHYSICAL_ADDRESS resource;\r
- unsigned long len;\r
- void *base;\r
- struct usb_hcd *hcd;\r
- int retval, region;\r
- char buf [8];\r
- //char *bufp = buf;\r
-\r
- printk("usbcore: usb_hcd_pci_probe() called\n");\r
-\r
- if (usb_disabled())\r
- return -ENODEV;\r
-\r
- if (!id || !(driver = (struct hc_driver *) id->driver_data))\r
- return -EINVAL;\r
-\r
- if (pci_enable_device (dev) < 0)\r
- return -ENODEV;\r
-\r
- if (!dev->irq) {\r
- err ("Found HC with no IRQ. Check BIOS/PCI %s setup!",\r
- dev->slot_name);\r
- return -ENODEV;\r
- }\r
-\r
- if (driver->flags & HCD_MEMORY) { // EHCI, OHCI\r
- region = 0;\r
- resource = pci_resource_start (dev, 0);\r
- len = pci_resource_len (dev, 0);\r
- if (!request_mem_region (resource, len, driver->description)) {\r
- dbg ("controller already in use");\r
- return -EBUSY;\r
- }\r
- base = ioremap_nocache (resource, len);\r
- if (base == NULL) {\r
- dbg ("error mapping memory");\r
- retval = -EFAULT;\r
-clean_1:\r
- release_mem_region (resource, len);\r
- err ("init %s fail, %d", dev->slot_name, retval);\r
- return retval;\r
- }\r
-\r
- } else { // UHCI\r
- //resource = 0;\r
- len = 0;\r
- for (region = 0; region < PCI_ROM_RESOURCE; region++) {\r
- if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))\r
- continue;\r
-\r
- resource = pci_resource_start (dev, region);\r
- len = pci_resource_len (dev, region);\r
- if (request_region (resource, len,\r
- driver->description))\r
- break;\r
- }\r
- if (region == PCI_ROM_RESOURCE) {\r
- dbg ("no i/o regions available");\r
- return -EBUSY;\r
- }\r
- base = (void *) (ULONG_PTR)resource.u.LowPart;\r
- }\r
-\r
- // driver->start(), later on, will transfer device from\r
- // control by SMM/BIOS to control by Linux (if needed)\r
-\r
- pci_set_master (dev);\r
-\r
- hcd = driver->hcd_alloc ();\r
- if (hcd == NULL){\r
- dbg ("hcd alloc fail");\r
- retval = -ENOMEM;\r
-clean_2:\r
- if (driver->flags & HCD_MEMORY) {\r
- iounmap (base);\r
- goto clean_1;\r
- } else {\r
- release_region (resource, len);\r
- err ("init %s fail, %d", dev->slot_name, retval);\r
- return retval;\r
- }\r
- }\r
- pci_set_drvdata (dev, hcd);\r
- hcd->driver = driver;\r
- hcd->description = driver->description;\r
- hcd->pdev = dev;\r
- hcd->self.bus_name = dev->slot_name;\r
- hcd->product_desc = dev->dev.name;\r
- hcd->self.controller = &dev->dev;\r
- hcd->controller = hcd->self.controller;\r
-\r
- if ((retval = hcd_buffer_create (hcd)) != 0) {\r
-clean_3:\r
- driver->hcd_free (hcd);\r
- goto clean_2;\r
- }\r
-\r
- dev_info (hcd->controller, "%s\n", hcd->product_desc);\r
-\r
-#ifndef __sparc__\r
- sprintf (buf, "%d", dev->irq);\r
-#else\r
- bufp = __irq_itoa(dev->irq);\r
-#endif\r
- if (request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, hcd->description, hcd)\r
- != 0) {\r
- dev_err (hcd->controller,\r
- "request interrupt %s failed\n", buf);\r
- retval = -EBUSY;\r
- goto clean_3;\r
- }\r
- hcd->irq = dev->irq;\r
-\r
- hcd->regs = base;\r
- hcd->region = region;\r
- dev_info (hcd->controller, "irq %s, %s %p\n", buf,\r
- (driver->flags & HCD_MEMORY) ? "pci mem" : "io base",\r
- base);\r
-\r
- usb_bus_init (&hcd->self);\r
- hcd->self.op = &usb_hcd_operations;\r
- hcd->self.hcpriv = (void *) hcd;\r
-\r
- INIT_LIST_HEAD (&hcd->dev_list);\r
-\r
- usb_register_bus (&hcd->self);\r
-\r
- if ((retval = driver->start (hcd)) < 0)\r
- usb_hcd_pci_remove (dev);\r
-\r
- return retval;\r
-} \r
-EXPORT_SYMBOL (usb_hcd_pci_probe);\r
-\r
-\r
-/* may be called without controller electrically present */\r
-/* may be called with controller, bus, and devices active */\r
-\r
-/**\r
- * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs\r
- * @dev: USB Host Controller being removed\r
- * Context: !in_interrupt()\r
- *\r
- * Reverses the effect of usb_hcd_pci_probe(), first invoking\r
- * the HCD's stop() method. It is always called from a thread\r
- * context, normally "rmmod", "apmd", or something similar.\r
- *\r
- * Store this function in the HCD's struct pci_driver as remove().\r
- */\r
-void STDCALL usb_hcd_pci_remove (struct pci_dev *dev)\r
-{\r
- struct usb_hcd *hcd;\r
- struct usb_device *hub;\r
-\r
- hcd = pci_get_drvdata(dev);\r
- if (!hcd)\r
- return;\r
- dev_info (hcd->controller, "remove, state %x\n", hcd->state);\r
-\r
- if (in_interrupt ())\r
- BUG ();\r
-\r
- hub = hcd->self.root_hub;\r
- hcd->state = USB_STATE_QUIESCING;\r
-\r
- dev_dbg (hcd->controller, "roothub graceful disconnect\n");\r
- usb_disconnect (&hub);\r
-\r
- hcd->driver->stop (hcd);\r
- hcd_buffer_destroy (hcd);\r
- hcd->state = USB_STATE_HALT;\r
- pci_set_drvdata (dev, 0);\r
-\r
- free_irq (hcd->irq, hcd);\r
- if (hcd->driver->flags & HCD_MEMORY) {\r
- iounmap (hcd->regs);\r
- release_mem_region (pci_resource_start (dev, 0),\r
- pci_resource_len (dev, 0));\r
- } else {\r
- release_region (pci_resource_start (dev, hcd->region),\r
- pci_resource_len (dev, hcd->region));\r
- }\r
-\r
- usb_deregister_bus (&hcd->self);\r
- if (atomic_read (&hcd->self.refcnt) != 1) {\r
- dev_warn (hcd->controller,\r
- "dangling refs (%d) to bus %d!\n",\r
- atomic_read (&hcd->self.refcnt) - 1,\r
- hcd->self.busnum);\r
- }\r
- hcd->driver->hcd_free (hcd);\r
-}\r
-EXPORT_SYMBOL (usb_hcd_pci_remove);\r
-\r
-\r
-#ifdef CONFIG_PM\r
-\r
-/*\r
- * Some "sleep" power levels imply updating struct usb_driver\r
- * to include a callback asking hcds to do their bit by checking\r
- * if all the drivers can suspend. Gets involved with remote wakeup.\r
- *\r
- * If there are pending urbs, then HCs will need to access memory,\r
- * causing extra power drain. New sleep()/wakeup() PM calls might\r
- * be needed, beyond PCI suspend()/resume(). The root hub timer\r
- * still be accessing memory though ...\r
- *\r
- * FIXME: USB should have some power budgeting support working with\r
- * all kinds of hubs.\r
- *\r
- * FIXME: This assumes only D0->D3 suspend and D3->D0 resume.\r
- * D1 and D2 states should do something, yes?\r
- *\r
- * FIXME: Should provide generic enable_wake(), calling pci_enable_wake()\r
- * for all supported states, so that USB remote wakeup can work for any\r
- * devices that support it (and are connected via powered hubs).\r
- *\r
- * FIXME: resume doesn't seem to work right any more...\r
- */\r
-\r
-\r
-// 2.4 kernels have issued concurrent resumes (w/APM)\r
-// we defend against that error; PCI doesn't yet.\r
-\r
-/**\r
- * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD\r
- * @dev: USB Host Controller being suspended\r
- *\r
- * Store this function in the HCD's struct pci_driver as suspend().\r
- */\r
-int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)\r
-{\r
- struct usb_hcd *hcd;\r
- int retval;\r
-\r
- hcd = pci_get_drvdata(dev);\r
- dev_info (hcd->controller, "suspend to state %d\n", state);\r
-\r
- pci_save_state (dev, hcd->pci_state);\r
-\r
- // FIXME for all connected devices, leaf-to-root:\r
- // driver->suspend()\r
- // proposed "new 2.5 driver model" will automate that\r
-\r
- /* driver may want to disable DMA etc */\r
- retval = hcd->driver->suspend (hcd, state);\r
- hcd->state = USB_STATE_SUSPENDED;\r
-\r
- pci_set_power_state (dev, state);\r
- return retval;\r
-}\r
-EXPORT_SYMBOL (usb_hcd_pci_suspend);\r
-\r
-/**\r
- * usb_hcd_pci_resume - power management resume of a PCI-based HCD\r
- * @dev: USB Host Controller being resumed\r
- *\r
- * Store this function in the HCD's struct pci_driver as resume().\r
- */\r
-int usb_hcd_pci_resume (struct pci_dev *dev)\r
-{\r
- struct usb_hcd *hcd;\r
- int retval;\r
-\r
- hcd = pci_get_drvdata(dev);\r
- dev_info (hcd->controller, "resume\n");\r
-\r
- /* guard against multiple resumes (APM bug?) */\r
- atomic_inc (&hcd->resume_count);\r
- if (atomic_read (&hcd->resume_count) != 1) {\r
- dev_err (hcd->controller, "concurrent PCI resumes\n");\r
- retval = 0;\r
- goto done;\r
- }\r
-\r
- retval = -EBUSY;\r
- if (hcd->state != USB_STATE_SUSPENDED) {\r
- dev_dbg (hcd->controller, "can't resume, not suspended!\n");\r
- goto done;\r
- }\r
- hcd->state = USB_STATE_RESUMING;\r
-\r
- pci_set_power_state (dev, 0);\r
- pci_restore_state (dev, hcd->pci_state);\r
-\r
- retval = hcd->driver->resume (hcd);\r
- if (!HCD_IS_RUNNING (hcd->state)) {\r
- dev_dbg (hcd->controller, "resume fail, retval %d\n", retval);\r
- usb_hc_died (hcd);\r
-// FIXME: recover, reset etc.\r
- } else {\r
- // FIXME for all connected devices, root-to-leaf:\r
- // driver->resume ();\r
- // proposed "new 2.5 driver model" will automate that\r
- }\r
-\r
-done:\r
- atomic_dec (&hcd->resume_count);\r
- return retval;\r
-}\r
-EXPORT_SYMBOL (usb_hcd_pci_resume);\r
-\r
-#endif /* CONFIG_PM */\r
-\r
-\r
+/*
+ * (C) Copyright David Brownell 2000-2002
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if 0
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/usb.h>
+#include "hcd.h"
+#else
+#define DEBUG
+#include "../usb_wrapper.h"
+#include "hcd.h"
+#endif
+
+
+/* PCI-based HCs are normal, but custom bus glue should be ok */
+
+
+/*-------------------------------------------------------------------------*/
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_pci_probe - initialize PCI-based HCDs
+ * @dev: USB Host Controller being probed
+ * @id: pci hotplug id connecting controller to HCD framework
+ * Context: !in_interrupt()
+ *
+ * Allocates basic PCI resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+int STDCALL usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
+{
+ struct hc_driver *driver;
+ PHYSICAL_ADDRESS resource;
+ unsigned long len;
+ void *base;
+ struct usb_hcd *hcd;
+ int retval, region;
+ char buf [8];
+ //char *bufp = buf;
+
+ printk("usbcore: usb_hcd_pci_probe() called\n");
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ if (!id || !(driver = (struct hc_driver *) id->driver_data))
+ return -EINVAL;
+
+ if (pci_enable_device (dev) < 0)
+ return -ENODEV;
+
+ if (!dev->irq) {
+ err ("Found HC with no IRQ. Check BIOS/PCI %s setup!",
+ dev->slot_name);
+ return -ENODEV;
+ }
+
+ if (driver->flags & HCD_MEMORY) { // EHCI, OHCI
+ region = 0;
+ resource = pci_resource_start (dev, 0);
+ len = pci_resource_len (dev, 0);
+ if (!request_mem_region (resource, len, driver->description)) {
+ dbg ("controller already in use");
+ return -EBUSY;
+ }
+ base = ioremap_nocache (resource, len);
+ if (base == NULL) {
+ dbg ("error mapping memory");
+ retval = -EFAULT;
+clean_1:
+ release_mem_region (resource, len);
+ err ("init %s fail, %d", dev->slot_name, retval);
+ return retval;
+ }
+
+ } else { // UHCI
+ //resource = 0;
+ len = 0;
+ for (region = 0; region < PCI_ROM_RESOURCE; region++) {
+ if (!(pci_resource_flags (dev, region) & IORESOURCE_IO))
+ continue;
+
+ resource = pci_resource_start (dev, region);
+ len = pci_resource_len (dev, region);
+ if (request_region (resource, len,
+ driver->description))
+ break;
+ }
+ if (region == PCI_ROM_RESOURCE) {
+ dbg ("no i/o regions available");
+ return -EBUSY;
+ }
+ base = (void *) (ULONG_PTR)resource.u.LowPart;
+ }
+
+ // driver->start(), later on, will transfer device from
+ // control by SMM/BIOS to control by Linux (if needed)
+
+ pci_set_master (dev);
+
+ hcd = driver->hcd_alloc ();
+ if (hcd == NULL){
+ dbg ("hcd alloc fail");
+ retval = -ENOMEM;
+clean_2:
+ if (driver->flags & HCD_MEMORY) {
+ iounmap (base);
+ goto clean_1;
+ } else {
+ release_region (resource, len);
+ err ("init %s fail, %d", dev->slot_name, retval);
+ return retval;
+ }
+ }
+ pci_set_drvdata (dev, hcd);
+ hcd->driver = driver;
+ hcd->description = driver->description;
+ hcd->pdev = dev;
+ hcd->self.bus_name = dev->slot_name;
+ hcd->product_desc = dev->dev.name;
+ hcd->self.controller = &dev->dev;
+ hcd->controller = hcd->self.controller;
+
+ if ((retval = hcd_buffer_create (hcd)) != 0) {
+clean_3:
+ driver->hcd_free (hcd);
+ goto clean_2;
+ }
+
+ dev_info (hcd->controller, "%s\n", hcd->product_desc);
+
+#ifndef __sparc__
+ sprintf (buf, "%d", dev->irq);
+#else
+ bufp = __irq_itoa(dev->irq);
+#endif
+ if (request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, hcd->description, hcd)
+ != 0) {
+ dev_err (hcd->controller,
+ "request interrupt %s failed\n", buf);
+ retval = -EBUSY;
+ goto clean_3;
+ }
+ hcd->irq = dev->irq;
+
+ hcd->regs = base;
+ hcd->region = region;
+ dev_info (hcd->controller, "irq %s, %s %p\n", buf,
+ (driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
+ base);
+
+ usb_bus_init (&hcd->self);
+ hcd->self.op = &usb_hcd_operations;
+ hcd->self.hcpriv = (void *) hcd;
+
+ INIT_LIST_HEAD (&hcd->dev_list);
+
+ usb_register_bus (&hcd->self);
+
+ if ((retval = driver->start (hcd)) < 0)
+ usb_hcd_pci_remove (dev);
+
+ return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_probe);
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_pci_remove - shutdown processing for PCI-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_pci_probe(), first invoking
+ * the HCD's stop() method. It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ * Store this function in the HCD's struct pci_driver as remove().
+ */
+void STDCALL usb_hcd_pci_remove (struct pci_dev *dev)
+{
+ struct usb_hcd *hcd;
+ struct usb_device *hub;
+
+ hcd = pci_get_drvdata(dev);
+ if (!hcd)
+ return;
+ dev_info (hcd->controller, "remove, state %x\n", hcd->state);
+
+ if (in_interrupt ())
+ BUG ();
+
+ hub = hcd->self.root_hub;
+ hcd->state = USB_STATE_QUIESCING;
+
+ dev_dbg (hcd->controller, "roothub graceful disconnect\n");
+ usb_disconnect (&hub);
+
+ hcd->driver->stop (hcd);
+ hcd_buffer_destroy (hcd);
+ hcd->state = USB_STATE_HALT;
+ pci_set_drvdata (dev, 0);
+
+ free_irq (hcd->irq, hcd);
+ if (hcd->driver->flags & HCD_MEMORY) {
+ iounmap (hcd->regs);
+ release_mem_region (pci_resource_start (dev, 0),
+ pci_resource_len (dev, 0));
+ } else {
+ release_region (pci_resource_start (dev, hcd->region),
+ pci_resource_len (dev, hcd->region));
+ }
+
+ usb_deregister_bus (&hcd->self);
+ if (atomic_read (&hcd->self.refcnt) != 1) {
+ dev_warn (hcd->controller,
+ "dangling refs (%d) to bus %d!\n",
+ atomic_read (&hcd->self.refcnt) - 1,
+ hcd->self.busnum);
+ }
+ hcd->driver->hcd_free (hcd);
+}
+EXPORT_SYMBOL (usb_hcd_pci_remove);
+
+
+#ifdef CONFIG_PM
+
+/*
+ * Some "sleep" power levels imply updating struct usb_driver
+ * to include a callback asking hcds to do their bit by checking
+ * if all the drivers can suspend. Gets involved with remote wakeup.
+ *
+ * If there are pending urbs, then HCs will need to access memory,
+ * causing extra power drain. New sleep()/wakeup() PM calls might
+ * be needed, beyond PCI suspend()/resume(). The root hub timer
+ * still be accessing memory though ...
+ *
+ * FIXME: USB should have some power budgeting support working with
+ * all kinds of hubs.
+ *
+ * FIXME: This assumes only D0->D3 suspend and D3->D0 resume.
+ * D1 and D2 states should do something, yes?
+ *
+ * FIXME: Should provide generic enable_wake(), calling pci_enable_wake()
+ * for all supported states, so that USB remote wakeup can work for any
+ * devices that support it (and are connected via powered hubs).
+ *
+ * FIXME: resume doesn't seem to work right any more...
+ */
+
+
+// 2.4 kernels have issued concurrent resumes (w/APM)
+// we defend against that error; PCI doesn't yet.
+
+/**
+ * usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
+ * @dev: USB Host Controller being suspended
+ *
+ * Store this function in the HCD's struct pci_driver as suspend().
+ */
+int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state)
+{
+ struct usb_hcd *hcd;
+ int retval;
+
+ hcd = pci_get_drvdata(dev);
+ dev_info (hcd->controller, "suspend to state %d\n", state);
+
+ pci_save_state (dev, hcd->pci_state);
+
+ // FIXME for all connected devices, leaf-to-root:
+ // driver->suspend()
+ // proposed "new 2.5 driver model" will automate that
+
+ /* driver may want to disable DMA etc */
+ retval = hcd->driver->suspend (hcd, state);
+ hcd->state = USB_STATE_SUSPENDED;
+
+ pci_set_power_state (dev, state);
+ return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_suspend);
+
+/**
+ * usb_hcd_pci_resume - power management resume of a PCI-based HCD
+ * @dev: USB Host Controller being resumed
+ *
+ * Store this function in the HCD's struct pci_driver as resume().
+ */
+int usb_hcd_pci_resume (struct pci_dev *dev)
+{
+ struct usb_hcd *hcd;
+ int retval;
+
+ hcd = pci_get_drvdata(dev);
+ dev_info (hcd->controller, "resume\n");
+
+ /* guard against multiple resumes (APM bug?) */
+ atomic_inc (&hcd->resume_count);
+ if (atomic_read (&hcd->resume_count) != 1) {
+ dev_err (hcd->controller, "concurrent PCI resumes\n");
+ retval = 0;
+ goto done;
+ }
+
+ retval = -EBUSY;
+ if (hcd->state != USB_STATE_SUSPENDED) {
+ dev_dbg (hcd->controller, "can't resume, not suspended!\n");
+ goto done;
+ }
+ hcd->state = USB_STATE_RESUMING;
+
+ pci_set_power_state (dev, 0);
+ pci_restore_state (dev, hcd->pci_state);
+
+ retval = hcd->driver->resume (hcd);
+ if (!HCD_IS_RUNNING (hcd->state)) {
+ dev_dbg (hcd->controller, "resume fail, retval %d\n", retval);
+ usb_hc_died (hcd);
+// FIXME: recover, reset etc.
+ } else {
+ // FIXME for all connected devices, root-to-leaf:
+ // driver->resume ();
+ // proposed "new 2.5 driver model" will automate that
+ }
+
+done:
+ atomic_dec (&hcd->resume_count);
+ return retval;
+}
+EXPORT_SYMBOL (usb_hcd_pci_resume);
+
+#endif /* CONFIG_PM */
+
+
-/*\r
- * (C) Copyright Linus Torvalds 1999\r
- * (C) Copyright Johannes Erdfelt 1999-2001\r
- * (C) Copyright Andreas Gal 1999\r
- * (C) Copyright Gregory P. Smith 1999\r
- * (C) Copyright Deti Fliegl 1999\r
- * (C) Copyright Randy Dunlap 2000\r
- * (C) Copyright David Brownell 2000-2002\r
- * \r
- * This program is free software; you can redistribute it and/or modify it\r
- * under the terms of the GNU General Public License as published by the\r
- * Free Software Foundation; either version 2 of the License, or (at your\r
- * option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful, but\r
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\r
- * for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software Foundation,\r
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
- */\r
-\r
-#if 0\r
-#include <linux/config.h>\r
-\r
-#ifdef CONFIG_USB_DEBUG\r
-#define DEBUG\r
-#endif\r
-\r
-#include <linux/module.h>\r
-#include <linux/version.h>\r
-#include <linux/kernel.h>\r
-#include <linux/slab.h>\r
-#include <linux/completion.h>\r
-#include <linux/uts.h> /* for UTS_SYSNAME */\r
-#include <linux/pci.h> /* for hcd->pdev and dma addressing */\r
-#include <linux/dma-mapping.h>\r
-#include <asm/byteorder.h>\r
-\r
-#include <linux/usb.h>\r
-#else\r
-#include "../usb_wrapper.h"\r
-//#define DEBUG\r
-#endif\r
-\r
-#include "hcd.h"\r
-\r
-// #define USB_BANDWIDTH_MESSAGES\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * USB Host Controller Driver framework\r
- *\r
- * Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing\r
- * HCD-specific behaviors/bugs.\r
- *\r
- * This does error checks, tracks devices and urbs, and delegates to a\r
- * "hc_driver" only for code (and data) that really needs to know about\r
- * hardware differences. That includes root hub registers, i/o queues,\r
- * and so on ... but as little else as possible.\r
- *\r
- * Shared code includes most of the "root hub" code (these are emulated,\r
- * though each HC's hardware works differently) and PCI glue, plus request\r
- * tracking overhead. The HCD code should only block on spinlocks or on\r
- * hardware handshaking; blocking on software events (such as other kernel\r
- * threads releasing resources, or completing actions) is all generic.\r
- *\r
- * Happens the USB 2.0 spec says this would be invisible inside the "USBD",\r
- * and includes mostly a "HCDI" (HCD Interface) along with some APIs used\r
- * only by the hub driver ... and that neither should be seen or used by\r
- * usb client device drivers.\r
- *\r
- * Contributors of ideas or unattributed patches include: David Brownell,\r
- * Roman Weissgaerber, Rory Bolt, Greg Kroah-Hartman, ...\r
- *\r
- * HISTORY:\r
- * 2002-02-21 Pull in most of the usb_bus support from usb.c; some\r
- * associated cleanup. "usb_hcd" still != "usb_bus".\r
- * 2001-12-12 Initial patch version for Linux 2.5.1 kernel.\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* host controllers we manage */\r
-LIST_HEAD (usb_bus_list);\r
-EXPORT_SYMBOL_GPL (usb_bus_list);\r
-\r
-/* used when allocating bus numbers */\r
-#define USB_MAXBUS 64\r
-struct usb_busmap {\r
- unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];\r
-};\r
-static struct usb_busmap busmap;\r
-\r
-/* used when updating list of hcds */\r
-DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */\r
-EXPORT_SYMBOL_GPL (usb_bus_list_lock);\r
-\r
-/* used when updating hcd data */\r
-static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * Sharable chunks of root hub code.\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)\r
-#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)\r
-\r
-/* usb 2.0 root hub device descriptor */\r
-static const u8 usb2_rh_dev_descriptor [18] = {\r
- 0x12, /* __u8 bLength; */\r
- 0x01, /* __u8 bDescriptorType; Device */\r
- 0x00, 0x02, /* __u16 bcdUSB; v2.0 */\r
-\r
- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */\r
- 0x00, /* __u8 bDeviceSubClass; */\r
- 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/\r
- 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */\r
-\r
- 0x00, 0x00, /* __u16 idVendor; */\r
- 0x00, 0x00, /* __u16 idProduct; */\r
- KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */\r
-\r
- 0x03, /* __u8 iManufacturer; */\r
- 0x02, /* __u8 iProduct; */\r
- 0x01, /* __u8 iSerialNumber; */\r
- 0x01 /* __u8 bNumConfigurations; */\r
-};\r
-\r
-/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */\r
-\r
-/* usb 1.1 root hub device descriptor */\r
-static const u8 usb11_rh_dev_descriptor [18] = {\r
- 0x12, /* __u8 bLength; */\r
- 0x01, /* __u8 bDescriptorType; Device */\r
- 0x10, 0x01, /* __u16 bcdUSB; v1.1 */\r
-\r
- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */\r
- 0x00, /* __u8 bDeviceSubClass; */\r
- 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */\r
- 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */\r
-\r
- 0x00, 0x00, /* __u16 idVendor; */\r
- 0x00, 0x00, /* __u16 idProduct; */\r
- KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */\r
-\r
- 0x03, /* __u8 iManufacturer; */\r
- 0x02, /* __u8 iProduct; */\r
- 0x01, /* __u8 iSerialNumber; */\r
- 0x01 /* __u8 bNumConfigurations; */\r
-};\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* Configuration descriptors for our root hubs */\r
-\r
-static const u8 fs_rh_config_descriptor [] = {\r
-\r
- /* one configuration */\r
- 0x09, /* __u8 bLength; */\r
- 0x02, /* __u8 bDescriptorType; Configuration */\r
- 0x19, 0x00, /* __u16 wTotalLength; */\r
- 0x01, /* __u8 bNumInterfaces; (1) */\r
- 0x01, /* __u8 bConfigurationValue; */\r
- 0x00, /* __u8 iConfiguration; */\r
- 0x40, /* __u8 bmAttributes; \r
- Bit 7: Bus-powered,\r
- 6: Self-powered,\r
- 5 Remote-wakwup,\r
- 4..0: resvd */\r
- 0x00, /* __u8 MaxPower; */\r
- \r
- /* USB 1.1:\r
- * USB 2.0, single TT organization (mandatory):\r
- * one interface, protocol 0\r
- *\r
- * USB 2.0, multiple TT organization (optional):\r
- * two interfaces, protocols 1 (like single TT)\r
- * and 2 (multiple TT mode) ... config is\r
- * sometimes settable\r
- * NOT IMPLEMENTED\r
- */\r
-\r
- /* one interface */\r
- 0x09, /* __u8 if_bLength; */\r
- 0x04, /* __u8 if_bDescriptorType; Interface */\r
- 0x00, /* __u8 if_bInterfaceNumber; */\r
- 0x00, /* __u8 if_bAlternateSetting; */\r
- 0x01, /* __u8 if_bNumEndpoints; */\r
- 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */\r
- 0x00, /* __u8 if_bInterfaceSubClass; */\r
- 0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */\r
- 0x00, /* __u8 if_iInterface; */\r
- \r
- /* one endpoint (status change endpoint) */\r
- 0x07, /* __u8 ep_bLength; */\r
- 0x05, /* __u8 ep_bDescriptorType; Endpoint */\r
- 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */\r
- 0x03, /* __u8 ep_bmAttributes; Interrupt */\r
- 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */\r
- 0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */\r
-};\r
-\r
-static const u8 hs_rh_config_descriptor [] = {\r
-\r
- /* one configuration */\r
- 0x09, /* __u8 bLength; */\r
- 0x02, /* __u8 bDescriptorType; Configuration */\r
- 0x19, 0x00, /* __u16 wTotalLength; */\r
- 0x01, /* __u8 bNumInterfaces; (1) */\r
- 0x01, /* __u8 bConfigurationValue; */\r
- 0x00, /* __u8 iConfiguration; */\r
- 0x40, /* __u8 bmAttributes; \r
- Bit 7: Bus-powered,\r
- 6: Self-powered,\r
- 5 Remote-wakwup,\r
- 4..0: resvd */\r
- 0x00, /* __u8 MaxPower; */\r
- \r
- /* USB 1.1:\r
- * USB 2.0, single TT organization (mandatory):\r
- * one interface, protocol 0\r
- *\r
- * USB 2.0, multiple TT organization (optional):\r
- * two interfaces, protocols 1 (like single TT)\r
- * and 2 (multiple TT mode) ... config is\r
- * sometimes settable\r
- * NOT IMPLEMENTED\r
- */\r
-\r
- /* one interface */\r
- 0x09, /* __u8 if_bLength; */\r
- 0x04, /* __u8 if_bDescriptorType; Interface */\r
- 0x00, /* __u8 if_bInterfaceNumber; */\r
- 0x00, /* __u8 if_bAlternateSetting; */\r
- 0x01, /* __u8 if_bNumEndpoints; */\r
- 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */\r
- 0x00, /* __u8 if_bInterfaceSubClass; */\r
- 0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */\r
- 0x00, /* __u8 if_iInterface; */\r
- \r
- /* one endpoint (status change endpoint) */\r
- 0x07, /* __u8 ep_bLength; */\r
- 0x05, /* __u8 ep_bDescriptorType; Endpoint */\r
- 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */\r
- 0x03, /* __u8 ep_bmAttributes; Interrupt */\r
- 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */\r
- 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */\r
-};\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * helper routine for returning string descriptors in UTF-16LE\r
- * input can actually be ISO-8859-1; ASCII is its 7-bit subset\r
- */\r
-static int ascii2utf (char *s, u8 *utf, int utfmax)\r
-{\r
- int retval;\r
-\r
- for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {\r
- *utf++ = *s++;\r
- *utf++ = 0;\r
- }\r
- return retval;\r
-}\r
-\r
-/*\r
- * rh_string - provides manufacturer, product and serial strings for root hub\r
- * @id: the string ID number (1: serial number, 2: product, 3: vendor)\r
- * @hcd: the host controller for this root hub\r
- * @type: string describing our driver \r
- * @data: return packet in UTF-16 LE\r
- * @len: length of the return packet\r
- *\r
- * Produces either a manufacturer, product or serial number string for the\r
- * virtual root hub device.\r
- */\r
-static int rh_string (\r
- int id,\r
- struct usb_hcd *hcd,\r
- u8 *data,\r
- int len\r
-) {\r
- char buf [100];\r
-\r
- // language ids\r
- if (id == 0) {\r
- *data++ = 4; *data++ = 3; /* 4 bytes string data */\r
- *data++ = 0x09; *data++ = 0x04; /* MSFT-speak for "en-us" */\r
- return 4;\r
-\r
- // serial number\r
- } else if (id == 1) {\r
- strcpy (buf, hcd->self.bus_name);\r
-\r
- // product description\r
- } else if (id == 2) {\r
- strcpy (buf, hcd->product_desc);\r
-\r
- // id 3 == vendor description\r
- } else if (id == 3) {\r
- sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE,\r
- hcd->description);\r
-\r
- // unsupported IDs --> "protocol stall"\r
- } else\r
- return 0;\r
-\r
- data [0] = 2 * (strlen (buf) + 1);\r
- data [1] = 3; /* type == string */\r
- return 2 + ascii2utf (buf, data + 2, len - 2);\r
-}\r
-\r
-\r
-/* Root hub control transfers execute synchronously */\r
-static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)\r
-{\r
- struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;\r
- u16 typeReq, wValue, wIndex, wLength;\r
- const u8 *bufp = 0;\r
- u8 *ubuf = urb->transfer_buffer;\r
- int len = 0;\r
- //unsigned long flags;\r
-\r
- typeReq = (cmd->bRequestType << 8) | cmd->bRequest;\r
- wValue = le16_to_cpu (cmd->wValue);\r
- wIndex = le16_to_cpu (cmd->wIndex);\r
- wLength = le16_to_cpu (cmd->wLength);\r
-\r
- if (wLength > urb->transfer_buffer_length)\r
- goto error;\r
-\r
- /* set up for success */\r
- urb->status = 0;\r
- urb->actual_length = wLength;\r
- switch (typeReq) {\r
-\r
- /* DEVICE REQUESTS */\r
-\r
- case DeviceRequest | USB_REQ_GET_STATUS:\r
- // DEVICE_REMOTE_WAKEUP\r
- ubuf [0] = 1; // selfpowered\r
- ubuf [1] = 0;\r
- /* FALLTHROUGH */\r
- case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:\r
- case DeviceOutRequest | USB_REQ_SET_FEATURE:\r
- dev_dbg (hcd->controller, "no device features yet yet\n");\r
- break;\r
- case DeviceRequest | USB_REQ_GET_CONFIGURATION:\r
- ubuf [0] = 1;\r
- /* FALLTHROUGH */\r
- case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:\r
- break;\r
- case DeviceRequest | USB_REQ_GET_DESCRIPTOR:\r
- switch (wValue & 0xff00) {\r
- case USB_DT_DEVICE << 8:\r
- if (hcd->driver->flags & HCD_USB2)\r
- bufp = usb2_rh_dev_descriptor;\r
- else if (hcd->driver->flags & HCD_USB11)\r
- bufp = usb11_rh_dev_descriptor;\r
- else\r
- goto error;\r
- len = 18;\r
- break;\r
- case USB_DT_CONFIG << 8:\r
- if (hcd->driver->flags & HCD_USB2) {\r
- bufp = hs_rh_config_descriptor;\r
- len = sizeof hs_rh_config_descriptor;\r
- } else {\r
- bufp = fs_rh_config_descriptor;\r
- len = sizeof fs_rh_config_descriptor;\r
- }\r
- break;\r
- case USB_DT_STRING << 8:\r
- urb->actual_length = rh_string (\r
- wValue & 0xff, hcd,\r
- ubuf, wLength);\r
- break;\r
- default:\r
- goto error;\r
- }\r
- break;\r
- case DeviceRequest | USB_REQ_GET_INTERFACE:\r
- ubuf [0] = 0;\r
- /* FALLTHROUGH */\r
- case DeviceOutRequest | USB_REQ_SET_INTERFACE:\r
- break;\r
- case DeviceOutRequest | USB_REQ_SET_ADDRESS:\r
- // wValue == urb->dev->devaddr\r
- dev_dbg (hcd->controller, "root hub device address %d\n",\r
- wValue);\r
- break;\r
-\r
- /* INTERFACE REQUESTS (no defined feature/status flags) */\r
-\r
- /* ENDPOINT REQUESTS */\r
-\r
- case EndpointRequest | USB_REQ_GET_STATUS:\r
- // ENDPOINT_HALT flag\r
- ubuf [0] = 0;\r
- ubuf [1] = 0;\r
- /* FALLTHROUGH */\r
- case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:\r
- case EndpointOutRequest | USB_REQ_SET_FEATURE:\r
- dev_dbg (hcd->controller, "no endpoint features yet\n");\r
- break;\r
-\r
- /* CLASS REQUESTS (and errors) */\r
-\r
- default:\r
- /* non-generic request */\r
- urb->status = hcd->driver->hub_control (hcd,\r
- typeReq, wValue, wIndex,\r
- ubuf, wLength);\r
- break;\r
-error:\r
- /* "protocol stall" on error */\r
- urb->status = -EPIPE;\r
- dev_dbg (hcd->controller, "unsupported hub control message (maxchild %d)\n",\r
- urb->dev->maxchild);\r
- }\r
- if (urb->status) {\r
- urb->actual_length = 0;\r
- dev_dbg (hcd->controller, "CTRL: TypeReq=0x%x val=0x%x idx=0x%x len=%d ==> %d\n",\r
- typeReq, wValue, wIndex, wLength, urb->status);\r
- }\r
- if (bufp) {\r
- if (urb->transfer_buffer_length < len)\r
- len = urb->transfer_buffer_length;\r
- urb->actual_length = len;\r
- // always USB_DIR_IN, toward host\r
- memcpy (ubuf, bufp, len);\r
- }\r
-\r
- /* any errors get returned through the urb completion */\r
- local_irq_save (flags);\r
- usb_hcd_giveback_urb (hcd, urb, NULL);\r
- local_irq_restore (flags);\r
- return 0;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * Root Hub interrupt transfers are synthesized with a timer.\r
- * Completions are called in_interrupt() but not in_irq().\r
- */\r
-\r
-static void rh_report_status (unsigned long ptr);\r
-\r
-static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb) \r
-{\r
- int len = 1 + (urb->dev->maxchild / 8);\r
-\r
- /* rh_timer protected by hcd_data_lock */\r
- if (hcd->rh_timer.data\r
- || urb->status != -EINPROGRESS\r
- || urb->transfer_buffer_length < len) {\r
- dev_dbg (hcd->controller,\r
- "not queuing rh status urb, stat %d\n",\r
- urb->status);\r
- return -EINVAL;\r
- }\r
-\r
- init_timer (&hcd->rh_timer);\r
-\r
- hcd->rh_timer.function = rh_report_status;\r
- hcd->rh_timer.data = (unsigned long) urb;\r
- /* USB 2.0 spec says 256msec; this is close enough */\r
- hcd->rh_timer.expires = jiffies + HZ/4;\r
- add_timer (&hcd->rh_timer);\r
- urb->hcpriv = hcd; /* nonzero to indicate it's queued */\r
- return 0;\r
-}\r
-\r
-/* timer callback */\r
-\r
-static void rh_report_status (unsigned long ptr)\r
-{\r
- struct urb *urb;\r
- struct usb_hcd *hcd;\r
- int length;\r
- //unsigned long flags;\r
-\r
- urb = (struct urb *) ptr;\r
- local_irq_save (flags);\r
- spin_lock (&urb->lock);\r
-\r
- /* do nothing if the hc is gone or the urb's been unlinked */\r
- if (!urb->dev\r
- || urb->status != -EINPROGRESS\r
- || (hcd = urb->dev->bus->hcpriv) == 0\r
- || !HCD_IS_RUNNING (hcd->state)) {\r
- spin_unlock (&urb->lock);\r
- local_irq_restore (flags);\r
- return;\r
- }\r
-\r
- length = hcd->driver->hub_status_data (hcd, urb->transfer_buffer);\r
-\r
- /* complete the status urb, or retrigger the timer */\r
- spin_lock (&hcd_data_lock);\r
- if (length > 0) {\r
- hcd->rh_timer.data = 0;\r
- urb->actual_length = length;\r
- urb->status = 0;\r
- urb->hcpriv = 0;\r
- } else\r
- mod_timer (&hcd->rh_timer, jiffies + HZ/4);\r
- spin_unlock (&hcd_data_lock);\r
- spin_unlock (&urb->lock);\r
-\r
- /* local irqs are always blocked in completions */\r
- if (length > 0)\r
- usb_hcd_giveback_urb (hcd, urb, NULL);\r
- local_irq_restore (flags);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)\r
-{\r
- if (usb_pipeint (urb->pipe)) {\r
- int retval;\r
- unsigned long flags;\r
-\r
- spin_lock_irqsave (&hcd_data_lock, flags);\r
- retval = rh_status_urb (hcd, urb);\r
- spin_unlock_irqrestore (&hcd_data_lock, flags);\r
- return retval;\r
- }\r
- if (usb_pipecontrol (urb->pipe))\r
- return rh_call_control (hcd, urb);\r
- else\r
- return -EINVAL;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)\r
-{\r
- //unsigned long flags;\r
-\r
- /* note: always a synchronous unlink */\r
- del_timer_sync (&hcd->rh_timer);\r
- hcd->rh_timer.data = 0;\r
-\r
- local_irq_save (flags);\r
- urb->hcpriv = 0;\r
- usb_hcd_giveback_urb (hcd, urb, NULL);\r
- local_irq_restore (flags);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* exported only within usbcore */\r
-void usb_bus_get (struct usb_bus *bus)\r
-{\r
- atomic_inc (&bus->refcnt);\r
-}\r
-\r
-/* exported only within usbcore */\r
-void usb_bus_put (struct usb_bus *bus)\r
-{\r
- if (atomic_dec_and_test (&bus->refcnt))\r
- kfree (bus);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/**\r
- * usb_bus_init - shared initialization code\r
- * @bus: the bus structure being initialized\r
- *\r
- * This code is used to initialize a usb_bus structure, memory for which is\r
- * separately managed.\r
- */\r
-void STDCALL usb_bus_init (struct usb_bus *bus)\r
-{\r
- memset (&bus->devmap, 0, sizeof(struct usb_devmap));\r
-\r
- bus->devnum_next = 1;\r
-\r
- bus->root_hub = NULL;\r
- bus->hcpriv = NULL;\r
- bus->busnum = -1;\r
- bus->bandwidth_allocated = 0;\r
- bus->bandwidth_int_reqs = 0;\r
- bus->bandwidth_isoc_reqs = 0;\r
-\r
- INIT_LIST_HEAD (&bus->bus_list);\r
-\r
- atomic_set (&bus->refcnt, 1);\r
-}\r
-\r
-/**\r
- * usb_alloc_bus - creates a new USB host controller structure\r
- * @op: pointer to a struct usb_operations that this bus structure should use\r
- * Context: !in_interrupt()\r
- *\r
- * Creates a USB host controller bus structure with the specified \r
- * usb_operations and initializes all the necessary internal objects.\r
- *\r
- * If no memory is available, NULL is returned.\r
- *\r
- * The caller should call usb_free_bus() when it is finished with the structure.\r
- */\r
-struct usb_bus STDCALL *usb_alloc_bus (struct usb_operations *op)\r
-{\r
- struct usb_bus *bus;\r
-\r
- bus = kmalloc (sizeof *bus, GFP_KERNEL);\r
- if (!bus)\r
- return NULL;\r
- usb_bus_init (bus);\r
- bus->op = op;\r
- return bus;\r
-}\r
-\r
-/**\r
- * usb_free_bus - frees the memory used by a bus structure\r
- * @bus: pointer to the bus to free\r
- *\r
- * To be invoked by a HCD, only as the last step of decoupling from\r
- * hardware. It is an error to call this if the reference count is\r
- * anything but one. That would indicate that some system component\r
- * did not correctly shut down, and thought the hardware was still\r
- * accessible.\r
- */\r
-void STDCALL usb_free_bus (struct usb_bus *bus)\r
-{\r
- if (!bus)\r
- return;\r
- if (atomic_read (&bus->refcnt) != 1)\r
- err ("usb_free_bus #%d, count != 1", bus->busnum);\r
- usb_bus_put (bus);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/**\r
- * usb_register_bus - registers the USB host controller with the usb core\r
- * @bus: pointer to the bus to register\r
- * Context: !in_interrupt()\r
- *\r
- * Assigns a bus number, and links the controller into usbcore data\r
- * structures so that it can be seen by scanning the bus list.\r
- */\r
-void STDCALL usb_register_bus(struct usb_bus *bus)\r
-{\r
- int busnum;\r
-\r
- down (&usb_bus_list_lock);\r
- busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);\r
- if (busnum < USB_MAXBUS) {\r
- set_bit (busnum, busmap.busmap);\r
- bus->busnum = busnum;\r
- } else\r
- warn ("too many buses");\r
-\r
- usb_bus_get (bus);\r
-\r
- /* Add it to the list of buses */\r
- list_add (&bus->bus_list, &usb_bus_list);\r
- up (&usb_bus_list_lock);\r
-\r
- usbfs_add_bus (bus);\r
-\r
- dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);\r
-}\r
-\r
-/**\r
- * usb_deregister_bus - deregisters the USB host controller\r
- * @bus: pointer to the bus to deregister\r
- * Context: !in_interrupt()\r
- *\r
- * Recycles the bus number, and unlinks the controller from usbcore data\r
- * structures so that it won't be seen by scanning the bus list.\r
- */\r
-void STDCALL usb_deregister_bus (struct usb_bus *bus)\r
-{\r
- dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum);\r
-\r
- /*\r
- * NOTE: make sure that all the devices are removed by the\r
- * controller code, as well as having it call this when cleaning\r
- * itself up\r
- */\r
- down (&usb_bus_list_lock);\r
- list_del (&bus->bus_list);\r
- up (&usb_bus_list_lock);\r
-\r
- usbfs_remove_bus (bus);\r
-\r
- clear_bit (bus->busnum, busmap.busmap);\r
-\r
- usb_bus_put (bus);\r
-}\r
-\r
-/**\r
- * usb_register_root_hub - called by HCD to register its root hub \r
- * @usb_dev: the usb root hub device to be registered.\r
- * @parent_dev: the parent device of this root hub.\r
- *\r
- * The USB host controller calls this function to register the root hub\r
- * properly with the USB subsystem. It sets up the device properly in\r
- * the driverfs tree, and then calls usb_new_device() to register the\r
- * usb device.\r
- */\r
-int STDCALL usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev)\r
-{\r
- int retval;\r
-\r
- sprintf (&usb_dev->dev.bus_id[0], "usb%d", usb_dev->bus->busnum);\r
- usb_dev->state = USB_STATE_DEFAULT;\r
- retval = usb_new_device (usb_dev, parent_dev);\r
- if (retval)\r
- dev_err (parent_dev, "can't register root hub for %s, %d\n",\r
- usb_dev->dev.bus_id, retval);\r
- return retval;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/**\r
- * usb_calc_bus_time - approximate periodic transaction time in nanoseconds\r
- * @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH}\r
- * @is_input: true iff the transaction sends data to the host\r
- * @isoc: true for isochronous transactions, false for interrupt ones\r
- * @bytecount: how many bytes in the transaction.\r
- *\r
- * Returns approximate bus time in nanoseconds for a periodic transaction.\r
- * See USB 2.0 spec section 5.11.3; only periodic transfers need to be\r
- * scheduled in software, this function is only used for such scheduling.\r
- */\r
-long STDCALL usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)\r
-{\r
- unsigned long tmp;\r
-\r
- switch (speed) {\r
- case USB_SPEED_LOW: /* INTR only */\r
- if (is_input) {\r
- tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;\r
- return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);\r
- } else {\r
- tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;\r
- return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);\r
- }\r
- case USB_SPEED_FULL: /* ISOC or INTR */\r
- if (isoc) {\r
- tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;\r
- return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);\r
- } else {\r
- tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;\r
- return (9107L + BW_HOST_DELAY + tmp);\r
- }\r
- case USB_SPEED_HIGH: /* ISOC or INTR */\r
- // FIXME adjust for input vs output\r
- if (isoc)\r
- tmp = HS_USECS (bytecount);\r
- else\r
- tmp = HS_USECS_ISO (bytecount);\r
- return tmp;\r
- default:\r
- dbg ("bogus device speed!");\r
- return -1;\r
- }\r
-}\r
-\r
-/*\r
- * usb_check_bandwidth():\r
- *\r
- * old_alloc is from host_controller->bandwidth_allocated in microseconds;\r
- * bustime is from calc_bus_time(), but converted to microseconds.\r
- *\r
- * returns <bustime in us> if successful,\r
- * or -ENOSPC if bandwidth request fails.\r
- *\r
- * FIXME:\r
- * This initial implementation does not use Endpoint.bInterval\r
- * in managing bandwidth allocation.\r
- * It probably needs to be expanded to use Endpoint.bInterval.\r
- * This can be done as a later enhancement (correction).\r
- *\r
- * This will also probably require some kind of\r
- * frame allocation tracking...meaning, for example,\r
- * that if multiple drivers request interrupts every 10 USB frames,\r
- * they don't all have to be allocated at\r
- * frame numbers N, N+10, N+20, etc. Some of them could be at\r
- * N+11, N+21, N+31, etc., and others at\r
- * N+12, N+22, N+32, etc.\r
- *\r
- * Similarly for isochronous transfers...\r
- *\r
- * Individual HCDs can schedule more directly ... this logic\r
- * is not correct for high speed transfers.\r
- */\r
-int STDCALL usb_check_bandwidth (struct usb_device *dev, struct urb *urb)\r
-{\r
- unsigned int pipe = urb->pipe;\r
- long bustime;\r
- int is_in = usb_pipein (pipe);\r
- int is_iso = usb_pipeisoc (pipe);\r
- int old_alloc = dev->bus->bandwidth_allocated;\r
- int new_alloc;\r
-\r
-\r
- bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso,\r
- usb_maxpacket (dev, pipe, !is_in)));\r
- if (is_iso)\r
- bustime /= urb->number_of_packets;\r
-\r
- new_alloc = old_alloc + (int) bustime;\r
- if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) {\r
-#ifdef DEBUG\r
- char *mode = \r
-#ifdef CONFIG_USB_BANDWIDTH\r
- "";\r
-#else\r
- "would have ";\r
-#endif\r
- dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n",\r
- mode, old_alloc, bustime, new_alloc);\r
-#endif\r
-#ifdef CONFIG_USB_BANDWIDTH\r
- bustime = -ENOSPC; /* report error */\r
-#endif\r
- }\r
-\r
- return bustime;\r
-}\r
-\r
-\r
-/**\r
- * usb_claim_bandwidth - records bandwidth for a periodic transfer\r
- * @dev: source/target of request\r
- * @urb: request (urb->dev == dev)\r
- * @bustime: bandwidth consumed, in (average) microseconds per frame\r
- * @isoc: true iff the request is isochronous\r
- *\r
- * Bus bandwidth reservations are recorded purely for diagnostic purposes.\r
- * HCDs are expected not to overcommit periodic bandwidth, and to record such\r
- * reservations whenever endpoints are added to the periodic schedule.\r
- *\r
- * FIXME averaging per-frame is suboptimal. Better to sum over the HCD's\r
- * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable\r
- * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how\r
- * large its periodic schedule is.\r
- */\r
-void STDCALL usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)\r
-{\r
- dev->bus->bandwidth_allocated += bustime;\r
- if (isoc)\r
- dev->bus->bandwidth_isoc_reqs++;\r
- else\r
- dev->bus->bandwidth_int_reqs++;\r
- urb->bandwidth = bustime;\r
-\r
-#ifdef USB_BANDWIDTH_MESSAGES\r
- dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n",\r
- bustime,\r
- isoc ? "ISOC" : "INTR",\r
- dev->bus->bandwidth_allocated,\r
- dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);\r
-#endif\r
-}\r
-\r
-\r
-/**\r
- * usb_release_bandwidth - reverses effect of usb_claim_bandwidth()\r
- * @dev: source/target of request\r
- * @urb: request (urb->dev == dev)\r
- * @isoc: true iff the request is isochronous\r
- *\r
- * This records that previously allocated bandwidth has been released.\r
- * Bandwidth is released when endpoints are removed from the host controller's\r
- * periodic schedule.\r
- */\r
-void STDCALL usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)\r
-{\r
- dev->bus->bandwidth_allocated -= urb->bandwidth;\r
- if (isoc)\r
- dev->bus->bandwidth_isoc_reqs--;\r
- else\r
- dev->bus->bandwidth_int_reqs--;\r
-\r
-#ifdef USB_BANDWIDTH_MESSAGES\r
- dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n",\r
- urb->bandwidth,\r
- isoc ? "ISOC" : "INTR",\r
- dev->bus->bandwidth_allocated,\r
- dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);\r
-#endif\r
- urb->bandwidth = 0;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * Generic HC operations.\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* called from khubd, or root hub init threads for hcd-private init */\r
-static int hcd_alloc_dev (struct usb_device *udev)\r
-{\r
- struct hcd_dev *dev;\r
- struct usb_hcd *hcd;\r
- unsigned long flags;\r
-\r
- if (!udev || udev->hcpriv)\r
- return -EINVAL;\r
- if (!udev->bus || !udev->bus->hcpriv)\r
- return -ENODEV;\r
- hcd = udev->bus->hcpriv;\r
- if (hcd->state == USB_STATE_QUIESCING)\r
- return -ENOLINK;\r
-\r
- dev = (struct hcd_dev *) kmalloc (sizeof *dev, GFP_KERNEL);\r
- if (dev == NULL)\r
- return -ENOMEM;\r
- memset (dev, 0, sizeof *dev);\r
-\r
- INIT_LIST_HEAD (&dev->dev_list);\r
- INIT_LIST_HEAD (&dev->urb_list);\r
-\r
- spin_lock_irqsave (&hcd_data_lock, flags);\r
- list_add (&dev->dev_list, &hcd->dev_list);\r
- // refcount is implicit\r
- udev->hcpriv = dev;\r
- spin_unlock_irqrestore (&hcd_data_lock, flags);\r
-\r
- return 0;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static void urb_unlink (struct urb *urb)\r
-{\r
- unsigned long flags;\r
- struct usb_device *dev;\r
-\r
- /* Release any periodic transfer bandwidth */\r
- if (urb->bandwidth)\r
- usb_release_bandwidth (urb->dev, urb,\r
- usb_pipeisoc (urb->pipe));\r
-\r
- /* clear all state linking urb to this dev (and hcd) */\r
-\r
- spin_lock_irqsave (&hcd_data_lock, flags);\r
- list_del_init (&urb->urb_list);\r
- dev = urb->dev;\r
- spin_unlock_irqrestore (&hcd_data_lock, flags);\r
- usb_put_dev (dev);\r
-}\r
-\r
-\r
-/* may be called in any context with a valid urb->dev usecount\r
- * caller surrenders "ownership" of urb\r
- * expects usb_submit_urb() to have sanity checked and conditioned all\r
- * inputs in the urb\r
- */\r
-static int hcd_submit_urb (struct urb *urb, int mem_flags)\r
-{\r
- int status;\r
- struct usb_hcd *hcd = urb->dev->bus->hcpriv;\r
- struct hcd_dev *dev = urb->dev->hcpriv;\r
- unsigned long flags;\r
- \r
-\r
- if (!hcd || !dev)\r
- return -ENODEV;\r
-// printk("submit_urb %p, # %i, t %i\n",urb,urb->dev->devnum,usb_pipetype(urb->pipe));\r
- /*\r
- * FIXME: make urb timeouts be generic, keeping the HCD cores\r
- * as simple as possible.\r
- */\r
-\r
- // NOTE: a generic device/urb monitoring hook would go here.\r
- // hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)\r
- // It would catch submission paths for all urbs.\r
-\r
- /*\r
- * Atomically queue the urb, first to our records, then to the HCD.\r
- * Access to urb->status is controlled by urb->lock ... changes on\r
- * i/o completion (normal or fault) or unlinking.\r
- */\r
-\r
- // FIXME: verify that quiescing hc works right (RH cleans up)\r
-\r
- spin_lock_irqsave (&hcd_data_lock, flags);\r
- if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {\r
- usb_get_dev (urb->dev);\r
- list_add_tail (&urb->urb_list, &dev->urb_list);\r
- status = 0;\r
- } else {\r
- INIT_LIST_HEAD (&urb->urb_list);\r
- status = -ESHUTDOWN;\r
- }\r
- spin_unlock_irqrestore (&hcd_data_lock, flags);\r
- if (status)\r
- return status;\r
-\r
- /* increment urb's reference count as part of giving it to the HCD\r
- * (which now controls it). HCD guarantees that it either returns\r
- * an error or calls giveback(), but not both.\r
- */\r
-\r
- urb = usb_get_urb (urb);\r
- if (urb->dev == hcd->self.root_hub) {\r
- /* NOTE: requirement on hub callers (usbfs and the hub\r
- * driver, for now) that URBs' urb->transfer_buffer be\r
- * valid and usb_buffer_{sync,unmap}() not be needed, since\r
- * they could clobber root hub response data.\r
- */\r
- urb->transfer_flags |= URB_NO_DMA_MAP;\r
- status = rh_urb_enqueue (hcd, urb);\r
- goto done;\r
- }\r
-\r
- /* lower level hcd code should use *_dma exclusively,\r
- * unless it uses pio or talks to another transport.\r
- */\r
- if (!(urb->transfer_flags & URB_NO_DMA_MAP)\r
- && hcd->controller->dma_mask) {\r
- if (usb_pipecontrol (urb->pipe))\r
- urb->setup_dma = dma_map_single (\r
- hcd->controller,\r
- urb->setup_packet,\r
- sizeof (struct usb_ctrlrequest),\r
- DMA_TO_DEVICE);\r
- if (urb->transfer_buffer_length != 0)\r
- urb->transfer_dma = dma_map_single (\r
- hcd->controller,\r
- urb->transfer_buffer,\r
- urb->transfer_buffer_length,\r
- usb_pipein (urb->pipe)\r
- ? DMA_FROM_DEVICE\r
- : DMA_TO_DEVICE);\r
- }\r
-\r
- status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);\r
-done:\r
- if (status) {\r
- usb_put_urb (urb);\r
- urb_unlink (urb);\r
- }\r
- return status;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* called in any context */\r
-static int hcd_get_frame_number (struct usb_device *udev)\r
-{\r
- struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv;\r
- return hcd->driver->get_frame_number (hcd);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* this makes the hcd giveback() the urb more quickly, by kicking it\r
- * off hardware queues (which may take a while) and returning it as\r
- * soon as practical. we've already set up the urb's return status,\r
- * but we can't know if the callback completed already.\r
- */\r
-static void\r
-unlink1 (struct usb_hcd *hcd, struct urb *urb)\r
-{\r
- if (urb == (struct urb *) hcd->rh_timer.data)\r
- usb_rh_status_dequeue (hcd, urb);\r
- else {\r
- int value;\r
-\r
- /* failures "should" be harmless */\r
- value = hcd->driver->urb_dequeue (hcd, urb);\r
- if (value != 0)\r
- dev_dbg (hcd->controller,\r
- "dequeue %p --> %d\n",\r
- urb, value);\r
- }\r
-}\r
-\r
-struct completion_splice { // modified urb context:\r
- /* did we complete? */\r
- struct completion done;\r
-\r
- /* original urb data */\r
- usb_complete_t complete;\r
- void *context;\r
-};\r
-\r
-static void unlink_complete (struct urb *urb, struct pt_regs *regs)\r
-{\r
- struct completion_splice *splice;\r
-\r
- splice = (struct completion_splice *) urb->context;\r
-\r
- /* issue original completion call */\r
- urb->complete = splice->complete;\r
- urb->context = splice->context;\r
- urb->complete (urb, regs);\r
-\r
- /* then let the synchronous unlink call complete */\r
- complete (&splice->done);\r
-}\r
-\r
-/*\r
- * called in any context; note ASYNC_UNLINK restrictions\r
- *\r
- * caller guarantees urb won't be recycled till both unlink()\r
- * and the urb's completion function return\r
- */\r
-static int hcd_unlink_urb (struct urb *urb)\r
-{\r
- struct hcd_dev *dev;\r
- struct usb_hcd *hcd = 0;\r
- struct device *sys = 0;\r
- unsigned long flags;\r
- struct completion_splice splice;\r
- int retval;\r
-\r
- if (!urb)\r
- return -EINVAL;\r
-\r
- /*\r
- * we contend for urb->status with the hcd core,\r
- * which changes it while returning the urb.\r
- *\r
- * Caller guaranteed that the urb pointer hasn't been freed, and\r
- * that it was submitted. But as a rule it can't know whether or\r
- * not it's already been unlinked ... so we respect the reversed\r
- * lock sequence needed for the usb_hcd_giveback_urb() code paths\r
- * (urb lock, then hcd_data_lock) in case some other CPU is now\r
- * unlinking it.\r
- */\r
- spin_lock_irqsave (&urb->lock, flags);\r
- spin_lock (&hcd_data_lock);\r
-\r
- if (!urb->dev || !urb->dev->bus) {\r
- retval = -ENODEV;\r
- goto done;\r
- }\r
-\r
- dev = urb->dev->hcpriv;\r
- sys = &urb->dev->dev;\r
- hcd = urb->dev->bus->hcpriv;\r
- if (!dev || !hcd) {\r
- retval = -ENODEV;\r
- goto done;\r
- }\r
-\r
- if (!urb->hcpriv) {\r
- retval = -EINVAL;\r
- goto done;\r
- }\r
-\r
- /* Any status except -EINPROGRESS means something already started to\r
- * unlink this URB from the hardware. So there's no more work to do.\r
- *\r
- * FIXME use better explicit urb state\r
- */\r
- if (urb->status != -EINPROGRESS) {\r
- retval = -EBUSY;\r
- goto done;\r
- }\r
-\r
- /* maybe set up to block until the urb's completion fires. the\r
- * lower level hcd code is always async, locking on urb->status\r
- * updates; an intercepted completion unblocks us.\r
- */\r
- if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {\r
- if (in_interrupt ()) {\r
- dev_dbg (hcd->controller, "non-async unlink in_interrupt");\r
- retval = -EWOULDBLOCK;\r
- goto done;\r
- }\r
- /* synchronous unlink: block till we see the completion */\r
- init_completion (&splice.done);\r
- splice.complete = urb->complete;\r
- splice.context = urb->context;\r
- urb->complete = unlink_complete;\r
- urb->context = &splice;\r
- urb->status = -ENOENT;\r
- } else {\r
- /* asynchronous unlink */\r
- urb->status = -ECONNRESET;\r
- }\r
- spin_unlock (&hcd_data_lock);\r
- spin_unlock_irqrestore (&urb->lock, flags);\r
-\r
- // FIXME remove splicing, so this becomes unlink1 (hcd, urb);\r
- if (urb == (struct urb *) hcd->rh_timer.data) {\r
- usb_rh_status_dequeue (hcd, urb);\r
- retval = 0;\r
- } else {\r
- retval = hcd->driver->urb_dequeue (hcd, urb);\r
-\r
- /* hcds shouldn't really fail these calls, but... */\r
- if (retval) {\r
- dev_dbg (sys, "dequeue %p --> %d\n", urb, retval);\r
- if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {\r
- spin_lock_irqsave (&urb->lock, flags);\r
- urb->complete = splice.complete;\r
- urb->context = splice.context;\r
- spin_unlock_irqrestore (&urb->lock, flags);\r
- }\r
- goto bye;\r
- }\r
- }\r
-\r
- /* block till giveback, if needed */\r
- if (urb->transfer_flags & URB_ASYNC_UNLINK)\r
- return -EINPROGRESS;\r
-\r
- wait_for_completion (&splice.done);\r
- return 0;\r
-\r
-done:\r
- spin_unlock (&hcd_data_lock);\r
- spin_unlock_irqrestore (&urb->lock, flags);\r
-bye:\r
- if (retval && sys && sys->driver)\r
- dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);\r
- return retval;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* disables the endpoint: cancels any pending urbs, then synchronizes with\r
- * the hcd to make sure all endpoint state is gone from hardware. use for\r
- * set_configuration, set_interface, driver removal, physical disconnect.\r
- *\r
- * example: a qh stored in hcd_dev.ep[], holding state related to endpoint\r
- * type, maxpacket size, toggle, halt status, and scheduling.\r
- */\r
-static void hcd_endpoint_disable (struct usb_device *udev, int endpoint)\r
-{\r
- unsigned long flags;\r
- struct hcd_dev *dev;\r
- struct usb_hcd *hcd;\r
- struct urb *urb;\r
- unsigned epnum = endpoint & USB_ENDPOINT_NUMBER_MASK;\r
-\r
- dev = udev->hcpriv;\r
- hcd = udev->bus->hcpriv;\r
-\r
-rescan:\r
- /* (re)block new requests, as best we can */\r
- if (endpoint & USB_DIR_IN) {\r
- usb_endpoint_halt (udev, epnum, 0);\r
- udev->epmaxpacketin [epnum] = 0;\r
- } else {\r
- usb_endpoint_halt (udev, epnum, 1);\r
- udev->epmaxpacketout [epnum] = 0;\r
- }\r
-\r
- /* then kill any current requests */\r
- spin_lock_irqsave (&hcd_data_lock, flags);\r
- list_for_each_entry (urb, &dev->urb_list, urb_list) {\r
- int tmp = urb->pipe;\r
-\r
- /* ignore urbs for other endpoints */\r
- if (usb_pipeendpoint (tmp) != epnum)\r
- continue;\r
- if ((tmp ^ endpoint) & USB_DIR_IN)\r
- continue;\r
-\r
- /* another cpu may be in hcd, spinning on hcd_data_lock\r
- * to giveback() this urb. the races here should be\r
- * small, but a full fix needs a new "can't submit"\r
- * urb state.\r
- */\r
- if (urb->status != -EINPROGRESS)\r
- continue;\r
- usb_get_urb (urb);\r
- spin_unlock_irqrestore (&hcd_data_lock, flags);\r
-\r
- spin_lock_irqsave (&urb->lock, flags);\r
- tmp = urb->status;\r
- if (tmp == -EINPROGRESS)\r
- urb->status = -ESHUTDOWN;\r
- spin_unlock_irqrestore (&urb->lock, flags);\r
-\r
- /* kick hcd unless it's already returning this */\r
- if (tmp == -EINPROGRESS) {\r
- tmp = urb->pipe;\r
- unlink1 (hcd, urb);\r
- dev_dbg (hcd->controller,\r
- "shutdown urb %p pipe %08x ep%d%s%s\n",\r
- urb, tmp, usb_pipeendpoint (tmp),\r
- (tmp & USB_DIR_IN) ? "in" : "out",\r
- ({ char *s; \\r
- switch (usb_pipetype (tmp)) { \\r
- case PIPE_CONTROL: s = ""; break; \\r
- case PIPE_BULK: s = "-bulk"; break; \\r
- case PIPE_INTERRUPT: s = "-intr"; break; \\r
- default: s = "-iso"; break; \\r
- }; s;}));\r
- }\r
- usb_put_urb (urb);\r
-\r
- /* list contents may have changed */\r
- goto rescan;\r
- }\r
- spin_unlock_irqrestore (&hcd_data_lock, flags);\r
-\r
- /* synchronize with the hardware, so old configuration state\r
- * clears out immediately (and will be freed).\r
- */\r
- might_sleep ();\r
- if (hcd->driver->endpoint_disable)\r
- hcd->driver->endpoint_disable (hcd, dev, endpoint);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup.\r
- * we're guaranteed that the device is fully quiesced. also, that each\r
- * endpoint has been hcd_endpoint_disabled.\r
- */\r
-\r
-static int hcd_free_dev (struct usb_device *udev)\r
-{\r
- struct hcd_dev *dev;\r
- struct usb_hcd *hcd;\r
- unsigned long flags;\r
-\r
- if (!udev || !udev->hcpriv)\r
- return -EINVAL;\r
-\r
- if (!udev->bus || !udev->bus->hcpriv)\r
- return -ENODEV;\r
-\r
- // should udev->devnum == -1 ??\r
-\r
- dev = udev->hcpriv;\r
- hcd = udev->bus->hcpriv;\r
-\r
- /* device driver problem with refcounts? */\r
- if (!list_empty (&dev->urb_list)) {\r
- dev_dbg (hcd->controller, "free busy dev, %s devnum %d (bug!)\n",\r
- hcd->self.bus_name, udev->devnum);\r
- return -EINVAL;\r
- }\r
-\r
- spin_lock_irqsave (&hcd_data_lock, flags);\r
- list_del (&dev->dev_list);\r
- udev->hcpriv = NULL;\r
- spin_unlock_irqrestore (&hcd_data_lock, flags);\r
-\r
- kfree (dev);\r
- return 0;\r
-}\r
-\r
-/*\r
- * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)\r
- *\r
- * When registering a USB bus through the HCD framework code, use this\r
- * usb_operations vector. The PCI glue layer does so automatically; only\r
- * bus glue for non-PCI system busses will need to use this.\r
- */\r
-struct usb_operations usb_hcd_operations = {\r
- .allocate = hcd_alloc_dev,\r
- .get_frame_number = hcd_get_frame_number,\r
- .submit_urb = hcd_submit_urb,\r
- .unlink_urb = hcd_unlink_urb,\r
- .deallocate = hcd_free_dev,\r
- .buffer_alloc = hcd_buffer_alloc,\r
- .buffer_free = hcd_buffer_free,\r
- .disable = hcd_endpoint_disable,\r
-};\r
-EXPORT_SYMBOL (usb_hcd_operations);\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/**\r
- * usb_hcd_giveback_urb - return URB from HCD to device driver\r
- * @hcd: host controller returning the URB\r
- * @urb: urb being returned to the USB device driver.\r
- * @regs: pt_regs, passed down to the URB completion handler\r
- * Context: in_interrupt()\r
- *\r
- * This hands the URB from HCD to its USB device driver, using its\r
- * completion function. The HCD has freed all per-urb resources\r
- * (and is done using urb->hcpriv). It also released all HCD locks;\r
- * the device driver won't cause problems if it frees, modifies,\r
- * or resubmits this URB.\r
- */\r
-void STDCALL usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)\r
-{\r
- urb_unlink (urb);\r
-\r
- // NOTE: a generic device/urb monitoring hook would go here.\r
- // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)\r
- // It would catch exit/unlink paths for all urbs.\r
-\r
- /* lower level hcd code should use *_dma exclusively */\r
- if (!(urb->transfer_flags & URB_NO_DMA_MAP)) {\r
- if (usb_pipecontrol (urb->pipe))\r
- pci_unmap_single (hcd->pdev, urb->setup_dma,\r
- sizeof (struct usb_ctrlrequest),\r
- PCI_DMA_TODEVICE);\r
- if (urb->transfer_buffer_length != 0)\r
- pci_unmap_single (hcd->pdev, urb->transfer_dma,\r
- urb->transfer_buffer_length,\r
- usb_pipein (urb->pipe)\r
- ? PCI_DMA_FROMDEVICE\r
- : PCI_DMA_TODEVICE);\r
- }\r
-\r
- /* pass ownership to the completion handler */\r
- urb->complete (urb, regs);\r
- usb_put_urb (urb);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/**\r
- * usb_hcd_irq - hook IRQs to HCD framework (bus glue)\r
- * @irq: the IRQ being raised\r
- * @__hcd: pointer to the HCD whose IRQ is beinng signaled\r
- * @r: saved hardware registers\r
- *\r
- * When registering a USB bus through the HCD framework code, use this\r
- * to handle interrupts. The PCI glue layer does so automatically; only\r
- * bus glue for non-PCI system busses will need to use this.\r
- */\r
-irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)\r
-{\r
- struct usb_hcd *hcd = __hcd;\r
- int start = hcd->state;\r
-\r
- if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */\r
- return IRQ_NONE;\r
-\r
- hcd->driver->irq (hcd, r);\r
- if (hcd->state != start && hcd->state == USB_STATE_HALT)\r
- usb_hc_died (hcd);\r
- return IRQ_HANDLED;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static void hcd_panic (void *_hcd)\r
-{\r
- struct usb_hcd *hcd = _hcd;\r
- hcd->driver->stop (hcd);\r
-}\r
-\r
-/**\r
- * usb_hc_died - report abnormal shutdown of a host controller (bus glue)\r
- * @hcd: pointer to the HCD representing the controller\r
- *\r
- * This is called by bus glue to report a USB host controller that died\r
- * while operations may still have been pending. It's called automatically\r
- * by the PCI glue, so only glue for non-PCI busses should need to call it. \r
- */\r
-void STDCALL usb_hc_died (struct usb_hcd *hcd)\r
-{\r
- struct list_head *devlist, *urblist;\r
- struct hcd_dev *dev;\r
- struct urb *urb;\r
- unsigned long flags;\r
- \r
- /* flag every pending urb as done */\r
- spin_lock_irqsave (&hcd_data_lock, flags);\r
- list_for_each (devlist, &hcd->dev_list) {\r
- dev = list_entry (devlist, struct hcd_dev, dev_list);\r
- list_for_each (urblist, &dev->urb_list) {\r
- urb = list_entry (urblist, struct urb, urb_list);\r
- dev_dbg (hcd->controller, "shutdown %s urb %p pipe %x, current status %d\n",\r
- hcd->self.bus_name, urb, urb->pipe, urb->status);\r
- if (urb->status == -EINPROGRESS)\r
- urb->status = -ESHUTDOWN;\r
- }\r
- }\r
- urb = (struct urb *) hcd->rh_timer.data;\r
- if (urb)\r
- urb->status = -ESHUTDOWN;\r
- spin_unlock_irqrestore (&hcd_data_lock, flags);\r
-\r
- /* hcd->stop() needs a task context */\r
- INIT_WORK (&hcd->work, hcd_panic, hcd);\r
- (void) schedule_work (&hcd->work);\r
-}\r
-\r
+/*
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2002
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if 0
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/uts.h> /* for UTS_SYSNAME */
+#include <linux/pci.h> /* for hcd->pdev and dma addressing */
+#include <linux/dma-mapping.h>
+#include <asm/byteorder.h>
+
+#include <linux/usb.h>
+#else
+#include "../usb_wrapper.h"
+//#define DEBUG
+#endif
+
+#include "hcd.h"
+
+// #define USB_BANDWIDTH_MESSAGES
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver framework
+ *
+ * Plugs into usbcore (usb_bus) and lets HCDs share code, minimizing
+ * HCD-specific behaviors/bugs.
+ *
+ * This does error checks, tracks devices and urbs, and delegates to a
+ * "hc_driver" only for code (and data) that really needs to know about
+ * hardware differences. That includes root hub registers, i/o queues,
+ * and so on ... but as little else as possible.
+ *
+ * Shared code includes most of the "root hub" code (these are emulated,
+ * though each HC's hardware works differently) and PCI glue, plus request
+ * tracking overhead. The HCD code should only block on spinlocks or on
+ * hardware handshaking; blocking on software events (such as other kernel
+ * threads releasing resources, or completing actions) is all generic.
+ *
+ * Happens the USB 2.0 spec says this would be invisible inside the "USBD",
+ * and includes mostly a "HCDI" (HCD Interface) along with some APIs used
+ * only by the hub driver ... and that neither should be seen or used by
+ * usb client device drivers.
+ *
+ * Contributors of ideas or unattributed patches include: David Brownell,
+ * Roman Weissgaerber, Rory Bolt, Greg Kroah-Hartman, ...
+ *
+ * HISTORY:
+ * 2002-02-21 Pull in most of the usb_bus support from usb.c; some
+ * associated cleanup. "usb_hcd" still != "usb_bus".
+ * 2001-12-12 Initial patch version for Linux 2.5.1 kernel.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* host controllers we manage */
+LIST_HEAD (usb_bus_list);
+EXPORT_SYMBOL_GPL (usb_bus_list);
+
+/* used when allocating bus numbers */
+#define USB_MAXBUS 64
+struct usb_busmap {
+ unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
+};
+static struct usb_busmap busmap;
+
+/* used when updating list of hcds */
+DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */
+EXPORT_SYMBOL_GPL (usb_bus_list_lock);
+
+/* used when updating hcd data */
+static spinlock_t hcd_data_lock = SPIN_LOCK_UNLOCKED;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sharable chunks of root hub code.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)
+#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)
+
+/* usb 2.0 root hub device descriptor */
+static const u8 usb2_rh_dev_descriptor [18] = {
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x00, 0x02, /* __u16 bcdUSB; v2.0 */
+
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x01, /* __u8 bDeviceProtocol; [ usb 2.0 single TT ]*/
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x00, 0x00, /* __u16 idVendor; */
+ 0x00, 0x00, /* __u16 idProduct; */
+ KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */
+
+ 0x03, /* __u8 iManufacturer; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
+
+/* usb 1.1 root hub device descriptor */
+static const u8 usb11_rh_dev_descriptor [18] = {
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x10, 0x01, /* __u16 bcdUSB; v1.1 */
+
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+
+ 0x00, 0x00, /* __u16 idVendor; */
+ 0x00, 0x00, /* __u16 idProduct; */
+ KERNEL_VER, KERNEL_REL, /* __u16 bcdDevice */
+
+ 0x03, /* __u8 iManufacturer; */
+ 0x02, /* __u8 iProduct; */
+ 0x01, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Configuration descriptors for our root hubs */
+
+static const u8 fs_rh_config_descriptor [] = {
+
+ /* one configuration */
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, 0x00, /* __u16 wTotalLength; */
+ 0x01, /* __u8 bNumInterfaces; (1) */
+ 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; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one 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; [usb1.1 or single tt] */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ 0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const u8 hs_rh_config_descriptor [] = {
+
+ /* one configuration */
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, 0x00, /* __u16 wTotalLength; */
+ 0x01, /* __u8 bNumInterfaces; (1) */
+ 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; */
+
+ /* USB 1.1:
+ * USB 2.0, single TT organization (mandatory):
+ * one interface, protocol 0
+ *
+ * USB 2.0, multiple TT organization (optional):
+ * two interfaces, protocols 1 (like single TT)
+ * and 2 (multiple TT mode) ... config is
+ * sometimes settable
+ * NOT IMPLEMENTED
+ */
+
+ /* one 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; [usb1.1 or single tt] */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* one endpoint (status change endpoint) */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x02, 0x00, /* __u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+ 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * helper routine for returning string descriptors in UTF-16LE
+ * input can actually be ISO-8859-1; ASCII is its 7-bit subset
+ */
+static int ascii2utf (char *s, u8 *utf, int utfmax)
+{
+ int retval;
+
+ for (retval = 0; *s && utfmax > 1; utfmax -= 2, retval += 2) {
+ *utf++ = *s++;
+ *utf++ = 0;
+ }
+ return retval;
+}
+
+/*
+ * rh_string - provides manufacturer, product and serial strings for root hub
+ * @id: the string ID number (1: serial number, 2: product, 3: vendor)
+ * @hcd: the host controller for this root hub
+ * @type: string describing our driver
+ * @data: return packet in UTF-16 LE
+ * @len: length of the return packet
+ *
+ * Produces either a manufacturer, product or serial number string for the
+ * virtual root hub device.
+ */
+static int rh_string (
+ int id,
+ struct usb_hcd *hcd,
+ u8 *data,
+ int len
+) {
+ char buf [100];
+
+ // language ids
+ if (id == 0) {
+ *data++ = 4; *data++ = 3; /* 4 bytes string data */
+ *data++ = 0x09; *data++ = 0x04; /* MSFT-speak for "en-us" */
+ return 4;
+
+ // serial number
+ } else if (id == 1) {
+ strcpy (buf, hcd->self.bus_name);
+
+ // product description
+ } else if (id == 2) {
+ strcpy (buf, hcd->product_desc);
+
+ // id 3 == vendor description
+ } else if (id == 3) {
+ sprintf (buf, "%s %s %s", UTS_SYSNAME, UTS_RELEASE,
+ hcd->description);
+
+ // unsupported IDs --> "protocol stall"
+ } else
+ return 0;
+
+ data [0] = 2 * (strlen (buf) + 1);
+ data [1] = 3; /* type == string */
+ return 2 + ascii2utf (buf, data + 2, len - 2);
+}
+
+
+/* Root hub control transfers execute synchronously */
+static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
+{
+ struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+ u16 typeReq, wValue, wIndex, wLength;
+ const u8 *bufp = 0;
+ u8 *ubuf = urb->transfer_buffer;
+ int len = 0;
+ //unsigned long flags;
+
+ typeReq = (cmd->bRequestType << 8) | cmd->bRequest;
+ wValue = le16_to_cpu (cmd->wValue);
+ wIndex = le16_to_cpu (cmd->wIndex);
+ wLength = le16_to_cpu (cmd->wLength);
+
+ if (wLength > urb->transfer_buffer_length)
+ goto error;
+
+ /* set up for success */
+ urb->status = 0;
+ urb->actual_length = wLength;
+ switch (typeReq) {
+
+ /* DEVICE REQUESTS */
+
+ case DeviceRequest | USB_REQ_GET_STATUS:
+ // DEVICE_REMOTE_WAKEUP
+ ubuf [0] = 1; // selfpowered
+ ubuf [1] = 0;
+ /* FALLTHROUGH */
+ case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+ case DeviceOutRequest | USB_REQ_SET_FEATURE:
+ dev_dbg (hcd->controller, "no device features yet yet\n");
+ break;
+ case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+ ubuf [0] = 1;
+ /* FALLTHROUGH */
+ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+ break;
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch (wValue & 0xff00) {
+ case USB_DT_DEVICE << 8:
+ if (hcd->driver->flags & HCD_USB2)
+ bufp = usb2_rh_dev_descriptor;
+ else if (hcd->driver->flags & HCD_USB11)
+ bufp = usb11_rh_dev_descriptor;
+ else
+ goto error;
+ len = 18;
+ break;
+ case USB_DT_CONFIG << 8:
+ if (hcd->driver->flags & HCD_USB2) {
+ bufp = hs_rh_config_descriptor;
+ len = sizeof hs_rh_config_descriptor;
+ } else {
+ bufp = fs_rh_config_descriptor;
+ len = sizeof fs_rh_config_descriptor;
+ }
+ break;
+ case USB_DT_STRING << 8:
+ urb->actual_length = rh_string (
+ wValue & 0xff, hcd,
+ ubuf, wLength);
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case DeviceRequest | USB_REQ_GET_INTERFACE:
+ ubuf [0] = 0;
+ /* FALLTHROUGH */
+ case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+ break;
+ case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+ // wValue == urb->dev->devaddr
+ dev_dbg (hcd->controller, "root hub device address %d\n",
+ wValue);
+ break;
+
+ /* INTERFACE REQUESTS (no defined feature/status flags) */
+
+ /* ENDPOINT REQUESTS */
+
+ case EndpointRequest | USB_REQ_GET_STATUS:
+ // ENDPOINT_HALT flag
+ ubuf [0] = 0;
+ ubuf [1] = 0;
+ /* FALLTHROUGH */
+ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+ case EndpointOutRequest | USB_REQ_SET_FEATURE:
+ dev_dbg (hcd->controller, "no endpoint features yet\n");
+ break;
+
+ /* CLASS REQUESTS (and errors) */
+
+ default:
+ /* non-generic request */
+ urb->status = hcd->driver->hub_control (hcd,
+ typeReq, wValue, wIndex,
+ ubuf, wLength);
+ break;
+error:
+ /* "protocol stall" on error */
+ urb->status = -EPIPE;
+ dev_dbg (hcd->controller, "unsupported hub control message (maxchild %d)\n",
+ urb->dev->maxchild);
+ }
+ if (urb->status) {
+ urb->actual_length = 0;
+ dev_dbg (hcd->controller, "CTRL: TypeReq=0x%x val=0x%x idx=0x%x len=%d ==> %d\n",
+ typeReq, wValue, wIndex, wLength, urb->status);
+ }
+ if (bufp) {
+ if (urb->transfer_buffer_length < len)
+ len = urb->transfer_buffer_length;
+ urb->actual_length = len;
+ // always USB_DIR_IN, toward host
+ memcpy (ubuf, bufp, len);
+ }
+
+ /* any errors get returned through the urb completion */
+ local_irq_save (flags);
+ usb_hcd_giveback_urb (hcd, urb, NULL);
+ local_irq_restore (flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Root Hub interrupt transfers are synthesized with a timer.
+ * Completions are called in_interrupt() but not in_irq().
+ */
+
+static void rh_report_status (unsigned long ptr);
+
+static int rh_status_urb (struct usb_hcd *hcd, struct urb *urb)
+{
+ int len = 1 + (urb->dev->maxchild / 8);
+
+ /* rh_timer protected by hcd_data_lock */
+ if (hcd->rh_timer.data
+ || urb->status != -EINPROGRESS
+ || urb->transfer_buffer_length < len) {
+ dev_dbg (hcd->controller,
+ "not queuing rh status urb, stat %d\n",
+ urb->status);
+ return -EINVAL;
+ }
+
+ init_timer (&hcd->rh_timer);
+
+ hcd->rh_timer.function = rh_report_status;
+ hcd->rh_timer.data = (unsigned long) urb;
+ /* USB 2.0 spec says 256msec; this is close enough */
+ hcd->rh_timer.expires = jiffies + HZ/4;
+ add_timer (&hcd->rh_timer);
+ urb->hcpriv = hcd; /* nonzero to indicate it's queued */
+ return 0;
+}
+
+/* timer callback */
+
+static void rh_report_status (unsigned long ptr)
+{
+ struct urb *urb;
+ struct usb_hcd *hcd;
+ int length;
+ //unsigned long flags;
+
+ urb = (struct urb *) ptr;
+ local_irq_save (flags);
+ spin_lock (&urb->lock);
+
+ /* do nothing if the hc is gone or the urb's been unlinked */
+ if (!urb->dev
+ || urb->status != -EINPROGRESS
+ || (hcd = urb->dev->bus->hcpriv) == 0
+ || !HCD_IS_RUNNING (hcd->state)) {
+ spin_unlock (&urb->lock);
+ local_irq_restore (flags);
+ return;
+ }
+
+ length = hcd->driver->hub_status_data (hcd, urb->transfer_buffer);
+
+ /* complete the status urb, or retrigger the timer */
+ spin_lock (&hcd_data_lock);
+ if (length > 0) {
+ hcd->rh_timer.data = 0;
+ urb->actual_length = length;
+ urb->status = 0;
+ urb->hcpriv = 0;
+ } else
+ mod_timer (&hcd->rh_timer, jiffies + HZ/4);
+ spin_unlock (&hcd_data_lock);
+ spin_unlock (&urb->lock);
+
+ /* local irqs are always blocked in completions */
+ if (length > 0)
+ usb_hcd_giveback_urb (hcd, urb, NULL);
+ local_irq_restore (flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
+{
+ if (usb_pipeint (urb->pipe)) {
+ int retval;
+ unsigned long flags;
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ retval = rh_status_urb (hcd, urb);
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+ return retval;
+ }
+ if (usb_pipecontrol (urb->pipe))
+ return rh_call_control (hcd, urb);
+ else
+ return -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+ //unsigned long flags;
+
+ /* note: always a synchronous unlink */
+ del_timer_sync (&hcd->rh_timer);
+ hcd->rh_timer.data = 0;
+
+ local_irq_save (flags);
+ urb->hcpriv = 0;
+ usb_hcd_giveback_urb (hcd, urb, NULL);
+ local_irq_restore (flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* exported only within usbcore */
+void usb_bus_get (struct usb_bus *bus)
+{
+ atomic_inc (&bus->refcnt);
+}
+
+/* exported only within usbcore */
+void usb_bus_put (struct usb_bus *bus)
+{
+ if (atomic_dec_and_test (&bus->refcnt))
+ kfree (bus);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_bus_init - shared initialization code
+ * @bus: the bus structure being initialized
+ *
+ * This code is used to initialize a usb_bus structure, memory for which is
+ * separately managed.
+ */
+void STDCALL usb_bus_init (struct usb_bus *bus)
+{
+ memset (&bus->devmap, 0, sizeof(struct usb_devmap));
+
+ bus->devnum_next = 1;
+
+ bus->root_hub = NULL;
+ bus->hcpriv = NULL;
+ bus->busnum = -1;
+ bus->bandwidth_allocated = 0;
+ bus->bandwidth_int_reqs = 0;
+ bus->bandwidth_isoc_reqs = 0;
+
+ INIT_LIST_HEAD (&bus->bus_list);
+
+ atomic_set (&bus->refcnt, 1);
+}
+
+/**
+ * usb_alloc_bus - creates a new USB host controller structure
+ * @op: pointer to a struct usb_operations that this bus structure should use
+ * Context: !in_interrupt()
+ *
+ * Creates a USB host controller bus structure with the specified
+ * usb_operations and initializes all the necessary internal objects.
+ *
+ * If no memory is available, NULL is returned.
+ *
+ * The caller should call usb_free_bus() when it is finished with the structure.
+ */
+struct usb_bus STDCALL *usb_alloc_bus (struct usb_operations *op)
+{
+ struct usb_bus *bus;
+
+ bus = kmalloc (sizeof *bus, GFP_KERNEL);
+ if (!bus)
+ return NULL;
+ usb_bus_init (bus);
+ bus->op = op;
+ return bus;
+}
+
+/**
+ * usb_free_bus - frees the memory used by a bus structure
+ * @bus: pointer to the bus to free
+ *
+ * To be invoked by a HCD, only as the last step of decoupling from
+ * hardware. It is an error to call this if the reference count is
+ * anything but one. That would indicate that some system component
+ * did not correctly shut down, and thought the hardware was still
+ * accessible.
+ */
+void STDCALL usb_free_bus (struct usb_bus *bus)
+{
+ if (!bus)
+ return;
+ if (atomic_read (&bus->refcnt) != 1)
+ err ("usb_free_bus #%d, count != 1", bus->busnum);
+ usb_bus_put (bus);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_register_bus - registers the USB host controller with the usb core
+ * @bus: pointer to the bus to register
+ * Context: !in_interrupt()
+ *
+ * Assigns a bus number, and links the controller into usbcore data
+ * structures so that it can be seen by scanning the bus list.
+ */
+void STDCALL usb_register_bus(struct usb_bus *bus)
+{
+ int busnum;
+
+ down (&usb_bus_list_lock);
+ busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
+ if (busnum < USB_MAXBUS) {
+ set_bit (busnum, busmap.busmap);
+ bus->busnum = busnum;
+ } else
+ warn ("too many buses");
+
+ usb_bus_get (bus);
+
+ /* Add it to the list of buses */
+ list_add (&bus->bus_list, &usb_bus_list);
+ up (&usb_bus_list_lock);
+
+ usbfs_add_bus (bus);
+
+ dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum);
+}
+
+/**
+ * usb_deregister_bus - deregisters the USB host controller
+ * @bus: pointer to the bus to deregister
+ * Context: !in_interrupt()
+ *
+ * Recycles the bus number, and unlinks the controller from usbcore data
+ * structures so that it won't be seen by scanning the bus list.
+ */
+void STDCALL usb_deregister_bus (struct usb_bus *bus)
+{
+ dev_info (bus->controller, "USB bus %d deregistered\n", bus->busnum);
+
+ /*
+ * NOTE: make sure that all the devices are removed by the
+ * controller code, as well as having it call this when cleaning
+ * itself up
+ */
+ down (&usb_bus_list_lock);
+ list_del (&bus->bus_list);
+ up (&usb_bus_list_lock);
+
+ usbfs_remove_bus (bus);
+
+ clear_bit (bus->busnum, busmap.busmap);
+
+ usb_bus_put (bus);
+}
+
+/**
+ * usb_register_root_hub - called by HCD to register its root hub
+ * @usb_dev: the usb root hub device to be registered.
+ * @parent_dev: the parent device of this root hub.
+ *
+ * The USB host controller calls this function to register the root hub
+ * properly with the USB subsystem. It sets up the device properly in
+ * the driverfs tree, and then calls usb_new_device() to register the
+ * usb device.
+ */
+int STDCALL usb_register_root_hub (struct usb_device *usb_dev, struct device *parent_dev)
+{
+ int retval;
+
+ sprintf (&usb_dev->dev.bus_id[0], "usb%d", usb_dev->bus->busnum);
+ usb_dev->state = USB_STATE_DEFAULT;
+ retval = usb_new_device (usb_dev, parent_dev);
+ if (retval)
+ dev_err (parent_dev, "can't register root hub for %s, %d\n",
+ usb_dev->dev.bus_id, retval);
+ return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_calc_bus_time - approximate periodic transaction time in nanoseconds
+ * @speed: from dev->speed; USB_SPEED_{LOW,FULL,HIGH}
+ * @is_input: true iff the transaction sends data to the host
+ * @isoc: true for isochronous transactions, false for interrupt ones
+ * @bytecount: how many bytes in the transaction.
+ *
+ * Returns approximate bus time in nanoseconds for a periodic transaction.
+ * See USB 2.0 spec section 5.11.3; only periodic transfers need to be
+ * scheduled in software, this function is only used for such scheduling.
+ */
+long STDCALL usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
+{
+ unsigned long tmp;
+
+ switch (speed) {
+ case USB_SPEED_LOW: /* INTR only */
+ if (is_input) {
+ tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ } else {
+ tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+ }
+ case USB_SPEED_FULL: /* ISOC or INTR */
+ if (isoc) {
+ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+ } else {
+ tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
+ return (9107L + BW_HOST_DELAY + tmp);
+ }
+ case USB_SPEED_HIGH: /* ISOC or INTR */
+ // FIXME adjust for input vs output
+ if (isoc)
+ tmp = HS_USECS (bytecount);
+ else
+ tmp = HS_USECS_ISO (bytecount);
+ return tmp;
+ default:
+ dbg ("bogus device speed!");
+ return -1;
+ }
+}
+
+/*
+ * usb_check_bandwidth():
+ *
+ * old_alloc is from host_controller->bandwidth_allocated in microseconds;
+ * bustime is from calc_bus_time(), but converted to microseconds.
+ *
+ * returns <bustime in us> if successful,
+ * or -ENOSPC if bandwidth request fails.
+ *
+ * FIXME:
+ * This initial implementation does not use Endpoint.bInterval
+ * in managing bandwidth allocation.
+ * It probably needs to be expanded to use Endpoint.bInterval.
+ * This can be done as a later enhancement (correction).
+ *
+ * This will also probably require some kind of
+ * frame allocation tracking...meaning, for example,
+ * that if multiple drivers request interrupts every 10 USB frames,
+ * they don't all have to be allocated at
+ * frame numbers N, N+10, N+20, etc. Some of them could be at
+ * N+11, N+21, N+31, etc., and others at
+ * N+12, N+22, N+32, etc.
+ *
+ * Similarly for isochronous transfers...
+ *
+ * Individual HCDs can schedule more directly ... this logic
+ * is not correct for high speed transfers.
+ */
+int STDCALL usb_check_bandwidth (struct usb_device *dev, struct urb *urb)
+{
+ unsigned int pipe = urb->pipe;
+ long bustime;
+ int is_in = usb_pipein (pipe);
+ int is_iso = usb_pipeisoc (pipe);
+ int old_alloc = dev->bus->bandwidth_allocated;
+ int new_alloc;
+
+
+ bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso,
+ usb_maxpacket (dev, pipe, !is_in)));
+ if (is_iso)
+ bustime /= urb->number_of_packets;
+
+ new_alloc = old_alloc + (int) bustime;
+ if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) {
+#ifdef DEBUG
+ char *mode =
+#ifdef CONFIG_USB_BANDWIDTH
+ "";
+#else
+ "would have ";
+#endif
+ dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n",
+ mode, old_alloc, bustime, new_alloc);
+#endif
+#ifdef CONFIG_USB_BANDWIDTH
+ bustime = -ENOSPC; /* report error */
+#endif
+ }
+
+ return bustime;
+}
+
+
+/**
+ * usb_claim_bandwidth - records bandwidth for a periodic transfer
+ * @dev: source/target of request
+ * @urb: request (urb->dev == dev)
+ * @bustime: bandwidth consumed, in (average) microseconds per frame
+ * @isoc: true iff the request is isochronous
+ *
+ * Bus bandwidth reservations are recorded purely for diagnostic purposes.
+ * HCDs are expected not to overcommit periodic bandwidth, and to record such
+ * reservations whenever endpoints are added to the periodic schedule.
+ *
+ * FIXME averaging per-frame is suboptimal. Better to sum over the HCD's
+ * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable
+ * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how
+ * large its periodic schedule is.
+ */
+void STDCALL usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc)
+{
+ dev->bus->bandwidth_allocated += bustime;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs++;
+ else
+ dev->bus->bandwidth_int_reqs++;
+ urb->bandwidth = bustime;
+
+#ifdef USB_BANDWIDTH_MESSAGES
+ dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n",
+ bustime,
+ isoc ? "ISOC" : "INTR",
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+#endif
+}
+
+
+/**
+ * usb_release_bandwidth - reverses effect of usb_claim_bandwidth()
+ * @dev: source/target of request
+ * @urb: request (urb->dev == dev)
+ * @isoc: true iff the request is isochronous
+ *
+ * This records that previously allocated bandwidth has been released.
+ * Bandwidth is released when endpoints are removed from the host controller's
+ * periodic schedule.
+ */
+void STDCALL usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc)
+{
+ dev->bus->bandwidth_allocated -= urb->bandwidth;
+ if (isoc)
+ dev->bus->bandwidth_isoc_reqs--;
+ else
+ dev->bus->bandwidth_int_reqs--;
+
+#ifdef USB_BANDWIDTH_MESSAGES
+ dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n",
+ urb->bandwidth,
+ isoc ? "ISOC" : "INTR",
+ dev->bus->bandwidth_allocated,
+ dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs);
+#endif
+ urb->bandwidth = 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic HC operations.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* called from khubd, or root hub init threads for hcd-private init */
+static int hcd_alloc_dev (struct usb_device *udev)
+{
+ struct hcd_dev *dev;
+ struct usb_hcd *hcd;
+ unsigned long flags;
+
+ if (!udev || udev->hcpriv)
+ return -EINVAL;
+ if (!udev->bus || !udev->bus->hcpriv)
+ return -ENODEV;
+ hcd = udev->bus->hcpriv;
+ if (hcd->state == USB_STATE_QUIESCING)
+ return -ENOLINK;
+
+ dev = (struct hcd_dev *) kmalloc (sizeof *dev, GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+ memset (dev, 0, sizeof *dev);
+
+ INIT_LIST_HEAD (&dev->dev_list);
+ INIT_LIST_HEAD (&dev->urb_list);
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_add (&dev->dev_list, &hcd->dev_list);
+ // refcount is implicit
+ udev->hcpriv = dev;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void urb_unlink (struct urb *urb)
+{
+ unsigned long flags;
+ struct usb_device *dev;
+
+ /* Release any periodic transfer bandwidth */
+ if (urb->bandwidth)
+ usb_release_bandwidth (urb->dev, urb,
+ usb_pipeisoc (urb->pipe));
+
+ /* clear all state linking urb to this dev (and hcd) */
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_del_init (&urb->urb_list);
+ dev = urb->dev;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+ usb_put_dev (dev);
+}
+
+
+/* may be called in any context with a valid urb->dev usecount
+ * caller surrenders "ownership" of urb
+ * expects usb_submit_urb() to have sanity checked and conditioned all
+ * inputs in the urb
+ */
+static int hcd_submit_urb (struct urb *urb, int mem_flags)
+{
+ int status;
+ struct usb_hcd *hcd = urb->dev->bus->hcpriv;
+ struct hcd_dev *dev = urb->dev->hcpriv;
+ unsigned long flags;
+
+
+ if (!hcd || !dev)
+ return -ENODEV;
+// printk("submit_urb %p, # %i, t %i\n",urb,urb->dev->devnum,usb_pipetype(urb->pipe));
+ /*
+ * FIXME: make urb timeouts be generic, keeping the HCD cores
+ * as simple as possible.
+ */
+
+ // NOTE: a generic device/urb monitoring hook would go here.
+ // hcd_monitor_hook(MONITOR_URB_SUBMIT, urb)
+ // It would catch submission paths for all urbs.
+
+ /*
+ * Atomically queue the urb, first to our records, then to the HCD.
+ * Access to urb->status is controlled by urb->lock ... changes on
+ * i/o completion (normal or fault) or unlinking.
+ */
+
+ // FIXME: verify that quiescing hc works right (RH cleans up)
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ if (HCD_IS_RUNNING (hcd->state) && hcd->state != USB_STATE_QUIESCING) {
+ usb_get_dev (urb->dev);
+ list_add_tail (&urb->urb_list, &dev->urb_list);
+ status = 0;
+ } else {
+ INIT_LIST_HEAD (&urb->urb_list);
+ status = -ESHUTDOWN;
+ }
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+ if (status)
+ return status;
+
+ /* increment urb's reference count as part of giving it to the HCD
+ * (which now controls it). HCD guarantees that it either returns
+ * an error or calls giveback(), but not both.
+ */
+
+ urb = usb_get_urb (urb);
+ if (urb->dev == hcd->self.root_hub) {
+ /* NOTE: requirement on hub callers (usbfs and the hub
+ * driver, for now) that URBs' urb->transfer_buffer be
+ * valid and usb_buffer_{sync,unmap}() not be needed, since
+ * they could clobber root hub response data.
+ */
+ urb->transfer_flags |= URB_NO_DMA_MAP;
+ status = rh_urb_enqueue (hcd, urb);
+ goto done;
+ }
+
+ /* lower level hcd code should use *_dma exclusively,
+ * unless it uses pio or talks to another transport.
+ */
+ if (!(urb->transfer_flags & URB_NO_DMA_MAP)
+ && hcd->controller->dma_mask) {
+ if (usb_pipecontrol (urb->pipe))
+ urb->setup_dma = dma_map_single (
+ hcd->controller,
+ urb->setup_packet,
+ sizeof (struct usb_ctrlrequest),
+ DMA_TO_DEVICE);
+ if (urb->transfer_buffer_length != 0)
+ urb->transfer_dma = dma_map_single (
+ hcd->controller,
+ urb->transfer_buffer,
+ urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? DMA_FROM_DEVICE
+ : DMA_TO_DEVICE);
+ }
+
+ status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
+done:
+ if (status) {
+ usb_put_urb (urb);
+ urb_unlink (urb);
+ }
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called in any context */
+static int hcd_get_frame_number (struct usb_device *udev)
+{
+ struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv;
+ return hcd->driver->get_frame_number (hcd);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* this makes the hcd giveback() the urb more quickly, by kicking it
+ * off hardware queues (which may take a while) and returning it as
+ * soon as practical. we've already set up the urb's return status,
+ * but we can't know if the callback completed already.
+ */
+static void
+unlink1 (struct usb_hcd *hcd, struct urb *urb)
+{
+ if (urb == (struct urb *) hcd->rh_timer.data)
+ usb_rh_status_dequeue (hcd, urb);
+ else {
+ int value;
+
+ /* failures "should" be harmless */
+ value = hcd->driver->urb_dequeue (hcd, urb);
+ if (value != 0)
+ dev_dbg (hcd->controller,
+ "dequeue %p --> %d\n",
+ urb, value);
+ }
+}
+
+struct completion_splice { // modified urb context:
+ /* did we complete? */
+ struct completion done;
+
+ /* original urb data */
+ usb_complete_t complete;
+ void *context;
+};
+
+static void unlink_complete (struct urb *urb, struct pt_regs *regs)
+{
+ struct completion_splice *splice;
+
+ splice = (struct completion_splice *) urb->context;
+
+ /* issue original completion call */
+ urb->complete = splice->complete;
+ urb->context = splice->context;
+ urb->complete (urb, regs);
+
+ /* then let the synchronous unlink call complete */
+ complete (&splice->done);
+}
+
+/*
+ * called in any context; note ASYNC_UNLINK restrictions
+ *
+ * caller guarantees urb won't be recycled till both unlink()
+ * and the urb's completion function return
+ */
+static int hcd_unlink_urb (struct urb *urb)
+{
+ struct hcd_dev *dev;
+ struct usb_hcd *hcd = 0;
+ struct device *sys = 0;
+ unsigned long flags;
+ struct completion_splice splice;
+ int retval;
+
+ if (!urb)
+ return -EINVAL;
+
+ /*
+ * we contend for urb->status with the hcd core,
+ * which changes it while returning the urb.
+ *
+ * Caller guaranteed that the urb pointer hasn't been freed, and
+ * that it was submitted. But as a rule it can't know whether or
+ * not it's already been unlinked ... so we respect the reversed
+ * lock sequence needed for the usb_hcd_giveback_urb() code paths
+ * (urb lock, then hcd_data_lock) in case some other CPU is now
+ * unlinking it.
+ */
+ spin_lock_irqsave (&urb->lock, flags);
+ spin_lock (&hcd_data_lock);
+
+ if (!urb->dev || !urb->dev->bus) {
+ retval = -ENODEV;
+ goto done;
+ }
+
+ dev = urb->dev->hcpriv;
+ sys = &urb->dev->dev;
+ hcd = urb->dev->bus->hcpriv;
+ if (!dev || !hcd) {
+ retval = -ENODEV;
+ goto done;
+ }
+
+ if (!urb->hcpriv) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ /* Any status except -EINPROGRESS means something already started to
+ * unlink this URB from the hardware. So there's no more work to do.
+ *
+ * FIXME use better explicit urb state
+ */
+ if (urb->status != -EINPROGRESS) {
+ retval = -EBUSY;
+ goto done;
+ }
+
+ /* maybe set up to block until the urb's completion fires. the
+ * lower level hcd code is always async, locking on urb->status
+ * updates; an intercepted completion unblocks us.
+ */
+ if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
+ if (in_interrupt ()) {
+ dev_dbg (hcd->controller, "non-async unlink in_interrupt");
+ retval = -EWOULDBLOCK;
+ goto done;
+ }
+ /* synchronous unlink: block till we see the completion */
+ init_completion (&splice.done);
+ splice.complete = urb->complete;
+ splice.context = urb->context;
+ urb->complete = unlink_complete;
+ urb->context = &splice;
+ urb->status = -ENOENT;
+ } else {
+ /* asynchronous unlink */
+ urb->status = -ECONNRESET;
+ }
+ spin_unlock (&hcd_data_lock);
+ spin_unlock_irqrestore (&urb->lock, flags);
+
+ // FIXME remove splicing, so this becomes unlink1 (hcd, urb);
+ if (urb == (struct urb *) hcd->rh_timer.data) {
+ usb_rh_status_dequeue (hcd, urb);
+ retval = 0;
+ } else {
+ retval = hcd->driver->urb_dequeue (hcd, urb);
+
+ /* hcds shouldn't really fail these calls, but... */
+ if (retval) {
+ dev_dbg (sys, "dequeue %p --> %d\n", urb, retval);
+ if (!(urb->transfer_flags & URB_ASYNC_UNLINK)) {
+ spin_lock_irqsave (&urb->lock, flags);
+ urb->complete = splice.complete;
+ urb->context = splice.context;
+ spin_unlock_irqrestore (&urb->lock, flags);
+ }
+ goto bye;
+ }
+ }
+
+ /* block till giveback, if needed */
+ if (urb->transfer_flags & URB_ASYNC_UNLINK)
+ return -EINPROGRESS;
+
+ wait_for_completion (&splice.done);
+ return 0;
+
+done:
+ spin_unlock (&hcd_data_lock);
+ spin_unlock_irqrestore (&urb->lock, flags);
+bye:
+ if (retval && sys && sys->driver)
+ dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval);
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* disables the endpoint: cancels any pending urbs, then synchronizes with
+ * the hcd to make sure all endpoint state is gone from hardware. use for
+ * set_configuration, set_interface, driver removal, physical disconnect.
+ *
+ * example: a qh stored in hcd_dev.ep[], holding state related to endpoint
+ * type, maxpacket size, toggle, halt status, and scheduling.
+ */
+static void hcd_endpoint_disable (struct usb_device *udev, int endpoint)
+{
+ unsigned long flags;
+ struct hcd_dev *dev;
+ struct usb_hcd *hcd;
+ struct urb *urb;
+ unsigned epnum = endpoint & USB_ENDPOINT_NUMBER_MASK;
+
+ dev = udev->hcpriv;
+ hcd = udev->bus->hcpriv;
+
+rescan:
+ /* (re)block new requests, as best we can */
+ if (endpoint & USB_DIR_IN) {
+ usb_endpoint_halt (udev, epnum, 0);
+ udev->epmaxpacketin [epnum] = 0;
+ } else {
+ usb_endpoint_halt (udev, epnum, 1);
+ udev->epmaxpacketout [epnum] = 0;
+ }
+
+ /* then kill any current requests */
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_for_each_entry (urb, &dev->urb_list, urb_list) {
+ int tmp = urb->pipe;
+
+ /* ignore urbs for other endpoints */
+ if (usb_pipeendpoint (tmp) != epnum)
+ continue;
+ if ((tmp ^ endpoint) & USB_DIR_IN)
+ continue;
+
+ /* another cpu may be in hcd, spinning on hcd_data_lock
+ * to giveback() this urb. the races here should be
+ * small, but a full fix needs a new "can't submit"
+ * urb state.
+ */
+ if (urb->status != -EINPROGRESS)
+ continue;
+ usb_get_urb (urb);
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ spin_lock_irqsave (&urb->lock, flags);
+ tmp = urb->status;
+ if (tmp == -EINPROGRESS)
+ urb->status = -ESHUTDOWN;
+ spin_unlock_irqrestore (&urb->lock, flags);
+
+ /* kick hcd unless it's already returning this */
+ if (tmp == -EINPROGRESS) {
+ tmp = urb->pipe;
+ unlink1 (hcd, urb);
+ dev_dbg (hcd->controller,
+ "shutdown urb %p pipe %08x ep%d%s%s\n",
+ urb, tmp, usb_pipeendpoint (tmp),
+ (tmp & USB_DIR_IN) ? "in" : "out",
+ ({ char *s; \
+ switch (usb_pipetype (tmp)) { \
+ case PIPE_CONTROL: s = ""; break; \
+ case PIPE_BULK: s = "-bulk"; break; \
+ case PIPE_INTERRUPT: s = "-intr"; break; \
+ default: s = "-iso"; break; \
+ }; s;}));
+ }
+ usb_put_urb (urb);
+
+ /* list contents may have changed */
+ goto rescan;
+ }
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ /* synchronize with the hardware, so old configuration state
+ * clears out immediately (and will be freed).
+ */
+ might_sleep ();
+ if (hcd->driver->endpoint_disable)
+ hcd->driver->endpoint_disable (hcd, dev, endpoint);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* called by khubd, rmmod, apmd, or other thread for hcd-private cleanup.
+ * we're guaranteed that the device is fully quiesced. also, that each
+ * endpoint has been hcd_endpoint_disabled.
+ */
+
+static int hcd_free_dev (struct usb_device *udev)
+{
+ struct hcd_dev *dev;
+ struct usb_hcd *hcd;
+ unsigned long flags;
+
+ if (!udev || !udev->hcpriv)
+ return -EINVAL;
+
+ if (!udev->bus || !udev->bus->hcpriv)
+ return -ENODEV;
+
+ // should udev->devnum == -1 ??
+
+ dev = udev->hcpriv;
+ hcd = udev->bus->hcpriv;
+
+ /* device driver problem with refcounts? */
+ if (!list_empty (&dev->urb_list)) {
+ dev_dbg (hcd->controller, "free busy dev, %s devnum %d (bug!)\n",
+ hcd->self.bus_name, udev->devnum);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_del (&dev->dev_list);
+ udev->hcpriv = NULL;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ kfree (dev);
+ return 0;
+}
+
+/*
+ * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
+ *
+ * When registering a USB bus through the HCD framework code, use this
+ * usb_operations vector. The PCI glue layer does so automatically; only
+ * bus glue for non-PCI system busses will need to use this.
+ */
+struct usb_operations usb_hcd_operations = {
+ .allocate = hcd_alloc_dev,
+ .get_frame_number = hcd_get_frame_number,
+ .submit_urb = hcd_submit_urb,
+ .unlink_urb = hcd_unlink_urb,
+ .deallocate = hcd_free_dev,
+ .buffer_alloc = hcd_buffer_alloc,
+ .buffer_free = hcd_buffer_free,
+ .disable = hcd_endpoint_disable,
+};
+EXPORT_SYMBOL (usb_hcd_operations);
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_hcd_giveback_urb - return URB from HCD to device driver
+ * @hcd: host controller returning the URB
+ * @urb: urb being returned to the USB device driver.
+ * @regs: pt_regs, passed down to the URB completion handler
+ * Context: in_interrupt()
+ *
+ * This hands the URB from HCD to its USB device driver, using its
+ * completion function. The HCD has freed all per-urb resources
+ * (and is done using urb->hcpriv). It also released all HCD locks;
+ * the device driver won't cause problems if it frees, modifies,
+ * or resubmits this URB.
+ */
+void STDCALL usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
+{
+ urb_unlink (urb);
+
+ // NOTE: a generic device/urb monitoring hook would go here.
+ // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev)
+ // It would catch exit/unlink paths for all urbs.
+
+ /* lower level hcd code should use *_dma exclusively */
+ if (!(urb->transfer_flags & URB_NO_DMA_MAP)) {
+ if (usb_pipecontrol (urb->pipe))
+ pci_unmap_single (hcd->pdev, urb->setup_dma,
+ sizeof (struct usb_ctrlrequest),
+ PCI_DMA_TODEVICE);
+ if (urb->transfer_buffer_length != 0)
+ pci_unmap_single (hcd->pdev, urb->transfer_dma,
+ urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE);
+ }
+
+ /* pass ownership to the completion handler */
+ urb->complete (urb, regs);
+ usb_put_urb (urb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_hcd_irq - hook IRQs to HCD framework (bus glue)
+ * @irq: the IRQ being raised
+ * @__hcd: pointer to the HCD whose IRQ is beinng signaled
+ * @r: saved hardware registers
+ *
+ * When registering a USB bus through the HCD framework code, use this
+ * to handle interrupts. The PCI glue layer does so automatically; only
+ * bus glue for non-PCI system busses will need to use this.
+ */
+irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r)
+{
+ struct usb_hcd *hcd = __hcd;
+ int start = hcd->state;
+
+ if (unlikely (hcd->state == USB_STATE_HALT)) /* irq sharing? */
+ return IRQ_NONE;
+
+ hcd->driver->irq (hcd, r);
+ if (hcd->state != start && hcd->state == USB_STATE_HALT)
+ usb_hc_died (hcd);
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void hcd_panic (void *_hcd)
+{
+ struct usb_hcd *hcd = _hcd;
+ hcd->driver->stop (hcd);
+}
+
+/**
+ * usb_hc_died - report abnormal shutdown of a host controller (bus glue)
+ * @hcd: pointer to the HCD representing the controller
+ *
+ * This is called by bus glue to report a USB host controller that died
+ * while operations may still have been pending. It's called automatically
+ * by the PCI glue, so only glue for non-PCI busses should need to call it.
+ */
+void STDCALL usb_hc_died (struct usb_hcd *hcd)
+{
+ struct list_head *devlist, *urblist;
+ struct hcd_dev *dev;
+ struct urb *urb;
+ unsigned long flags;
+
+ /* flag every pending urb as done */
+ spin_lock_irqsave (&hcd_data_lock, flags);
+ list_for_each (devlist, &hcd->dev_list) {
+ dev = list_entry (devlist, struct hcd_dev, dev_list);
+ list_for_each (urblist, &dev->urb_list) {
+ urb = list_entry (urblist, struct urb, urb_list);
+ dev_dbg (hcd->controller, "shutdown %s urb %p pipe %x, current status %d\n",
+ hcd->self.bus_name, urb, urb->pipe, urb->status);
+ if (urb->status == -EINPROGRESS)
+ urb->status = -ESHUTDOWN;
+ }
+ }
+ urb = (struct urb *) hcd->rh_timer.data;
+ if (urb)
+ urb->status = -ESHUTDOWN;
+ spin_unlock_irqrestore (&hcd_data_lock, flags);
+
+ /* hcd->stop() needs a task context */
+ INIT_WORK (&hcd->work, hcd_panic, hcd);
+ (void) schedule_work (&hcd->work);
+}
+
-/*\r
- * Copyright (c) 2001-2002 by David Brownell\r
- *\r
- * This program is free software; you can redistribute it and/or modify it\r
- * under the terms of the GNU General Public License as published by the\r
- * Free Software Foundation; either version 2 of the License, or (at your\r
- * option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful, but\r
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\r
- * for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software Foundation,\r
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
- */\r
-\r
-\r
-#ifdef __KERNEL__\r
-\r
-/* This file contains declarations of usbcore internals that are mostly\r
- * used or exposed by Host Controller Drivers.\r
- */\r
-\r
-/*\r
- * USB Packet IDs (PIDs)\r
- */\r
-#define USB_PID_UNDEF_0 0xf0\r
-#define USB_PID_OUT 0xe1\r
-#define USB_PID_ACK 0xd2\r
-#define USB_PID_DATA0 0xc3\r
-#define USB_PID_PING 0xb4 /* USB 2.0 */\r
-#define USB_PID_SOF 0xa5\r
-#define USB_PID_NYET 0x96 /* USB 2.0 */\r
-#define USB_PID_DATA2 0x87 /* USB 2.0 */\r
-#define USB_PID_SPLIT 0x78 /* USB 2.0 */\r
-#define USB_PID_IN 0x69\r
-#define USB_PID_NAK 0x5a\r
-#define USB_PID_DATA1 0x4b\r
-#define USB_PID_PREAMBLE 0x3c /* Token mode */\r
-#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */\r
-#define USB_PID_SETUP 0x2d\r
-#define USB_PID_STALL 0x1e\r
-#define USB_PID_MDATA 0x0f /* USB 2.0 */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * USB Host Controller Driver (usb_hcd) framework\r
- *\r
- * Since "struct usb_bus" is so thin, you can't share much code in it.\r
- * This framework is a layer over that, and should be more sharable.\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-struct usb_hcd { /* usb_bus.hcpriv points to this */\r
-\r
- /*\r
- * housekeeping\r
- */\r
- struct usb_bus self; /* hcd is-a bus */\r
-\r
- const char *product_desc; /* product/vendor string */\r
- const char *description; /* "ehci-hcd" etc */\r
-\r
- struct timer_list rh_timer; /* drives root hub */\r
- struct list_head dev_list; /* devices on this bus */\r
- struct work_struct work;\r
-\r
- /*\r
- * hardware info/state\r
- */\r
- struct hc_driver *driver; /* hw-specific hooks */\r
- int irq; /* irq allocated */\r
- void *regs; /* device memory/io */\r
- struct device *controller; /* handle to hardware */\r
-\r
- /* a few non-PCI controllers exist, mostly for OHCI */\r
- struct pci_dev *pdev; /* pci is typical */\r
-#ifdef CONFIG_PCI\r
- int region; /* pci region for regs */\r
- u32 pci_state [16]; /* for PM state save */\r
- atomic_t resume_count; /* multiple resumes issue */\r
-#endif\r
-\r
-#define HCD_BUFFER_POOLS 4\r
- struct pci_pool *pool [HCD_BUFFER_POOLS];\r
-\r
- int state;\r
-# define __ACTIVE 0x01\r
-# define __SLEEPY 0x02\r
-# define __SUSPEND 0x04\r
-# define __TRANSIENT 0x80\r
-\r
-# define USB_STATE_HALT 0\r
-# define USB_STATE_RUNNING (__ACTIVE)\r
-# define USB_STATE_READY (__ACTIVE|__SLEEPY)\r
-# define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)\r
-# define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT)\r
-# define USB_STATE_SUSPENDED (__SUSPEND)\r
-\r
-#define HCD_IS_RUNNING(state) ((state) & __ACTIVE)\r
-#define HCD_IS_SUSPENDED(state) ((state) & __SUSPEND)\r
-\r
- /* more shared queuing code would be good; it should support\r
- * smarter scheduling, handle transaction translators, etc;\r
- * input size of periodic table to an interrupt scheduler. \r
- * (ohci 32, uhci 1024, ehci 256/512/1024).\r
- */\r
-};\r
-\r
-/* 2.4 does this a bit differently ... */\r
-static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)\r
-{\r
- return &hcd->self;\r
-}\r
-\r
-\r
-struct hcd_dev { /* usb_device.hcpriv points to this */\r
- struct list_head dev_list; /* on this hcd */\r
- struct list_head urb_list; /* pending on this dev */\r
-\r
- /* per-configuration HC/HCD state, such as QH or ED */\r
- void *ep[32];\r
-};\r
-\r
-// urb.hcpriv is really hardware-specific\r
-\r
-struct hcd_timeout { /* timeouts we allocate */\r
- struct list_head timeout_list;\r
- struct timer_list timer;\r
-};\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * FIXME usb_operations should vanish or become hc_driver,\r
- * when usb_bus and usb_hcd become the same thing.\r
- */\r
-\r
-struct usb_operations {\r
- int (*allocate)(struct usb_device *);\r
- int (*deallocate)(struct usb_device *);\r
- int (*get_frame_number) (struct usb_device *usb_dev);\r
- int (*submit_urb) (struct urb *urb, int mem_flags);\r
- int (*unlink_urb) (struct urb *urb);\r
-\r
- /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */\r
- void *(*buffer_alloc)(struct usb_bus *bus, size_t size,\r
- int mem_flags,\r
- dma_addr_t *dma);\r
- void (*buffer_free)(struct usb_bus *bus, size_t size,\r
- void *addr, dma_addr_t dma);\r
-\r
- void (*disable)(struct usb_device *udev, int bEndpointAddress);\r
-};\r
-\r
-/* each driver provides one of these, and hardware init support */\r
-\r
-struct pt_regs;\r
-\r
-// new struct from 2.6\r
-struct hc_driver {\r
- const char *description; /* "ehci-hcd" etc */\r
-\r
- /* irq handler */\r
- irqreturn_t (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);\r
-\r
- int flags;\r
-#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */\r
-#define HCD_USB11 0x0010 /* USB 1.1 */\r
-#define HCD_USB2 0x0020 /* USB 2.0 */\r
-\r
- /* called to init HCD and root hub */\r
- int (*reset) (struct usb_hcd *hcd);\r
- int (*start) (struct usb_hcd *hcd);\r
-\r
- /* called after all devices were suspended */\r
- int (*suspend) (struct usb_hcd *hcd, u32 state);\r
-\r
- /* called before any devices get resumed */\r
- int (*resume) (struct usb_hcd *hcd);\r
-\r
- /* cleanly make HCD stop writing memory and doing I/O */\r
- void (*stop) (struct usb_hcd *hcd);\r
-\r
- /* return current frame number */\r
- int (*get_frame_number) (struct usb_hcd *hcd);\r
-\r
- /* memory lifecycle */\r
- struct usb_hcd *(*hcd_alloc) (void);\r
- void (*hcd_free) (struct usb_hcd *hcd);\r
-\r
- /* manage i/o requests, device state */\r
- int (*urb_enqueue) (struct usb_hcd *hcd, struct urb *urb,\r
- int mem_flags);\r
- int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);\r
-\r
- /* hw synch, freeing endpoint resources that urb_dequeue can't */\r
- void (*endpoint_disable)(struct usb_hcd *hcd,\r
- struct hcd_dev *dev, int bEndpointAddress);\r
-\r
- /* root hub support */\r
- int (*hub_status_data) (struct usb_hcd *hcd, char *buf);\r
- int (*hub_control) (struct usb_hcd *hcd,\r
- u16 typeReq, u16 wValue, u16 wIndex,\r
- char *buf, u16 wLength);\r
-};\r
-\r
-// old version, "just in case"\r
-#if 0\r
-struct hc_driver {\r
- const char *description; /* "ehci-hcd" etc */\r
-\r
- /* irq handler */\r
- void (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);\r
-\r
- int flags;\r
-#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */\r
-#define HCD_USB11 0x0010 /* USB 1.1 */\r
-#define HCD_USB2 0x0020 /* USB 2.0 */\r
-\r
- /* called to init HCD and root hub */\r
- int (*start) (struct usb_hcd *hcd);\r
-\r
- /* called after all devices were suspended */\r
- int (*suspend) (struct usb_hcd *hcd, u32 state);\r
-\r
- /* called before any devices get resumed */\r
- int (*resume) (struct usb_hcd *hcd);\r
-\r
- /* cleanly make HCD stop writing memory and doing I/O */\r
- void (*stop) (struct usb_hcd *hcd);\r
-\r
- /* return current frame number */\r
- int (*get_frame_number) (struct usb_hcd *hcd);\r
-\r
- /* memory lifecycle */\r
- struct usb_hcd *(*hcd_alloc) (void);\r
- void (*hcd_free) (struct usb_hcd *hcd);\r
-\r
- /* manage i/o requests, device state */\r
- int (*urb_enqueue) (struct usb_hcd *hcd, struct urb *urb,\r
- int mem_flags);\r
- int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);\r
-\r
- /* hw synch, freeing endpoint resources that urb_dequeue can't */\r
- void (*endpoint_disable)(struct usb_hcd *hcd,\r
- struct hcd_dev *dev, int bEndpointAddress);\r
-\r
- /* root hub support */\r
- int (*hub_status_data) (struct usb_hcd *hcd, char *buf);\r
- int (*hub_control) (struct usb_hcd *hcd,\r
- u16 typeReq, u16 wValue, u16 wIndex,\r
- char *buf, u16 wLength);\r
-};\r
-#endif\r
-\r
-extern void STDCALL usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);\r
-extern void STDCALL usb_bus_init (struct usb_bus *bus);\r
-extern void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb);\r
-\r
-#ifdef CONFIG_PCI\r
-struct pci_dev;\r
-struct pci_device_id;\r
-extern int STDCALL usb_hcd_pci_probe (struct pci_dev *dev,\r
- const struct pci_device_id *id);\r
-extern void STDCALL usb_hcd_pci_remove (struct pci_dev *dev);\r
-\r
-#ifdef CONFIG_PM\r
-// FIXME: see Documentation/power/pci.txt (2.4.6 and later?)\r
-// extern int usb_hcd_pci_save_state (struct pci_dev *dev, u32 state);\r
-extern int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state);\r
-extern int usb_hcd_pci_resume (struct pci_dev *dev);\r
-// extern int usb_hcd_pci_enable_wake (struct pci_dev *dev, u32 state, int flg);\r
-#endif /* CONFIG_PM */\r
-\r
-#endif /* CONFIG_PCI */\r
-\r
-/* pci-ish (pdev null is ok) buffer alloc/mapping support */\r
-int hcd_buffer_create (struct usb_hcd *hcd);\r
-void hcd_buffer_destroy (struct usb_hcd *hcd);\r
-\r
-void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,\r
- int mem_flags, dma_addr_t *dma);\r
-void hcd_buffer_free (struct usb_bus *bus, size_t size,\r
- void *addr, dma_addr_t dma);\r
-\r
-/* generic bus glue, needed for host controllers that don't use PCI */\r
-extern struct usb_operations usb_hcd_operations;\r
-extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);\r
-extern void STDCALL usb_hc_died (struct usb_hcd *hcd);\r
-\r
-/* -------------------------------------------------------------------------- */\r
-\r
-/* Enumeration is only for the hub driver, or HCD virtual root hubs */\r
-extern int usb_new_device(struct usb_device *dev, struct device *parent);\r
-extern void STDCALL usb_connect(struct usb_device *dev);\r
-extern void usb_disconnect(struct usb_device **);\r
-\r
-/* exported to hub driver ONLY to support usb_reset_device () */\r
-extern int usb_get_configuration(struct usb_device *dev);\r
-extern void usb_set_maxpacket(struct usb_device *dev);\r
-extern void usb_destroy_configuration(struct usb_device *dev);\r
-extern int usb_set_address(struct usb_device *dev);\r
-\r
-/* use these only before the device's address has been set */\r
-#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30))\r
-#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | USB_DIR_IN)\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * HCD Root Hub support\r
- */\r
-\r
-#include "hub.h"\r
-\r
-/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */\r
-#define DeviceRequest \\r
- ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)\r
-#define DeviceOutRequest \\r
- ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)\r
-\r
-#define InterfaceRequest \\r
- ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)\r
-\r
-#define EndpointRequest \\r
- ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)\r
-#define EndpointOutRequest \\r
- ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)\r
-\r
-/* table 9.6 standard features */\r
-#define DEVICE_REMOTE_WAKEUP 1\r
-#define ENDPOINT_HALT 0\r
-\r
-/* class requests from the USB 2.0 hub spec, table 11-15 */\r
-/* GetBusState and SetHubDescriptor are optional, omitted */\r
-#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)\r
-#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)\r
-#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)\r
-#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)\r
-#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)\r
-#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)\r
-#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * Generic bandwidth allocation constants/support\r
- */\r
-#define FRAME_TIME_USECS 1000L\r
-#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */\r
- /* Trying not to use worst-case bit-stuffing\r
- of (7/6 * 8 * bytecount) = 9.33 * bytecount */\r
- /* bytecount = data payload byte count */\r
-\r
-#define NS_TO_US(ns) ((ns + 500L) / 1000L)\r
- /* convert & round nanoseconds to microseconds */\r
-\r
-extern void STDCALL usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,\r
- int bustime, int isoc);\r
-extern void STDCALL usb_release_bandwidth (struct usb_device *dev, struct urb *urb,\r
- int isoc);\r
-\r
-/*\r
- * Full/low speed bandwidth allocation constants/support.\r
- */\r
-#define BW_HOST_DELAY 1000L /* nanoseconds */\r
-#define BW_HUB_LS_SETUP 333L /* nanoseconds */\r
- /* 4 full-speed bit times (est.) */\r
-\r
-#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */\r
-#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)\r
-#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)\r
-\r
-extern int STDCALL usb_check_bandwidth (struct usb_device *dev, struct urb *urb);\r
-\r
-/*\r
- * Ceiling microseconds (typical) for that many bytes at high speed\r
- * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed\r
- * to preallocate bandwidth)\r
- */\r
-#define USB2_HOST_DELAY 5 /* nsec, guess */\r
-#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \\r
- + ((2083UL * (3167 + BitTime (bytes)))/1000) \\r
- + USB2_HOST_DELAY)\r
-#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \\r
- + ((2083UL * (3167 + BitTime (bytes)))/1000) \\r
- + USB2_HOST_DELAY)\r
-\r
-extern long STDCALL usb_calc_bus_time (int speed, int is_input,\r
- int isoc, int bytecount);\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-extern struct usb_bus STDCALL *usb_alloc_bus (struct usb_operations *);\r
-extern void STDCALL usb_free_bus (struct usb_bus *);\r
-\r
-extern void STDCALL usb_register_bus (struct usb_bus *);\r
-extern void STDCALL usb_deregister_bus (struct usb_bus *);\r
-\r
-extern int STDCALL usb_register_root_hub (struct usb_device *usb_dev,\r
- struct device *parent_dev);\r
-\r
-/* for portability to 2.4, hcds should call this */\r
-static inline int hcd_register_root (struct usb_hcd *hcd)\r
-{\r
- return usb_register_root_hub (\r
- hcd_to_bus (hcd)->root_hub, hcd->controller);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* exported only within usbcore */\r
-\r
-extern struct list_head usb_bus_list;\r
-extern struct semaphore usb_bus_list_lock;\r
-\r
-extern void usb_bus_get (struct usb_bus *bus);\r
-extern void usb_bus_put (struct usb_bus *bus);\r
-\r
-extern int usb_find_interface_driver (struct usb_device *dev,\r
- struct usb_interface *interface);\r
-\r
-#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep)))\r
-\r
-#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))\r
-\r
-/*\r
- * USB device fs stuff\r
- */\r
-\r
-#ifdef CONFIG_USB_DEVICEFS\r
-\r
-/*\r
- * these are expected to be called from the USB core/hub thread\r
- * with the kernel lock held\r
- */\r
-extern void usbfs_add_bus(struct usb_bus *bus);\r
-extern void usbfs_remove_bus(struct usb_bus *bus);\r
-extern void usbfs_add_device(struct usb_device *dev);\r
-extern void usbfs_remove_device(struct usb_device *dev);\r
-extern void usbfs_update_special (void);\r
-\r
-extern int usbfs_init(void);\r
-extern void usbfs_cleanup(void);\r
-\r
-#else /* CONFIG_USB_DEVICEFS */\r
-\r
-static inline void usbfs_add_bus(struct usb_bus *bus) {}\r
-static inline void usbfs_remove_bus(struct usb_bus *bus) {}\r
-static inline void usbfs_add_device(struct usb_device *dev) {}\r
-static inline void usbfs_remove_device(struct usb_device *dev) {}\r
-static inline void usbfs_update_special (void) {}\r
-\r
-static inline int usbfs_init(void) { return 0; }\r
-static inline void usbfs_cleanup(void) { }\r
-\r
-#endif /* CONFIG_USB_DEVICEFS */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */\r
-// bleech -- resurfaced in 2.4.11 or 2.4.12\r
-#define bitmap DeviceRemovable\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* random stuff */\r
-\r
-#define RUN_CONTEXT (in_irq () ? "in_irq" \\r
- : (in_interrupt () ? "in_interrupt" : "can sleep"))\r
-\r
-\r
-#endif /* __KERNEL__ */\r
-\r
+/*
+ * Copyright (c) 2001-2002 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifdef __KERNEL__
+
+/* This file contains declarations of usbcore internals that are mostly
+ * used or exposed by Host Controller Drivers.
+ */
+
+/*
+ * USB Packet IDs (PIDs)
+ */
+#define USB_PID_UNDEF_0 0xf0
+#define USB_PID_OUT 0xe1
+#define USB_PID_ACK 0xd2
+#define USB_PID_DATA0 0xc3
+#define USB_PID_PING 0xb4 /* USB 2.0 */
+#define USB_PID_SOF 0xa5
+#define USB_PID_NYET 0x96 /* USB 2.0 */
+#define USB_PID_DATA2 0x87 /* USB 2.0 */
+#define USB_PID_SPLIT 0x78 /* USB 2.0 */
+#define USB_PID_IN 0x69
+#define USB_PID_NAK 0x5a
+#define USB_PID_DATA1 0x4b
+#define USB_PID_PREAMBLE 0x3c /* Token mode */
+#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
+#define USB_PID_SETUP 0x2d
+#define USB_PID_STALL 0x1e
+#define USB_PID_MDATA 0x0f /* USB 2.0 */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * USB Host Controller Driver (usb_hcd) framework
+ *
+ * Since "struct usb_bus" is so thin, you can't share much code in it.
+ * This framework is a layer over that, and should be more sharable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+struct usb_hcd { /* usb_bus.hcpriv points to this */
+
+ /*
+ * housekeeping
+ */
+ struct usb_bus self; /* hcd is-a bus */
+
+ const char *product_desc; /* product/vendor string */
+ const char *description; /* "ehci-hcd" etc */
+
+ struct timer_list rh_timer; /* drives root hub */
+ struct list_head dev_list; /* devices on this bus */
+ struct work_struct work;
+
+ /*
+ * hardware info/state
+ */
+ struct hc_driver *driver; /* hw-specific hooks */
+ int irq; /* irq allocated */
+ void *regs; /* device memory/io */
+ struct device *controller; /* handle to hardware */
+
+ /* a few non-PCI controllers exist, mostly for OHCI */
+ struct pci_dev *pdev; /* pci is typical */
+#ifdef CONFIG_PCI
+ int region; /* pci region for regs */
+ u32 pci_state [16]; /* for PM state save */
+ atomic_t resume_count; /* multiple resumes issue */
+#endif
+
+#define HCD_BUFFER_POOLS 4
+ struct pci_pool *pool [HCD_BUFFER_POOLS];
+
+ int state;
+# define __ACTIVE 0x01
+# define __SLEEPY 0x02
+# define __SUSPEND 0x04
+# define __TRANSIENT 0x80
+
+# define USB_STATE_HALT 0
+# define USB_STATE_RUNNING (__ACTIVE)
+# define USB_STATE_READY (__ACTIVE|__SLEEPY)
+# define USB_STATE_QUIESCING (__SUSPEND|__TRANSIENT|__ACTIVE)
+# define USB_STATE_RESUMING (__SUSPEND|__TRANSIENT)
+# define USB_STATE_SUSPENDED (__SUSPEND)
+
+#define HCD_IS_RUNNING(state) ((state) & __ACTIVE)
+#define HCD_IS_SUSPENDED(state) ((state) & __SUSPEND)
+
+ /* more shared queuing code would be good; it should support
+ * smarter scheduling, handle transaction translators, etc;
+ * input size of periodic table to an interrupt scheduler.
+ * (ohci 32, uhci 1024, ehci 256/512/1024).
+ */
+};
+
+/* 2.4 does this a bit differently ... */
+static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
+{
+ return &hcd->self;
+}
+
+
+struct hcd_dev { /* usb_device.hcpriv points to this */
+ struct list_head dev_list; /* on this hcd */
+ struct list_head urb_list; /* pending on this dev */
+
+ /* per-configuration HC/HCD state, such as QH or ED */
+ void *ep[32];
+};
+
+// urb.hcpriv is really hardware-specific
+
+struct hcd_timeout { /* timeouts we allocate */
+ struct list_head timeout_list;
+ struct timer_list timer;
+};
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * FIXME usb_operations should vanish or become hc_driver,
+ * when usb_bus and usb_hcd become the same thing.
+ */
+
+struct usb_operations {
+ int (*allocate)(struct usb_device *);
+ int (*deallocate)(struct usb_device *);
+ int (*get_frame_number) (struct usb_device *usb_dev);
+ int (*submit_urb) (struct urb *urb, int mem_flags);
+ int (*unlink_urb) (struct urb *urb);
+
+ /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
+ void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
+ int mem_flags,
+ dma_addr_t *dma);
+ void (*buffer_free)(struct usb_bus *bus, size_t size,
+ void *addr, dma_addr_t dma);
+
+ void (*disable)(struct usb_device *udev, int bEndpointAddress);
+};
+
+/* each driver provides one of these, and hardware init support */
+
+struct pt_regs;
+
+// new struct from 2.6
+struct hc_driver {
+ const char *description; /* "ehci-hcd" etc */
+
+ /* irq handler */
+ irqreturn_t (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
+
+ int flags;
+#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
+#define HCD_USB11 0x0010 /* USB 1.1 */
+#define HCD_USB2 0x0020 /* USB 2.0 */
+
+ /* called to init HCD and root hub */
+ int (*reset) (struct usb_hcd *hcd);
+ int (*start) (struct usb_hcd *hcd);
+
+ /* called after all devices were suspended */
+ int (*suspend) (struct usb_hcd *hcd, u32 state);
+
+ /* called before any devices get resumed */
+ int (*resume) (struct usb_hcd *hcd);
+
+ /* cleanly make HCD stop writing memory and doing I/O */
+ void (*stop) (struct usb_hcd *hcd);
+
+ /* return current frame number */
+ int (*get_frame_number) (struct usb_hcd *hcd);
+
+ /* memory lifecycle */
+ struct usb_hcd *(*hcd_alloc) (void);
+ void (*hcd_free) (struct usb_hcd *hcd);
+
+ /* manage i/o requests, device state */
+ int (*urb_enqueue) (struct usb_hcd *hcd, struct urb *urb,
+ int mem_flags);
+ int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
+
+ /* hw synch, freeing endpoint resources that urb_dequeue can't */
+ void (*endpoint_disable)(struct usb_hcd *hcd,
+ struct hcd_dev *dev, int bEndpointAddress);
+
+ /* root hub support */
+ int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
+ int (*hub_control) (struct usb_hcd *hcd,
+ u16 typeReq, u16 wValue, u16 wIndex,
+ char *buf, u16 wLength);
+};
+
+// old version, "just in case"
+#if 0
+struct hc_driver {
+ const char *description; /* "ehci-hcd" etc */
+
+ /* irq handler */
+ void (*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
+
+ int flags;
+#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
+#define HCD_USB11 0x0010 /* USB 1.1 */
+#define HCD_USB2 0x0020 /* USB 2.0 */
+
+ /* called to init HCD and root hub */
+ int (*start) (struct usb_hcd *hcd);
+
+ /* called after all devices were suspended */
+ int (*suspend) (struct usb_hcd *hcd, u32 state);
+
+ /* called before any devices get resumed */
+ int (*resume) (struct usb_hcd *hcd);
+
+ /* cleanly make HCD stop writing memory and doing I/O */
+ void (*stop) (struct usb_hcd *hcd);
+
+ /* return current frame number */
+ int (*get_frame_number) (struct usb_hcd *hcd);
+
+ /* memory lifecycle */
+ struct usb_hcd *(*hcd_alloc) (void);
+ void (*hcd_free) (struct usb_hcd *hcd);
+
+ /* manage i/o requests, device state */
+ int (*urb_enqueue) (struct usb_hcd *hcd, struct urb *urb,
+ int mem_flags);
+ int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
+
+ /* hw synch, freeing endpoint resources that urb_dequeue can't */
+ void (*endpoint_disable)(struct usb_hcd *hcd,
+ struct hcd_dev *dev, int bEndpointAddress);
+
+ /* root hub support */
+ int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
+ int (*hub_control) (struct usb_hcd *hcd,
+ u16 typeReq, u16 wValue, u16 wIndex,
+ char *buf, u16 wLength);
+};
+#endif
+
+extern void STDCALL usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
+extern void STDCALL usb_bus_init (struct usb_bus *bus);
+extern void usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb);
+
+#ifdef CONFIG_PCI
+struct pci_dev;
+struct pci_device_id;
+extern int STDCALL usb_hcd_pci_probe (struct pci_dev *dev,
+ const struct pci_device_id *id);
+extern void STDCALL usb_hcd_pci_remove (struct pci_dev *dev);
+
+#ifdef CONFIG_PM
+// FIXME: see Documentation/power/pci.txt (2.4.6 and later?)
+// extern int usb_hcd_pci_save_state (struct pci_dev *dev, u32 state);
+extern int usb_hcd_pci_suspend (struct pci_dev *dev, u32 state);
+extern int usb_hcd_pci_resume (struct pci_dev *dev);
+// extern int usb_hcd_pci_enable_wake (struct pci_dev *dev, u32 state, int flg);
+#endif /* CONFIG_PM */
+
+#endif /* CONFIG_PCI */
+
+/* pci-ish (pdev null is ok) buffer alloc/mapping support */
+int hcd_buffer_create (struct usb_hcd *hcd);
+void hcd_buffer_destroy (struct usb_hcd *hcd);
+
+void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,
+ int mem_flags, dma_addr_t *dma);
+void hcd_buffer_free (struct usb_bus *bus, size_t size,
+ void *addr, dma_addr_t dma);
+
+/* generic bus glue, needed for host controllers that don't use PCI */
+extern struct usb_operations usb_hcd_operations;
+extern irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
+extern void STDCALL usb_hc_died (struct usb_hcd *hcd);
+
+/* -------------------------------------------------------------------------- */
+
+/* Enumeration is only for the hub driver, or HCD virtual root hubs */
+extern int usb_new_device(struct usb_device *dev, struct device *parent);
+extern void STDCALL usb_connect(struct usb_device *dev);
+extern void usb_disconnect(struct usb_device **);
+
+/* exported to hub driver ONLY to support usb_reset_device () */
+extern int usb_get_configuration(struct usb_device *dev);
+extern void usb_set_maxpacket(struct usb_device *dev);
+extern void usb_destroy_configuration(struct usb_device *dev);
+extern int usb_set_address(struct usb_device *dev);
+
+/* use these only before the device's address has been set */
+#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30))
+#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | USB_DIR_IN)
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * HCD Root Hub support
+ */
+
+#include "hub.h"
+
+/* (shifted) direction/type/recipient from the USB 2.0 spec, table 9.2 */
+#define DeviceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+
+#define InterfaceRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+#define EndpointRequest \
+ ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointOutRequest \
+ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+
+/* table 9.6 standard features */
+#define DEVICE_REMOTE_WAKEUP 1
+#define ENDPOINT_HALT 0
+
+/* class requests from the USB 2.0 hub spec, table 11-15 */
+/* GetBusState and SetHubDescriptor are optional, omitted */
+#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus (0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus (0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE)
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Generic bandwidth allocation constants/support
+ */
+#define FRAME_TIME_USECS 1000L
+#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
+ /* Trying not to use worst-case bit-stuffing
+ of (7/6 * 8 * bytecount) = 9.33 * bytecount */
+ /* bytecount = data payload byte count */
+
+#define NS_TO_US(ns) ((ns + 500L) / 1000L)
+ /* convert & round nanoseconds to microseconds */
+
+extern void STDCALL usb_claim_bandwidth (struct usb_device *dev, struct urb *urb,
+ int bustime, int isoc);
+extern void STDCALL usb_release_bandwidth (struct usb_device *dev, struct urb *urb,
+ int isoc);
+
+/*
+ * Full/low speed bandwidth allocation constants/support.
+ */
+#define BW_HOST_DELAY 1000L /* nanoseconds */
+#define BW_HUB_LS_SETUP 333L /* nanoseconds */
+ /* 4 full-speed bit times (est.) */
+
+#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
+#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
+#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
+
+extern int STDCALL usb_check_bandwidth (struct usb_device *dev, struct urb *urb);
+
+/*
+ * Ceiling microseconds (typical) for that many bytes at high speed
+ * ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
+ * to preallocate bandwidth)
+ */
+#define USB2_HOST_DELAY 5 /* nsec, guess */
+#define HS_USECS(bytes) NS_TO_US ( ((55 * 8 * 2083)/1000) \
+ + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ + USB2_HOST_DELAY)
+#define HS_USECS_ISO(bytes) NS_TO_US ( ((long)(38 * 8 * 2.083)) \
+ + ((2083UL * (3167 + BitTime (bytes)))/1000) \
+ + USB2_HOST_DELAY)
+
+extern long STDCALL usb_calc_bus_time (int speed, int is_input,
+ int isoc, int bytecount);
+
+/*-------------------------------------------------------------------------*/
+
+extern struct usb_bus STDCALL *usb_alloc_bus (struct usb_operations *);
+extern void STDCALL usb_free_bus (struct usb_bus *);
+
+extern void STDCALL usb_register_bus (struct usb_bus *);
+extern void STDCALL usb_deregister_bus (struct usb_bus *);
+
+extern int STDCALL usb_register_root_hub (struct usb_device *usb_dev,
+ struct device *parent_dev);
+
+/* for portability to 2.4, hcds should call this */
+static inline int hcd_register_root (struct usb_hcd *hcd)
+{
+ return usb_register_root_hub (
+ hcd_to_bus (hcd)->root_hub, hcd->controller);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* exported only within usbcore */
+
+extern struct list_head usb_bus_list;
+extern struct semaphore usb_bus_list_lock;
+
+extern void usb_bus_get (struct usb_bus *bus);
+extern void usb_bus_put (struct usb_bus *bus);
+
+extern int usb_find_interface_driver (struct usb_device *dev,
+ struct usb_interface *interface);
+
+#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep)))
+
+#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
+
+/*
+ * USB device fs stuff
+ */
+
+#ifdef CONFIG_USB_DEVICEFS
+
+/*
+ * these are expected to be called from the USB core/hub thread
+ * with the kernel lock held
+ */
+extern void usbfs_add_bus(struct usb_bus *bus);
+extern void usbfs_remove_bus(struct usb_bus *bus);
+extern void usbfs_add_device(struct usb_device *dev);
+extern void usbfs_remove_device(struct usb_device *dev);
+extern void usbfs_update_special (void);
+
+extern int usbfs_init(void);
+extern void usbfs_cleanup(void);
+
+#else /* CONFIG_USB_DEVICEFS */
+
+static inline void usbfs_add_bus(struct usb_bus *bus) {}
+static inline void usbfs_remove_bus(struct usb_bus *bus) {}
+static inline void usbfs_add_device(struct usb_device *dev) {}
+static inline void usbfs_remove_device(struct usb_device *dev) {}
+static inline void usbfs_update_special (void) {}
+
+static inline int usbfs_init(void) { return 0; }
+static inline void usbfs_cleanup(void) { }
+
+#endif /* CONFIG_USB_DEVICEFS */
+
+/*-------------------------------------------------------------------------*/
+
+/* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */
+// bleech -- resurfaced in 2.4.11 or 2.4.12
+#define bitmap DeviceRemovable
+
+
+/*-------------------------------------------------------------------------*/
+
+/* random stuff */
+
+#define RUN_CONTEXT (in_irq () ? "in_irq" \
+ : (in_interrupt () ? "in_interrupt" : "can sleep"))
+
+
+#endif /* __KERNEL__ */
+
-/*\r
- * USB hub driver.\r
- *\r
- * (C) Copyright 1999 Linus Torvalds\r
- * (C) Copyright 1999 Johannes Erdfelt\r
- * (C) Copyright 1999 Gregory P. Smith\r
- * (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au)\r
- *\r
- */\r
-#define DEBUG\r
-#if 0\r
-#include <linux/config.h>\r
-#include <linux/kernel.h>\r
-#include <linux/errno.h>\r
-#include <linux/module.h>\r
-#include <linux/completion.h>\r
-#include <linux/sched.h>\r
-#include <linux/list.h>\r
-#include <linux/slab.h>\r
-#include <linux/smp_lock.h>\r
-#include <linux/ioctl.h>\r
-#ifdef CONFIG_USB_DEBUG\r
- #define DEBUG\r
-#else\r
- #undef DEBUG\r
-#endif\r
-#include <linux/usb.h>\r
-#include <linux/usbdevice_fs.h>\r
-#include <linux/suspend.h>\r
-\r
-#include <asm/semaphore.h>\r
-#include <asm/uaccess.h>\r
-#include <asm/byteorder.h>\r
-\r
-#include "hcd.h"\r
-#include "hub.h"\r
-#else\r
-#include "../usb_wrapper.h"\r
-#include "hcd.h"\r
-#include "hub.h"\r
-#define DEBUG\r
-#endif\r
-\r
-/* Wakes up khubd */\r
-static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;\r
-static DECLARE_MUTEX(usb_address0_sem);\r
-\r
-static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */\r
-static LIST_HEAD(hub_list); /* List of all hubs (for cleanup) */\r
-\r
-static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);\r
-static pid_t khubd_pid = 0; /* PID of khubd */\r
-static DECLARE_COMPLETION(khubd_exited);\r
-\r
-#ifdef DEBUG\r
-static inline char *portspeed (int portstatus)\r
-{\r
- if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))\r
- return "480 Mb/s";\r
- else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))\r
- return "1.5 Mb/s";\r
- else\r
- return "12 Mb/s";\r
-}\r
-#endif\r
-\r
-/* for dev_info, dev_dbg, etc */\r
-static inline struct device *hubdev (struct usb_device *dev)\r
-{\r
- return &dev->actconfig->interface [0].dev;\r
-}\r
-\r
-/* USB 2.0 spec Section 11.24.4.5 */\r
-static int get_hub_descriptor(struct usb_device *dev, void *data, int size)\r
-{\r
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),\r
- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,\r
- USB_DT_HUB << 8, 0, data, size, HZ * USB_CTRL_GET_TIMEOUT);\r
-}\r
-\r
-/*\r
- * USB 2.0 spec Section 11.24.2.1\r
- */\r
-static int clear_hub_feature(struct usb_device *dev, int feature)\r
-{\r
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),\r
- USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, HZ);\r
-}\r
-\r
-/*\r
- * USB 2.0 spec Section 11.24.2.2\r
- * BUG: doesn't handle port indicator selector in high byte of wIndex\r
- */\r
-static int clear_port_feature(struct usb_device *dev, int port, int feature)\r
-{\r
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),\r
- USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);\r
-}\r
-\r
-/*\r
- * USB 2.0 spec Section 11.24.2.13\r
- * BUG: doesn't handle port indicator selector in high byte of wIndex\r
- */\r
-static int set_port_feature(struct usb_device *dev, int port, int feature)\r
-{\r
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),\r
- USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);\r
-}\r
-\r
-/*\r
- * USB 2.0 spec Section 11.24.2.6\r
- */\r
-static int get_hub_status(struct usb_device *dev,\r
- struct usb_hub_status *data)\r
-{\r
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),\r
- USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,\r
- data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);\r
-}\r
-\r
-/*\r
- * USB 2.0 spec Section 11.24.2.7\r
- */\r
-static int get_port_status(struct usb_device *dev, int port,\r
- struct usb_port_status *data)\r
-{\r
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),\r
- USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,\r
- data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);\r
-}\r
-\r
-/* completion function, fires on port status changes and various faults */\r
-static void hub_irq(struct urb *urb, struct pt_regs *regs)\r
-{\r
- struct usb_hub *hub = (struct usb_hub *)urb->context;\r
- unsigned long flags;\r
- int status;\r
-\r
- switch (urb->status) {\r
- case -ENOENT: /* synchronous unlink */\r
- case -ECONNRESET: /* async unlink */\r
- case -ESHUTDOWN: /* hardware going away */\r
- return;\r
-\r
- default: /* presumably an error */\r
- /* Cause a hub reset after 10 consecutive errors */\r
- dev_dbg (&hub->intf->dev, "transfer --> %d\n", urb->status);\r
- if ((++hub->nerrors < 10) || hub->error)\r
- goto resubmit;\r
- hub->error = urb->status;\r
- /* FALL THROUGH */\r
- \r
- /* let khubd handle things */\r
- case 0: /* we got data: port status changed */\r
- break;\r
- }\r
-\r
- hub->nerrors = 0;\r
-\r
- /* Something happened, let khubd figure it out */\r
- spin_lock_irqsave(&hub_event_lock, flags);\r
- if (list_empty(&hub->event_list)) {\r
- list_add(&hub->event_list, &hub_event_list);\r
- wake_up(&khubd_wait);\r
- }\r
- spin_unlock_irqrestore(&hub_event_lock, flags);\r
-\r
-resubmit:\r
- if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0\r
- /* ENODEV means we raced disconnect() */\r
- && status != -ENODEV)\r
- dev_err (&hub->intf->dev, "resubmit --> %d\n", urb->status);\r
-}\r
-\r
-/* USB 2.0 spec Section 11.24.2.3 */\r
-static inline int\r
-hub_clear_tt_buffer (struct usb_device *hub, u16 devinfo, u16 tt)\r
-{\r
- return usb_control_msg (hub, usb_rcvctrlpipe (hub, 0),\r
- HUB_CLEAR_TT_BUFFER, USB_DIR_IN | USB_RECIP_OTHER,\r
- devinfo, tt, 0, 0, HZ);\r
-}\r
-\r
-/*\r
- * enumeration blocks khubd for a long time. we use keventd instead, since\r
- * long blocking there is the exception, not the rule. accordingly, HCDs\r
- * talking to TTs must queue control transfers (not just bulk and iso), so\r
- * both can talk to the same hub concurrently.\r
- */\r
-static void hub_tt_kevent (void *arg)\r
-{\r
- struct usb_hub *hub = arg;\r
- unsigned long flags;\r
-\r
- spin_lock_irqsave (&hub->tt.lock, flags);\r
- while (!list_empty (&hub->tt.clear_list)) {\r
- struct list_head *temp;\r
- struct usb_tt_clear *clear;\r
- struct usb_device *dev;\r
- int status;\r
-\r
- temp = hub->tt.clear_list.next;\r
- clear = list_entry (temp, struct usb_tt_clear, clear_list);\r
- list_del (&clear->clear_list);\r
-\r
- /* drop lock so HCD can concurrently report other TT errors */\r
- spin_unlock_irqrestore (&hub->tt.lock, flags);\r
- dev = interface_to_usbdev (hub->intf);\r
- status = hub_clear_tt_buffer (dev, clear->devinfo, clear->tt);\r
- spin_lock_irqsave (&hub->tt.lock, flags);\r
-\r
- if (status)\r
- err ("usb-%s-%s clear tt %d (%04x) error %d",\r
- dev->bus->bus_name, dev->devpath,\r
- clear->tt, clear->devinfo, status);\r
- kfree (clear);\r
- }\r
- spin_unlock_irqrestore (&hub->tt.lock, flags);\r
-}\r
-\r
-/**\r
- * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub\r
- * @dev: the device whose split transaction failed\r
- * @pipe: identifies the endpoint of the failed transaction\r
- *\r
- * High speed HCDs use this to tell the hub driver that some split control or\r
- * bulk transaction failed in a way that requires clearing internal state of\r
- * a transaction translator. This is normally detected (and reported) from\r
- * interrupt context.\r
- *\r
- * It may not be possible for that hub to handle additional full (or low)\r
- * speed transactions until that state is fully cleared out.\r
- */\r
-void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe)\r
-{\r
- struct usb_tt *tt = dev->tt;\r
- unsigned long flags;\r
- struct usb_tt_clear *clear;\r
-\r
- /* we've got to cope with an arbitrary number of pending TT clears,\r
- * since each TT has "at least two" buffers that can need it (and\r
- * there can be many TTs per hub). even if they're uncommon.\r
- */\r
- if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == 0) {\r
- err ("can't save CLEAR_TT_BUFFER state for hub at usb-%s-%s",\r
- dev->bus->bus_name, tt->hub->devpath);\r
- /* FIXME recover somehow ... RESET_TT? */\r
- return;\r
- }\r
-\r
- /* info that CLEAR_TT_BUFFER needs */\r
- clear->tt = tt->multi ? dev->ttport : 1;\r
- clear->devinfo = usb_pipeendpoint (pipe);\r
- clear->devinfo |= dev->devnum << 4;\r
- clear->devinfo |= usb_pipecontrol (pipe)\r
- ? (USB_ENDPOINT_XFER_CONTROL << 11)\r
- : (USB_ENDPOINT_XFER_BULK << 11);\r
- if (usb_pipein (pipe))\r
- clear->devinfo |= 1 << 15;\r
- \r
- /* tell keventd to clear state for this TT */\r
- spin_lock_irqsave (&tt->lock, flags);\r
- list_add_tail (&clear->clear_list, &tt->clear_list);\r
- schedule_work (&tt->kevent);\r
- spin_unlock_irqrestore (&tt->lock, flags);\r
-}\r
-\r
-static void hub_power_on(struct usb_hub *hub)\r
-{\r
- struct usb_device *dev;\r
- int i;\r
-\r
- /* Enable power to the ports */\r
- dev_dbg(hubdev(interface_to_usbdev(hub->intf)),\r
- "enabling power on all ports\n");\r
- dev = interface_to_usbdev(hub->intf);\r
-\r
- for (i = 0; i < hub->descriptor->bNbrPorts; i++)\r
- set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);\r
-\r
- /* Wait for power to be enabled */\r
- wait_ms(hub->descriptor->bPwrOn2PwrGood * 2);\r
-}\r
-\r
-static int hub_hub_status(struct usb_hub *hub,\r
- u16 *status, u16 *change)\r
-{\r
- struct usb_device *dev = interface_to_usbdev (hub->intf);\r
- int ret;\r
-\r
- ret = get_hub_status(dev, &hub->status->hub);\r
- if (ret < 0)\r
- dev_err (hubdev (dev),\r
- "%s failed (err = %d)\n", __FUNCTION__, ret);\r
- else {\r
- *status = le16_to_cpu(hub->status->hub.wHubStatus);\r
- *change = le16_to_cpu(hub->status->hub.wHubChange); \r
- ret = 0;\r
- }\r
- return ret;\r
-}\r
-\r
-static int hub_configure(struct usb_hub *hub,\r
- struct usb_endpoint_descriptor *endpoint)\r
-{\r
- struct usb_device *dev = interface_to_usbdev (hub->intf);\r
- struct device *hub_dev;\r
- u16 hubstatus, hubchange;\r
- unsigned int pipe;\r
- int maxp, ret;\r
- char *message;\r
-\r
- hub->buffer = usb_buffer_alloc(dev, sizeof(*hub->buffer), GFP_KERNEL,\r
- &hub->buffer_dma);\r
- if (!hub->buffer) {\r
- message = "can't allocate hub irq buffer";\r
- ret = -ENOMEM;\r
- goto fail;\r
- }\r
-\r
- hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);\r
- if (!hub->status) {\r
- message = "can't kmalloc hub status buffer";\r
- ret = -ENOMEM;\r
- goto fail;\r
- }\r
-\r
- hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);\r
- if (!hub->descriptor) {\r
- message = "can't kmalloc hub descriptor";\r
- ret = -ENOMEM;\r
- goto fail;\r
- }\r
-\r
- /* Request the entire hub descriptor.\r
- * hub->descriptor can handle USB_MAXCHILDREN ports,\r
- * but the hub can/will return fewer bytes here.\r
- */\r
- ret = get_hub_descriptor(dev, hub->descriptor,\r
- sizeof(*hub->descriptor));\r
- if (ret < 0) {\r
- message = "can't read hub descriptor";\r
- goto fail;\r
- } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {\r
- message = "hub has too many ports!";\r
- ret = -ENODEV;\r
- goto fail;\r
- }\r
-\r
- hub_dev = hubdev(dev);\r
- dev->maxchild = hub->descriptor->bNbrPorts;\r
- dev_info (hub_dev, "%d port%s detected\n", dev->maxchild,\r
- (dev->maxchild == 1) ? "" : "s");\r
-\r
- le16_to_cpus(&hub->descriptor->wHubCharacteristics);\r
-\r
- if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) {\r
- int i;\r
- char portstr [USB_MAXCHILDREN + 1];\r
-\r
- for (i = 0; i < dev->maxchild; i++)\r
- portstr[i] = hub->descriptor->DeviceRemovable\r
- [((i + 1) / 8)] & (1 << ((i + 1) % 8))\r
- ? 'F' : 'R';\r
- portstr[dev->maxchild] = 0;\r
- dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);\r
- } else\r
- dev_dbg(hub_dev, "standalone hub\n");\r
-\r
- switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) {\r
- case 0x00:\r
- dev_dbg(hub_dev, "ganged power switching\n");\r
- break;\r
- case 0x01:\r
- dev_dbg(hub_dev, "individual port power switching\n");\r
- break;\r
- case 0x02:\r
- case 0x03:\r
- dev_dbg(hub_dev, "unknown reserved power switching mode\n");\r
- break;\r
- }\r
-\r
- switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) {\r
- case 0x00:\r
- dev_dbg(hub_dev, "global over-current protection\n");\r
- break;\r
- case 0x08:\r
- dev_dbg(hub_dev, "individual port over-current protection\n");\r
- break;\r
- case 0x10:\r
- case 0x18:\r
- dev_dbg(hub_dev, "no over-current protection\n");\r
- break;\r
- }\r
-\r
- spin_lock_init (&hub->tt.lock);\r
- INIT_LIST_HEAD (&hub->tt.clear_list);\r
- INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub);\r
- switch (dev->descriptor.bDeviceProtocol) {\r
- case 0:\r
- break;\r
- case 1:\r
- dev_dbg(hub_dev, "Single TT\n");\r
- hub->tt.hub = dev;\r
- break;\r
- case 2:\r
- dev_dbg(hub_dev, "TT per port\n");\r
- hub->tt.hub = dev;\r
- hub->tt.multi = 1;\r
- break;\r
- default:\r
- dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",\r
- dev->descriptor.bDeviceProtocol);\r
- break;\r
- }\r
-\r
- switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {\r
- case 0x00:\r
- if (dev->descriptor.bDeviceProtocol != 0)\r
- dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n");\r
- break;\r
- case 0x20:\r
- dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n");\r
- break;\r
- case 0x40:\r
- dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n");\r
- break;\r
- case 0x60:\r
- dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n");\r
- break;\r
- }\r
-\r
- dev_dbg(hub_dev, "Port indicators are %s supported\n", \r
- (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND)\r
- ? "" : "not");\r
-\r
- dev_dbg(hub_dev, "power on to power good time: %dms\n",\r
- hub->descriptor->bPwrOn2PwrGood * 2);\r
- dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",\r
- hub->descriptor->bHubContrCurrent);\r
-\r
- ret = hub_hub_status(hub, &hubstatus, &hubchange);\r
- if (ret < 0) {\r
- message = "can't get hub status";\r
- goto fail;\r
- }\r
-\r
- dev_dbg(hub_dev, "local power source is %s\n",\r
- (hubstatus & HUB_STATUS_LOCAL_POWER)\r
- ? "lost (inactive)" : "good");\r
-\r
- dev_dbg(hub_dev, "%sover-current condition exists\n",\r
- (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");\r
-\r
- /* Start the interrupt endpoint */\r
- pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);\r
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));\r
-\r
- if (maxp > sizeof(*hub->buffer))\r
- maxp = sizeof(*hub->buffer);\r
-\r
- hub->urb = usb_alloc_urb(0, GFP_KERNEL);\r
- if (!hub->urb) {\r
- message = "couldn't allocate interrupt urb";\r
- ret = -ENOMEM;\r
- goto fail;\r
- }\r
-\r
- usb_fill_int_urb(hub->urb, dev, pipe, *hub->buffer, maxp, hub_irq,\r
- hub, endpoint->bInterval);\r
- hub->urb->transfer_dma = hub->buffer_dma;\r
- hub->urb->transfer_flags |= URB_NO_DMA_MAP;\r
- ret = usb_submit_urb(hub->urb, GFP_KERNEL);\r
- if (ret) {\r
- message = "couldn't submit status urb";\r
- goto fail;\r
- }\r
-\r
- /* Wake up khubd */\r
- wake_up(&khubd_wait);\r
-\r
- hub_power_on(hub);\r
-\r
- return 0;\r
-\r
-fail:\r
- dev_err (&hub->intf->dev, "config failed, %s (err %d)\n",\r
- message, ret);\r
- /* hub_disconnect() frees urb and descriptor */\r
- return ret;\r
-}\r
-\r
-static void hub_disconnect(struct usb_interface *intf)\r
-{\r
- struct usb_hub *hub = usb_get_intfdata (intf);\r
- unsigned long flags;\r
-\r
- if (!hub)\r
- return;\r
-\r
- usb_set_intfdata (intf, NULL);\r
- spin_lock_irqsave(&hub_event_lock, flags);\r
-\r
- /* Delete it and then reset it */\r
- list_del(&hub->event_list);\r
- INIT_LIST_HEAD(&hub->event_list);\r
- list_del(&hub->hub_list);\r
- INIT_LIST_HEAD(&hub->hub_list);\r
-\r
- spin_unlock_irqrestore(&hub_event_lock, flags);\r
-\r
- down(&hub->khubd_sem); /* Wait for khubd to leave this hub alone. */\r
- up(&hub->khubd_sem);\r
-\r
- /* assuming we used keventd, it must quiesce too */\r
- if (hub->tt.hub)\r
- flush_scheduled_work ();\r
-\r
- if (hub->urb) {\r
- usb_unlink_urb(hub->urb);\r
- usb_free_urb(hub->urb);\r
- hub->urb = NULL;\r
- }\r
-\r
- if (hub->descriptor) {\r
- kfree(hub->descriptor);\r
- hub->descriptor = NULL;\r
- }\r
-\r
- if (hub->status) {\r
- kfree(hub->status);\r
- hub->status = NULL;\r
- }\r
-\r
- if (hub->buffer) {\r
- usb_buffer_free(interface_to_usbdev(intf),\r
- sizeof(*hub->buffer), hub->buffer,\r
- hub->buffer_dma);\r
- hub->buffer = NULL;\r
- }\r
-\r
- /* Free the memory */\r
- kfree(hub);\r
-}\r
-\r
-static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)\r
-{\r
- struct usb_host_interface *desc;\r
- struct usb_endpoint_descriptor *endpoint;\r
- struct usb_device *dev;\r
- struct usb_hub *hub;\r
- unsigned long flags;\r
-\r
- desc = intf->altsetting + intf->act_altsetting;\r
- dev = interface_to_usbdev(intf);\r
-\r
- /* Some hubs have a subclass of 1, which AFAICT according to the */\r
- /* specs is not defined, but it works */\r
- if ((desc->desc.bInterfaceSubClass != 0) &&\r
- (desc->desc.bInterfaceSubClass != 1)) {\r
-descriptor_error:\r
- dev_err (&intf->dev, "bad descriptor, ignoring hub\n");\r
- return -EIO;\r
- }\r
-\r
- /* Multiple endpoints? What kind of mutant ninja-hub is this? */\r
- if (desc->desc.bNumEndpoints != 1) {\r
- goto descriptor_error;\r
- }\r
-\r
- endpoint = &desc->endpoint[0].desc;\r
-\r
- /* Output endpoint? Curiouser and curiouser.. */\r
- if (!(endpoint->bEndpointAddress & USB_DIR_IN)) {\r
- goto descriptor_error;\r
- }\r
-\r
- /* If it's not an interrupt endpoint, we'd better punt! */\r
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)\r
- != USB_ENDPOINT_XFER_INT) {\r
- goto descriptor_error;\r
- return -EIO;\r
- }\r
-\r
- /* We found a hub */\r
- dev_info (hubdev (dev), "USB hub found\n");\r
-\r
- hub = kmalloc(sizeof(*hub), GFP_KERNEL);\r
- if (!hub) {\r
- err("couldn't kmalloc hub struct");\r
- return -ENOMEM;\r
- }\r
-\r
- memset(hub, 0, sizeof(*hub));\r
-\r
- INIT_LIST_HEAD(&hub->event_list);\r
- hub->intf = intf;\r
- init_MUTEX(&hub->khubd_sem);\r
-\r
- /* Record the new hub's existence */\r
- spin_lock_irqsave(&hub_event_lock, flags);\r
- INIT_LIST_HEAD(&hub->hub_list);\r
- list_add(&hub->hub_list, &hub_list);\r
- spin_unlock_irqrestore(&hub_event_lock, flags);\r
-\r
- usb_set_intfdata (intf, hub);\r
-\r
- if (hub_configure(hub, endpoint) >= 0) {\r
- strcpy (intf->dev.name, "Hub");\r
- return 0;\r
- }\r
-\r
- hub_disconnect (intf);\r
- return -ENODEV;\r
-}\r
-\r
-static int\r
-hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)\r
-{\r
- struct usb_device *hub = interface_to_usbdev (intf);\r
-\r
- /* assert ifno == 0 (part of hub spec) */\r
- switch (code) {\r
- case USBDEVFS_HUB_PORTINFO: {\r
- struct usbdevfs_hub_portinfo *info = user_data;\r
- unsigned long flags;\r
- int i;\r
-\r
- spin_lock_irqsave(&hub_event_lock, flags);\r
- if (hub->devnum <= 0)\r
- info->nports = 0;\r
- else {\r
- info->nports = hub->maxchild;\r
- for (i = 0; i < info->nports; i++) {\r
- if (hub->children[i] == NULL)\r
- info->port[i] = 0;\r
- else\r
- info->port[i] =\r
- hub->children[i]->devnum;\r
- }\r
- }\r
- spin_unlock_irqrestore(&hub_event_lock, flags);\r
-\r
- return info->nports + 1;\r
- }\r
-\r
- default:\r
- return -ENOSYS;\r
- }\r
-}\r
-\r
-static int hub_reset(struct usb_hub *hub)\r
-{\r
- struct usb_device *dev = interface_to_usbdev(hub->intf);\r
- int i;\r
-\r
- /* Disconnect any attached devices */\r
- for (i = 0; i < hub->descriptor->bNbrPorts; i++) {\r
- if (dev->children[i])\r
- usb_disconnect(&dev->children[i]);\r
- }\r
-\r
- /* Attempt to reset the hub */\r
- if (hub->urb)\r
- usb_unlink_urb(hub->urb);\r
- else\r
- return -1;\r
-\r
- if (usb_reset_device(dev))\r
- return -1;\r
-\r
- hub->urb->dev = dev; \r
- if (usb_submit_urb(hub->urb, GFP_KERNEL))\r
- return -1;\r
-\r
- hub_power_on(hub);\r
-\r
- return 0;\r
-}\r
-\r
-static void hub_start_disconnect(struct usb_device *dev)\r
-{\r
- struct usb_device *parent = dev->parent;\r
- int i;\r
-\r
- /* Find the device pointer to disconnect */\r
- if (parent) {\r
- for (i = 0; i < parent->maxchild; i++) {\r
- if (parent->children[i] == dev) {\r
- usb_disconnect(&parent->children[i]);\r
- return;\r
- }\r
- }\r
- }\r
-\r
- err("cannot disconnect hub %s", dev->devpath);\r
-}\r
-\r
-static int hub_port_status(struct usb_device *dev, int port,\r
- u16 *status, u16 *change)\r
-{\r
- struct usb_hub *hub = usb_get_intfdata (dev->actconfig->interface);\r
- int ret;\r
-\r
- ret = get_port_status(dev, port + 1, &hub->status->port);\r
- if (ret < 0)\r
- dev_err (hubdev (dev),\r
- "%s failed (err = %d)\n", __FUNCTION__, ret);\r
- else {\r
- *status = le16_to_cpu(hub->status->port.wPortStatus);\r
- *change = le16_to_cpu(hub->status->port.wPortChange); \r
- ret = 0;\r
- }\r
- return ret;\r
-}\r
-\r
-#define HUB_RESET_TRIES 5\r
-#define HUB_PROBE_TRIES 2\r
-#define HUB_SHORT_RESET_TIME 10\r
-#define HUB_LONG_RESET_TIME 200\r
-#define HUB_RESET_TIMEOUT 500\r
-\r
-/* return: -1 on error, 0 on success, 1 on disconnect. */\r
-static int hub_port_wait_reset(struct usb_device *hub, int port,\r
- struct usb_device *dev, unsigned int delay)\r
-{\r
- int delay_time, ret;\r
- u16 portstatus;\r
- u16 portchange;\r
-\r
- for (delay_time = 0;\r
- delay_time < HUB_RESET_TIMEOUT;\r
- delay_time += delay) {\r
- /* wait to give the device a chance to reset */\r
- wait_ms(delay);\r
-\r
- /* read and decode port status */\r
- ret = hub_port_status(hub, port, &portstatus, &portchange);\r
- if (ret < 0) {\r
- return -1;\r
- }\r
-\r
- /* Device went away? */\r
- if (!(portstatus & USB_PORT_STAT_CONNECTION))\r
- return 1;\r
-\r
- /* bomb out completely if something weird happened */\r
- if ((portchange & USB_PORT_STAT_C_CONNECTION))\r
- return -1;\r
-\r
- /* if we`ve finished resetting, then break out of the loop */\r
- if (!(portstatus & USB_PORT_STAT_RESET) &&\r
- (portstatus & USB_PORT_STAT_ENABLE)) {\r
- if (portstatus & USB_PORT_STAT_HIGH_SPEED)\r
- dev->speed = USB_SPEED_HIGH;\r
- else if (portstatus & USB_PORT_STAT_LOW_SPEED)\r
- dev->speed = USB_SPEED_LOW;\r
- else\r
- dev->speed = USB_SPEED_FULL;\r
- return 0;\r
- }\r
-\r
- /* switch to the long delay after two short delay failures */\r
- if (delay_time >= 2 * HUB_SHORT_RESET_TIME)\r
- delay = HUB_LONG_RESET_TIME;\r
-\r
- dev_dbg (hubdev (hub),\r
- "port %d not reset yet, waiting %dms\n",\r
- port + 1, delay);\r
- }\r
-\r
- return -1;\r
-}\r
-\r
-/* return: -1 on error, 0 on success, 1 on disconnect. */\r
-static int hub_port_reset(struct usb_device *hub, int port,\r
- struct usb_device *dev, unsigned int delay)\r
-{\r
- int i, status;\r
-\r
- /* Reset the port */\r
- for (i = 0; i < HUB_RESET_TRIES; i++) {\r
- set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);\r
-\r
- /* return on disconnect or reset */\r
- status = hub_port_wait_reset(hub, port, dev, delay);\r
- if (status != -1) {\r
- clear_port_feature(hub,\r
- port + 1, USB_PORT_FEAT_C_RESET);\r
- dev->state = status\r
- ? USB_STATE_NOTATTACHED\r
- : USB_STATE_DEFAULT;\r
- return status;\r
- }\r
-\r
- dev_dbg (hubdev (hub),\r
- "port %d not enabled, trying reset again...\n",\r
- port + 1);\r
- delay = HUB_LONG_RESET_TIME;\r
- }\r
-\r
- dev_err (hubdev (hub),\r
- "Cannot enable port %i. Maybe the USB cable is bad?\n",\r
- port + 1);\r
-\r
- return -1;\r
-}\r
-\r
-int hub_port_disable(struct usb_device *hub, int port)\r
-{\r
- int ret;\r
-\r
- ret = clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE);\r
- if (ret)\r
- dev_err(hubdev(hub), "cannot disable port %d (err = %d)\n",\r
- port + 1, ret);\r
-\r
- return ret;\r
-}\r
-\r
-/* USB 2.0 spec, 7.1.7.3 / fig 7-29:\r
- *\r
- * Between connect detection and reset signaling there must be a delay\r
- * of 100ms at least for debounce and power-settling. The corresponding\r
- * timer shall restart whenever the downstream port detects a disconnect.\r
- * \r
- * Apparently there are some bluetooth and irda-dongles and a number\r
- * of low-speed devices which require longer delays of about 200-400ms.\r
- * Not covered by the spec - but easy to deal with.\r
- *\r
- * This implementation uses 400ms minimum debounce timeout and checks\r
- * every 25ms for transient disconnects to restart the delay.\r
- */\r
-\r
-#define HUB_DEBOUNCE_TIMEOUT 400\r
-#define HUB_DEBOUNCE_STEP 25\r
-#define HUB_DEBOUNCE_STABLE 4\r
-\r
-/* return: -1 on error, 0 on success, 1 on disconnect. */\r
-static int hub_port_debounce(struct usb_device *hub, int port)\r
-{\r
- int ret;\r
- int delay_time, stable_count;\r
- u16 portchange, portstatus;\r
- unsigned connection;\r
-\r
- connection = 0;\r
- stable_count = 0;\r
- for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; delay_time += HUB_DEBOUNCE_STEP) {\r
- wait_ms(HUB_DEBOUNCE_STEP);\r
-\r
- ret = hub_port_status(hub, port, &portstatus, &portchange);\r
- if (ret < 0)\r
- return -1;\r
-\r
- if ((portstatus & USB_PORT_STAT_CONNECTION) == connection) {\r
- if (connection) {\r
- if (++stable_count == HUB_DEBOUNCE_STABLE)\r
- break;\r
- }\r
- } else {\r
- stable_count = 0;\r
- }\r
- connection = portstatus & USB_PORT_STAT_CONNECTION;\r
-\r
- if ((portchange & USB_PORT_STAT_C_CONNECTION)) {\r
- clear_port_feature(hub, port+1, USB_PORT_FEAT_C_CONNECTION);\r
- }\r
- }\r
-\r
- /* XXX Replace this with dbg() when 2.6 is about to ship. */\r
- dev_dbg (hubdev (hub),\r
- "debounce: port %d: delay %dms stable %d status 0x%x\n",\r
- port + 1, delay_time, stable_count, portstatus);\r
-\r
- return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1;\r
-}\r
-\r
-static void hub_port_connect_change(struct usb_hub *hubstate, int port,\r
- u16 portstatus, u16 portchange)\r
-{\r
- struct usb_device *hub = interface_to_usbdev(hubstate->intf);\r
- struct usb_device *dev;\r
- unsigned int delay = HUB_SHORT_RESET_TIME;\r
- int i;\r
-\r
- dev_dbg (&hubstate->intf->dev,\r
- "port %d, status %x, change %x, %s\n",\r
- port + 1, portstatus, portchange, portspeed (portstatus));\r
-\r
- /* Clear the connection change status */\r
- clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);\r
-\r
- /* Disconnect any existing devices under this port */\r
- if (hub->children[port])\r
- usb_disconnect(&hub->children[port]);\r
-\r
- /* Return now if nothing is connected */\r
- if (!(portstatus & USB_PORT_STAT_CONNECTION)) {\r
- if (portstatus & USB_PORT_STAT_ENABLE)\r
- hub_port_disable(hub, port);\r
-\r
- return;\r
- }\r
-\r
- if (hub_port_debounce(hub, port)) {\r
- dev_err (&hubstate->intf->dev,\r
- "connect-debounce failed, port %d disabled\n",\r
- port+1);\r
- hub_port_disable(hub, port);\r
- return;\r
- }\r
-\r
- /* Some low speed devices have problems with the quick delay, so */\r
- /* be a bit pessimistic with those devices. RHbug #23670 */\r
- if (portstatus & USB_PORT_STAT_LOW_SPEED)\r
- delay = HUB_LONG_RESET_TIME;\r
-\r
- down(&usb_address0_sem);\r
-\r
- for (i = 0; i < HUB_PROBE_TRIES; i++) {\r
- struct usb_device *pdev;\r
- int len;\r
-\r
- /* Allocate a new device struct */\r
- dev = usb_alloc_dev(hub, hub->bus);\r
- if (!dev) {\r
- dev_err (&hubstate->intf->dev,\r
- "couldn't allocate usb_device\n");\r
- break;\r
- }\r
-\r
- hub->children[port] = dev;\r
- dev->state = USB_STATE_POWERED;\r
-\r
- /* Reset the device, and detect its speed */\r
- if (hub_port_reset(hub, port, dev, delay)) {\r
- usb_put_dev(dev);\r
- break;\r
- }\r
-\r
- /* Find a new address for it */\r
- usb_connect(dev);\r
-\r
- /* Set up TT records, if needed */\r
- if (hub->tt) {\r
- dev->tt = hub->tt;\r
- dev->ttport = hub->ttport;\r
- } else if (dev->speed != USB_SPEED_HIGH\r
- && hub->speed == USB_SPEED_HIGH) {\r
- dev->tt = &hubstate->tt;\r
- dev->ttport = port + 1;\r
- }\r
-\r
- /* Save readable and stable topology id, distinguishing\r
- * devices by location for diagnostics, tools, etc. The\r
- * string is a path along hub ports, from the root. Each\r
- * device's id will be stable until USB is re-cabled, and\r
- * hubs are often labeled with these port numbers.\r
- *\r
- * Initial size: ".NN" times five hubs + NUL = 16 bytes max\r
- * (quite rare, since most hubs have 4-6 ports).\r
- */\r
- pdev = dev->parent;\r
- if (pdev->devpath [0] != '0') /* parent not root? */\r
- len = snprintf (dev->devpath, sizeof dev->devpath,\r
- "%s.%d", pdev->devpath, port + 1);\r
- /* root == "0", root port 2 == "2", port 3 that hub "2.3" */\r
- else\r
- len = snprintf (dev->devpath, sizeof dev->devpath,\r
- "%d", port + 1);\r
- if (len == sizeof dev->devpath)\r
- dev_err (&hubstate->intf->dev,\r
- "devpath size! usb/%03d/%03d path %s\n",\r
- dev->bus->busnum, dev->devnum, dev->devpath);\r
- dev_info (&hubstate->intf->dev,\r
- "new USB device on port %d, assigned address %d\n",\r
- port + 1, dev->devnum);\r
-\r
- /* put the device in the global device tree. the hub port\r
- * is the "bus_id"; hubs show in hierarchy like bridges\r
- */\r
- dev->dev.parent = dev->parent->dev.parent->parent;\r
-\r
- /* Run it through the hoops (find a driver, etc) */\r
- if (!usb_new_device(dev, &hub->dev))\r
- goto done;\r
-\r
- /* Free the configuration if there was an error */\r
- usb_put_dev(dev);\r
-\r
- /* Switch to a long reset time */\r
- delay = HUB_LONG_RESET_TIME;\r
- }\r
-\r
- hub->children[port] = NULL;\r
- hub_port_disable(hub, port);\r
-done:\r
- up(&usb_address0_sem);\r
-}\r
-\r
-static void hub_events(void)\r
-{\r
- unsigned long flags;\r
- struct list_head *tmp;\r
- struct usb_device *dev;\r
- struct usb_hub *hub;\r
- u16 hubstatus;\r
- u16 hubchange;\r
- u16 portstatus;\r
- u16 portchange;\r
- int i, ret;\r
- int m=0;\r
- /*\r
- * We restart the list every time to avoid a deadlock with\r
- * deleting hubs downstream from this one. This should be\r
- * safe since we delete the hub from the event list.\r
- * Not the most efficient, but avoids deadlocks.\r
- */\r
-\r
- while (m<5) {\r
- m++;\r
- spin_lock_irqsave(&hub_event_lock, flags);\r
-\r
- if (list_empty(&hub_event_list))\r
- break;\r
-\r
- /* Grab the next entry from the beginning of the list */\r
- tmp = hub_event_list.next;\r
-\r
- hub = list_entry(tmp, struct usb_hub, event_list);\r
- dev = interface_to_usbdev(hub->intf);\r
-\r
- list_del_init(tmp);\r
-\r
- if (unlikely(down_trylock(&hub->khubd_sem)))\r
- BUG(); /* never blocks, we were on list */\r
-\r
- spin_unlock_irqrestore(&hub_event_lock, flags);\r
-\r
- if (hub->error) {\r
- dev_dbg (&hub->intf->dev, "resetting for error %d\n",\r
- hub->error);\r
-\r
- if (hub_reset(hub)) {\r
- dev_dbg (&hub->intf->dev,\r
- "can't reset; disconnecting\n");\r
- up(&hub->khubd_sem);\r
- hub_start_disconnect(dev);\r
- continue;\r
- }\r
-\r
- hub->nerrors = 0;\r
- hub->error = 0;\r
- }\r
-\r
- for (i = 0; i < hub->descriptor->bNbrPorts; i++) {\r
- ret = hub_port_status(dev, i, &portstatus, &portchange);\r
- if (ret < 0) {\r
- continue;\r
- }\r
-\r
- if (portchange & USB_PORT_STAT_C_CONNECTION) {\r
- hub_port_connect_change(hub, i, portstatus, portchange);\r
- } else if (portchange & USB_PORT_STAT_C_ENABLE) {\r
- dev_dbg (hubdev (dev),\r
- "port %d enable change, status %x\n",\r
- i + 1, portstatus);\r
- clear_port_feature(dev,\r
- i + 1, USB_PORT_FEAT_C_ENABLE);\r
-\r
- /*\r
- * EM interference sometimes causes badly\r
- * shielded USB devices to be shutdown by\r
- * the hub, this hack enables them again.\r
- * Works at least with mouse driver. \r
- */\r
- if (!(portstatus & USB_PORT_STAT_ENABLE)\r
- && (portstatus & USB_PORT_STAT_CONNECTION)\r
- && (dev->children[i])) {\r
- dev_err (&hub->intf->dev,\r
- "port %i "\r
- "disabled by hub (EMI?), "\r
- "re-enabling...",\r
- i + 1);\r
- hub_port_connect_change(hub,\r
- i, portstatus, portchange);\r
- }\r
- }\r
-\r
- if (portchange & USB_PORT_STAT_C_SUSPEND) {\r
- dev_dbg (&hub->intf->dev,\r
- "suspend change on port %d\n",\r
- i + 1);\r
- clear_port_feature(dev,\r
- i + 1, USB_PORT_FEAT_C_SUSPEND);\r
- }\r
- \r
- if (portchange & USB_PORT_STAT_C_OVERCURRENT) {\r
- dev_err (&hub->intf->dev,\r
- "over-current change on port %d\n",\r
- i + 1);\r
- clear_port_feature(dev,\r
- i + 1, USB_PORT_FEAT_C_OVER_CURRENT);\r
- hub_power_on(hub);\r
- }\r
-\r
- if (portchange & USB_PORT_STAT_C_RESET) {\r
- dev_dbg (&hub->intf->dev,\r
- "reset change on port %d\n",\r
- i + 1);\r
- clear_port_feature(dev,\r
- i + 1, USB_PORT_FEAT_C_RESET);\r
- }\r
- } /* end for i */\r
-\r
- /* deal with hub status changes */\r
- if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)\r
- dev_err (&hub->intf->dev, "get_hub_status failed\n");\r
- else {\r
- if (hubchange & HUB_CHANGE_LOCAL_POWER) {\r
- dev_dbg (&hub->intf->dev, "power change\n");\r
- clear_hub_feature(dev, C_HUB_LOCAL_POWER);\r
- }\r
- if (hubchange & HUB_CHANGE_OVERCURRENT) {\r
- dev_dbg (&hub->intf->dev, "overcurrent change\n");\r
- wait_ms(500); /* Cool down */\r
- clear_hub_feature(dev, C_HUB_OVER_CURRENT);\r
- hub_power_on(hub);\r
- }\r
- }\r
- up(&hub->khubd_sem);\r
- } /* end while (1) */\r
-\r
- spin_unlock_irqrestore(&hub_event_lock, flags);\r
-}\r
-\r
-static int hub_thread(void *__hub)\r
-{\r
- /*\r
- * This thread doesn't need any user-level access,\r
- * so get rid of all our resources\r
- */\r
-\r
- daemonize("khubd");\r
- allow_signal(SIGKILL);\r
- /* Send me a signal to get me die (for debugging) */\r
- do {\r
- \r
- hub_events();\r
-\r
- //FIXME: Correct this\r
- //wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); // interruptable_sleep_on analog - below\r
- while (!list_empty(&hub_event_list)) {\r
- interruptible_sleep_on(&khubd_wait);\r
- }\r
-\r
- if (current->flags & PF_FREEZE)\r
- refrigerator(PF_IOTHREAD);\r
-\r
- } while (!signal_pending(current));\r
-\r
-// dbg("hub_thread exiting");\r
- complete_and_exit(&khubd_exited, 0);\r
-}\r
-\r
-static struct usb_device_id hub_id_table [] = {\r
- { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,\r
- .bDeviceClass = USB_CLASS_HUB},\r
- { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,\r
- .bInterfaceClass = USB_CLASS_HUB},\r
- { } /* Terminating entry */\r
-};\r
-\r
-MODULE_DEVICE_TABLE (usb, hub_id_table);\r
-\r
-static struct usb_driver hub_driver = {\r
- .owner = THIS_MODULE,\r
- .name = "hub",\r
- .probe = hub_probe,\r
- .disconnect = hub_disconnect,\r
- .ioctl = hub_ioctl,\r
- .id_table = hub_id_table,\r
-};\r
-\r
-/*\r
- * This should be a separate module.\r
- */\r
-int usb_hub_init(void)\r
-{\r
- pid_t pid;\r
-\r
- // ReactOS-specific\r
- // Create Event object, initialize other sync events\r
- KeInitializeEvent(&khubd_wait, NotificationEvent, TRUE); // signalled state\r
-\r
- if (usb_register(&hub_driver) < 0) {\r
- err("Unable to register USB hub driver");\r
- return -1;\r
- }\r
-\r
- pid = kernel_thread(hub_thread, NULL,\r
- CLONE_FS | CLONE_FILES | CLONE_SIGHAND);\r
- if (pid >= 0) {\r
- khubd_pid = pid;\r
- return 0;\r
- }\r
-\r
- /* Fall through if kernel_thread failed */\r
- usb_deregister(&hub_driver);\r
- err("failed to start hub_thread");\r
-\r
- return -1;\r
-}\r
-\r
-void usb_hub_cleanup(void)\r
-{\r
- int ret;\r
-\r
- /* Kill the thread */\r
- ret = kill_proc(khubd_pid, SIGKILL, 1);\r
-\r
- wait_for_completion(&khubd_exited);\r
-\r
- /*\r
- * Hub resources are freed for us by usb_deregister. It calls\r
- * usb_driver_purge on every device which in turn calls that\r
- * devices disconnect function if it is using this driver.\r
- * The hub_disconnect function takes care of releasing the\r
- * individual hub resources. -greg\r
- */\r
- usb_deregister(&hub_driver);\r
-} /* usb_hub_cleanup() */\r
-\r
-/*\r
- * WARNING - If a driver calls usb_reset_device, you should simulate a\r
- * disconnect() and probe() for other interfaces you doesn't claim. This\r
- * is left up to the driver writer right now. This insures other drivers\r
- * have a chance to re-setup their interface.\r
- *\r
- * Take a look at proc_resetdevice in devio.c for some sample code to\r
- * do this.\r
- * Use this only from within your probe function, otherwise use\r
- * usb_reset_device() below, which ensure proper locking\r
- */\r
-int usb_physical_reset_device(struct usb_device *dev)\r
-{\r
- struct usb_device *parent = dev->parent;\r
- struct usb_device_descriptor *descriptor;\r
- int i, ret, port = -1;\r
-\r
- if (!parent) {\r
- err("attempting to reset root hub!");\r
- return -EINVAL;\r
- }\r
-\r
- for (i = 0; i < parent->maxchild; i++)\r
- if (parent->children[i] == dev) {\r
- port = i;\r
- break;\r
- }\r
-\r
- if (port < 0)\r
- return -ENOENT;\r
-\r
- descriptor = kmalloc(sizeof *descriptor, GFP_NOIO);\r
- if (!descriptor) {\r
- return -ENOMEM;\r
- }\r
-\r
- down(&usb_address0_sem);\r
-\r
- /* Send a reset to the device */\r
- if (hub_port_reset(parent, port, dev, HUB_SHORT_RESET_TIME)) {\r
- hub_port_disable(parent, port);\r
- up(&usb_address0_sem);\r
- kfree(descriptor);\r
- return(-ENODEV);\r
- }\r
-\r
- /* Reprogram the Address */\r
- ret = usb_set_address(dev);\r
- if (ret < 0) {\r
- err("USB device not accepting new address (error=%d)", ret);\r
- hub_port_disable(parent, port);\r
- up(&usb_address0_sem);\r
- kfree(descriptor);\r
- return ret;\r
- }\r
-\r
- /* Let the SET_ADDRESS settle */\r
- wait_ms(10);\r
-\r
- up(&usb_address0_sem);\r
-\r
- /*\r
- * Now we fetch the configuration descriptors for the device and\r
- * see if anything has changed. If it has, we dump the current\r
- * parsed descriptors and reparse from scratch. Then we leave\r
- * the device alone for the caller to finish setting up.\r
- *\r
- * If nothing changed, we reprogram the configuration and then\r
- * the alternate settings.\r
- */\r
-\r
- ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,\r
- sizeof(*descriptor));\r
- if (ret < 0) {\r
- kfree(descriptor);\r
- return ret;\r
- }\r
-\r
- le16_to_cpus(&descriptor->bcdUSB);\r
- le16_to_cpus(&descriptor->idVendor);\r
- le16_to_cpus(&descriptor->idProduct);\r
- le16_to_cpus(&descriptor->bcdDevice);\r
-\r
- if (RtlCompareMemory(&dev->descriptor, descriptor, sizeof(*descriptor))) {\r
- kfree(descriptor);\r
- usb_destroy_configuration(dev);\r
-\r
- ret = usb_get_device_descriptor(dev);\r
- if (ret < sizeof(dev->descriptor)) {\r
- if (ret < 0)\r
- err("unable to get device %s descriptor "\r
- "(error=%d)", dev->devpath, ret);\r
- else\r
- err("USB device %s descriptor short read "\r
- "(expected %Zi, got %i)",\r
- dev->devpath,\r
- sizeof(dev->descriptor), ret);\r
-\r
- clear_bit(dev->devnum, dev->bus->devmap.devicemap);\r
- dev->devnum = -1;\r
- return -EIO;\r
- }\r
-\r
- ret = usb_get_configuration(dev);\r
- if (ret < 0) {\r
- err("unable to get configuration (error=%d)", ret);\r
- usb_destroy_configuration(dev);\r
- clear_bit(dev->devnum, dev->bus->devmap.devicemap);\r
- dev->devnum = -1;\r
- return 1;\r
- }\r
-\r
- dev->actconfig = dev->config;\r
- usb_set_maxpacket(dev);\r
-\r
- return 1;\r
- }\r
-\r
- kfree(descriptor);\r
-\r
- ret = usb_set_configuration(dev, dev->actconfig->desc.bConfigurationValue);\r
- if (ret < 0) {\r
- err("failed to set dev %s active configuration (error=%d)",\r
- dev->devpath, ret);\r
- return ret;\r
- }\r
-\r
- for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {\r
- struct usb_interface *intf = &dev->actconfig->interface[i];\r
- struct usb_interface_descriptor *as;\r
-\r
- as = &intf->altsetting[intf->act_altsetting].desc;\r
- ret = usb_set_interface(dev, as->bInterfaceNumber,\r
- as->bAlternateSetting);\r
- if (ret < 0) {\r
- err("failed to set active alternate setting "\r
- "for dev %s interface %d (error=%d)",\r
- dev->devpath, i, ret);\r
- return ret;\r
- }\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-int usb_reset_device(struct usb_device *udev)\r
-{\r
- //struct device *gdev = &udev->dev;\r
- int r;\r
- \r
- down_read(&gdev->bus->subsys.rwsem);\r
- r = usb_physical_reset_device(udev);\r
- up_read(&gdev->bus->subsys.rwsem);\r
-\r
- return r;\r
-}\r
-\r
-\r
+/*
+ * USB hub driver.
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Gregory P. Smith
+ * (C) Copyright 2001 Brad Hards (bhards@bigpond.net.au)
+ *
+ */
+#define DEBUG
+#if 0
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/ioctl.h>
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+#include <linux/usbdevice_fs.h>
+#include <linux/suspend.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include "hcd.h"
+#include "hub.h"
+#else
+#include "../usb_wrapper.h"
+#include "hcd.h"
+#include "hub.h"
+#define DEBUG
+#endif
+
+/* Wakes up khubd */
+static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_MUTEX(usb_address0_sem);
+
+static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
+static LIST_HEAD(hub_list); /* List of all hubs (for cleanup) */
+
+static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
+static pid_t khubd_pid = 0; /* PID of khubd */
+static DECLARE_COMPLETION(khubd_exited);
+
+#ifdef DEBUG
+static inline char *portspeed (int portstatus)
+{
+ if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED))
+ return "480 Mb/s";
+ else if (portstatus & (1 << USB_PORT_FEAT_LOWSPEED))
+ return "1.5 Mb/s";
+ else
+ return "12 Mb/s";
+}
+#endif
+
+/* for dev_info, dev_dbg, etc */
+static inline struct device *hubdev (struct usb_device *dev)
+{
+ return &dev->actconfig->interface [0].dev;
+}
+
+/* USB 2.0 spec Section 11.24.4.5 */
+static int get_hub_descriptor(struct usb_device *dev, void *data, int size)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
+ USB_DT_HUB << 8, 0, data, size, HZ * USB_CTRL_GET_TIMEOUT);
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.1
+ */
+static int clear_hub_feature(struct usb_device *dev, int feature)
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, HZ);
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.2
+ * BUG: doesn't handle port indicator selector in high byte of wIndex
+ */
+static int clear_port_feature(struct usb_device *dev, int port, int feature)
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.13
+ * BUG: doesn't handle port indicator selector in high byte of wIndex
+ */
+static int set_port_feature(struct usb_device *dev, int port, int feature)
+{
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ);
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.6
+ */
+static int get_hub_status(struct usb_device *dev,
+ struct usb_hub_status *data)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
+ data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);
+}
+
+/*
+ * USB 2.0 spec Section 11.24.2.7
+ */
+static int get_port_status(struct usb_device *dev, int port,
+ struct usb_port_status *data)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port,
+ data, sizeof(*data), HZ * USB_CTRL_GET_TIMEOUT);
+}
+
+/* completion function, fires on port status changes and various faults */
+static void hub_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_hub *hub = (struct usb_hub *)urb->context;
+ unsigned long flags;
+ int status;
+
+ switch (urb->status) {
+ case -ENOENT: /* synchronous unlink */
+ case -ECONNRESET: /* async unlink */
+ case -ESHUTDOWN: /* hardware going away */
+ return;
+
+ default: /* presumably an error */
+ /* Cause a hub reset after 10 consecutive errors */
+ dev_dbg (&hub->intf->dev, "transfer --> %d\n", urb->status);
+ if ((++hub->nerrors < 10) || hub->error)
+ goto resubmit;
+ hub->error = urb->status;
+ /* FALL THROUGH */
+
+ /* let khubd handle things */
+ case 0: /* we got data: port status changed */
+ break;
+ }
+
+ hub->nerrors = 0;
+
+ /* Something happened, let khubd figure it out */
+ spin_lock_irqsave(&hub_event_lock, flags);
+ if (list_empty(&hub->event_list)) {
+ list_add(&hub->event_list, &hub_event_list);
+ wake_up(&khubd_wait);
+ }
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+
+resubmit:
+ if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
+ /* ENODEV means we raced disconnect() */
+ && status != -ENODEV)
+ dev_err (&hub->intf->dev, "resubmit --> %d\n", urb->status);
+}
+
+/* USB 2.0 spec Section 11.24.2.3 */
+static inline int
+hub_clear_tt_buffer (struct usb_device *hub, u16 devinfo, u16 tt)
+{
+ return usb_control_msg (hub, usb_rcvctrlpipe (hub, 0),
+ HUB_CLEAR_TT_BUFFER, USB_DIR_IN | USB_RECIP_OTHER,
+ devinfo, tt, 0, 0, HZ);
+}
+
+/*
+ * enumeration blocks khubd for a long time. we use keventd instead, since
+ * long blocking there is the exception, not the rule. accordingly, HCDs
+ * talking to TTs must queue control transfers (not just bulk and iso), so
+ * both can talk to the same hub concurrently.
+ */
+static void hub_tt_kevent (void *arg)
+{
+ struct usb_hub *hub = arg;
+ unsigned long flags;
+
+ spin_lock_irqsave (&hub->tt.lock, flags);
+ while (!list_empty (&hub->tt.clear_list)) {
+ struct list_head *temp;
+ struct usb_tt_clear *clear;
+ struct usb_device *dev;
+ int status;
+
+ temp = hub->tt.clear_list.next;
+ clear = list_entry (temp, struct usb_tt_clear, clear_list);
+ list_del (&clear->clear_list);
+
+ /* drop lock so HCD can concurrently report other TT errors */
+ spin_unlock_irqrestore (&hub->tt.lock, flags);
+ dev = interface_to_usbdev (hub->intf);
+ status = hub_clear_tt_buffer (dev, clear->devinfo, clear->tt);
+ spin_lock_irqsave (&hub->tt.lock, flags);
+
+ if (status)
+ err ("usb-%s-%s clear tt %d (%04x) error %d",
+ dev->bus->bus_name, dev->devpath,
+ clear->tt, clear->devinfo, status);
+ kfree (clear);
+ }
+ spin_unlock_irqrestore (&hub->tt.lock, flags);
+}
+
+/**
+ * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub
+ * @dev: the device whose split transaction failed
+ * @pipe: identifies the endpoint of the failed transaction
+ *
+ * High speed HCDs use this to tell the hub driver that some split control or
+ * bulk transaction failed in a way that requires clearing internal state of
+ * a transaction translator. This is normally detected (and reported) from
+ * interrupt context.
+ *
+ * It may not be possible for that hub to handle additional full (or low)
+ * speed transactions until that state is fully cleared out.
+ */
+void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe)
+{
+ struct usb_tt *tt = dev->tt;
+ unsigned long flags;
+ struct usb_tt_clear *clear;
+
+ /* we've got to cope with an arbitrary number of pending TT clears,
+ * since each TT has "at least two" buffers that can need it (and
+ * there can be many TTs per hub). even if they're uncommon.
+ */
+ if ((clear = kmalloc (sizeof *clear, SLAB_ATOMIC)) == 0) {
+ err ("can't save CLEAR_TT_BUFFER state for hub at usb-%s-%s",
+ dev->bus->bus_name, tt->hub->devpath);
+ /* FIXME recover somehow ... RESET_TT? */
+ return;
+ }
+
+ /* info that CLEAR_TT_BUFFER needs */
+ clear->tt = tt->multi ? dev->ttport : 1;
+ clear->devinfo = usb_pipeendpoint (pipe);
+ clear->devinfo |= dev->devnum << 4;
+ clear->devinfo |= usb_pipecontrol (pipe)
+ ? (USB_ENDPOINT_XFER_CONTROL << 11)
+ : (USB_ENDPOINT_XFER_BULK << 11);
+ if (usb_pipein (pipe))
+ clear->devinfo |= 1 << 15;
+
+ /* tell keventd to clear state for this TT */
+ spin_lock_irqsave (&tt->lock, flags);
+ list_add_tail (&clear->clear_list, &tt->clear_list);
+ schedule_work (&tt->kevent);
+ spin_unlock_irqrestore (&tt->lock, flags);
+}
+
+static void hub_power_on(struct usb_hub *hub)
+{
+ struct usb_device *dev;
+ int i;
+
+ /* Enable power to the ports */
+ dev_dbg(hubdev(interface_to_usbdev(hub->intf)),
+ "enabling power on all ports\n");
+ dev = interface_to_usbdev(hub->intf);
+
+ for (i = 0; i < hub->descriptor->bNbrPorts; i++)
+ set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER);
+
+ /* Wait for power to be enabled */
+ wait_ms(hub->descriptor->bPwrOn2PwrGood * 2);
+}
+
+static int hub_hub_status(struct usb_hub *hub,
+ u16 *status, u16 *change)
+{
+ struct usb_device *dev = interface_to_usbdev (hub->intf);
+ int ret;
+
+ ret = get_hub_status(dev, &hub->status->hub);
+ if (ret < 0)
+ dev_err (hubdev (dev),
+ "%s failed (err = %d)\n", __FUNCTION__, ret);
+ else {
+ *status = le16_to_cpu(hub->status->hub.wHubStatus);
+ *change = le16_to_cpu(hub->status->hub.wHubChange);
+ ret = 0;
+ }
+ return ret;
+}
+
+static int hub_configure(struct usb_hub *hub,
+ struct usb_endpoint_descriptor *endpoint)
+{
+ struct usb_device *dev = interface_to_usbdev (hub->intf);
+ struct device *hub_dev;
+ u16 hubstatus, hubchange;
+ unsigned int pipe;
+ int maxp, ret;
+ char *message;
+
+ hub->buffer = usb_buffer_alloc(dev, sizeof(*hub->buffer), GFP_KERNEL,
+ &hub->buffer_dma);
+ if (!hub->buffer) {
+ message = "can't allocate hub irq buffer";
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
+ if (!hub->status) {
+ message = "can't kmalloc hub status buffer";
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
+ if (!hub->descriptor) {
+ message = "can't kmalloc hub descriptor";
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /* Request the entire hub descriptor.
+ * hub->descriptor can handle USB_MAXCHILDREN ports,
+ * but the hub can/will return fewer bytes here.
+ */
+ ret = get_hub_descriptor(dev, hub->descriptor,
+ sizeof(*hub->descriptor));
+ if (ret < 0) {
+ message = "can't read hub descriptor";
+ goto fail;
+ } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
+ message = "hub has too many ports!";
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ hub_dev = hubdev(dev);
+ dev->maxchild = hub->descriptor->bNbrPorts;
+ dev_info (hub_dev, "%d port%s detected\n", dev->maxchild,
+ (dev->maxchild == 1) ? "" : "s");
+
+ le16_to_cpus(&hub->descriptor->wHubCharacteristics);
+
+ if (hub->descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) {
+ int i;
+ char portstr [USB_MAXCHILDREN + 1];
+
+ for (i = 0; i < dev->maxchild; i++)
+ portstr[i] = hub->descriptor->DeviceRemovable
+ [((i + 1) / 8)] & (1 << ((i + 1) % 8))
+ ? 'F' : 'R';
+ portstr[dev->maxchild] = 0;
+ dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
+ } else
+ dev_dbg(hub_dev, "standalone hub\n");
+
+ switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_LPSM) {
+ case 0x00:
+ dev_dbg(hub_dev, "ganged power switching\n");
+ break;
+ case 0x01:
+ dev_dbg(hub_dev, "individual port power switching\n");
+ break;
+ case 0x02:
+ case 0x03:
+ dev_dbg(hub_dev, "unknown reserved power switching mode\n");
+ break;
+ }
+
+ switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_OCPM) {
+ case 0x00:
+ dev_dbg(hub_dev, "global over-current protection\n");
+ break;
+ case 0x08:
+ dev_dbg(hub_dev, "individual port over-current protection\n");
+ break;
+ case 0x10:
+ case 0x18:
+ dev_dbg(hub_dev, "no over-current protection\n");
+ break;
+ }
+
+ spin_lock_init (&hub->tt.lock);
+ INIT_LIST_HEAD (&hub->tt.clear_list);
+ INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub);
+ switch (dev->descriptor.bDeviceProtocol) {
+ case 0:
+ break;
+ case 1:
+ dev_dbg(hub_dev, "Single TT\n");
+ hub->tt.hub = dev;
+ break;
+ case 2:
+ dev_dbg(hub_dev, "TT per port\n");
+ hub->tt.hub = dev;
+ hub->tt.multi = 1;
+ break;
+ default:
+ dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
+ dev->descriptor.bDeviceProtocol);
+ break;
+ }
+
+ switch (hub->descriptor->wHubCharacteristics & HUB_CHAR_TTTT) {
+ case 0x00:
+ if (dev->descriptor.bDeviceProtocol != 0)
+ dev_dbg(hub_dev, "TT requires at most 8 FS bit times\n");
+ break;
+ case 0x20:
+ dev_dbg(hub_dev, "TT requires at most 16 FS bit times\n");
+ break;
+ case 0x40:
+ dev_dbg(hub_dev, "TT requires at most 24 FS bit times\n");
+ break;
+ case 0x60:
+ dev_dbg(hub_dev, "TT requires at most 32 FS bit times\n");
+ break;
+ }
+
+ dev_dbg(hub_dev, "Port indicators are %s supported\n",
+ (hub->descriptor->wHubCharacteristics & HUB_CHAR_PORTIND)
+ ? "" : "not");
+
+ dev_dbg(hub_dev, "power on to power good time: %dms\n",
+ hub->descriptor->bPwrOn2PwrGood * 2);
+ dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
+ hub->descriptor->bHubContrCurrent);
+
+ ret = hub_hub_status(hub, &hubstatus, &hubchange);
+ if (ret < 0) {
+ message = "can't get hub status";
+ goto fail;
+ }
+
+ dev_dbg(hub_dev, "local power source is %s\n",
+ (hubstatus & HUB_STATUS_LOCAL_POWER)
+ ? "lost (inactive)" : "good");
+
+ dev_dbg(hub_dev, "%sover-current condition exists\n",
+ (hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
+
+ /* Start the interrupt endpoint */
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ if (maxp > sizeof(*hub->buffer))
+ maxp = sizeof(*hub->buffer);
+
+ hub->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!hub->urb) {
+ message = "couldn't allocate interrupt urb";
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ usb_fill_int_urb(hub->urb, dev, pipe, *hub->buffer, maxp, hub_irq,
+ hub, endpoint->bInterval);
+ hub->urb->transfer_dma = hub->buffer_dma;
+ hub->urb->transfer_flags |= URB_NO_DMA_MAP;
+ ret = usb_submit_urb(hub->urb, GFP_KERNEL);
+ if (ret) {
+ message = "couldn't submit status urb";
+ goto fail;
+ }
+
+ /* Wake up khubd */
+ wake_up(&khubd_wait);
+
+ hub_power_on(hub);
+
+ return 0;
+
+fail:
+ dev_err (&hub->intf->dev, "config failed, %s (err %d)\n",
+ message, ret);
+ /* hub_disconnect() frees urb and descriptor */
+ return ret;
+}
+
+static void hub_disconnect(struct usb_interface *intf)
+{
+ struct usb_hub *hub = usb_get_intfdata (intf);
+ unsigned long flags;
+
+ if (!hub)
+ return;
+
+ usb_set_intfdata (intf, NULL);
+ spin_lock_irqsave(&hub_event_lock, flags);
+
+ /* Delete it and then reset it */
+ list_del(&hub->event_list);
+ INIT_LIST_HEAD(&hub->event_list);
+ list_del(&hub->hub_list);
+ INIT_LIST_HEAD(&hub->hub_list);
+
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+
+ down(&hub->khubd_sem); /* Wait for khubd to leave this hub alone. */
+ up(&hub->khubd_sem);
+
+ /* assuming we used keventd, it must quiesce too */
+ if (hub->tt.hub)
+ flush_scheduled_work ();
+
+ if (hub->urb) {
+ usb_unlink_urb(hub->urb);
+ usb_free_urb(hub->urb);
+ hub->urb = NULL;
+ }
+
+ if (hub->descriptor) {
+ kfree(hub->descriptor);
+ hub->descriptor = NULL;
+ }
+
+ if (hub->status) {
+ kfree(hub->status);
+ hub->status = NULL;
+ }
+
+ if (hub->buffer) {
+ usb_buffer_free(interface_to_usbdev(intf),
+ sizeof(*hub->buffer), hub->buffer,
+ hub->buffer_dma);
+ hub->buffer = NULL;
+ }
+
+ /* Free the memory */
+ kfree(hub);
+}
+
+static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_host_interface *desc;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_device *dev;
+ struct usb_hub *hub;
+ unsigned long flags;
+
+ desc = intf->altsetting + intf->act_altsetting;
+ dev = interface_to_usbdev(intf);
+
+ /* Some hubs have a subclass of 1, which AFAICT according to the */
+ /* specs is not defined, but it works */
+ if ((desc->desc.bInterfaceSubClass != 0) &&
+ (desc->desc.bInterfaceSubClass != 1)) {
+descriptor_error:
+ dev_err (&intf->dev, "bad descriptor, ignoring hub\n");
+ return -EIO;
+ }
+
+ /* Multiple endpoints? What kind of mutant ninja-hub is this? */
+ if (desc->desc.bNumEndpoints != 1) {
+ goto descriptor_error;
+ }
+
+ endpoint = &desc->endpoint[0].desc;
+
+ /* Output endpoint? Curiouser and curiouser.. */
+ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) {
+ goto descriptor_error;
+ }
+
+ /* If it's not an interrupt endpoint, we'd better punt! */
+ if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ != USB_ENDPOINT_XFER_INT) {
+ goto descriptor_error;
+ return -EIO;
+ }
+
+ /* We found a hub */
+ dev_info (hubdev (dev), "USB hub found\n");
+
+ hub = kmalloc(sizeof(*hub), GFP_KERNEL);
+ if (!hub) {
+ err("couldn't kmalloc hub struct");
+ return -ENOMEM;
+ }
+
+ memset(hub, 0, sizeof(*hub));
+
+ INIT_LIST_HEAD(&hub->event_list);
+ hub->intf = intf;
+ init_MUTEX(&hub->khubd_sem);
+
+ /* Record the new hub's existence */
+ spin_lock_irqsave(&hub_event_lock, flags);
+ INIT_LIST_HEAD(&hub->hub_list);
+ list_add(&hub->hub_list, &hub_list);
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+
+ usb_set_intfdata (intf, hub);
+
+ if (hub_configure(hub, endpoint) >= 0) {
+ strcpy (intf->dev.name, "Hub");
+ return 0;
+ }
+
+ hub_disconnect (intf);
+ return -ENODEV;
+}
+
+static int
+hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
+{
+ struct usb_device *hub = interface_to_usbdev (intf);
+
+ /* assert ifno == 0 (part of hub spec) */
+ switch (code) {
+ case USBDEVFS_HUB_PORTINFO: {
+ struct usbdevfs_hub_portinfo *info = user_data;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&hub_event_lock, flags);
+ if (hub->devnum <= 0)
+ info->nports = 0;
+ else {
+ info->nports = hub->maxchild;
+ for (i = 0; i < info->nports; i++) {
+ if (hub->children[i] == NULL)
+ info->port[i] = 0;
+ else
+ info->port[i] =
+ hub->children[i]->devnum;
+ }
+ }
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+
+ return info->nports + 1;
+ }
+
+ default:
+ return -ENOSYS;
+ }
+}
+
+static int hub_reset(struct usb_hub *hub)
+{
+ struct usb_device *dev = interface_to_usbdev(hub->intf);
+ int i;
+
+ /* Disconnect any attached devices */
+ for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
+ if (dev->children[i])
+ usb_disconnect(&dev->children[i]);
+ }
+
+ /* Attempt to reset the hub */
+ if (hub->urb)
+ usb_unlink_urb(hub->urb);
+ else
+ return -1;
+
+ if (usb_reset_device(dev))
+ return -1;
+
+ hub->urb->dev = dev;
+ if (usb_submit_urb(hub->urb, GFP_KERNEL))
+ return -1;
+
+ hub_power_on(hub);
+
+ return 0;
+}
+
+static void hub_start_disconnect(struct usb_device *dev)
+{
+ struct usb_device *parent = dev->parent;
+ int i;
+
+ /* Find the device pointer to disconnect */
+ if (parent) {
+ for (i = 0; i < parent->maxchild; i++) {
+ if (parent->children[i] == dev) {
+ usb_disconnect(&parent->children[i]);
+ return;
+ }
+ }
+ }
+
+ err("cannot disconnect hub %s", dev->devpath);
+}
+
+static int hub_port_status(struct usb_device *dev, int port,
+ u16 *status, u16 *change)
+{
+ struct usb_hub *hub = usb_get_intfdata (dev->actconfig->interface);
+ int ret;
+
+ ret = get_port_status(dev, port + 1, &hub->status->port);
+ if (ret < 0)
+ dev_err (hubdev (dev),
+ "%s failed (err = %d)\n", __FUNCTION__, ret);
+ else {
+ *status = le16_to_cpu(hub->status->port.wPortStatus);
+ *change = le16_to_cpu(hub->status->port.wPortChange);
+ ret = 0;
+ }
+ return ret;
+}
+
+#define HUB_RESET_TRIES 5
+#define HUB_PROBE_TRIES 2
+#define HUB_SHORT_RESET_TIME 10
+#define HUB_LONG_RESET_TIME 200
+#define HUB_RESET_TIMEOUT 500
+
+/* return: -1 on error, 0 on success, 1 on disconnect. */
+static int hub_port_wait_reset(struct usb_device *hub, int port,
+ struct usb_device *dev, unsigned int delay)
+{
+ int delay_time, ret;
+ u16 portstatus;
+ u16 portchange;
+
+ for (delay_time = 0;
+ delay_time < HUB_RESET_TIMEOUT;
+ delay_time += delay) {
+ /* wait to give the device a chance to reset */
+ wait_ms(delay);
+
+ /* read and decode port status */
+ ret = hub_port_status(hub, port, &portstatus, &portchange);
+ if (ret < 0) {
+ return -1;
+ }
+
+ /* Device went away? */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION))
+ return 1;
+
+ /* bomb out completely if something weird happened */
+ if ((portchange & USB_PORT_STAT_C_CONNECTION))
+ return -1;
+
+ /* if we`ve finished resetting, then break out of the loop */
+ if (!(portstatus & USB_PORT_STAT_RESET) &&
+ (portstatus & USB_PORT_STAT_ENABLE)) {
+ if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+ dev->speed = USB_SPEED_HIGH;
+ else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+ dev->speed = USB_SPEED_LOW;
+ else
+ dev->speed = USB_SPEED_FULL;
+ return 0;
+ }
+
+ /* switch to the long delay after two short delay failures */
+ if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
+ delay = HUB_LONG_RESET_TIME;
+
+ dev_dbg (hubdev (hub),
+ "port %d not reset yet, waiting %dms\n",
+ port + 1, delay);
+ }
+
+ return -1;
+}
+
+/* return: -1 on error, 0 on success, 1 on disconnect. */
+static int hub_port_reset(struct usb_device *hub, int port,
+ struct usb_device *dev, unsigned int delay)
+{
+ int i, status;
+
+ /* Reset the port */
+ for (i = 0; i < HUB_RESET_TRIES; i++) {
+ set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET);
+
+ /* return on disconnect or reset */
+ status = hub_port_wait_reset(hub, port, dev, delay);
+ if (status != -1) {
+ clear_port_feature(hub,
+ port + 1, USB_PORT_FEAT_C_RESET);
+ dev->state = status
+ ? USB_STATE_NOTATTACHED
+ : USB_STATE_DEFAULT;
+ return status;
+ }
+
+ dev_dbg (hubdev (hub),
+ "port %d not enabled, trying reset again...\n",
+ port + 1);
+ delay = HUB_LONG_RESET_TIME;
+ }
+
+ dev_err (hubdev (hub),
+ "Cannot enable port %i. Maybe the USB cable is bad?\n",
+ port + 1);
+
+ return -1;
+}
+
+int hub_port_disable(struct usb_device *hub, int port)
+{
+ int ret;
+
+ ret = clear_port_feature(hub, port + 1, USB_PORT_FEAT_ENABLE);
+ if (ret)
+ dev_err(hubdev(hub), "cannot disable port %d (err = %d)\n",
+ port + 1, ret);
+
+ return ret;
+}
+
+/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
+ *
+ * Between connect detection and reset signaling there must be a delay
+ * of 100ms at least for debounce and power-settling. The corresponding
+ * timer shall restart whenever the downstream port detects a disconnect.
+ *
+ * Apparently there are some bluetooth and irda-dongles and a number
+ * of low-speed devices which require longer delays of about 200-400ms.
+ * Not covered by the spec - but easy to deal with.
+ *
+ * This implementation uses 400ms minimum debounce timeout and checks
+ * every 25ms for transient disconnects to restart the delay.
+ */
+
+#define HUB_DEBOUNCE_TIMEOUT 400
+#define HUB_DEBOUNCE_STEP 25
+#define HUB_DEBOUNCE_STABLE 4
+
+/* return: -1 on error, 0 on success, 1 on disconnect. */
+static int hub_port_debounce(struct usb_device *hub, int port)
+{
+ int ret;
+ int delay_time, stable_count;
+ u16 portchange, portstatus;
+ unsigned connection;
+
+ connection = 0;
+ stable_count = 0;
+ for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; delay_time += HUB_DEBOUNCE_STEP) {
+ wait_ms(HUB_DEBOUNCE_STEP);
+
+ ret = hub_port_status(hub, port, &portstatus, &portchange);
+ if (ret < 0)
+ return -1;
+
+ if ((portstatus & USB_PORT_STAT_CONNECTION) == connection) {
+ if (connection) {
+ if (++stable_count == HUB_DEBOUNCE_STABLE)
+ break;
+ }
+ } else {
+ stable_count = 0;
+ }
+ connection = portstatus & USB_PORT_STAT_CONNECTION;
+
+ if ((portchange & USB_PORT_STAT_C_CONNECTION)) {
+ clear_port_feature(hub, port+1, USB_PORT_FEAT_C_CONNECTION);
+ }
+ }
+
+ /* XXX Replace this with dbg() when 2.6 is about to ship. */
+ dev_dbg (hubdev (hub),
+ "debounce: port %d: delay %dms stable %d status 0x%x\n",
+ port + 1, delay_time, stable_count, portstatus);
+
+ return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1;
+}
+
+static void hub_port_connect_change(struct usb_hub *hubstate, int port,
+ u16 portstatus, u16 portchange)
+{
+ struct usb_device *hub = interface_to_usbdev(hubstate->intf);
+ struct usb_device *dev;
+ unsigned int delay = HUB_SHORT_RESET_TIME;
+ int i;
+
+ dev_dbg (&hubstate->intf->dev,
+ "port %d, status %x, change %x, %s\n",
+ port + 1, portstatus, portchange, portspeed (portstatus));
+
+ /* Clear the connection change status */
+ clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION);
+
+ /* Disconnect any existing devices under this port */
+ if (hub->children[port])
+ usb_disconnect(&hub->children[port]);
+
+ /* Return now if nothing is connected */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+ if (portstatus & USB_PORT_STAT_ENABLE)
+ hub_port_disable(hub, port);
+
+ return;
+ }
+
+ if (hub_port_debounce(hub, port)) {
+ dev_err (&hubstate->intf->dev,
+ "connect-debounce failed, port %d disabled\n",
+ port+1);
+ hub_port_disable(hub, port);
+ return;
+ }
+
+ /* Some low speed devices have problems with the quick delay, so */
+ /* be a bit pessimistic with those devices. RHbug #23670 */
+ if (portstatus & USB_PORT_STAT_LOW_SPEED)
+ delay = HUB_LONG_RESET_TIME;
+
+ down(&usb_address0_sem);
+
+ for (i = 0; i < HUB_PROBE_TRIES; i++) {
+ struct usb_device *pdev;
+ int len;
+
+ /* Allocate a new device struct */
+ dev = usb_alloc_dev(hub, hub->bus);
+ if (!dev) {
+ dev_err (&hubstate->intf->dev,
+ "couldn't allocate usb_device\n");
+ break;
+ }
+
+ hub->children[port] = dev;
+ dev->state = USB_STATE_POWERED;
+
+ /* Reset the device, and detect its speed */
+ if (hub_port_reset(hub, port, dev, delay)) {
+ usb_put_dev(dev);
+ break;
+ }
+
+ /* Find a new address for it */
+ usb_connect(dev);
+
+ /* Set up TT records, if needed */
+ if (hub->tt) {
+ dev->tt = hub->tt;
+ dev->ttport = hub->ttport;
+ } else if (dev->speed != USB_SPEED_HIGH
+ && hub->speed == USB_SPEED_HIGH) {
+ dev->tt = &hubstate->tt;
+ dev->ttport = port + 1;
+ }
+
+ /* Save readable and stable topology id, distinguishing
+ * devices by location for diagnostics, tools, etc. The
+ * string is a path along hub ports, from the root. Each
+ * device's id will be stable until USB is re-cabled, and
+ * hubs are often labeled with these port numbers.
+ *
+ * Initial size: ".NN" times five hubs + NUL = 16 bytes max
+ * (quite rare, since most hubs have 4-6 ports).
+ */
+ pdev = dev->parent;
+ if (pdev->devpath [0] != '0') /* parent not root? */
+ len = snprintf (dev->devpath, sizeof dev->devpath,
+ "%s.%d", pdev->devpath, port + 1);
+ /* root == "0", root port 2 == "2", port 3 that hub "2.3" */
+ else
+ len = snprintf (dev->devpath, sizeof dev->devpath,
+ "%d", port + 1);
+ if (len == sizeof dev->devpath)
+ dev_err (&hubstate->intf->dev,
+ "devpath size! usb/%03d/%03d path %s\n",
+ dev->bus->busnum, dev->devnum, dev->devpath);
+ dev_info (&hubstate->intf->dev,
+ "new USB device on port %d, assigned address %d\n",
+ port + 1, dev->devnum);
+
+ /* put the device in the global device tree. the hub port
+ * is the "bus_id"; hubs show in hierarchy like bridges
+ */
+ dev->dev.parent = dev->parent->dev.parent->parent;
+
+ /* Run it through the hoops (find a driver, etc) */
+ if (!usb_new_device(dev, &hub->dev))
+ goto done;
+
+ /* Free the configuration if there was an error */
+ usb_put_dev(dev);
+
+ /* Switch to a long reset time */
+ delay = HUB_LONG_RESET_TIME;
+ }
+
+ hub->children[port] = NULL;
+ hub_port_disable(hub, port);
+done:
+ up(&usb_address0_sem);
+}
+
+static void hub_events(void)
+{
+ unsigned long flags;
+ struct list_head *tmp;
+ struct usb_device *dev;
+ struct usb_hub *hub;
+ u16 hubstatus;
+ u16 hubchange;
+ u16 portstatus;
+ u16 portchange;
+ int i, ret;
+ int m=0;
+ /*
+ * We restart the list every time to avoid a deadlock with
+ * deleting hubs downstream from this one. This should be
+ * safe since we delete the hub from the event list.
+ * Not the most efficient, but avoids deadlocks.
+ */
+
+ while (m<5) {
+ m++;
+ spin_lock_irqsave(&hub_event_lock, flags);
+
+ if (list_empty(&hub_event_list))
+ break;
+
+ /* Grab the next entry from the beginning of the list */
+ tmp = hub_event_list.next;
+
+ hub = list_entry(tmp, struct usb_hub, event_list);
+ dev = interface_to_usbdev(hub->intf);
+
+ list_del_init(tmp);
+
+ if (unlikely(down_trylock(&hub->khubd_sem)))
+ BUG(); /* never blocks, we were on list */
+
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+
+ if (hub->error) {
+ dev_dbg (&hub->intf->dev, "resetting for error %d\n",
+ hub->error);
+
+ if (hub_reset(hub)) {
+ dev_dbg (&hub->intf->dev,
+ "can't reset; disconnecting\n");
+ up(&hub->khubd_sem);
+ hub_start_disconnect(dev);
+ continue;
+ }
+
+ hub->nerrors = 0;
+ hub->error = 0;
+ }
+
+ for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
+ ret = hub_port_status(dev, i, &portstatus, &portchange);
+ if (ret < 0) {
+ continue;
+ }
+
+ if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ hub_port_connect_change(hub, i, portstatus, portchange);
+ } else if (portchange & USB_PORT_STAT_C_ENABLE) {
+ dev_dbg (hubdev (dev),
+ "port %d enable change, status %x\n",
+ i + 1, portstatus);
+ clear_port_feature(dev,
+ i + 1, USB_PORT_FEAT_C_ENABLE);
+
+ /*
+ * EM interference sometimes causes badly
+ * shielded USB devices to be shutdown by
+ * the hub, this hack enables them again.
+ * Works at least with mouse driver.
+ */
+ if (!(portstatus & USB_PORT_STAT_ENABLE)
+ && (portstatus & USB_PORT_STAT_CONNECTION)
+ && (dev->children[i])) {
+ dev_err (&hub->intf->dev,
+ "port %i "
+ "disabled by hub (EMI?), "
+ "re-enabling...",
+ i + 1);
+ hub_port_connect_change(hub,
+ i, portstatus, portchange);
+ }
+ }
+
+ if (portchange & USB_PORT_STAT_C_SUSPEND) {
+ dev_dbg (&hub->intf->dev,
+ "suspend change on port %d\n",
+ i + 1);
+ clear_port_feature(dev,
+ i + 1, USB_PORT_FEAT_C_SUSPEND);
+ }
+
+ if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
+ dev_err (&hub->intf->dev,
+ "over-current change on port %d\n",
+ i + 1);
+ clear_port_feature(dev,
+ i + 1, USB_PORT_FEAT_C_OVER_CURRENT);
+ hub_power_on(hub);
+ }
+
+ if (portchange & USB_PORT_STAT_C_RESET) {
+ dev_dbg (&hub->intf->dev,
+ "reset change on port %d\n",
+ i + 1);
+ clear_port_feature(dev,
+ i + 1, USB_PORT_FEAT_C_RESET);
+ }
+ } /* end for i */
+
+ /* deal with hub status changes */
+ if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
+ dev_err (&hub->intf->dev, "get_hub_status failed\n");
+ else {
+ if (hubchange & HUB_CHANGE_LOCAL_POWER) {
+ dev_dbg (&hub->intf->dev, "power change\n");
+ clear_hub_feature(dev, C_HUB_LOCAL_POWER);
+ }
+ if (hubchange & HUB_CHANGE_OVERCURRENT) {
+ dev_dbg (&hub->intf->dev, "overcurrent change\n");
+ wait_ms(500); /* Cool down */
+ clear_hub_feature(dev, C_HUB_OVER_CURRENT);
+ hub_power_on(hub);
+ }
+ }
+ up(&hub->khubd_sem);
+ } /* end while (1) */
+
+ spin_unlock_irqrestore(&hub_event_lock, flags);
+}
+
+static int hub_thread(void *__hub)
+{
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources
+ */
+
+ daemonize("khubd");
+ allow_signal(SIGKILL);
+ /* Send me a signal to get me die (for debugging) */
+ do {
+
+ hub_events();
+
+ //FIXME: Correct this
+ //wait_event_interruptible(khubd_wait, !list_empty(&hub_event_list)); // interruptable_sleep_on analog - below
+ while (!list_empty(&hub_event_list)) {
+ interruptible_sleep_on(&khubd_wait);
+ }
+
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_IOTHREAD);
+
+ } while (!signal_pending(current));
+
+// dbg("hub_thread exiting");
+ complete_and_exit(&khubd_exited, 0);
+}
+
+static struct usb_device_id hub_id_table [] = {
+ { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
+ .bDeviceClass = USB_CLASS_HUB},
+ { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
+ .bInterfaceClass = USB_CLASS_HUB},
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, hub_id_table);
+
+static struct usb_driver hub_driver = {
+ .owner = THIS_MODULE,
+ .name = "hub",
+ .probe = hub_probe,
+ .disconnect = hub_disconnect,
+ .ioctl = hub_ioctl,
+ .id_table = hub_id_table,
+};
+
+/*
+ * This should be a separate module.
+ */
+int usb_hub_init(void)
+{
+ pid_t pid;
+
+ // ReactOS-specific
+ // Create Event object, initialize other sync events
+ KeInitializeEvent(&khubd_wait, NotificationEvent, TRUE); // signalled state
+
+ if (usb_register(&hub_driver) < 0) {
+ err("Unable to register USB hub driver");
+ return -1;
+ }
+
+ pid = kernel_thread(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);
+ err("failed to start hub_thread");
+
+ return -1;
+}
+
+void usb_hub_cleanup(void)
+{
+ int ret;
+
+ /* Kill the thread */
+ ret = kill_proc(khubd_pid, SIGKILL, 1);
+
+ wait_for_completion(&khubd_exited);
+
+ /*
+ * Hub resources are freed for us by usb_deregister. It calls
+ * usb_driver_purge on every device which in turn calls that
+ * devices disconnect function if it is using this driver.
+ * The hub_disconnect function takes care of releasing the
+ * individual hub resources. -greg
+ */
+ usb_deregister(&hub_driver);
+} /* usb_hub_cleanup() */
+
+/*
+ * WARNING - If a driver calls usb_reset_device, you should simulate a
+ * disconnect() and probe() for other interfaces you doesn't claim. This
+ * is left up to the driver writer right now. This insures other drivers
+ * have a chance to re-setup their interface.
+ *
+ * Take a look at proc_resetdevice in devio.c for some sample code to
+ * do this.
+ * Use this only from within your probe function, otherwise use
+ * usb_reset_device() below, which ensure proper locking
+ */
+int usb_physical_reset_device(struct usb_device *dev)
+{
+ struct usb_device *parent = dev->parent;
+ struct usb_device_descriptor *descriptor;
+ int i, ret, port = -1;
+
+ if (!parent) {
+ err("attempting to reset root hub!");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < parent->maxchild; i++)
+ if (parent->children[i] == dev) {
+ port = i;
+ break;
+ }
+
+ if (port < 0)
+ return -ENOENT;
+
+ descriptor = kmalloc(sizeof *descriptor, GFP_NOIO);
+ if (!descriptor) {
+ return -ENOMEM;
+ }
+
+ down(&usb_address0_sem);
+
+ /* Send a reset to the device */
+ if (hub_port_reset(parent, port, dev, HUB_SHORT_RESET_TIME)) {
+ hub_port_disable(parent, port);
+ up(&usb_address0_sem);
+ kfree(descriptor);
+ return(-ENODEV);
+ }
+
+ /* Reprogram the Address */
+ ret = usb_set_address(dev);
+ if (ret < 0) {
+ err("USB device not accepting new address (error=%d)", ret);
+ hub_port_disable(parent, port);
+ up(&usb_address0_sem);
+ kfree(descriptor);
+ return ret;
+ }
+
+ /* Let the SET_ADDRESS settle */
+ wait_ms(10);
+
+ up(&usb_address0_sem);
+
+ /*
+ * Now we fetch the configuration descriptors for the device and
+ * see if anything has changed. If it has, we dump the current
+ * parsed descriptors and reparse from scratch. Then we leave
+ * the device alone for the caller to finish setting up.
+ *
+ * If nothing changed, we reprogram the configuration and then
+ * the alternate settings.
+ */
+
+ ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, descriptor,
+ sizeof(*descriptor));
+ if (ret < 0) {
+ kfree(descriptor);
+ return ret;
+ }
+
+ le16_to_cpus(&descriptor->bcdUSB);
+ le16_to_cpus(&descriptor->idVendor);
+ le16_to_cpus(&descriptor->idProduct);
+ le16_to_cpus(&descriptor->bcdDevice);
+
+ if (RtlCompareMemory(&dev->descriptor, descriptor, sizeof(*descriptor))) {
+ kfree(descriptor);
+ usb_destroy_configuration(dev);
+
+ ret = usb_get_device_descriptor(dev);
+ if (ret < sizeof(dev->descriptor)) {
+ if (ret < 0)
+ err("unable to get device %s descriptor "
+ "(error=%d)", dev->devpath, ret);
+ else
+ err("USB device %s descriptor short read "
+ "(expected %Zi, got %i)",
+ dev->devpath,
+ sizeof(dev->descriptor), ret);
+
+ clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return -EIO;
+ }
+
+ ret = usb_get_configuration(dev);
+ if (ret < 0) {
+ err("unable to get configuration (error=%d)", ret);
+ usb_destroy_configuration(dev);
+ clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ dev->actconfig = dev->config;
+ usb_set_maxpacket(dev);
+
+ return 1;
+ }
+
+ kfree(descriptor);
+
+ ret = usb_set_configuration(dev, dev->actconfig->desc.bConfigurationValue);
+ if (ret < 0) {
+ err("failed to set dev %s active configuration (error=%d)",
+ dev->devpath, ret);
+ return ret;
+ }
+
+ for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+ struct usb_interface *intf = &dev->actconfig->interface[i];
+ struct usb_interface_descriptor *as;
+
+ as = &intf->altsetting[intf->act_altsetting].desc;
+ ret = usb_set_interface(dev, as->bInterfaceNumber,
+ as->bAlternateSetting);
+ if (ret < 0) {
+ err("failed to set active alternate setting "
+ "for dev %s interface %d (error=%d)",
+ dev->devpath, i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int usb_reset_device(struct usb_device *udev)
+{
+ //struct device *gdev = &udev->dev;
+ int r;
+
+ down_read(&gdev->bus->subsys.rwsem);
+ r = usb_physical_reset_device(udev);
+ up_read(&gdev->bus->subsys.rwsem);
+
+ return r;
+}
+
+
-#ifndef __LINUX_HUB_H\r
-#define __LINUX_HUB_H\r
-\r
-/*\r
- * Hub protocol and driver data structures.\r
- *\r
- * Some of these are known to the "virtual root hub" code\r
- * in host controller drivers.\r
- */\r
-#if 0\r
-#include <linux/list.h>\r
-#include <linux/workqueue.h>\r
-#include <linux/compiler.h> /* likely()/unlikely() */\r
-#endif \r
-/*\r
- * Hub request types\r
- */\r
-\r
-#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)\r
-#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)\r
-\r
-/*\r
- * Hub class requests\r
- * See USB 2.0 spec Table 11-16\r
- */\r
-#define HUB_CLEAR_TT_BUFFER 8\r
-#define HUB_RESET_TT 9\r
-#define HUB_GET_TT_STATE 10\r
-#define HUB_STOP_TT 11\r
-\r
-/*\r
- * Hub Class feature numbers\r
- * See USB 2.0 spec Table 11-17\r
- */\r
-#define C_HUB_LOCAL_POWER 0\r
-#define C_HUB_OVER_CURRENT 1\r
-\r
-/*\r
- * Port feature numbers\r
- * See USB 2.0 spec Table 11-17\r
- */\r
-#define USB_PORT_FEAT_CONNECTION 0\r
-#define USB_PORT_FEAT_ENABLE 1\r
-#define USB_PORT_FEAT_SUSPEND 2\r
-#define USB_PORT_FEAT_OVER_CURRENT 3\r
-#define USB_PORT_FEAT_RESET 4\r
-#define USB_PORT_FEAT_POWER 8\r
-#define USB_PORT_FEAT_LOWSPEED 9\r
-#define USB_PORT_FEAT_HIGHSPEED 10\r
-#define USB_PORT_FEAT_C_CONNECTION 16\r
-#define USB_PORT_FEAT_C_ENABLE 17\r
-#define USB_PORT_FEAT_C_SUSPEND 18\r
-#define USB_PORT_FEAT_C_OVER_CURRENT 19\r
-#define USB_PORT_FEAT_C_RESET 20\r
-#define USB_PORT_FEAT_TEST 21\r
-#define USB_PORT_FEAT_INDICATOR 22\r
-\r
-/* \r
- * Hub Status and Hub Change results\r
- * See USB 2.0 spec Table 11-19 and Table 11-20\r
- */\r
-struct usb_port_status {\r
- __u16 wPortStatus;\r
- __u16 wPortChange; \r
-} __attribute__ ((packed));\r
-\r
-/* \r
- * wPortStatus bit field\r
- * See USB 2.0 spec Table 11-21\r
- */\r
-#define USB_PORT_STAT_CONNECTION 0x0001\r
-#define USB_PORT_STAT_ENABLE 0x0002\r
-#define USB_PORT_STAT_SUSPEND 0x0004\r
-#define USB_PORT_STAT_OVERCURRENT 0x0008\r
-#define USB_PORT_STAT_RESET 0x0010\r
-/* bits 5 to 7 are reserved */\r
-#define USB_PORT_STAT_POWER 0x0100\r
-#define USB_PORT_STAT_LOW_SPEED 0x0200\r
-#define USB_PORT_STAT_HIGH_SPEED 0x0400\r
-#define USB_PORT_STAT_TEST 0x0800\r
-#define USB_PORT_STAT_INDICATOR 0x1000\r
-/* bits 13 to 15 are reserved */\r
-\r
-/* \r
- * wPortChange bit field\r
- * See USB 2.0 spec Table 11-22\r
- * Bits 0 to 4 shown, bits 5 to 15 are reserved\r
- */\r
-#define USB_PORT_STAT_C_CONNECTION 0x0001\r
-#define USB_PORT_STAT_C_ENABLE 0x0002\r
-#define USB_PORT_STAT_C_SUSPEND 0x0004\r
-#define USB_PORT_STAT_C_OVERCURRENT 0x0008\r
-#define USB_PORT_STAT_C_RESET 0x0010\r
-\r
-/*\r
- * wHubCharacteristics (masks) \r
- * See USB 2.0 spec Table 11-13, offset 3\r
- */\r
-#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */\r
-#define HUB_CHAR_COMPOUND 0x0004 /* D2 */\r
-#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */\r
-#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */\r
-#define HUB_CHAR_PORTIND 0x0080 /* D7 */\r
-\r
-struct usb_hub_status {\r
- __u16 wHubStatus;\r
- __u16 wHubChange;\r
-} __attribute__ ((packed));\r
-\r
-/*\r
- * Hub Status & Hub Change bit masks\r
- * See USB 2.0 spec Table 11-19 and Table 11-20\r
- * Bits 0 and 1 for wHubStatus and wHubChange\r
- * Bits 2 to 15 are reserved for both\r
- */\r
-#define HUB_STATUS_LOCAL_POWER 0x0001\r
-#define HUB_STATUS_OVERCURRENT 0x0002\r
-#define HUB_CHANGE_LOCAL_POWER 0x0001\r
-#define HUB_CHANGE_OVERCURRENT 0x0002\r
-\r
-\r
-/* \r
- * Hub descriptor \r
- * See USB 2.0 spec Table 11-13\r
- */\r
-\r
-#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)\r
-#define USB_DT_HUB_NONVAR_SIZE 7\r
-\r
-struct usb_hub_descriptor {\r
- __u8 bDescLength;\r
- __u8 bDescriptorType;\r
- __u8 bNbrPorts;\r
- __u16 wHubCharacteristics;\r
- __u8 bPwrOn2PwrGood;\r
- __u8 bHubContrCurrent;\r
- /* add 1 bit for hub status change; round to bytes */\r
- __u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];\r
- __u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];\r
-} __attribute__ ((packed));\r
-\r
-struct usb_device;\r
-\r
-/*\r
- * As of USB 2.0, full/low speed devices are segregated into trees.\r
- * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).\r
- * The other type grows from high speed hubs when they connect to\r
- * full/low speed devices using "Transaction Translators" (TTs).\r
- *\r
- * TTs should only be known to the hub driver, and high speed bus\r
- * drivers (only EHCI for now). They affect periodic scheduling and\r
- * sometimes control/bulk error recovery.\r
- */\r
-struct usb_tt {\r
- struct usb_device *hub; /* upstream highspeed hub */\r
- int multi; /* true means one TT per port */\r
-\r
- /* for control/bulk error recovery (CLEAR_TT_BUFFER) */\r
- spinlock_t lock;\r
- struct list_head clear_list; /* of usb_tt_clear */\r
- struct work_struct kevent;\r
-};\r
-\r
-struct usb_tt_clear {\r
- struct list_head clear_list;\r
- unsigned tt;\r
- u16 devinfo;\r
-};\r
-\r
-extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);\r
-\r
-struct usb_hub {\r
- struct usb_interface *intf; /* the "real" device */\r
- struct urb *urb; /* for interrupt polling pipe */\r
-\r
- /* buffer for urb ... 1 bit each for hub and children, rounded up */\r
- char (*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8];\r
- dma_addr_t buffer_dma; /* DMA address for buffer */\r
- union {\r
- struct usb_hub_status hub;\r
- struct usb_port_status port;\r
- } *status; /* buffer for status reports */\r
-\r
- int error; /* last reported error */\r
- int nerrors; /* track consecutive errors */\r
-\r
- struct list_head hub_list; /* all hubs */\r
- struct list_head event_list; /* hubs w/data or errs ready */\r
-\r
- struct usb_hub_descriptor *descriptor; /* class descriptor */\r
- struct semaphore khubd_sem;\r
- struct usb_tt tt; /* Transaction Translator */\r
-};\r
-\r
-#endif /* __LINUX_HUB_H */\r
+#ifndef __LINUX_HUB_H
+#define __LINUX_HUB_H
+
+/*
+ * Hub protocol and driver data structures.
+ *
+ * Some of these are known to the "virtual root hub" code
+ * in host controller drivers.
+ */
+#if 0
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/compiler.h> /* likely()/unlikely() */
+#endif
+/*
+ * Hub request types
+ */
+
+#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
+#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
+
+/*
+ * Hub class requests
+ * See USB 2.0 spec Table 11-16
+ */
+#define HUB_CLEAR_TT_BUFFER 8
+#define HUB_RESET_TT 9
+#define HUB_GET_TT_STATE 10
+#define HUB_STOP_TT 11
+
+/*
+ * Hub Class feature numbers
+ * See USB 2.0 spec Table 11-17
+ */
+#define C_HUB_LOCAL_POWER 0
+#define C_HUB_OVER_CURRENT 1
+
+/*
+ * Port feature numbers
+ * See USB 2.0 spec Table 11-17
+ */
+#define USB_PORT_FEAT_CONNECTION 0
+#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_HIGHSPEED 10
+#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
+#define USB_PORT_FEAT_TEST 21
+#define USB_PORT_FEAT_INDICATOR 22
+
+/*
+ * Hub Status and Hub Change results
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ */
+struct usb_port_status {
+ __u16 wPortStatus;
+ __u16 wPortChange;
+} __attribute__ ((packed));
+
+/*
+ * wPortStatus bit field
+ * See USB 2.0 spec Table 11-21
+ */
+#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
+/* bits 5 to 7 are reserved */
+#define USB_PORT_STAT_POWER 0x0100
+#define USB_PORT_STAT_LOW_SPEED 0x0200
+#define USB_PORT_STAT_HIGH_SPEED 0x0400
+#define USB_PORT_STAT_TEST 0x0800
+#define USB_PORT_STAT_INDICATOR 0x1000
+/* bits 13 to 15 are reserved */
+
+/*
+ * wPortChange bit field
+ * See USB 2.0 spec Table 11-22
+ * Bits 0 to 4 shown, bits 5 to 15 are reserved
+ */
+#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
+
+/*
+ * wHubCharacteristics (masks)
+ * See USB 2.0 spec Table 11-13, offset 3
+ */
+#define HUB_CHAR_LPSM 0x0003 /* D1 .. D0 */
+#define HUB_CHAR_COMPOUND 0x0004 /* D2 */
+#define HUB_CHAR_OCPM 0x0018 /* D4 .. D3 */
+#define HUB_CHAR_TTTT 0x0060 /* D6 .. D5 */
+#define HUB_CHAR_PORTIND 0x0080 /* D7 */
+
+struct usb_hub_status {
+ __u16 wHubStatus;
+ __u16 wHubChange;
+} __attribute__ ((packed));
+
+/*
+ * Hub Status & Hub Change bit masks
+ * See USB 2.0 spec Table 11-19 and Table 11-20
+ * Bits 0 and 1 for wHubStatus and wHubChange
+ * Bits 2 to 15 are reserved for both
+ */
+#define HUB_STATUS_LOCAL_POWER 0x0001
+#define HUB_STATUS_OVERCURRENT 0x0002
+#define HUB_CHANGE_LOCAL_POWER 0x0001
+#define HUB_CHANGE_OVERCURRENT 0x0002
+
+
+/*
+ * Hub descriptor
+ * See USB 2.0 spec Table 11-13
+ */
+
+#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
+#define USB_DT_HUB_NONVAR_SIZE 7
+
+struct usb_hub_descriptor {
+ __u8 bDescLength;
+ __u8 bDescriptorType;
+ __u8 bNbrPorts;
+ __u16 wHubCharacteristics;
+ __u8 bPwrOn2PwrGood;
+ __u8 bHubContrCurrent;
+ /* add 1 bit for hub status change; round to bytes */
+ __u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
+ __u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
+} __attribute__ ((packed));
+
+struct usb_device;
+
+/*
+ * As of USB 2.0, full/low speed devices are segregated into trees.
+ * One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
+ * The other type grows from high speed hubs when they connect to
+ * full/low speed devices using "Transaction Translators" (TTs).
+ *
+ * TTs should only be known to the hub driver, and high speed bus
+ * drivers (only EHCI for now). They affect periodic scheduling and
+ * sometimes control/bulk error recovery.
+ */
+struct usb_tt {
+ struct usb_device *hub; /* upstream highspeed hub */
+ int multi; /* true means one TT per port */
+
+ /* for control/bulk error recovery (CLEAR_TT_BUFFER) */
+ spinlock_t lock;
+ struct list_head clear_list; /* of usb_tt_clear */
+ struct work_struct kevent;
+};
+
+struct usb_tt_clear {
+ struct list_head clear_list;
+ unsigned tt;
+ u16 devinfo;
+};
+
+extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
+
+struct usb_hub {
+ struct usb_interface *intf; /* the "real" device */
+ struct urb *urb; /* for interrupt polling pipe */
+
+ /* buffer for urb ... 1 bit each for hub and children, rounded up */
+ char (*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8];
+ dma_addr_t buffer_dma; /* DMA address for buffer */
+ union {
+ struct usb_hub_status hub;
+ struct usb_port_status port;
+ } *status; /* buffer for status reports */
+
+ int error; /* last reported error */
+ int nerrors; /* track consecutive errors */
+
+ struct list_head hub_list; /* all hubs */
+ struct list_head event_list; /* hubs w/data or errs ready */
+
+ struct usb_hub_descriptor *descriptor; /* class descriptor */
+ struct semaphore khubd_sem;
+ struct usb_tt tt; /* Transaction Translator */
+};
+
+#endif /* __LINUX_HUB_H */
-PATH_TO_TOP = ../../../..\r
-\r
-TARGET_TYPE = export_driver\r
-\r
-TARGET_NAME = usbcore\r
-\r
-TARGET_DDKLIBS = ntoskrnl.a\r
-\r
-TARGET_CFLAGS = -Wall -I$(PATH_TO_TOP)/ntoskrnl/include -DDEBUG_MODE\r
-\r
-TARGET_OBJECTS = \\r
- message.o hcd.o hcd-pci.o hub.o usb.o config.o urb.o \\r
- buffer_simple.o usb-debug.o ../sys/ros_wrapper.o \\r
- ../sys/linuxwrapper.o usbcore.o\r
-\r
-include $(PATH_TO_TOP)/rules.mak\r
-\r
-include $(TOOLS_PATH)/helper.mk\r
-\r
-# Automatic dependency tracking\r
-DEP_OBJECTS := $(TARGET_OBJECTS)\r
-include $(PATH_TO_TOP)/tools/depend.mk\r
+PATH_TO_TOP = ../../../..
+
+TARGET_TYPE = export_driver
+
+TARGET_NAME = usbcore
+
+TARGET_DDKLIBS = ntoskrnl.a
+
+TARGET_CFLAGS = -Wall -I$(PATH_TO_TOP)/ntoskrnl/include -DDEBUG_MODE
+
+TARGET_OBJECTS = \
+ message.o hcd.o hcd-pci.o hub.o usb.o config.o urb.o \
+ buffer_simple.o usb-debug.o ../sys/ros_wrapper.o \
+ ../sys/linuxwrapper.o usbcore.o
+
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
+
+# Automatic dependency tracking
+DEP_OBJECTS := $(TARGET_OBJECTS)
+include $(PATH_TO_TOP)/tools/depend.mk
-\r
-O_TARGET := message.o hcd.o hcd-pci.o hub.o usb.o config.o urb.o buffer_simple.o urb.o usb-debug.o\r
-\r
-#O_TARGET := urb.o\r
-\r
-include $(TOPDIR)/Rules.make\r
+
+O_TARGET := message.o hcd.o hcd-pci.o hub.o usb.o config.o urb.o buffer_simple.o urb.o usb-debug.o
+
+#O_TARGET := urb.o
+
+include $(TOPDIR)/Rules.make
-/*\r
- * message.c - synchronous message handling\r
- */\r
-#if 0\r
-#include <linux/pci.h> /* for scatterlist macros */\r
-#include <linux/usb.h>\r
-#include <linux/module.h>\r
-#include <linux/slab.h>\r
-#include <linux/init.h>\r
-#include <linux/mm.h>\r
-#include <asm/byteorder.h>\r
-#else\r
-#include "../usb_wrapper.h"\r
-#endif\r
-\r
-#include "hcd.h" /* for usbcore internals */\r
-\r
-// ReactOS specific: No WAITQUEUEs here\r
-#define wake_up(a) do {} while(0)\r
-\r
-struct usb_api_data {\r
- wait_queue_head_t wqh;\r
- int done;\r
-};\r
-\r
-static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)\r
-{\r
- struct usb_api_data *awd = (struct usb_api_data *)urb->context;\r
-\r
- awd->done = 1;\r
- wmb();\r
- wake_up(&awd->wqh);\r
-}\r
-\r
-// Starts urb and waits for completion or timeout\r
-static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)\r
-{ \r
- //DECLARE_WAITQUEUE(wait, current); // Fireball, 24Jan05 - silent gcc complaining about unused wait variable\r
- struct usb_api_data awd;\r
- int status;\r
-\r
- init_waitqueue_head(&awd.wqh); \r
- awd.done = 0;\r
-\r
- set_current_state(TASK_UNINTERRUPTIBLE);\r
- add_wait_queue(&awd.wqh, &wait);\r
-\r
- urb->context = &awd;\r
- status = usb_submit_urb(urb, GFP_ATOMIC);\r
- if (status) {\r
- // something went wrong\r
- usb_free_urb(urb);\r
- set_current_state(TASK_RUNNING);\r
- remove_wait_queue(&awd.wqh, &wait);\r
- return status;\r
- }\r
- \r
- while (timeout && !awd.done)\r
- { \r
- timeout = schedule_timeout(timeout);\r
- set_current_state(TASK_UNINTERRUPTIBLE);\r
- rmb();\r
- }\r
-\r
- set_current_state(TASK_RUNNING);\r
- remove_wait_queue(&awd.wqh, &wait);\r
-\r
- if (!timeout && !awd.done) {\r
- if (urb->status != -EINPROGRESS) { /* No callback?!! */\r
- printk(KERN_ERR "usb: raced timeout, "\r
- "pipe 0x%x status %d time left %d\n",\r
- urb->pipe, urb->status, timeout);\r
- status = urb->status;\r
- } else {\r
- warn("usb_control/bulk_msg: timeout");\r
- usb_unlink_urb(urb); // remove urb safely\r
- status = -ETIMEDOUT;\r
- }\r
- } else\r
- status = urb->status;\r
-\r
- if (actual_length)\r
- *actual_length = urb->actual_length;\r
-\r
- usb_free_urb(urb);\r
- return status;\r
-}\r
-\r
-/*-------------------------------------------------------------------*/\r
-// returns status (negative) or length (positive)\r
-int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, \r
- struct usb_ctrlrequest *cmd, void *data, int len, int timeout)\r
-{\r
- struct urb *urb;\r
- int retv;\r
- int length;\r
-\r
- urb = usb_alloc_urb(0, GFP_NOIO);\r
- if (!urb)\r
- return -ENOMEM;\r
- \r
- usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char*)cmd, data, len,\r
- usb_api_blocking_completion, 0);\r
-\r
- retv = usb_start_wait_urb(urb, timeout, &length);\r
-\r
- if (retv < 0)\r
- return retv;\r
- else\r
- return length;\r
-}\r
-\r
-/**\r
- * usb_control_msg - Builds a control urb, sends it off and waits for completion\r
- * @dev: pointer to the usb device to send the message to\r
- * @pipe: endpoint "pipe" to send the message to\r
- * @request: USB message request value\r
- * @requesttype: USB message request type value\r
- * @value: USB message value\r
- * @index: USB message index value\r
- * @data: pointer to the data to send\r
- * @size: length in bytes of the data to send\r
- * @timeout: time in jiffies to wait for the message to complete before\r
- * timing out (if 0 the wait is forever)\r
- * Context: !in_interrupt ()\r
- *\r
- * This function sends a simple control message to a specified endpoint\r
- * and waits for the message to complete, or timeout.\r
- * \r
- * If successful, it returns the number of bytes transferred, otherwise a negative error number.\r
- *\r
- * Don't use this function from within an interrupt context, like a\r
- * bottom half handler. If you need an asynchronous message, or need to send\r
- * a message from within interrupt context, use usb_submit_urb()\r
- * If a thread in your driver uses this call, make sure your disconnect()\r
- * method can wait for it to complete. Since you don't have a handle on\r
- * the URB used, you can't cancel the request.\r
- */\r
-int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,\r
- __u16 value, __u16 index, void *data, __u16 size, int timeout)\r
-{\r
- struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);\r
- int ret;\r
- \r
- if (!dr)\r
- return -ENOMEM;\r
-\r
- dr->bRequestType= requesttype;\r
- dr->bRequest = request;\r
- dr->wValue = cpu_to_le16p(&value);\r
- dr->wIndex = cpu_to_le16p(&index);\r
- dr->wLength = cpu_to_le16p(&size);\r
-\r
- //dbg("usb_control_msg"); \r
-\r
- ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);\r
-\r
- kfree(dr);\r
-\r
- return ret;\r
-}\r
-\r
-\r
-/**\r
- * usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion\r
- * @usb_dev: pointer to the usb device to send the message to\r
- * @pipe: endpoint "pipe" to send the message to\r
- * @data: pointer to the data to send\r
- * @len: length in bytes of the data to send\r
- * @actual_length: pointer to a location to put the actual length transferred in bytes\r
- * @timeout: time in jiffies to wait for the message to complete before\r
- * timing out (if 0 the wait is forever)\r
- * Context: !in_interrupt ()\r
- *\r
- * This function sends a simple bulk message to a specified endpoint\r
- * and waits for the message to complete, or timeout.\r
- * \r
- * If successful, it returns 0, otherwise a negative error number.\r
- * The number of actual bytes transferred will be stored in the \r
- * actual_length paramater.\r
- *\r
- * Don't use this function from within an interrupt context, like a\r
- * bottom half handler. If you need an asynchronous message, or need to\r
- * send a message from within interrupt context, use usb_submit_urb()\r
- * If a thread in your driver uses this call, make sure your disconnect()\r
- * method can wait for it to complete. Since you don't have a handle on\r
- * the URB used, you can't cancel the request.\r
- */\r
-int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, \r
- void *data, int len, int *actual_length, int timeout)\r
-{\r
- struct urb *urb;\r
-\r
- if (len < 0)\r
- return -EINVAL;\r
-\r
- urb=usb_alloc_urb(0, GFP_KERNEL);\r
- if (!urb)\r
- return -ENOMEM;\r
-\r
- usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,\r
- usb_api_blocking_completion, 0);\r
-\r
- return usb_start_wait_urb(urb,timeout,actual_length);\r
-}\r
-\r
-/*-------------------------------------------------------------------*/\r
-//#warning "Scatter-gather stuff disabled"\r
-#if 0\r
-static void sg_clean (struct usb_sg_request *io)\r
-{\r
- if (io->urbs) {\r
- while (io->entries--)\r
- usb_free_urb (io->urbs [io->entries]);\r
- kfree (io->urbs);\r
- io->urbs = 0;\r
- }\r
- if (io->dev->dev.dma_mask != 0)\r
- usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents);\r
- io->dev = 0;\r
-}\r
-\r
-static void sg_complete (struct urb *urb, struct pt_regs *regs)\r
-{\r
- struct usb_sg_request *io = (struct usb_sg_request *) urb->context;\r
- unsigned long flags;\r
-\r
- spin_lock_irqsave (&io->lock, flags);\r
-\r
- /* In 2.5 we require hcds' endpoint queues not to progress after fault\r
- * reports, until the completion callback (this!) returns. That lets\r
- * device driver code (like this routine) unlink queued urbs first,\r
- * if it needs to, since the HC won't work on them at all. So it's\r
- * not possible for page N+1 to overwrite page N, and so on.\r
- *\r
- * That's only for "hard" faults; "soft" faults (unlinks) sometimes\r
- * complete before the HCD can get requests away from hardware,\r
- * though never during cleanup after a hard fault.\r
- */\r
- if (io->status\r
- && (io->status != -ECONNRESET\r
- || urb->status != -ECONNRESET)\r
- && urb->actual_length) {\r
- dev_err (io->dev->bus->controller,\r
- "dev %s ep%d%s scatterlist error %d/%d\n",\r
- io->dev->devpath,\r
- usb_pipeendpoint (urb->pipe),\r
- usb_pipein (urb->pipe) ? "in" : "out",\r
- urb->status, io->status);\r
- // BUG ();\r
- }\r
-\r
- if (urb->status && urb->status != -ECONNRESET) {\r
- int i, found, status;\r
-\r
- io->status = urb->status;\r
-\r
- /* the previous urbs, and this one, completed already.\r
- * unlink the later ones so they won't rx/tx bad data,\r
- *\r
- * FIXME don't bother unlinking urbs that haven't yet been\r
- * submitted; those non-error cases shouldn't be syslogged\r
- */\r
- for (i = 0, found = 0; i < io->entries; i++) {\r
- if (found) {\r
- status = usb_unlink_urb (io->urbs [i]);\r
- if (status && status != -EINPROGRESS)\r
- err ("sg_complete, unlink --> %d",\r
- status);\r
- } else if (urb == io->urbs [i])\r
- found = 1;\r
- }\r
- }\r
-\r
- /* on the last completion, signal usb_sg_wait() */\r
- io->bytes += urb->actual_length;\r
- io->count--;\r
- if (!io->count)\r
- complete (&io->complete);\r
-\r
- spin_unlock_irqrestore (&io->lock, flags);\r
-}\r
-\r
-\r
-/**\r
- * usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request\r
- * @io: request block being initialized. until usb_sg_wait() returns,\r
- * treat this as a pointer to an opaque block of memory,\r
- * @dev: the usb device that will send or receive the data\r
- * @pipe: endpoint "pipe" used to transfer the data\r
- * @period: polling rate for interrupt endpoints, in frames or\r
- * (for high speed endpoints) microframes; ignored for bulk\r
- * @sg: scatterlist entries\r
- * @nents: how many entries in the scatterlist\r
- * @length: how many bytes to send from the scatterlist, or zero to\r
- * send every byte identified in the list.\r
- * @mem_flags: SLAB_* flags affecting memory allocations in this call\r
- *\r
- * Returns zero for success, else a negative errno value. This initializes a\r
- * scatter/gather request, allocating resources such as I/O mappings and urb\r
- * memory (except maybe memory used by USB controller drivers).\r
- *\r
- * The request must be issued using usb_sg_wait(), which waits for the I/O to\r
- * complete (or to be canceled) and then cleans up all resources allocated by\r
- * usb_sg_init().\r
- *\r
- * The request may be canceled with usb_sg_cancel(), either before or after\r
- * usb_sg_wait() is called.\r
- */\r
-int usb_sg_init (\r
- struct usb_sg_request *io,\r
- struct usb_device *dev,\r
- unsigned pipe, \r
- unsigned period,\r
- struct scatterlist *sg,\r
- int nents,\r
- size_t length,\r
- int mem_flags\r
-)\r
-{\r
- int i;\r
- int urb_flags;\r
- int dma;\r
-\r
- if (!io || !dev || !sg\r
- || usb_pipecontrol (pipe)\r
- || usb_pipeisoc (pipe)\r
- || nents <= 0)\r
- return -EINVAL;\r
-\r
- spin_lock_init (&io->lock);\r
- io->dev = dev;\r
- io->pipe = pipe;\r
- io->sg = sg;\r
- io->nents = nents;\r
-\r
- /* not all host controllers use DMA (like the mainstream pci ones);\r
- * they can use PIO (sl811) or be software over another transport.\r
- */\r
- dma = (dev->dev.dma_mask != 0);\r
- if (dma)\r
- io->entries = usb_buffer_map_sg (dev, pipe, sg, nents);\r
- else\r
- io->entries = nents;\r
-\r
- /* initialize all the urbs we'll use */\r
- if (io->entries <= 0)\r
- return io->entries;\r
-\r
- io->count = 0;\r
- io->urbs = kmalloc (io->entries * sizeof *io->urbs, mem_flags);\r
- if (!io->urbs)\r
- goto nomem;\r
-\r
- urb_flags = URB_ASYNC_UNLINK | URB_NO_DMA_MAP | URB_NO_INTERRUPT;\r
- if (usb_pipein (pipe))\r
- urb_flags |= URB_SHORT_NOT_OK;\r
-\r
- for (i = 0; i < io->entries; i++, io->count = i) {\r
- unsigned len;\r
-\r
- io->urbs [i] = usb_alloc_urb (0, mem_flags);\r
- if (!io->urbs [i]) {\r
- io->entries = i;\r
- goto nomem;\r
- }\r
-\r
- io->urbs [i]->dev = dev;\r
- io->urbs [i]->pipe = pipe;\r
- io->urbs [i]->interval = period;\r
- io->urbs [i]->transfer_flags = urb_flags;\r
-\r
- io->urbs [i]->complete = sg_complete;\r
- io->urbs [i]->context = io;\r
- io->urbs [i]->status = -EINPROGRESS;\r
- io->urbs [i]->actual_length = 0;\r
-\r
- if (dma) {\r
- /* hc may use _only_ transfer_dma */\r
- io->urbs [i]->transfer_dma = sg_dma_address (sg + i);\r
- len = sg_dma_len (sg + i);\r
- } else {\r
- /* hc may use _only_ transfer_buffer */\r
- io->urbs [i]->transfer_buffer =\r
- page_address (sg [i].page) + sg [i].offset;\r
- len = sg [i].length;\r
- }\r
-\r
- if (length) {\r
- len = min_t (unsigned, len, length);\r
- length -= len;\r
- if (length == 0)\r
- io->entries = i + 1;\r
- }\r
- io->urbs [i]->transfer_buffer_length = len;\r
- }\r
- io->urbs [--i]->transfer_flags &= ~URB_NO_INTERRUPT;\r
-\r
- /* transaction state */\r
- io->status = 0;\r
- io->bytes = 0;\r
- init_completion (&io->complete);\r
- return 0;\r
-\r
-nomem:\r
- sg_clean (io);\r
- return -ENOMEM;\r
-}\r
-\r
-\r
-/**\r
- * usb_sg_wait - synchronously execute scatter/gather request\r
- * @io: request block handle, as initialized with usb_sg_init().\r
- * some fields become accessible when this call returns.\r
- * Context: !in_interrupt ()\r
- *\r
- * This function blocks until the specified I/O operation completes. It\r
- * leverages the grouping of the related I/O requests to get good transfer\r
- * rates, by queueing the requests. At higher speeds, such queuing can\r
- * significantly improve USB throughput.\r
- *\r
- * There are three kinds of completion for this function.\r
- * (1) success, where io->status is zero. The number of io->bytes\r
- * transferred is as requested.\r
- * (2) error, where io->status is a negative errno value. The number\r
- * of io->bytes transferred before the error is usually less\r
- * than requested, and can be nonzero.\r
- * (3) cancelation, a type of error with status -ECONNRESET that\r
- * is initiated by usb_sg_cancel().\r
- *\r
- * When this function returns, all memory allocated through usb_sg_init() or\r
- * this call will have been freed. The request block parameter may still be\r
- * passed to usb_sg_cancel(), or it may be freed. It could also be\r
- * reinitialized and then reused.\r
- *\r
- * Data Transfer Rates:\r
- *\r
- * Bulk transfers are valid for full or high speed endpoints.\r
- * The best full speed data rate is 19 packets of 64 bytes each\r
- * per frame, or 1216 bytes per millisecond.\r
- * The best high speed data rate is 13 packets of 512 bytes each\r
- * per microframe, or 52 KBytes per millisecond.\r
- *\r
- * The reason to use interrupt transfers through this API would most likely\r
- * be to reserve high speed bandwidth, where up to 24 KBytes per millisecond\r
- * could be transferred. That capability is less useful for low or full\r
- * speed interrupt endpoints, which allow at most one packet per millisecond,\r
- * of at most 8 or 64 bytes (respectively).\r
- */\r
-void usb_sg_wait (struct usb_sg_request *io)\r
-{\r
- int i;\r
- unsigned long flags;\r
-\r
- /* queue the urbs. */\r
- spin_lock_irqsave (&io->lock, flags);\r
- for (i = 0; i < io->entries && !io->status; i++) {\r
- int retval;\r
-\r
- retval = usb_submit_urb (io->urbs [i], SLAB_ATOMIC);\r
-\r
- /* after we submit, let completions or cancelations fire;\r
- * we handshake using io->status.\r
- */\r
- spin_unlock_irqrestore (&io->lock, flags);\r
- switch (retval) {\r
- /* maybe we retrying will recover */\r
- case -ENXIO: // hc didn't queue this one\r
- case -EAGAIN:\r
- case -ENOMEM:\r
- retval = 0;\r
- i--;\r
- // FIXME: should it usb_sg_cancel() on INTERRUPT?\r
- yield ();\r
- break;\r
-\r
- /* no error? continue immediately.\r
- *\r
- * NOTE: to work better with UHCI (4K I/O buffer may\r
- * need 3K of TDs) it may be good to limit how many\r
- * URBs are queued at once; N milliseconds?\r
- */\r
- case 0:\r
- cpu_relax ();\r
- break;\r
-\r
- /* fail any uncompleted urbs */\r
- default:\r
- io->urbs [i]->status = retval;\r
- dbg ("usb_sg_msg, submit --> %d", retval);\r
- usb_sg_cancel (io);\r
- }\r
- spin_lock_irqsave (&io->lock, flags);\r
- if (retval && io->status == -ECONNRESET)\r
- io->status = retval;\r
- }\r
- spin_unlock_irqrestore (&io->lock, flags);\r
-\r
- /* OK, yes, this could be packaged as non-blocking.\r
- * So could the submit loop above ... but it's easier to\r
- * solve neither problem than to solve both!\r
- */\r
- wait_for_completion (&io->complete);\r
-\r
- sg_clean (io);\r
-}\r
-\r
-/**\r
- * usb_sg_cancel - stop scatter/gather i/o issued by usb_sg_wait()\r
- * @io: request block, initialized with usb_sg_init()\r
- *\r
- * This stops a request after it has been started by usb_sg_wait().\r
- * It can also prevents one initialized by usb_sg_init() from starting,\r
- * so that call just frees resources allocated to the request.\r
- */\r
-void usb_sg_cancel (struct usb_sg_request *io)\r
-{\r
- unsigned long flags;\r
-\r
- spin_lock_irqsave (&io->lock, flags);\r
-\r
- /* shut everything down, if it didn't already */\r
- if (!io->status) {\r
- int i;\r
-\r
- io->status = -ECONNRESET;\r
- for (i = 0; i < io->entries; i++) {\r
- int retval;\r
-\r
- if (!io->urbs [i]->dev)\r
- continue;\r
- retval = usb_unlink_urb (io->urbs [i]);\r
- if (retval && retval != -EINPROGRESS)\r
- warn ("usb_sg_cancel, unlink --> %d", retval);\r
- // FIXME don't warn on "not yet submitted" error\r
- }\r
- }\r
- spin_unlock_irqrestore (&io->lock, flags);\r
-}\r
-#endif\r
-/*-------------------------------------------------------------------*/\r
-\r
-/**\r
- * usb_get_descriptor - issues a generic GET_DESCRIPTOR request\r
- * @dev: the device whose descriptor is being retrieved\r
- * @type: the descriptor type (USB_DT_*)\r
- * @index: the number of the descriptor\r
- * @buf: where to put the descriptor\r
- * @size: how big is "buf"?\r
- * Context: !in_interrupt ()\r
- *\r
- * Gets a USB descriptor. Convenience functions exist to simplify\r
- * getting some types of descriptors. Use\r
- * usb_get_device_descriptor() for USB_DT_DEVICE,\r
- * and usb_get_string() or usb_string() for USB_DT_STRING.\r
- * Configuration descriptors (USB_DT_CONFIG) are part of the device\r
- * structure, at least for the current configuration.\r
- * In addition to a number of USB-standard descriptors, some\r
- * devices also use class-specific or vendor-specific descriptors.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- *\r
- * Returns the number of bytes received on success, or else the status code\r
- * returned by the underlying usb_control_msg() call.\r
- */\r
-int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)\r
-{\r
- int i = 5;\r
- int result;\r
- \r
- memset(buf,0,size); // Make sure we parse really received data\r
-\r
- while (i--) {\r
- /* retries if the returned length was 0; flakey device */\r
- if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),\r
- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,\r
- (type << 8) + index, 0, buf, size,\r
- HZ * USB_CTRL_GET_TIMEOUT)) > 0\r
- || result == -EPIPE)\r
- break;\r
- }\r
- return result;\r
-}\r
-\r
-/**\r
- * usb_get_string - gets a string descriptor\r
- * @dev: the device whose string descriptor is being retrieved\r
- * @langid: code for language chosen (from string descriptor zero)\r
- * @index: the number of the descriptor\r
- * @buf: where to put the string\r
- * @size: how big is "buf"?\r
- * Context: !in_interrupt ()\r
- *\r
- * Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character,\r
- * in little-endian byte order).\r
- * The usb_string() function will often be a convenient way to turn\r
- * these strings into kernel-printable form.\r
- *\r
- * Strings may be referenced in device, configuration, interface, or other\r
- * descriptors, and could also be used in vendor-specific ways.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- *\r
- * Returns the number of bytes received on success, or else the status code\r
- * returned by the underlying usb_control_msg() call.\r
- */\r
-int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)\r
-{\r
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),\r
- USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,\r
- (USB_DT_STRING << 8) + index, langid, buf, size,\r
- HZ * USB_CTRL_GET_TIMEOUT);\r
-}\r
-\r
-/**\r
- * usb_get_device_descriptor - (re)reads the device descriptor\r
- * @dev: the device whose device descriptor is being updated\r
- * Context: !in_interrupt ()\r
- *\r
- * Updates the copy of the device descriptor stored in the device structure,\r
- * which dedicates space for this purpose. Note that several fields are\r
- * converted to the host CPU's byte order: the USB version (bcdUSB), and\r
- * vendors product and version fields (idVendor, idProduct, and bcdDevice).\r
- * That lets device drivers compare against non-byteswapped constants.\r
- *\r
- * There's normally no need to use this call, although some devices\r
- * will change their descriptors after events like updating firmware.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- *\r
- * Returns the number of bytes received on success, or else the status code\r
- * returned by the underlying usb_control_msg() call.\r
- */\r
-int usb_get_device_descriptor(struct usb_device *dev)\r
-{\r
- int ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor,\r
- sizeof(dev->descriptor));\r
- if (ret >= 0) {\r
- le16_to_cpus(&dev->descriptor.bcdUSB);\r
- le16_to_cpus(&dev->descriptor.idVendor);\r
- le16_to_cpus(&dev->descriptor.idProduct);\r
- le16_to_cpus(&dev->descriptor.bcdDevice);\r
- }\r
- return ret;\r
-}\r
-\r
-/**\r
- * usb_get_status - issues a GET_STATUS call\r
- * @dev: the device whose status is being checked\r
- * @type: USB_RECIP_*; for device, interface, or endpoint\r
- * @target: zero (for device), else interface or endpoint number\r
- * @data: pointer to two bytes of bitmap data\r
- * Context: !in_interrupt ()\r
- *\r
- * Returns device, interface, or endpoint status. Normally only of\r
- * interest to see if the device is self powered, or has enabled the\r
- * remote wakeup facility; or whether a bulk or interrupt endpoint\r
- * is halted ("stalled").\r
- *\r
- * Bits in these status bitmaps are set using the SET_FEATURE request,\r
- * and cleared using the CLEAR_FEATURE request. The usb_clear_halt()\r
- * function should be used to clear halt ("stall") status.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- *\r
- * Returns the number of bytes received on success, or else the status code\r
- * returned by the underlying usb_control_msg() call.\r
- */\r
-int usb_get_status(struct usb_device *dev, int type, int target, void *data)\r
-{\r
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),\r
- USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2,\r
- HZ * USB_CTRL_GET_TIMEOUT);\r
-}\r
-\r
-\r
-// hub-only!! ... and only exported for reset/reinit path.\r
-// otherwise used internally, when setting up a config\r
-void usb_set_maxpacket(struct usb_device *dev)\r
-{\r
- int i, b;\r
-\r
- /* NOTE: affects all endpoints _except_ ep0 */\r
- for (i=0; i<dev->actconfig->desc.bNumInterfaces; i++) {\r
- struct usb_interface *ifp = dev->actconfig->interface + i;\r
- struct usb_host_interface *as = ifp->altsetting + ifp->act_altsetting;\r
- struct usb_host_endpoint *ep = as->endpoint;\r
- int e;\r
-\r
- for (e=0; e<as->desc.bNumEndpoints; e++) {\r
- struct usb_endpoint_descriptor *d;\r
- d = &ep [e].desc;\r
- b = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;\r
- if ((d->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==\r
- USB_ENDPOINT_XFER_CONTROL) { /* Control => bidirectional */\r
- dev->epmaxpacketout[b] = d->wMaxPacketSize;\r
- dev->epmaxpacketin [b] = d->wMaxPacketSize;\r
- }\r
- else if (usb_endpoint_out(d->bEndpointAddress)) {\r
- if (d->wMaxPacketSize > dev->epmaxpacketout[b])\r
- dev->epmaxpacketout[b] = d->wMaxPacketSize;\r
- }\r
- else {\r
- if (d->wMaxPacketSize > dev->epmaxpacketin [b])\r
- dev->epmaxpacketin [b] = d->wMaxPacketSize;\r
- }\r
- }\r
- }\r
-}\r
-\r
-/**\r
- * usb_clear_halt - tells device to clear endpoint halt/stall condition\r
- * @dev: device whose endpoint is halted\r
- * @pipe: endpoint "pipe" being cleared\r
- * Context: !in_interrupt ()\r
- *\r
- * This is used to clear halt conditions for bulk and interrupt endpoints,\r
- * as reported by URB completion status. Endpoints that are halted are\r
- * sometimes referred to as being "stalled". Such endpoints are unable\r
- * to transmit or receive data until the halt status is cleared. Any URBs\r
- * queued for such an endpoint should normally be unlinked by the driver\r
- * before clearing the halt condition, as described in sections 5.7.5\r
- * and 5.8.5 of the USB 2.0 spec.\r
- *\r
- * Note that control and isochronous endpoints don't halt, although control\r
- * endpoints report "protocol stall" (for unsupported requests) using the\r
- * same status code used to report a true stall.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- *\r
- * Returns zero on success, or else the status code returned by the\r
- * underlying usb_control_msg() call.\r
- */\r
-int usb_clear_halt(struct usb_device *dev, int pipe)\r
-{\r
- int result;\r
- int endp = usb_pipeendpoint(pipe);\r
- \r
- if (usb_pipein (pipe))\r
- endp |= USB_DIR_IN;\r
-\r
- /* we don't care if it wasn't halted first. in fact some devices\r
- * (like some ibmcam model 1 units) seem to expect hosts to make\r
- * this request for iso endpoints, which can't halt!\r
- */\r
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),\r
- USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0,\r
- HZ * USB_CTRL_SET_TIMEOUT);\r
-\r
- /* don't un-halt or force to DATA0 except on success */\r
- if (result < 0)\r
- return result;\r
-\r
- /* NOTE: seems like Microsoft and Apple don't bother verifying\r
- * the clear "took", so some devices could lock up if you check...\r
- * such as the Hagiwara FlashGate DUAL. So we won't bother.\r
- *\r
- * NOTE: make sure the logic here doesn't diverge much from\r
- * the copy in usb-storage, for as long as we need two copies.\r
- */\r
-\r
- /* toggle was reset by the clear, then ep was reactivated */\r
- usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);\r
- usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));\r
-\r
- return 0;\r
-}\r
-\r
-/**\r
- * usb_set_interface - Makes a particular alternate setting be current\r
- * @dev: the device whose interface is being updated\r
- * @interface: the interface being updated\r
- * @alternate: the setting being chosen.\r
- * Context: !in_interrupt ()\r
- *\r
- * This is used to enable data transfers on interfaces that may not\r
- * be enabled by default. Not all devices support such configurability.\r
- * Only the driver bound to an interface may change its setting.\r
- *\r
- * Within any given configuration, each interface may have several\r
- * alternative settings. These are often used to control levels of\r
- * bandwidth consumption. For example, the default setting for a high\r
- * speed interrupt endpoint may not send more than 64 bytes per microframe,\r
- * while interrupt transfers of up to 3KBytes per microframe are legal.\r
- * Also, isochronous endpoints may never be part of an\r
- * interface's default setting. To access such bandwidth, alternate\r
- * interface settings must be made current.\r
- *\r
- * Note that in the Linux USB subsystem, bandwidth associated with\r
- * an endpoint in a given alternate setting is not reserved until an URB\r
- * is submitted that needs that bandwidth. Some other operating systems\r
- * allocate bandwidth early, when a configuration is chosen.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- * Also, drivers must not change altsettings while urbs are scheduled for\r
- * endpoints in that interface; all such urbs must first be completed\r
- * (perhaps forced by unlinking).\r
- *\r
- * Returns zero on success, or else the status code returned by the\r
- * underlying usb_control_msg() call.\r
- */\r
-int usb_set_interface(struct usb_device *dev, int interface, int alternate)\r
-{\r
- struct usb_interface *iface;\r
- struct usb_host_interface *iface_as;\r
- int i, ret;\r
- void (*disable)(struct usb_device *, int) = dev->bus->op->disable;\r
-\r
- iface = usb_ifnum_to_if(dev, interface);\r
- if (!iface) {\r
- warn("selecting invalid interface %d", interface);\r
- return -EINVAL;\r
- }\r
-\r
- /* 9.4.10 says devices don't need this, if the interface\r
- only has one alternate setting */\r
- if (iface->num_altsetting == 1) {\r
- dbg("ignoring set_interface for dev %d, iface %d, alt %d",\r
- dev->devnum, interface, alternate);\r
- return 0;\r
- }\r
-\r
- if (alternate < 0 || alternate >= iface->num_altsetting)\r
- return -EINVAL;\r
-\r
- if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),\r
- USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,\r
- iface->altsetting[alternate]\r
- .desc.bAlternateSetting,\r
- interface, NULL, 0, HZ * 5)) < 0)\r
- return ret;\r
-\r
- /* FIXME drivers shouldn't need to replicate/bugfix the logic here\r
- * when they implement async or easily-killable versions of this or\r
- * other "should-be-internal" functions (like clear_halt).\r
- * should hcd+usbcore postprocess control requests?\r
- */\r
-\r
- /* prevent submissions using previous endpoint settings */\r
- iface_as = iface->altsetting + iface->act_altsetting;\r
- for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {\r
- u8 ep = iface_as->endpoint [i].desc.bEndpointAddress;\r
- int out = !(ep & USB_DIR_IN);\r
-\r
- /* clear out hcd state, then usbcore state */\r
- if (disable)\r
- disable (dev, ep);\r
- ep &= USB_ENDPOINT_NUMBER_MASK;\r
- (out ? dev->epmaxpacketout : dev->epmaxpacketin ) [ep] = 0;\r
- }\r
- iface->act_altsetting = alternate;\r
-\r
- /* 9.1.1.5: reset toggles for all endpoints affected by this iface-as\r
- *\r
- * Note:\r
- * Despite EP0 is always present in all interfaces/AS, the list of\r
- * endpoints from the descriptor does not contain EP0. Due to its\r
- * omnipresence one might expect EP0 being considered "affected" by\r
- * any SetInterface request and hence assume toggles need to be reset.\r
- * However, EP0 toggles are re-synced for every individual transfer\r
- * during the SETUP stage - hence EP0 toggles are "don't care" here.\r
- * (Likewise, EP0 never "halts" on well designed devices.)\r
- */\r
-\r
- iface_as = &iface->altsetting[alternate];\r
- for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {\r
- u8 ep = iface_as->endpoint[i].desc.bEndpointAddress;\r
- int out = !(ep & USB_DIR_IN);\r
-\r
- ep &= USB_ENDPOINT_NUMBER_MASK;\r
- usb_settoggle (dev, ep, out, 0);\r
- (out ? dev->epmaxpacketout : dev->epmaxpacketin) [ep]\r
- = iface_as->endpoint [i].desc.wMaxPacketSize;\r
- usb_endpoint_running (dev, ep, out);\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-/**\r
- * usb_set_configuration - Makes a particular device setting be current\r
- * @dev: the device whose configuration is being updated\r
- * @configuration: the configuration being chosen.\r
- * Context: !in_interrupt ()\r
- *\r
- * This is used to enable non-default device modes. Not all devices\r
- * support this kind of configurability. By default, configuration\r
- * zero is selected after enumeration; many devices only have a single\r
- * configuration.\r
- *\r
- * USB devices may support one or more configurations, which affect\r
- * power consumption and the functionality available. For example,\r
- * the default configuration is limited to using 100mA of bus power,\r
- * so that when certain device functionality requires more power,\r
- * and the device is bus powered, that functionality will be in some\r
- * non-default device configuration. Other device modes may also be\r
- * reflected as configuration options, such as whether two ISDN\r
- * channels are presented as independent 64Kb/s interfaces or as one\r
- * bonded 128Kb/s interface.\r
- *\r
- * Note that USB has an additional level of device configurability,\r
- * associated with interfaces. That configurability is accessed using\r
- * usb_set_interface().\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- *\r
- * Returns zero on success, or else the status code returned by the\r
- * underlying usb_control_msg() call.\r
- */\r
-int usb_set_configuration(struct usb_device *dev, int configuration)\r
-{\r
- int i, ret;\r
- struct usb_host_config *cp = NULL;\r
- void (*disable)(struct usb_device *, int) = dev->bus->op->disable;\r
- \r
- for (i=0; i<dev->descriptor.bNumConfigurations; i++) {\r
- if (dev->config[i].desc.bConfigurationValue == configuration) {\r
- cp = &dev->config[i];\r
- break;\r
- }\r
- }\r
- if ((!cp && configuration != 0) || (cp && configuration == 0)) {\r
- warn("selecting invalid configuration %d", configuration);\r
- return -EINVAL;\r
- }\r
-\r
- /* if it's already configured, clear out old state first. */\r
- if (dev->state != USB_STATE_ADDRESS && disable) {\r
- for (i = 1 /* skip ep0 */; i < 15; i++) {\r
- disable (dev, i);\r
- disable (dev, USB_DIR_IN | i);\r
- }\r
- }\r
- dev->toggle[0] = dev->toggle[1] = 0;\r
- dev->halted[0] = dev->halted[1] = 0;\r
- dev->state = USB_STATE_ADDRESS;\r
-\r
- if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),\r
- USB_REQ_SET_CONFIGURATION, 0, configuration, 0,\r
- NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)\r
- return ret;\r
- if (configuration)\r
- dev->state = USB_STATE_CONFIGURED;\r
- dev->actconfig = cp;\r
-\r
- /* reset more hc/hcd endpoint state */\r
- usb_set_maxpacket(dev);\r
-\r
- return 0;\r
-}\r
-\r
-\r
-/**\r
- * usb_string - returns ISO 8859-1 version of a string descriptor\r
- * @dev: the device whose string descriptor is being retrieved\r
- * @index: the number of the descriptor\r
- * @buf: where to put the string\r
- * @size: how big is "buf"?\r
- * Context: !in_interrupt ()\r
- * \r
- * This converts the UTF-16LE encoded strings returned by devices, from\r
- * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones\r
- * that are more usable in most kernel contexts. Note that all characters\r
- * in the chosen descriptor that can't be encoded using ISO-8859-1\r
- * are converted to the question mark ("?") character, and this function\r
- * chooses strings in the first language supported by the device.\r
- *\r
- * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit\r
- * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,\r
- * and is appropriate for use many uses of English and several other\r
- * Western European languages. (But it doesn't include the "Euro" symbol.)\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- *\r
- * Returns length of the string (>= 0) or usb_control_msg status (< 0).\r
- */\r
-int usb_string(struct usb_device *dev, int index, char *buf, size_t size)\r
-{\r
- unsigned char *tbuf;\r
- int err, len;\r
- unsigned int u, idx;\r
-\r
- if (size <= 0 || !buf || !index)\r
- return -EINVAL;\r
- buf[0] = 0;\r
- tbuf = kmalloc(256, GFP_KERNEL);\r
- if (!tbuf)\r
- return -ENOMEM;\r
-\r
- /* get langid for strings if it's not yet known */\r
- if (!dev->have_langid) {\r
- err = usb_get_string(dev, 0, 0, tbuf, 4);\r
- if (err < 0) {\r
- err("error getting string descriptor 0 (error=%d)", err);\r
- goto errout;\r
- } else if (tbuf[0] < 4) {\r
- err("string descriptor 0 too short");\r
- err = -EINVAL;\r
- goto errout;\r
- } else {\r
- dev->have_langid = -1;\r
- dev->string_langid = tbuf[2] | (tbuf[3]<< 8);\r
- /* always use the first langid listed */\r
- dbg("USB device number %d default language ID 0x%x",\r
- dev->devnum, dev->string_langid);\r
- }\r
- }\r
-\r
- /*\r
- * ask for the length of the string \r
- */\r
-\r
- err = usb_get_string(dev, dev->string_langid, index, tbuf, 2);\r
- if(err<2)\r
- goto errout;\r
- len=tbuf[0]; \r
- \r
- err = usb_get_string(dev, dev->string_langid, index, tbuf, len);\r
- if (err < 0)\r
- goto errout;\r
-\r
- size--; /* leave room for trailing NULL char in output buffer */\r
- for (idx = 0, u = 2; u < err; u += 2) {\r
- if (idx >= size)\r
- break;\r
- if (tbuf[u+1]) /* high byte */\r
- buf[idx++] = '?'; /* non ISO-8859-1 character */\r
- else\r
- buf[idx++] = tbuf[u];\r
- }\r
- buf[idx] = 0;\r
- err = idx;\r
-\r
- errout:\r
- kfree(tbuf);\r
- return err;\r
-}\r
-\r
-// synchronous request completion model\r
-EXPORT_SYMBOL(usb_control_msg);\r
-EXPORT_SYMBOL(usb_bulk_msg);\r
-\r
-EXPORT_SYMBOL(usb_sg_init);\r
-EXPORT_SYMBOL(usb_sg_cancel);\r
-EXPORT_SYMBOL(usb_sg_wait);\r
-\r
-// synchronous control message convenience routines\r
-EXPORT_SYMBOL(usb_get_descriptor);\r
-EXPORT_SYMBOL(usb_get_device_descriptor);\r
-EXPORT_SYMBOL(usb_get_status);\r
-EXPORT_SYMBOL(usb_get_string);\r
-EXPORT_SYMBOL(usb_string);\r
-EXPORT_SYMBOL(usb_clear_halt);\r
-EXPORT_SYMBOL(usb_set_configuration);\r
-EXPORT_SYMBOL(usb_set_interface);\r
-\r
+/*
+ * message.c - synchronous message handling
+ */
+#if 0
+#include <linux/pci.h> /* for scatterlist macros */
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <asm/byteorder.h>
+#else
+#include "../usb_wrapper.h"
+#endif
+
+#include "hcd.h" /* for usbcore internals */
+
+// ReactOS specific: No WAITQUEUEs here
+#define wake_up(a) do {} while(0)
+
+struct usb_api_data {
+ wait_queue_head_t wqh;
+ int done;
+};
+
+static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_api_data *awd = (struct usb_api_data *)urb->context;
+
+ awd->done = 1;
+ wmb();
+ wake_up(&awd->wqh);
+}
+
+// Starts urb and waits for completion or timeout
+static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
+{
+ //DECLARE_WAITQUEUE(wait, current); // Fireball, 24Jan05 - silent gcc complaining about unused wait variable
+ struct usb_api_data awd;
+ int status;
+
+ init_waitqueue_head(&awd.wqh);
+ awd.done = 0;
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&awd.wqh, &wait);
+
+ urb->context = &awd;
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status) {
+ // something went wrong
+ usb_free_urb(urb);
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&awd.wqh, &wait);
+ return status;
+ }
+
+ while (timeout && !awd.done)
+ {
+ timeout = schedule_timeout(timeout);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ rmb();
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&awd.wqh, &wait);
+
+ if (!timeout && !awd.done) {
+ if (urb->status != -EINPROGRESS) { /* No callback?!! */
+ printk(KERN_ERR "usb: raced timeout, "
+ "pipe 0x%x status %d time left %d\n",
+ urb->pipe, urb->status, timeout);
+ status = urb->status;
+ } else {
+ warn("usb_control/bulk_msg: timeout");
+ usb_unlink_urb(urb); // remove urb safely
+ status = -ETIMEDOUT;
+ }
+ } else
+ status = urb->status;
+
+ if (actual_length)
+ *actual_length = urb->actual_length;
+
+ usb_free_urb(urb);
+ return status;
+}
+
+/*-------------------------------------------------------------------*/
+// returns status (negative) or length (positive)
+int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe,
+ struct usb_ctrlrequest *cmd, void *data, int len, int timeout)
+{
+ struct urb *urb;
+ int retv;
+ int length;
+
+ urb = usb_alloc_urb(0, GFP_NOIO);
+ if (!urb)
+ return -ENOMEM;
+
+ usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char*)cmd, data, len,
+ usb_api_blocking_completion, 0);
+
+ retv = usb_start_wait_urb(urb, timeout, &length);
+
+ if (retv < 0)
+ return retv;
+ else
+ return length;
+}
+
+/**
+ * usb_control_msg - Builds a control urb, sends it off and waits for completion
+ * @dev: pointer to the usb device to send the message to
+ * @pipe: endpoint "pipe" to send the message to
+ * @request: USB message request value
+ * @requesttype: USB message request type value
+ * @value: USB message value
+ * @index: USB message index value
+ * @data: pointer to the data to send
+ * @size: length in bytes of the data to send
+ * @timeout: time in jiffies to wait for the message to complete before
+ * timing out (if 0 the wait is forever)
+ * Context: !in_interrupt ()
+ *
+ * This function sends a simple control message to a specified endpoint
+ * and waits for the message to complete, or timeout.
+ *
+ * If successful, it returns the number of bytes transferred, otherwise a negative error number.
+ *
+ * Don't use this function from within an interrupt context, like a
+ * bottom half handler. If you need an asynchronous message, or need to send
+ * a message from within interrupt context, use usb_submit_urb()
+ * If a thread in your driver uses this call, make sure your disconnect()
+ * method can wait for it to complete. Since you don't have a handle on
+ * the URB used, you can't cancel the request.
+ */
+int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype,
+ __u16 value, __u16 index, void *data, __u16 size, int timeout)
+{
+ struct usb_ctrlrequest *dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+ int ret;
+
+ if (!dr)
+ return -ENOMEM;
+
+ dr->bRequestType= requesttype;
+ dr->bRequest = request;
+ dr->wValue = cpu_to_le16p(&value);
+ dr->wIndex = cpu_to_le16p(&index);
+ dr->wLength = cpu_to_le16p(&size);
+
+ //dbg("usb_control_msg");
+
+ ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+
+ kfree(dr);
+
+ return ret;
+}
+
+
+/**
+ * usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion
+ * @usb_dev: pointer to the usb device to send the message to
+ * @pipe: endpoint "pipe" to send the message to
+ * @data: pointer to the data to send
+ * @len: length in bytes of the data to send
+ * @actual_length: pointer to a location to put the actual length transferred in bytes
+ * @timeout: time in jiffies to wait for the message to complete before
+ * timing out (if 0 the wait is forever)
+ * Context: !in_interrupt ()
+ *
+ * This function sends a simple bulk message to a specified endpoint
+ * and waits for the message to complete, or timeout.
+ *
+ * If successful, it returns 0, otherwise a negative error number.
+ * The number of actual bytes transferred will be stored in the
+ * actual_length paramater.
+ *
+ * Don't use this function from within an interrupt context, like a
+ * bottom half handler. If you need an asynchronous message, or need to
+ * send a message from within interrupt context, use usb_submit_urb()
+ * If a thread in your driver uses this call, make sure your disconnect()
+ * method can wait for it to complete. Since you don't have a handle on
+ * the URB used, you can't cancel the request.
+ */
+int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length, int timeout)
+{
+ struct urb *urb;
+
+ if (len < 0)
+ return -EINVAL;
+
+ urb=usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
+ usb_api_blocking_completion, 0);
+
+ return usb_start_wait_urb(urb,timeout,actual_length);
+}
+
+/*-------------------------------------------------------------------*/
+//#warning "Scatter-gather stuff disabled"
+#if 0
+static void sg_clean (struct usb_sg_request *io)
+{
+ if (io->urbs) {
+ while (io->entries--)
+ usb_free_urb (io->urbs [io->entries]);
+ kfree (io->urbs);
+ io->urbs = 0;
+ }
+ if (io->dev->dev.dma_mask != 0)
+ usb_buffer_unmap_sg (io->dev, io->pipe, io->sg, io->nents);
+ io->dev = 0;
+}
+
+static void sg_complete (struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_sg_request *io = (struct usb_sg_request *) urb->context;
+ unsigned long flags;
+
+ spin_lock_irqsave (&io->lock, flags);
+
+ /* In 2.5 we require hcds' endpoint queues not to progress after fault
+ * reports, until the completion callback (this!) returns. That lets
+ * device driver code (like this routine) unlink queued urbs first,
+ * if it needs to, since the HC won't work on them at all. So it's
+ * not possible for page N+1 to overwrite page N, and so on.
+ *
+ * That's only for "hard" faults; "soft" faults (unlinks) sometimes
+ * complete before the HCD can get requests away from hardware,
+ * though never during cleanup after a hard fault.
+ */
+ if (io->status
+ && (io->status != -ECONNRESET
+ || urb->status != -ECONNRESET)
+ && urb->actual_length) {
+ dev_err (io->dev->bus->controller,
+ "dev %s ep%d%s scatterlist error %d/%d\n",
+ io->dev->devpath,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ urb->status, io->status);
+ // BUG ();
+ }
+
+ if (urb->status && urb->status != -ECONNRESET) {
+ int i, found, status;
+
+ io->status = urb->status;
+
+ /* the previous urbs, and this one, completed already.
+ * unlink the later ones so they won't rx/tx bad data,
+ *
+ * FIXME don't bother unlinking urbs that haven't yet been
+ * submitted; those non-error cases shouldn't be syslogged
+ */
+ for (i = 0, found = 0; i < io->entries; i++) {
+ if (found) {
+ status = usb_unlink_urb (io->urbs [i]);
+ if (status && status != -EINPROGRESS)
+ err ("sg_complete, unlink --> %d",
+ status);
+ } else if (urb == io->urbs [i])
+ found = 1;
+ }
+ }
+
+ /* on the last completion, signal usb_sg_wait() */
+ io->bytes += urb->actual_length;
+ io->count--;
+ if (!io->count)
+ complete (&io->complete);
+
+ spin_unlock_irqrestore (&io->lock, flags);
+}
+
+
+/**
+ * usb_sg_init - initializes scatterlist-based bulk/interrupt I/O request
+ * @io: request block being initialized. until usb_sg_wait() returns,
+ * treat this as a pointer to an opaque block of memory,
+ * @dev: the usb device that will send or receive the data
+ * @pipe: endpoint "pipe" used to transfer the data
+ * @period: polling rate for interrupt endpoints, in frames or
+ * (for high speed endpoints) microframes; ignored for bulk
+ * @sg: scatterlist entries
+ * @nents: how many entries in the scatterlist
+ * @length: how many bytes to send from the scatterlist, or zero to
+ * send every byte identified in the list.
+ * @mem_flags: SLAB_* flags affecting memory allocations in this call
+ *
+ * Returns zero for success, else a negative errno value. This initializes a
+ * scatter/gather request, allocating resources such as I/O mappings and urb
+ * memory (except maybe memory used by USB controller drivers).
+ *
+ * The request must be issued using usb_sg_wait(), which waits for the I/O to
+ * complete (or to be canceled) and then cleans up all resources allocated by
+ * usb_sg_init().
+ *
+ * The request may be canceled with usb_sg_cancel(), either before or after
+ * usb_sg_wait() is called.
+ */
+int usb_sg_init (
+ struct usb_sg_request *io,
+ struct usb_device *dev,
+ unsigned pipe,
+ unsigned period,
+ struct scatterlist *sg,
+ int nents,
+ size_t length,
+ int mem_flags
+)
+{
+ int i;
+ int urb_flags;
+ int dma;
+
+ if (!io || !dev || !sg
+ || usb_pipecontrol (pipe)
+ || usb_pipeisoc (pipe)
+ || nents <= 0)
+ return -EINVAL;
+
+ spin_lock_init (&io->lock);
+ io->dev = dev;
+ io->pipe = pipe;
+ io->sg = sg;
+ io->nents = nents;
+
+ /* not all host controllers use DMA (like the mainstream pci ones);
+ * they can use PIO (sl811) or be software over another transport.
+ */
+ dma = (dev->dev.dma_mask != 0);
+ if (dma)
+ io->entries = usb_buffer_map_sg (dev, pipe, sg, nents);
+ else
+ io->entries = nents;
+
+ /* initialize all the urbs we'll use */
+ if (io->entries <= 0)
+ return io->entries;
+
+ io->count = 0;
+ io->urbs = kmalloc (io->entries * sizeof *io->urbs, mem_flags);
+ if (!io->urbs)
+ goto nomem;
+
+ urb_flags = URB_ASYNC_UNLINK | URB_NO_DMA_MAP | URB_NO_INTERRUPT;
+ if (usb_pipein (pipe))
+ urb_flags |= URB_SHORT_NOT_OK;
+
+ for (i = 0; i < io->entries; i++, io->count = i) {
+ unsigned len;
+
+ io->urbs [i] = usb_alloc_urb (0, mem_flags);
+ if (!io->urbs [i]) {
+ io->entries = i;
+ goto nomem;
+ }
+
+ io->urbs [i]->dev = dev;
+ io->urbs [i]->pipe = pipe;
+ io->urbs [i]->interval = period;
+ io->urbs [i]->transfer_flags = urb_flags;
+
+ io->urbs [i]->complete = sg_complete;
+ io->urbs [i]->context = io;
+ io->urbs [i]->status = -EINPROGRESS;
+ io->urbs [i]->actual_length = 0;
+
+ if (dma) {
+ /* hc may use _only_ transfer_dma */
+ io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
+ len = sg_dma_len (sg + i);
+ } else {
+ /* hc may use _only_ transfer_buffer */
+ io->urbs [i]->transfer_buffer =
+ page_address (sg [i].page) + sg [i].offset;
+ len = sg [i].length;
+ }
+
+ if (length) {
+ len = min_t (unsigned, len, length);
+ length -= len;
+ if (length == 0)
+ io->entries = i + 1;
+ }
+ io->urbs [i]->transfer_buffer_length = len;
+ }
+ io->urbs [--i]->transfer_flags &= ~URB_NO_INTERRUPT;
+
+ /* transaction state */
+ io->status = 0;
+ io->bytes = 0;
+ init_completion (&io->complete);
+ return 0;
+
+nomem:
+ sg_clean (io);
+ return -ENOMEM;
+}
+
+
+/**
+ * usb_sg_wait - synchronously execute scatter/gather request
+ * @io: request block handle, as initialized with usb_sg_init().
+ * some fields become accessible when this call returns.
+ * Context: !in_interrupt ()
+ *
+ * This function blocks until the specified I/O operation completes. It
+ * leverages the grouping of the related I/O requests to get good transfer
+ * rates, by queueing the requests. At higher speeds, such queuing can
+ * significantly improve USB throughput.
+ *
+ * There are three kinds of completion for this function.
+ * (1) success, where io->status is zero. The number of io->bytes
+ * transferred is as requested.
+ * (2) error, where io->status is a negative errno value. The number
+ * of io->bytes transferred before the error is usually less
+ * than requested, and can be nonzero.
+ * (3) cancelation, a type of error with status -ECONNRESET that
+ * is initiated by usb_sg_cancel().
+ *
+ * When this function returns, all memory allocated through usb_sg_init() or
+ * this call will have been freed. The request block parameter may still be
+ * passed to usb_sg_cancel(), or it may be freed. It could also be
+ * reinitialized and then reused.
+ *
+ * Data Transfer Rates:
+ *
+ * Bulk transfers are valid for full or high speed endpoints.
+ * The best full speed data rate is 19 packets of 64 bytes each
+ * per frame, or 1216 bytes per millisecond.
+ * The best high speed data rate is 13 packets of 512 bytes each
+ * per microframe, or 52 KBytes per millisecond.
+ *
+ * The reason to use interrupt transfers through this API would most likely
+ * be to reserve high speed bandwidth, where up to 24 KBytes per millisecond
+ * could be transferred. That capability is less useful for low or full
+ * speed interrupt endpoints, which allow at most one packet per millisecond,
+ * of at most 8 or 64 bytes (respectively).
+ */
+void usb_sg_wait (struct usb_sg_request *io)
+{
+ int i;
+ unsigned long flags;
+
+ /* queue the urbs. */
+ spin_lock_irqsave (&io->lock, flags);
+ for (i = 0; i < io->entries && !io->status; i++) {
+ int retval;
+
+ retval = usb_submit_urb (io->urbs [i], SLAB_ATOMIC);
+
+ /* after we submit, let completions or cancelations fire;
+ * we handshake using io->status.
+ */
+ spin_unlock_irqrestore (&io->lock, flags);
+ switch (retval) {
+ /* maybe we retrying will recover */
+ case -ENXIO: // hc didn't queue this one
+ case -EAGAIN:
+ case -ENOMEM:
+ retval = 0;
+ i--;
+ // FIXME: should it usb_sg_cancel() on INTERRUPT?
+ yield ();
+ break;
+
+ /* no error? continue immediately.
+ *
+ * NOTE: to work better with UHCI (4K I/O buffer may
+ * need 3K of TDs) it may be good to limit how many
+ * URBs are queued at once; N milliseconds?
+ */
+ case 0:
+ cpu_relax ();
+ break;
+
+ /* fail any uncompleted urbs */
+ default:
+ io->urbs [i]->status = retval;
+ dbg ("usb_sg_msg, submit --> %d", retval);
+ usb_sg_cancel (io);
+ }
+ spin_lock_irqsave (&io->lock, flags);
+ if (retval && io->status == -ECONNRESET)
+ io->status = retval;
+ }
+ spin_unlock_irqrestore (&io->lock, flags);
+
+ /* OK, yes, this could be packaged as non-blocking.
+ * So could the submit loop above ... but it's easier to
+ * solve neither problem than to solve both!
+ */
+ wait_for_completion (&io->complete);
+
+ sg_clean (io);
+}
+
+/**
+ * usb_sg_cancel - stop scatter/gather i/o issued by usb_sg_wait()
+ * @io: request block, initialized with usb_sg_init()
+ *
+ * This stops a request after it has been started by usb_sg_wait().
+ * It can also prevents one initialized by usb_sg_init() from starting,
+ * so that call just frees resources allocated to the request.
+ */
+void usb_sg_cancel (struct usb_sg_request *io)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave (&io->lock, flags);
+
+ /* shut everything down, if it didn't already */
+ if (!io->status) {
+ int i;
+
+ io->status = -ECONNRESET;
+ for (i = 0; i < io->entries; i++) {
+ int retval;
+
+ if (!io->urbs [i]->dev)
+ continue;
+ retval = usb_unlink_urb (io->urbs [i]);
+ if (retval && retval != -EINPROGRESS)
+ warn ("usb_sg_cancel, unlink --> %d", retval);
+ // FIXME don't warn on "not yet submitted" error
+ }
+ }
+ spin_unlock_irqrestore (&io->lock, flags);
+}
+#endif
+/*-------------------------------------------------------------------*/
+
+/**
+ * usb_get_descriptor - issues a generic GET_DESCRIPTOR request
+ * @dev: the device whose descriptor is being retrieved
+ * @type: the descriptor type (USB_DT_*)
+ * @index: the number of the descriptor
+ * @buf: where to put the descriptor
+ * @size: how big is "buf"?
+ * Context: !in_interrupt ()
+ *
+ * Gets a USB descriptor. Convenience functions exist to simplify
+ * getting some types of descriptors. Use
+ * usb_get_device_descriptor() for USB_DT_DEVICE,
+ * and usb_get_string() or usb_string() for USB_DT_STRING.
+ * Configuration descriptors (USB_DT_CONFIG) are part of the device
+ * structure, at least for the current configuration.
+ * In addition to a number of USB-standard descriptors, some
+ * devices also use class-specific or vendor-specific descriptors.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns the number of bytes received on success, or else the status code
+ * returned by the underlying usb_control_msg() call.
+ */
+int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
+{
+ int i = 5;
+ int result;
+
+ memset(buf,0,size); // Make sure we parse really received data
+
+ while (i--) {
+ /* retries if the returned length was 0; flakey device */
+ if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+ (type << 8) + index, 0, buf, size,
+ HZ * USB_CTRL_GET_TIMEOUT)) > 0
+ || result == -EPIPE)
+ break;
+ }
+ return result;
+}
+
+/**
+ * usb_get_string - gets a string descriptor
+ * @dev: the device whose string descriptor is being retrieved
+ * @langid: code for language chosen (from string descriptor zero)
+ * @index: the number of the descriptor
+ * @buf: where to put the string
+ * @size: how big is "buf"?
+ * Context: !in_interrupt ()
+ *
+ * Retrieves a string, encoded using UTF-16LE (Unicode, 16 bits per character,
+ * in little-endian byte order).
+ * The usb_string() function will often be a convenient way to turn
+ * these strings into kernel-printable form.
+ *
+ * Strings may be referenced in device, configuration, interface, or other
+ * descriptors, and could also be used in vendor-specific ways.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns the number of bytes received on success, or else the status code
+ * returned by the underlying usb_control_msg() call.
+ */
+int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+ (USB_DT_STRING << 8) + index, langid, buf, size,
+ HZ * USB_CTRL_GET_TIMEOUT);
+}
+
+/**
+ * usb_get_device_descriptor - (re)reads the device descriptor
+ * @dev: the device whose device descriptor is being updated
+ * Context: !in_interrupt ()
+ *
+ * Updates the copy of the device descriptor stored in the device structure,
+ * which dedicates space for this purpose. Note that several fields are
+ * converted to the host CPU's byte order: the USB version (bcdUSB), and
+ * vendors product and version fields (idVendor, idProduct, and bcdDevice).
+ * That lets device drivers compare against non-byteswapped constants.
+ *
+ * There's normally no need to use this call, although some devices
+ * will change their descriptors after events like updating firmware.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns the number of bytes received on success, or else the status code
+ * returned by the underlying usb_control_msg() call.
+ */
+int usb_get_device_descriptor(struct usb_device *dev)
+{
+ int ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor,
+ sizeof(dev->descriptor));
+ if (ret >= 0) {
+ le16_to_cpus(&dev->descriptor.bcdUSB);
+ le16_to_cpus(&dev->descriptor.idVendor);
+ le16_to_cpus(&dev->descriptor.idProduct);
+ le16_to_cpus(&dev->descriptor.bcdDevice);
+ }
+ return ret;
+}
+
+/**
+ * usb_get_status - issues a GET_STATUS call
+ * @dev: the device whose status is being checked
+ * @type: USB_RECIP_*; for device, interface, or endpoint
+ * @target: zero (for device), else interface or endpoint number
+ * @data: pointer to two bytes of bitmap data
+ * Context: !in_interrupt ()
+ *
+ * Returns device, interface, or endpoint status. Normally only of
+ * interest to see if the device is self powered, or has enabled the
+ * remote wakeup facility; or whether a bulk or interrupt endpoint
+ * is halted ("stalled").
+ *
+ * Bits in these status bitmaps are set using the SET_FEATURE request,
+ * and cleared using the CLEAR_FEATURE request. The usb_clear_halt()
+ * function should be used to clear halt ("stall") status.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns the number of bytes received on success, or else the status code
+ * returned by the underlying usb_control_msg() call.
+ */
+int usb_get_status(struct usb_device *dev, int type, int target, void *data)
+{
+ return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2,
+ HZ * USB_CTRL_GET_TIMEOUT);
+}
+
+
+// hub-only!! ... and only exported for reset/reinit path.
+// otherwise used internally, when setting up a config
+void usb_set_maxpacket(struct usb_device *dev)
+{
+ int i, b;
+
+ /* NOTE: affects all endpoints _except_ ep0 */
+ for (i=0; i<dev->actconfig->desc.bNumInterfaces; i++) {
+ struct usb_interface *ifp = dev->actconfig->interface + i;
+ struct usb_host_interface *as = ifp->altsetting + ifp->act_altsetting;
+ struct usb_host_endpoint *ep = as->endpoint;
+ int e;
+
+ for (e=0; e<as->desc.bNumEndpoints; e++) {
+ struct usb_endpoint_descriptor *d;
+ d = &ep [e].desc;
+ b = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+ if ((d->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_CONTROL) { /* Control => bidirectional */
+ dev->epmaxpacketout[b] = d->wMaxPacketSize;
+ dev->epmaxpacketin [b] = d->wMaxPacketSize;
+ }
+ else if (usb_endpoint_out(d->bEndpointAddress)) {
+ if (d->wMaxPacketSize > dev->epmaxpacketout[b])
+ dev->epmaxpacketout[b] = d->wMaxPacketSize;
+ }
+ else {
+ if (d->wMaxPacketSize > dev->epmaxpacketin [b])
+ dev->epmaxpacketin [b] = d->wMaxPacketSize;
+ }
+ }
+ }
+}
+
+/**
+ * usb_clear_halt - tells device to clear endpoint halt/stall condition
+ * @dev: device whose endpoint is halted
+ * @pipe: endpoint "pipe" being cleared
+ * Context: !in_interrupt ()
+ *
+ * This is used to clear halt conditions for bulk and interrupt endpoints,
+ * as reported by URB completion status. Endpoints that are halted are
+ * sometimes referred to as being "stalled". Such endpoints are unable
+ * to transmit or receive data until the halt status is cleared. Any URBs
+ * queued for such an endpoint should normally be unlinked by the driver
+ * before clearing the halt condition, as described in sections 5.7.5
+ * and 5.8.5 of the USB 2.0 spec.
+ *
+ * Note that control and isochronous endpoints don't halt, although control
+ * endpoints report "protocol stall" (for unsupported requests) using the
+ * same status code used to report a true stall.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns zero on success, or else the status code returned by the
+ * underlying usb_control_msg() call.
+ */
+int usb_clear_halt(struct usb_device *dev, int pipe)
+{
+ int result;
+ int endp = usb_pipeendpoint(pipe);
+
+ if (usb_pipein (pipe))
+ endp |= USB_DIR_IN;
+
+ /* we don't care if it wasn't halted first. in fact some devices
+ * (like some ibmcam model 1 units) seem to expect hosts to make
+ * this request for iso endpoints, which can't halt!
+ */
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0,
+ HZ * USB_CTRL_SET_TIMEOUT);
+
+ /* don't un-halt or force to DATA0 except on success */
+ if (result < 0)
+ return result;
+
+ /* NOTE: seems like Microsoft and Apple don't bother verifying
+ * the clear "took", so some devices could lock up if you check...
+ * such as the Hagiwara FlashGate DUAL. So we won't bother.
+ *
+ * NOTE: make sure the logic here doesn't diverge much from
+ * the copy in usb-storage, for as long as we need two copies.
+ */
+
+ /* toggle was reset by the clear, then ep was reactivated */
+ usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
+ usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+
+ return 0;
+}
+
+/**
+ * usb_set_interface - Makes a particular alternate setting be current
+ * @dev: the device whose interface is being updated
+ * @interface: the interface being updated
+ * @alternate: the setting being chosen.
+ * Context: !in_interrupt ()
+ *
+ * This is used to enable data transfers on interfaces that may not
+ * be enabled by default. Not all devices support such configurability.
+ * Only the driver bound to an interface may change its setting.
+ *
+ * Within any given configuration, each interface may have several
+ * alternative settings. These are often used to control levels of
+ * bandwidth consumption. For example, the default setting for a high
+ * speed interrupt endpoint may not send more than 64 bytes per microframe,
+ * while interrupt transfers of up to 3KBytes per microframe are legal.
+ * Also, isochronous endpoints may never be part of an
+ * interface's default setting. To access such bandwidth, alternate
+ * interface settings must be made current.
+ *
+ * Note that in the Linux USB subsystem, bandwidth associated with
+ * an endpoint in a given alternate setting is not reserved until an URB
+ * is submitted that needs that bandwidth. Some other operating systems
+ * allocate bandwidth early, when a configuration is chosen.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ * Also, drivers must not change altsettings while urbs are scheduled for
+ * endpoints in that interface; all such urbs must first be completed
+ * (perhaps forced by unlinking).
+ *
+ * Returns zero on success, or else the status code returned by the
+ * underlying usb_control_msg() call.
+ */
+int usb_set_interface(struct usb_device *dev, int interface, int alternate)
+{
+ struct usb_interface *iface;
+ struct usb_host_interface *iface_as;
+ int i, ret;
+ void (*disable)(struct usb_device *, int) = dev->bus->op->disable;
+
+ iface = usb_ifnum_to_if(dev, interface);
+ if (!iface) {
+ warn("selecting invalid interface %d", interface);
+ return -EINVAL;
+ }
+
+ /* 9.4.10 says devices don't need this, if the interface
+ only has one alternate setting */
+ if (iface->num_altsetting == 1) {
+ dbg("ignoring set_interface for dev %d, iface %d, alt %d",
+ dev->devnum, interface, alternate);
+ return 0;
+ }
+
+ if (alternate < 0 || alternate >= iface->num_altsetting)
+ return -EINVAL;
+
+ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
+ iface->altsetting[alternate]
+ .desc.bAlternateSetting,
+ interface, NULL, 0, HZ * 5)) < 0)
+ return ret;
+
+ /* FIXME drivers shouldn't need to replicate/bugfix the logic here
+ * when they implement async or easily-killable versions of this or
+ * other "should-be-internal" functions (like clear_halt).
+ * should hcd+usbcore postprocess control requests?
+ */
+
+ /* prevent submissions using previous endpoint settings */
+ iface_as = iface->altsetting + iface->act_altsetting;
+ for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
+ u8 ep = iface_as->endpoint [i].desc.bEndpointAddress;
+ int out = !(ep & USB_DIR_IN);
+
+ /* clear out hcd state, then usbcore state */
+ if (disable)
+ disable (dev, ep);
+ ep &= USB_ENDPOINT_NUMBER_MASK;
+ (out ? dev->epmaxpacketout : dev->epmaxpacketin ) [ep] = 0;
+ }
+ iface->act_altsetting = alternate;
+
+ /* 9.1.1.5: reset toggles for all endpoints affected by this iface-as
+ *
+ * Note:
+ * Despite EP0 is always present in all interfaces/AS, the list of
+ * endpoints from the descriptor does not contain EP0. Due to its
+ * omnipresence one might expect EP0 being considered "affected" by
+ * any SetInterface request and hence assume toggles need to be reset.
+ * However, EP0 toggles are re-synced for every individual transfer
+ * during the SETUP stage - hence EP0 toggles are "don't care" here.
+ * (Likewise, EP0 never "halts" on well designed devices.)
+ */
+
+ iface_as = &iface->altsetting[alternate];
+ for (i = 0; i < iface_as->desc.bNumEndpoints; i++) {
+ u8 ep = iface_as->endpoint[i].desc.bEndpointAddress;
+ int out = !(ep & USB_DIR_IN);
+
+ ep &= USB_ENDPOINT_NUMBER_MASK;
+ usb_settoggle (dev, ep, out, 0);
+ (out ? dev->epmaxpacketout : dev->epmaxpacketin) [ep]
+ = iface_as->endpoint [i].desc.wMaxPacketSize;
+ usb_endpoint_running (dev, ep, out);
+ }
+
+ return 0;
+}
+
+/**
+ * usb_set_configuration - Makes a particular device setting be current
+ * @dev: the device whose configuration is being updated
+ * @configuration: the configuration being chosen.
+ * Context: !in_interrupt ()
+ *
+ * This is used to enable non-default device modes. Not all devices
+ * support this kind of configurability. By default, configuration
+ * zero is selected after enumeration; many devices only have a single
+ * configuration.
+ *
+ * USB devices may support one or more configurations, which affect
+ * power consumption and the functionality available. For example,
+ * the default configuration is limited to using 100mA of bus power,
+ * so that when certain device functionality requires more power,
+ * and the device is bus powered, that functionality will be in some
+ * non-default device configuration. Other device modes may also be
+ * reflected as configuration options, such as whether two ISDN
+ * channels are presented as independent 64Kb/s interfaces or as one
+ * bonded 128Kb/s interface.
+ *
+ * Note that USB has an additional level of device configurability,
+ * associated with interfaces. That configurability is accessed using
+ * usb_set_interface().
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns zero on success, or else the status code returned by the
+ * underlying usb_control_msg() call.
+ */
+int usb_set_configuration(struct usb_device *dev, int configuration)
+{
+ int i, ret;
+ struct usb_host_config *cp = NULL;
+ void (*disable)(struct usb_device *, int) = dev->bus->op->disable;
+
+ for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
+ if (dev->config[i].desc.bConfigurationValue == configuration) {
+ cp = &dev->config[i];
+ break;
+ }
+ }
+ if ((!cp && configuration != 0) || (cp && configuration == 0)) {
+ warn("selecting invalid configuration %d", configuration);
+ return -EINVAL;
+ }
+
+ /* if it's already configured, clear out old state first. */
+ if (dev->state != USB_STATE_ADDRESS && disable) {
+ for (i = 1 /* skip ep0 */; i < 15; i++) {
+ disable (dev, i);
+ disable (dev, USB_DIR_IN | i);
+ }
+ }
+ dev->toggle[0] = dev->toggle[1] = 0;
+ dev->halted[0] = dev->halted[1] = 0;
+ dev->state = USB_STATE_ADDRESS;
+
+ if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
+ NULL, 0, HZ * USB_CTRL_SET_TIMEOUT)) < 0)
+ return ret;
+ if (configuration)
+ dev->state = USB_STATE_CONFIGURED;
+ dev->actconfig = cp;
+
+ /* reset more hc/hcd endpoint state */
+ usb_set_maxpacket(dev);
+
+ return 0;
+}
+
+
+/**
+ * usb_string - returns ISO 8859-1 version of a string descriptor
+ * @dev: the device whose string descriptor is being retrieved
+ * @index: the number of the descriptor
+ * @buf: where to put the string
+ * @size: how big is "buf"?
+ * Context: !in_interrupt ()
+ *
+ * This converts the UTF-16LE encoded strings returned by devices, from
+ * usb_get_string_descriptor(), to null-terminated ISO-8859-1 encoded ones
+ * that are more usable in most kernel contexts. Note that all characters
+ * in the chosen descriptor that can't be encoded using ISO-8859-1
+ * are converted to the question mark ("?") character, and this function
+ * chooses strings in the first language supported by the device.
+ *
+ * The ASCII (or, redundantly, "US-ASCII") character set is the seven-bit
+ * subset of ISO 8859-1. ISO-8859-1 is the eight-bit subset of Unicode,
+ * and is appropriate for use many uses of English and several other
+ * Western European languages. (But it doesn't include the "Euro" symbol.)
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Returns length of the string (>= 0) or usb_control_msg status (< 0).
+ */
+int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
+{
+ unsigned char *tbuf;
+ int err, len;
+ unsigned int u, idx;
+
+ if (size <= 0 || !buf || !index)
+ return -EINVAL;
+ buf[0] = 0;
+ tbuf = kmalloc(256, GFP_KERNEL);
+ if (!tbuf)
+ return -ENOMEM;
+
+ /* get langid for strings if it's not yet known */
+ if (!dev->have_langid) {
+ err = usb_get_string(dev, 0, 0, tbuf, 4);
+ if (err < 0) {
+ err("error getting string descriptor 0 (error=%d)", err);
+ goto errout;
+ } else if (tbuf[0] < 4) {
+ err("string descriptor 0 too short");
+ err = -EINVAL;
+ goto errout;
+ } else {
+ dev->have_langid = -1;
+ dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
+ /* always use the first langid listed */
+ dbg("USB device number %d default language ID 0x%x",
+ dev->devnum, dev->string_langid);
+ }
+ }
+
+ /*
+ * ask for the length of the string
+ */
+
+ err = usb_get_string(dev, dev->string_langid, index, tbuf, 2);
+ if(err<2)
+ goto errout;
+ len=tbuf[0];
+
+ err = usb_get_string(dev, dev->string_langid, index, tbuf, len);
+ if (err < 0)
+ goto errout;
+
+ size--; /* leave room for trailing NULL char in output buffer */
+ for (idx = 0, u = 2; u < err; u += 2) {
+ if (idx >= size)
+ break;
+ if (tbuf[u+1]) /* high byte */
+ buf[idx++] = '?'; /* non ISO-8859-1 character */
+ else
+ buf[idx++] = tbuf[u];
+ }
+ buf[idx] = 0;
+ err = idx;
+
+ errout:
+ kfree(tbuf);
+ return err;
+}
+
+// synchronous request completion model
+EXPORT_SYMBOL(usb_control_msg);
+EXPORT_SYMBOL(usb_bulk_msg);
+
+EXPORT_SYMBOL(usb_sg_init);
+EXPORT_SYMBOL(usb_sg_cancel);
+EXPORT_SYMBOL(usb_sg_wait);
+
+// synchronous control message convenience routines
+EXPORT_SYMBOL(usb_get_descriptor);
+EXPORT_SYMBOL(usb_get_device_descriptor);
+EXPORT_SYMBOL(usb_get_status);
+EXPORT_SYMBOL(usb_get_string);
+EXPORT_SYMBOL(usb_string);
+EXPORT_SYMBOL(usb_clear_halt);
+EXPORT_SYMBOL(usb_set_configuration);
+EXPORT_SYMBOL(usb_set_interface);
+
-#include "../usb_wrapper.h"\r
-#include "hcd.h"\r
-\r
-/**\r
- * usb_init_urb - initializes a urb so that it can be used by a USB driver\r
- * @urb: pointer to the urb to initialize\r
- *\r
- * Initializes a urb so that the USB subsystem can use it properly.\r
- *\r
- * If a urb is created with a call to usb_alloc_urb() it is not\r
- * necessary to call this function. Only use this if you allocate the\r
- * space for a struct urb on your own. If you call this function, be\r
- * careful when freeing the memory for your urb that it is no longer in\r
- * use by the USB core.\r
- *\r
- * Only use this function if you _really_ understand what you are doing.\r
- */\r
-void STDCALL usb_init_urb(struct urb *urb)\r
-{\r
- if (urb) {\r
- memset(urb, 0, sizeof(*urb));\r
- urb->count = (atomic_t)ATOMIC_INIT(1);\r
- spin_lock_init(&urb->lock);\r
- }\r
-}\r
-\r
-/**\r
- * usb_alloc_urb - creates a new urb for a USB driver to use\r
- * @iso_packets: number of iso packets for this urb\r
- * @mem_flags: the type of memory to allocate, see kmalloc() for a list of\r
- * valid options for this.\r
- *\r
- * Creates an urb for the USB driver to use, initializes a few internal\r
- * structures, incrementes the usage counter, and returns a pointer to it.\r
- *\r
- * If no memory is available, NULL is returned.\r
- *\r
- * If the driver want to use this urb for interrupt, control, or bulk\r
- * endpoints, pass '0' as the number of iso packets.\r
- *\r
- * The driver must call usb_free_urb() when it is finished with the urb.\r
- */\r
-struct urb STDCALL *usb_alloc_urb(int iso_packets, int mem_flags)\r
-{\r
- struct urb *urb;\r
-\r
- urb = (struct urb *)kmalloc(sizeof(struct urb) + \r
- iso_packets * sizeof(struct usb_iso_packet_descriptor),\r
- mem_flags);\r
- if (!urb) {\r
- err("alloc_urb: kmalloc failed");\r
- return NULL;\r
- }\r
- usb_init_urb(urb);\r
- return urb;\r
-}\r
-\r
-/**\r
- * usb_free_urb - frees the memory used by a urb when all users of it are finished\r
- * @urb: pointer to the urb to free\r
- *\r
- * Must be called when a user of a urb is finished with it. When the last user\r
- * of the urb calls this function, the memory of the urb is freed.\r
- *\r
- * Note: The transfer buffer associated with the urb is not freed, that must be\r
- * done elsewhere.\r
- */\r
-void STDCALL usb_free_urb(struct urb *urb)\r
-{\r
- if (urb)\r
- if (atomic_dec_and_test(&urb->count))\r
- {\r
- kfree(urb);\r
- }\r
-}\r
-\r
-/**\r
- * usb_get_urb - increments the reference count of the urb\r
- * @urb: pointer to the urb to modify\r
- *\r
- * This must be called whenever a urb is transferred from a device driver to a\r
- * host controller driver. This allows proper reference counting to happen\r
- * for urbs.\r
- *\r
- * A pointer to the urb with the incremented reference counter is returned.\r
- */\r
-struct urb STDCALL * usb_get_urb(struct urb *urb)\r
-{\r
- if (urb) {\r
- atomic_inc(&urb->count);\r
- return urb;\r
- } else\r
- return NULL;\r
-}\r
- \r
-\r
-/*-------------------------------------------------------------------*/\r
-\r
-/**\r
- * usb_submit_urb - issue an asynchronous transfer request for an endpoint\r
- * @urb: pointer to the urb describing the request\r
- * @mem_flags: the type of memory to allocate, see kmalloc() for a list\r
- * of valid options for this.\r
- *\r
- * This submits a transfer request, and transfers control of the URB\r
- * describing that request to the USB subsystem. Request completion will\r
- * be indicated later, asynchronously, by calling the completion handler.\r
- * The three types of completion are success, error, and unlink\r
- * (also called "request cancellation").\r
- * URBs may be submitted in interrupt context.\r
- *\r
- * The caller must have correctly initialized the URB before submitting\r
- * it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are\r
- * available to ensure that most fields are correctly initialized, for\r
- * the particular kind of transfer, although they will not initialize\r
- * any transfer flags.\r
- *\r
- * Successful submissions return 0; otherwise this routine returns a\r
- * negative error number. If the submission is successful, the complete()\r
- * callback from the urb will be called exactly once, when the USB core and\r
- * host controller driver are finished with the urb. When the completion\r
- * function is called, control of the URB is returned to the device\r
- * driver which issued the request. The completion handler may then\r
- * immediately free or reuse that URB.\r
- *\r
- * For control endpoints, the synchronous usb_control_msg() call is\r
- * often used (in non-interrupt context) instead of this call.\r
- * That is often used through convenience wrappers, for the requests\r
- * that are standardized in the USB 2.0 specification. For bulk\r
- * endpoints, a synchronous usb_bulk_msg() call is available.\r
- *\r
- * Request Queuing:\r
- *\r
- * URBs may be submitted to endpoints before previous ones complete, to\r
- * minimize the impact of interrupt latencies and system overhead on data\r
- * throughput. This is required for continuous isochronous data streams,\r
- * and may also be required for some kinds of interrupt transfers. Such\r
- * queueing also maximizes bandwidth utilization by letting USB controllers\r
- * start work on later requests before driver software has finished the\r
- * completion processing for earlier requests.\r
- *\r
- * Bulk and Isochronous URBs may always be queued. At this writing, all\r
- * mainstream host controller drivers support queueing for control and\r
- * interrupt transfer requests.\r
- *\r
- * Reserved Bandwidth Transfers:\r
- *\r
- * Periodic transfers (interrupt or isochronous) are performed repeatedly,\r
- * using the interval specified in the urb. Submitting the first urb to\r
- * the endpoint reserves the bandwidth necessary to make those transfers.\r
- * If the USB subsystem can't allocate sufficient bandwidth to perform\r
- * the periodic request, submitting such a periodic request should fail.\r
- *\r
- * Device drivers must explicitly request that repetition, by ensuring that\r
- * some URB is always on the endpoint's queue (except possibly for short\r
- * periods during completion callacks). When there is no longer an urb\r
- * queued, the endpoint's bandwidth reservation is canceled. This means\r
- * drivers can use their completion handlers to ensure they keep bandwidth\r
- * they need, by reinitializing and resubmitting the just-completed urb\r
- * until the driver longer needs that periodic bandwidth.\r
- *\r
- * Memory Flags:\r
- *\r
- * The general rules for how to decide which mem_flags to use\r
- * are the same as for kmalloc. There are four\r
- * different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and\r
- * GFP_ATOMIC.\r
- *\r
- * GFP_NOFS is not ever used, as it has not been implemented yet.\r
- *\r
- * GFP_ATOMIC is used when\r
- * (a) you are inside a completion handler, an interrupt, bottom half,\r
- * tasklet or timer, or\r
- * (b) you are holding a spinlock or rwlock (does not apply to\r
- * semaphores), or\r
- * (c) current->state != TASK_RUNNING, this is the case only after\r
- * you've changed it.\r
- * \r
- * GFP_NOIO is used in the block io path and error handling of storage\r
- * devices.\r
- *\r
- * All other situations use GFP_KERNEL.\r
- *\r
- * Some more specific rules for mem_flags can be inferred, such as\r
- * (1) start_xmit, timeout, and receive methods of network drivers must\r
- * use GFP_ATOMIC (they are called with a spinlock held);\r
- * (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also\r
- * called with a spinlock held);\r
- * (3) If you use a kernel thread with a network driver you must use\r
- * GFP_NOIO, unless (b) or (c) apply;\r
- * (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c)\r
- * apply or your are in a storage driver's block io path;\r
- * (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and\r
- * (6) changing firmware on a running storage or net device uses\r
- * GFP_NOIO, unless b) or c) apply\r
- *\r
- */\r
-int STDCALL usb_submit_urb(struct urb *urb, int mem_flags)\r
-{\r
- int pipe, temp, max;\r
- struct usb_device *dev;\r
- struct usb_operations *op;\r
- int is_out;\r
-// printk("sub dev %p bus %p num %i op %p sub %p\n",\r
-// urb->dev, urb->dev->bus,urb->dev->devnum,urb->dev->bus->op, urb->dev->bus->op->submit_urb);\r
- if (!urb || urb->hcpriv || !urb->complete)\r
- return -EINVAL;\r
- if (!(dev = urb->dev) ||\r
- (dev->state < USB_STATE_DEFAULT) ||\r
- (!dev->bus) || (dev->devnum <= 0))\r
- return -ENODEV;\r
- if (!(op = dev->bus->op) || !op->submit_urb)\r
- return -ENODEV;\r
-\r
- urb->status = -EINPROGRESS;\r
- urb->actual_length = 0;\r
- urb->bandwidth = 0;\r
-\r
- /* Lots of sanity checks, so HCDs can rely on clean data\r
- * and don't need to duplicate tests\r
- */\r
- pipe = urb->pipe;\r
- temp = usb_pipetype (pipe);\r
- is_out = usb_pipeout (pipe);\r
-\r
- if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED)\r
- return -ENODEV;\r
-\r
- /* (actually HCDs may need to duplicate this, endpoint might yet\r
- * stall due to queued bulk/intr transactions that complete after\r
- * we check)\r
- */\r
- if (usb_endpoint_halted (dev, usb_pipeendpoint (pipe), is_out))\r
- return -EPIPE;\r
-\r
- /* FIXME there should be a sharable lock protecting us against\r
- * config/altsetting changes and disconnects, kicking in here.\r
- * (here == before maxpacket, and eventually endpoint type,\r
- * checks get made.)\r
- */\r
-\r
- max = usb_maxpacket (dev, pipe, is_out);\r
- if (max <= 0) {\r
- dbg ("%s: bogus endpoint %d-%s on usb-%s-%s (bad maxpacket %d)",\r
- __FUNCTION__,\r
- usb_pipeendpoint (pipe), is_out ? "OUT" : "IN",\r
- dev->bus->bus_name, dev->devpath,\r
- max);\r
- return -EMSGSIZE;\r
- }\r
-\r
- /* periodic transfers limit size per frame/uframe,\r
- * but drivers only control those sizes for ISO.\r
- * while we're checking, initialize return status.\r
- */\r
- if (temp == PIPE_ISOCHRONOUS) {\r
- int n, len;\r
-\r
- /* "high bandwidth" mode, 1-3 packets/uframe? */\r
- if (dev->speed == USB_SPEED_HIGH) {\r
- int mult = 1 + ((max >> 11) & 0x03);\r
- max &= 0x03ff;\r
- max *= mult;\r
- }\r
-\r
- if (urb->number_of_packets <= 0) \r
- return -EINVAL;\r
- for (n = 0; n < urb->number_of_packets; n++) {\r
- len = urb->iso_frame_desc [n].length;\r
- if (len < 0 || len > max) \r
- return -EMSGSIZE;\r
- urb->iso_frame_desc [n].status = -EXDEV;\r
- urb->iso_frame_desc [n].actual_length = 0;\r
- }\r
- }\r
-\r
- /* the I/O buffer must be mapped/unmapped, except when length=0 */\r
- if (urb->transfer_buffer_length < 0)\r
- return -EMSGSIZE;\r
-\r
-#ifdef DEBUG\r
- /* stuff that drivers shouldn't do, but which shouldn't\r
- * cause problems in HCDs if they get it wrong.\r
- */\r
- {\r
- unsigned int orig_flags = urb->transfer_flags;\r
- unsigned int allowed;\r
-\r
- /* enforce simple/standard policy */\r
- allowed = URB_ASYNC_UNLINK; // affects later unlinks\r
- allowed |= URB_NO_DMA_MAP;\r
- allowed |= URB_NO_INTERRUPT;\r
- switch (temp) {\r
- case PIPE_BULK:\r
- if (is_out)\r
- allowed |= URB_ZERO_PACKET;\r
- /* FALLTHROUGH */\r
- case PIPE_CONTROL:\r
- allowed |= URB_NO_FSBR; /* only affects UHCI */\r
- /* FALLTHROUGH */\r
- default: /* all non-iso endpoints */\r
- if (!is_out)\r
- allowed |= URB_SHORT_NOT_OK;\r
- break;\r
- case PIPE_ISOCHRONOUS:\r
- allowed |= URB_ISO_ASAP;\r
- break;\r
- }\r
- urb->transfer_flags &= allowed;\r
-\r
- /* fail if submitter gave bogus flags */\r
- if (urb->transfer_flags != orig_flags) {\r
- err ("BOGUS urb flags, %x --> %x",\r
- orig_flags, urb->transfer_flags);\r
- return -EINVAL;\r
- }\r
- }\r
-#endif\r
- /*\r
- * Force periodic transfer intervals to be legal values that are\r
- * a power of two (so HCDs don't need to).\r
- *\r
- * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC\r
- * supports different values... this uses EHCI/UHCI defaults (and\r
- * EHCI can use smaller non-default values).\r
- */\r
- switch (temp) {\r
- case PIPE_ISOCHRONOUS:\r
- case PIPE_INTERRUPT:\r
- /* too small? */\r
- if (urb->interval <= 0)\r
- return -EINVAL;\r
- /* too big? */\r
- switch (dev->speed) {\r
- case USB_SPEED_HIGH: /* units are microframes */\r
- // NOTE usb handles 2^15\r
- if (urb->interval > (1024 * 8))\r
- urb->interval = 1024 * 8;\r
- temp = 1024 * 8;\r
- break;\r
- case USB_SPEED_FULL: /* units are frames/msec */\r
- case USB_SPEED_LOW:\r
- if (temp == PIPE_INTERRUPT) {\r
- if (urb->interval > 255)\r
- return -EINVAL;\r
- // NOTE ohci only handles up to 32\r
- temp = 128;\r
- } else {\r
- if (urb->interval > 1024)\r
- urb->interval = 1024;\r
- // NOTE usb and ohci handle up to 2^15\r
- temp = 1024;\r
- }\r
- break;\r
- default:\r
- return -EINVAL;\r
- }\r
- /* power of two? */\r
- while (temp > urb->interval)\r
- temp >>= 1;\r
- urb->interval = temp;\r
- }\r
-\r
- return op->submit_urb (urb, mem_flags);\r
-}\r
-\r
-/*-------------------------------------------------------------------*/\r
-\r
-/**\r
- * usb_unlink_urb - abort/cancel a transfer request for an endpoint\r
- * @urb: pointer to urb describing a previously submitted request\r
- *\r
- * This routine cancels an in-progress request. The requests's\r
- * completion handler will be called with a status code indicating\r
- * that the request has been canceled, and that control of the URB\r
- * has been returned to that device driver.\r
- *\r
- * When the URB_ASYNC_UNLINK transfer flag for the URB is clear, this\r
- * request is synchronous. Success is indicated by returning zero,\r
- * at which time the urb will have been unlinked,\r
- * and the completion function will see status -ENOENT. Failure is\r
- * indicated by any other return value. This mode may not be used\r
- * when unlinking an urb from an interrupt context, such as a bottom\r
- * half or a completion handler,\r
- *\r
- * When the URB_ASYNC_UNLINK transfer flag for the URB is set, this\r
- * request is asynchronous. Success is indicated by returning -EINPROGRESS,\r
- * at which time the urb will normally not have been unlinked,\r
- * and the completion function will see status -ECONNRESET. Failure is\r
- * indicated by any other return value.\r
- */\r
-int STDCALL usb_unlink_urb(struct urb *urb)\r
-{\r
- if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)\r
- return urb->dev->bus->op->unlink_urb(urb);\r
- else\r
- return -ENODEV;\r
-}\r
+#include "../usb_wrapper.h"
+#include "hcd.h"
+
+/**
+ * usb_init_urb - initializes a urb so that it can be used by a USB driver
+ * @urb: pointer to the urb to initialize
+ *
+ * Initializes a urb so that the USB subsystem can use it properly.
+ *
+ * If a urb is created with a call to usb_alloc_urb() it is not
+ * necessary to call this function. Only use this if you allocate the
+ * space for a struct urb on your own. If you call this function, be
+ * careful when freeing the memory for your urb that it is no longer in
+ * use by the USB core.
+ *
+ * Only use this function if you _really_ understand what you are doing.
+ */
+void STDCALL usb_init_urb(struct urb *urb)
+{
+ if (urb) {
+ memset(urb, 0, sizeof(*urb));
+ urb->count = (atomic_t)ATOMIC_INIT(1);
+ spin_lock_init(&urb->lock);
+ }
+}
+
+/**
+ * usb_alloc_urb - creates a new urb for a USB driver to use
+ * @iso_packets: number of iso packets for this urb
+ * @mem_flags: the type of memory to allocate, see kmalloc() for a list of
+ * valid options for this.
+ *
+ * Creates an urb for the USB driver to use, initializes a few internal
+ * structures, incrementes the usage counter, and returns a pointer to it.
+ *
+ * If no memory is available, NULL is returned.
+ *
+ * If the driver want to use this urb for interrupt, control, or bulk
+ * endpoints, pass '0' as the number of iso packets.
+ *
+ * The driver must call usb_free_urb() when it is finished with the urb.
+ */
+struct urb STDCALL *usb_alloc_urb(int iso_packets, int mem_flags)
+{
+ struct urb *urb;
+
+ urb = (struct urb *)kmalloc(sizeof(struct urb) +
+ iso_packets * sizeof(struct usb_iso_packet_descriptor),
+ mem_flags);
+ if (!urb) {
+ err("alloc_urb: kmalloc failed");
+ return NULL;
+ }
+ usb_init_urb(urb);
+ return urb;
+}
+
+/**
+ * usb_free_urb - frees the memory used by a urb when all users of it are finished
+ * @urb: pointer to the urb to free
+ *
+ * Must be called when a user of a urb is finished with it. When the last user
+ * of the urb calls this function, the memory of the urb is freed.
+ *
+ * Note: The transfer buffer associated with the urb is not freed, that must be
+ * done elsewhere.
+ */
+void STDCALL usb_free_urb(struct urb *urb)
+{
+ if (urb)
+ if (atomic_dec_and_test(&urb->count))
+ {
+ kfree(urb);
+ }
+}
+
+/**
+ * usb_get_urb - increments the reference count of the urb
+ * @urb: pointer to the urb to modify
+ *
+ * This must be called whenever a urb is transferred from a device driver to a
+ * host controller driver. This allows proper reference counting to happen
+ * for urbs.
+ *
+ * A pointer to the urb with the incremented reference counter is returned.
+ */
+struct urb STDCALL * usb_get_urb(struct urb *urb)
+{
+ if (urb) {
+ atomic_inc(&urb->count);
+ return urb;
+ } else
+ return NULL;
+}
+
+
+/*-------------------------------------------------------------------*/
+
+/**
+ * usb_submit_urb - issue an asynchronous transfer request for an endpoint
+ * @urb: pointer to the urb describing the request
+ * @mem_flags: the type of memory to allocate, see kmalloc() for a list
+ * of valid options for this.
+ *
+ * This submits a transfer request, and transfers control of the URB
+ * describing that request to the USB subsystem. Request completion will
+ * be indicated later, asynchronously, by calling the completion handler.
+ * The three types of completion are success, error, and unlink
+ * (also called "request cancellation").
+ * URBs may be submitted in interrupt context.
+ *
+ * The caller must have correctly initialized the URB before submitting
+ * it. Functions such as usb_fill_bulk_urb() and usb_fill_control_urb() are
+ * available to ensure that most fields are correctly initialized, for
+ * the particular kind of transfer, although they will not initialize
+ * any transfer flags.
+ *
+ * Successful submissions return 0; otherwise this routine returns a
+ * negative error number. If the submission is successful, the complete()
+ * callback from the urb will be called exactly once, when the USB core and
+ * host controller driver are finished with the urb. When the completion
+ * function is called, control of the URB is returned to the device
+ * driver which issued the request. The completion handler may then
+ * immediately free or reuse that URB.
+ *
+ * For control endpoints, the synchronous usb_control_msg() call is
+ * often used (in non-interrupt context) instead of this call.
+ * That is often used through convenience wrappers, for the requests
+ * that are standardized in the USB 2.0 specification. For bulk
+ * endpoints, a synchronous usb_bulk_msg() call is available.
+ *
+ * Request Queuing:
+ *
+ * URBs may be submitted to endpoints before previous ones complete, to
+ * minimize the impact of interrupt latencies and system overhead on data
+ * throughput. This is required for continuous isochronous data streams,
+ * and may also be required for some kinds of interrupt transfers. Such
+ * queueing also maximizes bandwidth utilization by letting USB controllers
+ * start work on later requests before driver software has finished the
+ * completion processing for earlier requests.
+ *
+ * Bulk and Isochronous URBs may always be queued. At this writing, all
+ * mainstream host controller drivers support queueing for control and
+ * interrupt transfer requests.
+ *
+ * Reserved Bandwidth Transfers:
+ *
+ * Periodic transfers (interrupt or isochronous) are performed repeatedly,
+ * using the interval specified in the urb. Submitting the first urb to
+ * the endpoint reserves the bandwidth necessary to make those transfers.
+ * If the USB subsystem can't allocate sufficient bandwidth to perform
+ * the periodic request, submitting such a periodic request should fail.
+ *
+ * Device drivers must explicitly request that repetition, by ensuring that
+ * some URB is always on the endpoint's queue (except possibly for short
+ * periods during completion callacks). When there is no longer an urb
+ * queued, the endpoint's bandwidth reservation is canceled. This means
+ * drivers can use their completion handlers to ensure they keep bandwidth
+ * they need, by reinitializing and resubmitting the just-completed urb
+ * until the driver longer needs that periodic bandwidth.
+ *
+ * Memory Flags:
+ *
+ * The general rules for how to decide which mem_flags to use
+ * are the same as for kmalloc. There are four
+ * different possible values; GFP_KERNEL, GFP_NOFS, GFP_NOIO and
+ * GFP_ATOMIC.
+ *
+ * GFP_NOFS is not ever used, as it has not been implemented yet.
+ *
+ * GFP_ATOMIC is used when
+ * (a) you are inside a completion handler, an interrupt, bottom half,
+ * tasklet or timer, or
+ * (b) you are holding a spinlock or rwlock (does not apply to
+ * semaphores), or
+ * (c) current->state != TASK_RUNNING, this is the case only after
+ * you've changed it.
+ *
+ * GFP_NOIO is used in the block io path and error handling of storage
+ * devices.
+ *
+ * All other situations use GFP_KERNEL.
+ *
+ * Some more specific rules for mem_flags can be inferred, such as
+ * (1) start_xmit, timeout, and receive methods of network drivers must
+ * use GFP_ATOMIC (they are called with a spinlock held);
+ * (2) queuecommand methods of scsi drivers must use GFP_ATOMIC (also
+ * called with a spinlock held);
+ * (3) If you use a kernel thread with a network driver you must use
+ * GFP_NOIO, unless (b) or (c) apply;
+ * (4) after you have done a down() you can use GFP_KERNEL, unless (b) or (c)
+ * apply or your are in a storage driver's block io path;
+ * (5) USB probe and disconnect can use GFP_KERNEL unless (b) or (c) apply; and
+ * (6) changing firmware on a running storage or net device uses
+ * GFP_NOIO, unless b) or c) apply
+ *
+ */
+int STDCALL usb_submit_urb(struct urb *urb, int mem_flags)
+{
+ int pipe, temp, max;
+ struct usb_device *dev;
+ struct usb_operations *op;
+ int is_out;
+// printk("sub dev %p bus %p num %i op %p sub %p\n",
+// urb->dev, urb->dev->bus,urb->dev->devnum,urb->dev->bus->op, urb->dev->bus->op->submit_urb);
+ if (!urb || urb->hcpriv || !urb->complete)
+ return -EINVAL;
+ if (!(dev = urb->dev) ||
+ (dev->state < USB_STATE_DEFAULT) ||
+ (!dev->bus) || (dev->devnum <= 0))
+ return -ENODEV;
+ if (!(op = dev->bus->op) || !op->submit_urb)
+ return -ENODEV;
+
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->bandwidth = 0;
+
+ /* Lots of sanity checks, so HCDs can rely on clean data
+ * and don't need to duplicate tests
+ */
+ pipe = urb->pipe;
+ temp = usb_pipetype (pipe);
+ is_out = usb_pipeout (pipe);
+
+ if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED)
+ return -ENODEV;
+
+ /* (actually HCDs may need to duplicate this, endpoint might yet
+ * stall due to queued bulk/intr transactions that complete after
+ * we check)
+ */
+ if (usb_endpoint_halted (dev, usb_pipeendpoint (pipe), is_out))
+ return -EPIPE;
+
+ /* FIXME there should be a sharable lock protecting us against
+ * config/altsetting changes and disconnects, kicking in here.
+ * (here == before maxpacket, and eventually endpoint type,
+ * checks get made.)
+ */
+
+ max = usb_maxpacket (dev, pipe, is_out);
+ if (max <= 0) {
+ dbg ("%s: bogus endpoint %d-%s on usb-%s-%s (bad maxpacket %d)",
+ __FUNCTION__,
+ usb_pipeendpoint (pipe), is_out ? "OUT" : "IN",
+ dev->bus->bus_name, dev->devpath,
+ max);
+ return -EMSGSIZE;
+ }
+
+ /* periodic transfers limit size per frame/uframe,
+ * but drivers only control those sizes for ISO.
+ * while we're checking, initialize return status.
+ */
+ if (temp == PIPE_ISOCHRONOUS) {
+ int n, len;
+
+ /* "high bandwidth" mode, 1-3 packets/uframe? */
+ if (dev->speed == USB_SPEED_HIGH) {
+ int mult = 1 + ((max >> 11) & 0x03);
+ max &= 0x03ff;
+ max *= mult;
+ }
+
+ if (urb->number_of_packets <= 0)
+ return -EINVAL;
+ for (n = 0; n < urb->number_of_packets; n++) {
+ len = urb->iso_frame_desc [n].length;
+ if (len < 0 || len > max)
+ return -EMSGSIZE;
+ urb->iso_frame_desc [n].status = -EXDEV;
+ urb->iso_frame_desc [n].actual_length = 0;
+ }
+ }
+
+ /* the I/O buffer must be mapped/unmapped, except when length=0 */
+ if (urb->transfer_buffer_length < 0)
+ return -EMSGSIZE;
+
+#ifdef DEBUG
+ /* stuff that drivers shouldn't do, but which shouldn't
+ * cause problems in HCDs if they get it wrong.
+ */
+ {
+ unsigned int orig_flags = urb->transfer_flags;
+ unsigned int allowed;
+
+ /* enforce simple/standard policy */
+ allowed = URB_ASYNC_UNLINK; // affects later unlinks
+ allowed |= URB_NO_DMA_MAP;
+ allowed |= URB_NO_INTERRUPT;
+ switch (temp) {
+ case PIPE_BULK:
+ if (is_out)
+ allowed |= URB_ZERO_PACKET;
+ /* FALLTHROUGH */
+ case PIPE_CONTROL:
+ allowed |= URB_NO_FSBR; /* only affects UHCI */
+ /* FALLTHROUGH */
+ default: /* all non-iso endpoints */
+ if (!is_out)
+ allowed |= URB_SHORT_NOT_OK;
+ break;
+ case PIPE_ISOCHRONOUS:
+ allowed |= URB_ISO_ASAP;
+ break;
+ }
+ urb->transfer_flags &= allowed;
+
+ /* fail if submitter gave bogus flags */
+ if (urb->transfer_flags != orig_flags) {
+ err ("BOGUS urb flags, %x --> %x",
+ orig_flags, urb->transfer_flags);
+ return -EINVAL;
+ }
+ }
+#endif
+ /*
+ * Force periodic transfer intervals to be legal values that are
+ * a power of two (so HCDs don't need to).
+ *
+ * FIXME want bus->{intr,iso}_sched_horizon values here. Each HC
+ * supports different values... this uses EHCI/UHCI defaults (and
+ * EHCI can use smaller non-default values).
+ */
+ switch (temp) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ /* too small? */
+ if (urb->interval <= 0)
+ return -EINVAL;
+ /* too big? */
+ switch (dev->speed) {
+ case USB_SPEED_HIGH: /* units are microframes */
+ // NOTE usb handles 2^15
+ if (urb->interval > (1024 * 8))
+ urb->interval = 1024 * 8;
+ temp = 1024 * 8;
+ break;
+ case USB_SPEED_FULL: /* units are frames/msec */
+ case USB_SPEED_LOW:
+ if (temp == PIPE_INTERRUPT) {
+ if (urb->interval > 255)
+ return -EINVAL;
+ // NOTE ohci only handles up to 32
+ temp = 128;
+ } else {
+ if (urb->interval > 1024)
+ urb->interval = 1024;
+ // NOTE usb and ohci handle up to 2^15
+ temp = 1024;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* power of two? */
+ while (temp > urb->interval)
+ temp >>= 1;
+ urb->interval = temp;
+ }
+
+ return op->submit_urb (urb, mem_flags);
+}
+
+/*-------------------------------------------------------------------*/
+
+/**
+ * usb_unlink_urb - abort/cancel a transfer request for an endpoint
+ * @urb: pointer to urb describing a previously submitted request
+ *
+ * This routine cancels an in-progress request. The requests's
+ * completion handler will be called with a status code indicating
+ * that the request has been canceled, and that control of the URB
+ * has been returned to that device driver.
+ *
+ * When the URB_ASYNC_UNLINK transfer flag for the URB is clear, this
+ * request is synchronous. Success is indicated by returning zero,
+ * at which time the urb will have been unlinked,
+ * and the completion function will see status -ENOENT. Failure is
+ * indicated by any other return value. This mode may not be used
+ * when unlinking an urb from an interrupt context, such as a bottom
+ * half or a completion handler,
+ *
+ * When the URB_ASYNC_UNLINK transfer flag for the URB is set, this
+ * request is asynchronous. Success is indicated by returning -EINPROGRESS,
+ * at which time the urb will normally not have been unlinked,
+ * and the completion function will see status -ECONNRESET. Failure is
+ * indicated by any other return value.
+ */
+int STDCALL usb_unlink_urb(struct urb *urb)
+{
+ if (urb && urb->dev && urb->dev->bus && urb->dev->bus->op)
+ return urb->dev->bus->op->unlink_urb(urb);
+ else
+ return -ENODEV;
+}
-/*\r
- * debug.c - USB debug helper routines.\r
- *\r
- * I just want these out of the way where they aren't in your\r
- * face, but so that you can still use them..\r
- */\r
-#define CONFIG_USB_DEBUG\r
-#if 0\r
-#include <linux/config.h>\r
-#include <linux/kernel.h>\r
-#include <linux/mm.h>\r
-#include <linux/slab.h>\r
-#ifdef CONFIG_USB_DEBUG\r
- #define DEBUG\r
-#else\r
- #undef DEBUG\r
-#endif\r
-#include <linux/usb.h>\r
-#else\r
-#include "../usb_wrapper.h"\r
-#endif\r
-\r
-static void usb_show_endpoint(struct usb_host_endpoint *endpoint)\r
-{\r
- usb_show_endpoint_descriptor(&endpoint->desc);\r
-}\r
-\r
-static void usb_show_interface(struct usb_host_interface *altsetting)\r
-{\r
- int i;\r
-\r
- usb_show_interface_descriptor(&altsetting->desc);\r
-\r
- for (i = 0; i < altsetting->desc.bNumEndpoints; i++)\r
- usb_show_endpoint(altsetting->endpoint + i);\r
-}\r
-\r
-static void usb_show_config(struct usb_host_config *config)\r
-{\r
- int i, j;\r
- struct usb_interface *ifp;\r
-\r
- usb_show_config_descriptor(&config->desc);\r
- for (i = 0; i < config->desc.bNumInterfaces; i++) {\r
- ifp = config->interface + i;\r
-\r
- if (!ifp)\r
- break;\r
-\r
- printk("\n Interface: %d\n", i);\r
- for (j = 0; j < ifp->num_altsetting; j++)\r
- usb_show_interface(ifp->altsetting + j);\r
- }\r
-}\r
-\r
-void usb_show_device(struct usb_device *dev)\r
-{\r
- int i;\r
-\r
- usb_show_device_descriptor(&dev->descriptor);\r
- for (i = 0; i < dev->descriptor.bNumConfigurations; i++)\r
- usb_show_config(dev->config + i);\r
-}\r
-\r
-/*\r
- * Parse and show the different USB descriptors.\r
- */\r
-void usb_show_device_descriptor(struct usb_device_descriptor *desc)\r
-{\r
- if (!desc)\r
- {\r
- printk("Invalid USB device descriptor (NULL POINTER)\n");\r
- return;\r
- }\r
- printk(" Length = %2d%s\n", desc->bLength,\r
- desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)");\r
- printk(" DescriptorType = %02x\n", desc->bDescriptorType);\r
-\r
- printk(" USB version = %x.%02x\n",\r
- desc->bcdUSB >> 8, desc->bcdUSB & 0xff);\r
- printk(" Vendor:Product = %04x:%04x\n",\r
- desc->idVendor, desc->idProduct);\r
- printk(" MaxPacketSize0 = %d\n", desc->bMaxPacketSize0);\r
- printk(" NumConfigurations = %d\n", desc->bNumConfigurations);\r
- printk(" Device version = %x.%02x\n",\r
- desc->bcdDevice >> 8, desc->bcdDevice & 0xff);\r
-\r
- printk(" Device Class:SubClass:Protocol = %02x:%02x:%02x\n",\r
- desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol);\r
- switch (desc->bDeviceClass) {\r
- case 0:\r
- printk(" Per-interface classes\n");\r
- break;\r
- case USB_CLASS_AUDIO:\r
- printk(" Audio device class\n");\r
- break;\r
- case USB_CLASS_COMM:\r
- printk(" Communications class\n");\r
- break;\r
- case USB_CLASS_HID:\r
- printk(" Human Interface Devices class\n");\r
- break;\r
- case USB_CLASS_PRINTER:\r
- printk(" Printer device class\n");\r
- break;\r
- case USB_CLASS_MASS_STORAGE:\r
- printk(" Mass Storage device class\n");\r
- break;\r
- case USB_CLASS_HUB:\r
- printk(" Hub device class\n");\r
- break;\r
- case USB_CLASS_VENDOR_SPEC:\r
- printk(" Vendor class\n");\r
- break;\r
- default:\r
- printk(" Unknown class\n");\r
- }\r
-}\r
-\r
-void usb_show_config_descriptor(struct usb_config_descriptor *desc)\r
-{\r
- printk("Configuration:\n");\r
- printk(" bLength = %4d%s\n", desc->bLength,\r
- desc->bLength == USB_DT_CONFIG_SIZE ? "" : " (!!!)");\r
- printk(" bDescriptorType = %02x\n", desc->bDescriptorType);\r
- printk(" wTotalLength = %04x\n", desc->wTotalLength);\r
- printk(" bNumInterfaces = %02x\n", desc->bNumInterfaces);\r
- printk(" bConfigurationValue = %02x\n", desc->bConfigurationValue);\r
- printk(" iConfiguration = %02x\n", desc->iConfiguration);\r
- printk(" bmAttributes = %02x\n", desc->bmAttributes);\r
- printk(" bMaxPower = %4dmA\n", desc->bMaxPower * 2);\r
-}\r
-\r
-void usb_show_interface_descriptor(struct usb_interface_descriptor *desc)\r
-{\r
- printk(" Alternate Setting: %2d\n", desc->bAlternateSetting);\r
- printk(" bLength = %4d%s\n", desc->bLength,\r
- desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)");\r
- printk(" bDescriptorType = %02x\n", desc->bDescriptorType);\r
- printk(" bInterfaceNumber = %02x\n", desc->bInterfaceNumber);\r
- printk(" bAlternateSetting = %02x\n", desc->bAlternateSetting);\r
- printk(" bNumEndpoints = %02x\n", desc->bNumEndpoints);\r
- printk(" bInterface Class:SubClass:Protocol = %02x:%02x:%02x\n",\r
- desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol);\r
- printk(" iInterface = %02x\n", desc->iInterface);\r
-}\r
-\r
-void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *desc)\r
-{\r
- char *LengthCommentString = (desc->bLength ==\r
- USB_DT_ENDPOINT_AUDIO_SIZE) ? " (Audio)" : (desc->bLength ==\r
- USB_DT_ENDPOINT_SIZE) ? "" : " (!!!)";\r
- char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" };\r
-\r
- printk(" Endpoint:\n");\r
- printk(" bLength = %4d%s\n",\r
- desc->bLength, LengthCommentString);\r
- printk(" bDescriptorType = %02x\n", desc->bDescriptorType);\r
- printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress,\r
- (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==\r
- USB_ENDPOINT_XFER_CONTROL ? "i/o" :\r
- (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? "in" : "out");\r
- printk(" bmAttributes = %02x (%s)\n", desc->bmAttributes,\r
- EndpointType[USB_ENDPOINT_XFERTYPE_MASK & desc->bmAttributes]);\r
- printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize);\r
- printk(" bInterval = %02x\n", desc->bInterval);\r
-\r
- /* Audio extensions to the endpoint descriptor */\r
- if (desc->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) {\r
- printk(" bRefresh = %02x\n", desc->bRefresh);\r
- printk(" bSynchAddress = %02x\n", desc->bSynchAddress);\r
- }\r
-}\r
-\r
-void usb_show_string(struct usb_device *dev, char *id, int index)\r
-{\r
- char *buf;\r
-\r
- if (!index)\r
- return;\r
- if (!(buf = kmalloc(256, GFP_KERNEL)))\r
- return;\r
- if (usb_string(dev, index, buf, 256) > 0)\r
- dev_printk(KERN_INFO, &dev->dev, "%s: %s\n", id, buf);\r
- kfree(buf);\r
-}\r
-\r
-void usb_dump_urb (struct urb *urb)\r
-{\r
- printk ("urb :%p\n", urb);\r
- printk ("dev :%p\n", urb->dev);\r
- printk ("pipe :%08X\n", urb->pipe);\r
- printk ("status :%d\n", urb->status);\r
- printk ("transfer_flags :%08X\n", urb->transfer_flags);\r
- printk ("transfer_buffer :%p\n", urb->transfer_buffer);\r
- printk ("transfer_buffer_length:%d\n", urb->transfer_buffer_length);\r
- printk ("actual_length :%d\n", urb->actual_length);\r
- printk ("setup_packet :%p\n", urb->setup_packet);\r
- printk ("start_frame :%d\n", urb->start_frame);\r
- printk ("number_of_packets :%d\n", urb->number_of_packets);\r
- printk ("interval :%d\n", urb->interval);\r
- printk ("error_count :%d\n", urb->error_count);\r
- printk ("context :%p\n", urb->context);\r
- printk ("complete :%p\n", urb->complete);\r
-}\r
-\r
+/*
+ * 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..
+ */
+#define CONFIG_USB_DEBUG
+#if 0
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+#else
+#include "../usb_wrapper.h"
+#endif
+
+static void usb_show_endpoint(struct usb_host_endpoint *endpoint)
+{
+ usb_show_endpoint_descriptor(&endpoint->desc);
+}
+
+static void usb_show_interface(struct usb_host_interface *altsetting)
+{
+ int i;
+
+ usb_show_interface_descriptor(&altsetting->desc);
+
+ for (i = 0; i < altsetting->desc.bNumEndpoints; i++)
+ usb_show_endpoint(altsetting->endpoint + i);
+}
+
+static void usb_show_config(struct usb_host_config *config)
+{
+ int i, j;
+ struct usb_interface *ifp;
+
+ usb_show_config_descriptor(&config->desc);
+ for (i = 0; i < config->desc.bNumInterfaces; i++) {
+ ifp = config->interface + i;
+
+ if (!ifp)
+ break;
+
+ printk("\n Interface: %d\n", i);
+ for (j = 0; j < ifp->num_altsetting; j++)
+ usb_show_interface(ifp->altsetting + j);
+ }
+}
+
+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)
+{
+ if (!desc)
+ {
+ printk("Invalid USB device descriptor (NULL POINTER)\n");
+ return;
+ }
+ printk(" Length = %2d%s\n", desc->bLength,
+ desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)");
+ printk(" DescriptorType = %02x\n", desc->bDescriptorType);
+
+ printk(" USB version = %x.%02x\n",
+ desc->bcdUSB >> 8, desc->bcdUSB & 0xff);
+ printk(" Vendor:Product = %04x:%04x\n",
+ desc->idVendor, desc->idProduct);
+ printk(" MaxPacketSize0 = %d\n", desc->bMaxPacketSize0);
+ printk(" NumConfigurations = %d\n", desc->bNumConfigurations);
+ printk(" Device version = %x.%02x\n",
+ desc->bcdDevice >> 8, desc->bcdDevice & 0xff);
+
+ printk(" Device Class:SubClass:Protocol = %02x:%02x:%02x\n",
+ desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol);
+ switch (desc->bDeviceClass) {
+ case 0:
+ printk(" Per-interface classes\n");
+ break;
+ case USB_CLASS_AUDIO:
+ printk(" Audio device class\n");
+ break;
+ case USB_CLASS_COMM:
+ printk(" Communications class\n");
+ break;
+ case USB_CLASS_HID:
+ printk(" Human Interface Devices class\n");
+ break;
+ case USB_CLASS_PRINTER:
+ printk(" Printer device class\n");
+ break;
+ case USB_CLASS_MASS_STORAGE:
+ printk(" Mass Storage device class\n");
+ break;
+ case USB_CLASS_HUB:
+ printk(" Hub device class\n");
+ break;
+ case USB_CLASS_VENDOR_SPEC:
+ 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 == USB_DT_CONFIG_SIZE ? "" : " (!!!)");
+ 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(" bMaxPower = %4dmA\n", desc->bMaxPower * 2);
+}
+
+void usb_show_interface_descriptor(struct usb_interface_descriptor *desc)
+{
+ printk(" Alternate Setting: %2d\n", desc->bAlternateSetting);
+ printk(" bLength = %4d%s\n", desc->bLength,
+ desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)");
+ 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(" bInterface Class:SubClass:Protocol = %02x:%02x:%02x\n",
+ desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol);
+ printk(" iInterface = %02x\n", desc->iInterface);
+}
+
+void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *desc)
+{
+ char *LengthCommentString = (desc->bLength ==
+ USB_DT_ENDPOINT_AUDIO_SIZE) ? " (Audio)" : (desc->bLength ==
+ USB_DT_ENDPOINT_SIZE) ? "" : " (!!!)";
+ char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" };
+
+ printk(" Endpoint:\n");
+ printk(" bLength = %4d%s\n",
+ desc->bLength, LengthCommentString);
+ printk(" bDescriptorType = %02x\n", desc->bDescriptorType);
+ printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress,
+ (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_CONTROL ? "i/o" :
+ (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? "in" : "out");
+ printk(" bmAttributes = %02x (%s)\n", desc->bmAttributes,
+ EndpointType[USB_ENDPOINT_XFERTYPE_MASK & desc->bmAttributes]);
+ printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize);
+ printk(" bInterval = %02x\n", desc->bInterval);
+
+ /* Audio extensions to the endpoint descriptor */
+ if (desc->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) {
+ printk(" bRefresh = %02x\n", desc->bRefresh);
+ printk(" bSynchAddress = %02x\n", desc->bSynchAddress);
+ }
+}
+
+void usb_show_string(struct usb_device *dev, char *id, int index)
+{
+ char *buf;
+
+ if (!index)
+ return;
+ if (!(buf = kmalloc(256, GFP_KERNEL)))
+ return;
+ if (usb_string(dev, index, buf, 256) > 0)
+ dev_printk(KERN_INFO, &dev->dev, "%s: %s\n", id, buf);
+ kfree(buf);
+}
+
+void usb_dump_urb (struct urb *urb)
+{
+ printk ("urb :%p\n", urb);
+ printk ("dev :%p\n", urb->dev);
+ printk ("pipe :%08X\n", urb->pipe);
+ printk ("status :%d\n", urb->status);
+ printk ("transfer_flags :%08X\n", urb->transfer_flags);
+ printk ("transfer_buffer :%p\n", urb->transfer_buffer);
+ printk ("transfer_buffer_length:%d\n", urb->transfer_buffer_length);
+ printk ("actual_length :%d\n", urb->actual_length);
+ printk ("setup_packet :%p\n", urb->setup_packet);
+ printk ("start_frame :%d\n", urb->start_frame);
+ printk ("number_of_packets :%d\n", urb->number_of_packets);
+ printk ("interval :%d\n", urb->interval);
+ printk ("error_count :%d\n", urb->error_count);
+ printk ("context :%p\n", urb->context);
+ printk ("complete :%p\n", urb->complete);
+}
+
-/*\r
- * drivers/usb/usb.c\r
- *\r
- * (C) Copyright Linus Torvalds 1999\r
- * (C) Copyright Johannes Erdfelt 1999-2001\r
- * (C) Copyright Andreas Gal 1999\r
- * (C) Copyright Gregory P. Smith 1999\r
- * (C) Copyright Deti Fliegl 1999 (new USB architecture)\r
- * (C) Copyright Randy Dunlap 2000\r
- * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,\r
- more docs, etc)\r
- * (C) Copyright Yggdrasil Computing, Inc. 2000\r
- * (usb_device_id matching changes by Adam J. Richter)\r
- * (C) Copyright Greg Kroah-Hartman 2002-2003\r
- *\r
- * NOTE! This is not actually a driver at all, rather this is\r
- * just a collection of helper routines that implement the\r
- * generic USB things that the real drivers can use..\r
- *\r
- * Think of this as a "USB library" rather than anything else.\r
- * It should be considered a slave, with no callbacks. Callbacks\r
- * are evil.\r
- */\r
-\r
-#if 0\r
-#include <linux/config.h>\r
-\r
-#ifdef CONFIG_USB_DEBUG\r
- #define DEBUG\r
-#else\r
- #undef DEBUG\r
-#endif\r
-\r
-#include <linux/module.h>\r
-#include <linux/string.h>\r
-#include <linux/bitops.h>\r
-#include <linux/slab.h>\r
-#include <linux/interrupt.h> /* for in_interrupt() */\r
-#include <linux/kmod.h>\r
-#include <linux/init.h>\r
-#include <linux/spinlock.h>\r
-#include <linux/errno.h>\r
-#include <linux/smp_lock.h>\r
-#include <linux/usb.h>\r
-\r
-#include <asm/io.h>\r
-#include <asm/scatterlist.h>\r
-#include <linux/mm.h>\r
-#include <linux/dma-mapping.h>\r
-#include "hcd.h"\r
-#include "usb.h"\r
-#else\r
-#include "../usb_wrapper.h"\r
-#include "hcd.h"\r
-#endif\r
-\r
-\r
-extern int usb_hub_init(void);\r
-extern void usb_hub_cleanup(void);\r
-extern int usb_major_init(void);\r
-extern void usb_major_cleanup(void);\r
-\r
-\r
-int nousb; /* Disable USB when built into kernel image */\r
- /* Not honored on modular build */\r
-\r
-\r
-static int generic_probe (struct device *dev)\r
-{\r
- return 0;\r
-}\r
-static int generic_remove (struct device *dev)\r
-{\r
- return 0;\r
-}\r
-\r
-static struct device_driver usb_generic_driver = {\r
- .name = "usb",\r
- .bus = &usb_bus_type,\r
- .probe = generic_probe,\r
- .remove = generic_remove,\r
-};\r
-\r
-static int usb_generic_driver_data;\r
-\r
-/* needs to be called with BKL held */\r
-int usb_device_probe(struct device *dev)\r
-{\r
- struct usb_interface * intf = to_usb_interface(dev);\r
- struct usb_driver * driver = to_usb_driver(dev->driver);\r
- const struct usb_device_id *id;\r
- int error = -ENODEV;\r
-\r
- dev_dbg(dev, "%s\n", __FUNCTION__);\r
-\r
- if (!driver->probe)\r
- return error;\r
-\r
- id = usb_match_id (intf, driver->id_table);\r
- if (id) {\r
- dev_dbg (dev, "%s - got id\n", __FUNCTION__);\r
- down (&driver->serialize);\r
- error = driver->probe (intf, id);\r
- up (&driver->serialize);\r
- }\r
- if (!error)\r
- intf->driver = driver;\r
-\r
- return error;\r
-}\r
-\r
-int usb_device_remove(struct device *dev)\r
-{\r
- struct usb_interface *intf;\r
- struct usb_driver *driver;\r
-\r
- intf = list_entry(dev,struct usb_interface,dev);\r
- driver = to_usb_driver(dev->driver);\r
-\r
- down(&driver->serialize);\r
-\r
- if (intf->driver && intf->driver->disconnect)\r
- intf->driver->disconnect(intf);\r
-\r
- /* if driver->disconnect didn't release the interface */\r
- if (intf->driver)\r
- usb_driver_release_interface(driver, intf);\r
-\r
- up(&driver->serialize);\r
-\r
- return 0;\r
-}\r
-\r
-/**\r
- * usb_register - register a USB driver\r
- * @new_driver: USB operations for the driver\r
- *\r
- * Registers a USB driver with the USB core. The list of unattached\r
- * interfaces will be rescanned whenever a new driver is added, allowing\r
- * the new driver to attach to any recognized devices.\r
- * Returns a negative error code on failure and 0 on success.\r
- * \r
- * NOTE: if you want your driver to use the USB major number, you must call\r
- * usb_register_dev() to enable that functionality. This function no longer\r
- * takes care of that.\r
- */\r
-int usb_register(struct usb_driver *new_driver)\r
-{\r
- int retval = 0;\r
-\r
- if (nousb)\r
- return -ENODEV;\r
-\r
- new_driver->driver.name = (char *)new_driver->name;\r
- new_driver->driver.bus = &usb_bus_type;\r
- new_driver->driver.probe = usb_device_probe;\r
- new_driver->driver.remove = usb_device_remove;\r
-\r
- init_MUTEX(&new_driver->serialize);\r
-\r
- retval = driver_register(&new_driver->driver);\r
-\r
- if (!retval) {\r
- info("registered new driver %s", new_driver->name);\r
- usbfs_update_special();\r
- } else {\r
- err("problem %d when registering driver %s",\r
- retval, new_driver->name);\r
- }\r
-\r
- return retval;\r
-}\r
-\r
-/**\r
- * usb_deregister - unregister a USB driver\r
- * @driver: USB operations of the driver to unregister\r
- * Context: !in_interrupt (), must be called with BKL held\r
- *\r
- * Unlinks the specified driver from the internal USB driver list.\r
- * \r
- * NOTE: If you called usb_register_dev(), you still need to call\r
- * usb_deregister_dev() to clean up your driver's allocated minor numbers,\r
- * this * call will no longer do it for you.\r
- */\r
-void usb_deregister(struct usb_driver *driver)\r
-{\r
- info("deregistering driver %s", driver->name);\r
-\r
- driver_unregister (&driver->driver);\r
-\r
- usbfs_update_special();\r
-}\r
-\r
-/**\r
- * usb_ifnum_to_if - get the interface object with a given interface number (usbcore-internal)\r
- * @dev: the device whose current configuration is considered\r
- * @ifnum: the desired interface\r
- *\r
- * This walks the device descriptor for the currently active configuration\r
- * and returns a pointer to the interface with that particular interface\r
- * number, or null.\r
- *\r
- * Note that configuration descriptors are not required to assign interface\r
- * numbers sequentially, so that it would be incorrect to assume that\r
- * the first interface in that descriptor corresponds to interface zero.\r
- * This routine helps device drivers avoid such mistakes.\r
- * However, you should make sure that you do the right thing with any\r
- * alternate settings available for this interfaces.\r
- */\r
-struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)\r
-{\r
- int i;\r
-\r
- for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++)\r
- if (dev->actconfig->interface[i].altsetting[0]\r
- .desc.bInterfaceNumber == ifnum)\r
- return &dev->actconfig->interface[i];\r
-\r
- return NULL;\r
-}\r
-\r
-/**\r
- * usb_epnum_to_ep_desc - get the endpoint object with a given endpoint number\r
- * @dev: the device whose current configuration is considered\r
- * @epnum: the desired endpoint\r
- *\r
- * This walks the device descriptor for the currently active configuration,\r
- * and returns a pointer to the endpoint with that particular endpoint\r
- * number, or null.\r
- *\r
- * Note that interface descriptors are not required to assign endpont\r
- * numbers sequentially, so that it would be incorrect to assume that\r
- * the first endpoint in that descriptor corresponds to interface zero.\r
- * This routine helps device drivers avoid such mistakes.\r
- */\r
-struct usb_endpoint_descriptor *\r
-usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum)\r
-{\r
- int i, j, k;\r
-\r
- for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++)\r
- for (j = 0; j < dev->actconfig->interface[i].num_altsetting; j++)\r
- for (k = 0; k < dev->actconfig->interface[i]\r
- .altsetting[j].desc.bNumEndpoints; k++)\r
- if (epnum == dev->actconfig->interface[i]\r
- .altsetting[j].endpoint[k]\r
- .desc.bEndpointAddress)\r
- return &dev->actconfig->interface[i]\r
- .altsetting[j].endpoint[k]\r
- .desc;\r
-\r
- return NULL;\r
-}\r
-\r
-/**\r
- * usb_driver_claim_interface - bind a driver to an interface\r
- * @driver: the driver to be bound\r
- * @iface: the interface to which it will be bound\r
- * @priv: driver data associated with that interface\r
- *\r
- * This is used by usb device drivers that need to claim more than one\r
- * interface on a device when probing (audio and acm are current examples).\r
- * No device driver should directly modify internal usb_interface or\r
- * usb_device structure members.\r
- *\r
- * Few drivers should need to use this routine, since the most natural\r
- * way to bind to an interface is to return the private data from\r
- * the driver's probe() method. Any driver that does use this must\r
- * first be sure that no other driver has claimed the interface, by\r
- * checking with usb_interface_claimed().\r
- */\r
-void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)\r
-{\r
- if (!iface || !driver)\r
- return;\r
-\r
- // FIXME change API to report an error in this case\r
- if (iface->driver)\r
- err ("%s driver booted %s off interface %p",\r
- driver->name, iface->driver->name, iface);\r
- else\r
- dbg("%s driver claimed interface %p", driver->name, iface);\r
-\r
- iface->driver = driver;\r
- usb_set_intfdata(iface, priv);\r
-}\r
-\r
-/**\r
- * usb_interface_claimed - returns true iff an interface is claimed\r
- * @iface: the interface being checked\r
- *\r
- * This should be used by drivers to check other interfaces to see if\r
- * they are available or not. If another driver has claimed the interface,\r
- * they may not claim it. Otherwise it's OK to claim it using\r
- * usb_driver_claim_interface().\r
- *\r
- * Returns true (nonzero) iff the interface is claimed, else false (zero).\r
- */\r
-int usb_interface_claimed(struct usb_interface *iface)\r
-{\r
- if (!iface)\r
- return 0;\r
-\r
- return (iface->driver != NULL);\r
-} /* usb_interface_claimed() */\r
-\r
-/**\r
- * usb_driver_release_interface - unbind a driver from an interface\r
- * @driver: the driver to be unbound\r
- * @iface: the interface from which it will be unbound\r
- * \r
- * This should be used by drivers to release their claimed interfaces.\r
- * It is normally called in their disconnect() methods, and only for\r
- * drivers that bound to more than one interface in their probe().\r
- *\r
- * When the USB subsystem disconnect()s a driver from some interface,\r
- * it automatically invokes this method for that interface. That\r
- * means that even drivers that used usb_driver_claim_interface()\r
- * usually won't need to call this.\r
- */\r
-void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)\r
-{\r
- /* this should never happen, don't release something that's not ours */\r
- if (!iface || iface->driver != driver)\r
- return;\r
-\r
- iface->driver = NULL;\r
- usb_set_intfdata(iface, NULL);\r
-}\r
-\r
-/**\r
- * usb_match_id - find first usb_device_id matching device or interface\r
- * @interface: the interface of interest\r
- * @id: array of usb_device_id structures, terminated by zero entry\r
- *\r
- * usb_match_id searches an array of usb_device_id's and returns\r
- * the first one matching the device or interface, or null.\r
- * This is used when binding (or rebinding) a driver to an interface.\r
- * Most USB device drivers will use this indirectly, through the usb core,\r
- * but some layered driver frameworks use it directly.\r
- * These device tables are exported with MODULE_DEVICE_TABLE, through\r
- * modutils and "modules.usbmap", to support the driver loading\r
- * functionality of USB hotplugging.\r
- *\r
- * What Matches:\r
- *\r
- * The "match_flags" element in a usb_device_id controls which\r
- * members are used. If the corresponding bit is set, the\r
- * value in the device_id must match its corresponding member\r
- * in the device or interface descriptor, or else the device_id\r
- * does not match.\r
- *\r
- * "driver_info" is normally used only by device drivers,\r
- * but you can create a wildcard "matches anything" usb_device_id\r
- * as a driver's "modules.usbmap" entry if you provide an id with\r
- * only a nonzero "driver_info" field. If you do this, the USB device\r
- * driver's probe() routine should use additional intelligence to\r
- * decide whether to bind to the specified interface.\r
- * \r
- * What Makes Good usb_device_id Tables:\r
- *\r
- * The match algorithm is very simple, so that intelligence in\r
- * driver selection must come from smart driver id records.\r
- * Unless you have good reasons to use another selection policy,\r
- * provide match elements only in related groups, and order match\r
- * specifiers from specific to general. Use the macros provided\r
- * for that purpose if you can.\r
- *\r
- * The most specific match specifiers use device descriptor\r
- * data. These are commonly used with product-specific matches;\r
- * the USB_DEVICE macro lets you provide vendor and product IDs,\r
- * and you can also match against ranges of product revisions.\r
- * These are widely used for devices with application or vendor\r
- * specific bDeviceClass values.\r
- *\r
- * Matches based on device class/subclass/protocol specifications\r
- * are slightly more general; use the USB_DEVICE_INFO macro, or\r
- * its siblings. These are used with single-function devices\r
- * where bDeviceClass doesn't specify that each interface has\r
- * its own class. \r
- *\r
- * Matches based on interface class/subclass/protocol are the\r
- * most general; they let drivers bind to any interface on a\r
- * multiple-function device. Use the USB_INTERFACE_INFO\r
- * macro, or its siblings, to match class-per-interface style \r
- * devices (as recorded in bDeviceClass).\r
- * \r
- * Within those groups, remember that not all combinations are\r
- * meaningful. For example, don't give a product version range\r
- * without vendor and product IDs; or specify a protocol without\r
- * its associated class and subclass.\r
- */ \r
-const struct usb_device_id *\r
-usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)\r
-{\r
- struct usb_host_interface *intf;\r
- struct usb_device *dev;\r
-\r
- /* proc_connectinfo in devio.c may call us with id == NULL. */\r
- if (id == NULL)\r
- return NULL;\r
-\r
- intf = &interface->altsetting [interface->act_altsetting];\r
- dev = interface_to_usbdev(interface);\r
-\r
- /* It is important to check that id->driver_info is nonzero,\r
- since an entry that is all zeroes except for a nonzero\r
- id->driver_info is the way to create an entry that\r
- indicates that the driver want to examine every\r
- device and interface. */\r
- for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||\r
- id->driver_info; id++) {\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&\r
- id->idVendor != dev->descriptor.idVendor)\r
- continue;\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&\r
- id->idProduct != dev->descriptor.idProduct)\r
- continue;\r
-\r
- /* No need to test id->bcdDevice_lo != 0, since 0 is never\r
- greater than any unsigned number. */\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&\r
- (id->bcdDevice_lo > dev->descriptor.bcdDevice))\r
- continue;\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&\r
- (id->bcdDevice_hi < dev->descriptor.bcdDevice))\r
- continue;\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&\r
- (id->bDeviceClass != dev->descriptor.bDeviceClass))\r
- continue;\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&\r
- (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))\r
- continue;\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&\r
- (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))\r
- continue;\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&\r
- (id->bInterfaceClass != intf->desc.bInterfaceClass))\r
- continue;\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&\r
- (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))\r
- continue;\r
-\r
- if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&\r
- (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))\r
- continue;\r
-\r
- return id;\r
- }\r
-\r
- return NULL;\r
-}\r
-\r
-/**\r
- * usb_find_interface - find usb_interface pointer for driver and device\r
- * @drv: the driver whose current configuration is considered\r
- * @minor: the minor number of the desired device\r
- *\r
- * This walks the driver device list and returns a pointer to the interface \r
- * with the matching minor. Note, this only works for devices that share the\r
- * USB major number.\r
- */\r
-struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)\r
-{\r
- struct list_head *entry;\r
- struct device *dev;\r
- struct usb_interface *intf;\r
-\r
- list_for_each(entry, &drv->driver.devices) {\r
- dev = container_of(entry, struct device, driver_list);\r
-\r
- /* can't look at usb devices, only interfaces */\r
- if (dev->driver == &usb_generic_driver)\r
- continue;\r
-\r
- intf = to_usb_interface(dev);\r
- if (intf->minor == -1)\r
- continue;\r
- if (intf->minor == minor)\r
- return intf;\r
- }\r
-\r
- /* no device found that matches */\r
- return NULL; \r
-}\r
-\r
-static int usb_device_match (struct device *dev, struct device_driver *drv)\r
-{\r
- struct usb_interface *intf;\r
- struct usb_driver *usb_drv;\r
- const struct usb_device_id *id;\r
-\r
- /* check for generic driver, which we don't match any device with */\r
- if (drv == &usb_generic_driver)\r
- return 0;\r
-\r
- intf = to_usb_interface(dev);\r
-\r
- usb_drv = to_usb_driver(drv);\r
- id = usb_drv->id_table;\r
- \r
- id = usb_match_id (intf, usb_drv->id_table);\r
- if (id)\r
- return 1;\r
-\r
- return 0;\r
-}\r
-\r
-\r
-#ifdef CONFIG_HOTPLUG\r
-\r
-/*\r
- * USB hotplugging invokes what /proc/sys/kernel/hotplug says\r
- * (normally /sbin/hotplug) when USB devices get added or removed.\r
- *\r
- * This invokes a user mode policy agent, typically helping to load driver\r
- * or other modules, configure the device, and more. Drivers can provide\r
- * a MODULE_DEVICE_TABLE to help with module loading subtasks.\r
- *\r
- * We're called either from khubd (the typical case) or from root hub\r
- * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle\r
- * delays in event delivery. Use sysfs (and DEVPATH) to make sure the\r
- * device (and this configuration!) are still present.\r
- */\r
-static int usb_hotplug (struct device *dev, char **envp, int num_envp,\r
- char *buffer, int buffer_size)\r
-{\r
- struct usb_interface *intf;\r
- struct usb_device *usb_dev;\r
- char *scratch;\r
- int i = 0;\r
- int length = 0;\r
-\r
- dbg ("%s", __FUNCTION__);\r
-\r
- if (!dev)\r
- return -ENODEV;\r
-\r
- /* Must check driver_data here, as on remove driver is always NULL */\r
- if ((dev->driver == &usb_generic_driver) || \r
- (dev->driver_data == &usb_generic_driver_data))\r
- return 0;\r
-\r
- intf = to_usb_interface(dev);\r
- usb_dev = interface_to_usbdev (intf);\r
- \r
- if (usb_dev->devnum < 0) {\r
- dbg ("device already deleted ??");\r
- return -ENODEV;\r
- }\r
- if (!usb_dev->bus) {\r
- dbg ("bus already removed?");\r
- return -ENODEV;\r
- }\r
-\r
- scratch = buffer;\r
-\r
-#ifdef CONFIG_USB_DEVICEFS\r
- /* If this is available, userspace programs can directly read\r
- * all the device descriptors we don't tell them about. Or\r
- * even act as usermode drivers.\r
- *\r
- * FIXME reduce hardwired intelligence here\r
- */\r
- envp [i++] = scratch;\r
- length += snprintf (scratch, buffer_size - length,\r
- "DEVICE=/proc/bus/usb/%03d/%03d",\r
- usb_dev->bus->busnum, usb_dev->devnum);\r
- if ((buffer_size - length <= 0) || (i >= num_envp))\r
- return -ENOMEM;\r
- ++length;\r
- scratch += length;\r
-#endif\r
-\r
- /* per-device configurations are common */\r
- envp [i++] = scratch;\r
- length += snprintf (scratch, buffer_size - length, "PRODUCT=%x/%x/%x",\r
- usb_dev->descriptor.idVendor,\r
- usb_dev->descriptor.idProduct,\r
- usb_dev->descriptor.bcdDevice);\r
- if ((buffer_size - length <= 0) || (i >= num_envp))\r
- return -ENOMEM;\r
- ++length;\r
- scratch += length;\r
-\r
- /* class-based driver binding models */\r
- envp [i++] = scratch;\r
- length += snprintf (scratch, buffer_size - length, "TYPE=%d/%d/%d",\r
- usb_dev->descriptor.bDeviceClass,\r
- usb_dev->descriptor.bDeviceSubClass,\r
- usb_dev->descriptor.bDeviceProtocol);\r
- if ((buffer_size - length <= 0) || (i >= num_envp))\r
- return -ENOMEM;\r
- ++length;\r
- scratch += length;\r
-\r
- if (usb_dev->descriptor.bDeviceClass == 0) {\r
- int alt = intf->act_altsetting;\r
-\r
- /* 2.4 only exposed interface zero. in 2.5, hotplug\r
- * agents are called for all interfaces, and can use\r
- * $DEVPATH/bInterfaceNumber if necessary.\r
- */\r
- envp [i++] = scratch;\r
- length += snprintf (scratch, buffer_size - length,\r
- "INTERFACE=%d/%d/%d",\r
- intf->altsetting[alt].desc.bInterfaceClass,\r
- intf->altsetting[alt].desc.bInterfaceSubClass,\r
- intf->altsetting[alt].desc.bInterfaceProtocol);\r
- if ((buffer_size - length <= 0) || (i >= num_envp))\r
- return -ENOMEM;\r
- ++length;\r
- scratch += length;\r
-\r
- }\r
- envp [i++] = 0;\r
-\r
- return 0;\r
-}\r
-\r
-#else\r
-\r
-static int usb_hotplug (struct device *dev, char **envp,\r
- int num_envp, char *buffer, int buffer_size)\r
-{\r
- return -ENODEV;\r
-}\r
-\r
-#endif /* CONFIG_HOTPLUG */\r
-\r
-/**\r
- * usb_alloc_dev - allocate a usb device structure (usbcore-internal)\r
- * @parent: hub to which device is connected\r
- * @bus: bus used to access the device\r
- * Context: !in_interrupt ()\r
- *\r
- * Only hub drivers (including virtual root hub drivers for host\r
- * controllers) should ever call this.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- */\r
-struct usb_device STDCALL *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)\r
-{\r
- struct usb_device *dev;\r
-\r
- dev = kmalloc(sizeof(*dev), GFP_KERNEL);\r
- if (!dev)\r
- return NULL;\r
-\r
- memset(dev, 0, sizeof(*dev));\r
-\r
- device_initialize(&dev->dev);\r
- dev->state = USB_STATE_ATTACHED;\r
-\r
- usb_bus_get(bus);\r
-\r
- if (!parent)\r
- dev->devpath [0] = '0';\r
- dev->bus = bus;\r
- dev->parent = parent;\r
- INIT_LIST_HEAD(&dev->filelist);\r
-\r
- init_MUTEX(&dev->serialize);\r
-\r
- if (dev->bus->op->allocate)\r
- dev->bus->op->allocate(dev);\r
-\r
- return dev;\r
-}\r
-\r
-/**\r
- * usb_get_dev - increments the reference count of the usb device structure\r
- * @dev: the device being referenced\r
- *\r
- * Each live reference to a device should be refcounted.\r
- *\r
- * Drivers for USB interfaces should normally record such references in\r
- * their probe() methods, when they bind to an interface, and release\r
- * them by calling usb_put_dev(), in their disconnect() methods.\r
- *\r
- * A pointer to the device with the incremented reference counter is returned.\r
- */\r
-struct usb_device STDCALL *usb_get_dev (struct usb_device *dev)\r
-{\r
- struct device *tmp;\r
-\r
- if (!dev)\r
- return NULL;\r
-\r
- tmp = get_device(&dev->dev);\r
- if (tmp) \r
- return to_usb_device(tmp);\r
- else\r
- return NULL;\r
-}\r
-\r
-/**\r
- * usb_put_dev - release a use of the usb device structure\r
- * @dev: device that's been disconnected\r
- *\r
- * Must be called when a user of a device is finished with it. When the last\r
- * user of the device calls this function, the memory of the device is freed.\r
- */\r
-void STDCALL usb_put_dev(struct usb_device *dev)\r
-{\r
- if (dev)\r
- put_device(&dev->dev);\r
-}\r
-\r
-/**\r
- * usb_release_dev - free a usb device structure when all users of it are finished.\r
- * @dev: device that's been disconnected\r
- *\r
- * Will be called only by the device core when all users of this usb device are\r
- * done.\r
- */\r
-static void usb_release_dev(struct device *dev)\r
-{\r
- struct usb_device *udev;\r
-\r
- udev = to_usb_device(dev);\r
-\r
- if (udev->bus && udev->bus->op && udev->bus->op->deallocate)\r
- udev->bus->op->deallocate(udev);\r
- usb_destroy_configuration (udev);\r
- usb_bus_put (udev->bus);\r
- kfree (udev);\r
-}\r
-\r
-\r
-static struct usb_device *match_device(struct usb_device *dev,\r
- u16 vendor_id, u16 product_id)\r
-{\r
- struct usb_device *ret_dev = NULL;\r
- int child;\r
-\r
- dbg("looking at vendor %d, product %d",\r
- dev->descriptor.idVendor,\r
- dev->descriptor.idProduct);\r
-\r
- /* see if this device matches */\r
- if ((dev->descriptor.idVendor == vendor_id) &&\r
- (dev->descriptor.idProduct == product_id)) {\r
- dbg ("found the device!");\r
- ret_dev = usb_get_dev(dev);\r
- goto exit;\r
- }\r
-\r
- /* look through all of the children of this device */\r
- for (child = 0; child < dev->maxchild; ++child) {\r
- if (dev->children[child]) {\r
- ret_dev = match_device(dev->children[child],\r
- vendor_id, product_id);\r
- if (ret_dev)\r
- goto exit;\r
- }\r
- }\r
-exit:\r
- return ret_dev;\r
-}\r
-\r
-/**\r
- * usb_find_device - find a specific usb device in the system\r
- * @vendor_id: the vendor id of the device to find\r
- * @product_id: the product id of the device to find\r
- *\r
- * Returns a pointer to a struct usb_device if such a specified usb\r
- * device is present in the system currently. The usage count of the\r
- * device will be incremented if a device is found. Make sure to call\r
- * usb_put_dev() when the caller is finished with the device.\r
- *\r
- * If a device with the specified vendor and product id is not found,\r
- * NULL is returned.\r
- */\r
-struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)\r
-{\r
- struct list_head *buslist;\r
- struct usb_bus *bus;\r
- struct usb_device *dev = NULL;\r
- \r
- down(&usb_bus_list_lock);\r
- for (buslist = usb_bus_list.next;\r
- buslist != &usb_bus_list; \r
- buslist = buslist->next) {\r
- bus = container_of(buslist, struct usb_bus, bus_list);\r
- dev = match_device(bus->root_hub, vendor_id, product_id);\r
- if (dev)\r
- goto exit;\r
- }\r
-exit:\r
- up(&usb_bus_list_lock);\r
- return dev;\r
-}\r
-\r
-/**\r
- * usb_get_current_frame_number - return current bus frame number\r
- * @dev: the device whose bus is being queried\r
- *\r
- * Returns the current frame number for the USB host controller\r
- * used with the given USB device. This can be used when scheduling\r
- * isochronous requests.\r
- *\r
- * Note that different kinds of host controller have different\r
- * "scheduling horizons". While one type might support scheduling only\r
- * 32 frames into the future, others could support scheduling up to\r
- * 1024 frames into the future.\r
- */\r
-int usb_get_current_frame_number(struct usb_device *dev)\r
-{\r
- return dev->bus->op->get_frame_number (dev);\r
-}\r
-\r
-/*-------------------------------------------------------------------*/\r
-/*\r
- * __usb_get_extra_descriptor() finds a descriptor of specific type in the\r
- * extra field of the interface and endpoint descriptor structs.\r
- */\r
-\r
-int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr)\r
-{\r
- struct usb_descriptor_header *header;\r
-\r
- while (size >= sizeof(struct usb_descriptor_header)) {\r
- header = (struct usb_descriptor_header *)buffer;\r
-\r
- if (header->bLength < 2) {\r
- err("invalid descriptor length of %d", header->bLength);\r
- return -1;\r
- }\r
-\r
- if (header->bDescriptorType == type) {\r
- *ptr = header;\r
- return 0;\r
- }\r
-\r
- buffer += header->bLength;\r
- size -= header->bLength;\r
- }\r
- return -1;\r
-}\r
-\r
-/**\r
- * usb_disconnect - disconnect a device (usbcore-internal)\r
- * @pdev: pointer to device being disconnected\r
- * Context: !in_interrupt ()\r
- *\r
- * Something got disconnected. Get rid of it, and all of its children.\r
- *\r
- * Only hub drivers (including virtual root hub drivers for host\r
- * controllers) should ever call this.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- */\r
-void usb_disconnect(struct usb_device **pdev)\r
-{\r
- struct usb_device *dev = *pdev;\r
- struct usb_bus *bus;\r
- struct usb_operations *ops;\r
- int i;\r
-\r
- might_sleep ();\r
-\r
- if (!dev) {\r
- pr_debug ("%s nodev\n", __FUNCTION__);\r
- return;\r
- }\r
- bus = dev->bus;\r
- if (!bus) {\r
- pr_debug ("%s nobus\n", __FUNCTION__);\r
- return;\r
- }\r
- ops = bus->op;\r
-\r
- *pdev = NULL;\r
-\r
- /* mark the device as inactive, so any further urb submissions for\r
- * this device will fail.\r
- */\r
- dev->state = USB_STATE_NOTATTACHED;\r
-\r
- dev_info (&dev->dev, "USB disconnect, address %d\n", dev->devnum);\r
-\r
- /* Free up all the children before we remove this device */\r
- for (i = 0; i < USB_MAXCHILDREN; i++) {\r
- struct usb_device **child = dev->children + i;\r
- if (*child)\r
- usb_disconnect(child);\r
- }\r
-\r
- /* disconnect() drivers from interfaces (a key side effect) */\r
- dev_dbg (&dev->dev, "unregistering interfaces\n");\r
- if (dev->actconfig) {\r
- for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {\r
- struct usb_interface *interface;\r
-\r
- /* remove this interface */\r
- interface = &dev->actconfig->interface[i];\r
- device_unregister(&interface->dev);\r
- }\r
- }\r
-\r
- /* deallocate hcd/hardware state */\r
- if (ops->disable) {\r
- void (*disable)(struct usb_device *, int) = ops->disable;\r
-\r
- for (i = 0; i < 15; i++) {\r
- disable (dev, i);\r
- disable (dev, USB_DIR_IN | i);\r
- }\r
- }\r
-\r
- dev_dbg (&dev->dev, "unregistering device\n");\r
- /* Free the device number and remove the /proc/bus/usb entry */\r
- if (dev->devnum > 0) {\r
- clear_bit(dev->devnum, dev->bus->devmap.devicemap);\r
- usbfs_remove_device(dev);\r
- }\r
- device_unregister(&dev->dev);\r
-\r
- /* Decrement the reference count, it'll auto free everything when */\r
- /* it hits 0 which could very well be now */\r
- usb_put_dev(dev);\r
-}\r
-\r
-/**\r
- * usb_connect - pick device address (usbcore-internal)\r
- * @dev: newly detected device (in DEFAULT state)\r
- *\r
- * Picks a device address. It's up to the hub (or root hub) driver\r
- * to handle and manage enumeration, starting from the DEFAULT state.\r
- * Only hub drivers (including virtual root hub drivers for host\r
- * controllers) should ever call this.\r
- */\r
-void STDCALL usb_connect(struct usb_device *dev)\r
-{\r
- int devnum;\r
- // FIXME needs locking for SMP!!\r
- /* why? this is called only from the hub thread, \r
- * which hopefully doesn't run on multiple CPU's simultaneously 8-)\r
- * ... it's also called from modprobe/rmmod/apmd threads as part\r
- * of virtual root hub init/reinit. In the init case, the hub code \r
- * won't have seen this, but not so for reinit ... \r
- */\r
- dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */\r
-\r
- /* Try to allocate the next devnum beginning at bus->devnum_next. */\r
- devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, dev->bus->devnum_next);\r
- if (devnum >= 128)\r
- devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);\r
-\r
- dev->bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);\r
-\r
- if (devnum < 128) {\r
- set_bit(devnum, dev->bus->devmap.devicemap);\r
- dev->devnum = devnum;\r
- }\r
-}\r
-\r
-\r
-// hub-only!! ... and only exported for reset/reinit path.\r
-// otherwise used internally, for usb_new_device()\r
-int usb_set_address(struct usb_device *dev)\r
-{\r
- int retval;\r
-\r
- if (dev->devnum == 0)\r
- return -EINVAL;\r
- if (dev->state != USB_STATE_DEFAULT && dev->state != USB_STATE_ADDRESS)\r
- return -EINVAL;\r
- retval = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,\r
- 0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);\r
- if (retval == 0)\r
- dev->state = USB_STATE_ADDRESS;\r
- return retval;\r
-}\r
-\r
-\r
-/* improve on the default device description, if we can ... and\r
- * while we're at it, maybe show the vendor and product strings.\r
- */\r
-static void set_device_description (struct usb_device *dev)\r
-{\r
- void *buf;\r
- int mfgr = dev->descriptor.iManufacturer;\r
- int prod = dev->descriptor.iProduct;\r
- int vendor_id = dev->descriptor.idVendor;\r
- int product_id = dev->descriptor.idProduct;\r
- char *mfgr_str, *prod_str;\r
-\r
- /* set default; keep it if there are no strings, or kmalloc fails */\r
- sprintf (dev->dev.name, "USB device %04x:%04x",\r
- vendor_id, product_id);\r
-\r
- if (!(buf = kmalloc(256 * 2, GFP_KERNEL)))\r
- return;\r
- \r
- prod_str = (char *) buf;\r
- mfgr_str = (char *) buf + 256;\r
-\r
- if (prod && usb_string (dev, prod, prod_str, 256) > 0) {\r
-#ifdef DEBUG\r
- dev_printk (KERN_INFO, &dev->dev, "Product: %s\n", prod_str);\r
-#endif\r
- } else {\r
- prod_str = 0;\r
- }\r
-\r
- if (mfgr && usb_string (dev, mfgr, mfgr_str, 256) > 0) {\r
-#ifdef DEBUG\r
- dev_printk (KERN_INFO, &dev->dev, "Manufacturer: %s\n", mfgr_str);\r
-#endif\r
- } else {\r
- mfgr_str = 0;\r
- }\r
-\r
- /* much like pci ... describe as either:\r
- * - both strings: 'product descr (vendor descr)'\r
- * - product only: 'product descr (USB device vvvv:pppp)'\r
- * - vendor only: 'USB device vvvv:pppp (vendor descr)'\r
- * - neither string: 'USB device vvvv:pppp'\r
- */\r
-\r
- if (prod_str && mfgr_str) {\r
-\r
- snprintf(dev->dev.name, sizeof dev->dev.name,\r
- "%s (%s)", prod_str, mfgr_str);\r
- } else if (prod_str) {\r
- snprintf(dev->dev.name, sizeof dev->dev.name,\r
- "%s (USB device %04x:%04x)",\r
- prod_str, vendor_id, product_id);\r
-\r
- } else if (mfgr_str) {\r
- snprintf(dev->dev.name, sizeof dev->dev.name,\r
- "USB device %04x:%04x (%s)",\r
- vendor_id, product_id, mfgr_str);\r
- }\r
- usbprintk("USB connected: %s\n",dev->dev.name);\r
- kfree(buf);\r
-}\r
-\r
-/*\r
- * By the time we get here, we chose a new device address\r
- * and is in the default state. We need to identify the thing and\r
- * get the ball rolling..\r
- *\r
- * Returns 0 for success, != 0 for error.\r
- *\r
- * This call is synchronous, and may not be used in an interrupt context.\r
- *\r
- * Only hub drivers (including virtual root hub drivers for host\r
- * controllers) should ever call this.\r
- */\r
-#define NEW_DEVICE_RETRYS 2\r
-#define SET_ADDRESS_RETRYS 2\r
-int usb_new_device(struct usb_device *dev, struct device *parent)\r
-{\r
- int err = 0;\r
- int i;\r
- int j;\r
-\r
- /*\r
- * Set the driver for the usb device to point to the "generic" driver.\r
- * This prevents the main usb device from being sent to the usb bus\r
- * probe function. Yes, it's a hack, but a nice one :)\r
- *\r
- * Do it asap, so more driver model stuff (like the device.h message\r
- * utilities) can be used in hcd submit/unlink code paths.\r
- */\r
- usb_generic_driver.bus = &usb_bus_type;\r
- dev->dev.parent = parent;\r
- dev->dev.driver = &usb_generic_driver;\r
- dev->dev.bus = &usb_bus_type;\r
- dev->dev.release = usb_release_dev;\r
- dev->dev.driver_data = &usb_generic_driver_data;\r
- usb_get_dev(dev);\r
- if (dev->dev.bus_id[0] == 0)\r
- sprintf (&dev->dev.bus_id[0], "%d-%s",\r
- dev->bus->busnum, dev->devpath);\r
-\r
- /* dma masks come from the controller; readonly, except to hcd */\r
- dev->dev.dma_mask = parent->dma_mask;\r
-\r
- /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...\r
- * it's fixed size except for full speed devices.\r
- */\r
- switch (dev->speed) {\r
- case USB_SPEED_HIGH: /* fixed at 64 */\r
- i = 64;\r
- break;\r
- case USB_SPEED_FULL: /* 8, 16, 32, or 64 */\r
- /* to determine the ep0 maxpacket size, read the first 8\r
- * bytes from the device descriptor to get bMaxPacketSize0;\r
- * then correct our initial (small) guess.\r
- */\r
- // FALLTHROUGH\r
- case USB_SPEED_LOW: /* fixed at 8 */\r
- i = 8;\r
- break;\r
- default:\r
- return -EINVAL;\r
- }\r
- dev->epmaxpacketin [0] = i;\r
- dev->epmaxpacketout[0] = i;\r
-\r
- for (i = 0; i < NEW_DEVICE_RETRYS; ++i) {\r
-\r
- for (j = 0; j < SET_ADDRESS_RETRYS; ++j) {\r
- err = usb_set_address(dev);\r
- if (err >= 0)\r
- break;\r
- wait_ms(200);\r
- }\r
- if (err < 0) {\r
- dev_err(&dev->dev, "USB device not accepting new address=%d (error=%d)\n",\r
- dev->devnum, err);\r
- dev->state = USB_STATE_DEFAULT;\r
- clear_bit(dev->devnum, dev->bus->devmap.devicemap);\r
- dev->devnum = -1;\r
- return 1;\r
- }\r
-\r
- wait_ms(10); /* Let the SET_ADDRESS settle */\r
-\r
- /* high and low speed devices don't need this... */\r
- err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);\r
- if (err >= 8)\r
- break;\r
- wait_ms(100);\r
- }\r
-\r
- if (err < 8) {\r
- if (err < 0)\r
- dev_err(&dev->dev, "USB device not responding, giving up (error=%d)\n", err);\r
- else\r
- dev_err(&dev->dev, "USB device descriptor short read (expected %i, got %i)\n", 8, err);\r
- clear_bit(dev->devnum, dev->bus->devmap.devicemap);\r
- dev->devnum = -1;\r
- return 1;\r
- }\r
- if (dev->speed == USB_SPEED_FULL) {\r
- dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;\r
- dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;\r
- }\r
-\r
- /* USB device state == addressed ... still not usable */\r
-\r
- err = usb_get_device_descriptor(dev);\r
- if (err < (signed)sizeof(dev->descriptor)) {\r
- if (err < 0)\r
- dev_err(&dev->dev, "unable to get device descriptor (error=%d)\n", err);\r
- else\r
- dev_err(&dev->dev, "USB device descriptor short read (expected %Zi, got %i)\n",\r
- sizeof(dev->descriptor), err);\r
- \r
- clear_bit(dev->devnum, dev->bus->devmap.devicemap);\r
- dev->devnum = -1;\r
- return 1;\r
- }\r
-\r
- err = usb_get_configuration(dev);\r
- if (err < 0) {\r
- dev_err(&dev->dev, "unable to get device %d configuration (error=%d)\n",\r
- dev->devnum, err);\r
- clear_bit(dev->devnum, dev->bus->devmap.devicemap);\r
- dev->devnum = -1;\r
- return 1;\r
- }\r
-\r
- /* we set the default configuration here */\r
- err = usb_set_configuration(dev, dev->config[0].desc.bConfigurationValue);\r
- if (err) {\r
- dev_err(&dev->dev, "failed to set device %d default configuration (error=%d)\n",\r
- dev->devnum, err);\r
- clear_bit(dev->devnum, dev->bus->devmap.devicemap);\r
- dev->devnum = -1;\r
- return 1;\r
- }\r
-\r
- /* USB device state == configured ... tell the world! */\r
-\r
- dev_dbg(&dev->dev, "new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",\r
- dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);\r
- set_device_description (dev);\r
-\r
-#ifdef DEBUG\r
- if (dev->descriptor.iSerialNumber)\r
- usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);\r
-#endif\r
- /* put into sysfs, with device and config specific files */\r
- err = device_add (&dev->dev);\r
- if (err)\r
- return err;\r
- usb_create_driverfs_dev_files (dev);\r
-\r
- /* Register all of the interfaces for this device with the driver core.\r
- * Remember, interfaces get bound to drivers, not devices. */\r
- for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {\r
- struct usb_interface *interface = &dev->actconfig->interface[i];\r
- struct usb_interface_descriptor *desc;\r
-\r
- desc = &interface->altsetting [interface->act_altsetting].desc;\r
- interface->dev.parent = &dev->dev;\r
- interface->dev.driver = NULL;\r
- interface->dev.bus = &usb_bus_type;\r
- interface->dev.dma_mask = parent->dma_mask;\r
- sprintf (&interface->dev.bus_id[0], "%d-%s:%d",\r
- dev->bus->busnum, dev->devpath,\r
- desc->bInterfaceNumber);\r
- if (!desc->iInterface\r
- || usb_string (dev, desc->iInterface,\r
- interface->dev.name,\r
- sizeof interface->dev.name) <= 0) {\r
- /* typically devices won't bother with interface\r
- * descriptions; this is the normal case. an\r
- * interface's driver might describe it better.\r
- * (also: iInterface is per-altsetting ...)\r
- */\r
- sprintf (&interface->dev.name[0],\r
- "usb-%s-%s interface %d",\r
- dev->bus->bus_name, dev->devpath,\r
- desc->bInterfaceNumber);\r
- DPRINT1("usb_new_device: %s\n", interface->dev.name);\r
- }\r
- dev_dbg (&dev->dev, "%s - registering interface %s\n", __FUNCTION__, interface->dev.bus_id);\r
- device_add (&interface->dev);\r
- usb_create_driverfs_intf_files (interface);\r
- }\r
- /* add a /proc/bus/usb entry */\r
- usbfs_add_device(dev);\r
-\r
- return 0;\r
-}\r
-\r
-/**\r
- * usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_DMA_MAP\r
- * @dev: device the buffer will be used with\r
- * @size: requested buffer size\r
- * @mem_flags: affect whether allocation may block\r
- * @dma: used to return DMA address of buffer\r
- *\r
- * Return value is either null (indicating no buffer could be allocated), or\r
- * the cpu-space pointer to a buffer that may be used to perform DMA to the\r
- * specified device. Such cpu-space buffers are returned along with the DMA\r
- * address (through the pointer provided).\r
- *\r
- * These buffers are used with URB_NO_DMA_MAP set in urb->transfer_flags to\r
- * avoid behaviors like using "DMA bounce buffers", or tying down I/O mapping\r
- * hardware for long idle periods. The implementation varies between\r
- * platforms, depending on details of how DMA will work to this device.\r
- * Using these buffers also helps prevent cacheline sharing problems on\r
- * architectures where CPU caches are not DMA-coherent.\r
- *\r
- * When the buffer is no longer used, free it with usb_buffer_free().\r
- */\r
-void *usb_buffer_alloc (\r
- struct usb_device *dev,\r
- size_t size,\r
- int mem_flags,\r
- dma_addr_t *dma\r
-)\r
-{\r
- if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)\r
- return 0;\r
- return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);\r
-}\r
-\r
-/**\r
- * usb_buffer_free - free memory allocated with usb_buffer_alloc()\r
- * @dev: device the buffer was used with\r
- * @size: requested buffer size\r
- * @addr: CPU address of buffer\r
- * @dma: DMA address of buffer\r
- *\r
- * This reclaims an I/O buffer, letting it be reused. The memory must have\r
- * been allocated using usb_buffer_alloc(), and the parameters must match\r
- * those provided in that allocation request. \r
- */\r
-void usb_buffer_free (\r
- struct usb_device *dev,\r
- size_t size,\r
- void *addr,\r
- dma_addr_t dma\r
-)\r
-{\r
- if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)\r
- return;\r
- dev->bus->op->buffer_free (dev->bus, size, addr, dma);\r
-}\r
-\r
-/**\r
- * usb_buffer_map - create DMA mapping(s) for an urb\r
- * @urb: urb whose transfer_buffer will be mapped\r
- *\r
- * Return value is either null (indicating no buffer could be mapped), or\r
- * the parameter. URB_NO_DMA_MAP is added to urb->transfer_flags if the\r
- * operation succeeds. If the device is connected to this system through\r
- * a non-DMA controller, this operation always succeeds.\r
- *\r
- * This call would normally be used for an urb which is reused, perhaps\r
- * as the target of a large periodic transfer, with usb_buffer_dmasync()\r
- * calls to synchronize memory and dma state. It may not be used for\r
- * control requests.\r
- *\r
- * Reverse the effect of this call with usb_buffer_unmap().\r
- */\r
-struct urb *usb_buffer_map (struct urb *urb)\r
-{\r
- struct usb_bus *bus;\r
- struct device *controller;\r
-\r
- if (!urb\r
- || usb_pipecontrol (urb->pipe)\r
- || !urb->dev\r
- || !(bus = urb->dev->bus)\r
- || !(controller = bus->controller))\r
- return 0;\r
-\r
- if (controller->dma_mask) {\r
- urb->transfer_dma = dma_map_single (controller,\r
- urb->transfer_buffer, urb->transfer_buffer_length,\r
- usb_pipein (urb->pipe)\r
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);\r
- // FIXME generic api broken like pci, can't report errors\r
- // if (urb->transfer_dma == DMA_ADDR_INVALID) return 0;\r
- } else\r
- urb->transfer_dma = ~0;\r
- urb->transfer_flags |= URB_NO_DMA_MAP;\r
- return urb;\r
-}\r
-\r
-/**\r
- * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s)\r
- * @urb: urb whose transfer_buffer will be synchronized\r
- */\r
-void usb_buffer_dmasync (struct urb *urb)\r
-{\r
- struct usb_bus *bus;\r
- struct device *controller;\r
-\r
- if (!urb\r
- || !(urb->transfer_flags & URB_NO_DMA_MAP)\r
- || !urb->dev\r
- || !(bus = urb->dev->bus)\r
- || !(controller = bus->controller))\r
- return;\r
-\r
- if (controller->dma_mask)\r
- dma_sync_single (controller,\r
- urb->transfer_dma, urb->transfer_buffer_length,\r
- usb_pipein (urb->pipe)\r
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);\r
-}\r
-\r
-/**\r
- * usb_buffer_unmap - free DMA mapping(s) for an urb\r
- * @urb: urb whose transfer_buffer will be unmapped\r
- *\r
- * Reverses the effect of usb_buffer_map().\r
- */\r
-void usb_buffer_unmap (struct urb *urb)\r
-{\r
- struct usb_bus *bus;\r
- struct device *controller;\r
-\r
- if (!urb\r
- || !(urb->transfer_flags & URB_NO_DMA_MAP)\r
- || !urb->dev\r
- || !(bus = urb->dev->bus)\r
- || !(controller = bus->controller))\r
- return;\r
-\r
- if (controller->dma_mask)\r
- dma_unmap_single (controller,\r
- urb->transfer_dma, urb->transfer_buffer_length,\r
- usb_pipein (urb->pipe)\r
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE);\r
- urb->transfer_flags &= ~URB_NO_DMA_MAP;\r
-}\r
-\r
-/**\r
- * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint\r
- * @dev: device to which the scatterlist will be mapped\r
- * @pipe: endpoint defining the mapping direction\r
- * @sg: the scatterlist to map\r
- * @nents: the number of entries in the scatterlist\r
- *\r
- * Return value is either < 0 (indicating no buffers could be mapped), or\r
- * the number of DMA mapping array entries in the scatterlist.\r
- *\r
- * The caller is responsible for placing the resulting DMA addresses from\r
- * the scatterlist into URB transfer buffer pointers, and for setting the\r
- * URB_NO_DMA_MAP transfer flag in each of those URBs.\r
- *\r
- * Top I/O rates come from queuing URBs, instead of waiting for each one\r
- * to complete before starting the next I/O. This is particularly easy\r
- * to do with scatterlists. Just allocate and submit one URB for each DMA\r
- * mapping entry returned, stopping on the first error or when all succeed.\r
- * Better yet, use the usb_sg_*() calls, which do that (and more) for you.\r
- *\r
- * This call would normally be used when translating scatterlist requests,\r
- * rather than usb_buffer_map(), since on some hardware (with IOMMUs) it\r
- * may be able to coalesce mappings for improved I/O efficiency.\r
- *\r
- * Reverse the effect of this call with usb_buffer_unmap_sg().\r
- */\r
-int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,\r
- struct scatterlist *sg, int nents)\r
-{\r
- struct usb_bus *bus;\r
- struct device *controller;\r
-\r
- if (!dev\r
- || usb_pipecontrol (pipe)\r
- || !(bus = dev->bus)\r
- || !(controller = bus->controller)\r
- || !controller->dma_mask)\r
- return -1;\r
-\r
- // FIXME generic api broken like pci, can't report errors\r
- return dma_map_sg (controller, sg, nents,\r
- usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);\r
-}\r
-\r
-/**\r
- * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)\r
- * @dev: device to which the scatterlist will be mapped\r
- * @pipe: endpoint defining the mapping direction\r
- * @sg: the scatterlist to synchronize\r
- * @n_hw_ents: the positive return value from usb_buffer_map_sg\r
- *\r
- * Use this when you are re-using a scatterlist's data buffers for\r
- * another USB request.\r
- */\r
-void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,\r
- struct scatterlist *sg, int n_hw_ents)\r
-{\r
- struct usb_bus *bus;\r
- struct device *controller;\r
-\r
- if (!dev\r
- || !(bus = dev->bus)\r
- || !(controller = bus->controller)\r
- || !controller->dma_mask)\r
- return;\r
-\r
- dma_sync_sg (controller, sg, n_hw_ents,\r
- usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);\r
-}\r
-\r
-/**\r
- * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist\r
- * @dev: device to which the scatterlist will be mapped\r
- * @pipe: endpoint defining the mapping direction\r
- * @sg: the scatterlist to unmap\r
- * @n_hw_ents: the positive return value from usb_buffer_map_sg\r
- *\r
- * Reverses the effect of usb_buffer_map_sg().\r
- */\r
-void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,\r
- struct scatterlist *sg, int n_hw_ents)\r
-{\r
- struct usb_bus *bus;\r
- struct device *controller;\r
-\r
- if (!dev\r
- || !(bus = dev->bus)\r
- || !(controller = bus->controller)\r
- || !controller->dma_mask)\r
- return;\r
-\r
- dma_unmap_sg (controller, sg, n_hw_ents,\r
- usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);\r
-}\r
-\r
-\r
-struct bus_type usb_bus_type = {\r
- .name = "usb",\r
- .match = usb_device_match,\r
- .hotplug = usb_hotplug,\r
-};\r
-\r
-#ifndef MODULE\r
-\r
-static int __init usb_setup_disable(char *str)\r
-{\r
- nousb = 1;\r
- return 1;\r
-}\r
-\r
-/* format to disable USB on kernel command line is: nousb */\r
-__setup("nousb", usb_setup_disable);\r
-\r
-#endif\r
-\r
-/*\r
- * for external read access to <nousb>\r
- */\r
-int STDCALL usb_disabled(void)\r
-{\r
- return nousb;\r
-}\r
-\r
-/*\r
- * Init\r
- */\r
-int STDCALL __init usb_init(void)\r
-{\r
- if (nousb) {\r
- info("USB support disabled\n");\r
- return 0;\r
- }\r
-\r
- bus_register(&usb_bus_type);\r
- usb_major_init();\r
- usbfs_init();\r
- usb_hub_init();\r
-\r
- driver_register(&usb_generic_driver);\r
-\r
- return 0;\r
-}\r
-\r
-/*\r
- * Cleanup\r
- */\r
-void STDCALL __exit usb_exit(void)\r
-{\r
- /* This will matter if shutdown/reboot does exitcalls. */\r
- if (nousb)\r
- return;\r
-\r
- driver_unregister(&usb_generic_driver);\r
- usb_major_cleanup();\r
- usbfs_cleanup();\r
- usb_hub_cleanup();\r
- bus_unregister(&usb_bus_type);\r
-}\r
-\r
-subsys_initcall(usb_init);\r
-module_exit(usb_exit);\r
-\r
-/*\r
- * USB may be built into the kernel or be built as modules.\r
- * These symbols are exported for device (or host controller)\r
- * driver modules to use.\r
- */\r
-EXPORT_SYMBOL(usb_epnum_to_ep_desc);\r
-\r
-EXPORT_SYMBOL(usb_register);\r
-EXPORT_SYMBOL(usb_deregister);\r
-EXPORT_SYMBOL(usb_disabled);\r
-\r
-EXPORT_SYMBOL(usb_device_probe);\r
-EXPORT_SYMBOL(usb_device_remove);\r
-\r
-EXPORT_SYMBOL(usb_alloc_dev);\r
-EXPORT_SYMBOL(usb_put_dev);\r
-EXPORT_SYMBOL(usb_get_dev);\r
-EXPORT_SYMBOL(usb_hub_tt_clear_buffer);\r
-\r
-EXPORT_SYMBOL(usb_driver_claim_interface);\r
-EXPORT_SYMBOL(usb_interface_claimed);\r
-EXPORT_SYMBOL(usb_driver_release_interface);\r
-EXPORT_SYMBOL(usb_match_id);\r
-EXPORT_SYMBOL(usb_find_interface);\r
-EXPORT_SYMBOL(usb_ifnum_to_if);\r
-\r
-EXPORT_SYMBOL(usb_new_device);\r
-EXPORT_SYMBOL(usb_reset_device);\r
-EXPORT_SYMBOL(usb_connect);\r
-EXPORT_SYMBOL(usb_disconnect);\r
-\r
-EXPORT_SYMBOL(__usb_get_extra_descriptor);\r
-\r
-EXPORT_SYMBOL(usb_find_device);\r
-EXPORT_SYMBOL(usb_get_current_frame_number);\r
-\r
-EXPORT_SYMBOL (usb_buffer_alloc);\r
-EXPORT_SYMBOL (usb_buffer_free);\r
-\r
-EXPORT_SYMBOL (usb_buffer_map);\r
-EXPORT_SYMBOL (usb_buffer_dmasync);\r
-EXPORT_SYMBOL (usb_buffer_unmap);\r
-\r
-EXPORT_SYMBOL (usb_buffer_map_sg);\r
-EXPORT_SYMBOL (usb_buffer_dmasync_sg);\r
-EXPORT_SYMBOL (usb_buffer_unmap_sg);\r
-\r
-MODULE_LICENSE("GPL");\r
+/*
+ * drivers/usb/usb.c
+ *
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
+ more docs, etc)
+ * (C) Copyright Yggdrasil Computing, Inc. 2000
+ * (usb_device_id matching changes by Adam J. Richter)
+ * (C) Copyright Greg Kroah-Hartman 2002-2003
+ *
+ * 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.
+ */
+
+#if 0
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h> /* for in_interrupt() */
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include "hcd.h"
+#include "usb.h"
+#else
+#include "../usb_wrapper.h"
+#include "hcd.h"
+#endif
+
+
+extern int usb_hub_init(void);
+extern void usb_hub_cleanup(void);
+extern int usb_major_init(void);
+extern void usb_major_cleanup(void);
+
+
+int nousb; /* Disable USB when built into kernel image */
+ /* Not honored on modular build */
+
+
+static int generic_probe (struct device *dev)
+{
+ return 0;
+}
+static int generic_remove (struct device *dev)
+{
+ return 0;
+}
+
+static struct device_driver usb_generic_driver = {
+ .name = "usb",
+ .bus = &usb_bus_type,
+ .probe = generic_probe,
+ .remove = generic_remove,
+};
+
+static int usb_generic_driver_data;
+
+/* needs to be called with BKL held */
+int usb_device_probe(struct device *dev)
+{
+ struct usb_interface * intf = to_usb_interface(dev);
+ struct usb_driver * driver = to_usb_driver(dev->driver);
+ const struct usb_device_id *id;
+ int error = -ENODEV;
+
+ dev_dbg(dev, "%s\n", __FUNCTION__);
+
+ if (!driver->probe)
+ return error;
+
+ id = usb_match_id (intf, driver->id_table);
+ if (id) {
+ dev_dbg (dev, "%s - got id\n", __FUNCTION__);
+ down (&driver->serialize);
+ error = driver->probe (intf, id);
+ up (&driver->serialize);
+ }
+ if (!error)
+ intf->driver = driver;
+
+ return error;
+}
+
+int usb_device_remove(struct device *dev)
+{
+ struct usb_interface *intf;
+ struct usb_driver *driver;
+
+ intf = list_entry(dev,struct usb_interface,dev);
+ driver = to_usb_driver(dev->driver);
+
+ down(&driver->serialize);
+
+ if (intf->driver && intf->driver->disconnect)
+ intf->driver->disconnect(intf);
+
+ /* if driver->disconnect didn't release the interface */
+ if (intf->driver)
+ usb_driver_release_interface(driver, intf);
+
+ up(&driver->serialize);
+
+ return 0;
+}
+
+/**
+ * usb_register - register a USB driver
+ * @new_driver: USB operations for the driver
+ *
+ * Registers a USB driver with the USB core. The list of unattached
+ * interfaces will be rescanned whenever a new driver is added, allowing
+ * the new driver to attach to any recognized devices.
+ * Returns a negative error code on failure and 0 on success.
+ *
+ * NOTE: if you want your driver to use the USB major number, you must call
+ * usb_register_dev() to enable that functionality. This function no longer
+ * takes care of that.
+ */
+int usb_register(struct usb_driver *new_driver)
+{
+ int retval = 0;
+
+ if (nousb)
+ return -ENODEV;
+
+ new_driver->driver.name = (char *)new_driver->name;
+ new_driver->driver.bus = &usb_bus_type;
+ new_driver->driver.probe = usb_device_probe;
+ new_driver->driver.remove = usb_device_remove;
+
+ init_MUTEX(&new_driver->serialize);
+
+ retval = driver_register(&new_driver->driver);
+
+ if (!retval) {
+ info("registered new driver %s", new_driver->name);
+ usbfs_update_special();
+ } else {
+ err("problem %d when registering driver %s",
+ retval, new_driver->name);
+ }
+
+ return retval;
+}
+
+/**
+ * usb_deregister - unregister a USB driver
+ * @driver: USB operations of the driver to unregister
+ * Context: !in_interrupt (), must be called with BKL held
+ *
+ * Unlinks the specified driver from the internal USB driver list.
+ *
+ * NOTE: If you called usb_register_dev(), you still need to call
+ * usb_deregister_dev() to clean up your driver's allocated minor numbers,
+ * this * call will no longer do it for you.
+ */
+void usb_deregister(struct usb_driver *driver)
+{
+ info("deregistering driver %s", driver->name);
+
+ driver_unregister (&driver->driver);
+
+ usbfs_update_special();
+}
+
+/**
+ * usb_ifnum_to_if - get the interface object with a given interface number (usbcore-internal)
+ * @dev: the device whose current configuration is considered
+ * @ifnum: the desired interface
+ *
+ * This walks the device descriptor for the currently active configuration
+ * and returns a pointer to the interface with that particular interface
+ * number, or null.
+ *
+ * Note that configuration descriptors are not required to assign interface
+ * numbers sequentially, so that it would be incorrect to assume that
+ * the first interface in that descriptor corresponds to interface zero.
+ * This routine helps device drivers avoid such mistakes.
+ * However, you should make sure that you do the right thing with any
+ * alternate settings available for this interfaces.
+ */
+struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+{
+ int i;
+
+ for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++)
+ if (dev->actconfig->interface[i].altsetting[0]
+ .desc.bInterfaceNumber == ifnum)
+ return &dev->actconfig->interface[i];
+
+ return NULL;
+}
+
+/**
+ * usb_epnum_to_ep_desc - get the endpoint object with a given endpoint number
+ * @dev: the device whose current configuration is considered
+ * @epnum: the desired endpoint
+ *
+ * This walks the device descriptor for the currently active configuration,
+ * and returns a pointer to the endpoint with that particular endpoint
+ * number, or null.
+ *
+ * Note that interface descriptors are not required to assign endpont
+ * numbers sequentially, so that it would be incorrect to assume that
+ * the first endpoint in that descriptor corresponds to interface zero.
+ * This routine helps device drivers avoid such mistakes.
+ */
+struct usb_endpoint_descriptor *
+usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum)
+{
+ int i, j, k;
+
+ for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++)
+ for (j = 0; j < dev->actconfig->interface[i].num_altsetting; j++)
+ for (k = 0; k < dev->actconfig->interface[i]
+ .altsetting[j].desc.bNumEndpoints; k++)
+ if (epnum == dev->actconfig->interface[i]
+ .altsetting[j].endpoint[k]
+ .desc.bEndpointAddress)
+ return &dev->actconfig->interface[i]
+ .altsetting[j].endpoint[k]
+ .desc;
+
+ return NULL;
+}
+
+/**
+ * usb_driver_claim_interface - bind a driver to an interface
+ * @driver: the driver to be bound
+ * @iface: the interface to which it will be bound
+ * @priv: driver data associated with that interface
+ *
+ * This is used by usb device drivers that need to claim more than one
+ * interface on a device when probing (audio and acm are current examples).
+ * No device driver should directly modify internal usb_interface or
+ * usb_device structure members.
+ *
+ * Few drivers should need to use this routine, since the most natural
+ * way to bind to an interface is to return the private data from
+ * the driver's probe() method. Any driver that does use this must
+ * first be sure that no other driver has claimed the interface, by
+ * checking with usb_interface_claimed().
+ */
+void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv)
+{
+ if (!iface || !driver)
+ return;
+
+ // FIXME change API to report an error in this case
+ if (iface->driver)
+ err ("%s driver booted %s off interface %p",
+ driver->name, iface->driver->name, iface);
+ else
+ dbg("%s driver claimed interface %p", driver->name, iface);
+
+ iface->driver = driver;
+ usb_set_intfdata(iface, priv);
+}
+
+/**
+ * usb_interface_claimed - returns true iff an interface is claimed
+ * @iface: the interface being checked
+ *
+ * This should be used by drivers to check other interfaces to see if
+ * they are available or not. If another driver has claimed the interface,
+ * they may not claim it. Otherwise it's OK to claim it using
+ * usb_driver_claim_interface().
+ *
+ * Returns true (nonzero) iff the interface is claimed, else false (zero).
+ */
+int usb_interface_claimed(struct usb_interface *iface)
+{
+ if (!iface)
+ return 0;
+
+ return (iface->driver != NULL);
+} /* usb_interface_claimed() */
+
+/**
+ * usb_driver_release_interface - unbind a driver from an interface
+ * @driver: the driver to be unbound
+ * @iface: the interface from which it will be unbound
+ *
+ * This should be used by drivers to release their claimed interfaces.
+ * It is normally called in their disconnect() methods, and only for
+ * drivers that bound to more than one interface in their probe().
+ *
+ * When the USB subsystem disconnect()s a driver from some interface,
+ * it automatically invokes this method for that interface. That
+ * means that even drivers that used usb_driver_claim_interface()
+ * usually won't need to call this.
+ */
+void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface)
+{
+ /* this should never happen, don't release something that's not ours */
+ if (!iface || iface->driver != driver)
+ return;
+
+ iface->driver = NULL;
+ usb_set_intfdata(iface, NULL);
+}
+
+/**
+ * usb_match_id - find first usb_device_id matching device or interface
+ * @interface: the interface of interest
+ * @id: array of usb_device_id structures, terminated by zero entry
+ *
+ * usb_match_id searches an array of usb_device_id's and returns
+ * the first one matching the device or interface, or null.
+ * This is used when binding (or rebinding) a driver to an interface.
+ * Most USB device drivers will use this indirectly, through the usb core,
+ * but some layered driver frameworks use it directly.
+ * These device tables are exported with MODULE_DEVICE_TABLE, through
+ * modutils and "modules.usbmap", to support the driver loading
+ * functionality of USB hotplugging.
+ *
+ * What Matches:
+ *
+ * The "match_flags" element in a usb_device_id controls which
+ * members are used. If the corresponding bit is set, the
+ * value in the device_id must match its corresponding member
+ * in the device or interface descriptor, or else the device_id
+ * does not match.
+ *
+ * "driver_info" is normally used only by device drivers,
+ * but you can create a wildcard "matches anything" usb_device_id
+ * as a driver's "modules.usbmap" entry if you provide an id with
+ * only a nonzero "driver_info" field. If you do this, the USB device
+ * driver's probe() routine should use additional intelligence to
+ * decide whether to bind to the specified interface.
+ *
+ * What Makes Good usb_device_id Tables:
+ *
+ * The match algorithm is very simple, so that intelligence in
+ * driver selection must come from smart driver id records.
+ * Unless you have good reasons to use another selection policy,
+ * provide match elements only in related groups, and order match
+ * specifiers from specific to general. Use the macros provided
+ * for that purpose if you can.
+ *
+ * The most specific match specifiers use device descriptor
+ * data. These are commonly used with product-specific matches;
+ * the USB_DEVICE macro lets you provide vendor and product IDs,
+ * and you can also match against ranges of product revisions.
+ * These are widely used for devices with application or vendor
+ * specific bDeviceClass values.
+ *
+ * Matches based on device class/subclass/protocol specifications
+ * are slightly more general; use the USB_DEVICE_INFO macro, or
+ * its siblings. These are used with single-function devices
+ * where bDeviceClass doesn't specify that each interface has
+ * its own class.
+ *
+ * Matches based on interface class/subclass/protocol are the
+ * most general; they let drivers bind to any interface on a
+ * multiple-function device. Use the USB_INTERFACE_INFO
+ * macro, or its siblings, to match class-per-interface style
+ * devices (as recorded in bDeviceClass).
+ *
+ * Within those groups, remember that not all combinations are
+ * meaningful. For example, don't give a product version range
+ * without vendor and product IDs; or specify a protocol without
+ * its associated class and subclass.
+ */
+const struct usb_device_id *
+usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_host_interface *intf;
+ struct usb_device *dev;
+
+ /* proc_connectinfo in devio.c may call us with id == NULL. */
+ if (id == NULL)
+ return NULL;
+
+ intf = &interface->altsetting [interface->act_altsetting];
+ dev = interface_to_usbdev(interface);
+
+ /* It is important to check that id->driver_info is nonzero,
+ since an entry that is all zeroes except for a nonzero
+ id->driver_info is the way to create an entry that
+ indicates that the driver want to examine every
+ device and interface. */
+ for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
+ id->driver_info; id++) {
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+ id->idVendor != dev->descriptor.idVendor)
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
+ id->idProduct != dev->descriptor.idProduct)
+ continue;
+
+ /* No need to test id->bcdDevice_lo != 0, since 0 is never
+ greater than any unsigned number. */
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
+ (id->bcdDevice_lo > dev->descriptor.bcdDevice))
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
+ (id->bcdDevice_hi < dev->descriptor.bcdDevice))
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+ (id->bDeviceClass != dev->descriptor.bDeviceClass))
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
+ (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
+ (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
+ (id->bInterfaceClass != intf->desc.bInterfaceClass))
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
+ (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
+ continue;
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
+ (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
+ continue;
+
+ return id;
+ }
+
+ return NULL;
+}
+
+/**
+ * usb_find_interface - find usb_interface pointer for driver and device
+ * @drv: the driver whose current configuration is considered
+ * @minor: the minor number of the desired device
+ *
+ * This walks the driver device list and returns a pointer to the interface
+ * with the matching minor. Note, this only works for devices that share the
+ * USB major number.
+ */
+struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
+{
+ struct list_head *entry;
+ struct device *dev;
+ struct usb_interface *intf;
+
+ list_for_each(entry, &drv->driver.devices) {
+ dev = container_of(entry, struct device, driver_list);
+
+ /* can't look at usb devices, only interfaces */
+ if (dev->driver == &usb_generic_driver)
+ continue;
+
+ intf = to_usb_interface(dev);
+ if (intf->minor == -1)
+ continue;
+ if (intf->minor == minor)
+ return intf;
+ }
+
+ /* no device found that matches */
+ return NULL;
+}
+
+static int usb_device_match (struct device *dev, struct device_driver *drv)
+{
+ struct usb_interface *intf;
+ struct usb_driver *usb_drv;
+ const struct usb_device_id *id;
+
+ /* check for generic driver, which we don't match any device with */
+ if (drv == &usb_generic_driver)
+ return 0;
+
+ intf = to_usb_interface(dev);
+
+ usb_drv = to_usb_driver(drv);
+ id = usb_drv->id_table;
+
+ id = usb_match_id (intf, usb_drv->id_table);
+ if (id)
+ return 1;
+
+ return 0;
+}
+
+
+#ifdef CONFIG_HOTPLUG
+
+/*
+ * USB hotplugging invokes what /proc/sys/kernel/hotplug says
+ * (normally /sbin/hotplug) when USB devices get added or removed.
+ *
+ * This invokes a user mode policy agent, typically helping to load driver
+ * or other modules, configure the device, and more. Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+ *
+ * We're called either from khubd (the typical case) or from root hub
+ * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
+ * delays in event delivery. Use sysfs (and DEVPATH) to make sure the
+ * device (and this configuration!) are still present.
+ */
+static int usb_hotplug (struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct usb_interface *intf;
+ struct usb_device *usb_dev;
+ char *scratch;
+ int i = 0;
+ int length = 0;
+
+ dbg ("%s", __FUNCTION__);
+
+ if (!dev)
+ return -ENODEV;
+
+ /* Must check driver_data here, as on remove driver is always NULL */
+ if ((dev->driver == &usb_generic_driver) ||
+ (dev->driver_data == &usb_generic_driver_data))
+ return 0;
+
+ intf = to_usb_interface(dev);
+ usb_dev = interface_to_usbdev (intf);
+
+ if (usb_dev->devnum < 0) {
+ dbg ("device already deleted ??");
+ return -ENODEV;
+ }
+ if (!usb_dev->bus) {
+ dbg ("bus already removed?");
+ return -ENODEV;
+ }
+
+ scratch = buffer;
+
+#ifdef CONFIG_USB_DEVICEFS
+ /* If this is available, userspace programs can directly read
+ * all the device descriptors we don't tell them about. Or
+ * even act as usermode drivers.
+ *
+ * FIXME reduce hardwired intelligence here
+ */
+ envp [i++] = scratch;
+ length += snprintf (scratch, buffer_size - length,
+ "DEVICE=/proc/bus/usb/%03d/%03d",
+ usb_dev->bus->busnum, usb_dev->devnum);
+ if ((buffer_size - length <= 0) || (i >= num_envp))
+ return -ENOMEM;
+ ++length;
+ scratch += length;
+#endif
+
+ /* per-device configurations are common */
+ envp [i++] = scratch;
+ length += snprintf (scratch, buffer_size - length, "PRODUCT=%x/%x/%x",
+ usb_dev->descriptor.idVendor,
+ usb_dev->descriptor.idProduct,
+ usb_dev->descriptor.bcdDevice);
+ if ((buffer_size - length <= 0) || (i >= num_envp))
+ return -ENOMEM;
+ ++length;
+ scratch += length;
+
+ /* class-based driver binding models */
+ envp [i++] = scratch;
+ length += snprintf (scratch, buffer_size - length, "TYPE=%d/%d/%d",
+ usb_dev->descriptor.bDeviceClass,
+ usb_dev->descriptor.bDeviceSubClass,
+ usb_dev->descriptor.bDeviceProtocol);
+ if ((buffer_size - length <= 0) || (i >= num_envp))
+ return -ENOMEM;
+ ++length;
+ scratch += length;
+
+ if (usb_dev->descriptor.bDeviceClass == 0) {
+ int alt = intf->act_altsetting;
+
+ /* 2.4 only exposed interface zero. in 2.5, hotplug
+ * agents are called for all interfaces, and can use
+ * $DEVPATH/bInterfaceNumber if necessary.
+ */
+ envp [i++] = scratch;
+ length += snprintf (scratch, buffer_size - length,
+ "INTERFACE=%d/%d/%d",
+ intf->altsetting[alt].desc.bInterfaceClass,
+ intf->altsetting[alt].desc.bInterfaceSubClass,
+ intf->altsetting[alt].desc.bInterfaceProtocol);
+ if ((buffer_size - length <= 0) || (i >= num_envp))
+ return -ENOMEM;
+ ++length;
+ scratch += length;
+
+ }
+ envp [i++] = 0;
+
+ return 0;
+}
+
+#else
+
+static int usb_hotplug (struct device *dev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ return -ENODEV;
+}
+
+#endif /* CONFIG_HOTPLUG */
+
+/**
+ * usb_alloc_dev - allocate a usb device structure (usbcore-internal)
+ * @parent: hub to which device is connected
+ * @bus: bus used to access the device
+ * Context: !in_interrupt ()
+ *
+ * Only hub drivers (including virtual root hub drivers for host
+ * controllers) should ever call this.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ */
+struct usb_device STDCALL *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus)
+{
+ struct usb_device *dev;
+
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ memset(dev, 0, sizeof(*dev));
+
+ device_initialize(&dev->dev);
+ dev->state = USB_STATE_ATTACHED;
+
+ usb_bus_get(bus);
+
+ if (!parent)
+ dev->devpath [0] = '0';
+ dev->bus = bus;
+ dev->parent = parent;
+ INIT_LIST_HEAD(&dev->filelist);
+
+ init_MUTEX(&dev->serialize);
+
+ if (dev->bus->op->allocate)
+ dev->bus->op->allocate(dev);
+
+ return dev;
+}
+
+/**
+ * usb_get_dev - increments the reference count of the usb device structure
+ * @dev: the device being referenced
+ *
+ * Each live reference to a device should be refcounted.
+ *
+ * Drivers for USB interfaces should normally record such references in
+ * their probe() methods, when they bind to an interface, and release
+ * them by calling usb_put_dev(), in their disconnect() methods.
+ *
+ * A pointer to the device with the incremented reference counter is returned.
+ */
+struct usb_device STDCALL *usb_get_dev (struct usb_device *dev)
+{
+ struct device *tmp;
+
+ if (!dev)
+ return NULL;
+
+ tmp = get_device(&dev->dev);
+ if (tmp)
+ return to_usb_device(tmp);
+ else
+ return NULL;
+}
+
+/**
+ * usb_put_dev - release a use of the usb device structure
+ * @dev: device that's been disconnected
+ *
+ * Must be called when a user of a device is finished with it. When the last
+ * user of the device calls this function, the memory of the device is freed.
+ */
+void STDCALL usb_put_dev(struct usb_device *dev)
+{
+ if (dev)
+ put_device(&dev->dev);
+}
+
+/**
+ * usb_release_dev - free a usb device structure when all users of it are finished.
+ * @dev: device that's been disconnected
+ *
+ * Will be called only by the device core when all users of this usb device are
+ * done.
+ */
+static void usb_release_dev(struct device *dev)
+{
+ struct usb_device *udev;
+
+ udev = to_usb_device(dev);
+
+ if (udev->bus && udev->bus->op && udev->bus->op->deallocate)
+ udev->bus->op->deallocate(udev);
+ usb_destroy_configuration (udev);
+ usb_bus_put (udev->bus);
+ kfree (udev);
+}
+
+
+static struct usb_device *match_device(struct usb_device *dev,
+ u16 vendor_id, u16 product_id)
+{
+ struct usb_device *ret_dev = NULL;
+ int child;
+
+ dbg("looking at vendor %d, product %d",
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct);
+
+ /* see if this device matches */
+ if ((dev->descriptor.idVendor == vendor_id) &&
+ (dev->descriptor.idProduct == product_id)) {
+ dbg ("found the device!");
+ ret_dev = usb_get_dev(dev);
+ goto exit;
+ }
+
+ /* look through all of the children of this device */
+ for (child = 0; child < dev->maxchild; ++child) {
+ if (dev->children[child]) {
+ ret_dev = match_device(dev->children[child],
+ vendor_id, product_id);
+ if (ret_dev)
+ goto exit;
+ }
+ }
+exit:
+ return ret_dev;
+}
+
+/**
+ * usb_find_device - find a specific usb device in the system
+ * @vendor_id: the vendor id of the device to find
+ * @product_id: the product id of the device to find
+ *
+ * Returns a pointer to a struct usb_device if such a specified usb
+ * device is present in the system currently. The usage count of the
+ * device will be incremented if a device is found. Make sure to call
+ * usb_put_dev() when the caller is finished with the device.
+ *
+ * If a device with the specified vendor and product id is not found,
+ * NULL is returned.
+ */
+struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
+{
+ struct list_head *buslist;
+ struct usb_bus *bus;
+ struct usb_device *dev = NULL;
+
+ down(&usb_bus_list_lock);
+ for (buslist = usb_bus_list.next;
+ buslist != &usb_bus_list;
+ buslist = buslist->next) {
+ bus = container_of(buslist, struct usb_bus, bus_list);
+ dev = match_device(bus->root_hub, vendor_id, product_id);
+ if (dev)
+ goto exit;
+ }
+exit:
+ up(&usb_bus_list_lock);
+ return dev;
+}
+
+/**
+ * usb_get_current_frame_number - return current bus frame number
+ * @dev: the device whose bus is being queried
+ *
+ * Returns the current frame number for the USB host controller
+ * used with the given USB device. This can be used when scheduling
+ * isochronous requests.
+ *
+ * Note that different kinds of host controller have different
+ * "scheduling horizons". While one type might support scheduling only
+ * 32 frames into the future, others could support scheduling up to
+ * 1024 frames into the future.
+ */
+int usb_get_current_frame_number(struct usb_device *dev)
+{
+ return dev->bus->op->get_frame_number (dev);
+}
+
+/*-------------------------------------------------------------------*/
+/*
+ * __usb_get_extra_descriptor() finds a descriptor of specific type in the
+ * extra field of the interface and endpoint descriptor structs.
+ */
+
+int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr)
+{
+ struct usb_descriptor_header *header;
+
+ while (size >= sizeof(struct usb_descriptor_header)) {
+ header = (struct usb_descriptor_header *)buffer;
+
+ if (header->bLength < 2) {
+ err("invalid descriptor length of %d", header->bLength);
+ return -1;
+ }
+
+ if (header->bDescriptorType == type) {
+ *ptr = header;
+ return 0;
+ }
+
+ buffer += header->bLength;
+ size -= header->bLength;
+ }
+ return -1;
+}
+
+/**
+ * usb_disconnect - disconnect a device (usbcore-internal)
+ * @pdev: pointer to device being disconnected
+ * Context: !in_interrupt ()
+ *
+ * Something got disconnected. Get rid of it, and all of its children.
+ *
+ * Only hub drivers (including virtual root hub drivers for host
+ * controllers) should ever call this.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ */
+void usb_disconnect(struct usb_device **pdev)
+{
+ struct usb_device *dev = *pdev;
+ struct usb_bus *bus;
+ struct usb_operations *ops;
+ int i;
+
+ might_sleep ();
+
+ if (!dev) {
+ pr_debug ("%s nodev\n", __FUNCTION__);
+ return;
+ }
+ bus = dev->bus;
+ if (!bus) {
+ pr_debug ("%s nobus\n", __FUNCTION__);
+ return;
+ }
+ ops = bus->op;
+
+ *pdev = NULL;
+
+ /* mark the device as inactive, so any further urb submissions for
+ * this device will fail.
+ */
+ dev->state = USB_STATE_NOTATTACHED;
+
+ dev_info (&dev->dev, "USB disconnect, address %d\n", dev->devnum);
+
+ /* Free up all the children before we remove this device */
+ for (i = 0; i < USB_MAXCHILDREN; i++) {
+ struct usb_device **child = dev->children + i;
+ if (*child)
+ usb_disconnect(child);
+ }
+
+ /* disconnect() drivers from interfaces (a key side effect) */
+ dev_dbg (&dev->dev, "unregistering interfaces\n");
+ if (dev->actconfig) {
+ for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+ struct usb_interface *interface;
+
+ /* remove this interface */
+ interface = &dev->actconfig->interface[i];
+ device_unregister(&interface->dev);
+ }
+ }
+
+ /* deallocate hcd/hardware state */
+ if (ops->disable) {
+ void (*disable)(struct usb_device *, int) = ops->disable;
+
+ for (i = 0; i < 15; i++) {
+ disable (dev, i);
+ disable (dev, USB_DIR_IN | i);
+ }
+ }
+
+ dev_dbg (&dev->dev, "unregistering device\n");
+ /* Free the device number and remove the /proc/bus/usb entry */
+ if (dev->devnum > 0) {
+ clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+ usbfs_remove_device(dev);
+ }
+ device_unregister(&dev->dev);
+
+ /* Decrement the reference count, it'll auto free everything when */
+ /* it hits 0 which could very well be now */
+ usb_put_dev(dev);
+}
+
+/**
+ * usb_connect - pick device address (usbcore-internal)
+ * @dev: newly detected device (in DEFAULT state)
+ *
+ * Picks a device address. It's up to the hub (or root hub) driver
+ * to handle and manage enumeration, starting from the DEFAULT state.
+ * Only hub drivers (including virtual root hub drivers for host
+ * controllers) should ever call this.
+ */
+void STDCALL usb_connect(struct usb_device *dev)
+{
+ int devnum;
+ // FIXME needs locking for SMP!!
+ /* why? this is called only from the hub thread,
+ * which hopefully doesn't run on multiple CPU's simultaneously 8-)
+ * ... it's also called from modprobe/rmmod/apmd threads as part
+ * of virtual root hub init/reinit. In the init case, the hub code
+ * won't have seen this, but not so for reinit ...
+ */
+ dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */
+
+ /* Try to allocate the next devnum beginning at bus->devnum_next. */
+ devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, dev->bus->devnum_next);
+ if (devnum >= 128)
+ devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
+
+ dev->bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+
+ if (devnum < 128) {
+ set_bit(devnum, dev->bus->devmap.devicemap);
+ dev->devnum = devnum;
+ }
+}
+
+
+// hub-only!! ... and only exported for reset/reinit path.
+// otherwise used internally, for usb_new_device()
+int usb_set_address(struct usb_device *dev)
+{
+ int retval;
+
+ if (dev->devnum == 0)
+ return -EINVAL;
+ if (dev->state != USB_STATE_DEFAULT && dev->state != USB_STATE_ADDRESS)
+ return -EINVAL;
+ retval = usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS,
+ 0, dev->devnum, 0, NULL, 0, HZ * USB_CTRL_SET_TIMEOUT);
+ if (retval == 0)
+ dev->state = USB_STATE_ADDRESS;
+ return retval;
+}
+
+
+/* improve on the default device description, if we can ... and
+ * while we're at it, maybe show the vendor and product strings.
+ */
+static void set_device_description (struct usb_device *dev)
+{
+ void *buf;
+ int mfgr = dev->descriptor.iManufacturer;
+ int prod = dev->descriptor.iProduct;
+ int vendor_id = dev->descriptor.idVendor;
+ int product_id = dev->descriptor.idProduct;
+ char *mfgr_str, *prod_str;
+
+ /* set default; keep it if there are no strings, or kmalloc fails */
+ sprintf (dev->dev.name, "USB device %04x:%04x",
+ vendor_id, product_id);
+
+ if (!(buf = kmalloc(256 * 2, GFP_KERNEL)))
+ return;
+
+ prod_str = (char *) buf;
+ mfgr_str = (char *) buf + 256;
+
+ if (prod && usb_string (dev, prod, prod_str, 256) > 0) {
+#ifdef DEBUG
+ dev_printk (KERN_INFO, &dev->dev, "Product: %s\n", prod_str);
+#endif
+ } else {
+ prod_str = 0;
+ }
+
+ if (mfgr && usb_string (dev, mfgr, mfgr_str, 256) > 0) {
+#ifdef DEBUG
+ dev_printk (KERN_INFO, &dev->dev, "Manufacturer: %s\n", mfgr_str);
+#endif
+ } else {
+ mfgr_str = 0;
+ }
+
+ /* much like pci ... describe as either:
+ * - both strings: 'product descr (vendor descr)'
+ * - product only: 'product descr (USB device vvvv:pppp)'
+ * - vendor only: 'USB device vvvv:pppp (vendor descr)'
+ * - neither string: 'USB device vvvv:pppp'
+ */
+
+ if (prod_str && mfgr_str) {
+
+ snprintf(dev->dev.name, sizeof dev->dev.name,
+ "%s (%s)", prod_str, mfgr_str);
+ } else if (prod_str) {
+ snprintf(dev->dev.name, sizeof dev->dev.name,
+ "%s (USB device %04x:%04x)",
+ prod_str, vendor_id, product_id);
+
+ } else if (mfgr_str) {
+ snprintf(dev->dev.name, sizeof dev->dev.name,
+ "USB device %04x:%04x (%s)",
+ vendor_id, product_id, mfgr_str);
+ }
+ usbprintk("USB connected: %s\n",dev->dev.name);
+ kfree(buf);
+}
+
+/*
+ * By the time we get here, we chose a new device address
+ * and is in the default state. We need to identify the thing and
+ * get the ball rolling..
+ *
+ * Returns 0 for success, != 0 for error.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ *
+ * Only hub drivers (including virtual root hub drivers for host
+ * controllers) should ever call this.
+ */
+#define NEW_DEVICE_RETRYS 2
+#define SET_ADDRESS_RETRYS 2
+int usb_new_device(struct usb_device *dev, struct device *parent)
+{
+ int err = 0;
+ int i;
+ int j;
+
+ /*
+ * Set the driver for the usb device to point to the "generic" driver.
+ * This prevents the main usb device from being sent to the usb bus
+ * probe function. Yes, it's a hack, but a nice one :)
+ *
+ * Do it asap, so more driver model stuff (like the device.h message
+ * utilities) can be used in hcd submit/unlink code paths.
+ */
+ usb_generic_driver.bus = &usb_bus_type;
+ dev->dev.parent = parent;
+ dev->dev.driver = &usb_generic_driver;
+ dev->dev.bus = &usb_bus_type;
+ dev->dev.release = usb_release_dev;
+ dev->dev.driver_data = &usb_generic_driver_data;
+ usb_get_dev(dev);
+ if (dev->dev.bus_id[0] == 0)
+ sprintf (&dev->dev.bus_id[0], "%d-%s",
+ dev->bus->busnum, dev->devpath);
+
+ /* dma masks come from the controller; readonly, except to hcd */
+ dev->dev.dma_mask = parent->dma_mask;
+
+ /* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
+ * it's fixed size except for full speed devices.
+ */
+ switch (dev->speed) {
+ case USB_SPEED_HIGH: /* fixed at 64 */
+ i = 64;
+ break;
+ case USB_SPEED_FULL: /* 8, 16, 32, or 64 */
+ /* to determine the ep0 maxpacket size, read the first 8
+ * bytes from the device descriptor to get bMaxPacketSize0;
+ * then correct our initial (small) guess.
+ */
+ // FALLTHROUGH
+ case USB_SPEED_LOW: /* fixed at 8 */
+ i = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev->epmaxpacketin [0] = i;
+ dev->epmaxpacketout[0] = i;
+
+ for (i = 0; i < NEW_DEVICE_RETRYS; ++i) {
+
+ for (j = 0; j < SET_ADDRESS_RETRYS; ++j) {
+ err = usb_set_address(dev);
+ if (err >= 0)
+ break;
+ wait_ms(200);
+ }
+ if (err < 0) {
+ dev_err(&dev->dev, "USB device not accepting new address=%d (error=%d)\n",
+ dev->devnum, err);
+ dev->state = USB_STATE_DEFAULT;
+ clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ wait_ms(10); /* Let the SET_ADDRESS settle */
+
+ /* high and low speed devices don't need this... */
+ err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8);
+ if (err >= 8)
+ break;
+ wait_ms(100);
+ }
+
+ if (err < 8) {
+ if (err < 0)
+ dev_err(&dev->dev, "USB device not responding, giving up (error=%d)\n", err);
+ else
+ dev_err(&dev->dev, "USB device descriptor short read (expected %i, got %i)\n", 8, err);
+ clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+ if (dev->speed == USB_SPEED_FULL) {
+ dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0;
+ dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0;
+ }
+
+ /* USB device state == addressed ... still not usable */
+
+ err = usb_get_device_descriptor(dev);
+ if (err < (signed)sizeof(dev->descriptor)) {
+ if (err < 0)
+ dev_err(&dev->dev, "unable to get device descriptor (error=%d)\n", err);
+ else
+ dev_err(&dev->dev, "USB device descriptor short read (expected %Zi, got %i)\n",
+ sizeof(dev->descriptor), err);
+
+ clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ err = usb_get_configuration(dev);
+ if (err < 0) {
+ dev_err(&dev->dev, "unable to get device %d configuration (error=%d)\n",
+ dev->devnum, err);
+ clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ /* we set the default configuration here */
+ err = usb_set_configuration(dev, dev->config[0].desc.bConfigurationValue);
+ if (err) {
+ dev_err(&dev->dev, "failed to set device %d default configuration (error=%d)\n",
+ dev->devnum, err);
+ clear_bit(dev->devnum, dev->bus->devmap.devicemap);
+ dev->devnum = -1;
+ return 1;
+ }
+
+ /* USB device state == configured ... tell the world! */
+
+ dev_dbg(&dev->dev, "new device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
+ dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);
+ set_device_description (dev);
+
+#ifdef DEBUG
+ if (dev->descriptor.iSerialNumber)
+ usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
+#endif
+ /* put into sysfs, with device and config specific files */
+ err = device_add (&dev->dev);
+ if (err)
+ return err;
+ usb_create_driverfs_dev_files (dev);
+
+ /* Register all of the interfaces for this device with the driver core.
+ * Remember, interfaces get bound to drivers, not devices. */
+ for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+ struct usb_interface *interface = &dev->actconfig->interface[i];
+ struct usb_interface_descriptor *desc;
+
+ desc = &interface->altsetting [interface->act_altsetting].desc;
+ interface->dev.parent = &dev->dev;
+ interface->dev.driver = NULL;
+ interface->dev.bus = &usb_bus_type;
+ interface->dev.dma_mask = parent->dma_mask;
+ sprintf (&interface->dev.bus_id[0], "%d-%s:%d",
+ dev->bus->busnum, dev->devpath,
+ desc->bInterfaceNumber);
+ if (!desc->iInterface
+ || usb_string (dev, desc->iInterface,
+ interface->dev.name,
+ sizeof interface->dev.name) <= 0) {
+ /* typically devices won't bother with interface
+ * descriptions; this is the normal case. an
+ * interface's driver might describe it better.
+ * (also: iInterface is per-altsetting ...)
+ */
+ sprintf (&interface->dev.name[0],
+ "usb-%s-%s interface %d",
+ dev->bus->bus_name, dev->devpath,
+ desc->bInterfaceNumber);
+ DPRINT1("usb_new_device: %s\n", interface->dev.name);
+ }
+ dev_dbg (&dev->dev, "%s - registering interface %s\n", __FUNCTION__, interface->dev.bus_id);
+ device_add (&interface->dev);
+ usb_create_driverfs_intf_files (interface);
+ }
+ /* add a /proc/bus/usb entry */
+ usbfs_add_device(dev);
+
+ return 0;
+}
+
+/**
+ * usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_DMA_MAP
+ * @dev: device the buffer will be used with
+ * @size: requested buffer size
+ * @mem_flags: affect whether allocation may block
+ * @dma: used to return DMA address of buffer
+ *
+ * Return value is either null (indicating no buffer could be allocated), or
+ * the cpu-space pointer to a buffer that may be used to perform DMA to the
+ * specified device. Such cpu-space buffers are returned along with the DMA
+ * address (through the pointer provided).
+ *
+ * These buffers are used with URB_NO_DMA_MAP set in urb->transfer_flags to
+ * avoid behaviors like using "DMA bounce buffers", or tying down I/O mapping
+ * hardware for long idle periods. The implementation varies between
+ * platforms, depending on details of how DMA will work to this device.
+ * Using these buffers also helps prevent cacheline sharing problems on
+ * architectures where CPU caches are not DMA-coherent.
+ *
+ * When the buffer is no longer used, free it with usb_buffer_free().
+ */
+void *usb_buffer_alloc (
+ struct usb_device *dev,
+ size_t size,
+ int mem_flags,
+ dma_addr_t *dma
+)
+{
+ if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
+ return 0;
+ return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
+}
+
+/**
+ * usb_buffer_free - free memory allocated with usb_buffer_alloc()
+ * @dev: device the buffer was used with
+ * @size: requested buffer size
+ * @addr: CPU address of buffer
+ * @dma: DMA address of buffer
+ *
+ * This reclaims an I/O buffer, letting it be reused. The memory must have
+ * been allocated using usb_buffer_alloc(), and the parameters must match
+ * those provided in that allocation request.
+ */
+void usb_buffer_free (
+ struct usb_device *dev,
+ size_t size,
+ void *addr,
+ dma_addr_t dma
+)
+{
+ if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
+ return;
+ dev->bus->op->buffer_free (dev->bus, size, addr, dma);
+}
+
+/**
+ * usb_buffer_map - create DMA mapping(s) for an urb
+ * @urb: urb whose transfer_buffer will be mapped
+ *
+ * Return value is either null (indicating no buffer could be mapped), or
+ * the parameter. URB_NO_DMA_MAP is added to urb->transfer_flags if the
+ * operation succeeds. If the device is connected to this system through
+ * a non-DMA controller, this operation always succeeds.
+ *
+ * This call would normally be used for an urb which is reused, perhaps
+ * as the target of a large periodic transfer, with usb_buffer_dmasync()
+ * calls to synchronize memory and dma state. It may not be used for
+ * control requests.
+ *
+ * Reverse the effect of this call with usb_buffer_unmap().
+ */
+struct urb *usb_buffer_map (struct urb *urb)
+{
+ struct usb_bus *bus;
+ struct device *controller;
+
+ if (!urb
+ || usb_pipecontrol (urb->pipe)
+ || !urb->dev
+ || !(bus = urb->dev->bus)
+ || !(controller = bus->controller))
+ return 0;
+
+ if (controller->dma_mask) {
+ urb->transfer_dma = dma_map_single (controller,
+ urb->transfer_buffer, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ // FIXME generic api broken like pci, can't report errors
+ // if (urb->transfer_dma == DMA_ADDR_INVALID) return 0;
+ } else
+ urb->transfer_dma = ~0;
+ urb->transfer_flags |= URB_NO_DMA_MAP;
+ return urb;
+}
+
+/**
+ * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s)
+ * @urb: urb whose transfer_buffer will be synchronized
+ */
+void usb_buffer_dmasync (struct urb *urb)
+{
+ struct usb_bus *bus;
+ struct device *controller;
+
+ if (!urb
+ || !(urb->transfer_flags & URB_NO_DMA_MAP)
+ || !urb->dev
+ || !(bus = urb->dev->bus)
+ || !(controller = bus->controller))
+ return;
+
+ if (controller->dma_mask)
+ dma_sync_single (controller,
+ urb->transfer_dma, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+
+/**
+ * usb_buffer_unmap - free DMA mapping(s) for an urb
+ * @urb: urb whose transfer_buffer will be unmapped
+ *
+ * Reverses the effect of usb_buffer_map().
+ */
+void usb_buffer_unmap (struct urb *urb)
+{
+ struct usb_bus *bus;
+ struct device *controller;
+
+ if (!urb
+ || !(urb->transfer_flags & URB_NO_DMA_MAP)
+ || !urb->dev
+ || !(bus = urb->dev->bus)
+ || !(controller = bus->controller))
+ return;
+
+ if (controller->dma_mask)
+ dma_unmap_single (controller,
+ urb->transfer_dma, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ urb->transfer_flags &= ~URB_NO_DMA_MAP;
+}
+
+/**
+ * usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
+ * @dev: device to which the scatterlist will be mapped
+ * @pipe: endpoint defining the mapping direction
+ * @sg: the scatterlist to map
+ * @nents: the number of entries in the scatterlist
+ *
+ * Return value is either < 0 (indicating no buffers could be mapped), or
+ * the number of DMA mapping array entries in the scatterlist.
+ *
+ * The caller is responsible for placing the resulting DMA addresses from
+ * the scatterlist into URB transfer buffer pointers, and for setting the
+ * URB_NO_DMA_MAP transfer flag in each of those URBs.
+ *
+ * Top I/O rates come from queuing URBs, instead of waiting for each one
+ * to complete before starting the next I/O. This is particularly easy
+ * to do with scatterlists. Just allocate and submit one URB for each DMA
+ * mapping entry returned, stopping on the first error or when all succeed.
+ * Better yet, use the usb_sg_*() calls, which do that (and more) for you.
+ *
+ * This call would normally be used when translating scatterlist requests,
+ * rather than usb_buffer_map(), since on some hardware (with IOMMUs) it
+ * may be able to coalesce mappings for improved I/O efficiency.
+ *
+ * Reverse the effect of this call with usb_buffer_unmap_sg().
+ */
+int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int nents)
+{
+ struct usb_bus *bus;
+ struct device *controller;
+
+ if (!dev
+ || usb_pipecontrol (pipe)
+ || !(bus = dev->bus)
+ || !(controller = bus->controller)
+ || !controller->dma_mask)
+ return -1;
+
+ // FIXME generic api broken like pci, can't report errors
+ return dma_map_sg (controller, sg, nents,
+ usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+
+/**
+ * usb_buffer_dmasync_sg - synchronize DMA and CPU view of scatterlist buffer(s)
+ * @dev: device to which the scatterlist will be mapped
+ * @pipe: endpoint defining the mapping direction
+ * @sg: the scatterlist to synchronize
+ * @n_hw_ents: the positive return value from usb_buffer_map_sg
+ *
+ * Use this when you are re-using a scatterlist's data buffers for
+ * another USB request.
+ */
+void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int n_hw_ents)
+{
+ struct usb_bus *bus;
+ struct device *controller;
+
+ if (!dev
+ || !(bus = dev->bus)
+ || !(controller = bus->controller)
+ || !controller->dma_mask)
+ return;
+
+ dma_sync_sg (controller, sg, n_hw_ents,
+ usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+
+/**
+ * usb_buffer_unmap_sg - free DMA mapping(s) for a scatterlist
+ * @dev: device to which the scatterlist will be mapped
+ * @pipe: endpoint defining the mapping direction
+ * @sg: the scatterlist to unmap
+ * @n_hw_ents: the positive return value from usb_buffer_map_sg
+ *
+ * Reverses the effect of usb_buffer_map_sg().
+ */
+void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int n_hw_ents)
+{
+ struct usb_bus *bus;
+ struct device *controller;
+
+ if (!dev
+ || !(bus = dev->bus)
+ || !(controller = bus->controller)
+ || !controller->dma_mask)
+ return;
+
+ dma_unmap_sg (controller, sg, n_hw_ents,
+ usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+
+
+struct bus_type usb_bus_type = {
+ .name = "usb",
+ .match = usb_device_match,
+ .hotplug = usb_hotplug,
+};
+
+#ifndef MODULE
+
+static int __init usb_setup_disable(char *str)
+{
+ nousb = 1;
+ return 1;
+}
+
+/* format to disable USB on kernel command line is: nousb */
+__setup("nousb", usb_setup_disable);
+
+#endif
+
+/*
+ * for external read access to <nousb>
+ */
+int STDCALL usb_disabled(void)
+{
+ return nousb;
+}
+
+/*
+ * Init
+ */
+int STDCALL __init usb_init(void)
+{
+ if (nousb) {
+ info("USB support disabled\n");
+ return 0;
+ }
+
+ bus_register(&usb_bus_type);
+ usb_major_init();
+ usbfs_init();
+ usb_hub_init();
+
+ driver_register(&usb_generic_driver);
+
+ return 0;
+}
+
+/*
+ * Cleanup
+ */
+void STDCALL __exit usb_exit(void)
+{
+ /* This will matter if shutdown/reboot does exitcalls. */
+ if (nousb)
+ return;
+
+ driver_unregister(&usb_generic_driver);
+ usb_major_cleanup();
+ usbfs_cleanup();
+ usb_hub_cleanup();
+ bus_unregister(&usb_bus_type);
+}
+
+subsys_initcall(usb_init);
+module_exit(usb_exit);
+
+/*
+ * USB may be built into the kernel or be built as modules.
+ * These symbols are exported for device (or host controller)
+ * driver modules to use.
+ */
+EXPORT_SYMBOL(usb_epnum_to_ep_desc);
+
+EXPORT_SYMBOL(usb_register);
+EXPORT_SYMBOL(usb_deregister);
+EXPORT_SYMBOL(usb_disabled);
+
+EXPORT_SYMBOL(usb_device_probe);
+EXPORT_SYMBOL(usb_device_remove);
+
+EXPORT_SYMBOL(usb_alloc_dev);
+EXPORT_SYMBOL(usb_put_dev);
+EXPORT_SYMBOL(usb_get_dev);
+EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
+
+EXPORT_SYMBOL(usb_driver_claim_interface);
+EXPORT_SYMBOL(usb_interface_claimed);
+EXPORT_SYMBOL(usb_driver_release_interface);
+EXPORT_SYMBOL(usb_match_id);
+EXPORT_SYMBOL(usb_find_interface);
+EXPORT_SYMBOL(usb_ifnum_to_if);
+
+EXPORT_SYMBOL(usb_new_device);
+EXPORT_SYMBOL(usb_reset_device);
+EXPORT_SYMBOL(usb_connect);
+EXPORT_SYMBOL(usb_disconnect);
+
+EXPORT_SYMBOL(__usb_get_extra_descriptor);
+
+EXPORT_SYMBOL(usb_find_device);
+EXPORT_SYMBOL(usb_get_current_frame_number);
+
+EXPORT_SYMBOL (usb_buffer_alloc);
+EXPORT_SYMBOL (usb_buffer_free);
+
+EXPORT_SYMBOL (usb_buffer_map);
+EXPORT_SYMBOL (usb_buffer_dmasync);
+EXPORT_SYMBOL (usb_buffer_unmap);
+
+EXPORT_SYMBOL (usb_buffer_map_sg);
+EXPORT_SYMBOL (usb_buffer_dmasync_sg);
+EXPORT_SYMBOL (usb_buffer_unmap_sg);
+
+MODULE_LICENSE("GPL");
-/* Functions local to drivers/usb/core/ */\r
-\r
-extern void usb_create_driverfs_dev_files (struct usb_device *dev);\r
-extern void usb_create_driverfs_intf_files (struct usb_interface *intf);\r
-\r
+/* Functions local to drivers/usb/core/ */
+
+extern void usb_create_driverfs_dev_files (struct usb_device *dev);
+extern void usb_create_driverfs_intf_files (struct usb_interface *intf);
+
-/*\r
- ReactOS specific functions for usbcore module\r
- by Aleksey Bragin (aleksey@reactos.com)\r
-*/\r
-\r
-#include <ddk/ntddk.h>\r
-#include <debug.h>\r
-\r
-NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)\r
-{\r
- DbgPrint("usbcore: AddDevice called\n");\r
- \r
- /* we need to do kind of this stuff here (as usual)\r
- PDEVICE_OBJECT fdo;\r
- IoCreateDevice(..., &fdo);\r
- pdx->LowerDeviceObject = \r
- IoAttachDeviceToDeviceStack(fdo, pdo);*/\r
-\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-VOID DriverUnload(PDRIVER_OBJECT DriverObject)\r
-{\r
- // nothing to do here yet\r
-}\r
-\r
-// Dispatch PNP\r
-NTSTATUS DispatchPnp(PDEVICE_OBJECT fdo, PIRP Irp)\r
-{\r
- ULONG fcn;\r
- PIO_STACK_LOCATION stack;\r
- \r
- stack = IoGetCurrentIrpStackLocation(Irp);\r
- fcn = stack->MinorFunction; \r
- DbgPrint("IRP_MJ_PNP, fcn=%d\n", fcn);\r
-\r
- if (fcn == IRP_MN_REMOVE_DEVICE)\r
- {\r
- IoDeleteDevice(fdo);\r
- }\r
-\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-NTSTATUS DispatchPower(PDEVICE_OBJECT fido, PIRP Irp)\r
-{\r
- DbgPrint("IRP_MJ_POWER dispatch\n");\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-/*\r
- * Standard DriverEntry method.\r
- */\r
-NTSTATUS STDCALL\r
-DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegPath)\r
-{\r
- DriverObject->DriverUnload = DriverUnload;\r
- DriverObject->DriverExtension->AddDevice = AddDevice;\r
- DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;\r
- DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;\r
-\r
- return STATUS_SUCCESS;\r
-}\r
+/*
+ ReactOS specific functions for usbcore module
+ by Aleksey Bragin (aleksey@reactos.com)
+*/
+
+#include <ddk/ntddk.h>
+#include <debug.h>
+
+NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)
+{
+ DbgPrint("usbcore: AddDevice called\n");
+
+ /* we need to do kind of this stuff here (as usual)
+ PDEVICE_OBJECT fdo;
+ IoCreateDevice(..., &fdo);
+ pdx->LowerDeviceObject =
+ IoAttachDeviceToDeviceStack(fdo, pdo);*/
+
+ return STATUS_SUCCESS;
+}
+
+VOID DriverUnload(PDRIVER_OBJECT DriverObject)
+{
+ // nothing to do here yet
+}
+
+// Dispatch PNP
+NTSTATUS DispatchPnp(PDEVICE_OBJECT fdo, PIRP Irp)
+{
+ ULONG fcn;
+ PIO_STACK_LOCATION stack;
+
+ stack = IoGetCurrentIrpStackLocation(Irp);
+ fcn = stack->MinorFunction;
+ DbgPrint("IRP_MJ_PNP, fcn=%d\n", fcn);
+
+ if (fcn == IRP_MN_REMOVE_DEVICE)
+ {
+ IoDeleteDevice(fdo);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS DispatchPower(PDEVICE_OBJECT fido, PIRP Irp)
+{
+ DbgPrint("IRP_MJ_POWER dispatch\n");
+ return STATUS_SUCCESS;
+}
+
+/*
+ * Standard DriverEntry method.
+ */
+NTSTATUS STDCALL
+DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegPath)
+{
+ DriverObject->DriverUnload = DriverUnload;
+ DriverObject->DriverExtension->AddDevice = AddDevice;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
+ DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
+
+ return STATUS_SUCCESS;
+}
-;\r
-; Exports definition file for usbcore.sys\r
-;\r
-EXPORTS\r
-usb_init@0\r
-usb_exit@0\r
-usb_init_urb@4\r
-usb_alloc_urb@8\r
-usb_free_urb@4\r
-usb_get_urb@4\r
-usb_get_dev@4\r
-usb_submit_urb@8\r
-usb_unlink_urb@4\r
-usb_bus_init@4\r
-usb_alloc_bus@4\r
-usb_free_bus@4\r
-usb_register_bus@4\r
-usb_deregister_bus@4\r
-usb_register_root_hub@8\r
-usb_calc_bus_time@16\r
-usb_check_bandwidth@8\r
-usb_claim_bandwidth@16\r
-usb_release_bandwidth@12\r
-usb_hcd_giveback_urb@12\r
-;usb_hcd_irq@12\r
-usb_hc_died@4\r
-usb_alloc_dev@8\r
-usb_connect@4\r
-usb_put_dev@4\r
-usb_disabled@0\r
-usb_hcd_pci_probe@8\r
+;
+; Exports definition file for usbcore.sys
+;
+EXPORTS
+usb_init@0
+usb_exit@0
+usb_init_urb@4
+usb_alloc_urb@8
+usb_free_urb@4
+usb_get_urb@4
+usb_get_dev@4
+usb_submit_urb@8
+usb_unlink_urb@4
+usb_bus_init@4
+usb_alloc_bus@4
+usb_free_bus@4
+usb_register_bus@4
+usb_deregister_bus@4
+usb_register_root_hub@8
+usb_calc_bus_time@16
+usb_check_bandwidth@8
+usb_claim_bandwidth@16
+usb_release_bandwidth@12
+usb_hcd_giveback_urb@12
+;usb_hcd_irq@12
+usb_hc_died@4
+usb_alloc_dev@8
+usb_connect@4
+usb_put_dev@4
+usb_disabled@0
+usb_hcd_pci_probe@8
usb_hcd_pci_remove@4
\ No newline at end of file
-#define REACTOS_VERSION_DLL\r
-#define REACTOS_STR_FILE_DESCRIPTION "USB Core Device Driver\0"\r
-#define REACTOS_STR_INTERNAL_NAME "usbcore\0"\r
-#define REACTOS_STR_ORIGINAL_FILENAME "usbcore.sys\0"\r
-#include <reactos/version.rc>\r
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION "USB Core Device Driver\0"
+#define REACTOS_STR_INTERNAL_NAME "usbcore\0"
+#define REACTOS_STR_ORIGINAL_FILENAME "usbcore.sys\0"
+#include <reactos/version.rc>
-PATH_TO_TOP = ../../../..\r
-\r
-TARGET_TYPE = export_driver\r
-\r
-TARGET_NAME = ohci\r
-\r
-TARGET_DDKLIBS = ntoskrnl.a usbcore.a\r
-\r
-TARGET_CFLAGS = -Wall -I$(PATH_TO_TOP)/ntoskrnl/include -DDEBUG_MODE\r
-\r
-TARGET_OBJECTS = \\r
- ohci-hcd.o ohci_main.o ../sys/ros_wrapper.o ../sys/linuxwrapper.o\r
-\r
-include $(PATH_TO_TOP)/rules.mak\r
-\r
-include $(TOOLS_PATH)/helper.mk\r
-\r
-# Automatic dependency tracking\r
-DEP_OBJECTS := $(TARGET_OBJECTS)\r
-include $(PATH_TO_TOP)/tools/depend.mk\r
+PATH_TO_TOP = ../../../..
+
+TARGET_TYPE = export_driver
+
+TARGET_NAME = ohci
+
+TARGET_DDKLIBS = ntoskrnl.a usbcore.a
+
+TARGET_CFLAGS = -Wall -I$(PATH_TO_TOP)/ntoskrnl/include -DDEBUG_MODE
+
+TARGET_OBJECTS = \
+ ohci-hcd.o ohci_main.o ../sys/ros_wrapper.o ../sys/linuxwrapper.o
+
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
+
+# Automatic dependency tracking
+DEP_OBJECTS := $(TARGET_OBJECTS)
+include $(PATH_TO_TOP)/tools/depend.mk
-O_TARGET := ohci-hcd.o\r
-\r
-include $(TOPDIR)/Rules.make\r
+O_TARGET := ohci-hcd.o
+
+include $(TOPDIR)/Rules.make
-/*\r
- * OHCI HCD (Host Controller Driver) for USB.\r
- *\r
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>\r
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>\r
- *\r
- * This file is licenced under the GPL.\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-#ifdef DEBUG\r
-\r
-#define edstring(ed_type) ({ char *temp; \\r
- switch (ed_type) { \\r
- case PIPE_CONTROL: temp = "ctrl"; break; \\r
- case PIPE_BULK: temp = "bulk"; break; \\r
- case PIPE_INTERRUPT: temp = "intr"; break; \\r
- default: temp = "isoc"; break; \\r
- }; temp;})\r
-#define pipestring(pipe) edstring(usb_pipetype(pipe))\r
-\r
-/* debug| print the main components of an URB\r
- * small: 0) header + data packets 1) just header\r
- */\r
-static void __attribute__((unused))\r
-urb_print (struct urb * urb, char * str, int small)\r
-{\r
- unsigned int pipe= urb->pipe;\r
-\r
- if (!urb->dev || !urb->dev->bus) {\r
- dbg("%s URB: no dev", str);\r
- return;\r
- }\r
-\r
-#ifndef OHCI_VERBOSE_DEBUG\r
- if (urb->status != 0)\r
-#endif\r
- dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d",\r
- str,\r
- urb,\r
- usb_pipedevice (pipe),\r
- usb_pipeendpoint (pipe),\r
- usb_pipeout (pipe)? "out" : "in",\r
- pipestring (pipe),\r
- urb->transfer_flags,\r
- urb->actual_length,\r
- urb->transfer_buffer_length,\r
- urb->status);\r
-\r
-#ifdef OHCI_VERBOSE_DEBUG\r
- if (!small) {\r
- int i, len;\r
-\r
- if (usb_pipecontrol (pipe)) {\r
- printk (KERN_DEBUG __FILE__ ": setup(8):");\r
- for (i = 0; i < 8 ; i++)\r
- printk (" %02x", ((__u8 *) urb->setup_packet) [i]);\r
- printk ("\n");\r
- }\r
- if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {\r
- printk (KERN_DEBUG __FILE__ ": data(%d/%d):",\r
- urb->actual_length,\r
- urb->transfer_buffer_length);\r
- len = usb_pipeout (pipe)?\r
- urb->transfer_buffer_length: urb->actual_length;\r
- for (i = 0; i < 16 && i < len; i++)\r
- printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);\r
- printk ("%s stat:%d\n", i < len? "...": "", urb->status);\r
- }\r
- }\r
-#endif\r
-}\r
-\r
-#define ohci_dbg_sw(ohci, next, size, format, arg...) \\r
- do { \\r
- if (next) { \\r
- unsigned s_len; \\r
- s_len = snprintf (*next, *size, format, ## arg ); \\r
- *size -= s_len; *next += s_len; \\r
- } else \\r
- ohci_dbg(ohci,format, ## arg ); \\r
- } while (0);\r
-\r
-\r
-static void ohci_dump_intr_mask (\r
- struct ohci_hcd *ohci,\r
- char *label,\r
- u32 mask,\r
- char **next,\r
- unsigned *size)\r
-{\r
- ohci_dbg_sw (ohci, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s\n",\r
- label,\r
- mask,\r
- (mask & OHCI_INTR_MIE) ? " MIE" : "",\r
- (mask & OHCI_INTR_OC) ? " OC" : "",\r
- (mask & OHCI_INTR_RHSC) ? " RHSC" : "",\r
- (mask & OHCI_INTR_FNO) ? " FNO" : "",\r
- (mask & OHCI_INTR_UE) ? " UE" : "",\r
- (mask & OHCI_INTR_RD) ? " RD" : "",\r
- (mask & OHCI_INTR_SF) ? " SF" : "",\r
- (mask & OHCI_INTR_WDH) ? " WDH" : "",\r
- (mask & OHCI_INTR_SO) ? " SO" : ""\r
- );\r
-}\r
-\r
-static void maybe_print_eds (\r
- struct ohci_hcd *ohci,\r
- char *label,\r
- u32 value,\r
- char **next,\r
- unsigned *size)\r
-{\r
- if (value)\r
- ohci_dbg_sw (ohci, next, size, "%s %08x\n", label, value);\r
-}\r
-\r
-static char *hcfs2string (int state)\r
-{\r
- switch (state) {\r
- case OHCI_USB_RESET: return "reset";\r
- case OHCI_USB_RESUME: return "resume";\r
- case OHCI_USB_OPER: return "operational";\r
- case OHCI_USB_SUSPEND: return "suspend";\r
- }\r
- return "?";\r
-}\r
-\r
-// dump control and status registers\r
-static void\r
-ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)\r
-{\r
- struct ohci_regs *regs = controller->regs;\r
- u32 temp;\r
-\r
- temp = readl (®s->revision) & 0xff;\r
- ohci_dbg_sw (controller, next, size,\r
- "OHCI %d.%d, %s legacy support registers\n",\r
- 0x03 & (temp >> 4), (temp & 0x0f),\r
- (temp & 0x10) ? "with" : "NO");\r
-\r
- temp = readl (®s->control);\r
- ohci_dbg_sw (controller, next, size,\r
- "control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n",\r
- temp,\r
- (temp & OHCI_CTRL_RWE) ? " RWE" : "",\r
- (temp & OHCI_CTRL_RWC) ? " RWC" : "",\r
- (temp & OHCI_CTRL_IR) ? " IR" : "",\r
- hcfs2string (temp & OHCI_CTRL_HCFS),\r
- (temp & OHCI_CTRL_BLE) ? " BLE" : "",\r
- (temp & OHCI_CTRL_CLE) ? " CLE" : "",\r
- (temp & OHCI_CTRL_IE) ? " IE" : "",\r
- (temp & OHCI_CTRL_PLE) ? " PLE" : "",\r
- temp & OHCI_CTRL_CBSR\r
- );\r
-\r
- temp = readl (®s->cmdstatus);\r
- ohci_dbg_sw (controller, next, size,\r
- "cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp,\r
- (temp & OHCI_SOC) >> 16,\r
- (temp & OHCI_OCR) ? " OCR" : "",\r
- (temp & OHCI_BLF) ? " BLF" : "",\r
- (temp & OHCI_CLF) ? " CLF" : "",\r
- (temp & OHCI_HCR) ? " HCR" : ""\r
- );\r
-\r
- ohci_dump_intr_mask (controller, "intrstatus",\r
- readl (®s->intrstatus), next, size);\r
- ohci_dump_intr_mask (controller, "intrenable",\r
- readl (®s->intrenable), next, size);\r
- // intrdisable always same as intrenable\r
-\r
- maybe_print_eds (controller, "ed_periodcurrent",\r
- readl (®s->ed_periodcurrent), next, size);\r
-\r
- maybe_print_eds (controller, "ed_controlhead",\r
- readl (®s->ed_controlhead), next, size);\r
- maybe_print_eds (controller, "ed_controlcurrent",\r
- readl (®s->ed_controlcurrent), next, size);\r
-\r
- maybe_print_eds (controller, "ed_bulkhead",\r
- readl (®s->ed_bulkhead), next, size);\r
- maybe_print_eds (controller, "ed_bulkcurrent",\r
- readl (®s->ed_bulkcurrent), next, size);\r
-\r
- maybe_print_eds (controller, "donehead",\r
- readl (®s->donehead), next, size);\r
-}\r
-\r
-#define dbg_port_sw(hc,num,value,next,size) \\r
- ohci_dbg_sw (hc, next, size, \\r
- "roothub.portstatus [%d] " \\r
- "0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \\r
- num, temp, \\r
- (temp & RH_PS_PRSC) ? " PRSC" : "", \\r
- (temp & RH_PS_OCIC) ? " OCIC" : "", \\r
- (temp & RH_PS_PSSC) ? " PSSC" : "", \\r
- (temp & RH_PS_PESC) ? " PESC" : "", \\r
- (temp & RH_PS_CSC) ? " CSC" : "", \\r
- \\r
- (temp & RH_PS_LSDA) ? " LSDA" : "", \\r
- (temp & RH_PS_PPS) ? " PPS" : "", \\r
- (temp & RH_PS_PRS) ? " PRS" : "", \\r
- (temp & RH_PS_POCI) ? " POCI" : "", \\r
- (temp & RH_PS_PSS) ? " PSS" : "", \\r
- \\r
- (temp & RH_PS_PES) ? " PES" : "", \\r
- (temp & RH_PS_CCS) ? " CCS" : "" \\r
- );\r
-\r
-\r
-static void\r
-ohci_dump_roothub (\r
- struct ohci_hcd *controller,\r
- int verbose,\r
- char **next,\r
- unsigned *size)\r
-{\r
- u32 temp, ndp, i;\r
-\r
- temp = roothub_a (controller);\r
- if (temp == ~(u32)0)\r
- return;\r
- ndp = (temp & RH_A_NDP);\r
-\r
- if (verbose) {\r
- ohci_dbg_sw (controller, next, size,\r
- "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,\r
- ((temp & RH_A_POTPGT) >> 24) & 0xff,\r
- (temp & RH_A_NOCP) ? " NOCP" : "",\r
- (temp & RH_A_OCPM) ? " OCPM" : "",\r
- (temp & RH_A_DT) ? " DT" : "",\r
- (temp & RH_A_NPS) ? " NPS" : "",\r
- (temp & RH_A_PSM) ? " PSM" : "",\r
- ndp\r
- );\r
- temp = roothub_b (controller);\r
- ohci_dbg_sw (controller, next, size,\r
- "roothub.b %08x PPCM=%04x DR=%04x\n",\r
- temp,\r
- (temp & RH_B_PPCM) >> 16,\r
- (temp & RH_B_DR)\r
- );\r
- temp = roothub_status (controller);\r
- ohci_dbg_sw (controller, next, size,\r
- "roothub.status %08x%s%s%s%s%s%s\n",\r
- temp,\r
- (temp & RH_HS_CRWE) ? " CRWE" : "",\r
- (temp & RH_HS_OCIC) ? " OCIC" : "",\r
- (temp & RH_HS_LPSC) ? " LPSC" : "",\r
- (temp & RH_HS_DRWE) ? " DRWE" : "",\r
- (temp & RH_HS_OCI) ? " OCI" : "",\r
- (temp & RH_HS_LPS) ? " LPS" : ""\r
- );\r
- }\r
-\r
- for (i = 0; i < ndp; i++) {\r
- temp = roothub_portstatus (controller, i);\r
- dbg_port_sw (controller, i, temp, next, size);\r
- }\r
-}\r
-\r
-static void ohci_dump (struct ohci_hcd *controller, int verbose)\r
-{\r
- ohci_dbg (controller, "OHCI controller state\n");\r
-\r
- // dumps some of the state we know about\r
- ohci_dump_status (controller, NULL, 0);\r
- if (controller->hcca)\r
- ohci_dbg (controller,\r
- "hcca frame #%04x\n", controller->hcca->frame_no);\r
- ohci_dump_roothub (controller, 1, NULL, 0);\r
-}\r
-\r
-static const char data0 [] = "DATA0";\r
-static const char data1 [] = "DATA1";\r
-\r
-static void ohci_dump_td (struct ohci_hcd *ohci, char *label, struct td *td)\r
-{\r
- u32 tmp = le32_to_cpup (&td->hwINFO);\r
-\r
- ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x",\r
- label, td,\r
- (tmp & TD_DONE) ? " (DONE)" : "",\r
- td->urb, td->index,\r
- le32_to_cpup (&td->hwNextTD));\r
- if ((tmp & TD_ISO) == 0) {\r
- const char *toggle, *pid;\r
- u32 cbp, be;\r
-\r
- switch (tmp & TD_T) {\r
- case TD_T_DATA0: toggle = data0; break;\r
- case TD_T_DATA1: toggle = data1; break;\r
- case TD_T_TOGGLE: toggle = "(CARRY)"; break;\r
- default: toggle = "(?)"; break;\r
- }\r
- switch (tmp & TD_DP) {\r
- case TD_DP_SETUP: pid = "SETUP"; break;\r
- case TD_DP_IN: pid = "IN"; break;\r
- case TD_DP_OUT: pid = "OUT"; break;\r
- default: pid = "(bad pid)"; break;\r
- }\r
- ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s", tmp,\r
- TD_CC_GET(tmp), /* EC, */ toggle,\r
- (tmp & TD_DI) >> 21, pid,\r
- (tmp & TD_R) ? "R" : "");\r
- cbp = le32_to_cpup (&td->hwCBP);\r
- be = le32_to_cpup (&td->hwBE);\r
- ohci_dbg (ohci, " cbp %08x be %08x (len %d)", cbp, be,\r
- cbp ? (be + 1 - cbp) : 0);\r
- } else {\r
- unsigned i;\r
- ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x", tmp,\r
- TD_CC_GET(tmp),\r
- (tmp >> 24) & 0x07,\r
- (tmp & TD_DI) >> 21,\r
- tmp & 0x0000ffff);\r
- ohci_dbg (ohci, " bp0 %08x be %08x",\r
- le32_to_cpup (&td->hwCBP) & ~0x0fff,\r
- le32_to_cpup (&td->hwBE));\r
- for (i = 0; i < MAXPSW; i++) {\r
- u16 psw = le16_to_cpup (&td->hwPSW [i]);\r
- int cc = (psw >> 12) & 0x0f;\r
- ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d", i,\r
- psw, cc,\r
- (cc >= 0x0e) ? "OFFSET" : "SIZE",\r
- psw & 0x0fff);\r
- }\r
- }\r
-}\r
-\r
-/* caller MUST own hcd spinlock if verbose is set! */\r
-static void __attribute__((unused))\r
-ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)\r
-{\r
- u32 tmp = ed->hwINFO;\r
- char *type = "";\r
-\r
- ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x",\r
- label,\r
- ed, ed->state, edstring (ed->type),\r
- le32_to_cpup (&ed->hwNextED));\r
- switch (tmp & (ED_IN|ED_OUT)) {\r
- case ED_OUT: type = "-OUT"; break;\r
- case ED_IN: type = "-IN"; break;\r
- /* else from TDs ... control */\r
- }\r
- ohci_dbg (ohci,\r
- " info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),\r
- 0x03ff & (le32_to_cpu (tmp) >> 16),\r
- (tmp & ED_DEQUEUE) ? " DQ" : "",\r
- (tmp & ED_ISO) ? " ISO" : "",\r
- (tmp & ED_SKIP) ? " SKIP" : "",\r
- (tmp & ED_LOWSPEED) ? " LOW" : "",\r
- 0x000f & (le32_to_cpu (tmp) >> 7),\r
- type,\r
- 0x007f & le32_to_cpu (tmp));\r
- ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s",\r
- tmp = le32_to_cpup (&ed->hwHeadP),\r
- (ed->hwHeadP & ED_C) ? data1 : data0,\r
- (ed->hwHeadP & ED_H) ? " HALT" : "",\r
- le32_to_cpup (&ed->hwTailP),\r
- verbose ? "" : " (not listing)");\r
- if (verbose) {\r
- struct list_head *tmp;\r
-\r
- /* use ed->td_list because HC concurrently modifies\r
- * hwNextTD as it accumulates ed_donelist.\r
- */\r
- list_for_each (tmp, &ed->td_list) {\r
- struct td *td;\r
- td = list_entry (tmp, struct td, td_list);\r
- ohci_dump_td (ohci, " ->", td);\r
- }\r
- }\r
-}\r
-\r
-#else\r
-static inline void ohci_dump (struct ohci_hcd *controller, int verbose) {}\r
-\r
-#undef OHCI_VERBOSE_DEBUG\r
-\r
-#endif /* DEBUG */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-#ifdef STUB_DEBUG_FILES\r
-\r
-static inline void create_debug_files (struct ohci_hcd *bus) { }\r
-static inline void remove_debug_files (struct ohci_hcd *bus) { }\r
-\r
-#else\r
-\r
-static inline struct ohci_hcd *dev_to_ohci (struct device *dev)\r
-{\r
- struct usb_hcd *hcd = dev_get_drvdata (dev);\r
-\r
- return hcd_to_ohci (hcd);\r
-}\r
-\r
-static ssize_t\r
-show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)\r
-{\r
- unsigned temp, size = count;\r
-\r
- if (!ed)\r
- return 0;\r
-\r
- /* print first --> last */\r
- while (ed->ed_prev)\r
- ed = ed->ed_prev;\r
-\r
- /* dump a snapshot of the bulk or control schedule */\r
- while (ed) {\r
- u32 info = ed->hwINFO;\r
- u32 scratch = cpu_to_le32p (&ed->hwINFO);\r
- struct list_head *entry;\r
- struct td *td;\r
-\r
- temp = snprintf (buf, size,\r
- "ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s",\r
- ed,\r
- (info & ED_LOWSPEED) ? 'l' : 'f',\r
- scratch & 0x7f,\r
- (scratch >> 7) & 0xf,\r
- (info & ED_IN) ? "in" : "out",\r
- 0x03ff & (scratch >> 16),\r
- scratch,\r
- (info & ED_SKIP) ? " s" : "",\r
- (ed->hwHeadP & ED_H) ? " H" : "",\r
- (ed->hwHeadP & ED_C) ? data1 : data0);\r
- size -= temp;\r
- buf += temp;\r
-\r
- list_for_each (entry, &ed->td_list) {\r
- u32 cbp, be;\r
-\r
- td = list_entry (entry, struct td, td_list);\r
- scratch = cpu_to_le32p (&td->hwINFO);\r
- cbp = le32_to_cpup (&td->hwCBP);\r
- be = le32_to_cpup (&td->hwBE);\r
- temp = snprintf (buf, size,\r
- "\n\ttd %p %s %d cc=%x urb %p (%08x)",\r
- td,\r
- ({ char *pid;\r
- switch (scratch & TD_DP) {\r
- case TD_DP_SETUP: pid = "setup"; break;\r
- case TD_DP_IN: pid = "in"; break;\r
- case TD_DP_OUT: pid = "out"; break;\r
- default: pid = "(?)"; break;\r
- } pid;}),\r
- cbp ? (be + 1 - cbp) : 0,\r
- TD_CC_GET (scratch), td->urb, scratch);\r
- size -= temp;\r
- buf += temp;\r
- }\r
-\r
- temp = snprintf (buf, size, "\n");\r
- size -= temp;\r
- buf += temp;\r
-\r
- ed = ed->ed_next;\r
- }\r
- return count - size;\r
-}\r
-\r
-static ssize_t\r
-show_async (struct device *dev, char *buf)\r
-{\r
- struct ohci_hcd *ohci;\r
- size_t temp;\r
- unsigned long flags;\r
-\r
- ohci = dev_to_ohci(dev);\r
-\r
- /* display control and bulk lists together, for simplicity */\r
- spin_lock_irqsave (&ohci->lock, flags);\r
- temp = show_list (ohci, buf, PAGE_SIZE, ohci->ed_controltail);\r
- temp += show_list (ohci, buf + temp, PAGE_SIZE - temp, ohci->ed_bulktail);\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
-\r
- return temp;\r
-}\r
-static DEVICE_ATTR (async, S_IRUGO, show_async, NULL);\r
-\r
-\r
-#define DBG_SCHED_LIMIT 64\r
-\r
-static ssize_t\r
-show_periodic (struct device *dev, char *buf)\r
-{\r
- struct ohci_hcd *ohci;\r
- struct ed **seen, *ed;\r
- unsigned long flags;\r
- unsigned temp, size, seen_count;\r
- char *next;\r
- unsigned i;\r
-\r
- if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))\r
- return 0;\r
- seen_count = 0;\r
-\r
- ohci = dev_to_ohci(dev);\r
- next = buf;\r
- size = PAGE_SIZE;\r
-\r
- temp = snprintf (next, size, "size = %d\n", NUM_INTS);\r
- size -= temp;\r
- next += temp;\r
-\r
- /* dump a snapshot of the periodic schedule (and load) */\r
- spin_lock_irqsave (&ohci->lock, flags);\r
- for (i = 0; i < NUM_INTS; i++) {\r
- if (!(ed = ohci->periodic [i]))\r
- continue;\r
-\r
- temp = snprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);\r
- size -= temp;\r
- next += temp;\r
-\r
- do {\r
- temp = snprintf (next, size, " ed%d/%p",\r
- ed->interval, ed);\r
- size -= temp;\r
- next += temp;\r
- for (temp = 0; temp < seen_count; temp++) {\r
- if (seen [temp] == ed)\r
- break;\r
- }\r
-\r
- /* show more info the first time around */\r
- if (temp == seen_count) {\r
- u32 info = ed->hwINFO;\r
- u32 scratch = cpu_to_le32p (&ed->hwINFO);\r
-\r
- temp = snprintf (next, size,\r
- " (%cs dev%d%s ep%d%s"\r
- " max %d %08x%s%s)",\r
- (info & ED_LOWSPEED) ? 'l' : 'f',\r
- scratch & 0x7f,\r
- (info & ED_ISO) ? " iso" : "",\r
- (scratch >> 7) & 0xf,\r
- (info & ED_IN) ? "in" : "out",\r
- 0x03ff & (scratch >> 16),\r
- scratch,\r
- (info & ED_SKIP) ? " s" : "",\r
- (ed->hwHeadP & ED_H) ? " H" : "");\r
- size -= temp;\r
- next += temp;\r
-\r
- // FIXME some TD info too\r
-\r
- if (seen_count < DBG_SCHED_LIMIT)\r
- seen [seen_count++] = ed;\r
-\r
- ed = ed->ed_next;\r
-\r
- } else {\r
- /* we've seen it and what's after */\r
- temp = 0;\r
- ed = 0;\r
- }\r
-\r
- } while (ed);\r
-\r
- temp = snprintf (next, size, "\n");\r
- size -= temp;\r
- next += temp;\r
- }\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
- kfree (seen);\r
-\r
- return PAGE_SIZE - size;\r
-}\r
-static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);\r
-\r
-\r
-#undef DBG_SCHED_LIMIT\r
-\r
-static ssize_t\r
-show_registers (struct device *dev, char *buf)\r
-{\r
- struct ohci_hcd *ohci;\r
- struct ohci_regs *regs;\r
- unsigned long flags;\r
- unsigned temp, size;\r
- char *next;\r
- u32 rdata;\r
-\r
- ohci = dev_to_ohci(dev);\r
- regs = ohci->regs;\r
- next = buf;\r
- size = PAGE_SIZE;\r
-\r
- spin_lock_irqsave (&ohci->lock, flags);\r
-\r
- /* dump driver info, then registers in spec order */\r
-\r
- ohci_dbg_sw (ohci, &next, &size,\r
- "%s version " DRIVER_VERSION "\n", hcd_name);\r
-\r
- ohci_dump_status(ohci, &next, &size);\r
-\r
- /* hcca */\r
- if (ohci->hcca)\r
- ohci_dbg_sw (ohci, &next, &size,\r
- "hcca frame 0x%04x\n", ohci->hcca->frame_no);\r
-\r
- /* other registers mostly affect frame timings */\r
- rdata = readl (®s->fminterval);\r
- temp = snprintf (next, size,\r
- "fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",\r
- rdata, (rdata >> 31) ? " FIT" : "",\r
- (rdata >> 16) & 0xefff, rdata & 0xffff);\r
- size -= temp;\r
- next += temp;\r
-\r
- rdata = readl (®s->fmremaining);\r
- temp = snprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n",\r
- rdata, (rdata >> 31) ? " FRT" : "",\r
- rdata & 0x3fff);\r
- size -= temp;\r
- next += temp;\r
-\r
- rdata = readl (®s->periodicstart);\r
- temp = snprintf (next, size, "periodicstart 0x%04x\n",\r
- rdata & 0x3fff);\r
- size -= temp;\r
- next += temp;\r
-\r
- rdata = readl (®s->lsthresh);\r
- temp = snprintf (next, size, "lsthresh 0x%04x\n",\r
- rdata & 0x3fff);\r
- size -= temp;\r
- next += temp;\r
-\r
- /* roothub */\r
- ohci_dump_roothub (ohci, 1, &next, &size);\r
-\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
-\r
- return PAGE_SIZE - size;\r
-}\r
-static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);\r
-\r
-\r
-static inline void create_debug_files (struct ohci_hcd *bus)\r
-{\r
- device_create_file (bus->hcd.controller, &dev_attr_async);\r
- device_create_file (bus->hcd.controller, &dev_attr_periodic);\r
- device_create_file (bus->hcd.controller, &dev_attr_registers);\r
- ohci_dbg (bus, "created debug files\n");\r
-}\r
-\r
-static inline void remove_debug_files (struct ohci_hcd *bus)\r
-{\r
- device_remove_file (bus->hcd.controller, &dev_attr_async);\r
- device_remove_file (bus->hcd.controller, &dev_attr_periodic);\r
- device_remove_file (bus->hcd.controller, &dev_attr_registers);\r
-}\r
-\r
-#endif\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+
+#define edstring(ed_type) ({ char *temp; \
+ switch (ed_type) { \
+ case PIPE_CONTROL: temp = "ctrl"; break; \
+ case PIPE_BULK: temp = "bulk"; break; \
+ case PIPE_INTERRUPT: temp = "intr"; break; \
+ default: temp = "isoc"; break; \
+ }; temp;})
+#define pipestring(pipe) edstring(usb_pipetype(pipe))
+
+/* debug| print the main components of an URB
+ * small: 0) header + data packets 1) just header
+ */
+static void __attribute__((unused))
+urb_print (struct urb * urb, char * str, int small)
+{
+ unsigned int pipe= urb->pipe;
+
+ if (!urb->dev || !urb->dev->bus) {
+ dbg("%s URB: no dev", str);
+ return;
+ }
+
+#ifndef OHCI_VERBOSE_DEBUG
+ if (urb->status != 0)
+#endif
+ dbg("%s %p dev=%d ep=%d%s-%s flags=%x len=%d/%d stat=%d",
+ str,
+ urb,
+ usb_pipedevice (pipe),
+ usb_pipeendpoint (pipe),
+ usb_pipeout (pipe)? "out" : "in",
+ pipestring (pipe),
+ urb->transfer_flags,
+ urb->actual_length,
+ urb->transfer_buffer_length,
+ urb->status);
+
+#ifdef OHCI_VERBOSE_DEBUG
+ if (!small) {
+ int i, len;
+
+ if (usb_pipecontrol (pipe)) {
+ printk (KERN_DEBUG __FILE__ ": setup(8):");
+ for (i = 0; i < 8 ; i++)
+ printk (" %02x", ((__u8 *) urb->setup_packet) [i]);
+ printk ("\n");
+ }
+ if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) {
+ printk (KERN_DEBUG __FILE__ ": data(%d/%d):",
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ len = usb_pipeout (pipe)?
+ urb->transfer_buffer_length: urb->actual_length;
+ for (i = 0; i < 16 && i < len; i++)
+ printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]);
+ printk ("%s stat:%d\n", i < len? "...": "", urb->status);
+ }
+ }
+#endif
+}
+
+#define ohci_dbg_sw(ohci, next, size, format, arg...) \
+ do { \
+ if (next) { \
+ unsigned s_len; \
+ s_len = snprintf (*next, *size, format, ## arg ); \
+ *size -= s_len; *next += s_len; \
+ } else \
+ ohci_dbg(ohci,format, ## arg ); \
+ } while (0);
+
+
+static void ohci_dump_intr_mask (
+ struct ohci_hcd *ohci,
+ char *label,
+ u32 mask,
+ char **next,
+ unsigned *size)
+{
+ ohci_dbg_sw (ohci, next, size, "%s 0x%08x%s%s%s%s%s%s%s%s%s\n",
+ label,
+ mask,
+ (mask & OHCI_INTR_MIE) ? " MIE" : "",
+ (mask & OHCI_INTR_OC) ? " OC" : "",
+ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+ (mask & OHCI_INTR_FNO) ? " FNO" : "",
+ (mask & OHCI_INTR_UE) ? " UE" : "",
+ (mask & OHCI_INTR_RD) ? " RD" : "",
+ (mask & OHCI_INTR_SF) ? " SF" : "",
+ (mask & OHCI_INTR_WDH) ? " WDH" : "",
+ (mask & OHCI_INTR_SO) ? " SO" : ""
+ );
+}
+
+static void maybe_print_eds (
+ struct ohci_hcd *ohci,
+ char *label,
+ u32 value,
+ char **next,
+ unsigned *size)
+{
+ if (value)
+ ohci_dbg_sw (ohci, next, size, "%s %08x\n", label, value);
+}
+
+static char *hcfs2string (int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+ return "?";
+}
+
+// dump control and status registers
+static void
+ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
+{
+ struct ohci_regs *regs = controller->regs;
+ u32 temp;
+
+ temp = readl (®s->revision) & 0xff;
+ ohci_dbg_sw (controller, next, size,
+ "OHCI %d.%d, %s legacy support registers\n",
+ 0x03 & (temp >> 4), (temp & 0x0f),
+ (temp & 0x10) ? "with" : "NO");
+
+ temp = readl (®s->control);
+ ohci_dbg_sw (controller, next, size,
+ "control 0x%03x%s%s%s HCFS=%s%s%s%s%s CBSR=%d\n",
+ temp,
+ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
+ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
+ (temp & OHCI_CTRL_IR) ? " IR" : "",
+ hcfs2string (temp & OHCI_CTRL_HCFS),
+ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
+ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
+ (temp & OHCI_CTRL_IE) ? " IE" : "",
+ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
+ temp & OHCI_CTRL_CBSR
+ );
+
+ temp = readl (®s->cmdstatus);
+ ohci_dbg_sw (controller, next, size,
+ "cmdstatus 0x%05x SOC=%d%s%s%s%s\n", temp,
+ (temp & OHCI_SOC) >> 16,
+ (temp & OHCI_OCR) ? " OCR" : "",
+ (temp & OHCI_BLF) ? " BLF" : "",
+ (temp & OHCI_CLF) ? " CLF" : "",
+ (temp & OHCI_HCR) ? " HCR" : ""
+ );
+
+ ohci_dump_intr_mask (controller, "intrstatus",
+ readl (®s->intrstatus), next, size);
+ ohci_dump_intr_mask (controller, "intrenable",
+ readl (®s->intrenable), next, size);
+ // intrdisable always same as intrenable
+
+ maybe_print_eds (controller, "ed_periodcurrent",
+ readl (®s->ed_periodcurrent), next, size);
+
+ maybe_print_eds (controller, "ed_controlhead",
+ readl (®s->ed_controlhead), next, size);
+ maybe_print_eds (controller, "ed_controlcurrent",
+ readl (®s->ed_controlcurrent), next, size);
+
+ maybe_print_eds (controller, "ed_bulkhead",
+ readl (®s->ed_bulkhead), next, size);
+ maybe_print_eds (controller, "ed_bulkcurrent",
+ readl (®s->ed_bulkcurrent), next, size);
+
+ maybe_print_eds (controller, "donehead",
+ readl (®s->donehead), next, size);
+}
+
+#define dbg_port_sw(hc,num,value,next,size) \
+ ohci_dbg_sw (hc, next, size, \
+ "roothub.portstatus [%d] " \
+ "0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
+ num, temp, \
+ (temp & RH_PS_PRSC) ? " PRSC" : "", \
+ (temp & RH_PS_OCIC) ? " OCIC" : "", \
+ (temp & RH_PS_PSSC) ? " PSSC" : "", \
+ (temp & RH_PS_PESC) ? " PESC" : "", \
+ (temp & RH_PS_CSC) ? " CSC" : "", \
+ \
+ (temp & RH_PS_LSDA) ? " LSDA" : "", \
+ (temp & RH_PS_PPS) ? " PPS" : "", \
+ (temp & RH_PS_PRS) ? " PRS" : "", \
+ (temp & RH_PS_POCI) ? " POCI" : "", \
+ (temp & RH_PS_PSS) ? " PSS" : "", \
+ \
+ (temp & RH_PS_PES) ? " PES" : "", \
+ (temp & RH_PS_CCS) ? " CCS" : "" \
+ );
+
+
+static void
+ohci_dump_roothub (
+ struct ohci_hcd *controller,
+ int verbose,
+ char **next,
+ unsigned *size)
+{
+ u32 temp, ndp, i;
+
+ temp = roothub_a (controller);
+ if (temp == ~(u32)0)
+ return;
+ ndp = (temp & RH_A_NDP);
+
+ if (verbose) {
+ ohci_dbg_sw (controller, next, size,
+ "roothub.a %08x POTPGT=%d%s%s%s%s%s NDP=%d\n", temp,
+ ((temp & RH_A_POTPGT) >> 24) & 0xff,
+ (temp & RH_A_NOCP) ? " NOCP" : "",
+ (temp & RH_A_OCPM) ? " OCPM" : "",
+ (temp & RH_A_DT) ? " DT" : "",
+ (temp & RH_A_NPS) ? " NPS" : "",
+ (temp & RH_A_PSM) ? " PSM" : "",
+ ndp
+ );
+ temp = roothub_b (controller);
+ ohci_dbg_sw (controller, next, size,
+ "roothub.b %08x PPCM=%04x DR=%04x\n",
+ temp,
+ (temp & RH_B_PPCM) >> 16,
+ (temp & RH_B_DR)
+ );
+ temp = roothub_status (controller);
+ ohci_dbg_sw (controller, next, size,
+ "roothub.status %08x%s%s%s%s%s%s\n",
+ temp,
+ (temp & RH_HS_CRWE) ? " CRWE" : "",
+ (temp & RH_HS_OCIC) ? " OCIC" : "",
+ (temp & RH_HS_LPSC) ? " LPSC" : "",
+ (temp & RH_HS_DRWE) ? " DRWE" : "",
+ (temp & RH_HS_OCI) ? " OCI" : "",
+ (temp & RH_HS_LPS) ? " LPS" : ""
+ );
+ }
+
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus (controller, i);
+ dbg_port_sw (controller, i, temp, next, size);
+ }
+}
+
+static void ohci_dump (struct ohci_hcd *controller, int verbose)
+{
+ ohci_dbg (controller, "OHCI controller state\n");
+
+ // dumps some of the state we know about
+ ohci_dump_status (controller, NULL, 0);
+ if (controller->hcca)
+ ohci_dbg (controller,
+ "hcca frame #%04x\n", controller->hcca->frame_no);
+ ohci_dump_roothub (controller, 1, NULL, 0);
+}
+
+static const char data0 [] = "DATA0";
+static const char data1 [] = "DATA1";
+
+static void ohci_dump_td (struct ohci_hcd *ohci, char *label, struct td *td)
+{
+ u32 tmp = le32_to_cpup (&td->hwINFO);
+
+ ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x",
+ label, td,
+ (tmp & TD_DONE) ? " (DONE)" : "",
+ td->urb, td->index,
+ le32_to_cpup (&td->hwNextTD));
+ if ((tmp & TD_ISO) == 0) {
+ const char *toggle, *pid;
+ u32 cbp, be;
+
+ switch (tmp & TD_T) {
+ case TD_T_DATA0: toggle = data0; break;
+ case TD_T_DATA1: toggle = data1; break;
+ case TD_T_TOGGLE: toggle = "(CARRY)"; break;
+ default: toggle = "(?)"; break;
+ }
+ switch (tmp & TD_DP) {
+ case TD_DP_SETUP: pid = "SETUP"; break;
+ case TD_DP_IN: pid = "IN"; break;
+ case TD_DP_OUT: pid = "OUT"; break;
+ default: pid = "(bad pid)"; break;
+ }
+ ohci_dbg (ohci, " info %08x CC=%x %s DI=%d %s %s", tmp,
+ TD_CC_GET(tmp), /* EC, */ toggle,
+ (tmp & TD_DI) >> 21, pid,
+ (tmp & TD_R) ? "R" : "");
+ cbp = le32_to_cpup (&td->hwCBP);
+ be = le32_to_cpup (&td->hwBE);
+ ohci_dbg (ohci, " cbp %08x be %08x (len %d)", cbp, be,
+ cbp ? (be + 1 - cbp) : 0);
+ } else {
+ unsigned i;
+ ohci_dbg (ohci, " info %08x CC=%x FC=%d DI=%d SF=%04x", tmp,
+ TD_CC_GET(tmp),
+ (tmp >> 24) & 0x07,
+ (tmp & TD_DI) >> 21,
+ tmp & 0x0000ffff);
+ ohci_dbg (ohci, " bp0 %08x be %08x",
+ le32_to_cpup (&td->hwCBP) & ~0x0fff,
+ le32_to_cpup (&td->hwBE));
+ for (i = 0; i < MAXPSW; i++) {
+ u16 psw = le16_to_cpup (&td->hwPSW [i]);
+ int cc = (psw >> 12) & 0x0f;
+ ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d", i,
+ psw, cc,
+ (cc >= 0x0e) ? "OFFSET" : "SIZE",
+ psw & 0x0fff);
+ }
+ }
+}
+
+/* caller MUST own hcd spinlock if verbose is set! */
+static void __attribute__((unused))
+ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
+{
+ u32 tmp = ed->hwINFO;
+ char *type = "";
+
+ ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x",
+ label,
+ ed, ed->state, edstring (ed->type),
+ le32_to_cpup (&ed->hwNextED));
+ switch (tmp & (ED_IN|ED_OUT)) {
+ case ED_OUT: type = "-OUT"; break;
+ case ED_IN: type = "-IN"; break;
+ /* else from TDs ... control */
+ }
+ ohci_dbg (ohci,
+ " info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
+ 0x03ff & (le32_to_cpu (tmp) >> 16),
+ (tmp & ED_DEQUEUE) ? " DQ" : "",
+ (tmp & ED_ISO) ? " ISO" : "",
+ (tmp & ED_SKIP) ? " SKIP" : "",
+ (tmp & ED_LOWSPEED) ? " LOW" : "",
+ 0x000f & (le32_to_cpu (tmp) >> 7),
+ type,
+ 0x007f & le32_to_cpu (tmp));
+ ohci_dbg (ohci, " tds: head %08x %s%s tail %08x%s",
+ tmp = le32_to_cpup (&ed->hwHeadP),
+ (ed->hwHeadP & ED_C) ? data1 : data0,
+ (ed->hwHeadP & ED_H) ? " HALT" : "",
+ le32_to_cpup (&ed->hwTailP),
+ verbose ? "" : " (not listing)");
+ if (verbose) {
+ struct list_head *tmp;
+
+ /* use ed->td_list because HC concurrently modifies
+ * hwNextTD as it accumulates ed_donelist.
+ */
+ list_for_each (tmp, &ed->td_list) {
+ struct td *td;
+ td = list_entry (tmp, struct td, td_list);
+ ohci_dump_td (ohci, " ->", td);
+ }
+ }
+}
+
+#else
+static inline void ohci_dump (struct ohci_hcd *controller, int verbose) {}
+
+#undef OHCI_VERBOSE_DEBUG
+
+#endif /* DEBUG */
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILES
+
+static inline void create_debug_files (struct ohci_hcd *bus) { }
+static inline void remove_debug_files (struct ohci_hcd *bus) { }
+
+#else
+
+static inline struct ohci_hcd *dev_to_ohci (struct device *dev)
+{
+ struct usb_hcd *hcd = dev_get_drvdata (dev);
+
+ return hcd_to_ohci (hcd);
+}
+
+static ssize_t
+show_list (struct ohci_hcd *ohci, char *buf, size_t count, struct ed *ed)
+{
+ unsigned temp, size = count;
+
+ if (!ed)
+ return 0;
+
+ /* print first --> last */
+ while (ed->ed_prev)
+ ed = ed->ed_prev;
+
+ /* dump a snapshot of the bulk or control schedule */
+ while (ed) {
+ u32 info = ed->hwINFO;
+ u32 scratch = cpu_to_le32p (&ed->hwINFO);
+ struct list_head *entry;
+ struct td *td;
+
+ temp = snprintf (buf, size,
+ "ed/%p %cs dev%d ep%d%s max %d %08x%s%s %s",
+ ed,
+ (info & ED_LOWSPEED) ? 'l' : 'f',
+ scratch & 0x7f,
+ (scratch >> 7) & 0xf,
+ (info & ED_IN) ? "in" : "out",
+ 0x03ff & (scratch >> 16),
+ scratch,
+ (info & ED_SKIP) ? " s" : "",
+ (ed->hwHeadP & ED_H) ? " H" : "",
+ (ed->hwHeadP & ED_C) ? data1 : data0);
+ size -= temp;
+ buf += temp;
+
+ list_for_each (entry, &ed->td_list) {
+ u32 cbp, be;
+
+ td = list_entry (entry, struct td, td_list);
+ scratch = cpu_to_le32p (&td->hwINFO);
+ cbp = le32_to_cpup (&td->hwCBP);
+ be = le32_to_cpup (&td->hwBE);
+ temp = snprintf (buf, size,
+ "\n\ttd %p %s %d cc=%x urb %p (%08x)",
+ td,
+ ({ char *pid;
+ switch (scratch & TD_DP) {
+ case TD_DP_SETUP: pid = "setup"; break;
+ case TD_DP_IN: pid = "in"; break;
+ case TD_DP_OUT: pid = "out"; break;
+ default: pid = "(?)"; break;
+ } pid;}),
+ cbp ? (be + 1 - cbp) : 0,
+ TD_CC_GET (scratch), td->urb, scratch);
+ size -= temp;
+ buf += temp;
+ }
+
+ temp = snprintf (buf, size, "\n");
+ size -= temp;
+ buf += temp;
+
+ ed = ed->ed_next;
+ }
+ return count - size;
+}
+
+static ssize_t
+show_async (struct device *dev, char *buf)
+{
+ struct ohci_hcd *ohci;
+ size_t temp;
+ unsigned long flags;
+
+ ohci = dev_to_ohci(dev);
+
+ /* display control and bulk lists together, for simplicity */
+ spin_lock_irqsave (&ohci->lock, flags);
+ temp = show_list (ohci, buf, PAGE_SIZE, ohci->ed_controltail);
+ temp += show_list (ohci, buf + temp, PAGE_SIZE - temp, ohci->ed_bulktail);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ return temp;
+}
+static DEVICE_ATTR (async, S_IRUGO, show_async, NULL);
+
+
+#define DBG_SCHED_LIMIT 64
+
+static ssize_t
+show_periodic (struct device *dev, char *buf)
+{
+ struct ohci_hcd *ohci;
+ struct ed **seen, *ed;
+ unsigned long flags;
+ unsigned temp, size, seen_count;
+ char *next;
+ unsigned i;
+
+ if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC)))
+ return 0;
+ seen_count = 0;
+
+ ohci = dev_to_ohci(dev);
+ next = buf;
+ size = PAGE_SIZE;
+
+ temp = snprintf (next, size, "size = %d\n", NUM_INTS);
+ size -= temp;
+ next += temp;
+
+ /* dump a snapshot of the periodic schedule (and load) */
+ spin_lock_irqsave (&ohci->lock, flags);
+ for (i = 0; i < NUM_INTS; i++) {
+ if (!(ed = ohci->periodic [i]))
+ continue;
+
+ temp = snprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);
+ size -= temp;
+ next += temp;
+
+ do {
+ temp = snprintf (next, size, " ed%d/%p",
+ ed->interval, ed);
+ size -= temp;
+ next += temp;
+ for (temp = 0; temp < seen_count; temp++) {
+ if (seen [temp] == ed)
+ break;
+ }
+
+ /* show more info the first time around */
+ if (temp == seen_count) {
+ u32 info = ed->hwINFO;
+ u32 scratch = cpu_to_le32p (&ed->hwINFO);
+
+ temp = snprintf (next, size,
+ " (%cs dev%d%s ep%d%s"
+ " max %d %08x%s%s)",
+ (info & ED_LOWSPEED) ? 'l' : 'f',
+ scratch & 0x7f,
+ (info & ED_ISO) ? " iso" : "",
+ (scratch >> 7) & 0xf,
+ (info & ED_IN) ? "in" : "out",
+ 0x03ff & (scratch >> 16),
+ scratch,
+ (info & ED_SKIP) ? " s" : "",
+ (ed->hwHeadP & ED_H) ? " H" : "");
+ size -= temp;
+ next += temp;
+
+ // FIXME some TD info too
+
+ if (seen_count < DBG_SCHED_LIMIT)
+ seen [seen_count++] = ed;
+
+ ed = ed->ed_next;
+
+ } else {
+ /* we've seen it and what's after */
+ temp = 0;
+ ed = 0;
+ }
+
+ } while (ed);
+
+ temp = snprintf (next, size, "\n");
+ size -= temp;
+ next += temp;
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ kfree (seen);
+
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (periodic, S_IRUGO, show_periodic, NULL);
+
+
+#undef DBG_SCHED_LIMIT
+
+static ssize_t
+show_registers (struct device *dev, char *buf)
+{
+ struct ohci_hcd *ohci;
+ struct ohci_regs *regs;
+ unsigned long flags;
+ unsigned temp, size;
+ char *next;
+ u32 rdata;
+
+ ohci = dev_to_ohci(dev);
+ regs = ohci->regs;
+ next = buf;
+ size = PAGE_SIZE;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ /* dump driver info, then registers in spec order */
+
+ ohci_dbg_sw (ohci, &next, &size,
+ "%s version " DRIVER_VERSION "\n", hcd_name);
+
+ ohci_dump_status(ohci, &next, &size);
+
+ /* hcca */
+ if (ohci->hcca)
+ ohci_dbg_sw (ohci, &next, &size,
+ "hcca frame 0x%04x\n", ohci->hcca->frame_no);
+
+ /* other registers mostly affect frame timings */
+ rdata = readl (®s->fminterval);
+ temp = snprintf (next, size,
+ "fmintvl 0x%08x %sFSMPS=0x%04x FI=0x%04x\n",
+ rdata, (rdata >> 31) ? " FIT" : "",
+ (rdata >> 16) & 0xefff, rdata & 0xffff);
+ size -= temp;
+ next += temp;
+
+ rdata = readl (®s->fmremaining);
+ temp = snprintf (next, size, "fmremaining 0x%08x %sFR=0x%04x\n",
+ rdata, (rdata >> 31) ? " FRT" : "",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ rdata = readl (®s->periodicstart);
+ temp = snprintf (next, size, "periodicstart 0x%04x\n",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ rdata = readl (®s->lsthresh);
+ temp = snprintf (next, size, "lsthresh 0x%04x\n",
+ rdata & 0x3fff);
+ size -= temp;
+ next += temp;
+
+ /* roothub */
+ ohci_dump_roothub (ohci, 1, &next, &size);
+
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
+
+
+static inline void create_debug_files (struct ohci_hcd *bus)
+{
+ device_create_file (bus->hcd.controller, &dev_attr_async);
+ device_create_file (bus->hcd.controller, &dev_attr_periodic);
+ device_create_file (bus->hcd.controller, &dev_attr_registers);
+ ohci_dbg (bus, "created debug files\n");
+}
+
+static inline void remove_debug_files (struct ohci_hcd *bus)
+{
+ device_remove_file (bus->hcd.controller, &dev_attr_async);
+ device_remove_file (bus->hcd.controller, &dev_attr_periodic);
+ device_remove_file (bus->hcd.controller, &dev_attr_registers);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
-/*\r
- * OHCI HCD (Host Controller Driver) for USB.\r
- *\r
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>\r
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>\r
- * \r
- * [ Initialisation is based on Linus' ]\r
- * [ uhci code and gregs ohci fragments ]\r
- * [ (C) Copyright 1999 Linus Torvalds ]\r
- * [ (C) Copyright 1999 Gregory P. Smith]\r
- * \r
- * \r
- * OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller\r
- * interfaces (though some non-x86 Intel chips use it). It supports\r
- * smarter hardware than UHCI. A download link for the spec available\r
- * through the http://www.usb.org website.\r
- *\r
- * History:\r
- * \r
- * 2003/02/24 show registers in sysfs (Kevin Brosius)\r
- *\r
- * 2002/09/03 get rid of ed hashtables, rework periodic scheduling and\r
- * bandwidth accounting; if debugging, show schedules in driverfs\r
- * 2002/07/19 fixes to management of ED and schedule state.\r
- * 2002/06/09 SA-1111 support (Christopher Hoover)\r
- * 2002/06/01 remember frame when HC won't see EDs any more; use that info\r
- * to fix urb unlink races caused by interrupt latency assumptions;\r
- * minor ED field and function naming updates\r
- * 2002/01/18 package as a patch for 2.5.3; this should match the\r
- * 2.4.17 kernel modulo some bugs being fixed.\r
- *\r
- * 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes\r
- * from post-2.4.5 patches.\r
- * 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning\r
- * 2001/09/07 match PCI PM changes, errnos from Linus' tree\r
- * 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;\r
- * pbook pci quirks gone (please fix pbook pci sw!) (db)\r
- *\r
- * 2001/04/08 Identify version on module load (gb)\r
- * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);\r
- pci_map_single (db)\r
- * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)\r
- * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)\r
- *\r
- * 2000/09/26 fixed races in removing the private portion of the urb\r
- * 2000/09/07 disable bulk and control lists when unlinking the last\r
- * endpoint descriptor in order to avoid unrecoverable errors on\r
- * the Lucent chips. (rwc@sgi)\r
- * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some\r
- * urb unlink probs, indentation fixes\r
- * 2000/08/11 various oops fixes mostly affecting iso and cleanup from\r
- * device unplugs.\r
- * 2000/06/28 use PCI hotplug framework, for better power management\r
- * and for Cardbus support (David Brownell)\r
- * 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling\r
- * when the controller loses power; handle UE; cleanup; ...\r
- *\r
- * v5.2 1999/12/07 URB 3rd preview, \r
- * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)\r
- * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume \r
- * i386: HUB, Keyboard, Mouse, Printer \r
- *\r
- * v4.3 1999/10/27 multiple HCs, bulk_request\r
- * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes\r
- * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.\r
- * v4.0 1999/08/18 \r
- * v3.0 1999/06/25 \r
- * v2.1 1999/05/09 code clean up\r
- * v2.0 1999/05/04 \r
- * v1.0 1999/04/27 initial release\r
- *\r
- * This file is licenced under the GPL.\r
- */\r
-\r
-#if 0 \r
-#include <linux/config.h>\r
-\r
-#ifdef CONFIG_USB_DEBUG\r
-# define DEBUG\r
-#else\r
-# undef DEBUG\r
-#endif\r
-\r
-\r
-\r
-#include <linux/module.h>\r
-#include <linux/pci.h>\r
-#include <linux/kernel.h>\r
-#include <linux/delay.h>\r
-#include <linux/ioport.h>\r
-#include <linux/sched.h>\r
-#include <linux/slab.h>\r
-#include <linux/smp_lock.h>\r
-#include <linux/errno.h>\r
-#include <linux/init.h>\r
-#include <linux/timer.h>\r
-#include <linux/list.h>\r
-#include <linux/interrupt.h> /* for in_interrupt () */\r
-#include <linux/usb.h>\r
-#include "../core/hcd.h"\r
-\r
-#include <asm/io.h>\r
-#include <asm/irq.h>\r
-#include <asm/system.h>\r
-#include <asm/unaligned.h>\r
-#include <asm/byteorder.h>\r
-#else\r
-#include "ohci_config.h"\r
-\r
-#include "../usb_wrapper.h"\r
-#include "../core/hcd.h"\r
-\r
-//#define OHCI_VERBOSE_DEBUG\r
-#endif\r
-\r
-/*\r
- * TO DO:\r
- *\r
- * - "disabled" and "sleeping" should be in hcd->state\r
- * - lots more testing!!\r
- */\r
-\r
-#define DRIVER_VERSION "2003 Feb 24"\r
-#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"\r
-#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-// #define OHCI_VERBOSE_DEBUG /* not always helpful */\r
-\r
-/* For initializing controller (mask in an HCFS mode too) */\r
-#define OHCI_CONTROL_INIT \\r
- (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE\r
-\r
-#define OHCI_UNLINK_TIMEOUT (HZ / 10)\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static const char hcd_name [] = "ohci-hcd";\r
-\r
-#include "ohci.h"\r
-\r
-static inline void disable (struct ohci_hcd *ohci)\r
-{\r
- ohci->disabled = 1;\r
- ohci->hcd.state = USB_STATE_HALT;\r
-}\r
-\r
-#include "ohci-hub.c"\r
-#include "ohci-dbg.c"\r
-#include "ohci-mem.c"\r
-#include "ohci-q.c"\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * queue up an urb for anything except the root hub\r
- */\r
-static int ohci_urb_enqueue (\r
- struct usb_hcd *hcd,\r
- struct urb *urb,\r
- int mem_flags\r
-) {\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- struct ed *ed;\r
- urb_priv_t *urb_priv;\r
- unsigned int pipe = urb->pipe;\r
- int i, size = 0;\r
- unsigned long flags;\r
- int retval = 0;\r
- \r
-#ifdef OHCI_VERBOSE_DEBUG\r
- urb_print (urb, "SUB", usb_pipein (pipe));\r
-#endif\r
- \r
- /* every endpoint has a ed, locate and maybe (re)initialize it */\r
- if (! (ed = ed_get (ohci, urb->dev, pipe, urb->interval)))\r
- return -ENOMEM;\r
-\r
- /* for the private part of the URB we need the number of TDs (size) */\r
- switch (ed->type) {\r
- case PIPE_CONTROL:\r
- /* td_submit_urb() doesn't yet handle these */\r
- if (urb->transfer_buffer_length > 4096)\r
- return -EMSGSIZE;\r
-\r
- /* 1 TD for setup, 1 for ACK, plus ... */\r
- size = 2;\r
- /* FALLTHROUGH */\r
- // case PIPE_INTERRUPT:\r
- // case PIPE_BULK:\r
- default:\r
- /* one TD for every 4096 Bytes (can be upto 8K) */\r
- size += urb->transfer_buffer_length / 4096;\r
- /* ... and for any remaining bytes ... */\r
- if ((urb->transfer_buffer_length % 4096) != 0)\r
- size++;\r
- /* ... and maybe a zero length packet to wrap it up */\r
- if (size == 0)\r
- size++;\r
- else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0\r
- && (urb->transfer_buffer_length\r
- % usb_maxpacket (urb->dev, pipe,\r
- usb_pipeout (pipe))) == 0)\r
- size++;\r
- break;\r
- case PIPE_ISOCHRONOUS: /* number of packets from URB */\r
- size = urb->number_of_packets;\r
- break;\r
- }\r
-\r
- /* allocate the private part of the URB */\r
- urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),\r
- mem_flags);\r
- if (!urb_priv)\r
- return -ENOMEM;\r
- memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));\r
-\r
- /* fill the private part of the URB */\r
- urb_priv->length = size;\r
- urb_priv->ed = ed; \r
-\r
- /* allocate the TDs (deferring hash chain updates) */\r
- for (i = 0; i < size; i++) {\r
- urb_priv->td [i] = td_alloc (ohci, mem_flags);\r
- if (!urb_priv->td [i]) {\r
- urb_priv->length = i;\r
- urb_free_priv (ohci, urb_priv);\r
- return -ENOMEM;\r
- }\r
- } \r
-\r
- spin_lock_irqsave (&ohci->lock, flags);\r
-\r
- /* don't submit to a dead HC */\r
- if (ohci->disabled || ohci->sleeping) {\r
- retval = -ENODEV;\r
- goto fail;\r
- }\r
-\r
- /* schedule the ed if needed */\r
- if (ed->state == ED_IDLE) {\r
- retval = ed_schedule (ohci, ed);\r
- if (retval < 0)\r
- goto fail;\r
- if (ed->type == PIPE_ISOCHRONOUS) {\r
- u16 frame = le16_to_cpu (ohci->hcca->frame_no);\r
-\r
- /* delay a few frames before the first TD */\r
- frame += max_t (u16, 8, ed->interval);\r
- frame &= ~(ed->interval - 1);\r
- frame |= ed->branch;\r
- urb->start_frame = frame;\r
-\r
- /* yes, only URB_ISO_ASAP is supported, and\r
- * urb->start_frame is never used as input.\r
- */\r
- }\r
- } else if (ed->type == PIPE_ISOCHRONOUS)\r
- urb->start_frame = ed->last_iso + ed->interval;\r
-\r
- /* fill the TDs and link them to the ed; and\r
- * enable that part of the schedule, if needed\r
- * and update count of queued periodic urbs\r
- */\r
- urb->hcpriv = urb_priv;\r
- td_submit_urb (ohci, urb);\r
-\r
-fail:\r
- if (retval)\r
- urb_free_priv (ohci, urb_priv);\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
- return retval;\r
-}\r
-\r
-/*\r
- * decouple the URB from the HC queues (TDs, urb_priv); it's\r
- * already marked using urb->status. reporting is always done\r
- * asynchronously, and we might be dealing with an urb that's\r
- * partially transferred, or an ED with other urbs being unlinked.\r
- */\r
-static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)\r
-{\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- unsigned long flags;\r
- \r
-#ifdef OHCI_VERBOSE_DEBUG\r
- urb_print (urb, "UNLINK", 1);\r
-#endif \r
-\r
- spin_lock_irqsave (&ohci->lock, flags);\r
- if (!ohci->disabled) {\r
- urb_priv_t *urb_priv;\r
-\r
- /* Unless an IRQ completed the unlink while it was being\r
- * handed to us, flag it for unlink and giveback, and force\r
- * some upcoming INTR_SF to call finish_unlinks()\r
- */\r
- urb_priv = urb->hcpriv;\r
- if (urb_priv) {\r
- urb_priv->state = URB_DEL; \r
- if (urb_priv->ed->state == ED_OPER)\r
- start_urb_unlink (ohci, urb_priv->ed);\r
- }\r
- } else {\r
- /*\r
- * with HC dead, we won't respect hc queue pointers\r
- * any more ... just clean up every urb's memory.\r
- */\r
- if (urb->hcpriv) {\r
- spin_unlock (&ohci->lock);\r
- finish_urb (ohci, urb, NULL);\r
- spin_lock (&ohci->lock);\r
- }\r
- }\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
- return 0;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* frees config/altsetting state for endpoints,\r
- * including ED memory, dummy TD, and bulk/intr data toggle\r
- */\r
-\r
-static void\r
-ohci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)\r
-{\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- int epnum = ep & USB_ENDPOINT_NUMBER_MASK;\r
- unsigned long flags;\r
- struct ed *ed;\r
-\r
- /* ASSERT: any requests/urbs are being unlinked */\r
- /* ASSERT: nobody can be submitting urbs for this any more */\r
-\r
- epnum <<= 1;\r
- if (epnum != 0 && !(ep & USB_DIR_IN))\r
- epnum |= 1;\r
-\r
-rescan:\r
- spin_lock_irqsave (&ohci->lock, flags);\r
- ed = dev->ep [epnum];\r
- if (!ed)\r
- goto done;\r
-\r
- if (!HCD_IS_RUNNING (ohci->hcd.state) || ohci->disabled)\r
- ed->state = ED_IDLE;\r
- switch (ed->state) {\r
- case ED_UNLINK: /* wait for hw to finish? */\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
- set_current_state (TASK_UNINTERRUPTIBLE);\r
- schedule_timeout (1);\r
- goto rescan;\r
- case ED_IDLE: /* fully unlinked */\r
- if (list_empty (&ed->td_list)) {\r
- td_free (ohci, ed->dummy);\r
- ed_free (ohci, ed);\r
- break;\r
- }\r
- /* else FALL THROUGH */\r
- default:\r
- /* caller was supposed to have unlinked any requests;\r
- * that's not our job. can't recover; must leak ed.\r
- */\r
- ohci_err (ohci, "ed %p (#%d) state %d%s\n",\r
- ed, epnum, ed->state,\r
- list_empty (&ed->td_list) ? "" : "(has tds)");\r
- td_free (ohci, ed->dummy);\r
- break;\r
- }\r
- dev->ep [epnum] = 0;\r
-done:\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
- return;\r
-}\r
-\r
-static int ohci_get_frame (struct usb_hcd *hcd)\r
-{\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
-\r
- return le16_to_cpu (ohci->hcca->frame_no);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*\r
- * HC functions\r
- *-------------------------------------------------------------------------*/\r
-\r
-/* reset the HC and BUS */\r
-\r
-static int hc_reset (struct ohci_hcd *ohci)\r
-{\r
- u32 temp;\r
- u32 ints;\r
- u32 control;\r
- \r
- /* Disable HC interrupts */\r
- writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);\r
- // acknowledge all pending interrupts\r
- ints = readl(&ohci->regs->intrstatus);\r
- writel (ints, &ohci->regs->intrstatus);\r
-\r
- if (readl (&ohci->regs->control) & OHCI_CTRL_IR) {\r
- // takeover without negotiation - there is noone to negotiate with\r
- control = readl (&ohci->regs->control) & ~OHCI_CTRL_IR;\r
- writel (control, &ohci->regs->control);\r
- }\r
-\r
- ohci_dbg (ohci, "USB HC reset_hc %s: ctrl = 0x%x ;\n",\r
- hcd_to_bus (&ohci->hcd)->bus_name,\r
- readl (&ohci->regs->control));\r
-\r
- /* Reset USB (needed by some controllers); RemoteWakeupConnected\r
- * saved if boot firmware (BIOS/SMM/...) told us it's connected\r
- */\r
- ohci->hc_control = readl (&ohci->regs->control);\r
- ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- // flush those pci writes\r
- (void) readl (&ohci->regs->control);\r
- wait_ms (50);\r
-\r
- /* HC Reset requires max 10 us delay */\r
- writel (OHCI_HCR, &ohci->regs->cmdstatus);\r
- temp = 30; /* ... allow extra time */\r
- while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {\r
- if (--temp == 0) {\r
- ohci_err (ohci, "USB HC reset timed out!\n");\r
- return -1;\r
- }\r
- udelay (1);\r
- }\r
-\r
- /* now we're in the SUSPEND state ... must go OPERATIONAL\r
- * within 2msec else HC enters RESUME\r
- *\r
- * ... but some hardware won't init fmInterval "by the book"\r
- * (SiS, OPTi ...), so reset again instead. SiS doesn't need\r
- * this if we write fmInterval after we're OPERATIONAL.\r
- */\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- // flush those pci writes\r
- (void) readl (&ohci->regs->control);\r
-\r
- return 0;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-#define FI 0x2edf /* 12000 bits per frame (-1) */\r
-#define LSTHRESH 0x628 /* lowspeed bit threshold */\r
-\r
-/* Start an OHCI controller, set the BUS operational\r
- * enable interrupts \r
- * connect the virtual root hub\r
- */\r
-static int hc_start (struct ohci_hcd *ohci)\r
-{\r
- u32 mask, tmp;\r
- struct usb_device *udev;\r
- struct usb_bus *bus;\r
-\r
- spin_lock_init (&ohci->lock);\r
- ohci->disabled = 1;\r
- ohci->sleeping = 0;\r
-\r
- /* Tell the controller where the control and bulk lists are\r
- * The lists are empty now. */\r
- writel (0, &ohci->regs->ed_controlhead);\r
- writel (0, &ohci->regs->ed_bulkhead);\r
-\r
- /* a reset clears this */\r
- writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);\r
- usbprintk("HCCA: %p \n",ohci->regs->hcca);\r
-\r
- /* force default fmInterval (we won't adjust it); init thresholds\r
- * for last FS and LS packets, reserve 90% for periodic.\r
- */\r
- writel ((((6 * (FI - 210)) / 7) << 16) | FI, &ohci->regs->fminterval);\r
- writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart);\r
- writel (LSTHRESH, &ohci->regs->lsthresh);\r
-\r
- /* some OHCI implementations are finicky about how they init.\r
- * bogus values here mean not even enumeration could work.\r
- */\r
- if ((readl (&ohci->regs->fminterval) & 0x3fff0000) == 0\r
- || !readl (&ohci->regs->periodicstart)) {\r
- ohci_err (ohci, "init err\n");\r
- return -EOVERFLOW;\r
- }\r
-\r
- /* start controller operations */\r
- ohci->hc_control &= OHCI_CTRL_RWC;\r
- ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;\r
- ohci->disabled = 0;\r
- writel (ohci->hc_control, &ohci->regs->control);\r
-\r
- /* Choose the interrupts we care about now, others later on demand */\r
- mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH;\r
- writel (mask, &ohci->regs->intrstatus);\r
- writel (mask, &ohci->regs->intrenable);\r
-\r
- /* handle root hub init quirks ... */\r
- tmp = roothub_a (ohci);\r
- tmp &= ~(RH_A_PSM | RH_A_OCPM);\r
- if (ohci->flags & OHCI_QUIRK_SUPERIO) {\r
- /* NSC 87560 and maybe others */\r
- tmp |= RH_A_NOCP;\r
- tmp &= ~(RH_A_POTPGT | RH_A_NPS);\r
- } else {\r
- /* hub power always on; required for AMD-756 and some\r
- * Mac platforms, use this mode everywhere by default\r
- */\r
- tmp |= RH_A_NPS;\r
- }\r
- writel (tmp, &ohci->regs->roothub.a);\r
- writel (RH_HS_LPSC, &ohci->regs->roothub.status);\r
- writel (0, &ohci->regs->roothub.b);\r
- // flush those pci writes\r
- (void) readl (&ohci->regs->control);\r
-\r
- // POTPGT delay is bits 24-31, in 2 ms units.\r
- mdelay ((roothub_a (ohci) >> 23) & 0x1fe);\r
- \r
- /* connect the virtual root hub */\r
- bus = hcd_to_bus (&ohci->hcd);\r
- bus->root_hub = udev = usb_alloc_dev (NULL, bus);\r
- ohci->hcd.state = USB_STATE_READY;\r
- if (!udev) {\r
- disable (ohci);\r
- ohci->hc_control &= ~OHCI_CTRL_HCFS;\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- ohci_err(ohci,"out of mem");\r
- return -ENOMEM;\r
- }\r
-\r
- usb_connect (udev);\r
- udev->speed = USB_SPEED_FULL;\r
- if (hcd_register_root (&ohci->hcd) != 0) {\r
- usb_put_dev (udev);\r
- bus->root_hub = NULL;\r
- disable (ohci);\r
- ohci->hc_control &= ~OHCI_CTRL_HCFS;\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- return -ENODEV;\r
- }\r
- create_debug_files (ohci);\r
- return 0;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* an interrupt happens */\r
-\r
-static void ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)\r
-{\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- struct ohci_regs *regs = ohci->regs;\r
- int ints; \r
-\r
- /* we can eliminate a (slow) readl() if _only_ WDH caused this irq */\r
- if ((ohci->hcca->done_head != 0)\r
- && ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {\r
- ints = OHCI_INTR_WDH;\r
-\r
- /* cardbus/... hardware gone before remove() */\r
- } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) {\r
- disable (ohci);\r
- ohci_dbg (ohci, "device removed!\n");\r
- return;\r
-\r
- /* interrupt for some other device? */\r
- } else if ((ints &= readl (®s->intrenable)) == 0) {\r
- return;\r
- } \r
-\r
- if (ints & OHCI_INTR_UE) {\r
- disable (ohci);\r
- ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");\r
- // e.g. due to PCI Master/Target Abort\r
-\r
- ohci_dump (ohci, 1);\r
- hc_reset (ohci);\r
- }\r
- \r
- if (ints & OHCI_INTR_WDH) {\r
- writel (OHCI_INTR_WDH, ®s->intrdisable); \r
- dl_done_list (ohci, dl_reverse_done_list (ohci), ptregs);\r
- writel (OHCI_INTR_WDH, ®s->intrenable); \r
- }\r
- \r
- /* could track INTR_SO to reduce available PCI/... bandwidth */\r
-\r
- /* handle any pending URB/ED unlinks, leaving INTR_SF enabled\r
- * when there's still unlinking to be done (next frame).\r
- */\r
- spin_lock (&ohci->lock);\r
- if (ohci->ed_rm_list)\r
- finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no),\r
- ptregs);\r
- if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list)\r
- writel (OHCI_INTR_SF, ®s->intrdisable); \r
- spin_unlock (&ohci->lock);\r
-\r
- writel (ints, ®s->intrstatus);\r
- writel (OHCI_INTR_MIE, ®s->intrenable); \r
- // flush those pci writes\r
- (void) readl (&ohci->regs->control);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static void ohci_stop (struct usb_hcd *hcd)\r
-{ \r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- struct ohci_regs *regs = ohci->regs;\r
- int ints;\r
-\r
- ohci_dbg (ohci, "stop %s controller%s\n",\r
- hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),\r
- ohci->disabled ? " (disabled)" : ""\r
- );\r
- ohci_dump (ohci, 1);\r
-\r
- if (!ohci->disabled)\r
- hc_reset (ohci);\r
-\r
- // Disable all interrupts\r
- writel (OHCI_INTR_MIE, ®s->intrdisable); \r
- // acknowledge all pending interrupts\r
- ints = readl(®s->intrstatus);\r
- writel (ints, ®s->intrstatus);\r
- // flush register writes\r
- (void) readl (&ohci->regs->control);\r
- \r
- remove_debug_files (ohci);\r
- ohci_mem_cleanup (ohci);\r
- if (ohci->hcca) {\r
- pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,\r
- ohci->hcca, ohci->hcca_dma);\r
- ohci->hcca = NULL;\r
- ohci->hcca_dma = 0;\r
- }\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-// FIXME: this restart logic should be generic,\r
-// and handle full hcd state cleanup\r
-\r
-/* controller died; cleanup debris, then restart */\r
-/* must not be called from interrupt context */\r
-\r
-#ifdef CONFIG_PM\r
-static int hc_restart (struct ohci_hcd *ohci)\r
-{\r
- int temp;\r
- int i;\r
-\r
- ohci->disabled = 1;\r
- ohci->sleeping = 0;\r
- if (hcd_to_bus (&ohci->hcd)->root_hub)\r
- usb_disconnect (&hcd_to_bus (&ohci->hcd)->root_hub);\r
- \r
- /* empty the interrupt branches */\r
- for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;\r
- for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;\r
- \r
- /* no EDs to remove */\r
- ohci->ed_rm_list = NULL;\r
-\r
- /* empty control and bulk lists */ \r
- ohci->ed_controltail = NULL;\r
- ohci->ed_bulktail = NULL;\r
-\r
- if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {\r
- ohci_err (ohci, "can't restart, %d\n", temp);\r
- return temp;\r
- } else\r
- ohci_dbg (ohci, "restart complete\n");\r
- return 0;\r
-}\r
-#endif\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC\r
-\r
-MODULE_AUTHOR (DRIVER_AUTHOR);\r
-MODULE_DESCRIPTION (DRIVER_INFO);\r
-MODULE_LICENSE ("GPL");\r
-\r
-#ifdef CONFIG_PCI\r
-#include "ohci-pci.c"\r
-#endif\r
-\r
-#ifdef CONFIG_SA1111\r
-#include "ohci-sa1111.c"\r
-#endif\r
-\r
-#if !(defined(CONFIG_PCI) || defined(CONFIG_SA1111))\r
-#error "missing bus glue for ohci-hcd"\r
-#endif\r
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * [ Initialisation is based on Linus' ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ *
+ *
+ * OHCI is the main "non-Intel/VIA" standard for USB 1.1 host controller
+ * interfaces (though some non-x86 Intel chips use it). It supports
+ * smarter hardware than UHCI. A download link for the spec available
+ * through the http://www.usb.org website.
+ *
+ * History:
+ *
+ * 2003/02/24 show registers in sysfs (Kevin Brosius)
+ *
+ * 2002/09/03 get rid of ed hashtables, rework periodic scheduling and
+ * bandwidth accounting; if debugging, show schedules in driverfs
+ * 2002/07/19 fixes to management of ED and schedule state.
+ * 2002/06/09 SA-1111 support (Christopher Hoover)
+ * 2002/06/01 remember frame when HC won't see EDs any more; use that info
+ * to fix urb unlink races caused by interrupt latency assumptions;
+ * minor ED field and function naming updates
+ * 2002/01/18 package as a patch for 2.5.3; this should match the
+ * 2.4.17 kernel modulo some bugs being fixed.
+ *
+ * 2001/10/18 merge pmac cleanup (Benjamin Herrenschmidt) and bugfixes
+ * from post-2.4.5 patches.
+ * 2001/09/20 URB_ZERO_PACKET support; hcca_dma portability, OPTi warning
+ * 2001/09/07 match PCI PM changes, errnos from Linus' tree
+ * 2001/05/05 fork 2.4.5 version into "hcd" framework, cleanup, simplify;
+ * pbook pci quirks gone (please fix pbook pci sw!) (db)
+ *
+ * 2001/04/08 Identify version on module load (gb)
+ * 2001/03/24 td/ed hashing to remove bus_to_virt (Steve Longerbeam);
+ pci_map_single (db)
+ * 2001/03/21 td and dev/ed allocation uses new pci_pool API (db)
+ * 2001/03/07 hcca allocation uses pci_alloc_consistent (Steve Longerbeam)
+ *
+ * 2000/09/26 fixed races in removing the private portion of the urb
+ * 2000/09/07 disable bulk and control lists when unlinking the last
+ * endpoint descriptor in order to avoid unrecoverable errors on
+ * the Lucent chips. (rwc@sgi)
+ * 2000/08/29 use bandwidth claiming hooks (thanks Randy!), fix some
+ * urb unlink probs, indentation fixes
+ * 2000/08/11 various oops fixes mostly affecting iso and cleanup from
+ * device unplugs.
+ * 2000/06/28 use PCI hotplug framework, for better power management
+ * and for Cardbus support (David Brownell)
+ * 2000/earlier: fixes for NEC/Lucent chips; suspend/resume handling
+ * when the controller loses power; handle UE; cleanup; ...
+ *
+ * v5.2 1999/12/07 URB 3rd preview,
+ * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi)
+ * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume
+ * i386: HUB, Keyboard, Mouse, Printer
+ *
+ * v4.3 1999/10/27 multiple HCs, bulk_request
+ * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes
+ * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl.
+ * v4.0 1999/08/18
+ * v3.0 1999/06/25
+ * v2.1 1999/05/09 code clean up
+ * v2.0 1999/05/04
+ * v1.0 1999/04/27 initial release
+ *
+ * This file is licenced under the GPL.
+ */
+
+#if 0
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_DEBUG
+# define DEBUG
+#else
+# undef DEBUG
+#endif
+
+
+
+#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/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h> /* for in_interrupt () */
+#include <linux/usb.h>
+#include "../core/hcd.h"
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+#else
+#include "ohci_config.h"
+
+#include "../usb_wrapper.h"
+#include "../core/hcd.h"
+
+//#define OHCI_VERBOSE_DEBUG
+#endif
+
+/*
+ * TO DO:
+ *
+ * - "disabled" and "sleeping" should be in hcd->state
+ * - lots more testing!!
+ */
+
+#define DRIVER_VERSION "2003 Feb 24"
+#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
+#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
+
+/*-------------------------------------------------------------------------*/
+
+// #define OHCI_VERBOSE_DEBUG /* not always helpful */
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define OHCI_CONTROL_INIT \
+ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+#define OHCI_UNLINK_TIMEOUT (HZ / 10)
+
+/*-------------------------------------------------------------------------*/
+
+static const char hcd_name [] = "ohci-hcd";
+
+#include "ohci.h"
+
+static inline void disable (struct ohci_hcd *ohci)
+{
+ ohci->disabled = 1;
+ ohci->hcd.state = USB_STATE_HALT;
+}
+
+#include "ohci-hub.c"
+#include "ohci-dbg.c"
+#include "ohci-mem.c"
+#include "ohci-q.c"
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * queue up an urb for anything except the root hub
+ */
+static int ohci_urb_enqueue (
+ struct usb_hcd *hcd,
+ struct urb *urb,
+ int mem_flags
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ed *ed;
+ urb_priv_t *urb_priv;
+ unsigned int pipe = urb->pipe;
+ int i, size = 0;
+ unsigned long flags;
+ int retval = 0;
+
+#ifdef OHCI_VERBOSE_DEBUG
+ urb_print (urb, "SUB", usb_pipein (pipe));
+#endif
+
+ /* every endpoint has a ed, locate and maybe (re)initialize it */
+ if (! (ed = ed_get (ohci, urb->dev, pipe, urb->interval)))
+ return -ENOMEM;
+
+ /* for the private part of the URB we need the number of TDs (size) */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ /* td_submit_urb() doesn't yet handle these */
+ if (urb->transfer_buffer_length > 4096)
+ return -EMSGSIZE;
+
+ /* 1 TD for setup, 1 for ACK, plus ... */
+ size = 2;
+ /* FALLTHROUGH */
+ // case PIPE_INTERRUPT:
+ // case PIPE_BULK:
+ default:
+ /* one TD for every 4096 Bytes (can be upto 8K) */
+ size += urb->transfer_buffer_length / 4096;
+ /* ... and for any remaining bytes ... */
+ if ((urb->transfer_buffer_length % 4096) != 0)
+ size++;
+ /* ... and maybe a zero length packet to wrap it up */
+ if (size == 0)
+ size++;
+ else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
+ && (urb->transfer_buffer_length
+ % usb_maxpacket (urb->dev, pipe,
+ usb_pipeout (pipe))) == 0)
+ size++;
+ break;
+ case PIPE_ISOCHRONOUS: /* number of packets from URB */
+ size = urb->number_of_packets;
+ break;
+ }
+
+ /* allocate the private part of the URB */
+ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *),
+ mem_flags);
+ if (!urb_priv)
+ return -ENOMEM;
+ memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *));
+
+ /* fill the private part of the URB */
+ urb_priv->length = size;
+ urb_priv->ed = ed;
+
+ /* allocate the TDs (deferring hash chain updates) */
+ for (i = 0; i < size; i++) {
+ urb_priv->td [i] = td_alloc (ohci, mem_flags);
+ if (!urb_priv->td [i]) {
+ urb_priv->length = i;
+ urb_free_priv (ohci, urb_priv);
+ return -ENOMEM;
+ }
+ }
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ /* don't submit to a dead HC */
+ if (ohci->disabled || ohci->sleeping) {
+ retval = -ENODEV;
+ goto fail;
+ }
+
+ /* schedule the ed if needed */
+ if (ed->state == ED_IDLE) {
+ retval = ed_schedule (ohci, ed);
+ if (retval < 0)
+ goto fail;
+ if (ed->type == PIPE_ISOCHRONOUS) {
+ u16 frame = le16_to_cpu (ohci->hcca->frame_no);
+
+ /* delay a few frames before the first TD */
+ frame += max_t (u16, 8, ed->interval);
+ frame &= ~(ed->interval - 1);
+ frame |= ed->branch;
+ urb->start_frame = frame;
+
+ /* yes, only URB_ISO_ASAP is supported, and
+ * urb->start_frame is never used as input.
+ */
+ }
+ } else if (ed->type == PIPE_ISOCHRONOUS)
+ urb->start_frame = ed->last_iso + ed->interval;
+
+ /* fill the TDs and link them to the ed; and
+ * enable that part of the schedule, if needed
+ * and update count of queued periodic urbs
+ */
+ urb->hcpriv = urb_priv;
+ td_submit_urb (ohci, urb);
+
+fail:
+ if (retval)
+ urb_free_priv (ohci, urb_priv);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return retval;
+}
+
+/*
+ * decouple the URB from the HC queues (TDs, urb_priv); it's
+ * already marked using urb->status. reporting is always done
+ * asynchronously, and we might be dealing with an urb that's
+ * partially transferred, or an ED with other urbs being unlinked.
+ */
+static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+
+#ifdef OHCI_VERBOSE_DEBUG
+ urb_print (urb, "UNLINK", 1);
+#endif
+
+ spin_lock_irqsave (&ohci->lock, flags);
+ if (!ohci->disabled) {
+ urb_priv_t *urb_priv;
+
+ /* Unless an IRQ completed the unlink while it was being
+ * handed to us, flag it for unlink and giveback, and force
+ * some upcoming INTR_SF to call finish_unlinks()
+ */
+ urb_priv = urb->hcpriv;
+ if (urb_priv) {
+ urb_priv->state = URB_DEL;
+ if (urb_priv->ed->state == ED_OPER)
+ start_urb_unlink (ohci, urb_priv->ed);
+ }
+ } else {
+ /*
+ * with HC dead, we won't respect hc queue pointers
+ * any more ... just clean up every urb's memory.
+ */
+ if (urb->hcpriv) {
+ spin_unlock (&ohci->lock);
+ finish_urb (ohci, urb, NULL);
+ spin_lock (&ohci->lock);
+ }
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* frees config/altsetting state for endpoints,
+ * including ED memory, dummy TD, and bulk/intr data toggle
+ */
+
+static void
+ohci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int epnum = ep & USB_ENDPOINT_NUMBER_MASK;
+ unsigned long flags;
+ struct ed *ed;
+
+ /* ASSERT: any requests/urbs are being unlinked */
+ /* ASSERT: nobody can be submitting urbs for this any more */
+
+ epnum <<= 1;
+ if (epnum != 0 && !(ep & USB_DIR_IN))
+ epnum |= 1;
+
+rescan:
+ spin_lock_irqsave (&ohci->lock, flags);
+ ed = dev->ep [epnum];
+ if (!ed)
+ goto done;
+
+ if (!HCD_IS_RUNNING (ohci->hcd.state) || ohci->disabled)
+ ed->state = ED_IDLE;
+ switch (ed->state) {
+ case ED_UNLINK: /* wait for hw to finish? */
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ set_current_state (TASK_UNINTERRUPTIBLE);
+ schedule_timeout (1);
+ goto rescan;
+ case ED_IDLE: /* fully unlinked */
+ if (list_empty (&ed->td_list)) {
+ td_free (ohci, ed->dummy);
+ ed_free (ohci, ed);
+ break;
+ }
+ /* else FALL THROUGH */
+ default:
+ /* caller was supposed to have unlinked any requests;
+ * that's not our job. can't recover; must leak ed.
+ */
+ ohci_err (ohci, "ed %p (#%d) state %d%s\n",
+ ed, epnum, ed->state,
+ list_empty (&ed->td_list) ? "" : "(has tds)");
+ td_free (ohci, ed->dummy);
+ break;
+ }
+ dev->ep [epnum] = 0;
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return;
+}
+
+static int ohci_get_frame (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ return le16_to_cpu (ohci->hcca->frame_no);
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset (struct ohci_hcd *ohci)
+{
+ u32 temp;
+ u32 ints;
+ u32 control;
+
+ /* Disable HC interrupts */
+ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+ // acknowledge all pending interrupts
+ ints = readl(&ohci->regs->intrstatus);
+ writel (ints, &ohci->regs->intrstatus);
+
+ if (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+ // takeover without negotiation - there is noone to negotiate with
+ control = readl (&ohci->regs->control) & ~OHCI_CTRL_IR;
+ writel (control, &ohci->regs->control);
+ }
+
+ ohci_dbg (ohci, "USB HC reset_hc %s: ctrl = 0x%x ;\n",
+ hcd_to_bus (&ohci->hcd)->bus_name,
+ readl (&ohci->regs->control));
+
+ /* Reset USB (needed by some controllers); RemoteWakeupConnected
+ * saved if boot firmware (BIOS/SMM/...) told us it's connected
+ */
+ ohci->hc_control = readl (&ohci->regs->control);
+ ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
+ writel (ohci->hc_control, &ohci->regs->control);
+ // flush those pci writes
+ (void) readl (&ohci->regs->control);
+ wait_ms (50);
+
+ /* HC Reset requires max 10 us delay */
+ writel (OHCI_HCR, &ohci->regs->cmdstatus);
+ temp = 30; /* ... allow extra time */
+ while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+ if (--temp == 0) {
+ ohci_err (ohci, "USB HC reset timed out!\n");
+ return -1;
+ }
+ udelay (1);
+ }
+
+ /* now we're in the SUSPEND state ... must go OPERATIONAL
+ * within 2msec else HC enters RESUME
+ *
+ * ... but some hardware won't init fmInterval "by the book"
+ * (SiS, OPTi ...), so reset again instead. SiS doesn't need
+ * this if we write fmInterval after we're OPERATIONAL.
+ */
+ writel (ohci->hc_control, &ohci->regs->control);
+ // flush those pci writes
+ (void) readl (&ohci->regs->control);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define FI 0x2edf /* 12000 bits per frame (-1) */
+#define LSTHRESH 0x628 /* lowspeed bit threshold */
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts
+ * connect the virtual root hub
+ */
+static int hc_start (struct ohci_hcd *ohci)
+{
+ u32 mask, tmp;
+ struct usb_device *udev;
+ struct usb_bus *bus;
+
+ spin_lock_init (&ohci->lock);
+ ohci->disabled = 1;
+ ohci->sleeping = 0;
+
+ /* 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);
+
+ /* a reset clears this */
+ writel ((u32) ohci->hcca_dma, &ohci->regs->hcca);
+ usbprintk("HCCA: %p \n",ohci->regs->hcca);
+
+ /* force default fmInterval (we won't adjust it); init thresholds
+ * for last FS and LS packets, reserve 90% for periodic.
+ */
+ writel ((((6 * (FI - 210)) / 7) << 16) | FI, &ohci->regs->fminterval);
+ writel (((9 * FI) / 10) & 0x3fff, &ohci->regs->periodicstart);
+ writel (LSTHRESH, &ohci->regs->lsthresh);
+
+ /* some OHCI implementations are finicky about how they init.
+ * bogus values here mean not even enumeration could work.
+ */
+ if ((readl (&ohci->regs->fminterval) & 0x3fff0000) == 0
+ || !readl (&ohci->regs->periodicstart)) {
+ ohci_err (ohci, "init err\n");
+ return -EOVERFLOW;
+ }
+
+ /* start controller operations */
+ ohci->hc_control &= OHCI_CTRL_RWC;
+ ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci->disabled = 0;
+ writel (ohci->hc_control, &ohci->regs->control);
+
+ /* Choose the interrupts we care about now, others later on demand */
+ mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH;
+ writel (mask, &ohci->regs->intrstatus);
+ writel (mask, &ohci->regs->intrenable);
+
+ /* handle root hub init quirks ... */
+ tmp = roothub_a (ohci);
+ tmp &= ~(RH_A_PSM | RH_A_OCPM);
+ if (ohci->flags & OHCI_QUIRK_SUPERIO) {
+ /* NSC 87560 and maybe others */
+ tmp |= RH_A_NOCP;
+ tmp &= ~(RH_A_POTPGT | RH_A_NPS);
+ } else {
+ /* hub power always on; required for AMD-756 and some
+ * Mac platforms, use this mode everywhere by default
+ */
+ tmp |= RH_A_NPS;
+ }
+ writel (tmp, &ohci->regs->roothub.a);
+ writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+ writel (0, &ohci->regs->roothub.b);
+ // flush those pci writes
+ (void) readl (&ohci->regs->control);
+
+ // POTPGT delay is bits 24-31, in 2 ms units.
+ mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+
+ /* connect the virtual root hub */
+ bus = hcd_to_bus (&ohci->hcd);
+ bus->root_hub = udev = usb_alloc_dev (NULL, bus);
+ ohci->hcd.state = USB_STATE_READY;
+ if (!udev) {
+ disable (ohci);
+ ohci->hc_control &= ~OHCI_CTRL_HCFS;
+ writel (ohci->hc_control, &ohci->regs->control);
+ ohci_err(ohci,"out of mem");
+ return -ENOMEM;
+ }
+
+ usb_connect (udev);
+ udev->speed = USB_SPEED_FULL;
+ if (hcd_register_root (&ohci->hcd) != 0) {
+ usb_put_dev (udev);
+ bus->root_hub = NULL;
+ disable (ohci);
+ ohci->hc_control &= ~OHCI_CTRL_HCFS;
+ writel (ohci->hc_control, &ohci->regs->control);
+ return -ENODEV;
+ }
+ create_debug_files (ohci);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static void ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ohci_regs *regs = ohci->regs;
+ int ints;
+
+ /* we can eliminate a (slow) readl() if _only_ WDH caused this irq */
+ if ((ohci->hcca->done_head != 0)
+ && ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) {
+ ints = OHCI_INTR_WDH;
+
+ /* cardbus/... hardware gone before remove() */
+ } else if ((ints = readl (®s->intrstatus)) == ~(u32)0) {
+ disable (ohci);
+ ohci_dbg (ohci, "device removed!\n");
+ return;
+
+ /* interrupt for some other device? */
+ } else if ((ints &= readl (®s->intrenable)) == 0) {
+ return;
+ }
+
+ if (ints & OHCI_INTR_UE) {
+ disable (ohci);
+ ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n");
+ // e.g. due to PCI Master/Target Abort
+
+ ohci_dump (ohci, 1);
+ hc_reset (ohci);
+ }
+
+ if (ints & OHCI_INTR_WDH) {
+ writel (OHCI_INTR_WDH, ®s->intrdisable);
+ dl_done_list (ohci, dl_reverse_done_list (ohci), ptregs);
+ writel (OHCI_INTR_WDH, ®s->intrenable);
+ }
+
+ /* could track INTR_SO to reduce available PCI/... bandwidth */
+
+ /* handle any pending URB/ED unlinks, leaving INTR_SF enabled
+ * when there's still unlinking to be done (next frame).
+ */
+ spin_lock (&ohci->lock);
+ if (ohci->ed_rm_list)
+ finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no),
+ ptregs);
+ if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list)
+ writel (OHCI_INTR_SF, ®s->intrdisable);
+ spin_unlock (&ohci->lock);
+
+ writel (ints, ®s->intrstatus);
+ writel (OHCI_INTR_MIE, ®s->intrenable);
+ // flush those pci writes
+ (void) readl (&ohci->regs->control);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void ohci_stop (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct ohci_regs *regs = ohci->regs;
+ int ints;
+
+ ohci_dbg (ohci, "stop %s controller%s\n",
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS),
+ ohci->disabled ? " (disabled)" : ""
+ );
+ ohci_dump (ohci, 1);
+
+ if (!ohci->disabled)
+ hc_reset (ohci);
+
+ // Disable all interrupts
+ writel (OHCI_INTR_MIE, ®s->intrdisable);
+ // acknowledge all pending interrupts
+ ints = readl(®s->intrstatus);
+ writel (ints, ®s->intrstatus);
+ // flush register writes
+ (void) readl (&ohci->regs->control);
+
+ remove_debug_files (ohci);
+ ohci_mem_cleanup (ohci);
+ if (ohci->hcca) {
+ pci_free_consistent (ohci->hcd.pdev, sizeof *ohci->hcca,
+ ohci->hcca, ohci->hcca_dma);
+ ohci->hcca = NULL;
+ ohci->hcca_dma = 0;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+// FIXME: this restart logic should be generic,
+// and handle full hcd state cleanup
+
+/* controller died; cleanup debris, then restart */
+/* must not be called from interrupt context */
+
+#ifdef CONFIG_PM
+static int hc_restart (struct ohci_hcd *ohci)
+{
+ int temp;
+ int i;
+
+ ohci->disabled = 1;
+ ohci->sleeping = 0;
+ if (hcd_to_bus (&ohci->hcd)->root_hub)
+ usb_disconnect (&hcd_to_bus (&ohci->hcd)->root_hub);
+
+ /* empty the interrupt branches */
+ for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0;
+ for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0;
+
+ /* no EDs to remove */
+ ohci->ed_rm_list = NULL;
+
+ /* empty control and bulk lists */
+ ohci->ed_controltail = NULL;
+ ohci->ed_bulktail = NULL;
+
+ if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) {
+ ohci_err (ohci, "can't restart, %d\n", temp);
+ return temp;
+ } else
+ ohci_dbg (ohci, "restart complete\n");
+ return 0;
+}
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_INFO);
+MODULE_LICENSE ("GPL");
+
+#ifdef CONFIG_PCI
+#include "ohci-pci.c"
+#endif
+
+#ifdef CONFIG_SA1111
+#include "ohci-sa1111.c"
+#endif
+
+#if !(defined(CONFIG_PCI) || defined(CONFIG_SA1111))
+#error "missing bus glue for ohci-hcd"
+#endif
-/*\r
- * OHCI HCD (Host Controller Driver) for USB.\r
- * \r
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>\r
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>\r
- * \r
- * This file is licenced under GPL\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * OHCI Root Hub ... the nonsharable stuff\r
- *\r
- * Registers don't need cpu_to_le32, that happens transparently\r
- */\r
-\r
-/* AMD-756 (D2 rev) reports corrupt register contents in some cases.\r
- * The erratum (#4) description is incorrect. AMD's workaround waits\r
- * till some bits (mostly reserved) are clear; ok for all revs.\r
- */\r
-#define read_roothub(hc, register, mask) ({ \\r
- u32 temp = readl (&hc->regs->roothub.register); \\r
- if (temp == -1) \\r
- disable (hc); \\r
- else if (hc->flags & OHCI_QUIRK_AMD756) \\r
- while (temp & mask) \\r
- temp = readl (&hc->regs->roothub.register); \\r
- temp; })\r
-\r
-static u32 roothub_a (struct ohci_hcd *hc)\r
- { return read_roothub (hc, a, 0xfc0fe000); }\r
-static inline u32 roothub_b (struct ohci_hcd *hc)\r
- { return readl (&hc->regs->roothub.b); }\r
-static inline u32 roothub_status (struct ohci_hcd *hc)\r
- { return readl (&hc->regs->roothub.status); }\r
-static u32 roothub_portstatus (struct ohci_hcd *hc, int i)\r
- { return read_roothub (hc, portstatus [i], 0xffe0fce0); }\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-#define dbg_port(hc,label,num,value) \\r
- ohci_dbg (hc, \\r
- "%s roothub.portstatus [%d] " \\r
- "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \\r
- label, num, temp, \\r
- (temp & RH_PS_PRSC) ? " PRSC" : "", \\r
- (temp & RH_PS_OCIC) ? " OCIC" : "", \\r
- (temp & RH_PS_PSSC) ? " PSSC" : "", \\r
- (temp & RH_PS_PESC) ? " PESC" : "", \\r
- (temp & RH_PS_CSC) ? " CSC" : "", \\r
- \\r
- (temp & RH_PS_LSDA) ? " LSDA" : "", \\r
- (temp & RH_PS_PPS) ? " PPS" : "", \\r
- (temp & RH_PS_PRS) ? " PRS" : "", \\r
- (temp & RH_PS_POCI) ? " POCI" : "", \\r
- (temp & RH_PS_PSS) ? " PSS" : "", \\r
- \\r
- (temp & RH_PS_PES) ? " PES" : "", \\r
- (temp & RH_PS_CCS) ? " CCS" : "" \\r
- );\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* build "status change" packet (one or two bytes) from HC registers */\r
-\r
-static int\r
-ohci_hub_status_data (struct usb_hcd *hcd, char *buf)\r
-{\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- int ports, i, changed = 0, length = 1;\r
-\r
- ports = roothub_a (ohci) & RH_A_NDP; \r
- if (ports > MAX_ROOT_PORTS) {\r
- if (ohci->disabled)\r
- return -ESHUTDOWN;\r
- ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n",\r
- ports, readl (&ohci->regs->roothub.a) & RH_A_NDP);\r
- /* retry later; "should not happen" */\r
- return 0;\r
- }\r
-\r
- /* init status */\r
- if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))\r
- buf [0] = changed = 1;\r
- else\r
- buf [0] = 0;\r
- if (ports > 7) {\r
- buf [1] = 0;\r
- length++;\r
- }\r
-\r
- /* look at each port */\r
- for (i = 0; i < ports; i++) {\r
- u32 status = roothub_portstatus (ohci, i);\r
-\r
- status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC\r
- | RH_PS_OCIC | RH_PS_PRSC;\r
- if (status) {\r
- changed = 1;\r
- if (i < 7)\r
- buf [0] |= 1 << (i + 1);\r
- else\r
- buf [1] |= 1 << (i - 7);\r
- }\r
- }\r
- return changed ? length : 0;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static void\r
-ohci_hub_descriptor (\r
- struct ohci_hcd *ohci,\r
- struct usb_hub_descriptor *desc\r
-) {\r
- u32 rh = roothub_a (ohci);\r
- int ports = rh & RH_A_NDP; \r
- u16 temp;\r
-\r
- desc->bDescriptorType = 0x29;\r
- desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;\r
- desc->bHubContrCurrent = 0;\r
-\r
- desc->bNbrPorts = ports;\r
- temp = 1 + (ports / 8);\r
- desc->bDescLength = 7 + 2 * temp;\r
-\r
- temp = 0;\r
- if (rh & RH_A_PSM) /* per-port power switching? */\r
- temp |= 0x0001;\r
- if (rh & RH_A_NOCP) /* no overcurrent reporting? */\r
- temp |= 0x0010;\r
- else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */\r
- temp |= 0x0008;\r
- desc->wHubCharacteristics = cpu_to_le16 (temp);\r
-\r
- /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */\r
- rh = roothub_b (ohci);\r
- desc->bitmap [0] = rh & RH_B_DR;\r
- if (ports > 7) {\r
- desc->bitmap [1] = (rh & RH_B_DR) >> 8;\r
- desc->bitmap [2] = desc->bitmap [3] = 0xff;\r
- } else\r
- desc->bitmap [1] = 0xff;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static int ohci_hub_control (\r
- struct usb_hcd *hcd,\r
- u16 typeReq,\r
- u16 wValue,\r
- u16 wIndex,\r
- char *buf,\r
- u16 wLength\r
-) {\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- int ports = hcd_to_bus (hcd)->root_hub->maxchild;\r
- u32 temp;\r
- int retval = 0;\r
-\r
- switch (typeReq) {\r
- case ClearHubFeature:\r
- switch (wValue) {\r
- case C_HUB_OVER_CURRENT:\r
- writel (RH_HS_OCIC, &ohci->regs->roothub.status);\r
- case C_HUB_LOCAL_POWER:\r
- break;\r
- default:\r
- goto error;\r
- }\r
- break;\r
- case ClearPortFeature:\r
- if (!wIndex || wIndex > ports)\r
- goto error;\r
- wIndex--;\r
-\r
- switch (wValue) {\r
- case USB_PORT_FEAT_ENABLE:\r
- temp = RH_PS_CCS;\r
- break;\r
- case USB_PORT_FEAT_C_ENABLE:\r
- temp = RH_PS_PESC;\r
- break;\r
- case USB_PORT_FEAT_SUSPEND:\r
- temp = RH_PS_POCI;\r
- break;\r
- case USB_PORT_FEAT_C_SUSPEND:\r
- temp = RH_PS_PSSC;\r
- break;\r
- case USB_PORT_FEAT_POWER:\r
- temp = RH_PS_LSDA;\r
- break;\r
- case USB_PORT_FEAT_C_CONNECTION:\r
- temp = RH_PS_CSC;\r
- break;\r
- case USB_PORT_FEAT_C_OVER_CURRENT:\r
- temp = RH_PS_OCIC;\r
- break;\r
- case USB_PORT_FEAT_C_RESET:\r
- temp = RH_PS_PRSC;\r
- break;\r
- default:\r
- goto error;\r
- }\r
- writel (temp, &ohci->regs->roothub.portstatus [wIndex]);\r
- // readl (&ohci->regs->roothub.portstatus [wIndex]);\r
- break;\r
- case GetHubDescriptor:\r
- ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);\r
- break;\r
- case GetHubStatus:\r
- temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);\r
- *(u32 *) buf = cpu_to_le32 (temp);\r
- break;\r
- case GetPortStatus:\r
- if (!wIndex || wIndex > ports)\r
- goto error;\r
- wIndex--;\r
- temp = roothub_portstatus (ohci, wIndex);\r
- *(u32 *) buf = cpu_to_le32 (temp);\r
-\r
-#ifndef OHCI_VERBOSE_DEBUG\r
- if (*(u16*)(buf+2)) /* only if wPortChange is interesting */\r
-#endif\r
- dbg_port (ohci, "GetStatus", wIndex + 1, temp);\r
- break;\r
- case SetHubFeature:\r
- switch (wValue) {\r
- case C_HUB_OVER_CURRENT:\r
- // FIXME: this can be cleared, yes?\r
- case C_HUB_LOCAL_POWER:\r
- break;\r
- default:\r
- goto error;\r
- }\r
- break;\r
- case SetPortFeature:\r
- if (!wIndex || wIndex > ports)\r
- goto error;\r
- wIndex--;\r
- switch (wValue) {\r
- case USB_PORT_FEAT_SUSPEND:\r
- writel (RH_PS_PSS,\r
- &ohci->regs->roothub.portstatus [wIndex]);\r
- break;\r
- case USB_PORT_FEAT_POWER:\r
- writel (RH_PS_PPS,\r
- &ohci->regs->roothub.portstatus [wIndex]);\r
- break;\r
- case USB_PORT_FEAT_RESET:\r
- temp = readl (&ohci->regs->roothub.portstatus [wIndex]);\r
- if (temp & RH_PS_CCS)\r
- writel (RH_PS_PRS,\r
- &ohci->regs->roothub.portstatus [wIndex]);\r
- break;\r
- default:\r
- goto error;\r
- }\r
- break;\r
-\r
- default:\r
-error:\r
- /* "protocol stall" on error */\r
- retval = -EPIPE;\r
- }\r
- return retval;\r
-}\r
-\r
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under GPL
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * OHCI Root Hub ... the nonsharable stuff
+ *
+ * Registers don't need cpu_to_le32, that happens transparently
+ */
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect. AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define read_roothub(hc, register, mask) ({ \
+ u32 temp = readl (&hc->regs->roothub.register); \
+ if (temp == -1) \
+ disable (hc); \
+ else if (hc->flags & OHCI_QUIRK_AMD756) \
+ while (temp & mask) \
+ temp = readl (&hc->regs->roothub.register); \
+ temp; })
+
+static u32 roothub_a (struct ohci_hcd *hc)
+ { return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci_hcd *hc)
+ { return readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci_hcd *hc)
+ { return readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
+ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+/*-------------------------------------------------------------------------*/
+
+#define dbg_port(hc,label,num,value) \
+ ohci_dbg (hc, \
+ "%s roothub.portstatus [%d] " \
+ "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
+ label, num, temp, \
+ (temp & RH_PS_PRSC) ? " PRSC" : "", \
+ (temp & RH_PS_OCIC) ? " OCIC" : "", \
+ (temp & RH_PS_PSSC) ? " PSSC" : "", \
+ (temp & RH_PS_PESC) ? " PESC" : "", \
+ (temp & RH_PS_CSC) ? " CSC" : "", \
+ \
+ (temp & RH_PS_LSDA) ? " LSDA" : "", \
+ (temp & RH_PS_PPS) ? " PPS" : "", \
+ (temp & RH_PS_PRS) ? " PRS" : "", \
+ (temp & RH_PS_POCI) ? " POCI" : "", \
+ (temp & RH_PS_PSS) ? " PSS" : "", \
+ \
+ (temp & RH_PS_PES) ? " PES" : "", \
+ (temp & RH_PS_CCS) ? " CCS" : "" \
+ );
+
+
+/*-------------------------------------------------------------------------*/
+
+/* build "status change" packet (one or two bytes) from HC registers */
+
+static int
+ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ports, i, changed = 0, length = 1;
+
+ ports = roothub_a (ohci) & RH_A_NDP;
+ if (ports > MAX_ROOT_PORTS) {
+ if (ohci->disabled)
+ return -ESHUTDOWN;
+ ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n",
+ ports, readl (&ohci->regs->roothub.a) & RH_A_NDP);
+ /* retry later; "should not happen" */
+ return 0;
+ }
+
+ /* init status */
+ if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
+ buf [0] = changed = 1;
+ else
+ buf [0] = 0;
+ if (ports > 7) {
+ buf [1] = 0;
+ length++;
+ }
+
+ /* look at each port */
+ for (i = 0; i < ports; i++) {
+ u32 status = roothub_portstatus (ohci, i);
+
+ status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
+ | RH_PS_OCIC | RH_PS_PRSC;
+ if (status) {
+ changed = 1;
+ if (i < 7)
+ buf [0] |= 1 << (i + 1);
+ else
+ buf [1] |= 1 << (i - 7);
+ }
+ }
+ return changed ? length : 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+ohci_hub_descriptor (
+ struct ohci_hcd *ohci,
+ struct usb_hub_descriptor *desc
+) {
+ u32 rh = roothub_a (ohci);
+ int ports = rh & RH_A_NDP;
+ u16 temp;
+
+ desc->bDescriptorType = 0x29;
+ desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
+ desc->bHubContrCurrent = 0;
+
+ desc->bNbrPorts = ports;
+ temp = 1 + (ports / 8);
+ desc->bDescLength = 7 + 2 * temp;
+
+ temp = 0;
+ if (rh & RH_A_PSM) /* per-port power switching? */
+ temp |= 0x0001;
+ if (rh & RH_A_NOCP) /* no overcurrent reporting? */
+ temp |= 0x0010;
+ else if (rh & RH_A_OCPM) /* per-port overcurrent reporting? */
+ temp |= 0x0008;
+ desc->wHubCharacteristics = cpu_to_le16 (temp);
+
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ rh = roothub_b (ohci);
+ desc->bitmap [0] = rh & RH_B_DR;
+ if (ports > 7) {
+ desc->bitmap [1] = (rh & RH_B_DR) >> 8;
+ desc->bitmap [2] = desc->bitmap [3] = 0xff;
+ } else
+ desc->bitmap [1] = 0xff;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_hub_control (
+ struct usb_hcd *hcd,
+ u16 typeReq,
+ u16 wValue,
+ u16 wIndex,
+ char *buf,
+ u16 wLength
+) {
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ports = hcd_to_bus (hcd)->root_hub->maxchild;
+ u32 temp;
+ int retval = 0;
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ writel (RH_HS_OCIC, &ohci->regs->roothub.status);
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case ClearPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ temp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ temp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ temp = RH_PS_POCI;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ temp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ temp = RH_PS_LSDA;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ temp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ temp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ temp = RH_PS_PRSC;
+ break;
+ default:
+ goto error;
+ }
+ writel (temp, &ohci->regs->roothub.portstatus [wIndex]);
+ // readl (&ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case GetHubDescriptor:
+ ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+ temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
+ *(u32 *) buf = cpu_to_le32 (temp);
+ break;
+ case GetPortStatus:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ temp = roothub_portstatus (ohci, wIndex);
+ *(u32 *) buf = cpu_to_le32 (temp);
+
+#ifndef OHCI_VERBOSE_DEBUG
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+#endif
+ dbg_port (ohci, "GetStatus", wIndex + 1, temp);
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ // FIXME: this can be cleared, yes?
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetPortFeature:
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ writel (RH_PS_PSS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case USB_PORT_FEAT_POWER:
+ writel (RH_PS_PPS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ case USB_PORT_FEAT_RESET:
+ temp = readl (&ohci->regs->roothub.portstatus [wIndex]);
+ if (temp & RH_PS_CCS)
+ writel (RH_PS_PRS,
+ &ohci->regs->roothub.portstatus [wIndex]);
+ break;
+ default:
+ goto error;
+ }
+ break;
+
+ default:
+error:
+ /* "protocol stall" on error */
+ retval = -EPIPE;
+ }
+ return retval;
+}
+
-/*\r
- * OHCI HCD (Host Controller Driver) for USB.\r
- * \r
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>\r
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>\r
- * \r
- * This file is licenced under the GPL.\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * There's basically three types of memory:\r
- * - data used only by the HCD ... kmalloc is fine\r
- * - async and periodic schedules, shared by HC and HCD ... these\r
- * need to use pci_pool or pci_alloc_consistent\r
- * - driver buffers, read/written by HC ... the hcd glue or the\r
- * device driver provides us with dma addresses\r
- *\r
- * There's also PCI "register" data, which is memory mapped.\r
- * No memory seen by this driver is pagable.\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static struct usb_hcd *ohci_hcd_alloc (void)\r
-{\r
- struct ohci_hcd *ohci;\r
-\r
- ohci = (struct ohci_hcd *) kmalloc (sizeof *ohci, GFP_KERNEL);\r
- if (ohci != 0) {\r
- memset (ohci, 0, sizeof (struct ohci_hcd));\r
- return &ohci->hcd;\r
- }\r
- return 0;\r
-}\r
-\r
-static void ohci_hcd_free (struct usb_hcd *hcd)\r
-{\r
- kfree (hcd_to_ohci (hcd));\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static int ohci_mem_init (struct ohci_hcd *ohci)\r
-{\r
- ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev,\r
- sizeof (struct td),\r
- 32 /* byte alignment */,\r
- 0 /* no page-crossing issues */);\r
- if (!ohci->td_cache)\r
- return -ENOMEM;\r
- ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev,\r
- sizeof (struct ed),\r
- 16 /* byte alignment */,\r
- 0 /* no page-crossing issues */);\r
- if (!ohci->ed_cache) {\r
- pci_pool_destroy (ohci->td_cache);\r
- return -ENOMEM;\r
- }\r
- return 0;\r
-}\r
-\r
-static void ohci_mem_cleanup (struct ohci_hcd *ohci)\r
-{\r
- if (ohci->td_cache) {\r
- pci_pool_destroy (ohci->td_cache);\r
- ohci->td_cache = 0;\r
- }\r
- if (ohci->ed_cache) {\r
- pci_pool_destroy (ohci->ed_cache);\r
- ohci->ed_cache = 0;\r
- }\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* ohci "done list" processing needs this mapping */\r
-static inline struct td *\r
-dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)\r
-{\r
- struct td *td;\r
-\r
- td_dma &= TD_MASK;\r
- td = hc->td_hash [TD_HASH_FUNC(td_dma)];\r
- while (td && td->td_dma != td_dma)\r
- td = td->td_hash;\r
- return td;\r
-}\r
-\r
-/* TDs ... */\r
-static struct td *\r
-td_alloc (struct ohci_hcd *hc, int mem_flags)\r
-{\r
- dma_addr_t dma;\r
- struct td *td;\r
-\r
- td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);\r
- if (td) {\r
- /* in case hc fetches it, make it look dead */\r
- memset (td, 0, sizeof *td);\r
- td->hwNextTD = cpu_to_le32 (dma);\r
- td->td_dma = dma;\r
- /* hashed in td_fill */\r
- }\r
- return td;\r
-}\r
-\r
-static void\r
-td_free (struct ohci_hcd *hc, struct td *td)\r
-{\r
- struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];\r
-\r
- while (*prev && *prev != td)\r
- prev = &(*prev)->td_hash;\r
- if (*prev)\r
- *prev = td->td_hash;\r
- else if ((td->hwINFO & TD_DONE) != 0)\r
- ohci_dbg (hc, "no hash for td %p\n", td);\r
- pci_pool_free (hc->td_cache, td, td->td_dma);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* EDs ... */\r
-static struct ed *\r
-ed_alloc (struct ohci_hcd *hc, int mem_flags)\r
-{\r
- dma_addr_t dma;\r
- struct ed *ed;\r
-\r
- ed = pci_pool_alloc (hc->ed_cache, mem_flags, &dma);\r
- if (ed) {\r
- memset (ed, 0, sizeof (*ed));\r
- INIT_LIST_HEAD (&ed->td_list);\r
- ed->dma = dma;\r
- }\r
- return ed;\r
-}\r
-\r
-static void\r
-ed_free (struct ohci_hcd *hc, struct ed *ed)\r
-{\r
- pci_pool_free (hc->ed_cache, ed, ed->dma);\r
-}\r
-\r
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * There's basically three types of memory:
+ * - data used only by the HCD ... kmalloc is fine
+ * - async and periodic schedules, shared by HC and HCD ... these
+ * need to use pci_pool or pci_alloc_consistent
+ * - driver buffers, read/written by HC ... the hcd glue or the
+ * device driver provides us with dma addresses
+ *
+ * There's also PCI "register" data, which is memory mapped.
+ * No memory seen by this driver is pagable.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_hcd *ohci_hcd_alloc (void)
+{
+ struct ohci_hcd *ohci;
+
+ ohci = (struct ohci_hcd *) kmalloc (sizeof *ohci, GFP_KERNEL);
+ if (ohci != 0) {
+ memset (ohci, 0, sizeof (struct ohci_hcd));
+ return &ohci->hcd;
+ }
+ return 0;
+}
+
+static void ohci_hcd_free (struct usb_hcd *hcd)
+{
+ kfree (hcd_to_ohci (hcd));
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ohci_mem_init (struct ohci_hcd *ohci)
+{
+ ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev,
+ sizeof (struct td),
+ 32 /* byte alignment */,
+ 0 /* no page-crossing issues */);
+ if (!ohci->td_cache)
+ return -ENOMEM;
+ ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev,
+ sizeof (struct ed),
+ 16 /* byte alignment */,
+ 0 /* no page-crossing issues */);
+ if (!ohci->ed_cache) {
+ pci_pool_destroy (ohci->td_cache);
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void ohci_mem_cleanup (struct ohci_hcd *ohci)
+{
+ if (ohci->td_cache) {
+ pci_pool_destroy (ohci->td_cache);
+ ohci->td_cache = 0;
+ }
+ if (ohci->ed_cache) {
+ pci_pool_destroy (ohci->ed_cache);
+ ohci->ed_cache = 0;
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ohci "done list" processing needs this mapping */
+static inline struct td *
+dma_to_td (struct ohci_hcd *hc, dma_addr_t td_dma)
+{
+ struct td *td;
+
+ td_dma &= TD_MASK;
+ td = hc->td_hash [TD_HASH_FUNC(td_dma)];
+ while (td && td->td_dma != td_dma)
+ td = td->td_hash;
+ return td;
+}
+
+/* TDs ... */
+static struct td *
+td_alloc (struct ohci_hcd *hc, int mem_flags)
+{
+ dma_addr_t dma;
+ struct td *td;
+
+ td = pci_pool_alloc (hc->td_cache, mem_flags, &dma);
+ if (td) {
+ /* in case hc fetches it, make it look dead */
+ memset (td, 0, sizeof *td);
+ td->hwNextTD = cpu_to_le32 (dma);
+ td->td_dma = dma;
+ /* hashed in td_fill */
+ }
+ return td;
+}
+
+static void
+td_free (struct ohci_hcd *hc, struct td *td)
+{
+ struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)];
+
+ while (*prev && *prev != td)
+ prev = &(*prev)->td_hash;
+ if (*prev)
+ *prev = td->td_hash;
+ else if ((td->hwINFO & TD_DONE) != 0)
+ ohci_dbg (hc, "no hash for td %p\n", td);
+ pci_pool_free (hc->td_cache, td, td->td_dma);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* EDs ... */
+static struct ed *
+ed_alloc (struct ohci_hcd *hc, int mem_flags)
+{
+ dma_addr_t dma;
+ struct ed *ed;
+
+ ed = pci_pool_alloc (hc->ed_cache, mem_flags, &dma);
+ if (ed) {
+ memset (ed, 0, sizeof (*ed));
+ INIT_LIST_HEAD (&ed->td_list);
+ ed->dma = dma;
+ }
+ return ed;
+}
+
+static void
+ed_free (struct ohci_hcd *hc, struct ed *ed)
+{
+ pci_pool_free (hc->ed_cache, ed, ed->dma);
+}
+
-/*\r
- * OHCI HCD (Host Controller Driver) for USB.\r
- *\r
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>\r
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>\r
- * \r
- * [ Initialisation is based on Linus' ]\r
- * [ uhci code and gregs ohci fragments ]\r
- * [ (C) Copyright 1999 Linus Torvalds ]\r
- * [ (C) Copyright 1999 Gregory P. Smith]\r
- * \r
- * PCI Bus Glue\r
- *\r
- * This file is licenced under the GPL.\r
- */\r
- \r
-#ifdef CONFIG_PMAC_PBOOK\r
-#include <asm/machdep.h>\r
-#include <asm/pmac_feature.h>\r
-#include <asm/pci-bridge.h>\r
-#include <asm/prom.h>\r
-#ifndef CONFIG_PM\r
-# define CONFIG_PM\r
-#endif\r
-#endif\r
-\r
-#ifndef CONFIG_PCI\r
-#error "This file is PCI bus glue. CONFIG_PCI must be defined."\r
-#endif\r
-\r
-#include "../linux/pci_ids.h"\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static int __devinit\r
-ohci_pci_start (struct usb_hcd *hcd)\r
-{\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- int ret;\r
-\r
- DPRINT("ohci_pci_start()\n");\r
-\r
- if (hcd->pdev) {\r
- ohci->hcca = pci_alloc_consistent (hcd->pdev,\r
- sizeof *ohci->hcca, &ohci->hcca_dma);\r
- if (!ohci->hcca)\r
- return -ENOMEM;\r
-\r
- /* AMD 756, for most chips (early revs), corrupts register\r
- * values on read ... so enable the vendor workaround.\r
- */\r
- if (hcd->pdev->vendor == PCI_VENDOR_ID_AMD\r
- && hcd->pdev->device == 0x740c) {\r
- ohci->flags = OHCI_QUIRK_AMD756;\r
- ohci_info (ohci, "AMD756 erratum 4 workaround\n");\r
- }\r
-\r
- /* FIXME for some of the early AMD 760 southbridges, OHCI\r
- * won't work at all. blacklist them.\r
- */\r
-\r
- /* Apple's OHCI driver has a lot of bizarre workarounds\r
- * for this chip. Evidently control and bulk lists\r
- * can get confused. (B&W G3 models, and ...)\r
- */\r
- else if (hcd->pdev->vendor == PCI_VENDOR_ID_OPTI\r
- && hcd->pdev->device == 0xc861) {\r
- ohci_info (ohci,\r
- "WARNING: OPTi workarounds unavailable\n");\r
- }\r
-\r
- /* Check for NSC87560. We have to look at the bridge (fn1) to\r
- * identify the USB (fn2). This quirk might apply to more or\r
- * even all NSC stuff.\r
- */\r
- else if (hcd->pdev->vendor == PCI_VENDOR_ID_NS) {\r
- struct pci_dev *b, *hc;\r
-\r
- hc = hcd->pdev;\r
- b = pci_find_slot (hc->bus->number,\r
- PCI_DEVFN (PCI_SLOT (hc->devfn), 1));\r
- if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO\r
- && b->vendor == PCI_VENDOR_ID_NS) {\r
- ohci->flags |= OHCI_QUIRK_SUPERIO;\r
- ohci_info (ohci, "Using NSC SuperIO setup\n");\r
- }\r
- }\r
- \r
- }\r
-\r
- memset (ohci->hcca, 0, sizeof (struct ohci_hcca));\r
- if ((ret = ohci_mem_init (ohci)) < 0) {\r
- ohci_stop (hcd);\r
- return ret;\r
- }\r
- ohci->regs = hcd->regs;\r
-\r
- DPRINT("Controller memory init done\n");\r
-\r
- if (hc_reset (ohci) < 0) {\r
- ohci_stop (hcd);\r
- return -ENODEV;\r
- }\r
- DPRINT("Controller reset done\n");\r
-\r
- if (hc_start (ohci) < 0) {\r
- ohci_err (ohci, "can't start\n");\r
- ohci_stop (hcd);\r
- return -EBUSY;\r
- }\r
- DPRINT("Controller start done\n");\r
-\r
-#ifdef DEBUG\r
- ohci_dump (ohci, 1);\r
-#endif\r
- return 0;\r
-}\r
-\r
-#ifdef CONFIG_PM\r
-\r
-static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)\r
-{\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- unsigned long flags;\r
- u16 cmd;\r
-\r
- if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {\r
- ohci_dbg (ohci, "can't suspend (state is %s)\n",\r
- hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));\r
- return -EIO;\r
- }\r
-\r
- /* act as if usb suspend can always be used */\r
- ohci_dbg (ohci, "suspend to %d\n", state);\r
- ohci->sleeping = 1;\r
-\r
- /* First stop processing */\r
- spin_lock_irqsave (&ohci->lock, flags);\r
- ohci->hc_control &=\r
- ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- writel (OHCI_INTR_SF, &ohci->regs->intrstatus);\r
- (void) readl (&ohci->regs->intrstatus);\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
-\r
- /* Wait a frame or two */\r
- mdelay (1);\r
- if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)\r
- mdelay (1);\r
- \r
-#ifdef CONFIG_PMAC_PBOOK\r
- if (_machine == _MACH_Pmac)\r
- disable_irq (hcd->pdev->irq);\r
- /* else, 2.4 assumes shared irqs -- don't disable */\r
-#endif\r
-\r
- /* Enable remote wakeup */\r
- writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD,\r
- &ohci->regs->intrenable);\r
-\r
- /* Suspend chip and let things settle down a bit */\r
- ohci->hc_control = OHCI_USB_SUSPEND;\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- (void) readl (&ohci->regs->control);\r
- mdelay (500); /* No schedule here ! */\r
-\r
- switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {\r
- case OHCI_USB_RESET:\r
- ohci_dbg (ohci, "suspend->reset ?\n");\r
- break;\r
- case OHCI_USB_RESUME:\r
- ohci_dbg (ohci, "suspend->resume ?\n");\r
- break;\r
- case OHCI_USB_OPER:\r
- ohci_dbg (ohci, "suspend->operational ?\n");\r
- break;\r
- case OHCI_USB_SUSPEND:\r
- ohci_dbg (ohci, "suspended\n");\r
- break;\r
- }\r
-\r
- /* In some rare situations, Apple's OHCI have happily trashed\r
- * memory during sleep. We disable its bus master bit during\r
- * suspend\r
- */\r
- pci_read_config_word (hcd->pdev, PCI_COMMAND, &cmd);\r
- cmd &= ~PCI_COMMAND_MASTER;\r
- pci_write_config_word (hcd->pdev, PCI_COMMAND, cmd);\r
-#ifdef CONFIG_PMAC_PBOOK\r
- {\r
- struct device_node *of_node;\r
- \r
- /* Disable USB PAD & cell clock */\r
- of_node = pci_device_to_OF_node (hcd->pdev);\r
- if (of_node)\r
- pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);\r
- }\r
-#endif\r
- return 0;\r
-}\r
-\r
-\r
-static int ohci_pci_resume (struct usb_hcd *hcd)\r
-{\r
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);\r
- int temp;\r
- int retval = 0;\r
- unsigned long flags;\r
-\r
-#ifdef CONFIG_PMAC_PBOOK\r
- {\r
- struct device_node *of_node;\r
-\r
- /* Re-enable USB PAD & cell clock */\r
- of_node = pci_device_to_OF_node (hcd->pdev);\r
- if (of_node)\r
- pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);\r
- }\r
-#endif\r
- /* did we suspend, or were we powered off? */\r
- ohci->hc_control = readl (&ohci->regs->control);\r
- temp = ohci->hc_control & OHCI_CTRL_HCFS;\r
-\r
-#ifdef DEBUG\r
- /* the registers may look crazy here */\r
- ohci_dump_status (ohci, 0, 0);\r
-#endif\r
-\r
- /* Re-enable bus mastering */\r
- pci_set_master (ohci->hcd.pdev);\r
- \r
- switch (temp) {\r
-\r
- case OHCI_USB_RESET: // lost power\r
- ohci_info (ohci, "USB restart\n");\r
- retval = hc_restart (ohci);\r
- break;\r
-\r
- case OHCI_USB_SUSPEND: // host wakeup\r
- case OHCI_USB_RESUME: // remote wakeup\r
- ohci_info (ohci, "USB continue from %s wakeup\n",\r
- (temp == OHCI_USB_SUSPEND)\r
- ? "host" : "remote");\r
- ohci->hc_control = OHCI_USB_RESUME;\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- (void) readl (&ohci->regs->control);\r
- mdelay (20); /* no schedule here ! */\r
- /* Some controllers (lucent) need a longer delay here */\r
- mdelay (15);\r
-\r
- temp = readl (&ohci->regs->control);\r
- temp = ohci->hc_control & OHCI_CTRL_HCFS;\r
- if (temp != OHCI_USB_RESUME) {\r
- ohci_err (ohci, "controller won't resume\n");\r
- ohci->disabled = 1;\r
- retval = -EIO;\r
- break;\r
- }\r
-\r
- /* Some chips likes being resumed first */\r
- writel (OHCI_USB_OPER, &ohci->regs->control);\r
- (void) readl (&ohci->regs->control);\r
- mdelay (3);\r
-\r
- /* Then re-enable operations */\r
- spin_lock_irqsave (&ohci->lock, flags);\r
- ohci->disabled = 0;\r
- ohci->sleeping = 0;\r
- ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;\r
- if (!ohci->ed_rm_list) {\r
- if (ohci->ed_controltail)\r
- ohci->hc_control |= OHCI_CTRL_CLE;\r
- if (ohci->ed_bulktail)\r
- ohci->hc_control |= OHCI_CTRL_BLE;\r
- }\r
- hcd->state = USB_STATE_READY;\r
- writel (ohci->hc_control, &ohci->regs->control);\r
-\r
- /* trigger a start-frame interrupt (why?) */\r
- writel (OHCI_INTR_SF, &ohci->regs->intrstatus);\r
- writel (OHCI_INTR_SF, &ohci->regs->intrenable);\r
-\r
- /* Check for a pending done list */\r
- writel (OHCI_INTR_WDH, &ohci->regs->intrdisable); \r
- (void) readl (&ohci->regs->intrdisable);\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
-\r
-#ifdef CONFIG_PMAC_PBOOK\r
- if (_machine == _MACH_Pmac)\r
- enable_irq (hcd->pdev->irq);\r
-#endif\r
- if (ohci->hcca->done_head)\r
- dl_done_list (ohci, dl_reverse_done_list (ohci), NULL);\r
- writel (OHCI_INTR_WDH, &ohci->regs->intrenable); \r
-\r
- /* assume there are TDs on the bulk and control lists */\r
- writel (OHCI_BLF | OHCI_CLF, &ohci->regs->cmdstatus);\r
-\r
-// ohci_dump_status (ohci);\r
-ohci_dbg (ohci, "sleeping = %d, disabled = %d\n",\r
- ohci->sleeping, ohci->disabled);\r
- break;\r
-\r
- default:\r
- ohci_warn (ohci, "odd PCI resume\n");\r
- }\r
- return retval;\r
-}\r
-\r
-#endif /* CONFIG_PM */\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static const struct hc_driver ohci_pci_hc_driver = {\r
- .description = hcd_name,\r
-\r
- /*\r
- * generic hardware linkage\r
- */\r
- .irq = ohci_irq,\r
- .flags = HCD_MEMORY | HCD_USB11,\r
-\r
- /*\r
- * basic lifecycle operations\r
- */\r
- .start = ohci_pci_start,\r
-#ifdef CONFIG_PM\r
- .suspend = ohci_pci_suspend,\r
- .resume = ohci_pci_resume,\r
-#endif\r
- .stop = ohci_stop,\r
-\r
- /*\r
- * memory lifecycle (except per-request)\r
- */\r
- .hcd_alloc = ohci_hcd_alloc,\r
- .hcd_free = ohci_hcd_free,\r
-\r
- /*\r
- * managing i/o requests and associated device resources\r
- */\r
- .urb_enqueue = ohci_urb_enqueue,\r
- .urb_dequeue = ohci_urb_dequeue,\r
- .endpoint_disable = ohci_endpoint_disable,\r
-\r
- /*\r
- * scheduling support\r
- */\r
- .get_frame_number = ohci_get_frame,\r
-\r
- /*\r
- * root hub support\r
- */\r
- .hub_status_data = ohci_hub_status_data,\r
- .hub_control = ohci_hub_control,\r
-};\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-const struct pci_device_id __devinitdata pci_ids [] = { {\r
-\r
- /* handle any USB OHCI controller */\r
- .class = (PCI_CLASS_SERIAL_USB << 8) | 0x10,\r
- .class_mask = ~0,\r
- .driver_data = (unsigned long) &ohci_pci_hc_driver,\r
-\r
- /* no matter who makes it */\r
- .vendor = PCI_ANY_ID,\r
- .device = PCI_ANY_ID,\r
- .subvendor = PCI_ANY_ID,\r
- .subdevice = PCI_ANY_ID,\r
-\r
- }, { /* end: all zeroes */ }\r
-};\r
-MODULE_DEVICE_TABLE (pci, pci_ids);\r
-\r
-/* pci driver glue; this is a "new style" PCI driver module */\r
-struct pci_driver ohci_pci_driver = {\r
- .name = (char *) hcd_name,\r
- .id_table = pci_ids,\r
-\r
- .probe = usb_hcd_pci_probe,\r
- .remove = usb_hcd_pci_remove,\r
-\r
-#ifdef CONFIG_PM\r
- .suspend = usb_hcd_pci_suspend,\r
- .resume = usb_hcd_pci_resume,\r
-#endif\r
-};\r
-\r
- \r
-int ohci_hcd_pci_init (void) \r
-{\r
- printk (KERN_DEBUG "%s: " DRIVER_INFO " (PCI)\n", hcd_name);\r
- if (usb_disabled())\r
- return -ENODEV;\r
-\r
- // causes page fault in reactos\r
- //printk (KERN_DEBUG "%s: block sizes: ed %Zd td %Zd\n", hcd_name,\r
- // sizeof (struct ed), sizeof (struct td));\r
- return pci_module_init (&ohci_pci_driver);\r
-}\r
-/*module_init (ohci_hcd_pci_init);*/\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-void ohci_hcd_pci_cleanup (void) \r
-{ \r
- pci_unregister_driver (&ohci_pci_driver);\r
-}\r
-/*module_exit (ohci_hcd_pci_cleanup);*/\r
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * [ Initialisation is based on Linus' ]
+ * [ uhci code and gregs ohci fragments ]
+ * [ (C) Copyright 1999 Linus Torvalds ]
+ * [ (C) Copyright 1999 Gregory P. Smith]
+ *
+ * PCI Bus Glue
+ *
+ * This file is licenced under the GPL.
+ */
+
+#ifdef CONFIG_PMAC_PBOOK
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
+#include <asm/prom.h>
+#ifndef CONFIG_PM
+# define CONFIG_PM
+#endif
+#endif
+
+#ifndef CONFIG_PCI
+#error "This file is PCI bus glue. CONFIG_PCI must be defined."
+#endif
+
+#include "../linux/pci_ids.h"
+
+/*-------------------------------------------------------------------------*/
+
+static int __devinit
+ohci_pci_start (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int ret;
+
+ DPRINT("ohci_pci_start()\n");
+
+ if (hcd->pdev) {
+ ohci->hcca = pci_alloc_consistent (hcd->pdev,
+ sizeof *ohci->hcca, &ohci->hcca_dma);
+ if (!ohci->hcca)
+ return -ENOMEM;
+
+ /* AMD 756, for most chips (early revs), corrupts register
+ * values on read ... so enable the vendor workaround.
+ */
+ if (hcd->pdev->vendor == PCI_VENDOR_ID_AMD
+ && hcd->pdev->device == 0x740c) {
+ ohci->flags = OHCI_QUIRK_AMD756;
+ ohci_info (ohci, "AMD756 erratum 4 workaround\n");
+ }
+
+ /* FIXME for some of the early AMD 760 southbridges, OHCI
+ * won't work at all. blacklist them.
+ */
+
+ /* Apple's OHCI driver has a lot of bizarre workarounds
+ * for this chip. Evidently control and bulk lists
+ * can get confused. (B&W G3 models, and ...)
+ */
+ else if (hcd->pdev->vendor == PCI_VENDOR_ID_OPTI
+ && hcd->pdev->device == 0xc861) {
+ ohci_info (ohci,
+ "WARNING: OPTi workarounds unavailable\n");
+ }
+
+ /* Check for NSC87560. We have to look at the bridge (fn1) to
+ * identify the USB (fn2). This quirk might apply to more or
+ * even all NSC stuff.
+ */
+ else if (hcd->pdev->vendor == PCI_VENDOR_ID_NS) {
+ struct pci_dev *b, *hc;
+
+ hc = hcd->pdev;
+ b = pci_find_slot (hc->bus->number,
+ PCI_DEVFN (PCI_SLOT (hc->devfn), 1));
+ if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO
+ && b->vendor == PCI_VENDOR_ID_NS) {
+ ohci->flags |= OHCI_QUIRK_SUPERIO;
+ ohci_info (ohci, "Using NSC SuperIO setup\n");
+ }
+ }
+
+ }
+
+ memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+ if ((ret = ohci_mem_init (ohci)) < 0) {
+ ohci_stop (hcd);
+ return ret;
+ }
+ ohci->regs = hcd->regs;
+
+ DPRINT("Controller memory init done\n");
+
+ if (hc_reset (ohci) < 0) {
+ ohci_stop (hcd);
+ return -ENODEV;
+ }
+ DPRINT("Controller reset done\n");
+
+ if (hc_start (ohci) < 0) {
+ ohci_err (ohci, "can't start\n");
+ ohci_stop (hcd);
+ return -EBUSY;
+ }
+ DPRINT("Controller start done\n");
+
+#ifdef DEBUG
+ ohci_dump (ohci, 1);
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ unsigned long flags;
+ u16 cmd;
+
+ if ((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER) {
+ ohci_dbg (ohci, "can't suspend (state is %s)\n",
+ hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS));
+ return -EIO;
+ }
+
+ /* act as if usb suspend can always be used */
+ ohci_dbg (ohci, "suspend to %d\n", state);
+ ohci->sleeping = 1;
+
+ /* First stop processing */
+ spin_lock_irqsave (&ohci->lock, flags);
+ ohci->hc_control &=
+ ~(OHCI_CTRL_PLE|OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_IE);
+ writel (ohci->hc_control, &ohci->regs->control);
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ (void) readl (&ohci->regs->intrstatus);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+ /* Wait a frame or two */
+ mdelay (1);
+ if (!readl (&ohci->regs->intrstatus) & OHCI_INTR_SF)
+ mdelay (1);
+
+#ifdef CONFIG_PMAC_PBOOK
+ if (_machine == _MACH_Pmac)
+ disable_irq (hcd->pdev->irq);
+ /* else, 2.4 assumes shared irqs -- don't disable */
+#endif
+
+ /* Enable remote wakeup */
+ writel (readl (&ohci->regs->intrenable) | OHCI_INTR_RD,
+ &ohci->regs->intrenable);
+
+ /* Suspend chip and let things settle down a bit */
+ ohci->hc_control = OHCI_USB_SUSPEND;
+ writel (ohci->hc_control, &ohci->regs->control);
+ (void) readl (&ohci->regs->control);
+ mdelay (500); /* No schedule here ! */
+
+ switch (readl (&ohci->regs->control) & OHCI_CTRL_HCFS) {
+ case OHCI_USB_RESET:
+ ohci_dbg (ohci, "suspend->reset ?\n");
+ break;
+ case OHCI_USB_RESUME:
+ ohci_dbg (ohci, "suspend->resume ?\n");
+ break;
+ case OHCI_USB_OPER:
+ ohci_dbg (ohci, "suspend->operational ?\n");
+ break;
+ case OHCI_USB_SUSPEND:
+ ohci_dbg (ohci, "suspended\n");
+ break;
+ }
+
+ /* In some rare situations, Apple's OHCI have happily trashed
+ * memory during sleep. We disable its bus master bit during
+ * suspend
+ */
+ pci_read_config_word (hcd->pdev, PCI_COMMAND, &cmd);
+ cmd &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word (hcd->pdev, PCI_COMMAND, cmd);
+#ifdef CONFIG_PMAC_PBOOK
+ {
+ struct device_node *of_node;
+
+ /* Disable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (hcd->pdev);
+ if (of_node)
+ pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
+ }
+#endif
+ return 0;
+}
+
+
+static int ohci_pci_resume (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int temp;
+ int retval = 0;
+ unsigned long flags;
+
+#ifdef CONFIG_PMAC_PBOOK
+ {
+ struct device_node *of_node;
+
+ /* Re-enable USB PAD & cell clock */
+ of_node = pci_device_to_OF_node (hcd->pdev);
+ if (of_node)
+ pmac_call_feature (PMAC_FTR_USB_ENABLE, of_node, 0, 1);
+ }
+#endif
+ /* did we suspend, or were we powered off? */
+ ohci->hc_control = readl (&ohci->regs->control);
+ temp = ohci->hc_control & OHCI_CTRL_HCFS;
+
+#ifdef DEBUG
+ /* the registers may look crazy here */
+ ohci_dump_status (ohci, 0, 0);
+#endif
+
+ /* Re-enable bus mastering */
+ pci_set_master (ohci->hcd.pdev);
+
+ switch (temp) {
+
+ case OHCI_USB_RESET: // lost power
+ ohci_info (ohci, "USB restart\n");
+ retval = hc_restart (ohci);
+ break;
+
+ case OHCI_USB_SUSPEND: // host wakeup
+ case OHCI_USB_RESUME: // remote wakeup
+ ohci_info (ohci, "USB continue from %s wakeup\n",
+ (temp == OHCI_USB_SUSPEND)
+ ? "host" : "remote");
+ ohci->hc_control = OHCI_USB_RESUME;
+ writel (ohci->hc_control, &ohci->regs->control);
+ (void) readl (&ohci->regs->control);
+ mdelay (20); /* no schedule here ! */
+ /* Some controllers (lucent) need a longer delay here */
+ mdelay (15);
+
+ temp = readl (&ohci->regs->control);
+ temp = ohci->hc_control & OHCI_CTRL_HCFS;
+ if (temp != OHCI_USB_RESUME) {
+ ohci_err (ohci, "controller won't resume\n");
+ ohci->disabled = 1;
+ retval = -EIO;
+ break;
+ }
+
+ /* Some chips likes being resumed first */
+ writel (OHCI_USB_OPER, &ohci->regs->control);
+ (void) readl (&ohci->regs->control);
+ mdelay (3);
+
+ /* Then re-enable operations */
+ spin_lock_irqsave (&ohci->lock, flags);
+ ohci->disabled = 0;
+ ohci->sleeping = 0;
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ if (!ohci->ed_rm_list) {
+ if (ohci->ed_controltail)
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ if (ohci->ed_bulktail)
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ }
+ hcd->state = USB_STATE_READY;
+ writel (ohci->hc_control, &ohci->regs->control);
+
+ /* trigger a start-frame interrupt (why?) */
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+
+ /* Check for a pending done list */
+ writel (OHCI_INTR_WDH, &ohci->regs->intrdisable);
+ (void) readl (&ohci->regs->intrdisable);
+ spin_unlock_irqrestore (&ohci->lock, flags);
+
+#ifdef CONFIG_PMAC_PBOOK
+ if (_machine == _MACH_Pmac)
+ enable_irq (hcd->pdev->irq);
+#endif
+ if (ohci->hcca->done_head)
+ dl_done_list (ohci, dl_reverse_done_list (ohci), NULL);
+ writel (OHCI_INTR_WDH, &ohci->regs->intrenable);
+
+ /* assume there are TDs on the bulk and control lists */
+ writel (OHCI_BLF | OHCI_CLF, &ohci->regs->cmdstatus);
+
+// ohci_dump_status (ohci);
+ohci_dbg (ohci, "sleeping = %d, disabled = %d\n",
+ ohci->sleeping, ohci->disabled);
+ break;
+
+ default:
+ ohci_warn (ohci, "odd PCI resume\n");
+ }
+ return retval;
+}
+
+#endif /* CONFIG_PM */
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct hc_driver ohci_pci_hc_driver = {
+ .description = hcd_name,
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_pci_start,
+#ifdef CONFIG_PM
+ .suspend = ohci_pci_suspend,
+ .resume = ohci_pci_resume,
+#endif
+ .stop = ohci_stop,
+
+ /*
+ * memory lifecycle (except per-request)
+ */
+ .hcd_alloc = ohci_hcd_alloc,
+ .hcd_free = ohci_hcd_free,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+};
+
+/*-------------------------------------------------------------------------*/
+
+const struct pci_device_id __devinitdata pci_ids [] = { {
+
+ /* handle any USB OHCI controller */
+ .class = (PCI_CLASS_SERIAL_USB << 8) | 0x10,
+ .class_mask = ~0,
+ .driver_data = (unsigned long) &ohci_pci_hc_driver,
+
+ /* no matter who makes it */
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+
+ }, { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE (pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+struct pci_driver ohci_pci_driver = {
+ .name = (char *) hcd_name,
+ .id_table = pci_ids,
+
+ .probe = usb_hcd_pci_probe,
+ .remove = usb_hcd_pci_remove,
+
+#ifdef CONFIG_PM
+ .suspend = usb_hcd_pci_suspend,
+ .resume = usb_hcd_pci_resume,
+#endif
+};
+
+
+int ohci_hcd_pci_init (void)
+{
+ printk (KERN_DEBUG "%s: " DRIVER_INFO " (PCI)\n", hcd_name);
+ if (usb_disabled())
+ return -ENODEV;
+
+ // causes page fault in reactos
+ //printk (KERN_DEBUG "%s: block sizes: ed %Zd td %Zd\n", hcd_name,
+ // sizeof (struct ed), sizeof (struct td));
+ return pci_module_init (&ohci_pci_driver);
+}
+/*module_init (ohci_hcd_pci_init);*/
+
+/*-------------------------------------------------------------------------*/
+
+void ohci_hcd_pci_cleanup (void)
+{
+ pci_unregister_driver (&ohci_pci_driver);
+}
+/*module_exit (ohci_hcd_pci_cleanup);*/
-/*\r
- * OHCI HCD (Host Controller Driver) for USB.\r
- * \r
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>\r
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>\r
- * \r
- * This file is licenced under the GPL.\r
- */\r
-\r
-static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)\r
-{\r
- int last = urb_priv->length - 1;\r
-\r
- if (last >= 0) {\r
- int i;\r
- struct td *td;\r
-\r
- for (i = 0; i <= last; i++) {\r
- td = urb_priv->td [i];\r
- if (td)\r
- td_free (hc, td);\r
- }\r
- }\r
-\r
- kfree (urb_priv);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * URB goes back to driver, and isn't reissued.\r
- * It's completely gone from HC data structures.\r
- * PRECONDITION: no locks held, irqs blocked (Giveback can call into HCD.)\r
- */\r
-static void\r
-finish_urb (struct ohci_hcd *ohci, struct urb *urb, struct pt_regs *regs)\r
-{\r
- // ASSERT (urb->hcpriv != 0);\r
-\r
- urb_free_priv (ohci, urb->hcpriv);\r
- urb->hcpriv = NULL;\r
-\r
- spin_lock (&urb->lock);\r
- if (likely (urb->status == -EINPROGRESS))\r
- urb->status = 0;\r
- spin_unlock (&urb->lock);\r
-\r
- // what lock protects these?\r
- switch (usb_pipetype (urb->pipe)) {\r
- case PIPE_ISOCHRONOUS:\r
- hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs--;\r
- break;\r
- case PIPE_INTERRUPT:\r
- hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs--;\r
- break;\r
- }\r
-\r
-#ifdef OHCI_VERBOSE_DEBUG\r
- urb_print (urb, "RET", usb_pipeout (urb->pipe));\r
-#endif\r
- usb_hcd_giveback_urb (&ohci->hcd, urb, regs);\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------------*\r
- * ED handling functions\r
- *-------------------------------------------------------------------------*/ \r
-\r
-/* search for the right schedule branch to use for a periodic ed.\r
- * does some load balancing; returns the branch, or negative errno.\r
- */\r
-static int balance (struct ohci_hcd *ohci, int interval, int load)\r
-{\r
- int i, branch = -ENOSPC;\r
-\r
- /* iso periods can be huge; iso tds specify frame numbers */\r
- if (interval > NUM_INTS)\r
- interval = NUM_INTS;\r
-\r
- /* search for the least loaded schedule branch of that period\r
- * that has enough bandwidth left unreserved.\r
- */\r
- for (i = 0; i < interval ; i++) {\r
- if (branch < 0 || ohci->load [branch] > ohci->load [i]) {\r
-#if 1 /* CONFIG_USB_BANDWIDTH */\r
- int j;\r
-\r
- /* usb 1.1 says 90% of one frame */\r
- for (j = i; j < NUM_INTS; j += interval) {\r
- if ((ohci->load [j] + load) > 900)\r
- break;\r
- }\r
- if (j < NUM_INTS)\r
- continue;\r
-#endif\r
- branch = i; \r
- }\r
- }\r
- return branch;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* both iso and interrupt requests have periods; this routine puts them\r
- * into the schedule tree in the apppropriate place. most iso devices use\r
- * 1msec periods, but that's not required.\r
- */\r
-static void periodic_link (struct ohci_hcd *ohci, struct ed *ed)\r
-{\r
- unsigned i;\r
-\r
- ohci_vdbg (ohci, "link %sed %p branch %d [%dus.], interval %d\n",\r
- (ed->hwINFO & ED_ISO) ? "iso " : "",\r
- ed, ed->branch, ed->load, ed->interval);\r
-\r
- for (i = ed->branch; i < NUM_INTS; i += ed->interval) {\r
- struct ed **prev = &ohci->periodic [i];\r
- u32 *prev_p = &ohci->hcca->int_table [i];\r
- struct ed *here = *prev;\r
-\r
- /* sorting each branch by period (slow before fast)\r
- * lets us share the faster parts of the tree.\r
- * (plus maybe: put interrupt eds before iso)\r
- */\r
- while (here && ed != here) {\r
- if (ed->interval > here->interval)\r
- break;\r
- prev = &here->ed_next;\r
- prev_p = &here->hwNextED;\r
- here = *prev;\r
- }\r
- if (ed != here) {\r
- ed->ed_next = here;\r
- if (here)\r
- ed->hwNextED = *prev_p;\r
- wmb ();\r
- *prev = ed;\r
- *prev_p = cpu_to_le32p (&ed->dma);\r
- }\r
- ohci->load [i] += ed->load;\r
- }\r
- hcd_to_bus (&ohci->hcd)->bandwidth_allocated += ed->load / ed->interval;\r
-}\r
-\r
-/* link an ed into one of the HC chains */\r
-\r
-static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)\r
-{ \r
- int branch;\r
-\r
- ed->state = ED_OPER;\r
- ed->ed_prev = 0;\r
- ed->ed_next = 0;\r
- ed->hwNextED = 0;\r
- wmb ();\r
-\r
- /* we care about rm_list when setting CLE/BLE in case the HC was at\r
- * work on some TD when CLE/BLE was turned off, and isn't quiesced\r
- * yet. finish_unlinks() restarts as needed, some upcoming INTR_SF.\r
- *\r
- * control and bulk EDs are doubly linked (ed_next, ed_prev), but\r
- * periodic ones are singly linked (ed_next). that's because the\r
- * periodic schedule encodes a tree like figure 3-5 in the ohci\r
- * spec: each qh can have several "previous" nodes, and the tree\r
- * doesn't have unused/idle descriptors.\r
- */\r
- switch (ed->type) {\r
- case PIPE_CONTROL:\r
- if (ohci->ed_controltail == NULL) {\r
- writel (ed->dma, &ohci->regs->ed_controlhead);\r
- } else {\r
- ohci->ed_controltail->ed_next = ed;\r
- ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);\r
- }\r
- ed->ed_prev = ohci->ed_controltail;\r
- if (!ohci->ed_controltail && !ohci->ed_rm_list) {\r
- ohci->hc_control |= OHCI_CTRL_CLE;\r
- writel (0, &ohci->regs->ed_controlcurrent);\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- }\r
- ohci->ed_controltail = ed;\r
- break;\r
-\r
- case PIPE_BULK:\r
- if (ohci->ed_bulktail == NULL) {\r
- writel (ed->dma, &ohci->regs->ed_bulkhead);\r
- } else {\r
- ohci->ed_bulktail->ed_next = ed;\r
- ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);\r
- }\r
- ed->ed_prev = ohci->ed_bulktail;\r
- if (!ohci->ed_bulktail && !ohci->ed_rm_list) {\r
- ohci->hc_control |= OHCI_CTRL_BLE;\r
- writel (0, &ohci->regs->ed_bulkcurrent);\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- }\r
- ohci->ed_bulktail = ed;\r
- break;\r
-\r
- // case PIPE_INTERRUPT:\r
- // case PIPE_ISOCHRONOUS:\r
- default:\r
- branch = balance (ohci, ed->interval, ed->load);\r
- if (branch < 0) {\r
- ohci_dbg (ohci,\r
- "ERR %d, interval %d msecs, load %d\n",\r
- branch, ed->interval, ed->load);\r
- // FIXME if there are TDs queued, fail them!\r
- return branch;\r
- }\r
- ed->branch = branch;\r
- periodic_link (ohci, ed);\r
- } \r
-\r
- /* the HC may not see the schedule updates yet, but if it does\r
- * then they'll be properly ordered.\r
- */\r
- return 0;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* scan the periodic table to find and unlink this ED */\r
-static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed)\r
-{\r
- int i;\r
-\r
- for (i = ed->branch; i < NUM_INTS; i += ed->interval) {\r
- struct ed *temp;\r
- struct ed **prev = &ohci->periodic [i];\r
- u32 *prev_p = &ohci->hcca->int_table [i];\r
-\r
- while (*prev && (temp = *prev) != ed) {\r
- prev_p = &temp->hwNextED;\r
- prev = &temp->ed_next;\r
- }\r
- if (*prev) {\r
- *prev_p = ed->hwNextED;\r
- *prev = ed->ed_next;\r
- }\r
- ohci->load [i] -= ed->load;\r
- } \r
- hcd_to_bus (&ohci->hcd)->bandwidth_allocated -= ed->load / ed->interval;\r
-\r
- ohci_vdbg (ohci, "unlink %sed %p branch %d [%dus.], interval %d\n",\r
- (ed->hwINFO & ED_ISO) ? "iso " : "",\r
- ed, ed->branch, ed->load, ed->interval);\r
-}\r
-\r
-/* unlink an ed from one of the HC chains. \r
- * just the link to the ed is unlinked.\r
- * the link from the ed still points to another operational ed or 0\r
- * so the HC can eventually finish the processing of the unlinked ed\r
- */\r
-static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed) \r
-{\r
- ed->hwINFO |= ED_SKIP;\r
-\r
- switch (ed->type) {\r
- case PIPE_CONTROL:\r
- if (ed->ed_prev == NULL) {\r
- if (!ed->hwNextED) {\r
- ohci->hc_control &= ~OHCI_CTRL_CLE;\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- writel (0, &ohci->regs->ed_controlcurrent);\r
- // post those pci writes\r
- (void) readl (&ohci->regs->control);\r
- }\r
- writel (le32_to_cpup (&ed->hwNextED),\r
- &ohci->regs->ed_controlhead);\r
- } else {\r
- ed->ed_prev->ed_next = ed->ed_next;\r
- ed->ed_prev->hwNextED = ed->hwNextED;\r
- }\r
- if (ohci->ed_controltail == ed) {\r
- ohci->ed_controltail = ed->ed_prev;\r
- if (ohci->ed_controltail)\r
- ohci->ed_controltail->ed_next = 0;\r
- } else if (ed->ed_next) {\r
- ed->ed_next->ed_prev = ed->ed_prev;\r
- }\r
- break;\r
-\r
- case PIPE_BULK:\r
- if (ed->ed_prev == NULL) {\r
- if (!ed->hwNextED) {\r
- ohci->hc_control &= ~OHCI_CTRL_BLE;\r
- writel (ohci->hc_control, &ohci->regs->control);\r
- writel (0, &ohci->regs->ed_bulkcurrent);\r
- // post those pci writes\r
- (void) readl (&ohci->regs->control);\r
- }\r
- writel (le32_to_cpup (&ed->hwNextED),\r
- &ohci->regs->ed_bulkhead);\r
- } else {\r
- ed->ed_prev->ed_next = ed->ed_next;\r
- ed->ed_prev->hwNextED = ed->hwNextED;\r
- }\r
- if (ohci->ed_bulktail == ed) {\r
- ohci->ed_bulktail = ed->ed_prev;\r
- if (ohci->ed_bulktail)\r
- ohci->ed_bulktail->ed_next = 0;\r
- } else if (ed->ed_next) {\r
- ed->ed_next->ed_prev = ed->ed_prev;\r
- }\r
- break;\r
-\r
- // case PIPE_INTERRUPT:\r
- // case PIPE_ISOCHRONOUS:\r
- default:\r
- periodic_unlink (ohci, ed);\r
- break;\r
- }\r
-\r
- /* NOTE: Except for a couple of exceptionally clean unlink cases\r
- * (like unlinking the only c/b ED, with no TDs) HCs may still be\r
- * caching this operational ED (or its address). Safe unlinking\r
- * involves not marking it ED_IDLE till INTR_SF; we always do that\r
- * if td_list isn't empty. Otherwise the race is small; but ...\r
- */\r
- if (ed->state == ED_OPER) {\r
- ed->state = ED_IDLE;\r
- ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);\r
- ed->hwHeadP &= ~ED_H;\r
- wmb ();\r
- }\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* get and maybe (re)init an endpoint. init _should_ be done only as part\r
- * of usb_set_configuration() or usb_set_interface() ... but the USB stack\r
- * isn't very stateful, so we re-init whenever the HC isn't looking.\r
- */\r
-static struct ed *ed_get (\r
- struct ohci_hcd *ohci,\r
- struct usb_device *udev,\r
- unsigned int pipe,\r
- int interval\r
-) {\r
- int is_out = !usb_pipein (pipe);\r
- int type = usb_pipetype (pipe);\r
- struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv;\r
- struct ed *ed; \r
- unsigned ep;\r
- unsigned long flags;\r
-\r
- ep = usb_pipeendpoint (pipe) << 1;\r
- if (type != PIPE_CONTROL && is_out)\r
- ep |= 1;\r
-\r
- spin_lock_irqsave (&ohci->lock, flags);\r
-\r
- if (!(ed = dev->ep [ep])) {\r
- struct td *td;\r
-\r
- ed = ed_alloc (ohci, SLAB_ATOMIC);\r
- if (!ed) {\r
- /* out of memory */\r
- goto done;\r
- }\r
- dev->ep [ep] = ed;\r
-\r
- /* dummy td; end of td list for ed */\r
- td = td_alloc (ohci, SLAB_ATOMIC);\r
- if (!td) {\r
- /* out of memory */\r
- ed_free (ohci, ed);\r
- ed = 0;\r
- goto done;\r
- }\r
- ed->dummy = td;\r
- ed->hwTailP = cpu_to_le32 (td->td_dma);\r
- ed->hwHeadP = ed->hwTailP; /* ED_C, ED_H zeroed */\r
- ed->state = ED_IDLE;\r
- ed->type = type;\r
- }\r
-\r
- /* NOTE: only ep0 currently needs this "re"init logic, during\r
- * enumeration (after set_address, or if ep0 maxpacket >8).\r
- */\r
- if (ed->state == ED_IDLE) {\r
- u32 info;\r
-\r
- info = usb_pipedevice (pipe);\r
- info |= (ep >> 1) << 7;\r
- info |= usb_maxpacket (udev, pipe, is_out) << 16;\r
- info = cpu_to_le32 (info);\r
- if (udev->speed == USB_SPEED_LOW)\r
- info |= ED_LOWSPEED;\r
- /* only control transfers store pids in tds */\r
- if (type != PIPE_CONTROL) {\r
- info |= is_out ? ED_OUT : ED_IN;\r
- if (type != PIPE_BULK) {\r
- /* periodic transfers... */\r
- if (type == PIPE_ISOCHRONOUS)\r
- info |= ED_ISO;\r
- else if (interval > 32) /* iso can be bigger */\r
- interval = 32;\r
- ed->interval = interval;\r
- ed->load = usb_calc_bus_time (\r
- udev->speed, !is_out,\r
- type == PIPE_ISOCHRONOUS,\r
- usb_maxpacket (udev, pipe, is_out))\r
- / 1000;\r
- }\r
- }\r
- ed->hwINFO = info;\r
- }\r
-\r
-done:\r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
- return ed; \r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* request unlinking of an endpoint from an operational HC.\r
- * put the ep on the rm_list\r
- * real work is done at the next start frame (SF) hardware interrupt\r
- */\r
-static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)\r
-{ \r
- ed->hwINFO |= ED_DEQUEUE;\r
- ed->state = ED_UNLINK;\r
- ed_deschedule (ohci, ed);\r
-\r
- /* SF interrupt might get delayed; record the frame counter value that\r
- * indicates when the HC isn't looking at it, so concurrent unlinks\r
- * behave. frame_no wraps every 2^16 msec, and changes right before\r
- * SF is triggered.\r
- */\r
- ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1;\r
-\r
- /* rm_list is just singly linked, for simplicity */\r
- ed->ed_next = ohci->ed_rm_list;\r
- ed->ed_prev = 0;\r
- ohci->ed_rm_list = ed;\r
-\r
- /* enable SOF interrupt */\r
- if (!ohci->sleeping) {\r
- writel (OHCI_INTR_SF, &ohci->regs->intrstatus);\r
- writel (OHCI_INTR_SF, &ohci->regs->intrenable);\r
- // flush those pci writes\r
- (void) readl (&ohci->regs->control);\r
- }\r
-}\r
-\r
-/*-------------------------------------------------------------------------*\r
- * TD handling functions\r
- *-------------------------------------------------------------------------*/\r
-\r
-/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */\r
-\r
-static void\r
-td_fill (struct ohci_hcd *ohci, u32 info,\r
- dma_addr_t data, int len,\r
- struct urb *urb, int index)\r
-{\r
- struct td *td, *td_pt;\r
- struct urb_priv *urb_priv = urb->hcpriv;\r
- int is_iso = info & TD_ISO;\r
- int hash;\r
-\r
- // ASSERT (index < urb_priv->length);\r
-\r
- /* aim for only one interrupt per urb. mostly applies to control\r
- * and iso; other urbs rarely need more than one TD per urb.\r
- * this way, only final tds (or ones with an error) cause IRQs.\r
- * at least immediately; use DI=6 in case any control request is\r
- * tempted to die part way through.\r
- *\r
- * NOTE: could delay interrupts even for the last TD, and get fewer\r
- * interrupts ... increasing per-urb latency by sharing interrupts.\r
- * Drivers that queue bulk urbs may request that behavior.\r
- */\r
- if (index != (urb_priv->length - 1)\r
- || (urb->transfer_flags & URB_NO_INTERRUPT))\r
- info |= TD_DI_SET (6);\r
-\r
- /* use this td as the next dummy */\r
- td_pt = urb_priv->td [index];\r
-\r
- /* fill the old dummy TD */\r
- td = urb_priv->td [index] = urb_priv->ed->dummy;\r
- urb_priv->ed->dummy = td_pt;\r
-\r
- td->ed = urb_priv->ed;\r
- td->next_dl_td = NULL;\r
- td->index = index;\r
- td->urb = urb; \r
- td->data_dma = data;\r
- if (!len)\r
- data = 0;\r
-\r
- td->hwINFO = cpu_to_le32 (info);\r
- if (is_iso) {\r
- td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);\r
- td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);\r
- td->ed->last_iso = info & 0xffff;\r
- } else {\r
- td->hwCBP = cpu_to_le32 (data); \r
- } \r
- if (data)\r
- td->hwBE = cpu_to_le32 (data + len - 1);\r
- else\r
- td->hwBE = 0;\r
- td->hwNextTD = cpu_to_le32 (td_pt->td_dma);\r
-\r
- /* append to queue */\r
- list_add_tail (&td->td_list, &td->ed->td_list);\r
-\r
- /* hash it for later reverse mapping */\r
- hash = TD_HASH_FUNC (td->td_dma);\r
- td->td_hash = ohci->td_hash [hash];\r
- ohci->td_hash [hash] = td;\r
-\r
- /* HC might read the TD (or cachelines) right away ... */\r
- wmb ();\r
- td->ed->hwTailP = td->hwNextTD;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* Prepare all TDs of a transfer, and queue them onto the ED.\r
- * Caller guarantees HC is active.\r
- * Usually the ED is already on the schedule, so TDs might be\r
- * processed as soon as they're queued.\r
- */\r
-static void td_submit_urb (\r
- struct ohci_hcd *ohci,\r
- struct urb *urb\r
-) {\r
- struct urb_priv *urb_priv = urb->hcpriv;\r
- dma_addr_t data;\r
- int data_len = urb->transfer_buffer_length;\r
- int cnt = 0;\r
- u32 info = 0;\r
- int is_out = usb_pipeout (urb->pipe);\r
-\r
- /* OHCI handles the bulk/interrupt data toggles itself. We just\r
- * use the device toggle bits for resetting, and rely on the fact\r
- * that resetting toggle is meaningless if the endpoint is active.\r
- */\r
- if (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) {\r
- usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),\r
- is_out, 1);\r
- urb_priv->ed->hwHeadP &= ~ED_C;\r
- }\r
-\r
- urb_priv->td_cnt = 0;\r
-\r
- if (data_len)\r
- data = urb->transfer_dma;\r
- else\r
- data = 0;\r
-\r
- /* NOTE: TD_CC is set so we can tell which TDs the HC processed by\r
- * using TD_CC_GET, as well as by seeing them on the done list.\r
- * (CC = NotAccessed ... 0x0F, or 0x0E in PSWs for ISO.)\r
- */\r
- switch (urb_priv->ed->type) {\r
-\r
- /* Bulk and interrupt are identical except for where in the schedule\r
- * their EDs live.\r
- */\r
- case PIPE_INTERRUPT:\r
- /* ... and periodic urbs have extra accounting */\r
- hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs++;\r
- /* FALLTHROUGH */\r
- case PIPE_BULK:\r
- info = is_out\r
- ? TD_T_TOGGLE | TD_CC | TD_DP_OUT\r
- : TD_T_TOGGLE | TD_CC | TD_DP_IN;\r
- /* TDs _could_ transfer up to 8K each */\r
- while (data_len > 4096) {\r
- td_fill (ohci, info, data, 4096, urb, cnt);\r
- data += 4096;\r
- data_len -= 4096;\r
- cnt++;\r
- }\r
- /* maybe avoid ED halt on final TD short read */\r
- if (!(urb->transfer_flags & URB_SHORT_NOT_OK))\r
- info |= TD_R;\r
- td_fill (ohci, info, data, data_len, urb, cnt);\r
- cnt++;\r
- if ((urb->transfer_flags & URB_ZERO_PACKET)\r
- && cnt < urb_priv->length) {\r
- td_fill (ohci, info, 0, 0, urb, cnt);\r
- cnt++;\r
- }\r
- /* maybe kickstart bulk list */\r
- if (urb_priv->ed->type == PIPE_BULK) {\r
- wmb ();\r
- writel (OHCI_BLF, &ohci->regs->cmdstatus);\r
- }\r
- break;\r
-\r
- /* control manages DATA0/DATA1 toggle per-request; SETUP resets it,\r
- * any DATA phase works normally, and the STATUS ack is special.\r
- */\r
- case PIPE_CONTROL:\r
- info = TD_CC | TD_DP_SETUP | TD_T_DATA0;\r
- td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);\r
- if (data_len > 0) {\r
- info = TD_CC | TD_R | TD_T_DATA1;\r
- info |= is_out ? TD_DP_OUT : TD_DP_IN;\r
- /* NOTE: mishandles transfers >8K, some >4K */\r
- td_fill (ohci, info, data, data_len, urb, cnt++);\r
- }\r
- info = is_out\r
- ? TD_CC | TD_DP_IN | TD_T_DATA1\r
- : TD_CC | TD_DP_OUT | TD_T_DATA1;\r
- td_fill (ohci, info, data, 0, urb, cnt++);\r
- /* maybe kickstart control list */\r
- wmb ();\r
- writel (OHCI_CLF, &ohci->regs->cmdstatus);\r
- break;\r
-\r
- /* ISO has no retransmit, so no toggle; and it uses special TDs.\r
- * Each TD could handle multiple consecutive frames (interval 1);\r
- * we could often reduce the number of TDs here.\r
- */\r
- case PIPE_ISOCHRONOUS:\r
- for (cnt = 0; cnt < urb->number_of_packets; cnt++) {\r
- int frame = urb->start_frame;\r
-\r
- // FIXME scheduling should handle frame counter\r
- // roll-around ... exotic case (and OHCI has\r
- // a 2^16 iso range, vs other HCs max of 2^10)\r
- frame += cnt * urb->interval;\r
- frame &= 0xffff;\r
- td_fill (ohci, TD_CC | TD_ISO | frame,\r
- data + urb->iso_frame_desc [cnt].offset,\r
- urb->iso_frame_desc [cnt].length, urb, cnt);\r
- }\r
- hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs++;\r
- break;\r
- }\r
- // ASSERT (urb_priv->length == cnt);\r
-}\r
-\r
-/*-------------------------------------------------------------------------*\r
- * Done List handling functions\r
- *-------------------------------------------------------------------------*/\r
-\r
-/* calculate transfer length/status and update the urb\r
- * PRECONDITION: irqsafe (only for urb->status locking)\r
- */\r
-static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)\r
-{\r
- u32 tdINFO = le32_to_cpup (&td->hwINFO);\r
- int cc = 0;\r
-\r
- list_del (&td->td_list);\r
-\r
- /* ISO ... drivers see per-TD length/status */\r
- if (tdINFO & TD_ISO) {\r
- u16 tdPSW = le16_to_cpu (td->hwPSW [0]);\r
- int dlen = 0;\r
-\r
- /* NOTE: assumes FC in tdINFO == 0 (and MAXPSW == 1) */\r
-\r
- cc = (tdPSW >> 12) & 0xF;\r
- if (tdINFO & TD_CC) /* hc didn't touch? */\r
- return;\r
-\r
- if (usb_pipeout (urb->pipe))\r
- dlen = urb->iso_frame_desc [td->index].length;\r
- else {\r
- /* short reads are always OK for ISO */\r
- if (cc == TD_DATAUNDERRUN)\r
- cc = TD_CC_NOERROR;\r
- dlen = tdPSW & 0x3ff;\r
- }\r
- urb->actual_length += dlen;\r
- urb->iso_frame_desc [td->index].actual_length = dlen;\r
- urb->iso_frame_desc [td->index].status = cc_to_error [cc];\r
-\r
- if (cc != TD_CC_NOERROR)\r
- ohci_vdbg (ohci,\r
- "urb %p iso td %p (%d) len %d cc %d\n",\r
- urb, td, 1 + td->index, dlen, cc);\r
-\r
- /* BULK, INT, CONTROL ... drivers see aggregate length/status,\r
- * except that "setup" bytes aren't counted and "short" transfers\r
- * might not be reported as errors.\r
- */\r
- } else {\r
- int type = usb_pipetype (urb->pipe);\r
- u32 tdBE = le32_to_cpup (&td->hwBE);\r
-\r
- cc = TD_CC_GET (tdINFO);\r
-\r
- /* control endpoints only have soft stalls */\r
- if (type != PIPE_CONTROL && cc == TD_CC_STALL)\r
- usb_endpoint_halt (urb->dev,\r
- usb_pipeendpoint (urb->pipe),\r
- usb_pipeout (urb->pipe));\r
-\r
- /* update packet status if needed (short is normally ok) */\r
- if (cc == TD_DATAUNDERRUN\r
- && !(urb->transfer_flags & URB_SHORT_NOT_OK))\r
- cc = TD_CC_NOERROR;\r
- if (cc != TD_CC_NOERROR && cc < 0x0E) {\r
- spin_lock (&urb->lock);\r
- if (urb->status == -EINPROGRESS)\r
- urb->status = cc_to_error [cc];\r
- spin_unlock (&urb->lock);\r
- }\r
-\r
- /* count all non-empty packets except control SETUP packet */\r
- if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) {\r
- if (td->hwCBP == 0)\r
- urb->actual_length += tdBE - td->data_dma + 1;\r
- else\r
- urb->actual_length +=\r
- le32_to_cpup (&td->hwCBP)\r
- - td->data_dma;\r
- }\r
-\r
- if (cc != TD_CC_NOERROR && cc < 0x0E)\r
- ohci_vdbg (ohci,\r
- "urb %p td %p (%d) cc %d, len=%d/%d\n",\r
- urb, td, 1 + td->index, cc,\r
- urb->actual_length,\r
- urb->transfer_buffer_length);\r
- }\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-static inline struct td *\r
-ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)\r
-{\r
- struct urb *urb = td->urb;\r
- struct ed *ed = td->ed;\r
- struct list_head *tmp = td->td_list.next;\r
- u32 toggle = ed->hwHeadP & ED_C;\r
-\r
- /* clear ed halt; this is the td that caused it, but keep it inactive\r
- * until its urb->complete() has a chance to clean up.\r
- */\r
- ed->hwINFO |= ED_SKIP;\r
- wmb ();\r
- ed->hwHeadP &= ~ED_H; \r
-\r
- /* put any later tds from this urb onto the donelist, after 'td',\r
- * order won't matter here: no errors, and nothing was transferred.\r
- * also patch the ed so it looks as if those tds completed normally.\r
- */\r
- while (tmp != &ed->td_list) {\r
- struct td *next;\r
- u32 info;\r
-\r
- next = list_entry (tmp, struct td, td_list);\r
- tmp = next->td_list.next;\r
-\r
- if (next->urb != urb)\r
- break;\r
-\r
- /* NOTE: if multi-td control DATA segments get supported,\r
- * this urb had one of them, this td wasn't the last td\r
- * in that segment (TD_R clear), this ed halted because\r
- * of a short read, _and_ URB_SHORT_NOT_OK is clear ...\r
- * then we need to leave the control STATUS packet queued\r
- * and clear ED_SKIP.\r
- */\r
- info = next->hwINFO;\r
- info |= cpu_to_le32 (TD_DONE);\r
- info &= ~cpu_to_le32 (TD_CC);\r
- next->hwINFO = info;\r
-\r
- next->next_dl_td = rev; \r
- rev = next;\r
-\r
- if (ed->hwTailP == cpu_to_le32 (next->td_dma))\r
- ed->hwTailP = next->hwNextTD;\r
- ed->hwHeadP = next->hwNextTD | toggle;\r
- }\r
-\r
- /* help for troubleshooting: report anything that\r
- * looks odd ... that doesn't include protocol stalls\r
- * (or maybe some other things)\r
- */\r
- if (cc != TD_CC_STALL || !usb_pipecontrol (urb->pipe))\r
- ohci_dbg (ohci,\r
- "urb %p path %s ep%d%s %08x cc %d --> status %d\n",\r
- urb, urb->dev->devpath,\r
- usb_pipeendpoint (urb->pipe),\r
- usb_pipein (urb->pipe) ? "in" : "out",\r
- le32_to_cpu (td->hwINFO),\r
- cc, cc_to_error [cc]);\r
-\r
- return rev;\r
-}\r
-\r
-/* replies to the request have to be on a FIFO basis so\r
- * we unreverse the hc-reversed done-list\r
- */\r
-static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)\r
-{\r
- u32 td_dma;\r
- struct td *td_rev = NULL;\r
- struct td *td = NULL;\r
- unsigned long flags;\r
-\r
- spin_lock_irqsave (&ohci->lock, flags);\r
- td_dma = le32_to_cpup (&ohci->hcca->done_head);\r
- ohci->hcca->done_head = 0;\r
-\r
- /* get TD from hc's singly linked list, and\r
- * prepend to ours. ed->td_list changes later.\r
- */\r
- while (td_dma) { \r
- int cc;\r
-\r
- td = dma_to_td (ohci, td_dma);\r
- if (!td) {\r
- ohci_err (ohci, "bad entry %8x\n", td_dma);\r
- break;\r
- }\r
-\r
- td->hwINFO |= cpu_to_le32 (TD_DONE);\r
- cc = TD_CC_GET (le32_to_cpup (&td->hwINFO));\r
-\r
- /* Non-iso endpoints can halt on error; un-halt,\r
- * and dequeue any other TDs from this urb.\r
- * No other TD could have caused the halt.\r
- */\r
- if (cc != TD_CC_NOERROR && (td->ed->hwHeadP & ED_H))\r
- td_rev = ed_halted (ohci, td, cc, td_rev);\r
-\r
- td->next_dl_td = td_rev; \r
- td_rev = td;\r
- td_dma = le32_to_cpup (&td->hwNextTD);\r
- } \r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
- return td_rev;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* wrap-aware logic stolen from <linux/jiffies.h> */\r
-#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)\r
-\r
-/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */\r
-static void\r
-finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs)\r
-{\r
- struct ed *ed, **last;\r
-\r
-rescan_all:\r
- for (last = &ohci->ed_rm_list, ed = *last; ed != NULL; ed = *last) {\r
- struct list_head *entry, *tmp;\r
- int completed, modified;\r
- u32 *prev;\r
-\r
- /* only take off EDs that the HC isn't using, accounting for\r
- * frame counter wraps.\r
- */\r
- if (tick_before (tick, ed->tick) && !ohci->disabled) {\r
- last = &ed->ed_next;\r
- continue;\r
- }\r
-\r
- /* reentrancy: if we drop the schedule lock, someone might\r
- * have modified this list. normally it's just prepending\r
- * entries (which we'd ignore), but paranoia won't hurt.\r
- */\r
- *last = ed->ed_next;\r
- ed->ed_next = 0;\r
- modified = 0;\r
-\r
- /* unlink urbs as requested, but rescan the list after\r
- * we call a completion since it might have unlinked\r
- * another (earlier) urb\r
- */\r
-rescan_this:\r
- completed = 0;\r
- prev = &ed->hwHeadP;\r
- list_for_each_safe (entry, tmp, &ed->td_list) {\r
- struct td *td;\r
- struct urb *urb;\r
- urb_priv_t *urb_priv;\r
- u32 savebits;\r
-\r
- td = list_entry (entry, struct td, td_list);\r
- urb = td->urb;\r
- urb_priv = td->urb->hcpriv;\r
-\r
- if (urb_priv->state != URB_DEL) {\r
- prev = &td->hwNextTD;\r
- continue;\r
- }\r
-\r
- /* patch pointers hc uses ... tail, if we're removing\r
- * an otherwise active td, and whatever td pointer\r
- * points to this td\r
- */\r
- if (ed->hwTailP == cpu_to_le32 (td->td_dma))\r
- ed->hwTailP = td->hwNextTD;\r
- savebits = *prev & ~cpu_to_le32 (TD_MASK);\r
- *prev = td->hwNextTD | savebits;\r
-\r
- /* HC may have partly processed this TD */\r
- td_done (ohci, urb, td);\r
- urb_priv->td_cnt++;\r
-\r
- /* if URB is done, clean up */\r
- if (urb_priv->td_cnt == urb_priv->length) {\r
- modified = completed = 1;\r
- spin_unlock (&ohci->lock);\r
- finish_urb (ohci, urb, regs);\r
- spin_lock (&ohci->lock);\r
- }\r
- }\r
- if (completed && !list_empty (&ed->td_list))\r
- goto rescan_this;\r
-\r
- /* ED's now officially unlinked, hc doesn't see */\r
- ed->state = ED_IDLE;\r
- ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);\r
- ed->hwHeadP &= ~ED_H;\r
- ed->hwNextED = 0;\r
-\r
- /* but if there's work queued, reschedule */\r
- if (!list_empty (&ed->td_list)) {\r
- if (!ohci->disabled && !ohci->sleeping)\r
- ed_schedule (ohci, ed);\r
- }\r
-\r
- if (modified)\r
- goto rescan_all;\r
- }\r
-\r
- /* maybe reenable control and bulk lists */ \r
- if (!ohci->disabled && !ohci->ed_rm_list) {\r
- u32 command = 0, control = 0;\r
-\r
- if (ohci->ed_controltail) {\r
- command |= OHCI_CLF;\r
- if (!(ohci->hc_control & OHCI_CTRL_CLE)) {\r
- control |= OHCI_CTRL_CLE;\r
- writel (0, &ohci->regs->ed_controlcurrent);\r
- }\r
- }\r
- if (ohci->ed_bulktail) {\r
- command |= OHCI_BLF;\r
- if (!(ohci->hc_control & OHCI_CTRL_BLE)) {\r
- control |= OHCI_CTRL_BLE;\r
- writel (0, &ohci->regs->ed_bulkcurrent);\r
- }\r
- }\r
- \r
- /* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */\r
- if (control) {\r
- ohci->hc_control |= control;\r
- writel (ohci->hc_control, &ohci->regs->control); \r
- }\r
- if (command)\r
- writel (command, &ohci->regs->cmdstatus); \r
- }\r
-}\r
-\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * Process normal completions (error or success) and clean the schedules.\r
- *\r
- * This is the main path for handing urbs back to drivers. The only other\r
- * path is finish_unlinks(), which unlinks URBs using ed_rm_list, instead of\r
- * scanning the (re-reversed) donelist as this does.\r
- */\r
-static void\r
-dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs)\r
-{\r
- unsigned long flags;\r
-\r
- spin_lock_irqsave (&ohci->lock, flags);\r
- while (td) {\r
- struct td *td_next = td->next_dl_td;\r
- struct urb *urb = td->urb;\r
- urb_priv_t *urb_priv = urb->hcpriv;\r
- struct ed *ed = td->ed;\r
-\r
- /* update URB's length and status from TD */\r
- td_done (ohci, urb, td);\r
- urb_priv->td_cnt++;\r
-\r
- /* If all this urb's TDs are done, call complete() */\r
- if (urb_priv->td_cnt == urb_priv->length) {\r
- spin_unlock (&ohci->lock);\r
- finish_urb (ohci, urb, regs);\r
- spin_lock (&ohci->lock);\r
- }\r
-\r
- /* clean schedule: unlink EDs that are no longer busy */\r
- if (list_empty (&ed->td_list))\r
- ed_deschedule (ohci, ed);\r
- /* ... reenabling halted EDs only after fault cleanup */\r
- else if (!(ed->hwINFO & ED_DEQUEUE)) {\r
- td = list_entry (ed->td_list.next, struct td, td_list);\r
- if (!(td->hwINFO & TD_DONE))\r
- ed->hwINFO &= ~ED_SKIP;\r
- }\r
-\r
- td = td_next;\r
- } \r
- spin_unlock_irqrestore (&ohci->lock, flags);\r
-}\r
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
+{
+ int last = urb_priv->length - 1;
+
+ if (last >= 0) {
+ int i;
+ struct td *td;
+
+ for (i = 0; i <= last; i++) {
+ td = urb_priv->td [i];
+ if (td)
+ td_free (hc, td);
+ }
+ }
+
+ kfree (urb_priv);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * URB goes back to driver, and isn't reissued.
+ * It's completely gone from HC data structures.
+ * PRECONDITION: no locks held, irqs blocked (Giveback can call into HCD.)
+ */
+static void
+finish_urb (struct ohci_hcd *ohci, struct urb *urb, struct pt_regs *regs)
+{
+ // ASSERT (urb->hcpriv != 0);
+
+ urb_free_priv (ohci, urb->hcpriv);
+ urb->hcpriv = NULL;
+
+ spin_lock (&urb->lock);
+ if (likely (urb->status == -EINPROGRESS))
+ urb->status = 0;
+ spin_unlock (&urb->lock);
+
+ // what lock protects these?
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_ISOCHRONOUS:
+ hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs--;
+ break;
+ case PIPE_INTERRUPT:
+ hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs--;
+ break;
+ }
+
+#ifdef OHCI_VERBOSE_DEBUG
+ urb_print (urb, "RET", usb_pipeout (urb->pipe));
+#endif
+ usb_hcd_giveback_urb (&ohci->hcd, urb, regs);
+}
+
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* search for the right schedule branch to use for a periodic ed.
+ * does some load balancing; returns the branch, or negative errno.
+ */
+static int balance (struct ohci_hcd *ohci, int interval, int load)
+{
+ int i, branch = -ENOSPC;
+
+ /* iso periods can be huge; iso tds specify frame numbers */
+ if (interval > NUM_INTS)
+ interval = NUM_INTS;
+
+ /* search for the least loaded schedule branch of that period
+ * that has enough bandwidth left unreserved.
+ */
+ for (i = 0; i < interval ; i++) {
+ if (branch < 0 || ohci->load [branch] > ohci->load [i]) {
+#if 1 /* CONFIG_USB_BANDWIDTH */
+ int j;
+
+ /* usb 1.1 says 90% of one frame */
+ for (j = i; j < NUM_INTS; j += interval) {
+ if ((ohci->load [j] + load) > 900)
+ break;
+ }
+ if (j < NUM_INTS)
+ continue;
+#endif
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* both iso and interrupt requests have periods; this routine puts them
+ * into the schedule tree in the apppropriate place. most iso devices use
+ * 1msec periods, but that's not required.
+ */
+static void periodic_link (struct ohci_hcd *ohci, struct ed *ed)
+{
+ unsigned i;
+
+ ohci_vdbg (ohci, "link %sed %p branch %d [%dus.], interval %d\n",
+ (ed->hwINFO & ED_ISO) ? "iso " : "",
+ ed, ed->branch, ed->load, ed->interval);
+
+ for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
+ struct ed **prev = &ohci->periodic [i];
+ u32 *prev_p = &ohci->hcca->int_table [i];
+ struct ed *here = *prev;
+
+ /* sorting each branch by period (slow before fast)
+ * lets us share the faster parts of the tree.
+ * (plus maybe: put interrupt eds before iso)
+ */
+ while (here && ed != here) {
+ if (ed->interval > here->interval)
+ break;
+ prev = &here->ed_next;
+ prev_p = &here->hwNextED;
+ here = *prev;
+ }
+ if (ed != here) {
+ ed->ed_next = here;
+ if (here)
+ ed->hwNextED = *prev_p;
+ wmb ();
+ *prev = ed;
+ *prev_p = cpu_to_le32p (&ed->dma);
+ }
+ ohci->load [i] += ed->load;
+ }
+ hcd_to_bus (&ohci->hcd)->bandwidth_allocated += ed->load / ed->interval;
+}
+
+/* link an ed into one of the HC chains */
+
+static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
+{
+ int branch;
+
+ ed->state = ED_OPER;
+ ed->ed_prev = 0;
+ ed->ed_next = 0;
+ ed->hwNextED = 0;
+ wmb ();
+
+ /* we care about rm_list when setting CLE/BLE in case the HC was at
+ * work on some TD when CLE/BLE was turned off, and isn't quiesced
+ * yet. finish_unlinks() restarts as needed, some upcoming INTR_SF.
+ *
+ * control and bulk EDs are doubly linked (ed_next, ed_prev), but
+ * periodic ones are singly linked (ed_next). that's because the
+ * periodic schedule encodes a tree like figure 3-5 in the ohci
+ * spec: each qh can have several "previous" nodes, and the tree
+ * doesn't have unused/idle descriptors.
+ */
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ if (ohci->ed_controltail == NULL) {
+ writel (ed->dma, &ohci->regs->ed_controlhead);
+ } else {
+ ohci->ed_controltail->ed_next = ed;
+ ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma);
+ }
+ ed->ed_prev = ohci->ed_controltail;
+ if (!ohci->ed_controltail && !ohci->ed_rm_list) {
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ writel (0, &ohci->regs->ed_controlcurrent);
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ ohci->ed_controltail = ed;
+ break;
+
+ case PIPE_BULK:
+ if (ohci->ed_bulktail == NULL) {
+ writel (ed->dma, &ohci->regs->ed_bulkhead);
+ } else {
+ ohci->ed_bulktail->ed_next = ed;
+ ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma);
+ }
+ ed->ed_prev = ohci->ed_bulktail;
+ if (!ohci->ed_bulktail && !ohci->ed_rm_list) {
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ writel (0, &ohci->regs->ed_bulkcurrent);
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ ohci->ed_bulktail = ed;
+ break;
+
+ // case PIPE_INTERRUPT:
+ // case PIPE_ISOCHRONOUS:
+ default:
+ branch = balance (ohci, ed->interval, ed->load);
+ if (branch < 0) {
+ ohci_dbg (ohci,
+ "ERR %d, interval %d msecs, load %d\n",
+ branch, ed->interval, ed->load);
+ // FIXME if there are TDs queued, fail them!
+ return branch;
+ }
+ ed->branch = branch;
+ periodic_link (ohci, ed);
+ }
+
+ /* the HC may not see the schedule updates yet, but if it does
+ * then they'll be properly ordered.
+ */
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* scan the periodic table to find and unlink this ED */
+static void periodic_unlink (struct ohci_hcd *ohci, struct ed *ed)
+{
+ int i;
+
+ for (i = ed->branch; i < NUM_INTS; i += ed->interval) {
+ struct ed *temp;
+ struct ed **prev = &ohci->periodic [i];
+ u32 *prev_p = &ohci->hcca->int_table [i];
+
+ while (*prev && (temp = *prev) != ed) {
+ prev_p = &temp->hwNextED;
+ prev = &temp->ed_next;
+ }
+ if (*prev) {
+ *prev_p = ed->hwNextED;
+ *prev = ed->ed_next;
+ }
+ ohci->load [i] -= ed->load;
+ }
+ hcd_to_bus (&ohci->hcd)->bandwidth_allocated -= ed->load / ed->interval;
+
+ ohci_vdbg (ohci, "unlink %sed %p branch %d [%dus.], interval %d\n",
+ (ed->hwINFO & ED_ISO) ? "iso " : "",
+ ed, ed->branch, ed->load, ed->interval);
+}
+
+/* unlink an ed from one of the HC chains.
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed
+ */
+static void ed_deschedule (struct ohci_hcd *ohci, struct ed *ed)
+{
+ ed->hwINFO |= ED_SKIP;
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ writel (0, &ohci->regs->ed_controlcurrent);
+ // post those pci writes
+ (void) readl (&ohci->regs->control);
+ }
+ writel (le32_to_cpup (&ed->hwNextED),
+ &ohci->regs->ed_controlhead);
+ } else {
+ ed->ed_prev->ed_next = ed->ed_next;
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_controltail == ed) {
+ ohci->ed_controltail = ed->ed_prev;
+ if (ohci->ed_controltail)
+ ohci->ed_controltail->ed_next = 0;
+ } else if (ed->ed_next) {
+ ed->ed_next->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case PIPE_BULK:
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ writel (0, &ohci->regs->ed_bulkcurrent);
+ // post those pci writes
+ (void) readl (&ohci->regs->control);
+ }
+ writel (le32_to_cpup (&ed->hwNextED),
+ &ohci->regs->ed_bulkhead);
+ } else {
+ ed->ed_prev->ed_next = ed->ed_next;
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_bulktail == ed) {
+ ohci->ed_bulktail = ed->ed_prev;
+ if (ohci->ed_bulktail)
+ ohci->ed_bulktail->ed_next = 0;
+ } else if (ed->ed_next) {
+ ed->ed_next->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ // case PIPE_INTERRUPT:
+ // case PIPE_ISOCHRONOUS:
+ default:
+ periodic_unlink (ohci, ed);
+ break;
+ }
+
+ /* NOTE: Except for a couple of exceptionally clean unlink cases
+ * (like unlinking the only c/b ED, with no TDs) HCs may still be
+ * caching this operational ED (or its address). Safe unlinking
+ * involves not marking it ED_IDLE till INTR_SF; we always do that
+ * if td_list isn't empty. Otherwise the race is small; but ...
+ */
+ if (ed->state == ED_OPER) {
+ ed->state = ED_IDLE;
+ ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);
+ ed->hwHeadP &= ~ED_H;
+ wmb ();
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* get and maybe (re)init an endpoint. init _should_ be done only as part
+ * of usb_set_configuration() or usb_set_interface() ... but the USB stack
+ * isn't very stateful, so we re-init whenever the HC isn't looking.
+ */
+static struct ed *ed_get (
+ struct ohci_hcd *ohci,
+ struct usb_device *udev,
+ unsigned int pipe,
+ int interval
+) {
+ int is_out = !usb_pipein (pipe);
+ int type = usb_pipetype (pipe);
+ struct hcd_dev *dev = (struct hcd_dev *) udev->hcpriv;
+ struct ed *ed;
+ unsigned ep;
+ unsigned long flags;
+
+ ep = usb_pipeendpoint (pipe) << 1;
+ if (type != PIPE_CONTROL && is_out)
+ ep |= 1;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+
+ if (!(ed = dev->ep [ep])) {
+ struct td *td;
+
+ ed = ed_alloc (ohci, SLAB_ATOMIC);
+ if (!ed) {
+ /* out of memory */
+ goto done;
+ }
+ dev->ep [ep] = ed;
+
+ /* dummy td; end of td list for ed */
+ td = td_alloc (ohci, SLAB_ATOMIC);
+ if (!td) {
+ /* out of memory */
+ ed_free (ohci, ed);
+ ed = 0;
+ goto done;
+ }
+ ed->dummy = td;
+ ed->hwTailP = cpu_to_le32 (td->td_dma);
+ ed->hwHeadP = ed->hwTailP; /* ED_C, ED_H zeroed */
+ ed->state = ED_IDLE;
+ ed->type = type;
+ }
+
+ /* NOTE: only ep0 currently needs this "re"init logic, during
+ * enumeration (after set_address, or if ep0 maxpacket >8).
+ */
+ if (ed->state == ED_IDLE) {
+ u32 info;
+
+ info = usb_pipedevice (pipe);
+ info |= (ep >> 1) << 7;
+ info |= usb_maxpacket (udev, pipe, is_out) << 16;
+ info = cpu_to_le32 (info);
+ if (udev->speed == USB_SPEED_LOW)
+ info |= ED_LOWSPEED;
+ /* only control transfers store pids in tds */
+ if (type != PIPE_CONTROL) {
+ info |= is_out ? ED_OUT : ED_IN;
+ if (type != PIPE_BULK) {
+ /* periodic transfers... */
+ if (type == PIPE_ISOCHRONOUS)
+ info |= ED_ISO;
+ else if (interval > 32) /* iso can be bigger */
+ interval = 32;
+ ed->interval = interval;
+ ed->load = usb_calc_bus_time (
+ udev->speed, !is_out,
+ type == PIPE_ISOCHRONOUS,
+ usb_maxpacket (udev, pipe, is_out))
+ / 1000;
+ }
+ }
+ ed->hwINFO = info;
+ }
+
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return ed;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* request unlinking of an endpoint from an operational HC.
+ * put the ep on the rm_list
+ * real work is done at the next start frame (SF) hardware interrupt
+ */
+static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
+{
+ ed->hwINFO |= ED_DEQUEUE;
+ ed->state = ED_UNLINK;
+ ed_deschedule (ohci, ed);
+
+ /* SF interrupt might get delayed; record the frame counter value that
+ * indicates when the HC isn't looking at it, so concurrent unlinks
+ * behave. frame_no wraps every 2^16 msec, and changes right before
+ * SF is triggered.
+ */
+ ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1;
+
+ /* rm_list is just singly linked, for simplicity */
+ ed->ed_next = ohci->ed_rm_list;
+ ed->ed_prev = 0;
+ ohci->ed_rm_list = ed;
+
+ /* enable SOF interrupt */
+ if (!ohci->sleeping) {
+ writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
+ writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+ // flush those pci writes
+ (void) readl (&ohci->regs->control);
+ }
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void
+td_fill (struct ohci_hcd *ohci, u32 info,
+ dma_addr_t data, int len,
+ struct urb *urb, int index)
+{
+ struct td *td, *td_pt;
+ struct urb_priv *urb_priv = urb->hcpriv;
+ int is_iso = info & TD_ISO;
+ int hash;
+
+ // ASSERT (index < urb_priv->length);
+
+ /* aim for only one interrupt per urb. mostly applies to control
+ * and iso; other urbs rarely need more than one TD per urb.
+ * this way, only final tds (or ones with an error) cause IRQs.
+ * at least immediately; use DI=6 in case any control request is
+ * tempted to die part way through.
+ *
+ * NOTE: could delay interrupts even for the last TD, and get fewer
+ * interrupts ... increasing per-urb latency by sharing interrupts.
+ * Drivers that queue bulk urbs may request that behavior.
+ */
+ if (index != (urb_priv->length - 1)
+ || (urb->transfer_flags & URB_NO_INTERRUPT))
+ info |= TD_DI_SET (6);
+
+ /* use this td as the next dummy */
+ td_pt = urb_priv->td [index];
+
+ /* fill the old dummy TD */
+ td = urb_priv->td [index] = urb_priv->ed->dummy;
+ urb_priv->ed->dummy = td_pt;
+
+ td->ed = urb_priv->ed;
+ td->next_dl_td = NULL;
+ td->index = index;
+ td->urb = urb;
+ td->data_dma = data;
+ if (!len)
+ data = 0;
+
+ td->hwINFO = cpu_to_le32 (info);
+ if (is_iso) {
+ td->hwCBP = cpu_to_le32 (data & 0xFFFFF000);
+ td->hwPSW [0] = cpu_to_le16 ((data & 0x0FFF) | 0xE000);
+ td->ed->last_iso = info & 0xffff;
+ } else {
+ td->hwCBP = cpu_to_le32 (data);
+ }
+ if (data)
+ td->hwBE = cpu_to_le32 (data + len - 1);
+ else
+ td->hwBE = 0;
+ td->hwNextTD = cpu_to_le32 (td_pt->td_dma);
+
+ /* append to queue */
+ list_add_tail (&td->td_list, &td->ed->td_list);
+
+ /* hash it for later reverse mapping */
+ hash = TD_HASH_FUNC (td->td_dma);
+ td->td_hash = ohci->td_hash [hash];
+ ohci->td_hash [hash] = td;
+
+ /* HC might read the TD (or cachelines) right away ... */
+ wmb ();
+ td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Prepare all TDs of a transfer, and queue them onto the ED.
+ * Caller guarantees HC is active.
+ * Usually the ED is already on the schedule, so TDs might be
+ * processed as soon as they're queued.
+ */
+static void td_submit_urb (
+ struct ohci_hcd *ohci,
+ struct urb *urb
+) {
+ struct urb_priv *urb_priv = urb->hcpriv;
+ dma_addr_t data;
+ int data_len = urb->transfer_buffer_length;
+ int cnt = 0;
+ u32 info = 0;
+ int is_out = usb_pipeout (urb->pipe);
+
+ /* OHCI handles the bulk/interrupt data toggles itself. We just
+ * use the device toggle bits for resetting, and rely on the fact
+ * that resetting toggle is meaningless if the endpoint is active.
+ */
+ if (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), is_out)) {
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe),
+ is_out, 1);
+ urb_priv->ed->hwHeadP &= ~ED_C;
+ }
+
+ urb_priv->td_cnt = 0;
+
+ if (data_len)
+ data = urb->transfer_dma;
+ else
+ data = 0;
+
+ /* NOTE: TD_CC is set so we can tell which TDs the HC processed by
+ * using TD_CC_GET, as well as by seeing them on the done list.
+ * (CC = NotAccessed ... 0x0F, or 0x0E in PSWs for ISO.)
+ */
+ switch (urb_priv->ed->type) {
+
+ /* Bulk and interrupt are identical except for where in the schedule
+ * their EDs live.
+ */
+ case PIPE_INTERRUPT:
+ /* ... and periodic urbs have extra accounting */
+ hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs++;
+ /* FALLTHROUGH */
+ case PIPE_BULK:
+ info = is_out
+ ? TD_T_TOGGLE | TD_CC | TD_DP_OUT
+ : TD_T_TOGGLE | TD_CC | TD_DP_IN;
+ /* TDs _could_ transfer up to 8K each */
+ while (data_len > 4096) {
+ td_fill (ohci, info, data, 4096, urb, cnt);
+ data += 4096;
+ data_len -= 4096;
+ cnt++;
+ }
+ /* maybe avoid ED halt on final TD short read */
+ if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+ info |= TD_R;
+ td_fill (ohci, info, data, data_len, urb, cnt);
+ cnt++;
+ if ((urb->transfer_flags & URB_ZERO_PACKET)
+ && cnt < urb_priv->length) {
+ td_fill (ohci, info, 0, 0, urb, cnt);
+ cnt++;
+ }
+ /* maybe kickstart bulk list */
+ if (urb_priv->ed->type == PIPE_BULK) {
+ wmb ();
+ writel (OHCI_BLF, &ohci->regs->cmdstatus);
+ }
+ break;
+
+ /* control manages DATA0/DATA1 toggle per-request; SETUP resets it,
+ * any DATA phase works normally, and the STATUS ack is special.
+ */
+ case PIPE_CONTROL:
+ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+ td_fill (ohci, info, urb->setup_dma, 8, urb, cnt++);
+ if (data_len > 0) {
+ info = TD_CC | TD_R | TD_T_DATA1;
+ info |= is_out ? TD_DP_OUT : TD_DP_IN;
+ /* NOTE: mishandles transfers >8K, some >4K */
+ td_fill (ohci, info, data, data_len, urb, cnt++);
+ }
+ info = is_out
+ ? TD_CC | TD_DP_IN | TD_T_DATA1
+ : TD_CC | TD_DP_OUT | TD_T_DATA1;
+ td_fill (ohci, info, data, 0, urb, cnt++);
+ /* maybe kickstart control list */
+ wmb ();
+ writel (OHCI_CLF, &ohci->regs->cmdstatus);
+ break;
+
+ /* ISO has no retransmit, so no toggle; and it uses special TDs.
+ * Each TD could handle multiple consecutive frames (interval 1);
+ * we could often reduce the number of TDs here.
+ */
+ case PIPE_ISOCHRONOUS:
+ for (cnt = 0; cnt < urb->number_of_packets; cnt++) {
+ int frame = urb->start_frame;
+
+ // FIXME scheduling should handle frame counter
+ // roll-around ... exotic case (and OHCI has
+ // a 2^16 iso range, vs other HCs max of 2^10)
+ frame += cnt * urb->interval;
+ frame &= 0xffff;
+ td_fill (ohci, TD_CC | TD_ISO | frame,
+ data + urb->iso_frame_desc [cnt].offset,
+ urb->iso_frame_desc [cnt].length, urb, cnt);
+ }
+ hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs++;
+ break;
+ }
+ // ASSERT (urb_priv->length == cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+/* calculate transfer length/status and update the urb
+ * PRECONDITION: irqsafe (only for urb->status locking)
+ */
+static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td)
+{
+ u32 tdINFO = le32_to_cpup (&td->hwINFO);
+ int cc = 0;
+
+ list_del (&td->td_list);
+
+ /* ISO ... drivers see per-TD length/status */
+ if (tdINFO & TD_ISO) {
+ u16 tdPSW = le16_to_cpu (td->hwPSW [0]);
+ int dlen = 0;
+
+ /* NOTE: assumes FC in tdINFO == 0 (and MAXPSW == 1) */
+
+ cc = (tdPSW >> 12) & 0xF;
+ if (tdINFO & TD_CC) /* hc didn't touch? */
+ return;
+
+ if (usb_pipeout (urb->pipe))
+ dlen = urb->iso_frame_desc [td->index].length;
+ else {
+ /* short reads are always OK for ISO */
+ if (cc == TD_DATAUNDERRUN)
+ cc = TD_CC_NOERROR;
+ dlen = tdPSW & 0x3ff;
+ }
+ urb->actual_length += dlen;
+ urb->iso_frame_desc [td->index].actual_length = dlen;
+ urb->iso_frame_desc [td->index].status = cc_to_error [cc];
+
+ if (cc != TD_CC_NOERROR)
+ ohci_vdbg (ohci,
+ "urb %p iso td %p (%d) len %d cc %d\n",
+ urb, td, 1 + td->index, dlen, cc);
+
+ /* BULK, INT, CONTROL ... drivers see aggregate length/status,
+ * except that "setup" bytes aren't counted and "short" transfers
+ * might not be reported as errors.
+ */
+ } else {
+ int type = usb_pipetype (urb->pipe);
+ u32 tdBE = le32_to_cpup (&td->hwBE);
+
+ cc = TD_CC_GET (tdINFO);
+
+ /* control endpoints only have soft stalls */
+ if (type != PIPE_CONTROL && cc == TD_CC_STALL)
+ usb_endpoint_halt (urb->dev,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipeout (urb->pipe));
+
+ /* update packet status if needed (short is normally ok) */
+ if (cc == TD_DATAUNDERRUN
+ && !(urb->transfer_flags & URB_SHORT_NOT_OK))
+ cc = TD_CC_NOERROR;
+ if (cc != TD_CC_NOERROR && cc < 0x0E) {
+ spin_lock (&urb->lock);
+ if (urb->status == -EINPROGRESS)
+ urb->status = cc_to_error [cc];
+ spin_unlock (&urb->lock);
+ }
+
+ /* count all non-empty packets except control SETUP packet */
+ if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) {
+ if (td->hwCBP == 0)
+ urb->actual_length += tdBE - td->data_dma + 1;
+ else
+ urb->actual_length +=
+ le32_to_cpup (&td->hwCBP)
+ - td->data_dma;
+ }
+
+ if (cc != TD_CC_NOERROR && cc < 0x0E)
+ ohci_vdbg (ohci,
+ "urb %p td %p (%d) cc %d, len=%d/%d\n",
+ urb, td, 1 + td->index, cc,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline struct td *
+ed_halted (struct ohci_hcd *ohci, struct td *td, int cc, struct td *rev)
+{
+ struct urb *urb = td->urb;
+ struct ed *ed = td->ed;
+ struct list_head *tmp = td->td_list.next;
+ u32 toggle = ed->hwHeadP & ED_C;
+
+ /* clear ed halt; this is the td that caused it, but keep it inactive
+ * until its urb->complete() has a chance to clean up.
+ */
+ ed->hwINFO |= ED_SKIP;
+ wmb ();
+ ed->hwHeadP &= ~ED_H;
+
+ /* put any later tds from this urb onto the donelist, after 'td',
+ * order won't matter here: no errors, and nothing was transferred.
+ * also patch the ed so it looks as if those tds completed normally.
+ */
+ while (tmp != &ed->td_list) {
+ struct td *next;
+ u32 info;
+
+ next = list_entry (tmp, struct td, td_list);
+ tmp = next->td_list.next;
+
+ if (next->urb != urb)
+ break;
+
+ /* NOTE: if multi-td control DATA segments get supported,
+ * this urb had one of them, this td wasn't the last td
+ * in that segment (TD_R clear), this ed halted because
+ * of a short read, _and_ URB_SHORT_NOT_OK is clear ...
+ * then we need to leave the control STATUS packet queued
+ * and clear ED_SKIP.
+ */
+ info = next->hwINFO;
+ info |= cpu_to_le32 (TD_DONE);
+ info &= ~cpu_to_le32 (TD_CC);
+ next->hwINFO = info;
+
+ next->next_dl_td = rev;
+ rev = next;
+
+ if (ed->hwTailP == cpu_to_le32 (next->td_dma))
+ ed->hwTailP = next->hwNextTD;
+ ed->hwHeadP = next->hwNextTD | toggle;
+ }
+
+ /* help for troubleshooting: report anything that
+ * looks odd ... that doesn't include protocol stalls
+ * (or maybe some other things)
+ */
+ if (cc != TD_CC_STALL || !usb_pipecontrol (urb->pipe))
+ ohci_dbg (ohci,
+ "urb %p path %s ep%d%s %08x cc %d --> status %d\n",
+ urb, urb->dev->devpath,
+ usb_pipeendpoint (urb->pipe),
+ usb_pipein (urb->pipe) ? "in" : "out",
+ le32_to_cpu (td->hwINFO),
+ cc, cc_to_error [cc]);
+
+ return rev;
+}
+
+/* replies to the request have to be on a FIFO basis so
+ * we unreverse the hc-reversed done-list
+ */
+static struct td *dl_reverse_done_list (struct ohci_hcd *ohci)
+{
+ u32 td_dma;
+ struct td *td_rev = NULL;
+ struct td *td = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+ td_dma = le32_to_cpup (&ohci->hcca->done_head);
+ ohci->hcca->done_head = 0;
+
+ /* get TD from hc's singly linked list, and
+ * prepend to ours. ed->td_list changes later.
+ */
+ while (td_dma) {
+ int cc;
+
+ td = dma_to_td (ohci, td_dma);
+ if (!td) {
+ ohci_err (ohci, "bad entry %8x\n", td_dma);
+ break;
+ }
+
+ td->hwINFO |= cpu_to_le32 (TD_DONE);
+ cc = TD_CC_GET (le32_to_cpup (&td->hwINFO));
+
+ /* Non-iso endpoints can halt on error; un-halt,
+ * and dequeue any other TDs from this urb.
+ * No other TD could have caused the halt.
+ */
+ if (cc != TD_CC_NOERROR && (td->ed->hwHeadP & ED_H))
+ td_rev = ed_halted (ohci, td, cc, td_rev);
+
+ td->next_dl_td = td_rev;
+ td_rev = td;
+ td_dma = le32_to_cpup (&td->hwNextTD);
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+ return td_rev;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* wrap-aware logic stolen from <linux/jiffies.h> */
+#define tick_before(t1,t2) ((((s16)(t1))-((s16)(t2))) < 0)
+
+/* there are some urbs/eds to unlink; called in_irq(), with HCD locked */
+static void
+finish_unlinks (struct ohci_hcd *ohci, u16 tick, struct pt_regs *regs)
+{
+ struct ed *ed, **last;
+
+rescan_all:
+ for (last = &ohci->ed_rm_list, ed = *last; ed != NULL; ed = *last) {
+ struct list_head *entry, *tmp;
+ int completed, modified;
+ u32 *prev;
+
+ /* only take off EDs that the HC isn't using, accounting for
+ * frame counter wraps.
+ */
+ if (tick_before (tick, ed->tick) && !ohci->disabled) {
+ last = &ed->ed_next;
+ continue;
+ }
+
+ /* reentrancy: if we drop the schedule lock, someone might
+ * have modified this list. normally it's just prepending
+ * entries (which we'd ignore), but paranoia won't hurt.
+ */
+ *last = ed->ed_next;
+ ed->ed_next = 0;
+ modified = 0;
+
+ /* unlink urbs as requested, but rescan the list after
+ * we call a completion since it might have unlinked
+ * another (earlier) urb
+ */
+rescan_this:
+ completed = 0;
+ prev = &ed->hwHeadP;
+ list_for_each_safe (entry, tmp, &ed->td_list) {
+ struct td *td;
+ struct urb *urb;
+ urb_priv_t *urb_priv;
+ u32 savebits;
+
+ td = list_entry (entry, struct td, td_list);
+ urb = td->urb;
+ urb_priv = td->urb->hcpriv;
+
+ if (urb_priv->state != URB_DEL) {
+ prev = &td->hwNextTD;
+ continue;
+ }
+
+ /* patch pointers hc uses ... tail, if we're removing
+ * an otherwise active td, and whatever td pointer
+ * points to this td
+ */
+ if (ed->hwTailP == cpu_to_le32 (td->td_dma))
+ ed->hwTailP = td->hwNextTD;
+ savebits = *prev & ~cpu_to_le32 (TD_MASK);
+ *prev = td->hwNextTD | savebits;
+
+ /* HC may have partly processed this TD */
+ td_done (ohci, urb, td);
+ urb_priv->td_cnt++;
+
+ /* if URB is done, clean up */
+ if (urb_priv->td_cnt == urb_priv->length) {
+ modified = completed = 1;
+ spin_unlock (&ohci->lock);
+ finish_urb (ohci, urb, regs);
+ spin_lock (&ohci->lock);
+ }
+ }
+ if (completed && !list_empty (&ed->td_list))
+ goto rescan_this;
+
+ /* ED's now officially unlinked, hc doesn't see */
+ ed->state = ED_IDLE;
+ ed->hwINFO &= ~(ED_SKIP | ED_DEQUEUE);
+ ed->hwHeadP &= ~ED_H;
+ ed->hwNextED = 0;
+
+ /* but if there's work queued, reschedule */
+ if (!list_empty (&ed->td_list)) {
+ if (!ohci->disabled && !ohci->sleeping)
+ ed_schedule (ohci, ed);
+ }
+
+ if (modified)
+ goto rescan_all;
+ }
+
+ /* maybe reenable control and bulk lists */
+ if (!ohci->disabled && !ohci->ed_rm_list) {
+ u32 command = 0, control = 0;
+
+ if (ohci->ed_controltail) {
+ command |= OHCI_CLF;
+ if (!(ohci->hc_control & OHCI_CTRL_CLE)) {
+ control |= OHCI_CTRL_CLE;
+ writel (0, &ohci->regs->ed_controlcurrent);
+ }
+ }
+ if (ohci->ed_bulktail) {
+ command |= OHCI_BLF;
+ if (!(ohci->hc_control & OHCI_CTRL_BLE)) {
+ control |= OHCI_CTRL_BLE;
+ writel (0, &ohci->regs->ed_bulkcurrent);
+ }
+ }
+
+ /* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */
+ if (control) {
+ ohci->hc_control |= control;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ if (command)
+ writel (command, &ohci->regs->cmdstatus);
+ }
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Process normal completions (error or success) and clean the schedules.
+ *
+ * This is the main path for handing urbs back to drivers. The only other
+ * path is finish_unlinks(), which unlinks URBs using ed_rm_list, instead of
+ * scanning the (re-reversed) donelist as this does.
+ */
+static void
+dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave (&ohci->lock, flags);
+ while (td) {
+ struct td *td_next = td->next_dl_td;
+ struct urb *urb = td->urb;
+ urb_priv_t *urb_priv = urb->hcpriv;
+ struct ed *ed = td->ed;
+
+ /* update URB's length and status from TD */
+ td_done (ohci, urb, td);
+ urb_priv->td_cnt++;
+
+ /* If all this urb's TDs are done, call complete() */
+ if (urb_priv->td_cnt == urb_priv->length) {
+ spin_unlock (&ohci->lock);
+ finish_urb (ohci, urb, regs);
+ spin_lock (&ohci->lock);
+ }
+
+ /* clean schedule: unlink EDs that are no longer busy */
+ if (list_empty (&ed->td_list))
+ ed_deschedule (ohci, ed);
+ /* ... reenabling halted EDs only after fault cleanup */
+ else if (!(ed->hwINFO & ED_DEQUEUE)) {
+ td = list_entry (ed->td_list.next, struct td, td_list);
+ if (!(td->hwINFO & TD_DONE))
+ ed->hwINFO &= ~ED_SKIP;
+ }
+
+ td = td_next;
+ }
+ spin_unlock_irqrestore (&ohci->lock, flags);
+}
-LIBRARY ohci.sys\r
-EXPORTS\r
+LIBRARY ohci.sys
+EXPORTS
-/*\r
- * OHCI HCD (Host Controller Driver) for USB.\r
- * \r
- * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>\r
- * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>\r
- * \r
- * This file is licenced under the GPL.\r
- */\r
- \r
-/*\r
- * OHCI Endpoint Descriptor (ED) ... holds TD queue\r
- * See OHCI spec, section 4.2\r
- *\r
- * This is a "Queue Head" for those transfers, which is why\r
- * both EHCI and UHCI call similar structures a "QH".\r
- */\r
-struct ed {\r
- /* first fields are hardware-specified, le32 */\r
- __u32 hwINFO; /* endpoint config bitmap */\r
- /* info bits defined by hcd */\r
-#define ED_DEQUEUE __constant_cpu_to_le32(1 << 27)\r
- /* info bits defined by the hardware */\r
-#define ED_ISO __constant_cpu_to_le32(1 << 15)\r
-#define ED_SKIP __constant_cpu_to_le32(1 << 14)\r
-#define ED_LOWSPEED __constant_cpu_to_le32(1 << 13)\r
-#define ED_OUT __constant_cpu_to_le32(0x01 << 11)\r
-#define ED_IN __constant_cpu_to_le32(0x02 << 11)\r
- __u32 hwTailP; /* tail of TD list */\r
- __u32 hwHeadP; /* head of TD list (hc r/w) */\r
-#define ED_C __constant_cpu_to_le32(0x02) /* toggle carry */\r
-#define ED_H __constant_cpu_to_le32(0x01) /* halted */\r
- __u32 hwNextED; /* next ED in list */\r
-\r
- /* rest are purely for the driver's use */\r
- dma_addr_t dma; /* addr of ED */\r
- struct td *dummy; /* next TD to activate */\r
-\r
- /* host's view of schedule */\r
- struct ed *ed_next; /* on schedule or rm_list */\r
- struct ed *ed_prev; /* for non-interrupt EDs */\r
- struct list_head td_list; /* "shadow list" of our TDs */\r
-\r
- /* create --> IDLE --> OPER --> ... --> IDLE --> destroy\r
- * usually: OPER --> UNLINK --> (IDLE | OPER) --> ...\r
- * some special cases : OPER --> IDLE ...\r
- */\r
- u8 state; /* ED_{IDLE,UNLINK,OPER} */\r
-#define ED_IDLE 0x00 /* NOT linked to HC */\r
-#define ED_UNLINK 0x01 /* being unlinked from hc */\r
-#define ED_OPER 0x02 /* IS linked to hc */\r
-\r
- u8 type; /* PIPE_{BULK,...} */\r
-\r
- /* periodic scheduling params (for intr and iso) */\r
- u8 branch;\r
- u16 interval;\r
- u16 load;\r
- u16 last_iso; /* iso only */\r
-\r
- /* HC may see EDs on rm_list until next frame (frame_no == tick) */\r
- u16 tick;\r
-} __attribute__ ((aligned(16)));\r
-\r
-#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */\r
-\r
- \r
-/*\r
- * OHCI Transfer Descriptor (TD) ... one per transfer segment\r
- * See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)\r
- * and 4.3.2 (iso)\r
- */\r
-struct td {\r
- /* first fields are hardware-specified, le32 */\r
- __u32 hwINFO; /* transfer info bitmask */\r
-\r
- /* hwINFO bits for both general and iso tds: */\r
-#define TD_CC 0xf0000000 /* condition code */\r
-#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)\r
-//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)\r
-#define TD_DI 0x00E00000 /* frames before interrupt */\r
-#define TD_DI_SET(X) (((X) & 0x07)<< 21)\r
- /* these two bits are available for definition/use by HCDs in both\r
- * general and iso tds ... others are available for only one type\r
- */\r
-#define TD_DONE 0x00020000 /* retired to donelist */\r
-#define TD_ISO 0x00010000 /* copy of ED_ISO */\r
-\r
- /* hwINFO bits for general tds: */\r
-#define TD_EC 0x0C000000 /* error count */\r
-#define TD_T 0x03000000 /* data toggle state */\r
-#define TD_T_DATA0 0x02000000 /* DATA0 */\r
-#define TD_T_DATA1 0x03000000 /* DATA1 */\r
-#define TD_T_TOGGLE 0x00000000 /* uses ED_C */\r
-#define TD_DP 0x00180000 /* direction/pid */\r
-#define TD_DP_SETUP 0x00000000 /* SETUP pid */\r
-#define TD_DP_IN 0x00100000 /* IN pid */\r
-#define TD_DP_OUT 0x00080000 /* OUT pid */\r
- /* 0x00180000 rsvd */\r
-#define TD_R 0x00040000 /* round: short packets OK? */\r
-\r
- /* (no hwINFO #defines yet for iso tds) */\r
-\r
- __u32 hwCBP; /* Current Buffer Pointer (or 0) */\r
- __u32 hwNextTD; /* Next TD Pointer */\r
- __u32 hwBE; /* Memory Buffer End Pointer */\r
-\r
- /* PSW is only for ISO */\r
-#define MAXPSW 1 /* hardware allows 8 */\r
- __u16 hwPSW [MAXPSW];\r
-\r
- /* rest are purely for the driver's use */\r
- __u8 index;\r
- struct ed *ed;\r
- struct td *td_hash; /* dma-->td hashtable */\r
- struct td *next_dl_td;\r
- struct urb *urb;\r
-\r
- dma_addr_t td_dma; /* addr of this TD */\r
- dma_addr_t data_dma; /* addr of data it points to */\r
-\r
- struct list_head td_list; /* "shadow list", TDs on same ED */\r
-} __attribute__ ((aligned(32))); /* c/b/i need 16; only iso needs 32 */\r
-\r
-#define TD_MASK ((u32)~0x1f) /* strip hw status in low addr bits */\r
-\r
-/*\r
- * Hardware transfer status codes -- CC from td->hwINFO or td->hwPSW\r
- */\r
-#define TD_CC_NOERROR 0x00\r
-#define TD_CC_CRC 0x01\r
-#define TD_CC_BITSTUFFING 0x02\r
-#define TD_CC_DATATOGGLEM 0x03\r
-#define TD_CC_STALL 0x04\r
-#define TD_DEVNOTRESP 0x05\r
-#define TD_PIDCHECKFAIL 0x06\r
-#define TD_UNEXPECTEDPID 0x07\r
-#define TD_DATAOVERRUN 0x08\r
-#define TD_DATAUNDERRUN 0x09\r
- /* 0x0A, 0x0B reserved for hardware */\r
-#define TD_BUFFEROVERRUN 0x0C\r
-#define TD_BUFFERUNDERRUN 0x0D\r
- /* 0x0E, 0x0F reserved for HCD */\r
-#define TD_NOTACCESSED 0x0F\r
-\r
-\r
-/* map OHCI TD status codes (CC) to errno values */ \r
-static const int cc_to_error [16] = { \r
- /* No Error */ 0,\r
- /* CRC Error */ -EILSEQ,\r
- /* Bit Stuff */ -EPROTO,\r
- /* Data Togg */ -EILSEQ,\r
- /* Stall */ -EPIPE,\r
- /* DevNotResp */ -ETIMEDOUT,\r
- /* PIDCheck */ -EPROTO,\r
- /* UnExpPID */ -EPROTO,\r
- /* DataOver */ -EOVERFLOW,\r
- /* DataUnder */ -EREMOTEIO,\r
- /* (for hw) */ -EIO,\r
- /* (for hw) */ -EIO,\r
- /* BufferOver */ -ECOMM,\r
- /* BuffUnder */ -ENOSR,\r
- /* (for HCD) */ -EALREADY,\r
- /* (for HCD) */ -EALREADY \r
-};\r
-\r
-\r
-/*\r
- * The HCCA (Host Controller Communications Area) is a 256 byte\r
- * structure defined section 4.4.1 of the OHCI spec. The HC is\r
- * told the base address of it. It must be 256-byte aligned.\r
- */\r
-struct ohci_hcca {\r
-#define NUM_INTS 32\r
- __u32 int_table [NUM_INTS]; /* periodic schedule */\r
- __u16 frame_no; /* current frame number */\r
- __u16 pad1; /* set to 0 on each frame_no change */\r
- __u32 done_head; /* info returned for an interrupt */\r
- u8 reserved_for_hc [116];\r
- u8 what [4]; /* spec only identifies 252 bytes :) */\r
-} __attribute__ ((aligned(256)));\r
-\r
- \r
-/*\r
- * This is the structure of the OHCI controller's memory mapped I/O region.\r
- * You must use readl() and writel() (in <asm/io.h>) to access these fields!!\r
- * Layout is in section 7 (and appendix B) of the spec.\r
- */\r
-struct ohci_regs {\r
- /* control and status registers (section 7.1) */\r
- __u32 revision;\r
- __u32 control;\r
- __u32 cmdstatus;\r
- __u32 intrstatus;\r
- __u32 intrenable;\r
- __u32 intrdisable;\r
-\r
- /* memory pointers (section 7.2) */\r
- __u32 hcca;\r
- __u32 ed_periodcurrent;\r
- __u32 ed_controlhead;\r
- __u32 ed_controlcurrent;\r
- __u32 ed_bulkhead;\r
- __u32 ed_bulkcurrent;\r
- __u32 donehead;\r
-\r
- /* frame counters (section 7.3) */\r
- __u32 fminterval;\r
- __u32 fmremaining;\r
- __u32 fmnumber;\r
- __u32 periodicstart;\r
- __u32 lsthresh;\r
-\r
- /* Root hub ports (section 7.4) */\r
- struct ohci_roothub_regs {\r
- __u32 a;\r
- __u32 b;\r
- __u32 status;\r
-#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports (RH_A_NDP) */\r
- __u32 portstatus [MAX_ROOT_PORTS];\r
- } roothub;\r
-\r
- /* and optional "legacy support" registers (appendix B) at 0x0100 */\r
-\r
-} __attribute__ ((aligned(32)));\r
-\r
-\r
-/* OHCI CONTROL AND STATUS REGISTER MASKS */\r
-\r
-/*\r
- * HcControl (control) register masks\r
- */\r
-#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */\r
-#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */\r
-#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */\r
-#define OHCI_CTRL_CLE (1 << 4) /* control list enable */\r
-#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */\r
-#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */\r
-#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */\r
-#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */\r
-#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */\r
-\r
-/* pre-shifted values for HCFS */\r
-#define OHCI_USB_RESET (0 << 6)\r
-#define OHCI_USB_RESUME (1 << 6)\r
-#define OHCI_USB_OPER (2 << 6)\r
-#define OHCI_USB_SUSPEND (3 << 6)\r
-\r
-// HCFS itself\r
-static char *hcfs2string (int state)\r
-{\r
- switch (state) {\r
- case OHCI_USB_RESET: return "reset";\r
- case OHCI_USB_RESUME: return "resume";\r
- case OHCI_USB_OPER: return "operational";\r
- case OHCI_USB_SUSPEND: return "suspend";\r
- }\r
- return "?";\r
-}\r
-\r
-/*\r
- * HcCommandStatus (cmdstatus) register masks\r
- */\r
-#define OHCI_HCR (1 << 0) /* host controller reset */\r
-#define OHCI_CLF (1 << 1) /* control list filled */\r
-#define OHCI_BLF (1 << 2) /* bulk list filled */\r
-#define OHCI_OCR (1 << 3) /* ownership change request */\r
-#define OHCI_SOC (3 << 16) /* scheduling overrun count */\r
-\r
-/*\r
- * masks used with interrupt registers:\r
- * HcInterruptStatus (intrstatus)\r
- * HcInterruptEnable (intrenable)\r
- * HcInterruptDisable (intrdisable)\r
- */\r
-#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */\r
-#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */\r
-#define OHCI_INTR_SF (1 << 2) /* start frame */\r
-#define OHCI_INTR_RD (1 << 3) /* resume detect */\r
-#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */\r
-#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */\r
-#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */\r
-#define OHCI_INTR_OC (1 << 30) /* ownership change */\r
-#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */\r
-\r
-\r
-/* OHCI ROOT HUB REGISTER MASKS */\r
- \r
-/* roothub.portstatus [i] bits */\r
-#define RH_PS_CCS 0x00000001 /* current connect status */\r
-#define RH_PS_PES 0x00000002 /* port enable status*/\r
-#define RH_PS_PSS 0x00000004 /* port suspend status */\r
-#define RH_PS_POCI 0x00000008 /* port over current indicator */\r
-#define RH_PS_PRS 0x00000010 /* port reset status */\r
-#define RH_PS_PPS 0x00000100 /* port power status */\r
-#define RH_PS_LSDA 0x00000200 /* low speed device attached */\r
-#define RH_PS_CSC 0x00010000 /* connect status change */\r
-#define RH_PS_PESC 0x00020000 /* port enable status change */\r
-#define RH_PS_PSSC 0x00040000 /* port suspend status change */\r
-#define RH_PS_OCIC 0x00080000 /* over current indicator change */\r
-#define RH_PS_PRSC 0x00100000 /* port reset status change */\r
-\r
-/* roothub.status bits */\r
-#define RH_HS_LPS 0x00000001 /* local power status */\r
-#define RH_HS_OCI 0x00000002 /* over current indicator */\r
-#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */\r
-#define RH_HS_LPSC 0x00010000 /* local power status change */\r
-#define RH_HS_OCIC 0x00020000 /* over current indicator change */\r
-#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */\r
-\r
-/* roothub.b masks */\r
-#define RH_B_DR 0x0000ffff /* device removable flags */\r
-#define RH_B_PPCM 0xffff0000 /* port power control mask */\r
-\r
-/* roothub.a masks */\r
-#define RH_A_NDP (0xff << 0) /* number of downstream ports */\r
-#define RH_A_PSM (1 << 8) /* power switching mode */\r
-#define RH_A_NPS (1 << 9) /* no power switching */\r
-#define RH_A_DT (1 << 10) /* device type (mbz) */\r
-#define RH_A_OCPM (1 << 11) /* over current protection mode */\r
-#define RH_A_NOCP (1 << 12) /* no over current protection */\r
-#define RH_A_POTPGT (0xff << 24) /* power on to power good time */\r
-\r
-\r
-/* hcd-private per-urb state */\r
-typedef struct urb_priv {\r
- struct ed *ed;\r
- __u16 length; // # tds in this request\r
- __u16 td_cnt; // tds already serviced\r
- int state;\r
- struct td *td [0]; // all TDs in this request\r
-\r
-} urb_priv_t;\r
-\r
-#define URB_DEL 1\r
-\r
-#define TD_HASH_SIZE 64 /* power'o'two */\r
-// sizeof (struct td) ~= 64 == 2^6 ... \r
-#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)\r
-\r
-\r
-/*\r
- * This is the full ohci controller description\r
- *\r
- * Note how the "proper" USB information is just\r
- * a subset of what the full implementation needs. (Linus)\r
- */\r
-\r
-struct ohci_hcd {\r
- spinlock_t lock;\r
-\r
- /*\r
- * I/O memory used to communicate with the HC (dma-consistent)\r
- */\r
- struct ohci_regs *regs;\r
-\r
- /*\r
- * main memory used to communicate with the HC (dma-consistent).\r
- * hcd adds to schedule for a live hc any time, but removals finish\r
- * only at the start of the next frame.\r
- */\r
- struct ohci_hcca *hcca;\r
- dma_addr_t hcca_dma;\r
-\r
- struct ed *ed_rm_list; /* to be removed */\r
-\r
- struct ed *ed_bulktail; /* last in bulk list */\r
- struct ed *ed_controltail; /* last in ctrl list */\r
- struct ed *periodic [NUM_INTS]; /* shadow int_table */\r
-\r
- /*\r
- * memory management for queue data structures\r
- */\r
- struct pci_pool *td_cache;\r
- struct pci_pool *ed_cache;\r
- struct td *td_hash [TD_HASH_SIZE];\r
-\r
- /*\r
- * driver state\r
- */\r
- int disabled; /* e.g. got a UE, we're hung */\r
- int sleeping;\r
- int load [NUM_INTS];\r
- u32 hc_control; /* copy of hc control reg */\r
-\r
- unsigned long flags; /* for HC bugs */\r
-#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */\r
-#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */\r
- // there are also chip quirks/bugs in init logic\r
-\r
- /*\r
- * framework state\r
- */\r
- struct usb_hcd hcd;\r
-};\r
-\r
-#define hcd_to_ohci(hcd_ptr) container_of(hcd_ptr, struct ohci_hcd, hcd)\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-#ifndef DEBUG\r
-#define STUB_DEBUG_FILES\r
-#endif /* DEBUG */\r
-\r
-#define ohci_dbg(ohci, fmt, args...) \\r
- dev_dbg ((ohci)->hcd.controller , fmt , ## args )\r
-#define ohci_err(ohci, fmt, args...) \\r
- dev_err ((ohci)->hcd.controller , fmt , ## args )\r
-#define ohci_info(ohci, fmt, args...) \\r
- dev_info ((ohci)->hcd.controller , fmt , ## args )\r
-#define ohci_warn(ohci, fmt, args...) \\r
- dev_warn ((ohci)->hcd.controller , fmt , ## args )\r
-\r
-#ifdef OHCI_VERBOSE_DEBUG\r
-# define ohci_vdbg ohci_dbg\r
-#else\r
-# define ohci_vdbg(ohci, fmt, args...) do { } while (0)\r
-#endif\r
-\r
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * This file is licenced under the GPL.
+ */
+
+/*
+ * OHCI Endpoint Descriptor (ED) ... holds TD queue
+ * See OHCI spec, section 4.2
+ *
+ * This is a "Queue Head" for those transfers, which is why
+ * both EHCI and UHCI call similar structures a "QH".
+ */
+struct ed {
+ /* first fields are hardware-specified, le32 */
+ __u32 hwINFO; /* endpoint config bitmap */
+ /* info bits defined by hcd */
+#define ED_DEQUEUE __constant_cpu_to_le32(1 << 27)
+ /* info bits defined by the hardware */
+#define ED_ISO __constant_cpu_to_le32(1 << 15)
+#define ED_SKIP __constant_cpu_to_le32(1 << 14)
+#define ED_LOWSPEED __constant_cpu_to_le32(1 << 13)
+#define ED_OUT __constant_cpu_to_le32(0x01 << 11)
+#define ED_IN __constant_cpu_to_le32(0x02 << 11)
+ __u32 hwTailP; /* tail of TD list */
+ __u32 hwHeadP; /* head of TD list (hc r/w) */
+#define ED_C __constant_cpu_to_le32(0x02) /* toggle carry */
+#define ED_H __constant_cpu_to_le32(0x01) /* halted */
+ __u32 hwNextED; /* next ED in list */
+
+ /* rest are purely for the driver's use */
+ dma_addr_t dma; /* addr of ED */
+ struct td *dummy; /* next TD to activate */
+
+ /* host's view of schedule */
+ struct ed *ed_next; /* on schedule or rm_list */
+ struct ed *ed_prev; /* for non-interrupt EDs */
+ struct list_head td_list; /* "shadow list" of our TDs */
+
+ /* create --> IDLE --> OPER --> ... --> IDLE --> destroy
+ * usually: OPER --> UNLINK --> (IDLE | OPER) --> ...
+ * some special cases : OPER --> IDLE ...
+ */
+ u8 state; /* ED_{IDLE,UNLINK,OPER} */
+#define ED_IDLE 0x00 /* NOT linked to HC */
+#define ED_UNLINK 0x01 /* being unlinked from hc */
+#define ED_OPER 0x02 /* IS linked to hc */
+
+ u8 type; /* PIPE_{BULK,...} */
+
+ /* periodic scheduling params (for intr and iso) */
+ u8 branch;
+ u16 interval;
+ u16 load;
+ u16 last_iso; /* iso only */
+
+ /* HC may see EDs on rm_list until next frame (frame_no == tick) */
+ u16 tick;
+} __attribute__ ((aligned(16)));
+
+#define ED_MASK ((u32)~0x0f) /* strip hw status in low addr bits */
+
+
+/*
+ * OHCI Transfer Descriptor (TD) ... one per transfer segment
+ * See OHCI spec, sections 4.3.1 (general = control/bulk/interrupt)
+ * and 4.3.2 (iso)
+ */
+struct td {
+ /* first fields are hardware-specified, le32 */
+ __u32 hwINFO; /* transfer info bitmask */
+
+ /* hwINFO bits for both general and iso tds: */
+#define TD_CC 0xf0000000 /* condition code */
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+//#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_DI 0x00E00000 /* frames before interrupt */
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+ /* these two bits are available for definition/use by HCDs in both
+ * general and iso tds ... others are available for only one type
+ */
+#define TD_DONE 0x00020000 /* retired to donelist */
+#define TD_ISO 0x00010000 /* copy of ED_ISO */
+
+ /* hwINFO bits for general tds: */
+#define TD_EC 0x0C000000 /* error count */
+#define TD_T 0x03000000 /* data toggle state */
+#define TD_T_DATA0 0x02000000 /* DATA0 */
+#define TD_T_DATA1 0x03000000 /* DATA1 */
+#define TD_T_TOGGLE 0x00000000 /* uses ED_C */
+#define TD_DP 0x00180000 /* direction/pid */
+#define TD_DP_SETUP 0x00000000 /* SETUP pid */
+#define TD_DP_IN 0x00100000 /* IN pid */
+#define TD_DP_OUT 0x00080000 /* OUT pid */
+ /* 0x00180000 rsvd */
+#define TD_R 0x00040000 /* round: short packets OK? */
+
+ /* (no hwINFO #defines yet for iso tds) */
+
+ __u32 hwCBP; /* Current Buffer Pointer (or 0) */
+ __u32 hwNextTD; /* Next TD Pointer */
+ __u32 hwBE; /* Memory Buffer End Pointer */
+
+ /* PSW is only for ISO */
+#define MAXPSW 1 /* hardware allows 8 */
+ __u16 hwPSW [MAXPSW];
+
+ /* rest are purely for the driver's use */
+ __u8 index;
+ struct ed *ed;
+ struct td *td_hash; /* dma-->td hashtable */
+ struct td *next_dl_td;
+ struct urb *urb;
+
+ dma_addr_t td_dma; /* addr of this TD */
+ dma_addr_t data_dma; /* addr of data it points to */
+
+ struct list_head td_list; /* "shadow list", TDs on same ED */
+} __attribute__ ((aligned(32))); /* c/b/i need 16; only iso needs 32 */
+
+#define TD_MASK ((u32)~0x1f) /* strip hw status in low addr bits */
+
+/*
+ * Hardware transfer status codes -- CC from td->hwINFO or td->hwPSW
+ */
+#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
+ /* 0x0A, 0x0B reserved for hardware */
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+ /* 0x0E, 0x0F reserved for HCD */
+#define TD_NOTACCESSED 0x0F
+
+
+/* map OHCI TD status codes (CC) to errno values */
+static const int cc_to_error [16] = {
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIMEDOUT,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* (for hw) */ -EIO,
+ /* (for hw) */ -EIO,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ENOSR,
+ /* (for HCD) */ -EALREADY,
+ /* (for HCD) */ -EALREADY
+};
+
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined section 4.4.1 of the OHCI spec. The HC is
+ * told the base address of it. It must be 256-byte aligned.
+ */
+struct ohci_hcca {
+#define NUM_INTS 32
+ __u32 int_table [NUM_INTS]; /* periodic schedule */
+ __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];
+ u8 what [4]; /* spec only identifies 252 bytes :) */
+} __attribute__ ((aligned(256)));
+
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O region.
+ * You must use readl() and writel() (in <asm/io.h>) to access these fields!!
+ * Layout is in section 7 (and appendix B) of the spec.
+ */
+struct ohci_regs {
+ /* control and status registers (section 7.1) */
+ __u32 revision;
+ __u32 control;
+ __u32 cmdstatus;
+ __u32 intrstatus;
+ __u32 intrenable;
+ __u32 intrdisable;
+
+ /* memory pointers (section 7.2) */
+ __u32 hcca;
+ __u32 ed_periodcurrent;
+ __u32 ed_controlhead;
+ __u32 ed_controlcurrent;
+ __u32 ed_bulkhead;
+ __u32 ed_bulkcurrent;
+ __u32 donehead;
+
+ /* frame counters (section 7.3) */
+ __u32 fminterval;
+ __u32 fmremaining;
+ __u32 fmnumber;
+ __u32 periodicstart;
+ __u32 lsthresh;
+
+ /* Root hub ports (section 7.4) */
+ struct ohci_roothub_regs {
+ __u32 a;
+ __u32 b;
+ __u32 status;
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports (RH_A_NDP) */
+ __u32 portstatus [MAX_ROOT_PORTS];
+ } roothub;
+
+ /* and optional "legacy support" registers (appendix B) at 0x0100 */
+
+} __attribute__ ((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
+#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
+#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
+#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
+#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+#define OHCI_USB_RESET (0 << 6)
+#define OHCI_USB_RESUME (1 << 6)
+#define OHCI_USB_OPER (2 << 6)
+#define OHCI_USB_SUSPEND (3 << 6)
+
+// HCFS itself
+static char *hcfs2string (int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+ return "?";
+}
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_CLF (1 << 1) /* control list filled */
+#define OHCI_BLF (1 << 2) /* bulk list filled */
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
+
+
+/* hcd-private per-urb state */
+typedef struct urb_priv {
+ struct ed *ed;
+ __u16 length; // # tds in this request
+ __u16 td_cnt; // tds already serviced
+ int state;
+ struct td *td [0]; // all TDs in this request
+
+} urb_priv_t;
+
+#define URB_DEL 1
+
+#define TD_HASH_SIZE 64 /* power'o'two */
+// sizeof (struct td) ~= 64 == 2^6 ...
+#define TD_HASH_FUNC(td_dma) ((td_dma ^ (td_dma >> 6)) % TD_HASH_SIZE)
+
+
+/*
+ * 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_hcd {
+ spinlock_t lock;
+
+ /*
+ * I/O memory used to communicate with the HC (dma-consistent)
+ */
+ struct ohci_regs *regs;
+
+ /*
+ * main memory used to communicate with the HC (dma-consistent).
+ * hcd adds to schedule for a live hc any time, but removals finish
+ * only at the start of the next frame.
+ */
+ struct ohci_hcca *hcca;
+ dma_addr_t hcca_dma;
+
+ struct ed *ed_rm_list; /* to be removed */
+
+ struct ed *ed_bulktail; /* last in bulk list */
+ struct ed *ed_controltail; /* last in ctrl list */
+ struct ed *periodic [NUM_INTS]; /* shadow int_table */
+
+ /*
+ * memory management for queue data structures
+ */
+ struct pci_pool *td_cache;
+ struct pci_pool *ed_cache;
+ struct td *td_hash [TD_HASH_SIZE];
+
+ /*
+ * driver state
+ */
+ int disabled; /* e.g. got a UE, we're hung */
+ int sleeping;
+ int load [NUM_INTS];
+ u32 hc_control; /* copy of hc control reg */
+
+ unsigned long flags; /* for HC bugs */
+#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
+#define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */
+ // there are also chip quirks/bugs in init logic
+
+ /*
+ * framework state
+ */
+ struct usb_hcd hcd;
+};
+
+#define hcd_to_ohci(hcd_ptr) container_of(hcd_ptr, struct ohci_hcd, hcd)
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef DEBUG
+#define STUB_DEBUG_FILES
+#endif /* DEBUG */
+
+#define ohci_dbg(ohci, fmt, args...) \
+ dev_dbg ((ohci)->hcd.controller , fmt , ## args )
+#define ohci_err(ohci, fmt, args...) \
+ dev_err ((ohci)->hcd.controller , fmt , ## args )
+#define ohci_info(ohci, fmt, args...) \
+ dev_info ((ohci)->hcd.controller , fmt , ## args )
+#define ohci_warn(ohci, fmt, args...) \
+ dev_warn ((ohci)->hcd.controller , fmt , ## args )
+
+#ifdef OHCI_VERBOSE_DEBUG
+# define ohci_vdbg ohci_dbg
+#else
+# define ohci_vdbg(ohci, fmt, args...) do { } while (0)
+#endif
+
-#define REACTOS_VERSION_DLL\r
-#define REACTOS_STR_FILE_DESCRIPTION "USB OHCI Device Driver\0"\r
-#define REACTOS_STR_INTERNAL_NAME "ohci\0"\r
-#define REACTOS_STR_ORIGINAL_FILENAME "ohci.sys\0"\r
-#include <reactos/version.rc>\r
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION "USB OHCI Device Driver\0"
+#define REACTOS_STR_INTERNAL_NAME "ohci\0"
+#define REACTOS_STR_ORIGINAL_FILENAME "ohci.sys\0"
+#include <reactos/version.rc>
-/*\r
- * Configs for OHCI\r
- */\r
-\r
-#define CONFIG_PCI\r
+/*
+ * Configs for OHCI
+ */
+
+#define CONFIG_PCI
-/*\r
- ReactOS specific functions for OHCI module\r
- by Aleksey Bragin (aleksey@reactos.com)\r
- Some parts of code are inspired (or even just copied) from ReactOS Videoport driver\r
-*/\r
-\r
-#include <ddk/ntddk.h>\r
-#include <debug.h>\r
-#include "../linux/linux_wrapper.h"\r
-#include "ohci_main.h"\r
-\r
-// declare basic init funcs\r
-void init_wrapper(struct pci_dev *probe_dev);\r
-int ohci_hcd_pci_init (void);\r
-void ohci_hcd_pci_cleanup (void);\r
-int STDCALL usb_init(void);\r
-void STDCALL usb_exit(void);\r
-extern struct pci_driver ohci_pci_driver;\r
-extern const struct pci_device_id pci_ids[];\r
-\r
-\r
-\r
-// This should be removed, but for testing purposes it's here\r
-struct pci_dev *dev;\r
-//struct pci_device_id *dev_id;\r
-\r
-\r
-#define USB_OHCI_TAG TAG('u','s','b','o')\r
-\r
-NTSTATUS STDCALL AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)\r
-{\r
- PDEVICE_OBJECT fdo;\r
- NTSTATUS Status;\r
- WCHAR DeviceBuffer[20];\r
- UNICODE_STRING DeviceName;\r
- POHCI_DRIVER_EXTENSION DriverExtension;\r
- POHCI_DEVICE_EXTENSION DeviceExtension;\r
- ULONG Size, DeviceNumber;\r
-\r
- DPRINT1("ohci: AddDevice called\n");\r
-\r
- // Allocate driver extension now\r
- DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);\r
- if (DriverExtension == NULL)\r
- {\r
- Status = IoAllocateDriverObjectExtension(\r
- DriverObject,\r
- DriverObject,\r
- sizeof(OHCI_DRIVER_EXTENSION),\r
- (PVOID *)&DriverExtension);\r
-\r
- if (!NT_SUCCESS(Status))\r
- {\r
- DPRINT1("Allocating DriverObjectExtension failed.\n");\r
- return Status;\r
- }\r
- }\r
- \r
- // Create a unicode device name\r
- DeviceNumber = 0; //TODO: Allocate new device number every time\r
- swprintf(DeviceBuffer, L"\\Device\\USBFDO-%lu", DeviceNumber);\r
- RtlInitUnicodeString(&DeviceName, DeviceBuffer);\r
-\r
- Status = IoCreateDevice(DriverObject,\r
- sizeof(OHCI_DEVICE_EXTENSION)/* + DriverExtension->InitializationData.HwDeviceExtensionSize*/,\r
- &DeviceName,\r
- FILE_DEVICE_CONTROLLER,\r
- 0,\r
- FALSE,\r
- &fdo);\r
-\r
- if (!NT_SUCCESS(Status))\r
- {\r
- DPRINT("IoCreateDevice call failed with status 0x%08x\n", Status);\r
- return Status;\r
- }\r
-\r
- // zerofill device extension\r
- DeviceExtension = (POHCI_DEVICE_EXTENSION)pdo->DeviceExtension;\r
- RtlZeroMemory(DeviceExtension, sizeof(OHCI_DEVICE_EXTENSION));\r
- DeviceExtension->NextDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);\r
-\r
- fdo->Flags &= ~DO_DEVICE_INITIALIZING;\r
-\r
- // Initialize device extension\r
- DeviceExtension->DeviceNumber = DeviceNumber;\r
- DeviceExtension->PhysicalDeviceObject = pdo;\r
- DeviceExtension->FunctionalDeviceObject = fdo;\r
- DeviceExtension->DriverExtension = DriverExtension;\r
-\r
- /* Get bus number from the upper level bus driver. */\r
- Size = sizeof(ULONG);\r
- Status = IoGetDeviceProperty(\r
- pdo,\r
- DevicePropertyBusNumber,\r
- Size,\r
- &DeviceExtension->SystemIoBusNumber,\r
- &Size);\r
- \r
- if (!NT_SUCCESS(Status))\r
- {\r
- DPRINT("Couldn't get an information from bus driver. Panic!!!\n");\r
- return Status;\r
- }\r
-\r
- DPRINT("Done AddDevice\n");\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-VOID STDCALL DriverUnload(PDRIVER_OBJECT DriverObject)\r
-{\r
- DPRINT1("DriverUnload()\n");\r
-\r
- // Exit usb device\r
- usb_exit();\r
-\r
- // Remove device (ohci_pci_driver.remove)\r
- ohci_pci_driver.remove(dev);\r
-\r
- ExFreePool(dev->slot_name);\r
- ExFreePool(dev);\r
-\r
- // Perform some cleanup\r
- ohci_hcd_pci_cleanup();\r
-}\r
-\r
-NTSTATUS InitLinuxWrapper(PDEVICE_OBJECT DeviceObject)\r
-{\r
- NTSTATUS Status;\r
- POHCI_DEVICE_EXTENSION DeviceExtension = (POHCI_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
- // Fill generic linux structs\r
- dev = ExAllocatePoolWithTag(PagedPool, sizeof(struct pci_dev), USB_OHCI_TAG);\r
- \r
- init_wrapper(dev);\r
- dev->irq = DeviceExtension->InterruptLevel;\r
- dev->dev_ext = (PVOID)DeviceExtension;\r
- dev->slot_name = ExAllocatePoolWithTag(NonPagedPool, 128, USB_OHCI_TAG); // 128 max len for slot name\r
-\r
- strcpy(dev->dev.name, "OpenHCI PCI-USB Controller");\r
- strcpy(dev->slot_name, "OHCD PCI Slot");\r
-\r
- // Init the OHCI HCD. Probe will be called automatically, but will fail because id=NULL\r
- Status = ohci_hcd_pci_init();\r
- //FIXME: Check status returned value\r
-\r
- // Init core usb\r
- usb_init();\r
-\r
- // Probe device with real id now\r
- ohci_pci_driver.probe(dev, pci_ids);\r
-\r
- DPRINT("InitLinuxWrapper() done\n");\r
-\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-NTSTATUS STDCALL\r
-OHCD_PnPStartDevice(IN PDEVICE_OBJECT DeviceObject,\r
- IN PIRP Irp)\r
-{\r
- PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);\r
- PDRIVER_OBJECT DriverObject;\r
- POHCI_DRIVER_EXTENSION DriverExtension;\r
- POHCI_DEVICE_EXTENSION DeviceExtension;\r
- PCM_RESOURCE_LIST AllocatedResources;\r
-\r
- /*\r
- * Get the initialization data we saved in VideoPortInitialize.\r
- */\r
- DriverObject = DeviceObject->DriverObject;\r
- DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);\r
- DeviceExtension = (POHCI_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
- /*\r
- * Store some resources in the DeviceExtension.\r
- */\r
- AllocatedResources = Stack->Parameters.StartDevice.AllocatedResources;\r
- if (AllocatedResources != NULL)\r
- {\r
- CM_FULL_RESOURCE_DESCRIPTOR *FullList;\r
- CM_PARTIAL_RESOURCE_DESCRIPTOR *Descriptor;\r
- ULONG ResourceCount;\r
- ULONG ResourceListSize;\r
-\r
- /* Save the resource list */\r
- ResourceCount = AllocatedResources->List[0].PartialResourceList.Count;\r
- ResourceListSize =\r
- FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.\r
- PartialDescriptors[ResourceCount]);\r
- DeviceExtension->AllocatedResources = ExAllocatePool(PagedPool, ResourceListSize);\r
- if (DeviceExtension->AllocatedResources == NULL)\r
- {\r
- return STATUS_INSUFFICIENT_RESOURCES;\r
- }\r
-\r
- RtlCopyMemory(DeviceExtension->AllocatedResources,\r
- AllocatedResources,\r
- ResourceListSize);\r
-\r
- /* Get the interrupt level/vector - needed by HwFindAdapter sometimes */\r
- for (FullList = AllocatedResources->List;\r
- FullList < AllocatedResources->List + AllocatedResources->Count;\r
- FullList++)\r
- {\r
- /* FIXME: Is this ASSERT ok for resources from the PNP manager? */\r
- /*ASSERT(FullList->InterfaceType == PCIBus &&\r
- FullList->BusNumber == DeviceExtension->SystemIoBusNumber &&\r
- 1 == FullList->PartialResourceList.Version &&\r
- 1 == FullList->PartialResourceList.Revision);*/\r
- for (Descriptor = FullList->PartialResourceList.PartialDescriptors;\r
- Descriptor < FullList->PartialResourceList.PartialDescriptors + FullList->PartialResourceList.Count;\r
- Descriptor++)\r
- {\r
- if (Descriptor->Type == CmResourceTypeInterrupt)\r
- {\r
- DeviceExtension->InterruptLevel = Descriptor->u.Interrupt.Level;\r
- DeviceExtension->InterruptVector = Descriptor->u.Interrupt.Vector;\r
- }\r
- else if (Descriptor->Type == CmResourceTypeMemory)\r
- {\r
- DeviceExtension->BaseAddress = Descriptor->u.Memory.Start;\r
- DeviceExtension->BaseAddrLength = Descriptor->u.Memory.Length;\r
- }\r
- }\r
- }\r
- }\r
- DPRINT1("Interrupt level: 0x%x Interrupt Vector: 0x%x\n",\r
- DeviceExtension->InterruptLevel,\r
- DeviceExtension->InterruptVector);\r
-\r
- /*\r
- * Init wrapper with this object\r
- */\r
- return InitLinuxWrapper(DeviceObject);\r
-}\r
-\r
-// Dispatch PNP\r
-NTSTATUS STDCALL DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp)\r
-{\r
- PIO_STACK_LOCATION IrpSp;\r
- NTSTATUS Status;\r
-\r
- IrpSp = IoGetCurrentIrpStackLocation(Irp);\r
-\r
- switch (IrpSp->MinorFunction)\r
- {\r
- case IRP_MN_START_DEVICE:\r
- //Status = IntVideoPortForwardIrpAndWait(DeviceObject, Irp);\r
- //if (NT_SUCCESS(Status) && NT_SUCCESS(Irp->IoStatus.Status))\r
- \r
- Status = OHCD_PnPStartDevice(DeviceObject, Irp);\r
- Irp->IoStatus.Status = Status;\r
- Irp->IoStatus.Information = 0;\r
- IoCompleteRequest(Irp, IO_NO_INCREMENT);\r
- break;\r
-\r
-\r
- case IRP_MN_REMOVE_DEVICE:\r
- case IRP_MN_QUERY_REMOVE_DEVICE:\r
- case IRP_MN_CANCEL_REMOVE_DEVICE:\r
- case IRP_MN_SURPRISE_REMOVAL:\r
-\r
- case IRP_MN_STOP_DEVICE:\r
- //Status = IntVideoPortForwardIrpAndWait(DeviceObject, Irp);\r
- //if (NT_SUCCESS(Status) && NT_SUCCESS(Irp->IoStatus.Status))\r
- Status = STATUS_SUCCESS;\r
- Irp->IoStatus.Status = Status;\r
- Irp->IoStatus.Information = 0;\r
- IoCompleteRequest(Irp, IO_NO_INCREMENT);\r
-\r
- IoDeleteDevice(DeviceObject); // just delete device for now\r
- break;\r
-\r
- case IRP_MN_QUERY_STOP_DEVICE:\r
- case IRP_MN_CANCEL_STOP_DEVICE:\r
- Status = STATUS_SUCCESS;\r
- Irp->IoStatus.Status = STATUS_SUCCESS;\r
- Irp->IoStatus.Information = 0;\r
- IoCompleteRequest(Irp, IO_NO_INCREMENT);\r
- break;\r
- \r
- default:\r
- return STATUS_NOT_IMPLEMENTED;\r
- break;\r
- }\r
- \r
- return Status;\r
-}\r
-\r
-NTSTATUS STDCALL DispatchPower(PDEVICE_OBJECT fido, PIRP Irp)\r
-{\r
- DbgPrint("IRP_MJ_POWER dispatch\n");\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-/*\r
- * Standard DriverEntry method.\r
- */\r
-NTSTATUS STDCALL\r
-DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegPath)\r
-{\r
- DriverObject->DriverUnload = DriverUnload;\r
- DriverObject->DriverExtension->AddDevice = AddDevice;\r
- DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;\r
- DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;\r
-\r
- return STATUS_SUCCESS;\r
-}\r
+/*
+ ReactOS specific functions for OHCI module
+ by Aleksey Bragin (aleksey@reactos.com)
+ Some parts of code are inspired (or even just copied) from ReactOS Videoport driver
+*/
+
+#include <ddk/ntddk.h>
+#include <debug.h>
+#include "../linux/linux_wrapper.h"
+#include "ohci_main.h"
+
+// declare basic init funcs
+void init_wrapper(struct pci_dev *probe_dev);
+int ohci_hcd_pci_init (void);
+void ohci_hcd_pci_cleanup (void);
+int STDCALL usb_init(void);
+void STDCALL usb_exit(void);
+extern struct pci_driver ohci_pci_driver;
+extern const struct pci_device_id pci_ids[];
+
+
+
+// This should be removed, but for testing purposes it's here
+struct pci_dev *dev;
+//struct pci_device_id *dev_id;
+
+
+#define USB_OHCI_TAG TAG('u','s','b','o')
+
+NTSTATUS STDCALL AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)
+{
+ PDEVICE_OBJECT fdo;
+ NTSTATUS Status;
+ WCHAR DeviceBuffer[20];
+ UNICODE_STRING DeviceName;
+ POHCI_DRIVER_EXTENSION DriverExtension;
+ POHCI_DEVICE_EXTENSION DeviceExtension;
+ ULONG Size, DeviceNumber;
+
+ DPRINT1("ohci: AddDevice called\n");
+
+ // Allocate driver extension now
+ DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
+ if (DriverExtension == NULL)
+ {
+ Status = IoAllocateDriverObjectExtension(
+ DriverObject,
+ DriverObject,
+ sizeof(OHCI_DRIVER_EXTENSION),
+ (PVOID *)&DriverExtension);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Allocating DriverObjectExtension failed.\n");
+ return Status;
+ }
+ }
+
+ // Create a unicode device name
+ DeviceNumber = 0; //TODO: Allocate new device number every time
+ swprintf(DeviceBuffer, L"\\Device\\USBFDO-%lu", DeviceNumber);
+ RtlInitUnicodeString(&DeviceName, DeviceBuffer);
+
+ Status = IoCreateDevice(DriverObject,
+ sizeof(OHCI_DEVICE_EXTENSION)/* + DriverExtension->InitializationData.HwDeviceExtensionSize*/,
+ &DeviceName,
+ FILE_DEVICE_CONTROLLER,
+ 0,
+ FALSE,
+ &fdo);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("IoCreateDevice call failed with status 0x%08x\n", Status);
+ return Status;
+ }
+
+ // zerofill device extension
+ DeviceExtension = (POHCI_DEVICE_EXTENSION)pdo->DeviceExtension;
+ RtlZeroMemory(DeviceExtension, sizeof(OHCI_DEVICE_EXTENSION));
+ DeviceExtension->NextDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);
+
+ fdo->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ // Initialize device extension
+ DeviceExtension->DeviceNumber = DeviceNumber;
+ DeviceExtension->PhysicalDeviceObject = pdo;
+ DeviceExtension->FunctionalDeviceObject = fdo;
+ DeviceExtension->DriverExtension = DriverExtension;
+
+ /* Get bus number from the upper level bus driver. */
+ Size = sizeof(ULONG);
+ Status = IoGetDeviceProperty(
+ pdo,
+ DevicePropertyBusNumber,
+ Size,
+ &DeviceExtension->SystemIoBusNumber,
+ &Size);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("Couldn't get an information from bus driver. Panic!!!\n");
+ return Status;
+ }
+
+ DPRINT("Done AddDevice\n");
+ return STATUS_SUCCESS;
+}
+
+VOID STDCALL DriverUnload(PDRIVER_OBJECT DriverObject)
+{
+ DPRINT1("DriverUnload()\n");
+
+ // Exit usb device
+ usb_exit();
+
+ // Remove device (ohci_pci_driver.remove)
+ ohci_pci_driver.remove(dev);
+
+ ExFreePool(dev->slot_name);
+ ExFreePool(dev);
+
+ // Perform some cleanup
+ ohci_hcd_pci_cleanup();
+}
+
+NTSTATUS InitLinuxWrapper(PDEVICE_OBJECT DeviceObject)
+{
+ NTSTATUS Status;
+ POHCI_DEVICE_EXTENSION DeviceExtension = (POHCI_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ // Fill generic linux structs
+ dev = ExAllocatePoolWithTag(PagedPool, sizeof(struct pci_dev), USB_OHCI_TAG);
+
+ init_wrapper(dev);
+ dev->irq = DeviceExtension->InterruptLevel;
+ dev->dev_ext = (PVOID)DeviceExtension;
+ dev->slot_name = ExAllocatePoolWithTag(NonPagedPool, 128, USB_OHCI_TAG); // 128 max len for slot name
+
+ strcpy(dev->dev.name, "OpenHCI PCI-USB Controller");
+ strcpy(dev->slot_name, "OHCD PCI Slot");
+
+ // Init the OHCI HCD. Probe will be called automatically, but will fail because id=NULL
+ Status = ohci_hcd_pci_init();
+ //FIXME: Check status returned value
+
+ // Init core usb
+ usb_init();
+
+ // Probe device with real id now
+ ohci_pci_driver.probe(dev, pci_ids);
+
+ DPRINT("InitLinuxWrapper() done\n");
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL
+OHCD_PnPStartDevice(IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
+{
+ PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+ PDRIVER_OBJECT DriverObject;
+ POHCI_DRIVER_EXTENSION DriverExtension;
+ POHCI_DEVICE_EXTENSION DeviceExtension;
+ PCM_RESOURCE_LIST AllocatedResources;
+
+ /*
+ * Get the initialization data we saved in VideoPortInitialize.
+ */
+ DriverObject = DeviceObject->DriverObject;
+ DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
+ DeviceExtension = (POHCI_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ /*
+ * Store some resources in the DeviceExtension.
+ */
+ AllocatedResources = Stack->Parameters.StartDevice.AllocatedResources;
+ if (AllocatedResources != NULL)
+ {
+ CM_FULL_RESOURCE_DESCRIPTOR *FullList;
+ CM_PARTIAL_RESOURCE_DESCRIPTOR *Descriptor;
+ ULONG ResourceCount;
+ ULONG ResourceListSize;
+
+ /* Save the resource list */
+ ResourceCount = AllocatedResources->List[0].PartialResourceList.Count;
+ ResourceListSize =
+ FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.
+ PartialDescriptors[ResourceCount]);
+ DeviceExtension->AllocatedResources = ExAllocatePool(PagedPool, ResourceListSize);
+ if (DeviceExtension->AllocatedResources == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(DeviceExtension->AllocatedResources,
+ AllocatedResources,
+ ResourceListSize);
+
+ /* Get the interrupt level/vector - needed by HwFindAdapter sometimes */
+ for (FullList = AllocatedResources->List;
+ FullList < AllocatedResources->List + AllocatedResources->Count;
+ FullList++)
+ {
+ /* FIXME: Is this ASSERT ok for resources from the PNP manager? */
+ /*ASSERT(FullList->InterfaceType == PCIBus &&
+ FullList->BusNumber == DeviceExtension->SystemIoBusNumber &&
+ 1 == FullList->PartialResourceList.Version &&
+ 1 == FullList->PartialResourceList.Revision);*/
+ for (Descriptor = FullList->PartialResourceList.PartialDescriptors;
+ Descriptor < FullList->PartialResourceList.PartialDescriptors + FullList->PartialResourceList.Count;
+ Descriptor++)
+ {
+ if (Descriptor->Type == CmResourceTypeInterrupt)
+ {
+ DeviceExtension->InterruptLevel = Descriptor->u.Interrupt.Level;
+ DeviceExtension->InterruptVector = Descriptor->u.Interrupt.Vector;
+ }
+ else if (Descriptor->Type == CmResourceTypeMemory)
+ {
+ DeviceExtension->BaseAddress = Descriptor->u.Memory.Start;
+ DeviceExtension->BaseAddrLength = Descriptor->u.Memory.Length;
+ }
+ }
+ }
+ }
+ DPRINT1("Interrupt level: 0x%x Interrupt Vector: 0x%x\n",
+ DeviceExtension->InterruptLevel,
+ DeviceExtension->InterruptVector);
+
+ /*
+ * Init wrapper with this object
+ */
+ return InitLinuxWrapper(DeviceObject);
+}
+
+// Dispatch PNP
+NTSTATUS STDCALL DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ switch (IrpSp->MinorFunction)
+ {
+ case IRP_MN_START_DEVICE:
+ //Status = IntVideoPortForwardIrpAndWait(DeviceObject, Irp);
+ //if (NT_SUCCESS(Status) && NT_SUCCESS(Irp->IoStatus.Status))
+
+ Status = OHCD_PnPStartDevice(DeviceObject, Irp);
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ break;
+
+
+ case IRP_MN_REMOVE_DEVICE:
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ case IRP_MN_SURPRISE_REMOVAL:
+
+ case IRP_MN_STOP_DEVICE:
+ //Status = IntVideoPortForwardIrpAndWait(DeviceObject, Irp);
+ //if (NT_SUCCESS(Status) && NT_SUCCESS(Irp->IoStatus.Status))
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ IoDeleteDevice(DeviceObject); // just delete device for now
+ break;
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ break;
+
+ default:
+ return STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return Status;
+}
+
+NTSTATUS STDCALL DispatchPower(PDEVICE_OBJECT fido, PIRP Irp)
+{
+ DbgPrint("IRP_MJ_POWER dispatch\n");
+ return STATUS_SUCCESS;
+}
+
+/*
+ * Standard DriverEntry method.
+ */
+NTSTATUS STDCALL
+DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegPath)
+{
+ DriverObject->DriverUnload = DriverUnload;
+ DriverObject->DriverExtension->AddDevice = AddDevice;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
+ DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
+
+ return STATUS_SUCCESS;
+}
-/*\r
- * OHCI WDM/PNP driver\r
- *\r
- * Copyright (C) 2005 ReactOS Team\r
- *\r
- * Author: Aleksey Bragin (aleksey@reactos.com)\r
- *\r
- */\r
-\r
-#ifndef OHCI_MAIN_H\r
-#define OHCI_MAIN_H\r
-\r
-typedef struct _OHCI_DRIVER_EXTENSION\r
-{\r
- //OHCI_HW_INITIALIZATION_DATA InitializationData;\r
- PVOID HwContext;\r
- //UNICODE_STRING RegistryPath;\r
-} OHCI_DRIVER_EXTENSION, *POHCI_DRIVER_EXTENSION;\r
-\r
-typedef struct _OHCI_DEVICE_EXTENSTION\r
-{\r
- ULONG DeviceNumber;\r
- PDEVICE_OBJECT PhysicalDeviceObject;\r
- PDEVICE_OBJECT FunctionalDeviceObject;\r
- PDEVICE_OBJECT NextDeviceObject;\r
- //UNICODE_STRING RegistryPath;\r
- PKINTERRUPT InterruptObject;\r
- KSPIN_LOCK InterruptSpinLock;\r
- PCM_RESOURCE_LIST AllocatedResources;\r
- ULONG InterruptVector;\r
- ULONG InterruptLevel;\r
- PHYSICAL_ADDRESS BaseAddress;\r
- ULONG BaseAddrLength;\r
- ULONG Flags;\r
- ULONG AdapterInterfaceType;\r
- ULONG SystemIoBusNumber;\r
- ULONG SystemIoSlotNumber;\r
- LIST_ENTRY AddressMappingListHead;\r
- //KDPC DpcObject;\r
- OHCI_DRIVER_EXTENSION *DriverExtension;\r
- ULONG DeviceOpened;\r
- //KMUTEX DeviceLock;\r
- //CHAR MiniPortDeviceExtension[1];\r
-} OHCI_DEVICE_EXTENSION, *POHCI_DEVICE_EXTENSION;\r
-\r
-\r
-#endif\r
+/*
+ * OHCI WDM/PNP driver
+ *
+ * Copyright (C) 2005 ReactOS Team
+ *
+ * Author: Aleksey Bragin (aleksey@reactos.com)
+ *
+ */
+
+#ifndef OHCI_MAIN_H
+#define OHCI_MAIN_H
+
+typedef struct _OHCI_DRIVER_EXTENSION
+{
+ //OHCI_HW_INITIALIZATION_DATA InitializationData;
+ PVOID HwContext;
+ //UNICODE_STRING RegistryPath;
+} OHCI_DRIVER_EXTENSION, *POHCI_DRIVER_EXTENSION;
+
+typedef struct _OHCI_DEVICE_EXTENSTION
+{
+ ULONG DeviceNumber;
+ PDEVICE_OBJECT PhysicalDeviceObject;
+ PDEVICE_OBJECT FunctionalDeviceObject;
+ PDEVICE_OBJECT NextDeviceObject;
+ //UNICODE_STRING RegistryPath;
+ PKINTERRUPT InterruptObject;
+ KSPIN_LOCK InterruptSpinLock;
+ PCM_RESOURCE_LIST AllocatedResources;
+ ULONG InterruptVector;
+ ULONG InterruptLevel;
+ PHYSICAL_ADDRESS BaseAddress;
+ ULONG BaseAddrLength;
+ ULONG Flags;
+ ULONG AdapterInterfaceType;
+ ULONG SystemIoBusNumber;
+ ULONG SystemIoSlotNumber;
+ LIST_ENTRY AddressMappingListHead;
+ //KDPC DpcObject;
+ OHCI_DRIVER_EXTENSION *DriverExtension;
+ ULONG DeviceOpened;
+ //KMUTEX DeviceLock;
+ //CHAR MiniPortDeviceExtension[1];
+} OHCI_DEVICE_EXTENSION, *POHCI_DEVICE_EXTENSION;
+
+
+#endif
-#ifndef _I386_BITOPS_H\r
-#define _I386_BITOPS_H\r
-\r
-/*\r
- * Copyright 1992, Linus Torvalds.\r
- */\r
-\r
-//#include <linux/config.h>\r
-\r
-/*\r
- * These have to be done with inline assembly: that way the bit-setting\r
- * is guaranteed to be atomic. All bit operations return 0 if the bit\r
- * was cleared before the operation and != 0 if it was not.\r
- *\r
- * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).\r
- */\r
-\r
-#ifdef CONFIG_SMP\r
-#define LOCK_PREFIX "lock ; "\r
-#else\r
-#define LOCK_PREFIX ""\r
-#endif\r
-\r
-#define ADDR (*(volatile long *) addr)\r
-\r
-/**\r
- * set_bit - Atomically set a bit in memory\r
- * @nr: the bit to set\r
- * @addr: the address to start counting from\r
- *\r
- * This function is atomic and may not be reordered. See __set_bit()\r
- * if you do not require the atomic guarantees.\r
- * Note that @nr may be almost arbitrarily large; this function is not\r
- * restricted to acting on a single-word quantity.\r
- */\r
-static __inline__ void set_bit(int nr, volatile void * addr)\r
-{\r
- __asm__ __volatile__( LOCK_PREFIX\r
- "btsl %1,%0"\r
- :"=m" (ADDR)\r
- :"Ir" (nr));\r
-}\r
-\r
-/**\r
- * __set_bit - Set a bit in memory\r
- * @nr: the bit to set\r
- * @addr: the address to start counting from\r
- *\r
- * Unlike set_bit(), this function is non-atomic and may be reordered.\r
- * If it's called on the same region of memory simultaneously, the effect\r
- * may be that only one operation succeeds.\r
- */\r
-static __inline__ void __set_bit(int nr, volatile void * addr)\r
-{\r
- __asm__(\r
- "btsl %1,%0"\r
- :"=m" (ADDR)\r
- :"Ir" (nr));\r
-}\r
-\r
-/**\r
- * clear_bit - Clears a bit in memory\r
- * @nr: Bit to clear\r
- * @addr: Address to start counting from\r
- *\r
- * clear_bit() is atomic and may not be reordered. However, it does\r
- * not contain a memory barrier, so if it is used for locking purposes,\r
- * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()\r
- * in order to ensure changes are visible on other processors.\r
- */\r
-static __inline__ void clear_bit(int nr, volatile void * addr)\r
-{\r
- __asm__ __volatile__( LOCK_PREFIX\r
- "btrl %1,%0"\r
- :"=m" (ADDR)\r
- :"Ir" (nr));\r
-}\r
-#define smp_mb__before_clear_bit() barrier()\r
-#define smp_mb__after_clear_bit() barrier()\r
-\r
-/**\r
- * __change_bit - Toggle a bit in memory\r
- * @nr: the bit to set\r
- * @addr: the address to start counting from\r
- *\r
- * Unlike change_bit(), this function is non-atomic and may be reordered.\r
- * If it's called on the same region of memory simultaneously, the effect\r
- * may be that only one operation succeeds.\r
- */\r
-static __inline__ void __change_bit(int nr, volatile void * addr)\r
-{\r
- __asm__ __volatile__(\r
- "btcl %1,%0"\r
- :"=m" (ADDR)\r
- :"Ir" (nr));\r
-}\r
-\r
-/**\r
- * change_bit - Toggle a bit in memory\r
- * @nr: Bit to clear\r
- * @addr: Address to start counting from\r
- *\r
- * change_bit() is atomic and may not be reordered.\r
- * Note that @nr may be almost arbitrarily large; this function is not\r
- * restricted to acting on a single-word quantity.\r
- */\r
-static __inline__ void change_bit(int nr, volatile void * addr)\r
-{\r
- __asm__ __volatile__( LOCK_PREFIX\r
- "btcl %1,%0"\r
- :"=m" (ADDR)\r
- :"Ir" (nr));\r
-}\r
-\r
-/**\r
- * test_and_set_bit - Set a bit and return its old value\r
- * @nr: Bit to set\r
- * @addr: Address to count from\r
- *\r
- * This operation is atomic and cannot be reordered. \r
- * It also implies a memory barrier.\r
- */\r
-static __inline__ int test_and_set_bit(int nr, volatile void * addr)\r
-{\r
- int oldbit;\r
-\r
- __asm__ __volatile__( LOCK_PREFIX\r
- "btsl %2,%1\n\tsbbl %0,%0"\r
- :"=r" (oldbit),"=m" (ADDR)\r
- :"Ir" (nr) : "memory");\r
- return oldbit;\r
-}\r
-\r
-/**\r
- * __test_and_set_bit - Set a bit and return its old value\r
- * @nr: Bit to set\r
- * @addr: Address to count from\r
- *\r
- * This operation is non-atomic and can be reordered. \r
- * If two examples of this operation race, one can appear to succeed\r
- * but actually fail. You must protect multiple accesses with a lock.\r
- */\r
-static __inline__ int __test_and_set_bit(int nr, volatile void * addr)\r
-{\r
- int oldbit;\r
-\r
- __asm__(\r
- "btsl %2,%1\n\tsbbl %0,%0"\r
- :"=r" (oldbit),"=m" (ADDR)\r
- :"Ir" (nr));\r
- return oldbit;\r
-}\r
-\r
-/**\r
- * test_and_clear_bit - Clear a bit and return its old value\r
- * @nr: Bit to set\r
- * @addr: Address to count from\r
- *\r
- * This operation is atomic and cannot be reordered. \r
- * It also implies a memory barrier.\r
- */\r
-static __inline__ int test_and_clear_bit(int nr, volatile void * addr)\r
-{\r
- int oldbit;\r
-\r
- __asm__ __volatile__( LOCK_PREFIX\r
- "btrl %2,%1\n\tsbbl %0,%0"\r
- :"=r" (oldbit),"=m" (ADDR)\r
- :"Ir" (nr) : "memory");\r
- return oldbit;\r
-}\r
-\r
-/**\r
- * __test_and_clear_bit - Clear a bit and return its old value\r
- * @nr: Bit to set\r
- * @addr: Address to count from\r
- *\r
- * This operation is non-atomic and can be reordered. \r
- * If two examples of this operation race, one can appear to succeed\r
- * but actually fail. You must protect multiple accesses with a lock.\r
- */\r
-static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)\r
-{\r
- int oldbit;\r
-\r
- __asm__(\r
- "btrl %2,%1\n\tsbbl %0,%0"\r
- :"=r" (oldbit),"=m" (ADDR)\r
- :"Ir" (nr));\r
- return oldbit;\r
-}\r
-\r
-/* WARNING: non atomic and it can be reordered! */\r
-static __inline__ int __test_and_change_bit(int nr, volatile void * addr)\r
-{\r
- int oldbit;\r
-\r
- __asm__ __volatile__(\r
- "btcl %2,%1\n\tsbbl %0,%0"\r
- :"=r" (oldbit),"=m" (ADDR)\r
- :"Ir" (nr) : "memory");\r
- return oldbit;\r
-}\r
-\r
-/**\r
- * test_and_change_bit - Change a bit and return its new value\r
- * @nr: Bit to set\r
- * @addr: Address to count from\r
- *\r
- * This operation is atomic and cannot be reordered. \r
- * It also implies a memory barrier.\r
- */\r
-static __inline__ int test_and_change_bit(int nr, volatile void * addr)\r
-{\r
- int oldbit;\r
-\r
- __asm__ __volatile__( LOCK_PREFIX\r
- "btcl %2,%1\n\tsbbl %0,%0"\r
- :"=r" (oldbit),"=m" (ADDR)\r
- :"Ir" (nr) : "memory");\r
- return oldbit;\r
-}\r
-\r
-#if 0 /* Fool kernel-doc since it doesn't do macros yet */\r
-/**\r
- * test_bit - Determine whether a bit is set\r
- * @nr: bit number to test\r
- * @addr: Address to start counting from\r
- */\r
-static int test_bit(int nr, const volatile void * addr);\r
-#endif\r
-\r
-static __inline__ int constant_test_bit(int nr, const volatile void * addr)\r
-{\r
- return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;\r
-}\r
-\r
-static __inline__ int variable_test_bit(int nr, volatile void * addr)\r
-{\r
- int oldbit;\r
-\r
- __asm__ __volatile__(\r
- "btl %2,%1\n\tsbbl %0,%0"\r
- :"=r" (oldbit)\r
- :"m" (ADDR),"Ir" (nr));\r
- return oldbit;\r
-}\r
-\r
-#define test_bit(nr,addr) \\r
-(__builtin_constant_p(nr) ? \\r
- constant_test_bit((nr),(addr)) : \\r
- variable_test_bit((nr),(addr)))\r
-\r
-/**\r
- * find_first_zero_bit - find the first zero bit in a memory region\r
- * @addr: The address to start the search at\r
- * @size: The maximum size to search\r
- *\r
- * Returns the bit-number of the first zero bit, not the number of the byte\r
- * containing a bit.\r
- */\r
-static __inline__ int find_first_zero_bit(void * addr, unsigned size)\r
-{\r
- int d0, d1, d2;\r
- int res;\r
-\r
- if (!size)\r
- return 0;\r
- /* This looks at memory. Mark it volatile to tell gcc not to move it around */\r
- __asm__ __volatile__(\r
- "movl $-1,%%eax\n\t"\r
- "xorl %%edx,%%edx\n\t"\r
- "repe; scasl\n\t"\r
- "je 1f\n\t"\r
- "xorl -4(%%edi),%%eax\n\t"\r
- "subl $4,%%edi\n\t"\r
- "bsfl %%eax,%%edx\n"\r
- "1:\tsubl %%ebx,%%edi\n\t"\r
- "shll $3,%%edi\n\t"\r
- "addl %%edi,%%edx"\r
- :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2)\r
- :"1" ((size + 31) >> 5), "2" (addr), "b" (addr));\r
- return res;\r
-}\r
-\r
-/**\r
- * find_next_zero_bit - find the first zero bit in a memory region\r
- * @addr: The address to base the search on\r
- * @offset: The bitnumber to start searching at\r
- * @size: The maximum size to search\r
- */\r
-static __inline__ int find_next_zero_bit (void * addr, int size, int offset)\r
-{\r
- unsigned long * p = ((unsigned long *) addr) + (offset >> 5);\r
- int set = 0, bit = offset & 31, res;\r
- \r
- if (bit) {\r
- /*\r
- * Look for zero in first byte\r
- */\r
- __asm__("bsfl %1,%0\n\t"\r
- "jne 1f\n\t"\r
- "movl $32, %0\n"\r
- "1:"\r
- : "=r" (set)\r
- : "r" (~(*p >> bit)));\r
- if (set < (32 - bit))\r
- return set + offset;\r
- set = 32 - bit;\r
- p++;\r
- }\r
- /*\r
- * No zero yet, search remaining full bytes for a zero\r
- */\r
- res = find_first_zero_bit (p, size - 32 * (p - (unsigned long *) addr));\r
- return (offset + set + res);\r
-}\r
-\r
-/**\r
- * ffz - find first zero in word.\r
- * @word: The word to search\r
- *\r
- * Undefined if no zero exists, so code should check against ~0UL first.\r
- */\r
-static __inline__ unsigned long ffz(unsigned long word)\r
-{\r
- __asm__("bsfl %1,%0"\r
- :"=r" (word)\r
- :"r" (~word));\r
- return word;\r
-}\r
-\r
-#ifdef __KERNEL__\r
-\r
-/**\r
- * ffs - find first bit set\r
- * @x: the word to search\r
- *\r
- * This is defined the same way as\r
- * the libc and compiler builtin ffs routines, therefore\r
- * differs in spirit from the above ffz (man ffs).\r
- */\r
-static __inline__ int ffs(int x)\r
-{\r
- int r;\r
-\r
- __asm__("bsfl %1,%0\n\t"\r
- "jnz 1f\n\t"\r
- "movl $-1,%0\n"\r
- "1:" : "=r" (r) : "rm" (x));\r
- return r+1;\r
-}\r
-\r
-/**\r
- * hweightN - returns the hamming weight of a N-bit word\r
- * @x: the word to weigh\r
- *\r
- * The Hamming Weight of a number is the total number of bits set in it.\r
- */\r
-\r
-#define hweight32(x) generic_hweight32(x)\r
-#define hweight16(x) generic_hweight16(x)\r
-#define hweight8(x) generic_hweight8(x)\r
-\r
-#endif /* __KERNEL__ */\r
-\r
-#ifdef __KERNEL__\r
-\r
-#define ext2_set_bit __test_and_set_bit\r
-#define ext2_clear_bit __test_and_clear_bit\r
-#define ext2_test_bit test_bit\r
-#define ext2_find_first_zero_bit find_first_zero_bit\r
-#define ext2_find_next_zero_bit find_next_zero_bit\r
-\r
-/* Bitmap functions for the minix filesystem. */\r
-#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr)\r
-#define minix_set_bit(nr,addr) __set_bit(nr,addr)\r
-#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr)\r
-#define minix_test_bit(nr,addr) test_bit(nr,addr)\r
-#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)\r
-\r
-#endif /* __KERNEL__ */\r
-\r
-#endif /* _I386_BITOPS_H */\r
+#ifndef _I386_BITOPS_H
+#define _I386_BITOPS_H
+
+/*
+ * Copyright 1992, Linus Torvalds.
+ */
+
+//#include <linux/config.h>
+
+/*
+ * These have to be done with inline assembly: that way the bit-setting
+ * is guaranteed to be atomic. All bit operations return 0 if the bit
+ * was cleared before the operation and != 0 if it was not.
+ *
+ * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
+ */
+
+#ifdef CONFIG_SMP
+#define LOCK_PREFIX "lock ; "
+#else
+#define LOCK_PREFIX ""
+#endif
+
+#define ADDR (*(volatile long *) addr)
+
+/**
+ * set_bit - Atomically set a bit in memory
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ *
+ * This function is atomic and may not be reordered. See __set_bit()
+ * if you do not require the atomic guarantees.
+ * Note that @nr may be almost arbitrarily large; this function is not
+ * restricted to acting on a single-word quantity.
+ */
+static __inline__ void set_bit(int nr, volatile void * addr)
+{
+ __asm__ __volatile__( LOCK_PREFIX
+ "btsl %1,%0"
+ :"=m" (ADDR)
+ :"Ir" (nr));
+}
+
+/**
+ * __set_bit - Set a bit in memory
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ *
+ * Unlike set_bit(), this function is non-atomic and may be reordered.
+ * If it's called on the same region of memory simultaneously, the effect
+ * may be that only one operation succeeds.
+ */
+static __inline__ void __set_bit(int nr, volatile void * addr)
+{
+ __asm__(
+ "btsl %1,%0"
+ :"=m" (ADDR)
+ :"Ir" (nr));
+}
+
+/**
+ * clear_bit - Clears a bit in memory
+ * @nr: Bit to clear
+ * @addr: Address to start counting from
+ *
+ * clear_bit() is atomic and may not be reordered. However, it does
+ * not contain a memory barrier, so if it is used for locking purposes,
+ * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
+ * in order to ensure changes are visible on other processors.
+ */
+static __inline__ void clear_bit(int nr, volatile void * addr)
+{
+ __asm__ __volatile__( LOCK_PREFIX
+ "btrl %1,%0"
+ :"=m" (ADDR)
+ :"Ir" (nr));
+}
+#define smp_mb__before_clear_bit() barrier()
+#define smp_mb__after_clear_bit() barrier()
+
+/**
+ * __change_bit - Toggle a bit in memory
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ *
+ * Unlike change_bit(), this function is non-atomic and may be reordered.
+ * If it's called on the same region of memory simultaneously, the effect
+ * may be that only one operation succeeds.
+ */
+static __inline__ void __change_bit(int nr, volatile void * addr)
+{
+ __asm__ __volatile__(
+ "btcl %1,%0"
+ :"=m" (ADDR)
+ :"Ir" (nr));
+}
+
+/**
+ * change_bit - Toggle a bit in memory
+ * @nr: Bit to clear
+ * @addr: Address to start counting from
+ *
+ * change_bit() is atomic and may not be reordered.
+ * Note that @nr may be almost arbitrarily large; this function is not
+ * restricted to acting on a single-word quantity.
+ */
+static __inline__ void change_bit(int nr, volatile void * addr)
+{
+ __asm__ __volatile__( LOCK_PREFIX
+ "btcl %1,%0"
+ :"=m" (ADDR)
+ :"Ir" (nr));
+}
+
+/**
+ * test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is atomic and cannot be reordered.
+ * It also implies a memory barrier.
+ */
+static __inline__ int test_and_set_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__ __volatile__( LOCK_PREFIX
+ "btsl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"Ir" (nr) : "memory");
+ return oldbit;
+}
+
+/**
+ * __test_and_set_bit - Set a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic and can be reordered.
+ * If two examples of this operation race, one can appear to succeed
+ * but actually fail. You must protect multiple accesses with a lock.
+ */
+static __inline__ int __test_and_set_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__(
+ "btsl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"Ir" (nr));
+ return oldbit;
+}
+
+/**
+ * test_and_clear_bit - Clear a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is atomic and cannot be reordered.
+ * It also implies a memory barrier.
+ */
+static __inline__ int test_and_clear_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__ __volatile__( LOCK_PREFIX
+ "btrl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"Ir" (nr) : "memory");
+ return oldbit;
+}
+
+/**
+ * __test_and_clear_bit - Clear a bit and return its old value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is non-atomic and can be reordered.
+ * If two examples of this operation race, one can appear to succeed
+ * but actually fail. You must protect multiple accesses with a lock.
+ */
+static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__(
+ "btrl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"Ir" (nr));
+ return oldbit;
+}
+
+/* WARNING: non atomic and it can be reordered! */
+static __inline__ int __test_and_change_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__ __volatile__(
+ "btcl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"Ir" (nr) : "memory");
+ return oldbit;
+}
+
+/**
+ * test_and_change_bit - Change a bit and return its new value
+ * @nr: Bit to set
+ * @addr: Address to count from
+ *
+ * This operation is atomic and cannot be reordered.
+ * It also implies a memory barrier.
+ */
+static __inline__ int test_and_change_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__ __volatile__( LOCK_PREFIX
+ "btcl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"Ir" (nr) : "memory");
+ return oldbit;
+}
+
+#if 0 /* Fool kernel-doc since it doesn't do macros yet */
+/**
+ * test_bit - Determine whether a bit is set
+ * @nr: bit number to test
+ * @addr: Address to start counting from
+ */
+static int test_bit(int nr, const volatile void * addr);
+#endif
+
+static __inline__ int constant_test_bit(int nr, const volatile void * addr)
+{
+ return ((1UL << (nr & 31)) & (((const volatile unsigned int *) addr)[nr >> 5])) != 0;
+}
+
+static __inline__ int variable_test_bit(int nr, volatile void * addr)
+{
+ int oldbit;
+
+ __asm__ __volatile__(
+ "btl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit)
+ :"m" (ADDR),"Ir" (nr));
+ return oldbit;
+}
+
+#define test_bit(nr,addr) \
+(__builtin_constant_p(nr) ? \
+ constant_test_bit((nr),(addr)) : \
+ variable_test_bit((nr),(addr)))
+
+/**
+ * find_first_zero_bit - find the first zero bit in a memory region
+ * @addr: The address to start the search at
+ * @size: The maximum size to search
+ *
+ * Returns the bit-number of the first zero bit, not the number of the byte
+ * containing a bit.
+ */
+static __inline__ int find_first_zero_bit(void * addr, unsigned size)
+{
+ int d0, d1, d2;
+ int res;
+
+ if (!size)
+ return 0;
+ /* This looks at memory. Mark it volatile to tell gcc not to move it around */
+ __asm__ __volatile__(
+ "movl $-1,%%eax\n\t"
+ "xorl %%edx,%%edx\n\t"
+ "repe; scasl\n\t"
+ "je 1f\n\t"
+ "xorl -4(%%edi),%%eax\n\t"
+ "subl $4,%%edi\n\t"
+ "bsfl %%eax,%%edx\n"
+ "1:\tsubl %%ebx,%%edi\n\t"
+ "shll $3,%%edi\n\t"
+ "addl %%edi,%%edx"
+ :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2)
+ :"1" ((size + 31) >> 5), "2" (addr), "b" (addr));
+ return res;
+}
+
+/**
+ * find_next_zero_bit - find the first zero bit in a memory region
+ * @addr: The address to base the search on
+ * @offset: The bitnumber to start searching at
+ * @size: The maximum size to search
+ */
+static __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+{
+ unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
+ int set = 0, bit = offset & 31, res;
+
+ if (bit) {
+ /*
+ * Look for zero in first byte
+ */
+ __asm__("bsfl %1,%0\n\t"
+ "jne 1f\n\t"
+ "movl $32, %0\n"
+ "1:"
+ : "=r" (set)
+ : "r" (~(*p >> bit)));
+ if (set < (32 - bit))
+ return set + offset;
+ set = 32 - bit;
+ p++;
+ }
+ /*
+ * No zero yet, search remaining full bytes for a zero
+ */
+ res = find_first_zero_bit (p, size - 32 * (p - (unsigned long *) addr));
+ return (offset + set + res);
+}
+
+/**
+ * ffz - find first zero in word.
+ * @word: The word to search
+ *
+ * Undefined if no zero exists, so code should check against ~0UL first.
+ */
+static __inline__ unsigned long ffz(unsigned long word)
+{
+ __asm__("bsfl %1,%0"
+ :"=r" (word)
+ :"r" (~word));
+ return word;
+}
+
+#ifdef __KERNEL__
+
+/**
+ * ffs - find first bit set
+ * @x: the word to search
+ *
+ * This is defined the same way as
+ * the libc and compiler builtin ffs routines, therefore
+ * differs in spirit from the above ffz (man ffs).
+ */
+static __inline__ int ffs(int x)
+{
+ int r;
+
+ __asm__("bsfl %1,%0\n\t"
+ "jnz 1f\n\t"
+ "movl $-1,%0\n"
+ "1:" : "=r" (r) : "rm" (x));
+ return r+1;
+}
+
+/**
+ * hweightN - returns the hamming weight of a N-bit word
+ * @x: the word to weigh
+ *
+ * The Hamming Weight of a number is the total number of bits set in it.
+ */
+
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
+#endif /* __KERNEL__ */
+
+#ifdef __KERNEL__
+
+#define ext2_set_bit __test_and_set_bit
+#define ext2_clear_bit __test_and_clear_bit
+#define ext2_test_bit test_bit
+#define ext2_find_first_zero_bit find_first_zero_bit
+#define ext2_find_next_zero_bit find_next_zero_bit
+
+/* Bitmap functions for the minix filesystem. */
+#define minix_test_and_set_bit(nr,addr) __test_and_set_bit(nr,addr)
+#define minix_set_bit(nr,addr) __set_bit(nr,addr)
+#define minix_test_and_clear_bit(nr,addr) __test_and_clear_bit(nr,addr)
+#define minix_test_bit(nr,addr) test_bit(nr,addr)
+#define minix_find_first_zero_bit(addr,size) find_first_zero_bit(addr,size)
+
+#endif /* __KERNEL__ */
+
+#endif /* _I386_BITOPS_H */
-#ifndef _LINUX_BITOPS_H\r
-#define _LINUX_BITOPS_H\r
-\r
-\r
-/*\r
- * ffs: find first bit set. This is defined the same way as\r
- * the libc and compiler builtin ffs routines, therefore\r
- * differs in spirit from the above ffz (man ffs).\r
- */\r
-\r
-static inline int generic_ffs(int x)\r
-{\r
- int r = 1;\r
-\r
- if (!x)\r
- return 0;\r
- if (!(x & 0xffff)) {\r
- x >>= 16;\r
- r += 16;\r
- }\r
- if (!(x & 0xff)) {\r
- x >>= 8;\r
- r += 8;\r
- }\r
- if (!(x & 0xf)) {\r
- x >>= 4;\r
- r += 4;\r
- }\r
- if (!(x & 3)) {\r
- x >>= 2;\r
- r += 2;\r
- }\r
- if (!(x & 1)) {\r
- x >>= 1;\r
- r += 1;\r
- }\r
- return r;\r
-}\r
-\r
-/*\r
- * hweightN: returns the hamming weight (i.e. the number\r
- * of bits set) of a N-bit word\r
- */\r
-\r
-static inline unsigned int generic_hweight32(unsigned int w)\r
-{\r
- unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);\r
- res = (res & 0x33333333) + ((res >> 2) & 0x33333333);\r
- res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);\r
- res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);\r
- return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);\r
-}\r
-\r
-static inline unsigned int generic_hweight16(unsigned int w)\r
-{\r
- unsigned int res = (w & 0x5555) + ((w >> 1) & 0x5555);\r
- res = (res & 0x3333) + ((res >> 2) & 0x3333);\r
- res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);\r
- return (res & 0x00FF) + ((res >> 8) & 0x00FF);\r
-}\r
-\r
-static inline unsigned int generic_hweight8(unsigned int w)\r
-{\r
- unsigned int res = (w & 0x55) + ((w >> 1) & 0x55);\r
- res = (res & 0x33) + ((res >> 2) & 0x33);\r
- return (res & 0x0F) + ((res >> 4) & 0x0F);\r
-}\r
-\r
-#include "asm/bitops.h"\r
-\r
-\r
-#endif\r
+#ifndef _LINUX_BITOPS_H
+#define _LINUX_BITOPS_H
+
+
+/*
+ * ffs: find first bit set. This is defined the same way as
+ * the libc and compiler builtin ffs routines, therefore
+ * differs in spirit from the above ffz (man ffs).
+ */
+
+static inline int generic_ffs(int x)
+{
+ int r = 1;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff)) {
+ x >>= 16;
+ r += 16;
+ }
+ if (!(x & 0xff)) {
+ x >>= 8;
+ r += 8;
+ }
+ if (!(x & 0xf)) {
+ x >>= 4;
+ r += 4;
+ }
+ if (!(x & 3)) {
+ x >>= 2;
+ r += 2;
+ }
+ if (!(x & 1)) {
+ x >>= 1;
+ r += 1;
+ }
+ return r;
+}
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+static inline unsigned int generic_hweight32(unsigned int w)
+{
+ unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+ res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+ return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+static inline unsigned int generic_hweight16(unsigned int w)
+{
+ unsigned int res = (w & 0x5555) + ((w >> 1) & 0x5555);
+ res = (res & 0x3333) + ((res >> 2) & 0x3333);
+ res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
+ return (res & 0x00FF) + ((res >> 8) & 0x00FF);
+}
+
+static inline unsigned int generic_hweight8(unsigned int w)
+{
+ unsigned int res = (w & 0x55) + ((w >> 1) & 0x55);
+ res = (res & 0x33) + ((res >> 2) & 0x33);
+ return (res & 0x0F) + ((res >> 4) & 0x0F);
+}
+
+#include "asm/bitops.h"
+
+
+#endif
-#ifndef _Boot_H_\r
-#define _Boot_H_\r
-\r
-#include "config.h"\r
-\r
-/***************************************************************************\r
- Includes used by XBox boot code\r
- ***************************************************************************/\r
-/***************************************************************************\r
- * *\r
- * This program is free software; you can redistribute it and/or modify *\r
- * it under the terms of the GNU General Public License as published by *\r
- * the Free Software Foundation; either version 2 of the License, or *\r
- * (at your option) any later version. *\r
- * *\r
- ***************************************************************************/\r
-\r
-/////////////////////////////////\r
-// configuration\r
-\r
-#include "consts.h"\r
-#include "stdint.h"\r
-#include "cromwell_types.h"\r
-\r
-\r
-unsigned int cromwell_config;\r
-unsigned int cromwell_retryload;\r
-unsigned int cromwell_loadbank;\r
-unsigned int cromwell_Biostype;\r
-\r
-unsigned int xbox_ram;\r
-\r
-#define XROMWELL 0\r
-#define CROMWELL 1\r
-\r
-#define ICON_WIDTH 64\r
-#define ICON_HEIGHT 64\r
-/*\r
-static double min (double a, double b)\r
-{\r
- if (a < b) return a; else return b;\r
-}\r
-\r
-static inline double max (double a, double b)\r
-{\r
- if (a > b) return a; else return b;\r
-}\r
-*/\r
-//#include "iso_fs.h"\r
-//#include "BootVideo.h"\r
-\r
-//#define ASSERT(exp) { if(!(exp)) { bprintf("Assert failed file " __FILE__ " line %d\n", __LINE__); } }\r
-\r
-#if 0\r
-extern volatile CURRENT_VIDEO_MODE_DETAILS vmode;\r
-unsigned int video_encoder;\r
-\r
-volatile u32 VIDEO_CURSOR_POSX;\r
-volatile u32 VIDEO_CURSOR_POSY;\r
-volatile u32 VIDEO_ATTR;\r
-volatile u32 VIDEO_LUMASCALING;\r
-volatile u32 VIDEO_RSCALING;\r
-volatile u32 VIDEO_BSCALING;\r
-volatile u32 BIOS_TICK_COUNT;\r
-volatile u32 VIDEO_VSYNC_POSITION;\r
-volatile u32 VIDEO_VSYNC_DIR;\r
-volatile u32 DVD_TRAY_STATE;\r
-\r
-u8 VIDEO_AV_MODE ;\r
-\r
-#define DVD_CLOSED 0\r
-#define DVD_CLOSING 1\r
-#define DVD_OPEN 2\r
-#define DVD_OPENING 3\r
-\r
-/////////////////////////////////\r
-// Superfunky i386 internal structures\r
-\r
-typedef struct gdt_t {\r
- unsigned short m_wSize __attribute__ ((packed));\r
- unsigned long m_dwBase32 __attribute__ ((packed));\r
- unsigned short m_wDummy __attribute__ ((packed));\r
-} ts_descriptor_pointer;\r
-\r
-typedef struct { // inside an 8-byte protected mode interrupt vector\r
- u16 m_wHandlerHighAddressLow16;\r
- u16 m_wSelector;\r
- u16 m_wType;\r
- u16 m_wHandlerLinearAddressHigh16;\r
-} ts_pm_interrupt;\r
-\r
-typedef enum {\r
- EDT_UNKNOWN= 0,\r
- EDT_XBOXFS\r
-} enumDriveType;\r
-\r
-typedef struct tsHarddiskInfo { // this is the retained knowledge about an IDE device after init\r
- unsigned short m_fwPortBase;\r
- unsigned short m_wCountHeads;\r
- unsigned short m_wCountCylinders;\r
- unsigned short m_wCountSectorsPerTrack;\r
- unsigned long m_dwCountSectorsTotal; /* total */\r
- unsigned char m_bLbaMode; /* am i lba (0x40) or chs (0x00) */\r
- unsigned char m_szIdentityModelNumber[40];\r
- unsigned char term_space_1[2];\r
- unsigned char m_szSerial[20];\r
- unsigned char term_space_2[2];\r
- char m_szFirmware[8];\r
- unsigned char term_space_3[2];\r
- unsigned char m_fDriveExists;\r
- unsigned char m_fAtapi; // true if a CDROM, etc\r
- enumDriveType m_enumDriveType;\r
- unsigned char m_bCableConductors; // valid for device 0 if present\r
- unsigned short m_wAtaRevisionSupported;\r
- unsigned char s_length;\r
- unsigned char m_length;\r
- unsigned char m_fHasMbr;\r
- unsigned short m_securitySettings; //This contains the contents of the ATA security regs\r
-} tsHarddiskInfo;\r
-\r
-/////////////////////////////////\r
-// LED-flashing codes\r
-// or these together as argument to I2cSetFrontpanelLed\r
-\r
-enum {\r
- I2C_LED_RED0 = 0x80,\r
- I2C_LED_RED1 = 0x40,\r
- I2C_LED_RED2 = 0x20,\r
- I2C_LED_RED3 = 0x10,\r
- I2C_LED_GREEN0 = 0x08,\r
- I2C_LED_GREEN1 = 0x04,\r
- I2C_LED_GREEN2 = 0x02,\r
- I2C_LED_GREEN3 = 0x01\r
-};\r
-\r
-///////////////////////////////\r
-/* BIOS-wide error codes all have b31 set */\r
-\r
-enum {\r
- ERR_SUCCESS = 0, // completed without error\r
-\r
- ERR_I2C_ERROR_TIMEOUT = 0x80000001, // I2C action failed because it did not complete in a reasonable time\r
- ERR_I2C_ERROR_BUS = 0x80000002, // I2C action failed due to non retryable bus error\r
-\r
- ERR_BOOT_PIC_ALG_BROKEN = 0x80000101 // PIC algorithm did not pass its self-test\r
-};\r
-\r
-/////////////////////////////////\r
-// some Boot API prototypes\r
-\r
-//////// BootPerformPicChallengeResponseAction.c\r
-\r
-/* ---------------------------- IO primitives -----------------------------------------------------------\r
-*/\r
-\r
-static __inline void IoOutputByte(u16 wAds, u8 bValue) {\r
-// __asm__ (" out %%al,%%dx" : : "edx" (dwAds), "al" (bValue) );\r
- __asm__ __volatile__ ("outb %b0,%w1": :"a" (bValue), "Nd" (wAds));\r
-}\r
-\r
-static __inline void IoOutputWord(u16 wAds, u16 wValue) {\r
-// __asm__ (" out %%ax,%%dx " : : "edx" (dwAds), "ax" (wValue) );\r
- __asm__ __volatile__ ("outw %0,%w1": :"a" (wValue), "Nd" (wAds));\r
- }\r
-\r
-static __inline void IoOutputDword(u16 wAds, u32 dwValue) {\r
-// __asm__ (" out %%eax,%%dx " : : "edx" (dwAds), "ax" (wValue) );\r
- __asm__ __volatile__ ("outl %0,%w1": :"a" (dwValue), "Nd" (wAds));\r
-}\r
-\r
-\r
-static __inline u8 IoInputByte(u16 wAds) {\r
- unsigned char _v;\r
-\r
- __asm__ __volatile__ ("inb %w1,%0":"=a" (_v):"Nd" (wAds));\r
- return _v;\r
-}\r
-\r
-static __inline u16 IoInputWord(u16 wAds) {\r
- u16 _v;\r
-\r
- __asm__ __volatile__ ("inw %w1,%0":"=a" (_v):"Nd" (wAds));\r
- return _v;\r
-}\r
-\r
-static __inline u32 IoInputDword(u16 wAds) {\r
- u32 _v;\r
-\r
- __asm__ __volatile__ ("inl %w1,%0":"=a" (_v):"Nd" (wAds));\r
- return _v;\r
-}\r
-\r
-#define rdmsr(msr,val1,val2) \\r
- __asm__ __volatile__("rdmsr" \\r
- : "=a" (val1), "=d" (val2) \\r
- : "c" (msr))\r
-\r
-#define wrmsr(msr,val1,val2) \\r
- __asm__ __volatile__("wrmsr" \\r
- : /* no outputs */ \\r
- : "c" (msr), "a" (val1), "d" (val2))\r
-\r
-\r
-void BootPciInterruptEnable(void);\r
-\r
- // boot process\r
-int BootPerformPicChallengeResponseAction(void);\r
- // LED control (see associated enum above)\r
-int I2cSetFrontpanelLed(u8 b);\r
-\r
-#define bprintf(...)\r
-\r
-#if PRINT_TRACE\r
-#define TRACE bprintf(__FILE__ " :%d\n\r",__LINE__);\r
-#else\r
-#define TRACE\r
-#endif\r
-\r
-typedef struct _LIST_ENTRY {\r
- struct _LIST_ENTRY *m_plistentryNext;\r
- struct _LIST_ENTRY *m_plistentryPrevious;\r
-} LIST_ENTRY;\r
-\r
-void ListEntryInsertAfterCurrent(LIST_ENTRY *plistentryCurrent, LIST_ENTRY *plistentryNew);\r
-void ListEntryRemove(LIST_ENTRY *plistentryCurrent);\r
-\r
-////////// BootPerformXCodeActions.c\r
-\r
-int BootPerformXCodeActions(void);\r
-\r
-#include "BootEEPROM.h"\r
-#include "BootParser.h"\r
-\r
-////////// BootStartBios.c\r
-\r
-void StartBios(CONFIGENTRY *config,int nActivePartition, int nFATXPresent,int bootfrom);\r
-int BootMenu(CONFIGENTRY *config,int nDrive,int nActivePartition, int nFATXPresent);\r
-\r
-////////// BootResetActions.c\r
-void ClearIDT (void);\r
-void BootResetAction(void);\r
-void BootCpuCache(bool fEnable) ;\r
-int printk(const char *szFormat, ...);\r
-void BiosCmosWrite(u8 bAds, u8 bData);\r
-u8 BiosCmosRead(u8 bAds);\r
-\r
-\r
-///////// BootPciPeripheralInitialization.c\r
-void BootPciPeripheralInitialization(void);\r
-void BootAGPBUSInitialization(void);\r
-void BootDetectMemorySize(void);\r
-extern void ReadPCIByte(unsigned int bus, unsigned int dev, unsigned intfunc, unsigned int reg_off, unsigned char *pbyteval);\r
-extern void WritePCIByte(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned char byteval);\r
-extern void ReadPCIDword(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned int *pdwordval);\r
-extern void WritePCIDword(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned int dwordval);\r
-extern void ReadPCIBlock(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned char *buf, unsigned int nbytes);\r
-extern void WritePCIBlock(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned char *buf, unsigned int nbytes);\r
-\r
-void PciWriteByte (unsigned int bus, unsigned int dev, unsigned int func,\r
- unsigned int reg_off, unsigned char byteval);\r
-u8 PciReadByte(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off);\r
-u32 PciWriteDword(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, u32 dw);\r
-u32 PciReadDword(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off);\r
-\r
-///////// BootPerformPicChallengeResponseAction.c\r
-\r
-int I2CTransmitWord(u8 bPicAddressI2cFormat, u16 wDataToWrite);\r
-int I2CTransmitByteGetReturn(u8 bPicAddressI2cFormat, u8 bDataToWrite);\r
-bool I2CGetTemperature(int *, int *);\r
-void I2CModifyBits(u8 bAds, u8 bReg, u8 bData, u8 bMask);\r
-\r
-///////// BootIde.c\r
-\r
-extern tsHarddiskInfo tsaHarddiskInfo[]; // static struct stores data about attached drives\r
-int BootIdeInit(void);\r
-int BootIdeReadSector(int nDriveIndex, void * pbBuffer, unsigned int block, int byte_offset, int n_bytes);\r
-int BootIdeBootSectorHddOrElTorito(int nDriveIndex, u8 * pbaResult);\r
-int BootIdeAtapiAdditionalSenseCode(int nDrive, u8 * pba, int nLengthMaxReturn);\r
-int BootIdeSetTransferMode(int nIndexDrive, int nMode);\r
-int BootIdeWaitNotBusy(unsigned uIoBase);\r
-bool BootIdeAtapiReportFriendlyError(int nDriveIndex, char * szErrorReturn, int nMaxLengthError);\r
-void BootIdeAtapiPrintkFriendlyError(int nDriveIndex);\r
-\r
-///////// BootUSB.c\r
-\r
-void BootStopUSB(void);\r
-void BootStartUSB(void);\r
-void USBGetEvents(void);\r
-\r
-#include "xpad.h"\r
-\r
-extern struct xpad_data XPAD_current[4];\r
-extern struct xpad_data XPAD_last[4];\r
-\r
-extern void wait_ms(u32 ticks);\r
-extern void wait_us(u32 ticks);\r
-extern void wait_smalldelay(void);\r
-\r
-\r
-void * memcpy(void *dest, const void *src, size_t size);\r
-void * memset(void *dest, int data, size_t size);\r
-int memcmp(const void *buffer1, const void *buffer2, size_t num);\r
-int _strncmp(const char *sz1, const char *sz2, int nMax);\r
-char * strcpy(char *sz, const char *szc);\r
-char * _strncpy (char * dest, const char * src, size_t n);\r
-void chrreplace(char *string, char search, char ch);\r
-\r
-#define printf printk\r
-#define sleep wait_ms\r
-int tolower(int ch);\r
-int isspace (int c);\r
-\r
-void MemoryManagementInitialization(void * pvStartAddress, u32 dwTotalMemoryAllocLength);\r
-void * malloc(size_t size);\r
-void free(void *);\r
-\r
-extern volatile int nCountI2cinterrupts, nCountUnusedInterrupts, nCountUnusedInterruptsPic2, nCountInterruptsSmc, nCountInterruptsIde;\r
-extern volatile bool fSeenPowerdown;\r
-typedef enum {\r
- ETS_OPEN_OR_OPENING=0,\r
- ETS_CLOSING,\r
- ETS_CLOSED\r
-} TRAY_STATE;\r
-extern volatile TRAY_STATE traystate;\r
-\r
-\r
-extern void BootInterruptsWriteIdt(void);\r
-#endif\r
-int copy_swap_trim(unsigned char *dst, unsigned char *src, int len);\r
-void HMAC_SHA1( unsigned char *result,\r
- unsigned char *key, int key_length,\r
- unsigned char *text1, int text1_length,\r
- unsigned char *text2, int text2_length );\r
-\r
-char *strrchr0(char *string, char ch);\r
-\r
-#endif // _Boot_H_\r
+#ifndef _Boot_H_
+#define _Boot_H_
+
+#include "config.h"
+
+/***************************************************************************
+ Includes used by XBox boot code
+ ***************************************************************************/
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+/////////////////////////////////
+// configuration
+
+#include "consts.h"
+#include "stdint.h"
+#include "cromwell_types.h"
+
+
+unsigned int cromwell_config;
+unsigned int cromwell_retryload;
+unsigned int cromwell_loadbank;
+unsigned int cromwell_Biostype;
+
+unsigned int xbox_ram;
+
+#define XROMWELL 0
+#define CROMWELL 1
+
+#define ICON_WIDTH 64
+#define ICON_HEIGHT 64
+/*
+static double min (double a, double b)
+{
+ if (a < b) return a; else return b;
+}
+
+static inline double max (double a, double b)
+{
+ if (a > b) return a; else return b;
+}
+*/
+//#include "iso_fs.h"
+//#include "BootVideo.h"
+
+//#define ASSERT(exp) { if(!(exp)) { bprintf("Assert failed file " __FILE__ " line %d\n", __LINE__); } }
+
+#if 0
+extern volatile CURRENT_VIDEO_MODE_DETAILS vmode;
+unsigned int video_encoder;
+
+volatile u32 VIDEO_CURSOR_POSX;
+volatile u32 VIDEO_CURSOR_POSY;
+volatile u32 VIDEO_ATTR;
+volatile u32 VIDEO_LUMASCALING;
+volatile u32 VIDEO_RSCALING;
+volatile u32 VIDEO_BSCALING;
+volatile u32 BIOS_TICK_COUNT;
+volatile u32 VIDEO_VSYNC_POSITION;
+volatile u32 VIDEO_VSYNC_DIR;
+volatile u32 DVD_TRAY_STATE;
+
+u8 VIDEO_AV_MODE ;
+
+#define DVD_CLOSED 0
+#define DVD_CLOSING 1
+#define DVD_OPEN 2
+#define DVD_OPENING 3
+
+/////////////////////////////////
+// Superfunky i386 internal structures
+
+typedef struct gdt_t {
+ unsigned short m_wSize __attribute__ ((packed));
+ unsigned long m_dwBase32 __attribute__ ((packed));
+ unsigned short m_wDummy __attribute__ ((packed));
+} ts_descriptor_pointer;
+
+typedef struct { // inside an 8-byte protected mode interrupt vector
+ u16 m_wHandlerHighAddressLow16;
+ u16 m_wSelector;
+ u16 m_wType;
+ u16 m_wHandlerLinearAddressHigh16;
+} ts_pm_interrupt;
+
+typedef enum {
+ EDT_UNKNOWN= 0,
+ EDT_XBOXFS
+} enumDriveType;
+
+typedef struct tsHarddiskInfo { // this is the retained knowledge about an IDE device after init
+ unsigned short m_fwPortBase;
+ unsigned short m_wCountHeads;
+ unsigned short m_wCountCylinders;
+ unsigned short m_wCountSectorsPerTrack;
+ unsigned long m_dwCountSectorsTotal; /* total */
+ unsigned char m_bLbaMode; /* am i lba (0x40) or chs (0x00) */
+ unsigned char m_szIdentityModelNumber[40];
+ unsigned char term_space_1[2];
+ unsigned char m_szSerial[20];
+ unsigned char term_space_2[2];
+ char m_szFirmware[8];
+ unsigned char term_space_3[2];
+ unsigned char m_fDriveExists;
+ unsigned char m_fAtapi; // true if a CDROM, etc
+ enumDriveType m_enumDriveType;
+ unsigned char m_bCableConductors; // valid for device 0 if present
+ unsigned short m_wAtaRevisionSupported;
+ unsigned char s_length;
+ unsigned char m_length;
+ unsigned char m_fHasMbr;
+ unsigned short m_securitySettings; //This contains the contents of the ATA security regs
+} tsHarddiskInfo;
+
+/////////////////////////////////
+// LED-flashing codes
+// or these together as argument to I2cSetFrontpanelLed
+
+enum {
+ I2C_LED_RED0 = 0x80,
+ I2C_LED_RED1 = 0x40,
+ I2C_LED_RED2 = 0x20,
+ I2C_LED_RED3 = 0x10,
+ I2C_LED_GREEN0 = 0x08,
+ I2C_LED_GREEN1 = 0x04,
+ I2C_LED_GREEN2 = 0x02,
+ I2C_LED_GREEN3 = 0x01
+};
+
+///////////////////////////////
+/* BIOS-wide error codes all have b31 set */
+
+enum {
+ ERR_SUCCESS = 0, // completed without error
+
+ ERR_I2C_ERROR_TIMEOUT = 0x80000001, // I2C action failed because it did not complete in a reasonable time
+ ERR_I2C_ERROR_BUS = 0x80000002, // I2C action failed due to non retryable bus error
+
+ ERR_BOOT_PIC_ALG_BROKEN = 0x80000101 // PIC algorithm did not pass its self-test
+};
+
+/////////////////////////////////
+// some Boot API prototypes
+
+//////// BootPerformPicChallengeResponseAction.c
+
+/* ---------------------------- IO primitives -----------------------------------------------------------
+*/
+
+static __inline void IoOutputByte(u16 wAds, u8 bValue) {
+// __asm__ (" out %%al,%%dx" : : "edx" (dwAds), "al" (bValue) );
+ __asm__ __volatile__ ("outb %b0,%w1": :"a" (bValue), "Nd" (wAds));
+}
+
+static __inline void IoOutputWord(u16 wAds, u16 wValue) {
+// __asm__ (" out %%ax,%%dx " : : "edx" (dwAds), "ax" (wValue) );
+ __asm__ __volatile__ ("outw %0,%w1": :"a" (wValue), "Nd" (wAds));
+ }
+
+static __inline void IoOutputDword(u16 wAds, u32 dwValue) {
+// __asm__ (" out %%eax,%%dx " : : "edx" (dwAds), "ax" (wValue) );
+ __asm__ __volatile__ ("outl %0,%w1": :"a" (dwValue), "Nd" (wAds));
+}
+
+
+static __inline u8 IoInputByte(u16 wAds) {
+ unsigned char _v;
+
+ __asm__ __volatile__ ("inb %w1,%0":"=a" (_v):"Nd" (wAds));
+ return _v;
+}
+
+static __inline u16 IoInputWord(u16 wAds) {
+ u16 _v;
+
+ __asm__ __volatile__ ("inw %w1,%0":"=a" (_v):"Nd" (wAds));
+ return _v;
+}
+
+static __inline u32 IoInputDword(u16 wAds) {
+ u32 _v;
+
+ __asm__ __volatile__ ("inl %w1,%0":"=a" (_v):"Nd" (wAds));
+ return _v;
+}
+
+#define rdmsr(msr,val1,val2) \
+ __asm__ __volatile__("rdmsr" \
+ : "=a" (val1), "=d" (val2) \
+ : "c" (msr))
+
+#define wrmsr(msr,val1,val2) \
+ __asm__ __volatile__("wrmsr" \
+ : /* no outputs */ \
+ : "c" (msr), "a" (val1), "d" (val2))
+
+
+void BootPciInterruptEnable(void);
+
+ // boot process
+int BootPerformPicChallengeResponseAction(void);
+ // LED control (see associated enum above)
+int I2cSetFrontpanelLed(u8 b);
+
+#define bprintf(...)
+
+#if PRINT_TRACE
+#define TRACE bprintf(__FILE__ " :%d\n\r",__LINE__);
+#else
+#define TRACE
+#endif
+
+typedef struct _LIST_ENTRY {
+ struct _LIST_ENTRY *m_plistentryNext;
+ struct _LIST_ENTRY *m_plistentryPrevious;
+} LIST_ENTRY;
+
+void ListEntryInsertAfterCurrent(LIST_ENTRY *plistentryCurrent, LIST_ENTRY *plistentryNew);
+void ListEntryRemove(LIST_ENTRY *plistentryCurrent);
+
+////////// BootPerformXCodeActions.c
+
+int BootPerformXCodeActions(void);
+
+#include "BootEEPROM.h"
+#include "BootParser.h"
+
+////////// BootStartBios.c
+
+void StartBios(CONFIGENTRY *config,int nActivePartition, int nFATXPresent,int bootfrom);
+int BootMenu(CONFIGENTRY *config,int nDrive,int nActivePartition, int nFATXPresent);
+
+////////// BootResetActions.c
+void ClearIDT (void);
+void BootResetAction(void);
+void BootCpuCache(bool fEnable) ;
+int printk(const char *szFormat, ...);
+void BiosCmosWrite(u8 bAds, u8 bData);
+u8 BiosCmosRead(u8 bAds);
+
+
+///////// BootPciPeripheralInitialization.c
+void BootPciPeripheralInitialization(void);
+void BootAGPBUSInitialization(void);
+void BootDetectMemorySize(void);
+extern void ReadPCIByte(unsigned int bus, unsigned int dev, unsigned intfunc, unsigned int reg_off, unsigned char *pbyteval);
+extern void WritePCIByte(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned char byteval);
+extern void ReadPCIDword(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned int *pdwordval);
+extern void WritePCIDword(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned int dwordval);
+extern void ReadPCIBlock(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned char *buf, unsigned int nbytes);
+extern void WritePCIBlock(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, unsigned char *buf, unsigned int nbytes);
+
+void PciWriteByte (unsigned int bus, unsigned int dev, unsigned int func,
+ unsigned int reg_off, unsigned char byteval);
+u8 PciReadByte(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off);
+u32 PciWriteDword(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off, u32 dw);
+u32 PciReadDword(unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg_off);
+
+///////// BootPerformPicChallengeResponseAction.c
+
+int I2CTransmitWord(u8 bPicAddressI2cFormat, u16 wDataToWrite);
+int I2CTransmitByteGetReturn(u8 bPicAddressI2cFormat, u8 bDataToWrite);
+bool I2CGetTemperature(int *, int *);
+void I2CModifyBits(u8 bAds, u8 bReg, u8 bData, u8 bMask);
+
+///////// BootIde.c
+
+extern tsHarddiskInfo tsaHarddiskInfo[]; // static struct stores data about attached drives
+int BootIdeInit(void);
+int BootIdeReadSector(int nDriveIndex, void * pbBuffer, unsigned int block, int byte_offset, int n_bytes);
+int BootIdeBootSectorHddOrElTorito(int nDriveIndex, u8 * pbaResult);
+int BootIdeAtapiAdditionalSenseCode(int nDrive, u8 * pba, int nLengthMaxReturn);
+int BootIdeSetTransferMode(int nIndexDrive, int nMode);
+int BootIdeWaitNotBusy(unsigned uIoBase);
+bool BootIdeAtapiReportFriendlyError(int nDriveIndex, char * szErrorReturn, int nMaxLengthError);
+void BootIdeAtapiPrintkFriendlyError(int nDriveIndex);
+
+///////// BootUSB.c
+
+void BootStopUSB(void);
+void BootStartUSB(void);
+void USBGetEvents(void);
+
+#include "xpad.h"
+
+extern struct xpad_data XPAD_current[4];
+extern struct xpad_data XPAD_last[4];
+
+extern void wait_ms(u32 ticks);
+extern void wait_us(u32 ticks);
+extern void wait_smalldelay(void);
+
+
+void * memcpy(void *dest, const void *src, size_t size);
+void * memset(void *dest, int data, size_t size);
+int memcmp(const void *buffer1, const void *buffer2, size_t num);
+int _strncmp(const char *sz1, const char *sz2, int nMax);
+char * strcpy(char *sz, const char *szc);
+char * _strncpy (char * dest, const char * src, size_t n);
+void chrreplace(char *string, char search, char ch);
+
+#define printf printk
+#define sleep wait_ms
+int tolower(int ch);
+int isspace (int c);
+
+void MemoryManagementInitialization(void * pvStartAddress, u32 dwTotalMemoryAllocLength);
+void * malloc(size_t size);
+void free(void *);
+
+extern volatile int nCountI2cinterrupts, nCountUnusedInterrupts, nCountUnusedInterruptsPic2, nCountInterruptsSmc, nCountInterruptsIde;
+extern volatile bool fSeenPowerdown;
+typedef enum {
+ ETS_OPEN_OR_OPENING=0,
+ ETS_CLOSING,
+ ETS_CLOSED
+} TRAY_STATE;
+extern volatile TRAY_STATE traystate;
+
+
+extern void BootInterruptsWriteIdt(void);
+#endif
+int copy_swap_trim(unsigned char *dst, unsigned char *src, int len);
+void HMAC_SHA1( unsigned char *result,
+ unsigned char *key, int key_length,
+ unsigned char *text1, int text1_length,
+ unsigned char *text2, int text2_length );
+
+char *strrchr0(char *string, char ch);
+
+#endif // _Boot_H_
-#ifndef _Consts_H_\r
-#define _Consts_H_\r
-\r
-/*\r
- *\r
- * includes for startup code in a form usable by the .S files\r
- *\r
- */\r
-\r
- /***************************************************************************\r
- * *\r
- * This program is free software; you can redistribute it and/or modify *\r
- * it under the terms of the GNU General Public License as published by *\r
- * the Free Software Foundation; either version 2 of the License, or *\r
- * (at your option) any later version. *\r
- * *\r
- ***************************************************************************/\r
-\r
-#define PCI_CFG_ADDR 0x0CF8\r
-#define PCI_CFG_DATA 0x0CFC\r
-\r
-\r
-#define I2C_IO_BASE 0xc000\r
-\r
-#define BUS_0 0\r
-#define BUS_1 1\r
-\r
-#define DEV_0 0\r
-#define DEV_1 1\r
-#define DEV_2 2\r
-#define DEV_3 3\r
-#define DEV_4 4\r
-#define DEV_5 5\r
-#define DEV_6 6\r
-#define DEV_7 7\r
-#define DEV_8 8\r
-#define DEV_9 9\r
-#define DEV_a 0xa\r
-#define DEV_b 0xb\r
-#define DEV_c 0xc\r
-#define DEV_d 0xd\r
-#define DEV_e 0xe\r
-#define DEV_f 0xf\r
-#define DEV_10 0x10\r
-#define DEV_11 0x11\r
-#define DEV_12 0x12\r
-#define DEV_13 0x13\r
-#define DEV_14 0x14\r
-#define DEV_15 0x15\r
-#define DEV_16 0x16\r
-#define DEV_17 0x17\r
-#define DEV_18 0x18\r
-#define DEV_19 0x19\r
-#define DEV_1a 0x1a\r
-#define DEV_1b 0x1b\r
-#define DEV_1c 0x1c\r
-#define DEV_1d 0x1d\r
-#define DEV_1e 0x1e\r
-#define DEV_1f 0x1f\r
-\r
-#define FUNC_0 0\r
-/*\r
-#define boot_post_macro(value) \\r
- movb $(value), %al ;\\r
- outb %al, $0x80 \r
-*/\r
-\r
-#endif // _Consts_H_\r
-\r
-\r
+#ifndef _Consts_H_
+#define _Consts_H_
+
+/*
+ *
+ * includes for startup code in a form usable by the .S files
+ *
+ */
+
+ /***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#define PCI_CFG_ADDR 0x0CF8
+#define PCI_CFG_DATA 0x0CFC
+
+
+#define I2C_IO_BASE 0xc000
+
+#define BUS_0 0
+#define BUS_1 1
+
+#define DEV_0 0
+#define DEV_1 1
+#define DEV_2 2
+#define DEV_3 3
+#define DEV_4 4
+#define DEV_5 5
+#define DEV_6 6
+#define DEV_7 7
+#define DEV_8 8
+#define DEV_9 9
+#define DEV_a 0xa
+#define DEV_b 0xb
+#define DEV_c 0xc
+#define DEV_d 0xd
+#define DEV_e 0xe
+#define DEV_f 0xf
+#define DEV_10 0x10
+#define DEV_11 0x11
+#define DEV_12 0x12
+#define DEV_13 0x13
+#define DEV_14 0x14
+#define DEV_15 0x15
+#define DEV_16 0x16
+#define DEV_17 0x17
+#define DEV_18 0x18
+#define DEV_19 0x19
+#define DEV_1a 0x1a
+#define DEV_1b 0x1b
+#define DEV_1c 0x1c
+#define DEV_1d 0x1d
+#define DEV_1e 0x1e
+#define DEV_1f 0x1f
+
+#define FUNC_0 0
+/*
+#define boot_post_macro(value) \
+ movb $(value), %al ;\
+ outb %al, $0x80
+*/
+
+#endif // _Consts_H_
+
+
-#ifndef cromwell_types_h\r
-#define cromwell_types_h\r
-\r
-/////////////////////////////////\r
-// some typedefs to make for easy sizing\r
-\r
-//typedef unsigned long ULONG;\r
-typedef unsigned int u32;\r
-typedef unsigned short u16;\r
-typedef unsigned char u8;\r
-#ifndef bool_already_defined_\r
- typedef int bool;\r
-#endif\r
-typedef unsigned long RGBA; // LSB=R -> MSB = A\r
-//typedef long long __int64;\r
-\r
-#define guint int\r
-#define guint8 unsigned char\r
-\r
-#define true 1\r
-#define false 0\r
-\r
-#ifndef NULL\r
-#define NULL ((void *)0)\r
-#endif\r
-\r
-#endif /* #ifndef cromwell_types_h */\r
+#ifndef cromwell_types_h
+#define cromwell_types_h
+
+/////////////////////////////////
+// some typedefs to make for easy sizing
+
+//typedef unsigned long ULONG;
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+#ifndef bool_already_defined_
+ typedef int bool;
+#endif
+typedef unsigned long RGBA; // LSB=R -> MSB = A
+//typedef long long __int64;
+
+#define guint int
+#define guint8 unsigned char
+
+#define true 1
+#define false 0
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#endif /* #ifndef cromwell_types_h */
-#ifndef _I386_ERRNO_H\r
-#define _I386_ERRNO_H\r
-\r
-#define EPERM 1 /* Operation not permitted */\r
-#define ENOENT 2 /* No such file or directory */\r
-#define ESRCH 3 /* No such process */\r
-#define EINTR 4 /* Interrupted system call */\r
-#define EIO 5 /* I/O error */\r
-#define ENXIO 6 /* No such device or address */\r
-#define E2BIG 7 /* Argument list too long */\r
-#define ENOEXEC 8 /* Exec format error */\r
-#define EBADF 9 /* Bad file number */\r
-#define ECHILD 10 /* No child processes */\r
-#define EAGAIN 11 /* Try again */\r
-#define ENOMEM 12 /* Out of memory */\r
-#define EACCES 13 /* Permission denied */\r
-#define EFAULT 14 /* Bad address */\r
-#define ENOTBLK 15 /* Block device required */\r
-#define EBUSY 16 /* Device or resource busy */\r
-#define EEXIST 17 /* File exists */\r
-#define EXDEV 18 /* Cross-device link */\r
-#define ENODEV 19 /* No such device */\r
-#define ENOTDIR 20 /* Not a directory */\r
-#define EISDIR 21 /* Is a directory */\r
-#define EINVAL 22 /* Invalid argument */\r
-#define ENFILE 23 /* File table overflow */\r
-#define EMFILE 24 /* Too many open files */\r
-#define ENOTTY 25 /* Not a typewriter */\r
-#define ETXTBSY 26 /* Text file busy */\r
-#define EFBIG 27 /* File too large */\r
-#define ENOSPC 28 /* No space left on device */\r
-#define ESPIPE 29 /* Illegal seek */\r
-#define EROFS 30 /* Read-only file system */\r
-#define EMLINK 31 /* Too many links */\r
-#define EPIPE 32 /* Broken pipe */\r
-#define EDOM 33 /* Math argument out of domain of func */\r
-#define ERANGE 34 /* Math result not representable */\r
-#define EDEADLK 35 /* Resource deadlock would occur */\r
-#define ENAMETOOLONG 36 /* File name too long */\r
-#define ENOLCK 37 /* No record locks available */\r
-#define ENOSYS 38 /* Function not implemented */\r
-#define ENOTEMPTY 39 /* Directory not empty */\r
-#define ELOOP 40 /* Too many symbolic links encountered */\r
-#define EWOULDBLOCK EAGAIN /* Operation would block */\r
-#define ENOMSG 42 /* No message of desired type */\r
-#define EIDRM 43 /* Identifier removed */\r
-#define ECHRNG 44 /* Channel number out of range */\r
-#define EL2NSYNC 45 /* Level 2 not synchronized */\r
-#define EL3HLT 46 /* Level 3 halted */\r
-#define EL3RST 47 /* Level 3 reset */\r
-#define ELNRNG 48 /* Link number out of range */\r
-#define EUNATCH 49 /* Protocol driver not attached */\r
-#define ENOCSI 50 /* No CSI structure available */\r
-#define EL2HLT 51 /* Level 2 halted */\r
-#define EBADE 52 /* Invalid exchange */\r
-#define EBADR 53 /* Invalid request descriptor */\r
-#define EXFULL 54 /* Exchange full */\r
-#define ENOANO 55 /* No anode */\r
-#define EBADRQC 56 /* Invalid request code */\r
-#define EBADSLT 57 /* Invalid slot */\r
-\r
-#define EDEADLOCK EDEADLK\r
-\r
-#define EBFONT 59 /* Bad font file format */\r
-#define ENOSTR 60 /* Device not a stream */\r
-#define ENODATA 61 /* No data available */\r
-#define ETIME 62 /* Timer expired */\r
-#define ENOSR 63 /* Out of streams resources */\r
-#define ENONET 64 /* Machine is not on the network */\r
-#define ENOPKG 65 /* Package not installed */\r
-#define EREMOTE 66 /* Object is remote */\r
-#define ENOLINK 67 /* Link has been severed */\r
-#define EADV 68 /* Advertise error */\r
-#define ESRMNT 69 /* Srmount error */\r
-#define ECOMM 70 /* Communication error on send */\r
-#define EPROTO 71 /* Protocol error */\r
-#define EMULTIHOP 72 /* Multihop attempted */\r
-#define EDOTDOT 73 /* RFS specific error */\r
-#define EBADMSG 74 /* Not a data message */\r
-#define EOVERFLOW 75 /* Value too large for defined data type */\r
-#define ENOTUNIQ 76 /* Name not unique on network */\r
-#define EBADFD 77 /* File descriptor in bad state */\r
-#define EREMCHG 78 /* Remote address changed */\r
-#define ELIBACC 79 /* Can not access a needed shared library */\r
-#define ELIBBAD 80 /* Accessing a corrupted shared library */\r
-#define ELIBSCN 81 /* .lib section in a.out corrupted */\r
-#define ELIBMAX 82 /* Attempting to link in too many shared libraries */\r
-#define ELIBEXEC 83 /* Cannot exec a shared library directly */\r
-#define EILSEQ 84 /* Illegal byte sequence */\r
-#define ERESTART 85 /* Interrupted system call should be restarted */\r
-#define ESTRPIPE 86 /* Streams pipe error */\r
-#define EUSERS 87 /* Too many users */\r
-#define ENOTSOCK 88 /* Socket operation on non-socket */\r
-#define EDESTADDRREQ 89 /* Destination address required */\r
-#define EMSGSIZE 90 /* Message too long */\r
-#define EPROTOTYPE 91 /* Protocol wrong type for socket */\r
-#define ENOPROTOOPT 92 /* Protocol not available */\r
-#define EPROTONOSUPPORT 93 /* Protocol not supported */\r
-#define ESOCKTNOSUPPORT 94 /* Socket type not supported */\r
-#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */\r
-#define EPFNOSUPPORT 96 /* Protocol family not supported */\r
-#define EAFNOSUPPORT 97 /* Address family not supported by protocol */\r
-#define EADDRINUSE 98 /* Address already in use */\r
-#define EADDRNOTAVAIL 99 /* Cannot assign requested address */\r
-#define ENETDOWN 100 /* Network is down */\r
-#define ENETUNREACH 101 /* Network is unreachable */\r
-#define ENETRESET 102 /* Network dropped connection because of reset */\r
-#define ECONNABORTED 103 /* Software caused connection abort */\r
-#define ECONNRESET 104 /* Connection reset by peer */\r
-#define ENOBUFS 105 /* No buffer space available */\r
-#define EISCONN 106 /* Transport endpoint is already connected */\r
-#define ENOTCONN 107 /* Transport endpoint is not connected */\r
-#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */\r
-#define ETOOMANYREFS 109 /* Too many references: cannot splice */\r
-#define ETIMEDOUT 110 /* Connection timed out */\r
-#define ECONNREFUSED 111 /* Connection refused */\r
-#define EHOSTDOWN 112 /* Host is down */\r
-#define EHOSTUNREACH 113 /* No route to host */\r
-#define EALREADY 114 /* Operation already in progress */\r
-#define EINPROGRESS 115 /* Operation now in progress */\r
-#define ESTALE 116 /* Stale NFS file handle */\r
-#define EUCLEAN 117 /* Structure needs cleaning */\r
-#define ENOTNAM 118 /* Not a XENIX named type file */\r
-#define ENAVAIL 119 /* No XENIX semaphores available */\r
-#define EISNAM 120 /* Is a named type file */\r
-#define EREMOTEIO 121 /* Remote I/O error */\r
-#define EDQUOT 122 /* Quota exceeded */\r
-\r
-#define ENOMEDIUM 123 /* No medium found */\r
-#define EMEDIUMTYPE 124 /* Wrong medium type */\r
-\r
-#endif\r
+#ifndef _I386_ERRNO_H
+#define _I386_ERRNO_H
+
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* I/O error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Argument list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file number */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device or resource busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* File table overflow */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Not a typewriter */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Math argument out of domain of func */
+#define ERANGE 34 /* Math result not representable */
+#define EDEADLK 35 /* Resource deadlock would occur */
+#define ENAMETOOLONG 36 /* File name too long */
+#define ENOLCK 37 /* No record locks available */
+#define ENOSYS 38 /* Function not implemented */
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ELOOP 40 /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42 /* No message of desired type */
+#define EIDRM 43 /* Identifier removed */
+#define ECHRNG 44 /* Channel number out of range */
+#define EL2NSYNC 45 /* Level 2 not synchronized */
+#define EL3HLT 46 /* Level 3 halted */
+#define EL3RST 47 /* Level 3 reset */
+#define ELNRNG 48 /* Link number out of range */
+#define EUNATCH 49 /* Protocol driver not attached */
+#define ENOCSI 50 /* No CSI structure available */
+#define EL2HLT 51 /* Level 2 halted */
+#define EBADE 52 /* Invalid exchange */
+#define EBADR 53 /* Invalid request descriptor */
+#define EXFULL 54 /* Exchange full */
+#define ENOANO 55 /* No anode */
+#define EBADRQC 56 /* Invalid request code */
+#define EBADSLT 57 /* Invalid slot */
+
+#define EDEADLOCK EDEADLK
+
+#define EBFONT 59 /* Bad font file format */
+#define ENOSTR 60 /* Device not a stream */
+#define ENODATA 61 /* No data available */
+#define ETIME 62 /* Timer expired */
+#define ENOSR 63 /* Out of streams resources */
+#define ENONET 64 /* Machine is not on the network */
+#define ENOPKG 65 /* Package not installed */
+#define EREMOTE 66 /* Object is remote */
+#define ENOLINK 67 /* Link has been severed */
+#define EADV 68 /* Advertise error */
+#define ESRMNT 69 /* Srmount error */
+#define ECOMM 70 /* Communication error on send */
+#define EPROTO 71 /* Protocol error */
+#define EMULTIHOP 72 /* Multihop attempted */
+#define EDOTDOT 73 /* RFS specific error */
+#define EBADMSG 74 /* Not a data message */
+#define EOVERFLOW 75 /* Value too large for defined data type */
+#define ENOTUNIQ 76 /* Name not unique on network */
+#define EBADFD 77 /* File descriptor in bad state */
+#define EREMCHG 78 /* Remote address changed */
+#define ELIBACC 79 /* Can not access a needed shared library */
+#define ELIBBAD 80 /* Accessing a corrupted shared library */
+#define ELIBSCN 81 /* .lib section in a.out corrupted */
+#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define EILSEQ 84 /* Illegal byte sequence */
+#define ERESTART 85 /* Interrupted system call should be restarted */
+#define ESTRPIPE 86 /* Streams pipe error */
+#define EUSERS 87 /* Too many users */
+#define ENOTSOCK 88 /* Socket operation on non-socket */
+#define EDESTADDRREQ 89 /* Destination address required */
+#define EMSGSIZE 90 /* Message too long */
+#define EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92 /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96 /* Protocol family not supported */
+#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define EADDRINUSE 98 /* Address already in use */
+#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define ENETDOWN 100 /* Network is down */
+#define ENETUNREACH 101 /* Network is unreachable */
+#define ENETRESET 102 /* Network dropped connection because of reset */
+#define ECONNABORTED 103 /* Software caused connection abort */
+#define ECONNRESET 104 /* Connection reset by peer */
+#define ENOBUFS 105 /* No buffer space available */
+#define EISCONN 106 /* Transport endpoint is already connected */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define ETIMEDOUT 110 /* Connection timed out */
+#define ECONNREFUSED 111 /* Connection refused */
+#define EHOSTDOWN 112 /* Host is down */
+#define EHOSTUNREACH 113 /* No route to host */
+#define EALREADY 114 /* Operation already in progress */
+#define EINPROGRESS 115 /* Operation now in progress */
+#define ESTALE 116 /* Stale NFS file handle */
+#define EUCLEAN 117 /* Structure needs cleaning */
+#define ENOTNAM 118 /* Not a XENIX named type file */
+#define ENAVAIL 119 /* No XENIX semaphores available */
+#define EISNAM 120 /* Is a named type file */
+#define EREMOTEIO 121 /* Remote I/O error */
+#define EDQUOT 122 /* Quota exceeded */
+
+#define ENOMEDIUM 123 /* No medium found */
+#define EMEDIUMTYPE 124 /* Wrong medium type */
+
+#endif
-/*\r
- * linux-wrapper.h\r
- *\r
- * Hard coded Linux kernel replacements for x86\r
- *\r
- * (c) 2003 Georg Acher (georg@acher.org)\r
- *\r
- * Emulation of:\r
- * typedefs\r
- * structs\r
- * macros\r
- *\r
- * All structs and prototypes are based on kernel source 2.5.72\r
- *\r
- * Modified by Aleksey Bragin (aleksey@reactos.com) for ReactOS needs\r
- *\r
- *\r
- * #include <standard-GPL-header.h>\r
- */\r
-\r
-/*------------------------------------------------------------------------*/\r
-/* Typedefs */\r
-/*------------------------------------------------------------------------*/ \r
-#include "cromwell_types.h"\r
-\r
-typedef unsigned int __u32;\r
-//typedef __u32 u32;\r
-typedef unsigned short __u16;\r
-//typedef __u16 u16;\r
-typedef unsigned char __u8;\r
-//typedef __u8 u8;\r
-\r
-typedef short s16;\r
-\r
-typedef u32 dma_addr_t;\r
-\r
-typedef int spinlock_t;\r
-typedef int atomic_t;\r
-#ifndef STANDALONE\r
-typedef int mode_t;\r
-typedef int pid_t;\r
-typedef int ssize_t;\r
-\r
-#endif\r
-typedef int irqreturn_t;\r
-typedef unsigned long kernel_ulong_t;\r
-\r
-typedef int wait_queue_head_t;\r
-/*------------------------------------------------------------------------*/ \r
-/* Stuff from xbox/linux environment */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#include "list.h"\r
-\r
-#ifndef STANDALONE\r
-#ifdef MODULE\r
-typedef int size_t;\r
-#define NULL ((void*)0)\r
-extern void * memset(void *,int,unsigned int);\r
-extern void * memcpy(void *,const void *,unsigned int);\r
-#if 0\r
-extern char * strcpy(char *,const char *);\r
-#else\r
-static inline char * strcpy(char * dest,const char *src)\r
-{\r
-int d0, d1, d2;\r
-__asm__ __volatile__(\r
- "1:\tlodsb\n\t"\r
- "stosb\n\t"\r
- "testb %%al,%%al\n\t"\r
- "jne 1b"\r
- : "=&S" (d0), "=&D" (d1), "=&a" (d2)\r
- :"0" (src),"1" (dest) : "memory");\r
-return dest;\r
-}\r
-#endif\r
-extern size_t strlen(const char *);\r
-\r
-extern int memcmp(const void *,const void *,unsigned int);\r
-\r
-#else\r
-#include "boot.h"\r
-#include "config.h"\r
-#endif\r
-#else\r
-#include <stdarg.h>\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include "consts.h"\r
-#include <string.h>\r
-#endif\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* General structs */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-struct timer_list { \r
- void (*function)(unsigned long);\r
- unsigned long data;\r
- int expires;\r
- struct list_head timer_list;\r
-};\r
-\r
-struct work_struct {\r
- void (*func)(void *);\r
-};\r
-struct device {\r
- char name[128];\r
- struct bus_type *bus;\r
- int dma_mask;\r
- char bus_id[16];\r
- struct device_driver* driver;\r
- void *driver_data;\r
- struct device *parent;\r
- struct list_head driver_list;\r
- void (*release)(struct device * dev);\r
-};\r
-struct class_device{int a;};\r
-struct semaphore{int a;};\r
-\r
-struct device_driver{\r
- char *name;\r
- struct bus_type *bus;\r
- int (*probe) (struct device * dev);\r
- int (*remove) (struct device * dev);\r
- struct list_head devices;\r
-};\r
-\r
-struct bus_type {\r
- char * name; \r
- int (*match)(struct device * dev, struct device_driver * drv);\r
- struct device * (*add) (struct device * parent, char * bus_id);\r
- int (*hotplug) (struct device *dev, char **envp, \r
- int num_envp, char *buffer, int buffer_size);\r
-};\r
-\r
-struct dummy_process\r
-{\r
- int flags;\r
-};\r
-\r
-struct pt_regs\r
-{\r
- int a;\r
-};\r
-struct completion {\r
- unsigned int done;\r
- wait_queue_head_t wait;\r
-};\r
-\r
-// windows lookaside list head\r
-typedef void* kmem_cache_t;\r
-\r
-struct dma_pool\r
-{\r
- int dummy;\r
-};\r
-\r
-/* from mod_devicetable.h */\r
-\r
-struct usb_device_id {\r
- /* which fields to match against? */\r
- __u16 match_flags;\r
-\r
- /* Used for product specific matches; range is inclusive */\r
- __u16 idVendor;\r
- __u16 idProduct;\r
- __u16 bcdDevice_lo;\r
- __u16 bcdDevice_hi;\r
-\r
- /* Used for device class matches */\r
- __u8 bDeviceClass;\r
- __u8 bDeviceSubClass;\r
- __u8 bDeviceProtocol;\r
-\r
- /* Used for interface class matches */\r
- __u8 bInterfaceClass;\r
- __u8 bInterfaceSubClass;\r
- __u8 bInterfaceProtocol;\r
-\r
- /* not matched against */\r
- kernel_ulong_t driver_info;\r
-};\r
-\r
-/* Some useful macros to use to create struct usb_device_id */\r
-#define USB_DEVICE_ID_MATCH_VENDOR 0x0001\r
-#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002\r
-#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004\r
-#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008\r
-#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010\r
-#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020\r
-#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040\r
-#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080\r
-#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100\r
-#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* imported functions from top-level */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-//void zxprintf(char* fmt, ...);\r
-//void zxsprintf(char *buffer, char* fmt, ...);\r
-//int zxsnprintf(char *buffer, size_t s, char* fmt, ...);\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* PCI structs (taken from linux/pci.h et al., but slightly modified) */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-struct pci_dev {\r
- int vendor;\r
- int device;\r
- struct pci_bus *bus;\r
- int irq;\r
- char *slot_name;\r
- struct device dev;\r
- int base[4];\r
- int flags[4];\r
- void * data;\r
- void * dev_ext; // link to Windows DeviceExtension\r
-};\r
-\r
-struct pci_bus {\r
- unsigned char number;\r
-};\r
-\r
-struct pci_device_id {\r
- __u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/\r
- __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */\r
- __u32 class, class_mask; /* (class,subclass,prog-if) triplet */\r
- kernel_ulong_t driver_data; /* Data private to the driver */\r
-};\r
-\r
-struct pci_driver {\r
- struct list_head node;\r
- char *name;\r
- const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */\r
- int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */\r
- void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */\r
- int (*save_state) (struct pci_dev *dev, u32 state); /* Save Device Context */\r
- int (*suspend) (struct pci_dev *dev, u32 state); /* Device suspended */\r
- int (*resume) (struct pci_dev *dev); /* Device woken up */\r
- int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); /* Enable wake event */\r
-};\r
-\r
-struct scatterlist\r
-{\r
- int page;\r
- int offset;\r
- int length;\r
-};\r
-\r
-struct usbdevfs_hub_portinfo\r
-{\r
- int nports;\r
- int port[8];\r
-};\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* constant defines */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#define TASK_UNINTERRUPTIBLE 0\r
-#define HZ 100 /* Don't rely on that... */\r
-#define KERN_DEBUG "DBG: "\r
-#define KERN_ERR "ERR: "\r
-#define KERN_WARNING "WRN: "\r
-#define KERN_INFO "INF: "\r
-#define GFP_KERNEL 0\r
-#define GFP_ATOMIC 0x20\r
-#define GFP_NOIO 0\r
-#define SLAB_ATOMIC 0\r
-#define PCI_ANY_ID (~0)\r
-#define SIGKILL 9\r
-#define THIS_MODULE 0\r
-//#define PAGE_SIZE 4096\r
-\r
-\r
-#define CLONE_FS 0\r
-#define CLONE_FILES 0\r
-#define CLONE_SIGHAND 0\r
-#define PF_FREEZE 0\r
-#define PF_IOTHREAD 0\r
-\r
-\r
-#define USBDEVFS_HUB_PORTINFO 1234\r
-#define SA_SHIRQ 0\r
-\r
-#undef PCI_COMMAND\r
-#define PCI_COMMAND 0\r
-#undef PCI_COMMAND_MASTER\r
-#define PCI_COMMAND_MASTER 0\r
-/*------------------------------------------------------------------------*/ \r
-/* Module/export macros */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#define MODULE_AUTHOR(a)\r
-#define MODULE_DESCRIPTION(a)\r
-#define MODULE_LICENSE(a)\r
-#define MODULE_DEVICE_TABLE(type,name) void* module_table_##name=&name\r
-#define MODULE_PARM(a,b)\r
-#define MODULE_PARM_DESC(a,b)\r
-\r
-#define __devinit\r
-#define __exit\r
-#define __init\r
-#define __devinitdata\r
-#define module_init(x) static void module_init_##x(void){ x();}\r
-#define module_exit(x) void module_exit_##x(void){ x();}\r
-#define EXPORT_SYMBOL_GPL(x)\r
-#define EXPORT_SYMBOL(x)\r
-\r
-#define __setup(x,y) int setup_##y=(int)y\r
-#define subsys_initcall(x) void subsys_##x(void){x();}\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Access macros */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#define dev_get_drvdata(a) (a)->driver_data\r
-#define dev_set_drvdata(a,b) (a)->driver_data=(b)\r
-\r
-#define __io_virt(x) ((void *)(x))\r
-#define readl(addr) (*(volatile unsigned int *) __io_virt(addr))\r
-#define writel(b,addr) (*(volatile unsigned int *) __io_virt(addr) = (b))\r
-#define likely(x) (x)\r
-#define unlikely(x) (x)\r
-#define prefetch(x) 1\r
-\r
-#define inw(x) READ_PORT_USHORT((PUSHORT)(x))\r
-#define outw(x,p) WRITE_PORT_USHORT((PUSHORT)(p),(x))\r
-#define outl(x,p) WRITE_PORT_ULONG((PUSHORT)(p),(x))\r
-\r
-/* The kernel macro for list_for_each_entry makes nonsense (have no clue\r
- * why, this is just the same definition...) */\r
-\r
-#undef list_for_each_entry\r
-#define list_for_each_entry(pos, head, member) \\r
- for (pos = list_entry((head)->next, typeof(*pos), member), \\r
- prefetch(pos->member.next); \\r
- &pos->member != (head); \\r
- pos = list_entry(pos->member.next, typeof(*pos), member), \\r
- prefetch(pos->member.next))\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* function wrapper macros */\r
-/*------------------------------------------------------------------------*/ \r
-#define kmalloc(x,y) ExAllocatePool(PagedPool,x)\r
-#define kfree(x) ExFreePool(x)\r
-\r
-//#define sprintf(a,b,format, arg...) zxsprintf((a),(b),format, ## arg)\r
-//#define snprintf(a,b,format, arg...) zxsnprintf((a),(b),format, ##arg)\r
-//#define printk(format, arg...) zxprintf(format, ## arg)\r
-#define snprintf(a,b,format, arg...) _snprintf((a),(b),format, ##arg)\r
-#define printk(format, arg...) DPRINT1(format, ## arg)\r
-#define BUG(...) do {} while(0)\r
-\r
-/* Locks & friends */\r
-\r
-#define DECLARE_MUTEX(x) struct semaphore x\r
-#define init_MUTEX(x)\r
-\r
-#define SPIN_LOCK_UNLOCKED 0\r
-#define spin_lock_init(a) do {} while(0)\r
-#define spin_lock(a) *(int*)a=1\r
-#define spin_unlock(a) do {} while(0)\r
-\r
-#define spin_lock_irqsave(a,b) b=0\r
-#define spin_unlock_irqrestore(a,b)\r
-\r
-#if 0\r
-#define local_irq_save(x) __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory")\r
-#define local_irq_restore(x) __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory", "cc")\r
-#else\r
-#define local_irq_save(x) do {} while(0) \r
-#define local_irq_restore(x) do {} while(0) \r
-#endif\r
-\r
-#define atomic_inc(x) *(x)+=1\r
-#define atomic_dec(x) *(x)-=1\r
-#define atomic_dec_and_test(x) (*(x)-=1,(*(x))==0)\r
-#define atomic_set(x,a) *(x)=a\r
-#define atomic_read(x) *(x)\r
-#define ATOMIC_INIT(x) (x)\r
-\r
-#define down(x) do {} while(0) \r
-#define up(x) do {} while(0)\r
-#define down_trylock(a) 0\r
-\r
-#define down_read(a) do {} while(0)\r
-#define up_read(a) do {} while(0)\r
-\r
-#define DECLARE_WAIT_QUEUE_HEAD(x) KEVENT x\r
-\r
-#define DECLARE_COMPLETION(x) struct completion x\r
-\r
-/* driver */\r
-\r
-#define driver_unregister(a) do {} while(0)\r
-#define put_device(a) do {} while(0)\r
-\r
-\r
-/* PCI */\r
-#define to_pci_dev(n) container_of(n, struct pci_dev, dev)\r
-\r
-#define pci_pool_create(a,b,c,d,e) (void*)1\r
-\r
-#define pci_pool_alloc(a,b,c) my_pci_pool_alloc(a,b,c) \r
-\r
-static void __inline__ *my_pci_pool_alloc(void* pool, size_t size,\r
- dma_addr_t *dma_handle)\r
-{\r
- void* a;\r
- a=kmalloc(size,0); //FIXME\r
-#ifdef MODULE\r
- *dma_handle=((u32)a)&0xfffffff;\r
-#else\r
- *dma_handle=(u32)a;\r
-#endif\r
- return a;\r
-}\r
-\r
-\r
-#define pci_pool_free(a,b,c) kfree(b)\r
-#define pci_alloc_consistent(a,b,c) my_pci_alloc_consistent(a,b,c)\r
-\r
-static void __inline__ *my_pci_alloc_consistent(struct pci_dev *hwdev, size_t size,\r
- dma_addr_t *dma_handle)\r
-{\r
- void* a;\r
-\r
- a=kmalloc(size+256,0); //FIXME\r
- a=(void*)(((int)a+255)&~255); // 256 alignment\r
- *dma_handle=((u32)a)&0xfffffff;\r
-\r
- return a;\r
-}\r
-\r
-#define pci_free_consistent(a,b,c,d) kfree(c)\r
-#define pci_pool_destroy(a) do {} while(0)\r
-\r
-#define pci_module_init(x) my_pci_module_init(x)\r
-int my_pci_module_init(struct pci_driver *x);\r
-\r
-#define pci_unregister_driver(a) do {} while(0) \r
-\r
-#define pci_write_config_word(a,b,c) my_pci_write_config_word(a,b,c)\r
-\r
-#define bus_register(a) do {} while(0)\r
-#define bus_unregister(a) do {} while(0)\r
-\r
-/* DMA */\r
-//#define dma_pool_alloc(a,b,c) my_dma_pool_alloc((a),(b),(c))\r
-#define dma_pool_alloc(a,b,c) pci_pool_alloc(a,b,c)\r
-#define dma_pool_create(a,b,c,d,e) pci_pool_create(a,b,c,d,e)\r
-#define dma_pool_free(a,b,c) pci_pool_free(a,b,c)\r
-#define dma_pool_destroy(a) pci_pool_destroy(a)\r
-\r
-#define dma_alloc_coherent(a,b,c,d) NULL\r
-#define dma_free_coherent(a,b,c,d) do {} while(0)\r
-\r
-#define dma_map_single(a,b,c,d) ((u32)(b)&0xfffffff)\r
-#define dma_unmap_single(a,b,c,d) do {} while(0)\r
-#define pci_unmap_single(a,b,c,d) do {} while(0)\r
-#define dma_sync_single(a,b,c,d) do {} while(0)\r
-#define dma_sync_sg(a,b,c,d) do {} while(0)\r
-#define dma_map_sg(a,b,c,d) 0\r
-#define dma_unmap_sg(a,b,c,d) do {} while(0)\r
-\r
-#define usb_create_driverfs_dev_files(a) do {} while(0)\r
-#define usb_create_driverfs_intf_files(a) do {} while(0)\r
-#define sg_dma_address(x) ((u32)((x)->page*4096 + (x)->offset))\r
-#define sg_dma_len(x) ((x)->length) \r
-\r
-#define page_address(x) ((void*)(x/4096))\r
-\r
-#define DMA_TO_DEVICE 0\r
-#define DMA_FROM_DEVICE 0\r
-#define PCI_DMA_TODEVICE\r
-#define PCI_DMA_FROMDEVICE\r
-#define PCI_DMA_TODEVICE\r
-\r
-#define PCI_ROM_RESOURCE 1\r
-#define IORESOURCE_IO CM_RESOURCE_PORT_IO\r
-\r
-#define DECLARE_WAITQUEUE(a,b) KEVENT a=0\r
-#define init_waitqueue_head(a) my_init_waitqueue_head(a)\r
-#define add_wait_queue(a,b) do {} while(0)\r
-#define remove_wait_queue(a,b) do {} while(0)\r
-void my_init_waitqueue_head(PKEVENT a);\r
-\r
-VOID KeMemoryBarrier(VOID);\r
-\r
-#define mb() KeMemoryBarrier()\r
-#define wmb() __asm__ __volatile__ ("": : :"memory")\r
-#define rmb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")\r
-\r
-#define in_interrupt() 0\r
-\r
-#define init_completion(x) (x)->done=0\r
-#define wait_for_completion(x) my_wait_for_completion(x)\r
-void my_wait_for_completion(struct completion*);\r
-\r
-#define IRQ_NONE 0\r
-#define IRQ_HANDLED 1\r
-\r
-#define INIT_WORK(a,b,c) (a)->func=b\r
-\r
-#define set_current_state(a) do {} while(0)\r
-\r
-#define might_sleep() do {} while(0)\r
-#define daemonize(a) do {} while(0)\r
-#define allow_signal(a) do {} while(0)\r
-#define wait_event_interruptible(x,y) do {} while(0)\r
-\r
-#define interruptible_sleep_on(a) my_interruptible_sleep_on(a)\r
-void my_interruptible_sleep_on(PKEVENT evnt);\r
-\r
-#define flush_scheduled_work() do {} while(0)\r
-#define refrigerator(x) do {} while(0)\r
-#define signal_pending(x) 1 // fall through threads\r
-#define complete_and_exit(a,b) return 0\r
-\r
-//#define kill_proc(a,b,c) 0\r
-#define kill_proc(a,b,c) my_kill_proc(a, b, c);\r
-int my_kill_proc(int pid, int signal, int unk);\r
-\r
-#define yield() do {} while(0)\r
-#define cpu_relax() do {} while(0)\r
-\r
-#define WARN_ON(a) do {} while(0)\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Lookaside lists funcs */\r
-/*------------------------------------------------------------------------*/ \r
-#define kmem_cache_create(a,b,c,d,e,f) my_kmem_cache_create((a),(b),(c),(d),(e),(f))\r
-#define kmem_cache_destroy(a) my_kmem_cache_destroy((a))\r
-#define kmem_cache_alloc(co, flags) my_kmem_cache_alloc((co), (flags))\r
-#define kmem_cache_free(co, ptr) my_kmem_cache_free((co), (ptr))\r
-\r
-kmem_cache_t *my_kmem_cache_create(const char *tag, size_t alloc_size,\r
- size_t offset, unsigned long flags,\r
- void *ctor,\r
- void *dtor);\r
-\r
-BOOLEAN my_kmem_cache_destroy(kmem_cache_t *co);\r
-void *my_kmem_cache_alloc(kmem_cache_t *co, int flags);\r
-void my_kmem_cache_free(kmem_cache_t *co, void *ptr);\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Kernel macros */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#define LINUX_VERSION_CODE 0x020572\r
-#define UTS_SYSNAME "XBOX"\r
-#define UTS_RELEASE "----"\r
-\r
-/* from linux/kernel.h */\r
-#define max_t(type,x,y) \\r
- ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })\r
-\r
-#define min_t(type,x,y) \\r
- ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })\r
-\r
-#define container_of(ptr, type, member) ({ \\r
- const typeof( ((type *)0)->member ) *__mptr = (ptr); \\r
- (type *)( (char *)__mptr - offsetof(type,member) );})\r
-\r
-/* from linux/stddef.h */\r
-\r
-#undef offsetof\r
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Conversion macros */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#define __constant_cpu_to_le32(x) (x)\r
-#define cpu_to_le16(x) (x)\r
-#define le16_to_cpu(x) (x)\r
-#define cpu_to_le32(x) (x)\r
-#define cpu_to_le32p(x) (*(__u32*)(x))\r
-#define le32_to_cpup(x) (*(__u32*)(x))\r
-#define le32_to_cpu(x) ((u32)x)\r
-#define le16_to_cpus(x) do {} while (0)\r
-#define le16_to_cpup(x) (*(__u16*)(x))\r
-#define cpu_to_le16p(x) (*(__u16*)(x))\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Debug output */\r
-/*------------------------------------------------------------------------*/ \r
-#ifdef DEBUG_MODE\r
-#define dev_printk(lvl,x,f,arg...) printk(f, ## arg)\r
-#define dev_dbg(x,f,arg...) printk(f, ## arg)\r
-#define dev_info(x,f,arg...) printk(f,## arg)\r
-#define dev_warn(x,f,arg...) printk(f,## arg)\r
-#define dev_err(x,f,arg...) printk(f,## arg)\r
-#define pr_debug(x,f,arg...) printk(f,## arg)\r
-#define usbprintk printk\r
-#endif\r
-\r
-#ifndef DEBUG_MODE\r
-#define dev_printk(lvl,x,f,arg...) do {} while (0)\r
-#define dev_dbg(x,f,arg...) do {} while (0) //printk(f, ## arg)\r
-#define dev_info(x,f,arg...) do {} while (0)\r
-#define dev_warn(x,f,arg...) do {} while (0)\r
-#define dev_err(x,f,arg...) do {} while (0)\r
-#define pr_debug(x,f,arg...) do {} while (0)\r
-#define usbprintk\r
-#endif\r
-\r
-\r
-\r
-#define PCI_DEVFN(a,b) 0\r
-#define PCI_SLOT(a) 0\r
-\r
-/**\r
- * PCI_DEVICE_CLASS - macro used to describe a specific pci device class\r
- * @dev_class: the class, subclass, prog-if triple for this device\r
- * @dev_class_mask: the class mask for this device\r
- *\r
- * This macro is used to create a struct pci_device_id that matches a\r
- * specific PCI class. The vendor, device, subvendor, and subdevice \r
- * fields will be set to PCI_ANY_ID.\r
- */\r
-#define PCI_DEVICE_CLASS(dev_class,dev_class_mask) \\r
- .class = (dev_class), .class_mask = (dev_class_mask), \\r
- .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \\r
- .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID\r
-\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Stuff from kernel */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#include "errno.h"\r
-#include "bitops.h"\r
-//#include "linux/pci_ids.h"\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* global variables */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#define jiffies my_jiffies\r
-extern int my_jiffies;\r
-#define current my_current\r
-extern struct dummy_process *my_current;\r
-\r
-extern struct list_head interrupt_list;\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Function prototypes */\r
-/*------------------------------------------------------------------------*/ \r
-void STDCALL usb_hcd_pci_remove (struct pci_dev *dev);\r
-\r
-#define my_wait_ms(x) wait_ms(x)\r
-\r
-#define my_udelay(x) wait_ms(x)\r
-#define udelay(x) my_udelay(x)\r
-\r
-#define my_mdelay(x) wait_ms(1+x/1000);\r
-#define mdelay(x) my_mdelay(x);\r
-\r
-#define pci_find_slot(a,b) my_pci_find_slot(a,b)\r
-struct pci_dev *my_pci_find_slot(int a,int b);\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Timer management */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#define MAX_TIMERS 20\r
-extern struct timer_list *main_timer_list[MAX_TIMERS];\r
-\r
-static void __inline__ init_timer(struct timer_list* t)\r
-{\r
- INIT_LIST_HEAD(&t->timer_list);\r
- t->function=NULL;\r
- t->expires=0;\r
-}\r
-\r
-static void __inline__ add_timer(struct timer_list* t)\r
-{\r
- int n;\r
- for(n=0;n<MAX_TIMERS;n++)\r
- if (main_timer_list[n]==0)\r
- {\r
- main_timer_list[n]=t;\r
- break;\r
- }\r
-}\r
-\r
-static void __inline__ del_timer(struct timer_list* t)\r
-{\r
- int n;\r
- for(n=0;n<MAX_TIMERS;n++)\r
- if (main_timer_list[n]==t)\r
- {\r
- main_timer_list[n]=0;\r
- break;\r
- }\r
-}\r
-static void __inline__ del_timer_sync(struct timer_list* t)\r
-{\r
- int n;\r
- for(n=0;n<MAX_TIMERS;n++)\r
- if (main_timer_list[n]==t)\r
- {\r
- main_timer_list[n]=0;\r
- break;\r
- }\r
-\r
-}\r
-static void __inline__ mod_timer(struct timer_list* t, int ex)\r
-{\r
- del_timer(t);\r
- t->expires=ex;\r
- add_timer(t);\r
-}\r
-\r
-#define time_after_eq(a,b) \\r
- (((long)(a) - (long)(b) >= 0))\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Device driver and process related stuff */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-static int __inline__ usb_major_init(void){return 0;}\r
-static void __inline__ usb_major_cleanup(void){}\r
-static void __inline__ schedule_work(void* p){}\r
-\r
-#define device_initialize(x) my_device_initialize(x)\r
-void my_device_initialize(struct device *dev);\r
-\r
-#define get_device(x) my_get_device(x)\r
-struct device *my_get_device(struct device *dev);\r
-\r
-#define device_add(x) my_device_add(x)\r
-int my_device_add(struct device *dev);\r
-\r
-#define driver_register(x) my_driver_register(x)\r
-int my_driver_register(struct device_driver *driver);\r
-\r
-#define device_unregister(a) my_device_unregister(a)\r
-int my_device_unregister(struct device *dev);\r
-\r
-#define DEVICE_ATTR(a,b,c,d) int xxx_##a\r
-#define device_create_file(a,b) do {} while(0)\r
-#define device_remove_file(a,b) do {} while(0)\r
-\r
-#define schedule_timeout(x) my_schedule_timeout(x)\r
-int my_schedule_timeout(int x);\r
-\r
-#define wake_up(x) my_wake_up(x)\r
-void my_wake_up(PKEVENT);\r
-\r
-// cannot be mapped via macro due to collision with urb->complete\r
-static void __inline__ complete(struct completion *p)\r
-{\r
- /* Wake up x->wait */\r
- p->done++;\r
- wake_up(&p->wait);\r
-}\r
-\r
-#define kernel_thread(a,b,c) my_kernel_thread(a,b,c)\r
-int my_kernel_thread(int STDCALL (*handler)(void*), void* parm, int flags);\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* PCI, simple and inlined... */\r
-/*------------------------------------------------------------------------*/ \r
-#include "pci_hal.c"\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* IRQ handling */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-#define request_irq(a,b,c,d,e) my_request_irq(a,b,c,d,e)\r
-int my_request_irq(unsigned int irq,\r
- int (*handler)(int, void *, struct pt_regs *),\r
- unsigned long mode, const char *desc, void *data);\r
-\r
-#define free_irq(a,b) my_free_irq(a,b)\r
-int free_irq(int irq, void* p);\r
-\r
-\r
-\r
-struct my_irqs {\r
- int (*handler)(int, void *, struct pt_regs *);\r
- int irq;\r
- void* data;\r
-};\r
-\r
-#define MAX_IRQS 8\r
-\r
-// Exported to top level\r
-\r
-void handle_irqs(int irq);\r
-void inc_jiffies(int);\r
-void init_wrapper(struct pci_dev *pci_dev);\r
-void do_all_timers(void);\r
-\r
-#define __KERNEL_DS 0x18\r
-\r
-\r
+/*
+ * linux-wrapper.h
+ *
+ * Hard coded Linux kernel replacements for x86
+ *
+ * (c) 2003 Georg Acher (georg@acher.org)
+ *
+ * Emulation of:
+ * typedefs
+ * structs
+ * macros
+ *
+ * All structs and prototypes are based on kernel source 2.5.72
+ *
+ * Modified by Aleksey Bragin (aleksey@reactos.com) for ReactOS needs
+ *
+ *
+ * #include <standard-GPL-header.h>
+ */
+
+/*------------------------------------------------------------------------*/
+/* Typedefs */
+/*------------------------------------------------------------------------*/
+#include "cromwell_types.h"
+
+typedef unsigned int __u32;
+//typedef __u32 u32;
+typedef unsigned short __u16;
+//typedef __u16 u16;
+typedef unsigned char __u8;
+//typedef __u8 u8;
+
+typedef short s16;
+
+typedef u32 dma_addr_t;
+
+typedef int spinlock_t;
+typedef int atomic_t;
+#ifndef STANDALONE
+typedef int mode_t;
+typedef int pid_t;
+typedef int ssize_t;
+
+#endif
+typedef int irqreturn_t;
+typedef unsigned long kernel_ulong_t;
+
+typedef int wait_queue_head_t;
+/*------------------------------------------------------------------------*/
+/* Stuff from xbox/linux environment */
+/*------------------------------------------------------------------------*/
+
+#include "list.h"
+
+#ifndef STANDALONE
+#ifdef MODULE
+typedef int size_t;
+#define NULL ((void*)0)
+extern void * memset(void *,int,unsigned int);
+extern void * memcpy(void *,const void *,unsigned int);
+#if 0
+extern char * strcpy(char *,const char *);
+#else
+static inline char * strcpy(char * dest,const char *src)
+{
+int d0, d1, d2;
+__asm__ __volatile__(
+ "1:\tlodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b"
+ : "=&S" (d0), "=&D" (d1), "=&a" (d2)
+ :"0" (src),"1" (dest) : "memory");
+return dest;
+}
+#endif
+extern size_t strlen(const char *);
+
+extern int memcmp(const void *,const void *,unsigned int);
+
+#else
+#include "boot.h"
+#include "config.h"
+#endif
+#else
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "consts.h"
+#include <string.h>
+#endif
+
+/*------------------------------------------------------------------------*/
+/* General structs */
+/*------------------------------------------------------------------------*/
+
+struct timer_list {
+ void (*function)(unsigned long);
+ unsigned long data;
+ int expires;
+ struct list_head timer_list;
+};
+
+struct work_struct {
+ void (*func)(void *);
+};
+struct device {
+ char name[128];
+ struct bus_type *bus;
+ int dma_mask;
+ char bus_id[16];
+ struct device_driver* driver;
+ void *driver_data;
+ struct device *parent;
+ struct list_head driver_list;
+ void (*release)(struct device * dev);
+};
+struct class_device{int a;};
+struct semaphore{int a;};
+
+struct device_driver{
+ char *name;
+ struct bus_type *bus;
+ int (*probe) (struct device * dev);
+ int (*remove) (struct device * dev);
+ struct list_head devices;
+};
+
+struct bus_type {
+ char * name;
+ int (*match)(struct device * dev, struct device_driver * drv);
+ struct device * (*add) (struct device * parent, char * bus_id);
+ int (*hotplug) (struct device *dev, char **envp,
+ int num_envp, char *buffer, int buffer_size);
+};
+
+struct dummy_process
+{
+ int flags;
+};
+
+struct pt_regs
+{
+ int a;
+};
+struct completion {
+ unsigned int done;
+ wait_queue_head_t wait;
+};
+
+// windows lookaside list head
+typedef void* kmem_cache_t;
+
+struct dma_pool
+{
+ int dummy;
+};
+
+/* from mod_devicetable.h */
+
+struct usb_device_id {
+ /* which fields to match against? */
+ __u16 match_flags;
+
+ /* Used for product specific matches; range is inclusive */
+ __u16 idVendor;
+ __u16 idProduct;
+ __u16 bcdDevice_lo;
+ __u16 bcdDevice_hi;
+
+ /* Used for device class matches */
+ __u8 bDeviceClass;
+ __u8 bDeviceSubClass;
+ __u8 bDeviceProtocol;
+
+ /* Used for interface class matches */
+ __u8 bInterfaceClass;
+ __u8 bInterfaceSubClass;
+ __u8 bInterfaceProtocol;
+
+ /* not matched against */
+ kernel_ulong_t driver_info;
+};
+
+/* Some useful macros to use to create struct usb_device_id */
+#define USB_DEVICE_ID_MATCH_VENDOR 0x0001
+#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002
+#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004
+#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008
+#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010
+#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020
+#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040
+#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080
+#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100
+#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200
+
+/*------------------------------------------------------------------------*/
+/* imported functions from top-level */
+/*------------------------------------------------------------------------*/
+
+//void zxprintf(char* fmt, ...);
+//void zxsprintf(char *buffer, char* fmt, ...);
+//int zxsnprintf(char *buffer, size_t s, char* fmt, ...);
+
+/*------------------------------------------------------------------------*/
+/* PCI structs (taken from linux/pci.h et al., but slightly modified) */
+/*------------------------------------------------------------------------*/
+
+struct pci_dev {
+ int vendor;
+ int device;
+ struct pci_bus *bus;
+ int irq;
+ char *slot_name;
+ struct device dev;
+ int base[4];
+ int flags[4];
+ void * data;
+ void * dev_ext; // link to Windows DeviceExtension
+};
+
+struct pci_bus {
+ unsigned char number;
+};
+
+struct pci_device_id {
+ __u32 vendor, device; /* Vendor and device ID or PCI_ANY_ID*/
+ __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
+ __u32 class, class_mask; /* (class,subclass,prog-if) triplet */
+ kernel_ulong_t driver_data; /* Data private to the driver */
+};
+
+struct pci_driver {
+ struct list_head node;
+ char *name;
+ const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */
+ int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
+ void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
+ int (*save_state) (struct pci_dev *dev, u32 state); /* Save Device Context */
+ int (*suspend) (struct pci_dev *dev, u32 state); /* Device suspended */
+ int (*resume) (struct pci_dev *dev); /* Device woken up */
+ int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); /* Enable wake event */
+};
+
+struct scatterlist
+{
+ int page;
+ int offset;
+ int length;
+};
+
+struct usbdevfs_hub_portinfo
+{
+ int nports;
+ int port[8];
+};
+
+/*------------------------------------------------------------------------*/
+/* constant defines */
+/*------------------------------------------------------------------------*/
+
+#define TASK_UNINTERRUPTIBLE 0
+#define HZ 100 /* Don't rely on that... */
+#define KERN_DEBUG "DBG: "
+#define KERN_ERR "ERR: "
+#define KERN_WARNING "WRN: "
+#define KERN_INFO "INF: "
+#define GFP_KERNEL 0
+#define GFP_ATOMIC 0x20
+#define GFP_NOIO 0
+#define SLAB_ATOMIC 0
+#define PCI_ANY_ID (~0)
+#define SIGKILL 9
+#define THIS_MODULE 0
+//#define PAGE_SIZE 4096
+
+
+#define CLONE_FS 0
+#define CLONE_FILES 0
+#define CLONE_SIGHAND 0
+#define PF_FREEZE 0
+#define PF_IOTHREAD 0
+
+
+#define USBDEVFS_HUB_PORTINFO 1234
+#define SA_SHIRQ 0
+
+#undef PCI_COMMAND
+#define PCI_COMMAND 0
+#undef PCI_COMMAND_MASTER
+#define PCI_COMMAND_MASTER 0
+/*------------------------------------------------------------------------*/
+/* Module/export macros */
+/*------------------------------------------------------------------------*/
+
+#define MODULE_AUTHOR(a)
+#define MODULE_DESCRIPTION(a)
+#define MODULE_LICENSE(a)
+#define MODULE_DEVICE_TABLE(type,name) void* module_table_##name=&name
+#define MODULE_PARM(a,b)
+#define MODULE_PARM_DESC(a,b)
+
+#define __devinit
+#define __exit
+#define __init
+#define __devinitdata
+#define module_init(x) static void module_init_##x(void){ x();}
+#define module_exit(x) void module_exit_##x(void){ x();}
+#define EXPORT_SYMBOL_GPL(x)
+#define EXPORT_SYMBOL(x)
+
+#define __setup(x,y) int setup_##y=(int)y
+#define subsys_initcall(x) void subsys_##x(void){x();}
+
+/*------------------------------------------------------------------------*/
+/* Access macros */
+/*------------------------------------------------------------------------*/
+
+#define dev_get_drvdata(a) (a)->driver_data
+#define dev_set_drvdata(a,b) (a)->driver_data=(b)
+
+#define __io_virt(x) ((void *)(x))
+#define readl(addr) (*(volatile unsigned int *) __io_virt(addr))
+#define writel(b,addr) (*(volatile unsigned int *) __io_virt(addr) = (b))
+#define likely(x) (x)
+#define unlikely(x) (x)
+#define prefetch(x) 1
+
+#define inw(x) READ_PORT_USHORT((PUSHORT)(x))
+#define outw(x,p) WRITE_PORT_USHORT((PUSHORT)(p),(x))
+#define outl(x,p) WRITE_PORT_ULONG((PUSHORT)(p),(x))
+
+/* The kernel macro for list_for_each_entry makes nonsense (have no clue
+ * why, this is just the same definition...) */
+
+#undef list_for_each_entry
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/*------------------------------------------------------------------------*/
+/* function wrapper macros */
+/*------------------------------------------------------------------------*/
+#define kmalloc(x,y) ExAllocatePool(PagedPool,x)
+#define kfree(x) ExFreePool(x)
+
+//#define sprintf(a,b,format, arg...) zxsprintf((a),(b),format, ## arg)
+//#define snprintf(a,b,format, arg...) zxsnprintf((a),(b),format, ##arg)
+//#define printk(format, arg...) zxprintf(format, ## arg)
+#define snprintf(a,b,format, arg...) _snprintf((a),(b),format, ##arg)
+#define printk(format, arg...) DPRINT1(format, ## arg)
+#define BUG(...) do {} while(0)
+
+/* Locks & friends */
+
+#define DECLARE_MUTEX(x) struct semaphore x
+#define init_MUTEX(x)
+
+#define SPIN_LOCK_UNLOCKED 0
+#define spin_lock_init(a) do {} while(0)
+#define spin_lock(a) *(int*)a=1
+#define spin_unlock(a) do {} while(0)
+
+#define spin_lock_irqsave(a,b) b=0
+#define spin_unlock_irqrestore(a,b)
+
+#if 0
+#define local_irq_save(x) __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory")
+#define local_irq_restore(x) __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory", "cc")
+#else
+#define local_irq_save(x) do {} while(0)
+#define local_irq_restore(x) do {} while(0)
+#endif
+
+#define atomic_inc(x) *(x)+=1
+#define atomic_dec(x) *(x)-=1
+#define atomic_dec_and_test(x) (*(x)-=1,(*(x))==0)
+#define atomic_set(x,a) *(x)=a
+#define atomic_read(x) *(x)
+#define ATOMIC_INIT(x) (x)
+
+#define down(x) do {} while(0)
+#define up(x) do {} while(0)
+#define down_trylock(a) 0
+
+#define down_read(a) do {} while(0)
+#define up_read(a) do {} while(0)
+
+#define DECLARE_WAIT_QUEUE_HEAD(x) KEVENT x
+
+#define DECLARE_COMPLETION(x) struct completion x
+
+/* driver */
+
+#define driver_unregister(a) do {} while(0)
+#define put_device(a) do {} while(0)
+
+
+/* PCI */
+#define to_pci_dev(n) container_of(n, struct pci_dev, dev)
+
+#define pci_pool_create(a,b,c,d,e) (void*)1
+
+#define pci_pool_alloc(a,b,c) my_pci_pool_alloc(a,b,c)
+
+static void __inline__ *my_pci_pool_alloc(void* pool, size_t size,
+ dma_addr_t *dma_handle)
+{
+ void* a;
+ a=kmalloc(size,0); //FIXME
+#ifdef MODULE
+ *dma_handle=((u32)a)&0xfffffff;
+#else
+ *dma_handle=(u32)a;
+#endif
+ return a;
+}
+
+
+#define pci_pool_free(a,b,c) kfree(b)
+#define pci_alloc_consistent(a,b,c) my_pci_alloc_consistent(a,b,c)
+
+static void __inline__ *my_pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+ dma_addr_t *dma_handle)
+{
+ void* a;
+
+ a=kmalloc(size+256,0); //FIXME
+ a=(void*)(((int)a+255)&~255); // 256 alignment
+ *dma_handle=((u32)a)&0xfffffff;
+
+ return a;
+}
+
+#define pci_free_consistent(a,b,c,d) kfree(c)
+#define pci_pool_destroy(a) do {} while(0)
+
+#define pci_module_init(x) my_pci_module_init(x)
+int my_pci_module_init(struct pci_driver *x);
+
+#define pci_unregister_driver(a) do {} while(0)
+
+#define pci_write_config_word(a,b,c) my_pci_write_config_word(a,b,c)
+
+#define bus_register(a) do {} while(0)
+#define bus_unregister(a) do {} while(0)
+
+/* DMA */
+//#define dma_pool_alloc(a,b,c) my_dma_pool_alloc((a),(b),(c))
+#define dma_pool_alloc(a,b,c) pci_pool_alloc(a,b,c)
+#define dma_pool_create(a,b,c,d,e) pci_pool_create(a,b,c,d,e)
+#define dma_pool_free(a,b,c) pci_pool_free(a,b,c)
+#define dma_pool_destroy(a) pci_pool_destroy(a)
+
+#define dma_alloc_coherent(a,b,c,d) NULL
+#define dma_free_coherent(a,b,c,d) do {} while(0)
+
+#define dma_map_single(a,b,c,d) ((u32)(b)&0xfffffff)
+#define dma_unmap_single(a,b,c,d) do {} while(0)
+#define pci_unmap_single(a,b,c,d) do {} while(0)
+#define dma_sync_single(a,b,c,d) do {} while(0)
+#define dma_sync_sg(a,b,c,d) do {} while(0)
+#define dma_map_sg(a,b,c,d) 0
+#define dma_unmap_sg(a,b,c,d) do {} while(0)
+
+#define usb_create_driverfs_dev_files(a) do {} while(0)
+#define usb_create_driverfs_intf_files(a) do {} while(0)
+#define sg_dma_address(x) ((u32)((x)->page*4096 + (x)->offset))
+#define sg_dma_len(x) ((x)->length)
+
+#define page_address(x) ((void*)(x/4096))
+
+#define DMA_TO_DEVICE 0
+#define DMA_FROM_DEVICE 0
+#define PCI_DMA_TODEVICE
+#define PCI_DMA_FROMDEVICE
+#define PCI_DMA_TODEVICE
+
+#define PCI_ROM_RESOURCE 1
+#define IORESOURCE_IO CM_RESOURCE_PORT_IO
+
+#define DECLARE_WAITQUEUE(a,b) KEVENT a=0
+#define init_waitqueue_head(a) my_init_waitqueue_head(a)
+#define add_wait_queue(a,b) do {} while(0)
+#define remove_wait_queue(a,b) do {} while(0)
+void my_init_waitqueue_head(PKEVENT a);
+
+VOID KeMemoryBarrier(VOID);
+
+#define mb() KeMemoryBarrier()
+#define wmb() __asm__ __volatile__ ("": : :"memory")
+#define rmb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
+
+#define in_interrupt() 0
+
+#define init_completion(x) (x)->done=0
+#define wait_for_completion(x) my_wait_for_completion(x)
+void my_wait_for_completion(struct completion*);
+
+#define IRQ_NONE 0
+#define IRQ_HANDLED 1
+
+#define INIT_WORK(a,b,c) (a)->func=b
+
+#define set_current_state(a) do {} while(0)
+
+#define might_sleep() do {} while(0)
+#define daemonize(a) do {} while(0)
+#define allow_signal(a) do {} while(0)
+#define wait_event_interruptible(x,y) do {} while(0)
+
+#define interruptible_sleep_on(a) my_interruptible_sleep_on(a)
+void my_interruptible_sleep_on(PKEVENT evnt);
+
+#define flush_scheduled_work() do {} while(0)
+#define refrigerator(x) do {} while(0)
+#define signal_pending(x) 1 // fall through threads
+#define complete_and_exit(a,b) return 0
+
+//#define kill_proc(a,b,c) 0
+#define kill_proc(a,b,c) my_kill_proc(a, b, c);
+int my_kill_proc(int pid, int signal, int unk);
+
+#define yield() do {} while(0)
+#define cpu_relax() do {} while(0)
+
+#define WARN_ON(a) do {} while(0)
+
+/*------------------------------------------------------------------------*/
+/* Lookaside lists funcs */
+/*------------------------------------------------------------------------*/
+#define kmem_cache_create(a,b,c,d,e,f) my_kmem_cache_create((a),(b),(c),(d),(e),(f))
+#define kmem_cache_destroy(a) my_kmem_cache_destroy((a))
+#define kmem_cache_alloc(co, flags) my_kmem_cache_alloc((co), (flags))
+#define kmem_cache_free(co, ptr) my_kmem_cache_free((co), (ptr))
+
+kmem_cache_t *my_kmem_cache_create(const char *tag, size_t alloc_size,
+ size_t offset, unsigned long flags,
+ void *ctor,
+ void *dtor);
+
+BOOLEAN my_kmem_cache_destroy(kmem_cache_t *co);
+void *my_kmem_cache_alloc(kmem_cache_t *co, int flags);
+void my_kmem_cache_free(kmem_cache_t *co, void *ptr);
+
+/*------------------------------------------------------------------------*/
+/* Kernel macros */
+/*------------------------------------------------------------------------*/
+
+#define LINUX_VERSION_CODE 0x020572
+#define UTS_SYSNAME "XBOX"
+#define UTS_RELEASE "----"
+
+/* from linux/kernel.h */
+#define max_t(type,x,y) \
+ ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
+
+#define min_t(type,x,y) \
+ ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/* from linux/stddef.h */
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/*------------------------------------------------------------------------*/
+/* Conversion macros */
+/*------------------------------------------------------------------------*/
+
+#define __constant_cpu_to_le32(x) (x)
+#define cpu_to_le16(x) (x)
+#define le16_to_cpu(x) (x)
+#define cpu_to_le32(x) (x)
+#define cpu_to_le32p(x) (*(__u32*)(x))
+#define le32_to_cpup(x) (*(__u32*)(x))
+#define le32_to_cpu(x) ((u32)x)
+#define le16_to_cpus(x) do {} while (0)
+#define le16_to_cpup(x) (*(__u16*)(x))
+#define cpu_to_le16p(x) (*(__u16*)(x))
+
+/*------------------------------------------------------------------------*/
+/* Debug output */
+/*------------------------------------------------------------------------*/
+#ifdef DEBUG_MODE
+#define dev_printk(lvl,x,f,arg...) printk(f, ## arg)
+#define dev_dbg(x,f,arg...) printk(f, ## arg)
+#define dev_info(x,f,arg...) printk(f,## arg)
+#define dev_warn(x,f,arg...) printk(f,## arg)
+#define dev_err(x,f,arg...) printk(f,## arg)
+#define pr_debug(x,f,arg...) printk(f,## arg)
+#define usbprintk printk
+#endif
+
+#ifndef DEBUG_MODE
+#define dev_printk(lvl,x,f,arg...) do {} while (0)
+#define dev_dbg(x,f,arg...) do {} while (0) //printk(f, ## arg)
+#define dev_info(x,f,arg...) do {} while (0)
+#define dev_warn(x,f,arg...) do {} while (0)
+#define dev_err(x,f,arg...) do {} while (0)
+#define pr_debug(x,f,arg...) do {} while (0)
+#define usbprintk
+#endif
+
+
+
+#define PCI_DEVFN(a,b) 0
+#define PCI_SLOT(a) 0
+
+/**
+ * PCI_DEVICE_CLASS - macro used to describe a specific pci device class
+ * @dev_class: the class, subclass, prog-if triple for this device
+ * @dev_class_mask: the class mask for this device
+ *
+ * This macro is used to create a struct pci_device_id that matches a
+ * specific PCI class. The vendor, device, subvendor, and subdevice
+ * fields will be set to PCI_ANY_ID.
+ */
+#define PCI_DEVICE_CLASS(dev_class,dev_class_mask) \
+ .class = (dev_class), .class_mask = (dev_class_mask), \
+ .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, \
+ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+
+
+/*------------------------------------------------------------------------*/
+/* Stuff from kernel */
+/*------------------------------------------------------------------------*/
+
+#include "errno.h"
+#include "bitops.h"
+//#include "linux/pci_ids.h"
+
+/*------------------------------------------------------------------------*/
+/* global variables */
+/*------------------------------------------------------------------------*/
+
+#define jiffies my_jiffies
+extern int my_jiffies;
+#define current my_current
+extern struct dummy_process *my_current;
+
+extern struct list_head interrupt_list;
+
+/*------------------------------------------------------------------------*/
+/* Function prototypes */
+/*------------------------------------------------------------------------*/
+void STDCALL usb_hcd_pci_remove (struct pci_dev *dev);
+
+#define my_wait_ms(x) wait_ms(x)
+
+#define my_udelay(x) wait_ms(x)
+#define udelay(x) my_udelay(x)
+
+#define my_mdelay(x) wait_ms(1+x/1000);
+#define mdelay(x) my_mdelay(x);
+
+#define pci_find_slot(a,b) my_pci_find_slot(a,b)
+struct pci_dev *my_pci_find_slot(int a,int b);
+
+/*------------------------------------------------------------------------*/
+/* Timer management */
+/*------------------------------------------------------------------------*/
+
+#define MAX_TIMERS 20
+extern struct timer_list *main_timer_list[MAX_TIMERS];
+
+static void __inline__ init_timer(struct timer_list* t)
+{
+ INIT_LIST_HEAD(&t->timer_list);
+ t->function=NULL;
+ t->expires=0;
+}
+
+static void __inline__ add_timer(struct timer_list* t)
+{
+ int n;
+ for(n=0;n<MAX_TIMERS;n++)
+ if (main_timer_list[n]==0)
+ {
+ main_timer_list[n]=t;
+ break;
+ }
+}
+
+static void __inline__ del_timer(struct timer_list* t)
+{
+ int n;
+ for(n=0;n<MAX_TIMERS;n++)
+ if (main_timer_list[n]==t)
+ {
+ main_timer_list[n]=0;
+ break;
+ }
+}
+static void __inline__ del_timer_sync(struct timer_list* t)
+{
+ int n;
+ for(n=0;n<MAX_TIMERS;n++)
+ if (main_timer_list[n]==t)
+ {
+ main_timer_list[n]=0;
+ break;
+ }
+
+}
+static void __inline__ mod_timer(struct timer_list* t, int ex)
+{
+ del_timer(t);
+ t->expires=ex;
+ add_timer(t);
+}
+
+#define time_after_eq(a,b) \
+ (((long)(a) - (long)(b) >= 0))
+
+/*------------------------------------------------------------------------*/
+/* Device driver and process related stuff */
+/*------------------------------------------------------------------------*/
+
+static int __inline__ usb_major_init(void){return 0;}
+static void __inline__ usb_major_cleanup(void){}
+static void __inline__ schedule_work(void* p){}
+
+#define device_initialize(x) my_device_initialize(x)
+void my_device_initialize(struct device *dev);
+
+#define get_device(x) my_get_device(x)
+struct device *my_get_device(struct device *dev);
+
+#define device_add(x) my_device_add(x)
+int my_device_add(struct device *dev);
+
+#define driver_register(x) my_driver_register(x)
+int my_driver_register(struct device_driver *driver);
+
+#define device_unregister(a) my_device_unregister(a)
+int my_device_unregister(struct device *dev);
+
+#define DEVICE_ATTR(a,b,c,d) int xxx_##a
+#define device_create_file(a,b) do {} while(0)
+#define device_remove_file(a,b) do {} while(0)
+
+#define schedule_timeout(x) my_schedule_timeout(x)
+int my_schedule_timeout(int x);
+
+#define wake_up(x) my_wake_up(x)
+void my_wake_up(PKEVENT);
+
+// cannot be mapped via macro due to collision with urb->complete
+static void __inline__ complete(struct completion *p)
+{
+ /* Wake up x->wait */
+ p->done++;
+ wake_up(&p->wait);
+}
+
+#define kernel_thread(a,b,c) my_kernel_thread(a,b,c)
+int my_kernel_thread(int STDCALL (*handler)(void*), void* parm, int flags);
+
+/*------------------------------------------------------------------------*/
+/* PCI, simple and inlined... */
+/*------------------------------------------------------------------------*/
+#include "pci_hal.c"
+
+/*------------------------------------------------------------------------*/
+/* IRQ handling */
+/*------------------------------------------------------------------------*/
+
+#define request_irq(a,b,c,d,e) my_request_irq(a,b,c,d,e)
+int my_request_irq(unsigned int irq,
+ int (*handler)(int, void *, struct pt_regs *),
+ unsigned long mode, const char *desc, void *data);
+
+#define free_irq(a,b) my_free_irq(a,b)
+int free_irq(int irq, void* p);
+
+
+
+struct my_irqs {
+ int (*handler)(int, void *, struct pt_regs *);
+ int irq;
+ void* data;
+};
+
+#define MAX_IRQS 8
+
+// Exported to top level
+
+void handle_irqs(int irq);
+void inc_jiffies(int);
+void init_wrapper(struct pci_dev *pci_dev);
+void do_all_timers(void);
+
+#define __KERNEL_DS 0x18
+
+
-#ifndef _BOOT_LIST_H\r
-#define _BOOT_LIST_H\r
-\r
-/*\r
- * Simple doubly linked list implementation.\r
- *\r
- * Some of the internal functions ("__xxx") are useful when\r
- * manipulating whole lists rather than single entries, as\r
- * sometimes we already know the next/prev entries and we can\r
- * generate better code by using them directly rather than\r
- * using the generic single-entry routines.\r
- */\r
-\r
-struct list_head {\r
- struct list_head *next, *prev;\r
-};\r
-\r
-#define LIST_HEAD_INIT(name) { &(name), &(name) }\r
-\r
-#define LIST_HEAD(name) \\r
- struct list_head name = LIST_HEAD_INIT(name)\r
-\r
-#define INIT_LIST_HEAD(ptr) do { \\r
- (ptr)->next = (ptr); (ptr)->prev = (ptr); \\r
-} while (0)\r
-\r
-/*\r
- * Insert a new entry between two known consecutive entries. \r
- *\r
- * This is only for internal list manipulation where we know\r
- * the prev/next entries already!\r
- */\r
-static inline void __list_add(struct list_head *new,\r
- struct list_head *prev,\r
- struct list_head *next)\r
-{\r
- next->prev = new;\r
- new->next = next;\r
- new->prev = prev;\r
- prev->next = new;\r
-}\r
-\r
-/**\r
- * list_add - add a new entry\r
- * @new: new entry to be added\r
- * @head: list head to add it after\r
- *\r
- * Insert a new entry after the specified head.\r
- * This is good for implementing stacks.\r
- */\r
-static inline void list_add(struct list_head *new, struct list_head *head)\r
-{\r
- __list_add(new, head, head->next);\r
-}\r
-\r
-/**\r
- * list_add_tail - add a new entry\r
- * @new: new entry to be added\r
- * @head: list head to add it before\r
- *\r
- * Insert a new entry before the specified head.\r
- * This is useful for implementing queues.\r
- */\r
-static inline void list_add_tail(struct list_head *new, struct list_head *head)\r
-{\r
- __list_add(new, head->prev, head);\r
-}\r
-\r
-/*\r
- * Delete a list entry by making the prev/next entries\r
- * point to each other.\r
- *\r
- * This is only for internal list manipulation where we know\r
- * the prev/next entries already!\r
- */\r
-static inline void __list_del(struct list_head *prev, struct list_head *next)\r
-{\r
- next->prev = prev;\r
- prev->next = next;\r
-}\r
-\r
-/**\r
- * list_del - deletes entry from list.\r
- * @entry: the element to delete from the list.\r
- * Note: list_empty on entry does not return true after this, the entry is in an undefined state.\r
- */\r
-static inline void list_del(struct list_head *entry)\r
-{\r
- __list_del(entry->prev, entry->next);\r
- entry->next = (void *) 0;\r
- entry->prev = (void *) 0;\r
-}\r
-\r
-/**\r
- * list_del_init - deletes entry from list and reinitialize it.\r
- * @entry: the element to delete from the list.\r
- */\r
-static inline void list_del_init(struct list_head *entry)\r
-{\r
- __list_del(entry->prev, entry->next);\r
- INIT_LIST_HEAD(entry); \r
-}\r
-\r
-/**\r
- * list_move - delete from one list and add as another's head\r
- * @list: the entry to move\r
- * @head: the head that will precede our entry\r
- */\r
-static inline void list_move(struct list_head *list, struct list_head *head)\r
-{\r
- __list_del(list->prev, list->next);\r
- list_add(list, head);\r
-}\r
-\r
-/**\r
- * list_move_tail - delete from one list and add as another's tail\r
- * @list: the entry to move\r
- * @head: the head that will follow our entry\r
- */\r
-static inline void list_move_tail(struct list_head *list,\r
- struct list_head *head)\r
-{\r
- __list_del(list->prev, list->next);\r
- list_add_tail(list, head);\r
-}\r
-\r
-/**\r
- * list_empty - tests whether a list is empty\r
- * @head: the list to test.\r
- */\r
-static inline int list_empty(struct list_head *head)\r
-{\r
- return head->next == head;\r
-}\r
-\r
-static inline void __list_splice(struct list_head *list,\r
- struct list_head *head)\r
-{\r
- struct list_head *first = list->next;\r
- struct list_head *last = list->prev;\r
- struct list_head *at = head->next;\r
-\r
- first->prev = head;\r
- head->next = first;\r
-\r
- last->next = at;\r
- at->prev = last;\r
-}\r
-\r
-/**\r
- * list_splice - join two lists\r
- * @list: the new list to add.\r
- * @head: the place to add it in the first list.\r
- */\r
-static inline void list_splice(struct list_head *list, struct list_head *head)\r
-{\r
- if (!list_empty(list))\r
- __list_splice(list, head);\r
-}\r
-\r
-/**\r
- * list_splice_init - join two lists and reinitialise the emptied list.\r
- * @list: the new list to add.\r
- * @head: the place to add it in the first list.\r
- *\r
- * The list at @list is reinitialised\r
- */\r
-static inline void list_splice_init(struct list_head *list,\r
- struct list_head *head)\r
-{\r
- if (!list_empty(list)) {\r
- __list_splice(list, head);\r
- INIT_LIST_HEAD(list);\r
- }\r
-}\r
-\r
-/**\r
- * list_entry - get the struct for this entry\r
- * @ptr: the &struct list_head pointer.\r
- * @type: the type of the struct this is embedded in.\r
- * @member: the name of the list_struct within the struct.\r
- */\r
-#define list_entry(ptr, type, member) \\r
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))\r
-\r
-/**\r
- * list_for_each - iterate over a list\r
- * @pos: the &struct list_head to use as a loop counter.\r
- * @head: the head for your list.\r
- */\r
-#define list_for_each(pos, head) \\r
- for (pos = (head)->next; pos != (head); \\r
- pos = pos->next)\r
-/**\r
- * list_for_each_prev - iterate over a list backwards\r
- * @pos: the &struct list_head to use as a loop counter.\r
- * @head: the head for your list.\r
- */\r
-#define list_for_each_prev(pos, head) \\r
- for (pos = (head)->prev; pos != (head); \\r
- pos = pos->prev)\r
- \r
-/**\r
- * list_for_each_safe - iterate over a list safe against removal of list entry\r
- * @pos: the &struct list_head to use as a loop counter.\r
- * @n: another &struct list_head to use as temporary storage\r
- * @head: the head for your list.\r
- */\r
-#define list_for_each_safe(pos, n, head) \\r
- for (pos = (head)->next, n = pos->next; pos != (head); \\r
- pos = n, n = pos->next)\r
-\r
-/**\r
- * list_for_each_entry - iterate over list of given type\r
- * @pos: the type * to use as a loop counter.\r
- * @head: the head for your list.\r
- * @member: the name of the list_struct within the struct.\r
- */\r
-#define list_for_each_entry(pos, head, member) \\r
- for (pos = list_entry((head)->next, typeof(*pos), member) \\r
- &pos->member != (head); \\r
- pos = list_entry(pos->member.next, typeof(*pos), member))\r
-\r
-#endif\r
+#ifndef _BOOT_LIST_H
+#define _BOOT_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = (void *) 0;
+ entry->prev = (void *) 0;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next)
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); \
+ pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member) \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+#endif
-// PCI -> HAL interface\r
-// this file is part of linux_wrapper.h\r
-\r
-//FIXME: Move this file, make its definitions more general\r
-#include "../host/ohci_main.h"\r
-\r
-/*\r
- Initialize device before it's used by a driver. Ask low-level code to enable I/O and memory.\r
- Wake up the device if it was suspended. Beware, this function can fail. \r
- */\r
-static int __inline__ pci_enable_device(struct pci_dev *dev)\r
-{\r
- DPRINT1("pci_enable_device() called...\n");\r
- return 0;\r
-}\r
-\r
-// Get physical address where resource x resides\r
-static PHYSICAL_ADDRESS __inline__ pci_resource_start (struct pci_dev *dev, int x)\r
-{\r
- POHCI_DEVICE_EXTENSION dev_ext = (POHCI_DEVICE_EXTENSION)dev->dev_ext;\r
- DPRINT1("pci_resource_start() called, x=0x%x\n", x);\r
- \r
- //FIXME: Take x into account\r
- return dev_ext->BaseAddress;\r
- //return dev->base[x];\r
-}\r
-\r
-// ???\r
-static unsigned long __inline__ pci_resource_len (struct pci_dev *dev, int x)\r
-{\r
- POHCI_DEVICE_EXTENSION ext = (POHCI_DEVICE_EXTENSION)dev->dev_ext;\r
-\r
- DPRINT1("pci_resource_len() called, x=0x%x\n", x);\r
-\r
- //FIXME: Take x into account\r
- return ext->BaseAddrLength;\r
-}\r
-\r
-// ???\r
-static int __inline__ pci_resource_flags(struct pci_dev *dev, int x)\r
-{\r
- POHCI_DEVICE_EXTENSION ext = (POHCI_DEVICE_EXTENSION)dev->dev_ext;\r
- \r
- DPRINT1("pci_resource_flags() called, x=0x%x\n", x);\r
- \r
- //FIXME: Take x into account\r
- return ext->Flags;\r
-}\r
-\r
-/*\r
- Enables bus-mastering for device dev\r
-*/\r
-static int __inline__ pci_set_master(struct pci_dev *dev) {return 0;}\r
-\r
-// Store pointer to data for this device\r
-static int __inline__ pci_set_drvdata(struct pci_dev *dev, void* d)\r
-{\r
- DPRINT1("pci_set_drvdata() called...\n");\r
- dev->data=(void*)d;\r
- return 0;\r
-}\r
-\r
-// Get pointer to previously saved data\r
-static void __inline__ *pci_get_drvdata(struct pci_dev *dev)\r
-{\r
- DPRINT1("pci_get_drvdata() called...\n");\r
- return dev->data;\r
-}\r
-\r
-\r
-/*\r
- ===========================================================================\r
- I/O mem related stuff below\r
-*/\r
-\r
-/*\r
-Allocate I/O memory region. \r
-\r
-Parameters:\r
-start begin of region \r
-n length of region \r
-name name of requester \r
-*/\r
-static int __inline__ request_region(PHYSICAL_ADDRESS addr, unsigned long len, const char * d)\r
-{\r
- DPRINT1("request_region(): addr=0x%lx, len=0x%lx\n", addr.u.LowPart, len);\r
- return ~0;\r
-}\r
-\r
-/*\r
-Unmap I/O memory from kernel address space. \r
-\r
-Parameters:\r
-addr virtual start address \r
-\r
-*/\r
-static int __inline__ iounmap(void* p)\r
-{\r
- DPRINT1("iounmap(): p=0x%x. FIXME - how to obtain len of mapped region?\n", p);\r
- \r
- //MmUnnapIoSpace(p);\r
- \r
- return 0;\r
-}\r
-\r
-/*\r
-Release I/O port region. \r
-\r
-Parameters:\r
-start begin of region \r
-n length of region \r
-*/\r
-static int __inline__ release_region(PHYSICAL_ADDRESS addr, unsigned long len)\r
-{\r
- DPRINT1("release_region(): addr=0x%lx, len=0x%lx\n", addr.u.LowPart, len);\r
- return 0;\r
-}\r
-\r
-/*\r
-Allocate I/O memory region. \r
-\r
-Parameters:\r
-start begin of region \r
-n length of region \r
-name name of requester \r
-*/\r
-static int __inline__ request_mem_region(PHYSICAL_ADDRESS addr, unsigned long len, const char * d)\r
-{\r
- DPRINT1("request_mem_region(): addr=0x%x, len=0x%x\n", addr, len);\r
- return 1;\r
-}\r
-\r
-/*\r
-Remap I/O memory into kernel address space (no cache). \r
-\r
-Parameters:\r
-phys_addr begin of physical address range \r
-size size of physical address range \r
-\r
-Returns:\r
-virtual start address of mapped range\r
-*/\r
-static void __inline__ *ioremap_nocache(PHYSICAL_ADDRESS addr, unsigned long len)\r
-{\r
- // MmMapIoSpace with NoCache param\r
- DPRINT1("ioremap_nocache(): addr=0x%x, len=0x%x\n", addr, len);\r
-\r
- return MmMapIoSpace(addr, len, MmNonCached);\r
-}\r
-\r
-/*\r
-Release I/O memory region. \r
-\r
-Parameters:\r
-start begin of region \r
-n length of region \r
-*/\r
-static int __inline__ release_mem_region(PHYSICAL_ADDRESS addr, unsigned long len)\r
-{\r
- DPRINT1("release_mem_region(): addr=0x%x, len=0x%x\n", addr, len);\r
- return 0;\r
-}\r
+// PCI -> HAL interface
+// this file is part of linux_wrapper.h
+
+//FIXME: Move this file, make its definitions more general
+#include "../host/ohci_main.h"
+
+/*
+ Initialize device before it's used by a driver. Ask low-level code to enable I/O and memory.
+ Wake up the device if it was suspended. Beware, this function can fail.
+ */
+static int __inline__ pci_enable_device(struct pci_dev *dev)
+{
+ DPRINT1("pci_enable_device() called...\n");
+ return 0;
+}
+
+// Get physical address where resource x resides
+static PHYSICAL_ADDRESS __inline__ pci_resource_start (struct pci_dev *dev, int x)
+{
+ POHCI_DEVICE_EXTENSION dev_ext = (POHCI_DEVICE_EXTENSION)dev->dev_ext;
+ DPRINT1("pci_resource_start() called, x=0x%x\n", x);
+
+ //FIXME: Take x into account
+ return dev_ext->BaseAddress;
+ //return dev->base[x];
+}
+
+// ???
+static unsigned long __inline__ pci_resource_len (struct pci_dev *dev, int x)
+{
+ POHCI_DEVICE_EXTENSION ext = (POHCI_DEVICE_EXTENSION)dev->dev_ext;
+
+ DPRINT1("pci_resource_len() called, x=0x%x\n", x);
+
+ //FIXME: Take x into account
+ return ext->BaseAddrLength;
+}
+
+// ???
+static int __inline__ pci_resource_flags(struct pci_dev *dev, int x)
+{
+ POHCI_DEVICE_EXTENSION ext = (POHCI_DEVICE_EXTENSION)dev->dev_ext;
+
+ DPRINT1("pci_resource_flags() called, x=0x%x\n", x);
+
+ //FIXME: Take x into account
+ return ext->Flags;
+}
+
+/*
+ Enables bus-mastering for device dev
+*/
+static int __inline__ pci_set_master(struct pci_dev *dev) {return 0;}
+
+// Store pointer to data for this device
+static int __inline__ pci_set_drvdata(struct pci_dev *dev, void* d)
+{
+ DPRINT1("pci_set_drvdata() called...\n");
+ dev->data=(void*)d;
+ return 0;
+}
+
+// Get pointer to previously saved data
+static void __inline__ *pci_get_drvdata(struct pci_dev *dev)
+{
+ DPRINT1("pci_get_drvdata() called...\n");
+ return dev->data;
+}
+
+
+/*
+ ===========================================================================
+ I/O mem related stuff below
+*/
+
+/*
+Allocate I/O memory region.
+
+Parameters:
+start begin of region
+n length of region
+name name of requester
+*/
+static int __inline__ request_region(PHYSICAL_ADDRESS addr, unsigned long len, const char * d)
+{
+ DPRINT1("request_region(): addr=0x%lx, len=0x%lx\n", addr.u.LowPart, len);
+ return ~0;
+}
+
+/*
+Unmap I/O memory from kernel address space.
+
+Parameters:
+addr virtual start address
+
+*/
+static int __inline__ iounmap(void* p)
+{
+ DPRINT1("iounmap(): p=0x%x. FIXME - how to obtain len of mapped region?\n", p);
+
+ //MmUnnapIoSpace(p);
+
+ return 0;
+}
+
+/*
+Release I/O port region.
+
+Parameters:
+start begin of region
+n length of region
+*/
+static int __inline__ release_region(PHYSICAL_ADDRESS addr, unsigned long len)
+{
+ DPRINT1("release_region(): addr=0x%lx, len=0x%lx\n", addr.u.LowPart, len);
+ return 0;
+}
+
+/*
+Allocate I/O memory region.
+
+Parameters:
+start begin of region
+n length of region
+name name of requester
+*/
+static int __inline__ request_mem_region(PHYSICAL_ADDRESS addr, unsigned long len, const char * d)
+{
+ DPRINT1("request_mem_region(): addr=0x%x, len=0x%x\n", addr, len);
+ return 1;
+}
+
+/*
+Remap I/O memory into kernel address space (no cache).
+
+Parameters:
+phys_addr begin of physical address range
+size size of physical address range
+
+Returns:
+virtual start address of mapped range
+*/
+static void __inline__ *ioremap_nocache(PHYSICAL_ADDRESS addr, unsigned long len)
+{
+ // MmMapIoSpace with NoCache param
+ DPRINT1("ioremap_nocache(): addr=0x%x, len=0x%x\n", addr, len);
+
+ return MmMapIoSpace(addr, len, MmNonCached);
+}
+
+/*
+Release I/O memory region.
+
+Parameters:
+start begin of region
+n length of region
+*/
+static int __inline__ release_mem_region(PHYSICAL_ADDRESS addr, unsigned long len)
+{
+ DPRINT1("release_mem_region(): addr=0x%x, len=0x%x\n", addr, len);
+ return 0;
+}
-#ifndef PCI_IDS__H\r
-#define PCI_IDS__H\r
-\r
-#define PCI_VENDOR_ID_NS 0x100b\r
-#define PCI_VENDOR_ID_AMD 0x1022\r
-#define PCI_VENDOR_ID_OPTI 0x1045\r
-#define PCI_VENDOR_ID_VIA 0x1106\r
-#define PCI_VENDOR_ID_INTEL 0x8086\r
-\r
-#define PCI_DEVICE_ID_NS_87560_LIO 0x000e\r
-#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112\r
-\r
-#define PCI_CLASS_SERIAL_USB (PCI_CLASS_SERIAL_BUS_CTLR << 8 + PCI_SUBCLASS_SB_USB)\r
-\r
-#endif\r
-\r
+#ifndef PCI_IDS__H
+#define PCI_IDS__H
+
+#define PCI_VENDOR_ID_NS 0x100b
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_VENDOR_ID_OPTI 0x1045
+#define PCI_VENDOR_ID_VIA 0x1106
+#define PCI_VENDOR_ID_INTEL 0x8086
+
+#define PCI_DEVICE_ID_NS_87560_LIO 0x000e
+#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
+
+#define PCI_CLASS_SERIAL_USB (PCI_CLASS_SERIAL_BUS_CTLR << 8 + PCI_SUBCLASS_SB_USB)
+
+#endif
+
-#ifndef __LINUX_USB_H\r
-#define __LINUX_USB_H\r
-\r
-\r
-#include "usb_ch9.h"\r
-\r
-#define USB_MAJOR 180\r
-\r
-\r
-#ifdef __KERNEL__\r
-#if 0\r
-#include <linux/config.h>\r
-#include <linux/errno.h> /* for -ENODEV */\r
-#include <linux/delay.h> /* for mdelay() */\r
-#include <linux/interrupt.h> /* for in_interrupt() */\r
-#include <linux/list.h> /* for struct list_head */\r
-#include <linux/device.h> /* for struct device */\r
-#include <linux/fs.h> /* for struct file_operations */\r
-#include <linux/completion.h> /* for struct completion */\r
-#include <linux/sched.h> /* for current && schedule_timeout */\r
-\r
-\r
-static __inline__ void wait_ms(unsigned int ms)\r
-{\r
- if(!in_interrupt()) {\r
- current->state = TASK_UNINTERRUPTIBLE;\r
- schedule_timeout(1 + ms * HZ / 1000);\r
- }\r
- else\r
- mdelay(ms);\r
-}\r
-#endif\r
-struct usb_device;\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * Host-side wrappers for standard USB descriptors ... these are parsed\r
- * from the data provided by devices. Parsing turns them from a flat\r
- * sequence of descriptors into a hierarchy:\r
- *\r
- * - devices have one (usually) or more configs;\r
- * - configs have one (often) or more interfaces;\r
- * - interfaces have one (usually) or more settings;\r
- * - each interface setting has zero or (usually) more endpoints.\r
- *\r
- * And there might be other descriptors mixed in with those.\r
- *\r
- * Devices may also have class-specific or vendor-specific descriptors.\r
- */\r
-\r
-/* host-side wrapper for parsed endpoint descriptors */\r
-struct usb_host_endpoint {\r
- struct usb_endpoint_descriptor desc;\r
-\r
- unsigned char *extra; /* Extra descriptors */\r
- int extralen;\r
-};\r
-\r
-/* host-side wrapper for one interface setting's parsed descriptors */\r
-struct usb_host_interface {\r
- struct usb_interface_descriptor desc;\r
-\r
- /* array of desc.bNumEndpoint endpoints associated with this\r
- * interface setting. these will be in no particular order.\r
- */\r
- struct usb_host_endpoint *endpoint;\r
-\r
- unsigned char *extra; /* Extra descriptors */\r
- int extralen;\r
-};\r
-\r
-/**\r
- * struct usb_interface - what usb device drivers talk to\r
- * @altsetting: array of interface descriptors, one for each alternate\r
- * setting that may be selected. Each one includes a set of\r
- * endpoint configurations and will be in numberic order,\r
- * 0..num_altsetting.\r
- * @num_altsetting: number of altsettings defined.\r
- * @act_altsetting: index of current altsetting. this number is always\r
- * less than num_altsetting. after the device is configured, each\r
- * interface uses its default setting of zero.\r
- * @max_altsetting:\r
- * @minor: the minor number assigned to this interface, if this\r
- * interface is bound to a driver that uses the USB major number.\r
- * If this interface does not use the USB major, this field should\r
- * be unused. The driver should set this value in the probe()\r
- * function of the driver, after it has been assigned a minor\r
- * number from the USB core by calling usb_register_dev().\r
- * @dev: driver model's view of this device\r
- * @class_dev: driver model's class view of this device.\r
- *\r
- * USB device drivers attach to interfaces on a physical device. Each\r
- * interface encapsulates a single high level function, such as feeding\r
- * an audio stream to a speaker or reporting a change in a volume control.\r
- * Many USB devices only have one interface. The protocol used to talk to\r
- * an interface's endpoints can be defined in a usb "class" specification,\r
- * or by a product's vendor. The (default) control endpoint is part of\r
- * every interface, but is never listed among the interface's descriptors.\r
- *\r
- * The driver that is bound to the interface can use standard driver model\r
- * calls such as dev_get_drvdata() on the dev member of this structure.\r
- *\r
- * Each interface may have alternate settings. The initial configuration\r
- * of a device sets the first of these, but the device driver can change\r
- * that setting using usb_set_interface(). Alternate settings are often\r
- * used to control the the use of periodic endpoints, such as by having\r
- * different endpoints use different amounts of reserved USB bandwidth.\r
- * All standards-conformant USB devices that use isochronous endpoints\r
- * will use them in non-default settings.\r
- */\r
-struct usb_interface {\r
- /* array of alternate settings for this interface.\r
- * these will be in numeric order, 0..num_altsettting\r
- */\r
- struct usb_host_interface *altsetting;\r
-\r
- unsigned act_altsetting; /* active alternate setting */\r
- unsigned num_altsetting; /* number of alternate settings */\r
- unsigned max_altsetting; /* total memory allocated */\r
-\r
- struct usb_driver *driver; /* driver */\r
- int minor; /* minor number this interface is bound to */\r
- struct device dev; /* interface specific device info */\r
- struct class_device class_dev;\r
-};\r
-#define to_usb_interface(d) container_of(d, struct usb_interface, dev)\r
-#define class_dev_to_usb_interface(d) container_of(d, struct usb_interface, class_dev)\r
-#define interface_to_usbdev(intf) \\r
- container_of(intf->dev.parent, struct usb_device, dev)\r
-\r
-static inline void *usb_get_intfdata (struct usb_interface *intf)\r
-{\r
- return dev_get_drvdata (&intf->dev);\r
-}\r
-\r
-static inline void usb_set_intfdata (struct usb_interface *intf, void *data)\r
-{\r
- dev_set_drvdata(&intf->dev, data);\r
-}\r
-\r
-/* USB_DT_CONFIG: Configuration descriptor information.\r
- *\r
- * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the\r
- * descriptor type is different. Highspeed-capable devices can look\r
- * different depending on what speed they're currently running. Only\r
- * devices with a USB_DT_DEVICE_QUALIFIER have an OTHER_SPEED_CONFIG.\r
- */\r
-struct usb_host_config {\r
- struct usb_config_descriptor desc;\r
-\r
- /* the interfaces associated with this configuration\r
- * these will be in numeric order, 0..desc.bNumInterfaces\r
- */\r
- struct usb_interface *interface;\r
-\r
- unsigned char *extra; /* Extra descriptors */\r
- int extralen;\r
-};\r
-\r
-// FIXME remove; exported only for drivers/usb/misc/auserwald.c\r
-// prefer usb_device->epnum[0..31]\r
-extern struct usb_endpoint_descriptor *\r
- usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum);\r
-\r
-int __usb_get_extra_descriptor(char *buffer, unsigned size,\r
- unsigned char type, void **ptr);\r
-#define usb_get_extra_descriptor(ifpoint,type,ptr)\\r
- __usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,\\r
- type,(void**)ptr)\r
-\r
-/* -------------------------------------------------------------------------- */\r
-\r
-struct usb_operations;\r
-\r
-/* USB device number allocation bitmap */\r
-struct usb_devmap {\r
- unsigned long devicemap[128 / (8*sizeof(unsigned long))];\r
-};\r
-\r
-/*\r
- * Allocated per bus (tree of devices) we have:\r
- */\r
-struct usb_bus {\r
- struct device *controller; /* host/master side hardware */\r
- int busnum; /* Bus number (in order of reg) */\r
- char *bus_name; /* stable id (PCI slot_name etc) */\r
-\r
- int devnum_next; /* Next open device number in round-robin allocation */\r
-\r
- struct usb_devmap devmap; /* device address allocation map */\r
- struct usb_operations *op; /* Operations (specific to the HC) */\r
- struct usb_device *root_hub; /* Root hub */\r
- struct list_head bus_list; /* list of busses */\r
- void *hcpriv; /* Host Controller private data */\r
-\r
- int bandwidth_allocated; /* on this bus: how much of the time\r
- * reserved for periodic (intr/iso)\r
- * requests is used, on average?\r
- * Units: microseconds/frame.\r
- * Limits: Full/low speed reserve 90%,\r
- * while high speed reserves 80%.\r
- */\r
- int bandwidth_int_reqs; /* number of Interrupt requests */\r
- int bandwidth_isoc_reqs; /* number of Isoc. requests */\r
-\r
- struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */\r
- struct dentry *usbdevfs_dentry; /* usbdevfs dentry entry for the bus */\r
-\r
- atomic_t refcnt;\r
-};\r
-\r
-\r
-/* -------------------------------------------------------------------------- */\r
-\r
-/* This is arbitrary.\r
- * From USB 2.0 spec Table 11-13, offset 7, a hub can\r
- * have up to 255 ports. The most yet reported is 10.\r
- */\r
-#define USB_MAXCHILDREN (16)\r
-\r
-struct usb_tt;\r
-\r
-struct usb_device {\r
- int devnum; /* Address on USB bus */\r
- char devpath [16]; /* Use in messages: /port/port/... */\r
- enum usb_device_state state; /* configured, not attached, etc */\r
- enum usb_device_speed speed; /* high/full/low (or error) */\r
-\r
- struct usb_tt *tt; /* low/full speed dev, highspeed hub */\r
- int ttport; /* device port on that tt hub */\r
-\r
- struct semaphore serialize;\r
-\r
- unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */\r
- unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; */\r
- /* [0] = IN, [1] = OUT */\r
- int epmaxpacketin[16]; /* INput endpoint specific maximums */\r
- int epmaxpacketout[16]; /* OUTput endpoint specific maximums */\r
-\r
- struct usb_device *parent; /* our hub, unless we're the root */\r
- struct usb_bus *bus; /* Bus we're part of */\r
-\r
- struct device dev; /* Generic device interface */\r
-\r
- struct usb_device_descriptor descriptor;/* Descriptor */\r
- struct usb_host_config *config; /* All of the configs */\r
- struct usb_host_config *actconfig;/* the active configuration */\r
-\r
- char **rawdescriptors; /* Raw descriptors for each config */\r
-\r
- int have_langid; /* whether string_langid is valid yet */\r
- int string_langid; /* language ID for strings */\r
-\r
- void *hcpriv; /* Host Controller private data */\r
- \r
- struct list_head filelist;\r
- struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */\r
- struct dentry *usbdevfs_dentry; /* usbdevfs dentry entry for the device */\r
-\r
- /*\r
- * Child devices - these can be either new devices\r
- * (if this is a hub device), or different instances\r
- * of this same device.\r
- *\r
- * Each instance needs its own set of data structures.\r
- */\r
-\r
- int maxchild; /* Number of ports if hub */\r
- struct usb_device *children[USB_MAXCHILDREN];\r
-};\r
-#define to_usb_device(d) container_of(d, struct usb_device, dev)\r
-\r
-extern struct usb_device STDCALL *usb_alloc_dev(struct usb_device *parent, struct usb_bus *);\r
-extern struct usb_device STDCALL *usb_get_dev(struct usb_device *dev);\r
-extern void STDCALL usb_put_dev(struct usb_device *dev);\r
-\r
-/* mostly for devices emulating SCSI over USB */\r
-extern int usb_reset_device(struct usb_device *dev);\r
-\r
-extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);\r
-\r
-/* for drivers using iso endpoints */\r
-extern int usb_get_current_frame_number (struct usb_device *usb_dev);\r
-\r
-/* used these for multi-interface device registration */\r
-extern void usb_driver_claim_interface(struct usb_driver *driver,\r
- struct usb_interface *iface, void* priv);\r
-extern int usb_interface_claimed(struct usb_interface *iface);\r
-extern void usb_driver_release_interface(struct usb_driver *driver,\r
- struct usb_interface *iface);\r
-const struct usb_device_id *usb_match_id(struct usb_interface *interface,\r
- const struct usb_device_id *id);\r
-\r
-extern struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor);\r
-extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum);\r
-\r
-\r
-/**\r
- * usb_make_path - returns stable device path in the usb tree\r
- * @dev: the device whose path is being constructed\r
- * @buf: where to put the string\r
- * @size: how big is "buf"?\r
- *\r
- * Returns length of the string (> 0) or negative if size was too small.\r
- *\r
- * This identifier is intended to be "stable", reflecting physical paths in\r
- * hardware such as physical bus addresses for host controllers or ports on\r
- * USB hubs. That makes it stay the same until systems are physically\r
- * reconfigured, by re-cabling a tree of USB devices or by moving USB host\r
- * controllers. Adding and removing devices, including virtual root hubs\r
- * in host controller driver modules, does not change these path identifers;\r
- * neither does rebooting or re-enumerating. These are more useful identifiers\r
- * than changeable ("unstable") ones like bus numbers or device addresses.\r
- *\r
- * With a partial exception for devices connected to USB 2.0 root hubs, these\r
- * identifiers are also predictable. So long as the device tree isn't changed,\r
- * plugging any USB device into a given hub port always gives it the same path.\r
- * Because of the use of "companion" controllers, devices connected to ports on\r
- * USB 2.0 root hubs (EHCI host controllers) will get one path ID if they are\r
- * high speed, and a different one if they are full or low speed.\r
- */\r
-static inline int usb_make_path (struct usb_device *dev, char *buf, size_t size)\r
-{\r
- int actual;\r
- actual = snprintf (buf, size, "usb-%s-%s", dev->bus->bus_name, dev->devpath);\r
- return (actual >= size) ? -1 : actual;\r
-}\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-#define USB_DEVICE_ID_MATCH_DEVICE (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)\r
-#define USB_DEVICE_ID_MATCH_DEV_RANGE (USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)\r
-#define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)\r
-#define USB_DEVICE_ID_MATCH_DEV_INFO \\r
- (USB_DEVICE_ID_MATCH_DEV_CLASS | USB_DEVICE_ID_MATCH_DEV_SUBCLASS | USB_DEVICE_ID_MATCH_DEV_PROTOCOL)\r
-#define USB_DEVICE_ID_MATCH_INT_INFO \\r
- (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | USB_DEVICE_ID_MATCH_INT_PROTOCOL)\r
-\r
-/**\r
- * USB_DEVICE - macro used to describe a specific usb device\r
- * @vend: the 16 bit USB Vendor ID\r
- * @prod: the 16 bit USB Product ID\r
- *\r
- * This macro is used to create a struct usb_device_id that matches a\r
- * specific device.\r
- */\r
-#define USB_DEVICE(vend,prod) \\r
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = (vend), .idProduct = (prod)\r
-/**\r
- * USB_DEVICE_VER - macro used to describe a specific usb device with a version range\r
- * @vend: the 16 bit USB Vendor ID\r
- * @prod: the 16 bit USB Product ID\r
- * @lo: the bcdDevice_lo value\r
- * @hi: the bcdDevice_hi value\r
- *\r
- * This macro is used to create a struct usb_device_id that matches a\r
- * specific device, with a version range.\r
- */\r
-#define USB_DEVICE_VER(vend,prod,lo,hi) \\r
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, .idVendor = (vend), .idProduct = (prod), .bcdDevice_lo = (lo), .bcdDevice_hi = (hi)\r
-\r
-/**\r
- * USB_DEVICE_INFO - macro used to describe a class of usb devices\r
- * @cl: bDeviceClass value\r
- * @sc: bDeviceSubClass value\r
- * @pr: bDeviceProtocol value\r
- *\r
- * This macro is used to create a struct usb_device_id that matches a\r
- * specific class of devices.\r
- */\r
-#define USB_DEVICE_INFO(cl,sc,pr) \\r
- .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, .bDeviceClass = (cl), .bDeviceSubClass = (sc), .bDeviceProtocol = (pr)\r
-\r
-/**\r
- * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces \r
- * @cl: bInterfaceClass value\r
- * @sc: bInterfaceSubClass value\r
- * @pr: bInterfaceProtocol value\r
- *\r
- * This macro is used to create a struct usb_device_id that matches a\r
- * specific class of interfaces.\r
- */\r
-#define USB_INTERFACE_INFO(cl,sc,pr) \\r
- .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)\r
-\r
-/* -------------------------------------------------------------------------- */\r
-\r
-/**\r
- * struct usb_driver - identifies USB driver to usbcore\r
- * @owner: Pointer to the module owner of this driver; initialize\r
- * it using THIS_MODULE.\r
- * @name: The driver name should be unique among USB drivers,\r
- * and should normally be the same as the module name.\r
- * @probe: Called to see if the driver is willing to manage a particular\r
- * interface on a device. If it is, probe returns zero and uses\r
- * dev_set_drvdata() to associate driver-specific data with the\r
- * interface. It may also use usb_set_interface() to specify the\r
- * appropriate altsetting. If unwilling to manage the interface,\r
- * return a negative errno value.\r
- * @disconnect: Called when the interface is no longer accessible, usually\r
- * because its device has been (or is being) disconnected or the\r
- * driver module is being unloaded.\r
- * @ioctl: Used for drivers that want to talk to userspace through\r
- * the "usbfs" filesystem. This lets devices provide ways to\r
- * expose information to user space regardless of where they\r
- * do (or don't) show up otherwise in the filesystem.\r
- * @id_table: USB drivers use ID table to support hotplugging.\r
- * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set\r
- * or your driver's probe function will never get called. \r
- *\r
- * USB drivers must provide a name, probe() and disconnect() methods,\r
- * and an id_table. Other driver fields are optional.\r
- *\r
- * The id_table is used in hotplugging. It holds a set of descriptors,\r
- * and specialized data may be associated with each entry. That table\r
- * is used by both user and kernel mode hotplugging support.\r
- *\r
- * The probe() and disconnect() methods are called in a context where\r
- * they can sleep, but they should avoid abusing the privilege. Most\r
- * work to connect to a device should be done when the device is opened,\r
- * and undone at the last close. The disconnect code needs to address\r
- * concurrency issues with respect to open() and close() methods, as\r
- * well as forcing all pending I/O requests to complete (by unlinking\r
- * them as necessary, and blocking until the unlinks complete).\r
- */\r
-struct usb_driver {\r
- struct module *owner;\r
-\r
- const char *name;\r
-\r
- int (*probe) (struct usb_interface *intf,\r
- const struct usb_device_id *id);\r
-\r
- void (*disconnect) (struct usb_interface *intf);\r
-\r
- int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);\r
-\r
- const struct usb_device_id *id_table;\r
-\r
- struct device_driver driver;\r
-\r
- struct semaphore serialize;\r
-};\r
-#define to_usb_driver(d) container_of(d, struct usb_driver, driver)\r
-\r
-extern struct bus_type usb_bus_type;\r
-\r
-/**\r
- * struct usb_class_driver - identifies a USB driver that wants to use the USB major number\r
- * @name: devfs name for this driver. Will also be used by the driver\r
- * class code to create a usb class device.\r
- * @fops: pointer to the struct file_operations of this driver.\r
- * @mode: the mode for the devfs file to be created for this driver.\r
- * @minor_base: the start of the minor range for this driver.\r
- *\r
- * This structure is used for the usb_register_dev() and\r
- * usb_unregister_dev() functions, to consolodate a number of the\r
- * paramaters used for them.\r
- */\r
-struct usb_class_driver {\r
- char *name;\r
- struct file_operations *fops;\r
- mode_t mode;\r
- int minor_base; \r
-};\r
-\r
-/*\r
- * use these in module_init()/module_exit()\r
- * and don't forget MODULE_DEVICE_TABLE(usb, ...)\r
- */\r
-extern int usb_register(struct usb_driver *);\r
-extern void usb_deregister(struct usb_driver *);\r
-\r
-extern int usb_register_dev(struct usb_interface *intf,\r
- struct usb_class_driver *class_driver);\r
-extern void usb_deregister_dev(struct usb_interface *intf,\r
- struct usb_class_driver *class_driver);\r
-\r
-extern int usb_device_probe(struct device *dev);\r
-extern int usb_device_remove(struct device *dev);\r
-extern int STDCALL usb_disabled(void);\r
-\r
-/* -------------------------------------------------------------------------- */\r
-\r
-/*\r
- * URB support, for asynchronous request completions\r
- */\r
-\r
-/*\r
- * urb->transfer_flags:\r
- */\r
-#define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */\r
-#define URB_ISO_ASAP 0x0002 /* iso-only, urb->start_frame ignored */\r
-#define URB_NO_DMA_MAP 0x0004 /* urb->*_dma are valid on submit */\r
-#define URB_ASYNC_UNLINK 0x0008 /* usb_unlink_urb() returns asap */\r
-#define URB_NO_FSBR 0x0020 /* UHCI-specific */\r
-#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet */\r
-#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */\r
-\r
-struct usb_iso_packet_descriptor {\r
- unsigned int offset;\r
- unsigned int length; /* expected length */\r
- unsigned int actual_length;\r
- unsigned int status;\r
-};\r
-\r
-struct urb;\r
-struct pt_regs;\r
-\r
-typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);\r
-\r
-/**\r
- * struct urb - USB Request Block\r
- * @urb_list: For use by current owner of the URB.\r
- * @pipe: Holds endpoint number, direction, type, and more.\r
- * Create these values with the eight macros available;\r
- * usb_{snd,rcv}TYPEpipe(dev,endpoint), where the type is "ctrl"\r
- * (control), "bulk", "int" (interrupt), or "iso" (isochronous).\r
- * For example usb_sndbulkpipe() or usb_rcvintpipe(). Endpoint\r
- * numbers range from zero to fifteen. Note that "in" endpoint two\r
- * is a different endpoint (and pipe) from "out" endpoint two.\r
- * The current configuration controls the existence, type, and\r
- * maximum packet size of any given endpoint.\r
- * @dev: Identifies the USB device to perform the request.\r
- * @status: This is read in non-iso completion functions to get the\r
- * status of the particular request. ISO requests only use it\r
- * to tell whether the URB was unlinked; detailed status for\r
- * each frame is in the fields of the iso_frame-desc.\r
- * @transfer_flags: A variety of flags may be used to affect how URB\r
- * submission, unlinking, or operation are handled. Different\r
- * kinds of URB can use different flags.\r
- * @transfer_buffer: This identifies the buffer to (or from) which\r
- * the I/O request will be performed (unless URB_NO_DMA_MAP is set).\r
- * This buffer must be suitable for DMA; allocate it with kmalloc()\r
- * or equivalent. For transfers to "in" endpoints, contents of\r
- * this buffer will be modified. This buffer is used for data\r
- * phases of control transfers.\r
- * @transfer_dma: When transfer_flags includes URB_NO_DMA_MAP, the device\r
- * driver is saying that it provided this DMA address, which the host\r
- * controller driver should use instead of the transfer_buffer.\r
- * @transfer_buffer_length: How big is transfer_buffer. The transfer may\r
- * be broken up into chunks according to the current maximum packet\r
- * size for the endpoint, which is a function of the configuration\r
- * and is encoded in the pipe. When the length is zero, neither\r
- * transfer_buffer nor transfer_dma is used.\r
- * @actual_length: This is read in non-iso completion functions, and\r
- * it tells how many bytes (out of transfer_buffer_length) were\r
- * transferred. It will normally be the same as requested, unless\r
- * either an error was reported or a short read was performed.\r
- * The URB_SHORT_NOT_OK transfer flag may be used to make such\r
- * short reads be reported as errors. \r
- * @setup_packet: Only used for control transfers, this points to eight bytes\r
- * of setup data. Control transfers always start by sending this data\r
- * to the device. Then transfer_buffer is read or written, if needed.\r
- * (Not used when URB_NO_DMA_MAP is set.)\r
- * @setup_dma: For control transfers with URB_NO_DMA_MAP set, the device\r
- * driver has provided this DMA address for the setup packet. The\r
- * host controller driver should use this instead of setup_buffer.\r
- * If there is a data phase, its buffer is identified by transfer_dma.\r
- * @start_frame: Returns the initial frame for interrupt or isochronous\r
- * transfers.\r
- * @number_of_packets: Lists the number of ISO transfer buffers.\r
- * @interval: Specifies the polling interval for interrupt or isochronous\r
- * transfers. The units are frames (milliseconds) for for full and low\r
- * speed devices, and microframes (1/8 millisecond) for highspeed ones.\r
- * @error_count: Returns the number of ISO transfers that reported errors.\r
- * @context: For use in completion functions. This normally points to\r
- * request-specific driver context.\r
- * @complete: Completion handler. This URB is passed as the parameter to the\r
- * completion function. The completion function may then do what\r
- * it likes with the URB, including resubmitting or freeing it.\r
- * @iso_frame_desc: Used to provide arrays of ISO transfer buffers and to \r
- * collect the transfer status for each buffer.\r
- *\r
- * This structure identifies USB transfer requests. URBs must be allocated by\r
- * calling usb_alloc_urb() and freed with a call to usb_free_urb().\r
- * Initialization may be done using various usb_fill_*_urb() functions. URBs\r
- * are submitted using usb_submit_urb(), and pending requests may be canceled\r
- * using usb_unlink_urb().\r
- *\r
- * Data Transfer Buffers:\r
- *\r
- * Normally drivers provide I/O buffers allocated with kmalloc() or otherwise\r
- * taken from the general page pool. That is provided by transfer_buffer\r
- * (control requests also use setup_packet), and host controller drivers\r
- * perform a dma mapping (and unmapping) for each buffer transferred. Those\r
- * mapping operations can be expensive on some platforms (perhaps using a dma\r
- * bounce buffer or talking to an IOMMU),\r
- * although they're cheap on commodity x86 and ppc hardware.\r
- *\r
- * Alternatively, drivers may pass the URB_NO_DMA_MAP transfer flag, which\r
- * tells the host controller driver that no such mapping is needed since\r
- * the device driver is DMA-aware. For example, they might allocate a DMA\r
- * buffer with usb_buffer_alloc(), or call usb_buffer_map().\r
- * When this transfer flag is provided, host controller drivers will use the\r
- * dma addresses found in the transfer_dma and/or setup_dma fields rather than\r
- * determing a dma address themselves.\r
- *\r
- * Initialization:\r
- *\r
- * All URBs submitted must initialize dev, pipe,\r
- * transfer_flags (may be zero), complete, timeout (may be zero).\r
- * The URB_ASYNC_UNLINK transfer flag affects later invocations of\r
- * the usb_unlink_urb() routine.\r
- *\r
- * All URBs must also initialize \r
- * transfer_buffer and transfer_buffer_length. They may provide the\r
- * URB_SHORT_NOT_OK transfer flag, indicating that short reads are\r
- * to be treated as errors; that flag is invalid for write requests.\r
- *\r
- * Bulk URBs may\r
- * use the URB_ZERO_PACKET transfer flag, indicating that bulk OUT transfers\r
- * should always terminate with a short packet, even if it means adding an\r
- * extra zero length packet.\r
- *\r
- * Control URBs must provide a setup_packet.\r
- *\r
- * Interrupt UBS must provide an interval, saying how often (in milliseconds\r
- * or, for highspeed devices, 125 microsecond units)\r
- * to poll for transfers. After the URB has been submitted, the interval\r
- * and start_frame fields reflect how the transfer was actually scheduled.\r
- * The polling interval may be more frequent than requested.\r
- * For example, some controllers have a maximum interval of 32 microseconds,\r
- * while others support intervals of up to 1024 microseconds.\r
- * Isochronous URBs also have transfer intervals. (Note that for isochronous\r
- * endpoints, as well as high speed interrupt endpoints, the encoding of\r
- * the transfer interval in the endpoint descriptor is logarithmic.)\r
- *\r
- * Isochronous URBs normally use the URB_ISO_ASAP transfer flag, telling\r
- * the host controller to schedule the transfer as soon as bandwidth\r
- * utilization allows, and then set start_frame to reflect the actual frame\r
- * selected during submission. Otherwise drivers must specify the start_frame\r
- * and handle the case where the transfer can't begin then. However, drivers\r
- * won't know how bandwidth is currently allocated, and while they can\r
- * find the current frame using usb_get_current_frame_number () they can't\r
- * know the range for that frame number. (Ranges for frame counter values\r
- * are HC-specific, and can go from 256 to 65536 frames from "now".)\r
- *\r
- * Isochronous URBs have a different data transfer model, in part because\r
- * the quality of service is only "best effort". Callers provide specially\r
- * allocated URBs, with number_of_packets worth of iso_frame_desc structures\r
- * at the end. Each such packet is an individual ISO transfer. Isochronous\r
- * URBs are normally queued, submitted by drivers to arrange that\r
- * transfers are at least double buffered, and then explicitly resubmitted\r
- * in completion handlers, so\r
- * that data (such as audio or video) streams at as constant a rate as the\r
- * host controller scheduler can support.\r
- *\r
- * Completion Callbacks:\r
- *\r
- * The completion callback is made in_interrupt(), and one of the first\r
- * things that a completion handler should do is check the status field.\r
- * The status field is provided for all URBs. It is used to report\r
- * unlinked URBs, and status for all non-ISO transfers. It should not\r
- * be examined before the URB is returned to the completion handler.\r
- *\r
- * The context field is normally used to link URBs back to the relevant\r
- * driver or request state.\r
- *\r
- * When completion callback is invoked for non-isochronous URBs, the\r
- * actual_length field tells how many bytes were transferred.\r
- *\r
- * ISO transfer status is reported in the status and actual_length fields\r
- * of the iso_frame_desc array, and the number of errors is reported in\r
- * error_count. Completion callbacks for ISO transfers will normally\r
- * (re)submit URBs to ensure a constant transfer rate.\r
- */\r
-struct urb\r
-{\r
- spinlock_t lock; /* lock for the URB */\r
- atomic_t count; /* reference count of the URB */\r
- void *hcpriv; /* private data for host controller */\r
- struct list_head urb_list; /* list pointer to all active urbs */\r
- struct usb_device *dev; /* (in) pointer to associated device */\r
- unsigned int pipe; /* (in) pipe information */\r
- int status; /* (return) non-ISO status */\r
- unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/\r
- void *transfer_buffer; /* (in) associated data buffer */\r
- dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */\r
- int transfer_buffer_length; /* (in) data buffer length */\r
- int actual_length; /* (return) actual transfer length */\r
- int bandwidth; /* bandwidth for INT/ISO request */\r
- unsigned char *setup_packet; /* (in) setup packet (control only) */\r
- dma_addr_t setup_dma; /* (in) dma addr for setup_packet */\r
- int start_frame; /* (modify) start frame (INT/ISO) */\r
- int number_of_packets; /* (in) number of ISO packets */\r
- int interval; /* (in) transfer interval (INT/ISO) */\r
- int error_count; /* (return) number of ISO errors */\r
- int timeout; /* (in) timeout, in jiffies */\r
- void *context; /* (in) context for completion */\r
- usb_complete_t complete; /* (in) completion routine */\r
- struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */\r
-};\r
-\r
-/* -------------------------------------------------------------------------- */\r
-\r
-/**\r
- * usb_fill_control_urb - initializes a control urb\r
- * @urb: pointer to the urb to initialize.\r
- * @dev: pointer to the struct usb_device for this urb.\r
- * @pipe: the endpoint pipe\r
- * @setup_packet: pointer to the setup_packet buffer\r
- * @transfer_buffer: pointer to the transfer buffer\r
- * @buffer_length: length of the transfer buffer\r
- * @complete: pointer to the usb_complete_t function\r
- * @context: what to set the urb context to.\r
- *\r
- * Initializes a control urb with the proper information needed to submit\r
- * it to a device.\r
- */\r
-static inline void usb_fill_control_urb (struct urb *urb,\r
- struct usb_device *dev,\r
- unsigned int pipe,\r
- unsigned char *setup_packet,\r
- void *transfer_buffer,\r
- int buffer_length,\r
- usb_complete_t complete,\r
- void *context)\r
-{\r
- spin_lock_init(&urb->lock);\r
- urb->dev = dev;\r
- urb->pipe = pipe;\r
- urb->setup_packet = setup_packet;\r
- urb->transfer_buffer = transfer_buffer;\r
- urb->transfer_buffer_length = buffer_length;\r
- urb->complete = complete;\r
- urb->context = context;\r
-}\r
-\r
-/**\r
- * usb_fill_bulk_urb - macro to help initialize a bulk urb\r
- * @urb: pointer to the urb to initialize.\r
- * @dev: pointer to the struct usb_device for this urb.\r
- * @pipe: the endpoint pipe\r
- * @transfer_buffer: pointer to the transfer buffer\r
- * @buffer_length: length of the transfer buffer\r
- * @complete: pointer to the usb_complete_t function\r
- * @context: what to set the urb context to.\r
- *\r
- * Initializes a bulk urb with the proper information needed to submit it\r
- * to a device.\r
- */\r
-static inline void usb_fill_bulk_urb (struct urb *urb,\r
- struct usb_device *dev,\r
- unsigned int pipe,\r
- void *transfer_buffer,\r
- int buffer_length,\r
- usb_complete_t complete,\r
- void *context)\r
-{\r
- spin_lock_init(&urb->lock);\r
- urb->dev = dev;\r
- urb->pipe = pipe;\r
- urb->transfer_buffer = transfer_buffer;\r
- urb->transfer_buffer_length = buffer_length;\r
- urb->complete = complete;\r
- urb->context = context;\r
-}\r
-\r
-/**\r
- * usb_fill_int_urb - macro to help initialize a interrupt urb\r
- * @urb: pointer to the urb to initialize.\r
- * @dev: pointer to the struct usb_device for this urb.\r
- * @pipe: the endpoint pipe\r
- * @transfer_buffer: pointer to the transfer buffer\r
- * @buffer_length: length of the transfer buffer\r
- * @complete: pointer to the usb_complete_t function\r
- * @context: what to set the urb context to.\r
- * @interval: what to set the urb interval to, encoded like\r
- * the endpoint descriptor's bInterval value.\r
- *\r
- * Initializes a interrupt urb with the proper information needed to submit\r
- * it to a device.\r
- * Note that high speed interrupt endpoints use a logarithmic encoding of\r
- * the endpoint interval, and express polling intervals in microframes\r
- * (eight per millisecond) rather than in frames (one per millisecond).\r
- */\r
-static inline void usb_fill_int_urb (struct urb *urb,\r
- struct usb_device *dev,\r
- unsigned int pipe,\r
- void *transfer_buffer,\r
- int buffer_length,\r
- usb_complete_t complete,\r
- void *context,\r
- int interval)\r
-{\r
- spin_lock_init(&urb->lock);\r
- urb->dev = dev;\r
- urb->pipe = pipe;\r
- urb->transfer_buffer = transfer_buffer;\r
- urb->transfer_buffer_length = buffer_length;\r
- urb->complete = complete;\r
- urb->context = context;\r
- if (dev->speed == USB_SPEED_HIGH)\r
- urb->interval = 1 << (interval - 1);\r
- else\r
- urb->interval = interval;\r
- urb->start_frame = -1;\r
-}\r
-\r
-extern void STDCALL usb_init_urb(struct urb *urb);\r
-extern struct urb STDCALL *usb_alloc_urb(int iso_packets, int mem_flags);\r
-extern void STDCALL usb_free_urb(struct urb *urb);\r
-#define usb_put_urb usb_free_urb\r
-extern struct urb STDCALL *usb_get_urb(struct urb *urb);\r
-extern int STDCALL usb_submit_urb(struct urb *urb, int mem_flags);\r
-extern int STDCALL usb_unlink_urb(struct urb *urb);\r
-\r
-#define HAVE_USB_BUFFERS\r
-void *usb_buffer_alloc (struct usb_device *dev, size_t size,\r
- int mem_flags, dma_addr_t *dma);\r
-void usb_buffer_free (struct usb_device *dev, size_t size,\r
- void *addr, dma_addr_t dma);\r
-\r
-struct urb *usb_buffer_map (struct urb *urb);\r
-void usb_buffer_dmasync (struct urb *urb);\r
-void usb_buffer_unmap (struct urb *urb);\r
-\r
-struct scatterlist;\r
-int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,\r
- struct scatterlist *sg, int nents);\r
-void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,\r
- struct scatterlist *sg, int n_hw_ents);\r
-void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,\r
- struct scatterlist *sg, int n_hw_ents);\r
-\r
-/*-------------------------------------------------------------------*\r
- * SYNCHRONOUS CALL SUPPORT *\r
- *-------------------------------------------------------------------*/\r
-\r
-extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,\r
- __u8 request, __u8 requesttype, __u16 value, __u16 index,\r
- void *data, __u16 size, int timeout);\r
-extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,\r
- void *data, int len, int *actual_length,\r
- int timeout);\r
-\r
-/* wrappers around usb_control_msg() for the most common standard requests */\r
-extern int usb_get_descriptor(struct usb_device *dev, unsigned char desctype,\r
- unsigned char descindex, void *buf, int size);\r
-extern int usb_get_device_descriptor(struct usb_device *dev);\r
-extern int usb_get_status(struct usb_device *dev,\r
- int type, int target, void *data);\r
-extern int usb_get_string(struct usb_device *dev,\r
- unsigned short langid, unsigned char index, void *buf, int size);\r
-extern int usb_string(struct usb_device *dev, int index,\r
- char *buf, size_t size);\r
-\r
-/* wrappers that also update important state inside usbcore */\r
-extern int usb_clear_halt(struct usb_device *dev, int pipe);\r
-extern int usb_set_configuration(struct usb_device *dev, int configuration);\r
-extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);\r
-\r
-/*\r
- * timeouts, in seconds, used for sending/receiving control messages\r
- * they typically complete within a few frames (msec) after they're issued\r
- * USB identifies 5 second timeouts, maybe more in a few cases, and a few\r
- * slow devices (like some MGE Ellipse UPSes) actually push that limit.\r
- */\r
-#define USB_CTRL_GET_TIMEOUT 5\r
-#define USB_CTRL_SET_TIMEOUT 5\r
-\r
-\r
-/**\r
- * struct usb_sg_request - support for scatter/gather I/O\r
- * @status: zero indicates success, else negative errno\r
- * @bytes: counts bytes transferred.\r
- *\r
- * These requests are initialized using usb_sg_init(), and then are used\r
- * as request handles passed to usb_sg_wait() or usb_sg_cancel(). Most\r
- * members of the request object aren't for driver access.\r
- *\r
- * The status and bytecount values are valid only after usb_sg_wait()\r
- * returns. If the status is zero, then the bytecount matches the total\r
- * from the request.\r
- *\r
- * After an error completion, drivers may need to clear a halt condition\r
- * on the endpoint.\r
- */\r
-struct usb_sg_request {\r
- int status;\r
- size_t bytes;\r
-\r
- // members not documented above are private to usbcore,\r
- // and are not provided for driver access!\r
- spinlock_t lock;\r
-\r
- struct usb_device *dev;\r
- int pipe;\r
- struct scatterlist *sg;\r
- int nents;\r
-\r
- int entries;\r
- struct urb **urbs;\r
-\r
- int count;\r
- struct completion complete;\r
-};\r
-\r
-int usb_sg_init (\r
- struct usb_sg_request *io,\r
- struct usb_device *dev,\r
- unsigned pipe, \r
- unsigned period,\r
- struct scatterlist *sg,\r
- int nents,\r
- size_t length,\r
- int mem_flags\r
-);\r
-void usb_sg_cancel (struct usb_sg_request *io);\r
-void usb_sg_wait (struct usb_sg_request *io);\r
-\r
-\r
-/* -------------------------------------------------------------------------- */\r
-\r
-/*\r
- * Calling this entity a "pipe" is glorifying it. A USB pipe\r
- * is something embarrassingly simple: it basically consists\r
- * of the following information:\r
- * - device number (7 bits)\r
- * - endpoint number (4 bits)\r
- * - current Data0/1 state (1 bit) [Historical; now gone]\r
- * - direction (1 bit)\r
- * - speed (1 bit) [Historical and specific to USB 1.1; now gone.]\r
- * - max packet size (2 bits: 8, 16, 32 or 64) [Historical; now gone.]\r
- * - pipe type (2 bits: control, interrupt, bulk, isochronous)\r
- *\r
- * That's 18 bits. Really. Nothing more. And the USB people have\r
- * documented these eighteen bits as some kind of glorious\r
- * virtual data structure.\r
- *\r
- * Let's not fall in that trap. We'll just encode it as a simple\r
- * unsigned int. The encoding is:\r
- *\r
- * - max size: bits 0-1 [Historical; now gone.]\r
- * - direction: bit 7 (0 = Host-to-Device [Out],\r
- * 1 = Device-to-Host [In] ...\r
- * like endpoint bEndpointAddress)\r
- * - device: bits 8-14 ... bit positions known to uhci-hcd\r
- * - endpoint: bits 15-18 ... bit positions known to uhci-hcd\r
- * - Data0/1: bit 19 [Historical; now gone. ]\r
- * - lowspeed: bit 26 [Historical; now gone. ]\r
- * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt,\r
- * 10 = control, 11 = bulk)\r
- *\r
- * Why? Because it's arbitrary, and whatever encoding we select is really\r
- * up to us. This one happens to share a lot of bit positions with the UHCI\r
- * specification, so that much of the uhci driver can just mask the bits\r
- * appropriately.\r
- */\r
-\r
-/* NOTE: these are not the standard USB_ENDPOINT_XFER_* values!! */\r
-#define PIPE_ISOCHRONOUS 0\r
-#define PIPE_INTERRUPT 1\r
-#define PIPE_CONTROL 2\r
-#define PIPE_BULK 3\r
-\r
-#define usb_maxpacket(dev, pipe, out) (out \\r
- ? (dev)->epmaxpacketout[usb_pipeendpoint(pipe)] \\r
- : (dev)->epmaxpacketin [usb_pipeendpoint(pipe)] )\r
-\r
-#define usb_pipein(pipe) ((pipe) & USB_DIR_IN)\r
-#define usb_pipeout(pipe) (!usb_pipein(pipe))\r
-#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f)\r
-#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf)\r
-#define usb_pipetype(pipe) (((pipe) >> 30) & 3)\r
-#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS)\r
-#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT)\r
-#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL)\r
-#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK)\r
-\r
-/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */\r
-#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)\r
-#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep)))\r
-#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | ((bit) << (ep)))\r
-\r
-/* Endpoint halt control/status ... likewise USE WITH CAUTION */\r
-#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep)))\r
-#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep)))\r
-\r
-\r
-static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)\r
-{\r
- return (dev->devnum << 8) | (endpoint << 15);\r
-}\r
-\r
-/* Create various pipes... */\r
-#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))\r
-#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)\r
-#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))\r
-#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)\r
-#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint))\r
-#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)\r
-#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))\r
-#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)\r
-\r
-/* -------------------------------------------------------------------------- */\r
-\r
-/*\r
- * Debugging and troubleshooting/diagnostic helpers.\r
- */\r
-void usb_show_device_descriptor(struct usb_device_descriptor *);\r
-void usb_show_config_descriptor(struct usb_config_descriptor *);\r
-void usb_show_interface_descriptor(struct usb_interface_descriptor *);\r
-void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *);\r
-void usb_show_device(struct usb_device *);\r
-void usb_show_string(struct usb_device *dev, char *id, int index);\r
-\r
-#ifdef DEBUG\r
-#define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , __FILE__ , ## arg)\r
-#else\r
-#define dbg(format, arg...) do {} while (0)\r
-#endif\r
-\r
-\r
-\r
-#ifdef DEBUG_MODE\r
-#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ## arg)\r
-#define err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)\r
-#define warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ## arg)\r
-#endif\r
-\r
-#ifndef DEBUG_MODE \r
-#define info(format, arg...) do {} while (0)\r
-#define err(format, arg...) do {} while (0)\r
-#define warn(format, arg...) do {} while (0)\r
-#endif\r
-\r
-#endif /* __KERNEL__ */\r
-\r
-#endif\r
+#ifndef __LINUX_USB_H
+#define __LINUX_USB_H
+
+
+#include "usb_ch9.h"
+
+#define USB_MAJOR 180
+
+
+#ifdef __KERNEL__
+#if 0
+#include <linux/config.h>
+#include <linux/errno.h> /* for -ENODEV */
+#include <linux/delay.h> /* for mdelay() */
+#include <linux/interrupt.h> /* for in_interrupt() */
+#include <linux/list.h> /* for struct list_head */
+#include <linux/device.h> /* for struct device */
+#include <linux/fs.h> /* for struct file_operations */
+#include <linux/completion.h> /* for struct completion */
+#include <linux/sched.h> /* for current && schedule_timeout */
+
+
+static __inline__ void wait_ms(unsigned int ms)
+{
+ if(!in_interrupt()) {
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1 + ms * HZ / 1000);
+ }
+ else
+ mdelay(ms);
+}
+#endif
+struct usb_device;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Host-side wrappers for standard USB descriptors ... these are parsed
+ * from the data provided by devices. Parsing turns them from a flat
+ * sequence of descriptors into a hierarchy:
+ *
+ * - devices have one (usually) or more configs;
+ * - configs have one (often) or more interfaces;
+ * - interfaces have one (usually) or more settings;
+ * - each interface setting has zero or (usually) more endpoints.
+ *
+ * And there might be other descriptors mixed in with those.
+ *
+ * Devices may also have class-specific or vendor-specific descriptors.
+ */
+
+/* host-side wrapper for parsed endpoint descriptors */
+struct usb_host_endpoint {
+ struct usb_endpoint_descriptor desc;
+
+ unsigned char *extra; /* Extra descriptors */
+ int extralen;
+};
+
+/* host-side wrapper for one interface setting's parsed descriptors */
+struct usb_host_interface {
+ struct usb_interface_descriptor desc;
+
+ /* array of desc.bNumEndpoint endpoints associated with this
+ * interface setting. these will be in no particular order.
+ */
+ struct usb_host_endpoint *endpoint;
+
+ unsigned char *extra; /* Extra descriptors */
+ int extralen;
+};
+
+/**
+ * struct usb_interface - what usb device drivers talk to
+ * @altsetting: array of interface descriptors, one for each alternate
+ * setting that may be selected. Each one includes a set of
+ * endpoint configurations and will be in numberic order,
+ * 0..num_altsetting.
+ * @num_altsetting: number of altsettings defined.
+ * @act_altsetting: index of current altsetting. this number is always
+ * less than num_altsetting. after the device is configured, each
+ * interface uses its default setting of zero.
+ * @max_altsetting:
+ * @minor: the minor number assigned to this interface, if this
+ * interface is bound to a driver that uses the USB major number.
+ * If this interface does not use the USB major, this field should
+ * be unused. The driver should set this value in the probe()
+ * function of the driver, after it has been assigned a minor
+ * number from the USB core by calling usb_register_dev().
+ * @dev: driver model's view of this device
+ * @class_dev: driver model's class view of this device.
+ *
+ * USB device drivers attach to interfaces on a physical device. Each
+ * interface encapsulates a single high level function, such as feeding
+ * an audio stream to a speaker or reporting a change in a volume control.
+ * Many USB devices only have one interface. The protocol used to talk to
+ * an interface's endpoints can be defined in a usb "class" specification,
+ * or by a product's vendor. The (default) control endpoint is part of
+ * every interface, but is never listed among the interface's descriptors.
+ *
+ * The driver that is bound to the interface can use standard driver model
+ * calls such as dev_get_drvdata() on the dev member of this structure.
+ *
+ * Each interface may have alternate settings. The initial configuration
+ * of a device sets the first of these, but the device driver can change
+ * that setting using usb_set_interface(). Alternate settings are often
+ * used to control the the use of periodic endpoints, such as by having
+ * different endpoints use different amounts of reserved USB bandwidth.
+ * All standards-conformant USB devices that use isochronous endpoints
+ * will use them in non-default settings.
+ */
+struct usb_interface {
+ /* array of alternate settings for this interface.
+ * these will be in numeric order, 0..num_altsettting
+ */
+ struct usb_host_interface *altsetting;
+
+ unsigned act_altsetting; /* active alternate setting */
+ unsigned num_altsetting; /* number of alternate settings */
+ unsigned max_altsetting; /* total memory allocated */
+
+ struct usb_driver *driver; /* driver */
+ int minor; /* minor number this interface is bound to */
+ struct device dev; /* interface specific device info */
+ struct class_device class_dev;
+};
+#define to_usb_interface(d) container_of(d, struct usb_interface, dev)
+#define class_dev_to_usb_interface(d) container_of(d, struct usb_interface, class_dev)
+#define interface_to_usbdev(intf) \
+ container_of(intf->dev.parent, struct usb_device, dev)
+
+static inline void *usb_get_intfdata (struct usb_interface *intf)
+{
+ return dev_get_drvdata (&intf->dev);
+}
+
+static inline void usb_set_intfdata (struct usb_interface *intf, void *data)
+{
+ dev_set_drvdata(&intf->dev, data);
+}
+
+/* USB_DT_CONFIG: Configuration descriptor information.
+ *
+ * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the
+ * descriptor type is different. Highspeed-capable devices can look
+ * different depending on what speed they're currently running. Only
+ * devices with a USB_DT_DEVICE_QUALIFIER have an OTHER_SPEED_CONFIG.
+ */
+struct usb_host_config {
+ struct usb_config_descriptor desc;
+
+ /* the interfaces associated with this configuration
+ * these will be in numeric order, 0..desc.bNumInterfaces
+ */
+ struct usb_interface *interface;
+
+ unsigned char *extra; /* Extra descriptors */
+ int extralen;
+};
+
+// FIXME remove; exported only for drivers/usb/misc/auserwald.c
+// prefer usb_device->epnum[0..31]
+extern struct usb_endpoint_descriptor *
+ usb_epnum_to_ep_desc(struct usb_device *dev, unsigned epnum);
+
+int __usb_get_extra_descriptor(char *buffer, unsigned size,
+ unsigned char type, void **ptr);
+#define usb_get_extra_descriptor(ifpoint,type,ptr)\
+ __usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,\
+ type,(void**)ptr)
+
+/* -------------------------------------------------------------------------- */
+
+struct usb_operations;
+
+/* USB device number allocation bitmap */
+struct usb_devmap {
+ unsigned long devicemap[128 / (8*sizeof(unsigned long))];
+};
+
+/*
+ * Allocated per bus (tree of devices) we have:
+ */
+struct usb_bus {
+ struct device *controller; /* host/master side hardware */
+ int busnum; /* Bus number (in order of reg) */
+ char *bus_name; /* stable id (PCI slot_name etc) */
+
+ int devnum_next; /* Next open device number in round-robin allocation */
+
+ struct usb_devmap devmap; /* device address allocation map */
+ struct usb_operations *op; /* Operations (specific to the HC) */
+ struct usb_device *root_hub; /* Root hub */
+ struct list_head bus_list; /* list of busses */
+ void *hcpriv; /* Host Controller private data */
+
+ int bandwidth_allocated; /* on this bus: how much of the time
+ * reserved for periodic (intr/iso)
+ * requests is used, on average?
+ * Units: microseconds/frame.
+ * Limits: Full/low speed reserve 90%,
+ * while high speed reserves 80%.
+ */
+ int bandwidth_int_reqs; /* number of Interrupt requests */
+ int bandwidth_isoc_reqs; /* number of Isoc. requests */
+
+ struct dentry *usbfs_dentry; /* usbfs dentry entry for the bus */
+ struct dentry *usbdevfs_dentry; /* usbdevfs dentry entry for the bus */
+
+ atomic_t refcnt;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+/* This is arbitrary.
+ * From USB 2.0 spec Table 11-13, offset 7, a hub can
+ * have up to 255 ports. The most yet reported is 10.
+ */
+#define USB_MAXCHILDREN (16)
+
+struct usb_tt;
+
+struct usb_device {
+ int devnum; /* Address on USB bus */
+ char devpath [16]; /* Use in messages: /port/port/... */
+ enum usb_device_state state; /* configured, not attached, etc */
+ enum usb_device_speed speed; /* high/full/low (or error) */
+
+ struct usb_tt *tt; /* low/full speed dev, highspeed hub */
+ int ttport; /* device port on that tt hub */
+
+ struct semaphore serialize;
+
+ unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */
+ unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; */
+ /* [0] = IN, [1] = OUT */
+ int epmaxpacketin[16]; /* INput endpoint specific maximums */
+ int epmaxpacketout[16]; /* OUTput endpoint specific maximums */
+
+ struct usb_device *parent; /* our hub, unless we're the root */
+ struct usb_bus *bus; /* Bus we're part of */
+
+ struct device dev; /* Generic device interface */
+
+ struct usb_device_descriptor descriptor;/* Descriptor */
+ struct usb_host_config *config; /* All of the configs */
+ struct usb_host_config *actconfig;/* the active configuration */
+
+ char **rawdescriptors; /* Raw descriptors for each config */
+
+ int have_langid; /* whether string_langid is valid yet */
+ int string_langid; /* language ID for strings */
+
+ void *hcpriv; /* Host Controller private data */
+
+ struct list_head filelist;
+ struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */
+ struct dentry *usbdevfs_dentry; /* usbdevfs dentry entry for the device */
+
+ /*
+ * 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 structures.
+ */
+
+ int maxchild; /* Number of ports if hub */
+ struct usb_device *children[USB_MAXCHILDREN];
+};
+#define to_usb_device(d) container_of(d, struct usb_device, dev)
+
+extern struct usb_device STDCALL *usb_alloc_dev(struct usb_device *parent, struct usb_bus *);
+extern struct usb_device STDCALL *usb_get_dev(struct usb_device *dev);
+extern void STDCALL usb_put_dev(struct usb_device *dev);
+
+/* mostly for devices emulating SCSI over USB */
+extern int usb_reset_device(struct usb_device *dev);
+
+extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
+
+/* for drivers using iso endpoints */
+extern int usb_get_current_frame_number (struct usb_device *usb_dev);
+
+/* used these for multi-interface device registration */
+extern void usb_driver_claim_interface(struct usb_driver *driver,
+ struct usb_interface *iface, void* priv);
+extern int usb_interface_claimed(struct usb_interface *iface);
+extern void usb_driver_release_interface(struct usb_driver *driver,
+ struct usb_interface *iface);
+const struct usb_device_id *usb_match_id(struct usb_interface *interface,
+ const struct usb_device_id *id);
+
+extern struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor);
+extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum);
+
+
+/**
+ * usb_make_path - returns stable device path in the usb tree
+ * @dev: the device whose path is being constructed
+ * @buf: where to put the string
+ * @size: how big is "buf"?
+ *
+ * Returns length of the string (> 0) or negative if size was too small.
+ *
+ * This identifier is intended to be "stable", reflecting physical paths in
+ * hardware such as physical bus addresses for host controllers or ports on
+ * USB hubs. That makes it stay the same until systems are physically
+ * reconfigured, by re-cabling a tree of USB devices or by moving USB host
+ * controllers. Adding and removing devices, including virtual root hubs
+ * in host controller driver modules, does not change these path identifers;
+ * neither does rebooting or re-enumerating. These are more useful identifiers
+ * than changeable ("unstable") ones like bus numbers or device addresses.
+ *
+ * With a partial exception for devices connected to USB 2.0 root hubs, these
+ * identifiers are also predictable. So long as the device tree isn't changed,
+ * plugging any USB device into a given hub port always gives it the same path.
+ * Because of the use of "companion" controllers, devices connected to ports on
+ * USB 2.0 root hubs (EHCI host controllers) will get one path ID if they are
+ * high speed, and a different one if they are full or low speed.
+ */
+static inline int usb_make_path (struct usb_device *dev, char *buf, size_t size)
+{
+ int actual;
+ actual = snprintf (buf, size, "usb-%s-%s", dev->bus->bus_name, dev->devpath);
+ return (actual >= size) ? -1 : actual;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define USB_DEVICE_ID_MATCH_DEVICE (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
+#define USB_DEVICE_ID_MATCH_DEV_RANGE (USB_DEVICE_ID_MATCH_DEV_LO | USB_DEVICE_ID_MATCH_DEV_HI)
+#define USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION (USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_RANGE)
+#define USB_DEVICE_ID_MATCH_DEV_INFO \
+ (USB_DEVICE_ID_MATCH_DEV_CLASS | USB_DEVICE_ID_MATCH_DEV_SUBCLASS | USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
+#define USB_DEVICE_ID_MATCH_INT_INFO \
+ (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | USB_DEVICE_ID_MATCH_INT_PROTOCOL)
+
+/**
+ * USB_DEVICE - macro used to describe a specific usb device
+ * @vend: the 16 bit USB Vendor ID
+ * @prod: the 16 bit USB Product ID
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific device.
+ */
+#define USB_DEVICE(vend,prod) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = (vend), .idProduct = (prod)
+/**
+ * USB_DEVICE_VER - macro used to describe a specific usb device with a version range
+ * @vend: the 16 bit USB Vendor ID
+ * @prod: the 16 bit USB Product ID
+ * @lo: the bcdDevice_lo value
+ * @hi: the bcdDevice_hi value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific device, with a version range.
+ */
+#define USB_DEVICE_VER(vend,prod,lo,hi) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, .idVendor = (vend), .idProduct = (prod), .bcdDevice_lo = (lo), .bcdDevice_hi = (hi)
+
+/**
+ * USB_DEVICE_INFO - macro used to describe a class of usb devices
+ * @cl: bDeviceClass value
+ * @sc: bDeviceSubClass value
+ * @pr: bDeviceProtocol value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific class of devices.
+ */
+#define USB_DEVICE_INFO(cl,sc,pr) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, .bDeviceClass = (cl), .bDeviceSubClass = (sc), .bDeviceProtocol = (pr)
+
+/**
+ * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces
+ * @cl: bInterfaceClass value
+ * @sc: bInterfaceSubClass value
+ * @pr: bInterfaceProtocol value
+ *
+ * This macro is used to create a struct usb_device_id that matches a
+ * specific class of interfaces.
+ */
+#define USB_INTERFACE_INFO(cl,sc,pr) \
+ .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr)
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * struct usb_driver - identifies USB driver to usbcore
+ * @owner: Pointer to the module owner of this driver; initialize
+ * it using THIS_MODULE.
+ * @name: The driver name should be unique among USB drivers,
+ * and should normally be the same as the module name.
+ * @probe: Called to see if the driver is willing to manage a particular
+ * interface on a device. If it is, probe returns zero and uses
+ * dev_set_drvdata() to associate driver-specific data with the
+ * interface. It may also use usb_set_interface() to specify the
+ * appropriate altsetting. If unwilling to manage the interface,
+ * return a negative errno value.
+ * @disconnect: Called when the interface is no longer accessible, usually
+ * because its device has been (or is being) disconnected or the
+ * driver module is being unloaded.
+ * @ioctl: Used for drivers that want to talk to userspace through
+ * the "usbfs" filesystem. This lets devices provide ways to
+ * expose information to user space regardless of where they
+ * do (or don't) show up otherwise in the filesystem.
+ * @id_table: USB drivers use ID table to support hotplugging.
+ * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set
+ * or your driver's probe function will never get called.
+ *
+ * USB drivers must provide a name, probe() and disconnect() methods,
+ * and an id_table. Other driver fields are optional.
+ *
+ * The id_table is used in hotplugging. It holds a set of descriptors,
+ * and specialized data may be associated with each entry. That table
+ * is used by both user and kernel mode hotplugging support.
+ *
+ * The probe() and disconnect() methods are called in a context where
+ * they can sleep, but they should avoid abusing the privilege. Most
+ * work to connect to a device should be done when the device is opened,
+ * and undone at the last close. The disconnect code needs to address
+ * concurrency issues with respect to open() and close() methods, as
+ * well as forcing all pending I/O requests to complete (by unlinking
+ * them as necessary, and blocking until the unlinks complete).
+ */
+struct usb_driver {
+ struct module *owner;
+
+ const char *name;
+
+ int (*probe) (struct usb_interface *intf,
+ const struct usb_device_id *id);
+
+ void (*disconnect) (struct usb_interface *intf);
+
+ int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
+
+ const struct usb_device_id *id_table;
+
+ struct device_driver driver;
+
+ struct semaphore serialize;
+};
+#define to_usb_driver(d) container_of(d, struct usb_driver, driver)
+
+extern struct bus_type usb_bus_type;
+
+/**
+ * struct usb_class_driver - identifies a USB driver that wants to use the USB major number
+ * @name: devfs name for this driver. Will also be used by the driver
+ * class code to create a usb class device.
+ * @fops: pointer to the struct file_operations of this driver.
+ * @mode: the mode for the devfs file to be created for this driver.
+ * @minor_base: the start of the minor range for this driver.
+ *
+ * This structure is used for the usb_register_dev() and
+ * usb_unregister_dev() functions, to consolodate a number of the
+ * paramaters used for them.
+ */
+struct usb_class_driver {
+ char *name;
+ struct file_operations *fops;
+ mode_t mode;
+ int minor_base;
+};
+
+/*
+ * use these in module_init()/module_exit()
+ * and don't forget MODULE_DEVICE_TABLE(usb, ...)
+ */
+extern int usb_register(struct usb_driver *);
+extern void usb_deregister(struct usb_driver *);
+
+extern int usb_register_dev(struct usb_interface *intf,
+ struct usb_class_driver *class_driver);
+extern void usb_deregister_dev(struct usb_interface *intf,
+ struct usb_class_driver *class_driver);
+
+extern int usb_device_probe(struct device *dev);
+extern int usb_device_remove(struct device *dev);
+extern int STDCALL usb_disabled(void);
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * URB support, for asynchronous request completions
+ */
+
+/*
+ * urb->transfer_flags:
+ */
+#define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */
+#define URB_ISO_ASAP 0x0002 /* iso-only, urb->start_frame ignored */
+#define URB_NO_DMA_MAP 0x0004 /* urb->*_dma are valid on submit */
+#define URB_ASYNC_UNLINK 0x0008 /* usb_unlink_urb() returns asap */
+#define URB_NO_FSBR 0x0020 /* UHCI-specific */
+#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet */
+#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */
+
+struct usb_iso_packet_descriptor {
+ unsigned int offset;
+ unsigned int length; /* expected length */
+ unsigned int actual_length;
+ unsigned int status;
+};
+
+struct urb;
+struct pt_regs;
+
+typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);
+
+/**
+ * struct urb - USB Request Block
+ * @urb_list: For use by current owner of the URB.
+ * @pipe: Holds endpoint number, direction, type, and more.
+ * Create these values with the eight macros available;
+ * usb_{snd,rcv}TYPEpipe(dev,endpoint), where the type is "ctrl"
+ * (control), "bulk", "int" (interrupt), or "iso" (isochronous).
+ * For example usb_sndbulkpipe() or usb_rcvintpipe(). Endpoint
+ * numbers range from zero to fifteen. Note that "in" endpoint two
+ * is a different endpoint (and pipe) from "out" endpoint two.
+ * The current configuration controls the existence, type, and
+ * maximum packet size of any given endpoint.
+ * @dev: Identifies the USB device to perform the request.
+ * @status: This is read in non-iso completion functions to get the
+ * status of the particular request. ISO requests only use it
+ * to tell whether the URB was unlinked; detailed status for
+ * each frame is in the fields of the iso_frame-desc.
+ * @transfer_flags: A variety of flags may be used to affect how URB
+ * submission, unlinking, or operation are handled. Different
+ * kinds of URB can use different flags.
+ * @transfer_buffer: This identifies the buffer to (or from) which
+ * the I/O request will be performed (unless URB_NO_DMA_MAP is set).
+ * This buffer must be suitable for DMA; allocate it with kmalloc()
+ * or equivalent. For transfers to "in" endpoints, contents of
+ * this buffer will be modified. This buffer is used for data
+ * phases of control transfers.
+ * @transfer_dma: When transfer_flags includes URB_NO_DMA_MAP, the device
+ * driver is saying that it provided this DMA address, which the host
+ * controller driver should use instead of the transfer_buffer.
+ * @transfer_buffer_length: How big is transfer_buffer. The transfer may
+ * be broken up into chunks according to the current maximum packet
+ * size for the endpoint, which is a function of the configuration
+ * and is encoded in the pipe. When the length is zero, neither
+ * transfer_buffer nor transfer_dma is used.
+ * @actual_length: This is read in non-iso completion functions, and
+ * it tells how many bytes (out of transfer_buffer_length) were
+ * transferred. It will normally be the same as requested, unless
+ * either an error was reported or a short read was performed.
+ * The URB_SHORT_NOT_OK transfer flag may be used to make such
+ * short reads be reported as errors.
+ * @setup_packet: Only used for control transfers, this points to eight bytes
+ * of setup data. Control transfers always start by sending this data
+ * to the device. Then transfer_buffer is read or written, if needed.
+ * (Not used when URB_NO_DMA_MAP is set.)
+ * @setup_dma: For control transfers with URB_NO_DMA_MAP set, the device
+ * driver has provided this DMA address for the setup packet. The
+ * host controller driver should use this instead of setup_buffer.
+ * If there is a data phase, its buffer is identified by transfer_dma.
+ * @start_frame: Returns the initial frame for interrupt or isochronous
+ * transfers.
+ * @number_of_packets: Lists the number of ISO transfer buffers.
+ * @interval: Specifies the polling interval for interrupt or isochronous
+ * transfers. The units are frames (milliseconds) for for full and low
+ * speed devices, and microframes (1/8 millisecond) for highspeed ones.
+ * @error_count: Returns the number of ISO transfers that reported errors.
+ * @context: For use in completion functions. This normally points to
+ * request-specific driver context.
+ * @complete: Completion handler. This URB is passed as the parameter to the
+ * completion function. The completion function may then do what
+ * it likes with the URB, including resubmitting or freeing it.
+ * @iso_frame_desc: Used to provide arrays of ISO transfer buffers and to
+ * collect the transfer status for each buffer.
+ *
+ * This structure identifies USB transfer requests. URBs must be allocated by
+ * calling usb_alloc_urb() and freed with a call to usb_free_urb().
+ * Initialization may be done using various usb_fill_*_urb() functions. URBs
+ * are submitted using usb_submit_urb(), and pending requests may be canceled
+ * using usb_unlink_urb().
+ *
+ * Data Transfer Buffers:
+ *
+ * Normally drivers provide I/O buffers allocated with kmalloc() or otherwise
+ * taken from the general page pool. That is provided by transfer_buffer
+ * (control requests also use setup_packet), and host controller drivers
+ * perform a dma mapping (and unmapping) for each buffer transferred. Those
+ * mapping operations can be expensive on some platforms (perhaps using a dma
+ * bounce buffer or talking to an IOMMU),
+ * although they're cheap on commodity x86 and ppc hardware.
+ *
+ * Alternatively, drivers may pass the URB_NO_DMA_MAP transfer flag, which
+ * tells the host controller driver that no such mapping is needed since
+ * the device driver is DMA-aware. For example, they might allocate a DMA
+ * buffer with usb_buffer_alloc(), or call usb_buffer_map().
+ * When this transfer flag is provided, host controller drivers will use the
+ * dma addresses found in the transfer_dma and/or setup_dma fields rather than
+ * determing a dma address themselves.
+ *
+ * Initialization:
+ *
+ * All URBs submitted must initialize dev, pipe,
+ * transfer_flags (may be zero), complete, timeout (may be zero).
+ * The URB_ASYNC_UNLINK transfer flag affects later invocations of
+ * the usb_unlink_urb() routine.
+ *
+ * All URBs must also initialize
+ * transfer_buffer and transfer_buffer_length. They may provide the
+ * URB_SHORT_NOT_OK transfer flag, indicating that short reads are
+ * to be treated as errors; that flag is invalid for write requests.
+ *
+ * Bulk URBs may
+ * use the URB_ZERO_PACKET transfer flag, indicating that bulk OUT transfers
+ * should always terminate with a short packet, even if it means adding an
+ * extra zero length packet.
+ *
+ * Control URBs must provide a setup_packet.
+ *
+ * Interrupt UBS must provide an interval, saying how often (in milliseconds
+ * or, for highspeed devices, 125 microsecond units)
+ * to poll for transfers. After the URB has been submitted, the interval
+ * and start_frame fields reflect how the transfer was actually scheduled.
+ * The polling interval may be more frequent than requested.
+ * For example, some controllers have a maximum interval of 32 microseconds,
+ * while others support intervals of up to 1024 microseconds.
+ * Isochronous URBs also have transfer intervals. (Note that for isochronous
+ * endpoints, as well as high speed interrupt endpoints, the encoding of
+ * the transfer interval in the endpoint descriptor is logarithmic.)
+ *
+ * Isochronous URBs normally use the URB_ISO_ASAP transfer flag, telling
+ * the host controller to schedule the transfer as soon as bandwidth
+ * utilization allows, and then set start_frame to reflect the actual frame
+ * selected during submission. Otherwise drivers must specify the start_frame
+ * and handle the case where the transfer can't begin then. However, drivers
+ * won't know how bandwidth is currently allocated, and while they can
+ * find the current frame using usb_get_current_frame_number () they can't
+ * know the range for that frame number. (Ranges for frame counter values
+ * are HC-specific, and can go from 256 to 65536 frames from "now".)
+ *
+ * Isochronous URBs have a different data transfer model, in part because
+ * the quality of service is only "best effort". Callers provide specially
+ * allocated URBs, with number_of_packets worth of iso_frame_desc structures
+ * at the end. Each such packet is an individual ISO transfer. Isochronous
+ * URBs are normally queued, submitted by drivers to arrange that
+ * transfers are at least double buffered, and then explicitly resubmitted
+ * in completion handlers, so
+ * that data (such as audio or video) streams at as constant a rate as the
+ * host controller scheduler can support.
+ *
+ * Completion Callbacks:
+ *
+ * The completion callback is made in_interrupt(), and one of the first
+ * things that a completion handler should do is check the status field.
+ * The status field is provided for all URBs. It is used to report
+ * unlinked URBs, and status for all non-ISO transfers. It should not
+ * be examined before the URB is returned to the completion handler.
+ *
+ * The context field is normally used to link URBs back to the relevant
+ * driver or request state.
+ *
+ * When completion callback is invoked for non-isochronous URBs, the
+ * actual_length field tells how many bytes were transferred.
+ *
+ * ISO transfer status is reported in the status and actual_length fields
+ * of the iso_frame_desc array, and the number of errors is reported in
+ * error_count. Completion callbacks for ISO transfers will normally
+ * (re)submit URBs to ensure a constant transfer rate.
+ */
+struct urb
+{
+ spinlock_t lock; /* lock for the URB */
+ atomic_t count; /* reference count of the URB */
+ void *hcpriv; /* private data for host controller */
+ struct list_head urb_list; /* list pointer to all active urbs */
+ struct usb_device *dev; /* (in) pointer to associated device */
+ unsigned int pipe; /* (in) pipe information */
+ int status; /* (return) non-ISO status */
+ unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
+ void *transfer_buffer; /* (in) associated data buffer */
+ dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
+ int transfer_buffer_length; /* (in) data buffer length */
+ int actual_length; /* (return) actual transfer length */
+ int bandwidth; /* bandwidth for INT/ISO request */
+ unsigned char *setup_packet; /* (in) setup packet (control only) */
+ dma_addr_t setup_dma; /* (in) dma addr for setup_packet */
+ int start_frame; /* (modify) start frame (INT/ISO) */
+ int number_of_packets; /* (in) number of ISO packets */
+ int interval; /* (in) transfer interval (INT/ISO) */
+ int error_count; /* (return) number of ISO errors */
+ int timeout; /* (in) timeout, in jiffies */
+ void *context; /* (in) context for completion */
+ usb_complete_t complete; /* (in) completion routine */
+ struct usb_iso_packet_descriptor iso_frame_desc[0]; /* (in) ISO ONLY */
+};
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * usb_fill_control_urb - initializes a control urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @setup_packet: pointer to the setup_packet buffer
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ *
+ * Initializes a control urb with the proper information needed to submit
+ * it to a device.
+ */
+static inline void usb_fill_control_urb (struct urb *urb,
+ struct usb_device *dev,
+ unsigned int pipe,
+ unsigned char *setup_packet,
+ void *transfer_buffer,
+ int buffer_length,
+ usb_complete_t complete,
+ void *context)
+{
+ spin_lock_init(&urb->lock);
+ urb->dev = dev;
+ urb->pipe = pipe;
+ urb->setup_packet = setup_packet;
+ urb->transfer_buffer = transfer_buffer;
+ urb->transfer_buffer_length = buffer_length;
+ urb->complete = complete;
+ urb->context = context;
+}
+
+/**
+ * usb_fill_bulk_urb - macro to help initialize a bulk urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ *
+ * Initializes a bulk urb with the proper information needed to submit it
+ * to a device.
+ */
+static inline void usb_fill_bulk_urb (struct urb *urb,
+ struct usb_device *dev,
+ unsigned int pipe,
+ void *transfer_buffer,
+ int buffer_length,
+ usb_complete_t complete,
+ void *context)
+{
+ spin_lock_init(&urb->lock);
+ urb->dev = dev;
+ urb->pipe = pipe;
+ urb->transfer_buffer = transfer_buffer;
+ urb->transfer_buffer_length = buffer_length;
+ urb->complete = complete;
+ urb->context = context;
+}
+
+/**
+ * usb_fill_int_urb - macro to help initialize a interrupt urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ * @interval: what to set the urb interval to, encoded like
+ * the endpoint descriptor's bInterval value.
+ *
+ * Initializes a interrupt urb with the proper information needed to submit
+ * it to a device.
+ * Note that high speed interrupt endpoints use a logarithmic encoding of
+ * the endpoint interval, and express polling intervals in microframes
+ * (eight per millisecond) rather than in frames (one per millisecond).
+ */
+static inline void usb_fill_int_urb (struct urb *urb,
+ struct usb_device *dev,
+ unsigned int pipe,
+ void *transfer_buffer,
+ int buffer_length,
+ usb_complete_t complete,
+ void *context,
+ int interval)
+{
+ spin_lock_init(&urb->lock);
+ urb->dev = dev;
+ urb->pipe = pipe;
+ urb->transfer_buffer = transfer_buffer;
+ urb->transfer_buffer_length = buffer_length;
+ urb->complete = complete;
+ urb->context = context;
+ if (dev->speed == USB_SPEED_HIGH)
+ urb->interval = 1 << (interval - 1);
+ else
+ urb->interval = interval;
+ urb->start_frame = -1;
+}
+
+extern void STDCALL usb_init_urb(struct urb *urb);
+extern struct urb STDCALL *usb_alloc_urb(int iso_packets, int mem_flags);
+extern void STDCALL usb_free_urb(struct urb *urb);
+#define usb_put_urb usb_free_urb
+extern struct urb STDCALL *usb_get_urb(struct urb *urb);
+extern int STDCALL usb_submit_urb(struct urb *urb, int mem_flags);
+extern int STDCALL usb_unlink_urb(struct urb *urb);
+
+#define HAVE_USB_BUFFERS
+void *usb_buffer_alloc (struct usb_device *dev, size_t size,
+ int mem_flags, dma_addr_t *dma);
+void usb_buffer_free (struct usb_device *dev, size_t size,
+ void *addr, dma_addr_t dma);
+
+struct urb *usb_buffer_map (struct urb *urb);
+void usb_buffer_dmasync (struct urb *urb);
+void usb_buffer_unmap (struct urb *urb);
+
+struct scatterlist;
+int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int nents);
+void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int n_hw_ents);
+void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int n_hw_ents);
+
+/*-------------------------------------------------------------------*
+ * SYNCHRONOUS CALL SUPPORT *
+ *-------------------------------------------------------------------*/
+
+extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
+ __u8 request, __u8 requesttype, __u16 value, __u16 index,
+ void *data, __u16 size, int timeout);
+extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
+ void *data, int len, int *actual_length,
+ int timeout);
+
+/* wrappers around usb_control_msg() for the most common standard requests */
+extern int usb_get_descriptor(struct usb_device *dev, unsigned char desctype,
+ unsigned char descindex, void *buf, int size);
+extern int usb_get_device_descriptor(struct usb_device *dev);
+extern int usb_get_status(struct usb_device *dev,
+ int type, int target, void *data);
+extern int usb_get_string(struct usb_device *dev,
+ unsigned short langid, unsigned char index, void *buf, int size);
+extern int usb_string(struct usb_device *dev, int index,
+ char *buf, size_t size);
+
+/* wrappers that also update important state inside usbcore */
+extern int usb_clear_halt(struct usb_device *dev, int pipe);
+extern int usb_set_configuration(struct usb_device *dev, int configuration);
+extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
+
+/*
+ * timeouts, in seconds, used for sending/receiving control messages
+ * they typically complete within a few frames (msec) after they're issued
+ * USB identifies 5 second timeouts, maybe more in a few cases, and a few
+ * slow devices (like some MGE Ellipse UPSes) actually push that limit.
+ */
+#define USB_CTRL_GET_TIMEOUT 5
+#define USB_CTRL_SET_TIMEOUT 5
+
+
+/**
+ * struct usb_sg_request - support for scatter/gather I/O
+ * @status: zero indicates success, else negative errno
+ * @bytes: counts bytes transferred.
+ *
+ * These requests are initialized using usb_sg_init(), and then are used
+ * as request handles passed to usb_sg_wait() or usb_sg_cancel(). Most
+ * members of the request object aren't for driver access.
+ *
+ * The status and bytecount values are valid only after usb_sg_wait()
+ * returns. If the status is zero, then the bytecount matches the total
+ * from the request.
+ *
+ * After an error completion, drivers may need to clear a halt condition
+ * on the endpoint.
+ */
+struct usb_sg_request {
+ int status;
+ size_t bytes;
+
+ // members not documented above are private to usbcore,
+ // and are not provided for driver access!
+ spinlock_t lock;
+
+ struct usb_device *dev;
+ int pipe;
+ struct scatterlist *sg;
+ int nents;
+
+ int entries;
+ struct urb **urbs;
+
+ int count;
+ struct completion complete;
+};
+
+int usb_sg_init (
+ struct usb_sg_request *io,
+ struct usb_device *dev,
+ unsigned pipe,
+ unsigned period,
+ struct scatterlist *sg,
+ int nents,
+ size_t length,
+ int mem_flags
+);
+void usb_sg_cancel (struct usb_sg_request *io);
+void usb_sg_wait (struct usb_sg_request *io);
+
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * 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) [Historical; now gone]
+ * - direction (1 bit)
+ * - speed (1 bit) [Historical and specific to USB 1.1; now gone.]
+ * - max packet size (2 bits: 8, 16, 32 or 64) [Historical; now gone.]
+ * - 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:
+ *
+ * - max size: bits 0-1 [Historical; now gone.]
+ * - direction: bit 7 (0 = Host-to-Device [Out],
+ * 1 = Device-to-Host [In] ...
+ * like endpoint bEndpointAddress)
+ * - device: bits 8-14 ... bit positions known to uhci-hcd
+ * - endpoint: bits 15-18 ... bit positions known to uhci-hcd
+ * - Data0/1: bit 19 [Historical; now gone. ]
+ * - lowspeed: bit 26 [Historical; now gone. ]
+ * - 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 UHCI
+ * specification, so that much of the uhci driver can just mask the bits
+ * appropriately.
+ */
+
+/* NOTE: these are not the standard USB_ENDPOINT_XFER_* values!! */
+#define PIPE_ISOCHRONOUS 0
+#define PIPE_INTERRUPT 1
+#define PIPE_CONTROL 2
+#define PIPE_BULK 3
+
+#define usb_maxpacket(dev, pipe, out) (out \
+ ? (dev)->epmaxpacketout[usb_pipeendpoint(pipe)] \
+ : (dev)->epmaxpacketin [usb_pipeendpoint(pipe)] )
+
+#define usb_pipein(pipe) ((pipe) & USB_DIR_IN)
+#define usb_pipeout(pipe) (!usb_pipein(pipe))
+#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f)
+#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf)
+#define usb_pipetype(pipe) (((pipe) >> 30) & 3)
+#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS)
+#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT)
+#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL)
+#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK)
+
+/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
+#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)
+#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep)))
+#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | ((bit) << (ep)))
+
+/* Endpoint halt control/status ... likewise USE WITH CAUTION */
+#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep)))
+#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep)))
+
+
+static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
+{
+ return (dev->devnum << 8) | (endpoint << 15);
+}
+
+/* Create various pipes... */
+#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)
+
+/* -------------------------------------------------------------------------- */
+
+/*
+ * Debugging and troubleshooting/diagnostic 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_device(struct usb_device *);
+void usb_show_string(struct usb_device *dev, char *id, int index);
+
+#ifdef DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , __FILE__ , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+
+
+
+#ifdef DEBUG_MODE
+#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ## arg)
+#define err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ## arg)
+#endif
+
+#ifndef DEBUG_MODE
+#define info(format, arg...) do {} while (0)
+#define err(format, arg...) do {} while (0)
+#define warn(format, arg...) do {} while (0)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif
-/*\r
- * This file holds USB constants and structures that are needed for USB\r
- * device APIs. These are used by the USB device model, which is defined\r
- * in chapter 9 of the USB 2.0 specification. Linux has several APIs in C\r
- * that need these:\r
- *\r
- * - the master/host side Linux-USB kernel driver API;\r
- * - the "usbfs" user space API; and\r
- * - (eventually) a Linux "gadget" slave/device side driver API.\r
- *\r
- * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems\r
- * act either as a USB master/host or as a USB slave/device. That means\r
- * the master and slave side APIs will benefit from working well together.\r
- */\r
-\r
-#ifndef __LINUX_USB_CH9_H\r
-#define __LINUX_USB_CH9_H\r
-#if 0\r
-#include <asm/types.h> /* __u8 etc */\r
-#endif\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* CONTROL REQUEST SUPPORT */\r
-\r
-/*\r
- * USB directions\r
- *\r
- * This bit flag is used in endpoint descriptors' bEndpointAddress field.\r
- * It's also one of three fields in control requests bRequestType.\r
- */\r
-#define USB_DIR_OUT 0 /* to device */\r
-#define USB_DIR_IN 0x80 /* to host */\r
-\r
-/*\r
- * USB types, the second of three bRequestType fields\r
- */\r
-#define USB_TYPE_MASK (0x03 << 5)\r
-#define USB_TYPE_STANDARD (0x00 << 5)\r
-#define USB_TYPE_CLASS (0x01 << 5)\r
-#define USB_TYPE_VENDOR (0x02 << 5)\r
-#define USB_TYPE_RESERVED (0x03 << 5)\r
-\r
-/*\r
- * USB recipients, the third of three bRequestType fields\r
- */\r
-#define USB_RECIP_MASK 0x1f\r
-#define USB_RECIP_DEVICE 0x00\r
-#define USB_RECIP_INTERFACE 0x01\r
-#define USB_RECIP_ENDPOINT 0x02\r
-#define USB_RECIP_OTHER 0x03\r
-\r
-/*\r
- * Standard requests, for the bRequest field of a SETUP packet.\r
- *\r
- * These are qualified by the bRequestType field, so that for example\r
- * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved\r
- * by a GET_STATUS request.\r
- */\r
-#define USB_REQ_GET_STATUS 0x00\r
-#define USB_REQ_CLEAR_FEATURE 0x01\r
-#define USB_REQ_SET_FEATURE 0x03\r
-#define USB_REQ_SET_ADDRESS 0x05\r
-#define USB_REQ_GET_DESCRIPTOR 0x06\r
-#define USB_REQ_SET_DESCRIPTOR 0x07\r
-#define USB_REQ_GET_CONFIGURATION 0x08\r
-#define USB_REQ_SET_CONFIGURATION 0x09\r
-#define USB_REQ_GET_INTERFACE 0x0A\r
-#define USB_REQ_SET_INTERFACE 0x0B\r
-#define USB_REQ_SYNCH_FRAME 0x0C\r
-\r
-\r
-/**\r
- * struct usb_ctrlrequest - SETUP data for a USB device control request\r
- * @bRequestType: matches the USB bmRequestType field\r
- * @bRequest: matches the USB bRequest field\r
- * @wValue: matches the USB wValue field (le16 byte order)\r
- * @wIndex: matches the USB wIndex field (le16 byte order)\r
- * @wLength: matches the USB wLength field (le16 byte order)\r
- *\r
- * This structure is used to send control requests to a USB device. It matches\r
- * the different fields of the USB 2.0 Spec section 9.3, table 9-2. See the\r
- * USB spec for a fuller description of the different fields, and what they are\r
- * used for.\r
- *\r
- * Note that the driver for any interface can issue control requests.\r
- * For most devices, interfaces don't coordinate with each other, so\r
- * such requests may be made at any time.\r
- */\r
-struct usb_ctrlrequest {\r
- __u8 bRequestType;\r
- __u8 bRequest;\r
- __u16 wValue;\r
- __u16 wIndex;\r
- __u16 wLength;\r
-} __attribute__ ((packed));\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/*\r
- * STANDARD DESCRIPTORS ... as returned by GET_DESCRIPTOR, or\r
- * (rarely) accepted by SET_DESCRIPTOR.\r
- *\r
- * Note that all multi-byte values here are encoded in little endian\r
- * byte order "on the wire". But when exposed through Linux-USB APIs,\r
- * they've been converted to cpu byte order.\r
- */\r
-\r
-/*\r
- * Descriptor types ... USB 2.0 spec table 9.5\r
- */\r
-#define USB_DT_DEVICE 0x01\r
-#define USB_DT_CONFIG 0x02\r
-#define USB_DT_STRING 0x03\r
-#define USB_DT_INTERFACE 0x04\r
-#define USB_DT_ENDPOINT 0x05\r
-#define USB_DT_DEVICE_QUALIFIER 0x06\r
-#define USB_DT_OTHER_SPEED_CONFIG 0x07\r
-#define USB_DT_INTERFACE_POWER 0x08\r
-\r
-/* All standard descriptors have these 2 fields at the beginning */\r
-struct usb_descriptor_header {\r
- __u8 bLength;\r
- __u8 bDescriptorType;\r
-} __attribute__ ((packed));\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* USB_DT_DEVICE: Device descriptor */\r
-struct usb_device_descriptor {\r
- __u8 bLength;\r
- __u8 bDescriptorType;\r
-\r
- __u16 bcdUSB;\r
- __u8 bDeviceClass;\r
- __u8 bDeviceSubClass;\r
- __u8 bDeviceProtocol;\r
- __u8 bMaxPacketSize0;\r
- __u16 idVendor;\r
- __u16 idProduct;\r
- __u16 bcdDevice;\r
- __u8 iManufacturer;\r
- __u8 iProduct;\r
- __u8 iSerialNumber;\r
- __u8 bNumConfigurations;\r
-} __attribute__ ((packed));\r
-\r
-#define USB_DT_DEVICE_SIZE 18\r
-\r
-\r
-/*\r
- * Device and/or Interface Class codes\r
- * as found in bDeviceClass or bInterfaceClass\r
- * and defined by www.usb.org documents\r
- */\r
-#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */\r
-#define USB_CLASS_AUDIO 1\r
-#define USB_CLASS_COMM 2\r
-#define USB_CLASS_HID 3\r
-#define USB_CLASS_PHYSICAL 5\r
-#define USB_CLASS_STILL_IMAGE 6\r
-#define USB_CLASS_PRINTER 7\r
-#define USB_CLASS_MASS_STORAGE 8\r
-#define USB_CLASS_HUB 9\r
-#define USB_CLASS_CDC_DATA 0x0a\r
-#define USB_CLASS_CSCID 0x0b /* chip+ smart card */\r
-#define USB_CLASS_CONTENT_SEC 0x0d /* content security */\r
-#define USB_CLASS_APP_SPEC 0xfe\r
-#define USB_CLASS_VENDOR_SPEC 0xff\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* USB_DT_CONFIG: Configuration descriptor information.\r
- *\r
- * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the\r
- * descriptor type is different. Highspeed-capable devices can look\r
- * different depending on what speed they're currently running. Only\r
- * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG\r
- * descriptors.\r
- */\r
-struct usb_config_descriptor {\r
- __u8 bLength;\r
- __u8 bDescriptorType;\r
-\r
- __u16 wTotalLength;\r
- __u8 bNumInterfaces;\r
- __u8 bConfigurationValue;\r
- __u8 iConfiguration;\r
- __u8 bmAttributes;\r
- __u8 bMaxPower;\r
-} __attribute__ ((packed));\r
-\r
-#define USB_DT_CONFIG_SIZE 9\r
-\r
-/* from config descriptor bmAttributes */\r
-#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */\r
-#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */\r
-#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* USB_DT_STRING: String descriptor */\r
-struct usb_string_descriptor {\r
- __u8 bLength;\r
- __u8 bDescriptorType;\r
-\r
- __u16 wData[1]; /* UTF-16LE encoded */\r
-} __attribute__ ((packed));\r
-\r
-/* note that "string" zero is special, it holds language codes that\r
- * the device supports, not Unicode characters.\r
- */\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* USB_DT_INTERFACE: Interface descriptor */\r
-struct usb_interface_descriptor {\r
- __u8 bLength;\r
- __u8 bDescriptorType;\r
-\r
- __u8 bInterfaceNumber;\r
- __u8 bAlternateSetting;\r
- __u8 bNumEndpoints;\r
- __u8 bInterfaceClass;\r
- __u8 bInterfaceSubClass;\r
- __u8 bInterfaceProtocol;\r
- __u8 iInterface;\r
-} __attribute__ ((packed));\r
-\r
-#define USB_DT_INTERFACE_SIZE 9\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* USB_DT_ENDPOINT: Endpoint descriptor */\r
-struct usb_endpoint_descriptor {\r
- __u8 bLength;\r
- __u8 bDescriptorType;\r
-\r
- __u8 bEndpointAddress;\r
- __u8 bmAttributes;\r
- __u16 wMaxPacketSize;\r
- __u8 bInterval;\r
-\r
- // NOTE: these two are _only_ in audio endpoints.\r
- // use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof.\r
- __u8 bRefresh;\r
- __u8 bSynchAddress;\r
-} __attribute__ ((packed));\r
-\r
-#define USB_DT_ENDPOINT_SIZE 7\r
-#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */\r
-\r
-\r
-/*\r
- * Endpoints\r
- */\r
-#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */\r
-#define USB_ENDPOINT_DIR_MASK 0x80\r
-\r
-#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */\r
-#define USB_ENDPOINT_XFER_CONTROL 0\r
-#define USB_ENDPOINT_XFER_ISOC 1\r
-#define USB_ENDPOINT_XFER_BULK 2\r
-#define USB_ENDPOINT_XFER_INT 3\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */\r
-struct usb_qualifier_descriptor {\r
- __u8 bLength;\r
- __u8 bDescriptorType;\r
-\r
- __u16 bcdUSB;\r
- __u8 bDeviceClass;\r
- __u8 bDeviceSubClass;\r
- __u8 bDeviceProtocol;\r
- __u8 bMaxPacketSize0;\r
- __u8 bNumConfigurations;\r
- __u8 bRESERVED;\r
-} __attribute__ ((packed));\r
-\r
-\r
-/*-------------------------------------------------------------------------*/\r
-\r
-/* USB 2.0 defines three speeds, here's how Linux identifies them */\r
-\r
-enum usb_device_speed {\r
- USB_SPEED_UNKNOWN = 0, /* enumerating */\r
- USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */\r
- USB_SPEED_HIGH /* usb 2.0 */\r
-};\r
-\r
-enum usb_device_state {\r
- /* NOTATTACHED isn't in the USB spec, and this state acts\r
- * the same as ATTACHED ... but it's clearer this way.\r
- */\r
- USB_STATE_NOTATTACHED = 0,\r
-\r
- /* the chapter 9 device states */\r
- USB_STATE_ATTACHED,\r
- USB_STATE_POWERED,\r
- USB_STATE_DEFAULT, /* limited function */\r
- USB_STATE_ADDRESS,\r
- USB_STATE_CONFIGURED, /* most functions */\r
-\r
- USB_STATE_SUSPENDED\r
-\r
- /* NOTE: there are actually four different SUSPENDED\r
- * states, returning to POWERED, DEFAULT, ADDRESS, or\r
- * CONFIGURED respectively when SOF tokens flow again.\r
- */\r
-};\r
-\r
-#endif /* __LINUX_USB_CH9_H */\r
+/*
+ * This file holds USB constants and structures that are needed for USB
+ * device APIs. These are used by the USB device model, which is defined
+ * in chapter 9 of the USB 2.0 specification. Linux has several APIs in C
+ * that need these:
+ *
+ * - the master/host side Linux-USB kernel driver API;
+ * - the "usbfs" user space API; and
+ * - (eventually) a Linux "gadget" slave/device side driver API.
+ *
+ * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems
+ * act either as a USB master/host or as a USB slave/device. That means
+ * the master and slave side APIs will benefit from working well together.
+ */
+
+#ifndef __LINUX_USB_CH9_H
+#define __LINUX_USB_CH9_H
+#if 0
+#include <asm/types.h> /* __u8 etc */
+#endif
+/*-------------------------------------------------------------------------*/
+
+/* CONTROL REQUEST SUPPORT */
+
+/*
+ * USB directions
+ *
+ * This bit flag is used in endpoint descriptors' bEndpointAddress field.
+ * It's also one of three fields in control requests bRequestType.
+ */
+#define USB_DIR_OUT 0 /* to device */
+#define USB_DIR_IN 0x80 /* to host */
+
+/*
+ * USB types, the second of three bRequestType fields
+ */
+#define USB_TYPE_MASK (0x03 << 5)
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+/*
+ * USB recipients, the third of three bRequestType fields
+ */
+#define USB_RECIP_MASK 0x1f
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+/*
+ * Standard requests, for the bRequest field of a SETUP packet.
+ *
+ * These are qualified by the bRequestType field, so that for example
+ * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved
+ * by a GET_STATUS request.
+ */
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+#define USB_REQ_SET_FEATURE 0x03
+#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
+
+
+/**
+ * struct usb_ctrlrequest - SETUP data for a USB device control request
+ * @bRequestType: matches the USB bmRequestType field
+ * @bRequest: matches the USB bRequest field
+ * @wValue: matches the USB wValue field (le16 byte order)
+ * @wIndex: matches the USB wIndex field (le16 byte order)
+ * @wLength: matches the USB wLength field (le16 byte order)
+ *
+ * This structure is used to send control requests to a USB device. It matches
+ * the different fields of the USB 2.0 Spec section 9.3, table 9-2. See the
+ * USB spec for a fuller description of the different fields, and what they are
+ * used for.
+ *
+ * Note that the driver for any interface can issue control requests.
+ * For most devices, interfaces don't coordinate with each other, so
+ * such requests may be made at any time.
+ */
+struct usb_ctrlrequest {
+ __u8 bRequestType;
+ __u8 bRequest;
+ __u16 wValue;
+ __u16 wIndex;
+ __u16 wLength;
+} __attribute__ ((packed));
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * STANDARD DESCRIPTORS ... as returned by GET_DESCRIPTOR, or
+ * (rarely) accepted by SET_DESCRIPTOR.
+ *
+ * Note that all multi-byte values here are encoded in little endian
+ * byte order "on the wire". But when exposed through Linux-USB APIs,
+ * they've been converted to cpu byte order.
+ */
+
+/*
+ * Descriptor types ... USB 2.0 spec table 9.5
+ */
+#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_DEVICE_QUALIFIER 0x06
+#define USB_DT_OTHER_SPEED_CONFIG 0x07
+#define USB_DT_INTERFACE_POWER 0x08
+
+/* All standard descriptors have these 2 fields at the beginning */
+struct usb_descriptor_header {
+ __u8 bLength;
+ __u8 bDescriptorType;
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_DEVICE: Device descriptor */
+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;
+} __attribute__ ((packed));
+
+#define USB_DT_DEVICE_SIZE 18
+
+
+/*
+ * Device and/or Interface Class codes
+ * as found in bDeviceClass or bInterfaceClass
+ * and defined by www.usb.org documents
+ */
+#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
+#define USB_CLASS_AUDIO 1
+#define USB_CLASS_COMM 2
+#define USB_CLASS_HID 3
+#define USB_CLASS_PHYSICAL 5
+#define USB_CLASS_STILL_IMAGE 6
+#define USB_CLASS_PRINTER 7
+#define USB_CLASS_MASS_STORAGE 8
+#define USB_CLASS_HUB 9
+#define USB_CLASS_CDC_DATA 0x0a
+#define USB_CLASS_CSCID 0x0b /* chip+ smart card */
+#define USB_CLASS_CONTENT_SEC 0x0d /* content security */
+#define USB_CLASS_APP_SPEC 0xfe
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_CONFIG: Configuration descriptor information.
+ *
+ * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the
+ * descriptor type is different. Highspeed-capable devices can look
+ * different depending on what speed they're currently running. Only
+ * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG
+ * descriptors.
+ */
+struct usb_config_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u16 wTotalLength;
+ __u8 bNumInterfaces;
+ __u8 bConfigurationValue;
+ __u8 iConfiguration;
+ __u8 bmAttributes;
+ __u8 bMaxPower;
+} __attribute__ ((packed));
+
+#define USB_DT_CONFIG_SIZE 9
+
+/* from config descriptor bmAttributes */
+#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */
+#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */
+#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_STRING: String descriptor */
+struct usb_string_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u16 wData[1]; /* UTF-16LE encoded */
+} __attribute__ ((packed));
+
+/* note that "string" zero is special, it holds language codes that
+ * the device supports, not Unicode characters.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_INTERFACE: Interface descriptor */
+struct usb_interface_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bInterfaceNumber;
+ __u8 bAlternateSetting;
+ __u8 bNumEndpoints;
+ __u8 bInterfaceClass;
+ __u8 bInterfaceSubClass;
+ __u8 bInterfaceProtocol;
+ __u8 iInterface;
+} __attribute__ ((packed));
+
+#define USB_DT_INTERFACE_SIZE 9
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_ENDPOINT: Endpoint descriptor */
+struct usb_endpoint_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bEndpointAddress;
+ __u8 bmAttributes;
+ __u16 wMaxPacketSize;
+ __u8 bInterval;
+
+ // NOTE: these two are _only_ in audio endpoints.
+ // use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof.
+ __u8 bRefresh;
+ __u8 bSynchAddress;
+} __attribute__ ((packed));
+
+#define USB_DT_ENDPOINT_SIZE 7
+#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
+
+
+/*
+ * Endpoints
+ */
+#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK 0x80
+
+#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
+#define USB_ENDPOINT_XFER_CONTROL 0
+#define USB_ENDPOINT_XFER_ISOC 1
+#define USB_ENDPOINT_XFER_BULK 2
+#define USB_ENDPOINT_XFER_INT 3
+
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */
+struct usb_qualifier_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u16 bcdUSB;
+ __u8 bDeviceClass;
+ __u8 bDeviceSubClass;
+ __u8 bDeviceProtocol;
+ __u8 bMaxPacketSize0;
+ __u8 bNumConfigurations;
+ __u8 bRESERVED;
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
+/* USB 2.0 defines three speeds, here's how Linux identifies them */
+
+enum usb_device_speed {
+ USB_SPEED_UNKNOWN = 0, /* enumerating */
+ USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */
+ USB_SPEED_HIGH /* usb 2.0 */
+};
+
+enum usb_device_state {
+ /* NOTATTACHED isn't in the USB spec, and this state acts
+ * the same as ATTACHED ... but it's clearer this way.
+ */
+ USB_STATE_NOTATTACHED = 0,
+
+ /* the chapter 9 device states */
+ USB_STATE_ATTACHED,
+ USB_STATE_POWERED,
+ USB_STATE_DEFAULT, /* limited function */
+ USB_STATE_ADDRESS,
+ USB_STATE_CONFIGURED, /* most functions */
+
+ USB_STATE_SUSPENDED
+
+ /* NOTE: there are actually four different SUSPENDED
+ * states, returning to POWERED, DEFAULT, ADDRESS, or
+ * CONFIGURED respectively when SOF tokens flow again.
+ */
+};
+
+#endif /* __LINUX_USB_CH9_H */
-/*\r
- * USB support for XBOX, based on Linux kernel source\r
- *\r
- * 2003-06-21 Georg Acher (georg@acher.org)\r
- *\r
-*/\r
- \r
-#include "../usb_wrapper.h"\r
-\r
-void subsys_usb_init(void);\r
-void module_exit_usb_exit(void);\r
-\r
-extern struct pci_device_id *module_table_pci_ids;\r
-\r
-// straigth call...\r
-int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id);\r
-void usb_hcd_pci_remove (struct pci_dev *dev);\r
-\r
-void XPADInit(void);\r
-void XPADRemove(void);\r
-void XRemoteInit(void);\r
-void XRemoteRemove(void);\r
-\r
-extern int (*thread_handler)(void*);\r
-int (*hub_thread_handler)(void*);\r
-\r
-extern int nousb;\r
-extern int xpad_num;\r
-\r
-struct pci_dev xx_ohci_dev={\r
- .vendor = 0,\r
- .device = 0,\r
- .bus = NULL,\r
- .irq = 1, // currently not used...\r
- .slot_name = "OHCI",\r
- .dev = {.name = "PCI",.dma_mask=1},\r
- .base = {0xfed00000}, \r
- .flags = {}\r
-};\r
-\r
-/*------------------------------------------------------------------------*/ \r
-void BootStartUSB(void)\r
-{\r
- int n;\r
-\r
- nousb=0;\r
-\r
- init_wrapper();\r
- subsys_usb_init();\r
- hub_thread_handler=thread_handler;\r
- usb_hcd_pci_probe(&xx_ohci_dev, module_table_pci_ids); \r
- XPADInit();\r
- \r
- XRemoteInit();\r
- \r
- UsbKeyBoardInit();\r
-\r
- for(n=0;n<30;n++) {\r
- USBGetEvents();\r
- wait_ms(1);\r
- }\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void USBGetEvents(void)\r
-{ \r
- inc_jiffies(1);\r
- do_all_timers();\r
- hub_thread_handler(NULL);\r
- handle_irqs(-1); \r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void BootStopUSB(void)\r
-{\r
- int n;\r
- \r
- XPADRemove();\r
- XRemoteRemove();\r
- UsbKeyBoardRemove();\r
- \r
- for(n=0;n<100;n++)\r
- {\r
- USBGetEvents();\r
- wait_ms(1);\r
- } \r
-\r
- module_exit_usb_exit();\r
- usb_hcd_pci_remove(&xx_ohci_dev);\r
- \r
-} \r
-/*------------------------------------------------------------------------*/ \r
+/*
+ * USB support for XBOX, based on Linux kernel source
+ *
+ * 2003-06-21 Georg Acher (georg@acher.org)
+ *
+*/
+
+#include "../usb_wrapper.h"
+
+void subsys_usb_init(void);
+void module_exit_usb_exit(void);
+
+extern struct pci_device_id *module_table_pci_ids;
+
+// straigth call...
+int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id);
+void usb_hcd_pci_remove (struct pci_dev *dev);
+
+void XPADInit(void);
+void XPADRemove(void);
+void XRemoteInit(void);
+void XRemoteRemove(void);
+
+extern int (*thread_handler)(void*);
+int (*hub_thread_handler)(void*);
+
+extern int nousb;
+extern int xpad_num;
+
+struct pci_dev xx_ohci_dev={
+ .vendor = 0,
+ .device = 0,
+ .bus = NULL,
+ .irq = 1, // currently not used...
+ .slot_name = "OHCI",
+ .dev = {.name = "PCI",.dma_mask=1},
+ .base = {0xfed00000},
+ .flags = {}
+};
+
+/*------------------------------------------------------------------------*/
+void BootStartUSB(void)
+{
+ int n;
+
+ nousb=0;
+
+ init_wrapper();
+ subsys_usb_init();
+ hub_thread_handler=thread_handler;
+ usb_hcd_pci_probe(&xx_ohci_dev, module_table_pci_ids);
+ XPADInit();
+
+ XRemoteInit();
+
+ UsbKeyBoardInit();
+
+ for(n=0;n<30;n++) {
+ USBGetEvents();
+ wait_ms(1);
+ }
+}
+/*------------------------------------------------------------------------*/
+void USBGetEvents(void)
+{
+ inc_jiffies(1);
+ do_all_timers();
+ hub_thread_handler(NULL);
+ handle_irqs(-1);
+}
+/*------------------------------------------------------------------------*/
+void BootStopUSB(void)
+{
+ int n;
+
+ XPADRemove();
+ XRemoteRemove();
+ UsbKeyBoardRemove();
+
+ for(n=0;n<100;n++)
+ {
+ USBGetEvents();
+ wait_ms(1);
+ }
+
+ module_exit_usb_exit();
+ usb_hcd_pci_remove(&xx_ohci_dev);
+
+}
+/*------------------------------------------------------------------------*/
-\r
-O_TARGET := usbwrapper.o BootUSB.o linuxwrapper.o xpad.o xremote.o usbkey.o risefall.o\r
-\r
-include $(TOPDIR)/Rules.make\r
+
+O_TARGET := usbwrapper.o BootUSB.o linuxwrapper.o xpad.o xremote.o usbkey.o risefall.o
+
+include $(TOPDIR)/Rules.make
-/*\r
- * USB support based on Linux kernel source\r
- *\r
- * 2003-06-21 Georg Acher (georg@acher.org)\r
- *\r
- * Concept:\r
- * \r
- * 1) Forget all device interrupts, scheduling, semaphores, threads etc.\r
- * 1a) Forget all DMA and PCI helper functions\r
- * 2) Forget usbdevfs, procfs and ioctls\r
- * 3) Emulate OHCI interrupts and root hub timer by polling\r
- * 4) Emulate hub kernel thread by polling\r
- * 5) Emulate synchronous USB-messages (usb_*_msg) with busy waiting\r
- *\r
- * To be done:\r
- * 6) Remove code bloat \r
- *\r
- */\r
-\r
-#include "../usb_wrapper.h"\r
-\r
-/* internal state */\r
-\r
-static struct pci_dev *pci_probe_dev;\r
-extern int (*thread_handler)(void*);\r
-extern void* thread_parm;\r
-\r
-struct my_irqs reg_irqs[MAX_IRQS];\r
-int num_irqs;\r
-int need_wakeup;\r
-\r
-int my_jiffies;\r
-\r
-struct timer_list *main_timer_list[MAX_TIMERS];\r
-struct dummy_process act_cur={0};\r
-struct dummy_process *my_current;\r
-\r
-int (*thread_handler)(void*);\r
-void* thread_parm;\r
-\r
-#define MAX_DRVS 8\r
-static struct device_driver *m_drivers[MAX_DRVS];\r
-static int drvs_num;\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* \r
- * Helper functions for top-level system\r
- */\r
-/*------------------------------------------------------------------------*/ \r
-void init_wrapper(struct pci_dev *probe_dev)\r
-{\r
- int n;\r
- for(n=0;n<MAX_TIMERS;n++)\r
- {\r
- main_timer_list[n]=NULL;\r
- }\r
-\r
- my_jiffies=0;\r
- num_irqs=0;\r
- my_current=&act_cur;\r
- pci_probe_dev=probe_dev;\r
-\r
- for(n=0;n<MAX_IRQS;n++)\r
- {\r
- reg_irqs[n].handler=NULL;\r
- reg_irqs[n].irq=-1;\r
- }\r
- drvs_num=0;\r
- need_wakeup=0;\r
- for(n=0;n<MAX_DRVS;n++)\r
- m_drivers[n]=NULL;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void handle_irqs(int irq)\r
-{\r
- int n;\r
-// printk("handle irqs\n");\r
- for(n=0;n<MAX_IRQS;n++)\r
- {\r
- if (reg_irqs[n].handler && (irq==reg_irqs[n].irq || irq==-1))\r
- reg_irqs[n].handler(reg_irqs[n].irq,reg_irqs[n].data,NULL);\r
- }\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void inc_jiffies(int n)\r
-{\r
- my_jiffies+=n;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void do_all_timers(void)\r
-{\r
- int n;\r
- for(n=0;n<MAX_TIMERS;n++)\r
- {\r
- if (main_timer_list[n] &&\r
- main_timer_list[n]->function && main_timer_list[n]->expires) \r
- {\r
- void (*function)(unsigned long)=main_timer_list[n]->function;\r
- unsigned long data=main_timer_list[n]->data;\r
- main_timer_list[n]->expires=0;\r
-\r
- main_timer_list[n]=NULL; // remove timer\r
-// printk("do timer %i fn %p\n",n,function);\r
-\r
- function(data);\r
- }\r
- }\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-// Purpose: Remember thread procedure and data in global var\r
-// ReactOS Purpose: Create real kernel thread\r
-int my_kernel_thread(int STDCALL (*handler)(void*), void* parm, int flags)\r
-{\r
- HANDLE hThread;\r
- //thread_handler=handler;\r
- //thread_parm=parm;\r
- //return 42; // PID :-)\r
- \r
- ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);\r
-\r
- PsCreateSystemThread(&hThread,\r
- THREAD_ALL_ACCESS,\r
- NULL,\r
- NULL,\r
- NULL,\r
- (PKSTART_ROUTINE)handler,\r
- parm);\r
-\r
- return (int)hThread; // FIXME: Correct?\r
-}\r
-\r
-// Kill the process\r
-int my_kill_proc(int pid, int signal, int unk)\r
-{\r
- HANDLE hThread;\r
-\r
- // TODO: Implement actual process killing\r
-\r
- hThread = (HANDLE)pid;\r
- ZwClose(hThread);\r
-\r
- return 0;\r
-}\r
-\r
-/*------------------------------------------------------------------------*/ \r
-/* Device management\r
- * As simple as possible, but as complete as necessary ...\r
- */\r
-/*------------------------------------------------------------------------*/ \r
-\r
-\r
-/* calls probe function for hotplug (which does device matching), this is the\r
-only link between usbcore and the registered device drivers! */\r
-int my_device_add(struct device *dev)\r
-{\r
- int n,found=0;\r
-// printk("drv_num %i %p %p\n",drvs_num,m_drivers[0]->probe,m_drivers[1]->probe);\r
- if (dev->driver)\r
- {\r
- if (dev->driver->probe)\r
- return dev->driver->probe(dev);\r
- }\r
- else\r
- {\r
- for(n=0;n<drvs_num;n++)\r
- {\r
- if (m_drivers[n]->probe)\r
- {\r
- dev->driver=m_drivers[n];\r
-// printk("probe%i %p ",n,m_drivers[n]->probe);\r
- if (m_drivers[n]->probe(dev) == 0)\r
- {\r
-// return 0;\r
- found=1;\r
- }\r
- }\r
- }\r
- if (found) return 0;\r
- }\r
- dev->driver=NULL;\r
- return -ENODEV;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-int my_driver_register(struct device_driver *driver)\r
-{\r
-\r
- if (drvs_num<MAX_DRVS)\r
- {\r
-// printk("driver_register %i: %p %p",drvs_num,driver,driver->probe);\r
- m_drivers[drvs_num++]=driver;\r
- return 0;\r
- }\r
- return -1;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-int my_device_unregister(struct device *dev)\r
-{\r
- if (dev->driver && dev->driver->remove)\r
- dev->driver->remove(dev);\r
- return 0;\r
- \r
-}\r
-/*------------------------------------------------------------------------*/ \r
-struct device *my_get_device(struct device *dev)\r
-{\r
- return NULL;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void my_device_initialize(struct device *dev)\r
-{\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void my_wake_up(PKEVENT evnt)\r
-{\r
- need_wakeup=1;\r
-\r
- KeSetEvent(evnt, 0, FALSE); // Signal event\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void my_init_waitqueue_head(PKEVENT evnt)\r
-{\r
- // this is used only in core/message.c, and it isn't needed there\r
- //KeInitializeEvent(evnt, NotificationEvent, TRUE); // signalled state\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-/* wait until woken up (only one wait allowed!) */\r
-int my_schedule_timeout(int x)\r
-{\r
- int wait=1;\r
- x+=10; // safety\r
-// printk("schedule_timeout %i\n",x);\r
- while(x>0)\r
- {\r
- do_all_timers();\r
-#ifndef HAVE_IRQS\r
- handle_irqs(-1);\r
-\r
-#endif\r
- if (need_wakeup)\r
- break;\r
- wait_ms(wait);\r
- inc_jiffies(wait);\r
- x-=wait;\r
- }\r
- need_wakeup=0;\r
-// printk("schedule DONE!!!!!!\n");\r
- return x;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void my_wait_for_completion(struct completion *x)\r
-{\r
- int n=100;\r
-// printk("wait for completion\n");\r
- while(!x->done && (n>0))\r
- {\r
- do_all_timers(); \r
-#ifndef HAVE_IRQS\r
- handle_irqs(-1);\r
-\r
-#endif\r
- wait_ms(10); \r
- n--;\r
- }\r
-// printk("wait for completion done %i\n",x->done);\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void my_interruptible_sleep_on(PKEVENT evnt)\r
-{\r
- KeWaitForSingleObject(evnt, Executive, KernelMode, FALSE, NULL);\r
- KeClearEvent(evnt); // reset to not-signalled\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-// Helper for pci_module_init\r
-/*------------------------------------------------------------------------*/ \r
-int my_pci_module_init(struct pci_driver *x)\r
-{\r
- struct pci_dev *dev=pci_probe_dev;\r
- const struct pci_device_id *id=NULL;\r
- if (!pci_probe_dev)\r
- {\r
- printk(KERN_ERR "PCI device not set!\n");\r
- return 0;\r
- }\r
- x->probe(dev, id);\r
- return 0;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-struct pci_dev *my_pci_find_slot(int a,int b)\r
-{\r
- return NULL;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-int my_pci_write_config_word(struct pci_dev *dev, int where, u16 val)\r
-{\r
- //dev->bus, dev->devfn, where, val\r
- OHCI_DEVICE_EXTENSION *dev_ext = (OHCI_DEVICE_EXTENSION *)dev->dev_ext;\r
-\r
- //FIXME: Is returning this value correct?\r
- //FIXME: Mixing pci_dev and win structs isn't a good thing at all\r
- return HalSetBusDataByOffset(PCIConfiguration, dev->bus->number, dev_ext->SystemIoSlotNumber, &val, where, sizeof(val));\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-int my_request_irq(unsigned int irq,\r
- int (*handler)(int,void *, struct pt_regs *),\r
- unsigned long mode, const char *desc, void *data)\r
-{\r
- if (num_irqs<MAX_IRQS)\r
- {\r
- reg_irqs[num_irqs].handler=handler;\r
- reg_irqs[num_irqs].irq=irq;\r
- reg_irqs[num_irqs].data=data;\r
- num_irqs++;\r
- return 0;\r
- }\r
- return 1;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-int my_free_irq(int irq, void* p)\r
-{\r
- /* No free... */\r
- return 0;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-// Lookaside funcs\r
-/*------------------------------------------------------------------------*/ \r
-kmem_cache_t *my_kmem_cache_create(const char *tag, size_t alloc_size,\r
- size_t offset, unsigned long flags,\r
- void *ctor,\r
- void *dtor)\r
-{\r
- //TODO: Take in account ctor and dtor - callbacks for alloc/free, flags and offset\r
- //FIXME: We assume this cache is always NPaged\r
- PNPAGED_LOOKASIDE_LIST Lookaside;\r
- ULONG Tag=0x11223344; //FIXME: Make this from tag\r
-\r
- Lookaside = ExAllocatePool(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST));\r
- \r
- ExInitializeNPagedLookasideList(\r
- Lookaside,\r
- NULL,\r
- NULL,\r
- 0,\r
- alloc_size,\r
- Tag,\r
- 0);\r
-\r
- return (kmem_cache_t *)Lookaside;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-BOOLEAN my_kmem_cache_destroy(kmem_cache_t *co)\r
-{\r
- ExDeleteNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)co);\r
-\r
- ExFreePool(co);\r
- return FALSE;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void *my_kmem_cache_alloc(kmem_cache_t *co, int flags)\r
-{\r
- return ExAllocateFromNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)co);\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void my_kmem_cache_free(kmem_cache_t *co, void *ptr)\r
-{\r
- ExFreeToNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)co, ptr);\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-// DMA, not used now\r
-/*------------------------------------------------------------------------*/ \r
-void *my_dma_pool_alloc(struct dma_pool *pool, int gfp_flags, dma_addr_t *dma_handle)\r
-{\r
- // HalAllocCommonBuffer\r
- // But ideally IoGetDmaAdapter\r
- return NULL;\r
-}\r
+/*
+ * USB support based on Linux kernel source
+ *
+ * 2003-06-21 Georg Acher (georg@acher.org)
+ *
+ * Concept:
+ *
+ * 1) Forget all device interrupts, scheduling, semaphores, threads etc.
+ * 1a) Forget all DMA and PCI helper functions
+ * 2) Forget usbdevfs, procfs and ioctls
+ * 3) Emulate OHCI interrupts and root hub timer by polling
+ * 4) Emulate hub kernel thread by polling
+ * 5) Emulate synchronous USB-messages (usb_*_msg) with busy waiting
+ *
+ * To be done:
+ * 6) Remove code bloat
+ *
+ */
+
+#include "../usb_wrapper.h"
+
+/* internal state */
+
+static struct pci_dev *pci_probe_dev;
+extern int (*thread_handler)(void*);
+extern void* thread_parm;
+
+struct my_irqs reg_irqs[MAX_IRQS];
+int num_irqs;
+int need_wakeup;
+
+int my_jiffies;
+
+struct timer_list *main_timer_list[MAX_TIMERS];
+struct dummy_process act_cur={0};
+struct dummy_process *my_current;
+
+int (*thread_handler)(void*);
+void* thread_parm;
+
+#define MAX_DRVS 8
+static struct device_driver *m_drivers[MAX_DRVS];
+static int drvs_num;
+
+/*------------------------------------------------------------------------*/
+/*
+ * Helper functions for top-level system
+ */
+/*------------------------------------------------------------------------*/
+void init_wrapper(struct pci_dev *probe_dev)
+{
+ int n;
+ for(n=0;n<MAX_TIMERS;n++)
+ {
+ main_timer_list[n]=NULL;
+ }
+
+ my_jiffies=0;
+ num_irqs=0;
+ my_current=&act_cur;
+ pci_probe_dev=probe_dev;
+
+ for(n=0;n<MAX_IRQS;n++)
+ {
+ reg_irqs[n].handler=NULL;
+ reg_irqs[n].irq=-1;
+ }
+ drvs_num=0;
+ need_wakeup=0;
+ for(n=0;n<MAX_DRVS;n++)
+ m_drivers[n]=NULL;
+}
+/*------------------------------------------------------------------------*/
+void handle_irqs(int irq)
+{
+ int n;
+// printk("handle irqs\n");
+ for(n=0;n<MAX_IRQS;n++)
+ {
+ if (reg_irqs[n].handler && (irq==reg_irqs[n].irq || irq==-1))
+ reg_irqs[n].handler(reg_irqs[n].irq,reg_irqs[n].data,NULL);
+ }
+}
+/*------------------------------------------------------------------------*/
+void inc_jiffies(int n)
+{
+ my_jiffies+=n;
+}
+/*------------------------------------------------------------------------*/
+void do_all_timers(void)
+{
+ int n;
+ for(n=0;n<MAX_TIMERS;n++)
+ {
+ if (main_timer_list[n] &&
+ main_timer_list[n]->function && main_timer_list[n]->expires)
+ {
+ void (*function)(unsigned long)=main_timer_list[n]->function;
+ unsigned long data=main_timer_list[n]->data;
+ main_timer_list[n]->expires=0;
+
+ main_timer_list[n]=NULL; // remove timer
+// printk("do timer %i fn %p\n",n,function);
+
+ function(data);
+ }
+ }
+}
+/*------------------------------------------------------------------------*/
+// Purpose: Remember thread procedure and data in global var
+// ReactOS Purpose: Create real kernel thread
+int my_kernel_thread(int STDCALL (*handler)(void*), void* parm, int flags)
+{
+ HANDLE hThread;
+ //thread_handler=handler;
+ //thread_parm=parm;
+ //return 42; // PID :-)
+
+ ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+ PsCreateSystemThread(&hThread,
+ THREAD_ALL_ACCESS,
+ NULL,
+ NULL,
+ NULL,
+ (PKSTART_ROUTINE)handler,
+ parm);
+
+ return (int)hThread; // FIXME: Correct?
+}
+
+// Kill the process
+int my_kill_proc(int pid, int signal, int unk)
+{
+ HANDLE hThread;
+
+ // TODO: Implement actual process killing
+
+ hThread = (HANDLE)pid;
+ ZwClose(hThread);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------*/
+/* Device management
+ * As simple as possible, but as complete as necessary ...
+ */
+/*------------------------------------------------------------------------*/
+
+
+/* calls probe function for hotplug (which does device matching), this is the
+only link between usbcore and the registered device drivers! */
+int my_device_add(struct device *dev)
+{
+ int n,found=0;
+// printk("drv_num %i %p %p\n",drvs_num,m_drivers[0]->probe,m_drivers[1]->probe);
+ if (dev->driver)
+ {
+ if (dev->driver->probe)
+ return dev->driver->probe(dev);
+ }
+ else
+ {
+ for(n=0;n<drvs_num;n++)
+ {
+ if (m_drivers[n]->probe)
+ {
+ dev->driver=m_drivers[n];
+// printk("probe%i %p ",n,m_drivers[n]->probe);
+ if (m_drivers[n]->probe(dev) == 0)
+ {
+// return 0;
+ found=1;
+ }
+ }
+ }
+ if (found) return 0;
+ }
+ dev->driver=NULL;
+ return -ENODEV;
+}
+/*------------------------------------------------------------------------*/
+int my_driver_register(struct device_driver *driver)
+{
+
+ if (drvs_num<MAX_DRVS)
+ {
+// printk("driver_register %i: %p %p",drvs_num,driver,driver->probe);
+ m_drivers[drvs_num++]=driver;
+ return 0;
+ }
+ return -1;
+}
+/*------------------------------------------------------------------------*/
+int my_device_unregister(struct device *dev)
+{
+ if (dev->driver && dev->driver->remove)
+ dev->driver->remove(dev);
+ return 0;
+
+}
+/*------------------------------------------------------------------------*/
+struct device *my_get_device(struct device *dev)
+{
+ return NULL;
+}
+/*------------------------------------------------------------------------*/
+void my_device_initialize(struct device *dev)
+{
+}
+/*------------------------------------------------------------------------*/
+void my_wake_up(PKEVENT evnt)
+{
+ need_wakeup=1;
+
+ KeSetEvent(evnt, 0, FALSE); // Signal event
+}
+/*------------------------------------------------------------------------*/
+void my_init_waitqueue_head(PKEVENT evnt)
+{
+ // this is used only in core/message.c, and it isn't needed there
+ //KeInitializeEvent(evnt, NotificationEvent, TRUE); // signalled state
+}
+/*------------------------------------------------------------------------*/
+/* wait until woken up (only one wait allowed!) */
+int my_schedule_timeout(int x)
+{
+ int wait=1;
+ x+=10; // safety
+// printk("schedule_timeout %i\n",x);
+ while(x>0)
+ {
+ do_all_timers();
+#ifndef HAVE_IRQS
+ handle_irqs(-1);
+
+#endif
+ if (need_wakeup)
+ break;
+ wait_ms(wait);
+ inc_jiffies(wait);
+ x-=wait;
+ }
+ need_wakeup=0;
+// printk("schedule DONE!!!!!!\n");
+ return x;
+}
+/*------------------------------------------------------------------------*/
+void my_wait_for_completion(struct completion *x)
+{
+ int n=100;
+// printk("wait for completion\n");
+ while(!x->done && (n>0))
+ {
+ do_all_timers();
+#ifndef HAVE_IRQS
+ handle_irqs(-1);
+
+#endif
+ wait_ms(10);
+ n--;
+ }
+// printk("wait for completion done %i\n",x->done);
+}
+/*------------------------------------------------------------------------*/
+void my_interruptible_sleep_on(PKEVENT evnt)
+{
+ KeWaitForSingleObject(evnt, Executive, KernelMode, FALSE, NULL);
+ KeClearEvent(evnt); // reset to not-signalled
+}
+/*------------------------------------------------------------------------*/
+// Helper for pci_module_init
+/*------------------------------------------------------------------------*/
+int my_pci_module_init(struct pci_driver *x)
+{
+ struct pci_dev *dev=pci_probe_dev;
+ const struct pci_device_id *id=NULL;
+ if (!pci_probe_dev)
+ {
+ printk(KERN_ERR "PCI device not set!\n");
+ return 0;
+ }
+ x->probe(dev, id);
+ return 0;
+}
+/*------------------------------------------------------------------------*/
+struct pci_dev *my_pci_find_slot(int a,int b)
+{
+ return NULL;
+}
+/*------------------------------------------------------------------------*/
+int my_pci_write_config_word(struct pci_dev *dev, int where, u16 val)
+{
+ //dev->bus, dev->devfn, where, val
+ OHCI_DEVICE_EXTENSION *dev_ext = (OHCI_DEVICE_EXTENSION *)dev->dev_ext;
+
+ //FIXME: Is returning this value correct?
+ //FIXME: Mixing pci_dev and win structs isn't a good thing at all
+ return HalSetBusDataByOffset(PCIConfiguration, dev->bus->number, dev_ext->SystemIoSlotNumber, &val, where, sizeof(val));
+}
+/*------------------------------------------------------------------------*/
+int my_request_irq(unsigned int irq,
+ int (*handler)(int,void *, struct pt_regs *),
+ unsigned long mode, const char *desc, void *data)
+{
+ if (num_irqs<MAX_IRQS)
+ {
+ reg_irqs[num_irqs].handler=handler;
+ reg_irqs[num_irqs].irq=irq;
+ reg_irqs[num_irqs].data=data;
+ num_irqs++;
+ return 0;
+ }
+ return 1;
+}
+/*------------------------------------------------------------------------*/
+int my_free_irq(int irq, void* p)
+{
+ /* No free... */
+ return 0;
+}
+/*------------------------------------------------------------------------*/
+// Lookaside funcs
+/*------------------------------------------------------------------------*/
+kmem_cache_t *my_kmem_cache_create(const char *tag, size_t alloc_size,
+ size_t offset, unsigned long flags,
+ void *ctor,
+ void *dtor)
+{
+ //TODO: Take in account ctor and dtor - callbacks for alloc/free, flags and offset
+ //FIXME: We assume this cache is always NPaged
+ PNPAGED_LOOKASIDE_LIST Lookaside;
+ ULONG Tag=0x11223344; //FIXME: Make this from tag
+
+ Lookaside = ExAllocatePool(NonPagedPool, sizeof(NPAGED_LOOKASIDE_LIST));
+
+ ExInitializeNPagedLookasideList(
+ Lookaside,
+ NULL,
+ NULL,
+ 0,
+ alloc_size,
+ Tag,
+ 0);
+
+ return (kmem_cache_t *)Lookaside;
+}
+/*------------------------------------------------------------------------*/
+BOOLEAN my_kmem_cache_destroy(kmem_cache_t *co)
+{
+ ExDeleteNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)co);
+
+ ExFreePool(co);
+ return FALSE;
+}
+/*------------------------------------------------------------------------*/
+void *my_kmem_cache_alloc(kmem_cache_t *co, int flags)
+{
+ return ExAllocateFromNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)co);
+}
+/*------------------------------------------------------------------------*/
+void my_kmem_cache_free(kmem_cache_t *co, void *ptr)
+{
+ ExFreeToNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)co, ptr);
+}
+/*------------------------------------------------------------------------*/
+// DMA, not used now
+/*------------------------------------------------------------------------*/
+void *my_dma_pool_alloc(struct dma_pool *pool, int gfp_flags, dma_addr_t *dma_handle)
+{
+ // HalAllocCommonBuffer
+ // But ideally IoGetDmaAdapter
+ return NULL;
+}
-#include "../usb_wrapper.h"\r
-#include "config.h"\r
-#include "xremote.h"\r
-\r
-// This is for the Xpad\r
-extern unsigned char xpad_button_history[7];\r
-\r
-// This is for the Keyboard\r
-extern unsigned int current_keyboard_key;\r
-\r
-int risefall_xpad_BUTTON(unsigned char selected_Button) {\r
- \r
- int xpad_id; \r
- int match;\r
- extern int xpad_num;\r
- \r
- // USB keyboard section \r
- \r
- match=0;\r
- if (current_keyboard_key!=0) {\r
- switch (selected_Button) {\r
- case TRIGGER_XPAD_KEY_A :\r
- if (current_keyboard_key == 0x28) match=1;\r
- break;\r
- case TRIGGER_XPAD_KEY_B :\r
- if (current_keyboard_key == 0x29) match=1;\r
- break;\r
- case TRIGGER_XPAD_PAD_UP :\r
- if (current_keyboard_key == 0x52) match=1;\r
- break;\r
- case TRIGGER_XPAD_PAD_DOWN :\r
- if (current_keyboard_key == 0x51) match=1;\r
- break;\r
- case TRIGGER_XPAD_PAD_LEFT :\r
- if (current_keyboard_key == 0x50) match=1;\r
- break;\r
- case TRIGGER_XPAD_PAD_RIGHT :\r
- if (current_keyboard_key == 0x4f) match=1;\r
- break;\r
- }\r
-\r
- if (match) {\r
- //A match occurred, so the event has now been processed\r
- //Clear it, and return success\r
- current_keyboard_key=0;\r
- return 1;\r
- }\r
- }\r
- \r
- // Xbox IR remote section\r
- \r
- match=0;\r
- if (!remotekeyIsRepeat) {\r
- /* We only grab the key event when the button is first pressed.\r
- * If it's being held down, we ignore the multiple events this \r
- * generates */\r
- \r
- switch (selected_Button) {\r
- case TRIGGER_XPAD_KEY_A:\r
- if (current_remote_key == RC_KEY_SELECT) match=1;\r
- break;\r
- case TRIGGER_XPAD_PAD_UP:\r
- if (current_remote_key == RC_KEY_UP) match=1;\r
- break;\r
- case TRIGGER_XPAD_PAD_DOWN:\r
- if (current_remote_key == RC_KEY_DOWN) match=1;\r
- break;\r
- case TRIGGER_XPAD_PAD_LEFT:\r
- if (current_remote_key == RC_KEY_LEFT) match=1;\r
- break;\r
- case TRIGGER_XPAD_PAD_RIGHT:\r
- if (current_remote_key == RC_KEY_RIGHT) match=1;\r
- break;\r
- case TRIGGER_XPAD_KEY_BACK:\r
- if (current_remote_key == RC_KEY_BACK) match=1;\r
- break;\r
- }\r
- if (match) {\r
- //A match occurred, so the event has now been processed\r
- //Clear it, and return success\r
- current_remote_key=0;\r
- remotekeyIsRepeat=0;\r
- return 1;\r
- }\r
- }\r
- \r
- // Xbox controller section\r
- if (selected_Button < 6) {\r
- \r
- unsigned char Button;\r
- \r
- Button = XPAD_current[0].keys[selected_Button];\r
- \r
- if ((Button>0x30)&&(xpad_button_history[selected_Button]==0)) {\r
- // Button Rising Edge\r
- xpad_button_history[selected_Button] = 1; \r
- return 1;\r
- } \r
- \r
- if ((Button==0x00)&&(xpad_button_history[selected_Button]==1)) {\r
- // Button Falling Edge\r
- xpad_button_history[selected_Button] = 0; \r
- return -1;\r
- } \r
- }\r
- \r
- if ((selected_Button > 5) & (selected_Button < 10) ) {\r
- \r
- unsigned char Buttonmask;\r
- \r
- switch (selected_Button) {\r
- case TRIGGER_XPAD_PAD_UP :\r
- Buttonmask = XPAD_PAD_UP; \r
- break;\r
- case TRIGGER_XPAD_PAD_DOWN :\r
- Buttonmask = XPAD_PAD_DOWN;\r
- break;\r
- case TRIGGER_XPAD_PAD_LEFT :\r
- Buttonmask = XPAD_PAD_LEFT;\r
- break;\r
- case TRIGGER_XPAD_PAD_RIGHT :\r
- Buttonmask = XPAD_PAD_RIGHT;\r
- break;\r
- } \r
- \r
- // Rising Edge\r
- if (((XPAD_current[0].pad&Buttonmask) != 0) & ((xpad_button_history[6]&Buttonmask) == 0)) {\r
- xpad_button_history[6] ^= Buttonmask; // Flip the Bit\r
- return 1;\r
- } \r
- // Falling Edge\r
- if (((XPAD_current[0].pad&Buttonmask) == 0) & ((xpad_button_history[6]&Buttonmask) != 0)) {\r
- xpad_button_history[6] ^= Buttonmask; // Flip the Bit\r
- return -1;\r
- }\r
- }\r
- return 0;\r
-}\r
+#include "../usb_wrapper.h"
+#include "config.h"
+#include "xremote.h"
+
+// This is for the Xpad
+extern unsigned char xpad_button_history[7];
+
+// This is for the Keyboard
+extern unsigned int current_keyboard_key;
+
+int risefall_xpad_BUTTON(unsigned char selected_Button) {
+
+ int xpad_id;
+ int match;
+ extern int xpad_num;
+
+ // USB keyboard section
+
+ match=0;
+ if (current_keyboard_key!=0) {
+ switch (selected_Button) {
+ case TRIGGER_XPAD_KEY_A :
+ if (current_keyboard_key == 0x28) match=1;
+ break;
+ case TRIGGER_XPAD_KEY_B :
+ if (current_keyboard_key == 0x29) match=1;
+ break;
+ case TRIGGER_XPAD_PAD_UP :
+ if (current_keyboard_key == 0x52) match=1;
+ break;
+ case TRIGGER_XPAD_PAD_DOWN :
+ if (current_keyboard_key == 0x51) match=1;
+ break;
+ case TRIGGER_XPAD_PAD_LEFT :
+ if (current_keyboard_key == 0x50) match=1;
+ break;
+ case TRIGGER_XPAD_PAD_RIGHT :
+ if (current_keyboard_key == 0x4f) match=1;
+ break;
+ }
+
+ if (match) {
+ //A match occurred, so the event has now been processed
+ //Clear it, and return success
+ current_keyboard_key=0;
+ return 1;
+ }
+ }
+
+ // Xbox IR remote section
+
+ match=0;
+ if (!remotekeyIsRepeat) {
+ /* We only grab the key event when the button is first pressed.
+ * If it's being held down, we ignore the multiple events this
+ * generates */
+
+ switch (selected_Button) {
+ case TRIGGER_XPAD_KEY_A:
+ if (current_remote_key == RC_KEY_SELECT) match=1;
+ break;
+ case TRIGGER_XPAD_PAD_UP:
+ if (current_remote_key == RC_KEY_UP) match=1;
+ break;
+ case TRIGGER_XPAD_PAD_DOWN:
+ if (current_remote_key == RC_KEY_DOWN) match=1;
+ break;
+ case TRIGGER_XPAD_PAD_LEFT:
+ if (current_remote_key == RC_KEY_LEFT) match=1;
+ break;
+ case TRIGGER_XPAD_PAD_RIGHT:
+ if (current_remote_key == RC_KEY_RIGHT) match=1;
+ break;
+ case TRIGGER_XPAD_KEY_BACK:
+ if (current_remote_key == RC_KEY_BACK) match=1;
+ break;
+ }
+ if (match) {
+ //A match occurred, so the event has now been processed
+ //Clear it, and return success
+ current_remote_key=0;
+ remotekeyIsRepeat=0;
+ return 1;
+ }
+ }
+
+ // Xbox controller section
+ if (selected_Button < 6) {
+
+ unsigned char Button;
+
+ Button = XPAD_current[0].keys[selected_Button];
+
+ if ((Button>0x30)&&(xpad_button_history[selected_Button]==0)) {
+ // Button Rising Edge
+ xpad_button_history[selected_Button] = 1;
+ return 1;
+ }
+
+ if ((Button==0x00)&&(xpad_button_history[selected_Button]==1)) {
+ // Button Falling Edge
+ xpad_button_history[selected_Button] = 0;
+ return -1;
+ }
+ }
+
+ if ((selected_Button > 5) & (selected_Button < 10) ) {
+
+ unsigned char Buttonmask;
+
+ switch (selected_Button) {
+ case TRIGGER_XPAD_PAD_UP :
+ Buttonmask = XPAD_PAD_UP;
+ break;
+ case TRIGGER_XPAD_PAD_DOWN :
+ Buttonmask = XPAD_PAD_DOWN;
+ break;
+ case TRIGGER_XPAD_PAD_LEFT :
+ Buttonmask = XPAD_PAD_LEFT;
+ break;
+ case TRIGGER_XPAD_PAD_RIGHT :
+ Buttonmask = XPAD_PAD_RIGHT;
+ break;
+ }
+
+ // Rising Edge
+ if (((XPAD_current[0].pad&Buttonmask) != 0) & ((xpad_button_history[6]&Buttonmask) == 0)) {
+ xpad_button_history[6] ^= Buttonmask; // Flip the Bit
+ return 1;
+ }
+ // Falling Edge
+ if (((XPAD_current[0].pad&Buttonmask) == 0) & ((xpad_button_history[6]&Buttonmask) != 0)) {
+ xpad_button_history[6] ^= Buttonmask; // Flip the Bit
+ return -1;
+ }
+ }
+ return 0;
+}
-#include "../usb_wrapper.h"\r
-\r
-\r
-void wait_ms(int mils)\r
-{\r
- LARGE_INTEGER Interval;\r
-\r
- Interval.QuadPart = -mils*10;\r
- KeDelayExecutionThread(KernelMode, FALSE, &Interval);\r
-}\r
+#include "../usb_wrapper.h"
+
+
+void wait_ms(int mils)
+{
+ LARGE_INTEGER Interval;
+
+ Interval.QuadPart = -mils*10;
+ KeDelayExecutionThread(KernelMode, FALSE, &Interval);
+}
-#include "../usb_wrapper.h"\r
-\r
-#define keyboarddebug 0\r
-\r
-#if keyboarddebug\r
-extern int printe(const char *szFormat, ...);\r
-int ycoffset = 0;\r
-#endif\r
-\r
-unsigned int current_keyboard_key;\r
-\r
-struct usb_kbd_info {\r
- struct urb *urb;\r
- unsigned char kbd_pkt[8];\r
- unsigned char old[8];\r
-\r
- /*\r
- struct input_dev dev;\r
- struct usb_device *usbdev;\r
- struct urb irq, led;\r
- struct usb_ctrlrequest dr;\r
- unsigned char leds, newleds;\r
- char name[128];\r
- int open;\r
- */\r
-};\r
-\r
-static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)\r
-{\r
- struct usb_kbd_info *kbd = urb->context;\r
- int i;\r
-\r
- if (urb->status) return;\r
- \r
- memcpy(kbd->kbd_pkt, urb->transfer_buffer, 8);\r
- \r
- current_keyboard_key = kbd->kbd_pkt[2];\r
- \r
- \r
- #if keyboarddebug\r
- ycoffset += 15;\r
- ycoffset = ycoffset % 600;\r
- VIDEO_CURSOR_POSX=20;\r
- VIDEO_CURSOR_POSY=ycoffset; \r
- printe(" -%02x %02x %02x %02x %02x %02x\n",kbd->kbd_pkt[0],kbd->kbd_pkt[1],kbd->kbd_pkt[2],kbd->kbd_pkt[3],kbd->kbd_pkt[4],kbd->kbd_pkt[5]);\r
- #endif\r
- \r
- usb_submit_urb(urb,GFP_ATOMIC);\r
- \r
-}\r
-\r
-static int usb_kbd_probe(struct usb_interface *intf, const struct usb_device_id *id)\r
-{\r
- struct urb *urb;\r
- struct usb_device *udev = interface_to_usbdev (intf);\r
- struct usb_endpoint_descriptor *ep_irq_in;\r
- struct usb_endpoint_descriptor *ep_irq_out;\r
- struct usb_kbd_info *usbk;\r
-\r
- int i, pipe, maxp;\r
- char *buf;\r
-\r
- usbk=(struct usb_kbd_info *)kmalloc(sizeof(struct usb_kbd_info),0);\r
- if (!usbk) return -1;\r
-\r
- urb=usb_alloc_urb(0,0);\r
- if (!urb) return -1;\r
-\r
- usbk->urb=urb;\r
-\r
- ep_irq_in = &intf->altsetting[0].endpoint[0].desc;\r
- usb_fill_int_urb(urb, udev,\r
- usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),\r
- usbk->kbd_pkt, 8, usb_kbd_irq,\r
- usbk, 8);\r
-\r
- usb_submit_urb(urb,GFP_ATOMIC);\r
- usb_set_intfdata(intf,usbk);\r
- #if keyboarddebug\r
- printe("USB Keyboard Connected\n"); \r
- #endif\r
-}\r
-\r
-\r
-static void usb_kbd_disconnect(struct usb_interface *intf)\r
-{\r
- struct usb_kbd_info *usbk = usb_get_intfdata (intf);\r
- usbprintk("Keyboard disconnected\n ");\r
- usb_unlink_urb(usbk->urb);\r
- usb_free_urb(usbk->urb);\r
- kfree(usbk);\r
-}\r
-\r
-static struct usb_device_id usb_kbd_id_table [] = {\r
- { USB_INTERFACE_INFO(3, 1, 1) },\r
- { } /* Terminating entry */\r
-};\r
-\r
-\r
-static struct usb_driver usb_kbd_driver = {\r
- .owner = THIS_MODULE,\r
- .name = "keyboard",\r
- .probe = usb_kbd_probe,\r
- .disconnect = usb_kbd_disconnect,\r
- .id_table = usb_kbd_id_table,\r
-};\r
-\r
-void UsbKeyBoardInit(void)\r
-{\r
-\r
- //current_remote_key=0;\r
- //sbprintk("Keyboard probe %p ",xremote_probe);\r
- if (usb_register(&usb_kbd_driver) < 0) {\r
- #if keyboarddebug\r
- printe("Unable to register Keyboard driver");\r
- #endif\r
- return;\r
- } \r
-}\r
-\r
-void UsbKeyBoardRemove(void) {\r
- usb_deregister(&usb_kbd_driver);\r
-}\r
+#include "../usb_wrapper.h"
+
+#define keyboarddebug 0
+
+#if keyboarddebug
+extern int printe(const char *szFormat, ...);
+int ycoffset = 0;
+#endif
+
+unsigned int current_keyboard_key;
+
+struct usb_kbd_info {
+ struct urb *urb;
+ unsigned char kbd_pkt[8];
+ unsigned char old[8];
+
+ /*
+ struct input_dev dev;
+ struct usb_device *usbdev;
+ struct urb irq, led;
+ struct usb_ctrlrequest dr;
+ unsigned char leds, newleds;
+ char name[128];
+ int open;
+ */
+};
+
+static void usb_kbd_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_kbd_info *kbd = urb->context;
+ int i;
+
+ if (urb->status) return;
+
+ memcpy(kbd->kbd_pkt, urb->transfer_buffer, 8);
+
+ current_keyboard_key = kbd->kbd_pkt[2];
+
+
+ #if keyboarddebug
+ ycoffset += 15;
+ ycoffset = ycoffset % 600;
+ VIDEO_CURSOR_POSX=20;
+ VIDEO_CURSOR_POSY=ycoffset;
+ printe(" -%02x %02x %02x %02x %02x %02x\n",kbd->kbd_pkt[0],kbd->kbd_pkt[1],kbd->kbd_pkt[2],kbd->kbd_pkt[3],kbd->kbd_pkt[4],kbd->kbd_pkt[5]);
+ #endif
+
+ usb_submit_urb(urb,GFP_ATOMIC);
+
+}
+
+static int usb_kbd_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct urb *urb;
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct usb_endpoint_descriptor *ep_irq_in;
+ struct usb_endpoint_descriptor *ep_irq_out;
+ struct usb_kbd_info *usbk;
+
+ int i, pipe, maxp;
+ char *buf;
+
+ usbk=(struct usb_kbd_info *)kmalloc(sizeof(struct usb_kbd_info),0);
+ if (!usbk) return -1;
+
+ urb=usb_alloc_urb(0,0);
+ if (!urb) return -1;
+
+ usbk->urb=urb;
+
+ ep_irq_in = &intf->altsetting[0].endpoint[0].desc;
+ usb_fill_int_urb(urb, udev,
+ usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
+ usbk->kbd_pkt, 8, usb_kbd_irq,
+ usbk, 8);
+
+ usb_submit_urb(urb,GFP_ATOMIC);
+ usb_set_intfdata(intf,usbk);
+ #if keyboarddebug
+ printe("USB Keyboard Connected\n");
+ #endif
+}
+
+
+static void usb_kbd_disconnect(struct usb_interface *intf)
+{
+ struct usb_kbd_info *usbk = usb_get_intfdata (intf);
+ usbprintk("Keyboard disconnected\n ");
+ usb_unlink_urb(usbk->urb);
+ usb_free_urb(usbk->urb);
+ kfree(usbk);
+}
+
+static struct usb_device_id usb_kbd_id_table [] = {
+ { USB_INTERFACE_INFO(3, 1, 1) },
+ { } /* Terminating entry */
+};
+
+
+static struct usb_driver usb_kbd_driver = {
+ .owner = THIS_MODULE,
+ .name = "keyboard",
+ .probe = usb_kbd_probe,
+ .disconnect = usb_kbd_disconnect,
+ .id_table = usb_kbd_id_table,
+};
+
+void UsbKeyBoardInit(void)
+{
+
+ //current_remote_key=0;
+ //sbprintk("Keyboard probe %p ",xremote_probe);
+ if (usb_register(&usb_kbd_driver) < 0) {
+ #if keyboarddebug
+ printe("Unable to register Keyboard driver");
+ #endif
+ return;
+ }
+}
+
+void UsbKeyBoardRemove(void) {
+ usb_deregister(&usb_kbd_driver);
+}
-/*\r
- * Interface calls to BIOS\r
- *\r
- * 2003-06-21 Georg Acher (georg@acher.org)\r
- *\r
- */\r
-\r
-#include "boot.h"\r
-#include <stdarg.h>\r
-#include "video.h"\r
-\r
-/*------------------------------------------------------------------------*/ \r
-// Output window for USB messages\r
-int usb_curs_x=0;\r
-int usb_curs_y=0;\r
-\r
-void zxprintf(char* fmt, ...)\r
-{\r
- va_list ap;\r
- char buffer[1024];\r
- int tmp_x, tmp_y;\r
- tmp_x=VIDEO_CURSOR_POSX;\r
- tmp_y=VIDEO_CURSOR_POSY;\r
- \r
- VIDEO_CURSOR_POSX=usb_curs_x;\r
- VIDEO_CURSOR_POSY=usb_curs_y;\r
- \r
- if ((VIDEO_CURSOR_POSY==0) || (VIDEO_CURSOR_POSY > (vmode.height -16)))\r
- {\r
- BootVideoClearScreen(&jpegBackdrop, 3*vmode.height/4, \r
- vmode.height);\r
- VIDEO_CURSOR_POSY=3*vmode.height/4;\r
- }\r
-\r
- va_start(ap, fmt);\r
- vsprintf(buffer,fmt,ap);\r
- //printk(buffer);\r
- va_end(ap);\r
-\r
- usb_curs_x=VIDEO_CURSOR_POSX;\r
- usb_curs_y=VIDEO_CURSOR_POSY;\r
- VIDEO_CURSOR_POSX=tmp_x;\r
- VIDEO_CURSOR_POSY=tmp_y;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-int zxsnprintf(char *buffer, size_t s, char* fmt, ...)\r
-{\r
- va_list ap;\r
- int x;\r
- va_start(ap, fmt);\r
- x=vsprintf(buffer,fmt,ap);\r
- va_end(ap);\r
- return x;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-int zxsprintf(char *buffer, char* fmt, ...)\r
-{\r
- va_list ap;\r
- int x;\r
- va_start(ap, fmt);\r
- x=vsprintf(buffer,fmt,ap);\r
- va_end(ap);\r
- return x;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
+/*
+ * Interface calls to BIOS
+ *
+ * 2003-06-21 Georg Acher (georg@acher.org)
+ *
+ */
+
+#include "boot.h"
+#include <stdarg.h>
+#include "video.h"
+
+/*------------------------------------------------------------------------*/
+// Output window for USB messages
+int usb_curs_x=0;
+int usb_curs_y=0;
+
+void zxprintf(char* fmt, ...)
+{
+ va_list ap;
+ char buffer[1024];
+ int tmp_x, tmp_y;
+ tmp_x=VIDEO_CURSOR_POSX;
+ tmp_y=VIDEO_CURSOR_POSY;
+
+ VIDEO_CURSOR_POSX=usb_curs_x;
+ VIDEO_CURSOR_POSY=usb_curs_y;
+
+ if ((VIDEO_CURSOR_POSY==0) || (VIDEO_CURSOR_POSY > (vmode.height -16)))
+ {
+ BootVideoClearScreen(&jpegBackdrop, 3*vmode.height/4,
+ vmode.height);
+ VIDEO_CURSOR_POSY=3*vmode.height/4;
+ }
+
+ va_start(ap, fmt);
+ vsprintf(buffer,fmt,ap);
+ //printk(buffer);
+ va_end(ap);
+
+ usb_curs_x=VIDEO_CURSOR_POSX;
+ usb_curs_y=VIDEO_CURSOR_POSY;
+ VIDEO_CURSOR_POSX=tmp_x;
+ VIDEO_CURSOR_POSY=tmp_y;
+}
+/*------------------------------------------------------------------------*/
+int zxsnprintf(char *buffer, size_t s, char* fmt, ...)
+{
+ va_list ap;
+ int x;
+ va_start(ap, fmt);
+ x=vsprintf(buffer,fmt,ap);
+ va_end(ap);
+ return x;
+}
+/*------------------------------------------------------------------------*/
+int zxsprintf(char *buffer, char* fmt, ...)
+{
+ va_list ap;
+ int x;
+ va_start(ap, fmt);
+ x=vsprintf(buffer,fmt,ap);
+ va_end(ap);
+ return x;
+}
+/*------------------------------------------------------------------------*/
-/*\r
- * Simple XPAD driver for XBOX\r
- *\r
- * (c) 2003-07-04, Georg Acher (georg@acher.org)\r
- *\r
- * Inspired by linux/drivers/usb/input/xpad.c\r
- * by Marko Friedemann <mfr@bmx-chemnitz.de>\r
- *\r
- */\r
-\r
-\r
-\r
-#include "../usb_wrapper.h"\r
-#include "config.h"\r
-\r
-// history for the Rising - falling events\r
-unsigned char xpad_button_history[7];\r
-\r
-/* Stores time and XPAD state */\r
-struct xpad_data XPAD_current[4];\r
-struct xpad_data XPAD_last[4];\r
-\r
-struct xpad_info\r
-{\r
- struct urb *urb;\r
- int num;\r
- unsigned char data[32];\r
-};\r
-\r
-int xpad_num=0;\r
-/*------------------------------------------------------------------------*/ \r
-static void xpad_irq(struct urb *urb, struct pt_regs *regs)\r
-{\r
- struct xpad_info *xpi = urb->context;\r
- unsigned char* data= urb->transfer_buffer;\r
- \r
-// struct xpad_data *xp=&XPAD_current[xpi->num];\r
-// struct xpad_data *xpo=&XPAD_last[xpi->num];\r
-\r
- /* This hack means the xpad event always gets posted to the\r
- * first xpad - avoids problems iterating over multiple xpads\r
- * as the xpi->num entries are not reused when xpads are\r
- * connected, then removed */\r
-\r
- struct xpad_data *xp=&XPAD_current[0];\r
- struct xpad_data *xpo=&XPAD_last[0];\r
- \r
- if (xpi->num<0 || xpi->num>3)\r
- return;\r
- \r
- memcpy(xpo,xp,sizeof(struct xpad_data));\r
- \r
- xp->stick_left_x=(short) (((short)data[13] << 8) | data[12]);\r
- xp->stick_left_y=(short) (((short)data[15] << 8) | data[14]);\r
- xp->stick_right_x=(short) (((short)data[17] << 8) | data[16]);\r
- xp->stick_right_y=(short) (((short)data[19] << 8) | data[18]);\r
- xp->trig_left= data[10];\r
- xp->trig_right= data[11];\r
- xp->pad = data[2]&0xf;\r
- xp->state = (data[2]>>4)&0xf;\r
- xp->keys[0] = data[4]; // a\r
- xp->keys[1] = data[5]; // b\r
- xp->keys[2] = data[6]; // x\r
- xp->keys[3] = data[7]; // y\r
- xp->keys[4] = data[8]; // black\r
- xp->keys[5] = data[9]; // white\r
- xp->timestamp=jiffies; // FIXME: A more uniform flowing time would be better... \r
- usb_submit_urb(urb,GFP_ATOMIC);\r
-\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)\r
-{\r
- struct urb *urb;\r
- struct usb_device *udev = interface_to_usbdev (intf);\r
- struct usb_endpoint_descriptor *ep_irq_in;\r
- struct usb_endpoint_descriptor *ep_irq_out;\r
- struct xpad_info *xpi;\r
-\r
- xpi=kmalloc(sizeof(struct xpad_info),GFP_KERNEL);\r
- if (!xpi) return -1;\r
-\r
- urb=usb_alloc_urb(0,0);\r
- if (!urb) return -1;\r
-\r
- xpi->urb=urb;\r
- xpi->num=xpad_num;\r
- ep_irq_in = &intf->altsetting[0].endpoint[0].desc;\r
- usb_fill_int_urb(urb, udev,\r
- usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),\r
- xpi->data, 32, xpad_irq,\r
- xpi, 32);\r
-\r
- usb_submit_urb(urb,GFP_ATOMIC);\r
-\r
- usb_set_intfdata(intf,xpi);\r
- usbprintk("XPAD #%i connected\n",xpad_num);\r
- #ifdef XPAD_VIBRA_STARTUP\r
- {\r
- // Brum Brum\r
- char data1[6]={0,6,0,120,0,120};\r
- char data2[6]={0,6,0,0,0,0};\r
- int dummy; \r
-\r
- usb_bulk_msg(udev, usb_sndbulkpipe(udev,2),\r
- data1, 6, &dummy, 500);\r
- wait_ms(500);\r
- usb_bulk_msg(udev, usb_sndbulkpipe(udev,2),\r
- data2, 6, &dummy, 500); \r
- }\r
- #endif\r
- xpad_num++;\r
- return 0;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-static void xpad_disconnect(struct usb_interface *intf)\r
-{\r
- struct xpad_info *xpi=usb_get_intfdata (intf);\r
- usb_unlink_urb(xpi->urb);\r
- usb_free_urb(xpi->urb);\r
- kfree(xpi);\r
- xpad_num--;\r
-}\r
-/*------------------------------------------------------------------------*/ \r
-static struct usb_device_id xpad_ids [] = {\r
- { USB_DEVICE(0x044f, 0x0f07) },//Thrustmaster, Inc. Controller\r
- { USB_DEVICE(0x045e, 0x0202) },//Microsoft Xbox Controller\r
- { USB_DEVICE(0x045e, 0x0285) },//Microsoft Xbox Controller S\r
- { USB_DEVICE(0x045e, 0x0289) },//Microsoft Xbox Controller S\r
- { USB_DEVICE(0x046d, 0xca88) },//Logitech Compact Controller for Xbox\r
- { USB_DEVICE(0x05fd, 0x1007) },//???Mad Catz Controller???\r
- { USB_DEVICE(0x05fd, 0x107a) },//InterAct PowerPad Pro\r
- { USB_DEVICE(0x0738, 0x4516) },//Mad Catz Control Pad\r
- { USB_DEVICE(0x0738, 0x4522) },//Mad Catz LumiCON\r
- { USB_DEVICE(0x0738, 0x4526) },//Mad Catz Control Pad Pro\r
- { USB_DEVICE(0x0738, 0x4536) },//Mad Catz MicroCON\r
- { USB_DEVICE(0x0738, 0x4556) },//Mad Catz Lynx Wireless Controller\r
- { USB_DEVICE(0x0c12, 0x9902) },//HAMA VibraX - *FAULTY HARDWARE*\r
- { USB_DEVICE(0x0e4c, 0x1097) },//Radica Gamester Controller\r
- { USB_DEVICE(0x0e4c, 0x2390) },//Radica Games Jtech Controller\r
- { USB_DEVICE(0x0e6f, 0x0003) },//Logic3 Freebird wireless Controller\r
- { USB_DEVICE(0x0e6f, 0x0005) },//Eclipse wireless Controlle\r
- { USB_DEVICE(0x0f30, 0x0202) },//Joytech Advanced Controller \r
- { USB_DEVICE(0xffff, 0xffff) },//Chinese-made Xbox Controller \r
- { USB_DEVICE(0x0000, 0x0000) }, // nothing detected - FAIL\r
- { } /* Terminating entry */ \r
-};\r
-\r
-\r
-static struct usb_driver xpad_driver = {\r
- .owner = THIS_MODULE,\r
- .name = "XPAD",\r
- .probe = xpad_probe,\r
- .disconnect = xpad_disconnect,\r
- .id_table = xpad_ids,\r
-};\r
-\r
-/*------------------------------------------------------------------------*/ \r
-void XPADInit(void)\r
-{\r
- int n;\r
- for(n=0;n<4;n++)\r
- {\r
- memset(&XPAD_current[n], 0, sizeof(struct xpad_data));\r
- memset(&XPAD_last[n], 0, sizeof(struct xpad_data));\r
- }\r
- memset(&xpad_button_history, 0, sizeof(xpad_button_history));\r
- \r
- usbprintk("XPAD probe %p ",xpad_probe);\r
- if (usb_register(&xpad_driver) < 0) {\r
- err("Unable to register XPAD driver");\r
- return;\r
- } \r
-}\r
-/*------------------------------------------------------------------------*/ \r
-void XPADRemove(void) {\r
- usb_deregister(&xpad_driver);\r
-}\r
-\r
-/*------------------------------------------------------------------------*/ \r
-\r
-\r
-\r
+/*
+ * Simple XPAD driver for XBOX
+ *
+ * (c) 2003-07-04, Georg Acher (georg@acher.org)
+ *
+ * Inspired by linux/drivers/usb/input/xpad.c
+ * by Marko Friedemann <mfr@bmx-chemnitz.de>
+ *
+ */
+
+
+
+#include "../usb_wrapper.h"
+#include "config.h"
+
+// history for the Rising - falling events
+unsigned char xpad_button_history[7];
+
+/* Stores time and XPAD state */
+struct xpad_data XPAD_current[4];
+struct xpad_data XPAD_last[4];
+
+struct xpad_info
+{
+ struct urb *urb;
+ int num;
+ unsigned char data[32];
+};
+
+int xpad_num=0;
+/*------------------------------------------------------------------------*/
+static void xpad_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct xpad_info *xpi = urb->context;
+ unsigned char* data= urb->transfer_buffer;
+
+// struct xpad_data *xp=&XPAD_current[xpi->num];
+// struct xpad_data *xpo=&XPAD_last[xpi->num];
+
+ /* This hack means the xpad event always gets posted to the
+ * first xpad - avoids problems iterating over multiple xpads
+ * as the xpi->num entries are not reused when xpads are
+ * connected, then removed */
+
+ struct xpad_data *xp=&XPAD_current[0];
+ struct xpad_data *xpo=&XPAD_last[0];
+
+ if (xpi->num<0 || xpi->num>3)
+ return;
+
+ memcpy(xpo,xp,sizeof(struct xpad_data));
+
+ xp->stick_left_x=(short) (((short)data[13] << 8) | data[12]);
+ xp->stick_left_y=(short) (((short)data[15] << 8) | data[14]);
+ xp->stick_right_x=(short) (((short)data[17] << 8) | data[16]);
+ xp->stick_right_y=(short) (((short)data[19] << 8) | data[18]);
+ xp->trig_left= data[10];
+ xp->trig_right= data[11];
+ xp->pad = data[2]&0xf;
+ xp->state = (data[2]>>4)&0xf;
+ xp->keys[0] = data[4]; // a
+ xp->keys[1] = data[5]; // b
+ xp->keys[2] = data[6]; // x
+ xp->keys[3] = data[7]; // y
+ xp->keys[4] = data[8]; // black
+ xp->keys[5] = data[9]; // white
+ xp->timestamp=jiffies; // FIXME: A more uniform flowing time would be better...
+ usb_submit_urb(urb,GFP_ATOMIC);
+
+}
+/*------------------------------------------------------------------------*/
+static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct urb *urb;
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct usb_endpoint_descriptor *ep_irq_in;
+ struct usb_endpoint_descriptor *ep_irq_out;
+ struct xpad_info *xpi;
+
+ xpi=kmalloc(sizeof(struct xpad_info),GFP_KERNEL);
+ if (!xpi) return -1;
+
+ urb=usb_alloc_urb(0,0);
+ if (!urb) return -1;
+
+ xpi->urb=urb;
+ xpi->num=xpad_num;
+ ep_irq_in = &intf->altsetting[0].endpoint[0].desc;
+ usb_fill_int_urb(urb, udev,
+ usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
+ xpi->data, 32, xpad_irq,
+ xpi, 32);
+
+ usb_submit_urb(urb,GFP_ATOMIC);
+
+ usb_set_intfdata(intf,xpi);
+ usbprintk("XPAD #%i connected\n",xpad_num);
+ #ifdef XPAD_VIBRA_STARTUP
+ {
+ // Brum Brum
+ char data1[6]={0,6,0,120,0,120};
+ char data2[6]={0,6,0,0,0,0};
+ int dummy;
+
+ usb_bulk_msg(udev, usb_sndbulkpipe(udev,2),
+ data1, 6, &dummy, 500);
+ wait_ms(500);
+ usb_bulk_msg(udev, usb_sndbulkpipe(udev,2),
+ data2, 6, &dummy, 500);
+ }
+ #endif
+ xpad_num++;
+ return 0;
+}
+/*------------------------------------------------------------------------*/
+static void xpad_disconnect(struct usb_interface *intf)
+{
+ struct xpad_info *xpi=usb_get_intfdata (intf);
+ usb_unlink_urb(xpi->urb);
+ usb_free_urb(xpi->urb);
+ kfree(xpi);
+ xpad_num--;
+}
+/*------------------------------------------------------------------------*/
+static struct usb_device_id xpad_ids [] = {
+ { USB_DEVICE(0x044f, 0x0f07) },//Thrustmaster, Inc. Controller
+ { USB_DEVICE(0x045e, 0x0202) },//Microsoft Xbox Controller
+ { USB_DEVICE(0x045e, 0x0285) },//Microsoft Xbox Controller S
+ { USB_DEVICE(0x045e, 0x0289) },//Microsoft Xbox Controller S
+ { USB_DEVICE(0x046d, 0xca88) },//Logitech Compact Controller for Xbox
+ { USB_DEVICE(0x05fd, 0x1007) },//???Mad Catz Controller???
+ { USB_DEVICE(0x05fd, 0x107a) },//InterAct PowerPad Pro
+ { USB_DEVICE(0x0738, 0x4516) },//Mad Catz Control Pad
+ { USB_DEVICE(0x0738, 0x4522) },//Mad Catz LumiCON
+ { USB_DEVICE(0x0738, 0x4526) },//Mad Catz Control Pad Pro
+ { USB_DEVICE(0x0738, 0x4536) },//Mad Catz MicroCON
+ { USB_DEVICE(0x0738, 0x4556) },//Mad Catz Lynx Wireless Controller
+ { USB_DEVICE(0x0c12, 0x9902) },//HAMA VibraX - *FAULTY HARDWARE*
+ { USB_DEVICE(0x0e4c, 0x1097) },//Radica Gamester Controller
+ { USB_DEVICE(0x0e4c, 0x2390) },//Radica Games Jtech Controller
+ { USB_DEVICE(0x0e6f, 0x0003) },//Logic3 Freebird wireless Controller
+ { USB_DEVICE(0x0e6f, 0x0005) },//Eclipse wireless Controlle
+ { USB_DEVICE(0x0f30, 0x0202) },//Joytech Advanced Controller
+ { USB_DEVICE(0xffff, 0xffff) },//Chinese-made Xbox Controller
+ { USB_DEVICE(0x0000, 0x0000) }, // nothing detected - FAIL
+ { } /* Terminating entry */
+};
+
+
+static struct usb_driver xpad_driver = {
+ .owner = THIS_MODULE,
+ .name = "XPAD",
+ .probe = xpad_probe,
+ .disconnect = xpad_disconnect,
+ .id_table = xpad_ids,
+};
+
+/*------------------------------------------------------------------------*/
+void XPADInit(void)
+{
+ int n;
+ for(n=0;n<4;n++)
+ {
+ memset(&XPAD_current[n], 0, sizeof(struct xpad_data));
+ memset(&XPAD_last[n], 0, sizeof(struct xpad_data));
+ }
+ memset(&xpad_button_history, 0, sizeof(xpad_button_history));
+
+ usbprintk("XPAD probe %p ",xpad_probe);
+ if (usb_register(&xpad_driver) < 0) {
+ err("Unable to register XPAD driver");
+ return;
+ }
+}
+/*------------------------------------------------------------------------*/
+void XPADRemove(void) {
+ usb_deregister(&xpad_driver);
+}
+
+/*------------------------------------------------------------------------*/
+
+
+
-/*\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or \r
- * (at your option) any later version.\r
- * \r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- * \r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
- * \r
- */\r
-\r
-/*\r
- * $Id: xremote.c,v 1.5 2004/11/22 19:10:57 davidmpye Exp $\r
- *\r
- * Copyright (c) 2002 Steven Toth <steve@toth.demon.co.uk>\r
- *\r
- * XBOX DVD dongle infrared device driver for the input driver suite.\r
- *\r
- * This work was derived from the usbkbd.c kernel module.\r
- *\r
- * History:\r
- *\r
- * 2002_08_31 - 0.1 - Initial release\r
- * 2002_09_02 - 0.2 - Added IOCTL support enabling user space administration\r
- * of the translation matrix.\r
- *\r
- */\r
-\r
-#include "../usb_wrapper.h"\r
-\r
-\r
-u16 current_remote_key;\r
-u8 remotekeyIsRepeat;\r
-\r
-struct xremote_info \r
-{\r
- struct urb *urb;\r
- unsigned char irpkt[8];\r
-};\r
-\r
-/* USB callback completion handler\r
- * Code in transfer_buffer is received as six unsigned chars\r
- * Example PLAY=00 06 ea 0a 40 00\r
- * The command is located in byte[2], the rest are ignored.\r
- * Key position is byte[4] bit0 (7-0 format) 0=down, 1=up\r
- * All other bits are unknown / now required.\r
- */\r
-\r
-static void xremote_irq(struct urb *urb, struct pt_regs *regs)\r
-{\r
- struct xremote_info *xri = urb->context;\r
- \r
- if (urb->status) return;\r
- if (urb->actual_length < 6) return;\r
-\r
- /* Messy/unnecessary, fix this */\r
- memcpy(xri->irpkt, urb->transfer_buffer, 6);\r
-\r
- /* Set the key action based in the sent action */\r
- current_remote_key = ((xri->irpkt[2] & 0xff)<<8) | (xri->irpkt[3] & 0xff);\r
-\r
- if (((xri->irpkt[4] & 0xff) + ((xri->irpkt[5] & 0xff ) << 8))>0x41) {\r
- remotekeyIsRepeat=0;\r
- }\r
- else remotekeyIsRepeat=1;\r
- \r
- usb_submit_urb(urb,GFP_ATOMIC);\r
-}\r
-\r
-static int xremote_probe(struct usb_interface *intf, const struct usb_device_id *id)\r
-{\r
- struct urb *urb;\r
- struct usb_device *udev = interface_to_usbdev (intf);\r
- struct usb_endpoint_descriptor *ep_irq_in;\r
- struct usb_endpoint_descriptor *ep_irq_out;\r
- struct xremote_info *xri;\r
-\r
- xri=(struct xremote_info *)kmalloc(sizeof(struct xremote_info),0);\r
- if (!xri) return -1;\r
-\r
- urb=usb_alloc_urb(0,0);\r
- if (!urb) return -1;\r
-\r
- xri->urb=urb;\r
-\r
- ep_irq_in = &intf->altsetting[0].endpoint[0].desc;\r
- usb_fill_int_urb(urb, udev,\r
- usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),\r
- xri->irpkt, 8, xremote_irq,\r
- xri, 8);\r
-\r
- usb_submit_urb(urb,GFP_ATOMIC);\r
- usb_set_intfdata(intf,xri);\r
-\r
- usbprintk("DVD Remote connected\n");\r
- return 0;\r
-}\r
-\r
-static void xremote_disconnect(struct usb_interface *intf)\r
-{\r
- struct xremote_info *xri = usb_get_intfdata (intf);\r
- usbprintk("DVD Remote disconnected\n ");\r
- usb_unlink_urb(xri->urb);\r
- usb_free_urb(xri->urb);\r
- kfree(xri);\r
-}\r
-\r
-static struct usb_device_id xremote_id_table [] = {\r
- { USB_DEVICE(0x040b, 0x6521) }, /* Gamester Xbox DVD Movie Playback Kit IR */\r
- { USB_DEVICE(0x045e, 0x0284) }, /* Microsoft Xbox DVD Movie Playback Kit IR */\r
- { USB_DEVICE(0x0000, 0x0000) }, // nothing detected - FAIL\r
- { } /* Terminating entry */\r
-};\r
-\r
-static struct usb_driver xremote_driver = {\r
- .owner = THIS_MODULE,\r
- .name = "XRemote",\r
- .probe = xremote_probe,\r
- .disconnect = xremote_disconnect,\r
- .id_table = xremote_id_table,\r
-};\r
-\r
-void XRemoteInit(void)\r
-{\r
-\r
- current_remote_key=0;\r
- usbprintk("XRemote probe %p ",xremote_probe);\r
- if (usb_register(&xremote_driver) < 0) {\r
- err("Unable to register XRemote driver");\r
- return;\r
- } \r
-}\r
-\r
-void XRemoteRemove(void) {\r
- usb_deregister(&xremote_driver);\r
-}\r
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * $Id$
+ *
+ * Copyright (c) 2002 Steven Toth <steve@toth.demon.co.uk>
+ *
+ * XBOX DVD dongle infrared device driver for the input driver suite.
+ *
+ * This work was derived from the usbkbd.c kernel module.
+ *
+ * History:
+ *
+ * 2002_08_31 - 0.1 - Initial release
+ * 2002_09_02 - 0.2 - Added IOCTL support enabling user space administration
+ * of the translation matrix.
+ *
+ */
+
+#include "../usb_wrapper.h"
+
+
+u16 current_remote_key;
+u8 remotekeyIsRepeat;
+
+struct xremote_info
+{
+ struct urb *urb;
+ unsigned char irpkt[8];
+};
+
+/* USB callback completion handler
+ * Code in transfer_buffer is received as six unsigned chars
+ * Example PLAY=00 06 ea 0a 40 00
+ * The command is located in byte[2], the rest are ignored.
+ * Key position is byte[4] bit0 (7-0 format) 0=down, 1=up
+ * All other bits are unknown / now required.
+ */
+
+static void xremote_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct xremote_info *xri = urb->context;
+
+ if (urb->status) return;
+ if (urb->actual_length < 6) return;
+
+ /* Messy/unnecessary, fix this */
+ memcpy(xri->irpkt, urb->transfer_buffer, 6);
+
+ /* Set the key action based in the sent action */
+ current_remote_key = ((xri->irpkt[2] & 0xff)<<8) | (xri->irpkt[3] & 0xff);
+
+ if (((xri->irpkt[4] & 0xff) + ((xri->irpkt[5] & 0xff ) << 8))>0x41) {
+ remotekeyIsRepeat=0;
+ }
+ else remotekeyIsRepeat=1;
+
+ usb_submit_urb(urb,GFP_ATOMIC);
+}
+
+static int xremote_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct urb *urb;
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct usb_endpoint_descriptor *ep_irq_in;
+ struct usb_endpoint_descriptor *ep_irq_out;
+ struct xremote_info *xri;
+
+ xri=(struct xremote_info *)kmalloc(sizeof(struct xremote_info),0);
+ if (!xri) return -1;
+
+ urb=usb_alloc_urb(0,0);
+ if (!urb) return -1;
+
+ xri->urb=urb;
+
+ ep_irq_in = &intf->altsetting[0].endpoint[0].desc;
+ usb_fill_int_urb(urb, udev,
+ usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
+ xri->irpkt, 8, xremote_irq,
+ xri, 8);
+
+ usb_submit_urb(urb,GFP_ATOMIC);
+ usb_set_intfdata(intf,xri);
+
+ usbprintk("DVD Remote connected\n");
+ return 0;
+}
+
+static void xremote_disconnect(struct usb_interface *intf)
+{
+ struct xremote_info *xri = usb_get_intfdata (intf);
+ usbprintk("DVD Remote disconnected\n ");
+ usb_unlink_urb(xri->urb);
+ usb_free_urb(xri->urb);
+ kfree(xri);
+}
+
+static struct usb_device_id xremote_id_table [] = {
+ { USB_DEVICE(0x040b, 0x6521) }, /* Gamester Xbox DVD Movie Playback Kit IR */
+ { USB_DEVICE(0x045e, 0x0284) }, /* Microsoft Xbox DVD Movie Playback Kit IR */
+ { USB_DEVICE(0x0000, 0x0000) }, // nothing detected - FAIL
+ { } /* Terminating entry */
+};
+
+static struct usb_driver xremote_driver = {
+ .owner = THIS_MODULE,
+ .name = "XRemote",
+ .probe = xremote_probe,
+ .disconnect = xremote_disconnect,
+ .id_table = xremote_id_table,
+};
+
+void XRemoteInit(void)
+{
+
+ current_remote_key=0;
+ usbprintk("XRemote probe %p ",xremote_probe);
+ if (usb_register(&xremote_driver) < 0) {
+ err("Unable to register XRemote driver");
+ return;
+ }
+}
+
+void XRemoteRemove(void) {
+ usb_deregister(&xremote_driver);
+}
-PATH_TO_TOP = ../../../..\r
-\r
-TARGET_TYPE = export_driver\r
-\r
-TARGET_NAME = uhci\r
-\r
-TARGET_DDKLIBS = ntoskrnl.a usbcore.a\r
-\r
-TARGET_CFLAGS = -Wall -I$(PATH_TO_TOP)/ntoskrnl/include -DDEBUG_MODE\r
-\r
-TARGET_OBJECTS = \\r
- uhci-hcd.o uhci_main.o ../sys/ros_wrapper.o ../sys/linuxwrapper.o\r
-\r
-include $(PATH_TO_TOP)/rules.mak\r
-\r
-include $(TOOLS_PATH)/helper.mk\r
+PATH_TO_TOP = ../../../..
+
+TARGET_TYPE = export_driver
+
+TARGET_NAME = uhci
+
+TARGET_DDKLIBS = ntoskrnl.a usbcore.a
+
+TARGET_CFLAGS = -Wall -I$(PATH_TO_TOP)/ntoskrnl/include -DDEBUG_MODE
+
+TARGET_OBJECTS = \
+ uhci-hcd.o uhci_main.o ../sys/ros_wrapper.o ../sys/linuxwrapper.o
+
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
-LIBRARY uhci.sys\r
-EXPORTS\r
+LIBRARY uhci.sys
+EXPORTS
-#define REACTOS_VERSION_DLL\r
-#define REACTOS_STR_FILE_DESCRIPTION "USB UHCI Device Driver\0"\r
-#define REACTOS_STR_INTERNAL_NAME "uhci\0"\r
-#define REACTOS_STR_ORIGINAL_FILENAME "uhci.sys\0"\r
-#include <reactos/version.rc>\r
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION "USB UHCI Device Driver\0"
+#define REACTOS_STR_INTERNAL_NAME "uhci\0"
+#define REACTOS_STR_ORIGINAL_FILENAME "uhci.sys\0"
+#include <reactos/version.rc>
-/*\r
- * Configs for UHCI\r
- */\r
-\r
-#define CONFIG_PCI\r
+/*
+ * Configs for UHCI
+ */
+
+#define CONFIG_PCI
-/*\r
- ReactOS specific functions for UHCI module\r
- by Aleksey Bragin (aleksey@reactos.com)\r
- Some parts of code are inspired (or even just copied) from ReactOS Videoport driver\r
-*/\r
-\r
-#include <ddk/ntddk.h>\r
-#include <debug.h>\r
-#include "../linux/linux_wrapper.h"\r
-#include "../host/ohci_main.h"\r
-\r
-// declare basic init funcs\r
-void init_wrapper(struct pci_dev *probe_dev);\r
-int uhci_hcd_init(void);\r
-void uhci_hcd_cleanup(void);\r
-int STDCALL usb_init(void);\r
-void STDCALL usb_exit(void);\r
-extern struct pci_driver uhci_pci_driver;\r
-extern const struct pci_device_id uhci_pci_ids[];\r
-\r
-\r
-\r
-// This should be removed, but for testing purposes it's here\r
-struct pci_dev *dev;\r
-//struct pci_device_id *dev_id;\r
-\r
-\r
-#define USB_UHCI_TAG TAG('u','s','b','u')\r
-\r
-NTSTATUS STDCALL AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)\r
-{\r
- PDEVICE_OBJECT fdo;\r
- NTSTATUS Status;\r
- WCHAR DeviceBuffer[20];\r
- UNICODE_STRING DeviceName;\r
- POHCI_DRIVER_EXTENSION DriverExtension;\r
- POHCI_DEVICE_EXTENSION DeviceExtension;\r
- ULONG Size, DeviceNumber;\r
-\r
- DPRINT1("uhci: AddDevice called\n");\r
-\r
- // Allocate driver extension now\r
- DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);\r
- if (DriverExtension == NULL)\r
- {\r
- Status = IoAllocateDriverObjectExtension(\r
- DriverObject,\r
- DriverObject,\r
- sizeof(OHCI_DRIVER_EXTENSION),\r
- (PVOID *)&DriverExtension);\r
-\r
- if (!NT_SUCCESS(Status))\r
- {\r
- DPRINT1("Allocating DriverObjectExtension failed.\n");\r
- return Status;\r
- }\r
- }\r
- \r
- // Create a unicode device name\r
- DeviceNumber = 0; //TODO: Allocate new device number every time\r
- swprintf(DeviceBuffer, L"\\Device\\USBFDO-%lu", DeviceNumber);\r
- RtlInitUnicodeString(&DeviceName, DeviceBuffer);\r
-\r
- Status = IoCreateDevice(DriverObject,\r
- sizeof(OHCI_DEVICE_EXTENSION)/* + DriverExtension->InitializationData.HwDeviceExtensionSize*/,\r
- &DeviceName,\r
- FILE_DEVICE_CONTROLLER,\r
- 0,\r
- FALSE,\r
- &fdo);\r
-\r
- if (!NT_SUCCESS(Status))\r
- {\r
- DPRINT("IoCreateDevice call failed with status 0x%08x\n", Status);\r
- return Status;\r
- }\r
-\r
- // zerofill device extension\r
- DeviceExtension = (POHCI_DEVICE_EXTENSION)pdo->DeviceExtension;\r
- RtlZeroMemory(DeviceExtension, sizeof(OHCI_DEVICE_EXTENSION));\r
- DeviceExtension->NextDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);\r
-\r
- fdo->Flags &= ~DO_DEVICE_INITIALIZING;\r
-\r
- // Initialize device extension\r
- DeviceExtension->DeviceNumber = DeviceNumber;\r
- DeviceExtension->PhysicalDeviceObject = pdo;\r
- DeviceExtension->FunctionalDeviceObject = fdo;\r
- DeviceExtension->DriverExtension = DriverExtension;\r
-\r
- /* Get bus number from the upper level bus driver. */\r
- Size = sizeof(ULONG);\r
- Status = IoGetDeviceProperty(\r
- pdo,\r
- DevicePropertyBusNumber,\r
- Size,\r
- &DeviceExtension->SystemIoBusNumber,\r
- &Size);\r
- \r
- if (!NT_SUCCESS(Status))\r
- {\r
- DPRINT("Couldn't get an information from bus driver. Panic!!!\n");\r
- return Status;\r
- }\r
-\r
- DPRINT("Done AddDevice\n");\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-VOID STDCALL DriverUnload(PDRIVER_OBJECT DriverObject)\r
-{\r
- DPRINT1("DriverUnload()\n");\r
-\r
- // Exit usb device\r
- usb_exit();\r
-\r
- // Remove device (ohci_pci_driver.remove)\r
- uhci_pci_driver.remove(dev);\r
-\r
- ExFreePool(dev->slot_name);\r
- ExFreePool(dev);\r
-\r
- // Perform some cleanup\r
- uhci_hcd_cleanup();\r
-}\r
-\r
-NTSTATUS InitLinuxWrapper(PDEVICE_OBJECT DeviceObject)\r
-{\r
- NTSTATUS Status;\r
- POHCI_DEVICE_EXTENSION DeviceExtension = (POHCI_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
- // Fill generic linux structs\r
- dev = ExAllocatePoolWithTag(PagedPool, sizeof(struct pci_dev), USB_UHCI_TAG);\r
- \r
- init_wrapper(dev);\r
- dev->irq = DeviceExtension->InterruptVector;\r
- dev->dev_ext = (PVOID)DeviceExtension;\r
- dev->slot_name = ExAllocatePoolWithTag(NonPagedPool, 128, USB_UHCI_TAG); // 128 max len for slot name\r
-\r
- strcpy(dev->dev.name, "UnivHCI PCI-USB Controller");\r
- strcpy(dev->slot_name, "UHCD PCI Slot");\r
-\r
- // Init the OHCI HCD. Probe will be called automatically, but will fail because id=NULL\r
- Status = uhci_hcd_init();\r
- //FIXME: Check status returned value\r
-\r
- // Init core usb\r
- usb_init();\r
-\r
- // Probe device with real id now\r
- uhci_pci_driver.probe(dev, uhci_pci_ids);\r
-\r
- DPRINT("InitLinuxWrapper() done\n");\r
-\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-NTSTATUS STDCALL\r
-OHCD_PnPStartDevice(IN PDEVICE_OBJECT DeviceObject,\r
- IN PIRP Irp)\r
-{\r
- PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);\r
- PDRIVER_OBJECT DriverObject;\r
- POHCI_DRIVER_EXTENSION DriverExtension;\r
- POHCI_DEVICE_EXTENSION DeviceExtension;\r
- PCM_RESOURCE_LIST AllocatedResources;\r
-\r
- /*\r
- * Get the initialization data we saved in VideoPortInitialize.\r
- */\r
- DriverObject = DeviceObject->DriverObject;\r
- DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);\r
- DeviceExtension = (POHCI_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
- /*\r
- * Store some resources in the DeviceExtension.\r
- */\r
- AllocatedResources = Stack->Parameters.StartDevice.AllocatedResources;\r
- if (AllocatedResources != NULL)\r
- {\r
- CM_FULL_RESOURCE_DESCRIPTOR *FullList;\r
- CM_PARTIAL_RESOURCE_DESCRIPTOR *Descriptor;\r
- ULONG ResourceCount;\r
- ULONG ResourceListSize;\r
-\r
- /* Save the resource list */\r
- ResourceCount = AllocatedResources->List[0].PartialResourceList.Count;\r
- ResourceListSize =\r
- FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.\r
- PartialDescriptors[ResourceCount]);\r
- DeviceExtension->AllocatedResources = ExAllocatePool(PagedPool, ResourceListSize);\r
- if (DeviceExtension->AllocatedResources == NULL)\r
- {\r
- return STATUS_INSUFFICIENT_RESOURCES;\r
- }\r
-\r
- RtlCopyMemory(DeviceExtension->AllocatedResources,\r
- AllocatedResources,\r
- ResourceListSize);\r
-\r
- /* Get the interrupt level/vector - needed by HwFindAdapter sometimes */\r
- for (FullList = AllocatedResources->List;\r
- FullList < AllocatedResources->List + AllocatedResources->Count;\r
- FullList++)\r
- {\r
- /* FIXME: Is this ASSERT ok for resources from the PNP manager? */\r
- /*ASSERT(FullList->InterfaceType == PCIBus &&\r
- FullList->BusNumber == DeviceExtension->SystemIoBusNumber &&\r
- 1 == FullList->PartialResourceList.Version &&\r
- 1 == FullList->PartialResourceList.Revision);*/\r
- DPRINT1("AllocRess->Count: %d, PartResList.Count: %d\n",\r
- AllocatedResources->Count, FullList->PartialResourceList.Count);\r
-\r
- for (Descriptor = FullList->PartialResourceList.PartialDescriptors;\r
- Descriptor < FullList->PartialResourceList.PartialDescriptors + FullList->PartialResourceList.Count;\r
- Descriptor++)\r
- {\r
- if (Descriptor->Type == CmResourceTypeInterrupt)\r
- {\r
- DeviceExtension->InterruptLevel = Descriptor->u.Interrupt.Level;\r
- DeviceExtension->InterruptVector = Descriptor->u.Interrupt.Vector;\r
-\r
- DPRINT1("Interrupt level: 0x%x Interrupt Vector: 0x%x\n",\r
- DeviceExtension->InterruptLevel,\r
- DeviceExtension->InterruptVector);\r
- }\r
- else if (Descriptor->Type == CmResourceTypePort)\r
- {\r
- DeviceExtension->BaseAddress = Descriptor->u.Port.Start;\r
- DeviceExtension->BaseAddrLength = Descriptor->u.Port.Length;\r
- DeviceExtension->Flags = Descriptor->Flags;\r
- \r
- DPRINT1("I/O resource: start=0x%x, length=0x%x\n",\r
- DeviceExtension->BaseAddress.u.LowPart, DeviceExtension->BaseAddrLength);\r
- }\r
- else\r
- DPRINT1("Get resource type: %d, Generic start=0x%x Generic length=0x%x\n",\r
- Descriptor->Type, Descriptor->u.Generic.Start.u.LowPart, Descriptor->u.Generic.Length);\r
- \r
- }\r
- }\r
- }\r
-\r
- /*\r
- * Init wrapper with this object\r
- */\r
- return InitLinuxWrapper(DeviceObject);\r
-}\r
-\r
-// Dispatch PNP\r
-NTSTATUS STDCALL DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp)\r
-{\r
- PIO_STACK_LOCATION IrpSp;\r
- NTSTATUS Status;\r
-\r
- IrpSp = IoGetCurrentIrpStackLocation(Irp);\r
-\r
- switch (IrpSp->MinorFunction)\r
- {\r
- case IRP_MN_START_DEVICE:\r
- //Status = IntVideoPortForwardIrpAndWait(DeviceObject, Irp);\r
- //if (NT_SUCCESS(Status) && NT_SUCCESS(Irp->IoStatus.Status))\r
- \r
- Status = OHCD_PnPStartDevice(DeviceObject, Irp);\r
- Irp->IoStatus.Status = Status;\r
- Irp->IoStatus.Information = 0;\r
- IoCompleteRequest(Irp, IO_NO_INCREMENT);\r
- break;\r
-\r
-\r
- case IRP_MN_REMOVE_DEVICE:\r
- case IRP_MN_QUERY_REMOVE_DEVICE:\r
- case IRP_MN_CANCEL_REMOVE_DEVICE:\r
- case IRP_MN_SURPRISE_REMOVAL:\r
-\r
- case IRP_MN_STOP_DEVICE:\r
- //Status = IntVideoPortForwardIrpAndWait(DeviceObject, Irp);\r
- //if (NT_SUCCESS(Status) && NT_SUCCESS(Irp->IoStatus.Status))\r
- Status = STATUS_SUCCESS;\r
- Irp->IoStatus.Status = Status;\r
- Irp->IoStatus.Information = 0;\r
- IoCompleteRequest(Irp, IO_NO_INCREMENT);\r
-\r
- IoDeleteDevice(DeviceObject); // just delete device for now\r
- break;\r
-\r
- case IRP_MN_QUERY_STOP_DEVICE:\r
- case IRP_MN_CANCEL_STOP_DEVICE:\r
- Status = STATUS_SUCCESS;\r
- Irp->IoStatus.Status = STATUS_SUCCESS;\r
- Irp->IoStatus.Information = 0;\r
- IoCompleteRequest(Irp, IO_NO_INCREMENT);\r
- break;\r
- \r
- default:\r
- return STATUS_NOT_IMPLEMENTED;\r
- break;\r
- }\r
- \r
- return Status;\r
-}\r
-\r
-NTSTATUS STDCALL DispatchPower(PDEVICE_OBJECT fido, PIRP Irp)\r
-{\r
- DbgPrint("IRP_MJ_POWER dispatch\n");\r
- return STATUS_SUCCESS;\r
-}\r
-\r
-/*\r
- * Standard DriverEntry method.\r
- */\r
-NTSTATUS STDCALL\r
-DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegPath)\r
-{\r
- DriverObject->DriverUnload = DriverUnload;\r
- DriverObject->DriverExtension->AddDevice = AddDevice;\r
- DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;\r
- DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;\r
-\r
- return STATUS_SUCCESS;\r
-}\r
+/*
+ ReactOS specific functions for UHCI module
+ by Aleksey Bragin (aleksey@reactos.com)
+ Some parts of code are inspired (or even just copied) from ReactOS Videoport driver
+*/
+
+#include <ddk/ntddk.h>
+#include <debug.h>
+#include "../linux/linux_wrapper.h"
+#include "../host/ohci_main.h"
+
+// declare basic init funcs
+void init_wrapper(struct pci_dev *probe_dev);
+int uhci_hcd_init(void);
+void uhci_hcd_cleanup(void);
+int STDCALL usb_init(void);
+void STDCALL usb_exit(void);
+extern struct pci_driver uhci_pci_driver;
+extern const struct pci_device_id uhci_pci_ids[];
+
+
+
+// This should be removed, but for testing purposes it's here
+struct pci_dev *dev;
+//struct pci_device_id *dev_id;
+
+
+#define USB_UHCI_TAG TAG('u','s','b','u')
+
+NTSTATUS STDCALL AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)
+{
+ PDEVICE_OBJECT fdo;
+ NTSTATUS Status;
+ WCHAR DeviceBuffer[20];
+ UNICODE_STRING DeviceName;
+ POHCI_DRIVER_EXTENSION DriverExtension;
+ POHCI_DEVICE_EXTENSION DeviceExtension;
+ ULONG Size, DeviceNumber;
+
+ DPRINT1("uhci: AddDevice called\n");
+
+ // Allocate driver extension now
+ DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
+ if (DriverExtension == NULL)
+ {
+ Status = IoAllocateDriverObjectExtension(
+ DriverObject,
+ DriverObject,
+ sizeof(OHCI_DRIVER_EXTENSION),
+ (PVOID *)&DriverExtension);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Allocating DriverObjectExtension failed.\n");
+ return Status;
+ }
+ }
+
+ // Create a unicode device name
+ DeviceNumber = 0; //TODO: Allocate new device number every time
+ swprintf(DeviceBuffer, L"\\Device\\USBFDO-%lu", DeviceNumber);
+ RtlInitUnicodeString(&DeviceName, DeviceBuffer);
+
+ Status = IoCreateDevice(DriverObject,
+ sizeof(OHCI_DEVICE_EXTENSION)/* + DriverExtension->InitializationData.HwDeviceExtensionSize*/,
+ &DeviceName,
+ FILE_DEVICE_CONTROLLER,
+ 0,
+ FALSE,
+ &fdo);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("IoCreateDevice call failed with status 0x%08x\n", Status);
+ return Status;
+ }
+
+ // zerofill device extension
+ DeviceExtension = (POHCI_DEVICE_EXTENSION)pdo->DeviceExtension;
+ RtlZeroMemory(DeviceExtension, sizeof(OHCI_DEVICE_EXTENSION));
+ DeviceExtension->NextDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);
+
+ fdo->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ // Initialize device extension
+ DeviceExtension->DeviceNumber = DeviceNumber;
+ DeviceExtension->PhysicalDeviceObject = pdo;
+ DeviceExtension->FunctionalDeviceObject = fdo;
+ DeviceExtension->DriverExtension = DriverExtension;
+
+ /* Get bus number from the upper level bus driver. */
+ Size = sizeof(ULONG);
+ Status = IoGetDeviceProperty(
+ pdo,
+ DevicePropertyBusNumber,
+ Size,
+ &DeviceExtension->SystemIoBusNumber,
+ &Size);
+
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("Couldn't get an information from bus driver. Panic!!!\n");
+ return Status;
+ }
+
+ DPRINT("Done AddDevice\n");
+ return STATUS_SUCCESS;
+}
+
+VOID STDCALL DriverUnload(PDRIVER_OBJECT DriverObject)
+{
+ DPRINT1("DriverUnload()\n");
+
+ // Exit usb device
+ usb_exit();
+
+ // Remove device (ohci_pci_driver.remove)
+ uhci_pci_driver.remove(dev);
+
+ ExFreePool(dev->slot_name);
+ ExFreePool(dev);
+
+ // Perform some cleanup
+ uhci_hcd_cleanup();
+}
+
+NTSTATUS InitLinuxWrapper(PDEVICE_OBJECT DeviceObject)
+{
+ NTSTATUS Status;
+ POHCI_DEVICE_EXTENSION DeviceExtension = (POHCI_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ // Fill generic linux structs
+ dev = ExAllocatePoolWithTag(PagedPool, sizeof(struct pci_dev), USB_UHCI_TAG);
+
+ init_wrapper(dev);
+ dev->irq = DeviceExtension->InterruptVector;
+ dev->dev_ext = (PVOID)DeviceExtension;
+ dev->slot_name = ExAllocatePoolWithTag(NonPagedPool, 128, USB_UHCI_TAG); // 128 max len for slot name
+
+ strcpy(dev->dev.name, "UnivHCI PCI-USB Controller");
+ strcpy(dev->slot_name, "UHCD PCI Slot");
+
+ // Init the OHCI HCD. Probe will be called automatically, but will fail because id=NULL
+ Status = uhci_hcd_init();
+ //FIXME: Check status returned value
+
+ // Init core usb
+ usb_init();
+
+ // Probe device with real id now
+ uhci_pci_driver.probe(dev, uhci_pci_ids);
+
+ DPRINT("InitLinuxWrapper() done\n");
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS STDCALL
+OHCD_PnPStartDevice(IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp)
+{
+ PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+ PDRIVER_OBJECT DriverObject;
+ POHCI_DRIVER_EXTENSION DriverExtension;
+ POHCI_DEVICE_EXTENSION DeviceExtension;
+ PCM_RESOURCE_LIST AllocatedResources;
+
+ /*
+ * Get the initialization data we saved in VideoPortInitialize.
+ */
+ DriverObject = DeviceObject->DriverObject;
+ DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
+ DeviceExtension = (POHCI_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ /*
+ * Store some resources in the DeviceExtension.
+ */
+ AllocatedResources = Stack->Parameters.StartDevice.AllocatedResources;
+ if (AllocatedResources != NULL)
+ {
+ CM_FULL_RESOURCE_DESCRIPTOR *FullList;
+ CM_PARTIAL_RESOURCE_DESCRIPTOR *Descriptor;
+ ULONG ResourceCount;
+ ULONG ResourceListSize;
+
+ /* Save the resource list */
+ ResourceCount = AllocatedResources->List[0].PartialResourceList.Count;
+ ResourceListSize =
+ FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.
+ PartialDescriptors[ResourceCount]);
+ DeviceExtension->AllocatedResources = ExAllocatePool(PagedPool, ResourceListSize);
+ if (DeviceExtension->AllocatedResources == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory(DeviceExtension->AllocatedResources,
+ AllocatedResources,
+ ResourceListSize);
+
+ /* Get the interrupt level/vector - needed by HwFindAdapter sometimes */
+ for (FullList = AllocatedResources->List;
+ FullList < AllocatedResources->List + AllocatedResources->Count;
+ FullList++)
+ {
+ /* FIXME: Is this ASSERT ok for resources from the PNP manager? */
+ /*ASSERT(FullList->InterfaceType == PCIBus &&
+ FullList->BusNumber == DeviceExtension->SystemIoBusNumber &&
+ 1 == FullList->PartialResourceList.Version &&
+ 1 == FullList->PartialResourceList.Revision);*/
+ DPRINT1("AllocRess->Count: %d, PartResList.Count: %d\n",
+ AllocatedResources->Count, FullList->PartialResourceList.Count);
+
+ for (Descriptor = FullList->PartialResourceList.PartialDescriptors;
+ Descriptor < FullList->PartialResourceList.PartialDescriptors + FullList->PartialResourceList.Count;
+ Descriptor++)
+ {
+ if (Descriptor->Type == CmResourceTypeInterrupt)
+ {
+ DeviceExtension->InterruptLevel = Descriptor->u.Interrupt.Level;
+ DeviceExtension->InterruptVector = Descriptor->u.Interrupt.Vector;
+
+ DPRINT1("Interrupt level: 0x%x Interrupt Vector: 0x%x\n",
+ DeviceExtension->InterruptLevel,
+ DeviceExtension->InterruptVector);
+ }
+ else if (Descriptor->Type == CmResourceTypePort)
+ {
+ DeviceExtension->BaseAddress = Descriptor->u.Port.Start;
+ DeviceExtension->BaseAddrLength = Descriptor->u.Port.Length;
+ DeviceExtension->Flags = Descriptor->Flags;
+
+ DPRINT1("I/O resource: start=0x%x, length=0x%x\n",
+ DeviceExtension->BaseAddress.u.LowPart, DeviceExtension->BaseAddrLength);
+ }
+ else
+ DPRINT1("Get resource type: %d, Generic start=0x%x Generic length=0x%x\n",
+ Descriptor->Type, Descriptor->u.Generic.Start.u.LowPart, Descriptor->u.Generic.Length);
+
+ }
+ }
+ }
+
+ /*
+ * Init wrapper with this object
+ */
+ return InitLinuxWrapper(DeviceObject);
+}
+
+// Dispatch PNP
+NTSTATUS STDCALL DispatchPnp(PDEVICE_OBJECT DeviceObject, PIRP Irp)
+{
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+
+ IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ switch (IrpSp->MinorFunction)
+ {
+ case IRP_MN_START_DEVICE:
+ //Status = IntVideoPortForwardIrpAndWait(DeviceObject, Irp);
+ //if (NT_SUCCESS(Status) && NT_SUCCESS(Irp->IoStatus.Status))
+
+ Status = OHCD_PnPStartDevice(DeviceObject, Irp);
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ break;
+
+
+ case IRP_MN_REMOVE_DEVICE:
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ case IRP_MN_SURPRISE_REMOVAL:
+
+ case IRP_MN_STOP_DEVICE:
+ //Status = IntVideoPortForwardIrpAndWait(DeviceObject, Irp);
+ //if (NT_SUCCESS(Status) && NT_SUCCESS(Irp->IoStatus.Status))
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ IoDeleteDevice(DeviceObject); // just delete device for now
+ break;
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ break;
+
+ default:
+ return STATUS_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return Status;
+}
+
+NTSTATUS STDCALL DispatchPower(PDEVICE_OBJECT fido, PIRP Irp)
+{
+ DbgPrint("IRP_MJ_POWER dispatch\n");
+ return STATUS_SUCCESS;
+}
+
+/*
+ * Standard DriverEntry method.
+ */
+NTSTATUS STDCALL
+DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegPath)
+{
+ DriverObject->DriverUnload = DriverUnload;
+ DriverObject->DriverExtension->AddDevice = AddDevice;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
+ DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
+
+ return STATUS_SUCCESS;
+}
-//#include <stdlib.h>\r
-//#include <ntos/types.h>\r
-//#include <ddk/extypes.h>\r
-#include <ddk/ntddk.h>\r
-#include <debug.h>\r
-\r
-void wait_ms(int mils);\r
-\r
-#include "linux/linux_wrapper.h"\r
-#define __KERNEL__\r
-#undef CONFIG_PCI\r
-#define CONFIG_PCI\r
-\r
-#include "linux/usb.h"\r
+//#include <stdlib.h>
+//#include <ntos/types.h>
+//#include <ddk/extypes.h>
+#include <ddk/ntddk.h>
+#include <debug.h>
+
+void wait_ms(int mils);
+
+#include "linux/linux_wrapper.h"
+#define __KERNEL__
+#undef CONFIG_PCI
+#define CONFIG_PCI
+
+#include "linux/usb.h"
#include "linux/pci_ids.h"
\ No newline at end of file