diff -Nru a/CREDITS b/CREDITS --- a/CREDITS Wed Mar 27 16:40:07 2002 +++ b/CREDITS Wed Mar 27 16:40:07 2002 @@ -1916,6 +1916,7 @@ N: Petko Manolov E: petkan@users.sourceforge.net D: USB ethernet pegasus/pegasus-II driver +D: USB ethernet rtl8150 driver D: optimizing i[45]86 string routines D: i386 task switching hacks S: 482 Shadowgraph Dr. diff -Nru a/Documentation/devices.txt b/Documentation/devices.txt --- a/Documentation/devices.txt Wed Mar 27 16:40:08 2002 +++ b/Documentation/devices.txt Wed Mar 27 16:40:08 2002 @@ -2180,6 +2180,15 @@ ... 63 = /dev/usb/scanner15 16th USB scanner 64 = /dev/usb/rio500 Diamond Rio 500 + 80 = /dev/usb/dc2xx0 1st DC2xx camera device + ... + 95 = /dev/usb/dc2xx15 16th DC2xx camera device + 96 = /dev/usb/hiddev0 1st USB HID device + ... + 111 = /dev/usb/hiddev15 16th USB HID device + 112 = /dev/usb/auer0 1st auerswald ISDN device + ... + 127 = /dev/usb/auer15 16th auerswald ISDN device 181 char Conrad Electronic parallel port radio clocks 0 = /dev/pcfclock0 First Conrad radio clock diff -Nru a/Documentation/usb/hiddev.txt b/Documentation/usb/hiddev.txt --- a/Documentation/usb/hiddev.txt Wed Mar 27 16:40:06 2002 +++ b/Documentation/usb/hiddev.txt Wed Mar 27 16:40:06 2002 @@ -18,10 +18,10 @@ The data flow for a HID event produced by a device is something like the following : - usb.c ---> hid.c ----> input.c ----> [keyboard/mouse/joystick/event] - | - | - --> hiddev.c ----> POWER / MONITOR CONTROL + usb.c ---> hid-core.c ----> input.c ----> [keyboard/mouse/joystick/event] + | + | + --> hiddev.c ----> POWER / MONITOR CONTROL In addition, other subsystems (apart from USB) can potentially feed events into the input subsystem, but these have no effect on the hid @@ -63,12 +63,18 @@ The hiddev API uses a read() interface, and a set of ioctl() calls. +HID devices exchange data with the host computer using data +bundles called "reports". Each report is divided into "fields", +each of which can have one or more "usages". In the hid-core, +each one of these usages has a single signed 32 bit value. read(): -This is the event interface. When the HID device performs an -interrupt transfer, indicating a change of state, data will be made -available at the associated hiddev device with the content of a struct -hiddev_event: +This is the event interface. When the HID device's state changes, +it performs an interrupt transfer containing a report which contains +the changed value. The hid-core.c module parses the report, and +returns to hiddev.c the individual usages that have changed within +the report. In its basic mode, the hiddev will make these individual +usage changes available to the reader using a struct hiddev_event: struct hiddev_event { unsigned hid; @@ -78,7 +84,10 @@ containing the HID usage identifier for the status that changed, and the value that it was changed to. Note that the structure is defined within , along with some other useful #defines and -structures. +structures. The HID usage identifier is a composite of the HID usage +page shifted to the 16 high order bits ORed with the usage code. The +behavior of the read() function can be modified using the HIDIOCSFLAG +ioctl() described below. ioctl(): @@ -108,7 +117,9 @@ Instructs the kernel to retrieve all input and feature report values from the device. At this point, all the usage structures will contain current values for the device, and will maintain it as the device -changes. +changes. Note that the use of this ioctl is unnecessary in general, +since later kernels automatically initialize the reports from the +device at attach time. HIDIOCGNAME - string (variable length) Gets the device name @@ -116,12 +127,12 @@ HIDIOCGREPORT - struct hiddev_report_info (write) Instructs the kernel to get a feature or input report from the device, in order to selectively update the usage structures (in contrast to -INITREPORT). +INITREPORT). HIDIOCSREPORT - struct hiddev_report_info (write) Instructs the kernel to send a report to the device. This report can be filled in by the user through HIDIOCSUSAGE calls (below) to fill in -individual usage values in the report beforesending the report in full +individual usage values in the report before sending the report in full to the device. HIDIOCGREPORTINFO - struct hiddev_report_info (read/write) @@ -157,6 +168,25 @@ usage if it is found. HIDIOCSUSAGE - struct hiddev_usage_ref (write) -Sets the value of a usage in an output report. - - +Sets the value of a usage in an output report. The user fills in +the hiddev_usage_ref structure as above, but additionally fills in +the value field. + +HIDIOCGFLAG - int (read) +HIDIOCSFLAG - int (write) +These operations respectively inspect and replace the mode flags +that influence the read() call above. The flags are as follows: + + HIDDEV_FLAG_UREF - read() calls will now return + struct hiddev_usage_ref instead of struct hiddev_event. + This is a larger structure, but in situations where the + device has more than one usage in its reports with the + same usage code, this mode serves to resolve such + ambiguity. + + HIDDEV_FLAG_REPORT - This flag can only be used in conjunction + with HIDDEV_FLAG_UREF. With this flag set, when the device + sends a report, a struct hiddev_usage_ref will be returned + to read() filled in with the report_type and report_id, but + with field_index set to FIELD_INDEX_NONE. This serves as + additional notification when the device has sent a report. diff -Nru a/Documentation/usb/proc_usb_info.txt b/Documentation/usb/proc_usb_info.txt --- a/Documentation/usb/proc_usb_info.txt Wed Mar 27 16:40:05 2002 +++ b/Documentation/usb/proc_usb_info.txt Wed Mar 27 16:40:05 2002 @@ -1,32 +1,99 @@ /proc/bus/usb filesystem output =============================== -(version 2000.08.15) +(version 2002.03.19) -The /proc filesystem for USB devices generates -/proc/bus/usb/drivers and /proc/bus/usb/devices. +The /proc filesystem for USB devices provides /proc/bus/usb/drivers +and /proc/bus/usb/devices, as well as /proc/bus/usb/BBB/DDD files. -/proc/bus/usb/drivers lists the registered drivers, -one per line, with each driver's USB minor dev node -number range if applicable. -**NOTE**: If /proc/bus/usb appears empty, you need - to mount the filesystem, issue the command (as root): +**NOTE**: If /proc/bus/usb appears empty, and a host controller + driver has been linked, then you need to mount the + filesystem. Issue the command (as root): - mount -t usbdevfs none /proc/bus/usb + mount -t usbfs none /proc/bus/usb An alternative and more permanent method would be to add - none /proc/bus/usb usbdevfs defaults 0 0 + none /proc/bus/usb usbfs defaults 0 0 - to /etc/fstab. This will mount usbdevfs at each reboot. + to /etc/fstab. This will mount usbfs at each reboot. You can then issue `cat /proc/bus/usb/devices` to extract - USB device information. + USB device information, and user mode drivers can use usbfs + to interact with USB devices. -For more information on mounting the usbdevfs file system, see the + There are a number of mount options supported by usbfs. + Consult the source code (linux/drivers/usb/inode.c) for + information about those options. + +**NOTE**: The filesystem has been renamed from "usbdevfs" to + "usbfs", to reduce confusion with "devfs". You may + still see references to the older "usbdevfs" name. + +For more information on mounting the usbfs file system, see the "USB Device Filesystem" section of the USB Guide. The latest copy of the USB Guide can be found at http://www.linux-usb.org/ + +THE /proc/bus/usb/BBB/DDD FILES: +-------------------------------- +Each connected USB device has one file. The BBB indicates the bus +number. The DDD indicates the device address on that bus. Both +of these numbers are assigned sequentially, and can be reused, so +you can't rely on them for stable access to devices. For example, +it's relatively common for devices to re-enumerate while they are +still connected (perhaps someone jostled their power supply, hub, +or USB cable), so a device might be 002/027 when you first connect +it and 002/048 sometime later. + +These files can be read as binary data. The binary data consists +of first the device descriptor, then the descriptors for each +configuration of the device. That information is also shown in +text form by the /proc/bus/usb/devices file, described later. + +These files may also be used to write user-level drivers for the USB +devices. You would open the /proc/bus/usb/BBB/DDD file read/write, +read its descriptors to make sure it's the device you expect, and then +bind to an interface (or perhaps several) using an ioctl call. You +would issue more ioctls to the device to communicate to it using +control, bulk, or other kinds of USB transfers. The IOCTLs are +listed in the file, and at this writing the +source code (linux/drivers/usb/devio.c) is the primary reference +for how to access devices through those files. + +Note that since by default these BBB/DDD files are writable only by +root, only root can write such user mode drivers. You can selectively +grant read/write permissions to other users by using "chmod". Also, +usbfs mount options such as "devmode=0666" may be helpful. + + + +THE /proc/bus/usb/drivers FILE: +------------------------------- +Each of the USB device drivers linked into your kernel (statically, +or dynamically using "modprobe") is listed in the "drivers" file. +Here's an example from one system: + + usbdevfs + hub + 0- 15: usblp + usbnet + serial + usb-storage + pegasus + +If you see this file, "usbdevfs" and "hub" will always be listed, +since those are part of the "usbcore" framework. + +Drivers that use the USB major number (180) to provide character devices +will include a range of minor numbers, as shown above for the "usblp" +(actually "printer.o") module. USB device drivers can of course use any +major number, but it's easy to use the USB range since there's explicit +support for subdividing it in the USB device driver framework. + + +THE /proc/bus/usb/devices FILE: +------------------------------- In /proc/bus/usb/devices, each device's output has multiple lines of ASCII output. I made it ASCII instead of binary on purpose, so that someone @@ -34,8 +101,6 @@ auxiliary program. However, with an auxiliary program, the numbers in the first 4 columns of each "T:" line (topology info: Lev, Prnt, Port, Cnt) can be used to build a USB topology diagram. -(I think. I haven't proved this, but I have tested it with 3 -different topo/connections and it looked possible.) Each line is tagged with a one-character ID for that line: @@ -73,14 +138,30 @@ | |__Bus number |__Topology info tag + Speed may be: + 1.5 Mbit/s for low speed USB + 12 Mbit/s for full speed USB + 480 Mbit/s for high speed USB (added for USB 2.0) + Bandwidth info: B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd -| | | |__Number if isochronous requests +| | | |__Number of isochronous requests | | |__Number of interrupt requests | |__Total Bandwidth allocated to this bus |__Bandwidth info tag + Bandwidth allocation is an approximation of how much of one frame + (millisecond) is in use. It reflects only periodic transfers, which + are the only transfers that reserve bandwidth. Control and bulk + transfers use all other bandwidth, including reserved bandwidth that + is not used for transfers (such as for short packets). + + The percentage is how much of the "reserved" bandwidth is scheduled by + those transfers. For a low or full speed bus (loosely, "USB 1.1"), + 90% of the bus bandwidth is reserved. For a high speed bus (loosely, + "USB 2.0") 80% is reserved. + Device descriptor info & Product ID info: @@ -109,37 +190,55 @@ S: Manufacturer=ssss | |__Manufacturer of this device as read from the device. +| For USB host controller drivers (virtual root hubs) this may +| be omitted, or (for newer drivers) will identify the kernel +| version and the driver which provides this hub emulation. |__String info tag S: Product=ssss -| |__Product description of this device as read from the device, -| except that it is a generated string for USB host controllers -| (virtual root hubs), in the form "USB *HCI Root Hub". +| |__Product description of this device as read from the device. +| For older USB host controller drivers (virtual root hubs) this +| indicates the driver; for newer ones, it's a product (and vendor) +| description that often comes from the kernel's PCI ID database. |__String info tag S: SerialNumber=ssss -| |__Serial Number of this device as read from the device, -| except that it is a generated string for USB host controllers -| (virtual root hubs), and represents the host controller's -| unique identification in the system (currently I/O or -| memory-mapped base address). +| |__Serial Number of this device as read from the device. +| For USB host controller drivers (virtual root hubs) this is +| some unique ID, normally a bus ID (address or slot name) that +| can't be shared with any other device. |__String info tag + Configuration descriptor info: -C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA -| | | | |__MaxPower in mA -| | | |__Attributes -| | |__ConfiguratioNumber -| |__NumberOfInterfaces +C:* #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA +| | | | | |__MaxPower in mA +| | | | |__Attributes +| | | |__ConfiguratioNumber +| | |__NumberOfInterfaces +| |__ "*" indicates the active configuration (others are " ") |__Config info tag + + USB devices may have multiple configurations, each of which act + rather differently. For example, a bus-powered configuration + might be much less capable than one that is self-powered. Only + one device configuration can be active at a time; most devices + have only one configuration. + + Each configuration consists of one or more interfaces. Each + interface serves a distinct "function", which is typically bound + to a different USB device driver. One common example is a USB + speaker with an audio interface for playback, and a HID interface + for use with software volume control. Interface descriptor info (can be multiple per Config): I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss | | | | | | | |__Driver name +| | | | | | | or "(none)" | | | | | | |__InterfaceProtocol | | | | | |__InterfaceSubClass | | | | |__InterfaceClass @@ -148,16 +247,38 @@ | |__InterfaceNumber |__Interface info tag + A given interface may have one or more "alternate" settings. + For example, default settings may not use more than a small + amount of periodic bandwidth. To use significant fractions + of bus bandwidth, drivers must select a non-default altsetting. + + Only one setting for an interface may be active at a time, and + only one driver may bind to an interface at a time. Most devices + have only one alternate setting per interface. + Endpoint descriptor info (can be multiple per Interface): -E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms -E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms -| | | | |__Interval +E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddss +| | | | |__Interval (max) between transfers | | | |__EndpointMaxPacketSize | | |__Attributes(EndpointType) | |__EndpointAddress(I=In,O=Out) |__Endpoint info tag + + The interval is nonzero for all periodic (interrupt or isochronous) + endpoints. For high speed endpoints the transfer interval may be + measured in microseconds rather than milliseconds. + + For high speed periodic endpoints, the "MaxPacketSize" reflects + the per-microframe data transfer size. For "high bandwidth" + endpoints, that can reflect two or three packets (for up to + 3KBytes every 125 usec) per endpoint. + + With the Linux-USB stack, periodic bandwidth reservations use the + transfer intervals and sizes provided by URBs, which can be less + than those found in endpoint descriptor. + ======================================================================= diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Wed Mar 27 16:40:04 2002 +++ b/MAINTAINERS Wed Mar 27 16:40:04 2002 @@ -1660,6 +1660,7 @@ M: petkan@users.sourceforge.net L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net +W: http://pegasus2.sourceforge.net/ S: Maintained USB PRINTER DRIVER @@ -1667,6 +1668,14 @@ M: vojtech@suse.cz L: linux-usb-users@lists.sourceforge.net L: linux-usb-devel@lists.sourceforge.net +S: Maintained + +USB RTL8150 DRIVER +P: Petko Manolov +M: petkan@users.sourceforge.net +L: linux-usb-users@lists.sourceforge.net +L: linux-usb-devel@lists.sourceforge.net +W: http://pegasus2.sourceforge.net/ S: Maintained USB SE401 DRIVER diff -Nru a/drivers/usb/Config.help b/drivers/usb/Config.help --- a/drivers/usb/Config.help Wed Mar 27 16:40:04 2002 +++ b/drivers/usb/Config.help Wed Mar 27 16:40:04 2002 @@ -332,12 +332,22 @@ If in doubt then look at linux/drivers/usb/pegasus.h for the complete list of supported devices. If your particular adapter is not in the list and you are _sure_ it - is Pegasus or Pegasus II based then send me (pmanolov@users.sourceforge.net) + is Pegasus or Pegasus II based then send me (petkan@users.sourceforge.net) vendor and device IDs. This code is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called pegasus.o. If you want to compile it as a + module, say M here and read . + +CONFIG_USB_RTL8150 + Say Y here if you have RTL8150 based usb-ethernet adapter. + Send me (petkan@users.sourceforge.net) any comments you may have. + You can also check for updates at http://pegasus2.sourceforge.net/ + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called rtl8150.o. If you want to compile it as a module, say M here and read . CONFIG_USB_KAWETH diff -Nru a/drivers/usb/Config.in b/drivers/usb/Config.in --- a/drivers/usb/Config.in Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/Config.in Wed Mar 27 16:40:07 2002 @@ -88,6 +88,7 @@ comment ' Networking support is needed for USB Networking device support' else dep_tristate ' USB Pegasus/Pegasus-II based ethernet device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL + dep_tristate ' USB RTL8150 based ethernet device support (EXPERIMENTAL)' CONFIG_USB_RTL8150 $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB KLSI KL5USB101-based ethernet device support (EXPERIMENTAL)' CONFIG_USB_KAWETH $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CATC $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL dep_tristate ' USB Communication Class Ethernet device support (EXPERIMENTAL)' CONFIG_USB_CDCETHER $CONFIG_USB $CONFIG_NET $CONFIG_EXPERIMENTAL diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/Makefile Wed Mar 27 16:40:07 2002 @@ -76,6 +76,7 @@ obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_PEGASUS) += pegasus.o +obj-$(CONFIG_USB_RTL8150) += rtl8150.o obj-$(CONFIG_USB_CATC) += catc.o obj-$(CONFIG_USB_KAWETH) += kaweth.o obj-$(CONFIG_USB_CDCETHER) += CDCEther.o diff -Nru a/drivers/usb/devices.c b/drivers/usb/devices.c --- a/drivers/usb/devices.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/devices.c Wed Mar 27 16:40:09 2002 @@ -105,8 +105,8 @@ "I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; static char *format_endpt = -/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms */ - "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%3dms\n"; +/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ + "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n"; /* @@ -166,24 +166,71 @@ return (clas_info[ix].class_name); } -static char *usb_dump_endpoint_descriptor(char *start, char *end, const struct usb_endpoint_descriptor *desc) -{ - char *EndpointType [4] = {"Ctrl", "Isoc", "Bulk", "Int."}; +static char *usb_dump_endpoint_descriptor ( + int speed, + char *start, + char *end, + const struct usb_endpoint_descriptor *desc +) +{ + char dir, unit, *type; + unsigned interval, in, bandwidth = 1; if (start > end) return start; - start += sprintf(start, format_endpt, desc->bEndpointAddress, - (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL - ? 'B' : /* bidirectional */ - (desc->bEndpointAddress & USB_DIR_IN) ? 'I' : 'O', - desc->bmAttributes, EndpointType[desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK], - desc->wMaxPacketSize, desc->bInterval); - return start; -} + in = (desc->bEndpointAddress & USB_DIR_IN); + dir = in ? 'I' : 'O'; + if (speed == USB_SPEED_HIGH) { + switch (desc->wMaxPacketSize & (0x03 << 11)) { + case 1 << 11: bandwidth = 2; break; + case 2 << 11: bandwidth = 3; break; + } + } -static char *usb_dump_endpoint(char *start, char *end, const struct usb_endpoint_descriptor *endpoint) -{ - return usb_dump_endpoint_descriptor(start, end, endpoint); + /* this isn't checking for illegal values */ + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + type = "Ctrl"; + if (speed == USB_SPEED_HIGH) /* uframes per NAK */ + interval = desc->bInterval; + else + interval = 0; + dir = 'B'; /* ctrl is bidirectional */ + break; + case USB_ENDPOINT_XFER_ISOC: + type = "Isoc"; + interval = 1 << (desc->bInterval - 1); + break; + case USB_ENDPOINT_XFER_BULK: + type = "Bulk"; + if (speed == USB_SPEED_HIGH && !in) /* uframes per NAK */ + interval = desc->bInterval; + else + interval = 0; + break; + case USB_ENDPOINT_XFER_INT: + type = "Int."; + if (speed == USB_SPEED_HIGH) { + interval = 1 << (desc->bInterval - 1); + } else + interval = desc->bInterval; + break; + default: /* "can't happen" */ + return start; + } + interval *= (speed == USB_SPEED_HIGH) ? 125 : 1000; + if (interval % 1000) + unit = 'u'; + else { + unit = 'm'; + interval /= 1000; + } + + start += sprintf(start, format_endpt, desc->bEndpointAddress, dir, + desc->bmAttributes, type, + (desc->wMaxPacketSize & 0x07ff) * bandwidth, + interval, unit); + return start; } static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno) @@ -204,8 +251,13 @@ return start; } -static char *usb_dump_interface(char *start, char *end, const struct usb_interface *iface, int setno) -{ +static char *usb_dump_interface( + int speed, + char *start, + char *end, + const struct usb_interface *iface, + int setno +) { struct usb_interface_descriptor *desc = &iface->altsetting[setno]; int i; @@ -213,7 +265,8 @@ for (i = 0; i < desc->bNumEndpoints; i++) { if (start > end) return start; - start = usb_dump_endpoint(start, end, desc->endpoint + i); + start = usb_dump_endpoint_descriptor(speed, + start, end, desc->endpoint + i); } return start; } @@ -238,7 +291,13 @@ return start; } -static char *usb_dump_config(char *start, char *end, const struct usb_config_descriptor *config, int active) +static char *usb_dump_config ( + int speed, + char *start, + char *end, + const struct usb_config_descriptor *config, + int active +) { int i, j; struct usb_interface *interface; @@ -255,7 +314,8 @@ for (j = 0; j < interface->num_altsetting; j++) { if (start > end) return start; - start = usb_dump_interface(start, end, interface, j); + start = usb_dump_interface(speed, + start, end, interface, j); } } return start; @@ -336,8 +396,10 @@ for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { if (start > end) return start; - start = usb_dump_config(start, end, dev->config + i, - (dev->config + i) == dev->actconfig); /* active ? */ + start = usb_dump_config(dev->speed, + start, end, dev->config + i, + /* active ? */ + (dev->config + i) == dev->actconfig); } return start; } @@ -429,16 +491,27 @@ * count = device count at this level */ /* If this is the root hub, display the bandwidth information */ - /* FIXME high speed reserves 20% frametime for non-periodic, - * while full/low speed reserves only 10% ... so this is wrong - * for high speed busses. also, change how bandwidth is recorded. - */ - if (level == 0) - data_end += sprintf(data_end, format_bandwidth, bus->bandwidth_allocated, - FRAME_TIME_MAX_USECS_ALLOC, - (100 * bus->bandwidth_allocated + FRAME_TIME_MAX_USECS_ALLOC / 2) / FRAME_TIME_MAX_USECS_ALLOC, - bus->bandwidth_int_reqs, bus->bandwidth_isoc_reqs); + if (level == 0) { + int max; + + /* high speed reserves 80%, full/low reserves 90% */ + if (usbdev->speed == USB_SPEED_HIGH) + max = 800; + else + max = FRAME_TIME_MAX_USECS_ALLOC; + + /* report "average" periodic allocation over a microsecond. + * the schedules are actually bursty, HCDs need to deal with + * that and just compute/report this average. + */ + data_end += sprintf(data_end, format_bandwidth, + bus->bandwidth_allocated, max, + (100 * bus->bandwidth_allocated + max / 2) + / max, + bus->bandwidth_int_reqs, + bus->bandwidth_isoc_reqs); + } data_end = usb_dump_desc(data_end, pages_start + (2 * PAGE_SIZE) - 256, usbdev); if (data_end > (pages_start + (2 * PAGE_SIZE) - 256)) diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c --- a/drivers/usb/hcd/ehci-hcd.c Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/hcd/ehci-hcd.c Wed Mar 27 16:40:07 2002 @@ -89,12 +89,6 @@ // #define EHCI_VERBOSE_DEBUG // #define have_split_iso -#ifdef CONFIG_DEBUG_SLAB -# define EHCI_SLAB_FLAGS (SLAB_POISON) -#else -# define EHCI_SLAB_FLAGS 0 -#endif - /* magic numbers that can affect system performance */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */ @@ -197,7 +191,7 @@ * periodic_size can shrink by USBCMD update if hcc_params allows. */ ehci->periodic_size = DEFAULT_I_TDPS; - if ((retval = ehci_mem_init (ehci, EHCI_SLAB_FLAGS | SLAB_KERNEL)) < 0) + if ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0) return retval; hcc_params = readl (&ehci->caps->hcc_params); @@ -214,6 +208,7 @@ /* controller state: unknown --> reset */ /* EHCI spec section 4.1 */ + // FIXME require STS_HALT before reset... ehci_reset (ehci); writel (INTR_MASK, &ehci->regs->intr_enable); writel (ehci->periodic_dma, &ehci->regs->frame_list); @@ -221,16 +216,17 @@ /* * hcc_params controls whether ehci->regs->segment must (!!!) * be used; it constrains QH/ITD/SITD and QTD locations. - * By default, pci_alloc_consistent() won't hand out addresses - * above 4GB (via pdev->dma_mask) so we know this value. - * - * NOTE: that pdev->dma_mask setting means that all DMA mappings - * for I/O buffers will have the same restriction, though it's - * neither necessary nor desirable in that case. + * pci_pool consistent memory always uses segment zero. */ if (HCC_64BIT_ADDR (hcc_params)) { writel (0, &ehci->regs->segment); - info ("using segment 0 for 64bit DMA addresses ..."); + /* + * FIXME Enlarge pci_set_dma_mask() when possible. The DMA + * mapping API spec now says that'll affect only single shot + * mappings, and the pci_pool data will stay safe in seg 0. + * That's what we want: no extra copies for USB transfers. + */ + info ("restricting 64bit DMA mappings to segment 0 ..."); } /* clear interrupt enables, set irq latency */ @@ -240,6 +236,9 @@ temp |= 1 << (16 + log2_irq_thresh); // keeping default periodic framelist size temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE), + // Philips, Intel, and maybe others need CMD_RUN before the + // root hub will detect new devices (why?); NEC doesn't + temp |= CMD_RUN; writel (temp, &ehci->regs->command); dbg_cmd (ehci, "init", temp); @@ -266,7 +265,7 @@ readl (&ehci->regs->command); /* unblock posted write */ /* PCI Serial Bus Release Number is at 0x60 offset */ - pci_read_config_byte(hcd->pdev, 0x60, &tempbyte); + pci_read_config_byte (hcd->pdev, 0x60, &tempbyte); temp = readw (&ehci->caps->hci_version); info ("USB %x.%x support enabled, EHCI rev %x.%2x", ((tempbyte & 0xf0)>>4), @@ -393,6 +392,8 @@ // restore pci FLADJ value // khubd and drivers will set HC running, if needed; hcd->state = USB_STATE_READY; + // FIXME Philips/Intel/... etc don't really have a "READY" + // state ... turn on CMD_RUN too for (i = 0; i < ports; i++) { int temp = readl (&ehci->regs->port_status [i]); diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c --- a/drivers/usb/hcd/ehci-hub.c Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/hcd/ehci-hub.c Wed Mar 27 16:40:07 2002 @@ -309,13 +309,6 @@ hcd->bus_name, wIndex + 1); temp |= PORT_OWNER; } else { - /* Philips 1562 wants CMD_RUN to reset */ - if (!HCD_IS_RUNNING(ehci->hcd.state)) { - u32 cmd = readl (&ehci->regs->command); - cmd |= CMD_RUN; - writel (cmd, &ehci->regs->command); - ehci->hcd.state = USB_STATE_RUNNING; - } vdbg ("%s port %d reset", hcd->bus_name, wIndex + 1); temp |= PORT_RESET; diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c --- a/drivers/usb/hcd/ehci-mem.c Wed Mar 27 16:40:10 2002 +++ b/drivers/usb/hcd/ehci-mem.c Wed Mar 27 16:40:10 2002 @@ -224,8 +224,7 @@ ehci->periodic [i] = EHCI_LIST_END; /* software shadow of hardware table */ - ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), - flags & ~EHCI_SLAB_FLAGS); + ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags); if (ehci->pshadow == 0) { dbg ("no shadow periodic table"); ehci_mem_cleanup (ehci); diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c --- a/drivers/usb/hcd/ehci-q.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/hcd/ehci-q.c Wed Mar 27 16:40:09 2002 @@ -436,14 +436,6 @@ if (usb_pipecontrol (urb->pipe)) { /* control request data is passed in the "setup" pid */ - - /* NOTE: this isn't smart about 64bit DMA, since it uses the - * default (32bit) mask rather than using the whole address - * space. we could set pdev->dma_mask to all-ones while - * getting this mapping, locking it and restoring before - * allocating qtd/qh/... or maybe only do that for the main - * data phase (below). - */ qtd->buf_dma = pci_map_single ( ehci->hcd.pdev, urb->setup_packet, @@ -472,7 +464,6 @@ */ len = urb->transfer_buffer_length; if (likely (len > 0)) { - /* NOTE: sub-optimal mapping with 64bit DMA (see above) */ buf = map_buf = pci_map_single (ehci->hcd.pdev, urb->transfer_buffer, len, usb_pipein (urb->pipe) diff -Nru a/drivers/usb/hcd/ohci-dbg.c b/drivers/usb/hcd/ohci-dbg.c --- a/drivers/usb/hcd/ohci-dbg.c Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/hcd/ohci-dbg.c Wed Mar 27 16:40:08 2002 @@ -5,7 +5,7 @@ * (C) Copyright 2000-2002 David Brownell * * This file is licenced under the GPL. - * $Id: ohci-dbg.c,v 1.2 2002/01/19 00:15:45 dbrownell Exp $ + * $Id: ohci-dbg.c,v 1.4 2002/03/27 20:40:40 dbrownell Exp $ */ /*-------------------------------------------------------------------------*/ @@ -52,7 +52,7 @@ int i, len; if (usb_pipecontrol (pipe)) { - printk (KERN_DEBUG __FILE__ ": cmd(8):"); + printk (KERN_DEBUG __FILE__ ": setup(8):"); for (i = 0; i < 8 ; i++) printk (" %02x", ((__u8 *) urb->setup_packet) [i]); printk ("\n"); diff -Nru a/drivers/usb/hcd/ohci-hcd.c b/drivers/usb/hcd/ohci-hcd.c --- a/drivers/usb/hcd/ohci-hcd.c Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/hcd/ohci-hcd.c Wed Mar 27 16:40:08 2002 @@ -56,7 +56,7 @@ * v1.0 1999/04/27 initial release * * This file is licenced under the GPL. - * $Id: ohci-hcd.c,v 1.7 2002/01/19 00:20:56 dbrownell Exp $ + * $Id: ohci-hcd.c,v 1.9 2002/03/27 20:41:57 dbrownell Exp $ */ #include @@ -106,7 +106,7 @@ * - lots more testing!! */ -#define DRIVER_VERSION "$Revision: 1.7 $" +#define DRIVER_VERSION "$Revision: 1.9 $" #define DRIVER_AUTHOR "Roman Weissgaerber , David Brownell" #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" @@ -153,10 +153,8 @@ #endif /* every endpoint has a ed, locate and fill it */ - if (! (ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) { - usb_dec_dev_use (urb->dev); + if (! (ed = ep_add_ed (urb->dev, pipe, urb->interval, 1, mem_flags))) return -ENOMEM; - } /* for the private part of the URB we need the number of TDs (size) */ switch (usb_pipetype (pipe)) { @@ -181,10 +179,8 @@ break; case PIPE_ISOCHRONOUS: /* number of packets from URB */ size = urb->number_of_packets; - if (size <= 0) { - usb_dec_dev_use (urb->dev); + if (size <= 0) return -EINVAL; - } for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc [i].actual_length = 0; urb->iso_frame_desc [i].status = -EXDEV; @@ -198,10 +194,8 @@ /* allocate the private part of the URB */ urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (struct td *), mem_flags); - if (!urb_priv) { - usb_dec_dev_use (urb->dev); + if (!urb_priv) return -ENOMEM; - } memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (struct td *)); /* fill the private part of the URB */ @@ -216,7 +210,6 @@ urb_priv->length = i; urb_free_priv (ohci, urb_priv); spin_unlock_irqrestore (&ohci->lock, flags); - usb_dec_dev_use (urb->dev); return -ENOMEM; } } @@ -242,7 +235,6 @@ if (bustime < 0) { urb_free_priv (ohci, urb_priv); spin_unlock_irqrestore (&ohci->lock, flags); - usb_dec_dev_use (urb->dev); return bustime; } usb_claim_bandwidth (urb->dev, urb, @@ -356,9 +348,6 @@ { struct ohci_hcd *ohci = hcd_to_ohci (hcd); -#ifdef OHCI_VERBOSE_DEBUG - dbg ("%s: ohci_get_frame", hcd->bus_name); -#endif return le16_to_cpu (ohci->hcca->frame_no); } @@ -445,7 +434,7 @@ writel (ohci->hc_control, &ohci->regs->control); /* Choose the interrupts we care about now, others later on demand */ - mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH; writel (mask, &ohci->regs->intrstatus); writel (mask, &ohci->regs->intrenable); @@ -517,10 +506,7 @@ writel (OHCI_INTR_WDH, ®s->intrenable); } - if (ints & OHCI_INTR_SO) { - dbg ("USB Schedule overrun"); - writel (OHCI_INTR_SO, ®s->intrenable); - } + /* could track INTR_SO to reduce available PCI/... bandwidth */ // FIXME: this assumes SOF (1/ms) interrupts don't get lost... if (ints & OHCI_INTR_SF) { diff -Nru a/drivers/usb/hcd/ohci-hub.c b/drivers/usb/hcd/ohci-hub.c --- a/drivers/usb/hcd/ohci-hub.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/hcd/ohci-hub.c Wed Mar 27 16:40:09 2002 @@ -5,7 +5,7 @@ * (C) Copyright 2000-2002 David Brownell * * This file is licenced under GPL - * $Id: ohci-hub.c,v 1.2 2002/01/19 00:21:49 dbrownell Exp $ + * $Id: ohci-hub.c,v 1.3 2002/03/22 16:04:54 dbrownell Exp $ */ /*-------------------------------------------------------------------------*/ diff -Nru a/drivers/usb/hcd/ohci-mem.c b/drivers/usb/hcd/ohci-mem.c --- a/drivers/usb/hcd/ohci-mem.c Wed Mar 27 16:40:06 2002 +++ b/drivers/usb/hcd/ohci-mem.c Wed Mar 27 16:40:06 2002 @@ -5,7 +5,7 @@ * (C) Copyright 2000-2002 David Brownell * * This file is licenced under the GPL. - * $Id: ohci-mem.c,v 1.2 2002/01/19 00:22:13 dbrownell Exp $ + * $Id: ohci-mem.c,v 1.3 2002/03/22 16:04:54 dbrownell Exp $ */ /*-------------------------------------------------------------------------*/ @@ -42,12 +42,6 @@ /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_DEBUG_SLAB -# define OHCI_MEM_FLAGS SLAB_POISON -#else -# define OHCI_MEM_FLAGS 0 -#endif - #ifndef CONFIG_PCI # error "usb-ohci currently requires PCI-based controllers" /* to support non-PCI OHCIs, you need custom bus/mem/... glue */ @@ -169,14 +163,14 @@ sizeof (struct td), 32 /* byte alignment */, 0 /* no page-crossing issues */, - GFP_KERNEL | OHCI_MEM_FLAGS); + GFP_KERNEL); if (!ohci->td_cache) return -ENOMEM; ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev, sizeof (struct ed), 16 /* byte alignment */, 0 /* no page-crossing issues */, - GFP_KERNEL | OHCI_MEM_FLAGS); + GFP_KERNEL); if (!ohci->ed_cache) { pci_pool_destroy (ohci->td_cache); return -ENOMEM; diff -Nru a/drivers/usb/hcd/ohci-q.c b/drivers/usb/hcd/ohci-q.c --- a/drivers/usb/hcd/ohci-q.c Wed Mar 27 16:40:05 2002 +++ b/drivers/usb/hcd/ohci-q.c Wed Mar 27 16:40:05 2002 @@ -5,7 +5,7 @@ * (C) Copyright 2000-2002 David Brownell * * This file is licenced under the GPL. - * $Id: ohci-q.c,v 1.6 2002/01/19 00:23:15 dbrownell Exp $ + * $Id: ohci-q.c,v 1.8 2002/03/27 20:57:01 dbrownell Exp $ */ static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv) @@ -54,124 +54,81 @@ /* * URB goes back to driver, and isn't reissued. - * It's completely gone from HC data structures, so no locking - * is needed ... or desired! (Giveback can call back to hcd.) + * It's completely gone from HC data structures. + * PRECONDITION: no locks held (Giveback can call into HCD.) */ -static inline void finish_urb (struct ohci_hcd *ohci, struct urb *urb) +static void finish_urb (struct ohci_hcd *ohci, struct urb *urb) { - if (urb->hcpriv) { - urb_free_priv (ohci, urb->hcpriv); - urb->hcpriv = NULL; - } - usb_hcd_giveback_urb (&ohci->hcd, urb); -} - -static void td_submit_urb (struct urb *urb); - -/* - * URB is reported to driver, is reissued if it's periodic. - */ -static int return_urb (struct ohci_hcd *hc, struct urb *urb) -{ - urb_priv_t *urb_priv = urb->hcpriv; - struct urb *urbt; unsigned long flags; - int i; #ifdef DEBUG - if (!urb_priv) { + if (!urb->hcpriv) { err ("already unlinked!"); BUG (); } - - /* just to be sure */ - if (!urb->complete) { - err ("no completion!"); - BUG (); - } #endif + urb_free_priv (ohci, urb->hcpriv); + urb->hcpriv = NULL; + + spin_lock_irqsave (&urb->lock, flags); + if (likely (urb->status == -EINPROGRESS)) + urb->status = 0; + spin_unlock_irqrestore (&urb->lock, flags); + #ifdef OHCI_VERBOSE_DEBUG urb_print (urb, "RET", usb_pipeout (urb->pipe)); #endif + usb_hcd_giveback_urb (&ohci->hcd, urb); +} + +static void td_submit_urb (struct urb *urb); + +/* Report interrupt transfer completion, maybe reissue */ +static void intr_resub (struct ohci_hcd *hc, struct urb *urb) +{ + urb_priv_t *urb_priv = urb->hcpriv; + unsigned long flags; - switch (usb_pipetype (urb->pipe)) { - case PIPE_INTERRUPT: #ifdef CONFIG_PCI // FIXME rewrite this resubmit path. use pci_dma_sync_single() // and requeue more cheaply, and only if needed. - pci_unmap_single (hc->hcd.pdev, - urb_priv->td [0]->data_dma, - urb->transfer_buffer_length, - usb_pipeout (urb->pipe) - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); -#endif - /* FIXME: MP race. If another CPU partially unlinks - * this URB (urb->status was updated, hasn't yet told - * us to dequeue) before we call complete() here, an - * extra "unlinked" completion will be reported... - */ - urb->complete (urb); +// Better yet ... abolish the notion of automagic resubmission. + pci_unmap_single (hc->hcd.pdev, + urb_priv->td [0]->data_dma, + urb->transfer_buffer_length, + usb_pipeout (urb->pipe) + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); +#endif + /* FIXME: MP race. If another CPU partially unlinks + * this URB (urb->status was updated, hasn't yet told + * us to dequeue) before we call complete() here, an + * extra "unlinked" completion will be reported... + */ - /* always requeued, but ED_SKIP if complete() unlinks. - * removed from periodic table only at SOF intr. - */ - urb->actual_length = 0; - if (urb_priv->state != URB_DEL) - urb->status = -EINPROGRESS; - spin_lock_irqsave (&hc->lock, flags); - td_submit_urb (urb); - spin_unlock_irqrestore (&hc->lock, flags); - break; + spin_lock_irqsave (&urb->lock, flags); + if (likely (urb->status == -EINPROGRESS)) + urb->status = 0; + spin_unlock_irqrestore (&urb->lock, flags); - case PIPE_ISOCHRONOUS: - for (urbt = urb->next; - urbt && (urbt != urb); - urbt = urbt->next) - continue; - if (urbt) { /* send the reply and requeue URB */ -#ifdef CONFIG_PCI -// FIXME rewrite this resubmit path too - pci_unmap_single (hc->hcd.pdev, - urb_priv->td [0]->data_dma, - urb->transfer_buffer_length, - usb_pipeout (urb->pipe) - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); -#endif - urb->complete (urb); - spin_lock_irqsave (&hc->lock, flags); - urb->actual_length = 0; - urb->status = -EINPROGRESS; - urb->start_frame = urb_priv->ed->last_iso + 1; - if (urb_priv->state != URB_DEL) { - for (i = 0; i < urb->number_of_packets; - i++) { - urb->iso_frame_desc [i] - .actual_length = 0; - urb->iso_frame_desc [i] - .status = -EXDEV; - } - td_submit_urb (urb); - } -// FIXME if not deleted, should have been "finished" - spin_unlock_irqrestore (&hc->lock, flags); - - } else { /* not reissued */ - finish_urb (hc, urb); - } - break; +#ifdef OHCI_VERBOSE_DEBUG + urb_print (urb, "INTR", usb_pipeout (urb->pipe)); +#endif + urb->complete (urb); - /* - * C/B requests that get here are never reissued. - */ - case PIPE_BULK: - case PIPE_CONTROL: - finish_urb (hc, urb); - break; - } - return 0; + /* always requeued, but ED_SKIP if complete() unlinks. + * EDs are removed from periodic table only at SOF intr. + */ + urb->actual_length = 0; + spin_lock_irqsave (&urb->lock, flags); + if (urb_priv->state != URB_DEL) + urb->status = -EINPROGRESS; + spin_unlock (&urb->lock); + + spin_lock (&hc->lock); + td_submit_urb (urb); + spin_unlock_irqrestore (&hc->lock, flags); } @@ -329,6 +286,26 @@ /*-------------------------------------------------------------------------*/ +/* scan the periodic table to find and unlink this ED */ +static void periodic_unlink ( + struct ohci_hcd *ohci, + struct ed *ed, + unsigned index, + unsigned period +) { + for (; index < NUM_INTS; index += period) { + __u32 *ed_p = &ohci->hcca->int_table [index]; + + while (*ed_p != 0) { + if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { + *ed_p = ed->hwNextED; + break; + } + ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED); + } + } +} + /* unlink an ed from one of the HC chains. * just the link to the ed is unlinked. * the link from the ed still points to another operational ed or 0 @@ -336,11 +313,7 @@ */ static int ep_unlink (struct ohci_hcd *ohci, struct ed *ed) { - int int_branch; int i; - int inter; - int interval; - __u32 *ed_p; ed->hwINFO |= ED_SKIP; @@ -384,22 +357,9 @@ break; case PIPE_INTERRUPT: - int_branch = ed->int_branch; - interval = ed->int_interval; - - for (i = 0; i < ep_rev (6, interval); i += inter) { - for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i) + int_branch]), inter = 1; - (*ed_p != 0) && (*ed_p != ed->hwNextED); - ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED), - inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval)) { - if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { - *ed_p = ed->hwNextED; - break; - } - } - } - for (i = int_branch; i < NUM_INTS; i += interval) - ohci->ohci_int_load [i] -= ed->int_load; + periodic_unlink (ohci, ed, ed->int_branch, ed->int_interval); + for (i = ed->int_branch; i < NUM_INTS; i += ed->int_interval) + ohci->ohci_int_load [i] -= ed->int_load; #ifdef OHCI_VERBOSE_DEBUG ohci_dump_periodic (ohci, "UNLINK_INT"); #endif @@ -412,21 +372,10 @@ (dma_to_ed (ohci, le32_to_cpup (&ed->hwNextED))) ->ed_prev = ed->ed_prev; - if (ed->ed_prev != NULL) { + if (ed->ed_prev != NULL) ed->ed_prev->hwNextED = ed->hwNextED; - } else { - for (i = 0; i < NUM_INTS; i++) { - for (ed_p = & (ohci->hcca->int_table [ep_rev (5, i)]); - *ed_p != 0; - ed_p = & ((dma_to_ed (ohci, le32_to_cpup (ed_p)))->hwNextED)) { - // inter = ep_rev (6, (dma_to_ed (ohci, le32_to_cpup (ed_p)))->int_interval); - if ((dma_to_ed (ohci, le32_to_cpup (ed_p))) == ed) { - *ed_p = ed->hwNextED; - break; - } - } - } - } + else + periodic_unlink (ohci, ed, 0, 1); #ifdef OHCI_VERBOSE_DEBUG ohci_dump_periodic (ohci, "UNLINK_ISO"); #endif @@ -584,6 +533,12 @@ return; } +#if 0 + /* no interrupt needed except for URB's last TD */ + if (index != (urb_priv->length - 1)) + info |= TD_DI; +#endif + /* use this td as the next dummy */ td_pt = urb_priv->td [index]; td_pt->hwNextTD = 0; @@ -660,6 +615,9 @@ } else data = 0; + /* NOTE: TD_CC is set so we can tell which TDs the HC processed by + * using TD_CC_GET, as well as by seeing them on the done list. + */ switch (usb_pipetype (urb->pipe)) { case PIPE_BULK: info = usb_pipeout (urb->pipe) @@ -726,8 +684,14 @@ case PIPE_ISOCHRONOUS: for (cnt = 0; cnt < urb->number_of_packets; cnt++) { - td_fill (ohci, TD_CC | TD_ISO - | ((urb->start_frame + cnt) & 0xffff), + int frame = urb->start_frame; + + // FIXME scheduling should handle frame counter + // roll-around ... exotic case (and OHCI has + // a 2^16 iso range, vs other HCs max of 2^10) + frame += cnt * urb->interval; + frame &= 0xffff; + td_fill (ohci, TD_CC | TD_ISO | frame, data + urb->iso_frame_desc [cnt].offset, urb->iso_frame_desc [cnt].length, urb, cnt); } @@ -741,50 +705,77 @@ * Done List handling functions *-------------------------------------------------------------------------*/ -/* calculate the transfer length and update the urb */ - -static void dl_transfer_length (struct td *td) +/* calculate transfer length/status and update the urb + * PRECONDITION: irqsafe (only for urb->status locking) + */ +static void td_done (struct urb *urb, struct td *td) { - __u32 tdINFO, tdBE, tdCBP; - __u16 tdPSW; - struct urb *urb = td->urb; - urb_priv_t *urb_priv = urb->hcpriv; - int dlen = 0; - int cc = 0; - - tdINFO = le32_to_cpup (&td->hwINFO); - tdBE = le32_to_cpup (&td->hwBE); - tdCBP = le32_to_cpup (&td->hwCBP); + u32 tdINFO = le32_to_cpup (&td->hwINFO); + int cc = 0; + /* ISO ... drivers see per-TD length/status */ if (tdINFO & TD_ISO) { - tdPSW = le16_to_cpu (td->hwPSW [0]); + u16 tdPSW = le16_to_cpu (td->hwPSW [0]); + int dlen = 0; + cc = (tdPSW >> 12) & 0xF; - if (cc < 0xE) { - if (usb_pipeout (urb->pipe)) { - dlen = urb->iso_frame_desc [td->index].length; - } else { - dlen = tdPSW & 0x3ff; - } - urb->actual_length += dlen; - urb->iso_frame_desc [td->index].actual_length = dlen; - if (! (urb->transfer_flags & USB_DISABLE_SPD) - && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - urb->iso_frame_desc [td->index].status - = cc_to_error [cc]; - } - } else { /* BULK, INT, CONTROL DATA */ - if (! (usb_pipetype (urb->pipe) == PIPE_CONTROL && - ((td->index == 0) - || (td->index == urb_priv->length - 1)))) { - if (tdBE != 0) { - urb->actual_length += (td->hwCBP == 0) - ? (tdBE - td->data_dma + 1) - : (tdCBP - td->data_dma); - } - } + if (! ((urb->transfer_flags & USB_DISABLE_SPD) + && (cc == TD_DATAUNDERRUN))) + cc = TD_CC_NOERROR; + + if (usb_pipeout (urb->pipe)) + dlen = urb->iso_frame_desc [td->index].length; + else + dlen = tdPSW & 0x3ff; + urb->actual_length += dlen; + urb->iso_frame_desc [td->index].actual_length = dlen; + urb->iso_frame_desc [td->index].status = cc_to_error [cc]; + + if (cc != 0) + dbg (" urb %p iso TD %d len %d CC %d", + urb, td->index, dlen, cc); + + /* BULK, INT, CONTROL ... drivers see aggregate length/status, + * except that "setup" bytes aren't counted and "short" transfers + * might not be reported as errors. + */ + } else { + int type = usb_pipetype (urb->pipe); + u32 tdBE = le32_to_cpup (&td->hwBE); + + cc = TD_CC_GET (tdINFO); + + /* control endpoints only have soft stalls */ + if (type != PIPE_CONTROL && cc == TD_CC_STALL) + usb_endpoint_halt (urb->dev, + usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)); + + /* update packet status if needed (short may be ok) */ + if (((urb->transfer_flags & USB_DISABLE_SPD) != 0 + && cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + if (cc != TD_CC_NOERROR) { + spin_lock (&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = cc_to_error [cc]; + spin_unlock (&urb->lock); + } + + /* count all non-empty packets except control SETUP packet */ + if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) { + if (td->hwCBP == 0) + urb->actual_length += tdBE - td->data_dma + 1; + else + urb->actual_length += + le32_to_cpup (&td->hwCBP) + - td->data_dma; + } + + if (cc != 0) + dbg (" urb %p TD %d CC %d, len=%d", + urb, td->index, cc, urb->actual_length); } } @@ -811,13 +802,16 @@ if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { urb_priv = (urb_priv_t *) td_list->urb->hcpriv; - dbg (" USB-error/status: %x : %p", - TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), - td_list); - /* typically the endpoint halted too */ + /* typically the endpoint halts on error; un-halt, + * and maybe dequeue other TDs from this urb + */ if (td_list->ed->hwHeadP & ED_H) { if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { + dbg ("urb %p TD %d of %d, patch ED", + td_list->urb, + 1 + td_list->index, + urb_priv->length); td_list->ed->hwHeadP = (urb_priv->td [urb_priv->length - 1]->hwNextTD & __constant_cpu_to_le32 (TD_MASK)) @@ -870,16 +864,19 @@ le32_to_cpup (&td->hwNextTD)); if ((urb_priv->state == URB_DEL)) { tdINFO = le32_to_cpup (&td->hwINFO); + /* HC may have partly processed this TD */ if (TD_CC_GET (tdINFO) < 0xE) - dl_transfer_length (td); + td_done (urb, td); *td_p = td->hwNextTD | (*td_p & __constant_cpu_to_le32 (0x3)); /* URB is done; clean up */ - if (++ (urb_priv->td_cnt) == urb_priv->length) -// FIXME: we shouldn't hold ohci->lock here, else the -// completion function can't talk to this hcd ... + if (++ (urb_priv->td_cnt) == urb_priv->length) { + spin_unlock_irqrestore (&ohci->lock, + flags); finish_urb (ohci, urb); + spin_lock_irqsave (&ohci->lock, flags); + } } else { td_p = &td->hwNextTD; } @@ -931,71 +928,52 @@ /*-------------------------------------------------------------------------*/ /* - * process normal completions (error or success) and some unlinked eds - * this is the main path for handing urbs back to drivers + * Process normal completions (error or success) and clean the schedules. + * + * This is the main path for handing urbs back to drivers. The only other + * path is dl_del_list(), which unlinks URBs by scanning EDs, instead of + * scanning the (re-reversed) donelist as this does. */ -static void dl_done_list (struct ohci_hcd *ohci, struct td *td_list) +static void dl_done_list (struct ohci_hcd *ohci, struct td *td) { - struct td *td_list_next = NULL; - struct ed *ed; - int cc = 0; - struct urb *urb; - urb_priv_t *urb_priv; - __u32 tdINFO; - - unsigned long flags; - - while (td_list) { - td_list_next = td_list->next_dl_td; - - urb = td_list->urb; - urb_priv = urb->hcpriv; - tdINFO = le32_to_cpup (&td_list->hwINFO); - - ed = td_list->ed; - - dl_transfer_length (td_list); + unsigned long flags; - /* error code of transfer */ - cc = TD_CC_GET (tdINFO); - if (cc == TD_CC_STALL) - usb_endpoint_halt (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)); + spin_lock_irqsave (&ohci->lock, flags); + while (td) { + struct td *td_next = td->next_dl_td; + struct urb *urb = td->urb; + urb_priv_t *urb_priv = urb->hcpriv; + struct ed *ed = td->ed; + + /* update URB's length and status from TD */ + td_done (urb, td); + urb_priv->td_cnt++; + + /* If all this urb's TDs are done, call complete(). + * Interrupt transfers are the only special case: + * they're reissued, until "deleted" by usb_unlink_urb + * (real work done in a SOF intr, by dl_del_list). + */ + if (urb_priv->td_cnt == urb_priv->length) { + int resubmit; - if (! (urb->transfer_flags & USB_DISABLE_SPD) - && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; + resubmit = usb_pipeint (urb->pipe) + && (urb_priv->state != URB_DEL); - if (++ (urb_priv->td_cnt) == urb_priv->length) { - /* - * Except for periodic transfers, both branches do - * the same thing. Periodic urbs get reissued until - * they're "deleted" (in SOF intr) by usb_unlink_urb. - */ - if ((ed->state & (ED_OPER | ED_UNLINK)) - && (urb_priv->state != URB_DEL)) { - spin_lock (&urb->lock); - if (urb->status == -EINPROGRESS) - urb->status = cc_to_error [cc]; - spin_unlock (&urb->lock); - return_urb (ohci, urb); - } else + spin_unlock_irqrestore (&ohci->lock, flags); + if (resubmit) + intr_resub (ohci, urb); + else finish_urb (ohci, urb); + spin_lock_irqsave (&ohci->lock, flags); } - spin_lock_irqsave (&ohci->lock, flags); - if (ed->state != ED_NEW) { - u32 edHeadP = ed->hwHeadP; - - /* unlink eds if they are not busy */ - edHeadP &= __constant_cpu_to_le32 (ED_MASK); - if ((edHeadP == ed->hwTailP) && (ed->state == ED_OPER)) - ep_unlink (ohci, ed); - } - spin_unlock_irqrestore (&ohci->lock, flags); - - td_list = td_list_next; + /* clean schedule: unlink EDs that are no longer busy */ + if ((ed->hwHeadP & __constant_cpu_to_le32 (TD_MASK)) + == ed->hwTailP + && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + td = td_next; } + spin_unlock_irqrestore (&ohci->lock, flags); } - diff -Nru a/drivers/usb/hcd/ohci.h b/drivers/usb/hcd/ohci.h --- a/drivers/usb/hcd/ohci.h Wed Mar 27 16:40:10 2002 +++ b/drivers/usb/hcd/ohci.h Wed Mar 27 16:40:10 2002 @@ -5,7 +5,7 @@ * (C) Copyright 2000-2002 David Brownell * * This file is licenced under the GPL. - * $Id: ohci.h,v 1.5 2002/01/19 00:24:01 dbrownell Exp $ + * $Id: ohci.h,v 1.6 2002/03/22 16:04:54 dbrownell Exp $ */ /* diff -Nru a/drivers/usb/hcd.c b/drivers/usb/hcd.c --- a/drivers/usb/hcd.c Wed Mar 27 16:40:10 2002 +++ b/drivers/usb/hcd.c Wed Mar 27 16:40:10 2002 @@ -1356,6 +1356,7 @@ // NOTE usb and ohci handle up to 2^15 temp = 1024; } + break; default: return -EINVAL; } @@ -1374,7 +1375,7 @@ // hcd_monitor_hook(MONITOR_URB_SUBMIT, urb) // It would catch submission paths for all urbs. - /* increment the reference count of the urb, as we now also control it. */ + /* increment urb's reference count, we now control it. */ urb = usb_get_urb(urb); /* diff -Nru a/drivers/usb/hiddev.c b/drivers/usb/hiddev.c --- a/drivers/usb/hiddev.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/hiddev.c Wed Mar 27 16:40:09 2002 @@ -341,9 +341,6 @@ return 0; } -#define GET_TIMEOUT 3 -#define SET_TIMEOUT 3 - /* * "ioctl" file op */ @@ -529,36 +526,12 @@ return copy_to_user((void *) arg, &uref, sizeof(uref)); case HIDIOCGUSAGE: - if (copy_from_user(&uref, (void *) arg, sizeof(uref))) - return -EFAULT; - - if (uref.report_id == HID_REPORT_ID_UNKNOWN) { - field = hiddev_lookup_usage(hid, &uref); - if (field == NULL) - return -EINVAL; - } else { - rinfo.report_type = uref.report_type; - rinfo.report_id = uref.report_id; - if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL) - return -EINVAL; - - if (uref.field_index >= report->maxfield) - return -EINVAL; - - field = report->field[uref.field_index]; - if (uref.usage_index >= field->maxusage) - return -EINVAL; - } - - uref.value = field->value[uref.usage_index]; - - return copy_to_user((void *) arg, &uref, sizeof(uref)); - case HIDIOCSUSAGE: if (copy_from_user(&uref, (void *) arg, sizeof(uref))) return -EFAULT; - if (uref.report_type == HID_REPORT_TYPE_INPUT) + if (cmd == HIDIOCSUSAGE && + uref.report_type != HID_REPORT_TYPE_OUTPUT) return -EINVAL; if (uref.report_id == HID_REPORT_ID_UNKNOWN) { @@ -579,7 +552,12 @@ return -EINVAL; } - field->value[uref.usage_index] = uref.value; + if (cmd == HIDIOCGUSAGE) { + uref.value = field->value[uref.usage_index]; + return copy_to_user((void *) arg, &uref, sizeof(uref)); + } else { + field->value[uref.usage_index] = uref.value; + } return 0; diff -Nru a/drivers/usb/hpusbscsi.c b/drivers/usb/hpusbscsi.c --- a/drivers/usb/hpusbscsi.c Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/hpusbscsi.c Wed Mar 27 16:40:08 2002 @@ -129,6 +129,9 @@ if (scsi_register_host(&new->ctempl)) goto err_out; + new->sense_command[0] = REQUEST_SENSE; + new->sense_command[4] = HPUSBSCSI_SENSE_LENGTH; + /* adding to list for module unload */ list_add (&hpusbscsi_devices, &new->lh); @@ -379,6 +382,7 @@ static void control_interrupt_callback (struct urb *u) { struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context; + u8 scsi_state; DEBUG("Getting status byte %d \n",hpusbscsi->scsi_state_byte); if(unlikely(u->status < 0)) { @@ -386,10 +390,23 @@ handle_usb_error(hpusbscsi); return; } - hpusbscsi->srb->result &= SCSI_ERR_MASK; - hpusbscsi->srb->result |= hpusbscsi->scsi_state_byte; - if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT) + scsi_state = hpusbscsi->scsi_state_byte; + if (hpusbscsi->state != HP_STATE_ERROR) { + hpusbscsi->srb->result &= SCSI_ERR_MASK; + hpusbscsi->srb->result |= scsi_state; + } + + if (scsi_state == CHECK_CONDITION << 1) { + if (hpusbscsi->state == HP_STATE_WAIT) { + issue_request_sense(hpusbscsi); + } else { + /* we request sense after an eventual data transfer */ + hpusbscsi->state = HP_STATE_ERROR; + } + } + + if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT && scsi_state != CHECK_CONDITION <<1 ) /* we do a callback to the scsi layer if and only if all data has been transfered */ hpusbscsi->scallback(hpusbscsi->srb); @@ -404,6 +421,8 @@ hpusbscsi->state = HP_STATE_PREMATURE; TRACE_STATE; break; + case HP_STATE_ERROR: + break; default: printk(KERN_ERR"hpusbscsi: Unexpected status report.\n"); TRACE_STATE; @@ -432,32 +451,6 @@ } } -static void request_sense_callback (struct urb *u) -{ - struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context; - - if (unlikely(u->status<0)) { - handle_usb_error(hpusbscsi); - return; - } - - FILL_BULK_URB( - u, - hpusbscsi->dev, - hpusbscsi->current_data_pipe, - hpusbscsi->srb->sense_buffer, - SCSI_SENSE_BUFFERSIZE, - simple_done, - hpusbscsi - ); - - if (unlikely(0 > usb_submit_urb(u, GFP_ATOMIC))) { - handle_usb_error(hpusbscsi); - return; - } - hpusbscsi->state = HP_STATE_WORKING; -} - static void scatter_gather_callback(struct urb *u) { struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context; @@ -494,7 +487,7 @@ res = usb_submit_urb(u, GFP_ATOMIC); if (unlikely(res)) - hpusbscsi->state = HP_STATE_ERROR; + handle_usb_error(hpusbscsi); TRACE_STATE; } @@ -509,11 +502,15 @@ DEBUG("Data transfer done\n"); TRACE_STATE; if (hpusbscsi->state != HP_STATE_PREMATURE) { - if (unlikely(u->status < 0)) - hpusbscsi->state = HP_STATE_ERROR; - else - hpusbscsi->state = HP_STATE_WAIT; - TRACE_STATE; + if (unlikely(u->status < 0)) { + handle_usb_error(hpusbscsi); + } else { + if (hpusbscsi->state != HP_STATE_ERROR) { + hpusbscsi->state = HP_STATE_WAIT; + } else { + issue_request_sense(hpusbscsi); + } + } } else { if (likely(hpusbscsi->scallback != NULL)) hpusbscsi->scallback(hpusbscsi->srb); @@ -550,12 +547,52 @@ if (hpusbscsi->state != HP_STATE_PREMATURE) { hpusbscsi->state = HP_STATE_WORKING; TRACE_STATE; - } else { - if (likely(hpusbscsi->scallback != NULL)) - hpusbscsi->scallback(hpusbscsi->srb); - hpusbscsi->state = HP_STATE_FREE; - TRACE_STATE; + } +} + +static void request_sense_callback (struct urb *u) +{ + struct hpusbscsi * hpusbscsi = (struct hpusbscsi *)u->context; + + if (u->status<0) { + handle_usb_error(hpusbscsi); + return; + } + + FILL_BULK_URB( + u, + hpusbscsi->dev, + hpusbscsi->current_data_pipe, + hpusbscsi->srb->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + simple_done, + hpusbscsi + ); + + if (0 > usb_submit_urb(u, GFP_ATOMIC)) { + handle_usb_error(hpusbscsi); + return; } + if (hpusbscsi->state != HP_STATE_PREMATURE && hpusbscsi->state != HP_STATE_ERROR) + hpusbscsi->state = HP_STATE_WORKING; } +static void issue_request_sense (struct hpusbscsi *hpusbscsi) +{ + FILL_BULK_URB( + hpusbscsi->dataurb, + hpusbscsi->dev, + usb_sndbulkpipe(hpusbscsi->dev, hpusbscsi->ep_out), + &hpusbscsi->sense_command, + SENSE_COMMAND_SIZE, + request_sense_callback, + hpusbscsi + ); + + hpusbscsi->current_data_pipe = usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in); + + if (0 > usb_submit_urb(hpusbscsi->dataurb, GFP_ATOMIC)) { + handle_usb_error(hpusbscsi); + } +} diff -Nru a/drivers/usb/hpusbscsi.h b/drivers/usb/hpusbscsi.h --- a/drivers/usb/hpusbscsi.h Wed Mar 27 16:40:10 2002 +++ b/drivers/usb/hpusbscsi.h Wed Mar 27 16:40:10 2002 @@ -4,9 +4,14 @@ /* large parts based on or taken from code by John Fremlin and Matt Dharm */ /* this file is licensed under the GPL */ +/* A big thanks to Jose for untiring testing */ + typedef void (*usb_urb_callback) (struct urb *); typedef void (*scsi_callback)(Scsi_Cmnd *); +#define SENSE_COMMAND_SIZE 6 +#define HPUSBSCSI_SENSE_LENGTH 0x16 + struct hpusbscsi { struct list_head lh; @@ -19,8 +24,9 @@ struct Scsi_Host *host; Scsi_Host_Template ctempl; int number; - scsi_callback scallback; - Scsi_Cmnd *srb; + scsi_callback scallback; + Scsi_Cmnd *srb; + u8 sense_command[SENSE_COMMAND_SIZE]; int use_count; wait_queue_head_t pending; @@ -57,6 +63,7 @@ static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback); static int hpusbscsi_scsi_host_reset (Scsi_Cmnd *srb); static int hpusbscsi_scsi_abort (Scsi_Cmnd *srb); +static void issue_request_sense (struct hpusbscsi *hpusbscsi); static Scsi_Host_Template hpusbscsi_scsi_host_template = { name: "hpusbscsi", @@ -84,5 +91,6 @@ #define HP_STATE_ERROR 3 /* error has been reported */ #define HP_STATE_WAIT 4 /* waiting for status transfer */ #define HP_STATE_PREMATURE 5 /* status prematurely reported */ + diff -Nru a/drivers/usb/hub.c b/drivers/usb/hub.c --- a/drivers/usb/hub.c Wed Mar 27 16:40:10 2002 +++ b/drivers/usb/hub.c Wed Mar 27 16:40:10 2002 @@ -655,11 +655,11 @@ * Not covered by the spec - but easy to deal with. * * This implementation uses 400ms minimum debounce timeout and checks - * every 10ms for transient disconnects to restart the delay. + * every 100ms for transient disconnects to restart the delay. */ #define HUB_DEBOUNCE_TIMEOUT 400 -#define HUB_DEBOUNCE_STEP 10 +#define HUB_DEBOUNCE_STEP 100 /* return: -1 on error, 0 on success, 1 on disconnect. */ static int usb_hub_port_debounce(struct usb_device *hub, int port) diff -Nru a/drivers/usb/kaweth.c b/drivers/usb/kaweth.c --- a/drivers/usb/kaweth.c Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/kaweth.c Wed Mar 27 16:40:07 2002 @@ -124,6 +124,7 @@ { USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */ { USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */ { USB_DEVICE(0x0565, 0x0002) }, /* Peracom Enet */ + { USB_DEVICE(0x0565, 0x0003) }, /* Optus@Home UEP1045A */ { USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */ { USB_DEVICE(0x05e9, 0x0008) }, /* KLSI KL5KUSB101B */ { USB_DEVICE(0x05e9, 0x0009) }, /* KLSI KL5KUSB101B (Board change) */ diff -Nru a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c --- a/drivers/usb/pegasus.c Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/pegasus.c Wed Mar 27 16:40:07 2002 @@ -59,7 +59,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.5.1 (2002/03/06)" +#define DRIVER_VERSION "v0.5.2 (2002/03/21)" #define DRIVER_AUTHOR "Petko Manolov " #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" @@ -487,10 +487,10 @@ pegasus_t *pegasus = dev->priv; - if ( read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr) ) - return 1; - if ( !(bmsr & 0x20) && !loopback ) - warn( "%s: link NOT established (0x%x) - check the cable.", + read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr); + read_mii_word(pegasus, pegasus->phy, MII_BMSR, &bmsr); + if ( !(bmsr & 4) && !loopback ) + warn( "%s: link NOT established (%04x) - check the cable.", dev->name, bmsr ); if ( read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart) ) return 2; @@ -741,6 +741,7 @@ pegasus_t *pegasus = (pegasus_t *)net->priv; int res; + down(&pegasus->sem); FILL_BULK_URB( pegasus->rx_urb, pegasus->usb, usb_rcvbulkpipe(pegasus->usb, 1), pegasus->rx_buff, PEGASUS_MAX_MTU, @@ -759,11 +760,15 @@ pegasus->flags |= PEGASUS_RUNNING; if ( (res = enable_net_traffic(net, pegasus->usb)) ) { err("can't enable_net_traffic() - %d", res); - return -EIO; + res = -EIO; + goto exit; } set_carrier(net); + res = 0; +exit: + up(&pegasus->sem); - return 0; + return res; } @@ -771,6 +776,7 @@ { pegasus_t *pegasus = net->priv; + down(&pegasus->sem); pegasus->flags &= ~PEGASUS_RUNNING; netif_stop_queue( net ); if ( !(pegasus->flags & PEGASUS_UNPLUG) ) @@ -782,6 +788,7 @@ #ifdef PEGASUS_USE_INTR usb_unlink_urb( pegasus->intr_urb ); #endif + up(&pegasus->sem); return 0; } @@ -868,23 +875,32 @@ { __u16 *data = (__u16 *)&rq->ifr_data; pegasus_t *pegasus = net->priv; + int res; + down(&pegasus->sem); switch(cmd) { case SIOCETHTOOL: - return pegasus_ethtool_ioctl(net, rq->ifr_data); + res = pegasus_ethtool_ioctl(net, rq->ifr_data); + break; case SIOCDEVPRIVATE: data[0] = pegasus->phy; case SIOCDEVPRIVATE+1: read_mii_word(pegasus, data[0], data[1]&0x1f, &data[3]); - return 0; + res = 0; + break; case SIOCDEVPRIVATE+2: - if ( !capable(CAP_NET_ADMIN) ) + if ( !capable(CAP_NET_ADMIN) ) { + up(&pegasus->sem); return -EPERM; + } write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]); - return 0; + res = 0; + break; default: - return -EOPNOTSUPP; + res = -EOPNOTSUPP; } + up(&pegasus->sem); + return res; } @@ -953,7 +969,6 @@ err("usb_set_configuration() failed"); return NULL; } - if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) { err("out of memory allocating device structure"); return NULL; @@ -997,9 +1012,11 @@ usb_free_urb (pegasus->rx_urb); usb_free_urb (pegasus->ctrl_urb); kfree( pegasus ); - return NULL; + return NULL; } - + + init_MUTEX(&pegasus->sem); + down(&pegasus->sem); pegasus->usb = dev; pegasus->net = net; SET_MODULE_OWNER(net); @@ -1027,7 +1044,7 @@ kfree(pegasus->net); kfree(pegasus); pegasus = NULL; - return NULL; + goto exit; } info( "%s: %s", net->name, usb_dev_id[dev_index].name ); @@ -1044,7 +1061,8 @@ warn( "can't locate MII phy, using default" ); pegasus->phy = 1; } - +exit: + up(&pegasus->sem); return pegasus; } diff -Nru a/drivers/usb/pegasus.h b/drivers/usb/pegasus.h --- a/drivers/usb/pegasus.h Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/pegasus.h Wed Mar 27 16:40:07 2002 @@ -101,7 +101,7 @@ struct urb *ctrl_urb, *rx_urb, *tx_urb, *intr_urb; struct usb_ctrlrequest dr; wait_queue_head_t ctrl_wait; - struct semaphore ctrl_sem; + struct semaphore sem; unsigned char rx_buff[PEGASUS_MAX_MTU]; unsigned char tx_buff[PEGASUS_MAX_MTU]; unsigned char intr_buff[8]; diff -Nru a/drivers/usb/printer.c b/drivers/usb/printer.c --- a/drivers/usb/printer.c Wed Mar 27 16:40:06 2002 +++ b/drivers/usb/printer.c Wed Mar 27 16:40:06 2002 @@ -193,7 +193,14 @@ { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */ { 0x03f0, 0x0304, USBLP_QUIRK_BIDIR }, /* HP DeskJet 810C/812C */ { 0x03f0, 0x0404, USBLP_QUIRK_BIDIR }, /* HP DeskJet 830C */ + { 0x03f0, 0x0504, USBLP_QUIRK_BIDIR }, /* HP DeskJet 885C */ + { 0x03f0, 0x0604, USBLP_QUIRK_BIDIR }, /* HP DeskJet 840C */ + { 0x03f0, 0x0804, USBLP_QUIRK_BIDIR }, /* HP DeskJet 816C */ + { 0x03f0, 0x1104, USBLP_QUIRK_BIDIR }, /* HP Deskjet 959C */ { 0x0409, 0xefbe, USBLP_QUIRK_BIDIR }, /* NEC Picty900 (HP OEM) */ + { 0x0409, 0xbef4, USBLP_QUIRK_BIDIR }, /* NEC Picty760 (HP OEM) */ + { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */ + { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */ { 0, 0 } }; diff -Nru a/drivers/usb/rtl8150.c b/drivers/usb/rtl8150.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/rtl8150.c Wed Mar 27 16:40:10 2002 @@ -0,0 +1,788 @@ +/* + * Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net) + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* Version Information */ +#define DRIVER_VERSION "v0.0.1" +#define DRIVER_AUTHOR "Petko Manolov " +#define DRIVER_DESC "rtl8150 based usb-ethernet driver" + + +#define IRD 0x0120 +#define MAR 0x0126 +#define CR 0x012e +#define TCR 0x012f +#define RCR 0x0130 +#define TSR 0x0132 +#define RSR 0x0133 +#define CON0 0x0135 +#define CON1 0x0136 +#define MSR 0x0137 +#define PHYADD 0x0138 +#define PHYDAT 0x0139 +#define PHYCNT 0x013b +#define GPPC 0x013d +#define BMCR 0x0140 +#define BMSR 0x0142 +#define ANAR 0x0144 +#define ANLP 0x0146 +#define AER 0x0148 + +#define PHY_READ 0 +#define PHY_WRITE 0x20 +#define PHY_GO 0x40 + +#define RTL8150_REQT_READ 0xc0 +#define RTL8150_REQT_WRITE 0x40 +#define RTL8150_REQ_GET_REGS 0x05 +#define RTL8150_REQ_SET_REGS 0x05 + +#define RTL8150_MTU 1500 +#define RTL8150_MAX_MTU 1500 + +#define RTL8150_TX_TIMEOUT (HZ) + +/* rtl8150 flags */ +#define RTL8150_FLAG_HWCRC 0 +#define RX_REG_SET 1 + + +/* Define these values to match your device */ +#define VENDOR_ID_REALTEK 0x0bda +#define PRODUCT_ID_RTL8150 0x8150 + +/* table of devices that work with this driver */ +static struct usb_device_id rtl8150_table [] = { + { USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150) }, + { } +}; + +MODULE_DEVICE_TABLE (usb, rtl8150_table); + + +struct rtl8150 { + unsigned int flags; + struct usb_device *udev; + struct usb_interface *interface; + struct semaphore sem; + struct net_device_stats stats; + struct net_device *netdev; + struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb; + struct usb_ctrlrequest dr; + int intr_interval; + u16 rx_creg; + u8 rx_buff[RTL8150_MAX_MTU]; + u8 tx_buff[RTL8150_MAX_MTU]; + u8 intr_buff[8]; + u8 phy; +}; + +typedef struct rtl8150 rtl8150_t; + + +/* the global usb devfs handle */ +extern devfs_handle_t usb_devfs_handle; +unsigned long multicast_filter_limit = 32; + + +static void rtl8150_disconnect(struct usb_device *dev, void *ptr); +static void * rtl8150_probe(struct usb_device *dev, unsigned int ifnum, + const struct usb_device_id *id); + + +static struct usb_driver rtl8150_driver = { + name: "rtl8150", + probe: rtl8150_probe, + disconnect: rtl8150_disconnect, + id_table: rtl8150_table, +}; + + + +/* +** +** device related part of the code +** +*/ +static int get_registers(rtl8150_t *dev, u16 indx, u16 size, void *data) +{ + return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev,0), + RTL8150_REQ_GET_REGS, RTL8150_REQT_READ, + indx, 0, data, size, HZ/2); +} + + +static int set_registers(rtl8150_t *dev, u16 indx, u16 size, void *data) +{ + return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev,0), + RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE, + indx, 0, data, size, HZ/2); +} + + +static void ctrl_callback(struct urb *urb) +{ + rtl8150_t *dev; + + switch (urb->status) { + case 0: + info("async set regs OK"); + break; + case -EINPROGRESS: + warn("in progress"); + break; + case -ENOENT: + warn("no entry"); + break; + default: + warn("ctrl urb status %d", urb->status); + } + dev = urb->context; + clear_bit(RX_REG_SET, &dev->flags); +} + + +static int async_set_registers(rtl8150_t *dev, u16 indx, u16 size, void *data) +{ + int ret; + + if (test_bit(RX_REG_SET, &dev->flags)) { + warn("async registers change in progress"); + return -EAGAIN; + } + + dev->dr.bRequestType = RTL8150_REQT_WRITE; + dev->dr.bRequest = RTL8150_REQ_SET_REGS; + dev->dr.wValue = cpu_to_le16(indx); + dev->dr.wIndex = 0; + dev->dr.wLength = cpu_to_le16(size); + dev->ctrl_urb->transfer_buffer_length = size; + FILL_CONTROL_URB(dev->ctrl_urb, dev->udev, usb_sndctrlpipe(dev->udev,0), + (char*)&dev->dr, &dev->rx_creg, size, + ctrl_callback, dev); + if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC))) + err("control request submission failed: %d", ret); + else + set_bit(RX_REG_SET, &dev->flags); + + return ret; +} + + +static int read_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 *reg) +{ + int i; + u8 data[3], tmp; + + data[0] = phy; + data[1] = data[2] = 0; + tmp = indx | PHY_READ | PHY_GO; + i = 0; + + set_registers(dev, PHYADD, sizeof(data), data); + set_registers(dev, PHYCNT, 1, &tmp); + do { + get_registers(dev, PHYCNT, 1, data); + } while ((data[0] & PHY_GO) && (i++ < HZ)); + + if (i < HZ) { + get_registers(dev, PHYDAT, 2, data); + *reg = le16_to_cpup(data); + return 0; + } else + return 1; +} + + +static int write_mii_word(rtl8150_t *dev, u8 phy, __u8 indx, u16 reg) +{ + int i; + u8 data[3], tmp; + + data[0] = phy; + *(data + 1) = cpu_to_le16p(®); + tmp = indx | PHY_WRITE | PHY_GO; + i = 0; + + set_registers(dev, PHYADD, sizeof(data), data); + set_registers(dev, PHYCNT, 1, &tmp); + do { + get_registers(dev, PHYCNT, 1, data); + } while((data[0] & PHY_GO) && (i++ < HZ)); + + if (i < HZ) + return 0; + else + return 1; +} + + +static inline void set_ethernet_addr(rtl8150_t *dev) +{ + u8 node_id[6]; + + get_registers(dev, IRD, sizeof(node_id), node_id); + memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id)); + info("ethID->%02x:%02x:%02x:%02x:%02x:%02x", node_id[0], node_id[1], + node_id[2], node_id[3], node_id[4], node_id[5]); +} + + +static int rtl8150_reset(rtl8150_t *dev) +{ + u8 data=0x10; + int i=HZ; + + set_registers(dev, CR, 1, &data); + do { + get_registers(dev, CR, 1, &data); + } while ((data & 0x10) && --i); + + return (i > 0) ? 0 : -1; +} + + +static int alloc_all_urbs(rtl8150_t *dev) +{ + dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->rx_urb) + return 0; + dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->tx_urb) { + usb_free_urb(dev->rx_urb); + return 0; + } + dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->intr_urb) { + usb_free_urb(dev->rx_urb); + usb_free_urb(dev->tx_urb); + return 0; + } + dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->intr_urb) { + usb_free_urb(dev->rx_urb); + usb_free_urb(dev->tx_urb); + usb_free_urb(dev->intr_urb); + return 0; + } + + return 1; +} + + +static void free_all_urbs(rtl8150_t *dev) +{ + usb_free_urb(dev->rx_urb); + usb_free_urb(dev->tx_urb); + usb_free_urb(dev->intr_urb); + usb_free_urb(dev->ctrl_urb); +} + + +static void unlink_all_urbs(rtl8150_t *dev) +{ + usb_unlink_urb(dev->rx_urb); + usb_unlink_urb(dev->tx_urb); + usb_unlink_urb(dev->intr_urb); + usb_unlink_urb(dev->ctrl_urb); +} + + +static void read_bulk_callback(struct urb *urb) +{ + rtl8150_t *dev; + int pkt_len, res; + struct sk_buff *skb; + struct net_device *netdev; + + dev = urb->context; + if (!dev) { + warn("!dev"); + return; + } + netdev = dev->netdev; + if (!netif_device_present(netdev)) { + warn("netdev is not present"); + return; + } + switch (urb->status) { + case 0: + break; + case -ENOENT: + warn("-ENOENT"); + return; + case -ETIMEDOUT: + warn("reset needed may be?.."); + break; + default: + warn("Rx status %d", urb->status); + goto goon; + } + + pkt_len = urb->actual_length - 4; + + if (pkt_len < 60) { + warn("Rx short packet %d", pkt_len); + goto goon; + } +#if 0 + if (pkt_len <= 80) { + int i; + info("%s: Rx pkt_len %d", netdev->name, pkt_len); + for (i=0; i<(pkt_len + 4); i++) { + printk("%02x ", *(u8 *)(dev->rx_buff+i)); + } + printk("\b ..done\n"); + memset(dev->rx_buff, 0, pkt_len); + } +#endif + if (!(skb = dev_alloc_skb(pkt_len + 2))) + goto goon; + skb->dev = netdev; + skb_reserve(skb, 2); + eth_copy_and_sum(skb, dev->rx_buff, pkt_len, 0); + skb_put(skb, pkt_len); + skb->protocol = eth_type_trans(skb, netdev); + netif_rx(skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; +goon: + FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev,1), + dev->rx_buff, RTL8150_MAX_MTU, read_bulk_callback, dev); + if ((res=usb_submit_urb(dev->rx_urb, GFP_ATOMIC))) + warn("%s: Rx urb submission failed %d", netdev->name, res); +} + + +static void write_bulk_callback(struct urb *urb) +{ + rtl8150_t *dev; + + dev = urb->context; + if (!dev) + return; + if (!netif_device_present(dev->netdev)) + return; + if (urb->status) + info("%s: Tx status %d", dev->netdev->name, urb->status); + dev->netdev->trans_start = jiffies; + netif_wake_queue(dev->netdev); +} + + +void intr_callback(struct urb *urb) +{ + rtl8150_t *dev; + struct net_device *netdev; + u8 *d; + + dev = urb->context; + if (!dev) + return; + switch (urb->status) { + case 0: + break; + case -ENOENT: + return; + default: + info("%s: intr status %d", dev->netdev->name, + urb->status); + } + d = urb->transfer_buffer; + netdev = dev->netdev; + /* info("%s: Tx errs %02x:%02x:%02x:%02x", dev->netdev->name, + d[0], d[1], d[2], d[3]); */ +} + + + + +/* +** +** network related part of the code +** +*/ + + +static int enable_net_traffic(rtl8150_t *dev) +{ + u8 cr, tcr, rcr, msr; + + if (rtl8150_reset(dev)) { + warn("%s - device reset failed", __FUNCTION__); + } + dev->rx_creg = rcr = 0x9e; /* bit7=1 attach Rx info at the end */ + tcr = 0xd8; + cr = 0x0c; + set_registers(dev, RCR, 1, &rcr); + set_registers(dev, TCR, 1, &tcr); + set_registers(dev, CR, 1, &cr); + get_registers(dev, MSR, 1, &msr); + info("MSR = %02x", msr); + + return 0; +} + + +static void disable_net_traffic(rtl8150_t *dev) +{ + u8 cr; + + get_registers(dev, CR, 1, &cr); + cr &= 0xf3; + set_registers(dev, CR, 1, &cr); +} + + +static struct net_device_stats *rtl8150_netdev_stats(struct net_device *dev) +{ + return &((rtl8150_t *)dev->priv)->stats; +} + + +static void rtl8150_tx_timeout(struct net_device *netdev) +{ + rtl8150_t *dev; + + dev = netdev->priv; + if (!dev) + return; + warn("%s: tx timeout.", netdev->name); + dev->tx_urb->transfer_flags |= USB_ASYNC_UNLINK; + usb_unlink_urb(dev->tx_urb); + dev->stats.tx_errors++; +} + + +static void rtl8150_set_multicast(struct net_device *netdev) +{ + rtl8150_t *dev; + + dev = netdev->priv; + netif_stop_queue(netdev); + if (netdev->flags & IFF_PROMISC) { + dev->rx_creg |= 0x0001; + info("%s: promiscuous mode", netdev->name); + } else if ((netdev->mc_count > multicast_filter_limit) || + (netdev->flags & IFF_ALLMULTI)) { + dev->rx_creg &= 0xfffe; + dev->rx_creg |= 0x0002; + info("%s: allmulti set", netdev->name); + } else { + /* ~RX_MULTICAST, ~RX_PROMISCUOUS */ + dev->rx_creg &= 0x00fc; + } + async_set_registers(dev, RCR, 2, &dev->rx_creg); + netif_wake_queue(netdev); +} + + +static int rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + rtl8150_t *dev; + int count, res; + + netif_stop_queue(netdev); + dev = netdev->priv; + count = (skb->len & 0x3f) ? skb->len : skb->len+1; + memcpy(dev->tx_buff, skb->data, skb->len); + FILL_BULK_URB(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev,2), + dev->tx_buff, RTL8150_MAX_MTU, write_bulk_callback, dev); + dev->tx_urb->transfer_buffer_length = count; + + if ((res = usb_submit_urb(dev->tx_urb, GFP_KERNEL))) { + warn("failed tx_urb %d\n", res); + dev->stats.tx_errors++; + netif_start_queue(netdev); + } else { + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + netdev->trans_start = jiffies; + } + dev_kfree_skb(skb); + + return 0; +} + + +static int rtl8150_open(struct net_device *netdev) +{ + rtl8150_t *dev; + int res; + + dbg(__FUNCTION__); + + dev = netdev->priv; + if (dev == NULL) { + return -ENODEV; + } + + { + unsigned char mar[8]; + get_registers(dev, MAR, sizeof(mar), mar); + printk("%x:%x:%x:%x:%x:%x:%x:%x\n", mar[0], mar[1], mar[2], + mar[3], mar[4], mar[5], mar[6], mar[7]); + } + + FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev,1), + dev->rx_buff, RTL8150_MAX_MTU, read_bulk_callback, dev); + if ((res=usb_submit_urb(dev->rx_urb, GFP_KERNEL))) + warn("%s: rx_urb submit failed: %d", __FUNCTION__, res); +#if 0 + FILL_INT_URB(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev,3), + dev->intr_buff, sizeof(dev->intr_buff), intr_callback, + dev, dev->intr_interval); + if ((res=usb_submit_urb(dev->intr_urb, GFP_KERNEL))) + warn("%s: intr_urb submit failed: %d", __FUNCTION__, res); +#endif + netif_start_queue(netdev); + enable_net_traffic(dev); + + return res; +} + + +static int rtl8150_close(struct net_device *netdev) +{ + rtl8150_t *dev; + int res = 0; + + dev = netdev->priv; + if (!dev) + return -ENODEV; + + disable_net_traffic(dev); + unlink_all_urbs(dev); + netif_stop_queue(netdev); + + + return res; +} + + +static int rtl8150_ethtool_ioctl(struct net_device *netdev, void *uaddr) +{ + rtl8150_t *dev; + int cmd; + char tmp[128]; + + dev = netdev->priv; + if (get_user(cmd, (int *)uaddr)) + return -EFAULT; + + switch (cmd) { + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; + + strncpy(info.driver, DRIVER_DESC, ETHTOOL_BUSINFO_LEN); + strncpy(info.version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN); + sprintf(tmp, "usb%d:%d", dev->udev->bus->busnum, + dev->udev->devnum); + strncpy(info.bus_info, tmp, ETHTOOL_BUSINFO_LEN); + if (copy_to_user(uaddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd; + short lpa, bmcr; + + if (copy_from_user(&ecmd, uaddr, sizeof(ecmd))) + return -EFAULT; + ecmd.supported = (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg | + SUPPORTED_TP | + SUPPORTED_MII); + ecmd.port = PORT_TP; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.phy_address = dev->phy; + get_registers(dev, BMCR, 2, &bmcr); + get_registers(dev, ANLP, 2, &lpa); + if (bmcr & BMCR_ANENABLE) { + ecmd.autoneg = AUTONEG_ENABLE; + ecmd.speed = (lpa & (LPA_100HALF | LPA_100FULL)) ? + SPEED_100 : SPEED_10; + if (ecmd.speed == SPEED_100) + ecmd.duplex = (lpa & LPA_100FULL) ? + DUPLEX_FULL : DUPLEX_HALF; + else + ecmd.duplex = (lpa & LPA_10FULL) ? + DUPLEX_FULL : DUPLEX_HALF; + } else { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.speed = (bmcr & BMCR_SPEED100) ? + SPEED_100 : SPEED_10; + ecmd.duplex = (bmcr & BMCR_FULLDPLX) ? + DUPLEX_FULL : DUPLEX_HALF; + } + if (copy_to_user(uaddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + return 0; + } + case ETHTOOL_SSET: + return -ENOTSUPP; + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + + edata.data = netif_carrier_ok(netdev); + if (copy_to_user(uaddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + default: + return -EOPNOTSUPP; + } +} + + +static int rtl8150_ioctl (struct net_device *netdev, struct ifreq *rq, int cmd) +{ + rtl8150_t *dev; + u16 *data; + + dev = netdev->priv; + data = (u16 *)&rq->ifr_data; + + switch (cmd) { + case SIOCETHTOOL: + return rtl8150_ethtool_ioctl(netdev, rq->ifr_data); + case SIOCDEVPRIVATE: + data[0] = dev->phy; + case SIOCDEVPRIVATE+1: + read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]); + return 0; + case SIOCDEVPRIVATE+2: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + write_mii_word(dev, dev->phy, (data[1] & 0x1f), data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + + +static void * rtl8150_probe(struct usb_device *udev, unsigned int ifnum, + const struct usb_device_id *id) +{ + rtl8150_t *dev; + struct net_device *netdev; + + udev->config[0].bConfigurationValue = 1; + if (usb_set_configuration(udev, udev->config[0].bConfigurationValue)) { + err("usb_set_configuration() failed"); + return NULL; + } + if ((udev->descriptor.idVendor != VENDOR_ID_REALTEK) || + (udev->descriptor.idProduct != PRODUCT_ID_RTL8150)) { + err("Not the one we are interested about"); + return NULL; + } + + dev = kmalloc(sizeof(rtl8150_t), GFP_KERNEL); + if (!dev) { + err ("Out of memory"); + return NULL; + } else + memset(dev, 0, sizeof(rtl8150_t)); + + netdev = init_etherdev(NULL, 0); + if (!netdev) { + kfree(dev); + err("Oh boy, out of memory again?!?"); + return NULL; + } + + init_MUTEX(&dev->sem); + dev->udev = udev; + dev->netdev = netdev; + SET_MODULE_OWNER(netdev); + netdev->priv = dev; + netdev->open = rtl8150_open; + netdev->stop = rtl8150_close; + netdev->do_ioctl = rtl8150_ioctl; + netdev->watchdog_timeo = RTL8150_TX_TIMEOUT; + netdev->tx_timeout = rtl8150_tx_timeout; + netdev->hard_start_xmit = rtl8150_start_xmit; + netdev->set_multicast_list = rtl8150_set_multicast; + netdev->get_stats = rtl8150_netdev_stats; + netdev->mtu = RTL8150_MTU; + dev->intr_interval = 100; /* 100ms */ + + if (rtl8150_reset(dev) || !alloc_all_urbs(dev)) { + err("couldn't reset the device"); + free_all_urbs(dev); + unregister_netdev(dev->netdev); + kfree(netdev); + kfree(dev); + return NULL; + } + + set_ethernet_addr(dev); + + info("%s: the rtl8150 is detected", netdev->name); + info("compiled %s %s\n", __DATE__, __TIME__); + + return dev; +} + + +static void rtl8150_disconnect(struct usb_device *udev, void *ptr) +{ + rtl8150_t *dev; + + dev = ptr; + unregister_netdev(dev->netdev); + unlink_all_urbs(dev); + free_all_urbs(dev); + kfree(dev->netdev); + kfree(dev); + dev->netdev = NULL; + dev = NULL; +} + + + +static int __init usb_rtl8150_init(void) +{ + info(DRIVER_DESC " " DRIVER_VERSION); + return usb_register(&rtl8150_driver); +} + + +static void __exit usb_rtl8150_exit(void) +{ + usb_deregister(&rtl8150_driver); +} + + +module_init(usb_rtl8150_init); +module_exit(usb_rtl8150_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/serial/Config.help b/drivers/usb/serial/Config.help --- a/drivers/usb/serial/Config.help Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/serial/Config.help Wed Mar 27 16:40:09 2002 @@ -12,6 +12,26 @@ The module will be called usbserial.o. If you want to compile it as a module, say M here and read . +CONFIG_USB_SERIAL_CONSOLE + If you say Y here, it will be possible to use a USB to serial + converter port as the system console (the system console is the + device which receives all kernel messages and warnings and which + allows logins in single user mode). This could be useful if some + terminal or printer is connected to that serial port. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyUSB0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + + If you don't have a VGA card installed and you say Y here, the + kernel will automatically use the first USB to serial converter + port, /dev/ttyUSB0, as system console. + + If unsure, say N. + CONFIG_USB_SERIAL_GENERIC Say Y here if you want to use the generic USB serial driver. Please read for more information on diff -Nru a/drivers/usb/serial/Config.in b/drivers/usb/serial/Config.in --- a/drivers/usb/serial/Config.in Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/serial/Config.in Wed Mar 27 16:40:08 2002 @@ -8,6 +8,7 @@ if [ "$CONFIG_USB_SERIAL" = "y" ]; then dep_mbool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG $CONFIG_USB_SERIAL fi +dep_mbool ' USB Serial Console device support (EXPERIMENTAL)' CONFIG_USB_SERIAL_CONSOLE $CONFIG_USB_SERIAL dep_mbool ' USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC $CONFIG_USB_SERIAL dep_tristate ' USB Belkin and Peracom Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_BELKIN $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL dep_tristate ' USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL diff -Nru a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c --- a/drivers/usb/serial/belkin_sa.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/serial/belkin_sa.c Wed Mar 27 16:40:09 2002 @@ -191,9 +191,6 @@ /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i].open_count > 0) { - belkin_sa_close (&serial->port[i], NULL); - } /* My special items, the standard routines free my urbs */ if (serial->port[i].private) kfree(serial->port[i].private); @@ -207,26 +204,22 @@ dbg(__FUNCTION__" port %d", port->number); - ++port->open_count; - - if (port->open_count == 1) { - /*Start reading from the device*/ - /* TODO: Look at possibility of submitting mulitple URBs to device to - * enhance buffering. Win trace shows 16 initial read URBs. - */ - port->read_urb->dev = port->serial->dev; - retval = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (retval) { - err("usb_submit_urb(read bulk) failed"); - goto exit; - } - - port->interrupt_in_urb->dev = port->serial->dev; - retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (retval) - err(" usb_submit_urb(read int) failed"); + /*Start reading from the device*/ + /* TODO: Look at possibility of submitting mulitple URBs to device to + * enhance buffering. Win trace shows 16 initial read URBs. + */ + port->read_urb->dev = port->serial->dev; + retval = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (retval) { + err("usb_submit_urb(read bulk) failed"); + goto exit; } - + + port->interrupt_in_urb->dev = port->serial->dev; + retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (retval) + err(" usb_submit_urb(read int) failed"); + exit: return retval; } /* belkin_sa_open */ @@ -245,16 +238,11 @@ dbg(__FUNCTION__" port %d", port->number); - --port->open_count; - - if (port->open_count <= 0) { - if (serial->dev) { - /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - usb_unlink_urb (port->interrupt_in_urb); - } - port->open_count = 0; + if (serial->dev) { + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + usb_unlink_urb (port->interrupt_in_urb); } } /* belkin_sa_close */ @@ -336,12 +324,31 @@ { struct usb_serial *serial = port->serial; struct belkin_sa_private *priv = (struct belkin_sa_private *)port->private; - unsigned int iflag = port->tty->termios->c_iflag; - unsigned int cflag = port->tty->termios->c_cflag; - unsigned int old_iflag = old_termios->c_iflag; - unsigned int old_cflag = old_termios->c_cflag; + unsigned int iflag; + unsigned int cflag; + unsigned int old_iflag = 0; + unsigned int old_cflag = 0; __u16 urb_value = 0; /* Will hold the new flags */ + if ((!port->tty) || (!port->tty->termios)) { + dbg ("%s - no tty or termios structure", __FUNCTION__); + return; + } + + iflag = port->tty->termios->c_iflag; + 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; + } + old_iflag = old_termios->c_iflag; + old_cflag = old_termios->c_cflag; + } + /* Set the baud rate */ if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { /* reassert DTR and (maybe) RTS on transition from B0 */ diff -Nru a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c --- a/drivers/usb/serial/cyberjack.c Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/serial/cyberjack.c Wed Mar 27 16:40:07 2002 @@ -130,11 +130,7 @@ dbg (__FUNCTION__); - /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i].open_count > 0) { - cyberjack_close (&serial->port[i], NULL); - } /* My special items, the standard routines free my urbs */ if (serial->port[i].private) kfree(serial->port[i].private); @@ -151,31 +147,27 @@ dbg(__FUNCTION__ " - port %d", port->number); - ++port->open_count; - - if (port->open_count == 1) { - /* force low_latency on so that our tty_push actually forces - * the data through, otherwise it is scheduled, and with high - * data rates (like with OHCI) data can get lost. - */ - port->tty->low_latency = 1; - - priv = (struct cyberjack_private *)port->private; - priv->rdtodo = 0; - priv->wrfilled = 0; - priv->wrsent = 0; - - /* shutdown any bulk reads that might be going on */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - usb_unlink_urb (port->interrupt_in_urb); - - port->interrupt_in_urb->dev = port->serial->dev; - result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (result) - err(" usb_submit_urb(read int) failed"); - dbg(__FUNCTION__ " - usb_submit_urb(int urb)"); - } + /* force low_latency on so that our tty_push actually forces + * the data through, otherwise it is scheduled, and with high + * data rates (like with OHCI) data can get lost. + */ + port->tty->low_latency = 1; + + priv = (struct cyberjack_private *)port->private; + priv->rdtodo = 0; + priv->wrfilled = 0; + priv->wrsent = 0; + + /* shutdown any bulk reads that might be going on */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + usb_unlink_urb (port->interrupt_in_urb); + + port->interrupt_in_urb->dev = port->serial->dev; + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (result) + err(" usb_submit_urb(read int) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(int urb)"); return result; } @@ -184,16 +176,11 @@ { dbg(__FUNCTION__ " - port %d", port->number); - --port->open_count; - - if (port->open_count <= 0) { - if (port->serial->dev) { - /* shutdown any bulk reads that might be going on */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - usb_unlink_urb (port->interrupt_in_urb); - } - port->open_count = 0; + if (port->serial->dev) { + /* shutdown any bulk reads that might be going on */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + usb_unlink_urb (port->interrupt_in_urb); } } diff -Nru a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c --- a/drivers/usb/serial/digi_acceleport.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/serial/digi_acceleport.c Wed Mar 27 16:40:09 2002 @@ -1490,28 +1490,17 @@ return( -EAGAIN ); } - /* inc module use count before sleeping to wait for closes */ - ++port->open_count; - /* wait for a close in progress to finish */ while( priv->dp_in_close ) { cond_wait_interruptible_timeout_irqrestore( &priv->dp_close_wait, DIGI_RETRY_TIMEOUT, &priv->dp_port_lock, flags ); if( signal_pending(current) ) { - --port->open_count; return( -EINTR ); } spin_lock_irqsave( &priv->dp_port_lock, flags ); } - /* if port is already open, just return */ - /* be sure exactly one open proceeds */ - if( port->open_count != 1) { - spin_unlock_irqrestore( &priv->dp_port_lock, flags ); - return( 0 ); - } - spin_unlock_irqrestore( &priv->dp_port_lock, flags ); /* read modem signals automatically whenever they change */ @@ -1557,14 +1546,6 @@ /* do cleanup only after final close on this port */ spin_lock_irqsave( &priv->dp_port_lock, flags ); - if( port->open_count > 1 ) { - --port->open_count; - spin_unlock_irqrestore( &priv->dp_port_lock, flags ); - return; - } else if( port->open_count <= 0 ) { - spin_unlock_irqrestore( &priv->dp_port_lock, flags ); - return; - } priv->dp_in_close = 1; spin_unlock_irqrestore( &priv->dp_port_lock, flags ); @@ -1637,7 +1618,6 @@ spin_lock_irqsave( &priv->dp_port_lock, flags ); priv->dp_write_urb_in_use = 0; priv->dp_in_close = 0; - --port->open_count; wake_up_interruptible( &priv->dp_close_wait ); spin_unlock_irqrestore( &priv->dp_port_lock, flags ); @@ -1765,8 +1745,6 @@ { int i; - struct digi_port *priv; - unsigned long flags; dbg( "digi_shutdown: TOP, in_interrupt()=%d", in_interrupt() ); @@ -1775,16 +1753,6 @@ for( i=0; itype->num_ports+1; i++ ) { usb_unlink_urb( serial->port[i].read_urb ); usb_unlink_urb( serial->port[i].write_urb ); - } - - /* dec module use count */ - for( i=0; itype->num_ports; i++ ) { - priv = serial->port[i].private; - spin_lock_irqsave( &priv->dp_port_lock, flags ); - while( serial->port[i].open_count > 0 ) { - --serial->port[i].open_count; - } - spin_unlock_irqrestore( &priv->dp_port_lock, flags ); } /* free the private data structures for all ports */ diff -Nru a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c --- a/drivers/usb/serial/empeg.c Wed Mar 27 16:40:05 2002 +++ b/drivers/usb/serial/empeg.c Wed Mar 27 16:40:05 2002 @@ -157,35 +157,29 @@ dbg(__FUNCTION__ " - port %d", port->number); - ++port->open_count; + /* Force default termio settings */ + empeg_set_termios (port, NULL) ; - if (port->open_count == 1) { + bytes_in = 0; + bytes_out = 0; - /* Force default termio settings */ - empeg_set_termios (port, NULL) ; - - bytes_in = 0; - bytes_out = 0; - - /* Start reading from the device */ - FILL_BULK_URB( - port->read_urb, - serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - empeg_read_bulk_callback, - port); - - port->read_urb->transfer_flags |= USB_QUEUE_BULK; + /* Start reading from the device */ + FILL_BULK_URB( + port->read_urb, + serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + empeg_read_bulk_callback, + port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); + port->read_urb->transfer_flags |= USB_QUEUE_BULK; - if (result) - err(__FUNCTION__ " - failed submitting read urb, error %d", result); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); - } + if (result) + err(__FUNCTION__ " - failed submitting read urb, error %d", result); return result; } @@ -204,16 +198,10 @@ if (!serial) return; - --port->open_count; - - if (port->open_count <= 0) { - if (serial->dev) { - /* shutdown our bulk read */ - usb_unlink_urb (port->read_urb); - } - port->open_count = 0; + if (serial->dev) { + /* shutdown our bulk read */ + usb_unlink_urb (port->read_urb); } - /* Uncomment the following line if you want to see some statistics in your syslog */ /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */ } @@ -491,17 +479,7 @@ static void empeg_shutdown (struct usb_serial *serial) { - int i; - dbg (__FUNCTION__); - - /* stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i].open_count > 0) { - empeg_close (&serial->port[i], NULL); - } - } - } diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/serial/ftdi_sio.c Wed Mar 27 16:40:08 2002 @@ -294,14 +294,8 @@ static void ftdi_sio_shutdown (struct usb_serial *serial) { - dbg (__FUNCTION__); - - /* stop reads and writes on all ports */ - while (serial->port[0].open_count > 0) { - ftdi_sio_close (&serial->port[0], NULL); - } if (serial->port[0].private){ kfree(serial->port[0].private); serial->port[0].private = NULL; @@ -319,45 +313,41 @@ dbg(__FUNCTION__); - ++port->open_count; - - if (port->open_count == 1){ - /* This will push the characters through immediately rather - than queue a task to deliver them */ - port->tty->low_latency = 1; - - /* No error checking for this (will get errors later anyway) */ - /* See ftdi_sio.h for description of what is reset */ - usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, - FTDI_SIO_RESET_SIO, - 0, buf, 0, WDR_TIMEOUT); - - /* Setup termios defaults. According to tty_io.c the - settings are driver specific */ - port->tty->termios->c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - - /* ftdi_sio_set_termios will send usb control messages */ - ftdi_sio_set_termios(port, &tmp_termios); - - /* Turn on RTS and DTR since we are not flow controlling by default */ - if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0) { - err(__FUNCTION__ " Error from DTR HIGH urb"); - } - if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0){ - err(__FUNCTION__ " Error from RTS HIGH urb"); - } - - /* Start reading from the device */ - FILL_BULK_URB(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ftdi_sio_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - err(__FUNCTION__ " - failed submitting read urb, error %d", result); + /* This will push the characters through immediately rather + than queue a task to deliver them */ + port->tty->low_latency = 1; + + /* No error checking for this (will get errors later anyway) */ + /* See ftdi_sio.h for description of what is reset */ + usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, + FTDI_SIO_RESET_SIO, + 0, buf, 0, WDR_TIMEOUT); + + /* Setup termios defaults. According to tty_io.c the + settings are driver specific */ + port->tty->termios->c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + + /* ftdi_sio_set_termios will send usb control messages */ + ftdi_sio_set_termios(port, &tmp_termios); + + /* Turn on RTS and DTR since we are not flow controlling by default */ + if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0) { + err(__FUNCTION__ " Error from DTR HIGH urb"); } + if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0){ + err(__FUNCTION__ " Error from RTS HIGH urb"); + } + + /* Start reading from the device */ + FILL_BULK_URB(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, + ftdi_sio_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) + err(__FUNCTION__ " - failed submitting read urb, error %d", result); return result; } /* ftdi_sio_open */ @@ -371,41 +361,31 @@ dbg( __FUNCTION__); - --port->open_count; - - if (port->open_count <= 0) { - if (serial->dev) { - if (c_cflag & HUPCL){ - /* Disable flow control */ - if (usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0, 0, buf, 0, WDR_TIMEOUT) < 0) { - err("error from flowcontrol urb"); - } - - /* drop DTR */ - if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){ - err("Error from DTR LOW urb"); - } - /* drop RTS */ - if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0) { - err("Error from RTS LOW urb"); - } - } /* Note change no line is hupcl is off */ - - /* shutdown our bulk reads and writes */ - /* ***CHECK*** behaviour when there is nothing queued */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - } - port->open_count = 0; - } else { - /* Send a HUP if necessary */ - if (!(port->tty->termios->c_cflag & CLOCAL)){ - tty_hangup(port->tty); - } + if (serial->dev) { + if (c_cflag & HUPCL){ + /* Disable flow control */ + if (usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, 0, buf, 0, WDR_TIMEOUT) < 0) { + err("error from flowcontrol urb"); + } + + /* drop DTR */ + if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) < 0){ + err("Error from DTR LOW urb"); + } + /* drop RTS */ + if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0) { + err("Error from RTS LOW urb"); + } + } /* Note change no line is hupcl is off */ + + /* shutdown our bulk reads and writes */ + /* ***CHECK*** behaviour when there is nothing queued */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); } } /* ftdi_sio_close */ diff -Nru a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c --- a/drivers/usb/serial/io_edgeport.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/serial/io_edgeport.c Wed Mar 27 16:40:09 2002 @@ -987,124 +987,117 @@ if (edge_port == NULL) return -ENODEV; - ++port->open_count; - - if (port->open_count == 1) { - /* force low_latency on so that our tty_push actually forces the data through, - otherwise it is scheduled, and with high data rates (like with OHCI) data - can get lost. */ - port->tty->low_latency = 1; + /* force low_latency on so that our tty_push actually forces the data through, + otherwise it is scheduled, and with high data rates (like with OHCI) data + can get lost. */ + port->tty->low_latency = 1; + + /* see if we've set up our endpoint info yet (can't set it up in edge_startup + as the structures were not set up at that time.) */ + serial = port->serial; + edge_serial = (struct edgeport_serial *)serial->private; + if (edge_serial == NULL) { + return -ENODEV; + } + if (edge_serial->interrupt_in_buffer == NULL) { + struct usb_serial_port *port0 = &serial->port[0]; + + /* not set up yet, so do it now */ + edge_serial->interrupt_in_buffer = port0->interrupt_in_buffer; + edge_serial->interrupt_in_endpoint = port0->interrupt_in_endpointAddress; + edge_serial->interrupt_read_urb = port0->interrupt_in_urb; + edge_serial->bulk_in_buffer = port0->bulk_in_buffer; + edge_serial->bulk_in_endpoint = port0->bulk_in_endpointAddress; + edge_serial->read_urb = port0->read_urb; + edge_serial->bulk_out_endpoint = port0->bulk_out_endpointAddress; - /* see if we've set up our endpoint info yet (can't set it up in edge_startup - as the structures were not set up at that time.) */ - serial = port->serial; - edge_serial = (struct edgeport_serial *)serial->private; - if (edge_serial == NULL) { - port->open_count = 0; - return -ENODEV; - } - if (edge_serial->interrupt_in_buffer == NULL) { - struct usb_serial_port *port0 = &serial->port[0]; - - /* not set up yet, so do it now */ - edge_serial->interrupt_in_buffer = port0->interrupt_in_buffer; - edge_serial->interrupt_in_endpoint = port0->interrupt_in_endpointAddress; - edge_serial->interrupt_read_urb = port0->interrupt_in_urb; - edge_serial->bulk_in_buffer = port0->bulk_in_buffer; - edge_serial->bulk_in_endpoint = port0->bulk_in_endpointAddress; - edge_serial->read_urb = port0->read_urb; - edge_serial->bulk_out_endpoint = port0->bulk_out_endpointAddress; + /* set up our interrupt urb */ + FILL_INT_URB(edge_serial->interrupt_read_urb, + serial->dev, + usb_rcvintpipe(serial->dev, + port0->interrupt_in_endpointAddress), + port0->interrupt_in_buffer, + edge_serial->interrupt_read_urb->transfer_buffer_length, + edge_interrupt_callback, edge_serial, + edge_serial->interrupt_read_urb->interval); - /* set up our interrupt urb */ - FILL_INT_URB(edge_serial->interrupt_read_urb, - serial->dev, - usb_rcvintpipe(serial->dev, - port0->interrupt_in_endpointAddress), - port0->interrupt_in_buffer, - edge_serial->interrupt_read_urb->transfer_buffer_length, - edge_interrupt_callback, edge_serial, - edge_serial->interrupt_read_urb->interval); - - /* set up our bulk in urb */ - FILL_BULK_URB(edge_serial->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port0->bulk_in_endpointAddress), - port0->bulk_in_buffer, - edge_serial->read_urb->transfer_buffer_length, - edge_bulk_in_callback, edge_serial); - - /* start interrupt read for this edgeport - * this interrupt will continue as long as the edgeport is connected */ - response = usb_submit_urb (edge_serial->interrupt_read_urb, GFP_KERNEL); - if (response) { - err(__FUNCTION__" - Error %d submitting control urb", response); - } + /* set up our bulk in urb */ + FILL_BULK_URB(edge_serial->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port0->bulk_in_endpointAddress), + port0->bulk_in_buffer, + edge_serial->read_urb->transfer_buffer_length, + edge_bulk_in_callback, edge_serial); + + /* start interrupt read for this edgeport + * this interrupt will continue as long as the edgeport is connected */ + response = usb_submit_urb (edge_serial->interrupt_read_urb, GFP_KERNEL); + if (response) { + err(__FUNCTION__" - Error %d submitting control urb", response); } - - /* initialize our wait queues */ - init_waitqueue_head(&edge_port->wait_open); - init_waitqueue_head(&edge_port->wait_chase); - init_waitqueue_head(&edge_port->delta_msr_wait); - init_waitqueue_head(&edge_port->wait_command); - - /* initialize our icount structure */ - memset (&(edge_port->icount), 0x00, sizeof(edge_port->icount)); - - /* initialize our port settings */ - edge_port->txCredits = 0; /* Can't send any data yet */ - edge_port->shadowMCR = MCR_MASTER_IE; /* Must always set this bit to enable ints! */ - edge_port->chaseResponsePending = FALSE; + } + + /* initialize our wait queues */ + init_waitqueue_head(&edge_port->wait_open); + init_waitqueue_head(&edge_port->wait_chase); + init_waitqueue_head(&edge_port->delta_msr_wait); + init_waitqueue_head(&edge_port->wait_command); + + /* initialize our icount structure */ + memset (&(edge_port->icount), 0x00, sizeof(edge_port->icount)); + + /* initialize our port settings */ + edge_port->txCredits = 0; /* Can't send any data yet */ + edge_port->shadowMCR = MCR_MASTER_IE; /* Must always set this bit to enable ints! */ + edge_port->chaseResponsePending = FALSE; + + /* send a open port command */ + edge_port->openPending = TRUE; + edge_port->open = FALSE; + response = send_iosp_ext_cmd (edge_port, IOSP_CMD_OPEN_PORT, 0); - /* send a open port command */ - edge_port->openPending = TRUE; - edge_port->open = FALSE; - response = send_iosp_ext_cmd (edge_port, IOSP_CMD_OPEN_PORT, 0); + if (response < 0) { + err(__FUNCTION__" - error sending open port command"); + edge_port->openPending = FALSE; + return -ENODEV; + } - if (response < 0) { - err(__FUNCTION__" - error sending open port command"); - edge_port->openPending = FALSE; - port->open_count = 0; - return -ENODEV; - } - - /* now wait for the port to be completly opened */ - timeout = OPEN_TIMEOUT; - while (timeout && edge_port->openPending == TRUE) { - timeout = interruptible_sleep_on_timeout (&edge_port->wait_open, timeout); - } - - if (edge_port->open == FALSE) { - /* open timed out */ - dbg(__FUNCTION__" - open timedout"); - edge_port->openPending = FALSE; - port->open_count = 0; - return -ENODEV; - } - - /* create the txfifo */ - edge_port->txfifo.head = 0; - edge_port->txfifo.tail = 0; - edge_port->txfifo.count = 0; - edge_port->txfifo.size = edge_port->maxTxCredits; - edge_port->txfifo.fifo = kmalloc (edge_port->maxTxCredits, GFP_KERNEL); - - if (!edge_port->txfifo.fifo) { - dbg(__FUNCTION__" - no memory"); - edge_close (port, filp); - return -ENOMEM; - } + /* now wait for the port to be completly opened */ + timeout = OPEN_TIMEOUT; + while (timeout && edge_port->openPending == TRUE) { + timeout = interruptible_sleep_on_timeout (&edge_port->wait_open, timeout); + } - /* Allocate a URB for the write */ - edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL); + if (edge_port->open == FALSE) { + /* open timed out */ + dbg(__FUNCTION__" - open timedout"); + edge_port->openPending = FALSE; + return -ENODEV; + } - if (!edge_port->write_urb) { - dbg(__FUNCTION__" - no memory"); - edge_close (port, filp); - return -ENOMEM; - } + /* create the txfifo */ + edge_port->txfifo.head = 0; + edge_port->txfifo.tail = 0; + edge_port->txfifo.count = 0; + edge_port->txfifo.size = edge_port->maxTxCredits; + edge_port->txfifo.fifo = kmalloc (edge_port->maxTxCredits, GFP_KERNEL); + + if (!edge_port->txfifo.fifo) { + dbg(__FUNCTION__" - no memory"); + edge_close (port, filp); + return -ENOMEM; + } + + /* Allocate a URB for the write */ + edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL); - dbg(__FUNCTION__"(%d) - Initialize TX fifo to %d bytes", port->number, edge_port->maxTxCredits); + if (!edge_port->write_urb) { + dbg(__FUNCTION__" - no memory"); + edge_close (port, filp); + return -ENOMEM; } + dbg(__FUNCTION__"(%d) - Initialize TX fifo to %d bytes", port->number, edge_port->maxTxCredits); + dbg(__FUNCTION__" exited"); return 0; @@ -1234,52 +1227,47 @@ if ((edge_serial == NULL) || (edge_port == NULL)) return; - --port->open_count; + if (serial->dev) { + // block until tx is empty + block_until_tx_empty(edge_port); + + edge_port->closePending = TRUE; + + /* flush and chase */ + edge_port->chaseResponsePending = TRUE; + + dbg(__FUNCTION__" - Sending IOSP_CMD_CHASE_PORT"); + status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); + if (status == 0) { + // block until chase finished + block_until_chase_response(edge_port); + } else { + edge_port->chaseResponsePending = FALSE; + } - if (port->open_count <= 0) { - if (serial->dev) { - // block until tx is empty - block_until_tx_empty(edge_port); - - edge_port->closePending = TRUE; - - /* flush and chase */ - edge_port->chaseResponsePending = TRUE; - - dbg(__FUNCTION__" - Sending IOSP_CMD_CHASE_PORT"); - status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); - if (status == 0) { - // block until chase finished - block_until_chase_response(edge_port); - } else { - edge_port->chaseResponsePending = FALSE; - } + /* close the port */ + dbg(__FUNCTION__" - Sending IOSP_CMD_CLOSE_PORT"); + send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); - /* close the port */ - dbg(__FUNCTION__" - Sending IOSP_CMD_CLOSE_PORT"); - send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); - - //port->close = TRUE; - edge_port->closePending = FALSE; - edge_port->open = FALSE; - edge_port->openPending = FALSE; + //port->close = TRUE; + edge_port->closePending = FALSE; + edge_port->open = FALSE; + edge_port->openPending = FALSE; - if (edge_port->write_urb) { - usb_unlink_urb (edge_port->write_urb); - } - } - if (edge_port->write_urb) { - /* if this urb had a transfer buffer already (old transfer) free it */ - if (edge_port->write_urb->transfer_buffer != NULL) { - kfree(edge_port->write_urb->transfer_buffer); - } - usb_free_urb (edge_port->write_urb); + usb_unlink_urb (edge_port->write_urb); } - if (edge_port->txfifo.fifo) { - kfree(edge_port->txfifo.fifo); + } + + if (edge_port->write_urb) { + /* if this urb had a transfer buffer already (old transfer) free it */ + if (edge_port->write_urb->transfer_buffer != NULL) { + kfree(edge_port->write_urb->transfer_buffer); } - port->open_count = 0; + usb_free_urb (edge_port->write_urb); + } + if (edge_port->txfifo.fifo) { + kfree(edge_port->txfifo.fifo); } dbg(__FUNCTION__" exited"); @@ -3027,9 +3015,6 @@ /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i].open_count > 0) { - edge_close (&serial->port[i], NULL); - } kfree (serial->port[i].private); serial->port[i].private = NULL; } diff -Nru a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c --- a/drivers/usb/serial/ipaq.c Wed Mar 27 16:40:10 2002 +++ b/drivers/usb/serial/ipaq.c Wed Mar 27 16:40:10 2002 @@ -9,6 +9,10 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * + * (19/3/2002) ganesh + * Don't submit urbs while holding spinlocks. Not strictly necessary + * in 2.5.x. + * * (8/3/2002) ganesh * The ipaq sometimes emits a '\0' before the CLIENT string. At this * point of time, the ppp ldisc is not yet attached to the tty, so @@ -65,7 +69,7 @@ int count); static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); -static int ipaq_write_flush(struct usb_serial_port *port); +static void ipaq_write_gather(struct usb_serial_port *port); static void ipaq_read_bulk_callback (struct urb *urb); static void ipaq_write_bulk_callback(struct urb *urb); static int ipaq_write_room(struct usb_serial_port *port); @@ -119,93 +123,89 @@ dbg(__FUNCTION__ " - port %d", port->number); - ++port->open_count; - - if (port->open_count == 1) { - bytes_in = 0; - bytes_out = 0; - priv = (struct ipaq_private *)kmalloc(sizeof(struct ipaq_private), GFP_KERNEL); - if (priv == NULL) { - err(__FUNCTION__ " - Out of memory"); - return -ENOMEM; - } - port->private = (void *)priv; - priv->active = 0; - priv->queue_len = 0; - INIT_LIST_HEAD(&priv->queue); - INIT_LIST_HEAD(&priv->freelist); - - for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) { - pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL); - if (pkt == NULL) { - goto enomem; - } - pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL); - if (pkt->data == NULL) { - kfree(pkt); - goto enomem; - } - pkt->len = 0; - pkt->written = 0; - INIT_LIST_HEAD(&pkt->list); - list_add(&pkt->list, &priv->freelist); - priv->free_len += PACKET_SIZE; - } - - /* - * Force low latency on. This will immediately push data to the line - * discipline instead of queueing. - */ - - port->tty->low_latency = 1; - port->tty->raw = 1; - port->tty->real_raw = 1; - - /* - * Lose the small buffers usbserial provides. Make larger ones. - */ - - kfree(port->bulk_in_buffer); - kfree(port->bulk_out_buffer); - port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL); - if (port->bulk_in_buffer == NULL) { + bytes_in = 0; + bytes_out = 0; + priv = (struct ipaq_private *)kmalloc(sizeof(struct ipaq_private), GFP_KERNEL); + if (priv == NULL) { + err(__FUNCTION__ " - Out of memory"); + return -ENOMEM; + } + port->private = (void *)priv; + priv->active = 0; + priv->queue_len = 0; + INIT_LIST_HEAD(&priv->queue); + INIT_LIST_HEAD(&priv->freelist); + + for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) { + pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL); + if (pkt == NULL) { goto enomem; } - port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL); - if (port->bulk_out_buffer == NULL) { - kfree(port->bulk_in_buffer); + pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL); + if (pkt->data == NULL) { + kfree(pkt); goto enomem; } - port->read_urb->transfer_buffer = port->bulk_in_buffer; - port->write_urb->transfer_buffer = port->bulk_out_buffer; - port->read_urb->transfer_buffer_length = URBDATA_SIZE; - port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE; - - /* Start reading from the device */ - FILL_BULK_URB(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - ipaq_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) { - err(__FUNCTION__ " - failed submitting read urb, error %d", result); - } + pkt->len = 0; + pkt->written = 0; + INIT_LIST_HEAD(&pkt->list); + list_add(&pkt->list, &priv->freelist); + priv->free_len += PACKET_SIZE; + } + + /* + * Force low latency on. This will immediately push data to the line + * discipline instead of queueing. + */ + + port->tty->low_latency = 1; + port->tty->raw = 1; + port->tty->real_raw = 1; + + /* + * Lose the small buffers usbserial provides. Make larger ones. + */ + + kfree(port->bulk_in_buffer); + kfree(port->bulk_out_buffer); + port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL); + if (port->bulk_in_buffer == NULL) { + goto enomem; + } + port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL); + if (port->bulk_out_buffer == NULL) { + kfree(port->bulk_in_buffer); + goto enomem; + } + port->read_urb->transfer_buffer = port->bulk_in_buffer; + port->write_urb->transfer_buffer = port->bulk_out_buffer; + port->read_urb->transfer_buffer_length = URBDATA_SIZE; + port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE; + + /* Start reading from the device */ + FILL_BULK_URB(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, + ipaq_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) { + err(__FUNCTION__ " - failed submitting read urb, error %d", result); + } - /* - * Send out two control messages observed in win98 sniffs. Not sure what - * they do. - */ - - result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21, - 0x1, 0, NULL, 0, 5 * HZ); - if (result < 0) { - err(__FUNCTION__ " - failed doing control urb, error %d", result); - } - result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21, - 0x1, 0, NULL, 0, 5 * HZ); - if (result < 0) { - err(__FUNCTION__ " - failed doing control urb, error %d", result); - } + /* + * Send out two control messages observed in win98 sniffs. Not sure what + * they do. + */ + + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21, + 0x1, 0, NULL, 0, 5 * HZ); + if (result < 0) { + err(__FUNCTION__ " - failed doing control urb, error %d", result); + } + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21, + 0x1, 0, NULL, 0, 5 * HZ); + if (result < 0) { + err(__FUNCTION__ " - failed doing control urb, error %d", result); } return result; @@ -233,22 +233,16 @@ if (!serial) return; - --port->open_count; - - if (port->open_count <= 0) { + /* + * shut down bulk read and write + */ - /* - * shut down bulk read and write - */ - - usb_unlink_urb(port->write_urb); - usb_unlink_urb(port->read_urb); - ipaq_destroy_lists(port); - kfree(priv); - port->private = NULL; - port->open_count = 0; + usb_unlink_urb(port->write_urb); + usb_unlink_urb(port->read_urb); + ipaq_destroy_lists(port); + kfree(priv); + port->private = NULL; - } /* Uncomment the following line if you want to see some statistics in your syslog */ /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */ } @@ -367,17 +361,23 @@ priv->queue_len += count; if (priv->active == 0) { priv->active = 1; - result = ipaq_write_flush(port); + ipaq_write_gather(port); + spin_unlock_irqrestore(&write_list_lock, flags); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (result) { + err(__FUNCTION__ " - failed submitting write urb, error %d", result); + } + } else { + spin_unlock_irqrestore(&write_list_lock, flags); } - spin_unlock_irqrestore(&write_list_lock, flags); return result; } -static int ipaq_write_flush(struct usb_serial_port *port) +static void ipaq_write_gather(struct usb_serial_port *port) { struct ipaq_private *priv = (struct ipaq_private *)port->private; struct usb_serial *serial = port->serial; - int count, room, result; + int count, room; struct ipaq_packet *pkt; struct urb *urb = port->write_urb; struct list_head *tmp; @@ -385,7 +385,7 @@ if (urb->status == -EINPROGRESS) { /* Should never happen */ err(__FUNCTION__ " - flushing while urb is active !"); - return -EAGAIN; + return; } room = URBDATA_SIZE; for (tmp = priv->queue.next; tmp != &priv->queue;) { @@ -412,11 +412,7 @@ usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, count, ipaq_write_bulk_callback, port); - result = usb_submit_urb(urb, GFP_ATOMIC); - if (result) { - err(__FUNCTION__ " - failed submitting write urb, error %d", result); - } - return result; + return; } static void ipaq_write_bulk_callback(struct urb *urb) @@ -424,6 +420,7 @@ struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct ipaq_private *priv = (struct ipaq_private *)port->private; unsigned long flags; + int result; if (port_paranoia_check (port, __FUNCTION__)) { return; @@ -437,11 +434,16 @@ spin_lock_irqsave(&write_list_lock, flags); if (!list_empty(&priv->queue)) { - ipaq_write_flush(port); + ipaq_write_gather(port); + spin_unlock_irqrestore(&write_list_lock, flags); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); + if (result) { + err(__FUNCTION__ " - failed submitting write urb, error %d", result); + } } else { priv->active = 0; + spin_unlock_irqrestore(&write_list_lock, flags); } - spin_unlock_irqrestore(&write_list_lock, flags); queue_task(&port->tqueue, &tq_immediate); mark_bh(IMMEDIATE_BH); @@ -495,16 +497,7 @@ static void ipaq_shutdown(struct usb_serial *serial) { - int i; - dbg (__FUNCTION__); - - /* stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i].open_count > 0) { - ipaq_close(&serial->port[i], NULL); - } - } } static int __init ipaq_init(void) diff -Nru a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c --- a/drivers/usb/serial/ir-usb.c Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/serial/ir-usb.c Wed Mar 27 16:40:08 2002 @@ -283,45 +283,42 @@ dbg("%s - port %d", __FUNCTION__, port->number); - ++port->open_count; - - if (port->open_count == 1) { - if (buffer_size) { - /* override the default buffer sizes */ - buffer = kmalloc (buffer_size, GFP_KERNEL); - if (!buffer) { - err ("%s - out of memory.", __FUNCTION__); - return -ENOMEM; - } - kfree (port->read_urb->transfer_buffer); - port->read_urb->transfer_buffer = buffer; - port->read_urb->transfer_buffer_length = buffer_size; - - buffer = kmalloc (buffer_size, GFP_KERNEL); - if (!buffer) { - err ("%s - out of memory.", __FUNCTION__); - return -ENOMEM; - } - kfree (port->write_urb->transfer_buffer); - port->write_urb->transfer_buffer = buffer; - port->write_urb->transfer_buffer_length = buffer_size; - port->bulk_out_size = buffer_size; + if (buffer_size) { + /* override the default buffer sizes */ + buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!buffer) { + err ("%s - out of memory.", __FUNCTION__); + return -ENOMEM; } - - /* Start reading from the device */ - usb_fill_bulk_urb ( - port->read_urb, - serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ir_read_bulk_callback, - port); - port->read_urb->transfer_flags = USB_QUEUE_BULK; - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - err("%s - failed submitting read urb, error %d", __FUNCTION__, result); + kfree (port->read_urb->transfer_buffer); + port->read_urb->transfer_buffer = buffer; + port->read_urb->transfer_buffer_length = buffer_size; + + buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!buffer) { + err ("%s - out of memory.", __FUNCTION__); + return -ENOMEM; + } + kfree (port->write_urb->transfer_buffer); + port->write_urb->transfer_buffer = buffer; + port->write_urb->transfer_buffer_length = buffer_size; + port->bulk_out_size = buffer_size; } + + /* Start reading from the device */ + usb_fill_bulk_urb ( + port->read_urb, + serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ir_read_bulk_callback, + port); + port->read_urb->transfer_flags = USB_QUEUE_BULK; + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) + err("%s - failed submitting read urb, error %d", __FUNCTION__, result); + return result; } @@ -338,15 +335,9 @@ if (!serial) return; - --port->open_count; - - if (port->open_count <= 0) { - if (serial->dev) { - /* shutdown our bulk read */ - usb_unlink_urb (port->read_urb); - } - port->open_count = 0; - + if (serial->dev) { + /* shutdown our bulk read */ + usb_unlink_urb (port->read_urb); } } diff -Nru a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c --- a/drivers/usb/serial/keyspan.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/serial/keyspan.c Wed Mar 27 16:40:09 2002 @@ -852,7 +852,7 @@ struct keyspan_serial_private *s_priv; struct usb_serial *serial = port->serial; const struct keyspan_device_details *d_details; - int i, already_active, err; + int i, err; struct urb *urb; s_priv = (struct keyspan_serial_private *)(serial->private); @@ -861,12 +861,6 @@ dbg("keyspan_open called for port%d.\n", port->number); - already_active = port->open_count; - ++port->open_count; - - if (already_active) - return 0; - p_priv = (struct keyspan_port_private *)(port->private); /* Set some sane defaults */ @@ -924,19 +918,16 @@ p_priv->out_flip = 0; p_priv->in_flip = 0; - if (--port->open_count <= 0) { - if (serial->dev) { - /* Stop reading/writing urbs */ - stop_urb(p_priv->inack_urb); - stop_urb(p_priv->outcont_urb); - for (i = 0; i < 2; i++) { - stop_urb(p_priv->in_urbs[i]); - stop_urb(p_priv->out_urbs[i]); - } + if (serial->dev) { + /* Stop reading/writing urbs */ + stop_urb(p_priv->inack_urb); + stop_urb(p_priv->outcont_urb); + for (i = 0; i < 2; i++) { + stop_urb(p_priv->in_urbs[i]); + stop_urb(p_priv->out_urbs[i]); } - port->open_count = 0; - port->tty = 0; } + port->tty = 0; } @@ -1762,9 +1753,6 @@ /* Now free per port private data */ for (i = 0; i < serial->num_ports; i++) { port = &serial->port[i]; - while (port->open_count > 0) { - --port->open_count; - } kfree(port->private); } } diff -Nru a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c --- a/drivers/usb/serial/keyspan_pda.c Wed Mar 27 16:40:05 2002 +++ b/drivers/usb/serial/keyspan_pda.c Wed Mar 27 16:40:05 2002 @@ -662,52 +662,45 @@ int rc = 0; struct keyspan_pda_private *priv; - ++port->open_count; - - if (port->open_count == 1) { - /* find out how much room is in the Tx ring */ - rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), - 6, /* write_room */ - USB_TYPE_VENDOR | USB_RECIP_INTERFACE - | USB_DIR_IN, - 0, /* value */ - 0, /* index */ - &room, - 1, - 2*HZ); - if (rc < 0) { - dbg(__FUNCTION__" - roomquery failed"); - goto error; - } - if (rc == 0) { - dbg(__FUNCTION__" - roomquery returned 0 bytes"); - rc = -EIO; - goto error; - } - priv = (struct keyspan_pda_private *)(port->private); - priv->tx_room = room; - priv->tx_throttled = room ? 0 : 1; - - /* the normal serial device seems to always turn on DTR and RTS here, - so do the same */ - if (port->tty->termios->c_cflag & CBAUD) - keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) ); - else - keyspan_pda_set_modem_info(serial, 0); - - /*Start reading from the device*/ - port->interrupt_in_urb->dev = serial->dev; - rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (rc) { - dbg(__FUNCTION__" - usb_submit_urb(read int) failed"); - goto error; - } - + /* find out how much room is in the Tx ring */ + rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + 6, /* write_room */ + USB_TYPE_VENDOR | USB_RECIP_INTERFACE + | USB_DIR_IN, + 0, /* value */ + 0, /* index */ + &room, + 1, + 2*HZ); + if (rc < 0) { + dbg(__FUNCTION__" - roomquery failed"); + goto error; + } + if (rc == 0) { + dbg(__FUNCTION__" - roomquery returned 0 bytes"); + rc = -EIO; + goto error; + } + priv = (struct keyspan_pda_private *)(port->private); + priv->tx_room = room; + priv->tx_throttled = room ? 0 : 1; + + /* the normal serial device seems to always turn on DTR and RTS here, + so do the same */ + if (port->tty->termios->c_cflag & CBAUD) + keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) ); + else + keyspan_pda_set_modem_info(serial, 0); + + /*Start reading from the device*/ + port->interrupt_in_urb->dev = serial->dev; + rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (rc) { + dbg(__FUNCTION__" - usb_submit_urb(read int) failed"); + goto error; } - return rc; error: - --port->open_count; return rc; } @@ -716,19 +709,14 @@ { struct usb_serial *serial = port->serial; - --port->open_count; + if (serial->dev) { + /* the normal serial device seems to always shut off DTR and RTS now */ + if (port->tty->termios->c_cflag & HUPCL) + keyspan_pda_set_modem_info(serial, 0); - if (port->open_count <= 0) { - if (serial->dev) { - /* the normal serial device seems to always shut off DTR and RTS now */ - if (port->tty->termios->c_cflag & HUPCL) - keyspan_pda_set_modem_info(serial, 0); - - /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->interrupt_in_urb); - } - port->open_count = 0; + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->interrupt_in_urb); } } @@ -805,9 +793,6 @@ { dbg (__FUNCTION__); - while (serial->port[0].open_count > 0) { - keyspan_pda_close (&serial->port[0], NULL); - } kfree(serial->port[0].private); } diff -Nru a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c --- a/drivers/usb/serial/kl5kusb105.c Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/serial/kl5kusb105.c Wed Mar 27 16:40:08 2002 @@ -317,9 +317,6 @@ struct klsi_105_private *priv = (struct klsi_105_private*) serial->port[i].private; unsigned long flags; - while (serial->port[i].open_count > 0) { - klsi_105_close (&serial->port[i], NULL); - } if (priv) { /* kill our write urb pool */ @@ -355,85 +352,80 @@ struct usb_serial *serial = port->serial; struct klsi_105_private *priv = (struct klsi_105_private *)port->private; int retval = 0; + int rc; + int i; + unsigned long line_state; dbg(__FUNCTION__" port %d", port->number); - ++port->open_count; - - if (port->open_count == 1) { - int rc; - int i; - unsigned long line_state; - - /* force low_latency on so that our tty_push actually forces - * the data through - * port->tty->low_latency = 1; */ - - /* Do a defined restart: - * Set up sane default baud rate and send the 'READ_ON' - * vendor command. - * FIXME: set modem line control (how?) - * Then read the modem line control and store values in - * priv->line_state. - */ - priv->cfg.pktlen = 5; - priv->cfg.baudrate = kl5kusb105a_sio_b9600; - priv->cfg.databits = kl5kusb105a_dtb_8; - priv->cfg.unknown1 = 0; - priv->cfg.unknown2 = 1; - klsi_105_chg_port_settings(serial, &(priv->cfg)); - - /* set up termios structure */ - priv->termios.c_iflag = port->tty->termios->c_iflag; - priv->termios.c_oflag = port->tty->termios->c_oflag; - priv->termios.c_cflag = port->tty->termios->c_cflag; - priv->termios.c_lflag = port->tty->termios->c_lflag; - for (i=0; itermios.c_cc[i] = port->tty->termios->c_cc[i]; - - - /* READ_ON and urb submission */ - FILL_BULK_URB(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - klsi_105_read_bulk_callback, - port); - port->read_urb->transfer_flags |= USB_QUEUE_BULK; - - rc = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (rc) { - err(__FUNCTION__ - " - failed submitting read urb, error %d", rc); - retval = rc; - goto exit; - } - - rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), - KL5KUSB105A_SIO_CONFIGURE, - USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE, - KL5KUSB105A_SIO_CONFIGURE_READ_ON, - 0, /* index */ - NULL, - 0, - KLSI_TIMEOUT); - if (rc < 0) { - err("Enabling read failed (error = %d)", rc); - retval = rc; - } else - dbg(__FUNCTION__ " - enabled reading"); - - rc = klsi_105_get_line_state(serial, &line_state); - if (rc >= 0) { - priv->line_state = line_state; - dbg(__FUNCTION__ - " - read line state 0x%lx", line_state); - retval = 0; - } else - retval = rc; + /* force low_latency on so that our tty_push actually forces + * the data through + * port->tty->low_latency = 1; */ + + /* Do a defined restart: + * Set up sane default baud rate and send the 'READ_ON' + * vendor command. + * FIXME: set modem line control (how?) + * Then read the modem line control and store values in + * priv->line_state. + */ + priv->cfg.pktlen = 5; + priv->cfg.baudrate = kl5kusb105a_sio_b9600; + priv->cfg.databits = kl5kusb105a_dtb_8; + priv->cfg.unknown1 = 0; + priv->cfg.unknown2 = 1; + klsi_105_chg_port_settings(serial, &(priv->cfg)); + + /* set up termios structure */ + priv->termios.c_iflag = port->tty->termios->c_iflag; + priv->termios.c_oflag = port->tty->termios->c_oflag; + priv->termios.c_cflag = port->tty->termios->c_cflag; + priv->termios.c_lflag = port->tty->termios->c_lflag; + for (i=0; itermios.c_cc[i] = port->tty->termios->c_cc[i]; + + + /* READ_ON and urb submission */ + FILL_BULK_URB(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + klsi_105_read_bulk_callback, + port); + port->read_urb->transfer_flags |= USB_QUEUE_BULK; + + rc = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (rc) { + err(__FUNCTION__ + " - failed submitting read urb, error %d", rc); + retval = rc; + goto exit; } + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), + KL5KUSB105A_SIO_CONFIGURE, + USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE, + KL5KUSB105A_SIO_CONFIGURE_READ_ON, + 0, /* index */ + NULL, + 0, + KLSI_TIMEOUT); + if (rc < 0) { + err("Enabling read failed (error = %d)", rc); + retval = rc; + } else + dbg(__FUNCTION__ " - enabled reading"); + + rc = klsi_105_get_line_state(serial, &line_state); + if (rc >= 0) { + priv->line_state = line_state; + dbg(__FUNCTION__ + " - read line state 0x%lx", line_state); + retval = 0; + } else + retval = rc; + exit: return retval; } /* klsi_105_open */ @@ -444,6 +436,7 @@ struct usb_serial *serial; struct klsi_105_private *priv = (struct klsi_105_private *)port->private; + int rc; dbg(__FUNCTION__" port %d", port->number); serial = get_usb_serial (port, __FUNCTION__); @@ -451,31 +444,26 @@ if(!serial) return; - --port->open_count; - - if (port->open_count <= 0) { - /* send READ_OFF */ - int rc = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - KL5KUSB105A_SIO_CONFIGURE, - USB_TYPE_VENDOR | USB_DIR_OUT, - KL5KUSB105A_SIO_CONFIGURE_READ_OFF, - 0, /* index */ - NULL, 0, - KLSI_TIMEOUT); - if (rc < 0) - err("Disabling read failed (error = %d)", rc); - - /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - /* unlink our write pool */ - /* FIXME */ - /* wgg - do I need this? I think so. */ - usb_unlink_urb (port->interrupt_in_urb); - port->open_count = 0; - info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out); - } + /* send READ_OFF */ + rc = usb_control_msg (serial->dev, + usb_sndctrlpipe(serial->dev, 0), + KL5KUSB105A_SIO_CONFIGURE, + USB_TYPE_VENDOR | USB_DIR_OUT, + KL5KUSB105A_SIO_CONFIGURE_READ_OFF, + 0, /* index */ + NULL, 0, + KLSI_TIMEOUT); + if (rc < 0) + err("Disabling read failed (error = %d)", rc); + + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + /* unlink our write pool */ + /* FIXME */ + /* wgg - do I need this? I think so. */ + usb_unlink_urb (port->interrupt_in_urb); + info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out); } /* klsi_105_close */ diff -Nru a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c --- a/drivers/usb/serial/mct_u232.c Wed Mar 27 16:40:10 2002 +++ b/drivers/usb/serial/mct_u232.c Wed Mar 27 16:40:10 2002 @@ -324,9 +324,6 @@ /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i].open_count > 0) { - mct_u232_close (&serial->port[i], NULL); - } /* My special items, the standard routines free my urbs */ if (serial->port[i].private) kfree(serial->port[i].private); @@ -341,60 +338,55 @@ dbg(__FUNCTION__" port %d", port->number); - ++port->open_count; - - if (port->open_count == 1) { - /* Compensate for a hardware bug: although the Sitecom U232-P25 - * device reports a maximum output packet size of 32 bytes, - * it seems to be able to accept only 16 bytes (and that's what - * SniffUSB says too...) - */ - if (serial->dev->descriptor.idProduct == MCT_U232_SITECOM_PID) - port->bulk_out_size = 16; - - /* Do a defined restart: the normal serial device seems to - * always turn on DTR and RTS here, so do the same. I'm not - * sure if this is really necessary. But it should not harm - * either. - */ - if (port->tty->termios->c_cflag & CBAUD) - priv->control_state = TIOCM_DTR | TIOCM_RTS; - else - priv->control_state = 0; - mct_u232_set_modem_ctrl(serial, priv->control_state); - - priv->last_lcr = (MCT_U232_DATA_BITS_8 | - MCT_U232_PARITY_NONE | - MCT_U232_STOP_BITS_1); - mct_u232_set_line_ctrl(serial, priv->last_lcr); - - /* Read modem status and update control state */ - mct_u232_get_modem_stat(serial, &priv->last_msr); - mct_u232_msr_to_state(&priv->control_state, priv->last_msr); - - { - /* Puh, that's dirty */ - struct usb_serial_port *rport; - rport = &serial->port[1]; - rport->tty = port->tty; - rport->private = port->private; - port->read_urb = rport->interrupt_in_urb; - } - - port->read_urb->dev = port->serial->dev; - retval = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (retval) { - err("usb_submit_urb(read bulk) failed"); - goto exit; - } - - port->interrupt_in_urb->dev = port->serial->dev; - retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (retval) - err(" usb_submit_urb(read int) failed"); + /* Compensate for a hardware bug: although the Sitecom U232-P25 + * device reports a maximum output packet size of 32 bytes, + * it seems to be able to accept only 16 bytes (and that's what + * SniffUSB says too...) + */ + if (serial->dev->descriptor.idProduct == MCT_U232_SITECOM_PID) + port->bulk_out_size = 16; + + /* Do a defined restart: the normal serial device seems to + * always turn on DTR and RTS here, so do the same. I'm not + * sure if this is really necessary. But it should not harm + * either. + */ + if (port->tty->termios->c_cflag & CBAUD) + priv->control_state = TIOCM_DTR | TIOCM_RTS; + else + priv->control_state = 0; + mct_u232_set_modem_ctrl(serial, priv->control_state); + + priv->last_lcr = (MCT_U232_DATA_BITS_8 | + MCT_U232_PARITY_NONE | + MCT_U232_STOP_BITS_1); + mct_u232_set_line_ctrl(serial, priv->last_lcr); + + /* Read modem status and update control state */ + mct_u232_get_modem_stat(serial, &priv->last_msr); + mct_u232_msr_to_state(&priv->control_state, priv->last_msr); + + { + /* Puh, that's dirty */ + struct usb_serial_port *rport; + rport = &serial->port[1]; + rport->tty = port->tty; + rport->private = port->private; + port->read_urb = rport->interrupt_in_urb; + } + port->read_urb->dev = port->serial->dev; + retval = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (retval) { + err("usb_submit_urb(read bulk) failed"); + goto exit; } + port->interrupt_in_urb->dev = port->serial->dev; + retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (retval) + err(" usb_submit_urb(read int) failed"); + exit: return 0; } /* mct_u232_open */ @@ -404,16 +396,11 @@ { dbg(__FUNCTION__" port %d", port->number); - --port->open_count; - - if (port->open_count <= 0) { - if (port->serial->dev) { - /* shutdown our urbs */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - usb_unlink_urb (port->interrupt_in_urb); - } - port->open_count = 0; + if (port->serial->dev) { + /* shutdown our urbs */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + usb_unlink_urb (port->interrupt_in_urb); } } /* mct_u232_close */ diff -Nru a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c --- a/drivers/usb/serial/omninet.c Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/serial/omninet.c Wed Mar 27 16:40:07 2002 @@ -157,29 +157,24 @@ if (!serial) return -ENODEV; - ++port->open_count; - - if (port->open_count == 1) { - od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); - if( !od ) { - err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct omninet_data)); - port->open_count = 0; - return -ENOMEM; - } + od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); + if( !od ) { + err(__FUNCTION__"- kmalloc(%Zd) failed.", sizeof(struct omninet_data)); + return -ENOMEM; + } - port->private = od; - wport = &serial->port[1]; - wport->tty = port->tty; + port->private = od; + wport = &serial->port[1]; + wport->tty = port->tty; - /* Start reading from the device */ - FILL_BULK_URB(port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, - omninet_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - err(__FUNCTION__ " - failed submitting read urb, error %d", result); - } + /* Start reading from the device */ + FILL_BULK_URB(port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, + omninet_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) + err(__FUNCTION__ " - failed submitting read urb, error %d", result); return result; } @@ -199,20 +194,15 @@ if (!serial) return; - --port->open_count; - - if (port->open_count <= 0) { - if (serial->dev) { - wport = &serial->port[1]; - usb_unlink_urb (wport->write_urb); - usb_unlink_urb (port->read_urb); - } - - port->open_count = 0; - od = (struct omninet_data *)port->private; - if (od) - kfree(od); + if (serial->dev) { + wport = &serial->port[1]; + usb_unlink_urb (wport->write_urb); + usb_unlink_urb (port->read_urb); } + + od = (struct omninet_data *)port->private; + if (od) + kfree(od); } @@ -377,10 +367,6 @@ static void omninet_shutdown (struct usb_serial *serial) { dbg (__FUNCTION__); - - while (serial->port[0].open_count > 0) { - omninet_close (&serial->port[0], NULL); - } } diff -Nru a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c --- a/drivers/usb/serial/pl2303.c Wed Mar 27 16:40:05 2002 +++ b/drivers/usb/serial/pl2303.c Wed Mar 27 16:40:05 2002 @@ -1,7 +1,7 @@ /* * Prolific PL2303 USB to serial adaptor driver * - * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) * * Original driver for 2.2.x by anonymous * @@ -12,6 +12,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * 2002_Mar_26 gkh + * allowed driver to work properly if there is no tty assigned to a port + * (this happens for serial console devices.) + * * 2001_Oct_06 gkh * Added RTS and DTR line control. Thanks to joe@bndlg.de for parts of it. * @@ -173,11 +177,6 @@ dbg (__FUNCTION__ " - port %d, %d bytes", port->number, count); - if (!port->tty) { - err (__FUNCTION__ " - no tty???"); - return 0; - } - if (port->write_urb->status == -EINPROGRESS) { dbg (__FUNCTION__ " - already writing"); return 0; @@ -367,56 +366,54 @@ dbg (__FUNCTION__ " - port %d", port->number); - ++port->open_count; - - if (port->open_count == 1) { -#define FISH(a,b,c,d) \ - result=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0), \ - b, a, c, d, buf, 1, 100); \ - dbg("0x%x:0x%x:0x%x:0x%x %d - %x",a,b,c,d,result,buf[0]); - -#define SOUP(a,b,c,d) \ - result=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \ - b, a, c, d, NULL, 0, 100); \ - dbg("0x%x:0x%x:0x%x:0x%x %d",a,b,c,d,result); - - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 0); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); - FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0xc0); - SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 4); +#define FISH(a,b,c,d) \ + result=usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev,0), \ + b, a, c, d, buf, 1, 100); \ + dbg("0x%x:0x%x:0x%x:0x%x %d - %x",a,b,c,d,result,buf[0]); + +#define SOUP(a,b,c,d) \ + result=usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0), \ + b, a, c, d, NULL, 0, 100); \ + dbg("0x%x:0x%x:0x%x:0x%x %d",a,b,c,d,result); + + FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); + SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 0); + FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); + FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); + FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); + SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0x0404, 1); + FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8484, 0); + FISH (VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, 0x8383, 0); + SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 0, 1); + SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 1, 0xc0); + SOUP (VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, 2, 4); - /* Setup termios */ + /* Setup termios */ + if (port->tty) { *(port->tty->termios) = tty_std_termios; port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; pl2303_set_termios (port, &tmp_termios); + } - //FIXME: need to assert RTS and DTR if CRTSCTS off - - dbg (__FUNCTION__ " - submitting read urb"); - port->read_urb->dev = serial->dev; - result = usb_submit_urb (port->read_urb, GFP_KERNEL); - if (result) { - err(__FUNCTION__ " - failed submitting read urb, error %d", result); - pl2303_close (port, NULL); - return -EPROTO; - } + //FIXME: need to assert RTS and DTR if CRTSCTS off - dbg (__FUNCTION__ " - submitting interrupt urb"); - port->interrupt_in_urb->dev = serial->dev; - result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL); - if (result) { - err(__FUNCTION__ " - failed submitting interrupt urb, error %d", result); - pl2303_close (port, NULL); - return -EPROTO; - } + dbg (__FUNCTION__ " - submitting read urb"); + port->read_urb->dev = serial->dev; + result = usb_submit_urb (port->read_urb, GFP_KERNEL); + if (result) { + err(__FUNCTION__ " - failed submitting read urb, error %d", result); + pl2303_close (port, NULL); + return -EPROTO; + } + + dbg (__FUNCTION__ " - submitting interrupt urb"); + port->interrupt_in_urb->dev = serial->dev; + result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL); + if (result) { + err(__FUNCTION__ " - failed submitting interrupt urb, error %d", result); + pl2303_close (port, NULL); + return -EPROTO; } return 0; } @@ -437,9 +434,8 @@ dbg (__FUNCTION__ " - port %d", port->number); - --port->open_count; - if (port->open_count <= 0) { - if (serial->dev) { + if (serial->dev) { + if (port->tty) { c_cflag = port->tty->termios->c_cflag; if (c_cflag & HUPCL) { /* drop DTR and RTS */ @@ -448,28 +444,27 @@ set_control_lines (port->serial->dev, priv->line_control); } - - /* shutdown our urbs */ - dbg (__FUNCTION__ " - shutting down urbs"); - result = usb_unlink_urb (port->write_urb); - if (result) - dbg (__FUNCTION__ " - usb_unlink_urb " - "(write_urb) failed with reason: %d", - result); - - result = usb_unlink_urb (port->read_urb); - if (result) - dbg (__FUNCTION__ " - usb_unlink_urb " - "(read_urb) failed with reason: %d", - result); - - result = usb_unlink_urb (port->interrupt_in_urb); - if (result) - dbg (__FUNCTION__ " - usb_unlink_urb " - "(interrupt_in_urb) failed with reason: %d", - result); } - port->open_count = 0; + + /* shutdown our urbs */ + dbg (__FUNCTION__ " - shutting down urbs"); + result = usb_unlink_urb (port->write_urb); + if (result) + dbg (__FUNCTION__ " - usb_unlink_urb " + "(write_urb) failed with reason: %d", + result); + + result = usb_unlink_urb (port->read_urb); + if (result) + dbg (__FUNCTION__ " - usb_unlink_urb " + "(read_urb) failed with reason: %d", + result); + + result = usb_unlink_urb (port->interrupt_in_urb); + if (result) + dbg (__FUNCTION__ " - usb_unlink_urb " + "(interrupt_in_urb) failed with reason: %d", + result); } } @@ -577,12 +572,8 @@ dbg (__FUNCTION__); - /* stop everything on all ports */ for (i = 0; i < serial->num_ports; ++i) - while (serial->port[i].open_count > 0) { - pl2303_close (&serial->port[i], NULL); - kfree (serial->port[i].private); - } + kfree (serial->port[i].private); } @@ -655,7 +646,7 @@ usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); tty = port->tty; - if (urb->actual_length) { + if (tty && urb->actual_length) { for (i = 0; i < urb->actual_length; ++i) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { tty_flip_buffer_push(tty); diff -Nru a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h --- a/drivers/usb/serial/usb-serial.h Wed Mar 27 16:40:06 2002 +++ b/drivers/usb/serial/usb-serial.h Wed Mar 27 16:40:06 2002 @@ -1,7 +1,7 @@ /* * USB Serial Converter driver * - * Copyright (C) 1999 - 2001 + * Copyright (C) 1999 - 2002 * Greg Kroah-Hartman (greg@kroah.com) * * This program is free software; you can redistribute it and/or modify @@ -11,6 +11,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (03/26/2002) gkh + * removed the port->tty check from port_paranoia_check() due to serial + * consoles not having a tty device assigned to them. + * * (12/03/2001) gkh * removed active from the port structure. * added documentation to the usb_serial_device_type structure @@ -224,10 +228,6 @@ } if (!port->serial) { dbg("%s - port->serial == NULL", function); - return -1; - } - if (!port->tty) { - dbg("%s - port->tty == NULL", function); return -1; } diff -Nru a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c --- a/drivers/usb/serial/usbserial.c Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/serial/usbserial.c Wed Mar 27 16:40:08 2002 @@ -15,6 +15,19 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (03/27/2002) gkh + * Got USB serial console code working properly and merged into the main + * version of the tree. Thanks to Randy Dunlap for the initial version + * of this code, and for pushing me to finish it up. + * The USB serial console works with any usb serial driver device. + * + * (03/21/2002) gkh + * Moved all manipulation of port->open_count into the core. Now the + * individual driver's open and close functions are called only when the + * first open() and last close() is called. Making the drivers a bit + * smaller and simpler. + * Fixed a bug if a driver didn't have the owner field set. + * * (02/26/2002) gkh * Moved all locking into the main serial_* functions, instead of having * the individual drivers have to grab the port semaphore. This should @@ -307,6 +320,14 @@ #include #include +#ifdef MODULE +#undef CONFIG_USB_SERIAL_CONSOLE +#endif +#ifdef CONFIG_USB_SERIAL_CONSOLE +#include +#include +#endif + #ifdef CONFIG_USB_SERIAL_DEBUG static int debug = 1; #else @@ -395,6 +416,16 @@ static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ static LIST_HEAD(usb_serial_driver_list); +#ifdef CONFIG_USB_SERIAL_CONSOLE +struct usbcons_info { + int magic; + int break_flag; + struct usb_serial_port *port; +}; + +static struct usbcons_info usbcons_info; +static struct console usbcons; +#endif /* CONFIG_USB_SERIAL_CONSOLE */ static struct usb_serial *get_serial_by_minor (unsigned int minor) { @@ -500,7 +531,7 @@ struct usb_serial *serial; struct usb_serial_port *port; unsigned int portNumber; - int retval; + int retval = 0; dbg(__FUNCTION__); @@ -525,14 +556,21 @@ if (serial->type->owner) __MOD_INC_USE_COUNT(serial->type->owner); - /* pass on to the driver specific version of this function if it is available */ - if (serial->type->open) - retval = serial->type->open(port, filp); - else - retval = generic_open(port, filp); + ++port->open_count; + if (port->open_count == 1) { + /* only call the device specific open if this + * is the first time the port is opened */ + if (serial->type->open) + retval = serial->type->open(port, filp); + else + retval = generic_open(port, filp); + } - if (retval) - __MOD_DEC_USE_COUNT(serial->type->owner); + if (retval) { + port->open_count = 0; + if (serial->type->owner) + __MOD_DEC_USE_COUNT(serial->type->owner); + } up (&port->sem); return retval; @@ -559,11 +597,16 @@ goto exit_no_mod_dec; } - /* pass on to the driver specific version of this function if it is available */ - if (serial->type->close) - serial->type->close(port, filp); - else - generic_close(port, filp); + --port->open_count; + if (port->open_count <= 0) { + /* only call the device specific close if this + * port is being closed by the last owner */ + if (serial->type->close) + serial->type->close(port, filp); + else + generic_close(port, filp); + port->open_count = 0; + } exit: if (serial->type->owner) @@ -791,6 +834,8 @@ static void serial_shutdown (struct usb_serial *serial) { + dbg(__FUNCTION__); + if (serial->type->shutdown) serial->type->shutdown(serial); else @@ -810,54 +855,52 @@ dbg(__FUNCTION__ " - port %d", port->number); - ++port->open_count; - - if (port->open_count == 1) { - /* force low_latency on so that our tty_push actually forces the data through, - otherwise it is scheduled, and with high data rates (like with OHCI) data - can get lost. */ + /* force low_latency on so that our tty_push actually forces the data through, + otherwise it is scheduled, and with high data rates (like with OHCI) data + can get lost. */ + if (port->tty) port->tty->low_latency = 1; - /* if we have a bulk interrupt, start reading from it */ - if (serial->num_bulk_in) { - /* Start reading from the device */ - usb_fill_bulk_urb (port->read_urb, serial->dev, - usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ((serial->type->read_bulk_callback) ? - serial->type->read_bulk_callback : - generic_read_bulk_callback), - port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - err(__FUNCTION__ " - failed resubmitting read urb, error %d", result); - } + /* if we have a bulk interrupt, start reading from it */ + if (serial->num_bulk_in) { + /* Start reading from the device */ + usb_fill_bulk_urb (port->read_urb, serial->dev, + usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ((serial->type->read_bulk_callback) ? + serial->type->read_bulk_callback : + generic_read_bulk_callback), + port); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) + err(__FUNCTION__ " - failed resubmitting read urb, error %d", result); } return result; } -static void generic_close (struct usb_serial_port *port, struct file * filp) +static void generic_cleanup (struct usb_serial_port *port) { struct usb_serial *serial = port->serial; dbg(__FUNCTION__ " - port %d", port->number); - --port->open_count; - - if (port->open_count <= 0) { - if (serial->dev) { - /* shutdown any bulk reads that might be going on */ - if (serial->num_bulk_out) - usb_unlink_urb (port->write_urb); - if (serial->num_bulk_in) - usb_unlink_urb (port->read_urb); - } - port->open_count = 0; + if (serial->dev) { + /* shutdown any bulk reads that might be going on */ + if (serial->num_bulk_out) + usb_unlink_urb (port->write_urb); + if (serial->num_bulk_in) + usb_unlink_urb (port->read_urb); } } +static void generic_close (struct usb_serial_port *port, struct file * filp) +{ + dbg(__FUNCTION__ " - port %d", port->number); + generic_cleanup (port); +} + static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) { struct usb_serial *serial = port->serial; @@ -968,7 +1011,7 @@ usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); tty = port->tty; - if (urb->actual_length) { + if (tty && urb->actual_length) { for (i = 0; i < urb->actual_length ; ++i) { /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */ if(tty->flip.count >= TTY_FLIPBUF_SIZE) { @@ -1025,10 +1068,7 @@ /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { - down (&serial->port[i].sem); - while (serial->port[i].open_count > 0) - generic_close (&serial->port[i], NULL); - up (&serial->port[i].sem); + generic_cleanup (&serial->port[i]); } } @@ -1040,11 +1080,13 @@ dbg(__FUNCTION__ " - port %d", port->number); - if (!serial) { + if (!serial) return; - } tty = port->tty; + if (!tty) + return; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { dbg(__FUNCTION__ " - write wakeup call."); (tty->ldisc.write_wakeup)(tty); @@ -1294,6 +1336,26 @@ type->name, serial->port[i].number, serial->port[i].number); } +#ifdef CONFIG_USB_SERIAL_CONSOLE + if (minor == 0) { + /* + * Call register_console() if this is the first device plugged + * in. If we call it earlier, then the callback to + * console_setup() will fail, as there is not a device seen by + * the USB subsystem yet. + */ + /* + * Register console. + * NOTES: + * console_setup() is called (back) immediately (from register_console). + * console_write() is called immediately from register_console iff + * CON_PRINTBUFFER is set in flags. + */ + dbg ("registering the USB serial console."); + register_console(&usbcons); + } +#endif + return serial; /* success */ @@ -1334,6 +1396,7 @@ struct usb_serial_port *port; int i; + dbg(__FUNCTION__); if (serial) { /* fail all future close/read/write/ioctl/etc calls */ for (i = 0; i < serial->num_ports; ++i) { @@ -1466,6 +1529,9 @@ static void __exit usb_serial_exit(void) { +#ifdef CONFIG_USB_SERIAL_CONSOLE + unregister_console(&usbcons); +#endif #ifdef CONFIG_USB_SERIAL_GENERIC /* remove our generic driver */ @@ -1515,7 +1581,7 @@ -/* If the usb-serial core is build into the core, the usb-serial drivers +/* If the usb-serial core is built into the core, the usb-serial drivers need these symbols to load properly as modules. */ EXPORT_SYMBOL(usb_serial_register); EXPORT_SYMBOL(usb_serial_deregister); @@ -1523,6 +1589,241 @@ EXPORT_SYMBOL(ezusb_writememory); EXPORT_SYMBOL(ezusb_set_reset); #endif + + +#ifdef CONFIG_USB_SERIAL_CONSOLE +/* + * ------------------------------------------------------------ + * USB Serial console driver + * + * Much of the code here is copied from drivers/char/serial.c + * and implements a phony serial console in the same way that + * serial.c does so that in case some software queries it, + * it will get the same results. + * + * Things that are different from the way the serial port code + * does things, is that we call the lower level usb-serial + * driver code to initialize the device, and we set the initial + * console speeds based on the command line arguments. + * ------------------------------------------------------------ + */ + +#if 0 +static kdev_t usb_console_device(struct console *co) +{ + return MKDEV(SERIAL_TTY_MAJOR, co->index); /* TBD */ +} +#endif + +/* + * The parsing of the command line works exactly like the + * serial.c code, except that the specifier is "ttyUSB" instead + * of "ttyS". + */ +static int __init usb_console_setup(struct console *co, char *options) +{ + struct usbcons_info *info = &usbcons_info; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int doflow = 0; + int cflag = CREAD | HUPCL | CLOCAL; + char *s; + struct usb_serial *serial; + struct usb_serial_port *port; + int retval = 0; + struct tty_struct *tty; + struct termios *termios; + + dbg ("%s", __FUNCTION__); + + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while (*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s++ - '0'; + if (*s) doflow = (*s++ == 'r'); + } + + /* build a cflag setting */ + switch (baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + /* + * Set this to a sane value to prevent a divide error + */ + baud = 9600; + break; + } + switch (bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch (parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* grab the first serial port that happens to be connected */ + serial = get_serial_by_minor (0); + if (serial_paranoia_check (serial, __FUNCTION__)) { + /* no device is connected yet, sorry :( */ + err ("No USB device connected to ttyUSB0"); + return -ENODEV; + } + + port = &serial->port[0]; + down (&port->sem); + port->tty = NULL; + + info->port = port; + + ++port->open_count; + if (port->open_count == 1) { + /* only call the device specific open if this + * is the first time the port is opened */ + if (serial->type->open) + retval = serial->type->open(port, NULL); + else + retval = generic_open(port, NULL); + if (retval) + port->open_count = 0; + } + + up (&port->sem); + + if (retval) { + err ("could not open USB console port"); + return retval; + } + + if (serial->type->set_termios) { + /* build up a fake tty structure so that the open call has something + * to look at to get the cflag value */ + tty = kmalloc (sizeof (*tty), GFP_KERNEL); + if (!tty) { + err ("no more memory"); + return -ENOMEM; + } + termios = kmalloc (sizeof (*termios), GFP_KERNEL); + if (!termios) { + err ("no more memory"); + kfree (tty); + return -ENOMEM; + } + memset (tty, 0x00, sizeof(*tty)); + memset (termios, 0x00, sizeof(*termios)); + termios->c_cflag = cflag; + tty->termios = termios; + port->tty = tty; + + /* set up the initial termios settings */ + serial->type->set_termios(port, NULL); + port->tty = NULL; + kfree (termios); + kfree (tty); + } + + return retval; +} + +static void usb_console_write(struct console *co, const char *buf, unsigned count) +{ + static struct usbcons_info *info = &usbcons_info; + struct usb_serial_port *port = info->port; + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); + int retval = -ENODEV; + + if (!serial || !port) + return; + + if (count == 0) + return; + + down (&port->sem); + + dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count); + + if (!port->open_count) { + dbg (__FUNCTION__ " - port not opened"); + goto exit; + } + + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->write) + retval = serial->type->write(port, 0, buf, count); + else + retval = generic_write(port, 0, buf, count); + +exit: + up (&port->sem); + dbg("%s - return value (if we had one): %d", __FUNCTION__, retval); +} + +#if 0 +/* + * Receive character from the serial port + */ +static int usb_console_wait_key(struct console *co) +{ + static struct usbcons_info *info = &usbcons_info; + int c = 0; + + dbg("%s", __FUNCTION__); + + /* maybe use generic_read_bulk_callback ??? */ + + return c; +} +#endif + +static struct console usbcons = { + name: "ttyUSB", /* only [8] */ + write: usb_console_write, +#if 0 + device: usb_console_device, /* TBD */ + wait_key: usb_console_wait_key, /* TBD */ +#endif + setup: usb_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +#endif /* CONFIG_USB_SERIAL_CONSOLE */ /* Module information */ diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c --- a/drivers/usb/serial/visor.c Wed Mar 27 16:40:04 2002 +++ b/drivers/usb/serial/visor.c Wed Mar 27 16:40:04 2002 @@ -11,7 +11,19 @@ * (at your option) any later version. * * See Documentation/usb/usb-serial.txt for more information on using this driver - * + * + * (03/27/2002) gkh + * Removed assumptions that port->tty was always valid (is not true + * for usb serial console devices.) + * + * (03/23/2002) gkh + * Added support for the Palm i705 device, thanks to Thomas Riemer + * for the information. + * + * (03/21/2002) gkh + * Added support for the Palm m130 device, thanks to Udo Eisenbarth + * for the information. + * * (02/27/2002) gkh * Reworked the urb handling logic. We have no more pool, but dynamically * allocate the urb and the transfer buffer on the fly. In testing this @@ -169,6 +181,8 @@ { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) }, @@ -186,6 +200,8 @@ { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_3_5_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_0_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) }, @@ -262,32 +278,32 @@ dbg(__FUNCTION__ " - port %d", port->number); if (!port->read_urb) { + /* this is needed for some brain dead Sony devices */ err ("Device lied about number of ports, please use a lower one."); return -ENODEV; } - ++port->open_count; - - if (port->open_count == 1) { - bytes_in = 0; - bytes_out = 0; - - /* force low_latency on so that our tty_push actually forces the data through, - otherwise it is scheduled, and with high data rates (like with OHCI) data - can get lost. */ + bytes_in = 0; + bytes_out = 0; + + /* + * Force low_latency on so that our tty_push actually forces the data + * through, otherwise it is scheduled, and with high data rates (like + * with OHCI) data can get lost. + */ + if (port->tty) port->tty->low_latency = 1; - - /* Start reading from the device */ - usb_fill_bulk_urb (port->read_urb, serial->dev, - usb_rcvbulkpipe (serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - visor_read_bulk_callback, port); - result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) - err(__FUNCTION__ " - failed submitting read urb, error %d", result); - } + + /* Start reading from the device */ + usb_fill_bulk_urb (port->read_urb, serial->dev, + usb_rcvbulkpipe (serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + visor_read_bulk_callback, port); + result = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (result) + err(__FUNCTION__ " - failed submitting read urb, error %d", result); return result; } @@ -307,28 +323,23 @@ if (!serial) return; - --port->open_count; - - if (port->open_count <= 0) { - if (serial->dev) { - /* only send a shutdown message if the - * device is still here */ - transfer_buffer = kmalloc (0x12, GFP_KERNEL); - if (!transfer_buffer) { - err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12); - } else { - /* send a shutdown message to the device */ - usb_control_msg (serial->dev, - usb_rcvctrlpipe(serial->dev, 0), - VISOR_CLOSE_NOTIFICATION, 0xc2, - 0x0000, 0x0000, - transfer_buffer, 0x12, 300); - kfree (transfer_buffer); - } - /* shutdown our bulk read */ - usb_unlink_urb (port->read_urb); + if (serial->dev) { + /* only send a shutdown message if the + * device is still here */ + transfer_buffer = kmalloc (0x12, GFP_KERNEL); + if (!transfer_buffer) { + err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12); + } else { + /* send a shutdown message to the device */ + usb_control_msg (serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + VISOR_CLOSE_NOTIFICATION, 0xc2, + 0x0000, 0x0000, + transfer_buffer, 0x12, 300); + kfree (transfer_buffer); } - port->open_count = 0; + /* shutdown our bulk read */ + usb_unlink_urb (port->read_urb); } /* Uncomment the following line if you want to see some statistics in your syslog */ /* info ("Bytes In = %d Bytes Out = %d", bytes_in, bytes_out); */ @@ -390,7 +401,7 @@ usb_free_urb (urb); return count; -} +} static int visor_write_room (struct usb_serial_port *port) @@ -471,7 +482,7 @@ usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data); tty = port->tty; - if (urb->actual_length) { + if (tty && urb->actual_length) { for (i = 0; i < urb->actual_length ; ++i) { /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */ if(tty->flip.count >= TTY_FLIPBUF_SIZE) { @@ -481,8 +492,8 @@ tty_insert_flip_char(tty, data[i], 0); } tty_flip_buffer_push(tty); - bytes_in += urb->actual_length; } + bytes_in += urb->actual_length; /* Continue trying to always read */ usb_fill_bulk_urb (port->read_urb, serial->dev, @@ -649,15 +660,8 @@ static void visor_shutdown (struct usb_serial *serial) { - int i; - dbg (__FUNCTION__); - - /* stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) - serial->port[i].open_count = 0; } - static int visor_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) { diff -Nru a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h --- a/drivers/usb/serial/visor.h Wed Mar 27 16:40:07 2002 +++ b/drivers/usb/serial/visor.h Wed Mar 27 16:40:07 2002 @@ -24,7 +24,9 @@ #define PALM_M500_ID 0x0001 #define PALM_M505_ID 0x0002 #define PALM_M515_ID 0x0003 +#define PALM_I705_ID 0x0020 #define PALM_M125_ID 0x0040 +#define PALM_M130_ID 0x0050 #define SONY_VENDOR_ID 0x054C #define SONY_CLIE_3_5_ID 0x0038 diff -Nru a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c --- a/drivers/usb/serial/whiteheat.c Wed Mar 27 16:40:10 2002 +++ b/drivers/usb/serial/whiteheat.c Wed Mar 27 16:40:10 2002 @@ -306,58 +306,49 @@ dbg(__FUNCTION__" - port %d", port->number); - ++port->open_count; - - if (port->open_count == 1) { - /* set up some stuff for our command port */ - command_port = &port->serial->port[COMMAND_PORT]; - if (command_port->private == NULL) { - info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL); - if (info == NULL) { - err(__FUNCTION__ " - out of memory"); - retval = -ENOMEM; - goto error_exit; - } - - init_waitqueue_head(&info->wait_command); - command_port->private = info; - command_port->write_urb->complete = command_port_write_callback; - command_port->read_urb->complete = command_port_read_callback; - command_port->read_urb->dev = port->serial->dev; - command_port->tty = port->tty; /* need this to "fake" our our sanity check macros */ - retval = usb_submit_urb (command_port->read_urb, GFP_KERNEL); - if (retval) { - err(__FUNCTION__ " - failed submitting read urb, error %d", retval); - goto error_exit; - } + /* set up some stuff for our command port */ + command_port = &port->serial->port[COMMAND_PORT]; + if (command_port->private == NULL) { + info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL); + if (info == NULL) { + err(__FUNCTION__ " - out of memory"); + retval = -ENOMEM; + goto exit; } - /* Start reading from the device */ - port->read_urb->dev = port->serial->dev; - retval = usb_submit_urb(port->read_urb, GFP_KERNEL); + init_waitqueue_head(&info->wait_command); + command_port->private = info; + command_port->write_urb->complete = command_port_write_callback; + command_port->read_urb->complete = command_port_read_callback; + command_port->read_urb->dev = port->serial->dev; + command_port->tty = port->tty; /* need this to "fake" our our sanity check macros */ + retval = usb_submit_urb (command_port->read_urb, GFP_KERNEL); if (retval) { err(__FUNCTION__ " - failed submitting read urb, error %d", retval); - goto error_exit; + goto exit; } + } - /* send an open port command */ - /* firmware uses 1 based port numbering */ - open_command.port = port->number - port->serial->minor + 1; - retval = whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); - if (retval) - goto error_exit; - - /* Need to do device specific setup here (control lines, baud rate, etc.) */ - /* FIXME!!! */ + /* Start reading from the device */ + port->read_urb->dev = port->serial->dev; + retval = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (retval) { + err(__FUNCTION__ " - failed submitting read urb, error %d", retval); + goto exit; } - dbg(__FUNCTION__ " - exit"); - return retval; + /* send an open port command */ + /* firmware uses 1 based port numbering */ + open_command.port = port->number - port->serial->minor + 1; + retval = whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); + if (retval) + goto exit; -error_exit: - --port->open_count; + /* Need to do device specific setup here (control lines, baud rate, etc.) */ + /* FIXME!!! */ - dbg(__FUNCTION__ " - error_exit"); +exit: + dbg(__FUNCTION__ " - exit, retval = %d", retval); return retval; } @@ -368,22 +359,17 @@ dbg(__FUNCTION__ " - port %d", port->number); - --port->open_count; + /* send a close command to the port */ + /* firmware uses 1 based port numbering */ + close_command.port = port->number - port->serial->minor + 1; + whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command)); - if (port->open_count <= 0) { - /* send a close command to the port */ - /* firmware uses 1 based port numbering */ - close_command.port = port->number - port->serial->minor + 1; - whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command)); + /* Need to change the control lines here */ + /* FIXME */ - /* Need to change the control lines here */ - /* FIXME */ - - /* shutdown our bulk reads and writes */ - usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - port->open_count = 0; - } + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); } @@ -641,17 +627,9 @@ static void whiteheat_real_shutdown (struct usb_serial *serial) { - struct usb_serial_port *command_port; - int i; + struct usb_serial_port *command_port; dbg(__FUNCTION__); - - /* stop reads and writes on all ports */ - for (i=0; i < serial->num_ports; ++i) { - while (serial->port[i].open_count > 0) { - whiteheat_close (&serial->port[i], NULL); - } - } /* free up our private data for our command port */ command_port = &serial->port[COMMAND_PORT]; diff -Nru a/drivers/usb/uhci.c b/drivers/usb/uhci.c --- a/drivers/usb/uhci.c Wed Mar 27 16:40:08 2002 +++ b/drivers/usb/uhci.c Wed Mar 27 16:40:08 2002 @@ -337,6 +337,7 @@ qh->link = UHCI_PTR_TERM; qh->dev = dev; + qh->urbp = NULL; INIT_LIST_HEAD(&qh->list); INIT_LIST_HEAD(&qh->remove_list); @@ -411,20 +412,19 @@ spin_unlock_irqrestore(&uhci->frame_list_lock, flags); } -static void uhci_remove_qh(struct uhci *uhci, struct urb *urb) +static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh) { - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; unsigned long flags; - struct uhci_qh *qh = urbp->qh, *pqh; + struct uhci_qh *pqh; if (!qh) return; + qh->urbp = NULL; + /* Only go through the hoops if it's actually linked in */ spin_lock_irqsave(&uhci->frame_list_lock, flags); if (!list_empty(&qh->list)) { - qh->urbp = NULL; - pqh = list_entry(qh->list.prev, struct uhci_qh, list); if (pqh->urbp) { @@ -619,7 +619,7 @@ { struct urb_priv *urbp; - urbp = kmem_cache_alloc(uhci_up_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC); if (!urbp) { err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n"); return NULL; @@ -1043,7 +1043,7 @@ urbp->short_control_packet = 1; /* Create a new QH to avoid pointer overwriting problems */ - uhci_remove_qh(uhci, urb); + uhci_remove_qh(uhci, urbp->qh); /* Delete all of the TD's except for the status TD at the end */ head = &urbp->td_list; @@ -1264,20 +1264,29 @@ data); data += pktsze; - len -= pktsze; + len -= maxsze; usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); } while (len > 0); + /* + * USB_ZERO_PACKET means adding a 0-length packet, if + * direction is OUT and the transfer_length was an + * exact multiple of maxsze, hence + * (len = transfer_length - N * maxsze) == 0 + * however, if transfer_length == 0, the zero packet + * was already prepared above. + */ if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) && - urb->transfer_buffer_length) { + !len && urb->transfer_buffer_length) { td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | UHCI_NULL_DATA_SIZE | + uhci_fill_td(td, status, destination | + (UHCI_NULL_DATA_SIZE << 21) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE), data); @@ -1594,7 +1603,9 @@ spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); - uhci_call_completion(urb); + /* Only call completion if it was successful */ + if (!ret) + uhci_call_completion(urb); return ret; } @@ -1660,6 +1671,7 @@ /* Interrupts are an exception */ if (urb->interval) { uhci_add_complete(urb); + spin_unlock_irqrestore(&urb->lock, flags); return; /* <-- note return */ } @@ -1734,7 +1746,8 @@ uhci_delete_queued_urb(uhci, urb); /* The interrupt loop will reclaim the QH's */ - uhci_remove_qh(uhci, urb); + uhci_remove_qh(uhci, urbp->qh); + urbp->qh = NULL; } static int uhci_unlink_urb(struct urb *urb) @@ -2373,7 +2386,7 @@ } else { if (is_ring && !killed) { urb->dev = dev; - uhci_submit_urb(urb, GFP_KERNEL); + uhci_submit_urb(urb, GFP_ATOMIC); } else { /* We decrement the usage count after we're done */ /* with everything */ diff -Nru a/drivers/usb/usb-ohci.h b/drivers/usb/usb-ohci.h --- a/drivers/usb/usb-ohci.h Wed Mar 27 16:40:06 2002 +++ b/drivers/usb/usb-ohci.h Wed Mar 27 16:40:06 2002 @@ -454,7 +454,7 @@ /* Recover a TD/ED using its collision chain */ -static inline void * +static void * dma_to_ed_td (struct hash_list_t * entry, dma_addr_t dma) { struct hash_t * scan = entry->head; @@ -465,14 +465,14 @@ return scan->virt; } -static inline struct ed * +static struct ed * dma_to_ed (struct ohci * hc, dma_addr_t ed_dma) { return (struct ed *) dma_to_ed_td(&(hc->ed_hash[ED_HASH_FUNC(ed_dma)]), ed_dma); } -static inline struct td * +static struct td * dma_to_td (struct ohci * hc, dma_addr_t td_dma) { return (struct td *) dma_to_ed_td(&(hc->td_hash[TD_HASH_FUNC(td_dma)]), @@ -480,7 +480,7 @@ } /* Add a hash entry for a TD/ED; return true on success */ -static inline int +static int hash_add_ed_td(struct hash_list_t * entry, void * virt, dma_addr_t dma) { struct hash_t * scan; @@ -502,14 +502,14 @@ return 1; } -static inline int +static int hash_add_ed (struct ohci * hc, struct ed * ed) { return hash_add_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed, ed->dma); } -static inline int +static int hash_add_td (struct ohci * hc, struct td * td) { return hash_add_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), @@ -517,7 +517,7 @@ } -static inline void +static void hash_free_ed_td (struct hash_list_t * entry, void * virt) { struct hash_t *scan, *prev; @@ -543,13 +543,13 @@ } } -static inline void +static void hash_free_ed (struct ohci * hc, struct ed * ed) { hash_free_ed_td (&(hc->ed_hash[ED_HASH_FUNC(ed->dma)]), ed); } -static inline void +static void hash_free_td (struct ohci * hc, struct td * td) { hash_free_ed_td (&(hc->td_hash[TD_HASH_FUNC(td->td_dma)]), td); @@ -588,7 +588,7 @@ } /* TDs ... */ -static inline struct td * +static struct td * td_alloc (struct ohci *hc, int mem_flags) { dma_addr_t dma; @@ -616,7 +616,7 @@ /* DEV + EDs ... only the EDs need to be consistent */ -static inline struct ohci_device * +static struct ohci_device * dev_alloc (struct ohci *hc, int mem_flags) { dma_addr_t dma; diff -Nru a/drivers/usb/usb.c b/drivers/usb/usb.c --- a/drivers/usb/usb.c Wed Mar 27 16:40:09 2002 +++ b/drivers/usb/usb.c Wed Mar 27 16:40:09 2002 @@ -791,9 +791,22 @@ // usbcore-internal ... // but usb_dec_dev_use() is #defined to this, and that's public!! +// FIXME the public call should BUG() whenever count goes to zero, +// the usbcore-internal one should do so _unless_ it does so... void usb_free_dev(struct usb_device *dev) { if (atomic_dec_and_test(&dev->refcnt)) { + /* Normally only goes to zero in usb_disconnect(), from + * khubd or from roothub shutdown (rmmod/apmd/... thread). + * Abnormally, roothub init errors can happen, so HCDs + * call this directly. + * + * Otherwise this is a nasty device driver bug, often in + * disconnect processing. + */ + if (in_interrupt ()) + BUG (); + dev->bus->op->deallocate(dev); usb_destroy_configuration(dev); diff -Nru a/include/linux/hiddev.h b/include/linux/hiddev.h --- a/include/linux/hiddev.h Wed Mar 27 16:40:07 2002 +++ b/include/linux/hiddev.h Wed Mar 27 16:40:07 2002 @@ -119,6 +119,10 @@ __s32 value; }; +/* FIELD_INDEX_NONE is returned in read() data from the kernel when flags + * is set to (HIDDEV_FLAG_UREF | HIDDEV_FLAG_REPORT) and a new report has + * been sent by the device + */ #define HID_FIELD_INDEX_NONE 0xffffffff /* @@ -139,20 +143,20 @@ #define HIDIOCGNAME(len) _IOC(_IOC_READ, 'H', 0x06, len) #define HIDIOCGREPORT _IOW('H', 0x07, struct hiddev_report_info) #define HIDIOCSREPORT _IOW('H', 0x08, struct hiddev_report_info) -#define HIDIOCGREPORTINFO _IOWR('H', 0x09, struct hiddev_report_info) -#define HIDIOCGFIELDINFO _IOWR('H', 0x0A, struct hiddev_field_info) -#define HIDIOCGUSAGE _IOWR('H', 0x0B, struct hiddev_usage_ref) -#define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref) -#define HIDIOCGUCODE _IOWR('H', 0x0D, struct hiddev_usage_ref) -#define HIDIOCGFLAG _IOR('H', 0x0E, int) -#define HIDIOCSFLAG _IOW('H', 0x0F, int) +#define HIDIOCGREPORTINFO _IOWR('H', 0x09, struct hiddev_report_info) +#define HIDIOCGFIELDINFO _IOWR('H', 0x0A, struct hiddev_field_info) +#define HIDIOCGUSAGE _IOWR('H', 0x0B, struct hiddev_usage_ref) +#define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref) +#define HIDIOCGUCODE _IOWR('H', 0x0D, struct hiddev_usage_ref) +#define HIDIOCGFLAG _IOR('H', 0x0E, int) +#define HIDIOCSFLAG _IOW('H', 0x0F, int) /* * Flags to be used in HIDIOCSFLAG */ -#define HIDDEV_FLAG_UREF 0x1 -#define HIDDEV_FLAG_REPORT 0x2 -#define HIDDEV_FLAGS 0x3 +#define HIDDEV_FLAG_UREF 0x1 +#define HIDDEV_FLAG_REPORT 0x2 +#define HIDDEV_FLAGS 0x3 /* To traverse the input report descriptor info for a HID device, perform the * following: diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h Wed Mar 27 16:40:08 2002 +++ b/include/linux/usb.h Wed Mar 27 16:40:08 2002 @@ -588,7 +588,7 @@ /** * struct urb - USB Request Block * @urb_list: For use by current owner of the URB. - * @next: Used primarily to link ISO requests into rings. + * @next: Used to link ISO requests into rings. * @pipe: Holds endpoint number, direction, type, and max packet size. * Create these values with the eight macros available; * usb_{snd,rcv}TYPEpipe(dev,endpoint), where the type is "ctrl" @@ -627,8 +627,9 @@ * @start_frame: Returns the initial frame for interrupt or isochronous * transfers. * @number_of_packets: Lists the number of ISO transfer buffers. - * @interval: Specifies the polling interval for interrupt transfers, in - * milliseconds. + * @interval: Specifies the polling interval for interrupt or isochronous + * transfers. The units are frames (milliseconds) for for full and low + * speed devices, and microframes (1/8 millisecond) for highspeed ones. * @error_count: Returns the number of ISO transfers that reported errors. * @context: For use in completion functions. This normally points to * request-specific driver context. @@ -668,12 +669,16 @@ * * Control URBs must provide a setup_packet. * - * Interupt UBS must provide an interval, saying how often (in milliseconds) + * Interrupt UBS must provide an interval, saying how often (in milliseconds + * or, for highspeed devices, 125 microsecond units) * to poll for transfers. After the URB has been submitted, the interval * and start_frame fields reflect how the transfer was actually scheduled. * The polling interval may be more frequent than requested. * For example, some controllers have a maximum interval of 32 microseconds, * while others support intervals of up to 1024 microseconds. + * Isochronous URBs also have transfer intervals. (Note that for isochronous + * endpoints, as well as high speed interrupt endpoints, the encoding of + * the transfer interval in the endpoint descriptor is logarithmic.) * * Isochronous URBs normally use the USB_ISO_ASAP transfer flag, telling * the host controller to schedule the transfer as soon as bandwidth @@ -682,8 +687,8 @@ * and handle the case where the transfer can't begin then. However, drivers * won't know how bandwidth is currently allocated, and while they can * find the current frame using usb_get_current_frame_number () they can't - * know the range for that frame number. (Common ranges for the frame - * counter include 256, 512, and 1024 frames.) + * know the range for that frame number. (Ranges for frame counter values + * are HC-specific, and can go from 256 to 65536 frames from "now".) * * Isochronous URBs have a different data transfer model, in part because * the quality of service is only "best effort". Callers provide specially @@ -734,7 +739,7 @@ unsigned char *setup_packet; /* (in) setup packet (control only) */ int start_frame; /* (modify) start frame (INT/ISO) */ int number_of_packets; /* (in) number of ISO packets */ - int interval; /* (in) polling interval (INT only) */ + int interval; /* (in) transfer interval (INT/ISO) */ int error_count; /* (return) number of ISO errors */ int timeout; /* (in) timeout, in jiffies */ void *context; /* (in) context for completion */