diff options
147 files changed, 14872 insertions, 4565 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index f9937ad..9734577 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -39,3 +39,16 @@ Description: If you want to suspend a device immediately but leave it free to wake up in response to I/O requests, you should write "0" to power/autosuspend. + +What: /sys/bus/usb/devices/.../power/persist +Date: May 2007 +KernelVersion: 2.6.23 +Contact: Alan Stern <stern@rowland.harvard.edu> +Description: + If CONFIG_USB_PERSIST is set, then each USB device directory + will contain a file named power/persist. The file holds a + boolean value (0 or 1) indicating whether or not the + "USB-Persist" facility is enabled for the device. Since the + facility is inherently dangerous, it is disabled by default + for all devices except hubs. For more information, see + Documentation/usb/persist.txt. diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt index 5b8d695..152b510 100644 --- a/Documentation/power/swsusp.txt +++ b/Documentation/power/swsusp.txt @@ -393,6 +393,9 @@ safest thing is to unmount all filesystems on removable media (such USB, Firewire, CompactFlash, MMC, external SATA, or even IDE hotplug bays) before suspending; then remount them after resuming. +There is a work-around for this problem. For more information, see +Documentation/usb/persist.txt. + Q: I upgraded the kernel from 2.6.15 to 2.6.16. Both kernels were compiled with the similar configuration files. Anyway I found that suspend to disk (and resume) is much slower on 2.6.16 compared to diff --git a/Documentation/usb/dma.txt b/Documentation/usb/dma.txt index 62844ae..e8b50b7 100644 --- a/Documentation/usb/dma.txt +++ b/Documentation/usb/dma.txt @@ -32,12 +32,15 @@ ELIMINATING COPIES It's good to avoid making CPUs copy data needlessly. The costs can add up, and effects like cache-trashing can impose subtle penalties. -- When you're allocating a buffer for DMA purposes anyway, use the buffer - primitives. Think of them as kmalloc and kfree that give you the right - kind of addresses to store in urb->transfer_buffer and urb->transfer_dma, - while guaranteeing that no hidden copies through DMA "bounce" buffers will - slow things down. You'd also set URB_NO_TRANSFER_DMA_MAP in - urb->transfer_flags: +- If you're doing lots of small data transfers from the same buffer all + the time, that can really burn up resources on systems which use an + IOMMU to manage the DMA mappings. It can cost MUCH more to set up and + tear down the IOMMU mappings with each request than perform the I/O! + + For those specific cases, USB has primitives to allocate less expensive + memory. They work like kmalloc and kfree versions that give you the right + kind of addresses to store in urb->transfer_buffer and urb->transfer_dma. + You'd also set URB_NO_TRANSFER_DMA_MAP in urb->transfer_flags: void *usb_buffer_alloc (struct usb_device *dev, size_t size, int mem_flags, dma_addr_t *dma); @@ -45,6 +48,10 @@ and effects like cache-trashing can impose subtle penalties. void usb_buffer_free (struct usb_device *dev, size_t size, void *addr, dma_addr_t dma); + Most drivers should *NOT* be using these primitives; they don't need + to use this type of memory ("dma-coherent"), and memory returned from + kmalloc() will work just fine. + For control transfers you can use the buffer primitives or not for each of the transfer buffer and setup buffer independently. Set the flag bits URB_NO_TRANSFER_DMA_MAP and URB_NO_SETUP_DMA_MAP to indicate which @@ -54,29 +61,39 @@ and effects like cache-trashing can impose subtle penalties. The memory buffer returned is "dma-coherent"; sometimes you might need to force a consistent memory access ordering by using memory barriers. It's not using a streaming DMA mapping, so it's good for small transfers on - systems where the I/O would otherwise tie up an IOMMU mapping. (See + systems where the I/O would otherwise thrash an IOMMU mapping. (See Documentation/DMA-mapping.txt for definitions of "coherent" and "streaming" DMA mappings.) Asking for 1/Nth of a page (as well as asking for N pages) is reasonably space-efficient. + On most systems the memory returned will be uncached, because the + semantics of dma-coherent memory require either bypassing CPU caches + or using cache hardware with bus-snooping support. While x86 hardware + has such bus-snooping, many other systems use software to flush cache + lines to prevent DMA conflicts. + - Devices on some EHCI controllers could handle DMA to/from high memory. - Driver probe() routines can notice this using a generic DMA call, then - tell higher level code (network, scsi, etc) about it like this: - if (dma_supported (&intf->dev, 0xffffffffffffffffULL)) - net->features |= NETIF_F_HIGHDMA; + Unfortunately, the current Linux DMA infrastructure doesn't have a sane + way to expose these capabilities ... and in any case, HIGHMEM is mostly a + design wart specific to x86_32. So your best bet is to ensure you never + pass a highmem buffer into a USB driver. That's easy; it's the default + behavior. Just don't override it; e.g. with NETIF_F_HIGHDMA. - That can eliminate dma bounce buffering of requests that originate (or - terminate) in high memory, in cases where the buffers aren't allocated - with usb_buffer_alloc() but instead are dma-mapped. + This may force your callers to do some bounce buffering, copying from + high memory to "normal" DMA memory. If you can come up with a good way + to fix this issue (for x86_32 machines with over 1 GByte of memory), + feel free to submit patches. WORKING WITH EXISTING BUFFERS Existing buffers aren't usable for DMA without first being mapped into the -DMA address space of the device. +DMA address space of the device. However, most buffers passed to your +driver can safely be used with such DMA mapping. (See the first section +of DMA-mapping.txt, titled "What memory is DMA-able?") - When you're using scatterlists, you can map everything at once. On some systems, this kicks in an IOMMU and turns the scatterlists into single @@ -114,3 +131,8 @@ DMA address space of the device. The calls manage urb->transfer_dma for you, and set URB_NO_TRANSFER_DMA_MAP so that usbcore won't map or unmap the buffer. The same goes for urb->setup_dma and URB_NO_SETUP_DMA_MAP for control requests. + +Note that several of those interfaces are currently commented out, since +they don't have current users. See the source code. Other than the dmasync +calls (where the underlying DMA primitives have changed), most of them can +easily be commented back in if you want to use them. diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt new file mode 100644 index 0000000..df54d64 --- /dev/null +++ b/Documentation/usb/persist.txt @@ -0,0 +1,156 @@ + USB device persistence during system suspend + + Alan Stern <stern@rowland.harvard.edu> + + September 2, 2006 (Updated May 29, 2007) + + + What is the problem? + +According to the USB specification, when a USB bus is suspended the +bus must continue to supply suspend current (around 1-5 mA). This +is so that devices can maintain their internal state and hubs can +detect connect-change events (devices being plugged in or unplugged). +The technical term is "power session". + +If a USB device's power session is interrupted then the system is +required to behave as though the device has been unplugged. It's a +conservative approach; in the absence of suspend current the computer +has no way to know what has actually happened. Perhaps the same +device is still attached or perhaps it was removed and a different +device plugged into the port. The system must assume the worst. + +By default, Linux behaves according to the spec. If a USB host +controller loses power during a system suspend, then when the system +wakes up all the devices attached to that controller are treated as +though they had disconnected. This is always safe and it is the +"officially correct" thing to do. + +For many sorts of devices this behavior doesn't matter in the least. +If the kernel wants to believe that your USB keyboard was unplugged +while the system was asleep and a new keyboard was plugged in when the +system woke up, who cares? It'll still work the same when you type on +it. + +Unfortunately problems _can_ arise, particularly with mass-storage +devices. The effect is exactly the same as if the device really had +been unplugged while the system was suspended. If you had a mounted +filesystem on the device, you're out of luck -- everything in that +filesystem is now inaccessible. This is especially annoying if your +root filesystem was located on the device, since your system will +instantly crash. + +Loss of power isn't the only mechanism to worry about. Anything that +interrupts a power session will have the same effect. For example, +even though suspend current may have been maintained while the system +was asleep, on many systems during the initial stages of wakeup the +firmware (i.e., the BIOS) resets the motherboard's USB host +controllers. Result: all the power sessions are destroyed and again +it's as though you had unplugged all the USB devices. Yes, it's +entirely the BIOS's fault, but that doesn't do _you_ any good unless +you can convince the BIOS supplier to fix the problem (lots of luck!). + +On many systems the USB host controllers will get reset after a +suspend-to-RAM. On almost all systems, no suspend current is +available during hibernation (also known as swsusp or suspend-to-disk). +You can check the kernel log after resuming to see if either of these +has happened; look for lines saying "root hub lost power or was reset". + +In practice, people are forced to unmount any filesystems on a USB +device before suspending. If the root filesystem is on a USB device, +the system can't be suspended at all. (All right, it _can_ be +suspended -- but it will crash as soon as it wakes up, which isn't +much better.) + + + What is the solution? + +Setting CONFIG_USB_PERSIST will cause the kernel to work around these +issues. It enables a mode in which the core USB device data +structures are allowed to persist across a power-session disruption. +It works like this. If the kernel sees that a USB host controller is +not in the expected state during resume (i.e., if the controller was +reset or otherwise had lost power) then it applies a persistence check +to each of the USB devices below that controller for which the +"persist" attribute is set. It doesn't try to resume the device; that +can't work once the power session is gone. Instead it issues a USB +port reset and then re-enumerates the device. (This is exactly the +same thing that happens whenever a USB device is reset.) If the +re-enumeration shows that the device now attached to that port has the +same descriptors as before, including the Vendor and Product IDs, then +the kernel continues to use the same device structure. In effect, the +kernel treats the device as though it had merely been reset instead of +unplugged. + +If no device is now attached to the port, or if the descriptors are +different from what the kernel remembers, then the treatment is what +you would expect. The kernel destroys the old device structure and +behaves as though the old device had been unplugged and a new device +plugged in, just as it would without the CONFIG_USB_PERSIST option. + +The end result is that the USB device remains available and usable. +Filesystem mounts and memory mappings are unaffected, and the world is +now a good and happy place. + +Note that even when CONFIG_USB_PERSIST is set, the "persist" feature +will be applied only to those devices for which it is enabled. You +can enable the feature by doing (as root): + + echo 1 >/sys/bus/usb/devices/.../power/persist + +where the "..." should be filled in the with the device's ID. Disable +the feature by writing 0 instead of 1. For hubs the feature is +automatically and permanently enabled, so you only have to worry about +setting it for devices where it really matters. + + + Is this the best solution? + +Perhaps not. Arguably, keeping track of mounted filesystems and +memory mappings across device disconnects should be handled by a +centralized Logical Volume Manager. Such a solution would allow you +to plug in a USB flash device, create a persistent volume associated +with it, unplug the flash device, plug it back in later, and still +have the same persistent volume associated with the device. As such +it would be more far-reaching than CONFIG_USB_PERSIST. + +On the other hand, writing a persistent volume manager would be a big +job and using it would require significant input from the user. This +solution is much quicker and easier -- and it exists now, a giant +point in its favor! + +Furthermore, the USB_PERSIST option applies to _all_ USB devices, not +just mass-storage devices. It might turn out to be equally useful for +other device types, such as network interfaces. + + + WARNING: Using CONFIG_USB_PERSIST can be dangerous!! + +When recovering an interrupted power session the kernel does its best +to make sure the USB device hasn't been changed; that is, the same +device is still plugged into the port as before. But the checks +aren't guaranteed to be 100% accurate. + +If you replace one USB device with another of the same type (same +manufacturer, same IDs, and so on) there's an excellent chance the +kernel won't detect the change. Serial numbers and other strings are +not compared. In many cases it wouldn't help if they were, because +manufacturers frequently omit serial numbers entirely in their +devices. + +Furthermore it's quite possible to leave a USB device exactly the same +while changing its media. If you replace the flash memory card in a +USB card reader while the system is asleep, the kernel will have no +way to know you did it. The kernel will assume that nothing has +happened and will continue to use the partition tables, inodes, and +memory mappings for the old card. + +If the kernel gets fooled in this way, it's almost certain to cause +data corruption and to crash your system. You'll have no one to blame +but yourself. + +YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK! + +That having been said, most of the time there shouldn't be any trouble +at all. The "persist" feature can be extremely useful. Make the most +of it. diff --git a/MAINTAINERS b/MAINTAINERS index 05a1f1a..845fbf4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3724,12 +3724,12 @@ L: netdev@vger.kernel.org W: http://pegasus2.sourceforge.net/ S: Maintained -USB PRINTER DRIVER -P: Vojtech Pavlik -M: vojtech@suse.cz +USB PRINTER DRIVER (usblp) +P: Pete Zaitcev +M: zaitcev@redhat.com L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net -S: Maintained +S: Supported USB RTL8150 DRIVER P: Petko Manolov diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 746a118..18c8b6c 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -1547,10 +1547,8 @@ static void ub_reset_enter(struct ub_dev *sc, int try) #endif #if 0 /* We let them stop themselves. */ - struct list_head *p; struct ub_lun *lun; - list_for_each(p, &sc->luns) { - lun = list_entry(p, struct ub_lun, link); + list_for_each_entry(lun, &sc->luns, link) { blk_stop_queue(lun->disk->queue); } #endif @@ -1562,7 +1560,6 @@ static void ub_reset_task(struct work_struct *work) { struct ub_dev *sc = container_of(work, struct ub_dev, reset_work); unsigned long flags; - struct list_head *p; struct ub_lun *lun; int lkr, rc; @@ -1608,8 +1605,7 @@ static void ub_reset_task(struct work_struct *work) spin_lock_irqsave(sc->lock, flags); sc->reset = 0; tasklet_schedule(&sc->tasklet); - list_for_each(p, &sc->luns) { - lun = list_entry(p, struct ub_lun, link); + list_for_each_entry(lun, &sc->luns, link) { blk_start_queue(lun->disk->queue); } wake_up(&sc->reset_wait); @@ -2348,7 +2344,6 @@ err_alloc: static void ub_disconnect(struct usb_interface *intf) { struct ub_dev *sc = usb_get_intfdata(intf); - struct list_head *p; struct ub_lun *lun; unsigned long flags; @@ -2403,8 +2398,7 @@ static void ub_disconnect(struct usb_interface *intf) /* * Unregister the upper layer. */ - list_for_each (p, &sc->luns) { - lun = list_entry(p, struct ub_lun, link); + list_for_each_entry(lun, &sc->luns, link) { del_gendisk(lun->disk); /* * I wish I could do: diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 3afa4a5..b2baeae 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1009,20 +1009,22 @@ static int hid_resume(struct usb_interface *intf) } /* Treat USB reset pretty much the same as suspend/resume */ -static void hid_pre_reset(struct usb_interface *intf) +static int hid_pre_reset(struct usb_interface *intf) { /* FIXME: What if the interface is already suspended? */ hid_suspend(intf, PMSG_ON); + return 0; } -static void hid_post_reset(struct usb_interface *intf) +/* Same routine used for post_reset and reset_resume */ +static int hid_post_reset(struct usb_interface *intf) { struct usb_device *dev = interface_to_usbdev (intf); hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0); /* FIXME: Any more reinitialization needed? */ - hid_resume(intf); + return hid_resume(intf); } static struct usb_device_id hid_usb_ids [] = { @@ -1039,6 +1041,7 @@ static struct usb_driver hid_driver = { .disconnect = hid_disconnect, .suspend = hid_suspend, .resume = hid_resume, + .reset_resume = hid_post_reset, .pre_reset = hid_pre_reset, .post_reset = hid_post_reset, .id_table = hid_usb_ids, diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 15499b7..172a606 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -2,9 +2,12 @@ # USB device configuration # -menu "USB support" +menuconfig USB_SUPPORT + bool "USB support" depends on HAS_IOMEM +if USB_SUPPORT + # Host-side USB depends on having a host controller # NOTE: dummy_hcd is always an option, but it's ignored here ... # NOTE: SL-811 option should be board-specific ... @@ -12,6 +15,7 @@ config USB_ARCH_HAS_HCD boolean default y if USB_ARCH_HAS_OHCI default y if USB_ARCH_HAS_EHCI + default y if PCMCIA # sl811_cs default y if ARM # SL-811 default PCI @@ -130,5 +134,4 @@ source "drivers/usb/atm/Kconfig" source "drivers/usb/gadget/Kconfig" -endmenu - +endif # USB_SUPPORT diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 72464b5..befff5f 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ -obj-$(CONFIG_USB_OHCI_AT91) += host/ +obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_ACM) += class/ obj-$(CONFIG_USB_PRINTER) += class/ diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 8bcf7fe..1bc8840 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -171,7 +171,7 @@ struct cxacru_data { struct delayed_work poll_work; u32 card_info[CXINF_MAX]; struct mutex poll_state_serialize; - int poll_state; + enum cxacru_poll_state poll_state; /* contol handles */ struct mutex cm_serialize; @@ -226,58 +226,48 @@ static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf) static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf) { - if (unlikely(value < 0)) { - return snprintf(buf, PAGE_SIZE, "%d.%02u\n", - value / 100, -value % 100); - } else { - return snprintf(buf, PAGE_SIZE, "%d.%02u\n", - value / 100, value % 100); - } + return snprintf(buf, PAGE_SIZE, "%d.%02u\n", + value / 100, abs(value) % 100); } static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf) { - switch (value) { - case 0: return snprintf(buf, PAGE_SIZE, "no\n"); - case 1: return snprintf(buf, PAGE_SIZE, "yes\n"); - default: return 0; - } + static char *str[] = { "no", "yes" }; + if (unlikely(value >= ARRAY_SIZE(str))) + return snprintf(buf, PAGE_SIZE, "%u\n", value); + return snprintf(buf, PAGE_SIZE, "%s\n", str[value]); } static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf) { - switch (value) { - case 1: return snprintf(buf, PAGE_SIZE, "not connected\n"); - case 2: return snprintf(buf, PAGE_SIZE, "connected\n"); - case 3: return snprintf(buf, PAGE_SIZE, "lost\n"); - default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); - } + static char *str[] = { NULL, "not connected", "connected", "lost" }; + if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL)) + return snprintf(buf, PAGE_SIZE, "%u\n", value); + return snprintf(buf, PAGE_SIZE, "%s\n", str[value]); } static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf) { - switch (value) { - case 0: return snprintf(buf, PAGE_SIZE, "down\n"); - case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n"); - case 2: return snprintf(buf, PAGE_SIZE, "training\n"); - case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n"); - case 4: return snprintf(buf, PAGE_SIZE, "exchange\n"); - case 5: return snprintf(buf, PAGE_SIZE, "up\n"); - case 6: return snprintf(buf, PAGE_SIZE, "waiting\n"); - case 7: return snprintf(buf, PAGE_SIZE, "initialising\n"); - default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); - } + static char *str[] = { "down", "attempting to activate", + "training", "channel analysis", "exchange", "up", + "waiting", "initialising" + }; + if (unlikely(value >= ARRAY_SIZE(str))) + return snprintf(buf, PAGE_SIZE, "%u\n", value); + return snprintf(buf, PAGE_SIZE, "%s\n", str[value]); } static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf) { - switch (value) { - case 0: return 0; - case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n"); - case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n"); - case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n"); - default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); - } + static char *str[] = { + NULL, + "ANSI T1.413", + "ITU-T G.992.1 (G.DMT)", + "ITU-T G.992.2 (G.LITE)" + }; + if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL)) + return snprintf(buf, PAGE_SIZE, "%u\n", value); + return snprintf(buf, PAGE_SIZE, "%s\n", str[value]); } /* @@ -308,11 +298,10 @@ static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev, struct cxacru_data *instance = usbatm_instance->driver_data; u32 value = instance->card_info[CXINF_LINE_STARTABLE]; - switch (value) { - case 0: return snprintf(buf, PAGE_SIZE, "running\n"); - case 1: return snprintf(buf, PAGE_SIZE, "stopped\n"); - default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); - } + static char *str[] = { "running", "stopped" }; + if (unlikely(value >= ARRAY_SIZE(str))) + return snprintf(buf, PAGE_SIZE, "%u\n", value); + return snprintf(buf, PAGE_SIZE, "%s\n", str[value]); } static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev, diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 0081c1d..cd51520c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1157,6 +1157,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 6778f9a..9a14789 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1,5 +1,5 @@ /* - * usblp.c Version 0.13 + * usblp.c * * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com> * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> @@ -61,11 +61,11 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.13" #define DRIVER_AUTHOR "Michael Gee, Pavel Machek, Vojtech Pavlik, Randy Dunlap, Pete Zaitcev, David Paschal" #define DRIVER_DESC "USB Printer Device Class driver" #define USBLP_BUF_SIZE 8192 +#define USBLP_BUF_SIZE_IN 1024 #define USBLP_DEVICE_ID_SIZE 1024 /* ioctls: */ @@ -127,14 +127,22 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H */ #define STATUS_BUF_SIZE 8 +/* + * Locks down the locking order: + * ->wmut locks wstatus. + * ->mut locks the whole usblp, except [rw]complete, and thus, by indirection, + * [rw]status. We only touch status when we know the side idle. + * ->lock locks what interrupt accesses. + */ struct usblp { struct usb_device *dev; /* USB device */ - struct mutex mut; /* locks this struct, especially "dev" */ - char *writebuf; /* write transfer_buffer */ + struct mutex wmut; + struct mutex mut; + spinlock_t lock; /* locks rcomplete, wcomplete */ char *readbuf; /* read transfer_buffer */ char *statusbuf; /* status transfer_buffer */ - struct urb *readurb, *writeurb; /* The urbs */ - wait_queue_head_t wait; /* Zzzzz ... */ + struct usb_anchor urbs; + wait_queue_head_t rwait, wwait; int readcount; /* Counter for reads */ int ifnum; /* Interface number */ struct usb_interface *intf; /* The interface */ @@ -147,8 +155,9 @@ struct usblp { } protocol[USBLP_MAX_PROTOCOLS]; int current_protocol; int minor; /* minor number of device */ - int wcomplete; /* writing is completed */ - int rcomplete; /* reading is completed */ + int wcomplete, rcomplete; + int wstatus; /* bytes written or error */ + int rstatus; /* bytes ready or error */ unsigned int quirks; /* quirks flags */ unsigned char used; /* True if open */ unsigned char present; /* True if not disconnected */ @@ -166,9 +175,6 @@ static void usblp_dump(struct usblp *usblp) { dbg("dev=0x%p", usblp->dev); dbg("present=%d", usblp->present); dbg("readbuf=0x%p", usblp->readbuf); - dbg("writebuf=0x%p", usblp->writebuf); - dbg("readurb=0x%p", usblp->readurb); - dbg("writeurb=0x%p", usblp->writeurb); dbg("readcount=%d", usblp->readcount); dbg("ifnum=%d", usblp->ifnum); for (p = USBLP_FIRST_PROTOCOL; p <= USBLP_LAST_PROTOCOL; p++) { @@ -178,8 +184,8 @@ static void usblp_dump(struct usblp *usblp) { } dbg("current_protocol=%d", usblp->current_protocol); dbg("minor=%d", usblp->minor); - dbg("wcomplete=%d", usblp->wcomplete); - dbg("rcomplete=%d", usblp->rcomplete); + dbg("wstatus=%d", usblp->wstatus); + dbg("rstatus=%d", usblp->rstatus); dbg("quirks=%d", usblp->quirks); dbg("used=%d", usblp->used); dbg("bidir=%d", usblp->bidir); @@ -222,6 +228,11 @@ static const struct quirk_printer_struct quirk_printers[] = { { 0, 0 } }; +static int usblp_wwait(struct usblp *usblp, int nonblock); +static int usblp_wtest(struct usblp *usblp, int nonblock); +static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock); +static int usblp_rtest(struct usblp *usblp, int nonblock); +static int usblp_submit_read(struct usblp *usblp); static int usblp_select_alts(struct usblp *usblp); static int usblp_set_protocol(struct usblp *usblp, int protocol); static int usblp_cache_device_id_string(struct usblp *usblp); @@ -279,33 +290,47 @@ static void usblp_bulk_read(struct urb *urb) { struct usblp *usblp = urb->context; - if (unlikely(!usblp || !usblp->dev || !usblp->used)) - return; - - if (unlikely(!usblp->present)) - goto unplug; - if (unlikely(urb->status)) - warn("usblp%d: nonzero read/write bulk status received: %d", - usblp->minor, urb->status); + if (usblp->present && usblp->used) { + if (urb->status) + printk(KERN_WARNING "usblp%d: " + "nonzero read bulk status received: %d\n", + usblp->minor, urb->status); + } + spin_lock(&usblp->lock); + if (urb->status < 0) + usblp->rstatus = urb->status; + else + usblp->rstatus = urb->actual_length; usblp->rcomplete = 1; -unplug: - wake_up_interruptible(&usblp->wait); + wake_up(&usblp->rwait); + spin_unlock(&usblp->lock); + + usb_free_urb(urb); } static void usblp_bulk_write(struct urb *urb) { struct usblp *usblp = urb->context; - if (unlikely(!usblp || !usblp->dev || !usblp->used)) - return; - if (unlikely(!usblp->present)) - goto unplug; - if (unlikely(urb->status)) - warn("usblp%d: nonzero read/write bulk status received: %d", - usblp->minor, urb->status); + if (usblp->present && usblp->used) { + if (urb->status) + printk(KERN_WARNING "usblp%d: " + "nonzero write bulk status received: %d\n", + usblp->minor, urb->status); + } + spin_lock(&usblp->lock); + if (urb->status < 0) + usblp->wstatus = urb->status; + else + usblp->wstatus = urb->actual_length; usblp->wcomplete = 1; -unplug: - wake_up_interruptible(&usblp->wait); + wake_up(&usblp->wwait); + spin_unlock(&usblp->lock); + + /* XXX Use usb_setup_bulk_urb when available. Talk to Marcel. */ + kfree(urb->transfer_buffer); + urb->transfer_buffer = NULL; /* Not refcounted, so to be safe... */ + usb_free_urb(urb); } /* @@ -322,7 +347,8 @@ static int usblp_check_status(struct usblp *usblp, int err) error = usblp_read_status (usblp, usblp->statusbuf); if (error < 0) { if (printk_ratelimit()) - err("usblp%d: error %d reading printer status", + printk(KERN_ERR + "usblp%d: error %d reading printer status\n", usblp->minor, error); return 0; } @@ -336,8 +362,10 @@ static int usblp_check_status(struct usblp *usblp, int err) if (~status & LP_PSELECD) newerr = 2; - if (newerr != err) - info("usblp%d: %s", usblp->minor, usblp_messages[newerr]); + if (newerr != err) { + printk(KERN_INFO "usblp%d: %s\n", + usblp->minor, usblp_messages[newerr]); + } return newerr; } @@ -345,12 +373,9 @@ static int usblp_check_status(struct usblp *usblp, int err) static int handle_bidir (struct usblp *usblp) { if (usblp->bidir && usblp->used && !usblp->sleeping) { - usblp->readcount = 0; - usblp->readurb->dev = usblp->dev; - if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0) + if (usblp_submit_read(usblp) < 0) return -EIO; } - return 0; } @@ -403,11 +428,9 @@ static int usblp_open(struct inode *inode, struct file *file) usblp->used = 1; file->private_data = usblp; - usblp->writeurb->transfer_buffer_length = 0; usblp->wcomplete = 1; /* we begin writeable */ + usblp->wstatus = 0; usblp->rcomplete = 0; - usblp->writeurb->status = 0; - usblp->readurb->status = 0; if (handle_bidir(usblp) < 0) { usblp->used = 0; @@ -421,20 +444,17 @@ out: static void usblp_cleanup (struct usblp *usblp) { - info("usblp%d: removed", usblp->minor); + printk(KERN_INFO "usblp%d: removed\n", usblp->minor); + kfree(usblp->readbuf); kfree (usblp->device_id_string); kfree (usblp->statusbuf); - usb_free_urb(usblp->writeurb); - usb_free_urb(usblp->readurb); kfree (usblp); } static void usblp_unlink_urbs(struct usblp *usblp) { - usb_kill_urb(usblp->writeurb); - if (usblp->bidir) - usb_kill_urb(usblp->readurb); + usb_kill_anchored_urbs(&usblp->urbs); } static int usblp_release(struct inode *inode, struct file *file) @@ -455,10 +475,18 @@ static int usblp_release(struct inode *inode, struct file *file) /* No kernel lock - fine */ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait) { + int ret; + unsigned long flags; + struct usblp *usblp = file->private_data; - poll_wait(file, &usblp->wait, wait); - return ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN | POLLRDNORM) + /* Should we check file->f_mode & FMODE_WRITE before poll_wait()? */ + poll_wait(file, &usblp->rwait, wait); + poll_wait(file, &usblp->wwait, wait); + spin_lock_irqsave(&usblp->lock, flags); + ret = ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN | POLLRDNORM) | (!usblp->wcomplete ? 0 : POLLOUT | POLLWRNORM); + spin_unlock_irqrestore(&usblp->lock, flags); + return ret; } static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -632,10 +660,11 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case LPGETSTATUS: - if (usblp_read_status(usblp, usblp->statusbuf)) { + if ((retval = usblp_read_status(usblp, usblp->statusbuf))) { if (printk_ratelimit()) - err("usblp%d: failed reading printer status", - usblp->minor); + printk(KERN_ERR "usblp%d:" + "failed reading printer status (%d)\n", + usblp->minor, retval); retval = -EIO; goto done; } @@ -656,168 +685,303 @@ done: static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct usblp *usblp = file->private_data; - int timeout, intr, rv, err = 0, transfer_length = 0; - size_t writecount = 0; + char *writebuf; + struct urb *writeurb; + int rv; + int transfer_length; + ssize_t writecount = 0; + + if (mutex_lock_interruptible(&usblp->wmut)) { + rv = -EINTR; + goto raise_biglock; + } + if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0) + goto raise_wait; while (writecount < count) { - if (!usblp->wcomplete) { - barrier(); - if (file->f_flags & O_NONBLOCK) { - writecount += transfer_length; - return writecount ? writecount : -EAGAIN; - } - - timeout = USBLP_WRITE_TIMEOUT; - - rv = wait_event_interruptible_timeout(usblp->wait, usblp->wcomplete || !usblp->present , timeout); - if (rv < 0) - return writecount ? writecount : -EINTR; - } - intr = mutex_lock_interruptible (&usblp->mut); - if (intr) - return writecount ? writecount : -EINTR; - if (!usblp->present) { - mutex_unlock (&usblp->mut); - return -ENODEV; - } - - if (usblp->sleeping) { - mutex_unlock (&usblp->mut); - return writecount ? writecount : -ENODEV; - } - - if (usblp->writeurb->status != 0) { - if (usblp->quirks & USBLP_QUIRK_BIDIR) { - if (!usblp->wcomplete) - err("usblp%d: error %d writing to printer", - usblp->minor, usblp->writeurb->status); - err = usblp->writeurb->status; - } else - err = usblp_check_status(usblp, err); - mutex_unlock (&usblp->mut); - - /* if the fault was due to disconnect, let khubd's - * call to usblp_disconnect() grab usblp->mut ... - */ - schedule (); - continue; - } - - /* We must increment writecount here, and not at the - * end of the loop. Otherwise, the final loop iteration may - * be skipped, leading to incomplete printer output. + /* + * Step 1: Submit next block. */ - writecount += transfer_length; - if (writecount == count) { - mutex_unlock(&usblp->mut); - break; - } - - transfer_length=(count - writecount); - if (transfer_length > USBLP_BUF_SIZE) + if ((transfer_length = count - writecount) > USBLP_BUF_SIZE) transfer_length = USBLP_BUF_SIZE; - usblp->writeurb->transfer_buffer_length = transfer_length; - - if (copy_from_user(usblp->writeurb->transfer_buffer, + rv = -ENOMEM; + if ((writebuf = kmalloc(USBLP_BUF_SIZE, GFP_KERNEL)) == NULL) + goto raise_buf; + if ((writeurb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) + goto raise_urb; + usb_fill_bulk_urb(writeurb, usblp->dev, + usb_sndbulkpipe(usblp->dev, + usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress), + writebuf, transfer_length, usblp_bulk_write, usblp); + usb_anchor_urb(writeurb, &usblp->urbs); + + if (copy_from_user(writebuf, buffer + writecount, transfer_length)) { - mutex_unlock(&usblp->mut); - return writecount ? writecount : -EFAULT; + rv = -EFAULT; + goto raise_badaddr; } - usblp->writeurb->dev = usblp->dev; + spin_lock_irq(&usblp->lock); usblp->wcomplete = 0; - err = usb_submit_urb(usblp->writeurb, GFP_KERNEL); - if (err) { + spin_unlock_irq(&usblp->lock); + if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) { + usblp->wstatus = 0; + spin_lock_irq(&usblp->lock); usblp->wcomplete = 1; - if (err != -ENOMEM) - count = -EIO; - else - count = writecount ? writecount : -ENOMEM; - mutex_unlock (&usblp->mut); - break; + wake_up(&usblp->wwait); + spin_unlock_irq(&usblp->lock); + if (rv != -ENOMEM) + rv = -EIO; + goto raise_submit; + } + + /* + * Step 2: Wait for transfer to end, collect results. + */ + rv = usblp_wwait(usblp, !!(file->f_flags&O_NONBLOCK)); + if (rv < 0) { + /* + * If interrupted, we simply leave the URB to dangle, + * so the ->release will call usb_kill_urb(). + */ + goto collect_error; } - mutex_unlock (&usblp->mut); + + if (usblp->wstatus < 0) { + usblp_check_status(usblp, 0); + rv = -EIO; + goto collect_error; + } + /* + * This is critical: it must be our URB, not other writer's. + * The wmut exists mainly to cover us here. + */ + writecount += usblp->wstatus; } - return count; + mutex_unlock(&usblp->wmut); + return writecount; + +raise_submit: +raise_badaddr: + usb_unanchor_urb(writeurb); + usb_free_urb(writeurb); +raise_urb: + kfree(writebuf); +raise_buf: +raise_wait: +collect_error: /* Out of raise sequence */ + mutex_unlock(&usblp->wmut); +raise_biglock: + return writecount ? writecount : rv; } -static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +/* + * Notice that we fail to restart in a few cases: on EFAULT, on restart + * error, etc. This is the historical behaviour. In all such cases we return + * EIO, and applications loop in order to get the new read going. + */ +static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, loff_t *ppos) { struct usblp *usblp = file->private_data; - int rv, intr; + ssize_t count; + ssize_t avail; + int rv; if (!usblp->bidir) return -EINVAL; - intr = mutex_lock_interruptible (&usblp->mut); - if (intr) - return -EINTR; - if (!usblp->present) { - count = -ENODEV; + rv = usblp_rwait_and_lock(usblp, !!(file->f_flags & O_NONBLOCK)); + if (rv < 0) + return rv; + + if ((avail = usblp->rstatus) < 0) { + printk(KERN_ERR "usblp%d: error %d reading from printer\n", + usblp->minor, (int)avail); + usblp_submit_read(usblp); + count = -EIO; goto done; } - if (!usblp->rcomplete) { - barrier(); + count = len < avail - usblp->readcount ? len : avail - usblp->readcount; + if (count != 0 && + copy_to_user(buffer, usblp->readbuf + usblp->readcount, count)) { + count = -EFAULT; + goto done; + } - if (file->f_flags & O_NONBLOCK) { - count = -EAGAIN; - goto done; - } - mutex_unlock(&usblp->mut); - rv = wait_event_interruptible(usblp->wait, usblp->rcomplete || !usblp->present); - mutex_lock(&usblp->mut); - if (rv < 0) { - count = -EINTR; + if ((usblp->readcount += count) == avail) { + if (usblp_submit_read(usblp) < 0) { + /* We don't want to leak USB return codes into errno. */ + if (count == 0) + count = -EIO; goto done; } } - if (!usblp->present) { - count = -ENODEV; - goto done; +done: + mutex_unlock (&usblp->mut); + return count; +} + +/* + * Wait for the write path to come idle. + * This is called under the ->wmut, so the idle path stays idle. + * + * Our write path has a peculiar property: it does not buffer like a tty, + * but waits for the write to succeed. This allows our ->release to bug out + * without waiting for writes to drain. But it obviously does not work + * when O_NONBLOCK is set. So, applications setting O_NONBLOCK must use + * select(2) or poll(2) to wait for the buffer to drain before closing. + * Alternatively, set blocking mode with fcntl and issue a zero-size write. + * + * Old v0.13 code had a non-functional timeout for wait_event(). Someone forgot + * to check the return code for timeout expiration, so it had no effect. + * Apparently, it was intended to check for error conditons, such as out + * of paper. It is going to return when we settle things with CUPS. XXX + */ +static int usblp_wwait(struct usblp *usblp, int nonblock) +{ + DECLARE_WAITQUEUE(waita, current); + int rc; + + add_wait_queue(&usblp->wwait, &waita); + for (;;) { + if (mutex_lock_interruptible(&usblp->mut)) { + rc = -EINTR; + break; + } + set_current_state(TASK_INTERRUPTIBLE); + if ((rc = usblp_wtest(usblp, nonblock)) < 0) { + mutex_unlock(&usblp->mut); + break; + } + mutex_unlock(&usblp->mut); + if (rc == 0) + break; + schedule(); } + set_current_state(TASK_RUNNING); + remove_wait_queue(&usblp->wwait, &waita); + return rc; +} - if (usblp->sleeping) { - count = -ENODEV; - goto done; +static int usblp_wtest(struct usblp *usblp, int nonblock) +{ + unsigned long flags; + + if (!usblp->present) + return -ENODEV; + if (signal_pending(current)) + return -EINTR; + spin_lock_irqsave(&usblp->lock, flags); + if (usblp->wcomplete) { + spin_unlock_irqrestore(&usblp->lock, flags); + return 0; } + spin_unlock_irqrestore(&usblp->lock, flags); + if (usblp->sleeping) + return -ENODEV; + if (nonblock) + return -EAGAIN; + return 1; +} - if (usblp->readurb->status) { - err("usblp%d: error %d reading from printer", - usblp->minor, usblp->readurb->status); - usblp->readurb->dev = usblp->dev; - usblp->readcount = 0; - usblp->rcomplete = 0; - if (usb_submit_urb(usblp->readurb, GFP_KERNEL) < 0) - dbg("error submitting urb"); - count = -EIO; - goto done; +/* + * Wait for read bytes to become available. This probably should have been + * called usblp_r_lock_and_wait(), because we lock first. But it's a traditional + * name for functions which lock and return. + * + * We do not use wait_event_interruptible because it makes locking iffy. + */ +static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock) +{ + DECLARE_WAITQUEUE(waita, current); + int rc; + + add_wait_queue(&usblp->rwait, &waita); + for (;;) { + if (mutex_lock_interruptible(&usblp->mut)) { + rc = -EINTR; + break; + } + set_current_state(TASK_INTERRUPTIBLE); + if ((rc = usblp_rtest(usblp, nonblock)) < 0) { + mutex_unlock(&usblp->mut); + break; + } + if (rc == 0) /* Keep it locked */ + break; + mutex_unlock(&usblp->mut); + schedule(); } + set_current_state(TASK_RUNNING); + remove_wait_queue(&usblp->rwait, &waita); + return rc; +} - count = count < usblp->readurb->actual_length - usblp->readcount ? - count : usblp->readurb->actual_length - usblp->readcount; +static int usblp_rtest(struct usblp *usblp, int nonblock) +{ + unsigned long flags; - if (copy_to_user(buffer, usblp->readurb->transfer_buffer + usblp->readcount, count)) { - count = -EFAULT; - goto done; + if (!usblp->present) + return -ENODEV; + if (signal_pending(current)) + return -EINTR; + spin_lock_irqsave(&usblp->lock, flags); + if (usblp->rcomplete) { + spin_unlock_irqrestore(&usblp->lock, flags); + return 0; } + spin_unlock_irqrestore(&usblp->lock, flags); + if (usblp->sleeping) + return -ENODEV; + if (nonblock) + return -EAGAIN; + return 1; +} - if ((usblp->readcount += count) == usblp->readurb->actual_length) { - usblp->readcount = 0; - usblp->readurb->dev = usblp->dev; - usblp->rcomplete = 0; - if (usb_submit_urb(usblp->readurb, GFP_KERNEL)) { - count = -EIO; - goto done; - } +/* + * Please check ->bidir and other such things outside for now. + */ +static int usblp_submit_read(struct usblp *usblp) +{ + struct urb *urb; + unsigned long flags; + int rc; + + rc = -ENOMEM; + if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) + goto raise_urb; + + usb_fill_bulk_urb(urb, usblp->dev, + usb_rcvbulkpipe(usblp->dev, + usblp->protocol[usblp->current_protocol].epread->bEndpointAddress), + usblp->readbuf, USBLP_BUF_SIZE_IN, + usblp_bulk_read, usblp); + usb_anchor_urb(urb, &usblp->urbs); + + spin_lock_irqsave(&usblp->lock, flags); + usblp->readcount = 0; /* XXX Why here? */ + usblp->rcomplete = 0; + spin_unlock_irqrestore(&usblp->lock, flags); + if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) { + dbg("error submitting urb (%d)", rc); + spin_lock_irqsave(&usblp->lock, flags); + usblp->rstatus = rc; + usblp->rcomplete = 1; + spin_unlock_irqrestore(&usblp->lock, flags); + goto raise_submit; } -done: - mutex_unlock (&usblp->mut); - return count; + return 0; + +raise_submit: + usb_unanchor_urb(urb); + usb_free_urb(urb); +raise_urb: + return rc; } /* @@ -891,55 +1055,41 @@ static int usblp_probe(struct usb_interface *intf, /* Malloc and start initializing usblp structure so we can use it * directly. */ if (!(usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL))) { - err("out of memory for usblp"); + retval = -ENOMEM; goto abort; } usblp->dev = dev; + mutex_init(&usblp->wmut); mutex_init (&usblp->mut); - init_waitqueue_head(&usblp->wait); + spin_lock_init(&usblp->lock); + init_waitqueue_head(&usblp->rwait); + init_waitqueue_head(&usblp->wwait); + init_usb_anchor(&usblp->urbs); usblp->ifnum = intf->cur_altsetting->desc.bInterfaceNumber; usblp->intf = intf; - usblp->writeurb = usb_alloc_urb(0, GFP_KERNEL); - if (!usblp->writeurb) { - err("out of memory"); - goto abort; - } - usblp->readurb = usb_alloc_urb(0, GFP_KERNEL); - if (!usblp->readurb) { - err("out of memory"); - goto abort; - } - /* Malloc device ID string buffer to the largest expected length, * since we can re-query it on an ioctl and a dynamic string * could change in length. */ if (!(usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL))) { - err("out of memory for device_id_string"); + retval = -ENOMEM; goto abort; } - usblp->writebuf = usblp->readbuf = NULL; - usblp->writeurb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - usblp->readurb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - /* Malloc write & read buffers. We somewhat wastefully + /* + * Allocate read buffer. We somewhat wastefully * malloc both regardless of bidirectionality, because the - * alternate setting can be changed later via an ioctl. */ - if (!(usblp->writebuf = usb_buffer_alloc(dev, USBLP_BUF_SIZE, - GFP_KERNEL, &usblp->writeurb->transfer_dma))) { - err("out of memory for write buf"); - goto abort; - } - if (!(usblp->readbuf = usb_buffer_alloc(dev, USBLP_BUF_SIZE, - GFP_KERNEL, &usblp->readurb->transfer_dma))) { - err("out of memory for read buf"); + * alternate setting can be changed later via an ioctl. + */ + if (!(usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL))) { + retval = -ENOMEM; goto abort; } /* Allocate buffer for printer status */ usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL); if (!usblp->statusbuf) { - err("out of memory for statusbuf"); + retval = -ENOMEM; goto abort; } @@ -954,12 +1104,15 @@ static int usblp_probe(struct usb_interface *intf, dbg("incompatible printer-class device 0x%4.4X/0x%4.4X", le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); + retval = -ENODEV; goto abort; } /* Setup the selected alternate setting and endpoints. */ - if (usblp_set_protocol(usblp, protocol) < 0) + if (usblp_set_protocol(usblp, protocol) < 0) { + retval = -ENODEV; /* ->probe isn't ->ioctl */ goto abort; + } /* Retrieve and store the device ID string. */ usblp_cache_device_id_string(usblp); @@ -977,12 +1130,14 @@ static int usblp_probe(struct usb_interface *intf, retval = usb_register_dev(intf, &usblp_class); if (retval) { - err("Not able to get a minor for this device."); + printk(KERN_ERR "usblp: Not able to get a minor" + " (base %u, slice default): %d\n", + USBLP_MINOR_BASE, retval); goto abort_intfdata; } usblp->minor = intf->minor; - info("usblp%d: USB %sdirectional printer dev %d " - "if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X", + printk(KERN_INFO "usblp%d: USB %sdirectional printer dev %d " + "if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X\n", usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum, usblp->ifnum, usblp->protocol[usblp->current_protocol].alt_setting, @@ -997,19 +1152,12 @@ abort_intfdata: device_remove_file(&intf->dev, &dev_attr_ieee1284_id); abort: if (usblp) { - if (usblp->writebuf) - usb_buffer_free (usblp->dev, USBLP_BUF_SIZE, - usblp->writebuf, usblp->writeurb->transfer_dma); - if (usblp->readbuf) - usb_buffer_free (usblp->dev, USBLP_BUF_SIZE, - usblp->readbuf, usblp->readurb->transfer_dma); + kfree(usblp->readbuf); kfree(usblp->statusbuf); kfree(usblp->device_id_string); - usb_free_urb(usblp->writeurb); - usb_free_urb(usblp->readurb); kfree(usblp); } - return -EIO; + return retval; } /* @@ -1078,8 +1226,9 @@ static int usblp_select_alts(struct usblp *usblp) if (ifd->desc.bInterfaceProtocol == 1) { epread = NULL; } else if (usblp->quirks & USBLP_QUIRK_BIDIR) { - info("Disabling reads from problem bidirectional " - "printer on usblp%d", usblp->minor); + printk(KERN_INFO "usblp%d: Disabling reads from " + "problematic bidirectional printer\n", + usblp->minor); epread = NULL; } @@ -1119,25 +1268,12 @@ static int usblp_set_protocol(struct usblp *usblp, int protocol) return -EINVAL; r = usb_set_interface(usblp->dev, usblp->ifnum, alts); if (r < 0) { - err("can't set desired altsetting %d on interface %d", + printk(KERN_ERR "usblp: can't set desired altsetting %d on interface %d\n", alts, usblp->ifnum); return r; } - usb_fill_bulk_urb(usblp->writeurb, usblp->dev, - usb_sndbulkpipe(usblp->dev, - usblp->protocol[protocol].epwrite->bEndpointAddress), - usblp->writebuf, 0, - usblp_bulk_write, usblp); - usblp->bidir = (usblp->protocol[protocol].epread != NULL); - if (usblp->bidir) - usb_fill_bulk_urb(usblp->readurb, usblp->dev, - usb_rcvbulkpipe(usblp->dev, - usblp->protocol[protocol].epread->bEndpointAddress), - usblp->readbuf, USBLP_BUF_SIZE, - usblp_bulk_read, usblp); - usblp->current_protocol = protocol; dbg("usblp%d set protocol %d", usblp->minor, protocol); return 0; @@ -1190,13 +1326,11 @@ static void usblp_disconnect(struct usb_interface *intf) mutex_lock (&usblp_mutex); mutex_lock (&usblp->mut); usblp->present = 0; + wake_up(&usblp->wwait); + wake_up(&usblp->rwait); usb_set_intfdata (intf, NULL); usblp_unlink_urbs(usblp); - usb_buffer_free (usblp->dev, USBLP_BUF_SIZE, - usblp->writebuf, usblp->writeurb->transfer_dma); - usb_buffer_free (usblp->dev, USBLP_BUF_SIZE, - usblp->readbuf, usblp->readurb->transfer_dma); mutex_unlock (&usblp->mut); if (!usblp->used) @@ -1211,6 +1345,11 @@ static int usblp_suspend (struct usb_interface *intf, pm_message_t message) /* we take no more IO */ usblp->sleeping = 1; usblp_unlink_urbs(usblp); +#if 0 /* XXX Do we want this? What if someone is reading, should we fail? */ + /* not strictly necessary, but just in case */ + wake_up(&usblp->wwait); + wake_up(&usblp->rwait); +#endif return 0; } @@ -1251,12 +1390,7 @@ static struct usb_driver usblp_driver = { static int __init usblp_init(void) { - int retval; - retval = usb_register(&usblp_driver); - if (!retval) - info(DRIVER_VERSION ": " DRIVER_DESC); - - return retval; + return usb_register(&usblp_driver); } static void __exit usblp_exit(void) diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 346fc03..97b09f28 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -86,6 +86,31 @@ config USB_SUSPEND If you are unsure about this, say N here. +config USB_PERSIST + bool "USB device persistence during system suspend (DANGEROUS)" + depends on USB && PM && EXPERIMENTAL + default n + help + + If you say Y here and enable the "power/persist" attribute + for a USB device, the device's data structures will remain + persistent across system suspend, even if the USB bus loses + power. (This includes hibernation, also known as swsusp or + suspend-to-disk.) The devices will reappear as if by magic + when the system wakes up, with no need to unmount USB + filesystems, rmmod host-controller drivers, or do anything + else. + + WARNING: This option can be dangerous! + + If a USB device is replaced by another of the same type while + the system is asleep, there's a good chance the kernel won't + detect the change. Likewise if the media in a USB storage + device is replaced. When this happens it's almost certain to + cause data corruption and maybe even crash your system. + + If you are unsure, say N here. + config USB_OTG bool depends on USB && EXPERIMENTAL diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index dd34823..cb69aa1 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -85,15 +85,21 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, memcpy(&endpoint->desc, d, n); INIT_LIST_HEAD(&endpoint->urb_list); - /* If the bInterval value is outside the legal range, - * set it to a default value: 32 ms */ + /* Fix up bInterval values outside the legal range. Use 32 ms if no + * proper value can be guessed. */ i = 0; /* i = min, j = max, n = default */ j = 255; if (usb_endpoint_xfer_int(d)) { i = 1; switch (to_usb_device(ddev)->speed) { case USB_SPEED_HIGH: - n = 9; /* 32 ms = 2^(9-1) uframes */ + /* Many device manufacturers are using full-speed + * bInterval values in high-speed interrupt endpoint + * descriptors. Try to fix those and fall back to a + * 32 ms default value otherwise. */ + n = fls(d->bInterval*8); + if (n == 0) + n = 9; /* 32 ms = 2^(9-1) uframes */ j = 16; break; default: /* USB_SPEED_FULL or _LOW */ @@ -124,6 +130,21 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, endpoint->desc.bInterval = n; } + /* Some buggy low-speed devices have Bulk endpoints, which is + * explicitly forbidden by the USB spec. In an attempt to make + * them usable, we will try treating them as Interrupt endpoints. + */ + if (to_usb_device(ddev)->speed == USB_SPEED_LOW && + usb_endpoint_xfer_bulk(d)) { + dev_warn(ddev, "config %d interface %d altsetting %d " + "endpoint 0x%X is Bulk; changing to Interrupt\n", + cfgno, inum, asnum, d->bEndpointAddress); + endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT; + endpoint->desc.bInterval = 1; + if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8) + endpoint->desc.wMaxPacketSize = cpu_to_le16(8); + } + /* Skip over any Class Specific or Vendor Specific descriptors; * find the next endpoint or interface descriptor */ endpoint->extra = buffer; @@ -274,6 +295,7 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, struct usb_descriptor_header *header; int len, retval; u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; + unsigned iad_num = 0; memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); if (config->desc.bDescriptorType != USB_DT_CONFIG || @@ -351,6 +373,20 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, ++n; } + } else if (header->bDescriptorType == + USB_DT_INTERFACE_ASSOCIATION) { + if (iad_num == USB_MAXIADS) { + dev_warn(ddev, "found more Interface " + "Association Descriptors " + "than allocated for in " + "configuration %d\n", cfgno); + } else { + config->intf_assoc[iad_num] = + (struct usb_interface_assoc_descriptor + *)header; + iad_num++; + } + } else if (header->bDescriptorType == USB_DT_DEVICE || header->bDescriptorType == USB_DT_CONFIG) dev_warn(ddev, "config %d contains an unexpected " diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 6753ca0..87c794d6 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -102,6 +102,10 @@ static const char *format_config = /* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; +static const char *format_iad = +/* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */ + "A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n"; + static const char *format_iface = /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; @@ -146,6 +150,7 @@ static const struct class_info clas_info[] = {USB_CLASS_STILL_IMAGE, "still"}, {USB_CLASS_CSCID, "scard"}, {USB_CLASS_CONTENT_SEC, "c-sec"}, + {USB_CLASS_VIDEO, "video"}, {-1, "unk."} /* leave as last */ }; @@ -286,6 +291,21 @@ static char *usb_dump_interface( return start; } +static char *usb_dump_iad_descriptor(char *start, char *end, + const struct usb_interface_assoc_descriptor *iad) +{ + if (start > end) + return start; + start += sprintf(start, format_iad, + iad->bFirstInterface, + iad->bInterfaceCount, + iad->bFunctionClass, + class_decode(iad->bFunctionClass), + iad->bFunctionSubClass, + iad->bFunctionProtocol); + return start; +} + /* TBD: * 0. TBDs * 1. marking active interface altsettings (code lists all, but should mark @@ -322,6 +342,12 @@ static char *usb_dump_config ( if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ return start + sprintf(start, "(null Cfg. desc.)\n"); start = usb_dump_config_descriptor(start, end, &config->desc, active); + for (i = 0; i < USB_MAXIADS; i++) { + if (config->intf_assoc[i] == NULL) + break; + start = usb_dump_iad_descriptor(start, end, + config->intf_assoc[i]); + } for (i = 0; i < config->desc.bNumInterfaces; i++) { intfc = config->intf_cache[i]; interface = config->interface[i]; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2619986..73c4936 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -24,10 +24,19 @@ #include <linux/device.h> #include <linux/usb.h> +#include <linux/usb/quirks.h> #include <linux/workqueue.h> #include "hcd.h" #include "usb.h" +#define VERBOSE_DEBUG 0 + +#if VERBOSE_DEBUG +#define dev_vdbg dev_dbg +#else +#define dev_vdbg(dev, fmt, args...) do { } while (0) +#endif + #ifdef CONFIG_HOTPLUG /* @@ -802,18 +811,17 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) udev->state == USB_STATE_SUSPENDED) goto done; - /* For devices that don't have a driver, we do a standard suspend. */ - if (udev->dev.driver == NULL) { + /* For devices that don't have a driver, we do a generic suspend. */ + if (udev->dev.driver) + udriver = to_usb_device_driver(udev->dev.driver); + else { udev->do_remote_wakeup = 0; - status = usb_port_suspend(udev); - goto done; + udriver = &usb_generic_driver; } - - udriver = to_usb_device_driver(udev->dev.driver); status = udriver->suspend(udev, msg); -done: - // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + done: + dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) udev->dev.power.power_state.event = msg.event; return status; @@ -825,8 +833,9 @@ static int usb_resume_device(struct usb_device *udev) struct usb_device_driver *udriver; int status = 0; - if (udev->state == USB_STATE_NOTATTACHED || - udev->state != USB_STATE_SUSPENDED) + if (udev->state == USB_STATE_NOTATTACHED) + goto done; + if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume) goto done; /* Can't resume it if it doesn't have a driver. */ @@ -835,11 +844,14 @@ static int usb_resume_device(struct usb_device *udev) goto done; } + if (udev->quirks & USB_QUIRK_RESET_RESUME) + udev->reset_resume = 1; + udriver = to_usb_device_driver(udev->dev.driver); status = udriver->resume(udev); -done: - // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + done: + dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) { udev->autoresume_disabled = 0; udev->dev.power.power_state.event = PM_EVENT_ON; @@ -877,15 +889,13 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) mark_quiesced(intf); } -done: - // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); - if (status == 0) - intf->dev.power.power_state.event = msg.event; + done: + dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); return status; } /* Caller has locked intf's usb_device's pm_mutex */ -static int usb_resume_interface(struct usb_interface *intf) +static int usb_resume_interface(struct usb_interface *intf, int reset_resume) { struct usb_driver *driver; int status = 0; @@ -905,23 +915,37 @@ static int usb_resume_interface(struct usb_interface *intf) } driver = to_usb_driver(intf->dev.driver); - if (driver->resume) { - status = driver->resume(intf); - if (status) - dev_err(&intf->dev, "%s error %d\n", - "resume", status); - else - mark_active(intf); + if (reset_resume) { + if (driver->reset_resume) { + status = driver->reset_resume(intf); + if (status) + dev_err(&intf->dev, "%s error %d\n", + "reset_resume", status); + } else { + // status = -EOPNOTSUPP; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "reset_resume", driver->name); + } } else { - dev_warn(&intf->dev, "no resume for driver %s?\n", - driver->name); - mark_active(intf); + if (driver->resume) { + status = driver->resume(intf); + if (status) + dev_err(&intf->dev, "%s error %d\n", + "resume", status); + } else { + // status = -EOPNOTSUPP; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "resume", driver->name); + } } done: - // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); + dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) - intf->dev.power.power_state.event = PM_EVENT_ON; + mark_active(intf); + + /* FIXME: Unbind the driver and reprobe if the resume failed + * (not possible if auto_pm is set) */ return status; } @@ -958,6 +982,18 @@ static int autosuspend_check(struct usb_device *udev) "for autosuspend\n"); return -EOPNOTSUPP; } + + /* Don't allow autosuspend if the device will need + * a reset-resume and any of its interface drivers + * doesn't include support. + */ + if (udev->quirks & USB_QUIRK_RESET_RESUME) { + struct usb_driver *driver; + + driver = to_usb_driver(intf->dev.driver); + if (!driver->reset_resume) + return -EOPNOTSUPP; + } } } @@ -974,7 +1010,7 @@ static int autosuspend_check(struct usb_device *udev) * or for the past. */ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - suspend_time - jiffies); + round_jiffies_relative(suspend_time - jiffies)); } return -EAGAIN; } @@ -1054,14 +1090,21 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) break; } } - if (status == 0) + if (status == 0) { + + /* Non-root devices don't need to do anything for FREEZE + * or PRETHAW. */ + if (udev->parent && (msg.event == PM_EVENT_FREEZE || + msg.event == PM_EVENT_PRETHAW)) + goto done; status = usb_suspend_device(udev, msg); + } /* If the suspend failed, resume interfaces that did get suspended */ if (status != 0) { while (--i >= 0) { intf = udev->actconfig->interface[i]; - usb_resume_interface(intf); + usb_resume_interface(intf, 0); } /* Try another autosuspend when the interfaces aren't busy */ @@ -1076,7 +1119,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) } done: - // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status; } @@ -1131,7 +1174,8 @@ static int usb_resume_both(struct usb_device *udev) status = usb_autoresume_device(parent); if (status == 0) { status = usb_resume_device(udev); - if (status) { + if (status || udev->state == + USB_STATE_NOTATTACHED) { usb_autosuspend_device(parent); /* It's possible usb_resume_device() @@ -1152,28 +1196,25 @@ static int usb_resume_both(struct usb_device *udev) /* We can't progagate beyond the USB subsystem, * so if a root hub's controller is suspended * then we're stuck. */ - if (udev->dev.parent->power.power_state.event != - PM_EVENT_ON) - status = -EHOSTUNREACH; - else - status = usb_resume_device(udev); + status = usb_resume_device(udev); } } else { - /* Needed only for setting udev->dev.power.power_state.event - * and for possible debugging message. */ + /* Needed for setting udev->dev.power.power_state.event, + * for possible debugging message, and for reset_resume. */ status = usb_resume_device(udev); } if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; - usb_resume_interface(intf); + usb_resume_interface(intf, udev->reset_resume); } } done: - // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + udev->reset_resume = 0; return status; } @@ -1240,8 +1281,8 @@ void usb_autosuspend_device(struct usb_device *udev) int status; status = usb_autopm_do_device(udev, -1); - // dev_dbg(&udev->dev, "%s: cnt %d\n", - // __FUNCTION__, udev->pm_usage_cnt); + dev_vdbg(&udev->dev, "%s: cnt %d\n", + __FUNCTION__, udev->pm_usage_cnt); } /** @@ -1260,8 +1301,8 @@ void usb_autosuspend_device(struct usb_device *udev) void usb_try_autosuspend_device(struct usb_device *udev) { usb_autopm_do_device(udev, 0); - // dev_dbg(&udev->dev, "%s: cnt %d\n", - // __FUNCTION__, udev->pm_usage_cnt); + dev_vdbg(&udev->dev, "%s: cnt %d\n", + __FUNCTION__, udev->pm_usage_cnt); } /** @@ -1288,8 +1329,8 @@ int usb_autoresume_device(struct usb_device *udev) int status; status = usb_autopm_do_device(udev, 1); - // dev_dbg(&udev->dev, "%s: status %d cnt %d\n", - // __FUNCTION__, status, udev->pm_usage_cnt); + dev_vdbg(&udev->dev, "%s: status %d cnt %d\n", + __FUNCTION__, status, udev->pm_usage_cnt); return status; } @@ -1361,8 +1402,8 @@ void usb_autopm_put_interface(struct usb_interface *intf) int status; status = usb_autopm_do_interface(intf, -1); - // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", - // __FUNCTION__, status, intf->pm_usage_cnt); + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __FUNCTION__, status, intf->pm_usage_cnt); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface); @@ -1405,8 +1446,8 @@ int usb_autopm_get_interface(struct usb_interface *intf) int status; status = usb_autopm_do_interface(intf, 1); - // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", - // __FUNCTION__, status, intf->pm_usage_cnt); + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __FUNCTION__, status, intf->pm_usage_cnt); return status; } EXPORT_SYMBOL_GPL(usb_autopm_get_interface); @@ -1427,8 +1468,8 @@ int usb_autopm_set_interface(struct usb_interface *intf) int status; status = usb_autopm_do_interface(intf, 0); - // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", - // __FUNCTION__, status, intf->pm_usage_cnt); + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __FUNCTION__, status, intf->pm_usage_cnt); return status; } EXPORT_SYMBOL_GPL(usb_autopm_set_interface); @@ -1508,8 +1549,15 @@ static int usb_resume(struct device *dev) if (!is_usb_device(dev)) /* Ignore PM for interfaces */ return 0; udev = to_usb_device(dev); - if (udev->autoresume_disabled) - return -EPERM; + + /* If autoresume is disabled then we also want to prevent resume + * during system wakeup. However, a "persistent-device" reset-resume + * after power loss counts as a wakeup event. So allow a + * reset-resume to occur if remote wakeup is enabled. */ + if (udev->autoresume_disabled) { + if (!(udev->reset_resume && udev->do_remote_wakeup)) + return -EPERM; + } return usb_external_resume_device(udev); } diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 01c857a..5d860bc 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -16,15 +16,15 @@ */ #include <linux/module.h> -#include <linux/spinlock.h> #include <linux/errno.h> +#include <linux/rwsem.h> #include <linux/usb.h> #include "usb.h" #define MAX_USB_MINORS 256 static const struct file_operations *usb_minors[MAX_USB_MINORS]; -static DEFINE_SPINLOCK(minor_lock); +static DECLARE_RWSEM(minor_rwsem); static int usb_open(struct inode * inode, struct file * file) { @@ -33,14 +33,11 @@ static int usb_open(struct inode * inode, struct file * file) int err = -ENODEV; const struct file_operations *old_fops, *new_fops = NULL; - spin_lock (&minor_lock); + down_read(&minor_rwsem); c = usb_minors[minor]; - if (!c || !(new_fops = fops_get(c))) { - spin_unlock(&minor_lock); - return err; - } - spin_unlock(&minor_lock); + if (!c || !(new_fops = fops_get(c))) + goto done; old_fops = file->f_op; file->f_op = new_fops; @@ -52,6 +49,8 @@ static int usb_open(struct inode * inode, struct file * file) file->f_op = fops_get(old_fops); } fops_put(old_fops); + done: + up_read(&minor_rwsem); return err; } @@ -166,7 +165,7 @@ int usb_register_dev(struct usb_interface *intf, if (class_driver->fops == NULL) goto exit; - spin_lock (&minor_lock); + down_write(&minor_rwsem); for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { if (usb_minors[minor]) continue; @@ -176,7 +175,7 @@ int usb_register_dev(struct usb_interface *intf, retval = 0; break; } - spin_unlock (&minor_lock); + up_write(&minor_rwsem); if (retval) goto exit; @@ -197,9 +196,9 @@ int usb_register_dev(struct usb_interface *intf, 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); + down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; - spin_unlock (&minor_lock); + up_write(&minor_rwsem); retval = PTR_ERR(intf->usb_dev); } exit: @@ -236,9 +235,9 @@ void usb_deregister_dev(struct usb_interface *intf, dbg ("removing %d minor", intf->minor); - spin_lock (&minor_lock); + down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; - spin_unlock (&minor_lock); + up_write(&minor_rwsem); snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base); device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); @@ -247,5 +246,3 @@ void usb_deregister_dev(struct usb_interface *intf, destroy_usb_class(); } EXPORT_SYMBOL(usb_deregister_dev); - - diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 9bbcb20e..b2fc2b1 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -19,6 +19,7 @@ #include <linux/usb.h> #include "usb.h" +#include "hcd.h" static inline const char *plural(int n) { @@ -193,16 +194,34 @@ static void generic_disconnect(struct usb_device *udev) static int generic_suspend(struct usb_device *udev, pm_message_t msg) { - /* USB devices enter SUSPEND state through their hubs, but can be - * marked for FREEZE as soon as their children are already idled. - * But those semantics are useless, so we equate the two (sigh). + int rc; + + /* Normal USB devices suspend through their upstream port. + * Root hubs don't have upstream ports to suspend, + * so we have to shut down their downstream HC-to-USB + * interfaces manually by doing a bus (or "global") suspend. */ - return usb_port_suspend(udev); + if (!udev->parent) + rc = hcd_bus_suspend(udev); + else + rc = usb_port_suspend(udev); + return rc; } static int generic_resume(struct usb_device *udev) { - return usb_port_resume(udev); + int rc; + + /* Normal USB devices resume/reset through their upstream port. + * Root hubs don't have upstream ports to resume or reset, + * so we have to start up their downstream HC-to-USB + * interfaces manually by doing a bus (or "global") resume. + */ + if (!udev->parent) + rc = hcd_bus_resume(udev); + else + rc = usb_port_resume(udev); + return rc; } #endif /* CONFIG_PM */ diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index edf4300..5cf6d5f 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -207,7 +207,8 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) * We must ignore the FREEZE vs SUSPEND distinction here, because * otherwise the swsusp will save (and restore) garbage state. */ - if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON) + if (!(hcd->state == HC_STATE_SUSPENDED || + hcd->state == HC_STATE_HALT)) return -EBUSY; if (hcd->driver->suspend) { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 8969e42..963520f 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -582,10 +582,12 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) } /* The USB 2.0 spec says 256 ms. This is close enough and won't - * exceed that limit if HZ is 100. */ + * exceed that limit if HZ is 100. The math is more clunky than + * maybe expected, this is to make sure that all timers for USB devices + * fire at the same time to give the CPU a break inbetween */ if (hcd->uses_new_polling ? hcd->poll_rh : (length == 0 && hcd->status_urb != NULL)) - mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250)); + mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); } EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status); @@ -614,8 +616,8 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) urb->hcpriv = hcd; /* indicate it's queued */ if (!hcd->uses_new_polling) - mod_timer (&hcd->rh_timer, jiffies + - msecs_to_jiffies(250)); + mod_timer (&hcd->rh_timer, + (jiffies/(HZ/4) + 1) * (HZ/4)); /* If a status change has already occurred, report it ASAP */ else if (hcd->poll_pending) @@ -901,17 +903,32 @@ EXPORT_SYMBOL (usb_calc_bus_time); /*-------------------------------------------------------------------------*/ -static void urb_unlink (struct urb *urb) +static void urb_unlink(struct usb_hcd *hcd, struct urb *urb) { unsigned long flags; + int at_root_hub = (urb->dev == hcd->self.root_hub); /* clear all state linking urb to this dev (and hcd) */ - spin_lock_irqsave (&hcd_data_lock, flags); list_del_init (&urb->urb_list); spin_unlock_irqrestore (&hcd_data_lock, flags); -} + if (hcd->self.uses_dma && !at_root_hub) { + if (usb_pipecontrol (urb->pipe) + && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + dma_unmap_single (hcd->self.controller, urb->setup_dma, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (urb->transfer_buffer_length != 0 + && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) + dma_unmap_single (hcd->self.controller, + urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE); + } +} /* may be called in any context with a valid urb->dev usecount * caller surrenders "ownership" of urb @@ -948,19 +965,9 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) else switch (hcd->state) { case HC_STATE_RUNNING: case HC_STATE_RESUMING: -doit: list_add_tail (&urb->urb_list, &ep->urb_list); status = 0; break; - case HC_STATE_SUSPENDED: - /* HC upstream links (register access, wakeup signaling) can work - * even when the downstream links (and DMA etc) are quiesced; let - * usbcore talk to the root hub. - */ - if (hcd->self.controller->power.power_state.event == PM_EVENT_ON - && urb->dev->parent == NULL) - goto doit; - /* FALL THROUGH */ default: status = -ESHUTDOWN; break; @@ -1014,7 +1021,7 @@ doit: status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags); done: if (unlikely (status)) { - urb_unlink (urb); + urb_unlink(hcd, urb); atomic_dec (&urb->use_count); if (urb->reject) wake_up (&usb_kill_urb_queue); @@ -1255,42 +1262,59 @@ rescan: #ifdef CONFIG_PM -int hcd_bus_suspend (struct usb_bus *bus) +int hcd_bus_suspend(struct usb_device *rhdev) { - struct usb_hcd *hcd; - int status; + struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); + int status; + int old_state = hcd->state; - hcd = container_of (bus, struct usb_hcd, self); - if (!hcd->driver->bus_suspend) - return -ENOENT; - hcd->state = HC_STATE_QUIESCING; - status = hcd->driver->bus_suspend (hcd); - if (status == 0) + dev_dbg(&rhdev->dev, "bus %s%s\n", + rhdev->auto_pm ? "auto-" : "", "suspend"); + if (!hcd->driver->bus_suspend) { + status = -ENOENT; + } else { + hcd->state = HC_STATE_QUIESCING; + status = hcd->driver->bus_suspend(hcd); + } + if (status == 0) { + usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; - else - dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n", + } else { + hcd->state = old_state; + dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "suspend", status); + } return status; } -int hcd_bus_resume (struct usb_bus *bus) +int hcd_bus_resume(struct usb_device *rhdev) { - struct usb_hcd *hcd; - int status; + struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); + int status; + int old_state = hcd->state; - hcd = container_of (bus, struct usb_hcd, self); + dev_dbg(&rhdev->dev, "usb %s%s\n", + rhdev->auto_pm ? "auto-" : "", "resume"); if (!hcd->driver->bus_resume) return -ENOENT; if (hcd->state == HC_STATE_RUNNING) return 0; + hcd->state = HC_STATE_RESUMING; - status = hcd->driver->bus_resume (hcd); - if (status == 0) + status = hcd->driver->bus_resume(hcd); + if (status == 0) { + /* TRSMRCY = 10 msec */ + msleep(10); + usb_set_device_state(rhdev, rhdev->actconfig + ? USB_STATE_CONFIGURED + : USB_STATE_ADDRESS); hcd->state = HC_STATE_RUNNING; - else { - dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n", + } else { + hcd->state = old_state; + dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "resume", status); - usb_hc_died(hcd); + if (status != -ESHUTDOWN) + usb_hc_died(hcd); } return status; } @@ -1384,30 +1408,10 @@ EXPORT_SYMBOL (usb_bus_start_enum); */ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) { - int at_root_hub; - - at_root_hub = (urb->dev == hcd->self.root_hub); - urb_unlink (urb); - - /* lower level hcd code should use *_dma exclusively if the - * host controller does DMA */ - if (hcd->self.uses_dma && !at_root_hub) { - if (usb_pipecontrol (urb->pipe) - && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) - dma_unmap_single (hcd->self.controller, urb->setup_dma, - sizeof (struct usb_ctrlrequest), - DMA_TO_DEVICE); - if (urb->transfer_buffer_length != 0 - && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) - dma_unmap_single (hcd->self.controller, - urb->transfer_dma, - urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? DMA_FROM_DEVICE - : DMA_TO_DEVICE); - } - + urb_unlink(hcd, urb); usbmon_urb_complete (&hcd->self, urb); + usb_unanchor_urb(urb); + /* pass ownership to the completion handler */ urb->complete (urb); atomic_dec (&urb->use_count); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index ef50fa4..b5ebb73 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -364,23 +364,13 @@ extern int usb_find_interface_driver (struct usb_device *dev, #ifdef CONFIG_PM extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); extern void usb_root_hub_lost_power (struct usb_device *rhdev); -extern int hcd_bus_suspend (struct usb_bus *bus); -extern int hcd_bus_resume (struct usb_bus *bus); +extern int hcd_bus_suspend(struct usb_device *rhdev); +extern int hcd_bus_resume(struct usb_device *rhdev); #else static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) { return; } - -static inline int hcd_bus_suspend(struct usb_bus *bus) -{ - return 0; -} - -static inline int hcd_bus_resume (struct usb_bus *bus) -{ - return 0; -} #endif /* CONFIG_PM */ /* diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a9cf8b3..50e79010 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -31,9 +31,16 @@ #include "hcd.h" #include "hub.h" +#ifdef CONFIG_USB_PERSIST +#define USB_PERSIST 1 +#else +#define USB_PERSIST 0 +#endif + struct usb_hub { struct device *intfdev; /* the "interface" device */ struct usb_device *hdev; + struct kref kref; struct urb *urb; /* for interrupt polling pipe */ /* buffer for urb ... with extra space in case of babble */ @@ -66,6 +73,7 @@ struct usb_hub { unsigned limited_power:1; unsigned quiescing:1; unsigned activating:1; + unsigned disconnected:1; unsigned has_indicators:1; u8 indicator[USB_MAXCHILDREN]; @@ -321,7 +329,7 @@ static void kick_khubd(struct usb_hub *hub) to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; spin_lock_irqsave(&hub_event_lock, flags); - if (list_empty(&hub->event_list)) { + if (!hub->disconnected & list_empty(&hub->event_list)) { list_add_tail(&hub->event_list, &hub_event_list); wake_up(&khubd_wait); } @@ -330,6 +338,7 @@ static void kick_khubd(struct usb_hub *hub) void usb_kick_khubd(struct usb_device *hdev) { + /* FIXME: What if hdev isn't bound to the hub driver? */ kick_khubd(hdev_to_hub(hdev)); } @@ -400,9 +409,10 @@ static void hub_tt_kevent (struct work_struct *work) struct usb_hub *hub = container_of(work, struct usb_hub, tt.kevent); unsigned long flags; + int limit = 100; spin_lock_irqsave (&hub->tt.lock, flags); - while (!list_empty (&hub->tt.clear_list)) { + while (--limit && !list_empty (&hub->tt.clear_list)) { struct list_head *temp; struct usb_tt_clear *clear; struct usb_device *hdev = hub->hdev; @@ -550,48 +560,68 @@ static int hub_hub_status(struct usb_hub *hub, static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) { struct usb_device *hdev = hub->hdev; - int ret; + int ret = 0; - if (hdev->children[port1-1] && set_state) { + if (hdev->children[port1-1] && set_state) usb_set_device_state(hdev->children[port1-1], USB_STATE_NOTATTACHED); - } - ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); + if (!hub->error) + ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); if (ret) dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", - port1, ret); - + port1, ret); return ret; } +/* + * Disable a port and mark a logical connnect-change event, so that some + * time later khubd will disconnect() any existing usb_device on the port + * and will re-enumerate if there actually is a device attached. + */ +static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) +{ + dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1); + hub_port_disable(hub, port1, 1); + + /* FIXME let caller ask to power down the port: + * - some devices won't enumerate without a VBUS power cycle + * - SRP saves power that way + * - ... new call, TBD ... + * That's easy if this hub can switch power per-port, and + * khubd reactivates the port later (timer, SRP, etc). + * Powerdown must be optional, because of reset/DFU. + */ + + set_bit(port1, hub->change_bits); + kick_khubd(hub); +} /* caller has locked the hub device */ -static void hub_pre_reset(struct usb_interface *intf) +static int hub_pre_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = hub->hdev; - int port1; + int i; - for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - if (hdev->children[port1 - 1]) { - usb_disconnect(&hdev->children[port1 - 1]); - if (hub->error == 0) - hub_port_disable(hub, port1, 0); - } + /* Disconnect all the children */ + for (i = 0; i < hdev->maxchild; ++i) { + if (hdev->children[i]) + usb_disconnect(&hdev->children[i]); } hub_quiesce(hub); + return 0; } /* caller has locked the hub device */ -static void hub_post_reset(struct usb_interface *intf) +static int hub_post_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); - hub_activate(hub); hub_power_on(hub); + hub_activate(hub); + return 0; } - static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { @@ -845,43 +875,42 @@ fail: return ret; } +static void hub_release(struct kref *kref) +{ + struct usb_hub *hub = container_of(kref, struct usb_hub, kref); + + usb_put_intf(to_usb_interface(hub->intfdev)); + kfree(hub); +} + static unsigned highspeed_hubs; static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); - struct usb_device *hdev; + + /* Take the hub off the event list and don't let it be added again */ + spin_lock_irq(&hub_event_lock); + list_del_init(&hub->event_list); + hub->disconnected = 1; + spin_unlock_irq(&hub_event_lock); /* Disconnect all children and quiesce the hub */ hub->error = 0; hub_pre_reset(intf); usb_set_intfdata (intf, NULL); - hdev = hub->hdev; - if (hdev->speed == USB_SPEED_HIGH) + if (hub->hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; usb_free_urb(hub->urb); - hub->urb = NULL; - - spin_lock_irq(&hub_event_lock); - list_del_init(&hub->event_list); - spin_unlock_irq(&hub_event_lock); - kfree(hub->descriptor); - hub->descriptor = NULL; - kfree(hub->status); - hub->status = NULL; - - if (hub->buffer) { - usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer, - hub->buffer_dma); - hub->buffer = NULL; - } + usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer, + hub->buffer_dma); - kfree(hub); + kref_put(&hub->kref, hub_release); } static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -929,10 +958,12 @@ descriptor_error: return -ENOMEM; } + kref_init(&hub->kref); INIT_LIST_HEAD(&hub->event_list); hub->intfdev = &intf->dev; hub->hdev = hdev; INIT_DELAYED_WORK(&hub->leds, led_work); + usb_get_intf(intf); usb_set_intfdata (intf, hub); intf->needs_remote_wakeup = 1; @@ -982,49 +1013,6 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) } -/* grab device/port lock, returning index of that port (zero based). - * protects the upstream link used by this device from concurrent - * tree operations like suspend, resume, reset, and disconnect, which - * apply to everything downstream of a given port. - */ -static int locktree(struct usb_device *udev) -{ - int t; - struct usb_device *hdev; - - if (!udev) - return -ENODEV; - - /* root hub is always the first lock in the series */ - hdev = udev->parent; - if (!hdev) { - usb_lock_device(udev); - return 0; - } - - /* on the path from root to us, lock everything from - * top down, dropping parent locks when not needed - */ - t = locktree(hdev); - if (t < 0) - return t; - - /* everything is fail-fast once disconnect - * processing starts - */ - if (udev->state == USB_STATE_NOTATTACHED) { - usb_unlock_device(hdev); - return -ENODEV; - } - - /* when everyone grabs locks top->bottom, - * non-overlapping work may be concurrent - */ - usb_lock_device(udev); - usb_unlock_device(hdev); - return udev->portnum; -} - static void recursively_mark_NOTATTACHED(struct usb_device *udev) { int i; @@ -1089,41 +1077,6 @@ void usb_set_device_state(struct usb_device *udev, spin_unlock_irqrestore(&device_state_lock, flags); } - -#ifdef CONFIG_PM - -/** - * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power - * @rhdev: struct usb_device for the root hub - * - * The USB host controller driver calls this function when its root hub - * is resumed and Vbus power has been interrupted or the controller - * has been reset. The routine marks all the children of the root hub - * as NOTATTACHED and marks logical connect-change events on their ports. - */ -void usb_root_hub_lost_power(struct usb_device *rhdev) -{ - struct usb_hub *hub; - int port1; - unsigned long flags; - - dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); - - spin_lock_irqsave(&device_state_lock, flags); - hub = hdev_to_hub(rhdev); - for (port1 = 1; port1 <= rhdev->maxchild; ++port1) { - if (rhdev->children[port1 - 1]) { - recursively_mark_NOTATTACHED( - rhdev->children[port1 - 1]); - set_bit(port1, hub->change_bits); - } - } - spin_unlock_irqrestore(&device_state_lock, flags); -} -EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); - -#endif /* CONFIG_PM */ - static void choose_address(struct usb_device *udev) { int devnum; @@ -1264,7 +1217,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string) #ifdef CONFIG_USB_OTG #include "otg_whitelist.h" -static int __usb_port_suspend(struct usb_device *, int port1); #endif /** @@ -1370,11 +1322,11 @@ int usb_new_device(struct usb_device *udev) * (Includes HNP test device.) */ if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { - err = __usb_port_suspend(udev, udev->bus->otg_port); + err = usb_port_suspend(udev); if (err < 0) dev_dbg(&udev->dev, "HNP fail, %d\n", err); } - err = -ENODEV; + err = -ENOTSUPP; goto fail; } #endif @@ -1471,9 +1423,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; - /* bomb out completely if something weird happened */ + /* bomb out completely if the connection bounced */ if ((portchange & USB_PORT_STAT_C_CONNECTION)) - return -EINVAL; + return -ENOTCONN; /* if we`ve finished resetting, then break out of the loop */ if (!(portstatus & USB_PORT_STAT_RESET) && @@ -1552,34 +1504,24 @@ static int hub_port_reset(struct usb_hub *hub, int port1, return status; } -/* - * Disable a port and mark a logical connnect-change event, so that some - * time later khubd will disconnect() any existing usb_device on the port - * and will re-enumerate if there actually is a device attached. - */ -static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) -{ - dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1); - hub_port_disable(hub, port1, 1); - - /* FIXME let caller ask to power down the port: - * - some devices won't enumerate without a VBUS power cycle - * - SRP saves power that way - * - ... new call, TBD ... - * That's easy if this hub can switch power per-port, and - * khubd reactivates the port later (timer, SRP, etc). - * Powerdown must be optional, because of reset/DFU. - */ - - set_bit(port1, hub->change_bits); - kick_khubd(hub); -} - #ifdef CONFIG_PM #ifdef CONFIG_USB_SUSPEND /* + * usb_port_suspend - suspend a usb device's upstream port + * @udev: device that's no longer in active use, not a root hub + * Context: must be able to sleep; device not locked; pm locks held + * + * Suspends a USB device that isn't in active use, conserving power. + * Devices may wake out of a suspend, if anything important happens, + * using the remote wakeup mechanism. They may also be taken out of + * suspend by the host, using usb_port_resume(). It's also routine + * to disconnect devices while they are suspended. + * + * This only affects the USB hardware for a device; its interfaces + * (and, for hubs, child devices) must already have been suspended. + * * Selective port suspend reduces power; most suspended devices draw * less than 500 uA. It's also used in OTG, along with remote wakeup. * All devices below the suspended port are also suspended. @@ -1588,11 +1530,35 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) * also support "remote wakeup", where the device can activate the USB * tree above them to deliver data, such as a keypress or packet. In * some cases, this wakes the USB host. + * + * Suspending OTG devices may trigger HNP, if that's been enabled + * between a pair of dual-role devices. That will change roles, such + * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. + * + * Devices on USB hub ports have only one "suspend" state, corresponding + * to ACPI D2, "may cause the device to lose some context". + * State transitions include: + * + * - suspend, resume ... when the VBUS power link stays live + * - suspend, disconnect ... VBUS lost + * + * Once VBUS drop breaks the circuit, the port it's using has to go through + * normal re-enumeration procedures, starting with enabling VBUS power. + * Other than re-initializing the hub (plug/unplug, except for root hubs), + * Linux (2.6) currently has NO mechanisms to initiate that: no khubd + * timer, no SRP, no requests through sysfs. + * + * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when + * the root hub for their bus goes into global suspend ... so we don't + * (falsely) update the device power state to say it suspended. + * + * Returns 0 on success, else negative errno. */ -static int hub_port_suspend(struct usb_hub *hub, int port1, - struct usb_device *udev) +int usb_port_suspend(struct usb_device *udev) { - int status; + struct usb_hub *hub = hdev_to_hub(udev->parent); + int port1 = udev->portnum; + int status; // dev_dbg(hub->intfdev, "suspend port %d\n", port1); @@ -1609,17 +1575,15 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, NULL, 0, USB_CTRL_SET_TIMEOUT); if (status) - dev_dbg(&udev->dev, - "won't remote wakeup, status %d\n", - status); + dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", + status); } /* see 7.1.7.6 */ status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { - dev_dbg(hub->intfdev, - "can't suspend port %d, status %d\n", - port1, status); + dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", + port1, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, @@ -1637,85 +1601,24 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, } /* - * Devices on USB hub ports have only one "suspend" state, corresponding - * to ACPI D2, "may cause the device to lose some context". - * State transitions include: - * - * - suspend, resume ... when the VBUS power link stays live - * - suspend, disconnect ... VBUS lost - * - * Once VBUS drop breaks the circuit, the port it's using has to go through - * normal re-enumeration procedures, starting with enabling VBUS power. - * Other than re-initializing the hub (plug/unplug, except for root hubs), - * Linux (2.6) currently has NO mechanisms to initiate that: no khubd - * timer, no SRP, no requests through sysfs. - * - * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when - * the root hub for their bus goes into global suspend ... so we don't - * (falsely) update the device power state to say it suspended. - */ -static int __usb_port_suspend (struct usb_device *udev, int port1) -{ - int status = 0; - - /* caller owns the udev device lock */ - if (port1 < 0) - return port1; - - /* we change the device's upstream USB link, - * but root hubs have no upstream USB link. - */ - if (udev->parent) - status = hub_port_suspend(hdev_to_hub(udev->parent), port1, - udev); - else { - dev_dbg(&udev->dev, "usb %ssuspend\n", - udev->auto_pm ? "auto-" : ""); - usb_set_device_state(udev, USB_STATE_SUSPENDED); - } - return status; -} - -/* - * usb_port_suspend - suspend a usb device's upstream port - * @udev: device that's no longer in active use - * Context: must be able to sleep; device not locked; pm locks held - * - * Suspends a USB device that isn't in active use, conserving power. - * Devices may wake out of a suspend, if anything important happens, - * using the remote wakeup mechanism. They may also be taken out of - * suspend by the host, using usb_port_resume(). It's also routine - * to disconnect devices while they are suspended. - * - * This only affects the USB hardware for a device; its interfaces - * (and, for hubs, child devices) must already have been suspended. - * - * Suspending OTG devices may trigger HNP, if that's been enabled - * between a pair of dual-role devices. That will change roles, such - * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. - * - * Returns 0 on success, else negative errno. - */ -int usb_port_suspend(struct usb_device *udev) -{ - return __usb_port_suspend(udev, udev->portnum); -} - -/* * If the USB "suspend" state is in use (rather than "global suspend"), * many devices will be individually taken out of suspend state using - * special" resume" signaling. These routines kick in shortly after + * special "resume" signaling. This routine kicks in shortly after * hardware resume signaling is finished, either because of selective * resume (by host) or remote wakeup (by device) ... now see what changed * in the tree that's rooted at this device. + * + * If @udev->reset_resume is set then the device is reset before the + * status check is done. */ static int finish_port_resume(struct usb_device *udev) { - int status; + int status = 0; u16 devstatus; /* caller owns the udev device lock */ - dev_dbg(&udev->dev, "finish resume\n"); + dev_dbg(&udev->dev, "finish %sresume\n", + udev->reset_resume ? "reset-" : ""); /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the @@ -1726,22 +1629,30 @@ static int finish_port_resume(struct usb_device *udev) ? USB_STATE_CONFIGURED : USB_STATE_ADDRESS); + /* 10.5.4.5 says not to reset a suspended port if the attached + * device is enabled for remote wakeup. Hence the reset + * operation is carried out here, after the port has been + * resumed. + */ + if (udev->reset_resume) + status = usb_reset_device(udev); + /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, * and device drivers will know about any resume quirks. */ - status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); - if (status >= 0) - status = (status == 2 ? 0 : -ENODEV); + if (status == 0) { + status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); + if (status >= 0) + status = (status == 2 ? 0 : -ENODEV); + } - if (status) - dev_dbg(&udev->dev, - "gone after usb resume? status %d\n", - status); - else if (udev->actconfig) { + if (status) { + dev_dbg(&udev->dev, "gone after usb resume? status %d\n", + status); + } else if (udev->actconfig) { le16_to_cpus(&devstatus); - if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) - && udev->parent) { + if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, @@ -1754,19 +1665,52 @@ static int finish_port_resume(struct usb_device *udev) "wakeup, status %d\n", status); } status = 0; - - } else if (udev->devnum <= 0) { - dev_dbg(&udev->dev, "bogus resume!\n"); - status = -EINVAL; } return status; } -static int -hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) +/* + * usb_port_resume - re-activate a suspended usb device's upstream port + * @udev: device to re-activate, not a root hub + * Context: must be able to sleep; device not locked; pm locks held + * + * This will re-activate the suspended device, increasing power usage + * while letting drivers communicate again with its endpoints. + * USB resume explicitly guarantees that the power session between + * the host and the device is the same as it was when the device + * suspended. + * + * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this + * routine won't check that the port is still enabled. Furthermore, + * if @udev->reset_resume is set then finish_port_resume() above will + * reset @udev. The end result is that a broken power session can be + * recovered and @udev will appear to persist across a loss of VBUS power. + * + * For example, if a host controller doesn't maintain VBUS suspend current + * during a system sleep or is reset when the system wakes up, all the USB + * power sessions below it will be broken. This is especially troublesome + * for mass-storage devices containing mounted filesystems, since the + * device will appear to have disconnected and all the memory mappings + * to it will be lost. Using the USB_PERSIST facility, the device can be + * made to appear as if it had not disconnected. + * + * This facility is inherently dangerous. Although usb_reset_device() + * makes every effort to insure that the same device is present after the + * reset as before, it cannot provide a 100% guarantee. Furthermore it's + * quite possible for a device to remain unaltered but its media to be + * changed. If the user replaces a flash memory card while the system is + * asleep, he will have only himself to blame when the filesystem on the + * new card is corrupted and the system crashes. + * + * Returns 0 on success, else negative errno. + */ +int usb_port_resume(struct usb_device *udev) { - int status; - u16 portchange, portstatus; + struct usb_hub *hub = hdev_to_hub(udev->parent); + int port1 = udev->portnum; + int status; + u16 portchange, portstatus; + unsigned mask_flags, want_flags; /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); @@ -1781,30 +1725,31 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) status = clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { - dev_dbg(hub->intfdev, - "can't resume port %d, status %d\n", - port1, status); + dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", + port1, status); } else { /* drive resume for at least 20 msec */ - if (udev) - dev_dbg(&udev->dev, "usb %sresume\n", - udev->auto_pm ? "auto-" : ""); + dev_dbg(&udev->dev, "usb %sresume\n", + udev->auto_pm ? "auto-" : ""); msleep(25); -#define LIVE_FLAGS ( USB_PORT_STAT_POWER \ - | USB_PORT_STAT_ENABLE \ - | USB_PORT_STAT_CONNECTION) - /* Virtual root hubs can trigger on GET_PORT_STATUS to * stop resume signaling. Then finish the resume * sequence. */ status = hub_port_status(hub, port1, &portstatus, &portchange); -SuspendCleared: - if (status < 0 - || (portstatus & LIVE_FLAGS) != LIVE_FLAGS - || (portstatus & USB_PORT_STAT_SUSPEND) != 0 - ) { + + SuspendCleared: + if (USB_PERSIST && udev->reset_resume) + want_flags = USB_PORT_STAT_POWER + | USB_PORT_STAT_CONNECTION; + else + want_flags = USB_PORT_STAT_POWER + | USB_PORT_STAT_CONNECTION + | USB_PORT_STAT_ENABLE; + mask_flags = want_flags | USB_PORT_STAT_SUSPEND; + + if (status < 0 || (portstatus & mask_flags) != want_flags) { dev_dbg(hub->intfdev, "port %d status %04x.%04x after resume, %d\n", port1, portchange, portstatus, status); @@ -1816,51 +1761,19 @@ SuspendCleared: USB_PORT_FEAT_C_SUSPEND); /* TRSMRCY = 10 msec */ msleep(10); - if (udev) - status = finish_port_resume(udev); } } - if (status < 0) - hub_port_logical_disconnect(hub, port1); clear_bit(port1, hub->busy_bits); if (!hub->hdev->parent && !hub->busy_bits[0]) usb_enable_root_hub_irq(hub->hdev->bus); - return status; -} - -/* - * usb_port_resume - re-activate a suspended usb device's upstream port - * @udev: device to re-activate - * Context: must be able to sleep; device not locked; pm locks held - * - * This will re-activate the suspended device, increasing power usage - * while letting drivers communicate again with its endpoints. - * USB resume explicitly guarantees that the power session between - * the host and the device is the same as it was when the device - * suspended. - * - * Returns 0 on success, else negative errno. - */ -int usb_port_resume(struct usb_device *udev) -{ - int status; - - /* we change the device's upstream USB link, - * but root hubs have no upstream USB link. - */ - if (udev->parent) { - // NOTE this fails if parent is also suspended... - status = hub_port_resume(hdev_to_hub(udev->parent), - udev->portnum, udev); - } else { - dev_dbg(&udev->dev, "usb %sresume\n", - udev->auto_pm ? "auto-" : ""); + if (status == 0) status = finish_port_resume(udev); - } - if (status < 0) + if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); + hub_port_logical_disconnect(hub, port1); + } return status; } @@ -1887,21 +1800,16 @@ int usb_port_suspend(struct usb_device *udev) return 0; } -static inline int -finish_port_resume(struct usb_device *udev) -{ - return 0; -} - -static inline int -hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) -{ - return 0; -} - int usb_port_resume(struct usb_device *udev) { - return 0; + int status = 0; + + /* However we may need to do a reset-resume */ + if (udev->reset_resume) { + dev_dbg(&udev->dev, "reset-resume\n"); + status = usb_reset_device(udev); + } + return status; } static inline int remote_wakeup(struct usb_device *udev) @@ -1916,7 +1824,6 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; - int status = 0; /* fail if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { @@ -1942,49 +1849,75 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) /* stop khubd and related activity */ hub_quiesce(hub); - - /* "global suspend" of the downstream HC-to-USB interface */ - if (!hdev->parent) { - status = hcd_bus_suspend(hdev->bus); - if (status != 0) { - dev_dbg(&hdev->dev, "'global' suspend %d\n", status); - hub_activate(hub); - } - } - return status; + return 0; } static int hub_resume(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); - struct usb_device *hdev = hub->hdev; - int status; dev_dbg(&intf->dev, "%s\n", __FUNCTION__); - /* "global resume" of the downstream HC-to-USB interface */ - if (!hdev->parent) { - struct usb_bus *bus = hdev->bus; - if (bus) { - status = hcd_bus_resume (bus); - if (status) { - dev_dbg(&intf->dev, "'global' resume %d\n", - status); - return status; + /* tell khubd to look for changes on this hub */ + hub_activate(hub); + return 0; +} + +static int hub_reset_resume(struct usb_interface *intf) +{ + struct usb_hub *hub = usb_get_intfdata(intf); + struct usb_device *hdev = hub->hdev; + int port1; + + hub_power_on(hub); + + for (port1 = 1; port1 <= hdev->maxchild; ++port1) { + struct usb_device *child = hdev->children[port1-1]; + + if (child) { + + /* For "USB_PERSIST"-enabled children we must + * mark the child device for reset-resume and + * turn off the connect-change status to prevent + * khubd from disconnecting it later. + */ + if (USB_PERSIST && child->persist_enabled) { + child->reset_resume = 1; + clear_port_feature(hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + + /* Otherwise we must disconnect the child, + * but as we may not lock the child device here + * we have to do a "logical" disconnect. + */ + } else { + hub_port_logical_disconnect(hub, port1); } - } else - return -EOPNOTSUPP; - if (status == 0) { - /* TRSMRCY = 10 msec */ - msleep(10); } } - /* tell khubd to look for changes on this hub */ hub_activate(hub); return 0; } +/** + * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power + * @rhdev: struct usb_device for the root hub + * + * The USB host controller driver calls this function when its root hub + * is resumed and Vbus power has been interrupted or the controller + * has been reset. The routine marks @rhdev as having lost power. When + * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST + * is enabled then it will carry out power-session recovery, otherwise + * it will disconnect all the child devices. + */ +void usb_root_hub_lost_power(struct usb_device *rhdev) +{ + dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); + rhdev->reset_resume = 1; +} +EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); + #else /* CONFIG_PM */ static inline int remote_wakeup(struct usb_device *udev) @@ -1992,8 +1925,9 @@ static inline int remote_wakeup(struct usb_device *udev) return 0; } -#define hub_suspend NULL -#define hub_resume NULL +#define hub_suspend NULL +#define hub_resume NULL +#define hub_reset_resume NULL #endif @@ -2456,19 +2390,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, return; } -#ifdef CONFIG_USB_SUSPEND - /* If something is connected, but the port is suspended, wake it up. */ - if (portstatus & USB_PORT_STAT_SUSPEND) { - status = hub_port_resume(hub, port1, NULL); - if (status < 0) { - dev_dbg(hub_dev, - "can't clear suspend on port %d; %d\n", - port1, status); - goto done; - } - } -#endif - for (i = 0; i < SET_CONFIG_TRIES; i++) { struct usb_device *udev; @@ -2579,7 +2500,7 @@ loop: ep0_reinit(udev); release_address(udev); usb_put_dev(udev); - if (status == -ENOTCONN) + if ((status == -ENOTCONN) || (status == -ENOTSUPP)) break; } @@ -2620,10 +2541,12 @@ static void hub_events(void) list_del_init(tmp); hub = list_entry(tmp, struct usb_hub, event_list); - hdev = hub->hdev; - intf = to_usb_interface(hub->intfdev); - hub_dev = &intf->dev; + kref_get(&hub->kref); + spin_unlock_irq(&hub_event_lock); + hdev = hub->hdev; + hub_dev = hub->intfdev; + intf = to_usb_interface(hub_dev); dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", hdev->state, hub->descriptor ? hub->descriptor->bNbrPorts @@ -2632,16 +2555,10 @@ static void hub_events(void) (u16) hub->change_bits[0], (u16) hub->event_bits[0]); - usb_get_intf(intf); - spin_unlock_irq(&hub_event_lock); - /* Lock the device, then check to see if we were * disconnected while waiting for the lock to succeed. */ - if (locktree(hdev) < 0) { - usb_put_intf(intf); - continue; - } - if (hub != usb_get_intfdata(intf)) + usb_lock_device(hdev); + if (unlikely(hub->disconnected)) goto loop; /* If the hub has died, clean up after it */ @@ -2804,7 +2721,7 @@ loop_autopm: usb_autopm_enable(intf); loop: usb_unlock_device(hdev); - usb_put_intf(intf); + kref_put(&hub->kref, hub_release); } /* end while (1) */ } @@ -2839,6 +2756,7 @@ static struct usb_driver hub_driver = { .disconnect = hub_disconnect, .suspend = hub_suspend, .resume = hub_resume, + .reset_resume = hub_reset_resume, .pre_reset = hub_pre_reset, .post_reset = hub_post_reset, .ioctl = hub_ioctl, @@ -2941,6 +2859,11 @@ static int config_descriptors_changed(struct usb_device *udev) * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). + * + * Locking exception: This routine may also be called from within an + * autoresume handler. Such usage won't conflict with other tasks + * holding the device lock because these tasks should always call + * usb_autopm_resume_device(), thereby preventing any unwanted autoresume. */ int usb_reset_device(struct usb_device *udev) { @@ -2971,7 +2894,7 @@ int usb_reset_device(struct usb_device *udev) * Other endpoints will be handled by re-enumeration. */ ep0_reinit(udev); ret = hub_port_init(parent_hub, udev, port1, i); - if (ret >= 0) + if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV) break; } clear_bit(port1, parent_hub->busy_bits); @@ -3087,6 +3010,7 @@ int usb_reset_composite_device(struct usb_device *udev, drv = to_usb_driver(cintf->dev.driver); if (drv->pre_reset) (drv->pre_reset)(cintf); + /* FIXME: Unbind if pre_reset returns an error or isn't defined */ } } } @@ -3105,6 +3029,7 @@ int usb_reset_composite_device(struct usb_device *udev, drv = to_usb_driver(cintf->dev.driver); if (drv->post_reset) (drv->post_reset)(cintf); + /* FIXME: Unbind if post_reset returns an error or isn't defined */ } if (cintf != iface) up(&cintf->dev.sem); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f9fed34..530e854 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -404,8 +404,6 @@ int usb_sg_init ( io->urbs [i]->complete = sg_complete; io->urbs [i]->context = io; - io->urbs [i]->status = -EINPROGRESS; - io->urbs [i]->actual_length = 0; /* * Some systems need to revert to PIO when DMA is temporarily @@ -499,7 +497,8 @@ void usb_sg_wait (struct usb_sg_request *io) /* queue the urbs. */ spin_lock_irq (&io->lock); - for (i = 0; i < entries && !io->status; i++) { + i = 0; + while (i < entries && !io->status) { int retval; io->urbs [i]->dev = io->dev; @@ -516,7 +515,6 @@ void usb_sg_wait (struct usb_sg_request *io) case -ENOMEM: io->urbs[i]->dev = NULL; retval = 0; - i--; yield (); break; @@ -527,6 +525,7 @@ void usb_sg_wait (struct usb_sg_request *io) * URBs are queued at once; N milliseconds? */ case 0: + ++i; cpu_relax (); break; @@ -1385,6 +1384,36 @@ struct device_type usb_if_device_type = { .uevent = usb_if_uevent, }; +static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, + struct usb_host_config *config, + u8 inum) +{ + struct usb_interface_assoc_descriptor *retval = NULL; + struct usb_interface_assoc_descriptor *intf_assoc; + int first_intf; + int last_intf; + int i; + + for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) { + intf_assoc = config->intf_assoc[i]; + if (intf_assoc->bInterfaceCount == 0) + continue; + + first_intf = intf_assoc->bFirstInterface; + last_intf = first_intf + (intf_assoc->bInterfaceCount - 1); + if (inum >= first_intf && inum <= last_intf) { + if (!retval) + retval = intf_assoc; + else + dev_err(&dev->dev, "Interface #%d referenced" + " by multiple IADs\n", inum); + } + } + + return retval; +} + + /* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated @@ -1531,6 +1560,7 @@ free_interfaces: intfc = cp->intf_cache[i]; intf->altsetting = intfc->altsetting; intf->num_altsetting = intfc->num_altsetting; + intf->intf_assoc = find_iad(dev, cp, i); kref_get(&intfc->ref); alt = usb_altnum_to_altsetting(intf, 0); diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 739f520..aa21b38 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -30,10 +30,28 @@ static const struct usb_device_id usb_quirk_list[] = { /* HP 5300/5370C scanner */ { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, + /* Benq S2W 3300U */ + { USB_DEVICE(0x04a5, 0x20b0), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Seiko Epson Corp. Perfection 1200 */ + { USB_DEVICE(0x04b8, 0x0104), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, /* Seiko Epson Corp - Perfection 1670 */ { USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Samsung ML-2510 Series printer */ + { USB_DEVICE(0x04e8, 0x327e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, /* Elsa MicroLink 56k (V.250) */ { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Ultima Electronics Corp.*/ + { USB_DEVICE(0x05d8, 0x4005), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Umax [hex] Astra 3400U */ + { USB_DEVICE(0x1606, 0x0060), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + + /* Philips PSC805 audio device */ + { USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* RIM Blackberry */ + { USB_DEVICE(0x0fca, 0x0001), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + { USB_DEVICE(0x0fca, 0x0004), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + { USB_DEVICE(0x0fca, 0x0006), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, { } /* terminating entry must be last */ }; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index be37c86..d47ae89 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -169,6 +169,73 @@ show_quirks(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); + +#if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND) +static const char power_group[] = "power"; +#endif + +#ifdef CONFIG_USB_PERSIST + +static ssize_t +show_persist(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + + return sprintf(buf, "%d\n", udev->persist_enabled); +} + +static ssize_t +set_persist(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int value; + + /* Hubs are always enabled for USB_PERSIST */ + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EPERM; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + usb_pm_lock(udev); + udev->persist_enabled = !!value; + usb_pm_unlock(udev); + return count; +} + +static DEVICE_ATTR(persist, S_IRUGO | S_IWUSR, show_persist, set_persist); + +static int add_persist_attributes(struct device *dev) +{ + int rc = 0; + + if (is_usb_device(dev)) { + struct usb_device *udev = to_usb_device(dev); + + /* Hubs are automatically enabled for USB_PERSIST */ + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) + udev->persist_enabled = 1; + rc = sysfs_add_file_to_group(&dev->kobj, + &dev_attr_persist.attr, + power_group); + } + return rc; +} + +static void remove_persist_attributes(struct device *dev) +{ + sysfs_remove_file_from_group(&dev->kobj, + &dev_attr_persist.attr, + power_group); +} + +#else + +#define add_persist_attributes(dev) 0 +#define remove_persist_attributes(dev) do {} while (0) + +#endif /* CONFIG_USB_PERSIST */ + #ifdef CONFIG_USB_SUSPEND static ssize_t @@ -276,8 +343,6 @@ set_level(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); -static char power_group[] = "power"; - static int add_power_attributes(struct device *dev) { int rc = 0; @@ -311,6 +376,7 @@ static void remove_power_attributes(struct device *dev) #endif /* CONFIG_USB_SUSPEND */ + /* Descriptor fields */ #define usb_descriptor_attr_le16(field, format_string) \ static ssize_t \ @@ -384,6 +450,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) if (retval) return retval; + retval = add_persist_attributes(dev); + if (retval) + goto error; + retval = add_power_attributes(dev); if (retval) goto error; @@ -421,9 +491,29 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) device_remove_file(dev, &dev_attr_product); device_remove_file(dev, &dev_attr_serial); remove_power_attributes(dev); + remove_persist_attributes(dev); sysfs_remove_group(&dev->kobj, &dev_attr_grp); } +/* Interface Accociation Descriptor fields */ +#define usb_intf_assoc_attr(field, format_string) \ +static ssize_t \ +show_iad_##field (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface (dev); \ + \ + return sprintf (buf, format_string, \ + intf->intf_assoc->field); \ +} \ +static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL); + +usb_intf_assoc_attr (bFirstInterface, "%02x\n") +usb_intf_assoc_attr (bInterfaceCount, "%02d\n") +usb_intf_assoc_attr (bFunctionClass, "%02x\n") +usb_intf_assoc_attr (bFunctionSubClass, "%02x\n") +usb_intf_assoc_attr (bFunctionProtocol, "%02x\n") + /* Interface fields */ #define usb_intf_attr(field, format_string) \ static ssize_t \ @@ -487,6 +577,18 @@ static ssize_t show_modalias(struct device *dev, } static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); +static struct attribute *intf_assoc_attrs[] = { + &dev_attr_iad_bFirstInterface.attr, + &dev_attr_iad_bInterfaceCount.attr, + &dev_attr_iad_bFunctionClass.attr, + &dev_attr_iad_bFunctionSubClass.attr, + &dev_attr_iad_bFunctionProtocol.attr, + NULL, +}; +static struct attribute_group intf_assoc_attr_grp = { + .attrs = intf_assoc_attrs, +}; + static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, &dev_attr_bAlternateSetting.attr, @@ -538,6 +640,8 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf) alt->string = usb_cache_string(udev, alt->desc.iInterface); if (alt->string) retval = device_create_file(dev, &dev_attr_interface); + if (intf->intf_assoc) + retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp); usb_create_intf_ep_files(intf, udev); return 0; } @@ -549,4 +653,5 @@ void usb_remove_sysfs_intf_files(struct usb_interface *intf) usb_remove_intf_ep_files(intf); device_remove_file(dev, &dev_attr_interface); sysfs_remove_group(&dev->kobj, &intf_attr_grp); + sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp); } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 94ea972..52ec44b 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -4,6 +4,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/usb.h> +#include <linux/wait.h> #include "hcd.h" #define to_urb(d) container_of(d, struct urb, kref) @@ -11,6 +12,10 @@ static void urb_destroy(struct kref *kref) { struct urb *urb = to_urb(kref); + + if (urb->transfer_flags & URB_FREE_BUFFER) + kfree(urb->transfer_buffer); + kfree(urb); } @@ -34,6 +39,7 @@ void usb_init_urb(struct urb *urb) memset(urb, 0, sizeof(*urb)); kref_init(&urb->kref); spin_lock_init(&urb->lock); + INIT_LIST_HEAD(&urb->anchor_list); } } @@ -100,8 +106,60 @@ struct urb * usb_get_urb(struct urb *urb) kref_get(&urb->kref); return urb; } - - + +/** + * usb_anchor_urb - anchors an URB while it is processed + * @urb: pointer to the urb to anchor + * @anchor: pointer to the anchor + * + * This can be called to have access to URBs which are to be executed + * without bothering to track them + */ +void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor) +{ + unsigned long flags; + + spin_lock_irqsave(&anchor->lock, flags); + usb_get_urb(urb); + list_add_tail(&urb->anchor_list, &anchor->urb_list); + urb->anchor = anchor; + spin_unlock_irqrestore(&anchor->lock, flags); +} +EXPORT_SYMBOL_GPL(usb_anchor_urb); + +/** + * usb_unanchor_urb - unanchors an URB + * @urb: pointer to the urb to anchor + * + * Call this to stop the system keeping track of this URB + */ +void usb_unanchor_urb(struct urb *urb) +{ + unsigned long flags; + struct usb_anchor *anchor; + + if (!urb) + return; + + anchor = urb->anchor; + if (!anchor) + return; + + spin_lock_irqsave(&anchor->lock, flags); + if (unlikely(anchor != urb->anchor)) { + /* we've lost the race to another thread */ + spin_unlock_irqrestore(&anchor->lock, flags); + return; + } + urb->anchor = NULL; + list_del(&urb->anchor_list); + spin_unlock_irqrestore(&anchor->lock, flags); + usb_put_urb(urb); + if (list_empty(&anchor->urb_list)) + wake_up(&anchor->wait); +} +EXPORT_SYMBOL_GPL(usb_unanchor_urb); + /*-------------------------------------------------------------------*/ /** @@ -478,6 +536,48 @@ void usb_kill_urb(struct urb *urb) spin_unlock_irq(&urb->lock); } +/** + * usb_kill_anchored_urbs - cancel transfer requests en masse + * @anchor: anchor the requests are bound to + * + * this allows all outstanding URBs to be killed starting + * from the back of the queue + */ +void usb_kill_anchored_urbs(struct usb_anchor *anchor) +{ + struct urb *victim; + + spin_lock_irq(&anchor->lock); + while (!list_empty(&anchor->urb_list)) { + victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list); + /* we must make sure the URB isn't freed before we kill it*/ + usb_get_urb(victim); + spin_unlock_irq(&anchor->lock); + /* this will unanchor the URB */ + usb_kill_urb(victim); + usb_put_urb(victim); + spin_lock_irq(&anchor->lock); + } + spin_unlock_irq(&anchor->lock); +} +EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs); + +/** + * usb_wait_anchor_empty_timeout - wait for an anchor to be unused + * @anchor: the anchor you want to become unused + * @timeout: how long you are willing to wait in milliseconds + * + * Call this is you want to be sure all an anchor's + * URBs have finished + */ +int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, + unsigned int timeout) +{ + return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list), + msecs_to_jiffies(timeout)); +} +EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout); + EXPORT_SYMBOL(usb_init_urb); EXPORT_SYMBOL(usb_alloc_urb); EXPORT_SYMBOL(usb_free_urb); @@ -485,4 +585,3 @@ EXPORT_SYMBOL(usb_get_urb); EXPORT_SYMBOL(usb_submit_urb); EXPORT_SYMBOL(usb_unlink_urb); EXPORT_SYMBOL(usb_kill_urb); - diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 4a6299b..0fee5c6 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -253,6 +253,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) dev->dev.bus = &usb_bus_type; dev->dev.type = &usb_device_type; dev->dev.dma_mask = bus->controller->dma_mask; + set_dev_node(&dev->dev, dev_to_node(bus->controller)); dev->state = USB_STATE_ATTACHED; INIT_LIST_HEAD(&dev->ep0.urb_list); @@ -578,11 +579,12 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size, * address (through the pointer provided). * * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags - * to avoid behaviors like using "DMA bounce buffers", or tying down I/O - * mapping hardware for long idle periods. The implementation varies between + * to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU + * hardware during URB completion/resubmit. The implementation varies between * platforms, depending on details of how DMA will work to this device. - * Using these buffers also helps prevent cacheline sharing problems on - * architectures where CPU caches are not DMA-coherent. + * Using these buffers also eliminates cacheline sharing problems on + * architectures where CPU caches are not DMA-coherent. On systems without + * bus-snooping caches, these buffers are uncached. * * When the buffer is no longer used, free it with usb_buffer_free(). */ @@ -607,7 +609,7 @@ void *usb_buffer_alloc( * * This reclaims an I/O buffer, letting it be reused. The memory must have * been allocated using usb_buffer_alloc(), and the parameters must match - * those provided in that allocation request. + * those provided in that allocation request. */ void usb_buffer_free( struct usb_device *dev, diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index bf2eb0d..ad5fa03 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -52,8 +52,16 @@ static inline void usb_pm_unlock(struct usb_device *udev) #else -#define usb_port_suspend(dev) 0 -#define usb_port_resume(dev) 0 +static inline int usb_port_suspend(struct usb_device *udev) +{ + return 0; +} + +static inline int usb_port_resume(struct usb_device *udev) +{ + return 0; +} + static inline void usb_pm_lock(struct usb_device *udev) {} static inline void usb_pm_unlock(struct usb_device *udev) {} @@ -100,11 +108,13 @@ static inline int is_usb_device_driver(struct device_driver *drv) static inline void mark_active(struct usb_interface *f) { f->is_active = 1; + f->dev.power.power_state.event = PM_EVENT_ON; } static inline void mark_quiesced(struct usb_interface *f) { f->is_active = 0; + f->dev.power.power_state.event = PM_EVENT_SUSPEND; } static inline int is_active(const struct usb_interface *f) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f771a7c..45e01e2 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -42,6 +42,20 @@ config USB_GADGET For more information, see <http://www.linux-usb.org/gadget> and the kernel DocBook documentation for this API. +config USB_GADGET_DEBUG + boolean "Debugging messages" + depends on USB_GADGET && DEBUG_KERNEL && EXPERIMENTAL + help + Many controller and gadget drivers will print some debugging + messages if you use this option to ask for those messages. + + Avoid enabling these messages, even if you're actively + debugging such a driver. Many drivers will emit so many + messages that the driver timings are affected, which will + either create new failure modes or remove the one you're + trying to track down. Never enable these messages for a + production build. + config USB_GADGET_DEBUG_FILES boolean "Debugging information files" depends on USB_GADGET && PROC_FS @@ -208,6 +222,27 @@ config USB_OTG Select this only if your OMAP board has a Mini-AB connector. +config USB_GADGET_S3C2410 + boolean "S3C2410 USB Device Controller" + depends on ARCH_S3C2410 + help + Samsung's S3C2410 is an ARM-4 processor with an integrated + full speed USB 1.1 device controller. It has 4 configurable + endpoints, as well as endpoint zero (for control transfers). + + This driver has been tested on the S3C2410, S3C2412, and + S3C2440 processors. + +config USB_S3C2410 + tristate + depends on USB_GADGET_S3C2410 + default USB_GADGET + select USB_GADGET_SELECTED + +config USB_S3C2410_DEBUG + boolean "S3C2410 udc debug messages" + depends on USB_GADGET_S3C2410 + config USB_GADGET_AT91 boolean "AT91 USB Device Port" depends on ARCH_AT91 && !ARCH_AT91SAM9RL @@ -226,6 +261,24 @@ config USB_AT91 depends on USB_GADGET_AT91 default USB_GADGET +config USB_GADGET_M66592 + boolean "M66592 driver" + select USB_GADGET_DUALSPEED + help + M66592 is a USB 2.0 peripheral controller. + + It has seven configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "m66592_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_M66592 + tristate + depends on USB_GADGET_M66592 + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_DUMMY_HCD boolean "Dummy HCD (DEVELOPMENT)" depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5db1939..8ae76f7 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,14 +1,20 @@ # # USB peripheral controller drivers # +ifeq ($(CONFIG_USB_GADGET_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o +obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o obj-$(CONFIG_USB_AT91) += at91_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o +obj-$(CONFIG_USB_M66592) += m66592-udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index ba163f3..63d7d65 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -601,25 +601,6 @@ static void at91_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) kfree(req); } -static void *at91_ep_alloc_buffer( - struct usb_ep *_ep, - unsigned bytes, - dma_addr_t *dma, - gfp_t gfp_flags) -{ - *dma = ~0; - return kmalloc(bytes, gfp_flags); -} - -static void at91_ep_free_buffer( - struct usb_ep *ep, - void *buf, - dma_addr_t dma, - unsigned bytes) -{ - kfree(buf); -} - static int at91_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { @@ -788,8 +769,6 @@ static const struct usb_ep_ops at91_ep_ops = { .disable = at91_ep_disable, .alloc_request = at91_ep_alloc_request, .free_request = at91_ep_free_request, - .alloc_buffer = at91_ep_alloc_buffer, - .free_buffer = at91_ep_free_buffer, .queue = at91_ep_queue, .dequeue = at91_ep_dequeue, .set_halt = at91_ep_set_halt, diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index fcb5526..f2fbdc7 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -497,38 +497,6 @@ dummy_free_request (struct usb_ep *_ep, struct usb_request *_req) kfree (req); } -static void * -dummy_alloc_buffer ( - struct usb_ep *_ep, - unsigned bytes, - dma_addr_t *dma, - gfp_t mem_flags -) { - char *retval; - struct dummy_ep *ep; - struct dummy *dum; - - ep = usb_ep_to_dummy_ep (_ep); - dum = ep_to_dummy (ep); - - if (!dum->driver) - return NULL; - retval = kmalloc (bytes, mem_flags); - *dma = (dma_addr_t) retval; - return retval; -} - -static void -dummy_free_buffer ( - struct usb_ep *_ep, - void *buf, - dma_addr_t dma, - unsigned bytes -) { - if (bytes) - kfree (buf); -} - static void fifo_complete (struct usb_ep *ep, struct usb_request *req) { @@ -659,10 +627,6 @@ static const struct usb_ep_ops dummy_ep_ops = { .alloc_request = dummy_alloc_request, .free_request = dummy_free_request, - .alloc_buffer = dummy_alloc_buffer, - .free_buffer = dummy_free_buffer, - /* map, unmap, ... eventually hook the "generic" dma calls */ - .queue = dummy_queue, .dequeue = dummy_dequeue, @@ -1784,8 +1748,7 @@ static int dummy_bus_resume (struct usb_hcd *hcd) spin_lock_irq (&dum->lock); if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { - dev_warn (&hcd->self.root_hub->dev, "HC isn't running!\n"); - rc = -ENODEV; + rc = -ESHUTDOWN; } else { dum->rh_state = DUMMY_RH_RUNNING; set_link_state (dum); diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 325bf7c..dbaf867 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -277,7 +277,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif -#ifdef CONFIG_USB_GADGET_HUSB2DEV +#ifdef CONFIG_USB_GADGET_ATMEL_USBA #define DEV_CONFIG_CDC #endif @@ -292,7 +292,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_SUBSET #endif -#ifdef CONFIG_USB_GADGET_SH +#ifdef CONFIG_USB_GADGET_SUPERH #define DEV_CONFIG_SUBSET #endif @@ -301,6 +301,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_SUBSET #endif +#ifdef CONFIG_USB_GADGET_M66592 +#define DEV_CONFIG_CDC +#endif + /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 4639b62..8712ef9 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3733,19 +3733,12 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) } /* Free the data buffers */ - for (i = 0; i < NUM_BUFFERS; ++i) { - struct fsg_buffhd *bh = &fsg->buffhds[i]; - - if (bh->buf) - usb_ep_free_buffer(fsg->bulk_in, bh->buf, bh->dma, - mod_data.buflen); - } + for (i = 0; i < NUM_BUFFERS; ++i) + kfree(fsg->buffhds[i].buf); /* Free the request and buffer for endpoint 0 */ if (req) { - if (req->buf) - usb_ep_free_buffer(fsg->ep0, req->buf, - req->dma, EP0_BUFSIZE); + kfree(req->buf); usb_ep_free_request(fsg->ep0, req); } @@ -3963,8 +3956,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) #endif if (gadget->is_otg) { - otg_desc.bmAttributes |= USB_OTG_HNP, - config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + otg_desc.bmAttributes |= USB_OTG_HNP; } rc = -ENOMEM; @@ -3973,8 +3965,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); if (!req) goto out; - req->buf = usb_ep_alloc_buffer(fsg->ep0, EP0_BUFSIZE, - &req->dma, GFP_KERNEL); + req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL); if (!req->buf) goto out; req->complete = ep0_complete; @@ -3986,8 +3977,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) /* Allocate for the bulk-in endpoint. We assume that * the buffer will also work with the bulk-out (and * interrupt-in) endpoint. */ - bh->buf = usb_ep_alloc_buffer(fsg->bulk_in, mod_data.buflen, - &bh->dma, GFP_KERNEL); + bh->buf = kmalloc(mod_data.buflen, GFP_KERNEL); if (!bh->buf) goto out; bh->next = bh + 1; diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index 3ca2b31..10b2b33 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -228,7 +228,7 @@ static int dr_controller_setup(struct fsl_udc *udc) /* Config PHY interface */ portctrl = fsl_readl(&dr_regs->portsc1); - portctrl &= ~(PORTSCX_PHY_TYPE_SEL & PORTSCX_PORT_WIDTH); + portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); switch (udc->phy_mode) { case FSL_USB2_PHY_ULPI: portctrl |= PORTSCX_PTS_ULPI; @@ -601,39 +601,6 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req) kfree(req); } -/*------------------------------------------------------------------ - * Allocate an I/O buffer -*---------------------------------------------------------------------*/ -static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes, - dma_addr_t *dma, gfp_t gfp_flags) -{ - struct fsl_ep *ep; - - if (!_ep) - return NULL; - - ep = container_of(_ep, struct fsl_ep, ep); - - return dma_alloc_coherent(ep->udc->gadget.dev.parent, - bytes, dma, gfp_flags); -} - -/*------------------------------------------------------------------ - * frees an i/o buffer -*---------------------------------------------------------------------*/ -static void fsl_free_buffer(struct usb_ep *_ep, void *buf, - dma_addr_t dma, unsigned bytes) -{ - struct fsl_ep *ep; - - if (!_ep) - return; - - ep = container_of(_ep, struct fsl_ep, ep); - - dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma); -} - /*-------------------------------------------------------------------------*/ static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) { @@ -1047,9 +1014,6 @@ static struct usb_ep_ops fsl_ep_ops = { .alloc_request = fsl_alloc_request, .free_request = fsl_free_request, - .alloc_buffer = fsl_alloc_buffer, - .free_buffer = fsl_free_buffer, - .queue = fsl_ep_queue, .dequeue = fsl_ep_dequeue, @@ -2189,27 +2153,19 @@ static void fsl_udc_release(struct device *dev) * init resource for globle controller * Return the udc handle on success or NULL on failure ------------------------------------------------------------------*/ -static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev) +static int __init struct_udc_setup(struct fsl_udc *udc, + struct platform_device *pdev) { - struct fsl_udc *udc; struct fsl_usb2_platform_data *pdata; size_t size; - udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); - if (udc == NULL) { - ERR("malloc udc failed\n"); - return NULL; - } - pdata = pdev->dev.platform_data; udc->phy_mode = pdata->phy_mode; - /* max_ep_nr is bidirectional ep number, max_ep doubles the number */ - udc->max_ep = pdata->max_ep_nr * 2; udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL); if (!udc->eps) { ERR("malloc fsl_ep failed\n"); - goto cleanup; + return -1; } /* initialized QHs, take care of alignment */ @@ -2225,7 +2181,7 @@ static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev) if (!udc->ep_qh) { ERR("malloc QHs for udc failed\n"); kfree(udc->eps); - goto cleanup; + return -1; } udc->ep_qh_size = size; @@ -2244,11 +2200,7 @@ static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev) udc->remote_wakeup = 0; /* default to 0 on reset */ spin_lock_init(&udc->lock); - return udc; - -cleanup: - kfree(udc); - return NULL; + return 0; } /*---------------------------------------------------------------- @@ -2287,35 +2239,37 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, } /* Driver probe function - * all intialize operations implemented here except enabling usb_intr reg + * all intialization operations implemented here except enabling usb_intr reg + * board setup should have been done in the platform code */ static int __init fsl_udc_probe(struct platform_device *pdev) { struct resource *res; int ret = -ENODEV; unsigned int i; + u32 dccparams; if (strcmp(pdev->name, driver_name)) { VDBG("Wrong device\n"); return -ENODEV; } - /* board setup should have been done in the platform code */ - - /* Initialize the udc structure including QH member and other member */ - udc_controller = struct_udc_setup(pdev); - if (!udc_controller) { - VDBG("udc_controller is NULL \n"); + udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); + if (udc_controller == NULL) { + ERR("malloc udc failed\n"); return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) + if (!res) { + kfree(udc_controller); return -ENXIO; + } if (!request_mem_region(res->start, res->end - res->start + 1, driver_name)) { ERR("request mem region for %s failed \n", pdev->name); + kfree(udc_controller); return -EBUSY; } @@ -2328,13 +2282,24 @@ static int __init fsl_udc_probe(struct platform_device *pdev) usb_sys_regs = (struct usb_sys_interface *) ((u32)dr_regs + USB_DR_SYS_OFFSET); + /* Read Device Controller Capability Parameters register */ + dccparams = fsl_readl(&dr_regs->dccparams); + if (!(dccparams & DCCPARAMS_DC)) { + ERR("This SOC doesn't support device role\n"); + ret = -ENODEV; + goto err2; + } + /* Get max device endpoints */ + /* DEN is bidirectional ep number, max_ep doubles the number */ + udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2; + udc_controller->irq = platform_get_irq(pdev, 0); if (!udc_controller->irq) { ret = -ENODEV; goto err2; } - ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ, + ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED, driver_name, udc_controller); if (ret != 0) { ERR("cannot request irq %d err %d \n", @@ -2342,6 +2307,13 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err2; } + /* Initialize the udc structure including QH member and other member */ + if (struct_udc_setup(udc_controller, pdev)) { + ERR("Can't initialize udc data structure\n"); + ret = -ENOMEM; + goto err3; + } + /* initialize usb hw reg except for regs for EP, * leave usbintr reg untouched */ dr_controller_setup(udc_controller); @@ -2403,6 +2375,7 @@ err2: iounmap(dr_regs); err1: release_mem_region(res->start, res->end - res->start + 1); + kfree(udc_controller); return ret; } diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index c6291e0..832ab82 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -101,6 +101,10 @@ struct usb_sys_interface { #define WAIT_FOR_OUT_STATUS 3 #define DATA_STATE_RECV 4 +/* Device Controller Capability Parameter register */ +#define DCCPARAMS_DC 0x00000080 +#define DCCPARAMS_DEN_MASK 0x0000001f + /* Frame Index Register Bit Masks */ #define USB_FRINDEX_MASKS 0x3fff /* USB CMD Register Bit Masks */ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index d041b91..53e9139 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -8,6 +8,8 @@ * (And avoiding all runtime comparisons in typical one-choice configs!) * * NOTE: some of these controller drivers may not be available yet. + * Some are available on 2.4 kernels; several are available, but not + * yet pushed in the 2.6 mainline tree. */ #ifdef CONFIG_USB_GADGET_NET2280 #define gadget_is_net2280(g) !strcmp("net2280", (g)->name) @@ -33,12 +35,14 @@ #define gadget_is_goku(g) 0 #endif +/* SH3 UDC -- not yet ported 2.4 --> 2.6 */ #ifdef CONFIG_USB_GADGET_SUPERH #define gadget_is_sh(g) !strcmp("sh_udc", (g)->name) #else #define gadget_is_sh(g) 0 #endif +/* not yet stable on 2.6 (would help "original Zaurus") */ #ifdef CONFIG_USB_GADGET_SA1100 #define gadget_is_sa1100(g) !strcmp("sa1100_udc", (g)->name) #else @@ -51,6 +55,7 @@ #define gadget_is_lh7a40x(g) 0 #endif +/* handhelds.org tree (?) */ #ifdef CONFIG_USB_GADGET_MQ11XX #define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name) #else @@ -63,22 +68,24 @@ #define gadget_is_omap(g) 0 #endif +/* not yet ported 2.4 --> 2.6 */ #ifdef CONFIG_USB_GADGET_N9604 #define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name) #else #define gadget_is_n9604(g) 0 #endif +/* various unstable versions available */ #ifdef CONFIG_USB_GADGET_PXA27X #define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name) #else #define gadget_is_pxa27x(g) 0 #endif -#ifdef CONFIG_USB_GADGET_HUSB2DEV -#define gadget_is_husb2dev(g) !strcmp("husb2_udc", (g)->name) +#ifdef CONFIG_USB_GADGET_ATMEL_USBA +#define gadget_is_atmel_usba(g) !strcmp("atmel_usba_udc", (g)->name) #else -#define gadget_is_husb2dev(g) 0 +#define gadget_is_atmel_usba(g) 0 #endif #ifdef CONFIG_USB_GADGET_S3C2410 @@ -93,6 +100,7 @@ #define gadget_is_at91(g) 0 #endif +/* status unclear */ #ifdef CONFIG_USB_GADGET_IMX #define gadget_is_imx(g) !strcmp("imx_udc", (g)->name) #else @@ -106,6 +114,7 @@ #endif /* Mentor high speed function controller */ +/* from Montavista kernel (?) */ #ifdef CONFIG_USB_GADGET_MUSBHSFC #define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name) #else @@ -119,12 +128,20 @@ #define gadget_is_musbhdrc(g) 0 #endif +/* from Montavista kernel (?) */ #ifdef CONFIG_USB_GADGET_MPC8272 #define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name) #else #define gadget_is_mpc8272(g) 0 #endif +#ifdef CONFIG_USB_GADGET_M66592 +#define gadget_is_m66592(g) !strcmp("m66592_udc", (g)->name) +#else +#define gadget_is_m66592(g) 0 +#endif + + // CONFIG_USB_GADGET_SX2 // CONFIG_USB_GADGET_AU1X00 // ... @@ -181,9 +198,11 @@ 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)) + else if (gadget_is_atmel_usba(gadget)) return 0x18; else if (gadget_is_fsl_usb2(gadget)) return 0x19; + else if (gadget_is_m66592(gadget)) + return 0x20; return -ENOENT; } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index d08a8d0..1c5aa49 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -1248,17 +1248,11 @@ autoconf_fail: tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev); /* preallocate control response and buffer */ - dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ); if (!dev->req) { err = -ENOMEM; goto fail; } - dev->req->buf = usb_ep_alloc_buffer(gadget->ep0, USB_BUFSIZ, - &dev->req->dma, GFP_KERNEL); - if (!dev->req->buf) { - err = -ENOMEM; - goto fail; - } dev->req->complete = gmidi_setup_complete; diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index ae931af..d6c5f11 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -20,7 +20,6 @@ * - DMA works with ep1 (OUT transfers) and ep2 (IN transfers). */ -#undef DEBUG // #define VERBOSE /* extra debug messages (success too) */ // #define USB_TRACE /* packet-level success messages */ @@ -296,51 +295,6 @@ goku_free_request(struct usb_ep *_ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ -/* allocating buffers this way eliminates dma mapping overhead, which - * on some platforms will mean eliminating a per-io buffer copy. with - * some kinds of system caches, further tweaks may still be needed. - */ -static void * -goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes, - dma_addr_t *dma, gfp_t gfp_flags) -{ - void *retval; - struct goku_ep *ep; - - ep = container_of(_ep, struct goku_ep, ep); - if (!_ep) - return NULL; - *dma = DMA_ADDR_INVALID; - - if (ep->dma) { - /* the main problem with this call is that it wastes memory - * on typical 1/N page allocations: it allocates 1-N pages. - */ -#warning Using dma_alloc_coherent even with buffers smaller than a page. - retval = dma_alloc_coherent(&ep->dev->pdev->dev, - bytes, dma, gfp_flags); - } else - retval = kmalloc(bytes, gfp_flags); - return retval; -} - -static void -goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) -{ - /* free memory into the right allocator */ - if (dma != DMA_ADDR_INVALID) { - struct goku_ep *ep; - - ep = container_of(_ep, struct goku_ep, ep); - if (!_ep) - return; - dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma); - } else - kfree (buf); -} - -/*-------------------------------------------------------------------------*/ - static void done(struct goku_ep *ep, struct goku_request *req, int status) { @@ -485,7 +439,7 @@ top: /* use ep1/ep2 double-buffering for OUT */ if (!(size & PACKET_ACTIVE)) size = readl(®s->EPxSizeLB[ep->num]); - if (!(size & PACKET_ACTIVE)) // "can't happen" + if (!(size & PACKET_ACTIVE)) /* "can't happen" */ break; size &= DATASIZE; /* EPxSizeH == 0 */ @@ -1026,9 +980,6 @@ static struct usb_ep_ops goku_ep_ops = { .alloc_request = goku_alloc_request, .free_request = goku_free_request, - .alloc_buffer = goku_alloc_buffer, - .free_buffer = goku_free_buffer, - .queue = goku_queue, .dequeue = goku_dequeue, @@ -1140,17 +1091,17 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, is_usb_connected ? ((tmp & PW_PULLUP) ? "full speed" : "powered") : "disconnected", - ({char *tmp; + ({char *state; switch(dev->ep0state){ - case EP0_DISCONNECT: tmp = "ep0_disconnect"; break; - case EP0_IDLE: tmp = "ep0_idle"; break; - case EP0_IN: tmp = "ep0_in"; break; - case EP0_OUT: tmp = "ep0_out"; break; - case EP0_STATUS: tmp = "ep0_status"; break; - case EP0_STALL: tmp = "ep0_stall"; break; - case EP0_SUSPEND: tmp = "ep0_suspend"; break; - default: tmp = "ep0_?"; break; - } tmp; }) + case EP0_DISCONNECT: state = "ep0_disconnect"; break; + case EP0_IDLE: state = "ep0_idle"; break; + case EP0_IN: state = "ep0_in"; break; + case EP0_OUT: state = "ep0_out"; break; + case EP0_STATUS: state = "ep0_status"; break; + case EP0_STALL: state = "ep0_stall"; break; + case EP0_SUSPEND: state = "ep0_suspend"; break; + default: state = "ep0_?"; break; + } state; }) ); size -= t; next += t; @@ -1195,7 +1146,6 @@ udc_proc_read(char *buffer, char **start, off_t off, int count, for (i = 0; i < 4; i++) { struct goku_ep *ep = &dev->ep [i]; struct goku_request *req; - int t; if (i && !ep->desc) continue; @@ -1283,7 +1233,7 @@ done: static void udc_reinit (struct goku_udc *dev) { static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" }; - + unsigned i; INIT_LIST_HEAD (&dev->gadget.ep_list); @@ -1896,9 +1846,9 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* done */ the_controller = dev; - device_register(&dev->gadget.dev); - - return 0; + retval = device_register(&dev->gadget.dev); + if (retval == 0) + return 0; done: if (dev) @@ -1910,8 +1860,8 @@ done: /*-------------------------------------------------------------------------*/ static struct pci_device_id pci_ids [] = { { - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, .vendor = 0x102f, /* Toshiba */ .device = 0x0107, /* this UDC */ .subvendor = PCI_ANY_ID, diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h index ea8c8e5..bc4eb1e 100644 --- a/drivers/usb/gadget/goku_udc.h +++ b/drivers/usb/gadget/goku_udc.h @@ -41,8 +41,10 @@ struct goku_udc_regs { #define INT_SYSERROR 0x40000 #define INT_PWRDETECT 0x80000 -#define INT_DEVWIDE (INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND) -#define INT_EP0 (INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK) +#define INT_DEVWIDE \ + (INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND) +#define INT_EP0 \ + (INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK) u32 dma_master; #define MST_EOPB_DIS 0x0800 @@ -231,7 +233,7 @@ struct goku_request { enum ep0state { EP0_DISCONNECT, /* no host */ EP0_IDLE, /* between STATUS ack and SETUP report */ - EP0_IN, EP0_OUT, /* data stage */ + EP0_IN, EP0_OUT, /* data stage */ EP0_STATUS, /* status stage */ EP0_STALL, /* data or status stages */ EP0_SUSPEND, /* usb suspend */ @@ -242,7 +244,7 @@ struct goku_udc { struct usb_gadget gadget; spinlock_t lock; struct goku_ep ep[4]; - struct usb_gadget_driver *driver; + struct usb_gadget_driver *driver; enum ep0state ep0state; unsigned got_irq:1, diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 46d0e52..e60745ff 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -37,7 +37,7 @@ #include <linux/device.h> #include <linux/moduleparam.h> -#include <linux/usb_gadgetfs.h> +#include <linux/usb/gadgetfs.h> #include <linux/usb_gadget.h> @@ -923,7 +923,7 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req) struct dev_data *dev = ep->driver_data; if (req->buf != dev->rbuf) { - usb_ep_free_buffer (ep, req->buf, req->dma, req->length); + kfree(req->buf); req->buf = dev->rbuf; req->dma = DMA_ADDR_INVALID; } @@ -963,7 +963,7 @@ static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len) return -EBUSY; } if (len > sizeof (dev->rbuf)) - req->buf = usb_ep_alloc_buffer (ep, len, &req->dma, GFP_ATOMIC); + req->buf = kmalloc(len, GFP_ATOMIC); if (req->buf == 0) { req->buf = dev->rbuf; return -ENOMEM; @@ -1505,7 +1505,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) } break; -#ifndef CONFIG_USB_GADGETFS_PXA2XX +#ifndef CONFIG_USB_GADGET_PXA2XX /* PXA automagically handles this request too */ case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != 0x80) diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c index a0a73c0..e78c2dd 100644 --- a/drivers/usb/gadget/lh7a40x_udc.c +++ b/drivers/usb/gadget/lh7a40x_udc.c @@ -75,10 +75,6 @@ static int lh7a40x_ep_enable(struct usb_ep *ep, static int lh7a40x_ep_disable(struct usb_ep *ep); static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t); static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *); -static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *, - gfp_t); -static void lh7a40x_free_buffer(struct usb_ep *ep, void *, dma_addr_t, - unsigned); static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, gfp_t); static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *); static int lh7a40x_set_halt(struct usb_ep *ep, int); @@ -104,9 +100,6 @@ static struct usb_ep_ops lh7a40x_ep_ops = { .alloc_request = lh7a40x_alloc_request, .free_request = lh7a40x_free_request, - .alloc_buffer = lh7a40x_alloc_buffer, - .free_buffer = lh7a40x_free_buffer, - .queue = lh7a40x_queue, .dequeue = lh7a40x_dequeue, @@ -1134,26 +1127,6 @@ static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *_req) kfree(req); } -static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned bytes, - dma_addr_t * dma, gfp_t gfp_flags) -{ - char *retval; - - DEBUG("%s (%p, %d, %d)\n", __FUNCTION__, ep, bytes, gfp_flags); - - retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM)); - if (retval) - *dma = virt_to_bus(retval); - return retval; -} - -static void lh7a40x_free_buffer(struct usb_ep *ep, void *buf, dma_addr_t dma, - unsigned bytes) -{ - DEBUG("%s, %p\n", __FUNCTION__, ep); - kfree(buf); -} - /** Queue one request * Kickstart transfer if needed * NOTE: Sets INDEX register diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c new file mode 100644 index 0000000..c0a962b --- /dev/null +++ b/drivers/usb/gadget/m66592-udc.c @@ -0,0 +1,1653 @@ +/* + * M66592 UDC (USB gadget) + * + * Copyright (C) 2006-2007 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/usb/ch9.h> +#include <linux/usb_gadget.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> + +#include "m66592-udc.h" + +MODULE_DESCRIPTION("M66592 USB gadget driiver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yoshihiro Shimoda"); + +#define DRIVER_VERSION "29 May 2007" + +/* module parameters */ +static unsigned short clock = M66592_XTAL24; +module_param(clock, ushort, 0644); +MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0(default=16384)"); +static unsigned short vif = M66592_LDRV; +module_param(vif, ushort, 0644); +MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0(default=32768)"); +static unsigned short endian = 0; +module_param(endian, ushort, 0644); +MODULE_PARM_DESC(endian, "data endian: big=256, little=0(default=0)"); +static unsigned short irq_sense = M66592_INTL; +module_param(irq_sense, ushort, 0644); +MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=2, falling edge=0(default=2)"); + +static const char udc_name[] = "m66592_udc"; +static const char *m66592_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7" +}; + +static void disable_controller(struct m66592 *m66592); +static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req); +static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req); +static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags); + +static void transfer_complete(struct m66592_ep *ep, + struct m66592_request *req, + int status); +/*-------------------------------------------------------------------------*/ +static inline u16 get_usb_speed(struct m66592 *m66592) +{ + return (m66592_read(m66592, M66592_DVSTCTR) & M66592_RHST); +} + +static void enable_pipe_irq(struct m66592 *m66592, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = m66592_read(m66592, M66592_INTENB0); + m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE, + M66592_INTENB0); + m66592_bset(m66592, (1 << pipenum), reg); + m66592_write(m66592, tmp, M66592_INTENB0); +} + +static void disable_pipe_irq(struct m66592 *m66592, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = m66592_read(m66592, M66592_INTENB0); + m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE, + M66592_INTENB0); + m66592_bclr(m66592, (1 << pipenum), reg); + m66592_write(m66592, tmp, M66592_INTENB0); +} + +static void m66592_usb_connect(struct m66592 *m66592) +{ + m66592_bset(m66592, M66592_CTRE, M66592_INTENB0); + m66592_bset(m66592, M66592_WDST | M66592_RDST | M66592_CMPL, + M66592_INTENB0); + m66592_bset(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0); + + m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG); +} + +static void m66592_usb_disconnect(struct m66592 *m66592) +{ + m66592_bclr(m66592, M66592_CTRE, M66592_INTENB0); + m66592_bclr(m66592, M66592_WDST | M66592_RDST | M66592_CMPL, + M66592_INTENB0); + m66592_bclr(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0); + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + + m66592->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock(&m66592->lock); + m66592->driver->disconnect(&m66592->gadget); + spin_lock(&m66592->lock); + + disable_controller(m66592); + INIT_LIST_HEAD(&m66592->ep[0].queue); +} + +static inline u16 control_reg_get_pid(struct m66592 *m66592, u16 pipenum) +{ + u16 pid = 0; + unsigned long offset; + + if (pipenum == 0) + pid = m66592_read(m66592, M66592_DCPCTR) & M66592_PID; + else if (pipenum < M66592_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + pid = m66592_read(m66592, offset) & M66592_PID; + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + + return pid; +} + +static inline void control_reg_set_pid(struct m66592 *m66592, u16 pipenum, + u16 pid) +{ + unsigned long offset; + + if (pipenum == 0) + m66592_mdfy(m66592, pid, M66592_PID, M66592_DCPCTR); + else if (pipenum < M66592_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + m66592_mdfy(m66592, pid, M66592_PID, offset); + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); +} + +static inline void pipe_start(struct m66592 *m66592, u16 pipenum) +{ + control_reg_set_pid(m66592, pipenum, M66592_PID_BUF); +} + +static inline void pipe_stop(struct m66592 *m66592, u16 pipenum) +{ + control_reg_set_pid(m66592, pipenum, M66592_PID_NAK); +} + +static inline void pipe_stall(struct m66592 *m66592, u16 pipenum) +{ + control_reg_set_pid(m66592, pipenum, M66592_PID_STALL); +} + +static inline u16 control_reg_get(struct m66592 *m66592, u16 pipenum) +{ + u16 ret = 0; + unsigned long offset; + + if (pipenum == 0) + ret = m66592_read(m66592, M66592_DCPCTR); + else if (pipenum < M66592_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + ret = m66592_read(m66592, offset); + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + + return ret; +} + +static inline void control_reg_sqclr(struct m66592 *m66592, u16 pipenum) +{ + unsigned long offset; + + pipe_stop(m66592, pipenum); + + if (pipenum == 0) + m66592_bset(m66592, M66592_SQCLR, M66592_DCPCTR); + else if (pipenum < M66592_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + m66592_bset(m66592, M66592_SQCLR, offset); + } else + printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum); +} + +static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum) +{ + u16 tmp; + int size; + + if (pipenum == 0) { + tmp = m66592_read(m66592, M66592_DCPCFG); + if ((tmp & M66592_CNTMD) != 0) + size = 256; + else { + tmp = m66592_read(m66592, M66592_DCPMAXP); + size = tmp & M66592_MAXP; + } + } else { + m66592_write(m66592, pipenum, M66592_PIPESEL); + tmp = m66592_read(m66592, M66592_PIPECFG); + if ((tmp & M66592_CNTMD) != 0) { + tmp = m66592_read(m66592, M66592_PIPEBUF); + size = ((tmp >> 10) + 1) * 64; + } else { + tmp = m66592_read(m66592, M66592_PIPEMAXP); + size = tmp & M66592_MXPS; + } + } + + return size; +} + +static inline void pipe_change(struct m66592 *m66592, u16 pipenum) +{ + struct m66592_ep *ep = m66592->pipenum2ep[pipenum]; + + if (ep->use_dma) + return; + + m66592_mdfy(m66592, pipenum, M66592_CURPIPE, ep->fifosel); + + ndelay(450); + + m66592_bset(m66592, M66592_MBW, ep->fifosel); +} + +static int pipe_buffer_setting(struct m66592 *m66592, + struct m66592_pipe_info *info) +{ + u16 bufnum = 0, buf_bsize = 0; + u16 pipecfg = 0; + + if (info->pipe == 0) + return -EINVAL; + + m66592_write(m66592, info->pipe, M66592_PIPESEL); + + if (info->dir_in) + pipecfg |= M66592_DIR; + pipecfg |= info->type; + pipecfg |= info->epnum; + switch (info->type) { + case M66592_INT: + bufnum = 4 + (info->pipe - M66592_BASE_PIPENUM_INT); + buf_bsize = 0; + break; + case M66592_BULK: + bufnum = m66592->bi_bufnum + + (info->pipe - M66592_BASE_PIPENUM_BULK) * 16; + m66592->bi_bufnum += 16; + buf_bsize = 7; + pipecfg |= M66592_DBLB; + if (!info->dir_in) + pipecfg |= M66592_SHTNAK; + break; + case M66592_ISO: + bufnum = m66592->bi_bufnum + + (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16; + m66592->bi_bufnum += 16; + buf_bsize = 7; + break; + } + if (m66592->bi_bufnum > M66592_MAX_BUFNUM) { + printk(KERN_ERR "m66592 pipe memory is insufficient(%d)\n", + m66592->bi_bufnum); + return -ENOMEM; + } + + m66592_write(m66592, pipecfg, M66592_PIPECFG); + m66592_write(m66592, (buf_bsize << 10) | (bufnum), M66592_PIPEBUF); + m66592_write(m66592, info->maxpacket, M66592_PIPEMAXP); + if (info->interval) + info->interval--; + m66592_write(m66592, info->interval, M66592_PIPEPERI); + + return 0; +} + +static void pipe_buffer_release(struct m66592 *m66592, + struct m66592_pipe_info *info) +{ + if (info->pipe == 0) + return; + + switch (info->type) { + case M66592_BULK: + if (is_bulk_pipe(info->pipe)) + m66592->bi_bufnum -= 16; + break; + case M66592_ISO: + if (is_isoc_pipe(info->pipe)) + m66592->bi_bufnum -= 16; + break; + } + + if (is_bulk_pipe(info->pipe)) { + m66592->bulk--; + } else if (is_interrupt_pipe(info->pipe)) + m66592->interrupt--; + else if (is_isoc_pipe(info->pipe)) { + m66592->isochronous--; + if (info->type == M66592_BULK) + m66592->bulk--; + } else + printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n", + info->pipe); +} + +static void pipe_initialize(struct m66592_ep *ep) +{ + struct m66592 *m66592 = ep->m66592; + + m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel); + + m66592_write(m66592, M66592_ACLRM, ep->pipectr); + m66592_write(m66592, 0, ep->pipectr); + m66592_write(m66592, M66592_SQCLR, ep->pipectr); + if (ep->use_dma) { + m66592_mdfy(m66592, ep->pipenum, M66592_CURPIPE, ep->fifosel); + + ndelay(450); + + m66592_bset(m66592, M66592_MBW, ep->fifosel); + } +} + +static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, + const struct usb_endpoint_descriptor *desc, + u16 pipenum, int dma) +{ + if ((pipenum != 0) && dma) { + if (m66592->num_dma == 0) { + m66592->num_dma++; + ep->use_dma = 1; + ep->fifoaddr = M66592_D0FIFO; + ep->fifosel = M66592_D0FIFOSEL; + ep->fifoctr = M66592_D0FIFOCTR; + ep->fifotrn = M66592_D0FIFOTRN; + } else if (m66592->num_dma == 1) { + m66592->num_dma++; + ep->use_dma = 1; + ep->fifoaddr = M66592_D1FIFO; + ep->fifosel = M66592_D1FIFOSEL; + ep->fifoctr = M66592_D1FIFOCTR; + ep->fifotrn = M66592_D1FIFOTRN; + } else { + ep->use_dma = 0; + ep->fifoaddr = M66592_CFIFO; + ep->fifosel = M66592_CFIFOSEL; + ep->fifoctr = M66592_CFIFOCTR; + ep->fifotrn = 0; + } + } else { + ep->use_dma = 0; + ep->fifoaddr = M66592_CFIFO; + ep->fifosel = M66592_CFIFOSEL; + ep->fifoctr = M66592_CFIFOCTR; + ep->fifotrn = 0; + } + + ep->pipectr = get_pipectr_addr(pipenum); + ep->pipenum = pipenum; + ep->ep.maxpacket = desc->wMaxPacketSize; + m66592->pipenum2ep[pipenum] = ep; + m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep; + INIT_LIST_HEAD(&ep->queue); +} + +static void m66592_ep_release(struct m66592_ep *ep) +{ + struct m66592 *m66592 = ep->m66592; + u16 pipenum = ep->pipenum; + + if (pipenum == 0) + return; + + if (ep->use_dma) + m66592->num_dma--; + ep->pipenum = 0; + ep->busy = 0; + ep->use_dma = 0; +} + +static int alloc_pipe_config(struct m66592_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct m66592 *m66592 = ep->m66592; + struct m66592_pipe_info info; + int dma = 0; + int *counter; + int ret; + + ep->desc = desc; + + BUG_ON(ep->pipenum); + + switch(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + if (m66592->bulk >= M66592_MAX_NUM_BULK) { + if (m66592->isochronous >= M66592_MAX_NUM_ISOC) { + printk(KERN_ERR "bulk pipe is insufficient\n"); + return -ENODEV; + } else { + info.pipe = M66592_BASE_PIPENUM_ISOC + + m66592->isochronous; + counter = &m66592->isochronous; + } + } else { + info.pipe = M66592_BASE_PIPENUM_BULK + m66592->bulk; + counter = &m66592->bulk; + } + info.type = M66592_BULK; + dma = 1; + break; + case USB_ENDPOINT_XFER_INT: + if (m66592->interrupt >= M66592_MAX_NUM_INT) { + printk(KERN_ERR "interrupt pipe is insufficient\n"); + return -ENODEV; + } + info.pipe = M66592_BASE_PIPENUM_INT + m66592->interrupt; + info.type = M66592_INT; + counter = &m66592->interrupt; + break; + case USB_ENDPOINT_XFER_ISOC: + if (m66592->isochronous >= M66592_MAX_NUM_ISOC) { + printk(KERN_ERR "isochronous pipe is insufficient\n"); + return -ENODEV; + } + info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous; + info.type = M66592_ISO; + counter = &m66592->isochronous; + break; + default: + printk(KERN_ERR "unexpect xfer type\n"); + return -EINVAL; + } + ep->type = info.type; + + info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + info.maxpacket = desc->wMaxPacketSize; + info.interval = desc->bInterval; + if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + info.dir_in = 1; + else + info.dir_in = 0; + + ret = pipe_buffer_setting(m66592, &info); + if (ret < 0) { + printk(KERN_ERR "pipe_buffer_setting fail\n"); + return ret; + } + + (*counter)++; + if ((counter == &m66592->isochronous) && info.type == M66592_BULK) + m66592->bulk++; + + m66592_ep_setting(m66592, ep, desc, info.pipe, dma); + pipe_initialize(ep); + + return 0; +} + +static int free_pipe_config(struct m66592_ep *ep) +{ + struct m66592 *m66592 = ep->m66592; + struct m66592_pipe_info info; + + info.pipe = ep->pipenum; + info.type = ep->type; + pipe_buffer_release(m66592, &info); + m66592_ep_release(ep); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +static void pipe_irq_enable(struct m66592 *m66592, u16 pipenum) +{ + enable_irq_ready(m66592, pipenum); + enable_irq_nrdy(m66592, pipenum); +} + +static void pipe_irq_disable(struct m66592 *m66592, u16 pipenum) +{ + disable_irq_ready(m66592, pipenum); + disable_irq_nrdy(m66592, pipenum); +} + +/* if complete is true, gadget driver complete function is not call */ +static void control_end(struct m66592 *m66592, unsigned ccpl) +{ + m66592->ep[0].internal_ccpl = ccpl; + pipe_start(m66592, 0); + m66592_bset(m66592, M66592_CCPL, M66592_DCPCTR); +} + +static void start_ep0_write(struct m66592_ep *ep, struct m66592_request *req) +{ + struct m66592 *m66592 = ep->m66592; + + pipe_change(m66592, ep->pipenum); + m66592_mdfy(m66592, M66592_ISEL | M66592_PIPE0, + (M66592_ISEL | M66592_CURPIPE), + M66592_CFIFOSEL); + m66592_write(m66592, M66592_BCLR, ep->fifoctr); + if (req->req.length == 0) { + m66592_bset(m66592, M66592_BVAL, ep->fifoctr); + pipe_start(m66592, 0); + transfer_complete(ep, req, 0); + } else { + m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS); + irq_ep0_write(ep, req); + } +} + +static void start_packet_write(struct m66592_ep *ep, struct m66592_request *req) +{ + struct m66592 *m66592 = ep->m66592; + u16 tmp; + + pipe_change(m66592, ep->pipenum); + disable_irq_empty(m66592, ep->pipenum); + pipe_start(m66592, ep->pipenum); + + tmp = m66592_read(m66592, ep->fifoctr); + if (unlikely((tmp & M66592_FRDY) == 0)) + pipe_irq_enable(m66592, ep->pipenum); + else + irq_packet_write(ep, req); +} + +static void start_packet_read(struct m66592_ep *ep, struct m66592_request *req) +{ + struct m66592 *m66592 = ep->m66592; + u16 pipenum = ep->pipenum; + + if (ep->pipenum == 0) { + m66592_mdfy(m66592, M66592_PIPE0, + (M66592_ISEL | M66592_CURPIPE), + M66592_CFIFOSEL); + m66592_write(m66592, M66592_BCLR, ep->fifoctr); + pipe_start(m66592, pipenum); + pipe_irq_enable(m66592, pipenum); + } else { + if (ep->use_dma) { + m66592_bset(m66592, M66592_TRCLR, ep->fifosel); + pipe_change(m66592, pipenum); + m66592_bset(m66592, M66592_TRENB, ep->fifosel); + m66592_write(m66592, + (req->req.length + ep->ep.maxpacket - 1) / + ep->ep.maxpacket, ep->fifotrn); + } + pipe_start(m66592, pipenum); /* trigger once */ + pipe_irq_enable(m66592, pipenum); + } +} + +static void start_packet(struct m66592_ep *ep, struct m66592_request *req) +{ + if (ep->desc->bEndpointAddress & USB_DIR_IN) + start_packet_write(ep, req); + else + start_packet_read(ep, req); +} + +static void start_ep0(struct m66592_ep *ep, struct m66592_request *req) +{ + u16 ctsq; + + ctsq = m66592_read(ep->m66592, M66592_INTSTS0) & M66592_CTSQ; + + switch (ctsq) { + case M66592_CS_RDDS: + start_ep0_write(ep, req); + break; + case M66592_CS_WRDS: + start_packet_read(ep, req); + break; + + case M66592_CS_WRND: + control_end(ep->m66592, 0); + break; + default: + printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq); + break; + } +} + +static void init_controller(struct m66592 *m66592) +{ + m66592_bset(m66592, (vif & M66592_LDRV) | (endian & M66592_BIGEND), + M66592_PINCFG); + m66592_bset(m66592, M66592_HSE, M66592_SYSCFG); /* High spd */ + m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, M66592_SYSCFG); + + m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG); + m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG); + m66592_bset(m66592, M66592_USBE, M66592_SYSCFG); + + m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); + + msleep(3); + + m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG); + + msleep(1); + + m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG); + + m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1); + m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR, + M66592_DMA0CFG); +} + +static void disable_controller(struct m66592 *m66592) +{ + m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG); + udelay(1); + m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG); +} + +static void m66592_start_xclock(struct m66592 *m66592) +{ + u16 tmp; + + tmp = m66592_read(m66592, M66592_SYSCFG); + if (!(tmp & M66592_XCKE)) + m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG); +} + +/*-------------------------------------------------------------------------*/ +static void transfer_complete(struct m66592_ep *ep, + struct m66592_request *req, + int status) +{ + int restart = 0; + + if (unlikely(ep->pipenum == 0)) { + if (ep->internal_ccpl) { + ep->internal_ccpl = 0; + return; + } + } + + list_del_init(&req->queue); + if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + if (!list_empty(&ep->queue)) + restart = 1; + + if (likely(req->req.complete)) + req->req.complete(&ep->ep, &req->req); + + if (restart) { + req = list_entry(ep->queue.next, struct m66592_request, queue); + if (ep->desc) + start_packet(ep, req); + } +} + +static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req) +{ + int i; + volatile u16 tmp; + unsigned bufsize; + size_t size; + void *buf; + u16 pipenum = ep->pipenum; + struct m66592 *m66592 = ep->m66592; + + pipe_change(m66592, pipenum); + m66592_bset(m66592, M66592_ISEL, ep->fifosel); + + i = 0; + do { + tmp = m66592_read(m66592, ep->fifoctr); + if (i++ > 100000) { + printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus" + "conflict. please power off this controller."); + return; + } + ndelay(1); + } while ((tmp & M66592_FRDY) == 0); + + /* prepare parameters */ + bufsize = get_buffer_size(m66592, pipenum); + buf = req->req.buf + req->req.actual; + size = min(bufsize, req->req.length - req->req.actual); + + /* write fifo */ + if (req->req.buf) { + if (size > 0) + m66592_write_fifo(m66592, ep->fifoaddr, buf, size); + if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) + m66592_bset(m66592, M66592_BVAL, ep->fifoctr); + } + + /* update parameters */ + req->req.actual += size; + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) || + (size % ep->ep.maxpacket) || (size == 0)) { + disable_irq_ready(m66592, pipenum); + disable_irq_empty(m66592, pipenum); + } else { + disable_irq_ready(m66592, pipenum); + enable_irq_empty(m66592, pipenum); + } + pipe_start(m66592, pipenum); +} + +static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req) +{ + u16 tmp; + unsigned bufsize; + size_t size; + void *buf; + u16 pipenum = ep->pipenum; + struct m66592 *m66592 = ep->m66592; + + pipe_change(m66592, pipenum); + tmp = m66592_read(m66592, ep->fifoctr); + if (unlikely((tmp & M66592_FRDY) == 0)) { + pipe_stop(m66592, pipenum); + pipe_irq_disable(m66592, pipenum); + printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum); + return; + } + + /* prepare parameters */ + bufsize = get_buffer_size(m66592, pipenum); + buf = req->req.buf + req->req.actual; + size = min(bufsize, req->req.length - req->req.actual); + + /* write fifo */ + if (req->req.buf) { + m66592_write_fifo(m66592, ep->fifoaddr, buf, size); + if ((size == 0) || ((size % ep->ep.maxpacket) != 0) || + ((bufsize != ep->ep.maxpacket) && (bufsize > size))) + m66592_bset(m66592, M66592_BVAL, ep->fifoctr); + } + + /* update parameters */ + req->req.actual += size; + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) || + (size % ep->ep.maxpacket) || (size == 0)) { + disable_irq_ready(m66592, pipenum); + enable_irq_empty(m66592, pipenum); + } else { + disable_irq_empty(m66592, pipenum); + pipe_irq_enable(m66592, pipenum); + } +} + +static void irq_packet_read(struct m66592_ep *ep, struct m66592_request *req) +{ + u16 tmp; + int rcv_len, bufsize, req_len; + int size; + void *buf; + u16 pipenum = ep->pipenum; + struct m66592 *m66592 = ep->m66592; + int finish = 0; + + pipe_change(m66592, pipenum); + tmp = m66592_read(m66592, ep->fifoctr); + if (unlikely((tmp & M66592_FRDY) == 0)) { + req->req.status = -EPIPE; + pipe_stop(m66592, pipenum); + pipe_irq_disable(m66592, pipenum); + printk(KERN_ERR "read fifo not ready"); + return; + } + + /* prepare parameters */ + rcv_len = tmp & M66592_DTLN; + bufsize = get_buffer_size(m66592, pipenum); + + buf = req->req.buf + req->req.actual; + req_len = req->req.length - req->req.actual; + if (rcv_len < bufsize) + size = min(rcv_len, req_len); + else + size = min(bufsize, req_len); + + /* update parameters */ + req->req.actual += size; + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) || + (size % ep->ep.maxpacket) || (size == 0)) { + pipe_stop(m66592, pipenum); + pipe_irq_disable(m66592, pipenum); + finish = 1; + } + + /* read fifo */ + if (req->req.buf) { + if (size == 0) + m66592_write(m66592, M66592_BCLR, ep->fifoctr); + else + m66592_read_fifo(m66592, ep->fifoaddr, buf, size); + } + + if ((ep->pipenum != 0) && finish) + transfer_complete(ep, req, 0); +} + +static void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb) +{ + u16 check; + u16 pipenum; + struct m66592_ep *ep; + struct m66592_request *req; + + if ((status & M66592_BRDY0) && (enb & M66592_BRDY0)) { + m66592_write(m66592, ~M66592_BRDY0, M66592_BRDYSTS); + m66592_mdfy(m66592, M66592_PIPE0, M66592_CURPIPE, + M66592_CFIFOSEL); + + ep = &m66592->ep[0]; + req = list_entry(ep->queue.next, struct m66592_request, queue); + irq_packet_read(ep, req); + } else { + for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if ((status & check) && (enb & check)) { + m66592_write(m66592, ~check, M66592_BRDYSTS); + ep = m66592->pipenum2ep[pipenum]; + req = list_entry(ep->queue.next, + struct m66592_request, queue); + if (ep->desc->bEndpointAddress & USB_DIR_IN) + irq_packet_write(ep, req); + else + irq_packet_read(ep, req); + } + } + } +} + +static void irq_pipe_empty(struct m66592 *m66592, u16 status, u16 enb) +{ + u16 tmp; + u16 check; + u16 pipenum; + struct m66592_ep *ep; + struct m66592_request *req; + + if ((status & M66592_BEMP0) && (enb & M66592_BEMP0)) { + m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS); + + ep = &m66592->ep[0]; + req = list_entry(ep->queue.next, struct m66592_request, queue); + irq_ep0_write(ep, req); + } else { + for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if ((status & check) && (enb & check)) { + m66592_write(m66592, ~check, M66592_BEMPSTS); + tmp = control_reg_get(m66592, pipenum); + if ((tmp & M66592_INBUFM) == 0) { + disable_irq_empty(m66592, pipenum); + pipe_irq_disable(m66592, pipenum); + pipe_stop(m66592, pipenum); + ep = m66592->pipenum2ep[pipenum]; + req = list_entry(ep->queue.next, + struct m66592_request, + queue); + if (!list_empty(&ep->queue)) + transfer_complete(ep, req, 0); + } + } + } + } +} + +static void get_status(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) +{ + struct m66592_ep *ep; + u16 pid; + u16 status = 0; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = 1; /* selfpower */ + break; + case USB_RECIP_INTERFACE: + status = 0; + break; + case USB_RECIP_ENDPOINT: + ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK]; + pid = control_reg_get_pid(m66592, ep->pipenum); + if (pid == M66592_PID_STALL) + status = 1; + else + status = 0; + break; + default: + pipe_stall(m66592, 0); + return; /* exit */ + } + + *m66592->ep0_buf = status; + m66592->ep0_req->buf = m66592->ep0_buf; + m66592->ep0_req->length = 2; + m66592_queue(m66592->gadget.ep0, m66592->ep0_req, GFP_KERNEL); +} + +static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) +{ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + control_end(m66592, 1); + break; + case USB_RECIP_INTERFACE: + control_end(m66592, 1); + break; + case USB_RECIP_ENDPOINT: { + struct m66592_ep *ep; + struct m66592_request *req; + + ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK]; + pipe_stop(m66592, ep->pipenum); + control_reg_sqclr(m66592, ep->pipenum); + + control_end(m66592, 1); + + req = list_entry(ep->queue.next, + struct m66592_request, queue); + if (ep->busy) { + ep->busy = 0; + if (list_empty(&ep->queue)) + break; + start_packet(ep, req); + } else if (!list_empty(&ep->queue)) + pipe_start(m66592, ep->pipenum); + } + break; + default: + pipe_stall(m66592, 0); + break; + } +} + +static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) +{ + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + control_end(m66592, 1); + break; + case USB_RECIP_INTERFACE: + control_end(m66592, 1); + break; + case USB_RECIP_ENDPOINT: { + struct m66592_ep *ep; + + ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK]; + pipe_stall(m66592, ep->pipenum); + + control_end(m66592, 1); + } + break; + default: + pipe_stall(m66592, 0); + break; + } +} + +/* if return value is true, call class driver's setup() */ +static int setup_packet(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) +{ + u16 *p = (u16 *)ctrl; + unsigned long offset = M66592_USBREQ; + int i, ret = 0; + + /* read fifo */ + m66592_write(m66592, ~M66592_VALID, M66592_INTSTS0); + + for (i = 0; i < 4; i++) + p[i] = m66592_read(m66592, offset + i*2); + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + get_status(m66592, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + clear_feature(m66592, ctrl); + break; + case USB_REQ_SET_FEATURE: + set_feature(m66592, ctrl); + break; + default: + ret = 1; + break; + } + } else + ret = 1; + return ret; +} + +static void m66592_update_usb_speed(struct m66592 *m66592) +{ + u16 speed = get_usb_speed(m66592); + + switch (speed) { + case M66592_HSMODE: + m66592->gadget.speed = USB_SPEED_HIGH; + break; + case M66592_FSMODE: + m66592->gadget.speed = USB_SPEED_FULL; + break; + default: + m66592->gadget.speed = USB_SPEED_UNKNOWN; + printk(KERN_ERR "USB speed unknown\n"); + } +} + +static void irq_device_state(struct m66592 *m66592) +{ + u16 dvsq; + + dvsq = m66592_read(m66592, M66592_INTSTS0) & M66592_DVSQ; + m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0); + + if (dvsq == M66592_DS_DFLT) { /* bus reset */ + m66592->driver->disconnect(&m66592->gadget); + m66592_update_usb_speed(m66592); + } + if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG) + m66592_update_usb_speed(m66592); + if ((dvsq == M66592_DS_CNFG || dvsq == M66592_DS_ADDS) && + m66592->gadget.speed == USB_SPEED_UNKNOWN) + m66592_update_usb_speed(m66592); + + m66592->old_dvsq = dvsq; +} + +static void irq_control_stage(struct m66592 *m66592) +{ + struct usb_ctrlrequest ctrl; + u16 ctsq; + + ctsq = m66592_read(m66592, M66592_INTSTS0) & M66592_CTSQ; + m66592_write(m66592, ~M66592_CTRT, M66592_INTSTS0); + + switch (ctsq) { + case M66592_CS_IDST: { + struct m66592_ep *ep; + struct m66592_request *req; + ep = &m66592->ep[0]; + req = list_entry(ep->queue.next, struct m66592_request, queue); + transfer_complete(ep, req, 0); + } + break; + + case M66592_CS_RDDS: + case M66592_CS_WRDS: + case M66592_CS_WRND: + if (setup_packet(m66592, &ctrl)) { + if (m66592->driver->setup(&m66592->gadget, &ctrl) < 0) + pipe_stall(m66592, 0); + } + break; + case M66592_CS_RDSS: + case M66592_CS_WRSS: + control_end(m66592, 0); + break; + default: + printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq); + break; + } +} + +static irqreturn_t m66592_irq(int irq, void *_m66592) +{ + struct m66592 *m66592 = _m66592; + u16 intsts0; + u16 intenb0; + u16 brdysts, nrdysts, bempsts; + u16 brdyenb, nrdyenb, bempenb; + u16 savepipe; + u16 mask0; + + intsts0 = m66592_read(m66592, M66592_INTSTS0); + intenb0 = m66592_read(m66592, M66592_INTENB0); + + savepipe = m66592_read(m66592, M66592_CFIFOSEL); + + mask0 = intsts0 & intenb0; + if (mask0) { + brdysts = m66592_read(m66592, M66592_BRDYSTS); + nrdysts = m66592_read(m66592, M66592_NRDYSTS); + bempsts = m66592_read(m66592, M66592_BEMPSTS); + brdyenb = m66592_read(m66592, M66592_BRDYENB); + nrdyenb = m66592_read(m66592, M66592_NRDYENB); + bempenb = m66592_read(m66592, M66592_BEMPENB); + + if (mask0 & M66592_VBINT) { + m66592_write(m66592, (u16)~M66592_VBINT, + M66592_INTSTS0); + m66592_start_xclock(m66592); + + /* start vbus sampling */ + m66592->old_vbus = m66592_read(m66592, M66592_INTSTS0) + & M66592_VBSTS; + m66592->scount = M66592_MAX_SAMPLING; + + mod_timer(&m66592->timer, + jiffies + msecs_to_jiffies(50)); + } + if (intsts0 & M66592_DVSQ) + irq_device_state(m66592); + + if ((intsts0 & M66592_BRDY) && (intenb0 & M66592_BRDYE) && + (brdysts & brdyenb)) { + irq_pipe_ready(m66592, brdysts, brdyenb); + } + if ((intsts0 & M66592_BEMP) && (intenb0 & M66592_BEMPE) && + (bempsts & bempenb)) { + irq_pipe_empty(m66592, bempsts, bempenb); + } + + if (intsts0 & M66592_CTRT) + irq_control_stage(m66592); + } + + m66592_write(m66592, savepipe, M66592_CFIFOSEL); + + return IRQ_HANDLED; +} + +static void m66592_timer(unsigned long _m66592) +{ + struct m66592 *m66592 = (struct m66592 *)_m66592; + unsigned long flags; + u16 tmp; + + spin_lock_irqsave(&m66592->lock, flags); + tmp = m66592_read(m66592, M66592_SYSCFG); + if (!(tmp & M66592_RCKE)) { + m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG); + udelay(10); + m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG); + } + if (m66592->scount > 0) { + tmp = m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS; + if (tmp == m66592->old_vbus) { + m66592->scount--; + if (m66592->scount == 0) { + if (tmp == M66592_VBSTS) + m66592_usb_connect(m66592); + else + m66592_usb_disconnect(m66592); + } else { + mod_timer(&m66592->timer, + jiffies + msecs_to_jiffies(50)); + } + } else { + m66592->scount = M66592_MAX_SAMPLING; + m66592->old_vbus = tmp; + mod_timer(&m66592->timer, + jiffies + msecs_to_jiffies(50)); + } + } + spin_unlock_irqrestore(&m66592->lock, flags); +} + +/*-------------------------------------------------------------------------*/ +static int m66592_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct m66592_ep *ep; + + ep = container_of(_ep, struct m66592_ep, ep); + return alloc_pipe_config(ep, desc); +} + +static int m66592_disable(struct usb_ep *_ep) +{ + struct m66592_ep *ep; + struct m66592_request *req; + unsigned long flags; + + ep = container_of(_ep, struct m66592_ep, ep); + BUG_ON(!ep); + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct m66592_request, queue); + spin_lock_irqsave(&ep->m66592->lock, flags); + transfer_complete(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->m66592->lock, flags); + } + + pipe_irq_disable(ep->m66592, ep->pipenum); + return free_pipe_config(ep); +} + +static struct usb_request *m66592_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct m66592_request *req; + + req = kzalloc(sizeof(struct m66592_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void m66592_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct m66592_request *req; + + req = container_of(_req, struct m66592_request, req); + kfree(req); +} + +static void *m66592_alloc_buffer(struct usb_ep *_ep, unsigned bytes, + dma_addr_t *dma, gfp_t gfp_flags) +{ + void *buf; + + buf = kzalloc(bytes, gfp_flags); + if (dma) + *dma = virt_to_bus(buf); + + return buf; +} + +static void m66592_free_buffer(struct usb_ep *_ep, void *buf, + dma_addr_t dma, unsigned bytes) +{ + kfree(buf); +} + +static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct m66592_ep *ep; + struct m66592_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct m66592_ep, ep); + req = container_of(_req, struct m66592_request, req); + + if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->m66592->lock, flags); + + if (list_empty(&ep->queue)) + request = 1; + + list_add_tail(&req->queue, &ep->queue); + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (ep->desc == 0) /* control */ + start_ep0(ep, req); + else { + if (request && !ep->busy) + start_packet(ep, req); + } + + spin_unlock_irqrestore(&ep->m66592->lock, flags); + + return 0; +} + +static int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct m66592_ep *ep; + struct m66592_request *req; + unsigned long flags; + + ep = container_of(_ep, struct m66592_ep, ep); + req = container_of(_req, struct m66592_request, req); + + spin_lock_irqsave(&ep->m66592->lock, flags); + if (!list_empty(&ep->queue)) + transfer_complete(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->m66592->lock, flags); + + return 0; +} + +static int m66592_set_halt(struct usb_ep *_ep, int value) +{ + struct m66592_ep *ep; + struct m66592_request *req; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct m66592_ep, ep); + req = list_entry(ep->queue.next, struct m66592_request, queue); + + spin_lock_irqsave(&ep->m66592->lock, flags); + if (!list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + if (value) { + ep->busy = 1; + pipe_stall(ep->m66592, ep->pipenum); + } else { + ep->busy = 0; + pipe_stop(ep->m66592, ep->pipenum); + } + +out: + spin_unlock_irqrestore(&ep->m66592->lock, flags); + return ret; +} + +static int m66592_fifo_status(struct usb_ep *_ep) +{ + return -EOPNOTSUPP; +} + +static void m66592_fifo_flush(struct usb_ep *_ep) +{ + struct m66592_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct m66592_ep, ep); + spin_lock_irqsave(&ep->m66592->lock, flags); + if (list_empty(&ep->queue) && !ep->busy) { + pipe_stop(ep->m66592, ep->pipenum); + m66592_bclr(ep->m66592, M66592_BCLR, ep->fifoctr); + } + spin_unlock_irqrestore(&ep->m66592->lock, flags); +} + +static struct usb_ep_ops m66592_ep_ops = { + .enable = m66592_enable, + .disable = m66592_disable, + + .alloc_request = m66592_alloc_request, + .free_request = m66592_free_request, + + .alloc_buffer = m66592_alloc_buffer, + .free_buffer = m66592_free_buffer, + + .queue = m66592_queue, + .dequeue = m66592_dequeue, + + .set_halt = m66592_set_halt, + .fifo_status = m66592_fifo_status, + .fifo_flush = m66592_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ +static struct m66592 *the_controller; + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct m66592 *m66592 = the_controller; + int retval; + + if (!driver || + driver->speed != USB_SPEED_HIGH || + !driver->bind || + !driver->unbind || + !driver->setup) + return -EINVAL; + if (!m66592) + return -ENODEV; + if (m66592->driver) + return -EBUSY; + + /* hook up the driver */ + driver->driver.bus = NULL; + m66592->driver = driver; + m66592->gadget.dev.driver = &driver->driver; + + retval = device_add(&m66592->gadget.dev); + if (retval) { + printk(KERN_ERR "device_add error (%d)\n", retval); + goto error; + } + + retval = driver->bind (&m66592->gadget); + if (retval) { + printk(KERN_ERR "bind to driver error (%d)\n", retval); + device_del(&m66592->gadget.dev); + goto error; + } + + m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); + if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) { + m66592_start_xclock(m66592); + /* start vbus sampling */ + m66592->old_vbus = m66592_read(m66592, + M66592_INTSTS0) & M66592_VBSTS; + m66592->scount = M66592_MAX_SAMPLING; + mod_timer(&m66592->timer, + jiffies + msecs_to_jiffies(50)); + } + + return 0; + +error: + m66592->driver = NULL; + m66592->gadget.dev.driver = NULL; + + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct m66592 *m66592 = the_controller; + unsigned long flags; + + spin_lock_irqsave(&m66592->lock, flags); + if (m66592->gadget.speed != USB_SPEED_UNKNOWN) + m66592_usb_disconnect(m66592); + spin_unlock_irqrestore(&m66592->lock, flags); + + m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0); + + driver->unbind(&m66592->gadget); + + init_controller(m66592); + disable_controller(m66592); + + device_del(&m66592->gadget.dev); + m66592->driver = NULL; + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ +static int m66592_get_frame(struct usb_gadget *_gadget) +{ + struct m66592 *m66592 = gadget_to_m66592(_gadget); + return m66592_read(m66592, M66592_FRMNUM) & 0x03FF; +} + +static struct usb_gadget_ops m66592_gadget_ops = { + .get_frame = m66592_get_frame, +}; + +#if defined(CONFIG_PM) +static int m66592_suspend(struct platform_device *pdev, pm_message_t state) +{ + pdev->dev.power.power_state = state; + return 0; +} + +static int m66592_resume(struct platform_device *pdev) +{ + pdev->dev.power.power_state = PMSG_ON; + return 0; +} +#else /* if defined(CONFIG_PM) */ +#define m66592_suspend NULL +#define m66592_resume NULL +#endif + +static int __init_or_module m66592_remove(struct platform_device *pdev) +{ + struct m66592 *m66592 = dev_get_drvdata(&pdev->dev); + + del_timer_sync(&m66592->timer); + iounmap(m66592->reg); + free_irq(platform_get_irq(pdev, 0), m66592); + kfree(m66592); + return 0; +} + +#define resource_len(r) (((r)->end - (r)->start) + 1) +static int __init m66592_probe(struct platform_device *pdev) +{ + struct resource *res = NULL; + int irq = -1; + void __iomem *reg = NULL; + struct m66592 *m66592 = NULL; + int ret = 0; + int i; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (char *)udc_name); + if (!res) { + ret = -ENODEV; + printk(KERN_ERR "platform_get_resource_byname error.\n"); + goto clean_up; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = -ENODEV; + printk(KERN_ERR "platform_get_irq error.\n"); + goto clean_up; + } + + reg = ioremap(res->start, resource_len(res)); + if (reg == NULL) { + ret = -ENOMEM; + printk(KERN_ERR "ioremap error.\n"); + goto clean_up; + } + + /* initialize ucd */ + m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL); + if (m66592 == NULL) { + printk(KERN_ERR "kzalloc error\n"); + goto clean_up; + } + + spin_lock_init(&m66592->lock); + dev_set_drvdata(&pdev->dev, m66592); + + m66592->gadget.ops = &m66592_gadget_ops; + device_initialize(&m66592->gadget.dev); + strcpy(m66592->gadget.dev.bus_id, "gadget"); + m66592->gadget.is_dualspeed = 1; + m66592->gadget.dev.parent = &pdev->dev; + m66592->gadget.dev.dma_mask = pdev->dev.dma_mask; + m66592->gadget.dev.release = pdev->dev.release; + m66592->gadget.name = udc_name; + + init_timer(&m66592->timer); + m66592->timer.function = m66592_timer; + m66592->timer.data = (unsigned long)m66592; + m66592->reg = reg; + + m66592->bi_bufnum = M66592_BASE_BUFNUM; + + ret = request_irq(irq, m66592_irq, IRQF_DISABLED | IRQF_SHARED, + udc_name, m66592); + if (ret < 0) { + printk(KERN_ERR "request_irq error (%d)\n", ret); + goto clean_up; + } + + INIT_LIST_HEAD(&m66592->gadget.ep_list); + m66592->gadget.ep0 = &m66592->ep[0].ep; + INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list); + for (i = 0; i < M66592_MAX_NUM_PIPE; i++) { + struct m66592_ep *ep = &m66592->ep[i]; + + if (i != 0) { + INIT_LIST_HEAD(&m66592->ep[i].ep.ep_list); + list_add_tail(&m66592->ep[i].ep.ep_list, + &m66592->gadget.ep_list); + } + ep->m66592 = m66592; + INIT_LIST_HEAD(&ep->queue); + ep->ep.name = m66592_ep_name[i]; + ep->ep.ops = &m66592_ep_ops; + ep->ep.maxpacket = 512; + } + m66592->ep[0].ep.maxpacket = 64; + m66592->ep[0].pipenum = 0; + m66592->ep[0].fifoaddr = M66592_CFIFO; + m66592->ep[0].fifosel = M66592_CFIFOSEL; + m66592->ep[0].fifoctr = M66592_CFIFOCTR; + m66592->ep[0].fifotrn = 0; + m66592->ep[0].pipectr = get_pipectr_addr(0); + m66592->pipenum2ep[0] = &m66592->ep[0]; + m66592->epaddr2ep[0] = &m66592->ep[0]; + + the_controller = m66592; + + m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL); + if (m66592->ep0_req == NULL) + goto clean_up; + m66592->ep0_buf = m66592_alloc_buffer(&m66592->ep[0].ep, 2, NULL, + GFP_KERNEL); + if (m66592->ep0_buf == NULL) + goto clean_up; + + init_controller(m66592); + + printk("driver %s, %s\n", udc_name, DRIVER_VERSION); + return 0; + +clean_up: + if (m66592) { + if (m66592->ep0_req) + m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req); + kfree(m66592); + } + if (reg) + iounmap(reg); + + return ret; +} + +/*-------------------------------------------------------------------------*/ +static struct platform_driver m66592_driver = { + .probe = m66592_probe, + .remove = m66592_remove, + .suspend = m66592_suspend, + .resume = m66592_resume, + .driver = { + .name = (char *) udc_name, + }, +}; + +static int __init m66592_udc_init(void) +{ + return platform_driver_register(&m66592_driver); +} +module_init(m66592_udc_init); + +static void __exit m66592_udc_cleanup(void) +{ + platform_driver_unregister(&m66592_driver); +} +module_exit(m66592_udc_cleanup); + diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h new file mode 100644 index 0000000..26b54f8 --- /dev/null +++ b/drivers/usb/gadget/m66592-udc.h @@ -0,0 +1,577 @@ +/* + * M66592 UDC (USB gadget) + * + * Copyright (C) 2006-2007 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __M66592_UDC_H__ +#define __M66592_UDC_H__ + +#define M66592_SYSCFG 0x00 +#define M66592_XTAL 0xC000 /* b15-14: Crystal selection */ +#define M66592_XTAL48 0x8000 /* 48MHz */ +#define M66592_XTAL24 0x4000 /* 24MHz */ +#define M66592_XTAL12 0x0000 /* 12MHz */ +#define M66592_XCKE 0x2000 /* b13: External clock enable */ +#define M66592_RCKE 0x1000 /* b12: Register clock enable */ +#define M66592_PLLC 0x0800 /* b11: PLL control */ +#define M66592_SCKE 0x0400 /* b10: USB clock enable */ +#define M66592_ATCKM 0x0100 /* b8: Automatic supply functional enable */ +#define M66592_HSE 0x0080 /* b7: Hi-speed enable */ +#define M66592_DCFM 0x0040 /* b6: Controller function select */ +#define M66592_DMRPD 0x0020 /* b5: D- pull down control */ +#define M66592_DPRPU 0x0010 /* b4: D+ pull up control */ +#define M66592_FSRPC 0x0004 /* b2: Full-speed receiver enable */ +#define M66592_PCUT 0x0002 /* b1: Low power sleep enable */ +#define M66592_USBE 0x0001 /* b0: USB module operation enable */ + +#define M66592_SYSSTS 0x02 +#define M66592_LNST 0x0003 /* b1-0: D+, D- line status */ +#define M66592_SE1 0x0003 /* SE1 */ +#define M66592_KSTS 0x0002 /* K State */ +#define M66592_JSTS 0x0001 /* J State */ +#define M66592_SE0 0x0000 /* SE0 */ + +#define M66592_DVSTCTR 0x04 +#define M66592_WKUP 0x0100 /* b8: Remote wakeup */ +#define M66592_RWUPE 0x0080 /* b7: Remote wakeup sense */ +#define M66592_USBRST 0x0040 /* b6: USB reset enable */ +#define M66592_RESUME 0x0020 /* b5: Resume enable */ +#define M66592_UACT 0x0010 /* b4: USB bus enable */ +#define M66592_RHST 0x0003 /* b1-0: Reset handshake status */ +#define M66592_HSMODE 0x0003 /* Hi-Speed mode */ +#define M66592_FSMODE 0x0002 /* Full-Speed mode */ +#define M66592_HSPROC 0x0001 /* HS handshake is processing */ + +#define M66592_TESTMODE 0x06 +#define M66592_UTST 0x000F /* b4-0: Test select */ +#define M66592_H_TST_PACKET 0x000C /* HOST TEST Packet */ +#define M66592_H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ +#define M66592_H_TST_K 0x000A /* HOST TEST K */ +#define M66592_H_TST_J 0x0009 /* HOST TEST J */ +#define M66592_H_TST_NORMAL 0x0000 /* HOST Normal Mode */ +#define M66592_P_TST_PACKET 0x0004 /* PERI TEST Packet */ +#define M66592_P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ +#define M66592_P_TST_K 0x0002 /* PERI TEST K */ +#define M66592_P_TST_J 0x0001 /* PERI TEST J */ +#define M66592_P_TST_NORMAL 0x0000 /* PERI Normal Mode */ + +#define M66592_PINCFG 0x0A +#define M66592_LDRV 0x8000 /* b15: Drive Current Adjust */ +#define M66592_BIGEND 0x0100 /* b8: Big endian mode */ + +#define M66592_DMA0CFG 0x0C +#define M66592_DMA1CFG 0x0E +#define M66592_DREQA 0x4000 /* b14: Dreq active select */ +#define M66592_BURST 0x2000 /* b13: Burst mode */ +#define M66592_DACKA 0x0400 /* b10: Dack active select */ +#define M66592_DFORM 0x0380 /* b9-7: DMA mode select */ +#define M66592_CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ +#define M66592_CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ +#define M66592_CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ +#define M66592_SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ +#define M66592_SPLIT_DACK_DSTB 0x0300 /* DACK + DSTB0 mode (SPLIT bus) */ +#define M66592_DENDA 0x0040 /* b6: Dend active select */ +#define M66592_PKTM 0x0020 /* b5: Packet mode */ +#define M66592_DENDE 0x0010 /* b4: Dend enable */ +#define M66592_OBUS 0x0004 /* b2: OUTbus mode */ + +#define M66592_CFIFO 0x10 +#define M66592_D0FIFO 0x14 +#define M66592_D1FIFO 0x18 + +#define M66592_CFIFOSEL 0x1E +#define M66592_D0FIFOSEL 0x24 +#define M66592_D1FIFOSEL 0x2A +#define M66592_RCNT 0x8000 /* b15: Read count mode */ +#define M66592_REW 0x4000 /* b14: Buffer rewind */ +#define M66592_DCLRM 0x2000 /* b13: DMA buffer clear mode */ +#define M66592_DREQE 0x1000 /* b12: DREQ output enable */ +#define M66592_MBW 0x0400 /* b10: Maximum bit width for FIFO access */ +#define M66592_MBW_8 0x0000 /* 8bit */ +#define M66592_MBW_16 0x0400 /* 16bit */ +#define M66592_TRENB 0x0200 /* b9: Transaction counter enable */ +#define M66592_TRCLR 0x0100 /* b8: Transaction counter clear */ +#define M66592_DEZPM 0x0080 /* b7: Zero-length packet additional mode */ +#define M66592_ISEL 0x0020 /* b5: DCP FIFO port direction select */ +#define M66592_CURPIPE 0x0007 /* b2-0: PIPE select */ + +#define M66592_CFIFOCTR 0x20 +#define M66592_D0FIFOCTR 0x26 +#define M66592_D1FIFOCTR 0x2c +#define M66592_BVAL 0x8000 /* b15: Buffer valid flag */ +#define M66592_BCLR 0x4000 /* b14: Buffer clear */ +#define M66592_FRDY 0x2000 /* b13: FIFO ready */ +#define M66592_DTLN 0x0FFF /* b11-0: FIFO received data length */ + +#define M66592_CFIFOSIE 0x22 +#define M66592_TGL 0x8000 /* b15: Buffer toggle */ +#define M66592_SCLR 0x4000 /* b14: Buffer clear */ +#define M66592_SBUSY 0x2000 /* b13: SIE_FIFO busy */ + +#define M66592_D0FIFOTRN 0x28 +#define M66592_D1FIFOTRN 0x2E +#define M66592_TRNCNT 0xFFFF /* b15-0: Transaction counter */ + +#define M66592_INTENB0 0x30 +#define M66592_VBSE 0x8000 /* b15: VBUS interrupt */ +#define M66592_RSME 0x4000 /* b14: Resume interrupt */ +#define M66592_SOFE 0x2000 /* b13: Frame update interrupt */ +#define M66592_DVSE 0x1000 /* b12: Device state transition interrupt */ +#define M66592_CTRE 0x0800 /* b11: Control transfer stage transition interrupt */ +#define M66592_BEMPE 0x0400 /* b10: Buffer empty interrupt */ +#define M66592_NRDYE 0x0200 /* b9: Buffer not ready interrupt */ +#define M66592_BRDYE 0x0100 /* b8: Buffer ready interrupt */ +#define M66592_URST 0x0080 /* b7: USB reset detected interrupt */ +#define M66592_SADR 0x0040 /* b6: Set address executed interrupt */ +#define M66592_SCFG 0x0020 /* b5: Set configuration executed interrupt */ +#define M66592_SUSP 0x0010 /* b4: Suspend detected interrupt */ +#define M66592_WDST 0x0008 /* b3: Control write data stage completed interrupt */ +#define M66592_RDST 0x0004 /* b2: Control read data stage completed interrupt */ +#define M66592_CMPL 0x0002 /* b1: Control transfer complete interrupt */ +#define M66592_SERR 0x0001 /* b0: Sequence error interrupt */ + +#define M66592_INTENB1 0x32 +#define M66592_BCHGE 0x4000 /* b14: USB us chenge interrupt */ +#define M66592_DTCHE 0x1000 /* b12: Detach sense interrupt */ +#define M66592_SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ +#define M66592_SACKE 0x0010 /* b4: SETUP ACK interrupt */ +#define M66592_BRDYM 0x0004 /* b2: BRDY clear timing */ +#define M66592_INTL 0x0002 /* b1: Interrupt sense select */ +#define M66592_PCSE 0x0001 /* b0: PCUT enable by CS assert */ + +#define M66592_BRDYENB 0x36 +#define M66592_BRDYSTS 0x46 +#define M66592_BRDY7 0x0080 /* b7: PIPE7 */ +#define M66592_BRDY6 0x0040 /* b6: PIPE6 */ +#define M66592_BRDY5 0x0020 /* b5: PIPE5 */ +#define M66592_BRDY4 0x0010 /* b4: PIPE4 */ +#define M66592_BRDY3 0x0008 /* b3: PIPE3 */ +#define M66592_BRDY2 0x0004 /* b2: PIPE2 */ +#define M66592_BRDY1 0x0002 /* b1: PIPE1 */ +#define M66592_BRDY0 0x0001 /* b1: PIPE0 */ + +#define M66592_NRDYENB 0x38 +#define M66592_NRDYSTS 0x48 +#define M66592_NRDY7 0x0080 /* b7: PIPE7 */ +#define M66592_NRDY6 0x0040 /* b6: PIPE6 */ +#define M66592_NRDY5 0x0020 /* b5: PIPE5 */ +#define M66592_NRDY4 0x0010 /* b4: PIPE4 */ +#define M66592_NRDY3 0x0008 /* b3: PIPE3 */ +#define M66592_NRDY2 0x0004 /* b2: PIPE2 */ +#define M66592_NRDY1 0x0002 /* b1: PIPE1 */ +#define M66592_NRDY0 0x0001 /* b1: PIPE0 */ + +#define M66592_BEMPENB 0x3A +#define M66592_BEMPSTS 0x4A +#define M66592_BEMP7 0x0080 /* b7: PIPE7 */ +#define M66592_BEMP6 0x0040 /* b6: PIPE6 */ +#define M66592_BEMP5 0x0020 /* b5: PIPE5 */ +#define M66592_BEMP4 0x0010 /* b4: PIPE4 */ +#define M66592_BEMP3 0x0008 /* b3: PIPE3 */ +#define M66592_BEMP2 0x0004 /* b2: PIPE2 */ +#define M66592_BEMP1 0x0002 /* b1: PIPE1 */ +#define M66592_BEMP0 0x0001 /* b0: PIPE0 */ + +#define M66592_SOFCFG 0x3C +#define M66592_SOFM 0x000C /* b3-2: SOF palse mode */ +#define M66592_SOF_125US 0x0008 /* SOF OUT 125us uFrame Signal */ +#define M66592_SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ +#define M66592_SOF_DISABLE 0x0000 /* SOF OUT Disable */ + +#define M66592_INTSTS0 0x40 +#define M66592_VBINT 0x8000 /* b15: VBUS interrupt */ +#define M66592_RESM 0x4000 /* b14: Resume interrupt */ +#define M66592_SOFR 0x2000 /* b13: SOF frame update interrupt */ +#define M66592_DVST 0x1000 /* b12: Device state transition interrupt */ +#define M66592_CTRT 0x0800 /* b11: Control transfer stage transition interrupt */ +#define M66592_BEMP 0x0400 /* b10: Buffer empty interrupt */ +#define M66592_NRDY 0x0200 /* b9: Buffer not ready interrupt */ +#define M66592_BRDY 0x0100 /* b8: Buffer ready interrupt */ +#define M66592_VBSTS 0x0080 /* b7: VBUS input port */ +#define M66592_DVSQ 0x0070 /* b6-4: Device state */ +#define M66592_DS_SPD_CNFG 0x0070 /* Suspend Configured */ +#define M66592_DS_SPD_ADDR 0x0060 /* Suspend Address */ +#define M66592_DS_SPD_DFLT 0x0050 /* Suspend Default */ +#define M66592_DS_SPD_POWR 0x0040 /* Suspend Powered */ +#define M66592_DS_SUSP 0x0040 /* Suspend */ +#define M66592_DS_CNFG 0x0030 /* Configured */ +#define M66592_DS_ADDS 0x0020 /* Address */ +#define M66592_DS_DFLT 0x0010 /* Default */ +#define M66592_DS_POWR 0x0000 /* Powered */ +#define M66592_DVSQS 0x0030 /* b5-4: Device state */ +#define M66592_VALID 0x0008 /* b3: Setup packet detected flag */ +#define M66592_CTSQ 0x0007 /* b2-0: Control transfer stage */ +#define M66592_CS_SQER 0x0006 /* Sequence error */ +#define M66592_CS_WRND 0x0005 /* Control write nodata status stage */ +#define M66592_CS_WRSS 0x0004 /* Control write status stage */ +#define M66592_CS_WRDS 0x0003 /* Control write data stage */ +#define M66592_CS_RDSS 0x0002 /* Control read status stage */ +#define M66592_CS_RDDS 0x0001 /* Control read data stage */ +#define M66592_CS_IDST 0x0000 /* Idle or setup stage */ + +#define M66592_INTSTS1 0x42 +#define M66592_BCHG 0x4000 /* b14: USB bus chenge interrupt */ +#define M66592_DTCH 0x1000 /* b12: Detach sense interrupt */ +#define M66592_SIGN 0x0020 /* b5: SETUP IGNORE interrupt */ +#define M66592_SACK 0x0010 /* b4: SETUP ACK interrupt */ + +#define M66592_FRMNUM 0x4C +#define M66592_OVRN 0x8000 /* b15: Overrun error */ +#define M66592_CRCE 0x4000 /* b14: Received data error */ +#define M66592_SOFRM 0x0800 /* b11: SOF output mode */ +#define M66592_FRNM 0x07FF /* b10-0: Frame number */ + +#define M66592_UFRMNUM 0x4E +#define M66592_UFRNM 0x0007 /* b2-0: Micro frame number */ + +#define M66592_RECOVER 0x50 +#define M66592_STSRECOV 0x0700 /* Status recovery */ +#define M66592_STSR_HI 0x0400 /* FULL(0) or HI(1) Speed */ +#define M66592_STSR_DEFAULT 0x0100 /* Default state */ +#define M66592_STSR_ADDRESS 0x0200 /* Address state */ +#define M66592_STSR_CONFIG 0x0300 /* Configured state */ +#define M66592_USBADDR 0x007F /* b6-0: USB address */ + +#define M66592_USBREQ 0x54 +#define M66592_bRequest 0xFF00 /* b15-8: bRequest */ +#define M66592_GET_STATUS 0x0000 +#define M66592_CLEAR_FEATURE 0x0100 +#define M66592_ReqRESERVED 0x0200 +#define M66592_SET_FEATURE 0x0300 +#define M66592_ReqRESERVED1 0x0400 +#define M66592_SET_ADDRESS 0x0500 +#define M66592_GET_DESCRIPTOR 0x0600 +#define M66592_SET_DESCRIPTOR 0x0700 +#define M66592_GET_CONFIGURATION 0x0800 +#define M66592_SET_CONFIGURATION 0x0900 +#define M66592_GET_INTERFACE 0x0A00 +#define M66592_SET_INTERFACE 0x0B00 +#define M66592_SYNCH_FRAME 0x0C00 +#define M66592_bmRequestType 0x00FF /* b7-0: bmRequestType */ +#define M66592_bmRequestTypeDir 0x0080 /* b7 : Data transfer direction */ +#define M66592_HOST_TO_DEVICE 0x0000 +#define M66592_DEVICE_TO_HOST 0x0080 +#define M66592_bmRequestTypeType 0x0060 /* b6-5: Type */ +#define M66592_STANDARD 0x0000 +#define M66592_CLASS 0x0020 +#define M66592_VENDOR 0x0040 +#define M66592_bmRequestTypeRecip 0x001F /* b4-0: Recipient */ +#define M66592_DEVICE 0x0000 +#define M66592_INTERFACE 0x0001 +#define M66592_ENDPOINT 0x0002 + +#define M66592_USBVAL 0x56 +#define M66592_wValue 0xFFFF /* b15-0: wValue */ +/* Standard Feature Selector */ +#define M66592_ENDPOINT_HALT 0x0000 +#define M66592_DEVICE_REMOTE_WAKEUP 0x0001 +#define M66592_TEST_MODE 0x0002 +/* Descriptor Types */ +#define M66592_DT_TYPE 0xFF00 +#define M66592_GET_DT_TYPE(v) (((v) & DT_TYPE) >> 8) +#define M66592_DT_DEVICE 0x01 +#define M66592_DT_CONFIGURATION 0x02 +#define M66592_DT_STRING 0x03 +#define M66592_DT_INTERFACE 0x04 +#define M66592_DT_ENDPOINT 0x05 +#define M66592_DT_DEVICE_QUALIFIER 0x06 +#define M66592_DT_OTHER_SPEED_CONFIGURATION 0x07 +#define M66592_DT_INTERFACE_POWER 0x08 +#define M66592_DT_INDEX 0x00FF +#define M66592_CONF_NUM 0x00FF +#define M66592_ALT_SET 0x00FF + +#define M66592_USBINDEX 0x58 +#define M66592_wIndex 0xFFFF /* b15-0: wIndex */ +#define M66592_TEST_SELECT 0xFF00 /* b15-b8: Test Mode Selectors */ +#define M66592_TEST_J 0x0100 /* Test_J */ +#define M66592_TEST_K 0x0200 /* Test_K */ +#define M66592_TEST_SE0_NAK 0x0300 /* Test_SE0_NAK */ +#define M66592_TEST_PACKET 0x0400 /* Test_Packet */ +#define M66592_TEST_FORCE_ENABLE 0x0500 /* Test_Force_Enable */ +#define M66592_TEST_STSelectors 0x0600 /* Standard test selectors */ +#define M66592_TEST_Reserved 0x4000 /* Reserved */ +#define M66592_TEST_VSTModes 0xC000 /* Vendor-specific test modes */ +#define M66592_EP_DIR 0x0080 /* b7: Endpoint Direction */ +#define M66592_EP_DIR_IN 0x0080 +#define M66592_EP_DIR_OUT 0x0000 + +#define M66592_USBLENG 0x5A +#define M66592_wLength 0xFFFF /* b15-0: wLength */ + +#define M66592_DCPCFG 0x5C +#define M66592_CNTMD 0x0100 /* b8: Continuous transfer mode select */ +#define M66592_DIR 0x0010 /* b4: Control transfer DIR select */ + +#define M66592_DCPMAXP 0x5E +#define M66592_DEVSEL 0xC000 /* b15-14: Device address select */ +#define M66592_DEVICE_0 0x0000 /* Device address 0 */ +#define M66592_DEVICE_1 0x4000 /* Device address 1 */ +#define M66592_DEVICE_2 0x8000 /* Device address 2 */ +#define M66592_DEVICE_3 0xC000 /* Device address 3 */ +#define M66592_MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */ + +#define M66592_DCPCTR 0x60 +#define M66592_BSTS 0x8000 /* b15: Buffer status */ +#define M66592_SUREQ 0x4000 /* b14: Send USB request */ +#define M66592_SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define M66592_SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define M66592_SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define M66592_CCPL 0x0004 /* b2: Enable control transfer complete */ +#define M66592_PID 0x0003 /* b1-0: Response PID */ +#define M66592_PID_STALL 0x0002 /* STALL */ +#define M66592_PID_BUF 0x0001 /* BUF */ +#define M66592_PID_NAK 0x0000 /* NAK */ + +#define M66592_PIPESEL 0x64 +#define M66592_PIPENM 0x0007 /* b2-0: Pipe select */ +#define M66592_PIPE0 0x0000 /* PIPE 0 */ +#define M66592_PIPE1 0x0001 /* PIPE 1 */ +#define M66592_PIPE2 0x0002 /* PIPE 2 */ +#define M66592_PIPE3 0x0003 /* PIPE 3 */ +#define M66592_PIPE4 0x0004 /* PIPE 4 */ +#define M66592_PIPE5 0x0005 /* PIPE 5 */ +#define M66592_PIPE6 0x0006 /* PIPE 6 */ +#define M66592_PIPE7 0x0007 /* PIPE 7 */ + +#define M66592_PIPECFG 0x66 +#define M66592_TYP 0xC000 /* b15-14: Transfer type */ +#define M66592_ISO 0xC000 /* Isochronous */ +#define M66592_INT 0x8000 /* Interrupt */ +#define M66592_BULK 0x4000 /* Bulk */ +#define M66592_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */ +#define M66592_DBLB 0x0200 /* b9: Double buffer mode select */ +#define M66592_CNTMD 0x0100 /* b8: Continuous transfer mode select */ +#define M66592_SHTNAK 0x0080 /* b7: Transfer end NAK */ +#define M66592_DIR 0x0010 /* b4: Transfer direction select */ +#define M66592_DIR_H_OUT 0x0010 /* HOST OUT */ +#define M66592_DIR_P_IN 0x0010 /* PERI IN */ +#define M66592_DIR_H_IN 0x0000 /* HOST IN */ +#define M66592_DIR_P_OUT 0x0000 /* PERI OUT */ +#define M66592_EPNUM 0x000F /* b3-0: Eendpoint number select */ +#define M66592_EP1 0x0001 +#define M66592_EP2 0x0002 +#define M66592_EP3 0x0003 +#define M66592_EP4 0x0004 +#define M66592_EP5 0x0005 +#define M66592_EP6 0x0006 +#define M66592_EP7 0x0007 +#define M66592_EP8 0x0008 +#define M66592_EP9 0x0009 +#define M66592_EP10 0x000A +#define M66592_EP11 0x000B +#define M66592_EP12 0x000C +#define M66592_EP13 0x000D +#define M66592_EP14 0x000E +#define M66592_EP15 0x000F + +#define M66592_PIPEBUF 0x68 +#define M66592_BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ +#define M66592_BUF_SIZE(x) ((((x) / 64) - 1) << 10) +#define M66592_BUFNMB 0x00FF /* b7-0: Pipe buffer number */ + +#define M66592_PIPEMAXP 0x6A +#define M66592_MXPS 0x07FF /* b10-0: Maxpacket size */ + +#define M66592_PIPEPERI 0x6C +#define M66592_IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */ +#define M66592_IITV 0x0007 /* b2-0: Isochronous interval */ + +#define M66592_PIPE1CTR 0x70 +#define M66592_PIPE2CTR 0x72 +#define M66592_PIPE3CTR 0x74 +#define M66592_PIPE4CTR 0x76 +#define M66592_PIPE5CTR 0x78 +#define M66592_PIPE6CTR 0x7A +#define M66592_PIPE7CTR 0x7C +#define M66592_BSTS 0x8000 /* b15: Buffer status */ +#define M66592_INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */ +#define M66592_ACLRM 0x0200 /* b9: Out buffer auto clear mode */ +#define M66592_SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define M66592_SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define M66592_SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define M66592_PID 0x0003 /* b1-0: Response PID */ + +#define M66592_INVALID_REG 0x7E + + +#define __iomem + +#define get_pipectr_addr(pipenum) (M66592_PIPE1CTR + (pipenum - 1) * 2) + +#define M66592_MAX_SAMPLING 10 + +#define M66592_MAX_NUM_PIPE 8 +#define M66592_MAX_NUM_BULK 3 +#define M66592_MAX_NUM_ISOC 2 +#define M66592_MAX_NUM_INT 2 + +#define M66592_BASE_PIPENUM_BULK 3 +#define M66592_BASE_PIPENUM_ISOC 1 +#define M66592_BASE_PIPENUM_INT 6 + +#define M66592_BASE_BUFNUM 6 +#define M66592_MAX_BUFNUM 0x4F + +struct m66592_pipe_info { + u16 pipe; + u16 epnum; + u16 maxpacket; + u16 type; + u16 interval; + u16 dir_in; +}; + +struct m66592_request { + struct usb_request req; + struct list_head queue; +}; + +struct m66592_ep { + struct usb_ep ep; + struct m66592 *m66592; + + struct list_head queue; + unsigned busy:1; + unsigned internal_ccpl:1; /* use only control */ + + /* this member can able to after m66592_enable */ + unsigned use_dma:1; + u16 pipenum; + u16 type; + const struct usb_endpoint_descriptor *desc; + /* register address */ + unsigned long fifoaddr; + unsigned long fifosel; + unsigned long fifoctr; + unsigned long fifotrn; + unsigned long pipectr; +}; + +struct m66592 { + spinlock_t lock; + void __iomem *reg; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct m66592_ep ep[M66592_MAX_NUM_PIPE]; + struct m66592_ep *pipenum2ep[M66592_MAX_NUM_PIPE]; + struct m66592_ep *epaddr2ep[16]; + + struct usb_request *ep0_req; /* for internal request */ + u16 *ep0_buf; /* for internal request */ + + struct timer_list timer; + + u16 old_vbus; + int scount; + + int old_dvsq; + + /* pipe config */ + int bulk; + int interrupt; + int isochronous; + int num_dma; + int bi_bufnum; /* bulk and isochronous's bufnum */ +}; + +#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget) +#define m66592_to_gadget(m66592) (&m66592->gadget) + +#define is_bulk_pipe(pipenum) \ + ((pipenum >= M66592_BASE_PIPENUM_BULK) && \ + (pipenum < (M66592_BASE_PIPENUM_BULK + M66592_MAX_NUM_BULK))) +#define is_interrupt_pipe(pipenum) \ + ((pipenum >= M66592_BASE_PIPENUM_INT) && \ + (pipenum < (M66592_BASE_PIPENUM_INT + M66592_MAX_NUM_INT))) +#define is_isoc_pipe(pipenum) \ + ((pipenum >= M66592_BASE_PIPENUM_ISOC) && \ + (pipenum < (M66592_BASE_PIPENUM_ISOC + M66592_MAX_NUM_ISOC))) + +#define enable_irq_ready(m66592, pipenum) \ + enable_pipe_irq(m66592, pipenum, M66592_BRDYENB) +#define disable_irq_ready(m66592, pipenum) \ + disable_pipe_irq(m66592, pipenum, M66592_BRDYENB) +#define enable_irq_empty(m66592, pipenum) \ + enable_pipe_irq(m66592, pipenum, M66592_BEMPENB) +#define disable_irq_empty(m66592, pipenum) \ + disable_pipe_irq(m66592, pipenum, M66592_BEMPENB) +#define enable_irq_nrdy(m66592, pipenum) \ + enable_pipe_irq(m66592, pipenum, M66592_NRDYENB) +#define disable_irq_nrdy(m66592, pipenum) \ + disable_pipe_irq(m66592, pipenum, M66592_NRDYENB) + +/*-------------------------------------------------------------------------*/ +static inline u16 m66592_read(struct m66592 *m66592, unsigned long offset) +{ + return inw((unsigned long)m66592->reg + offset); +} + +static inline void m66592_read_fifo(struct m66592 *m66592, + unsigned long offset, + void *buf, unsigned long len) +{ + unsigned long fifoaddr = (unsigned long)m66592->reg + offset; + + len = (len + 1) / 2; + insw(fifoaddr, buf, len); +} + +static inline void m66592_write(struct m66592 *m66592, u16 val, + unsigned long offset) +{ + outw(val, (unsigned long)m66592->reg + offset); +} + +static inline void m66592_write_fifo(struct m66592 *m66592, + unsigned long offset, + void *buf, unsigned long len) +{ + unsigned long fifoaddr = (unsigned long)m66592->reg + offset; + unsigned long odd = len & 0x0001; + + len = len / 2; + outsw(fifoaddr, buf, len); + if (odd) { + unsigned char *p = buf + len*2; + outb(*p, fifoaddr); + } +} + +static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat, + unsigned long offset) +{ + u16 tmp; + tmp = m66592_read(m66592, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + m66592_write(m66592, tmp, offset); +} + +#define m66592_bclr(m66592, val, offset) \ + m66592_mdfy(m66592, 0, val, offset) +#define m66592_bset(m66592, val, offset) \ + m66592_mdfy(m66592, val, 0, offset) + +#endif /* ifndef __M66592_UDC_H__ */ + + diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 00fda33..c3d364e 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -450,100 +450,6 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ -/* - * dma-coherent memory allocation (for dma-capable endpoints) - * - * NOTE: the dma_*_coherent() API calls suck. Most implementations are - * (a) page-oriented, so small buffers lose big; and (b) asymmetric with - * respect to calls with irqs disabled: alloc is safe, free is not. - * We currently work around (b), but not (a). - */ - -static void * -net2280_alloc_buffer ( - struct usb_ep *_ep, - unsigned bytes, - dma_addr_t *dma, - gfp_t gfp_flags -) -{ - void *retval; - struct net2280_ep *ep; - - ep = container_of (_ep, struct net2280_ep, ep); - if (!_ep) - return NULL; - *dma = DMA_ADDR_INVALID; - - if (ep->dma) - retval = dma_alloc_coherent(&ep->dev->pdev->dev, - bytes, dma, gfp_flags); - else - retval = kmalloc(bytes, gfp_flags); - return retval; -} - -static DEFINE_SPINLOCK(buflock); -static LIST_HEAD(buffers); - -struct free_record { - struct list_head list; - struct device *dev; - unsigned bytes; - dma_addr_t dma; -}; - -static void do_free(unsigned long ignored) -{ - spin_lock_irq(&buflock); - while (!list_empty(&buffers)) { - struct free_record *buf; - - buf = list_entry(buffers.next, struct free_record, list); - list_del(&buf->list); - spin_unlock_irq(&buflock); - - dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma); - - spin_lock_irq(&buflock); - } - spin_unlock_irq(&buflock); -} - -static DECLARE_TASKLET(deferred_free, do_free, 0); - -static void -net2280_free_buffer ( - struct usb_ep *_ep, - void *address, - dma_addr_t dma, - unsigned bytes -) { - /* free memory into the right allocator */ - if (dma != DMA_ADDR_INVALID) { - struct net2280_ep *ep; - struct free_record *buf = address; - unsigned long flags; - - ep = container_of(_ep, struct net2280_ep, ep); - if (!_ep) - return; - - ep = container_of (_ep, struct net2280_ep, ep); - buf->dev = &ep->dev->pdev->dev; - buf->bytes = bytes; - buf->dma = dma; - - spin_lock_irqsave(&buflock, flags); - list_add_tail(&buf->list, &buffers); - tasklet_schedule(&deferred_free); - spin_unlock_irqrestore(&buflock, flags); - } else - kfree (address); -} - -/*-------------------------------------------------------------------------*/ - /* load a packet into the fifo we use for usb IN transfers. * works for all endpoints. * @@ -1392,9 +1298,6 @@ static const struct usb_ep_ops net2280_ep_ops = { .alloc_request = net2280_alloc_request, .free_request = net2280_free_request, - .alloc_buffer = net2280_alloc_buffer, - .free_buffer = net2280_free_buffer, - .queue = net2280_queue, .dequeue = net2280_dequeue, diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index c4975a6..9b0f092 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -296,111 +296,6 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ -/* - * dma-coherent memory allocation (for dma-capable endpoints) - * - * NOTE: the dma_*_coherent() API calls suck. Most implementations are - * (a) page-oriented, so small buffers lose big; and (b) asymmetric with - * respect to calls with irqs disabled: alloc is safe, free is not. - * We currently work around (b), but not (a). - */ - -static void * -omap_alloc_buffer( - struct usb_ep *_ep, - unsigned bytes, - dma_addr_t *dma, - gfp_t gfp_flags -) -{ - void *retval; - struct omap_ep *ep; - - if (!_ep) - return NULL; - - ep = container_of(_ep, struct omap_ep, ep); - if (use_dma && ep->has_dma) { - static int warned; - if (!warned && bytes < PAGE_SIZE) { - dev_warn(ep->udc->gadget.dev.parent, - "using dma_alloc_coherent for " - "small allocations wastes memory\n"); - warned++; - } - return dma_alloc_coherent(ep->udc->gadget.dev.parent, - bytes, dma, gfp_flags); - } - - retval = kmalloc(bytes, gfp_flags); - if (retval) - *dma = virt_to_phys(retval); - return retval; -} - -static DEFINE_SPINLOCK(buflock); -static LIST_HEAD(buffers); - -struct free_record { - struct list_head list; - struct device *dev; - unsigned bytes; - dma_addr_t dma; -}; - -static void do_free(unsigned long ignored) -{ - spin_lock_irq(&buflock); - while (!list_empty(&buffers)) { - struct free_record *buf; - - buf = list_entry(buffers.next, struct free_record, list); - list_del(&buf->list); - spin_unlock_irq(&buflock); - - dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma); - - spin_lock_irq(&buflock); - } - spin_unlock_irq(&buflock); -} - -static DECLARE_TASKLET(deferred_free, do_free, 0); - -static void omap_free_buffer( - struct usb_ep *_ep, - void *buf, - dma_addr_t dma, - unsigned bytes -) -{ - if (!_ep) { - WARN_ON(1); - return; - } - - /* free memory into the right allocator */ - if (dma != DMA_ADDR_INVALID) { - struct omap_ep *ep; - struct free_record *rec = buf; - unsigned long flags; - - ep = container_of(_ep, struct omap_ep, ep); - - rec->dev = ep->udc->gadget.dev.parent; - rec->bytes = bytes; - rec->dma = dma; - - spin_lock_irqsave(&buflock, flags); - list_add_tail(&rec->list, &buffers); - tasklet_schedule(&deferred_free); - spin_unlock_irqrestore(&buflock, flags); - } else - kfree(buf); -} - -/*-------------------------------------------------------------------------*/ - static void done(struct omap_ep *ep, struct omap_req *req, int status) { @@ -1271,9 +1166,6 @@ static struct usb_ep_ops omap_ep_ops = { .alloc_request = omap_alloc_request, .free_request = omap_free_request, - .alloc_buffer = omap_alloc_buffer, - .free_buffer = omap_free_buffer, - .queue = omap_ep_queue, .dequeue = omap_ep_dequeue, diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index 84392e8..63b9521 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -24,9 +24,9 @@ * */ -#undef DEBUG // #define VERBOSE DBG_VERBOSE +#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/ioport.h> @@ -46,19 +46,17 @@ #include <asm/byteorder.h> #include <asm/dma.h> +#include <asm/gpio.h> #include <asm/io.h> #include <asm/system.h> #include <asm/mach-types.h> #include <asm/unaligned.h> #include <asm/hardware.h> -#ifdef CONFIG_ARCH_PXA -#include <asm/arch/pxa-regs.h> -#endif #include <linux/usb/ch9.h> #include <linux/usb_gadget.h> -#include <asm/arch/udc.h> +#include <asm/mach/udc_pxa2xx.h> /* @@ -76,9 +74,17 @@ * it constrains the sorts of USB configuration change events that work. * The errata for these chips are misleading; some "fixed" bugs from * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. + * + * Note that the UDC hardware supports DMA (except on IXP) but that's + * not used here. IN-DMA (to host) is simple enough, when the data is + * suitably aligned (16 bytes) ... the network stack doesn't do that, + * other software can. OUT-DMA is buggy in most chip versions, as well + * as poorly designed (data toggle not automatic). So this driver won't + * bother using DMA. (Mostly-working IN-DMA support was available in + * kernels before 2.6.23, but was never enabled or well tested.) */ -#define DRIVER_VERSION "4-May-2005" +#define DRIVER_VERSION "30-June-2007" #define DRIVER_DESC "PXA 25x USB Device Controller driver" @@ -87,12 +93,9 @@ static const char driver_name [] = "pxa2xx_udc"; static const char ep0name [] = "ep0"; -// #define USE_DMA -// #define USE_OUT_DMA // #define DISABLE_TEST_MODE #ifdef CONFIG_ARCH_IXP4XX -#undef USE_DMA /* cpu-specific register addresses are compiled in to this code */ #ifdef CONFIG_ARCH_PXA @@ -104,25 +107,6 @@ static const char ep0name [] = "ep0"; #include "pxa2xx_udc.h" -#ifdef USE_DMA -static int use_dma = 1; -module_param(use_dma, bool, 0); -MODULE_PARM_DESC (use_dma, "true to use dma"); - -static void dma_nodesc_handler (int dmach, void *_ep); -static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req); - -#ifdef USE_OUT_DMA -#define DMASTR " (dma support)" -#else -#define DMASTR " (dma in)" -#endif - -#else /* !USE_DMA */ -#define DMASTR " (pio only)" -#undef USE_OUT_DMA -#endif - #ifdef CONFIG_USB_PXA2XX_SMALL #define SIZE_STR " (small)" #else @@ -155,7 +139,7 @@ static int is_vbus_present(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_vbus) - return udc_gpio_get(mach->gpio_vbus); + return gpio_get_value(mach->gpio_vbus); if (mach->udc_is_connected) return mach->udc_is_connected(); return 1; @@ -167,7 +151,7 @@ static void pullup_off(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_pullup) - udc_gpio_set(mach->gpio_pullup, 0); + gpio_set_value(mach->gpio_pullup, 0); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); } @@ -177,7 +161,7 @@ static void pullup_on(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_pullup) - udc_gpio_set(mach->gpio_pullup, 1); + gpio_set_value(mach->gpio_pullup, 1); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); } @@ -281,9 +265,8 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, } ep->desc = desc; - ep->dma = -1; ep->stopped = 0; - ep->pio_irqs = ep->dma_irqs = 0; + ep->pio_irqs = 0; ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); /* flush fifo (mostly for OUT buffers) */ @@ -291,30 +274,6 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, /* ... reset halt state too, if we could ... */ -#ifdef USE_DMA - /* for (some) bulk and ISO endpoints, try to get a DMA channel and - * bind it to the endpoint. otherwise use PIO. - */ - switch (ep->bmAttributes) { - case USB_ENDPOINT_XFER_ISOC: - if (le16_to_cpu(desc->wMaxPacketSize) % 32) - break; - // fall through - case USB_ENDPOINT_XFER_BULK: - if (!use_dma || !ep->reg_drcmr) - break; - ep->dma = pxa_request_dma ((char *)_ep->name, - (le16_to_cpu (desc->wMaxPacketSize) > 64) - ? DMA_PRIO_MEDIUM /* some iso */ - : DMA_PRIO_LOW, - dma_nodesc_handler, ep); - if (ep->dma >= 0) { - *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma; - DMSG("%s using dma%d\n", _ep->name, ep->dma); - } - } -#endif - DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); return 0; } @@ -334,14 +293,6 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) nuke (ep, -ESHUTDOWN); -#ifdef USE_DMA - if (ep->dma >= 0) { - *ep->reg_drcmr = 0; - pxa_free_dma (ep->dma); - ep->dma = -1; - } -#endif - /* flush fifo (mostly for IN buffers) */ pxa2xx_ep_fifo_flush (_ep); @@ -390,35 +341,6 @@ pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) kfree(req); } - -/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's - * no device-affinity and the heap works perfectly well for i/o buffers. - * It wastes much less memory than dma_alloc_coherent() would, and even - * prevents cacheline (32 bytes wide) sharing problems. - */ -static void * -pxa2xx_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, - dma_addr_t *dma, gfp_t gfp_flags) -{ - char *retval; - - retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); - if (retval) -#ifdef USE_DMA - *dma = virt_to_bus (retval); -#else - *dma = (dma_addr_t)~0; -#endif - return retval; -} - -static void -pxa2xx_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, - unsigned bytes) -{ - kfree (buf); -} - /*-------------------------------------------------------------------------*/ /* @@ -518,18 +440,8 @@ write_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) /* requests complete when all IN data is in the FIFO */ if (is_last) { done (ep, req, 0); - if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) { + if (list_empty(&ep->queue)) pio_irq_disable (ep->bEndpointAddress); -#ifdef USE_DMA - /* unaligned data and zlps couldn't use dma */ - if (unlikely(!list_empty(&ep->queue))) { - req = list_entry(ep->queue.next, - struct pxa2xx_request, queue); - kick_dma(ep,req); - return 0; - } -#endif - } return 1; } @@ -728,182 +640,6 @@ read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) return 0; } -#ifdef USE_DMA - -#define MAX_IN_DMA ((DCMD_LENGTH + 1) - BULK_FIFO_SIZE) - -static void -start_dma_nodesc(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int is_in) -{ - u32 dcmd = req->req.length; - u32 buf = req->req.dma; - u32 fifo = io_v2p ((u32)ep->reg_uddr); - - /* caller guarantees there's a packet or more remaining - * - IN may end with a short packet (TSP set separately), - * - OUT is always full length - */ - buf += req->req.actual; - dcmd -= req->req.actual; - ep->dma_fixup = 0; - - /* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */ - DCSR(ep->dma) = DCSR_NODESC; - if (is_in) { - DSADR(ep->dma) = buf; - DTADR(ep->dma) = fifo; - if (dcmd > MAX_IN_DMA) - dcmd = MAX_IN_DMA; - else - ep->dma_fixup = (dcmd % ep->ep.maxpacket) != 0; - dcmd |= DCMD_BURST32 | DCMD_WIDTH1 - | DCMD_FLOWTRG | DCMD_INCSRCADDR; - } else { -#ifdef USE_OUT_DMA - DSADR(ep->dma) = fifo; - DTADR(ep->dma) = buf; - if (ep->bmAttributes != USB_ENDPOINT_XFER_ISOC) - dcmd = ep->ep.maxpacket; - dcmd |= DCMD_BURST32 | DCMD_WIDTH1 - | DCMD_FLOWSRC | DCMD_INCTRGADDR; -#endif - } - DCMD(ep->dma) = dcmd; - DCSR(ep->dma) = DCSR_RUN | DCSR_NODESC - | (unlikely(is_in) - ? DCSR_STOPIRQEN /* use dma_nodesc_handler() */ - : 0); /* use handle_ep() */ -} - -static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req) -{ - int is_in = ep->bEndpointAddress & USB_DIR_IN; - - if (is_in) { - /* unaligned tx buffers and zlps only work with PIO */ - if ((req->req.dma & 0x0f) != 0 - || unlikely((req->req.length - req->req.actual) - == 0)) { - pio_irq_enable(ep->bEndpointAddress); - if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0) - (void) write_fifo(ep, req); - } else { - start_dma_nodesc(ep, req, USB_DIR_IN); - } - } else { - if ((req->req.length - req->req.actual) < ep->ep.maxpacket) { - DMSG("%s short dma read...\n", ep->ep.name); - /* we're always set up for pio out */ - read_fifo (ep, req); - } else { - *ep->reg_udccs = UDCCS_BO_DME - | (*ep->reg_udccs & UDCCS_BO_FST); - start_dma_nodesc(ep, req, USB_DIR_OUT); - } - } -} - -static void cancel_dma(struct pxa2xx_ep *ep) -{ - struct pxa2xx_request *req; - u32 tmp; - - if (DCSR(ep->dma) == 0 || list_empty(&ep->queue)) - return; - - DCSR(ep->dma) = 0; - while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0) - cpu_relax(); - - req = list_entry(ep->queue.next, struct pxa2xx_request, queue); - tmp = DCMD(ep->dma) & DCMD_LENGTH; - req->req.actual = req->req.length - (tmp & DCMD_LENGTH); - - /* the last tx packet may be incomplete, so flush the fifo. - * FIXME correct req.actual if we can - */ - if (ep->bEndpointAddress & USB_DIR_IN) - *ep->reg_udccs = UDCCS_BI_FTF; -} - -/* dma channel stopped ... normal tx end (IN), or on error (IN/OUT) */ -static void dma_nodesc_handler(int dmach, void *_ep) -{ - struct pxa2xx_ep *ep = _ep; - struct pxa2xx_request *req; - u32 tmp, completed; - - local_irq_disable(); - - req = list_entry(ep->queue.next, struct pxa2xx_request, queue); - - ep->dma_irqs++; - ep->dev->stats.irqs++; - HEX_DISPLAY(ep->dev->stats.irqs); - - /* ack/clear */ - tmp = DCSR(ep->dma); - DCSR(ep->dma) = tmp; - if ((tmp & DCSR_STOPSTATE) == 0 - || (DDADR(ep->dma) & DDADR_STOP) != 0) { - DBG(DBG_VERBOSE, "%s, dcsr %08x ddadr %08x\n", - ep->ep.name, DCSR(ep->dma), DDADR(ep->dma)); - goto done; - } - DCSR(ep->dma) = 0; /* clear DCSR_STOPSTATE */ - - /* update transfer status */ - completed = tmp & DCSR_BUSERR; - if (ep->bEndpointAddress & USB_DIR_IN) - tmp = DSADR(ep->dma); - else - tmp = DTADR(ep->dma); - req->req.actual = tmp - req->req.dma; - - /* FIXME seems we sometimes see partial transfers... */ - - if (unlikely(completed != 0)) - req->req.status = -EIO; - else if (req->req.actual) { - /* these registers have zeroes in low bits; they miscount - * some (end-of-transfer) short packets: tx 14 as tx 12 - */ - if (ep->dma_fixup) - req->req.actual = min(req->req.actual + 3, - req->req.length); - - tmp = (req->req.length - req->req.actual); - completed = (tmp == 0); - if (completed && (ep->bEndpointAddress & USB_DIR_IN)) { - - /* maybe validate final short packet ... */ - if ((req->req.actual % ep->ep.maxpacket) != 0) - *ep->reg_udccs = UDCCS_BI_TSP/*|UDCCS_BI_TPC*/; - - /* ... or zlp, using pio fallback */ - else if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK - && req->req.zero) { - DMSG("%s zlp terminate ...\n", ep->ep.name); - completed = 0; - } - } - } - - if (likely(completed)) { - done(ep, req, 0); - - /* maybe re-activate after completion */ - if (ep->stopped || list_empty(&ep->queue)) - goto done; - req = list_entry(ep->queue.next, struct pxa2xx_request, queue); - } - kick_dma(ep, req); -done: - local_irq_enable(); -} - -#endif - /*-------------------------------------------------------------------------*/ static int @@ -942,19 +678,8 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) (ep->desc->wMaxPacketSize))) return -EMSGSIZE; -#ifdef USE_DMA - // FIXME caller may already have done the dma mapping - if (ep->dma >= 0) { - _req->dma = dma_map_single(dev->dev, - _req->buf, _req->length, - ((ep->bEndpointAddress & USB_DIR_IN) != 0) - ? DMA_TO_DEVICE - : DMA_FROM_DEVICE); - } -#endif - DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", - _ep->name, _req, _req->length, _req->buf); + _ep->name, _req, _req->length, _req->buf); local_irq_save(flags); @@ -1002,11 +727,6 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) local_irq_restore (flags); return -EL2HLT; } -#ifdef USE_DMA - /* either start dma or prime pio pump */ - } else if (ep->dma >= 0) { - kick_dma(ep, req); -#endif /* can the FIFO can satisfy the request immediately? */ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0 @@ -1017,7 +737,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) req = NULL; } - if (likely (req && ep->desc) && ep->dma < 0) + if (likely (req && ep->desc)) pio_irq_enable(ep->bEndpointAddress); } @@ -1038,10 +758,6 @@ static void nuke(struct pxa2xx_ep *ep, int status) struct pxa2xx_request *req; /* called with irqs blocked */ -#ifdef USE_DMA - if (ep->dma >= 0 && !ep->stopped) - cancel_dma(ep); -#endif while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct pxa2xx_request, @@ -1076,19 +792,7 @@ static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) return -EINVAL; } -#ifdef USE_DMA - if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { - cancel_dma(ep); - done(ep, req, -ECONNRESET); - /* restart i/o */ - if (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, - struct pxa2xx_request, queue); - kick_dma(ep, req); - } - } else -#endif - done(ep, req, -ECONNRESET); + done(ep, req, -ECONNRESET); local_irq_restore(flags); return 0; @@ -1203,9 +907,6 @@ static struct usb_ep_ops pxa2xx_ep_ops = { .alloc_request = pxa2xx_ep_alloc_request, .free_request = pxa2xx_ep_free_request, - .alloc_buffer = pxa2xx_ep_alloc_buffer, - .free_buffer = pxa2xx_ep_free_buffer, - .queue = pxa2xx_ep_queue, .dequeue = pxa2xx_ep_dequeue, @@ -1325,7 +1026,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, /* basic device status */ t = scnprintf(next, size, DRIVER_DESC "\n" "%s version: %s\nGadget driver: %s\nHost %s\n\n", - driver_name, DRIVER_VERSION SIZE_STR DMASTR, + driver_name, DRIVER_VERSION SIZE_STR "(pio)", dev->driver ? dev->driver->driver.name : "(none)", is_vbus_present() ? "full speed" : "disconnected"); size -= t; @@ -1390,7 +1091,6 @@ udc_proc_read(char *page, char **start, off_t off, int count, for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { struct pxa2xx_ep *ep = &dev->ep [i]; struct pxa2xx_request *req; - int t; if (i != 0) { const struct usb_endpoint_descriptor *d; @@ -1400,10 +1100,9 @@ udc_proc_read(char *page, char **start, off_t off, int count, continue; tmp = *dev->ep [i].reg_udccs; t = scnprintf(next, size, - "%s max %d %s udccs %02x irqs %lu/%lu\n", + "%s max %d %s udccs %02x irqs %lu\n", ep->ep.name, le16_to_cpu (d->wMaxPacketSize), - (ep->dma >= 0) ? "dma" : "pio", tmp, - ep->pio_irqs, ep->dma_irqs); + "pio", tmp, ep->pio_irqs); /* TODO translate all five groups of udccs bits! */ } else /* ep0 should only have one transfer queued */ @@ -1423,19 +1122,7 @@ udc_proc_read(char *page, char **start, off_t off, int count, continue; } list_for_each_entry(req, &ep->queue, queue) { -#ifdef USE_DMA - if (ep->dma >= 0 && req->queue.prev == &ep->queue) - t = scnprintf(next, size, - "\treq %p len %d/%d " - "buf %p (dma%d dcmd %08x)\n", - &req->req, req->req.actual, - req->req.length, req->req.buf, - ep->dma, DCMD(ep->dma) - // low 13 bits == bytes-to-go - ); - else -#endif - t = scnprintf(next, size, + t = scnprintf(next, size, "\treq %p len %d/%d buf %p\n", &req->req, req->req.actual, req->req.length, req->req.buf); @@ -1488,7 +1175,6 @@ static void udc_disable(struct pxa2xx_udc *dev) ep0_idle (dev); dev->gadget.speed = USB_SPEED_UNKNOWN; - LED_CONNECTED_OFF; } @@ -1514,7 +1200,7 @@ static void udc_reinit(struct pxa2xx_udc *dev) ep->desc = NULL; ep->stopped = 0; INIT_LIST_HEAD (&ep->queue); - ep->pio_irqs = ep->dma_irqs = 0; + ep->pio_irqs = 0; } /* the rest was statically initialized, and is read-only */ @@ -1666,7 +1352,6 @@ stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver) del_timer_sync(&dev->timer); /* report disconnect; the driver is already quiesced */ - LED_CONNECTED_OFF; if (driver) driver->disconnect(&dev->gadget); @@ -1715,16 +1400,13 @@ lubbock_vbus_irq(int irq, void *_dev) int vbus; dev->stats.irqs++; - HEX_DISPLAY(dev->stats.irqs); switch (irq) { case LUBBOCK_USB_IRQ: - LED_CONNECTED_ON; vbus = 1; disable_irq(LUBBOCK_USB_IRQ); enable_irq(LUBBOCK_USB_DISC_IRQ); break; case LUBBOCK_USB_DISC_IRQ: - LED_CONNECTED_OFF; vbus = 0; disable_irq(LUBBOCK_USB_DISC_IRQ); enable_irq(LUBBOCK_USB_IRQ); @@ -1742,7 +1424,7 @@ lubbock_vbus_irq(int irq, void *_dev) static irqreturn_t udc_vbus_irq(int irq, void *_dev) { struct pxa2xx_udc *dev = _dev; - int vbus = udc_gpio_get(dev->mach->gpio_vbus); + int vbus = gpio_get_value(dev->mach->gpio_vbus); pxa2xx_udc_vbus_session(&dev->gadget, vbus); return IRQ_HANDLED; @@ -2040,18 +1722,6 @@ static void handle_ep(struct pxa2xx_ep *ep) /* fifos can hold packets, ready for reading... */ if (likely(req)) { -#ifdef USE_OUT_DMA -// TODO didn't yet debug out-dma. this approach assumes -// the worst about short packets and RPC; it might be better. - - if (likely(ep->dma >= 0)) { - if (!(udccs & UDCCS_BO_RSP)) { - *ep->reg_udccs = UDCCS_BO_RPC; - ep->dma_irqs++; - return; - } - } -#endif completed = read_fifo(ep, req); } else pio_irq_disable (ep->bEndpointAddress); @@ -2074,7 +1744,6 @@ pxa2xx_udc_irq(int irq, void *_dev) int handled; dev->stats.irqs++; - HEX_DISPLAY(dev->stats.irqs); do { u32 udccr = UDCCR; @@ -2125,7 +1794,6 @@ pxa2xx_udc_irq(int irq, void *_dev) } else { DBG(DBG_VERBOSE, "USB reset end\n"); dev->gadget.speed = USB_SPEED_FULL; - LED_CONNECTED_ON; memset(&dev->stats, 0, sizeof dev->stats); /* driver and endpoints are still reset */ } @@ -2217,7 +1885,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS1, .reg_uddr = &UDDR1, - drcmr (25) }, .ep[2] = { .ep = { @@ -2232,7 +1899,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS2, .reg_ubcr = &UBCR2, .reg_uddr = &UDDR2, - drcmr (26) }, #ifndef CONFIG_USB_PXA2XX_SMALL .ep[3] = { @@ -2247,7 +1913,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS3, .reg_uddr = &UDDR3, - drcmr (27) }, .ep[4] = { .ep = { @@ -2262,7 +1927,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS4, .reg_ubcr = &UBCR4, .reg_uddr = &UDDR4, - drcmr (28) }, .ep[5] = { .ep = { @@ -2291,7 +1955,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS6, .reg_uddr = &UDDR6, - drcmr (30) }, .ep[7] = { .ep = { @@ -2306,7 +1969,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS7, .reg_ubcr = &UBCR7, .reg_uddr = &UDDR7, - drcmr (31) }, .ep[8] = { .ep = { @@ -2320,7 +1982,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS8, .reg_uddr = &UDDR8, - drcmr (32) }, .ep[9] = { .ep = { @@ -2335,7 +1996,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS9, .reg_ubcr = &UBCR9, .reg_uddr = &UDDR9, - drcmr (33) }, .ep[10] = { .ep = { @@ -2364,7 +2024,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_BULK, .reg_udccs = &UDCCS11, .reg_uddr = &UDDR11, - drcmr (35) }, .ep[12] = { .ep = { @@ -2379,7 +2038,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS12, .reg_ubcr = &UBCR12, .reg_uddr = &UDDR12, - drcmr (36) }, .ep[13] = { .ep = { @@ -2393,7 +2051,6 @@ static struct pxa2xx_udc memory = { .bmAttributes = USB_ENDPOINT_XFER_ISOC, .reg_udccs = &UDCCS13, .reg_uddr = &UDDR13, - drcmr (37) }, .ep[14] = { .ep = { @@ -2408,7 +2065,6 @@ static struct pxa2xx_udc memory = { .reg_udccs = &UDCCS14, .reg_ubcr = &UBCR14, .reg_uddr = &UDDR14, - drcmr (38) }, .ep[15] = { .ep = { @@ -2466,7 +2122,7 @@ static struct pxa2xx_udc memory = { static int __init pxa2xx_udc_probe(struct platform_device *pdev) { struct pxa2xx_udc *dev = &memory; - int retval, out_dma = 1, vbus_irq, irq; + int retval, vbus_irq, irq; u32 chiprev; /* insist on Intel/ARM/XScale */ @@ -2489,7 +2145,7 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) case PXA250_B2: case PXA210_B2: case PXA250_B1: case PXA210_B1: case PXA250_B0: case PXA210_B0: - out_dma = 0; + /* OUT-DMA is broken ... */ /* fall through */ case PXA250_C0: case PXA210_C0: break; @@ -2498,11 +2154,9 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) case IXP425_B0: case IXP465_AD: dev->has_cfr = 1; - out_dma = 0; break; #endif default: - out_dma = 0; printk(KERN_ERR "%s: unrecognized processor: %08x\n", driver_name, chiprev); /* iop3xx, ixp4xx, ... */ @@ -2513,36 +2167,41 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq, + pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, dev->has_cfr ? "" : " (!cfr)", - out_dma ? "" : " (broken dma-out)", - SIZE_STR DMASTR + SIZE_STR "(pio)" ); -#ifdef USE_DMA -#ifndef USE_OUT_DMA - out_dma = 0; -#endif - /* pxa 250 erratum 130 prevents using OUT dma (fixed C0) */ - if (!out_dma) { - DMSG("disabled OUT dma\n"); - dev->ep[ 2].reg_drcmr = dev->ep[ 4].reg_drcmr = 0; - dev->ep[ 7].reg_drcmr = dev->ep[ 9].reg_drcmr = 0; - dev->ep[12].reg_drcmr = dev->ep[14].reg_drcmr = 0; - } -#endif - /* other non-static parts of init */ dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; + if (dev->mach->gpio_vbus) { - udc_gpio_init_vbus(dev->mach->gpio_vbus); - vbus_irq = udc_gpio_to_irq(dev->mach->gpio_vbus); + if ((retval = gpio_request(dev->mach->gpio_vbus, + "pxa2xx_udc GPIO VBUS"))) { + dev_dbg(&pdev->dev, + "can't get vbus gpio %d, err: %d\n", + dev->mach->gpio_vbus, retval); + return -EBUSY; + } + gpio_direction_input(dev->mach->gpio_vbus); + vbus_irq = gpio_to_irq(dev->mach->gpio_vbus); set_irq_type(vbus_irq, IRQT_BOTHEDGE); } else vbus_irq = 0; - if (dev->mach->gpio_pullup) - udc_gpio_init_pullup(dev->mach->gpio_pullup); + + if (dev->mach->gpio_pullup) { + if ((retval = gpio_request(dev->mach->gpio_pullup, + "pca2xx_udc GPIO PULLUP"))) { + dev_dbg(&pdev->dev, + "can't get pullup gpio %d, err: %d\n", + dev->mach->gpio_pullup, retval); + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); + return -EBUSY; + } + gpio_direction_output(dev->mach->gpio_pullup, 0); + } init_timer(&dev->timer); dev->timer.function = udc_watchdog; @@ -2566,6 +2225,10 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) if (retval != 0) { printk(KERN_ERR "%s: can't get irq %d, err %d\n", driver_name, irq, retval); + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); return -EBUSY; } dev->got_irq = 1; @@ -2581,6 +2244,10 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) driver_name, LUBBOCK_USB_DISC_IRQ, retval); lubbock_fail0: free_irq(irq, dev); + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); return -EBUSY; } retval = request_irq(LUBBOCK_USB_IRQ, @@ -2593,11 +2260,6 @@ lubbock_fail0: free_irq(LUBBOCK_USB_DISC_IRQ, dev); goto lubbock_fail0; } -#ifdef DEBUG - /* with U-Boot (but not BLOB), hex is off by default */ - HEX_DISPLAY(dev->stats.irqs); - LUB_DISC_BLNK_LED &= 0xff; -#endif } else #endif if (vbus_irq) { @@ -2608,6 +2270,10 @@ lubbock_fail0: printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, vbus_irq, retval); free_irq(irq, dev); + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); return -EBUSY; } } @@ -2641,8 +2307,13 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev) free_irq(LUBBOCK_USB_IRQ, dev); } #endif - if (dev->mach->gpio_vbus) - free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev); + if (dev->mach->gpio_vbus) { + free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); + gpio_free(dev->mach->gpio_vbus); + } + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + platform_set_drvdata(pdev, NULL); the_controller = NULL; return 0; diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h index 773e549..0e5d0e6 100644 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ b/drivers/usb/gadget/pxa2xx_udc.h @@ -54,8 +54,6 @@ struct pxa2xx_ep { const struct usb_endpoint_descriptor *desc; struct list_head queue; unsigned long pio_irqs; - unsigned long dma_irqs; - short dma; unsigned short fifo_size; u8 bEndpointAddress; @@ -63,7 +61,7 @@ struct pxa2xx_ep { unsigned stopped : 1; unsigned dma_fixup : 1; - + /* UDCCS = UDC Control/Status for this EP * UBCR = UDC Byte Count Remaining (contents of OUT fifo) * UDDR = UDC Endpoint Data Register (the fifo) @@ -72,12 +70,6 @@ struct pxa2xx_ep { volatile u32 *reg_udccs; volatile u32 *reg_ubcr; volatile u32 *reg_uddr; -#ifdef USE_DMA - volatile u32 *reg_drcmr; -#define drcmr(n) .reg_drcmr = & DRCMR ## n , -#else -#define drcmr(n) -#endif }; struct pxa2xx_request { @@ -85,7 +77,7 @@ struct pxa2xx_request { struct list_head queue; }; -enum ep0_state { +enum ep0_state { EP0_IDLE, EP0_IN_DATA_PHASE, EP0_OUT_DATA_PHASE, @@ -108,7 +100,6 @@ struct udc_stats { #ifdef CONFIG_USB_PXA2XX_SMALL /* when memory's tight, SMALL config saves code+data. */ -#undef USE_DMA #define PXA_UDC_NUM_ENDPOINTS 3 #endif @@ -144,37 +135,8 @@ struct pxa2xx_udc { #ifdef CONFIG_ARCH_LUBBOCK #include <asm/arch/lubbock.h> /* lubbock can also report usb connect/disconnect irqs */ - -#ifdef DEBUG -#define HEX_DISPLAY(n) if (machine_is_lubbock()) { LUB_HEXLED = (n); } #endif -#endif - -/*-------------------------------------------------------------------------*/ - -/* LEDs are only for debug */ -#ifndef HEX_DISPLAY -#define HEX_DISPLAY(n) do {} while(0) -#endif - -#ifdef DEBUG -#include <asm/leds.h> - -#define LED_CONNECTED_ON leds_event(led_green_on) -#define LED_CONNECTED_OFF do { \ - leds_event(led_green_off); \ - HEX_DISPLAY(0); \ - } while(0) -#endif - -#ifndef LED_CONNECTED_ON -#define LED_CONNECTED_ON do {} while(0) -#define LED_CONNECTED_OFF do {} while(0) -#endif - -/*-------------------------------------------------------------------------*/ - static struct pxa2xx_udc *the_controller; /*-------------------------------------------------------------------------*/ @@ -204,7 +166,7 @@ static const char *state_name[] = { # define UDC_DEBUG DBG_NORMAL #endif -static void __attribute__ ((__unused__)) +static void __maybe_unused dump_udccr(const char *label) { u32 udccr = UDCCR; @@ -220,7 +182,7 @@ dump_udccr(const char *label) (udccr & UDCCR_UDE) ? " ude" : ""); } -static void __attribute__ ((__unused__)) +static void __maybe_unused dump_udccs0(const char *label) { u32 udccs0 = UDCCS0; @@ -237,7 +199,7 @@ dump_udccs0(const char *label) (udccs0 & UDCCS0_OPR) ? " opr" : ""); } -static void __attribute__ ((__unused__)) +static void __maybe_unused dump_state(struct pxa2xx_udc *dev) { u32 tmp; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 708657c..db1b2bf 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -53,7 +53,7 @@ */ #if 0 -#define DEBUG(str,args...) do { \ +#define DBG(str,args...) do { \ if (rndis_debug) \ printk(KERN_DEBUG str , ## args ); \ } while (0) @@ -65,7 +65,7 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #else #define rndis_debug 0 -#define DEBUG(str,args...) do{}while(0) +#define DBG(str,args...) do{}while(0) #endif #define RNDIS_MAX_CONFIGS 1 @@ -183,9 +183,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, if (!resp) return -ENOMEM; if (buf_len && rndis_debug > 1) { - DEBUG("query OID %08x value, len %d:\n", OID, buf_len); + DBG("query OID %08x value, len %d:\n", OID, buf_len); for (i = 0; i < buf_len; i += 16) { - DEBUG ("%03d: %08x %08x %08x %08x\n", i, + DBG("%03d: %08x %08x %08x %08x\n", i, le32_to_cpu(get_unaligned((__le32 *) &buf[i])), le32_to_cpu(get_unaligned((__le32 *) @@ -207,7 +207,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_SUPPORTED_LIST: - DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); + DBG("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); length = sizeof (oid_supported_list); count = length / sizeof (u32); for (i = 0; i < count; i++) @@ -217,7 +217,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_HARDWARE_STATUS: - DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); + DBG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); /* Bogus question! * Hardware must be ready to receive high level protocols. * BTW: @@ -230,14 +230,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_MEDIA_SUPPORTED: - DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); + DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); retval = 0; break; /* mandatory */ case OID_GEN_MEDIA_IN_USE: - DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); + DBG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); /* one medium, one transport... (maybe you do it better) */ *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium); retval = 0; @@ -245,7 +245,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_MAXIMUM_FRAME_SIZE: - DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); + DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); @@ -256,7 +256,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_LINK_SPEED: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); + DBG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); if (rndis_per_dev_params [configNr].media_state == NDIS_MEDIA_STATE_DISCONNECTED) *outbuf = __constant_cpu_to_le32 (0); @@ -268,7 +268,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_TRANSMIT_BLOCK_SIZE: - DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); + DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); @@ -278,7 +278,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RECEIVE_BLOCK_SIZE: - DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); + DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].dev->mtu); @@ -288,7 +288,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_VENDOR_ID: - DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); + DBG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].vendorID); retval = 0; @@ -296,7 +296,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_VENDOR_DESCRIPTION: - DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); + DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); length = strlen (rndis_per_dev_params [configNr].vendorDescr); memcpy (outbuf, rndis_per_dev_params [configNr].vendorDescr, length); @@ -304,7 +304,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_VENDOR_DRIVER_VERSION: - DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); + DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); /* Created as LE */ *outbuf = rndis_driver_version; retval = 0; @@ -312,14 +312,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_CURRENT_PACKET_FILTER: - DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); + DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); *outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter); retval = 0; break; /* mandatory */ case OID_GEN_MAXIMUM_TOTAL_SIZE: - DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); + DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE); retval = 0; break; @@ -327,14 +327,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_MEDIA_CONNECT_STATUS: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); + DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .media_state); retval = 0; break; case OID_GEN_PHYSICAL_MEDIUM: - DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); + DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; @@ -344,7 +344,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, * versions emit undefined RNDIS messages. DOCUMENT ALL THESE! */ case OID_GEN_MAC_OPTIONS: /* from WinME */ - DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); + DBG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32( NDIS_MAC_OPTION_RECEIVE_SERIALIZED | NDIS_MAC_OPTION_FULL_DUPLEX); @@ -356,7 +356,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_XMIT_OK: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); + DBG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].stats->tx_packets - @@ -369,7 +369,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RCV_OK: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); + DBG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 ( rndis_per_dev_params [configNr].stats->rx_packets - @@ -382,7 +382,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_XMIT_ERROR: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); + DBG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_errors); @@ -393,7 +393,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RCV_ERROR: if (rndis_debug > 1) - DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); + DBG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_errors); @@ -403,7 +403,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_GEN_RCV_NO_BUFFER: - DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); + DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_dropped); @@ -413,7 +413,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, #ifdef RNDIS_OPTIONAL_STATS case OID_GEN_DIRECTED_BYTES_XMIT: - DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__); /* * Aunt Tilly's size of shoes * minus antarctica count of penguins @@ -433,7 +433,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_DIRECTED_FRAMES_XMIT: - DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); /* dito */ if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 ( @@ -449,7 +449,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_MULTICAST_BYTES_XMIT: - DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast*1234); @@ -458,7 +458,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_MULTICAST_FRAMES_XMIT: - DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast); @@ -467,7 +467,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_BROADCAST_BYTES_XMIT: - DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_packets/42*255); @@ -476,7 +476,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_BROADCAST_FRAMES_XMIT: - DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); + DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->tx_packets/42); @@ -485,19 +485,19 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_DIRECTED_BYTES_RCV: - DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; case OID_GEN_DIRECTED_FRAMES_RCV: - DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; case OID_GEN_MULTICAST_BYTES_RCV: - DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast * 1111); @@ -506,7 +506,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_MULTICAST_FRAMES_RCV: - DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->multicast); @@ -515,7 +515,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_BROADCAST_BYTES_RCV: - DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_packets/42*255); @@ -524,7 +524,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_BROADCAST_FRAMES_RCV: - DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); + DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_packets/42); @@ -533,7 +533,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_RCV_CRC_ERROR: - DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); + DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_crc_errors); @@ -542,7 +542,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, break; case OID_GEN_TRANSMIT_QUEUE_LENGTH: - DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); + DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; @@ -552,7 +552,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_PERMANENT_ADDRESS: - DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); + DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { length = ETH_ALEN; memcpy (outbuf, @@ -564,7 +564,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_CURRENT_ADDRESS: - DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); + DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); if (rndis_per_dev_params [configNr].dev) { length = ETH_ALEN; memcpy (outbuf, @@ -576,7 +576,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_MULTICAST_LIST: - DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); + DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); /* Multicast base address only */ *outbuf = __constant_cpu_to_le32 (0xE0000000); retval = 0; @@ -584,21 +584,21 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_MAXIMUM_LIST_SIZE: - DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); + DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); /* Multicast base address only */ *outbuf = __constant_cpu_to_le32 (1); retval = 0; break; case OID_802_3_MAC_OPTIONS: - DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); + DBG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); break; /* ieee802.3 statistics OIDs (table 4-4) */ /* mandatory */ case OID_802_3_RCV_ERROR_ALIGNMENT: - DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); + DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); if (rndis_per_dev_params [configNr].stats) { *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr] .stats->rx_frame_errors); @@ -608,51 +608,51 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, /* mandatory */ case OID_802_3_XMIT_ONE_COLLISION: - DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; /* mandatory */ case OID_802_3_XMIT_MORE_COLLISIONS: - DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); *outbuf = __constant_cpu_to_le32 (0); retval = 0; break; #ifdef RNDIS_OPTIONAL_STATS case OID_802_3_XMIT_DEFERRED: - DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_MAX_COLLISIONS: - DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__); /* TODO */ break; case OID_802_3_RCV_OVERRUN: - DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__); + DBG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_UNDERRUN: - DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_HEARTBEAT_FAILURE: - DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_TIMES_CRS_LOST: - DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__); /* TODO */ break; case OID_802_3_XMIT_LATE_COLLISIONS: - DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__); + DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__); /* TODO */ break; #endif /* RNDIS_OPTIONAL_STATS */ @@ -660,7 +660,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, #ifdef RNDIS_PM /* power management OIDs (table 4-5) */ case OID_PNP_CAPABILITIES: - DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); + DBG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); /* for now, no wakeup capabilities */ length = sizeof (struct NDIS_PNP_CAPABILITIES); @@ -668,7 +668,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, retval = 0; break; case OID_PNP_QUERY_POWER: - DEBUG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__, + DBG("%s: OID_PNP_QUERY_POWER D%d\n", __FUNCTION__, le32_to_cpu(get_unaligned((__le32 *)buf)) - 1); /* only suspend is a real power state, and * it can't be entered by OID_PNP_SET_POWER... @@ -705,9 +705,9 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, return -ENOMEM; if (buf_len && rndis_debug > 1) { - DEBUG("set OID %08x value, len %d:\n", OID, buf_len); + DBG("set OID %08x value, len %d:\n", OID, buf_len); for (i = 0; i < buf_len; i += 16) { - DEBUG ("%03d: %08x %08x %08x %08x\n", i, + DBG("%03d: %08x %08x %08x %08x\n", i, le32_to_cpu(get_unaligned((__le32 *) &buf[i])), le32_to_cpu(get_unaligned((__le32 *) @@ -731,7 +731,7 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, */ *params->filter = (u16) le32_to_cpu(get_unaligned( (__le32 *)buf)); - DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", + DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", __FUNCTION__, *params->filter); /* this call has a significant side effect: it's @@ -756,7 +756,7 @@ update_linkstate: case OID_802_3_MULTICAST_LIST: /* I think we can ignore this */ - DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); + DBG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); retval = 0; break; #if 0 @@ -764,7 +764,7 @@ update_linkstate: { struct rndis_config_parameter *param; param = (struct rndis_config_parameter *) buf; - DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", + DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", __FUNCTION__, min(cpu_to_le32(param->ParameterNameLength),80), buf + param->ParameterNameOffset); @@ -781,7 +781,7 @@ update_linkstate: * FIXME ... then things go batty; Windows wedges itself. */ i = le32_to_cpu(get_unaligned((__le32 *)buf)); - DEBUG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1); + DBG("%s: OID_PNP_SET_POWER D%d\n", __FUNCTION__, i - 1); switch (i) { case NdisDeviceStateD0: *params->filter = params->saved_filter; @@ -858,7 +858,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf) rndis_query_cmplt_type *resp; rndis_resp_t *r; - // DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID)); + // DBG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID)); if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; /* @@ -911,15 +911,15 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf) BufOffset = le32_to_cpu (buf->InformationBufferOffset); #ifdef VERBOSE - DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength); - DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset); - DEBUG("%s: InfoBuffer: ", __FUNCTION__); + DBG("%s: Length: %d\n", __FUNCTION__, BufLength); + DBG("%s: Offset: %d\n", __FUNCTION__, BufOffset); + DBG("%s: InfoBuffer: ", __FUNCTION__); for (i = 0; i < BufLength; i++) { - DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); + DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); } - DEBUG ("\n"); + DBG("\n"); #endif resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); @@ -1082,14 +1082,14 @@ int rndis_msg_parser (u8 configNr, u8 *buf) /* For USB: responses may take up to 10 seconds */ switch (MsgType) { case REMOTE_NDIS_INITIALIZE_MSG: - DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", + DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", __FUNCTION__ ); params->state = RNDIS_INITIALIZED; return rndis_init_response (configNr, (rndis_init_msg_type *) buf); case REMOTE_NDIS_HALT_MSG: - DEBUG("%s: REMOTE_NDIS_HALT_MSG\n", + DBG("%s: REMOTE_NDIS_HALT_MSG\n", __FUNCTION__ ); params->state = RNDIS_UNINITIALIZED; if (params->dev) { @@ -1107,7 +1107,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf) (rndis_set_msg_type *) buf); case REMOTE_NDIS_RESET_MSG: - DEBUG("%s: REMOTE_NDIS_RESET_MSG\n", + DBG("%s: REMOTE_NDIS_RESET_MSG\n", __FUNCTION__ ); return rndis_reset_response (configNr, (rndis_reset_msg_type *) buf); @@ -1115,7 +1115,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf) case REMOTE_NDIS_KEEPALIVE_MSG: /* For USB: host does this every 5 seconds */ if (rndis_debug > 1) - DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", + DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", __FUNCTION__ ); return rndis_keepalive_response (configNr, (rndis_keepalive_msg_type *) @@ -1132,7 +1132,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf) { unsigned i; for (i = 0; i < MsgLength; i += 16) { - DEBUG ("%03d: " + DBG("%03d: " " %02x %02x %02x %02x" " %02x %02x %02x %02x" " %02x %02x %02x %02x" @@ -1163,18 +1163,18 @@ int rndis_register (int (* rndis_control_ack) (struct net_device *)) if (!rndis_per_dev_params [i].used) { rndis_per_dev_params [i].used = 1; rndis_per_dev_params [i].ack = rndis_control_ack; - DEBUG("%s: configNr = %d\n", __FUNCTION__, i); + DBG("%s: configNr = %d\n", __FUNCTION__, i); return i; } } - DEBUG("failed\n"); + DBG("failed\n"); return -1; } void rndis_deregister (int configNr) { - DEBUG("%s: \n", __FUNCTION__ ); + DBG("%s: \n", __FUNCTION__ ); if (configNr >= RNDIS_MAX_CONFIGS) return; rndis_per_dev_params [configNr].used = 0; @@ -1186,7 +1186,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev, struct net_device_stats *stats, u16 *cdc_filter) { - DEBUG("%s:\n", __FUNCTION__ ); + DBG("%s:\n", __FUNCTION__ ); if (!dev || !stats) return -1; if (configNr >= RNDIS_MAX_CONFIGS) return -1; @@ -1199,7 +1199,7 @@ int rndis_set_param_dev (u8 configNr, struct net_device *dev, int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) { - DEBUG("%s:\n", __FUNCTION__ ); + DBG("%s:\n", __FUNCTION__ ); if (!vendorDescr) return -1; if (configNr >= RNDIS_MAX_CONFIGS) return -1; @@ -1211,7 +1211,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) { - DEBUG("%s: %u %u\n", __FUNCTION__, medium, speed); + DBG("%s: %u %u\n", __FUNCTION__, medium, speed); if (configNr >= RNDIS_MAX_CONFIGS) return -1; rndis_per_dev_params [configNr].medium = medium; @@ -1390,7 +1390,7 @@ static int rndis_proc_write (struct file *file, const char __user *buffer, break; default: if (fl_speed) p->speed = speed; - else DEBUG ("%c is not valid\n", c); + else DBG("%c is not valid\n", c); break; } @@ -1419,12 +1419,12 @@ int __devinit rndis_init (void) if (!(rndis_connect_state [i] = create_proc_entry (name, 0660, NULL))) { - DEBUG ("%s :remove entries", __FUNCTION__); + DBG("%s :remove entries", __FUNCTION__); while (i) { sprintf (name, NAME_TEMPLATE, --i); remove_proc_entry (name, NULL); } - DEBUG ("\n"); + DBG("\n"); return -EIO; } diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c new file mode 100644 index 0000000..0be80c6 --- /dev/null +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -0,0 +1,2045 @@ +/* + * linux/drivers/usb/gadget/s3c2410_udc.c + * + * Samsung S3C24xx series on-chip full speed USB device controllers + * + * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard + * Additional cleanups by Ben Dooks <ben-linux@fluff.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/version.h> +#include <linux/clk.h> + +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +#include <linux/usb.h> +#include <linux/usb_gadget.h> + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/unaligned.h> +#include <asm/arch/irqs.h> + +#include <asm/arch/hardware.h> +#include <asm/arch/regs-clock.h> +#include <asm/arch/regs-gpio.h> +#include <asm/arch/regs-udc.h> +#include <asm/arch/udc.h> + +#include <asm/mach-types.h> + +#include "s3c2410_udc.h" + +#define DRIVER_DESC "S3C2410 USB Device Controller Gadget" +#define DRIVER_VERSION "29 Apr 2007" +#define DRIVER_AUTHOR "Herbert Pötzl <herbert@13thfloor.at>, " \ + "Arnaud Patard <arnaud.patard@rtp-net.org>" + +static const char gadget_name[] = "s3c2410_udc"; +static const char driver_desc[] = DRIVER_DESC; + +static struct s3c2410_udc *the_controller; +static struct clk *udc_clock; +static struct clk *usb_bus_clock; +static void __iomem *base_addr; +static u64 rsrc_start; +static u64 rsrc_len; +static struct dentry *s3c2410_udc_debugfs_root; + +static inline u32 udc_read(u32 reg) +{ + return readb(base_addr + reg); +} + +static inline void udc_write(u32 value, u32 reg) +{ + writeb(value, base_addr + reg); +} + +static inline void udc_writeb(void __iomem *base, u32 value, u32 reg) +{ + writeb(value, base + reg); +} + +static struct s3c2410_udc_mach_info *udc_info; + +/*************************** DEBUG FUNCTION ***************************/ +#define DEBUG_NORMAL 1 +#define DEBUG_VERBOSE 2 + +#ifdef CONFIG_USB_S3C2410_DEBUG +#define USB_S3C2410_DEBUG_LEVEL 0 + +static uint32_t s3c2410_ticks = 0; + +static int dprintk(int level, const char *fmt, ...) +{ + static char printk_buf[1024]; + static long prevticks; + static int invocation; + va_list args; + int len; + + if (level > USB_S3C2410_DEBUG_LEVEL) + return 0; + + if (s3c2410_ticks != prevticks) { + prevticks = s3c2410_ticks; + invocation = 0; + } + + len = scnprintf(printk_buf, + sizeof(printk_buf), "%1lu.%02d USB: ", + prevticks, invocation++); + + va_start(args, fmt); + len = vscnprintf(printk_buf+len, + sizeof(printk_buf)-len, fmt, args); + va_end(args); + + return printk(KERN_DEBUG "%s", printk_buf); +} +#else +static int dprintk(int level, const char *fmt, ...) +{ + return 0; +} +#endif +static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) +{ + u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg; + u32 ep_int_en_reg, usb_int_en_reg, ep0_csr; + u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2; + u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2; + + addr_reg = udc_read(S3C2410_UDC_FUNC_ADDR_REG); + pwr_reg = udc_read(S3C2410_UDC_PWR_REG); + ep_int_reg = udc_read(S3C2410_UDC_EP_INT_REG); + usb_int_reg = udc_read(S3C2410_UDC_USB_INT_REG); + ep_int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); + usb_int_en_reg = udc_read(S3C2410_UDC_USB_INT_EN_REG); + udc_write(0, S3C2410_UDC_INDEX_REG); + ep0_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + udc_write(1, S3C2410_UDC_INDEX_REG); + ep1_i_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + ep1_i_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG); + ep1_o_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + ep1_o_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG); + udc_write(2, S3C2410_UDC_INDEX_REG); + ep2_i_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + ep2_i_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG); + ep2_o_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + ep2_o_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG); + + seq_printf(m, "FUNC_ADDR_REG : 0x%04X\n" + "PWR_REG : 0x%04X\n" + "EP_INT_REG : 0x%04X\n" + "USB_INT_REG : 0x%04X\n" + "EP_INT_EN_REG : 0x%04X\n" + "USB_INT_EN_REG : 0x%04X\n" + "EP0_CSR : 0x%04X\n" + "EP1_I_CSR1 : 0x%04X\n" + "EP1_I_CSR2 : 0x%04X\n" + "EP1_O_CSR1 : 0x%04X\n" + "EP1_O_CSR2 : 0x%04X\n" + "EP2_I_CSR1 : 0x%04X\n" + "EP2_I_CSR2 : 0x%04X\n" + "EP2_O_CSR1 : 0x%04X\n" + "EP2_O_CSR2 : 0x%04X\n", + addr_reg,pwr_reg,ep_int_reg,usb_int_reg, + ep_int_en_reg, usb_int_en_reg, ep0_csr, + ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2, + ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2 + ); + + return 0; +} + +static int s3c2410_udc_debugfs_fops_open(struct inode *inode, + struct file *file) +{ + return single_open(file, s3c2410_udc_debugfs_seq_show, NULL); +} + +static const struct file_operations s3c2410_udc_debugfs_fops = { + .open = s3c2410_udc_debugfs_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +/* io macros */ + +static inline void s3c2410_udc_clear_ep0_opr(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, S3C2410_UDC_EP0_CSR_SOPKTRDY, + S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_clear_ep0_sst(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + writeb(0x00, base + S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_clear_ep0_se(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, S3C2410_UDC_EP0_CSR_SSE, S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_ipr(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_de(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG); +} + +inline void s3c2410_udc_set_ep0_ss(void __iomem *b) +{ + udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + + udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY + | S3C2410_UDC_EP0_CSR_DE), + S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY + | S3C2410_UDC_EP0_CSR_SSE), + S3C2410_UDC_EP0_CSR_REG); +} + +static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base) +{ + udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY + | S3C2410_UDC_EP0_CSR_DE), + S3C2410_UDC_EP0_CSR_REG); +} + +/*------------------------- I/O ----------------------------------*/ + +/* + * s3c2410_udc_done + */ +static void s3c2410_udc_done(struct s3c2410_ep *ep, + struct s3c2410_request *req, int status) +{ + unsigned halted = ep->halted; + + list_del_init(&req->queue); + + if (likely (req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + ep->halted = 1; + req->req.complete(&ep->ep, &req->req); + ep->halted = halted; +} + +static void s3c2410_udc_nuke(struct s3c2410_udc *udc, + struct s3c2410_ep *ep, int status) +{ + /* Sanity check */ + if (&ep->queue == NULL) + return; + + while (!list_empty (&ep->queue)) { + struct s3c2410_request *req; + req = list_entry (ep->queue.next, struct s3c2410_request, + queue); + s3c2410_udc_done(ep, req, status); + } +} + +static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev) +{ + unsigned i; + + /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint + * fifos, and pending transactions mustn't be continued in any case. + */ + + for (i = 1; i < S3C2410_ENDPOINTS; i++) + s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED); +} + +static inline int s3c2410_udc_fifo_count_out(void) +{ + int tmp; + + tmp = udc_read(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8; + tmp |= udc_read(S3C2410_UDC_OUT_FIFO_CNT1_REG); + return tmp; +} + +/* + * s3c2410_udc_write_packet + */ +static inline int s3c2410_udc_write_packet(int fifo, + struct s3c2410_request *req, + unsigned max) +{ + unsigned len = min(req->req.length - req->req.actual, max); + u8 *buf = req->req.buf + req->req.actual; + + prefetch(buf); + + dprintk(DEBUG_VERBOSE, "%s %d %d %d %d\n", __func__, + req->req.actual, req->req.length, len, req->req.actual + len); + + req->req.actual += len; + + udelay(5); + writesb(base_addr + fifo, buf, len); + return len; +} + +/* + * s3c2410_udc_write_fifo + * + * return: 0 = still running, 1 = completed, negative = errno + */ +static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep, + struct s3c2410_request *req) +{ + unsigned count; + int is_last; + u32 idx; + int fifo_reg; + u32 ep_csr; + + idx = ep->bEndpointAddress & 0x7F; + switch (idx) { + default: + idx = 0; + case 0: + fifo_reg = S3C2410_UDC_EP0_FIFO_REG; + break; + case 1: + fifo_reg = S3C2410_UDC_EP1_FIFO_REG; + break; + case 2: + fifo_reg = S3C2410_UDC_EP2_FIFO_REG; + break; + case 3: + fifo_reg = S3C2410_UDC_EP3_FIFO_REG; + break; + case 4: + fifo_reg = S3C2410_UDC_EP4_FIFO_REG; + break; + } + + count = s3c2410_udc_write_packet(fifo_reg, req, ep->ep.maxpacket); + + /* last packet is often short (sometimes a zlp) */ + if (count != ep->ep.maxpacket) + is_last = 1; + else if (req->req.length != req->req.actual || req->req.zero) + is_last = 0; + else + is_last = 2; + + /* Only ep0 debug messages are interesting */ + if (idx == 0) + dprintk(DEBUG_NORMAL, + "Written ep%d %d.%d of %d b [last %d,z %d]\n", + idx, count, req->req.actual, req->req.length, + is_last, req->req.zero); + + if (is_last) { + /* The order is important. It prevents sending 2 packets + * at the same time */ + + if (idx == 0) { + /* Reset signal => no need to say 'data sent' */ + if (! (udc_read(S3C2410_UDC_USB_INT_REG) + & S3C2410_UDC_USBINT_RESET)) + s3c2410_udc_set_ep0_de_in(base_addr); + ep->dev->ep0state=EP0_IDLE; + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY, + S3C2410_UDC_IN_CSR1_REG); + } + + s3c2410_udc_done(ep, req, 0); + is_last = 1; + } else { + if (idx == 0) { + /* Reset signal => no need to say 'data sent' */ + if (! (udc_read(S3C2410_UDC_USB_INT_REG) + & S3C2410_UDC_USBINT_RESET)) + s3c2410_udc_set_ep0_ipr(base_addr); + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY, + S3C2410_UDC_IN_CSR1_REG); + } + } + + return is_last; +} + +static inline int s3c2410_udc_read_packet(int fifo, u8 *buf, + struct s3c2410_request *req, unsigned avail) +{ + unsigned len; + + len = min(req->req.length - req->req.actual, avail); + req->req.actual += len; + + readsb(fifo + base_addr, buf, len); + return len; +} + +/* + * return: 0 = still running, 1 = queue empty, negative = errno + */ +static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep, + struct s3c2410_request *req) +{ + u8 *buf; + u32 ep_csr; + unsigned bufferspace; + int is_last=1; + unsigned avail; + int fifo_count = 0; + u32 idx; + int fifo_reg; + + idx = ep->bEndpointAddress & 0x7F; + + switch (idx) { + default: + idx = 0; + case 0: + fifo_reg = S3C2410_UDC_EP0_FIFO_REG; + break; + case 1: + fifo_reg = S3C2410_UDC_EP1_FIFO_REG; + break; + case 2: + fifo_reg = S3C2410_UDC_EP2_FIFO_REG; + break; + case 3: + fifo_reg = S3C2410_UDC_EP3_FIFO_REG; + break; + case 4: + fifo_reg = S3C2410_UDC_EP4_FIFO_REG; + break; + } + + if (!req->req.length) + return 1; + + buf = req->req.buf + req->req.actual; + bufferspace = req->req.length - req->req.actual; + if (!bufferspace) { + dprintk(DEBUG_NORMAL, "%s: buffer full!\n", __func__); + return -1; + } + + udc_write(idx, S3C2410_UDC_INDEX_REG); + + fifo_count = s3c2410_udc_fifo_count_out(); + dprintk(DEBUG_NORMAL, "%s fifo count : %d\n", __func__, fifo_count); + + if (fifo_count > ep->ep.maxpacket) + avail = ep->ep.maxpacket; + else + avail = fifo_count; + + fifo_count = s3c2410_udc_read_packet(fifo_reg, buf, req, avail); + + /* checking this with ep0 is not accurate as we already + * read a control request + **/ + if (idx != 0 && fifo_count < ep->ep.maxpacket) { + is_last = 1; + /* overflowed this request? flush extra data */ + if (fifo_count != avail) + req->req.status = -EOVERFLOW; + } else { + is_last = (req->req.length <= req->req.actual) ? 1 : 0; + } + + udc_write(idx, S3C2410_UDC_INDEX_REG); + fifo_count = s3c2410_udc_fifo_count_out(); + + /* Only ep0 debug messages are interesting */ + if (idx == 0) + dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n", + __func__, fifo_count,is_last); + + if (is_last) { + if (idx == 0) { + s3c2410_udc_set_ep0_de_out(base_addr); + ep->dev->ep0state = EP0_IDLE; + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY, + S3C2410_UDC_OUT_CSR1_REG); + } + + s3c2410_udc_done(ep, req, 0); + } else { + if (idx == 0) { + s3c2410_udc_clear_ep0_opr(base_addr); + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY, + S3C2410_UDC_OUT_CSR1_REG); + } + } + + return is_last; +} + +static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq) +{ + unsigned char *outbuf = (unsigned char*)crq; + int bytes_read = 0; + + udc_write(0, S3C2410_UDC_INDEX_REG); + + bytes_read = s3c2410_udc_fifo_count_out(); + + dprintk(DEBUG_NORMAL, "%s: fifo_count=%d\n", __func__, bytes_read); + + if (bytes_read > sizeof(struct usb_ctrlrequest)) + bytes_read = sizeof(struct usb_ctrlrequest); + + readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read); + + dprintk(DEBUG_VERBOSE, "%s: len=%d %02x:%02x {%x,%x,%x}\n", __func__, + bytes_read, crq->bRequest, crq->bRequestType, + crq->wValue, crq->wIndex, crq->wLength); + + return bytes_read; +} + +static int s3c2410_udc_get_status(struct s3c2410_udc *dev, + struct usb_ctrlrequest *crq) +{ + u16 status = 0; + u8 ep_num = crq->wIndex & 0x7F; + u8 is_in = crq->wIndex & USB_DIR_IN; + + switch (crq->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + break; + + case USB_RECIP_DEVICE: + status = dev->devstatus; + break; + + case USB_RECIP_ENDPOINT: + if (ep_num > 4 || crq->wLength > 2) + return 1; + + if (ep_num == 0) { + udc_write(0, S3C2410_UDC_INDEX_REG); + status = udc_read(S3C2410_UDC_IN_CSR1_REG); + status = status & S3C2410_UDC_EP0_CSR_SENDSTL; + } else { + udc_write(ep_num, S3C2410_UDC_INDEX_REG); + if (is_in) { + status = udc_read(S3C2410_UDC_IN_CSR1_REG); + status = status & S3C2410_UDC_ICSR1_SENDSTL; + } else { + status = udc_read(S3C2410_UDC_OUT_CSR1_REG); + status = status & S3C2410_UDC_OCSR1_SENDSTL; + } + } + + status = status ? 1 : 0; + break; + + default: + return 1; + } + + /* Seems to be needed to get it working. ouch :( */ + udelay(5); + udc_write(status & 0xFF, S3C2410_UDC_EP0_FIFO_REG); + udc_write(status >> 8, S3C2410_UDC_EP0_FIFO_REG); + s3c2410_udc_set_ep0_de_in(base_addr); + + return 0; +} +/*------------------------- usb state machine -------------------------------*/ +static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value); + +static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, + struct s3c2410_ep *ep, + struct usb_ctrlrequest *crq, + u32 ep0csr) +{ + int len, ret, tmp; + + /* start control request? */ + if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY)) + return; + + s3c2410_udc_nuke(dev, ep, -EPROTO); + + len = s3c2410_udc_read_fifo_crq(crq); + if (len != sizeof(*crq)) { + dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR" + " wanted %d bytes got %d. Stalling out...\n", + sizeof(*crq), len); + s3c2410_udc_set_ep0_ss(base_addr); + return; + } + + dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n", + crq->bRequest, crq->bRequestType, crq->wLength); + + /* cope with automagic for some standard requests. */ + dev->req_std = (crq->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + dev->req_config = 0; + dev->req_pending = 1; + + switch (crq->bRequest) { + case USB_REQ_SET_CONFIGURATION: + dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n"); + + if (crq->bRequestType == USB_RECIP_DEVICE) { + dev->req_config = 1; + s3c2410_udc_set_ep0_de_out(base_addr); + } + break; + + case USB_REQ_SET_INTERFACE: + dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n"); + + if (crq->bRequestType == USB_RECIP_INTERFACE) { + dev->req_config = 1; + s3c2410_udc_set_ep0_de_out(base_addr); + } + break; + + case USB_REQ_SET_ADDRESS: + dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n"); + + if (crq->bRequestType == USB_RECIP_DEVICE) { + tmp = crq->wValue & 0x7F; + dev->address = tmp; + udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE), + S3C2410_UDC_FUNC_ADDR_REG); + s3c2410_udc_set_ep0_de_out(base_addr); + return; + } + break; + + case USB_REQ_GET_STATUS: + dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n"); + s3c2410_udc_clear_ep0_opr(base_addr); + + if (dev->req_std) { + if (!s3c2410_udc_get_status(dev, crq)) { + return; + } + } + break; + + case USB_REQ_CLEAR_FEATURE: + s3c2410_udc_clear_ep0_opr(base_addr); + + if (crq->bRequestType != USB_RECIP_ENDPOINT) + break; + + if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0) + break; + + s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0); + s3c2410_udc_set_ep0_de_out(base_addr); + return; + + case USB_REQ_SET_FEATURE: + s3c2410_udc_clear_ep0_opr(base_addr); + + if (crq->bRequestType != USB_RECIP_ENDPOINT) + break; + + if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0) + break; + + s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1); + s3c2410_udc_set_ep0_de_out(base_addr); + return; + + default: + s3c2410_udc_clear_ep0_opr(base_addr); + break; + } + + if (crq->bRequestType & USB_DIR_IN) + dev->ep0state = EP0_IN_DATA_PHASE; + else + dev->ep0state = EP0_OUT_DATA_PHASE; + + ret = dev->driver->setup(&dev->gadget, crq); + if (ret < 0) { + if (dev->req_config) { + dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n", + crq->bRequest, ret); + return; + } + + if (ret == -EOPNOTSUPP) + dprintk(DEBUG_NORMAL, "Operation not supported\n"); + else + dprintk(DEBUG_NORMAL, + "dev->driver->setup failed. (%d)\n", ret); + + udelay(5); + s3c2410_udc_set_ep0_ss(base_addr); + s3c2410_udc_set_ep0_de_out(base_addr); + dev->ep0state = EP0_IDLE; + /* deferred i/o == no response yet */ + } else if (dev->req_pending) { + dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n"); + dev->req_pending=0; + } + + dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]); +} + +static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev) +{ + u32 ep0csr; + struct s3c2410_ep *ep = &dev->ep[0]; + struct s3c2410_request *req; + struct usb_ctrlrequest crq; + + if (list_empty(&ep->queue)) + req = NULL; + else + req = list_entry(ep->queue.next, struct s3c2410_request, queue); + + /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to + * S3C2410_UDC_EP0_CSR_REG when index is zero */ + + udc_write(0, S3C2410_UDC_INDEX_REG); + ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + + dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n", + ep0csr, ep0states[dev->ep0state]); + + /* clear stall status */ + if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) { + s3c2410_udc_nuke(dev, ep, -EPIPE); + dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n"); + s3c2410_udc_clear_ep0_sst(base_addr); + dev->ep0state = EP0_IDLE; + return; + } + + /* clear setup end */ + if (ep0csr & S3C2410_UDC_EP0_CSR_SE) { + dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n"); + s3c2410_udc_nuke(dev, ep, 0); + s3c2410_udc_clear_ep0_se(base_addr); + dev->ep0state = EP0_IDLE; + } + + switch (dev->ep0state) { + case EP0_IDLE: + s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); + break; + + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ + dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n"); + if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) { + s3c2410_udc_write_fifo(ep, req); + } + break; + + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ + dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n"); + if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) { + s3c2410_udc_read_fifo(ep,req); + } + break; + + case EP0_END_XFER: + dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n"); + dev->ep0state = EP0_IDLE; + break; + + case EP0_STALL: + dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n"); + dev->ep0state = EP0_IDLE; + break; + } +} + +/* + * handle_ep - Manage I/O endpoints + */ + +static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep) +{ + struct s3c2410_request *req; + int is_in = ep->bEndpointAddress & USB_DIR_IN; + u32 ep_csr1; + u32 idx; + + if (likely (!list_empty(&ep->queue))) + req = list_entry(ep->queue.next, + struct s3c2410_request, queue); + else + req = NULL; + + idx = ep->bEndpointAddress & 0x7F; + + if (is_in) { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); + dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n", + idx, ep_csr1, req ? 1 : 0); + + if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) { + dprintk(DEBUG_VERBOSE, "st\n"); + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, + S3C2410_UDC_IN_CSR1_REG); + return; + } + + if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) { + s3c2410_udc_write_fifo(ep,req); + } + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG); + dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1); + + if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) { + udc_write(idx, S3C2410_UDC_INDEX_REG); + udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, + S3C2410_UDC_OUT_CSR1_REG); + return; + } + + if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) { + s3c2410_udc_read_fifo(ep,req); + } + } +} + +#include <asm/arch/regs-irq.h> + +/* + * s3c2410_udc_irq - interrupt handler + */ +static irqreturn_t s3c2410_udc_irq(int irq, void *_dev) +{ + struct s3c2410_udc *dev = _dev; + int usb_status; + int usbd_status; + int pwr_reg; + int ep0csr; + int i; + u32 idx; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + /* Driver connected ? */ + if (!dev->driver) { + /* Clear interrupts */ + udc_write(udc_read(S3C2410_UDC_USB_INT_REG), + S3C2410_UDC_USB_INT_REG); + udc_write(udc_read(S3C2410_UDC_EP_INT_REG), + S3C2410_UDC_EP_INT_REG); + } + + /* Save index */ + idx = udc_read(S3C2410_UDC_INDEX_REG); + + /* Read status registers */ + usb_status = udc_read(S3C2410_UDC_USB_INT_REG); + usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); + pwr_reg = udc_read(S3C2410_UDC_PWR_REG); + + udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); + ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + + dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", + usb_status, usbd_status, pwr_reg, ep0csr); + + /* + * Now, handle interrupts. There's two types : + * - Reset, Resume, Suspend coming -> usb_int_reg + * - EP -> ep_int_reg + */ + + /* RESET */ + if (usb_status & S3C2410_UDC_USBINT_RESET) { + /* two kind of reset : + * - reset start -> pwr reg = 8 + * - reset end -> pwr reg = 0 + **/ + dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n", + ep0csr, pwr_reg); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + udc_write(0x00, S3C2410_UDC_INDEX_REG); + udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3, + S3C2410_UDC_MAXP_REG); + dev->address = 0; + + dev->ep0state = EP0_IDLE; + dev->gadget.speed = USB_SPEED_FULL; + + /* clear interrupt */ + udc_write(S3C2410_UDC_USBINT_RESET, + S3C2410_UDC_USB_INT_REG); + + udc_write(idx, S3C2410_UDC_INDEX_REG); + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_HANDLED; + } + + /* RESUME */ + if (usb_status & S3C2410_UDC_USBINT_RESUME) { + dprintk(DEBUG_NORMAL, "USB resume\n"); + + /* clear interrupt */ + udc_write(S3C2410_UDC_USBINT_RESUME, + S3C2410_UDC_USB_INT_REG); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume) + dev->driver->resume(&dev->gadget); + } + + /* SUSPEND */ + if (usb_status & S3C2410_UDC_USBINT_SUSPEND) { + dprintk(DEBUG_NORMAL, "USB suspend\n"); + + /* clear interrupt */ + udc_write(S3C2410_UDC_USBINT_SUSPEND, + S3C2410_UDC_USB_INT_REG); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + + dev->ep0state = EP0_IDLE; + } + + /* EP */ + /* control traffic */ + /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready + * generate an interrupt + */ + if (usbd_status & S3C2410_UDC_INT_EP0) { + dprintk(DEBUG_VERBOSE, "USB ep0 irq\n"); + /* Clear the interrupt bit by setting it to 1 */ + udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG); + s3c2410_udc_handle_ep0(dev); + } + + /* endpoint data transfers */ + for (i = 1; i < S3C2410_ENDPOINTS; i++) { + u32 tmp = 1 << i; + if (usbd_status & tmp) { + dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i); + + /* Clear the interrupt bit by setting it to 1 */ + udc_write(tmp, S3C2410_UDC_EP_INT_REG); + s3c2410_udc_handle_ep(&dev->ep[i]); + } + } + + dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", irq); + + /* Restore old index */ + udc_write(idx, S3C2410_UDC_INDEX_REG); + + spin_unlock_irqrestore(&dev->lock, flags); + + return IRQ_HANDLED; +} +/*------------------------- s3c2410_ep_ops ----------------------------------*/ + +static inline struct s3c2410_ep *to_s3c2410_ep(struct usb_ep *ep) +{ + return container_of(ep, struct s3c2410_ep, ep); +} + +static inline struct s3c2410_udc *to_s3c2410_udc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct s3c2410_udc, gadget); +} + +static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req) +{ + return container_of(req, struct s3c2410_request, req); +} + +/* + * s3c2410_udc_ep_enable + */ +static int s3c2410_udc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct s3c2410_udc *dev; + struct s3c2410_ep *ep; + u32 max, tmp; + unsigned long flags; + u32 csr1,csr2; + u32 int_en_reg; + + ep = to_s3c2410_ep(_ep); + + if (!_ep || !desc || ep->desc + || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT) + return -EINVAL; + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + + local_irq_save (flags); + _ep->maxpacket = max & 0x7ff; + ep->desc = desc; + ep->halted = 0; + ep->bEndpointAddress = desc->bEndpointAddress; + + /* set max packet */ + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(max >> 3, S3C2410_UDC_MAXP_REG); + + /* set type, direction, address; reset fifo counters */ + if (desc->bEndpointAddress & USB_DIR_IN) { + csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT; + csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN; + + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr1, S3C2410_UDC_IN_CSR1_REG); + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr2, S3C2410_UDC_IN_CSR2_REG); + } else { + /* don't flush in fifo or it will cause endpoint interrupt */ + csr1 = S3C2410_UDC_ICSR1_CLRDT; + csr2 = S3C2410_UDC_ICSR2_DMAIEN; + + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr1, S3C2410_UDC_IN_CSR1_REG); + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr2, S3C2410_UDC_IN_CSR2_REG); + + csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT; + csr2 = S3C2410_UDC_OCSR2_DMAIEN; + + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG); + udc_write(ep->num, S3C2410_UDC_INDEX_REG); + udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG); + } + + /* enable irqs */ + int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); + udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG); + + /* print some debug message */ + tmp = desc->bEndpointAddress; + dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n", + _ep->name,ep->num, tmp, + desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max); + + local_irq_restore (flags); + s3c2410_udc_set_halt(_ep, 0); + + return 0; +} + +/* + * s3c2410_udc_ep_disable + */ +static int s3c2410_udc_ep_disable(struct usb_ep *_ep) +{ + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + unsigned long flags; + u32 int_en_reg; + + if (!_ep || !ep->desc) { + dprintk(DEBUG_NORMAL, "%s not enabled\n", + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + local_irq_save(flags); + + dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name); + + ep->desc = NULL; + ep->halted = 1; + + s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN); + + /* disable irqs */ + int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); + udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG); + + local_irq_restore(flags); + + dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name); + + return 0; +} + +/* + * s3c2410_udc_alloc_request + */ +static struct usb_request * +s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags) +{ + struct s3c2410_request *req; + + dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags); + + if (!_ep) + return NULL; + + req = kzalloc (sizeof(struct s3c2410_request), mem_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD (&req->queue); + return &req->req; +} + +/* + * s3c2410_udc_free_request + */ +static void +s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + struct s3c2410_request *req = to_s3c2410_req(_req); + + dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); + + if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) + return; + + WARN_ON (!list_empty (&req->queue)); + kfree(req); +} + +/* + * s3c2410_udc_queue + */ +static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct s3c2410_request *req = to_s3c2410_req(_req); + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + struct s3c2410_udc *dev; + u32 ep_csr = 0; + int fifo_count = 0; + unsigned long flags; + + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__); + return -EINVAL; + } + + dev = ep->dev; + if (unlikely (!dev->driver + || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + return -ESHUTDOWN; + } + + local_irq_save (flags); + + if (unlikely(!_req || !_req->complete + || !_req->buf || !list_empty(&req->queue))) { + if (!_req) + dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__); + else { + dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", + __func__, !_req->complete,!_req->buf, + !list_empty(&req->queue)); + } + + local_irq_restore(flags); + return -EINVAL; + } + + _req->status = -EINPROGRESS; + _req->actual = 0; + + dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n", + __func__, ep->bEndpointAddress, _req->length); + + if (ep->bEndpointAddress) { + udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG); + + ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) + ? S3C2410_UDC_IN_CSR1_REG + : S3C2410_UDC_OUT_CSR1_REG); + fifo_count = s3c2410_udc_fifo_count_out(); + } else { + udc_write(0, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); + fifo_count = s3c2410_udc_fifo_count_out(); + } + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->halted) { + if (ep->bEndpointAddress == 0 /* ep0 */) { + switch (dev->ep0state) { + case EP0_IN_DATA_PHASE: + if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) + && s3c2410_udc_write_fifo(ep, + req)) { + dev->ep0state = EP0_IDLE; + req = NULL; + } + break; + + case EP0_OUT_DATA_PHASE: + if ((!_req->length) + || ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) + && s3c2410_udc_read_fifo(ep, + req))) { + dev->ep0state = EP0_IDLE; + req = NULL; + } + break; + + default: + local_irq_restore(flags); + return -EL2HLT; + } + } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 + && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) + && s3c2410_udc_write_fifo(ep, req)) { + req = NULL; + } else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) + && fifo_count + && s3c2410_udc_read_fifo(ep, req)) { + req = NULL; + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely (req != 0)) + list_add_tail(&req->queue, &ep->queue); + + local_irq_restore(flags); + + dprintk(DEBUG_VERBOSE, "%s ok\n", __func__); + return 0; +} + +/* + * s3c2410_udc_dequeue + */ +static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + struct s3c2410_udc *udc; + int retval = -EINVAL; + unsigned long flags; + struct s3c2410_request *req = NULL; + + dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); + + if (!the_controller->driver) + return -ESHUTDOWN; + + if (!_ep || !_req) + return retval; + + udc = to_s3c2410_udc(ep->gadget); + + local_irq_save (flags); + + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) { + list_del_init (&req->queue); + _req->status = -ECONNRESET; + retval = 0; + break; + } + } + + if (retval == 0) { + dprintk(DEBUG_VERBOSE, + "dequeued req %p from %s, len %d buf %p\n", + req, _ep->name, _req->length, _req->buf); + + s3c2410_udc_done(ep, req, -ECONNRESET); + } + + local_irq_restore (flags); + return retval; +} + +/* + * s3c2410_udc_set_halt + */ +static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value) +{ + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); + u32 ep_csr = 0; + unsigned long flags; + u32 idx; + + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { + dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__); + return -EINVAL; + } + + local_irq_save (flags); + + idx = ep->bEndpointAddress & 0x7F; + + if (idx == 0) { + s3c2410_udc_set_ep0_ss(base_addr); + s3c2410_udc_set_ep0_de_out(base_addr); + } else { + udc_write(idx, S3C2410_UDC_INDEX_REG); + ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN) + ? S3C2410_UDC_IN_CSR1_REG + : S3C2410_UDC_OUT_CSR1_REG); + + if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { + if (value) + udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL, + S3C2410_UDC_IN_CSR1_REG); + else { + ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL; + udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG); + ep_csr |= S3C2410_UDC_ICSR1_CLRDT; + udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG); + } + } else { + if (value) + udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL, + S3C2410_UDC_OUT_CSR1_REG); + else { + ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL; + udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG); + ep_csr |= S3C2410_UDC_OCSR1_CLRDT; + udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG); + } + } + } + + ep->halted = value ? 1 : 0; + local_irq_restore (flags); + + return 0; +} + +static const struct usb_ep_ops s3c2410_ep_ops = { + .enable = s3c2410_udc_ep_enable, + .disable = s3c2410_udc_ep_disable, + + .alloc_request = s3c2410_udc_alloc_request, + .free_request = s3c2410_udc_free_request, + + .queue = s3c2410_udc_queue, + .dequeue = s3c2410_udc_dequeue, + + .set_halt = s3c2410_udc_set_halt, +}; + +/*------------------------- usb_gadget_ops ----------------------------------*/ + +/* + * s3c2410_udc_get_frame + */ +static int s3c2410_udc_get_frame(struct usb_gadget *_gadget) +{ + int tmp; + + dprintk(DEBUG_VERBOSE, "%s()\n", __func__); + + tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8; + tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG); + return tmp; +} + +/* + * s3c2410_udc_wakeup + */ +static int s3c2410_udc_wakeup(struct usb_gadget *_gadget) +{ + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + return 0; +} + +/* + * s3c2410_udc_set_selfpowered + */ +static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value) +{ + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + if (value) + udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); + else + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); + + return 0; +} + +static void s3c2410_udc_disable(struct s3c2410_udc *dev); +static void s3c2410_udc_enable(struct s3c2410_udc *dev); + +static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on) +{ + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + if (udc_info && udc_info->udc_command) { + if (is_on) + s3c2410_udc_enable(udc); + else { + if (udc->gadget.speed != USB_SPEED_UNKNOWN) { + if (udc->driver && udc->driver->disconnect) + udc->driver->disconnect(&udc->gadget); + + } + s3c2410_udc_disable(udc); + } + } + else + return -EOPNOTSUPP; + + return 0; +} + +static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + udc->vbus = (is_active != 0); + s3c2410_udc_set_pullup(udc, is_active); + return 0; +} + +static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on) +{ + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + s3c2410_udc_set_pullup(udc, is_on ? 0 : 1); + return 0; +} + +static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev) +{ + struct s3c2410_udc *dev = _dev; + unsigned int value; + + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + value = s3c2410_gpio_getpin(udc_info->vbus_pin); + + if (udc_info->vbus_pin_inverted) + value = !value; + + if (value != dev->vbus) + s3c2410_udc_vbus_session(&dev->gadget, value); + + return IRQ_HANDLED; +} + +static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma) +{ + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + if (udc_info && udc_info->vbus_draw) { + udc_info->vbus_draw(ma); + return 0; + } + + return -ENOTSUPP; +} + +static const struct usb_gadget_ops s3c2410_ops = { + .get_frame = s3c2410_udc_get_frame, + .wakeup = s3c2410_udc_wakeup, + .set_selfpowered = s3c2410_udc_set_selfpowered, + .pullup = s3c2410_udc_pullup, + .vbus_session = s3c2410_udc_vbus_session, + .vbus_draw = s3c2410_vbus_draw, +}; + +/*------------------------- gadget driver handling---------------------------*/ +/* + * s3c2410_udc_disable + */ +static void s3c2410_udc_disable(struct s3c2410_udc *dev) +{ + dprintk(DEBUG_NORMAL, "%s()\n", __func__); + + /* Disable all interrupts */ + udc_write(0x00, S3C2410_UDC_USB_INT_EN_REG); + udc_write(0x00, S3C2410_UDC_EP_INT_EN_REG); + + /* Clear the interrupt registers */ + udc_write(S3C2410_UDC_USBINT_RESET + | S3C2410_UDC_USBINT_RESUME + | S3C2410_UDC_USBINT_SUSPEND, + S3C2410_UDC_USB_INT_REG); + + udc_write(0x1F, S3C2410_UDC_EP_INT_REG); + + /* Good bye, cruel world */ + if (udc_info && udc_info->udc_command) + udc_info->udc_command(S3C2410_UDC_P_DISABLE); + + /* Set speed to unknown */ + dev->gadget.speed = USB_SPEED_UNKNOWN; +} + +/* + * s3c2410_udc_reinit + */ +static void s3c2410_udc_reinit(struct s3c2410_udc *dev) +{ + u32 i; + + /* device/ep0 records init */ + INIT_LIST_HEAD (&dev->gadget.ep_list); + INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); + dev->ep0state = EP0_IDLE; + + for (i = 0; i < S3C2410_ENDPOINTS; i++) { + struct s3c2410_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->dev = dev; + ep->desc = NULL; + ep->halted = 0; + INIT_LIST_HEAD (&ep->queue); + } +} + +/* + * s3c2410_udc_enable + */ +static void s3c2410_udc_enable(struct s3c2410_udc *dev) +{ + int i; + + dprintk(DEBUG_NORMAL, "s3c2410_udc_enable called\n"); + + /* dev->gadget.speed = USB_SPEED_UNKNOWN; */ + dev->gadget.speed = USB_SPEED_FULL; + + /* Set MAXP for all endpoints */ + for (i = 0; i < S3C2410_ENDPOINTS; i++) { + udc_write(i, S3C2410_UDC_INDEX_REG); + udc_write((dev->ep[i].ep.maxpacket & 0x7ff) >> 3, + S3C2410_UDC_MAXP_REG); + } + + /* Set default power state */ + udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG); + + /* Enable reset and suspend interrupt interrupts */ + udc_write(S3C2410_UDC_USBINT_RESET | S3C2410_UDC_USBINT_SUSPEND, + S3C2410_UDC_USB_INT_EN_REG); + + /* Enable ep0 interrupt */ + udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG); + + /* time to say "hello, world" */ + if (udc_info && udc_info->udc_command) + udc_info->udc_command(S3C2410_UDC_P_ENABLE); +} + +/* + * usb_gadget_register_driver + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct s3c2410_udc *udc = the_controller; + int retval; + + dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n", + driver->driver.name); + + /* Sanity checks */ + if (!udc) + return -ENODEV; + + if (udc->driver) + return -EBUSY; + + if (!driver->bind || !driver->setup + || driver->speed != USB_SPEED_FULL) { + printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n", + driver->bind, driver->setup, driver->speed); + return -EINVAL; + } +#if defined(MODULE) + if (!driver->unbind) { + printk(KERN_ERR "Invalid driver: no unbind method\n"); + return -EINVAL; + } +#endif + + /* Hook the driver */ + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + + /* Bind the driver */ + if ((retval = device_add(&udc->gadget.dev)) != 0) { + printk(KERN_ERR "Error in device_add() : %d\n",retval); + goto register_error; + } + + dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", + driver->driver.name); + + if ((retval = driver->bind (&udc->gadget)) != 0) { + device_del(&udc->gadget.dev); + goto register_error; + } + + /* Enable udc */ + s3c2410_udc_enable(udc); + + return 0; + +register_error: + udc->driver = NULL; + udc->gadget.dev.driver = NULL; + return retval; +} + +/* + * usb_gadget_unregister_driver + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct s3c2410_udc *udc = the_controller; + + if (!udc) + return -ENODEV; + + if (!driver || driver != udc->driver || !driver->unbind) + return -EINVAL; + + dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n", + driver->driver.name); + + if (driver->disconnect) + driver->disconnect(&udc->gadget); + + device_del(&udc->gadget.dev); + udc->driver = NULL; + + /* Disable udc */ + s3c2410_udc_disable(udc); + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static struct s3c2410_udc memory = { + .gadget = { + .ops = &s3c2410_ops, + .ep0 = &memory.ep[0].ep, + .name = gadget_name, + .dev = { + .bus_id = "gadget", + }, + }, + + /* control endpoint */ + .ep[0] = { + .num = 0, + .ep = { + .name = ep0name, + .ops = &s3c2410_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + }, + + /* first group of endpoints */ + .ep[1] = { + .num = 1, + .ep = { + .name = "ep1-bulk", + .ops = &s3c2410_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = EP_FIFO_SIZE, + .bEndpointAddress = 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .ep[2] = { + .num = 2, + .ep = { + .name = "ep2-bulk", + .ops = &s3c2410_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = EP_FIFO_SIZE, + .bEndpointAddress = 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .ep[3] = { + .num = 3, + .ep = { + .name = "ep3-bulk", + .ops = &s3c2410_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = EP_FIFO_SIZE, + .bEndpointAddress = 3, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + }, + .ep[4] = { + .num = 4, + .ep = { + .name = "ep4-bulk", + .ops = &s3c2410_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + .fifo_size = EP_FIFO_SIZE, + .bEndpointAddress = 4, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + } + +}; + +/* + * probe - binds to the platform device + */ +static int s3c2410_udc_probe(struct platform_device *pdev) +{ + struct s3c2410_udc *udc = &memory; + struct device *dev = &pdev->dev; + int retval; + unsigned int irq; + + dev_dbg(dev, "%s()\n", __func__); + + usb_bus_clock = clk_get(NULL, "usb-bus-gadget"); + if (IS_ERR(usb_bus_clock)) { + dev_err(dev, "failed to get usb bus clock source\n"); + return PTR_ERR(usb_bus_clock); + } + + clk_enable(usb_bus_clock); + + udc_clock = clk_get(NULL, "usb-device"); + if (IS_ERR(udc_clock)) { + dev_err(dev, "failed to get udc clock source\n"); + return PTR_ERR(udc_clock); + } + + clk_enable(udc_clock); + + mdelay(10); + + dev_dbg(dev, "got and enabled clocks\n"); + + if (strncmp(pdev->name, "s3c2440", 7) == 0) { + dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n"); + memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE; + memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE; + memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE; + memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE; + } + + spin_lock_init (&udc->lock); + udc_info = pdev->dev.platform_data; + + rsrc_start = S3C2410_PA_USBDEV; + rsrc_len = S3C24XX_SZ_USBDEV; + + if (!request_mem_region(rsrc_start, rsrc_len, gadget_name)) + return -EBUSY; + + base_addr = ioremap(rsrc_start, rsrc_len); + if (!base_addr) { + retval = -ENOMEM; + goto err_mem; + } + + device_initialize(&udc->gadget.dev); + udc->gadget.dev.parent = &pdev->dev; + udc->gadget.dev.dma_mask = pdev->dev.dma_mask; + + the_controller = udc; + platform_set_drvdata(pdev, udc); + + s3c2410_udc_disable(udc); + s3c2410_udc_reinit(udc); + + /* irq setup after old hardware state is cleaned up */ + retval = request_irq(IRQ_USBD, s3c2410_udc_irq, + IRQF_DISABLED, gadget_name, udc); + + if (retval != 0) { + dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); + retval = -EBUSY; + goto err_map; + } + + dev_dbg(dev, "got irq %i\n", IRQ_USBD); + + if (udc_info && udc_info->vbus_pin > 0) { + irq = s3c2410_gpio_getirq(udc_info->vbus_pin); + retval = request_irq(irq, s3c2410_udc_vbus_irq, + IRQF_DISABLED | IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING, + gadget_name, udc); + + if (retval != 0) { + dev_err(dev, "can't get vbus irq %i, err %d\n", + irq, retval); + retval = -EBUSY; + goto err_int; + } + + dev_dbg(dev, "got irq %i\n", irq); + } else { + udc->vbus = 1; + } + + if (s3c2410_udc_debugfs_root) { + udc->regs_info = debugfs_create_file("registers", S_IRUGO, + s3c2410_udc_debugfs_root, + udc, &s3c2410_udc_debugfs_fops); + if (IS_ERR(udc->regs_info)) { + dev_warn(dev, "debugfs file creation failed %ld\n", + PTR_ERR(udc->regs_info)); + udc->regs_info = NULL; + } + } + + dev_dbg(dev, "probe ok\n"); + + return 0; + +err_int: + free_irq(IRQ_USBD, udc); +err_map: + iounmap(base_addr); +err_mem: + release_mem_region(rsrc_start, rsrc_len); + + return retval; +} + +/* + * s3c2410_udc_remove + */ +static int s3c2410_udc_remove(struct platform_device *pdev) +{ + struct s3c2410_udc *udc = platform_get_drvdata(pdev); + unsigned int irq; + + dev_dbg(&pdev->dev, "%s()\n", __func__); + if (udc->driver) + return -EBUSY; + + debugfs_remove(udc->regs_info); + + if (udc_info && udc_info->vbus_pin > 0) { + irq = s3c2410_gpio_getirq(udc_info->vbus_pin); + free_irq(irq, udc); + } + + free_irq(IRQ_USBD, udc); + + iounmap(base_addr); + release_mem_region(rsrc_start, rsrc_len); + + platform_set_drvdata(pdev, NULL); + + if (!IS_ERR(udc_clock) && udc_clock != NULL) { + clk_disable(udc_clock); + clk_put(udc_clock); + udc_clock = NULL; + } + + if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) { + clk_disable(usb_bus_clock); + clk_put(usb_bus_clock); + usb_bus_clock = NULL; + } + + dev_dbg(&pdev->dev, "%s: remove ok\n", __func__); + return 0; +} + +#ifdef CONFIG_PM +static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message) +{ + if (udc_info && udc_info->udc_command) + udc_info->udc_command(S3C2410_UDC_P_DISABLE); + + return 0; +} + +static int s3c2410_udc_resume(struct platform_device *pdev) +{ + if (udc_info && udc_info->udc_command) + udc_info->udc_command(S3C2410_UDC_P_ENABLE); + + return 0; +} +#else +#define s3c2410_udc_suspend NULL +#define s3c2410_udc_resume NULL +#endif + +static struct platform_driver udc_driver_2410 = { + .driver = { + .name = "s3c2410-usbgadget", + .owner = THIS_MODULE, + }, + .probe = s3c2410_udc_probe, + .remove = s3c2410_udc_remove, + .suspend = s3c2410_udc_suspend, + .resume = s3c2410_udc_resume, +}; + +static struct platform_driver udc_driver_2440 = { + .driver = { + .name = "s3c2440-usbgadget", + .owner = THIS_MODULE, + }, + .probe = s3c2410_udc_probe, + .remove = s3c2410_udc_remove, + .suspend = s3c2410_udc_suspend, + .resume = s3c2410_udc_resume, +}; + +static int __init udc_init(void) +{ + int retval; + + dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION); + + s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL); + if (IS_ERR(s3c2410_udc_debugfs_root)) { + printk(KERN_ERR "%s: debugfs dir creation failed %ld\n", + gadget_name, PTR_ERR(s3c2410_udc_debugfs_root)); + s3c2410_udc_debugfs_root = NULL; + } + + retval = platform_driver_register(&udc_driver_2410); + if (retval) + goto err; + + retval = platform_driver_register(&udc_driver_2440); + if (retval) + goto err; + + return 0; + +err: + debugfs_remove(s3c2410_udc_debugfs_root); + return retval; +} + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver_2410); + platform_driver_unregister(&udc_driver_2440); + debugfs_remove(s3c2410_udc_debugfs_root); +} + +EXPORT_SYMBOL(usb_gadget_unregister_driver); +EXPORT_SYMBOL(usb_gadget_register_driver); + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h new file mode 100644 index 0000000..9e0bece --- /dev/null +++ b/drivers/usb/gadget/s3c2410_udc.h @@ -0,0 +1,110 @@ +/* + * linux/drivers/usb/gadget/s3c2410_udc.h + * Samsung on-chip full speed USB device controllers + * + * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard + * Additional cleanups by Ben Dooks <ben-linux@fluff.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _S3C2410_UDC_H +#define _S3C2410_UDC_H + +struct s3c2410_ep { + struct list_head queue; + unsigned long last_io; /* jiffies timestamp */ + struct usb_gadget *gadget; + struct s3c2410_udc *dev; + const struct usb_endpoint_descriptor *desc; + struct usb_ep ep; + u8 num; + + unsigned short fifo_size; + u8 bEndpointAddress; + u8 bmAttributes; + + unsigned halted : 1; + unsigned already_seen : 1; + unsigned setup_stage : 1; +}; + + +/* Warning : ep0 has a fifo of 16 bytes */ +/* Don't try to set 32 or 64 */ +/* also testusb 14 fails wit 16 but is */ +/* fine with 8 */ +#define EP0_FIFO_SIZE 8 +#define EP_FIFO_SIZE 64 +#define DEFAULT_POWER_STATE 0x00 + +#define S3C2440_EP_FIFO_SIZE 128 + +static const char ep0name [] = "ep0"; + +static const char *const ep_name[] = { + ep0name, /* everyone has ep0 */ + /* s3c2410 four bidirectional bulk endpoints */ + "ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk", +}; + +#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name) + +struct s3c2410_request { + struct list_head queue; /* ep's requests */ + struct usb_request req; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_END_XFER, + EP0_STALL, +}; + +static const char *ep0states[]= { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", + "EP0_OUT_DATA_PHASE", + "EP0_END_XFER", + "EP0_STALL", +}; + +struct s3c2410_udc { + spinlock_t lock; + + struct s3c2410_ep ep[S3C2410_ENDPOINTS]; + int address; + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct s3c2410_request fifo_req; + u8 fifo_buf[EP_FIFO_SIZE]; + u16 devstatus; + + u32 port_status; + int ep0state; + + unsigned got_irq : 1; + + unsigned req_std : 1; + unsigned req_config : 1; + unsigned req_pending : 1; + u8 vbus; + struct dentry *regs_info; +}; + +#endif diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index f847c34..dd33ff0 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -2215,7 +2215,7 @@ static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags) * * Free the buffer and all associated memory. */ -void gs_buf_free(struct gs_buf *gb) +static void gs_buf_free(struct gs_buf *gb) { if (gb) { kfree(gb->buf_buf); @@ -2228,7 +2228,7 @@ void gs_buf_free(struct gs_buf *gb) * * Clear out all data in the circular buffer. */ -void gs_buf_clear(struct gs_buf *gb) +static void gs_buf_clear(struct gs_buf *gb) { if (gb != NULL) gb->buf_get = gb->buf_put; @@ -2241,7 +2241,7 @@ void gs_buf_clear(struct gs_buf *gb) * Return the number of bytes of data available in the circular * buffer. */ -unsigned int gs_buf_data_avail(struct gs_buf *gb) +static unsigned int gs_buf_data_avail(struct gs_buf *gb) { if (gb != NULL) return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; @@ -2255,7 +2255,7 @@ unsigned int gs_buf_data_avail(struct gs_buf *gb) * Return the number of bytes of space available in the circular * buffer. */ -unsigned int gs_buf_space_avail(struct gs_buf *gb) +static unsigned int gs_buf_space_avail(struct gs_buf *gb) { if (gb != NULL) return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; @@ -2271,7 +2271,8 @@ unsigned int gs_buf_space_avail(struct gs_buf *gb) * * Return the number of bytes copied. */ -unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) +static unsigned int +gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) { unsigned int len; @@ -2309,7 +2310,8 @@ unsigned int gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count) * * Return the number of bytes copied. */ -unsigned int gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count) +static unsigned int +gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count) { unsigned int len; diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 7078374..a2e6e3fc 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -481,8 +481,7 @@ alloc_ep_req (struct usb_ep *ep, unsigned length) req = usb_ep_alloc_request (ep, GFP_ATOMIC); if (req) { req->length = length; - req->buf = usb_ep_alloc_buffer (ep, length, - &req->dma, GFP_ATOMIC); + req->buf = kmalloc(length, GFP_ATOMIC); if (!req->buf) { usb_ep_free_request (ep, req); req = NULL; @@ -493,8 +492,7 @@ alloc_ep_req (struct usb_ep *ep, unsigned length) static void free_ep_req (struct usb_ep *ep, struct usb_request *req) { - if (req->buf) - usb_ep_free_buffer (ep, req->buf, req->dma, req->length); + kfree(req->buf); usb_ep_free_request (ep, req); } @@ -1199,8 +1197,7 @@ autoconf_fail: dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); if (!dev->req) goto enomem; - dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ, - &dev->req->dma, GFP_KERNEL); + dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); if (!dev->req->buf) goto enomem; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6271187..2f52982 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -69,8 +69,20 @@ config USB_EHCI_TT_NEWSCHED config USB_EHCI_BIG_ENDIAN_MMIO bool - depends on USB_EHCI_HCD - default n + depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX) + default y + +config USB_EHCI_BIG_ENDIAN_DESC + bool + depends on USB_EHCI_HCD && 440EPX + default y + +config USB_EHCI_FSL + bool + select USB_EHCI_ROOT_HUB_TT + default y if MPC834x || PPC_MPC831x + ---help--- + Variation of ARC USB block used in some Freescale chips. config USB_ISP116X_HCD tristate "ISP116X HCD support" @@ -224,3 +236,15 @@ config USB_SL811_CS To compile this driver as a module, choose M here: the module will be called "sl811_cs". +config USB_R8A66597_HCD + tristate "R8A66597 HCD suppoort" + depends on USB + help + The R8A66597 is a USB 2.0 host and peripheral controller. + + Enable this option if your board has this chip, and you want + to use it as a host controller. If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called r8a66597-hcd. + diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 2ff396bd..bb8e9d4 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -15,3 +15,5 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o +obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o + diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 43eddae..c9cc441 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -52,7 +52,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label) HCS_INDICATOR (params) ? " ind" : "", HCS_N_CC (params), HCS_N_PCC (params), - HCS_PORTROUTED (params) ? "" : " ordered", + HCS_PORTROUTED (params) ? "" : " ordered", HCS_PPC (params) ? "" : " !ppc", HCS_N_PORTS (params) ); @@ -91,20 +91,20 @@ static void dbg_hcc_params (struct ehci_hcd *ehci, char *label) if (HCC_ISOC_CACHE (params)) { ehci_dbg (ehci, - "%s hcc_params %04x caching frame %s%s%s\n", - label, params, - HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", - HCC_CANPARK (params) ? " park" : "", - HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); + "%s hcc_params %04x caching frame %s%s%s\n", + label, params, + HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", + HCC_CANPARK(params) ? " park" : "", + HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); } else { ehci_dbg (ehci, - "%s hcc_params %04x thresh %d uframes %s%s%s\n", - label, - params, - HCC_ISOC_THRES (params), - HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024", - HCC_CANPARK (params) ? " park" : "", - HCC_64BIT_ADDR (params) ? " 64 bit addr" : ""); + "%s hcc_params %04x thresh %d uframes %s%s%s\n", + label, + params, + HCC_ISOC_THRES(params), + HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", + HCC_CANPARK(params) ? " park" : "", + HCC_64BIT_ADDR(params) ? " 64 bit addr" : ""); } } #else @@ -115,23 +115,23 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {} #ifdef DEBUG -static void __attribute__((__unused__)) +static void __maybe_unused dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd) { - ehci_dbg (ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, - le32_to_cpup (&qtd->hw_next), - le32_to_cpup (&qtd->hw_alt_next), - le32_to_cpup (&qtd->hw_token), - le32_to_cpup (&qtd->hw_buf [0])); + ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, + hc32_to_cpup(ehci, &qtd->hw_next), + hc32_to_cpup(ehci, &qtd->hw_alt_next), + hc32_to_cpup(ehci, &qtd->hw_token), + hc32_to_cpup(ehci, &qtd->hw_buf [0])); if (qtd->hw_buf [1]) - ehci_dbg (ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n", - le32_to_cpup (&qtd->hw_buf [1]), - le32_to_cpup (&qtd->hw_buf [2]), - le32_to_cpup (&qtd->hw_buf [3]), - le32_to_cpup (&qtd->hw_buf [4])); + ehci_dbg(ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n", + hc32_to_cpup(ehci, &qtd->hw_buf[1]), + hc32_to_cpup(ehci, &qtd->hw_buf[2]), + hc32_to_cpup(ehci, &qtd->hw_buf[3]), + hc32_to_cpup(ehci, &qtd->hw_buf[4])); } -static void __attribute__((__unused__)) +static void __maybe_unused dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) { ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label, @@ -140,51 +140,53 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next); } -static void __attribute__((__unused__)) +static void __maybe_unused dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd) { ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n", - label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb); + label, itd->frame, itd, hc32_to_cpu(ehci, itd->hw_next), + itd->urb); ehci_dbg (ehci, " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", - le32_to_cpu(itd->hw_transaction[0]), - le32_to_cpu(itd->hw_transaction[1]), - le32_to_cpu(itd->hw_transaction[2]), - le32_to_cpu(itd->hw_transaction[3]), - le32_to_cpu(itd->hw_transaction[4]), - le32_to_cpu(itd->hw_transaction[5]), - le32_to_cpu(itd->hw_transaction[6]), - le32_to_cpu(itd->hw_transaction[7])); + hc32_to_cpu(ehci, itd->hw_transaction[0]), + hc32_to_cpu(ehci, itd->hw_transaction[1]), + hc32_to_cpu(ehci, itd->hw_transaction[2]), + hc32_to_cpu(ehci, itd->hw_transaction[3]), + hc32_to_cpu(ehci, itd->hw_transaction[4]), + hc32_to_cpu(ehci, itd->hw_transaction[5]), + hc32_to_cpu(ehci, itd->hw_transaction[6]), + hc32_to_cpu(ehci, itd->hw_transaction[7])); ehci_dbg (ehci, " buf: %08x %08x %08x %08x %08x %08x %08x\n", - le32_to_cpu(itd->hw_bufp[0]), - le32_to_cpu(itd->hw_bufp[1]), - le32_to_cpu(itd->hw_bufp[2]), - le32_to_cpu(itd->hw_bufp[3]), - le32_to_cpu(itd->hw_bufp[4]), - le32_to_cpu(itd->hw_bufp[5]), - le32_to_cpu(itd->hw_bufp[6])); + hc32_to_cpu(ehci, itd->hw_bufp[0]), + hc32_to_cpu(ehci, itd->hw_bufp[1]), + hc32_to_cpu(ehci, itd->hw_bufp[2]), + hc32_to_cpu(ehci, itd->hw_bufp[3]), + hc32_to_cpu(ehci, itd->hw_bufp[4]), + hc32_to_cpu(ehci, itd->hw_bufp[5]), + hc32_to_cpu(ehci, itd->hw_bufp[6])); ehci_dbg (ehci, " index: %d %d %d %d %d %d %d %d\n", itd->index[0], itd->index[1], itd->index[2], itd->index[3], itd->index[4], itd->index[5], itd->index[6], itd->index[7]); } -static void __attribute__((__unused__)) +static void __maybe_unused dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd) { ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n", - label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb); + label, sitd->frame, sitd, hc32_to_cpu(ehci, sitd->hw_next), + sitd->urb); ehci_dbg (ehci, " addr %08x sched %04x result %08x buf %08x %08x\n", - le32_to_cpu(sitd->hw_fullspeed_ep), - le32_to_cpu(sitd->hw_uframe), - le32_to_cpu(sitd->hw_results), - le32_to_cpu(sitd->hw_buf [0]), - le32_to_cpu(sitd->hw_buf [1])); + hc32_to_cpu(ehci, sitd->hw_fullspeed_ep), + hc32_to_cpu(ehci, sitd->hw_uframe), + hc32_to_cpu(ehci, sitd->hw_results), + hc32_to_cpu(ehci, sitd->hw_buf[0]), + hc32_to_cpu(ehci, sitd->hw_buf[1])); } -static int __attribute__((__unused__)) +static int __maybe_unused dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) { return scnprintf (buf, len, @@ -203,7 +205,7 @@ dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) ); } -static int __attribute__((__unused__)) +static int __maybe_unused dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) { return scnprintf (buf, len, @@ -267,28 +269,27 @@ dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) (status & PORT_PEC) ? " PEC" : "", (status & PORT_PE) ? " PE" : "", (status & PORT_CSC) ? " CSC" : "", - (status & PORT_CONNECT) ? " CONNECT" : "" - ); + (status & PORT_CONNECT) ? " CONNECT" : ""); } #else -static inline void __attribute__((__unused__)) +static inline void __maybe_unused dbg_qh (char *label, struct ehci_hcd *ehci, struct ehci_qh *qh) {} -static inline int __attribute__((__unused__)) +static inline int __maybe_unused dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) { return 0; } -static inline int __attribute__((__unused__)) +static inline int __maybe_unused dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) { return 0; } -static inline int __attribute__((__unused__)) +static inline int __maybe_unused dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) { return 0; } -static inline int __attribute__((__unused__)) +static inline int __maybe_unused dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) { return 0; } @@ -332,9 +333,10 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { } default: tmp = '?'; break; \ }; tmp; }) -static inline char token_mark (__le32 token) +static inline char token_mark(struct ehci_hcd *ehci, __hc32 token) { - __u32 v = le32_to_cpu (token); + __u32 v = hc32_to_cpu(ehci, token); + if (v & QTD_STS_ACTIVE) return '*'; if (v & QTD_STS_HALT) @@ -360,46 +362,48 @@ static void qh_lines ( unsigned size = *sizep; char *next = *nextp; char mark; + u32 list_end = EHCI_LIST_END(ehci); - if (qh->hw_qtd_next == EHCI_LIST_END) /* NEC does this */ + if (qh->hw_qtd_next == list_end) /* NEC does this */ mark = '@'; else - mark = token_mark (qh->hw_token); + mark = token_mark(ehci, qh->hw_token); if (mark == '/') { /* qh_alt_next controls qh advance? */ - if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next) + if ((qh->hw_alt_next & QTD_MASK(ehci)) + == ehci->async->hw_alt_next) mark = '#'; /* blocked */ - else if (qh->hw_alt_next == EHCI_LIST_END) + else if (qh->hw_alt_next == list_end) mark = '.'; /* use hw_qtd_next */ /* else alt_next points to some other qtd */ } - scratch = le32_to_cpup (&qh->hw_info1); - hw_curr = (mark == '*') ? le32_to_cpup (&qh->hw_current) : 0; + scratch = hc32_to_cpup(ehci, &qh->hw_info1); + hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &qh->hw_current) : 0; temp = scnprintf (next, size, "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)", qh, scratch & 0x007f, speed_char (scratch), (scratch >> 8) & 0x000f, - scratch, le32_to_cpup (&qh->hw_info2), - le32_to_cpup (&qh->hw_token), mark, - (__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token) + scratch, hc32_to_cpup(ehci, &qh->hw_info2), + hc32_to_cpup(ehci, &qh->hw_token), mark, + (cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw_token) ? "data1" : "data0", - (le32_to_cpup (&qh->hw_alt_next) >> 1) & 0x0f); + (hc32_to_cpup(ehci, &qh->hw_alt_next) >> 1) & 0x0f); size -= temp; next += temp; /* hc may be modifying the list as we read it ... */ list_for_each (entry, &qh->qtd_list) { td = list_entry (entry, struct ehci_qtd, qtd_list); - scratch = le32_to_cpup (&td->hw_token); + scratch = hc32_to_cpup(ehci, &td->hw_token); mark = ' '; if (hw_curr == td->qtd_dma) mark = '*'; - else if (qh->hw_qtd_next == cpu_to_le32(td->qtd_dma)) + else if (qh->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma)) mark = '+'; else if (QTD_LENGTH (scratch)) { if (td->hw_alt_next == ehci->async->hw_alt_next) mark = '#'; - else if (td->hw_alt_next != EHCI_LIST_END) + else if (td->hw_alt_next != list_end) mark = '/'; } temp = snprintf (next, size, @@ -490,7 +494,7 @@ show_periodic (struct class_device *class_dev, char *buf) unsigned temp, size, seen_count; char *next; unsigned i; - __le32 tag; + __hc32 tag; if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC))) return 0; @@ -514,18 +518,19 @@ show_periodic (struct class_device *class_dev, char *buf) p = ehci->pshadow [i]; if (likely (!p.ptr)) continue; - tag = Q_NEXT_TYPE (ehci->periodic [i]); + tag = Q_NEXT_TYPE(ehci, ehci->periodic [i]); temp = scnprintf (next, size, "%4d: ", i); size -= temp; next += temp; do { - switch (tag) { + switch (hc32_to_cpu(ehci, tag)) { case Q_TYPE_QH: temp = scnprintf (next, size, " qh%d-%04x/%p", p.qh->period, - le32_to_cpup (&p.qh->hw_info2) + hc32_to_cpup(ehci, + &p.qh->hw_info2) /* uframe masks */ & (QH_CMASK | QH_SMASK), p.qh); @@ -543,7 +548,7 @@ show_periodic (struct class_device *class_dev, char *buf) } /* show more info the first time around */ if (temp == seen_count && p.ptr) { - u32 scratch = le32_to_cpup ( + u32 scratch = hc32_to_cpup(ehci, &p.qh->hw_info1); struct ehci_qtd *qtd; char *type = ""; @@ -554,7 +559,8 @@ show_periodic (struct class_device *class_dev, char *buf) &p.qh->qtd_list, qtd_list) { temp++; - switch (0x03 & (le32_to_cpu ( + switch (0x03 & (hc32_to_cpu( + ehci, qtd->hw_token) >> 8)) { case 0: type = "out"; continue; case 1: type = "in"; continue; @@ -576,7 +582,7 @@ show_periodic (struct class_device *class_dev, char *buf) } else temp = 0; if (p.qh) { - tag = Q_NEXT_TYPE (p.qh->hw_next); + tag = Q_NEXT_TYPE(ehci, p.qh->hw_next); p = p.qh->qh_next; } break; @@ -584,23 +590,23 @@ show_periodic (struct class_device *class_dev, char *buf) temp = scnprintf (next, size, " fstn-%8x/%p", p.fstn->hw_prev, p.fstn); - tag = Q_NEXT_TYPE (p.fstn->hw_next); + tag = Q_NEXT_TYPE(ehci, p.fstn->hw_next); p = p.fstn->fstn_next; break; case Q_TYPE_ITD: temp = scnprintf (next, size, " itd/%p", p.itd); - tag = Q_NEXT_TYPE (p.itd->hw_next); + tag = Q_NEXT_TYPE(ehci, p.itd->hw_next); p = p.itd->itd_next; break; case Q_TYPE_SITD: temp = scnprintf (next, size, " sitd%d-%04x/%p", p.sitd->stream->interval, - le32_to_cpup (&p.sitd->hw_uframe) + hc32_to_cpup(ehci, &p.sitd->hw_uframe) & 0x0000ffff, p.sitd); - tag = Q_NEXT_TYPE (p.sitd->hw_next); + tag = Q_NEXT_TYPE(ehci, p.sitd->hw_next); p = p.sitd->sitd_next; break; } @@ -673,7 +679,8 @@ 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 (ehci_readl(ehci, &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) { @@ -740,14 +747,16 @@ show_registers (struct class_device *class_dev, char *buf) for (i = 1; i <= HCS_N_PORTS (ehci->hcs_params); i++) { temp = dbg_port_buf (scratch, sizeof scratch, label, i, - ehci_readl(ehci, &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", - ehci_readl(ehci, &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 c7a7c59..b7b7bfb 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -67,7 +67,8 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver, * in host mode. */ if (!((pdata->operating_mode == FSL_USB2_DR_HOST) || - (pdata->operating_mode == FSL_USB2_MPH_HOST))) { + (pdata->operating_mode == FSL_USB2_MPH_HOST) || + (pdata->operating_mode == FSL_USB2_DR_OTG))) { dev_err(&pdev->dev, "Non Host Mode configured for %s. Wrong driver linked.\n", pdev->dev.bus_id); @@ -185,12 +186,14 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct fsl_usb2_platform_data *pdata; void __iomem *non_ehci = hcd->regs; + u32 temp; pdata = (struct fsl_usb2_platform_data *)hcd->self.controller-> platform_data; /* Enable PHY interface in the control reg. */ - out_be32(non_ehci + FSL_SOC_USB_CTRL, 0x00000004); + temp = in_be32(non_ehci + FSL_SOC_USB_CTRL); + out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004); out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b); #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) @@ -206,7 +209,8 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd) out_be32(non_ehci + FSL_SOC_USB_SNOOP2, 0x80000000 | SNOOP_SIZE_2GB); #endif - if (pdata->operating_mode == FSL_USB2_DR_HOST) + if ((pdata->operating_mode == FSL_USB2_DR_HOST) || + (pdata->operating_mode == FSL_USB2_DR_OTG)) mpc83xx_setup_phy(ehci, pdata->phy_mode, 0); if (pdata->operating_mode == FSL_USB2_MPH_HOST) { diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 099aff6..c4e15ed 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -41,10 +41,6 @@ #include <asm/irq.h> #include <asm/system.h> #include <asm/unaligned.h> -#ifdef CONFIG_PPC_PS3 -#include <asm/firmware.h> -#endif - /*-------------------------------------------------------------------------*/ @@ -201,9 +197,15 @@ static void tdi_reset (struct ehci_hcd *ehci) u32 __iomem *reg_ptr; u32 tmp; - reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + 0x68); + reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); tmp = ehci_readl(ehci, reg_ptr); - tmp |= 0x3; + tmp |= USBMODE_CM_HC; + /* The default byte access to MMR space is LE after + * controller reset. Set the required endian mode + * for transfer buffers to match the host microprocessor + */ + if (ehci_big_endian_mmio(ehci)) + tmp |= USBMODE_BE; ehci_writel(ehci, tmp, reg_ptr); } @@ -273,6 +275,58 @@ static void ehci_work(struct ehci_hcd *ehci); /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_CPU_FREQ + +#include <linux/cpufreq.h> + +static void ehci_cpufreq_pause (struct ehci_hcd *ehci) +{ + unsigned long flags; + + spin_lock_irqsave(&ehci->lock, flags); + if (!ehci->cpufreq_changing++) + qh_inactivate_split_intr_qhs(ehci); + spin_unlock_irqrestore(&ehci->lock, flags); +} + +static void ehci_cpufreq_unpause (struct ehci_hcd *ehci) +{ + unsigned long flags; + + spin_lock_irqsave(&ehci->lock, flags); + if (!--ehci->cpufreq_changing) + qh_reactivate_split_intr_qhs(ehci); + spin_unlock_irqrestore(&ehci->lock, flags); +} + +/* + * ehci_cpufreq_notifier is needed to avoid MMF errors that occur when + * EHCI controllers that don't cache many uframes get delayed trying to + * read main memory during CPU frequency transitions. This can cause + * split interrupt transactions to not be completed in the required uframe. + * This has been observed on the Broadcom/ServerWorks HT1000 controller. + */ +static int ehci_cpufreq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct ehci_hcd *ehci = container_of(nb, struct ehci_hcd, + cpufreq_transition); + + switch (val) { + case CPUFREQ_PRECHANGE: + ehci_cpufreq_pause(ehci); + break; + case CPUFREQ_POSTCHANGE: + ehci_cpufreq_unpause(ehci); + break; + } + return 0; +} + +#endif + +/*-------------------------------------------------------------------------*/ + static void ehci_watchdog (unsigned long param) { struct ehci_hcd *ehci = (struct ehci_hcd *) param; @@ -347,6 +401,8 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on) is_on ? SetPortFeature : ClearPortFeature, USB_PORT_FEAT_POWER, port--, NULL, 0); + /* Flush those writes */ + ehci_readl(ehci, &ehci->regs->command); msleep(20); } @@ -404,6 +460,10 @@ static void ehci_stop (struct usb_hcd *hcd) ehci_writel(ehci, 0, &ehci->regs->intr_enable); spin_unlock_irq(&ehci->lock); +#ifdef CONFIG_CPU_FREQ + cpufreq_unregister_notifier(&ehci->cpufreq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +#endif /* let companion controllers work when we aren't */ ehci_writel(ehci, 0, &ehci->regs->configured_flag); @@ -470,12 +530,12 @@ static int ehci_init(struct usb_hcd *hcd) * from automatically advancing to the next td after short reads. */ ehci->async->qh_next.qh = NULL; - ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma); - ehci->async->hw_info1 = cpu_to_le32(QH_HEAD); - ehci->async->hw_token = cpu_to_le32(QTD_STS_HALT); - ehci->async->hw_qtd_next = EHCI_LIST_END; + ehci->async->hw_next = QH_NEXT(ehci, ehci->async->qh_dma); + ehci->async->hw_info1 = cpu_to_hc32(ehci, QH_HEAD); + ehci->async->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT); + ehci->async->hw_qtd_next = EHCI_LIST_END(ehci); ehci->async->qh_state = QH_STATE_LINKED; - ehci->async->hw_alt_next = QTD_NEXT(ehci->async->dummy->qtd_dma); + ehci->async->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma); /* clear interrupt enables, set irq latency */ if (log2_irq_thresh < 0 || log2_irq_thresh > 6) @@ -509,6 +569,17 @@ static int ehci_init(struct usb_hcd *hcd) } ehci->command = temp; +#ifdef CONFIG_CPU_FREQ + INIT_LIST_HEAD(&ehci->split_intr_qhs); + /* + * If the EHCI controller caches enough uframes, this probably + * isn't needed unless there are so many low/full speed devices + * that the controller's can't cache it all. + */ + ehci->cpufreq_transition.notifier_call = ehci_cpufreq_notifier; + cpufreq_register_notifier(&ehci->cpufreq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +#endif return 0; } @@ -925,7 +996,7 @@ MODULE_LICENSE ("GPL"); #define PCI_DRIVER ehci_pci_driver #endif -#ifdef CONFIG_MPC834x +#ifdef CONFIG_USB_EHCI_FSL #include "ehci-fsl.c" #define PLATFORM_DRIVER ehci_fsl_driver #endif @@ -937,7 +1008,12 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_PPC_PS3 #include "ehci-ps3.c" -#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_sb_driver +#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver +#endif + +#ifdef CONFIG_440EPX +#include "ehci-ppc-soc.c" +#define PLATFORM_DRIVER ehci_ppc_soc_driver #endif #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ @@ -971,18 +1047,15 @@ static int __init ehci_hcd_init(void) #endif #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { - retval = ps3_system_bus_driver_register( - &PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) { + retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) { #ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); + platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PCI_DRIVER - pci_unregister_driver(&PCI_DRIVER); + pci_unregister_driver(&PCI_DRIVER); #endif - return retval; - } + return retval; } #endif @@ -999,8 +1072,7 @@ static void __exit ehci_hcd_cleanup(void) pci_unregister_driver(&PCI_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + ps3_ehci_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 f4d301b..0dcb416 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -28,6 +28,89 @@ /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_PERSIST + +static int ehci_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +); + +/* After a power loss, ports that were owned by the companion must be + * reset so that the companion can still own them. + */ +static void ehci_handover_companion_ports(struct ehci_hcd *ehci) +{ + u32 __iomem *reg; + u32 status; + int port; + __le32 buf; + struct usb_hcd *hcd = ehci_to_hcd(ehci); + + if (!ehci->owned_ports) + return; + + /* Give the connections some time to appear */ + msleep(20); + + port = HCS_N_PORTS(ehci->hcs_params); + while (port--) { + if (test_bit(port, &ehci->owned_ports)) { + reg = &ehci->regs->port_status[port]; + status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; + + /* Port already owned by companion? */ + if (status & PORT_OWNER) + clear_bit(port, &ehci->owned_ports); + else if (test_bit(port, &ehci->companion_ports)) + ehci_writel(ehci, status & ~PORT_PE, reg); + else + ehci_hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_RESET, port + 1, + NULL, 0); + } + } + + if (!ehci->owned_ports) + return; + msleep(90); /* Wait for resets to complete */ + + port = HCS_N_PORTS(ehci->hcs_params); + while (port--) { + if (test_bit(port, &ehci->owned_ports)) { + ehci_hub_control(hcd, GetPortStatus, + 0, port + 1, + (char *) &buf, sizeof(buf)); + + /* The companion should now own the port, + * but if something went wrong the port must not + * remain enabled. + */ + reg = &ehci->regs->port_status[port]; + status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; + if (status & PORT_OWNER) + ehci_writel(ehci, status | PORT_CSC, reg); + else { + ehci_dbg(ehci, "failed handover port %d: %x\n", + port + 1, status); + ehci_writel(ehci, status & ~PORT_PE, reg); + } + } + } + + ehci->owned_ports = 0; +} + +#else /* CONFIG_USB_PERSIST */ + +static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci) +{ } + +#endif + #ifdef CONFIG_PM static int ehci_bus_suspend (struct usb_hcd *hcd) @@ -60,14 +143,16 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) * then manually resume them in the bus_resume() routine. */ ehci->bus_suspended = 0; + ehci->owned_ports = 0; while (port--) { u32 __iomem *reg = &ehci->regs->port_status [port]; u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; u32 t2 = t1; /* keep track of which ports we suspend */ - if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) && - !(t1 & PORT_SUSPEND)) { + if (t1 & PORT_OWNER) + set_bit(port, &ehci->owned_ports); + else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) { t2 |= PORT_SUSPEND; set_bit(port, &ehci->bus_suspended); } @@ -108,11 +193,16 @@ static int ehci_bus_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; + u32 power_okay; int i; if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + spin_unlock_irq(&ehci->lock); + return -ESHUTDOWN; + } /* Ideally and we've got a real resume here, and no port's power * was lost. (For PCI, that means Vaux was maintained.) But we @@ -120,8 +210,9 @@ 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 = ehci_readl(ehci, &ehci->regs->intr_enable); - ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss"); + power_okay = ehci_readl(ehci, &ehci->regs->intr_enable); + ehci_dbg(ehci, "resume root hub%s\n", + power_okay ? "" : " after power loss"); /* at least some APM implementations will try to deliver * IRQs right away, so delay them until we're ready. @@ -184,6 +275,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); spin_unlock_irq (&ehci->lock); + + if (!power_okay) + ehci_handover_companion_ports(ehci); return 0; } @@ -448,7 +542,8 @@ 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 __iomem *status_reg = &ehci->regs->port_status[ + (wIndex & 0xff) - 1]; u32 temp, status; unsigned long flags; int retval = 0; @@ -556,9 +651,24 @@ static int ehci_hub_control ( status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; - if ((temp & PORT_OCC) && !ignore_oc) + + if ((temp & PORT_OCC) && !ignore_oc){ status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; + /* + * Hubs should disable port power on over-current. + * However, not all EHCI implementations do this + * automatically, even if they _do_ support per-port + * power switching; they're allowed to just limit the + * current. khubd will turn the power back on. + */ + if (HCS_PPC (ehci->hcs_params)){ + ehci_writel(ehci, + temp & ~(PORT_RWC_BITS | PORT_POWER), + status_reg); + } + } + /* whoever resumes must GetPortStatus to complete it!! */ if (temp & PORT_RESUME) { diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index a8ba2e1..8816d09 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -27,7 +27,7 @@ * need to use dma_pool or dma_alloc_coherent * - driver buffers, read/written by HC ... single shot DMA mapped * - * There's also PCI "register" data, which is memory mapped. + * There's also "register" data (e.g. PCI or SOC), which is memory mapped. * No memory seen by this driver is pageable. */ @@ -35,13 +35,14 @@ /* Allocate the key transfer structures from the previously allocated pool */ -static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma) +static inline void ehci_qtd_init(struct ehci_hcd *ehci, struct ehci_qtd *qtd, + dma_addr_t dma) { memset (qtd, 0, sizeof *qtd); qtd->qtd_dma = dma; qtd->hw_token = cpu_to_le32 (QTD_STS_HALT); - qtd->hw_next = EHCI_LIST_END; - qtd->hw_alt_next = EHCI_LIST_END; + qtd->hw_next = EHCI_LIST_END(ehci); + qtd->hw_alt_next = EHCI_LIST_END(ehci); INIT_LIST_HEAD (&qtd->qtd_list); } @@ -52,7 +53,7 @@ static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, gfp_t flags) qtd = dma_pool_alloc (ehci->qtd_pool, flags, &dma); if (qtd != NULL) { - ehci_qtd_init (qtd, dma); + ehci_qtd_init(ehci, qtd, dma); } return qtd; } @@ -63,9 +64,8 @@ static inline void ehci_qtd_free (struct ehci_hcd *ehci, struct ehci_qtd *qtd) } -static void qh_destroy (struct kref *kref) +static void qh_destroy(struct ehci_qh *qh) { - struct ehci_qh *qh = container_of(kref, struct ehci_qh, kref); struct ehci_hcd *ehci = qh->ehci; /* clean qtds first, and know this is not linked */ @@ -89,11 +89,14 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) return qh; memset (qh, 0, sizeof *qh); - kref_init(&qh->kref); + qh->refcount = 1; qh->ehci = ehci; qh->qh_dma = dma; // INIT_LIST_HEAD (&qh->qh_list); INIT_LIST_HEAD (&qh->qtd_list); +#ifdef CONFIG_CPU_FREQ + INIT_LIST_HEAD (&qh->split_intr_qhs); +#endif /* dummy td enables safe urb queuing */ qh->dummy = ehci_qtd_alloc (ehci, flags); @@ -108,13 +111,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) /* to share a qh (cpu threads, or hc) */ static inline struct ehci_qh *qh_get (struct ehci_qh *qh) { - kref_get(&qh->kref); + WARN_ON(!qh->refcount); + qh->refcount++; return qh; } static inline void qh_put (struct ehci_qh *qh) { - kref_put(&qh->kref, qh_destroy); + if (!--qh->refcount) + qh_destroy(qh); } /*-------------------------------------------------------------------------*/ @@ -217,7 +222,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) goto fail; } for (i = 0; i < ehci->periodic_size; i++) - ehci->periodic [i] = EHCI_LIST_END; + ehci->periodic [i] = EHCI_LIST_END(ehci); /* software shadow of hardware table */ ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 966965f..a7816e3 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -312,13 +312,14 @@ static int ehci_pci_resume(struct usb_hcd *hcd) ehci_work(ehci); spin_unlock_irq(&ehci->lock); - /* here we "know" root ports should always stay powered */ - ehci_port_power(ehci, 1); - 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 */ + /* here we "know" root ports should always stay powered */ + ehci_port_power(ehci, 1); + ehci_handover_companion_ports(ehci); + hcd->state = HC_STATE_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ehci-ppc-soc.c b/drivers/usb/host/ehci-ppc-soc.c new file mode 100644 index 0000000..c2cedb0 --- /dev/null +++ b/drivers/usb/host/ehci-ppc-soc.c @@ -0,0 +1,182 @@ +/* + * EHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 2006-2007 Stefan Roese <sr@denx.de>, DENX Software Engineering + * + * Bus Glue for PPC On-Chip EHCI driver + * Tested on AMCC 440EPx + * + * Based on "ehci-au12xx.c" by David Brownell <dbrownell@users.sourceforge.net> + * + * This file is licenced under the GPL. + */ + +#include <linux/platform_device.h> + +extern int usb_disabled(void); + +/** + * usb_ehci_ppc_soc_probe - initialize PPC-SoC-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + */ +int usb_ehci_ppc_soc_probe(const struct hc_driver *driver, + struct usb_hcd **hcd_out, + struct platform_device *dev) +{ + int retval; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + + if (dev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ"); + retval = -ENOMEM; + } + hcd = usb_create_hcd(driver, &dev->dev, "PPC-SOC EHCI"); + if (!hcd) + return -ENOMEM; + hcd->rsrc_start = dev->resource[0].start; + hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed"); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed"); + retval = -ENOMEM; + goto err2; + } + + ehci = hcd_to_ehci(hcd); + ehci->big_endian_mmio = 1; + ehci->big_endian_desc = 1; + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + +#if defined(CONFIG_440EPX) + /* + * 440EPx Errata USBH_3 + * Fix: Enable Break Memory Transfer (BMT) in INSNREG3 + */ + out_be32((void *)((ulong)(&ehci->regs->command) + 0x8c), (1 << 0)); + ehci_dbg(ehci, "Break Memory Transfer (BMT) has beed enabled!\n"); +#endif + + retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); + if (retval == 0) + return retval; + + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + return retval; +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_ehci_hcd_ppc_soc_remove - shutdown processing for PPC-SoC-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_ehci_hcd_ppc_soc_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +void usb_ehci_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device *dev) +{ + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); +} + +static const struct hc_driver ehci_ppc_soc_hc_driver = { + .description = hcd_name, + .product_desc = "PPC-SOC EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .hub_suspend = ehci_hub_suspend, + .hub_resume = ehci_hub_resume, +#endif +}; + +static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd = NULL; + int ret; + + pr_debug("In ehci_hcd_ppc_soc_drv_probe\n"); + + if (usb_disabled()) + return -ENODEV; + + ret = usb_ehci_ppc_soc_probe(&ehci_ppc_soc_hc_driver, &hcd, pdev); + return ret; +} + +static int ehci_hcd_ppc_soc_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_ehci_ppc_soc_remove(hcd, pdev); + return 0; +} + +MODULE_ALIAS("ppc-soc-ehci"); +static struct platform_driver ehci_ppc_soc_driver = { + .probe = ehci_hcd_ppc_soc_drv_probe, + .remove = ehci_hcd_ppc_soc_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ppc-soc-ehci", + .bus = &platform_bus_type + } +}; diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 37b83ba..829fe64 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <asm/firmware.h> #include <asm/ps3.h> static int ps3_ehci_hc_reset(struct usb_hcd *hcd) @@ -73,7 +74,7 @@ static const struct hc_driver ps3_ehci_hc_driver = { #endif }; -static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev) +static int ps3_ehci_probe(struct ps3_system_bus_device *dev) { int result; struct usb_hcd *hcd; @@ -85,13 +86,30 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev) goto fail_start; } + result = ps3_open_hv_device(dev); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed\n", + __func__, __LINE__); + goto fail_open; + } + + result = ps3_dma_region_create(dev->d_region); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: " + "(%d)\n", __func__, __LINE__, result); + BUG_ON("check region type"); + goto fail_dma_region; + } + 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; + goto fail_mmio_region; } dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__, @@ -120,6 +138,11 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev) hcd->rsrc_start = dev->m_region->lpar_addr; hcd->rsrc_len = dev->m_region->len; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) + dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n", + __func__, __LINE__); + hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len); if (!hcd->regs) { @@ -153,34 +176,73 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev) fail_add_hcd: iounmap(hcd->regs); fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); fail_create_hcd: ps3_io_irq_destroy(virq); fail_irq: ps3_free_mmio_region(dev->m_region); -fail_mmio: +fail_mmio_region: + ps3_dma_region_free(dev->d_region); +fail_dma_region: + ps3_close_hv_device(dev); +fail_open: fail_start: return result; } -static int ps3_ehci_sb_remove(struct ps3_system_bus_device *dev) +static int ps3_ehci_remove(struct ps3_system_bus_device *dev) { + unsigned int tmp; struct usb_hcd *hcd = (struct usb_hcd *)ps3_system_bus_get_driver_data(dev); - usb_put_hcd(hcd); + BUG_ON(!hcd); + + dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs); + dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq); + + tmp = hcd->irq; + + usb_remove_hcd(hcd); + ps3_system_bus_set_driver_data(dev, NULL); + BUG_ON(!hcd->regs); + iounmap(hcd->regs); + + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + ps3_io_irq_destroy(tmp); + ps3_free_mmio_region(dev->m_region); + + ps3_dma_region_free(dev->d_region); + ps3_close_hv_device(dev); + return 0; } -MODULE_ALIAS("ps3-ehci"); +static int ps3_ehci_driver_register(struct ps3_system_bus_driver *drv) +{ + return firmware_has_feature(FW_FEATURE_PS3_LV1) + ? ps3_system_bus_driver_register(drv) + : 0; +} + +static void ps3_ehci_driver_unregister(struct ps3_system_bus_driver *drv) +{ + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(drv); +} + +MODULE_ALIAS(PS3_MODULE_ALIAS_EHCI); -static struct ps3_system_bus_driver ps3_ehci_sb_driver = { +static struct ps3_system_bus_driver ps3_ehci_driver = { + .core.name = "ps3-ehci-driver", + .core.owner = THIS_MODULE, .match_id = PS3_MATCH_ID_EHCI, - .core = { - .name = "ps3-ehci-driver", - }, - .probe = ps3_ehci_sb_probe, - .remove = ps3_ehci_sb_remove, + .probe = ps3_ehci_probe, + .remove = ps3_ehci_remove, + .shutdown = ps3_ehci_remove, }; diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index e7fbbd0..2284028 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -43,15 +43,15 @@ /* fill a qtd, returning how much of the buffer we were able to queue up */ static int -qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, - int token, int maxpacket) +qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf, + size_t len, int token, int maxpacket) { int i, count; u64 addr = buf; /* one buffer entry per 4K ... first might be short or unaligned */ - qtd->hw_buf [0] = cpu_to_le32 ((u32)addr); - qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32)); + qtd->hw_buf[0] = cpu_to_hc32(ehci, (u32)addr); + qtd->hw_buf_hi[0] = cpu_to_hc32(ehci, (u32)(addr >> 32)); count = 0x1000 - (buf & 0x0fff); /* rest of that page */ if (likely (len < count)) /* ... iff needed */ count = len; @@ -62,8 +62,9 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, /* per-qtd limit: from 16K to 20K (best alignment) */ for (i = 1; count < len && i < 5; i++) { addr = buf; - qtd->hw_buf [i] = cpu_to_le32 ((u32)addr); - qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32)); + qtd->hw_buf[i] = cpu_to_hc32(ehci, (u32)addr); + qtd->hw_buf_hi[i] = cpu_to_hc32(ehci, + (u32)(addr >> 32)); buf += 0x1000; if ((count + 0x1000) < len) count += 0x1000; @@ -75,7 +76,7 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, if (count != len) count -= (count % maxpacket); } - qtd->hw_token = cpu_to_le32 ((count << 16) | token); + qtd->hw_token = cpu_to_hc32(ehci, (count << 16) | token); qtd->length = count; return count; @@ -89,28 +90,28 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) /* writes to an active overlay are unsafe */ BUG_ON(qh->qh_state != QH_STATE_IDLE); - qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma); - qh->hw_alt_next = EHCI_LIST_END; + qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); + qh->hw_alt_next = EHCI_LIST_END(ehci); /* Except for control endpoints, we make hardware maintain data * toggle (like OHCI) ... here (re)initialize the toggle in the QH, * and set the pseudo-toggle in udev. Only usb_clear_halt() will * ever clear it. */ - if (!(qh->hw_info1 & cpu_to_le32(1 << 14))) { + if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { unsigned is_out, epnum; - is_out = !(qtd->hw_token & cpu_to_le32(1 << 8)); - epnum = (le32_to_cpup(&qh->hw_info1) >> 8) & 0x0f; + is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8)); + epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f; if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { - qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE); + qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); usb_settoggle (qh->dev, epnum, is_out, 1); } } /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ wmb (); - qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING); + qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); } /* if it weren't for a common silicon quirk (writing the dummy into the qh @@ -128,7 +129,7 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list); /* first qtd may already be partially processed */ - if (cpu_to_le32 (qtd->qtd_dma) == qh->hw_current) + if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw_current) qtd = NULL; } @@ -222,7 +223,7 @@ __acquires(ehci->lock) struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; /* S-mask in a QH means it's an interrupt urb */ - if ((qh->hw_info2 & __constant_cpu_to_le32 (QH_SMASK)) != 0) { + if ((qh->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) { /* ... update hc-wide periodic stats (for usbfs) */ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--; @@ -277,7 +278,6 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh); * Chases up to qh->hw_current. Returns number of completions called, * indicating how much "real" work we did. */ -#define HALT_BIT __constant_cpu_to_le32(QTD_STS_HALT) static unsigned qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) { @@ -287,6 +287,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) unsigned count = 0; int do_status = 0; u8 state; + u32 halt = HALT_BIT(ehci); if (unlikely (list_empty (&qh->qtd_list))) return count; @@ -311,6 +312,10 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) struct urb *urb; u32 token = 0; + /* ignore QHs that are currently inactive */ + if (qh->hw_info1 & __constant_cpu_to_le32(QH_INACTIVATE)) + break; + qtd = list_entry (entry, struct ehci_qtd, qtd_list); urb = qtd->urb; @@ -330,7 +335,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* hardware copies qtd out of qh overlay */ rmb (); - token = le32_to_cpu (qtd->hw_token); + token = hc32_to_cpu(ehci, qtd->hw_token); /* always clean up qtds the hc de-activated */ if ((token & QTD_STS_ACTIVE) == 0) { @@ -342,7 +347,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) * that silicon quirk can kick in with this dummy too. */ } else if (IS_SHORT_READ (token) - && !(qtd->hw_alt_next & EHCI_LIST_END)) { + && !(qtd->hw_alt_next + & EHCI_LIST_END(ehci))) { stopped = 1; goto halt; } @@ -374,17 +380,17 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* token in overlay may be most current */ if (state == QH_STATE_IDLE - && cpu_to_le32 (qtd->qtd_dma) + && cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw_current) - token = le32_to_cpu (qh->hw_token); + token = hc32_to_cpu(ehci, qh->hw_token); /* force halt for unlinked or blocked qh, so we'll * patch the qh later and so that completions can't * activate it while we "know" it's stopped. */ - if ((HALT_BIT & qh->hw_token) == 0) { + if ((halt & qh->hw_token) == 0) { halt: - qh->hw_token |= HALT_BIT; + qh->hw_token |= halt; wmb (); } } @@ -419,7 +425,7 @@ halt: * it after fault cleanup, or recovering from silicon wrongly * overlaying the dummy qtd (which reduces DMA chatter). */ - if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END) { + if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) { switch (state) { case QH_STATE_IDLE: qh_refresh(ehci, qh); @@ -428,7 +434,7 @@ halt: /* should be rare for periodic transfers, * except maybe high bandwidth ... */ - if ((__constant_cpu_to_le32 (QH_SMASK) + if ((cpu_to_hc32(ehci, QH_SMASK) & qh->hw_info2) != 0) { intr_deschedule (ehci, qh); (void) qh_schedule (ehci, qh); @@ -502,8 +508,9 @@ qh_urb_transaction ( is_input = usb_pipein (urb->pipe); if (usb_pipecontrol (urb->pipe)) { /* SETUP pid */ - qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest), - token | (2 /* "setup" */ << 8), 8); + qtd_fill(ehci, qtd, urb->setup_dma, + sizeof (struct usb_ctrlrequest), + token | (2 /* "setup" */ << 8), 8); /* ... and always at least one more pid */ token ^= QTD_TOGGLE; @@ -512,7 +519,7 @@ qh_urb_transaction ( if (unlikely (!qtd)) goto cleanup; qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); + qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); /* for zero length DATA stages, STATUS is always IN */ @@ -539,7 +546,7 @@ qh_urb_transaction ( for (;;) { int this_qtd_len; - this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket); + this_qtd_len = qtd_fill(ehci, qtd, buf, len, token, maxpacket); len -= this_qtd_len; buf += this_qtd_len; if (is_input) @@ -557,7 +564,7 @@ qh_urb_transaction ( if (unlikely (!qtd)) goto cleanup; qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); + qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); } @@ -566,7 +573,7 @@ qh_urb_transaction ( */ if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 || usb_pipecontrol (urb->pipe))) - qtd->hw_alt_next = EHCI_LIST_END; + qtd->hw_alt_next = EHCI_LIST_END(ehci); /* * control requests may need a terminating data "status" ack; @@ -590,17 +597,17 @@ qh_urb_transaction ( if (unlikely (!qtd)) goto cleanup; qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); + qtd_prev->hw_next = QTD_NEXT(ehci, qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); /* never any data in such packets */ - qtd_fill (qtd, 0, 0, token, 0); + qtd_fill(ehci, qtd, 0, 0, token, 0); } } /* by default, enable interrupt on urb completion */ if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT))) - qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC); + qtd->hw_token |= cpu_to_hc32(ehci, QTD_IOC); return head; cleanup: @@ -769,8 +776,8 @@ done: /* init as live, toggle clear, advance to dummy */ qh->qh_state = QH_STATE_IDLE; - qh->hw_info1 = cpu_to_le32 (info1); - qh->hw_info2 = cpu_to_le32 (info2); + qh->hw_info1 = cpu_to_hc32(ehci, info1); + qh->hw_info2 = cpu_to_hc32(ehci, info2); usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); qh_refresh (ehci, qh); return qh; @@ -782,7 +789,7 @@ done: static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { - __le32 dma = QH_NEXT (qh->qh_dma); + __hc32 dma = QH_NEXT(ehci, qh->qh_dma); struct ehci_qh *head; /* (re)start the async schedule? */ @@ -820,8 +827,6 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /*-------------------------------------------------------------------------*/ -#define QH_ADDR_MASK __constant_cpu_to_le32(0x7f) - /* * For control/bulk/interrupt, return QH with these TDs appended. * Allocates and initializes the QH if necessary. @@ -837,6 +842,7 @@ static struct ehci_qh *qh_append_tds ( ) { struct ehci_qh *qh = NULL; + u32 qh_addr_mask = cpu_to_hc32(ehci, 0x7f); qh = (struct ehci_qh *) *ptr; if (unlikely (qh == NULL)) { @@ -858,7 +864,7 @@ static struct ehci_qh *qh_append_tds ( /* usb_reset_device() briefly reverts to address 0 */ if (usb_pipedevice (urb->pipe) == 0) - qh->hw_info1 &= ~QH_ADDR_MASK; + qh->hw_info1 &= ~qh_addr_mask; } /* just one way to queue requests: swap with the dummy qtd. @@ -867,7 +873,7 @@ static struct ehci_qh *qh_append_tds ( if (likely (qtd != NULL)) { struct ehci_qtd *dummy; dma_addr_t dma; - __le32 token; + __hc32 token; /* to avoid racing the HC, use the dummy td instead of * the first td of our list (becomes new dummy). both @@ -875,7 +881,7 @@ static struct ehci_qh *qh_append_tds ( * HC is allowed to fetch the old dummy (4.10.2). */ token = qtd->hw_token; - qtd->hw_token = HALT_BIT; + qtd->hw_token = HALT_BIT(ehci); wmb (); dummy = qh->dummy; @@ -887,14 +893,14 @@ static struct ehci_qh *qh_append_tds ( list_add (&dummy->qtd_list, qtd_list); __list_splice (qtd_list, qh->qtd_list.prev); - ehci_qtd_init (qtd, qtd->qtd_dma); + ehci_qtd_init(ehci, qtd, qtd->qtd_dma); qh->dummy = qtd; /* hc must see the new dummy at list end */ dma = qtd->qtd_dma; qtd = list_entry (qh->qtd_list.prev, struct ehci_qtd, qtd_list); - qtd->hw_next = QTD_NEXT (dma); + qtd->hw_next = QTD_NEXT(ehci, dma); /* let the hc process these next qtds */ wmb (); @@ -970,7 +976,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) timer_action_done (ehci, TIMER_IAA_WATCHDOG); - // qh->hw_next = cpu_to_le32 (qh->qh_dma); + // qh->hw_next = cpu_to_hc32(qh->qh_dma); qh->qh_state = QH_STATE_IDLE; qh->qh_next.qh = NULL; qh_put (qh); // refcount from reclaim diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 7b5ae71..d4a8ace 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -44,9 +44,10 @@ static int ehci_get_frame (struct usb_hcd *hcd); * @tag: hardware tag for type of this record */ static union ehci_shadow * -periodic_next_shadow (union ehci_shadow *periodic, __le32 tag) +periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic, + __hc32 tag) { - switch (tag) { + switch (hc32_to_cpu(ehci, tag)) { case Q_TYPE_QH: return &periodic->qh->qh_next; case Q_TYPE_FSTN: @@ -62,13 +63,14 @@ periodic_next_shadow (union ehci_shadow *periodic, __le32 tag) /* caller must hold ehci->lock */ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) { - union ehci_shadow *prev_p = &ehci->pshadow [frame]; - __le32 *hw_p = &ehci->periodic [frame]; + union ehci_shadow *prev_p = &ehci->pshadow[frame]; + __hc32 *hw_p = &ehci->periodic[frame]; union ehci_shadow here = *prev_p; /* find predecessor of "ptr"; hw and shadow lists are in sync */ while (here.ptr && here.ptr != ptr) { - prev_p = periodic_next_shadow (prev_p, Q_NEXT_TYPE (*hw_p)); + prev_p = periodic_next_shadow(ehci, prev_p, + Q_NEXT_TYPE(ehci, *hw_p)); hw_p = here.hw_next; here = *prev_p; } @@ -79,7 +81,8 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) /* update shadow and hardware lists ... the old "next" pointers * from ptr may still be in use, the caller updates them. */ - *prev_p = *periodic_next_shadow (&here, Q_NEXT_TYPE (*hw_p)); + *prev_p = *periodic_next_shadow(ehci, &here, + Q_NEXT_TYPE(ehci, *hw_p)); *hw_p = *here.hw_next; } @@ -87,18 +90,19 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) static unsigned short periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) { - __le32 *hw_p = &ehci->periodic [frame]; + __hc32 *hw_p = &ehci->periodic [frame]; union ehci_shadow *q = &ehci->pshadow [frame]; unsigned usecs = 0; while (q->ptr) { - switch (Q_NEXT_TYPE (*hw_p)) { + switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) { case Q_TYPE_QH: /* is it in the S-mask? */ - if (q->qh->hw_info2 & cpu_to_le32 (1 << uframe)) + if (q->qh->hw_info2 & cpu_to_hc32(ehci, 1 << uframe)) usecs += q->qh->usecs; /* ... or C-mask? */ - if (q->qh->hw_info2 & cpu_to_le32 (1 << (8 + uframe))) + if (q->qh->hw_info2 & cpu_to_hc32(ehci, + 1 << (8 + uframe))) usecs += q->qh->c_usecs; hw_p = &q->qh->hw_next; q = &q->qh->qh_next; @@ -108,7 +112,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) /* for "save place" FSTNs, count the relevant INTR * bandwidth from the previous frame */ - if (q->fstn->hw_prev != EHCI_LIST_END) { + if (q->fstn->hw_prev != EHCI_LIST_END(ehci)) { ehci_dbg (ehci, "ignoring FSTN cost ...\n"); } hw_p = &q->fstn->hw_next; @@ -121,9 +125,10 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) break; case Q_TYPE_SITD: /* is it in the S-mask? (count SPLIT, DATA) */ - if (q->sitd->hw_uframe & cpu_to_le32 (1 << uframe)) { + if (q->sitd->hw_uframe & cpu_to_hc32(ehci, + 1 << uframe)) { if (q->sitd->hw_fullspeed_ep & - __constant_cpu_to_le32 (1<<31)) + cpu_to_hc32(ehci, 1<<31)) usecs += q->sitd->stream->usecs; else /* worst case for OUT start-split */ usecs += HS_USECS_ISO (188); @@ -131,7 +136,7 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) /* ... C-mask? (count CSPLIT, DATA) */ if (q->sitd->hw_uframe & - cpu_to_le32 (1 << (8 + uframe))) { + cpu_to_hc32(ehci, 1 << (8 + uframe))) { /* worst case for IN complete-split */ usecs += q->sitd->stream->c_usecs; } @@ -173,9 +178,9 @@ static int same_tt (struct usb_device *dev1, struct usb_device *dev2) * will cause a transfer in "B-frame" uframe 0. "B-frames" lag * "H-frames" by 1 uframe. See the EHCI spec sec 4.5 and figure 4.7. */ -static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __le32 mask) +static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask) { - unsigned char smask = QH_SMASK & le32_to_cpu(mask); + unsigned char smask = QH_SMASK & hc32_to_cpu(ehci, mask); if (!smask) { ehci_err(ehci, "invalid empty smask!\n"); /* uframe 7 can't have bw so this will indicate failure */ @@ -217,14 +222,14 @@ periodic_tt_usecs ( unsigned short tt_usecs[8] ) { - __le32 *hw_p = &ehci->periodic [frame]; + __hc32 *hw_p = &ehci->periodic [frame]; union ehci_shadow *q = &ehci->pshadow [frame]; unsigned char uf; memset(tt_usecs, 0, 16); while (q->ptr) { - switch (Q_NEXT_TYPE(*hw_p)) { + switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) { case Q_TYPE_ITD: hw_p = &q->itd->hw_next; q = &q->itd->itd_next; @@ -247,8 +252,8 @@ periodic_tt_usecs ( continue; // case Q_TYPE_FSTN: default: - ehci_dbg(ehci, - "ignoring periodic frame %d FSTN\n", frame); + ehci_dbg(ehci, "ignoring periodic frame %d FSTN\n", + frame); hw_p = &q->fstn->hw_next; q = &q->fstn->fstn_next; } @@ -368,41 +373,42 @@ static int tt_no_collision ( */ for (; frame < ehci->periodic_size; frame += period) { union ehci_shadow here; - __le32 type; + __hc32 type; here = ehci->pshadow [frame]; - type = Q_NEXT_TYPE (ehci->periodic [frame]); + type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]); while (here.ptr) { - switch (type) { + switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_ITD: - type = Q_NEXT_TYPE (here.itd->hw_next); + type = Q_NEXT_TYPE(ehci, here.itd->hw_next); here = here.itd->itd_next; continue; case Q_TYPE_QH: if (same_tt (dev, here.qh->dev)) { u32 mask; - mask = le32_to_cpu (here.qh->hw_info2); + mask = hc32_to_cpu(ehci, + here.qh->hw_info2); /* "knows" no gap is needed */ mask |= mask >> 8; if (mask & uf_mask) break; } - type = Q_NEXT_TYPE (here.qh->hw_next); + type = Q_NEXT_TYPE(ehci, here.qh->hw_next); here = here.qh->qh_next; continue; case Q_TYPE_SITD: if (same_tt (dev, here.sitd->urb->dev)) { u16 mask; - mask = le32_to_cpu (here.sitd + mask = hc32_to_cpu(ehci, here.sitd ->hw_uframe); /* FIXME assumes no gap for IN! */ mask |= mask >> 8; if (mask & uf_mask) break; } - type = Q_NEXT_TYPE (here.sitd->hw_next); + type = Q_NEXT_TYPE(ehci, here.sitd->hw_next); here = here.sitd->sitd_next; continue; // case Q_TYPE_FSTN: @@ -473,6 +479,109 @@ static int disable_periodic (struct ehci_hcd *ehci) } /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_CPU_FREQ + +static int safe_to_modify_i (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + int now; /* current (frame * 8) + uframe */ + int prev_start, next_start; /* uframes from/to split start */ + int start_uframe = ffs(le32_to_cpup (&qh->hw_info2) & QH_SMASK); + int end_uframe = fls((le32_to_cpup (&qh->hw_info2) & QH_CMASK) >> 8); + int split_duration = end_uframe - start_uframe; + + now = readl(&ehci->regs->frame_index) % (ehci->periodic_size << 3); + + next_start = ((1024 << 3) + (qh->start << 3) + start_uframe - now) + % (qh->period << 3); + prev_start = (qh->period << 3) - next_start; + + /* + * Make sure there will be at least one uframe when qh is safe. + */ + if ((qh->period << 3) <= (ehci->i_thresh + 2 + split_duration)) + /* never safe */ + return -EINVAL; + + /* + * Wait 1 uframe after transaction should have started, to make + * sure controller has time to write back overlay, so we can + * check QTD_STS_STS to see if transaction is in progress. + */ + if ((next_start > ehci->i_thresh) && (prev_start > 1)) + /* safe to set "i" bit if split isn't in progress */ + return (qh->hw_token & STATUS_BIT(ehci)) ? 0 : 1; + else + return 0; +} + +/* Set inactivate bit for all the split interrupt QHs. */ +static void qh_inactivate_split_intr_qhs (struct ehci_hcd *ehci) +{ + struct ehci_qh *qh; + int not_done, safe; + u32 inactivate = INACTIVATE_BIT(ehci); + u32 active = ACTIVE_BIT(ehci); + + do { + not_done = 0; + list_for_each_entry(qh, &ehci->split_intr_qhs, + split_intr_qhs) { + if (qh->hw_info1 & inactivate) + /* already off */ + continue; + /* + * To avoid setting "I" after the start split happens, + * don't set it if the QH might be cached in the + * controller. Some HCs (Broadcom/ServerWorks HT1000) + * will stop in the middle of a split transaction when + * the "I" bit is set. + */ + safe = safe_to_modify_i(ehci, qh); + if (safe == 0) { + not_done = 1; + } else if (safe > 0) { + qh->was_active = qh->hw_token & active; + qh->hw_info1 |= inactivate; + } + } + } while (not_done); + wmb(); +} + +static void qh_reactivate_split_intr_qhs (struct ehci_hcd *ehci) +{ + struct ehci_qh *qh; + u32 token; + int not_done, safe; + u32 inactivate = INACTIVATE_BIT(ehci); + u32 active = ACTIVE_BIT(ehci); + u32 halt = HALT_BIT(ehci); + + do { + not_done = 0; + list_for_each_entry(qh, &ehci->split_intr_qhs, split_intr_qhs) { + if (!(qh->hw_info1 & inactivate)) /* already on */ + continue; + /* + * Don't reactivate if cached, or controller might + * overwrite overlay after we modify it! + */ + safe = safe_to_modify_i(ehci, qh); + if (safe == 0) { + not_done = 1; + } else if (safe > 0) { + /* See EHCI 1.0 section 4.15.2.4. */ + token = qh->hw_token; + qh->hw_token = (token | halt) & ~active; + wmb(); + qh->hw_info1 &= ~inactivate; + wmb(); + qh->hw_token = (token & ~halt) | qh->was_active; + } + } + } while (not_done); +} +#endif /* periodic schedule slots have iso tds (normal or split) first, then a * sparse tree for active interrupt transfers. @@ -487,25 +596,36 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) dev_dbg (&qh->dev->dev, "link qh%d-%04x/%p start %d [%d/%d us]\n", - period, le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), + period, hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); +#ifdef CONFIG_CPU_FREQ + /* + * If low/full speed interrupt QHs are inactive (because of + * cpufreq changing processor speeds), start QH with I flag set-- + * it will automatically be cleared when cpufreq is done. + */ + if (ehci->cpufreq_changing) + if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) + qh->hw_info1 |= INACTIVATE_BIT(ehci); +#endif + /* high bandwidth, or otherwise every microframe */ if (period == 0) period = 1; for (i = qh->start; i < ehci->periodic_size; i += period) { - union ehci_shadow *prev = &ehci->pshadow [i]; - __le32 *hw_p = &ehci->periodic [i]; + union ehci_shadow *prev = &ehci->pshadow[i]; + __hc32 *hw_p = &ehci->periodic[i]; union ehci_shadow here = *prev; - __le32 type = 0; + __hc32 type = 0; /* skip the iso nodes at list head */ while (here.ptr) { - type = Q_NEXT_TYPE (*hw_p); - if (type == Q_TYPE_QH) + type = Q_NEXT_TYPE(ehci, *hw_p); + if (type == cpu_to_hc32(ehci, Q_TYPE_QH)) break; - prev = periodic_next_shadow (prev, type); + prev = periodic_next_shadow(ehci, prev, type); hw_p = &here.qh->hw_next; here = *prev; } @@ -527,7 +647,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->hw_next = *hw_p; wmb (); prev->qh = qh; - *hw_p = QH_NEXT (qh->qh_dma); + *hw_p = QH_NEXT (ehci, qh->qh_dma); } } qh->qh_state = QH_STATE_LINKED; @@ -538,6 +658,12 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) ? ((qh->usecs + qh->c_usecs) / qh->period) : (qh->usecs * 8); +#ifdef CONFIG_CPU_FREQ + /* add qh to list of low/full speed interrupt QHs, if applicable */ + if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) { + list_add(&qh->split_intr_qhs, &ehci->split_intr_qhs); + } +#endif /* maybe enable periodic schedule processing */ if (!ehci->periodic_sched++) return enable_periodic (ehci); @@ -555,7 +681,14 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) // and this qh is active in the current uframe // (and overlay token SplitXstate is false?) // THEN - // qh->hw_info1 |= __constant_cpu_to_le32 (1 << 7 /* "ignore" */); + // qh->hw_info1 |= __constant_cpu_to_hc32(1 << 7 /* "ignore" */); + +#ifdef CONFIG_CPU_FREQ + /* remove qh from list of low/full speed interrupt QHs */ + if (!(qh->hw_info1 & (cpu_to_le32(1 << 13)))) { + list_del_init(&qh->split_intr_qhs); + } +#endif /* high bandwidth, or otherwise part of every microframe */ if ((period = qh->period) == 0) @@ -572,7 +705,7 @@ static void qh_unlink_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh) dev_dbg (&qh->dev->dev, "unlink qh%d-%04x/%p start %d [%d/%d us]\n", qh->period, - le32_to_cpup (&qh->hw_info2) & (QH_CMASK | QH_SMASK), + hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); /* qh->qh_next still "live" to HC */ @@ -598,7 +731,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) * active high speed queues may need bigger delays... */ if (list_empty (&qh->qtd_list) - || (__constant_cpu_to_le32 (QH_CMASK) + || (cpu_to_hc32(ehci, QH_CMASK) & qh->hw_info2) != 0) wait = 2; else @@ -606,7 +739,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) udelay (wait); qh->qh_state = QH_STATE_IDLE; - qh->hw_next = EHCI_LIST_END; + qh->hw_next = EHCI_LIST_END(ehci); wmb (); } @@ -663,7 +796,7 @@ static int check_intr_schedule ( unsigned frame, unsigned uframe, const struct ehci_qh *qh, - __le32 *c_maskp + __hc32 *c_maskp ) { int retval = -ENOSPC; @@ -695,7 +828,7 @@ static int check_intr_schedule ( retval = 0; - *c_maskp = cpu_to_le32 (mask << 8); + *c_maskp = cpu_to_hc32(ehci, mask << 8); } #else /* Make sure this tt's buffer is also available for CSPLITs. @@ -706,7 +839,7 @@ static int check_intr_schedule ( * one smart pass... */ mask = 0x03 << (uframe + qh->gap_uf); - *c_maskp = cpu_to_le32 (mask << 8); + *c_maskp = cpu_to_hc32(ehci, mask << 8); mask |= 1 << uframe; if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) { @@ -726,20 +859,20 @@ done: /* "first fit" scheduling policy used the first time through, * or when the previous schedule slot can't be re-used. */ -static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh) { int status; unsigned uframe; - __le32 c_mask; + __hc32 c_mask; unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ qh_refresh(ehci, qh); - qh->hw_next = EHCI_LIST_END; + qh->hw_next = EHCI_LIST_END(ehci); frame = qh->start; /* reuse the previous schedule slots, if we can */ if (frame < qh->period) { - uframe = ffs (le32_to_cpup (&qh->hw_info2) & QH_SMASK); + uframe = ffs(hc32_to_cpup(ehci, &qh->hw_info2) & QH_SMASK); status = check_intr_schedule (ehci, frame, --uframe, qh, &c_mask); } else { @@ -775,10 +908,10 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) qh->start = frame; /* reset S-frame and (maybe) C-frame masks */ - qh->hw_info2 &= __constant_cpu_to_le32(~(QH_CMASK | QH_SMASK)); + qh->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK)); qh->hw_info2 |= qh->period - ? cpu_to_le32 (1 << uframe) - : __constant_cpu_to_le32 (QH_SMASK); + ? cpu_to_hc32(ehci, 1 << uframe) + : cpu_to_hc32(ehci, QH_SMASK); qh->hw_info2 |= c_mask; } else ehci_dbg (ehci, "reused qh %p schedule\n", qh); @@ -808,7 +941,7 @@ static int intr_submit ( spin_lock_irqsave (&ehci->lock, flags); if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, - &ehci_to_hcd(ehci)->flags))) { + &ehci_to_hcd(ehci)->flags))) { status = -ESHUTDOWN; goto done; } @@ -898,9 +1031,9 @@ iso_stream_init ( buf1 |= maxp; maxp *= multi; - stream->buf0 = cpu_to_le32 ((epnum << 8) | dev->devnum); - stream->buf1 = cpu_to_le32 (buf1); - stream->buf2 = cpu_to_le32 (multi); + stream->buf0 = cpu_to_hc32(ehci, (epnum << 8) | dev->devnum); + stream->buf1 = cpu_to_hc32(ehci, buf1); + stream->buf2 = cpu_to_hc32(ehci, multi); /* usbfs wants to report the average usecs per frame tied up * when transfers on this endpoint are scheduled ... @@ -943,7 +1076,7 @@ iso_stream_init ( bandwidth /= 1 << (interval + 2); /* stream->splits gets created from raw_mask later */ - stream->address = cpu_to_le32 (addr); + stream->address = cpu_to_hc32(ehci, addr); } stream->bandwidth = bandwidth; @@ -1077,7 +1210,8 @@ iso_sched_alloc (unsigned packets, gfp_t mem_flags) } static inline void -itd_sched_init ( +itd_sched_init( + struct ehci_hcd *ehci, struct ehci_iso_sched *iso_sched, struct ehci_iso_stream *stream, struct urb *urb @@ -1107,7 +1241,7 @@ itd_sched_init ( && !(urb->transfer_flags & URB_NO_INTERRUPT)) trans |= EHCI_ITD_IOC; trans |= length << 16; - uframe->transaction = cpu_to_le32 (trans); + uframe->transaction = cpu_to_hc32(ehci, trans); /* might need to cross a buffer page within a uframe */ uframe->bufp = (buf & ~(u64)0x0fff); @@ -1149,7 +1283,7 @@ itd_urb_transaction ( if (unlikely (sched == NULL)) return -ENOMEM; - itd_sched_init (sched, stream, urb); + itd_sched_init(ehci, sched, stream, urb); if (urb->interval < 8) num_itds = 1 + (sched->span + 7) / 8; @@ -1167,7 +1301,7 @@ itd_urb_transaction ( /* prefer previously-allocated itds */ if (likely (!list_empty(&stream->free_list))) { itd = list_entry (stream->free_list.prev, - struct ehci_itd, itd_list); + struct ehci_itd, itd_list); list_del (&itd->itd_list); itd_dma = itd->itd_dma; } else @@ -1294,7 +1428,7 @@ sitd_slot_ok ( uframe += period_uframes; } while (uframe < mod); - stream->splits = cpu_to_le32(stream->raw_mask << (uframe & 7)); + stream->splits = cpu_to_hc32(ehci, stream->raw_mask << (uframe & 7)); return 1; } @@ -1415,12 +1549,13 @@ ready: /*-------------------------------------------------------------------------*/ static inline void -itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd) +itd_init(struct ehci_hcd *ehci, struct ehci_iso_stream *stream, + struct ehci_itd *itd) { int i; /* it's been recently zeroed */ - itd->hw_next = EHCI_LIST_END; + itd->hw_next = EHCI_LIST_END(ehci); itd->hw_bufp [0] = stream->buf0; itd->hw_bufp [1] = stream->buf1; itd->hw_bufp [2] = stream->buf2; @@ -1432,7 +1567,8 @@ itd_init (struct ehci_iso_stream *stream, struct ehci_itd *itd) } static inline void -itd_patch ( +itd_patch( + struct ehci_hcd *ehci, struct ehci_itd *itd, struct ehci_iso_sched *iso_sched, unsigned index, @@ -1447,17 +1583,18 @@ itd_patch ( uframe &= 0x07; itd->index [uframe] = index; - itd->hw_transaction [uframe] = uf->transaction; - itd->hw_transaction [uframe] |= cpu_to_le32 (pg << 12); - itd->hw_bufp [pg] |= cpu_to_le32 (uf->bufp & ~(u32)0); - itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(uf->bufp >> 32)); + itd->hw_transaction[uframe] = uf->transaction; + itd->hw_transaction[uframe] |= cpu_to_hc32(ehci, pg << 12); + itd->hw_bufp[pg] |= cpu_to_hc32(ehci, uf->bufp & ~(u32)0); + itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(uf->bufp >> 32)); /* iso_frame_desc[].offset must be strictly increasing */ if (unlikely (uf->cross)) { u64 bufp = uf->bufp + 4096; + itd->pg = ++pg; - itd->hw_bufp [pg] |= cpu_to_le32 (bufp & ~(u32)0); - itd->hw_bufp_hi [pg] |= cpu_to_le32 ((u32)(bufp >> 32)); + itd->hw_bufp[pg] |= cpu_to_hc32(ehci, bufp & ~(u32)0); + itd->hw_bufp_hi[pg] |= cpu_to_hc32(ehci, (u32)(bufp >> 32)); } } @@ -1470,7 +1607,7 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) ehci->pshadow [frame].itd = itd; itd->frame = frame; wmb (); - ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD; + ehci->periodic[frame] = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD); } /* fit urb's itds into the selected schedule slot; activate as needed */ @@ -1515,14 +1652,14 @@ itd_link_urb ( list_move_tail (&itd->itd_list, &stream->td_list); itd->stream = iso_stream_get (stream); itd->urb = usb_get_urb (urb); - itd_init (stream, itd); + itd_init (ehci, stream, itd); } uframe = next_uframe & 0x07; frame = next_uframe >> 3; itd->usecs [uframe] = stream->usecs; - itd_patch (itd, iso_sched, packet, uframe); + itd_patch(ehci, itd, iso_sched, packet, uframe); next_uframe += stream->interval; stream->depth += stream->interval; @@ -1570,7 +1707,7 @@ itd_complete ( urb_index = itd->index[uframe]; desc = &urb->iso_frame_desc [urb_index]; - t = le32_to_cpup (&itd->hw_transaction [uframe]); + t = hc32_to_cpup(ehci, &itd->hw_transaction [uframe]); itd->hw_transaction [uframe] = 0; stream->depth -= stream->interval; @@ -1700,7 +1837,8 @@ done: */ static inline void -sitd_sched_init ( +sitd_sched_init( + struct ehci_hcd *ehci, struct ehci_iso_sched *iso_sched, struct ehci_iso_stream *stream, struct urb *urb @@ -1729,7 +1867,7 @@ sitd_sched_init ( && !(urb->transfer_flags & URB_NO_INTERRUPT)) trans |= SITD_IOC; trans |= length << 16; - packet->transaction = cpu_to_le32 (trans); + packet->transaction = cpu_to_hc32(ehci, trans); /* might need to cross a buffer page within a td */ packet->bufp = buf; @@ -1765,7 +1903,7 @@ sitd_urb_transaction ( if (iso_sched == NULL) return -ENOMEM; - sitd_sched_init (iso_sched, stream, urb); + sitd_sched_init(ehci, iso_sched, stream, urb); /* allocate/init sITDs */ spin_lock_irqsave (&ehci->lock, flags); @@ -1817,7 +1955,8 @@ sitd_urb_transaction ( /*-------------------------------------------------------------------------*/ static inline void -sitd_patch ( +sitd_patch( + struct ehci_hcd *ehci, struct ehci_iso_stream *stream, struct ehci_sitd *sitd, struct ehci_iso_sched *iso_sched, @@ -1827,20 +1966,20 @@ sitd_patch ( struct ehci_iso_packet *uf = &iso_sched->packet [index]; u64 bufp = uf->bufp; - sitd->hw_next = EHCI_LIST_END; + sitd->hw_next = EHCI_LIST_END(ehci); sitd->hw_fullspeed_ep = stream->address; sitd->hw_uframe = stream->splits; sitd->hw_results = uf->transaction; - sitd->hw_backpointer = EHCI_LIST_END; + sitd->hw_backpointer = EHCI_LIST_END(ehci); bufp = uf->bufp; - sitd->hw_buf [0] = cpu_to_le32 (bufp); - sitd->hw_buf_hi [0] = cpu_to_le32 (bufp >> 32); + sitd->hw_buf[0] = cpu_to_hc32(ehci, bufp); + sitd->hw_buf_hi[0] = cpu_to_hc32(ehci, bufp >> 32); - sitd->hw_buf [1] = cpu_to_le32 (uf->buf1); + sitd->hw_buf[1] = cpu_to_hc32(ehci, uf->buf1); if (uf->cross) bufp += 4096; - sitd->hw_buf_hi [1] = cpu_to_le32 (bufp >> 32); + sitd->hw_buf_hi[1] = cpu_to_hc32(ehci, bufp >> 32); sitd->index = index; } @@ -1853,7 +1992,7 @@ sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd) ehci->pshadow [frame].sitd = sitd; sitd->frame = frame; wmb (); - ehci->periodic [frame] = cpu_to_le32 (sitd->sitd_dma) | Q_TYPE_SITD; + ehci->periodic[frame] = cpu_to_hc32(ehci, sitd->sitd_dma | Q_TYPE_SITD); } /* fit urb's sitds into the selected schedule slot; activate as needed */ @@ -1881,7 +2020,7 @@ sitd_link_urb ( urb->dev->devpath, stream->bEndpointAddress & 0x0f, (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", (next_uframe >> 3) % ehci->periodic_size, - stream->interval, le32_to_cpu (stream->splits)); + stream->interval, hc32_to_cpu(ehci, stream->splits)); stream->start = jiffies; } ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; @@ -1902,7 +2041,7 @@ sitd_link_urb ( sitd->stream = iso_stream_get (stream); sitd->urb = usb_get_urb (urb); - sitd_patch (stream, sitd, sched, packet); + sitd_patch(ehci, stream, sitd, sched, packet); sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, sitd); @@ -1940,7 +2079,7 @@ sitd_complete ( urb_index = sitd->index; desc = &urb->iso_frame_desc [urb_index]; - t = le32_to_cpup (&sitd->hw_results); + t = hc32_to_cpup(ehci, &sitd->hw_results); /* report transfer status */ if (t & SITD_ERRS) { @@ -2095,7 +2234,7 @@ scan_periodic (struct ehci_hcd *ehci) for (;;) { union ehci_shadow q, *q_p; - __le32 type, *hw_p; + __hc32 type, *hw_p; unsigned uframes; /* don't scan past the live uframe */ @@ -2113,7 +2252,7 @@ restart: q_p = &ehci->pshadow [frame]; hw_p = &ehci->periodic [frame]; q.ptr = q_p->ptr; - type = Q_NEXT_TYPE (*hw_p); + type = Q_NEXT_TYPE(ehci, *hw_p); modified = 0; while (q.ptr != NULL) { @@ -2122,11 +2261,11 @@ restart: int live; live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state); - switch (type) { + switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_QH: /* handle any completions */ temp.qh = qh_get (q.qh); - type = Q_NEXT_TYPE (q.qh->hw_next); + type = Q_NEXT_TYPE(ehci, q.qh->hw_next); q = q.qh->qh_next; modified = qh_completions (ehci, temp.qh); if (unlikely (list_empty (&temp.qh->qtd_list))) @@ -2137,10 +2276,10 @@ restart: /* for "save place" FSTNs, look at QH entries * in the previous frame for completions. */ - if (q.fstn->hw_prev != EHCI_LIST_END) { + if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) { dbg ("ignoring completions from FSTNs"); } - type = Q_NEXT_TYPE (q.fstn->hw_next); + type = Q_NEXT_TYPE(ehci, q.fstn->hw_next); q = q.fstn->fstn_next; break; case Q_TYPE_ITD: @@ -2148,11 +2287,12 @@ restart: rmb (); for (uf = live ? uframes : 8; uf < 8; uf++) { if (0 == (q.itd->hw_transaction [uf] - & ITD_ACTIVE)) + & ITD_ACTIVE(ehci))) continue; q_p = &q.itd->itd_next; hw_p = &q.itd->hw_next; - type = Q_NEXT_TYPE (q.itd->hw_next); + type = Q_NEXT_TYPE(ehci, + q.itd->hw_next); q = *q_p; break; } @@ -2164,23 +2304,24 @@ restart: */ *q_p = q.itd->itd_next; *hw_p = q.itd->hw_next; - type = Q_NEXT_TYPE (q.itd->hw_next); + type = Q_NEXT_TYPE(ehci, q.itd->hw_next); wmb(); modified = itd_complete (ehci, q.itd); q = *q_p; break; case Q_TYPE_SITD: - if ((q.sitd->hw_results & SITD_ACTIVE) + if ((q.sitd->hw_results & SITD_ACTIVE(ehci)) && live) { q_p = &q.sitd->sitd_next; hw_p = &q.sitd->hw_next; - type = Q_NEXT_TYPE (q.sitd->hw_next); + type = Q_NEXT_TYPE(ehci, + q.sitd->hw_next); q = *q_p; break; } *q_p = q.sitd->sitd_next; *hw_p = q.sitd->hw_next; - type = Q_NEXT_TYPE (q.sitd->hw_next); + type = Q_NEXT_TYPE(ehci, q.sitd->hw_next); wmb(); modified = sitd_complete (ehci, q.sitd); q = *q_p; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 46fa57a..2c68a04 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -21,6 +21,22 @@ /* definitions used for the EHCI driver */ +/* + * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to + * __leXX (normally) or __beXX (given EHCI_BIG_ENDIAN_DESC), depending on + * the host controller implementation. + * + * To facilitate the strongest possible byte-order checking from "sparse" + * and so on, we use __leXX unless that's not practical. + */ +#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC +typedef __u32 __bitwise __hc32; +typedef __u16 __bitwise __hc16; +#else +#define __hc32 __le32 +#define __hc16 __le16 +#endif + /* statistics can be kept for for tuning/monitoring */ struct ehci_stats { /* irq usage */ @@ -55,6 +71,12 @@ struct ehci_hcd { /* one per controller */ __u32 hcs_params; /* cached register copy */ spinlock_t lock; +#ifdef CONFIG_CPU_FREQ + struct notifier_block cpufreq_transition; + int cpufreq_changing; + struct list_head split_intr_qhs; +#endif + /* async schedule support */ struct ehci_qh *async; struct ehci_qh *reclaim; @@ -64,7 +86,7 @@ struct ehci_hcd { /* one per controller */ /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */ unsigned periodic_size; - __le32 *periodic; /* hw periodic table */ + __hc32 *periodic; /* hw periodic table */ dma_addr_t periodic_dma; unsigned i_thresh; /* uframes HC might cache */ @@ -74,11 +96,14 @@ struct ehci_hcd { /* one per controller */ /* per root hub port */ unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; + /* 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 */ + unsigned long owned_ports; /* which ports are + owned by the companion during a bus suspend */ /* per-HC memory pools (could be per-bus, but ...) */ struct dma_pool *qh_pool; /* qh per active urb */ @@ -97,6 +122,7 @@ struct ehci_hcd { /* one per controller */ unsigned no_selective_suspend:1; unsigned has_fsl_port_bug:1; /* FreeScale */ unsigned big_endian_mmio:1; + unsigned big_endian_desc:1; u8 sbrn; /* packed release number */ @@ -276,6 +302,12 @@ struct ehci_regs { #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) } __attribute__ ((packed)); +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1<<3) /* Stream disable */ +#define USBMODE_BE (1<<2) /* BE/LE endianness select */ +#define USBMODE_CM_HC (3<<0) /* host controller mode */ +#define USBMODE_CM_IDLE (0<<0) /* idle state */ + /* Appendix C, Debug port ... intended for use with special "debug devices" * that can help if there's no serial console. (nonstandard enumeration.) */ @@ -303,7 +335,7 @@ struct ehci_dbg_port { /*-------------------------------------------------------------------------*/ -#define QTD_NEXT(dma) cpu_to_le32((u32)dma) +#define QTD_NEXT(ehci, dma) cpu_to_hc32(ehci, (u32)dma) /* * EHCI Specification 0.95 Section 3.5 @@ -315,9 +347,9 @@ struct ehci_dbg_port { */ struct ehci_qtd { /* first part defined by EHCI spec */ - __le32 hw_next; /* see EHCI 3.5.1 */ - __le32 hw_alt_next; /* see EHCI 3.5.2 */ - __le32 hw_token; /* see EHCI 3.5.3 */ + __hc32 hw_next; /* see EHCI 3.5.1 */ + __hc32 hw_alt_next; /* see EHCI 3.5.2 */ + __hc32 hw_token; /* see EHCI 3.5.3 */ #define QTD_TOGGLE (1 << 31) /* data toggle */ #define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) #define QTD_IOC (1 << 15) /* interrupt on complete */ @@ -331,8 +363,13 @@ struct ehci_qtd { #define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ #define QTD_STS_STS (1 << 1) /* split transaction state */ #define QTD_STS_PING (1 << 0) /* issue PING? */ - __le32 hw_buf [5]; /* see EHCI 3.5.4 */ - __le32 hw_buf_hi [5]; /* Appendix B */ + +#define ACTIVE_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_ACTIVE) +#define HALT_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_HALT) +#define STATUS_BIT(ehci) cpu_to_hc32(ehci, QTD_STS_STS) + + __hc32 hw_buf [5]; /* see EHCI 3.5.4 */ + __hc32 hw_buf_hi [5]; /* Appendix B */ /* the rest is HCD-private */ dma_addr_t qtd_dma; /* qtd address */ @@ -342,26 +379,33 @@ struct ehci_qtd { } __attribute__ ((aligned (32))); /* mask NakCnt+T in qh->hw_alt_next */ -#define QTD_MASK __constant_cpu_to_le32 (~0x1f) +#define QTD_MASK(ehci) cpu_to_hc32 (ehci, ~0x1f) #define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) /*-------------------------------------------------------------------------*/ /* type tag from {qh,itd,sitd,fstn}->hw_next */ -#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1)) +#define Q_NEXT_TYPE(ehci,dma) ((dma) & cpu_to_hc32(ehci, 3 << 1)) +/* + * Now the following defines are not converted using the + * __constant_cpu_to_le32() macro anymore, since we have to support + * "dynamic" switching between be and le support, so that the driver + * can be used on one system with SoC EHCI controller using big-endian + * descriptors as well as a normal little-endian PCI EHCI controller. + */ /* values for that type tag */ -#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1) -#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1) -#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1) -#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1) +#define Q_TYPE_ITD (0 << 1) +#define Q_TYPE_QH (1 << 1) +#define Q_TYPE_SITD (2 << 1) +#define Q_TYPE_FSTN (3 << 1) /* next async queue entry, or pointer to interrupt/periodic QH */ -#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH) +#define QH_NEXT(ehci,dma) (cpu_to_hc32(ehci, (((u32)dma)&~0x01f)|Q_TYPE_QH)) /* for periodic/async schedules and qtd lists, mark end of list */ -#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */ +#define EHCI_LIST_END(ehci) cpu_to_hc32(ehci, 1) /* "null pointer" to hw */ /* * Entries in periodic shadow table are pointers to one of four kinds @@ -376,7 +420,7 @@ union ehci_shadow { struct ehci_itd *itd; /* Q_TYPE_ITD */ struct ehci_sitd *sitd; /* Q_TYPE_SITD */ struct ehci_fstn *fstn; /* Q_TYPE_FSTN */ - __le32 *hw_next; /* (all types) */ + __hc32 *hw_next; /* (all types) */ void *ptr; }; @@ -392,23 +436,27 @@ union ehci_shadow { struct ehci_qh { /* first part defined by EHCI spec */ - __le32 hw_next; /* see EHCI 3.6.1 */ - __le32 hw_info1; /* see EHCI 3.6.2 */ + __hc32 hw_next; /* see EHCI 3.6.1 */ + __hc32 hw_info1; /* see EHCI 3.6.2 */ #define QH_HEAD 0x00008000 - __le32 hw_info2; /* see EHCI 3.6.2 */ +#define QH_INACTIVATE 0x00000080 + +#define INACTIVATE_BIT(ehci) cpu_to_hc32(ehci, QH_INACTIVATE) + + __hc32 hw_info2; /* see EHCI 3.6.2 */ #define QH_SMASK 0x000000ff #define QH_CMASK 0x0000ff00 #define QH_HUBADDR 0x007f0000 #define QH_HUBPORT 0x3f800000 #define QH_MULT 0xc0000000 - __le32 hw_current; /* qtd list - see EHCI 3.6.4 */ + __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ /* qtd overlay (hardware parts of a struct ehci_qtd) */ - __le32 hw_qtd_next; - __le32 hw_alt_next; - __le32 hw_token; - __le32 hw_buf [5]; - __le32 hw_buf_hi [5]; + __hc32 hw_qtd_next; + __hc32 hw_alt_next; + __hc32 hw_token; + __hc32 hw_buf [5]; + __hc32 hw_buf_hi [5]; /* the rest is HCD-private */ dma_addr_t qh_dma; /* address of qh */ @@ -418,7 +466,14 @@ struct ehci_qh { struct ehci_qh *reclaim; /* next to reclaim */ struct ehci_hcd *ehci; - struct kref kref; + + /* + * Do NOT use atomic operations for QH refcounting. On some CPUs + * (PPC7448 for example), atomic operations cannot be performed on + * memory that is cache-inhibited (i.e. being used for DMA). + * Spinlocks are used to protect all QH fields. + */ + u32 refcount; unsigned stamp; u8 qh_state; @@ -437,6 +492,10 @@ struct ehci_qh { unsigned short start; /* where polling starts */ #define NO_FRAME ((unsigned short)~0) /* pick new start */ struct usb_device *dev; /* access to TT */ +#ifdef CONFIG_CPU_FREQ + struct list_head split_intr_qhs; /* list of split qhs */ + __le32 was_active; /* active bit before "i" set */ +#endif } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ @@ -445,7 +504,7 @@ struct ehci_qh { struct ehci_iso_packet { /* These will be copied to iTD when scheduling */ u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ - __le32 transaction; /* itd->hw_transaction[i] |= */ + __hc32 transaction; /* itd->hw_transaction[i] |= */ u8 cross; /* buf crosses pages */ /* for full speed OUT splits */ u32 buf1; @@ -467,8 +526,8 @@ struct ehci_iso_sched { */ struct ehci_iso_stream { /* first two fields match QH, but info1 == 0 */ - __le32 hw_next; - __le32 hw_info1; + __hc32 hw_next; + __hc32 hw_info1; u32 refcount; u8 bEndpointAddress; @@ -483,7 +542,7 @@ struct ehci_iso_stream { unsigned long start; /* jiffies */ unsigned long rescheduled; int next_uframe; - __le32 splits; + __hc32 splits; /* the rest is derived from the endpoint descriptor, * trusting urb->interval == f(epdesc->bInterval) and @@ -497,12 +556,12 @@ struct ehci_iso_stream { unsigned bandwidth; /* This is used to initialize iTD's hw_bufp fields */ - __le32 buf0; - __le32 buf1; - __le32 buf2; + __hc32 buf0; + __hc32 buf1; + __hc32 buf2; /* this is used to initialize sITD's tt info */ - __le32 address; + __hc32 address; }; /*-------------------------------------------------------------------------*/ @@ -515,8 +574,8 @@ struct ehci_iso_stream { */ struct ehci_itd { /* first part defined by EHCI spec */ - __le32 hw_next; /* see EHCI 3.3.1 */ - __le32 hw_transaction [8]; /* see EHCI 3.3.2 */ + __hc32 hw_next; /* see EHCI 3.3.1 */ + __hc32 hw_transaction [8]; /* see EHCI 3.3.2 */ #define EHCI_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ #define EHCI_ISOC_BUF_ERR (1<<30) /* Data buffer error */ #define EHCI_ISOC_BABBLE (1<<29) /* babble detected */ @@ -524,10 +583,10 @@ struct ehci_itd { #define EHCI_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) #define EHCI_ITD_IOC (1 << 15) /* interrupt on complete */ -#define ITD_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE) +#define ITD_ACTIVE(ehci) cpu_to_hc32(ehci, EHCI_ISOC_ACTIVE) - __le32 hw_bufp [7]; /* see EHCI 3.3.3 */ - __le32 hw_bufp_hi [7]; /* Appendix B */ + __hc32 hw_bufp [7]; /* see EHCI 3.3.3 */ + __hc32 hw_bufp_hi [7]; /* Appendix B */ /* the rest is HCD-private */ dma_addr_t itd_dma; /* for this itd */ @@ -554,11 +613,11 @@ struct ehci_itd { */ struct ehci_sitd { /* first part defined by EHCI spec */ - __le32 hw_next; + __hc32 hw_next; /* uses bit field macros above - see EHCI 0.95 Table 3-8 */ - __le32 hw_fullspeed_ep; /* EHCI table 3-9 */ - __le32 hw_uframe; /* EHCI table 3-10 */ - __le32 hw_results; /* EHCI table 3-11 */ + __hc32 hw_fullspeed_ep; /* EHCI table 3-9 */ + __hc32 hw_uframe; /* EHCI table 3-10 */ + __hc32 hw_results; /* EHCI table 3-11 */ #define SITD_IOC (1 << 31) /* interrupt on completion */ #define SITD_PAGE (1 << 30) /* buffer 0/1 */ #define SITD_LENGTH(x) (0x3ff & ((x)>>16)) @@ -570,11 +629,11 @@ struct ehci_sitd { #define SITD_STS_MMF (1 << 2) /* incomplete split transaction */ #define SITD_STS_STS (1 << 1) /* split transaction state */ -#define SITD_ACTIVE __constant_cpu_to_le32(SITD_STS_ACTIVE) +#define SITD_ACTIVE(ehci) cpu_to_hc32(ehci, SITD_STS_ACTIVE) - __le32 hw_buf [2]; /* EHCI table 3-12 */ - __le32 hw_backpointer; /* EHCI table 3-13 */ - __le32 hw_buf_hi [2]; /* Appendix B */ + __hc32 hw_buf [2]; /* EHCI table 3-12 */ + __hc32 hw_backpointer; /* EHCI table 3-13 */ + __hc32 hw_buf_hi [2]; /* Appendix B */ /* the rest is HCD-private */ dma_addr_t sitd_dma; @@ -599,8 +658,8 @@ struct ehci_sitd { * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. */ struct ehci_fstn { - __le32 hw_next; /* any periodic q entry */ - __le32 hw_prev; /* qh or EHCI_LIST_END */ + __hc32 hw_next; /* any periodic q entry */ + __hc32 hw_prev; /* qh or EHCI_LIST_END */ /* the rest is HCD-private */ dma_addr_t fstn_dma; @@ -672,8 +731,21 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) #define ehci_big_endian_mmio(e) 0 #endif -static inline unsigned int ehci_readl (const struct ehci_hcd *ehci, - __u32 __iomem * regs) +/* + * 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 4xx support is + * finally ported over. + */ +#if defined(CONFIG_PPC) +#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 ehci_readl(const struct ehci_hcd *ehci, + __u32 __iomem * regs) { #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO return ehci_big_endian_mmio(ehci) ? @@ -684,8 +756,8 @@ static inline unsigned int ehci_readl (const struct ehci_hcd *ehci, #endif } -static inline void ehci_writel (const struct ehci_hcd *ehci, - const unsigned int val, __u32 __iomem *regs) +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) ? @@ -698,6 +770,62 @@ static inline void ehci_writel (const struct ehci_hcd *ehci, /*-------------------------------------------------------------------------*/ +/* + * The AMCC 440EPx not only implements its EHCI registers in big-endian + * format, but also its DMA data structures (descriptors). + * + * EHCI controllers accessed through PCI work normally (little-endian + * everywhere), so we won't bother supporting a BE-only mode for now. + */ +#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC +#define ehci_big_endian_desc(e) ((e)->big_endian_desc) + +/* cpu to ehci */ +static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x) +{ + return ehci_big_endian_desc(ehci) + ? (__force __hc32)cpu_to_be32(x) + : (__force __hc32)cpu_to_le32(x); +} + +/* ehci to cpu */ +static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x) +{ + return ehci_big_endian_desc(ehci) + ? be32_to_cpu((__force __be32)x) + : le32_to_cpu((__force __le32)x); +} + +static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) +{ + return ehci_big_endian_desc(ehci) + ? be32_to_cpup((__force __be32 *)x) + : le32_to_cpup((__force __le32 *)x); +} + +#else + +/* cpu to ehci */ +static inline __hc32 cpu_to_hc32 (const struct ehci_hcd *ehci, const u32 x) +{ + return cpu_to_le32(x); +} + +/* ehci to cpu */ +static inline u32 hc32_to_cpu (const struct ehci_hcd *ehci, const __hc32 x) +{ + return le32_to_cpu(x); +} + +static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) +{ + return le32_to_cpup(x); +} + +#endif + +/*-------------------------------------------------------------------------*/ + #ifndef DEBUG #define STUB_DEBUG_FILES #endif /* DEBUG */ diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 273d5dd..6f9e43e 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -23,7 +23,7 @@ /* debug| print the main components of an URB * small: 0) header + data packets 1) just header */ -static void __attribute__((unused)) +static void __maybe_unused urb_print (struct urb * urb, char * str, int small) { unsigned int pipe= urb->pipe; @@ -338,7 +338,7 @@ static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label, } /* caller MUST own hcd spinlock if verbose is set! */ -static void __attribute__((unused)) +static void __maybe_unused ohci_dump_ed (const struct ohci_hcd *ohci, const char *label, const struct ed *ed, int verbose) { diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index a66637e..2038125 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -35,15 +35,13 @@ #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/reboot.h> +#include <linux/workqueue.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/unaligned.h> #include <asm/byteorder.h> -#ifdef CONFIG_PPC_PS3 -#include <asm/firmware.h> -#endif #include "../core/hcd.h" @@ -82,6 +80,8 @@ static const char hcd_name [] = "ohci_hcd"; static void ohci_dump (struct ohci_hcd *ohci, int verbose); static int ohci_init (struct ohci_hcd *ohci); static void ohci_stop (struct usb_hcd *hcd); +static int ohci_restart (struct ohci_hcd *ohci); +static void ohci_quirk_nec_worker (struct work_struct *work); #include "ohci-hub.c" #include "ohci-dbg.c" @@ -510,15 +510,7 @@ static int ohci_run (struct ohci_hcd *ohci) // flush the writes (void) ohci_readl (ohci, &ohci->regs->control); msleep(temp); - temp = roothub_a (ohci); - if (!(temp & RH_A_NPS)) { - /* power down each port */ - for (temp = 0; temp < ohci->num_ports; temp++) - ohci_writel (ohci, RH_PS_LSDA, - &ohci->regs->roothub.portstatus [temp]); - } - // flush those writes - (void) ohci_readl (ohci, &ohci->regs->control); + memset (ohci->hcca, 0, sizeof (struct ohci_hcca)); /* 2msec timelimit here means no irqs/preempt */ @@ -659,9 +651,20 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) } if (ints & OHCI_INTR_UE) { - disable (ohci); - ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); // e.g. due to PCI Master/Target Abort + if (ohci->flags & OHCI_QUIRK_NEC) { + /* Workaround for a silicon bug in some NEC chips used + * in Apple's PowerBooks. Adapted from Darwin code. + */ + ohci_err (ohci, "OHCI Unrecoverable Error, scheduling NEC chip restart\n"); + + ohci_writel (ohci, OHCI_INTR_UE, ®s->intrdisable); + + schedule_work (&ohci->nec_work); + } else { + disable (ohci); + ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); + } ohci_dump (ohci, 1); ohci_usb_reset (ohci); @@ -763,23 +766,16 @@ static void ohci_stop (struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ /* must not be called from interrupt context */ - -#ifdef CONFIG_PM - static int ohci_restart (struct ohci_hcd *ohci) { int temp; int i; struct urb_priv *priv; - /* mark any devices gone, so they do nothing till khubd disconnects. - * recycle any "live" eds/tds (and urbs) right away. - * later, khubd disconnect processing will recycle the other state, - * (either as disconnect/reconnect, or maybe someday as a reset). - */ spin_lock_irq(&ohci->lock); disable (ohci); - usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub); + + /* Recycle any "live" eds/tds (and urbs). */ if (!list_empty (&ohci->pending)) ohci_dbg(ohci, "abort schedule...\n"); list_for_each_entry (priv, &ohci->pending, pending) { @@ -826,20 +822,31 @@ static int ohci_restart (struct ohci_hcd *ohci) if ((temp = ohci_run (ohci)) < 0) { ohci_err (ohci, "can't restart, %d\n", temp); return temp; - } else { - /* here we "know" root ports should always stay powered, - * and that if we try to turn them back on the root hub - * will respond to CSC processing. - */ - i = ohci->num_ports; - while (i--) - ohci_writel (ohci, RH_PS_PSS, - &ohci->regs->roothub.portstatus [i]); - ohci_dbg (ohci, "restart complete\n"); } + ohci_dbg(ohci, "restart complete\n"); return 0; } -#endif + +/*-------------------------------------------------------------------------*/ + +/* NEC workaround */ +static void ohci_quirk_nec_worker(struct work_struct *work) +{ + struct ohci_hcd *ohci = container_of(work, struct ohci_hcd, nec_work); + int status; + + status = ohci_init(ohci); + if (status != 0) { + ohci_err(ohci, "Restarting NEC controller failed " + "in ohci_init, %d\n", status); + return; + } + + status = ohci_restart(ohci); + if (status != 0) + ohci_err(ohci, "Restarting NEC controller failed " + "in ohci_restart, %d\n", status); +} /*-------------------------------------------------------------------------*/ @@ -917,7 +924,7 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_PPC_PS3 #include "ohci-ps3.c" -#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_sb_driver +#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver #endif #if !defined(PCI_DRIVER) && \ @@ -940,12 +947,9 @@ static int __init ohci_hcd_mod_init(void) sizeof (struct ed), sizeof (struct td)); #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { - retval = ps3_system_bus_driver_register( - &PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) - goto error_ps3; - } + retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) + goto error_ps3; #endif #ifdef PLATFORM_DRIVER @@ -991,8 +995,7 @@ static int __init ohci_hcd_mod_init(void) error_platform: #endif #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); error_ps3: #endif return retval; @@ -1014,8 +1017,7 @@ static void __exit ohci_hcd_mod_exit(void) platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif } module_exit(ohci_hcd_mod_exit); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index bb9cc59..48e4b11 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -55,8 +55,6 @@ static void dl_done_list (struct ohci_hcd *); static void finish_unlinks (struct ohci_hcd *, u16); #ifdef CONFIG_PM -static int ohci_restart(struct ohci_hcd *ohci); - static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop) __releases(ohci->lock) __acquires(ohci->lock) @@ -191,6 +189,9 @@ __acquires(ohci->lock) spin_unlock_irq (&ohci->lock); (void) ohci_init (ohci); status = ohci_restart (ohci); + + usb_root_hub_lost_power(hcd->self.root_hub); + spin_lock_irq (&ohci->lock); } return status; diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 2f20d3d..450c7b4 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -28,6 +28,7 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) ohci->next_statechange = jiffies; spin_lock_init (&ohci->lock); INIT_LIST_HEAD (&ohci->pending); + INIT_WORK (&ohci->nec_work, ohci_quirk_nec_worker); } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index ca62cb5..a5e2eb8 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -111,6 +111,18 @@ static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd) #endif } +/* Check for NEC chip and apply quirk for allegedly lost interrupts. + */ +static int ohci_quirk_nec(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + ohci->flags |= OHCI_QUIRK_NEC; + ohci_dbg (ohci, "enabled NEC chipset lost interrupt quirk\n"); + + return 0; +} + /* List of quirks for OHCI */ static const struct pci_device_id ohci_pci_quirks[] = { { @@ -134,6 +146,10 @@ static const struct pci_device_id ohci_pci_quirks[] = { .driver_data = (unsigned long)ohci_quirk_toshiba_scc, }, { + PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_USB), + .driver_data = (unsigned long)ohci_quirk_nec, + }, + { /* Toshiba portege 4000 */ .vendor = PCI_VENDOR_ID_AL, .device = 0x5237, @@ -202,6 +218,42 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd) return ret; } +#if defined(CONFIG_USB_PERSIST) && (defined(CONFIG_USB_EHCI_HCD) || \ + defined(CONFIG_USB_EHCI_HCD_MODULE)) + +/* Following a power loss, we must prepare to regain control of the ports + * we used to own. This means turning on the port power before ehci-hcd + * tries to switch ownership. + * + * This isn't a 100% perfect solution. On most systems the OHCI controllers + * lie at lower PCI addresses than the EHCI controller, so they will be + * discovered (and hence resumed) first. But there is no guarantee things + * will always work this way. If the EHCI controller is resumed first and + * the OHCI ports are unpowered, then the handover will fail. + */ +static void prepare_for_handover(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int port; + + /* Here we "know" root ports should always stay powered */ + ohci_dbg(ohci, "powerup ports\n"); + for (port = 0; port < ohci->num_ports; port++) + ohci_writel(ohci, RH_PS_PPS, + &ohci->regs->roothub.portstatus[port]); + + /* Flush those writes */ + ohci_readl(ohci, &ohci->regs->control); + msleep(20); +} + +#else + +static inline void prepare_for_handover(struct usb_hcd *hcd) +{ } + +#endif /* CONFIG_USB_PERSIST etc. */ + #ifdef CONFIG_PM static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) @@ -241,7 +293,10 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) static int ohci_pci_resume (struct usb_hcd *hcd) { set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - usb_hcd_resume_root_hub(hcd); + + /* FIXME: we should try to detect loss of VBUS power here */ + prepare_for_handover(hcd); + return 0; } diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index d601bbb..ca2a6ab 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -134,7 +134,7 @@ static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind) { struct i2c_client *c; - c = (struct i2c_client *)kzalloc(sizeof(*c), GFP_KERNEL); + c = kzalloc(sizeof(*c), GFP_KERNEL); if (!c) return -ENOMEM; diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index d7cf072..01a0cae 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <asm/firmware.h> #include <asm/ps3.h> static int ps3_ohci_hc_reset(struct usb_hcd *hcd) @@ -75,7 +76,7 @@ static const struct hc_driver ps3_ohci_hc_driver = { #endif }; -static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev) +static int ps3_ohci_probe(struct ps3_system_bus_device *dev) { int result; struct usb_hcd *hcd; @@ -87,13 +88,31 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev) goto fail_start; } + result = ps3_open_hv_device(dev); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed: %s\n", + __func__, __LINE__, ps3_result(result)); + result = -EPERM; + goto fail_open; + } + + result = ps3_dma_region_create(dev->d_region); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: " + "(%d)\n", __func__, __LINE__, result); + BUG_ON("check region type"); + goto fail_dma_region; + } + 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; + goto fail_mmio_region; } dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__, @@ -122,6 +141,11 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev) hcd->rsrc_start = dev->m_region->lpar_addr; hcd->rsrc_len = dev->m_region->len; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) + dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n", + __func__, __LINE__); + hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len); if (!hcd->regs) { @@ -155,34 +179,73 @@ static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev) fail_add_hcd: iounmap(hcd->regs); fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); fail_create_hcd: ps3_io_irq_destroy(virq); fail_irq: ps3_free_mmio_region(dev->m_region); -fail_mmio: +fail_mmio_region: + ps3_dma_region_free(dev->d_region); +fail_dma_region: + ps3_close_hv_device(dev); +fail_open: fail_start: return result; } -static int ps3_ohci_sb_remove (struct ps3_system_bus_device *dev) +static int ps3_ohci_remove (struct ps3_system_bus_device *dev) { + unsigned int tmp; struct usb_hcd *hcd = (struct usb_hcd *)ps3_system_bus_get_driver_data(dev); - usb_put_hcd(hcd); + BUG_ON(!hcd); + + dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs); + dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq); + + tmp = hcd->irq; + + usb_remove_hcd(hcd); + ps3_system_bus_set_driver_data(dev, NULL); + BUG_ON(!hcd->regs); + iounmap(hcd->regs); + + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + ps3_io_irq_destroy(tmp); + ps3_free_mmio_region(dev->m_region); + + ps3_dma_region_free(dev->d_region); + ps3_close_hv_device(dev); + return 0; } -MODULE_ALIAS("ps3-ohci"); +static int ps3_ohci_driver_register(struct ps3_system_bus_driver *drv) +{ + return firmware_has_feature(FW_FEATURE_PS3_LV1) + ? ps3_system_bus_driver_register(drv) + : 0; +} + +static void ps3_ohci_driver_unregister(struct ps3_system_bus_driver *drv) +{ + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(drv); +} + +MODULE_ALIAS(PS3_MODULE_ALIAS_OHCI); -static struct ps3_system_bus_driver ps3_ohci_sb_driver = { +static struct ps3_system_bus_driver ps3_ohci_driver = { + .core.name = "ps3-ohci-driver", + .core.owner = THIS_MODULE, .match_id = PS3_MATCH_ID_OHCI, - .core = { - .name = "ps3-ohci-driver", - }, - .probe = ps3_ohci_sb_probe, - .remove = ps3_ohci_sb_remove, + .probe = ps3_ohci_probe, + .remove = ps3_ohci_remove, + .shutdown = ps3_ohci_remove, }; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index c2b5ecf..4ada43c 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -397,8 +397,10 @@ struct ohci_hcd { #define OHCI_QUIRK_BE_DESC 0x08 /* BE descriptors */ #define OHCI_QUIRK_BE_MMIO 0x10 /* BE registers */ #define OHCI_QUIRK_ZFMICRO 0x20 /* Compaq ZFMicro chipset*/ +#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */ // there are also chip quirks/bugs in init logic + struct work_struct nec_work; /* Worker for NEC quirk */ }; /* convert between an hcd pointer and the corresponding ohci_hcd */ diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c new file mode 100644 index 0000000..a7a7070 --- /dev/null +++ b/drivers/usb/host/r8a66597-hcd.c @@ -0,0 +1,2244 @@ +/* + * R8A66597 HCD (Host Controller Driver) + * + * Copyright (C) 2006-2007 Renesas Solutions Corp. + * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO) + * Portions Copyright (C) 2004-2005 David Brownell + * Portions Copyright (C) 1999 Roman Weissgaerber + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/usb.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> + +#include "../core/hcd.h" +#include "r8a66597.h" + +MODULE_DESCRIPTION("R8A66597 USB Host Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yoshihiro Shimoda"); + +#define DRIVER_VERSION "29 May 2007" + +static const char hcd_name[] = "r8a66597_hcd"; + +/* module parameters */ +static unsigned short clock = XTAL12; +module_param(clock, ushort, 0644); +MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0(default=0)"); +static unsigned short vif = LDRV; +module_param(vif, ushort, 0644); +MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0(default=32768)"); +static unsigned short endian = 0; +module_param(endian, ushort, 0644); +MODULE_PARM_DESC(endian, "data endian: big=256, little=0(default=0)"); +static unsigned short irq_sense = INTL; +module_param(irq_sense, ushort, 0644); +MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=32, falling edge=0(default=32)"); + +static void packet_write(struct r8a66597 *r8a66597, u16 pipenum); +static int r8a66597_get_frame(struct usb_hcd *hcd); + +/* this function must be called with interrupt disabled */ +static void enable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, INTENB0); + r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0); + r8a66597_bset(r8a66597, 1 << pipenum, reg); + r8a66597_write(r8a66597, tmp, INTENB0); +} + +/* this function must be called with interrupt disabled */ +static void disable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, INTENB0); + r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0); + r8a66597_bclr(r8a66597, 1 << pipenum, reg); + r8a66597_write(r8a66597, tmp, INTENB0); +} + +static void set_devadd_reg(struct r8a66597 *r8a66597, u8 r8a66597_address, + u16 usbspd, u8 upphub, u8 hubport, int port) +{ + u16 val; + unsigned long devadd_reg = get_devadd_addr(r8a66597_address); + + val = (upphub << 11) | (hubport << 8) | (usbspd << 6) | (port & 0x0001); + r8a66597_write(r8a66597, val, devadd_reg); +} + +static int enable_controller(struct r8a66597 *r8a66597) +{ + u16 tmp; + int i = 0; + + do { + r8a66597_write(r8a66597, USBE, SYSCFG0); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 1000) { + err("register access fail."); + return -ENXIO; + } + } while ((tmp & USBE) != USBE); + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_mdfy(r8a66597, clock, XTAL, SYSCFG0); + + i = 0; + r8a66597_bset(r8a66597, XCKE, SYSCFG0); + do { + msleep(1); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (i++ > 500) { + err("register access fail."); + return -ENXIO; + } + } while ((tmp & SCKE) != SCKE); + + r8a66597_bset(r8a66597, DCFM | DRPD, SYSCFG0); + r8a66597_bset(r8a66597, DRPD, SYSCFG1); + + r8a66597_bset(r8a66597, vif & LDRV, PINCFG); + r8a66597_bset(r8a66597, HSE, SYSCFG0); + r8a66597_bset(r8a66597, HSE, SYSCFG1); + r8a66597_bset(r8a66597, USBE, SYSCFG0); + + r8a66597_bset(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0); + r8a66597_bset(r8a66597, irq_sense & INTL, SOFCFG); + r8a66597_bset(r8a66597, BRDY0, BRDYENB); + r8a66597_bset(r8a66597, BEMP0, BEMPENB); + + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, DMA0CFG); + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, DMA1CFG); + + r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL); + r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL); + r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL); + + r8a66597_bset(r8a66597, TRNENSEL, SOFCFG); + + r8a66597_bset(r8a66597, SIGNE | SACKE, INTENB1); + r8a66597_bclr(r8a66597, DTCHE, INTENB1); + r8a66597_bset(r8a66597, ATTCHE, INTENB1); + r8a66597_bclr(r8a66597, DTCHE, INTENB2); + r8a66597_bset(r8a66597, ATTCHE, INTENB2); + + return 0; +} + +static void disable_controller(struct r8a66597 *r8a66597) +{ + u16 tmp; + + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTENB1); + r8a66597_write(r8a66597, 0, INTENB2); + r8a66597_write(r8a66597, 0, INTSTS0); + r8a66597_write(r8a66597, 0, INTSTS1); + r8a66597_write(r8a66597, 0, INTSTS2); + + r8a66597_port_power(r8a66597, 0, 0); + r8a66597_port_power(r8a66597, 1, 0); + + do { + tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS; + udelay(640); + } while (tmp == EDGESTS); + + r8a66597_bclr(r8a66597, DCFM | DRPD, SYSCFG0); + r8a66597_bclr(r8a66597, DRPD, SYSCFG1); + r8a66597_bclr(r8a66597, HSE, SYSCFG0); + r8a66597_bclr(r8a66597, HSE, SYSCFG1); + + r8a66597_bclr(r8a66597, SCKE, SYSCFG0); + udelay(1); + r8a66597_bclr(r8a66597, PLLC, SYSCFG0); + r8a66597_bclr(r8a66597, XCKE, SYSCFG0); + r8a66597_bclr(r8a66597, USBE, SYSCFG0); +} + +static int get_parent_r8a66597_address(struct r8a66597 *r8a66597, + struct usb_device *udev) +{ + struct r8a66597_device *dev; + + if (udev->parent && udev->parent->devnum != 1) + udev = udev->parent; + + dev = dev_get_drvdata(&udev->dev); + if (dev) + return dev->address; + else + return 0; +} + +static int is_child_device(char *devpath) +{ + return (devpath[2] ? 1 : 0); +} + +static int is_hub_limit(char *devpath) +{ + return ((strlen(devpath) >= 4) ? 1 : 0); +} + +static void get_port_number(char *devpath, u16 *root_port, u16 *hub_port) +{ + if (root_port) { + *root_port = (devpath[0] & 0x0F) - 1; + if (*root_port >= R8A66597_MAX_ROOT_HUB) + err("illegal root port number"); + } + if (hub_port) + *hub_port = devpath[2] & 0x0F; +} + +static u16 get_r8a66597_usb_speed(enum usb_device_speed speed) +{ + u16 usbspd = 0; + + switch (speed) { + case USB_SPEED_LOW: + usbspd = LSMODE; + break; + case USB_SPEED_FULL: + usbspd = FSMODE; + break; + case USB_SPEED_HIGH: + usbspd = HSMODE; + break; + default: + err("unknown speed"); + break; + } + + return usbspd; +} + +static void set_child_connect_map(struct r8a66597 *r8a66597, int address) +{ + int idx; + + idx = address / 32; + r8a66597->child_connect_map[idx] |= 1 << (address % 32); +} + +static void put_child_connect_map(struct r8a66597 *r8a66597, int address) +{ + int idx; + + idx = address / 32; + r8a66597->child_connect_map[idx] &= ~(1 << (address % 32)); +} + +static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch) +{ + u16 pipenum = pipe->info.pipenum; + unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO}; + unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL}; + unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR}; + + if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */ + dma_ch = R8A66597_PIPE_NO_DMA; + + pipe->fifoaddr = fifoaddr[dma_ch]; + pipe->fifosel = fifosel[dma_ch]; + pipe->fifoctr = fifoctr[dma_ch]; + + if (pipenum == 0) + pipe->pipectr = DCPCTR; + else + pipe->pipectr = get_pipectr_addr(pipenum); + + if (check_bulk_or_isoc(pipenum)) { + pipe->pipetre = get_pipetre_addr(pipenum); + pipe->pipetrn = get_pipetrn_addr(pipenum); + } else { + pipe->pipetre = 0; + pipe->pipetrn = 0; + } +} + +static struct r8a66597_device * +get_urb_to_r8a66597_dev(struct r8a66597 *r8a66597, struct urb *urb) +{ + if (usb_pipedevice(urb->pipe) == 0) + return &r8a66597->device0; + + return dev_get_drvdata(&urb->dev->dev); +} + +static int make_r8a66597_device(struct r8a66597 *r8a66597, + struct urb *urb, u8 addr) +{ + struct r8a66597_device *dev; + int usb_address = urb->setup_packet[2]; /* urb->pipe is address 0 */ + + dev = kzalloc(sizeof(struct r8a66597_device), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + + dev_set_drvdata(&urb->dev->dev, dev); + dev->udev = urb->dev; + dev->address = addr; + dev->usb_address = usb_address; + dev->state = USB_STATE_ADDRESS; + dev->ep_in_toggle = 0; + dev->ep_out_toggle = 0; + INIT_LIST_HEAD(&dev->device_list); + list_add_tail(&dev->device_list, &r8a66597->child_device); + + get_port_number(urb->dev->devpath, &dev->root_port, &dev->hub_port); + if (!is_child_device(urb->dev->devpath)) + r8a66597->root_hub[dev->root_port].dev = dev; + + set_devadd_reg(r8a66597, dev->address, + get_r8a66597_usb_speed(urb->dev->speed), + get_parent_r8a66597_address(r8a66597, urb->dev), + dev->hub_port, dev->root_port); + + return 0; +} + +/* this function must be called with interrupt disabled */ +static u8 alloc_usb_address(struct r8a66597 *r8a66597, struct urb *urb) +{ + u8 addr; /* R8A66597's address */ + struct r8a66597_device *dev; + + if (is_hub_limit(urb->dev->devpath)) { + err("Externel hub limit reached."); + return 0; + } + + dev = get_urb_to_r8a66597_dev(r8a66597, urb); + if (dev && dev->state >= USB_STATE_ADDRESS) + return dev->address; + + for (addr = 1; addr <= R8A66597_MAX_DEVICE; addr++) { + if (r8a66597->address_map & (1 << addr)) + continue; + + dbg("alloc_address: r8a66597_addr=%d", addr); + r8a66597->address_map |= 1 << addr; + + if (make_r8a66597_device(r8a66597, urb, addr) < 0) + return 0; + + return addr; + } + + err("cannot communicate with a USB device more than 10.(%x)", + r8a66597->address_map); + + return 0; +} + +/* this function must be called with interrupt disabled */ +static void free_usb_address(struct r8a66597 *r8a66597, + struct r8a66597_device *dev) +{ + int port; + + if (!dev) + return; + + dbg("free_addr: addr=%d", dev->address); + + dev->state = USB_STATE_DEFAULT; + r8a66597->address_map &= ~(1 << dev->address); + dev->address = 0; + dev_set_drvdata(&dev->udev->dev, NULL); + list_del(&dev->device_list); + kfree(dev); + + for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) { + if (r8a66597->root_hub[port].dev == dev) { + r8a66597->root_hub[port].dev = NULL; + break; + } + } +} + +static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg, + u16 mask, u16 loop) +{ + u16 tmp; + int i = 0; + + do { + tmp = r8a66597_read(r8a66597, reg); + if (i++ > 1000000) { + err("register%lx, loop %x is timeout", reg, loop); + break; + } + ndelay(1); + } while ((tmp & mask) != loop); +} + +/* this function must be called with interrupt disabled */ +static void pipe_start(struct r8a66597 *r8a66597, struct r8a66597_pipe *pipe) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, pipe->pipectr) & PID; + if ((pipe->info.pipenum != 0) & ((tmp & PID_STALL) != 0)) /* stall? */ + r8a66597_mdfy(r8a66597, PID_NAK, PID, pipe->pipectr); + r8a66597_mdfy(r8a66597, PID_BUF, PID, pipe->pipectr); +} + +/* this function must be called with interrupt disabled */ +static void pipe_stop(struct r8a66597 *r8a66597, struct r8a66597_pipe *pipe) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, pipe->pipectr) & PID; + if ((tmp & PID_STALL11) != PID_STALL11) /* force stall? */ + r8a66597_mdfy(r8a66597, PID_STALL, PID, pipe->pipectr); + r8a66597_mdfy(r8a66597, PID_NAK, PID, pipe->pipectr); + r8a66597_reg_wait(r8a66597, pipe->pipectr, PBUSY, 0); +} + +/* this function must be called with interrupt disabled */ +static void clear_all_buffer(struct r8a66597 *r8a66597, + struct r8a66597_pipe *pipe) +{ + u16 tmp; + + if (!pipe || pipe->info.pipenum == 0) + return; + + pipe_stop(r8a66597, pipe); + r8a66597_bset(r8a66597, ACLRM, pipe->pipectr); + tmp = r8a66597_read(r8a66597, pipe->pipectr); + tmp = r8a66597_read(r8a66597, pipe->pipectr); + tmp = r8a66597_read(r8a66597, pipe->pipectr); + r8a66597_bclr(r8a66597, ACLRM, pipe->pipectr); +} + +/* this function must be called with interrupt disabled */ +static void r8a66597_pipe_toggle(struct r8a66597 *r8a66597, + struct r8a66597_pipe *pipe, int toggle) +{ + if (toggle) + r8a66597_bset(r8a66597, SQSET, pipe->pipectr); + else + r8a66597_bset(r8a66597, SQCLR, pipe->pipectr); +} + +/* this function must be called with interrupt disabled */ +static inline void cfifo_change(struct r8a66597 *r8a66597, u16 pipenum) +{ + r8a66597_mdfy(r8a66597, MBW | pipenum, MBW | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, pipenum); +} + +/* this function must be called with interrupt disabled */ +static inline void fifo_change_from_pipe(struct r8a66597 *r8a66597, + struct r8a66597_pipe *pipe) +{ + cfifo_change(r8a66597, 0); + r8a66597_mdfy(r8a66597, MBW | 0, MBW | CURPIPE, D0FIFOSEL); + r8a66597_mdfy(r8a66597, MBW | 0, MBW | CURPIPE, D1FIFOSEL); + + r8a66597_mdfy(r8a66597, MBW | pipe->info.pipenum, MBW | CURPIPE, + pipe->fifosel); + r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE, pipe->info.pipenum); +} + +static u16 r8a66597_get_pipenum(struct urb *urb, struct usb_host_endpoint *hep) +{ + struct r8a66597_pipe *pipe = hep->hcpriv; + + if (usb_pipeendpoint(urb->pipe) == 0) + return 0; + else + return pipe->info.pipenum; +} + +static u16 get_urb_to_r8a66597_addr(struct r8a66597 *r8a66597, struct urb *urb) +{ + struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb); + + return (usb_pipedevice(urb->pipe) == 0) ? 0 : dev->address; +} + +static unsigned short *get_toggle_pointer(struct r8a66597_device *dev, + int urb_pipe) +{ + if (!dev) + return NULL; + + return usb_pipein(urb_pipe) ? &dev->ep_in_toggle : &dev->ep_out_toggle; +} + +/* this function must be called with interrupt disabled */ +static void pipe_toggle_set(struct r8a66597 *r8a66597, + struct r8a66597_pipe *pipe, + struct urb *urb, int set) +{ + struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb); + unsigned char endpoint = usb_pipeendpoint(urb->pipe); + unsigned short *toggle = get_toggle_pointer(dev, urb->pipe); + + if (!toggle) + return; + + if (set) + *toggle |= 1 << endpoint; + else + *toggle &= ~(1 << endpoint); +} + +/* this function must be called with interrupt disabled */ +static void pipe_toggle_save(struct r8a66597 *r8a66597, + struct r8a66597_pipe *pipe, + struct urb *urb) +{ + if (r8a66597_read(r8a66597, pipe->pipectr) & SQMON) + pipe_toggle_set(r8a66597, pipe, urb, 1); + else + pipe_toggle_set(r8a66597, pipe, urb, 0); +} + +/* this function must be called with interrupt disabled */ +static void pipe_toggle_restore(struct r8a66597 *r8a66597, + struct r8a66597_pipe *pipe, + struct urb *urb) +{ + struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb); + unsigned char endpoint = usb_pipeendpoint(urb->pipe); + unsigned short *toggle = get_toggle_pointer(dev, urb->pipe); + + if (!toggle) + return; + + r8a66597_pipe_toggle(r8a66597, pipe, *toggle & (1 << endpoint)); +} + +/* this function must be called with interrupt disabled */ +static void pipe_buffer_setting(struct r8a66597 *r8a66597, + struct r8a66597_pipe_info *info) +{ + u16 val = 0; + + if (info->pipenum == 0) + return; + + r8a66597_bset(r8a66597, ACLRM, get_pipectr_addr(info->pipenum)); + r8a66597_bclr(r8a66597, ACLRM, get_pipectr_addr(info->pipenum)); + r8a66597_write(r8a66597, info->pipenum, PIPESEL); + if (!info->dir_in) + val |= R8A66597_DIR; + if (info->type == R8A66597_BULK && info->dir_in) + val |= R8A66597_DBLB | R8A66597_SHTNAK; + val |= info->type | info->epnum; + r8a66597_write(r8a66597, val, PIPECFG); + + r8a66597_write(r8a66597, (info->buf_bsize << 10) | (info->bufnum), + PIPEBUF); + r8a66597_write(r8a66597, make_devsel(info->address) | info->maxpacket, + PIPEMAXP); + if (info->interval) + info->interval--; + r8a66597_write(r8a66597, info->interval, PIPEPERI); +} + + + +/* this function must be called with interrupt disabled */ +static void pipe_setting(struct r8a66597 *r8a66597, struct r8a66597_td *td) +{ + struct r8a66597_pipe_info *info; + struct urb *urb = td->urb; + + if (td->pipenum > 0) { + info = &td->pipe->info; + cfifo_change(r8a66597, 0); + pipe_buffer_setting(r8a66597, info); + + if (!usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)) && + !usb_pipecontrol(urb->pipe)) { + r8a66597_pipe_toggle(r8a66597, td->pipe, 0); + pipe_toggle_set(r8a66597, td->pipe, urb, 0); + clear_all_buffer(r8a66597, td->pipe); + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), 1); + } + pipe_toggle_restore(r8a66597, td->pipe, urb); + } +} + +/* this function must be called with interrupt disabled */ +static u16 get_empty_pipenum(struct r8a66597 *r8a66597, + struct usb_endpoint_descriptor *ep) +{ + u16 array[R8A66597_MAX_NUM_PIPE], i = 0, min; + + memset(array, 0, sizeof(array)); + switch(ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + array[i++] = 4; + else { + array[i++] = 3; + array[i++] = 5; + } + break; + case USB_ENDPOINT_XFER_INT: + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + array[i++] = 6; + array[i++] = 7; + array[i++] = 8; + } else + array[i++] = 9; + break; + case USB_ENDPOINT_XFER_ISOC: + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + array[i++] = 2; + else + array[i++] = 1; + break; + default: + err("Illegal type"); + return 0; + } + + i = 1; + min = array[0]; + while (array[i] != 0) { + if (r8a66597->pipe_cnt[min] > r8a66597->pipe_cnt[array[i]]) + min = array[i]; + i++; + } + + return min; +} + +static u16 get_r8a66597_type(__u8 type) +{ + u16 r8a66597_type; + + switch(type) { + case USB_ENDPOINT_XFER_BULK: + r8a66597_type = R8A66597_BULK; + break; + case USB_ENDPOINT_XFER_INT: + r8a66597_type = R8A66597_INT; + break; + case USB_ENDPOINT_XFER_ISOC: + r8a66597_type = R8A66597_ISO; + break; + default: + err("Illegal type"); + r8a66597_type = 0x0000; + break; + } + + return r8a66597_type; +} + +static u16 get_bufnum(u16 pipenum) +{ + u16 bufnum = 0; + + if (pipenum == 0) + bufnum = 0; + else if (check_bulk_or_isoc(pipenum)) + bufnum = 8 + (pipenum - 1) * R8A66597_BUF_BSIZE*2; + else if (check_interrupt(pipenum)) + bufnum = 4 + (pipenum - 6); + else + err("Illegal pipenum (%d)", pipenum); + + return bufnum; +} + +static u16 get_buf_bsize(u16 pipenum) +{ + u16 buf_bsize = 0; + + if (pipenum == 0) + buf_bsize = 3; + else if (check_bulk_or_isoc(pipenum)) + buf_bsize = R8A66597_BUF_BSIZE - 1; + else if (check_interrupt(pipenum)) + buf_bsize = 0; + else + err("Illegal pipenum (%d)", pipenum); + + return buf_bsize; +} + +/* this function must be called with interrupt disabled */ +static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597, + struct r8a66597_device *dev, + struct r8a66597_pipe *pipe, + struct urb *urb) +{ + int i; + struct r8a66597_pipe_info *info = &pipe->info; + + if ((pipe->info.pipenum != 0) && (info->type != R8A66597_INT)) { + for (i = 0; i < R8A66597_MAX_DMA_CHANNEL; i++) { + if ((r8a66597->dma_map & (1 << i)) != 0) + continue; + + info("address %d, EndpointAddress 0x%02x use DMA FIFO", + usb_pipedevice(urb->pipe), + info->dir_in ? USB_ENDPOINT_DIR_MASK + info->epnum + : info->epnum); + + r8a66597->dma_map |= 1 << i; + dev->dma_map |= 1 << i; + set_pipe_reg_addr(pipe, i); + + cfifo_change(r8a66597, 0); + r8a66597_mdfy(r8a66597, MBW | pipe->info.pipenum, + MBW | CURPIPE, pipe->fifosel); + + r8a66597_reg_wait(r8a66597, pipe->fifosel, CURPIPE, + pipe->info.pipenum); + r8a66597_bset(r8a66597, BCLR, pipe->fifoctr); + break; + } + } +} + +/* this function must be called with interrupt disabled */ +static void enable_r8a66597_pipe(struct r8a66597 *r8a66597, struct urb *urb, + struct usb_host_endpoint *hep, + struct r8a66597_pipe_info *info) +{ + struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb); + struct r8a66597_pipe *pipe = hep->hcpriv; + + dbg("enable_pipe:"); + + pipe->info = *info; + set_pipe_reg_addr(pipe, R8A66597_PIPE_NO_DMA); + r8a66597->pipe_cnt[pipe->info.pipenum]++; + dev->pipe_cnt[pipe->info.pipenum]++; + + enable_r8a66597_pipe_dma(r8a66597, dev, pipe, urb); +} + +/* this function must be called with interrupt disabled */ +static void force_dequeue(struct r8a66597 *r8a66597, u16 pipenum, u16 address) +{ + struct r8a66597_td *td, *next; + struct urb *urb; + struct list_head *list = &r8a66597->pipe_queue[pipenum]; + + if (list_empty(list)) + return; + + list_for_each_entry_safe(td, next, list, queue) { + if (!td) + continue; + if (td->address != address) + continue; + + urb = td->urb; + list_del(&td->queue); + kfree(td); + + if (urb) { + urb->status = -ENODEV; + urb->hcpriv = NULL; + spin_unlock(&r8a66597->lock); + usb_hcd_giveback_urb(r8a66597_to_hcd(r8a66597), urb); + spin_lock(&r8a66597->lock); + } + break; + } +} + +/* this function must be called with interrupt disabled */ +static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597, + struct r8a66597_device *dev) +{ + int check_ep0 = 0; + u16 pipenum; + + if (!dev) + return; + + for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + if (!dev->pipe_cnt[pipenum]) + continue; + + if (!check_ep0) { + check_ep0 = 1; + force_dequeue(r8a66597, 0, dev->address); + } + + r8a66597->pipe_cnt[pipenum] -= dev->pipe_cnt[pipenum]; + dev->pipe_cnt[pipenum] = 0; + force_dequeue(r8a66597, pipenum, dev->address); + } + + dbg("disable_pipe"); + + r8a66597->dma_map &= ~(dev->dma_map); + dev->dma_map = 0; +} + +/* this function must be called with interrupt disabled */ +static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb, + struct usb_host_endpoint *hep, + struct usb_endpoint_descriptor *ep) +{ + struct r8a66597_pipe_info info; + + info.pipenum = get_empty_pipenum(r8a66597, ep); + info.address = get_urb_to_r8a66597_addr(r8a66597, urb); + info.epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + info.maxpacket = ep->wMaxPacketSize; + info.type = get_r8a66597_type(ep->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK); + info.bufnum = get_bufnum(info.pipenum); + info.buf_bsize = get_buf_bsize(info.pipenum); + info.interval = ep->bInterval; + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + info.dir_in = 1; + else + info.dir_in = 0; + + enable_r8a66597_pipe(r8a66597, urb, hep, &info); +} + +static void init_pipe_config(struct r8a66597 *r8a66597, struct urb *urb) +{ + struct r8a66597_device *dev; + + dev = get_urb_to_r8a66597_dev(r8a66597, urb); + dev->state = USB_STATE_CONFIGURED; +} + +static void pipe_irq_enable(struct r8a66597 *r8a66597, struct urb *urb, + u16 pipenum) +{ + if (pipenum == 0 && usb_pipeout(urb->pipe)) + enable_irq_empty(r8a66597, pipenum); + else + enable_irq_ready(r8a66597, pipenum); + + if (!usb_pipeisoc(urb->pipe)) + enable_irq_nrdy(r8a66597, pipenum); +} + +static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum) +{ + disable_irq_ready(r8a66597, pipenum); + disable_irq_nrdy(r8a66597, pipenum); +} + +/* this function must be called with interrupt disabled */ +static void r8a66597_usb_preconnect(struct r8a66597 *r8a66597, int port) +{ + r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_CONNECTION) + | (1 << USB_PORT_FEAT_C_CONNECTION); + r8a66597_write(r8a66597, (u16)~DTCH, get_intsts_reg(port)); + r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port)); +} + +/* this function must be called with interrupt disabled */ +static void r8a66597_usb_connect(struct r8a66597 *r8a66597, int port) +{ + u16 speed = get_rh_usb_speed(r8a66597, port); + struct r8a66597_root_hub *rh = &r8a66597->root_hub[port]; + + if (speed == HSMODE) + rh->port |= (1 << USB_PORT_FEAT_HIGHSPEED); + else if (speed == LSMODE) + rh->port |= (1 << USB_PORT_FEAT_LOWSPEED); + + rh->port &= ~(1 << USB_PORT_FEAT_RESET); + rh->port |= 1 << USB_PORT_FEAT_ENABLE; +} + +/* this function must be called with interrupt disabled */ +static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597, int port) +{ + struct r8a66597_device *dev = r8a66597->root_hub[port].dev; + + r8a66597->root_hub[port].port &= ~(1 << USB_PORT_FEAT_CONNECTION); + r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_C_CONNECTION); + + disable_r8a66597_pipe_all(r8a66597, dev); + free_usb_address(r8a66597, dev); + + r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port)); +} + +/* this function must be called with interrupt disabled */ +static void prepare_setup_packet(struct r8a66597 *r8a66597, + struct r8a66597_td *td) +{ + int i; + u16 *p = (u16 *)td->urb->setup_packet; + unsigned long setup_addr = USBREQ; + + r8a66597_write(r8a66597, make_devsel(td->address) | td->maxpacket, + DCPMAXP); + r8a66597_write(r8a66597, (u16)~(SIGN | SACK), INTSTS1); + + for (i = 0; i < 4; i++) { + r8a66597_write(r8a66597, p[i], setup_addr); + setup_addr += 2; + } + r8a66597_write(r8a66597, SUREQ, DCPCTR); +} + +/* this function must be called with interrupt disabled */ +static void prepare_packet_read(struct r8a66597 *r8a66597, + struct r8a66597_td *td) +{ + struct urb *urb = td->urb; + + if (usb_pipecontrol(urb->pipe)) { + r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + if (urb->actual_length == 0) { + r8a66597_pipe_toggle(r8a66597, td->pipe, 1); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + } + pipe_irq_disable(r8a66597, td->pipenum); + pipe_start(r8a66597, td->pipe); + pipe_irq_enable(r8a66597, urb, td->pipenum); + } else { + if (urb->actual_length == 0) { + pipe_irq_disable(r8a66597, td->pipenum); + pipe_setting(r8a66597, td); + pipe_stop(r8a66597, td->pipe); + r8a66597_write(r8a66597, (u16)~(1 << td->pipenum), + BRDYSTS); + + if (td->pipe->pipetre) { + r8a66597_write(r8a66597, TRCLR, + td->pipe->pipetre); + r8a66597_write(r8a66597, + (urb->transfer_buffer_length + + td->maxpacket - 1) + / td->maxpacket, + td->pipe->pipetrn); + r8a66597_bset(r8a66597, TRENB, + td->pipe->pipetre); + } + + pipe_start(r8a66597, td->pipe); + pipe_irq_enable(r8a66597, urb, td->pipenum); + } + } +} + +/* this function must be called with interrupt disabled */ +static void prepare_packet_write(struct r8a66597 *r8a66597, + struct r8a66597_td *td) +{ + u16 tmp; + struct urb *urb = td->urb; + + if (usb_pipecontrol(urb->pipe)) { + pipe_stop(r8a66597, td->pipe); + r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + if (urb->actual_length == 0) { + r8a66597_pipe_toggle(r8a66597, td->pipe, 1); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + } + } else { + if (urb->actual_length == 0) + pipe_setting(r8a66597, td); + if (td->pipe->pipetre) + r8a66597_bclr(r8a66597, TRENB, td->pipe->pipetre); + } + r8a66597_write(r8a66597, (u16)~(1 << td->pipenum), BRDYSTS); + + fifo_change_from_pipe(r8a66597, td->pipe); + tmp = r8a66597_read(r8a66597, td->pipe->fifoctr); + if (unlikely((tmp & FRDY) == 0)) + pipe_irq_enable(r8a66597, urb, td->pipenum); + else + packet_write(r8a66597, td->pipenum); + pipe_start(r8a66597, td->pipe); +} + +/* this function must be called with interrupt disabled */ +static void prepare_status_packet(struct r8a66597 *r8a66597, + struct r8a66597_td *td) +{ + struct urb *urb = td->urb; + + r8a66597_pipe_toggle(r8a66597, td->pipe, 1); + + if (urb->setup_packet[0] & USB_ENDPOINT_DIR_MASK) { + r8a66597_bset(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_write(r8a66597, BVAL | BCLR, CFIFOCTR); + r8a66597_write(r8a66597, (u16)~BEMP0, BEMPSTS); + enable_irq_empty(r8a66597, 0); + } else { + r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG); + r8a66597_mdfy(r8a66597, 0, ISEL | CURPIPE, CFIFOSEL); + r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0); + r8a66597_write(r8a66597, BCLR, CFIFOCTR); + r8a66597_write(r8a66597, (u16)~BRDY0, BRDYSTS); + r8a66597_write(r8a66597, (u16)~BEMP0, BEMPSTS); + enable_irq_ready(r8a66597, 0); + } + enable_irq_nrdy(r8a66597, 0); + pipe_start(r8a66597, td->pipe); +} + +/* this function must be called with interrupt disabled */ +static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td) +{ + BUG_ON(!td); + + switch (td->type) { + case USB_PID_SETUP: + if (td->urb->setup_packet[1] == USB_REQ_SET_ADDRESS) { + td->set_address = 1; + td->urb->setup_packet[2] = alloc_usb_address(r8a66597, + td->urb); + if (td->urb->setup_packet[2] == 0) + return -EPIPE; + } + prepare_setup_packet(r8a66597, td); + break; + case USB_PID_IN: + prepare_packet_read(r8a66597, td); + break; + case USB_PID_OUT: + prepare_packet_write(r8a66597, td); + break; + case USB_PID_ACK: + prepare_status_packet(r8a66597, td); + break; + default: + err("invalid type."); + break; + } + + return 0; +} + +static int check_transfer_finish(struct r8a66597_td *td, struct urb *urb) +{ + if (usb_pipeisoc(urb->pipe)) { + if (urb->number_of_packets == td->iso_cnt) + return 1; + } + + /* control or bulk or interrupt */ + if ((urb->transfer_buffer_length <= urb->actual_length) || + (td->short_packet) || (td->zero_packet)) + return 1; + + return 0; +} + +/* this function must be called with interrupt disabled */ +static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td) +{ + unsigned long time; + + BUG_ON(!td); + + if (!list_empty(&r8a66597->pipe_queue[td->pipenum]) && + !usb_pipecontrol(td->urb->pipe) && usb_pipein(td->urb->pipe)) { + r8a66597->timeout_map |= 1 << td->pipenum; + switch (usb_pipetype(td->urb->pipe)) { + case PIPE_INTERRUPT: + case PIPE_ISOCHRONOUS: + time = 30; + break; + default: + time = 300; + break; + } + + mod_timer(&r8a66597->td_timer[td->pipenum], + jiffies + msecs_to_jiffies(time)); + } +} + +/* this function must be called with interrupt disabled */ +static void done(struct r8a66597 *r8a66597, struct r8a66597_td *td, + u16 pipenum, struct urb *urb) +{ + int restart = 0; + struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); + + r8a66597->timeout_map &= ~(1 << pipenum); + + if (likely(td)) { + if (td->set_address && urb->status != 0) + r8a66597->address_map &= ~(1 << urb->setup_packet[2]); + + pipe_toggle_save(r8a66597, td->pipe, urb); + list_del(&td->queue); + kfree(td); + } + + if (!list_empty(&r8a66597->pipe_queue[pipenum])) + restart = 1; + + if (likely(urb)) { + if (usb_pipeisoc(urb->pipe)) + urb->start_frame = r8a66597_get_frame(hcd); + + urb->hcpriv = NULL; + spin_unlock(&r8a66597->lock); + usb_hcd_giveback_urb(hcd, urb); + spin_lock(&r8a66597->lock); + } + + if (restart) { + td = r8a66597_get_td(r8a66597, pipenum); + if (unlikely(!td)) + return; + + start_transfer(r8a66597, td); + set_td_timer(r8a66597, td); + } +} + +/* this function must be called with interrupt disabled */ +static void finish_request(struct r8a66597 *r8a66597, struct r8a66597_td *td, + u16 pipenum, struct urb *urb) +__releases(r8a66597->lock) __acquires(r8a66597->lock) +{ + done(r8a66597, td, pipenum, urb); +} + +static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) +{ + u16 tmp; + int rcv_len, bufsize, urb_len, size; + u16 *buf; + struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum); + struct urb *urb; + int finish = 0; + + if (unlikely(!td)) + return; + urb = td->urb; + + fifo_change_from_pipe(r8a66597, td->pipe); + tmp = r8a66597_read(r8a66597, td->pipe->fifoctr); + if (unlikely((tmp & FRDY) == 0)) { + urb->status = -EPIPE; + pipe_stop(r8a66597, td->pipe); + pipe_irq_disable(r8a66597, pipenum); + err("in fifo not ready (%d)", pipenum); + finish_request(r8a66597, td, pipenum, td->urb); + return; + } + + /* prepare parameters */ + rcv_len = tmp & DTLN; + bufsize = td->maxpacket; + if (usb_pipeisoc(urb->pipe)) { + buf = (u16 *)(urb->transfer_buffer + + urb->iso_frame_desc[td->iso_cnt].offset); + urb_len = urb->iso_frame_desc[td->iso_cnt].length; + } else { + buf = (void *)urb->transfer_buffer + urb->actual_length; + urb_len = urb->transfer_buffer_length - urb->actual_length; + } + if (rcv_len < bufsize) + size = min(rcv_len, urb_len); + else + size = min(bufsize, urb_len); + + /* update parameters */ + urb->actual_length += size; + if (rcv_len == 0) + td->zero_packet = 1; + if ((size % td->maxpacket) > 0) { + td->short_packet = 1; + if (urb->transfer_buffer_length != urb->actual_length && + urb->transfer_flags & URB_SHORT_NOT_OK) + td->urb->status = -EREMOTEIO; + } + if (usb_pipeisoc(urb->pipe)) { + urb->iso_frame_desc[td->iso_cnt].actual_length = size; + urb->iso_frame_desc[td->iso_cnt].status = 0; + td->iso_cnt++; + } + + /* check transfer finish */ + if (check_transfer_finish(td, urb)) { + pipe_stop(r8a66597, td->pipe); + pipe_irq_disable(r8a66597, pipenum); + finish = 1; + } + + /* read fifo */ + if (urb->transfer_buffer) { + if (size == 0) + r8a66597_write(r8a66597, BCLR, td->pipe->fifoctr); + else + r8a66597_read_fifo(r8a66597, td->pipe->fifoaddr, + buf, size); + } + + if (finish && pipenum != 0) { + if (td->urb->status == -EINPROGRESS) + td->urb->status = 0; + finish_request(r8a66597, td, pipenum, urb); + } +} + +static void packet_write(struct r8a66597 *r8a66597, u16 pipenum) +{ + u16 tmp; + int bufsize, size; + u16 *buf; + struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum); + struct urb *urb; + + if (unlikely(!td)) + return; + urb = td->urb; + + fifo_change_from_pipe(r8a66597, td->pipe); + tmp = r8a66597_read(r8a66597, td->pipe->fifoctr); + if (unlikely((tmp & FRDY) == 0)) { + urb->status = -EPIPE; + pipe_stop(r8a66597, td->pipe); + pipe_irq_disable(r8a66597, pipenum); + err("out write fifo not ready. (%d)", pipenum); + finish_request(r8a66597, td, pipenum, td->urb); + return; + } + + /* prepare parameters */ + bufsize = td->maxpacket; + if (usb_pipeisoc(urb->pipe)) { + buf = (u16 *)(urb->transfer_buffer + + urb->iso_frame_desc[td->iso_cnt].offset); + size = min(bufsize, + (int)urb->iso_frame_desc[td->iso_cnt].length); + } else { + buf = (u16 *)(urb->transfer_buffer + urb->actual_length); + size = min((int)bufsize, + urb->transfer_buffer_length - urb->actual_length); + } + + /* write fifo */ + if (pipenum > 0) + r8a66597_write(r8a66597, (u16)~(1 << pipenum), BEMPSTS); + if (urb->transfer_buffer) { + r8a66597_write_fifo(r8a66597, td->pipe->fifoaddr, buf, size); + if (!usb_pipebulk(urb->pipe) || td->maxpacket != size) + r8a66597_write(r8a66597, BVAL, td->pipe->fifoctr); + } + + /* update parameters */ + urb->actual_length += size; + if (usb_pipeisoc(urb->pipe)) { + urb->iso_frame_desc[td->iso_cnt].actual_length = size; + urb->iso_frame_desc[td->iso_cnt].status = 0; + td->iso_cnt++; + } + + /* check transfer finish */ + if (check_transfer_finish(td, urb)) { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + if (!usb_pipeisoc(urb->pipe)) + enable_irq_nrdy(r8a66597, pipenum); + } else + pipe_irq_enable(r8a66597, urb, pipenum); +} + + +static void check_next_phase(struct r8a66597 *r8a66597) +{ + struct r8a66597_td *td = r8a66597_get_td(r8a66597, 0); + struct urb *urb; + u8 finish = 0; + + if (unlikely(!td)) + return; + urb = td->urb; + + switch (td->type) { + case USB_PID_IN: + case USB_PID_OUT: + if (urb->status != -EINPROGRESS) { + finish = 1; + break; + } + if (check_transfer_finish(td, urb)) + td->type = USB_PID_ACK; + break; + case USB_PID_SETUP: + if (urb->status != -EINPROGRESS) + finish = 1; + else if (urb->transfer_buffer_length == urb->actual_length) { + td->type = USB_PID_ACK; + urb->status = 0; + } else if (usb_pipeout(urb->pipe)) + td->type = USB_PID_OUT; + else + td->type = USB_PID_IN; + break; + case USB_PID_ACK: + finish = 1; + if (urb->status == -EINPROGRESS) + urb->status = 0; + break; + } + + if (finish) + finish_request(r8a66597, td, 0, urb); + else + start_transfer(r8a66597, td); +} + +static void set_urb_error(struct r8a66597 *r8a66597, u16 pipenum) +{ + struct r8a66597_td *td = r8a66597_get_td(r8a66597, pipenum); + + if (td && td->urb) { + u16 pid = r8a66597_read(r8a66597, td->pipe->pipectr) & PID; + + if (pid == PID_NAK) + td->urb->status = -ECONNRESET; + else + td->urb->status = -EPIPE; + } +} + +static void irq_pipe_ready(struct r8a66597 *r8a66597) +{ + u16 check; + u16 pipenum; + u16 mask; + struct r8a66597_td *td; + + mask = r8a66597_read(r8a66597, BRDYSTS) + & r8a66597_read(r8a66597, BRDYENB); + r8a66597_write(r8a66597, (u16)~mask, BRDYSTS); + if (mask & BRDY0) { + td = r8a66597_get_td(r8a66597, 0); + if (td && td->type == USB_PID_IN) + packet_read(r8a66597, 0); + else + pipe_irq_disable(r8a66597, 0); + check_next_phase(r8a66597); + } + + for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if (mask & check) { + td = r8a66597_get_td(r8a66597, pipenum); + if (unlikely(!td)) + continue; + + if (td->type == USB_PID_IN) + packet_read(r8a66597, pipenum); + else if (td->type == USB_PID_OUT) + packet_write(r8a66597, pipenum); + } + } +} + +static void irq_pipe_empty(struct r8a66597 *r8a66597) +{ + u16 tmp; + u16 check; + u16 pipenum; + u16 mask; + struct r8a66597_td *td; + + mask = r8a66597_read(r8a66597, BEMPSTS) + & r8a66597_read(r8a66597, BEMPENB); + r8a66597_write(r8a66597, (u16)~mask, BEMPSTS); + if (mask & BEMP0) { + cfifo_change(r8a66597, 0); + td = r8a66597_get_td(r8a66597, 0); + if (td && td->type != USB_PID_OUT) + disable_irq_empty(r8a66597, 0); + check_next_phase(r8a66597); + } + + for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if (mask & check) { + struct r8a66597_td *td; + td = r8a66597_get_td(r8a66597, pipenum); + if (unlikely(!td)) + continue; + + tmp = r8a66597_read(r8a66597, td->pipe->pipectr); + if ((tmp & INBUFM) == 0) { + disable_irq_empty(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + if (td->urb->status == -EINPROGRESS) + td->urb->status = 0; + finish_request(r8a66597, td, pipenum, td->urb); + } + } + } +} + +static void irq_pipe_nrdy(struct r8a66597 *r8a66597) +{ + u16 check; + u16 pipenum; + u16 mask; + + mask = r8a66597_read(r8a66597, NRDYSTS) + & r8a66597_read(r8a66597, NRDYENB); + r8a66597_write(r8a66597, (u16)~mask, NRDYSTS); + if (mask & NRDY0) { + cfifo_change(r8a66597, 0); + set_urb_error(r8a66597, 0); + pipe_irq_disable(r8a66597, 0); + check_next_phase(r8a66597); + } + + for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if (mask & check) { + struct r8a66597_td *td; + td = r8a66597_get_td(r8a66597, pipenum); + if (unlikely(!td)) + continue; + + set_urb_error(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + pipe_stop(r8a66597, td->pipe); + finish_request(r8a66597, td, pipenum, td->urb); + } + } +} + +static void start_root_hub_sampling(struct r8a66597 *r8a66597, int port) +{ + struct r8a66597_root_hub *rh = &r8a66597->root_hub[port]; + + rh->old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST; + rh->scount = R8A66597_MAX_SAMPLING; + mod_timer(&r8a66597->rh_timer, jiffies + msecs_to_jiffies(50)); +} + +static irqreturn_t r8a66597_irq(struct usb_hcd *hcd) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + u16 intsts0, intsts1, intsts2; + u16 intenb0, intenb1, intenb2; + u16 mask0, mask1, mask2; + + spin_lock(&r8a66597->lock); + + intsts0 = r8a66597_read(r8a66597, INTSTS0); + intsts1 = r8a66597_read(r8a66597, INTSTS1); + intsts2 = r8a66597_read(r8a66597, INTSTS2); + intenb0 = r8a66597_read(r8a66597, INTENB0); + intenb1 = r8a66597_read(r8a66597, INTENB1); + intenb2 = r8a66597_read(r8a66597, INTENB2); + + mask2 = intsts2 & intenb2; + mask1 = intsts1 & intenb1; + mask0 = intsts0 & intenb0 & (BEMP | NRDY | BRDY); + if (mask2) { + if (mask2 & ATTCH) { + r8a66597_write(r8a66597, (u16)~ATTCH, INTSTS2); + r8a66597_bclr(r8a66597, ATTCHE, INTENB2); + + /* start usb bus sampling */ + start_root_hub_sampling(r8a66597, 1); + } + if (mask2 & DTCH) { + r8a66597_write(r8a66597, (u16)~DTCH, INTSTS2); + r8a66597_bclr(r8a66597, DTCHE, INTENB2); + r8a66597_usb_disconnect(r8a66597, 1); + } + } + + if (mask1) { + if (mask1 & ATTCH) { + r8a66597_write(r8a66597, (u16)~ATTCH, INTSTS1); + r8a66597_bclr(r8a66597, ATTCHE, INTENB1); + + /* start usb bus sampling */ + start_root_hub_sampling(r8a66597, 0); + } + if (mask1 & DTCH) { + r8a66597_write(r8a66597, (u16)~DTCH, INTSTS1); + r8a66597_bclr(r8a66597, DTCHE, INTENB1); + r8a66597_usb_disconnect(r8a66597, 0); + } + if (mask1 & SIGN) { + r8a66597_write(r8a66597, (u16)~SIGN, INTSTS1); + set_urb_error(r8a66597, 0); + check_next_phase(r8a66597); + } + if (mask1 & SACK) { + r8a66597_write(r8a66597, (u16)~SACK, INTSTS1); + check_next_phase(r8a66597); + } + } + if (mask0) { + if (mask0 & BRDY) + irq_pipe_ready(r8a66597); + if (mask0 & BEMP) + irq_pipe_empty(r8a66597); + if (mask0 & NRDY) + irq_pipe_nrdy(r8a66597); + } + + spin_unlock(&r8a66597->lock); + return IRQ_HANDLED; +} + +/* this function must be called with interrupt disabled */ +static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port) +{ + u16 tmp; + struct r8a66597_root_hub *rh = &r8a66597->root_hub[port]; + + if (rh->port & (1 << USB_PORT_FEAT_RESET)) { + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + tmp = r8a66597_read(r8a66597, dvstctr_reg); + if ((tmp & USBRST) == USBRST) { + r8a66597_mdfy(r8a66597, UACT, USBRST | UACT, + dvstctr_reg); + mod_timer(&r8a66597->rh_timer, + jiffies + msecs_to_jiffies(50)); + } else + r8a66597_usb_connect(r8a66597, port); + } + + if (rh->scount > 0) { + tmp = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST; + if (tmp == rh->old_syssts) { + rh->scount--; + if (rh->scount == 0) { + if (tmp == FS_JSTS) { + r8a66597_bset(r8a66597, HSE, + get_syscfg_reg(port)); + r8a66597_usb_preconnect(r8a66597, port); + } else if (tmp == LS_JSTS) { + r8a66597_bclr(r8a66597, HSE, + get_syscfg_reg(port)); + r8a66597_usb_preconnect(r8a66597, port); + } else if (tmp == SE0) + r8a66597_bset(r8a66597, ATTCHE, + get_intenb_reg(port)); + } else { + mod_timer(&r8a66597->rh_timer, + jiffies + msecs_to_jiffies(50)); + } + } else { + rh->scount = R8A66597_MAX_SAMPLING; + rh->old_syssts = tmp; + mod_timer(&r8a66597->rh_timer, + jiffies + msecs_to_jiffies(50)); + } + } +} + +static void r8a66597_td_timer(unsigned long _r8a66597) +{ + struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597; + unsigned long flags; + u16 pipenum; + struct r8a66597_td *td, *new_td = NULL; + struct r8a66597_pipe *pipe; + + spin_lock_irqsave(&r8a66597->lock, flags); + for (pipenum = 0; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + if (!(r8a66597->timeout_map & (1 << pipenum))) + continue; + if (timer_pending(&r8a66597->td_timer[pipenum])) + continue; + + td = r8a66597_get_td(r8a66597, pipenum); + if (!td) { + r8a66597->timeout_map &= ~(1 << pipenum); + continue; + } + + if (td->urb->actual_length) { + set_td_timer(r8a66597, td); + break; + } + + pipe = td->pipe; + pipe_stop(r8a66597, pipe); + + new_td = td; + do { + list_move_tail(&new_td->queue, + &r8a66597->pipe_queue[pipenum]); + new_td = r8a66597_get_td(r8a66597, pipenum); + if (!new_td) { + new_td = td; + break; + } + } while (td != new_td && td->address == new_td->address); + + start_transfer(r8a66597, new_td); + + if (td == new_td) + r8a66597->timeout_map &= ~(1 << pipenum); + else + set_td_timer(r8a66597, new_td); + break; + } + spin_unlock_irqrestore(&r8a66597->lock, flags); +} + +static void r8a66597_timer(unsigned long _r8a66597) +{ + struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597; + unsigned long flags; + + spin_lock_irqsave(&r8a66597->lock, flags); + + r8a66597_root_hub_control(r8a66597, 0); + r8a66597_root_hub_control(r8a66597, 1); + + spin_unlock_irqrestore(&r8a66597->lock, flags); +} + +static int check_pipe_config(struct r8a66597 *r8a66597, struct urb *urb) +{ + struct r8a66597_device *dev = get_urb_to_r8a66597_dev(r8a66597, urb); + + if (dev && dev->address && dev->state != USB_STATE_CONFIGURED && + (urb->dev->state == USB_STATE_CONFIGURED)) + return 1; + else + return 0; +} + +static int r8a66597_start(struct usb_hcd *hcd) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + int ret; + + hcd->state = HC_STATE_RUNNING; + if ((ret = enable_controller(r8a66597)) < 0) + return ret; + + return 0; +} + +static void r8a66597_stop(struct usb_hcd *hcd) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + + disable_controller(r8a66597); +} + +static void set_address_zero(struct r8a66597 *r8a66597, struct urb *urb) +{ + unsigned int usb_address = usb_pipedevice(urb->pipe); + u16 root_port, hub_port; + + if (usb_address == 0) { + get_port_number(urb->dev->devpath, + &root_port, &hub_port); + set_devadd_reg(r8a66597, 0, + get_r8a66597_usb_speed(urb->dev->speed), + get_parent_r8a66597_address(r8a66597, urb->dev), + hub_port, root_port); + } +} + +static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597, + struct urb *urb, + struct usb_host_endpoint *hep, + gfp_t mem_flags) +{ + struct r8a66597_td *td; + u16 pipenum; + + td = kzalloc(sizeof(struct r8a66597_td), mem_flags); + if (td == NULL) + return NULL; + + pipenum = r8a66597_get_pipenum(urb, hep); + td->pipenum = pipenum; + td->pipe = hep->hcpriv; + td->urb = urb; + td->address = get_urb_to_r8a66597_addr(r8a66597, urb); + td->maxpacket = usb_maxpacket(urb->dev, urb->pipe, + !usb_pipein(urb->pipe)); + if (usb_pipecontrol(urb->pipe)) + td->type = USB_PID_SETUP; + else if (usb_pipein(urb->pipe)) + td->type = USB_PID_IN; + else + td->type = USB_PID_OUT; + INIT_LIST_HEAD(&td->queue); + + return td; +} + +static int r8a66597_urb_enqueue(struct usb_hcd *hcd, + struct usb_host_endpoint *hep, + struct urb *urb, + gfp_t mem_flags) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + struct r8a66597_td *td = NULL; + int ret = 0, request = 0; + unsigned long flags; + + spin_lock_irqsave(&r8a66597->lock, flags); + if (!get_urb_to_r8a66597_dev(r8a66597, urb)) { + ret = -ENODEV; + goto error; + } + + if (!hep->hcpriv) { + hep->hcpriv = kzalloc(sizeof(struct r8a66597_pipe), mem_flags); + if (!hep->hcpriv) { + ret = -ENOMEM; + goto error; + } + set_pipe_reg_addr(hep->hcpriv, R8A66597_PIPE_NO_DMA); + if (usb_pipeendpoint(urb->pipe)) + init_pipe_info(r8a66597, urb, hep, &hep->desc); + } + + if (unlikely(check_pipe_config(r8a66597, urb))) + init_pipe_config(r8a66597, urb); + + set_address_zero(r8a66597, urb); + td = r8a66597_make_td(r8a66597, urb, hep, mem_flags); + if (td == NULL) { + ret = -ENOMEM; + goto error; + } + if (list_empty(&r8a66597->pipe_queue[td->pipenum])) + request = 1; + list_add_tail(&td->queue, &r8a66597->pipe_queue[td->pipenum]); + + spin_lock(&urb->lock); + if (urb->status != -EINPROGRESS) { + spin_unlock(&urb->lock); + ret = -EPIPE; + goto error; + } + urb->hcpriv = td; + spin_unlock(&urb->lock); + + if (request) { + ret = start_transfer(r8a66597, td); + if (ret < 0) { + list_del(&td->queue); + kfree(td); + } + } else + set_td_timer(r8a66597, td); + +error: + spin_unlock_irqrestore(&r8a66597->lock, flags); + return ret; +} + +static int r8a66597_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + struct r8a66597_td *td; + unsigned long flags; + + spin_lock_irqsave(&r8a66597->lock, flags); + if (urb->hcpriv) { + td = urb->hcpriv; + pipe_stop(r8a66597, td->pipe); + pipe_irq_disable(r8a66597, td->pipenum); + disable_irq_empty(r8a66597, td->pipenum); + done(r8a66597, td, td->pipenum, urb); + } + spin_unlock_irqrestore(&r8a66597->lock, flags); + return 0; +} + +static void r8a66597_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *hep) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + struct r8a66597_pipe *pipe = (struct r8a66597_pipe *)hep->hcpriv; + struct r8a66597_td *td; + struct urb *urb = NULL; + u16 pipenum; + unsigned long flags; + + if (pipe == NULL) + return; + pipenum = pipe->info.pipenum; + + if (pipenum == 0) { + kfree(hep->hcpriv); + hep->hcpriv = NULL; + return; + } + + spin_lock_irqsave(&r8a66597->lock, flags); + pipe_stop(r8a66597, pipe); + pipe_irq_disable(r8a66597, pipenum); + disable_irq_empty(r8a66597, pipenum); + td = r8a66597_get_td(r8a66597, pipenum); + if (td) + urb = td->urb; + done(r8a66597, td, pipenum, urb); + kfree(hep->hcpriv); + hep->hcpriv = NULL; + spin_unlock_irqrestore(&r8a66597->lock, flags); +} + +static int r8a66597_get_frame(struct usb_hcd *hcd) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + return r8a66597_read(r8a66597, FRMNUM) & 0x03FF; +} + +static void collect_usb_address_map(struct usb_device *udev, unsigned long *map) +{ + int chix; + + if (udev->state == USB_STATE_CONFIGURED && + udev->parent && udev->parent->devnum > 1 && + udev->parent->descriptor.bDeviceClass == USB_CLASS_HUB) + map[udev->devnum/32] |= (1 << (udev->devnum % 32)); + + for (chix = 0; chix < udev->maxchild; chix++) { + struct usb_device *childdev = udev->children[chix]; + + if (childdev) + collect_usb_address_map(childdev, map); + } +} + +/* this function must be called with interrupt disabled */ +static struct r8a66597_device *get_r8a66597_device(struct r8a66597 *r8a66597, + int addr) +{ + struct r8a66597_device *dev; + struct list_head *list = &r8a66597->child_device; + + list_for_each_entry(dev, list, device_list) { + if (!dev) + continue; + if (dev->usb_address != addr) + continue; + + return dev; + } + + err("get_r8a66597_device fail.(%d)\n", addr); + return NULL; +} + +static void update_usb_address_map(struct r8a66597 *r8a66597, + struct usb_device *root_hub, + unsigned long *map) +{ + int i, j, addr; + unsigned long diff; + unsigned long flags; + + for (i = 0; i < 4; i++) { + diff = r8a66597->child_connect_map[i] ^ map[i]; + if (!diff) + continue; + + for (j = 0; j < 32; j++) { + if (!(diff & (1 << j))) + continue; + + addr = i * 32 + j; + if (map[i] & (1 << j)) + set_child_connect_map(r8a66597, addr); + else { + struct r8a66597_device *dev; + + spin_lock_irqsave(&r8a66597->lock, flags); + dev = get_r8a66597_device(r8a66597, addr); + disable_r8a66597_pipe_all(r8a66597, dev); + free_usb_address(r8a66597, dev); + put_child_connect_map(r8a66597, addr); + spin_unlock_irqrestore(&r8a66597->lock, flags); + } + } + } +} + +static void r8a66597_check_detect_child(struct r8a66597 *r8a66597, + struct usb_hcd *hcd) +{ + struct usb_bus *bus; + unsigned long now_map[4]; + + memset(now_map, 0, sizeof(now_map)); + + list_for_each_entry(bus, &usb_bus_list, bus_list) { + if (!bus->root_hub) + continue; + + if (bus->busnum != hcd->self.busnum) + continue; + + collect_usb_address_map(bus->root_hub, now_map); + update_usb_address_map(r8a66597, bus->root_hub, now_map); + } +} + +static int r8a66597_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + unsigned long flags; + int i; + + r8a66597_check_detect_child(r8a66597, hcd); + + spin_lock_irqsave(&r8a66597->lock, flags); + + *buf = 0; /* initialize (no change) */ + + for (i = 0; i < R8A66597_MAX_ROOT_HUB; i++) { + if (r8a66597->root_hub[i].port & 0xffff0000) + *buf |= 1 << (i + 1); + } + + spin_unlock_irqrestore(&r8a66597->lock, flags); + + return (*buf != 0); +} + +static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597, + struct usb_hub_descriptor *desc) +{ + desc->bDescriptorType = 0x29; + desc->bHubContrCurrent = 0; + desc->bNbrPorts = R8A66597_MAX_ROOT_HUB; + desc->bDescLength = 9; + desc->bPwrOn2PwrGood = 0; + desc->wHubCharacteristics = cpu_to_le16(0x0011); + desc->bitmap[0] = ((1 << R8A66597_MAX_ROOT_HUB) - 1) << 1; + desc->bitmap[1] = ~0; +} + +static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd); + int ret; + int port = (wIndex & 0x00FF) - 1; + struct r8a66597_root_hub *rh = &r8a66597->root_hub[port]; + unsigned long flags; + + ret = 0; + + spin_lock_irqsave(&r8a66597->lock, flags); + switch (typeReq) { + case ClearHubFeature: + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (wIndex > R8A66597_MAX_ROOT_HUB) + goto error; + if (wLength != 0) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + rh->port &= (1 << USB_PORT_FEAT_POWER); + break; + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_POWER: + r8a66597_port_power(r8a66597, port, 0); + break; + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + break; + default: + goto error; + } + rh->port &= ~(1 << wValue); + break; + case GetHubDescriptor: + r8a66597_hub_descriptor(r8a66597, + (struct usb_hub_descriptor *)buf); + break; + case GetHubStatus: + *buf = 0x00; + break; + case GetPortStatus: + if (wIndex > R8A66597_MAX_ROOT_HUB) + goto error; + *(u32 *)buf = rh->port; + break; + case SetPortFeature: + if (wIndex > R8A66597_MAX_ROOT_HUB) + goto error; + if (wLength != 0) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_POWER: + r8a66597_port_power(r8a66597, port, 1); + rh->port |= (1 << USB_PORT_FEAT_POWER); + break; + case USB_PORT_FEAT_RESET: { + struct r8a66597_device *dev = rh->dev; + + rh->port |= (1 << USB_PORT_FEAT_RESET); + + disable_r8a66597_pipe_all(r8a66597, dev); + free_usb_address(r8a66597, dev); + + r8a66597_mdfy(r8a66597, USBRST, USBRST | UACT, + get_dvstctr_reg(port)); + mod_timer(&r8a66597->rh_timer, + jiffies + msecs_to_jiffies(50)); + } + break; + default: + goto error; + } + rh->port |= 1 << wValue; + break; + default: +error: + ret = -EPIPE; + break; + } + + spin_unlock_irqrestore(&r8a66597->lock, flags); + return ret; +} + +static struct hc_driver r8a66597_hc_driver = { + .description = hcd_name, + .hcd_priv_size = sizeof(struct r8a66597), + .irq = r8a66597_irq, + + /* + * generic hardware linkage + */ + .flags = HCD_USB2, + + .start = r8a66597_start, + .stop = r8a66597_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = r8a66597_urb_enqueue, + .urb_dequeue = r8a66597_urb_dequeue, + .endpoint_disable = r8a66597_endpoint_disable, + + /* + * periodic schedule support + */ + .get_frame_number = r8a66597_get_frame, + + /* + * root hub support + */ + .hub_status_data = r8a66597_hub_status_data, + .hub_control = r8a66597_hub_control, +}; + +#if defined(CONFIG_PM) +static int r8a66597_suspend(struct platform_device *pdev, pm_message_t state) +{ + pdev->dev.power.power_state = state; + return 0; +} + +static int r8a66597_resume(struct platform_device *pdev) +{ + pdev->dev.power.power_state = PMSG_ON; + return 0; +} +#else /* if defined(CONFIG_PM) */ +#define r8a66597_suspend NULL +#define r8a66597_resume NULL +#endif + +static int __init_or_module r8a66597_remove(struct platform_device *pdev) +{ + struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev); + struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); + + del_timer_sync(&r8a66597->rh_timer); + iounmap((void *)r8a66597->reg); + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + return 0; +} + +#define resource_len(r) (((r)->end - (r)->start) + 1) +static int __init r8a66597_probe(struct platform_device *pdev) +{ + struct resource *res = NULL; + int irq = -1; + void __iomem *reg = NULL; + struct usb_hcd *hcd = NULL; + struct r8a66597 *r8a66597; + int ret = 0; + int i; + + if (pdev->dev.dma_mask) { + ret = -EINVAL; + err("dma not support"); + goto clean_up; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + (char *)hcd_name); + if (!res) { + ret = -ENODEV; + err("platform_get_resource_byname error."); + goto clean_up; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = -ENODEV; + err("platform_get_irq error."); + goto clean_up; + } + + reg = ioremap(res->start, resource_len(res)); + if (reg == NULL) { + ret = -ENOMEM; + err("ioremap error."); + goto clean_up; + } + + /* initialize hcd */ + hcd = usb_create_hcd(&r8a66597_hc_driver, &pdev->dev, (char *)hcd_name); + if (!hcd) { + ret = -ENOMEM; + err("Failed to create hcd"); + goto clean_up; + } + r8a66597 = hcd_to_r8a66597(hcd); + memset(r8a66597, 0, sizeof(struct r8a66597)); + dev_set_drvdata(&pdev->dev, r8a66597); + + spin_lock_init(&r8a66597->lock); + init_timer(&r8a66597->rh_timer); + r8a66597->rh_timer.function = r8a66597_timer; + r8a66597->rh_timer.data = (unsigned long)r8a66597; + r8a66597->reg = (unsigned long)reg; + + for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) { + INIT_LIST_HEAD(&r8a66597->pipe_queue[i]); + init_timer(&r8a66597->td_timer[i]); + r8a66597->td_timer[i].function = r8a66597_td_timer; + r8a66597->td_timer[i].data = (unsigned long)r8a66597; + } + INIT_LIST_HEAD(&r8a66597->child_device); + + hcd->rsrc_start = res->start; + ret = usb_add_hcd(hcd, irq, 0); + if (ret != 0) { + err("Failed to add hcd"); + goto clean_up; + } + + return 0; + +clean_up: + if (reg) + iounmap(reg); + if (res) + release_mem_region(res->start, 1); + + return ret; +} + +static struct platform_driver r8a66597_driver = { + .probe = r8a66597_probe, + .remove = r8a66597_remove, + .suspend = r8a66597_suspend, + .resume = r8a66597_resume, + .driver = { + .name = (char *) hcd_name, + }, +}; + +static int __init r8a66597_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + info("driver %s, %s", hcd_name, DRIVER_VERSION); + return platform_driver_register(&r8a66597_driver); +} +module_init(r8a66597_init); + +static void __exit r8a66597_cleanup(void) +{ + platform_driver_unregister(&r8a66597_driver); +} +module_exit(r8a66597_cleanup); + diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h new file mode 100644 index 0000000..97c2a71 --- /dev/null +++ b/drivers/usb/host/r8a66597.h @@ -0,0 +1,634 @@ +/* + * R8A66597 HCD (Host Controller Driver) + * + * Copyright (C) 2006-2007 Renesas Solutions Corp. + * Portions Copyright (C) 2004 Psion Teklogix (for NetBook PRO) + * Portions Copyright (C) 2004-2005 David Brownell + * Portions Copyright (C) 1999 Roman Weissgaerber + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com> + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __R8A66597_H__ +#define __R8A66597_H__ + +#define SYSCFG0 0x00 +#define SYSCFG1 0x02 +#define SYSSTS0 0x04 +#define SYSSTS1 0x06 +#define DVSTCTR0 0x08 +#define DVSTCTR1 0x0A +#define TESTMODE 0x0C +#define PINCFG 0x0E +#define DMA0CFG 0x10 +#define DMA1CFG 0x12 +#define CFIFO 0x14 +#define D0FIFO 0x18 +#define D1FIFO 0x1C +#define CFIFOSEL 0x20 +#define CFIFOCTR 0x22 +#define CFIFOSIE 0x24 +#define D0FIFOSEL 0x28 +#define D0FIFOCTR 0x2A +#define D1FIFOSEL 0x2C +#define D1FIFOCTR 0x2E +#define INTENB0 0x30 +#define INTENB1 0x32 +#define INTENB2 0x34 +#define BRDYENB 0x36 +#define NRDYENB 0x38 +#define BEMPENB 0x3A +#define SOFCFG 0x3C +#define INTSTS0 0x40 +#define INTSTS1 0x42 +#define INTSTS2 0x44 +#define BRDYSTS 0x46 +#define NRDYSTS 0x48 +#define BEMPSTS 0x4A +#define FRMNUM 0x4C +#define UFRMNUM 0x4E +#define USBADDR 0x50 +#define USBREQ 0x54 +#define USBVAL 0x56 +#define USBINDX 0x58 +#define USBLENG 0x5A +#define DCPCFG 0x5C +#define DCPMAXP 0x5E +#define DCPCTR 0x60 +#define PIPESEL 0x64 +#define PIPECFG 0x68 +#define PIPEBUF 0x6A +#define PIPEMAXP 0x6C +#define PIPEPERI 0x6E +#define PIPE1CTR 0x70 +#define PIPE2CTR 0x72 +#define PIPE3CTR 0x74 +#define PIPE4CTR 0x76 +#define PIPE5CTR 0x78 +#define PIPE6CTR 0x7A +#define PIPE7CTR 0x7C +#define PIPE8CTR 0x7E +#define PIPE9CTR 0x80 +#define PIPE1TRE 0x90 +#define PIPE1TRN 0x92 +#define PIPE2TRE 0x94 +#define PIPE2TRN 0x96 +#define PIPE3TRE 0x98 +#define PIPE3TRN 0x9A +#define PIPE4TRE 0x9C +#define PIPE4TRN 0x9E +#define PIPE5TRE 0xA0 +#define PIPE5TRN 0xA2 +#define DEVADD0 0xD0 +#define DEVADD1 0xD2 +#define DEVADD2 0xD4 +#define DEVADD3 0xD6 +#define DEVADD4 0xD8 +#define DEVADD5 0xDA +#define DEVADD6 0xDC +#define DEVADD7 0xDE +#define DEVADD8 0xE0 +#define DEVADD9 0xE2 +#define DEVADDA 0xE4 + +/* System Configuration Control Register */ +#define XTAL 0xC000 /* b15-14: Crystal selection */ +#define XTAL48 0x8000 /* 48MHz */ +#define XTAL24 0x4000 /* 24MHz */ +#define XTAL12 0x0000 /* 12MHz */ +#define XCKE 0x2000 /* b13: External clock enable */ +#define PLLC 0x0800 /* b11: PLL control */ +#define SCKE 0x0400 /* b10: USB clock enable */ +#define PCSDIS 0x0200 /* b9: not CS wakeup */ +#define LPSME 0x0100 /* b8: Low power sleep mode */ +#define HSE 0x0080 /* b7: Hi-speed enable */ +#define DCFM 0x0040 /* b6: Controller function select */ +#define DRPD 0x0020 /* b5: D+/- pull down control */ +#define DPRPU 0x0010 /* b4: D+ pull up control */ +#define USBE 0x0001 /* b0: USB module operation enable */ + +/* System Configuration Status Register */ +#define OVCBIT 0x8000 /* b15-14: Over-current bit */ +#define OVCMON 0xC000 /* b15-14: Over-current monitor */ +#define SOFEA 0x0020 /* b5: SOF monitor */ +#define IDMON 0x0004 /* b3: ID-pin monitor */ +#define LNST 0x0003 /* b1-0: D+, D- line status */ +#define SE1 0x0003 /* SE1 */ +#define FS_KSTS 0x0002 /* Full-Speed K State */ +#define FS_JSTS 0x0001 /* Full-Speed J State */ +#define LS_JSTS 0x0002 /* Low-Speed J State */ +#define LS_KSTS 0x0001 /* Low-Speed K State */ +#define SE0 0x0000 /* SE0 */ + +/* Device State Control Register */ +#define EXTLP0 0x0400 /* b10: External port */ +#define VBOUT 0x0200 /* b9: VBUS output */ +#define WKUP 0x0100 /* b8: Remote wakeup */ +#define RWUPE 0x0080 /* b7: Remote wakeup sense */ +#define USBRST 0x0040 /* b6: USB reset enable */ +#define RESUME 0x0020 /* b5: Resume enable */ +#define UACT 0x0010 /* b4: USB bus enable */ +#define RHST 0x0007 /* b1-0: Reset handshake status */ +#define HSPROC 0x0004 /* HS handshake is processing */ +#define HSMODE 0x0003 /* Hi-Speed mode */ +#define FSMODE 0x0002 /* Full-Speed mode */ +#define LSMODE 0x0001 /* Low-Speed mode */ +#define UNDECID 0x0000 /* Undecided */ + +/* Test Mode Register */ +#define UTST 0x000F /* b3-0: Test select */ +#define H_TST_PACKET 0x000C /* HOST TEST Packet */ +#define H_TST_SE0_NAK 0x000B /* HOST TEST SE0 NAK */ +#define H_TST_K 0x000A /* HOST TEST K */ +#define H_TST_J 0x0009 /* HOST TEST J */ +#define H_TST_NORMAL 0x0000 /* HOST Normal Mode */ +#define P_TST_PACKET 0x0004 /* PERI TEST Packet */ +#define P_TST_SE0_NAK 0x0003 /* PERI TEST SE0 NAK */ +#define P_TST_K 0x0002 /* PERI TEST K */ +#define P_TST_J 0x0001 /* PERI TEST J */ +#define P_TST_NORMAL 0x0000 /* PERI Normal Mode */ + +/* Data Pin Configuration Register */ +#define LDRV 0x8000 /* b15: Drive Current Adjust */ +#define VIF1 0x0000 /* VIF = 1.8V */ +#define VIF3 0x8000 /* VIF = 3.3V */ +#define INTA 0x0001 /* b1: USB INT-pin active */ + +/* DMAx Pin Configuration Register */ +#define DREQA 0x4000 /* b14: Dreq active select */ +#define BURST 0x2000 /* b13: Burst mode */ +#define DACKA 0x0400 /* b10: Dack active select */ +#define DFORM 0x0380 /* b9-7: DMA mode select */ +#define CPU_ADR_RD_WR 0x0000 /* Address + RD/WR mode (CPU bus) */ +#define CPU_DACK_RD_WR 0x0100 /* DACK + RD/WR mode (CPU bus) */ +#define CPU_DACK_ONLY 0x0180 /* DACK only mode (CPU bus) */ +#define SPLIT_DACK_ONLY 0x0200 /* DACK only mode (SPLIT bus) */ +#define DENDA 0x0040 /* b6: Dend active select */ +#define PKTM 0x0020 /* b5: Packet mode */ +#define DENDE 0x0010 /* b4: Dend enable */ +#define OBUS 0x0004 /* b2: OUTbus mode */ + +/* CFIFO/DxFIFO Port Select Register */ +#define RCNT 0x8000 /* b15: Read count mode */ +#define REW 0x4000 /* b14: Buffer rewind */ +#define DCLRM 0x2000 /* b13: DMA buffer clear mode */ +#define DREQE 0x1000 /* b12: DREQ output enable */ +#define MBW 0x0400 /* b10: Maximum bit width for FIFO access */ +#define MBW_8 0x0000 /* 8bit */ +#define MBW_16 0x0400 /* 16bit */ +#define BIGEND 0x0100 /* b8: Big endian mode */ +#define BYTE_LITTLE 0x0000 /* little dendian */ +#define BYTE_BIG 0x0100 /* big endifan */ +#define ISEL 0x0020 /* b5: DCP FIFO port direction select */ +#define CURPIPE 0x000F /* b2-0: PIPE select */ + +/* CFIFO/DxFIFO Port Control Register */ +#define BVAL 0x8000 /* b15: Buffer valid flag */ +#define BCLR 0x4000 /* b14: Buffer clear */ +#define FRDY 0x2000 /* b13: FIFO ready */ +#define DTLN 0x0FFF /* b11-0: FIFO received data length */ + +/* Interrupt Enable Register 0 */ +#define VBSE 0x8000 /* b15: VBUS interrupt */ +#define RSME 0x4000 /* b14: Resume interrupt */ +#define SOFE 0x2000 /* b13: Frame update interrupt */ +#define DVSE 0x1000 /* b12: Device state transition interrupt */ +#define CTRE 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMPE 0x0400 /* b10: Buffer empty interrupt */ +#define NRDYE 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDYE 0x0100 /* b8: Buffer ready interrupt */ + +/* Interrupt Enable Register 1 */ +#define OVRCRE 0x8000 /* b15: Over-current interrupt */ +#define BCHGE 0x4000 /* b14: USB us chenge interrupt */ +#define DTCHE 0x1000 /* b12: Detach sense interrupt */ +#define ATTCHE 0x0800 /* b11: Attach sense interrupt */ +#define EOFERRE 0x0040 /* b6: EOF error interrupt */ +#define SIGNE 0x0020 /* b5: SETUP IGNORE interrupt */ +#define SACKE 0x0010 /* b4: SETUP ACK interrupt */ + +/* BRDY Interrupt Enable/Status Register */ +#define BRDY9 0x0200 /* b9: PIPE9 */ +#define BRDY8 0x0100 /* b8: PIPE8 */ +#define BRDY7 0x0080 /* b7: PIPE7 */ +#define BRDY6 0x0040 /* b6: PIPE6 */ +#define BRDY5 0x0020 /* b5: PIPE5 */ +#define BRDY4 0x0010 /* b4: PIPE4 */ +#define BRDY3 0x0008 /* b3: PIPE3 */ +#define BRDY2 0x0004 /* b2: PIPE2 */ +#define BRDY1 0x0002 /* b1: PIPE1 */ +#define BRDY0 0x0001 /* b1: PIPE0 */ + +/* NRDY Interrupt Enable/Status Register */ +#define NRDY9 0x0200 /* b9: PIPE9 */ +#define NRDY8 0x0100 /* b8: PIPE8 */ +#define NRDY7 0x0080 /* b7: PIPE7 */ +#define NRDY6 0x0040 /* b6: PIPE6 */ +#define NRDY5 0x0020 /* b5: PIPE5 */ +#define NRDY4 0x0010 /* b4: PIPE4 */ +#define NRDY3 0x0008 /* b3: PIPE3 */ +#define NRDY2 0x0004 /* b2: PIPE2 */ +#define NRDY1 0x0002 /* b1: PIPE1 */ +#define NRDY0 0x0001 /* b1: PIPE0 */ + +/* BEMP Interrupt Enable/Status Register */ +#define BEMP9 0x0200 /* b9: PIPE9 */ +#define BEMP8 0x0100 /* b8: PIPE8 */ +#define BEMP7 0x0080 /* b7: PIPE7 */ +#define BEMP6 0x0040 /* b6: PIPE6 */ +#define BEMP5 0x0020 /* b5: PIPE5 */ +#define BEMP4 0x0010 /* b4: PIPE4 */ +#define BEMP3 0x0008 /* b3: PIPE3 */ +#define BEMP2 0x0004 /* b2: PIPE2 */ +#define BEMP1 0x0002 /* b1: PIPE1 */ +#define BEMP0 0x0001 /* b0: PIPE0 */ + +/* SOF Pin Configuration Register */ +#define TRNENSEL 0x0100 /* b8: Select transaction enable period */ +#define BRDYM 0x0040 /* b6: BRDY clear timing */ +#define INTL 0x0020 /* b5: Interrupt sense select */ +#define EDGESTS 0x0010 /* b4: */ +#define SOFMODE 0x000C /* b3-2: SOF pin select */ +#define SOF_125US 0x0008 /* SOF OUT 125us Frame Signal */ +#define SOF_1MS 0x0004 /* SOF OUT 1ms Frame Signal */ +#define SOF_DISABLE 0x0000 /* SOF OUT Disable */ + +/* Interrupt Status Register 0 */ +#define VBINT 0x8000 /* b15: VBUS interrupt */ +#define RESM 0x4000 /* b14: Resume interrupt */ +#define SOFR 0x2000 /* b13: SOF frame update interrupt */ +#define DVST 0x1000 /* b12: Device state transition interrupt */ +#define CTRT 0x0800 /* b11: Control transfer stage transition interrupt */ +#define BEMP 0x0400 /* b10: Buffer empty interrupt */ +#define NRDY 0x0200 /* b9: Buffer not ready interrupt */ +#define BRDY 0x0100 /* b8: Buffer ready interrupt */ +#define VBSTS 0x0080 /* b7: VBUS input port */ +#define DVSQ 0x0070 /* b6-4: Device state */ +#define DS_SPD_CNFG 0x0070 /* Suspend Configured */ +#define DS_SPD_ADDR 0x0060 /* Suspend Address */ +#define DS_SPD_DFLT 0x0050 /* Suspend Default */ +#define DS_SPD_POWR 0x0040 /* Suspend Powered */ +#define DS_SUSP 0x0040 /* Suspend */ +#define DS_CNFG 0x0030 /* Configured */ +#define DS_ADDS 0x0020 /* Address */ +#define DS_DFLT 0x0010 /* Default */ +#define DS_POWR 0x0000 /* Powered */ +#define DVSQS 0x0030 /* b5-4: Device state */ +#define VALID 0x0008 /* b3: Setup packet detected flag */ +#define CTSQ 0x0007 /* b2-0: Control transfer stage */ +#define CS_SQER 0x0006 /* Sequence error */ +#define CS_WRND 0x0005 /* Control write nodata status stage */ +#define CS_WRSS 0x0004 /* Control write status stage */ +#define CS_WRDS 0x0003 /* Control write data stage */ +#define CS_RDSS 0x0002 /* Control read status stage */ +#define CS_RDDS 0x0001 /* Control read data stage */ +#define CS_IDST 0x0000 /* Idle or setup stage */ + +/* Interrupt Status Register 1 */ +#define OVRCR 0x8000 /* b15: Over-current interrupt */ +#define BCHG 0x4000 /* b14: USB bus chenge interrupt */ +#define DTCH 0x1000 /* b12: Detach sense interrupt */ +#define ATTCH 0x0800 /* b11: Attach sense interrupt */ +#define EOFERR 0x0040 /* b6: EOF-error interrupt */ +#define SIGN 0x0020 /* b5: Setup ignore interrupt */ +#define SACK 0x0010 /* b4: Setup acknowledge interrupt */ + +/* Frame Number Register */ +#define OVRN 0x8000 /* b15: Overrun error */ +#define CRCE 0x4000 /* b14: Received data error */ +#define FRNM 0x07FF /* b10-0: Frame number */ + +/* Micro Frame Number Register */ +#define UFRNM 0x0007 /* b2-0: Micro frame number */ + +/* USB Address / Low Power Status Recovery Register */ +//#define USBADDR 0x007F /* b6-0: USB address */ + +/* Default Control Pipe Maxpacket Size Register */ +/* Pipe Maxpacket Size Register */ +#define DEVSEL 0xF000 /* b15-14: Device address select */ +#define MAXP 0x007F /* b6-0: Maxpacket size of default control pipe */ + +/* Default Control Pipe Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define SUREQ 0x4000 /* b14: Send USB request */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define SUREQCLR 0x0800 /* b11: stop setup request */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PINGE 0x0010 /* b4: ping enable */ +#define CCPL 0x0004 /* b2: Enable control transfer complete */ +#define PID 0x0003 /* b1-0: Response PID */ +#define PID_STALL11 0x0003 /* STALL */ +#define PID_STALL 0x0002 /* STALL */ +#define PID_BUF 0x0001 /* BUF */ +#define PID_NAK 0x0000 /* NAK */ + +/* Pipe Window Select Register */ +#define PIPENM 0x0007 /* b2-0: Pipe select */ + +/* Pipe Configuration Register */ +#define R8A66597_TYP 0xC000 /* b15-14: Transfer type */ +#define R8A66597_ISO 0xC000 /* Isochronous */ +#define R8A66597_INT 0x8000 /* Interrupt */ +#define R8A66597_BULK 0x4000 /* Bulk */ +#define R8A66597_BFRE 0x0400 /* b10: Buffer ready interrupt mode select */ +#define R8A66597_DBLB 0x0200 /* b9: Double buffer mode select */ +#define R8A66597_CNTMD 0x0100 /* b8: Continuous transfer mode select */ +#define R8A66597_SHTNAK 0x0080 /* b7: Transfer end NAK */ +#define R8A66597_DIR 0x0010 /* b4: Transfer direction select */ +#define R8A66597_EPNUM 0x000F /* b3-0: Eendpoint number select */ + +/* Pipe Buffer Configuration Register */ +#define BUFSIZE 0x7C00 /* b14-10: Pipe buffer size */ +#define BUFNMB 0x007F /* b6-0: Pipe buffer number */ +#define PIPE0BUF 256 +#define PIPExBUF 64 + +/* Pipe Maxpacket Size Register */ +#define MXPS 0x07FF /* b10-0: Maxpacket size */ + +/* Pipe Cycle Configuration Register */ +#define IFIS 0x1000 /* b12: Isochronous in-buffer flush mode select */ +#define IITV 0x0007 /* b2-0: Isochronous interval */ + +/* Pipex Control Register */ +#define BSTS 0x8000 /* b15: Buffer status */ +#define INBUFM 0x4000 /* b14: IN buffer monitor (Only for PIPE1 to 5) */ +#define CSCLR 0x2000 /* b13: complete-split status clear */ +#define CSSTS 0x1000 /* b12: complete-split status */ +#define ATREPM 0x0400 /* b10: Auto repeat mode */ +#define ACLRM 0x0200 /* b9: Out buffer auto clear mode */ +#define SQCLR 0x0100 /* b8: Sequence toggle bit clear */ +#define SQSET 0x0080 /* b7: Sequence toggle bit set */ +#define SQMON 0x0040 /* b6: Sequence toggle bit monitor */ +#define PBUSY 0x0020 /* b5: pipe busy */ +#define PID 0x0003 /* b1-0: Response PID */ + +/* PIPExTRE */ +#define TRENB 0x0200 /* b9: Transaction counter enable */ +#define TRCLR 0x0100 /* b8: Transaction counter clear */ + +/* PIPExTRN */ +#define TRNCNT 0xFFFF /* b15-0: Transaction counter */ + +/* DEVADDx */ +#define UPPHUB 0x7800 +#define HUBPORT 0x0700 +#define USBSPD 0x00C0 +#define RTPORT 0x0001 + +#define R8A66597_MAX_NUM_PIPE 10 +#define R8A66597_BUF_BSIZE 8 +#define R8A66597_MAX_DEVICE 10 +#define R8A66597_MAX_ROOT_HUB 2 +#define R8A66597_MAX_SAMPLING 10 +#define R8A66597_MAX_DMA_CHANNEL 2 +#define R8A66597_PIPE_NO_DMA R8A66597_MAX_DMA_CHANNEL +#define check_bulk_or_isoc(pipenum) ((pipenum >= 1 && pipenum <= 5)) +#define check_interrupt(pipenum) ((pipenum >= 6 && pipenum <= 9)) +#define make_devsel(addr) (addr << 12) + +struct r8a66597_pipe_info { + u16 pipenum; + u16 address; /* R8A66597 HCD usb addres */ + u16 epnum; + u16 maxpacket; + u16 type; + u16 bufnum; + u16 buf_bsize; + u16 interval; + u16 dir_in; +}; + +struct r8a66597_pipe { + struct r8a66597_pipe_info info; + + unsigned long fifoaddr; + unsigned long fifosel; + unsigned long fifoctr; + unsigned long pipectr; + unsigned long pipetre; + unsigned long pipetrn; +}; + +struct r8a66597_td { + struct r8a66597_pipe *pipe; + struct urb *urb; + struct list_head queue; + + u16 type; + u16 pipenum; + int iso_cnt; + + u16 address; /* R8A66597's USB address */ + u16 maxpacket; + + unsigned zero_packet:1; + unsigned short_packet:1; + unsigned set_address:1; +}; + +struct r8a66597_device { + u16 address; /* R8A66597's USB address */ + u16 hub_port; + u16 root_port; + + unsigned short ep_in_toggle; + unsigned short ep_out_toggle; + unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE]; + unsigned char dma_map; + + enum usb_device_state state; + + struct usb_device *udev; + int usb_address; + struct list_head device_list; +}; + +struct r8a66597_root_hub { + u32 port; + u16 old_syssts; + int scount; + + struct r8a66597_device *dev; +}; + +struct r8a66597 { + spinlock_t lock; + unsigned long reg; + + struct r8a66597_device device0; + struct r8a66597_root_hub root_hub[R8A66597_MAX_ROOT_HUB]; + struct list_head pipe_queue[R8A66597_MAX_NUM_PIPE]; + + struct timer_list rh_timer; + struct timer_list td_timer[R8A66597_MAX_NUM_PIPE]; + + unsigned short address_map; + unsigned short timeout_map; + unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE]; + unsigned char dma_map; + + struct list_head child_device; + unsigned long child_connect_map[4]; +}; + +static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd) +{ + return (struct r8a66597 *)(hcd->hcd_priv); +} + +static inline struct usb_hcd *r8a66597_to_hcd(struct r8a66597 *r8a66597) +{ + return container_of((void *)r8a66597, struct usb_hcd, hcd_priv); +} + +static inline struct r8a66597_td *r8a66597_get_td(struct r8a66597 *r8a66597, + u16 pipenum) +{ + if (unlikely(list_empty(&r8a66597->pipe_queue[pipenum]))) + return NULL; + + return list_entry(r8a66597->pipe_queue[pipenum].next, + struct r8a66597_td, queue); +} + +static inline struct urb *r8a66597_get_urb(struct r8a66597 *r8a66597, + u16 pipenum) +{ + struct r8a66597_td *td; + + td = r8a66597_get_td(r8a66597, pipenum); + return (td ? td->urb : NULL); +} + +static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) +{ + return inw(r8a66597->reg + offset); +} + +static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, + unsigned long offset, u16 *buf, + int len) +{ + len = (len + 1) / 2; + insw(r8a66597->reg + offset, buf, len); +} + +static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, + unsigned long offset) +{ + outw(val, r8a66597->reg + offset); +} + +static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, + unsigned long offset, u16 *buf, + int len) +{ + unsigned long fifoaddr = r8a66597->reg + offset; + int odd = len & 0x0001; + + len = len / 2; + outsw(fifoaddr, buf, len); + if (unlikely(odd)) { + buf = &buf[len]; + outb((unsigned char)*buf, fifoaddr); + } +} + +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + +static inline unsigned long get_syscfg_reg(int port) +{ + return port == 0 ? SYSCFG0 : SYSCFG1; +} + +static inline unsigned long get_syssts_reg(int port) +{ + return port == 0 ? SYSSTS0 : SYSSTS1; +} + +static inline unsigned long get_dvstctr_reg(int port) +{ + return port == 0 ? DVSTCTR0 : DVSTCTR1; +} + +static inline unsigned long get_intenb_reg(int port) +{ + return port == 0 ? INTENB1 : INTENB2; +} + +static inline unsigned long get_intsts_reg(int port) +{ + return port == 0 ? INTSTS1 : INTSTS2; +} + +static inline u16 get_rh_usb_speed(struct r8a66597 *r8a66597, int port) +{ + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + return r8a66597_read(r8a66597, dvstctr_reg) & RHST; +} + +static inline void r8a66597_port_power(struct r8a66597 *r8a66597, int port, + int power) +{ + unsigned long dvstctr_reg = get_dvstctr_reg(port); + + if (power) + r8a66597_bset(r8a66597, VBOUT, dvstctr_reg); + else + r8a66597_bclr(r8a66597, VBOUT, dvstctr_reg); +} + +#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) +#define get_devadd_addr(address) (DEVADD0 + address * 2) + +#define enable_irq_ready(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, BRDYENB) +#define disable_irq_ready(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, BRDYENB) +#define enable_irq_empty(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, BEMPENB) +#define disable_irq_empty(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, BEMPENB) +#define enable_irq_nrdy(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, NRDYENB) +#define disable_irq_nrdy(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, NRDYENB) + +#endif /* __R8A66597_H__ */ + diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index d22da26..76c555a 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -730,10 +730,9 @@ static int uhci_rh_resume(struct usb_hcd *hcd) int rc = 0; spin_lock_irq(&uhci->lock); - if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { - dev_warn(&hcd->self.root_hub->dev, "HC isn't running!\n"); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) rc = -ESHUTDOWN; - } else if (!uhci->dead) + else if (!uhci->dead) wakeup_rh(uhci); spin_unlock_irq(&uhci->lock); return rc; diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 77145f9..d72c42e 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -108,8 +108,6 @@ struct adu_device { struct urb* interrupt_out_urb; }; -/* prevent races between open() and disconnect */ -static DEFINE_MUTEX(disconnect_mutex); static struct usb_driver adu_driver; static void adu_debug_data(int level, const char *function, int size, @@ -256,8 +254,6 @@ static int adu_open(struct inode *inode, struct file *file) subminor = iminor(inode); - mutex_lock(&disconnect_mutex); - interface = usb_find_interface(&adu_driver, subminor); if (!interface) { err("%s - error, can't find device for minor %d", @@ -306,7 +302,6 @@ static int adu_open(struct inode *inode, struct file *file) up(&dev->sem); exit_no_device: - mutex_unlock(&disconnect_mutex); dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval); return retval; @@ -318,12 +313,6 @@ static int adu_release_internal(struct adu_device *dev) dbg(2," %s : enter", __FUNCTION__); - if (dev->udev == NULL) { - /* the device was unplugged before the file was released */ - adu_delete(dev); - goto exit; - } - /* decrement our usage count for the device */ --dev->open_count; dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); @@ -332,7 +321,6 @@ static int adu_release_internal(struct adu_device *dev) dev->open_count = 0; } -exit: dbg(2," %s : leave", __FUNCTION__); return retval; } @@ -367,8 +355,15 @@ static int adu_release(struct inode *inode, struct file *file) goto exit; } - /* do the work */ - retval = adu_release_internal(dev); + if (dev->udev == NULL) { + /* the device was unplugged before the file was released */ + up(&dev->sem); + adu_delete(dev); + dev = NULL; + } else { + /* do the work */ + retval = adu_release_internal(dev); + } exit: if (dev) @@ -831,19 +826,17 @@ static void adu_disconnect(struct usb_interface *interface) dbg(2," %s : enter", __FUNCTION__); - mutex_lock(&disconnect_mutex); /* not interruptible */ - dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); - down(&dev->sem); /* not interruptible */ - minor = dev->minor; /* give back our minor */ usb_deregister_dev(interface, &adu_class); dev->minor = 0; + down(&dev->sem); /* not interruptible */ + /* if the device is not opened, then we clean up right now */ dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); if (!dev->open_count) { @@ -854,8 +847,6 @@ static void adu_disconnect(struct usb_interface *interface) up(&dev->sem); } - mutex_unlock(&disconnect_mutex); - dev_info(&interface->dev, "ADU device adutux%d now disconnected", (minor - ADU_MINOR_BASE)); diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index cac1500..1fd5fc2 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -2034,12 +2034,12 @@ static void auerswald_disconnect (struct usb_interface *intf) if (!cp) return; - down (&cp->mutex); - info ("device /dev/%s now disconnecting", cp->name); - /* give back our USB minor number */ usb_deregister_dev(intf, &auerswald_class); + down (&cp->mutex); + info ("device /dev/%s now disconnecting", cp->name); + /* Stop the interrupt endpoint */ auerswald_int_release (cp); diff --git a/drivers/usb/misc/berry_charge.c b/drivers/usb/misc/berry_charge.c index b15f2fd..92c1d27 100644 --- a/drivers/usb/misc/berry_charge.c +++ b/drivers/usb/misc/berry_charge.c @@ -26,8 +26,11 @@ #define RIM_VENDOR 0x0fca #define BLACKBERRY 0x0001 +#define BLACKBERRY_PEARL_DUAL 0x0004 +#define BLACKBERRY_PEARL 0x0006 static int debug; +static int pearl_dual_mode = 1; #ifdef dbg #undef dbg @@ -38,6 +41,8 @@ static int debug; static struct usb_device_id id_table [] = { { USB_DEVICE(RIM_VENDOR, BLACKBERRY) }, + { USB_DEVICE(RIM_VENDOR, BLACKBERRY_PEARL) }, + { USB_DEVICE(RIM_VENDOR, BLACKBERRY_PEARL_DUAL) }, { }, /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); @@ -86,6 +91,30 @@ static int magic_charge(struct usb_device *udev) return retval; } +static int magic_dual_mode(struct usb_device *udev) +{ + char *dummy_buffer = kzalloc(2, GFP_KERNEL); + int retval; + + if (!dummy_buffer) + return -ENOMEM; + + /* send magic command so that the Blackberry Pearl device exposes + * two interfaces: both the USB mass-storage one and one which can + * be used for database access. */ + dbg(&udev->dev, "Sending magic pearl command\n"); + retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0xa9, 0xc0, 1, 1, dummy_buffer, 2, 100); + dbg(&udev->dev, "Magic pearl command returned %d\n", retval); + + dbg(&udev->dev, "Calling set_configuration\n"); + retval = usb_driver_set_configuration(udev, 1); + if (retval) + dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval); + + return retval; +} + static int berry_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -105,6 +134,10 @@ static int berry_probe(struct usb_interface *intf, /* turn the power on */ magic_charge(udev); + if ((le16_to_cpu(udev->descriptor.idProduct) == BLACKBERRY_PEARL) && + (pearl_dual_mode)) + magic_dual_mode(udev); + /* we don't really want to bind to the device, userspace programs can * handle the syncing just fine, so get outta here. */ return -ENODEV; @@ -138,3 +171,5 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug enabled or not"); +module_param(pearl_dual_mode, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(pearl_dual_mode, "Change Blackberry Pearl to run in dual mode"); diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 8d0e360..e6fd024 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -119,9 +119,6 @@ static struct usb_driver idmouse_driver = { .id_table = idmouse_table, }; -/* prevent races between open() and disconnect() */ -static DEFINE_MUTEX(disconnect_mutex); - static int idmouse_create_image(struct usb_idmouse *dev) { int bytes_read; @@ -211,21 +208,15 @@ static int idmouse_open(struct inode *inode, struct file *file) struct usb_interface *interface; int result; - /* prevent disconnects */ - mutex_lock(&disconnect_mutex); - /* get the interface from minor number and driver information */ interface = usb_find_interface (&idmouse_driver, iminor (inode)); - if (!interface) { - mutex_unlock(&disconnect_mutex); + if (!interface) return -ENODEV; - } + /* get the device information block from the interface */ dev = usb_get_intfdata(interface); - if (!dev) { - mutex_unlock(&disconnect_mutex); + if (!dev) return -ENODEV; - } /* lock this device */ down(&dev->sem); @@ -255,9 +246,6 @@ error: /* unlock this device */ up(&dev->sem); - - /* unlock the disconnect semaphore */ - mutex_unlock(&disconnect_mutex); return result; } @@ -265,15 +253,10 @@ static int idmouse_release(struct inode *inode, struct file *file) { struct usb_idmouse *dev; - /* prevent a race condition with open() */ - mutex_lock(&disconnect_mutex); - dev = file->private_data; - if (dev == NULL) { - mutex_unlock(&disconnect_mutex); + if (dev == NULL) return -ENODEV; - } /* lock our device */ down(&dev->sem); @@ -281,7 +264,6 @@ static int idmouse_release(struct inode *inode, struct file *file) /* are we really open? */ if (dev->open <= 0) { up(&dev->sem); - mutex_unlock(&disconnect_mutex); return -ENODEV; } @@ -291,12 +273,9 @@ static int idmouse_release(struct inode *inode, struct file *file) /* the device was unplugged before the file was released */ up(&dev->sem); idmouse_delete(dev); - mutex_unlock(&disconnect_mutex); - return 0; + } else { + up(&dev->sem); } - - up(&dev->sem); - mutex_unlock(&disconnect_mutex); return 0; } @@ -391,30 +370,27 @@ static void idmouse_disconnect(struct usb_interface *interface) { struct usb_idmouse *dev; - /* prevent races with open() */ - mutex_lock(&disconnect_mutex); - /* get device structure */ dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); - /* lock it */ - down(&dev->sem); - /* give back our minor */ usb_deregister_dev(interface, &idmouse_class); + /* lock it */ + down(&dev->sem); + /* prevent device read, write and ioctl */ dev->present = 0; - /* unlock */ - up(&dev->sem); - /* if the device is opened, idmouse_release will clean this up */ - if (!dev->open) + if (!dev->open) { + up(&dev->sem); idmouse_delete(dev); - - mutex_unlock(&disconnect_mutex); + } else { + /* unlock */ + up(&dev->sem); + } info("%s disconnected", DRIVER_DESC); } diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 3bb33f7..28548d1 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -100,8 +100,6 @@ struct iowarrior { /*--------------*/ /* globals */ /*--------------*/ -/* prevent races between open() and disconnect() */ -static DECLARE_MUTEX(disconnect_sem); /* * USB spec identifies 5 second timeouts. @@ -600,22 +598,18 @@ static int iowarrior_open(struct inode *inode, struct file *file) subminor = iminor(inode); - /* prevent disconnects */ - down(&disconnect_sem); - interface = usb_find_interface(&iowarrior_driver, subminor); if (!interface) { err("%s - error, can't find device for minor %d", __FUNCTION__, subminor); - retval = -ENODEV; - goto out; + return -ENODEV; } dev = usb_get_intfdata(interface); - if (!dev) { - retval = -ENODEV; - goto out; - } + if (!dev) + return -ENODEV; + + mutex_lock(&dev->mutex); /* Only one process can open each device, no sharing. */ if (dev->opened) { @@ -636,7 +630,7 @@ static int iowarrior_open(struct inode *inode, struct file *file) retval = 0; out: - up(&disconnect_sem); + mutex_unlock(&dev->mutex); return retval; } @@ -868,19 +862,16 @@ static void iowarrior_disconnect(struct usb_interface *interface) struct iowarrior *dev; int minor; - /* prevent races with open() */ - down(&disconnect_sem); - dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); - mutex_lock(&dev->mutex); - minor = dev->minor; /* give back our minor */ usb_deregister_dev(interface, &iowarrior_class); + mutex_lock(&dev->mutex); + /* prevent device read, write and ioctl */ dev->present = 0; @@ -898,7 +889,6 @@ static void iowarrior_disconnect(struct usb_interface *interface) /* no process is using the device, cleanup now */ iowarrior_delete(dev); } - up(&disconnect_sem); dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n", minor - IOWARRIOR_MINOR_BASE); diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 7bad494..5e950b9 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -176,9 +176,6 @@ struct ld_usb { int interrupt_out_busy; }; -/* prevent races between open() and disconnect() */ -static DEFINE_MUTEX(disconnect_mutex); - static struct usb_driver ld_usb_driver; /** @@ -298,35 +295,28 @@ static int ld_usb_open(struct inode *inode, struct file *file) { struct ld_usb *dev; int subminor; - int retval = 0; + int retval; struct usb_interface *interface; nonseekable_open(inode, file); subminor = iminor(inode); - mutex_lock(&disconnect_mutex); - interface = usb_find_interface(&ld_usb_driver, subminor); if (!interface) { err("%s - error, can't find device for minor %d\n", __FUNCTION__, subminor); - retval = -ENODEV; - goto unlock_disconnect_exit; + return -ENODEV; } dev = usb_get_intfdata(interface); - if (!dev) { - retval = -ENODEV; - goto unlock_disconnect_exit; - } + if (!dev) + return -ENODEV; /* lock this device */ - if (down_interruptible(&dev->sem)) { - retval = -ERESTARTSYS; - goto unlock_disconnect_exit; - } + if (down_interruptible(&dev->sem)) + return -ERESTARTSYS; /* allow opening only once */ if (dev->open_count) { @@ -366,9 +356,6 @@ static int ld_usb_open(struct inode *inode, struct file *file) unlock_exit: up(&dev->sem); -unlock_disconnect_exit: - mutex_unlock(&disconnect_mutex); - return retval; } @@ -766,18 +753,16 @@ static void ld_usb_disconnect(struct usb_interface *intf) struct ld_usb *dev; int minor; - mutex_lock(&disconnect_mutex); - dev = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); - down(&dev->sem); - minor = intf->minor; /* give back our minor */ usb_deregister_dev(intf, &ld_usb_class); + down(&dev->sem); + /* if the device is not opened, then we clean up right now */ if (!dev->open_count) { up(&dev->sem); @@ -787,8 +772,6 @@ static void ld_usb_disconnect(struct usb_interface *intf) up(&dev->sem); } - mutex_unlock(&disconnect_mutex); - dev_info(&intf->dev, "LD USB Device #%d now disconnected\n", (minor - USB_LD_MINOR_BASE)); } diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 1713e19..2ed0dae 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -254,9 +254,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_devic static void tower_disconnect (struct usb_interface *interface); -/* prevent races between open() and disconnect */ -static DEFINE_MUTEX (disconnect_mutex); - /* file operations needed when we register this driver */ static const struct file_operations tower_fops = { .owner = THIS_MODULE, @@ -344,28 +341,26 @@ static int tower_open (struct inode *inode, struct file *file) nonseekable_open(inode, file); subminor = iminor(inode); - mutex_lock (&disconnect_mutex); - interface = usb_find_interface (&tower_driver, subminor); if (!interface) { err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; - goto unlock_disconnect_exit; + goto exit; } dev = usb_get_intfdata(interface); if (!dev) { retval = -ENODEV; - goto unlock_disconnect_exit; + goto exit; } /* lock this device */ if (down_interruptible (&dev->sem)) { retval = -ERESTARTSYS; - goto unlock_disconnect_exit; + goto exit; } /* allow opening only once */ @@ -421,9 +416,7 @@ static int tower_open (struct inode *inode, struct file *file) unlock_exit: up (&dev->sem); -unlock_disconnect_exit: - mutex_unlock (&disconnect_mutex); - +exit: dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval); return retval; @@ -993,19 +986,16 @@ static void tower_disconnect (struct usb_interface *interface) dbg(2, "%s: enter", __FUNCTION__); - mutex_lock (&disconnect_mutex); - dev = usb_get_intfdata (interface); usb_set_intfdata (interface, NULL); - - down (&dev->sem); - minor = dev->minor; /* give back our minor */ usb_deregister_dev (interface, &tower_class); + down (&dev->sem); + /* if the device is not opened, then we clean up right now */ if (!dev->open_count) { up (&dev->sem); @@ -1015,8 +1005,6 @@ static void tower_disconnect (struct usb_interface *interface) up (&dev->sem); } - mutex_unlock (&disconnect_mutex); - info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); dbg(2, "%s: leave", __FUNCTION__); diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 6f8b134..9f37ba4 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -72,8 +72,6 @@ MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES static struct usb_driver sisusb_driver; -DEFINE_MUTEX(disconnect_mutex); - static void sisusb_free_buffers(struct sisusb_usb_data *sisusb) { @@ -2511,31 +2509,24 @@ sisusb_open(struct inode *inode, struct file *file) struct usb_interface *interface; int subminor = iminor(inode); - mutex_lock(&disconnect_mutex); - if (!(interface = usb_find_interface(&sisusb_driver, subminor))) { printk(KERN_ERR "sisusb[%d]: Failed to find interface\n", subminor); - mutex_unlock(&disconnect_mutex); return -ENODEV; } - if (!(sisusb = usb_get_intfdata(interface))) { - mutex_unlock(&disconnect_mutex); + if (!(sisusb = usb_get_intfdata(interface))) return -ENODEV; - } mutex_lock(&sisusb->lock); if (!sisusb->present || !sisusb->ready) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); return -ENODEV; } if (sisusb->isopen) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); return -EBUSY; } @@ -2543,7 +2534,6 @@ sisusb_open(struct inode *inode, struct file *file) if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) { if (sisusb_init_gfxdevice(sisusb, 0)) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); printk(KERN_ERR "sisusbvga[%d]: Failed to initialize " "device\n", @@ -2552,7 +2542,6 @@ sisusb_open(struct inode *inode, struct file *file) } } else { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); printk(KERN_ERR "sisusbvga[%d]: Device not attached to " "USB 2.0 hub\n", @@ -2570,8 +2559,6 @@ sisusb_open(struct inode *inode, struct file *file) mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); - return 0; } @@ -2601,12 +2588,8 @@ sisusb_release(struct inode *inode, struct file *file) struct sisusb_usb_data *sisusb; int myminor; - mutex_lock(&disconnect_mutex); - - if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) { - mutex_unlock(&disconnect_mutex); + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) return -ENODEV; - } mutex_lock(&sisusb->lock); @@ -2626,8 +2609,6 @@ sisusb_release(struct inode *inode, struct file *file) /* decrement the usage count on our device */ kref_put(&sisusb->kref, sisusb_delete); - mutex_unlock(&disconnect_mutex); - return 0; } @@ -3383,12 +3364,9 @@ static void sisusb_disconnect(struct usb_interface *intf) sisusb_console_exit(sisusb); #endif - /* The above code doesn't need the disconnect - * semaphore to be down; its meaning is to - * protect all other routines from the disconnect - * case, not the other way round. - */ - mutex_lock(&disconnect_mutex); + minor = sisusb->minor; + + usb_deregister_dev(intf, &usb_sisusb_class); mutex_lock(&sisusb->lock); @@ -3396,12 +3374,8 @@ static void sisusb_disconnect(struct usb_interface *intf) if (!sisusb_wait_all_out_complete(sisusb)) sisusb_kill_all_busy(sisusb); - minor = sisusb->minor; - usb_set_intfdata(intf, NULL); - usb_deregister_dev(intf, &usb_sisusb_class); - #ifdef SISUSB_OLD_CONFIG_COMPAT if (sisusb->ioctl32registered) { int ret; @@ -3426,8 +3400,6 @@ static void sisusb_disconnect(struct usb_interface *intf) /* decrement our usage count */ kref_put(&sisusb->kref, sisusb_delete); - mutex_unlock(&disconnect_mutex); - printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor); } diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index 5947afb..8d0edc8 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -214,18 +214,13 @@ sisusbcon_init(struct vc_data *c, int init) * are set up/restored. */ - mutex_lock(&disconnect_mutex); - - if (!(sisusb = sisusb_get_sisusb(c->vc_num))) { - mutex_unlock(&disconnect_mutex); + if (!(sisusb = sisusb_get_sisusb(c->vc_num))) return; - } mutex_lock(&sisusb->lock); if (!sisusb_sisusb_valid(sisusb)) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); return; } @@ -264,8 +259,6 @@ sisusbcon_init(struct vc_data *c, int init) mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); - if (init) { c->vc_cols = cols; c->vc_rows = rows; @@ -284,12 +277,8 @@ sisusbcon_deinit(struct vc_data *c) * and others, ie not under our control. */ - mutex_lock(&disconnect_mutex); - - if (!(sisusb = sisusb_get_sisusb(c->vc_num))) { - mutex_unlock(&disconnect_mutex); + if (!(sisusb = sisusb_get_sisusb(c->vc_num))) return; - } mutex_lock(&sisusb->lock); @@ -314,8 +303,6 @@ sisusbcon_deinit(struct vc_data *c) /* decrement the usage count on our sisusb */ kref_put(&sisusb->kref, sisusb_delete); - - mutex_unlock(&disconnect_mutex); } /* interface routine */ @@ -1490,14 +1477,11 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) { int i, ret, minor = sisusb->minor; - mutex_lock(&disconnect_mutex); - mutex_lock(&sisusb->lock); /* Erm.. that should not happen */ if (sisusb->haveconsole || !sisusb->SiS_Pr) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); return 1; } @@ -1508,14 +1492,12 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) first > MAX_NR_CONSOLES || last > MAX_NR_CONSOLES) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); return 1; } /* If gfxcore not initialized or no consoles given, quit graciously */ if (!sisusb->gfxinit || first < 1 || last < 1) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); return 0; } @@ -1526,7 +1508,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) /* Set up text mode (and upload default font) */ if (sisusb_reset_text_mode(sisusb, 1)) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); printk(KERN_ERR "sisusbvga[%d]: Failed to set up text mode\n", minor); @@ -1550,7 +1531,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) /* Allocate screen buffer */ if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) { mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); printk(KERN_ERR "sisusbvga[%d]: Failed to allocate screen buffer\n", minor); @@ -1558,7 +1538,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) } mutex_unlock(&sisusb->lock); - mutex_unlock(&disconnect_mutex); /* Now grab the desired console(s) */ ret = take_over_console(&sisusb_con, first - 1, last - 1, 0); diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.h b/drivers/usb/misc/sisusbvga/sisusb_init.h index f05f832..864bc0e 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_init.h +++ b/drivers/usb/misc/sisusbvga/sisusb_init.h @@ -808,8 +808,6 @@ static const struct SiS_VCLKData SiSUSB_VCLKData[] = { 0x2b,0xc2, 35} /* 0x71 768@576@60 */ }; -extern struct mutex disconnect_mutex; - int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo); int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo); diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 12bad8a..504f722 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -45,13 +45,13 @@ struct usb_lcd { struct kref kref; struct semaphore limit_sem; /* to stop writes at full throttle from * using up all RAM */ + struct usb_anchor submitted; /* URBs to wait for before suspend */ }; #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) #define USB_LCD_CONCURRENT_WRITES 5 static struct usb_driver lcd_driver; -static DEFINE_MUTEX(usb_lcd_open_mutex); static void lcd_delete(struct kref *kref) @@ -68,35 +68,35 @@ static int lcd_open(struct inode *inode, struct file *file) { struct usb_lcd *dev; struct usb_interface *interface; - int subminor; - int retval = 0; + int subminor, r; subminor = iminor(inode); - mutex_lock(&usb_lcd_open_mutex); interface = usb_find_interface(&lcd_driver, subminor); if (!interface) { err ("USBLCD: %s - error, can't find device for minor %d", __FUNCTION__, subminor); - retval = -ENODEV; - goto exit; + return -ENODEV; } dev = usb_get_intfdata(interface); - if (!dev) { - retval = -ENODEV; - goto exit; - } + if (!dev) + return -ENODEV; /* increment our usage count for the device */ kref_get(&dev->kref); + /* grab a power reference */ + r = usb_autopm_get_interface(interface); + if (r < 0) { + kref_put(&dev->kref, lcd_delete); + return r; + } + /* save our object in the file's private structure */ file->private_data = dev; -exit: - mutex_unlock(&usb_lcd_open_mutex); - return retval; + return 0; } static int lcd_release(struct inode *inode, struct file *file) @@ -108,6 +108,7 @@ static int lcd_release(struct inode *inode, struct file *file) return -ENODEV; /* decrement the count on our device */ + usb_autopm_put_interface(dev->interface); kref_put(&dev->kref, lcd_delete); return 0; } @@ -233,12 +234,14 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, count, lcd_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + usb_anchor_urb(urb, &dev->submitted); /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { err("USBLCD: %s - failed submitting write urb, error %d", __FUNCTION__, retval); - goto error; + goto error_unanchor; } /* release our reference to this urb, the USB core will eventually free it entirely */ @@ -246,7 +249,8 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz exit: return count; - +error_unanchor: + usb_unanchor_urb(urb); error: usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); usb_free_urb(urb); @@ -291,6 +295,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id } kref_init(&dev->kref); sema_init(&dev->limit_sem, USB_LCD_CONCURRENT_WRITES); + init_usb_anchor(&dev->submitted); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; @@ -358,22 +363,41 @@ error: return retval; } +static void lcd_draw_down(struct usb_lcd *dev) +{ + int time; + + time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); + if (!time) + usb_kill_anchored_urbs(&dev->submitted); +} + +static int lcd_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_lcd *dev = usb_get_intfdata(intf); + + if (!dev) + return 0; + lcd_draw_down(dev); + return 0; +} + +static int lcd_resume (struct usb_interface *intf) +{ + return 0; +} + static void lcd_disconnect(struct usb_interface *interface) { struct usb_lcd *dev; int minor = interface->minor; - /* prevent skel_open() from racing skel_disconnect() */ - mutex_lock(&usb_lcd_open_mutex); - dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); /* give back our minor */ usb_deregister_dev(interface, &lcd_class); - mutex_unlock(&usb_lcd_open_mutex); - /* decrement our usage count */ kref_put(&dev->kref, lcd_delete); @@ -384,7 +408,10 @@ static struct usb_driver lcd_driver = { .name = "usblcd", .probe = lcd_probe, .disconnect = lcd_disconnect, + .suspend = lcd_suspend, + .resume = lcd_resume, .id_table = id_table, + .supports_autosuspend = 1, }; static int __init usb_lcd_init(void) diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 0af11a6..c03dfd7 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -4,7 +4,7 @@ * This is a binary format reader. * * Copyright (C) 2006 Paolo Abeni (paolo.abeni@email.it) - * Copyright (C) 2006 Pete Zaitcev (zaitcev@redhat.com) + * Copyright (C) 2006,2007 Pete Zaitcev (zaitcev@redhat.com) */ #include <linux/kernel.h> @@ -172,6 +172,7 @@ static inline struct mon_bin_hdr *MON_OFF2HDR(const struct mon_reader_bin *rp, #define MON_RING_EMPTY(rp) ((rp)->b_cnt == 0) +static struct class *mon_bin_class; static dev_t mon_bin_dev0; static struct cdev mon_bin_cdev; @@ -1144,10 +1145,38 @@ static void mon_free_buff(struct mon_pgmap *map, int npages) free_page((unsigned long) map[n].ptr); } +int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus) +{ + struct device *dev; + unsigned minor = ubus? ubus->busnum: 0; + + if (minor >= MON_BIN_MAX_MINOR) + return 0; + + dev = device_create(mon_bin_class, ubus? ubus->controller: NULL, + MKDEV(MAJOR(mon_bin_dev0), minor), "usbmon%d", minor); + if (IS_ERR(dev)) + return 0; + + mbus->classdev = dev; + return 1; +} + +void mon_bin_del(struct mon_bus *mbus) +{ + device_destroy(mon_bin_class, mbus->classdev->devt); +} + int __init mon_bin_init(void) { int rc; + mon_bin_class = class_create(THIS_MODULE, "usbmon"); + if (IS_ERR(mon_bin_class)) { + rc = PTR_ERR(mon_bin_class); + goto err_class; + } + rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon"); if (rc < 0) goto err_dev; @@ -1164,6 +1193,8 @@ int __init mon_bin_init(void) err_add: unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); err_dev: + class_destroy(mon_bin_class); +err_class: return rc; } @@ -1171,4 +1202,5 @@ void mon_bin_exit(void) { cdev_del(&mon_bin_cdev); unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); + class_destroy(mon_bin_class); } diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index 8977ec0..ce61d8b 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -220,6 +220,8 @@ static void mon_bus_remove(struct usb_bus *ubus) list_del(&mbus->bus_link); if (mbus->text_inited) mon_text_del(mbus); + if (mbus->bin_inited) + mon_bin_del(mbus); mon_dissolve(mbus, ubus); kref_put(&mbus->ref, mon_bus_drop); @@ -301,8 +303,8 @@ static void mon_bus_init(struct usb_bus *ubus) mbus->u_bus = ubus; ubus->mon_bus = mbus; - mbus->text_inited = mon_text_add(mbus, ubus->busnum); - // mon_bin_add(...) + mbus->text_inited = mon_text_add(mbus, ubus); + mbus->bin_inited = mon_bin_add(mbus, ubus); mutex_lock(&mon_lock); list_add_tail(&mbus->bus_link, &mon_buses); @@ -321,8 +323,8 @@ static void mon_bus0_init(void) spin_lock_init(&mbus->lock); INIT_LIST_HEAD(&mbus->r_list); - mbus->text_inited = mon_text_add(mbus, 0); - // mbus->bin_inited = mon_bin_add(mbus, 0); + mbus->text_inited = mon_text_add(mbus, NULL); + mbus->bin_inited = mon_bin_add(mbus, NULL); } /* @@ -403,6 +405,8 @@ static void __exit mon_exit(void) if (mbus->text_inited) mon_text_del(mbus); + if (mbus->bin_inited) + mon_bin_del(mbus); /* * This never happens, because the open/close paths in @@ -423,6 +427,8 @@ static void __exit mon_exit(void) mbus = &mon_bus0; if (mbus->text_inited) mon_text_del(mbus); + if (mbus->bin_inited) + mon_bin_del(mbus); mutex_unlock(&mon_lock); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index ec0cc51..982b773 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -655,20 +655,24 @@ static const struct file_operations mon_fops_text_u = { .release = mon_text_release, }; -int mon_text_add(struct mon_bus *mbus, int busnum) +int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) { struct dentry *d; enum { NAMESZ = 10 }; char name[NAMESZ]; + int busnum = ubus? ubus->busnum: 0; int rc; - rc = snprintf(name, NAMESZ, "%dt", busnum); - if (rc <= 0 || rc >= NAMESZ) - goto err_print_t; - d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t); - if (d == NULL) - goto err_create_t; - mbus->dent_t = d; + if (ubus != NULL) { + rc = snprintf(name, NAMESZ, "%dt", busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_t; + d = debugfs_create_file(name, 0600, mon_dir, mbus, + &mon_fops_text_t); + if (d == NULL) + goto err_create_t; + mbus->dent_t = d; + } rc = snprintf(name, NAMESZ, "%du", busnum); if (rc <= 0 || rc >= NAMESZ) @@ -694,8 +698,10 @@ err_print_s: mbus->dent_u = NULL; err_create_u: err_print_u: - debugfs_remove(mbus->dent_t); - mbus->dent_t = NULL; + if (ubus != NULL) { + debugfs_remove(mbus->dent_t); + mbus->dent_t = NULL; + } err_create_t: err_print_t: return 0; @@ -704,7 +710,8 @@ err_print_t: void mon_text_del(struct mon_bus *mbus) { debugfs_remove(mbus->dent_u); - debugfs_remove(mbus->dent_t); + if (mbus->dent_t != NULL) + debugfs_remove(mbus->dent_t); debugfs_remove(mbus->dent_s); } diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index 13d6325..f68ad6d 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -20,9 +20,11 @@ struct mon_bus { struct usb_bus *u_bus; int text_inited; + int bin_inited; struct dentry *dent_s; /* Debugging file */ struct dentry *dent_t; /* Text interface file */ struct dentry *dent_u; /* Second text interface file */ + struct device *classdev; /* Device in usbmon class */ /* Ref */ int nreaders; /* Under mon_lock AND mbus->lock */ @@ -52,9 +54,10 @@ 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, int busnum); +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 /*bool*/ mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus); +void mon_bin_del(struct mon_bus *mbus); int __init mon_text_init(void); void mon_text_exit(void); diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 3efe670..43d6db6 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -464,6 +464,16 @@ config USB_SERIAL_PL2303 To compile this driver as a module, choose M here: the module will be called pl2303. +config USB_SERIAL_OTI6858 + tristate "USB Ours Technology Inc. OTi-6858 USB To RS232 Bridge Controller (EXPERIMENTAL)" + depends on USB_SERIAL + help + Say Y here if you want to use the OTi-6858 single port USB to serial + converter device. + + To compile this driver as a module, choose M here: the + module will be called oti6858. + config USB_SERIAL_HP4X tristate "USB HP4x Calculators support" depends on USB_SERIAL diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 61166ad..07a976e 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o obj-$(CONFIG_USB_SERIAL_OPTION) += option.o +obj-$(CONFIG_USB_SERIAL_OTI6858) += oti6858.o obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index fbc8c27..1cd29cd 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -411,12 +411,13 @@ static int aircable_write(struct usb_serial_port *port, static void aircable_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; + int status = urb->status; int result; - dbg("%s - urb->status: %d", __FUNCTION__ , urb->status); + dbg("%s - urb status: %d", __FUNCTION__ , status); /* This has been taken from cypress_m8.c cypress_write_int_callback */ - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -425,14 +426,14 @@ static void aircable_write_bulk_callback(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); port->write_urb_busy = 0; return; default: /* error in the urb, so we have to resubmit it */ dbg("%s - Overflow in write", __FUNCTION__); dbg("%s - nonzero write bulk status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); port->write_urb->transfer_buffer_length = 1; port->write_urb->dev = port->serial->dev; result = usb_submit_urb(port->write_urb, GFP_ATOMIC); @@ -457,16 +458,17 @@ static void aircable_read_bulk_callback(struct urb *urb) unsigned long no_packages, remaining, package_length, i; int result, shift = 0; unsigned char *temp; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - urb status = %d", __FUNCTION__, status); if (!port->open_count) { dbg("%s - port is closed, exiting.", __FUNCTION__); return; } - if (urb->status == -EPROTO) { + if (status == -EPROTO) { dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__); usb_fill_bulk_urb(port->read_urb, port->serial->dev, diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index 39a4983..cff6fd1 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -82,12 +82,13 @@ static void airprime_read_bulk_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; struct tty_struct *tty; int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { + if (status) { dbg("%s - nonzero read bulk status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); return; } usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); @@ -109,6 +110,7 @@ static void airprime_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct airprime_private *priv = usb_get_serial_port_data(port); + int status = urb->status; unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); @@ -116,9 +118,9 @@ static void airprime_write_bulk_callback(struct urb *urb) /* free up the transfer buffer, as usb_free_urb() does not do this */ kfree (urb->transfer_buffer); - if (urb->status) + if (status) dbg("%s - nonzero write bulk status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); spin_lock_irqsave(&priv->lock, flags); --priv->outstanding_urbs; spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index fe43712..c9fd486 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -172,7 +172,7 @@ static void ark3116_set_termios(struct usb_serial_port *port, dbg("%s - port %d", __FUNCTION__, port->number); - if ((!port->tty) || (!port->tty->termios)) { + if (!port->tty || !port->tty->termios) { dbg("%s - no tty structures", __FUNCTION__); return; } @@ -188,16 +188,6 @@ static void ark3116_set_termios(struct usb_serial_port *port, cflag = port->tty->termios->c_cflag; - /* check that they really want us to change something: */ - if (old_termios) { - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(port->tty->termios->c_iflag) == - RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("%s - nothing to change...", __FUNCTION__); - return; - } - } - buf = kmalloc(1, GFP_KERNEL); if (!buf) { dbg("error kmalloc"); @@ -220,7 +210,7 @@ static void ark3116_set_termios(struct usb_serial_port *port, dbg("setting CS7"); break; default: - err("CSIZE was set but not CS5-CS8, using CS8!"); + dbg("CSIZE was set but not CS5-CS8, using CS8!"); /* fall through */ case CS8: config |= 0x03; @@ -251,38 +241,33 @@ static void ark3116_set_termios(struct usb_serial_port *port, } /* set baudrate */ - baud = 0; - switch (cflag & CBAUD) { - case B0: - err("can't set 0 baud, using 9600 instead"); + baud = tty_get_baud_rate(port->tty); + + switch (baud) { + case 75: + case 150: + case 300: + case 600: + case 1200: + case 1800: + case 2400: + case 4800: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + case 230400: + case 460800: break; - case B75: baud = 75; break; - case B150: baud = 150; break; - case B300: baud = 300; break; - case B600: baud = 600; break; - case B1200: baud = 1200; break; - case B1800: baud = 1800; break; - case B2400: baud = 2400; break; - case B4800: baud = 4800; break; - case B9600: baud = 9600; break; - case B19200: baud = 19200; break; - case B38400: baud = 38400; break; - case B57600: baud = 57600; break; - case B115200: baud = 115200; break; - case B230400: baud = 230400; break; - case B460800: baud = 460800; break; + /* set 9600 as default (if given baudrate is invalid for example) */ default: - dbg("does not support the baudrate requested (fix it)"); - break; + baud = 9600; } - /* set 9600 as default (if given baudrate is invalid for example) */ - if (baud == 0) - baud = 9600; - /* * found by try'n'error, be careful, maybe there are other options - * for multiplicator etc! + * for multiplicator etc! (3.5 for example) */ if (baud == 460800) /* strange, for 460800 the formula is wrong diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 3b800d2..e67ce25 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -255,9 +255,10 @@ static void belkin_sa_read_int_callback (struct urb *urb) struct belkin_sa_private *priv; unsigned char *data = urb->transfer_buffer; int retval; + int status = urb->status; unsigned long flags; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -265,10 +266,12 @@ static void belkin_sa_read_int_callback (struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, status); goto exit; } @@ -346,6 +349,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios unsigned long flags; unsigned long control_state; int bad_flow_control; + speed_t baud; if ((!port->tty) || (!port->tty->termios)) { dbg ("%s - no tty or termios structure", __FUNCTION__); @@ -361,16 +365,8 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios bad_flow_control = priv->bad_flow_control; spin_unlock_irqrestore(&priv->lock, flags); - /* check that they really want us to change something */ - if (old_termios) { - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("%s - nothing to change...", __FUNCTION__); - return; - } - old_iflag = old_termios->c_iflag; - old_cflag = old_termios->c_cflag; - } + old_iflag = old_termios->c_iflag; + old_cflag = old_termios->c_cflag; /* Set the baud rate */ if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { @@ -384,38 +380,30 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 1) < 0) err("Set RTS error"); } + } - switch(cflag & CBAUD) { - case B0: /* handled below */ break; - case B300: urb_value = BELKIN_SA_BAUD(300); break; - case B600: urb_value = BELKIN_SA_BAUD(600); break; - case B1200: urb_value = BELKIN_SA_BAUD(1200); break; - case B2400: urb_value = BELKIN_SA_BAUD(2400); break; - case B4800: urb_value = BELKIN_SA_BAUD(4800); break; - case B9600: urb_value = BELKIN_SA_BAUD(9600); break; - case B19200: urb_value = BELKIN_SA_BAUD(19200); break; - case B38400: urb_value = BELKIN_SA_BAUD(38400); break; - case B57600: urb_value = BELKIN_SA_BAUD(57600); break; - case B115200: urb_value = BELKIN_SA_BAUD(115200); break; - case B230400: urb_value = BELKIN_SA_BAUD(230400); break; - default: err("BELKIN USB Serial Adapter: unsupported baudrate request, using default of 9600"); - urb_value = BELKIN_SA_BAUD(9600); break; - } - if ((cflag & CBAUD) != B0 ) { - if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) - err("Set baudrate error"); - } else { - /* Disable flow control */ - if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, BELKIN_SA_FLOW_NONE) < 0) - err("Disable flowcontrol error"); - - /* Drop RTS and DTR */ - control_state &= ~(TIOCM_DTR | TIOCM_RTS); - if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0) - err("DTR LOW error"); - if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0) - err("RTS LOW error"); - } + baud = tty_get_baud_rate(port->tty); + urb_value = BELKIN_SA_BAUD(baud); + /* Clip to maximum speed */ + if (urb_value == 0) + urb_value = 1; + /* Turn it back into a resulting real baud rate */ + baud = BELKIN_SA_BAUD(urb_value); + /* FIXME: Once the tty updates are done then push this back to the tty */ + + if ((cflag & CBAUD) != B0 ) { + if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) + err("Set baudrate error"); + } else { + /* Disable flow control */ + if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, BELKIN_SA_FLOW_NONE) < 0) + err("Disable flowcontrol error"); + /* Drop RTS and DTR */ + control_state &= ~(TIOCM_DTR | TIOCM_RTS); + if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0) + err("DTR LOW error"); + if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0) + err("RTS LOW error"); } /* set the parity */ @@ -435,7 +423,7 @@ static void belkin_sa_set_termios (struct usb_serial_port *port, struct ktermios case CS6: urb_value = BELKIN_SA_DATA_BITS(6); break; case CS7: urb_value = BELKIN_SA_DATA_BITS(7); break; case CS8: urb_value = BELKIN_SA_DATA_BITS(8); break; - default: err("CSIZE was not CS5-CS8, using default of 8"); + default: dbg("CSIZE was not CS5-CS8, using default of 8"); urb_value = BELKIN_SA_DATA_BITS(8); break; } diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 4167753..4353df9 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -305,12 +305,13 @@ static void cyberjack_read_int_callback( struct urb *urb ) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; + int status = urb->status; int result; dbg("%s - port %d", __FUNCTION__, port->number); /* the urb might have been killed. */ - if (urb->status) + if (status) return; usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); @@ -365,12 +366,14 @@ static void cyberjack_read_bulk_callback (struct urb *urb) unsigned char *data = urb->transfer_buffer; short todo; int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); return; } @@ -411,12 +414,14 @@ static void cyberjack_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct cyberjack_private *priv = usb_get_serial_port_data(port); + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); port->write_urb_busy = 0; - if (urb->status) { - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); return; } diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 57b8e27..1633863 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1275,10 +1275,11 @@ static void cypress_read_int_callback(struct urb *urb) int bytes = 0; int result; int i = 0; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - switch (urb->status) { + switch (status) { case 0: /* success */ break; case -ECONNRESET: @@ -1292,7 +1293,7 @@ static void cypress_read_int_callback(struct urb *urb) default: /* something ugly is going on... */ dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n", - __FUNCTION__,urb->status); + __FUNCTION__, status); cypress_set_dead(port); return; } @@ -1419,10 +1420,11 @@ static void cypress_write_int_callback(struct urb *urb) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct cypress_private *priv = usb_get_serial_port_data(port); int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - - switch (urb->status) { + + switch (status) { case 0: /* success */ break; @@ -1430,7 +1432,8 @@ static void cypress_write_int_callback(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, status); priv->write_urb_in_use = 0; return; case -EPIPE: /* no break needed; clear halt and resubmit */ @@ -1438,7 +1441,8 @@ static void cypress_write_int_callback(struct urb *urb) break; usb_clear_halt(port->serial->dev, 0x02); /* error in the urb, so we have to resubmit it */ - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); port->interrupt_out_urb->transfer_buffer_length = 1; port->interrupt_out_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC); @@ -1450,7 +1454,7 @@ static void cypress_write_int_callback(struct urb *urb) break; default: dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n", - __FUNCTION__,urb->status); + __FUNCTION__, status); cypress_set_dead(port); break; } diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index d78692c..976f54e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -416,9 +416,6 @@ struct digi_port { int dp_port_num; int dp_out_buf_len; unsigned char dp_out_buf[DIGI_OUT_BUF_SIZE]; - int dp_in_buf_len; - unsigned char dp_in_buf[DIGI_IN_BUF_SIZE]; - unsigned char dp_in_flag_buf[DIGI_IN_BUF_SIZE]; int dp_write_urb_in_use; unsigned int dp_modem_signals; wait_queue_head_t dp_modem_change_wait; @@ -920,7 +917,6 @@ dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num ); spin_lock_irqsave( &priv->dp_port_lock, flags ); priv->dp_throttled = 1; priv->dp_throttle_restart = 0; - priv->dp_in_buf_len = 0; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); } @@ -930,23 +926,16 @@ static void digi_rx_unthrottle( struct usb_serial_port *port ) { int ret = 0; - int len; unsigned long flags; struct digi_port *priv = usb_get_serial_port_data(port); - struct tty_struct *tty = port->tty; - dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num ); spin_lock_irqsave( &priv->dp_port_lock, flags ); - /* send any buffered chars from throttle time on to tty subsystem */ - - len = tty_buffer_request_room(tty, priv->dp_in_buf_len); - if( len > 0 ) { - tty_insert_flip_string_flags(tty, priv->dp_in_buf, priv->dp_in_flag_buf, len); - tty_flip_buffer_push( tty ); - } + /* turn throttle off */ + priv->dp_throttled = 0; + priv->dp_throttle_restart = 0; /* restart read chain */ if( priv->dp_throttle_restart ) { @@ -954,11 +943,6 @@ dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num ); ret = usb_submit_urb( port->read_urb, GFP_ATOMIC ); } - /* turn throttle off */ - priv->dp_throttled = 0; - priv->dp_in_buf_len = 0; - priv->dp_throttle_restart = 0; - spin_unlock_irqrestore( &priv->dp_port_lock, flags ); if( ret ) { @@ -1340,19 +1324,21 @@ static void digi_write_bulk_callback( struct urb *urb ) struct digi_port *priv; struct digi_serial *serial_priv; int ret = 0; + int status = urb->status; -dbg( "digi_write_bulk_callback: TOP, urb->status=%d", urb->status ); + dbg("digi_write_bulk_callback: TOP, urb status=%d", status); /* port and serial sanity check */ if( port == NULL || (priv=usb_get_serial_port_data(port)) == NULL ) { - err("%s: port or port->private is NULL, status=%d", __FUNCTION__, - urb->status ); + err("%s: port or port->private is NULL, status=%d", + __FUNCTION__, status); return; } serial = port->serial; if( serial == NULL || (serial_priv=usb_get_serial_data(serial)) == NULL ) { - err("%s: serial or serial->private is NULL, status=%d", __FUNCTION__, urb->status ); + err("%s: serial or serial->private is NULL, status=%d", + __FUNCTION__, status); return; } @@ -1687,7 +1673,6 @@ dbg( "digi_startup: TOP" ); spin_lock_init( &priv->dp_port_lock ); priv->dp_port_num = i; priv->dp_out_buf_len = 0; - priv->dp_in_buf_len = 0; priv->dp_write_urb_in_use = 0; priv->dp_modem_signals = 0; init_waitqueue_head( &priv->dp_modem_change_wait ); @@ -1757,25 +1742,28 @@ static void digi_read_bulk_callback( struct urb *urb ) struct digi_port *priv; struct digi_serial *serial_priv; int ret; + int status = urb->status; dbg( "digi_read_bulk_callback: TOP" ); /* port sanity check, do not resubmit if port is not valid */ if( port == NULL || (priv=usb_get_serial_port_data(port)) == NULL ) { - err("%s: port or port->private is NULL, status=%d", __FUNCTION__, - urb->status ); + err("%s: port or port->private is NULL, status=%d", + __FUNCTION__, status); return; } if( port->serial == NULL || (serial_priv=usb_get_serial_data(port->serial)) == NULL ) { - err("%s: serial is bad or serial->private is NULL, status=%d", __FUNCTION__, urb->status ); + err("%s: serial is bad or serial->private is NULL, status=%d", + __FUNCTION__, status); return; } /* do not resubmit urb if it has any status error */ - if( urb->status ) { - err("%s: nonzero read bulk status: status=%d, port=%d", __FUNCTION__, urb->status, priv->dp_port_num ); + if (status) { + err("%s: nonzero read bulk status: status=%d, port=%d", + __FUNCTION__, status, priv->dp_port_num); return; } @@ -1816,10 +1804,11 @@ static int digi_read_inb_callback( struct urb *urb ) struct digi_port *priv = usb_get_serial_port_data(port); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; - int status = ((unsigned char *)urb->transfer_buffer)[2]; + int port_status = ((unsigned char *)urb->transfer_buffer)[2]; unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3; int flag,throttled; int i; + int status = urb->status; /* do not process callbacks on closed ports */ /* but do continue the read chain */ @@ -1828,7 +1817,10 @@ static int digi_read_inb_callback( struct urb *urb ) /* short/multiple packet check */ if( urb->actual_length != len + 2 ) { - err("%s: INCOMPLETE OR MULTIPLE PACKET, urb->status=%d, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", __FUNCTION__, urb->status, priv->dp_port_num, opcode, len, urb->actual_length, status ); + err("%s: INCOMPLETE OR MULTIPLE PACKET, urb status=%d, " + "port=%d, opcode=%d, len=%d, actual_length=%d, " + "port_status=%d", __FUNCTION__, status, priv->dp_port_num, + opcode, len, urb->actual_length, port_status); return( -1 ); } @@ -1843,52 +1835,37 @@ static int digi_read_inb_callback( struct urb *urb ) /* receive data */ if( opcode == DIGI_CMD_RECEIVE_DATA ) { - /* get flag from status */ + /* get flag from port_status */ flag = 0; /* overrun is special, not associated with a char */ - if( status & DIGI_OVERRUN_ERROR ) { + if (port_status & DIGI_OVERRUN_ERROR) { tty_insert_flip_char( tty, 0, TTY_OVERRUN ); } /* break takes precedence over parity, */ /* which takes precedence over framing errors */ - if( status & DIGI_BREAK_ERROR ) { + if (port_status & DIGI_BREAK_ERROR) { flag = TTY_BREAK; - } else if( status & DIGI_PARITY_ERROR ) { + } else if (port_status & DIGI_PARITY_ERROR) { flag = TTY_PARITY; - } else if( status & DIGI_FRAMING_ERROR ) { + } else if (port_status & DIGI_FRAMING_ERROR) { flag = TTY_FRAME; } - /* data length is len-1 (one byte of len is status) */ + /* data length is len-1 (one byte of len is port_status) */ --len; - if( throttled ) { - - len = min( len, - DIGI_IN_BUF_SIZE - priv->dp_in_buf_len ); - - if( len > 0 ) { - memcpy( priv->dp_in_buf + priv->dp_in_buf_len, - data, len ); - memset( priv->dp_in_flag_buf - + priv->dp_in_buf_len, flag, len ); - priv->dp_in_buf_len += len; - } - - } else { - len = tty_buffer_request_room(tty, len); - if( len > 0 ) { - /* Hot path */ - if(flag == TTY_NORMAL) - tty_insert_flip_string(tty, data, len); - else { - for(i = 0; i < len; i++) - tty_insert_flip_char(tty, data[i], flag); - } - tty_flip_buffer_push( tty ); + len = tty_buffer_request_room(tty, len); + if( len > 0 ) { + /* Hot path */ + if(flag == TTY_NORMAL) + tty_insert_flip_string(tty, data, len); + else { + for(i = 0; i < len; i++) + tty_insert_flip_char(tty, data[i], flag); } + tty_flip_buffer_push( tty ); } } diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 4703c8f..050fcc9 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -326,12 +326,14 @@ static int empeg_chars_in_buffer (struct usb_serial_port *port) static void empeg_write_bulk_callback (struct urb *urb) { - struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial_port *port = urb->context; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); return; } @@ -345,11 +347,13 @@ static void empeg_read_bulk_callback (struct urb *urb) struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); return; } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index da1c6f7..7b1673a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -271,26 +271,58 @@ static int debug; static __u16 vendor = FTDI_VID; static __u16 product; +struct ftdi_private { + ftdi_chip_type_t chip_type; + /* type of the device, either SIO or FT8U232AM */ + int baud_base; /* baud base clock for divisor setting */ + int custom_divisor; /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */ + __u16 last_set_data_urb_value ; + /* the last data state set - needed for doing a break */ + int write_offset; /* This is the offset in the usb data block to write the serial data - + * it is different between devices + */ + int flags; /* some ASYNC_xxxx flags are supported */ + unsigned long last_dtr_rts; /* saved modem control outputs */ + wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ + char prev_status, diff_status; /* Used for TIOCMIWAIT */ + __u8 rx_flags; /* receive state flags (throttling) */ + spinlock_t rx_lock; /* spinlock for receive state */ + struct delayed_work rx_work; + struct usb_serial_port *port; + int rx_processed; + unsigned long rx_bytes; + + __u16 interface; /* FT2232C port interface (0 for FT232/245) */ + + int force_baud; /* if non-zero, force the baud rate to this value */ + int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */ + + spinlock_t tx_lock; /* spinlock for transmit state */ + unsigned long tx_bytes; + unsigned long tx_outstanding_bytes; + unsigned long tx_outstanding_urbs; +}; + /* struct ftdi_sio_quirk is used by devices requiring special attention. */ struct ftdi_sio_quirk { int (*probe)(struct usb_serial *); - void (*setup)(struct usb_serial *); /* Special settings during startup. */ + void (*port_probe)(struct ftdi_private *); /* Special settings for probed ports. */ }; static int ftdi_olimex_probe (struct usb_serial *serial); -static void ftdi_USB_UIRT_setup (struct usb_serial *serial); -static void ftdi_HE_TIRA1_setup (struct usb_serial *serial); +static void ftdi_USB_UIRT_setup (struct ftdi_private *priv); +static void ftdi_HE_TIRA1_setup (struct ftdi_private *priv); static struct ftdi_sio_quirk ftdi_olimex_quirk = { .probe = ftdi_olimex_probe, }; static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = { - .setup = ftdi_USB_UIRT_setup, + .port_probe = ftdi_USB_UIRT_setup, }; static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = { - .setup = ftdi_HE_TIRA1_setup, + .port_probe = ftdi_HE_TIRA1_setup, }; /* @@ -567,38 +599,6 @@ static const char *ftdi_chip_name[] = { #define THROTTLED 0x01 #define ACTUALLY_THROTTLED 0x02 -struct ftdi_private { - ftdi_chip_type_t chip_type; - /* type of the device, either SIO or FT8U232AM */ - int baud_base; /* baud base clock for divisor setting */ - int custom_divisor; /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */ - __u16 last_set_data_urb_value ; - /* the last data state set - needed for doing a break */ - int write_offset; /* This is the offset in the usb data block to write the serial data - - * it is different between devices - */ - int flags; /* some ASYNC_xxxx flags are supported */ - unsigned long last_dtr_rts; /* saved modem control outputs */ - wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ - char prev_status, diff_status; /* Used for TIOCMIWAIT */ - __u8 rx_flags; /* receive state flags (throttling) */ - spinlock_t rx_lock; /* spinlock for receive state */ - struct delayed_work rx_work; - struct usb_serial_port *port; - int rx_processed; - unsigned long rx_bytes; - - __u16 interface; /* FT2232C port interface (0 for FT232/245) */ - - int force_baud; /* if non-zero, force the baud rate to this value */ - int force_rtscts; /* if non-zero, force RTS-CTS to always be enabled */ - - spinlock_t tx_lock; /* spinlock for transmit state */ - unsigned long tx_bytes; - unsigned long tx_outstanding_bytes; - unsigned long tx_outstanding_urbs; -}; - /* Used for TIOCMIWAIT */ #define FTDI_STATUS_B0_MASK (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD) #define FTDI_STATUS_B1_MASK (FTDI_RS_BI) @@ -609,7 +609,6 @@ struct ftdi_private { /* function prototypes for a FTDI serial converter */ static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id *id); -static int ftdi_sio_attach (struct usb_serial *serial); static void ftdi_shutdown (struct usb_serial *serial); static int ftdi_sio_port_probe (struct usb_serial_port *port); static int ftdi_sio_port_remove (struct usb_serial_port *port); @@ -663,7 +662,6 @@ static struct usb_serial_driver ftdi_sio_device = { .ioctl = ftdi_ioctl, .set_termios = ftdi_set_termios, .break_ctl = ftdi_break_ctl, - .attach = ftdi_sio_attach, .shutdown = ftdi_shutdown, }; @@ -1149,7 +1147,9 @@ static int create_sysfs_attrs(struct usb_serial_port *port) dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]); retval = device_create_file(&port->dev, &dev_attr_event_char); if ((!retval) && - (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) { + (priv->chip_type == FT232BM || + priv->chip_type == FT2232C || + priv->chip_type == FT232RL)) { retval = device_create_file(&port->dev, &dev_attr_latency_timer); } @@ -1198,6 +1198,8 @@ static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id static int ftdi_sio_port_probe(struct usb_serial_port *port) { struct ftdi_private *priv; + struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial); + dbg("%s",__FUNCTION__); @@ -1214,6 +1216,9 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) than queue a task to deliver them */ priv->flags = ASYNC_LOW_LATENCY; + if (quirk && quirk->port_probe) + quirk->port_probe(priv); + /* Increase the size of read buffers */ kfree(port->bulk_in_buffer); port->bulk_in_buffer = kmalloc (BUFSZ, GFP_KERNEL); @@ -1244,29 +1249,13 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) return 0; } -/* attach subroutine */ -static int ftdi_sio_attach (struct usb_serial *serial) -{ - /* Check for device requiring special set up. */ - struct ftdi_sio_quirk *quirk = usb_get_serial_data(serial); - - if (quirk && quirk->setup) - quirk->setup(serial); - - return 0; -} /* ftdi_sio_attach */ - - /* Setup for the USB-UIRT device, which requires hardwired * baudrate (38400 gets mapped to 312500) */ /* Called from usbserial:serial_probe */ -static void ftdi_USB_UIRT_setup (struct usb_serial *serial) +static void ftdi_USB_UIRT_setup (struct ftdi_private *priv) { - struct ftdi_private *priv; - dbg("%s",__FUNCTION__); - priv = usb_get_serial_port_data(serial->port[0]); priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 77; priv->force_baud = B38400; @@ -1274,13 +1263,10 @@ static void ftdi_USB_UIRT_setup (struct usb_serial *serial) /* Setup for the HE-TIRA1 device, which requires hardwired * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled. */ -static void ftdi_HE_TIRA1_setup (struct usb_serial *serial) +static void ftdi_HE_TIRA1_setup (struct ftdi_private *priv) { - struct ftdi_private *priv; - dbg("%s",__FUNCTION__); - priv = usb_get_serial_port_data(serial->port[0]); priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 240; priv->force_baud = B38400; @@ -1574,14 +1560,15 @@ static void ftdi_write_bulk_callback (struct urb *urb) struct ftdi_private *priv; int data_offset; /* will be 1 for the SIO and 0 otherwise */ unsigned long countback; + int status = urb->status; /* free up the transfer buffer, as usb_free_urb() does not do this */ kfree (urb->transfer_buffer); dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("nonzero write bulk status received: %d", urb->status); + if (status) { + dbg("nonzero write bulk status received: %d", status); return; } @@ -1657,6 +1644,7 @@ static void ftdi_read_bulk_callback (struct urb *urb) struct ftdi_private *priv; unsigned long countread; unsigned long flags; + int status = urb->status; if (urb->number_of_packets > 0) { err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__, @@ -1685,9 +1673,10 @@ static void ftdi_read_bulk_callback (struct urb *urb) err("%s - Not my urb!", __FUNCTION__); } - if (urb->status) { + if (status) { /* This will happen at close every time so it is a dbg not an err */ - dbg("(this is ok on close) nonzero read bulk status received: %d", urb->status); + dbg("(this is ok on close) nonzero read bulk status received: " + "%d", status); return; } diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 74660a3..04bd3b7 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1036,15 +1036,16 @@ static void garmin_write_bulk_callback (struct urb *urb) unsigned long flags; struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); + int status = urb->status; /* free up the transfer buffer, as usb_free_urb() does not do this */ kfree (urb->transfer_buffer); dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { + if (status) { dbg("%s - nonzero write bulk status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= CLEAR_HALT_REQUIRED; spin_unlock_irqrestore(&garmin_data_p->lock, flags); @@ -1281,7 +1282,8 @@ static void garmin_read_bulk_callback (struct urb *urb) struct usb_serial *serial = port->serial; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; - int status; + int status = urb->status; + int retval; dbg("%s - port %d", __FUNCTION__, port->number); @@ -1290,9 +1292,9 @@ static void garmin_read_bulk_callback (struct urb *urb) return; } - if (urb->status) { + if (status) { dbg("%s - nonzero read bulk status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); return; } @@ -1306,19 +1308,19 @@ static void garmin_read_bulk_callback (struct urb *urb) spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART; spin_unlock_irqrestore(&garmin_data_p->lock, flags); - status = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (status) + retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (retval) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", - __FUNCTION__, status); + __FUNCTION__, retval); } else if (urb->actual_length > 0) { /* Continue trying to read until nothing more is received */ if (0 == (garmin_data_p->flags & FLAGS_THROTTLED)) { - status = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (status) + retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (retval) dev_err(&port->dev, - "%s - failed resubmitting read urb, error %d\n", - __FUNCTION__, status); + "%s - failed resubmitting read urb, " + "error %d\n", __FUNCTION__, retval); } } else { dbg("%s - end of bulk data", __FUNCTION__); @@ -1333,13 +1335,14 @@ static void garmin_read_bulk_callback (struct urb *urb) static void garmin_read_int_callback (struct urb *urb) { unsigned long flags; - int status; + int retval; struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; struct garmin_data * garmin_data_p = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; + int status = urb->status; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -1348,11 +1351,11 @@ static void garmin_read_int_callback (struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); return; default: dbg("%s - nonzero urb status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); return; } @@ -1374,11 +1377,11 @@ static void garmin_read_int_callback (struct urb *urb) port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, garmin_read_bulk_callback, port); - status = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (status) { + retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (retval) { dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", - __FUNCTION__, status); + __FUNCTION__, retval); } else { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE; @@ -1422,11 +1425,11 @@ static void garmin_read_int_callback (struct urb *urb) } port->interrupt_in_urb->dev = port->serial->dev; - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n", - __FUNCTION__, status); + __FUNCTION__, retval); } diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 4f8282a..88a2c7d 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -69,6 +69,7 @@ struct usb_serial_driver usb_serial_generic_device = { .shutdown = usb_serial_generic_shutdown, .throttle = usb_serial_generic_throttle, .unthrottle = usb_serial_generic_unthrottle, + .resume = usb_serial_generic_resume, }; static int generic_probe(struct usb_interface *interface, @@ -169,6 +170,23 @@ static void generic_cleanup (struct usb_serial_port *port) } } +int usb_serial_generic_resume(struct usb_serial *serial) +{ + struct usb_serial_port *port; + int i, c = 0, r; + + for (i = 0; i < serial->num_ports; i++) { + port = serial->port[i]; + if (port->open_count && port->read_urb) { + r = usb_submit_urb(port->read_urb, GFP_NOIO); + if (r < 0) + c++; + } + } + + return c ? -EIO : 0; +} + void usb_serial_generic_close (struct usb_serial_port *port, struct file * filp) { dbg("%s - port %d", __FUNCTION__, port->number); @@ -263,79 +281,82 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) return (chars); } -/* Push data to tty layer and resubmit the bulk read URB */ -static void flush_and_resubmit_read_urb (struct usb_serial_port *port) + +static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) { - struct usb_serial *serial = port->serial; struct urb *urb = port->read_urb; - struct tty_struct *tty = port->tty; + struct usb_serial *serial = port->serial; int result; - /* Push data to tty */ - if (tty && urb->actual_length) { - tty_buffer_request_room(tty, urb->actual_length); - tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length); - tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */ - } - /* Continue reading from device */ - usb_fill_bulk_urb (port->read_urb, serial->dev, + usb_fill_bulk_urb (urb, serial->dev, usb_rcvbulkpipe (serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_buffer_length, ((serial->type->read_bulk_callback) ? serial->type->read_bulk_callback : usb_serial_generic_read_bulk_callback), port); - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + result = usb_submit_urb(urb, mem_flags); if (result) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); } +/* Push data to tty layer and resubmit the bulk read URB */ +static void flush_and_resubmit_read_urb (struct usb_serial_port *port) +{ + struct urb *urb = port->read_urb; + struct tty_struct *tty = port->tty; + int room; + + /* Push data to tty */ + if (tty && urb->actual_length) { + room = tty_buffer_request_room(tty, urb->actual_length); + if (room) { + tty_insert_flip_string(tty, urb->transfer_buffer, room); + tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */ + } + } + + resubmit_read_urb(port, GFP_ATOMIC); +} + void usb_serial_generic_read_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; unsigned char *data = urb->transfer_buffer; - int is_throttled; - unsigned long flags; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (unlikely(status != 0)) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); return; } usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); /* Throttle the device if requested by tty */ - if (urb->actual_length) { - spin_lock_irqsave(&port->lock, flags); - is_throttled = port->throttled = port->throttle_req; - spin_unlock_irqrestore(&port->lock, flags); - if (is_throttled) { - /* Let the received data linger in the read URB; - * usb_serial_generic_unthrottle() will pick it - * up later. */ - dbg("%s - throttling device", __FUNCTION__); - return; - } - } - - /* Handle data and continue reading from device */ - flush_and_resubmit_read_urb(port); + spin_lock(&port->lock); + if (!(port->throttled = port->throttle_req)) + /* Handle data and continue reading from device */ + flush_and_resubmit_read_urb(port); + spin_unlock(&port->lock); } EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); void usb_serial_generic_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); port->write_urb_busy = 0; - if (urb->status) { - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); return; } @@ -370,8 +391,8 @@ void usb_serial_generic_unthrottle (struct usb_serial_port *port) spin_unlock_irqrestore(&port->lock, flags); if (was_throttled) { - /* Handle pending data and resume reading from device */ - flush_and_resubmit_read_urb(port); + /* Resume reading from device */ + resubmit_read_urb(port, GFP_KERNEL); } } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 056e192..dd42f57 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -599,10 +599,11 @@ static void edge_interrupt_callback (struct urb *urb) int txCredits; int portNumber; int result; + int status = urb->status; dbg("%s", __FUNCTION__); - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -610,10 +611,12 @@ static void edge_interrupt_callback (struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, status); goto exit; } @@ -688,13 +691,15 @@ static void edge_bulk_in_callback (struct urb *urb) { struct edgeport_serial *edge_serial = (struct edgeport_serial *)urb->context; unsigned char *data = urb->transfer_buffer; - int status; + int retval; __u16 raw_data_length; + int status = urb->status; dbg("%s", __FUNCTION__); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); edge_serial->read_in_progress = false; return; } @@ -722,9 +727,11 @@ static void edge_bulk_in_callback (struct urb *urb) if (edge_serial->rxBytesAvail > 0) { dbg("%s - posting a read", __FUNCTION__); edge_serial->read_urb->dev = edge_serial->serial->dev; - status = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC); - if (status) { - dev_err(&urb->dev->dev, "%s - usb_submit_urb(read bulk) failed, status = %d\n", __FUNCTION__, status); + retval = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC); + if (retval) { + dev_err(&urb->dev->dev, + "%s - usb_submit_urb(read bulk) failed, " + "retval = %d\n", __FUNCTION__, retval); edge_serial->read_in_progress = false; } } else { @@ -744,11 +751,13 @@ static void edge_bulk_out_data_callback (struct urb *urb) { struct edgeport_port *edge_port = (struct edgeport_port *)urb->context; struct tty_struct *tty; + int status = urb->status; dbg("%s", __FUNCTION__); - if (urb->status) { - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); } tty = edge_port->port->tty; @@ -1504,15 +1513,6 @@ static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old } cflag = tty->termios->c_cflag; - /* check that they really want us to change something */ - if (old_termios) { - if (cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) { - dbg("%s - nothing to change", __FUNCTION__); - return; - } - } - dbg("%s - clfag %08x iflag %08x", __FUNCTION__, tty->termios->c_cflag, tty->termios->c_iflag); if (old_termios) { diff --git a/drivers/usb/serial/io_fw_down3.h b/drivers/usb/serial/io_fw_down3.h index 93b56d6..4496b06 100644 --- a/drivers/usb/serial/io_fw_down3.h +++ b/drivers/usb/serial/io_fw_down3.h @@ -5,7 +5,7 @@ //************************************************************** -static int IMAGE_SIZE = 12749; +static int IMAGE_SIZE = 12938; struct EDGE_FIRMWARE_VERSION_INFO { @@ -16,7 +16,7 @@ struct EDGE_FIRMWARE_VERSION_INFO static struct EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME = { - 4, 10, 0 // Major, Minor, Build + 4, 80, 0 // Major, Minor, Build }; @@ -27,16 +27,16 @@ static unsigned char IMAGE_ARRAY_NAME[] = // WORD Length; // BYTE CheckSum; // }; -0xca, 0x31, -0xa8, +0x87, 0x32, +0x9a, -0x02, 0x26, 0xfe, 0x02, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, +0x02, 0x27, 0xbf, 0x02, 0x21, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x1a, 0x85, 0x3f, 0x8c, 0x85, 0x40, 0x8a, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0xe6, 0x60, 0x2b, 0xe5, 0x3e, 0x24, 0x10, 0xf8, 0xa6, 0x81, 0xe5, 0x3e, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0x78, 0x8c, 0xe5, 0x81, -0x04, 0xc3, 0x98, 0xf9, 0x94, 0x22, 0x40, 0x03, 0x02, 0x11, 0x94, 0xe6, 0xf0, 0x08, 0xa3, 0xd9, +0x04, 0xc3, 0x98, 0xf9, 0x94, 0x22, 0x40, 0x03, 0x02, 0x11, 0xdc, 0xe6, 0xf0, 0x08, 0xa3, 0xd9, 0xfa, 0x74, 0x08, 0x25, 0x3e, 0xf8, 0x05, 0x3e, 0x08, 0xe6, 0x54, 0x80, 0x70, 0x0c, 0xe5, 0x3e, 0xb4, 0x07, 0xf3, 0x78, 0x08, 0x75, 0x3e, 0x00, 0x80, 0xef, 0xe5, 0x3e, 0x24, 0x10, 0xf8, 0x86, 0x81, 0xe5, 0x3e, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, @@ -49,387 +49,398 @@ static unsigned char IMAGE_ARRAY_NAME[] = 0xc9, 0xf0, 0x69, 0x60, 0x02, 0x7e, 0x04, 0xa3, 0xe0, 0xca, 0xf0, 0x6a, 0x60, 0x02, 0x7e, 0x04, 0xa3, 0xe0, 0xcb, 0xf0, 0x6b, 0x60, 0x02, 0x7e, 0x04, 0x22, 0xc0, 0xe0, 0xc0, 0xd0, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, -0xc0, 0x06, 0xc0, 0x07, 0x90, 0xff, 0x93, 0x74, 0x01, 0xf0, 0xe5, 0x81, 0x94, 0xfd, 0x40, 0x03, -0x02, 0x11, 0x94, 0x85, 0x41, 0x8d, 0x85, 0x42, 0x8b, 0x74, 0xaf, 0xf5, 0x82, 0x74, 0xfa, 0xf5, -0x83, 0xe0, 0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x4a, 0xe0, 0x30, 0xe7, 0x2c, -0x90, 0xff, 0x4e, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x20, -0xb4, 0x02, 0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x27, -0x8d, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, -0xa3, 0xe0, 0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x52, 0xe0, 0x30, 0xe7, 0x2c, -0x90, 0xff, 0x56, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x25, -0xb4, 0x02, 0x22, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x27, -0x8d, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, -0x80, 0x03, 0x02, 0x02, 0x62, 0x74, 0x15, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x20, 0x04, -0xf1, 0x20, 0x02, 0x03, 0x30, 0x01, 0xeb, 0x74, 0x18, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, -0x14, 0xfc, 0xf0, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0x64, 0x04, 0x70, 0x0f, 0xec, 0x70, 0x62, -0x7e, 0x01, 0x12, 0x00, 0xc9, 0x7c, 0x0a, 0x7d, 0xfa, 0x02, 0x02, 0x33, 0x12, 0x00, 0xc9, 0xee, -0x64, 0x04, 0x60, 0x1d, 0xec, 0x70, 0x4b, 0x7c, 0x0a, 0xed, 0x14, 0xfd, 0x70, 0x15, 0xee, 0x64, -0x02, 0x60, 0x07, 0x7e, 0x02, 0x7d, 0x32, 0x02, 0x02, 0x33, 0x7e, 0x01, 0x7d, 0xfa, 0x02, 0x02, -0x33, 0x7c, 0x0a, 0x74, 0x18, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, -0xa3, 0xee, 0xf0, 0x14, 0x60, 0x18, 0x20, 0xe1, 0x0f, 0x20, 0x01, 0x06, 0xd2, 0xb1, 0xc2, 0xb0, -0x80, 0x10, 0xc2, 0xb1, 0xd2, 0xb0, 0x80, 0x0a, 0xc2, 0xb1, 0xc2, 0xb0, 0x80, 0x04, 0xd2, 0xb0, -0xd2, 0xb1, 0x78, 0x19, 0x79, 0x09, 0x7a, 0x07, 0xe7, 0x70, 0x04, 0xa6, 0x00, 0x80, 0x0b, 0xe6, -0x60, 0x08, 0x16, 0xe6, 0x70, 0x04, 0xe7, 0x44, 0x80, 0xf7, 0x08, 0x09, 0xda, 0xea, 0xe5, 0x3d, -0x60, 0x13, 0x14, 0xf5, 0x3d, 0x70, 0x0e, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, -0x0f, 0xd2, 0x8c, 0xd2, 0x8d, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, -0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, -0x90, 0xff, 0x04, 0xe0, 0x90, 0xfa, 0xb6, 0xf0, 0x90, 0xff, 0x06, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, -0xec, 0xff, 0xea, 0xfe, 0xef, 0xc3, 0x94, 0x08, 0xee, 0x94, 0x01, 0x50, 0x02, 0x80, 0x04, 0x7e, -0x01, 0x7f, 0x08, 0x8e, 0x3b, 0x8f, 0x3c, 0x90, 0xff, 0x02, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, -0xff, 0xea, 0x90, 0xfa, 0xba, 0xf0, 0xef, 0xa3, 0xf0, 0x12, 0x1c, 0x30, 0xe4, 0xf5, 0x4d, 0xe5, -0x4d, 0xc3, 0x94, 0x02, 0x50, 0x0f, 0x12, 0x1c, 0x11, 0xe4, 0x12, 0x1a, 0x38, 0x05, 0x4d, 0x04, -0x12, 0x1c, 0x02, 0x80, 0xea, 0x12, 0x1c, 0x30, 0x90, 0xff, 0x00, 0xe0, 0xff, 0x54, 0x60, 0x24, -0xc0, 0x70, 0x03, 0x02, 0x08, 0xc5, 0x24, 0x40, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, -0xe0, 0xfe, 0x54, 0x0f, 0xf5, 0x4d, 0xee, 0x30, 0xe7, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0a, -0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, 0x4c, 0x03, 0x56, 0x00, 0x04, 0x29, 0x01, 0x05, 0x3c, 0x03, -0x06, 0x03, 0x05, 0x06, 0x45, 0x06, 0x07, 0xa7, 0x08, 0x07, 0xef, 0x09, 0x08, 0x4b, 0x0a, 0x08, -0x8b, 0x0b, 0x00, 0x00, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, -0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x64, 0x02, 0x45, -0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xef, 0x54, 0x1f, 0x14, 0x60, 0x2b, 0x14, 0x60, 0x47, 0x24, -0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xee, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0x11, 0x74, -0x01, 0x12, 0x1a, 0x38, 0x78, 0x67, 0xe6, 0x30, 0xe0, 0x08, 0x12, 0x1c, 0x11, 0x74, 0x02, 0x12, -0x1a, 0x38, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe1, 0x09, 0x90, 0xfa, 0xb6, 0xe0, -0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, -0x26, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe1, 0x0e, 0x90, 0xfa, 0xb6, 0xe0, 0xff, -0x60, 0x07, 0x64, 0x80, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0xb2, 0x40, 0x03, 0x02, 0x0f, -0x26, 0xe5, 0x4d, 0x70, 0x19, 0x30, 0x0a, 0x0b, 0x90, 0xff, 0x80, 0x12, 0x1c, 0x0e, 0x12, 0x1a, -0x38, 0x80, 0x24, 0x90, 0xff, 0x82, 0x12, 0x1c, 0x0e, 0x12, 0x1a, 0x38, 0x80, 0x19, 0x15, 0x4d, -0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0x12, 0x1c, 0x0c, 0x12, 0x1a, 0x38, 0x80, 0x09, 0x12, 0x1c, -0xb3, 0x12, 0x1c, 0x0c, 0x12, 0x1a, 0x38, 0x12, 0x1c, 0x11, 0x12, 0x19, 0xf2, 0x60, 0x05, 0x74, -0x01, 0x12, 0x1a, 0x38, 0x7f, 0x02, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, -0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x14, 0x60, 0x2d, -0x14, 0x60, 0x59, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x04, -0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, -0x0f, 0x26, 0x78, 0x67, 0xe6, 0x54, 0xfe, 0xf6, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, -0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 0x09, 0x90, 0xfa, 0xb6, -0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x0c, 0x90, 0xfa, 0xb6, 0xe0, 0xd3, -0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, 0x26, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xfa, 0xba, 0xe0, -0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0xb2, 0x40, 0x03, 0x02, 0x0f, -0x26, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, -0x07, 0xe5, 0x4d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x4d, 0x70, 0x0f, 0x90, 0xff, 0x82, 0xe0, -0x54, 0xf7, 0xf0, 0x90, 0xff, 0x80, 0xe0, 0x54, 0xf7, 0xf0, 0x22, 0xe5, 0x4d, 0x24, 0xfe, 0x60, -0x20, 0x24, 0xfb, 0x60, 0x34, 0x24, 0x06, 0x70, 0x35, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, -0xfd, 0x7f, 0x03, 0x12, 0x2d, 0xa8, 0x80, 0x26, 0xe4, 0xfd, 0x7f, 0x03, 0x12, 0x2d, 0xa8, 0x80, -0x1d, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, 0x04, 0x12, 0x2d, 0xa8, 0x80, 0x0e, -0xe4, 0xfd, 0x7f, 0x04, 0x12, 0x2d, 0xa8, 0x80, 0x05, 0x7f, 0x87, 0x12, 0x31, 0x32, 0x15, 0x4d, -0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x80, 0x09, 0x12, 0x1c, -0xb3, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, -0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, -0x14, 0x60, 0x2d, 0x14, 0x60, 0x55, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, -0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, -0x60, 0x03, 0x02, 0x0f, 0x26, 0x78, 0x67, 0xe6, 0x44, 0x01, 0xf6, 0xe4, 0xff, 0x02, 0x31, 0xb1, -0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe0, 0x07, -0xe5, 0x4d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x0a, 0xe5, 0x4d, 0xd3, 0x94, -0x01, 0x40, 0x03, 0x02, 0x0f, 0x26, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xfa, 0xba, 0xe0, 0x70, -0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x12, 0x31, 0x82, -0x40, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x26, -0xe5, 0x4d, 0x70, 0x09, 0x30, 0x0a, 0x03, 0x02, 0x1d, 0x64, 0x02, 0x1d, 0x2f, 0xe5, 0x35, 0x20, -0xe1, 0x03, 0x02, 0x0f, 0x26, 0x15, 0x4d, 0x30, 0x0a, 0x0b, 0x12, 0x1c, 0xa5, 0xf5, 0x83, 0xe0, -0x44, 0x08, 0xf0, 0x80, 0x09, 0x12, 0x1c, 0xb3, 0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0xe4, 0xff, -0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, -0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, -0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x30, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xbb, -0xe0, 0x90, 0xff, 0xff, 0xf0, 0xe0, 0x60, 0x05, 0x43, 0x35, 0x01, 0x80, 0x03, 0x53, 0x35, 0xfe, -0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, -0x3b, 0x70, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, -0xba, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0x24, 0xfe, 0x60, 0x3a, 0x14, 0x60, 0x75, 0x24, 0x02, -0x60, 0x03, 0x02, 0x0f, 0x26, 0xed, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0x30, 0x12, 0x1d, -0x5d, 0x7d, 0x03, 0x12, 0x0f, 0x6d, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0x2a, 0x90, 0xfa, -0xb3, 0xe0, 0xfd, 0xa3, 0x12, 0x1c, 0x7b, 0x12, 0x0f, 0x89, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, -0xaf, 0x3c, 0x02, 0x0f, 0xba, 0x12, 0x1c, 0x30, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe4, 0x0d, 0x12, -0x1d, 0x5d, 0x7d, 0x14, 0x12, 0x0f, 0x6d, 0x60, 0x10, 0x02, 0x0f, 0x26, 0x12, 0x1d, 0x5d, 0x7d, -0x04, 0x12, 0x0f, 0xc1, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x0f, 0x2a, 0x90, 0xfa, 0xb3, 0xe0, -0xfd, 0xa3, 0x12, 0x1c, 0x7b, 0x12, 0x0f, 0x89, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, -0x02, 0x0f, 0xba, 0x12, 0x1d, 0x5d, 0x7d, 0x05, 0x12, 0x0f, 0xc1, 0x60, 0x03, 0x02, 0x0f, 0x26, -0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, 0x12, 0x1c, 0x78, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, -0xb4, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xbb, 0xe0, 0x90, 0xfa, 0xb2, 0xf0, -0xe4, 0xf5, 0x4c, 0x90, 0xfa, 0xb2, 0xe0, 0xff, 0xe5, 0x4c, 0xc3, 0x9f, 0x50, 0x24, 0x12, 0x1c, -0x72, 0x12, 0x0f, 0xcc, 0xff, 0xfd, 0x90, 0xfa, 0xb4, 0xe4, 0x8d, 0xf0, 0x12, 0x1a, 0x6c, 0x90, -0xfa, 0xb3, 0xe0, 0xc3, 0x9f, 0xf0, 0xd3, 0x94, 0x00, 0x50, 0x03, 0x02, 0x0f, 0x26, 0x05, 0x4c, -0x80, 0xd1, 0x12, 0x1c, 0x72, 0x12, 0x0f, 0xcc, 0x24, 0xfe, 0xff, 0x90, 0xfa, 0xb3, 0xf0, 0xfd, -0xa3, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1a, 0x6c, 0x7a, 0xf9, 0x79, 0x6f, 0x7b, 0x01, 0x8b, 0x36, -0x8a, 0x37, 0x89, 0x38, 0xe9, 0x24, 0x02, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, 0x1c, 0x78, 0x12, 0x25, -0xd7, 0x8f, 0x4c, 0x05, 0x4c, 0x05, 0x4c, 0x12, 0x1c, 0x11, 0xe5, 0x4c, 0x12, 0x1a, 0x38, 0x12, -0x1c, 0x11, 0x90, 0x00, 0x01, 0x74, 0x03, 0x12, 0x1a, 0x4a, 0xaf, 0x4c, 0x7e, 0x00, 0xc3, 0xef, -0x95, 0x3c, 0xee, 0x95, 0x3b, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 0x8e, 0x39, 0x8f, -0x3a, 0x02, 0x2c, 0x07, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, -0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, -0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, -0x12, 0x1c, 0xc9, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, -0x02, 0x0f, 0x26, 0x75, 0x36, 0x00, 0x75, 0x37, 0x00, 0x75, 0x38, 0x32, 0x02, 0x0f, 0xa9, 0xe5, -0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, -0x90, 0xfa, 0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xd3, 0x90, 0xfa, 0xbb, 0xe0, 0x94, 0x01, -0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x60, 0x03, -0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, -0xbb, 0xe0, 0xf5, 0x32, 0xe5, 0x32, 0x70, 0x08, 0x43, 0x35, 0x01, 0x53, 0x35, 0xfd, 0x80, 0x06, -0x53, 0x35, 0xfe, 0x43, 0x35, 0x02, 0xe4, 0xff, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x20, 0xe7, 0x03, -0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, -0xb6, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x26, 0x90, 0xfa, 0xba, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, -0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, 0xc9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, -0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, 0x7f, 0x01, 0x02, 0x31, 0xb1, 0xe5, 0x35, 0x30, 0xe7, 0x03, -0x02, 0x0f, 0x26, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xd3, 0x90, 0xfa, 0xbb, -0xe0, 0x94, 0x00, 0x90, 0xfa, 0xba, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x26, 0x12, 0x1c, -0xc9, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x26, 0xe5, 0x35, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x26, -0xe4, 0xff, 0x02, 0x31, 0xb1, 0x90, 0xff, 0x01, 0x12, 0x1d, 0x74, 0xef, 0x12, 0x1a, 0x38, 0x90, -0xfa, 0xb6, 0x12, 0x1d, 0x74, 0x90, 0x00, 0x01, 0xef, 0x12, 0x1a, 0x4a, 0x90, 0x00, 0x02, 0xe4, -0x12, 0x1a, 0x4a, 0x74, 0x03, 0x12, 0x1c, 0x02, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0xa3, 0xe0, 0x85, -0x38, 0x82, 0x85, 0x37, 0x83, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, -0x4c, 0x09, 0x4a, 0x02, 0x09, 0x6c, 0x04, 0x09, 0x8e, 0x05, 0x09, 0xba, 0x06, 0x09, 0xd8, 0x07, -0x09, 0xf6, 0x08, 0x0a, 0x14, 0x09, 0x0a, 0x32, 0x0b, 0x0a, 0xe7, 0x80, 0x0d, 0x6f, 0x81, 0x0d, -0xa0, 0x82, 0x0b, 0x2e, 0x83, 0x0b, 0x77, 0x84, 0x0b, 0x96, 0x85, 0x0b, 0xdb, 0x86, 0x0c, 0x26, -0x87, 0x0c, 0xb7, 0x88, 0x0d, 0x42, 0x89, 0x0a, 0x50, 0x92, 0x0a, 0x50, 0x93, 0x0e, 0x53, 0xc0, -0x0e, 0x7f, 0xc1, 0x0e, 0x90, 0xc2, 0x00, 0x00, 0x0f, 0x15, 0xe5, 0x35, 0x20, 0xe7, 0x05, 0x7f, -0x05, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, -0x7f, 0x07, 0x02, 0x11, 0x16, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0x18, 0xe5, 0x35, 0x20, 0xe7, -0x05, 0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, -0x7c, 0x00, 0x7f, 0x0c, 0x02, 0x11, 0x16, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0x18, 0xe5, 0x35, -0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1d, 0x92, 0x50, 0x06, 0xe5, 0x3c, 0x45, 0x3b, 0x70, -0x05, 0x7f, 0x02, 0x02, 0x30, 0xec, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfe, 0x24, 0xfd, 0x50, 0x02, -0x80, 0x03, 0x02, 0x31, 0x6f, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, -0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x08, -0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, -0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x09, 0x02, 0x11, -0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, -0xc1, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0a, 0x02, 0x11, 0x16, 0x7f, -0x07, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, -0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0b, 0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, -0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x29, 0x12, 0x1c, 0xc1, 0x60, 0x03, 0x04, -0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0e, 0x02, 0x11, 0x16, 0x7f, 0x07, 0x02, 0x30, 0xec, -0xe5, 0x35, 0x30, 0xe7, 0x56, 0x12, 0x1c, 0xc9, 0x70, 0x4a, 0x90, 0xff, 0x02, 0xe0, 0xf5, 0x4c, -0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, 0x4c, 0xb4, 0x83, 0x05, 0x75, -0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 0x4c, 0x12, 0x1b, 0x72, 0x12, -0x1d, 0x8b, 0x12, 0x25, 0x39, 0x12, 0x1c, 0xd9, 0x12, 0x1a, 0x0b, 0x60, 0x05, 0x12, 0x31, 0xbd, -0x80, 0x06, 0x85, 0x33, 0x39, 0x85, 0x34, 0x3a, 0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, -0x72, 0x02, 0x2c, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 0x18, 0x12, 0x1c, 0xc9, 0x60, 0x05, -0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x1d, 0x92, 0x40, 0x05, 0x7f, 0x03, 0x02, 0x30, 0xec, 0x90, -0xff, 0x02, 0xe0, 0xf5, 0x4c, 0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, -0x4c, 0xb4, 0x83, 0x05, 0x75, 0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, -0x4c, 0x12, 0x1b, 0x72, 0x02, 0x31, 0x6f, 0x12, 0x1d, 0x9c, 0x12, 0x2a, 0x06, 0x12, 0x1c, 0x83, -0xe0, 0x54, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x78, 0x68, 0x12, 0x1b, -0x28, 0x90, 0x00, 0x02, 0x12, 0x1a, 0x0b, 0x30, 0xe7, 0xf2, 0x90, 0x00, 0x02, 0xe4, 0x12, 0x1a, -0x4a, 0x90, 0xfa, 0xb7, 0xe0, 0x44, 0x80, 0xff, 0xf0, 0x78, 0x7c, 0xe6, 0xfc, 0x08, 0xe6, 0x8c, -0x83, 0x12, 0x1c, 0x8b, 0xef, 0xf0, 0x12, 0x31, 0xc7, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x90, 0xfa, -0xb6, 0xe0, 0x64, 0x01, 0x70, 0x1f, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, -0xe0, 0xf5, 0x90, 0x80, 0x2d, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0x90, 0x90, 0xfa, 0xbb, 0xe0, 0x42, -0x90, 0xd2, 0xaf, 0x80, 0x1d, 0x90, 0xfa, 0xba, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, 0xe0, -0xf5, 0xb0, 0x80, 0x0e, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0xb0, 0x90, 0xfa, 0xbb, 0xe0, 0x42, 0xb0, -0xd2, 0xaf, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0x30, 0x90, 0xfa, 0xb6, 0xe0, 0xb4, 0x01, -0x0a, 0x12, 0x1c, 0x11, 0xe5, 0x90, 0x12, 0x1a, 0x38, 0x80, 0x08, 0x12, 0x1c, 0x11, 0xe5, 0xb0, -0x12, 0x1a, 0x38, 0x02, 0x0f, 0xa9, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x24, 0x12, 0x12, 0x1c, 0x41, -0x20, 0xe1, 0x33, 0x12, 0x1c, 0xd0, 0xef, 0x24, 0xfc, 0x60, 0x18, 0x04, 0x70, 0x28, 0x90, 0xfa, -0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x19, 0x12, 0x1d, 0xa6, -0xf0, 0x80, 0x13, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0xf0, -0x80, 0x04, 0x12, 0x1d, 0xad, 0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x90, 0xfa, 0xb6, 0xe0, 0xff, -0x24, 0x12, 0x12, 0x1c, 0x41, 0x20, 0xe1, 0x39, 0x12, 0x1c, 0xd0, 0xef, 0x24, 0xfc, 0x60, 0x1b, -0x04, 0x70, 0x2e, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, -0x80, 0x1f, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xb7, 0xe0, 0x60, -0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, -0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x12, 0x1c, 0xc1, 0x60, 0x4d, 0x04, 0x60, -0x03, 0x02, 0x0c, 0xb2, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x0f, 0x90, 0xff, 0xa4, 0x12, 0x1c, 0x3a, -0x30, 0xe1, 0x6f, 0x12, 0x1d, 0x7c, 0x02, 0x0c, 0xb2, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfb, 0x12, -0x1c, 0x3d, 0xfe, 0x30, 0xe1, 0x5c, 0x30, 0xe2, 0x11, 0x30, 0xb4, 0x05, 0x12, 0x1d, 0x7c, 0x80, -0x51, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x48, 0x30, 0x95, 0x05, 0x12, 0x1d, 0x7c, -0x80, 0x40, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x37, 0x90, 0xfa, 0xb7, 0xe0, 0x60, -0x12, 0x90, 0xff, 0xb4, 0x12, 0x1c, 0x3a, 0x30, 0xe1, 0x28, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, -0xf0, 0x80, 0x1f, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xfb, 0x12, 0x1c, 0x3d, 0x30, 0xe1, 0x13, 0x30, -0x93, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, -0xfd, 0xf0, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfc, -0x60, 0x40, 0x04, 0x70, 0x78, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xa2, 0xe0, 0x44, -0x40, 0xf0, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x65, 0xd2, 0x03, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, -0xff, 0xa3, 0xef, 0x54, 0x7f, 0xf0, 0x80, 0x55, 0x30, 0x03, 0x0e, 0x90, 0xff, 0xa3, 0xe0, 0x44, -0x80, 0xf0, 0xc2, 0x03, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x54, 0xbf, 0xf0, -0x80, 0x3b, 0x90, 0xfa, 0xb7, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xb2, 0xe0, 0x44, 0x40, 0xf0, 0xa3, -0xe0, 0xff, 0x30, 0xe7, 0x28, 0xd2, 0x04, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, 0xff, 0xb3, 0xef, -0x54, 0x7f, 0xf0, 0x80, 0x18, 0x30, 0x04, 0x0e, 0x90, 0xff, 0xb3, 0xe0, 0x44, 0x80, 0xf0, 0xc2, -0x04, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x54, 0xbf, 0xf0, 0xe4, 0xff, 0x02, -0x30, 0xec, 0x12, 0x1c, 0x30, 0x90, 0xfa, 0xb6, 0xe0, 0x24, 0xfc, 0x60, 0x0f, 0x04, 0x70, 0x16, -0x90, 0xff, 0xa6, 0xe0, 0x12, 0x1c, 0x11, 0x12, 0x1a, 0x38, 0x80, 0x0a, 0x90, 0xff, 0xb6, 0xe0, -0x12, 0x1c, 0x11, 0x12, 0x1a, 0x38, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 0x2c, 0x07, 0xe4, -0xff, 0x12, 0x30, 0xec, 0x12, 0x1d, 0x37, 0x7f, 0x03, 0x12, 0x12, 0x19, 0x90, 0xf9, 0x15, 0xe0, -0x30, 0xe4, 0x08, 0x90, 0xff, 0x93, 0x74, 0x80, 0xf0, 0x80, 0x10, 0x90, 0xff, 0xfc, 0xe0, 0x54, -0x7f, 0xf0, 0x7f, 0xff, 0x7e, 0x00, 0x12, 0x30, 0x16, 0xc2, 0x90, 0xc2, 0xaf, 0x00, 0x80, 0xfd, -0xe4, 0xf5, 0x4e, 0xf5, 0x4f, 0x90, 0xfa, 0xbc, 0x74, 0x3e, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0xfa, -0xb4, 0xf0, 0xa3, 0x74, 0x15, 0xf0, 0xe0, 0x54, 0x3f, 0xff, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0xfa, -0xb9, 0xf0, 0xd3, 0x94, 0x00, 0xe4, 0x94, 0x3e, 0x40, 0x08, 0x90, 0xfa, 0xbd, 0xe0, 0x90, 0xfa, -0xb9, 0xf0, 0x12, 0x0f, 0x50, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x73, 0x12, 0x1c, 0x4a, 0x90, 0xfa, -0xbc, 0x12, 0x1d, 0x56, 0x60, 0x27, 0xd3, 0xef, 0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x08, 0x90, -0xfa, 0xb9, 0x74, 0x40, 0xf0, 0x80, 0x08, 0x90, 0xfa, 0xbd, 0xe0, 0x90, 0xfa, 0xb9, 0xf0, 0x12, -0x0f, 0x50, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x46, 0x12, 0x1c, 0x4a, 0x80, 0xd1, 0x75, 0x4c, 0x02, -0x90, 0xfa, 0xbc, 0xe4, 0xf0, 0xa3, 0x04, 0xf0, 0x90, 0xfa, 0xb4, 0xe4, 0xf0, 0xa3, 0x74, 0x0f, -0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0x90, 0xfa, 0xbd, 0xe0, 0xf5, 0x4a, 0x7d, 0x0f, 0x7c, -0x00, 0x12, 0x28, 0x9f, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0xe4, -0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0xe4, 0xf5, 0x30, 0xf5, 0x31, 0xaf, 0x31, -0x02, 0x30, 0xec, 0x12, 0x1c, 0xd0, 0x30, 0xe7, 0x10, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x64, 0xf0, -0xd3, 0x94, 0x00, 0x40, 0x15, 0xc2, 0x95, 0x80, 0x11, 0x90, 0xfa, 0xb7, 0xe0, 0x54, 0x0f, 0x90, -0xf9, 0x63, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x02, 0xc2, 0x94, 0xe4, 0xff, 0x02, 0x30, 0xec, 0x12, -0x1d, 0x9c, 0xbf, 0x01, 0x04, 0xd2, 0x93, 0x80, 0x02, 0xc2, 0x93, 0xe4, 0xff, 0x02, 0x30, 0xec, -0x12, 0x1c, 0xd0, 0x54, 0x03, 0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, -0x70, 0x2b, 0xd2, 0x91, 0x80, 0x27, 0xc2, 0x91, 0x80, 0x23, 0x12, 0x1d, 0xa6, 0x12, 0x0f, 0x78, -0x60, 0x04, 0xd2, 0x91, 0x80, 0x17, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0x78, 0xff, -0xbf, 0xa0, 0x04, 0xc2, 0x91, 0x80, 0x02, 0xd2, 0x91, 0x12, 0x1d, 0xa6, 0xf0, 0x90, 0xfa, 0xb7, -0xe0, 0x54, 0x0c, 0xff, 0x13, 0x13, 0x54, 0x3f, 0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, -0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x92, 0x80, 0x27, 0xc2, 0x92, 0x80, 0x23, 0x12, 0x1d, 0xad, -0x12, 0x0f, 0x98, 0x60, 0x04, 0xd2, 0x92, 0x80, 0x17, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0x12, -0x0f, 0x98, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x92, 0x80, 0x02, 0xd2, 0x92, 0x12, 0x1d, 0xad, 0xf0, -0xe4, 0xff, 0x02, 0x30, 0xec, 0xe5, 0x35, 0x30, 0xe7, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, -0x18, 0x7f, 0x05, 0x02, 0x30, 0xec, 0x12, 0x31, 0xbd, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb3, -0x90, 0xfa, 0xb4, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, -0xfa, 0xb4, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, -0xaa, 0x4e, 0xa9, 0x4f, 0x7b, 0xff, 0x90, 0xfa, 0xb4, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, -0xb9, 0xe0, 0xf5, 0x4a, 0x12, 0x28, 0x9f, 0x75, 0x30, 0x00, 0x8f, 0x31, 0x22, 0x12, 0x22, 0xa0, -0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, 0xf0, 0x7f, 0x01, 0x12, 0x12, 0x19, 0x90, 0xff, -0xa6, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x54, 0xa0, 0x22, 0x12, 0x25, 0xd7, 0x8f, 0x4c, 0x7e, 0x00, -0xc3, 0xef, 0x95, 0x3c, 0xee, 0x95, 0x3b, 0x22, 0xf0, 0x7f, 0x01, 0x12, 0x12, 0x19, 0x90, 0xff, -0xb6, 0xe0, 0x90, 0xfa, 0xb8, 0xf0, 0x54, 0xa0, 0x22, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, -0x2c, 0x07, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x02, 0x31, 0x82, 0x8e, 0x39, 0x8f, 0x3a, 0x02, 0x2c, -0x07, 0x12, 0x22, 0xa0, 0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, 0x7d, 0x01, 0x12, 0x25, -0xd7, 0x90, 0xfa, 0xb1, 0xe0, 0x22, 0xef, 0x90, 0xf8, 0x04, 0xf0, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, -0xee, 0x60, 0x0a, 0xc0, 0x05, 0x7d, 0x7f, 0xdd, 0xfe, 0xde, 0xfa, 0xd0, 0x05, 0xef, 0xc3, 0x94, -0x15, 0x50, 0x03, 0xd0, 0xa8, 0x22, 0x13, 0x70, 0x03, 0xd0, 0xa8, 0x22, 0xff, 0xd5, 0x07, 0xfd, -0xd0, 0xa8, 0x22, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, 0xc0, 0x05, 0xe5, 0x3e, 0x24, -0x08, 0xf8, 0x86, 0x05, 0x53, 0x05, 0x7f, 0x7c, 0xff, 0x12, 0x10, 0x78, 0x7f, 0x00, 0x7e, 0x00, -0xe5, 0x43, 0x60, 0x46, 0xfc, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x7f, 0x6d, 0x70, 0x0f, 0xc0, 0x83, -0xc0, 0x82, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, -0xdc, 0xe6, 0x80, 0x26, 0xdc, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0x1e, 0xe0, 0xf8, 0xa3, 0xe0, -0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, -0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x12, 0x11, 0x0f, 0xd0, 0x05, 0xd0, -0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x85, 0xa8, 0x44, 0x75, 0xa8, 0x88, 0xec, 0x70, -0x02, 0x7c, 0x3f, 0x8c, 0x3d, 0x22, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 0x66, -0x80, 0xfb, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x04, 0xc0, 0x06, 0x7c, 0xff, 0x12, 0x10, -0x78, 0xe5, 0x43, 0x60, 0x42, 0xfe, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x7f, 0x6f, 0x70, 0x0b, 0xc0, -0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xde, 0xea, 0x80, -0x26, 0xde, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, 0xd8, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, -0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, -0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x78, 0x08, 0x08, 0x79, 0x18, 0x09, 0x7c, 0x01, 0xe6, -0x54, 0x7f, 0x6f, 0x70, 0x06, 0x76, 0x00, 0x77, 0x00, 0x80, 0x06, 0x08, 0x09, 0x0c, 0xbc, 0x08, -0xee, 0x12, 0x11, 0x0f, 0xd0, 0x06, 0xd0, 0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x75, -0x3d, 0x00, 0x85, 0x44, 0xa8, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc3, 0xe5, 0x43, 0x24, -0xe8, 0x50, 0x05, 0x12, 0x11, 0x66, 0x80, 0xf4, 0xef, 0x60, 0x31, 0x90, 0x30, 0x54, 0xe4, 0x93, -0xc3, 0x9f, 0x40, 0x2f, 0xc0, 0x04, 0x7c, 0xff, 0x12, 0x10, 0x78, 0xd0, 0x04, 0x43, 0x07, 0x80, -0xe5, 0x43, 0x75, 0xf0, 0x03, 0xa4, 0x24, 0x1b, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xef, -0xf0, 0xec, 0xa3, 0xf0, 0xed, 0xa3, 0xf0, 0x05, 0x43, 0x12, 0x11, 0x0f, 0xd0, 0x83, 0xd0, 0x82, -0xd0, 0xf0, 0x22, 0x02, 0x11, 0x94, 0xc0, 0x04, 0x7c, 0x20, 0xd2, 0x8c, 0xd2, 0x8d, 0xd5, 0x04, -0xfd, 0xd0, 0x04, 0x22, 0x75, 0xa8, 0x00, 0x75, 0x88, 0x00, 0x75, 0xb8, 0x00, 0x75, 0xf0, 0x00, -0x75, 0xd0, 0x00, 0xe4, 0xf8, 0x90, 0xf8, 0x04, 0xf0, 0x90, 0x00, 0x00, 0xf6, 0x08, 0xb8, 0x00, -0xfb, 0x02, 0x00, 0x00, 0xc2, 0xaf, 0xe4, 0x90, 0xff, 0x48, 0xf0, 0x90, 0xff, 0x50, 0xf0, 0x90, -0xff, 0x08, 0xf0, 0x90, 0xff, 0x10, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xa3, 0xa3, 0xf0, 0xd2, 0xb1, -0xc2, 0xb0, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, -0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0xd2, 0xb0, 0xd2, 0xb1, 0x7e, 0xff, 0x7f, 0xff, 0x12, -0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x0f, 0xdc, -0x80, 0xcc, 0xc3, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x03, 0x7f, 0xe8, 0xef, 0xf4, 0xff, 0xee, -0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x42, 0x8e, 0x41, 0x22, 0xc3, 0xef, 0x94, 0xbc, -0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x07, 0x7f, 0xd0, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, -0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x40, 0x8e, 0x3f, 0x22, 0xef, 0x70, 0x01, 0x22, 0xc0, 0x00, 0xc0, -0xa8, 0xc2, 0xaf, 0xe5, 0x3e, 0x24, 0x18, 0xf8, 0xa6, 0x07, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0xc6, -0x54, 0x7f, 0xf6, 0xd0, 0xa8, 0xe6, 0x30, 0xe7, 0x03, 0xd0, 0x00, 0x22, 0x12, 0x11, 0x66, 0x80, -0xf4, 0xc0, 0x00, 0x7f, 0x01, 0xef, 0x24, 0x08, 0xf8, 0xe6, 0x60, 0x09, 0x0f, 0xbf, 0x08, 0xf5, -0x12, 0x11, 0x66, 0x80, 0xee, 0xd0, 0x00, 0x22, 0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, -0xc0, 0x06, 0xc0, 0x04, 0xed, 0x24, 0x10, 0xf8, 0x76, 0x9a, 0xed, 0x75, 0xf0, 0x21, 0xa4, 0x24, -0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, 0xc0, 0x82, 0xc0, 0x83, 0xa3, 0xa3, 0xe4, 0x78, -0x0d, 0xf0, 0xa3, 0xd8, 0xfc, 0xef, 0x54, 0x7f, 0x75, 0xf0, 0x02, 0xa4, 0x24, 0x36, 0xf5, 0x82, -0xe5, 0xf0, 0x34, 0x30, 0xf5, 0x83, 0xe4, 0x93, 0xfe, 0x74, 0x01, 0x93, 0xfc, 0xd0, 0x83, 0xd0, -0x82, 0xec, 0xf0, 0xa3, 0xee, 0xf0, 0xed, 0x24, 0x08, 0xf8, 0xef, 0x44, 0x80, 0xf6, 0xd0, 0x04, -0xd0, 0x06, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x75, 0x3e, 0x00, 0x75, 0x43, -0x00, 0x7a, 0x08, 0x79, 0x18, 0x78, 0x08, 0x76, 0x00, 0x77, 0x00, 0x08, 0x09, 0xda, 0xf8, 0x90, -0xf8, 0x04, 0xe0, 0xfc, 0x90, 0x30, 0x54, 0xe4, 0x93, 0xc3, 0x9c, 0x50, 0x05, 0xe4, 0x90, 0xf8, -0x04, 0xf0, 0x78, 0x08, 0x74, 0x80, 0x44, 0x7f, 0xf6, 0x74, 0x01, 0x44, 0x10, 0xf5, 0x89, 0x75, -0xb8, 0x00, 0xd2, 0xab, 0xd2, 0xa9, 0x22, 0x75, 0x81, 0x8b, 0xd2, 0x8e, 0xd2, 0x8c, 0xd2, 0xaf, -0xe5, 0x43, 0x60, 0x36, 0xff, 0x90, 0xf9, 0x1b, 0xe0, 0x54, 0x80, 0x60, 0x28, 0x78, 0x08, 0x79, -0x08, 0xe0, 0x54, 0x7f, 0xfa, 0x7b, 0x00, 0xe6, 0x54, 0x7f, 0xb5, 0x02, 0x02, 0x7b, 0xff, 0x08, -0xd9, 0xf5, 0xeb, 0x70, 0x10, 0xea, 0xf0, 0xc0, 0x07, 0x12, 0x12, 0x41, 0xad, 0x07, 0xaf, 0x02, -0x12, 0x12, 0x58, 0xd0, 0x07, 0xa3, 0xa3, 0xa3, 0xdf, 0xce, 0x12, 0x11, 0x66, 0x80, 0xc1, 0x8f, -0x24, 0x12, 0x2a, 0x06, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x08, 0x12, -0x21, 0xf3, 0xe0, 0xfd, 0x12, 0x22, 0x8a, 0x8a, 0x83, 0x24, 0x0a, 0x12, 0x21, 0xf3, 0xed, 0xf0, -0x12, 0x22, 0x56, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xe0, 0xff, 0x12, 0x22, 0x99, 0x24, 0x09, 0x12, -0x21, 0xf3, 0xef, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe4, 0x20, 0x08, 0x12, 0x22, 0x09, 0xc0, -0x83, 0xc0, 0x82, 0xa3, 0xe0, 0x25, 0xe0, 0xff, 0x05, 0x82, 0xd5, 0x82, 0x02, 0x15, 0x83, 0x15, -0x82, 0xe0, 0x33, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0xa3, 0xef, 0xf0, 0x78, 0x80, 0x12, 0x22, 0x09, -0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0xff, 0x12, 0x22, 0x8a, 0x8a, 0x83, 0x24, 0x08, 0x12, 0x21, -0xf3, 0xef, 0xf0, 0xed, 0x12, 0x22, 0x99, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xed, 0xf0, 0x12, 0x21, -0xfb, 0xe0, 0xff, 0x30, 0xe7, 0x19, 0x12, 0x22, 0x6e, 0x12, 0x21, 0xf3, 0xe0, 0x60, 0x09, 0x12, -0x21, 0xfb, 0xef, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x12, 0x21, 0xfb, 0xef, 0x54, 0xfd, 0xf0, 0x78, -0x7e, 0x12, 0x22, 0x09, 0xa3, 0xa3, 0xe0, 0xff, 0x53, 0x07, 0xc7, 0x08, 0xe6, 0xfc, 0x08, 0xe6, -0xfd, 0x12, 0x22, 0x43, 0xa3, 0xe0, 0x30, 0xe3, 0x12, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, -0x05, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x31, 0x94, 0x93, 0x42, 0x07, 0x53, 0x07, 0xfb, 0x78, 0x80, -0xe6, 0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x24, 0x06, 0x12, 0x21, 0xf3, 0xe0, 0x60, 0x03, 0x43, 0x07, -0x04, 0x53, 0x07, 0xfc, 0x78, 0x80, 0x12, 0x22, 0x7a, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0x42, -0x07, 0x43, 0x07, 0x80, 0x12, 0x22, 0x8a, 0xf5, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xef, 0xf0, 0x12, -0x22, 0x99, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0xff, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xe0, -0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe1, 0x05, 0x53, 0x07, 0xdf, 0x80, 0x03, 0x43, 0x07, 0x20, 0xec, -0x30, 0xe4, 0x05, 0x53, 0x07, 0xef, 0x80, 0x03, 0x43, 0x07, 0x10, 0x12, 0x21, 0xfb, 0xe0, 0xfe, -0x54, 0x03, 0x60, 0x73, 0x53, 0x07, 0xdf, 0xee, 0x30, 0xe1, 0x69, 0x78, 0x80, 0x12, 0x22, 0x6f, -0x12, 0x21, 0xf3, 0xe0, 0x12, 0x1b, 0x4c, 0x14, 0xa6, 0x00, 0x14, 0xda, 0x01, 0x14, 0xdf, 0x03, -0x14, 0xda, 0x05, 0x14, 0xdf, 0x07, 0x14, 0xda, 0x09, 0x14, 0xdf, 0x0b, 0x14, 0xda, 0x0d, 0x14, -0xdf, 0x0f, 0x00, 0x00, 0x14, 0xe7, 0xe5, 0x24, 0x64, 0x03, 0x70, 0x21, 0x90, 0xf9, 0x15, 0xe0, -0x30, 0xe2, 0x0d, 0x30, 0xb4, 0x05, 0x43, 0x07, 0x02, 0x80, 0x2c, 0x53, 0x07, 0xfd, 0x80, 0x27, -0x30, 0x95, 0x05, 0x43, 0x07, 0x02, 0x80, 0x1f, 0x53, 0x07, 0xfd, 0x80, 0x1a, 0x30, 0x93, 0x05, -0x43, 0x07, 0x02, 0x80, 0x12, 0x53, 0x07, 0xfd, 0x80, 0x0d, 0x43, 0x07, 0x02, 0x80, 0x08, 0x53, -0x07, 0xfd, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x12, 0x22, 0x78, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xef, -0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xff, 0x12, 0x21, 0xfb, 0xe0, 0xfe, 0x54, -0x03, 0x70, 0x03, 0x02, 0x15, 0xd7, 0xee, 0x20, 0xe1, 0x03, 0x02, 0x15, 0xd4, 0x12, 0x22, 0x6e, -0x12, 0x21, 0xf3, 0xe0, 0x12, 0x1b, 0x4c, 0x15, 0x36, 0x00, 0x15, 0x6c, 0x01, 0x15, 0x6c, 0x03, -0x15, 0xa0, 0x05, 0x15, 0xa0, 0x07, 0x15, 0x86, 0x09, 0x15, 0x86, 0x0b, 0x15, 0xba, 0x0d, 0x15, -0xba, 0x0f, 0x00, 0x00, 0x15, 0xd7, 0xe5, 0x24, 0x64, 0x03, 0x70, 0x23, 0x90, 0xf9, 0x15, 0xe0, -0x30, 0xe2, 0x0f, 0x30, 0xb1, 0x06, 0x53, 0x07, 0x7f, 0x02, 0x15, 0xd7, 0x43, 0x07, 0x80, 0x02, -0x15, 0xd7, 0x30, 0x94, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x7d, 0x43, 0x07, 0x80, 0x80, 0x78, 0x30, -0x92, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x70, 0x43, 0x07, 0x80, 0x80, 0x6b, 0xe5, 0x24, 0xb4, 0x03, -0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xdf, -0xf0, 0x53, 0x07, 0x7f, 0x80, 0x51, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x44, -0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x53, 0x07, 0x7f, 0x80, 0x37, -0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, -0x9e, 0xe0, 0x54, 0xdf, 0xf0, 0x43, 0x07, 0x80, 0x80, 0x1d, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, -0xff, 0x9e, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x43, -0x07, 0x80, 0x80, 0x03, 0x53, 0x07, 0x7f, 0x78, 0x80, 0x12, 0x22, 0x3f, 0xe0, 0xfc, 0xa3, 0xe0, -0xfd, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x20, 0x80, 0x03, 0x53, 0x07, 0xdf, 0xec, 0x30, 0xe3, 0x05, -0x43, 0x07, 0x40, 0x80, 0x03, 0x53, 0x07, 0xbf, 0xec, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x10, 0x80, -0x03, 0x53, 0x07, 0xef, 0xed, 0x30, 0xe4, 0x05, 0x43, 0x07, 0x08, 0x80, 0x03, 0x53, 0x07, 0xf7, -0xed, 0x30, 0xe5, 0x05, 0x43, 0x07, 0x04, 0x80, 0x03, 0x53, 0x07, 0xfb, 0xed, 0x30, 0xe6, 0x05, -0x43, 0x07, 0x01, 0x80, 0x03, 0x53, 0x07, 0xfe, 0xed, 0x30, 0xe7, 0x05, 0x43, 0x07, 0x02, 0x80, -0x03, 0x53, 0x07, 0xfd, 0x78, 0x7e, 0x12, 0x22, 0x3f, 0xa3, 0xef, 0xf0, 0x12, 0x31, 0xc7, 0x7f, -0x00, 0x22, 0x90, 0xff, 0xfa, 0x74, 0x08, 0xf0, 0xa3, 0x74, 0x16, 0xf0, 0x90, 0xff, 0xf9, 0x74, -0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcc, 0xe4, 0xfd, 0x12, 0x22, 0xa0, 0x90, 0xfa, 0xcc, -0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1a, 0x6c, 0x12, 0x18, 0xe2, 0xe5, 0x23, 0x30, 0xe7, 0x02, 0xd2, -0x02, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x24, 0x90, 0xfa, 0xcc, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, -0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x7b, -0x00, 0x7a, 0x00, 0x79, 0x23, 0x75, 0x2d, 0x00, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0xe5, -0x23, 0x24, 0x80, 0x90, 0xff, 0xf8, 0xf0, 0xe5, 0x23, 0x64, 0x07, 0x60, 0x1e, 0xe5, 0x23, 0x64, -0x06, 0x60, 0x18, 0xe5, 0x23, 0x64, 0x14, 0x60, 0x12, 0xe5, 0x23, 0x64, 0x41, 0x60, 0x0c, 0xe5, -0x23, 0x64, 0x1a, 0x70, 0x46, 0xe5, 0x24, 0x64, 0x02, 0x70, 0x40, 0xe5, 0x23, 0xb4, 0x07, 0x16, -0xd2, 0x94, 0xd2, 0x95, 0xd2, 0x92, 0xd2, 0x93, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x02, 0xf0, 0xa3, -0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1e, 0xe5, 0x23, 0xb4, 0x41, 0x12, 0x90, 0xf9, 0x15, 0xe0, 0x44, -0x06, 0xf0, 0xa3, 0xe0, 0x44, 0x06, 0xf0, 0xd2, 0xb1, 0xd2, 0xb4, 0x80, 0x07, 0x90, 0xf9, 0x15, -0xe0, 0x44, 0x01, 0xf0, 0x90, 0xf9, 0x16, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x23, 0x64, 0x42, 0x60, -0x05, 0xe5, 0x23, 0xb4, 0x43, 0x0c, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x80, 0xf0, 0xa3, 0xe0, 0x44, -0x80, 0xf0, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, 0x74, 0x0d, 0xf0, 0x12, 0x18, 0xe2, 0x90, 0xff, -0xf5, 0xe5, 0x23, 0xf0, 0xe4, 0xf5, 0x35, 0xf5, 0x33, 0xf5, 0x34, 0xf5, 0x32, 0x12, 0x1d, 0x84, -0x12, 0x1c, 0x30, 0x12, 0x1d, 0x8b, 0x90, 0xf9, 0x67, 0x12, 0x1b, 0x43, 0x90, 0xf9, 0x6c, 0x12, -0x1b, 0x43, 0x90, 0xff, 0xff, 0xe4, 0xf0, 0x90, 0xff, 0x83, 0xe0, 0xe4, 0xf0, 0x90, 0xff, 0x81, +0xc0, 0x06, 0xc0, 0x07, 0x74, 0x15, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x60, 0x23, 0x74, +0x66, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x14, 0xf0, 0x70, 0x16, 0x74, 0xff, 0xf0, 0x74, +0x1c, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x60, 0x04, 0x14, 0xf0, 0x70, 0x04, 0xc2, 0x90, +0x80, 0xfc, 0x90, 0xff, 0x93, 0x74, 0x81, 0xf0, 0xe5, 0x81, 0x94, 0xfd, 0x40, 0x03, 0x02, 0x11, +0xdc, 0x85, 0x41, 0x8d, 0x85, 0x42, 0x8b, 0x74, 0xb2, 0xf5, 0x82, 0x74, 0xfa, 0xf5, 0x83, 0xe0, +0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x4a, 0xe0, 0x30, 0xe7, 0x2c, 0x90, 0xff, +0x4e, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x20, 0xb4, 0x02, +0x1d, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x28, 0x4e, 0x80, +0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x04, 0xd0, 0x83, 0xd0, 0x82, 0xa3, 0xe0, +0xb4, 0x01, 0x1b, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x52, 0xe0, 0x30, 0xe7, 0x2c, 0x90, 0xff, +0x56, 0xe0, 0x30, 0xe7, 0x25, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x02, 0xf0, 0x80, 0x25, 0xb4, 0x02, +0x22, 0xc0, 0x82, 0xc0, 0x83, 0x90, 0xff, 0x7a, 0xe0, 0x30, 0xe7, 0x05, 0x12, 0x28, 0x4e, 0x80, +0x09, 0xd0, 0x83, 0xd0, 0x82, 0x74, 0x03, 0xf0, 0x80, 0x09, 0xd0, 0x83, 0xd0, 0x82, 0x80, 0x03, +0x02, 0x02, 0x90, 0x74, 0x16, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x20, 0x04, 0xf1, 0x20, +0x02, 0x03, 0x30, 0x01, 0xeb, 0x74, 0x19, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xe0, 0x14, 0xfc, +0xf0, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0x64, 0x04, 0x70, 0x0f, 0xec, 0x70, 0x62, 0x7e, 0x01, +0x12, 0x00, 0xc9, 0x7c, 0x0a, 0x7d, 0xfa, 0x02, 0x02, 0x61, 0x12, 0x00, 0xc9, 0xee, 0x64, 0x04, +0x60, 0x1d, 0xec, 0x70, 0x4b, 0x7c, 0x0a, 0xed, 0x14, 0xfd, 0x70, 0x15, 0xee, 0x64, 0x02, 0x60, +0x07, 0x7e, 0x02, 0x7d, 0x32, 0x02, 0x02, 0x61, 0x7e, 0x01, 0x7d, 0xfa, 0x02, 0x02, 0x61, 0x7c, +0x0a, 0x74, 0x19, 0xf5, 0x82, 0x74, 0xf9, 0xf5, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, 0xa3, 0xee, +0xf0, 0x14, 0x60, 0x18, 0x20, 0xe1, 0x0f, 0x20, 0x01, 0x06, 0xd2, 0xb1, 0xc2, 0xb0, 0x80, 0x10, +0xc2, 0xb1, 0xd2, 0xb0, 0x80, 0x0a, 0xc2, 0xb1, 0xc2, 0xb0, 0x80, 0x04, 0xd2, 0xb0, 0xd2, 0xb1, +0x78, 0x19, 0x79, 0x09, 0x7a, 0x07, 0xe7, 0x70, 0x04, 0xa6, 0x00, 0x80, 0x0b, 0xe6, 0x60, 0x08, +0x16, 0xe6, 0x70, 0x04, 0xe7, 0x44, 0x80, 0xf7, 0x08, 0x09, 0xda, 0xea, 0xe5, 0x3d, 0x60, 0x13, +0x14, 0xf5, 0x3d, 0x70, 0x0e, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 0x57, 0xd2, +0x8c, 0xd2, 0x8d, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, +0x01, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0xd0, 0xd0, 0xd0, 0xe0, 0x32, 0x90, 0xff, +0x04, 0xe0, 0x90, 0xfa, 0xb9, 0xf0, 0x90, 0xff, 0x06, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, 0xff, +0xea, 0xfe, 0xef, 0xc3, 0x94, 0x08, 0xee, 0x94, 0x01, 0x50, 0x02, 0x80, 0x04, 0x7e, 0x01, 0x7f, +0x08, 0x8e, 0x3b, 0x8f, 0x3c, 0x90, 0xff, 0x02, 0xe0, 0xfc, 0xa3, 0xe0, 0xfa, 0xec, 0xff, 0xea, +0x90, 0xfa, 0xbd, 0xf0, 0xef, 0xa3, 0xf0, 0x12, 0x1c, 0xe0, 0xe4, 0xf5, 0x4d, 0xe5, 0x4d, 0xc3, +0x94, 0x02, 0x50, 0x0f, 0x12, 0x1c, 0xc1, 0xe4, 0x12, 0x1a, 0xe8, 0x05, 0x4d, 0x04, 0x12, 0x1c, +0xb2, 0x80, 0xea, 0x12, 0x1c, 0xe0, 0x90, 0xff, 0x00, 0xe0, 0xff, 0x54, 0x60, 0x24, 0xc0, 0x70, +0x03, 0x02, 0x08, 0xf3, 0x24, 0x40, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0xfe, +0x54, 0x0f, 0xf5, 0x4d, 0xee, 0x30, 0xe7, 0x03, 0xd3, 0x80, 0x01, 0xc3, 0x92, 0x0a, 0x90, 0xff, +0x01, 0xe0, 0x12, 0x1b, 0xfc, 0x03, 0x84, 0x00, 0x04, 0x57, 0x01, 0x05, 0x6a, 0x03, 0x06, 0x31, +0x05, 0x06, 0x73, 0x06, 0x07, 0xd5, 0x08, 0x08, 0x1d, 0x09, 0x08, 0x79, 0x0a, 0x08, 0xb9, 0x0b, +0x00, 0x00, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, +0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x64, 0x02, 0x45, 0x3b, 0x60, +0x03, 0x02, 0x0f, 0x6e, 0xef, 0x54, 0x1f, 0x14, 0x60, 0x2b, 0x14, 0x60, 0x47, 0x24, 0x02, 0x60, +0x03, 0x02, 0x0f, 0x6e, 0xee, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1c, 0xc1, 0x74, 0x01, 0x12, +0x1a, 0xe8, 0x78, 0x67, 0xe6, 0x30, 0xe0, 0x08, 0x12, 0x1c, 0xc1, 0x74, 0x02, 0x12, 0x1a, 0xe8, +0x7f, 0x02, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x09, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03, +0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0xd3, 0x94, 0x01, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0x7f, +0x02, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x0e, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x60, 0x07, +0x64, 0x80, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x0f, 0xfa, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0xe5, +0x4d, 0x70, 0x19, 0x30, 0x0a, 0x0b, 0x90, 0xff, 0x80, 0x12, 0x1c, 0xbe, 0x12, 0x1a, 0xe8, 0x80, +0x24, 0x90, 0xff, 0x82, 0x12, 0x1c, 0xbe, 0x12, 0x1a, 0xe8, 0x80, 0x19, 0x15, 0x4d, 0x30, 0x0a, +0x0b, 0x12, 0x1d, 0x55, 0x12, 0x1c, 0xbc, 0x12, 0x1a, 0xe8, 0x80, 0x09, 0x12, 0x1d, 0x63, 0x12, +0x1c, 0xbc, 0x12, 0x1a, 0xe8, 0x12, 0x1c, 0xc1, 0x12, 0x1a, 0xa2, 0x60, 0x05, 0x74, 0x01, 0x12, +0x1a, 0xe8, 0x7f, 0x02, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, +0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x14, 0x60, 0x2d, 0x14, 0x60, +0x59, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x04, 0xa3, 0xe0, +0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, +0x78, 0x67, 0xe6, 0x54, 0xfe, 0xf6, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x06, +0x20, 0xe0, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe0, 0x09, 0x90, 0xfa, 0xb9, 0xe0, 0x60, +0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe1, 0x0c, 0x90, 0xfa, 0xb9, 0xe0, 0xd3, 0x94, 0x01, +0x40, 0x03, 0x02, 0x0f, 0x6e, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x02, +0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x0f, 0xfa, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0xe5, +0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe0, 0x07, 0xe5, +0x4d, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x4d, 0x70, 0x0f, 0x90, 0xff, 0x82, 0xe0, 0x54, 0xf7, +0xf0, 0x90, 0xff, 0x80, 0xe0, 0x54, 0xf7, 0xf0, 0x22, 0xe5, 0x4d, 0x24, 0xfe, 0x60, 0x20, 0x24, +0xfb, 0x60, 0x34, 0x24, 0x06, 0x70, 0x35, 0x30, 0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, +0x03, 0x12, 0x2e, 0x79, 0x80, 0x26, 0xe4, 0xfd, 0x7f, 0x03, 0x12, 0x2e, 0x79, 0x80, 0x1d, 0x30, +0x0a, 0x0c, 0xa2, 0x0a, 0xe4, 0x33, 0xfd, 0x7f, 0x04, 0x12, 0x2e, 0x79, 0x80, 0x0e, 0xe4, 0xfd, +0x7f, 0x04, 0x12, 0x2e, 0x79, 0x80, 0x05, 0x7f, 0x87, 0x12, 0x31, 0xef, 0x15, 0x4d, 0x30, 0x0a, +0x0b, 0x12, 0x1d, 0x55, 0xf5, 0x83, 0xe0, 0x54, 0xf7, 0xf0, 0x80, 0x09, 0x12, 0x1d, 0x63, 0xf5, +0x83, 0xe0, 0x54, 0xf7, 0xf0, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, +0x0f, 0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x14, 0x60, +0x2d, 0x14, 0x60, 0x55, 0x24, 0x02, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, +0x04, 0xa3, 0xe0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03, +0x02, 0x0f, 0x6e, 0x78, 0x67, 0xe6, 0x44, 0x01, 0xf6, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0xe5, 0x35, +0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe0, 0x07, 0xe5, 0x4d, +0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe1, 0x0a, 0xe5, 0x4d, 0xd3, 0x94, 0x01, 0x40, +0x03, 0x02, 0x0f, 0x6e, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x02, 0xa3, +0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x12, 0x32, 0x3f, 0x40, 0x03, +0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x06, 0x20, 0xe0, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x4d, +0x70, 0x09, 0x30, 0x0a, 0x03, 0x02, 0x1e, 0x14, 0x02, 0x1d, 0xdf, 0xe5, 0x35, 0x20, 0xe1, 0x03, +0x02, 0x0f, 0x6e, 0x15, 0x4d, 0x30, 0x0a, 0x0b, 0x12, 0x1d, 0x55, 0xf5, 0x83, 0xe0, 0x44, 0x08, +0xf0, 0x80, 0x09, 0x12, 0x1d, 0x63, 0xf5, 0x83, 0xe0, 0x44, 0x08, 0xf0, 0xe4, 0xff, 0x02, 0x32, +0x6e, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, +0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x60, 0x03, +0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x30, 0xe1, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbe, 0xe0, 0x90, +0xff, 0xff, 0xf0, 0xe0, 0x60, 0x05, 0x43, 0x35, 0x01, 0x80, 0x03, 0x53, 0x35, 0xfe, 0xe4, 0xff, +0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x70, +0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, +0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0x24, 0xfe, 0x60, 0x3a, 0x14, 0x60, 0x75, 0x24, 0x02, 0x60, 0x03, +0x02, 0x0f, 0x6e, 0xed, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1c, 0xe0, 0x12, 0x1e, 0x0d, 0x7d, +0x03, 0x12, 0x0f, 0xb5, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x0f, 0x72, 0x90, 0xfa, 0xb6, 0xe0, +0xfd, 0xa3, 0x12, 0x1d, 0x2b, 0x12, 0x0f, 0xd1, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, +0x02, 0x10, 0x02, 0x12, 0x1c, 0xe0, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe4, 0x0d, 0x12, 0x1e, 0x0d, +0x7d, 0x14, 0x12, 0x0f, 0xb5, 0x60, 0x10, 0x02, 0x0f, 0x6e, 0x12, 0x1e, 0x0d, 0x7d, 0x04, 0x12, +0x10, 0x09, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x0f, 0x72, 0x90, 0xfa, 0xb6, 0xe0, 0xfd, 0xa3, +0x12, 0x1d, 0x2b, 0x12, 0x0f, 0xd1, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 0x02, 0x10, +0x02, 0x12, 0x1e, 0x0d, 0x7d, 0x05, 0x12, 0x10, 0x09, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x7b, 0x01, +0x7a, 0xfa, 0x79, 0xb6, 0x12, 0x1d, 0x28, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xb7, 0xe4, +0x75, 0xf0, 0x03, 0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xbe, 0xe0, 0x90, 0xfa, 0xb5, 0xf0, 0xe4, 0xf5, +0x4c, 0x90, 0xfa, 0xb5, 0xe0, 0xff, 0xe5, 0x4c, 0xc3, 0x9f, 0x50, 0x24, 0x12, 0x1d, 0x22, 0x12, +0x10, 0x14, 0xff, 0xfd, 0x90, 0xfa, 0xb7, 0xe4, 0x8d, 0xf0, 0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xb6, +0xe0, 0xc3, 0x9f, 0xf0, 0xd3, 0x94, 0x00, 0x50, 0x03, 0x02, 0x0f, 0x6e, 0x05, 0x4c, 0x80, 0xd1, +0x12, 0x1d, 0x22, 0x12, 0x10, 0x14, 0x24, 0xfe, 0xff, 0x90, 0xfa, 0xb6, 0xf0, 0xfd, 0xa3, 0xe4, +0x75, 0xf0, 0x02, 0x12, 0x1b, 0x1c, 0x7a, 0xf9, 0x79, 0x72, 0x7b, 0x01, 0x8b, 0x36, 0x8a, 0x37, +0x89, 0x38, 0xe9, 0x24, 0x02, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, 0x1d, 0x28, 0x12, 0x26, 0x98, 0x8f, +0x4c, 0x05, 0x4c, 0x05, 0x4c, 0x12, 0x1c, 0xc1, 0xe5, 0x4c, 0x12, 0x1a, 0xe8, 0x12, 0x1c, 0xc1, +0x90, 0x00, 0x01, 0x74, 0x03, 0x12, 0x1a, 0xfa, 0xaf, 0x4c, 0x7e, 0x00, 0xc3, 0xef, 0x95, 0x3c, +0xee, 0x95, 0x3b, 0x50, 0x02, 0x80, 0x04, 0xae, 0x3b, 0xaf, 0x3c, 0x8e, 0x39, 0x8f, 0x3a, 0x02, +0x2c, 0xd8, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x64, +0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, 0x60, 0x03, 0x02, 0x0f, +0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, +0x79, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0f, +0x6e, 0x75, 0x36, 0x00, 0x75, 0x37, 0x00, 0x75, 0x38, 0x32, 0x02, 0x0f, 0xf1, 0xe5, 0x35, 0x30, +0xe7, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, +0xb9, 0xe0, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xd3, 0x90, 0xfa, 0xbe, 0xe0, 0x94, 0x01, 0x90, 0xfa, +0xbd, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x60, 0x03, 0x02, 0x0f, +0x6e, 0xe5, 0x35, 0x20, 0xe0, 0x06, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbe, 0xe0, +0xf5, 0x32, 0xe5, 0x32, 0x70, 0x08, 0x43, 0x35, 0x01, 0x53, 0x35, 0xfd, 0x80, 0x06, 0x53, 0x35, +0xfe, 0x43, 0x35, 0x02, 0xe4, 0xff, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x20, 0xe7, 0x03, 0x02, 0x0f, +0x6e, 0xe5, 0x3c, 0x64, 0x01, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xb9, 0xe0, +0x60, 0x03, 0x02, 0x0f, 0x6e, 0x90, 0xfa, 0xbd, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x03, 0x02, +0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x64, 0x01, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe1, +0x03, 0x02, 0x0f, 0x6e, 0x7f, 0x01, 0x02, 0x32, 0x6e, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, +0x6e, 0xe5, 0x3c, 0x45, 0x3b, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xd3, 0x90, 0xfa, 0xbe, 0xe0, 0x94, +0x00, 0x90, 0xfa, 0xbd, 0xe0, 0x94, 0x00, 0x40, 0x03, 0x02, 0x0f, 0x6e, 0x12, 0x1d, 0x79, 0x64, +0x01, 0x60, 0x03, 0x02, 0x0f, 0x6e, 0xe5, 0x35, 0x20, 0xe1, 0x03, 0x02, 0x0f, 0x6e, 0xe4, 0xff, +0x02, 0x32, 0x6e, 0x90, 0xff, 0x01, 0x12, 0x1e, 0x24, 0xef, 0x12, 0x1a, 0xe8, 0x90, 0xfa, 0xb9, +0x12, 0x1e, 0x24, 0x90, 0x00, 0x01, 0xef, 0x12, 0x1a, 0xfa, 0x90, 0x00, 0x02, 0xe4, 0x12, 0x1a, +0xfa, 0x74, 0x03, 0x12, 0x1c, 0xb2, 0x90, 0xfa, 0xbd, 0xe0, 0xff, 0xa3, 0xe0, 0x85, 0x38, 0x82, +0x85, 0x37, 0x83, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xff, 0x01, 0xe0, 0x12, 0x1b, 0xfc, 0x09, +0x7b, 0x02, 0x09, 0x9d, 0x04, 0x09, 0xbf, 0x05, 0x09, 0xeb, 0x06, 0x0a, 0x09, 0x07, 0x0a, 0x27, +0x08, 0x0a, 0x45, 0x09, 0x0a, 0x63, 0x0b, 0x0b, 0x18, 0x80, 0x0d, 0xb7, 0x81, 0x0d, 0xe8, 0x82, +0x0b, 0x5f, 0x83, 0x0b, 0xa8, 0x84, 0x0b, 0xc7, 0x85, 0x0c, 0x0c, 0x86, 0x0c, 0x57, 0x87, 0x0c, +0xe8, 0x88, 0x0d, 0x73, 0x89, 0x0a, 0x81, 0x92, 0x0a, 0x81, 0x93, 0x0d, 0xa0, 0xb0, 0x0e, 0x9b, +0xc0, 0x0e, 0xc7, 0xc1, 0x0e, 0xd8, 0xc2, 0x00, 0x00, 0x0f, 0x5d, 0xe5, 0x35, 0x20, 0xe7, 0x05, +0x7f, 0x05, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, +0x00, 0x7f, 0x07, 0x02, 0x11, 0x5e, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0xb4, 0xe5, 0x35, 0x20, +0xe7, 0x05, 0x7f, 0x05, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, +0xfd, 0x7c, 0x00, 0x7f, 0x0c, 0x02, 0x11, 0x5e, 0xe4, 0xfd, 0x7f, 0x07, 0x02, 0x2f, 0xb4, 0xe5, +0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x71, 0x12, 0x1e, 0x42, 0x50, 0x06, 0xe5, 0x3c, 0x45, 0x3b, +0x70, 0x05, 0x7f, 0x02, 0x02, 0x31, 0xa9, 0x90, 0xfa, 0xb9, 0xe0, 0x24, 0xfe, 0x24, 0xfd, 0x50, +0x02, 0x80, 0x03, 0x02, 0x32, 0x2c, 0x7f, 0x07, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, +0x02, 0x0f, 0x71, 0x12, 0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, +0x08, 0x02, 0x11, 0x5e, 0x7f, 0x07, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, +0x71, 0x12, 0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x09, 0x02, +0x11, 0x5e, 0x7f, 0x07, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x71, 0x12, +0x1d, 0x71, 0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0a, 0x02, 0x11, 0x5e, +0x7f, 0x07, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x71, 0x12, 0x1d, 0x71, +0x60, 0x03, 0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0b, 0x02, 0x11, 0x5e, 0x7f, 0x07, +0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x03, 0x02, 0x0f, 0x71, 0x12, 0x1d, 0x71, 0x60, 0x03, +0x04, 0x70, 0x09, 0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0e, 0x02, 0x11, 0x5e, 0x7f, 0x07, 0x02, 0x31, +0xa9, 0xe5, 0x35, 0x30, 0xe7, 0x56, 0x12, 0x1d, 0x79, 0x70, 0x4a, 0x90, 0xff, 0x02, 0xe0, 0xf5, +0x4c, 0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, 0xe5, 0x4c, 0xb4, 0x83, 0x05, +0x75, 0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, 0xf5, 0x4c, 0x12, 0x1c, 0x22, +0x12, 0x1e, 0x3b, 0x12, 0x25, 0xfa, 0x12, 0x1d, 0x89, 0x12, 0x1a, 0xbb, 0x60, 0x05, 0x12, 0x32, +0x7a, 0x80, 0x06, 0x85, 0x33, 0x39, 0x85, 0x34, 0x3a, 0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, +0x38, 0x75, 0x02, 0x2c, 0xd8, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 0xb4, 0x12, 0x1d, 0x79, 0x60, +0x05, 0x7f, 0x05, 0x02, 0x31, 0xa9, 0x12, 0x1e, 0x42, 0x40, 0x05, 0x7f, 0x03, 0x02, 0x31, 0xa9, +0x90, 0xff, 0x02, 0xe0, 0xf5, 0x4c, 0xe5, 0x4c, 0xb4, 0x82, 0x05, 0x75, 0x4c, 0x61, 0x80, 0x12, +0xe5, 0x4c, 0xb4, 0x83, 0x05, 0x75, 0x4c, 0x62, 0x80, 0x08, 0xe5, 0x4c, 0xc4, 0x54, 0xf0, 0x04, +0xf5, 0x4c, 0x12, 0x1c, 0x22, 0x02, 0x32, 0x2c, 0x12, 0x1e, 0x4c, 0x12, 0x2a, 0xc7, 0x12, 0x1d, +0x33, 0xe0, 0x54, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0xe0, 0x90, 0xfa, 0xba, 0xf0, 0x78, 0x68, 0x12, +0x1b, 0xd8, 0x90, 0x00, 0x02, 0x12, 0x1a, 0xbb, 0x30, 0xe7, 0xf2, 0x90, 0x00, 0x02, 0xe4, 0x12, +0x1a, 0xfa, 0x90, 0xfa, 0xba, 0xe0, 0x44, 0x80, 0xff, 0xf0, 0x78, 0x7c, 0xe6, 0xfc, 0x08, 0xe6, +0x8c, 0x83, 0x12, 0x1d, 0x3b, 0xef, 0xf0, 0x12, 0x32, 0x84, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x90, +0xfa, 0xb9, 0xe0, 0x64, 0x01, 0x70, 0x1f, 0x90, 0xfa, 0xbd, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, +0xa3, 0xe0, 0xf5, 0x90, 0x80, 0x2d, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0x90, 0x90, 0xfa, 0xbe, 0xe0, +0x42, 0x90, 0xd2, 0xaf, 0x80, 0x1d, 0x90, 0xfa, 0xbd, 0xe0, 0xff, 0x7e, 0x00, 0x70, 0x06, 0xa3, +0xe0, 0xf5, 0xb0, 0x80, 0x0e, 0xc2, 0xaf, 0xef, 0xf4, 0x52, 0xb0, 0x90, 0xfa, 0xbe, 0xe0, 0x42, +0xb0, 0xd2, 0xaf, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1c, 0xe0, 0x90, 0xfa, 0xb9, 0xe0, 0xb4, +0x01, 0x0a, 0x12, 0x1c, 0xc1, 0xe5, 0x90, 0x12, 0x1a, 0xe8, 0x80, 0x08, 0x12, 0x1c, 0xc1, 0xe5, +0xb0, 0x12, 0x1a, 0xe8, 0x02, 0x0f, 0xf1, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x24, 0x13, 0x12, 0x1c, +0xf1, 0x20, 0xe1, 0x33, 0x12, 0x1d, 0x80, 0xef, 0x24, 0xfc, 0x60, 0x18, 0x04, 0x70, 0x28, 0x90, +0xfa, 0xba, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x19, 0x12, 0x1e, +0x56, 0xf0, 0x80, 0x13, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, +0xf0, 0x80, 0x04, 0x12, 0x1e, 0x5d, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x90, 0xfa, 0xb9, 0xe0, +0xff, 0x24, 0x13, 0x12, 0x1c, 0xf1, 0x20, 0xe1, 0x39, 0x12, 0x1d, 0x80, 0xef, 0x24, 0xfc, 0x60, +0x1b, 0x04, 0x70, 0x2e, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, +0xf0, 0x80, 0x1f, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x16, 0x90, 0xfa, 0xba, 0xe0, +0x60, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, +0xdf, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x80, 0x12, 0x1d, 0x71, 0x60, 0x4d, 0x04, +0x60, 0x03, 0x02, 0x0c, 0xe3, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x0f, 0x90, 0xff, 0xa4, 0x12, 0x1c, +0xea, 0x30, 0xe1, 0x6f, 0x12, 0x1e, 0x2c, 0x02, 0x0c, 0xe3, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfb, +0x12, 0x1c, 0xed, 0xfe, 0x30, 0xe1, 0x5c, 0x30, 0xe2, 0x11, 0x30, 0xb4, 0x05, 0x12, 0x1e, 0x2c, +0x80, 0x51, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x48, 0x30, 0x95, 0x05, 0x12, 0x1e, +0x2c, 0x80, 0x40, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xfd, 0xf0, 0x80, 0x37, 0x90, 0xfa, 0xba, 0xe0, +0x60, 0x12, 0x90, 0xff, 0xb4, 0x12, 0x1c, 0xea, 0x30, 0xe1, 0x28, 0x90, 0xff, 0xb4, 0xe0, 0x44, +0x02, 0xf0, 0x80, 0x1f, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xfb, 0x12, 0x1c, 0xed, 0x30, 0xe1, 0x13, +0x30, 0x93, 0x09, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, +0x54, 0xfd, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x80, 0x90, 0xfa, 0xb9, 0xe0, 0x24, +0xfc, 0x60, 0x40, 0x04, 0x70, 0x78, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xa2, 0xe0, +0x44, 0x40, 0xf0, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x65, 0xd2, 0x03, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, +0x90, 0xff, 0xa3, 0xef, 0x54, 0x7f, 0xf0, 0x80, 0x55, 0x30, 0x03, 0x0e, 0x90, 0xff, 0xa3, 0xe0, +0x44, 0x80, 0xf0, 0xc2, 0x03, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x54, 0xbf, +0xf0, 0x80, 0x3b, 0x90, 0xfa, 0xba, 0xe0, 0x60, 0x1d, 0x90, 0xff, 0xb2, 0xe0, 0x44, 0x40, 0xf0, +0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x28, 0xd2, 0x04, 0xa3, 0xe0, 0x54, 0xdf, 0xf0, 0x90, 0xff, 0xb3, +0xef, 0x54, 0x7f, 0xf0, 0x80, 0x18, 0x30, 0x04, 0x0e, 0x90, 0xff, 0xb3, 0xe0, 0x44, 0x80, 0xf0, +0xc2, 0x04, 0xa3, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x54, 0xbf, 0xf0, 0xe4, 0xff, +0x02, 0x31, 0xa9, 0x12, 0x1c, 0xe0, 0x90, 0xfa, 0xb9, 0xe0, 0x24, 0xfc, 0x60, 0x0f, 0x04, 0x70, +0x16, 0x90, 0xff, 0xa6, 0xe0, 0x12, 0x1c, 0xc1, 0x12, 0x1a, 0xe8, 0x80, 0x0a, 0x90, 0xff, 0xb6, +0xe0, 0x12, 0x1c, 0xc1, 0x12, 0x1a, 0xe8, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 0x2c, 0xd8, +0x90, 0xf9, 0x15, 0x74, 0x01, 0xf0, 0x90, 0xf9, 0x1c, 0x74, 0x19, 0xf0, 0x90, 0xf9, 0x66, 0x74, +0xff, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0xe4, 0xff, 0x12, 0x31, 0xa9, 0x12, 0x1d, 0xe7, 0x7f, +0x03, 0x12, 0x12, 0x61, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe4, 0x08, 0x90, 0xff, 0x93, 0x74, 0x80, +0xf0, 0x80, 0x10, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0xff, 0x7e, 0x00, 0x12, 0x30, +0xd3, 0xc2, 0x90, 0xc2, 0xaf, 0x00, 0x80, 0xfd, 0xe4, 0xf5, 0x4e, 0xf5, 0x4f, 0x90, 0xfa, 0xbf, +0x74, 0x3e, 0xf0, 0xa3, 0xe4, 0xf0, 0x90, 0xfa, 0xb7, 0xf0, 0xa3, 0x74, 0x15, 0xf0, 0xe0, 0x54, +0x3f, 0xff, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0xfa, 0xbc, 0xf0, 0xd3, 0x94, 0x00, 0xe4, 0x94, 0x3e, +0x40, 0x08, 0x90, 0xfa, 0xc0, 0xe0, 0x90, 0xfa, 0xbc, 0xf0, 0x12, 0x0f, 0x98, 0xe5, 0x31, 0x45, +0x30, 0x70, 0x73, 0x12, 0x1c, 0xfa, 0x90, 0xfa, 0xbf, 0x12, 0x1e, 0x06, 0x60, 0x27, 0xd3, 0xef, +0x94, 0x40, 0xee, 0x94, 0x00, 0x40, 0x08, 0x90, 0xfa, 0xbc, 0x74, 0x40, 0xf0, 0x80, 0x08, 0x90, +0xfa, 0xc0, 0xe0, 0x90, 0xfa, 0xbc, 0xf0, 0x12, 0x0f, 0x98, 0xe5, 0x31, 0x45, 0x30, 0x70, 0x46, +0x12, 0x1c, 0xfa, 0x80, 0xd1, 0x75, 0x4c, 0x02, 0x90, 0xfa, 0xbf, 0xe4, 0xf0, 0xa3, 0x04, 0xf0, +0x90, 0xfa, 0xb7, 0xe4, 0xf0, 0xa3, 0x74, 0x0f, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0x90, +0xfa, 0xc0, 0xe0, 0xf5, 0x4a, 0x7d, 0x0f, 0x7c, 0x00, 0x12, 0x29, 0x60, 0x75, 0x30, 0x00, 0x8f, +0x31, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x4c, 0xe4, 0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, +0x98, 0xe4, 0xf5, 0x30, 0xf5, 0x31, 0xaf, 0x31, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x80, 0x30, 0xe7, +0x10, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x67, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x15, 0xc2, 0x95, 0x80, +0x11, 0x90, 0xfa, 0xba, 0xe0, 0x54, 0x0f, 0x90, 0xf9, 0x65, 0xf0, 0xd3, 0x94, 0x00, 0x40, 0x02, +0xc2, 0x94, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1e, 0x4c, 0xbf, 0x01, 0x04, 0xd2, 0x93, 0x80, +0x02, 0xc2, 0x93, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0x12, 0x1d, 0x80, 0x54, 0x03, 0x14, 0x60, 0x0a, +0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x91, 0x80, 0x27, 0xc2, 0x91, +0x80, 0x23, 0x12, 0x1e, 0x56, 0x12, 0x0f, 0xc0, 0x60, 0x04, 0xd2, 0x91, 0x80, 0x17, 0x90, 0xff, +0xa4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0xc0, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x91, 0x80, 0x02, 0xd2, +0x91, 0x12, 0x1e, 0x56, 0xf0, 0x90, 0xfa, 0xba, 0xe0, 0x54, 0x0c, 0xff, 0x13, 0x13, 0x54, 0x3f, +0x14, 0x60, 0x0a, 0x14, 0x60, 0x0f, 0x14, 0x60, 0x08, 0x24, 0x03, 0x70, 0x2b, 0xd2, 0x92, 0x80, +0x27, 0xc2, 0x92, 0x80, 0x23, 0x12, 0x1e, 0x5d, 0x12, 0x0f, 0xe0, 0x60, 0x04, 0xd2, 0x92, 0x80, +0x17, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x10, 0x12, 0x0f, 0xe0, 0xff, 0xbf, 0xa0, 0x04, 0xc2, 0x92, +0x80, 0x02, 0xd2, 0x92, 0x12, 0x1e, 0x5d, 0xf0, 0xe4, 0xff, 0x02, 0x31, 0xa9, 0xe5, 0x35, 0x30, +0xe7, 0x07, 0xe4, 0xfd, 0x7f, 0x05, 0x02, 0x2f, 0xb4, 0x7f, 0x05, 0x02, 0x31, 0xa9, 0x12, 0x32, +0x7a, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb6, 0x90, 0xfa, 0xb7, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, +0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xb7, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1b, +0x1c, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, 0xaa, 0x4e, 0xa9, 0x4f, 0x7b, 0xff, 0x90, 0xfa, +0xb7, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xbc, 0xe0, 0xf5, 0x4a, 0x12, 0x29, 0x60, 0x75, +0x30, 0x00, 0x8f, 0x31, 0x22, 0x12, 0x23, 0x61, 0x7e, 0x00, 0x8e, 0x30, 0x8f, 0x31, 0xef, 0x22, +0xf0, 0x7f, 0x01, 0x12, 0x12, 0x61, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xbb, 0xf0, 0x54, 0xa0, +0x22, 0x12, 0x26, 0x98, 0x8f, 0x4c, 0x7e, 0x00, 0xc3, 0xef, 0x95, 0x3c, 0xee, 0x95, 0x3b, 0x22, +0xf0, 0x7f, 0x01, 0x12, 0x12, 0x61, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xbb, 0xf0, 0x54, 0xa0, +0x22, 0x75, 0x39, 0x00, 0x75, 0x3a, 0x01, 0x02, 0x2c, 0xd8, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x02, +0x32, 0x3f, 0x8e, 0x39, 0x8f, 0x3a, 0x02, 0x2c, 0xd8, 0x12, 0x23, 0x61, 0x7e, 0x00, 0x8e, 0x30, +0x8f, 0x31, 0xef, 0x22, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xb4, 0xe0, 0x22, 0xef, 0x90, +0xf8, 0x04, 0xf0, 0x22, 0xc0, 0xa8, 0xc2, 0xaf, 0xee, 0x60, 0x0a, 0xc0, 0x05, 0x7d, 0x7f, 0xdd, +0xfe, 0xde, 0xfa, 0xd0, 0x05, 0xef, 0xc3, 0x94, 0x15, 0x50, 0x03, 0xd0, 0xa8, 0x22, 0x13, 0x70, +0x03, 0xd0, 0xa8, 0x22, 0xff, 0xd5, 0x07, 0xfd, 0xd0, 0xa8, 0x22, 0xc0, 0x00, 0xc0, 0x01, 0xc0, +0x02, 0xc0, 0x04, 0xc0, 0x05, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0x86, 0x05, 0x53, 0x05, 0x7f, 0x7c, +0xff, 0x12, 0x10, 0xc0, 0x7f, 0x00, 0x7e, 0x00, 0xe5, 0x43, 0x60, 0x46, 0xfc, 0x90, 0xf9, 0x1d, +0xe0, 0x54, 0x7f, 0x6d, 0x70, 0x0f, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, +0xa3, 0x15, 0x43, 0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xdc, 0xe6, 0x80, 0x26, 0xdc, 0x06, 0xd0, 0x82, +0xd0, 0x83, 0x80, 0x1e, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, +0xe8, 0xf0, 0xa3, 0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, +0x80, 0xda, 0x12, 0x11, 0x57, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, +0x85, 0xa8, 0x44, 0x75, 0xa8, 0x88, 0xec, 0x70, 0x02, 0x7c, 0x3f, 0x8c, 0x3d, 0x22, 0xe5, 0x3e, +0x24, 0x08, 0xf8, 0x76, 0x00, 0x12, 0x11, 0xae, 0x80, 0xfb, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, +0xc0, 0x04, 0xc0, 0x06, 0x7c, 0xff, 0x12, 0x10, 0xc0, 0xe5, 0x43, 0x60, 0x42, 0xfe, 0x90, 0xf9, +0x1d, 0xe0, 0x54, 0x7f, 0x6f, 0x70, 0x0b, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x15, 0x43, +0x80, 0x07, 0xa3, 0xa3, 0xa3, 0xde, 0xea, 0x80, 0x26, 0xde, 0x06, 0xd0, 0x82, 0xd0, 0x83, 0x80, +0xd8, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xd0, 0x82, 0xd0, 0x83, 0xe8, 0xf0, 0xa3, +0xe9, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0x80, 0xda, 0x78, +0x08, 0x08, 0x79, 0x18, 0x09, 0x7c, 0x01, 0xe6, 0x54, 0x7f, 0x6f, 0x70, 0x06, 0x76, 0x00, 0x77, +0x00, 0x80, 0x06, 0x08, 0x09, 0x0c, 0xbc, 0x08, 0xee, 0x12, 0x11, 0x57, 0xd0, 0x06, 0xd0, 0x04, +0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0x22, 0x75, 0x3d, 0x00, 0x85, 0x44, 0xa8, 0x22, 0xc0, 0xf0, +0xc0, 0x82, 0xc0, 0x83, 0xc3, 0xe5, 0x43, 0x24, 0xe8, 0x50, 0x05, 0x12, 0x11, 0xae, 0x80, 0xf4, +0xef, 0x60, 0x31, 0x90, 0x31, 0x11, 0xe4, 0x93, 0xc3, 0x9f, 0x40, 0x2f, 0xc0, 0x04, 0x7c, 0xff, +0x12, 0x10, 0xc0, 0xd0, 0x04, 0x43, 0x07, 0x80, 0xe5, 0x43, 0x75, 0xf0, 0x03, 0xa4, 0x24, 0x1d, +0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xef, 0xf0, 0xec, 0xa3, 0xf0, 0xed, 0xa3, 0xf0, 0x05, +0x43, 0x12, 0x11, 0x57, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0xf0, 0x22, 0x02, 0x11, 0xdc, 0xc0, 0x04, +0x7c, 0x20, 0xd2, 0x8c, 0xd2, 0x8d, 0xd5, 0x04, 0xfd, 0xd0, 0x04, 0x22, 0x75, 0xa8, 0x00, 0x75, +0x88, 0x00, 0x75, 0xb8, 0x00, 0x75, 0xf0, 0x00, 0x75, 0xd0, 0x00, 0xe4, 0xf8, 0x90, 0xf8, 0x04, +0xf0, 0x90, 0x00, 0x00, 0xf6, 0x08, 0xb8, 0x00, 0xfb, 0x02, 0x00, 0x00, 0xc2, 0xaf, 0xe4, 0x90, +0xff, 0x48, 0xf0, 0x90, 0xff, 0x50, 0xf0, 0x90, 0xff, 0x08, 0xf0, 0x90, 0xff, 0x10, 0xf0, 0x90, +0xff, 0x80, 0xf0, 0xa3, 0xa3, 0xf0, 0xd2, 0xb1, 0xc2, 0xb0, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, +0x24, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, 0x24, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, 0x24, 0xd2, +0xb0, 0xd2, 0xb1, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, 0x24, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, +0x24, 0x7e, 0xff, 0x7f, 0xff, 0x12, 0x10, 0x24, 0x80, 0xcc, 0xc3, 0xee, 0x94, 0x02, 0x50, 0x04, +0x7e, 0x03, 0x7f, 0xe8, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, +0x42, 0x8e, 0x41, 0x22, 0xc3, 0xef, 0x94, 0xbc, 0xee, 0x94, 0x02, 0x50, 0x04, 0x7e, 0x07, 0x7f, +0xd0, 0xef, 0xf4, 0xff, 0xee, 0xf4, 0xfe, 0x0f, 0xbf, 0x00, 0x01, 0x0e, 0x8f, 0x40, 0x8e, 0x3f, +0x22, 0xef, 0x70, 0x01, 0x22, 0xc0, 0x00, 0xc0, 0xa8, 0xc2, 0xaf, 0xe5, 0x3e, 0x24, 0x18, 0xf8, +0xa6, 0x07, 0xe5, 0x3e, 0x24, 0x08, 0xf8, 0xc6, 0x54, 0x7f, 0xf6, 0xd0, 0xa8, 0xe6, 0x30, 0xe7, +0x03, 0xd0, 0x00, 0x22, 0x12, 0x11, 0xae, 0x80, 0xf4, 0xc0, 0x00, 0x7f, 0x01, 0xef, 0x24, 0x08, +0xf8, 0xe6, 0x60, 0x09, 0x0f, 0xbf, 0x08, 0xf5, 0x12, 0x11, 0xae, 0x80, 0xee, 0xd0, 0x00, 0x22, +0xc0, 0xf0, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x00, 0xc0, 0x06, 0xc0, 0x04, 0xed, 0x24, 0x10, 0xf8, +0x76, 0x9a, 0xed, 0x75, 0xf0, 0x21, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xf8, 0xf5, 0x83, +0xc0, 0x82, 0xc0, 0x83, 0xa3, 0xa3, 0xe4, 0x78, 0x0d, 0xf0, 0xa3, 0xd8, 0xfc, 0xef, 0x54, 0x7f, +0x75, 0xf0, 0x02, 0xa4, 0x24, 0xf3, 0xf5, 0x82, 0xe5, 0xf0, 0x34, 0x30, 0xf5, 0x83, 0xe4, 0x93, +0xfe, 0x74, 0x01, 0x93, 0xfc, 0xd0, 0x83, 0xd0, 0x82, 0xec, 0xf0, 0xa3, 0xee, 0xf0, 0xed, 0x24, +0x08, 0xf8, 0xef, 0x44, 0x80, 0xf6, 0xd0, 0x04, 0xd0, 0x06, 0xd0, 0x00, 0xd0, 0x83, 0xd0, 0x82, +0xd0, 0xf0, 0x22, 0x75, 0x3e, 0x00, 0x75, 0x43, 0x00, 0x7a, 0x08, 0x79, 0x18, 0x78, 0x08, 0x76, +0x00, 0x77, 0x00, 0x08, 0x09, 0xda, 0xf8, 0x90, 0xf8, 0x04, 0xe0, 0xfc, 0x90, 0x31, 0x11, 0xe4, +0x93, 0xc3, 0x9c, 0x50, 0x05, 0xe4, 0x90, 0xf8, 0x04, 0xf0, 0x78, 0x08, 0x74, 0x80, 0x44, 0x7f, +0xf6, 0x74, 0x01, 0x44, 0x10, 0xf5, 0x89, 0x75, 0xb8, 0x00, 0xd2, 0xab, 0xd2, 0xa9, 0x22, 0x75, +0x81, 0x8b, 0xd2, 0x8e, 0xd2, 0x8c, 0xd2, 0xaf, 0xe5, 0x43, 0x60, 0x36, 0xff, 0x90, 0xf9, 0x1d, +0xe0, 0x54, 0x80, 0x60, 0x28, 0x78, 0x08, 0x79, 0x08, 0xe0, 0x54, 0x7f, 0xfa, 0x7b, 0x00, 0xe6, +0x54, 0x7f, 0xb5, 0x02, 0x02, 0x7b, 0xff, 0x08, 0xd9, 0xf5, 0xeb, 0x70, 0x10, 0xea, 0xf0, 0xc0, +0x07, 0x12, 0x12, 0x89, 0xad, 0x07, 0xaf, 0x02, 0x12, 0x12, 0xa0, 0xd0, 0x07, 0xa3, 0xa3, 0xa3, +0xdf, 0xce, 0x12, 0x11, 0xae, 0x80, 0xc1, 0x8f, 0x24, 0x12, 0x2a, 0xc7, 0x12, 0x22, 0xb5, 0xa3, +0xa3, 0xe0, 0xa3, 0x30, 0xe7, 0x28, 0x78, 0x7e, 0x12, 0x22, 0x99, 0xe0, 0x44, 0x01, 0xf0, 0x12, +0x22, 0xfa, 0x12, 0x22, 0x9d, 0xe0, 0x20, 0xe0, 0xf6, 0x12, 0x23, 0x50, 0x74, 0x02, 0xf0, 0x12, +0x22, 0xda, 0xe0, 0xa3, 0x30, 0xe5, 0x07, 0x12, 0x23, 0x50, 0xe0, 0x44, 0x01, 0xf0, 0x78, 0x80, +0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x08, 0x12, 0x22, 0xa1, 0xe0, 0xfd, 0x12, 0x23, +0x39, 0x8a, 0x83, 0x24, 0x0a, 0x12, 0x22, 0xa1, 0xed, 0xf0, 0x12, 0x23, 0x06, 0x24, 0x07, 0x12, +0x22, 0xa1, 0xe0, 0xff, 0x12, 0x23, 0x5a, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xef, 0xf0, 0x90, 0xf9, +0x16, 0xe0, 0x30, 0xe4, 0x20, 0x08, 0x12, 0x22, 0xb7, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xe0, 0x25, +0xe0, 0xff, 0x05, 0x82, 0xd5, 0x82, 0x02, 0x15, 0x83, 0x15, 0x82, 0xe0, 0x33, 0xd0, 0x82, 0xd0, +0x83, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x22, 0xb5, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xec, 0xff, 0x12, +0x23, 0x39, 0x8a, 0x83, 0x24, 0x08, 0x12, 0x22, 0xa1, 0xef, 0xf0, 0xed, 0x12, 0x23, 0x5a, 0x24, +0x07, 0x12, 0x22, 0xa1, 0xed, 0xf0, 0x12, 0x22, 0xa9, 0xe0, 0x30, 0xe6, 0x0a, 0x12, 0x23, 0x41, +0x24, 0x09, 0x12, 0x22, 0xa1, 0xe4, 0xf0, 0x12, 0x22, 0xa9, 0xe0, 0xff, 0x30, 0xe7, 0x1b, 0x12, +0x23, 0x1e, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xe0, 0x60, 0x09, 0x12, 0x22, 0xa9, 0xef, 0x44, 0x02, +0xf0, 0x80, 0x07, 0x12, 0x22, 0xa9, 0xef, 0x54, 0xfd, 0xf0, 0x78, 0x7e, 0x12, 0x22, 0xb7, 0xa3, +0xa3, 0xe0, 0xff, 0x53, 0x07, 0xc7, 0x08, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x12, 0x22, 0xe0, 0xa3, +0xe0, 0x30, 0xe3, 0x12, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x05, 0x12, 0x22, 0xa1, 0xe0, +0x90, 0x32, 0x51, 0x93, 0x42, 0x07, 0x53, 0x07, 0xfb, 0x12, 0x23, 0x1e, 0x24, 0x06, 0x12, 0x22, +0xa1, 0xe0, 0x60, 0x03, 0x43, 0x07, 0x04, 0x53, 0x07, 0xfc, 0x78, 0x80, 0x12, 0x23, 0x29, 0x24, +0x04, 0x12, 0x22, 0xa1, 0xe0, 0x42, 0x07, 0x43, 0x07, 0x80, 0x12, 0x23, 0x39, 0xf5, 0x82, 0x8a, +0x83, 0xa3, 0xa3, 0xef, 0xf0, 0x12, 0x23, 0x5a, 0x24, 0x04, 0x12, 0x22, 0xa1, 0xe0, 0xff, 0x8d, +0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe1, 0x05, 0x53, 0x07, 0xdf, +0x80, 0x03, 0x43, 0x07, 0x20, 0xec, 0x30, 0xe4, 0x05, 0x53, 0x07, 0xef, 0x80, 0x03, 0x43, 0x07, +0x10, 0x12, 0x22, 0xa9, 0xe0, 0xfe, 0x54, 0x03, 0x60, 0x73, 0x53, 0x07, 0xdf, 0xee, 0x30, 0xe1, +0x69, 0x12, 0x23, 0x1e, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xe0, 0x12, 0x1b, 0xfc, 0x15, 0x2c, 0x00, +0x15, 0x60, 0x01, 0x15, 0x65, 0x03, 0x15, 0x60, 0x05, 0x15, 0x65, 0x07, 0x15, 0x60, 0x09, 0x15, +0x65, 0x0b, 0x15, 0x60, 0x0d, 0x15, 0x65, 0x0f, 0x00, 0x00, 0x15, 0x6d, 0xe5, 0x24, 0x64, 0x03, +0x70, 0x21, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe2, 0x0d, 0x30, 0xb4, 0x05, 0x43, 0x07, 0x02, 0x80, +0x2c, 0x53, 0x07, 0xfd, 0x80, 0x27, 0x30, 0x95, 0x05, 0x43, 0x07, 0x02, 0x80, 0x1f, 0x53, 0x07, +0xfd, 0x80, 0x1a, 0x30, 0x93, 0x05, 0x43, 0x07, 0x02, 0x80, 0x12, 0x53, 0x07, 0xfd, 0x80, 0x0d, +0x43, 0x07, 0x02, 0x80, 0x08, 0x53, 0x07, 0xfd, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x12, 0x23, 0x27, +0x24, 0x04, 0x12, 0x22, 0xa1, 0xef, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xff, +0x12, 0x22, 0xa9, 0xe0, 0xfe, 0x54, 0x03, 0x70, 0x03, 0x02, 0x16, 0x60, 0xee, 0x20, 0xe1, 0x03, +0x02, 0x16, 0x5d, 0x08, 0x12, 0x23, 0x20, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xe0, 0x12, 0x1b, 0xfc, +0x15, 0xbf, 0x00, 0x15, 0xf5, 0x01, 0x15, 0xf5, 0x03, 0x16, 0x29, 0x05, 0x16, 0x29, 0x07, 0x16, +0x0f, 0x09, 0x16, 0x0f, 0x0b, 0x16, 0x43, 0x0d, 0x16, 0x43, 0x0f, 0x00, 0x00, 0x16, 0x60, 0xe5, +0x24, 0x64, 0x03, 0x70, 0x23, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe2, 0x0f, 0x30, 0xb1, 0x06, 0x53, +0x07, 0x7f, 0x02, 0x16, 0x60, 0x43, 0x07, 0x80, 0x02, 0x16, 0x60, 0x30, 0x94, 0x05, 0x53, 0x07, +0x7f, 0x80, 0x7d, 0x43, 0x07, 0x80, 0x80, 0x78, 0x30, 0x92, 0x05, 0x53, 0x07, 0x7f, 0x80, 0x70, +0x43, 0x07, 0x80, 0x80, 0x6b, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xef, +0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xdf, 0xf0, 0x53, 0x07, 0x7f, 0x80, 0x51, 0xe5, +0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x10, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, +0xe0, 0x44, 0x20, 0xf0, 0x53, 0x07, 0x7f, 0x80, 0x37, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, +0x9e, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x07, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xdf, 0xf0, 0x43, 0x07, +0x80, 0x80, 0x1d, 0xe5, 0x24, 0xb4, 0x03, 0x09, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x10, 0xf0, 0x80, +0x07, 0x90, 0xff, 0x9e, 0xe0, 0x44, 0x20, 0xf0, 0x43, 0x07, 0x80, 0x80, 0x03, 0x53, 0x07, 0x7f, +0x12, 0x22, 0xda, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x30, 0xe0, 0x05, 0x43, 0x07, 0x20, 0x80, 0x03, +0x53, 0x07, 0xdf, 0xec, 0x30, 0xe3, 0x05, 0x43, 0x07, 0x40, 0x80, 0x03, 0x53, 0x07, 0xbf, 0xec, +0x30, 0xe0, 0x05, 0x43, 0x07, 0x10, 0x80, 0x03, 0x53, 0x07, 0xef, 0xed, 0x30, 0xe4, 0x05, 0x43, +0x07, 0x08, 0x80, 0x03, 0x53, 0x07, 0xf7, 0xed, 0x30, 0xe5, 0x05, 0x43, 0x07, 0x04, 0x80, 0x03, +0x53, 0x07, 0xfb, 0xed, 0x30, 0xe6, 0x05, 0x43, 0x07, 0x01, 0x80, 0x03, 0x53, 0x07, 0xfe, 0xed, +0x30, 0xe7, 0x05, 0x43, 0x07, 0x02, 0x80, 0x03, 0x53, 0x07, 0xfd, 0x78, 0x7e, 0x12, 0x22, 0xdc, +0xa3, 0xef, 0xf0, 0x12, 0x32, 0x84, 0x7f, 0x00, 0x22, 0x90, 0xff, 0xfa, 0x74, 0x08, 0xf0, 0xa3, +0x74, 0x16, 0xf0, 0x90, 0xff, 0xf9, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcf, 0xe4, +0xfd, 0x12, 0x23, 0x61, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x1b, 0x1c, 0x12, 0x19, +0x92, 0xe5, 0x23, 0x30, 0xe7, 0x02, 0xd2, 0x02, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x24, 0x90, 0xfa, +0xcf, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xcf, +0xe4, 0xf0, 0xa3, 0x74, 0x0b, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x75, 0x2d, 0x00, 0xf5, +0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0xe5, 0x23, 0x24, 0x80, 0x90, 0xff, 0xf8, 0xf0, 0xe5, 0x23, +0x64, 0x07, 0x60, 0x1e, 0xe5, 0x23, 0x64, 0x06, 0x60, 0x18, 0xe5, 0x23, 0x64, 0x14, 0x60, 0x12, +0xe5, 0x23, 0x64, 0x41, 0x60, 0x0c, 0xe5, 0x23, 0x64, 0x1a, 0x70, 0x46, 0xe5, 0x24, 0x64, 0x02, +0x70, 0x40, 0xe5, 0x23, 0xb4, 0x07, 0x16, 0xd2, 0x94, 0xd2, 0x95, 0xd2, 0x92, 0xd2, 0x93, 0x90, +0xf9, 0x16, 0xe0, 0x44, 0x02, 0xf0, 0xa3, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1e, 0xe5, 0x23, 0xb4, +0x41, 0x12, 0x90, 0xf9, 0x16, 0xe0, 0x44, 0x06, 0xf0, 0xa3, 0xe0, 0x44, 0x06, 0xf0, 0xd2, 0xb1, +0xd2, 0xb4, 0x80, 0x07, 0x90, 0xf9, 0x16, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x44, +0x01, 0xf0, 0xe5, 0x23, 0x64, 0x42, 0x60, 0x0c, 0xe5, 0x23, 0x64, 0x43, 0x60, 0x06, 0xe5, 0x23, +0x64, 0x44, 0x70, 0x2e, 0x90, 0xf9, 0x16, 0xe0, 0xff, 0xe5, 0x23, 0xb4, 0x44, 0x04, 0x7e, 0x40, +0x80, 0x02, 0x7e, 0x00, 0xee, 0x24, 0x80, 0x4f, 0x90, 0xf9, 0x16, 0xf0, 0xa3, 0xe0, 0xff, 0xe5, +0x23, 0xb4, 0x44, 0x04, 0x7e, 0x40, 0x80, 0x02, 0x7e, 0x00, 0xee, 0x24, 0x80, 0x4f, 0x90, 0xf9, +0x17, 0xf0, 0x90, 0xfa, 0xcf, 0xe4, 0xf0, 0xa3, 0x74, 0x0d, 0xf0, 0x12, 0x19, 0x92, 0x90, 0xff, +0xf5, 0xe5, 0x23, 0xf0, 0xe4, 0xf5, 0x35, 0xf5, 0x33, 0xf5, 0x34, 0xf5, 0x32, 0x12, 0x1e, 0x34, +0x12, 0x1c, 0xe0, 0x12, 0x1e, 0x3b, 0x90, 0xf9, 0x6a, 0x12, 0x1b, 0xf3, 0x90, 0xf9, 0x6f, 0x12, +0x1b, 0xf3, 0x90, 0xff, 0xff, 0xe4, 0xf0, 0x90, 0xff, 0x83, 0xe0, 0xe4, 0xf0, 0x90, 0xff, 0x81, 0x74, 0x80, 0xf0, 0xa3, 0x74, 0x84, 0xf0, 0x90, 0xff, 0x80, 0xf0, 0xe4, 0xf5, 0x23, 0xe5, 0x23, -0x12, 0x1c, 0xa7, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x23, 0x12, 0x1c, 0xb5, 0xf5, 0x83, 0xe4, 0xf0, -0x05, 0x23, 0xe5, 0x23, 0xb4, 0x07, 0xe7, 0x78, 0x7a, 0x76, 0xfe, 0x08, 0x76, 0xf0, 0x90, 0x31, -0x4d, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xad, 0x07, 0x90, 0x31, 0x5a, 0xe4, 0x93, 0xff, -0x08, 0xf6, 0xff, 0xed, 0x54, 0x0f, 0xfd, 0x12, 0x1c, 0x97, 0x74, 0x84, 0xf0, 0xed, 0x75, 0xf0, +0x12, 0x1d, 0x57, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x23, 0x12, 0x1d, 0x65, 0xf5, 0x83, 0xe4, 0xf0, +0x05, 0x23, 0xe5, 0x23, 0xb4, 0x07, 0xe7, 0x78, 0x7a, 0x76, 0xfe, 0x08, 0x76, 0xf0, 0x90, 0x32, +0x0a, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xad, 0x07, 0x90, 0x32, 0x17, 0xe4, 0x93, 0xff, +0x08, 0xf6, 0xff, 0xed, 0x54, 0x0f, 0xfd, 0x12, 0x1d, 0x47, 0x74, 0x84, 0xf0, 0xed, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x47, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xc3, 0x74, 0xf0, -0x9f, 0x78, 0x7b, 0xf6, 0x74, 0xfe, 0x94, 0x00, 0x18, 0x12, 0x1c, 0x28, 0xce, 0xc3, 0x13, 0xce, -0x13, 0xd8, 0xf9, 0xff, 0xed, 0x12, 0x1c, 0xf8, 0xef, 0xf0, 0xed, 0x12, 0x1d, 0x1e, 0xe4, 0xf5, -0x23, 0xe5, 0x23, 0x90, 0x31, 0x47, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xe5, 0x23, 0x25, 0xe0, -0x24, 0x4e, 0xf5, 0x82, 0xe4, 0x34, 0x31, 0xf5, 0x83, 0xe4, 0x93, 0x08, 0xf6, 0xed, 0x30, 0xe7, -0x53, 0x18, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1c, 0x97, 0x12, 0x1d, 0x06, 0x24, 0x47, 0xf5, 0x82, -0xe4, 0x34, 0xff, 0x12, 0x1c, 0x18, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0xe9, 0x12, -0x1c, 0xf8, 0xef, 0xf0, 0x12, 0x1c, 0x1f, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, -0x0b, 0x24, 0x45, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x12, 0x1d, 0x1e, +0x9f, 0x78, 0x7b, 0xf6, 0x74, 0xfe, 0x94, 0x00, 0x18, 0x12, 0x1c, 0xd8, 0xce, 0xc3, 0x13, 0xce, +0x13, 0xd8, 0xf9, 0xff, 0xed, 0x12, 0x1d, 0xa8, 0xef, 0xf0, 0xed, 0x12, 0x1d, 0xce, 0xe4, 0xf5, +0x23, 0xe5, 0x23, 0x90, 0x32, 0x04, 0x93, 0xff, 0x78, 0x78, 0xf6, 0xfd, 0xe5, 0x23, 0x25, 0xe0, +0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x34, 0x32, 0xf5, 0x83, 0xe4, 0x93, 0x08, 0xf6, 0xed, 0x30, 0xe7, +0x53, 0x18, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1d, 0x47, 0x12, 0x1d, 0xb6, 0x24, 0x47, 0xf5, 0x82, +0xe4, 0x34, 0xff, 0x12, 0x1c, 0xc8, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0xe9, 0x12, +0x1d, 0xa8, 0xef, 0xf0, 0x12, 0x1c, 0xcf, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, +0xbb, 0x24, 0x45, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x12, 0x1d, 0xce, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x46, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, -0xf0, 0x02, 0x18, 0xb7, 0x78, 0x78, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1c, 0xea, 0x12, 0x1d, 0x06, -0x24, 0x07, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x12, 0x1c, 0x18, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, -0xf9, 0x12, 0x1d, 0x0b, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0x12, -0x1c, 0x1f, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, 0x0b, 0x24, 0x05, 0xf5, 0x82, +0xf0, 0x02, 0x19, 0x67, 0x78, 0x78, 0xe6, 0x54, 0x0f, 0xf9, 0x12, 0x1d, 0x9a, 0x12, 0x1d, 0xb6, +0x24, 0x07, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x12, 0x1c, 0xc8, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, +0xf9, 0x12, 0x1d, 0xbb, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0x12, +0x1c, 0xcf, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0x12, 0x1d, 0xbb, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xef, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0xe9, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0xe4, 0xf0, 0x05, 0x23, 0xe5, 0x23, 0x64, 0x04, 0x60, 0x03, 0x02, -0x17, 0xe1, 0x90, 0x31, 0x4c, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0x12, 0x1c, 0xe8, 0xe4, 0xf0, -0x90, 0x31, 0x4b, 0x93, 0xff, 0xf6, 0x12, 0x1c, 0x95, 0xe4, 0xf0, 0x90, 0xff, 0xfd, 0x74, 0x05, -0xf0, 0x22, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, -0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x25, 0xd7, 0xe7, 0x09, 0xf6, 0x08, +0x18, 0x91, 0x90, 0x32, 0x09, 0xe4, 0x93, 0xff, 0x78, 0x78, 0xf6, 0x12, 0x1d, 0x98, 0xe4, 0xf0, +0x90, 0x32, 0x08, 0x93, 0xff, 0xf6, 0x12, 0x1d, 0x45, 0xe4, 0xf0, 0x90, 0xff, 0xfd, 0x74, 0x05, +0xf0, 0x22, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, 0x12, +0x1b, 0x32, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x26, 0x98, 0xe7, 0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x46, 0xe7, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x3e, 0x88, 0x82, 0x8c, 0x83, 0xe7, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x32, 0xe3, 0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x78, 0xe3, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x70, 0x88, 0x82, 0x8c, 0x83, 0xe3, 0x09, 0xf0, 0xa3, @@ -445,7 +456,7 @@ static unsigned char IMAGE_ARRAY_NAME[] = 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf2, 0x08, 0xdf, 0xf9, 0x80, 0xcc, 0x88, 0xf0, 0xef, 0x60, 0x01, 0x0e, 0x4e, 0x60, 0xc3, 0x88, 0xf0, 0xed, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xb9, 0xf5, 0x82, 0xeb, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xaf, 0x23, 0x23, 0x45, 0x82, 0x23, 0x90, 0x19, -0x4c, 0x73, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, +0xfc, 0x73, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02, 0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22, 0xe5, 0x82, 0x29, 0xf5, 0x82, @@ -469,364 +480,365 @@ static unsigned char IMAGE_ARRAY_NAME[] = 0xe0, 0xf9, 0x22, 0xeb, 0xf0, 0xa3, 0xea, 0xf0, 0xa3, 0xe9, 0xf0, 0x22, 0xd0, 0x83, 0xd0, 0x82, 0xf8, 0xe4, 0x93, 0x70, 0x12, 0x74, 0x01, 0x93, 0x70, 0x0d, 0xa3, 0xa3, 0x93, 0xf8, 0x74, 0x01, 0x93, 0xf5, 0x82, 0x88, 0x83, 0xe4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xef, 0xa3, 0xa3, 0xa3, -0x80, 0xdf, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0xe5, 0x4c, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, +0x80, 0xdf, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0xe5, 0x4c, 0x12, 0x1a, 0xe8, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0x74, 0x11, 0x12, -0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x06, -0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, -0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, 0x38, 0x04, 0x25, +0x1a, 0xe8, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x06, +0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0xe8, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, +0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, 0xe8, 0x04, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0xab, 0x36, 0xfa, 0xa9, 0x38, 0xe4, 0x12, 0x1a, -0x38, 0x04, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x04, 0xe0, 0xab, -0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, -0x37, 0xf5, 0x37, 0x90, 0xff, 0x05, 0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0x38, +0xe8, 0x04, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x90, 0xff, 0x04, 0xe0, 0xab, +0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0xe8, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, +0x37, 0xf5, 0x37, 0x90, 0xff, 0x05, 0xe0, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x12, 0x1a, 0xe8, 0x74, 0x01, 0x25, 0x38, 0xf5, 0x38, 0xe4, 0x35, 0x37, 0xf5, 0x37, 0x22, 0xf5, 0x83, 0xe0, 0x54, 0x08, 0xab, 0x36, 0xaa, 0x37, 0xa9, 0x38, 0x22, 0xf5, 0x83, 0xef, 0xf0, 0xfd, 0x7c, 0x00, 0xc3, 0x78, 0x7b, 0xe6, 0x9d, 0xf6, 0x18, 0xe6, 0x9c, 0xf6, 0xe6, 0xfe, 0x08, 0xe6, 0x78, 0x03, 0x22, -0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, 0x6f, 0x22, 0xe0, 0x44, 0x04, 0xf0, 0x74, 0x12, -0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xe0, 0x22, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x7e, -0x00, 0xc3, 0x90, 0xfa, 0xbd, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xbc, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, -0xb4, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0xef, 0x25, 0x4f, 0xf5, 0x4f, 0xee, 0x35, 0x4e, 0xf5, -0x4e, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb1, 0x90, 0xfa, 0xb4, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, +0x75, 0x36, 0x01, 0x75, 0x37, 0xf9, 0x75, 0x38, 0x72, 0x22, 0xe0, 0x44, 0x04, 0xf0, 0x74, 0x13, +0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0xe0, 0x22, 0x90, 0xfa, 0xbc, 0xe0, 0xff, 0x7e, +0x00, 0xc3, 0x90, 0xfa, 0xc0, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xbf, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, +0xb7, 0xee, 0x8f, 0xf0, 0x12, 0x1b, 0x1c, 0xef, 0x25, 0x4f, 0xf5, 0x4f, 0xee, 0x35, 0x4e, 0xf5, +0x4e, 0x22, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xb4, 0x90, 0xfa, 0xb7, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, 0x2e, 0x22, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0xe5, 0x4d, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x48, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0x22, 0xe5, 0x4d, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x08, 0xf5, 0x82, 0xe4, 0x34, 0xff, -0x22, 0x90, 0xfa, 0xb6, 0xe0, 0xff, 0x24, 0xfc, 0x22, 0x90, 0xff, 0x00, 0xe0, 0x54, 0x1f, 0x22, -0x90, 0xfa, 0xbb, 0xe0, 0x90, 0xfa, 0xb7, 0xf0, 0x22, 0x75, 0x33, 0x00, 0x8f, 0x34, 0x90, 0xf9, -0x6c, 0x12, 0x1b, 0x3a, 0x90, 0x00, 0x02, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, +0x22, 0x90, 0xfa, 0xb9, 0xe0, 0xff, 0x24, 0xfc, 0x22, 0x90, 0xff, 0x00, 0xe0, 0x54, 0x1f, 0x22, +0x90, 0xfa, 0xbe, 0xe0, 0x90, 0xfa, 0xba, 0xf0, 0x22, 0x75, 0x33, 0x00, 0x8f, 0x34, 0x90, 0xf9, +0x6f, 0x12, 0x1b, 0xea, 0x90, 0x00, 0x02, 0x22, 0x54, 0x0f, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x41, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x22, 0x74, 0x80, 0xf0, 0x08, 0xe6, 0xff, 0xe9, 0x75, 0xf0, 0x08, -0xa4, 0x22, 0x74, 0xaf, 0x25, 0x22, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0x22, 0x75, 0xf0, +0xa4, 0x22, 0x74, 0xb2, 0x25, 0x22, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0x22, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x42, 0xf5, 0x82, 0xe4, 0x34, 0xff, 0xf5, 0x83, 0x74, 0x80, 0xf0, 0x22, 0x90, 0xff, 0x82, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x03, 0xf0, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0xfd, 0xf0, 0x22, 0x78, 0x67, 0xe6, 0x54, 0xfd, 0xf6, 0x90, 0xff, 0xfd, 0x74, -0x65, 0xf0, 0x22, 0x12, 0x1b, 0x1c, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x22, 0x7b, 0x01, 0x7a, -0xfa, 0x79, 0xb4, 0x22, 0x90, 0xff, 0x80, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0x83, 0xe0, -0x54, 0x7f, 0xf0, 0x22, 0xe0, 0xff, 0x90, 0xf9, 0x67, 0x02, 0x1b, 0x3a, 0x90, 0xff, 0xa4, 0xe0, +0x65, 0xf0, 0x22, 0x12, 0x1b, 0xcc, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x22, 0x7b, 0x01, 0x7a, +0xfa, 0x79, 0xb7, 0x22, 0x90, 0xff, 0x80, 0xe0, 0x44, 0x08, 0xf0, 0x22, 0x90, 0xff, 0x83, 0xe0, +0x54, 0x7f, 0xf0, 0x22, 0xe0, 0xff, 0x90, 0xf9, 0x6a, 0x02, 0x1b, 0xea, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x02, 0xf0, 0x22, 0x75, 0x39, 0x01, 0x75, 0x3a, 0x09, 0x22, 0x7b, 0x01, 0x7a, 0xf9, 0x79, -0x6f, 0x22, 0xd3, 0xe5, 0x3c, 0x94, 0x08, 0xe5, 0x3b, 0x94, 0x01, 0x22, 0x90, 0xfa, 0xbb, 0xe0, -0xff, 0x90, 0xfa, 0xb7, 0xf0, 0x22, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xef, 0x22, 0x90, 0xff, 0xb4, -0xe0, 0x54, 0xef, 0x22, 0x12, 0x10, 0x03, 0x78, 0x88, 0xef, 0xf6, 0x12, 0x2a, 0x06, 0x12, 0x22, -0x4a, 0x8e, 0x83, 0x24, 0x09, 0x12, 0x21, 0xf3, 0xe0, 0xfd, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x0a, -0x12, 0x22, 0x52, 0x24, 0x0a, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x00, 0x0b, 0x12, 0x1a, 0x4a, 0x12, -0x22, 0x4a, 0xf5, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x53, 0x12, 0x22, 0x56, 0x24, -0x04, 0x12, 0x21, 0xf3, 0xe0, 0xf5, 0x54, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xf5, 0x55, +0x72, 0x22, 0xd3, 0xe5, 0x3c, 0x94, 0x08, 0xe5, 0x3b, 0x94, 0x01, 0x22, 0x90, 0xfa, 0xbe, 0xe0, +0xff, 0x90, 0xfa, 0xba, 0xf0, 0x22, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xef, 0x22, 0x90, 0xff, 0xb4, +0xe0, 0x54, 0xef, 0x22, 0x12, 0x10, 0x4b, 0x78, 0x88, 0xef, 0xf6, 0x12, 0x2a, 0xc7, 0x12, 0x22, +0xfa, 0x8e, 0x83, 0x24, 0x09, 0x12, 0x22, 0xa1, 0xe0, 0xfd, 0x12, 0x22, 0xe8, 0x90, 0x00, 0x0a, +0x12, 0x23, 0x02, 0x24, 0x0a, 0x12, 0x22, 0xa1, 0xe0, 0x90, 0x00, 0x0b, 0x12, 0x1a, 0xfa, 0x12, +0x22, 0xfa, 0xf5, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x53, 0x12, 0x23, 0x06, 0x24, +0x04, 0x12, 0x22, 0xa1, 0xe0, 0xf5, 0x54, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xf5, 0x55, 0xe5, 0x53, 0xc4, 0x13, 0x13, 0x13, 0x54, 0x01, 0x78, 0x88, 0xf6, 0xd3, 0x94, 0x00, 0x40, 0x06, -0xe5, 0x54, 0x30, 0xe1, 0x01, 0x06, 0x78, 0x88, 0xe6, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x0c, 0xef, -0x12, 0x1a, 0x4a, 0x78, 0x80, 0x12, 0x22, 0x09, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x53, -0x07, 0x0c, 0x53, 0x06, 0xe6, 0xe5, 0x53, 0x30, 0xe5, 0x03, 0x43, 0x07, 0x01, 0xe5, 0x54, 0x20, -0xe5, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 0x03, 0x43, 0x07, 0x02, -0xe5, 0x53, 0x30, 0xe3, 0x03, 0x43, 0x07, 0x10, 0xe5, 0x53, 0x30, 0xe2, 0x03, 0x43, 0x07, 0x20, -0xe5, 0x53, 0x54, 0x03, 0x60, 0x03, 0x43, 0x07, 0x40, 0xe5, 0x53, 0x30, 0xe1, 0x03, 0x43, 0x07, -0x80, 0xe5, 0x53, 0x30, 0xe4, 0x03, 0x43, 0x06, 0x01, 0xe5, 0x53, 0x30, 0xe6, 0x03, 0x43, 0x06, -0x08, 0xe5, 0x54, 0x20, 0xe4, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, -0x03, 0x43, 0x06, 0x10, 0x53, 0x07, 0xfb, 0x53, 0x06, 0x79, 0x90, 0x00, 0x05, 0xee, 0x8f, 0xf0, -0x12, 0x1a, 0xef, 0xe5, 0x55, 0x30, 0xe3, 0x12, 0x54, 0x30, 0xff, 0xc4, 0x54, 0x0f, 0x12, 0x22, -0x2c, 0x90, 0x00, 0x08, 0xef, 0x12, 0x1a, 0x4a, 0x80, 0x0a, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x08, -0xe4, 0x12, 0x1a, 0x4a, 0xe5, 0x55, 0x54, 0x03, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x07, 0xef, 0x12, -0x1a, 0x4a, 0xe5, 0x55, 0x54, 0x04, 0xff, 0xc3, 0x13, 0x90, 0x00, 0x09, 0x12, 0x1a, 0x4a, 0x90, -0x00, 0x07, 0x12, 0x1a, 0x0b, 0x70, 0x13, 0x12, 0x22, 0x2d, 0xe9, 0x24, 0x09, 0xf9, 0xe4, 0x3a, -0xfa, 0x12, 0x19, 0xf2, 0xff, 0xc3, 0x13, 0x12, 0x1a, 0x38, 0x12, 0x22, 0x78, 0x24, 0x08, 0x12, -0x21, 0xf3, 0xe0, 0xfe, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x07, 0x12, 0x21, 0xf3, 0xe0, -0xfd, 0xee, 0xed, 0x12, 0x22, 0x2c, 0x90, 0x00, 0x03, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0xef, 0x12, -0x31, 0xc7, 0x7d, 0x0a, 0xe4, 0xff, 0x12, 0x2f, 0x18, 0x02, 0x10, 0x86, 0x90, 0xfa, 0xe3, 0xe0, -0xb4, 0x03, 0x06, 0x7e, 0x00, 0x7f, 0x40, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x08, 0x90, 0xfa, 0xd7, -0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0xff, 0x7e, 0x00, 0x90, 0xfa, -0xd3, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x70, 0x03, 0x7f, 0x08, 0x22, 0x90, 0x00, 0x08, 0x12, 0x1a, -0x98, 0xff, 0x90, 0xfa, 0xd5, 0xe5, 0xf0, 0xf0, 0xa3, 0xef, 0xf0, 0xae, 0x02, 0xaf, 0x01, 0x8e, -0x50, 0x8f, 0x51, 0x74, 0x0a, 0x25, 0x51, 0xf5, 0x51, 0xe4, 0x35, 0x50, 0xf5, 0x50, 0x90, 0xfa, -0xd8, 0xe0, 0xff, 0x14, 0xfe, 0x90, 0xfa, 0xd6, 0xe0, 0x5e, 0xfe, 0xc3, 0xef, 0x9e, 0xff, 0x90, -0xfa, 0xda, 0xf0, 0xc3, 0x90, 0xfa, 0xd4, 0xe0, 0x9f, 0x90, 0xfa, 0xd3, 0xe0, 0x94, 0x00, 0x50, -0x06, 0xa3, 0xe0, 0x90, 0xfa, 0xda, 0xf0, 0x12, 0x1f, 0xfb, 0x60, 0x03, 0xe0, 0xff, 0x22, 0x12, -0x2d, 0x5a, 0x90, 0xfa, 0xd3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x60, 0x2b, 0x90, 0xfa, 0xd7, -0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xd3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x07, 0xe0, 0x90, 0xfa, 0xda, -0xf0, 0x80, 0x08, 0x90, 0xfa, 0xd4, 0xe0, 0x90, 0xfa, 0xda, 0xf0, 0x12, 0x1f, 0xfb, 0x60, 0x03, -0xe0, 0xff, 0x22, 0x12, 0x2d, 0x5a, 0x80, 0xca, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x52, 0xe4, 0xf5, -0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x7f, 0x00, 0x22, 0xaa, 0x50, 0xa9, 0x51, 0x7b, -0x01, 0x90, 0xfa, 0xd5, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xda, 0xe0, 0xf5, 0x4a, 0x12, -0x28, 0x9f, 0x90, 0xfa, 0xd9, 0xef, 0xf0, 0x22, 0xef, 0x24, 0xae, 0x60, 0x52, 0x24, 0xfe, 0x60, -0x2e, 0x24, 0xfe, 0x70, 0x03, 0x02, 0x20, 0xbb, 0x24, 0x06, 0x60, 0x03, 0x02, 0x21, 0x03, 0x78, -0x71, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xa5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x33, -0x90, 0xfa, 0x91, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xaf, 0x74, 0x01, 0xf0, 0x22, 0x78, -0x72, 0xe6, 0x54, 0xfb, 0xf6, 0x90, 0xff, 0xb5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x43, -0x90, 0xfa, 0x93, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xb0, 0x74, 0x01, 0xf0, 0x22, 0x90, -0xfa, 0x9d, 0xe0, 0xa3, 0x20, 0xe5, 0x03, 0x02, 0x21, 0x03, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, -0xca, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xca, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, -0xa6, 0x12, 0x22, 0x5d, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xca, 0xf0, 0x80, 0xe6, 0x90, 0xfa, -0xcb, 0xe0, 0xff, 0x74, 0x34, 0xfe, 0x12, 0x2c, 0xb4, 0xef, 0x70, 0x57, 0x90, 0xfa, 0xcb, 0xe0, -0xff, 0x74, 0x34, 0x90, 0xfa, 0x95, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0x90, 0xfa, 0xa7, 0xe0, 0xa3, -0x30, 0xe5, 0x40, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xca, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xca, -0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xb6, 0x12, 0x22, 0x5d, 0x90, 0xff, 0xb6, -0xe0, 0x90, 0xfa, 0xca, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xcb, 0xe0, 0xff, 0x74, 0x44, 0xfe, 0x12, -0x2c, 0xb4, 0xef, 0x70, 0x0e, 0x90, 0xfa, 0xcb, 0xe0, 0xff, 0x74, 0x44, 0x90, 0xfa, 0x97, 0xf0, -0xef, 0xa3, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, -0x00, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, -0x07, 0x90, 0xff, 0x92, 0xe0, 0xff, 0x90, 0xfa, 0xc9, 0xf0, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0xef, -0x12, 0x1b, 0x4c, 0x21, 0xbb, 0x26, 0x21, 0xbb, 0x2e, 0x21, 0x5e, 0x30, 0x21, 0x5e, 0x32, 0x21, -0x6c, 0x38, 0x21, 0x7e, 0x3a, 0x21, 0xb0, 0x3e, 0x21, 0x9b, 0x44, 0x21, 0x90, 0x46, 0x21, 0xa6, -0x50, 0x21, 0xa6, 0x52, 0x21, 0xa6, 0x54, 0x21, 0xa6, 0x56, 0x00, 0x00, 0x21, 0xc0, 0x90, 0xfa, -0xc9, 0xe0, 0xfd, 0x7c, 0x00, 0x7f, 0x01, 0x12, 0x11, 0x16, 0x80, 0x62, 0x7c, 0x00, 0x7d, 0x01, -0x7f, 0x03, 0x12, 0x11, 0x16, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x50, 0x7c, 0x00, -0x7d, 0x01, 0x7f, 0x02, 0x12, 0x11, 0x16, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x40, 0xf0, 0x80, 0x3e, -0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x05, 0x12, 0x11, 0x16, 0x80, 0x33, 0x7c, 0x00, 0x7d, 0x01, 0x7f, -0x06, 0x12, 0x11, 0x16, 0x80, 0x28, 0x90, 0xfa, 0xc9, 0xe0, 0xff, 0x12, 0x20, 0x18, 0x80, 0x1e, -0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x04, 0x12, 0x11, 0x16, 0x80, 0x13, 0x12, 0x27, 0x8d, 0x80, 0x0e, -0x90, 0xfa, 0xc9, 0xe0, 0x24, 0x00, 0xff, 0xe4, 0x34, 0xff, 0xfe, 0x12, 0x2c, 0xb4, 0xd0, 0x07, -0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, -0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x24, -0x04, 0x8e, 0x83, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x74, 0x12, 0x25, 0x24, 0xf5, -0x82, 0xe4, 0x34, 0xf9, 0xf5, 0x83, 0x22, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, -0x83, 0x22, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0x7b, -0xff, 0x7a, 0x31, 0x79, 0x99, 0x7e, 0x00, 0x7f, 0x0a, 0x02, 0x19, 0xcc, 0xff, 0x90, 0xf9, 0x6c, -0x02, 0x1b, 0x3a, 0x90, 0xf9, 0x67, 0x12, 0x1b, 0x3a, 0x90, 0x00, 0x04, 0x02, 0x1a, 0x0b, 0xe6, -0xfc, 0x08, 0xe6, 0xf5, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0x22, 0x78, 0x7e, 0xe6, 0xfe, 0x08, 0xe6, -0xff, 0x22, 0xed, 0x12, 0x1a, 0x4a, 0x8f, 0x82, 0x8e, 0x83, 0xe5, 0x82, 0x22, 0xef, 0xf0, 0x90, -0xfa, 0xcb, 0xe0, 0x54, 0x0f, 0x4e, 0xfe, 0xf0, 0xef, 0x54, 0xf0, 0x4e, 0xf0, 0x22, 0x08, 0xe6, -0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x24, 0x09, 0x22, 0x78, 0x7e, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x8c, -0x83, 0x22, 0xa6, 0x07, 0xe6, 0x24, 0x6e, 0xf8, 0xe6, 0x22, 0x78, 0x7e, 0xe6, 0xfa, 0x08, 0xe6, -0xfb, 0x22, 0x26, 0xf6, 0x18, 0xee, 0x36, 0xf6, 0x22, 0x8b, 0x82, 0x8a, 0x83, 0xe5, 0x82, 0x22, -0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0x8d, 0x28, 0x90, 0xfa, 0xcf, 0xe4, 0xf0, 0xa3, 0x74, 0x02, -0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xce, 0x90, 0xfa, 0xcf, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, 0xf5, -0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xce, 0xe0, 0x65, 0x28, 0x60, 0x46, 0xa3, 0xe0, -0xff, 0xa3, 0xe0, 0xa3, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x23, 0x2f, 0x90, 0xfa, 0xce, 0xe0, -0xff, 0x90, 0xfa, 0xd1, 0xe4, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0x12, 0x23, 0x2f, 0x90, 0xfa, 0xd1, -0xe0, 0xff, 0xa3, 0xe0, 0x90, 0xfa, 0xcf, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xfa, 0xce, 0xe0, -0xa3, 0x75, 0xf0, 0x00, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x04, 0x12, 0x1a, -0x6c, 0x02, 0x22, 0xb1, 0x90, 0xfa, 0xd0, 0xe0, 0x24, 0x01, 0xff, 0x90, 0xfa, 0xcf, 0xe0, 0x34, -0x00, 0xab, 0x25, 0xaa, 0x26, 0xa9, 0x27, 0x8f, 0xf0, 0x12, 0x1a, 0xd0, 0x7f, 0x00, 0x22, 0x7b, -0x01, 0x7a, 0xfa, 0x79, 0xce, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1a, 0x6c, 0x85, -0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x25, 0xd7, 0x8f, 0x62, 0x12, 0x2a, 0x06, 0x12, 0x22, -0x4a, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x21, 0xf3, 0xe0, 0x54, 0xfb, 0xf0, 0x44, 0x02, 0xf0, 0x08, -0x12, 0x22, 0x3f, 0xe0, 0xa3, 0x30, 0xe5, 0x0c, 0x12, 0x22, 0x56, 0x24, 0x0b, 0x12, 0x21, 0xf3, -0xe0, 0x44, 0x01, 0xf0, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0xf5, 0x82, 0x8e, 0x83, 0xe0, -0x54, 0xb8, 0xfd, 0xf0, 0xe5, 0x62, 0x24, 0xfe, 0x44, 0x20, 0xfc, 0x4d, 0xf0, 0xe5, 0x82, 0x24, -0x04, 0x12, 0x21, 0xf3, 0xe0, 0x54, 0xb8, 0xf0, 0x4c, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0x74, -0x03, 0xf0, 0x18, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x05, 0x12, 0x21, 0xf3, 0xc0, -0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x74, 0x96, 0x25, 0x62, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, -0xe0, 0x54, 0xfc, 0x44, 0x03, 0xfc, 0xed, 0x4c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, -0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x21, 0xf3, 0xe0, 0x44, 0x80, 0xf0, -0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x62, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, 0x12, -0x10, 0x03, 0x7f, 0x02, 0x12, 0x12, 0x19, 0x78, 0x67, 0xe6, 0x44, 0x02, 0xf6, 0xd2, 0xb0, 0xd2, -0xb1, 0x90, 0xf9, 0x15, 0xe0, 0x30, 0xe7, 0x07, 0x90, 0xff, 0x9e, 0xe4, 0xf0, 0x80, 0x36, 0xd2, -0xb3, 0x90, 0xff, 0xa4, 0xe0, 0x90, 0xfa, 0x7b, 0xf0, 0x90, 0xff, 0xb4, 0xe0, 0x90, 0xfa, 0x7c, -0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x90, 0xfa, 0x79, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x90, 0xfa, 0x7a, -0xf0, 0x90, 0xff, 0xa4, 0x74, 0x30, 0xf0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xff, 0xa2, 0x74, 0x40, -0xf0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xfa, 0xe4, 0xe5, 0xa8, 0xf0, 0x75, 0xa8, 0x81, 0x90, 0xff, -0x92, 0xe0, 0x60, 0x04, 0xe4, 0xf0, 0x80, 0xf6, 0x90, 0xff, 0xfd, 0x74, 0x3a, 0xf0, 0x43, 0x87, -0x01, 0x00, 0x00, 0x00, 0x90, 0xfa, 0x7b, 0xe0, 0x90, 0xff, 0xa4, 0xf0, 0x90, 0xfa, 0x7c, 0xe0, -0x90, 0xff, 0xb4, 0xf0, 0x90, 0xfa, 0x79, 0xe0, 0x90, 0xff, 0xa2, 0xf0, 0x90, 0xfa, 0x7a, 0xe0, -0x90, 0xff, 0xb2, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x60, 0x02, 0xc2, 0xb3, 0x90, 0xfa, 0xe4, 0xe0, -0xf5, 0xa8, 0x02, 0x10, 0x86, 0x8b, 0x5c, 0x8a, 0x5d, 0x89, 0x5e, 0x12, 0x2d, 0x3c, 0x90, 0xfa, -0xc0, 0x12, 0x1b, 0x43, 0xaa, 0x5d, 0xa9, 0x5e, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0x43, 0x90, 0xfa, -0xc4, 0xe4, 0x75, 0xf0, 0x0a, 0x12, 0x1a, 0x6c, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0x3a, 0xe9, 0x24, -0x01, 0xf9, 0xe4, 0x3a, 0xfa, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x43, 0xab, 0x5c, 0xaa, 0x5d, 0xa9, -0x5e, 0x12, 0x2d, 0x48, 0xe0, 0xff, 0xc3, 0x13, 0xf0, 0xe4, 0x78, 0x82, 0xf6, 0x90, 0xfa, 0xbe, -0xe0, 0xff, 0x78, 0x82, 0xe6, 0xc3, 0x9f, 0x50, 0x4a, 0x90, 0xfa, 0xc0, 0x12, 0x2d, 0x1d, 0xff, -0x78, 0x83, 0xf6, 0x90, 0xfa, 0xc3, 0x12, 0x2d, 0x1d, 0xfe, 0xf4, 0x5f, 0xff, 0x78, 0x83, 0xf6, -0x12, 0x2d, 0x1a, 0x5e, 0x4f, 0xff, 0x78, 0x83, 0xf6, 0x12, 0x2d, 0x23, 0x75, 0xf0, 0x02, 0x12, -0x1a, 0x6c, 0x90, 0xfa, 0xc4, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1a, 0x6c, 0xab, 0x5c, 0xaa, 0x5d, -0xa9, 0x5e, 0x90, 0x00, 0x04, 0x12, 0x1a, 0x0b, 0x30, 0xe4, 0x03, 0x12, 0x2d, 0x32, 0x78, 0x82, -0x06, 0x80, 0xaa, 0xe4, 0x90, 0xfa, 0xbf, 0xf0, 0x22, 0x8b, 0x56, 0x8a, 0x57, 0x89, 0x58, 0x90, -0xfa, 0xbf, 0x74, 0x06, 0xf0, 0xe4, 0x90, 0xfa, 0xbe, 0xf0, 0x12, 0x19, 0xf2, 0x24, 0x6e, 0x60, -0x26, 0x14, 0x70, 0x70, 0x12, 0x2d, 0x09, 0x60, 0x09, 0x24, 0x30, 0x70, 0x12, 0x12, 0x24, 0x95, -0x80, 0x62, 0x12, 0x2d, 0x53, 0x12, 0x1f, 0x2c, 0x90, 0xfa, 0xbf, 0xef, 0xf0, 0x80, 0x55, 0x90, -0xfa, 0xbf, 0x74, 0x81, 0xf0, 0x80, 0x4d, 0x12, 0x2d, 0x09, 0x60, 0x09, 0x24, 0x30, 0x70, 0x3e, -0x12, 0x2c, 0x5f, 0x80, 0x3f, 0xe5, 0x58, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x57, 0xfa, 0x7b, 0x01, -0xc0, 0x03, 0xc0, 0x02, 0xc0, 0x01, 0x12, 0x2d, 0x53, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0xfd, -0x90, 0x00, 0x08, 0x12, 0x1a, 0x98, 0xf5, 0x2e, 0x85, 0xf0, 0x2d, 0xd0, 0x01, 0xd0, 0x02, 0xd0, -0x03, 0x12, 0x25, 0xd7, 0x90, 0xfa, 0xbe, 0xef, 0xf0, 0xe4, 0xa3, 0xf0, 0x80, 0x06, 0x90, 0xfa, -0xbf, 0x74, 0x81, 0xf0, 0x90, 0xfa, 0xbf, 0xe0, 0x12, 0x2d, 0x53, 0x90, 0x00, 0x02, 0x12, 0x1a, -0x4a, 0x90, 0xfa, 0xbe, 0xe0, 0xff, 0x22, 0x8b, 0x29, 0x8a, 0x2a, 0x89, 0x2b, 0x8d, 0x2c, 0xe5, -0x2c, 0x70, 0x03, 0xaf, 0x2c, 0x22, 0x12, 0x2d, 0x82, 0x70, 0x16, 0x12, 0x2d, 0xa1, 0xe5, 0x2d, -0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0x1b, 0x50, 0xf2, 0x12, 0x26, 0x64, 0x40, 0x0b, 0x7f, 0x00, -0x22, 0x12, 0x2d, 0xa1, 0x12, 0x26, 0x64, 0x50, 0xf8, 0x90, 0xff, 0xf3, 0x74, 0xa1, 0xf0, 0xe5, -0x2c, 0xb4, 0x01, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0xf1, 0xe4, 0xf0, -0xf5, 0x2f, 0xe5, 0x2c, 0x14, 0xff, 0xe5, 0x2f, 0xc3, 0x9f, 0x50, 0x2a, 0x12, 0x31, 0x04, 0x40, -0x03, 0xaf, 0x2f, 0x22, 0xc3, 0xe5, 0x2c, 0x95, 0x2f, 0xff, 0xbf, 0x02, 0x07, 0x90, 0xff, 0xf0, -0xe0, 0x44, 0x02, 0xf0, 0x12, 0x2d, 0x94, 0x05, 0x2f, 0x74, 0x01, 0x25, 0x2b, 0xf5, 0x2b, 0xe4, -0x35, 0x2a, 0xf5, 0x2a, 0x80, 0xcc, 0x12, 0x31, 0x04, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x12, 0x2d, -0x94, 0xaf, 0x2c, 0x22, 0x90, 0xff, 0xf1, 0xe5, 0x2e, 0xf0, 0x02, 0x31, 0x1b, 0x12, 0x10, 0x03, -0x78, 0x84, 0x12, 0x22, 0x82, 0x30, 0xe1, 0x08, 0x7f, 0x13, 0x12, 0x30, 0xec, 0x02, 0x26, 0xfb, -0x78, 0x84, 0xe6, 0xf9, 0x24, 0x12, 0x12, 0x21, 0xff, 0xe0, 0xff, 0x30, 0xe7, 0x40, 0x54, 0x03, -0x60, 0x1e, 0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x44, 0x04, -0xf0, 0x80, 0x46, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfd, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x80, 0x39, -0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x80, -0x28, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf7, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1b, 0xef, 0x54, -0x03, 0x60, 0x14, 0xe9, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, 0x07, -0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, 0xf0, 0xc2, 0xb3, 0x90, 0xf9, 0x17, 0xe0, 0x04, 0xf0, 0xaf, -0x01, 0x12, 0x22, 0x33, 0xfd, 0x12, 0x2f, 0x49, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x75, 0xa8, -0x40, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x8b, 0x02, 0x27, 0x48, 0x02, 0x30, 0xcf, -0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2, 0x08, 0xdf, 0xf4, -0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, -0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, -0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x2b, 0x4c, 0xe4, 0x7e, 0x01, 0x93, 0x60, -0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, -0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, -0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3, 0xc8, -0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe, 0xe4, 0xf5, 0x22, -0x12, 0x1d, 0x12, 0xe0, 0xb4, 0x04, 0x0d, 0xe5, 0x22, 0x24, 0x03, 0xff, 0x12, 0x2f, 0x77, 0x12, -0x1d, 0x12, 0xe4, 0xf0, 0x05, 0x22, 0xe5, 0x22, 0xc3, 0x94, 0x02, 0x40, 0xe3, 0xe4, 0xf5, 0x22, -0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x91, 0x12, 0x1d, 0x53, 0x60, 0x2c, 0x12, 0x2c, 0xb4, -0xef, 0x60, 0x52, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x91, 0x12, 0x1b, 0x1c, 0xe4, 0xf0, -0xa3, 0xf0, 0x75, 0xf0, 0x0a, 0xe5, 0x22, 0x90, 0xfa, 0x9d, 0x12, 0x1b, 0x1c, 0xe0, 0xa3, 0x30, -0xe6, 0x33, 0x12, 0x1d, 0x12, 0x74, 0x04, 0xf0, 0x22, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, -0x95, 0x12, 0x1d, 0x53, 0x60, 0x16, 0x12, 0x2c, 0xb4, 0xef, 0x60, 0x19, 0x75, 0xf0, 0x02, 0xe5, -0x22, 0x90, 0xfa, 0x95, 0x12, 0x1b, 0x1c, 0xe4, 0xf0, 0xa3, 0xf0, 0x22, 0x05, 0x22, 0xe5, 0x22, -0xc3, 0x94, 0x02, 0x40, 0x9b, 0x22, 0xe4, 0xff, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xfe, 0xef, -0xc3, 0x9e, 0x50, 0x17, 0x74, 0xf0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xe0, 0x12, -0x1c, 0x11, 0x12, 0x1a, 0x38, 0x0f, 0x12, 0x1c, 0x00, 0x80, 0xdd, 0xef, 0xfd, 0xc3, 0xe5, 0x3a, -0x9d, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, 0xd3, 0xe5, 0x3a, 0x94, 0x00, 0xe5, 0x39, -0x94, 0x00, 0x40, 0x06, 0xe4, 0x90, 0xff, 0x83, 0xf0, 0x22, 0x12, 0x1d, 0x2f, 0x12, 0x1d, 0x84, -0x12, 0x1d, 0x76, 0x12, 0x19, 0xf2, 0x24, 0x6e, 0x60, 0x1e, 0x14, 0x60, 0x1b, 0x24, 0x8e, 0x70, -0x2d, 0x90, 0x00, 0x01, 0x12, 0x1a, 0x0b, 0xff, 0x24, 0xfc, 0x60, 0x03, 0x04, 0x70, 0x1f, 0xef, -0xfd, 0x7c, 0x00, 0x7f, 0x0d, 0x02, 0x11, 0x16, 0x12, 0x1d, 0x8b, 0x12, 0x25, 0x39, 0x12, 0x1c, -0xd9, 0x12, 0x1a, 0x0b, 0x60, 0x03, 0x02, 0x31, 0xbd, 0xe4, 0xff, 0x12, 0x31, 0xb1, 0x22, 0x8b, -0x45, 0x8a, 0x46, 0x89, 0x47, 0x8c, 0x48, 0x8d, 0x49, 0xd2, 0x00, 0x12, 0x2d, 0x82, 0x70, 0x16, -0x12, 0x2d, 0xa1, 0xe5, 0x48, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0x1b, 0x50, 0xf2, 0x12, 0x29, -0x14, 0x40, 0x0b, 0x7f, 0x18, 0x22, 0x12, 0x2d, 0xa1, 0x12, 0x29, 0x14, 0x50, 0xf8, 0xe4, 0xf5, -0x4b, 0xe5, 0x4a, 0x14, 0xff, 0xe5, 0x4b, 0xc3, 0x9f, 0x50, 0x17, 0x12, 0x29, 0x04, 0x40, 0x03, -0x7f, 0x18, 0x22, 0x05, 0x4b, 0x74, 0x01, 0x25, 0x47, 0xf5, 0x47, 0xe4, 0x35, 0x46, 0xf5, 0x46, -0x80, 0xdf, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x29, 0x04, 0x40, 0x03, 0x7f, 0x18, -0x22, 0x7f, 0x00, 0x22, 0xab, 0x45, 0xaa, 0x46, 0xa9, 0x47, 0x12, 0x19, 0xf2, 0x90, 0xff, 0xf1, -0xf0, 0x02, 0x31, 0x1b, 0x90, 0xff, 0xf1, 0xe5, 0x49, 0xf0, 0x02, 0x31, 0x1b, 0x7b, 0x01, 0x7a, -0xfa, 0x79, 0xcc, 0xe4, 0xfd, 0x12, 0x22, 0xa0, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x09, 0x12, -0x1a, 0x6c, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, -0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x90, 0xff, 0xf7, 0xe5, -0x23, 0x12, 0x29, 0x78, 0x90, 0xff, 0xf6, 0xe5, 0x23, 0xf0, 0x90, 0xfa, 0xcc, 0xe4, 0xf0, 0xa3, -0x74, 0x06, 0x12, 0x29, 0x78, 0xe5, 0x23, 0x30, 0xe0, 0x07, 0x90, 0xff, 0xfc, 0x74, 0x94, 0xf0, -0x22, 0x90, 0xff, 0xfc, 0x74, 0x90, 0xf0, 0x22, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, -0xfa, 0xcc, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1a, 0x82, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, -0x02, 0x25, 0xd7, 0x90, 0xff, 0x93, 0x74, 0x2a, 0xf0, 0x90, 0xff, 0xff, 0xe0, 0x60, 0x06, 0x90, -0xff, 0xfc, 0x74, 0x10, 0xf0, 0x90, 0xff, 0x91, 0xe0, 0x44, 0x90, 0xf0, 0xe4, 0x90, 0xf9, 0x15, -0xf0, 0xa3, 0xf0, 0x12, 0x2a, 0x78, 0x12, 0x16, 0x42, 0x12, 0x2f, 0xcd, 0x7e, 0x07, 0x7f, 0xd0, -0x12, 0x11, 0xe2, 0x7e, 0x0f, 0x7f, 0xa0, 0x12, 0x11, 0xfc, 0xe4, 0x78, 0x77, 0xf6, 0x78, 0x77, -0xe6, 0xff, 0xc3, 0x94, 0x06, 0x50, 0x0b, 0x74, 0x6e, 0x2f, 0xf8, 0xe4, 0xf6, 0x78, 0x77, 0x06, -0x80, 0xec, 0x7f, 0x03, 0x12, 0x2e, 0xb3, 0x90, 0xf9, 0x15, 0xe0, 0x20, 0xe4, 0x05, 0x7f, 0x04, -0x12, 0x2e, 0xb3, 0x90, 0xff, 0x9b, 0xe4, 0xf0, 0x90, 0xff, 0x9a, 0xf0, 0x90, 0xff, 0xe8, 0xe0, -0x54, 0x1f, 0xf0, 0xd2, 0xa8, 0x22, 0x15, 0x65, 0xa8, 0x65, 0xa6, 0x07, 0x30, 0x08, 0x05, 0x12, -0x11, 0x66, 0x80, 0xf8, 0xd2, 0x08, 0xa8, 0x65, 0xe6, 0xff, 0xb4, 0x03, 0x0f, 0x78, 0x7c, 0x76, -0xff, 0x08, 0x76, 0xe0, 0x08, 0x76, 0xff, 0x08, 0x76, 0xa0, 0x80, 0x0d, 0x78, 0x7c, 0x76, 0xff, -0x08, 0x76, 0xe2, 0x08, 0x76, 0xff, 0x08, 0x76, 0xb0, 0x78, 0x80, 0x76, 0xfa, 0x08, 0x76, 0x9b, -0xef, 0x24, 0xfd, 0x75, 0xf0, 0x0a, 0xa4, 0xae, 0xf0, 0x12, 0x22, 0x92, 0x7b, 0x01, 0x7a, 0xff, -0x79, 0x48, 0x78, 0x68, 0x12, 0x1b, 0x31, 0xa8, 0x65, 0xe6, 0x24, 0xfd, 0x75, 0xf0, 0x08, 0xa4, -0xff, 0xae, 0xf0, 0x78, 0x6a, 0x12, 0x22, 0x92, 0x79, 0x08, 0x78, 0x6b, 0x12, 0x1b, 0x31, 0x78, -0x6d, 0xef, 0x12, 0x22, 0x92, 0x05, 0x65, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xab, 0xf0, 0xe0, -0x44, 0x20, 0xf0, 0x90, 0xfa, 0xe3, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcc, 0xe4, -0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x25, 0xd7, 0x7e, 0x00, 0x90, 0xfa, 0xe1, 0xee, 0xf0, -0xa3, 0xef, 0xf0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcc, 0xe0, 0xb4, 0x52, 0x09, 0x90, 0xf9, -0x15, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x29, 0x90, 0xfa, 0xe1, 0xe0, 0x70, 0x04, 0xa3, 0xe0, 0x64, -0x01, 0x70, 0x10, 0x90, 0xfa, 0xcc, 0xe0, 0xb4, 0x10, 0x09, 0x90, 0xf9, 0x15, 0xe0, 0x44, 0x10, -0xf0, 0x80, 0x0d, 0x90, 0xfa, 0xe3, 0x74, 0x03, 0xf0, 0x90, 0xf9, 0x15, 0xe0, 0x54, 0xef, 0xf0, -0x90, 0xff, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x22, 0x12, 0x10, 0x03, 0x78, 0x8a, 0xef, 0xf6, 0x12, -0x2a, 0x06, 0x12, 0x22, 0x33, 0x30, 0xe0, 0x25, 0x12, 0x22, 0x07, 0xe0, 0x54, 0x7f, 0xf0, 0x78, -0x6b, 0x12, 0x1b, 0x28, 0x90, 0x00, 0x02, 0x12, 0x1a, 0x0b, 0x30, 0xe7, 0x09, 0x90, 0x00, 0x02, -0xe4, 0x12, 0x1a, 0x4a, 0x80, 0xe9, 0x12, 0x22, 0x07, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x22, 0x33, -0x30, 0xe1, 0x1e, 0x12, 0x21, 0xe9, 0xe0, 0x54, 0x7f, 0xf0, 0x12, 0x31, 0x5c, 0x78, 0x68, 0x12, -0x1b, 0x28, 0x90, 0x00, 0x02, 0x74, 0x80, 0x12, 0x1a, 0x4a, 0x12, 0x21, 0xe9, 0xe0, 0x44, 0x80, -0xf0, 0x12, 0x31, 0xc7, 0xe4, 0xff, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x03, 0x68, 0x01, 0xff, -0x48, 0x03, 0x6b, 0x01, 0xff, 0x08, 0x02, 0x66, 0x00, 0x00, 0x44, 0xfa, 0x95, 0x00, 0x00, 0x00, -0x00, 0x44, 0xfa, 0x91, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xaf, 0x00, 0x00, 0x42, 0xfa, 0x7b, -0x00, 0x00, 0x42, 0xfa, 0x79, 0x00, 0x00, 0x42, 0xf9, 0x6a, 0xff, 0xff, 0x42, 0xfa, 0x77, 0x00, -0x00, 0x43, 0xf9, 0x18, 0x0a, 0x32, 0x02, 0x41, 0xf9, 0x65, 0x20, 0x41, 0xf9, 0x66, 0x20, 0x41, -0xf9, 0x63, 0x00, 0x41, 0xf9, 0x64, 0x00, 0x44, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xf9, -0x15, 0x00, 0x00, 0x41, 0xf9, 0x17, 0x00, 0x01, 0x20, 0x00, 0x41, 0xf8, 0x04, 0x00, 0x00, 0x12, -0x10, 0x03, 0x78, 0x85, 0xef, 0xf6, 0x12, 0x30, 0x93, 0x12, 0x30, 0xec, 0x78, 0x85, 0xe6, 0xff, -0x24, 0x12, 0x12, 0x21, 0xff, 0xe0, 0xfe, 0x30, 0xe7, 0x16, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, -0x9e, 0xe0, 0x54, 0xfa, 0xf0, 0x80, 0x22, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf5, 0xf0, 0x80, 0x19, -0xee, 0x54, 0x03, 0x60, 0x14, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, 0xf0, -0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xf9, 0x17, 0xe0, 0x14, 0xf0, 0xe0, -0x70, 0x02, 0xd2, 0xb3, 0x02, 0x10, 0x86, 0x12, 0x1d, 0x6c, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, -0xe5, 0x39, 0x64, 0x01, 0x60, 0x48, 0xc3, 0xe5, 0x3a, 0x94, 0x08, 0xe5, 0x39, 0x94, 0x00, 0x40, -0x11, 0x7f, 0x08, 0xef, 0xe5, 0x3a, 0x94, 0x08, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, -0x80, 0x05, 0xaf, 0x3a, 0x12, 0x1d, 0x84, 0xe4, 0xfe, 0xee, 0xc3, 0x9f, 0x50, 0x19, 0x12, 0x1c, -0x11, 0x12, 0x19, 0xf2, 0xfd, 0x74, 0xf8, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xed, -0xf0, 0x0e, 0x12, 0x1c, 0x00, 0x80, 0xe2, 0xef, 0x54, 0x7f, 0x90, 0xff, 0x81, 0xf0, 0x22, 0x8b, -0x59, 0x8a, 0x5a, 0x89, 0x5b, 0x12, 0x2d, 0x48, 0x70, 0x05, 0xa3, 0x74, 0x08, 0xf0, 0x22, 0xab, -0x59, 0xaa, 0x5a, 0xa9, 0x5b, 0x12, 0x2d, 0x3c, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x43, 0xe5, 0x5b, -0x24, 0x03, 0xf9, 0xe4, 0x35, 0x5a, 0xfa, 0x90, 0xfa, 0xc0, 0x12, 0x1b, 0x43, 0xe4, 0x90, 0xfa, -0xbf, 0xf0, 0x78, 0x8b, 0xf6, 0x90, 0xfa, 0xbe, 0xe0, 0xff, 0x78, 0x8b, 0xe6, 0xc3, 0x9f, 0x50, -0x12, 0x12, 0x2d, 0x1a, 0xff, 0x12, 0x2d, 0x23, 0x12, 0x2d, 0x36, 0x78, 0x8b, 0x06, 0x12, 0x2d, -0x32, 0x80, 0xe2, 0x22, 0xad, 0x07, 0xac, 0x06, 0x90, 0x31, 0x4d, 0xe4, 0x93, 0xff, 0x78, 0x74, -0xf6, 0x54, 0x0f, 0x12, 0x1c, 0xf8, 0xe0, 0x08, 0x76, 0x00, 0x08, 0xf6, 0x18, 0x12, 0x1c, 0x29, -0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x78, 0x75, 0xee, 0xf6, 0x08, 0xef, 0xf6, 0xee, -0x44, 0xf8, 0x18, 0xf6, 0xef, 0x08, 0xf6, 0x90, 0xff, 0x7a, 0xe0, 0x20, 0xe7, 0x03, 0x7f, 0x00, -0x22, 0x78, 0x75, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0xec, 0xf0, 0xa3, 0xed, 0xf0, -0x90, 0xff, 0x7a, 0x74, 0x02, 0xf0, 0x7f, 0x01, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x90, -0x00, 0x03, 0x12, 0x1a, 0x0b, 0x54, 0xf0, 0x24, 0xa0, 0x22, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0x3a, -0x02, 0x19, 0xf2, 0x90, 0xfa, 0xc0, 0x12, 0x1b, 0x3a, 0xef, 0x12, 0x1a, 0x38, 0x90, 0xfa, 0xc7, -0xe4, 0x22, 0x90, 0xfa, 0xc1, 0xe4, 0x75, 0xf0, 0x01, 0x02, 0x1a, 0x6c, 0x90, 0x00, 0x08, 0x12, -0x1a, 0x98, 0xaa, 0xf0, 0xf9, 0x7b, 0x01, 0x22, 0x90, 0x00, 0x05, 0x12, 0x1a, 0x0b, 0x90, 0xfa, -0xbe, 0xf0, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x22, 0x90, 0xfa, 0xda, 0xe0, 0xff, 0x7e, -0x00, 0xc3, 0x90, 0xfa, 0xd4, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xd3, 0xe0, 0x9e, 0xf0, 0x90, 0xfa, -0xd5, 0xee, 0x8f, 0xf0, 0x12, 0x1a, 0x6c, 0xef, 0x25, 0x51, 0xf5, 0x51, 0xee, 0x35, 0x50, 0xf5, -0x50, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0xfa, 0xe3, -0xe0, 0x64, 0x03, 0x22, 0x90, 0xff, 0xf2, 0xe0, 0xab, 0x29, 0xaa, 0x2a, 0xa9, 0x2b, 0x02, 0x1a, -0x38, 0x90, 0xff, 0xf3, 0x74, 0xa0, 0xf0, 0x22, 0x8f, 0x64, 0xed, 0x70, 0x0f, 0xe5, 0x64, 0xb4, -0x03, 0x05, 0x7f, 0x01, 0x02, 0x31, 0x32, 0x7f, 0x02, 0x02, 0x31, 0x32, 0xaf, 0x64, 0x12, 0x2a, -0x06, 0x74, 0x6e, 0x25, 0x64, 0xf8, 0xe6, 0x30, 0xe2, 0x0b, 0xd2, 0x09, 0x12, 0x1c, 0x83, 0xe0, -0x54, 0x7f, 0xf0, 0x80, 0x02, 0xc2, 0x09, 0xe5, 0x64, 0xb4, 0x03, 0x07, 0x7f, 0x81, 0x12, 0x31, -0x32, 0x80, 0x05, 0x7f, 0x82, 0x12, 0x31, 0x32, 0x30, 0x09, 0x07, 0x12, 0x1c, 0x83, 0xe0, 0x44, -0x80, 0xf0, 0x12, 0x31, 0xc7, 0x22, 0x12, 0x10, 0x03, 0x90, 0xff, 0xfd, 0xe0, 0x44, 0x60, 0xf0, -0xd2, 0x01, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0x00, 0xe0, 0x30, 0xe7, 0x13, -0x90, 0xff, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x35, 0x80, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x01, -0xf0, 0x80, 0x0d, 0x12, 0x1d, 0x2f, 0x53, 0x35, 0x7f, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0xfe, 0xf0, -0x90, 0xff, 0x81, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x02, 0xb0, 0x12, 0x1d, 0x37, 0x02, 0x10, 0x86, -0x12, 0x10, 0x03, 0x78, 0x89, 0xef, 0xf6, 0xd2, 0x00, 0x12, 0x2a, 0x06, 0x90, 0xf9, 0x67, 0x12, -0x1b, 0x3a, 0xe9, 0x24, 0x03, 0xf9, 0xe4, 0x3a, 0xfa, 0xc0, 0x02, 0x78, 0x80, 0xe6, 0xfe, 0x08, -0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0xd0, 0x02, 0x12, 0x22, 0x25, 0x12, 0x31, 0xc7, -0x78, 0x89, 0xe6, 0xff, 0x12, 0x13, 0x3f, 0x12, 0x30, 0xec, 0x02, 0x10, 0x86, 0x8f, 0x63, 0x12, -0x2a, 0x06, 0x12, 0x22, 0x07, 0xe0, 0x54, 0x3f, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x21, 0xf3, -0xe0, 0x54, 0x3f, 0xf0, 0x08, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x21, 0xf3, -0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x63, 0xf8, 0x74, 0xfb, 0x56, 0xf6, -0x7f, 0x00, 0x22, 0x8f, 0x23, 0xc2, 0x08, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x12, 0x78, 0x7e, 0x12, -0x21, 0xeb, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x22, 0x4a, 0x12, 0x21, 0xef, 0xe0, 0x20, 0xe0, 0xf6, -0xef, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x3e, 0xf5, 0x83, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x31, 0xc7, -0xaf, 0x23, 0x12, 0x13, 0x3f, 0x22, 0x12, 0x10, 0x03, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x4a, 0x24, -0x06, 0x12, 0x21, 0xf1, 0xe0, 0xfd, 0x12, 0x22, 0x2d, 0x90, 0x00, 0x03, 0x12, 0x22, 0x52, 0x24, -0x05, 0x12, 0x21, 0xf3, 0xe0, 0x90, 0x00, 0x04, 0x12, 0x1a, 0x4a, 0x12, 0x31, 0xc7, 0x7d, 0x02, -0xe4, 0xff, 0x12, 0x2f, 0x18, 0x02, 0x10, 0x86, 0xae, 0x05, 0x12, 0x1c, 0xde, 0xef, 0x12, 0x1a, -0x4a, 0x0e, 0x0e, 0x0e, 0xee, 0xd3, 0x95, 0x3c, 0xe4, 0x95, 0x3b, 0x40, 0x02, 0xae, 0x3c, 0xee, -0xd3, 0x94, 0x08, 0x74, 0x80, 0x94, 0x81, 0x40, 0x0a, 0x7e, 0x03, 0x90, 0x00, 0x02, 0x74, 0x02, -0x12, 0x1a, 0x4a, 0xaf, 0x06, 0x12, 0x31, 0xb1, 0x22, 0xae, 0x07, 0xed, 0x54, 0x03, 0x64, 0x01, -0x60, 0x03, 0x7f, 0x10, 0x22, 0xed, 0x54, 0x7c, 0xc3, 0x94, 0x04, 0x50, 0x03, 0x7f, 0x0b, 0x22, -0x74, 0x6e, 0x2e, 0xf8, 0x74, 0x02, 0x46, 0xf6, 0x74, 0x96, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfa, -0xf5, 0x83, 0xed, 0xf0, 0x7f, 0x00, 0x22, 0xbf, 0x03, 0x06, 0x7c, 0xff, 0x7d, 0xe0, 0x80, 0x04, -0x7c, 0xff, 0x7d, 0xe2, 0x8d, 0x82, 0x8c, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, -0x12, 0x21, 0xf3, 0xe0, 0x44, 0x80, 0xf0, 0x74, 0x6e, 0x2f, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, -0x00, 0x22, 0x12, 0x10, 0x03, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, -0x16, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xff, 0xc3, 0xe5, 0x3a, 0x9f, 0xe5, 0x39, 0x94, 0x00, -0x40, 0x05, 0x12, 0x28, 0x16, 0x80, 0x03, 0x12, 0x31, 0xbd, 0x02, 0x10, 0x86, 0x90, 0xff, 0xfc, -0xe0, 0x20, 0xe7, 0x1f, 0xc2, 0xaf, 0x7d, 0xff, 0xac, 0x05, 0x1d, 0xec, 0x60, 0x15, 0x7e, 0x04, -0x7f, 0x00, 0xef, 0x1f, 0xaa, 0x06, 0x70, 0x01, 0x1e, 0x4a, 0x60, 0xec, 0x90, 0xff, 0x92, 0xe4, -0xf0, 0x80, 0xef, 0x22, 0x12, 0x10, 0x03, 0x78, 0x66, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x30, 0xe0, -0x12, 0x30, 0xe1, 0x0f, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x20, 0xf0, 0x7f, 0x04, 0x12, 0x12, 0x19, -0x12, 0x1d, 0x46, 0x02, 0x10, 0x86, 0x8e, 0x5f, 0x8f, 0x60, 0xe5, 0x60, 0x15, 0x60, 0xae, 0x5f, -0x70, 0x02, 0x15, 0x5f, 0xd3, 0x94, 0x00, 0xee, 0x94, 0x00, 0x40, 0x09, 0x7e, 0x07, 0x7f, 0xd0, -0x12, 0x0f, 0xdc, 0x80, 0xe5, 0x22, 0x11, 0x94, 0x2d, 0xf6, 0x23, 0xef, 0x31, 0xa3, 0x2f, 0xf4, -0x2f, 0xa2, 0x30, 0xb2, 0x2e, 0xe6, 0x26, 0x6d, 0x2b, 0xaf, 0x30, 0x55, 0x30, 0x74, 0x1d, 0xb4, -0x2e, 0x40, 0x2a, 0xe8, 0x0e, 0x12, 0x10, 0x03, 0x78, 0x86, 0x12, 0x22, 0x82, 0x20, 0xe1, 0x07, -0x7f, 0x12, 0x12, 0x30, 0xec, 0x80, 0x0a, 0x78, 0x86, 0xe6, 0xff, 0x12, 0x23, 0x49, 0x12, 0x30, -0xec, 0x02, 0x10, 0x86, 0x12, 0x10, 0x03, 0x78, 0x87, 0x12, 0x22, 0x82, 0x20, 0xe2, 0x07, 0x7f, -0x11, 0x12, 0x30, 0xec, 0x80, 0x0a, 0x78, 0x87, 0xe6, 0xff, 0x12, 0x2e, 0x7d, 0x12, 0x30, 0xec, -0x02, 0x10, 0x86, 0x8f, 0x61, 0x12, 0x2e, 0x7d, 0xaf, 0x61, 0x12, 0x2a, 0x06, 0x12, 0x22, 0x12, -0x12, 0x31, 0xc7, 0x74, 0x6e, 0x25, 0x61, 0xf8, 0x74, 0xfd, 0x56, 0xf6, 0xaf, 0x61, 0x12, 0x13, -0x3f, 0x22, 0x12, 0x10, 0x03, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, -0x05, 0x12, 0x2c, 0x07, 0x80, 0x06, 0x12, 0x1d, 0x64, 0x12, 0x1d, 0x6c, 0x02, 0x10, 0x86, 0x12, -0x29, 0x93, 0x12, 0x12, 0xbb, 0x90, 0xf8, 0x04, 0xe0, 0xff, 0x60, 0x05, 0x7d, 0x01, 0x12, 0x12, -0x58, 0x12, 0x29, 0x1d, 0x12, 0x12, 0xf7, 0x12, 0x11, 0x74, 0x80, 0xe3, 0x12, 0x1c, 0xde, 0xef, -0x12, 0x1a, 0x4a, 0xe4, 0xf5, 0x33, 0xf5, 0x34, 0xef, 0x60, 0x03, 0x02, 0x31, 0xbd, 0xe4, 0xff, -0x12, 0x31, 0xb1, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0xa0, 0x60, 0xf7, 0xef, 0x30, 0xe5, -0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, -0x54, 0x28, 0x60, 0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, -0xd3, 0x22, 0xef, 0x30, 0xe7, 0x08, 0x12, 0x1c, 0x95, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0xef, 0x12, -0x1c, 0xe8, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0x81, 0x01, 0x82, 0x02, 0x83, 0x03, 0x87, 0x40, 0x00, -0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x08, 0x00, 0x78, 0x7e, 0x12, 0x22, -0x09, 0xa3, 0xa3, 0xe0, 0xff, 0x30, 0xe7, 0x06, 0x54, 0x7f, 0xf0, 0x44, 0x80, 0xf0, 0x22, 0x85, -0x3b, 0x39, 0x85, 0x3c, 0x3a, 0x90, 0xff, 0x82, 0xe0, 0x54, 0xf7, 0xf0, 0xa3, 0xe0, 0x54, 0x7f, -0xf0, 0x22, 0xe4, 0xfe, 0xee, 0x90, 0x31, 0x47, 0x93, 0xb5, 0x07, 0x02, 0xd3, 0x22, 0x0e, 0xbe, -0x07, 0xf2, 0xc3, 0x22, 0x00, 0x08, 0x18, 0x28, 0x38, 0x01, 0x81, 0x10, 0x0a, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x12, 0x10, 0x03, 0x7f, 0x02, 0x12, 0x10, 0x92, 0x12, 0x1d, 0x46, 0x02, 0x10, -0x86, 0x75, 0x39, 0x00, 0x8f, 0x3a, 0x12, 0x1c, 0x30, 0x12, 0x2c, 0x07, 0x22, 0x12, 0x1d, 0x6c, -0x12, 0x1d, 0x2f, 0x12, 0x1d, 0x64, 0x22, 0xc2, 0x08, 0x22, +0xe5, 0x54, 0x30, 0xe1, 0x01, 0x06, 0x78, 0x88, 0xe6, 0x12, 0x22, 0xe7, 0x90, 0x00, 0x0c, 0xef, +0x12, 0x1a, 0xfa, 0x12, 0x22, 0xb5, 0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x53, 0x07, 0x0c, +0x53, 0x06, 0xe6, 0xe5, 0x53, 0x30, 0xe5, 0x03, 0x43, 0x07, 0x01, 0xe5, 0x54, 0x20, 0xe5, 0x0e, +0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 0x03, 0x43, 0x07, 0x02, 0xe5, 0x53, +0x30, 0xe3, 0x03, 0x43, 0x07, 0x10, 0xe5, 0x53, 0x30, 0xe2, 0x03, 0x43, 0x07, 0x20, 0xe5, 0x53, +0x54, 0x03, 0x60, 0x03, 0x43, 0x07, 0x40, 0xe5, 0x53, 0x30, 0xe1, 0x03, 0x43, 0x07, 0x80, 0xe5, +0x53, 0x30, 0xe4, 0x03, 0x43, 0x06, 0x01, 0xe5, 0x53, 0x30, 0xe6, 0x03, 0x43, 0x06, 0x08, 0xe5, +0x54, 0x20, 0xe4, 0x0e, 0xe5, 0x53, 0x54, 0x7f, 0x70, 0x08, 0xe5, 0x53, 0x20, 0xe7, 0x03, 0x43, +0x06, 0x10, 0x53, 0x07, 0xfb, 0x53, 0x06, 0x79, 0x90, 0x00, 0x05, 0xee, 0x8f, 0xf0, 0x12, 0x1b, +0x9f, 0xe5, 0x55, 0x30, 0xe3, 0x12, 0x54, 0x30, 0xff, 0xc4, 0x54, 0x0f, 0x12, 0x22, 0xe7, 0x90, +0x00, 0x08, 0xef, 0x12, 0x1a, 0xfa, 0x80, 0x0a, 0x12, 0x22, 0xe8, 0x90, 0x00, 0x08, 0xe4, 0x12, +0x1a, 0xfa, 0xe5, 0x55, 0x54, 0x03, 0x12, 0x22, 0xe7, 0x90, 0x00, 0x07, 0xef, 0x12, 0x1a, 0xfa, +0xe5, 0x55, 0x54, 0x04, 0xff, 0xc3, 0x13, 0x90, 0x00, 0x09, 0x12, 0x1a, 0xfa, 0x90, 0x00, 0x07, +0x12, 0x1a, 0xbb, 0x70, 0x13, 0x12, 0x22, 0xe8, 0xe9, 0x24, 0x09, 0xf9, 0xe4, 0x3a, 0xfa, 0x12, +0x1a, 0xa2, 0xff, 0xc3, 0x13, 0x12, 0x1a, 0xe8, 0x12, 0x23, 0x27, 0x24, 0x08, 0x12, 0x22, 0xa1, +0xe0, 0xfe, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x07, 0x12, 0x22, 0xa1, 0xe0, 0xfd, 0xee, +0xed, 0x12, 0x22, 0xe7, 0x90, 0x00, 0x03, 0xee, 0x8f, 0xf0, 0x12, 0x1b, 0x9f, 0x12, 0x32, 0x84, +0x7d, 0x0a, 0xe4, 0xff, 0x12, 0x2f, 0xb4, 0x02, 0x10, 0xce, 0x90, 0xfa, 0xe6, 0xe0, 0xb4, 0x03, +0x06, 0x7e, 0x00, 0x7f, 0x40, 0x80, 0x04, 0x7e, 0x00, 0x7f, 0x08, 0x90, 0xfa, 0xda, 0xee, 0xf0, +0xa3, 0xef, 0xf0, 0x90, 0x00, 0x05, 0x12, 0x1a, 0xbb, 0xff, 0x7e, 0x00, 0x90, 0xfa, 0xd6, 0xee, +0xf0, 0xa3, 0xef, 0xf0, 0x70, 0x03, 0x7f, 0x08, 0x22, 0x90, 0x00, 0x08, 0x12, 0x1b, 0x48, 0xff, +0x90, 0xfa, 0xd8, 0xe5, 0xf0, 0xf0, 0xa3, 0xef, 0xf0, 0xae, 0x02, 0xaf, 0x01, 0x8e, 0x50, 0x8f, +0x51, 0x74, 0x0a, 0x25, 0x51, 0xf5, 0x51, 0xe4, 0x35, 0x50, 0xf5, 0x50, 0x90, 0xfa, 0xdb, 0xe0, +0xff, 0x14, 0xfe, 0x90, 0xfa, 0xd9, 0xe0, 0x5e, 0xfe, 0xc3, 0xef, 0x9e, 0xff, 0x90, 0xfa, 0xdd, +0xf0, 0xc3, 0x90, 0xfa, 0xd7, 0xe0, 0x9f, 0x90, 0xfa, 0xd6, 0xe0, 0x94, 0x00, 0x50, 0x06, 0xa3, +0xe0, 0x90, 0xfa, 0xdd, 0xf0, 0x12, 0x20, 0xa9, 0x60, 0x03, 0xe0, 0xff, 0x22, 0x12, 0x2e, 0x2b, +0x90, 0xfa, 0xd6, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x4e, 0x60, 0x2b, 0x90, 0xfa, 0xda, 0xe0, 0xfc, +0xa3, 0xe0, 0xfd, 0xd3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x07, 0xe0, 0x90, 0xfa, 0xdd, 0xf0, 0x80, +0x08, 0x90, 0xfa, 0xd7, 0xe0, 0x90, 0xfa, 0xdd, 0xf0, 0x12, 0x20, 0xa9, 0x60, 0x03, 0xe0, 0xff, +0x22, 0x12, 0x2e, 0x2b, 0x80, 0xca, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x52, 0xe4, 0xf5, 0x2d, 0xf5, +0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x7f, 0x00, 0x22, 0xaa, 0x50, 0xa9, 0x51, 0x7b, 0x01, 0x90, +0xfa, 0xd8, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0x90, 0xfa, 0xdd, 0xe0, 0xf5, 0x4a, 0x12, 0x29, 0x60, +0x90, 0xfa, 0xdc, 0xef, 0xf0, 0x22, 0xef, 0x24, 0xae, 0x60, 0x52, 0x24, 0xfe, 0x60, 0x2e, 0x24, +0xfe, 0x70, 0x03, 0x02, 0x21, 0x69, 0x24, 0x06, 0x60, 0x03, 0x02, 0x21, 0xb1, 0x78, 0x71, 0xe6, +0x54, 0xfb, 0xf6, 0x90, 0xff, 0xa5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x33, 0x90, 0xfa, +0x94, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xb2, 0x74, 0x01, 0xf0, 0x22, 0x78, 0x72, 0xe6, +0x54, 0xfb, 0xf6, 0x90, 0xff, 0xb5, 0xe0, 0xf5, 0x22, 0x44, 0x0f, 0xf0, 0x74, 0x43, 0x90, 0xfa, +0x96, 0xf0, 0xe5, 0x22, 0xa3, 0xf0, 0x90, 0xfa, 0xb3, 0x74, 0x01, 0xf0, 0x22, 0x90, 0xfa, 0xa0, +0xe0, 0xa3, 0x20, 0xe5, 0x03, 0x02, 0x21, 0xb1, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xcd, 0xf0, +0xa3, 0xf0, 0x90, 0xfa, 0xcd, 0xe0, 0xff, 0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xa6, 0x12, +0x23, 0x0d, 0x90, 0xff, 0xa6, 0xe0, 0x90, 0xfa, 0xcd, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xce, 0xe0, +0xff, 0x74, 0x34, 0xfe, 0x12, 0x2d, 0x85, 0xef, 0x70, 0x57, 0x90, 0xfa, 0xce, 0xe0, 0xff, 0x74, +0x34, 0x90, 0xfa, 0x98, 0xf0, 0xef, 0xa3, 0xf0, 0x22, 0x90, 0xfa, 0xaa, 0xe0, 0xa3, 0x30, 0xe5, +0x40, 0x90, 0xff, 0xb6, 0xe0, 0x90, 0xfa, 0xcd, 0xf0, 0xa3, 0xf0, 0x90, 0xfa, 0xcd, 0xe0, 0xff, +0x54, 0x0f, 0xfe, 0x60, 0x10, 0x90, 0xff, 0xb6, 0x12, 0x23, 0x0d, 0x90, 0xff, 0xb6, 0xe0, 0x90, +0xfa, 0xcd, 0xf0, 0x80, 0xe6, 0x90, 0xfa, 0xce, 0xe0, 0xff, 0x74, 0x44, 0xfe, 0x12, 0x2d, 0x85, +0xef, 0x70, 0x0e, 0x90, 0xfa, 0xce, 0xe0, 0xff, 0x74, 0x44, 0x90, 0xfa, 0x9a, 0xf0, 0xef, 0xa3, +0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x00, 0xc0, +0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0x90, +0xff, 0x92, 0xe0, 0xff, 0x90, 0xfa, 0xcc, 0xf0, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0xef, 0x12, 0x1b, +0xfc, 0x22, 0x69, 0x26, 0x22, 0x69, 0x2e, 0x22, 0x0c, 0x30, 0x22, 0x0c, 0x32, 0x22, 0x1a, 0x38, +0x22, 0x2c, 0x3a, 0x22, 0x5e, 0x3e, 0x22, 0x49, 0x44, 0x22, 0x3e, 0x46, 0x22, 0x54, 0x50, 0x22, +0x54, 0x52, 0x22, 0x54, 0x54, 0x22, 0x54, 0x56, 0x00, 0x00, 0x22, 0x6e, 0x90, 0xfa, 0xcc, 0xe0, +0xfd, 0x7c, 0x00, 0x7f, 0x01, 0x12, 0x11, 0x5e, 0x80, 0x62, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x03, +0x12, 0x11, 0x5e, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x50, 0x7c, 0x00, 0x7d, 0x01, +0x7f, 0x02, 0x12, 0x11, 0x5e, 0x90, 0xff, 0xfe, 0xe0, 0x44, 0x40, 0xf0, 0x80, 0x3e, 0x7c, 0x00, +0x7d, 0x01, 0x7f, 0x05, 0x12, 0x11, 0x5e, 0x80, 0x33, 0x7c, 0x00, 0x7d, 0x01, 0x7f, 0x06, 0x12, +0x11, 0x5e, 0x80, 0x28, 0x90, 0xfa, 0xcc, 0xe0, 0xff, 0x12, 0x20, 0xc6, 0x80, 0x1e, 0x7c, 0x00, +0x7d, 0x01, 0x7f, 0x04, 0x12, 0x11, 0x5e, 0x80, 0x13, 0x12, 0x28, 0x4e, 0x80, 0x0e, 0x90, 0xfa, +0xcc, 0xe0, 0x24, 0x00, 0xff, 0xe4, 0x34, 0xff, 0xfe, 0x12, 0x2d, 0x85, 0xd0, 0x07, 0xd0, 0x06, +0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x82, +0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0x24, 0x04, 0x8e, +0x83, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0x22, 0x74, 0x13, 0x25, 0x24, 0xf5, 0x82, 0xe4, +0x34, 0xf9, 0xf5, 0x83, 0x22, 0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0x22, +0x78, 0x80, 0xe6, 0xfe, 0x08, 0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0x7b, 0xff, 0x7a, +0x32, 0x79, 0x56, 0x7e, 0x00, 0x7f, 0x0a, 0x02, 0x1a, 0x7c, 0x78, 0x80, 0xe6, 0xfc, 0x08, 0xe6, +0xf5, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0x22, 0xff, 0x90, 0xf9, 0x6f, 0x02, 0x1b, 0xea, 0x90, 0xf9, +0x6a, 0x12, 0x1b, 0xea, 0x90, 0x00, 0x04, 0x02, 0x1a, 0xbb, 0x78, 0x7e, 0xe6, 0xfe, 0x08, 0xe6, +0xff, 0x22, 0xed, 0x12, 0x1a, 0xfa, 0x8f, 0x82, 0x8e, 0x83, 0xe5, 0x82, 0x22, 0xef, 0xf0, 0x90, +0xfa, 0xce, 0xe0, 0x54, 0x0f, 0x4e, 0xfe, 0xf0, 0xef, 0x54, 0xf0, 0x4e, 0xf0, 0x22, 0x78, 0x80, +0xe6, 0xfc, 0x08, 0xe6, 0x8c, 0x83, 0x22, 0x78, 0x7e, 0xe6, 0xfc, 0x08, 0xe6, 0xfd, 0x8c, 0x83, +0x22, 0xa6, 0x07, 0xe6, 0x24, 0x6e, 0xf8, 0xe6, 0x22, 0x78, 0x7e, 0xe6, 0xfa, 0x08, 0xe6, 0xfb, +0x22, 0x08, 0xe6, 0xfe, 0x08, 0xe6, 0x8e, 0x83, 0x22, 0x26, 0xf6, 0x18, 0xee, 0x36, 0xf6, 0x22, +0xef, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x3e, 0xf5, 0x83, 0x22, 0x8b, 0x82, 0x8a, 0x83, 0xe5, 0x82, +0x22, 0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0x8d, 0x28, 0x90, 0xfa, 0xd2, 0xe4, 0xf0, 0xa3, 0x74, +0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xd1, 0x90, 0xfa, 0xd2, 0xe0, 0xf5, 0x2d, 0xa3, 0xe0, +0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xd1, 0xe0, 0x65, 0x28, 0x60, 0x46, 0xa3, +0xe0, 0xff, 0xa3, 0xe0, 0xa3, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x12, 0x23, 0xf0, 0x90, 0xfa, 0xd1, +0xe0, 0xff, 0x90, 0xfa, 0xd4, 0xe4, 0x8f, 0xf0, 0x12, 0x1b, 0x1c, 0x12, 0x23, 0xf0, 0x90, 0xfa, +0xd4, 0xe0, 0xff, 0xa3, 0xe0, 0x90, 0xfa, 0xd2, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0xfa, 0xd1, +0xe0, 0xa3, 0x75, 0xf0, 0x00, 0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xd2, 0xe4, 0x75, 0xf0, 0x04, 0x12, +0x1b, 0x1c, 0x02, 0x23, 0x72, 0x90, 0xfa, 0xd3, 0xe0, 0x24, 0x01, 0xff, 0x90, 0xfa, 0xd2, 0xe0, +0x34, 0x00, 0xab, 0x25, 0xaa, 0x26, 0xa9, 0x27, 0x8f, 0xf0, 0x12, 0x1b, 0x80, 0x7f, 0x00, 0x22, +0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xd1, 0x90, 0xfa, 0xd2, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1b, 0x1c, +0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x02, 0x26, 0x98, 0x8f, 0x62, 0x12, 0x2a, 0xc7, 0x12, +0x22, 0xfa, 0x8e, 0x83, 0x24, 0x0b, 0x12, 0x22, 0xa1, 0xe0, 0x54, 0xfb, 0xf0, 0x44, 0x02, 0xf0, +0x08, 0x12, 0x22, 0xdc, 0xe0, 0xa3, 0x30, 0xe5, 0x0c, 0x12, 0x23, 0x06, 0x24, 0x0b, 0x12, 0x22, +0xa1, 0xe0, 0x44, 0x01, 0xf0, 0x78, 0x7c, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0xf5, 0x82, 0x8e, 0x83, +0xe0, 0x54, 0xb8, 0xfd, 0xf0, 0xe5, 0x62, 0x24, 0xfe, 0x44, 0x20, 0xfc, 0x4d, 0xf0, 0xe5, 0x82, +0x24, 0x04, 0x12, 0x22, 0xa1, 0xe0, 0x54, 0xb8, 0xf0, 0x4c, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, +0x74, 0x03, 0xf0, 0x18, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x8e, 0x83, 0x24, 0x05, 0x12, 0x22, 0xa1, +0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x74, 0x99, 0x25, 0x62, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, +0x83, 0xe0, 0x54, 0xfc, 0x44, 0x03, 0xfc, 0xed, 0x4c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, +0x8e, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x22, 0xa1, 0xe0, 0x44, 0x80, +0xf0, 0x12, 0x32, 0x84, 0x74, 0x6e, 0x25, 0x62, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, +0x12, 0x10, 0x4b, 0x7f, 0x02, 0x12, 0x12, 0x61, 0x78, 0x67, 0xe6, 0x44, 0x02, 0xf6, 0xd2, 0xb0, +0xd2, 0xb1, 0x90, 0xf9, 0x16, 0xe0, 0x30, 0xe7, 0x07, 0x90, 0xff, 0x9e, 0xe4, 0xf0, 0x80, 0x36, +0xd2, 0xb3, 0x90, 0xff, 0xa4, 0xe0, 0x90, 0xfa, 0x7e, 0xf0, 0x90, 0xff, 0xb4, 0xe0, 0x90, 0xfa, +0x7f, 0xf0, 0x90, 0xff, 0xa2, 0xe0, 0x90, 0xfa, 0x7c, 0xf0, 0x90, 0xff, 0xb2, 0xe0, 0x90, 0xfa, +0x7d, 0xf0, 0x90, 0xff, 0xa4, 0x74, 0x30, 0xf0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xff, 0xa2, 0x74, +0x40, 0xf0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xfa, 0xe7, 0xe5, 0xa8, 0xf0, 0x75, 0xa8, 0x81, 0x90, +0xff, 0x92, 0xe0, 0x60, 0x04, 0xe4, 0xf0, 0x80, 0xf6, 0x90, 0xff, 0xfd, 0x74, 0x3a, 0xf0, 0x43, +0x87, 0x01, 0x00, 0x00, 0x00, 0x90, 0xfa, 0x7e, 0xe0, 0x90, 0xff, 0xa4, 0xf0, 0x90, 0xfa, 0x7f, +0xe0, 0x90, 0xff, 0xb4, 0xf0, 0x90, 0xfa, 0x7c, 0xe0, 0x90, 0xff, 0xa2, 0xf0, 0x90, 0xfa, 0x7d, +0xe0, 0x90, 0xff, 0xb2, 0xf0, 0x90, 0xf9, 0x18, 0xe0, 0x60, 0x02, 0xc2, 0xb3, 0x90, 0xfa, 0xe7, +0xe0, 0xf5, 0xa8, 0x02, 0x10, 0xce, 0x8b, 0x5c, 0x8a, 0x5d, 0x89, 0x5e, 0x12, 0x2e, 0x0d, 0x90, +0xfa, 0xc3, 0x12, 0x1b, 0xf3, 0xaa, 0x5d, 0xa9, 0x5e, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0xf3, 0x90, +0xfa, 0xc7, 0xe4, 0x75, 0xf0, 0x0a, 0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xc6, 0x12, 0x1b, 0xea, 0xe9, +0x24, 0x01, 0xf9, 0xe4, 0x3a, 0xfa, 0x90, 0xfa, 0xc9, 0x12, 0x1b, 0xf3, 0xab, 0x5c, 0xaa, 0x5d, +0xa9, 0x5e, 0x12, 0x2e, 0x19, 0xe0, 0xff, 0xc3, 0x13, 0xf0, 0xe4, 0x78, 0x82, 0xf6, 0x90, 0xfa, +0xc1, 0xe0, 0xff, 0x78, 0x82, 0xe6, 0xc3, 0x9f, 0x50, 0x4a, 0x90, 0xfa, 0xc3, 0x12, 0x2d, 0xee, +0xff, 0x78, 0x83, 0xf6, 0x90, 0xfa, 0xc6, 0x12, 0x2d, 0xee, 0xfe, 0xf4, 0x5f, 0xff, 0x78, 0x83, +0xf6, 0x12, 0x2d, 0xeb, 0x5e, 0x4f, 0xff, 0x78, 0x83, 0xf6, 0x12, 0x2d, 0xf4, 0x75, 0xf0, 0x02, +0x12, 0x1b, 0x1c, 0x90, 0xfa, 0xc7, 0xe4, 0x75, 0xf0, 0x02, 0x12, 0x1b, 0x1c, 0xab, 0x5c, 0xaa, +0x5d, 0xa9, 0x5e, 0x90, 0x00, 0x04, 0x12, 0x1a, 0xbb, 0x30, 0xe4, 0x03, 0x12, 0x2e, 0x03, 0x78, +0x82, 0x06, 0x80, 0xaa, 0xe4, 0x90, 0xfa, 0xc2, 0xf0, 0x22, 0x8b, 0x56, 0x8a, 0x57, 0x89, 0x58, +0x90, 0xfa, 0xc2, 0x74, 0x06, 0xf0, 0xe4, 0x90, 0xfa, 0xc1, 0xf0, 0x12, 0x1a, 0xa2, 0x24, 0x6e, +0x60, 0x26, 0x14, 0x70, 0x70, 0x12, 0x2d, 0xda, 0x60, 0x09, 0x24, 0x30, 0x70, 0x12, 0x12, 0x25, +0x56, 0x80, 0x62, 0x12, 0x2e, 0x24, 0x12, 0x1f, 0xda, 0x90, 0xfa, 0xc2, 0xef, 0xf0, 0x80, 0x55, +0x90, 0xfa, 0xc2, 0x74, 0x81, 0xf0, 0x80, 0x4d, 0x12, 0x2d, 0xda, 0x60, 0x09, 0x24, 0x30, 0x70, +0x3e, 0x12, 0x2d, 0x30, 0x80, 0x3f, 0xe5, 0x58, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x57, 0xfa, 0x7b, +0x01, 0xc0, 0x03, 0xc0, 0x02, 0xc0, 0x01, 0x12, 0x2e, 0x24, 0x90, 0x00, 0x05, 0x12, 0x1a, 0xbb, +0xfd, 0x90, 0x00, 0x08, 0x12, 0x1b, 0x48, 0xf5, 0x2e, 0x85, 0xf0, 0x2d, 0xd0, 0x01, 0xd0, 0x02, +0xd0, 0x03, 0x12, 0x26, 0x98, 0x90, 0xfa, 0xc1, 0xef, 0xf0, 0xe4, 0xa3, 0xf0, 0x80, 0x06, 0x90, +0xfa, 0xc2, 0x74, 0x81, 0xf0, 0x90, 0xfa, 0xc2, 0xe0, 0x12, 0x2e, 0x24, 0x90, 0x00, 0x02, 0x12, +0x1a, 0xfa, 0x90, 0xfa, 0xc1, 0xe0, 0xff, 0x22, 0x8b, 0x29, 0x8a, 0x2a, 0x89, 0x2b, 0x8d, 0x2c, +0xe5, 0x2c, 0x70, 0x03, 0xaf, 0x2c, 0x22, 0x12, 0x2e, 0x53, 0x70, 0x16, 0x12, 0x2e, 0x72, 0xe5, +0x2d, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0xd8, 0x50, 0xf2, 0x12, 0x27, 0x25, 0x40, 0x0b, 0x7f, +0x00, 0x22, 0x12, 0x2e, 0x72, 0x12, 0x27, 0x25, 0x50, 0xf8, 0x90, 0xff, 0xf3, 0x74, 0xa1, 0xf0, +0xe5, 0x2c, 0xb4, 0x01, 0x07, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0xf1, 0xe4, +0xf0, 0xf5, 0x2f, 0xe5, 0x2c, 0x14, 0xff, 0xe5, 0x2f, 0xc3, 0x9f, 0x50, 0x2a, 0x12, 0x31, 0xc1, +0x40, 0x03, 0xaf, 0x2f, 0x22, 0xc3, 0xe5, 0x2c, 0x95, 0x2f, 0xff, 0xbf, 0x02, 0x07, 0x90, 0xff, +0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x12, 0x2e, 0x65, 0x05, 0x2f, 0x74, 0x01, 0x25, 0x2b, 0xf5, 0x2b, +0xe4, 0x35, 0x2a, 0xf5, 0x2a, 0x80, 0xcc, 0x12, 0x31, 0xc1, 0x40, 0x03, 0x7f, 0x18, 0x22, 0x12, +0x2e, 0x65, 0xaf, 0x2c, 0x22, 0x90, 0xff, 0xf1, 0xe5, 0x2e, 0xf0, 0x02, 0x31, 0xd8, 0x12, 0x10, +0x4b, 0x78, 0x84, 0x12, 0x23, 0x31, 0x30, 0xe1, 0x08, 0x7f, 0x13, 0x12, 0x31, 0xa9, 0x02, 0x27, +0xbc, 0x78, 0x84, 0xe6, 0xf9, 0x24, 0x13, 0x12, 0x22, 0xad, 0xe0, 0xff, 0x30, 0xe7, 0x40, 0x54, +0x03, 0x60, 0x1e, 0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x44, +0x04, 0xf0, 0x80, 0x46, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfd, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x80, +0x39, 0xe9, 0xb4, 0x03, 0x0d, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x01, 0xf0, +0x80, 0x28, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf7, 0xf0, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x1b, 0xef, +0x54, 0x03, 0x60, 0x14, 0xe9, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x54, 0xdf, 0xf0, 0x80, +0x07, 0x90, 0xff, 0xb4, 0xe0, 0x54, 0xdf, 0xf0, 0xc2, 0xb3, 0x90, 0xf9, 0x18, 0xe0, 0x04, 0xf0, +0xaf, 0x01, 0x12, 0x22, 0xee, 0xfd, 0x12, 0x2f, 0xe5, 0x12, 0x31, 0xa9, 0x02, 0x10, 0xce, 0x75, +0xa8, 0x40, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x8b, 0x02, 0x28, 0x09, 0x02, 0x31, +0x8c, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2, 0x08, 0xdf, +0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, +0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80, +0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x2b, 0xa9, 0xe4, 0x7e, 0x01, 0x93, +0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, +0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, +0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3, +0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe, 0xe4, 0xf5, +0x22, 0x12, 0x1d, 0xc2, 0xe0, 0xb4, 0x04, 0x0d, 0xe5, 0x22, 0x24, 0x03, 0xff, 0x12, 0x30, 0x13, +0x12, 0x1d, 0xc2, 0xe4, 0xf0, 0x05, 0x22, 0xe5, 0x22, 0xc3, 0x94, 0x02, 0x40, 0xe3, 0xe4, 0xf5, +0x22, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x94, 0x12, 0x1e, 0x03, 0x60, 0x2c, 0x12, 0x2d, +0x85, 0xef, 0x60, 0x52, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, 0xfa, 0x94, 0x12, 0x1b, 0xcc, 0xe4, +0xf0, 0xa3, 0xf0, 0x75, 0xf0, 0x0a, 0xe5, 0x22, 0x90, 0xfa, 0xa0, 0x12, 0x1b, 0xcc, 0xe0, 0xa3, +0x30, 0xe6, 0x33, 0x12, 0x1d, 0xc2, 0x74, 0x04, 0xf0, 0x22, 0x75, 0xf0, 0x02, 0xe5, 0x22, 0x90, +0xfa, 0x98, 0x12, 0x1e, 0x03, 0x60, 0x16, 0x12, 0x2d, 0x85, 0xef, 0x60, 0x19, 0x75, 0xf0, 0x02, +0xe5, 0x22, 0x90, 0xfa, 0x98, 0x12, 0x1b, 0xcc, 0xe4, 0xf0, 0xa3, 0xf0, 0x22, 0x05, 0x22, 0xe5, +0x22, 0xc3, 0x94, 0x02, 0x40, 0x9b, 0x22, 0xe4, 0xff, 0x90, 0xff, 0x83, 0xe0, 0x54, 0x0f, 0xfe, +0xef, 0xc3, 0x9e, 0x50, 0x17, 0x74, 0xf0, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, 0xe0, +0x12, 0x1c, 0xc1, 0x12, 0x1a, 0xe8, 0x0f, 0x12, 0x1c, 0xb0, 0x80, 0xdd, 0xef, 0xfd, 0xc3, 0xe5, +0x3a, 0x9d, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, 0x39, 0xd3, 0xe5, 0x3a, 0x94, 0x00, 0xe5, +0x39, 0x94, 0x00, 0x40, 0x06, 0xe4, 0x90, 0xff, 0x83, 0xf0, 0x22, 0x12, 0x1d, 0xdf, 0x12, 0x1e, +0x34, 0x12, 0x1e, 0x26, 0x12, 0x1a, 0xa2, 0x24, 0x6e, 0x60, 0x1e, 0x14, 0x60, 0x1b, 0x24, 0x8e, +0x70, 0x2d, 0x90, 0x00, 0x01, 0x12, 0x1a, 0xbb, 0xff, 0x24, 0xfc, 0x60, 0x03, 0x04, 0x70, 0x1f, +0xef, 0xfd, 0x7c, 0x00, 0x7f, 0x0d, 0x02, 0x11, 0x5e, 0x12, 0x1e, 0x3b, 0x12, 0x25, 0xfa, 0x12, +0x1d, 0x89, 0x12, 0x1a, 0xbb, 0x60, 0x03, 0x02, 0x32, 0x7a, 0xe4, 0xff, 0x12, 0x32, 0x6e, 0x22, +0x8b, 0x45, 0x8a, 0x46, 0x89, 0x47, 0x8c, 0x48, 0x8d, 0x49, 0xd2, 0x00, 0x12, 0x2e, 0x53, 0x70, +0x16, 0x12, 0x2e, 0x72, 0xe5, 0x48, 0x90, 0xff, 0xf1, 0xf0, 0x12, 0x31, 0xd8, 0x50, 0xf2, 0x12, +0x29, 0xd5, 0x40, 0x0b, 0x7f, 0x18, 0x22, 0x12, 0x2e, 0x72, 0x12, 0x29, 0xd5, 0x50, 0xf8, 0xe4, +0xf5, 0x4b, 0xe5, 0x4a, 0x14, 0xff, 0xe5, 0x4b, 0xc3, 0x9f, 0x50, 0x17, 0x12, 0x29, 0xc5, 0x40, +0x03, 0x7f, 0x18, 0x22, 0x05, 0x4b, 0x74, 0x01, 0x25, 0x47, 0xf5, 0x47, 0xe4, 0x35, 0x46, 0xf5, +0x46, 0x80, 0xdf, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x01, 0xf0, 0x12, 0x29, 0xc5, 0x40, 0x03, 0x7f, +0x18, 0x22, 0x7f, 0x00, 0x22, 0xab, 0x45, 0xaa, 0x46, 0xa9, 0x47, 0x12, 0x1a, 0xa2, 0x90, 0xff, +0xf1, 0xf0, 0x02, 0x31, 0xd8, 0x90, 0xff, 0xf1, 0xe5, 0x49, 0xf0, 0x02, 0x31, 0xd8, 0x7b, 0x01, +0x7a, 0xfa, 0x79, 0xcf, 0xe4, 0xfd, 0x12, 0x23, 0x61, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x09, +0x12, 0x1b, 0x1c, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, 0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, +0x12, 0x1b, 0x32, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x90, 0xff, 0xf7, +0xe5, 0x23, 0x12, 0x2a, 0x39, 0x90, 0xff, 0xf6, 0xe5, 0x23, 0xf0, 0x90, 0xfa, 0xcf, 0xe4, 0xf0, +0xa3, 0x74, 0x06, 0x12, 0x2a, 0x39, 0xe5, 0x23, 0x30, 0xe0, 0x07, 0x90, 0xff, 0xfc, 0x74, 0x94, +0xf0, 0x22, 0x90, 0xff, 0xfc, 0x74, 0x90, 0xf0, 0x22, 0xf0, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x23, +0x90, 0xfa, 0xcf, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x1b, 0x32, 0x85, 0xf0, 0x2e, 0xf5, 0x2d, 0x7d, +0x01, 0x02, 0x26, 0x98, 0x90, 0xff, 0x93, 0x74, 0x81, 0xf0, 0x90, 0xff, 0xff, 0xe0, 0x60, 0x06, +0x90, 0xff, 0xfc, 0x74, 0x10, 0xf0, 0x90, 0xff, 0x91, 0xe0, 0x44, 0x90, 0xf0, 0xe4, 0x90, 0xf9, +0x16, 0xf0, 0xa3, 0xf0, 0x12, 0x2b, 0x39, 0x12, 0x16, 0xc9, 0x12, 0x30, 0x69, 0x7e, 0x07, 0x7f, +0xd0, 0x12, 0x12, 0x2a, 0x7e, 0x0f, 0x7f, 0xa0, 0x12, 0x12, 0x44, 0xe4, 0x78, 0x77, 0xf6, 0x78, +0x77, 0xe6, 0xff, 0xc3, 0x94, 0x06, 0x50, 0x0b, 0x74, 0x6e, 0x2f, 0xf8, 0xe4, 0xf6, 0x78, 0x77, +0x06, 0x80, 0xec, 0x7f, 0x03, 0x12, 0x30, 0xb2, 0x90, 0xf9, 0x16, 0xe0, 0x20, 0xe4, 0x05, 0x7f, +0x04, 0x12, 0x30, 0xb2, 0x90, 0xff, 0x9b, 0xe4, 0xf0, 0x90, 0xff, 0x9a, 0xf0, 0x90, 0xff, 0xe8, +0xe0, 0x54, 0x1f, 0xf0, 0xd2, 0xa8, 0x22, 0x15, 0x65, 0xa8, 0x65, 0xa6, 0x07, 0x30, 0x08, 0x05, +0x12, 0x11, 0xae, 0x80, 0xf8, 0xd2, 0x08, 0xa8, 0x65, 0xe6, 0xff, 0xb4, 0x03, 0x0f, 0x78, 0x7c, +0x76, 0xff, 0x08, 0x76, 0xe0, 0x08, 0x76, 0xff, 0x08, 0x76, 0xa0, 0x80, 0x0d, 0x78, 0x7c, 0x76, +0xff, 0x08, 0x76, 0xe2, 0x08, 0x76, 0xff, 0x08, 0x76, 0xb0, 0x78, 0x80, 0x76, 0xfa, 0x08, 0x76, +0x9e, 0xef, 0x24, 0xfd, 0x75, 0xf0, 0x0a, 0xa4, 0xae, 0xf0, 0x12, 0x23, 0x49, 0x7b, 0x01, 0x7a, +0xff, 0x79, 0x48, 0x78, 0x68, 0x12, 0x1b, 0xe1, 0xa8, 0x65, 0xe6, 0x24, 0xfd, 0x75, 0xf0, 0x08, +0xa4, 0xff, 0xae, 0xf0, 0x78, 0x6a, 0x12, 0x23, 0x49, 0x79, 0x08, 0x78, 0x6b, 0x12, 0x1b, 0xe1, +0x78, 0x6d, 0xef, 0x12, 0x23, 0x49, 0x05, 0x65, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xab, 0xf0, +0xe0, 0x44, 0x20, 0xf0, 0x90, 0xfa, 0xe6, 0x74, 0x02, 0xf0, 0x7b, 0x01, 0x7a, 0xfa, 0x79, 0xcf, +0xe4, 0xf5, 0x2d, 0xf5, 0x2e, 0x7d, 0x01, 0x12, 0x26, 0x98, 0x7e, 0x00, 0x90, 0xfa, 0xe4, 0xee, +0xf0, 0xa3, 0xef, 0xf0, 0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcf, 0xe0, 0xb4, 0x52, 0x09, 0x90, +0xf9, 0x16, 0xe0, 0x54, 0xef, 0xf0, 0x80, 0x29, 0x90, 0xfa, 0xe4, 0xe0, 0x70, 0x04, 0xa3, 0xe0, +0x64, 0x01, 0x70, 0x10, 0x90, 0xfa, 0xcf, 0xe0, 0xb4, 0x10, 0x09, 0x90, 0xf9, 0x16, 0xe0, 0x44, +0x10, 0xf0, 0x80, 0x0d, 0x90, 0xfa, 0xe6, 0x74, 0x03, 0xf0, 0x90, 0xf9, 0x16, 0xe0, 0x54, 0xef, +0xf0, 0x90, 0xff, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x22, 0x03, 0x68, 0x01, 0xff, 0x48, 0x03, 0x6b, +0x01, 0xff, 0x08, 0x02, 0x66, 0x00, 0x00, 0x44, 0xfa, 0x98, 0x00, 0x00, 0x00, 0x00, 0x44, 0xfa, +0x94, 0x00, 0x00, 0x00, 0x00, 0x42, 0xfa, 0xb2, 0x00, 0x00, 0x42, 0xfa, 0x7e, 0x00, 0x00, 0x42, +0xfa, 0x7c, 0x00, 0x00, 0x42, 0xf9, 0x6d, 0xff, 0xff, 0x42, 0xfa, 0x7a, 0x00, 0x00, 0x41, 0xf9, +0x66, 0xff, 0x41, 0xf9, 0x1c, 0x19, 0x41, 0xf9, 0x15, 0x00, 0x43, 0xf9, 0x19, 0x0a, 0x32, 0x02, +0x41, 0xf9, 0x68, 0x20, 0x41, 0xf9, 0x69, 0x20, 0x41, 0xf9, 0x65, 0x00, 0x41, 0xf9, 0x67, 0x00, +0x44, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0xf9, 0x16, 0x00, 0x00, 0x41, 0xf9, 0x18, 0x00, +0x01, 0x20, 0x00, 0x41, 0xf8, 0x04, 0x00, 0x00, 0x12, 0x10, 0x4b, 0x78, 0x8a, 0xef, 0xf6, 0x12, +0x2a, 0xc7, 0x12, 0x22, 0xee, 0x30, 0xe0, 0x29, 0x78, 0x7c, 0x12, 0x22, 0xb7, 0xe0, 0x54, 0x7f, +0xf0, 0x78, 0x6b, 0x12, 0x1b, 0xd8, 0x90, 0x00, 0x02, 0x12, 0x1a, 0xbb, 0x30, 0xe7, 0x09, 0x90, +0x00, 0x02, 0xe4, 0x12, 0x1a, 0xfa, 0x80, 0xe9, 0x78, 0x7c, 0x12, 0x22, 0xb7, 0xe0, 0x44, 0x80, +0xf0, 0x12, 0x22, 0xee, 0x30, 0xe1, 0x1e, 0x12, 0x22, 0x97, 0xe0, 0x54, 0x7f, 0xf0, 0x12, 0x32, +0x19, 0x78, 0x68, 0x12, 0x1b, 0xd8, 0x90, 0x00, 0x02, 0x74, 0x80, 0x12, 0x1a, 0xfa, 0x12, 0x22, +0x97, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x32, 0x84, 0xe4, 0xff, 0x12, 0x31, 0xa9, 0x02, 0x10, 0xce, +0x12, 0x10, 0x4b, 0x78, 0x85, 0xef, 0xf6, 0x12, 0x31, 0x50, 0x12, 0x31, 0xa9, 0x78, 0x85, 0xe6, +0xff, 0x24, 0x13, 0x12, 0x22, 0xad, 0xe0, 0xfe, 0x30, 0xe7, 0x16, 0xef, 0xb4, 0x03, 0x09, 0x90, +0xff, 0x9e, 0xe0, 0x54, 0xfa, 0xf0, 0x80, 0x22, 0x90, 0xff, 0x9e, 0xe0, 0x54, 0xf5, 0xf0, 0x80, +0x19, 0xee, 0x54, 0x03, 0x60, 0x14, 0xef, 0xb4, 0x03, 0x09, 0x90, 0xff, 0xa4, 0xe0, 0x44, 0x20, +0xf0, 0x80, 0x07, 0x90, 0xff, 0xb4, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0xf9, 0x18, 0xe0, 0x14, 0xf0, +0xe0, 0x70, 0x02, 0xd2, 0xb3, 0x02, 0x10, 0xce, 0x12, 0x1e, 0x1c, 0xe5, 0x3a, 0x64, 0x09, 0x70, +0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, 0x48, 0xc3, 0xe5, 0x3a, 0x94, 0x08, 0xe5, 0x39, 0x94, 0x00, +0x40, 0x11, 0x7f, 0x08, 0xef, 0xe5, 0x3a, 0x94, 0x08, 0xf5, 0x3a, 0xe5, 0x39, 0x94, 0x00, 0xf5, +0x39, 0x80, 0x05, 0xaf, 0x3a, 0x12, 0x1e, 0x34, 0xe4, 0xfe, 0xee, 0xc3, 0x9f, 0x50, 0x19, 0x12, +0x1c, 0xc1, 0x12, 0x1a, 0xa2, 0xfd, 0x74, 0xf8, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfe, 0xf5, 0x83, +0xed, 0xf0, 0x0e, 0x12, 0x1c, 0xb0, 0x80, 0xe2, 0xef, 0x54, 0x7f, 0x90, 0xff, 0x81, 0xf0, 0x22, +0x8b, 0x59, 0x8a, 0x5a, 0x89, 0x5b, 0x12, 0x2e, 0x19, 0x70, 0x05, 0xa3, 0x74, 0x08, 0xf0, 0x22, +0xab, 0x59, 0xaa, 0x5a, 0xa9, 0x5b, 0x12, 0x2e, 0x0d, 0x90, 0xfa, 0xc9, 0x12, 0x1b, 0xf3, 0xe5, +0x5b, 0x24, 0x03, 0xf9, 0xe4, 0x35, 0x5a, 0xfa, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0xf3, 0xe4, 0x90, +0xfa, 0xc2, 0xf0, 0x78, 0x8b, 0xf6, 0x90, 0xfa, 0xc1, 0xe0, 0xff, 0x78, 0x8b, 0xe6, 0xc3, 0x9f, +0x50, 0x12, 0x12, 0x2d, 0xeb, 0xff, 0x12, 0x2d, 0xf4, 0x12, 0x2e, 0x07, 0x78, 0x8b, 0x06, 0x12, +0x2e, 0x03, 0x80, 0xe2, 0x22, 0xad, 0x07, 0xac, 0x06, 0x90, 0x32, 0x0a, 0xe4, 0x93, 0xff, 0x78, +0x74, 0xf6, 0x54, 0x0f, 0x12, 0x1d, 0xa8, 0xe0, 0x08, 0x76, 0x00, 0x08, 0xf6, 0x18, 0x12, 0x1c, +0xd9, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0x78, 0x75, 0xee, 0xf6, 0x08, 0xef, 0xf6, +0xee, 0x44, 0xf8, 0x18, 0xf6, 0xef, 0x08, 0xf6, 0x90, 0xff, 0x7a, 0xe0, 0x20, 0xe7, 0x03, 0x7f, +0x00, 0x22, 0x78, 0x75, 0xe6, 0xfe, 0x08, 0xe6, 0xf5, 0x82, 0x8e, 0x83, 0xec, 0xf0, 0xa3, 0xed, +0xf0, 0x90, 0xff, 0x7a, 0x74, 0x02, 0xf0, 0x7f, 0x01, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, +0x90, 0x00, 0x03, 0x12, 0x1a, 0xbb, 0x54, 0xf0, 0x24, 0xa0, 0x22, 0x90, 0xfa, 0xc9, 0x12, 0x1b, +0xea, 0x02, 0x1a, 0xa2, 0x90, 0xfa, 0xc3, 0x12, 0x1b, 0xea, 0xef, 0x12, 0x1a, 0xe8, 0x90, 0xfa, +0xca, 0xe4, 0x22, 0x90, 0xfa, 0xc4, 0xe4, 0x75, 0xf0, 0x01, 0x02, 0x1b, 0x1c, 0x90, 0x00, 0x08, +0x12, 0x1b, 0x48, 0xaa, 0xf0, 0xf9, 0x7b, 0x01, 0x22, 0x90, 0x00, 0x05, 0x12, 0x1a, 0xbb, 0x90, +0xfa, 0xc1, 0xf0, 0x22, 0xab, 0x56, 0xaa, 0x57, 0xa9, 0x58, 0x22, 0x90, 0xfa, 0xdd, 0xe0, 0xff, +0x7e, 0x00, 0xc3, 0x90, 0xfa, 0xd7, 0xe0, 0x9f, 0xf0, 0x90, 0xfa, 0xd6, 0xe0, 0x9e, 0xf0, 0x90, +0xfa, 0xd8, 0xee, 0x8f, 0xf0, 0x12, 0x1b, 0x1c, 0xef, 0x25, 0x51, 0xf5, 0x51, 0xee, 0x35, 0x50, +0xf5, 0x50, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0x54, 0xfe, 0xf0, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0xfa, +0xe6, 0xe0, 0x64, 0x03, 0x22, 0x90, 0xff, 0xf2, 0xe0, 0xab, 0x29, 0xaa, 0x2a, 0xa9, 0x2b, 0x02, +0x1a, 0xe8, 0x90, 0xff, 0xf3, 0x74, 0xa0, 0xf0, 0x22, 0x8f, 0x64, 0xed, 0x70, 0x0f, 0xe5, 0x64, +0xb4, 0x03, 0x05, 0x7f, 0x01, 0x02, 0x31, 0xef, 0x7f, 0x02, 0x02, 0x31, 0xef, 0xaf, 0x64, 0x12, +0x2a, 0xc7, 0x74, 0x6e, 0x25, 0x64, 0xf8, 0xe6, 0x30, 0xe2, 0x0b, 0xd2, 0x09, 0x12, 0x1d, 0x33, +0xe0, 0x54, 0x7f, 0xf0, 0x80, 0x02, 0xc2, 0x09, 0xe5, 0x64, 0xb4, 0x03, 0x07, 0x7f, 0x81, 0x12, +0x31, 0xef, 0x80, 0x05, 0x7f, 0x82, 0x12, 0x31, 0xef, 0x30, 0x09, 0x07, 0x12, 0x1d, 0x33, 0xe0, +0x44, 0x80, 0xf0, 0x12, 0x32, 0x84, 0x22, 0x12, 0x10, 0x4b, 0x90, 0xff, 0xfd, 0xe0, 0x44, 0x60, +0xf0, 0xd2, 0x01, 0x90, 0xff, 0xfc, 0xe0, 0x44, 0x02, 0xf0, 0x90, 0xff, 0x00, 0xe0, 0x30, 0xe7, +0x13, 0x90, 0xff, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0x43, 0x35, 0x80, 0x90, 0xff, 0xfc, 0xe0, 0x44, +0x01, 0xf0, 0x80, 0x0d, 0x12, 0x1d, 0xdf, 0x53, 0x35, 0x7f, 0x90, 0xff, 0xfc, 0xe0, 0x54, 0xfe, +0xf0, 0x90, 0xff, 0x81, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x02, 0xde, 0x12, 0x1d, 0xe7, 0x02, 0x10, +0xce, 0x12, 0x10, 0x4b, 0x78, 0x89, 0xef, 0xf6, 0xd2, 0x00, 0x12, 0x2a, 0xc7, 0x90, 0xf9, 0x6a, +0x12, 0x1b, 0xea, 0xe9, 0x24, 0x03, 0xf9, 0xe4, 0x3a, 0xfa, 0xc0, 0x02, 0x78, 0x80, 0xe6, 0xfe, +0x08, 0xe6, 0xaa, 0x06, 0xf8, 0xac, 0x02, 0x7d, 0x01, 0xd0, 0x02, 0x12, 0x22, 0xd3, 0x12, 0x32, +0x84, 0x78, 0x89, 0xe6, 0xff, 0x12, 0x13, 0x87, 0x12, 0x31, 0xa9, 0x02, 0x10, 0xce, 0x8f, 0x63, +0x12, 0x2a, 0xc7, 0x78, 0x7c, 0x12, 0x22, 0xb7, 0xe0, 0x54, 0x3f, 0xf0, 0xe5, 0x82, 0x24, 0x04, +0x12, 0x22, 0xa1, 0xe0, 0x54, 0x3f, 0xf0, 0x12, 0x23, 0x41, 0x24, 0x0b, 0x12, 0x22, 0xa1, 0xe0, +0x54, 0xf8, 0xf0, 0x12, 0x32, 0x84, 0x74, 0x6e, 0x25, 0x63, 0xf8, 0x74, 0xfb, 0x56, 0xf6, 0x7f, +0x00, 0x22, 0x12, 0x10, 0x4b, 0x12, 0x2a, 0xc7, 0x12, 0x22, 0xfa, 0x24, 0x06, 0x12, 0x22, 0x9f, +0xe0, 0xfd, 0x12, 0x22, 0xe8, 0x90, 0x00, 0x03, 0x12, 0x23, 0x02, 0x24, 0x05, 0x12, 0x22, 0xa1, +0xe0, 0x90, 0x00, 0x04, 0x12, 0x1a, 0xfa, 0x12, 0x32, 0x84, 0x7d, 0x02, 0xe4, 0xff, 0x12, 0x2f, +0xb4, 0x02, 0x10, 0xce, 0xae, 0x05, 0x12, 0x1d, 0x8e, 0xef, 0x12, 0x1a, 0xfa, 0x0e, 0x0e, 0x0e, +0xee, 0xd3, 0x95, 0x3c, 0xe4, 0x95, 0x3b, 0x40, 0x02, 0xae, 0x3c, 0xee, 0xd3, 0x94, 0x08, 0x74, +0x80, 0x94, 0x81, 0x40, 0x0a, 0x7e, 0x03, 0x90, 0x00, 0x02, 0x74, 0x02, 0x12, 0x1a, 0xfa, 0xaf, +0x06, 0x12, 0x32, 0x6e, 0x22, 0xae, 0x07, 0xed, 0x54, 0x03, 0x64, 0x01, 0x60, 0x03, 0x7f, 0x10, +0x22, 0xed, 0x54, 0x7c, 0xc3, 0x94, 0x04, 0x50, 0x03, 0x7f, 0x0b, 0x22, 0x74, 0x6e, 0x2e, 0xf8, +0x74, 0x02, 0x46, 0xf6, 0x74, 0x99, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0xfa, 0xf5, 0x83, 0xed, 0xf0, +0x7f, 0x00, 0x22, 0xbf, 0x03, 0x06, 0x7c, 0xff, 0x7d, 0xe0, 0x80, 0x04, 0x7c, 0xff, 0x7d, 0xe2, +0x8d, 0x82, 0x8c, 0x83, 0xe0, 0x44, 0x80, 0xf0, 0xe5, 0x82, 0x24, 0x04, 0x12, 0x22, 0xa1, 0xe0, +0x44, 0x80, 0xf0, 0x74, 0x6e, 0x2f, 0xf8, 0x74, 0x04, 0x46, 0xf6, 0x7f, 0x00, 0x22, 0x12, 0x10, +0x4b, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, 0x16, 0x90, 0xff, 0x83, +0xe0, 0x54, 0x0f, 0xff, 0xc3, 0xe5, 0x3a, 0x9f, 0xe5, 0x39, 0x94, 0x00, 0x40, 0x05, 0x12, 0x28, +0xd7, 0x80, 0x03, 0x12, 0x32, 0x7a, 0x02, 0x10, 0xce, 0x90, 0xff, 0xfc, 0xe0, 0x20, 0xe7, 0x1f, +0xc2, 0xaf, 0x7d, 0xff, 0xac, 0x05, 0x1d, 0xec, 0x60, 0x15, 0x7e, 0x04, 0x7f, 0x00, 0xef, 0x1f, +0xaa, 0x06, 0x70, 0x01, 0x1e, 0x4a, 0x60, 0xec, 0x90, 0xff, 0x92, 0xe4, 0xf0, 0x80, 0xef, 0x22, +0x12, 0x10, 0x4b, 0x78, 0x66, 0xe6, 0xfe, 0x08, 0xe6, 0xff, 0x30, 0xe0, 0x12, 0x30, 0xe1, 0x0f, +0x90, 0xff, 0xfc, 0xe0, 0x44, 0x20, 0xf0, 0x7f, 0x04, 0x12, 0x12, 0x61, 0x12, 0x1d, 0xf6, 0x02, +0x10, 0xce, 0x8f, 0x23, 0xc2, 0x08, 0x12, 0x2a, 0xc7, 0x12, 0x22, 0xc0, 0x78, 0x7e, 0x12, 0x23, +0x42, 0x24, 0x0b, 0x12, 0x22, 0xa1, 0xe0, 0x54, 0xf8, 0xf0, 0x12, 0x32, 0x84, 0xaf, 0x23, 0x12, +0x13, 0x87, 0x22, 0x8e, 0x5f, 0x8f, 0x60, 0xe5, 0x60, 0x15, 0x60, 0xae, 0x5f, 0x70, 0x02, 0x15, +0x5f, 0xd3, 0x94, 0x00, 0xee, 0x94, 0x00, 0x40, 0x09, 0x7e, 0x07, 0x7f, 0xd0, 0x12, 0x10, 0x24, +0x80, 0xe5, 0x22, 0x11, 0xdc, 0x2e, 0xc7, 0x24, 0xb0, 0x32, 0x60, 0x30, 0x90, 0x30, 0x3e, 0x31, +0x6f, 0x2f, 0x82, 0x27, 0x2e, 0x2c, 0x80, 0x31, 0x12, 0x31, 0x31, 0x1e, 0x64, 0x2f, 0x11, 0x2c, +0x18, 0x0e, 0x12, 0x10, 0x4b, 0x78, 0x86, 0x12, 0x23, 0x31, 0x20, 0xe1, 0x07, 0x7f, 0x12, 0x12, +0x31, 0xa9, 0x80, 0x0a, 0x78, 0x86, 0xe6, 0xff, 0x12, 0x24, 0x0a, 0x12, 0x31, 0xa9, 0x02, 0x10, +0xce, 0x12, 0x10, 0x4b, 0x78, 0x87, 0x12, 0x23, 0x31, 0x20, 0xe2, 0x07, 0x7f, 0x11, 0x12, 0x31, +0xa9, 0x80, 0x0a, 0x78, 0x87, 0xe6, 0xff, 0x12, 0x2f, 0x4e, 0x12, 0x31, 0xa9, 0x02, 0x10, 0xce, +0x8f, 0x61, 0x12, 0x2f, 0x4e, 0xaf, 0x61, 0x12, 0x2a, 0xc7, 0x12, 0x22, 0xc0, 0x12, 0x32, 0x84, +0x74, 0x6e, 0x25, 0x61, 0xf8, 0x74, 0xfd, 0x56, 0xf6, 0xaf, 0x61, 0x12, 0x13, 0x87, 0x22, 0x12, +0x10, 0x4b, 0xe5, 0x3a, 0x64, 0x09, 0x70, 0x04, 0xe5, 0x39, 0x64, 0x01, 0x60, 0x05, 0x12, 0x2c, +0xd8, 0x80, 0x06, 0x12, 0x1e, 0x14, 0x12, 0x1e, 0x1c, 0x02, 0x10, 0xce, 0x12, 0x2a, 0x54, 0x12, +0x13, 0x03, 0x90, 0xf8, 0x04, 0xe0, 0xff, 0x60, 0x05, 0x7d, 0x01, 0x12, 0x12, 0xa0, 0x12, 0x29, +0xde, 0x12, 0x13, 0x3f, 0x12, 0x11, 0xbc, 0x80, 0xe3, 0x12, 0x1d, 0x8e, 0xef, 0x12, 0x1a, 0xfa, +0xe4, 0xf5, 0x33, 0xf5, 0x34, 0xef, 0x60, 0x03, 0x02, 0x32, 0x7a, 0xe4, 0xff, 0x12, 0x32, 0x6e, +0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0xa0, 0x60, 0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, +0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0x90, 0xff, 0xf0, 0xe0, 0xff, 0x54, 0x28, 0x60, +0xf7, 0xef, 0x30, 0xe5, 0x08, 0x90, 0xff, 0xf0, 0x44, 0x20, 0xf0, 0xc3, 0x22, 0xd3, 0x22, 0xef, +0x30, 0xe7, 0x08, 0x12, 0x1d, 0x45, 0xe0, 0x54, 0xdf, 0xf0, 0x22, 0xef, 0x12, 0x1d, 0x98, 0xe0, +0x54, 0xdf, 0xf0, 0x22, 0x81, 0x01, 0x82, 0x02, 0x83, 0x03, 0x87, 0x40, 0x00, 0x40, 0x00, 0x40, +0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x08, 0x00, 0x78, 0x7e, 0x12, 0x22, 0xb7, 0xa3, 0xa3, +0xe0, 0xff, 0x30, 0xe7, 0x06, 0x54, 0x7f, 0xf0, 0x44, 0x80, 0xf0, 0x22, 0x85, 0x3b, 0x39, 0x85, +0x3c, 0x3a, 0x90, 0xff, 0x82, 0xe0, 0x54, 0xf7, 0xf0, 0xa3, 0xe0, 0x54, 0x7f, 0xf0, 0x22, 0xe4, +0xfe, 0xee, 0x90, 0x32, 0x04, 0x93, 0xb5, 0x07, 0x02, 0xd3, 0x22, 0x0e, 0xbe, 0x07, 0xf2, 0xc3, +0x22, 0x00, 0x08, 0x18, 0x28, 0x38, 0x01, 0x81, 0x90, 0x0a, 0x02, 0x00, 0x00, 0x11, 0x13, 0x00, +0x12, 0x10, 0x4b, 0x7f, 0x02, 0x12, 0x10, 0xda, 0x12, 0x1d, 0xf6, 0x02, 0x10, 0xce, 0x75, 0x39, +0x00, 0x8f, 0x3a, 0x12, 0x1c, 0xe0, 0x12, 0x2c, 0xd8, 0x22, 0x12, 0x1e, 0x1c, 0x12, 0x1d, 0xdf, +0x12, 0x1e, 0x14, 0x22, 0xc2, 0x08, 0x22, }; #undef IMAGE_VERSION_NAME diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 544098d..0d39036 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -48,7 +48,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.7" +#define DRIVER_VERSION "v0.7mode043006" #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com> and David Iacovelli" #define DRIVER_DESC "Edgeport USB Serial Driver" @@ -173,8 +173,12 @@ static struct usb_device_id edgeport_2port_id_table [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_221C) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) }, -// The 4-port shows up as two 2-port devices + /* The 4, 8 and 16 port devices show up as multiple 2 port devices */ { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8S) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416B) }, { } }; @@ -209,6 +213,10 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_8S) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_416B) }, { } }; @@ -231,6 +239,7 @@ static int TIStayInBootMode = 0; static int low_latency = EDGE_LOW_LATENCY; static int closing_wait = EDGE_CLOSING_WAIT; static int ignore_cpu_rev = 0; +static int default_uart_mode = 0; /* RS232 */ static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length); @@ -241,6 +250,10 @@ static int restart_read(struct edgeport_port *edge_port); static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); static void edge_send(struct usb_serial_port *port); +/* sysfs attributes */ +static int edge_create_sysfs_attrs(struct usb_serial_port *port); +static int edge_remove_sysfs_attrs(struct usb_serial_port *port); + /* circular buffer */ static struct edge_buf *edge_buf_alloc(unsigned int size); static void edge_buf_free(struct edge_buf *eb); @@ -1706,13 +1719,14 @@ static void edge_interrupt_callback (struct urb *urb) int length = urb->actual_length; int port_number; int function; - int status; + int retval; __u8 lsr; __u8 msr; + int status = urb->status; dbg("%s", __FUNCTION__); - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -1720,10 +1734,12 @@ static void edge_interrupt_callback (struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, status); return; default: - dev_err(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __FUNCTION__, urb->status); + dev_err(&urb->dev->dev, "%s - nonzero urb status received: " + "%d\n", __FUNCTION__, status); goto exit; } @@ -1781,10 +1797,10 @@ static void edge_interrupt_callback (struct urb *urb) } exit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n", - __FUNCTION__, status); + __FUNCTION__, retval); } static void edge_bulk_in_callback (struct urb *urb) @@ -1792,12 +1808,13 @@ static void edge_bulk_in_callback (struct urb *urb) struct edgeport_port *edge_port = (struct edgeport_port *)urb->context; unsigned char *data = urb->transfer_buffer; struct tty_struct *tty; - int status = 0; + int retval = 0; int port_number; + int status = urb->status; dbg("%s", __FUNCTION__); - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -1805,17 +1822,18 @@ static void edge_bulk_in_callback (struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, status); return; default: dev_err (&urb->dev->dev,"%s - nonzero read bulk status received: %d\n", - __FUNCTION__, urb->status ); + __FUNCTION__, status); } - if (urb->status == -EPIPE) + if (status == -EPIPE) goto exit; - if (urb->status) { + if (status) { dev_err(&urb->dev->dev,"%s - stopping read!\n", __FUNCTION__); return; } @@ -1849,14 +1867,14 @@ exit: spin_lock(&edge_port->ep_lock); if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING) { urb->dev = edge_port->port->serial->dev; - status = usb_submit_urb(urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); } else if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPING) { edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPED; } spin_unlock(&edge_port->ep_lock); - if (status) + if (retval) dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n", - __FUNCTION__, status); + __FUNCTION__, retval); } static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length) @@ -1883,12 +1901,13 @@ static void edge_bulk_out_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct edgeport_port *edge_port = usb_get_serial_port_data(port); + int status = urb->status; dbg ("%s - port %d", __FUNCTION__, port->number); edge_port->ep_write_urb_in_use = 0; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -1896,11 +1915,12 @@ static void edge_bulk_out_callback (struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, status); return; default: - dev_err (&urb->dev->dev,"%s - nonzero write bulk status received: %d\n", - __FUNCTION__, urb->status); + dev_err(&urb->dev->dev, "%s - nonzero write bulk status " + "received: %d\n", __FUNCTION__, status); } /* send any buffered data */ @@ -2351,7 +2371,7 @@ static int restart_read(struct edgeport_port *edge_port) urb->complete = edge_bulk_in_callback; urb->context = edge_port; urb->dev = edge_port->port->serial->dev; - status = usb_submit_urb(urb, GFP_KERNEL); + status = usb_submit_urb(urb, GFP_ATOMIC); } edge_port->ep_read_urb_state = EDGE_READ_URB_RUNNING; edge_port->shadow_mcr |= MCR_RTS; @@ -2524,14 +2544,6 @@ static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old } cflag = tty->termios->c_cflag; - /* check that they really want us to change something */ - if (old_termios) { - if (cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) { - dbg ("%s - nothing to change", __FUNCTION__); - return; - } - } dbg("%s - clfag %08x iflag %08x", __FUNCTION__, tty->termios->c_cflag, tty->termios->c_iflag); @@ -2758,7 +2770,7 @@ static int edge_startup (struct usb_serial *serial) edge_port->port = serial->port[i]; edge_port->edge_serial = edge_serial; usb_set_serial_port_data(serial->port[i], edge_port); - edge_port->bUartMode = 0; /* Default is RS232 */ + edge_port->bUartMode = default_uart_mode; } return 0; @@ -2784,6 +2796,7 @@ static void edge_shutdown (struct usb_serial *serial) for (i=0; i < serial->num_ports; ++i) { edge_port = usb_get_serial_port_data(serial->port[i]); + edge_remove_sysfs_attrs(edge_port->port); if (edge_port) { edge_buf_free(edge_port->ep_out_buf); kfree(edge_port); @@ -2795,6 +2808,48 @@ static void edge_shutdown (struct usb_serial *serial) } +/* Sysfs Attributes */ + +static ssize_t show_uart_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_serial_port *port = to_usb_serial_port(dev); + struct edgeport_port *edge_port = usb_get_serial_port_data(port); + + return sprintf(buf, "%d\n", edge_port->bUartMode); +} + +static ssize_t store_uart_mode(struct device *dev, + struct device_attribute *attr, const char *valbuf, size_t count) +{ + struct usb_serial_port *port = to_usb_serial_port(dev); + struct edgeport_port *edge_port = usb_get_serial_port_data(port); + unsigned int v = simple_strtoul(valbuf, NULL, 0); + + dbg("%s: setting uart_mode = %d", __FUNCTION__, v); + + if (v < 256) + edge_port->bUartMode = v; + else + dev_err(dev, "%s - uart_mode %d is invalid\n", __FUNCTION__, v); + + return count; +} + +static DEVICE_ATTR(uart_mode, S_IWUSR | S_IRUGO, show_uart_mode, store_uart_mode); + +static int edge_create_sysfs_attrs(struct usb_serial_port *port) +{ + return device_create_file(&port->dev, &dev_attr_uart_mode); +} + +static int edge_remove_sysfs_attrs(struct usb_serial_port *port) +{ + device_remove_file(&port->dev, &dev_attr_uart_mode); + return 0; +} + + /* Circular Buffer */ /* @@ -2991,6 +3046,7 @@ static struct usb_serial_driver edgeport_1port_device = { .unthrottle = edge_unthrottle, .attach = edge_startup, .shutdown = edge_shutdown, + .port_probe = edge_create_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -3022,6 +3078,7 @@ static struct usb_serial_driver edgeport_2port_device = { .unthrottle = edge_unthrottle, .attach = edge_startup, .shutdown = edge_shutdown, + .port_probe = edge_create_sysfs_attrs, .ioctl = edge_ioctl, .set_termios = edge_set_termios, .tiocmget = edge_tiocmget, @@ -3085,3 +3142,6 @@ MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain, in .01 secs"); module_param(ignore_cpu_rev, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ignore_cpu_rev, "Ignore the cpu revision when connecting to a device"); +module_param(default_uart_mode, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(default_uart_mode, "Default uart_mode, 0=RS232, ..."); + diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index e57fa11..8e1a491 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -131,7 +131,7 @@ #define ION_DEVICE_ID_TI_EDGEPORT_2I 0x0207 // Edgeport/2i RS422/RS485 #define ION_DEVICE_ID_TI_EDGEPORT_421 0x020C // Edgeport/421 4 hub 2 RS232 + Parallel (lucent on a different hub port) #define ION_DEVICE_ID_TI_EDGEPORT_21 0x020D // Edgeport/21 2 RS232 + Parallel (lucent on a different hub port) -#define ION_DEVICE_ID_TI_EDGEPORT_8 0x020F // Edgeport/8 (single-CPU) +#define ION_DEVICE_ID_TI_EDGEPORT_416 0x0212 // Edgeport/416 #define ION_DEVICE_ID_TI_EDGEPORT_1 0x0215 // Edgeport/1 RS232 #define ION_DEVICE_ID_TI_EDGEPORT_42 0x0217 // Edgeport/42 4 hub 2 RS232 #define ION_DEVICE_ID_TI_EDGEPORT_22I 0x021A // Edgeport/22I is an Edgeport/4 with ports 1&2 RS422 and ports 3&4 RS232 @@ -143,12 +143,14 @@ #define ION_DEVICE_ID_TI_EDGEPORT_21C 0x021E // Edgeport/21c is a TI based Edgeport/2 with lucent chip // Generation 3 devices -- 3410 based edgport/1 (256 byte I2C) -#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1 0x240 // Edgeport/1 RS232 -#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I 0x241 // Edgeport/1i- RS422 model +#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1 0x0240 // Edgeport/1 RS232 +#define ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I 0x0241 // Edgeport/1i- RS422 model // Ti based software switchable RS232/RS422/RS485 devices -#define ION_DEVICE_ID_TI_EDGEPORT_4S 0x242 // Edgeport/4s - software switchable model -#define ION_DEVICE_ID_IT_EDGEPORT_8S 0x243 // Edgeport/8s - software switchable model +#define ION_DEVICE_ID_TI_EDGEPORT_4S 0x0242 // Edgeport/4s - software switchable model +#define ION_DEVICE_ID_TI_EDGEPORT_8S 0x0243 // Edgeport/8s - software switchable model +#define ION_DEVICE_ID_TI_EDGEPORT_8 0x0244 // Edgeport/8 (single-CPU) +#define ION_DEVICE_ID_TI_EDGEPORT_416B 0x0247 // Edgeport/416 /************************************************************************ diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index 4df0ec7..0455c15 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -732,11 +732,13 @@ static void ipaq_read_bulk_callback(struct urb *urb) struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); return; } @@ -870,11 +872,13 @@ static void ipaq_write_bulk_callback(struct urb *urb) struct ipaq_private *priv = usb_get_serial_port_data(port); unsigned long flags; int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->status) { - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + + if (status) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); return; } diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 1bc5860..1b94daa 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -167,11 +167,13 @@ static void ipw_read_bulk_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; struct tty_struct *tty; int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); return; } @@ -369,13 +371,15 @@ static void ipw_close(struct usb_serial_port *port, struct file * filp) static void ipw_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; + int status = urb->status; dbg("%s", __FUNCTION__); port->write_urb_busy = 0; - if (urb->status) - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + if (status) + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); usb_serial_port_softint(port); } diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 9d847f6..5ab6a0c 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -21,6 +21,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * 2007_Jun_21 Alan Cox <alan@redhat.com> + * Minimal cleanups for some of the driver problens and tty layer abuse. + * Still needs fixing to allow multiple dongles. + * * 2002_Mar_07 greg kh * moved some needed structures and #define values from the * net/irda/irda-usb.h file into our file, as we don't want to depend on @@ -109,6 +113,7 @@ static void ir_write_bulk_callback (struct urb *urb); static void ir_read_bulk_callback (struct urb *urb); static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); +/* Not that this lot means you can only have one per system */ static u8 ir_baud = 0; static u8 ir_xbof = 0; static u8 ir_add_bof = 0; @@ -392,12 +397,14 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int static void ir_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); port->write_urb_busy = 0; - if (urb->status) { - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); return; } @@ -417,6 +424,7 @@ static void ir_read_bulk_callback (struct urb *urb) struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); @@ -425,8 +433,7 @@ static void ir_read_bulk_callback (struct urb *urb) return; } - switch (urb->status) { - + switch (status) { case 0: /* Successful */ /* @@ -444,22 +451,12 @@ static void ir_read_bulk_callback (struct urb *urb) urb->actual_length, data); - /* - * Bypass flip-buffers, and feed the ldisc directly - * due to our potentially large buffer size. Since we - * used to set low_latency, this is exactly what the - * tty layer did anyway :) - */ tty = port->tty; - /* - * FIXME: must not do this in IRQ context - */ - tty->ldisc.receive_buf( - tty, - data+1, - NULL, - urb->actual_length-1); + if (tty_buffer_request_room(tty, urb->actual_length - 1)) { + tty_insert_flip_string(tty, data+1, urb->actual_length - 1); + tty_flip_buffer_push(tty); + } /* * No break here. @@ -490,7 +487,7 @@ static void ir_read_bulk_callback (struct urb *urb) default: dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, - urb->status); + status); break ; } @@ -501,8 +498,9 @@ static void ir_read_bulk_callback (struct urb *urb) static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) { unsigned char *transfer_buffer; - unsigned int cflag; int result; + speed_t baud; + int ir_baud; dbg("%s - port %d", __FUNCTION__, port->number); @@ -511,77 +509,59 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t return; } - cflag = port->tty->termios->c_cflag; - /* check that they really want us to change something */ - if (old_termios) { - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("%s - nothing to change...", __FUNCTION__); - return; - } + baud = tty_get_baud_rate(port->tty); + + /* + * FIXME, we should compare the baud request against the + * capability stated in the IR header that we got in the + * startup function. + */ + + switch (baud) { + case 2400: ir_baud = SPEED_2400; break; + case 9600: ir_baud = SPEED_9600; break; + case 19200: ir_baud = SPEED_19200; break; + case 38400: ir_baud = SPEED_38400; break; + case 57600: ir_baud = SPEED_57600; break; + case 115200: ir_baud = SPEED_115200; break; + case 576000: ir_baud = SPEED_576000; break; + case 1152000: ir_baud = SPEED_1152000; break; + case 4000000: ir_baud = SPEED_4000000; break; + break; + default: + ir_baud = SPEED_9600; + baud = 9600; + /* And once the new tty stuff is all done we need to + call back to correct the baud bits */ } - /* All we can change is the baud rate */ - if (cflag & CBAUD) { - - dbg ("%s - asking for baud %d", - __FUNCTION__, - tty_get_baud_rate(port->tty)); - - /* - * FIXME, we should compare the baud request against the - * capability stated in the IR header that we got in the - * startup function. - */ - switch (cflag & CBAUD) { - case B2400: ir_baud = SPEED_2400; break; - default: - case B9600: ir_baud = SPEED_9600; break; - case B19200: ir_baud = SPEED_19200; break; - case B38400: ir_baud = SPEED_38400; break; - case B57600: ir_baud = SPEED_57600; break; - case B115200: ir_baud = SPEED_115200; break; - case B576000: ir_baud = SPEED_576000; break; - case B1152000: ir_baud = SPEED_1152000; break; -#ifdef B4000000 - case B4000000: ir_baud = SPEED_4000000; break; -#endif - } + if (xbof == -1) + ir_xbof = ir_xbof_change(ir_add_bof); + else + ir_xbof = ir_xbof_change(xbof) ; - if (xbof == -1) { - ir_xbof = ir_xbof_change(ir_add_bof); - } else { - ir_xbof = ir_xbof_change(xbof) ; - } + /* FIXME need to check to see if our write urb is busy right + * now, or use a urb pool. + * + * send the baud change out on an "empty" data packet + */ + transfer_buffer = port->write_urb->transfer_buffer; + *transfer_buffer = ir_xbof | ir_baud; - /* Notify the tty driver that the termios have changed. */ - port->tty->ldisc.set_termios(port->tty, NULL); - - /* FIXME need to check to see if our write urb is busy right - * now, or use a urb pool. - * - * send the baud change out on an "empty" data packet - */ - transfer_buffer = port->write_urb->transfer_buffer; - *transfer_buffer = ir_xbof | ir_baud; - - usb_fill_bulk_urb ( - port->write_urb, - port->serial->dev, - usb_sndbulkpipe(port->serial->dev, - port->bulk_out_endpointAddress), - port->write_urb->transfer_buffer, - 1, - ir_write_bulk_callback, - port); - - port->write_urb->transfer_flags = URB_ZERO_PACKET; - - result = usb_submit_urb (port->write_urb, GFP_KERNEL); - if (result) - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); - } - return; + usb_fill_bulk_urb ( + port->write_urb, + port->serial->dev, + usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), + port->write_urb->transfer_buffer, + 1, + ir_write_bulk_callback, + port); + + port->write_urb->transfer_flags = URB_ZERO_PACKET; + + result = usb_submit_urb (port->write_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result); } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index e6966f1..f2a6fce 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -115,12 +115,13 @@ static int debug; /* * Version Information */ -#define DRIVER_VERSION "v1.1.4" +#define DRIVER_VERSION "v1.1.5" #define DRIVER_AUTHOR "Hugh Blemings <hugh@misc.nu" #define DRIVER_DESC "Keyspan USB to Serial Converter Driver" #define INSTAT_BUFLEN 32 #define GLOCONT_BUFLEN 64 +#define INDAT49W_BUFLEN 512 /* Per device and per port private data */ struct keyspan_serial_private { @@ -129,9 +130,15 @@ struct keyspan_serial_private { struct urb *instat_urb; char instat_buf[INSTAT_BUFLEN]; + /* added to support 49wg, where data from all 4 ports comes in on 1 EP */ + /* and high-speed supported */ + struct urb *indat_urb; + char indat_buf[INDAT49W_BUFLEN]; + /* XXX this one probably will need a lock */ struct urb *glocont_urb; char glocont_buf[GLOCONT_BUFLEN]; + char ctrl_buf[8]; // for EP0 control message }; struct keyspan_port_private { @@ -179,12 +186,13 @@ struct keyspan_port_private { /* Include Keyspan message headers. All current Keyspan Adapters - make use of one of four message formats which are referred - to as USA-26, USA-28 and USA-49, USA-90 by Keyspan and within this driver. */ + make use of one of five message formats which are referred + to as USA-26, USA-28, USA-49, USA-90, USA-67 by Keyspan and within this driver. */ #include "keyspan_usa26msg.h" #include "keyspan_usa28msg.h" #include "keyspan_usa49msg.h" #include "keyspan_usa90msg.h" +#include "keyspan_usa67msg.h" /* Functions used by new usb-serial code. */ @@ -419,14 +427,15 @@ static void usa26_indat_callback(struct urb *urb) struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; + int status = urb->status; dbg ("%s", __FUNCTION__); endpoint = usb_pipeendpoint(urb->pipe); - if (urb->status) { + if (status) { dbg("%s - nonzero status: %x on endpoint %d.", - __FUNCTION__, urb->status, endpoint); + __FUNCTION__, status, endpoint); return; } @@ -511,11 +520,12 @@ static void usa26_instat_callback(struct urb *urb) struct usb_serial_port *port; struct keyspan_port_private *p_priv; int old_dcd_state, err; + int status = urb->status; serial = (struct usb_serial *) urb->context; - if (urb->status) { - dbg("%s - nonzero status: %x", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero status: %x", __FUNCTION__, status); return; } if (urb->actual_length != 9) { @@ -579,6 +589,7 @@ static void usa28_indat_callback(struct urb *urb) struct tty_struct *tty; unsigned char *data; struct keyspan_port_private *p_priv; + int status = urb->status; dbg ("%s", __FUNCTION__); @@ -590,9 +601,9 @@ static void usa28_indat_callback(struct urb *urb) return; do { - if (urb->status) { + if (status) { dbg("%s - nonzero status: %x on endpoint %d.", - __FUNCTION__, urb->status, usb_pipeendpoint(urb->pipe)); + __FUNCTION__, status, usb_pipeendpoint(urb->pipe)); return; } @@ -648,11 +659,12 @@ static void usa28_instat_callback(struct urb *urb) struct usb_serial_port *port; struct keyspan_port_private *p_priv; int old_dcd_state; + int status = urb->status; serial = (struct usb_serial *) urb->context; - if (urb->status) { - dbg("%s - nonzero status: %x", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero status: %x", __FUNCTION__, status); return; } @@ -739,13 +751,14 @@ static void usa49_instat_callback(struct urb *urb) struct usb_serial_port *port; struct keyspan_port_private *p_priv; int old_dcd_state; + int status = urb->status; dbg ("%s", __FUNCTION__); serial = (struct usb_serial *) urb->context; - if (urb->status) { - dbg("%s - nonzero status: %x", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero status: %x", __FUNCTION__, status); return; } @@ -805,14 +818,15 @@ static void usa49_indat_callback(struct urb *urb) struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; + int status = urb->status; dbg ("%s", __FUNCTION__); endpoint = usb_pipeendpoint(urb->pipe); - if (urb->status) { + if (status) { dbg("%s - nonzero status: %x on endpoint %d.", __FUNCTION__, - urb->status, endpoint); + status, endpoint); return; } @@ -850,13 +864,90 @@ static void usa49_indat_callback(struct urb *urb) } } +static void usa49wg_indat_callback(struct urb *urb) +{ + int i, len, x, err; + struct usb_serial *serial; + struct usb_serial_port *port; + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + int status = urb->status; + + dbg ("%s", __FUNCTION__); + + serial = urb->context; + + if (status) { + dbg("%s - nonzero status: %x", __FUNCTION__, status); + return; + } + + /* inbound data is in the form P#, len, status, data */ + i = 0; + len = 0; + + if (urb->actual_length) { + while (i < urb->actual_length) { + + /* Check port number from message*/ + if (data[i] >= serial->num_ports) { + dbg ("%s - Unexpected port number %d", + __FUNCTION__, data[i]); + return; + } + port = serial->port[data[i++]]; + tty = port->tty; + len = data[i++]; + + /* 0x80 bit is error flag */ + if ((data[i] & 0x80) == 0) { + /* no error on any byte */ + i++; + for (x = 1; x < len ; ++x) + if (port->open_count) + tty_insert_flip_char(tty, + data[i++], 0); + else + i++; + } else { + /* + * some bytes had errors, every byte has status + */ + for (x = 0; x + 1 < len; x += 2) { + int stat = data[i], flag = 0; + if (stat & RXERROR_OVERRUN) + flag |= TTY_OVERRUN; + if (stat & RXERROR_FRAMING) + flag |= TTY_FRAME; + if (stat & RXERROR_PARITY) + flag |= TTY_PARITY; + /* XXX should handle break (0x10) */ + if (port->open_count) + tty_insert_flip_char(tty, + data[i+1], flag); + i += 2; + } + } + if (port->open_count) + tty_flip_buffer_push(tty); + } + } + + /* Resubmit urb so we continue receiving */ + urb->dev = serial->dev; + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) + dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err); +} + /* not used, usa-49 doesn't have per-port control endpoints */ -static void usa49_outcont_callback(struct urb *urb) +static void usa49_outcont_callback(struct urb *urb) { dbg ("%s", __FUNCTION__); } -static void usa90_indat_callback(struct urb *urb) +static void usa90_indat_callback(struct urb *urb) { int i, err; int endpoint; @@ -864,15 +955,15 @@ static void usa90_indat_callback(struct urb *urb) struct keyspan_port_private *p_priv; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; + int status = urb->status; dbg ("%s", __FUNCTION__); endpoint = usb_pipeendpoint(urb->pipe); - - if (urb->status) { + if (status) { dbg("%s - nonzero status: %x on endpoint %d.", - __FUNCTION__, urb->status, endpoint); + __FUNCTION__, status, endpoint); return; } @@ -938,11 +1029,12 @@ static void usa90_instat_callback(struct urb *urb) struct usb_serial_port *port; struct keyspan_port_private *p_priv; int old_dcd_state, err; + int status = urb->status; serial = (struct usb_serial *) urb->context; - if (urb->status) { - dbg("%s - nonzero status: %x", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero status: %x", __FUNCTION__, status); return; } if (urb->actual_length < 14) { @@ -995,6 +1087,88 @@ static void usa90_outcont_callback(struct urb *urb) } } +/* Status messages from the 28xg */ +static void usa67_instat_callback(struct urb *urb) +{ + int err; + unsigned char *data = urb->transfer_buffer; + struct keyspan_usa67_portStatusMessage *msg; + struct usb_serial *serial; + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + int old_dcd_state; + int status = urb->status; + + dbg ("%s", __FUNCTION__); + + serial = urb->context; + + if (status) { + dbg("%s - nonzero status: %x", __FUNCTION__, status); + return; + } + + if (urb->actual_length != sizeof(struct keyspan_usa67_portStatusMessage)) { + dbg("%s - bad length %d", __FUNCTION__, urb->actual_length); + return; + } + + + /* Now do something useful with the data */ + msg = (struct keyspan_usa67_portStatusMessage *)data; + + /* Check port number from message and retrieve private data */ + if (msg->port >= serial->num_ports) { + dbg ("%s - Unexpected port number %d", __FUNCTION__, msg->port); + return; + } + + port = serial->port[msg->port]; + p_priv = usb_get_serial_port_data(port); + + /* Update handshaking pin state information */ + old_dcd_state = p_priv->dcd_state; + p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); + p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); + + if (port->tty && !C_CLOCAL(port->tty) + && old_dcd_state != p_priv->dcd_state) { + if (old_dcd_state) + tty_hangup(port->tty); + /* else */ + /* wake_up_interruptible(&p_priv->open_wait); */ + } + + /* Resubmit urb so we continue receiving */ + urb->dev = serial->dev; + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err != 0) + dbg("%s - resubmit read urb failed. (%d)", __FUNCTION__, err); +} + +static void usa67_glocont_callback(struct urb *urb) +{ + struct usb_serial *serial; + struct usb_serial_port *port; + struct keyspan_port_private *p_priv; + int i; + + dbg ("%s", __FUNCTION__); + + serial = urb->context; + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + p_priv = usb_get_serial_port_data(port); + + if (p_priv->resend_cont) { + dbg ("%s - sending setup", __FUNCTION__); + keyspan_usa67_send_setup(serial, port, + p_priv->resend_cont - 1); + break; + } + } +} + static int keyspan_write_room (struct usb_serial_port *port) { struct keyspan_port_private *p_priv; @@ -1311,6 +1485,11 @@ static struct urb *keyspan_setup_urb (struct usb_serial *serial, int endpoint, return NULL; } + if (endpoint == 0) { + /* control EP filled in when used */ + return urb; + } + ep_desc = find_ep(serial, endpoint); if (!ep_desc) { /* leak the urb, something's wrong and the callers don't care */ @@ -1380,6 +1559,14 @@ static struct callbacks { .outdat_callback = usa2x_outdat_callback, .inack_callback = usa28_inack_callback, .outcont_callback = usa90_outcont_callback, + }, { + /* msg_usa67 callbacks */ + .instat_callback = usa67_instat_callback, + .glocont_callback = usa67_glocont_callback, + .indat_callback = usa26_indat_callback, + .outdat_callback = usa2x_outdat_callback, + .inack_callback = usa26_inack_callback, + .outcont_callback = usa26_outcont_callback, } }; @@ -1410,6 +1597,11 @@ static void keyspan_setup_urbs(struct usb_serial *serial) serial, s_priv->instat_buf, INSTAT_BUFLEN, cback->instat_callback); + s_priv->indat_urb = keyspan_setup_urb + (serial, d_details->indat_endpoint, USB_DIR_IN, + serial, s_priv->indat_buf, INDAT49W_BUFLEN, + usa49wg_indat_callback); + s_priv->glocont_urb = keyspan_setup_urb (serial, d_details->glocont_endpoint, USB_DIR_OUT, serial, s_priv->glocont_buf, GLOCONT_BUFLEN, @@ -1685,8 +1877,8 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial, } /* Save reset port val for resend. - Don't overwrite resend for close condition. */ - if (p_priv->resend_cont != 3) + Don't overwrite resend for open/close condition. */ + if ((reset_port + 1) > p_priv->resend_cont) p_priv->resend_cont = reset_port + 1; if (this_urb->status == -EINPROGRESS) { /* dbg ("%s - already writing", __FUNCTION__); */ @@ -1836,8 +2028,8 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial, } /* Save reset port val for resend. - Don't overwrite resend for close condition. */ - if (p_priv->resend_cont != 3) + Don't overwrite resend for open/close condition. */ + if ((reset_port + 1) > p_priv->resend_cont) p_priv->resend_cont = reset_port + 1; if (this_urb->status == -EINPROGRESS) { dbg ("%s already writing", __FUNCTION__); @@ -1940,11 +2132,11 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, struct usb_serial_port *port, int reset_port) { - struct keyspan_usa49_portControlMessage msg; + struct keyspan_usa49_portControlMessage msg; + struct usb_ctrlrequest *dr = NULL; struct keyspan_serial_private *s_priv; struct keyspan_port_private *p_priv; const struct keyspan_device_details *d_details; - int glocont_urb; struct urb *this_urb; int err, device_port; @@ -1954,10 +2146,9 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, p_priv = usb_get_serial_port_data(port); d_details = s_priv->device_details; - glocont_urb = d_details->glocont_endpoint; this_urb = s_priv->glocont_urb; - /* Work out which port within the device is being setup */ + /* Work out which port within the device is being setup */ device_port = port->number - port->serial->minor; dbg("%s - endpoint %d port %d (%d)",__FUNCTION__, usb_pipeendpoint(this_urb->pipe), port->number, device_port); @@ -1969,9 +2160,10 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, } /* Save reset port val for resend. - Don't overwrite resend for close condition. */ - if (p_priv->resend_cont != 3) + Don't overwrite resend for open/close condition. */ + if ((reset_port + 1) > p_priv->resend_cont) p_priv->resend_cont = reset_port + 1; + if (this_urb->status == -EINPROGRESS) { /* dbg ("%s - already writing", __FUNCTION__); */ mdelay(5); @@ -2083,20 +2275,39 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial, msg.dtr = p_priv->dtr_state; p_priv->resend_cont = 0; - memcpy (this_urb->transfer_buffer, &msg, sizeof(msg)); + + /* if the device is a 49wg, we send control message on usb control EP 0 */ + + if (d_details->product_id == keyspan_usa49wg_product_id) { + dr = (void *)(s_priv->ctrl_buf); + dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_OUT; + dr->bRequest = 0xB0; /* 49wg control message */; + dr->wValue = 0; + dr->wIndex = 0; + dr->wLength = cpu_to_le16(sizeof(msg)); + + memcpy (s_priv->glocont_buf, &msg, sizeof(msg)); + + usb_fill_control_urb(this_urb, serial->dev, usb_sndctrlpipe(serial->dev, 0), + (unsigned char *)dr, s_priv->glocont_buf, sizeof(msg), + usa49_glocont_callback, serial); + + } else { + memcpy(this_urb->transfer_buffer, &msg, sizeof(msg)); - /* send the data out the device on control endpoint */ - this_urb->transfer_buffer_length = sizeof(msg); + /* send the data out the device on control endpoint */ + this_urb->transfer_buffer_length = sizeof(msg); - this_urb->dev = serial->dev; + this_urb->dev = serial->dev; + } if ((err = usb_submit_urb(this_urb, GFP_ATOMIC)) != 0) { dbg("%s - usb_submit_urb(setup) failed (%d)", __FUNCTION__, err); } #if 0 else { dbg("%s - usb_submit_urb(%d) OK %d bytes (end %d)", __FUNCTION__, - outcont_urb, this_urb->transfer_buffer_length, - usb_pipeendpoint(this_urb->pipe)); + outcont_urb, this_urb->transfer_buffer_length, + usb_pipeendpoint(this_urb->pipe)); } #endif @@ -2241,6 +2452,154 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial, return (0); } +static int keyspan_usa67_send_setup(struct usb_serial *serial, + struct usb_serial_port *port, + int reset_port) +{ + struct keyspan_usa67_portControlMessage msg; + struct keyspan_serial_private *s_priv; + struct keyspan_port_private *p_priv; + const struct keyspan_device_details *d_details; + struct urb *this_urb; + int err, device_port; + + dbg ("%s", __FUNCTION__); + + s_priv = usb_get_serial_data(serial); + p_priv = usb_get_serial_port_data(port); + d_details = s_priv->device_details; + + this_urb = s_priv->glocont_urb; + + /* Work out which port within the device is being setup */ + device_port = port->number - port->serial->minor; + + /* Make sure we have an urb then send the message */ + if (this_urb == NULL) { + dbg("%s - oops no urb for port %d.", __FUNCTION__, + port->number); + return -1; + } + + /* Save reset port val for resend. + Don't overwrite resend for open/close condition. */ + if ((reset_port + 1) > p_priv->resend_cont) + p_priv->resend_cont = reset_port + 1; + if (this_urb->status == -EINPROGRESS) { + /* dbg ("%s - already writing", __FUNCTION__); */ + mdelay(5); + return(-1); + } + + memset(&msg, 0, sizeof(struct keyspan_usa67_portControlMessage)); + + msg.port = device_port; + + /* Only set baud rate if it's changed */ + if (p_priv->old_baud != p_priv->baud) { + p_priv->old_baud = p_priv->baud; + msg.setClocking = 0xff; + if (d_details->calculate_baud_rate + (p_priv->baud, d_details->baudclk, &msg.baudHi, + &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE ) { + dbg("%s - Invalid baud rate %d requested, using 9600.", __FUNCTION__, + p_priv->baud); + msg.baudLo = 0; + msg.baudHi = 125; /* Values for 9600 baud */ + msg.prescaler = 10; + } + msg.setPrescaler = 0xff; + } + + msg.lcr = (p_priv->cflag & CSTOPB) ? STOPBITS_678_2 : STOPBITS_5678_1; + switch (p_priv->cflag & CSIZE) { + case CS5: + msg.lcr |= USA_DATABITS_5; + break; + case CS6: + msg.lcr |= USA_DATABITS_6; + break; + case CS7: + msg.lcr |= USA_DATABITS_7; + break; + case CS8: + msg.lcr |= USA_DATABITS_8; + break; + } + if (p_priv->cflag & PARENB) { + /* note USA_PARITY_NONE == 0 */ + msg.lcr |= (p_priv->cflag & PARODD)? + USA_PARITY_ODD: USA_PARITY_EVEN; + } + msg.setLcr = 0xff; + + msg.ctsFlowControl = (p_priv->flow_control == flow_cts); + msg.xonFlowControl = 0; + msg.setFlowControl = 0xff; + msg.forwardingLength = 16; + msg.xonChar = 17; + msg.xoffChar = 19; + + if (reset_port == 1) { + /* Opening port */ + msg._txOn = 1; + msg._txOff = 0; + msg.txFlush = 0; + msg.txBreak = 0; + msg.rxOn = 1; + msg.rxOff = 0; + msg.rxFlush = 1; + msg.rxForward = 0; + msg.returnStatus = 0; + msg.resetDataToggle = 0xff; + } else if (reset_port == 2) { + /* Closing port */ + msg._txOn = 0; + msg._txOff = 1; + msg.txFlush = 0; + msg.txBreak = 0; + msg.rxOn = 0; + msg.rxOff = 1; + msg.rxFlush = 1; + msg.rxForward = 0; + msg.returnStatus = 0; + msg.resetDataToggle = 0; + } else { + /* Sending intermediate configs */ + msg._txOn = (! p_priv->break_on); + msg._txOff = 0; + msg.txFlush = 0; + msg.txBreak = (p_priv->break_on); + msg.rxOn = 0; + msg.rxOff = 0; + msg.rxFlush = 0; + msg.rxForward = 0; + msg.returnStatus = 0; + msg.resetDataToggle = 0x0; + } + + /* Do handshaking outputs */ + msg.setTxTriState_setRts = 0xff; + msg.txTriState_rts = p_priv->rts_state; + + msg.setHskoa_setDtr = 0xff; + msg.hskoa_dtr = p_priv->dtr_state; + + p_priv->resend_cont = 0; + + memcpy(this_urb->transfer_buffer, &msg, sizeof(msg)); + + /* send the data out the device on control endpoint */ + this_urb->transfer_buffer_length = sizeof(msg); + this_urb->dev = serial->dev; + + err = usb_submit_urb(this_urb, GFP_ATOMIC); + if (err != 0) + dbg("%s - usb_submit_urb(setup) failed (%d)", __FUNCTION__, + err); + return (0); +} + static void keyspan_send_setup(struct usb_serial_port *port, int reset_port) { struct usb_serial *serial = port->serial; @@ -2265,6 +2624,9 @@ static void keyspan_send_setup(struct usb_serial_port *port, int reset_port) case msg_usa90: keyspan_usa90_send_setup(serial, port, reset_port); break; + case msg_usa67: + keyspan_usa67_send_setup(serial, port, reset_port); + break; } } @@ -2313,9 +2675,19 @@ static int keyspan_startup (struct usb_serial *serial) keyspan_setup_urbs(serial); - s_priv->instat_urb->dev = serial->dev; - if ((err = usb_submit_urb(s_priv->instat_urb, GFP_KERNEL)) != 0) { - dbg("%s - submit instat urb failed %d", __FUNCTION__, err); + if (s_priv->instat_urb != NULL) { + s_priv->instat_urb->dev = serial->dev; + err = usb_submit_urb(s_priv->instat_urb, GFP_KERNEL); + if (err != 0) + dbg("%s - submit instat urb failed %d", __FUNCTION__, + err); + } + if (s_priv->indat_urb != NULL) { + s_priv->indat_urb->dev = serial->dev; + err = usb_submit_urb(s_priv->indat_urb, GFP_KERNEL); + if (err != 0) + dbg("%s - submit indat urb failed %d", __FUNCTION__, + err); } return (0); @@ -2335,6 +2707,7 @@ static void keyspan_shutdown (struct usb_serial *serial) /* Stop reading/writing urbs */ stop_urb(s_priv->instat_urb); stop_urb(s_priv->glocont_urb); + stop_urb(s_priv->indat_urb); for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; p_priv = usb_get_serial_port_data(port); @@ -2348,6 +2721,7 @@ static void keyspan_shutdown (struct usb_serial *serial) /* Now free them */ usb_free_urb(s_priv->instat_urb); + usb_free_urb(s_priv->indat_urb); usb_free_urb(s_priv->glocont_urb); for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index c6830cb..8a0d174 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -99,6 +99,10 @@ static int keyspan_usa90_send_setup (struct usb_serial *serial, struct usb_serial_port *port, int reset_port); +static int keyspan_usa67_send_setup (struct usb_serial *serial, + struct usb_serial_port *port, + int reset_port); + /* Struct used for firmware - increased size of data section to allow Keyspan's 'C' firmware struct to be used unmodified */ struct ezusb_hex_record { @@ -229,15 +233,17 @@ 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_usa28xg_product_id 0x0135 #define keyspan_usa49w_product_id 0x010a #define keyspan_usa49wlc_product_id 0x012a - +#define keyspan_usa49wg_product_id 0x0131 struct keyspan_device_details { /* product ID value */ int product_id; - enum {msg_usa26, msg_usa28, msg_usa49, msg_usa90} msg_format; + enum {msg_usa26, msg_usa28, msg_usa49, msg_usa90, msg_usa67} msg_format; /* Number of physical ports */ int num_ports; @@ -264,6 +270,9 @@ struct keyspan_device_details { /* Endpoint used for input status */ int instat_endpoint; + /* Endpoint used for input data 49WG only */ + int indat_endpoint; + /* Endpoint used for global control functions */ int glocont_endpoint; @@ -287,6 +296,7 @@ static const struct keyspan_device_details usa18x_device_details = { .inack_endpoints = {0x85}, .outcont_endpoints = {0x05}, .instat_endpoint = 0x87, + .indat_endpoint = -1, .glocont_endpoint = 0x07, .calculate_baud_rate = keyspan_usa19w_calc_baud, .baudclk = KEYSPAN_USA18X_BAUDCLK, @@ -303,6 +313,7 @@ static const struct keyspan_device_details usa19_device_details = { .inack_endpoints = {0x83}, .outcont_endpoints = {0x03}, .instat_endpoint = 0x84, + .indat_endpoint = -1, .glocont_endpoint = -1, .calculate_baud_rate = keyspan_usa19_calc_baud, .baudclk = KEYSPAN_USA19_BAUDCLK, @@ -319,6 +330,7 @@ static const struct keyspan_device_details usa19qi_device_details = { .inack_endpoints = {0x83}, .outcont_endpoints = {0x03}, .instat_endpoint = 0x84, + .indat_endpoint = -1, .glocont_endpoint = -1, .calculate_baud_rate = keyspan_usa28_calc_baud, .baudclk = KEYSPAN_USA19_BAUDCLK, @@ -335,6 +347,7 @@ static const struct keyspan_device_details mpr_device_details = { .inack_endpoints = {0x83}, .outcont_endpoints = {0x03}, .instat_endpoint = 0x84, + .indat_endpoint = -1, .glocont_endpoint = -1, .calculate_baud_rate = keyspan_usa28_calc_baud, .baudclk = KEYSPAN_USA19_BAUDCLK, @@ -351,6 +364,7 @@ static const struct keyspan_device_details usa19qw_device_details = { .inack_endpoints = {0x85}, .outcont_endpoints = {0x05}, .instat_endpoint = 0x87, + .indat_endpoint = -1, .glocont_endpoint = 0x07, .calculate_baud_rate = keyspan_usa19w_calc_baud, .baudclk = KEYSPAN_USA19W_BAUDCLK, @@ -367,6 +381,7 @@ static const struct keyspan_device_details usa19w_device_details = { .inack_endpoints = {0x85}, .outcont_endpoints = {0x05}, .instat_endpoint = 0x87, + .indat_endpoint = -1, .glocont_endpoint = 0x07, .calculate_baud_rate = keyspan_usa19w_calc_baud, .baudclk = KEYSPAN_USA19W_BAUDCLK, @@ -383,6 +398,7 @@ static const struct keyspan_device_details usa19hs_device_details = { .inack_endpoints = {-1}, .outcont_endpoints = {0x02}, .instat_endpoint = 0x82, + .indat_endpoint = -1, .glocont_endpoint = -1, .calculate_baud_rate = keyspan_usa19hs_calc_baud, .baudclk = KEYSPAN_USA19HS_BAUDCLK, @@ -399,6 +415,7 @@ static const struct keyspan_device_details usa28_device_details = { .inack_endpoints = {0x85, 0x86}, .outcont_endpoints = {0x05, 0x06}, .instat_endpoint = 0x87, + .indat_endpoint = -1, .glocont_endpoint = 0x07, .calculate_baud_rate = keyspan_usa28_calc_baud, .baudclk = KEYSPAN_USA28_BAUDCLK, @@ -415,6 +432,7 @@ static const struct keyspan_device_details usa28x_device_details = { .inack_endpoints = {0x85, 0x86}, .outcont_endpoints = {0x05, 0x06}, .instat_endpoint = 0x87, + .indat_endpoint = -1, .glocont_endpoint = 0x07, .calculate_baud_rate = keyspan_usa19w_calc_baud, .baudclk = KEYSPAN_USA28X_BAUDCLK, @@ -431,11 +449,28 @@ static const struct keyspan_device_details usa28xa_device_details = { .inack_endpoints = {0x85, 0x86}, .outcont_endpoints = {0x05, 0x06}, .instat_endpoint = 0x87, + .indat_endpoint = -1, .glocont_endpoint = 0x07, .calculate_baud_rate = keyspan_usa19w_calc_baud, .baudclk = KEYSPAN_USA28X_BAUDCLK, }; +static const struct keyspan_device_details usa28xg_device_details = { + .product_id = keyspan_usa28xg_product_id, + .msg_format = msg_usa67, + .num_ports = 2, + .indat_endp_flip = 0, + .outdat_endp_flip = 0, + .indat_endpoints = {0x84, 0x88}, + .outdat_endpoints = {0x02, 0x06}, + .inack_endpoints = {-1, -1}, + .outcont_endpoints = {-1, -1}, + .instat_endpoint = 0x81, + .indat_endpoint = -1, + .glocont_endpoint = 0x01, + .calculate_baud_rate = keyspan_usa19w_calc_baud, + .baudclk = KEYSPAN_USA28X_BAUDCLK, +}; /* We don't need a separate entry for the usa28xb as it appears as a 28x anyway */ static const struct keyspan_device_details usa49w_device_details = { @@ -449,6 +484,7 @@ static const struct keyspan_device_details usa49w_device_details = { .inack_endpoints = {-1, -1, -1, -1}, .outcont_endpoints = {-1, -1, -1, -1}, .instat_endpoint = 0x87, + .indat_endpoint = -1, .glocont_endpoint = 0x07, .calculate_baud_rate = keyspan_usa19w_calc_baud, .baudclk = KEYSPAN_USA49W_BAUDCLK, @@ -465,11 +501,29 @@ static const struct keyspan_device_details usa49wlc_device_details = { .inack_endpoints = {-1, -1, -1, -1}, .outcont_endpoints = {-1, -1, -1, -1}, .instat_endpoint = 0x87, + .indat_endpoint = -1, .glocont_endpoint = 0x07, .calculate_baud_rate = keyspan_usa19w_calc_baud, .baudclk = KEYSPAN_USA19W_BAUDCLK, }; +static const struct keyspan_device_details usa49wg_device_details = { + .product_id = keyspan_usa49wg_product_id, + .msg_format = msg_usa49, + .num_ports = 4, + .indat_endp_flip = 0, + .outdat_endp_flip = 0, + .indat_endpoints = {-1, -1, -1, -1}, /* single 'global' data in EP */ + .outdat_endpoints = {0x01, 0x02, 0x04, 0x06}, + .inack_endpoints = {-1, -1, -1, -1}, + .outcont_endpoints = {-1, -1, -1, -1}, + .instat_endpoint = 0x81, + .indat_endpoint = 0x88, + .glocont_endpoint = 0x00, /* uses control EP */ + .calculate_baud_rate = keyspan_usa19w_calc_baud, + .baudclk = KEYSPAN_USA19W_BAUDCLK, +}; + static const struct keyspan_device_details *keyspan_devices[] = { &usa18x_device_details, &usa19_device_details, @@ -481,9 +535,11 @@ static const struct keyspan_device_details *keyspan_devices[] = { &usa28_device_details, &usa28x_device_details, &usa28xa_device_details, + &usa28xg_device_details, /* 28xb not required as it renumerates as a 28x */ &usa49w_device_details, &usa49wlc_device_details, + &usa49wg_device_details, NULL, }; @@ -510,8 +566,11 @@ 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_usa28xg_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id)}, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)}, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)}, { } /* Terminating entry */ }; @@ -557,12 +616,15 @@ 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) }, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa28xg_product_id) }, { } /* Terminating entry */ }; static struct usb_device_id keyspan_4port_ids[] = { { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49w_product_id) }, { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wlc_product_id)}, + { USB_DEVICE(KEYSPAN_VENDOR_ID, keyspan_usa49wg_product_id)}, { } /* Terminating entry */ }; @@ -573,7 +635,6 @@ 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, @@ -588,7 +649,6 @@ 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, @@ -616,7 +676,6 @@ 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,11 +703,10 @@ 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, - .num_bulk_out = 5, + .num_bulk_in = NUM_DONT_CARE, + .num_bulk_out = NUM_DONT_CARE, .num_ports = 4, .open = keyspan_open, .close = keyspan_close, diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index dd0b66a..be9ac20 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -218,11 +218,12 @@ static void keyspan_pda_rx_interrupt (struct urb *urb) struct tty_struct *tty = port->tty; unsigned char *data = urb->transfer_buffer; int i; - int status; + int retval; + int status = urb->status; struct keyspan_pda_private *priv; priv = usb_get_serial_port_data(port); - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -230,10 +231,12 @@ static void keyspan_pda_rx_interrupt (struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, status); goto exit; } @@ -268,10 +271,10 @@ static void keyspan_pda_rx_interrupt (struct urb *urb) } exit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) err ("%s - usb_submit_urb failed with result %d", - __FUNCTION__, status); + __FUNCTION__, retval); } diff --git a/drivers/usb/serial/keyspan_usa67msg.h b/drivers/usb/serial/keyspan_usa67msg.h new file mode 100644 index 0000000..20fa3e2 --- /dev/null +++ b/drivers/usb/serial/keyspan_usa67msg.h @@ -0,0 +1,254 @@ +/* + usa67msg.h + + Copyright (c) 1998-2007 InnoSys Incorporated. All Rights Reserved + This file is available under a BSD-style copyright + + Keyspan USB Async Firmware to run on Anchor FX1 + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain this licence text + without modification, this list of conditions, and the following + disclaimer. The following copyright notice must appear immediately at + the beginning of all source files: + + Copyright (c) 1998-2007 InnoSys Incorporated. All Rights Reserved + + This file is available under a BSD-style copyright + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of InnoSys Incorprated may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY INNOSYS CORP. ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + Fourth revision: This message format supports the USA28XG + + Buffer formats for RX/TX data messages are not defined by + a structure, but are described here: + + USB OUT (host -> USAxx, transmit) messages contain a + REQUEST_ACK indicator (set to 0xff to request an ACK at the + completion of transmit; 0x00 otherwise), followed by data: + + RQSTACK DAT DAT DAT ... + + with a total data length of up to 63. + + USB IN (USAxx -> host, receive) messages begin with a status + byte in which the 0x80 bit is either: + + (a) 0x80 bit clear + indicates that the bytes following it are all data + bytes: + + STAT DATA DATA DATA DATA DATA ... + + for a total of up to 63 DATA bytes, + + or: + + (b) 0x80 bit set + indiates that the bytes following alternate data and + status bytes: + + STAT DATA STAT DATA STAT DATA STAT DATA ... + + for a total of up to 32 DATA bytes. + + The valid bits in the STAT bytes are: + + OVERRUN 0x02 + PARITY 0x04 + FRAMING 0x08 + BREAK 0x10 + + Notes: + + (1) The OVERRUN bit can appear in either (a) or (b) format + messages, but the but the PARITY/FRAMING/BREAK bits + only appear in (b) format messages. + (2) For the host to determine the exact point at which the + overrun occurred (to identify the point in the data + stream at which the data was lost), it needs to count + 128 characters, starting at the first character of the + message in which OVERRUN was reported; the lost character(s) + would have been received between the 128th and 129th + characters. + (3) An RX data message in which the first byte has 0x80 clear + serves as a "break off" indicator. + + revision history: + + 1999feb10 add reportHskiaChanges to allow us to ignore them + 1999feb10 add txAckThreshold for fast+loose throughput enhancement + 1999mar30 beef up support for RX error reporting + 1999apr14 add resetDataToggle to control message + 2000jan04 merge with usa17msg.h + 2000jun01 add extended BSD-style copyright text + 2001jul05 change message format to improve OVERRUN case + 2002jun05 update copyright date, improve comments + 2006feb06 modify for FX1 chip + +*/ + +#ifndef __USA67MSG__ +#define __USA67MSG__ + + +// all things called "ControlMessage" are sent on the 'control' endpoint + +typedef struct keyspan_usa67_portControlMessage +{ + u8 port; // 0 or 1 (selects port) + /* + there are three types of "commands" sent in the control message: + + 1. configuration changes which must be requested by setting + the corresponding "set" flag (and should only be requested + when necessary, to reduce overhead on the device): + */ + u8 setClocking, // host requests baud rate be set + baudLo, // host does baud divisor calculation + baudHi, // baudHi is only used for first port (gives lower rates) + externalClock_txClocking, + // 0=internal, other=external + + setLcr, // host requests lcr be set + lcr, // use PARITY, STOPBITS, DATABITS below + + setFlowControl, // host requests flow control be set + ctsFlowControl, // 1=use CTS flow control, 0=don't + xonFlowControl, // 1=use XON/XOFF flow control, 0=don't + xonChar, // specified in current character format + xoffChar, // specified in current character format + + setTxTriState_setRts, + // host requests TX tri-state be set + txTriState_rts, // 1=active (normal), 0=tristate (off) + + setHskoa_setDtr, + // host requests HSKOA output be set + hskoa_dtr, // 1=on, 0=off + + setPrescaler, // host requests prescalar be set (default: 13) + prescaler; // specified as N/8; values 8-ff are valid + // must be set any time internal baud rate is set; + // must not be set when external clocking is used + + /* + 3. configuration data which is simply used as is (no overhead, + but must be specified correctly in every host message). + */ + u8 forwardingLength, // forward when this number of chars available + reportHskiaChanges_dsrFlowControl, + // 1=normal; 0=ignore external clock + // 1=use DSR flow control, 0=don't + txAckThreshold, // 0=not allowed, 1=normal, 2-255 deliver ACK faster + loopbackMode; // 0=no loopback, 1=loopback enabled + + /* + 4. commands which are flags only; these are processed in order + (so that, e.g., if both _txOn and _txOff flags are set, the + port ends in a TX_OFF state); any non-zero value is respected + */ + u8 _txOn, // enable transmitting (and continue if there's data) + _txOff, // stop transmitting + txFlush, // toss outbound data + txBreak, // turn on break (cleared by _txOn) + rxOn, // turn on receiver + rxOff, // turn off receiver + rxFlush, // toss inbound data + rxForward, // forward all inbound data, NOW (as if fwdLen==1) + returnStatus, // return current status (even if it hasn't changed) + resetDataToggle;// reset data toggle state to DATA0 + +} keyspan_usa67_portControlMessage; + +// defines for bits in lcr +#define USA_DATABITS_5 0x00 +#define USA_DATABITS_6 0x01 +#define USA_DATABITS_7 0x02 +#define USA_DATABITS_8 0x03 +#define STOPBITS_5678_1 0x00 // 1 stop bit for all byte sizes +#define STOPBITS_5_1p5 0x04 // 1.5 stop bits for 5-bit byte +#define STOPBITS_678_2 0x04 // 2 stop bits for 6/7/8-bit byte +#define USA_PARITY_NONE 0x00 +#define USA_PARITY_ODD 0x08 +#define USA_PARITY_EVEN 0x18 +#define PARITY_1 0x28 +#define PARITY_0 0x38 + +// all things called "StatusMessage" are sent on the status endpoint + +typedef struct keyspan_usa67_portStatusMessage // one for each port +{ + u8 port, // 0=first, 1=second, other=see below + hskia_cts, // reports HSKIA pin + gpia_dcd, // reports GPIA pin + _txOff, // port has been disabled (by host) + _txXoff, // port is in XOFF state (either host or RX XOFF) + txAck, // indicates a TX message acknowledgement + rxEnabled, // as configured by rxOn/rxOff 1=on, 0=off + controlResponse;// 1=a control message has been processed +} keyspan_usa67_portStatusMessage; + +// bits in RX data message when STAT byte is included +#define RXERROR_OVERRUN 0x02 +#define RXERROR_PARITY 0x04 +#define RXERROR_FRAMING 0x08 +#define RXERROR_BREAK 0x10 + +typedef struct keyspan_usa67_globalControlMessage +{ + u8 port, // 3 + sendGlobalStatus, // 2=request for two status responses + resetStatusToggle, // 1=reset global status toggle + resetStatusCount; // a cycling value +} keyspan_usa67_globalControlMessage; + +typedef struct keyspan_usa67_globalStatusMessage +{ + u8 port, // 3 + sendGlobalStatus, // from request, decremented + resetStatusCount; // as in request +} keyspan_usa67_globalStatusMessage; + +typedef struct keyspan_usa67_globalDebugMessage +{ + u8 port, // 2 + a, + b, + c, + d; +} keyspan_usa67_globalDebugMessage; + +// ie: the maximum length of an FX1 endpoint buffer +#define MAX_DATA_LEN 64 + +// update status approx. 60 times a second (16.6666 ms) +#define STATUS_UPDATE_INTERVAL 16 + +// status rationing tuning value (each port gets checked each n ms) +#define STATUS_RATION 10 + +#endif + + diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 7b085f3..5a4127e 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -567,12 +567,13 @@ exit: static void klsi_105_write_bulk_callback ( struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->status) { + + if (status) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, - urb->status); + status); return; } @@ -631,16 +632,17 @@ static void klsi_105_read_bulk_callback (struct urb *urb) struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int rc; + int status = urb->status; - dbg("%s - port %d", __FUNCTION__, port->number); + dbg("%s - port %d", __FUNCTION__, port->number); /* The urb might have been killed. */ - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, - urb->status); - return; - } - + if (status) { + dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, + status); + return; + } + /* The data received is again preceded by a length double-byte in LSB- * first order (see klsi_105_write() ) */ diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 0683b51..02a86db 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -358,24 +358,26 @@ static void kobil_close (struct usb_serial_port *port, struct file *filp) } -static void kobil_read_int_callback( struct urb *purb) +static void kobil_read_int_callback(struct urb *urb) { int result; - struct usb_serial_port *port = (struct usb_serial_port *) purb->context; + struct usb_serial_port *port = urb->context; struct tty_struct *tty; - unsigned char *data = purb->transfer_buffer; + unsigned char *data = urb->transfer_buffer; + int status = urb->status; // char *dbg_data; dbg("%s - port %d", __FUNCTION__, port->number); - if (purb->status) { - dbg("%s - port %d Read int status not zero: %d", __FUNCTION__, port->number, purb->status); + if (status) { + dbg("%s - port %d Read int status not zero: %d", + __FUNCTION__, port->number, status); return; } - - tty = port->tty; - if (purb->actual_length) { - + + tty = port->tty; + if (urb->actual_length) { + // BEGIN DEBUG /* dbg_data = kzalloc((3 * purb->actual_length + 10) * sizeof(char), GFP_KERNEL); @@ -390,15 +392,15 @@ static void kobil_read_int_callback( struct urb *purb) */ // END DEBUG - tty_buffer_request_room(tty, purb->actual_length); - tty_insert_flip_string(tty, data, purb->actual_length); + tty_buffer_request_room(tty, urb->actual_length); + tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } // someone sets the dev to 0 if the close method has been called port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb( port->interrupt_in_urb, GFP_ATOMIC ); + result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); dbg("%s - port %d Send read URB returns: %i", __FUNCTION__, port->number, result); } diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 3db1adc..2a3fabc 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -81,7 +81,7 @@ /* * Version Information */ -#define DRIVER_VERSION "z2.0" /* Linux in-kernel version */ +#define DRIVER_VERSION "z2.1" /* Linux in-kernel version */ #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>" #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver" @@ -110,6 +110,10 @@ static int mct_u232_tiocmget (struct usb_serial_port *port, static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear); +static void mct_u232_throttle (struct usb_serial_port *port); +static void mct_u232_unthrottle (struct usb_serial_port *port); + + /* * All of the device info needed for the MCT USB-RS232 converter. */ @@ -145,6 +149,8 @@ static struct usb_serial_driver mct_u232_device = { .num_ports = 1, .open = mct_u232_open, .close = mct_u232_close, + .throttle = mct_u232_throttle, + .unthrottle = mct_u232_unthrottle, .read_int_callback = mct_u232_read_int_callback, .ioctl = mct_u232_ioctl, .set_termios = mct_u232_set_termios, @@ -162,8 +168,11 @@ struct mct_u232_private { unsigned char last_lcr; /* Line Control Register */ unsigned char last_lsr; /* Line Status Register */ unsigned char last_msr; /* Modem Status Register */ + unsigned int rx_flags; /* Throttling flags */ }; +#define THROTTLED 0x01 + /* * Handle vendor specific USB requests */ @@ -216,11 +225,13 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int value) } } -static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) +static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port, + int value) { __le32 divisor; int rc; unsigned char zero_byte = 0; + unsigned char cts_enable_byte = 0; divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value)); @@ -238,10 +249,17 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) 'baud rate change' message. The actual functionality of the request codes in these messages is not fully understood but these particular codes are never seen in any operation besides a baud - rate change. Both of these messages send a single byte of data - whose value is always zero. The second of these two extra messages - is required in order for data to be properly written to an RS-232 - device which does not assert the 'CTS' signal. */ + rate change. Both of these messages send a single byte of data. + In the first message, the value of this byte is always zero. + + The second message has been determined experimentally to control + whether data will be transmitted to a device which is not asserting + the 'CTS' signal. If the second message's data byte is zero, data + will be transmitted even if 'CTS' is not asserted (i.e. no hardware + flow control). if the second message's data byte is nonzero (a value + of 1 is used by this driver), data will not be transmitted to a device + which is not asserting 'CTS'. + */ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCT_U232_SET_UNKNOWN1_REQUEST, @@ -252,14 +270,19 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) err("Sending USB device request code %d failed (error = %d)", MCT_U232_SET_UNKNOWN1_REQUEST, rc); + if (port && C_CRTSCTS(port->tty)) { + cts_enable_byte = 1; + } + + dbg("set_baud_rate: send second control message, data = %02X", cts_enable_byte); rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - MCT_U232_SET_UNKNOWN2_REQUEST, + MCT_U232_SET_CTS_REQUEST, MCT_U232_SET_REQUEST_TYPE, - 0, 0, &zero_byte, MCT_U232_SET_UNKNOWN2_SIZE, + 0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE, WDR_TIMEOUT); if (rc < 0) - err("Sending USB device request code %d failed (error = %d)", - MCT_U232_SET_UNKNOWN2_REQUEST, rc); + err("Sending USB device request code %d failed (error = %d)", + MCT_U232_SET_CTS_REQUEST, rc); return rc; } /* mct_u232_set_baud_rate */ @@ -458,8 +481,25 @@ error: static void mct_u232_close (struct usb_serial_port *port, struct file *filp) { + unsigned int c_cflag; + unsigned long flags; + unsigned int control_state; + struct mct_u232_private *priv = usb_get_serial_port_data(port); dbg("%s port %d", __FUNCTION__, port->number); + if (port->tty) { + c_cflag = port->tty->termios->c_cflag; + if (c_cflag & HUPCL) { + /* drop DTR and RTS */ + spin_lock_irqsave(&priv->lock, flags); + priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + mct_u232_set_modem_ctrl(port->serial, control_state); + } + } + + if (port->serial->dev) { /* shutdown our urbs */ usb_kill_urb(port->write_urb); @@ -476,10 +516,11 @@ static void mct_u232_read_int_callback (struct urb *urb) struct usb_serial *serial = port->serial; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; - int status; + int retval; + int status = urb->status; unsigned long flags; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -487,10 +528,12 @@ static void mct_u232_read_int_callback (struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, status); goto exit; } @@ -554,10 +597,10 @@ static void mct_u232_read_int_callback (struct urb *urb) #endif spin_unlock_irqrestore(&priv->lock, flags); exit: - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status) + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) err ("%s - usb_submit_urb failed with result %d", - __FUNCTION__, status); + __FUNCTION__, retval); } /* mct_u232_read_int_callback */ static void mct_u232_set_termios (struct usb_serial_port *port, @@ -565,11 +608,10 @@ static void mct_u232_set_termios (struct usb_serial_port *port, { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); - unsigned int iflag = port->tty->termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; unsigned long flags; - unsigned int control_state, new_state; + unsigned int control_state; unsigned char last_lcr; /* get a local copy of the current port settings */ @@ -585,18 +627,14 @@ static void mct_u232_set_termios (struct usb_serial_port *port, * Premature optimization is the root of all evil. */ - /* reassert DTR and (maybe) RTS on transition from B0 */ + /* reassert DTR and RTS on transition from B0 */ if ((old_cflag & CBAUD) == B0) { dbg("%s: baud was B0", __FUNCTION__); - control_state |= TIOCM_DTR; - /* don't set RTS if using hardware flow control */ - if (!(old_cflag & CRTSCTS)) { - control_state |= TIOCM_RTS; - } + control_state |= TIOCM_DTR | TIOCM_RTS; mct_u232_set_modem_ctrl(serial, control_state); } - mct_u232_set_baud_rate(serial, cflag & CBAUD); + mct_u232_set_baud_rate(serial, port, cflag & CBAUD); if ((cflag & CBAUD) == B0 ) { dbg("%s: baud is B0", __FUNCTION__); @@ -638,21 +676,6 @@ static void mct_u232_set_termios (struct usb_serial_port *port, mct_u232_set_line_ctrl(serial, last_lcr); - /* - * Set flow control: well, I do not really now how to handle DTR/RTS. - * Just do what we have seen with SniffUSB on Win98. - */ - /* Drop DTR/RTS if no flow control otherwise assert */ - new_state = control_state; - if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) - new_state |= TIOCM_DTR | TIOCM_RTS; - else - new_state &= ~(TIOCM_DTR | TIOCM_RTS); - if (new_state != control_state) { - mct_u232_set_modem_ctrl(serial, new_state); - control_state = new_state; - } - /* save off the modified port settings */ spin_lock_irqsave(&priv->lock, flags); priv->control_state = control_state; @@ -747,6 +770,50 @@ static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file, return 0; } /* mct_u232_ioctl */ +static void mct_u232_throttle (struct usb_serial_port *port) +{ + struct mct_u232_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int control_state; + struct tty_struct *tty; + + tty = port->tty; + dbg("%s - port %d", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + priv->rx_flags |= THROTTLED; + if (C_CRTSCTS(tty)) { + priv->control_state &= ~TIOCM_RTS; + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + (void) mct_u232_set_modem_ctrl(port->serial, control_state); + } else { + spin_unlock_irqrestore(&priv->lock, flags); + } +} + + +static void mct_u232_unthrottle (struct usb_serial_port *port) +{ + struct mct_u232_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int control_state; + struct tty_struct *tty; + + dbg("%s - port %d", __FUNCTION__, port->number); + + tty = port->tty; + spin_lock_irqsave(&priv->lock, flags); + if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { + priv->rx_flags &= ~THROTTLED; + priv->control_state |= TIOCM_RTS; + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + (void) mct_u232_set_modem_ctrl(port->serial, control_state); + } else { + spin_unlock_irqrestore(&priv->lock, flags); + } +} static int __init mct_u232_init (void) { diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h index 73dd0d9..a61bac8 100644 --- a/drivers/usb/serial/mct_u232.h +++ b/drivers/usb/serial/mct_u232.h @@ -63,14 +63,15 @@ #define MCT_U232_SET_UNKNOWN1_REQUEST 11 /* Unknown functionality */ #define MCT_U232_SET_UNKNOWN1_SIZE 1 -/* This USB device request code is not well understood. It is transmitted by - the MCT-supplied Windows driver whenever the baud rate changes. +/* This USB device request code appears to control whether CTS is required + during transmission. - Without this USB device request, the USB/RS-232 adapter will not write to - RS-232 devices which do not assert the 'CTS' signal. + Sending a zero byte allows data transmission to a device which is not + asserting CTS. Sending a '1' byte will cause transmission to be deferred + until the device asserts CTS. */ -#define MCT_U232_SET_UNKNOWN2_REQUEST 12 /* Unknown functionality */ -#define MCT_U232_SET_UNKNOWN2_SIZE 1 +#define MCT_U232_SET_CTS_REQUEST 12 +#define MCT_U232_SET_CTS_SIZE 1 /* * Baud rate (divisor) @@ -439,7 +440,7 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int value); * which says "U232-P9" ;-) * * The circuit board inside the adaptor contains a Philips PDIUSBD12 - * USB endpoint chip and a Phillips P87C52UBAA microcontroller with + * USB endpoint chip and a Philips P87C52UBAA microcontroller with * embedded UART. Exhaustive documentation for these is available at: * * http://www.semiconductors.philips.com/pip/p87c52ubaa diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index b563e2a..231b584 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -9,9 +9,9 @@ * the Free Software Foundation, version 2 of the License. * * Developed by: - * VijayaKumar.G.N. <vijaykumar@aspirecom.net> - * AjayKumar <ajay@aspirecom.net> - * Gurudeva.N. <gurudev@aspirecom.net> + * Vijaya Kumar <vijaykumar.gn@gmail.com> + * Ajay Kumar <naanuajay@yahoo.com> + * Gurudeva <ngurudeva@yahoo.com> * * Cleaned up from the original by: * Greg Kroah-Hartman <gregkh@suse.de> @@ -103,6 +103,7 @@ static void mos7720_interrupt_callback(struct urb *urb) { int result; int length; + int status = urb->status; __u8 *data; __u8 sp1; __u8 sp2; @@ -114,7 +115,7 @@ static void mos7720_interrupt_callback(struct urb *urb) return; } - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -123,11 +124,11 @@ static void mos7720_interrupt_callback(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __FUNCTION__, - urb->status); + status); return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, - urb->status); + status); goto exit; } @@ -198,14 +199,15 @@ exit: */ static void mos7720_bulk_in_callback(struct urb *urb) { - int status; + int retval; unsigned char *data ; struct usb_serial_port *port; struct moschip_port *mos7720_port; struct tty_struct *tty; + int status = urb->status; - if (urb->status) { - dbg("nonzero read bulk status received: %d",urb->status); + if (status) { + dbg("nonzero read bulk status received: %d", status); return; } @@ -236,10 +238,10 @@ static void mos7720_bulk_in_callback(struct urb *urb) if (port->read_urb->status != -EINPROGRESS) { port->read_urb->dev = port->serial->dev; - status = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (status) - dbg("usb_submit_urb(read bulk) failed, status = %d", - status); + retval = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (retval) + dbg("usb_submit_urb(read bulk) failed, retval = %d", + retval); } } @@ -252,9 +254,10 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) { struct moschip_port *mos7720_port; struct tty_struct *tty; + int status = urb->status; - if (urb->status) { - dbg("nonzero write bulk status received:%d", urb->status); + if (status) { + dbg("nonzero write bulk status received:%d", status); return; } @@ -1235,16 +1238,6 @@ static void mos7720_set_termios(struct usb_serial_port *port, return; } - /* check that they really want us to change something */ - if (old_termios) { - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(tty->termios->c_iflag) == - RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("Nothing to change"); - return; - } - } - dbg("%s - clfag %08x iflag %08x", __FUNCTION__, tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag)); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 36620c6..37f41f5 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -434,6 +434,7 @@ static void mos7840_control_callback(struct urb *urb) struct moschip_port *mos7840_port; __u8 regval = 0x0; int result = 0; + int status = urb->status; if (!urb) { dbg("%s", "Invalid Pointer !!!!:\n"); @@ -442,7 +443,7 @@ static void mos7840_control_callback(struct urb *urb) mos7840_port = (struct moschip_port *)urb->context; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -451,11 +452,11 @@ static void mos7840_control_callback(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __FUNCTION__, - urb->status); + status); return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, - urb->status); + status); goto exit; } @@ -521,6 +522,7 @@ static void mos7840_interrupt_callback(struct urb *urb) __u8 sp[5], st; int i, rv = 0; __u16 wval, wreg = 0; + int status = urb->status; dbg("%s", " : Entering\n"); if (!urb) { @@ -528,7 +530,7 @@ static void mos7840_interrupt_callback(struct urb *urb) return; } - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -537,11 +539,11 @@ static void mos7840_interrupt_callback(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __FUNCTION__, - urb->status); + status); return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, - urb->status); + status); goto exit; } @@ -666,20 +668,21 @@ static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port, static void mos7840_bulk_in_callback(struct urb *urb) { - int status; + int retval; unsigned char *data; struct usb_serial *serial; struct usb_serial_port *port; struct moschip_port *mos7840_port; struct tty_struct *tty; + int status = urb->status; if (!urb) { dbg("%s", "Invalid Pointer !!!!:\n"); return; } - if (urb->status) { - dbg("nonzero read bulk status received: %d", urb->status); + if (status) { + dbg("nonzero read bulk status received: %d", status); return; } @@ -729,11 +732,11 @@ static void mos7840_bulk_in_callback(struct urb *urb) mos7840_port->read_urb->dev = serial->dev; - status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); + retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); - if (status) { - dbg(" usb_submit_urb(read bulk) failed, status = %d", - status); + if (retval) { + dbg(" usb_submit_urb(read bulk) failed, retval = %d", + retval); } } @@ -747,6 +750,7 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) { struct moschip_port *mos7840_port; struct tty_struct *tty; + int status = urb->status; int i; if (!urb) { @@ -764,8 +768,8 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) } spin_unlock(&mos7840_port->pool_lock); - if (urb->status) { - dbg("nonzero write bulk status received:%d\n", urb->status); + if (status) { + dbg("nonzero write bulk status received:%d\n", status); return; } @@ -2185,16 +2189,6 @@ static void mos7840_set_termios(struct usb_serial_port *port, return; } - /* check that they really want us to change something */ - if (old_termios) { - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(tty->termios->c_iflag) == - RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("%s\n", "Nothing to change"); - return; - } - } - dbg("%s - clfag %08x iflag %08x", __FUNCTION__, tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag)); @@ -2254,30 +2248,6 @@ static int mos7840_get_lsr_info(struct moschip_port *mos7840_port, } /***************************************************************************** - * mos7840_get_bytes_avail - get number of bytes available - * - * Purpose: Let user call ioctl to get the count of number of bytes available. - *****************************************************************************/ - -static int mos7840_get_bytes_avail(struct moschip_port *mos7840_port, - unsigned int __user *value) -{ - unsigned int result = 0; - struct tty_struct *tty = mos7840_port->port->tty; - - if (!tty) - return -ENOIOCTLCMD; - - result = tty->read_cnt; - - dbg("%s(%d) = %d", __FUNCTION__, mos7840_port->port->number, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - - return -ENOIOCTLCMD; -} - -/***************************************************************************** * mos7840_set_modem_info * function to set modem info *****************************************************************************/ @@ -2425,8 +2395,6 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, struct async_icount cprev; struct serial_icounter_struct icount; int mosret = 0; - int retval; - struct tty_ldisc *ld; if (mos7840_port_paranoia_check(port, __FUNCTION__)) { dbg("%s", "Invalid port \n"); @@ -2445,42 +2413,6 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, switch (cmd) { /* return number of bytes available */ - case TIOCINQ: - dbg("%s (%d) TIOCINQ", __FUNCTION__, port->number); - return mos7840_get_bytes_avail(mos7840_port, argp); - - case TIOCOUTQ: - dbg("%s (%d) TIOCOUTQ", __FUNCTION__, port->number); - return put_user(tty->driver->chars_in_buffer ? - tty->driver->chars_in_buffer(tty) : 0, - (int __user *)arg); - - case TCFLSH: - retval = tty_check_change(tty); - if (retval) - return retval; - - ld = tty_ldisc_ref(tty); - switch (arg) { - case TCIFLUSH: - if (ld && ld->flush_buffer) - ld->flush_buffer(tty); - break; - case TCIOFLUSH: - if (ld && ld->flush_buffer) - ld->flush_buffer(tty); - /* fall through */ - case TCOFLUSH: - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - break; - default: - tty_ldisc_deref(ld); - return -EINVAL; - } - tty_ldisc_deref(ld); - return 0; - case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __FUNCTION__, port->number); return mos7840_get_lsr_info(mos7840_port, argp); diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 9070111..7f337c9 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -37,9 +37,10 @@ static void navman_read_int_callback(struct urb *urb) struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; struct tty_struct *tty; + int status = urb->status; int result; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -48,11 +49,11 @@ static void navman_read_int_callback(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); return; default: dbg("%s - nonzero urb status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); goto exit; } diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 00afc17..ee94d96 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -1,10 +1,9 @@ /* * USB ZyXEL omni.net LCD PLUS driver * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. * * See Documentation/usb/usb-serial.txt for more information on using this driver * @@ -201,14 +200,15 @@ static void omninet_read_bulk_callback (struct urb *urb) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; unsigned char *data = urb->transfer_buffer; struct omninet_header *header = (struct omninet_header *) &data[0]; - + int status = urb->status; int i; int result; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); return; } @@ -312,12 +312,14 @@ static void omninet_write_bulk_callback (struct urb *urb) { /* struct omninet_header *header = (struct omninet_header *) urb->transfer_buffer; */ struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + int status = urb->status; dbg("%s - port %0x\n", __FUNCTION__, port->number); port->write_urb_busy = 0; - if (urb->status) { - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); return; } diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5d3999e..84c12b5 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -38,6 +38,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/module.h> +#include <linux/bitops.h> #include <linux/usb.h> #include <linux/usb/serial.h> @@ -240,6 +241,7 @@ struct option_port_private { /* Output endpoints and buffer for this port */ struct urb *out_urbs[N_OUT_URB]; char out_buffer[N_OUT_URB][OUT_BUFLEN]; + unsigned long out_busy; /* Bit vector of URBs in use */ /* Settings for the port */ int rts_state; /* Handshaking pins (outputs) */ @@ -370,7 +372,7 @@ static int option_write(struct usb_serial_port *port, todo = OUT_BUFLEN; this_urb = portdata->out_urbs[i]; - if (this_urb->status == -EINPROGRESS) { + if (test_and_set_bit(i, &portdata->out_busy)) { if (time_before(jiffies, portdata->tx_start_time[i] + 10 * HZ)) continue; @@ -394,6 +396,7 @@ static int option_write(struct usb_serial_port *port, dbg("usb_submit_urb %p (write bulk) failed " "(%d, has %d)", this_urb, err, this_urb->status); + clear_bit(i, &portdata->out_busy); continue; } portdata->tx_start_time[i] = jiffies; @@ -413,15 +416,16 @@ static void option_indat_callback(struct urb *urb) struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; + int status = urb->status; dbg("%s: %p", __FUNCTION__, urb); endpoint = usb_pipeendpoint(urb->pipe); port = (struct usb_serial_port *) urb->context; - if (urb->status) { + if (status) { dbg("%s: nonzero status: %d on endpoint %02x.", - __FUNCTION__, urb->status, endpoint); + __FUNCTION__, status, endpoint); } else { tty = port->tty; if (urb->actual_length) { @@ -433,7 +437,7 @@ static void option_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && urb->status != -ESHUTDOWN) { + if (port->open_count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) printk(KERN_ERR "%s: resubmit read urb failed. " @@ -446,17 +450,29 @@ static void option_indat_callback(struct urb *urb) static void option_outdat_callback(struct urb *urb) { struct usb_serial_port *port; + struct option_port_private *portdata; + int i; dbg("%s", __FUNCTION__); port = (struct usb_serial_port *) urb->context; usb_serial_port_softint(port); + + portdata = usb_get_serial_port_data(port); + for (i = 0; i < N_OUT_URB; ++i) { + if (portdata->out_urbs[i] == urb) { + smp_mb__before_clear_bit(); + clear_bit(i, &portdata->out_busy); + break; + } + } } static void option_instat_callback(struct urb *urb) { int err; + int status = urb->status; struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct option_port_private *portdata = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -464,7 +480,7 @@ static void option_instat_callback(struct urb *urb) dbg("%s", __FUNCTION__); dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata); - if (urb->status == 0) { + if (status == 0) { struct usb_ctrlrequest *req_pkt = (struct usb_ctrlrequest *)urb->transfer_buffer; @@ -495,10 +511,10 @@ static void option_instat_callback(struct urb *urb) req_pkt->bRequestType,req_pkt->bRequest); } } else - dbg("%s: error %d", __FUNCTION__, urb->status); + dbg("%s: error %d", __FUNCTION__, status); /* Resubmit urb so we continue receiving IRQ data */ - if (urb->status != -ESHUTDOWN) { + if (status != -ESHUTDOWN) { urb->dev = serial->dev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) @@ -518,7 +534,7 @@ static int option_write_room(struct usb_serial_port *port) for (i=0; i < N_OUT_URB; i++) { this_urb = portdata->out_urbs[i]; - if (this_urb && this_urb->status != -EINPROGRESS) + if (this_urb && !test_bit(i, &portdata->out_busy)) data_len += OUT_BUFLEN; } @@ -537,7 +553,7 @@ static int option_chars_in_buffer(struct usb_serial_port *port) for (i=0; i < N_OUT_URB; i++) { this_urb = portdata->out_urbs[i]; - if (this_urb && this_urb->status == -EINPROGRESS) + if (this_urb && test_bit(i, &portdata->out_busy)) data_len += this_urb->transfer_buffer_length; } dbg("%s: %d", __FUNCTION__, data_len); diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c new file mode 100644 index 0000000..d7db71e --- /dev/null +++ b/drivers/usb/serial/oti6858.c @@ -0,0 +1,1342 @@ +/* + * Ours Technology Inc. OTi-6858 USB to serial adapter driver. + * + * Copyleft (C) 2007 Kees Lemmens (adapted for kernel 2.6.20) + * Copyright (C) 2006 Tomasz Michal Lukaszewski (FIXME: add e-mail) + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2003 IBM Corp. + * + * Many thanks to the authors of pl2303 driver: all functions in this file + * are heavily based on pl2303 code, buffering code is a 1-to-1 copy. + * + * Warning! You use this driver on your own risk! The only official + * description of this device I have is datasheet from manufacturer, + * and it doesn't contain almost any information needed to write a driver. + * Almost all knowlegde used while writing this driver was gathered by: + * - analyzing traffic between device and the M$ Windows 2000 driver, + * - trying different bit combinations and checking pin states + * with a voltmeter, + * - receiving malformed frames and producing buffer overflows + * to learn how errors are reported, + * So, THIS CODE CAN DESTROY OTi-6858 AND ANY OTHER DEVICES, THAT ARE + * CONNECTED TO IT! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * TODO: + * - implement correct flushing for ioctls and oti6858_close() + * - check how errors (rx overflow, parity error, framing error) are reported + * - implement oti6858_break_ctl() + * - implement more ioctls + * - test/implement flow control + * - allow setting custom baud rates + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/spinlock.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> +#include <asm/uaccess.h> +#include "oti6858.h" + +#define OTI6858_DESCRIPTION \ + "Ours Technology Inc. OTi-6858 USB to serial adapter driver" +#define OTI6858_AUTHOR "Tomasz Michal Lukaszewski <FIXME@FIXME>" +#define OTI6858_VERSION "0.1" + +static struct usb_device_id id_table [] = { + { USB_DEVICE(OTI6858_VENDOR_ID, OTI6858_PRODUCT_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_driver oti6858_driver = { + .name = "oti6858", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = id_table, + .no_dynamic_id = 1, +}; + +static int debug; + + +/* buffering code, copied from pl2303 driver */ +#define PL2303_BUF_SIZE 1024 +#define PL2303_TMP_BUF_SIZE 1024 + +struct pl2303_buf { + unsigned int buf_size; + char *buf_buf; + char *buf_get; + char *buf_put; +}; + +/* requests */ +#define OTI6858_REQ_GET_STATUS (USB_DIR_IN | USB_TYPE_VENDOR | 0x00) +#define OTI6858_REQ_T_GET_STATUS 0x01 + +#define OTI6858_REQ_SET_LINE (USB_DIR_OUT | USB_TYPE_VENDOR | 0x00) +#define OTI6858_REQ_T_SET_LINE 0x00 + +#define OTI6858_REQ_CHECK_TXBUFF (USB_DIR_IN | USB_TYPE_VENDOR | 0x01) +#define OTI6858_REQ_T_CHECK_TXBUFF 0x00 + +/* format of the control packet */ +struct oti6858_control_pkt { + u16 divisor; /* baud rate = 96000000 / (16 * divisor), LE */ +#define OTI6858_MAX_BAUD_RATE 3000000 + u8 frame_fmt; +#define FMT_STOP_BITS_MASK 0xc0 +#define FMT_STOP_BITS_1 0x00 +#define FMT_STOP_BITS_2 0x40 /* 1.5 stop bits if FMT_DATA_BITS_5 */ +#define FMT_PARITY_MASK 0x38 +#define FMT_PARITY_NONE 0x00 +#define FMT_PARITY_ODD 0x08 +#define FMT_PARITY_EVEN 0x18 +#define FMT_PARITY_MARK 0x28 +#define FMT_PARITY_SPACE 0x38 +#define FMT_DATA_BITS_MASK 0x03 +#define FMT_DATA_BITS_5 0x00 +#define FMT_DATA_BITS_6 0x01 +#define FMT_DATA_BITS_7 0x02 +#define FMT_DATA_BITS_8 0x03 + u8 something; /* always equals 0x43 */ + u8 control; /* settings of flow control lines */ +#define CONTROL_MASK 0x0c +#define CONTROL_DTR_HIGH 0x08 +#define CONTROL_RTS_HIGH 0x04 + u8 tx_status; +#define TX_BUFFER_EMPTIED 0x09 + u8 pin_state; +#define PIN_MASK 0x3f +#define PIN_RTS 0x20 /* output pin */ +#define PIN_CTS 0x10 /* input pin, active low */ +#define PIN_DSR 0x08 /* input pin, active low */ +#define PIN_DTR 0x04 /* output pin */ +#define PIN_RI 0x02 /* input pin, active low */ +#define PIN_DCD 0x01 /* input pin, active low */ + u8 rx_bytes_avail; /* number of bytes in rx buffer */; +}; + +#define OTI6858_CTRL_PKT_SIZE sizeof(struct oti6858_control_pkt) +#define OTI6858_CTRL_EQUALS_PENDING(a, priv) \ + ( ((a)->divisor == (priv)->pending_setup.divisor) \ + && ((a)->control == (priv)->pending_setup.control) \ + && ((a)->frame_fmt == (priv)->pending_setup.frame_fmt) ) + +/* function prototypes */ +static int oti6858_open(struct usb_serial_port *port, struct file *filp); +static void oti6858_close(struct usb_serial_port *port, struct file *filp); +static void oti6858_set_termios(struct usb_serial_port *port, + struct ktermios *old); +static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg); +static void oti6858_read_int_callback(struct urb *urb); +static void oti6858_read_bulk_callback(struct urb *urb); +static void oti6858_write_bulk_callback(struct urb *urb); +static int oti6858_write(struct usb_serial_port *port, + const unsigned char *buf, int count); +static int oti6858_write_room(struct usb_serial_port *port); +static void oti6858_break_ctl(struct usb_serial_port *port, int break_state); +static int oti6858_chars_in_buffer(struct usb_serial_port *port); +static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file); +static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear); +static int oti6858_startup(struct usb_serial *serial); +static void oti6858_shutdown(struct usb_serial *serial); + +/* functions operating on buffers */ +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size); +static void pl2303_buf_free(struct pl2303_buf *pb); +static void pl2303_buf_clear(struct pl2303_buf *pb); +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb); +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb); +static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, + unsigned int count); +static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count); + + +/* device info */ +static struct usb_serial_driver oti6858_device = { + .driver = { + .owner = THIS_MODULE, + .name = "oti6858", + }, + .id_table = id_table, + .num_interrupt_in = 1, + .num_bulk_in = 1, + .num_bulk_out = 1, + .num_ports = 1, + .open = oti6858_open, + .close = oti6858_close, + .write = oti6858_write, + .ioctl = oti6858_ioctl, + .break_ctl = oti6858_break_ctl, + .set_termios = oti6858_set_termios, + .tiocmget = oti6858_tiocmget, + .tiocmset = oti6858_tiocmset, + .read_bulk_callback = oti6858_read_bulk_callback, + .read_int_callback = oti6858_read_int_callback, + .write_bulk_callback = oti6858_write_bulk_callback, + .write_room = oti6858_write_room, + .chars_in_buffer = oti6858_chars_in_buffer, + .attach = oti6858_startup, + .shutdown = oti6858_shutdown, +}; + +struct oti6858_private { + spinlock_t lock; + + struct pl2303_buf *buf; + struct oti6858_control_pkt status; + + struct { + u8 read_urb_in_use; + u8 write_urb_in_use; + u8 termios_initialized; + } flags; + struct delayed_work delayed_write_work; + + struct { + u16 divisor; + u8 frame_fmt; + u8 control; + } pending_setup; + u8 transient; + u8 setup_done; + struct delayed_work delayed_setup_work; + + wait_queue_head_t intr_wait; + struct usb_serial_port *port; /* USB port with which associated */ +}; + +#undef dbg +/* #define dbg(format, arg...) printk(KERN_INFO "%s: " format "\n", __FILE__, ## arg) */ +#define dbg(format, arg...) printk(KERN_INFO "" format "\n", ## arg) + +static void setup_line(struct work_struct *work) +{ + struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_setup_work.work); + struct usb_serial_port *port = priv->port; + struct oti6858_control_pkt *new_setup; + unsigned long flags; + int result; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + if ((new_setup = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) { + dev_err(&port->dev, "%s(): out of memory!\n", __FUNCTION__); + /* we will try again */ + schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2)); + return; + } + + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + OTI6858_REQ_T_GET_STATUS, + OTI6858_REQ_GET_STATUS, + 0, 0, + new_setup, OTI6858_CTRL_PKT_SIZE, + 100); + + if (result != OTI6858_CTRL_PKT_SIZE) { + dev_err(&port->dev, "%s(): error reading status", __FUNCTION__); + kfree(new_setup); + /* we will try again */ + schedule_delayed_work(&priv->delayed_setup_work, msecs_to_jiffies(2)); + return; + } + + spin_lock_irqsave(&priv->lock, flags); + if (!OTI6858_CTRL_EQUALS_PENDING(new_setup, priv)) { + new_setup->divisor = priv->pending_setup.divisor; + new_setup->control = priv->pending_setup.control; + new_setup->frame_fmt = priv->pending_setup.frame_fmt; + + spin_unlock_irqrestore(&priv->lock, flags); + result = usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + OTI6858_REQ_T_SET_LINE, + OTI6858_REQ_SET_LINE, + 0, 0, + new_setup, OTI6858_CTRL_PKT_SIZE, + 100); + } else { + spin_unlock_irqrestore(&priv->lock, flags); + result = 0; + } + kfree(new_setup); + + spin_lock_irqsave(&priv->lock, flags); + if (result != OTI6858_CTRL_PKT_SIZE) + priv->transient = 0; + priv->setup_done = 1; + spin_unlock_irqrestore(&priv->lock, flags); + + dbg("%s(): submitting interrupt urb", __FUNCTION__); + port->interrupt_in_urb->dev = port->serial->dev; + result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + if (result != 0) { + dev_err(&port->dev, "%s(): usb_submit_urb() failed" + " with error %d\n", __FUNCTION__, result); + } +} + +void send_data(struct work_struct *work) +{ + struct oti6858_private *priv = container_of(work, struct oti6858_private, delayed_write_work.work); + struct usb_serial_port *port = priv->port; + int count = 0, result; + unsigned long flags; + unsigned char allow; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + if (priv->flags.write_urb_in_use) { + spin_unlock_irqrestore(&priv->lock, flags); + schedule_delayed_work(&priv->delayed_write_work, msecs_to_jiffies(2)); + return; + } + priv->flags.write_urb_in_use = 1; + + count = pl2303_buf_data_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + if (count > port->bulk_out_size) + count = port->bulk_out_size; + + if (count != 0) { + result = usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + OTI6858_REQ_T_CHECK_TXBUFF, + OTI6858_REQ_CHECK_TXBUFF, + count, 0, &allow, 1, 100); + if (result != 1 || allow != 0) + count = 0; + } + + if (count == 0) { + priv->flags.write_urb_in_use = 0; + + dbg("%s(): submitting interrupt urb", __FUNCTION__); + port->interrupt_in_urb->dev = port->serial->dev; + result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + if (result != 0) { + dev_err(&port->dev, "%s(): usb_submit_urb() failed" + " with error %d\n", __FUNCTION__, result); + } + return; + } + + spin_lock_irqsave(&priv->lock, flags); + pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer, count); + spin_unlock_irqrestore(&priv->lock, flags); + + port->write_urb->transfer_buffer_length = count; + port->write_urb->dev = port->serial->dev; + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (result != 0) { + dev_err(&port->dev, "%s(): usb_submit_urb() failed" + " with error %d\n", __FUNCTION__, result); + priv->flags.write_urb_in_use = 0; + } + + usb_serial_port_softint(port); +} + +static int oti6858_startup(struct usb_serial *serial) +{ + struct usb_serial_port *port = serial->port[0]; + struct oti6858_private *priv; + int i; + + for (i = 0; i < serial->num_ports; ++i) { + priv = kzalloc(sizeof(struct oti6858_private), GFP_KERNEL); + if (!priv) + break; + priv->buf = pl2303_buf_alloc(PL2303_BUF_SIZE); + if (priv->buf == NULL) { + kfree(priv); + break; + } + + spin_lock_init(&priv->lock); + init_waitqueue_head(&priv->intr_wait); +// INIT_WORK(&priv->setup_work, setup_line, serial->port[i]); +// INIT_WORK(&priv->write_work, send_data, serial->port[i]); + priv->port = port; + INIT_DELAYED_WORK(&priv->delayed_setup_work, setup_line); + INIT_DELAYED_WORK(&priv->delayed_write_work, send_data); + + usb_set_serial_port_data(serial->port[i], priv); + } + if (i == serial->num_ports) + return 0; + + for (--i; i >= 0; --i) { + priv = usb_get_serial_port_data(serial->port[i]); + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(serial->port[i], NULL); + } + return -ENOMEM; +} + +static int oti6858_write(struct usb_serial_port *port, + const unsigned char *buf, int count) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + + dbg("%s(port = %d, count = %d)", __FUNCTION__, port->number, count); + + if (!count) + return count; + + spin_lock_irqsave(&priv->lock, flags); + count = pl2303_buf_put(priv->buf, buf, count); + spin_unlock_irqrestore(&priv->lock, flags); + + return count; +} + +static int oti6858_write_room(struct usb_serial_port *port) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + int room = 0; + unsigned long flags; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + room = pl2303_buf_space_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + return room; +} + +static int oti6858_chars_in_buffer(struct usb_serial_port *port) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + int chars = 0; + unsigned long flags; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + spin_lock_irqsave(&priv->lock, flags); + chars = pl2303_buf_data_avail(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + return chars; +} + +static void oti6858_set_termios(struct usb_serial_port *port, + struct ktermios *old_termios) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int cflag; + u8 frame_fmt, control; + u16 divisor; + int br; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + if ((!port->tty) || (!port->tty->termios)) { + dbg("%s(): no tty structures", __FUNCTION__); + return; + } + + spin_lock_irqsave(&priv->lock, flags); + if (!priv->flags.termios_initialized) { + *(port->tty->termios) = tty_std_termios; + port->tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + priv->flags.termios_initialized = 1; + } + spin_unlock_irqrestore(&priv->lock, flags); + + cflag = port->tty->termios->c_cflag; + + spin_lock_irqsave(&priv->lock, flags); + divisor = priv->pending_setup.divisor; + frame_fmt = priv->pending_setup.frame_fmt; + control = priv->pending_setup.control; + spin_unlock_irqrestore(&priv->lock, flags); + + frame_fmt &= ~FMT_DATA_BITS_MASK; + switch (cflag & CSIZE) { + case CS5: + frame_fmt |= FMT_DATA_BITS_5; + break; + case CS6: + frame_fmt |= FMT_DATA_BITS_6; + break; + case CS7: + frame_fmt |= FMT_DATA_BITS_7; + break; + default: + case CS8: + frame_fmt |= FMT_DATA_BITS_8; + break; + } + + /* manufacturer claims that this device can work with baud rates + * up to 3 Mbps; I've tested it only on 115200 bps, so I can't + * guarantee that any other baud rate will work (especially + * the higher ones) + */ + br = tty_get_baud_rate(port->tty); + if (br == 0) { + divisor = 0; + } else if (br <= OTI6858_MAX_BAUD_RATE) { + int real_br; + + divisor = (96000000 + 8 * br) / (16 * br); + real_br = 96000000 / (16 * divisor); + if ((((real_br - br) * 100 + br - 1) / br) > 2) { + dbg("%s(): baud rate %d is invalid", __FUNCTION__, br); + return; + } + divisor = cpu_to_le16(divisor); + } else { + dbg("%s(): baud rate %d is too high", __FUNCTION__, br); + return; + } + + frame_fmt &= ~FMT_STOP_BITS_MASK; + if ((cflag & CSTOPB) != 0) { + frame_fmt |= FMT_STOP_BITS_2; + } else { + frame_fmt |= FMT_STOP_BITS_1; + } + + frame_fmt &= ~FMT_PARITY_MASK; + if ((cflag & PARENB) != 0) { + if ((cflag & PARODD) != 0) { + frame_fmt |= FMT_PARITY_ODD; + } else { + frame_fmt |= FMT_PARITY_EVEN; + } + } else { + frame_fmt |= FMT_PARITY_NONE; + } + + control &= ~CONTROL_MASK; + if ((cflag & CRTSCTS) != 0) + control |= (CONTROL_DTR_HIGH | CONTROL_RTS_HIGH); + + /* change control lines if we are switching to or from B0 */ + /* FIXME: + spin_lock_irqsave(&priv->lock, flags); + control = priv->line_control; + if ((cflag & CBAUD) == B0) + priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS); + else + priv->line_control |= (CONTROL_DTR | CONTROL_RTS); + if (control != priv->line_control) { + control = priv->line_control; + spin_unlock_irqrestore(&priv->lock, flags); + set_control_lines(serial->dev, control); + } else { + spin_unlock_irqrestore(&priv->lock, flags); + } + */ + + spin_lock_irqsave(&priv->lock, flags); + if (divisor != priv->pending_setup.divisor + || control != priv->pending_setup.control + || frame_fmt != priv->pending_setup.frame_fmt) { + priv->pending_setup.divisor = divisor; + priv->pending_setup.control = control; + priv->pending_setup.frame_fmt = frame_fmt; + } + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int oti6858_open(struct usb_serial_port *port, struct file *filp) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + struct ktermios tmp_termios; + struct usb_serial *serial = port->serial; + struct oti6858_control_pkt *buf; + unsigned long flags; + int result; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + usb_clear_halt(serial->dev, port->write_urb->pipe); + usb_clear_halt(serial->dev, port->read_urb->pipe); + + if (port->open_count != 1) + return 0; + + if ((buf = kmalloc(OTI6858_CTRL_PKT_SIZE, GFP_KERNEL)) == NULL) { + dev_err(&port->dev, "%s(): out of memory!\n", __FUNCTION__); + return -ENOMEM; + } + + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + OTI6858_REQ_T_GET_STATUS, + OTI6858_REQ_GET_STATUS, + 0, 0, + buf, OTI6858_CTRL_PKT_SIZE, + 100); + if (result != OTI6858_CTRL_PKT_SIZE) { + /* assume default (after power-on reset) values */ + buf->divisor = cpu_to_le16(0x009c); /* 38400 bps */ + buf->frame_fmt = 0x03; /* 8N1 */ + buf->something = 0x43; + buf->control = 0x4c; /* DTR, RTS */ + buf->tx_status = 0x00; + buf->pin_state = 0x5b; /* RTS, CTS, DSR, DTR, RI, DCD */ + buf->rx_bytes_avail = 0x00; + } + + spin_lock_irqsave(&priv->lock, flags); + memcpy(&priv->status, buf, OTI6858_CTRL_PKT_SIZE); + priv->pending_setup.divisor = buf->divisor; + priv->pending_setup.frame_fmt = buf->frame_fmt; + priv->pending_setup.control = buf->control; + spin_unlock_irqrestore(&priv->lock, flags); + kfree(buf); + + dbg("%s(): submitting interrupt urb", __FUNCTION__); + port->interrupt_in_urb->dev = serial->dev; + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (result != 0) { + dev_err(&port->dev, "%s(): usb_submit_urb() failed" + " with error %d\n", __FUNCTION__, result); + oti6858_close(port, NULL); + return -EPROTO; + } + + /* setup termios */ + if (port->tty) + oti6858_set_termios(port, &tmp_termios); + + return 0; +} + +static void oti6858_close(struct usb_serial_port *port, struct file *filp) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + long timeout; + wait_queue_t wait; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + /* wait for data to drain from the buffer */ + spin_lock_irqsave(&priv->lock, flags); + timeout = 30 * HZ; /* PL2303_CLOSING_WAIT */ + init_waitqueue_entry(&wait, current); + add_wait_queue(&port->tty->write_wait, &wait); + dbg("%s(): entering wait loop", __FUNCTION__); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (pl2303_buf_data_avail(priv->buf) == 0 + || timeout == 0 || signal_pending(current) + || !usb_get_intfdata(port->serial->interface)) /* disconnect */ + break; + spin_unlock_irqrestore(&priv->lock, flags); + timeout = schedule_timeout(timeout); + spin_lock_irqsave(&priv->lock, flags); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->tty->write_wait, &wait); + dbg("%s(): after wait loop", __FUNCTION__); + + /* clear out any remaining data in the buffer */ + pl2303_buf_clear(priv->buf); + spin_unlock_irqrestore(&priv->lock, flags); + + /* wait for characters to drain from the device */ + /* (this is long enough for the entire 256 byte */ + /* pl2303 hardware buffer to drain with no flow */ + /* control for data rates of 1200 bps or more, */ + /* for lower rates we should really know how much */ + /* data is in the buffer to compute a delay */ + /* that is not unnecessarily long) */ + /* FIXME + bps = tty_get_baud_rate(port->tty); + if (bps > 1200) + timeout = max((HZ*2560)/bps,HZ/10); + else + */ + timeout = 2*HZ; + schedule_timeout_interruptible(timeout); + dbg("%s(): after schedule_timeout_interruptible()", __FUNCTION__); + + /* cancel scheduled setup */ + cancel_delayed_work(&priv->delayed_setup_work); + cancel_delayed_work(&priv->delayed_write_work); + flush_scheduled_work(); + + /* shutdown our urbs */ + dbg("%s(): shutting down urbs", __FUNCTION__); + usb_kill_urb(port->write_urb); + usb_kill_urb(port->read_urb); + usb_kill_urb(port->interrupt_in_urb); + + /* + if (port->tty && (port->tty->termios->c_cflag) & HUPCL) { + // drop DTR and RTS + spin_lock_irqsave(&priv->lock, flags); + priv->pending_setup.control &= ~CONTROL_MASK; + spin_unlock_irqrestore(&priv->lock, flags); + } + */ +} + +static int oti6858_tiocmset(struct usb_serial_port *port, struct file *file, + unsigned int set, unsigned int clear) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + u8 control; + + dbg("%s(port = %d, set = 0x%08x, clear = 0x%08x)", + __FUNCTION__, port->number, set, clear); + + if (!usb_get_intfdata(port->serial->interface)) + return -ENODEV; + + /* FIXME: check if this is correct (active high/low) */ + spin_lock_irqsave(&priv->lock, flags); + control = priv->pending_setup.control; + if ((set & TIOCM_RTS) != 0) + control |= CONTROL_RTS_HIGH; + if ((set & TIOCM_DTR) != 0) + control |= CONTROL_DTR_HIGH; + if ((clear & TIOCM_RTS) != 0) + control &= ~CONTROL_RTS_HIGH; + if ((clear & TIOCM_DTR) != 0) + control &= ~CONTROL_DTR_HIGH; + + if (control != priv->pending_setup.control) { + priv->pending_setup.control = control; + } + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int oti6858_tiocmget(struct usb_serial_port *port, struct file *file) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned pin_state; + unsigned result = 0; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + if (!usb_get_intfdata(port->serial->interface)) + return -ENODEV; + + spin_lock_irqsave(&priv->lock, flags); + pin_state = priv->status.pin_state & PIN_MASK; + spin_unlock_irqrestore(&priv->lock, flags); + + /* FIXME: check if this is correct (active high/low) */ + if ((pin_state & PIN_RTS) != 0) + result |= TIOCM_RTS; + if ((pin_state & PIN_CTS) != 0) + result |= TIOCM_CTS; + if ((pin_state & PIN_DSR) != 0) + result |= TIOCM_DSR; + if ((pin_state & PIN_DTR) != 0) + result |= TIOCM_DTR; + if ((pin_state & PIN_RI) != 0) + result |= TIOCM_RI; + if ((pin_state & PIN_DCD) != 0) + result |= TIOCM_CD; + + dbg("%s() = 0x%08x", __FUNCTION__, result); + + return result; +} + +static int wait_modem_info(struct usb_serial_port *port, unsigned int arg) +{ + struct oti6858_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + unsigned int prev, status; + unsigned int changed; + + spin_lock_irqsave(&priv->lock, flags); + prev = priv->status.pin_state; + spin_unlock_irqrestore(&priv->lock, flags); + + while (1) { + wait_event_interruptible(priv->intr_wait, priv->status.pin_state != prev); + if (signal_pending(current)) + return -ERESTARTSYS; + + spin_lock_irqsave(&priv->lock, flags); + status = priv->status.pin_state & PIN_MASK; + spin_unlock_irqrestore(&priv->lock, flags); + + changed = prev ^ status; + /* FIXME: check if this is correct (active high/low) */ + if ( ((arg & TIOCM_RNG) && (changed & PIN_RI)) || + ((arg & TIOCM_DSR) && (changed & PIN_DSR)) || + ((arg & TIOCM_CD) && (changed & PIN_DCD)) || + ((arg & TIOCM_CTS) && (changed & PIN_CTS))) { + return 0; + } + prev = status; + } + + /* NOTREACHED */ + return 0; +} + +static int oti6858_ioctl(struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *user_arg = (void __user *) arg; + unsigned int x; + + dbg("%s(port = %d, cmd = 0x%04x, arg = 0x%08lx)", + __FUNCTION__, port->number, cmd, arg); + + switch (cmd) { + case TCGETS: + if (copy_to_user(user_arg, port->tty->termios, + sizeof(struct ktermios))) { + return -EFAULT; + } + return 0; + + case TCSETS: + case TCSETSW: /* FIXME: this is not the same! */ + case TCSETSF: /* FIXME: this is not the same! */ + if (copy_from_user(port->tty->termios, user_arg, + sizeof(struct ktermios))) { + return -EFAULT; + } + oti6858_set_termios(port, NULL); + return 0; + + case TCFLSH: + /* FIXME */ + return 0; + + case TIOCMBIS: + if (copy_from_user(&x, user_arg, sizeof(x))) + return -EFAULT; + return oti6858_tiocmset(port, NULL, x, 0); + + case TIOCMBIC: + if (copy_from_user(&x, user_arg, sizeof(x))) + return -EFAULT; + return oti6858_tiocmset(port, NULL, 0, x); + + case TIOCGSERIAL: + if (copy_to_user(user_arg, port->tty->termios, + sizeof(struct ktermios))) { + return -EFAULT; + } + return 0; + + case TIOCSSERIAL: + if (copy_from_user(port->tty->termios, user_arg, + sizeof(struct ktermios))) { + return -EFAULT; + } + oti6858_set_termios(port, NULL); + return 0; + + case TIOCMIWAIT: + dbg("%s(): TIOCMIWAIT", __FUNCTION__); + return wait_modem_info(port, arg); + + default: + dbg("%s(): 0x%04x not supported", __FUNCTION__, cmd); + break; + } + + return -ENOIOCTLCMD; +} + +static void oti6858_break_ctl(struct usb_serial_port *port, int break_state) +{ + int state; + + dbg("%s(port = %d)", __FUNCTION__, port->number); + + state = (break_state == 0) ? 0 : 1; + dbg("%s(): turning break %s", __FUNCTION__, state ? "on" : "off"); + + /* FIXME */ +/* + result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), + BREAK_REQUEST, BREAK_REQUEST_TYPE, state, + 0, NULL, 0, 100); + if (result != 0) + dbg("%s(): error sending break", __FUNCTION__); + */ +} + +static void oti6858_shutdown(struct usb_serial *serial) +{ + struct oti6858_private *priv; + int i; + + dbg("%s()", __FUNCTION__); + + for (i = 0; i < serial->num_ports; ++i) { + priv = usb_get_serial_port_data(serial->port[i]); + if (priv) { + pl2303_buf_free(priv->buf); + kfree(priv); + usb_set_serial_port_data(serial->port[i], NULL); + } + } +} + +static void oti6858_read_int_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct oti6858_private *priv = usb_get_serial_port_data(port); + int transient = 0, can_recv = 0, resubmit = 1; + int status = urb->status; + + dbg("%s(port = %d, status = %d)", + __FUNCTION__, port->number, status); + + switch (status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s(): urb shutting down with status: %d", + __FUNCTION__, status); + return; + default: + dbg("%s(): nonzero urb status received: %d", + __FUNCTION__, status); + break; + } + + if (status == 0 && urb->actual_length == OTI6858_CTRL_PKT_SIZE) { + struct oti6858_control_pkt *xs = urb->transfer_buffer; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + if (!priv->transient) { + if (!OTI6858_CTRL_EQUALS_PENDING(xs, priv)) { + if (xs->rx_bytes_avail == 0) { + priv->transient = 4; + priv->setup_done = 0; + resubmit = 0; + dbg("%s(): scheduling setup_line()", + __FUNCTION__); + schedule_delayed_work(&priv->delayed_setup_work, 0); + } + } + } else { + if (OTI6858_CTRL_EQUALS_PENDING(xs, priv)) { + priv->transient = 0; + } else if (!priv->setup_done) { + resubmit = 0; + } else if (--priv->transient == 0) { + if (xs->rx_bytes_avail == 0) { + priv->transient = 4; + priv->setup_done = 0; + resubmit = 0; + dbg("%s(): scheduling setup_line()", + __FUNCTION__); + schedule_delayed_work(&priv->delayed_setup_work, 0); + } + } + } + + if (!priv->transient) { + if (xs->pin_state != priv->status.pin_state) + wake_up_interruptible(&priv->intr_wait); + memcpy(&priv->status, xs, OTI6858_CTRL_PKT_SIZE); + } + + if (!priv->transient && xs->rx_bytes_avail != 0) { + can_recv = xs->rx_bytes_avail; + priv->flags.read_urb_in_use = 1; + } + + transient = priv->transient; + spin_unlock_irqrestore(&priv->lock, flags); + } + + if (can_recv) { + int result; + + port->read_urb->dev = port->serial->dev; + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result != 0) { + priv->flags.read_urb_in_use = 0; + dev_err(&port->dev, "%s(): usb_submit_urb() failed," + " error %d\n", __FUNCTION__, result); + } else { + resubmit = 0; + } + } else if (!transient) { + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + if (priv->flags.write_urb_in_use == 0 + && pl2303_buf_data_avail(priv->buf) != 0) { + schedule_delayed_work(&priv->delayed_write_work,0); + resubmit = 0; + } + spin_unlock_irqrestore(&priv->lock, flags); + } + + if (resubmit) { + int result; + +// dbg("%s(): submitting interrupt urb", __FUNCTION__); + urb->dev = port->serial->dev; + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result != 0) { + dev_err(&urb->dev->dev, + "%s(): usb_submit_urb() failed with" + " error %d\n", __FUNCTION__, result); + } + } +} + +static void oti6858_read_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct oti6858_private *priv = usb_get_serial_port_data(port); + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + unsigned long flags; + int i, result; + int status = urb->status; + char tty_flag; + + dbg("%s(port = %d, status = %d)", + __FUNCTION__, port->number, status); + + spin_lock_irqsave(&priv->lock, flags); + priv->flags.read_urb_in_use = 0; + spin_unlock_irqrestore(&priv->lock, flags); + + if (status != 0) { + if (!port->open_count) { + dbg("%s(): port is closed, exiting", __FUNCTION__); + return; + } + /* + if (status == -EPROTO) { + // PL2303 mysteriously fails with -EPROTO reschedule the read + dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__); + result = usb_submit_urb(urb, GFP_ATOMIC); + if (result) + dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); + return; + } + */ + dbg("%s(): unable to handle the error, exiting", __FUNCTION__); + return; + } + + // get tty_flag from status + tty_flag = TTY_NORMAL; + +/* FIXME: probably, errors will be signalled using interrupt pipe! */ +/* + // break takes precedence over parity, + // which takes precedence over framing errors + if (status & UART_BREAK_ERROR ) + tty_flag = TTY_BREAK; + else if (status & UART_PARITY_ERROR) + tty_flag = TTY_PARITY; + else if (status & UART_FRAME_ERROR) + tty_flag = TTY_FRAME; + dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag); +*/ + + tty = port->tty; + if (tty != NULL && urb->actual_length > 0) { + tty_buffer_request_room(tty, urb->actual_length); + for (i = 0; i < urb->actual_length; ++i) + tty_insert_flip_char(tty, data[i], tty_flag); + tty_flip_buffer_push(tty); + } + + // schedule the interrupt urb if we are still open */ + if (port->open_count != 0) { + port->interrupt_in_urb->dev = port->serial->dev; + result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + if (result != 0) { + dev_err(&port->dev, "%s(): usb_submit_urb() failed," + " error %d\n", __FUNCTION__, result); + } + } +} + +static void oti6858_write_bulk_callback(struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct oti6858_private *priv = usb_get_serial_port_data(port); + int status = urb->status; + int result; + + dbg("%s(port = %d, status = %d)", + __FUNCTION__, port->number, status); + + switch (status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s(): urb shutting down with status: %d", + __FUNCTION__, status); + priv->flags.write_urb_in_use = 0; + return; + default: + /* error in the urb, so we have to resubmit it */ + dbg("%s(): nonzero write bulk status received: %d", + __FUNCTION__, status); + dbg("%s(): overflow in write", __FUNCTION__); + + port->write_urb->transfer_buffer_length = 1; + port->write_urb->dev = port->serial->dev; + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (result) { + dev_err(&port->dev, "%s(): usb_submit_urb() failed," + " error %d\n", __FUNCTION__, result); + } else { + return; + } + } + + priv->flags.write_urb_in_use = 0; + + // schedule the interrupt urb if we are still open */ + port->interrupt_in_urb->dev = port->serial->dev; + dbg("%s(): submitting interrupt urb", __FUNCTION__); + result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); + if (result != 0) { + dev_err(&port->dev, "%s(): failed submitting int urb," + " error %d\n", __FUNCTION__, result); + } +} + + +/* + * pl2303_buf_alloc + * + * Allocate a circular buffer and all associated memory. + */ +static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) +{ + struct pl2303_buf *pb; + + if (size == 0) + return NULL; + + pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL); + if (pb == NULL) + return NULL; + + pb->buf_buf = kmalloc(size, GFP_KERNEL); + if (pb->buf_buf == NULL) { + kfree(pb); + return NULL; + } + + pb->buf_size = size; + pb->buf_get = pb->buf_put = pb->buf_buf; + + return pb; +} + +/* + * pl2303_buf_free + * + * Free the buffer and all associated memory. + */ +static void pl2303_buf_free(struct pl2303_buf *pb) +{ + if (pb) { + kfree(pb->buf_buf); + kfree(pb); + } +} + +/* + * pl2303_buf_clear + * + * Clear out all data in the circular buffer. + */ +static void pl2303_buf_clear(struct pl2303_buf *pb) +{ + if (pb != NULL) { + /* equivalent to a get of all data available */ + pb->buf_get = pb->buf_put; + } +} + +/* + * pl2303_buf_data_avail + * + * Return the number of bytes of data available in the circular + * buffer. + */ +static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb) +{ + if (pb == NULL) + return 0; + return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size); +} + +/* + * pl2303_buf_space_avail + * + * Return the number of bytes of space available in the circular + * buffer. + */ +static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb) +{ + if (pb == NULL) + return 0; + return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size); +} + +/* + * pl2303_buf_put + * + * Copy data data from a user buffer and put it into the circular buffer. + * Restrict to the amount of space available. + * + * Return the number of bytes copied. + */ +static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, + unsigned int count) +{ + unsigned int len; + + if (pb == NULL) + return 0; + + len = pl2303_buf_space_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_put; + if (count > len) { + memcpy(pb->buf_put, buf, len); + memcpy(pb->buf_buf, buf+len, count - len); + pb->buf_put = pb->buf_buf + count - len; + } else { + memcpy(pb->buf_put, buf, count); + if (count < len) + pb->buf_put += count; + else /* count == len */ + pb->buf_put = pb->buf_buf; + } + + return count; +} + +/* + * pl2303_buf_get + * + * Get data from the circular buffer and copy to the given buffer. + * Restrict to the amount of data available. + * + * Return the number of bytes copied. + */ +static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf, + unsigned int count) +{ + unsigned int len; + + if (pb == NULL) + return 0; + + len = pl2303_buf_data_avail(pb); + if (count > len) + count = len; + + if (count == 0) + return 0; + + len = pb->buf_buf + pb->buf_size - pb->buf_get; + if (count > len) { + memcpy(buf, pb->buf_get, len); + memcpy(buf+len, pb->buf_buf, count - len); + pb->buf_get = pb->buf_buf + count - len; + } else { + memcpy(buf, pb->buf_get, count); + if (count < len) + pb->buf_get += count; + else /* count == len */ + pb->buf_get = pb->buf_buf; + } + + return count; +} + +/* module description and (de)initialization */ + +static int __init oti6858_init(void) +{ + int retval; + + if ((retval = usb_serial_register(&oti6858_device)) == 0) { + if ((retval = usb_register(&oti6858_driver)) != 0) + usb_serial_deregister(&oti6858_device); + else + return 0; + } + + return retval; +} + +static void __exit oti6858_exit(void) +{ + usb_deregister(&oti6858_driver); + usb_serial_deregister(&oti6858_device); +} + +module_init(oti6858_init); +module_exit(oti6858_exit); + +MODULE_DESCRIPTION(OTI6858_DESCRIPTION); +MODULE_AUTHOR(OTI6858_AUTHOR); +MODULE_VERSION(OTI6858_VERSION); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "enable debug output"); + diff --git a/drivers/usb/serial/oti6858.h b/drivers/usb/serial/oti6858.h new file mode 100644 index 0000000..704ac3a --- /dev/null +++ b/drivers/usb/serial/oti6858.h @@ -0,0 +1,15 @@ +/* + * Ours Technology Inc. OTi-6858 USB to serial adapter driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __LINUX_USB_SERIAL_OTI6858_H +#define __LINUX_USB_SERIAL_OTI6858_H + +#define OTI6858_VENDOR_ID 0x0ea0 +#define OTI6858_PRODUCT_ID 0x6858 + +#endif diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 83dfae9..f9f85f5 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -1,14 +1,14 @@ /* * Prolific PL2303 USB to serial adaptor driver * - * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001-2007 Greg Kroah-Hartman (greg@kroah.com) * Copyright (C) 2003 IBM Corp. * * Original driver for 2.2.x by anonymous * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. * * See Documentation/usb/usb-serial.txt for more information on using this driver * @@ -484,15 +484,6 @@ static void pl2303_set_termios(struct usb_serial_port *port, spin_unlock_irqrestore(&priv->lock, flags); cflag = port->tty->termios->c_cflag; - /* check that they really want us to change something */ - if (old_termios) { - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(port->tty->termios->c_iflag) == - RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("%s - nothing to change...", __FUNCTION__); - return; - } - } buf = kzalloc(7, GFP_KERNEL); if (!buf) { @@ -517,29 +508,7 @@ static void pl2303_set_termios(struct usb_serial_port *port, dbg("%s - data bits = %d", __FUNCTION__, buf[6]); } - baud = 0; - switch (cflag & CBAUD) { - case B0: baud = 0; break; - case B75: baud = 75; break; - case B150: baud = 150; break; - case B300: baud = 300; break; - case B600: baud = 600; break; - case B1200: baud = 1200; break; - case B1800: baud = 1800; break; - case B2400: baud = 2400; break; - case B4800: baud = 4800; break; - case B9600: baud = 9600; break; - case B19200: baud = 19200; break; - case B38400: baud = 38400; break; - case B57600: baud = 57600; break; - case B115200: baud = 115200; break; - case B230400: baud = 230400; break; - case B460800: baud = 460800; break; - default: - dev_err(&port->dev, "pl2303 driver does not support" - " the baudrate requested (fix it)\n"); - break; - } + baud = tty_get_baud_rate(port->tty);; dbg("%s - baud = %d", __FUNCTION__, baud); if (baud) { buf[0] = baud & 0xff; @@ -617,6 +586,13 @@ static void pl2303_set_termios(struct usb_serial_port *port, VENDOR_WRITE_REQUEST_TYPE, 0x0, index, NULL, 0, 100); dbg("0x40:0x1:0x0:0x%x %d", index, i); + } else { + i = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + VENDOR_WRITE_REQUEST, + VENDOR_WRITE_REQUEST_TYPE, + 0x0, 0x0, NULL, 0, 100); + dbg ("0x40:0x1:0x0:0x0 %d", i); } kfree(buf); @@ -954,11 +930,12 @@ static void pl2303_read_int_callback(struct urb *urb) struct usb_serial_port *port = (struct usb_serial_port *) urb->context; unsigned char *data = urb->transfer_buffer; unsigned int actual_length = urb->actual_length; - int status; + int status = urb->status; + int retval; dbg("%s (%d)", __FUNCTION__, port->number); - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -967,11 +944,11 @@ static void pl2303_read_int_callback(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __FUNCTION__, - urb->status); + status); return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, - urb->status); + status); goto exit; } @@ -981,11 +958,11 @@ static void pl2303_read_int_callback(struct urb *urb) pl2303_update_line_status(port, data, actual_length); exit: - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n", - __FUNCTION__, status); + __FUNCTION__, retval); } static void pl2303_read_bulk_callback(struct urb *urb) @@ -997,23 +974,23 @@ static void pl2303_read_bulk_callback(struct urb *urb) unsigned long flags; int i; int result; - u8 status; + int status = urb->status; + u8 line_status; char tty_flag; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - urb->status = %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - urb status = %d", __FUNCTION__, status); if (!port->open_count) { dbg("%s - port is closed, exiting.", __FUNCTION__); return; } - if (urb->status == -EPROTO) { + if (status == -EPROTO) { /* PL2303 mysteriously fails with -EPROTO reschedule * the read */ dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__); - urb->status = 0; urb->dev = port->serial->dev; result = usb_submit_urb(urb, GFP_ATOMIC); if (result) @@ -1033,18 +1010,18 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_flag = TTY_NORMAL; spin_lock_irqsave(&priv->lock, flags); - status = priv->line_status; + line_status = priv->line_status; priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); wake_up_interruptible(&priv->delta_msr_wait); /* break takes precedence over parity, */ /* which takes precedence over framing errors */ - if (status & UART_BREAK_ERROR ) + if (line_status & UART_BREAK_ERROR ) tty_flag = TTY_BREAK; - else if (status & UART_PARITY_ERROR) + else if (line_status & UART_PARITY_ERROR) tty_flag = TTY_PARITY; - else if (status & UART_FRAME_ERROR) + else if (line_status & UART_FRAME_ERROR) tty_flag = TTY_FRAME; dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag); @@ -1052,7 +1029,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ - if (status & UART_OVERRUN_ERROR) + if (line_status & UART_OVERRUN_ERROR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); for (i = 0; i < urb->actual_length; ++i) tty_insert_flip_char(tty, data[i], tty_flag); @@ -1076,10 +1053,11 @@ static void pl2303_write_bulk_callback(struct urb *urb) struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct pl2303_private *priv = usb_get_serial_port_data(port); int result; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -1088,14 +1066,14 @@ static void pl2303_write_bulk_callback(struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __FUNCTION__, - urb->status); + status); priv->write_urb_in_use = 0; return; default: /* error in the urb, so we have to resubmit it */ dbg("%s - Overflow in write", __FUNCTION__); dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, - urb->status); + status); port->write_urb->transfer_buffer_length = 1; port->write_urb->dev = port->serial->dev; result = usb_submit_urb(port->write_urb, GFP_ATOMIC); diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 5a03a3f..86899d5 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -211,11 +211,13 @@ static void safe_read_bulk_callback (struct urb *urb) unsigned char length = urb->actual_length; int i; int result; + int status = urb->status; dbg ("%s", __FUNCTION__); - if (urb->status) { - dbg ("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); return; } diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ac1829c..e7db203 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -86,15 +86,14 @@ static int debug; #define N_IN_URB 4 #define N_OUT_URB 4 #define IN_BUFLEN 4096 -#define OUT_BUFLEN 128 struct sierra_port_private { + spinlock_t lock; /* lock the structure */ + int outstanding_urbs; /* number of out urbs in flight */ + /* Input endpoints and buffer for this port */ struct urb *in_urbs[N_IN_URB]; char in_buffer[N_IN_URB][IN_BUFLEN]; - /* Output endpoints and buffer for this port */ - struct urb *out_urbs[N_OUT_URB]; - char out_buffer[N_OUT_URB][OUT_BUFLEN]; /* Settings for the port */ int rts_state; /* Handshaking pins (outputs) */ @@ -103,8 +102,6 @@ struct sierra_port_private { int dsr_state; int dcd_state; int ri_state; - - unsigned long tx_start_time[N_OUT_URB]; }; static int sierra_send_setup(struct usb_serial_port *port) @@ -197,61 +194,98 @@ static int sierra_ioctl(struct usb_serial_port *port, struct file *file, return -ENOIOCTLCMD; } +static void sierra_outdat_callback(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + struct sierra_port_private *portdata = usb_get_serial_port_data(port); + int status = urb->status; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* free up the transfer buffer, as usb_free_urb() does not do this */ + kfree(urb->transfer_buffer); + + if (status) + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); + + spin_lock_irqsave(&portdata->lock, flags); + --portdata->outstanding_urbs; + spin_unlock_irqrestore(&portdata->lock, flags); + + usb_serial_port_softint(port); +} + /* Write */ static int sierra_write(struct usb_serial_port *port, const unsigned char *buf, int count) { - struct sierra_port_private *portdata; - int i; - int left, todo; - struct urb *this_urb = NULL; /* spurious */ - int err; + struct sierra_port_private *portdata = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + unsigned long flags; + unsigned char *buffer; + struct urb *urb; + int status; portdata = usb_get_serial_port_data(port); dbg("%s: write (%d chars)", __FUNCTION__, count); - i = 0; - left = count; - for (i=0; left > 0 && i < N_OUT_URB; i++) { - todo = left; - if (todo > OUT_BUFLEN) - todo = OUT_BUFLEN; - - this_urb = portdata->out_urbs[i]; - if (this_urb->status == -EINPROGRESS) { - if (time_before(jiffies, - portdata->tx_start_time[i] + 10 * HZ)) - continue; - usb_unlink_urb(this_urb); - continue; - } - if (this_urb->status != 0) - dbg("usb_write %p failed (err=%d)", - this_urb, this_urb->status); - - dbg("%s: endpoint %d buf %d", __FUNCTION__, - usb_pipeendpoint(this_urb->pipe), i); - - /* send the data */ - memcpy (this_urb->transfer_buffer, buf, todo); - this_urb->transfer_buffer_length = todo; - - this_urb->dev = port->serial->dev; - err = usb_submit_urb(this_urb, GFP_ATOMIC); - if (err) { - dbg("usb_submit_urb %p (write bulk) failed " - "(%d, has %d)", this_urb, - err, this_urb->status); - continue; - } - portdata->tx_start_time[i] = jiffies; - buf += todo; - left -= todo; + spin_lock_irqsave(&portdata->lock, flags); + if (portdata->outstanding_urbs > N_OUT_URB) { + spin_unlock_irqrestore(&portdata->lock, flags); + dbg("%s - write limit hit\n", __FUNCTION__); + return 0; + } + portdata->outstanding_urbs++; + spin_unlock_irqrestore(&portdata->lock, flags); + + buffer = kmalloc(count, GFP_ATOMIC); + if (!buffer) { + dev_err(&port->dev, "out of memory\n"); + count = -ENOMEM; + goto error_no_buffer; + } + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + dev_err(&port->dev, "no more free urbs\n"); + count = -ENOMEM; + goto error_no_urb; + } + + memcpy(buffer, buf, count); + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer); + + usb_fill_bulk_urb(urb, serial->dev, + usb_sndbulkpipe(serial->dev, + port->bulk_out_endpointAddress), + buffer, count, sierra_outdat_callback, port); + + /* send it down the pipe */ + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " + "with status = %d\n", __FUNCTION__, status); + count = status; + goto error; } - count -= left; - dbg("%s: wrote (did %d)", __FUNCTION__, count); + /* we are done with this urb, so let the host driver + * really free it when it is finished with it */ + usb_free_urb(urb); + + return count; +error: + usb_free_urb(urb); +error_no_urb: + kfree(buffer); +error_no_buffer: + spin_lock_irqsave(&portdata->lock, flags); + --portdata->outstanding_urbs; + spin_unlock_irqrestore(&portdata->lock, flags); return count; } @@ -262,15 +296,16 @@ static void sierra_indat_callback(struct urb *urb) struct usb_serial_port *port; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; + int status = urb->status; dbg("%s: %p", __FUNCTION__, urb); endpoint = usb_pipeendpoint(urb->pipe); port = (struct usb_serial_port *) urb->context; - if (urb->status) { + if (status) { dbg("%s: nonzero status: %d on endpoint %02x.", - __FUNCTION__, urb->status, endpoint); + __FUNCTION__, status, endpoint); } else { tty = port->tty; if (urb->actual_length) { @@ -282,30 +317,20 @@ static void sierra_indat_callback(struct urb *urb) } /* Resubmit urb so we continue receiving */ - if (port->open_count && urb->status != -ESHUTDOWN) { + if (port->open_count && status != -ESHUTDOWN) { err = usb_submit_urb(urb, GFP_ATOMIC); if (err) - printk(KERN_ERR "%s: resubmit read urb failed. " - "(%d)", __FUNCTION__, err); + dev_err(&port->dev, "resubmit read urb failed." + "(%d)", err); } } return; } -static void sierra_outdat_callback(struct urb *urb) -{ - struct usb_serial_port *port; - - dbg("%s", __FUNCTION__); - - port = (struct usb_serial_port *) urb->context; - - usb_serial_port_softint(port); -} - static void sierra_instat_callback(struct urb *urb) { int err; + int status = urb->status; struct usb_serial_port *port = (struct usb_serial_port *) urb->context; struct sierra_port_private *portdata = usb_get_serial_port_data(port); struct usb_serial *serial = port->serial; @@ -313,7 +338,7 @@ static void sierra_instat_callback(struct urb *urb) dbg("%s", __FUNCTION__); dbg("%s: urb %p port %p has data %p", __FUNCTION__,urb,port,portdata); - if (urb->status == 0) { + if (status == 0) { struct usb_ctrlrequest *req_pkt = (struct usb_ctrlrequest *)urb->transfer_buffer; @@ -344,10 +369,10 @@ static void sierra_instat_callback(struct urb *urb) req_pkt->bRequestType,req_pkt->bRequest); } } else - dbg("%s: error %d", __FUNCTION__, urb->status); + dbg("%s: error %d", __FUNCTION__, status); /* Resubmit urb so we continue receiving IRQ data */ - if (urb->status != -ESHUTDOWN) { + if (status != -ESHUTDOWN) { urb->dev = serial->dev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) @@ -358,46 +383,42 @@ static void sierra_instat_callback(struct urb *urb) static int sierra_write_room(struct usb_serial_port *port) { - struct sierra_port_private *portdata; - int i; - int data_len = 0; - struct urb *this_urb; + struct sierra_port_private *portdata = usb_get_serial_port_data(port); + unsigned long flags; - portdata = usb_get_serial_port_data(port); + dbg("%s - port %d", __FUNCTION__, port->number); - for (i=0; i < N_OUT_URB; i++) { - this_urb = portdata->out_urbs[i]; - if (this_urb && this_urb->status != -EINPROGRESS) - data_len += OUT_BUFLEN; + /* try to give a good number back based on if we have any free urbs at + * this point in time */ + spin_lock_irqsave(&portdata->lock, flags); + if (portdata->outstanding_urbs > N_OUT_URB * 2 / 3) { + spin_unlock_irqrestore(&portdata->lock, flags); + dbg("%s - write limit hit\n", __FUNCTION__); + return 0; } + spin_unlock_irqrestore(&portdata->lock, flags); - dbg("%s: %d", __FUNCTION__, data_len); - return data_len; + return 2048; } static int sierra_chars_in_buffer(struct usb_serial_port *port) { - struct sierra_port_private *portdata; - int i; - int data_len = 0; - struct urb *this_urb; - - portdata = usb_get_serial_port_data(port); - - for (i=0; i < N_OUT_URB; i++) { - this_urb = portdata->out_urbs[i]; - if (this_urb && this_urb->status == -EINPROGRESS) - data_len += this_urb->transfer_buffer_length; - } - dbg("%s: %d", __FUNCTION__, data_len); - return data_len; + dbg("%s - port %d", __FUNCTION__, port->number); + + /* + * We can't really account for how much data we + * have sent out, but hasn't made it through to the + * device as we can't see the backend here, so just + * tell the tty layer that everything is flushed. + */ + return 0; } static int sierra_open(struct usb_serial_port *port, struct file *filp) { struct sierra_port_private *portdata; struct usb_serial *serial = port->serial; - int i, err; + int i; struct urb *urb; int result; __u16 set_mode_dzero = 0x0000; @@ -413,7 +434,7 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) /* Reset low level data toggle and start reading from endpoints */ for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; - if (! urb) + if (!urb) continue; if (urb->dev != serial->dev) { dbg("%s: dev %p != %p", __FUNCTION__, @@ -427,24 +448,13 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) */ usb_clear_halt(urb->dev, urb->pipe); - err = usb_submit_urb(urb, GFP_KERNEL); - if (err) { - dbg("%s: submit urb %d failed (%d) %d", - __FUNCTION__, i, err, - urb->transfer_buffer_length); + result = usb_submit_urb(urb, GFP_KERNEL); + if (result) { + dev_err(&port->dev, "submit urb %d failed (%d) %d", + i, result, urb->transfer_buffer_length); } } - /* Reset low level data toggle on out endpoints */ - for (i = 0; i < N_OUT_URB; i++) { - urb = portdata->out_urbs[i]; - if (! urb) - continue; - urb->dev = serial->dev; - /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), 0); */ - } - port->tty->low_latency = 1; /* set mode to D0 */ @@ -455,7 +465,14 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) sierra_send_setup(port); - return (0); + /* start up the interrupt endpoint if we have one */ + if (port->interrupt_in_urb) { + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (result) + dev_err(&port->dev, "submit irq_in urb failed %d", + result); + } + return 0; } static void sierra_close(struct usb_serial_port *port, struct file *filp) @@ -475,71 +492,21 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) - usb_unlink_urb(portdata->in_urbs[i]); - for (i = 0; i < N_OUT_URB; i++) - usb_unlink_urb(portdata->out_urbs[i]); + usb_kill_urb(portdata->in_urbs[i]); } - port->tty = NULL; -} - -/* Helper functions used by sierra_setup_urbs */ -static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint, - int dir, void *ctx, char *buf, int len, - usb_complete_t callback) -{ - struct urb *urb; - - if (endpoint == -1) - return NULL; /* endpoint not needed */ - - urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */ - if (urb == NULL) { - dbg("%s: alloc for endpoint %d failed.", __FUNCTION__, 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); - - return urb; -} -/* Setup urbs */ -static void sierra_setup_urbs(struct usb_serial *serial) -{ - int i,j; - struct usb_serial_port *port; - struct sierra_port_private *portdata; - - dbg("%s", __FUNCTION__); + usb_kill_urb(port->interrupt_in_urb); - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - portdata = usb_get_serial_port_data(port); - - /* Do indat endpoints first */ - for (j = 0; j < N_IN_URB; ++j) { - portdata->in_urbs[j] = sierra_setup_urb (serial, - port->bulk_in_endpointAddress, USB_DIR_IN, port, - portdata->in_buffer[j], IN_BUFLEN, sierra_indat_callback); - } - - /* outdat endpoints */ - for (j = 0; j < N_OUT_URB; ++j) { - portdata->out_urbs[j] = sierra_setup_urb (serial, - port->bulk_out_endpointAddress, USB_DIR_OUT, port, - portdata->out_buffer[j], OUT_BUFLEN, sierra_outdat_callback); - } - } + port->tty = NULL; } static int sierra_startup(struct usb_serial *serial) { - int i, err; struct usb_serial_port *port; struct sierra_port_private *portdata; + struct urb *urb; + int i; + int j; dbg("%s", __FUNCTION__); @@ -550,22 +517,31 @@ static int sierra_startup(struct usb_serial *serial) if (!portdata) { dbg("%s: kmalloc for sierra_port_private (%d) failed!.", __FUNCTION__, i); - return (1); + return -ENOMEM; } + spin_lock_init(&portdata->lock); usb_set_serial_port_data(port, portdata); - if (! port->interrupt_in_urb) - continue; - err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (err) - dbg("%s: submit irq_in urb failed %d", - __FUNCTION__, err); + /* initialize the in urbs */ + for (j = 0; j < N_IN_URB; ++j) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) { + dbg("%s: alloc for in port failed.", + __FUNCTION__); + continue; + } + /* Fill URB using supplied data. */ + usb_fill_bulk_urb(urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), + portdata->in_buffer[j], IN_BUFLEN, + sierra_indat_callback, port); + portdata->in_urbs[j] = urb; + } } - sierra_setup_urbs(serial); - - return (0); + return 0; } static void sierra_shutdown(struct usb_serial *serial) @@ -576,22 +552,6 @@ static void sierra_shutdown(struct usb_serial *serial) dbg("%s", __FUNCTION__); - /* Stop reading/writing urbs */ - for (i = 0; i < serial->num_ports; ++i) { - port = serial->port[i]; - if (!port) - continue; - portdata = usb_get_serial_port_data(port); - if (!portdata) - continue; - - for (j = 0; j < N_IN_URB; j++) - usb_unlink_urb(portdata->in_urbs[j]); - for (j = 0; j < N_OUT_URB; j++) - usb_unlink_urb(portdata->out_urbs[j]); - } - - /* Now free them */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (!port) @@ -601,25 +561,12 @@ static void sierra_shutdown(struct usb_serial *serial) continue; for (j = 0; j < N_IN_URB; j++) { - if (portdata->in_urbs[j]) { - usb_free_urb(portdata->in_urbs[j]); - portdata->in_urbs[j] = NULL; - } + usb_kill_urb(portdata->in_urbs[j]); + usb_free_urb(portdata->in_urbs[j]); + portdata->in_urbs[j] = NULL; } - for (j = 0; j < N_OUT_URB; j++) { - if (portdata->out_urbs[j]) { - usb_free_urb(portdata->out_urbs[j]); - portdata->out_urbs[j] = NULL; - } - } - } - - /* Now free per port private data */ - for (i = 0; i < serial->num_ports; i++) { - port = serial->port[i]; - if (!port) - continue; - kfree(usb_get_serial_port_data(port)); + kfree(portdata); + usb_set_serial_port_data(port, NULL); } } diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 3d505fd..f98626a 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1112,22 +1112,24 @@ static void ti_interrupt_callback(struct urb *urb) int length = urb->actual_length; int port_number; int function; - int status; + int status = urb->status; + int retval; __u8 msr; dbg("%s", __FUNCTION__); - switch (urb->status) { + switch (status) { case 0: break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down, %d", __FUNCTION__, status); tdev->td_urb_error = 1; return; default: - dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status); + dev_err(dev, "%s - nonzero urb status, %d\n", + __FUNCTION__, status); tdev->td_urb_error = 1; goto exit; } @@ -1175,9 +1177,10 @@ static void ti_interrupt_callback(struct urb *urb) } exit: - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) - dev_err(dev, "%s - resubmit interrupt urb failed, %d\n", __FUNCTION__, status); + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "%s - resubmit interrupt urb failed, %d\n", + __FUNCTION__, retval); } @@ -1186,30 +1189,32 @@ static void ti_bulk_in_callback(struct urb *urb) struct ti_port *tport = (struct ti_port *)urb->context; struct usb_serial_port *port = tport->tp_port; struct device *dev = &urb->dev->dev; - int status = 0; + int status = urb->status; + int retval = 0; dbg("%s", __FUNCTION__); - switch (urb->status) { + switch (status) { case 0: break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down, %d", __FUNCTION__, status); tport->tp_tdev->td_urb_error = 1; wake_up_interruptible(&tport->tp_write_wait); return; default: - dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status ); + dev_err(dev, "%s - nonzero urb status, %d\n", + __FUNCTION__, status ); tport->tp_tdev->td_urb_error = 1; wake_up_interruptible(&tport->tp_write_wait); } - if (urb->status == -EPIPE) + if (status == -EPIPE) goto exit; - if (urb->status) { + if (status) { dev_err(dev, "%s - stopping read!\n", __FUNCTION__); return; } @@ -1234,13 +1239,14 @@ exit: spin_lock(&tport->tp_lock); if (tport->tp_read_urb_state == TI_READ_URB_RUNNING) { urb->dev = port->serial->dev; - status = usb_submit_urb(urb, GFP_ATOMIC); + retval = usb_submit_urb(urb, GFP_ATOMIC); } else if (tport->tp_read_urb_state == TI_READ_URB_STOPPING) { tport->tp_read_urb_state = TI_READ_URB_STOPPED; } spin_unlock(&tport->tp_lock); - if (status) - dev_err(dev, "%s - resubmit read urb failed, %d\n", __FUNCTION__, status); + if (retval) + dev_err(dev, "%s - resubmit read urb failed, %d\n", + __FUNCTION__, retval); } @@ -1249,23 +1255,25 @@ static void ti_bulk_out_callback(struct urb *urb) struct ti_port *tport = (struct ti_port *)urb->context; struct usb_serial_port *port = tport->tp_port; struct device *dev = &urb->dev->dev; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); tport->tp_write_urb_in_use = 0; - switch (urb->status) { + switch (status) { case 0: break; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: - dbg("%s - urb shutting down, %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down, %d", __FUNCTION__, status); tport->tp_tdev->td_urb_error = 1; wake_up_interruptible(&tport->tp_write_wait); return; default: - dev_err(dev, "%s - nonzero urb status, %d\n", __FUNCTION__, urb->status); + dev_err(dev, "%s - nonzero urb status, %d\n", + __FUNCTION__, status); tport->tp_tdev->td_urb_error = 1; wake_up_interruptible(&tport->tp_write_wait); } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 87f3788..a366565 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -46,6 +46,8 @@ static struct usb_driver usb_serial_driver = { .name = "usbserial", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, + .suspend = usb_serial_suspend, + .resume = usb_serial_resume, .no_dynamic_id = 1, }; @@ -120,11 +122,9 @@ 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) @@ -172,7 +172,9 @@ static void destroy_serial(struct kref *kref) void usb_serial_put(struct usb_serial *serial) { + spin_lock(&table_lock); kref_put(&serial->kref, destroy_serial); + spin_unlock(&table_lock); } /***************************************************************************** @@ -1069,6 +1071,35 @@ void usb_serial_disconnect(struct usb_interface *interface) dev_info(dev, "device disconnected\n"); } +int usb_serial_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_serial *serial = usb_get_intfdata(intf); + struct usb_serial_port *port; + int i, r = 0; + + if (serial) { + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + if (port) + kill_traffic(port); + } + } + + if (serial->type->suspend) + serial->type->suspend(serial, message); + + return r; +} +EXPORT_SYMBOL(usb_serial_suspend); + +int usb_serial_resume(struct usb_interface *intf) +{ + struct usb_serial *serial = usb_get_intfdata(intf); + + return serial->type->resume(serial); +} +EXPORT_SYMBOL(usb_serial_resume); + static const struct tty_operations serial_ops = { .open = serial_open, .close = serial_close, diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index ffbe601..7d84a76 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -5,9 +5,9 @@ * Copyright (C) 1999 - 2004 * Greg Kroah-Hartman (greg@kroah.com) * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. * * See Documentation/usb/usb-serial.txt for more information on using this driver * @@ -273,7 +273,8 @@ struct visor_private { int bytes_in; int bytes_out; int outstanding_urbs; - int throttled; + unsigned char throttled; + unsigned char actually_throttled; }; /* number of outstanding urbs to prevent userspace DoS from happening */ @@ -484,16 +485,17 @@ static void visor_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct visor_private *priv = usb_get_serial_port_data(port); + int status = urb->status; unsigned long flags; /* free up the transfer buffer, as usb_free_urb() does not do this */ kfree (urb->transfer_buffer); dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->status) + + if (status) dbg("%s - nonzero write bulk status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); spin_lock_irqsave(&priv->lock, flags); --priv->outstanding_urbs; @@ -508,15 +510,16 @@ static void visor_read_bulk_callback (struct urb *urb) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct visor_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; + int status = urb->status; struct tty_struct *tty; - unsigned long flags; - int throttled; int result; + int available_room; dbg("%s - port %d", __FUNCTION__, port->number); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); return; } @@ -524,17 +527,20 @@ static void visor_read_bulk_callback (struct urb *urb) tty = port->tty; if (tty && urb->actual_length) { - tty_buffer_request_room(tty, urb->actual_length); - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); + available_room = tty_buffer_request_room(tty, urb->actual_length); + if (available_room) { + tty_insert_flip_string(tty, data, available_room); + tty_flip_buffer_push(tty); + } + spin_lock(&priv->lock); + priv->bytes_in += available_room; + + } else { + spin_lock(&priv->lock); } - spin_lock_irqsave(&priv->lock, flags); - priv->bytes_in += urb->actual_length; - throttled = priv->throttled; - spin_unlock_irqrestore(&priv->lock, flags); /* Continue trying to always read if we should */ - if (!throttled) { + if (!priv->throttled) { usb_fill_bulk_urb (port->read_urb, port->serial->dev, usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), @@ -544,16 +550,19 @@ static void visor_read_bulk_callback (struct urb *urb) result = usb_submit_urb(port->read_urb, GFP_ATOMIC); if (result) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); + } else { + priv->actually_throttled = 1; } - return; + spin_unlock(&priv->lock); } static void visor_read_int_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + int status = urb->status; int result; - switch (urb->status) { + switch (status) { case 0: /* success */ break; @@ -562,11 +571,11 @@ static void visor_read_int_callback (struct urb *urb) case -ESHUTDOWN: /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); return; default: dbg("%s - nonzero urb status received: %d", - __FUNCTION__, urb->status); + __FUNCTION__, status); goto exit; } @@ -608,6 +617,7 @@ static void visor_unthrottle (struct usb_serial_port *port) dbg("%s - port %d", __FUNCTION__, port->number); spin_lock_irqsave(&priv->lock, flags); priv->throttled = 0; + priv->actually_throttled = 0; spin_unlock_irqrestore(&priv->lock, flags); port->read_urb->dev = port->serial->dev; @@ -938,14 +948,6 @@ static void visor_set_termios (struct usb_serial_port *port, struct ktermios *ol } cflag = port->tty->termios->c_cflag; - /* check that they really want us to change something */ - if (old_termios) { - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { - dbg("%s - nothing to change...", __FUNCTION__); - return; - } - } /* get the byte size */ switch (cflag & CSIZE) { diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 27c5f8f..cc8b44c 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -74,6 +74,7 @@ #include <linux/tty_flip.h> #include <linux/module.h> #include <linux/spinlock.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/termbits.h> #include <linux/usb.h> @@ -203,7 +204,7 @@ static struct usb_serial_driver whiteheat_device = { struct whiteheat_command_private { - spinlock_t lock; + struct mutex mutex; __u8 port_running; __u8 command_finished; wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */ @@ -232,6 +233,7 @@ struct whiteheat_private { struct usb_serial_port *port; struct list_head tx_urbs_free; struct list_head tx_urbs_submitted; + struct mutex deathwarrant; }; @@ -425,6 +427,7 @@ static int whiteheat_attach (struct usb_serial *serial) } spin_lock_init(&info->lock); + mutex_init(&info->deathwarrant); info->flags = 0; info->mcr = 0; INIT_WORK(&info->rx_work, rx_data_softint); @@ -495,7 +498,7 @@ static int whiteheat_attach (struct usb_serial *serial) goto no_command_private; } - spin_lock_init(&command_info->lock); + mutex_init(&command_info->mutex); command_info->port_running = 0; init_waitqueue_head(&command_info->wait_command); usb_set_serial_port_data(command_port, command_info); @@ -654,7 +657,6 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) struct urb *urb; struct list_head *tmp; struct list_head *tmp2; - unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); @@ -683,24 +685,32 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp) firm_close(port); +printk(KERN_ERR"Before processing rx_urbs_submitted.\n"); /* shutdown our bulk reads and writes */ - spin_lock_irqsave(&info->lock, flags); + mutex_lock(&info->deathwarrant); + spin_lock_irq(&info->lock); list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; + list_del(tmp); + spin_unlock_irq(&info->lock); usb_kill_urb(urb); - list_move(tmp, &info->rx_urbs_free); + spin_lock_irq(&info->lock); + list_add(tmp, &info->rx_urbs_free); } list_for_each_safe(tmp, tmp2, &info->rx_urb_q) list_move(tmp, &info->rx_urbs_free); - list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; + list_del(tmp); + spin_unlock_irq(&info->lock); usb_kill_urb(urb); - list_move(tmp, &info->tx_urbs_free); + spin_lock_irq(&info->lock); + list_add(tmp, &info->tx_urbs_free); } - spin_unlock_irqrestore(&info->lock, flags); + spin_unlock_irq(&info->lock); + mutex_unlock(&info->deathwarrant); stop_command_port(port->serial); @@ -872,7 +882,7 @@ static int whiteheat_ioctl (struct usb_serial_port *port, struct file * file, un } -static void whiteheat_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void whiteheat_set_termios(struct usb_serial_port *port, struct ktermios *old_termios) { dbg("%s -port %d", __FUNCTION__, port->number); @@ -881,15 +891,6 @@ static void whiteheat_set_termios (struct usb_serial_port *port, struct ktermios goto exit; } - /* check that they really want us to change something */ - if (old_termios) { - if ((port->tty->termios->c_cflag == old_termios->c_cflag) && - (port->tty->termios->c_iflag == old_termios->c_iflag)) { - dbg("%s - nothing to change...", __FUNCTION__); - goto exit; - } - } - firm_setup_port(port); exit: @@ -920,7 +921,7 @@ static int whiteheat_chars_in_buffer(struct usb_serial_port *port) spin_unlock_irqrestore(&info->lock, flags); dbg ("%s - returns %d", __FUNCTION__, chars); - return (chars); + return chars; } @@ -962,54 +963,57 @@ static void whiteheat_unthrottle (struct usb_serial_port *port) /***************************************************************************** * Connect Tech's White Heat callback routines *****************************************************************************/ -static void command_port_write_callback (struct urb *urb) +static void command_port_write_callback(struct urb *urb) { + int status = urb->status; + dbg("%s", __FUNCTION__); - if (urb->status) { - dbg ("nonzero urb status: %d", urb->status); + if (status) { + dbg("nonzero urb status: %d", status); return; } } -static void command_port_read_callback (struct urb *urb) +static void command_port_read_callback(struct urb *urb) { struct usb_serial_port *command_port = (struct usb_serial_port *)urb->context; struct whiteheat_command_private *command_info; + int status = urb->status; unsigned char *data = urb->transfer_buffer; int result; - unsigned long flags; dbg("%s", __FUNCTION__); - if (urb->status) { - dbg("%s - nonzero urb status: %d", __FUNCTION__, urb->status); - return; - } - - usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data); - command_info = usb_get_serial_port_data(command_port); if (!command_info) { dbg ("%s - command_info is NULL, exiting.", __FUNCTION__); return; } - spin_lock_irqsave(&command_info->lock, flags); + if (status) { + dbg("%s - nonzero urb status: %d", __FUNCTION__, status); + if (status != -ENOENT) + command_info->command_finished = WHITEHEAT_CMD_FAILURE; + wake_up(&command_info->wait_command); + return; + } + + usb_serial_debug_data(debug, &command_port->dev, __FUNCTION__, urb->actual_length, data); if (data[0] == WHITEHEAT_CMD_COMPLETE) { command_info->command_finished = WHITEHEAT_CMD_COMPLETE; - wake_up_interruptible(&command_info->wait_command); + wake_up(&command_info->wait_command); } else if (data[0] == WHITEHEAT_CMD_FAILURE) { command_info->command_finished = WHITEHEAT_CMD_FAILURE; - wake_up_interruptible(&command_info->wait_command); + wake_up(&command_info->wait_command); } else if (data[0] == WHITEHEAT_EVENT) { /* These are unsolicited reports from the firmware, hence no waiting command to wakeup */ dbg("%s - event received", __FUNCTION__); } else if (data[0] == WHITEHEAT_GET_DTR_RTS) { memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1); command_info->command_finished = WHITEHEAT_CMD_COMPLETE; - wake_up_interruptible(&command_info->wait_command); + wake_up(&command_info->wait_command); } else { dbg("%s - bad reply from firmware", __FUNCTION__); } @@ -1017,7 +1021,6 @@ static void command_port_read_callback (struct urb *urb) /* Continue trying to always read */ command_port->read_urb->dev = command_port->serial->dev; result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC); - spin_unlock_irqrestore(&command_info->lock, flags); if (result) dbg("%s - failed resubmitting read urb, error %d", __FUNCTION__, result); } @@ -1029,6 +1032,7 @@ static void whiteheat_read_callback(struct urb *urb) struct whiteheat_urb_wrap *wrap; unsigned char *data = urb->transfer_buffer; struct whiteheat_private *info = usb_get_serial_port_data(port); + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); @@ -1042,8 +1046,9 @@ static void whiteheat_read_callback(struct urb *urb) list_del(&wrap->list); spin_unlock(&info->lock); - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero read bulk status received: %d", + __FUNCTION__, status); spin_lock(&info->lock); list_add(&wrap->list, &info->rx_urbs_free); spin_unlock(&info->lock); @@ -1070,6 +1075,7 @@ static void whiteheat_write_callback(struct urb *urb) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct whiteheat_private *info = usb_get_serial_port_data(port); struct whiteheat_urb_wrap *wrap; + int status = urb->status; dbg("%s - port %d", __FUNCTION__, port->number); @@ -1083,8 +1089,9 @@ static void whiteheat_write_callback(struct urb *urb) list_move(&wrap->list, &info->tx_urbs_free); spin_unlock(&info->lock); - if (urb->status) { - dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); + if (status) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, status); return; } @@ -1095,20 +1102,20 @@ static void whiteheat_write_callback(struct urb *urb) /***************************************************************************** * Connect Tech's White Heat firmware interface *****************************************************************************/ -static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize) +static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize) { struct usb_serial_port *command_port; struct whiteheat_command_private *command_info; struct whiteheat_private *info; __u8 *transfer_buffer; int retval = 0; - unsigned long flags; + int t; dbg("%s - command %d", __FUNCTION__, command); command_port = port->serial->port[COMMAND_PORT]; command_info = usb_get_serial_port_data(command_port); - spin_lock_irqsave(&command_info->lock, flags); + mutex_lock(&command_info->mutex); command_info->command_finished = false; transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; @@ -1116,18 +1123,17 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 * memcpy (&transfer_buffer[1], data, datasize); command_port->write_urb->transfer_buffer_length = datasize + 1; command_port->write_urb->dev = port->serial->dev; - retval = usb_submit_urb (command_port->write_urb, GFP_KERNEL); + retval = usb_submit_urb (command_port->write_urb, GFP_NOIO); if (retval) { dbg("%s - submit urb failed", __FUNCTION__); goto exit; } - spin_unlock_irqrestore(&command_info->lock, flags); /* wait for the command to complete */ - wait_event_interruptible_timeout(command_info->wait_command, + t = wait_event_timeout(command_info->wait_command, (bool)command_info->command_finished, COMMAND_TIMEOUT); - - spin_lock_irqsave(&command_info->lock, flags); + if (!t) + usb_kill_urb(command_port->write_urb); if (command_info->command_finished == false) { dbg("%s - command timed out.", __FUNCTION__); @@ -1152,7 +1158,7 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 * } exit: - spin_unlock_irqrestore(&command_info->lock, flags); + mutex_unlock(&command_info->mutex); return retval; } @@ -1305,12 +1311,11 @@ static int start_command_port(struct usb_serial *serial) { struct usb_serial_port *command_port; struct whiteheat_command_private *command_info; - unsigned long flags; int retval = 0; command_port = serial->port[COMMAND_PORT]; command_info = usb_get_serial_port_data(command_port); - spin_lock_irqsave(&command_info->lock, flags); + mutex_lock(&command_info->mutex); if (!command_info->port_running) { /* Work around HCD bugs */ usb_clear_halt(serial->dev, command_port->read_urb->pipe); @@ -1325,7 +1330,7 @@ static int start_command_port(struct usb_serial *serial) command_info->port_running++; exit: - spin_unlock_irqrestore(&command_info->lock, flags); + mutex_unlock(&command_info->mutex); return retval; } @@ -1334,15 +1339,14 @@ static void stop_command_port(struct usb_serial *serial) { struct usb_serial_port *command_port; struct whiteheat_command_private *command_info; - unsigned long flags; command_port = serial->port[COMMAND_PORT]; command_info = usb_get_serial_port_data(command_port); - spin_lock_irqsave(&command_info->lock, flags); + mutex_lock(&command_info->mutex); command_info->port_running--; if (!command_info->port_running) usb_kill_urb(command_port->read_urb); - spin_unlock_irqrestore(&command_info->lock, flags); + mutex_unlock(&command_info->mutex); } @@ -1363,17 +1367,23 @@ static int start_port_read(struct usb_serial_port *port) wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; urb->dev = port->serial->dev; + spin_unlock_irqrestore(&info->lock, flags); retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { + spin_lock_irqsave(&info->lock, flags); list_add(tmp, &info->rx_urbs_free); list_for_each_safe(tmp, tmp2, &info->rx_urbs_submitted) { wrap = list_entry(tmp, struct whiteheat_urb_wrap, list); urb = wrap->urb; + list_del(tmp); + spin_unlock_irqrestore(&info->lock, flags); usb_kill_urb(urb); - list_move(tmp, &info->rx_urbs_free); + spin_lock_irqsave(&info->lock, flags); + list_add(tmp, &info->rx_urbs_free); } break; } + spin_lock_irqsave(&info->lock, flags); list_add(tmp, &info->rx_urbs_submitted); } diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index e227f64..47e5607 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -285,10 +285,15 @@ static int device_reset(struct scsi_cmnd *srb) US_DEBUGP("%s called\n", __FUNCTION__); - /* lock the device pointers and do the reset */ - mutex_lock(&(us->dev_mutex)); - result = us->transport_reset(us); - mutex_unlock(&us->dev_mutex); + result = usb_autopm_get_interface(us->pusb_intf); + if (result == 0) { + + /* lock the device pointers and do the reset */ + mutex_lock(&(us->dev_mutex)); + result = us->transport_reset(us); + mutex_unlock(&us->dev_mutex); + usb_autopm_put_interface(us->pusb_intf); + } return result < 0 ? FAILED : SUCCESS; } @@ -321,10 +326,14 @@ void usb_stor_report_device_reset(struct us_data *us) /* Report a driver-initiated bus reset to the SCSI layer. * Calling this for a SCSI-initiated reset is unnecessary but harmless. - * The caller must own the SCSI host lock. */ + * The caller must not own the SCSI host lock. */ void usb_stor_report_bus_reset(struct us_data *us) { - scsi_report_bus_reset(us_to_host(us), 0); + struct Scsi_Host *host = us_to_host(us); + + scsi_lock(host); + scsi_report_bus_reset(host, 0); + scsi_unlock(host); } /*********************************************************************** diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 54979c2..b6bf31a 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -50,10 +50,10 @@ /* patch submitted by Vivian Bregier <Vivian.Bregier@imag.fr> */ UNUSUAL_DEV( 0x03eb, 0x2002, 0x0100, 0x0100, - "ATMEL", - "SND1 Storage", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE), + "ATMEL", + "SND1 Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), /* modified by Tobias Lorenz <tobias.lorenz@gmx.net> */ UNUSUAL_DEV( 0x03ee, 0x6901, 0x0000, 0x0200, @@ -69,18 +69,18 @@ UNUSUAL_DEV( 0x03ee, 0x6906, 0x0003, 0x0003, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), -UNUSUAL_DEV( 0x03f0, 0x0107, 0x0200, 0x0200, +UNUSUAL_DEV( 0x03f0, 0x0107, 0x0200, 0x0200, "HP", "CD-Writer+", - US_SC_8070, US_PR_CB, NULL, 0), + US_SC_8070, US_PR_CB, NULL, 0), #ifdef CONFIG_USB_STORAGE_USBAT -UNUSUAL_DEV( 0x03f0, 0x0207, 0x0001, 0x0001, +UNUSUAL_DEV( 0x03f0, 0x0207, 0x0001, 0x0001, "HP", "CD-Writer+ 8200e", US_SC_8070, US_PR_USBAT, init_usbat_cd, 0), -UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, +UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, "HP", "CD-Writer+ CD-4e", US_SC_8070, US_PR_USBAT, init_usbat_cd, 0), @@ -115,10 +115,10 @@ UNUSUAL_DEV( 0x0411, 0x001c, 0x0113, 0x0113, /* Submitted by Ernestas Vaiciukevicius <ernisv@gmail.com> */ UNUSUAL_DEV( 0x0419, 0x0100, 0x0100, 0x0100, - "Samsung Info. Systems America, Inc.", - "MP3 Player", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), + "Samsung Info. Systems America, Inc.", + "MP3 Player", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), /* Reported by Orgad Shaneh <orgads@gmail.com> */ UNUSUAL_DEV( 0x0419, 0xaace, 0x0100, 0x0100, @@ -256,10 +256,10 @@ UNUSUAL_DEV( 0x0457, 0x0150, 0x0100, 0x0100, * the revision to my model only */ UNUSUAL_DEV( 0x0457, 0x0151, 0x0100, 0x0100, - "USB 2.0", - "Flash Disk", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_NOT_LOCKABLE ), + "USB 2.0", + "Flash Disk", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_NOT_LOCKABLE ), #ifdef CONFIG_USB_STORAGE_KARMA UNUSUAL_DEV( 0x045a, 0x5210, 0x0101, 0x0101, @@ -408,19 +408,19 @@ UNUSUAL_DEV( 0x04da, 0x2373, 0x0000, 0x9999, /* Most of the following entries were developed with the help of * Shuttle/SCM directly. */ -UNUSUAL_DEV( 0x04e6, 0x0001, 0x0200, 0x0200, +UNUSUAL_DEV( 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita", "LS-120", US_SC_8020, US_PR_CB, NULL, 0), -UNUSUAL_DEV( 0x04e6, 0x0002, 0x0100, 0x0100, +UNUSUAL_DEV( 0x04e6, 0x0002, 0x0100, 0x0100, "Shuttle", "eUSCSI Bridge", US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init, - US_FL_SCM_MULT_TARG ), + US_FL_SCM_MULT_TARG ), #ifdef CONFIG_USB_STORAGE_SDDR09 -UNUSUAL_DEV( 0x04e6, 0x0003, 0x0000, 0x9999, +UNUSUAL_DEV( 0x04e6, 0x0003, 0x0000, 0x9999, "Sandisk", "ImageMate SDDR09", US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init, @@ -431,52 +431,52 @@ UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208, "SCM Microsystems", "eUSB SmartMedia / CompactFlash Adapter", US_SC_SCSI, US_PR_DPCM_USB, usb_stor_sddr09_dpcm_init, - 0), + 0), #endif /* Reported by Markus Demleitner <msdemlei@cl.uni-heidelberg.de> */ -UNUSUAL_DEV( 0x04e6, 0x0006, 0x0100, 0x0100, +UNUSUAL_DEV( 0x04e6, 0x0006, 0x0100, 0x0100, "SCM Microsystems Inc.", "eUSB MMC Adapter", - US_SC_SCSI, US_PR_CB, NULL, - US_FL_SINGLE_LUN), + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN), /* Reported by Daniel Nouri <dpunktnpunkt@web.de> */ -UNUSUAL_DEV( 0x04e6, 0x0006, 0x0205, 0x0205, +UNUSUAL_DEV( 0x04e6, 0x0006, 0x0205, 0x0205, "Shuttle", "eUSB MMC Adapter", - US_SC_SCSI, US_PR_DEVICE, NULL, - US_FL_SINGLE_LUN), + US_SC_SCSI, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN), -UNUSUAL_DEV( 0x04e6, 0x0007, 0x0100, 0x0200, +UNUSUAL_DEV( 0x04e6, 0x0007, 0x0100, 0x0200, "Sony", "Hifd", - US_SC_SCSI, US_PR_CB, NULL, - US_FL_SINGLE_LUN), + US_SC_SCSI, US_PR_CB, NULL, + US_FL_SINGLE_LUN), -UNUSUAL_DEV( 0x04e6, 0x0009, 0x0200, 0x0200, +UNUSUAL_DEV( 0x04e6, 0x0009, 0x0200, 0x0200, "Shuttle", "eUSB ATA/ATAPI Adapter", US_SC_8020, US_PR_CB, NULL, 0), -UNUSUAL_DEV( 0x04e6, 0x000a, 0x0200, 0x0200, +UNUSUAL_DEV( 0x04e6, 0x000a, 0x0200, 0x0200, "Shuttle", "eUSB CompactFlash Adapter", US_SC_8020, US_PR_CB, NULL, 0), -UNUSUAL_DEV( 0x04e6, 0x000B, 0x0100, 0x0100, +UNUSUAL_DEV( 0x04e6, 0x000B, 0x0100, 0x0100, "Shuttle", "eUSCSI Bridge", - US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, + US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, US_FL_SCM_MULT_TARG ), -UNUSUAL_DEV( 0x04e6, 0x000C, 0x0100, 0x0100, +UNUSUAL_DEV( 0x04e6, 0x000C, 0x0100, 0x0100, "Shuttle", "eUSCSI Bridge", - US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, - US_FL_SCM_MULT_TARG ), + US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), -UNUSUAL_DEV( 0x04e6, 0x0101, 0x0200, 0x0200, +UNUSUAL_DEV( 0x04e6, 0x0101, 0x0200, 0x0200, "Shuttle", "CD-RW Device", US_SC_8020, US_PR_CB, NULL, 0), @@ -556,9 +556,9 @@ UNUSUAL_DEV( 0x052b, 0x1911, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), -UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, +UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, "Sony", - "DSC-S30/S70/S75/505V/F505/F707/F717/P8", + "DSC-S30/S70/S75/505V/F505/F707/F717/P8", US_SC_SCSI, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN | US_FL_NOT_LOCKABLE | US_FL_NO_WP_DETECT ), @@ -572,7 +572,7 @@ UNUSUAL_DEV( 0x054c, 0x0010, 0x0500, 0x0610, /* Reported by wim@geeks.nl */ -UNUSUAL_DEV( 0x054c, 0x0025, 0x0100, 0x0100, +UNUSUAL_DEV( 0x054c, 0x0025, 0x0100, 0x0100, "Sony", "Memorystick NW-MS7", US_SC_DEVICE, US_PR_DEVICE, NULL, @@ -593,21 +593,21 @@ UNUSUAL_DEV( 0x054c, 0x002c, 0x0501, 0x2000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), -UNUSUAL_DEV( 0x054c, 0x002d, 0x0100, 0x0100, +UNUSUAL_DEV( 0x054c, 0x002d, 0x0100, 0x0100, "Sony", "Memorystick MSAC-US1", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), /* Submitted by Klaus Mueller <k.mueller@intershop.de> */ -UNUSUAL_DEV( 0x054c, 0x002e, 0x0106, 0x0310, +UNUSUAL_DEV( 0x054c, 0x002e, 0x0106, 0x0310, "Sony", "Handycam", US_SC_SCSI, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), /* Submitted by Rajesh Kumble Nayak <nayak@obs-nice.fr> */ -UNUSUAL_DEV( 0x054c, 0x002e, 0x0500, 0x0500, +UNUSUAL_DEV( 0x054c, 0x002e, 0x0500, 0x0500, "Sony", "Handycam HC-85", US_SC_UFI, US_PR_DEVICE, NULL, @@ -648,26 +648,26 @@ UNUSUAL_DEV( 0x054c, 0x016a, 0x0000, 0x9999, /* Submitted by Frank Engel <frankie@cse.unsw.edu.au> */ UNUSUAL_DEV( 0x054c, 0x0099, 0x0000, 0x9999, - "Sony", - "PEG Mass Storage", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), + "Sony", + "PEG Mass Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), /* floppy reports multiple luns */ UNUSUAL_DEV( 0x055d, 0x2020, 0x0000, 0x0210, - "SAMSUNG", - "SFD-321U [FW 0C]", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_SINGLE_LUN ), + "SAMSUNG", + "SFD-321U [FW 0C]", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN ), -UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, +UNUSUAL_DEV( 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data", "Flashbuster-U", US_SC_DEVICE, US_PR_CB, NULL, US_FL_SINGLE_LUN), -UNUSUAL_DEV( 0x057b, 0x0000, 0x0300, 0x9999, +UNUSUAL_DEV( 0x057b, 0x0000, 0x0300, 0x9999, "Y-E Data", "Flashbuster-U", US_SC_DEVICE, US_PR_DEVICE, NULL, @@ -677,7 +677,7 @@ UNUSUAL_DEV( 0x057b, 0x0000, 0x0300, 0x9999, * This entry is needed only because the device reports * bInterfaceClass = 0xff (vendor-specific) */ -UNUSUAL_DEV( 0x057b, 0x0022, 0x0000, 0x9999, +UNUSUAL_DEV( 0x057b, 0x0022, 0x0000, 0x9999, "Y-E Data", "Silicon Media R/W", US_SC_DEVICE, US_PR_DEVICE, NULL, 0), @@ -825,13 +825,13 @@ UNUSUAL_DEV( 0x0636, 0x0003, 0x0000, 0x9999, US_SC_SCSI, US_PR_BULK, NULL, US_FL_FIX_INQUIRY ), -UNUSUAL_DEV( 0x0644, 0x0000, 0x0100, 0x0100, +UNUSUAL_DEV( 0x0644, 0x0000, 0x0100, 0x0100, "TEAC", "Floppy Drive", - US_SC_UFI, US_PR_CB, NULL, 0 ), + US_SC_UFI, US_PR_CB, NULL, 0 ), #ifdef CONFIG_USB_STORAGE_SDDR09 -UNUSUAL_DEV( 0x066b, 0x0105, 0x0100, 0x0100, +UNUSUAL_DEV( 0x066b, 0x0105, 0x0100, 0x0100, "Olympus", "Camedia MAUSB-2", US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init, @@ -867,14 +867,14 @@ UNUSUAL_DEV( 0x0686, 0x4011, 0x0001, 0x0001, /* Reported by Miguel A. Fosas <amn3s1a@ono.com> */ UNUSUAL_DEV( 0x0686, 0x4017, 0x0001, 0x0001, - "Minolta", - "DIMAGE E223", - US_SC_SCSI, US_PR_DEVICE, NULL, 0 ), + "Minolta", + "DIMAGE E223", + US_SC_SCSI, US_PR_DEVICE, NULL, 0 ), UNUSUAL_DEV( 0x0693, 0x0005, 0x0100, 0x0100, "Hagiwara", "Flashgate", - US_SC_SCSI, US_PR_BULK, NULL, 0 ), + US_SC_SCSI, US_PR_BULK, NULL, 0 ), /* Reported by David Hamilton <niftimusmaximus@lycos.com> */ UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001, @@ -918,7 +918,7 @@ UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100, US_FL_SINGLE_LUN ), #ifdef CONFIG_USB_STORAGE_SDDR09 -UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999, +UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999, "Sandisk", "ImageMate SDDR-09", US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init, @@ -939,17 +939,17 @@ UNUSUAL_DEV( 0x07ab, 0xfccd, 0x0000, 0x9999, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), -UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133, +UNUSUAL_DEV( 0x07af, 0x0004, 0x0100, 0x0133, "Microtech", "USB-SCSI-DB25", US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init, US_FL_SCM_MULT_TARG ), -UNUSUAL_DEV( 0x07af, 0x0005, 0x0100, 0x0100, +UNUSUAL_DEV( 0x07af, 0x0005, 0x0100, 0x0100, "Microtech", "USB-SCSI-HD50", US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init, - US_FL_SCM_MULT_TARG ), + US_FL_SCM_MULT_TARG ), #ifdef CONFIG_USB_STORAGE_DPCM UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100, @@ -1053,10 +1053,10 @@ UNUSUAL_DEV( 0x07c4, 0xa109, 0x0000, 0xffff, * as "DualSlot CompactFlash(TM) & MStick Drive USB" */ UNUSUAL_DEV( 0x07c4, 0xa10b, 0x0000, 0xffff, - "DataFab Systems Inc.", - "USB CF+MS", - US_SC_SCSI, US_PR_DATAFAB, NULL, - 0 ), + "DataFab Systems Inc.", + "USB CF+MS", + US_SC_SCSI, US_PR_DATAFAB, NULL, + 0 ), #endif @@ -1119,10 +1119,10 @@ UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000, * US_FL_IGNORE_RESIDUE Needed */ UNUSUAL_DEV( 0x08ca, 0x3103, 0x0100, 0x0100, - "AIPTEK", - "Aiptek USB Keychain MP3 Player", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE), + "AIPTEK", + "Aiptek USB Keychain MP3 Player", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), /* Entry needed for flags. Moreover, all devices with this ID use * bulk-only transport, but _some_ falsely report Control/Bulk instead. @@ -1166,26 +1166,26 @@ UNUSUAL_DEV( 0x090c, 0x1132, 0x0000, 0xffff, * Submitted by James Courtier-Dutton <James@superbug.demon.co.uk> */ UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000, - "Pentax", - "Optio 2/3/400", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), + "Pentax", + "Optio 2/3/400", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), /* Submitted by Per Winkvist <per.winkvist@uk.com> */ UNUSUAL_DEV( 0x0a17, 0x006, 0x0000, 0xffff, - "Pentax", - "Optio S/S4", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_INQUIRY ), + "Pentax", + "Optio S/S4", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_INQUIRY ), /* These are virtual windows driver CDs, which the zd1211rw driver * automatically converts into WLAN devices. */ UNUSUAL_DEV( 0x0ace, 0x2011, 0x0101, 0x0101, - "ZyXEL", - "G-220F USB-WLAN Install", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_DEVICE ), + "ZyXEL", + "G-220F USB-WLAN Install", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE ), UNUSUAL_DEV( 0x0ace, 0x20ff, 0x0101, 0x0101, "SiteCom", @@ -1211,17 +1211,17 @@ UNUSUAL_DEV( 0x0bf6, 0xa001, 0x0100, 0x0110, #ifdef CONFIG_USB_STORAGE_DATAFAB UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, - "Acomdata", - "CF", - US_SC_SCSI, US_PR_DATAFAB, NULL, - US_FL_SINGLE_LUN ), + "Acomdata", + "CF", + US_SC_SCSI, US_PR_DATAFAB, NULL, + US_FL_SINGLE_LUN ), #endif #ifdef CONFIG_USB_STORAGE_SDDR55 UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, - "Acomdata", - "SM", - US_SC_SCSI, US_PR_SDDR55, NULL, - US_FL_SINGLE_LUN ), + "Acomdata", + "SM", + US_SC_SCSI, US_PR_SDDR55, NULL, + US_FL_SINGLE_LUN ), #endif /* Submitted by: Nick Sillik <n.sillik@temple.edu> diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 8e898e3..bef8bcd 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -191,16 +191,13 @@ static int storage_suspend(struct usb_interface *iface, pm_message_t message) { struct us_data *us = usb_get_intfdata(iface); + US_DEBUGP("%s\n", __FUNCTION__); + /* Wait until no command is running */ mutex_lock(&us->dev_mutex); - US_DEBUGP("%s\n", __FUNCTION__); if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_SUSPEND); - iface->dev.power.power_state.event = message.event; - - /* When runtime PM is working, we'll set a flag to indicate - * whether we should autoresume when a SCSI request arrives. */ mutex_unlock(&us->dev_mutex); return 0; @@ -210,14 +207,25 @@ static int storage_resume(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); - mutex_lock(&us->dev_mutex); - US_DEBUGP("%s\n", __FUNCTION__); + if (us->suspend_resume_hook) (us->suspend_resume_hook)(us, US_RESUME); - iface->dev.power.power_state.event = PM_EVENT_ON; - mutex_unlock(&us->dev_mutex); + return 0; +} + +static int storage_reset_resume(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + + US_DEBUGP("%s\n", __FUNCTION__); + + /* Report the reset to the SCSI core */ + usb_stor_report_bus_reset(us); + + /* FIXME: Notify the subdrivers that they need to reinitialize + * the device */ return 0; } @@ -228,7 +236,7 @@ static int storage_resume(struct usb_interface *iface) * a USB port reset, whether from this driver or a different one. */ -static void storage_pre_reset(struct usb_interface *iface) +static int storage_pre_reset(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); @@ -236,22 +244,23 @@ static void storage_pre_reset(struct usb_interface *iface) /* Make sure no command runs during the reset */ mutex_lock(&us->dev_mutex); + return 0; } -static void storage_post_reset(struct usb_interface *iface) +static int storage_post_reset(struct usb_interface *iface) { struct us_data *us = usb_get_intfdata(iface); US_DEBUGP("%s\n", __FUNCTION__); /* Report the reset to the SCSI core */ - scsi_lock(us_to_host(us)); usb_stor_report_bus_reset(us); - scsi_unlock(us_to_host(us)); /* FIXME: Notify the subdrivers that they need to reinitialize * the device */ + mutex_unlock(&us->dev_mutex); + return 0; } /* @@ -300,6 +309,7 @@ static int usb_stor_control_thread(void * __us) { struct us_data *us = (struct us_data *)__us; struct Scsi_Host *host = us_to_host(us); + int autopm_rc; current->flags |= PF_NOFREEZE; @@ -310,6 +320,9 @@ static int usb_stor_control_thread(void * __us) US_DEBUGP("*** thread awakened.\n"); + /* Autoresume the device */ + autopm_rc = usb_autopm_get_interface(us->pusb_intf); + /* lock the device pointers */ mutex_lock(&(us->dev_mutex)); @@ -368,6 +381,12 @@ static int usb_stor_control_thread(void * __us) us->srb->result = SAM_STAT_GOOD; } + /* Did the autoresume fail? */ + else if (autopm_rc < 0) { + US_DEBUGP("Could not wake device\n"); + us->srb->result = DID_ERROR << 16; + } + /* we've got a command, let's do it! */ else { US_DEBUG(usb_stor_show_command(us->srb)); @@ -410,25 +429,21 @@ SkipForAbort: /* unlock the device pointers */ mutex_unlock(&us->dev_mutex); - } /* for (;;) */ - scsi_host_put(host); + /* Start an autosuspend */ + if (autopm_rc == 0) + usb_autopm_put_interface(us->pusb_intf); + } /* for (;;) */ - /* notify the exit routine that we're actually exiting now - * - * complete()/wait_for_completion() is similar to up()/down(), - * except that complete() is safe in the case where the structure - * is getting deleted in a parallel mode of execution (i.e. just - * after the down() -- that's necessary for the thread-shutdown - * case. - * - * complete_and_exit() goes even further than this -- it is safe in - * the case that the thread of the caller is going away (not just - * the structure) -- this is necessary for the module-remove case. - * This is important in preemption kernels, which transfer the flow - * of execution immediately upon a complete(). - */ - complete_and_exit(&threads_gone, 0); + /* Wait until we are told to stop */ + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + return 0; } /*********************************************************************** @@ -796,19 +811,13 @@ static int usb_stor_acquire_resources(struct us_data *us) } /* Start up our control thread */ - th = kthread_create(usb_stor_control_thread, us, "usb-storage"); + th = kthread_run(usb_stor_control_thread, us, "usb-storage"); if (IS_ERR(th)) { printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n"); return PTR_ERR(th); } - - /* Take a reference to the host for the control thread and - * count it among all the threads we have launched. Then - * start it up. */ - scsi_host_get(us_to_host(us)); - atomic_inc(&total_threads); - wake_up_process(th); + us->ctl_thread = th; return 0; } @@ -825,6 +834,8 @@ static void usb_stor_release_resources(struct us_data *us) US_DEBUGP("-- sending exit command to thread\n"); set_bit(US_FLIDX_DISCONNECTING, &us->flags); up(&us->sema); + if (us->ctl_thread) + kthread_stop(us->ctl_thread); /* Call the destructor routine, if it exists */ if (us->extra_destructor) { @@ -938,6 +949,7 @@ retry: } scsi_host_put(us_to_host(us)); + usb_autopm_put_interface(us->pusb_intf); complete_and_exit(&threads_gone, 0); } @@ -1027,6 +1039,7 @@ static int storage_probe(struct usb_interface *intf, * start it up. */ scsi_host_get(us_to_host(us)); atomic_inc(&total_threads); + usb_autopm_get_interface(intf); /* dropped in the scanning thread */ wake_up_process(th); return 0; @@ -1059,10 +1072,12 @@ static struct usb_driver usb_storage_driver = { #ifdef CONFIG_PM .suspend = storage_suspend, .resume = storage_resume, + .reset_resume = storage_reset_resume, #endif .pre_reset = storage_pre_reset, .post_reset = storage_post_reset, .id_table = storage_usb_ids, + .supports_autosuspend = 1, }; static int __init usb_stor_init(void) diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 6dac1ff..6445665 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -144,6 +144,7 @@ struct us_data { unsigned char *sensebuf; /* sense data buffer */ dma_addr_t cr_dma; /* buffer DMA addresses */ dma_addr_t iobuf_dma; + struct task_struct *ctl_thread; /* the control thread */ /* mutual exclusion and synchronization structures */ struct semaphore sema; /* to sleep thread on */ diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 8432bf1..8de11de 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -34,9 +34,6 @@ static struct usb_device_id skel_table [] = { }; MODULE_DEVICE_TABLE(usb, skel_table); -/* to prevent a race between open and disconnect */ -static DEFINE_MUTEX(skel_open_lock); - /* Get a minor range for your devices from the usb maintainer */ #define USB_SKEL_MINOR_BASE 192 @@ -54,16 +51,21 @@ struct usb_skel { struct usb_device *udev; /* the usb device for this device */ struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ + struct usb_anchor submitted; /* in case we need to retract our submissions */ unsigned char *bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + int errors; /* the last request tanked */ + int open_count; /* count the number of openers */ + spinlock_t err_lock; /* lock for errors */ struct kref kref; struct mutex io_mutex; /* synchronize I/O with disconnect */ }; #define to_skel_dev(d) container_of(d, struct usb_skel, kref) static struct usb_driver skel_driver; +static void skel_draw_down(struct usb_skel *dev); static void skel_delete(struct kref *kref) { @@ -83,10 +85,8 @@ static int skel_open(struct inode *inode, struct file *file) subminor = iminor(inode); - mutex_lock(&skel_open_lock); interface = usb_find_interface(&skel_driver, subminor); if (!interface) { - mutex_unlock(&skel_open_lock); err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; @@ -95,22 +95,33 @@ static int skel_open(struct inode *inode, struct file *file) dev = usb_get_intfdata(interface); if (!dev) { - mutex_unlock(&skel_open_lock); retval = -ENODEV; goto exit; } /* increment our usage count for the device */ kref_get(&dev->kref); - /* now we can drop the lock */ - mutex_unlock(&skel_open_lock); - /* prevent the device from being autosuspended */ - retval = usb_autopm_get_interface(interface); - if (retval) { + /* lock the device to allow correctly handling errors + * in resumption */ + mutex_lock(&dev->io_mutex); + + if (!dev->open_count++) { + retval = usb_autopm_get_interface(interface); + if (retval) { + dev->open_count--; + mutex_unlock(&dev->io_mutex); + kref_put(&dev->kref, skel_delete); + goto exit; + } + } /* else { //uncomment this block if you want exclusive open + retval = -EBUSY; + dev->open_count--; + mutex_unlock(&dev->io_mutex); kref_put(&dev->kref, skel_delete); goto exit; - } + } */ + /* prevent the device from being autosuspended */ /* save our object in the file's private structure */ file->private_data = dev; @@ -129,7 +140,7 @@ static int skel_release(struct inode *inode, struct file *file) /* allow the device to be autosuspended */ mutex_lock(&dev->io_mutex); - if (dev->interface) + if (!--dev->open_count && dev->interface) usb_autopm_put_interface(dev->interface); mutex_unlock(&dev->io_mutex); @@ -138,6 +149,30 @@ static int skel_release(struct inode *inode, struct file *file) return 0; } +static int skel_flush(struct file *file, fl_owner_t id) +{ + struct usb_skel *dev; + int res; + + dev = (struct usb_skel *)file->private_data; + if (dev == NULL) + return -ENODEV; + + /* wait for io to stop */ + mutex_lock(&dev->io_mutex); + skel_draw_down(dev); + + /* read out errors, leave subsequent opens a clean slate */ + spin_lock_irq(&dev->err_lock); + res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0; + dev->errors = 0; + spin_unlock_irq(&dev->err_lock); + + mutex_unlock(&dev->io_mutex); + + return res; +} + static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct usb_skel *dev; @@ -179,12 +214,16 @@ static void skel_write_bulk_callback(struct urb *urb) dev = (struct usb_skel *)urb->context; /* sync/async unlink faults aren't errors */ - if (urb->status && - !(urb->status == -ENOENT || - urb->status == -ECONNRESET || - urb->status == -ESHUTDOWN)) { - err("%s - nonzero write bulk status received: %d", - __FUNCTION__, urb->status); + if (urb->status) { + if(!(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) + err("%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + + spin_lock(&dev->err_lock); + dev->errors = urb->status; + spin_unlock(&dev->err_lock); } /* free up our allocated buffer */ @@ -213,6 +252,17 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou goto exit; } + spin_lock_irq(&dev->err_lock); + if ((retval = dev->errors) < 0) { + /* any error is reported once */ + dev->errors = 0; + /* to preserve notifications about reset */ + retval = (retval == -EPIPE) ? retval : -EIO; + } + spin_unlock_irq(&dev->err_lock); + if (retval < 0) + goto error; + /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { @@ -244,13 +294,14 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), buf, writesize, skel_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->submitted); /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); mutex_unlock(&dev->io_mutex); if (retval) { err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); - goto error; + goto error_unanchor; } /* release our reference to this urb, the USB core will eventually free it entirely */ @@ -259,6 +310,8 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou return writesize; +error_unanchor: + usb_unanchor_urb(urb); error: if (urb) { usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); @@ -276,6 +329,7 @@ static const struct file_operations skel_fops = { .write = skel_write, .open = skel_open, .release = skel_release, + .flush = skel_flush, }; /* @@ -306,6 +360,8 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i kref_init(&dev->kref); sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); mutex_init(&dev->io_mutex); + spin_lock_init(&dev->err_lock); + init_usb_anchor(&dev->submitted); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; @@ -368,22 +424,18 @@ static void skel_disconnect(struct usb_interface *interface) struct usb_skel *dev; int minor = interface->minor; - /* prevent skel_open() from racing skel_disconnect() */ - mutex_lock(&skel_open_lock); - dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); /* give back our minor */ usb_deregister_dev(interface, &skel_class); - mutex_unlock(&skel_open_lock); /* prevent more I/O from starting */ mutex_lock(&dev->io_mutex); dev->interface = NULL; mutex_unlock(&dev->io_mutex); - + usb_kill_anchored_urbs(&dev->submitted); /* decrement our usage count */ kref_put(&dev->kref, skel_delete); @@ -391,10 +443,59 @@ static void skel_disconnect(struct usb_interface *interface) info("USB Skeleton #%d now disconnected", minor); } +static void skel_draw_down(struct usb_skel *dev) +{ + int time; + + time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000); + if (!time) + usb_kill_anchored_urbs(&dev->submitted); +} + +static int skel_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + if (!dev) + return 0; + skel_draw_down(dev); + return 0; +} + +static int skel_resume (struct usb_interface *intf) +{ + return 0; +} + +static int skel_pre_reset(struct usb_interface *intf) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + mutex_lock(&dev->io_mutex); + skel_draw_down(dev); + + return 0; +} + +static int skel_post_reset(struct usb_interface *intf) +{ + struct usb_skel *dev = usb_get_intfdata(intf); + + /* we are sure no URBs are active - no locking needed */ + dev->errors = -EPIPE; + mutex_unlock(&dev->io_mutex); + + return 0; +} + static struct usb_driver skel_driver = { .name = "skeleton", .probe = skel_probe, .disconnect = skel_disconnect, + .suspend = skel_suspend, + .resume = skel_resume, + .pre_reset = skel_pre_reset, + .post_reset = skel_post_reset, .id_table = skel_table, .supports_autosuspend = 1, }; diff --git a/include/asm-arm/arch-ixp4xx/udc.h b/include/asm-arm/arch-ixp4xx/udc.h index 79b850a..dbdec36f 100644 --- a/include/asm-arm/arch-ixp4xx/udc.h +++ b/include/asm-arm/arch-ixp4xx/udc.h @@ -6,25 +6,3 @@ extern void ixp4xx_set_udc_info(struct pxa2xx_udc_mach_info *info); -static inline int udc_gpio_to_irq(unsigned gpio) -{ - return 0; -} - -static inline void udc_gpio_init_vbus(unsigned gpio) -{ -} - -static inline void udc_gpio_init_pullup(unsigned gpio) -{ -} - -static inline int udc_gpio_get(unsigned gpio) -{ - return 0; -} - -static inline void udc_gpio_set(unsigned gpio, int is_on) -{ -} - diff --git a/include/asm-arm/arch-pxa/udc.h b/include/asm-arm/arch-pxa/udc.h index 8bc6f9c..27aa3a9 100644 --- a/include/asm-arm/arch-pxa/udc.h +++ b/include/asm-arm/arch-pxa/udc.h @@ -1,41 +1,8 @@ /* * linux/include/asm-arm/arch-pxa/udc.h * - * This supports machine-specific differences in how the PXA2xx - * USB Device Controller (UDC) is wired. - * */ #include <asm/mach/udc_pxa2xx.h> extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info); -static inline int udc_gpio_to_irq(unsigned gpio) -{ - return IRQ_GPIO(gpio & GPIO_MD_MASK_NR); -} - -static inline void udc_gpio_init_vbus(unsigned gpio) -{ - pxa_gpio_mode((gpio & GPIO_MD_MASK_NR) | GPIO_IN); -} - -static inline void udc_gpio_init_pullup(unsigned gpio) -{ - pxa_gpio_mode((gpio & GPIO_MD_MASK_NR) | GPIO_OUT | GPIO_DFLT_LOW); -} - -static inline int udc_gpio_get(unsigned gpio) -{ - return (GPLR(gpio) & GPIO_bit(gpio)) != 0; -} - -static inline void udc_gpio_set(unsigned gpio, int is_on) -{ - int mask = GPIO_bit(gpio); - - if (is_on) - GPSR(gpio) = mask; - else - GPCR(gpio) = mask; -} - diff --git a/include/linux/usb.h b/include/linux/usb.h index 56aa2ee..7a60946 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -146,6 +146,10 @@ struct usb_interface { * active alternate setting */ unsigned num_altsetting; /* number of alternate settings */ + /* If there is an interface association descriptor then it will list + * the associated interfaces */ + struct usb_interface_assoc_descriptor *intf_assoc; + int minor; /* minor number this interface is * bound to */ enum usb_interface_condition condition; /* state of binding */ @@ -175,6 +179,7 @@ void usb_put_intf(struct usb_interface *intf); /* this maximum is arbitrary */ #define USB_MAXINTERFACES 32 +#define USB_MAXIADS USB_MAXINTERFACES/2 /** * struct usb_interface_cache - long-term representation of a device interface @@ -245,6 +250,11 @@ struct usb_host_config { struct usb_config_descriptor desc; char *string; /* iConfiguration string, if present */ + + /* List of any Interface Association Descriptors in this + * configuration. */ + struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; + /* the interfaces associated with this configuration, * stored in no particular order */ struct usb_interface *interface[USB_MAXINTERFACES]; @@ -403,6 +413,8 @@ struct usb_device { unsigned auto_pm:1; /* autosuspend/resume in progress */ unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */ + unsigned reset_resume:1; /* needs reset instead of resume */ + unsigned persist_enabled:1; /* USB_PERSIST enabled for this dev */ unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ unsigned autoresume_disabled:1; /* disabled by the user */ #endif @@ -770,6 +782,28 @@ static inline int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, .bInterfaceClass = (cl), \ .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr) +/** + * USB_DEVICE_AND_INTERFACE_INFO - macro used to describe a specific usb device + * with a class of usb interfaces + * @vend: the 16 bit USB Vendor ID + * @prod: the 16 bit USB Product ID + * @cl: bInterfaceClass value + * @sc: bInterfaceSubClass value + * @pr: bInterfaceProtocol value + * + * This macro is used to create a struct usb_device_id that matches a + * specific device with a specific class of interfaces. + * + * This is especially useful when explicitly matching devices that have + * vendor specific bDeviceClass values, but standards-compliant interfaces. + */ +#define USB_DEVICE_AND_INTERFACE_INFO(vend,prod,cl,sc,pr) \ + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \ + | USB_DEVICE_ID_MATCH_DEVICE, \ + .idVendor = (vend), .idProduct = (prod), \ + .bInterfaceClass = (cl), \ + .bInterfaceSubClass = (sc), .bInterfaceProtocol = (pr) + /* ----------------------------------------------------------------------- */ /* Stuff for dynamic usb ids */ @@ -816,10 +850,15 @@ struct usbdrv_wrap { * do (or don't) show up otherwise in the filesystem. * @suspend: Called when the device is going to be suspended by the system. * @resume: Called when the device is being resumed by the system. + * @reset_resume: Called when the suspended device has been reset instead + * of being resumed. * @pre_reset: Called by usb_reset_composite_device() when the device * is about to be reset. * @post_reset: Called by usb_reset_composite_device() after the device - * has been reset. + * has been reset, or in lieu of @resume following a reset-resume + * (i.e., the device is reset instead of being resumed, as might + * happen if power was lost). The second argument tells which is + * the reason. * @id_table: USB drivers use ID table to support hotplugging. * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set * or your driver's probe function will never get called. @@ -859,9 +898,10 @@ struct usb_driver { int (*suspend) (struct usb_interface *intf, pm_message_t message); int (*resume) (struct usb_interface *intf); + int (*reset_resume)(struct usb_interface *intf); - void (*pre_reset) (struct usb_interface *intf); - void (*post_reset) (struct usb_interface *intf); + int (*pre_reset)(struct usb_interface *intf); + int (*post_reset)(struct usb_interface *intf); const struct usb_device_id *id_table; @@ -964,6 +1004,7 @@ extern int usb_disabled(void); #define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */ #define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt * needed */ +#define URB_FREE_BUFFER 0x0100 /* Free transfer buffer with the URB */ struct usb_iso_packet_descriptor { unsigned int offset; @@ -974,11 +1015,26 @@ struct usb_iso_packet_descriptor { struct urb; +struct usb_anchor { + struct list_head urb_list; + wait_queue_head_t wait; + spinlock_t lock; +}; + +static inline void init_usb_anchor(struct usb_anchor *anchor) +{ + INIT_LIST_HEAD(&anchor->urb_list); + init_waitqueue_head(&anchor->wait); + spin_lock_init(&anchor->lock); +} + typedef void (*usb_complete_t)(struct urb *); /** * struct urb - USB Request Block * @urb_list: For use by current owner of the URB. + * @anchor_list: membership in the list of an anchor + * @anchor: to anchor URBs to a common mooring * @pipe: Holds endpoint number, direction, type, and more. * Create these values with the eight macros available; * usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl" @@ -1151,6 +1207,8 @@ struct urb /* public: documented fields in the urb that can be used by drivers */ struct list_head urb_list; /* list head for use by the urb's * current owner */ + struct list_head anchor_list; /* the URB may be anchored by the driver */ + struct usb_anchor *anchor; struct usb_device *dev; /* (in) pointer to associated device */ unsigned int pipe; /* (in) pipe information */ int status; /* (return) non-ISO status */ @@ -1286,6 +1344,11 @@ extern struct urb *usb_get_urb(struct urb *urb); extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_unlink_urb(struct urb *urb); extern void usb_kill_urb(struct urb *urb); +extern void usb_kill_anchored_urbs(struct usb_anchor *anchor); +extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor); +extern void usb_unanchor_urb(struct urb *urb); +extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, + unsigned int timeout); void *usb_buffer_alloc (struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma); diff --git a/include/linux/usb/Kbuild b/include/linux/usb/Kbuild index 43f160c..6ce42bf 100644 --- a/include/linux/usb/Kbuild +++ b/include/linux/usb/Kbuild @@ -1,5 +1,6 @@ unifdef-y += audio.h unifdef-y += cdc.h unifdef-y += ch9.h +unifdef-y += gadgetfs.h unifdef-y += midi.h diff --git a/include/linux/usb_gadgetfs.h b/include/linux/usb/gadgetfs.h index 8086d5a..e8654c3 100644 --- a/include/linux/usb_gadgetfs.h +++ b/include/linux/usb/gadgetfs.h @@ -1,3 +1,5 @@ +#ifndef __LINUX_USB_GADGETFS_H +#define __LINUX_USB_GADGETFS_H #include <asm/types.h> #include <asm/ioctl.h> @@ -7,11 +9,12 @@ /* * Filesystem based user-mode API to USB Gadget controller hardware * - * Almost everything can be done with only read and write operations, + * Other than ep0 operations, most things are done by read() and write() * on endpoint files found in one directory. They are configured by * writing descriptors, and then may be used for normal stream style * i/o requests. When ep0 is configured, the device can enumerate; - * when it's closed, the device disconnects from usb. + * when it's closed, the device disconnects from usb. Operations on + * ep0 require ioctl() operations. * * Configuration and device descriptors get written to /dev/gadget/$CHIP, * which may then be used to read usb_gadgetfs_event structs. The driver @@ -21,9 +24,9 @@ */ /* - * Events are delivered on the ep0 file descriptor, if the user mode driver + * Events are delivered on the ep0 file descriptor, when the user mode driver * reads from this file descriptor after writing the descriptors. Don't - * stop polling this descriptor, if you write that kind of driver. + * stop polling this descriptor. */ enum usb_gadgetfs_event_type { @@ -36,8 +39,10 @@ enum usb_gadgetfs_event_type { // and likely more ! }; +/* NOTE: this structure must stay the same size and layout on + * both 32-bit and 64-bit kernels. + */ struct usb_gadgetfs_event { - enum usb_gadgetfs_event_type type; union { // NOP, DISCONNECT, SUSPEND: nothing // ... some hardware can't report disconnection @@ -46,19 +51,20 @@ struct usb_gadgetfs_event { enum usb_device_speed speed; // SETUP: packet; DATA phase i/o precedes next event - // (setup.bmRequestType & USB_DIR_IN) flags direction + // (setup.bmRequestType & USB_DIR_IN) flags direction // ... includes SET_CONFIGURATION, SET_INTERFACE struct usb_ctrlrequest setup; } u; + enum usb_gadgetfs_event_type type; }; /* endpoint ioctls */ /* IN transfers may be reported to the gadget driver as complete - * when the fifo is loaded, before the host reads the data; + * when the fifo is loaded, before the host reads the data; * OUT transfers may be reported to the host's "client" driver as - * complete when they're sitting in the FIFO unread. + * complete when they're sitting in the FIFO unread. * THIS returns how many bytes are "unclaimed" in the endpoint fifo * (needed for precise fault handling, when the hardware allows it) */ @@ -72,4 +78,4 @@ struct usb_gadgetfs_event { */ #define GADGETFS_CLEAR_HALT _IO('g',3) - +#endif /* __LINUX_USB_GADGETFS_H */ diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index 6bac8fa..8da374c 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -9,3 +9,6 @@ /* string descriptors must not be fetched using a 255-byte read */ #define USB_QUIRK_STRING_FETCH_255 0x00000002 + +/* device can't resume correctly so reset it instead */ +#define USB_QUIRK_RESET_RESUME 0x00000004 diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 32acbae..e8b8928 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -221,6 +221,9 @@ struct usb_serial_driver { int (*port_probe) (struct usb_serial_port *port); int (*port_remove) (struct usb_serial_port *port); + int (*suspend) (struct usb_serial *serial, pm_message_t message); + int (*resume) (struct usb_serial *serial); + /* serial function calls */ int (*open) (struct usb_serial_port *port, struct file * filp); void (*close) (struct usb_serial_port *port, struct file * filp); @@ -249,6 +252,9 @@ extern void usb_serial_port_softint(struct usb_serial_port *port); extern int usb_serial_probe(struct usb_interface *iface, const struct usb_device_id *id); extern void usb_serial_disconnect(struct usb_interface *iface); +extern int usb_serial_suspend(struct usb_interface *intf, pm_message_t message); +extern int usb_serial_resume(struct usb_interface *intf); + extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest); extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit); @@ -269,6 +275,7 @@ extern void usb_serial_put(struct usb_serial *serial); extern int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp); extern int usb_serial_generic_write (struct usb_serial_port *port, const unsigned char *buf, int count); extern void usb_serial_generic_close (struct usb_serial_port *port, struct file *filp); +extern int usb_serial_generic_resume (struct usb_serial *serial); extern int usb_serial_generic_write_room (struct usb_serial_port *port); extern int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port); extern void usb_serial_generic_read_bulk_callback (struct urb *urb); diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h index e17186d..4f59b2a 100644 --- a/include/linux/usb_gadget.h +++ b/include/linux/usb_gadget.h @@ -110,13 +110,6 @@ struct usb_ep_ops { gfp_t gfp_flags); void (*free_request) (struct usb_ep *ep, struct usb_request *req); - void *(*alloc_buffer) (struct usb_ep *ep, unsigned bytes, - dma_addr_t *dma, gfp_t gfp_flags); - void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma, - unsigned bytes); - // NOTE: on 2.6, drivers may also use dma_map() and - // dma_sync_single_*() to directly manage dma overhead. - int (*queue) (struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags); int (*dequeue) (struct usb_ep *ep, struct usb_request *req); @@ -235,47 +228,6 @@ usb_ep_free_request (struct usb_ep *ep, struct usb_request *req) } /** - * usb_ep_alloc_buffer - allocate an I/O buffer - * @ep:the endpoint associated with the buffer - * @len:length of the desired buffer - * @dma:pointer to the buffer's DMA address; must be valid - * @gfp_flags:GFP_* flags to use - * - * Returns a new buffer, or null if one could not be allocated. - * The buffer is suitably aligned for dma, if that endpoint uses DMA, - * and the caller won't have to care about dma-inconsistency - * or any hidden "bounce buffer" mechanism. No additional per-request - * DMA mapping will be required for such buffers. - * Free it later with usb_ep_free_buffer(). - * - * You don't need to use this call to allocate I/O buffers unless you - * want to make sure drivers don't incur costs for such "bounce buffer" - * copies or per-request DMA mappings. - */ -static inline void * -usb_ep_alloc_buffer (struct usb_ep *ep, unsigned len, dma_addr_t *dma, - gfp_t gfp_flags) -{ - return ep->ops->alloc_buffer (ep, len, dma, gfp_flags); -} - -/** - * usb_ep_free_buffer - frees an i/o buffer - * @ep:the endpoint associated with the buffer - * @buf:CPU view address of the buffer - * @dma:the buffer's DMA address - * @len:length of the buffer - * - * reverses the effect of usb_ep_alloc_buffer(). - * caller guarantees the buffer will no longer be accessed - */ -static inline void -usb_ep_free_buffer (struct usb_ep *ep, void *buf, dma_addr_t dma, unsigned len) -{ - ep->ops->free_buffer (ep, buf, dma, len); -} - -/** * usb_ep_queue - queues (submits) an I/O request to an endpoint. * @ep:the endpoint associated with the request * @req:the request being submitted |