diff --git a/reactos/drivers/usb/cromwell/Makefile b/reactos/drivers/usb/cromwell/Makefile index 1f124735144..f2d4bf03902 100644 --- a/reactos/drivers/usb/cromwell/Makefile +++ b/reactos/drivers/usb/cromwell/Makefile @@ -1,50 +1,50 @@ -# -# 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 +# +# 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 diff --git a/reactos/drivers/usb/cromwell/core/buffer_simple.c b/reactos/drivers/usb/cromwell/core/buffer_simple.c index 7ac70ce30cb..b0c0470235d 100644 --- a/reactos/drivers/usb/cromwell/core/buffer_simple.c +++ b/reactos/drivers/usb/cromwell/core/buffer_simple.c @@ -1,42 +1,42 @@ -/* - * 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); -} - +/* + * 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); +} + diff --git a/reactos/drivers/usb/cromwell/core/config.c b/reactos/drivers/usb/cromwell/core/config.c index 7d400e982d8..1c40bbd1c95 100644 --- a/reactos/drivers/usb/cromwell/core/config.c +++ b/reactos/drivers/usb/cromwell/core/config.c @@ -1,508 +1,508 @@ -#if 0 -#include -#include -#include -#include -#include -#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; -} - +#if 0 +#include +#include +#include +#include +#include +#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; +} + diff --git a/reactos/drivers/usb/cromwell/core/hcd-pci.c b/reactos/drivers/usb/cromwell/core/hcd-pci.c index 148cf85617d..42afc274d90 100644 --- a/reactos/drivers/usb/cromwell/core/hcd-pci.c +++ b/reactos/drivers/usb/cromwell/core/hcd-pci.c @@ -1,369 +1,369 @@ -/* - * (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 - -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif - -#include -#include -#include -#include -#include -#include -#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 */ - - +/* + * (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 + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#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 */ + + diff --git a/reactos/drivers/usb/cromwell/core/hcd.c b/reactos/drivers/usb/cromwell/core/hcd.c index a9efdf7a77f..e594362f341 100644 --- a/reactos/drivers/usb/cromwell/core/hcd.c +++ b/reactos/drivers/usb/cromwell/core/hcd.c @@ -1,1499 +1,1499 @@ -/* - * (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 - -#ifdef CONFIG_USB_DEBUG -#define DEBUG -#endif - -#include -#include -#include -#include -#include -#include /* for UTS_SYSNAME */ -#include /* for hcd->pdev and dma addressing */ -#include -#include - -#include -#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 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); -} - +/* + * (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 + +#ifdef CONFIG_USB_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include /* for UTS_SYSNAME */ +#include /* for hcd->pdev and dma addressing */ +#include +#include + +#include +#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 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); +} + diff --git a/reactos/drivers/usb/cromwell/core/hcd.h b/reactos/drivers/usb/cromwell/core/hcd.h index 266324d5d15..a5b3859db2d 100644 --- a/reactos/drivers/usb/cromwell/core/hcd.h +++ b/reactos/drivers/usb/cromwell/core/hcd.h @@ -1,481 +1,481 @@ -/* - * 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__ */ - +/* + * 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__ */ + diff --git a/reactos/drivers/usb/cromwell/core/hub.c b/reactos/drivers/usb/cromwell/core/hub.c index 1b7e8207f1f..8efb82df297 100644 --- a/reactos/drivers/usb/cromwell/core/hub.c +++ b/reactos/drivers/usb/cromwell/core/hub.c @@ -1,1392 +1,1392 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif -#include -#include -#include - -#include -#include -#include - -#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; -} - - +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include +#include +#include + +#include +#include +#include + +#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; +} + + diff --git a/reactos/drivers/usb/cromwell/core/hub.h b/reactos/drivers/usb/cromwell/core/hub.h index f51a9992cc6..1e250782061 100644 --- a/reactos/drivers/usb/cromwell/core/hub.h +++ b/reactos/drivers/usb/cromwell/core/hub.h @@ -1,195 +1,195 @@ -#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 -#include -#include /* 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 */ +#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 +#include +#include /* 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 */ diff --git a/reactos/drivers/usb/cromwell/core/makefile b/reactos/drivers/usb/cromwell/core/makefile index 5301aa379f9..10ffd283f25 100644 --- a/reactos/drivers/usb/cromwell/core/makefile +++ b/reactos/drivers/usb/cromwell/core/makefile @@ -1,22 +1,22 @@ -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 +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 diff --git a/reactos/drivers/usb/cromwell/core/makefile.crom b/reactos/drivers/usb/cromwell/core/makefile.crom index 489af5802be..dd6262ccaaa 100644 --- a/reactos/drivers/usb/cromwell/core/makefile.crom +++ b/reactos/drivers/usb/cromwell/core/makefile.crom @@ -1,6 +1,6 @@ - -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 + +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 diff --git a/reactos/drivers/usb/cromwell/core/message.c b/reactos/drivers/usb/cromwell/core/message.c index 749c76b8029..38857711bbc 100644 --- a/reactos/drivers/usb/cromwell/core/message.c +++ b/reactos/drivers/usb/cromwell/core/message.c @@ -1,1056 +1,1056 @@ -/* - * message.c - synchronous message handling - */ -#if 0 -#include /* for scatterlist macros */ -#include -#include -#include -#include -#include -#include -#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; iactconfig->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; edesc.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; idescriptor.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); - +/* + * message.c - synchronous message handling + */ +#if 0 +#include /* for scatterlist macros */ +#include +#include +#include +#include +#include +#include +#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; iactconfig->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; edesc.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; idescriptor.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); + diff --git a/reactos/drivers/usb/cromwell/core/urb.c b/reactos/drivers/usb/cromwell/core/urb.c index 08b691fd1ff..4ba8cb4cc0b 100644 --- a/reactos/drivers/usb/cromwell/core/urb.c +++ b/reactos/drivers/usb/cromwell/core/urb.c @@ -1,398 +1,398 @@ -#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; -} +#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; +} diff --git a/reactos/drivers/usb/cromwell/core/usb-debug.c b/reactos/drivers/usb/cromwell/core/usb-debug.c index a2a3404f4c6..a6a6160e05a 100644 --- a/reactos/drivers/usb/cromwell/core/usb-debug.c +++ b/reactos/drivers/usb/cromwell/core/usb-debug.c @@ -1,206 +1,206 @@ -/* - * 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 -#include -#include -#include -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif -#include -#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); -} - +/* + * 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 +#include +#include +#include +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include +#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); +} + diff --git a/reactos/drivers/usb/cromwell/core/usb.c b/reactos/drivers/usb/cromwell/core/usb.c index a7726177e56..c4a2060301e 100644 --- a/reactos/drivers/usb/cromwell/core/usb.c +++ b/reactos/drivers/usb/cromwell/core/usb.c @@ -1,1597 +1,1597 @@ -/* - * 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 - -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif - -#include -#include -#include -#include -#include /* for in_interrupt() */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#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 - */ -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"); +/* + * 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 + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include +#include +#include +#include +#include /* for in_interrupt() */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#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 + */ +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"); diff --git a/reactos/drivers/usb/cromwell/core/usb.h b/reactos/drivers/usb/cromwell/core/usb.h index 52210ef7fa6..09f459f67ac 100644 --- a/reactos/drivers/usb/cromwell/core/usb.h +++ b/reactos/drivers/usb/cromwell/core/usb.h @@ -1,5 +1,5 @@ -/* 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); - +/* 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); + diff --git a/reactos/drivers/usb/cromwell/core/usbcore.c b/reactos/drivers/usb/cromwell/core/usbcore.c index 34df7e4b021..650e0f47994 100644 --- a/reactos/drivers/usb/cromwell/core/usbcore.c +++ b/reactos/drivers/usb/cromwell/core/usbcore.c @@ -1,63 +1,63 @@ -/* - ReactOS specific functions for usbcore module - by Aleksey Bragin (aleksey@reactos.com) -*/ - -#include -#include - -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; -} +/* + ReactOS specific functions for usbcore module + by Aleksey Bragin (aleksey@reactos.com) +*/ + +#include +#include + +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; +} diff --git a/reactos/drivers/usb/cromwell/core/usbcore.def b/reactos/drivers/usb/cromwell/core/usbcore.def index 25446cd4fff..cb675e2477d 100644 --- a/reactos/drivers/usb/cromwell/core/usbcore.def +++ b/reactos/drivers/usb/cromwell/core/usbcore.def @@ -1,32 +1,32 @@ -; -; 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 +; +; 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 diff --git a/reactos/drivers/usb/cromwell/core/usbcore.rc b/reactos/drivers/usb/cromwell/core/usbcore.rc index bccb64ac3d2..f12b4d6d391 100644 --- a/reactos/drivers/usb/cromwell/core/usbcore.rc +++ b/reactos/drivers/usb/cromwell/core/usbcore.rc @@ -1,5 +1,5 @@ -#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 +#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 diff --git a/reactos/drivers/usb/cromwell/host/makefile b/reactos/drivers/usb/cromwell/host/makefile index e2e02edacfc..f81185fe1e7 100644 --- a/reactos/drivers/usb/cromwell/host/makefile +++ b/reactos/drivers/usb/cromwell/host/makefile @@ -1,20 +1,20 @@ -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 +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 diff --git a/reactos/drivers/usb/cromwell/host/makefile.crom b/reactos/drivers/usb/cromwell/host/makefile.crom index c23390585ae..d8bfc9a4c4f 100644 --- a/reactos/drivers/usb/cromwell/host/makefile.crom +++ b/reactos/drivers/usb/cromwell/host/makefile.crom @@ -1,3 +1,3 @@ -O_TARGET := ohci-hcd.o - -include $(TOPDIR)/Rules.make +O_TARGET := ohci-hcd.o + +include $(TOPDIR)/Rules.make diff --git a/reactos/drivers/usb/cromwell/host/ohci-dbg.c b/reactos/drivers/usb/cromwell/host/ohci-dbg.c index 6c9661b6738..0997ddfa28e 100644 --- a/reactos/drivers/usb/cromwell/host/ohci-dbg.c +++ b/reactos/drivers/usb/cromwell/host/ohci-dbg.c @@ -1,666 +1,666 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * 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 - -/*-------------------------------------------------------------------------*/ - +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * 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 + +/*-------------------------------------------------------------------------*/ + diff --git a/reactos/drivers/usb/cromwell/host/ohci-hcd.c b/reactos/drivers/usb/cromwell/host/ohci-hcd.c index 3470fa9e825..68c006b88aa 100644 --- a/reactos/drivers/usb/cromwell/host/ohci-hcd.c +++ b/reactos/drivers/usb/cromwell/host/ohci-hcd.c @@ -1,703 +1,703 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * [ 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 - -#ifdef CONFIG_USB_DEBUG -# define DEBUG -#else -# undef DEBUG -#endif - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for in_interrupt () */ -#include -#include "../core/hcd.h" - -#include -#include -#include -#include -#include -#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 +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * [ 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 + +#ifdef CONFIG_USB_DEBUG +# define DEBUG +#else +# undef DEBUG +#endif + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt () */ +#include +#include "../core/hcd.h" + +#include +#include +#include +#include +#include +#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 diff --git a/reactos/drivers/usb/cromwell/host/ohci-hub.c b/reactos/drivers/usb/cromwell/host/ohci-hub.c index 03be82741e0..a7584a499af 100644 --- a/reactos/drivers/usb/cromwell/host/ohci-hub.c +++ b/reactos/drivers/usb/cromwell/host/ohci-hub.c @@ -1,271 +1,271 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * 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; -} - +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * 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; +} + diff --git a/reactos/drivers/usb/cromwell/host/ohci-mem.c b/reactos/drivers/usb/cromwell/host/ohci-mem.c index 85371e9756a..4fc839304ce 100644 --- a/reactos/drivers/usb/cromwell/host/ohci-mem.c +++ b/reactos/drivers/usb/cromwell/host/ohci-mem.c @@ -1,146 +1,146 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * 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); -} - +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * 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); +} + diff --git a/reactos/drivers/usb/cromwell/host/ohci-pci.c b/reactos/drivers/usb/cromwell/host/ohci-pci.c index 0ce7123c541..a7c3980f177 100644 --- a/reactos/drivers/usb/cromwell/host/ohci-pci.c +++ b/reactos/drivers/usb/cromwell/host/ohci-pci.c @@ -1,412 +1,412 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * [ 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 -#include -#include -#include -#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);*/ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * [ 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 +#include +#include +#include +#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);*/ diff --git a/reactos/drivers/usb/cromwell/host/ohci-q.c b/reactos/drivers/usb/cromwell/host/ohci-q.c index 71e6350b87b..eae584ae317 100644 --- a/reactos/drivers/usb/cromwell/host/ohci-q.c +++ b/reactos/drivers/usb/cromwell/host/ohci-q.c @@ -1,1014 +1,1014 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * 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 */ -#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); -} +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * 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 */ +#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); +} diff --git a/reactos/drivers/usb/cromwell/host/ohci.def b/reactos/drivers/usb/cromwell/host/ohci.def index 9bc5544dd2f..26010d9aa68 100644 --- a/reactos/drivers/usb/cromwell/host/ohci.def +++ b/reactos/drivers/usb/cromwell/host/ohci.def @@ -1,2 +1,2 @@ -LIBRARY ohci.sys -EXPORTS +LIBRARY ohci.sys +EXPORTS diff --git a/reactos/drivers/usb/cromwell/host/ohci.h b/reactos/drivers/usb/cromwell/host/ohci.h index 632382e88ba..8af7d8803ec 100644 --- a/reactos/drivers/usb/cromwell/host/ohci.h +++ b/reactos/drivers/usb/cromwell/host/ohci.h @@ -1,418 +1,418 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * (C) Copyright 2000-2002 David Brownell - * - * 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 ) 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 - +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * + * 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 ) 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 + diff --git a/reactos/drivers/usb/cromwell/host/ohci.rc b/reactos/drivers/usb/cromwell/host/ohci.rc index fa183242a97..a31d60c3796 100644 --- a/reactos/drivers/usb/cromwell/host/ohci.rc +++ b/reactos/drivers/usb/cromwell/host/ohci.rc @@ -1,5 +1,5 @@ -#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 +#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 diff --git a/reactos/drivers/usb/cromwell/host/ohci_config.h b/reactos/drivers/usb/cromwell/host/ohci_config.h index ec73b9099f3..01fd7cdcb08 100644 --- a/reactos/drivers/usb/cromwell/host/ohci_config.h +++ b/reactos/drivers/usb/cromwell/host/ohci_config.h @@ -1,5 +1,5 @@ -/* - * Configs for OHCI - */ - -#define CONFIG_PCI +/* + * Configs for OHCI + */ + +#define CONFIG_PCI diff --git a/reactos/drivers/usb/cromwell/host/ohci_main.c b/reactos/drivers/usb/cromwell/host/ohci_main.c index aba234a8082..57c61828e48 100644 --- a/reactos/drivers/usb/cromwell/host/ohci_main.c +++ b/reactos/drivers/usb/cromwell/host/ohci_main.c @@ -1,309 +1,309 @@ -/* - 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 -#include -#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; -} +/* + 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 +#include +#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; +} diff --git a/reactos/drivers/usb/cromwell/host/ohci_main.h b/reactos/drivers/usb/cromwell/host/ohci_main.h index 0ee0992873c..06e3b4ea875 100644 --- a/reactos/drivers/usb/cromwell/host/ohci_main.h +++ b/reactos/drivers/usb/cromwell/host/ohci_main.h @@ -1,47 +1,47 @@ -/* - * 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 +/* + * 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 diff --git a/reactos/drivers/usb/cromwell/linux/asm/bitops.h b/reactos/drivers/usb/cromwell/linux/asm/bitops.h index f16396ad4ac..bb194fa25f2 100644 --- a/reactos/drivers/usb/cromwell/linux/asm/bitops.h +++ b/reactos/drivers/usb/cromwell/linux/asm/bitops.h @@ -1,384 +1,384 @@ -#ifndef _I386_BITOPS_H -#define _I386_BITOPS_H - -/* - * Copyright 1992, Linus Torvalds. - */ - -//#include - -/* - * 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 _I386_BITOPS_H +#define _I386_BITOPS_H + +/* + * Copyright 1992, Linus Torvalds. + */ + +//#include + +/* + * 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 */ diff --git a/reactos/drivers/usb/cromwell/linux/bitops.h b/reactos/drivers/usb/cromwell/linux/bitops.h index 6509d07ba90..ac05ed65c53 100644 --- a/reactos/drivers/usb/cromwell/linux/bitops.h +++ b/reactos/drivers/usb/cromwell/linux/bitops.h @@ -1,72 +1,72 @@ -#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 _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 diff --git a/reactos/drivers/usb/cromwell/linux/boot.h b/reactos/drivers/usb/cromwell/linux/boot.h index d99b7f22427..db64976aa76 100644 --- a/reactos/drivers/usb/cromwell/linux/boot.h +++ b/reactos/drivers/usb/cromwell/linux/boot.h @@ -1,337 +1,337 @@ -#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 _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_ diff --git a/reactos/drivers/usb/cromwell/linux/consts.h b/reactos/drivers/usb/cromwell/linux/consts.h index 755bf6a5128..d3fb6cebae0 100644 --- a/reactos/drivers/usb/cromwell/linux/consts.h +++ b/reactos/drivers/usb/cromwell/linux/consts.h @@ -1,70 +1,70 @@ -#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 _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_ + + diff --git a/reactos/drivers/usb/cromwell/linux/cromwell_types.h b/reactos/drivers/usb/cromwell/linux/cromwell_types.h index d03e9158393..cccc05c681b 100644 --- a/reactos/drivers/usb/cromwell/linux/cromwell_types.h +++ b/reactos/drivers/usb/cromwell/linux/cromwell_types.h @@ -1,27 +1,27 @@ -#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 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 */ diff --git a/reactos/drivers/usb/cromwell/linux/errno.h b/reactos/drivers/usb/cromwell/linux/errno.h index dcd2ede98d7..1b667890877 100644 --- a/reactos/drivers/usb/cromwell/linux/errno.h +++ b/reactos/drivers/usb/cromwell/linux/errno.h @@ -1,132 +1,132 @@ -#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 +#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 diff --git a/reactos/drivers/usb/cromwell/linux/linux_wrapper.h b/reactos/drivers/usb/cromwell/linux/linux_wrapper.h index 0c759efb55c..dd79888d8e1 100644 --- a/reactos/drivers/usb/cromwell/linux/linux_wrapper.h +++ b/reactos/drivers/usb/cromwell/linux/linux_wrapper.h @@ -1,802 +1,802 @@ -/* - * 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 - */ - -/*------------------------------------------------------------------------*/ -/* 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 -#include -#include -#include "consts.h" -#include -#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;nexpires=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 - - +/* + * 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 + */ + +/*------------------------------------------------------------------------*/ +/* 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 +#include +#include +#include "consts.h" +#include +#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;nexpires=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 + + diff --git a/reactos/drivers/usb/cromwell/linux/list.h b/reactos/drivers/usb/cromwell/linux/list.h index fbbfabed39a..ef8fdd29f55 100644 --- a/reactos/drivers/usb/cromwell/linux/list.h +++ b/reactos/drivers/usb/cromwell/linux/list.h @@ -1,224 +1,224 @@ -#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 +#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 diff --git a/reactos/drivers/usb/cromwell/linux/pci_hal.c b/reactos/drivers/usb/cromwell/linux/pci_hal.c index f3d538f3659..625fc90053b 100644 --- a/reactos/drivers/usb/cromwell/linux/pci_hal.c +++ b/reactos/drivers/usb/cromwell/linux/pci_hal.c @@ -1,162 +1,162 @@ -// 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; -} +// 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; +} diff --git a/reactos/drivers/usb/cromwell/linux/pci_ids.h b/reactos/drivers/usb/cromwell/linux/pci_ids.h index 5d0f0185c54..8cb847eb6b7 100644 --- a/reactos/drivers/usb/cromwell/linux/pci_ids.h +++ b/reactos/drivers/usb/cromwell/linux/pci_ids.h @@ -1,16 +1,16 @@ -#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 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 + diff --git a/reactos/drivers/usb/cromwell/linux/usb.h b/reactos/drivers/usb/cromwell/linux/usb.h index 0e3d67ff5a3..ef1c1f7785e 100644 --- a/reactos/drivers/usb/cromwell/linux/usb.h +++ b/reactos/drivers/usb/cromwell/linux/usb.h @@ -1,1032 +1,1032 @@ -#ifndef __LINUX_USB_H -#define __LINUX_USB_H - - -#include "usb_ch9.h" - -#define USB_MAJOR 180 - - -#ifdef __KERNEL__ -#if 0 -#include -#include /* for -ENODEV */ -#include /* for mdelay() */ -#include /* for in_interrupt() */ -#include /* for struct list_head */ -#include /* for struct device */ -#include /* for struct file_operations */ -#include /* for struct completion */ -#include /* 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 +#ifndef __LINUX_USB_H +#define __LINUX_USB_H + + +#include "usb_ch9.h" + +#define USB_MAJOR 180 + + +#ifdef __KERNEL__ +#if 0 +#include +#include /* for -ENODEV */ +#include /* for mdelay() */ +#include /* for in_interrupt() */ +#include /* for struct list_head */ +#include /* for struct device */ +#include /* for struct file_operations */ +#include /* for struct completion */ +#include /* 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 diff --git a/reactos/drivers/usb/cromwell/linux/usb_ch9.h b/reactos/drivers/usb/cromwell/linux/usb_ch9.h index a679168973a..109ec384d59 100644 --- a/reactos/drivers/usb/cromwell/linux/usb_ch9.h +++ b/reactos/drivers/usb/cromwell/linux/usb_ch9.h @@ -1,315 +1,315 @@ -/* - * 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 /* __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 */ +/* + * 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 /* __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 */ diff --git a/reactos/drivers/usb/cromwell/sys/BootUSB.c b/reactos/drivers/usb/cromwell/sys/BootUSB.c index 2f582c5925a..243d101f9e7 100644 --- a/reactos/drivers/usb/cromwell/sys/BootUSB.c +++ b/reactos/drivers/usb/cromwell/sys/BootUSB.c @@ -1,90 +1,90 @@ -/* - * 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); - -} -/*------------------------------------------------------------------------*/ +/* + * 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); + +} +/*------------------------------------------------------------------------*/ diff --git a/reactos/drivers/usb/cromwell/sys/Makefile b/reactos/drivers/usb/cromwell/sys/Makefile index a57d8cec5c6..bdac3da9593 100644 --- a/reactos/drivers/usb/cromwell/sys/Makefile +++ b/reactos/drivers/usb/cromwell/sys/Makefile @@ -1,4 +1,4 @@ - -O_TARGET := usbwrapper.o BootUSB.o linuxwrapper.o xpad.o xremote.o usbkey.o risefall.o - -include $(TOPDIR)/Rules.make + +O_TARGET := usbwrapper.o BootUSB.o linuxwrapper.o xpad.o xremote.o usbkey.o risefall.o + +include $(TOPDIR)/Rules.make diff --git a/reactos/drivers/usb/cromwell/sys/linuxwrapper.c b/reactos/drivers/usb/cromwell/sys/linuxwrapper.c index 616cbd79a74..1a8f783cea5 100644 --- a/reactos/drivers/usb/cromwell/sys/linuxwrapper.c +++ b/reactos/drivers/usb/cromwell/sys/linuxwrapper.c @@ -1,375 +1,375 @@ -/* - * 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;nfunction && 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;nprobe) - { - 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_numprobe); - 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_irqsfunction && 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;nprobe) + { + 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_numprobe); + 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_irqs0x30)&&(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" +#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; +} diff --git a/reactos/drivers/usb/cromwell/sys/ros_wrapper.c b/reactos/drivers/usb/cromwell/sys/ros_wrapper.c index d97f1c6aa40..9a27f77ff44 100644 --- a/reactos/drivers/usb/cromwell/sys/ros_wrapper.c +++ b/reactos/drivers/usb/cromwell/sys/ros_wrapper.c @@ -1,10 +1,10 @@ -#include "../usb_wrapper.h" - - -void wait_ms(int mils) -{ - LARGE_INTEGER Interval; - - Interval.QuadPart = -mils*10; - KeDelayExecutionThread(KernelMode, FALSE, &Interval); -} +#include "../usb_wrapper.h" + + +void wait_ms(int mils) +{ + LARGE_INTEGER Interval; + + Interval.QuadPart = -mils*10; + KeDelayExecutionThread(KernelMode, FALSE, &Interval); +} diff --git a/reactos/drivers/usb/cromwell/sys/usbkey.c b/reactos/drivers/usb/cromwell/sys/usbkey.c index efb237d49a9..cab2d1e591d 100644 --- a/reactos/drivers/usb/cromwell/sys/usbkey.c +++ b/reactos/drivers/usb/cromwell/sys/usbkey.c @@ -1,123 +1,123 @@ -#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); -} +#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); +} diff --git a/reactos/drivers/usb/cromwell/sys/usbwrapper.c b/reactos/drivers/usb/cromwell/sys/usbwrapper.c index f2de2204543..ce952e4ab4a 100644 --- a/reactos/drivers/usb/cromwell/sys/usbwrapper.c +++ b/reactos/drivers/usb/cromwell/sys/usbwrapper.c @@ -1,65 +1,65 @@ -/* - * Interface calls to BIOS - * - * 2003-06-21 Georg Acher (georg@acher.org) - * - */ - -#include "boot.h" -#include -#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; -} -/*------------------------------------------------------------------------*/ +/* + * Interface calls to BIOS + * + * 2003-06-21 Georg Acher (georg@acher.org) + * + */ + +#include "boot.h" +#include +#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; +} +/*------------------------------------------------------------------------*/ diff --git a/reactos/drivers/usb/cromwell/sys/xpad.c b/reactos/drivers/usb/cromwell/sys/xpad.c index e6d3122c80f..55360369955 100644 --- a/reactos/drivers/usb/cromwell/sys/xpad.c +++ b/reactos/drivers/usb/cromwell/sys/xpad.c @@ -1,183 +1,183 @@ -/* - * 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 - * - */ - - - -#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); -} - -/*------------------------------------------------------------------------*/ - - - +/* + * 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 + * + */ + + + +#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); +} + +/*------------------------------------------------------------------------*/ + + + diff --git a/reactos/drivers/usb/cromwell/sys/xremote.c b/reactos/drivers/usb/cromwell/sys/xremote.c index 7f53f211cc7..3b98dec8572 100644 --- a/reactos/drivers/usb/cromwell/sys/xremote.c +++ b/reactos/drivers/usb/cromwell/sys/xremote.c @@ -1,142 +1,142 @@ -/* - * 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: xremote.c,v 1.5 2004/11/22 19:10:57 davidmpye Exp $ - * - * Copyright (c) 2002 Steven Toth - * - * 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); -} +/* + * 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 + * + * 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); +} diff --git a/reactos/drivers/usb/cromwell/uhci/makefile b/reactos/drivers/usb/cromwell/uhci/makefile index b1170524da6..02196fed0a2 100644 --- a/reactos/drivers/usb/cromwell/uhci/makefile +++ b/reactos/drivers/usb/cromwell/uhci/makefile @@ -1,16 +1,16 @@ -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 +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 diff --git a/reactos/drivers/usb/cromwell/uhci/uhci.def b/reactos/drivers/usb/cromwell/uhci/uhci.def index b0ec75808f2..320634a6a47 100644 --- a/reactos/drivers/usb/cromwell/uhci/uhci.def +++ b/reactos/drivers/usb/cromwell/uhci/uhci.def @@ -1,2 +1,2 @@ -LIBRARY uhci.sys -EXPORTS +LIBRARY uhci.sys +EXPORTS diff --git a/reactos/drivers/usb/cromwell/uhci/uhci.rc b/reactos/drivers/usb/cromwell/uhci/uhci.rc index d5eb940f114..5acdfd37517 100644 --- a/reactos/drivers/usb/cromwell/uhci/uhci.rc +++ b/reactos/drivers/usb/cromwell/uhci/uhci.rc @@ -1,5 +1,5 @@ -#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 +#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 diff --git a/reactos/drivers/usb/cromwell/uhci/uhci_config.h b/reactos/drivers/usb/cromwell/uhci/uhci_config.h index cd59c84d5b7..207aa328174 100644 --- a/reactos/drivers/usb/cromwell/uhci/uhci_config.h +++ b/reactos/drivers/usb/cromwell/uhci/uhci_config.h @@ -1,5 +1,5 @@ -/* - * Configs for UHCI - */ - -#define CONFIG_PCI +/* + * Configs for UHCI + */ + +#define CONFIG_PCI diff --git a/reactos/drivers/usb/cromwell/uhci/uhci_main.c b/reactos/drivers/usb/cromwell/uhci/uhci_main.c index b5cc9523190..d329f5f4189 100644 --- a/reactos/drivers/usb/cromwell/uhci/uhci_main.c +++ b/reactos/drivers/usb/cromwell/uhci/uhci_main.c @@ -1,321 +1,321 @@ -/* - 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 -#include -#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; -} +/* + 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 +#include +#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; +} diff --git a/reactos/drivers/usb/cromwell/usb_wrapper.h b/reactos/drivers/usb/cromwell/usb_wrapper.h index 4b810cad0cb..74b46f957af 100644 --- a/reactos/drivers/usb/cromwell/usb_wrapper.h +++ b/reactos/drivers/usb/cromwell/usb_wrapper.h @@ -1,15 +1,15 @@ -//#include -//#include -//#include -#include -#include - -void wait_ms(int mils); - -#include "linux/linux_wrapper.h" -#define __KERNEL__ -#undef CONFIG_PCI -#define CONFIG_PCI - -#include "linux/usb.h" +//#include +//#include +//#include +#include +#include + +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