diff options
139 files changed, 5820 insertions, 1563 deletions
diff --git a/Documentation/usb/proc_usb_info.txt b/Documentation/usb/proc_usb_info.txt index 22c5331..077e903 100644 --- a/Documentation/usb/proc_usb_info.txt +++ b/Documentation/usb/proc_usb_info.txt @@ -213,15 +213,16 @@ C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA Interface descriptor info (can be multiple per Config): -I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss -| | | | | | | |__Driver name -| | | | | | | or "(none)" -| | | | | | |__InterfaceProtocol -| | | | | |__InterfaceSubClass -| | | | |__InterfaceClass -| | | |__NumberOfEndpoints -| | |__AlternateSettingNumber -| |__InterfaceNumber +I:* If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss +| | | | | | | | |__Driver name +| | | | | | | | or "(none)" +| | | | | | | |__InterfaceProtocol +| | | | | | |__InterfaceSubClass +| | | | | |__InterfaceClass +| | | | |__NumberOfEndpoints +| | | |__AlternateSettingNumber +| | |__InterfaceNumber +| |__ "*" indicates the active altsetting (others are " ") |__Interface info tag A given interface may have one or more "alternate" settings. @@ -277,7 +278,7 @@ of the USB devices on a system's root hub. (See more below on how to do this.) The Interface lines can be used to determine what driver is -being used for each device. +being used for each device, and which altsetting it activated. The Configuration lines could be used to list maximum power (in milliamps) that a system's USB devices are using. diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt index e65ec82..0f6808a 100644 --- a/Documentation/usb/usbmon.txt +++ b/Documentation/usb/usbmon.txt @@ -77,7 +77,7 @@ that the file size is not excessive for your favourite editor. The '1t' type data consists of a stream of events, such as URB submission, URB callback, submission error. Every event is a text line, which consists -of whitespace separated words. The number of position of words may depend +of whitespace separated words. The number or position of words may depend on the event type, but there is a set of words, common for all types. Here is the list of words, from left to right: @@ -170,4 +170,152 @@ dd65f0e8 4128379808 C Bo:005:02 0 31 > * Raw binary format and API -TBD +The overall architecture of the API is about the same as the one above, +only the events are delivered in binary format. Each event is sent in +the following structure (its name is made up, so that we can refer to it): + +struct usbmon_packet { + u64 id; /* 0: URB ID - from submission to callback */ + unsigned char type; /* 8: Same as text; extensible. */ + unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */ + unsigned char epnum; /* Endpoint number and transfer direction */ + unsigned char devnum; /* Device address */ + u16 busnum; /* 12: Bus number */ + char flag_setup; /* 14: Same as text */ + char flag_data; /* 15: Same as text; Binary zero is OK. */ + s64 ts_sec; /* 16: gettimeofday */ + s32 ts_usec; /* 24: gettimeofday */ + int status; /* 28: */ + unsigned int length; /* 32: Length of data (submitted or actual) */ + unsigned int len_cap; /* 36: Delivered length */ + unsigned char setup[8]; /* 40: Only for Control 'S' */ +}; /* 48 bytes total */ + +These events can be received from a character device by reading with read(2), +with an ioctl(2), or by accessing the buffer with mmap. + +The character device is usually called /dev/usbmonN, where N is the USB bus +number. Number zero (/dev/usbmon0) is special and means "all buses". +However, this feature is not implemented yet. Note that specific naming +policy is set by your Linux distribution. + +If you create /dev/usbmon0 by hand, make sure that it is owned by root +and has mode 0600. Otherwise, unpriviledged users will be able to snoop +keyboard traffic. + +The following ioctl calls are available, with MON_IOC_MAGIC 0x92: + + MON_IOCQ_URB_LEN, defined as _IO(MON_IOC_MAGIC, 1) + +This call returns the length of data in the next event. Note that majority of +events contain no data, so if this call returns zero, it does not mean that +no events are available. + + MON_IOCG_STATS, defined as _IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats) + +The argument is a pointer to the following structure: + +struct mon_bin_stats { + u32 queued; + u32 dropped; +}; + +The member "queued" refers to the number of events currently queued in the +buffer (and not to the number of events processed since the last reset). + +The member "dropped" is the number of events lost since the last call +to MON_IOCG_STATS. + + MON_IOCT_RING_SIZE, defined as _IO(MON_IOC_MAGIC, 4) + +This call sets the buffer size. The argument is the size in bytes. +The size may be rounded down to the next chunk (or page). If the requested +size is out of [unspecified] bounds for this kernel, the call fails with +-EINVAL. + + MON_IOCQ_RING_SIZE, defined as _IO(MON_IOC_MAGIC, 5) + +This call returns the current size of the buffer in bytes. + + MON_IOCX_GET, defined as _IOW(MON_IOC_MAGIC, 6, struct mon_get_arg) + +This call waits for events to arrive if none were in the kernel buffer, +then returns the first event. Its argument is a pointer to the following +structure: + +struct mon_get_arg { + struct usbmon_packet *hdr; + void *data; + size_t alloc; /* Length of data (can be zero) */ +}; + +Before the call, hdr, data, and alloc should be filled. Upon return, the area +pointed by hdr contains the next event structure, and the data buffer contains +the data, if any. The event is removed from the kernel buffer. + + MON_IOCX_MFETCH, defined as _IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg) + +This ioctl is primarily used when the application accesses the buffer +with mmap(2). Its argument is a pointer to the following structure: + +struct mon_mfetch_arg { + uint32_t *offvec; /* Vector of events fetched */ + uint32_t nfetch; /* Number of events to fetch (out: fetched) */ + uint32_t nflush; /* Number of events to flush */ +}; + +The ioctl operates in 3 stages. + +First, it removes and discards up to nflush events from the kernel buffer. +The actual number of events discarded is returned in nflush. + +Second, it waits for an event to be present in the buffer, unless the pseudo- +device is open with O_NONBLOCK. + +Third, it extracts up to nfetch offsets into the mmap buffer, and stores +them into the offvec. The actual number of event offsets is stored into +the nfetch. + + MON_IOCH_MFLUSH, defined as _IO(MON_IOC_MAGIC, 8) + +This call removes a number of events from the kernel buffer. Its argument +is the number of events to remove. If the buffer contains fewer events +than requested, all events present are removed, and no error is reported. +This works when no events are available too. + + FIONBIO + +The ioctl FIONBIO may be implemented in the future, if there's a need. + +In addition to ioctl(2) and read(2), the special file of binary API can +be polled with select(2) and poll(2). But lseek(2) does not work. + +* Memory-mapped access of the kernel buffer for the binary API + +The basic idea is simple: + +To prepare, map the buffer by getting the current size, then using mmap(2). +Then, execute a loop similar to the one written in pseudo-code below: + + struct mon_mfetch_arg fetch; + struct usbmon_packet *hdr; + int nflush = 0; + for (;;) { + fetch.offvec = vec; // Has N 32-bit words + fetch.nfetch = N; // Or less than N + fetch.nflush = nflush; + ioctl(fd, MON_IOCX_MFETCH, &fetch); // Process errors, too + nflush = fetch.nfetch; // This many packets to flush when done + for (i = 0; i < nflush; i++) { + hdr = (struct ubsmon_packet *) &mmap_area[vec[i]]; + if (hdr->type == '@') // Filler packet + continue; + caddr_t data = &mmap_area[vec[i]] + 64; + process_packet(hdr, data); + } + } + +Thus, the main idea is to execute only one ioctl per N events. + +Although the buffer is circular, the returned headers and data do not cross +the end of the buffer, so the above pseudo-code does not need any gathering. diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index d6abe49..c1e01b2 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -529,6 +529,11 @@ config PPC_PS3 bool "Sony PS3 (incomplete)" depends on PPC_MULTIPLATFORM && PPC64 select PPC_CELL + select USB_ARCH_HAS_OHCI + select USB_OHCI_LITTLE_ENDIAN + select USB_OHCI_BIG_ENDIAN_MMIO + select USB_ARCH_HAS_EHCI + select USB_EHCI_BIG_ENDIAN_MMIO help This option enables support for the Sony PS3 game console and other platforms using the PS3 hypervisor. diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/i2c/chips/isp1301_omap.c index ccdf3e9..9fafadb 100644 --- a/drivers/i2c/chips/isp1301_omap.c +++ b/drivers/i2c/chips/isp1301_omap.c @@ -27,7 +27,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/platform_device.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include <linux/usb.h> #include <linux/usb/otg.h> diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h index 4363a91..3daf049 100644 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ b/drivers/media/video/zc0301/zc0301_sensor.h @@ -75,7 +75,6 @@ static const struct usb_device_id zc0301_id_table[] = { \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ { ZC0301_USB_DEVICE(0x055f, 0xd003, 0xff), }, /* TAS5130 */ \ { ZC0301_USB_DEVICE(0x055f, 0xd004, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ { ZC0301_USB_DEVICE(0x0ac8, 0x301b, 0xff), }, /* PB-0330/HV7131 */ \ { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \ diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 8ed6c75..638b800 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -36,7 +36,7 @@ #include <linux/stat.h> #include <linux/timer.h> #include <linux/types.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/workqueue.h> #include "usbatm.h" diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 6377db1..63e50a1 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -398,6 +398,9 @@ static int usblp_open(struct inode *inode, struct file *file) retval = 0; #endif + retval = usb_autopm_get_interface(intf); + if (retval < 0) + goto out; usblp->used = 1; file->private_data = usblp; @@ -442,6 +445,7 @@ static int usblp_release(struct inode *inode, struct file *file) usblp->used = 0; if (usblp->present) { usblp_unlink_urbs(usblp); + usb_autopm_put_interface(usblp->intf); } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); mutex_unlock (&usblp_mutex); @@ -1203,14 +1207,9 @@ static int usblp_suspend (struct usb_interface *intf, pm_message_t message) { struct usblp *usblp = usb_get_intfdata (intf); - /* this races against normal access and open */ - mutex_lock (&usblp_mutex); - mutex_lock (&usblp->mut); /* we take no more IO */ usblp->sleeping = 1; usblp_unlink_urbs(usblp); - mutex_unlock (&usblp->mut); - mutex_unlock (&usblp_mutex); return 0; } @@ -1220,15 +1219,9 @@ static int usblp_resume (struct usb_interface *intf) struct usblp *usblp = usb_get_intfdata (intf); int r; - mutex_lock (&usblp_mutex); - mutex_lock (&usblp->mut); - usblp->sleeping = 0; r = handle_bidir (usblp); - mutex_unlock (&usblp->mut); - mutex_unlock (&usblp_mutex); - return r; } @@ -1251,6 +1244,7 @@ static struct usb_driver usblp_driver = { .suspend = usblp_suspend, .resume = usblp_resume, .id_table = usblp_ids, + .supports_autosuspend = 1, }; static int __init usblp_init(void) diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 3e66b2a..2fc0f88 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -33,19 +33,6 @@ config USB_DEVICEFS Most users want to say Y here. -config USB_BANDWIDTH - bool "Enforce USB bandwidth allocation (EXPERIMENTAL)" - depends on USB && EXPERIMENTAL - help - If you say Y here, the USB subsystem enforces USB bandwidth - allocation and will prevent some device opens from succeeding - if they would cause USB bandwidth usage to go above 90% of - the bus bandwidth. - - If you say N here, these conditions will cause warning messages - about USB bandwidth usage to be logged and some devices or - drivers may not work correctly. - config USB_DYNAMIC_MINORS bool "Dynamic USB minor allocation (EXPERIMENTAL)" depends on USB && EXPERIMENTAL diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index c3915dc..ead2475 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -49,9 +49,9 @@ static const size_t pool_max [HCD_BUFFER_POOLS] = { * * Call hcd_buffer_destroy() to clean up after using those pools. */ -int hcd_buffer_create (struct usb_hcd *hcd) +int hcd_buffer_create(struct usb_hcd *hcd) { - char name [16]; + char name[16]; int i, size; if (!hcd->self.controller->dma_mask) @@ -60,11 +60,11 @@ int hcd_buffer_create (struct usb_hcd *hcd) for (i = 0; i < HCD_BUFFER_POOLS; i++) { if (!(size = pool_max [i])) continue; - snprintf (name, sizeof name, "buffer-%d", size); - hcd->pool [i] = dma_pool_create (name, hcd->self.controller, + snprintf(name, sizeof name, "buffer-%d", size); + hcd->pool[i] = dma_pool_create(name, hcd->self.controller, size, size, 0); if (!hcd->pool [i]) { - hcd_buffer_destroy (hcd); + hcd_buffer_destroy(hcd); return -ENOMEM; } } @@ -79,14 +79,14 @@ int hcd_buffer_create (struct usb_hcd *hcd) * * This frees the buffer pools created by hcd_buffer_create(). */ -void hcd_buffer_destroy (struct usb_hcd *hcd) +void hcd_buffer_destroy(struct usb_hcd *hcd) { int i; for (i = 0; i < HCD_BUFFER_POOLS; i++) { - struct dma_pool *pool = hcd->pool [i]; + struct dma_pool *pool = hcd->pool[i]; if (pool) { - dma_pool_destroy (pool); + dma_pool_destroy(pool); hcd->pool[i] = NULL; } } @@ -97,8 +97,8 @@ void hcd_buffer_destroy (struct usb_hcd *hcd) * better sharing and to leverage mm/slab.c intelligence. */ -void *hcd_buffer_alloc ( - struct usb_bus *bus, +void *hcd_buffer_alloc( + struct usb_bus *bus, size_t size, gfp_t mem_flags, dma_addr_t *dma @@ -110,18 +110,18 @@ void *hcd_buffer_alloc ( /* some USB hosts just use PIO */ if (!bus->controller->dma_mask) { *dma = ~(dma_addr_t) 0; - return kmalloc (size, mem_flags); + return kmalloc(size, mem_flags); } for (i = 0; i < HCD_BUFFER_POOLS; i++) { if (size <= pool_max [i]) - return dma_pool_alloc (hcd->pool [i], mem_flags, dma); + return dma_pool_alloc(hcd->pool [i], mem_flags, dma); } - return dma_alloc_coherent (hcd->self.controller, size, dma, 0); + return dma_alloc_coherent(hcd->self.controller, size, dma, 0); } -void hcd_buffer_free ( - struct usb_bus *bus, +void hcd_buffer_free( + struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma @@ -134,15 +134,15 @@ void hcd_buffer_free ( return; if (!bus->controller->dma_mask) { - kfree (addr); + kfree(addr); return; } for (i = 0; i < HCD_BUFFER_POOLS; i++) { if (size <= pool_max [i]) { - dma_pool_free (hcd->pool [i], addr, dma); + dma_pool_free(hcd->pool [i], addr, dma); return; } } - dma_free_coherent (hcd->self.controller, size, addr, dma); + dma_free_coherent(hcd->self.controller, size, addr, dma); } diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index ea398e5..a47c30b 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -104,7 +104,7 @@ static const char *format_config = static const char *format_iface = /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ - "I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; + "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; static const char *format_endpt = /* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ @@ -164,10 +164,10 @@ static const char *class_decode(const int class) for (ix = 0; clas_info[ix].class != -1; ix++) if (clas_info[ix].class == class) break; - return (clas_info[ix].class_name); + return clas_info[ix].class_name; } -static char *usb_dump_endpoint_descriptor ( +static char *usb_dump_endpoint_descriptor( int speed, char *start, char *end, @@ -212,9 +212,9 @@ static char *usb_dump_endpoint_descriptor ( break; case USB_ENDPOINT_XFER_INT: type = "Int."; - if (speed == USB_SPEED_HIGH) { + if (speed == USB_SPEED_HIGH) interval = 1 << (desc->bInterval - 1); - } else + else interval = desc->bInterval; break; default: /* "can't happen" */ @@ -242,15 +242,19 @@ static char *usb_dump_interface_descriptor(char *start, char *end, { const struct usb_interface_descriptor *desc = &intfc->altsetting[setno].desc; const char *driver_name = ""; + int active = 0; if (start > end) return start; down_read(&usb_bus_type.subsys.rwsem); - if (iface) + if (iface) { driver_name = (iface->dev.driver ? iface->dev.driver->name : "(none)"); + active = (desc == &iface->cur_altsetting->desc); + } start += sprintf(start, format_iface, + active ? '*' : ' ', /* mark active altsetting */ desc->bInterfaceNumber, desc->bAlternateSetting, desc->bNumEndpoints, @@ -343,7 +347,7 @@ static char *usb_dump_device_descriptor(char *start, char *end, const struct usb if (start > end) return start; - start += sprintf (start, format_device1, + start += sprintf(start, format_device1, bcdUSB >> 8, bcdUSB & 0xff, desc->bDeviceClass, class_decode (desc->bDeviceClass), @@ -363,7 +367,7 @@ static char *usb_dump_device_descriptor(char *start, char *end, const struct usb /* * Dump the different strings that this device holds. */ -static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev) +static char *usb_dump_device_strings(char *start, char *end, struct usb_device *dev) { if (start > end) return start; @@ -395,7 +399,7 @@ static char *usb_dump_desc(char *start, char *end, struct usb_device *dev) if (start > end) return start; - start = usb_dump_device_strings (start, end, dev); + start = usb_dump_device_strings(start, end, dev); for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (start > end) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 4b3a6ab..2087766 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -522,19 +522,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig static struct usb_device *usbdev_lookup_minor(int minor) { - struct class_device *class_dev; - struct usb_device *dev = NULL; + struct device *device; + struct usb_device *udev = NULL; down(&usb_device_class->sem); - list_for_each_entry(class_dev, &usb_device_class->children, node) { - if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { - dev = class_dev->class_data; + list_for_each_entry(device, &usb_device_class->devices, node) { + if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { + udev = device->platform_data; break; } } up(&usb_device_class->sem); - return dev; + return udev; }; /* @@ -570,6 +570,7 @@ static int usbdev_open(struct inode *inode, struct file *file) ps->dev = dev; ps->file = file; spin_lock_init(&ps->lock); + INIT_LIST_HEAD(&ps->list); INIT_LIST_HEAD(&ps->async_pending); INIT_LIST_HEAD(&ps->async_completed); init_waitqueue_head(&ps->wait); @@ -1596,19 +1597,19 @@ static int usbdev_add(struct usb_device *dev) { int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); - dev->class_dev = class_device_create(usb_device_class, NULL, - MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev, + dev->usbfs_dev = device_create(usb_device_class, &dev->dev, + MKDEV(USB_DEVICE_MAJOR, minor), "usbdev%d.%d", dev->bus->busnum, dev->devnum); - if (IS_ERR(dev->class_dev)) - return PTR_ERR(dev->class_dev); + if (IS_ERR(dev->usbfs_dev)) + return PTR_ERR(dev->usbfs_dev); - dev->class_dev->class_data = dev; + dev->usbfs_dev->platform_data = dev; return 0; } static void usbdev_remove(struct usb_device *dev) { - class_device_unregister(dev->class_dev); + device_unregister(dev->usbfs_dev); } static int usbdev_notify(struct notifier_block *self, unsigned long action, diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d505926..600d1bc 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -28,24 +28,16 @@ #include "hcd.h" #include "usb.h" -static int usb_match_one_id(struct usb_interface *interface, - const struct usb_device_id *id); - -struct usb_dynid { - struct list_head node; - struct usb_device_id id; -}; - #ifdef CONFIG_HOTPLUG /* * Adds a new dynamic USBdevice ID to this driver, * and cause the driver to probe for all devices again. */ -static ssize_t store_new_id(struct device_driver *driver, - const char *buf, size_t count) +ssize_t usb_store_new_id(struct usb_dynids *dynids, + struct device_driver *driver, + const char *buf, size_t count) { - struct usb_driver *usb_drv = to_usb_driver(driver); struct usb_dynid *dynid; u32 idVendor = 0; u32 idProduct = 0; @@ -65,9 +57,9 @@ static ssize_t store_new_id(struct device_driver *driver, dynid->id.idProduct = idProduct; dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE; - spin_lock(&usb_drv->dynids.lock); - list_add_tail(&usb_drv->dynids.list, &dynid->node); - spin_unlock(&usb_drv->dynids.lock); + spin_lock(&dynids->lock); + list_add_tail(&dynids->list, &dynid->node); + spin_unlock(&dynids->lock); if (get_driver(driver)) { retval = driver_attach(driver); @@ -78,6 +70,15 @@ static ssize_t store_new_id(struct device_driver *driver, return retval; return count; } +EXPORT_SYMBOL_GPL(usb_store_new_id); + +static ssize_t store_new_id(struct device_driver *driver, + const char *buf, size_t count) +{ + struct usb_driver *usb_drv = to_usb_driver(driver); + + return usb_store_new_id(&usb_drv->dynids, driver, buf, count); +} static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); static int usb_create_newid_file(struct usb_driver *usb_drv) @@ -365,8 +366,8 @@ void usb_driver_release_interface(struct usb_driver *driver, EXPORT_SYMBOL(usb_driver_release_interface); /* returns 0 if no match, 1 if match */ -static int usb_match_one_id(struct usb_interface *interface, - const struct usb_device_id *id) +int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_host_interface *intf; struct usb_device *dev; @@ -432,6 +433,8 @@ static int usb_match_one_id(struct usb_interface *interface, return 1; } +EXPORT_SYMBOL_GPL(usb_match_one_id); + /** * usb_match_id - find first usb_device_id matching device or interface * @interface: the interface of interest diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index f794f07..01c857a 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -194,14 +194,13 @@ int usb_register_dev(struct usb_interface *intf, ++temp; else temp = name; - intf->class_dev = class_device_create(usb_class->class, NULL, - MKDEV(USB_MAJOR, minor), - &intf->dev, "%s", temp); - if (IS_ERR(intf->class_dev)) { + intf->usb_dev = device_create(usb_class->class, &intf->dev, + MKDEV(USB_MAJOR, minor), "%s", temp); + if (IS_ERR(intf->usb_dev)) { spin_lock (&minor_lock); usb_minors[intf->minor] = NULL; spin_unlock (&minor_lock); - retval = PTR_ERR(intf->class_dev); + retval = PTR_ERR(intf->usb_dev); } exit: return retval; @@ -242,8 +241,8 @@ void usb_deregister_dev(struct usb_interface *intf, spin_unlock (&minor_lock); snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base); - class_device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); - intf->class_dev = NULL; + device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); + intf->usb_dev = NULL; intf->minor = -1; destroy_usb_class(); } diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index ebb20ff..b531a4f 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -25,6 +25,20 @@ static inline const char *plural(int n) return (n == 1 ? "" : "s"); } +static int is_rndis(struct usb_interface_descriptor *desc) +{ + return desc->bInterfaceClass == USB_CLASS_COMM + && desc->bInterfaceSubClass == 2 + && desc->bInterfaceProtocol == 0xff; +} + +static int is_activesync(struct usb_interface_descriptor *desc) +{ + return desc->bInterfaceClass == USB_CLASS_MISC + && desc->bInterfaceSubClass == 1 + && desc->bInterfaceProtocol == 1; +} + static int choose_configuration(struct usb_device *udev) { int i; @@ -87,14 +101,12 @@ static int choose_configuration(struct usb_device *udev) continue; } - /* If the first config's first interface is COMM/2/0xff - * (MSFT RNDIS), rule it out unless Linux has host-side - * RNDIS support. */ - if (i == 0 && desc - && desc->bInterfaceClass == USB_CLASS_COMM - && desc->bInterfaceSubClass == 2 - && desc->bInterfaceProtocol == 0xff) { -#ifndef CONFIG_USB_NET_RNDIS_HOST + /* When the first config's first interface is one of Microsoft's + * pet nonstandard Ethernet-over-USB protocols, ignore it unless + * this kernel has enabled the necessary host side driver. + */ + if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) { +#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) continue; #else best = c; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 10064af..b26c19e 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -45,8 +45,6 @@ #include "hub.h" -// #define USB_BANDWIDTH_MESSAGES - /*-------------------------------------------------------------------------*/ /* @@ -891,136 +889,6 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) } EXPORT_SYMBOL (usb_calc_bus_time); -/* - * usb_check_bandwidth(): - * - * old_alloc is from host_controller->bandwidth_allocated in microseconds; - * bustime is from calc_bus_time(), but converted to microseconds. - * - * returns <bustime in us> if successful, - * or -ENOSPC if bandwidth request fails. - * - * FIXME: - * This initial implementation does not use Endpoint.bInterval - * in managing bandwidth allocation. - * It probably needs to be expanded to use Endpoint.bInterval. - * This can be done as a later enhancement (correction). - * - * This will also probably require some kind of - * frame allocation tracking...meaning, for example, - * that if multiple drivers request interrupts every 10 USB frames, - * they don't all have to be allocated at - * frame numbers N, N+10, N+20, etc. Some of them could be at - * N+11, N+21, N+31, etc., and others at - * N+12, N+22, N+32, etc. - * - * Similarly for isochronous transfers... - * - * Individual HCDs can schedule more directly ... this logic - * is not correct for high speed transfers. - */ -int 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; -} -EXPORT_SYMBOL (usb_check_bandwidth); - - -/** - * 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 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 -} -EXPORT_SYMBOL (usb_claim_bandwidth); - - -/** - * 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 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; -} -EXPORT_SYMBOL (usb_release_bandwidth); - /*-------------------------------------------------------------------------*/ @@ -1034,11 +902,6 @@ static void urb_unlink (struct urb *urb) { unsigned long flags; - /* 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); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 8f8df0d..2a269ca 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -308,10 +308,6 @@ extern void usb_destroy_configuration(struct usb_device *dev); #define NS_TO_US(ns) ((ns + 500L) / 1000L) /* convert & round nanoseconds to microseconds */ -extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, - int bustime, int isoc); -extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, - int isoc); /* * Full/low speed bandwidth allocation constants/support. @@ -324,8 +320,6 @@ extern void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, #define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L) #define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L) -extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); - /* * Ceiling [nano/micro]seconds (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 diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1988224..590ec82 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -87,9 +87,6 @@ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); static struct task_struct *khubd_task; -/* multithreaded probe logic */ -static int multithread_probe = 0; - /* cycle leds on hubs that aren't blinking for attention */ static int blinkenlights = 0; module_param (blinkenlights, bool, S_IRUGO); @@ -1256,9 +1253,28 @@ static inline void show_string(struct usb_device *udev, char *id, char *string) static int __usb_port_suspend(struct usb_device *, int port1); #endif -static int __usb_new_device(void *void_data) +/** + * usb_new_device - perform initial device setup (usbcore-internal) + * @udev: newly addressed device (in ADDRESS state) + * + * This is called with devices which have been enumerated, but not yet + * configured. The device descriptor is available, but not descriptors + * for any device configuration. The caller must have locked either + * the parent hub (if udev is a normal device) or else the + * usb_bus_list_lock (if udev is a root hub). The parent's pointer to + * udev has already been installed, but udev is not yet visible through + * sysfs or other filesystem code. + * + * It will return if the device is configured properly or not. Zero if + * the interface was registered with the driver core; else a negative + * errno value. + * + * This call is synchronous, and may not be used in an interrupt context. + * + * Only the hub driver or root-hub registrar should ever call this. + */ +int usb_new_device(struct usb_device *udev) { - struct usb_device *udev = void_data; int err; /* Lock ourself into memory in order to keep a probe sequence @@ -1375,44 +1391,6 @@ fail: goto exit; } -/** - * usb_new_device - perform initial device setup (usbcore-internal) - * @udev: newly addressed device (in ADDRESS state) - * - * This is called with devices which have been enumerated, but not yet - * configured. The device descriptor is available, but not descriptors - * for any device configuration. The caller must have locked either - * the parent hub (if udev is a normal device) or else the - * usb_bus_list_lock (if udev is a root hub). The parent's pointer to - * udev has already been installed, but udev is not yet visible through - * sysfs or other filesystem code. - * - * The return value for this function depends on if the - * multithread_probe variable is set or not. If it's set, it will - * return a if the probe thread was successfully created or not. If the - * variable is not set, it will return if the device is configured - * properly or not. interfaces, in sysfs); else a negative errno value. - * - * This call is synchronous, and may not be used in an interrupt context. - * - * Only the hub driver or root-hub registrar should ever call this. - */ -int usb_new_device(struct usb_device *udev) -{ - struct task_struct *probe_task; - int ret = 0; - - if (multithread_probe) { - probe_task = kthread_run(__usb_new_device, udev, - "usb-probe-%s", udev->devnum); - if (IS_ERR(probe_task)) - ret = PTR_ERR(probe_task); - } else - ret = __usb_new_device(udev); - - return ret; -} - static int hub_port_status(struct usb_hub *hub, int port1, u16 *status, u16 *change) { diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 149aa8b..8aca357 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1545,11 +1545,7 @@ int usb_driver_set_configuration(struct usb_device *udev, int config) INIT_WORK(&req->work, driver_set_config_work); usb_get_dev(udev); - if (!schedule_work(&req->work)) { - usb_put_dev(udev); - kfree(req); - return -EINVAL; - } + schedule_work(&req->work); return 0; } EXPORT_SYMBOL_GPL(usb_driver_set_configuration); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 55d8f57..4eaa0ee 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -16,16 +16,16 @@ /* Active configuration fields */ #define usb_actconfig_show(field, multiplier, format_string) \ -static ssize_t show_##field (struct device *dev, \ +static ssize_t show_##field(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_device *udev; \ struct usb_host_config *actconfig; \ \ - udev = to_usb_device (dev); \ + udev = to_usb_device(dev); \ actconfig = udev->actconfig; \ if (actconfig) \ - return sprintf (buf, format_string, \ + return sprintf(buf, format_string, \ actconfig->desc.field * multiplier); \ else \ return 0; \ @@ -35,9 +35,9 @@ static ssize_t show_##field (struct device *dev, \ usb_actconfig_show(field, multiplier, format_string) \ static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); -usb_actconfig_attr (bNumInterfaces, 1, "%2d\n") -usb_actconfig_attr (bmAttributes, 1, "%2x\n") -usb_actconfig_attr (bMaxPower, 2, "%3dmA\n") +usb_actconfig_attr(bNumInterfaces, 1, "%2d\n") +usb_actconfig_attr(bmAttributes, 1, "%2x\n") +usb_actconfig_attr(bMaxPower, 2, "%3dmA\n") static ssize_t show_configuration_string(struct device *dev, struct device_attribute *attr, char *buf) @@ -45,7 +45,7 @@ static ssize_t show_configuration_string(struct device *dev, struct usb_device *udev; struct usb_host_config *actconfig; - udev = to_usb_device (dev); + udev = to_usb_device(dev); actconfig = udev->actconfig; if ((!actconfig) || (!actconfig->string)) return 0; @@ -57,16 +57,16 @@ static DEVICE_ATTR(configuration, S_IRUGO, show_configuration_string, NULL); usb_actconfig_show(bConfigurationValue, 1, "%u\n"); static ssize_t -set_bConfigurationValue (struct device *dev, struct device_attribute *attr, +set_bConfigurationValue(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct usb_device *udev = to_usb_device (dev); + struct usb_device *udev = to_usb_device(dev); int config, value; - if (sscanf (buf, "%u", &config) != 1 || config > 255) + if (sscanf(buf, "%u", &config) != 1 || config > 255) return -EINVAL; usb_lock_device(udev); - value = usb_set_configuration (udev, config); + value = usb_set_configuration(udev, config); usb_unlock_device(udev); return (value < 0) ? value : count; } @@ -81,7 +81,7 @@ static ssize_t show_##name(struct device *dev, \ { \ struct usb_device *udev; \ \ - udev = to_usb_device (dev); \ + udev = to_usb_device(dev); \ return sprintf(buf, "%s\n", udev->name); \ } \ static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); @@ -91,12 +91,12 @@ usb_string_attr(manufacturer); usb_string_attr(serial); static ssize_t -show_speed (struct device *dev, struct device_attribute *attr, char *buf) +show_speed(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; char *speed; - udev = to_usb_device (dev); + udev = to_usb_device(dev); switch (udev->speed) { case USB_SPEED_LOW: @@ -112,22 +112,22 @@ show_speed (struct device *dev, struct device_attribute *attr, char *buf) default: speed = "unknown"; } - return sprintf (buf, "%s\n", speed); + return sprintf(buf, "%s\n", speed); } static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL); static ssize_t -show_devnum (struct device *dev, struct device_attribute *attr, char *buf) +show_devnum(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; - udev = to_usb_device (dev); - return sprintf (buf, "%d\n", udev->devnum); + udev = to_usb_device(dev); + return sprintf(buf, "%d\n", udev->devnum); } static DEVICE_ATTR(devnum, S_IRUGO, show_devnum, NULL); static ssize_t -show_version (struct device *dev, struct device_attribute *attr, char *buf) +show_version(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; u16 bcdUSB; @@ -139,25 +139,25 @@ show_version (struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); static ssize_t -show_maxchild (struct device *dev, struct device_attribute *attr, char *buf) +show_maxchild(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; - udev = to_usb_device (dev); - return sprintf (buf, "%d\n", udev->maxchild); + udev = to_usb_device(dev); + return sprintf(buf, "%d\n", udev->maxchild); } static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); /* Descriptor fields */ #define usb_descriptor_attr_le16(field, format_string) \ static ssize_t \ -show_##field (struct device *dev, struct device_attribute *attr, \ +show_##field(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct usb_device *udev; \ \ - udev = to_usb_device (dev); \ - return sprintf (buf, format_string, \ + udev = to_usb_device(dev); \ + return sprintf(buf, format_string, \ le16_to_cpu(udev->descriptor.field)); \ } \ static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); @@ -168,21 +168,21 @@ usb_descriptor_attr_le16(bcdDevice, "%04x\n") #define usb_descriptor_attr(field, format_string) \ static ssize_t \ -show_##field (struct device *dev, struct device_attribute *attr, \ +show_##field(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct usb_device *udev; \ \ - udev = to_usb_device (dev); \ - return sprintf (buf, format_string, udev->descriptor.field); \ + udev = to_usb_device(dev); \ + return sprintf(buf, format_string, udev->descriptor.field); \ } \ static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); -usb_descriptor_attr (bDeviceClass, "%02x\n") -usb_descriptor_attr (bDeviceSubClass, "%02x\n") -usb_descriptor_attr (bDeviceProtocol, "%02x\n") -usb_descriptor_attr (bNumConfigurations, "%d\n") -usb_descriptor_attr (bMaxPacketSize0, "%d\n") +usb_descriptor_attr(bDeviceClass, "%02x\n") +usb_descriptor_attr(bDeviceSubClass, "%02x\n") +usb_descriptor_attr(bDeviceProtocol, "%02x\n") +usb_descriptor_attr(bNumConfigurations, "%d\n") +usb_descriptor_attr(bMaxPacketSize0, "%d\n") static struct attribute *dev_attrs[] = { /* current configuration's attributes */ @@ -220,17 +220,17 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) return retval; if (udev->manufacturer) { - retval = device_create_file (dev, &dev_attr_manufacturer); + retval = device_create_file(dev, &dev_attr_manufacturer); if (retval) goto error; } if (udev->product) { - retval = device_create_file (dev, &dev_attr_product); + retval = device_create_file(dev, &dev_attr_product); if (retval) goto error; } if (udev->serial) { - retval = device_create_file (dev, &dev_attr_serial); + retval = device_create_file(dev, &dev_attr_serial); if (retval) goto error; } @@ -246,7 +246,7 @@ error: return retval; } -void usb_remove_sysfs_dev_files (struct usb_device *udev) +void usb_remove_sysfs_dev_files(struct usb_device *udev) { struct device *dev = &udev->dev; @@ -264,22 +264,22 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev) /* Interface fields */ #define usb_intf_attr(field, format_string) \ static ssize_t \ -show_##field (struct device *dev, struct device_attribute *attr, \ +show_##field(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ - struct usb_interface *intf = to_usb_interface (dev); \ + struct usb_interface *intf = to_usb_interface(dev); \ \ - return sprintf (buf, format_string, \ + return sprintf(buf, format_string, \ intf->cur_altsetting->desc.field); \ } \ static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); -usb_intf_attr (bInterfaceNumber, "%02x\n") -usb_intf_attr (bAlternateSetting, "%2d\n") -usb_intf_attr (bNumEndpoints, "%02x\n") -usb_intf_attr (bInterfaceClass, "%02x\n") -usb_intf_attr (bInterfaceSubClass, "%02x\n") -usb_intf_attr (bInterfaceProtocol, "%02x\n") +usb_intf_attr(bInterfaceNumber, "%02x\n") +usb_intf_attr(bAlternateSetting, "%2d\n") +usb_intf_attr(bNumEndpoints, "%02x\n") +usb_intf_attr(bInterfaceClass, "%02x\n") +usb_intf_attr(bInterfaceSubClass, "%02x\n") +usb_intf_attr(bInterfaceProtocol, "%02x\n") static ssize_t show_interface_string(struct device *dev, struct device_attribute *attr, char *buf) @@ -288,8 +288,8 @@ static ssize_t show_interface_string(struct device *dev, struct usb_device *udev; int len; - intf = to_usb_interface (dev); - udev = interface_to_usbdev (intf); + intf = to_usb_interface(dev); + udev = interface_to_usbdev(intf); len = snprintf(buf, 256, "%s", intf->cur_altsetting->string); if (len < 0) return 0; @@ -384,7 +384,7 @@ error: return retval; } -void usb_remove_sysfs_intf_files (struct usb_interface *intf) +void usb_remove_sysfs_intf_files(struct usb_interface *intf) { usb_remove_intf_ep_files(intf); sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9801d08e..94ea972 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -235,16 +235,15 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) 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); + temp = usb_pipetype(pipe); + is_out = usb_pipeout(pipe); - if (!usb_pipecontrol (pipe) && dev->state < USB_STATE_CONFIGURED) + if (!usb_pipecontrol(pipe) && dev->state < USB_STATE_CONFIGURED) return -ENODEV; /* FIXME there should be a sharable lock protecting us against @@ -253,11 +252,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) * checks get made.) */ - max = usb_maxpacket (dev, pipe, is_out); + max = usb_maxpacket(dev, pipe, is_out); if (max <= 0) { dev_dbg(&dev->dev, "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n", - usb_pipeendpoint (pipe), is_out ? "out" : "in", + usb_pipeendpoint(pipe), is_out ? "out" : "in", __FUNCTION__, max); return -EMSGSIZE; } @@ -279,11 +278,11 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (urb->number_of_packets <= 0) return -EINVAL; for (n = 0; n < urb->number_of_packets; n++) { - len = urb->iso_frame_desc [n].length; + 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; + urb->iso_frame_desc[n].status = -EXDEV; + urb->iso_frame_desc[n].actual_length = 0; } } @@ -322,7 +321,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) /* fail if submitter gave bogus flags */ if (urb->transfer_flags != orig_flags) { - err ("BOGUS urb flags, %x --> %x", + err("BOGUS urb flags, %x --> %x", orig_flags, urb->transfer_flags); return -EINVAL; } @@ -373,7 +372,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) urb->interval = temp; } - return usb_hcd_submit_urb (urb, mem_flags); + return usb_hcd_submit_urb(urb, mem_flags); } /*-------------------------------------------------------------------*/ diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 02426d0..3db721c 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -233,7 +233,7 @@ static void usb_autosuspend_work(struct work_struct *work) * @parent: hub to which device is connected; null to allocate a root hub * @bus: bus used to access the device * @port1: one-based index of port; ignored for root hubs - * Context: !in_interrupt () + * Context: !in_interrupt() * * Only hub drivers (including virtual root hub drivers for host * controllers) should ever call this. @@ -277,22 +277,22 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) * as stable: bus->busnum changes easily from modprobe order, * cardbus or pci hotplugging, and so on. */ - if (unlikely (!parent)) { - dev->devpath [0] = '0'; + if (unlikely(!parent)) { + dev->devpath[0] = '0'; dev->dev.parent = bus->controller; - sprintf (&dev->dev.bus_id[0], "usb%d", bus->busnum); + sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum); } else { /* match any labeling on the hubs; it's one-based */ - if (parent->devpath [0] == '0') - snprintf (dev->devpath, sizeof dev->devpath, + if (parent->devpath[0] == '0') + snprintf(dev->devpath, sizeof dev->devpath, "%d", port1); else - snprintf (dev->devpath, sizeof dev->devpath, + snprintf(dev->devpath, sizeof dev->devpath, "%s.%d", parent->devpath, port1); dev->dev.parent = &parent->dev; - sprintf (&dev->dev.bus_id[0], "%d-%s", + sprintf(&dev->dev.bus_id[0], "%d-%s", bus->busnum, dev->devpath); /* hub driver sets up TT records */ @@ -463,7 +463,7 @@ static struct usb_device *match_device(struct usb_device *dev, /* see if this device matches */ if ((vendor_id == le16_to_cpu(dev->descriptor.idVendor)) && (product_id == le16_to_cpu(dev->descriptor.idProduct))) { - dev_dbg (&dev->dev, "matched this device!\n"); + dev_dbg(&dev->dev, "matched this device!\n"); ret_dev = usb_get_dev(dev); goto exit; } @@ -535,7 +535,7 @@ exit: */ int usb_get_current_frame_number(struct usb_device *dev) { - return usb_hcd_get_frame_number (dev); + return usb_hcd_get_frame_number(dev); } /*-------------------------------------------------------------------*/ @@ -593,7 +593,7 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size, * * When the buffer is no longer used, free it with usb_buffer_free(). */ -void *usb_buffer_alloc ( +void *usb_buffer_alloc( struct usb_device *dev, size_t size, gfp_t mem_flags, @@ -602,7 +602,7 @@ void *usb_buffer_alloc ( { if (!dev || !dev->bus) return NULL; - return hcd_buffer_alloc (dev->bus, size, mem_flags, dma); + return hcd_buffer_alloc(dev->bus, size, mem_flags, dma); } /** @@ -616,7 +616,7 @@ void *usb_buffer_alloc ( * been allocated using usb_buffer_alloc(), and the parameters must match * those provided in that allocation request. */ -void usb_buffer_free ( +void usb_buffer_free( struct usb_device *dev, size_t size, void *addr, @@ -627,7 +627,7 @@ void usb_buffer_free ( return; if (!addr) return; - hcd_buffer_free (dev->bus, size, addr, dma); + hcd_buffer_free(dev->bus, size, addr, dma); } /** @@ -647,7 +647,7 @@ void usb_buffer_free ( * Reverse the effect of this call with usb_buffer_unmap(). */ #if 0 -struct urb *usb_buffer_map (struct urb *urb) +struct urb *usb_buffer_map(struct urb *urb) { struct usb_bus *bus; struct device *controller; @@ -659,14 +659,14 @@ struct urb *usb_buffer_map (struct urb *urb) return NULL; if (controller->dma_mask) { - urb->transfer_dma = dma_map_single (controller, + urb->transfer_dma = dma_map_single(controller, urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein (urb->pipe) + usb_pipein(urb->pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - if (usb_pipecontrol (urb->pipe)) - urb->setup_dma = dma_map_single (controller, + if (usb_pipecontrol(urb->pipe)) + urb->setup_dma = dma_map_single(controller, urb->setup_packet, - sizeof (struct usb_ctrlrequest), + sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); // FIXME generic api broken like pci, can't report errors // if (urb->transfer_dma == DMA_ADDR_INVALID) return 0; @@ -689,7 +689,7 @@ struct urb *usb_buffer_map (struct urb *urb) * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s) * @urb: urb whose transfer_buffer/setup_packet will be synchronized */ -void usb_buffer_dmasync (struct urb *urb) +void usb_buffer_dmasync(struct urb *urb) { struct usb_bus *bus; struct device *controller; @@ -702,14 +702,14 @@ void usb_buffer_dmasync (struct urb *urb) return; if (controller->dma_mask) { - dma_sync_single (controller, + dma_sync_single(controller, urb->transfer_dma, urb->transfer_buffer_length, - usb_pipein (urb->pipe) + usb_pipein(urb->pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - if (usb_pipecontrol (urb->pipe)) - dma_sync_single (controller, + if (usb_pipecontrol(urb->pipe)) + dma_sync_single(controller, urb->setup_dma, - sizeof (struct usb_ctrlrequest), + sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); } } @@ -722,7 +722,7 @@ void usb_buffer_dmasync (struct urb *urb) * Reverses the effect of usb_buffer_map(). */ #if 0 -void usb_buffer_unmap (struct urb *urb) +void usb_buffer_unmap(struct urb *urb) { struct usb_bus *bus; struct device *controller; @@ -735,14 +735,14 @@ void usb_buffer_unmap (struct urb *urb) return; if (controller->dma_mask) { - dma_unmap_single (controller, + dma_unmap_single(controller, urb->transfer_dma, urb->transfer_buffer_length, - usb_pipein (urb->pipe) + usb_pipein(urb->pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - if (usb_pipecontrol (urb->pipe)) - dma_unmap_single (controller, + if (usb_pipecontrol(urb->pipe)) + dma_unmap_single(controller, urb->setup_dma, - sizeof (struct usb_ctrlrequest), + sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE); } urb->transfer_flags &= ~(URB_NO_TRANSFER_DMA_MAP @@ -783,15 +783,15 @@ int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe, struct device *controller; if (!dev - || usb_pipecontrol (pipe) + || 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); + return dma_map_sg(controller, sg, nents, + usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } /* XXX DISABLED, no users currently. If you wish to re-enable this @@ -823,8 +823,8 @@ void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe, || !controller->dma_mask) return; - dma_sync_sg (controller, sg, n_hw_ents, - usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + dma_sync_sg(controller, sg, n_hw_ents, + usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } #endif @@ -849,8 +849,8 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe, || !controller->dma_mask) return; - dma_unmap_sg (controller, sg, n_hw_ents, - usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + dma_unmap_sg(controller, sg, n_hw_ents, + usb_pipein(pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } /* format to disable USB on kernel command line is: nousb */ @@ -871,7 +871,7 @@ static int __init usb_init(void) { int retval; if (nousb) { - pr_info ("%s: USB support disabled\n", usbcore_name); + pr_info("%s: USB support disabled\n", usbcore_name); return 0; } @@ -971,19 +971,19 @@ 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_alloc); +EXPORT_SYMBOL(usb_buffer_free); #if 0 -EXPORT_SYMBOL (usb_buffer_map); -EXPORT_SYMBOL (usb_buffer_dmasync); -EXPORT_SYMBOL (usb_buffer_unmap); +EXPORT_SYMBOL(usb_buffer_map); +EXPORT_SYMBOL(usb_buffer_dmasync); +EXPORT_SYMBOL(usb_buffer_unmap); #endif -EXPORT_SYMBOL (usb_buffer_map_sg); +EXPORT_SYMBOL(usb_buffer_map_sg); #if 0 -EXPORT_SYMBOL (usb_buffer_dmasync_sg); +EXPORT_SYMBOL(usb_buffer_dmasync_sg); #endif -EXPORT_SYMBOL (usb_buffer_unmap_sg); +EXPORT_SYMBOL(usb_buffer_unmap_sg); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 812c733..f390501 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -39,7 +39,7 @@ #include <linux/interrupt.h> #include <linux/proc_fs.h> #include <linux/clk.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include <asm/byteorder.h> @@ -1807,16 +1807,13 @@ static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg) || !wake || at91_suspend_entering_slow_clock()) { pullup(udc, 0); - disable_irq_wake(udc->udp_irq); + wake = 0; } else enable_irq_wake(udc->udp_irq); - if (udc->board.vbus_pin > 0) { - if (wake) - enable_irq_wake(udc->board.vbus_pin); - else - disable_irq_wake(udc->board.vbus_pin); - } + udc->active_suspend = wake; + if (udc->board.vbus_pin > 0 && wake) + enable_irq_wake(udc->board.vbus_pin); return 0; } @@ -1824,8 +1821,14 @@ static int at91udc_resume(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); + if (udc->board.vbus_pin > 0 && udc->active_suspend) + disable_irq_wake(udc->board.vbus_pin); + /* maybe reconnect to host; if so, clocks on */ - pullup(udc, 1); + if (udc->active_suspend) + disable_irq_wake(udc->udp_irq); + else + pullup(udc, 1); return 0; } #else diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index 677089b..7e34e2f 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -136,6 +136,7 @@ struct at91_udc { unsigned wait_for_addr_ack:1; unsigned wait_for_config_ack:1; unsigned selfpowered:1; + unsigned active_suspend:1; u8 addr; struct at91_udc_data board; struct clk *iclk, *fclk; diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 83b4866..d18901b 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -24,7 +24,7 @@ #include <linux/string.h> #include <linux/device.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 53d5845..f28af06 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -27,7 +27,7 @@ #include <linux/ctype.h> #include <linux/string.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include "gadget_chips.h" diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index d15bf22..22e3c94 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -47,7 +47,7 @@ #include <asm/uaccess.h> #include <asm/unaligned.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb/cdc.h> #include <linux/usb_gadget.h> @@ -72,9 +72,18 @@ * * There's some hardware that can't talk CDC. We make that hardware * implement a "minimalist" vendor-agnostic CDC core: same framing, but - * link-level setup only requires activating the configuration. - * Linux supports it, but other host operating systems may not. - * (This is a subset of CDC Ethernet.) + * link-level setup only requires activating the configuration. Only the + * endpoint descriptors, and product/vendor IDs, are relevant; no control + * operations are available. Linux supports it, but other host operating + * systems may not. (This is a subset of CDC Ethernet.) + * + * It turns out that if you add a few descriptors to that "CDC Subset", + * (Windows) host side drivers from MCCI can treat it as one submode of + * a proprietary scheme called "SAFE" ... without needing to know about + * specific product/vendor IDs. So we do that, making it easier to use + * those MS-Windows drivers. Those added descriptors make it resemble a + * CDC MDLM device, but they don't change device behavior at all. (See + * MCCI Engineering report 950198 "SAFE Networking Functions".) * * A third option is also in use. Rather than CDC Ethernet, or something * simpler, Microsoft pushes their own approach: RNDIS. The published @@ -254,6 +263,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_S3C2410 +#define DEV_CONFIG_CDC +#endif + #ifdef CONFIG_USB_GADGET_AT91 #define DEV_CONFIG_CDC #endif @@ -266,6 +279,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_HUSB2DEV +#define DEV_CONFIG_CDC +#endif + /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -283,9 +300,6 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_SUBSET #endif -#ifdef CONFIG_USB_GADGET_S3C2410 -#define DEV_CONFIG_CDC -#endif /*-------------------------------------------------------------------------*/ @@ -487,8 +501,17 @@ rndis_config = { * endpoint. Both have a "data" interface and two bulk endpoints. * There are also differences in how control requests are handled. * - * RNDIS shares a lot with CDC-Ethernet, since it's a variant of - * the CDC-ACM (modem) spec. + * RNDIS shares a lot with CDC-Ethernet, since it's a variant of the + * CDC-ACM (modem) spec. Unfortunately MSFT's RNDIS driver is buggy; it + * may hang or oops. Since bugfixes (or accurate specs, letting Linux + * work around those bugs) are unlikely to ever come from MSFT, you may + * wish to avoid using RNDIS. + * + * MCCI offers an alternative to RNDIS if you need to connect to Windows + * but have hardware that can't support CDC Ethernet. We add descriptors + * to present the CDC Subset as a (nonconformant) CDC MDLM variant called + * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can + * get those drivers from MCCI, or bundled with various products. */ #ifdef DEV_CONFIG_CDC @@ -522,8 +545,6 @@ rndis_control_intf = { }; #endif -#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) - static const struct usb_cdc_header_desc header_desc = { .bLength = sizeof header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -532,6 +553,8 @@ static const struct usb_cdc_header_desc header_desc = { .bcdCDC = __constant_cpu_to_le16 (0x0110), }; +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) + static const struct usb_cdc_union_desc union_desc = { .bLength = sizeof union_desc, .bDescriptorType = USB_DT_CS_INTERFACE, @@ -564,7 +587,40 @@ static const struct usb_cdc_acm_descriptor acm_descriptor = { #endif -#ifdef DEV_CONFIG_CDC +#ifndef DEV_CONFIG_CDC + +/* "SAFE" loosely follows CDC WMC MDLM, violating the spec in various + * ways: data endpoints live in the control interface, there's no data + * interface, and it's not used to talk to a cell phone radio. + */ + +static const struct usb_cdc_mdlm_desc mdlm_desc = { + .bLength = sizeof mdlm_desc, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_CDC_MDLM_TYPE, + + .bcdVersion = __constant_cpu_to_le16(0x0100), + .bGUID = { + 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, + 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, + }, +}; + +/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we + * can't really use its struct. All we do here is say that we're using + * the submode of "SAFE" which directly matches the CDC Subset. + */ +static const u8 mdlm_detail_desc[] = { + 6, + USB_DT_CS_INTERFACE, + USB_CDC_MDLM_DETAIL_TYPE, + + 0, /* "SAFE" */ + 0, /* network control capabilities (none) */ + 0, /* network data capabilities ("raw" encapsulation) */ +}; + +#endif static const struct usb_cdc_ether_desc ether_desc = { .bLength = sizeof ether_desc, @@ -579,7 +635,6 @@ static const struct usb_cdc_ether_desc ether_desc = { .bNumberPowerFilters = 0, }; -#endif #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) @@ -672,6 +727,9 @@ rndis_data_intf = { /* * "Simple" CDC-subset option is a simple vendor-neutral model that most * full speed controllers can handle: one interface, two bulk endpoints. + * + * To assist host side drivers, we fancy it up a bit, and add descriptors + * so some host side drivers will understand it as a "SAFE" variant. */ static const struct usb_interface_descriptor @@ -682,8 +740,8 @@ subset_data_intf = { .bInterfaceNumber = 0, .bAlternateSetting = 0, .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = 0, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM, .bInterfaceProtocol = 0, .iInterface = STRING_DATA, }; @@ -731,10 +789,15 @@ static const struct usb_descriptor_header *fs_eth_function [11] = { static inline void __init fs_subset_descriptors(void) { #ifdef DEV_CONFIG_SUBSET + /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; - fs_eth_function[2] = (struct usb_descriptor_header *) &fs_source_desc; - fs_eth_function[3] = (struct usb_descriptor_header *) &fs_sink_desc; - fs_eth_function[4] = NULL; + fs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; + fs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; + fs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; + fs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; + fs_eth_function[6] = (struct usb_descriptor_header *) &fs_source_desc; + fs_eth_function[7] = (struct usb_descriptor_header *) &fs_sink_desc; + fs_eth_function[8] = NULL; #else fs_eth_function[1] = NULL; #endif @@ -828,10 +891,15 @@ static const struct usb_descriptor_header *hs_eth_function [11] = { static inline void __init hs_subset_descriptors(void) { #ifdef DEV_CONFIG_SUBSET + /* behavior is "CDC Subset"; extra descriptors say "SAFE" */ hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; - hs_eth_function[2] = (struct usb_descriptor_header *) &fs_source_desc; - hs_eth_function[3] = (struct usb_descriptor_header *) &fs_sink_desc; - hs_eth_function[4] = NULL; + hs_eth_function[2] = (struct usb_descriptor_header *) &header_desc; + hs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc; + hs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc; + hs_eth_function[5] = (struct usb_descriptor_header *) ðer_desc; + hs_eth_function[6] = (struct usb_descriptor_header *) &hs_source_desc; + hs_eth_function[7] = (struct usb_descriptor_header *) &hs_sink_desc; + hs_eth_function[8] = NULL; #else hs_eth_function[1] = NULL; #endif @@ -878,10 +946,8 @@ static char manufacturer [50]; static char product_desc [40] = DRIVER_DESC; static char serial_number [20]; -#ifdef DEV_CONFIG_CDC /* address that the host will use ... usually assigned at random */ static char ethaddr [2 * ETH_ALEN + 1]; -#endif /* static strings, in UTF-8 */ static struct usb_string strings [] = { @@ -889,9 +955,9 @@ static struct usb_string strings [] = { { STRING_PRODUCT, product_desc, }, { STRING_SERIALNUMBER, serial_number, }, { STRING_DATA, "Ethernet Data", }, + { STRING_ETHADDR, ethaddr, }, #ifdef DEV_CONFIG_CDC { STRING_CDC, "CDC Ethernet", }, - { STRING_ETHADDR, ethaddr, }, { STRING_CONTROL, "CDC Communications Control", }, #endif #ifdef DEV_CONFIG_SUBSET @@ -986,10 +1052,10 @@ set_ether_config (struct eth_dev *dev, gfp_t gfp_flags) } #endif - dev->in = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); + dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc); dev->in_ep->driver_data = dev; - dev->out = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); + dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc); dev->out_ep->driver_data = dev; /* With CDC, the host isn't allowed to use these two data @@ -2278,10 +2344,10 @@ eth_bind (struct usb_gadget *gadget) "RNDIS/%s", driver_desc); /* CDC subset ... recognized by Linux since 2.4.10, but Windows - * drivers aren't widely available. + * drivers aren't widely available. (That may be improved by + * supporting one submode of the "SAFE" variant of MDLM.) */ } else if (!cdc) { - device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; device_desc.idVendor = __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); device_desc.idProduct = @@ -2352,6 +2418,10 @@ autoconf_fail: if (!cdc) { eth_config.bNumInterfaces = 1; eth_config.iConfiguration = STRING_SUBSET; + + /* use functions to set these up, in case we're built to work + * with multiple controllers and must override CDC Ethernet. + */ fs_subset_descriptors(); hs_subset_descriptors(); } @@ -2415,22 +2485,20 @@ autoconf_fail: /* Module params for these addresses should come from ID proms. * The host side address is used with CDC and RNDIS, and commonly - * ends up in a persistent config database. + * ends up in a persistent config database. It's not clear if + * host side code for the SAFE thing cares -- its original BLAN + * thing didn't, Sharp never assigned those addresses on Zaurii. */ if (get_ether_addr(dev_addr, net->dev_addr)) dev_warn(&gadget->dev, "using random %s ethernet address\n", "self"); - if (cdc || rndis) { - if (get_ether_addr(host_addr, dev->host_mac)) - dev_warn(&gadget->dev, - "using random %s ethernet address\n", "host"); -#ifdef DEV_CONFIG_CDC - snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", - dev->host_mac [0], dev->host_mac [1], - dev->host_mac [2], dev->host_mac [3], - dev->host_mac [4], dev->host_mac [5]); -#endif - } + if (get_ether_addr(host_addr, dev->host_mac)) + dev_warn(&gadget->dev, + "using random %s ethernet address\n", "host"); + snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", + dev->host_mac [0], dev->host_mac [1], + dev->host_mac [2], dev->host_mac [3], + dev->host_mac [4], dev->host_mac [5]); if (rndis) { status = rndis_init(); diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 72f2ae9..f04a29a 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -253,7 +253,7 @@ #include <linux/freezer.h> #include <linux/utsname.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include "gadget_chips.h" @@ -1148,7 +1148,7 @@ static int ep0_queue(struct fsg_dev *fsg) static void ep0_complete(struct usb_ep *ep, struct usb_request *req) { - struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; + struct fsg_dev *fsg = ep->driver_data; if (req->actual > 0) dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual); @@ -1170,8 +1170,8 @@ static void ep0_complete(struct usb_ep *ep, struct usb_request *req) static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) { - struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; - struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; + struct fsg_dev *fsg = ep->driver_data; + struct fsg_buffhd *bh = req->context; if (req->status || req->actual != req->length) DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__, @@ -1190,8 +1190,8 @@ static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) { - struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; - struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; + struct fsg_dev *fsg = ep->driver_data; + struct fsg_buffhd *bh = req->context; dump_msg(fsg, "bulk-out", req->buf, req->actual); if (req->status || req->actual != bh->bulk_out_intended_length) @@ -1214,8 +1214,8 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) #ifdef CONFIG_USB_FILE_STORAGE_TEST static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) { - struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; - struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; + struct fsg_dev *fsg = ep->driver_data; + struct fsg_buffhd *bh = req->context; if (req->status || req->actual != req->length) DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__, @@ -2577,7 +2577,7 @@ static int send_status(struct fsg_dev *fsg) } if (transport_is_bbb()) { - struct bulk_cs_wrap *csw = (struct bulk_cs_wrap *) bh->buf; + struct bulk_cs_wrap *csw = bh->buf; /* Store and send the Bulk-only CSW */ csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG); @@ -2596,8 +2596,7 @@ static int send_status(struct fsg_dev *fsg) return 0; } else { // USB_PR_CBI - struct interrupt_data *buf = (struct interrupt_data *) - bh->buf; + struct interrupt_data *buf = bh->buf; /* Store and send the Interrupt data. UFI sends the ASC * and ASCQ bytes. Everything else sends a Type (which @@ -2982,7 +2981,7 @@ static int do_scsi_command(struct fsg_dev *fsg) static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) { struct usb_request *req = bh->outreq; - struct bulk_cb_wrap *cbw = (struct bulk_cb_wrap *) req->buf; + struct bulk_cb_wrap *cbw = req->buf; /* Was this a real packet? */ if (req->status) @@ -3428,7 +3427,7 @@ static void handle_exception(struct fsg_dev *fsg) static int fsg_main_thread(void *fsg_) { - struct fsg_dev *fsg = (struct fsg_dev *) fsg_; + struct fsg_dev *fsg = fsg_; /* Allow the thread to be killed by a signal, but set the signal mask * to block everything but INT, TERM, KILL, and USR1. */ @@ -3600,7 +3599,7 @@ static ssize_t show_ro(struct device *dev, struct device_attribute *attr, char * static ssize_t show_file(struct device *dev, struct device_attribute *attr, char *buf) { struct lun *curlun = dev_to_lun(dev); - struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev); + struct fsg_dev *fsg = dev_get_drvdata(dev); char *p; ssize_t rc; @@ -3629,7 +3628,7 @@ static ssize_t store_ro(struct device *dev, struct device_attribute *attr, const { ssize_t rc = count; struct lun *curlun = dev_to_lun(dev); - struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev); + struct fsg_dev *fsg = dev_get_drvdata(dev); int i; if (sscanf(buf, "%d", &i) != 1) @@ -3652,7 +3651,7 @@ static ssize_t store_ro(struct device *dev, struct device_attribute *attr, const static ssize_t store_file(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lun *curlun = dev_to_lun(dev); - struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev); + struct fsg_dev *fsg = dev_get_drvdata(dev); int rc = 0; if (curlun->prevent_medium_removal && backing_file_is_open(curlun)) { @@ -3700,7 +3699,7 @@ static void fsg_release(struct kref *ref) static void lun_release(struct device *dev) { - struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev); + struct fsg_dev *fsg = dev_get_drvdata(dev); kref_put(&fsg->ref, fsg_release); } diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index aa80f09..2e3d662 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -75,6 +75,12 @@ #define gadget_is_pxa27x(g) 0 #endif +#ifdef CONFIG_USB_GADGET_HUSB2DEV +#define gadget_is_husb2dev(g) !strcmp("husb2_udc", (g)->name) +#else +#define gadget_is_husb2dev(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_S3C2410 #define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name) #else @@ -169,5 +175,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x16; else if (gadget_is_mpc8272(gadget)) return 0x17; + else if (gadget_is_husb2dev(gadget)) + return 0x18; return -ENOENT; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index f1a67965..d08a8d0 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -35,7 +35,7 @@ #include <sound/initval.h> #include <sound/rawmidi.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include <linux/usb/audio.h> #include <linux/usb/midi.h> diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index d0ef1d6..e873cf4 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -39,7 +39,7 @@ #include <linux/interrupt.h> #include <linux/proc_fs.h> #include <linux/device.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include <asm/byteorder.h> diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 3fb1044a..34296e7 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -20,7 +20,7 @@ */ -// #define DEBUG /* data to help fault diagnosis */ +// #define DEBUG /* data to help fault diagnosis */ // #define VERBOSE /* extra debug messages (success too) */ #include <linux/init.h> @@ -59,11 +59,11 @@ * may serve as a source of device events, used to handle all control * requests other than basic enumeration. * - * - Then either immediately, or after a SET_CONFIGURATION control request, - * ep_config() is called when each /dev/gadget/ep* file is configured - * (by writing endpoint descriptors). Afterwards these files are used - * to write() IN data or to read() OUT data. To halt the endpoint, a - * "wrong direction" request is issued (like reading an IN endpoint). + * - Then, after a SET_CONFIGURATION control request, ep_config() is + * called when each /dev/gadget/ep* file is configured (by writing + * endpoint descriptors). Afterwards these files are used to write() + * IN data or to read() OUT data. To halt the endpoint, a "wrong + * direction" request is issued (like reading an IN endpoint). * * Unlike "usbfs" the only ioctl()s are for things that are rare, and maybe * not possible on all hardware. For example, precise fault handling with @@ -98,16 +98,16 @@ enum ep0_state { * must always write descriptors to initialize the device, then * the device becomes UNCONNECTED until enumeration. */ - STATE_OPENED, + STATE_DEV_OPENED, /* From then on, ep0 fd is in either of two basic modes: * - (UN)CONNECTED: read usb_gadgetfs_event(s) from it * - SETUP: read/write will transfer control data and succeed; * or if "wrong direction", performs protocol stall */ - STATE_UNCONNECTED, - STATE_CONNECTED, - STATE_SETUP, + STATE_DEV_UNCONNECTED, + STATE_DEV_CONNECTED, + STATE_DEV_SETUP, /* UNBOUND means the driver closed ep0, so the device won't be * accessible again (DEV_DISABLED) until all fds are closed. @@ -121,7 +121,7 @@ enum ep0_state { struct dev_data { spinlock_t lock; atomic_t count; - enum ep0_state state; + enum ep0_state state; /* P: lock */ struct usb_gadgetfs_event event [N_EVENT]; unsigned ev_next; struct fasync_struct *fasync; @@ -188,7 +188,6 @@ static struct dev_data *dev_new (void) enum ep_state { STATE_EP_DISABLED = 0, STATE_EP_READY, - STATE_EP_DEFER_ENABLE, STATE_EP_ENABLED, STATE_EP_UNBOUND, }; @@ -313,18 +312,10 @@ nonblock: if ((val = down_interruptible (&epdata->lock)) < 0) return val; -newstate: + switch (epdata->state) { case STATE_EP_ENABLED: break; - case STATE_EP_DEFER_ENABLE: - DBG (epdata->dev, "%s wait for host\n", epdata->name); - if ((val = wait_event_interruptible (epdata->wait, - epdata->state != STATE_EP_DEFER_ENABLE - || epdata->dev->state == STATE_DEV_UNBOUND - )) < 0) - goto fail; - goto newstate; // case STATE_EP_DISABLED: /* "can't happen" */ // case STATE_EP_READY: /* "can't happen" */ default: /* error! */ @@ -333,7 +324,6 @@ newstate: // FALLTHROUGH case STATE_EP_UNBOUND: /* clean disconnect */ val = -ENODEV; -fail: up (&epdata->lock); } return val; @@ -565,29 +555,28 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb) ssize_t len, total; int i; - /* we "retry" to get the right mm context for this: */ - - /* copy stuff into user buffers */ - total = priv->actual; - len = 0; - for (i=0; i < priv->nr_segs; i++) { - ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total); - - if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) { - if (len == 0) - len = -EFAULT; - break; - } - - total -= this; - len += this; - if (total == 0) - break; - } - kfree(priv->buf); - kfree(priv); - aio_put_req(iocb); - return len; + /* we "retry" to get the right mm context for this: */ + + /* copy stuff into user buffers */ + total = priv->actual; + len = 0; + for (i=0; i < priv->nr_segs; i++) { + ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total); + + if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) { + if (len == 0) + len = -EFAULT; + break; + } + + total -= this; + len += this; + if (total == 0) + break; + } + kfree(priv->buf); + kfree(priv); + return len; } static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) @@ -600,18 +589,17 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) spin_lock(&epdata->dev->lock); priv->req = NULL; priv->epdata = NULL; - if (priv->iv == NULL - || unlikely(req->actual == 0) - || unlikely(kiocbIsCancelled(iocb))) { + + /* if this was a write or a read returning no data then we + * don't need to copy anything to userspace, so we can + * complete the aio request immediately. + */ + if (priv->iv == NULL || unlikely(req->actual == 0)) { kfree(req->buf); kfree(priv); iocb->private = NULL; /* aio_complete() reports bytes-transferred _and_ faults */ - if (unlikely(kiocbIsCancelled(iocb))) - aio_put_req(iocb); - else - aio_complete(iocb, - req->actual ? req->actual : req->status, + aio_complete(iocb, req->actual ? req->actual : req->status, req->status); } else { /* retry() won't report both; so we hide some faults */ @@ -636,7 +624,7 @@ ep_aio_rwtail( size_t len, struct ep_data *epdata, const struct iovec *iv, - unsigned long nr_segs + unsigned long nr_segs ) { struct kiocb_priv *priv; @@ -852,9 +840,9 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) break; #endif default: - DBG (data->dev, "unconnected, %s init deferred\n", + DBG(data->dev, "unconnected, %s init abandoned\n", data->name); - data->state = STATE_EP_DEFER_ENABLE; + value = -EINVAL; } if (value == 0) { fd->f_op = &ep_io_operations; @@ -943,22 +931,24 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req) static void ep0_complete (struct usb_ep *ep, struct usb_request *req) { struct dev_data *dev = ep->driver_data; + unsigned long flags; int free = 1; /* for control OUT, data must still get to userspace */ + spin_lock_irqsave(&dev->lock, flags); if (!dev->setup_in) { dev->setup_out_error = (req->status != 0); if (!dev->setup_out_error) free = 0; dev->setup_out_ready = 1; ep0_readable (dev); - } else if (dev->state == STATE_SETUP) - dev->state = STATE_CONNECTED; + } /* clean up as appropriate */ if (free && req->buf != &dev->rbuf) clean_req (ep, req); req->complete = epio_complete; + spin_unlock_irqrestore(&dev->lock, flags); } static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len) @@ -998,13 +988,13 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) } /* control DATA stage */ - if ((state = dev->state) == STATE_SETUP) { + if ((state = dev->state) == STATE_DEV_SETUP) { if (dev->setup_in) { /* stall IN */ VDEBUG(dev, "ep0in stall\n"); (void) usb_ep_set_halt (dev->gadget->ep0); retval = -EL2HLT; - dev->state = STATE_CONNECTED; + dev->state = STATE_DEV_CONNECTED; } else if (len == 0) { /* ack SET_CONFIGURATION etc */ struct usb_ep *ep = dev->gadget->ep0; @@ -1012,7 +1002,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) if ((retval = setup_req (ep, req, 0)) == 0) retval = usb_ep_queue (ep, req, GFP_ATOMIC); - dev->state = STATE_CONNECTED; + dev->state = STATE_DEV_CONNECTED; /* assume that was SET_CONFIGURATION */ if (dev->current_config) { @@ -1040,6 +1030,13 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr) spin_lock_irq (&dev->lock); if (retval) goto done; + + if (dev->state != STATE_DEV_SETUP) { + retval = -ECANCELED; + goto done; + } + dev->state = STATE_DEV_CONNECTED; + if (dev->setup_out_error) retval = -EIO; else { @@ -1066,39 +1063,36 @@ scan: /* return queued events right away */ if (dev->ev_next != 0) { unsigned i, n; - int tmp = dev->ev_next; - len = min (len, tmp * sizeof (struct usb_gadgetfs_event)); n = len / sizeof (struct usb_gadgetfs_event); + if (dev->ev_next < n) + n = dev->ev_next; - /* ep0 can't deliver events when STATE_SETUP */ + /* ep0 i/o has special semantics during STATE_DEV_SETUP */ for (i = 0; i < n; i++) { if (dev->event [i].type == GADGETFS_SETUP) { - len = i + 1; - len *= sizeof (struct usb_gadgetfs_event); - n = 0; + dev->state = STATE_DEV_SETUP; + n = i + 1; break; } } spin_unlock_irq (&dev->lock); + len = n * sizeof (struct usb_gadgetfs_event); if (copy_to_user (buf, &dev->event, len)) retval = -EFAULT; else retval = len; if (len > 0) { - len /= sizeof (struct usb_gadgetfs_event); - /* NOTE this doesn't guard against broken drivers; * concurrent ep0 readers may lose events. */ spin_lock_irq (&dev->lock); - dev->ev_next -= len; - if (dev->ev_next != 0) - memmove (&dev->event, &dev->event [len], + if (dev->ev_next > n) { + memmove(&dev->event[0], &dev->event[n], sizeof (struct usb_gadgetfs_event) - * (tmp - len)); - if (n == 0) - dev->state = STATE_SETUP; + * (dev->ev_next - n)); + } + dev->ev_next -= n; spin_unlock_irq (&dev->lock); } return retval; @@ -1113,8 +1107,8 @@ scan: DBG (dev, "fail %s, state %d\n", __FUNCTION__, state); retval = -ESRCH; break; - case STATE_UNCONNECTED: - case STATE_CONNECTED: + case STATE_DEV_UNCONNECTED: + case STATE_DEV_CONNECTED: spin_unlock_irq (&dev->lock); DBG (dev, "%s wait\n", __FUNCTION__); @@ -1141,7 +1135,7 @@ next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type) switch (type) { /* these events purge the queue */ case GADGETFS_DISCONNECT: - if (dev->state == STATE_SETUP) + if (dev->state == STATE_DEV_SETUP) dev->setup_abort = 1; // FALL THROUGH case GADGETFS_CONNECT: @@ -1153,7 +1147,7 @@ next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type) for (i = 0; i != dev->ev_next; i++) { if (dev->event [i].type != type) continue; - DBG (dev, "discard old event %d\n", type); + DBG(dev, "discard old event[%d] %d\n", i, type); dev->ev_next--; if (i == dev->ev_next) break; @@ -1166,9 +1160,9 @@ next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type) default: BUG (); } + VDEBUG(dev, "event[%d] = %d\n", dev->ev_next, type); event = &dev->event [dev->ev_next++]; BUG_ON (dev->ev_next > N_EVENT); - VDEBUG (dev, "ev %d, next %d\n", type, dev->ev_next); memset (event, 0, sizeof *event); event->type = type; return event; @@ -1188,12 +1182,13 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) retval = -EIDRM; /* data and/or status stage for control request */ - } else if (dev->state == STATE_SETUP) { + } else if (dev->state == STATE_DEV_SETUP) { /* IN DATA+STATUS caller makes len <= wLength */ if (dev->setup_in) { retval = setup_req (dev->gadget->ep0, dev->req, len); if (retval == 0) { + dev->state = STATE_DEV_CONNECTED; spin_unlock_irq (&dev->lock); if (copy_from_user (dev->req->buf, buf, len)) retval = -EFAULT; @@ -1219,7 +1214,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) VDEBUG(dev, "ep0out stall\n"); (void) usb_ep_set_halt (dev->gadget->ep0); retval = -EL2HLT; - dev->state = STATE_CONNECTED; + dev->state = STATE_DEV_CONNECTED; } else { DBG(dev, "bogus ep0out stall!\n"); } @@ -1261,7 +1256,9 @@ dev_release (struct inode *inode, struct file *fd) put_dev (dev); /* other endpoints were all decoupled from this device */ + spin_lock_irq(&dev->lock); dev->state = STATE_DEV_DISABLED; + spin_unlock_irq(&dev->lock); return 0; } @@ -1282,7 +1279,7 @@ ep0_poll (struct file *fd, poll_table *wait) goto out; } - if (dev->state == STATE_SETUP) { + if (dev->state == STATE_DEV_SETUP) { if (dev->setup_in || dev->setup_can_stall) mask = POLLOUT; } else { @@ -1392,52 +1389,29 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) spin_lock (&dev->lock); dev->setup_abort = 0; - if (dev->state == STATE_UNCONNECTED) { - struct usb_ep *ep; - struct ep_data *data; - - dev->state = STATE_CONNECTED; - dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; - + if (dev->state == STATE_DEV_UNCONNECTED) { #ifdef CONFIG_USB_GADGET_DUALSPEED if (gadget->speed == USB_SPEED_HIGH && dev->hs_config == 0) { + spin_unlock(&dev->lock); ERROR (dev, "no high speed config??\n"); return -EINVAL; } #endif /* CONFIG_USB_GADGET_DUALSPEED */ + dev->state = STATE_DEV_CONNECTED; + dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; + INFO (dev, "connected\n"); event = next_event (dev, GADGETFS_CONNECT); event->u.speed = gadget->speed; ep0_readable (dev); - list_for_each_entry (ep, &gadget->ep_list, ep_list) { - data = ep->driver_data; - /* ... down_trylock (&data->lock) ... */ - if (data->state != STATE_EP_DEFER_ENABLE) - continue; -#ifdef CONFIG_USB_GADGET_DUALSPEED - if (gadget->speed == USB_SPEED_HIGH) - value = usb_ep_enable (ep, &data->hs_desc); - else -#endif /* CONFIG_USB_GADGET_DUALSPEED */ - value = usb_ep_enable (ep, &data->desc); - if (value) { - ERROR (dev, "deferred %s enable --> %d\n", - data->name, value); - continue; - } - data->state = STATE_EP_ENABLED; - wake_up (&data->wait); - DBG (dev, "woke up %s waiters\n", data->name); - } - /* host may have given up waiting for response. we can miss control * requests handled lower down (device/endpoint status and features); * then ep0_{read,write} will report the wrong status. controller * driver will have aborted pending i/o. */ - } else if (dev->state == STATE_SETUP) + } else if (dev->state == STATE_DEV_SETUP) dev->setup_abort = 1; req->buf = dev->rbuf; @@ -1583,7 +1557,7 @@ delegate: } /* proceed with data transfer and status phases? */ - if (value >= 0 && dev->state != STATE_SETUP) { + if (value >= 0 && dev->state != STATE_DEV_SETUP) { req->length = value; req->zero = value < w_length; value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); @@ -1747,7 +1721,9 @@ gadgetfs_bind (struct usb_gadget *gadget) goto enomem; INFO (dev, "bound to %s driver\n", gadget->name); - dev->state = STATE_UNCONNECTED; + spin_lock_irq(&dev->lock); + dev->state = STATE_DEV_UNCONNECTED; + spin_unlock_irq(&dev->lock); get_dev (dev); return 0; @@ -1762,11 +1738,9 @@ gadgetfs_disconnect (struct usb_gadget *gadget) struct dev_data *dev = get_gadget_data (gadget); spin_lock (&dev->lock); - if (dev->state == STATE_UNCONNECTED) { - DBG (dev, "already unconnected\n"); + if (dev->state == STATE_DEV_UNCONNECTED) goto exit; - } - dev->state = STATE_UNCONNECTED; + dev->state = STATE_DEV_UNCONNECTED; INFO (dev, "disconnected\n"); next_event (dev, GADGETFS_DISCONNECT); @@ -1783,9 +1757,9 @@ gadgetfs_suspend (struct usb_gadget *gadget) INFO (dev, "suspended from state %d\n", dev->state); spin_lock (&dev->lock); switch (dev->state) { - case STATE_SETUP: // VERY odd... host died?? - case STATE_CONNECTED: - case STATE_UNCONNECTED: + case STATE_DEV_SETUP: // VERY odd... host died?? + case STATE_DEV_CONNECTED: + case STATE_DEV_UNCONNECTED: next_event (dev, GADGETFS_SUSPEND); ep0_readable (dev); /* FALLTHROUGH */ @@ -1808,7 +1782,7 @@ static struct usb_gadget_driver gadgetfs_driver = { .disconnect = gadgetfs_disconnect, .suspend = gadgetfs_suspend, - .driver = { + .driver = { .name = (char *) shortname, }, }; @@ -1829,7 +1803,7 @@ static struct usb_gadget_driver probe_driver = { .unbind = gadgetfs_nop, .setup = (void *)gadgetfs_nop, .disconnect = gadgetfs_nop, - .driver = { + .driver = { .name = "nop", }, }; @@ -1849,19 +1823,16 @@ static struct usb_gadget_driver probe_driver = { * . full/low speed config ... all wTotalLength bytes (with interface, * class, altsetting, endpoint, and other descriptors) * . high speed config ... all descriptors, for high speed operation; - * this one's optional except for high-speed hardware + * this one's optional except for high-speed hardware * . device descriptor * - * Endpoints are not yet enabled. Drivers may want to immediately - * initialize them, using the /dev/gadget/ep* files that are available - * as soon as the kernel sees the configuration, or they can wait - * until device configuration and interface altsetting changes create + * Endpoints are not yet enabled. Drivers must wait until device + * configuration and interface altsetting changes create * the need to configure (or unconfigure) them. * * After initialization, the device stays active for as long as that - * $CHIP file is open. Events may then be read from that descriptor, - * such as configuration notifications. More complex drivers will handle - * some control requests in user space. + * $CHIP file is open. Events must then be read from that descriptor, + * such as configuration notifications. */ static int is_valid_config (struct usb_config_descriptor *config) @@ -1884,9 +1855,6 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) u32 tag; char *kbuf; - if (dev->state != STATE_OPENED) - return -EEXIST; - if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) return -EINVAL; @@ -1978,13 +1946,15 @@ dev_open (struct inode *inode, struct file *fd) struct dev_data *dev = inode->i_private; int value = -EBUSY; + spin_lock_irq(&dev->lock); if (dev->state == STATE_DEV_DISABLED) { dev->ev_next = 0; - dev->state = STATE_OPENED; + dev->state = STATE_DEV_OPENED; fd->private_data = dev; get_dev (dev); value = 0; } + spin_unlock_irq(&dev->lock); return value; } diff --git a/drivers/usb/gadget/lh7a40x_udc.h b/drivers/usb/gadget/lh7a40x_udc.h index e3bb785..b3fe197 100644 --- a/drivers/usb/gadget/lh7a40x_udc.h +++ b/drivers/usb/gadget/lh7a40x_udc.h @@ -49,7 +49,7 @@ #include <asm/unaligned.h> #include <asm/hardware.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> /* diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 569eb8c..7617ff7 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -63,7 +63,7 @@ #include <linux/interrupt.h> #include <linux/moduleparam.h> #include <linux/device.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include <asm/byteorder.h> diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index cdcfd42..1401043 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -38,7 +38,7 @@ #include <linux/mm.h> #include <linux/moduleparam.h> #include <linux/platform_device.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include <linux/usb/otg.h> #include <linux/dma-mapping.h> diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index b78de96..0d22536 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -56,7 +56,7 @@ #include <asm/arch/pxa-regs.h> #endif -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include <asm/arch/udc.h> diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index f8a3ec6..6c742a9 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -43,7 +43,7 @@ #include <asm/unaligned.h> #include <asm/uaccess.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb/cdc.h> #include <linux/usb_gadget.h> diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index b173576..3459ea6 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -14,7 +14,7 @@ #include <linux/device.h> #include <linux/init.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include <asm/unaligned.h> diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 40710ea..ebe04e0 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -84,7 +84,7 @@ #include <asm/system.h> #include <asm/unaligned.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #include <linux/usb_gadget.h> #include "gadget_chips.h" diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index cc60759..6271187 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -67,6 +67,11 @@ config USB_EHCI_TT_NEWSCHED If unsure, say N. +config USB_EHCI_BIG_ENDIAN_MMIO + bool + depends on USB_EHCI_HCD + default n + config USB_ISP116X_HCD tristate "ISP116X HCD support" depends on USB @@ -101,21 +106,48 @@ config USB_OHCI_HCD_PPC_SOC bool "OHCI support for on-chip PPC USB controller" depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx) default y - select USB_OHCI_BIG_ENDIAN + select USB_OHCI_BIG_ENDIAN_DESC + select USB_OHCI_BIG_ENDIAN_MMIO ---help--- Enables support for the USB controller on the MPC52xx or STB03xxx processor chip. If unsure, say Y. +config USB_OHCI_HCD_PPC_OF + bool "OHCI support for PPC USB controller on OF platform bus" + depends on USB_OHCI_HCD && PPC_OF + default y + ---help--- + Enables support for the USB controller PowerPC present on the + OpenFirmware platform bus. + +config USB_OHCI_HCD_PPC_OF_BE + bool "Support big endian HC" + depends on USB_OHCI_HCD_PPC_OF + default y + select USB_OHCI_BIG_ENDIAN_DESC + select USB_OHCI_BIG_ENDIAN_MMIO + +config USB_OHCI_HCD_PPC_OF_LE + bool "Support little endian HC" + depends on USB_OHCI_HCD_PPC_OF + default n + select USB_OHCI_LITTLE_ENDIAN + config USB_OHCI_HCD_PCI bool "OHCI support for PCI-bus USB controllers" - depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx) + depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx || USB_OHCI_HCD_PPC_OF) default y select USB_OHCI_LITTLE_ENDIAN ---help--- Enables support for PCI-bus plug-in USB controller cards. If unsure, say Y. -config USB_OHCI_BIG_ENDIAN +config USB_OHCI_BIG_ENDIAN_DESC + bool + depends on USB_OHCI_HCD + default n + +config USB_OHCI_BIG_ENDIAN_MMIO bool depends on USB_OHCI_HCD default n diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 56349d2..246afea 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -43,7 +43,7 @@ */ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) { - u32 params = readl (&ehci->caps->hcs_params); + u32 params = ehci_readl(ehci, &ehci->caps->hcs_params); ehci_dbg (ehci, "%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n", @@ -87,7 +87,7 @@ static inline void dbg_hcs_params (struct ehci_hcd *ehci, char *label) {} * */ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) { - u32 params = readl (&ehci->caps->hcc_params); + u32 params = ehci_readl(ehci, &ehci->caps->hcc_params); if (HCC_ISOC_CACHE (params)) { ehci_dbg (ehci, @@ -653,7 +653,7 @@ show_registers (struct class_device *class_dev, char *buf) } /* Capability Registers */ - i = HC_VERSION(readl (&ehci->caps->hc_capbase)); + i = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); temp = scnprintf (next, size, "bus %s, device %s (driver " DRIVER_VERSION ")\n" "%s\n" @@ -673,7 +673,7 @@ show_registers (struct class_device *class_dev, char *buf) unsigned count = 256/4; pdev = to_pci_dev(ehci_to_hcd(ehci)->self.controller); - offset = HCC_EXT_CAPS (readl (&ehci->caps->hcc_params)); + offset = HCC_EXT_CAPS (ehci_readl(ehci, &ehci->caps->hcc_params)); while (offset && count--) { pci_read_config_dword (pdev, offset, &cap); switch (cap & 0xff) { @@ -704,50 +704,50 @@ show_registers (struct class_device *class_dev, char *buf) #endif // FIXME interpret both types of params - i = readl (&ehci->caps->hcs_params); + i = ehci_readl(ehci, &ehci->caps->hcs_params); temp = scnprintf (next, size, "structural params 0x%08x\n", i); size -= temp; next += temp; - i = readl (&ehci->caps->hcc_params); + i = ehci_readl(ehci, &ehci->caps->hcc_params); temp = scnprintf (next, size, "capability params 0x%08x\n", i); size -= temp; next += temp; /* Operational Registers */ temp = dbg_status_buf (scratch, sizeof scratch, label, - readl (&ehci->regs->status)); + ehci_readl(ehci, &ehci->regs->status)); temp = scnprintf (next, size, fmt, temp, scratch); size -= temp; next += temp; temp = dbg_command_buf (scratch, sizeof scratch, label, - readl (&ehci->regs->command)); + ehci_readl(ehci, &ehci->regs->command)); temp = scnprintf (next, size, fmt, temp, scratch); size -= temp; next += temp; temp = dbg_intr_buf (scratch, sizeof scratch, label, - readl (&ehci->regs->intr_enable)); + ehci_readl(ehci, &ehci->regs->intr_enable)); temp = scnprintf (next, size, fmt, temp, scratch); size -= temp; next += temp; temp = scnprintf (next, size, "uframe %04x\n", - readl (&ehci->regs->frame_index)); + ehci_readl(ehci, &ehci->regs->frame_index)); size -= temp; next += temp; for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) { temp = dbg_port_buf (scratch, sizeof scratch, label, i, - readl (&ehci->regs->port_status [i - 1])); + ehci_readl(ehci, &ehci->regs->port_status [i - 1])); temp = scnprintf (next, size, fmt, temp, scratch); size -= temp; next += temp; if (i == HCS_DEBUG_PORT(ehci->hcs_params) && ehci->debug) { temp = scnprintf (next, size, " debug control %08x\n", - readl (&ehci->debug->control)); + ehci_readl(ehci, &ehci->debug->control)); size -= temp; next += temp; } diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 1a915e9..a524805 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -177,7 +177,7 @@ static void mpc83xx_setup_phy(struct ehci_hcd *ehci, case FSL_USB2_PHY_NONE: break; } - writel(portsc, &ehci->regs->port_status[port_offset]); + ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]); } static void mpc83xx_usb_setup(struct usb_hcd *hcd) @@ -214,7 +214,7 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd) } /* put controller in host mode. */ - writel(0x00000003, non_ehci + FSL_SOC_USB_USBMODE); + ehci_writel(ehci, 0x00000003, non_ehci + FSL_SOC_USB_USBMODE); out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c); out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040); out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001); @@ -238,12 +238,12 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(readl(&ehci->caps->hc_capbase)); + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); retval = ehci_halt(ehci); if (retval) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 025d333..185721d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -157,12 +157,13 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications"); * before driver shutdown. But it also seems to be caused by bugs in cardbus * bridge shutdown: shutting down the bridge before the devices using it. */ -static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec) +static int handshake (struct ehci_hcd *ehci, void __iomem *ptr, + u32 mask, u32 done, int usec) { u32 result; do { - result = readl (ptr); + result = ehci_readl(ehci, ptr); if (result == ~(u32)0) /* card removed */ return -ENODEV; result &= mask; @@ -177,18 +178,19 @@ static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec) /* force HC to halt state from unknown (EHCI spec section 2.3) */ static int ehci_halt (struct ehci_hcd *ehci) { - u32 temp = readl (&ehci->regs->status); + u32 temp = ehci_readl(ehci, &ehci->regs->status); /* disable any irqs left enabled by previous code */ - writel (0, &ehci->regs->intr_enable); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); if ((temp & STS_HALT) != 0) return 0; - temp = readl (&ehci->regs->command); + temp = ehci_readl(ehci, &ehci->regs->command); temp &= ~CMD_RUN; - writel (temp, &ehci->regs->command); - return handshake (&ehci->regs->status, STS_HALT, STS_HALT, 16 * 125); + ehci_writel(ehci, temp, &ehci->regs->command); + return handshake (ehci, &ehci->regs->status, + STS_HALT, STS_HALT, 16 * 125); } /* put TDI/ARC silicon into EHCI mode */ @@ -198,23 +200,24 @@ static void tdi_reset (struct ehci_hcd *ehci) u32 tmp; reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x68); - tmp = readl (reg_ptr); + tmp = ehci_readl(ehci, reg_ptr); tmp |= 0x3; - writel (tmp, reg_ptr); + ehci_writel(ehci, tmp, reg_ptr); } /* reset a non-running (STS_HALT == 1) controller */ static int ehci_reset (struct ehci_hcd *ehci) { int retval; - u32 command = readl (&ehci->regs->command); + u32 command = ehci_readl(ehci, &ehci->regs->command); command |= CMD_RESET; dbg_cmd (ehci, "reset", command); - writel (command, &ehci->regs->command); + ehci_writel(ehci, command, &ehci->regs->command); ehci_to_hcd(ehci)->state = HC_STATE_HALT; ehci->next_statechange = jiffies; - retval = handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000); + retval = handshake (ehci, &ehci->regs->command, + CMD_RESET, 0, 250 * 1000); if (retval) return retval; @@ -236,21 +239,21 @@ static void ehci_quiesce (struct ehci_hcd *ehci) #endif /* wait for any schedule enables/disables to take effect */ - temp = readl (&ehci->regs->command) << 10; + temp = ehci_readl(ehci, &ehci->regs->command) << 10; temp &= STS_ASS | STS_PSS; - if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + if (handshake (ehci, &ehci->regs->status, STS_ASS | STS_PSS, temp, 16 * 125) != 0) { ehci_to_hcd(ehci)->state = HC_STATE_HALT; return; } /* then disable anything that's still active */ - temp = readl (&ehci->regs->command); + temp = ehci_readl(ehci, &ehci->regs->command); temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE); - writel (temp, &ehci->regs->command); + ehci_writel(ehci, temp, &ehci->regs->command); /* hardware can take 16 microframes to turn off ... */ - if (handshake (&ehci->regs->status, STS_ASS | STS_PSS, + if (handshake (ehci, &ehci->regs->status, STS_ASS | STS_PSS, 0, 16 * 125) != 0) { ehci_to_hcd(ehci)->state = HC_STATE_HALT; return; @@ -277,11 +280,11 @@ static void ehci_watchdog (unsigned long param) /* lost IAA irqs wedge things badly; seen with a vt8235 */ if (ehci->reclaim) { - u32 status = readl (&ehci->regs->status); + u32 status = ehci_readl(ehci, &ehci->regs->status); if (status & STS_IAA) { ehci_vdbg (ehci, "lost IAA\n"); COUNT (ehci->stats.lost_iaa); - writel (STS_IAA, &ehci->regs->status); + ehci_writel(ehci, STS_IAA, &ehci->regs->status); ehci->reclaim_ready = 1; } } @@ -309,7 +312,7 @@ ehci_shutdown (struct usb_hcd *hcd) (void) ehci_halt (ehci); /* make BIOS/etc use companion controller during reboot */ - writel (0, &ehci->regs->configured_flag); + ehci_writel(ehci, 0, &ehci->regs->configured_flag); } static void ehci_port_power (struct ehci_hcd *ehci, int is_on) @@ -379,12 +382,13 @@ static void ehci_stop (struct usb_hcd *hcd) ehci_quiesce (ehci); ehci_reset (ehci); - writel (0, &ehci->regs->intr_enable); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); spin_unlock_irq(&ehci->lock); /* let companion controllers work when we aren't */ - writel (0, &ehci->regs->configured_flag); + ehci_writel(ehci, 0, &ehci->regs->configured_flag); + remove_companion_file(ehci); remove_debug_files (ehci); /* root hub is shut down separately (first, when possible) */ @@ -402,7 +406,8 @@ static void ehci_stop (struct usb_hcd *hcd) ehci->stats.complete, ehci->stats.unlink); #endif - dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status)); + dbg_status (ehci, "ehci_stop completed", + ehci_readl(ehci, &ehci->regs->status)); } /* one-time init, only for memory state */ @@ -428,7 +433,7 @@ static int ehci_init(struct usb_hcd *hcd) return retval; /* controllers may cache some of the periodic schedule ... */ - hcc_params = readl(&ehci->caps->hcc_params); + hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); if (HCC_ISOC_CACHE(hcc_params)) // full frame cache ehci->i_thresh = 8; else // N microframes cached @@ -496,13 +501,16 @@ static int ehci_run (struct usb_hcd *hcd) u32 temp; u32 hcc_params; + hcd->uses_new_polling = 1; + hcd->poll_rh = 0; + /* EHCI spec section 4.1 */ if ((retval = ehci_reset(ehci)) != 0) { ehci_mem_cleanup(ehci); return retval; } - writel(ehci->periodic_dma, &ehci->regs->frame_list); - writel((u32)ehci->async->qh_dma, &ehci->regs->async_next); + ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); /* * hcc_params controls whether ehci->regs->segment must (!!!) @@ -516,9 +524,9 @@ static int ehci_run (struct usb_hcd *hcd) * Scsi_Host.highmem_io, and so forth. It's readonly to all * host side drivers though. */ - hcc_params = readl(&ehci->caps->hcc_params); + hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); if (HCC_64BIT_ADDR(hcc_params)) { - writel(0, &ehci->regs->segment); + ehci_writel(ehci, 0, &ehci->regs->segment); #if 0 // this is deeply broken on almost all architectures if (!dma_set_mask(hcd->self.controller, DMA_64BIT_MASK)) @@ -531,7 +539,7 @@ static int ehci_run (struct usb_hcd *hcd) // root hub will detect new devices (why?); NEC doesn't ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); ehci->command |= CMD_RUN; - writel (ehci->command, &ehci->regs->command); + ehci_writel(ehci, ehci->command, &ehci->regs->command); dbg_cmd (ehci, "init", ehci->command); /* @@ -541,23 +549,25 @@ static int ehci_run (struct usb_hcd *hcd) * and there's no companion controller unless maybe for USB OTG.) */ hcd->state = HC_STATE_RUNNING; - writel (FLAG_CF, &ehci->regs->configured_flag); - readl (&ehci->regs->command); /* unblock posted writes */ + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ - temp = HC_VERSION(readl (&ehci->caps->hc_capbase)); + temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci_info (ehci, "USB %x.%x started, EHCI %x.%02x, driver %s%s\n", ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), temp >> 8, temp & 0xff, DRIVER_VERSION, ignore_oc ? ", overcurrent ignored" : ""); - writel (INTR_MASK, &ehci->regs->intr_enable); /* Turn On Interrupts */ + ehci_writel(ehci, INTR_MASK, + &ehci->regs->intr_enable); /* Turn On Interrupts */ /* GRR this is run-once init(), being done every time the HC starts. * So long as they're part of class devices, we can't do it init() * since the class device isn't created that early. */ create_debug_files(ehci); + create_companion_file(ehci); return 0; } @@ -567,12 +577,12 @@ static int ehci_run (struct usb_hcd *hcd) static irqreturn_t ehci_irq (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 status; + u32 status, pcd_status = 0; int bh; spin_lock (&ehci->lock); - status = readl (&ehci->regs->status); + status = ehci_readl(ehci, &ehci->regs->status); /* e.g. cardbus physical eject */ if (status == ~(u32) 0) { @@ -587,8 +597,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) } /* clear (just) interrupts */ - writel (status, &ehci->regs->status); - readl (&ehci->regs->command); /* unblock posted write */ + ehci_writel(ehci, status, &ehci->regs->status); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted write */ bh = 0; #ifdef EHCI_VERBOSE_DEBUG @@ -617,13 +627,15 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* remote wakeup [4.3.1] */ if (status & STS_PCD) { unsigned i = HCS_N_PORTS (ehci->hcs_params); + pcd_status = status; /* resume root hub? */ - if (!(readl(&ehci->regs->command) & CMD_RUN)) + if (!(ehci_readl(ehci, &ehci->regs->command) & CMD_RUN)) usb_hcd_resume_root_hub(hcd); while (i--) { - int pstatus = readl (&ehci->regs->port_status [i]); + int pstatus = ehci_readl(ehci, + &ehci->regs->port_status [i]); if (pstatus & PORT_OWNER) continue; @@ -643,14 +655,15 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { /* bogus "fatal" IRQs appear on some chips... why? */ - status = readl (&ehci->regs->status); - dbg_cmd (ehci, "fatal", readl (&ehci->regs->command)); + status = ehci_readl(ehci, &ehci->regs->status); + dbg_cmd (ehci, "fatal", ehci_readl(ehci, + &ehci->regs->command)); dbg_status (ehci, "fatal", status); if (status & STS_HALT) { ehci_err (ehci, "fatal error\n"); dead: ehci_reset (ehci); - writel (0, &ehci->regs->configured_flag); + ehci_writel(ehci, 0, &ehci->regs->configured_flag); /* generic layer kills/unlinks all urbs, then * uses ehci_stop to clean up the rest */ @@ -661,6 +674,8 @@ dead: if (bh) ehci_work (ehci); spin_unlock (&ehci->lock); + if (pcd_status & STS_PCD) + usb_hcd_poll_rh_status(hcd); return IRQ_HANDLED; } @@ -873,7 +888,8 @@ done: static int ehci_get_frame (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size; + return (ehci_readl(ehci, &ehci->regs->frame_index) >> 3) % + ehci->periodic_size; } /*-------------------------------------------------------------------------*/ @@ -899,7 +915,13 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_hcd_au1xxx_driver #endif -#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) +#ifdef CONFIG_PPC_PS3 +#include "ehci-ps3.c" +#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_sb_driver +#endif + +#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) #error "missing bus glue for ehci-hcd" #endif @@ -924,6 +946,20 @@ static int __init ehci_hcd_init(void) #ifdef PLATFORM_DRIVER platform_driver_unregister(&PLATFORM_DRIVER); #endif + return retval; + } +#endif + +#ifdef PS3_SYSTEM_BUS_DRIVER + retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) { +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +#endif +#ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); +#endif + return retval; } #endif @@ -939,6 +975,9 @@ static void __exit ehci_hcd_cleanup(void) #ifdef PCI_DRIVER pci_unregister_driver(&PCI_DRIVER); #endif +#ifdef PS3_SYSTEM_BUS_DRIVER + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); +#endif } module_exit(ehci_hcd_cleanup); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index bfe5f30..0d83c6d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -47,7 +47,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci_quiesce (ehci); hcd->state = HC_STATE_QUIESCING; } - ehci->command = readl (&ehci->regs->command); + ehci->command = ehci_readl(ehci, &ehci->regs->command); if (ehci->reclaim) ehci->reclaim_ready = 1; ehci_work(ehci); @@ -60,7 +60,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ehci->bus_suspended = 0; while (port--) { u32 __iomem *reg = &ehci->regs->port_status [port]; - u32 t1 = readl (reg) & ~PORT_RWC_BITS; + u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; u32 t2 = t1; /* keep track of which ports we suspend */ @@ -79,7 +79,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if (t1 != t2) { ehci_vdbg (ehci, "port %d, %08x -> %08x\n", port + 1, t1, t2); - writel (t2, reg); + ehci_writel(ehci, t2, reg); } } @@ -92,8 +92,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) mask = INTR_MASK; if (!device_may_wakeup(&hcd->self.root_hub->dev)) mask &= ~STS_PCD; - writel(mask, &ehci->regs->intr_enable); - readl(&ehci->regs->intr_enable); + ehci_writel(ehci, mask, &ehci->regs->intr_enable); + ehci_readl(ehci, &ehci->regs->intr_enable); ehci->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irq (&ehci->lock); @@ -118,26 +118,26 @@ static int ehci_bus_resume (struct usb_hcd *hcd) * the last user of the controller, not reset/pm hardware keeping * state we gave to it. */ - temp = readl(&ehci->regs->intr_enable); + temp = ehci_readl(ehci, &ehci->regs->intr_enable); ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); /* at least some APM implementations will try to deliver * IRQs right away, so delay them until we're ready. */ - writel(0, &ehci->regs->intr_enable); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); /* re-init operational registers */ - writel(0, &ehci->regs->segment); - writel(ehci->periodic_dma, &ehci->regs->frame_list); - writel((u32) ehci->async->qh_dma, &ehci->regs->async_next); + ehci_writel(ehci, 0, &ehci->regs->segment); + ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); /* restore CMD_RUN, framelist size, and irq threshold */ - writel (ehci->command, &ehci->regs->command); + ehci_writel(ehci, ehci->command, &ehci->regs->command); /* manually resume the ports we suspended during bus_suspend() */ i = HCS_N_PORTS (ehci->hcs_params); while (i--) { - temp = readl (&ehci->regs->port_status [i]); + temp = ehci_readl(ehci, &ehci->regs->port_status [i]); temp &= ~(PORT_RWC_BITS | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); if (test_bit(i, &ehci->bus_suspended) && @@ -145,20 +145,20 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); temp |= PORT_RESUME; } - writel (temp, &ehci->regs->port_status [i]); + ehci_writel(ehci, temp, &ehci->regs->port_status [i]); } i = HCS_N_PORTS (ehci->hcs_params); mdelay (20); while (i--) { - temp = readl (&ehci->regs->port_status [i]); + temp = ehci_readl(ehci, &ehci->regs->port_status [i]); if (test_bit(i, &ehci->bus_suspended) && (temp & PORT_SUSPEND)) { temp &= ~(PORT_RWC_BITS | PORT_RESUME); - writel (temp, &ehci->regs->port_status [i]); + ehci_writel(ehci, temp, &ehci->regs->port_status [i]); ehci_vdbg (ehci, "resumed port %d\n", i + 1); } } - (void) readl (&ehci->regs->command); + (void) ehci_readl(ehci, &ehci->regs->command); /* maybe re-activate the schedule(s) */ temp = 0; @@ -168,14 +168,14 @@ static int ehci_bus_resume (struct usb_hcd *hcd) temp |= CMD_PSE; if (temp) { ehci->command |= temp; - writel (ehci->command, &ehci->regs->command); + ehci_writel(ehci, ehci->command, &ehci->regs->command); } ehci->next_statechange = jiffies + msecs_to_jiffies(5); hcd->state = HC_STATE_RUNNING; /* Now we can safely re-enable irqs */ - writel(INTR_MASK, &ehci->regs->intr_enable); + ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); spin_unlock_irq (&ehci->lock); return 0; @@ -190,9 +190,107 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ +/* Display the ports dedicated to the companion controller */ +static ssize_t show_companion(struct class_device *class_dev, char *buf) +{ + struct ehci_hcd *ehci; + int nports, index, n; + int count = PAGE_SIZE; + char *ptr = buf; + + ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev))); + nports = HCS_N_PORTS(ehci->hcs_params); + + for (index = 0; index < nports; ++index) { + if (test_bit(index, &ehci->companion_ports)) { + n = scnprintf(ptr, count, "%d\n", index + 1); + ptr += n; + count -= n; + } + } + return ptr - buf; +} + +/* + * Dedicate or undedicate a port to the companion controller. + * Syntax is "[-]portnum", where a leading '-' sign means + * return control of the port to the EHCI controller. + */ +static ssize_t store_companion(struct class_device *class_dev, + const char *buf, size_t count) +{ + struct ehci_hcd *ehci; + int portnum, new_owner, try; + u32 __iomem *status_reg; + u32 port_status; + + ehci = hcd_to_ehci(bus_to_hcd(class_get_devdata(class_dev))); + new_owner = PORT_OWNER; /* Owned by companion */ + if (sscanf(buf, "%d", &portnum) != 1) + return -EINVAL; + if (portnum < 0) { + portnum = - portnum; + new_owner = 0; /* Owned by EHCI */ + } + if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) + return -ENOENT; + status_reg = &ehci->regs->port_status[--portnum]; + if (new_owner) + set_bit(portnum, &ehci->companion_ports); + else + clear_bit(portnum, &ehci->companion_ports); + + /* + * The controller won't set the OWNER bit if the port is + * enabled, so this loop will sometimes require at least two + * iterations: one to disable the port and one to set OWNER. + */ + + for (try = 4; try > 0; --try) { + spin_lock_irq(&ehci->lock); + port_status = ehci_readl(ehci, status_reg); + if ((port_status & PORT_OWNER) == new_owner + || (port_status & (PORT_OWNER | PORT_CONNECT)) + == 0) + try = 0; + else { + port_status ^= PORT_OWNER; + port_status &= ~(PORT_PE | PORT_RWC_BITS); + ehci_writel(ehci, port_status, status_reg); + } + spin_unlock_irq(&ehci->lock); + if (try > 1) + msleep(5); + } + return count; +} +static CLASS_DEVICE_ATTR(companion, 0644, show_companion, store_companion); + +static inline void create_companion_file(struct ehci_hcd *ehci) +{ + int i; + + /* with integrated TT there is no companion! */ + if (!ehci_is_TDI(ehci)) + i = class_device_create_file(ehci_to_hcd(ehci)->self.class_dev, + &class_device_attr_companion); +} + +static inline void remove_companion_file(struct ehci_hcd *ehci) +{ + /* with integrated TT there is no companion! */ + if (!ehci_is_TDI(ehci)) + class_device_remove_file(ehci_to_hcd(ehci)->self.class_dev, + &class_device_attr_companion); +} + + +/*-------------------------------------------------------------------------*/ + static int check_reset_complete ( struct ehci_hcd *ehci, int index, + u32 __iomem *status_reg, int port_status ) { if (!(port_status & PORT_CONNECT)) { @@ -217,7 +315,7 @@ static int check_reset_complete ( // what happens if HCS_N_CC(params) == 0 ? port_status |= PORT_OWNER; port_status &= ~PORT_RWC_BITS; - writel (port_status, &ehci->regs->port_status [index]); + ehci_writel(ehci, port_status, status_reg); } else ehci_dbg (ehci, "port %d high speed\n", index + 1); @@ -268,22 +366,21 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) /* port N changes (bit N)? */ spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < ports; i++) { - temp = readl (&ehci->regs->port_status [i]); - if (temp & PORT_OWNER) { - /* don't report this in GetPortStatus */ - if (temp & PORT_CSC) { - temp &= ~PORT_RWC_BITS; - temp |= PORT_CSC; - writel (temp, &ehci->regs->port_status [i]); - } - continue; - } + temp = ehci_readl(ehci, &ehci->regs->port_status [i]); + + /* + * Return status information even for ports with OWNER set. + * Otherwise khubd wouldn't see the disconnect event when a + * high-speed device is switched over to the companion + * controller by the user. + */ + if (!(temp & PORT_CONNECT)) ehci->reset_done [i] = 0; if ((temp & mask) != 0 || ((temp & PORT_RESUME) != 0 - && time_after (jiffies, - ehci->reset_done [i]))) { + && time_after_eq(jiffies, + ehci->reset_done[i]))) { if (i < 7) buf [0] |= 1 << (i + 1); else @@ -345,6 +442,7 @@ static int ehci_hub_control ( ) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); int ports = HCS_N_PORTS (ehci->hcs_params); + u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1]; u32 temp, status; unsigned long flags; int retval = 0; @@ -373,18 +471,22 @@ static int ehci_hub_control ( if (!wIndex || wIndex > ports) goto error; wIndex--; - temp = readl (&ehci->regs->port_status [wIndex]); - if (temp & PORT_OWNER) - break; + temp = ehci_readl(ehci, status_reg); + + /* + * Even if OWNER is set, so the port is owned by the + * companion controller, khubd needs to be able to clear + * the port-change status bits (especially + * USB_PORT_FEAT_C_CONNECTION). + */ switch (wValue) { case USB_PORT_FEAT_ENABLE: - writel (temp & ~PORT_PE, - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, temp & ~PORT_PE, status_reg); break; case USB_PORT_FEAT_C_ENABLE: - writel((temp & ~PORT_RWC_BITS) | PORT_PEC, - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_PEC, + status_reg); break; case USB_PORT_FEAT_SUSPEND: if (temp & PORT_RESET) @@ -396,8 +498,8 @@ static int ehci_hub_control ( goto error; /* resume signaling for 20 msec */ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); - writel (temp | PORT_RESUME, - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, temp | PORT_RESUME, + status_reg); ehci->reset_done [wIndex] = jiffies + msecs_to_jiffies (20); } @@ -407,16 +509,17 @@ static int ehci_hub_control ( break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) - writel (temp & ~(PORT_RWC_BITS | PORT_POWER), - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, + temp & ~(PORT_RWC_BITS | PORT_POWER), + status_reg); break; case USB_PORT_FEAT_C_CONNECTION: - writel((temp & ~PORT_RWC_BITS) | PORT_CSC, - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_CSC, + status_reg); break; case USB_PORT_FEAT_C_OVER_CURRENT: - writel((temp & ~PORT_RWC_BITS) | PORT_OCC, - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_OCC, + status_reg); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ @@ -424,7 +527,7 @@ static int ehci_hub_control ( default: goto error; } - readl (&ehci->regs->command); /* unblock posted write */ + ehci_readl(ehci, &ehci->regs->command); /* unblock posted write */ break; case GetHubDescriptor: ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) @@ -440,7 +543,7 @@ static int ehci_hub_control ( goto error; wIndex--; status = 0; - temp = readl (&ehci->regs->port_status [wIndex]); + temp = ehci_readl(ehci, status_reg); // wPortChange bits if (temp & PORT_CSC) @@ -451,42 +554,55 @@ static int ehci_hub_control ( status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; /* whoever resumes must GetPortStatus to complete it!! */ - if ((temp & PORT_RESUME) - && time_after (jiffies, - ehci->reset_done [wIndex])) { - status |= 1 << USB_PORT_FEAT_C_SUSPEND; - ehci->reset_done [wIndex] = 0; + if (temp & PORT_RESUME) { - /* stop resume signaling */ - temp = readl (&ehci->regs->port_status [wIndex]); - writel (temp & ~(PORT_RWC_BITS | PORT_RESUME), - &ehci->regs->port_status [wIndex]); - retval = handshake ( - &ehci->regs->port_status [wIndex], - PORT_RESUME, 0, 2000 /* 2msec */); - if (retval != 0) { - ehci_err (ehci, "port %d resume error %d\n", - wIndex + 1, retval); - goto error; + /* Remote Wakeup received? */ + if (!ehci->reset_done[wIndex]) { + /* resume signaling for 20 msec */ + ehci->reset_done[wIndex] = jiffies + + msecs_to_jiffies(20); + /* check the port again */ + mod_timer(&ehci_to_hcd(ehci)->rh_timer, + ehci->reset_done[wIndex]); + } + + /* resume completed? */ + else if (time_after_eq(jiffies, + ehci->reset_done[wIndex])) { + status |= 1 << USB_PORT_FEAT_C_SUSPEND; + ehci->reset_done[wIndex] = 0; + + /* stop resume signaling */ + temp = ehci_readl(ehci, status_reg); + ehci_writel(ehci, + temp & ~(PORT_RWC_BITS | PORT_RESUME), + status_reg); + retval = handshake(ehci, status_reg, + PORT_RESUME, 0, 2000 /* 2msec */); + if (retval != 0) { + ehci_err(ehci, + "port %d resume error %d\n", + wIndex + 1, retval); + goto error; + } + temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); } - temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); } /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) - && time_after (jiffies, - ehci->reset_done [wIndex])) { + && time_after_eq(jiffies, + ehci->reset_done[wIndex])) { status |= 1 << USB_PORT_FEAT_C_RESET; ehci->reset_done [wIndex] = 0; /* force reset to complete */ - writel (temp & ~(PORT_RWC_BITS | PORT_RESET), - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), + status_reg); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ - retval = handshake ( - &ehci->regs->port_status [wIndex], + retval = handshake(ehci, status_reg, PORT_RESET, 0, 750); if (retval != 0) { ehci_err (ehci, "port %d reset error %d\n", @@ -495,28 +611,41 @@ static int ehci_hub_control ( } /* see what we found out */ - temp = check_reset_complete (ehci, wIndex, - readl (&ehci->regs->port_status [wIndex])); + temp = check_reset_complete (ehci, wIndex, status_reg, + ehci_readl(ehci, status_reg)); } - // don't show wPortStatus if it's owned by a companion hc - if (!(temp & PORT_OWNER)) { - if (temp & PORT_CONNECT) { - status |= 1 << USB_PORT_FEAT_CONNECTION; - // status may be from integrated TT - status |= ehci_port_speed(ehci, temp); - } - if (temp & PORT_PE) - status |= 1 << USB_PORT_FEAT_ENABLE; - if (temp & (PORT_SUSPEND|PORT_RESUME)) - status |= 1 << USB_PORT_FEAT_SUSPEND; - if (temp & PORT_OC) - status |= 1 << USB_PORT_FEAT_OVER_CURRENT; - if (temp & PORT_RESET) - status |= 1 << USB_PORT_FEAT_RESET; - if (temp & PORT_POWER) - status |= 1 << USB_PORT_FEAT_POWER; + /* transfer dedicated ports to the companion hc */ + if ((temp & PORT_CONNECT) && + test_bit(wIndex, &ehci->companion_ports)) { + temp &= ~PORT_RWC_BITS; + temp |= PORT_OWNER; + ehci_writel(ehci, temp, status_reg); + ehci_dbg(ehci, "port %d --> companion\n", wIndex + 1); + temp = ehci_readl(ehci, status_reg); + } + + /* + * Even if OWNER is set, there's no harm letting khubd + * see the wPortStatus values (they should all be 0 except + * for PORT_POWER anyway). + */ + + if (temp & PORT_CONNECT) { + status |= 1 << USB_PORT_FEAT_CONNECTION; + // status may be from integrated TT + status |= ehci_port_speed(ehci, temp); } + if (temp & PORT_PE) + status |= 1 << USB_PORT_FEAT_ENABLE; + if (temp & (PORT_SUSPEND|PORT_RESUME)) + status |= 1 << USB_PORT_FEAT_SUSPEND; + if (temp & PORT_OC) + status |= 1 << USB_PORT_FEAT_OVER_CURRENT; + if (temp & PORT_RESET) + status |= 1 << USB_PORT_FEAT_RESET; + if (temp & PORT_POWER) + status |= 1 << USB_PORT_FEAT_POWER; #ifndef EHCI_VERBOSE_DEBUG if (status & ~0xffff) /* only if wPortChange is interesting */ @@ -541,7 +670,7 @@ static int ehci_hub_control ( if (!wIndex || wIndex > ports) goto error; wIndex--; - temp = readl (&ehci->regs->port_status [wIndex]); + temp = ehci_readl(ehci, status_reg); if (temp & PORT_OWNER) break; @@ -555,13 +684,12 @@ static int ehci_hub_control ( goto error; if (device_may_wakeup(&hcd->self.root_hub->dev)) temp |= PORT_WAKE_BITS; - writel (temp | PORT_SUSPEND, - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) - writel (temp | PORT_POWER, - &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, temp | PORT_POWER, + status_reg); break; case USB_PORT_FEAT_RESET: if (temp & PORT_RESUME) @@ -589,7 +717,7 @@ static int ehci_hub_control ( ehci->reset_done [wIndex] = jiffies + msecs_to_jiffies (50); } - writel (temp, &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, temp, status_reg); break; /* For downstream facing ports (these): one hub port is put @@ -604,13 +732,13 @@ static int ehci_hub_control ( ehci_quiesce(ehci); ehci_halt(ehci); temp |= selector << 16; - writel (temp, &ehci->regs->port_status [wIndex]); + ehci_writel(ehci, temp, status_reg); break; default: goto error; } - readl (&ehci->regs->command); /* unblock posted writes */ + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ break; default: diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 4bc7970..12edc72 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -38,7 +38,7 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) if ((temp & (3 << 13)) == (1 << 13)) { temp &= 0x1fff; ehci->debug = ehci_to_hcd(ehci)->regs + temp; - temp = readl(&ehci->debug->control); + temp = ehci_readl(ehci, &ehci->debug->control); ehci_info(ehci, "debug port %d%s\n", HCS_DEBUG_PORT(ehci->hcs_params), (temp & DBGP_ENABLED) @@ -71,8 +71,24 @@ static int ehci_pci_setup(struct usb_hcd *hcd) u32 temp; int retval; + switch (pdev->vendor) { + case PCI_VENDOR_ID_TOSHIBA_2: + /* celleb's companion chip */ + if (pdev->device == 0x01b5) { +#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO + ehci->big_endian_mmio = 1; +#else + ehci_warn(ehci, + "unsupported big endian Toshiba quirk\n"); +#endif + } + break; + } + ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); @@ -101,7 +117,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) } /* cache this readonly data; minimize chip reads */ - ehci->hcs_params = readl(&ehci->caps->hcs_params); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); retval = ehci_halt(ehci); if (retval) @@ -235,8 +251,8 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message) rc = -EINVAL; goto bail; } - writel (0, &ehci->regs->intr_enable); - (void)readl(&ehci->regs->intr_enable); + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + (void)ehci_readl(ehci, &ehci->regs->intr_enable); /* make sure snapshot being resumed re-enumerates everything */ if (message.event == PM_EVENT_PRETHAW) { @@ -270,13 +286,13 @@ static int ehci_pci_resume(struct usb_hcd *hcd) /* If CF is still set, we maintained PCI Vaux power. * Just undo the effect of ehci_pci_suspend(). */ - if (readl(&ehci->regs->configured_flag) == FLAG_CF) { + if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { int mask = INTR_MASK; if (!device_may_wakeup(&hcd->self.root_hub->dev)) mask &= ~STS_PCD; - writel(mask, &ehci->regs->intr_enable); - readl(&ehci->regs->intr_enable); + ehci_writel(ehci, mask, &ehci->regs->intr_enable); + ehci_readl(ehci, &ehci->regs->intr_enable); return 0; } @@ -300,9 +316,9 @@ static int ehci_pci_resume(struct usb_hcd *hcd) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - writel(ehci->command, &ehci->regs->command); - writel(FLAG_CF, &ehci->regs->configured_flag); - readl(&ehci->regs->command); /* unblock posted writes */ + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ hcd->state = HC_STATE_SUSPENDED; return 0; diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c new file mode 100644 index 0000000..371f194 --- /dev/null +++ b/drivers/usb/host/ehci-ps3.c @@ -0,0 +1,193 @@ +/* + * PS3 EHCI Host Controller driver + * + * Copyright (C) 2006 Sony Computer Entertainment Inc. + * Copyright 2006 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#include <asm/ps3.h> + +static int ps3_ehci_hc_reset(struct usb_hcd *hcd) +{ + int result; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + ehci->big_endian_mmio = 1; + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + &ehci->caps->hc_capbase)); + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + result = ehci_halt(ehci); + + if (result) + return result; + + result = ehci_init(hcd); + + if (result) + return result; + + ehci_port_power(ehci, 0); + + return result; +} + +static const struct hc_driver ps3_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "PS3 EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + .reset = ps3_ehci_hc_reset, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .get_frame_number = ehci_get_frame, + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#if defined(CONFIG_PM) + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif +}; + +#if !defined(DEBUG) +#undef dev_dbg +static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg( + const struct device *_dev, const char *fmt, ...) {return 0;} +#endif + + +static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev) +{ + int result; + struct usb_hcd *hcd; + unsigned int virq; + static u64 dummy_mask = DMA_32BIT_MASK; + + if (usb_disabled()) { + result = -ENODEV; + goto fail_start; + } + + result = ps3_mmio_region_create(dev->m_region); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n", + __func__, __LINE__); + result = -EPERM; + goto fail_mmio; + } + + dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__, + __LINE__, dev->m_region->lpar_addr); + + result = ps3_alloc_io_irq(dev->interrupt_id, &virq); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n", + __func__, __LINE__, virq); + result = -EPERM; + goto fail_irq; + } + + dev->core.power.power_state = PMSG_ON; + dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */ + + hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev->core.bus_id); + + if (!hcd) { + dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__, + __LINE__); + result = -ENOMEM; + goto fail_create_hcd; + } + + hcd->rsrc_start = dev->m_region->lpar_addr; + hcd->rsrc_len = dev->m_region->len; + hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len); + + if (!hcd->regs) { + dev_dbg(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); + result = -EPERM; + goto fail_ioremap; + } + + dev_dbg(&dev->core, "%s:%d: hcd->rsrc_start %lxh\n", __func__, __LINE__, + (unsigned long)hcd->rsrc_start); + dev_dbg(&dev->core, "%s:%d: hcd->rsrc_len %lxh\n", __func__, __LINE__, + (unsigned long)hcd->rsrc_len); + dev_dbg(&dev->core, "%s:%d: hcd->regs %lxh\n", __func__, __LINE__, + (unsigned long)hcd->regs); + dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__, + (unsigned long)virq); + + ps3_system_bus_set_driver_data(dev, hcd); + + result = usb_add_hcd(hcd, virq, IRQF_DISABLED); + + if (result) { + dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n", + __func__, __LINE__, result); + goto fail_add_hcd; + } + + return result; + +fail_add_hcd: + iounmap(hcd->regs); +fail_ioremap: + usb_put_hcd(hcd); +fail_create_hcd: + ps3_free_io_irq(virq); +fail_irq: + ps3_free_mmio_region(dev->m_region); +fail_mmio: +fail_start: + return result; +} + +static int ps3_ehci_sb_remove(struct ps3_system_bus_device *dev) +{ + struct usb_hcd *hcd = + (struct usb_hcd *)ps3_system_bus_get_driver_data(dev); + + usb_put_hcd(hcd); + ps3_system_bus_set_driver_data(dev, NULL); + + return 0; +} + +MODULE_ALIAS("ps3-ehci"); + +static struct ps3_system_bus_driver ps3_ehci_sb_driver = { + .match_id = PS3_MATCH_ID_EHCI, + .core = { + .name = "ps3-ehci-driver", + }, + .probe = ps3_ehci_sb_probe, + .remove = ps3_ehci_sb_remove, +}; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 62e46dc..e7fbbd0 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -789,13 +789,14 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) head = ehci->async; timer_action_done (ehci, TIMER_ASYNC_OFF); if (!head->qh_next.qh) { - u32 cmd = readl (&ehci->regs->command); + u32 cmd = ehci_readl(ehci, &ehci->regs->command); if (!(cmd & CMD_ASE)) { /* in case a clear of CMD_ASE didn't take yet */ - (void) handshake (&ehci->regs->status, STS_ASS, 0, 150); + (void)handshake(ehci, &ehci->regs->status, + STS_ASS, 0, 150); cmd |= CMD_ASE | CMD_RUN; - writel (cmd, &ehci->regs->command); + ehci_writel(ehci, cmd, &ehci->regs->command); ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* posted write need not be known to HC yet ... */ } @@ -1007,7 +1008,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { - int cmd = readl (&ehci->regs->command); + int cmd = ehci_readl(ehci, &ehci->regs->command); struct ehci_qh *prev; #ifdef DEBUG @@ -1025,7 +1026,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) if (ehci_to_hcd(ehci)->state != HC_STATE_HALT && !ehci->reclaim) { /* ... and CMD_IAAD clear */ - writel (cmd & ~CMD_ASE, &ehci->regs->command); + ehci_writel(ehci, cmd & ~CMD_ASE, + &ehci->regs->command); wmb (); // handshake later, if we need to timer_action_done (ehci, TIMER_ASYNC_OFF); @@ -1054,8 +1056,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) ehci->reclaim_ready = 0; cmd |= CMD_IAAD; - writel (cmd, &ehci->regs->command); - (void) readl (&ehci->regs->command); + ehci_writel(ehci, cmd, &ehci->regs->command); + (void)ehci_readl(ehci, &ehci->regs->command); timer_action (ehci, TIMER_IAA_WATCHDOG); } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 65c402a..7b5ae71 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -433,20 +433,20 @@ static int enable_periodic (struct ehci_hcd *ehci) /* did clearing PSE did take effect yet? * takes effect only at frame boundaries... */ - status = handshake (&ehci->regs->status, STS_PSS, 0, 9 * 125); + status = handshake(ehci, &ehci->regs->status, STS_PSS, 0, 9 * 125); if (status != 0) { ehci_to_hcd(ehci)->state = HC_STATE_HALT; return status; } - cmd = readl (&ehci->regs->command) | CMD_PSE; - writel (cmd, &ehci->regs->command); + cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE; + ehci_writel(ehci, cmd, &ehci->regs->command); /* posted write ... PSS happens later */ ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* make sure ehci_work scans these */ - ehci->next_uframe = readl (&ehci->regs->frame_index) - % (ehci->periodic_size << 3); + ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) + % (ehci->periodic_size << 3); return 0; } @@ -458,14 +458,14 @@ static int disable_periodic (struct ehci_hcd *ehci) /* did setting PSE not take effect yet? * takes effect only at frame boundaries... */ - status = handshake (&ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); + status = handshake(ehci, &ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); if (status != 0) { ehci_to_hcd(ehci)->state = HC_STATE_HALT; return status; } - cmd = readl (&ehci->regs->command) & ~CMD_PSE; - writel (cmd, &ehci->regs->command); + cmd = ehci_readl(ehci, &ehci->regs->command) & ~CMD_PSE; + ehci_writel(ehci, cmd, &ehci->regs->command); /* posted write ... */ ehci->next_uframe = -1; @@ -1336,7 +1336,7 @@ iso_stream_schedule ( goto fail; } - now = readl (&ehci->regs->frame_index) % mod; + now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; /* when's the last uframe this urb could start? */ max = now + mod; @@ -2088,7 +2088,7 @@ scan_periodic (struct ehci_hcd *ehci) */ now_uframe = ehci->next_uframe; if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) - clock = readl (&ehci->regs->frame_index); + clock = ehci_readl(ehci, &ehci->regs->frame_index); else clock = now_uframe + mod - 1; clock %= mod; @@ -2213,7 +2213,7 @@ restart: if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) break; ehci->next_uframe = now_uframe; - now = readl (&ehci->regs->frame_index) % mod; + now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; if (now_uframe == now) break; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 74dbc6c..ec0da03 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -74,7 +74,11 @@ struct ehci_hcd { /* one per controller */ /* per root hub port */ unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; - unsigned long bus_suspended; + /* bit vectors (one bit per port) */ + unsigned long bus_suspended; /* which ports were + already suspended at the start of a bus suspend */ + unsigned long companion_ports; /* which ports are + dedicated to the companion controller */ /* per-HC memory pools (could be per-bus, but ...) */ struct dma_pool *qh_pool; /* qh per active urb */ @@ -92,6 +96,7 @@ struct ehci_hcd { /* one per controller */ unsigned is_tdi_rh_tt:1; /* TDI roothub with TT */ unsigned no_selective_suspend:1; unsigned has_fsl_port_bug:1; /* FreeScale */ + unsigned big_endian_mmio:1; u8 sbrn; /* packed release number */ @@ -651,6 +656,45 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) #define ehci_has_fsl_portno_bug(e) (0) #endif +/* + * While most USB host controllers implement their registers in + * little-endian format, a minority (celleb companion chip) implement + * them in big endian format. + * + * This attempts to support either format at compile time without a + * runtime penalty, or both formats with the additional overhead + * of checking a flag bit. + */ + +#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO +#define ehci_big_endian_mmio(e) ((e)->big_endian_mmio) +#else +#define ehci_big_endian_mmio(e) 0 +#endif + +static inline unsigned int ehci_readl (const struct ehci_hcd *ehci, + __u32 __iomem * regs) +{ +#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO + return ehci_big_endian_mmio(ehci) ? + readl_be((__force u32 *)regs) : + readl((__force u32 *)regs); +#else + return readl((__force u32 *)regs); +#endif +} + +static inline void ehci_writel (const struct ehci_hcd *ehci, + const unsigned int val, __u32 __iomem *regs) +{ +#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO + ehci_big_endian_mmio(ehci) ? + writel_be(val, (__force u32 *)regs) : + writel(val, (__force u32 *)regs); +#else + writel(val, (__force u32 *)regs); +#endif +} /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index cc40551..9303464 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -170,7 +170,6 @@ static int usb_hcd_at91_remove(struct usb_hcd *hcd, at91_stop_hc(pdev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - disable_irq_wake(hcd->irq); clk_put(fclk); clk_put(iclk); @@ -271,8 +270,6 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) if (device_may_wakeup(&pdev->dev)) enable_irq_wake(hcd->irq); - else - disable_irq_wake(hcd->irq); /* * The integrated transceivers seem unable to notice disconnect, @@ -293,6 +290,11 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) { + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(hcd->irq); + if (!clocked) { clk_enable(iclk); clk_enable(fclk); @@ -320,18 +322,3 @@ static struct platform_driver ohci_hcd_at91_driver = { }, }; -static int __init ohci_hcd_at91_init (void) -{ - if (usb_disabled()) - return -ENODEV; - - return platform_driver_register(&ohci_hcd_at91_driver); -} - -static void __exit ohci_hcd_at91_cleanup (void) -{ - platform_driver_unregister(&ohci_hcd_at91_driver); -} - -module_init (ohci_hcd_at91_init); -module_exit (ohci_hcd_at91_cleanup); diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index e70b243..663a060 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -345,19 +345,3 @@ static struct platform_driver ohci_hcd_au1xxx_driver = { }, }; -static int __init ohci_hcd_au1xxx_init (void) -{ - pr_debug (DRIVER_INFO " (Au1xxx)"); - pr_debug ("block sizes: ed %d td %d\n", - sizeof (struct ed), sizeof (struct td)); - - return platform_driver_register(&ohci_hcd_au1xxx_driver); -} - -static void __exit ohci_hcd_au1xxx_cleanup (void) -{ - platform_driver_unregister(&ohci_hcd_au1xxx_driver); -} - -module_init (ohci_hcd_au1xxx_init); -module_exit (ohci_hcd_au1xxx_cleanup); diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 3348b07..44c60fba 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -214,15 +214,3 @@ static struct platform_driver ohci_hcd_ep93xx_driver = { }, }; -static int __init ohci_hcd_ep93xx_init(void) -{ - return platform_driver_register(&ohci_hcd_ep93xx_driver); -} - -static void __exit ohci_hcd_ep93xx_cleanup(void) -{ - platform_driver_unregister(&ohci_hcd_ep93xx_driver); -} - -module_init(ohci_hcd_ep93xx_init); -module_exit(ohci_hcd_ep93xx_cleanup); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index c1c1d87..fa6a7ce 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -855,63 +855,167 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_PCI #include "ohci-pci.c" +#define PCI_DRIVER ohci_pci_driver #endif #ifdef CONFIG_SA1111 #include "ohci-sa1111.c" +#define SA1111_DRIVER ohci_hcd_sa1111_driver #endif #ifdef CONFIG_ARCH_S3C2410 #include "ohci-s3c2410.c" +#define PLATFORM_DRIVER ohci_hcd_s3c2410_driver #endif #ifdef CONFIG_ARCH_OMAP #include "ohci-omap.c" +#define PLATFORM_DRIVER ohci_hcd_omap_driver #endif #ifdef CONFIG_ARCH_LH7A404 #include "ohci-lh7a404.c" +#define PLATFORM_DRIVER ohci_hcd_lh7a404_driver #endif #ifdef CONFIG_PXA27x #include "ohci-pxa27x.c" +#define PLATFORM_DRIVER ohci_hcd_pxa27x_driver #endif #ifdef CONFIG_ARCH_EP93XX #include "ohci-ep93xx.c" +#define PLATFORM_DRIVER ohci_hcd_ep93xx_driver #endif #ifdef CONFIG_SOC_AU1X00 #include "ohci-au1xxx.c" +#define PLATFORM_DRIVER ohci_hcd_au1xxx_driver #endif #ifdef CONFIG_PNX8550 #include "ohci-pnx8550.c" +#define PLATFORM_DRIVER ohci_hcd_pnx8550_driver #endif #ifdef CONFIG_USB_OHCI_HCD_PPC_SOC #include "ohci-ppc-soc.c" +#define PLATFORM_DRIVER ohci_hcd_ppc_soc_driver #endif #ifdef CONFIG_ARCH_AT91 #include "ohci-at91.c" +#define PLATFORM_DRIVER ohci_hcd_at91_driver #endif #ifdef CONFIG_ARCH_PNX4008 #include "ohci-pnx4008.c" +#define PLATFORM_DRIVER usb_hcd_pnx4008_driver #endif -#if !(defined(CONFIG_PCI) \ - || defined(CONFIG_SA1111) \ - || defined(CONFIG_ARCH_S3C2410) \ - || defined(CONFIG_ARCH_OMAP) \ - || defined (CONFIG_ARCH_LH7A404) \ - || defined (CONFIG_PXA27x) \ - || defined (CONFIG_ARCH_EP93XX) \ - || defined (CONFIG_SOC_AU1X00) \ - || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \ - || defined (CONFIG_ARCH_AT91) \ - || defined (CONFIG_ARCH_PNX4008) \ - ) + +#ifdef CONFIG_USB_OHCI_HCD_PPC_OF +#include "ohci-ppc-of.c" +#define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver +#endif + +#ifdef CONFIG_PPC_PS3 +#include "ohci-ps3.c" +#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_sb_driver +#endif + +#if !defined(PCI_DRIVER) && \ + !defined(PLATFORM_DRIVER) && \ + !defined(OF_PLATFORM_DRIVER) && \ + !defined(SA1111_DRIVER) && \ + !defined(PS3_SYSTEM_BUS_DRIVER) #error "missing bus glue for ohci-hcd" #endif + +static int __init ohci_hcd_mod_init(void) +{ + int retval = 0; + + if (usb_disabled()) + return -ENODEV; + + printk (KERN_DEBUG "%s: " DRIVER_INFO "\n", hcd_name); + pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name, + sizeof (struct ed), sizeof (struct td)); + +#ifdef PS3_SYSTEM_BUS_DRIVER + retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) + goto error_ps3; +#endif + +#ifdef PLATFORM_DRIVER + retval = platform_driver_register(&PLATFORM_DRIVER); + if (retval < 0) + goto error_platform; +#endif + +#ifdef OF_PLATFORM_DRIVER + retval = of_register_platform_driver(&OF_PLATFORM_DRIVER); + if (retval < 0) + goto error_of_platform; +#endif + +#ifdef SA1111_DRIVER + retval = sa1111_driver_register(&SA1111_DRIVER); + if (retval < 0) + goto error_sa1111; +#endif + +#ifdef PCI_DRIVER + retval = pci_register_driver(&PCI_DRIVER); + if (retval < 0) + goto error_pci; +#endif + + return retval; + + /* Error path */ +#ifdef PCI_DRIVER + error_pci: +#endif +#ifdef SA1111_DRIVER + sa1111_driver_unregister(&SA1111_DRIVER); + error_sa1111: +#endif +#ifdef OF_PLATFORM_DRIVER + of_unregister_platform_driver(&OF_PLATFORM_DRIVER); + error_of_platform: +#endif +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); + error_platform: +#endif +#ifdef PS3_SYSTEM_BUS_DRIVER + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + error_ps3: +#endif + return retval; +} +module_init(ohci_hcd_mod_init); + +static void __exit ohci_hcd_mod_exit(void) +{ +#ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); +#endif +#ifdef SA1111_DRIVER + sa1111_driver_unregister(&SA1111_DRIVER); +#endif +#ifdef OF_PLATFORM_DRIVER + of_unregister_platform_driver(&OF_PLATFORM_DRIVER); +#endif +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +#endif +#ifdef PS3_SYSTEM_BUS_DRIVER + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); +#endif +} +module_exit(ohci_hcd_mod_exit); + diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c index e9807cf..4a043ab 100644 --- a/drivers/usb/host/ohci-lh7a404.c +++ b/drivers/usb/host/ohci-lh7a404.c @@ -251,19 +251,3 @@ static struct platform_driver ohci_hcd_lh7a404_driver = { }, }; -static int __init ohci_hcd_lh7a404_init (void) -{ - pr_debug (DRIVER_INFO " (LH7A404)"); - pr_debug ("block sizes: ed %d td %d\n", - sizeof (struct ed), sizeof (struct td)); - - return platform_driver_register(&ohci_hcd_lh7a404_driver); -} - -static void __exit ohci_hcd_lh7a404_cleanup (void) -{ - platform_driver_unregister(&ohci_hcd_lh7a404_driver); -} - -module_init (ohci_hcd_lh7a404_init); -module_exit (ohci_hcd_lh7a404_cleanup); diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 27be1f9..5cfa3d1 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -544,22 +544,3 @@ static struct platform_driver ohci_hcd_omap_driver = { }, }; -static int __init ohci_hcd_omap_init (void) -{ - printk (KERN_DEBUG "%s: " DRIVER_INFO " (OMAP)\n", hcd_name); - if (usb_disabled()) - return -ENODEV; - - pr_debug("%s: block sizes: ed %Zd td %Zd\n", hcd_name, - sizeof (struct ed), sizeof (struct td)); - - return platform_driver_register(&ohci_hcd_omap_driver); -} - -static void __exit ohci_hcd_omap_cleanup (void) -{ - platform_driver_unregister(&ohci_hcd_omap_driver); -} - -module_init (ohci_hcd_omap_init); -module_exit (ohci_hcd_omap_cleanup); diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 596e0b4..b331ac4 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -20,79 +20,154 @@ /*-------------------------------------------------------------------------*/ -static int -ohci_pci_reset (struct usb_hcd *hcd) +/* AMD 756, for most chips (early revs), corrupts register + * values on read ... so enable the vendor workaround. + */ +static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci_hcd_init (ohci); - return ohci_init (ohci); + ohci->flags = OHCI_QUIRK_AMD756; + ohci_dbg (ohci, "AMD756 erratum 4 workaround\n"); + + /* also erratum 10 (suspend/resume issues) */ + device_init_wakeup(&hcd->self.root_hub->dev, 0); + + return 0; } -static int __devinit -ohci_pci_start (struct usb_hcd *hcd) +/* 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 ...) + */ +static int __devinit ohci_quirk_opti(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - int ret; - /* REVISIT this whole block should move to reset(), which handles - * all the other one-time init. + ohci_dbg (ohci, "WARNING: OPTi workarounds unavailable\n"); + + return 0; +} + +/* 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. + */ +static int __devinit ohci_quirk_ns(struct usb_hcd *hcd) +{ + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + struct pci_dev *b; + + b = pci_get_slot (pdev->bus, PCI_DEVFN (PCI_SLOT (pdev->devfn), 1)); + if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO + && b->vendor == PCI_VENDOR_ID_NS) { + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + ohci->flags |= OHCI_QUIRK_SUPERIO; + ohci_dbg (ohci, "Using NSC SuperIO setup\n"); + } + pci_dev_put(b); + + return 0; +} + +/* Check for Compaq's ZFMicro chipset, which needs short + * delays before control or bulk queues get re-activated + * in finish_unlinks() + */ +static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + ohci->flags |= OHCI_QUIRK_ZFMICRO; + ohci_dbg (ohci, "enabled Compaq ZFMicro chipset quirk\n"); + + return 0; +} + +/* Check for Toshiba SCC OHCI which has big endian registers + * and little endian in memory data structures + */ +static int __devinit ohci_quirk_toshiba_scc(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + /* That chip is only present in the southbridge of some + * cell based platforms which are supposed to select + * CONFIG_USB_OHCI_BIG_ENDIAN_MMIO. We verify here if + * that was the case though. + */ +#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO + ohci->flags |= OHCI_QUIRK_BE_MMIO; + ohci_dbg (ohci, "enabled big endian Toshiba quirk\n"); + return 0; +#else + ohci_err (ohci, "unsupported big endian Toshiba quirk\n"); + return -ENXIO; +#endif +} + +/* List of quirks for OHCI */ +static const struct pci_device_id ohci_pci_quirks[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x740c), + .driver_data = (unsigned long)ohci_quirk_amd756, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_OPTI, 0xc861), + .driver_data = (unsigned long)ohci_quirk_opti, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_ANY_ID), + .driver_data = (unsigned long)ohci_quirk_ns, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xa0f8), + .driver_data = (unsigned long)ohci_quirk_zfmicro, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6), + .driver_data = (unsigned long)ohci_quirk_toshiba_scc, + }, + /* FIXME for some of the early AMD 760 southbridges, OHCI + * won't work at all. blacklist them. */ + + {}, +}; + +static int ohci_pci_reset (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret = 0; + if (hcd->self.controller) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + const struct pci_device_id *quirk_id; - /* AMD 756, for most chips (early revs), corrupts register - * values on read ... so enable the vendor workaround. - */ - if (pdev->vendor == PCI_VENDOR_ID_AMD - && pdev->device == 0x740c) { - ohci->flags = OHCI_QUIRK_AMD756; - ohci_dbg (ohci, "AMD756 erratum 4 workaround\n"); - /* also erratum 10 (suspend/resume issues) */ - device_init_wakeup(&hcd->self.root_hub->dev, 0); + quirk_id = pci_match_id(ohci_pci_quirks, pdev); + if (quirk_id != NULL) { + int (*quirk)(struct usb_hcd *ohci); + quirk = (void *)quirk_id->driver_data; + ret = quirk(hcd); } + } + if (ret == 0) { + ohci_hcd_init (ohci); + return ohci_init (ohci); + } + return ret; +} - /* 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 (pdev->vendor == PCI_VENDOR_ID_OPTI - && pdev->device == 0xc861) { - ohci_dbg (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 (pdev->vendor == PCI_VENDOR_ID_NS) { - struct pci_dev *b; - - b = pci_get_slot (pdev->bus, - PCI_DEVFN (PCI_SLOT (pdev->devfn), 1)); - if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO - && b->vendor == PCI_VENDOR_ID_NS) { - ohci->flags |= OHCI_QUIRK_SUPERIO; - ohci_dbg (ohci, "Using NSC SuperIO setup\n"); - } - pci_dev_put(b); - } +static int __devinit ohci_pci_start (struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; - /* Check for Compaq's ZFMicro chipset, which needs short - * delays before control or bulk queues get re-activated - * in finish_unlinks() - */ - else if (pdev->vendor == PCI_VENDOR_ID_COMPAQ - && pdev->device == 0xa0f8) { - ohci->flags |= OHCI_QUIRK_ZFMICRO; - ohci_dbg (ohci, - "enabled Compaq ZFMicro chipset quirk\n"); - } +#ifdef CONFIG_PM /* avoid warnings about unused pdev */ + if (hcd->self.controller) { + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); /* RWC may not be set for add-in PCI cards, since boot * firmware probably ignored them. This transfers PCI @@ -101,16 +176,14 @@ ohci_pci_start (struct usb_hcd *hcd) if (device_may_wakeup(&pdev->dev)) ohci->hc_control |= OHCI_CTRL_RWC; } +#endif /* CONFIG_PM */ - /* NOTE: there may have already been a first reset, to - * keep bios/smm irqs from making trouble - */ - if ((ret = ohci_run (ohci)) < 0) { + ret = ohci_run (ohci); + if (ret < 0) { ohci_err (ohci, "can't start\n"); ohci_stop (hcd); - return ret; } - return 0; + return ret; } #ifdef CONFIG_PM @@ -238,23 +311,3 @@ static struct pci_driver ohci_pci_driver = { .shutdown = usb_hcd_pci_shutdown, }; - -static int __init ohci_hcd_pci_init (void) -{ - printk (KERN_DEBUG "%s: " DRIVER_INFO " (PCI)\n", hcd_name); - if (usb_disabled()) - return -ENODEV; - - pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name, - sizeof (struct ed), sizeof (struct td)); - return pci_register_driver (&ohci_pci_driver); -} -module_init (ohci_hcd_pci_init); - -/*-------------------------------------------------------------------------*/ - -static void __exit ohci_hcd_pci_cleanup (void) -{ - pci_unregister_driver (&ohci_pci_driver); -} -module_exit (ohci_hcd_pci_cleanup); diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 3a8cbfb..893b172 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -465,15 +465,3 @@ static struct platform_driver usb_hcd_pnx4008_driver = { .remove = usb_hcd_pnx4008_remove, }; -static int __init usb_hcd_pnx4008_init(void) -{ - return platform_driver_register(&usb_hcd_pnx4008_driver); -} - -static void __exit usb_hcd_pnx4008_cleanup(void) -{ - return platform_driver_unregister(&usb_hcd_pnx4008_driver); -} - -module_init(usb_hcd_pnx4008_init); -module_exit(usb_hcd_pnx4008_cleanup); diff --git a/drivers/usb/host/ohci-pnx8550.c b/drivers/usb/host/ohci-pnx8550.c index 6922b91b..de45eb0 100644 --- a/drivers/usb/host/ohci-pnx8550.c +++ b/drivers/usb/host/ohci-pnx8550.c @@ -240,19 +240,3 @@ static struct platform_driver ohci_hcd_pnx8550_driver = { .remove = ohci_hcd_pnx8550_drv_remove, }; -static int __init ohci_hcd_pnx8550_init (void) -{ - pr_debug (DRIVER_INFO " (pnx8550)"); - pr_debug ("block sizes: ed %d td %d\n", - sizeof (struct ed), sizeof (struct td)); - - return platform_driver_register(&ohci_hcd_pnx8550_driver); -} - -static void __exit ohci_hcd_pnx8550_cleanup (void) -{ - platform_driver_unregister(&ohci_hcd_pnx8550_driver); -} - -module_init (ohci_hcd_pnx8550_init); -module_exit (ohci_hcd_pnx8550_cleanup); diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c new file mode 100644 index 0000000..08e237c --- /dev/null +++ b/drivers/usb/host/ohci-ppc-of.c @@ -0,0 +1,232 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> + * (C) Copyright 2002 Hewlett-Packard Company + * (C) Copyright 2006 Sylvain Munaut <tnt@246tNt.com> + * + * Bus glue for OHCI HC on the of_platform bus + * + * Modified for of_platform bus from ohci-sa1111.c + * + * This file is licenced under the GPL. + */ + +#include <linux/signal.h> + +#include <asm/of_platform.h> +#include <asm/prom.h> + + +static int __devinit +ohci_ppc_of_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + if ((ret = ohci_run(ohci)) < 0) { + err("can't start %s", ohci_to_hcd(ohci)->self.bus_name); + ohci_stop(hcd); + return ret; + } + + return 0; +} + +static const struct hc_driver ohci_ppc_of_hc_driver = { + .description = hcd_name, + .product_desc = "OF OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .start = ohci_ppc_of_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * 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, + .hub_irq_enable = ohci_rhsc_enable, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + + +static int __devinit +ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct device_node *dn = op->node; + struct usb_hcd *hcd; + struct ohci_hcd *ohci; + struct resource res; + int irq; + + int rv; + int is_bigendian; + + if (usb_disabled()) + return -ENODEV; + + is_bigendian = + device_is_compatible(dn, "ohci-bigendian") || + device_is_compatible(dn, "ohci-be"); + + dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n"); + + rv = of_address_to_resource(dn, 0, &res); + if (rv) + return rv; + + hcd = usb_create_hcd(&ohci_ppc_of_hc_driver, &op->dev, "PPC-OF USB"); + if (!hcd) + return -ENOMEM; + + hcd->rsrc_start = res.start; + hcd->rsrc_len = res.end - res.start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + printk(KERN_ERR __FILE__ ": request_mem_region failed\n"); + rv = -EBUSY; + goto err_rmr; + } + + irq = irq_of_parse_and_map(dn, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n"); + rv = -EBUSY; + goto err_irq; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + printk(KERN_ERR __FILE__ ": ioremap failed\n"); + rv = -ENOMEM; + goto err_ioremap; + } + + ohci = hcd_to_ohci(hcd); + if (is_bigendian) + ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; + + ohci_hcd_init(ohci); + + rv = usb_add_hcd(hcd, irq, 0); + if (rv == 0) + return 0; + + iounmap(hcd->regs); +err_ioremap: + irq_dispose_mapping(irq); +err_irq: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_rmr: + usb_put_hcd(hcd); + + return rv; +} + +static int ohci_hcd_ppc_of_remove(struct of_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + dev_set_drvdata(&op->dev, NULL); + + dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n"); + + usb_remove_hcd(hcd); + + iounmap(hcd->regs); + irq_dispose_mapping(hcd->irq); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + usb_put_hcd(hcd); + + return 0; +} + +static int ohci_hcd_ppc_of_shutdown(struct of_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); + + return 0; +} + + +static struct of_device_id ohci_hcd_ppc_of_match[] = { +#ifdef CONFIG_USB_OHCI_HCD_PPC_OF_BE + { + .name = "usb", + .compatible = "ohci-bigendian", + }, + { + .name = "usb", + .compatible = "ohci-be", + }, +#endif +#ifdef CONFIG_USB_OHCI_HCD_PPC_OF_LE + { + .name = "usb", + .compatible = "ohci-littledian", + }, + { + .name = "usb", + .compatible = "ohci-le", + }, +#endif + {}, +}; +MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match); + +#if !defined(CONFIG_USB_OHCI_HCD_PPC_OF_BE) && \ + !defined(CONFIG_USB_OHCI_HCD_PPC_OF_LE) +#error "No endianess selected for ppc-of-ohci" +#endif + + +static struct of_platform_driver ohci_hcd_ppc_of_driver = { + .name = "ppc-of-ohci", + .match_table = ohci_hcd_ppc_of_match, + .probe = ohci_hcd_ppc_of_probe, + .remove = ohci_hcd_ppc_of_remove, + .shutdown = ohci_hcd_ppc_of_shutdown, +#ifdef CONFIG_PM + /*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/ + /*.resume = ohci_hcd_ppc_soc_drv_resume,*/ +#endif + .driver = { + .name = "ppc-of-ohci", + .owner = THIS_MODULE, + }, +}; + diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c index e1a7eb8..1a2e177 100644 --- a/drivers/usb/host/ohci-ppc-soc.c +++ b/drivers/usb/host/ohci-ppc-soc.c @@ -72,7 +72,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, } ohci = hcd_to_ohci(hcd); - ohci->flags |= OHCI_BIG_ENDIAN; + ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC; ohci_hcd_init(ohci); retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); @@ -208,19 +208,3 @@ static struct platform_driver ohci_hcd_ppc_soc_driver = { }, }; -static int __init ohci_hcd_ppc_soc_init(void) -{ - pr_debug(DRIVER_INFO " (PPC SOC)\n"); - pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed), - sizeof(struct td)); - - return platform_driver_register(&ohci_hcd_ppc_soc_driver); -} - -static void __exit ohci_hcd_ppc_soc_cleanup(void) -{ - platform_driver_unregister(&ohci_hcd_ppc_soc_driver); -} - -module_init(ohci_hcd_ppc_soc_init); -module_exit(ohci_hcd_ppc_soc_cleanup); diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c new file mode 100644 index 0000000..69d948b --- /dev/null +++ b/drivers/usb/host/ohci-ps3.c @@ -0,0 +1,196 @@ +/* + * PS3 OHCI Host Controller driver + * + * Copyright (C) 2006 Sony Computer Entertainment Inc. + * Copyright 2006 Sony Corp. + * + * 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; version 2 of the License. + * + * 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 + */ + +#include <asm/ps3.h> + +static int ps3_ohci_hc_reset(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + ohci->flags |= OHCI_QUIRK_BE_MMIO; + ohci_hcd_init(ohci); + return ohci_init(ohci); +} + +static int __devinit ps3_ohci_hc_start(struct usb_hcd *hcd) +{ + int result; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + /* Handle root hub init quirk in spider south bridge. */ + /* Also set PwrOn2PwrGood to 0x7f (254ms). */ + + ohci_writel(ohci, 0x7f000000 | RH_A_PSM | RH_A_OCPM, + &ohci->regs->roothub.a); + ohci_writel(ohci, 0x00060000, &ohci->regs->roothub.b); + + result = ohci_run(ohci); + + if (result < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + } + + return result; +} + +static const struct hc_driver ps3_ohci_hc_driver = { + .description = hcd_name, + .product_desc = "PS3 OHCI Host Controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + .reset = ps3_ohci_hc_reset, + .start = ps3_ohci_hc_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + .get_frame_number = ohci_get_frame, + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, + .start_port_reset = ohci_start_port_reset, +#if defined(CONFIG_PM) + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif +}; + +/* redefine dev_dbg to do a syntax check */ + +#if !defined(DEBUG) +#undef dev_dbg +static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg( + const struct device *_dev, const char *fmt, ...) {return 0;} +#endif + +static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev) +{ + int result; + struct usb_hcd *hcd; + unsigned int virq; + static u64 dummy_mask = DMA_32BIT_MASK; + + if (usb_disabled()) { + result = -ENODEV; + goto fail_start; + } + + result = ps3_mmio_region_create(dev->m_region); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n", + __func__, __LINE__); + result = -EPERM; + goto fail_mmio; + } + + dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__, + __LINE__, dev->m_region->lpar_addr); + + result = ps3_alloc_io_irq(dev->interrupt_id, &virq); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n", + __func__, __LINE__, virq); + result = -EPERM; + goto fail_irq; + } + + dev->core.power.power_state = PMSG_ON; + dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */ + + hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev->core.bus_id); + + if (!hcd) { + dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__, + __LINE__); + result = -ENOMEM; + goto fail_create_hcd; + } + + hcd->rsrc_start = dev->m_region->lpar_addr; + hcd->rsrc_len = dev->m_region->len; + hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len); + + if (!hcd->regs) { + dev_dbg(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); + result = -EPERM; + goto fail_ioremap; + } + + dev_dbg(&dev->core, "%s:%d: hcd->rsrc_start %lxh\n", __func__, __LINE__, + (unsigned long)hcd->rsrc_start); + dev_dbg(&dev->core, "%s:%d: hcd->rsrc_len %lxh\n", __func__, __LINE__, + (unsigned long)hcd->rsrc_len); + dev_dbg(&dev->core, "%s:%d: hcd->regs %lxh\n", __func__, __LINE__, + (unsigned long)hcd->regs); + dev_dbg(&dev->core, "%s:%d: virq %lu\n", __func__, __LINE__, + (unsigned long)virq); + + ps3_system_bus_set_driver_data(dev, hcd); + + result = usb_add_hcd(hcd, virq, IRQF_DISABLED); + + if (result) { + dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n", + __func__, __LINE__, result); + goto fail_add_hcd; + } + + return result; + +fail_add_hcd: + iounmap(hcd->regs); +fail_ioremap: + usb_put_hcd(hcd); +fail_create_hcd: + ps3_free_io_irq(virq); +fail_irq: + ps3_free_mmio_region(dev->m_region); +fail_mmio: +fail_start: + return result; +} + +static int ps3_ohci_sb_remove (struct ps3_system_bus_device *dev) +{ + struct usb_hcd *hcd = + (struct usb_hcd *)ps3_system_bus_get_driver_data(dev); + + usb_put_hcd(hcd); + ps3_system_bus_set_driver_data(dev, NULL); + + return 0; +} + +MODULE_ALIAS("ps3-ohci"); + +static struct ps3_system_bus_driver ps3_ohci_sb_driver = { + .match_id = PS3_MATCH_ID_OHCI, + .core = { + .name = "ps3-ohci-driver", + }, + .probe = ps3_ohci_sb_probe, + .remove = ps3_ohci_sb_remove, +}; diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 3bbea84..f1563dc 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -369,19 +369,3 @@ static struct platform_driver ohci_hcd_pxa27x_driver = { }, }; -static int __init ohci_hcd_pxa27x_init (void) -{ - pr_debug (DRIVER_INFO " (pxa27x)"); - pr_debug ("block sizes: ed %d td %d\n", - sizeof (struct ed), sizeof (struct td)); - - return platform_driver_register(&ohci_hcd_pxa27x_driver); -} - -static void __exit ohci_hcd_pxa27x_cleanup (void) -{ - platform_driver_unregister(&ohci_hcd_pxa27x_driver); -} - -module_init (ohci_hcd_pxa27x_init); -module_exit (ohci_hcd_pxa27x_cleanup); diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index b350d45..6829814 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -501,15 +501,3 @@ static struct platform_driver ohci_hcd_s3c2410_driver = { }, }; -static int __init ohci_hcd_s3c2410_init (void) -{ - return platform_driver_register(&ohci_hcd_s3c2410_driver); -} - -static void __exit ohci_hcd_s3c2410_cleanup (void) -{ - platform_driver_unregister(&ohci_hcd_s3c2410_driver); -} - -module_init (ohci_hcd_s3c2410_init); -module_exit (ohci_hcd_s3c2410_cleanup); diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index fe0090e..0f48f2d 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -269,19 +269,3 @@ static struct sa1111_driver ohci_hcd_sa1111_driver = { .remove = ohci_hcd_sa1111_drv_remove, }; -static int __init ohci_hcd_sa1111_init (void) -{ - dbg (DRIVER_INFO " (SA-1111)"); - dbg ("block sizes: ed %d td %d", - sizeof (struct ed), sizeof (struct td)); - - return sa1111_driver_register(&ohci_hcd_sa1111_driver); -} - -static void __exit ohci_hcd_sa1111_cleanup (void) -{ - sa1111_driver_unregister(&ohci_hcd_sa1111_driver); -} - -module_init (ohci_hcd_sa1111_init); -module_exit (ohci_hcd_sa1111_cleanup); diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 405257f..0dafcda 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -394,8 +394,9 @@ struct ohci_hcd { #define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */ #define OHCI_QUIRK_SUPERIO 0x02 /* natsemi */ #define OHCI_QUIRK_INITRESET 0x04 /* SiS, OPTi, ... */ -#define OHCI_BIG_ENDIAN 0x08 /* big endian HC */ -#define OHCI_QUIRK_ZFMICRO 0x10 /* Compaq ZFMicro chipset*/ +#define OHCI_QUIRK_BE_DESC 0x08 /* BE descriptors */ +#define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */ +#define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/ // there are also chip quirks/bugs in init logic }; @@ -439,117 +440,164 @@ static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci) * a minority (notably the IBM STB04XXX and the Motorola MPC5200 * processors) implement them in big endian format. * + * In addition some more exotic implementations like the Toshiba + * Spider (aka SCC) cell southbridge are "mixed" endian, that is, + * they have a different endianness for registers vs. in-memory + * descriptors. + * * This attempts to support either format at compile time without a * runtime penalty, or both formats with the additional overhead * of checking a flag bit. + * + * That leads to some tricky Kconfig rules howevber. There are + * different defaults based on some arch/ppc platforms, though + * the basic rules are: + * + * Controller type Kconfig options needed + * --------------- ---------------------- + * little endian CONFIG_USB_OHCI_LITTLE_ENDIAN + * + * fully big endian CONFIG_USB_OHCI_BIG_ENDIAN_DESC _and_ + * CONFIG_USB_OHCI_BIG_ENDIAN_MMIO + * + * mixed endian CONFIG_USB_OHCI_LITTLE_ENDIAN _and_ + * CONFIG_USB_OHCI_BIG_ENDIAN_{MMIO,DESC} + * + * (If you have a mixed endian controller, you -must- also define + * CONFIG_USB_OHCI_LITTLE_ENDIAN or things will not work when building + * both your mixed endian and a fully big endian controller support in + * the same kernel image). */ -#ifdef CONFIG_USB_OHCI_BIG_ENDIAN +#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_DESC +#ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN +#define big_endian_desc(ohci) (ohci->flags & OHCI_QUIRK_BE_DESC) +#else +#define big_endian_desc(ohci) 1 /* only big endian */ +#endif +#else +#define big_endian_desc(ohci) 0 /* only little endian */ +#endif +#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO #ifdef CONFIG_USB_OHCI_LITTLE_ENDIAN -#define big_endian(ohci) (ohci->flags & OHCI_BIG_ENDIAN) /* either */ +#define big_endian_mmio(ohci) (ohci->flags & OHCI_QUIRK_BE_MMIO) #else -#define big_endian(ohci) 1 /* only big endian */ +#define big_endian_mmio(ohci) 1 /* only big endian */ +#endif +#else +#define big_endian_mmio(ohci) 0 /* only little endian */ #endif /* * Big-endian read/write functions are arch-specific. * Other arches can be added if/when they're needed. + * + * REVISIT: arch/powerpc now has readl/writel_be, so the + * definition below can die once the STB04xxx support is + * finally ported over. */ -#if defined(CONFIG_PPC) +#if defined(CONFIG_PPC) && !defined(CONFIG_PPC_MERGE) #define readl_be(addr) in_be32((__force unsigned *)addr) #define writel_be(val, addr) out_be32((__force unsigned *)addr, val) #endif -static inline unsigned int ohci_readl (const struct ohci_hcd *ohci, - __hc32 __iomem * regs) +static inline unsigned int _ohci_readl (const struct ohci_hcd *ohci, + __hc32 __iomem * regs) { - return big_endian(ohci) ? readl_be (regs) : readl ((__force u32 *)regs); +#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO + return big_endian_mmio(ohci) ? + readl_be ((__force u32 *)regs) : + readl ((__force u32 *)regs); +#else + return readl ((__force u32 *)regs); +#endif } -static inline void ohci_writel (const struct ohci_hcd *ohci, - const unsigned int val, __hc32 __iomem *regs) +static inline void _ohci_writel (const struct ohci_hcd *ohci, + const unsigned int val, __hc32 __iomem *regs) { - big_endian(ohci) ? writel_be (val, regs) : - writel (val, (__force u32 *)regs); +#ifdef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO + big_endian_mmio(ohci) ? + writel_be (val, (__force u32 *)regs) : + writel (val, (__force u32 *)regs); +#else + writel (val, (__force u32 *)regs); +#endif } -#else /* !CONFIG_USB_OHCI_BIG_ENDIAN */ - -#define big_endian(ohci) 0 /* only little endian */ - #ifdef CONFIG_ARCH_LH7A404 - /* Marc Singer: at the time this code was written, the LH7A404 - * had a problem reading the USB host registers. This - * implementation of the ohci_readl function performs the read - * twice as a work-around. - */ -static inline unsigned int -ohci_readl (const struct ohci_hcd *ohci, const __hc32 *regs) -{ - *(volatile __force unsigned int*) regs; - return *(volatile __force unsigned int*) regs; -} +/* Marc Singer: at the time this code was written, the LH7A404 + * had a problem reading the USB host registers. This + * implementation of the ohci_readl function performs the read + * twice as a work-around. + */ +#define ohci_readl(o,r) (_ohci_readl(o,r),_ohci_readl(o,r)) +#define ohci_writel(o,v,r) _ohci_writel(o,v,r) #else - /* Standard version of ohci_readl uses standard, platform - * specific implementation. */ -static inline unsigned int -ohci_readl (const struct ohci_hcd *ohci, __hc32 __iomem * regs) -{ - return readl(regs); -} +#define ohci_readl(o,r) _ohci_readl(o,r) +#define ohci_writel(o,v,r) _ohci_writel(o,v,r) #endif -static inline void ohci_writel (const struct ohci_hcd *ohci, - const unsigned int val, __hc32 __iomem *regs) -{ - writel (val, regs); -} - -#endif /* !CONFIG_USB_OHCI_BIG_ENDIAN */ /*-------------------------------------------------------------------------*/ /* cpu to ohci */ static inline __hc16 cpu_to_hc16 (const struct ohci_hcd *ohci, const u16 x) { - return big_endian(ohci) ? (__force __hc16)cpu_to_be16(x) : (__force __hc16)cpu_to_le16(x); + return big_endian_desc(ohci) ? + (__force __hc16)cpu_to_be16(x) : + (__force __hc16)cpu_to_le16(x); } static inline __hc16 cpu_to_hc16p (const struct ohci_hcd *ohci, const u16 *x) { - return big_endian(ohci) ? cpu_to_be16p(x) : cpu_to_le16p(x); + return big_endian_desc(ohci) ? + cpu_to_be16p(x) : + cpu_to_le16p(x); } static inline __hc32 cpu_to_hc32 (const struct ohci_hcd *ohci, const u32 x) { - return big_endian(ohci) ? (__force __hc32)cpu_to_be32(x) : (__force __hc32)cpu_to_le32(x); + return big_endian_desc(ohci) ? + (__force __hc32)cpu_to_be32(x) : + (__force __hc32)cpu_to_le32(x); } static inline __hc32 cpu_to_hc32p (const struct ohci_hcd *ohci, const u32 *x) { - return big_endian(ohci) ? cpu_to_be32p(x) : cpu_to_le32p(x); + return big_endian_desc(ohci) ? + cpu_to_be32p(x) : + cpu_to_le32p(x); } /* ohci to cpu */ static inline u16 hc16_to_cpu (const struct ohci_hcd *ohci, const __hc16 x) { - return big_endian(ohci) ? be16_to_cpu((__force __be16)x) : le16_to_cpu((__force __le16)x); + return big_endian_desc(ohci) ? + be16_to_cpu((__force __be16)x) : + le16_to_cpu((__force __le16)x); } static inline u16 hc16_to_cpup (const struct ohci_hcd *ohci, const __hc16 *x) { - return big_endian(ohci) ? be16_to_cpup((__force __be16 *)x) : le16_to_cpup((__force __le16 *)x); + return big_endian_desc(ohci) ? + be16_to_cpup((__force __be16 *)x) : + le16_to_cpup((__force __le16 *)x); } static inline u32 hc32_to_cpu (const struct ohci_hcd *ohci, const __hc32 x) { - return big_endian(ohci) ? be32_to_cpu((__force __be32)x) : le32_to_cpu((__force __le32)x); + return big_endian_desc(ohci) ? + be32_to_cpu((__force __be32)x) : + le32_to_cpu((__force __le32)x); } static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x) { - return big_endian(ohci) ? be32_to_cpup((__force __be32 *)x) : le32_to_cpup((__force __le32 *)x); + return big_endian_desc(ohci) ? + be32_to_cpup((__force __be32 *)x) : + le32_to_cpup((__force __le32 *)x); } /*-------------------------------------------------------------------------*/ @@ -557,6 +605,9 @@ static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x) /* HCCA frame number is 16 bits, but is accessed as 32 bits since not all * hardware handles 16 bit reads. That creates a different confusion on * some big-endian SOC implementations. Same thing happens with PSW access. + * + * FIXME: Deal with that as a runtime quirk when STB03xxx is ported over + * to arch/powerpc */ #ifdef CONFIG_STB03xxx @@ -568,7 +619,7 @@ static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x) static inline u16 ohci_frame_no(const struct ohci_hcd *ohci) { u32 tmp; - if (big_endian(ohci)) { + if (big_endian_desc(ohci)) { tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no); tmp >>= OHCI_BE_FRAME_NO_SHIFT; } else @@ -580,7 +631,7 @@ static inline u16 ohci_frame_no(const struct ohci_hcd *ohci) static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci, const struct td *td, int index) { - return (__hc16 *)(big_endian(ohci) ? + return (__hc16 *)(big_endian_desc(ohci) ? &td->hwPSW[index ^ 1] : &td->hwPSW[index]); } diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index e345f15..5d6c06b 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -168,9 +168,13 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) space, "", qh, qtype, le32_to_cpu(qh->link), le32_to_cpu(element)); if (qh->type == USB_ENDPOINT_XFER_ISOC) - out += sprintf(out, "%*s period %d frame %x desc [%p]\n", - space, "", qh->period, qh->iso_frame, - qh->iso_packet_desc); + out += sprintf(out, "%*s period %d phase %d load %d us, " + "frame %x desc [%p]\n", + space, "", qh->period, qh->phase, qh->load, + qh->iso_frame, qh->iso_packet_desc); + else if (qh->type == USB_ENDPOINT_XFER_INT) + out += sprintf(out, "%*s period %d phase %d load %d us\n", + space, "", qh->period, qh->phase, qh->load); if (element & UHCI_PTR_QH) out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); @@ -208,7 +212,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) space, "", nurbs); } - if (qh->udev) { + if (qh->dummy_td) { out += sprintf(out, "%*s Dummy TD\n", space, ""); out += uhci_show_td(qh->dummy_td, out, len - (out - buf), 0); } @@ -347,31 +351,80 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) struct uhci_qh *qh; struct uhci_td *td; struct list_head *tmp, *head; + int nframes, nerrs; out += uhci_show_root_hub_state(uhci, out, len - (out - buf)); out += sprintf(out, "HC status\n"); out += uhci_show_status(uhci, out, len - (out - buf)); + + out += sprintf(out, "Periodic load table\n"); + for (i = 0; i < MAX_PHASE; ++i) { + out += sprintf(out, "\t%d", uhci->load[i]); + if (i % 8 == 7) + *out++ = '\n'; + } + out += sprintf(out, "Total: %d, #INT: %d, #ISO: %d\n", + uhci->total_load, + uhci_to_hcd(uhci)->self.bandwidth_int_reqs, + uhci_to_hcd(uhci)->self.bandwidth_isoc_reqs); if (debug <= 1) return out - buf; out += sprintf(out, "Frame List\n"); + nframes = 10; + nerrs = 0; for (i = 0; i < UHCI_NUMFRAMES; ++i) { + __le32 link, qh_dma; + + j = 0; td = uhci->frame_cpu[i]; + link = uhci->frame[i]; if (!td) - continue; + goto check_link; - out += sprintf(out, "- Frame %d\n", i); \ - if (td->dma_handle != (dma_addr_t)uhci->frame[i]) - out += sprintf(out, " frame list does not match td->dma_handle!\n"); + if (nframes > 0) { + out += sprintf(out, "- Frame %d -> (%08x)\n", + i, le32_to_cpu(link)); + j = 1; + } head = &td->fl_list; tmp = head; do { td = list_entry(tmp, struct uhci_td, fl_list); tmp = tmp->next; - out += uhci_show_td(td, out, len - (out - buf), 4); + if (cpu_to_le32(td->dma_handle) != link) { + if (nframes > 0) + out += sprintf(out, " link does " + "not match list entry!\n"); + else + ++nerrs; + } + if (nframes > 0) + out += uhci_show_td(td, out, + len - (out - buf), 4); + link = td->link; } while (tmp != head); + +check_link: + qh_dma = uhci_frame_skel_link(uhci, i); + if (link != qh_dma) { + if (nframes > 0) { + if (!j) { + out += sprintf(out, + "- Frame %d -> (%08x)\n", + i, le32_to_cpu(link)); + j = 1; + } + out += sprintf(out, " link does not match " + "QH (%08x)!\n", le32_to_cpu(qh_dma)); + } else + ++nerrs; + } + nframes -= j; } + if (nerrs > 0) + out += sprintf(out, "Skipped %d bad links\n", nerrs); out += sprintf(out, "Skeleton QHs\n"); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index e0d4c23..49b9d39 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -92,6 +92,34 @@ static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state); static void wakeup_rh(struct uhci_hcd *uhci); static void uhci_get_current_frame_number(struct uhci_hcd *uhci); +/* + * Calculate the link pointer DMA value for the first Skeleton QH in a frame. + */ +static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) +{ + int skelnum; + + /* + * The interrupt queues will be interleaved as evenly as possible. + * There's not much to be done about period-1 interrupts; they have + * to occur in every frame. But we can schedule period-2 interrupts + * in odd-numbered frames, period-4 interrupts in frames congruent + * to 2 (mod 4), and so on. This way each frame only has two + * interrupt QHs, which will help spread out bandwidth utilization. + * + * ffs (Find First bit Set) does exactly what we need: + * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], + * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. + * ffs >= 7 => not on any high-period queue, so use + * skel_int1_qh = skelqh[9]. + * Add in UHCI_NUMFRAMES to insure at least one bit is set. + */ + skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES); + if (skelnum <= 1) + skelnum = 9; + return UHCI_PTR_QH | cpu_to_le32(uhci->skelqh[skelnum]->dma_handle); +} + #include "uhci-debug.c" #include "uhci-q.c" #include "uhci-hub.c" @@ -631,32 +659,11 @@ static int uhci_start(struct usb_hcd *hcd) /* * Fill the frame list: make all entries point to the proper * interrupt queue. - * - * The interrupt queues will be interleaved as evenly as possible. - * There's not much to be done about period-1 interrupts; they have - * to occur in every frame. But we can schedule period-2 interrupts - * in odd-numbered frames, period-4 interrupts in frames congruent - * to 2 (mod 4), and so on. This way each frame only has two - * interrupt QHs, which will help spread out bandwidth utilization. */ for (i = 0; i < UHCI_NUMFRAMES; i++) { - int irq; - - /* - * ffs (Find First bit Set) does exactly what we need: - * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], - * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. - * ffs >= 7 => not on any high-period queue, so use - * skel_int1_qh = skelqh[9]. - * Add UHCI_NUMFRAMES to insure at least one bit is set. - */ - irq = 8 - (int) __ffs(i + UHCI_NUMFRAMES); - if (irq <= 1) - irq = 9; /* Only place we don't use the frame list routines */ - uhci->frame[i] = UHCI_PTR_QH | - cpu_to_le32(uhci->skelqh[irq]->dma_handle); + uhci->frame[i] = uhci_frame_skel_link(uhci, i); } /* diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 108e3de..74469b5 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -83,6 +83,7 @@ #define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ #define CAN_SCHEDULE_FRAMES 1000 /* how far in the future frames * can be scheduled */ +#define MAX_PHASE 32 /* Periodic scheduling length */ /* When no queues need Full-Speed Bandwidth Reclamation, * delay this long before turning FSBR off */ @@ -141,6 +142,8 @@ struct uhci_qh { unsigned long advance_jiffies; /* Time of last queue advance */ unsigned int unlink_frame; /* When the QH was unlinked */ unsigned int period; /* For Interrupt and Isochronous QHs */ + short phase; /* Between 0 and period-1 */ + short load; /* Periodic time requirement, in us */ unsigned int iso_frame; /* Frame # for iso_packet_desc */ int iso_status; /* Status for Isochronous URBs */ @@ -153,6 +156,8 @@ struct uhci_qh { unsigned int needs_fixup:1; /* Must fix the TD toggle values */ unsigned int is_stopped:1; /* Queue was stopped by error/unlink */ unsigned int wait_expired:1; /* QH_WAIT_TIMEOUT has expired */ + unsigned int bandwidth_reserved:1; /* Periodic bandwidth has + * been allocated */ } __attribute__((aligned(16))); /* @@ -414,6 +419,9 @@ struct uhci_hcd { wait_queue_head_t waitqh; /* endpoint_disable waiters */ int num_waiting; /* Number of waiters */ + + int total_load; /* Sum of array values */ + short load[MAX_PHASE]; /* Periodic allocations */ }; /* Convert between a usb_hcd pointer and the corresponding uhci_hcd */ diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 30b8845..2cbb239 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -248,16 +248,26 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, INIT_LIST_HEAD(&qh->node); if (udev) { /* Normal QH */ - qh->dummy_td = uhci_alloc_td(uhci); - if (!qh->dummy_td) { - dma_pool_free(uhci->qh_pool, qh, dma_handle); - return NULL; + qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (qh->type != USB_ENDPOINT_XFER_ISOC) { + qh->dummy_td = uhci_alloc_td(uhci); + if (!qh->dummy_td) { + dma_pool_free(uhci->qh_pool, qh, dma_handle); + return NULL; + } } qh->state = QH_STATE_IDLE; qh->hep = hep; qh->udev = udev; hep->hcpriv = qh; - qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + if (qh->type == USB_ENDPOINT_XFER_INT || + qh->type == USB_ENDPOINT_XFER_ISOC) + qh->load = usb_calc_bus_time(udev->speed, + usb_endpoint_dir_in(&hep->desc), + qh->type == USB_ENDPOINT_XFER_ISOC, + le16_to_cpu(hep->desc.wMaxPacketSize)) + / 1000 + 1; } else { /* Skeleton QH */ qh->state = QH_STATE_ACTIVE; @@ -275,7 +285,8 @@ static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) list_del(&qh->node); if (qh->udev) { qh->hep->hcpriv = NULL; - uhci_free_td(uhci, qh->dummy_td); + if (qh->dummy_td) + uhci_free_td(uhci, qh->dummy_td); } dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); } @@ -327,7 +338,7 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh, goto done; qh->element = UHCI_PTR_TERM; - /* Control pipes have to worry about toggles */ + /* Control pipes don't have to worry about toggles */ if (qh->type == USB_ENDPOINT_XFER_CONTROL) goto done; @@ -493,6 +504,121 @@ static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh) wake_up_all(&uhci->waitqh); } +/* + * Find the highest existing bandwidth load for a given phase and period. + */ +static int uhci_highest_load(struct uhci_hcd *uhci, int phase, int period) +{ + int highest_load = uhci->load[phase]; + + for (phase += period; phase < MAX_PHASE; phase += period) + highest_load = max_t(int, highest_load, uhci->load[phase]); + return highest_load; +} + +/* + * Set qh->phase to the optimal phase for a periodic transfer and + * check whether the bandwidth requirement is acceptable. + */ +static int uhci_check_bandwidth(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ + int minimax_load; + + /* Find the optimal phase (unless it is already set) and get + * its load value. */ + if (qh->phase >= 0) + minimax_load = uhci_highest_load(uhci, qh->phase, qh->period); + else { + int phase, load; + int max_phase = min_t(int, MAX_PHASE, qh->period); + + qh->phase = 0; + minimax_load = uhci_highest_load(uhci, qh->phase, qh->period); + for (phase = 1; phase < max_phase; ++phase) { + load = uhci_highest_load(uhci, phase, qh->period); + if (load < minimax_load) { + minimax_load = load; + qh->phase = phase; + } + } + } + + /* Maximum allowable periodic bandwidth is 90%, or 900 us per frame */ + if (minimax_load + qh->load > 900) { + dev_dbg(uhci_dev(uhci), "bandwidth allocation failed: " + "period %d, phase %d, %d + %d us\n", + qh->period, qh->phase, minimax_load, qh->load); + return -ENOSPC; + } + return 0; +} + +/* + * Reserve a periodic QH's bandwidth in the schedule + */ +static void uhci_reserve_bandwidth(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ + int i; + int load = qh->load; + char *p = "??"; + + for (i = qh->phase; i < MAX_PHASE; i += qh->period) { + uhci->load[i] += load; + uhci->total_load += load; + } + uhci_to_hcd(uhci)->self.bandwidth_allocated = + uhci->total_load / MAX_PHASE; + switch (qh->type) { + case USB_ENDPOINT_XFER_INT: + ++uhci_to_hcd(uhci)->self.bandwidth_int_reqs; + p = "INT"; + break; + case USB_ENDPOINT_XFER_ISOC: + ++uhci_to_hcd(uhci)->self.bandwidth_isoc_reqs; + p = "ISO"; + break; + } + qh->bandwidth_reserved = 1; + dev_dbg(uhci_dev(uhci), + "%s dev %d ep%02x-%s, period %d, phase %d, %d us\n", + "reserve", qh->udev->devnum, + qh->hep->desc.bEndpointAddress, p, + qh->period, qh->phase, load); +} + +/* + * Release a periodic QH's bandwidth reservation + */ +static void uhci_release_bandwidth(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ + int i; + int load = qh->load; + char *p = "??"; + + for (i = qh->phase; i < MAX_PHASE; i += qh->period) { + uhci->load[i] -= load; + uhci->total_load -= load; + } + uhci_to_hcd(uhci)->self.bandwidth_allocated = + uhci->total_load / MAX_PHASE; + switch (qh->type) { + case USB_ENDPOINT_XFER_INT: + --uhci_to_hcd(uhci)->self.bandwidth_int_reqs; + p = "INT"; + break; + case USB_ENDPOINT_XFER_ISOC: + --uhci_to_hcd(uhci)->self.bandwidth_isoc_reqs; + p = "ISO"; + break; + } + qh->bandwidth_reserved = 0; + dev_dbg(uhci_dev(uhci), + "%s dev %d ep%02x-%s, period %d, phase %d, %d us\n", + "release", qh->udev->devnum, + qh->hep->desc.bEndpointAddress, p, + qh->period, qh->phase, load); +} + static inline struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb) { @@ -796,7 +922,6 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, wmb(); qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); qh->dummy_td = td; - qh->period = urb->interval; usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); @@ -827,28 +952,42 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct uhci_qh *qh) { - int exponent; + int ret; /* USB 1.1 interrupt transfers only involve one packet per interval. * Drivers can submit URBs of any length, but longer ones will need * multiple intervals to complete. */ - /* Figure out which power-of-two queue to use */ - for (exponent = 7; exponent >= 0; --exponent) { - if ((1 << exponent) <= urb->interval) - break; - } - if (exponent < 0) - return -EINVAL; - urb->interval = 1 << exponent; + if (!qh->bandwidth_reserved) { + int exponent; - if (qh->period == 0) + /* Figure out which power-of-two queue to use */ + for (exponent = 7; exponent >= 0; --exponent) { + if ((1 << exponent) <= urb->interval) + break; + } + if (exponent < 0) + return -EINVAL; + qh->period = 1 << exponent; qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)]; - else if (qh->period != urb->interval) - return -EINVAL; /* Can't change the period */ - return uhci_submit_common(uhci, urb, qh); + /* For now, interrupt phase is fixed by the layout + * of the QH lists. */ + qh->phase = (qh->period / 2) & (MAX_PHASE - 1); + ret = uhci_check_bandwidth(uhci, qh); + if (ret) + return ret; + } else if (qh->period > urb->interval) + return -EINVAL; /* Can't decrease the period */ + + ret = uhci_submit_common(uhci, urb, qh); + if (ret == 0) { + urb->interval = qh->period; + if (!qh->bandwidth_reserved) + uhci_reserve_bandwidth(uhci, qh); + } + return ret; } /* @@ -995,15 +1134,32 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, return -EFBIG; /* Check the period and figure out the starting frame number */ - if (qh->period == 0) { + if (!qh->bandwidth_reserved) { + qh->period = urb->interval; if (urb->transfer_flags & URB_ISO_ASAP) { + qh->phase = -1; /* Find the best phase */ + i = uhci_check_bandwidth(uhci, qh); + if (i) + return i; + + /* Allow a little time to allocate the TDs */ uhci_get_current_frame_number(uhci); - urb->start_frame = uhci->frame_number + 10; + frame = uhci->frame_number + 10; + + /* Move forward to the first frame having the + * correct phase */ + urb->start_frame = frame + ((qh->phase - frame) & + (qh->period - 1)); } else { i = urb->start_frame - uhci->last_iso_frame; if (i <= 0 || i >= UHCI_NUMFRAMES) return -EINVAL; + qh->phase = urb->start_frame & (qh->period - 1); + i = uhci_check_bandwidth(uhci, qh); + if (i) + return i; } + } else if (qh->period != urb->interval) { return -EINVAL; /* Can't change the period */ @@ -1049,9 +1205,6 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, /* Set the interrupt-on-completion flag on the last packet. */ td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); - qh->skel = uhci->skel_iso_qh; - qh->period = urb->interval; - /* Add the TDs to the frame list */ frame = urb->start_frame; list_for_each_entry(td, &urbp->td_list, list) { @@ -1065,6 +1218,9 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, qh->iso_status = 0; } + qh->skel = uhci->skel_iso_qh; + if (!qh->bandwidth_reserved) + uhci_reserve_bandwidth(uhci, qh); return 0; } @@ -1119,7 +1275,6 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, unsigned long flags; struct urb_priv *urbp; struct uhci_qh *qh; - int bustime; spin_lock_irqsave(&uhci->lock, flags); @@ -1149,35 +1304,11 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd, ret = uhci_submit_bulk(uhci, urb, qh); break; case USB_ENDPOINT_XFER_INT: - if (list_empty(&qh->queue)) { - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) - ret = bustime; - else { - ret = uhci_submit_interrupt(uhci, urb, qh); - if (ret == 0) - usb_claim_bandwidth(urb->dev, urb, bustime, 0); - } - } else { /* inherit from parent */ - struct urb_priv *eurbp; - - eurbp = list_entry(qh->queue.prev, struct urb_priv, - node); - urb->bandwidth = eurbp->urb->bandwidth; - ret = uhci_submit_interrupt(uhci, urb, qh); - } + ret = uhci_submit_interrupt(uhci, urb, qh); break; case USB_ENDPOINT_XFER_ISOC: urb->error_count = 0; - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) { - ret = bustime; - break; - } - ret = uhci_submit_isochronous(uhci, urb, qh); - if (ret == 0) - usb_claim_bandwidth(urb->dev, urb, bustime, 1); break; } if (ret != 0) @@ -1274,24 +1405,6 @@ __acquires(uhci->lock) uhci_free_urb_priv(uhci, urbp); - switch (qh->type) { - case USB_ENDPOINT_XFER_ISOC: - /* Release bandwidth for Interrupt or Isoc. transfers */ - if (urb->bandwidth) - usb_release_bandwidth(urb->dev, urb, 1); - break; - case USB_ENDPOINT_XFER_INT: - /* Release bandwidth for Interrupt or Isoc. transfers */ - /* Make sure we don't release if we have a queued URB */ - if (list_empty(&qh->queue) && urb->bandwidth) - usb_release_bandwidth(urb->dev, urb, 0); - else - /* bandwidth was passed on to queued URB, */ - /* so don't let usb_unlink_urb() release it */ - urb->bandwidth = 0; - break; - } - spin_unlock(&uhci->lock); usb_hcd_giveback_urb(uhci_to_hcd(uhci), urb); spin_lock(&uhci->lock); @@ -1300,9 +1413,8 @@ __acquires(uhci->lock) * reserved bandwidth. */ if (list_empty(&qh->queue)) { uhci_unlink_qh(uhci, qh); - - /* Bandwidth stuff not yet implemented */ - qh->period = 0; + if (qh->bandwidth_reserved) + uhci_release_bandwidth(uhci, qh); } } diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 63a84bb..d308afd 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -565,11 +565,15 @@ static void mdc800_usb_disconnect (struct usb_interface *intf) usb_deregister_dev(intf, &mdc800_class); + /* must be under lock to make sure no URB + is submitted after usb_kill_urb() */ + mutex_lock(&mdc800->io_lock); mdc800->state=NOT_CONNECTED; usb_kill_urb(mdc800->irq_urb); usb_kill_urb(mdc800->write_urb); usb_kill_urb(mdc800->download_urb); + mutex_unlock(&mdc800->io_lock); mdc800->dev = NULL; usb_set_intfdata(intf, NULL); diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index aa6a620..2e71d3c 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -352,3 +352,15 @@ config USB_APPLETOUCH To compile this driver as a module, choose M here: the module will be called appletouch. + +config USB_GTCO + tristate "GTCO CalComp/InterWrite USB Support" + depends on USB && INPUT + ---help--- + Say Y here if you want to use the USB version of the GTCO + CalComp/InterWrite Tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called gtco. diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index a06024e..a9d206c 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_USB_ACECAD) += acecad.o obj-$(CONFIG_USB_YEALINK) += yealink.o obj-$(CONFIG_USB_XPAD) += xpad.o obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o +obj-$(CONFIG_USB_GTCO) += gtco.o ifeq ($(CONFIG_USB_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/usb/input/gtco.c b/drivers/usb/input/gtco.c new file mode 100644 index 0000000..203cdc1bb --- /dev/null +++ b/drivers/usb/input/gtco.c @@ -0,0 +1,1104 @@ +/* -*- linux-c -*- + +GTCO digitizer USB driver + +Use the err(), dbg() and info() macros from usb.h for system logging + +TO CHECK: Is pressure done right on report 5? + +Copyright (C) 2006 GTCO CalComp + +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; version 2 +of the License. + +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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of GTCO-CalComp not be used in advertising +or publicity pertaining to distribution of the software without specific, +written prior permission. GTCO-CalComp makes no representations about the +suitability of this software for any purpose. It is provided "as is" +without express or implied warranty. + +GTCO-CALCOMP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL GTCO-CALCOMP BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTIONS, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +GTCO CalComp, Inc. +7125 Riverwood Drive +Columbia, MD 21046 + +Jeremy Roberson jroberson@gtcocalcomp.com +Scott Hill shill@gtcocalcomp.com +*/ + + + +/*#define DEBUG*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/usb.h> +#include <asm/uaccess.h> +#include <asm/unaligned.h> +#include <asm/byteorder.h> + + +#include <linux/version.h> +#include <linux/usb/input.h> + +/* Version with a Major number of 2 is for kernel inclusion only. */ +#define GTCO_VERSION "2.00.0006" + + +/* MACROS */ + +#define VENDOR_ID_GTCO 0x078C +#define PID_400 0x400 +#define PID_401 0x401 +#define PID_1000 0x1000 +#define PID_1001 0x1001 +#define PID_1002 0x1002 + +/* Max size of a single report */ +#define REPORT_MAX_SIZE 10 + + +/* Bitmask whether pen is in range */ +#define MASK_INRANGE 0x20 +#define MASK_BUTTON 0x01F + +#define PATHLENGTH 64 + +/* DATA STRUCTURES */ + +/* Device table */ +static struct usb_device_id gtco_usbid_table [] = { + { USB_DEVICE(VENDOR_ID_GTCO, PID_400) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_401) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1000) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1001) }, + { USB_DEVICE(VENDOR_ID_GTCO, PID_1002) }, + { } +}; +MODULE_DEVICE_TABLE (usb, gtco_usbid_table); + + +/* Structure to hold all of our device specific stuff */ +struct gtco { + + struct input_dev *inputdevice; /* input device struct pointer */ + struct usb_device *usbdev; /* the usb device for this device */ + struct urb *urbinfo; /* urb for incoming reports */ + dma_addr_t buf_dma; /* dma addr of the data buffer*/ + unsigned char * buffer; /* databuffer for reports */ + + char usbpath[PATHLENGTH]; + int openCount; + + /* Information pulled from Report Descriptor */ + u32 usage; + u32 min_X; + u32 max_X; + u32 min_Y; + u32 max_Y; + s8 mintilt_X; + s8 maxtilt_X; + s8 mintilt_Y; + s8 maxtilt_Y; + u32 maxpressure; + u32 minpressure; +}; + + + +/* Code for parsing the HID REPORT DESCRIPTOR */ + +/* From HID1.11 spec */ +struct hid_descriptor +{ + struct usb_descriptor_header header; + __le16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + u8 bDescriptorType; + __le16 wDescriptorLength; +} __attribute__ ((packed)); + + +#define HID_DESCRIPTOR_SIZE 9 +#define HID_DEVICE_TYPE 33 +#define REPORT_DEVICE_TYPE 34 + + +#define PREF_TAG(x) ((x)>>4) +#define PREF_TYPE(x) ((x>>2)&0x03) +#define PREF_SIZE(x) ((x)&0x03) + +#define TYPE_MAIN 0 +#define TYPE_GLOBAL 1 +#define TYPE_LOCAL 2 +#define TYPE_RESERVED 3 + +#define TAG_MAIN_INPUT 0x8 +#define TAG_MAIN_OUTPUT 0x9 +#define TAG_MAIN_FEATURE 0xB +#define TAG_MAIN_COL_START 0xA +#define TAG_MAIN_COL_END 0xC + +#define TAG_GLOB_USAGE 0 +#define TAG_GLOB_LOG_MIN 1 +#define TAG_GLOB_LOG_MAX 2 +#define TAG_GLOB_PHYS_MIN 3 +#define TAG_GLOB_PHYS_MAX 4 +#define TAG_GLOB_UNIT_EXP 5 +#define TAG_GLOB_UNIT 6 +#define TAG_GLOB_REPORT_SZ 7 +#define TAG_GLOB_REPORT_ID 8 +#define TAG_GLOB_REPORT_CNT 9 +#define TAG_GLOB_PUSH 10 +#define TAG_GLOB_POP 11 + +#define TAG_GLOB_MAX 12 + +#define DIGITIZER_USAGE_TIP_PRESSURE 0x30 +#define DIGITIZER_USAGE_TILT_X 0x3D +#define DIGITIZER_USAGE_TILT_Y 0x3E + + +/* + * + * This is an abbreviated parser for the HID Report Descriptor. We + * know what devices we are talking to, so this is by no means meant + * to be generic. We can make some safe assumptions: + * + * - We know there are no LONG tags, all short + * - We know that we have no MAIN Feature and MAIN Output items + * - We know what the IRQ reports are supposed to look like. + * + * The main purpose of this is to use the HID report desc to figure + * out the mins and maxs of the fields in the IRQ reports. The IRQ + * reports for 400/401 change slightly if the max X is bigger than 64K. + * + */ +static void parse_hid_report_descriptor(struct gtco *device, char * report, + int length) +{ + int x,i=0; + + /* Tag primitive vars */ + __u8 prefix; + __u8 size; + __u8 tag; + __u8 type; + __u8 data = 0; + __u16 data16 = 0; + __u32 data32 = 0; + + + /* For parsing logic */ + int inputnum = 0; + __u32 usage = 0; + + /* Global Values, indexed by TAG */ + __u32 globalval[TAG_GLOB_MAX]; + __u32 oldval[TAG_GLOB_MAX]; + + /* Debug stuff */ + char maintype='x'; + char globtype[12]; + int indent=0; + char indentstr[10]=""; + + + + dbg("======>>>>>>PARSE<<<<<<======"); + + /* Walk this report and pull out the info we need */ + while (i<length){ + prefix=report[i]; + + /* Skip over prefix */ + i++; + + /* Determine data size and save the data in the proper variable */ + size = PREF_SIZE(prefix); + switch(size){ + case 1: + data = report[i]; + break; + case 2: + data16 = le16_to_cpu(get_unaligned((__le16*)(&(report[i])))); + break; + case 3: + size = 4; + data32 = le32_to_cpu(get_unaligned((__le32*)(&(report[i])))); + } + + /* Skip size of data */ + i+=size; + + /* What we do depends on the tag type */ + tag = PREF_TAG(prefix); + type = PREF_TYPE(prefix); + switch(type){ + case TYPE_MAIN: + strcpy(globtype,""); + switch(tag){ + + case TAG_MAIN_INPUT: + /* + * The INPUT MAIN tag signifies this is + * information from a report. We need to + * figure out what it is and store the + * min/max values + */ + + maintype='I'; + if (data==2){ + strcpy(globtype,"Variable"); + } + if (data==3){ + strcpy(globtype,"Var|Const"); + } + + dbg("::::: Saving Report: %d input #%d Max: 0x%X(%d) Min:0x%X(%d) of %d bits", + globalval[TAG_GLOB_REPORT_ID],inputnum, + globalval[TAG_GLOB_LOG_MAX],globalval[TAG_GLOB_LOG_MAX], + globalval[TAG_GLOB_LOG_MIN],globalval[TAG_GLOB_LOG_MIN], + (globalval[TAG_GLOB_REPORT_SZ] * globalval[TAG_GLOB_REPORT_CNT])); + + + /* + We can assume that the first two input items + are always the X and Y coordinates. After + that, we look for everything else by + local usage value + */ + switch (inputnum){ + case 0: /* X coord */ + dbg("GER: X Usage: 0x%x",usage); + if (device->max_X == 0){ + device->max_X = globalval[TAG_GLOB_LOG_MAX]; + device->min_X = globalval[TAG_GLOB_LOG_MIN]; + } + + break; + case 1: /* Y coord */ + dbg("GER: Y Usage: 0x%x",usage); + if (device->max_Y == 0){ + device->max_Y = globalval[TAG_GLOB_LOG_MAX]; + device->min_Y = globalval[TAG_GLOB_LOG_MIN]; + } + break; + default: + /* Tilt X */ + if (usage == DIGITIZER_USAGE_TILT_X){ + if (device->maxtilt_X == 0){ + device->maxtilt_X = globalval[TAG_GLOB_LOG_MAX]; + device->mintilt_X = globalval[TAG_GLOB_LOG_MIN]; + } + } + + /* Tilt Y */ + if (usage == DIGITIZER_USAGE_TILT_Y){ + if (device->maxtilt_Y == 0){ + device->maxtilt_Y = globalval[TAG_GLOB_LOG_MAX]; + device->mintilt_Y = globalval[TAG_GLOB_LOG_MIN]; + } + } + + + /* Pressure */ + if (usage == DIGITIZER_USAGE_TIP_PRESSURE){ + if (device->maxpressure == 0){ + device->maxpressure = globalval[TAG_GLOB_LOG_MAX]; + device->minpressure = globalval[TAG_GLOB_LOG_MIN]; + } + } + + break; + } + + inputnum++; + + + break; + case TAG_MAIN_OUTPUT: + maintype='O'; + break; + case TAG_MAIN_FEATURE: + maintype='F'; + break; + case TAG_MAIN_COL_START: + maintype='S'; + + if (data==0){ + dbg("======>>>>>> Physical"); + strcpy(globtype,"Physical"); + }else{ + dbg("======>>>>>>"); + } + + /* Indent the debug output */ + indent++; + for (x=0;x<indent;x++){ + indentstr[x]='-'; + } + indentstr[x]=0; + + /* Save global tags */ + for (x=0;x<TAG_GLOB_MAX;x++){ + oldval[x] = globalval[x]; + } + + break; + case TAG_MAIN_COL_END: + dbg("<<<<<<======"); + maintype='E'; + indent--; + for (x=0;x<indent;x++){ + indentstr[x]='-'; + } + indentstr[x]=0; + + /* Copy global tags back */ + for (x=0;x<TAG_GLOB_MAX;x++){ + globalval[x] = oldval[x]; + } + + break; + } + + switch (size){ + case 1: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr,tag,maintype,size,globtype,data); + break; + case 2: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr,tag,maintype,size,globtype, data16); + break; + case 4: + dbg("%sMAINTAG:(%d) %c SIZE: %d Data: %s 0x%x", + indentstr,tag,maintype,size,globtype,data32); + break; + } + break; + case TYPE_GLOBAL: + switch(tag){ + case TAG_GLOB_USAGE: + /* + * First time we hit the global usage tag, + * it should tell us the type of device + */ + if (device->usage == 0){ + device->usage = data; + } + strcpy(globtype,"USAGE"); + break; + case TAG_GLOB_LOG_MIN : + strcpy(globtype,"LOG_MIN"); + break; + case TAG_GLOB_LOG_MAX : + strcpy(globtype,"LOG_MAX"); + break; + case TAG_GLOB_PHYS_MIN : + strcpy(globtype,"PHYS_MIN"); + break; + case TAG_GLOB_PHYS_MAX : + strcpy(globtype,"PHYS_MAX"); + break; + case TAG_GLOB_UNIT_EXP : + strcpy(globtype,"EXP"); + break; + case TAG_GLOB_UNIT : + strcpy(globtype,"UNIT"); + break; + case TAG_GLOB_REPORT_SZ : + strcpy(globtype,"REPORT_SZ"); + break; + case TAG_GLOB_REPORT_ID : + strcpy(globtype,"REPORT_ID"); + /* New report, restart numbering */ + inputnum=0; + break; + case TAG_GLOB_REPORT_CNT: + strcpy(globtype,"REPORT_CNT"); + break; + case TAG_GLOB_PUSH : + strcpy(globtype,"PUSH"); + break; + case TAG_GLOB_POP: + strcpy(globtype,"POP"); + break; + } + + + /* Check to make sure we have a good tag number + so we don't overflow array */ + if (tag < TAG_GLOB_MAX){ + switch (size){ + case 1: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",indentstr,globtype,tag,size,data); + globalval[tag]=data; + break; + case 2: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",indentstr,globtype,tag,size,data16); + globalval[tag]=data16; + break; + case 4: + dbg("%sGLOBALTAG:%s(%d) SIZE: %d Data: 0x%x",indentstr,globtype,tag,size,data32); + globalval[tag]=data32; + break; + } + }else{ + dbg("%sGLOBALTAG: ILLEGAL TAG:%d SIZE: %d ", + indentstr,tag,size); + } + + + break; + + case TYPE_LOCAL: + switch(tag){ + case TAG_GLOB_USAGE: + strcpy(globtype,"USAGE"); + /* Always 1 byte */ + usage = data; + break; + case TAG_GLOB_LOG_MIN : + strcpy(globtype,"MIN"); + break; + case TAG_GLOB_LOG_MAX : + strcpy(globtype,"MAX"); + break; + default: + strcpy(globtype,"UNKNOWN"); + } + + switch (size){ + case 1: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr,tag,globtype,size,data); + break; + case 2: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr,tag,globtype,size,data16); + break; + case 4: + dbg("%sLOCALTAG:(%d) %s SIZE: %d Data: 0x%x", + indentstr,tag,globtype,size,data32); + break; + } + + break; + } + + } + +} + + + +/* INPUT DRIVER Routines */ + + +/* + * Called when opening the input device. This will submit the URB to + * the usb system so we start getting reports + */ +static int gtco_input_open(struct input_dev *inputdev) +{ + struct gtco *device; + device = inputdev->private; + + device->urbinfo->dev = device->usbdev; + if (usb_submit_urb(device->urbinfo, GFP_KERNEL)) { + return -EIO; + } + return 0; +} + +/** + Called when closing the input device. This will unlink the URB +*/ +static void gtco_input_close(struct input_dev *inputdev) +{ + struct gtco *device = inputdev->private; + + usb_kill_urb(device->urbinfo); + +} + + +/* + * Setup input device capabilities. Tell the input system what this + * device is capable of generating. + * + * This information is based on what is read from the HID report and + * placed in the struct gtco structure + * + */ +static void gtco_setup_caps(struct input_dev *inputdev) +{ + struct gtco *device = inputdev->private; + + + /* Which events */ + inputdev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_MSC); + + + /* Misc event menu block */ + inputdev->mscbit[0] = BIT(MSC_SCAN)|BIT(MSC_SERIAL)|BIT(MSC_RAW) ; + + + /* Absolute values based on HID report info */ + input_set_abs_params(inputdev, ABS_X, device->min_X, device->max_X, + 0, 0); + input_set_abs_params(inputdev, ABS_Y, device->min_Y, device->max_Y, + 0, 0); + + /* Proximity */ + input_set_abs_params(inputdev, ABS_DISTANCE, 0, 1, 0, 0); + + /* Tilt & pressure */ + input_set_abs_params(inputdev, ABS_TILT_X, device->mintilt_X, + device->maxtilt_X, 0, 0); + input_set_abs_params(inputdev, ABS_TILT_Y, device->mintilt_Y, + device->maxtilt_Y, 0, 0); + input_set_abs_params(inputdev, ABS_PRESSURE, device->minpressure, + device->maxpressure, 0, 0); + + + /* Transducer */ + input_set_abs_params(inputdev, ABS_MISC, 0,0xFF, 0, 0); + +} + + + +/* USB Routines */ + + +/* + * URB callback routine. Called when we get IRQ reports from the + * digitizer. + * + * This bridges the USB and input device worlds. It generates events + * on the input device based on the USB reports. + */ +static void gtco_urb_callback(struct urb *urbinfo) +{ + + + struct gtco *device = urbinfo->context; + struct input_dev *inputdev; + int rc; + u32 val = 0; + s8 valsigned = 0; + char le_buffer[2]; + + inputdev = device->inputdevice; + + + /* Was callback OK? */ + if ((urbinfo->status == -ECONNRESET ) || + (urbinfo->status == -ENOENT ) || + (urbinfo->status == -ESHUTDOWN )){ + + /* Shutdown is occurring. Return and don't queue up any more */ + return; + } + + if (urbinfo->status != 0 ) { + /* Some unknown error. Hopefully temporary. Just go and */ + /* requeue an URB */ + goto resubmit; + } + + /* + * Good URB, now process + */ + + /* PID dependent when we interpret the report */ + if ((inputdev->id.product == PID_1000 )|| + (inputdev->id.product == PID_1001 )|| + (inputdev->id.product == PID_1002 )) + { + + /* + * Switch on the report ID + * Conveniently, the reports have more information, the higher + * the report number. We can just fall through the case + * statements if we start with the highest number report + */ + switch(device->buffer[0]){ + case 5: + /* Pressure is 9 bits */ + val = ((u16)(device->buffer[8]) << 1); + val |= (u16)(device->buffer[7] >> 7); + input_report_abs(inputdev, ABS_PRESSURE, + device->buffer[8]); + + /* Mask out the Y tilt value used for pressure */ + device->buffer[7] = (u8)((device->buffer[7]) & 0x7F); + + + /* Fall thru */ + case 4: + /* Tilt */ + + /* Sign extend these 7 bit numbers. */ + if (device->buffer[6] & 0x40) + device->buffer[6] |= 0x80; + + if (device->buffer[7] & 0x40) + device->buffer[7] |= 0x80; + + + valsigned = (device->buffer[6]); + input_report_abs(inputdev, ABS_TILT_X, (s32)valsigned); + + valsigned = (device->buffer[7]); + input_report_abs(inputdev, ABS_TILT_Y, (s32)valsigned); + + /* Fall thru */ + + case 2: + case 3: + /* Convert buttons, only 5 bits possible */ + val = (device->buffer[5])&MASK_BUTTON; + + /* We don't apply any meaning to the bitmask, + just report */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); + + /* Fall thru */ + case 1: + + /* All reports have X and Y coords in the same place */ + val = le16_to_cpu(get_unaligned((__le16 *) &(device->buffer[1]))); + input_report_abs(inputdev, ABS_X, val); + + val = le16_to_cpu(get_unaligned((__le16 *) &(device->buffer[3]))); + input_report_abs(inputdev, ABS_Y, val); + + + /* Ditto for proximity bit */ + if (device->buffer[5]& MASK_INRANGE){ + val = 1; + }else{ + val=0; + } + input_report_abs(inputdev, ABS_DISTANCE, val); + + + /* Report 1 is an exception to how we handle buttons */ + /* Buttons are an index, not a bitmask */ + if (device->buffer[0] == 1){ + + /* Convert buttons, 5 bit index */ + /* Report value of index set as one, + the rest as 0 */ + val = device->buffer[5]& MASK_BUTTON; + dbg("======>>>>>>REPORT 1: val 0x%X(%d)", + val,val); + + /* + * We don't apply any meaning to the button + * index, just report it + */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); + + + } + + break; + case 7: + /* Menu blocks */ + input_event(inputdev, EV_MSC, MSC_SCAN, + device->buffer[1]); + + + break; + + } + + + } + /* Other pid class */ + if ((inputdev->id.product == PID_400 )|| + (inputdev->id.product == PID_401 )) + { + + /* Report 2 */ + if (device->buffer[0] == 2){ + /* Menu blocks */ + input_event(inputdev, EV_MSC, MSC_SCAN, + device->buffer[1]); + } + + /* Report 1 */ + if (device->buffer[0] == 1){ + char buttonbyte; + + + /* IF X max > 64K, we still a bit from the y report */ + if (device->max_X > 0x10000){ + + val = (u16)(((u16)(device->buffer[2]<<8))|((u8)(device->buffer[1]))); + val |= (u32)(((u8)device->buffer[3]&0x1)<< 16); + + input_report_abs(inputdev, ABS_X, val); + + le_buffer[0] = (u8)((u8)(device->buffer[3])>>1); + le_buffer[0] |= (u8)((device->buffer[3]&0x1)<<7); + + le_buffer[1] = (u8)(device->buffer[4]>>1); + le_buffer[1] |= (u8)((device->buffer[5]&0x1)<<7); + + val = le16_to_cpu(get_unaligned((__le16 *)(le_buffer))); + + input_report_abs(inputdev, ABS_Y, val); + + + /* + * Shift the button byte right by one to + * make it look like the standard report + */ + buttonbyte = (device->buffer[5])>>1; + }else{ + + val = le16_to_cpu(get_unaligned((__le16 *) (&(device->buffer[1])))); + input_report_abs(inputdev, ABS_X, val); + + val = le16_to_cpu(get_unaligned((__le16 *) (&(device->buffer[3])))); + input_report_abs(inputdev, ABS_Y, val); + + buttonbyte = device->buffer[5]; + + } + + + /* BUTTONS and PROXIMITY */ + if (buttonbyte& MASK_INRANGE){ + val = 1; + }else{ + val=0; + } + input_report_abs(inputdev, ABS_DISTANCE, val); + + /* Convert buttons, only 4 bits possible */ + val = buttonbyte&0x0F; +#ifdef USE_BUTTONS + for ( i=0;i<5;i++){ + input_report_key(inputdev, BTN_DIGI+i,val&(1<<i)); + } +#else + /* We don't apply any meaning to the bitmask, just report */ + input_event(inputdev, EV_MSC, MSC_SERIAL, val); +#endif + /* TRANSDUCER */ + input_report_abs(inputdev, ABS_MISC, device->buffer[6]); + + } + } + + /* Everybody gets report ID's */ + input_event(inputdev, EV_MSC, MSC_RAW, device->buffer[0]); + + /* Sync it up */ + input_sync(inputdev); + + resubmit: + rc = usb_submit_urb(urbinfo, GFP_ATOMIC); + if (rc != 0) { + err("usb_submit_urb failed rc=0x%x",rc); + } + +} + +/* + * The probe routine. This is called when the kernel find the matching USB + * vendor/product. We do the following: + * + * - Allocate mem for a local structure to manage the device + * - Request a HID Report Descriptor from the device and parse it to + * find out the device parameters + * - Create an input device and assign it attributes + * - Allocate an URB so the device can talk to us when the input + * queue is open + */ +static int gtco_probe(struct usb_interface *usbinterface, + const struct usb_device_id *id) +{ + + struct gtco *device = NULL; + char path[PATHLENGTH]; + struct input_dev *inputdev; + struct hid_descriptor *hid_desc; + char *report; + int result=0, retry; + struct usb_endpoint_descriptor *endpoint; + + /* Allocate memory for device structure */ + device = kzalloc(sizeof(struct gtco), GFP_KERNEL); + if (device == NULL) { + err("No more memory"); + return -ENOMEM; + } + + + device->inputdevice = input_allocate_device(); + if (!device->inputdevice){ + kfree(device); + err("No more memory"); + return -ENOMEM; + } + + /* Get pointer to the input device */ + inputdev = device->inputdevice; + + /* Save interface information */ + device->usbdev = usb_get_dev(interface_to_usbdev(usbinterface)); + + + /* Allocate some data for incoming reports */ + device->buffer = usb_buffer_alloc(device->usbdev, REPORT_MAX_SIZE, + GFP_KERNEL, &(device->buf_dma)); + if (!device->buffer){ + input_free_device(device->inputdevice); + kfree(device); + err("No more memory"); + return -ENOMEM; + } + + /* Allocate URB for reports */ + device->urbinfo = usb_alloc_urb(0, GFP_KERNEL); + if (!device->urbinfo) { + usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, + device->buffer, device->buf_dma); + input_free_device(device->inputdevice); + kfree(device); + err("No more memory"); + return -ENOMEM; + } + + + /* + * The endpoint is always altsetting 0, we know this since we know + * this device only has one interrupt endpoint + */ + endpoint = &usbinterface->altsetting[0].endpoint[0].desc; + + /* Some debug */ + dbg("gtco # interfaces: %d",usbinterface->num_altsetting); + dbg("num endpoints: %d",usbinterface->cur_altsetting->desc.bNumEndpoints); + dbg("interface class: %d",usbinterface->cur_altsetting->desc.bInterfaceClass); + dbg("endpoint: attribute:0x%x type:0x%x",endpoint->bmAttributes,endpoint->bDescriptorType); + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + dbg("endpoint: we have interrupt endpoint\n"); + + dbg("endpoint extra len:%d ",usbinterface->altsetting[0].extralen); + + + + /* + * Find the HID descriptor so we can find out the size of the + * HID report descriptor + */ + if (usb_get_extra_descriptor(usbinterface->cur_altsetting, + HID_DEVICE_TYPE,&hid_desc) != 0){ + err("Can't retrieve exta USB descriptor to get hid report descriptor length"); + usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, + device->buffer, device->buf_dma); + input_free_device(device->inputdevice); + kfree(device); + return -EIO; + } + + dbg("Extra descriptor success: type:%d len:%d", + hid_desc->bDescriptorType, hid_desc->wDescriptorLength); + + if (!(report = kzalloc(hid_desc->wDescriptorLength, GFP_KERNEL))) { + usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, + device->buffer, device->buf_dma); + + input_free_device(device->inputdevice); + kfree(device); + err("No more memory"); + return -ENOMEM; + } + + /* Couple of tries to get reply */ + for (retry=0;retry<3;retry++) { + result = usb_control_msg(device->usbdev, + usb_rcvctrlpipe(device->usbdev, 0), + USB_REQ_GET_DESCRIPTOR, + USB_RECIP_INTERFACE | USB_DIR_IN, + (REPORT_DEVICE_TYPE << 8), + 0, /* interface */ + report, + hid_desc->wDescriptorLength, + 5000); /* 5 secs */ + + if (result == hid_desc->wDescriptorLength) + break; + } + + /* If we didn't get the report, fail */ + dbg("usb_control_msg result: :%d", result); + if (result != hid_desc->wDescriptorLength){ + kfree(report); + usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, + device->buffer, device->buf_dma); + input_free_device(device->inputdevice); + kfree(device); + err("Failed to get HID Report Descriptor of size: %d", + hid_desc->wDescriptorLength); + return -EIO; + } + + + /* Now we parse the report */ + parse_hid_report_descriptor(device,report,result); + + /* Now we delete it */ + kfree(report); + + /* Create a device file node */ + usb_make_path(device->usbdev, path, PATHLENGTH); + sprintf(device->usbpath, "%s/input0", path); + + + /* Set Input device functions */ + inputdev->open = gtco_input_open; + inputdev->close = gtco_input_close; + + /* Set input device information */ + inputdev->name = "GTCO_CalComp"; + inputdev->phys = device->usbpath; + inputdev->private = device; + + + /* Now set up all the input device capabilities */ + gtco_setup_caps(inputdev); + + /* Set input device required ID information */ + usb_to_input_id(device->usbdev, &device->inputdevice->id); + inputdev->cdev.dev = &usbinterface->dev; + + /* Setup the URB, it will be posted later on open of input device */ + endpoint = &usbinterface->altsetting[0].endpoint[0].desc; + + usb_fill_int_urb(device->urbinfo, + device->usbdev, + usb_rcvintpipe(device->usbdev, + endpoint->bEndpointAddress), + device->buffer, + REPORT_MAX_SIZE, + gtco_urb_callback, + device, + endpoint->bInterval); + + device->urbinfo->transfer_dma = device->buf_dma; + device->urbinfo->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + + /* Save device pointer in USB interface device */ + usb_set_intfdata(usbinterface, device); + + /* All done, now register the input device */ + input_register_device(inputdev); + + info( "gtco driver created usb: %s\n", path); + return 0; + +} + +/* + * This function is a standard USB function called when the USB device + * is disconnected. We will get rid of the URV, de-register the input + * device, and free up allocated memory + */ +static void gtco_disconnect(struct usb_interface *interface) +{ + + /* Grab private device ptr */ + struct gtco *device = usb_get_intfdata (interface); + struct input_dev *inputdev; + + inputdev = device->inputdevice; + + /* Now reverse all the registration stuff */ + if (device) { + input_unregister_device(inputdev); + usb_kill_urb(device->urbinfo); + usb_free_urb(device->urbinfo); + usb_buffer_free(device->usbdev, REPORT_MAX_SIZE, + device->buffer, device->buf_dma); + kfree(device); + } + + info("gtco driver disconnected"); +} + + +/* STANDARD MODULE LOAD ROUTINES */ + +static struct usb_driver gtco_driverinfo_table = { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)) + .owner = THIS_MODULE, +#endif + .name = "gtco", + .id_table = gtco_usbid_table, + .probe = gtco_probe, + .disconnect = gtco_disconnect, +}; +/* + * Register this module with the USB subsystem + */ +static int __init gtco_init(void) +{ + int rc; + rc = usb_register(>co_driverinfo_table); + if (rc) { + err("usb_register() failed rc=0x%x", rc); + } + printk("GTCO usb driver version: %s",GTCO_VERSION); + return rc; +} + +/* + * Deregister this module with the USB subsystem + */ +static void __exit gtco_exit(void) +{ + usb_deregister(>co_driverinfo_table); +} + +module_init (gtco_init); +module_exit (gtco_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index e07a304..84983d1 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -768,6 +768,9 @@ void usbhid_init_reports(struct hid_device *hid) #define USB_VENDOR_ID_PANTHERLORD 0x0810 #define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 +#define USB_VENDOR_ID_SONY 0x054c +#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 + /* * Alphabetically sorted blacklist by quirk type. */ @@ -949,6 +952,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, + { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, + { 0, 0 } }; @@ -1013,6 +1018,32 @@ static void hid_fixup_cymotion_descriptor(char *rdesc, int rsize) } } +/* + * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller + * to "operational". Without this, the ps3 controller will not report any + * events. + */ +static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) +{ + int result; + char *buf = kmalloc(18, GFP_KERNEL); + + if (!buf) + return; + + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + HID_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + (3 << 8) | 0xf2, ifnum, buf, 17, + USB_CTRL_GET_TIMEOUT); + + if (result < 0) + err("%s failed: %d\n", __func__, result); + + kfree(buf); +} + static struct hid_device *usb_hid_configure(struct usb_interface *intf) { struct usb_host_interface *interface = intf->cur_altsetting; @@ -1303,6 +1334,10 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) if ((hid->claimed & HID_CLAIMED_INPUT)) hid_ff_init(hid); + if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER) + hid_fixup_sony_ps3_controller(interface_to_usbdev(intf), + intf->cur_altsetting->desc.bInterfaceNumber); + printk(KERN_INFO); if (hid->claimed & HID_CLAIMED_INPUT) diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index c941853..15c70bd 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -269,7 +269,7 @@ static int idmouse_release(struct inode *inode, struct file *file) /* prevent a race condition with open() */ mutex_lock(&disconnect_mutex); - dev = (struct usb_idmouse *) file->private_data; + dev = file->private_data; if (dev == NULL) { mutex_unlock(&disconnect_mutex); @@ -304,17 +304,15 @@ static int idmouse_release(struct inode *inode, struct file *file) static ssize_t idmouse_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos) { - struct usb_idmouse *dev; + struct usb_idmouse *dev = file->private_data; int result; - dev = (struct usb_idmouse *) file->private_data; - /* lock this object */ - down (&dev->sem); + down(&dev->sem); /* verify that the device wasn't unplugged */ if (!dev->present) { - up (&dev->sem); + up(&dev->sem); return -ENODEV; } diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index 384fa37..fdf6847 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -69,7 +69,7 @@ struct rio_usb_data { char *obuf, *ibuf; /* transfer buffers */ char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ wait_queue_head_t wait_q; /* for timeouts */ - struct semaphore lock; /* general race avoidance */ + struct mutex lock; /* general race avoidance */ }; static struct rio_usb_data rio_instance; @@ -78,17 +78,17 @@ static int open_rio(struct inode *inode, struct file *file) { struct rio_usb_data *rio = &rio_instance; - down(&(rio->lock)); + mutex_lock(&(rio->lock)); if (rio->isopen || !rio->present) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return -EBUSY; } rio->isopen = 1; init_waitqueue_head(&rio->wait_q); - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); info("Rio opened."); @@ -117,7 +117,7 @@ ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd, int retries; int retval=0; - down(&(rio->lock)); + mutex_lock(&(rio->lock)); /* Sanity check to make sure rio is connected, powered, etc */ if ( rio == NULL || rio->present == 0 || @@ -257,7 +257,7 @@ ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd, err_out: - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return retval; } @@ -275,14 +275,17 @@ write_rio(struct file *file, const char __user *buffer, int result = 0; int maxretry; int errn = 0; + int intr; - down(&(rio->lock)); + intr = mutex_lock_interruptible(&(rio->lock)); + if (intr) + return -EINTR; /* Sanity check to make sure rio is connected, powered, etc */ if ( rio == NULL || rio->present == 0 || rio->rio_dev == NULL ) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return -ENODEV; } @@ -305,7 +308,7 @@ write_rio(struct file *file, const char __user *buffer, goto error; } if (signal_pending(current)) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return bytes_written ? bytes_written : -EINTR; } @@ -341,12 +344,12 @@ write_rio(struct file *file, const char __user *buffer, buffer += copy_size; } while (count > 0); - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return bytes_written ? bytes_written : -EIO; error: - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return errn; } @@ -361,14 +364,17 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) int result; int maxretry = 10; char *ibuf; + int intr; - down(&(rio->lock)); + intr = mutex_lock_interruptible(&(rio->lock)); + if (intr) + return -EINTR; /* Sanity check to make sure rio is connected, powered, etc */ if ( rio == NULL || rio->present == 0 || rio->rio_dev == NULL ) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return -ENODEV; } @@ -379,11 +385,11 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) while (count > 0) { if (signal_pending(current)) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return read_count ? read_count : -EINTR; } if (!rio->rio_dev) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return -ENODEV; } this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count; @@ -400,7 +406,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) count = this_read = partial; } else if (result == -ETIMEDOUT || result == 15) { /* FIXME: 15 ??? */ if (!maxretry--) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); err("read_rio: maxretry timeout"); return -ETIME; } @@ -409,18 +415,18 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) finish_wait(&rio->wait_q, &wait); continue; } else if (result != -EREMOTEIO) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); err("Read Whoops - result:%u partial:%u this_read:%u", result, partial, this_read); return -EIO; } else { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return (0); } if (this_read) { if (copy_to_user(buffer, ibuf, this_read)) { - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return -EFAULT; } count -= this_read; @@ -428,7 +434,7 @@ read_rio(struct file *file, char __user *buffer, size_t count, loff_t * ppos) buffer += this_read; } } - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return read_count; } @@ -480,7 +486,7 @@ static int probe_rio(struct usb_interface *intf, } dbg("probe_rio: ibuf address:%p", rio->ibuf); - init_MUTEX(&(rio->lock)); + mutex_init(&(rio->lock)); usb_set_intfdata (intf, rio); rio->present = 1; @@ -496,12 +502,12 @@ static void disconnect_rio(struct usb_interface *intf) if (rio) { usb_deregister_dev(intf, &usb_rio_class); - down(&(rio->lock)); + mutex_lock(&(rio->lock)); if (rio->isopen) { rio->isopen = 0; /* better let it finish - the release will do whats needed */ rio->rio_dev = NULL; - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); return; } kfree(rio->ibuf); @@ -510,7 +516,7 @@ static void disconnect_rio(struct usb_interface *intf) info("USB Rio disconnected."); rio->present = 0; - up(&(rio->lock)); + mutex_unlock(&(rio->lock)); } } diff --git a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile index 3cf3ea3a..90c5953 100644 --- a/drivers/usb/mon/Makefile +++ b/drivers/usb/mon/Makefile @@ -2,7 +2,7 @@ # Makefile for USB Core files and filesystem # -usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_dma.o +usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o # This does not use CONFIG_USB_MON because we want this to use a tristate. obj-$(CONFIG_USB) += usbmon.o diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c new file mode 100644 index 0000000..c01dfe6 --- /dev/null +++ b/drivers/usb/mon/mon_bin.c @@ -0,0 +1,1172 @@ +/* + * The USB Monitor, inspired by Dave Harding's USBMon. + * + * This is a binary format reader. + * + * Copyright (C) 2006 Paolo Abeni (paolo.abeni@email.it) + * Copyright (C) 2006 Pete Zaitcev (zaitcev@redhat.com) + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/usb.h> +#include <linux/poll.h> +#include <linux/compat.h> +#include <linux/mm.h> + +#include <asm/uaccess.h> + +#include "usb_mon.h" + +/* + * Defined by USB 2.0 clause 9.3, table 9.2. + */ +#define SETUP_LEN 8 + +/* ioctl macros */ +#define MON_IOC_MAGIC 0x92 + +#define MON_IOCQ_URB_LEN _IO(MON_IOC_MAGIC, 1) +/* #2 used to be MON_IOCX_URB, removed before it got into Linus tree */ +#define MON_IOCG_STATS _IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats) +#define MON_IOCT_RING_SIZE _IO(MON_IOC_MAGIC, 4) +#define MON_IOCQ_RING_SIZE _IO(MON_IOC_MAGIC, 5) +#define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get) +#define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch) +#define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8) +#ifdef CONFIG_COMPAT +#define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32) +#define MON_IOCX_MFETCH32 _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch32) +#endif + +/* + * Some architectures have enormous basic pages (16KB for ia64, 64KB for ppc). + * But it's all right. Just use a simple way to make sure the chunk is never + * smaller than a page. + * + * N.B. An application does not know our chunk size. + * + * Woops, get_zeroed_page() returns a single page. I guess we're stuck with + * page-sized chunks for the time being. + */ +#define CHUNK_SIZE PAGE_SIZE +#define CHUNK_ALIGN(x) (((x)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1)) + +/* + * The magic limit was calculated so that it allows the monitoring + * application to pick data once in two ticks. This way, another application, + * which presumably drives the bus, gets to hog CPU, yet we collect our data. + * If HZ is 100, a 480 mbit/s bus drives 614 KB every jiffy. USB has an + * enormous overhead built into the bus protocol, so we need about 1000 KB. + * + * This is still too much for most cases, where we just snoop a few + * descriptor fetches for enumeration. So, the default is a "reasonable" + * amount for systems with HZ=250 and incomplete bus saturation. + * + * XXX What about multi-megabyte URBs which take minutes to transfer? + */ +#define BUFF_MAX CHUNK_ALIGN(1200*1024) +#define BUFF_DFL CHUNK_ALIGN(300*1024) +#define BUFF_MIN CHUNK_ALIGN(8*1024) + +/* + * The per-event API header (2 per URB). + * + * This structure is seen in userland as defined by the documentation. + */ +struct mon_bin_hdr { + u64 id; /* URB ID - from submission to callback */ + unsigned char type; /* Same as in text API; extensible. */ + unsigned char xfer_type; /* ISO, Intr, Control, Bulk */ + unsigned char epnum; /* Endpoint number and transfer direction */ + unsigned char devnum; /* Device address */ + unsigned short busnum; /* Bus number */ + char flag_setup; + char flag_data; + s64 ts_sec; /* gettimeofday */ + s32 ts_usec; /* gettimeofday */ + int status; + unsigned int len_urb; /* Length of data (submitted or actual) */ + unsigned int len_cap; /* Delivered length */ + unsigned char setup[SETUP_LEN]; /* Only for Control S-type */ +}; + +/* per file statistic */ +struct mon_bin_stats { + u32 queued; + u32 dropped; +}; + +struct mon_bin_get { + struct mon_bin_hdr __user *hdr; /* Only 48 bytes, not 64. */ + void __user *data; + size_t alloc; /* Length of data (can be zero) */ +}; + +struct mon_bin_mfetch { + u32 __user *offvec; /* Vector of events fetched */ + u32 nfetch; /* Number of events to fetch (out: fetched) */ + u32 nflush; /* Number of events to flush */ +}; + +#ifdef CONFIG_COMPAT +struct mon_bin_get32 { + u32 hdr32; + u32 data32; + u32 alloc32; +}; + +struct mon_bin_mfetch32 { + u32 offvec32; + u32 nfetch32; + u32 nflush32; +}; +#endif + +/* Having these two values same prevents wrapping of the mon_bin_hdr */ +#define PKT_ALIGN 64 +#define PKT_SIZE 64 + +/* max number of USB bus supported */ +#define MON_BIN_MAX_MINOR 128 + +/* + * The buffer: map of used pages. + */ +struct mon_pgmap { + struct page *pg; + unsigned char *ptr; /* XXX just use page_to_virt everywhere? */ +}; + +/* + * This gets associated with an open file struct. + */ +struct mon_reader_bin { + /* The buffer: one per open. */ + spinlock_t b_lock; /* Protect b_cnt, b_in */ + unsigned int b_size; /* Current size of the buffer - bytes */ + unsigned int b_cnt; /* Bytes used */ + unsigned int b_in, b_out; /* Offsets into buffer - bytes */ + unsigned int b_read; /* Amount of read data in curr. pkt. */ + struct mon_pgmap *b_vec; /* The map array */ + wait_queue_head_t b_wait; /* Wait for data here */ + + struct mutex fetch_lock; /* Protect b_read, b_out */ + int mmap_active; + + /* A list of these is needed for "bus 0". Some time later. */ + struct mon_reader r; + + /* Stats */ + unsigned int cnt_lost; +}; + +static inline struct mon_bin_hdr *MON_OFF2HDR(const struct mon_reader_bin *rp, + unsigned int offset) +{ + return (struct mon_bin_hdr *) + (rp->b_vec[offset / CHUNK_SIZE].ptr + offset % CHUNK_SIZE); +} + +#define MON_RING_EMPTY(rp) ((rp)->b_cnt == 0) + +static dev_t mon_bin_dev0; +static struct cdev mon_bin_cdev; + +static void mon_buff_area_fill(const struct mon_reader_bin *rp, + unsigned int offset, unsigned int size); +static int mon_bin_wait_event(struct file *file, struct mon_reader_bin *rp); +static int mon_alloc_buff(struct mon_pgmap *map, int npages); +static void mon_free_buff(struct mon_pgmap *map, int npages); + +/* + * This is a "chunked memcpy". It does not manipulate any counters. + * But it returns the new offset for repeated application. + */ +unsigned int mon_copy_to_buff(const struct mon_reader_bin *this, + unsigned int off, const unsigned char *from, unsigned int length) +{ + unsigned int step_len; + unsigned char *buf; + unsigned int in_page; + + while (length) { + /* + * Determine step_len. + */ + step_len = length; + in_page = CHUNK_SIZE - (off & (CHUNK_SIZE-1)); + if (in_page < step_len) + step_len = in_page; + + /* + * Copy data and advance pointers. + */ + buf = this->b_vec[off / CHUNK_SIZE].ptr + off % CHUNK_SIZE; + memcpy(buf, from, step_len); + if ((off += step_len) >= this->b_size) off = 0; + from += step_len; + length -= step_len; + } + return off; +} + +/* + * This is a little worse than the above because it's "chunked copy_to_user". + * The return value is an error code, not an offset. + */ +static int copy_from_buf(const struct mon_reader_bin *this, unsigned int off, + char __user *to, int length) +{ + unsigned int step_len; + unsigned char *buf; + unsigned int in_page; + + while (length) { + /* + * Determine step_len. + */ + step_len = length; + in_page = CHUNK_SIZE - (off & (CHUNK_SIZE-1)); + if (in_page < step_len) + step_len = in_page; + + /* + * Copy data and advance pointers. + */ + buf = this->b_vec[off / CHUNK_SIZE].ptr + off % CHUNK_SIZE; + if (copy_to_user(to, buf, step_len)) + return -EINVAL; + if ((off += step_len) >= this->b_size) off = 0; + to += step_len; + length -= step_len; + } + return 0; +} + +/* + * Allocate an (aligned) area in the buffer. + * This is called under b_lock. + * Returns ~0 on failure. + */ +static unsigned int mon_buff_area_alloc(struct mon_reader_bin *rp, + unsigned int size) +{ + unsigned int offset; + + size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + if (rp->b_cnt + size > rp->b_size) + return ~0; + offset = rp->b_in; + rp->b_cnt += size; + if ((rp->b_in += size) >= rp->b_size) + rp->b_in -= rp->b_size; + return offset; +} + +/* + * This is the same thing as mon_buff_area_alloc, only it does not allow + * buffers to wrap. This is needed by applications which pass references + * into mmap-ed buffers up their stacks (libpcap can do that). + * + * Currently, we always have the header stuck with the data, although + * it is not strictly speaking necessary. + * + * When a buffer would wrap, we place a filler packet to mark the space. + */ +static unsigned int mon_buff_area_alloc_contiguous(struct mon_reader_bin *rp, + unsigned int size) +{ + unsigned int offset; + unsigned int fill_size; + + size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + if (rp->b_cnt + size > rp->b_size) + return ~0; + if (rp->b_in + size > rp->b_size) { + /* + * This would wrap. Find if we still have space after + * skipping to the end of the buffer. If we do, place + * a filler packet and allocate a new packet. + */ + fill_size = rp->b_size - rp->b_in; + if (rp->b_cnt + size + fill_size > rp->b_size) + return ~0; + mon_buff_area_fill(rp, rp->b_in, fill_size); + + offset = 0; + rp->b_in = size; + rp->b_cnt += size + fill_size; + } else if (rp->b_in + size == rp->b_size) { + offset = rp->b_in; + rp->b_in = 0; + rp->b_cnt += size; + } else { + offset = rp->b_in; + rp->b_in += size; + rp->b_cnt += size; + } + return offset; +} + +/* + * Return a few (kilo-)bytes to the head of the buffer. + * This is used if a DMA fetch fails. + */ +static void mon_buff_area_shrink(struct mon_reader_bin *rp, unsigned int size) +{ + + size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + rp->b_cnt -= size; + if (rp->b_in < size) + rp->b_in += rp->b_size; + rp->b_in -= size; +} + +/* + * This has to be called under both b_lock and fetch_lock, because + * it accesses both b_cnt and b_out. + */ +static void mon_buff_area_free(struct mon_reader_bin *rp, unsigned int size) +{ + + size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + rp->b_cnt -= size; + if ((rp->b_out += size) >= rp->b_size) + rp->b_out -= rp->b_size; +} + +static void mon_buff_area_fill(const struct mon_reader_bin *rp, + unsigned int offset, unsigned int size) +{ + struct mon_bin_hdr *ep; + + ep = MON_OFF2HDR(rp, offset); + memset(ep, 0, PKT_SIZE); + ep->type = '@'; + ep->len_cap = size - PKT_SIZE; +} + +static inline char mon_bin_get_setup(unsigned char *setupb, + const struct urb *urb, char ev_type) +{ + + if (!usb_pipecontrol(urb->pipe) || ev_type != 'S') + return '-'; + + if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP) + return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN); + if (urb->setup_packet == NULL) + return 'Z'; + + memcpy(setupb, urb->setup_packet, SETUP_LEN); + return 0; +} + +static char mon_bin_get_data(const struct mon_reader_bin *rp, + unsigned int offset, struct urb *urb, unsigned int length) +{ + + if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) { + mon_dmapeek_vec(rp, offset, urb->transfer_dma, length); + return 0; + } + + if (urb->transfer_buffer == NULL) + return 'Z'; + + mon_copy_to_buff(rp, offset, urb->transfer_buffer, length); + return 0; +} + +static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, + char ev_type) +{ + unsigned long flags; + struct timeval ts; + unsigned int urb_length; + unsigned int offset; + unsigned int length; + struct mon_bin_hdr *ep; + char data_tag = 0; + + do_gettimeofday(&ts); + + spin_lock_irqsave(&rp->b_lock, flags); + + /* + * Find the maximum allowable length, then allocate space. + */ + urb_length = (ev_type == 'S') ? + urb->transfer_buffer_length : urb->actual_length; + length = urb_length; + + if (length >= rp->b_size/5) + length = rp->b_size/5; + + if (usb_pipein(urb->pipe)) { + if (ev_type == 'S') { + length = 0; + data_tag = '<'; + } + } else { + if (ev_type == 'C') { + length = 0; + data_tag = '>'; + } + } + + if (rp->mmap_active) + offset = mon_buff_area_alloc_contiguous(rp, length + PKT_SIZE); + else + offset = mon_buff_area_alloc(rp, length + PKT_SIZE); + if (offset == ~0) { + rp->cnt_lost++; + spin_unlock_irqrestore(&rp->b_lock, flags); + return; + } + + ep = MON_OFF2HDR(rp, offset); + if ((offset += PKT_SIZE) >= rp->b_size) offset = 0; + + /* + * Fill the allocated area. + */ + memset(ep, 0, PKT_SIZE); + ep->type = ev_type; + ep->xfer_type = usb_pipetype(urb->pipe); + /* We use the fact that usb_pipein() returns 0x80 */ + ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe); + ep->devnum = usb_pipedevice(urb->pipe); + ep->busnum = rp->r.m_bus->u_bus->busnum; + ep->id = (unsigned long) urb; + ep->ts_sec = ts.tv_sec; + ep->ts_usec = ts.tv_usec; + ep->status = urb->status; + ep->len_urb = urb_length; + ep->len_cap = length; + + ep->flag_setup = mon_bin_get_setup(ep->setup, urb, ev_type); + if (length != 0) { + ep->flag_data = mon_bin_get_data(rp, offset, urb, length); + if (ep->flag_data != 0) { /* Yes, it's 0x00, not '0' */ + ep->len_cap = 0; + mon_buff_area_shrink(rp, length); + } + } else { + ep->flag_data = data_tag; + } + + spin_unlock_irqrestore(&rp->b_lock, flags); + + wake_up(&rp->b_wait); +} + +static void mon_bin_submit(void *data, struct urb *urb) +{ + struct mon_reader_bin *rp = data; + mon_bin_event(rp, urb, 'S'); +} + +static void mon_bin_complete(void *data, struct urb *urb) +{ + struct mon_reader_bin *rp = data; + mon_bin_event(rp, urb, 'C'); +} + +static void mon_bin_error(void *data, struct urb *urb, int error) +{ + struct mon_reader_bin *rp = data; + unsigned long flags; + unsigned int offset; + struct mon_bin_hdr *ep; + + spin_lock_irqsave(&rp->b_lock, flags); + + offset = mon_buff_area_alloc(rp, PKT_SIZE); + if (offset == ~0) { + /* Not incrementing cnt_lost. Just because. */ + spin_unlock_irqrestore(&rp->b_lock, flags); + return; + } + + ep = MON_OFF2HDR(rp, offset); + + memset(ep, 0, PKT_SIZE); + ep->type = 'E'; + ep->xfer_type = usb_pipetype(urb->pipe); + /* We use the fact that usb_pipein() returns 0x80 */ + ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe); + ep->devnum = usb_pipedevice(urb->pipe); + ep->busnum = rp->r.m_bus->u_bus->busnum; + ep->id = (unsigned long) urb; + ep->status = error; + + ep->flag_setup = '-'; + ep->flag_data = 'E'; + + spin_unlock_irqrestore(&rp->b_lock, flags); + + wake_up(&rp->b_wait); +} + +static int mon_bin_open(struct inode *inode, struct file *file) +{ + struct mon_bus *mbus; + struct usb_bus *ubus; + struct mon_reader_bin *rp; + size_t size; + int rc; + + mutex_lock(&mon_lock); + if ((mbus = mon_bus_lookup(iminor(inode))) == NULL) { + mutex_unlock(&mon_lock); + return -ENODEV; + } + if ((ubus = mbus->u_bus) == NULL) { + printk(KERN_ERR TAG ": consistency error on open\n"); + mutex_unlock(&mon_lock); + return -ENODEV; + } + + rp = kzalloc(sizeof(struct mon_reader_bin), GFP_KERNEL); + if (rp == NULL) { + rc = -ENOMEM; + goto err_alloc; + } + spin_lock_init(&rp->b_lock); + init_waitqueue_head(&rp->b_wait); + mutex_init(&rp->fetch_lock); + + rp->b_size = BUFF_DFL; + + size = sizeof(struct mon_pgmap) * (rp->b_size/CHUNK_SIZE); + if ((rp->b_vec = kzalloc(size, GFP_KERNEL)) == NULL) { + rc = -ENOMEM; + goto err_allocvec; + } + + if ((rc = mon_alloc_buff(rp->b_vec, rp->b_size/CHUNK_SIZE)) < 0) + goto err_allocbuff; + + rp->r.m_bus = mbus; + rp->r.r_data = rp; + rp->r.rnf_submit = mon_bin_submit; + rp->r.rnf_error = mon_bin_error; + rp->r.rnf_complete = mon_bin_complete; + + mon_reader_add(mbus, &rp->r); + + file->private_data = rp; + mutex_unlock(&mon_lock); + return 0; + +err_allocbuff: + kfree(rp->b_vec); +err_allocvec: + kfree(rp); +err_alloc: + mutex_unlock(&mon_lock); + return rc; +} + +/* + * Extract an event from buffer and copy it to user space. + * Wait if there is no event ready. + * Returns zero or error. + */ +static int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp, + struct mon_bin_hdr __user *hdr, void __user *data, unsigned int nbytes) +{ + unsigned long flags; + struct mon_bin_hdr *ep; + size_t step_len; + unsigned int offset; + int rc; + + mutex_lock(&rp->fetch_lock); + + if ((rc = mon_bin_wait_event(file, rp)) < 0) { + mutex_unlock(&rp->fetch_lock); + return rc; + } + + ep = MON_OFF2HDR(rp, rp->b_out); + + if (copy_to_user(hdr, ep, sizeof(struct mon_bin_hdr))) { + mutex_unlock(&rp->fetch_lock); + return -EFAULT; + } + + step_len = min(ep->len_cap, nbytes); + if ((offset = rp->b_out + PKT_SIZE) >= rp->b_size) offset = 0; + + if (copy_from_buf(rp, offset, data, step_len)) { + mutex_unlock(&rp->fetch_lock); + return -EFAULT; + } + + spin_lock_irqsave(&rp->b_lock, flags); + mon_buff_area_free(rp, PKT_SIZE + ep->len_cap); + spin_unlock_irqrestore(&rp->b_lock, flags); + rp->b_read = 0; + + mutex_unlock(&rp->fetch_lock); + return 0; +} + +static int mon_bin_release(struct inode *inode, struct file *file) +{ + struct mon_reader_bin *rp = file->private_data; + struct mon_bus* mbus = rp->r.m_bus; + + mutex_lock(&mon_lock); + + if (mbus->nreaders <= 0) { + printk(KERN_ERR TAG ": consistency error on close\n"); + mutex_unlock(&mon_lock); + return 0; + } + mon_reader_del(mbus, &rp->r); + + mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); + kfree(rp->b_vec); + kfree(rp); + + mutex_unlock(&mon_lock); + return 0; +} + +static ssize_t mon_bin_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct mon_reader_bin *rp = file->private_data; + unsigned long flags; + struct mon_bin_hdr *ep; + unsigned int offset; + size_t step_len; + char *ptr; + ssize_t done = 0; + int rc; + + mutex_lock(&rp->fetch_lock); + + if ((rc = mon_bin_wait_event(file, rp)) < 0) { + mutex_unlock(&rp->fetch_lock); + return rc; + } + + ep = MON_OFF2HDR(rp, rp->b_out); + + if (rp->b_read < sizeof(struct mon_bin_hdr)) { + step_len = min(nbytes, sizeof(struct mon_bin_hdr) - rp->b_read); + ptr = ((char *)ep) + rp->b_read; + if (step_len && copy_to_user(buf, ptr, step_len)) { + mutex_unlock(&rp->fetch_lock); + return -EFAULT; + } + nbytes -= step_len; + buf += step_len; + rp->b_read += step_len; + done += step_len; + } + + if (rp->b_read >= sizeof(struct mon_bin_hdr)) { + step_len = min(nbytes, (size_t)ep->len_cap); + offset = rp->b_out + PKT_SIZE; + offset += rp->b_read - sizeof(struct mon_bin_hdr); + if (offset >= rp->b_size) + offset -= rp->b_size; + if (copy_from_buf(rp, offset, buf, step_len)) { + mutex_unlock(&rp->fetch_lock); + return -EFAULT; + } + nbytes -= step_len; + buf += step_len; + rp->b_read += step_len; + done += step_len; + } + + /* + * Check if whole packet was read, and if so, jump to the next one. + */ + if (rp->b_read >= sizeof(struct mon_bin_hdr) + ep->len_cap) { + spin_lock_irqsave(&rp->b_lock, flags); + mon_buff_area_free(rp, PKT_SIZE + ep->len_cap); + spin_unlock_irqrestore(&rp->b_lock, flags); + rp->b_read = 0; + } + + mutex_unlock(&rp->fetch_lock); + return done; +} + +/* + * Remove at most nevents from chunked buffer. + * Returns the number of removed events. + */ +static int mon_bin_flush(struct mon_reader_bin *rp, unsigned nevents) +{ + unsigned long flags; + struct mon_bin_hdr *ep; + int i; + + mutex_lock(&rp->fetch_lock); + spin_lock_irqsave(&rp->b_lock, flags); + for (i = 0; i < nevents; ++i) { + if (MON_RING_EMPTY(rp)) + break; + + ep = MON_OFF2HDR(rp, rp->b_out); + mon_buff_area_free(rp, PKT_SIZE + ep->len_cap); + } + spin_unlock_irqrestore(&rp->b_lock, flags); + rp->b_read = 0; + mutex_unlock(&rp->fetch_lock); + return i; +} + +/* + * Fetch at most max event offsets into the buffer and put them into vec. + * The events are usually freed later with mon_bin_flush. + * Return the effective number of events fetched. + */ +static int mon_bin_fetch(struct file *file, struct mon_reader_bin *rp, + u32 __user *vec, unsigned int max) +{ + unsigned int cur_out; + unsigned int bytes, avail; + unsigned int size; + unsigned int nevents; + struct mon_bin_hdr *ep; + unsigned long flags; + int rc; + + mutex_lock(&rp->fetch_lock); + + if ((rc = mon_bin_wait_event(file, rp)) < 0) { + mutex_unlock(&rp->fetch_lock); + return rc; + } + + spin_lock_irqsave(&rp->b_lock, flags); + avail = rp->b_cnt; + spin_unlock_irqrestore(&rp->b_lock, flags); + + cur_out = rp->b_out; + nevents = 0; + bytes = 0; + while (bytes < avail) { + if (nevents >= max) + break; + + ep = MON_OFF2HDR(rp, cur_out); + if (put_user(cur_out, &vec[nevents])) { + mutex_unlock(&rp->fetch_lock); + return -EFAULT; + } + + nevents++; + size = ep->len_cap + PKT_SIZE; + size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + if ((cur_out += size) >= rp->b_size) + cur_out -= rp->b_size; + bytes += size; + } + + mutex_unlock(&rp->fetch_lock); + return nevents; +} + +/* + * Count events. This is almost the same as the above mon_bin_fetch, + * only we do not store offsets into user vector, and we have no limit. + */ +static int mon_bin_queued(struct mon_reader_bin *rp) +{ + unsigned int cur_out; + unsigned int bytes, avail; + unsigned int size; + unsigned int nevents; + struct mon_bin_hdr *ep; + unsigned long flags; + + mutex_lock(&rp->fetch_lock); + + spin_lock_irqsave(&rp->b_lock, flags); + avail = rp->b_cnt; + spin_unlock_irqrestore(&rp->b_lock, flags); + + cur_out = rp->b_out; + nevents = 0; + bytes = 0; + while (bytes < avail) { + ep = MON_OFF2HDR(rp, cur_out); + + nevents++; + size = ep->len_cap + PKT_SIZE; + size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + if ((cur_out += size) >= rp->b_size) + cur_out -= rp->b_size; + bytes += size; + } + + mutex_unlock(&rp->fetch_lock); + return nevents; +} + +/* + */ +static int mon_bin_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mon_reader_bin *rp = file->private_data; + // struct mon_bus* mbus = rp->r.m_bus; + int ret = 0; + struct mon_bin_hdr *ep; + unsigned long flags; + + switch (cmd) { + + case MON_IOCQ_URB_LEN: + /* + * N.B. This only returns the size of data, without the header. + */ + spin_lock_irqsave(&rp->b_lock, flags); + if (!MON_RING_EMPTY(rp)) { + ep = MON_OFF2HDR(rp, rp->b_out); + ret = ep->len_cap; + } + spin_unlock_irqrestore(&rp->b_lock, flags); + break; + + case MON_IOCQ_RING_SIZE: + ret = rp->b_size; + break; + + case MON_IOCT_RING_SIZE: + /* + * Changing the buffer size will flush it's contents; the new + * buffer is allocated before releasing the old one to be sure + * the device will stay functional also in case of memory + * pressure. + */ + { + int size; + struct mon_pgmap *vec; + + if (arg < BUFF_MIN || arg > BUFF_MAX) + return -EINVAL; + + size = CHUNK_ALIGN(arg); + if ((vec = kzalloc(sizeof(struct mon_pgmap) * (size/CHUNK_SIZE), + GFP_KERNEL)) == NULL) { + ret = -ENOMEM; + break; + } + + ret = mon_alloc_buff(vec, size/CHUNK_SIZE); + if (ret < 0) { + kfree(vec); + break; + } + + mutex_lock(&rp->fetch_lock); + spin_lock_irqsave(&rp->b_lock, flags); + mon_free_buff(rp->b_vec, size/CHUNK_SIZE); + kfree(rp->b_vec); + rp->b_vec = vec; + rp->b_size = size; + rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0; + rp->cnt_lost = 0; + spin_unlock_irqrestore(&rp->b_lock, flags); + mutex_unlock(&rp->fetch_lock); + } + break; + + case MON_IOCH_MFLUSH: + ret = mon_bin_flush(rp, arg); + break; + + case MON_IOCX_GET: + { + struct mon_bin_get getb; + + if (copy_from_user(&getb, (void __user *)arg, + sizeof(struct mon_bin_get))) + return -EFAULT; + + if (getb.alloc > 0x10000000) /* Want to cast to u32 */ + return -EINVAL; + ret = mon_bin_get_event(file, rp, + getb.hdr, getb.data, (unsigned int)getb.alloc); + } + break; + +#ifdef CONFIG_COMPAT + case MON_IOCX_GET32: { + struct mon_bin_get32 getb; + + if (copy_from_user(&getb, (void __user *)arg, + sizeof(struct mon_bin_get32))) + return -EFAULT; + + ret = mon_bin_get_event(file, rp, + compat_ptr(getb.hdr32), compat_ptr(getb.data32), + getb.alloc32); + } + break; +#endif + + case MON_IOCX_MFETCH: + { + struct mon_bin_mfetch mfetch; + struct mon_bin_mfetch __user *uptr; + + uptr = (struct mon_bin_mfetch __user *)arg; + + if (copy_from_user(&mfetch, uptr, sizeof(mfetch))) + return -EFAULT; + + if (mfetch.nflush) { + ret = mon_bin_flush(rp, mfetch.nflush); + if (ret < 0) + return ret; + if (put_user(ret, &uptr->nflush)) + return -EFAULT; + } + ret = mon_bin_fetch(file, rp, mfetch.offvec, mfetch.nfetch); + if (ret < 0) + return ret; + if (put_user(ret, &uptr->nfetch)) + return -EFAULT; + ret = 0; + } + break; + +#ifdef CONFIG_COMPAT + case MON_IOCX_MFETCH32: + { + struct mon_bin_mfetch32 mfetch; + struct mon_bin_mfetch32 __user *uptr; + + uptr = (struct mon_bin_mfetch32 __user *) compat_ptr(arg); + + if (copy_from_user(&mfetch, uptr, sizeof(mfetch))) + return -EFAULT; + + if (mfetch.nflush32) { + ret = mon_bin_flush(rp, mfetch.nflush32); + if (ret < 0) + return ret; + if (put_user(ret, &uptr->nflush32)) + return -EFAULT; + } + ret = mon_bin_fetch(file, rp, compat_ptr(mfetch.offvec32), + mfetch.nfetch32); + if (ret < 0) + return ret; + if (put_user(ret, &uptr->nfetch32)) + return -EFAULT; + ret = 0; + } + break; +#endif + + case MON_IOCG_STATS: { + struct mon_bin_stats __user *sp; + unsigned int nevents; + unsigned int ndropped; + + spin_lock_irqsave(&rp->b_lock, flags); + ndropped = rp->cnt_lost; + rp->cnt_lost = 0; + spin_unlock_irqrestore(&rp->b_lock, flags); + nevents = mon_bin_queued(rp); + + sp = (struct mon_bin_stats __user *)arg; + if (put_user(rp->cnt_lost, &sp->dropped)) + return -EFAULT; + if (put_user(nevents, &sp->queued)) + return -EFAULT; + + } + break; + + default: + return -ENOTTY; + } + + return ret; +} + +static unsigned int +mon_bin_poll(struct file *file, struct poll_table_struct *wait) +{ + struct mon_reader_bin *rp = file->private_data; + unsigned int mask = 0; + unsigned long flags; + + if (file->f_mode & FMODE_READ) + poll_wait(file, &rp->b_wait, wait); + + spin_lock_irqsave(&rp->b_lock, flags); + if (!MON_RING_EMPTY(rp)) + mask |= POLLIN | POLLRDNORM; /* readable */ + spin_unlock_irqrestore(&rp->b_lock, flags); + return mask; +} + +/* + * open and close: just keep track of how many times the device is + * mapped, to use the proper memory allocation function. + */ +static void mon_bin_vma_open(struct vm_area_struct *vma) +{ + struct mon_reader_bin *rp = vma->vm_private_data; + rp->mmap_active++; +} + +static void mon_bin_vma_close(struct vm_area_struct *vma) +{ + struct mon_reader_bin *rp = vma->vm_private_data; + rp->mmap_active--; +} + +/* + * Map ring pages to user space. + */ +struct page *mon_bin_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + struct mon_reader_bin *rp = vma->vm_private_data; + unsigned long offset, chunk_idx; + struct page *pageptr; + + offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + if (offset >= rp->b_size) + return NOPAGE_SIGBUS; + chunk_idx = offset / CHUNK_SIZE; + pageptr = rp->b_vec[chunk_idx].pg; + get_page(pageptr); + if (type) + *type = VM_FAULT_MINOR; + return pageptr; +} + +struct vm_operations_struct mon_bin_vm_ops = { + .open = mon_bin_vma_open, + .close = mon_bin_vma_close, + .nopage = mon_bin_vma_nopage, +}; + +int mon_bin_mmap(struct file *filp, struct vm_area_struct *vma) +{ + /* don't do anything here: "nopage" will set up page table entries */ + vma->vm_ops = &mon_bin_vm_ops; + vma->vm_flags |= VM_RESERVED; + vma->vm_private_data = filp->private_data; + mon_bin_vma_open(vma); + return 0; +} + +struct file_operations mon_fops_binary = { + .owner = THIS_MODULE, + .open = mon_bin_open, + .llseek = no_llseek, + .read = mon_bin_read, + /* .write = mon_text_write, */ + .poll = mon_bin_poll, + .ioctl = mon_bin_ioctl, + .release = mon_bin_release, +}; + +static int mon_bin_wait_event(struct file *file, struct mon_reader_bin *rp) +{ + DECLARE_WAITQUEUE(waita, current); + unsigned long flags; + + add_wait_queue(&rp->b_wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&rp->b_lock, flags); + while (MON_RING_EMPTY(rp)) { + spin_unlock_irqrestore(&rp->b_lock, flags); + + if (file->f_flags & O_NONBLOCK) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&rp->b_wait, &waita); + return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ + } + schedule(); + if (signal_pending(current)) { + remove_wait_queue(&rp->b_wait, &waita); + return -EINTR; + } + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&rp->b_lock, flags); + } + spin_unlock_irqrestore(&rp->b_lock, flags); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&rp->b_wait, &waita); + return 0; +} + +static int mon_alloc_buff(struct mon_pgmap *map, int npages) +{ + int n; + unsigned long vaddr; + + for (n = 0; n < npages; n++) { + vaddr = get_zeroed_page(GFP_KERNEL); + if (vaddr == 0) { + while (n-- != 0) + free_page((unsigned long) map[n].ptr); + return -ENOMEM; + } + map[n].ptr = (unsigned char *) vaddr; + map[n].pg = virt_to_page(vaddr); + } + return 0; +} + +static void mon_free_buff(struct mon_pgmap *map, int npages) +{ + int n; + + for (n = 0; n < npages; n++) + free_page((unsigned long) map[n].ptr); +} + +int __init mon_bin_init(void) +{ + int rc; + + rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon"); + if (rc < 0) + goto err_dev; + + cdev_init(&mon_bin_cdev, &mon_fops_binary); + mon_bin_cdev.owner = THIS_MODULE; + + rc = cdev_add(&mon_bin_cdev, mon_bin_dev0, MON_BIN_MAX_MINOR); + if (rc < 0) + goto err_add; + + return 0; + +err_add: + unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); +err_dev: + return rc; +} + +void __exit mon_bin_exit(void) +{ + cdev_del(&mon_bin_cdev); + unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); +} diff --git a/drivers/usb/mon/mon_dma.c b/drivers/usb/mon/mon_dma.c index ddcfc01..140cc80 100644 --- a/drivers/usb/mon/mon_dma.c +++ b/drivers/usb/mon/mon_dma.c @@ -48,6 +48,36 @@ char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len) local_irq_restore(flags); return 0; } + +void mon_dmapeek_vec(const struct mon_reader_bin *rp, + unsigned int offset, dma_addr_t dma_addr, unsigned int length) +{ + unsigned long flags; + unsigned int step_len; + struct page *pg; + unsigned char *map; + unsigned long page_off, page_len; + + local_irq_save(flags); + while (length) { + /* compute number of bytes we are going to copy in this page */ + step_len = length; + page_off = dma_addr & (PAGE_SIZE-1); + page_len = PAGE_SIZE - page_off; + if (page_len < step_len) + step_len = page_len; + + /* copy data and advance pointers */ + pg = phys_to_page(dma_addr); + map = kmap_atomic(pg, KM_IRQ0); + offset = mon_copy_to_buff(rp, offset, map + page_off, step_len); + kunmap_atomic(map, KM_IRQ0); + dma_addr += step_len; + length -= step_len; + } + local_irq_restore(flags); +} + #endif /* __i386__ */ #ifndef MON_HAS_UNMAP @@ -55,4 +85,11 @@ char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len) { return 'D'; } -#endif + +void mon_dmapeek_vec(const struct mon_reader_bin *rp, + unsigned int offset, dma_addr_t dma_addr, unsigned int length) +{ + ; +} + +#endif /* MON_HAS_UNMAP */ diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index 394bbf2..c9739e7 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -9,7 +9,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/usb.h> -#include <linux/debugfs.h> #include <linux/smp_lock.h> #include <linux/notifier.h> #include <linux/mutex.h> @@ -22,11 +21,10 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb); static void mon_stop(struct mon_bus *mbus); static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); static void mon_bus_drop(struct kref *r); -static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus); +static void mon_bus_init(struct usb_bus *ubus); DEFINE_MUTEX(mon_lock); -static struct dentry *mon_dir; /* /dbg/usbmon */ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ /* @@ -200,7 +198,7 @@ static void mon_stop(struct mon_bus *mbus) */ static void mon_bus_add(struct usb_bus *ubus) { - mon_bus_init(mon_dir, ubus); + mon_bus_init(ubus); } /* @@ -212,8 +210,8 @@ static void mon_bus_remove(struct usb_bus *ubus) mutex_lock(&mon_lock); list_del(&mbus->bus_link); - debugfs_remove(mbus->dent_t); - debugfs_remove(mbus->dent_s); + if (mbus->text_inited) + mon_text_del(mbus); mon_dissolve(mbus, ubus); kref_put(&mbus->ref, mon_bus_drop); @@ -281,13 +279,9 @@ static void mon_bus_drop(struct kref *r) * - refcount USB bus struct * - link */ -static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus) +static void mon_bus_init(struct usb_bus *ubus) { - struct dentry *d; struct mon_bus *mbus; - enum { NAMESZ = 10 }; - char name[NAMESZ]; - int rc; if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL) goto err_alloc; @@ -303,57 +297,54 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus) ubus->mon_bus = mbus; mbus->uses_dma = ubus->uses_dma; - rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_t; - d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text); - if (d == NULL) - goto err_create_t; - mbus->dent_t = d; - - rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_s; - d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat); - if (d == NULL) - goto err_create_s; - mbus->dent_s = d; + mbus->text_inited = mon_text_add(mbus, ubus); + // mon_bin_add(...) mutex_lock(&mon_lock); list_add_tail(&mbus->bus_link, &mon_buses); mutex_unlock(&mon_lock); return; -err_create_s: -err_print_s: - debugfs_remove(mbus->dent_t); -err_create_t: -err_print_t: - kfree(mbus); err_alloc: return; } +/* + * Search a USB bus by number. Notice that USB bus numbers start from one, + * which we may later use to identify "all" with zero. + * + * This function must be called with mon_lock held. + * + * This is obviously inefficient and may be revised in the future. + */ +struct mon_bus *mon_bus_lookup(unsigned int num) +{ + struct list_head *p; + struct mon_bus *mbus; + + list_for_each (p, &mon_buses) { + mbus = list_entry(p, struct mon_bus, bus_link); + if (mbus->u_bus->busnum == num) { + return mbus; + } + } + return NULL; +} + static int __init mon_init(void) { struct usb_bus *ubus; - struct dentry *mondir; + int rc; - mondir = debugfs_create_dir("usbmon", NULL); - if (IS_ERR(mondir)) { - printk(KERN_NOTICE TAG ": debugfs is not available\n"); - return -ENODEV; - } - if (mondir == NULL) { - printk(KERN_NOTICE TAG ": unable to create usbmon directory\n"); - return -ENODEV; - } - mon_dir = mondir; + if ((rc = mon_text_init()) != 0) + goto err_text; + if ((rc = mon_bin_init()) != 0) + goto err_bin; if (usb_mon_register(&mon_ops_0) != 0) { printk(KERN_NOTICE TAG ": unable to register with the core\n"); - debugfs_remove(mondir); - return -ENODEV; + rc = -ENODEV; + goto err_reg; } // MOD_INC_USE_COUNT(which_module?); @@ -361,10 +352,17 @@ static int __init mon_init(void) mutex_lock(&usb_bus_list_lock); list_for_each_entry (ubus, &usb_bus_list, bus_list) { - mon_bus_init(mondir, ubus); + mon_bus_init(ubus); } mutex_unlock(&usb_bus_list_lock); return 0; + +err_reg: + mon_bin_exit(); +err_bin: + mon_text_exit(); +err_text: + return rc; } static void __exit mon_exit(void) @@ -381,8 +379,8 @@ static void __exit mon_exit(void) mbus = list_entry(p, struct mon_bus, bus_link); list_del(p); - debugfs_remove(mbus->dent_t); - debugfs_remove(mbus->dent_s); + if (mbus->text_inited) + mon_text_del(mbus); /* * This never happens, because the open/close paths in @@ -401,7 +399,8 @@ static void __exit mon_exit(void) } mutex_unlock(&mon_lock); - debugfs_remove(mon_dir); + mon_text_exit(); + mon_bin_exit(); } module_init(mon_init); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 05cf2c9..d38a127 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -9,6 +9,7 @@ #include <linux/usb.h> #include <linux/time.h> #include <linux/mutex.h> +#include <linux/debugfs.h> #include <asm/uaccess.h> #include "usb_mon.h" @@ -63,6 +64,8 @@ struct mon_reader_text { char slab_name[SLAB_NAME_SZ]; }; +static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */ + static void mon_text_ctor(void *, struct kmem_cache *, unsigned long); /* @@ -436,7 +439,7 @@ static int mon_text_release(struct inode *inode, struct file *file) return 0; } -const struct file_operations mon_fops_text = { +static const struct file_operations mon_fops_text = { .owner = THIS_MODULE, .open = mon_text_open, .llseek = no_llseek, @@ -447,6 +450,47 @@ const struct file_operations mon_fops_text = { .release = mon_text_release, }; +int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) +{ + struct dentry *d; + enum { NAMESZ = 10 }; + char name[NAMESZ]; + int rc; + + rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_t; + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text); + if (d == NULL) + goto err_create_t; + mbus->dent_t = d; + + /* XXX The stats do not belong to here (text API), but oh well... */ + rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_s; + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat); + if (d == NULL) + goto err_create_s; + mbus->dent_s = d; + + return 1; + +err_create_s: +err_print_s: + debugfs_remove(mbus->dent_t); + mbus->dent_t = NULL; +err_create_t: +err_print_t: + return 0; +} + +void mon_text_del(struct mon_bus *mbus) +{ + debugfs_remove(mbus->dent_t); + debugfs_remove(mbus->dent_s); +} + /* * Slab interface: constructor. */ @@ -459,3 +503,24 @@ static void mon_text_ctor(void *mem, struct kmem_cache *slab, unsigned long sfla memset(mem, 0xe5, sizeof(struct mon_event_text)); } +int __init mon_text_init(void) +{ + struct dentry *mondir; + + mondir = debugfs_create_dir("usbmon", NULL); + if (IS_ERR(mondir)) { + printk(KERN_NOTICE TAG ": debugfs is not available\n"); + return -ENODEV; + } + if (mondir == NULL) { + printk(KERN_NOTICE TAG ": unable to create usbmon directory\n"); + return -ENODEV; + } + mon_dir = mondir; + return 0; +} + +void __exit mon_text_exit(void) +{ + debugfs_remove(mon_dir); +} diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index ab9d02d..4f949ce 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -17,9 +17,11 @@ struct mon_bus { struct list_head bus_link; spinlock_t lock; + struct usb_bus *u_bus; + + int text_inited; struct dentry *dent_s; /* Debugging file */ struct dentry *dent_t; /* Text interface file */ - struct usb_bus *u_bus; int uses_dma; /* Ref */ @@ -48,13 +50,35 @@ struct mon_reader { void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r); void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r); +struct mon_bus *mon_bus_lookup(unsigned int num); + +int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus); +void mon_text_del(struct mon_bus *mbus); +// void mon_bin_add(struct mon_bus *); + +int __init mon_text_init(void); +void __exit mon_text_exit(void); +int __init mon_bin_init(void); +void __exit mon_bin_exit(void); + /* - */ + * DMA interface. + * + * XXX The vectored side needs a serious re-thinking. Abstracting vectors, + * like in Paolo's original patch, produces a double pkmap. We need an idea. +*/ extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len); +struct mon_reader_bin; +extern void mon_dmapeek_vec(const struct mon_reader_bin *rp, + unsigned int offset, dma_addr_t dma_addr, unsigned int len); +extern unsigned int mon_copy_to_buff(const struct mon_reader_bin *rp, + unsigned int offset, const unsigned char *from, unsigned int len); + +/* + */ extern struct mutex mon_lock; -extern const struct file_operations mon_fops_text; extern const struct file_operations mon_fops_stat; #endif /* __USB_MON_H */ diff --git a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig index e081836..a2b94ef5 100644 --- a/drivers/usb/net/Kconfig +++ b/drivers/usb/net/Kconfig @@ -222,13 +222,15 @@ config USB_NET_MCS7830 adapters marketed under the DeLOCK brand. config USB_NET_RNDIS_HOST - tristate "Host for RNDIS devices (EXPERIMENTAL)" + tristate "Host for RNDIS and ActiveSync devices (EXPERIMENTAL)" depends on USB_USBNET && EXPERIMENTAL select USB_NET_CDCETHER help This option enables hosting "Remote NDIS" USB networking links, as encouraged by Microsoft (instead of CDC Ethernet!) for use in - various devices that may only support this protocol. + various devices that may only support this protocol. A variant + of this protocol (with even less public documentation) seems to + be at the root of Microsoft's "ActiveSync" too. Avoid using this protocol unless you have no better options. The protocol specification is incomplete, and is controlled by diff --git a/drivers/usb/net/cdc_ether.c b/drivers/usb/net/cdc_ether.c index 44a9154..e5cdafa 100644 --- a/drivers/usb/net/cdc_ether.c +++ b/drivers/usb/net/cdc_ether.c @@ -1,6 +1,7 @@ /* * CDC Ethernet based networking peripherals * Copyright (C) 2003-2005 by David Brownell + * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) * * 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 @@ -35,6 +36,29 @@ #include "usbnet.h" +#if defined(CONFIG_USB_NET_RNDIS_HOST) || defined(CONFIG_USB_NET_RNDIS_HOST_MODULE) + +static int is_rndis(struct usb_interface_descriptor *desc) +{ + return desc->bInterfaceClass == USB_CLASS_COMM + && desc->bInterfaceSubClass == 2 + && desc->bInterfaceProtocol == 0xff; +} + +static int is_activesync(struct usb_interface_descriptor *desc) +{ + return desc->bInterfaceClass == USB_CLASS_MISC + && desc->bInterfaceSubClass == 1 + && desc->bInterfaceProtocol == 1; +} + +#else + +#define is_rndis(desc) 0 +#define is_activesync(desc) 0 + +#endif + /* * probes control interface, claims data interface, collects the bulk * endpoints, activates data interface (if needed), maybe sets MTU. @@ -71,7 +95,8 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) /* this assumes that if there's a non-RNDIS vendor variant * of cdc-acm, it'll fail RNDIS requests cleanly. */ - rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff); + rndis = is_rndis(&intf->cur_altsetting->desc) + || is_activesync(&intf->cur_altsetting->desc); memset(info, 0, sizeof *info); info->control = intf; @@ -99,6 +124,23 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf) goto bad_desc; } break; + case USB_CDC_ACM_TYPE: + /* paranoia: disambiguate a "real" vendor-specific + * modem interface from an RNDIS non-modem. + */ + if (rndis) { + struct usb_cdc_acm_descriptor *d; + + d = (void *) buf; + if (d->bmCapabilities) { + dev_dbg(&intf->dev, + "ACM capabilities %02x, " + "not really RNDIS?\n", + d->bmCapabilities); + goto bad_desc; + } + } + break; case USB_CDC_UNION_TYPE: if (info->u) { dev_dbg(&intf->dev, "extra CDC union\n"); @@ -171,7 +213,21 @@ next_desc: buf += buf [0]; } - if (!info->header || !info->u || (!rndis && !info->ether)) { + /* Microsoft ActiveSync based RNDIS devices lack the CDC descriptors, + * so we'll hard-wire the interfaces and not check for descriptors. + */ + if (is_activesync(&intf->cur_altsetting->desc) && !info->u) { + info->control = usb_ifnum_to_if(dev->udev, 0); + info->data = usb_ifnum_to_if(dev->udev, 1); + if (!info->control || !info->data) { + dev_dbg(&intf->dev, + "activesync: master #0/%p slave #1/%p\n", + info->control, + info->data); + goto bad_desc; + } + + } else if (!info->header || !info->u || (!rndis && !info->ether)) { dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n", info->header ? "" : "header ", info->u ? "" : "union ", diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index fa78326..36a9891 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -179,6 +179,7 @@ static struct usb_driver kaweth_driver = { .suspend = kaweth_suspend, .resume = kaweth_resume, .id_table = usb_klsi_table, + .supports_autosuspend = 1, }; typedef __u8 eth_addr_t[6]; @@ -225,6 +226,7 @@ struct kaweth_device struct delayed_work lowmem_work; struct usb_device *dev; + struct usb_interface *intf; struct net_device *net; wait_queue_head_t term_wait; @@ -662,9 +664,14 @@ static int kaweth_open(struct net_device *net) dbg("Opening network device."); + res = usb_autopm_get_interface(kaweth->intf); + if (res) { + err("Interface cannot be resumed."); + return -EIO; + } res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL); if (res) - return -EIO; + goto err_out; usb_fill_int_urb( kaweth->irq_urb, @@ -681,7 +688,7 @@ static int kaweth_open(struct net_device *net) res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL); if (res) { usb_kill_urb(kaweth->rx_urb); - return -EIO; + goto err_out; } kaweth->opened = 1; @@ -689,10 +696,14 @@ static int kaweth_open(struct net_device *net) kaweth_async_set_rx_mode(kaweth); return 0; + +err_out: + usb_autopm_enable(kaweth->intf); + return -EIO; } /**************************************************************** - * kaweth_close + * kaweth_kill_urbs ****************************************************************/ static void kaweth_kill_urbs(struct kaweth_device *kaweth) { @@ -724,17 +735,29 @@ static int kaweth_close(struct net_device *net) kaweth->status &= ~KAWETH_STATUS_CLOSING; + usb_autopm_enable(kaweth->intf); + return 0; } static void kaweth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { + struct kaweth_device *kaweth = netdev_priv(dev); strlcpy(info->driver, driver_name, sizeof(info->driver)); + usb_make_path(kaweth->dev, info->bus_info, sizeof (info->bus_info)); +} + +static u32 kaweth_get_link(struct net_device *dev) +{ + struct kaweth_device *kaweth = netdev_priv(dev); + + return kaweth->linkstate; } static struct ethtool_ops ops = { - .get_drvinfo = kaweth_get_drvinfo + .get_drvinfo = kaweth_get_drvinfo, + .get_link = kaweth_get_link }; /**************************************************************** @@ -908,6 +931,7 @@ static int kaweth_suspend(struct usb_interface *intf, pm_message_t message) struct kaweth_device *kaweth = usb_get_intfdata(intf); unsigned long flags; + dbg("Suspending device"); spin_lock_irqsave(&kaweth->device_lock, flags); kaweth->status |= KAWETH_STATUS_SUSPENDING; spin_unlock_irqrestore(&kaweth->device_lock, flags); @@ -924,6 +948,7 @@ static int kaweth_resume(struct usb_interface *intf) struct kaweth_device *kaweth = usb_get_intfdata(intf); unsigned long flags; + dbg("Resuming device"); spin_lock_irqsave(&kaweth->device_lock, flags); kaweth->status &= ~KAWETH_STATUS_SUSPENDING; spin_unlock_irqrestore(&kaweth->device_lock, flags); @@ -1086,6 +1111,8 @@ err_fw: dbg("Initializing net device."); + kaweth->intf = intf; + kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!kaweth->tx_urb) goto err_free_netdev; @@ -1265,7 +1292,7 @@ static int kaweth_internal_control_msg(struct usb_device *usb_dev, { struct urb *urb; int retv; - int length; + int length = 0; /* shut up GCC */ urb = usb_alloc_urb(0, GFP_NOIO); if (!urb) diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c index a322a16..be888d2 100644 --- a/drivers/usb/net/rndis_host.c +++ b/drivers/usb/net/rndis_host.c @@ -49,6 +49,8 @@ * - In some cases, MS-Windows will emit undocumented requests; this * matters more to peripheral implementations than host ones. * + * Moreover there's a no-open-specs variant of RNDIS called "ActiveSync". + * * For these reasons and others, ** USE OF RNDIS IS STRONGLY DISCOURAGED ** in * favor of such non-proprietary alternatives as CDC Ethernet or the newer (and * currently rare) "Ethernet Emulation Model" (EEM). @@ -61,6 +63,9 @@ * - control-in: GET_ENCAPSULATED * * We'll try to ignore the RESPONSE_AVAILABLE notifications. + * + * REVISIT some RNDIS implementations seem to have curious issues still + * to be resolved. */ struct rndis_msg_hdr { __le32 msg_type; /* RNDIS_MSG_* */ @@ -71,8 +76,14 @@ struct rndis_msg_hdr { // ... and more } __attribute__ ((packed)); -/* RNDIS defines this (absurdly huge) control timeout */ -#define RNDIS_CONTROL_TIMEOUT_MS (10 * 1000) +/* MS-Windows uses this strange size, but RNDIS spec says 1024 minimum */ +#define CONTROL_BUFFER_SIZE 1025 + +/* RNDIS defines an (absurdly huge) 10 second control timeout, + * but ActiveSync seems to use a more usual 5 second timeout + * (which matches the USB 2.0 spec). + */ +#define RNDIS_CONTROL_TIMEOUT_MS (5 * 1000) #define ccpu2 __constant_cpu_to_le32 @@ -270,6 +281,7 @@ static void rndis_status(struct usbnet *dev, struct urb *urb) static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) { struct cdc_state *info = (void *) &dev->data; + int master_ifnum; int retval; unsigned count; __le32 rsp; @@ -279,7 +291,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) * disconnect(): either serialize, or dispatch responses on xid */ - /* Issue the request; don't bother byteswapping our xid */ + /* Issue the request; xid is unique, don't bother byteswapping it */ if (likely(buf->msg_type != RNDIS_MSG_HALT && buf->msg_type != RNDIS_MSG_RESET)) { xid = dev->xid++; @@ -287,11 +299,12 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) xid = dev->xid++; buf->request_id = (__force __le32) xid; } + master_ifnum = info->control->cur_altsetting->desc.bInterfaceNumber; retval = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), USB_CDC_SEND_ENCAPSULATED_COMMAND, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, info->u->bMasterInterface0, + 0, master_ifnum, buf, le32_to_cpu(buf->msg_len), RNDIS_CONTROL_TIMEOUT_MS); if (unlikely(retval < 0 || xid == 0)) @@ -306,13 +319,13 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) */ rsp = buf->msg_type | RNDIS_MSG_COMPLETION; for (count = 0; count < 10; count++) { - memset(buf, 0, 1024); + memset(buf, 0, CONTROL_BUFFER_SIZE); retval = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), USB_CDC_GET_ENCAPSULATED_RESPONSE, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, info->u->bMasterInterface0, - buf, 1024, + 0, master_ifnum, + buf, CONTROL_BUFFER_SIZE, RNDIS_CONTROL_TIMEOUT_MS); if (likely(retval >= 8)) { msg_len = le32_to_cpu(buf->msg_len); @@ -350,7 +363,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) usb_sndctrlpipe(dev->udev, 0), USB_CDC_SEND_ENCAPSULATED_COMMAND, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - 0, info->u->bMasterInterface0, + 0, master_ifnum, msg, sizeof *msg, RNDIS_CONTROL_TIMEOUT_MS); if (unlikely(retval < 0)) @@ -393,38 +406,64 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) u32 tmp; /* we can't rely on i/o from stack working, or stack allocation */ - u.buf = kmalloc(1024, GFP_KERNEL); + u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); if (!u.buf) return -ENOMEM; retval = usbnet_generic_cdc_bind(dev, intf); if (retval < 0) goto fail; - net->hard_header_len += sizeof (struct rndis_data_hdr); - - /* initialize; max transfer is 16KB at full speed */ u.init->msg_type = RNDIS_MSG_INIT; u.init->msg_len = ccpu2(sizeof *u.init); u.init->major_version = ccpu2(1); u.init->minor_version = ccpu2(0); - u.init->max_transfer_size = ccpu2(net->mtu + net->hard_header_len); + /* max transfer (in spec) is 0x4000 at full speed, but for + * TX we'll stick to one Ethernet packet plus RNDIS framing. + * For RX we handle drivers that zero-pad to end-of-packet. + * Don't let userspace change these settings. + */ + net->hard_header_len += sizeof (struct rndis_data_hdr); + dev->hard_mtu = net->mtu + net->hard_header_len; + + dev->rx_urb_size = dev->hard_mtu + (dev->maxpacket + 1); + dev->rx_urb_size &= ~(dev->maxpacket - 1); + u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); + + net->change_mtu = NULL; retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); + goto fail_and_release; + } + tmp = le32_to_cpu(u.init_c->max_transfer_size); + if (tmp < dev->hard_mtu) { + dev_err(&intf->dev, + "dev can't take %u byte packets (max %u)\n", + dev->hard_mtu, tmp); goto fail_and_release; } - dev->hard_mtu = le32_to_cpu(u.init_c->max_transfer_size); + /* REVISIT: peripheral "alignment" request is ignored ... */ - dev_dbg(&intf->dev, "hard mtu %u, align %d\n", dev->hard_mtu, + dev_dbg(&intf->dev, + "hard mtu %u (%u from dev), rx buflen %Zu, align %d\n", + dev->hard_mtu, tmp, dev->rx_urb_size, 1 << le32_to_cpu(u.init_c->packet_alignment)); - /* get designated host ethernet address */ - memset(u.get, 0, sizeof *u.get); + /* Get designated host ethernet address. + * + * Adding a payload exactly the same size as the expected response + * payload is an evident requirement MSFT added for ActiveSync. + * This undocumented (and nonsensical) issue was found by sniffing + * protocol requests from the ActiveSync 4.1 Windows driver. + */ + memset(u.get, 0, sizeof *u.get + 48); u.get->msg_type = RNDIS_MSG_QUERY; - u.get->msg_len = ccpu2(sizeof *u.get); + u.get->msg_len = ccpu2(sizeof *u.get + 48); u.get->oid = OID_802_3_PERMANENT_ADDRESS; + u.get->len = ccpu2(48); + u.get->offset = ccpu2(20); retval = rndis_command(dev, u.header); if (unlikely(retval < 0)) { @@ -432,7 +471,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) goto fail_and_release; } tmp = le32_to_cpu(u.get_c->offset); - if (unlikely((tmp + 8) > (1024 - ETH_ALEN) + if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN) || u.get_c->len != ccpu2(ETH_ALEN))) { dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n", tmp, le32_to_cpu(u.get_c->len)); @@ -598,6 +637,10 @@ static const struct usb_device_id products [] = { /* RNDIS is MSFT's un-official variant of CDC ACM */ USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff), .driver_info = (unsigned long) &rndis_info, +}, { + /* "ActiveSync" is an undocumented variant of RNDIS, used in WM5 */ + USB_INTERFACE_INFO(USB_CLASS_MISC, 1, 1), + .driver_info = (unsigned long) &rndis_info, }, { }, // END }; diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 86bcf63..11dad42 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -572,8 +572,20 @@ static void aircable_unthrottle(struct usb_serial_port *port) schedule_work(&priv->rx_work); } +static struct usb_driver aircable_driver = { + .name = "aircable", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + static struct usb_serial_driver aircable_device = { - .description = "aircable", + .driver = { + .owner = THIS_MODULE, + .name = "aircable", + }, + .usb_driver = &aircable_driver, .id_table = id_table, .num_ports = 1, .attach = aircable_attach, @@ -587,13 +599,6 @@ static struct usb_serial_driver aircable_device = { .unthrottle = aircable_unthrottle, }; -static struct usb_driver aircable_driver = { - .name = "aircable", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, -}; - static int __init aircable_init (void) { int retval; diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index f2ca76a..0af42e3 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -277,6 +277,7 @@ static struct usb_serial_driver airprime_device = { .owner = THIS_MODULE, .name = "airprime", }, + .usb_driver = &airprime_driver, .id_table = id_table, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 5261cd2..edd6857 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -444,6 +444,7 @@ static struct usb_driver ark3116_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; static struct usb_serial_driver ark3116_device = { @@ -452,6 +453,7 @@ static struct usb_serial_driver ark3116_device = { .name = "ark3116", }, .id_table = id_table, + .usb_driver = &ark3116_driver, .num_interrupt_in = 1, .num_bulk_in = 1, .num_bulk_out = 1, diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 38b4dae..3b800d2 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -126,6 +126,7 @@ static struct usb_serial_driver belkin_device = { .name = "belkin", }, .description = "Belkin / Peracom / GoHubs USB Serial Adapter", + .usb_driver = &belkin_driver, .id_table = id_table_combined, .num_interrupt_in = 1, .num_bulk_in = 1, diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 6542f22..c08a384 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -103,11 +103,52 @@ exit: return retval; } +#ifdef CONFIG_HOTPLUG +static ssize_t store_new_id(struct device_driver *driver, + const char *buf, size_t count) +{ + struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver); + ssize_t retval = usb_store_new_id(&usb_drv->dynids, driver, buf, count); + + if (retval >= 0 && usb_drv->usb_driver != NULL) + retval = usb_store_new_id(&usb_drv->usb_driver->dynids, + &usb_drv->usb_driver->drvwrap.driver, + buf, count); + return retval; +} + +static struct driver_attribute drv_attrs[] = { + __ATTR(new_id, S_IWUSR, NULL, store_new_id), + __ATTR_NULL, +}; + +static void free_dynids(struct usb_serial_driver *drv) +{ + struct usb_dynid *dynid, *n; + + spin_lock(&drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { + list_del(&dynid->node); + kfree(dynid); + } + spin_unlock(&drv->dynids.lock); +} + +#else +static struct driver_attribute drv_attrs[] = { + __ATTR_NULL, +}; +static inline void free_dynids(struct usb_driver *drv) +{ +} +#endif + struct bus_type usb_serial_bus_type = { .name = "usb-serial", .match = usb_serial_device_match, .probe = usb_serial_device_probe, .remove = usb_serial_device_remove, + .drv_attrs = drv_attrs, }; int usb_serial_bus_register(struct usb_serial_driver *driver) @@ -115,6 +156,9 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) int retval; driver->driver.bus = &usb_serial_bus_type; + spin_lock_init(&driver->dynids.lock); + INIT_LIST_HEAD(&driver->dynids.list); + retval = driver_register(&driver->driver); return retval; @@ -122,6 +166,7 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) void usb_serial_bus_deregister(struct usb_serial_driver *driver) { + free_dynids(driver); driver_unregister(&driver->driver); } diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 7ebaffd..06b4fff 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -89,6 +89,7 @@ static struct usb_serial_driver cp2101_device = { .owner = THIS_MODULE, .name = "cp2101", }, + .usb_driver = &cp2101_driver, .id_table = id_table, .num_interrupt_in = 0, .num_bulk_in = 0, diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index a63c328..4167753 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -88,6 +88,7 @@ static struct usb_serial_driver cyberjack_device = { .name = "cyberjack", }, .description = "Reiner SCT Cyberjack USB card reader", + .usb_driver = &cyberjack_driver, .id_table = id_table, .num_interrupt_in = 1, .num_bulk_in = 1, @@ -98,7 +99,7 @@ static struct usb_serial_driver cyberjack_device = { .open = cyberjack_open, .close = cyberjack_close, .write = cyberjack_write, - .write_room = cyberjack_write_room, + .write_room = cyberjack_write_room, .read_int_callback = cyberjack_read_int_callback, .read_bulk_callback = cyberjack_read_bulk_callback, .write_bulk_callback = cyberjack_write_bulk_callback, diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 6bc1f40..57b8e27 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -193,6 +193,7 @@ static struct usb_serial_driver cypress_earthmate_device = { .name = "earthmate", }, .description = "DeLorme Earthmate USB", + .usb_driver = &cypress_driver, .id_table = id_table_earthmate, .num_interrupt_in = 1, .num_interrupt_out = 1, @@ -222,6 +223,7 @@ static struct usb_serial_driver cypress_hidcom_device = { .name = "cyphidcom", }, .description = "HID->COM RS232 Adapter", + .usb_driver = &cypress_driver, .id_table = id_table_cyphidcomrs232, .num_interrupt_in = 1, .num_interrupt_out = 1, @@ -251,6 +253,7 @@ static struct usb_serial_driver cypress_ca42v2_device = { .name = "nokiaca42v2", }, .description = "Nokia CA-42 V2 Adapter", + .usb_driver = &cypress_driver, .id_table = id_table_nokiaca42v2, .num_interrupt_in = 1, .num_interrupt_out = 1, diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index efd9ce3..0b0fb51 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -509,6 +509,7 @@ static struct usb_serial_driver digi_acceleport_2_device = { .name = "digi_2", }, .description = "Digi 2 port USB adapter", + .usb_driver = &digi_driver, .id_table = id_table_2, .num_interrupt_in = 0, .num_bulk_in = 4, @@ -538,6 +539,7 @@ static struct usb_serial_driver digi_acceleport_4_device = { .name = "digi_4", }, .description = "Digi 4 port USB adapter", + .usb_driver = &digi_driver, .id_table = id_table_4, .num_interrupt_in = 0, .num_bulk_in = 5, diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 92beeb1..4703c8f 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -117,6 +117,7 @@ static struct usb_serial_driver empeg_device = { .name = "empeg", }, .id_table = id_table, + .usb_driver = &empeg_driver, .num_interrupt_in = 0, .num_bulk_in = 1, .num_bulk_out = 1, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 6986e75..4695952 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -464,7 +464,6 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) }, { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) }, { USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) }, - { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_0_PID) }, { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) }, { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_0_PID) }, @@ -615,6 +614,7 @@ static struct usb_serial_driver ftdi_sio_device = { .name = "ftdi_sio", }, .description = "FTDI USB Serial Device", + .usb_driver = &ftdi_driver , .id_table = id_table_combined, .num_interrupt_in = 0, .num_bulk_in = 1, diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 40dd394..7eff1c0 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -364,7 +364,6 @@ * USB-TTY activ, USB-TTY passiv. Some PIDs are used by several devices * and I'm not entirely sure which are used by which. */ -#define FTDI_4N_GALAXY_DE_0_PID 0x8372 #define FTDI_4N_GALAXY_DE_1_PID 0xF3C0 #define FTDI_4N_GALAXY_DE_2_PID 0xF3C1 diff --git a/drivers/usb/serial/funsoft.c b/drivers/usb/serial/funsoft.c index 2bebd63..4092f6d 100644 --- a/drivers/usb/serial/funsoft.c +++ b/drivers/usb/serial/funsoft.c @@ -58,6 +58,7 @@ static struct usb_serial_driver funsoft_device = { .name = "funsoft", }, .id_table = id_table, + .usb_driver = &funsoft_driver, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 6530d39..74660a3 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1566,6 +1566,7 @@ static struct usb_serial_driver garmin_device = { .name = "garmin_gps", }, .description = "Garmin GPS usb/tty", + .usb_driver = &garmin_driver, .id_table = id_table, .num_interrupt_in = 1, .num_bulk_in = 1, diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 3604293..601e064 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -20,6 +20,10 @@ #include <linux/usb/serial.h> #include <asm/uaccess.h> +static int generic_probe(struct usb_interface *interface, + const struct usb_device_id *id); + + static int debug; #ifdef CONFIG_USB_SERIAL_GENERIC @@ -34,6 +38,21 @@ MODULE_PARM_DESC(product, "User specified USB idProduct"); static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */ +/* we want to look at all devices, as the vendor/product id can change + * depending on the command line argument */ +static struct usb_device_id generic_serial_ids[] = { + {.driver_info = 42}, + {} +}; + +static struct usb_driver generic_driver = { + .name = "usbserial_generic", + .probe = generic_probe, + .disconnect = usb_serial_disconnect, + .id_table = generic_serial_ids, + .no_dynamic_id = 1, +}; + /* All of the device info needed for the Generic Serial Converter */ struct usb_serial_driver usb_serial_generic_device = { .driver = { @@ -41,6 +60,7 @@ struct usb_serial_driver usb_serial_generic_device = { .name = "generic", }, .id_table = generic_device_ids, + .usb_driver = &generic_driver, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, @@ -48,13 +68,6 @@ struct usb_serial_driver usb_serial_generic_device = { .shutdown = usb_serial_generic_shutdown, }; -/* we want to look at all devices, as the vendor/product id can change - * depending on the command line argument */ -static struct usb_device_id generic_serial_ids[] = { - {.driver_info = 42}, - {} -}; - static int generic_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -65,14 +78,6 @@ static int generic_probe(struct usb_interface *interface, return usb_serial_probe(interface, id); return -ENODEV; } - -static struct usb_driver generic_driver = { - .name = "usbserial_generic", - .probe = generic_probe, - .disconnect = usb_serial_disconnect, - .id_table = generic_serial_ids, - .no_dynamic_id = 1, -}; #endif int usb_serial_generic_register (int _debug) diff --git a/drivers/usb/serial/hp4x.c b/drivers/usb/serial/hp4x.c index ebcac70..6c6ebae 100644 --- a/drivers/usb/serial/hp4x.c +++ b/drivers/usb/serial/hp4x.c @@ -49,6 +49,7 @@ static struct usb_serial_driver hp49gp_device = { .name = "hp4X", }, .id_table = id_table, + .usb_driver = &hp49gp_driver, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index f623d58..6a26a2e 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -146,6 +146,8 @@ struct edgeport_serial { struct edge_manuf_descriptor manuf_descriptor; /* the manufacturer descriptor */ struct edge_boot_descriptor boot_descriptor; /* the boot firmware descriptor */ struct edgeport_product_info product_info; /* Product Info */ + struct edge_compatibility_descriptor epic_descriptor; /* Edgeport compatible descriptor */ + int is_epic; /* flag if EPiC device or not */ __u8 interrupt_in_endpoint; /* the interrupt endpoint handle */ unsigned char * interrupt_in_buffer; /* the buffer we use for the interrupt endpoint */ @@ -240,14 +242,6 @@ static void edge_shutdown (struct usb_serial *serial); #include "io_tables.h" /* all of the devices that this driver supports */ -static struct usb_driver io_driver = { - .name = "io_edgeport", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table_combined, - .no_dynamic_id = 1, -}; - /* function prototypes for all of our local functions */ static void process_rcvd_data (struct edgeport_serial *edge_serial, unsigned char *buffer, __u16 bufferLength); static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2, __u8 byte3); @@ -397,6 +391,7 @@ static int get_string (struct usb_device *dev, int Id, char *string, int buflen) unicode_to_ascii(string, buflen, pStringDesc->wData, pStringDesc->bLength/2); kfree(pStringDesc); + dbg("%s - USB String %s", __FUNCTION__, string); return strlen(string); } @@ -434,6 +429,34 @@ static int get_string_desc (struct usb_device *dev, int Id, struct usb_string_de } #endif +static void dump_product_info(struct edgeport_product_info *product_info) +{ + // Dump Product Info structure + dbg("**Product Information:"); + dbg(" ProductId %x", product_info->ProductId ); + dbg(" NumPorts %d", product_info->NumPorts ); + dbg(" ProdInfoVer %d", product_info->ProdInfoVer ); + dbg(" IsServer %d", product_info->IsServer); + dbg(" IsRS232 %d", product_info->IsRS232 ); + dbg(" IsRS422 %d", product_info->IsRS422 ); + dbg(" IsRS485 %d", product_info->IsRS485 ); + dbg(" RomSize %d", product_info->RomSize ); + dbg(" RamSize %d", product_info->RamSize ); + dbg(" CpuRev %x", product_info->CpuRev ); + dbg(" BoardRev %x", product_info->BoardRev); + dbg(" BootMajorVersion %d.%d.%d", product_info->BootMajorVersion, + product_info->BootMinorVersion, + le16_to_cpu(product_info->BootBuildNumber)); + dbg(" FirmwareMajorVersion %d.%d.%d", product_info->FirmwareMajorVersion, + product_info->FirmwareMinorVersion, + le16_to_cpu(product_info->FirmwareBuildNumber)); + dbg(" ManufactureDescDate %d/%d/%d", product_info->ManufactureDescDate[0], + product_info->ManufactureDescDate[1], + product_info->ManufactureDescDate[2]+1900); + dbg(" iDownloadFile 0x%x", product_info->iDownloadFile); + dbg(" EpicVer %d", product_info->EpicVer); +} + static void get_product_info(struct edgeport_serial *edge_serial) { struct edgeport_product_info *product_info = &edge_serial->product_info; @@ -495,30 +518,60 @@ static void get_product_info(struct edgeport_serial *edge_serial) break; } - // Dump Product Info structure - dbg("**Product Information:"); - dbg(" ProductId %x", product_info->ProductId ); - dbg(" NumPorts %d", product_info->NumPorts ); - dbg(" ProdInfoVer %d", product_info->ProdInfoVer ); - dbg(" IsServer %d", product_info->IsServer); - dbg(" IsRS232 %d", product_info->IsRS232 ); - dbg(" IsRS422 %d", product_info->IsRS422 ); - dbg(" IsRS485 %d", product_info->IsRS485 ); - dbg(" RomSize %d", product_info->RomSize ); - dbg(" RamSize %d", product_info->RamSize ); - dbg(" CpuRev %x", product_info->CpuRev ); - dbg(" BoardRev %x", product_info->BoardRev); - dbg(" BootMajorVersion %d.%d.%d", product_info->BootMajorVersion, - product_info->BootMinorVersion, - le16_to_cpu(product_info->BootBuildNumber)); - dbg(" FirmwareMajorVersion %d.%d.%d", product_info->FirmwareMajorVersion, - product_info->FirmwareMinorVersion, - le16_to_cpu(product_info->FirmwareBuildNumber)); - dbg(" ManufactureDescDate %d/%d/%d", product_info->ManufactureDescDate[0], - product_info->ManufactureDescDate[1], - product_info->ManufactureDescDate[2]+1900); - dbg(" iDownloadFile 0x%x", product_info->iDownloadFile); + dump_product_info(product_info); +} +static int get_epic_descriptor(struct edgeport_serial *ep) +{ + int result; + struct usb_serial *serial = ep->serial; + struct edgeport_product_info *product_info = &ep->product_info; + struct edge_compatibility_descriptor *epic = &ep->epic_descriptor; + struct edge_compatibility_bits *bits; + + ep->is_epic = 0; + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + USB_REQUEST_ION_GET_EPIC_DESC, + 0xC0, 0x00, 0x00, + &ep->epic_descriptor, + sizeof(struct edge_compatibility_descriptor), + 300); + + dbg("%s result = %d", __FUNCTION__, result); + + if (result > 0) { + ep->is_epic = 1; + memset(product_info, 0, sizeof(struct edgeport_product_info)); + + product_info->NumPorts = epic->NumPorts; + product_info->ProdInfoVer = 0; + product_info->FirmwareMajorVersion = epic->MajorVersion; + product_info->FirmwareMinorVersion = epic->MinorVersion; + product_info->FirmwareBuildNumber = epic->BuildNumber; + product_info->iDownloadFile = epic->iDownloadFile; + product_info->EpicVer = epic->EpicVer; + product_info->Epic = epic->Supports; + product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE; + dump_product_info(product_info); + + bits = &ep->epic_descriptor.Supports; + dbg("**EPIC descriptor:"); + dbg(" VendEnableSuspend: %s", bits->VendEnableSuspend ? "TRUE": "FALSE"); + dbg(" IOSPOpen : %s", bits->IOSPOpen ? "TRUE": "FALSE" ); + dbg(" IOSPClose : %s", bits->IOSPClose ? "TRUE": "FALSE" ); + dbg(" IOSPChase : %s", bits->IOSPChase ? "TRUE": "FALSE" ); + dbg(" IOSPSetRxFlow : %s", bits->IOSPSetRxFlow ? "TRUE": "FALSE" ); + dbg(" IOSPSetTxFlow : %s", bits->IOSPSetTxFlow ? "TRUE": "FALSE" ); + dbg(" IOSPSetXChar : %s", bits->IOSPSetXChar ? "TRUE": "FALSE" ); + dbg(" IOSPRxCheck : %s", bits->IOSPRxCheck ? "TRUE": "FALSE" ); + dbg(" IOSPSetClrBreak : %s", bits->IOSPSetClrBreak ? "TRUE": "FALSE" ); + dbg(" IOSPWriteMCR : %s", bits->IOSPWriteMCR ? "TRUE": "FALSE" ); + dbg(" IOSPWriteLCR : %s", bits->IOSPWriteLCR ? "TRUE": "FALSE" ); + dbg(" IOSPSetBaudRate : %s", bits->IOSPSetBaudRate ? "TRUE": "FALSE" ); + dbg(" TrueEdgeport : %s", bits->TrueEdgeport ? "TRUE": "FALSE" ); + } + + return result; } @@ -1017,21 +1070,29 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) edge_port->closePending = TRUE; - /* flush and chase */ - edge_port->chaseResponsePending = TRUE; - - dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); - if (status == 0) { - // block until chase finished - block_until_chase_response(edge_port); - } else { - edge_port->chaseResponsePending = FALSE; + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (edge_serial->epic_descriptor.Supports.IOSPChase))) { + /* flush and chase */ + edge_port->chaseResponsePending = TRUE; + + dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); + status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); + if (status == 0) { + // block until chase finished + block_until_chase_response(edge_port); + } else { + edge_port->chaseResponsePending = FALSE; + } } - /* close the port */ - dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __FUNCTION__); - send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (edge_serial->epic_descriptor.Supports.IOSPClose))) { + /* close the port */ + dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __FUNCTION__); + send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); + } //port->close = TRUE; edge_port->closePending = FALSE; @@ -1694,29 +1755,38 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned static void edge_break (struct usb_serial_port *port, int break_state) { struct edgeport_port *edge_port = usb_get_serial_port_data(port); + struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial); int status; - /* flush and chase */ - edge_port->chaseResponsePending = TRUE; - - dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); - if (status == 0) { - // block until chase finished - block_until_chase_response(edge_port); - } else { - edge_port->chaseResponsePending = FALSE; + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (edge_serial->epic_descriptor.Supports.IOSPChase))) { + /* flush and chase */ + edge_port->chaseResponsePending = TRUE; + + dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); + status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); + if (status == 0) { + // block until chase finished + block_until_chase_response(edge_port); + } else { + edge_port->chaseResponsePending = FALSE; + } } - if (break_state == -1) { - dbg("%s - Sending IOSP_CMD_SET_BREAK", __FUNCTION__); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_BREAK, 0); - } else { - dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __FUNCTION__); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CLEAR_BREAK, 0); - } - if (status) { - dbg("%s - error sending break set/clear command.", __FUNCTION__); + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (edge_serial->epic_descriptor.Supports.IOSPSetClrBreak))) { + if (break_state == -1) { + dbg("%s - Sending IOSP_CMD_SET_BREAK", __FUNCTION__); + status = send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_BREAK, 0); + } else { + dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __FUNCTION__); + status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CLEAR_BREAK, 0); + } + if (status) { + dbg("%s - error sending break set/clear command.", __FUNCTION__); + } } return; @@ -2288,6 +2358,7 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer *****************************************************************************/ static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRate) { + struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); unsigned char *cmdBuffer; unsigned char *currCmd; int cmdLen = 0; @@ -2295,6 +2366,14 @@ static int send_cmd_write_baud_rate (struct edgeport_port *edge_port, int baudRa int status; unsigned char number = edge_port->port->number - edge_port->port->serial->minor; + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (!edge_serial->epic_descriptor.Supports.IOSPSetBaudRate))) { + dbg("SendCmdWriteBaudRate - NOT Setting baud rate for port = %d, baud = %d", + edge_port->port->number, baudRate); + return 0; + } + dbg("%s - port = %d, baud = %d", __FUNCTION__, edge_port->port->number, baudRate); status = calc_baud_rate_divisor (baudRate, &divisor); @@ -2374,6 +2453,7 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor) *****************************************************************************/ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 regNum, __u8 regValue) { + struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); unsigned char *cmdBuffer; unsigned char *currCmd; unsigned long cmdLen = 0; @@ -2381,6 +2461,22 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r dbg("%s - write to %s register 0x%02x", (regNum == MCR) ? "MCR" : "LCR", __FUNCTION__, regValue); + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (!edge_serial->epic_descriptor.Supports.IOSPWriteMCR) && + (regNum == MCR))) { + dbg("SendCmdWriteUartReg - Not writting to MCR Register"); + return 0; + } + + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (!edge_serial->epic_descriptor.Supports.IOSPWriteLCR) && + (regNum == LCR))) { + dbg ("SendCmdWriteUartReg - Not writting to LCR Register"); + return 0; + } + // Alloc memory for the string of commands. cmdBuffer = kmalloc (0x10, GFP_ATOMIC); if (cmdBuffer == NULL ) { @@ -2414,6 +2510,7 @@ static int send_cmd_write_uart_register (struct edgeport_port *edge_port, __u8 r #endif static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios) { + struct edgeport_serial *edge_serial = usb_get_serial_data(edge_port->port->serial); struct tty_struct *tty; int baud; unsigned cflag; @@ -2494,8 +2591,12 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi unsigned char stop_char = STOP_CHAR(tty); unsigned char start_char = START_CHAR(tty); - send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XON_CHAR, start_char); - send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char); + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (edge_serial->epic_descriptor.Supports.IOSPSetXChar))) { + send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_XON_CHAR, start_char); + send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_XOFF_CHAR, stop_char); + } /* if we are implementing INBOUND XON/XOFF */ if (I_IXOFF(tty)) { @@ -2515,8 +2616,14 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi } /* Set flow control to the configured value */ - send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow); - send_iosp_ext_cmd (edge_port, IOSP_CMD_SET_TX_FLOW, txFlow); + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (edge_serial->epic_descriptor.Supports.IOSPSetRxFlow))) + send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_RX_FLOW, rxFlow); + if ((!edge_serial->is_epic) || + ((edge_serial->is_epic) && + (edge_serial->epic_descriptor.Supports.IOSPSetTxFlow))) + send_iosp_ext_cmd(edge_port, IOSP_CMD_SET_TX_FLOW, txFlow); edge_port->shadowLCR &= ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK); @@ -2728,6 +2835,13 @@ static int edge_startup (struct usb_serial *serial) struct edgeport_port *edge_port; struct usb_device *dev; int i, j; + int response; + int interrupt_in_found; + int bulk_in_found; + int bulk_out_found; + static __u32 descriptor[3] = { EDGE_COMPATIBILITY_MASK0, + EDGE_COMPATIBILITY_MASK1, + EDGE_COMPATIBILITY_MASK2 }; dev = serial->dev; @@ -2750,38 +2864,50 @@ static int edge_startup (struct usb_serial *serial) dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name); - /* get the manufacturing descriptor for this device */ - get_manufacturing_desc (edge_serial); + /* Read the epic descriptor */ + if (get_epic_descriptor(edge_serial) <= 0) { + /* memcpy descriptor to Supports structures */ + memcpy(&edge_serial->epic_descriptor.Supports, descriptor, + sizeof(struct edge_compatibility_bits)); + + /* get the manufacturing descriptor for this device */ + get_manufacturing_desc (edge_serial); - /* get the boot descriptor */ - get_boot_desc (edge_serial); + /* get the boot descriptor */ + get_boot_desc (edge_serial); - get_product_info(edge_serial); + get_product_info(edge_serial); + } /* set the number of ports from the manufacturing description */ /* serial->num_ports = serial->product_info.NumPorts; */ - if (edge_serial->product_info.NumPorts != serial->num_ports) { - warn("%s - Device Reported %d serial ports vs core " - "thinking we have %d ports, email greg@kroah.com this info.", - __FUNCTION__, edge_serial->product_info.NumPorts, - serial->num_ports); + if ((!edge_serial->is_epic) && + (edge_serial->product_info.NumPorts != serial->num_ports)) { + dev_warn(&serial->dev->dev, "Device Reported %d serial ports " + "vs. core thinking we have %d ports, email " + "greg@kroah.com this information.", + edge_serial->product_info.NumPorts, + serial->num_ports); } dbg("%s - time 1 %ld", __FUNCTION__, jiffies); - /* now load the application firmware into this device */ - load_application_firmware (edge_serial); + /* If not an EPiC device */ + if (!edge_serial->is_epic) { + /* now load the application firmware into this device */ + load_application_firmware (edge_serial); - dbg("%s - time 2 %ld", __FUNCTION__, jiffies); + dbg("%s - time 2 %ld", __FUNCTION__, jiffies); - /* Check current Edgeport EEPROM and update if necessary */ - update_edgeport_E2PROM (edge_serial); - - dbg("%s - time 3 %ld", __FUNCTION__, jiffies); + /* Check current Edgeport EEPROM and update if necessary */ + update_edgeport_E2PROM (edge_serial); - /* set the configuration to use #1 */ -// dbg("set_configuration 1"); -// usb_set_configuration (dev, 1); + dbg("%s - time 3 %ld", __FUNCTION__, jiffies); + + /* set the configuration to use #1 */ +// dbg("set_configuration 1"); +// usb_set_configuration (dev, 1); + } /* we set up the pointers to the endpoints in the edge_open function, * as the structures aren't created yet. */ @@ -2804,8 +2930,101 @@ static int edge_startup (struct usb_serial *serial) edge_port->port = serial->port[i]; usb_set_serial_port_data(serial->port[i], edge_port); } - - return 0; + + response = 0; + + if (edge_serial->is_epic) { + /* EPIC thing, set up our interrupt polling now and our read urb, so + * that the device knows it really is connected. */ + interrupt_in_found = bulk_in_found = bulk_out_found = FALSE; + for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) { + struct usb_endpoint_descriptor *endpoint; + int buffer_size; + + endpoint = &serial->interface->altsetting[0].endpoint[i].desc; + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + if ((!interrupt_in_found) && + (usb_endpoint_is_int_in(endpoint))) { + /* we found a interrupt in endpoint */ + dbg("found interrupt in"); + + /* not set up yet, so do it now */ + edge_serial->interrupt_read_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!edge_serial->interrupt_read_urb) { + err("out of memory"); + return -ENOMEM; + } + edge_serial->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!edge_serial->interrupt_in_buffer) { + err("out of memory"); + usb_free_urb(edge_serial->interrupt_read_urb); + return -ENOMEM; + } + edge_serial->interrupt_in_endpoint = endpoint->bEndpointAddress; + + /* set up our interrupt urb */ + usb_fill_int_urb(edge_serial->interrupt_read_urb, + dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + edge_serial->interrupt_in_buffer, + buffer_size, + edge_interrupt_callback, + edge_serial, + endpoint->bInterval); + + interrupt_in_found = TRUE; + } + + if ((!bulk_in_found) && + (usb_endpoint_is_bulk_in(endpoint))) { + /* we found a bulk in endpoint */ + dbg("found bulk in"); + + /* not set up yet, so do it now */ + edge_serial->read_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!edge_serial->read_urb) { + err("out of memory"); + return -ENOMEM; + } + edge_serial->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!edge_serial->bulk_in_buffer) { + err ("out of memory"); + usb_free_urb(edge_serial->read_urb); + return -ENOMEM; + } + edge_serial->bulk_in_endpoint = endpoint->bEndpointAddress; + + /* set up our bulk in urb */ + usb_fill_bulk_urb(edge_serial->read_urb, dev, + usb_rcvbulkpipe(dev, endpoint->bEndpointAddress), + edge_serial->bulk_in_buffer, + endpoint->wMaxPacketSize, + edge_bulk_in_callback, + edge_serial); + bulk_in_found = TRUE; + } + + if ((!bulk_out_found) && + (usb_endpoint_is_bulk_out(endpoint))) { + /* we found a bulk out endpoint */ + dbg("found bulk out"); + edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress; + bulk_out_found = TRUE; + } + } + + if ((!interrupt_in_found) || (!bulk_in_found) || (!bulk_out_found)) { + err ("Error - the proper endpoints were not found!"); + return -ENODEV; + } + + /* start interrupt read for this edgeport this interrupt will + * continue as long as the edgeport is connected */ + response = usb_submit_urb(edge_serial->interrupt_read_urb, GFP_KERNEL); + if (response) + err("%s - Error %d submitting control urb", __FUNCTION__, response); + } + return response; } @@ -2815,6 +3034,7 @@ static int edge_startup (struct usb_serial *serial) ****************************************************************************/ static void edge_shutdown (struct usb_serial *serial) { + struct edgeport_serial *edge_serial = usb_get_serial_data(serial); int i; dbg("%s", __FUNCTION__); @@ -2824,7 +3044,18 @@ static void edge_shutdown (struct usb_serial *serial) kfree (usb_get_serial_port_data(serial->port[i])); usb_set_serial_port_data(serial->port[i], NULL); } - kfree (usb_get_serial_data(serial)); + /* free up our endpoint stuff */ + if (edge_serial->is_epic) { + usb_unlink_urb(edge_serial->interrupt_read_urb); + usb_free_urb(edge_serial->interrupt_read_urb); + kfree(edge_serial->interrupt_in_buffer); + + usb_unlink_urb(edge_serial->read_urb); + usb_free_urb(edge_serial->read_urb); + kfree(edge_serial->bulk_in_buffer); + } + + kfree(edge_serial); usb_set_serial_data(serial, NULL); } @@ -2846,6 +3077,9 @@ static int __init edgeport_init(void) retval = usb_serial_register(&edgeport_8port_device); if (retval) goto failed_8port_device_register; + retval = usb_serial_register(&epic_device); + if (retval) + goto failed_epic_device_register; retval = usb_register(&io_driver); if (retval) goto failed_usb_register; @@ -2853,6 +3087,8 @@ static int __init edgeport_init(void) return 0; failed_usb_register: + usb_serial_deregister(&epic_device); +failed_epic_device_register: usb_serial_deregister(&edgeport_8port_device); failed_8port_device_register: usb_serial_deregister(&edgeport_4port_device); @@ -2873,6 +3109,7 @@ static void __exit edgeport_exit (void) usb_serial_deregister (&edgeport_2port_device); usb_serial_deregister (&edgeport_4port_device); usb_serial_deregister (&edgeport_8port_device); + usb_serial_deregister (&epic_device); } module_init(edgeport_init); diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index 123fa8a..29a913a 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -111,10 +111,12 @@ struct edgeport_product_info { __le16 FirmwareBuildNumber; /* zzzz (LE format) */ __u8 ManufactureDescDate[3]; /* MM/DD/YY when descriptor template was compiled */ - __u8 Unused1[1]; /* Available */ + __u8 HardwareType; __u8 iDownloadFile; /* What to download to EPiC device */ - __u8 Unused2[2]; /* Available */ + __u8 EpicVer; /* What version of EPiC spec this device supports */ + + struct edge_compatibility_bits Epic; }; /* diff --git a/drivers/usb/serial/io_tables.h b/drivers/usb/serial/io_tables.h index fad561c..6d30087 100644 --- a/drivers/usb/serial/io_tables.h +++ b/drivers/usb/serial/io_tables.h @@ -47,6 +47,18 @@ static struct usb_device_id edgeport_8port_id_table [] = { { } }; +static struct usb_device_id Epic_port_id_table [] = { + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) }, + { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) }, + { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) }, + { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) }, + { } +}; + /* Devices that this driver supports */ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_4) }, @@ -70,17 +82,34 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0311) }, + { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0312) }, + { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A758) }, + { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A794) }, + { USB_DEVICE(USB_VENDOR_ID_AXIOHM, AXIOHM_DEVICE_ID_EPIC_A225) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, id_table_combined); +static struct usb_driver io_driver = { + .name = "io_edgeport", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table_combined, + .no_dynamic_id = 1, +}; + static struct usb_serial_driver edgeport_2port_device = { .driver = { .owner = THIS_MODULE, .name = "edgeport_2", }, .description = "Edgeport 2 port adapter", + .usb_driver = &io_driver, .id_table = edgeport_2port_id_table, .num_interrupt_in = 1, .num_bulk_in = 1, @@ -111,6 +140,7 @@ static struct usb_serial_driver edgeport_4port_device = { .name = "edgeport_4", }, .description = "Edgeport 4 port adapter", + .usb_driver = &io_driver, .id_table = edgeport_4port_id_table, .num_interrupt_in = 1, .num_bulk_in = 1, @@ -141,6 +171,7 @@ static struct usb_serial_driver edgeport_8port_device = { .name = "edgeport_8", }, .description = "Edgeport 8 port adapter", + .usb_driver = &io_driver, .id_table = edgeport_8port_id_table, .num_interrupt_in = 1, .num_bulk_in = 1, @@ -165,5 +196,35 @@ static struct usb_serial_driver edgeport_8port_device = { .write_bulk_callback = edge_bulk_out_data_callback, }; +static struct usb_serial_driver epic_device = { + .driver = { + .owner = THIS_MODULE, + .name = "epic", + }, + .description = "EPiC device", + .id_table = Epic_port_id_table, + .num_interrupt_in = 1, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = edge_open, + .close = edge_close, + .throttle = edge_throttle, + .unthrottle = edge_unthrottle, + .attach = edge_startup, + .shutdown = edge_shutdown, + .ioctl = edge_ioctl, + .set_termios = edge_set_termios, + .tiocmget = edge_tiocmget, + .tiocmset = edge_tiocmset, + .write = edge_write, + .write_room = edge_write_room, + .chars_in_buffer = edge_chars_in_buffer, + .break_ctl = edge_break, + .read_int_callback = edge_interrupt_callback, + .read_bulk_callback = edge_bulk_in_callback, + .write_bulk_callback = edge_bulk_out_data_callback, +}; + #endif diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 980285c..544098d 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2979,6 +2979,7 @@ static struct usb_serial_driver edgeport_1port_device = { .name = "edgeport_ti_1", }, .description = "Edgeport TI 1 port adapter", + .usb_driver = &io_driver, .id_table = edgeport_1port_id_table, .num_interrupt_in = 1, .num_bulk_in = 1, @@ -3009,6 +3010,7 @@ static struct usb_serial_driver edgeport_2port_device = { .name = "edgeport_ti_2", }, .description = "Edgeport TI 2 port adapter", + .usb_driver = &io_driver, .id_table = edgeport_2port_id_table, .num_interrupt_in = 1, .num_bulk_in = 2, diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index f1804fd..e57fa11 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -30,6 +30,7 @@ #define USB_VENDOR_ID_ION 0x1608 // Our VID #define USB_VENDOR_ID_TI 0x0451 // TI VID +#define USB_VENDOR_ID_AXIOHM 0x05D9 /* Axiohm VID */ // // Definitions of USB product IDs (PID) @@ -334,6 +335,10 @@ struct edge_compatibility_bits }; +#define EDGE_COMPATIBILITY_MASK0 0x0001 +#define EDGE_COMPATIBILITY_MASK1 0x3FFF +#define EDGE_COMPATIBILITY_MASK2 0x0001 + struct edge_compatibility_descriptor { __u8 Length; // Descriptor Length (per USB spec) diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 42f757a..a408184 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -563,6 +563,7 @@ static struct usb_serial_driver ipaq_device = { .name = "ipaq", }, .description = "PocketPC PDA", + .usb_driver = &ipaq_driver, .id_table = ipaq_id_table, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 1, diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index d3b9a35..1bc5860 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -442,6 +442,7 @@ static struct usb_serial_driver ipw_device = { .name = "ipw", }, .description = "IPWireless converter", + .usb_driver = &usb_ipw_driver, .id_table = usb_ipw_ids, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 1, diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 8fdf486..9d847f6 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -138,6 +138,7 @@ static struct usb_serial_driver ir_device = { .name = "ir-usb", }, .description = "IR Dongle", + .usb_driver = &ir_driver, .id_table = id_table, .num_interrupt_in = 1, .num_bulk_in = 1, diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 9d2fdfd..e6966f1 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1275,11 +1275,31 @@ static int keyspan_fake_startup (struct usb_serial *serial) } /* Helper functions used by keyspan_setup_urbs */ +static struct usb_endpoint_descriptor const *find_ep(struct usb_serial const *serial, + int endpoint) +{ + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *ep; + int i; + + iface_desc = serial->interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + ep = &iface_desc->endpoint[i].desc; + if (ep->bEndpointAddress == endpoint) + return ep; + } + dev_warn(&serial->interface->dev, "found no endpoint descriptor for " + "endpoint %x\n", endpoint); + return NULL; +} + static struct urb *keyspan_setup_urb (struct usb_serial *serial, int endpoint, int dir, void *ctx, char *buf, int len, void (*callback)(struct urb *)) { struct urb *urb; + struct usb_endpoint_descriptor const *ep_desc; + char const *ep_type_name; if (endpoint == -1) return NULL; /* endpoint not needed */ @@ -1291,11 +1311,32 @@ static struct urb *keyspan_setup_urb (struct usb_serial *serial, int endpoint, return NULL; } - /* Fill URB using supplied data. */ - usb_fill_bulk_urb(urb, serial->dev, - usb_sndbulkpipe(serial->dev, endpoint) | dir, - buf, len, callback, ctx); + ep_desc = find_ep(serial, endpoint); + if (!ep_desc) { + /* leak the urb, something's wrong and the callers don't care */ + return urb; + } + if (usb_endpoint_xfer_int(ep_desc)) { + ep_type_name = "INT"; + usb_fill_int_urb(urb, serial->dev, + usb_sndintpipe(serial->dev, endpoint) | dir, + buf, len, callback, ctx, + ep_desc->bInterval); + } else if (usb_endpoint_xfer_bulk(ep_desc)) { + ep_type_name = "BULK"; + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, endpoint) | dir, + buf, len, callback, ctx); + } else { + dev_warn(&serial->interface->dev, + "unsupported endpoint type %x\n", + ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + usb_free_urb(urb); + return NULL; + } + dbg("%s - using urb %p for %s endpoint %x", + __func__, urb, ep_type_name, endpoint); return urb; } diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 6413d73..c6830cb 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -229,7 +229,6 @@ struct ezusb_hex_record { #define keyspan_usa28_product_id 0x010f #define keyspan_usa28x_product_id 0x0110 #define keyspan_usa28xa_product_id 0x0115 -#define keyspan_usa28xb_product_id 0x0110 #define keyspan_usa49w_product_id 0x010a #define keyspan_usa49wlc_product_id 0x012a @@ -511,7 +510,6 @@ static struct usb_device_id keyspan_ids_combined[] = { { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) }, - { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)}, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)}, { } /* Terminating entry */ @@ -559,7 +557,6 @@ static struct usb_device_id keyspan_2port_ids[] = { { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28x_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xa_product_id) }, - { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xb_product_id) }, { } /* Terminating entry */ }; @@ -576,6 +573,7 @@ static struct usb_serial_driver keyspan_pre_device = { .name = "keyspan_no_firm", }, .description = "Keyspan - (without firmware)", + .usb_driver = &keyspan_driver, .id_table = keyspan_pre_ids, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, @@ -590,6 +588,7 @@ static struct usb_serial_driver keyspan_1port_device = { .name = "keyspan_1", }, .description = "Keyspan 1 port adapter", + .usb_driver = &keyspan_driver, .id_table = keyspan_1port_ids, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, @@ -617,6 +616,7 @@ static struct usb_serial_driver keyspan_2port_device = { .name = "keyspan_2", }, .description = "Keyspan 2 port adapter", + .usb_driver = &keyspan_driver, .id_table = keyspan_2port_ids, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, @@ -644,6 +644,7 @@ static struct usb_serial_driver keyspan_4port_device = { .name = "keyspan_4", }, .description = "Keyspan 4 port adapter", + .usb_driver = &keyspan_driver, .id_table = keyspan_4port_ids, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 5, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 126b970..da514cb 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -793,6 +793,7 @@ static struct usb_serial_driver keyspan_pda_fake_device = { .name = "keyspan_pda_pre", }, .description = "Keyspan PDA - (prerenumeration)", + .usb_driver = &keyspan_pda_driver, .id_table = id_table_fake, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, @@ -809,6 +810,7 @@ static struct usb_serial_driver xircom_pgs_fake_device = { .name = "xircom_no_firm", }, .description = "Xircom / Entregra PGS - (prerenumeration)", + .usb_driver = &keyspan_pda_driver, .id_table = id_table_fake_xircom, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, @@ -824,6 +826,7 @@ static struct usb_serial_driver keyspan_pda_device = { .name = "keyspan_pda", }, .description = "Keyspan PDA", + .usb_driver = &keyspan_pda_driver, .id_table = id_table_std, .num_interrupt_in = 1, .num_bulk_in = 0, diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 5c4b06a..b2097c4 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -124,6 +124,7 @@ static struct usb_serial_driver kl5kusb105d_device = { .name = "kl5kusb105d", }, .description = "KL5KUSB105D / PalmConnect", + .usb_driver = &kl5kusb105d_driver, .id_table = id_table, .num_interrupt_in = 1, .num_bulk_in = 1, diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 62bea0c..0683b51 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -110,6 +110,7 @@ static struct usb_serial_driver kobil_device = { .name = "kobil", }, .description = "KOBIL USB smart card terminal", + .usb_driver = &kobil_driver, .id_table = id_table, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 0, diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 38b1d17..4cd839b 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -137,6 +137,7 @@ static struct usb_serial_driver mct_u232_device = { .name = "mct_u232", }, .description = "MCT U232", + .usb_driver = &mct_u232_driver, .id_table = id_table_combined, .num_interrupt_in = 2, .num_bulk_in = 0, diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index e55f4ed..6109c67 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1605,12 +1605,21 @@ static void mos7720_shutdown(struct usb_serial *serial) usb_set_serial_data(serial, NULL); } +static struct usb_driver usb_driver = { + .name = "moschip7720", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = moschip_port_id_table, + .no_dynamic_id = 1, +}; + static struct usb_serial_driver moschip7720_2port_driver = { .driver = { .owner = THIS_MODULE, .name = "moschip7720", }, .description = "Moschip 2 port adapter", + .usb_driver = &usb_driver, .id_table = moschip_port_id_table, .num_interrupt_in = 1, .num_bulk_in = 2, @@ -1631,13 +1640,6 @@ static struct usb_serial_driver moschip7720_2port_driver = { .read_bulk_callback = mos7720_bulk_in_callback, }; -static struct usb_driver usb_driver = { - .name = "moschip7720", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = moschip_port_id_table, -}; - static int __init moschip7720_init(void) { int retval; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 83f6614..b2264a8 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2834,12 +2834,21 @@ static void mos7840_shutdown(struct usb_serial *serial) } +static struct usb_driver io_driver = { + .name = "mos7840", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = moschip_id_table_combined, + .no_dynamic_id = 1, +}; + static struct usb_serial_driver moschip7840_4port_device = { .driver = { .owner = THIS_MODULE, .name = "mos7840", }, .description = DRIVER_DESC, + .usb_driver = &io_driver, .id_table = moschip_port_id_table, .num_interrupt_in = 1, //NUM_DONT_CARE,//1, #ifdef check @@ -2869,13 +2878,6 @@ static struct usb_serial_driver moschip7840_4port_device = { .read_int_callback = mos7840_interrupt_callback, }; -static struct usb_driver io_driver = { - .name = "mos7840", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = moschip_id_table_combined, -}; - /**************************************************************************** * moschip7840_init * This is called by the module subsystem, or on startup to initialize us diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 054abee..9070111 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -119,6 +119,7 @@ static struct usb_serial_driver navman_device = { .name = "navman", }, .id_table = id_table, + .usb_driver = &navman_driver, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index bc91d3b..0216ac1 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -93,6 +93,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .name = "omninet", }, .description = "ZyXEL - omni.net lcd plus usb", + .usb_driver = &omninet_driver, .id_table = id_table, .num_interrupt_in = 1, .num_bulk_in = 1, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 0fed43a..ced9f32 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -135,6 +135,7 @@ static struct usb_serial_driver option_1port_device = { .name = "option1", }, .description = "GSM modem (1-port)", + .usb_driver = &option_driver, .id_table = option_ids1, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 5dc2ac9..6c083d4 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -1118,6 +1118,7 @@ static struct usb_serial_driver pl2303_device = { .name = "pl2303", }, .id_table = id_table, + .usb_driver = &pl2303_driver, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 1, .num_bulk_out = 1, diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 30b7ebc..5a03a3f 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -402,6 +402,7 @@ static struct usb_serial_driver safe_device = { .name = "safe_serial", }, .id_table = id_table, + .usb_driver = &safe_driver, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 6d8e91e..ecedd83 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -13,10 +13,9 @@ Portions based on the option driver by Matthias Urlichs <smurf@smurf.noris.de> Whom based his on the Keyspan driver by Hugh Blemings <hugh@blemings.org> - History: */ -#define DRIVER_VERSION "v.1.0.5" +#define DRIVER_VERSION "v.1.0.6" #define DRIVER_AUTHOR "Kevin Lloyd <linux@sierrawireless.com>" #define DRIVER_DESC "USB Driver for Sierra Wireless USB modems" @@ -31,14 +30,15 @@ static struct usb_device_id id_table [] = { + { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ - { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ - { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ - { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 for Europe */ { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 */ { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ @@ -55,14 +55,15 @@ static struct usb_device_id id_table_1port [] = { }; static struct usb_device_id id_table_3port [] = { + { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ - { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ - { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ + { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ - { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 for Europe */ { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 */ { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ { } @@ -81,7 +82,7 @@ static int debug; /* per port private data */ #define N_IN_URB 4 -#define N_OUT_URB 1 +#define N_OUT_URB 4 #define IN_BUFLEN 4096 #define OUT_BUFLEN 128 @@ -396,6 +397,8 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) struct usb_serial *serial = port->serial; int i, err; struct urb *urb; + int result; + __u16 set_mode_dzero = 0x0000; portdata = usb_get_serial_port_data(port); @@ -442,6 +445,12 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) port->tty->low_latency = 1; + /* set mode to D0 */ + result = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 0x00, 0x40, set_mode_dzero, 0, NULL, + 0, USB_CTRL_SET_TIMEOUT); + sierra_send_setup(port); return (0); @@ -614,6 +623,7 @@ static struct usb_serial_driver sierra_1port_device = { }, .description = "Sierra USB modem (1 port)", .id_table = id_table_1port, + .usb_driver = &sierra_driver, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 1, .num_bulk_out = 1, @@ -642,6 +652,7 @@ static struct usb_serial_driver sierra_3port_device = { }, .description = "Sierra USB modem (3 port)", .id_table = id_table_3port, + .usb_driver = &sierra_driver, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 3, .num_bulk_out = 3, diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 8318900..4203e2b 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -262,6 +262,7 @@ static struct usb_serial_driver ti_1port_device = { .name = "ti_usb_3410_5052_1", }, .description = "TI USB 3410 1 port adapter", + .usb_driver = &ti_usb_driver, .id_table = ti_id_table_3410, .num_interrupt_in = 1, .num_bulk_in = 1, @@ -292,6 +293,7 @@ static struct usb_serial_driver ti_2port_device = { .name = "ti_usb_3410_5052_2", }, .description = "TI USB 5052 2 port adapter", + .usb_driver = &ti_usb_driver, .id_table = ti_id_table_5052, .num_interrupt_in = 1, .num_bulk_in = 2, diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 716f680..6bf22a2 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -59,14 +59,19 @@ static struct usb_driver usb_serial_driver = { static int debug; static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ +static spinlock_t table_lock; static LIST_HEAD(usb_serial_driver_list); struct usb_serial *usb_serial_get_by_index(unsigned index) { - struct usb_serial *serial = serial_table[index]; + struct usb_serial *serial; + + spin_lock(&table_lock); + serial = serial_table[index]; if (serial) kref_get(&serial->kref); + spin_unlock(&table_lock); return serial; } @@ -78,6 +83,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po dbg("%s %d", __FUNCTION__, num_ports); *minor = 0; + spin_lock(&table_lock); for (i = 0; i < SERIAL_TTY_MINORS; ++i) { if (serial_table[i]) continue; @@ -96,8 +102,10 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po dbg("%s - minor base = %d", __FUNCTION__, *minor); for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) serial_table[i] = serial; + spin_unlock(&table_lock); return serial; } + spin_unlock(&table_lock); return NULL; } @@ -110,9 +118,11 @@ static void return_serial(struct usb_serial *serial) if (serial == NULL) return; + spin_lock(&table_lock); for (i = 0; i < serial->num_ports; ++i) { serial_table[serial->minor + i] = NULL; } + spin_unlock(&table_lock); } static void destroy_serial(struct kref *kref) @@ -271,7 +281,7 @@ static void serial_close(struct tty_struct *tty, struct file * filp) static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count) { struct usb_serial_port *port = tty->driver_data; - int retval = -EINVAL; + int retval = -ENODEV; if (!port || port->serial->dev->state == USB_STATE_NOTATTACHED) goto exit; @@ -279,6 +289,7 @@ static int serial_write (struct tty_struct * tty, const unsigned char *buf, int dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count); if (!port->open_count) { + retval = -EINVAL; dbg("%s - port not opened", __FUNCTION__); goto exit; } @@ -559,15 +570,20 @@ static void port_release(struct device *dev) port_free(port); } -static void port_free(struct usb_serial_port *port) +static void kill_traffic(struct usb_serial_port *port) { usb_kill_urb(port->read_urb); - usb_free_urb(port->read_urb); usb_kill_urb(port->write_urb); - usb_free_urb(port->write_urb); usb_kill_urb(port->interrupt_in_urb); - usb_free_urb(port->interrupt_in_urb); usb_kill_urb(port->interrupt_out_urb); +} + +static void port_free(struct usb_serial_port *port) +{ + kill_traffic(port); + usb_free_urb(port->read_urb); + usb_free_urb(port->write_urb); + usb_free_urb(port->interrupt_in_urb); usb_free_urb(port->interrupt_out_urb); kfree(port->bulk_in_buffer); kfree(port->bulk_out_buffer); @@ -596,6 +612,39 @@ static struct usb_serial * create_serial (struct usb_device *dev, return serial; } +static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf, + struct usb_serial_driver *drv) +{ + struct usb_dynid *dynid; + + spin_lock(&drv->dynids.lock); + list_for_each_entry(dynid, &drv->dynids.list, node) { + if (usb_match_one_id(intf, &dynid->id)) { + spin_unlock(&drv->dynids.lock); + return &dynid->id; + } + } + spin_unlock(&drv->dynids.lock); + return NULL; +} + +static const struct usb_device_id *get_iface_id(struct usb_serial_driver *drv, + struct usb_interface *intf) +{ + const struct usb_device_id *id; + + id = usb_match_id(intf, drv->id_table); + if (id) { + dbg("static descriptor matches"); + goto exit; + } + id = match_dynamic_id(intf, drv); + if (id) + dbg("dynamic descriptor matches"); +exit: + return id; +} + static struct usb_serial_driver *search_serial_device(struct usb_interface *iface) { struct list_head *p; @@ -605,11 +654,9 @@ static struct usb_serial_driver *search_serial_device(struct usb_interface *ifac /* Check if the usb id matches a known device */ list_for_each(p, &usb_serial_driver_list) { t = list_entry(p, struct usb_serial_driver, driver_list); - id = usb_match_id(iface, t->id_table); - if (id != NULL) { - dbg("descriptor matches"); + id = get_iface_id(t, iface); + if (id) return t; - } } return NULL; @@ -639,14 +686,17 @@ int usb_serial_probe(struct usb_interface *interface, int num_ports = 0; int max_endpoints; + lock_kernel(); /* guard against unloading a serial driver module */ type = search_serial_device(interface); if (!type) { + unlock_kernel(); dbg("none matched"); return -ENODEV; } serial = create_serial (dev, interface, type); if (!serial) { + unlock_kernel(); dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__); return -ENOMEM; } @@ -656,16 +706,18 @@ int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id; if (!try_module_get(type->driver.owner)) { + unlock_kernel(); dev_err(&interface->dev, "module get failed, exiting\n"); kfree (serial); return -EIO; } - id = usb_match_id(interface, type->id_table); + id = get_iface_id(type, interface); retval = type->probe(serial, id); module_put(type->driver.owner); if (retval) { + unlock_kernel(); dbg ("sub driver rejected device"); kfree (serial); return retval; @@ -735,6 +787,7 @@ int usb_serial_probe(struct usb_interface *interface, * properly during a later invocation of usb_serial_probe */ if (num_bulk_in == 0 || num_bulk_out == 0) { + unlock_kernel(); dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n"); kfree (serial); return -ENODEV; @@ -750,6 +803,7 @@ int usb_serial_probe(struct usb_interface *interface, if (type == &usb_serial_generic_device) { num_ports = num_bulk_out; if (num_ports == 0) { + unlock_kernel(); dev_err(&interface->dev, "Generic device with no bulk out, not allowed.\n"); kfree (serial); return -EIO; @@ -760,6 +814,7 @@ int usb_serial_probe(struct usb_interface *interface, /* if this device type has a calc_num_ports function, call it */ if (type->calc_num_ports) { if (!try_module_get(type->driver.owner)) { + unlock_kernel(); dev_err(&interface->dev, "module get failed, exiting\n"); kfree (serial); return -EIO; @@ -771,12 +826,6 @@ int usb_serial_probe(struct usb_interface *interface, num_ports = type->num_ports; } - if (get_free_serial (serial, num_ports, &minor) == NULL) { - dev_err(&interface->dev, "No more free serial devices\n"); - kfree (serial); - return -ENOMEM; - } - serial->minor = minor; serial->num_ports = num_ports; serial->num_bulk_in = num_bulk_in; @@ -791,6 +840,8 @@ int usb_serial_probe(struct usb_interface *interface, max_endpoints = max(max_endpoints, num_interrupt_out); max_endpoints = max(max_endpoints, (int)serial->num_ports); serial->num_port_pointers = max_endpoints; + unlock_kernel(); + dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints); for (i = 0; i < max_endpoints; ++i) { port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); @@ -925,6 +976,11 @@ int usb_serial_probe(struct usb_interface *interface, } } + if (get_free_serial (serial, num_ports, &minor) == NULL) { + dev_err(&interface->dev, "No more free serial devices\n"); + goto probe_error; + } + /* register all of the individual ports with the driver core */ for (i = 0; i < num_ports; ++i) { port = serial->port[i]; @@ -1002,8 +1058,11 @@ void usb_serial_disconnect(struct usb_interface *interface) if (serial) { for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; - if (port && port->tty) - tty_hangup(port->tty); + if (port) { + if (port->tty) + tty_hangup(port->tty); + kill_traffic(port); + } } /* let the last holder of this object * cause it to be cleaned up */ @@ -1040,6 +1099,7 @@ static int __init usb_serial_init(void) return -ENOMEM; /* Initialize our global data */ + spin_lock_init(&table_lock); for (i = 0; i < SERIAL_TTY_MINORS; ++i) { serial_table[i] = NULL; } @@ -1138,7 +1198,7 @@ static void fixup_generic(struct usb_serial_driver *device) set_to_generic_if_null(device, shutdown); } -int usb_serial_register(struct usb_serial_driver *driver) +int usb_serial_register(struct usb_serial_driver *driver) /* must be called with BKL held */ { int retval; @@ -1162,7 +1222,7 @@ int usb_serial_register(struct usb_serial_driver *driver) } -void usb_serial_deregister(struct usb_serial_driver *device) +void usb_serial_deregister(struct usb_serial_driver *device) /* must be called with BKL held */ { info("USB Serial deregistering driver %s", device->description); list_del(&device->driver_list); diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index b09f060..2f59ff2 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -90,8 +90,6 @@ static struct usb_device_id id_table [] = { .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, - { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE31_ID), - .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID), @@ -151,7 +149,6 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_TREO_650) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_Z_ID) }, - { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE31_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_ZIRE_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, @@ -189,6 +186,7 @@ static struct usb_serial_driver handspring_device = { .name = "visor", }, .description = "Handspring Visor / Palm OS", + .usb_driver = &visor_driver, .id_table = id_table, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 2, @@ -219,6 +217,7 @@ static struct usb_serial_driver clie_5_device = { .name = "clie_5", }, .description = "Sony Clie 5.0", + .usb_driver = &visor_driver, .id_table = clie_id_5_table, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = 2, @@ -249,6 +248,7 @@ static struct usb_serial_driver clie_3_5_device = { .name = "clie_3.5", }, .description = "Sony Clie 3.5", + .usb_driver = &visor_driver, .id_table = clie_id_3_5_table, .num_interrupt_in = 0, .num_bulk_in = 1, diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index 765118d..4ce6f62 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -32,7 +32,6 @@ #define PALM_TUNGSTEN_T_ID 0x0060 #define PALM_TREO_650 0x0061 #define PALM_TUNGSTEN_Z_ID 0x0031 -#define PALM_ZIRE31_ID 0x0061 #define PALM_ZIRE_ID 0x0070 #define PALM_M100_ID 0x0080 diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 5483d85..bf16e9e 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -161,6 +161,7 @@ static struct usb_serial_driver whiteheat_fake_device = { .name = "whiteheatnofirm", }, .description = "Connect Tech - WhiteHEAT - (prerenumeration)", + .usb_driver = &whiteheat_driver, .id_table = id_table_prerenumeration, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, @@ -176,6 +177,7 @@ static struct usb_serial_driver whiteheat_device = { .name = "whiteheat", }, .description = "Connect Tech - WhiteHEAT", + .usb_driver = &whiteheat_driver, .id_table = id_table_std, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index e565d3d..6d3dad3 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -33,7 +33,6 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/usb_ch9.h> #include <linux/usb/input.h> #include "usb.h" #include "onetouch.h" diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index e1072d5..70234f5 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -110,23 +110,6 @@ static int slave_configure(struct scsi_device *sdev) * the end, scatter-gather buffers follow page boundaries. */ blk_queue_dma_alignment(sdev->request_queue, (512 - 1)); - /* Set the SCSI level to at least 2. We'll leave it at 3 if that's - * what is originally reported. We need this to avoid confusing - * the SCSI layer with devices that report 0 or 1, but need 10-byte - * commands (ala ATAPI devices behind certain bridges, or devices - * which simply have broken INQUIRY data). - * - * NOTE: This means /dev/sg programs (ala cdrecord) will get the - * actual information. This seems to be the preference for - * programs like that. - * - * NOTE: This also means that /proc/scsi/scsi and sysfs may report - * the actual value or the modified one, depending on where the - * data comes from. - */ - if (sdev->scsi_level < SCSI_2) - sdev->scsi_level = sdev->sdev_target->scsi_level = SCSI_2; - /* Many devices have trouble transfering more than 32KB at a time, * while others have trouble with more than 64K. At this time we * are limiting both to 32K (64 sectores). @@ -176,7 +159,9 @@ static int slave_configure(struct scsi_device *sdev) * a Get-Max-LUN request, we won't lose much by setting the * revision level down to 2. The only devices that would be * affected are those with sparse LUNs. */ - sdev->scsi_level = sdev->sdev_target->scsi_level = SCSI_2; + if (sdev->scsi_level > SCSI_2) + sdev->sdev_target->scsi_level = + sdev->scsi_level = SCSI_2; /* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable * Hardware Error) when any low-level error occurs, @@ -194,6 +179,16 @@ static int slave_configure(struct scsi_device *sdev) sdev->use_10_for_ms = 1; } + /* The CB and CBI transports have no way to pass LUN values + * other than the bits in the second byte of a CDB. But those + * bits don't get set to the LUN value if the device reports + * scsi_level == 0 (UNKNOWN). Hence such devices must necessarily + * be single-LUN. + */ + if ((us->protocol == US_PR_CB || us->protocol == US_PR_CBI) && + sdev->scsi_level == SCSI_UNKNOWN) + us->max_lun = 0; + /* Some devices choke when they receive a PREVENT-ALLOW MEDIUM * REMOVAL command, so suppress those commands. */ if (us->flags & US_FL_NOT_LOCKABLE) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index b49f2a7..f49a62f 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -573,7 +573,7 @@ UNUSUAL_DEV( 0x054c, 0x002b, 0x0100, 0x0110, #endif /* Submitted by Olaf Hering, <olh@suse.de> SuSE Bugzilla #49049 */ -UNUSUAL_DEV( 0x054c, 0x002c, 0x0501, 0x0501, +UNUSUAL_DEV( 0x054c, 0x002c, 0x0501, 0x2000, "Sony", "USB Floppy Drive", US_SC_DEVICE, US_PR_DEVICE, NULL, @@ -1325,13 +1325,6 @@ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), -/* Reported by Jan Mate <mate@fiit.stuba.sk> */ -UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, - "Sony Ericsson", - "P990i", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), - /* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu> * Tested on hardware version 1.10. * Entry is needed only for the initializer function override. diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 7064450..7e7ec29 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -731,26 +731,27 @@ static int get_pipes(struct us_data *us) struct usb_endpoint_descriptor *ep_int = NULL; /* - * Find the endpoints we need. + * Find the first endpoint of each type we need. * We are expecting a minimum of 2 endpoints - in and out (bulk). - * An optional interrupt is OK (necessary for CBI protocol). + * An optional interrupt-in is OK (necessary for CBI protocol). * We will ignore any others. */ for (i = 0; i < altsetting->desc.bNumEndpoints; i++) { ep = &altsetting->endpoint[i].desc; - /* Is it a BULK endpoint? */ if (usb_endpoint_xfer_bulk(ep)) { - /* BULK in or out? */ - if (usb_endpoint_dir_in(ep)) - ep_in = ep; - else - ep_out = ep; + if (usb_endpoint_dir_in(ep)) { + if (!ep_in) + ep_in = ep; + } else { + if (!ep_out) + ep_out = ep; + } } - /* Is it an interrupt endpoint? */ - else if (usb_endpoint_xfer_int(ep)) { - ep_int = ep; + else if (usb_endpoint_is_int_in(ep)) { + if (!ep_int) + ep_int = ep; } } diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 157db77..683513e 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -11,6 +11,7 @@ header-y += netfilter_arp/ header-y += netfilter_bridge/ header-y += netfilter_ipv4/ header-y += netfilter_ipv6/ +header-y += usb/ header-y += affs_hardblocks.h header-y += aio_abi.h @@ -326,7 +327,6 @@ unifdef-y += udp.h unifdef-y += uinput.h unifdef-y += uio.h unifdef-y += unistd.h -unifdef-y += usb_ch9.h unifdef-y += usbdevice_fs.h unifdef-y += user.h unifdef-y += utsname.h diff --git a/include/linux/hid.h b/include/linux/hid.h index 93173fe..d26b08f 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -266,6 +266,7 @@ struct hid_item { #define HID_QUIRK_BAD_RELATIVE_KEYS 0x00010000 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00020000 #define HID_QUIRK_IGNORE_MOUSE 0x00040000 +#define HID_QUIRK_SONY_PS3_CONTROLLER 0x00080000 /* * This is the global environment of the parser. This information is diff --git a/include/linux/usb.h b/include/linux/usb.h index 733f38d..b5c226a 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -2,7 +2,7 @@ #define __LINUX_USB_H #include <linux/mod_devicetable.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> #define USB_MAJOR 180 #define USB_DEVICE_MAJOR 189 @@ -107,7 +107,8 @@ enum usb_interface_condition { * @needs_remote_wakeup: flag set when the driver requires remote-wakeup * capability during autosuspend. * @dev: driver model's view of this device - * @class_dev: driver model's class view of this device. + * @usb_dev: if an interface is bound to the USB major, this will point + * to the sysfs representation for that device. * @pm_usage_cnt: PM usage counter for this interface; autosuspend is not * allowed unless the counter is 0. * @@ -152,7 +153,7 @@ struct usb_interface { unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ struct device dev; /* interface specific device info */ - struct class_device *class_dev; + struct device *usb_dev; /* pointer to the usb class's device, if any */ int pm_usage_cnt; /* usage counter for autosuspend */ }; #define to_usb_interface(d) container_of(d, struct usb_interface, dev) @@ -372,7 +373,7 @@ struct usb_device { char *serial; /* iSerialNumber string, if present */ struct list_head filelist; - struct class_device *class_dev; + struct device *usbfs_dev; struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */ /* @@ -475,6 +476,8 @@ 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 int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id); extern struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor); @@ -554,6 +557,18 @@ static inline int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *e } /** + * usb_endpoint_xfer_control - check if the endpoint has control transfer type + * @epd: endpoint to be checked + * + * Returns true if the endpoint is of type control, otherwise it returns false. + */ +static inline int usb_endpoint_xfer_control(const struct usb_endpoint_descriptor *epd) +{ + return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_CONTROL); +} + +/** * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type * @epd: endpoint to be checked * @@ -723,11 +738,21 @@ static inline int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor /* ----------------------------------------------------------------------- */ +/* Stuff for dynamic usb ids */ struct usb_dynids { spinlock_t lock; struct list_head list; }; +struct usb_dynid { + struct list_head node; + struct usb_device_id id; +}; + +extern ssize_t usb_store_new_id(struct usb_dynids *dynids, + struct device_driver *driver, + const char *buf, size_t count); + /** * struct usbdrv_wrap - wrapper for driver-model structure * @driver: The driver-model core driver structure. @@ -1086,7 +1111,6 @@ struct urb struct kref kref; /* reference count of the URB */ spinlock_t lock; /* lock for the URB */ void *hcpriv; /* private data for host controller */ - int bandwidth; /* bandwidth for INT/ISO request */ atomic_t use_count; /* concurrent submissions counter */ u8 reject; /* submissions will fail */ diff --git a/include/linux/usb/Kbuild b/include/linux/usb/Kbuild new file mode 100644 index 0000000..43f160c --- /dev/null +++ b/include/linux/usb/Kbuild @@ -0,0 +1,5 @@ +unifdef-y += audio.h +unifdef-y += cdc.h +unifdef-y += ch9.h +unifdef-y += midi.h + diff --git a/include/linux/usb_ch9.h b/include/linux/usb/ch9.h index c720d10..ae78337 100644 --- a/include/linux/usb_ch9.h +++ b/include/linux/usb/ch9.h @@ -224,6 +224,7 @@ struct usb_device_descriptor { #define USB_CLASS_CONTENT_SEC 0x0d /* content security */ #define USB_CLASS_VIDEO 0x0e #define USB_CLASS_WIRELESS_CONTROLLER 0xe0 +#define USB_CLASS_MISC 0xef #define USB_CLASS_APP_SPEC 0xfe #define USB_CLASS_VENDOR_SPEC 0xff diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 10f99e5..33dcd85 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -179,6 +179,9 @@ static inline void usb_set_serial_data (struct usb_serial *serial, void *data) * memory structure allocation at this point in time. * @shutdown: pointer to the driver's shutdown function. This will be * called when the device is removed from the system. + * @usb_driver: pointer to the struct usb_driver that controls this + * device. This is necessary to allow dynamic ids to be added to + * the driver from sysfs. * * This structure is defines a USB Serial driver. It provides all of * the information that the USB serial core code needs. If the function @@ -202,6 +205,8 @@ struct usb_serial_driver { struct list_head driver_list; struct device_driver driver; + struct usb_driver *usb_driver; + struct usb_dynids dynids; int (*probe) (struct usb_serial *serial, const struct usb_device_id *id); int (*attach) (struct usb_serial *serial); diff --git a/include/linux/usb_gadgetfs.h b/include/linux/usb_gadgetfs.h index b53d6ae..8086d5a 100644 --- a/include/linux/usb_gadgetfs.h +++ b/include/linux/usb_gadgetfs.h @@ -2,7 +2,7 @@ #include <asm/types.h> #include <asm/ioctl.h> -#include <linux/usb_ch9.h> +#include <linux/usb/ch9.h> /* * Filesystem based user-mode API to USB Gadget controller hardware |