Commit 6c7a3c95 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge kroah.com:/home/greg/linux/BK/bleed-2.5

into kroah.com:/home/greg/linux/BK/gregkh-2.5
parents 80d316b7 3fe52eb6
...@@ -100,7 +100,8 @@ ...@@ -100,7 +100,8 @@
<chapter id="host"> <chapter id="host">
<title>USB Host-Side API Model</title> <title>USB Host-Side API Model</title>
<para>Host-side drivers for USB devices talk to the "usbcore" APIs. <para>Within the kernel,
host-side drivers for USB devices talk to the "usbcore" APIs.
There are two types of public "usbcore" APIs, targetted at two different There are two types of public "usbcore" APIs, targetted at two different
layers of USB driver. Those are layers of USB driver. Those are
<emphasis>general purpose</emphasis> drivers, exposed through <emphasis>general purpose</emphasis> drivers, exposed through
...@@ -289,6 +290,684 @@ ...@@ -289,6 +290,684 @@
!Edrivers/usb/core/buffer.c !Edrivers/usb/core/buffer.c
</chapter> </chapter>
<chapter>
<title>The USB Filesystem (usbfs)</title>
<para>This chapter presents the Linux <emphasis>usbfs</emphasis>.
You may prefer to avoid avoid writing new kernel code for your
USB driver; that's the problem that usbfs set out to solve.
User mode device drivers are usually packaged as applications
or libraries, and may use usbfs through some programming library
that wraps it. Such libraries include
<ulink url="http://libusb.sourceforge.net">libusb</ulink>
for C/C++, and
<ulink url="http://jUSB.sourceforge.net">jUSB</ulink> for Java.
</para>
<note><title>Unfinished</title>
<para>This particular documentation is incomplete,
especially with respect to the asynchronous mode.
As of kernel 2.5.66 the code and this (new) documentation
need to be cross-reviewed.
</para>
</note>
<para>Configure usbfs into Linux kernels by enabling the
<emphasis>USB filesystem</emphasis> option (CONFIG_USB_DEVICEFS),
and you get basic support for user mode USB device drivers.
Until relatively recently it was often (confusingly) called
<emphasis>usbdevfs</emphasis> although it wasn't solving what
<emphasis>devfs</emphasis> was.
Every USB device will appear in usbfs, regardless of whether or
not it has a kernel driver; but only devices with kernel drivers
show up in devfs.
</para>
<sect1>
<title>What files are in "usbfs"?</title>
<para>Conventionally mounted at
<filename>/proc/bus/usb</filename>, usbfs
features include:
<itemizedlist>
<listitem><para><filename>/proc/bus/usb/devices</filename>
... a text file
showing each of the USB devices on known to the kernel,
and their configuration descriptors.
You can also poll() this to learn about new devices.
</para></listitem>
<listitem><para><filename>/proc/bus/usb/BBB/DDD</filename>
... magic files
exposing the each device's configuration descriptors, and
supporting a series of ioctls for making device requests,
including I/O to devices. (Purely for access by programs.)
</para></listitem>
</itemizedlist>
</para>
<para> Each bus is given a number (BBB) based on when it was
enumerated; within each bus, each device is given a similar
number (DDD).
Those BBB/DDD paths are not "stable" identifiers;
expect them to change even if you always leave the devices
plugged in to the same hub port.
<emphasis>Don't even think of saving these in application
configuration files.</emphasis>
Stable identifiers are available, for user mode applications
that want to use them. HID and networking devices expose
these IDs.
</para>
</sect1>
<sect1>
<title>Mounting and Access Control</title>
<para>There are a number of mount options for usbfs, which will
be of most interest to you if you need to override the default
access control policy.
That policy is that only root may read or write device files
(<filename>/proc/bus/BBB/DDD</filename>) although anyone may read
the <filename>devices</filename>
or <filename>drivers</filename> files.
I/O requests to the device also need the CAP_SYS_RAWIO capability,
</para>
<para>The significance of that is that by default, all user mode
device drivers need super-user privileges.
You can change modes or ownership in a driver setup
when the device hotplugs, or maye just start the
driver right then, as a privileged server (or some activity
within one).
That's the most secure approach for multi-user systems,
but for single user systems ("trusted" by that user)
it's more convenient just to grant everyone all access
(using the <emphasis>devmode=0666</emphasis> option)
so the driver can start whenever it's needed.
</para>
<para>The mount options for usbfs, usable in /etc/fstab or
in command line invocations of <emphasis>mount</emphasis>, are:
<variablelist>
<varlistentry>
<term><emphasis>busgid</emphasis>=NNNNN</term>
<listitem><para>Controls the GID used for the
/proc/bus/usb/BBB
directories. (Default: 0)</para></listitem></varlistentry>
<varlistentry><term><emphasis>busmode</emphasis>=MMM</term>
<listitem><para>Controls the file mode used for the
/proc/bus/usb/BBB
directories. (Default: 0555)
</para></listitem></varlistentry>
<varlistentry><term><emphasis>busuid</emphasis>=NNNNN</term>
<listitem><para>Controls the UID used for the
/proc/bus/usb/BBB
directories. (Default: 0)</para></listitem></varlistentry>
<varlistentry><term><emphasis>devgid</emphasis>=NNNNN</term>
<listitem><para>Controls the GID used for the
/proc/bus/usb/BBB/DDD
files. (Default: 0)</para></listitem></varlistentry>
<varlistentry><term><emphasis>devmode</emphasis>=MMM</term>
<listitem><para>Controls the file mode used for the
/proc/bus/usb/BBB/DDD
files. (Default: 0644)</para></listitem></varlistentry>
<varlistentry><term><emphasis>devuid</emphasis>=NNNNN</term>
<listitem><para>Controls the UID used for the
/proc/bus/usb/BBB/DDD
files. (Default: 0)</para></listitem></varlistentry>
<varlistentry><term><emphasis>listgid</emphasis>=NNNNN</term>
<listitem><para>Controls the GID used for the
/proc/bus/usb/devices and drivers files.
(Default: 0)</para></listitem></varlistentry>
<varlistentry><term><emphasis>listmode</emphasis>=MMM</term>
<listitem><para>Controls the file mode used for the
/proc/bus/usb/devices and drivers files.
(Default: 0444)</para></listitem></varlistentry>
<varlistentry><term><emphasis>listuid</emphasis>=NNNNN</term>
<listitem><para>Controls the UID used for the
/proc/bus/usb/devices and drivers files.
(Default: 0)</para></listitem></varlistentry>
</variablelist>
</para>
<para>Note that many Linux distributions hard-wire the mount options
for usbfs in their init scripts, such as
<filename>/etc/rc.d/rc.sysinit</filename>,
rather than making it easy to set this per-system
policy in <filename>/etc/fstab</filename>.
</para>
</sect1>
<sect1>
<title>/proc/bus/usb/devices</title>
<para>This file is handy for status viewing tools in user
mode, which can scan the text format and ignore most of it.
More detailed device status (including class and vendor
status) is available from device-specific files.
For information about the current format of this file,
see the
<filename>Documentation/usb/proc_usb_info.txt</filename>
file in your Linux kernel sources.
</para>
<para>Otherwise the main use for this file from programs
is to poll() it to get notifications of usb devices
as they're plugged or unplugged.
To see what changed, you'd need to read the file and
compare "before" and "after" contents, scan the filesystem,
or see its hotplug event.
</para>
</sect1>
<sect1>
<title>/proc/bus/usb/BBB/DDD</title>
<para>Use these files in one of these basic ways:
</para>
<para><emphasis>They can be read,</emphasis>
producing first the device descriptor
(18 bytes) and then the descriptors for the current configuration.
See the USB 2.0 spec for details about those binary data formats.
You'll need to convert most multibyte values from little endian
format to your native host byte order, although a few of the
fields in the device descriptor (both of the BCD-encoded fields,
and the vendor and product IDs) will be byteswapped for you.
Note that configuration descriptors include descriptors for
interfaces, altsettings, endpoints, and maybe additional
class descriptors.
</para>
<para><emphasis>Perform USB operations</emphasis> using
<emphasis>ioctl()</emphasis> requests to make endpoint I/O
requests (synchronously or asynchronously) or manage
the device.
These requests need the CAP_SYS_RAWIO capability,
as well as filesystem access permissions.
Only one ioctl request can be made on one of these
device files at a time.
This means that if you are synchronously reading an endpoint
from one thread, you won't be able to write to a different
endpoint from another thread until the read completes.
This works for <emphasis>half duplex</emphasis> protocols,
but otherwise you'd use asynchronous i/o requests.
</para>
</sect1>
<sect1>
<title>Life Cycle of User Mode Drivers</title>
<para>Such a driver first needs to find a device file
for a device it knows how to handle.
Maybe it was told about it because a
<filename>/sbin/hotplug</filename> event handling agent
chose that driver to handle the new device.
Or maybe it's an application that scans all the
/proc/bus/usb device files, and ignores most devices.
In either case, it should <function>read()</function> all
the descriptors from the device file,
and check them against what it knows how to handle.
It might just reject everything except a particular
vendor and product ID, or need a more complex policy.
</para>
<para>Never assume there will only be one such device
on the system at a time!
If your code can't handle more than one device at
a time, at least detect when there's more than one, and
have your users choose which device to use.
</para>
<para>Once your user mode driver knows what device to use,
it interacts with it in either of two styles.
The simple style is to make only control requests; some
devices don't need more complex interactions than those.
(An example might be software using vendor-specific control
requests for some initialization or configuration tasks,
with a kernel driver for the rest.)
</para>
<para>More likely, you need a more complex style driver:
one using non-control endpoints, reading or writing data
and claiming exclusive use of an interface.
<emphasis>Bulk</emphasis> transfers are easiest to use,
but only their sibling <emphasis>interrupt</emphasis> transfers
work with low speed devices.
Both interrupt and <emphasis>isochronous</emphasis> transfers
offer service guarantees because their bandwidth is reserved.
Such "periodic" transfers are awkward to use through usbfs,
unless you're using the asynchronous calls. However, interrupt
transfers can also be used in a synchronous "one shot" style.
</para>
<para>Your user-mode driver should never need to worry
about cleaning up request state when the device is
disconnected, although it should close its open file
descriptors as soon as it starts seeing the ENODEV
errors.
</para>
</sect1>
<sect1><title>The ioctl() Requests</title>
<para>To use these ioctls, you need to include the following
headers in your userspace program:
<programlisting>#include &lt;linux/usb.h&gt;
#include &lt;linux/usbdevice_fs.h&gt;
#include &lt;asm/byteorder.h&gt;</programlisting>
The standard USB device model requests, from "Chapter 9" of
the USB 2.0 specification, are automatically included from
the <filename>&lt;linux/usb_ch9.h&gt;</filename> header.
</para>
<para>Unless noted otherwise, the ioctl requests
described here will
update the modification time on the usbfs file to which
they are applied (unless they fail).
A return of zero indicates success; otherwise, a
standard USB error code is returned. (These are
documented in
<filename>Documentation/usb/error-codes.txt</filename>
in your kernel sources.)
</para>
<para>Each of these files multiplexes access to several
I/O streams, one per endpoint.
Each device has one control endpoint (endpoint zero)
which supports a limited RPC style RPC access.
Devices are configured
by khubd (in the kernel) setting a device-wide
<emphasis>configuration</emphasis> that affects things
like power consumption and basic functionality.
The endpoints are part of USB <emphasis>interfaces</emphasis>,
which may have <emphasis>altsettings</emphasis>
affecting things like which endpoints are available.
Many devices only have a single configuration and interface,
so drivers for them will ignore configurations and altsettings.
</para>
<sect2>
<title>Management/Status Requests</title>
<para>A number of usbfs requests don't deal very directly
with device I/O.
They mostly relate to device management and status.
These are all synchronous requests.
</para>
<variablelist>
<varlistentry><term>USBDEVFS_CLAIMINTERFACE</term>
<listitem><para>This is used to force usbfs to
claim a specific interface,
which has not previously been claimed by usbfs or any other
kernel driver.
The ioctl parameter is an integer holding the number of
the interface (bInterfaceNumber from descriptor).
</para><para>
Note that if your driver doesn't claim an interface
before trying to use one of its endpoints, and no
other driver has bound to it, then the interface is
automatically claimed by usbfs.
</para><para>
This claim will be released by a RELEASEINTERFACE ioctl,
or by closing the file descriptor.
File modification time is not updated by this request.
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_CONNECTINFO</term>
<listitem><para>Says whether the device is lowspeed.
The ioctl parameter points to a structure like this:
<programlisting>struct usbdevfs_connectinfo {
unsigned int devnum;
unsigned char slow;
}; </programlisting>
File modification time is not updated by this request.
</para><para>
<emphasis>You can't tell whether a "not slow"
device is connected at high speed (480 MBit/sec)
or just full speed (12 MBit/sec).</emphasis>
You should know the devnum value already,
it's the DDD value of the device file name.
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_GETDRIVER</term>
<listitem><para>Returns the name of the kernel driver
bound to a given interface (a string). Parameter
is a pointer to this structure, which is modified:
<programlisting>struct usbdevfs_getdriver {
unsigned int interface;
char driver[USBDEVFS_MAXDRIVERNAME + 1];
};</programlisting>
File modification time is not updated by this request.
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_IOCTL</term>
<listitem><para>Passes a request from userspace through
to a kernel driver that has an ioctl entry in the
<emphasis>struct usb_driver</emphasis> it registered.
<programlisting>struct usbdevfs_ioctl {
int ifno;
int ioctl_code;
void *data;
};
/* user mode call looks like this.
* 'request' becomes the driver->ioctl() 'code' parameter.
* the size of 'param' is encoded in 'request', and that data
* is copied to or from the driver->ioctl() 'buf' parameter.
*/
static int
usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
{
struct usbdevfs_ioctl wrapper;
wrapper.ifno = ifno;
wrapper.ioctl_code = request;
wrapper.data = param;
return ioctl (fd, USBDEVFS_IOCTL, &amp;wrapper);
} </programlisting>
File modification time is not updated by this request.
</para><para>
This request lets kernel drivers talk to user mode code
through filesystem operations even when they don't create
a charactor or block special device.
It's also been used to do things like ask devices what
device special file should be used.
Two pre-defined ioctls are used
to disconnect and reconnect kernel drivers, so
that user mode code can completely manage binding
and configuration of devices.
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_RELEASEINTERFACE</term>
<listitem><para>This is used to release the claim usbfs
made on interface, either implicitly or because of a
USBDEVFS_CLAIMINTERFACE call, before the file
descriptor is closed.
The ioctl parameter is an integer holding the number of
the interface (bInterfaceNumber from descriptor);
File modification time is not updated by this request.
</para><warning><para>
<emphasis>No security check is made to ensure
that the task which made the claim is the one
which is releasing it.
This means that user mode driver may interfere
other ones. </emphasis>
</para></warning></listitem></varlistentry>
<varlistentry><term>USBDEVFS_RESETEP</term>
<listitem><para>Resets the data toggle value for an endpoint
(bulk or interrupt) to DATA0.
The ioctl parameter is an integer endpoint number
(1 to 15, as identified in the endpoint descriptor),
with USB_DIR_IN added if the device's endpoint sends
data to the host.
</para><warning><para>
<emphasis>Avoid using this request.
It should probably be removed.</emphasis>
Using it typically means the device and driver will lose
toggle synchronization. If you really lost synchronization,
you likely need to completely handshake with the device,
using a request like CLEAR_HALT
or SET_INTERFACE.
</para></warning></listitem></varlistentry>
</variablelist>
</sect2>
<sect2>
<title>Synchronous I/O Support</title>
<para>Synchronous requests involve the kernel blocking
until until the user mode request completes, either by
finishing successfully or by reporting an error.
In most cases this is the simplest way to use usbfs,
although as noted above it does prevent performing I/O
to more than one endpoint at a time.
</para>
<variablelist>
<varlistentry><term>USBDEVFS_BULK</term>
<listitem><para>Issues a bulk read or write request to the
device.
The ioctl parameter is a pointer to this structure:
<programlisting>struct usbdevfs_bulktransfer {
unsigned int ep;
unsigned int len;
unsigned int timeout; /* in milliseconds */
void *data;
};</programlisting>
</para><para>The "ep" value identifies a
bulk endpoint number (1 to 15, as identified in an endpoint
descriptor),
masked with USB_DIR_IN when referring to an endpoint which
sends data to the host from the device.
The length of the data buffer is identified by "len";
Recent kernels support requests up to about 128KBytes.
<emphasis>FIXME say how read length is returned,
and how short reads are handled.</emphasis>.
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_CLEAR_HALT</term>
<listitem><para>Clears endpoint halt (stall) and
resets the endpoint toggle. This is only
meaningful for bulk or interrupt endpoints.
The ioctl parameter is an integer endpoint number
(1 to 15, as identified in an endpoint descriptor),
masked with USB_DIR_IN when referring to an endpoint which
sends data to the host from the device.
</para><para>
Use this on bulk or interrupt endpoints which have
stalled, returning <emphasis>-EPIPE</emphasis> status
to a data transfer request.
Do not issue the control request directly, since
that could invalidate the host's record of the
data toggle.
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_CONTROL</term>
<listitem><para>Issues a control request to the device.
The ioctl parameter points to a structure like this:
<programlisting>struct usbdevfs_ctrltransfer {
__u8 bRequestType;
__u8 bRequest;
__u16 wValue;
__u16 wIndex;
__u16 wLength;
__u32 timeout; /* in milliseconds */
void *data;
};</programlisting>
</para><para>
The first eight bytes of this structure are the contents
of the SETUP packet to be sent to the device; see the
USB 2.0 specification for details.
The bRequestType value is composed by combining a
USB_TYPE_* value, a USB_DIR_* value, and a
USB_RECIP_* value (from
<emphasis>&lt;linux/usb.h&gt;</emphasis>).
If wLength is nonzero, it describes the length of the data
buffer, which is either written to the device
(USB_DIR_OUT) or read from the device (USB_DIR_IN).
</para><para>
At this writing, you can't transfer more than 4 KBytes
of data to or from a device; usbfs has a limit, and
some host controller drivers have a limit.
(That's not usually a problem.)
<emphasis>Also</emphasis> there's no way to say it's
not OK to get a short read back from the device.
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_RESET</term>
<listitem><para>Does a USB level device reset.
The ioctl parameter is ignored.
After the reset, this rebinds all device interfaces.
File modification time is not updated by this request.
</para><warning><para>
<emphasis>Avoid using this call</emphasis>
until some usbcore bugs get fixed,
since it does not fully synchronize device, interface,
and driver (not just usbfs) state.
</para></warning></listitem></varlistentry>
<varlistentry><term>USBDEVFS_SETINTERFACE</term>
<listitem><para>Sets the alternate setting for an
interface. The ioctl parameter is a pointer to a
structure like this:
<programlisting>struct usbdevfs_setinterface {
unsigned int interface;
unsigned int altsetting;
}; </programlisting>
File modification time is not updated by this request.
</para><para>
Those struct members are from some interface descriptor
applying to the the current configuration.
The interface number is the bInterfaceNumber value, and
the altsetting number is the bAlternateSetting value.
(This resets each endpoint in the interface.)
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_SETCONFIGURATION</term>
<listitem><para>Issues the
<function>usb_set_configuration</function> call
for the device.
The parameter is an integer holding the number of
a configuration (bConfigurationValue from descriptor).
File modification time is not updated by this request.
</para><warning><para>
<emphasis>Avoid using this call</emphasis>
until some usbcore bugs get fixed,
since it does not fully synchronize device, interface,
and driver (not just usbfs) state.
</para></warning></listitem></varlistentry>
</variablelist>
</sect2>
<sect2>
<title>Asynchronous I/O Support</title>
<para>As mentioned above, there are situations where it may be
important to initiate concurrent operations from user mode code.
This is particularly important for periodic transfers
(interrupt and isochronous), but it can be used for other
kinds of USB requests too.
In such cases, the asynchronous requests described here
are essential. Rather than submitting one request and having
the kernel block until it completes, the blocking is separate.
</para>
<para>These requests are packaged into a structure that
resembles the URB used by kernel device drivers.
(No POSIX Async I/O support here, sorry.)
It identifies the endpoint type (USBDEVFS_URB_TYPE_*),
endpoint (number, masked with USB_DIR_IN as appropriate),
buffer and length, and a user "context" value serving to
uniquely identify each request.
(It's usually a pointer to per-request data.)
Flags can modify requests (not as many as supported for
kernel drivers).
</para>
<para>Each request can specify a realtime signal number
(between SIGRTMIN and SIGRTMAX, inclusive) to request a
signal be sent when the request completes.
</para>
<para>When usbfs returns these urbs, the status value
is updated, and the buffer may have been modified.
Except for isochronous transfers, the actual_length is
updated to say how many bytes were transferred; if the
USBDEVFS_URB_DISABLE_SPD flag is set
("short packets are not OK"), if fewer bytes were read
than were requested then you get an error report.
</para>
<programlisting>struct usbdevfs_iso_packet_desc {
unsigned int length;
unsigned int actual_length;
unsigned int status;
};
struct usbdevfs_urb {
unsigned char type;
unsigned char endpoint;
int status;
unsigned int flags;
void *buffer;
int buffer_length;
int actual_length;
int start_frame;
int number_of_packets;
int error_count;
unsigned int signr;
void *usercontext;
struct usbdevfs_iso_packet_desc iso_frame_desc[];
};</programlisting>
<para> For these asynchronous requests, the file modification
time reflects when the request was initiated.
This contrasts with their use with the synchronous requests,
where it reflects when requests complete.
</para>
<variablelist>
<varlistentry><term>USBDEVFS_DISCARDURB</term>
<listitem><para>
<emphasis>TBS</emphasis>
File modification time is not updated by this request.
</para><para>
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_DISCSIGNAL</term>
<listitem><para>
<emphasis>TBS</emphasis>
File modification time is not updated by this request.
</para><para>
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_REAPURB</term>
<listitem><para>
<emphasis>TBS</emphasis>
File modification time is not updated by this request.
</para><para>
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_REAPURBNDELAY</term>
<listitem><para>
<emphasis>TBS</emphasis>
File modification time is not updated by this request.
</para><para>
</para></listitem></varlistentry>
<varlistentry><term>USBDEVFS_SUBMITURB</term>
<listitem><para>
<emphasis>TBS</emphasis>
</para><para>
</para></listitem></varlistentry>
</variablelist>
</sect2>
</sect1>
</chapter>
</book> </book>
<!-- vim:syntax=sgml:sw=4 <!-- vim:syntax=sgml:sw=4
--> -->
...@@ -598,7 +598,7 @@ static struct kobj_type ktype_edd = { ...@@ -598,7 +598,7 @@ static struct kobj_type ktype_edd = {
.default_attrs = def_attrs, .default_attrs = def_attrs,
}; };
static decl_subsys(edd,&ktype_edd); static decl_subsys(edd,&ktype_edd,NULL);
/** /**
......
...@@ -676,7 +676,7 @@ acpi_bus_init (void) ...@@ -676,7 +676,7 @@ acpi_bus_init (void)
return_VALUE(-ENODEV); return_VALUE(-ENODEV);
} }
decl_subsys(acpi,NULL); decl_subsys(acpi,NULL,NULL);
static int __init acpi_init (void) static int __init acpi_init (void)
{ {
......
...@@ -18,13 +18,8 @@ extern void interface_remove_dev(struct device *); ...@@ -18,13 +18,8 @@ extern void interface_remove_dev(struct device *);
#ifdef CONFIG_HOTPLUG #ifdef CONFIG_HOTPLUG
extern int dev_hotplug(struct device *dev, const char *action);
extern int class_hotplug(struct device *dev, const char *action); extern int class_hotplug(struct device *dev, const char *action);
#else #else
static inline int dev_hotplug(struct device *dev, const char *action)
{
return 0;
}
static inline int class_hotplug(struct device *dev, const char *action) static inline int class_hotplug(struct device *dev, const char *action)
{ {
return 0; return 0;
......
...@@ -132,7 +132,7 @@ static struct kobj_type ktype_bus = { ...@@ -132,7 +132,7 @@ static struct kobj_type ktype_bus = {
}; };
decl_subsys(bus,&ktype_bus); decl_subsys(bus,&ktype_bus,NULL);
/** /**
* bus_for_each_dev - device iterator. * bus_for_each_dev - device iterator.
......
...@@ -49,7 +49,9 @@ static struct kobj_type ktype_devclass = { ...@@ -49,7 +49,9 @@ static struct kobj_type ktype_devclass = {
.sysfs_ops = &class_sysfs_ops, .sysfs_ops = &class_sysfs_ops,
}; };
static decl_subsys(class,&ktype_devclass); /* Classes can't use the kobject hotplug logic, as
* they do not add new kobjects to the system */
static decl_subsys(class,&ktype_devclass,NULL);
static int devclass_dev_link(struct device_class * cls, struct device * dev) static int devclass_dev_link(struct device_class * cls, struct device * dev)
......
...@@ -23,13 +23,12 @@ int (*platform_notify_remove)(struct device * dev) = NULL; ...@@ -23,13 +23,12 @@ int (*platform_notify_remove)(struct device * dev) = NULL;
DECLARE_MUTEX(device_sem); DECLARE_MUTEX(device_sem);
#define to_dev(obj) container_of(obj,struct device,kobj)
/* /*
* sysfs bindings for devices. * sysfs bindings for devices.
*/ */
#define to_dev(obj) container_of(obj,struct device,kobj)
#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr) #define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr)
extern struct attribute * dev_default_attrs[]; extern struct attribute * dev_default_attrs[];
...@@ -86,11 +85,55 @@ static struct kobj_type ktype_device = { ...@@ -86,11 +85,55 @@ static struct kobj_type ktype_device = {
.default_attrs = dev_default_attrs, .default_attrs = dev_default_attrs,
}; };
static int dev_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
struct kobj_type *ktype = get_ktype(kobj);
if (ktype == &ktype_device) {
struct device *dev = to_dev(kobj);
if (dev->bus)
return 1;
}
return 0;
}
static char *dev_hotplug_name(struct kset *kset, struct kobject *kobj)
{
struct device *dev = to_dev(kobj);
return dev->bus->name;
}
static int dev_hotplug(struct kset *kset, struct kobject *kobj, char **envp,
int num_envp, char *buffer, int buffer_size)
{
struct device *dev = to_dev(kobj);
int retval = 0;
if (dev->bus->hotplug) {
/* have the bus specific function add its stuff */
retval = dev->bus->hotplug (dev, envp, num_envp, buffer, buffer_size);
if (retval) {
pr_debug ("%s - hotplug() returned %d\n",
__FUNCTION__, retval);
}
}
return retval;
}
static struct kset_hotplug_ops device_hotplug_ops = {
.filter = dev_hotplug_filter,
.name = dev_hotplug_name,
.hotplug = dev_hotplug,
};
/** /**
* device_subsys - structure to be registered with kobject core. * device_subsys - structure to be registered with kobject core.
*/ */
decl_subsys(devices,&ktype_device); decl_subsys(devices, &ktype_device, &device_hotplug_ops);
/** /**
...@@ -192,9 +235,6 @@ int device_add(struct device *dev) ...@@ -192,9 +235,6 @@ int device_add(struct device *dev)
if (platform_notify) if (platform_notify)
platform_notify(dev); platform_notify(dev);
/* notify userspace of device entry */
dev_hotplug(dev, "add");
devclass_add_device(dev); devclass_add_device(dev);
register_done: register_done:
if (error && parent) if (error && parent)
...@@ -278,9 +318,6 @@ void device_del(struct device * dev) ...@@ -278,9 +318,6 @@ void device_del(struct device * dev)
if (platform_notify_remove) if (platform_notify_remove)
platform_notify_remove(dev); platform_notify_remove(dev);
/* notify userspace that this device is about to disappear */
dev_hotplug (dev, "remove");
bus_remove_device(dev); bus_remove_device(dev);
kobject_del(&dev->kobj); kobject_del(&dev->kobj);
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
static decl_subsys(firmware,NULL); static decl_subsys(firmware,NULL,NULL);
int firmware_register(struct subsystem * s) int firmware_register(struct subsystem * s)
{ {
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* drivers/base/hotplug.c - hotplug call code * drivers/base/hotplug.c - hotplug call code
* *
* Copyright (c) 2000-2001 David Brownell * Copyright (c) 2000-2001 David Brownell
* Copyright (c) 2002 Greg Kroah-Hartman * Copyright (c) 2002-2003 Greg Kroah-Hartman
* Copyright (c) 2002 IBM Corp. * Copyright (c) 2002-2003 IBM Corp.
* *
* Based off of drivers/usb/core/usb.c:call_agent(), which was * Based off of drivers/usb/core/usb.c:call_agent(), which was
* written by David Brownell. * written by David Brownell.
...@@ -53,17 +53,6 @@ static int do_hotplug (struct device *dev, char *argv1, const char *action, ...@@ -53,17 +53,6 @@ static int do_hotplug (struct device *dev, char *argv1, const char *action,
if (!hotplug_path [0]) if (!hotplug_path [0])
return -ENODEV; return -ENODEV;
if (in_interrupt ()) {
pr_debug ("%s - in_interrupt, not allowed!", __FUNCTION__);
return -EIO;
}
if (!current->fs->root) {
/* don't try to do anything unless we have a root partition */
pr_debug ("%s - %s -- no FS yet\n", __FUNCTION__, action);
return -EIO;
}
envp = (char **) kmalloc (NUM_ENVP * sizeof (char *), GFP_KERNEL); envp = (char **) kmalloc (NUM_ENVP * sizeof (char *), GFP_KERNEL);
if (!envp) if (!envp)
return -ENOMEM; return -ENOMEM;
...@@ -128,23 +117,6 @@ static int do_hotplug (struct device *dev, char *argv1, const char *action, ...@@ -128,23 +117,6 @@ static int do_hotplug (struct device *dev, char *argv1, const char *action,
return retval; return retval;
} }
/*
* dev_hotplug - called when any device is added or removed from a bus
*/
int dev_hotplug (struct device *dev, const char *action)
{
pr_debug ("%s\n", __FUNCTION__);
if (!dev)
return -ENODEV;
if (!dev->bus)
return -ENODEV;
return do_hotplug (dev, dev->bus->name, action, dev->bus->hotplug);
}
/* /*
* class_hotplug - called when a class is added or removed from a device * class_hotplug - called when a class is added or removed from a device
*/ */
......
...@@ -525,9 +525,21 @@ static struct kobj_type ktype_block = { ...@@ -525,9 +525,21 @@ static struct kobj_type ktype_block = {
.default_attrs = default_attrs, .default_attrs = default_attrs,
}; };
extern struct kobj_type ktype_part;
static int block_hotplug_filter(struct kset *kset, struct kobject *kobj)
{
struct kobj_type *ktype = get_ktype(kobj);
return ((ktype == &ktype_block) || (ktype == &ktype_part));
}
static struct kset_hotplug_ops block_hotplug_ops = {
.filter = block_hotplug_filter,
};
/* declare block_subsys. */ /* declare block_subsys. */
static decl_subsys(block,&ktype_block); static decl_subsys(block, &ktype_block, &block_hotplug_ops);
struct gendisk *alloc_disk(int minors) struct gendisk *alloc_disk(int minors)
......
...@@ -100,7 +100,7 @@ static struct kobj_type hotplug_slot_ktype = { ...@@ -100,7 +100,7 @@ static struct kobj_type hotplug_slot_ktype = {
.sysfs_ops = &hotplug_slot_sysfs_ops .sysfs_ops = &hotplug_slot_sysfs_ops
}; };
static decl_subsys(hotplug_slots, &hotplug_slot_ktype); static decl_subsys(hotplug_slots, &hotplug_slot_ktype, NULL);
/* these strings match up with the values in pci_bus_speed */ /* these strings match up with the values in pci_bus_speed */
......
...@@ -1806,22 +1806,6 @@ static int detect_yamaha_device( struct usb_device *d, unsigned int ifnum, struc ...@@ -1806,22 +1806,6 @@ static int detect_yamaha_device( struct usb_device *d, unsigned int ifnum, struc
printk(KERN_INFO "usb-midi: Found YAMAHA USB-MIDI device on dev %04x:%04x, iface %d\n", printk(KERN_INFO "usb-midi: Found YAMAHA USB-MIDI device on dev %04x:%04x, iface %d\n",
d->descriptor.idVendor, d->descriptor.idProduct, ifnum); d->descriptor.idVendor, d->descriptor.idProduct, ifnum);
for ( i=0 ; i < d->descriptor.bNumConfigurations ; i++ ) {
if ( d->config+i == c ) goto configfound;
}
printk(KERN_INFO "usb-midi: Config not found.\n");
return -EINVAL;
configfound:
/* this may not be necessary. */
if ( usb_set_configuration( d, c->desc.bConfigurationValue ) < 0 ) {
printk(KERN_INFO "usb-midi: Could not set config.\n");
return -EINVAL;
}
ret = usb_get_descriptor( d, USB_DT_CONFIG, i, buf, USB_DT_CONFIG_SIZE ); ret = usb_get_descriptor( d, USB_DT_CONFIG, i, buf, USB_DT_CONFIG_SIZE );
if ( ret < 0 ) { if ( ret < 0 ) {
printk(KERN_INFO "usb-midi: Could not get config (error=%d).\n", ret); printk(KERN_INFO "usb-midi: Could not get config (error=%d).\n", ret);
...@@ -1916,21 +1900,6 @@ static int detect_midi_subclass(struct usb_device *d, unsigned int ifnum, struct ...@@ -1916,21 +1900,6 @@ static int detect_midi_subclass(struct usb_device *d, unsigned int ifnum, struct
printk(KERN_INFO "usb-midi: Found MIDISTREAMING on dev %04x:%04x, iface %d\n", printk(KERN_INFO "usb-midi: Found MIDISTREAMING on dev %04x:%04x, iface %d\n",
d->descriptor.idVendor, d->descriptor.idProduct, ifnum); d->descriptor.idVendor, d->descriptor.idProduct, ifnum);
for ( i=0 ; i < d->descriptor.bNumConfigurations ; i++ ) {
if ( d->config+i == c ) goto configfound;
}
printk(KERN_INFO "usb-midi: Config not found.\n");
return -EINVAL;
configfound:
/* this may not be necessary. */
if ( usb_set_configuration( d, c->desc.bConfigurationValue ) < 0 ) {
printk(KERN_INFO "usb-midi: Could not set config.\n");
return -EINVAL;
}
/* From USB Spec v2.0, Section 9.5. /* From USB Spec v2.0, Section 9.5.
If the class or vendor specific descriptors use the same format If the class or vendor specific descriptors use the same format
......
...@@ -1175,8 +1175,10 @@ void usb_hub_cleanup(void) ...@@ -1175,8 +1175,10 @@ void usb_hub_cleanup(void)
* *
* Take a look at proc_resetdevice in devio.c for some sample code to * Take a look at proc_resetdevice in devio.c for some sample code to
* do this. * do this.
* Use this only from within your probe function, otherwise use
* usb_reset_device() below, which ensure proper locking
*/ */
int usb_reset_device(struct usb_device *dev) int usb_physical_reset_device(struct usb_device *dev)
{ {
struct usb_device *parent = dev->parent; struct usb_device *parent = dev->parent;
struct usb_device_descriptor *descriptor; struct usb_device_descriptor *descriptor;
...@@ -1306,3 +1308,16 @@ int usb_reset_device(struct usb_device *dev) ...@@ -1306,3 +1308,16 @@ int usb_reset_device(struct usb_device *dev)
return 0; return 0;
} }
int usb_reset_device(struct usb_device *udev)
{
struct device *gdev = &udev->dev;
int r;
down_read(&gdev->bus->subsys.rwsem);
r = usb_physical_reset_device(udev);
up_read(&gdev->bus->subsys.rwsem);
return r;
}
...@@ -930,6 +930,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -930,6 +930,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
/* reset more hc/hcd endpoint state */ /* reset more hc/hcd endpoint state */
dev->toggle[0] = 0; dev->toggle[0] = 0;
dev->toggle[1] = 0; dev->toggle[1] = 0;
dev->halted[0] = 0;
dev->halted[1] = 0;
usb_set_maxpacket(dev); usb_set_maxpacket(dev);
return 0; return 0;
......
...@@ -89,11 +89,6 @@ int usb_device_probe(struct device *dev) ...@@ -89,11 +89,6 @@ int usb_device_probe(struct device *dev)
if (!driver->probe) if (!driver->probe)
return error; return error;
if (!try_module_get(driver->owner)) {
dev_err (dev, "Can't get a module reference for %s\n", driver->name);
return error;
}
id = usb_match_id (intf, driver->id_table); id = usb_match_id (intf, driver->id_table);
if (id) { if (id) {
dev_dbg (dev, "%s - got id\n", __FUNCTION__); dev_dbg (dev, "%s - got id\n", __FUNCTION__);
...@@ -104,8 +99,6 @@ int usb_device_probe(struct device *dev) ...@@ -104,8 +99,6 @@ int usb_device_probe(struct device *dev)
if (!error) if (!error)
intf->driver = driver; intf->driver = driver;
module_put(driver->owner);
return error; return error;
} }
...@@ -117,22 +110,6 @@ int usb_device_remove(struct device *dev) ...@@ -117,22 +110,6 @@ int usb_device_remove(struct device *dev)
intf = list_entry(dev,struct usb_interface,dev); intf = list_entry(dev,struct usb_interface,dev);
driver = to_usb_driver(dev->driver); driver = to_usb_driver(dev->driver);
if (!driver) {
dev_err(dev, "%s does not have a valid driver to work with!",
__FUNCTION__);
return -ENODEV;
}
if (!try_module_get(driver->owner)) {
// FIXME this happens even when we just rmmod
// drivers that aren't in active use...
dev_err(dev, "Dieing driver still bound to device.\n");
return -EIO;
}
/* if we sleep here on an umanaged driver
* the holder of the lock guards against
* module unload */
down(&driver->serialize); down(&driver->serialize);
if (intf->driver && intf->driver->disconnect) if (intf->driver && intf->driver->disconnect)
...@@ -143,7 +120,6 @@ int usb_device_remove(struct device *dev) ...@@ -143,7 +120,6 @@ int usb_device_remove(struct device *dev)
usb_driver_release_interface(driver, intf); usb_driver_release_interface(driver, intf);
up(&driver->serialize); up(&driver->serialize);
module_put(driver->owner);
return 0; return 0;
} }
...@@ -498,9 +474,6 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, kdev_t kdev) ...@@ -498,9 +474,6 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, kdev_t kdev)
continue; continue;
intf = to_usb_interface(dev); intf = to_usb_interface(dev);
if (!intf)
continue;
if (kdev_same(intf->kdev,kdev)) { if (kdev_same(intf->kdev,kdev)) {
return intf; return intf;
} }
...@@ -566,12 +539,7 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp, ...@@ -566,12 +539,7 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp,
return 0; return 0;
intf = to_usb_interface(dev); intf = to_usb_interface(dev);
if (!intf)
return -ENODEV;
usb_dev = interface_to_usbdev (intf); usb_dev = interface_to_usbdev (intf);
if (!usb_dev)
return -ENODEV;
if (usb_dev->devnum < 0) { if (usb_dev->devnum < 0) {
dbg ("device already deleted ??"); dbg ("device already deleted ??");
...@@ -748,8 +716,6 @@ static void usb_release_dev(struct device *dev) ...@@ -748,8 +716,6 @@ static void usb_release_dev(struct device *dev)
struct usb_device *udev; struct usb_device *udev;
udev = to_usb_device(dev); udev = to_usb_device(dev);
if (!udev)
return;
if (udev->bus && udev->bus->op && udev->bus->op->deallocate) if (udev->bus && udev->bus->op && udev->bus->op->deallocate)
udev->bus->op->deallocate(udev); udev->bus->op->deallocate(udev);
......
...@@ -62,6 +62,7 @@ static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma) ...@@ -62,6 +62,7 @@ static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
{ {
memset (qtd, 0, sizeof *qtd); memset (qtd, 0, sizeof *qtd);
qtd->qtd_dma = dma; qtd->qtd_dma = dma;
qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
qtd->hw_next = EHCI_LIST_END; qtd->hw_next = EHCI_LIST_END;
qtd->hw_alt_next = EHCI_LIST_END; qtd->hw_alt_next = EHCI_LIST_END;
INIT_LIST_HEAD (&qtd->qtd_list); INIT_LIST_HEAD (&qtd->qtd_list);
......
...@@ -137,7 +137,10 @@ static void qtd_copy_status ( ...@@ -137,7 +137,10 @@ static void qtd_copy_status (
if (QTD_CERR (token)) if (QTD_CERR (token))
urb->status = -EPIPE; urb->status = -EPIPE;
else { else {
dbg ("3strikes"); ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n",
urb->dev->devpath,
usb_pipeendpoint (urb->pipe),
usb_pipein (urb->pipe) ? "in" : "out");
urb->status = -EPROTO; urb->status = -EPROTO;
} }
/* CERR nonzero + no errors + halt --> stall */ /* CERR nonzero + no errors + halt --> stall */
...@@ -213,7 +216,6 @@ ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs) ...@@ -213,7 +216,6 @@ ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
/* complete() can reenter this HCD */ /* complete() can reenter this HCD */
spin_unlock (&ehci->lock); spin_unlock (&ehci->lock);
usb_hcd_giveback_urb (&ehci->hcd, urb, regs); usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
spin_lock (&ehci->lock); spin_lock (&ehci->lock);
} }
...@@ -827,7 +829,7 @@ static struct ehci_qh *qh_append_tds ( ...@@ -827,7 +829,7 @@ static struct ehci_qh *qh_append_tds (
* HC is allowed to fetch the old dummy (4.10.2). * HC is allowed to fetch the old dummy (4.10.2).
*/ */
token = qtd->hw_token; token = qtd->hw_token;
qtd->hw_token = 0; qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
wmb (); wmb ();
dummy = qh->dummy; dummy = qh->dummy;
...@@ -879,8 +881,7 @@ submit_async ( ...@@ -879,8 +881,7 @@ submit_async (
if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe)) if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe))
epnum |= 0x10; epnum |= 0x10;
vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", ehci_vdbg (ehci, "submit_async urb %p len %d ep%d%s qtd %p [qh %p]\n",
hcd_to_bus (&ehci->hcd)->bus_name,
urb, urb->transfer_buffer_length, urb, urb->transfer_buffer_length,
epnum & 0x0f, (epnum & 0x10) ? "in" : "out", epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
qtd, dev ? dev->ep [epnum] : (void *)~0); qtd, dev ? dev->ep [epnum] : (void *)~0);
...@@ -916,7 +917,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -916,7 +917,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
del_timer (&ehci->watchdog); del_timer (&ehci->watchdog);
qh->hw_next = cpu_to_le32 (qh->qh_dma); // qh->hw_next = cpu_to_le32 (qh->qh_dma);
qh->qh_state = QH_STATE_IDLE; qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = 0; qh->qh_next.qh = 0;
qh_put (ehci, qh); // refcount from reclaim qh_put (ehci, qh); // refcount from reclaim
......
...@@ -429,6 +429,8 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -429,6 +429,8 @@ static int hc_reset (struct ohci_hcd *ohci)
ohci->hc_control = readl (&ohci->regs->control); ohci->hc_control = readl (&ohci->regs->control);
ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
// flush those pci writes
(void) readl (&ohci->regs->control);
wait_ms (50); wait_ms (50);
/* HC Reset requires max 10 us delay */ /* HC Reset requires max 10 us delay */
...@@ -450,6 +452,8 @@ static int hc_reset (struct ohci_hcd *ohci) ...@@ -450,6 +452,8 @@ static int hc_reset (struct ohci_hcd *ohci)
* this if we write fmInterval after we're OPERATIONAL. * this if we write fmInterval after we're OPERATIONAL.
*/ */
writel (ohci->hc_control, &ohci->regs->control); writel (ohci->hc_control, &ohci->regs->control);
// flush those pci writes
(void) readl (&ohci->regs->control);
return 0; return 0;
} }
...@@ -524,6 +528,8 @@ static int hc_start (struct ohci_hcd *ohci) ...@@ -524,6 +528,8 @@ static int hc_start (struct ohci_hcd *ohci)
writel (tmp, &ohci->regs->roothub.a); writel (tmp, &ohci->regs->roothub.a);
writel (RH_HS_LPSC, &ohci->regs->roothub.status); writel (RH_HS_LPSC, &ohci->regs->roothub.status);
writel (0, &ohci->regs->roothub.b); writel (0, &ohci->regs->roothub.b);
// flush those pci writes
(void) readl (&ohci->regs->control);
// POTPGT delay is bits 24-31, in 2 ms units. // POTPGT delay is bits 24-31, in 2 ms units.
mdelay ((roothub_a (ohci) >> 23) & 0x1fe); mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
...@@ -610,6 +616,8 @@ static void ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) ...@@ -610,6 +616,8 @@ static void ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
writel (ints, &regs->intrstatus); writel (ints, &regs->intrstatus);
writel (OHCI_INTR_MIE, &regs->intrenable); writel (OHCI_INTR_MIE, &regs->intrenable);
// flush those pci writes
(void) readl (&ohci->regs->control);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
......
...@@ -446,6 +446,8 @@ static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed) ...@@ -446,6 +446,8 @@ static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
if (!ohci->sleeping) { if (!ohci->sleeping) {
writel (OHCI_INTR_SF, &ohci->regs->intrstatus); writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
writel (OHCI_INTR_SF, &ohci->regs->intrenable); writel (OHCI_INTR_SF, &ohci->regs->intrenable);
// flush those pci writes
(void) readl (&ohci->regs->control);
} }
} }
......
...@@ -1664,6 +1664,9 @@ static struct usb_driver hid_driver = { ...@@ -1664,6 +1664,9 @@ static struct usb_driver hid_driver = {
.probe = hid_probe, .probe = hid_probe,
.disconnect = hid_disconnect, .disconnect = hid_disconnect,
.id_table = hid_usb_ids, .id_table = hid_usb_ids,
.driver = {
.devclass = &input_devclass,
},
}; };
static int __init hid_init(void) static int __init hid_init(void)
......
...@@ -91,7 +91,7 @@ static void kbtab_irq(struct urb *urb, struct pt_regs *regs) ...@@ -91,7 +91,7 @@ static void kbtab_irq(struct urb *urb, struct pt_regs *regs)
} }
struct usb_device_id kbtab_ids[] = { struct usb_device_id kbtab_ids[] = {
{ USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), driver_info : 0 }, { USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 },
{ } { }
}; };
......
...@@ -359,6 +359,9 @@ static struct usb_driver usb_kbd_driver = { ...@@ -359,6 +359,9 @@ static struct usb_driver usb_kbd_driver = {
.probe = usb_kbd_probe, .probe = usb_kbd_probe,
.disconnect = usb_kbd_disconnect, .disconnect = usb_kbd_disconnect,
.id_table = usb_kbd_id_table, .id_table = usb_kbd_id_table,
.driver = {
.devclass = &input_devclass,
},
}; };
static int __init usb_kbd_init(void) static int __init usb_kbd_init(void)
......
...@@ -242,6 +242,9 @@ static struct usb_driver usb_mouse_driver = { ...@@ -242,6 +242,9 @@ static struct usb_driver usb_mouse_driver = {
.probe = usb_mouse_probe, .probe = usb_mouse_probe,
.disconnect = usb_mouse_disconnect, .disconnect = usb_mouse_disconnect,
.id_table = usb_mouse_id_table, .id_table = usb_mouse_id_table,
.driver = {
.devclass = &input_devclass,
},
}; };
static int __init usb_mouse_init(void) static int __init usb_mouse_init(void)
......
...@@ -1034,6 +1034,23 @@ static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void *arg) ...@@ -1034,6 +1034,23 @@ static int udsl_atm_ioctl (struct atm_dev *dev, unsigned int cmd, void *arg)
** USB ** ** USB **
**********/ **********/
static int udsl_set_alternate (struct udsl_instance_data *instance)
{
down (&instance->serialize); /* vs self */
if (!instance->firmware_loaded) {
int ret;
if ((ret = usb_set_interface (instance->usb_dev, 1, 1)) < 0) {
up (&instance->serialize);
return ret;
}
instance->firmware_loaded = 1;
}
up (&instance->serialize);
udsl_fire_receivers (instance);
return 0;
}
static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data) static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data)
{ {
struct udsl_instance_data *instance = usb_get_intfdata (intf); struct udsl_instance_data *instance = usb_get_intfdata (intf);
...@@ -1048,14 +1065,7 @@ static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void * ...@@ -1048,14 +1065,7 @@ static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *
switch (code) { switch (code) {
case UDSL_IOCTL_START: case UDSL_IOCTL_START:
instance->atm_dev->signal = ATM_PHY_SIG_FOUND; instance->atm_dev->signal = ATM_PHY_SIG_FOUND;
down (&instance->serialize); /* vs self */ return udsl_set_alternate (instance);
if (!instance->firmware_loaded) {
usb_set_interface (instance->usb_dev, 1, 1);
instance->firmware_loaded = 1;
}
up (&instance->serialize);
udsl_fire_receivers (instance);
return 0;
case UDSL_IOCTL_STOP: case UDSL_IOCTL_STOP:
instance->atm_dev->signal = ATM_PHY_SIG_LOST; instance->atm_dev->signal = ATM_PHY_SIG_LOST;
return 0; return 0;
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
/* /*
* Version Information * Version Information
*/ */
#define DRIVER_VERSION "v0.5.9 (2002/12/31)" #define DRIVER_VERSION "v0.5.10 (2003/04/01)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
...@@ -121,7 +121,7 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size, ...@@ -121,7 +121,7 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
char *buffer; char *buffer;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
buffer = kmalloc(size, GFP_KERNEL); buffer = kmalloc(size, GFP_DMA);
if (!buffer) { if (!buffer) {
warn("%s: looks like we're out of memory", __FUNCTION__); warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
...@@ -170,7 +170,7 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size, ...@@ -170,7 +170,7 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
char *buffer; char *buffer;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
buffer = kmalloc(size, GFP_KERNEL); buffer = kmalloc(size, GFP_DMA);
if (!buffer) { if (!buffer) {
warn("%s: looks like we're out of memory", __FUNCTION__); warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
...@@ -218,7 +218,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data) ...@@ -218,7 +218,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
char *tmp; char *tmp;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
tmp = kmalloc(1, GFP_KERNEL); tmp = kmalloc(1, GFP_DMA);
if (!tmp) { if (!tmp) {
warn("%s: looks like we're out of memory", __FUNCTION__); warn("%s: looks like we're out of memory", __FUNCTION__);
return -ENOMEM; return -ENOMEM;
...@@ -233,7 +233,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data) ...@@ -233,7 +233,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
pegasus->dr.bRequest = PEGASUS_REQ_SET_REG; pegasus->dr.bRequest = PEGASUS_REQ_SET_REG;
pegasus->dr.wValue = cpu_to_le16p(&data); pegasus->dr.wValue = cpu_to_le16(data);
pegasus->dr.wIndex = cpu_to_le16p(&indx); pegasus->dr.wIndex = cpu_to_le16p(&indx);
pegasus->dr.wLength = cpu_to_le16(1); pegasus->dr.wLength = cpu_to_le16(1);
pegasus->ctrl_urb->transfer_buffer_length = 1; pegasus->ctrl_urb->transfer_buffer_length = 1;
...@@ -711,11 +711,11 @@ static void intr_callback(struct urb *urb, struct pt_regs *regs) ...@@ -711,11 +711,11 @@ static void intr_callback(struct urb *urb, struct pt_regs *regs)
pegasus->stats.tx_aborted_errors++; pegasus->stats.tx_aborted_errors++;
if (d[0] & LATE_COL) if (d[0] & LATE_COL)
pegasus->stats.tx_window_errors++; pegasus->stats.tx_window_errors++;
if (d[0] & (NO_CARRIER | LOSS_CARRIER)) { if (d[5] & LINK_STATUS) {
netif_carrier_on(net);
} else {
pegasus->stats.tx_carrier_errors++; pegasus->stats.tx_carrier_errors++;
netif_carrier_off(net); netif_carrier_off(net);
} else {
netif_carrier_on(net);
} }
} }
......
...@@ -52,6 +52,8 @@ ...@@ -52,6 +52,8 @@
#define LOSS_CARRIER 0x08 #define LOSS_CARRIER 0x08
#define JABBER_TIMEOUT 0x04 #define JABBER_TIMEOUT 0x04
#define LINK_STATUS 0x01
#define PEGASUS_REQT_READ 0xc0 #define PEGASUS_REQT_READ 0xc0
#define PEGASUS_REQT_WRITE 0x40 #define PEGASUS_REQT_WRITE 0x40
#define PEGASUS_REQ_GET_REGS 0xf0 #define PEGASUS_REQ_GET_REGS 0xf0
......
...@@ -122,6 +122,11 @@ ...@@ -122,6 +122,11 @@
* cleanups and stubbed PXA-250 support (db), fix for framing * cleanups and stubbed PXA-250 support (db), fix for framing
* issues on Z, net1080, and gl620a (Toby Milne) * issues on Z, net1080, and gl620a (Toby Milne)
* *
* 31-mar-2003 Use endpoint descriptors: high speed support, simpler sa1100
* vs pxa25x, and CDC Ethernet. Throttle down log floods on
* disconnect; other cleanups. (db) Flush net1080 fifos
* after several sequential framing errors. (Johannes Erdfelt)
*
*-------------------------------------------------------------------------*/ *-------------------------------------------------------------------------*/
#include <linux/config.h> #include <linux/config.h>
...@@ -155,16 +160,17 @@ ...@@ -155,16 +160,17 @@
/* minidrivers _could_ be individually configured */ /* minidrivers _could_ be individually configured */
#define CONFIG_USB_AN2720 #define CONFIG_USB_AN2720
#define CONFIG_USB_BELKIN #define CONFIG_USB_BELKIN
#undef CONFIG_USB_CDCETHER
//#define CONFIG_USB_CDCETHER /* NYET */
#define CONFIG_USB_EPSON2888 #define CONFIG_USB_EPSON2888
#define CONFIG_USB_GENESYS #define CONFIG_USB_GENESYS
#define CONFIG_USB_NET1080 #define CONFIG_USB_NET1080
#define CONFIG_USB_PL2301 #define CONFIG_USB_PL2301
// #define CONFIG_USB_PXA #define CONFIG_USB_ARMLINUX
#define CONFIG_USB_SA1100
#define CONFIG_USB_ZAURUS #define CONFIG_USB_ZAURUS
#define DRIVER_VERSION "18-Oct-2002" #define DRIVER_VERSION "31-Mar-2003"
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -176,11 +182,11 @@ ...@@ -176,11 +182,11 @@
* Ethernet packets (so queues should be bigger). * Ethernet packets (so queues should be bigger).
*/ */
#ifdef REALLY_QUEUE #ifdef REALLY_QUEUE
#define RX_QLEN 4 #define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
#define TX_QLEN 4 #define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
#else #else
#define RX_QLEN 1 #define RX_QLEN(dev) 1
#define TX_QLEN 1 #define TX_QLEN(dev) 1
#endif #endif
// packets are always ethernet inside // packets are always ethernet inside
...@@ -191,6 +197,10 @@ ...@@ -191,6 +197,10 @@
// reawaken network queue this soon after stopping; else watchdog barks // reawaken network queue this soon after stopping; else watchdog barks
#define TX_TIMEOUT_JIFFIES (5*HZ) #define TX_TIMEOUT_JIFFIES (5*HZ)
// throttle rx/tx briefly after some faults, so khubd might disconnect()
// us (it polls at HZ/4 usually) before we report too many false errors.
#define THROTTLE_JIFFIES (HZ/8)
// for vendor-specific control operations // for vendor-specific control operations
#define CONTROL_TIMEOUT_MS (500) /* msec */ #define CONTROL_TIMEOUT_MS (500) /* msec */
#define CONTROL_TIMEOUT_JIFFIES ((CONTROL_TIMEOUT_MS * HZ)/1000) #define CONTROL_TIMEOUT_JIFFIES ((CONTROL_TIMEOUT_MS * HZ)/1000)
...@@ -200,10 +210,6 @@ ...@@ -200,10 +210,6 @@
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
// list of all devices we manage
static DECLARE_MUTEX (usbnet_mutex);
static LIST_HEAD (usbnet_list);
// randomly generated ethernet address // randomly generated ethernet address
static u8 node_id [ETH_ALEN]; static u8 node_id [ETH_ALEN];
...@@ -213,17 +219,18 @@ struct usbnet { ...@@ -213,17 +219,18 @@ struct usbnet {
struct usb_device *udev; struct usb_device *udev;
struct driver_info *driver_info; struct driver_info *driver_info;
struct semaphore mutex; struct semaphore mutex;
struct list_head dev_list;
wait_queue_head_t *wait; wait_queue_head_t *wait;
// i/o info: pipes etc
unsigned in, out;
unsigned maxpacket;
struct timer_list delay;
// protocol/interface state // protocol/interface state
struct net_device net; struct net_device net;
struct net_device_stats stats; struct net_device_stats stats;
int msg_level; int msg_level;
unsigned long data [5];
#ifdef CONFIG_USB_NET1080
u16 packet_id;
#endif
// various kinds of pending driver work // various kinds of pending driver work
struct sk_buff_head rxq; struct sk_buff_head rxq;
...@@ -243,11 +250,19 @@ struct driver_info { ...@@ -243,11 +250,19 @@ struct driver_info {
char *description; char *description;
int flags; int flags;
/* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */
#define FLAG_FRAMING_NC 0x0001 /* guard against device dropouts */ #define FLAG_FRAMING_NC 0x0001 /* guard against device dropouts */
#define FLAG_FRAMING_GL 0x0002 /* genelink batches packets */ #define FLAG_FRAMING_GL 0x0002 /* genelink batches packets */
#define FLAG_FRAMING_Z 0x0004 /* zaurus adds a trailer */ #define FLAG_FRAMING_Z 0x0004 /* zaurus adds a trailer */
#define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */ #define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */
/* init device ... can sleep, or cause probe() failure */
int (*bind)(struct usbnet *, struct usb_interface *);
/* cleanup device ... can sleep, but can't fail */
void (*unbind)(struct usbnet *, struct usb_interface *);
/* reset device ... can sleep */ /* reset device ... can sleep */
int (*reset)(struct usbnet *); int (*reset)(struct usbnet *);
...@@ -263,15 +278,13 @@ struct driver_info { ...@@ -263,15 +278,13 @@ struct driver_info {
// FIXME -- also an interrupt mechanism // FIXME -- also an interrupt mechanism
// useful for at least PL2301/2302 and GL620USB-A // useful for at least PL2301/2302 and GL620USB-A
// and CDC use them to report 'is it connected' changes
/* framework currently "knows" bulk EPs talk packets */ /* for new devices, use the descriptor-reading code instead */
int in; /* rx endpoint */ int in; /* rx endpoint */
int out; /* tx endpoint */ int out; /* tx endpoint */
int epsize;
}; };
#define EP_SIZE(usbnet) ((usbnet)->driver_info->epsize)
// we record the state for each of our queued skbs // we record the state for each of our queued skbs
enum skb_state { enum skb_state {
illegal = 0, illegal = 0,
...@@ -300,14 +313,6 @@ MODULE_PARM_DESC (msg_level, "Initial message level (default = 1)"); ...@@ -300,14 +313,6 @@ MODULE_PARM_DESC (msg_level, "Initial message level (default = 1)");
#define RUN_CONTEXT (in_irq () ? "in_irq" \ #define RUN_CONTEXT (in_irq () ? "in_irq" \
: (in_interrupt () ? "in_interrupt" : "can sleep")) : (in_interrupt () ? "in_interrupt" : "can sleep"))
/* mostly for PDA style devices, which are always present */
static int always_connected (struct usbnet *dev)
{
return 0;
}
/*-------------------------------------------------------------------------*/
#ifdef DEBUG #ifdef DEBUG
#define devdbg(usbnet, fmt, arg...) \ #define devdbg(usbnet, fmt, arg...) \
printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net.name, ## arg) printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net.name, ## arg)
...@@ -315,11 +320,76 @@ static int always_connected (struct usbnet *dev) ...@@ -315,11 +320,76 @@ static int always_connected (struct usbnet *dev)
#define devdbg(usbnet, fmt, arg...) do {} while(0) #define devdbg(usbnet, fmt, arg...) do {} while(0)
#endif #endif
#define deverr(usbnet, fmt, arg...) \
printk(KERN_ERR "%s: " fmt "\n" , (usbnet)->net.name, ## arg)
#define devwarn(usbnet, fmt, arg...) \
printk(KERN_WARNING "%s: " fmt "\n" , (usbnet)->net.name, ## arg)
#define devinfo(usbnet, fmt, arg...) \ #define devinfo(usbnet, fmt, arg...) \
do { if ((usbnet)->msg_level >= 1) \ do { if ((usbnet)->msg_level >= 1) \
printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net.name, ## arg); \ printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net.name, ## arg); \
} while (0) } while (0)
/*-------------------------------------------------------------------------*/
/* mostly for PDA style devices, which are always connected if present */
static int always_connected (struct usbnet *dev)
{
return 0;
}
/* handles CDC Ethernet and many other network "bulk data" interfaces */
static int
get_endpoints (struct usbnet *dev, struct usb_interface *intf)
{
int tmp;
struct usb_host_interface *alt;
struct usb_host_endpoint *in, *out;
for (tmp = 0; tmp < intf->max_altsetting; tmp++) {
unsigned ep;
in = out = 0;
alt = intf->altsetting + tmp;
/* take the first altsetting with in-bulk + out-bulk;
* ignore other endpoints and altsetttings.
*/
for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
struct usb_host_endpoint *e;
e = alt->endpoint + ep;
if (e->desc.bmAttributes != USB_ENDPOINT_XFER_BULK)
continue;
if (e->desc.bEndpointAddress & USB_DIR_IN) {
if (!in)
in = e;
} else {
if (!out)
out = e;
}
if (in && out)
goto found;
}
}
return -EINVAL;
found:
if (alt->desc.bAlternateSetting != 0
|| !(dev->driver_info->flags & FLAG_NO_SETINT)) {
tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
alt->desc.bAlternateSetting);
if (tmp < 0)
return tmp;
}
dev->in = usb_rcvbulkpipe (dev->udev,
in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
dev->out = usb_sndbulkpipe (dev->udev,
out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
return 0;
}
#ifdef CONFIG_USB_AN2720 #ifdef CONFIG_USB_AN2720
...@@ -340,7 +410,6 @@ static const struct driver_info an2720_info = { ...@@ -340,7 +410,6 @@ static const struct driver_info an2720_info = {
// no check_connect available! // no check_connect available!
.in = 2, .out = 2, // direction distinguishes these .in = 2, .out = 2, // direction distinguishes these
.epsize =64,
}; };
#endif /* CONFIG_USB_AN2720 */ #endif /* CONFIG_USB_AN2720 */
...@@ -359,14 +428,225 @@ static const struct driver_info an2720_info = { ...@@ -359,14 +428,225 @@ static const struct driver_info an2720_info = {
static const struct driver_info belkin_info = { static const struct driver_info belkin_info = {
.description = "Belkin, eTEK, or compatible", .description = "Belkin, eTEK, or compatible",
.in = 1, .out = 1, // direction distinguishes these
.epsize =64,
}; };
#endif /* CONFIG_USB_BELKIN */ #endif /* CONFIG_USB_BELKIN */
#if defined (CONFIG_USB_CDCETHER) || defined (CONFIG_USB_ZAURUS)
/*-------------------------------------------------------------------------
*
* Communications Device Class, Ethernet Control model
*
* Takes two interfaces. The DATA interface is inactive till an altsetting
* is selected. Configuration data includes class descriptors.
*
* Zaurus uses nonstandard framing, but is otherwise CDC Ether.
*
*-------------------------------------------------------------------------*/
/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */
struct header_desc {
u8 bLength;
u8 bDescriptorType;
u8 bDescriptorSubType;
u16 bcdCDC;
} __attribute__ ((packed));
/* "Union Functional Descriptor" from CDC spec 5.2.3.X */
struct union_desc {
u8 bLength;
u8 bDescriptorType;
u8 bDescriptorSubType;
u8 bMasterInterface0;
u8 bSlaveInterface0;
/* ... and there could be other slave interfaces */
} __attribute__ ((packed));
/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
struct ether_desc {
u8 bLength;
u8 bDescriptorType;
u8 bDescriptorSubType;
u8 iMACAddress;
u32 bmEthernetStatistics;
u16 wMaxSegmentSize;
u16 wNumberMCFilters;
u8 bNumberPowerFilters;
} __attribute__ ((packed));
struct cdc_info {
struct header_desc *header;
struct union_desc *u;
struct ether_desc *ether;
struct usb_interface *control;
struct usb_interface *data;
};
#include <linux/ctype.h>
static u8 nibble (unsigned char c)
{
if (likely (isdigit (c)))
return c - '0';
c = toupper (c);
if (likely (isxdigit (c)))
return 10 + c - 'A';
return 0;
}
static inline int get_ethernet_addr (struct usbnet *dev, struct ether_desc *e)
{
int tmp, i;
unsigned char buf [13];
tmp = usb_string (dev->udev, e->iMACAddress, buf, sizeof buf);
if (tmp < 0)
return tmp;
else if (tmp != 12)
return -EINVAL;
for (i = tmp = 0; i < 6; i++, tmp += 2)
dev->net.dev_addr [i] =
(nibble (buf [tmp]) << 4) + nibble (buf [tmp + 1]);
return 0;
}
static struct usb_driver usbnet_driver;
static int cdc_bind (struct usbnet *dev, struct usb_interface *intf)
{
u8 *buf = intf->altsetting->extra;
int len = intf->altsetting->extralen;
struct usb_interface_descriptor *d;
struct cdc_info *info = (void *) &dev->data;
int status;
if (sizeof dev->data < sizeof *info)
return -EDOM;
/* expect strict spec conformance for the descriptors */
memset (info, 0, sizeof *info);
info->control = intf;
while (len > 3) {
/* ignore bDescriptorType != CS_INTERFACE */
if (buf [1] != 0x24)
goto next_desc;
/* bDescriptorSubType identifies three "must have" descriptors;
* save them for later.
*/
switch (buf [2]) {
case 0x00: /* Header, mostly useless */
if (info->header)
goto bad_desc;
info->header = (void *) buf;
if (info->header->bLength != sizeof *info->header)
goto bad_desc;
break;
case 0x06: /* Union (groups interfaces) */
if (info->u)
goto bad_desc;
info->u = (void *) buf;
if (info->u->bLength != sizeof *info->u)
goto bad_desc;
d = &intf->altsetting->desc;
if (info->u->bMasterInterface0 != d->bInterfaceNumber)
goto bad_desc;
info->data = dev->udev->actconfig->interface;
if (intf != (info->data + info->u->bMasterInterface0))
goto bad_desc;
/* a data interface altsetting does the real i/o */
info->data += info->u->bSlaveInterface0;
d = &info->data->altsetting->desc;
if (info->u->bSlaveInterface0 != d->bInterfaceNumber
|| d->bInterfaceClass != USB_CLASS_CDC_DATA)
goto bad_desc;
if (usb_interface_claimed (info->data))
return -EBUSY;
break;
case 0x0F: /* Ethernet Networking */
if (info->ether)
goto bad_desc;
info->ether = (void *) buf;
if (info->ether->bLength != sizeof *info->ether)
goto bad_desc;
break;
}
next_desc:
len -= buf [0]; /* bLength */
buf += buf [0];
}
if (!info->header || !info ->u || !info->ether)
goto bad_desc;
status = get_ethernet_addr (dev, info->ether);
if (status < 0)
return status;
/* claim data interface and set it up ... with side effects.
* network traffic can't flow until an altsetting is enabled.
*/
usb_driver_claim_interface (&usbnet_driver, info->data, dev);
status = get_endpoints (dev, info->data);
if (status < 0) {
usb_driver_release_interface (&usbnet_driver, info->data);
return status;
}
/* FIXME cdc-ether has some multicast code too, though it complains
* in routine cases. info->ether describes the multicast support.
*/
dev->net.mtu = cpu_to_le16p (&info->ether->wMaxSegmentSize)
- ETH_HLEN;
if ((dev->driver_info->flags & FLAG_FRAMING_Z) == 0)
strcpy (dev->net.name, "eth%d");
return 0;
bad_desc:
// devdbg (dev, "bad CDC descriptors");
return -ENODEV;
}
static void cdc_unbind (struct usbnet *dev, struct usb_interface *intf)
{
struct cdc_info *info = (void *) &dev->data;
/* disconnect master --> disconnect slave */
if (intf == info->control && info->data) {
usb_driver_release_interface (&usbnet_driver, info->data);
info->data = 0;
}
/* and vice versa (just in case) */
else if (intf == info->data && info->control) {
usb_driver_release_interface (&usbnet_driver, info->control);
info->control = 0;
}
}
#endif /* CONFIG_USB_ZAURUS || CONFIG_USB_CDCETHER */
#ifdef CONFIG_USB_CDCETHER
static const struct driver_info cdc_info = {
.description = "CDC Ethernet Device",
// .check_connect = cdc_check_connect,
.bind = cdc_bind,
.unbind = cdc_unbind,
};
#endif /* CONFIG_USB_CDCETHER */
#ifdef CONFIG_USB_EPSON2888 #ifdef CONFIG_USB_EPSON2888
...@@ -386,7 +666,6 @@ static const struct driver_info epson2888_info = { ...@@ -386,7 +666,6 @@ static const struct driver_info epson2888_info = {
.check_connect = always_connected, .check_connect = always_connected,
.in = 4, .out = 3, .in = 4, .out = 3,
.epsize = 64,
}; };
#endif /* CONFIG_USB_EPSON2888 */ #endif /* CONFIG_USB_EPSON2888 */
...@@ -704,7 +983,7 @@ genelink_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags) ...@@ -704,7 +983,7 @@ genelink_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags)
*packet_len = length; *packet_len = length;
// add padding byte // add padding byte
if ((skb->len % EP_SIZE (dev)) == 0) if ((skb->len % dev->maxpacket) == 0)
skb_put (skb, 1); skb_put (skb, 1);
return skb; return skb;
...@@ -717,7 +996,6 @@ static const struct driver_info genelink_info = { ...@@ -717,7 +996,6 @@ static const struct driver_info genelink_info = {
.tx_fixup = genelink_tx_fixup, .tx_fixup = genelink_tx_fixup,
.in = 1, .out = 2, .in = 1, .out = 2,
.epsize =64,
#ifdef GENELINK_ACK #ifdef GENELINK_ACK
.check_connect =genelink_check_connect, .check_connect =genelink_check_connect,
...@@ -737,6 +1015,9 @@ static const struct driver_info genelink_info = { ...@@ -737,6 +1015,9 @@ static const struct driver_info genelink_info = {
* *
*-------------------------------------------------------------------------*/ *-------------------------------------------------------------------------*/
#define dev_packet_id data[0]
#define frame_errors data[1]
/* /*
* NetChip framing of ethernet packets, supporting additional error * NetChip framing of ethernet packets, supporting additional error
* checks for links that may drop bulk packets from inside messages. * checks for links that may drop bulk packets from inside messages.
...@@ -1064,6 +1345,60 @@ static int net1080_check_connect (struct usbnet *dev) ...@@ -1064,6 +1345,60 @@ static int net1080_check_connect (struct usbnet *dev)
return 0; return 0;
} }
static void nc_flush_complete (struct urb *urb, struct pt_regs *regs)
{
kfree (urb->context);
usb_free_urb(urb);
}
static void nc_ensure_sync (struct usbnet *dev)
{
dev->frame_errors++;
if (dev->frame_errors > 5) {
struct urb *urb;
struct usb_ctrlrequest *req;
int status;
/* Send a flush */
urb = usb_alloc_urb (0, SLAB_ATOMIC);
if (!urb)
return;
req = kmalloc (sizeof *req, GFP_ATOMIC);
if (!req) {
usb_free_urb (urb);
return;
}
req->bRequestType = USB_DIR_OUT
| USB_TYPE_VENDOR
| USB_RECIP_DEVICE;
req->bRequest = REQUEST_REGISTER;
req->wValue = cpu_to_le16 (USBCTL_FLUSH_THIS
| USBCTL_FLUSH_OTHER);
req->wIndex = cpu_to_le16 (REG_USBCTL);
req->wLength = cpu_to_le16 (0);
/* queue an async control request, we don't need
* to do anything when it finishes except clean up.
*/
usb_fill_control_urb (urb, dev->udev,
usb_sndctrlpipe (dev->udev, 0),
(unsigned char *) req,
NULL, 0,
nc_flush_complete, req);
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status) {
kfree (req);
usb_free_urb (urb);
return;
}
devdbg (dev, "flush net1080; too many framing errors");
dev->frame_errors = 0;
}
}
static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb) static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
{ {
struct nc_header *header; struct nc_header *header;
...@@ -1076,6 +1411,7 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb) ...@@ -1076,6 +1411,7 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
dbg ("rx framesize %d range %d..%d mtu %d", skb->len, dbg ("rx framesize %d range %d..%d mtu %d", skb->len,
(int)MIN_FRAMED, (int)FRAMED_SIZE (dev->net.mtu), (int)MIN_FRAMED, (int)FRAMED_SIZE (dev->net.mtu),
dev->net.mtu); dev->net.mtu);
nc_ensure_sync (dev);
return 0; return 0;
} }
...@@ -1085,15 +1421,18 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb) ...@@ -1085,15 +1421,18 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) { if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) {
dev->stats.rx_frame_errors++; dev->stats.rx_frame_errors++;
dbg ("packet too big, %d", header->packet_len); dbg ("packet too big, %d", header->packet_len);
nc_ensure_sync (dev);
return 0; return 0;
} else if (header->hdr_len < MIN_HEADER) { } else if (header->hdr_len < MIN_HEADER) {
dev->stats.rx_frame_errors++; dev->stats.rx_frame_errors++;
dbg ("header too short, %d", header->hdr_len); dbg ("header too short, %d", header->hdr_len);
nc_ensure_sync (dev);
return 0; return 0;
} else if (header->hdr_len > MIN_HEADER) { } else if (header->hdr_len > MIN_HEADER) {
// out of band data for us? // out of band data for us?
dbg ("header OOB, %d bytes", dbg ("header OOB, %d bytes",
header->hdr_len - MIN_HEADER); header->hdr_len - MIN_HEADER);
nc_ensure_sync (dev);
// switch (vendor/product ids) { ... } // switch (vendor/product ids) { ... }
} }
skb_pull (skb, header->hdr_len); skb_pull (skb, header->hdr_len);
...@@ -1114,6 +1453,7 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb) ...@@ -1114,6 +1453,7 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
dev->stats.rx_frame_errors++; dev->stats.rx_frame_errors++;
dbg ("bad packet len %d (expected %d)", dbg ("bad packet len %d (expected %d)",
skb->len, header->packet_len); skb->len, header->packet_len);
nc_ensure_sync (dev);
return 0; return 0;
} }
if (header->packet_id != get_unaligned (&trailer->packet_id)) { if (header->packet_id != get_unaligned (&trailer->packet_id)) {
...@@ -1126,6 +1466,7 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb) ...@@ -1126,6 +1466,7 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
devdbg (dev, "frame <rx h %d p %d id %d", header->hdr_len, devdbg (dev, "frame <rx h %d p %d id %d", header->hdr_len,
header->packet_len, header->packet_id); header->packet_len, header->packet_id);
#endif #endif
dev->frame_errors = 0;
return 1; return 1;
} }
...@@ -1143,11 +1484,13 @@ net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags) ...@@ -1143,11 +1484,13 @@ net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags)
if ((padlen + sizeof (struct nc_trailer)) <= tailroom if ((padlen + sizeof (struct nc_trailer)) <= tailroom
&& sizeof (struct nc_header) <= headroom) && sizeof (struct nc_header) <= headroom)
/* There's enough head and tail room */
return skb; return skb;
if ((sizeof (struct nc_header) + padlen if ((sizeof (struct nc_header) + padlen
+ sizeof (struct nc_trailer)) < + sizeof (struct nc_trailer)) <
(headroom + tailroom)) { (headroom + tailroom)) {
/* There's enough total room, so just readjust */
skb->data = memmove (skb->head skb->data = memmove (skb->head
+ sizeof (struct nc_header), + sizeof (struct nc_header),
skb->data, skb->len); skb->data, skb->len);
...@@ -1155,6 +1498,8 @@ net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags) ...@@ -1155,6 +1498,8 @@ net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags)
return skb; return skb;
} }
} }
/* Create a new skb to use with the correct size */
skb2 = skb_copy_expand (skb, skb2 = skb_copy_expand (skb,
sizeof (struct nc_header), sizeof (struct nc_header),
sizeof (struct nc_trailer) + padlen, sizeof (struct nc_trailer) + padlen,
...@@ -1170,9 +1515,6 @@ static const struct driver_info net1080_info = { ...@@ -1170,9 +1515,6 @@ static const struct driver_info net1080_info = {
.check_connect =net1080_check_connect, .check_connect =net1080_check_connect,
.rx_fixup = net1080_rx_fixup, .rx_fixup = net1080_rx_fixup,
.tx_fixup = net1080_tx_fixup, .tx_fixup = net1080_tx_fixup,
.in = 1, .out = 1, // direction distinguishes these
.epsize =64,
}; };
#endif /* CONFIG_USB_NET1080 */ #endif /* CONFIG_USB_NET1080 */
...@@ -1237,37 +1579,13 @@ static const struct driver_info prolific_info = { ...@@ -1237,37 +1579,13 @@ static const struct driver_info prolific_info = {
.flags = FLAG_NO_SETINT, .flags = FLAG_NO_SETINT,
/* some PL-2302 versions seem to fail usb_set_interface() */ /* some PL-2302 versions seem to fail usb_set_interface() */
.reset = pl_reset, .reset = pl_reset,
.in = 3, .out = 2,
.epsize =64,
}; };
#endif /* CONFIG_USB_PL2301 */ #endif /* CONFIG_USB_PL2301 */
#ifdef CONFIG_USB_PXA #ifdef CONFIG_USB_ARMLINUX
/*-------------------------------------------------------------------------
*
* PXA250 and PXA210 use XScale cores (ARM v5TE) with better USB support,
* and different USB endpoint numbering than the SA1100 devices.
*
*-------------------------------------------------------------------------*/
static const struct driver_info pxa_info = {
.description = "PXA-250 Linux Device",
.check_connect = always_connected,
.in = 1, .out = 2,
.epsize = 64,
};
#endif /* CONFIG_USB_PXA */
#ifdef CONFIG_USB_SA1100
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
...@@ -1279,25 +1597,24 @@ static const struct driver_info pxa_info = { ...@@ -1279,25 +1597,24 @@ static const struct driver_info pxa_info = {
* This describes the driver currently in standard ARM Linux kernels. * This describes the driver currently in standard ARM Linux kernels.
* The Zaurus uses a different driver (see later). * The Zaurus uses a different driver (see later).
* *
* PXA25x and PXA210 use XScale cores (ARM v5TE) with better USB support
* and different USB endpoint numbering than the SA1100 devices. The
* mach-pxa/usb-eth.c driver re-uses the device ids from mach-sa1100
* so we rely on the endpoint descriptors.
*
*-------------------------------------------------------------------------*/ *-------------------------------------------------------------------------*/
static const struct driver_info linuxdev_info = { static const struct driver_info linuxdev_info = {
.description = "SA-1100 Linux Device", .description = "Linux Device",
.check_connect = always_connected, .check_connect = always_connected,
.in = 2, .out = 1,
.epsize = 64,
}; };
static const struct driver_info yopy_info = { static const struct driver_info yopy_info = {
.description = "Yopy", .description = "Yopy",
.check_connect = always_connected, .check_connect = always_connected,
.in = 2, .out = 1,
.epsize = 64,
}; };
#endif /* CONFIG_USB_SA1100 */ #endif /* CONFIG_USB_ARMLINUX */
#ifdef CONFIG_USB_ZAURUS #ifdef CONFIG_USB_ZAURUS
...@@ -1349,10 +1666,9 @@ static const struct driver_info zaurus_sl5x00_info = { ...@@ -1349,10 +1666,9 @@ static const struct driver_info zaurus_sl5x00_info = {
.description = "Sharp Zaurus SL-5x00", .description = "Sharp Zaurus SL-5x00",
.flags = FLAG_FRAMING_Z, .flags = FLAG_FRAMING_Z,
.check_connect = always_connected, .check_connect = always_connected,
.bind = cdc_bind,
.unbind = cdc_unbind,
.tx_fixup = zaurus_tx_fixup, .tx_fixup = zaurus_tx_fixup,
.in = 2, .out = 1,
.epsize = 64,
}; };
static const struct driver_info zaurus_sla300_info = { static const struct driver_info zaurus_sla300_info = {
.description = "Sharp Zaurus SL-A300", .description = "Sharp Zaurus SL-A300",
...@@ -1361,7 +1677,6 @@ static const struct driver_info zaurus_sla300_info = { ...@@ -1361,7 +1677,6 @@ static const struct driver_info zaurus_sla300_info = {
.tx_fixup = zaurus_tx_fixup, .tx_fixup = zaurus_tx_fixup,
.in = 1, .out = 2, .in = 1, .out = 2,
.epsize = 64,
}; };
static const struct driver_info zaurus_slb500_info = { static const struct driver_info zaurus_slb500_info = {
/* Japanese B500 ~= US SL-5600 */ /* Japanese B500 ~= US SL-5600 */
...@@ -1371,7 +1686,6 @@ static const struct driver_info zaurus_slb500_info = { ...@@ -1371,7 +1686,6 @@ static const struct driver_info zaurus_slb500_info = {
.tx_fixup = zaurus_tx_fixup, .tx_fixup = zaurus_tx_fixup,
.in = 1, .out = 2, .in = 1, .out = 2,
.epsize = 64,
}; };
// SL-5600 and C-700 are PXA based; should resemble A300 // SL-5600 and C-700 are PXA based; should resemble A300
...@@ -1403,7 +1717,7 @@ static int usbnet_change_mtu (struct net_device *net, int new_mtu) ...@@ -1403,7 +1717,7 @@ static int usbnet_change_mtu (struct net_device *net, int new_mtu)
return -EINVAL; return -EINVAL;
#endif #endif
// no second zero-length packet read wanted after mtu-sized packets // no second zero-length packet read wanted after mtu-sized packets
if (((new_mtu + sizeof (struct ethhdr)) % EP_SIZE (dev)) == 0) if (((new_mtu + sizeof (struct ethhdr)) % dev->maxpacket) == 0)
return -EDOM; return -EDOM;
net->mtu = new_mtu; net->mtu = new_mtu;
return 0; return 0;
...@@ -1444,10 +1758,9 @@ static void defer_kevent (struct usbnet *dev, int work) ...@@ -1444,10 +1758,9 @@ static void defer_kevent (struct usbnet *dev, int work)
{ {
set_bit (work, &dev->flags); set_bit (work, &dev->flags);
if (!schedule_work (&dev->kevent)) if (!schedule_work (&dev->kevent))
err ("%s: kevent %d may have been dropped", deverr (dev, "kevent %d may have been dropped", work);
dev->net.name, work);
else else
dbg ("%s: kevent %d scheduled", dev->net.name, work); devdbg (dev, "kevent %d scheduled", work);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -1480,7 +1793,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags) ...@@ -1480,7 +1793,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
size = (sizeof (struct ethhdr) + dev->net.mtu); size = (sizeof (struct ethhdr) + dev->net.mtu);
if ((skb = alloc_skb (size, flags)) == 0) { if ((skb = alloc_skb (size, flags)) == 0) {
dbg ("no rx skb"); devdbg (dev, "no rx skb");
defer_kevent (dev, EVENT_RX_MEMORY); defer_kevent (dev, EVENT_RX_MEMORY);
usb_free_urb (urb); usb_free_urb (urb);
return; return;
...@@ -1492,14 +1805,14 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags) ...@@ -1492,14 +1805,14 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
entry->state = rx_start; entry->state = rx_start;
entry->length = 0; entry->length = 0;
usb_fill_bulk_urb (urb, dev->udev, usb_fill_bulk_urb (urb, dev->udev, dev->in,
usb_rcvbulkpipe (dev->udev, dev->driver_info->in),
skb->data, size, rx_complete, skb); skb->data, size, rx_complete, skb);
urb->transfer_flags |= URB_ASYNC_UNLINK; urb->transfer_flags |= URB_ASYNC_UNLINK;
spin_lock_irqsave (&dev->rxq.lock, lockflags); spin_lock_irqsave (&dev->rxq.lock, lockflags);
if (netif_running (&dev->net) if (netif_running (&dev->net)
&& netif_device_present (&dev->net)
&& !test_bit (EVENT_RX_HALT, &dev->flags)) { && !test_bit (EVENT_RX_HALT, &dev->flags)) {
switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){
case -EPIPE: case -EPIPE:
...@@ -1508,15 +1821,19 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags) ...@@ -1508,15 +1821,19 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
case -ENOMEM: case -ENOMEM:
defer_kevent (dev, EVENT_RX_MEMORY); defer_kevent (dev, EVENT_RX_MEMORY);
break; break;
case -ENODEV:
devdbg (dev, "device gone");
netif_device_detach (&dev->net);
break;
default: default:
dbg ("%s rx submit, %d", dev->net.name, retval); devdbg (dev, "rx submit, %d", retval);
tasklet_schedule (&dev->bh); tasklet_schedule (&dev->bh);
break; break;
case 0: case 0:
__skb_queue_tail (&dev->rxq, skb); __skb_queue_tail (&dev->rxq, skb);
} }
} else { } else {
dbg ("rx: stopped"); devdbg (dev, "rx: stopped");
retval = -ENOLINK; retval = -ENOLINK;
} }
spin_unlock_irqrestore (&dev->rxq.lock, lockflags); spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
...@@ -1553,7 +1870,7 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb) ...@@ -1553,7 +1870,7 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
if (status != NET_RX_SUCCESS) if (status != NET_RX_SUCCESS)
devdbg (dev, "netif_rx status %d", status); devdbg (dev, "netif_rx status %d", status);
} else { } else {
dbg ("drop"); devdbg (dev, "drop");
error: error:
dev->stats.rx_errors++; dev->stats.rx_errors++;
skb_queue_tail (&dev->done, skb); skb_queue_tail (&dev->done, skb);
...@@ -1580,7 +1897,7 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs) ...@@ -1580,7 +1897,7 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
entry->state = rx_cleanup; entry->state = rx_cleanup;
dev->stats.rx_errors++; dev->stats.rx_errors++;
dev->stats.rx_length_errors++; dev->stats.rx_length_errors++;
dbg ("rx length %d", skb->len); devdbg (dev, "rx length %d", skb->len);
} }
break; break;
...@@ -1589,15 +1906,31 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs) ...@@ -1589,15 +1906,31 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
// we avoid the highspeed version of the ETIMEOUT/EILSEQ // we avoid the highspeed version of the ETIMEOUT/EILSEQ
// storm, recovering as needed. // storm, recovering as needed.
case -EPIPE: case -EPIPE:
dev->stats.rx_errors++;
defer_kevent (dev, EVENT_RX_HALT); defer_kevent (dev, EVENT_RX_HALT);
// FALLTHROUGH // FALLTHROUGH
// software-driven interface shutdown // software-driven interface shutdown
case -ECONNRESET: // according to API spec case -ECONNRESET: // async unlink
case -ECONNABORTED: // some (now fixed?) UHCI bugs case -ESHUTDOWN: // hardware gone
dbg ("%s rx shutdown, code %d", dev->net.name, urb_status); #ifdef VERBOSE
devdbg (dev, "rx shutdown, code %d", urb_status);
#endif
goto block;
// we get controller i/o faults during khubd disconnect() delays.
// throttle down resubmits, to avoid log floods; just temporarily,
// so we still recover when the fault isn't a khubd delay.
case -EPROTO: // ehci
case -ETIMEDOUT: // ohci
case -EILSEQ: // uhci
dev->stats.rx_errors++;
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
devdbg (dev, "rx throttle %d", urb_status);
}
block:
entry->state = rx_cleanup; entry->state = rx_cleanup;
// do urb frees only in the tasklet (UHCI has oopsed ...)
entry->urb = urb; entry->urb = urb;
urb = 0; urb = 0;
break; break;
...@@ -1608,12 +1941,9 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs) ...@@ -1608,12 +1941,9 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
// FALLTHROUGH // FALLTHROUGH
default: default:
// on unplug we get ETIMEDOUT (ohci) or EILSEQ (uhci)
// until khubd sees its interrupt and disconnects us.
// that can easily be hundreds of passes through here.
entry->state = rx_cleanup; entry->state = rx_cleanup;
dev->stats.rx_errors++; dev->stats.rx_errors++;
dbg ("%s rx: status %d", dev->net.name, urb_status); devdbg (dev, "rx status %d", urb_status);
break; break;
} }
...@@ -1628,7 +1958,7 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs) ...@@ -1628,7 +1958,7 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
usb_free_urb (urb); usb_free_urb (urb);
} }
#ifdef VERBOSE #ifdef VERBOSE
dbg ("no read resubmitted"); devdbg (dev, "no read resubmitted");
#endif /* VERBOSE */ #endif /* VERBOSE */
} }
...@@ -1636,7 +1966,7 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs) ...@@ -1636,7 +1966,7 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
// unlink pending rx/tx; completion handlers do all other cleanup // unlink pending rx/tx; completion handlers do all other cleanup
static int unlink_urbs (struct sk_buff_head *q) static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
{ {
unsigned long flags; unsigned long flags;
struct sk_buff *skb, *skbnext; struct sk_buff *skb, *skbnext;
...@@ -1656,7 +1986,7 @@ static int unlink_urbs (struct sk_buff_head *q) ...@@ -1656,7 +1986,7 @@ static int unlink_urbs (struct sk_buff_head *q)
// these (async) unlinks complete immediately // these (async) unlinks complete immediately
retval = usb_unlink_urb (urb); retval = usb_unlink_urb (urb);
if (retval != -EINPROGRESS && retval != 0) if (retval != -EINPROGRESS && retval != 0)
dbg ("unlink urb err, %d", retval); devdbg (dev, "unlink urb err, %d", retval);
else else
count++; count++;
} }
...@@ -1688,7 +2018,7 @@ static int usbnet_stop (struct net_device *net) ...@@ -1688,7 +2018,7 @@ static int usbnet_stop (struct net_device *net)
// ensure there are no more active urbs // ensure there are no more active urbs
add_wait_queue (&unlink_wakeup, &wait); add_wait_queue (&unlink_wakeup, &wait);
dev->wait = &unlink_wakeup; dev->wait = &unlink_wakeup;
temp = unlink_urbs (&dev->txq) + unlink_urbs (&dev->rxq); temp = unlink_urbs (dev, &dev->txq) + unlink_urbs (dev, &dev->rxq);
// maybe wait for deletions to finish. // maybe wait for deletions to finish.
while (skb_queue_len (&dev->rxq) while (skb_queue_len (&dev->rxq)
...@@ -1696,11 +2026,16 @@ static int usbnet_stop (struct net_device *net) ...@@ -1696,11 +2026,16 @@ static int usbnet_stop (struct net_device *net)
&& skb_queue_len (&dev->done)) { && skb_queue_len (&dev->done)) {
set_current_state (TASK_UNINTERRUPTIBLE); set_current_state (TASK_UNINTERRUPTIBLE);
schedule_timeout (UNLINK_TIMEOUT_JIFFIES); schedule_timeout (UNLINK_TIMEOUT_JIFFIES);
dbg ("waited for %d urb completions", temp); devdbg (dev, "waited for %d urb completions", temp);
} }
dev->wait = 0; dev->wait = 0;
remove_wait_queue (&unlink_wakeup, &wait); remove_wait_queue (&unlink_wakeup, &wait);
// deferred work (task, timer, softirq) must also stop
flush_scheduled_work ();
del_timer_sync (&dev->delay);
tasklet_kill (&dev->bh);
mutex_unlock (&dev->mutex); mutex_unlock (&dev->mutex);
return 0; return 0;
} }
...@@ -1738,7 +2073,7 @@ static int usbnet_open (struct net_device *net) ...@@ -1738,7 +2073,7 @@ static int usbnet_open (struct net_device *net)
if (dev->msg_level >= 2) if (dev->msg_level >= 2)
devinfo (dev, "open: enable queueing " devinfo (dev, "open: enable queueing "
"(rx %d, tx %d) mtu %d %s framing", "(rx %d, tx %d) mtu %d %s framing",
RX_QLEN, TX_QLEN, dev->net.mtu, RX_QLEN (dev), TX_QLEN (dev), dev->net.mtu,
(info->flags & (FLAG_FRAMING_NC | FLAG_FRAMING_GL)) (info->flags & (FLAG_FRAMING_NC | FLAG_FRAMING_GL))
? ((info->flags & FLAG_FRAMING_NC) ? ((info->flags & FLAG_FRAMING_NC)
? "NetChip" ? "NetChip"
...@@ -1755,7 +2090,8 @@ static int usbnet_open (struct net_device *net) ...@@ -1755,7 +2090,8 @@ static int usbnet_open (struct net_device *net)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static int usbnet_ethtool_ioctl (struct net_device *net, void *useraddr) static inline int
usbnet_ethtool_ioctl (struct net_device *net, void *useraddr)
{ {
struct usbnet *dev = (struct usbnet *) net->priv; struct usbnet *dev = (struct usbnet *) net->priv;
u32 cmd; u32 cmd;
...@@ -1829,9 +2165,8 @@ static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd) ...@@ -1829,9 +2165,8 @@ static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
/* work that cannot be done in interrupt context uses keventd. /* work that cannot be done in interrupt context uses keventd.
* *
* NOTE: "uhci" and "usb-uhci" may have trouble with this since they don't * NOTE: with 2.5 we could do more of this using completion callbacks,
* queue control transfers to individual devices, and other threads could * especially now that control transfers can be queued.
* trigger control requests concurrently. hope that's rare.
*/ */
static void static void
kevent (void *data) kevent (void *data)
...@@ -1841,24 +2176,22 @@ kevent (void *data) ...@@ -1841,24 +2176,22 @@ kevent (void *data)
/* usb_clear_halt() needs a thread context */ /* usb_clear_halt() needs a thread context */
if (test_bit (EVENT_TX_HALT, &dev->flags)) { if (test_bit (EVENT_TX_HALT, &dev->flags)) {
unlink_urbs (&dev->txq); unlink_urbs (dev, &dev->txq);
status = usb_clear_halt (dev->udev, status = usb_clear_halt (dev->udev, dev->out);
usb_sndbulkpipe (dev->udev, dev->driver_info->out));
if (status < 0) if (status < 0)
err ("%s: can't clear tx halt, status %d", deverr (dev, "can't clear tx halt, status %d",
dev->net.name, status); status);
else { else {
clear_bit (EVENT_TX_HALT, &dev->flags); clear_bit (EVENT_TX_HALT, &dev->flags);
netif_wake_queue (&dev->net); netif_wake_queue (&dev->net);
} }
} }
if (test_bit (EVENT_RX_HALT, &dev->flags)) { if (test_bit (EVENT_RX_HALT, &dev->flags)) {
unlink_urbs (&dev->rxq); unlink_urbs (dev, &dev->rxq);
status = usb_clear_halt (dev->udev, status = usb_clear_halt (dev->udev, dev->in);
usb_rcvbulkpipe (dev->udev, dev->driver_info->in));
if (status < 0) if (status < 0)
err ("%s: can't clear rx halt, status %d", deverr (dev, "can't clear rx halt, status %d",
dev->net.name, status); status);
else { else {
clear_bit (EVENT_RX_HALT, &dev->flags); clear_bit (EVENT_RX_HALT, &dev->flags);
tasklet_schedule (&dev->bh); tasklet_schedule (&dev->bh);
...@@ -1881,8 +2214,8 @@ kevent (void *data) ...@@ -1881,8 +2214,8 @@ kevent (void *data)
} }
if (dev->flags) if (dev->flags)
dbg ("%s: kevent done, flags = 0x%lx", devdbg (dev, "kevent done, flags = 0x%lx",
dev->net.name, dev->flags); dev->flags);
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -1893,8 +2226,35 @@ static void tx_complete (struct urb *urb, struct pt_regs *regs) ...@@ -1893,8 +2226,35 @@ static void tx_complete (struct urb *urb, struct pt_regs *regs)
struct skb_data *entry = (struct skb_data *) skb->cb; struct skb_data *entry = (struct skb_data *) skb->cb;
struct usbnet *dev = entry->dev; struct usbnet *dev = entry->dev;
if (urb->status == -EPIPE) if (urb->status == 0) {
dev->stats.tx_packets++;
dev->stats.tx_bytes += entry->length;
} else {
dev->stats.tx_errors++;
switch (urb->status) {
case -EPIPE:
defer_kevent (dev, EVENT_TX_HALT); defer_kevent (dev, EVENT_TX_HALT);
break;
// like rx, tx gets controller i/o faults during khubd delays
// and so it uses the same throttling mechanism.
case -EPROTO: // ehci
case -ETIMEDOUT: // ohci
case -EILSEQ: // uhci
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay,
jiffies + THROTTLE_JIFFIES);
devdbg (dev, "tx throttle %d", urb->status);
}
netif_stop_queue (&dev->net);
break;
default:
devdbg (dev, "tx err %d", entry->urb->status);
break;
}
}
urb->dev = 0; urb->dev = 0;
entry->state = tx_done; entry->state = tx_done;
defer_bh (dev, skb); defer_bh (dev, skb);
...@@ -1906,7 +2266,7 @@ static void usbnet_tx_timeout (struct net_device *net) ...@@ -1906,7 +2266,7 @@ static void usbnet_tx_timeout (struct net_device *net)
{ {
struct usbnet *dev = (struct usbnet *) net->priv; struct usbnet *dev = (struct usbnet *) net->priv;
unlink_urbs (&dev->txq); unlink_urbs (dev, &dev->txq);
tasklet_schedule (&dev->bh); tasklet_schedule (&dev->bh);
// FIXME: device recovery -- reset? // FIXME: device recovery -- reset?
...@@ -1933,14 +2293,14 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) ...@@ -1933,14 +2293,14 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
if (info->tx_fixup) { if (info->tx_fixup) {
skb = info->tx_fixup (dev, skb, GFP_ATOMIC); skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
if (!skb) { if (!skb) {
dbg ("can't tx_fixup skb"); devdbg (dev, "can't tx_fixup skb");
goto drop; goto drop;
} }
} }
length = skb->len; length = skb->len;
if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) { if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
dbg ("no urb"); devdbg (dev, "no urb");
goto drop; goto drop;
} }
...@@ -1965,20 +2325,24 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) ...@@ -1965,20 +2325,24 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
} else } else
#endif /* CONFIG_USB_NET1080 */ #endif /* CONFIG_USB_NET1080 */
/* don't assume the hardware handles USB_ZERO_PACKET */ usb_fill_bulk_urb (urb, dev->udev, dev->out,
if ((length % EP_SIZE (dev)) == 0)
skb->len++;
usb_fill_bulk_urb (urb, dev->udev,
usb_sndbulkpipe (dev->udev, info->out),
skb->data, skb->len, tx_complete, skb); skb->data, skb->len, tx_complete, skb);
urb->transfer_flags |= URB_ASYNC_UNLINK; urb->transfer_flags |= URB_ASYNC_UNLINK;
/* don't assume the hardware handles USB_ZERO_PACKET
* NOTE: strictly conforming cdc-ether devices should expect
* the ZLP here, but ignore the one-byte packet.
*
* FIXME zero that byte, if it doesn't require a new skb.
*/
if ((length % dev->maxpacket) == 0)
urb->transfer_buffer_length++;
spin_lock_irqsave (&dev->txq.lock, flags); spin_lock_irqsave (&dev->txq.lock, flags);
#ifdef CONFIG_USB_NET1080 #ifdef CONFIG_USB_NET1080
if (info->flags & FLAG_FRAMING_NC) { if (info->flags & FLAG_FRAMING_NC) {
header->packet_id = cpu_to_le16 (dev->packet_id++); header->packet_id = cpu_to_le16 ((u16)dev->dev_packet_id++);
put_unaligned (header->packet_id, &trailer->packet_id); put_unaligned (header->packet_id, &trailer->packet_id);
#if 0 #if 0
devdbg (dev, "frame >tx h %d p %d id %d", devdbg (dev, "frame >tx h %d p %d id %d",
...@@ -1994,12 +2358,12 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) ...@@ -1994,12 +2358,12 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
defer_kevent (dev, EVENT_TX_HALT); defer_kevent (dev, EVENT_TX_HALT);
break; break;
default: default:
dbg ("%s tx: submit urb err %d", net->name, retval); devdbg (dev, "tx: submit urb err %d", retval);
break; break;
case 0: case 0:
net->trans_start = jiffies; net->trans_start = jiffies;
__skb_queue_tail (&dev->txq, skb); __skb_queue_tail (&dev->txq, skb);
if (dev->txq.qlen >= TX_QLEN) if (dev->txq.qlen >= TX_QLEN (dev))
netif_stop_queue (net); netif_stop_queue (net);
} }
spin_unlock_irqrestore (&dev->txq.lock, flags); spin_unlock_irqrestore (&dev->txq.lock, flags);
...@@ -2024,7 +2388,7 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) ...@@ -2024,7 +2388,7 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
// tasklet ... work that avoided running in_irq() // tasklet (work deferred from completions, in_irq) or timer
static void usbnet_bh (unsigned long param) static void usbnet_bh (unsigned long param)
{ {
...@@ -2040,23 +2404,12 @@ static void usbnet_bh (unsigned long param) ...@@ -2040,23 +2404,12 @@ static void usbnet_bh (unsigned long param)
rx_process (dev, skb); rx_process (dev, skb);
continue; continue;
case tx_done: case tx_done:
if (entry->urb->status) {
// can this statistic become more specific?
dev->stats.tx_errors++;
dbg ("%s tx: err %d", dev->net.name,
entry->urb->status);
} else {
dev->stats.tx_packets++;
dev->stats.tx_bytes += entry->length;
}
// FALLTHROUGH:
case rx_cleanup: case rx_cleanup:
usb_free_urb (entry->urb); usb_free_urb (entry->urb);
dev_kfree_skb (skb); dev_kfree_skb (skb);
continue; continue;
default: default:
dbg ("%s: bogus skb state %d", devdbg (dev, "bogus skb state %d", entry->state);
dev->net.name, entry->state);
} }
} }
...@@ -2068,23 +2421,28 @@ static void usbnet_bh (unsigned long param) ...@@ -2068,23 +2421,28 @@ static void usbnet_bh (unsigned long param)
// or are we maybe short a few urbs? // or are we maybe short a few urbs?
} else if (netif_running (&dev->net) } else if (netif_running (&dev->net)
&& netif_device_present (&dev->net)
&& !timer_pending (&dev->delay)
&& !test_bit (EVENT_RX_HALT, &dev->flags)) { && !test_bit (EVENT_RX_HALT, &dev->flags)) {
int temp = dev->rxq.qlen; int temp = dev->rxq.qlen;
int qlen = RX_QLEN (dev);
if (temp < RX_QLEN) { if (temp < qlen) {
struct urb *urb; struct urb *urb;
int i; int i;
for (i = 0; i < 3 && dev->rxq.qlen < RX_QLEN; i++) {
// don't refill the queue all at once
for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
if ((urb = usb_alloc_urb (0, GFP_ATOMIC)) != 0) if ((urb = usb_alloc_urb (0, GFP_ATOMIC)) != 0)
rx_submit (dev, urb, GFP_ATOMIC); rx_submit (dev, urb, GFP_ATOMIC);
} }
if (temp != dev->rxq.qlen) if (temp != dev->rxq.qlen)
devdbg (dev, "rxqlen %d --> %d", devdbg (dev, "rxqlen %d --> %d",
temp, dev->rxq.qlen); temp, dev->rxq.qlen);
if (dev->rxq.qlen < RX_QLEN) if (dev->rxq.qlen < qlen)
tasklet_schedule (&dev->bh); tasklet_schedule (&dev->bh);
} }
if (dev->txq.qlen < TX_QLEN) if (dev->txq.qlen < TX_QLEN (dev))
netif_wake_queue (&dev->net); netif_wake_queue (&dev->net);
} }
} }
...@@ -2117,13 +2475,8 @@ static void usbnet_disconnect (struct usb_interface *intf) ...@@ -2117,13 +2475,8 @@ static void usbnet_disconnect (struct usb_interface *intf)
unregister_netdev (&dev->net); unregister_netdev (&dev->net);
mutex_lock (&usbnet_mutex); if (dev->driver_info->unbind)
mutex_lock (&dev->mutex); dev->driver_info->unbind (dev, intf);
list_del (&dev->dev_list);
mutex_unlock (&usbnet_mutex);
// assuming we used keventd, it must quiesce too
flush_scheduled_work ();
kfree (dev); kfree (dev);
usb_put_dev (xdev); usb_put_dev (xdev);
...@@ -2142,20 +2495,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) ...@@ -2142,20 +2495,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
struct usb_host_interface *interface; struct usb_host_interface *interface;
struct driver_info *info; struct driver_info *info;
struct usb_device *xdev; struct usb_device *xdev;
int status;
info = (struct driver_info *) prod->driver_info; info = (struct driver_info *) prod->driver_info;
xdev = interface_to_usbdev (udev); xdev = interface_to_usbdev (udev);
interface = &udev->altsetting [udev->act_altsetting]; interface = &udev->altsetting [udev->act_altsetting];
if (!(info->flags & FLAG_NO_SETINT)) {
if (usb_set_interface (xdev, interface->desc.bInterfaceNumber,
interface->desc.bAlternateSetting) < 0) {
err ("set_interface failed");
return -EIO;
}
}
// set up our own records // set up our own records
if (!(dev = kmalloc (sizeof *dev, GFP_KERNEL))) { if (!(dev = kmalloc (sizeof *dev, GFP_KERNEL))) {
dbg ("can't kmalloc dev"); dbg ("can't kmalloc dev");
...@@ -2168,13 +2513,15 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) ...@@ -2168,13 +2513,15 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
dev->udev = xdev; dev->udev = xdev;
dev->driver_info = info; dev->driver_info = info;
dev->msg_level = msg_level; dev->msg_level = msg_level;
INIT_LIST_HEAD (&dev->dev_list);
skb_queue_head_init (&dev->rxq); skb_queue_head_init (&dev->rxq);
skb_queue_head_init (&dev->txq); skb_queue_head_init (&dev->txq);
skb_queue_head_init (&dev->done); skb_queue_head_init (&dev->done);
dev->bh.func = usbnet_bh; dev->bh.func = usbnet_bh;
dev->bh.data = (unsigned long) dev; dev->bh.data = (unsigned long) dev;
INIT_WORK (&dev->kevent, kevent, dev); INIT_WORK (&dev->kevent, kevent, dev);
dev->delay.function = usbnet_bh;
dev->delay.data = (unsigned long) dev;
init_timer (&dev->delay);
// set up network interface records // set up network interface records
net = &dev->net; net = &dev->net;
...@@ -2200,31 +2547,41 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) ...@@ -2200,31 +2547,41 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
net->tx_timeout = usbnet_tx_timeout; net->tx_timeout = usbnet_tx_timeout;
net->do_ioctl = usbnet_ioctl; net->do_ioctl = usbnet_ioctl;
// allow device-specific bind/init procedures
// NOTE net->name still not usable ...
if (info->bind)
status = info->bind (dev, udev);
else if (!info->in || info->out)
status = get_endpoints (dev, udev);
else {
dev->in = usb_rcvbulkpipe (xdev, info->in);
dev->out = usb_sndbulkpipe (xdev, info->out);
if (!(info->flags & FLAG_NO_SETINT))
status = usb_set_interface (xdev,
interface->desc.bInterfaceNumber,
interface->desc.bAlternateSetting);
else
status = 0;
}
if (status < 0) {
kfree (dev);
return status;
}
dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
register_netdev (&dev->net); register_netdev (&dev->net);
devinfo (dev, "register usbnet usb-%s-%s, %s", devinfo (dev, "register usbnet at usb-%s-%s, %s",
xdev->bus->bus_name, xdev->devpath, xdev->bus->bus_name, xdev->devpath,
dev->driver_info->description); dev->driver_info->description);
#ifdef CONFIG_USB_ZAURUS
if (dev->driver_info == &zaurus_sl5x00_info) {
int status;
status = usb_set_configuration (xdev, 1);
devinfo (dev, "set config --> %d", status);
status = usb_set_interface (xdev, 1, 1);
devinfo (dev, "set altsetting --> %d", status);
}
#endif
// ok, it's ready to go. // ok, it's ready to go.
usb_set_intfdata(udev, dev); usb_set_intfdata (udev, dev);
mutex_lock (&usbnet_mutex);
list_add (&dev->dev_list, &usbnet_list);
mutex_unlock (&dev->mutex); mutex_unlock (&dev->mutex);
// start as if the link is up // start as if the link is up
netif_device_attach (&dev->net); netif_device_attach (&dev->net);
mutex_unlock (&usbnet_mutex);
return 0; return 0;
} }
...@@ -2298,28 +2655,19 @@ static const struct usb_device_id products [] = { ...@@ -2298,28 +2655,19 @@ static const struct usb_device_id products [] = {
}, },
#endif #endif
#ifdef CONFIG_USB_PXA #ifdef CONFIG_USB_ARMLINUX
/*
* PXA250 or PXA210 ... these use a "usb-eth" driver much like
* the sa1100 one.
*/
{
// Compaq "Itsy" vendor/product id, version "2.0"
USB_DEVICE_VER (0x049F, 0x505A, 0x0200, 0x0200),
.driver_info = (unsigned long) &pxa_info,
},
#endif
#ifdef CONFIG_USB_SA1100
/* /*
* SA-1100 using standard ARM Linux kernels, or compatible. * SA-1100 using standard ARM Linux kernels, or compatible.
* Often used when talking to Linux PDAs (iPaq, Yopy, etc). * Often used when talking to Linux PDAs (iPaq, Yopy, etc).
* The sa-1100 "usb-eth" driver handles the basic framing. * The sa-1100 "usb-eth" driver handles the basic framing.
*
* PXA25x or PXA210 ... these use a "usb-eth" driver much like
* the sa1100 one, but hardware uses different endpoint numbers.
*/ */
{ {
// 1183 = 0x049F, both used as hex values? // 1183 = 0x049F, both used as hex values?
// Compaq "Itsy" vendor/product id, version "0.0" // Compaq "Itsy" vendor/product id
USB_DEVICE_VER (0x049F, 0x505A, 0, 0), USB_DEVICE (0x049F, 0x505A),
.driver_info = (unsigned long) &linuxdev_info, .driver_info = (unsigned long) &linuxdev_info,
}, { }, {
USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy" USB_DEVICE (0x0E7E, 0x1001), // G.Mate "Yopy"
...@@ -2337,9 +2685,10 @@ static const struct usb_device_id products [] = { ...@@ -2337,9 +2685,10 @@ static const struct usb_device_id products [] = {
| USB_DEVICE_ID_MATCH_DEVICE, | USB_DEVICE_ID_MATCH_DEVICE,
.idVendor = 0x04DD, .idVendor = 0x04DD,
.idProduct = 0x8004, .idProduct = 0x8004,
.bInterfaceClass = 0x0a, /* match the master interface */
.bInterfaceSubClass = 0x00, .bInterfaceClass = USB_CLASS_COMM,
.bInterfaceProtocol = 0x00, .bInterfaceSubClass = 6 /* Ethernet model */,
.bInterfaceProtocol = 0,
.driver_info = (unsigned long) &zaurus_sl5x00_info, .driver_info = (unsigned long) &zaurus_sl5x00_info,
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
...@@ -2360,6 +2709,24 @@ static const struct usb_device_id products [] = { ...@@ -2360,6 +2709,24 @@ static const struct usb_device_id products [] = {
.bInterfaceProtocol = 0x00, .bInterfaceProtocol = 0x00,
.driver_info = (unsigned long) &zaurus_slb500_info, .driver_info = (unsigned long) &zaurus_slb500_info,
}, },
#endif
#ifdef CONFIG_USB_CDCETHER
{
/* CDC Ether uses two interfaces, not necessarily consecutive.
* We match the main interface, ignoring the optional device
* class so we could handle devices that aren't exclusively
* CDC ether.
*
* NOTE: this match must come AFTER entries working around
* bugs/quirks in a given product (like Zaurus, above).
*/
.match_flags = USB_DEVICE_ID_MATCH_INT_INFO,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = 6 /* Ethernet model */,
.bInterfaceProtocol = 0,
.driver_info = (unsigned long) &cdc_info,
},
#endif #endif
{ }, // END { }, // END
......
...@@ -411,19 +411,19 @@ static const struct keyspan_device_details usa49w_device_details = { ...@@ -411,19 +411,19 @@ static const struct keyspan_device_details usa49w_device_details = {
}; };
static const struct keyspan_device_details usa49wlc_device_details = { static const struct keyspan_device_details usa49wlc_device_details = {
product_id: keyspan_usa49wlc_product_id, .product_id = keyspan_usa49wlc_product_id,
msg_format: msg_usa49, .msg_format = msg_usa49,
num_ports: 4, .num_ports = 4,
indat_endp_flip: 0, .indat_endp_flip = 0,
outdat_endp_flip: 0, .outdat_endp_flip = 0,
indat_endpoints: {0x81, 0x82, 0x83, 0x84}, .indat_endpoints = {0x81, 0x82, 0x83, 0x84},
outdat_endpoints: {0x01, 0x02, 0x03, 0x04}, .outdat_endpoints = {0x01, 0x02, 0x03, 0x04},
inack_endpoints: {-1, -1, -1, -1}, .inack_endpoints = {-1, -1, -1, -1},
outcont_endpoints: {-1, -1, -1, -1}, .outcont_endpoints = {-1, -1, -1, -1},
instat_endpoint: 0x87, .instat_endpoint = 0x87,
glocont_endpoint: 0x07, .glocont_endpoint = 0x07,
calculate_baud_rate: keyspan_usa19w_calc_baud, .calculate_baud_rate = keyspan_usa19w_calc_baud,
baudclk: KEYSPAN_USA19W_BAUDCLK, .baudclk = KEYSPAN_USA19W_BAUDCLK,
}; };
static const struct keyspan_device_details *keyspan_devices[] = { static const struct keyspan_device_details *keyspan_devices[] = {
......
...@@ -141,13 +141,19 @@ static int usb_storage_release(struct Scsi_Host *psh) ...@@ -141,13 +141,19 @@ static int usb_storage_release(struct Scsi_Host *psh)
static int usb_storage_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) static int usb_storage_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
{ {
struct us_data *us = (struct us_data *)srb->device->host->hostdata[0]; struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
int state = atomic_read(&us->sm_state);
US_DEBUGP("queuecommand() called\n"); US_DEBUGP("queuecommand() called\n");
srb->host_scribble = (unsigned char *)us; srb->host_scribble = (unsigned char *)us;
/* enqueue the command */ /* enqueue the command */
BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE); if (state != US_STATE_IDLE || us->srb != NULL) {
BUG_ON(us->srb != NULL); printk(KERN_ERR USB_STORAGE "Error in %s: "
"state = %d, us->srb = %p\n",
__FUNCTION__, state, us->srb);
return SCSI_MLQUEUE_HOST_BUSY;
}
srb->scsi_done = done; srb->scsi_done = done;
us->srb = srb; us->srb = srb;
...@@ -175,8 +181,7 @@ static int usb_storage_command_abort( Scsi_Cmnd *srb ) ...@@ -175,8 +181,7 @@ static int usb_storage_command_abort( Scsi_Cmnd *srb )
return FAILED; return FAILED;
} }
usb_stor_abort_transport(us); return usb_stor_abort_transport(us);
return SUCCESS;
} }
/* This invokes the transport reset mechanism to reset the state of the /* This invokes the transport reset mechanism to reset the state of the
...@@ -185,10 +190,15 @@ static int usb_storage_command_abort( Scsi_Cmnd *srb ) ...@@ -185,10 +190,15 @@ static int usb_storage_command_abort( Scsi_Cmnd *srb )
static int usb_storage_device_reset( Scsi_Cmnd *srb ) static int usb_storage_device_reset( Scsi_Cmnd *srb )
{ {
struct us_data *us = (struct us_data *)srb->device->host->hostdata[0]; struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
int state = atomic_read(&us->sm_state);
int result; int result;
US_DEBUGP("device_reset() called\n" ); US_DEBUGP("device_reset() called\n" );
BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE); if (state != US_STATE_IDLE) {
printk(KERN_ERR USB_STORAGE "Error in %s: "
"invalid state %d\n", __FUNCTION__, state);
return FAILED;
}
/* set the state and release the lock */ /* set the state and release the lock */
atomic_set(&us->sm_state, US_STATE_RESETTING); atomic_set(&us->sm_state, US_STATE_RESETTING);
...@@ -260,6 +270,7 @@ static int usb_storage_proc_info (char *buffer, char **start, off_t offset, ...@@ -260,6 +270,7 @@ static int usb_storage_proc_info (char *buffer, char **start, off_t offset,
struct us_data *us; struct us_data *us;
char *pos = buffer; char *pos = buffer;
struct Scsi_Host *hostptr; struct Scsi_Host *hostptr;
unsigned long f;
/* if someone is sending us data, just throw it away */ /* if someone is sending us data, just throw it away */
if (inout) if (inout)
...@@ -274,6 +285,7 @@ static int usb_storage_proc_info (char *buffer, char **start, off_t offset, ...@@ -274,6 +285,7 @@ static int usb_storage_proc_info (char *buffer, char **start, off_t offset,
/* if we couldn't find it, we return an error */ /* if we couldn't find it, we return an error */
if (!us) { if (!us) {
scsi_host_put(hostptr);
return -ESRCH; return -ESRCH;
} }
...@@ -289,6 +301,24 @@ static int usb_storage_proc_info (char *buffer, char **start, off_t offset, ...@@ -289,6 +301,24 @@ static int usb_storage_proc_info (char *buffer, char **start, off_t offset,
SPRINTF(" Protocol: %s\n", us->protocol_name); SPRINTF(" Protocol: %s\n", us->protocol_name);
SPRINTF(" Transport: %s\n", us->transport_name); SPRINTF(" Transport: %s\n", us->transport_name);
/* show the device flags */
if (pos < buffer + length) {
pos += sprintf(pos, " Quirks:");
f = us->flags;
#define DO_FLAG(a) if (f & US_FL_##a) pos += sprintf(pos, " " #a)
DO_FLAG(SINGLE_LUN);
DO_FLAG(MODE_XLATE);
DO_FLAG(START_STOP);
DO_FLAG(IGNORE_SER);
DO_FLAG(SCM_MULT_TARG);
DO_FLAG(FIX_INQUIRY);
DO_FLAG(FIX_CAPACITY);
#undef DO_FLAG
*(pos++) = '\n';
}
/* release the reference count on this host */ /* release the reference count on this host */
scsi_host_put(hostptr); scsi_host_put(hostptr);
......
...@@ -297,10 +297,11 @@ static int interpret_urb_result(struct us_data *us, unsigned int pipe, ...@@ -297,10 +297,11 @@ static int interpret_urb_result(struct us_data *us, unsigned int pipe,
/* stalled */ /* stalled */
case -EPIPE: case -EPIPE:
/* for control endpoints, a stall indicates a protocol error */ /* for control endpoints, (used by CB[I]) a stall indicates
* a failed command */
if (usb_pipecontrol(pipe)) { if (usb_pipecontrol(pipe)) {
US_DEBUGP("-- stall on control pipe\n"); US_DEBUGP("-- stall on control pipe\n");
return USB_STOR_XFER_ERROR; return USB_STOR_XFER_STALLED;
} }
/* for other sorts of endpoint, clear the stall */ /* for other sorts of endpoint, clear the stall */
...@@ -691,7 +692,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -691,7 +692,7 @@ void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
/* Abort the currently running scsi command or device reset. /* Abort the currently running scsi command or device reset.
* This must be called with scsi_lock(us->srb->host) held */ * This must be called with scsi_lock(us->srb->host) held */
void usb_stor_abort_transport(struct us_data *us) int usb_stor_abort_transport(struct us_data *us)
{ {
struct Scsi_Host *host; struct Scsi_Host *host;
int state = atomic_read(&us->sm_state); int state = atomic_read(&us->sm_state);
...@@ -701,7 +702,11 @@ void usb_stor_abort_transport(struct us_data *us) ...@@ -701,7 +702,11 @@ void usb_stor_abort_transport(struct us_data *us)
/* Normally the current state is RUNNING. If the control thread /* Normally the current state is RUNNING. If the control thread
* hasn't even started processing this command, the state will be * hasn't even started processing this command, the state will be
* IDLE. Anything else is a bug. */ * IDLE. Anything else is a bug. */
BUG_ON((state != US_STATE_RUNNING && state != US_STATE_IDLE)); if (state != US_STATE_RUNNING && state != US_STATE_IDLE) {
printk(KERN_ERR USB_STORAGE "Error in %s: "
"invalid state %d\n", __FUNCTION__, state);
return FAILED;
}
/* set state to abort and release the lock */ /* set state to abort and release the lock */
atomic_set(&us->sm_state, US_STATE_ABORTING); atomic_set(&us->sm_state, US_STATE_ABORTING);
...@@ -730,6 +735,7 @@ void usb_stor_abort_transport(struct us_data *us) ...@@ -730,6 +735,7 @@ void usb_stor_abort_transport(struct us_data *us)
/* Reacquire the lock: note that us->srb is now NULL */ /* Reacquire the lock: note that us->srb is now NULL */
scsi_lock(host); scsi_lock(host);
return SUCCESS;
} }
/* /*
...@@ -750,8 +756,14 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -750,8 +756,14 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
/* check the return code for the command */ /* check the return code for the command */
US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result); US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result);
if (result != USB_STOR_XFER_GOOD) {
/* if we stalled the command, it means command failed */
if (result == USB_STOR_XFER_STALLED) {
return USB_STOR_TRANSPORT_FAILED;
}
/* Uh oh... serious problem here */ /* Uh oh... serious problem here */
if (result != USB_STOR_XFER_GOOD) {
return USB_STOR_TRANSPORT_ERROR; return USB_STOR_TRANSPORT_ERROR;
} }
...@@ -834,8 +846,14 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us) ...@@ -834,8 +846,14 @@ int usb_stor_CB_transport(Scsi_Cmnd *srb, struct us_data *us)
/* check the return code for the command */ /* check the return code for the command */
US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result); US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result);
if (result != USB_STOR_XFER_GOOD) {
/* if we stalled the command, it means command failed */
if (result == USB_STOR_XFER_STALLED) {
return USB_STOR_TRANSPORT_FAILED;
}
/* Uh oh... serious problem here */ /* Uh oh... serious problem here */
if (result != USB_STOR_XFER_GOOD) {
return USB_STOR_TRANSPORT_ERROR; return USB_STOR_TRANSPORT_ERROR;
} }
......
...@@ -154,7 +154,7 @@ extern int usb_stor_Bulk_max_lun(struct us_data*); ...@@ -154,7 +154,7 @@ extern int usb_stor_Bulk_max_lun(struct us_data*);
extern int usb_stor_Bulk_reset(struct us_data*); extern int usb_stor_Bulk_reset(struct us_data*);
extern void usb_stor_invoke_transport(Scsi_Cmnd*, struct us_data*); extern void usb_stor_invoke_transport(Scsi_Cmnd*, struct us_data*);
extern void usb_stor_abort_transport(struct us_data*); extern int usb_stor_abort_transport(struct us_data*);
extern int usb_stor_bulk_msg(struct us_data *us, void *data, extern int usb_stor_bulk_msg(struct us_data *us, void *data,
unsigned int pipe, unsigned int len, unsigned int *act_len); unsigned int pipe, unsigned int len, unsigned int *act_len);
......
...@@ -430,39 +430,39 @@ static int usb_stor_control_thread(void * __us) ...@@ -430,39 +430,39 @@ static int usb_stor_control_thread(void * __us)
} }
/* Set up the URB and the usb_ctrlrequest. /* Set up the URB and the usb_ctrlrequest.
* ss->dev_semaphore must already be locked. * us->dev_semaphore must already be locked.
* Note that this function assumes that all the data in the us_data * Note that this function assumes that all the data in the us_data
* structure is current. * structure is current.
* Returns non-zero on failure, zero on success * Returns non-zero on failure, zero on success
*/ */
static int usb_stor_allocate_urbs(struct us_data *ss) static int usb_stor_allocate_urbs(struct us_data *us)
{ {
/* calculate and store the pipe values */ /* calculate and store the pipe values */
ss->send_ctrl_pipe = usb_sndctrlpipe(ss->pusb_dev, 0); us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);
ss->recv_ctrl_pipe = usb_rcvctrlpipe(ss->pusb_dev, 0); us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
ss->send_bulk_pipe = usb_sndbulkpipe(ss->pusb_dev, ss->ep_out); us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
ss->recv_bulk_pipe = usb_rcvbulkpipe(ss->pusb_dev, ss->ep_in); us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
ss->recv_intr_pipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int); us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev, us->ep_int);
/* allocate the usb_ctrlrequest for control packets */ /* allocate the usb_ctrlrequest for control packets */
US_DEBUGP("Allocating usb_ctrlrequest\n"); US_DEBUGP("Allocating usb_ctrlrequest\n");
ss->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); us->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
if (!ss->dr) { if (!us->dr) {
US_DEBUGP("allocation failed\n"); US_DEBUGP("allocation failed\n");
return 1; return 1;
} }
/* allocate the URB we're going to use */ /* allocate the URB we're going to use */
US_DEBUGP("Allocating URB\n"); US_DEBUGP("Allocating URB\n");
ss->current_urb = usb_alloc_urb(0, GFP_KERNEL); us->current_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!ss->current_urb) { if (!us->current_urb) {
US_DEBUGP("allocation failed\n"); US_DEBUGP("allocation failed\n");
return 2; return 2;
} }
US_DEBUGP("Allocating scatter-gather request block\n"); US_DEBUGP("Allocating scatter-gather request block\n");
ss->current_sg = kmalloc(sizeof(*ss->current_sg), GFP_KERNEL); us->current_sg = kmalloc(sizeof(*us->current_sg), GFP_KERNEL);
if (!ss->current_sg) { if (!us->current_sg) {
US_DEBUGP("allocation failed\n"); US_DEBUGP("allocation failed\n");
return 5; return 5;
} }
...@@ -471,32 +471,32 @@ static int usb_stor_allocate_urbs(struct us_data *ss) ...@@ -471,32 +471,32 @@ static int usb_stor_allocate_urbs(struct us_data *ss)
} }
/* Deallocate the URB, the usb_ctrlrequest, and the IRQ pipe. /* Deallocate the URB, the usb_ctrlrequest, and the IRQ pipe.
* ss->dev_semaphore must already be locked. * us->dev_semaphore must already be locked.
*/ */
static void usb_stor_deallocate_urbs(struct us_data *ss) static void usb_stor_deallocate_urbs(struct us_data *us)
{ {
/* free the scatter-gather request block */ /* free the scatter-gather request block */
if (ss->current_sg) { if (us->current_sg) {
kfree(ss->current_sg); kfree(us->current_sg);
ss->current_sg = NULL; us->current_sg = NULL;
} }
/* free up the main URB for this device */ /* free up the main URB for this device */
if (ss->current_urb) { if (us->current_urb) {
US_DEBUGP("-- releasing main URB\n"); US_DEBUGP("-- releasing main URB\n");
usb_free_urb(ss->current_urb); usb_free_urb(us->current_urb);
ss->current_urb = NULL; us->current_urb = NULL;
} }
/* free the usb_ctrlrequest buffer */ /* free the usb_ctrlrequest buffer */
if (ss->dr) { if (us->dr) {
kfree(ss->dr); kfree(us->dr);
ss->dr = NULL; us->dr = NULL;
} }
/* mark the device as gone */ /* mark the device as gone */
usb_put_dev(ss->pusb_dev); usb_put_dev(us->pusb_dev);
ss->pusb_dev = NULL; us->pusb_dev = NULL;
} }
/* Probe to see if a new device is actually a SCSI device */ /* Probe to see if a new device is actually a SCSI device */
...@@ -512,7 +512,7 @@ static int storage_probe(struct usb_interface *intf, ...@@ -512,7 +512,7 @@ static int storage_probe(struct usb_interface *intf,
char serial[USB_STOR_STRING_LEN]; /* serial number */ char serial[USB_STOR_STRING_LEN]; /* serial number */
unsigned int flags; unsigned int flags;
struct us_unusual_dev *unusual_dev; struct us_unusual_dev *unusual_dev;
struct us_data *ss = NULL; struct us_data *us = NULL;
int result; int result;
/* these are temporary copies -- we test on these, then put them /* these are temporary copies -- we test on these, then put them
...@@ -633,212 +633,212 @@ static int storage_probe(struct usb_interface *intf, ...@@ -633,212 +633,212 @@ static int storage_probe(struct usb_interface *intf,
serial, sizeof(serial)); serial, sizeof(serial));
/* New device -- allocate memory and initialize */ /* New device -- allocate memory and initialize */
if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data), if ((us = (struct us_data *)kmalloc(sizeof(struct us_data),
GFP_KERNEL)) == NULL) { GFP_KERNEL)) == NULL) {
printk(KERN_WARNING USB_STORAGE "Out of memory\n"); printk(KERN_WARNING USB_STORAGE "Out of memory\n");
usb_put_dev(dev); usb_put_dev(dev);
return -ENOMEM; return -ENOMEM;
} }
memset(ss, 0, sizeof(struct us_data)); memset(us, 0, sizeof(struct us_data));
/* Initialize the mutexes only when the struct is new */ /* Initialize the mutexes only when the struct is new */
init_completion(&(ss->notify)); init_completion(&(us->notify));
init_MUTEX_LOCKED(&(ss->dev_semaphore)); init_MUTEX_LOCKED(&(us->dev_semaphore));
/* copy over the subclass and protocol data */ /* copy over the subclass and protocol data */
ss->subclass = subclass; us->subclass = subclass;
ss->protocol = protocol; us->protocol = protocol;
ss->flags = flags; us->flags = flags;
ss->unusual_dev = unusual_dev; us->unusual_dev = unusual_dev;
/* copy over the endpoint data */ /* copy over the endpoint data */
ss->ep_in = ep_in->bEndpointAddress & us->ep_in = ep_in->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK; USB_ENDPOINT_NUMBER_MASK;
ss->ep_out = ep_out->bEndpointAddress & us->ep_out = ep_out->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK; USB_ENDPOINT_NUMBER_MASK;
if (ep_int) { if (ep_int) {
ss->ep_int = ep_int->bEndpointAddress & us->ep_int = ep_int->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK; USB_ENDPOINT_NUMBER_MASK;
ss->ep_bInterval = ep_int->bInterval; us->ep_bInterval = ep_int->bInterval;
} }
else else
ss->ep_int = ss->ep_bInterval = 0; us->ep_int = us->ep_bInterval = 0;
/* establish the connection to the new device */ /* establish the connection to the new device */
ss->ifnum = ifnum; us->ifnum = ifnum;
ss->pusb_dev = dev; us->pusb_dev = dev;
/* copy over the identifiying strings */ /* copy over the identifiying strings */
strncpy(ss->vendor, mf, USB_STOR_STRING_LEN); strncpy(us->vendor, mf, USB_STOR_STRING_LEN);
strncpy(ss->product, prod, USB_STOR_STRING_LEN); strncpy(us->product, prod, USB_STOR_STRING_LEN);
strncpy(ss->serial, serial, USB_STOR_STRING_LEN); strncpy(us->serial, serial, USB_STOR_STRING_LEN);
if (strlen(ss->vendor) == 0) { if (strlen(us->vendor) == 0) {
if (unusual_dev->vendorName) if (unusual_dev->vendorName)
strncpy(ss->vendor, unusual_dev->vendorName, strncpy(us->vendor, unusual_dev->vendorName,
USB_STOR_STRING_LEN); USB_STOR_STRING_LEN);
else else
strncpy(ss->vendor, "Unknown", strncpy(us->vendor, "Unknown",
USB_STOR_STRING_LEN); USB_STOR_STRING_LEN);
} }
if (strlen(ss->product) == 0) { if (strlen(us->product) == 0) {
if (unusual_dev->productName) if (unusual_dev->productName)
strncpy(ss->product, unusual_dev->productName, strncpy(us->product, unusual_dev->productName,
USB_STOR_STRING_LEN); USB_STOR_STRING_LEN);
else else
strncpy(ss->product, "Unknown", strncpy(us->product, "Unknown",
USB_STOR_STRING_LEN); USB_STOR_STRING_LEN);
} }
if (strlen(ss->serial) == 0) if (strlen(us->serial) == 0)
strncpy(ss->serial, "None", USB_STOR_STRING_LEN); strncpy(us->serial, "None", USB_STOR_STRING_LEN);
/* /*
* Set the handler pointers based on the protocol * Set the handler pointers based on the protocol
* Again, this data is persistent across reattachments * Again, this data is persistent across reattachments
*/ */
switch (ss->protocol) { switch (us->protocol) {
case US_PR_CB: case US_PR_CB:
ss->transport_name = "Control/Bulk"; us->transport_name = "Control/Bulk";
ss->transport = usb_stor_CB_transport; us->transport = usb_stor_CB_transport;
ss->transport_reset = usb_stor_CB_reset; us->transport_reset = usb_stor_CB_reset;
ss->max_lun = 7; us->max_lun = 7;
break; break;
case US_PR_CBI: case US_PR_CBI:
ss->transport_name = "Control/Bulk/Interrupt"; us->transport_name = "Control/Bulk/Interrupt";
ss->transport = usb_stor_CBI_transport; us->transport = usb_stor_CBI_transport;
ss->transport_reset = usb_stor_CB_reset; us->transport_reset = usb_stor_CB_reset;
ss->max_lun = 7; us->max_lun = 7;
break; break;
case US_PR_BULK: case US_PR_BULK:
ss->transport_name = "Bulk"; us->transport_name = "Bulk";
ss->transport = usb_stor_Bulk_transport; us->transport = usb_stor_Bulk_transport;
ss->transport_reset = usb_stor_Bulk_reset; us->transport_reset = usb_stor_Bulk_reset;
ss->max_lun = usb_stor_Bulk_max_lun(ss); us->max_lun = usb_stor_Bulk_max_lun(us);
break; break;
#ifdef CONFIG_USB_STORAGE_HP8200e #ifdef CONFIG_USB_STORAGE_HP8200e
case US_PR_SCM_ATAPI: case US_PR_SCM_ATAPI:
ss->transport_name = "SCM/ATAPI"; us->transport_name = "SCM/ATAPI";
ss->transport = hp8200e_transport; us->transport = hp8200e_transport;
ss->transport_reset = usb_stor_CB_reset; us->transport_reset = usb_stor_CB_reset;
ss->max_lun = 1; us->max_lun = 1;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_SDDR09 #ifdef CONFIG_USB_STORAGE_SDDR09
case US_PR_EUSB_SDDR09: case US_PR_EUSB_SDDR09:
ss->transport_name = "EUSB/SDDR09"; us->transport_name = "EUSB/SDDR09";
ss->transport = sddr09_transport; us->transport = sddr09_transport;
ss->transport_reset = usb_stor_CB_reset; us->transport_reset = usb_stor_CB_reset;
ss->max_lun = 0; us->max_lun = 0;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_SDDR55 #ifdef CONFIG_USB_STORAGE_SDDR55
case US_PR_SDDR55: case US_PR_SDDR55:
ss->transport_name = "SDDR55"; us->transport_name = "SDDR55";
ss->transport = sddr55_transport; us->transport = sddr55_transport;
ss->transport_reset = sddr55_reset; us->transport_reset = sddr55_reset;
ss->max_lun = 0; us->max_lun = 0;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_DPCM #ifdef CONFIG_USB_STORAGE_DPCM
case US_PR_DPCM_USB: case US_PR_DPCM_USB:
ss->transport_name = "Control/Bulk-EUSB/SDDR09"; us->transport_name = "Control/Bulk-EUSB/SDDR09";
ss->transport = dpcm_transport; us->transport = dpcm_transport;
ss->transport_reset = usb_stor_CB_reset; us->transport_reset = usb_stor_CB_reset;
ss->max_lun = 1; us->max_lun = 1;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_FREECOM #ifdef CONFIG_USB_STORAGE_FREECOM
case US_PR_FREECOM: case US_PR_FREECOM:
ss->transport_name = "Freecom"; us->transport_name = "Freecom";
ss->transport = freecom_transport; us->transport = freecom_transport;
ss->transport_reset = usb_stor_freecom_reset; us->transport_reset = usb_stor_freecom_reset;
ss->max_lun = 0; us->max_lun = 0;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_DATAFAB #ifdef CONFIG_USB_STORAGE_DATAFAB
case US_PR_DATAFAB: case US_PR_DATAFAB:
ss->transport_name = "Datafab Bulk-Only"; us->transport_name = "Datafab Bulk-Only";
ss->transport = datafab_transport; us->transport = datafab_transport;
ss->transport_reset = usb_stor_Bulk_reset; us->transport_reset = usb_stor_Bulk_reset;
ss->max_lun = 1; us->max_lun = 1;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_JUMPSHOT #ifdef CONFIG_USB_STORAGE_JUMPSHOT
case US_PR_JUMPSHOT: case US_PR_JUMPSHOT:
ss->transport_name = "Lexar Jumpshot Control/Bulk"; us->transport_name = "Lexar Jumpshot Control/Bulk";
ss->transport = jumpshot_transport; us->transport = jumpshot_transport;
ss->transport_reset = usb_stor_Bulk_reset; us->transport_reset = usb_stor_Bulk_reset;
ss->max_lun = 1; us->max_lun = 1;
break; break;
#endif #endif
default: default:
/* ss->transport_name = "Unknown"; */ /* us->transport_name = "Unknown"; */
goto BadDevice; goto BadDevice;
} }
US_DEBUGP("Transport: %s\n", ss->transport_name); US_DEBUGP("Transport: %s\n", us->transport_name);
/* fix for single-lun devices */ /* fix for single-lun devices */
if (ss->flags & US_FL_SINGLE_LUN) if (us->flags & US_FL_SINGLE_LUN)
ss->max_lun = 0; us->max_lun = 0;
switch (ss->subclass) { switch (us->subclass) {
case US_SC_RBC: case US_SC_RBC:
ss->protocol_name = "Reduced Block Commands (RBC)"; us->protocol_name = "Reduced Block Commands (RBC)";
ss->proto_handler = usb_stor_transparent_scsi_command; us->proto_handler = usb_stor_transparent_scsi_command;
break; break;
case US_SC_8020: case US_SC_8020:
ss->protocol_name = "8020i"; us->protocol_name = "8020i";
ss->proto_handler = usb_stor_ATAPI_command; us->proto_handler = usb_stor_ATAPI_command;
ss->max_lun = 0; us->max_lun = 0;
break; break;
case US_SC_QIC: case US_SC_QIC:
ss->protocol_name = "QIC-157"; us->protocol_name = "QIC-157";
ss->proto_handler = usb_stor_qic157_command; us->proto_handler = usb_stor_qic157_command;
ss->max_lun = 0; us->max_lun = 0;
break; break;
case US_SC_8070: case US_SC_8070:
ss->protocol_name = "8070i"; us->protocol_name = "8070i";
ss->proto_handler = usb_stor_ATAPI_command; us->proto_handler = usb_stor_ATAPI_command;
ss->max_lun = 0; us->max_lun = 0;
break; break;
case US_SC_SCSI: case US_SC_SCSI:
ss->protocol_name = "Transparent SCSI"; us->protocol_name = "Transparent SCSI";
ss->proto_handler = usb_stor_transparent_scsi_command; us->proto_handler = usb_stor_transparent_scsi_command;
break; break;
case US_SC_UFI: case US_SC_UFI:
ss->protocol_name = "Uniform Floppy Interface (UFI)"; us->protocol_name = "Uniform Floppy Interface (UFI)";
ss->proto_handler = usb_stor_ufi_command; us->proto_handler = usb_stor_ufi_command;
break; break;
#ifdef CONFIG_USB_STORAGE_ISD200 #ifdef CONFIG_USB_STORAGE_ISD200
case US_SC_ISD200: case US_SC_ISD200:
ss->protocol_name = "ISD200 ATA/ATAPI"; us->protocol_name = "ISD200 ATA/ATAPI";
ss->proto_handler = isd200_ata_command; us->proto_handler = isd200_ata_command;
break; break;
#endif #endif
default: default:
/* ss->protocol_name = "Unknown"; */ /* us->protocol_name = "Unknown"; */
goto BadDevice; goto BadDevice;
} }
US_DEBUGP("Protocol: %s\n", ss->protocol_name); US_DEBUGP("Protocol: %s\n", us->protocol_name);
/* allocate the URB, the usb_ctrlrequest, and the IRQ URB */ /* allocate the URB, the usb_ctrlrequest, and the IRQ URB */
if (usb_stor_allocate_urbs(ss)) if (usb_stor_allocate_urbs(us))
goto BadDevice; goto BadDevice;
/* /*
...@@ -849,59 +849,59 @@ static int storage_probe(struct usb_interface *intf, ...@@ -849,59 +849,59 @@ static int storage_probe(struct usb_interface *intf,
/* Just before we start our control thread, initialize /* Just before we start our control thread, initialize
* the device if it needs initialization */ * the device if it needs initialization */
if (unusual_dev && unusual_dev->initFunction) if (unusual_dev && unusual_dev->initFunction)
unusual_dev->initFunction(ss); unusual_dev->initFunction(us);
/* start up our control thread */ /* start up our control thread */
atomic_set(&ss->sm_state, US_STATE_IDLE); atomic_set(&us->sm_state, US_STATE_IDLE);
ss->pid = kernel_thread(usb_stor_control_thread, ss, us->pid = kernel_thread(usb_stor_control_thread, us,
CLONE_VM); CLONE_VM);
if (ss->pid < 0) { if (us->pid < 0) {
printk(KERN_WARNING USB_STORAGE printk(KERN_WARNING USB_STORAGE
"Unable to start control thread\n"); "Unable to start control thread\n");
goto BadDevice; goto BadDevice;
} }
/* wait for the thread to start */ /* wait for the thread to start */
wait_for_completion(&(ss->notify)); wait_for_completion(&(us->notify));
/* unlock the device pointers */ /* unlock the device pointers */
up(&(ss->dev_semaphore)); up(&(us->dev_semaphore));
/* now register */ /* now register */
ss->host = scsi_register(&usb_stor_host_template, sizeof(ss)); us->host = scsi_register(&usb_stor_host_template, sizeof(us));
if (!ss->host) { if (!us->host) {
printk(KERN_WARNING USB_STORAGE printk(KERN_WARNING USB_STORAGE
"Unable to register the scsi host\n"); "Unable to register the scsi host\n");
/* tell the control thread to exit */ /* tell the control thread to exit */
ss->srb = NULL; us->srb = NULL;
up(&ss->sema); up(&us->sema);
wait_for_completion(&ss->notify); wait_for_completion(&us->notify);
/* re-lock the device pointers */ /* re-lock the device pointers */
down(&ss->dev_semaphore); down(&us->dev_semaphore);
goto BadDevice; goto BadDevice;
} }
/* set the hostdata to prepare for scanning */ /* set the hostdata to prepare for scanning */
ss->host->hostdata[0] = (unsigned long)ss; us->host->hostdata[0] = (unsigned long)us;
/* associate this host with our interface */ /* associate this host with our interface */
scsi_set_device(ss->host, &intf->dev); scsi_set_device(us->host, &intf->dev);
/* now add the host */ /* now add the host */
result = scsi_add_host(ss->host, NULL); result = scsi_add_host(us->host, NULL);
if (result) { if (result) {
printk(KERN_WARNING USB_STORAGE printk(KERN_WARNING USB_STORAGE
"Unable to add the scsi host\n"); "Unable to add the scsi host\n");
/* tell the control thread to exit */ /* tell the control thread to exit */
ss->srb = NULL; us->srb = NULL;
up(&ss->sema); up(&us->sema);
wait_for_completion(&ss->notify); wait_for_completion(&us->notify);
/* re-lock the device pointers */ /* re-lock the device pointers */
down(&ss->dev_semaphore); down(&us->dev_semaphore);
goto BadDevice; goto BadDevice;
} }
...@@ -911,66 +911,61 @@ static int storage_probe(struct usb_interface *intf, ...@@ -911,66 +911,61 @@ static int storage_probe(struct usb_interface *intf,
"USB Mass Storage device found at %d\n", dev->devnum); "USB Mass Storage device found at %d\n", dev->devnum);
/* save a pointer to our structure */ /* save a pointer to our structure */
usb_set_intfdata(intf, ss); usb_set_intfdata(intf, us);
return 0; return 0;
/* we come here if there are any problems */ /* we come here if there are any problems */
/* ss->dev_semaphore must be locked */ /* us->dev_semaphore must be locked */
BadDevice: BadDevice:
US_DEBUGP("storage_probe() failed\n"); US_DEBUGP("storage_probe() failed\n");
usb_stor_deallocate_urbs(ss); usb_stor_deallocate_urbs(us);
up(&ss->dev_semaphore); up(&us->dev_semaphore);
kfree(ss); kfree(us);
return -EIO; return -EIO;
} }
/* Handle a disconnect event from the USB core */ /* Handle a disconnect event from the USB core */
static void storage_disconnect(struct usb_interface *intf) static void storage_disconnect(struct usb_interface *intf)
{ {
struct us_data *ss; struct us_data *us;
struct scsi_device *sdev; struct scsi_device *sdev;
US_DEBUGP("storage_disconnect() called\n"); US_DEBUGP("storage_disconnect() called\n");
ss = usb_get_intfdata(intf); us = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
/* serious error -- we're attempting to disconnect an interface but
* cannot locate the local data structure
*/
BUG_ON(ss == NULL);
/* set devices offline -- need host lock for this */ /* set devices offline -- need host lock for this */
scsi_lock(ss->host); scsi_lock(us->host);
list_for_each_entry(sdev, &ss->host->my_devices, siblings) list_for_each_entry(sdev, &us->host->my_devices, siblings)
sdev->online = 0; sdev->online = 0;
scsi_unlock(ss->host); scsi_unlock(us->host);
/* lock device access -- no need to unlock, as we're going away */ /* lock device access -- no need to unlock, as we're going away */
down(&(ss->dev_semaphore)); down(&(us->dev_semaphore));
/* Complete all pending commands with * cmd->result = DID_ERROR << 16. /* Complete all pending commands with * cmd->result = DID_ERROR << 16.
* Since we only queue one command at a time, this is pretty easy. */ * Since we only queue one command at a time, this is pretty easy. */
if (ss->srb) { if (us->srb) {
ss->srb->result = DID_ERROR << 16; us->srb->result = DID_ERROR << 16;
ss->srb->scsi_done(ss->srb); us->srb->scsi_done(us->srb);
} }
/* TODO: somehow, wait for the device to /* TODO: somehow, wait for the device to
* be 'idle' (tasklet completion) */ * be 'idle' (tasklet completion) */
/* remove the pointer to the data structure we were using */ /* remove the pointer to the data structure we were using */
(struct us_data*)ss->host->hostdata[0] = NULL; (struct us_data*)us->host->hostdata[0] = NULL;
/* begin SCSI host removal sequence */ /* begin SCSI host removal sequence */
if(scsi_remove_host(ss->host)) { if(scsi_remove_host(us->host)) {
US_DEBUGP("-- SCSI refused to unregister\n"); US_DEBUGP("-- SCSI refused to unregister\n");
BUG(); BUG();
return; return;
}; };
/* finish SCSI host removal sequence */ /* finish SCSI host removal sequence */
scsi_unregister(ss->host); scsi_unregister(us->host);
/* Kill the control threads /* Kill the control threads
* *
...@@ -978,34 +973,34 @@ static void storage_disconnect(struct usb_interface *intf) ...@@ -978,34 +973,34 @@ static void storage_disconnect(struct usb_interface *intf)
* notification that it has exited. * notification that it has exited.
*/ */
US_DEBUGP("-- sending exit command to thread\n"); US_DEBUGP("-- sending exit command to thread\n");
BUG_ON(atomic_read(&ss->sm_state) != US_STATE_IDLE); BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE);
ss->srb = NULL; us->srb = NULL;
up(&(ss->sema)); up(&(us->sema));
wait_for_completion(&(ss->notify)); wait_for_completion(&(us->notify));
/* free allocated urbs */ /* free allocated urbs */
usb_stor_deallocate_urbs(ss); usb_stor_deallocate_urbs(us);
/* If there's extra data in the us_data structure then /* If there's extra data in the us_data structure then
* free that first */ * free that first */
if (ss->extra) { if (us->extra) {
/* call the destructor routine, if it exists */ /* call the destructor routine, if it exists */
if (ss->extra_destructor) { if (us->extra_destructor) {
US_DEBUGP("-- calling extra_destructor()\n"); US_DEBUGP("-- calling extra_destructor()\n");
ss->extra_destructor(ss->extra); us->extra_destructor(us->extra);
} }
/* destroy the extra data */ /* destroy the extra data */
US_DEBUGP("-- freeing the data structure\n"); US_DEBUGP("-- freeing the data structure\n");
kfree(ss->extra); kfree(us->extra);
} }
/* up the semaphore so auto-code-checkers won't complain about /* up the semaphore so auto-code-checkers won't complain about
* the down/up imbalance */ * the down/up imbalance */
up(&(ss->dev_semaphore)); up(&(us->dev_semaphore));
/* free the structure itself */ /* free the structure itself */
kfree (ss); kfree (us);
} }
/*********************************************************************** /***********************************************************************
......
...@@ -61,7 +61,7 @@ static struct file_system_type **find_filesystem(const char *name) ...@@ -61,7 +61,7 @@ static struct file_system_type **find_filesystem(const char *name)
/* define fs_subsys */ /* define fs_subsys */
static decl_subsys(fs, NULL); static decl_subsys(fs, NULL, NULL);
static int register_fs_subsys(struct file_system_type * fs) static int register_fs_subsys(struct file_system_type * fs)
{ {
......
...@@ -248,7 +248,7 @@ static struct attribute * default_attrs[] = { ...@@ -248,7 +248,7 @@ static struct attribute * default_attrs[] = {
extern struct subsystem block_subsys; extern struct subsystem block_subsys;
static struct kobj_type ktype_part = { struct kobj_type ktype_part = {
.default_attrs = default_attrs, .default_attrs = default_attrs,
.sysfs_ops = &part_sysfs_ops, .sysfs_ops = &part_sysfs_ops,
}; };
......
...@@ -57,12 +57,24 @@ struct kobj_type { ...@@ -57,12 +57,24 @@ struct kobj_type {
* of object; multiple ksets can belong to one subsystem. All * of object; multiple ksets can belong to one subsystem. All
* ksets of a subsystem share the subsystem's lock. * ksets of a subsystem share the subsystem's lock.
* *
* Each kset can support hotplugging; if it does, it will be given
* the opportunity to filter out specific kobjects from being
* reported, as well as to add its own "data" elements to the
* environment being passed to the hotplug helper.
*/ */
struct kset_hotplug_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
char *(*name)(struct kset *kset, struct kobject *kobj);
int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp,
int num_envp, char *buffer, int buffer_size);
};
struct kset { struct kset {
struct subsystem * subsys; struct subsystem * subsys;
struct kobj_type * ktype; struct kobj_type * ktype;
struct list_head list; struct list_head list;
struct kobject kobj; struct kobject kobj;
struct kset_hotplug_ops * hotplug_ops;
}; };
...@@ -86,6 +98,13 @@ static inline void kset_put(struct kset * k) ...@@ -86,6 +98,13 @@ static inline void kset_put(struct kset * k)
kobject_put(&k->kobj); kobject_put(&k->kobj);
} }
static inline struct kobj_type * get_ktype(struct kobject * k)
{
if (k->kset && k->kset->ktype)
return k->kset->ktype;
else
return k->ktype;
}
extern struct kobject * kset_find_obj(struct kset *, const char *); extern struct kobject * kset_find_obj(struct kset *, const char *);
...@@ -95,11 +114,12 @@ struct subsystem { ...@@ -95,11 +114,12 @@ struct subsystem {
struct rw_semaphore rwsem; struct rw_semaphore rwsem;
}; };
#define decl_subsys(_name,_type) \ #define decl_subsys(_name,_type,_hotplug_ops) \
struct subsystem _name##_subsys = { \ struct subsystem _name##_subsys = { \
.kset = { \ .kset = { \
.kobj = { .name = __stringify(_name) }, \ .kobj = { .name = __stringify(_name) }, \
.ktype = _type, \ .ktype = _type, \
.hotplug_ops =_hotplug_ops, \
} \ } \
} }
......
...@@ -11,14 +11,6 @@ ...@@ -11,14 +11,6 @@
static spinlock_t kobj_lock = SPIN_LOCK_UNLOCKED; static spinlock_t kobj_lock = SPIN_LOCK_UNLOCKED;
static inline struct kobj_type * get_ktype(struct kobject * k)
{
if (k->kset && k->kset->ktype)
return k->kset->ktype;
else
return k->ktype;
}
/** /**
* populate_dir - populate directory with attributes. * populate_dir - populate directory with attributes.
* @kobj: object we're working on. * @kobj: object we're working on.
...@@ -67,6 +59,140 @@ static inline struct kobject * to_kobj(struct list_head * entry) ...@@ -67,6 +59,140 @@ static inline struct kobject * to_kobj(struct list_head * entry)
} }
#ifdef CONFIG_HOTPLUG
static int get_kobj_path_length(struct kset *kset, struct kobject *kobj)
{
int length = 1;
struct kobject * parent = kobj;
/* walk up the ancestors until we hit the one pointing to the
* root.
* Add 1 to strlen for leading '/' of each level.
*/
do {
length += strlen (parent->name) + 1;
parent = parent->parent;
} while (parent);
return length;
}
static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, int length)
{
struct kobject * parent;
--length;
for (parent = kobj; parent; parent = parent->parent) {
int cur = strlen (parent->name);
/* back up enough to print this name with '/' */
length -= cur;
strncpy (path + length, parent->name, cur);
*(path + --length) = '/';
}
pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
}
#define BUFFER_SIZE 1024 /* should be enough memory for the env */
#define NUM_ENVP 32 /* number of env pointers */
static void kset_hotplug(const char *action, struct kset *kset,
struct kobject *kobj)
{
char *argv [3];
char **envp;
char *buffer;
char *scratch;
int i = 0;
int retval;
int kobj_path_length;
char *kobj_path;
char *name = NULL;
/* If the kset has a filter operation, call it. If it returns
failure, no hotplug event is required. */
if (kset->hotplug_ops->filter) {
if (!kset->hotplug_ops->filter(kset, kobj))
return;
}
pr_debug ("%s\n", __FUNCTION__);
if (!hotplug_path[0])
return;
envp = (char **)kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
if (!envp)
return;
memset (envp, 0x00, NUM_ENVP * sizeof (char *));
buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
if (!buffer) {
kfree(envp);
return;
}
if (kset->hotplug_ops->name)
name = kset->hotplug_ops->name(kset, kobj);
if (name == NULL)
name = kset->kobj.name;
argv [0] = hotplug_path;
argv [1] = name;
argv [2] = 0;
/* minimal command environment */
envp [i++] = "HOME=/";
envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
scratch = buffer;
envp [i++] = scratch;
scratch += sprintf(scratch, "ACTION=%s", action) + 1;
kobj_path_length = get_kobj_path_length (kset, kobj);
kobj_path = kmalloc (kobj_path_length, GFP_KERNEL);
if (!kobj_path) {
kfree (buffer);
kfree (envp);
return;
}
memset (kobj_path, 0x00, kobj_path_length);
fill_kobj_path (kset, kobj, kobj_path, kobj_path_length);
envp [i++] = scratch;
scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1;
if (kset->hotplug_ops->hotplug) {
/* have the kset specific function add its stuff */
retval = kset->hotplug_ops->hotplug (kset, kobj,
&envp[i], NUM_ENVP - i, scratch,
BUFFER_SIZE - (scratch - buffer));
if (retval) {
pr_debug ("%s - hotplug() returned %d\n",
__FUNCTION__, retval);
goto exit;
}
}
pr_debug ("%s: %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1],
envp[0], envp[1], envp[2], envp[3]);
retval = call_usermodehelper (argv[0], argv, envp, 0);
if (retval)
pr_debug ("%s - call_usermodehelper returned %d\n",
__FUNCTION__, retval);
exit:
kfree (kobj_path);
kfree (buffer);
return;
}
#else
static void kset_hotplug(const char *action, struct kset *kset,
struct kobject *kobj)
{
return 0;
}
#endif /* CONFIG_HOTPLUG */
/** /**
* kobject_init - initialize object. * kobject_init - initialize object.
* @kobj: object in question. * @kobj: object in question.
...@@ -111,6 +237,7 @@ int kobject_add(struct kobject * kobj) ...@@ -111,6 +237,7 @@ int kobject_add(struct kobject * kobj)
{ {
int error = 0; int error = 0;
struct kobject * parent; struct kobject * parent;
struct kobject * top_kobj;
if (!(kobj = kobject_get(kobj))) if (!(kobj = kobject_get(kobj)))
return -ENOENT; return -ENOENT;
...@@ -134,6 +261,19 @@ int kobject_add(struct kobject * kobj) ...@@ -134,6 +261,19 @@ int kobject_add(struct kobject * kobj)
error = create_dir(kobj); error = create_dir(kobj);
if (error) if (error)
unlink(kobj); unlink(kobj);
else {
/* If this kobj does not belong to a kset,
try to find a parent that does. */
top_kobj = kobj;
if (!top_kobj->kset && top_kobj->parent) {
do {
top_kobj = top_kobj->parent;
} while (!top_kobj->kset && top_kobj->parent);
}
if (top_kobj->kset && top_kobj->kset->hotplug_ops)
kset_hotplug("add", top_kobj->kset, kobj);
}
return error; return error;
} }
...@@ -162,6 +302,20 @@ int kobject_register(struct kobject * kobj) ...@@ -162,6 +302,20 @@ int kobject_register(struct kobject * kobj)
void kobject_del(struct kobject * kobj) void kobject_del(struct kobject * kobj)
{ {
struct kobject * top_kobj;
/* If this kobj does not belong to a kset,
try to find a parent that does. */
top_kobj = kobj;
if (!top_kobj->kset && top_kobj->parent) {
do {
top_kobj = top_kobj->parent;
} while (!top_kobj->kset && top_kobj->parent);
}
if (top_kobj->kset && top_kobj->kset->hotplug_ops)
kset_hotplug("remove", top_kobj->kset, kobj);
sysfs_remove_dir(kobj); sysfs_remove_dir(kobj);
unlink(kobj); unlink(kobj);
} }
......
...@@ -2815,7 +2815,7 @@ extern void ip_auto_config(void); ...@@ -2815,7 +2815,7 @@ extern void ip_auto_config(void);
extern void dv_init(void); extern void dv_init(void);
#endif /* CONFIG_NET_DIVERT */ #endif /* CONFIG_NET_DIVERT */
static decl_subsys(net,NULL); static decl_subsys(net,NULL,NULL);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment