## Automatically generated incremental diff
## From:   linux-2.5.67-bk3
## To:     linux-2.5.67-bk4
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.5.67-bk3/CREDITS linux-2.5.67-bk4/CREDITS
--- linux-2.5.67-bk3/CREDITS	2003-04-07 10:32:15.000000000 -0700
+++ linux-2.5.67-bk4/CREDITS	2003-04-12 04:33:20.000000000 -0700
@@ -2750,6 +2750,10 @@
 E: wsalamon@nai.com
 D: portions of the Linux Security Module (LSM) framework and security modules
 
+N: Robert Sanders
+E: gt8134b@prism.gatech.edu
+D: Dosemu
+
 N: Duncan Sands
 E: duncan.sands@wanadoo.fr
 W: http://topo.math.u-psud.fr/~sands
@@ -2758,10 +2762,6 @@
 S: 75013 Paris
 S: France
 
-N: Robert Sanders
-E: gt8134b@prism.gatech.edu
-D: Dosemu
-
 N: Hannu Savolainen
 E: hannu@opensound.com
 D: Maintainer of the sound drivers until 2.1.x days.
diff -urN linux-2.5.67-bk3/Documentation/DocBook/usb.tmpl linux-2.5.67-bk4/Documentation/DocBook/usb.tmpl
--- linux-2.5.67-bk3/Documentation/DocBook/usb.tmpl	2003-04-07 10:33:02.000000000 -0700
+++ linux-2.5.67-bk4/Documentation/DocBook/usb.tmpl	2003-04-12 04:33:20.000000000 -0700
@@ -100,7 +100,8 @@
 <chapter id="host">
     <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
     layers of USB driver.  Those are
     <emphasis>general purpose</emphasis> drivers, exposed through
@@ -289,6 +290,684 @@
 !Edrivers/usb/core/buffer.c
     </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>
 <!-- vim:syntax=sgml:sw=4
 -->
diff -urN linux-2.5.67-bk3/Makefile linux-2.5.67-bk4/Makefile
--- linux-2.5.67-bk3/Makefile	2003-04-12 04:33:17.000000000 -0700
+++ linux-2.5.67-bk4/Makefile	2003-04-12 04:33:20.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
 SUBLEVEL = 67
-EXTRAVERSION = -bk3
+EXTRAVERSION = -bk4
 
 # *DOCUMENTATION*
 # To see a list of typical targets execute "make help"
diff -urN linux-2.5.67-bk3/arch/i386/kernel/edd.c linux-2.5.67-bk4/arch/i386/kernel/edd.c
--- linux-2.5.67-bk3/arch/i386/kernel/edd.c	2003-04-07 10:31:02.000000000 -0700
+++ linux-2.5.67-bk4/arch/i386/kernel/edd.c	2003-04-12 04:33:20.000000000 -0700
@@ -598,7 +598,7 @@
 	.default_attrs	= def_attrs,
 };
 
-static decl_subsys(edd,&ktype_edd);
+static decl_subsys(edd,&ktype_edd,NULL);
 
 
 /**
diff -urN linux-2.5.67-bk3/arch/sparc/kernel/sparc_ksyms.c linux-2.5.67-bk4/arch/sparc/kernel/sparc_ksyms.c
--- linux-2.5.67-bk3/arch/sparc/kernel/sparc_ksyms.c	2003-04-07 10:30:40.000000000 -0700
+++ linux-2.5.67-bk4/arch/sparc/kernel/sparc_ksyms.c	2003-04-12 04:33:20.000000000 -0700
@@ -127,6 +127,7 @@
 EXPORT_SYMBOL(__down_interruptible);
 
 EXPORT_SYMBOL(sparc_valid_addr_bitmap);
+EXPORT_SYMBOL(phys_base);
 
 /* Atomic operations. */
 EXPORT_SYMBOL_PRIVATE(_atomic_add);
diff -urN linux-2.5.67-bk3/drivers/acpi/bus.c linux-2.5.67-bk4/drivers/acpi/bus.c
--- linux-2.5.67-bk3/drivers/acpi/bus.c	2003-04-07 10:31:50.000000000 -0700
+++ linux-2.5.67-bk4/drivers/acpi/bus.c	2003-04-12 04:33:21.000000000 -0700
@@ -676,7 +676,7 @@
 	return_VALUE(-ENODEV);
 }
 
-decl_subsys(acpi,NULL);
+decl_subsys(acpi,NULL,NULL);
 
 static int __init acpi_init (void)
 {
diff -urN linux-2.5.67-bk3/drivers/base/base.h linux-2.5.67-bk4/drivers/base/base.h
--- linux-2.5.67-bk3/drivers/base/base.h	2003-04-07 10:31:04.000000000 -0700
+++ linux-2.5.67-bk4/drivers/base/base.h	2003-04-12 04:33:21.000000000 -0700
@@ -18,13 +18,8 @@
 
 
 #ifdef CONFIG_HOTPLUG
-extern int dev_hotplug(struct device *dev, const char *action);
 extern int class_hotplug(struct device *dev, const char *action);
 #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)
 {
 	return 0;
diff -urN linux-2.5.67-bk3/drivers/base/bus.c linux-2.5.67-bk4/drivers/base/bus.c
--- linux-2.5.67-bk3/drivers/base/bus.c	2003-04-07 10:30:57.000000000 -0700
+++ linux-2.5.67-bk4/drivers/base/bus.c	2003-04-12 04:33:21.000000000 -0700
@@ -132,7 +132,7 @@
 
 };
 
-decl_subsys(bus,&ktype_bus);
+decl_subsys(bus,&ktype_bus,NULL);
 
 /**
  *	bus_for_each_dev - device iterator.
diff -urN linux-2.5.67-bk3/drivers/base/class.c linux-2.5.67-bk4/drivers/base/class.c
--- linux-2.5.67-bk3/drivers/base/class.c	2003-04-07 10:33:00.000000000 -0700
+++ linux-2.5.67-bk4/drivers/base/class.c	2003-04-12 04:33:21.000000000 -0700
@@ -49,7 +49,9 @@
 	.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)
diff -urN linux-2.5.67-bk3/drivers/base/core.c linux-2.5.67-bk4/drivers/base/core.c
--- linux-2.5.67-bk3/drivers/base/core.c	2003-04-07 10:30:39.000000000 -0700
+++ linux-2.5.67-bk4/drivers/base/core.c	2003-04-12 04:33:21.000000000 -0700
@@ -23,13 +23,12 @@
 
 DECLARE_MUTEX(device_sem);
 
-#define to_dev(obj) container_of(obj,struct device,kobj)
-
 
 /*
  * 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)
 
 extern struct attribute * dev_default_attrs[];
@@ -86,11 +85,55 @@
 	.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.
  */
 
-decl_subsys(devices,&ktype_device);
+decl_subsys(devices, &ktype_device, &device_hotplug_ops);
 
 
 /**
@@ -192,9 +235,6 @@
 	if (platform_notify)
 		platform_notify(dev);
 
-	/* notify userspace of device entry */
-	dev_hotplug(dev, "add");
-
 	devclass_add_device(dev);
  register_done:
 	if (error && parent)
@@ -278,9 +318,6 @@
 	if (platform_notify_remove)
 		platform_notify_remove(dev);
 
-	/* notify userspace that this device is about to disappear */
-	dev_hotplug (dev, "remove");
-
 	bus_remove_device(dev);
 
 	kobject_del(&dev->kobj);
diff -urN linux-2.5.67-bk3/drivers/base/firmware.c linux-2.5.67-bk4/drivers/base/firmware.c
--- linux-2.5.67-bk3/drivers/base/firmware.c	2003-04-07 10:31:41.000000000 -0700
+++ linux-2.5.67-bk4/drivers/base/firmware.c	2003-04-12 04:33:21.000000000 -0700
@@ -6,7 +6,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 
-static decl_subsys(firmware,NULL);
+static decl_subsys(firmware,NULL,NULL);
 
 int firmware_register(struct subsystem * s)
 {
diff -urN linux-2.5.67-bk3/drivers/base/hotplug.c linux-2.5.67-bk4/drivers/base/hotplug.c
--- linux-2.5.67-bk3/drivers/base/hotplug.c	2003-04-07 10:30:34.000000000 -0700
+++ linux-2.5.67-bk4/drivers/base/hotplug.c	2003-04-12 04:33:21.000000000 -0700
@@ -2,8 +2,8 @@
  * drivers/base/hotplug.c - hotplug call code
  * 
  * Copyright (c) 2000-2001 David Brownell
- * Copyright (c) 2002 Greg Kroah-Hartman
- * Copyright (c) 2002 IBM Corp.
+ * Copyright (c) 2002-2003 Greg Kroah-Hartman
+ * Copyright (c) 2002-2003 IBM Corp.
  *
  * Based off of drivers/usb/core/usb.c:call_agent(), which was 
  * written by David Brownell.
@@ -53,17 +53,6 @@
 	if (!hotplug_path [0])
 		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);
 	if (!envp)
 		return -ENOMEM;
@@ -128,23 +117,6 @@
 	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
  */
diff -urN linux-2.5.67-bk3/drivers/block/genhd.c linux-2.5.67-bk4/drivers/block/genhd.c
--- linux-2.5.67-bk3/drivers/block/genhd.c	2003-04-07 10:31:16.000000000 -0700
+++ linux-2.5.67-bk4/drivers/block/genhd.c	2003-04-12 04:33:21.000000000 -0700
@@ -525,9 +525,21 @@
 	.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. */
-static decl_subsys(block,&ktype_block);
+static decl_subsys(block, &ktype_block, &block_hotplug_ops);
 
 
 struct gendisk *alloc_disk(int minors)
diff -urN linux-2.5.67-bk3/drivers/hotplug/pci_hotplug_core.c linux-2.5.67-bk4/drivers/hotplug/pci_hotplug_core.c
--- linux-2.5.67-bk3/drivers/hotplug/pci_hotplug_core.c	2003-04-07 10:32:26.000000000 -0700
+++ linux-2.5.67-bk4/drivers/hotplug/pci_hotplug_core.c	2003-04-12 04:33:21.000000000 -0700
@@ -100,7 +100,7 @@
 	.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 */
diff -urN linux-2.5.67-bk3/drivers/i2c/Kconfig linux-2.5.67-bk4/drivers/i2c/Kconfig
--- linux-2.5.67-bk3/drivers/i2c/Kconfig	2003-04-07 10:30:46.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/Kconfig	2003-04-12 04:33:21.000000000 -0700
@@ -142,6 +142,17 @@
 	  <file:Documentation/modules.txt>.
 	  The module will be called i2c-elektor.
 
+config I2C_KEYWEST
+	tristate "Powermac Keywest I2C interface"
+	depends on I2C && ALL_PPC
+	help
+	  This supports the use of the I2C interface in the combo-I/O
+	  chip on recent Apple machines.  Say Y if you have such a machine.
+
+	  This driver is also available as a module.  If you want to compile
+	  it as a module, say M here and read Documentation/modules.txt.
+	  The module will be called i2c-keywest.
+
 config ITE_I2C_ALGO
 	tristate "ITE I2C Algorithm"
 	depends on MIPS_ITE8172 && I2C
diff -urN linux-2.5.67-bk3/drivers/i2c/Makefile linux-2.5.67-bk4/drivers/i2c/Makefile
--- linux-2.5.67-bk3/drivers/i2c/Makefile	2003-04-07 10:30:34.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/Makefile	2003-04-12 04:33:21.000000000 -0700
@@ -10,6 +10,7 @@
 obj-$(CONFIG_I2C_VELLEMAN)	+= i2c-velleman.o
 obj-$(CONFIG_I2C_ALGOPCF)	+= i2c-algo-pcf.o
 obj-$(CONFIG_I2C_ELEKTOR)	+= i2c-elektor.o
+obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 obj-$(CONFIG_ITE_I2C_ALGO)	+= i2c-algo-ite.o
 obj-$(CONFIG_ITE_I2C_ADAP)	+= i2c-adap-ite.o
 obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
diff -urN linux-2.5.67-bk3/drivers/i2c/busses/Kconfig linux-2.5.67-bk4/drivers/i2c/busses/Kconfig
--- linux-2.5.67-bk3/drivers/i2c/busses/Kconfig	2003-04-07 10:32:27.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/busses/Kconfig	2003-04-12 04:33:21.000000000 -0700
@@ -116,5 +116,31 @@
 	  in the lm_sensors package, which you can download at 
 	  http://www.lm-sensors.nu
 
+
+config I2C_VIAPRO
+	tristate "  VIA 82C596/82C686/823x"
+	depends on I2C && PCI && EXPERIMENTAL
+	help
+
+	  If you say yes to this option, support will be included for the VIA
+	  82C596/82C686/823x I2C interfaces.  Specifically, the following 
+	  chipsets are supported:
+	  82C596A/B
+	  82C686A/B
+	  8231
+	  8233
+	  8233A
+	  8235
+
+	  This can also be built as a module which can be inserted and removed
+	  while the kernel is running.  If you want to compile it as a module,
+	  say M here and read <file:Documentation/modules.txt>.
+
+	  The module will be called i2c-viapro.
+
+	  You will also need the latest user-space utilties: you can find them
+	  in the lm_sensors package, which you can download at
+	  http://www.lm-sensors.nu
+
 endmenu
 
diff -urN linux-2.5.67-bk3/drivers/i2c/busses/Makefile linux-2.5.67-bk4/drivers/i2c/busses/Makefile
--- linux-2.5.67-bk3/drivers/i2c/busses/Makefile	2003-04-07 10:30:39.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/busses/Makefile	2003-04-12 04:33:21.000000000 -0700
@@ -8,3 +8,4 @@
 obj-$(CONFIG_I2C_I801)		+= i2c-i801.o
 obj-$(CONFIG_I2C_ISA)		+= i2c-isa.o
 obj-$(CONFIG_I2C_PIIX4)		+= i2c-piix4.o
+obj-$(CONFIG_I2C_VIAPRO)	+= i2c-viapro.o
diff -urN linux-2.5.67-bk3/drivers/i2c/busses/i2c-viapro.c linux-2.5.67-bk4/drivers/i2c/busses/i2c-viapro.c
--- linux-2.5.67-bk3/drivers/i2c/busses/i2c-viapro.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.67-bk4/drivers/i2c/busses/i2c-viapro.c	2003-04-12 04:33:21.000000000 -0700
@@ -0,0 +1,504 @@
+/*
+    i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware
+              monitoring
+    Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>, 
+    Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>,
+    Mark D. Studebaker <mdsxyz123@yahoo.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+   Supports Via devices:
+	82C596A/B (0x3050)
+	82C596B (0x3051)
+	82C686A/B
+	8231
+	8233
+	8233A (0x3147 and 0x3177)
+	8235
+   Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+#define SMBBA1	    0x90
+#define SMBBA2      0x80
+#define SMBBA3      0xD0
+
+/* SMBus address offsets */
+#define SMBHSTSTS (0 + vt596_smba)
+#define SMBHSLVSTS (1 + vt596_smba)
+#define SMBHSTCNT (2 + vt596_smba)
+#define SMBHSTCMD (3 + vt596_smba)
+#define SMBHSTADD (4 + vt596_smba)
+#define SMBHSTDAT0 (5 + vt596_smba)
+#define SMBHSTDAT1 (6 + vt596_smba)
+#define SMBBLKDAT (7 + vt596_smba)
+#define SMBSLVCNT (8 + vt596_smba)
+#define SMBSHDWCMD (9 + vt596_smba)
+#define SMBSLVEVT (0xA + vt596_smba)
+#define SMBSLVDAT (0xC + vt596_smba)
+
+/* PCI Address Constants */
+
+/* SMBus data in configuration space can be found in two places,
+   We try to select the better one*/
+
+static unsigned short smb_cf_hstcfg = 0xD2;
+
+#define SMBHSTCFG   (smb_cf_hstcfg)
+#define SMBSLVC     (SMBHSTCFG+1)
+#define SMBSHDW1    (SMBHSTCFG+2)
+#define SMBSHDW2    (SMBHSTCFG+3)
+#define SMBREV      (SMBHSTCFG+4)
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+#define  ENABLE_INT9 0
+
+/* VT82C596 constants */
+#define VT596_QUICK      0x00
+#define VT596_BYTE       0x04
+#define VT596_BYTE_DATA  0x08
+#define VT596_WORD_DATA  0x0C
+#define VT596_BLOCK_DATA 0x14
+
+/* insmod parameters */
+
+/* If force is set to anything different from 0, we forcibly enable the
+   VT596. DANGEROUS! */
+static int force = 0;
+MODULE_PARM(force, "i");
+MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!");
+
+/* If force_addr is set to anything different from 0, we forcibly enable
+   the VT596 at the given address. VERY DANGEROUS! */
+static int force_addr = 0;
+MODULE_PARM(force_addr, "i");
+MODULE_PARM_DESC(force_addr,
+		 "Forcibly enable the SMBus at the given address. "
+		 "EXTREMELY DANGEROUS!");
+
+static void vt596_do_pause(unsigned int amount);
+static int vt596_transaction(void);
+s32 vt596_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, 
+	char read_write, u8 command, int size, union i2c_smbus_data * data);
+u32 vt596_func(struct i2c_adapter *adapter);
+
+static struct i2c_algorithm smbus_algorithm = {
+	.name		= "Non-I2C SMBus adapter",
+	.id		= I2C_ALGO_SMBUS,
+	.smbus_xfer	= vt596_access,
+	.functionality	= vt596_func,
+};
+
+static struct i2c_adapter vt596_adapter = {
+	.owner		= THIS_MODULE,
+	.id		= I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2,
+	.algo		= &smbus_algorithm,
+	.dev		= {
+		.name	= "unset",
+	},
+};
+
+
+
+
+static unsigned short vt596_smba = 0;
+
+
+/* Detect whether a compatible device can be found, and initialize it. */
+int vt596_setup(struct pci_dev *VT596_dev, struct pci_device_id const *id)
+{
+	unsigned char temp;
+	
+	dev_info(&VT596_dev->dev, "Found Via %s device\n", VT596_dev->dev.name);
+
+	/* Determine the address of the SMBus areas */
+	if (force_addr) {
+		vt596_smba = force_addr & 0xfff0;
+		force = 0;
+	} else {
+		if ((pci_read_config_word(VT596_dev, id->driver_data, &vt596_smba))
+		    || !(vt596_smba & 0x1)) {
+			/* try 2nd address and config reg. for 596 */
+			if((id->device == PCI_DEVICE_ID_VIA_82C596_3) &&
+			   (!pci_read_config_word(VT596_dev, SMBBA2, &vt596_smba)) &&
+			   (vt596_smba & 0x1)) {
+				smb_cf_hstcfg = 0x84;
+			} else {
+			        /* no matches at all */
+			        dev_err(&VT596_dev->dev, "Cannot configure "
+					"SMBus I/O Base address\n");
+			        return(-ENODEV);
+			}
+		}
+		vt596_smba &= 0xfff0;
+		if(vt596_smba == 0) {
+			dev_err(&VT596_dev->dev, "SMBus base address "
+				"uninitialized - upgrade BIOS or use "
+				"force_addr=0xaddr\n");
+			return -ENODEV;
+		}
+	}
+
+	if (!request_region(vt596_smba, 8, "viapro-smbus")) {
+		dev_err(&VT596_dev->dev, "SMBus region 0x%x already in use!\n",
+		        vt596_smba);
+		return(-ENODEV);
+	}
+
+	pci_read_config_byte(VT596_dev, SMBHSTCFG, &temp);
+	/* If force_addr is set, we program the new address here. Just to make
+	   sure, we disable the VT596 first. */
+	if (force_addr) {
+		pci_write_config_byte(VT596_dev, SMBHSTCFG, temp & 0xfe);
+		pci_write_config_word(VT596_dev, id->driver_data, vt596_smba);
+		pci_write_config_byte(VT596_dev, SMBHSTCFG, temp | 0x01);
+		dev_warn(&VT596_dev->dev, "WARNING: SMBus interface set to new "
+		     "address 0x%04x!\n", vt596_smba);
+	} else if ((temp & 1) == 0) {
+		if (force) {
+			/* NOTE: This assumes I/O space and other allocations 
+			 * WERE done by the Bios!  Don't complain if your 
+			 * hardware does weird things after enabling this. 
+			 * :') Check for Bios updates before resorting to 
+			 * this.
+			 */
+			pci_write_config_byte(VT596_dev, SMBHSTCFG,
+					      temp | 1);
+			dev_info(&VT596_dev->dev, "Enabling SMBus device\n");
+		} else {
+			dev_err(&VT596_dev->dev, "SMBUS: Error: Host SMBus "
+				"controller not enabled! - upgrade BIOS or "
+				"use force=1\n");
+			return(-ENODEV);
+		}
+	}
+
+	if ((temp & 0x0E) == 8)
+		dev_dbg(&VT596_dev->dev, "using Interrupt 9 for SMBus.\n");
+	else if ((temp & 0x0E) == 0)
+		dev_dbg(&VT596_dev->dev, "using Interrupt SMI# for SMBus.\n");
+	else
+		dev_dbg(&VT596_dev->dev, "Illegal Interrupt configuration "
+			"(or code out of date)!\n");
+
+	pci_read_config_byte(VT596_dev, SMBREV, &temp);
+	dev_dbg(&VT596_dev->dev, "SMBREV = 0x%X\n", temp);
+	dev_dbg(&VT596_dev->dev, "VT596_smba = 0x%X\n", vt596_smba);
+
+	return(0);
+}
+
+
+/* Internally used pause function */
+void vt596_do_pause(unsigned int amount)
+{
+	current->state = TASK_INTERRUPTIBLE;
+	schedule_timeout(amount);
+}
+
+/* Another internally used function */
+int vt596_transaction(void)
+{
+	int temp;
+	int result = 0;
+	int timeout = 0;
+
+	dev_dbg(&vt596_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), 
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 
+		inb_p(SMBHSTDAT1));
+
+	/* Make sure the SMBus host is ready to start transmitting */
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+		dev_dbg(&vt596_adapter.dev, "SMBus busy (0x%02x). "
+				"Resetting...\n", temp);
+		
+		outb_p(temp, SMBHSTSTS);
+		if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+			dev_dbg(&vt596_adapter.dev, "Failed! (0x%02x)\n", temp);
+			
+			return -1;
+		} else {
+			dev_dbg(&vt596_adapter.dev, "Successfull!\n");
+		}
+	}
+
+	/* start the transaction by setting bit 6 */
+	outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
+
+	/* We will always wait for a fraction of a second! 
+	   I don't know if VIA needs this, Intel did  */
+	do {
+		vt596_do_pause(1);
+		temp = inb_p(SMBHSTSTS);
+	} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
+
+	/* If the SMBus is still busy, we give up */
+	if (timeout >= MAX_TIMEOUT) {
+		result = -1;
+		dev_dbg(&vt596_adapter.dev, "SMBus Timeout!\n");
+	}
+
+	if (temp & 0x10) {
+		result = -1;
+		dev_dbg(&vt596_adapter.dev, "Error: Failed bus transaction\n");
+	}
+
+	if (temp & 0x08) {
+		result = -1;
+		dev_info(&vt596_adapter.dev, "Bus collision! SMBus may be "
+			"locked until next hard\nreset. (sorry!)\n");
+		/* Clock stops and slave is stuck in mid-transmission */
+	}
+
+	if (temp & 0x04) {
+		result = -1;
+		dev_dbg(&vt596_adapter.dev, "Error: no response!\n");
+	}
+
+	if (inb_p(SMBHSTSTS) != 0x00)
+		outb_p(inb(SMBHSTSTS), SMBHSTSTS);
+
+	if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+		dev_dbg(&vt596_adapter.dev, "Failed reset at end of "
+			"transaction (%02x)\n", temp);
+	}
+	dev_dbg(&vt596_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+		"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
+		inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), 
+		inb_p(SMBHSTDAT1));
+	
+	return result;
+}
+
+/* Return -1 on error. */
+s32 vt596_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, 
+		char read_write, u8 command, int size, 
+		union i2c_smbus_data * data)
+{
+	int i, len;
+
+	switch (size) {
+	case I2C_SMBUS_PROC_CALL:
+		dev_info(&vt596_adapter.dev, "I2C_SMBUS_PROC_CALL not supported!\n");
+		return -1;
+	case I2C_SMBUS_QUICK:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		size = VT596_QUICK;
+		break;
+	case I2C_SMBUS_BYTE:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(command, SMBHSTCMD);
+		size = VT596_BYTE;
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE)
+			outb_p(data->byte, SMBHSTDAT0);
+		size = VT596_BYTE_DATA;
+		break;
+	case I2C_SMBUS_WORD_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			outb_p(data->word & 0xff, SMBHSTDAT0);
+			outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
+		}
+		size = VT596_WORD_DATA;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+		       SMBHSTADD);
+		outb_p(command, SMBHSTCMD);
+		if (read_write == I2C_SMBUS_WRITE) {
+			len = data->block[0];
+			if (len < 0)
+				len = 0;
+			if (len > 32)
+				len = 32;
+			outb_p(len, SMBHSTDAT0);
+			i = inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+			for (i = 1; i <= len; i++)
+				outb_p(data->block[i], SMBBLKDAT);
+		}
+		size = VT596_BLOCK_DATA;
+		break;
+	}
+
+	outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
+
+	if (vt596_transaction()) /* Error in transaction */
+		return -1;
+
+	if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK))
+		return 0;
+
+
+	switch (size) {
+	case VT596_BYTE:
+		/* Where is the result put? I assume here it is in
+		 * SMBHSTDAT0 but it might just as well be in the
+		 * SMBHSTCMD. No clue in the docs 
+		 */
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case VT596_BYTE_DATA:
+		data->byte = inb_p(SMBHSTDAT0);
+		break;
+	case VT596_WORD_DATA:
+		data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
+		break;
+	case VT596_BLOCK_DATA:
+		data->block[0] = inb_p(SMBHSTDAT0);
+		i = inb_p(SMBHSTCNT);	/* Reset SMBBLKDAT */
+		for (i = 1; i <= data->block[0]; i++)
+			data->block[i] = inb_p(SMBBLKDAT);
+		break;
+	}
+	return 0;
+}
+
+
+u32 vt596_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	    I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	    I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+
+
+static struct pci_device_id vt596_ids[] __devinitdata = {
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_82C596_3,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device		= PCI_DEVICE_ID_VIA_82C596B_3,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_82C686_4,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8233_0,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8233A,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8235,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA3
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_VIA,
+		.device 	= PCI_DEVICE_ID_VIA_8231_4,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.driver_data	= SMBBA1,
+	},
+	{ 0, }
+};
+
+static int __devinit vt596_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int retval;
+
+	retval = vt596_setup(dev, id);
+	if (retval)
+		return retval;
+
+	vt596_adapter.dev.parent = &dev->dev;
+
+	snprintf(vt596_adapter.dev.name, DEVICE_NAME_SIZE,
+			"SMBus Via Pro adapter at %04x", vt596_smba);
+	
+	retval = i2c_add_adapter(&vt596_adapter);
+
+	return retval;
+}
+
+static void __devexit vt596_remove(struct pci_dev *dev)
+{
+	i2c_del_adapter(&vt596_adapter);
+}
+
+static struct pci_driver vt596_driver = {
+	.name		= "vt596 smbus",
+	.id_table	= vt596_ids,
+	.probe		= vt596_probe,
+	.remove		= __devexit_p(vt596_remove),
+};
+
+static int __init i2c_vt596_init(void)
+{
+	return pci_module_init(&vt596_driver);
+}
+
+
+static void __exit i2c_vt596_exit(void)
+{
+	pci_unregister_driver(&vt596_driver);
+	release_region(vt596_smba, 8);
+}
+
+
+
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock <phil@netroedge.com>");
+MODULE_DESCRIPTION("vt82c596 SMBus driver");
+
+MODULE_LICENSE("GPL");
+
+module_init(i2c_vt596_init);
+module_exit(i2c_vt596_exit);
diff -urN linux-2.5.67-bk3/drivers/i2c/chips/Kconfig linux-2.5.67-bk4/drivers/i2c/chips/Kconfig
--- linux-2.5.67-bk3/drivers/i2c/chips/Kconfig	2003-04-07 10:31:43.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/chips/Kconfig	2003-04-12 04:33:21.000000000 -0700
@@ -66,7 +66,8 @@
 
 config I2C_SENSOR
 	tristate
-	depends on SENSORS_ADM1021 || SENSORS_LM75 || SENSORS_VIA686A || SENSORS_W83781D
-	default m
+	default y if SENSORS_ADM1021=y || SENSORS_LM75=y || SENSORS_VIA686A=y || SENSORS_W83781D=y
+	default m if SENSORS_ADM1021=m || SENSORS_LM75=m || SENSORS_VIA686A=m || SENSORS_W83781D=m
+	default n
 
 endmenu
diff -urN linux-2.5.67-bk3/drivers/i2c/chips/via686a.c linux-2.5.67-bk4/drivers/i2c/chips/via686a.c
--- linux-2.5.67-bk3/drivers/i2c/chips/via686a.c	2003-04-07 10:30:42.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/chips/via686a.c	2003-04-12 04:33:21.000000000 -0700
@@ -87,9 +87,9 @@
 static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e };
 
 /* temps numbered 1-3 */
-#define VIA686A_REG_TEMP(nr)		(regtemp[(nr) - 1])
-#define VIA686A_REG_TEMP_OVER(nr)	(regover[(nr) - 1])
-#define VIA686A_REG_TEMP_HYST(nr)	(reghyst[(nr) - 1])
+#define VIA686A_REG_TEMP(nr)		(regtemp[nr])
+#define VIA686A_REG_TEMP_OVER(nr)	(regover[nr])
+#define VIA686A_REG_TEMP_HYST(nr)	(reghyst[nr])
 #define VIA686A_REG_TEMP_LOW1	0x4b	// bits 7-6
 #define VIA686A_REG_TEMP_LOW23	0x49	// 2 = bits 5-4, 3 = bits 7-6
 
@@ -369,6 +369,8 @@
    dynamically allocated, at the same time when a new via686a client is
    allocated. */
 struct via686a_data {
+	int sysctl_id;
+
 	struct semaphore update_lock;
 	char valid;		/* !=0 if following fields are valid */
 	unsigned long last_updated;	/* In jiffies */
@@ -391,13 +393,258 @@
 static int via686a_detect(struct i2c_adapter *adapter, int address, int kind);
 static int via686a_detach_client(struct i2c_client *client);
 
-static int via686a_read_value(struct i2c_client *client, u8 register);
-static void via686a_write_value(struct i2c_client *client, u8 register,
-				u8 value);
+static inline int via686a_read_value(struct i2c_client *client, u8 reg)
+{
+	return (inb_p(client->addr + reg));
+}
+
+static inline void via686a_write_value(struct i2c_client *client, u8 reg,
+				       u8 value)
+{
+	outb_p(value, client->addr + reg);
+}
+
 static void via686a_update_client(struct i2c_client *client);
 static void via686a_init_client(struct i2c_client *client);
 
-static int via686a_id = 0;
+/* following are the sysfs callback functions */
+
+/* 7 voltage sensors */
+static ssize_t show_in(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr)*10 );
+}
+
+static ssize_t show_in_min(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr)*10 );
+}
+
+static ssize_t show_in_max(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr)*10 );
+}
+
+static ssize_t set_in_min(struct device *dev, const char *buf, 
+		size_t count, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	unsigned long val = simple_strtoul(buf, NULL, 10)/10;
+	data->in_min[nr] = IN_TO_REG(val,nr);
+	via686a_write_value(client, VIA686A_REG_IN_MIN(nr), 
+			data->in_min[nr]);
+	return count;
+}
+static ssize_t set_in_max(struct device *dev, const char *buf, 
+		size_t count, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	unsigned long val = simple_strtoul(buf, NULL, 10)/10;
+	data->in_max[nr] = IN_TO_REG(val,nr);
+	via686a_write_value(client, VIA686A_REG_IN_MAX(nr), 
+			data->in_max[nr]);
+	return count;
+}
+#define show_in_offset(offset)					\
+static ssize_t 							\
+	show_in##offset (struct device *dev, char *buf)		\
+{								\
+	return show_in(dev, buf, 0x##offset);			\
+}								\
+static ssize_t 							\
+	show_in##offset##_min (struct device *dev, char *buf)	\
+{								\
+	return show_in_min(dev, buf, 0x##offset);		\
+}								\
+static ssize_t 							\
+	show_in##offset##_max (struct device *dev, char *buf)	\
+{								\
+	return show_in_max(dev, buf, 0x##offset);		\
+}								\
+static ssize_t set_in##offset##_min (struct device *dev, 	\
+		const char *buf, size_t count) 			\
+{								\
+	return set_in_min(dev, buf, count, 0x##offset);		\
+}								\
+static ssize_t set_in##offset##_max (struct device *dev,	\
+			const char *buf, size_t count)		\
+{								\
+	return set_in_max(dev, buf, count, 0x##offset);		\
+}								\
+static DEVICE_ATTR(in_input##offset, S_IRUGO, show_in##offset, NULL) 	\
+static DEVICE_ATTR(in_min##offset, S_IRUGO | S_IWUSR, 		\
+		show_in##offset##_min, set_in##offset##_min)	\
+static DEVICE_ATTR(in_max##offset, S_IRUGO | S_IWUSR, 		\
+		show_in##offset##_max, set_in##offset##_max)
+
+show_in_offset(0);
+show_in_offset(1);
+show_in_offset(2);
+show_in_offset(3);
+show_in_offset(4);
+
+/* 3 temperatures */
+static ssize_t show_temp(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr])*10 );
+}
+/* more like overshoot temperature */
+static ssize_t show_temp_max(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr])*10);
+}
+/* more like hysteresis temperature */
+static ssize_t show_temp_min(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr])*10);
+}
+static ssize_t set_temp_max(struct device *dev, const char *buf, 
+		size_t count, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10)/10;
+	data->temp_over[nr] = TEMP_TO_REG(val);
+	via686a_write_value(client, VIA686A_REG_TEMP_OVER(nr), data->temp_over[nr]);
+	return count;
+}
+static ssize_t set_temp_min(struct device *dev, const char *buf, 
+		size_t count, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10)/10;
+	data->temp_hyst[nr] = TEMP_TO_REG(val);
+	via686a_write_value(client, VIA686A_REG_TEMP_HYST(nr), data->temp_hyst[nr]);
+	return count;
+}
+#define show_temp_offset(offset)					\
+static ssize_t show_temp_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_temp(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t								\
+show_temp_##offset##_max (struct device *dev, char *buf)		\
+{									\
+	return show_temp_max(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t								\
+show_temp_##offset##_min (struct device *dev, char *buf)		\
+{									\
+	return show_temp_min(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t set_temp_##offset##_max (struct device *dev, 		\
+		const char *buf, size_t count) 				\
+{									\
+	return set_temp_max(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t set_temp_##offset##_min (struct device *dev, 		\
+		const char *buf, size_t count) 				\
+{									\
+	return set_temp_min(dev, buf, count, 0x##offset - 1);		\
+}									\
+static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_temp_##offset, NULL) \
+static DEVICE_ATTR(temp_max##offset, S_IRUGO | S_IWUSR, 		\
+		show_temp_##offset##_max, set_temp_##offset##_max) 	\
+static DEVICE_ATTR(temp_min##offset, S_IRUGO | S_IWUSR, 		\
+		show_temp_##offset##_min, set_temp_##offset##_min)	
+
+show_temp_offset(1);
+show_temp_offset(2);
+show_temp_offset(3);
+
+/* 2 Fans */
+static ssize_t show_fan(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr], 
+				DIV_FROM_REG(data->fan_div[nr])) );
+}
+static ssize_t show_fan_min(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf,"%d\n",
+		FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) );
+}
+static ssize_t show_fan_div(struct device *dev, char *buf, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf,"%d\n", DIV_FROM_REG(data->fan_div[nr]) );
+}
+static ssize_t set_fan_min(struct device *dev, const char *buf, 
+		size_t count, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10);
+	data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr]));
+	via686a_write_value(client, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]);
+	return count;
+}
+static ssize_t set_fan_div(struct device *dev, const char *buf, 
+		size_t count, int nr) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	int val = simple_strtol(buf, NULL, 10);
+	int old = via686a_read_value(client, VIA686A_REG_FANDIV);
+	data->fan_div[nr] = DIV_TO_REG(val);
+	old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);
+	via686a_write_value(client, VIA686A_REG_FANDIV, old);
+	return count;
+}
+
+#define show_fan_offset(offset)						\
+static ssize_t show_fan_##offset (struct device *dev, char *buf)	\
+{									\
+	return show_fan(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t show_fan_##offset##_min (struct device *dev, char *buf)	\
+{									\
+	return show_fan_min(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t show_fan_##offset##_div (struct device *dev, char *buf)	\
+{									\
+	return show_fan_div(dev, buf, 0x##offset - 1);			\
+}									\
+static ssize_t set_fan_##offset##_min (struct device *dev, 		\
+	const char *buf, size_t count) 					\
+{									\
+	return set_fan_min(dev, buf, count, 0x##offset - 1);		\
+}									\
+static ssize_t set_fan_##offset##_div (struct device *dev, 		\
+		const char *buf, size_t count) 				\
+{									\
+	return set_fan_div(dev, buf, count, 0x##offset - 1);		\
+}									\
+static DEVICE_ATTR(fan_input##offset, S_IRUGO, show_fan_##offset, NULL) \
+static DEVICE_ATTR(fan_min##offset, S_IRUGO | S_IWUSR, 			\
+		show_fan_##offset##_min, set_fan_##offset##_min) 	\
+static DEVICE_ATTR(fan_div##offset, S_IRUGO | S_IWUSR, 			\
+		show_fan_##offset##_div, set_fan_##offset##_div)
+
+show_fan_offset(1);
+show_fan_offset(2);
+
+/* Alarm */
+static ssize_t show_alarm(struct device *dev, char *buf) {
+	struct i2c_client *client = to_i2c_client(dev);
+	struct via686a_data *data = i2c_get_clientdata(client);
+	via686a_update_client(client);
+	return sprintf(buf,"%d\n", ALARMS_FROM_REG(data->alarms));
+}
+static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, show_alarm, NULL);
 
 /* The driver. I choose to use type i2c_driver, as at is identical to both
    smbus_driver and isa_driver, and clients could be of either kind */
@@ -411,83 +658,6 @@
 };
 
 
-
-/* The /proc/sys entries */
-
-/* -- SENSORS SYSCTL START -- */
-#define VIA686A_SYSCTL_IN0 1000
-#define VIA686A_SYSCTL_IN1 1001
-#define VIA686A_SYSCTL_IN2 1002
-#define VIA686A_SYSCTL_IN3 1003
-#define VIA686A_SYSCTL_IN4 1004
-#define VIA686A_SYSCTL_FAN1 1101
-#define VIA686A_SYSCTL_FAN2 1102
-#define VIA686A_SYSCTL_TEMP 1200
-#define VIA686A_SYSCTL_TEMP2 1201
-#define VIA686A_SYSCTL_TEMP3 1202
-#define VIA686A_SYSCTL_FAN_DIV 2000
-#define VIA686A_SYSCTL_ALARMS 2001
-
-#define VIA686A_ALARM_IN0 0x01
-#define VIA686A_ALARM_IN1 0x02
-#define VIA686A_ALARM_IN2 0x04
-#define VIA686A_ALARM_IN3 0x08
-#define VIA686A_ALARM_TEMP 0x10
-#define VIA686A_ALARM_FAN1 0x40
-#define VIA686A_ALARM_FAN2 0x80
-#define VIA686A_ALARM_IN4 0x100
-#define VIA686A_ALARM_TEMP2 0x800
-#define VIA686A_ALARM_CHAS 0x1000
-#define VIA686A_ALARM_TEMP3 0x8000
-
-/* -- SENSORS SYSCTL END -- */
-
-#if 0
-/* These files are created for each detected VIA686A. This is just a template;
-   though at first sight, you might think we could use a statically
-   allocated list, we need some way to get back to the parent - which
-   is done through one of the 'extra' fields which are initialized 
-   when a new copy is allocated. */
-static ctl_table via686a_dir_table_template[] = {
-	{VIA686A_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real,
-	 &i2c_sysctl_real, NULL, &via686a_in},
-	{VIA686A_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real,
-	 &i2c_sysctl_real, NULL, &via686a_in},
-	{VIA686A_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real,
-	 &i2c_sysctl_real, NULL, &via686a_in},
-	{VIA686A_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real,
-	 &i2c_sysctl_real, NULL, &via686a_in},
-	{VIA686A_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real,
-	 &i2c_sysctl_real, NULL, &via686a_in},
-	{VIA686A_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real,
-	 &i2c_sysctl_real, NULL, &via686a_fan},
-	{VIA686A_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real,
-	 &i2c_sysctl_real, NULL, &via686a_fan},
-	{VIA686A_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real,
-	 &i2c_sysctl_real, NULL, &via686a_temp},
-	{VIA686A_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL,
-	 &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp},
-	{VIA686A_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL,
-	 &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp},
-	{VIA686A_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL,
-	 &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_fan_div},
-	{VIA686A_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL,
-	 &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_alarms},
-	{0}
-};
-#endif
-
-static inline int via686a_read_value(struct i2c_client *client, u8 reg)
-{
-	return (inb_p(client->addr + reg));
-}
-
-static inline void via686a_write_value(struct i2c_client *client, u8 reg,
-				       u8 value)
-{
-	outb_p(value, client->addr + reg);
-}
-
 /* This is called when the module is loaded */
 static int via686a_attach_adapter(struct i2c_adapter *adapter)
 {
@@ -499,7 +669,7 @@
 	struct i2c_client *new_client;
 	struct via686a_data *data;
 	int err = 0;
-	const char *name = "via686a";
+	const char client_name[] = "via686a chip";
 	u16 val;
 
 	/* Make sure we are probing the ISA bus!!  */
@@ -552,16 +722,49 @@
 	new_client->adapter = adapter;
 	new_client->driver = &via686a_driver;
 	new_client->flags = 0;
+	new_client->dev.parent = &adapter->dev;
 
 	/* Fill in the remaining client fields and put into the global list */
-	snprintf(new_client->dev.name, DEVICE_NAME_SIZE, name);
+	snprintf(new_client->dev.name, DEVICE_NAME_SIZE, client_name);
 
-	new_client->id = via686a_id++;
 	data->valid = 0;
 	init_MUTEX(&data->update_lock);
 	/* Tell the I2C layer a new client has arrived */
 	if ((err = i2c_attach_client(new_client)))
 		goto ERROR3;
+	
+	/* register sysfs hooks */
+	device_create_file(&new_client->dev, &dev_attr_in_input0);
+	device_create_file(&new_client->dev, &dev_attr_in_input1);
+	device_create_file(&new_client->dev, &dev_attr_in_input2);
+	device_create_file(&new_client->dev, &dev_attr_in_input3);
+	device_create_file(&new_client->dev, &dev_attr_in_input4);
+	device_create_file(&new_client->dev, &dev_attr_in_min0);
+	device_create_file(&new_client->dev, &dev_attr_in_min1);
+	device_create_file(&new_client->dev, &dev_attr_in_min2);
+	device_create_file(&new_client->dev, &dev_attr_in_min3);
+	device_create_file(&new_client->dev, &dev_attr_in_min4);
+	device_create_file(&new_client->dev, &dev_attr_in_max0);
+	device_create_file(&new_client->dev, &dev_attr_in_max1);
+	device_create_file(&new_client->dev, &dev_attr_in_max2);
+	device_create_file(&new_client->dev, &dev_attr_in_max3);
+	device_create_file(&new_client->dev, &dev_attr_in_max4);
+	device_create_file(&new_client->dev, &dev_attr_temp_input1);
+	device_create_file(&new_client->dev, &dev_attr_temp_input2);
+	device_create_file(&new_client->dev, &dev_attr_temp_input3);
+	device_create_file(&new_client->dev, &dev_attr_temp_max1);
+	device_create_file(&new_client->dev, &dev_attr_temp_max2);
+	device_create_file(&new_client->dev, &dev_attr_temp_max3);
+	device_create_file(&new_client->dev, &dev_attr_temp_min1);
+	device_create_file(&new_client->dev, &dev_attr_temp_min2);
+	device_create_file(&new_client->dev, &dev_attr_temp_min3);
+	device_create_file(&new_client->dev, &dev_attr_fan_input1);
+	device_create_file(&new_client->dev, &dev_attr_fan_input2);
+	device_create_file(&new_client->dev, &dev_attr_fan_min1);
+	device_create_file(&new_client->dev, &dev_attr_fan_min2);
+	device_create_file(&new_client->dev, &dev_attr_fan_div1);
+	device_create_file(&new_client->dev, &dev_attr_fan_div2);
+	device_create_file(&new_client->dev, &dev_attr_alarm);
 
 	/* Initialize the VIA686A chip */
 	via686a_init_client(new_client);
@@ -629,7 +832,7 @@
 			    FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2));
 	via686a_write_value(client, VIA686A_REG_FAN_MIN(2),
 			    FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2));
-	for (i = 1; i <= 3; i++) {
+	for (i = 0; i <= 2; i++) {
 		via686a_write_value(client, VIA686A_REG_TEMP_OVER(i),
 				    TEMP_TO_REG(VIA686A_INIT_TEMP_OVER));
 		via686a_write_value(client, VIA686A_REG_TEMP_HYST(i),
@@ -670,13 +873,13 @@
 			data->fan_min[i - 1] = via686a_read_value(client,
 						     VIA686A_REG_FAN_MIN(i));
 		}
-		for (i = 1; i <= 3; i++) {
-			data->temp[i - 1] = via686a_read_value(client,
+		for (i = 0; i <= 2; i++) {
+			data->temp[i] = via686a_read_value(client,
 						 VIA686A_REG_TEMP(i)) << 2;
-			data->temp_over[i - 1] =
+			data->temp_over[i] =
 			    via686a_read_value(client,
 					       VIA686A_REG_TEMP_OVER(i));
-			data->temp_hyst[i - 1] =
+			data->temp_hyst[i] =
 			    via686a_read_value(client,
 					       VIA686A_REG_TEMP_HYST(i));
 		}
@@ -709,164 +912,12 @@
 	up(&data->update_lock);
 }
 
-
-/* The next few functions are the call-back functions of the /proc/sys and
-   sysctl files. Which function is used is defined in the ctl_table in
-   the extra1 field.
-   Each function must return the magnitude (power of 10 to divide the date
-   with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must
-   put a maximum of *nrels elements in results reflecting the data of this
-   file, and set *nrels to the number it actually put in it, if operation==
-   SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from
-   results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE.
-   Note that on SENSORS_PROC_REAL_READ, I do not check whether results is
-   large enough (by checking the incoming value of *nrels). This is not very
-   good practice, but as long as you put less than about 5 values in results,
-   you can assume it is large enough. */
-/* FIXME, remove these functions, they are here to verify the sysfs conversion
- * is correct, or not */
-__attribute__((unused))
-static void via686a_in(struct i2c_client *client, int operation, int ctl_name,
-               int *nrels_mag, long *results)
-{
-	struct via686a_data *data = i2c_get_clientdata(client);
-	int nr = ctl_name - VIA686A_SYSCTL_IN0;
-
-	if (operation == SENSORS_PROC_REAL_INFO)
-		*nrels_mag = 2;
-	else if (operation == SENSORS_PROC_REAL_READ) {
-		via686a_update_client(client);
-		results[0] = IN_FROM_REG(data->in_min[nr], nr);
-		results[1] = IN_FROM_REG(data->in_max[nr], nr);
-		results[2] = IN_FROM_REG(data->in[nr], nr);
-		*nrels_mag = 3;
-	} else if (operation == SENSORS_PROC_REAL_WRITE) {
-		if (*nrels_mag >= 1) {
-			data->in_min[nr] = IN_TO_REG(results[0], nr);
-			via686a_write_value(client, VIA686A_REG_IN_MIN(nr),
-					    data->in_min[nr]);
-		}
-		if (*nrels_mag >= 2) {
-			data->in_max[nr] = IN_TO_REG(results[1], nr);
-			via686a_write_value(client, VIA686A_REG_IN_MAX(nr),
-					    data->in_max[nr]);
-		}
-	}
-}
-
-__attribute__((unused))
-static void via686a_fan(struct i2c_client *client, int operation, int ctl_name,
-		 int *nrels_mag, long *results)
-{
-	struct via686a_data *data = i2c_get_clientdata(client);
-	int nr = ctl_name - VIA686A_SYSCTL_FAN1 + 1;
-
-	if (operation == SENSORS_PROC_REAL_INFO)
-		*nrels_mag = 0;
-	else if (operation == SENSORS_PROC_REAL_READ) {
-		via686a_update_client(client);
-		results[0] = FAN_FROM_REG(data->fan_min[nr - 1],
-					  DIV_FROM_REG(data->fan_div
-						       [nr - 1]));
-		results[1] = FAN_FROM_REG(data->fan[nr - 1],
-				 DIV_FROM_REG(data->fan_div[nr - 1]));
-		*nrels_mag = 2;
-	} else if (operation == SENSORS_PROC_REAL_WRITE) {
-		if (*nrels_mag >= 1) {
-			data->fan_min[nr - 1] = FAN_TO_REG(results[0], 
-							   DIV_FROM_REG(data->
-							      fan_div[nr -1]));
-			via686a_write_value(client,
-					    VIA686A_REG_FAN_MIN(nr),
-					    data->fan_min[nr - 1]);
-		}
-	}
-}
-
-__attribute__((unused))
-static void via686a_temp(struct i2c_client *client, int operation, int ctl_name,
-		  int *nrels_mag, long *results)
-{
-	struct via686a_data *data = i2c_get_clientdata(client);
-	int nr = ctl_name - VIA686A_SYSCTL_TEMP;
-
-	if (operation == SENSORS_PROC_REAL_INFO)
-		*nrels_mag = 1;
-	else if (operation == SENSORS_PROC_REAL_READ) {
-		via686a_update_client(client);
-		results[0] = TEMP_FROM_REG(data->temp_over[nr]);
-		results[1] = TEMP_FROM_REG(data->temp_hyst[nr]);
-		results[2] = TEMP_FROM_REG10(data->temp[nr]);
-		*nrels_mag = 3;
-	} else if (operation == SENSORS_PROC_REAL_WRITE) {
-		if (*nrels_mag >= 1) {
-			data->temp_over[nr] = TEMP_TO_REG(results[0]);
-			via686a_write_value(client,
-					    VIA686A_REG_TEMP_OVER(nr + 1),
-					    data->temp_over[nr]);
-		}
-		if (*nrels_mag >= 2) {
-			data->temp_hyst[nr] = TEMP_TO_REG(results[1]);
-			via686a_write_value(client,
-					    VIA686A_REG_TEMP_HYST(nr + 1),
-					    data->temp_hyst[nr]);
-		}
-	}
-}
-
-__attribute__((unused))
-static void via686a_alarms(struct i2c_client *client, int operation, int ctl_name,
-		    int *nrels_mag, long *results)
-{
-	struct via686a_data *data = i2c_get_clientdata(client);
-	if (operation == SENSORS_PROC_REAL_INFO)
-		*nrels_mag = 0;
-	else if (operation == SENSORS_PROC_REAL_READ) {
-		via686a_update_client(client);
-		results[0] = ALARMS_FROM_REG(data->alarms);
-		*nrels_mag = 1;
-	}
-}
-
-__attribute__((unused))
-static void via686a_fan_div(struct i2c_client *client, int operation,
-		     int ctl_name, int *nrels_mag, long *results)
-{
-	struct via686a_data *data = i2c_get_clientdata(client);
-	int old;
-
-	if (operation == SENSORS_PROC_REAL_INFO)
-		*nrels_mag = 0;
-	else if (operation == SENSORS_PROC_REAL_READ) {
-		via686a_update_client(client);
-		results[0] = DIV_FROM_REG(data->fan_div[0]);
-		results[1] = DIV_FROM_REG(data->fan_div[1]);
-		*nrels_mag = 2;
-	} else if (operation == SENSORS_PROC_REAL_WRITE) {
-		old = via686a_read_value(client, VIA686A_REG_FANDIV);
-		if (*nrels_mag >= 2) {
-			data->fan_div[1] = DIV_TO_REG(results[1]);
-			old = (old & 0x3f) | (data->fan_div[1] << 6);
-		}
-		if (*nrels_mag >= 1) {
-			data->fan_div[0] = DIV_TO_REG(results[0]);
-			old = (old & 0xcf) | (data->fan_div[0] << 4);
-			via686a_write_value(client, VIA686A_REG_FANDIV,
-					    old);
-		}
-	}
-}
-
-
 static struct pci_device_id via686a_pci_ids[] __devinitdata = {
        {
 	       .vendor 		= PCI_VENDOR_ID_VIA, 
 	       .device 		= PCI_DEVICE_ID_VIA_82C686_4, 
 	       .subvendor	= PCI_ANY_ID, 
 	       .subdevice	= PCI_ANY_ID, 
-	       .class		= 0, 
-	       .class_mask	= 0, 
-	       .driver_data	= 0, 
        },
        { 0, }
 };
diff -urN linux-2.5.67-bk3/drivers/i2c/chips/w83781d.c linux-2.5.67-bk4/drivers/i2c/chips/w83781d.c
--- linux-2.5.67-bk3/drivers/i2c/chips/w83781d.c	2003-04-07 10:30:32.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/chips/w83781d.c	2003-04-12 04:33:21.000000000 -0700
@@ -364,7 +364,7 @@
 	 \
 	w83781d_update_client(client); \
 	 \
-	return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr])); \
+	return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr] * 10)); \
 }
 show_in_reg(in);
 show_in_reg(in_min);
@@ -378,7 +378,7 @@
 	u32 val; \
 	 \
 	val = simple_strtoul(buf, NULL, 10); \
-	data->in_##reg[nr] = IN_TO_REG(val); \
+	data->in_##reg[nr] = (IN_TO_REG(val) / 10); \
 	w83781d_write_value(client, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \
 	 \
 	return count; \
@@ -712,7 +712,7 @@
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct w83781d_data *data = i2c_get_clientdata(client);
-	u32 val, old, old2, old3;
+	u32 val, old, old2, old3 = 0;
 
 	val = simple_strtoul(buf, NULL, 10);
 	old = w83781d_read_value(client, W83781D_REG_VID_FANDIV);
diff -urN linux-2.5.67-bk3/drivers/i2c/i2c-adap-ite.c linux-2.5.67-bk4/drivers/i2c/i2c-adap-ite.c
--- linux-2.5.67-bk3/drivers/i2c/i2c-adap-ite.c	2003-04-07 10:30:57.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/i2c-adap-ite.c	2003-04-12 04:33:21.000000000 -0700
@@ -196,9 +196,11 @@
 
 static struct i2c_adapter iic_ite_ops = {
 	.owner		= THIS_MODULE,
-	.name		= "ITE IIC adapter",
 	.id		= I2C_HW_I_IIC,
 	.algo_data	= &iic_ite_data,
+	.dev		= {
+		.name	= "ITE IIC adapter",
+	},
 };
 
 /* Called when the module is loaded.  This function starts the
diff -urN linux-2.5.67-bk3/drivers/i2c/i2c-dev.c linux-2.5.67-bk4/drivers/i2c/i2c-dev.c
--- linux-2.5.67-bk3/drivers/i2c/i2c-dev.c	2003-04-07 10:32:18.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/i2c-dev.c	2003-04-12 04:33:21.000000000 -0700
@@ -77,7 +77,7 @@
 
 static struct i2c_driver i2cdev_driver = {
 	.owner		= THIS_MODULE,
-	.name		= "i2c-dev dummy driver",
+	.name		= "dev driver",
 	.id		= I2C_DRIVERID_I2CDEV,
 	.flags		= I2C_DF_DUMMY,
 	.attach_adapter	= i2cdev_attach_adapter,
@@ -100,10 +100,6 @@
 	char *tmp;
 	int ret;
 
-#ifdef DEBUG
-	struct inode *inode = file->f_dentry->d_inode;
-#endif /* DEBUG */
-
 	struct i2c_client *client = (struct i2c_client *)file->private_data;
 
 	if (count > 8192)
@@ -114,10 +110,8 @@
 	if (tmp==NULL)
 		return -ENOMEM;
 
-#ifdef DEBUG
-	printk(KERN_DEBUG "i2c-dev.o: i2c-%d reading %d bytes.\n",minor(inode->i_rdev),
-	       count);
-#endif
+	pr_debug("i2c-dev.o: i2c-%d reading %d bytes.\n",
+		minor(file->f_dentry->d_inode->i_rdev), count);
 
 	ret = i2c_master_recv(client,tmp,count);
 	if (ret >= 0)
@@ -133,10 +127,6 @@
 	char *tmp;
 	struct i2c_client *client = (struct i2c_client *)file->private_data;
 
-#ifdef DEBUG
-	struct inode *inode = file->f_dentry->d_inode;
-#endif /* DEBUG */
-
 	if (count > 8192)
 		count = 8192;
 
@@ -149,10 +139,9 @@
 		return -EFAULT;
 	}
 
-#ifdef DEBUG
-	printk(KERN_DEBUG "i2c-dev.o: i2c-%d writing %d bytes.\n",minor(inode->i_rdev),
-	       count);
-#endif
+	pr_debug("i2c-dev.o: i2c-%d writing %d bytes.\n",
+		minor(file->f_dentry->d_inode->i_rdev), count);
+
 	ret = i2c_master_send(client,tmp,count);
 	kfree(tmp);
 	return ret;
@@ -169,10 +158,8 @@
 	int i,datasize,res;
 	unsigned long funcs;
 
-#ifdef DEBUG
-	printk(KERN_DEBUG "i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", 
-	       minor(inode->i_rdev),cmd, arg);
-#endif /* DEBUG */
+	pr_debug("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n",
+		minor(inode->i_rdev),cmd, arg);
 
 	switch ( cmd ) {
 	case I2C_SLAVE:
@@ -207,6 +194,11 @@
 				   sizeof(rdwr_arg)))
 			return -EFAULT;
 
+		/* Put an arbritrary limit on the number of messages that can
+		 * be sent at once */
+		if (rdwr_arg.nmsgs > 42)
+			return -EINVAL;
+		
 		rdwr_pa = (struct i2c_msg *)
 			kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), 
 			GFP_KERNEL);
@@ -214,38 +206,43 @@
 		if (rdwr_pa == NULL) return -ENOMEM;
 
 		res = 0;
-		for( i=0; i<rdwr_arg.nmsgs; i++ )
-		{
+		for( i=0; i<rdwr_arg.nmsgs; i++ ) {
 		    	if(copy_from_user(&(rdwr_pa[i]),
 					&(rdwr_arg.msgs[i]),
-					sizeof(rdwr_pa[i])))
-			{
+					sizeof(rdwr_pa[i]))) {
 			        res = -EFAULT;
 				break;
 			}
+			/* Limit the size of the message to a sane amount */
+			if (rdwr_pa[i].len > 8192) {
+				res = -EINVAL;
+				break;
+			}
 			rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);
-			if(rdwr_pa[i].buf == NULL)
-			{
+			if(rdwr_pa[i].buf == NULL) {
 				res = -ENOMEM;
 				break;
 			}
 			if(copy_from_user(rdwr_pa[i].buf,
 				rdwr_arg.msgs[i].buf,
-				rdwr_pa[i].len))
-			{
-			    	kfree(rdwr_pa[i].buf);
+				rdwr_pa[i].len)) {
 			    	res = -EFAULT;
 				break;
 			}
 		}
-		if (!res) 
-		{
+		if (res < 0) {
+			int j;
+			for (j = 0; j < i; ++j)
+				kfree(rdwr_pa[j].buf);
+			kfree(rdwr_pa);
+			return res;
+		}
+		if (!res) {
 			res = i2c_transfer(client->adapter,
 				rdwr_pa,
 				rdwr_arg.nmsgs);
 		}
-		while(i-- > 0)
-		{
+		while(i-- > 0) {
 			if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD))
 			{
 				if(copy_to_user(
@@ -274,20 +271,18 @@
 		    (data_arg.size != I2C_SMBUS_BLOCK_DATA) &&
 		    (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) &&
 		    (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) {
-#ifdef DEBUG
-			printk(KERN_DEBUG "i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n",
-			       data_arg.size);
-#endif
+			dev_dbg(&client->dev,
+				"size out of range (%x) in ioctl I2C_SMBUS.\n",
+				data_arg.size);
 			return -EINVAL;
 		}
 		/* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, 
 		   so the check is valid if size==I2C_SMBUS_QUICK too. */
 		if ((data_arg.read_write != I2C_SMBUS_READ) && 
 		    (data_arg.read_write != I2C_SMBUS_WRITE)) {
-#ifdef DEBUG
-			printk(KERN_DEBUG "i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n",
-			       data_arg.read_write);
-#endif
+			dev_dbg(&client->dev, 
+				"read_write out of range (%x) in ioctl I2C_SMBUS.\n",
+				data_arg.read_write);
 			return -EINVAL;
 		}
 
@@ -298,15 +293,14 @@
 		    (data_arg.read_write == I2C_SMBUS_WRITE)))
 			/* These are special: we do not use data */
 			return i2c_smbus_xfer(client->adapter, client->addr,
-			                      client->flags,
-			                      data_arg.read_write,
-			                      data_arg.command,
-			                      data_arg.size, NULL);
+					      client->flags,
+					      data_arg.read_write,
+					      data_arg.command,
+					      data_arg.size, NULL);
 
 		if (data_arg.data == NULL) {
-#ifdef DEBUG
-			printk(KERN_DEBUG "i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n");
-#endif
+			dev_dbg(&client->dev,
+				"data is NULL pointer in ioctl I2C_SMBUS.\n");
 			return -EINVAL;
 		}
 
@@ -365,7 +359,7 @@
 
 	return 0;
 
- out_kfree:
+out_kfree:
 	kfree(client);
 	return -ENODEV;
 }
@@ -428,7 +422,8 @@
 {
 	int res;
 
-	printk(KERN_INFO "i2c-dev.o: i2c /dev entries driver module version %s (%s)\n", I2C_VERSION, I2C_DATE);
+	printk(KERN_INFO "i2c /dev entries driver module version %s (%s)\n",
+		I2C_VERSION, I2C_DATE);
 
 	if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) {
 		printk(KERN_ERR "i2c-dev.o: unable to get major %d for i2c bus\n",
diff -urN linux-2.5.67-bk3/drivers/i2c/i2c-frodo.c linux-2.5.67-bk4/drivers/i2c/i2c-frodo.c
--- linux-2.5.67-bk3/drivers/i2c/i2c-frodo.c	2003-04-07 10:30:33.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/i2c-frodo.c	2003-04-12 04:33:21.000000000 -0700
@@ -59,9 +59,11 @@
 
 static struct i2c_adapter frodo_ops = {
 	.owner			= THIS_MODULE,
-	.name			= "Frodo adapter driver",
 	.id			= I2C_HW_B_FRODO,
 	.algo_data		= &bit_frodo_data,
+	.dev			= {
+		.name		= "Frodo adapter driver",
+	},
 };
 
 static int __init i2c_frodo_init (void)
diff -urN linux-2.5.67-bk3/drivers/i2c/i2c-keywest.c linux-2.5.67-bk4/drivers/i2c/i2c-keywest.c
--- linux-2.5.67-bk3/drivers/i2c/i2c-keywest.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.67-bk4/drivers/i2c/i2c-keywest.c	2003-04-12 04:33:21.000000000 -0700
@@ -0,0 +1,666 @@
+/*
+    i2c Support for Apple Keywest I2C Bus Controller
+
+    Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+
+    Original work by
+    
+    Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Changes:
+
+    2001/12/13 BenH	New implementation
+    2001/12/15 BenH	Add support for "byte" and "quick"
+                        transfers. Add i2c_xfer routine.
+
+    My understanding of the various modes supported by keywest are:
+
+     - Dumb mode : not implemented, probably direct tweaking of lines
+     - Standard mode : simple i2c transaction of type
+         S Addr R/W A Data A Data ... T
+     - Standard sub mode : combined 8 bit subaddr write with data read
+         S Addr R/W A SubAddr A Data A Data ... T
+     - Combined mode : Subaddress and Data sequences appended with no stop
+         S Addr R/W A SubAddr S Addr R/W A Data A Data ... T
+
+    Currently, this driver uses only Standard mode for i2c xfer, and
+    smbus byte & quick transfers ; and uses StandardSub mode for
+    other smbus transfers instead of combined as we need that for the
+    sound driver to be happy
+*/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+
+#include "i2c-keywest.h"
+
+#undef POLLED_MODE
+
+#define DBG(x...) do {\
+	if (debug > 0) \
+		printk(KERN_DEBUG "KW:" x); \
+	} while(0)
+
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
+MODULE_LICENSE("GPL");
+MODULE_PARM(probe, "i");
+MODULE_PARM(debug, "i");
+
+int probe = 0;
+int debug = 0;
+
+static struct keywest_iface *ifaces = NULL;
+
+#ifdef POLLED_MODE
+/* This isn't fast, but will go once I implement interrupt with
+ * proper timeout
+ */
+static u8
+wait_interrupt(struct keywest_iface* iface)
+{
+	int i;
+	u8 isr;
+	
+	for (i = 0; i < POLL_TIMEOUT; i++) {
+		isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK;
+		if (isr != 0)
+			return isr;
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1);
+	}
+	return isr;
+}
+#endif /* POLLED_MODE */
+
+
+static void
+do_stop(struct keywest_iface* iface, int result)
+{
+	write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
+	iface->state = state_stop;
+	iface->result = result;
+}
+
+/* Main state machine for standard & standard sub mode */
+static void
+handle_interrupt(struct keywest_iface *iface, u8 isr)
+{
+	int ack;
+	
+	DBG("handle_interrupt(), got: %x, status: %x, state: %d\n",
+		isr, read_reg(reg_status), iface->state);
+	if (isr == 0 && iface->state != state_stop) {
+		do_stop(iface, -1);
+		return;
+	}
+	if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
+		iface->result = -1;
+		iface->state = state_stop;
+	}
+	switch(iface->state) {
+	case state_addr:
+		if (!(isr & KW_I2C_IRQ_ADDR)) {
+			do_stop(iface, -1);
+			break;
+		}
+		ack = read_reg(reg_status);
+		DBG("ack on set address: %x\n", ack);
+		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+			do_stop(iface, -1);
+			break;
+		}
+		/* Handle rw "quick" mode */
+		if (iface->datalen == 0)
+			do_stop(iface, 0);
+		else if (iface->read_write == I2C_SMBUS_READ) {
+			iface->state = state_read;
+			if (iface->datalen > 1)
+				write_reg(reg_control, read_reg(reg_control)
+					| KW_I2C_CTL_AAK);
+		} else {
+			iface->state = state_write;
+			DBG("write byte: %x\n", *(iface->data));
+			write_reg(reg_data, *(iface->data++));
+			iface->datalen--;
+		}
+		
+		break;
+	case state_read:
+		if (!(isr & KW_I2C_IRQ_DATA)) {
+			do_stop(iface, -1);
+			break;
+		}
+		*(iface->data++) = read_reg(reg_data);
+		DBG("read byte: %x\n", *(iface->data-1));
+		iface->datalen--;
+		if (iface->datalen == 0)
+			iface->state = state_stop;
+		else
+			write_reg(reg_control, 0);
+		break;
+	case state_write:
+		if (!(isr & KW_I2C_IRQ_DATA)) {
+			do_stop(iface, -1);
+			break;
+		}
+		/* Check ack status */
+		ack = read_reg(reg_status);
+		DBG("ack on data write: %x\n", ack);
+		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+			do_stop(iface, -1);
+			break;
+		}
+		if (iface->datalen) {
+			DBG("write byte: %x\n", *(iface->data));
+			write_reg(reg_data, *(iface->data++));
+			iface->datalen--;
+		} else
+			do_stop(iface, 0);
+		break;
+		
+	case state_stop:
+		if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
+			do_stop(iface, -1);
+		else {
+			iface->state = state_idle;
+			write_reg(reg_control, 0x00);
+			write_reg(reg_ier, 0x00);
+#ifndef POLLED_MODE
+			complete(&iface->complete);
+#endif /* POLLED_MODE */			
+		}
+		break;
+	}
+	
+	write_reg(reg_isr, isr);
+}
+
+#ifndef POLLED_MODE
+
+/* Interrupt handler */
+static void
+keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)dev_id;
+
+	spin_lock(&iface->lock);
+	del_timer(&iface->timeout_timer);
+	handle_interrupt(iface, read_reg(reg_isr));
+	if (iface->state != state_idle) {
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+	}
+	spin_unlock(&iface->lock);
+}
+
+static void
+keywest_timeout(unsigned long data)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)data;
+
+	DBG("timeout !\n");
+	spin_lock_irq(&iface->lock);
+	handle_interrupt(iface, read_reg(reg_isr));
+	if (iface->state != state_idle) {
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+	}
+	spin_unlock(&iface->lock);
+}
+
+#endif /* POLLED_MODE */
+
+/*
+ * SMBUS-type transfer entrypoint
+ */
+static s32
+keywest_smbus_xfer(	struct i2c_adapter*	adap,
+			u16			addr,
+			unsigned short		flags,
+			char			read_write,
+			u8			command,
+			int			size,
+			union i2c_smbus_data*	data)
+{
+	struct keywest_chan* chan = i2c_get_adapdata(adap);
+	struct keywest_iface* iface = chan->iface;
+	int len;
+	u8* buffer;
+	u16 cur_word;
+	int rc = 0;
+
+	if (iface->state == state_dead)
+		return -1;
+		
+	/* Prepare datas & select mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	switch (size) {
+	    case I2C_SMBUS_QUICK:
+	    	len = 0;
+	    	buffer = NULL;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+	    case I2C_SMBUS_BYTE:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+	    case I2C_SMBUS_BYTE_DATA:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	    	break;
+	    case I2C_SMBUS_WORD_DATA:
+	    	len = 2;
+	    	cur_word = cpu_to_le16(data->word);
+	    	buffer = (u8 *)&cur_word;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+		break;
+	    case I2C_SMBUS_BLOCK_DATA:
+	    	len = data->block[0];
+	    	buffer = &data->block[1];
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+		break;
+	    default:
+	    	return -1;
+	}
+
+	/* Original driver had this limitation */
+	if (len > 32)
+		len = 32;
+
+	down(&iface->sem);
+
+	DBG("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
+		chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
+
+	iface->data = buffer;
+	iface->datalen = len;
+	iface->state = state_addr;
+	iface->result = 0;
+	iface->stopretry = 0;
+	iface->read_write = read_write;
+	
+	/* Setup channel & clear pending irqs */
+	write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
+	write_reg(reg_isr, read_reg(reg_isr));
+	write_reg(reg_status, 0);
+
+	/* Set up address and r/w bit */
+	write_reg(reg_addr,
+		(addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+	/* Set up the sub address */
+	if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
+	    || (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
+		write_reg(reg_subaddr, command);
+
+	/* Arm timeout */
+	iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+	add_timer(&iface->timeout_timer);
+
+	/* Start sending address & enable interrupt*/
+	write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+	write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+#ifdef POLLED_MODE
+	DBG("using polled mode...\n");
+	/* State machine, to turn into an interrupt handler */
+	while(iface->state != state_idle) {
+		u8 isr = wait_interrupt(iface);
+		handle_interrupt(iface, isr);
+	}
+#else /* POLLED_MODE */
+	DBG("using interrupt mode...\n");
+	wait_for_completion(&iface->complete);	
+#endif /* POLLED_MODE */	
+
+	rc = iface->result;	
+	DBG("transfer done, result: %d\n", rc);
+
+	if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ)
+	    	data->word = le16_to_cpu(cur_word);
+	
+	/* Release sem */
+	up(&iface->sem);
+	
+	return rc;
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ */
+static int
+keywest_xfer(	struct i2c_adapter *adap,
+		struct i2c_msg msgs[], 
+		int num)
+{
+	struct keywest_chan* chan = i2c_get_adapdata(adap);
+	struct keywest_iface* iface = chan->iface;
+	struct i2c_msg *pmsg;
+	int i, completed;
+	int rc = 0;
+
+	down(&iface->sem);
+
+	/* Set adapter to standard mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+
+	completed = 0;
+	for (i = 0; rc >= 0 && i < num;) {
+		u8 addr;
+		
+		pmsg = &msgs[i++];
+		addr = pmsg->addr;
+		if (pmsg->flags & I2C_M_TEN) {
+			printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n");
+			rc = -EINVAL;
+			break;
+		}
+		DBG("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n",
+		     chan->chan_no,
+		     pmsg->flags & I2C_M_RD ? "read" : "write",
+                     pmsg->len, addr, i, num);
+    
+		/* Setup channel & clear pending irqs */
+		write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
+		write_reg(reg_isr, read_reg(reg_isr));
+		write_reg(reg_status, 0);
+		
+		iface->data = pmsg->buf;
+		iface->datalen = pmsg->len;
+		iface->state = state_addr;
+		iface->result = 0;
+		iface->stopretry = 0;
+		if (pmsg->flags & I2C_M_RD)
+			iface->read_write = I2C_SMBUS_READ;
+		else
+			iface->read_write = I2C_SMBUS_WRITE;
+
+		/* Set up address and r/w bit */
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;		
+		write_reg(reg_addr,
+			(addr << 1) |
+			((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+		/* Arm timeout */
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+
+		/* Start sending address & enable interrupt*/
+		write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+		write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+#ifdef POLLED_MODE
+		DBG("using polled mode...\n");
+		/* State machine, to turn into an interrupt handler */
+		while(iface->state != state_idle) {
+			u8 isr = wait_interrupt(iface);
+			handle_interrupt(iface, isr);
+		}
+#else /* POLLED_MODE */
+		DBG("using interrupt mode...\n");
+		wait_for_completion(&iface->complete);	
+#endif /* POLLED_MODE */	
+
+		rc = iface->result;
+		if (rc == 0)
+			completed++;
+		DBG("transfer done, result: %d\n", rc);
+	}
+
+	/* Release sem */
+	up(&iface->sem);
+
+	return completed;
+}
+
+static u32
+keywest_func(struct i2c_adapter * adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+/* For now, we only handle combined mode (smbus) */
+static struct i2c_algorithm keywest_algorithm = {
+	name:		"Keywest i2c",
+	id:		I2C_ALGO_SMBUS,
+	smbus_xfer:	keywest_smbus_xfer,
+	master_xfer:	keywest_xfer,
+	functionality:	keywest_func,
+};
+
+
+static int
+create_iface(struct device_node* np)
+{
+	unsigned long steps, *psteps, *prate;
+	unsigned bsteps, tsize, i, nchan, addroffset;
+	struct keywest_iface* iface;
+	int rc;
+
+	psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
+	steps = psteps ? (*psteps) : 0x10;
+
+	/* Hrm... maybe we can be smarter here */
+	for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
+		steps >>= 1;
+
+	if (!strcmp(np->parent->name, "uni-n")) {
+		nchan = 2;
+		addroffset = 3;
+	} else {
+		addroffset = 0;
+		nchan = 1;
+	}
+
+	tsize = sizeof(struct keywest_iface) +
+		(sizeof(struct keywest_chan) + 4) * nchan;
+	iface = (struct keywest_iface *) kmalloc(tsize, GFP_KERNEL);
+	if (iface == NULL) {
+		printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
+		return -ENOMEM;
+	}
+	memset(iface, 0, tsize);
+	init_MUTEX(&iface->sem);
+	spin_lock_init(&iface->lock);
+	init_completion(&iface->complete);
+	iface->bsteps = bsteps;
+	iface->chan_count = nchan;
+	iface->state = state_idle;
+	iface->irq = np->intrs[0].line;
+	iface->channels = (struct keywest_chan *)
+		(((unsigned long)(iface + 1) + 3UL) & ~3UL);
+	iface->base = (unsigned long)ioremap(np->addrs[0].address + addroffset,
+						np->addrs[0].size);
+	if (iface->base == 0) {
+		printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
+		kfree(iface);
+		return -ENOMEM;
+	}
+
+	init_timer(&iface->timeout_timer);
+	iface->timeout_timer.function = keywest_timeout;
+	iface->timeout_timer.data = (unsigned long)iface;
+
+	/* Select interface rate */
+	iface->cur_mode = KW_I2C_MODE_100KHZ;
+	prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
+	if (prate) switch(*prate) {
+	case 100:
+		iface->cur_mode = KW_I2C_MODE_100KHZ;
+		break;
+	case 50:
+		iface->cur_mode = KW_I2C_MODE_50KHZ;
+		break;
+	case 25:
+		iface->cur_mode = KW_I2C_MODE_25KHZ;
+		break;
+	default:
+		printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
+			*prate);
+	}
+	
+	/* Select standard sub mode */
+	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	
+	/* Write mode */
+	write_reg(reg_mode, iface->cur_mode);
+	
+	/* Switch interrupts off & clear them*/
+	write_reg(reg_ier, 0x00);
+	write_reg(reg_isr, KW_I2C_IRQ_MASK);
+
+#ifndef POLLED_MODE
+	/* Request chip interrupt */	
+	rc = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
+	if (rc) {
+		printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
+		iounmap((void *)iface->base);
+		kfree(iface);
+		return -ENODEV;
+	}
+#endif /* POLLED_MODE */
+
+	for (i=0; i<nchan; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		u8 addr;
+		
+		sprintf(chan->adapter.dev.name, "%s %d", np->parent->name, i);
+		chan->iface = iface;
+		chan->chan_no = i;
+		chan->adapter.id = I2C_ALGO_SMBUS;
+		chan->adapter.algo = &keywest_algorithm;
+		chan->adapter.algo_data = NULL;
+		chan->adapter.client_register = NULL;
+		chan->adapter.client_unregister = NULL;
+		i2c_set_adapdata(&chan->adapter, chan);
+
+		rc = i2c_add_adapter(&chan->adapter);
+		if (rc) {
+			printk("i2c-keywest.c: Adapter %s registration failed\n",
+				chan->adapter.dev.name);
+			i2c_set_adapdata(&chan->adapter, NULL);
+		}
+		if (probe) {
+			printk("Probe: ");
+			for (addr = 0x00; addr <= 0x7f; addr++) {
+				if (i2c_smbus_xfer(&chan->adapter,addr,
+				    0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
+					printk("%02x ", addr);
+			}
+			printk("\n");
+		}
+	}
+
+	printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
+		np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
+		
+	iface->next = ifaces;
+	ifaces = iface;
+	return 0;
+}
+
+static void
+dispose_iface(struct keywest_iface *iface)
+{
+	int i, rc;
+	
+	ifaces = iface->next;
+
+	/* Make sure we stop all activity */
+	down(&iface->sem);
+#ifndef POLLED_MODE
+	spin_lock_irq(&iface->lock);
+	while (iface->state != state_idle) {
+		spin_unlock_irq(&iface->lock);
+		schedule();
+		spin_lock_irq(&iface->lock);
+	}
+#endif /* POLLED_MODE */
+	iface->state = state_dead;
+#ifndef POLLED_MODE
+	spin_unlock_irq(&iface->lock);
+	free_irq(iface->irq, iface);
+#endif /* POLLED_MODE */
+	up(&iface->sem);
+
+	/* Release all channels */
+	for (i=0; i<iface->chan_count; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		if (i2c_get_adapdata(&chan->adapter) == NULL)
+			continue;
+		rc = i2c_del_adapter(&chan->adapter);
+		i2c_set_adapdata(&chan->adapter, NULL);
+		/* We aren't that prepared to deal with this... */
+		if (rc)
+			printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
+	}
+	iounmap((void *)iface->base);
+	kfree(iface);
+}
+
+static int __init
+i2c_keywest_init(void)
+{
+	struct device_node *np;
+	int rc = -ENODEV;
+	
+	np = find_compatible_devices("i2c", "keywest");
+	while (np != 0) {
+		if (np->n_addrs >= 1 && np->n_intrs >= 1)
+			rc = create_iface(np);
+		np = np->next;
+	}
+	if (ifaces)
+		rc = 0;
+	return rc;
+}
+
+static void __exit
+i2c_keywest_cleanup(void)
+{
+	while(ifaces)
+		dispose_iface(ifaces);
+}
+
+module_init(i2c_keywest_init);
+module_exit(i2c_keywest_cleanup);
diff -urN linux-2.5.67-bk3/drivers/i2c/i2c-keywest.h linux-2.5.67-bk4/drivers/i2c/i2c-keywest.h
--- linux-2.5.67-bk3/drivers/i2c/i2c-keywest.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.67-bk4/drivers/i2c/i2c-keywest.h	2003-04-12 04:33:21.000000000 -0700
@@ -0,0 +1,111 @@
+#ifndef __I2C_KEYWEST_H__
+#define __I2C_KEYWEST_H__
+
+/* The Tumbler audio equalizer can be really slow sometimes */
+#define POLL_TIMEOUT		(2*HZ)
+
+/* Register indices */
+typedef enum {
+	reg_mode = 0,
+	reg_control,
+	reg_status,
+	reg_isr,
+	reg_ier,
+	reg_addr,
+	reg_subaddr,
+	reg_data
+} reg_t;
+
+
+/* Mode register */
+#define KW_I2C_MODE_100KHZ	0x00
+#define KW_I2C_MODE_50KHZ	0x01
+#define KW_I2C_MODE_25KHZ	0x02
+#define KW_I2C_MODE_DUMB	0x00
+#define KW_I2C_MODE_STANDARD	0x04
+#define KW_I2C_MODE_STANDARDSUB	0x08
+#define KW_I2C_MODE_COMBINED	0x0C
+#define KW_I2C_MODE_MODE_MASK	0x0C
+#define KW_I2C_MODE_CHAN_MASK	0xF0
+
+/* Control register */
+#define KW_I2C_CTL_AAK		0x01
+#define KW_I2C_CTL_XADDR	0x02
+#define KW_I2C_CTL_STOP		0x04
+#define KW_I2C_CTL_START	0x08
+
+/* Status register */
+#define KW_I2C_STAT_BUSY	0x01
+#define KW_I2C_STAT_LAST_AAK	0x02
+#define KW_I2C_STAT_LAST_RW	0x04
+#define KW_I2C_STAT_SDA		0x08
+#define KW_I2C_STAT_SCL		0x10
+
+/* IER & ISR registers */
+#define KW_I2C_IRQ_DATA		0x01
+#define KW_I2C_IRQ_ADDR		0x02
+#define KW_I2C_IRQ_STOP		0x04
+#define KW_I2C_IRQ_START	0x08
+#define KW_I2C_IRQ_MASK		0x0F
+
+/* Physical interface */
+struct keywest_iface
+{
+	unsigned long		base;
+	unsigned		bsteps;
+	int			irq;
+	struct semaphore	sem;
+	spinlock_t		lock;
+	struct keywest_chan*	channels;
+	unsigned		chan_count;
+	u8			cur_mode;
+	char			read_write;
+	u8*			data;
+	unsigned		datalen;
+	int			state;
+	int			result;
+	int			stopretry;
+	struct timer_list	timeout_timer;
+	struct completion	complete;
+	struct keywest_iface*	next;
+};
+
+enum {
+	state_idle,
+	state_addr,
+	state_read,
+	state_write,
+	state_stop,
+	state_dead
+};
+
+/* Channel on an interface */
+struct keywest_chan
+{
+	struct i2c_adapter	adapter;
+	struct keywest_iface*	iface;
+	unsigned		chan_no;
+};
+
+/* Register access */
+
+static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg)
+{
+	return in_8(((volatile u8 *)iface->base)
+		+ (((unsigned)reg) << iface->bsteps));
+}
+
+static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
+{
+	out_8(((volatile u8 *)iface->base)
+		+ (((unsigned)reg) << iface->bsteps), val);
+	(void)__read_reg(iface, reg);
+	udelay(10);
+}
+
+#define write_reg(reg, val)	__write_reg(iface, reg, val) 
+#define read_reg(reg)		__read_reg(iface, reg) 
+
+
+
+#endif /* __I2C_KEYWEST_H__ */
diff -urN linux-2.5.67-bk3/drivers/i2c/scx200_i2c.c linux-2.5.67-bk4/drivers/i2c/scx200_i2c.c
--- linux-2.5.67-bk3/drivers/i2c/scx200_i2c.c	2003-04-07 10:31:18.000000000 -0700
+++ linux-2.5.67-bk4/drivers/i2c/scx200_i2c.c	2003-04-12 04:33:21.000000000 -0700
@@ -82,9 +82,11 @@
 
 static struct i2c_adapter scx200_i2c_ops = {
 	.owner		   = THIS_MODULE,
-	.name              = "NatSemi SCx200 I2C",
 	.id		   = I2C_HW_B_VELLE,
 	.algo_data	   = &scx200_i2c_data,
+	.dev		= {
+		.name	= "NatSemi SCx200 I2C",
+	},
 };
 
 int scx200_i2c_init(void)
@@ -110,7 +112,7 @@
 
 	if (i2c_bit_add_bus(&scx200_i2c_ops) < 0) {
 		printk(KERN_ERR NAME ": adapter %s registration failed\n", 
-		       scx200_i2c_ops.name);
+		       scx200_i2c_ops.dev.name);
 		return -ENODEV;
 	}
 	
diff -urN linux-2.5.67-bk3/drivers/ieee1394/csr.c linux-2.5.67-bk4/drivers/ieee1394/csr.c
--- linux-2.5.67-bk3/drivers/ieee1394/csr.c	2003-04-07 10:30:57.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/csr.c	2003-04-12 04:33:21.000000000 -0700
@@ -18,7 +18,8 @@
  */
 
 #include <linux/string.h>
-#include <linux/module.h> /* needed for MODULE_PARM */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
 
 #include "ieee1394_types.h"
 #include "hosts.h"
@@ -27,9 +28,10 @@
 
 /* Module Parameters */
 /* this module parameter can be used to disable mapping of the FCP registers */
-MODULE_PARM(fcp,"i");
-MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0).");
+
 static int fcp = 1;
+module_param(fcp, int, 0444);
+MODULE_PARM_DESC(fcp, "Map FCP registers (default = 1, disable = 0).");
 
 static u16 csr_crc16(unsigned *data, int length)
 {
diff -urN linux-2.5.67-bk3/drivers/ieee1394/dv1394.c linux-2.5.67-bk4/drivers/ieee1394/dv1394.c
--- linux-2.5.67-bk3/drivers/ieee1394/dv1394.c	2003-04-07 10:32:17.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/dv1394.c	2003-04-12 04:33:21.000000000 -0700
@@ -2919,11 +2919,7 @@
 	}
 
 #ifdef CONFIG_DEVFS_FS
-	if (!devfs_mk_dir("ieee1394/dv")) {
-		printk(KERN_ERR "dv1394: unable to create /dev/ieee1394/dv\n");
-		ieee1394_unregister_chardev(IEEE1394_MINOR_BLOCK_DV1394);
-		return -ENOMEM;
-	}
+	devfs_mk_dir("ieee1394/dv");
 #endif
 
 #ifdef CONFIG_PROC_FS
diff -urN linux-2.5.67-bk3/drivers/ieee1394/highlevel.c linux-2.5.67-bk4/drivers/ieee1394/highlevel.c
--- linux-2.5.67-bk3/drivers/ieee1394/highlevel.c	2003-04-07 10:32:27.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/highlevel.c	2003-04-12 04:33:21.000000000 -0700
@@ -57,7 +57,8 @@
         list_add_tail(&hl->hl_list, &hl_drivers);
 	up(&hl_drivers_lock);
 
-	hl_all_hosts(hl->op->add_host);
+	if (hl->op->add_host)
+		hl_all_hosts(hl->op->add_host);
 
         return hl;
 }
@@ -98,6 +99,7 @@
         struct hpsb_address_serve *as;
         struct list_head *entry;
         int retval = 0;
+        unsigned long flags;
 
         if (((start|end) & 3) || (start >= end) || (end > 0x1000000000000ULL)) {
                 HPSB_ERR("%s called with invalid addresses", __FUNCTION__);
@@ -116,7 +118,7 @@
         as->start = start;
         as->end = end;
 
-        write_lock_irq(&addr_space_lock);
+        write_lock_irqsave(&addr_space_lock, flags);
         entry = addr_space.next;
 
         while (list_entry(entry, struct hpsb_address_serve, as_list)->end <= start) {
@@ -128,7 +130,7 @@
                 }
                 entry = entry->next;
         }
-        write_unlock_irq(&addr_space_lock);
+        write_unlock_irqrestore(&addr_space_lock, flags);
 
         if (retval == 0) {
                 kfree(as);
@@ -142,8 +144,9 @@
         int retval = 0;
         struct hpsb_address_serve *as;
         struct list_head *entry;
+        unsigned long flags;
 
-        write_lock_irq(&addr_space_lock);
+        write_lock_irqsave(&addr_space_lock, flags);
 
         entry = hl->addr_list.next;
 
@@ -159,7 +162,7 @@
                 }
         }
 
-        write_unlock_irq(&addr_space_lock);
+        write_unlock_irqrestore(&addr_space_lock, flags);
 
         return retval;
 }
@@ -202,7 +205,8 @@
         list_for_each(entry, &hl_drivers) {
                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
 
-		hl->op->add_host(host);
+		if (hl->op->add_host)
+			hl->op->add_host(host);
         }
         up(&hl_drivers_lock);
 }
@@ -237,48 +241,40 @@
 	up(&hl_drivers_lock);
 }
 
-void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
+void highlevel_iso_receive(struct hpsb_host *host, void *data,
                            unsigned int length)
 {
         struct list_head *entry;
         struct hpsb_highlevel *hl;
-        int channel = (data[0] >> 8) & 0x3f;
+        int channel = (((quadlet_t *)data)[0] >> 8) & 0x3f;
 
         down(&hl_drivers_lock);
-        entry = hl_drivers.next;
-
-        while (entry != &hl_drivers) {
+	list_for_each(entry, &hl_drivers) {
                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
-                if (hl->op->iso_receive) {
+
+                if (hl->op->iso_receive)
                         hl->op->iso_receive(host, channel, data, length);
-                }
-                entry = entry->next;
         }
         up(&hl_drivers_lock);
 }
 
 void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
-                           u8 *data, unsigned int length)
+                           void *data, unsigned int length)
 {
         struct list_head *entry;
         struct hpsb_highlevel *hl;
-        int cts = data[0] >> 4;
+        int cts = ((quadlet_t *)data)[0] >> 4;
 
         down(&hl_drivers_lock);
-        entry = hl_drivers.next;
-
-        while (entry != &hl_drivers) {
+	list_for_each(entry, &hl_drivers) {
                 hl = list_entry(entry, struct hpsb_highlevel, hl_list);
-                if (hl->op->fcp_request) {
-                        hl->op->fcp_request(host, nodeid, direction, cts, data,
-                                            length);
-                }
-                entry = entry->next;
+                if (hl->op->fcp_request)
+                        hl->op->fcp_request(host, nodeid, direction, cts, data, length);
         }
         up(&hl_drivers_lock);
 }
 
-int highlevel_read(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+int highlevel_read(struct hpsb_host *host, int nodeid, void *data,
                    u64 addr, unsigned int length, u16 flags)
 {
         struct hpsb_address_serve *as;
@@ -295,13 +291,14 @@
                 if (as->end > addr) {
                         partlength = min(as->end - addr, (u64) length);
 
-                        if (as->op->read != NULL) {
-                                rcode = as->op->read(host, nodeid, buffer,
+                        if (as->op->read) {
+                                rcode = as->op->read(host, nodeid, data,
 						     addr, partlength, flags);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
 
+			(u8 *)data += partlength;
                         length -= partlength;
                         addr += partlength;
 
@@ -324,7 +321,7 @@
 }
 
 int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
-		    quadlet_t *data, u64 addr, unsigned int length, u16 flags)
+		    void *data, u64 addr, unsigned int length, u16 flags)
 {
         struct hpsb_address_serve *as;
         struct list_head *entry;
@@ -340,13 +337,14 @@
                 if (as->end > addr) {
                         partlength = min(as->end - addr, (u64) length);
 
-                        if (as->op->write != NULL) {
+                        if (as->op->write) {
                                 rcode = as->op->write(host, nodeid, destid,
 						      data, addr, partlength, flags);
                         } else {
                                 rcode = RCODE_TYPE_ERROR;
                         }
 
+			(u8 *)data += partlength;
                         length -= partlength;
                         addr += partlength;
 
@@ -383,7 +381,7 @@
 
         while (as->start <= addr) {
                 if (as->end > addr) {
-                        if (as->op->lock != NULL) {
+                        if (as->op->lock) {
                                 rcode = as->op->lock(host, nodeid, store, addr,
                                                      data, arg, ext_tcode, flags);
                         } else {
@@ -416,7 +414,7 @@
 
         while (as->start <= addr) {
                 if (as->end > addr) {
-                        if (as->op->lock64 != NULL) {
+                        if (as->op->lock64) {
                                 rcode = as->op->lock64(host, nodeid, store,
                                                        addr, data, arg,
                                                        ext_tcode, flags);
diff -urN linux-2.5.67-bk3/drivers/ieee1394/highlevel.h linux-2.5.67-bk4/drivers/ieee1394/highlevel.h
--- linux-2.5.67-bk3/drivers/ieee1394/highlevel.h	2003-04-07 10:31:00.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/highlevel.h	2003-04-12 04:33:21.000000000 -0700
@@ -108,19 +108,19 @@
    later case, no response will be sent and the driver, that handled the request
    will send the response itself.
 */
-int highlevel_read(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+int highlevel_read(struct hpsb_host *host, int nodeid, void *data,
                    u64 addr, unsigned int length, u16 flags);
 int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
-		    quadlet_t *data, u64 addr, unsigned int length, u16 flags);
+		    void *data, u64 addr, unsigned int length, u16 flags);
 int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
                    u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags);
 int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
                      u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags);
 
-void highlevel_iso_receive(struct hpsb_host *host, quadlet_t *data,
+void highlevel_iso_receive(struct hpsb_host *host, void *data,
                            unsigned int length);
 void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
-                           u8 *data, unsigned int length);
+                           void *data, unsigned int length);
 
 
 /*
diff -urN linux-2.5.67-bk3/drivers/ieee1394/hosts.h linux-2.5.67-bk4/drivers/ieee1394/hosts.h
--- linux-2.5.67-bk3/drivers/ieee1394/hosts.h	2003-04-07 10:30:44.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/hosts.h	2003-04-12 04:33:21.000000000 -0700
@@ -1,6 +1,7 @@
 #ifndef _IEEE1394_HOSTS_H
 #define _IEEE1394_HOSTS_H
 
+#include <linux/device.h>
 #include <linux/wait.h>
 #include <linux/list.h>
 #include <asm/semaphore.h>
@@ -64,6 +65,8 @@
         struct hpsb_host_driver *driver;
 
 	struct pci_dev *pdev;
+
+	struct device device;
 };
 
 
diff -urN linux-2.5.67-bk3/drivers/ieee1394/ieee1394.h linux-2.5.67-bk4/drivers/ieee1394/ieee1394.h
--- linux-2.5.67-bk3/drivers/ieee1394/ieee1394.h	2003-04-07 10:30:59.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/ieee1394.h	2003-04-12 04:33:21.000000000 -0700
@@ -46,9 +46,13 @@
 #define ACKX_TIMEOUT             (-4)
 
 
-#define SPEED_100                0x0
-#define SPEED_200                0x1
-#define SPEED_400                0x2
+#define SPEED_100		0x00
+#define SPEED_200		0x01
+#define SPEED_400		0x02
+#define SPEED_800		0x03
+#define SPEED_1600		0x04
+#define SPEED_3200		0x05
+
 
 /* Maps speed values above to a string representation */
 extern const char *hpsb_speedto_str[];
diff -urN linux-2.5.67-bk3/drivers/ieee1394/ieee1394_core.c linux-2.5.67-bk4/drivers/ieee1394/ieee1394_core.c
--- linux-2.5.67-bk3/drivers/ieee1394/ieee1394_core.c	2003-04-07 10:30:57.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/ieee1394_core.c	2003-04-12 04:33:21.000000000 -0700
@@ -28,6 +28,7 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/proc_fs.h>
 #include <linux/bitops.h>
 #include <asm/byteorder.h>
@@ -48,13 +49,9 @@
 /*
  * Disable the nodemgr detection and config rom reading functionality.
  */
-MODULE_PARM(disable_nodemgr, "i");
-MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality.");
 static int disable_nodemgr = 0;
-
-MODULE_PARM(disable_hotplug, "i");
-MODULE_PARM_DESC(disable_hotplug, "Disable hotplug for detected nodes.");
-static int disable_hotplug = 0;
+module_param(disable_nodemgr, int, 0444);
+MODULE_PARM_DESC(disable_nodemgr, "Disable nodemgr functionality.");
 
 /* We are GPL, so treat us special */
 MODULE_LICENSE("GPL");
@@ -62,7 +59,7 @@
 static kmem_cache_t *hpsb_packet_cache;
 
 /* Some globals used */
-const char *hpsb_speedto_str[] = { "S100", "S200", "S400" };
+const char *hpsb_speedto_str[] = { "S100", "S200", "S400", "S800", "S1600", "S3200" };
 
 static void dump_packet(const char *text, quadlet_t *data, int size)
 {
@@ -130,9 +127,8 @@
 {
         struct hpsb_packet *packet = NULL;
         void *data = NULL;
-        int kmflags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
 
-        packet = kmem_cache_alloc(hpsb_packet_cache, kmflags);
+        packet = kmem_cache_alloc(hpsb_packet_cache, GFP_ATOMIC);
         if (packet == NULL)
                 return NULL;
 
@@ -140,7 +136,7 @@
         packet->header = packet->embedded_header;
 
         if (data_size) {
-                data = kmalloc(data_size + 8, kmflags);
+                data = kmalloc(data_size + 8, GFP_ATOMIC);
                 if (data == NULL) {
 			kmem_cache_free(hpsb_packet_cache, packet);
                         return NULL;
@@ -496,8 +492,7 @@
                 quadlet_t *data;
                 size_t size=packet->data_size+packet->header_size;
 
-                int kmflags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
-                data = kmalloc(packet->header_size + packet->data_size, kmflags);
+                data = kmalloc(packet->header_size + packet->data_size, GFP_ATOMIC);
                 if (!data) {
                         HPSB_ERR("unable to allocate memory for concatenating header and data");
                         return 0;
@@ -1120,7 +1115,7 @@
 	/* follow through with the open() */
 	retval = file_ops->open(inode, file);
 
-	if(retval == 0) {
+	if (retval == 0) {
 		
 		/* If the open() succeeded, then ieee1394 will be left
 		 * with an extra module reference, so we discard it here.
@@ -1166,7 +1161,7 @@
 #ifdef CONFIG_PROC_FS
 	/* Must be done before we start everything else, since the drivers
 	 * may use it.  */
-	ieee1394_procfs_entry = proc_mkdir( "ieee1394", proc_bus);
+	ieee1394_procfs_entry = proc_mkdir("ieee1394", proc_bus);
 	if (ieee1394_procfs_entry == NULL) {
 		HPSB_ERR("unable to create /proc/bus/ieee1394\n");
 		unregister_chrdev(IEEE1394_MAJOR, "ieee1394");
@@ -1179,7 +1174,7 @@
 	init_hpsb_highlevel();
 	init_csr();
 	if (!disable_nodemgr)
-		init_ieee1394_nodemgr(disable_hotplug);
+		init_ieee1394_nodemgr();
 	else
 		HPSB_INFO("nodemgr functionality disabled");
 
@@ -1273,7 +1268,7 @@
 EXPORT_SYMBOL(hpsb_node_lock);
 EXPORT_SYMBOL(hpsb_register_protocol);
 EXPORT_SYMBOL(hpsb_unregister_protocol);
-EXPORT_SYMBOL(hpsb_release_unit_directory);
+EXPORT_SYMBOL(ieee1394_bus_type);
 
 /** csr.c **/
 EXPORT_SYMBOL(hpsb_update_config_rom);
diff -urN linux-2.5.67-bk3/drivers/ieee1394/ieee1394_core.h linux-2.5.67-bk4/drivers/ieee1394/ieee1394_core.h
--- linux-2.5.67-bk3/drivers/ieee1394/ieee1394_core.h	2003-04-07 10:30:44.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/ieee1394_core.h	2003-04-12 04:33:21.000000000 -0700
@@ -231,4 +231,7 @@
 /* the proc_fs entry for /proc/ieee1394 */
 extern struct proc_dir_entry *ieee1394_procfs_entry;
 
+/* Our sysfs bus entry */
+extern struct bus_type ieee1394_bus_type;
+
 #endif /* _IEEE1394_CORE_H */
diff -urN linux-2.5.67-bk3/drivers/ieee1394/ieee1394_hotplug.h linux-2.5.67-bk4/drivers/ieee1394/ieee1394_hotplug.h
--- linux-2.5.67-bk3/drivers/ieee1394/ieee1394_hotplug.h	2003-04-07 10:32:56.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/ieee1394_hotplug.h	2003-04-12 04:33:21.000000000 -0700
@@ -1,6 +1,8 @@
 #ifndef _IEEE1394_HOTPLUG_H
 #define _IEEE1394_HOTPLUG_H
 
+#include <linux/device.h>
+
 #include "ieee1394_core.h"
 #include "nodemgr.h"
 
@@ -32,26 +34,6 @@
 	struct ieee1394_device_id *id_table;
 
 	/* 
-	 * The probe function is called when a device is added to the
-	 * bus and the nodemgr finds a matching entry in the drivers
-	 * device id table or when registering this driver and a
-	 * previously unhandled device can be handled.  The driver may
-	 * decline to handle the device based on further investigation
-	 * of the device (or whatever reason) in which case a negative
-	 * error code should be returned, otherwise 0 should be
-	 * returned. The driver may use the driver_data field in the
-	 * unit directory to store per device driver specific data.
-	 */
-	int (*probe)(struct unit_directory *ud);
-
-	/* 
-	 * The disconnect function is called when a device is removed
-	 * from the bus or if it wasn't possible to read the guid
-	 * after the last bus reset.
-	 */
-	void (*disconnect)(struct unit_directory *ud);
-
-	/* 
 	 * The update function is called when the node has just
 	 * survived a bus reset, i.e. it is still present on the bus.
 	 * However, it may be necessary to reestablish the connection
@@ -59,18 +41,12 @@
 	 */
 	void (*update)(struct unit_directory *ud);
 
-	/* Driver in list of all registered drivers */
-	struct list_head list;
 
-	/* The list of unit directories managed by this driver */
-	struct list_head unit_directories;
+	/* Our LDM structure */
+	struct device_driver driver;
 };
 
 int hpsb_register_protocol(struct hpsb_protocol_driver *driver);
 void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver);
 
-int hpsb_claim_unit_directory(struct unit_directory *ud,
-			      struct hpsb_protocol_driver *driver);
-void hpsb_release_unit_directory(struct unit_directory *ud);
-
 #endif /* _IEEE1394_HOTPLUG_H */
diff -urN linux-2.5.67-bk3/drivers/ieee1394/ieee1394_transactions.c linux-2.5.67-bk4/drivers/ieee1394/ieee1394_transactions.c
--- linux-2.5.67-bk3/drivers/ieee1394/ieee1394_transactions.c	2003-04-07 10:31:00.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/ieee1394_transactions.c	2003-04-12 04:33:21.000000000 -0700
@@ -146,10 +146,10 @@
 
 	spin_lock_irqsave(&tp->lock, flags);
 	
-	packet->tlabel = find_next_zero_bit(&tp->pool, 64, tp->next);
+	packet->tlabel = find_next_zero_bit(tp->pool, 64, tp->next);
 	tp->next = (packet->tlabel + 1) % 64;
 	/* Should _never_ happen */
-	BUG_ON(test_and_set_bit(packet->tlabel, &tp->pool));
+	BUG_ON(test_and_set_bit(packet->tlabel, tp->pool));
 	tp->allocations++;
 	spin_unlock_irqrestore(&tp->lock, flags);
 
@@ -177,7 +177,7 @@
 	BUG_ON(packet->tlabel > 63 || packet->tlabel < 0);
 
         spin_lock_irqsave(&tp->lock, flags);
-	BUG_ON(!test_and_clear_bit(packet->tlabel, &tp->pool));
+	BUG_ON(!test_and_clear_bit(packet->tlabel, tp->pool));
         spin_unlock_irqrestore(&tp->lock, flags);
 
 	up(&tp->count);
diff -urN linux-2.5.67-bk3/drivers/ieee1394/ieee1394_types.h linux-2.5.67-bk4/drivers/ieee1394/ieee1394_types.h
--- linux-2.5.67-bk3/drivers/ieee1394/ieee1394_types.h	2003-04-07 10:32:17.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/ieee1394_types.h	2003-04-12 04:33:21.000000000 -0700
@@ -72,19 +72,20 @@
 
 /* Transaction Label handling */
 struct hpsb_tlabel_pool {
-	u64 pool;
+	DECLARE_BITMAP(pool, 64);
 	spinlock_t lock;
 	u8 next;
 	u32 allocations;
 	struct semaphore count;
 };
 
-#define HPSB_TPOOL_INIT(_tp)            \
-do {                                    \
-	sema_init(&(_tp)->count, 63);   \
-	spin_lock_init(&(_tp)->lock);   \
-	(_tp)->next = 0;                \
-	(_tp)->pool = 0;                \
+#define HPSB_TPOOL_INIT(_tp)            	\
+do {                                    	\
+	CLEAR_BITMAP((_tp)->pool, 64);		\
+	spin_lock_init(&(_tp)->lock);   	\
+	(_tp)->next = 0;                	\
+	(_tp)->allocations = 0;			\
+	sema_init(&(_tp)->count, 63);   	\
 } while(0)
 
 
diff -urN linux-2.5.67-bk3/drivers/ieee1394/iso.c linux-2.5.67-bk4/drivers/ieee1394/iso.c
--- linux-2.5.67-bk3/drivers/ieee1394/iso.c	2003-04-07 10:32:25.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/iso.c	2003-04-12 04:33:21.000000000 -0700
@@ -14,7 +14,7 @@
 
 void hpsb_iso_stop(struct hpsb_iso *iso)
 {
-	if(!iso->flags & HPSB_ISO_DRIVER_STARTED)
+	if (!(iso->flags & HPSB_ISO_DRIVER_STARTED))
 		return;
 
 	iso->host->driver->isoctl(iso, iso->type == HPSB_ISO_XMIT ?
diff -urN linux-2.5.67-bk3/drivers/ieee1394/nodemgr.c linux-2.5.67-bk4/drivers/ieee1394/nodemgr.c
--- linux-2.5.67-bk3/drivers/ieee1394/nodemgr.c	2003-04-07 10:31:51.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/nodemgr.c	2003-04-12 04:33:21.000000000 -0700
@@ -1,8 +1,8 @@
 /*
  * Node information (ConfigROM) collection and management.
  *
- * Copyright (C) 2000 Andreas E. Bombe
- *               2001 Ben Collins <bcollins@debian.net>
+ * Copyright (C) 2000		Andreas E. Bombe
+ *               2001-2003	Ben Collins <bcollins@debian.net>
  *
  * This code is licensed under the GPL.  See the file COPYING in the root
  * directory of the kernel sources for details.
@@ -17,9 +17,7 @@
 #include <linux/kmod.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
-#ifdef CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#endif
+#include <linux/pci.h>
 #include <asm/atomic.h>
 #include <asm/byteorder.h>
 
@@ -32,24 +30,23 @@
 #include "csr.h"
 #include "nodemgr.h"
 
-#ifdef CONFIG_IEEE1394_OUI_DB
-struct oui_list_struct {
-	int oui;
-	char *name;
-};
 
-extern struct oui_list_struct oui_list[];
 
-static char *nodemgr_find_oui_name(int oui) {
+static char *nodemgr_find_oui_name(int oui)
+{
+#ifdef CONFIG_IEEE1394_OUI_DB
+	extern struct oui_list_struct {
+		int oui;
+		char *name;
+	} oui_list[];
 	int i;
 
 	for (i = 0; oui_list[i].name; i++)
 		if (oui_list[i].oui == oui)
 			return oui_list[i].name;
-
+#endif
 	return NULL;
 }
-#endif
 
 /* 
  * Basically what we do here is start off retrieving the bus_info block.
@@ -60,163 +57,469 @@
  * complete directory entry (be it a leaf or a directory). We then process
  * it and add the info to our structure for that particular node.
  *
- * We verify CRC's along the way for each directory/block/leaf. The
- * entire node structure is generic, and simply stores the information in
- * a way that's easy to parse by the protocol interface.
+ * We verify CRC's along the way for each directory/block/leaf. The entire
+ * node structure is generic, and simply stores the information in a way
+ * that's easy to parse by the protocol interface.
  */
 
-/* The nodemgr maintains a number of data structures: the node list,
- * the driver list, unit directory list and the host info list.  The
- * first three lists are accessed from process context only: /proc
- * readers, insmod and rmmod, and the nodemgr thread.  Access to these
- * lists are serialized by means of the nodemgr_serialize mutex, which
- * must be taken before accessing the structures and released
- * afterwards.  The host info list is only accessed during insmod,
- * rmmod and from interrupt and allways only for a short period of
- * time, so a spinlock is used to protect this list.
+/* 
+ * The nodemgr relies heavily on the Drive Model for device callbacks and
+ * driver/device mappings. The old nodemgr used to handle all this itself,
+ * but now we are much simpler because of the LDM.
  */
 
 static DECLARE_MUTEX(nodemgr_serialize);
-static LIST_HEAD(node_list);
-static LIST_HEAD(driver_list);
-static LIST_HEAD(unit_directory_list);
 
 static LIST_HEAD(host_info_list);
 static spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED;
 
-/* Disables use of the hotplug calls.  */
-static int nodemgr_disable_hotplug = 0;
-
 struct host_info {
 	struct hpsb_host *host;
 	struct list_head list;
 	struct completion exited;
 	struct semaphore reset_sem;
 	int pid;
+	int id;
+	char daemon_name[15];
+};
+
+
+#define fw_attr(class, class_type, field, type, format_string)		\
+static ssize_t fw_show_##class##_##field (struct device *dev, char *buf)\
+{									\
+	class_type *class;						\
+	class = container_of(dev, class_type, device);			\
+	return sprintf(buf, format_string, (type)class->field);		\
+}									\
+static struct device_attribute dev_attr_##class##_##field = {		\
+	.attr = {.name = __stringify(field), .mode = S_IRUGO },		\
+	.show   = fw_show_##class##_##field,				\
 };
 
-#ifdef CONFIG_PROC_FS
 
-#define PUTF(fmt, args...) out += sprintf(out, fmt, ## args)
+#define fw_drv_attr(field, type, format_string)			\
+static ssize_t fw_drv_show_##field (struct device_driver *drv, char *buf) \
+{								\
+	struct hpsb_protocol_driver *driver;			\
+	driver = container_of(drv, struct hpsb_protocol_driver, driver); \
+	return sprintf(buf, format_string, (type)driver->field);\
+}								\
+static struct driver_attribute driver_attr_drv_##field = {	\
+        .attr = {.name = __stringify(field), .mode = S_IRUGO },	\
+        .show   = fw_drv_show_##field,				\
+};
+
 
-static int raw1394_read_proc(char *page, char **start, off_t off,
-			     int count, int *eof, void *data)
+static ssize_t fw_show_ne_bus_options(struct device *dev, char *buf)
 {
-	struct list_head *lh;
-	struct node_entry *ne;
-	int len;
-	char *out = page;
-	unsigned long flags;
+	struct node_entry *ne = container_of(dev, struct node_entry, device);
 
-	if (down_interruptible(&nodemgr_serialize))
-		return -EINTR;
+	return sprintf(buf, "IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d) "
+			"LSPD(%d) MAX_REC(%d) CYC_CLK_ACC(%d)\n", ne->busopt.irmc,
+			ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
+			ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd,
+			ne->busopt.max_rec, ne->busopt.cyc_clk_acc);
+}
+static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
 
-	list_for_each(lh, &node_list) {
-		struct list_head *l;
-		int ud_count = 0;
 
-		ne = list_entry(lh, struct node_entry, list);
-		if (!ne)
-			continue;
+static ssize_t fw_show_ne_tlabels_free(struct device *dev, char *buf)
+{
+	struct node_entry *ne = container_of(dev, struct node_entry, device);
+	return sprintf(buf, "%d\n", atomic_read(&ne->tpool->count.count) + 1);
+}
+static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
 
-		PUTF("Node[" NODE_BUS_FMT "]  GUID[%016Lx]:\n",
-		     NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
 
-		/* Generic Node information */
-		PUTF("  Vendor ID   : `%s' [0x%06x]\n", ne->oui_name, ne->vendor_id);
-		if (ne->vendor_name)
-			PUTF("  Vendor text : `%s'\n", ne->vendor_name);
-		PUTF("  Capabilities: 0x%06x\n", ne->capabilities);
-		PUTF("  Tlabel stats:\n");
-		spin_lock_irqsave(&ne->tpool->lock, flags);
-		PUTF("    Free  : %d\n", atomic_read(&ne->tpool->count.count) + 1);
-		PUTF("    Total : %u\n", ne->tpool->allocations);
-		PUTF("    Mask  : %016Lx\n", (unsigned long long)ne->tpool->pool);
-		spin_unlock_irqrestore(&ne->tpool->lock, flags);
-		PUTF("  Bus Options :\n");
-		PUTF("    IRMC(%d) CMC(%d) ISC(%d) BMC(%d) PMC(%d) GEN(%d)\n"
-		     "    LSPD(%d) MAX_REC(%d) CYC_CLK_ACC(%d)\n",
-		     ne->busopt.irmc, ne->busopt.cmc, ne->busopt.isc, ne->busopt.bmc,
-		     ne->busopt.pmc, ne->busopt.generation, ne->busopt.lnkspd,
-		     ne->busopt.max_rec, ne->busopt.cyc_clk_acc);
-
-		/* If this is the host entry, output some info about it aswell */
-		if (ne->host != NULL && ne->host->node_id == ne->nodeid) {
-			PUTF("  Host Node Status:\n");
-			PUTF("    Host Driver     : %s\n", ne->host->driver->name);
-			PUTF("    Nodes connected : %d\n", ne->host->node_count);
-			PUTF("    Nodes active    : %d\n", ne->host->nodes_active);
-			PUTF("    SelfIDs received: %d\n", ne->host->selfid_count);
-			PUTF("    Irm ID          : [" NODE_BUS_FMT "]\n",
-			     NODE_BUS_ARGS(ne->host->irm_id));
-			PUTF("    BusMgr ID       : [" NODE_BUS_FMT "]\n",
-			     NODE_BUS_ARGS(ne->host->busmgr_id));
-			PUTF("    In Bus Reset    : %s\n", ne->host->in_bus_reset ? "yes" : "no");
-			PUTF("    Root            : %s\n", ne->host->is_root ? "yes" : "no");
-			PUTF("    Cycle Master    : %s\n", ne->host->is_cycmst ? "yes" : "no");
-			PUTF("    IRM             : %s\n", ne->host->is_irm ? "yes" : "no");
-			PUTF("    Bus Manager     : %s\n", ne->host->is_busmgr ? "yes" : "no");
-		}
-
-		/* Now the unit directories */
-		list_for_each (l, &ne->unit_directories) {
-			struct unit_directory *ud = list_entry (l, struct unit_directory, node_list);
-			int printed = 0; // small hack
-
-			PUTF("  Unit Directory %d:\n", ud_count++);
-
-			if (ud->flags & UNIT_DIRECTORY_VENDOR_ID ||
-			    ud->flags & UNIT_DIRECTORY_MODEL_ID) {
-				PUTF("    Vendor/Model ID  : ");
-			}
-			if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) {
-				PUTF("%s [%06x]", ud->vendor_name ?: "Unknown",
-				     ud->vendor_id);
-				printed = 1;
-			}
-			if (ud->flags & UNIT_DIRECTORY_MODEL_ID) {
-				if (!printed) {
-					PUTF("%s [%06x]", ne->vendor_name ?: "Unknown",
-					     ne->vendor_id);
-				}
-				PUTF(" / %s [%06x]", ud->model_name ?: "Unknown", ud->model_id);
-				printed = 1;
-			}
-			if (printed)
-				PUTF("\n");
+static ssize_t fw_show_ne_tlabels_allocations(struct device *dev, char *buf)
+{
+	struct node_entry *ne = container_of(dev, struct node_entry, device);
+	return sprintf(buf, "%u\n", ne->tpool->allocations);
+}
+static DEVICE_ATTR(tlabels_allocations,S_IRUGO,fw_show_ne_tlabels_allocations,NULL);
+
+
+static ssize_t fw_show_ne_tlabels_mask(struct device *dev, char *buf)
+{
+	struct node_entry *ne = container_of(dev, struct node_entry, device);
+#if (BITS_PER_LONG <= 32)
+	return sprintf(buf, "0x%08lx%08lx\n", ne->tpool->pool[0], ne->tpool->pool[1]);
+#else
+	return sprintf(buf, "0x%016lx\n", ne->tpool->pool[0]);
+#endif
+}
+static DEVICE_ATTR(tlabels_mask,S_IRUGO,fw_show_ne_tlabels_mask,NULL);
+
+
+fw_attr(ne, struct node_entry, capabilities, unsigned int, "0x%06x\n")
+fw_attr(ne, struct node_entry, nodeid, unsigned int, "0x%04x\n")
+
+fw_attr(ne, struct node_entry, vendor_id, unsigned int, "0x%06x\n")
+fw_attr(ne, struct node_entry, vendor_name, const char *, "%s\n")
+fw_attr(ne, struct node_entry, vendor_oui, const char *, "%s\n")
+
+fw_attr(ne, struct node_entry, guid, unsigned long long, "0x%016Lx\n")
+fw_attr(ne, struct node_entry, guid_vendor_id, unsigned int, "0x%06x\n")
+fw_attr(ne, struct node_entry, guid_vendor_oui, const char *, "%s\n")
+
+static struct device_attribute *const fw_ne_attrs[] = {
+	&dev_attr_ne_guid,
+	&dev_attr_ne_guid_vendor_id,
+	&dev_attr_ne_capabilities,
+	&dev_attr_ne_vendor_id,
+	&dev_attr_ne_nodeid,
+	&dev_attr_bus_options,
+	&dev_attr_tlabels_free,
+	&dev_attr_tlabels_allocations,
+	&dev_attr_tlabels_mask,
+};
+
+
+
+fw_attr(ud, struct unit_directory, address, unsigned long long, "0x%016Lx\n")
+fw_attr(ud, struct unit_directory, length, int, "%d\n")
+/* These are all dependent on the value being provided */
+fw_attr(ud, struct unit_directory, vendor_id, unsigned int, "0x%06x\n")
+fw_attr(ud, struct unit_directory, model_id, unsigned int, "0x%06x\n")
+fw_attr(ud, struct unit_directory, specifier_id, unsigned int, "0x%06x\n")
+fw_attr(ud, struct unit_directory, version, unsigned int, "0x%06x\n")
+fw_attr(ud, struct unit_directory, vendor_name, const char *, "%s\n")
+fw_attr(ud, struct unit_directory, vendor_oui, const char *, "%s\n")
+fw_attr(ud, struct unit_directory, model_name, const char *, "%s\n")
+
+static struct device_attribute *const fw_ud_attrs[] = {
+	&dev_attr_ud_address,
+	&dev_attr_ud_length,
+};
+
+
+fw_attr(host, struct hpsb_host, node_count, int, "%d\n")
+fw_attr(host, struct hpsb_host, selfid_count, int, "%d\n")
+fw_attr(host, struct hpsb_host, nodes_active, int, "%d\n")
+fw_attr(host, struct hpsb_host, in_bus_reset, int, "%d\n")
+fw_attr(host, struct hpsb_host, is_root, int, "%d\n")
+fw_attr(host, struct hpsb_host, is_cycmst, int, "%d\n")
+fw_attr(host, struct hpsb_host, is_irm, int, "%d\n")
+fw_attr(host, struct hpsb_host, is_busmgr, int, "%d\n")
+
+static struct device_attribute *const fw_host_attrs[] = {
+	&dev_attr_host_node_count,
+	&dev_attr_host_selfid_count,
+	&dev_attr_host_nodes_active,
+	&dev_attr_host_in_bus_reset,
+	&dev_attr_host_is_root,
+	&dev_attr_host_is_cycmst,
+	&dev_attr_host_is_irm,
+	&dev_attr_host_is_busmgr,
+};
+
+
+static ssize_t fw_show_drv_device_ids(struct device_driver *drv, char *buf)
+{
+	struct hpsb_protocol_driver *driver;
+	struct ieee1394_device_id *id;
+	int length = 0;
+	char *scratch = buf;
+
+        driver = container_of(drv, struct hpsb_protocol_driver, driver);
+
+	for (id = driver->id_table; id->match_flags != 0; id++) {
+		int need_coma = 0;
+
+		if (id->match_flags & IEEE1394_MATCH_VENDOR_ID) {
+			length += sprintf(scratch, "vendor_id=0x%06x", id->vendor_id);
+			scratch = buf + length;
+			need_coma++;
+		}
+
+		if (id->match_flags & IEEE1394_MATCH_MODEL_ID) {
+			length += sprintf(scratch, "%smodel_id=0x%06x",
+					  need_coma++ ? "," : "",
+					  id->model_id);
+			scratch = buf + length;
+		}
+
+		if (id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) {
+			length += sprintf(scratch, "%sspecifier_id=0x%06x",
+					  need_coma++ ? "," : "",
+					  id->specifier_id);
+			scratch = buf + length;
+		}
 
-			if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
-				PUTF("    Software Spec ID : %06x\n", ud->specifier_id);
-			if (ud->flags & UNIT_DIRECTORY_VERSION)
-				PUTF("    Software Version : %06x\n", ud->version);
-			if (ud->driver)
-				PUTF("    Driver           : %s\n", ud->driver->name);
-			PUTF("    Length (in quads): %d\n", ud->count);
+		if (id->match_flags & IEEE1394_MATCH_VERSION) {
+			length += sprintf(scratch, "%sversion=0x%06x",
+					  need_coma++ ? "," : "",
+					  id->version);
+			scratch = buf + length;
 		}
 
+		if (need_coma) {
+			*scratch++ = '\n';
+			length++;
+		}
 	}
 
-	up(&nodemgr_serialize);
+	return length;
+}
+static DRIVER_ATTR(device_ids,S_IRUGO,fw_show_drv_device_ids,NULL);
 
-	len = out - page;
-	len -= off;
-	if (len < count) {
-		*eof = 1;
-		if (len <= 0)
-			return 0;
-	} else
-		len = count;
 
-        *start = page + off;
+fw_drv_attr(name, const char *, "%s\n")
+
+static struct driver_attribute *const fw_drv_attrs[] = {
+	&driver_attr_drv_name,
+	&driver_attr_device_ids,
+};
+
+
+static void nodemgr_create_drv_files(struct hpsb_protocol_driver *driver)
+{
+	struct device_driver *drv = &driver->driver;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++)
+		driver_create_file(drv, fw_drv_attrs[i]);
+}
+
+
+static void nodemgr_remove_drv_files(struct hpsb_protocol_driver *driver)
+{
+	struct device_driver *drv = &driver->driver;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fw_drv_attrs); i++)
+		driver_remove_file(drv, fw_drv_attrs[i]);
+}
+
 
-	return len;
+static void nodemgr_create_ne_dev_files(struct node_entry *ne)
+{
+	struct device *dev = &ne->device;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++)
+		device_create_file(dev, fw_ne_attrs[i]);
+}
+
+
+static void nodemgr_create_host_dev_files(struct hpsb_host *host)
+{
+	struct device *dev = &host->device;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fw_host_attrs); i++)
+		device_create_file(dev, fw_host_attrs[i]);
+}
+
+
+static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid);
+
+static void nodemgr_update_host_dev_links(struct hpsb_host *host)
+{
+	struct device *dev = &host->device;
+	struct node_entry *ne;
+
+	sysfs_remove_link(&dev->kobj, "irm_id");
+	sysfs_remove_link(&dev->kobj, "busmgr_id");
+	sysfs_remove_link(&dev->kobj, "host_id");
+
+	if ((ne = find_entry_by_nodeid(host, host->irm_id)))
+		sysfs_create_link(&dev->kobj, &ne->device.kobj, "irm_id");
+	if ((ne = find_entry_by_nodeid(host, host->busmgr_id)))
+		sysfs_create_link(&dev->kobj, &ne->device.kobj, "busmgr_id");
+	if ((ne = find_entry_by_nodeid(host, host->node_id)))
+		sysfs_create_link(&dev->kobj, &ne->device.kobj, "host_id");
 }
 
-#undef PUTF
-#endif /* CONFIG_PROC_FS */
+static void nodemgr_create_ud_dev_files(struct unit_directory *ud)
+{
+	struct device *dev = &ud->device;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fw_ud_attrs); i++)
+		device_create_file(dev, fw_ud_attrs[i]);
+
+	if (ud->flags & UNIT_DIRECTORY_SPECIFIER_ID)
+		device_create_file(dev, &dev_attr_ud_specifier_id);
+
+	if (ud->flags & UNIT_DIRECTORY_VERSION)
+		device_create_file(dev, &dev_attr_ud_version);
+
+	if (ud->flags & UNIT_DIRECTORY_VENDOR_ID) {
+		device_create_file(dev, &dev_attr_ud_vendor_id);
+		if (ud->flags & UNIT_DIRECTORY_VENDOR_TEXT)
+			device_create_file(dev, &dev_attr_ud_vendor_name);
+	}
+
+	if (ud->flags & UNIT_DIRECTORY_MODEL_ID) {
+		device_create_file(dev, &dev_attr_ud_model_id);
+		if (ud->flags & UNIT_DIRECTORY_MODEL_TEXT)
+			device_create_file(dev, &dev_attr_ud_model_name);
+	}
+}
+
+
+static int nodemgr_bus_match(struct device * dev, struct device_driver * drv)
+{
+        struct hpsb_protocol_driver *driver;
+        struct unit_directory *ud;
+	struct ieee1394_device_id *id;
+
+	if (dev->class_num != DEV_CLASS_UNIT_DIRECTORY)
+		return 0;
+
+	ud = container_of(dev, struct unit_directory, device);
+	driver = container_of(drv, struct hpsb_protocol_driver, driver);
+
+        for (id = driver->id_table; id->match_flags != 0; id++) {
+                if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
+                    id->vendor_id != ud->vendor_id)
+                        continue;
+
+                if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) &&
+                    id->model_id != ud->model_id)
+                        continue;
+
+                if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) &&
+                    id->specifier_id != ud->specifier_id)
+                        continue;
+
+                if ((id->match_flags & IEEE1394_MATCH_VERSION) &&
+                    id->version != ud->version)
+                        continue;
+
+		return 1;
+        }
+
+	return 0;
+}
+
+
+static void nodemgr_release_ud(struct device *dev)
+{
+	kfree(container_of(dev, struct unit_directory, device));
+}
+
+
+static void nodemgr_release_ne(struct device *dev)
+{
+	kfree(container_of(dev, struct node_entry, device));
+}
+
+
+static void nodemgr_remove_ud(struct unit_directory *ud)
+{
+	struct device *dev = &ud->device;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fw_ud_attrs); i++)
+		device_remove_file(dev, fw_ud_attrs[i]);
+
+	device_remove_file(dev, &dev_attr_ud_specifier_id);
+	device_remove_file(dev, &dev_attr_ud_version);
+	device_remove_file(dev, &dev_attr_ud_vendor_id);
+	device_remove_file(dev, &dev_attr_ud_vendor_name);
+	device_remove_file(dev, &dev_attr_ud_vendor_oui);
+	device_remove_file(dev, &dev_attr_ud_model_id);
+	device_remove_file(dev, &dev_attr_ud_model_name);
+
+	device_unregister(dev);
+}
+
+
+static void nodemgr_remove_node_uds(struct node_entry *ne)
+{
+	struct list_head *lh, *next;
+
+	list_for_each_safe(lh, next, &ne->device.children) {
+		struct unit_directory *ud;
+		ud = container_of(list_to_dev(lh), struct unit_directory, device);
+		nodemgr_remove_ud(ud);
+	}
+}
+
+
+static void nodemgr_update_ud_names(struct host_info *hi, struct node_entry *ne)
+{
+	struct list_head *lh;
+
+	list_for_each(lh, &ne->device.children) {
+		struct unit_directory *ud;
+		ud = container_of(list_to_dev(lh), struct unit_directory, device);
+
+		snprintf(ud->device.name, DEVICE_NAME_SIZE,
+			 "IEEE-1394 unit directory %d-" NODE_BUS_FMT "-%u",
+			 hi->id, NODE_BUS_ARGS(ne->nodeid), ud->id);
+	}
+}
+
+
+static void nodemgr_remove_ne(struct node_entry *ne)
+{
+	struct device *dev = &ne->device;
+	int i;
+
+	nodemgr_remove_node_uds(ne);
+
+	for (i = 0; i < ARRAY_SIZE(fw_ne_attrs); i++)
+		device_remove_file(dev, fw_ne_attrs[i]);
+
+	device_remove_file(dev, &dev_attr_ne_guid_vendor_oui);
+	device_remove_file(dev, &dev_attr_ne_vendor_name);
+	device_remove_file(dev, &dev_attr_ne_vendor_oui);
+
+	device_unregister(dev);
+}
+
+
+static void nodemgr_remove_host_dev(struct device *dev)
+{
+	int i;
+	struct list_head *lh, *next;
+
+	list_for_each_safe(lh, next, &dev->children) {
+		struct node_entry *ne;
+		ne = container_of(list_to_dev(lh), struct node_entry, device);
+		nodemgr_remove_ne(ne);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fw_host_attrs); i++)
+		device_remove_file(dev, fw_host_attrs[i]);
+
+	sysfs_remove_link(&dev->kobj, "irm_id");
+	sysfs_remove_link(&dev->kobj, "busmgr_id");
+	sysfs_remove_link(&dev->kobj, "host_id");
+}
+
+
+static struct device nodemgr_dev_template_ud = {
+	.bus		= &ieee1394_bus_type,
+	.release	= nodemgr_release_ud,
+	.class_num	= DEV_CLASS_UNIT_DIRECTORY,
+};
+
+static struct device nodemgr_dev_template_ne = {
+	.bus		= &ieee1394_bus_type,
+	.release	= nodemgr_release_ne,
+	.class_num	= DEV_CLASS_NODE,
+};
+
+static struct device nodemgr_dev_template_host = {
+	.bus		= &ieee1394_bus_type,
+	.class_num	= DEV_CLASS_HOST,
+};
+
+
+static int nodemgr_hotplug(struct device *dev, char **envp, int num_envp,
+                           char *buffer, int buffer_size);
+
+
+struct bus_type ieee1394_bus_type = {
+	.name		= "ieee1394",
+	.match		= nodemgr_bus_match,
+	.hotplug	= nodemgr_hotplug,
+};
 
-static void nodemgr_process_config_rom(struct node_entry *ne, 
-				       quadlet_t busoptions);
 
 static int nodemgr_read_quadlet(struct hpsb_host *host,
 				nodeid_t nodeid, unsigned int generation,
@@ -292,7 +595,8 @@
 	ret = -ENXIO;
 	for (; size > 0; size--, address += 4, quadp++) {
 		for (i = 0; i < 3; i++) {
-			ret = hpsb_read(ne->host, ne->nodeid, ne->generation, address, quadp, 4);
+			
+			ret = hpsb_node_read(ne, address, quadp, 4);
 			if (ret != -EAGAIN)
 				break;
 		}
@@ -349,9 +653,10 @@
 	if (!ne)
 		return NULL;
 
+	memset(ne, 0, total_size);
+
 	if (size != 0) {
-		ne->vendor_name
-			= (const char *) &(ne->quadlets[2]);
+		ne->vendor_name = (const char *) &(ne->quadlets[2]);
 		ne->quadlets[size] = 0;
 	} else {
 		ne->vendor_name = NULL;
@@ -360,62 +665,129 @@
 	return ne; 
 }
 
+
+static void nodemgr_process_config_rom(struct host_info *hi,
+		struct node_entry *ne, quadlet_t busoptions);
+
 static struct node_entry *nodemgr_create_node(octlet_t guid, quadlet_t busoptions,
-					      struct hpsb_host *host,
-					      nodeid_t nodeid, unsigned int generation)
+					      struct host_info *hi, nodeid_t nodeid,
+					      unsigned int generation)
 {
+	struct hpsb_host *host = hi->host;
         struct node_entry *ne;
 
 	ne = nodemgr_scan_root_directory (host, nodeid, generation);
         if (!ne) return NULL;
 
-        INIT_LIST_HEAD(&ne->list);
-	INIT_LIST_HEAD(&ne->unit_directories);
-
 	ne->tpool = &host->tpool[nodeid & NODE_MASK];
 
         ne->host = host;
         ne->nodeid = nodeid;
-        ne->guid = guid;
 	ne->generation = generation;
 
-        list_add_tail(&ne->list, &node_list);
+        ne->guid = guid;
+	ne->guid_vendor_id = (guid >> 40) & 0xffffff;
+	ne->guid_vendor_oui = nodemgr_find_oui_name(ne->guid_vendor_id);
+
+	memcpy(&ne->device, &nodemgr_dev_template_ne,
+	       sizeof(ne->device));
+	ne->device.parent = &host->device;
+	snprintf(ne->device.bus_id, BUS_ID_SIZE, "%016Lx",
+		 (unsigned long long)(ne->guid));
+	snprintf(ne->device.name, DEVICE_NAME_SIZE,
+		 "IEEE-1394 device %d-" NODE_BUS_FMT, hi->id,
+		 NODE_BUS_ARGS(ne->nodeid));
+
+	device_register(&ne->device);
 
-	nodemgr_process_config_rom (ne, busoptions);
+	if (ne->guid_vendor_oui)
+		device_create_file(&ne->device, &dev_attr_ne_guid_vendor_oui);
+	nodemgr_create_ne_dev_files(ne);
 
-	HPSB_DEBUG("%s added: ID:BUS[" NODE_BUS_FMT "]  GUID[%016Lx]  [%s] (%s)",
+	nodemgr_process_config_rom (hi, ne, busoptions);
+
+	nodemgr_update_ud_names(hi, ne);
+
+	HPSB_DEBUG("%s added: ID:BUS[%d-" NODE_BUS_FMT "]  GUID[%016Lx]",
 		   (host->node_id == nodeid) ? "Host" : "Node",
-		   NODE_BUS_ARGS(nodeid), (unsigned long long)guid,
-		   ne->oui_name,
-		   ne->vendor_name ?: "Unknown");
+		   hi->id, NODE_BUS_ARGS(nodeid), (unsigned long long)guid);
 
         return ne;
 }
 
-static struct node_entry *find_entry_by_guid(u64 guid)
+
+struct guid_search_baton {
+	u64 guid;
+	struct node_entry *ne;
+};
+
+static int nodemgr_guid_search_cb(struct device *dev, void *__data)
 {
-        struct list_head *lh;
+        struct guid_search_baton *search = __data;
         struct node_entry *ne;
-        
-        list_for_each(lh, &node_list) {
-                ne = list_entry(lh, struct node_entry, list);
-                if (ne->guid == guid) return ne;
-        }
 
-        return NULL;
+        if (dev->class_num != DEV_CLASS_NODE)
+                return 0;
+
+        ne = container_of(dev, struct node_entry, device);
+
+        if (ne->guid == search->guid) {
+		search->ne = ne;
+		return 1;
+	}
+
+	return 0;
 }
 
-static struct node_entry *find_entry_by_nodeid(nodeid_t nodeid)
+static struct node_entry *find_entry_by_guid(u64 guid)
 {
-	struct list_head *lh;
+	struct guid_search_baton search;
+
+	search.guid = guid;
+	search.ne = NULL;
+
+	bus_for_each_dev(&ieee1394_bus_type, NULL, &search, nodemgr_guid_search_cb);
+
+        return search.ne;
+}
+
+
+struct nodeid_search_baton {
+	nodeid_t nodeid;
 	struct node_entry *ne;
+	struct hpsb_host *host;
+};
+
+static int nodemgr_nodeid_search_cb(struct device *dev, void *__data)
+{
+	struct nodeid_search_baton *search = __data;
+	struct node_entry *ne;
+
+	if (dev->class_num != DEV_CLASS_NODE)
+		return 0;
+
+	ne = container_of(dev, struct node_entry, device);
 
-	list_for_each(lh, &node_list) {
-		ne = list_entry(lh, struct node_entry, list);
-		if (ne->nodeid == nodeid) return ne;
+	if (ne->host == search->host && ne->nodeid == search->nodeid) {
+		search->ne = ne;
+		/* Returning 1 stops the iteration */
+		return 1;
 	}
 
-	return NULL;
+	return 0;
+}
+
+static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host, nodeid_t nodeid)
+{
+	struct nodeid_search_baton search;
+
+	search.nodeid = nodeid;
+	search.ne = NULL;
+	search.host = host;
+
+	bus_for_each_dev(&ieee1394_bus_type, NULL, &search, nodemgr_nodeid_search_cb);
+
+	return search.ne;
 }
 
 static struct unit_directory *nodemgr_scan_unit_directory
@@ -500,28 +872,27 @@
 				return NULL;
 		}
 	}
+
 	total_size += count * sizeof (quadlet_t);
 	ud = kmalloc (total_size, GFP_KERNEL);
+
 	if (ud != NULL) {
-		memset (ud, 0, sizeof *ud);
+		memset (ud, 0, total_size);
 		ud->flags = flags;
-		ud->count = count;
+		ud->length = count;
 		ud->vendor_name_size = vendor_name_size;
 		ud->model_name_size = model_name_size;
-		/* If there is no vendor name in the unit directory,
-		   use the one in the root directory.  */
-		ud->vendor_name = ne->vendor_name;
 	}
+
 	return ud;
 }
 
+
 /* This implementation currently only scans the config rom and its
  * immediate unit directories looking for software_id and
- * software_version entries, in order to get driver autoloading working.
- */
-
-static void nodemgr_process_unit_directory(struct node_entry *ne, 
-					   octlet_t address)
+ * software_version entries, in order to get driver autoloading working. */
+static void nodemgr_process_unit_directory(struct host_info *hi, struct node_entry *ne, 
+					   octlet_t address, unsigned int id)
 {
 	struct unit_directory *ud;
 	quadlet_t quad;
@@ -533,6 +904,7 @@
 
 	ud->ne = ne;
 	ud->address = address;
+	ud->id = id;
 
 	if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation,
 				 address, &quad))
@@ -556,14 +928,16 @@
 		case CONFIG_ROM_VENDOR_ID:
 			ud->vendor_id = value;
 			ud->flags |= UNIT_DIRECTORY_VENDOR_ID;
+
+			if (ud->vendor_id)
+				ud->vendor_oui = nodemgr_find_oui_name(ud->vendor_id);
+
 			if ((ud->flags & UNIT_DIRECTORY_VENDOR_TEXT) != 0) {
 				length--;
 				address += 4;
-				quadp = &(ud->quadlets[ud->count]);
-				if (nodemgr_read_text_leaf(ne, address,
-							   quadp) == 0
-				    && quadp[0] == 0
-				    && quadp[1] == 0) {
+				quadp = &(ud->quadlets[ud->length]);
+				if (nodemgr_read_text_leaf(ne, address, quadp) == 0
+				    && quadp[0] == 0 && quadp[1] == 0) {
 				    	/* We only support minimal
 					   ASCII and English. */
 					quadp[ud->vendor_name_size] = 0;
@@ -579,11 +953,9 @@
 			if ((ud->flags & UNIT_DIRECTORY_MODEL_TEXT) != 0) {
 				length--;
 				address += 4;
-				quadp = &(ud->quadlets[ud->count + ud->vendor_name_size + 1]);
-				if (nodemgr_read_text_leaf(ne, address,
-							   quadp) == 0
-				    && quadp[0] == 0
-				    && quadp[1] == 0) {
+				quadp = &(ud->quadlets[ud->length + ud->vendor_name_size + 1]);
+				if (nodemgr_read_text_leaf(ne, address, quadp) == 0
+				    && quadp[0] == 0 && quadp[1] == 0) {
 				    	/* We only support minimal
 					   ASCII and English. */
 					quadp[ud->model_name_size] = 0;
@@ -619,8 +991,17 @@
 		}
 	}
 
-	list_add_tail(&ud->node_list, &ne->unit_directories);
-	list_add_tail(&ud->driver_list, &unit_directory_list);
+	memcpy(&ud->device, &nodemgr_dev_template_ud,
+	       sizeof(ud->device));
+	ud->device.parent = &ne->device;
+	snprintf(ud->device.bus_id, BUS_ID_SIZE, "%s-%u",
+		 ne->device.bus_id, ud->id);
+
+	device_register(&ud->device);
+
+	if (ud->vendor_oui)
+		device_create_file(&ud->device, &dev_attr_ud_vendor_oui);
+	nodemgr_create_ud_dev_files(ud);
 
 	return;
 
@@ -657,11 +1038,12 @@
 	return;
 }
 
-static void nodemgr_process_root_directory(struct node_entry *ne)
+static void nodemgr_process_root_directory(struct host_info *hi, struct node_entry *ne)
 {
 	octlet_t address;
 	quadlet_t quad;
 	int length;
+	unsigned int ud_id = 0;
 
 	address = CSR_REGISTER_BASE + CSR_CONFIG_ROM;
 	
@@ -688,24 +1070,23 @@
 		switch (code) {
 		case CONFIG_ROM_VENDOR_ID:
 			ne->vendor_id = value;
-#ifdef CONFIG_IEEE1394_OUI_DB
-			ne->oui_name = nodemgr_find_oui_name(value);
-#else
-			ne->oui_name = "Unknown";
-#endif
+
+			if (ne->vendor_id)
+				ne->vendor_oui = nodemgr_find_oui_name(ne->vendor_id);
+
 			/* Now check if there is a vendor name text
 			   string.  */
 			if (ne->vendor_name != NULL) {
 				length--;
 				address += 4;
-				if (nodemgr_read_text_leaf(ne, address,
-							   ne->quadlets)
-				    != 0
-				    || ne->quadlets [0] != 0
-				    || ne->quadlets [1] != 0)
+				if (nodemgr_read_text_leaf(ne, address, ne->quadlets) != 0
+				    || ne->quadlets[0] != 0 || ne->quadlets[1] != 0)
 				    	/* We only support minimal
 					   ASCII and English. */
 					ne->vendor_name = NULL;
+				else
+					device_create_file(&ne->device,
+						&dev_attr_ne_vendor_name);
 			}
 			break;
 
@@ -714,7 +1095,7 @@
 			break;
 
 		case CONFIG_ROM_UNIT_DIRECTORY:
-			nodemgr_process_unit_directory(ne, address + value * 4);
+			nodemgr_process_unit_directory(hi, ne, address + value * 4, ud_id++);
 			break;			
 
 		case CONFIG_ROM_DESCRIPTOR_LEAF:
@@ -729,208 +1110,94 @@
 
 #ifdef CONFIG_HOTPLUG
 
-static void nodemgr_call_policy(char *verb, struct unit_directory *ud)
+static int nodemgr_hotplug(struct device *dev, char **envp, int num_envp,
+			   char *buffer, int buffer_size)
 {
-	char *argv [3], **envp, *buf, *scratch;
-	int i = 0, value;
-
-	/* User requested to disable hotplug when module was loaded. */
-	if (nodemgr_disable_hotplug)
-		return;
-
-	if (!hotplug_path [0])
-		return;
-	if (!current->fs->root)
-		return;
-	if (!(envp = (char **) kmalloc(20 * sizeof (char *), GFP_KERNEL))) {
-		HPSB_DEBUG ("ENOMEM");
-		return;
-	}
-	if (!(buf = kmalloc(256, GFP_KERNEL))) {
-		kfree(envp);
-		HPSB_DEBUG("ENOMEM2");
-		return;
-	}
+	struct unit_directory *ud;
+	char *scratch;
+	int i = 0;
+	int length = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (dev->class_num != DEV_CLASS_UNIT_DIRECTORY)
+		return -ENODEV;
+
+	ud = container_of(dev, struct unit_directory, device);
+
+	scratch = buffer;
+
+#define PUT_ENVP(fmt,val) 					\
+do {								\
+	envp[i++] = scratch;					\
+	length += snprintf(scratch, buffer_size - length,	\
+			   fmt, val);				\
+	if ((buffer_size - length <= 0) || (i >= num_envp))	\
+		return -ENOMEM;					\
+	++length;						\
+	scratch = buffer + length;				\
+} while(0)
+
+	PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id);
+	PUT_ENVP("MODEL_ID=%06x", ud->model_id);
+	PUT_ENVP("GUID=%016Lx", (unsigned long long)ud->ne->guid);
+	PUT_ENVP("SPECIFIER_ID=%06x", ud->specifier_id);
+	PUT_ENVP("VERSION=%06x", ud->version);
 
-	/* only one standardized param to hotplug command: type */
-	argv[0] = hotplug_path;
-	argv[1] = "ieee1394";
-	argv[2] = 0;
-
-	/* minimal command environment */
-	envp[i++] = "HOME=/";
-	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+#undef PUT_ENVP
 
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-	/* hint that policy agent should enter no-stdout debug mode */
-	envp[i++] = "DEBUG=kernel";
-#endif
-	/* extensible set of named bus-specific parameters,
-	 * supporting multiple driver selection algorithms.
-	 */
-	scratch = buf;
-
-	envp[i++] = scratch;
-	scratch += sprintf(scratch, "ACTION=%s", verb) + 1;
-	envp[i++] = scratch;
-	scratch += sprintf(scratch, "VENDOR_ID=%06x", ud->ne->vendor_id) + 1;
-	envp[i++] = scratch;
-	scratch += sprintf(scratch, "GUID=%016Lx", (long long unsigned)ud->ne->guid) + 1;
-	envp[i++] = scratch;
-	scratch += sprintf(scratch, "SPECIFIER_ID=%06x", ud->specifier_id) + 1;
-	envp[i++] = scratch;
-	scratch += sprintf(scratch, "VERSION=%06x", ud->version) + 1;
-	envp[i++] = 0;
+	envp[i] = 0;
 
-	/* NOTE: user mode daemons can call the agents too */
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-	HPSB_DEBUG("NodeMgr: %s %s %016Lx", argv[0], verb, (long long unsigned)ud->ne->guid);
-#endif
-	value = call_usermodehelper(argv[0], argv, envp, 0);
-	kfree(buf);
-	kfree(envp);
-	if (value != 0)
-		HPSB_DEBUG("NodeMgr: hotplug policy returned %d", value);
+	return 0;
 }
 
 #else
 
-static inline void
-nodemgr_call_policy(char *verb, struct unit_directory *ud)
+static int nodemgr_hotplug(struct device *dev, char **envp, int num_envp,
+			   char *buffer, int buffer_size)
 {
-#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-	HPSB_DEBUG("NodeMgr: nodemgr_call_policy(): hotplug not enabled");
-#endif
-	return;
+	return -ENODEV;
 } 
 
 #endif /* CONFIG_HOTPLUG */
 
-static void nodemgr_claim_unit_directory(struct unit_directory *ud,
-					 struct hpsb_protocol_driver *driver)
-{
-	ud->driver = driver;
-	list_del(&ud->driver_list);
-	list_add_tail(&ud->driver_list, &driver->unit_directories);
-}
-
-static void nodemgr_release_unit_directory(struct unit_directory *ud)
-{
-	ud->driver = NULL;
-	list_del(&ud->driver_list);
-	list_add_tail(&ud->driver_list, &unit_directory_list);
-}
-
-void hpsb_release_unit_directory(struct unit_directory *ud)
-{
-	down(&nodemgr_serialize);
-	nodemgr_release_unit_directory(ud);
-	up(&nodemgr_serialize);
-}
 
-static void nodemgr_free_unit_directories(struct node_entry *ne)
+static int nodemgr_alloc_host_num(void)
 {
+	int hostnum = 0;
+	unsigned long flags;
 	struct list_head *lh;
-	struct unit_directory *ud;
 
-	lh = ne->unit_directories.next;
-	while (lh != &ne->unit_directories) {
-		ud = list_entry(lh, struct unit_directory, node_list);
-		lh = lh->next;
-		if (ud->driver && ud->driver->disconnect)
-			ud->driver->disconnect(ud);
-		nodemgr_release_unit_directory(ud);
-		nodemgr_call_policy("remove", ud);
-		list_del(&ud->driver_list);
-		kfree(ud);
-	}
-}
-
-static struct ieee1394_device_id *
-nodemgr_match_driver(struct hpsb_protocol_driver *driver, 
-		     struct unit_directory *ud)
-{
-	struct ieee1394_device_id *id;
-
-	for (id = driver->id_table; id->match_flags != 0; id++) {
-		if ((id->match_flags & IEEE1394_MATCH_VENDOR_ID) &&
-		    id->vendor_id != ud->vendor_id)
-			continue;
+	spin_lock_irqsave (&host_info_lock, flags);
 
-		if ((id->match_flags & IEEE1394_MATCH_MODEL_ID) &&
-		    id->model_id != ud->model_id)
-			continue;
+	while (1) {
+		int found = 0;
 
-		if ((id->match_flags & IEEE1394_MATCH_SPECIFIER_ID) &&
-		    id->specifier_id != ud->specifier_id)
-			continue;
+		list_for_each(lh, &host_info_list) {
+			struct host_info *hi = list_entry(lh, struct host_info, list);
+			if (hi->id == hostnum) {
+				found = 1;
+				break;
+			}
+		}
 
-		if ((id->match_flags & IEEE1394_MATCH_VERSION) &&
-		    id->version != ud->version)
-			continue;
+		if (!found)
+			break;
 
-		return id;
+		hostnum++;
 	}
 
-	return NULL;
-}
-
-static struct hpsb_protocol_driver *
-nodemgr_find_driver(struct unit_directory *ud)
-{
-	struct list_head *l;
-	struct hpsb_protocol_driver *match, *driver;
-	struct ieee1394_device_id *device_id;
-
-	match = NULL;
-	list_for_each(l, &driver_list) {
-		driver = list_entry(l, struct hpsb_protocol_driver, list);
-		device_id = nodemgr_match_driver(driver, ud);
-
-		if (device_id != NULL) {
-			match = driver;
-			break;
-		}
-	}
+	spin_unlock_irqrestore (&host_info_lock, flags);
 
-	return match;
+	return hostnum;
 }
 
-static void nodemgr_bind_drivers (struct node_entry *ne)
-{
-	struct list_head *lh;
-	struct hpsb_protocol_driver *driver;
-	struct unit_directory *ud;
-
-	list_for_each(lh, &ne->unit_directories) {
-		ud = list_entry(lh, struct unit_directory, node_list);
-		driver = nodemgr_find_driver(ud);
-		if (driver != NULL && driver->probe(ud) == 0)
-			nodemgr_claim_unit_directory(ud, driver);
-		nodemgr_call_policy("add", ud);
-	}
-}
 
 int hpsb_register_protocol(struct hpsb_protocol_driver *driver)
 {
-	struct unit_directory *ud;
-	struct list_head *lh;
-
-	if (down_interruptible(&nodemgr_serialize))
-		return -EINTR;
-
-	list_add_tail(&driver->list, &driver_list);
-
-	INIT_LIST_HEAD(&driver->unit_directories);
-	lh = unit_directory_list.next;
-	while (lh != &unit_directory_list) {
-		ud = list_entry(lh, struct unit_directory, driver_list);
-		lh = lh->next;
-		if (nodemgr_match_driver(driver, ud) && driver->probe(ud) == 0)
-			nodemgr_claim_unit_directory(ud, driver);
-	}
-
-	up(&nodemgr_serialize);
+	driver_register(&driver->driver);
+	nodemgr_create_drv_files(driver);
 
 	/*
 	 * Right now registration always succeeds, but maybe we should
@@ -942,26 +1209,14 @@
 
 void hpsb_unregister_protocol(struct hpsb_protocol_driver *driver)
 {
-	struct list_head *lh;
-	struct unit_directory *ud;
-
-	down(&nodemgr_serialize);
-
-	list_del(&driver->list);
-	lh = driver->unit_directories.next;
-	while (lh != &driver->unit_directories) {
-		ud = list_entry(lh, struct unit_directory, driver_list);
-		lh = lh->next;
-		if (ud->driver && ud->driver->disconnect)
-			ud->driver->disconnect(ud);
-		nodemgr_release_unit_directory(ud);
-	}
-
-	up(&nodemgr_serialize);
+	nodemgr_remove_drv_files(driver);
+	/* This will subsequently disconnect all devices that our driver
+	 * is attached to. */
+	driver_unregister(&driver->driver);
 }
 
-static void nodemgr_process_config_rom(struct node_entry *ne, 
-				       quadlet_t busoptions)
+static void nodemgr_process_config_rom(struct host_info *hi,
+		struct node_entry *ne, quadlet_t busoptions)
 {
 	ne->busopt.irmc		= (busoptions >> 31) & 1;
 	ne->busopt.cmc		= (busoptions >> 30) & 1;
@@ -981,16 +1236,42 @@
 		   ne->busopt.cyc_clk_acc, ne->busopt.max_rec,
 		   ne->busopt.generation, ne->busopt.lnkspd);
 #endif
+	device_remove_file(&ne->device, &dev_attr_ne_vendor_oui);
 
-	/*
-	 * When the config rom changes we disconnect all drivers and
-	 * free the cached unit directories and reread the whole
-	 * thing.  If this was a new device, the call to
-	 * nodemgr_disconnect_drivers is a no-op and all is well.
-	 */
-	nodemgr_free_unit_directories(ne);
-	nodemgr_process_root_directory(ne);
-	nodemgr_bind_drivers(ne);
+	nodemgr_process_root_directory(hi, ne);
+
+	if (ne->vendor_oui)
+		device_create_file(&ne->device, &dev_attr_ne_vendor_oui);
+}
+
+
+/* Searches the list of ud's that match a ne as the parent. If the ud has
+ * a driver associated with it, we call that driver's update function
+ * with the ud as the argument. */
+static int nodemgr_driver_search_cb(struct device *dev, void *__data)
+{
+	struct node_entry *ne = __data;
+	struct unit_directory *ud;
+
+	if (dev->class_num != DEV_CLASS_UNIT_DIRECTORY)
+		return 0;
+
+	ud = container_of(dev, struct unit_directory, device);
+
+	if (&ne->device != ud->device.parent)
+		return 0;
+
+	if (ud->device.driver) {
+		struct hpsb_protocol_driver *pdrv;
+
+		pdrv = container_of(ud->device.driver,
+				struct hpsb_protocol_driver, driver);
+
+		if (pdrv->update)
+			pdrv->update(ud);
+	}
+
+	return 0;
 }
 
 /*
@@ -1001,29 +1282,42 @@
  * the to take whatever actions required.
  */
 static void nodemgr_update_node(struct node_entry *ne, quadlet_t busoptions,
-                               struct hpsb_host *host,
-				nodeid_t nodeid, unsigned int generation)
+				struct host_info *hi, nodeid_t nodeid,
+				unsigned int generation)
 {
-	struct list_head *lh;
-	struct unit_directory *ud;
+	int update_ud_names = 0;
 
 	if (ne->nodeid != nodeid) {
+		snprintf(ne->device.name, DEVICE_NAME_SIZE,
+			 "IEEE-1394 device %d-" NODE_BUS_FMT,
+			 hi->id, NODE_BUS_ARGS(ne->nodeid));
 		HPSB_DEBUG("Node " NODE_BUS_FMT " changed to " NODE_BUS_FMT,
 			   NODE_BUS_ARGS(ne->nodeid), NODE_BUS_ARGS(nodeid));
 		ne->nodeid = nodeid;
+
+		update_ud_names++;
+	}
+
+	if (ne->busopt.generation != ((busoptions >> 4) & 0xf)) {
+		/* If the node's configrom generation has changed, we
+		 * unregister all the unit directories. */
+		nodemgr_remove_node_uds(ne);
+
+		/* This will re-register our unitdir's */
+		nodemgr_process_config_rom (hi, ne, busoptions);
+
+		update_ud_names++;
 	}
 
-	if (ne->busopt.generation != ((busoptions >> 4) & 0xf))
-		nodemgr_process_config_rom (ne, busoptions);
+	if (update_ud_names)
+		nodemgr_update_ud_names(hi, ne);
 
 	/* Since that's done, we can declare this record current */
 	ne->generation = generation;
 
-	list_for_each (lh, &ne->unit_directories) {
-		ud = list_entry (lh, struct unit_directory, node_list);
-		if (ud->driver != NULL && ud->driver->update != NULL)
-			ud->driver->update(ud);
-	}
+	/* Update unit_dirs with attached drivers */
+	bus_for_each_dev(&ieee1394_bus_type, NULL, ne,
+			 nodemgr_driver_search_cb);
 }
 
 static int read_businfo_block(struct hpsb_host *host, nodeid_t nodeid, unsigned int generation,
@@ -1087,24 +1381,13 @@
 	return 0;
 }		
 
-static void nodemgr_remove_node(struct node_entry *ne)
-{
-	HPSB_DEBUG("Device removed: Node[" NODE_BUS_FMT "]  GUID[%016Lx]  [%s]",
-		   NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid,
-		   ne->vendor_name ?: "Unknown");
-
-	nodemgr_free_unit_directories(ne);
-	list_del(&ne->list);
-	kfree(ne);
-
-	return;
-}
 
 /* This is where we probe the nodes for their information and provided
  * features.  */
-static void nodemgr_node_probe_one(struct hpsb_host *host,
+static void nodemgr_node_probe_one(struct host_info *hi,
 				   nodeid_t nodeid, int generation)
 {
+	struct hpsb_host *host = hi->host;
 	struct node_entry *ne;
 	quadlet_t buffer[5];
 	octlet_t guid;
@@ -1132,58 +1415,48 @@
 	ne = find_entry_by_guid(guid);
 
 	if (!ne)
-		nodemgr_create_node(guid, buffer[2], host, nodeid, generation);
+		nodemgr_create_node(guid, buffer[2], hi, nodeid, generation);
 	else
-		nodemgr_update_node(ne, buffer[2], host, nodeid, generation);
+		nodemgr_update_node(ne, buffer[2], hi, nodeid, generation);
 
 	return;
 }
 
-static void nodemgr_node_probe_cleanup(struct hpsb_host *host, unsigned int generation)
+
+struct cleanup_baton {
+	unsigned int generation;
+	struct hpsb_host *host;
+	struct node_entry *ne;
+};
+
+static int nodemgr_remove_node(struct device *dev, void *__data)
 {
-	struct list_head *lh, *next;
+	struct cleanup_baton *cleanup = __data;
 	struct node_entry *ne;
 
-	/* Now check to see if we have any nodes that aren't referenced
-	 * any longer.  */
-	list_for_each_safe(lh, next, &node_list) {
-		ne = list_entry(lh, struct node_entry, list);
+	if (dev->class_num != DEV_CLASS_NODE)
+		return 0;
 
-		/* Only checking this host */
-		if (ne->host != host)
-			continue;
+	ne = container_of(dev, struct node_entry, device);
+
+	if (ne->host != cleanup->host)
+		return 0;
 
-		/* If the generation didn't get updated, then either the
-		 * node was removed, or it failed the above probe. Either
-		 * way, we remove references to it, since they are
-		 * invalid.  */
-		if (ne->generation != generation)
-			nodemgr_remove_node(ne);
+	if (ne->generation != cleanup->generation) {
+		cleanup->ne = ne;
+		return 1;
 	}
 
-	return;
+	return 0;
 }
 
-static void nodemgr_node_probe(struct hpsb_host *host)
+
+static void nodemgr_node_probe(struct host_info *hi, int generation)
 {
 	int count;
+	struct hpsb_host *host = hi->host;
 	struct selfid *sid = (struct selfid *)host->topology_map;
 	nodeid_t nodeid = LOCAL_BUS;
-	unsigned int generation;
-
-	/* Pause for 1/4 second, to make sure things settle down. If
-	 * schedule_timeout returns non-zero, it means we caught a signal
-	 * and need to return. */
-	set_current_state(TASK_INTERRUPTIBLE);
-	if (schedule_timeout (HZ/4))
-		return;
-
-	/* Now get the generation in which the node ID's we collect
-	 * are valid.  During the bus scan we will use this generation
-	 * for the read transactions, so that if another reset occurs
-	 * during the scan the transactions will fail instead of
-	 * returning bogus data. */
-	generation = get_hpsb_generation(host);
 
 	/* Scan each node on the bus */
 	for (count = host->selfid_count; count; count--, sid++) {
@@ -1194,8 +1467,7 @@
 			nodeid++;
 			continue;
 		}
-
-		nodemgr_node_probe_one(host, nodeid++, generation);
+		nodemgr_node_probe_one(hi, nodeid++, generation);
 	}
 
 	/* If we had a bus reset while we were scanning the bus, it is
@@ -1204,8 +1476,33 @@
 	 * were still on the bus.  The bus reset increased
 	 * hi->reset_sem, so there's a bus scan pending which will do
 	 * the clean up eventually. */
-	if (generation == get_hpsb_generation(host))
-		nodemgr_node_probe_cleanup(host, generation);
+	if (generation == get_hpsb_generation(host)) {
+		struct cleanup_baton cleanup;
+
+		cleanup.generation = generation;
+		cleanup.host = host;
+
+		/* This will iterate until all devices that do not match
+		 * the generation are removed. */
+		while (bus_for_each_dev(&ieee1394_bus_type, NULL, &cleanup,
+					nodemgr_remove_node)) {
+			struct node_entry *ne = cleanup.ne;
+
+			HPSB_DEBUG("Device removed: ID:BUS[" NODE_BUS_FMT "]  GUID[%016Lx]",
+				   NODE_BUS_ARGS(ne->nodeid), (unsigned long long)ne->guid);
+
+			nodemgr_remove_ne(ne);
+		}
+
+		/* Now let's tell the bus to rescan our devices. This may
+		 * seem like overhead, but the driver-model core will only
+		 * scan a device for a driver when either the device is
+		 * added, or when a new driver is added. A bus reset is a
+		 * good reason to rescan devices that were there before.
+		 * For example, an sbp2 device may become available for
+		 * login, if the host that held it was just removed. */
+		bus_rescan_devices(&ieee1394_bus_type);
+	}
 
 	return;
 }
@@ -1226,8 +1523,7 @@
 
 	hpsb_write(host, LOCAL_BUS | ALL_NODES, get_hpsb_generation(host),
 		   (CSR_REGISTER_BASE | CSR_BROADCAST_CHANNEL),
-		   &bc,
-		   sizeof(quadlet_t));
+		   &bc, sizeof(quadlet_t));
 }
 
 /* We need to ensure that if we are not the IRM, that the IRM node is capable of
@@ -1249,7 +1545,7 @@
 	if (status < 0 || !(be32_to_cpu(bc) & 0x80000000)) {
 		/* The root node does not have a valid BROADCAST_CHANNEL
 		 * register and we do, so reset the bus with force_root set */
-		HPSB_INFO("Remote root is not IRM capable, resetting...");
+		HPSB_DEBUG("Remote root is not IRM capable, resetting...");
 		hpsb_reset_bus(host, LONG_RESET_FORCE_ROOT);
 		return 0;
 	}
@@ -1259,27 +1555,58 @@
 static int nodemgr_host_thread(void *__hi)
 {
 	struct host_info *hi = (struct host_info *)__hi;
+	struct hpsb_host *host = hi->host;
 
 	/* No userlevel access needed */
-	daemonize("knodemgrd");
+	daemonize(hi->daemon_name);
 	allow_signal(SIGTERM);
-	
+
+	/* Setup our device-model entries */
+	device_register(&host->device);
+	nodemgr_create_host_dev_files(host);
+
 	/* Sit and wait for a signal to probe the nodes on the bus. This
 	 * happens when we get a bus reset. */
 	while (!down_interruptible(&hi->reset_sem) &&
 	       !down_interruptible(&nodemgr_serialize)) {
+		unsigned int generation;
+		int i;
 
-		if (!nodemgr_check_root_capability(hi->host)) {
+		/* Pause for 1/4 second, to make sure things settle down. */
+		for (i = HZ/4; i > 0; i-= HZ/16) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (schedule_timeout(HZ/16))
+				goto caught_signal;
+
+			/* Now get the generation in which the node ID's we collect
+			 * are valid.  During the bus scan we will use this generation
+			 * for the read transactions, so that if another reset occurs
+			 * during the scan the transactions will fail instead of
+			 * returning bogus data. */
+			generation = get_hpsb_generation(hi->host);
+
+			/* If we get a reset before we are done waiting, then
+			 * start the the waiting over again */
+			while (!down_trylock(&hi->reset_sem))
+				i = HZ/4;
+		}
+
+		if (!nodemgr_check_root_capability(host)) {
 			/* Do nothing, we are resetting */
 			up(&nodemgr_serialize);
 			continue;
 		}
 
-		nodemgr_node_probe(hi->host);
-		nodemgr_do_irm_duties(hi->host);
+		nodemgr_node_probe(hi, generation);
+		nodemgr_do_irm_duties(host);
+
+		/* Update some of our sysfs symlinks */
+		nodemgr_update_host_dev_links(host);
 
 		up(&nodemgr_serialize);
 	}
+
+caught_signal:
 #ifdef CONFIG_IEEE1394_VERBOSEDEBUG
 	HPSB_DEBUG ("NodeMgr: Exiting thread for %s", hi->host->driver->name);
 #endif
@@ -1298,24 +1625,24 @@
         return ne;
 }
 
-struct node_entry *hpsb_nodeid_get_entry(nodeid_t nodeid)
+struct node_entry *hpsb_nodeid_get_entry(struct hpsb_host *host, nodeid_t nodeid)
 {
 	struct node_entry *ne;
 
 	down(&nodemgr_serialize);
-	ne = find_entry_by_nodeid(nodeid);
+	ne = find_entry_by_nodeid(host, nodeid);
 	up(&nodemgr_serialize);
 
 	return ne;
 }
 
-struct node_entry *hpsb_check_nodeid(nodeid_t nodeid)
+struct node_entry *hpsb_check_nodeid(struct hpsb_host *host, nodeid_t nodeid)
 {
 	struct node_entry *ne;
 
 	if (down_trylock(&nodemgr_serialize))
 		return NULL;
-	ne = find_entry_by_nodeid(nodeid);
+	ne = find_entry_by_nodeid(host, nodeid);
 	up(&nodemgr_serialize);
 
 	return ne;
@@ -1389,23 +1716,38 @@
 	/* Initialize the hostinfo here and start the thread.  The
 	 * thread blocks on the reset semaphore until a bus reset
 	 * happens. */
+	memset(hi, 0, sizeof(*hi));
 	hi->host = host;
 	INIT_LIST_HEAD(&hi->list);
 	init_completion(&hi->exited);
         sema_init(&hi->reset_sem, 0);
 
+	hi->id = nodemgr_alloc_host_num();
+
+	memcpy(&host->device, &nodemgr_dev_template_host,
+	       sizeof(host->device));
+	host->device.parent = &host->pdev->dev;
+	snprintf(host->device.bus_id, BUS_ID_SIZE, "fw-host%d", hi->id);
+	snprintf(host->device.name, DEVICE_NAME_SIZE, "IEEE-1394 Host %s-%d",
+		 host->driver->name, hi->id);
+
+	sprintf(hi->daemon_name, "knodemgrd_%d", hi->id);
+
+	spin_lock_irqsave (&host_info_lock, flags);
+
 	hi->pid = kernel_thread(nodemgr_host_thread, hi,
 				CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 	
 	if (hi->pid < 0) {
-		HPSB_ERR ("NodeMgr: failed to start NodeMgr thread for %s",
-			  host->driver->name);
+		HPSB_ERR ("NodeMgr: failed to start %s thread for %s",
+			  hi->daemon_name, host->driver->name);
 		kfree(hi);
+		spin_unlock_irqrestore (&host_info_lock, flags);
 		return;
 	}
 
-	spin_lock_irqsave (&host_info_lock, flags);
 	list_add_tail (&hi->list, &host_info_list);
+
 	spin_unlock_irqrestore (&host_info_lock, flags);
 
 	return;
@@ -1442,7 +1784,6 @@
 static void nodemgr_remove_host(struct hpsb_host *host)
 {
 	struct list_head *lh, *next;
-	struct node_entry *ne;
 	unsigned long flags;
 	struct host_info *hi = NULL;
 
@@ -1455,31 +1796,20 @@
 			break;
 		}
 	}
-	spin_unlock_irqrestore (&host_info_lock, flags);
 
 	if (hi) {
 		if (hi->pid >= 0) {
 			kill_proc(hi->pid, SIGTERM, 1);
 			wait_for_completion(&hi->exited);
+			nodemgr_remove_host_dev(&host->device);
+			device_unregister(&host->device);
 		}
 		kfree(hi);
-	}
-	else
+	} else
 		HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
 			 host->driver->name);
 
-	down(&nodemgr_serialize);
-
-	/* Even if we fail the host_info part, remove all the node
-	 * entries.  */
-	list_for_each_safe(lh, next, &node_list) {
-		ne = list_entry(lh, struct node_entry, list);
-
-		if (ne->host == host)
-			nodemgr_remove_node(ne);
-	}
-
-	up(&nodemgr_serialize);
+	spin_unlock_irqrestore (&host_info_lock, flags);
 
 	return;
 }
@@ -1492,15 +1822,10 @@
 
 static struct hpsb_highlevel *hl;
 
-#define PROC_ENTRY "devices"
-
-void init_ieee1394_nodemgr(int disable_hotplug)
+void init_ieee1394_nodemgr(void)
 {
-	nodemgr_disable_hotplug = disable_hotplug;
-#ifdef CONFIG_PROC_FS
-	if (!create_proc_read_entry(PROC_ENTRY, 0444, ieee1394_procfs_entry, raw1394_read_proc, NULL))
-		HPSB_ERR("Can't create devices procfs entry");
-#endif
+	bus_register(&ieee1394_bus_type);
+
         hl = hpsb_register_highlevel("Node manager", &nodemgr_ops);
         if (!hl) {
 		HPSB_ERR("NodeMgr: out of memory during ieee1394 initialization");
@@ -1510,7 +1835,6 @@
 void cleanup_ieee1394_nodemgr(void)
 {
         hpsb_unregister_highlevel(hl);
-#ifdef CONFIG_PROC_FS
-	remove_proc_entry(PROC_ENTRY, ieee1394_procfs_entry);
-#endif
+
+	bus_unregister(&ieee1394_bus_type);
 }
diff -urN linux-2.5.67-bk3/drivers/ieee1394/nodemgr.h linux-2.5.67-bk4/drivers/ieee1394/nodemgr.h
--- linux-2.5.67-bk3/drivers/ieee1394/nodemgr.h	2003-04-07 10:31:44.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/nodemgr.h	2003-04-12 04:33:21.000000000 -0700
@@ -20,6 +20,8 @@
 #ifndef _IEEE1394_NODEMGR_H
 #define _IEEE1394_NODEMGR_H
 
+#include <linux/device.h>
+
 #define CONFIG_ROM_BUS_INFO_LENGTH(q)		((q) >> 24)
 #define CONFIG_ROM_BUS_CRC_LENGTH(q)		(((q) >> 16) & 0xff)
 #define CONFIG_ROM_BUS_CRC(q)			((q) & 0xffff)
@@ -76,6 +78,12 @@
 	u16	max_rec;	/* Maximum packet size node can receive */
 };
 
+enum {
+	DEV_CLASS_NODE,
+	DEV_CLASS_UNIT_DIRECTORY,
+	DEV_CLASS_HOST,
+};
+
 #define UNIT_DIRECTORY_VENDOR_ID	0x01
 #define UNIT_DIRECTORY_MODEL_ID		0x02
 #define UNIT_DIRECTORY_SPECIFIER_ID	0x04
@@ -87,18 +95,16 @@
  * A unit directory corresponds to a protocol supported by the
  * node. If a node supports eg. IP/1394 and AV/C, its config rom has a
  * unit directory for each of these protocols.
- * 
- * Unit directories appear on two types of lists: for each node we
- * maintain a list of the unit directories found in its config rom and
- * for each driver we maintain a list of the unit directories
- * (ie. devices) the driver manages.
  */
 struct unit_directory {
 	struct node_entry *ne;  /* The node which this directory belongs to */
 	octlet_t address;	/* Address of the unit directory on the node */
 	u8 flags;		/* Indicates which entries were read */
+
 	quadlet_t vendor_id;
 	const char *vendor_name;
+	const char *vendor_oui;
+
 	int vendor_name_size;
 	quadlet_t model_id;
 	const char *model_name;
@@ -106,22 +112,21 @@
 	quadlet_t specifier_id;
 	quadlet_t version;
 
-	struct hpsb_protocol_driver *driver;
-	void *driver_data;
+	unsigned int id;
 
-	/* For linking the nodes managed by the driver, or unmanaged nodes */
-	struct list_head driver_list;
+	int length;		/* Number of quadlets */
 
-	/* For linking directories belonging to a node */
-	struct list_head node_list;
+	struct device device;
 
-	int count;		/* Number of quadlets */
+	/* XXX Must be last in the struct! */
 	quadlet_t quadlets[0];
 };
 
 struct node_entry {
-	struct list_head list;
 	u64 guid;			/* GUID of this node */
+	u32 guid_vendor_id;		/* Top 24bits of guid */
+	const char *guid_vendor_oui;	/* OUI name of guid vendor id */
+
 	struct hpsb_host *host;		/* Host this node is attached to */
 	nodeid_t nodeid;		/* NodeID */
 	struct bus_options busopt;	/* Bus Options */
@@ -129,14 +134,16 @@
 
 	/* The following is read from the config rom */
 	u32 vendor_id;
+	const char *vendor_name;
+	const char *vendor_oui;
+
 	u32 capabilities;	
-	struct list_head unit_directories;
 
 	struct hpsb_tlabel_pool *tpool;
 
-	const char *vendor_name;
-	char *oui_name;
+	struct device device;
 
+	/* XXX Must be last in the struct! */
 	quadlet_t quadlets[0];
 };
 
@@ -154,11 +161,11 @@
 
 /* Same as above, but use the nodeid to get an node entry. This is not
  * fool-proof by itself, since the nodeid can change.  */
-struct node_entry *hpsb_nodeid_get_entry(nodeid_t nodeid);
+struct node_entry *hpsb_nodeid_get_entry(struct hpsb_host *host, nodeid_t nodeid);
 
 /* Same as above except that it will not block waiting for the nodemgr
  * serialize semaphore.  */
-struct node_entry *hpsb_check_nodeid(nodeid_t nodeid);
+struct node_entry *hpsb_check_nodeid(struct hpsb_host *host, nodeid_t nodeid);
 
 /*
  * If the entry refers to a local host, this function will return the pointer
@@ -188,7 +195,7 @@
 		   int extcode, quadlet_t *data, quadlet_t arg);
 
 
-void init_ieee1394_nodemgr(int disable_hotplug);
+void init_ieee1394_nodemgr(void);
 void cleanup_ieee1394_nodemgr(void);
 
 #endif /* _IEEE1394_NODEMGR_H */
diff -urN linux-2.5.67-bk3/drivers/ieee1394/ohci1394.c linux-2.5.67-bk4/drivers/ieee1394/ohci1394.c
--- linux-2.5.67-bk3/drivers/ieee1394/ohci1394.c	2003-04-07 10:32:27.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/ohci1394.c	2003-04-12 04:33:21.000000000 -0700
@@ -80,6 +80,10 @@
  * Manfred Weihs <weihs@ict.tuwien.ac.at>
  *  . Reworked code for initiating bus resets
  *    (long, short, with or without hold-off)
+ *
+ * Nandu Santhi <contactnandu@users.sourceforge.net>
+ *  . Added support for nVidia nForce2 onboard Firewire chipset
+ *
  */
 
 #include <linux/config.h>
@@ -90,6 +94,7 @@
 #include <linux/wait.h>
 #include <linux/errno.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/pci.h>
 #include <linux/fs.h>
 #include <linux/poll.h>
@@ -145,7 +150,7 @@
 #define OHCI_DMA_FREE(fmt, args...) \
 	HPSB_ERR("%s(%s)free(%d): "fmt, OHCI1394_DRIVER_NAME, __FUNCTION__, \
 		--global_outstanding_dmas, ## args)
-u32 global_outstanding_dmas = 0;
+static int global_outstanding_dmas = 0;
 #else
 #define OHCI_DMA_ALLOC(fmt, args...)
 #define OHCI_DMA_FREE(fmt, args...)
@@ -160,12 +165,12 @@
 printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
 
 static char version[] __devinitdata =
-	"$Rev: 801 $ Ben Collins <bcollins@debian.org>";
+	"$Rev: 858 $ Ben Collins <bcollins@debian.org>";
 
 /* Module Parameters */
-MODULE_PARM(phys_dma,"i");
-MODULE_PARM_DESC(phys_dma, "Enable physical dma (default = 1).");
 static int phys_dma = 1;
+module_param(phys_dma, int, 0644);
+MODULE_PARM_DESC(phys_dma, "Enable physical dma (default = 1).");
 
 static void dma_trm_tasklet(unsigned long data);
 static void dma_trm_reset(struct dma_trm_ctx *d);
@@ -354,10 +359,10 @@
 static void ohci_soft_reset(struct ti_ohci *ohci) {
 	int i;
 
-	reg_write(ohci, OHCI1394_HCControlSet, 0x00010000);
+	reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);
   
 	for (i = 0; i < OHCI_LOOP_COUNT; i++) {
-		if (reg_read(ohci, OHCI1394_HCControlSet) & 0x00010000)
+		if (!reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_softReset)
 			break;
 		mdelay(1);
 	}
@@ -514,7 +519,7 @@
 	reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
 
 	/* Enable posted writes */
-	reg_write(ohci, OHCI1394_HCControlSet, 0x00040000);
+	reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_postedWriteEnable);
 
 	/* Clear link control register */
 	reg_write(ohci, OHCI1394_LinkControlClear, 0xffffffff);
@@ -577,7 +582,7 @@
 		  (OHCI1394_MAX_PHYS_RESP_RETRIES<<8));
 
 	/* We don't want hardware swapping */
-	reg_write(ohci, OHCI1394_HCControlClear, 0x40000000);
+	reg_write(ohci, OHCI1394_HCControlClear, OHCI1394_HCControl_noByteSwap);
 
 	/* Enable interrupts */
 	reg_write(ohci, OHCI1394_IntMaskSet,
@@ -594,7 +599,7 @@
 		  OHCI1394_cycleInconsistent);
 
 	/* Enable link */
-	reg_write(ohci, OHCI1394_HCControlSet, 0x00020000);
+	reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_linkEnable);
 
 	buf = reg_read(ohci, OHCI1394_Version);
 	PRINT(KERN_INFO, ohci->id, "OHCI-1394 %d.%d (PCI): IRQ=[%d]  "
@@ -1190,10 +1195,11 @@
 		/* iso->irq_interval is in packets - translate that to blocks */
 		/* (err, sort of... 1 is always the safest value) */
 		recv->block_irq_interval = iso->irq_interval / recv->nblocks;
+		if(recv->block_irq_interval*4 > recv->nblocks)
+			recv->block_irq_interval = recv->nblocks/4;
 		if(recv->block_irq_interval < 1)
 			recv->block_irq_interval = 1;
-		else if(recv->block_irq_interval*4 > recv->nblocks)
-			recv->block_irq_interval = recv->nblocks/4;
+
 	} else {
 		int max_packet_size;
 
@@ -2291,17 +2297,35 @@
 		 * selfID phase, so we disable busReset interrupts, to
 		 * avoid burying the cpu in interrupt requests. */
 		spin_lock_irqsave(&ohci->event_lock, flags);
-  		reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
-		if (ohci->dev->vendor == PCI_VENDOR_ID_APPLE && 
-		    ohci->dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) {
-  			udelay(10);
-  			while(reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
-  				reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+		reg_write(ohci, OHCI1394_IntMaskClear, OHCI1394_busReset);
+
+		if (ohci->check_busreset) {
+			int loop_count = 0;
+
+			udelay(10);
+
+			while (reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
+				reg_write(ohci, OHCI1394_IntEventClear, OHCI1394_busReset);
+
 				spin_unlock_irqrestore(&ohci->event_lock, flags);
-	  			udelay(10);
+				udelay(10);
 				spin_lock_irqsave(&ohci->event_lock, flags);
-  			}
-  		}
+
+				/* The loop counter check is to prevent the driver
+				 * from remaining in this state forever. For the
+				 * initial bus reset, the loop continues for ever
+				 * and the system hangs, until some device is plugged-in
+				 * or out manually into a port! The forced reset seems
+				 * to solve this problem. This mainly effects nForce2. */
+				if (loop_count > 10000) {
+					hpsb_reset_bus(host, 1);
+					DBGMSG(ohci->id, "Detected bus-reset loop. Forced a bus reset!");
+					loop_count = 0;
+				}
+
+				loop_count++;
+			}
+		}
 		spin_unlock_irqrestore(&ohci->event_lock, flags);
 		if (!host->in_bus_reset) {
 			DBGMSG(ohci->id, "irq_handler: Bus reset requested");
@@ -2438,6 +2462,8 @@
 	if (event)
 		PRINT(KERN_ERR, ohci->id, "Unhandled interrupt(s) 0x%08x",
 		      event);
+
+	return;
 }
 
 /* Put the buffer back into the dma context */
@@ -3277,6 +3303,18 @@
 		ohci->selfid_swap = 1;
 #endif
 
+#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_FW
+#define PCI_DEVICE_ID_NVIDIA_NFORCE2_FW 0x006e
+#endif
+
+	/* These chipsets require a bit of extra care when checking after
+	 * a busreset.  */
+	if ((dev->vendor == PCI_VENDOR_ID_APPLE &&
+	     dev->device == PCI_DEVICE_ID_APPLE_UNI_N_FW) ||
+	    (dev->vendor ==  PCI_VENDOR_ID_NVIDIA &&
+	     dev->device == PCI_DEVICE_ID_NVIDIA_NFORCE2_FW))
+		ohci->check_busreset = 1;
+
 	/* We hardwire the MMIO length, since some CardBus adaptors
 	 * fail to report the right length.  Anyway, the ohci spec
 	 * clearly says it's 2kb, so this shouldn't be a problem. */ 
@@ -3363,7 +3401,7 @@
 	 * accessing registers in the SClk domain without LPS enabled
 	 * will lock up the machine.  Wait 50msec to make sure we have
 	 * full link enabled.  */
-	reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
+	reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_LPS);
 	mdelay(50);
 
 	/* Determine the number of available IR and IT contexts. */
@@ -3489,7 +3527,7 @@
 static struct pci_device_id ohci1394_pci_tbl[] __devinitdata = {
 	{
 		.class = 	PCI_CLASS_FIREWIRE_OHCI,
-		.class_mask = 	~0,
+		.class_mask = 	PCI_ANY_ID,
 		.vendor =	PCI_ANY_ID,
 		.device =	PCI_ANY_ID,
 		.subvendor =	PCI_ANY_ID,
diff -urN linux-2.5.67-bk3/drivers/ieee1394/ohci1394.h linux-2.5.67-bk4/drivers/ieee1394/ohci1394.h
--- linux-2.5.67-bk3/drivers/ieee1394/ohci1394.h	2003-04-07 10:30:42.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/ohci1394.h	2003-04-12 04:33:21.000000000 -0700
@@ -233,6 +233,9 @@
 	unsigned int selfid_swap:1;
 	/* Some Apple chipset seem to swap incoming headers for us */
 	unsigned int no_swap_incoming:1;
+
+	/* Force extra paranoia checking on bus-reset handling */
+	unsigned int check_busreset:1;
 };
 
 static inline int cross_bound(unsigned long addr, unsigned int size)
@@ -288,6 +291,13 @@
 #define OHCI1394_VendorID                     0x040
 #define OHCI1394_HCControlSet                 0x050
 #define OHCI1394_HCControlClear               0x054
+#define  OHCI1394_HCControl_noByteSwap		0x40000000
+#define  OHCI1394_HCControl_programPhyEnable	0x00800000
+#define  OHCI1394_HCControl_aPhyEnhanceEnable	0x00400000
+#define  OHCI1394_HCControl_LPS			0x00080000
+#define  OHCI1394_HCControl_postedWriteEnable	0x00040000
+#define  OHCI1394_HCControl_linkEnable		0x00020000
+#define  OHCI1394_HCControl_softReset		0x00010000
 #define OHCI1394_SelfIDBuffer                 0x064
 #define OHCI1394_SelfIDCount                  0x068
 #define OHCI1394_IRMultiChanMaskHiSet         0x070
diff -urN linux-2.5.67-bk3/drivers/ieee1394/pcilynx.c linux-2.5.67-bk4/drivers/ieee1394/pcilynx.c
--- linux-2.5.67-bk3/drivers/ieee1394/pcilynx.c	2003-04-07 10:31:03.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/pcilynx.c	2003-04-12 04:33:21.000000000 -0700
@@ -37,6 +37,7 @@
 #include <linux/wait.h>
 #include <linux/errno.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/fs.h>
@@ -71,8 +72,8 @@
 
 
 /* Module Parameters */
-MODULE_PARM(skip_eeprom,"i");
-MODULE_PARM_DESC(skip_eeprom, "Do not try to read bus info block from serial eeprom, but user generic one (default = 0).");
+module_param(skip_eeprom, int, 0444);
+MODULE_PARM_DESC(skip_eeprom, "Use generic bus info block instead of serial eeprom (default = 0).");
 static int skip_eeprom = 0;
 
 
@@ -983,8 +984,9 @@
  * on performance - the value 2400 was found by experiment and may not work
  * everywhere as good as here - use mem_mindma option for modules to change 
  */
-short mem_mindma = 2400;
-MODULE_PARM(mem_mindma, "h");
+static short mem_mindma = 2400;
+module_param(mem_mindma, short, 0444);
+MODULE_PARM_DESC(mem_mindma, "Minimum amount of data required to use DMA");
 
 static ssize_t mem_dmaread(struct memdata *md, u32 physbuf, ssize_t count,
                            int offset)
diff -urN linux-2.5.67-bk3/drivers/ieee1394/sbp2.c linux-2.5.67-bk4/drivers/ieee1394/sbp2.c
--- linux-2.5.67-bk3/drivers/ieee1394/sbp2.c	2003-04-07 10:30:42.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/sbp2.c	2003-04-12 04:33:21.000000000 -0700
@@ -27,44 +27,22 @@
  * driver. It also registers as a SCSI lower-level driver in order to accept
  * SCSI commands for transport using SBP-2.
  *
- * The easiest way to add/detect new SBP-2 devices is to run the shell script
- * rescan-scsi-bus.sh (or re-load the SBP-2 driver). This script may be 
- * found at:
- * http://www.garloff.de/kurt/linux/rescan-scsi-bus.sh
- *
- * As an alternative, you may manually add/remove SBP-2 devices via the procfs with
- * add-single-device <h> <b> <t> <l> or remove-single-device <h> <b> <t> <l>, where:
- *	<h> = host (starting at zero for first SCSI adapter)
- *	<b> = bus (normally zero)
- *	<t> = target (starting at zero for first SBP-2 device)
- *	<l> = lun (normally zero)
- *
- * e.g. To manually add/detect a new SBP-2 device
- *	echo "scsi add-single-device 0 0 0 0" > /proc/scsi/scsi
- *
- * e.g. To manually remove a SBP-2 device after it's been unplugged
- *	echo "scsi remove-single-device 0 0 0 0" > /proc/scsi/scsi
- *
- * e.g. To check to see which SBP-2/SCSI devices are currently registered
- * 	cat /proc/scsi/scsi
- *
- * After scanning for new SCSI devices (above), you may access any attached 
- * SBP-2 storage devices as if they were SCSI devices (e.g. mount /dev/sda1, 
- * fdisk, mkfs, etc.).
+ * You may access any attached SBP-2 storage devices as if they were SCSI
+ * devices (e.g. mount /dev/sda1,  fdisk, mkfs, etc.).
  *
  *
  * Module Load Options:
  *
- * sbp2_max_speed 		- Force max speed allowed 
- *				  (2 = 400mb, 1 = 200mb, 0 = 100mb. default = 2)
- * sbp2_serialize_io		- Serialize all I/O coming down from the scsi drivers
- *				  (0 = deserialized, 1 = serialized, default = 0)
- * sbp2_max_sectors, 		- Change max sectors per I/O supported (default = 255)
- * sbp2_exclusive_login		- Set to zero if you'd like to allow multiple hosts the ability
- *				  to log in at the same time. Sbp2 device must support this,
- *				  and you must know what you're doing (default = 1)
+ * max_speed 		- Force max speed allowed 
+ *			  (2 = 400mb, 1 = 200mb, 0 = 100mb. default = 2)
+ * serialize_io		- Serialize all I/O coming down from the scsi drivers
+ *			  (0 = deserialized, 1 = serialized, default = 0)
+ * max_sectors, 	- Change max sectors per I/O supported (default = 255)
+ * exclusive_login	- Set to zero if you'd like to allow multiple hosts the ability
+ *			  to log in at the same time. Sbp2 device must support this,
+ *			  and you must know what you're doing (default = 1)
  *
- * (e.g. insmod sbp2 sbp2_serialize_io = 1)
+ * (e.g. insmod sbp2 sbp2.serialize_io = 1)
  *
  *
  * Current Support:
@@ -258,7 +236,7 @@
  *		   * New packet dump debug define (CONFIG_IEEE1394_SBP2_PACKET_DUMP) which allows
  *		     dumping of all sbp2 related packets sent and received. Especially effective
  *		     when phys dma is disabled on ohci controller (e.g. insmod ohci1394 phys_dma=0).
- *		   * Added new sbp2 module load option (sbp2_exclusive_login) for allowing
+ *		   * Added new sbp2 module load option (exclusive_login) for allowing
  *		     non-exclusive login to sbp2 device, for special multi-host applications.
  *	04/23/02 - Fix for Sony CD-ROM drives. Only send fetch agent reset to sbp2 device if it
  *		   returns the dead bit in status. Thanks to Chandan (chandan@toad.net) for this one.
@@ -285,6 +263,7 @@
 #include <linux/fs.h>
 #include <linux/poll.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/types.h>
 #include <linux/delay.h>
 #include <linux/sched.h>
@@ -319,14 +298,14 @@
 #include "sbp2.h"
 
 static char version[] __devinitdata =
-	"$Rev: 797 $ James Goodwin <jamesg@filanet.com>";
+	"$Rev: 846 $ James Goodwin <jamesg@filanet.com>";
 
 /*
  * Module load parameter definitions
  */
 
 /*
- * Change sbp2_max_speed on module load if you have a bad IEEE-1394
+ * Change max_speed on module load if you have a bad IEEE-1394
  * controller that has trouble running 2KB packets at 400mb.
  *
  * NOTE: On certain OHCI parts I have seen short packets on async transmit
@@ -334,34 +313,34 @@
  * bump down the speed if you are running into problems.
  *
  * Valid values:
- * sbp2_max_speed = 2 (default: max speed 400mb)
- * sbp2_max_speed = 1 (max speed 200mb)
- * sbp2_max_speed = 0 (max speed 100mb)
+ * max_speed = 2 (default: max speed 400mb)
+ * max_speed = 1 (max speed 200mb)
+ * max_speed = 0 (max speed 100mb)
  */
-MODULE_PARM(sbp2_max_speed,"i");
-MODULE_PARM_DESC(sbp2_max_speed, "Force max speed (2 = 400mb default, 1 = 200mb, 0 = 100mb)");
-static int sbp2_max_speed = SPEED_400;
+static int max_speed = SPEED_400;
+module_param(max_speed, int, 0644);
+MODULE_PARM_DESC(max_speed, "Force max speed (2 = 400mb default, 1 = 200mb, 0 = 100mb)");
 
 /*
- * Set sbp2_serialize_io to 1 if you'd like only one scsi command sent
+ * Set serialize_io to 1 if you'd like only one scsi command sent
  * down to us at a time (debugging). This might be necessary for very
  * badly behaved sbp2 devices.
  */
-MODULE_PARM(sbp2_serialize_io,"i");
-MODULE_PARM_DESC(sbp2_serialize_io, "Serialize all I/O coming down from the scsi drivers (default = 0)");
-static int sbp2_serialize_io = 0;	/* serialize I/O - available for debugging purposes */
+static int serialize_io = 0;
+module_param(serialize_io, int, 0444);
+MODULE_PARM_DESC(serialize_io, "Serialize all I/O coming down from the scsi drivers (default = 0)");
 
 /*
- * Bump up sbp2_max_sectors if you'd like to support very large sized
+ * Bump up max_sectors if you'd like to support very large sized
  * transfers. Please note that some older sbp2 bridge chips are broken for
  * transfers greater or equal to 128KB.  Default is a value of 255
  * sectors, or just under 128KB (at 512 byte sector size). I can note that
  * the Oxsemi sbp2 chipsets have no problems supporting very large
  * transfer sizes.
  */
-MODULE_PARM(sbp2_max_sectors,"i");
-MODULE_PARM_DESC(sbp2_max_sectors, "Change max sectors per I/O supported (default = 255)");
-static int sbp2_max_sectors = SBP2_MAX_SECTORS;
+static int max_sectors = SBP2_MAX_SECTORS;
+module_param(max_sectors, int, 0444);
+MODULE_PARM_DESC(max_sectors, "Change max sectors per I/O supported (default = 255)");
 
 /*
  * Exclusive login to sbp2 device? In most cases, the sbp2 driver should
@@ -370,13 +349,13 @@
  * etc.). If you're running an sbp2 device that supports multiple logins,
  * and you're either running read-only filesystems or some sort of special
  * filesystem supporting multiple hosts (one such filesystem is OpenGFS,
- * see opengfs.sourceforge.net for more info), then set sbp2_exclusive_login
+ * see opengfs.sourceforge.net for more info), then set exclusive_login
  * to zero. Note: The Oxsemi OXFW911 sbp2 chipset supports up to four
  * concurrent logins.
  */
-MODULE_PARM(sbp2_exclusive_login,"i");
-MODULE_PARM_DESC(sbp2_exclusive_login, "Exclusive login to sbp2 device (default = 1)");
-static int sbp2_exclusive_login = 1;
+static int exclusive_login = 1;
+module_param(exclusive_login, int, 0644);
+MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device (default = 1)");
 
 /*
  * SCSI inquiry hack for really badly behaved sbp2 devices. Turn this on
@@ -384,13 +363,13 @@
  * This hack makes the inquiry look more like a typical MS Windows
  * inquiry.
  * 
- * If sbp2_force_inquiry_hack=1 is required for your device to work,
+ * If force_inquiry_hack=1 is required for your device to work,
  * please submit the logged sbp2_firmware_revision value of this device to
  * the linux1394-devel mailing list.
  */
-MODULE_PARM(sbp2_force_inquiry_hack,"i");
-MODULE_PARM_DESC(sbp2_force_inquiry_hack, "Force SCSI inquiry hack (default = 0)");
-static int sbp2_force_inquiry_hack = 0;
+static int force_inquiry_hack = 0;
+module_param(force_inquiry_hack, int, 0444);
+MODULE_PARM_DESC(force_inquiry_hack, "Force SCSI inquiry hack (default = 0)");
 
 
 /*
@@ -466,12 +445,10 @@
  * Globals
  */
 
-static void sbp2scsi_complete_all_commands(struct sbp2scsi_host_info *hi,
-					   struct scsi_id_instance_data *scsi_id,
+static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id,
 					   u32 status);
 
-static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi,
-				      struct scsi_id_instance_data *scsi_id,
+static void sbp2scsi_complete_command(struct scsi_id_instance_data *scsi_id,
 				      u32 scsi_status, Scsi_Cmnd *SCpnt,
 				      void (*done)(Scsi_Cmnd *));
 	
@@ -486,7 +463,6 @@
 static struct hpsb_highlevel *sbp2_hl_handle = NULL;
 
 static struct hpsb_highlevel_ops sbp2_hl_ops = {
-	.add_host =	sbp2_add_host,
 	.remove_host =	sbp2_remove_host,
 };
 
@@ -502,13 +478,18 @@
 #endif
 
 static struct hpsb_protocol_driver sbp2_driver = {
-	.name =		"SBP2 Driver",
-	.id_table = 	sbp2_id_table,
-	.probe = 	sbp2_probe,
-	.disconnect = 	sbp2_disconnect,
-	.update = 	sbp2_update
+	.name		= "SBP2 Driver",
+	.id_table	= sbp2_id_table,
+	.update		= sbp2_update,
+	.driver		= {
+		.name		= SBP2_DEVICE_NAME,
+		.bus		= &ieee1394_bus_type,
+		.probe		= sbp2_probe,
+		.remove		= sbp2_remove,
+	},
 };
 
+
 /* List of device firmware's that require a forced 36 byte inquiry.  */
 static u32 sbp2_broken_inquiry_list[] = {
 	0x00002800,	/* Stefan Richter <richtest@bauwesen.tu-cottbus.de> */
@@ -648,14 +629,14 @@
  * This function is called to create a pool of command orbs used for
  * command processing. It is called when a new sbp2 device is detected.
  */
-static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id,
-					    struct sbp2scsi_host_info *hi)
+static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id)
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	int i;
 	unsigned long flags, orbs;
 	struct sbp2_command_info *command;
 
-	orbs = sbp2_serialize_io ? 2 : SBP2_MAX_COMMAND_ORBS;
+	orbs = serialize_io ? 2 : SBP2_MAX_COMMAND_ORBS;
         
 	spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
 	for (i = 0; i < orbs; i++) {
@@ -686,9 +667,9 @@
 /*
  * This function is called to delete a pool of command orbs.
  */
-static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id,
-					     struct sbp2scsi_host_info *hi)
+static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id)
 {
+	struct hpsb_host *host = scsi_id->hi->host;
 	struct list_head *lh, *next;
 	struct sbp2_command_info *command;
 	unsigned long flags;
@@ -699,11 +680,11 @@
 			command = list_entry(lh, struct sbp2_command_info, list);
 
 			/* Release our generic DMA's */
-			pci_unmap_single(hi->host->pdev, command->command_orb_dma,
+			pci_unmap_single(host->pdev, command->command_orb_dma,
 					 sizeof(struct sbp2_command_orb),
 					 PCI_DMA_BIDIRECTIONAL);
 			SBP2_DMA_FREE("single command orb DMA");
-			pci_unmap_single(hi->host->pdev, command->sge_dma,
+			pci_unmap_single(host->pdev, command->sge_dma,
 					 sizeof(command->scatter_gather_element),
 					 PCI_DMA_BIDIRECTIONAL);
 			SBP2_DMA_FREE("scatter_gather_element");
@@ -773,8 +754,7 @@
 static struct sbp2_command_info *sbp2util_allocate_command_orb(
 		struct scsi_id_instance_data *scsi_id, 
 		Scsi_Cmnd *Current_SCpnt, 
-		void (*Current_done)(Scsi_Cmnd *),
-		struct sbp2scsi_host_info *hi)
+		void (*Current_done)(Scsi_Cmnd *))
 {
 	struct list_head *lh;
 	struct sbp2_command_info *command = NULL;
@@ -849,78 +829,86 @@
  * IEEE-1394 core driver stack related section
  *********************************************/
 
-static int sbp2_probe(struct unit_directory *ud)
+static int sbp2_probe(struct device *dev)
 {
+	struct unit_directory *ud;
 	struct sbp2scsi_host_info *hi;
 
-	SBP2_DEBUG("sbp2_probe");
-	hi = sbp2_find_host_info(ud->ne->host);
+	SBP2_DEBUG(__FUNCTION__);
+
+	ud = container_of(dev, struct unit_directory, device);
+
+	/* This will only add it if it doesn't exist */
+	hi = sbp2_add_host(ud->ne->host);
+
+	if (!hi)
+		return -ENODEV;
 
 	return sbp2_start_device(hi, ud);
 }
 
-static void sbp2_disconnect(struct unit_directory *ud)
+static int sbp2_remove(struct device *dev)
 {
-	struct sbp2scsi_host_info *hi;
-	struct scsi_id_instance_data *scsi_id = ud->driver_data;
+	struct unit_directory *ud;
+	struct scsi_id_instance_data *scsi_id;
 
-	SBP2_DEBUG("sbp2_disconnect");
-	hi = sbp2_find_host_info(ud->ne->host);
+	SBP2_DEBUG(__FUNCTION__);
 
-	if (hi != NULL) {
-		sbp2_logout_device(hi, scsi_id);
- 		sbp2_remove_device(hi, scsi_id);
+	ud = container_of(dev, struct unit_directory, device);
+	scsi_id = ud->device.driver_data;
+	ud->device.driver_data = NULL;
+
+	if (scsi_id != NULL) {
+		sbp2_logout_device(scsi_id);
+ 		sbp2_remove_device(scsi_id);
 	}
+
+	return 0;
 }
 
 static void sbp2_update(struct unit_directory *ud)
 {
-	struct sbp2scsi_host_info *hi;
-	struct scsi_id_instance_data *scsi_id = ud->driver_data;
+	struct scsi_id_instance_data *scsi_id = ud->device.driver_data;
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	unsigned long flags;
 
 	SBP2_DEBUG("sbp2_update");
-	hi = sbp2_find_host_info(ud->ne->host);
 
-	if (sbp2_reconnect_device(hi, scsi_id)) {
+	if (sbp2_reconnect_device(scsi_id)) {
 		
 		/* 
 		 * Ok, reconnect has failed. Perhaps we didn't
 		 * reconnect fast enough. Try doing a regular login.
 		 */
-		if (sbp2_login_device(hi, scsi_id)) {
-
+		if (sbp2_login_device(scsi_id)) {
 			/* Login failed too, just remove the device. */
 			SBP2_ERR("sbp2_reconnect_device failed!");
-			sbp2_remove_device(hi, scsi_id);
-			hpsb_release_unit_directory(ud);
+			sbp2_remove_device(scsi_id);
 			return;
 		}
 	}
 
 	/* Set max retries to something large on the device. */
-	sbp2_set_busy_timeout(hi, scsi_id);
+	sbp2_set_busy_timeout(scsi_id);
 
 	/* Do a SBP-2 fetch agent reset. */
-	sbp2_agent_reset(hi, scsi_id, 1);
+	sbp2_agent_reset(scsi_id, 1);
 	
 	/* Get the max speed and packet size that we can use. */
-	sbp2_max_speed_and_size(hi, scsi_id);
+	sbp2_max_speed_and_size(scsi_id);
 
 	/* Complete any pending commands with busy (so they get
 	 * retried) and remove them from our queue
 	 */
 	spin_lock_irqsave(&hi->sbp2_command_lock, flags);
-	sbp2scsi_complete_all_commands(hi, scsi_id, DID_BUS_BUSY);
+	sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
 	spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
 }
 
-/*
- * This function is called after registering our operations in sbp2_init.
- * We go ahead and allocate some memory for our host info structure, and
- * init some structures.
- */
-static void sbp2_add_host(struct hpsb_host *host)
+/* This functions is called by the sbp2_probe, for each new device. If the
+ * host_info already exists, it will return it. If not, it allocated a new
+ * host_info entry and a corresponding scsi_host. */
+static struct sbp2scsi_host_info *sbp2_add_host(struct hpsb_host *host)
 {
 	struct sbp2scsi_host_info *hi;
 	unsigned long flags;
@@ -928,11 +916,15 @@
 
 	SBP2_DEBUG("sbp2_add_host");
 
+	hi = sbp2_find_host_info(host);
+	if (hi)
+		return hi;
+
 	/* Register our host with the SCSI stack. */
 	scsi_host = scsi_register (&scsi_driver_template, sizeof(struct sbp2scsi_host_info));
 	if (!scsi_host) {
 		SBP2_ERR("failed to register scsi host");
-		return;
+		return NULL;
 	}
 
 	hi = (struct sbp2scsi_host_info *)&scsi_host->hostdata;
@@ -948,11 +940,10 @@
 	list_add_tail(&hi->list, &sbp2_host_info_list);
 	spin_unlock_irqrestore(&sbp2_host_info_lock, flags);
 
-	/*
-	 * XXX(hch): Hopefully the ieee1394 code will be converted
-	 * to the driver model at some point.  Until that happens
-	 * we'll have to pass in NULL here.
-	 */
+	/* XXX We need a device to pass here as the scsi-host class. Can't
+	 * use the PCI device, since it is already bound to the ieee1394
+	 * host. Can't use the fw-host device since it is multi-class
+	 * enabled (scsi-host uses classdata member of the device). */
 	if (scsi_add_host(hi->scsi_host, NULL)) {
 		SBP2_ERR("failed to add scsi host");
 
@@ -963,7 +954,7 @@
 		scsi_unregister(hi->scsi_host);
 	}
 
-	return;
+	return hi;
 }
 
 /*
@@ -1048,6 +1039,8 @@
 		goto alloc_fail_first;
 	memset(scsi_id, 0, sizeof(struct scsi_id_instance_data));
 
+	scsi_id->hi = hi;
+
 	/* Login FIFO DMA */
 	scsi_id->login_response =
 		pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_login_response),
@@ -1076,7 +1069,7 @@
 	scsi_id->login_orb =
 		pci_alloc_consistent(hi->host->pdev, sizeof(struct sbp2_login_orb),
 				     &scsi_id->login_orb_dma);
-	if (scsi_id->login_orb == NULL) {
+	if (!scsi_id->login_orb) {
 alloc_fail:
 		if (scsi_id->logout_orb) {
 			pci_free_consistent(hi->host->pdev,
@@ -1105,7 +1098,8 @@
 		kfree(scsi_id);
 alloc_fail_first:
 		SBP2_ERR ("Could not allocate memory for scsi_id");
-		return(-ENOMEM);
+
+		return -ENOMEM;
 	}
 	SBP2_DMA_ALLOC("consistent DMA region for login ORB");
 
@@ -1116,7 +1110,7 @@
 	scsi_id->ud = ud;
 	scsi_id->speed_code = SPEED_100;
 	scsi_id->max_payload_size = sbp2_speedto_maxrec[SPEED_100];
-	ud->driver_data = scsi_id;
+	ud->device.driver_data = scsi_id;
 
 	atomic_set(&scsi_id->sbp2_login_complete, 0);
 
@@ -1149,9 +1143,9 @@
 	/*
 	 * Create our command orb pool
 	 */
-	if (sbp2util_create_command_orb_pool(scsi_id, hi)) {
+	if (sbp2util_create_command_orb_pool(scsi_id)) {
 		SBP2_ERR("sbp2util_create_command_orb_pool failed!");
-		sbp2_remove_device(hi, scsi_id);
+		sbp2_remove_device(scsi_id);
 		return -ENOMEM;
 	}
 
@@ -1160,35 +1154,33 @@
 	 */
 	if (i == hi->scsi_host->max_id) {
 		SBP2_ERR("No slots left for SBP-2 device");
-		sbp2_remove_device(hi, scsi_id);
+		sbp2_remove_device(scsi_id);
 		return -EBUSY;
 	}
 
 	/*
 	 * Login to the sbp-2 device
 	 */
-	if (sbp2_login_device(hi, scsi_id)) {
-
+	if (sbp2_login_device(scsi_id)) {
 		/* Login failed, just remove the device. */
-		SBP2_ERR("sbp2_login_device failed");
-		sbp2_remove_device(hi, scsi_id);
+		sbp2_remove_device(scsi_id);
 		return -EBUSY;
 	}
 
 	/*
 	 * Set max retries to something large on the device
 	 */
-	sbp2_set_busy_timeout(hi, scsi_id);
+	sbp2_set_busy_timeout(scsi_id);
 	
 	/*
 	 * Do a SBP-2 fetch agent reset
 	 */
-	sbp2_agent_reset(hi, scsi_id, 1);
+	sbp2_agent_reset(scsi_id, 1);
 	
 	/*
 	 * Get the max speed and packet size that we can use
 	 */
-	sbp2_max_speed_and_size(hi, scsi_id);
+	sbp2_max_speed_and_size(scsi_id);
 
 	/* Add this device to the scsi layer now */
 	sdev = scsi_add_device(hi->scsi_host, 0, scsi_id->id, 0);
@@ -1203,21 +1195,21 @@
 /*
  * This function removes an sbp2 device from the sbp2scsi_host_info struct.
  */
-static void sbp2_remove_device(struct sbp2scsi_host_info *hi, 
-			       struct scsi_id_instance_data *scsi_id)
+static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id)
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	struct scsi_device *sdev = scsi_find_device(hi->scsi_host, 0, scsi_id->id, 0);
 
 	SBP2_DEBUG("sbp2_remove_device");
 
 	/* Complete any pending commands with selection timeout */
-	sbp2scsi_complete_all_commands(hi, scsi_id, DID_NO_CONNECT);
+	sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT);
 
 	/* Remove it from the scsi layer now */
-	if (scsi_remove_device(sdev))
+	if (sdev && scsi_remove_device(sdev))
 		SBP2_ERR("scsi_remove_device failed");
 
-	sbp2util_remove_command_orb_pool(scsi_id, hi);
+	sbp2util_remove_command_orb_pool(scsi_id);
 
 	hi->scsi_id[scsi_id->id] = NULL;
 
@@ -1311,8 +1303,9 @@
  * This function is called in order to login to a particular SBP-2 device,
  * after a bus reset.
  */
-static int sbp2_login_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) 
+static int sbp2_login_device(struct scsi_id_instance_data *scsi_id) 
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	quadlet_t data[2];
 
 	SBP2_DEBUG("sbp2_login_device");
@@ -1333,7 +1326,7 @@
 
 	scsi_id->login_orb->lun_misc = ORB_SET_FUNCTION(LOGIN_REQUEST);
 	scsi_id->login_orb->lun_misc |= ORB_SET_RECONNECT(0);	/* One second reconnect time */
-	scsi_id->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(sbp2_exclusive_login);	/* Exclusive access to device */
+	scsi_id->login_orb->lun_misc |= ORB_SET_EXCLUSIVE(exclusive_login);	/* Exclusive access to device */
 	scsi_id->login_orb->lun_misc |= ORB_SET_NOTIFY(1);	/* Notify us of login complete */
 	/* Set the lun if we were able to pull it from the device's unit directory */
 	if (scsi_id->sbp2_device_type_and_lun != SBP2_DEVICE_TYPE_LUN_UNINITIALIZED) {
@@ -1438,8 +1431,9 @@
  * This function is called in order to logout from a particular SBP-2
  * device, usually called during driver unload.
  */
-static int sbp2_logout_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) 
+static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id) 
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	quadlet_t data[2];
 
 	SBP2_DEBUG("sbp2_logout_device");
@@ -1496,8 +1490,9 @@
  * This function is called in order to reconnect to a particular SBP-2
  * device, after a bus reset.
  */
-static int sbp2_reconnect_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id) 
+static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id) 
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	quadlet_t data[2];
 
 	SBP2_DEBUG("sbp2_reconnect_device");
@@ -1584,8 +1579,8 @@
  * This function is called in order to set the busy timeout (number of
  * retries to attempt) on the sbp2 device. 
  */
-static int sbp2_set_busy_timeout(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id)
-{      
+static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id)
+{
 	quadlet_t data;
 
 	SBP2_DEBUG("sbp2_set_busy_timeout");
@@ -1625,7 +1620,7 @@
 	ud = scsi_id->ud;
 
 	/* Handle different fields in the unit directory, based on keys */
-	for (i = 0; i < ud->count; i++) {
+	for (i = 0; i < ud->length; i++) {
 		switch (CONFIG_ROM_KEY(ud->quadlets[i])) {
 		case SBP2_CSR_OFFSET_KEY:
 			/* Save off the management agent address */
@@ -1679,7 +1674,7 @@
 			/* Firmware revision */
 			scsi_id->sbp2_firmware_revision
 				= CONFIG_ROM_VALUE(ud->quadlets[i]);
-			if (sbp2_force_inquiry_hack)
+			if (force_inquiry_hack)
 				SBP2_INFO("sbp2_firmware_revision = %x",
 				   (unsigned int) scsi_id->sbp2_firmware_revision);
 			else	SBP2_DEBUG("sbp2_firmware_revision = %x",
@@ -1697,20 +1692,20 @@
 
 	/* If the vendor id is 0xa0b8 (Symbios vendor id), then we have a
 	 * bridge with 128KB max transfer size limitation. For sanity, we
-	 * only voice this when the current sbp2_max_sectors setting
+	 * only voice this when the current max_sectors setting
 	 * exceeds the 128k limit. By default, that is not the case.
 	 *
 	 * It would be really nice if we could detect this before the scsi
 	 * host gets initialized. That way we can down-force the
-	 * sbp2_max_sectors to account for it. That is not currently
+	 * max_sectors to account for it. That is not currently
 	 * possible.  */
 	if ((scsi_id->sbp2_firmware_revision & 0xffff00) ==
 			SBP2_128KB_BROKEN_FIRMWARE &&
-			(sbp2_max_sectors * 512) > (128*1024)) {
+			(max_sectors * 512) > (128*1024)) {
 		SBP2_WARN("Node " NODE_BUS_FMT ": Bridge only supports 128KB max transfer size.",
 				NODE_BUS_ARGS(scsi_id->ne->nodeid));
-		SBP2_WARN("WARNING: Current sbp2_max_sectors setting is larger than 128KB (%d sectors)!",
-				sbp2_max_sectors);
+		SBP2_WARN("WARNING: Current max_sectors setting is larger than 128KB (%d sectors)!",
+				max_sectors);
 		scsi_id->workarounds |= SBP2_BREAKAGE_128K_MAX_TRANSFER;
 	}
 
@@ -1737,8 +1732,10 @@
  * the speed that it needs to use, and the max_rec the host supports, and
  * it takes care of the rest.
  */
-static int sbp2_max_speed_and_size(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id)
+static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id)
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
+
 	SBP2_DEBUG("sbp2_max_speed_and_size");
 
 	/* Initial setting comes from the hosts speed map */
@@ -1746,8 +1743,8 @@
 						  + NODEID_TO_NODE(scsi_id->ne->nodeid)];
 
 	/* Bump down our speed if the user requested it */
-	if (scsi_id->speed_code > sbp2_max_speed) {
-		scsi_id->speed_code = sbp2_max_speed;
+	if (scsi_id->speed_code > max_speed) {
+		scsi_id->speed_code = max_speed;
 		SBP2_ERR("Forcing SBP-2 max speed down to %s",
 			 hpsb_speedto_str[scsi_id->speed_code]);
 	}
@@ -1767,8 +1764,9 @@
 /*
  * This function is called in order to perform a SBP-2 agent reset. 
  */
-static int sbp2_agent_reset(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, int wait) 
+static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait) 
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	struct hpsb_packet *packet;
 	quadlet_t data;
 	
@@ -1811,8 +1809,7 @@
  * This function is called to create the actual command orb and s/g list
  * out of the scsi command itself.
  */
-static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi, 
-				   struct scsi_id_instance_data *scsi_id,
+static int sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id,
 				   struct sbp2_command_info *command,
 				   unchar *scsi_cmd,
 				   unsigned int scsi_use_sg,
@@ -1820,6 +1817,7 @@
 				   void *scsi_request_buffer, 
 				   unsigned char scsi_dir)
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	struct scatterlist *sgpnt = (struct scatterlist *) scsi_request_buffer;
 	struct sbp2_command_orb *command_orb = &command->command_orb;
 	struct sbp2_unrestricted_page_table *scatter_gather_element =
@@ -2062,9 +2060,10 @@
 /*
  * This function is called in order to begin a regular SBP-2 command. 
  */
-static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
+static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
 				 struct sbp2_command_info *command)
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
         struct hpsb_packet *packet;
 	struct sbp2_command_orb *command_orb = &command->command_orb;
 
@@ -2166,7 +2165,7 @@
 /*
  * This function is called in order to begin a regular SBP-2 command. 
  */
-static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
+static int sbp2_send_command(struct scsi_id_instance_data *scsi_id,
 			     Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
 	unchar *cmd = (unchar *) SCpnt->cmnd;
@@ -2184,7 +2183,7 @@
 	/*
 	 * Allocate a command orb and s/g structure
 	 */
-	command = sbp2util_allocate_command_orb(scsi_id, SCpnt, done, hi);
+	command = sbp2util_allocate_command_orb(scsi_id, SCpnt, done);
 	if (!command) {
 		return(-EIO);
 	}
@@ -2195,7 +2194,7 @@
 	 * reject this inquiry command. Fix the request_bufflen. 
 	 */
 	if (*cmd == INQUIRY) {
-		if (sbp2_force_inquiry_hack || scsi_id->workarounds & SBP2_BREAKAGE_INQUIRY_HACK)
+		if (force_inquiry_hack || scsi_id->workarounds & SBP2_BREAKAGE_INQUIRY_HACK)
 			request_bufflen = cmd[4] = 0x24;
 		else
 			request_bufflen = cmd[4];
@@ -2204,7 +2203,7 @@
 	/*
 	 * Now actually fill in the comamnd orb and sbp2 s/g list
 	 */
-	sbp2_create_command_orb(hi, scsi_id, command, cmd, SCpnt->use_sg,
+	sbp2_create_command_orb(scsi_id, command, cmd, SCpnt->use_sg,
 				request_bufflen, SCpnt->request_buffer,
 				SCpnt->sc_data_direction); 
 	/*
@@ -2224,7 +2223,7 @@
 	/*
 	 * Link up the orb, and ring the doorbell if needed
 	 */
-	sbp2_link_orb_command(hi, scsi_id, command);
+	sbp2_link_orb_command(scsi_id, command);
 	
 	return(0);
 }
@@ -2543,7 +2542,7 @@
 				 * Initiate a fetch agent reset. 
 				 */
 				SBP2_DEBUG("Dead bit set - initiating fetch agent reset");
-                                sbp2_agent_reset(hi, scsi_id, 0);
+                                sbp2_agent_reset(scsi_id, 0);
 			}
 
 			SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb);
@@ -2583,7 +2582,8 @@
 		 * io_request_lock (in sbp2scsi_queuecommand).
 		 */
 		SBP2_DEBUG("Completing SCSI command");
-		sbp2scsi_complete_command(hi, scsi_id, scsi_status, SCpnt, command->Current_done);
+		sbp2scsi_complete_command(scsi_id, scsi_status, SCpnt,
+					  command->Current_done);
 		SBP2_ORB_DEBUG("command orb completed");
 	}
 
@@ -2649,7 +2649,7 @@
 		SBP2_DEBUG("REQUEST_SENSE");
 		memcpy(SCpnt->request_buffer, SCpnt->sense_buffer, SCpnt->request_bufflen);
 		memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
-		sbp2scsi_complete_command(hi, scsi_id, SBP2_SCSI_STATUS_GOOD, SCpnt, done);
+		sbp2scsi_complete_command(scsi_id, SBP2_SCSI_STATUS_GOOD, SCpnt, done);
 		return(0);
 	}
 
@@ -2667,9 +2667,10 @@
 	 * Try and send our SCSI command
 	 */
 	spin_lock_irqsave(&hi->sbp2_command_lock, flags);
-	if (sbp2_send_command(hi, scsi_id, SCpnt, done)) {
+	if (sbp2_send_command(scsi_id, SCpnt, done)) {
 		SBP2_ERR("Error sending SCSI command");
-		sbp2scsi_complete_command(hi, scsi_id, SBP2_SCSI_STATUS_SELECTION_TIMEOUT, SCpnt, done);
+		sbp2scsi_complete_command(scsi_id, SBP2_SCSI_STATUS_SELECTION_TIMEOUT,
+					  SCpnt, done);
 	}
 	spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
 
@@ -2680,10 +2681,10 @@
  * This function is called in order to complete all outstanding SBP-2
  * commands (in case of resets, etc.).
  */
-static void sbp2scsi_complete_all_commands(struct sbp2scsi_host_info *hi,
-					   struct scsi_id_instance_data *scsi_id, 
+static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id, 
 					   u32 status)
 {
+	struct sbp2scsi_host_info *hi = scsi_id->hi;
 	struct list_head *lh;
 	struct sbp2_command_info *command;
 
@@ -2715,8 +2716,7 @@
  *
  * This can be called in interrupt context.
  */
-static void sbp2scsi_complete_command(struct sbp2scsi_host_info *hi,
-				      struct scsi_id_instance_data *scsi_id,
+static void sbp2scsi_complete_command(struct scsi_id_instance_data *scsi_id,
 				      u32 scsi_status, Scsi_Cmnd *SCpnt,
 				      void (*done)(Scsi_Cmnd *))
 {
@@ -2825,9 +2825,9 @@
 	done (SCpnt);
 	spin_unlock_irqrestore(&io_request_lock,flags);
 #else
-	spin_lock_irqsave(hi->scsi_host->host_lock,flags);
+	spin_lock_irqsave(scsi_id->hi->scsi_host->host_lock,flags);
 	done (SCpnt);
-	spin_unlock_irqrestore(hi->scsi_host->host_lock,flags);
+	spin_unlock_irqrestore(scsi_id->hi->scsi_host->host_lock,flags);
 #endif
 
 	return;
@@ -2876,8 +2876,8 @@
 		/*
 		 * Initiate a fetch agent reset. 
 		 */
-		sbp2_agent_reset(hi, scsi_id, 0);
-		sbp2scsi_complete_all_commands(hi, scsi_id, DID_BUS_BUSY);		
+		sbp2_agent_reset(scsi_id, 0);
+		sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);		
 		spin_unlock_irqrestore(&hi->sbp2_command_lock, flags);
 	}
 
@@ -2896,7 +2896,7 @@
 
 	if (scsi_id) {
 		SBP2_ERR("Generating sbp2 fetch agent reset");
-		sbp2_agent_reset(hi, scsi_id, 0);
+		sbp2_agent_reset(scsi_id, 0);
 	}
 
 	return(SUCCESS);
@@ -2904,7 +2904,7 @@
 
 static const char *sbp2scsi_info (struct Scsi_Host *host)
 {
-        return "SCSI emulation for for IEEE-1394 Storage Devices";
+        return "SCSI emulation for IEEE-1394 SBP-2 Devices";
 }
 
 /* Called for contents of procfs */
@@ -2936,10 +2936,10 @@
 	SPRINTF("Driver version         : %s\n", version);
 
 	SPRINTF("\nModule options         :\n");
-	SPRINTF("  sbp2_max_speed       : %s\n", hpsb_speedto_str[sbp2_max_speed]);
-	SPRINTF("  sbp2_max_sectors     : %d\n", sbp2_max_sectors);
-	SPRINTF("  sbp2_serialize_io    : %s\n", sbp2_serialize_io ? "yes" : "no");
-	SPRINTF("  sbp2_exclusive_login : %s\n", sbp2_exclusive_login ? "yes" : "no");
+	SPRINTF("  max_speed       : %s\n", hpsb_speedto_str[max_speed]);
+	SPRINTF("  max_sectors     : %d\n", max_sectors);
+	SPRINTF("  serialize_io    : %s\n", serialize_io ? "yes" : "no");
+	SPRINTF("  exclusive_login : %s\n", exclusive_login ? "yes" : "no");
 
 	SPRINTF("\nAttached devices       : %s\n", !list_empty(&host->my_devices) ?
 		"" : "none");
@@ -3010,7 +3010,7 @@
 
 	/* Module load debug option to force one command at a time
 	 * (serializing I/O) */
-	if (sbp2_serialize_io) {
+	if (serialize_io) {
 		SBP2_ERR("Driver forced to serialize I/O (serialize_io = 1)");
 		scsi_driver_template.can_queue = 1;
 		scsi_driver_template.cmd_per_lun = 1;
@@ -3019,7 +3019,7 @@
 	/* 
 	 * Set max sectors (module load option). Default is 255 sectors. 
 	 */
-	scsi_driver_template.max_sectors = sbp2_max_sectors;
+	scsi_driver_template.max_sectors = max_sectors;
 
 
 	/*
diff -urN linux-2.5.67-bk3/drivers/ieee1394/sbp2.h linux-2.5.67-bk4/drivers/ieee1394/sbp2.h
--- linux-2.5.67-bk3/drivers/ieee1394/sbp2.h	2003-04-07 10:30:44.000000000 -0700
+++ linux-2.5.67-bk4/drivers/ieee1394/sbp2.h	2003-04-12 04:33:21.000000000 -0700
@@ -238,7 +238,7 @@
  */
 
 #define SBP2_MAX_SG_ELEMENT_LENGTH	0xf000
-#define SBP2SCSI_MAX_SCSI_IDS		16	/* Max sbp2 device instances supported */
+#define SBP2SCSI_MAX_SCSI_IDS		32	/* Max sbp2 device instances supported */
 #define SBP2_MAX_SECTORS		255	/* Max sectors supported */
 
 #ifndef TYPE_SDAD
@@ -320,6 +320,10 @@
 #define SBP2_BREAKAGE_128K_MAX_TRANSFER		0x1
 #define SBP2_BREAKAGE_INQUIRY_HACK		0x2
 
+
+struct sbp2scsi_host_info;
+
+
 /*
  * Information needed on a per scsi id basis (one for each sbp2 device)
  */
@@ -375,6 +379,9 @@
 	/* Node entry, as retrieved from NodeMgr entries */
 	struct node_entry *ne;
 
+	/* A backlink to our host_info */
+	struct sbp2scsi_host_info *hi;
+
 	/* Device specific workarounds/brokeness */
 	u32 workarounds;
 };
@@ -406,7 +413,6 @@
 	 * SCSI ID instance data (one for each sbp2 device instance possible)
 	 */
 	struct scsi_id_instance_data *scsi_id[SBP2SCSI_MAX_SCSI_IDS];
-
 };
 
 /*
@@ -416,30 +422,30 @@
 /*
  * Various utility prototypes
  */
-static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id, struct sbp2scsi_host_info *hi);
-static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id, struct sbp2scsi_host_info *hi);
+static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_id);
+static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_id);
 static struct sbp2_command_info *sbp2util_find_command_for_orb(struct scsi_id_instance_data *scsi_id, dma_addr_t orb);
 static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(struct scsi_id_instance_data *scsi_id, void *SCpnt);
 static struct sbp2_command_info *sbp2util_allocate_command_orb(struct scsi_id_instance_data *scsi_id, 
 							  Scsi_Cmnd *Current_SCpnt, 
-							  void (*Current_done)(Scsi_Cmnd *),
-							  struct sbp2scsi_host_info *hi);
+							  void (*Current_done)(Scsi_Cmnd *));
 static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_id,
 		struct sbp2_command_info *command);
 
 /*
  * IEEE-1394 core driver related prototypes
  */
-static void sbp2_add_host(struct hpsb_host *host);
+static struct sbp2scsi_host_info *sbp2_add_host(struct hpsb_host *host);
 static struct sbp2scsi_host_info *sbp2_find_host_info(struct hpsb_host *host);
 static void sbp2_remove_host(struct hpsb_host *host);
-static int sbp2_probe(struct unit_directory *ud);
-static void sbp2_disconnect(struct unit_directory *ud);
+
+static int sbp2_probe(struct device *dev);
+static int sbp2_remove(struct device *dev);
 static void sbp2_update(struct unit_directory *ud);
+
 static int sbp2_start_device(struct sbp2scsi_host_info *hi, 
 			     struct unit_directory *ud);
-static void sbp2_remove_device(struct sbp2scsi_host_info *hi, 
-			       struct scsi_id_instance_data *scsi_id);
+static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id);
 
 #ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
 static int sbp2_handle_physdma_write(struct hpsb_host *host, int nodeid, int destid, quadlet_t *data,
@@ -451,29 +457,28 @@
 /*
  * SBP-2 protocol related prototypes
  */
-static int sbp2_login_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id);
-static int sbp2_reconnect_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id); 
-static int sbp2_logout_device(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id); 
+static int sbp2_login_device(struct scsi_id_instance_data *scsi_id);
+static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id); 
+static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id); 
 static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid,
 				    quadlet_t *data, u64 addr, unsigned int length, u16 flags);
-static int sbp2_agent_reset(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id, int wait);
-static int sbp2_create_command_orb(struct sbp2scsi_host_info *hi, 
-				   struct scsi_id_instance_data *scsi_id,
+static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait);
+static int sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id,
 				   struct sbp2_command_info *command,
 				   unchar *scsi_cmd,
 				   unsigned int scsi_use_sg,
 				   unsigned int scsi_request_bufflen,
 				   void *scsi_request_buffer, 
 				   unsigned char scsi_dir);
-static int sbp2_link_orb_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
+static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
 				 struct sbp2_command_info *command);
-static int sbp2_send_command(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id,
+static int sbp2_send_command(struct scsi_id_instance_data *scsi_id,
 			     Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
 static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense_data);
 static void sbp2_check_sbp2_command(struct scsi_id_instance_data *scsi_id, unchar *cmd);
 static void sbp2_check_sbp2_response(struct scsi_id_instance_data *scsi_id, Scsi_Cmnd *SCpnt);
 static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id);
-static int sbp2_set_busy_timeout(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id);
-static int sbp2_max_speed_and_size(struct sbp2scsi_host_info *hi, struct scsi_id_instance_data *scsi_id);
+static int sbp2_set_busy_timeout(struct scsi_id_instance_data *scsi_id);
+static int sbp2_max_speed_and_size(struct scsi_id_instance_data *scsi_id);
 
 #endif /* SBP2_H */
diff -urN linux-2.5.67-bk3/drivers/usb/class/audio.c linux-2.5.67-bk4/drivers/usb/class/audio.c
--- linux-2.5.67-bk3/drivers/usb/class/audio.c	2003-04-07 10:32:53.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/class/audio.c	2003-04-12 04:33:22.000000000 -0700
@@ -99,6 +99,8 @@
  *              for abs. Bug report by Andrew Morton <andrewm@uow.edu.au>
  * 2001-06-16:  Bryce Nesbitt <bryce@obviously.com>
  *              Fix SNDCTL_DSP_STEREO API violation
+ * 2003-04-08:	Oliver Neukum (oliver@neukum.name):
+ *		Setting a configuration is done by usbcore and must not be overridden
  */
 
 /*
@@ -3790,10 +3792,6 @@
 	 */
 	i = dev->actconfig - config;
 
-	if (usb_set_configuration(dev, config->desc.bConfigurationValue) < 0) {
-		printk(KERN_ERR "usbaudio: set_configuration failed (ConfigValue 0x%x)\n", config->desc.bConfigurationValue);
-		return -EIO;
-	}
 	ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8);
 	if (ret < 0) {
 		printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d (error %d)\n", i, dev->devnum, ret);
diff -urN linux-2.5.67-bk3/drivers/usb/class/usb-midi.c linux-2.5.67-bk4/drivers/usb/class/usb-midi.c
--- linux-2.5.67-bk3/drivers/usb/class/usb-midi.c	2003-04-07 10:30:35.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/class/usb-midi.c	2003-04-12 04:33:22.000000000 -0700
@@ -1806,22 +1806,6 @@
 	printk(KERN_INFO "usb-midi: Found YAMAHA USB-MIDI device on dev %04x:%04x, iface %d\n",
 	       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 );
 	if ( ret < 0 ) {
 		printk(KERN_INFO "usb-midi: Could not get config (error=%d).\n", ret);
@@ -1916,21 +1900,6 @@
 	printk(KERN_INFO "usb-midi: Found MIDISTREAMING on dev %04x:%04x, iface %d\n",
 	       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.
 	   If the class or vendor specific descriptors use the same format
diff -urN linux-2.5.67-bk3/drivers/usb/core/hub.c linux-2.5.67-bk4/drivers/usb/core/hub.c
--- linux-2.5.67-bk3/drivers/usb/core/hub.c	2003-04-07 10:31:15.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/core/hub.c	2003-04-12 04:33:22.000000000 -0700
@@ -1175,8 +1175,10 @@
  *
  * Take a look at proc_resetdevice in devio.c for some sample code to
  * do this.
+ * Use this only from within your probe function, otherwise use
+ * usb_reset_device() below, which ensure proper locking
  */
-int usb_reset_device(struct usb_device *dev)
+int usb_physical_reset_device(struct usb_device *dev)
 {
 	struct usb_device *parent = dev->parent;
 	struct usb_device_descriptor *descriptor;
@@ -1306,3 +1308,16 @@
 	return 0;
 }
 
+int usb_reset_device(struct usb_device *udev)
+{
+	struct device *gdev = &udev->dev;
+	int r;
+	
+	down_read(&gdev->bus->subsys.rwsem);
+	r = usb_physical_reset_device(udev);
+	up_read(&gdev->bus->subsys.rwsem);
+
+	return r;
+}
+
+
diff -urN linux-2.5.67-bk3/drivers/usb/core/message.c linux-2.5.67-bk4/drivers/usb/core/message.c
--- linux-2.5.67-bk3/drivers/usb/core/message.c	2003-04-07 10:30:57.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/core/message.c	2003-04-12 04:33:22.000000000 -0700
@@ -930,6 +930,8 @@
 	/* reset more hc/hcd endpoint state */
 	dev->toggle[0] = 0;
 	dev->toggle[1] = 0;
+	dev->halted[0] = 0;
+	dev->halted[1] = 0;
 	usb_set_maxpacket(dev);
 
 	return 0;
diff -urN linux-2.5.67-bk3/drivers/usb/core/usb.c linux-2.5.67-bk4/drivers/usb/core/usb.c
--- linux-2.5.67-bk3/drivers/usb/core/usb.c	2003-04-07 10:31:22.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/core/usb.c	2003-04-12 04:33:22.000000000 -0700
@@ -89,11 +89,6 @@
 	if (!driver->probe)
 		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);
 	if (id) {
 		dev_dbg (dev, "%s - got id\n", __FUNCTION__);
@@ -104,8 +99,6 @@
 	if (!error)
 		intf->driver = driver;
 
-	module_put(driver->owner);
-
 	return error;
 }
 
@@ -117,22 +110,6 @@
 	intf = list_entry(dev,struct usb_interface,dev);
 	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);
 
 	if (intf->driver && intf->driver->disconnect)
@@ -143,7 +120,6 @@
 		usb_driver_release_interface(driver, intf);
 
 	up(&driver->serialize);
-	module_put(driver->owner);
 
 	return 0;
 }
@@ -498,9 +474,6 @@
 			continue;
 
 		intf = to_usb_interface(dev);
-		if (!intf)
-			continue;
-
 		if (kdev_same(intf->kdev,kdev)) {
 			return intf;
 		}
@@ -566,12 +539,7 @@
 		return 0;
 
 	intf = to_usb_interface(dev);
-	if (!intf)
-		return -ENODEV;
-
 	usb_dev = interface_to_usbdev (intf);
-	if (!usb_dev)
-		return -ENODEV;
 	
 	if (usb_dev->devnum < 0) {
 		dbg ("device already deleted ??");
@@ -748,8 +716,6 @@
 	struct usb_device *udev;
 
 	udev = to_usb_device(dev);
-	if (!udev)
-		return;
 
 	if (udev->bus && udev->bus->op && udev->bus->op->deallocate)
 		udev->bus->op->deallocate(udev);
diff -urN linux-2.5.67-bk3/drivers/usb/host/ehci-mem.c linux-2.5.67-bk4/drivers/usb/host/ehci-mem.c
--- linux-2.5.67-bk3/drivers/usb/host/ehci-mem.c	2003-04-07 10:32:50.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/host/ehci-mem.c	2003-04-12 04:33:22.000000000 -0700
@@ -62,6 +62,7 @@
 {
 	memset (qtd, 0, sizeof *qtd);
 	qtd->qtd_dma = dma;
+	qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
 	qtd->hw_next = EHCI_LIST_END;
 	qtd->hw_alt_next = EHCI_LIST_END;
 	INIT_LIST_HEAD (&qtd->qtd_list);
diff -urN linux-2.5.67-bk3/drivers/usb/host/ehci-q.c linux-2.5.67-bk4/drivers/usb/host/ehci-q.c
--- linux-2.5.67-bk3/drivers/usb/host/ehci-q.c	2003-04-07 10:32:23.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/host/ehci-q.c	2003-04-12 04:33:22.000000000 -0700
@@ -137,7 +137,10 @@
 			if (QTD_CERR (token))
 				urb->status = -EPIPE;
 			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;
 			}
 		/* CERR nonzero + no errors + halt --> stall */
@@ -213,7 +216,6 @@
 	/* complete() can reenter this HCD */
 	spin_unlock (&ehci->lock);
 	usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
-
 	spin_lock (&ehci->lock);
 }
 
@@ -827,7 +829,7 @@
 			 * HC is allowed to fetch the old dummy (4.10.2).
 			 */
 			token = qtd->hw_token;
-			qtd->hw_token = 0;
+			qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
 			wmb ();
 			dummy = qh->dummy;
 
@@ -879,8 +881,7 @@
 	if (usb_pipein (urb->pipe) && !usb_pipecontrol (urb->pipe))
 		epnum |= 0x10;
 
-	vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]",
-		hcd_to_bus (&ehci->hcd)->bus_name,
+	ehci_vdbg (ehci, "submit_async urb %p len %d ep%d%s qtd %p [qh %p]\n",
 		urb, urb->transfer_buffer_length,
 		epnum & 0x0f, (epnum & 0x10) ? "in" : "out",
 		qtd, dev ? dev->ep [epnum] : (void *)~0);
@@ -916,7 +917,7 @@
 
 	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_next.qh = 0;
 	qh_put (ehci, qh);			// refcount from reclaim 
diff -urN linux-2.5.67-bk3/drivers/usb/host/ohci-hcd.c linux-2.5.67-bk4/drivers/usb/host/ohci-hcd.c
--- linux-2.5.67-bk3/drivers/usb/host/ohci-hcd.c	2003-04-07 10:30:40.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/host/ohci-hcd.c	2003-04-12 04:33:22.000000000 -0700
@@ -429,6 +429,8 @@
 	ohci->hc_control = readl (&ohci->regs->control);
 	ohci->hc_control &= OHCI_CTRL_RWC;	/* hcfs 0 = RESET */
 	writel (ohci->hc_control, &ohci->regs->control);
+	// flush those pci writes
+	(void) readl (&ohci->regs->control);
 	wait_ms (50);
 
 	/* HC Reset requires max 10 us delay */
@@ -450,6 +452,8 @@
 	 * this if we write fmInterval after we're OPERATIONAL.
 	 */
 	writel (ohci->hc_control, &ohci->regs->control);
+	// flush those pci writes
+	(void) readl (&ohci->regs->control);
 
 	return 0;
 }
@@ -524,6 +528,8 @@
 	writel (tmp, &ohci->regs->roothub.a);
 	writel (RH_HS_LPSC, &ohci->regs->roothub.status);
 	writel (0, &ohci->regs->roothub.b);
+	// flush those pci writes
+	(void) readl (&ohci->regs->control);
 
 	// POTPGT delay is bits 24-31, in 2 ms units.
 	mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
@@ -610,6 +616,8 @@
 
 	writel (ints, &regs->intrstatus);
 	writel (OHCI_INTR_MIE, &regs->intrenable);	
+	// flush those pci writes
+	(void) readl (&ohci->regs->control);
 }
 
 /*-------------------------------------------------------------------------*/
diff -urN linux-2.5.67-bk3/drivers/usb/host/ohci-q.c linux-2.5.67-bk4/drivers/usb/host/ohci-q.c
--- linux-2.5.67-bk3/drivers/usb/host/ohci-q.c	2003-04-07 10:32:50.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/host/ohci-q.c	2003-04-12 04:33:22.000000000 -0700
@@ -446,6 +446,8 @@
 	if (!ohci->sleeping) {
 		writel (OHCI_INTR_SF, &ohci->regs->intrstatus);
 		writel (OHCI_INTR_SF, &ohci->regs->intrenable);
+		// flush those pci writes
+		(void) readl (&ohci->regs->control);
 	}
 }
 
diff -urN linux-2.5.67-bk3/drivers/usb/input/hid-core.c linux-2.5.67-bk4/drivers/usb/input/hid-core.c
--- linux-2.5.67-bk3/drivers/usb/input/hid-core.c	2003-04-07 10:31:46.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/input/hid-core.c	2003-04-12 04:33:22.000000000 -0700
@@ -1664,6 +1664,9 @@
 	.probe =	hid_probe,
 	.disconnect =	hid_disconnect,
 	.id_table =	hid_usb_ids,
+	.driver	= {
+		.devclass = &input_devclass,
+	},
 };
 
 static int __init hid_init(void)
diff -urN linux-2.5.67-bk3/drivers/usb/input/kbtab.c linux-2.5.67-bk4/drivers/usb/input/kbtab.c
--- linux-2.5.67-bk3/drivers/usb/input/kbtab.c	2003-04-07 10:30:34.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/input/kbtab.c	2003-04-12 04:33:22.000000000 -0700
@@ -91,7 +91,7 @@
 }
 
 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 },
 	{ }
 };
 
diff -urN linux-2.5.67-bk3/drivers/usb/input/usbkbd.c linux-2.5.67-bk4/drivers/usb/input/usbkbd.c
--- linux-2.5.67-bk3/drivers/usb/input/usbkbd.c	2003-04-07 10:32:52.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/input/usbkbd.c	2003-04-12 04:33:22.000000000 -0700
@@ -359,6 +359,9 @@
 	.probe =	usb_kbd_probe,
 	.disconnect =	usb_kbd_disconnect,
 	.id_table =	usb_kbd_id_table,
+	.driver	= {
+		.devclass = &input_devclass,
+	},
 };
 
 static int __init usb_kbd_init(void)
diff -urN linux-2.5.67-bk3/drivers/usb/input/usbmouse.c linux-2.5.67-bk4/drivers/usb/input/usbmouse.c
--- linux-2.5.67-bk3/drivers/usb/input/usbmouse.c	2003-04-07 10:32:51.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/input/usbmouse.c	2003-04-12 04:33:22.000000000 -0700
@@ -242,6 +242,9 @@
 	.probe		= usb_mouse_probe,
 	.disconnect	= usb_mouse_disconnect,
 	.id_table	= usb_mouse_id_table,
+	.driver	= {
+		.devclass = &input_devclass,
+	},
 };
 
 static int __init usb_mouse_init(void)
diff -urN linux-2.5.67-bk3/drivers/usb/misc/Kconfig linux-2.5.67-bk4/drivers/usb/misc/Kconfig
--- linux-2.5.67-bk3/drivers/usb/misc/Kconfig	2003-04-07 10:32:52.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/misc/Kconfig	2003-04-12 04:33:22.000000000 -0700
@@ -94,7 +94,7 @@
 	  a module, say M here and read <file:Documentation/modules.txt>.
 
 config USB_SPEEDTOUCH
-	tristate "Alcatel Speedtouch ADSL USB Modem"
+	tristate "Alcatel Speedtouch USB support"
 	depends on USB && ATM
 	help
 	  Say Y here if you have an Alcatel SpeedTouch USB or SpeedTouch 330
diff -urN linux-2.5.67-bk3/drivers/usb/misc/speedtch.c linux-2.5.67-bk4/drivers/usb/misc/speedtch.c
--- linux-2.5.67-bk3/drivers/usb/misc/speedtch.c	2003-04-07 10:33:02.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/misc/speedtch.c	2003-04-12 04:33:22.000000000 -0700
@@ -933,15 +933,24 @@
 	if (vcc->qos.aal != ATM_AAL5)
 		return -EINVAL;
 
+	if (!instance->firmware_loaded) {
+		dbg ("firmware not loaded!");
+		return -EAGAIN;
+	}
+
+	MOD_INC_USE_COUNT;
+
 	down (&instance->serialize); /* vs self, udsl_atm_close */
 
 	if (udsl_find_vcc (instance, vpi, vci)) {
 		up (&instance->serialize);
+		MOD_DEC_USE_COUNT;
 		return -EADDRINUSE;
 	}
 
 	if (!(new = kmalloc (sizeof (struct udsl_vcc_data), GFP_KERNEL))) {
 		up (&instance->serialize);
+		MOD_DEC_USE_COUNT;
 		return -ENOMEM;
 	}
 
@@ -967,10 +976,7 @@
 
 	dbg ("Allocated new SARLib vcc 0x%p with vpi %d vci %d", new, vpi, vci);
 
-	MOD_INC_USE_COUNT;
-
-	if (instance->firmware_loaded)
-		udsl_fire_receivers (instance);
+	udsl_fire_receivers (instance);
 
 	dbg ("udsl_atm_open successful");
 
@@ -1034,6 +1040,24 @@
 **  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) {
+			dbg ("usb_set_interface returned %d!", ret);
+			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)
 {
 	struct udsl_instance_data *instance = usb_get_intfdata (intf);
@@ -1048,14 +1072,7 @@
 	switch (code) {
 	case UDSL_IOCTL_START:
 		instance->atm_dev->signal = ATM_PHY_SIG_FOUND;
-		down (&instance->serialize); /* vs self */
-		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;
+		return udsl_set_alternate (instance);
 	case UDSL_IOCTL_STOP:
 		instance->atm_dev->signal = ATM_PHY_SIG_LOST;
 		return 0;
diff -urN linux-2.5.67-bk3/drivers/usb/misc/uss720.c linux-2.5.67-bk4/drivers/usb/misc/uss720.c
--- linux-2.5.67-bk3/drivers/usb/misc/uss720.c	2003-04-07 10:30:34.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/misc/uss720.c	2003-04-12 04:33:22.000000000 -0700
@@ -493,54 +493,42 @@
 	return rlen;
 }
 
-void parport_uss720_inc_use_count(void)
-{
-	MOD_INC_USE_COUNT;
-}
-
-void parport_uss720_dec_use_count(void)
-{
-	MOD_DEC_USE_COUNT;
-}
-
 /* --------------------------------------------------------------------- */
 
 static struct parport_operations parport_uss720_ops = 
 {
-	parport_uss720_write_data,
-	parport_uss720_read_data,
-
-	parport_uss720_write_control,
-	parport_uss720_read_control,
-	parport_uss720_frob_control,
-
-	parport_uss720_read_status,
-
-	parport_uss720_enable_irq,
-	parport_uss720_disable_irq,
-
-	parport_uss720_data_forward,
-	parport_uss720_data_reverse,
-
-	parport_uss720_init_state,
-	parport_uss720_save_state,
-	parport_uss720_restore_state,
-
-	parport_uss720_inc_use_count,
-	parport_uss720_dec_use_count,
-
-	parport_uss720_epp_write_data,
-	parport_uss720_epp_read_data,
-	parport_uss720_epp_write_addr,
-	parport_uss720_epp_read_addr,
-
-	parport_uss720_ecp_write_data,
-	parport_uss720_ecp_read_data,
-	parport_uss720_ecp_write_addr,
-
-	parport_uss720_write_compat,
-	parport_ieee1284_read_nibble,
-	parport_ieee1284_read_byte,
+	.owner =		THIS_MODULE,
+	.write_data =		parport_uss720_write_data,
+	.read_data =		parport_uss720_read_data,
+
+	.write_control =	parport_uss720_write_control,
+	.read_control =		parport_uss720_read_control,
+	.frob_control =		parport_uss720_frob_control,
+
+	.read_status =		parport_uss720_read_status,
+
+	.enable_irq =		parport_uss720_enable_irq,
+	.disable_irq =		parport_uss720_disable_irq,
+
+	.data_forward =		parport_uss720_data_forward,
+	.data_reverse =		parport_uss720_data_reverse,
+
+	.init_state =		parport_uss720_init_state,
+	.save_state =		parport_uss720_save_state,
+	.restore_state =	parport_uss720_restore_state,
+
+	.epp_write_data =	parport_uss720_epp_write_data,
+	.epp_read_data =	parport_uss720_epp_read_data,
+	.epp_write_addr =	parport_uss720_epp_write_addr,
+	.epp_read_addr =	parport_uss720_epp_read_addr,
+
+	.ecp_write_data =	parport_uss720_ecp_write_data,
+	.ecp_read_data =	parport_uss720_ecp_read_data,
+	.ecp_write_addr =	parport_uss720_ecp_write_addr,
+
+	.compat_write_data =	parport_uss720_write_compat,
+	.nibble_read_data =	parport_ieee1284_read_nibble,
+	.byte_read_data =	parport_ieee1284_read_byte,
 };
 
 /* --------------------------------------------------------------------- */
@@ -607,7 +595,6 @@
 	parport_proc_register(pp);
 	parport_announce_port(pp);
 
-	MOD_INC_USE_COUNT;
 	usb_set_intfdata (intf, pp);
 	return 0;
 
@@ -635,7 +622,6 @@
 		parport_proc_unregister(pp);
 		parport_unregister_port(pp);
 		kfree(priv);
-		MOD_DEC_USE_COUNT;
 	}
 }
 
@@ -652,6 +638,7 @@
 
 
 static struct usb_driver uss720_driver = {
+	.owner =	THIS_MODULE,
 	.name =		"uss720",
 	.probe =	uss720_probe,
 	.disconnect =	uss720_disconnect,
diff -urN linux-2.5.67-bk3/drivers/usb/net/pegasus.c linux-2.5.67-bk4/drivers/usb/net/pegasus.c
--- linux-2.5.67-bk3/drivers/usb/net/pegasus.c	2003-04-07 10:32:55.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/net/pegasus.c	2003-04-12 04:33:22.000000000 -0700
@@ -45,7 +45,7 @@
 /*
  * 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_DESC "Pegasus/Pegasus II USB Ethernet driver"
 
@@ -121,7 +121,7 @@
 	char *buffer;
 	DECLARE_WAITQUEUE(wait, current);
 
-	buffer = kmalloc(size, GFP_KERNEL);
+	buffer = kmalloc(size, GFP_DMA);
 	if (!buffer) {
 		warn("%s: looks like we're out of memory", __FUNCTION__);
 		return -ENOMEM;
@@ -170,7 +170,7 @@
 	char *buffer;
 	DECLARE_WAITQUEUE(wait, current);
 
-	buffer = kmalloc(size, GFP_KERNEL);
+	buffer = kmalloc(size, GFP_DMA);
 	if (!buffer) {
 		warn("%s: looks like we're out of memory", __FUNCTION__);
 		return -ENOMEM;
@@ -218,7 +218,7 @@
 	char *tmp;
 	DECLARE_WAITQUEUE(wait, current);
 
-	tmp = kmalloc(1, GFP_KERNEL);
+	tmp = kmalloc(1, GFP_DMA);
 	if (!tmp) {
 		warn("%s: looks like we're out of memory", __FUNCTION__);
 		return -ENOMEM;
@@ -233,7 +233,7 @@
 
 	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
 	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.wLength = cpu_to_le16(1);
 	pegasus->ctrl_urb->transfer_buffer_length = 1;
@@ -711,11 +711,11 @@
 			pegasus->stats.tx_aborted_errors++;
 		if (d[0] & LATE_COL)
 			pegasus->stats.tx_window_errors++;
-		if (d[0] & (NO_CARRIER | LOSS_CARRIER)) {
-			pegasus->stats.tx_carrier_errors++;
-			netif_carrier_off(net);
-		} else {
+		if (d[5] & LINK_STATUS) {
 			netif_carrier_on(net);
+		} else {
+			pegasus->stats.tx_carrier_errors++;
+			netif_carrier_off(net);	
 		}
 	}
 
@@ -1171,10 +1171,6 @@
 	pegasus_t *pegasus;
 	int dev_index = id - pegasus_ids;
 
-	if (usb_set_configuration(dev, dev->config[0].desc.bConfigurationValue)) {
-		err("usb_set_configuration() failed");
-		return -ENODEV;
-	}
 	if (!(pegasus = kmalloc(sizeof (struct pegasus), GFP_KERNEL))) {
 		err("out of memory allocating device structure");
 		return -ENOMEM;
diff -urN linux-2.5.67-bk3/drivers/usb/net/pegasus.h linux-2.5.67-bk4/drivers/usb/net/pegasus.h
--- linux-2.5.67-bk3/drivers/usb/net/pegasus.h	2003-04-07 10:32:54.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/net/pegasus.h	2003-04-12 04:33:22.000000000 -0700
@@ -52,6 +52,8 @@
 #define	LOSS_CARRIER		0x08
 #define	JABBER_TIMEOUT		0x04
 
+#define	LINK_STATUS		0x01
+
 #define	PEGASUS_REQT_READ	0xc0
 #define	PEGASUS_REQT_WRITE	0x40
 #define	PEGASUS_REQ_GET_REGS	0xf0
diff -urN linux-2.5.67-bk3/drivers/usb/net/rtl8150.c linux-2.5.67-bk4/drivers/usb/net/rtl8150.c
--- linux-2.5.67-bk3/drivers/usb/net/rtl8150.c	2003-04-07 10:31:49.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/net/rtl8150.c	2003-04-12 04:33:22.000000000 -0700
@@ -791,10 +791,6 @@
 	rtl8150_t *dev;
 	struct net_device *netdev;
 
-	if (usb_set_configuration(udev, udev->config[0].desc.bConfigurationValue)) {
-		err("usb_set_configuration() failed");
-		return -EIO;
-	}
 	dev = kmalloc(sizeof(rtl8150_t), GFP_KERNEL);
 	if (!dev) {
 		err("Out of memory");
diff -urN linux-2.5.67-bk3/drivers/usb/net/usbnet.c linux-2.5.67-bk4/drivers/usb/net/usbnet.c
--- linux-2.5.67-bk3/drivers/usb/net/usbnet.c	2003-04-07 10:31:41.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/net/usbnet.c	2003-04-12 04:33:22.000000000 -0700
@@ -122,6 +122,11 @@
  * 		cleanups and stubbed PXA-250 support (db), fix for framing
  * 		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>
@@ -155,16 +160,17 @@
 /* minidrivers _could_ be individually configured */
 #define	CONFIG_USB_AN2720
 #define	CONFIG_USB_BELKIN
+#undef	CONFIG_USB_CDCETHER
+//#define	CONFIG_USB_CDCETHER		/* NYET */
 #define	CONFIG_USB_EPSON2888
 #define	CONFIG_USB_GENESYS
 #define	CONFIG_USB_NET1080
 #define	CONFIG_USB_PL2301
-// #define	CONFIG_USB_PXA
-#define	CONFIG_USB_SA1100
+#define	CONFIG_USB_ARMLINUX
 #define	CONFIG_USB_ZAURUS
 
 
-#define DRIVER_VERSION		"18-Oct-2002"
+#define DRIVER_VERSION		"31-Mar-2003"
 
 /*-------------------------------------------------------------------------*/
 
@@ -176,11 +182,11 @@
  * Ethernet packets (so queues should be bigger).
  */
 #ifdef REALLY_QUEUE
-#define	RX_QLEN		4
-#define	TX_QLEN		4
+#define	RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+#define	TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
 #else
-#define	RX_QLEN		1
-#define	TX_QLEN		1
+#define	RX_QLEN(dev)		1
+#define	TX_QLEN(dev)		1
 #endif
 
 // packets are always ethernet inside
@@ -191,6 +197,10 @@
 // reawaken network queue this soon after stopping; else watchdog barks
 #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
 #define	CONTROL_TIMEOUT_MS	(500)			/* msec */
 #define CONTROL_TIMEOUT_JIFFIES ((CONTROL_TIMEOUT_MS * HZ)/1000)
@@ -200,10 +210,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-// list of all devices we manage
-static DECLARE_MUTEX (usbnet_mutex);
-static LIST_HEAD (usbnet_list);
-
 // randomly generated ethernet address
 static u8	node_id [ETH_ALEN];
 
@@ -213,17 +219,18 @@
 	struct usb_device	*udev;
 	struct driver_info	*driver_info;
 	struct semaphore	mutex;
-	struct list_head	dev_list;
 	wait_queue_head_t	*wait;
 
+	// i/o info: pipes etc
+	unsigned		in, out;
+	unsigned		maxpacket;
+	struct timer_list	delay;
+
 	// protocol/interface state
 	struct net_device	net;
 	struct net_device_stats	stats;
 	int			msg_level;
-
-#ifdef CONFIG_USB_NET1080
-	u16			packet_id;
-#endif
+	unsigned long		data [5];
 
 	// various kinds of pending driver work
 	struct sk_buff_head	rxq;
@@ -231,7 +238,7 @@
 	struct sk_buff_head	done;
 	struct tasklet_struct	bh;
 
-	struct work_struct			kevent;
+	struct work_struct	kevent;
 	unsigned long		flags;
 #		define EVENT_TX_HALT	0
 #		define EVENT_RX_HALT	1
@@ -243,11 +250,19 @@
 	char		*description;
 
 	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_GL	0x0002		/* genelink batches packets */
 #define FLAG_FRAMING_Z	0x0004		/* zaurus adds a trailer */
+
 #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 */
 	int	(*reset)(struct usbnet *);
 
@@ -263,15 +278,13 @@
 
 	// FIXME -- also an interrupt mechanism
 	// 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		out;		/* tx endpoint */
-	int		epsize;
 };
 
-#define EP_SIZE(usbnet)	((usbnet)->driver_info->epsize)
-
 // we record the state for each of our queued skbs
 enum skb_state {
 	illegal = 0,
@@ -300,14 +313,6 @@
 #define	RUN_CONTEXT (in_irq () ? "in_irq" \
 			: (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
 #define devdbg(usbnet, fmt, arg...) \
 	printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net.name, ## arg)
@@ -315,11 +320,76 @@
 #define devdbg(usbnet, fmt, arg...) do {} while(0)
 #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...) \
 	do { if ((usbnet)->msg_level >= 1) \
 	printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net.name, ## arg); \
 	} 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
 
@@ -340,7 +410,6 @@
 	// no check_connect available!
 
 	.in = 2, .out = 2,		// direction distinguishes these
-	.epsize =64,
 };
 
 #endif	/* CONFIG_USB_AN2720 */
@@ -359,15 +428,226 @@
 
 static const struct driver_info	belkin_info = {
 	.description =	"Belkin, eTEK, or compatible",
-
-	.in = 1, .out = 1,		// direction distinguishes these
-	.epsize =64,
 };
 
 #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
 
 /*-------------------------------------------------------------------------
@@ -386,7 +666,6 @@
 	.check_connect = always_connected,
 
 	.in = 4, .out = 3,
-	.epsize = 64,
 };
 
 #endif	/* CONFIG_USB_EPSON2888 */
@@ -704,7 +983,7 @@
 	*packet_len = length;
 
 	// add padding byte
-	if ((skb->len % EP_SIZE (dev)) == 0)
+	if ((skb->len % dev->maxpacket) == 0)
 		skb_put (skb, 1);
 
 	return skb;
@@ -717,7 +996,6 @@
 	.tx_fixup =	genelink_tx_fixup,
 
 	.in = 1, .out = 2,
-	.epsize =64,
 
 #ifdef	GENELINK_ACK
 	.check_connect =genelink_check_connect,
@@ -737,6 +1015,9 @@
  *
  *-------------------------------------------------------------------------*/
 
+#define dev_packet_id	data[0]
+#define frame_errors	data[1]
+
 /*
  * NetChip framing of ethernet packets, supporting additional error
  * checks for links that may drop bulk packets from inside messages.
@@ -1064,6 +1345,60 @@
 	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)
 {
 	struct nc_header	*header;
@@ -1076,6 +1411,7 @@
 		dbg ("rx framesize %d range %d..%d mtu %d", skb->len,
 			(int)MIN_FRAMED, (int)FRAMED_SIZE (dev->net.mtu),
 			dev->net.mtu);
+		nc_ensure_sync (dev);
 		return 0;
 	}
 
@@ -1085,15 +1421,18 @@
 	if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) {
 		dev->stats.rx_frame_errors++;
 		dbg ("packet too big, %d", header->packet_len);
+		nc_ensure_sync (dev);
 		return 0;
 	} else if (header->hdr_len < MIN_HEADER) {
 		dev->stats.rx_frame_errors++;
 		dbg ("header too short, %d", header->hdr_len);
+		nc_ensure_sync (dev);
 		return 0;
 	} else if (header->hdr_len > MIN_HEADER) {
 		// out of band data for us?
 		dbg ("header OOB, %d bytes",
 			header->hdr_len - MIN_HEADER);
+		nc_ensure_sync (dev);
 		// switch (vendor/product ids) { ... }
 	}
 	skb_pull (skb, header->hdr_len);
@@ -1114,6 +1453,7 @@
 		dev->stats.rx_frame_errors++;
 		dbg ("bad packet len %d (expected %d)",
 			skb->len, header->packet_len);
+		nc_ensure_sync (dev);
 		return 0;
 	}
 	if (header->packet_id != get_unaligned (&trailer->packet_id)) {
@@ -1126,6 +1466,7 @@
 	devdbg (dev, "frame <rx h %d p %d id %d", header->hdr_len,
 		header->packet_len, header->packet_id);
 #endif
+	dev->frame_errors = 0;
 	return 1;
 }
 
@@ -1143,11 +1484,13 @@
 
 		if ((padlen + sizeof (struct nc_trailer)) <= tailroom
 			    && sizeof (struct nc_header) <= headroom)
+			/* There's enough head and tail room */
 			return skb;
 
 		if ((sizeof (struct nc_header) + padlen
 					+ sizeof (struct nc_trailer)) <
 				(headroom + tailroom)) {
+			/* There's enough total room, so just readjust */
 			skb->data = memmove (skb->head
 						+ sizeof (struct nc_header),
 					    skb->data, skb->len);
@@ -1155,6 +1498,8 @@
 			return skb;
 		}
 	}
+
+	/* Create a new skb to use with the correct size */
 	skb2 = skb_copy_expand (skb,
 				sizeof (struct nc_header),
 				sizeof (struct nc_trailer) + padlen,
@@ -1170,9 +1515,6 @@
 	.check_connect =net1080_check_connect,
 	.rx_fixup =	net1080_rx_fixup,
 	.tx_fixup =	net1080_tx_fixup,
-
-	.in = 1, .out = 1,		// direction distinguishes these
-	.epsize =64,
 };
 
 #endif /* CONFIG_USB_NET1080 */
@@ -1237,37 +1579,13 @@
 	.flags =	FLAG_NO_SETINT,
 		/* some PL-2302 versions seem to fail usb_set_interface() */
 	.reset =	pl_reset,
-
-	.in = 3, .out = 2,
-	.epsize =64,
 };
 
 #endif /* CONFIG_USB_PL2301 */
 
 
 
-#ifdef	CONFIG_USB_PXA
-
-/*-------------------------------------------------------------------------
- *
- * 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
+#ifdef	CONFIG_USB_ARMLINUX
 
 /*-------------------------------------------------------------------------
  *
@@ -1279,25 +1597,24 @@
  * This describes the driver currently in standard ARM Linux kernels.
  * 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 = {
-	.description =	"SA-1100 Linux Device",
+	.description =	"Linux Device",
 	.check_connect = always_connected,
-
-	.in = 2, .out = 1,
-	.epsize = 64,
 };
 
 static const struct driver_info	yopy_info = {
 	.description =	"Yopy",
 	.check_connect = always_connected,
-
-	.in = 2, .out = 1,
-	.epsize = 64,
 };
 
-#endif	/* CONFIG_USB_SA1100 */
+#endif	/* CONFIG_USB_ARMLINUX */
 
 
 #ifdef CONFIG_USB_ZAURUS
@@ -1349,10 +1666,9 @@
 	.description =	"Sharp Zaurus SL-5x00",
 	.flags =	FLAG_FRAMING_Z,
 	.check_connect = always_connected,
+	.bind =		cdc_bind,
+	.unbind =	cdc_unbind,
 	.tx_fixup = 	zaurus_tx_fixup,
-
-	.in = 2, .out = 1,
-	.epsize = 64,
 };
 static const struct driver_info	zaurus_sla300_info = {
 	.description =	"Sharp Zaurus SL-A300",
@@ -1361,7 +1677,6 @@
 	.tx_fixup = 	zaurus_tx_fixup,
 
 	.in = 1, .out = 2,
-	.epsize = 64,
 };
 static const struct driver_info	zaurus_slb500_info = {
 	/* Japanese B500 ~= US SL-5600 */
@@ -1371,7 +1686,6 @@
 	.tx_fixup = 	zaurus_tx_fixup,
 
 	.in = 1, .out = 2,
-	.epsize = 64,
 };
 
 // SL-5600 and C-700 are PXA based; should resemble A300
@@ -1403,7 +1717,7 @@
 		return -EINVAL;
 #endif
 	// 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;
 	net->mtu = new_mtu;
 	return 0;
@@ -1444,10 +1758,9 @@
 {
 	set_bit (work, &dev->flags);
 	if (!schedule_work (&dev->kevent))
-		err ("%s: kevent %d may have been dropped",
-			dev->net.name, work);
+		deverr (dev, "kevent %d may have been dropped", work);
 	else
-		dbg ("%s: kevent %d scheduled", dev->net.name, work);
+		devdbg (dev, "kevent %d scheduled", work);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1480,7 +1793,7 @@
 		size = (sizeof (struct ethhdr) + dev->net.mtu);
 
 	if ((skb = alloc_skb (size, flags)) == 0) {
-		dbg ("no rx skb");
+		devdbg (dev, "no rx skb");
 		defer_kevent (dev, EVENT_RX_MEMORY);
 		usb_free_urb (urb);
 		return;
@@ -1492,14 +1805,14 @@
 	entry->state = rx_start;
 	entry->length = 0;
 
-	usb_fill_bulk_urb (urb, dev->udev,
-		usb_rcvbulkpipe (dev->udev, dev->driver_info->in),
+	usb_fill_bulk_urb (urb, dev->udev, dev->in,
 		skb->data, size, rx_complete, skb);
 	urb->transfer_flags |= URB_ASYNC_UNLINK;
 
 	spin_lock_irqsave (&dev->rxq.lock, lockflags);
 
 	if (netif_running (&dev->net)
+			&& netif_device_present (&dev->net)
 			&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
 		switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ 
 		case -EPIPE:
@@ -1508,15 +1821,19 @@
 		case -ENOMEM:
 			defer_kevent (dev, EVENT_RX_MEMORY);
 			break;
+		case -ENODEV:
+			devdbg (dev, "device gone");
+			netif_device_detach (&dev->net);
+			break;
 		default:
-			dbg ("%s rx submit, %d", dev->net.name, retval);
+			devdbg (dev, "rx submit, %d", retval);
 			tasklet_schedule (&dev->bh);
 			break;
 		case 0:
 			__skb_queue_tail (&dev->rxq, skb);
 		}
 	} else {
-		dbg ("rx: stopped");
+		devdbg (dev, "rx: stopped");
 		retval = -ENOLINK;
 	}
 	spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
@@ -1553,7 +1870,7 @@
 		if (status != NET_RX_SUCCESS)
 			devdbg (dev, "netif_rx status %d", status);
 	} else {
-		dbg ("drop");
+		devdbg (dev, "drop");
 error:
 		dev->stats.rx_errors++;
 		skb_queue_tail (&dev->done, skb);
@@ -1580,7 +1897,7 @@
 			entry->state = rx_cleanup;
 			dev->stats.rx_errors++;
 			dev->stats.rx_length_errors++;
-			dbg ("rx length %d", skb->len);
+			devdbg (dev, "rx length %d", skb->len);
 		}
 		break;
 
@@ -1589,15 +1906,31 @@
 	    // we avoid the highspeed version of the ETIMEOUT/EILSEQ
 	    // storm, recovering as needed.
 	    case -EPIPE:
+		dev->stats.rx_errors++;
 		defer_kevent (dev, EVENT_RX_HALT);
 		// FALLTHROUGH
 
 	    // software-driven interface shutdown
-	    case -ECONNRESET:		// according to API spec
-	    case -ECONNABORTED:		// some (now fixed?) UHCI bugs
-		dbg ("%s rx shutdown, code %d", dev->net.name, urb_status);
+	    case -ECONNRESET:		// async unlink
+	    case -ESHUTDOWN:		// hardware gone
+#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;
-		// do urb frees only in the tasklet (UHCI has oopsed ...)
 		entry->urb = urb;
 		urb = 0;
 		break;
@@ -1608,12 +1941,9 @@
 		// FALLTHROUGH
 	    
 	    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;
 		dev->stats.rx_errors++;
-		dbg ("%s rx: status %d", dev->net.name, urb_status);
+		devdbg (dev, "rx status %d", urb_status);
 		break;
 	}
 
@@ -1628,7 +1958,7 @@
 		usb_free_urb (urb);
 	}
 #ifdef	VERBOSE
-	dbg ("no read resubmitted");
+	devdbg (dev, "no read resubmitted");
 #endif /* VERBOSE */
 }
 
@@ -1636,7 +1966,7 @@
 
 // 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;
 	struct sk_buff		*skb, *skbnext;
@@ -1656,7 +1986,7 @@
 		// these (async) unlinks complete immediately
 		retval = usb_unlink_urb (urb);
 		if (retval != -EINPROGRESS && retval != 0)
-			dbg ("unlink urb err, %d", retval);
+			devdbg (dev, "unlink urb err, %d", retval);
 		else
 			count++;
 	}
@@ -1688,7 +2018,7 @@
 	// ensure there are no more active urbs
 	add_wait_queue (&unlink_wakeup, &wait);
 	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.
 	while (skb_queue_len (&dev->rxq)
@@ -1696,11 +2026,16 @@
 			&& skb_queue_len (&dev->done)) {
 		set_current_state (TASK_UNINTERRUPTIBLE);
 		schedule_timeout (UNLINK_TIMEOUT_JIFFIES);
-		dbg ("waited for %d urb completions", temp);
+		devdbg (dev, "waited for %d urb completions", temp);
 	}
 	dev->wait = 0;
 	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);
 	return 0;
 }
@@ -1738,7 +2073,7 @@
 	if (dev->msg_level >= 2)
 		devinfo (dev, "open: enable queueing "
 				"(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)
 				? "NetChip"
@@ -1755,7 +2090,8 @@
 
 /*-------------------------------------------------------------------------*/
 
-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;
 	u32		cmd;
@@ -1829,9 +2165,8 @@
 
 /* work that cannot be done in interrupt context uses keventd.
  *
- * NOTE:  "uhci" and "usb-uhci" may have trouble with this since they don't
- * queue control transfers to individual devices, and other threads could
- * trigger control requests concurrently.  hope that's rare.
+ * NOTE:  with 2.5 we could do more of this using completion callbacks,
+ * especially now that control transfers can be queued.
  */
 static void
 kevent (void *data)
@@ -1841,24 +2176,22 @@
 
 	/* usb_clear_halt() needs a thread context */
 	if (test_bit (EVENT_TX_HALT, &dev->flags)) {
-		unlink_urbs (&dev->txq);
-		status = usb_clear_halt (dev->udev,
-			usb_sndbulkpipe (dev->udev, dev->driver_info->out));
+		unlink_urbs (dev, &dev->txq);
+		status = usb_clear_halt (dev->udev, dev->out);
 		if (status < 0)
-			err ("%s: can't clear tx halt, status %d",
-				dev->net.name, status);
+			deverr (dev, "can't clear tx halt, status %d",
+				status);
 		else {
 			clear_bit (EVENT_TX_HALT, &dev->flags);
 			netif_wake_queue (&dev->net);
 		}
 	}
 	if (test_bit (EVENT_RX_HALT, &dev->flags)) {
-		unlink_urbs (&dev->rxq);
-		status = usb_clear_halt (dev->udev,
-			usb_rcvbulkpipe (dev->udev, dev->driver_info->in));
+		unlink_urbs (dev, &dev->rxq);
+		status = usb_clear_halt (dev->udev, dev->in);
 		if (status < 0)
-			err ("%s: can't clear rx halt, status %d",
-				dev->net.name, status);
+			deverr (dev, "can't clear rx halt, status %d",
+				status);
 		else {
 			clear_bit (EVENT_RX_HALT, &dev->flags);
 			tasklet_schedule (&dev->bh);
@@ -1881,8 +2214,8 @@
 	}
 
 	if (dev->flags)
-		dbg ("%s: kevent done, flags = 0x%lx",
-			dev->net.name, dev->flags);
+		devdbg (dev, "kevent done, flags = 0x%lx",
+			dev->flags);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1893,8 +2226,35 @@
 	struct skb_data		*entry = (struct skb_data *) skb->cb;
 	struct usbnet		*dev = entry->dev;
 
-	if (urb->status == -EPIPE)
-		defer_kevent (dev, EVENT_TX_HALT);
+	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);
+			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;
 	entry->state = tx_done;
 	defer_bh (dev, skb);
@@ -1906,7 +2266,7 @@
 {
 	struct usbnet		*dev = (struct usbnet *) net->priv;
 
-	unlink_urbs (&dev->txq);
+	unlink_urbs (dev, &dev->txq);
 	tasklet_schedule (&dev->bh);
 
 	// FIXME: device recovery -- reset?
@@ -1933,14 +2293,14 @@
 	if (info->tx_fixup) {
 		skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
 		if (!skb) {
-			dbg ("can't tx_fixup skb");
+			devdbg (dev, "can't tx_fixup skb");
 			goto drop;
 		}
 	}
 	length = skb->len;
 
 	if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
-		dbg ("no urb");
+		devdbg (dev, "no urb");
 		goto drop;
 	}
 
@@ -1965,20 +2325,24 @@
 	} else
 #endif	/* CONFIG_USB_NET1080 */
 
-	/* don't assume the hardware handles USB_ZERO_PACKET */
-	if ((length % EP_SIZE (dev)) == 0)
-		skb->len++;
-
-	usb_fill_bulk_urb (urb, dev->udev,
-			usb_sndbulkpipe (dev->udev, info->out),
+	usb_fill_bulk_urb (urb, dev->udev, dev->out,
 			skb->data, skb->len, tx_complete, skb);
 	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);
 
 #ifdef	CONFIG_USB_NET1080
 	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);
 #if 0
 		devdbg (dev, "frame >tx h %d p %d id %d",
@@ -1994,12 +2358,12 @@
 		defer_kevent (dev, EVENT_TX_HALT);
 		break;
 	default:
-		dbg ("%s tx: submit urb err %d", net->name, retval);
+		devdbg (dev, "tx: submit urb err %d", retval);
 		break;
 	case 0:
 		net->trans_start = jiffies;
 		__skb_queue_tail (&dev->txq, skb);
-		if (dev->txq.qlen >= TX_QLEN)
+		if (dev->txq.qlen >= TX_QLEN (dev))
 			netif_stop_queue (net);
 	}
 	spin_unlock_irqrestore (&dev->txq.lock, flags);
@@ -2024,7 +2388,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-// tasklet ... work that avoided running in_irq()
+// tasklet (work deferred from completions, in_irq) or timer
 
 static void usbnet_bh (unsigned long param)
 {
@@ -2040,23 +2404,12 @@
 			rx_process (dev, skb);
 			continue;
 		    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:
 			usb_free_urb (entry->urb);
 			dev_kfree_skb (skb);
 			continue;
 		    default:
-			dbg ("%s: bogus skb state %d",
-				dev->net.name, entry->state);
+			devdbg (dev, "bogus skb state %d", entry->state);
 		}
 	}
 
@@ -2068,23 +2421,28 @@
 
 	// or are we maybe short a few urbs?
 	} else if (netif_running (&dev->net)
+			&& netif_device_present (&dev->net)
+			&& !timer_pending (&dev->delay)
 			&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
 		int	temp = dev->rxq.qlen;
+		int	qlen = RX_QLEN (dev);
 
-		if (temp < RX_QLEN) {
+		if (temp < qlen) {
 			struct urb	*urb;
 			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)
 					rx_submit (dev, urb, GFP_ATOMIC);
 			}
 			if (temp != dev->rxq.qlen)
 				devdbg (dev, "rxqlen %d --> %d",
 						temp, dev->rxq.qlen);
-			if (dev->rxq.qlen < RX_QLEN)
+			if (dev->rxq.qlen < qlen)
 				tasklet_schedule (&dev->bh);
 		}
-		if (dev->txq.qlen < TX_QLEN)
+		if (dev->txq.qlen < TX_QLEN (dev))
 			netif_wake_queue (&dev->net);
 	}
 }
@@ -2117,13 +2475,8 @@
 	
 	unregister_netdev (&dev->net);
 
-	mutex_lock (&usbnet_mutex);
-	mutex_lock (&dev->mutex);
-	list_del (&dev->dev_list);
-	mutex_unlock (&usbnet_mutex);
-
-	// assuming we used keventd, it must quiesce too
-	flush_scheduled_work ();
+	if (dev->driver_info->unbind)
+		dev->driver_info->unbind (dev, intf);
 
 	kfree (dev);
 	usb_put_dev (xdev);
@@ -2142,20 +2495,12 @@
 	struct usb_host_interface	*interface;
 	struct driver_info		*info;
 	struct usb_device		*xdev;
+	int				status;
 
 	info = (struct driver_info *) prod->driver_info;
-
 	xdev = interface_to_usbdev (udev);
 	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
 	if (!(dev = kmalloc (sizeof *dev, GFP_KERNEL))) {
 		dbg ("can't kmalloc dev");
@@ -2168,13 +2513,15 @@
 	dev->udev = xdev;
 	dev->driver_info = info;
 	dev->msg_level = msg_level;
-	INIT_LIST_HEAD (&dev->dev_list);
 	skb_queue_head_init (&dev->rxq);
 	skb_queue_head_init (&dev->txq);
 	skb_queue_head_init (&dev->done);
 	dev->bh.func = usbnet_bh;
 	dev->bh.data = (unsigned long) 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
 	net = &dev->net;
@@ -2200,31 +2547,41 @@
 	net->tx_timeout = usbnet_tx_timeout;
 	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);
-	devinfo (dev, "register usbnet usb-%s-%s, %s",
+	devinfo (dev, "register usbnet at usb-%s-%s, %s",
 		xdev->bus->bus_name, xdev->devpath,
 		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.
-	usb_set_intfdata(udev, dev);
-	mutex_lock (&usbnet_mutex);
-	list_add (&dev->dev_list, &usbnet_list);
+	usb_set_intfdata (udev, dev);
 	mutex_unlock (&dev->mutex);
 
 	// start as if the link is up
 	netif_device_attach (&dev->net);
 
-	mutex_unlock (&usbnet_mutex);
 	return 0;
 }
 
@@ -2298,28 +2655,19 @@
 },
 #endif
 
-#ifdef	CONFIG_USB_PXA
-/*
- * 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
+#ifdef	CONFIG_USB_ARMLINUX
 /*
  * SA-1100 using standard ARM Linux kernels, or compatible.
  * Often used when talking to Linux PDAs (iPaq, Yopy, etc).
  * 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?
-	// Compaq "Itsy" vendor/product id, version "0.0"
-	USB_DEVICE_VER (0x049F, 0x505A, 0, 0),
+	// Compaq "Itsy" vendor/product id
+	USB_DEVICE (0x049F, 0x505A),
 	.driver_info =	(unsigned long) &linuxdev_info,
 }, {
 	USB_DEVICE (0x0E7E, 0x1001),	// G.Mate "Yopy"
@@ -2337,9 +2685,10 @@
 			  | USB_DEVICE_ID_MATCH_DEVICE, 
 	.idVendor		= 0x04DD,
 	.idProduct		= 0x8004,
-	.bInterfaceClass	= 0x0a,
-	.bInterfaceSubClass	= 0x00,
-	.bInterfaceProtocol	= 0x00,
+	/* match the master interface */
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= 6 /* Ethernet model */,
+	.bInterfaceProtocol	= 0,
 	.driver_info =  (unsigned long) &zaurus_sl5x00_info,
 }, {
 	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
@@ -2362,6 +2711,24 @@
 },
 #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
+
 	{ },		// END
 };
 MODULE_DEVICE_TABLE (usb, products);
diff -urN linux-2.5.67-bk3/drivers/usb/serial/io_edgeport.c linux-2.5.67-bk4/drivers/usb/serial/io_edgeport.c
--- linux-2.5.67-bk3/drivers/usb/serial/io_edgeport.c	2003-04-07 10:30:39.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/serial/io_edgeport.c	2003-04-12 04:33:22.000000000 -0700
@@ -25,6 +25,10 @@
  *
  * Version history:
  * 
+ * 2003_04_03 al borchers
+ *  - fixed a bug (that shows up with dosemu) where the tty struct is
+ *    used in a callback after it has been freed
+ *
  * 2.3 2002_03_08 greg kroah-hartman
  *	- fixed bug when multiple devices were attached at the same time.
  *
@@ -918,7 +922,7 @@
 
 	tty = edge_port->port->tty;
 
-	if (tty) {
+	if (tty && edge_port->open) {
 		/* let the tty driver wakeup if it has a special write_wakeup function */
 		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
 			(tty->ldisc.write_wakeup)(tty);
@@ -975,7 +979,7 @@
 	tty = edge_port->port->tty;
 
 	/* tell the tty driver that something has changed */
-	if (tty)
+	if (tty && edge_port->open)
 		wake_up_interruptible(&tty->write_wait);
 
 	/* we have completed the command */
diff -urN linux-2.5.67-bk3/drivers/usb/serial/ipaq.c linux-2.5.67-bk4/drivers/usb/serial/ipaq.c
--- linux-2.5.67-bk3/drivers/usb/serial/ipaq.c	2003-04-07 10:32:26.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/serial/ipaq.c	2003-04-12 04:33:22.000000000 -0700
@@ -131,6 +131,7 @@
 	{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_E740_ID) },
 	{ USB_DEVICE(HTC_VENDOR_ID, HTC_PRODUCT_ID) },
 	{ USB_DEVICE(NEC_VENDOR_ID, NEC_PRODUCT_ID) },
+	{ USB_DEVICE(ASUS_VENDOR_ID, ASUS_A600_PRODUCT_ID) },
 	{ }					/* Terminating entry */
 };
 
diff -urN linux-2.5.67-bk3/drivers/usb/serial/ipaq.h linux-2.5.67-bk4/drivers/usb/serial/ipaq.h
--- linux-2.5.67-bk3/drivers/usb/serial/ipaq.h	2003-04-07 10:30:34.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/serial/ipaq.h	2003-04-12 04:33:22.000000000 -0700
@@ -77,6 +77,9 @@
 #define NEC_VENDOR_ID		0x0409
 #define NEC_PRODUCT_ID		0x00d5
 
+#define ASUS_VENDOR_ID		0x0b05
+#define ASUS_A600_PRODUCT_ID	0x4201
+
 /*
  * Since we can't queue our bulk write urbs (don't know why - it just
  * doesn't work), we can send down only one write urb at a time. The simplistic
diff -urN linux-2.5.67-bk3/drivers/usb/serial/keyspan.h linux-2.5.67-bk4/drivers/usb/serial/keyspan.h
--- linux-2.5.67-bk3/drivers/usb/serial/keyspan.h	2003-04-07 10:31:57.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/serial/keyspan.h	2003-04-12 04:33:22.000000000 -0700
@@ -411,19 +411,19 @@
 };
 
 static const struct keyspan_device_details usa49wlc_device_details = {
-	product_id:		keyspan_usa49wlc_product_id,
-	msg_format:		msg_usa49,
-	num_ports:		4,
-	indat_endp_flip:	0,
-	outdat_endp_flip:	0,
-	indat_endpoints:	{0x81, 0x82, 0x83, 0x84},
-	outdat_endpoints:	{0x01, 0x02, 0x03, 0x04},
-	inack_endpoints:	{-1, -1, -1, -1},
-	outcont_endpoints:	{-1, -1, -1, -1},
-	instat_endpoint:	0x87,
-	glocont_endpoint:	0x07,
-	calculate_baud_rate:	keyspan_usa19w_calc_baud,
-	baudclk:		KEYSPAN_USA19W_BAUDCLK,
+	.product_id		= keyspan_usa49wlc_product_id,
+	.msg_format		= msg_usa49,
+	.num_ports		= 4,
+	.indat_endp_flip	= 0,
+	.outdat_endp_flip	= 0,
+	.indat_endpoints	= {0x81, 0x82, 0x83, 0x84},
+	.outdat_endpoints	= {0x01, 0x02, 0x03, 0x04},
+	.inack_endpoints	= {-1, -1, -1, -1},
+	.outcont_endpoints	= {-1, -1, -1, -1},
+	.instat_endpoint	= 0x87,
+	.glocont_endpoint	= 0x07,
+	.calculate_baud_rate	= keyspan_usa19w_calc_baud,
+	.baudclk		= KEYSPAN_USA19W_BAUDCLK,
 };
 
 static const struct keyspan_device_details *keyspan_devices[] = {
diff -urN linux-2.5.67-bk3/drivers/usb/serial/usb-serial.c linux-2.5.67-bk4/drivers/usb/serial/usb-serial.c
--- linux-2.5.67-bk3/drivers/usb/serial/usb-serial.c	2003-04-07 10:31:06.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/serial/usb-serial.c	2003-04-12 04:33:22.000000000 -0700
@@ -531,7 +531,7 @@
 	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	/* if disconnect beat us to the punch here, there's nothing to do */
-	if (tty->driver_data) {
+	if (tty && tty->driver_data) {
 		__serial_close(port, filp);
 	}
 }
@@ -830,6 +830,7 @@
 			while (port->open_count > 0) {
 				__serial_close(port, NULL);
 			}
+			port->tty = NULL;
 		}
 	}
 
diff -urN linux-2.5.67-bk3/drivers/usb/storage/scsiglue.c linux-2.5.67-bk4/drivers/usb/storage/scsiglue.c
--- linux-2.5.67-bk3/drivers/usb/storage/scsiglue.c	2003-04-07 10:31:20.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/storage/scsiglue.c	2003-04-12 04:33:22.000000000 -0700
@@ -141,13 +141,19 @@
 static int usb_storage_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
 {
 	struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
+	int state = atomic_read(&us->sm_state);
 
 	US_DEBUGP("queuecommand() called\n");
 	srb->host_scribble = (unsigned char *)us;
 
 	/* enqueue the command */
-	BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE);
-	BUG_ON(us->srb != NULL);
+	if (state != US_STATE_IDLE || 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;
 	us->srb = srb;
 
@@ -175,8 +181,7 @@
 		return FAILED;
 	}
 
-	usb_stor_abort_transport(us);
-	return SUCCESS;
+	return usb_stor_abort_transport(us);
 }
 
 /* This invokes the transport reset mechanism to reset the state of the
@@ -185,10 +190,15 @@
 static int usb_storage_device_reset( Scsi_Cmnd *srb )
 {
 	struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
+	int state = atomic_read(&us->sm_state);
 	int result;
 
 	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 */
 	atomic_set(&us->sm_state, US_STATE_RESETTING);
@@ -260,6 +270,7 @@
 	struct us_data *us;
 	char *pos = buffer;
 	struct Scsi_Host *hostptr;
+	unsigned long f;
 
 	/* if someone is sending us data, just throw it away */
 	if (inout)
@@ -274,6 +285,7 @@
 
 	/* if we couldn't find it, we return an error */
 	if (!us) {
+		scsi_host_put(hostptr);
 		return -ESRCH;
 	}
 
@@ -289,6 +301,24 @@
 	SPRINTF("     Protocol: %s\n", us->protocol_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 */
 	scsi_host_put(hostptr);
 
diff -urN linux-2.5.67-bk3/drivers/usb/storage/transport.c linux-2.5.67-bk4/drivers/usb/storage/transport.c
--- linux-2.5.67-bk3/drivers/usb/storage/transport.c	2003-04-07 10:31:04.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/storage/transport.c	2003-04-12 04:33:22.000000000 -0700
@@ -297,10 +297,11 @@
 
 	/* stalled */
 	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)) {
 			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 */
@@ -691,7 +692,7 @@
 
 /* Abort the currently running scsi command or device reset.
  * 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;
 	int state = atomic_read(&us->sm_state);
@@ -701,7 +702,11 @@
 	/* Normally the current state is RUNNING.  If the control thread
 	 * hasn't even started processing this command, the state will be
 	 * 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 */
 	atomic_set(&us->sm_state, US_STATE_ABORTING);
@@ -730,6 +735,7 @@
 
 	/* Reacquire the lock: note that us->srb is now NULL */
 	scsi_lock(host);
+	return SUCCESS;
 }
 
 /*
@@ -750,8 +756,14 @@
 
 	/* check the return code for the command */
 	US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result);
+
+	/* 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 */
 	if (result != USB_STOR_XFER_GOOD) {
-		/* Uh oh... serious problem here */
 		return USB_STOR_TRANSPORT_ERROR;
 	}
 
@@ -834,8 +846,14 @@
 
 	/* check the return code for the command */
 	US_DEBUGP("Call to usb_stor_ctrl_transfer() returned %d\n", result);
+
+	/* 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 */
 	if (result != USB_STOR_XFER_GOOD) {
-		/* Uh oh... serious problem here */
 		return USB_STOR_TRANSPORT_ERROR;
 	}
 
diff -urN linux-2.5.67-bk3/drivers/usb/storage/transport.h linux-2.5.67-bk4/drivers/usb/storage/transport.h
--- linux-2.5.67-bk3/drivers/usb/storage/transport.h	2003-04-07 10:31:05.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/storage/transport.h	2003-04-12 04:33:22.000000000 -0700
@@ -154,7 +154,7 @@
 extern int usb_stor_Bulk_reset(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,
 		unsigned int pipe, unsigned int len, unsigned int *act_len);
diff -urN linux-2.5.67-bk3/drivers/usb/storage/unusual_devs.h linux-2.5.67-bk4/drivers/usb/storage/unusual_devs.h
--- linux-2.5.67-bk3/drivers/usb/storage/unusual_devs.h	2003-04-07 10:32:18.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/storage/unusual_devs.h	2003-04-12 04:33:22.000000000 -0700
@@ -286,6 +286,19 @@
 		"USB Hard Disk",
 		US_SC_RBC, US_PR_CB, NULL, 0 ), 
 
+/* This Pentax still camera is not conformant
+ * to the USB storage specification: -
+ * - It does not like the INQUIRY command. So we must handle this command
+ *   of the SCSI layer ourselves.
+ * Tested on Rev. 10.00 (0x1000)
+ * Submitted by James Courtier-Dutton <James@superbug.demon.co.uk>
+ */
+UNUSUAL_DEV( 0x0a17, 0x0004, 0x1000, 0x1000,
+                "ASAHI PENTAX",
+                "PENTAX OPTIO 430",
+                US_SC_8070, US_PR_CBI, NULL,
+                US_FL_FIX_INQUIRY ),
+
 #ifdef CONFIG_USB_STORAGE_ISD200
 UNUSUAL_DEV(  0x05ab, 0x0031, 0x0100, 0x0110,
 		"In-System",
diff -urN linux-2.5.67-bk3/drivers/usb/storage/usb.c linux-2.5.67-bk4/drivers/usb/storage/usb.c
--- linux-2.5.67-bk3/drivers/usb/storage/usb.c	2003-04-07 10:30:58.000000000 -0700
+++ linux-2.5.67-bk4/drivers/usb/storage/usb.c	2003-04-12 04:33:22.000000000 -0700
@@ -430,39 +430,39 @@
 }	
 
 /* 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
  * structure is current.
  * 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 */
-	ss->send_ctrl_pipe = usb_sndctrlpipe(ss->pusb_dev, 0);
-	ss->recv_ctrl_pipe = usb_rcvctrlpipe(ss->pusb_dev, 0);
-	ss->send_bulk_pipe = usb_sndbulkpipe(ss->pusb_dev, ss->ep_out);
-	ss->recv_bulk_pipe = usb_rcvbulkpipe(ss->pusb_dev, ss->ep_in);
-	ss->recv_intr_pipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+	us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);
+	us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
+	us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+	us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+	us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev, us->ep_int);
 
 	/* allocate the usb_ctrlrequest for control packets */
 	US_DEBUGP("Allocating usb_ctrlrequest\n");
-	ss->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
-	if (!ss->dr) {
+	us->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+	if (!us->dr) {
 		US_DEBUGP("allocation failed\n");
 		return 1;
 	}
 
 	/* allocate the URB we're going to use */
 	US_DEBUGP("Allocating URB\n");
-	ss->current_urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!ss->current_urb) {
+	us->current_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!us->current_urb) {
 		US_DEBUGP("allocation failed\n");
 		return 2;
 	}
 
 	US_DEBUGP("Allocating scatter-gather request block\n");
-	ss->current_sg = kmalloc(sizeof(*ss->current_sg), GFP_KERNEL);
-	if (!ss->current_sg) {
+	us->current_sg = kmalloc(sizeof(*us->current_sg), GFP_KERNEL);
+	if (!us->current_sg) {
 		US_DEBUGP("allocation failed\n");
 		return 5;
 	}
@@ -471,32 +471,32 @@
 }
 
 /* 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 */
-	if (ss->current_sg) {
-		kfree(ss->current_sg);
-		ss->current_sg = NULL;
+	if (us->current_sg) {
+		kfree(us->current_sg);
+		us->current_sg = NULL;
 	}
 
 	/* free up the main URB for this device */
-	if (ss->current_urb) {
+	if (us->current_urb) {
 		US_DEBUGP("-- releasing main URB\n");
-		usb_free_urb(ss->current_urb);
-		ss->current_urb = NULL;
+		usb_free_urb(us->current_urb);
+		us->current_urb = NULL;
 	}
 
 	/* free the usb_ctrlrequest buffer */
-	if (ss->dr) {
-		kfree(ss->dr);
-		ss->dr = NULL;
+	if (us->dr) {
+		kfree(us->dr);
+		us->dr = NULL;
 	}
 
 	/* mark the device as gone */
-	usb_put_dev(ss->pusb_dev);
-	ss->pusb_dev = NULL;
+	usb_put_dev(us->pusb_dev);
+	us->pusb_dev = NULL;
 }
 
 /* Probe to see if a new device is actually a SCSI device */
@@ -512,7 +512,7 @@
 	char serial[USB_STOR_STRING_LEN];	     /* serial number */
 	unsigned int flags;
 	struct us_unusual_dev *unusual_dev;
-	struct us_data *ss = NULL;
+	struct us_data *us = NULL;
 	int result;
 
 	/* these are temporary copies -- we test on these, then put them
@@ -633,212 +633,212 @@
 			   serial, sizeof(serial));
 
 	/* 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) {
 		printk(KERN_WARNING USB_STORAGE "Out of memory\n");
 		usb_put_dev(dev);
 		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 */
-	init_completion(&(ss->notify));
-	init_MUTEX_LOCKED(&(ss->dev_semaphore));
+	init_completion(&(us->notify));
+	init_MUTEX_LOCKED(&(us->dev_semaphore));
 
 	/* copy over the subclass and protocol data */
-	ss->subclass = subclass;
-	ss->protocol = protocol;
-	ss->flags = flags;
-	ss->unusual_dev = unusual_dev;
+	us->subclass = subclass;
+	us->protocol = protocol;
+	us->flags = flags;
+	us->unusual_dev = unusual_dev;
 
 	/* copy over the endpoint data */
-	ss->ep_in = ep_in->bEndpointAddress & 
+	us->ep_in = ep_in->bEndpointAddress & 
 		USB_ENDPOINT_NUMBER_MASK;
-	ss->ep_out = ep_out->bEndpointAddress & 
+	us->ep_out = ep_out->bEndpointAddress & 
 		USB_ENDPOINT_NUMBER_MASK;
 	if (ep_int) {
-		ss->ep_int = ep_int->bEndpointAddress & 
+		us->ep_int = ep_int->bEndpointAddress & 
 			USB_ENDPOINT_NUMBER_MASK;
-		ss->ep_bInterval = ep_int->bInterval;
+		us->ep_bInterval = ep_int->bInterval;
 	}
 	else
-		ss->ep_int = ss->ep_bInterval = 0;
+		us->ep_int = us->ep_bInterval = 0;
 
 	/* establish the connection to the new device */
-	ss->ifnum = ifnum;
-	ss->pusb_dev = dev;
+	us->ifnum = ifnum;
+	us->pusb_dev = dev;
 
 	/* copy over the identifiying strings */
-	strncpy(ss->vendor, mf, USB_STOR_STRING_LEN);
-	strncpy(ss->product, prod, USB_STOR_STRING_LEN);
-	strncpy(ss->serial, serial, USB_STOR_STRING_LEN);
-	if (strlen(ss->vendor) == 0) {
+	strncpy(us->vendor, mf, USB_STOR_STRING_LEN);
+	strncpy(us->product, prod, USB_STOR_STRING_LEN);
+	strncpy(us->serial, serial, USB_STOR_STRING_LEN);
+	if (strlen(us->vendor) == 0) {
 		if (unusual_dev->vendorName)
-			strncpy(ss->vendor, unusual_dev->vendorName,
+			strncpy(us->vendor, unusual_dev->vendorName,
 				USB_STOR_STRING_LEN);
 		else
-			strncpy(ss->vendor, "Unknown",
+			strncpy(us->vendor, "Unknown",
 				USB_STOR_STRING_LEN);
 	}
-	if (strlen(ss->product) == 0) {
+	if (strlen(us->product) == 0) {
 		if (unusual_dev->productName)
-			strncpy(ss->product, unusual_dev->productName,
+			strncpy(us->product, unusual_dev->productName,
 				USB_STOR_STRING_LEN);
 		else
-			strncpy(ss->product, "Unknown",
+			strncpy(us->product, "Unknown",
 				USB_STOR_STRING_LEN);
 	}
-	if (strlen(ss->serial) == 0)
-		strncpy(ss->serial, "None", USB_STOR_STRING_LEN);
+	if (strlen(us->serial) == 0)
+		strncpy(us->serial, "None", USB_STOR_STRING_LEN);
 
 	/* 
 	 * Set the handler pointers based on the protocol
 	 * Again, this data is persistent across reattachments
 	 */
-	switch (ss->protocol) {
+	switch (us->protocol) {
 	case US_PR_CB:
-		ss->transport_name = "Control/Bulk";
-		ss->transport = usb_stor_CB_transport;
-		ss->transport_reset = usb_stor_CB_reset;
-		ss->max_lun = 7;
+		us->transport_name = "Control/Bulk";
+		us->transport = usb_stor_CB_transport;
+		us->transport_reset = usb_stor_CB_reset;
+		us->max_lun = 7;
 		break;
 
 	case US_PR_CBI:
-		ss->transport_name = "Control/Bulk/Interrupt";
-		ss->transport = usb_stor_CBI_transport;
-		ss->transport_reset = usb_stor_CB_reset;
-		ss->max_lun = 7;
+		us->transport_name = "Control/Bulk/Interrupt";
+		us->transport = usb_stor_CBI_transport;
+		us->transport_reset = usb_stor_CB_reset;
+		us->max_lun = 7;
 		break;
 
 	case US_PR_BULK:
-		ss->transport_name = "Bulk";
-		ss->transport = usb_stor_Bulk_transport;
-		ss->transport_reset = usb_stor_Bulk_reset;
-		ss->max_lun = usb_stor_Bulk_max_lun(ss);
+		us->transport_name = "Bulk";
+		us->transport = usb_stor_Bulk_transport;
+		us->transport_reset = usb_stor_Bulk_reset;
+		us->max_lun = usb_stor_Bulk_max_lun(us);
 		break;
 
 #ifdef CONFIG_USB_STORAGE_HP8200e
 	case US_PR_SCM_ATAPI:
-		ss->transport_name = "SCM/ATAPI";
-		ss->transport = hp8200e_transport;
-		ss->transport_reset = usb_stor_CB_reset;
-		ss->max_lun = 1;
+		us->transport_name = "SCM/ATAPI";
+		us->transport = hp8200e_transport;
+		us->transport_reset = usb_stor_CB_reset;
+		us->max_lun = 1;
 		break;
 #endif
 
 #ifdef CONFIG_USB_STORAGE_SDDR09
 	case US_PR_EUSB_SDDR09:
-		ss->transport_name = "EUSB/SDDR09";
-		ss->transport = sddr09_transport;
-		ss->transport_reset = usb_stor_CB_reset;
-		ss->max_lun = 0;
+		us->transport_name = "EUSB/SDDR09";
+		us->transport = sddr09_transport;
+		us->transport_reset = usb_stor_CB_reset;
+		us->max_lun = 0;
 		break;
 #endif
 
 #ifdef CONFIG_USB_STORAGE_SDDR55
 	case US_PR_SDDR55:
-		ss->transport_name = "SDDR55";
-		ss->transport = sddr55_transport;
-		ss->transport_reset = sddr55_reset;
-		ss->max_lun = 0;
+		us->transport_name = "SDDR55";
+		us->transport = sddr55_transport;
+		us->transport_reset = sddr55_reset;
+		us->max_lun = 0;
 		break;
 #endif
 
 #ifdef CONFIG_USB_STORAGE_DPCM
 	case US_PR_DPCM_USB:
-		ss->transport_name = "Control/Bulk-EUSB/SDDR09";
-		ss->transport = dpcm_transport;
-		ss->transport_reset = usb_stor_CB_reset;
-		ss->max_lun = 1;
+		us->transport_name = "Control/Bulk-EUSB/SDDR09";
+		us->transport = dpcm_transport;
+		us->transport_reset = usb_stor_CB_reset;
+		us->max_lun = 1;
 		break;
 #endif
 
 #ifdef CONFIG_USB_STORAGE_FREECOM
 	case US_PR_FREECOM:
-		ss->transport_name = "Freecom";
-		ss->transport = freecom_transport;
-		ss->transport_reset = usb_stor_freecom_reset;
-		ss->max_lun = 0;
+		us->transport_name = "Freecom";
+		us->transport = freecom_transport;
+		us->transport_reset = usb_stor_freecom_reset;
+		us->max_lun = 0;
 		break;
 #endif
 
 #ifdef CONFIG_USB_STORAGE_DATAFAB
 	case US_PR_DATAFAB:
-		ss->transport_name  = "Datafab Bulk-Only";
-		ss->transport = datafab_transport;
-		ss->transport_reset = usb_stor_Bulk_reset;
-		ss->max_lun = 1;
+		us->transport_name  = "Datafab Bulk-Only";
+		us->transport = datafab_transport;
+		us->transport_reset = usb_stor_Bulk_reset;
+		us->max_lun = 1;
 		break;
 #endif
 
 #ifdef CONFIG_USB_STORAGE_JUMPSHOT
 	case US_PR_JUMPSHOT:
-		ss->transport_name  = "Lexar Jumpshot Control/Bulk";
-		ss->transport = jumpshot_transport;
-		ss->transport_reset = usb_stor_Bulk_reset;
-		ss->max_lun = 1;
+		us->transport_name  = "Lexar Jumpshot Control/Bulk";
+		us->transport = jumpshot_transport;
+		us->transport_reset = usb_stor_Bulk_reset;
+		us->max_lun = 1;
 		break;
 #endif
 
 	default:
-		/* ss->transport_name = "Unknown"; */
+		/* us->transport_name = "Unknown"; */
 		goto BadDevice;
 	}
-	US_DEBUGP("Transport: %s\n", ss->transport_name);
+	US_DEBUGP("Transport: %s\n", us->transport_name);
 
 	/* fix for single-lun devices */
-	if (ss->flags & US_FL_SINGLE_LUN)
-		ss->max_lun = 0;
+	if (us->flags & US_FL_SINGLE_LUN)
+		us->max_lun = 0;
 
-	switch (ss->subclass) {
+	switch (us->subclass) {
 	case US_SC_RBC:
-		ss->protocol_name = "Reduced Block Commands (RBC)";
-		ss->proto_handler = usb_stor_transparent_scsi_command;
+		us->protocol_name = "Reduced Block Commands (RBC)";
+		us->proto_handler = usb_stor_transparent_scsi_command;
 		break;
 
 	case US_SC_8020:
-		ss->protocol_name = "8020i";
-		ss->proto_handler = usb_stor_ATAPI_command;
-		ss->max_lun = 0;
+		us->protocol_name = "8020i";
+		us->proto_handler = usb_stor_ATAPI_command;
+		us->max_lun = 0;
 		break;
 
 	case US_SC_QIC:
-		ss->protocol_name = "QIC-157";
-		ss->proto_handler = usb_stor_qic157_command;
-		ss->max_lun = 0;
+		us->protocol_name = "QIC-157";
+		us->proto_handler = usb_stor_qic157_command;
+		us->max_lun = 0;
 		break;
 
 	case US_SC_8070:
-		ss->protocol_name = "8070i";
-		ss->proto_handler = usb_stor_ATAPI_command;
-		ss->max_lun = 0;
+		us->protocol_name = "8070i";
+		us->proto_handler = usb_stor_ATAPI_command;
+		us->max_lun = 0;
 		break;
 
 	case US_SC_SCSI:
-		ss->protocol_name = "Transparent SCSI";
-		ss->proto_handler = usb_stor_transparent_scsi_command;
+		us->protocol_name = "Transparent SCSI";
+		us->proto_handler = usb_stor_transparent_scsi_command;
 		break;
 
 	case US_SC_UFI:
-		ss->protocol_name = "Uniform Floppy Interface (UFI)";
-		ss->proto_handler = usb_stor_ufi_command;
+		us->protocol_name = "Uniform Floppy Interface (UFI)";
+		us->proto_handler = usb_stor_ufi_command;
 		break;
 
 #ifdef CONFIG_USB_STORAGE_ISD200
 	case US_SC_ISD200:
-		ss->protocol_name = "ISD200 ATA/ATAPI";
-		ss->proto_handler = isd200_ata_command;
+		us->protocol_name = "ISD200 ATA/ATAPI";
+		us->proto_handler = isd200_ata_command;
 		break;
 #endif
 
 	default:
-		/* ss->protocol_name = "Unknown"; */
+		/* us->protocol_name = "Unknown"; */
 		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 */
-	if (usb_stor_allocate_urbs(ss))
+	if (usb_stor_allocate_urbs(us))
 		goto BadDevice;
 
 	/*
@@ -849,59 +849,59 @@
 	/* Just before we start our control thread, initialize
 	 * the device if it needs initialization */
 	if (unusual_dev && unusual_dev->initFunction)
-		unusual_dev->initFunction(ss);
+		unusual_dev->initFunction(us);
 
 	/* start up our control thread */
-	atomic_set(&ss->sm_state, US_STATE_IDLE);
-	ss->pid = kernel_thread(usb_stor_control_thread, ss,
+	atomic_set(&us->sm_state, US_STATE_IDLE);
+	us->pid = kernel_thread(usb_stor_control_thread, us,
 				CLONE_VM);
-	if (ss->pid < 0) {
+	if (us->pid < 0) {
 		printk(KERN_WARNING USB_STORAGE 
 		       "Unable to start control thread\n");
 		goto BadDevice;
 	}
 
 	/* wait for the thread to start */
-	wait_for_completion(&(ss->notify));
+	wait_for_completion(&(us->notify));
 
 	/* unlock the device pointers */
-	up(&(ss->dev_semaphore));
+	up(&(us->dev_semaphore));
 
 	/* now register	*/
-	ss->host = scsi_register(&usb_stor_host_template, sizeof(ss));
-	if (!ss->host) {
+	us->host = scsi_register(&usb_stor_host_template, sizeof(us));
+	if (!us->host) {
 		printk(KERN_WARNING USB_STORAGE
 			"Unable to register the scsi host\n");
 
 		/* tell the control thread to exit */
-		ss->srb = NULL;
-		up(&ss->sema);
-		wait_for_completion(&ss->notify);
+		us->srb = NULL;
+		up(&us->sema);
+		wait_for_completion(&us->notify);
 
 		/* re-lock the device pointers */
-		down(&ss->dev_semaphore);
+		down(&us->dev_semaphore);
 		goto BadDevice;
 	}
 
 	/* 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 */
-	scsi_set_device(ss->host, &intf->dev);
+	scsi_set_device(us->host, &intf->dev);
 
 	/* now add the host */
-	result = scsi_add_host(ss->host, NULL);
+	result = scsi_add_host(us->host, NULL);
 	if (result) {
 		printk(KERN_WARNING USB_STORAGE
 			"Unable to add the scsi host\n");
 
 		/* tell the control thread to exit */
-		ss->srb = NULL;
-		up(&ss->sema);
-		wait_for_completion(&ss->notify);
+		us->srb = NULL;
+		up(&us->sema);
+		wait_for_completion(&us->notify);
 
 		/* re-lock the device pointers */
-		down(&ss->dev_semaphore);
+		down(&us->dev_semaphore);
 		goto BadDevice;
 	}
 
@@ -911,66 +911,61 @@
 	       "USB Mass Storage device found at %d\n", dev->devnum);
 
 	/* save a pointer to our structure */
-	usb_set_intfdata(intf, ss);
+	usb_set_intfdata(intf, us);
 	return 0;
 
 	/* we come here if there are any problems */
-	/* ss->dev_semaphore must be locked */
+	/* us->dev_semaphore must be locked */
 BadDevice:
 	US_DEBUGP("storage_probe() failed\n");
-	usb_stor_deallocate_urbs(ss);
-	up(&ss->dev_semaphore);
-	kfree(ss);
+	usb_stor_deallocate_urbs(us);
+	up(&us->dev_semaphore);
+	kfree(us);
 	return -EIO;
 }
 
 /* Handle a disconnect event from the USB core */
 static void storage_disconnect(struct usb_interface *intf)
 {
-	struct us_data *ss;
+	struct us_data *us;
 	struct scsi_device *sdev;
 
 	US_DEBUGP("storage_disconnect() called\n");
 
-	ss = usb_get_intfdata(intf);
+	us = usb_get_intfdata(intf);
 	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 */
-	scsi_lock(ss->host);
-	list_for_each_entry(sdev, &ss->host->my_devices, siblings)
+	scsi_lock(us->host);
+	list_for_each_entry(sdev, &us->host->my_devices, siblings)
 		sdev->online = 0;
-	scsi_unlock(ss->host);
+	scsi_unlock(us->host);
 
 	/* 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.
 	 * Since we only queue one command at a time, this is pretty easy. */
-	if (ss->srb) {
-		ss->srb->result = DID_ERROR << 16;
-		ss->srb->scsi_done(ss->srb);
+	if (us->srb) {
+		us->srb->result = DID_ERROR << 16;
+		us->srb->scsi_done(us->srb);
 	}
 
 	/* TODO: somehow, wait for the device to
 	 * be 'idle' (tasklet completion) */
 
 	/* 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 */
-	if(scsi_remove_host(ss->host)) {
+	if(scsi_remove_host(us->host)) {
 		US_DEBUGP("-- SCSI refused to unregister\n");
 		BUG();
 		return;
 	};
 
 	/* finish SCSI host removal sequence */
-	scsi_unregister(ss->host);
+	scsi_unregister(us->host);
 
 	/* Kill the control threads
 	 *
@@ -978,34 +973,34 @@
 	 * notification that it has exited.
 	 */
 	US_DEBUGP("-- sending exit command to thread\n");
-	BUG_ON(atomic_read(&ss->sm_state) != US_STATE_IDLE);
-	ss->srb = NULL;
-	up(&(ss->sema));
-	wait_for_completion(&(ss->notify));
+	BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE);
+	us->srb = NULL;
+	up(&(us->sema));
+	wait_for_completion(&(us->notify));
 
 	/* free allocated urbs */
-	usb_stor_deallocate_urbs(ss);
+	usb_stor_deallocate_urbs(us);
 
 	/* If there's extra data in the us_data structure then
 	 * free that first */
-	if (ss->extra) {
+	if (us->extra) {
 		/* call the destructor routine, if it exists */
-		if (ss->extra_destructor) {
+		if (us->extra_destructor) {
 			US_DEBUGP("-- calling extra_destructor()\n");
-			ss->extra_destructor(ss->extra);
+			us->extra_destructor(us->extra);
 		}
 
 		/* destroy the extra data */
 		US_DEBUGP("-- freeing the data structure\n");
-		kfree(ss->extra);
+		kfree(us->extra);
 	}
 
 	/* up the semaphore so auto-code-checkers won't complain about
 	 * the down/up imbalance */
-	up(&(ss->dev_semaphore));
+	up(&(us->dev_semaphore));
 
 	/* free the structure itself */
-	kfree (ss);
+	kfree (us);
 }
 
 /***********************************************************************
diff -urN linux-2.5.67-bk3/fs/exec.c linux-2.5.67-bk4/fs/exec.c
--- linux-2.5.67-bk3/fs/exec.c	2003-04-12 04:33:18.000000000 -0700
+++ linux-2.5.67-bk4/fs/exec.c	2003-04-12 04:33:22.000000000 -0700
@@ -111,7 +111,7 @@
  *
  * Also note that we take the address to load from from the file itself.
  */
-asmlinkage long sys_uselib(const char * library)
+asmlinkage long sys_uselib(const char __user * library)
 {
 	struct file * file;
 	struct nameidata nd;
diff -urN linux-2.5.67-bk3/fs/filesystems.c linux-2.5.67-bk4/fs/filesystems.c
--- linux-2.5.67-bk3/fs/filesystems.c	2003-04-07 10:32:22.000000000 -0700
+++ linux-2.5.67-bk4/fs/filesystems.c	2003-04-12 04:33:22.000000000 -0700
@@ -61,7 +61,7 @@
 
 
 /* define fs_subsys */
-static decl_subsys(fs, NULL);
+static decl_subsys(fs, NULL, NULL);
 
 static int register_fs_subsys(struct file_system_type * fs)
 {
diff -urN linux-2.5.67-bk3/fs/nfs/dir.c linux-2.5.67-bk4/fs/nfs/dir.c
--- linux-2.5.67-bk3/fs/nfs/dir.c	2003-04-12 04:33:18.000000000 -0700
+++ linux-2.5.67-bk4/fs/nfs/dir.c	2003-04-12 04:33:22.000000000 -0700
@@ -736,9 +736,10 @@
 
 		res = -EIO;
 		if (PageUptodate(page)) {
-			desc.ptr = kmap_atomic(page, KM_USER0);
+			void * kaddr = kmap_atomic(page, KM_USER0);
+			desc.ptr = kaddr;
 			res = find_dirent_name(&desc, page, dentry);
-			kunmap_atomic(desc.ptr, KM_USER0);
+			kunmap_atomic(kaddr, KM_USER0);
 		}
 		page_cache_release(page);
 
diff -urN linux-2.5.67-bk3/fs/partitions/check.c linux-2.5.67-bk4/fs/partitions/check.c
--- linux-2.5.67-bk3/fs/partitions/check.c	2003-04-07 10:32:48.000000000 -0700
+++ linux-2.5.67-bk4/fs/partitions/check.c	2003-04-12 04:33:22.000000000 -0700
@@ -248,7 +248,7 @@
 
 extern struct subsystem block_subsys;
 
-static struct kobj_type ktype_part = {
+struct kobj_type ktype_part = {
 	.default_attrs	= default_attrs,
 	.sysfs_ops	= &part_sysfs_ops,
 };
diff -urN linux-2.5.67-bk3/include/linux/i2c-dev.h linux-2.5.67-bk4/include/linux/i2c-dev.h
--- linux-2.5.67-bk3/include/linux/i2c-dev.h	2003-04-07 10:30:58.000000000 -0700
+++ linux-2.5.67-bk4/include/linux/i2c-dev.h	2003-04-12 04:33:22.000000000 -0700
@@ -31,16 +31,16 @@
 
 /* This is the structure as used in the I2C_SMBUS ioctl call */
 struct i2c_smbus_ioctl_data {
-	char read_write;
+	__u8 read_write;
 	__u8 command;
-	int size;
+	__u32 size;
 	union i2c_smbus_data *data;
 };
 
 /* This is the structure as used in the I2C_RDWR ioctl call */
 struct i2c_rdwr_ioctl_data {
 	struct i2c_msg *msgs;	/* pointers to i2c_msgs */
-	int nmsgs;		/* number of i2c_msgs */
+	__u32 nmsgs;		/* number of i2c_msgs */
 };
 
 #endif /* _LINUX_I2C_DEV_H */
diff -urN linux-2.5.67-bk3/include/linux/i2c.h linux-2.5.67-bk4/include/linux/i2c.h
--- linux-2.5.67-bk3/include/linux/i2c.h	2003-04-07 10:30:44.000000000 -0700
+++ linux-2.5.67-bk4/include/linux/i2c.h	2003-04-12 04:33:22.000000000 -0700
@@ -182,6 +182,13 @@
 	return dev_set_drvdata (&dev->dev, data);
 }
 
+#define I2C_DEVNAME(str)   .dev = { .name = str }
+
+static inline char *i2c_clientname(struct i2c_client *c)
+{
+	return c->dev.name;
+}
+
 /*
  * The following structs are for those who like to implement new bus drivers:
  * i2c_algorithm is the interface to a class of hardware solutions which can
@@ -360,15 +367,15 @@
  */
 struct i2c_msg {
 	__u16 addr;	/* slave address			*/
-	unsigned short flags;		
+ 	__u16 flags;		
 #define I2C_M_TEN	0x10	/* we have a ten bit chip address	*/
 #define I2C_M_RD	0x01
 #define I2C_M_NOSTART	0x4000
 #define I2C_M_REV_DIR_ADDR	0x2000
 #define I2C_M_IGNORE_NAK	0x1000
 #define I2C_M_NO_RD_ACK		0x0800
-	short len;		/* msg length				*/
-	char *buf;		/* pointer to msg data			*/
+ 	__u16 len;		/* msg length				*/
+ 	__u8 *buf;		/* pointer to msg data			*/
 };
 
 /* To determine what functionality is present */
diff -urN linux-2.5.67-bk3/include/linux/if_bridge.h linux-2.5.67-bk4/include/linux/if_bridge.h
--- linux-2.5.67-bk3/include/linux/if_bridge.h	2003-04-07 10:31:20.000000000 -0700
+++ linux-2.5.67-bk4/include/linux/if_bridge.h	2003-04-12 04:33:22.000000000 -0700
@@ -101,7 +101,7 @@
 struct net_bridge;
 struct net_bridge_port;
 
-extern int (*br_ioctl_hook)(unsigned long arg);
+extern void brioctl_set(int (*ioctl_hook)(unsigned long));
 extern int (*br_handle_frame_hook)(struct sk_buff *skb);
 extern int (*br_should_route_hook)(struct sk_buff **pskb);
 
diff -urN linux-2.5.67-bk3/include/linux/igmp.h linux-2.5.67-bk4/include/linux/igmp.h
--- linux-2.5.67-bk3/include/linux/igmp.h	2003-04-07 10:31:21.000000000 -0700
+++ linux-2.5.67-bk4/include/linux/igmp.h	2003-04-12 04:33:22.000000000 -0700
@@ -32,13 +32,60 @@
 	__u32 group;
 };
 
+/* V3 group record types [grec_type] */
+#define IGMPV3_MODE_IS_INCLUDE		1
+#define IGMPV3_MODE_IS_EXCLUDE		2
+#define IGMPV3_CHANGE_TO_INCLUDE	3
+#define IGMPV3_CHANGE_TO_EXCLUDE	4
+#define IGMPV3_ALLOW_NEW_SOURCES	5
+#define IGMPV3_BLOCK_OLD_SOURCES	6
+
+struct igmpv3_grec {
+	__u8	grec_type;
+	__u8	grec_auxwords;
+	__u16	grec_nsrcs;
+	__u32	grec_mca;
+	__u32	grec_src[0];
+};
+
+struct igmpv3_report {
+	__u8 type;
+	__u8 resv1;
+	__u16 csum;
+	__u16 resv2;
+	__u16 ngrec;
+	struct igmpv3_grec grec[0];
+};
+
+struct igmpv3_query {
+	__u8 type;
+	__u8 code;
+	__u16 csum;
+	__u32 group;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8 qrv:3,
+	     suppress:1,
+	     resv:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	__u8 resv:4,
+	     suppress:1,
+	     qrv:3;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+	__u8 qqic;
+	__u16 nsrcs;
+	__u32 srcs[0];
+};
+
 #define IGMP_HOST_MEMBERSHIP_QUERY	0x11	/* From RFC1112 */
 #define IGMP_HOST_MEMBERSHIP_REPORT	0x12	/* Ditto */
 #define IGMP_DVMRP			0x13	/* DVMRP routing */
 #define IGMP_PIM			0x14	/* PIM routing */
 #define IGMP_TRACE			0x15
-#define IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16	/* New version of 0x11 */
+#define IGMPV2_HOST_MEMBERSHIP_REPORT	0x16	/* V2 version of 0x11 */
 #define IGMP_HOST_LEAVE_MESSAGE 	0x17
+#define IGMPV3_HOST_MEMBERSHIP_REPORT	0x22	/* V3 version of 0x11 */
 
 #define IGMP_MTRACE_RESP		0x1e
 #define IGMP_MTRACE			0x1f
@@ -68,6 +115,7 @@
 
 #define IGMP_ALL_HOSTS		htonl(0xE0000001L)
 #define IGMP_ALL_ROUTER 	htonl(0xE0000002L)
+#define IGMPV3_ALL_MCR	 	htonl(0xE0000016L)
 #define IGMP_LOCAL_GROUP	htonl(0xE0000000L)
 #define IGMP_LOCAL_GROUP_MASK	htonl(0xFFFFFF00L)
 
@@ -79,6 +127,18 @@
 #include <linux/skbuff.h>
 #include <linux/in.h>
 
+struct ip_sf_socklist
+{
+	unsigned int		sl_max;
+	unsigned int		sl_count;
+	__u32			sl_addr[0];
+};
+
+#define IP_SFLSIZE(count)	(sizeof(struct ip_sf_socklist) + \
+	(count) * sizeof(__u32))
+
+#define IP_SFBLOCK	10	/* allocate this many at once */
+
 /* ip_mc_socklist is real list now. Speed is not argument;
    this list never used in fast path code
  */
@@ -88,12 +148,28 @@
 	struct ip_mc_socklist	*next;
 	int			count;
 	struct ip_mreqn		multi;
+	unsigned int		sfmode;		/* MCAST_{INCLUDE,EXCLUDE} */
+	struct ip_sf_socklist	*sflist;
+};
+
+struct ip_sf_list
+{
+	struct ip_sf_list	*sf_next;
+	__u32			sf_inaddr;
+	unsigned long		sf_count[2];	/* include/exclude counts */
+	unsigned char		sf_gsresp;	/* include in g & s response? */
+	unsigned char		sf_oldin;	/* change state */
+	unsigned char		sf_crcount;	/* retrans. left to send */
 };
 
 struct ip_mc_list
 {
 	struct in_device	*interface;
 	unsigned long		multiaddr;
+	struct ip_sf_list	*sources;
+	struct ip_sf_list	*tomb;
+	unsigned int		sfmode;
+	unsigned long		sfcount[2];
 	struct ip_mc_list	*next;
 	struct timer_list	timer;
 	int			users;
@@ -103,13 +179,31 @@
 	char			reporter;
 	char			unsolicit_count;
 	char			loaded;
+	unsigned char		gsquery;	/* check source marks? */
+	unsigned char		crcount;
 };
 
-extern int ip_check_mc(struct in_device *dev, u32 mc_addr);
+/* V3 exponential field decoding */
+#define IGMPV3_MASK(value, nb) ((nb)>=32 ? (value) : ((1<<(nb))-1) & (value))
+#define IGMPV3_EXP(thresh, nbmant, nbexp, value) \
+	((value) < (thresh) ? (value) : \
+        ((IGMPV3_MASK(value, nbmant) | (1<<(nbmant+nbexp))) << \
+         (IGMPV3_MASK((value) >> (nbmant), nbexp) + (nbexp))))
+
+#define IGMPV3_QQIC(value) IGMPV3_EXP(0x80, 4, 3, value)
+#define IGMPV3_MRC(value) IGMPV3_EXP(0x8000, 12, 3, value)
+
+extern int ip_check_mc(struct in_device *dev, u32 mc_addr, u32 src_addr, u16 proto);
 extern int igmp_rcv(struct sk_buff *);
 extern int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr);
 extern int ip_mc_leave_group(struct sock *sk, struct ip_mreqn *imr);
 extern void ip_mc_drop_socket(struct sock *sk);
+extern int ip_mc_source(int add, int omode, struct sock *sk,
+		struct ip_mreq_source *mreqs);
+extern int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf);
+extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
+		struct ip_msfilter *optval, int *optlen);
+extern int ip_mc_sf_allow(struct sock *sk, u32 local, u32 rmt, int dif);
 extern void ip_mr_init(void);
 extern void ip_mc_init_dev(struct in_device *);
 extern void ip_mc_destroy_dev(struct in_device *);
diff -urN linux-2.5.67-bk3/include/linux/in.h linux-2.5.67-bk4/include/linux/in.h
--- linux-2.5.67-bk3/include/linux/in.h	2003-04-07 10:33:03.000000000 -0700
+++ linux-2.5.67-bk4/include/linux/in.h	2003-04-12 04:33:22.000000000 -0700
@@ -85,6 +85,14 @@
 #define IP_MULTICAST_LOOP 		34
 #define IP_ADD_MEMBERSHIP		35
 #define IP_DROP_MEMBERSHIP		36
+#define IP_UNBLOCK_SOURCE		37
+#define IP_BLOCK_SOURCE			38
+#define IP_ADD_SOURCE_MEMBERSHIP	39
+#define IP_DROP_SOURCE_MEMBERSHIP	40
+#define IP_MSFILTER			41
+
+#define MCAST_EXCLUDE	0
+#define MCAST_INCLUDE	1
 
 /* These need to appear somewhere around here */
 #define IP_DEFAULT_MULTICAST_TTL        1
@@ -105,6 +113,24 @@
 	int		imr_ifindex;		/* Interface index */
 };
 
+struct ip_mreq_source {
+	__u32		imr_multiaddr;
+	__u32		imr_interface;
+	__u32		imr_sourceaddr;
+};
+
+struct ip_msfilter {
+	__u32		imsf_multiaddr;
+	__u32		imsf_interface;
+	__u32		imsf_fmode;
+	__u32		imsf_numsrc;
+	__u32		imsf_slist[1];
+};
+
+#define IP_MSFILTER_SIZE(numsrc) \
+	(sizeof(struct ip_msfilter) - sizeof(__u32) \
+	+ (numsrc) * sizeof(__u32))
+
 struct in_pktinfo
 {
 	int		ipi_ifindex;
diff -urN linux-2.5.67-bk3/include/linux/inetdevice.h linux-2.5.67-bk4/include/linux/inetdevice.h
--- linux-2.5.67-bk3/include/linux/inetdevice.h	2003-04-07 10:31:56.000000000 -0700
+++ linux-2.5.67-bk4/include/linux/inetdevice.h	2003-04-12 04:33:22.000000000 -0700
@@ -34,7 +34,17 @@
 	int			dead;
 	struct in_ifaddr	*ifa_list;	/* IP ifaddr chain		*/
 	struct ip_mc_list	*mc_list;	/* IP multicast filter chain    */
+	rwlock_t		mc_lock;	/* for mc_tomb */
+	struct ip_mc_list	*mc_tomb;
 	unsigned long		mr_v1_seen;
+	unsigned long		mr_v2_seen;
+	unsigned long		mr_maxdelay;
+	unsigned char		mr_qrv;
+	unsigned char		mr_gq_running;
+	unsigned char		mr_ifc_count;
+	struct timer_list	mr_gq_timer;	/* general query timer */
+	struct timer_list	mr_ifc_timer;	/* interface change timer */
+
 	struct neigh_parms	*arp_parms;
 	struct ipv4_devconf	cnf;
 };
diff -urN linux-2.5.67-bk3/include/linux/kobject.h linux-2.5.67-bk4/include/linux/kobject.h
--- linux-2.5.67-bk3/include/linux/kobject.h	2003-04-07 10:30:36.000000000 -0700
+++ linux-2.5.67-bk4/include/linux/kobject.h	2003-04-12 04:33:22.000000000 -0700
@@ -57,12 +57,24 @@
  *	of object; multiple ksets can belong to one subsystem. All 
  *	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 subsystem	* subsys;
 	struct kobj_type	* ktype;
 	struct list_head	list;
 	struct kobject		kobj;
+	struct kset_hotplug_ops	* hotplug_ops;
 };
 
 
@@ -86,6 +98,13 @@
 	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 *);
 
@@ -95,11 +114,12 @@
 	struct rw_semaphore	rwsem;
 };
 
-#define decl_subsys(_name,_type) \
+#define decl_subsys(_name,_type,_hotplug_ops) \
 struct subsystem _name##_subsys = { \
 	.kset = { \
 		.kobj = { .name = __stringify(_name) }, \
 		.ktype = _type, \
+		.hotplug_ops =_hotplug_ops, \
 	} \
 }
 
diff -urN linux-2.5.67-bk3/include/net/ip.h linux-2.5.67-bk4/include/net/ip.h
--- linux-2.5.67-bk3/include/net/ip.h	2003-04-07 10:31:58.000000000 -0700
+++ linux-2.5.67-bk4/include/net/ip.h	2003-04-12 04:33:22.000000000 -0700
@@ -79,6 +79,7 @@
 extern void		ip_mc_dropsocket(struct sock *);
 extern void		ip_mc_dropdevice(struct net_device *dev);
 extern int		ip_mc_procinfo(char *, char **, off_t, int);
+extern int		ip_mcf_procinfo(char *, char **, off_t, int);
 
 /*
  *	Functions provided by ip.c
diff -urN linux-2.5.67-bk3/include/net/ip6_fib.h linux-2.5.67-bk4/include/net/ip6_fib.h
--- linux-2.5.67-bk3/include/net/ip6_fib.h	2003-04-07 10:32:17.000000000 -0700
+++ linux-2.5.67-bk4/include/net/ip6_fib.h	2003-04-12 04:33:22.000000000 -0700
@@ -72,6 +72,8 @@
 
 	struct rt6key			rt6i_dst;
 	struct rt6key			rt6i_src;
+
+	u8				rt6i_protocol;
 };
 
 struct fib6_walker_t
@@ -160,11 +162,14 @@
 extern int			fib6_walk_continue(struct fib6_walker_t *w);
 
 extern int			fib6_add(struct fib6_node *root,
-					 struct rt6_info *rt);
+					 struct rt6_info *rt,
+					 struct nlmsghdr *nlh);
 
-extern int			fib6_del(struct rt6_info *rt);
+extern int			fib6_del(struct rt6_info *rt,
+					 struct nlmsghdr *nlh);
 
-extern void			inet6_rt_notify(int event, struct rt6_info *rt);
+extern void			inet6_rt_notify(int event, struct rt6_info *rt,
+						struct nlmsghdr *nlh);
 
 extern void			fib6_run_gc(unsigned long dummy);
 
diff -urN linux-2.5.67-bk3/include/net/ip6_route.h linux-2.5.67-bk4/include/net/ip6_route.h
--- linux-2.5.67-bk3/include/net/ip6_route.h	2003-04-07 10:30:57.000000000 -0700
+++ linux-2.5.67-bk4/include/net/ip6_route.h	2003-04-12 04:33:22.000000000 -0700
@@ -37,8 +37,10 @@
 
 extern int			ipv6_route_ioctl(unsigned int cmd, void *arg);
 
-extern int			ip6_route_add(struct in6_rtmsg *rtmsg);
-extern int			ip6_del_rt(struct rt6_info *);
+extern int			ip6_route_add(struct in6_rtmsg *rtmsg,
+					      struct nlmsghdr *);
+extern int			ip6_del_rt(struct rt6_info *,
+					   struct nlmsghdr *);
 
 extern int			ip6_rt_addr_add(struct in6_addr *addr,
 						struct net_device *dev);
diff -urN linux-2.5.67-bk3/kernel/timer.c linux-2.5.67-bk4/kernel/timer.c
--- linux-2.5.67-bk3/kernel/timer.c	2003-04-12 04:33:18.000000000 -0700
+++ linux-2.5.67-bk4/kernel/timer.c	2003-04-12 04:33:22.000000000 -0700
@@ -397,7 +397,8 @@
 
 	spin_lock_irq(&base->lock);
 	while (time_after_eq(jiffies, base->timer_jiffies)) {
-		struct list_head *head;
+		struct list_head work_list = LIST_HEAD_INIT(work_list);
+		struct list_head *head = &work_list;
  		int index = base->timer_jiffies & TVR_MASK;
  
 		/*
@@ -409,8 +410,8 @@
 					!cascade(base, &base->tv4, INDEX(2)))
 			cascade(base, &base->tv5, INDEX(3));
 		++base->timer_jiffies; 
+		list_splice_init(base->tv1.vec + index, &work_list);
 repeat:
-		head = base->tv1.vec + index;
 		if (!list_empty(head)) {
 			void (*fn)(unsigned long);
 			unsigned long data;
diff -urN linux-2.5.67-bk3/lib/kobject.c linux-2.5.67-bk4/lib/kobject.c
--- linux-2.5.67-bk3/lib/kobject.c	2003-04-07 10:32:48.000000000 -0700
+++ linux-2.5.67-bk4/lib/kobject.c	2003-04-12 04:33:22.000000000 -0700
@@ -11,14 +11,6 @@
 
 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.
  *	@kobj:	object we're working on.
@@ -67,6 +59,140 @@
 }
 
 
+#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.
  *	@kobj:	object in question.
@@ -111,6 +237,7 @@
 {
 	int error = 0;
 	struct kobject * parent;
+	struct kobject * top_kobj;
 
 	if (!(kobj = kobject_get(kobj)))
 		return -ENOENT;
@@ -134,6 +261,19 @@
 	error = create_dir(kobj);
 	if (error)
 		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;
 }
 
@@ -162,6 +302,20 @@
 
 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);
 	unlink(kobj);
 }
diff -urN linux-2.5.67-bk3/net/bridge/br.c linux-2.5.67-bk4/net/bridge/br.c
--- linux-2.5.67-bk3/net/bridge/br.c	2003-04-07 10:30:43.000000000 -0700
+++ linux-2.5.67-bk4/net/bridge/br.c	2003-04-12 04:33:22.000000000 -0700
@@ -49,8 +49,9 @@
 	if (br_netfilter_init())
 		return 1;
 #endif
+	brioctl_set(br_ioctl_deviceless_stub);
 	br_handle_frame_hook = br_handle_frame;
-	br_ioctl_hook = br_ioctl_deviceless_stub;
+
 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
 	br_fdb_get_hook = br_fdb_get;
 	br_fdb_put_hook = br_fdb_put;
@@ -60,24 +61,18 @@
 	return 0;
 }
 
-static void __br_clear_ioctl_hook(void)
-{
-	br_ioctl_hook = NULL;
-}
-
 static void __exit br_deinit(void)
 {
 #ifdef CONFIG_NETFILTER
 	br_netfilter_fini();
 #endif
 	unregister_netdevice_notifier(&br_device_notifier);
-	br_call_ioctl_atomic(__br_clear_ioctl_hook);
 
-	br_write_lock_bh(BR_NETPROTO_LOCK);
+	brioctl_set(NULL);
 	br_handle_frame_hook = NULL;
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
 
 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
+	/* FIX ME. move into hook structure with ref count */
 	br_fdb_get_hook = NULL;
 	br_fdb_put_hook = NULL;
 #endif
diff -urN linux-2.5.67-bk3/net/bridge/br_if.c linux-2.5.67-bk4/net/bridge/br_if.c
--- linux-2.5.67-bk3/net/bridge/br_if.c	2003-04-07 10:32:58.000000000 -0700
+++ linux-2.5.67-bk4/net/bridge/br_if.c	2003-04-12 04:33:22.000000000 -0700
@@ -23,6 +23,7 @@
 #include "br_private.h"
 
 static struct net_bridge *bridge_list;
+static spinlock_t bridge_lock = SPIN_LOCK_UNLOCKED;
 
 static int br_initial_port_cost(struct net_device *dev)
 {
@@ -69,6 +70,7 @@
 	return 0;
 }
 
+/* called with bridge_lock */
 static struct net_bridge **__find_br(char *name)
 {
 	struct net_bridge **b;
@@ -188,8 +190,10 @@
 		return -EEXIST;
 	}
 
+	spin_lock(&bridge_lock);
 	br->next = bridge_list;
 	bridge_list = br;
+	spin_unlock(&bridge_lock);
 
 	br_inc_use_count();
 	register_netdev(&br->dev);
@@ -202,17 +206,22 @@
 	struct net_bridge **b;
 	struct net_bridge *br;
 
-	if ((b = __find_br(name)) == NULL)
+	spin_lock(&bridge_lock);
+	if ((b = __find_br(name)) == NULL) {
+		spin_unlock(&bridge_lock);
 		return -ENXIO;
+	}
 
 	br = *b;
-
-	if (br->dev.flags & IFF_UP)
+	if (br->dev.flags & IFF_UP) {
+		spin_unlock(&bridge_lock);
 		return -EBUSY;
-
-	del_ifs(br);
+	}
 
 	*b = br->next;
+	spin_unlock(&bridge_lock);
+
+	del_ifs(br);
 
 	unregister_netdev(&br->dev);
 	kfree(br);
@@ -272,6 +281,7 @@
 	struct net_bridge *br;
 	int i;
 
+	spin_lock(&bridge_lock);
 	br = bridge_list;
 	for (i=0;i<num;i++) {
 		if (br == NULL)
@@ -280,6 +290,7 @@
 		indices[i] = br->dev.ifindex;
 		br = br->next;
 	}
+	spin_unlock(&bridge_lock);
 
 	return i;
 }
@@ -289,9 +300,11 @@
 {
 	struct net_bridge_port *p;
 
+	read_lock(&br->lock);
 	p = br->port_list;
 	while (p != NULL) {
 		ifindices[p->port_no] = p->dev->ifindex;
 		p = p->next;
 	}
+	read_unlock(&br->lock);
 }
diff -urN linux-2.5.67-bk3/net/bridge/br_ioctl.c linux-2.5.67-bk4/net/bridge/br_ioctl.c
--- linux-2.5.67-bk3/net/bridge/br_ioctl.c	2003-04-07 10:30:43.000000000 -0700
+++ linux-2.5.67-bk4/net/bridge/br_ioctl.c	2003-04-12 04:33:22.000000000 -0700
@@ -53,6 +53,7 @@
 	{
 		struct __bridge_info b;
 
+	        read_lock(&br->lock);
 		memset(&b, 0, sizeof(struct __bridge_info));
 		memcpy(&b.designated_root, &br->designated_root, 8);
 		memcpy(&b.bridge_id, &br->bridge_id, 8);
@@ -73,6 +74,7 @@
 		b.tcn_timer_value = br_timer_get_residue(&br->tcn_timer);
 		b.topology_change_timer_value = br_timer_get_residue(&br->topology_change_timer);
 		b.gc_timer_value = br_timer_get_residue(&br->gc_timer);
+	        read_unlock(&br->lock);
 
 		if (copy_to_user((void *)arg0, &b, sizeof(b)))
 			return -EFAULT;
@@ -96,21 +98,27 @@
 	}
 
 	case BRCTL_SET_BRIDGE_FORWARD_DELAY:
+		write_lock(&br->lock);
 		br->bridge_forward_delay = arg0;
 		if (br_is_root_bridge(br))
 			br->forward_delay = arg0;
+		write_unlock(&br->lock);
 		return 0;
 
 	case BRCTL_SET_BRIDGE_HELLO_TIME:
+		write_lock(&br->lock);
 		br->bridge_hello_time = arg0;
 		if (br_is_root_bridge(br))
 			br->hello_time = arg0;
+		write_unlock(&br->lock);
 		return 0;
 
 	case BRCTL_SET_BRIDGE_MAX_AGE:
+		write_lock(&br->lock);
 		br->bridge_max_age = arg0;
 		if (br_is_root_bridge(br))
 			br->max_age = arg0;
+		write_unlock(&br->lock);
 		return 0;
 
 	case BRCTL_SET_AGEING_TIME:
@@ -126,6 +134,7 @@
 		struct __port_info p;
 		struct net_bridge_port *pt;
 
+		read_lock(&br->lock);
 		if ((pt = br_get_port(br, arg1)) == NULL)
 			return -EINVAL;
 
@@ -143,6 +152,8 @@
 		p.forward_delay_timer_value = br_timer_get_residue(&pt->forward_delay_timer);
 		p.hold_timer_value = br_timer_get_residue(&pt->hold_timer);
 
+		read_unlock(&br->lock);
+
 		if (copy_to_user((void *)arg0, &p, sizeof(p)))
 			return -EFAULT;
 
@@ -154,16 +165,20 @@
 		return 0;
 
 	case BRCTL_SET_BRIDGE_PRIORITY:
+		write_lock(&br->lock);
 		br_stp_set_bridge_priority(br, arg0);
+		write_unlock(&br->lock);
 		return 0;
 
 	case BRCTL_SET_PORT_PRIORITY:
 	{
 		struct net_bridge_port *p;
 
+		write_lock(&br->lock);
 		if ((p = br_get_port(br, arg0)) == NULL)
 			return -EINVAL;
 		br_stp_set_port_priority(p, arg1);
+		write_unlock(&br->lock);
 		return 0;
 	}
 
@@ -171,9 +186,11 @@
 	{
 		struct net_bridge_port *p;
 
+		write_lock(&br->lock);
 		if ((p = br_get_port(br, arg0)) == NULL)
 			return -EINVAL;
 		br_stp_set_path_cost(p, arg1);
+		write_unlock(&br->lock);
 		return 0;
 	}
 
@@ -230,11 +247,9 @@
 	return -EOPNOTSUPP;
 }
 
-static DECLARE_MUTEX(ioctl_mutex);
 
 int br_ioctl_deviceless_stub(unsigned long arg)
 {
-	int err;
 	unsigned long i[3];
 
 	if (!capable(CAP_NET_ADMIN))
@@ -243,11 +258,7 @@
 	if (copy_from_user(i, (void *)arg, 3*sizeof(unsigned long)))
 		return -EFAULT;
 
-	down(&ioctl_mutex);
-	err = br_ioctl_deviceless(i[0], i[1], i[2]);
-	up(&ioctl_mutex);
-
-	return err;
+	return br_ioctl_deviceless(i[0], i[1], i[2]);
 }
 
 int br_ioctl(struct net_bridge *br, unsigned int cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2)
@@ -257,18 +268,9 @@
 	if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
 
-	down(&ioctl_mutex);
 	err = br_ioctl_deviceless(cmd, arg0, arg1);
 	if (err == -EOPNOTSUPP)
 		err = br_ioctl_device(br, cmd, arg0, arg1, arg2);
-	up(&ioctl_mutex);
 
 	return err;
 }
-
-void br_call_ioctl_atomic(void (*fn)(void))
-{
-	down(&ioctl_mutex);
-	fn();
-	up(&ioctl_mutex);
-}
diff -urN linux-2.5.67-bk3/net/bridge/br_notify.c linux-2.5.67-bk4/net/bridge/br_notify.c
--- linux-2.5.67-bk3/net/bridge/br_notify.c	2003-04-07 10:30:39.000000000 -0700
+++ linux-2.5.67-bk4/net/bridge/br_notify.c	2003-04-12 04:33:22.000000000 -0700
@@ -21,15 +21,14 @@
 
 struct notifier_block br_device_notifier =
 {
-	br_device_event,
-	NULL,
-	0
+	.notifier_call = br_device_event
 };
 
 static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
 {
 	struct net_device *dev;
 	struct net_bridge_port *p;
+	struct net_bridge *br;
 
 	dev = ptr;
 	p = dev->br_port;
@@ -37,13 +36,15 @@
 	if (p == NULL)
 		return NOTIFY_DONE;
 
-	switch (event)
+	br = p->br;
+
+	switch (event) 
 	{
 	case NETDEV_CHANGEADDR:
-		read_lock(&p->br->lock);
+		write_lock_bh(&br->lock);
 		br_fdb_changeaddr(p, dev->dev_addr);
-		br_stp_recalculate_bridge_id(p->br);
-		read_unlock(&p->br->lock);
+		br_stp_recalculate_bridge_id(br);
+		write_unlock_bh(&br->lock);
 		break;
 
 	case NETDEV_GOING_DOWN:
@@ -51,23 +52,23 @@
 		break;
 
 	case NETDEV_DOWN:
-		if (p->br->dev.flags & IFF_UP) {
-			read_lock(&p->br->lock);
-			br_stp_disable_port(dev->br_port);
-			read_unlock(&p->br->lock);
+		if (br->dev.flags & IFF_UP) {
+			write_lock_bh(&br->lock);
+			br_stp_disable_port(p);
+			write_unlock_bh(&br->lock);
 		}
 		break;
 
 	case NETDEV_UP:
-		if (p->br->dev.flags & IFF_UP) {
-			read_lock(&p->br->lock);
-			br_stp_enable_port(dev->br_port);
-			read_unlock(&p->br->lock);
+		if (!(br->dev.flags & IFF_UP)) {
+			write_lock_bh(&br->lock);
+			br_stp_enable_port(p);
+			write_unlock_bh(&br->lock);
 		}
 		break;
 
 	case NETDEV_UNREGISTER:
-		br_del_if(dev->br_port->br, dev);
+		br_del_if(br, dev);
 		break;
 	}
 
diff -urN linux-2.5.67-bk3/net/core/dev.c linux-2.5.67-bk4/net/core/dev.c
--- linux-2.5.67-bk3/net/core/dev.c	2003-04-07 10:31:20.000000000 -0700
+++ linux-2.5.67-bk4/net/core/dev.c	2003-04-12 04:33:22.000000000 -0700
@@ -1433,9 +1433,8 @@
 	}
 }
 
-#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+#if defined(CONFIG_BRIDGE) || defined (CONFIG_BRIDGE_MODULE)
 int (*br_handle_frame_hook)(struct sk_buff *skb) = NULL;
-#endif
 
 static __inline__ int handle_bridge(struct sk_buff *skb,
 				     struct packet_type *pt_prev)
@@ -1454,6 +1453,7 @@
 	return ret;
 }
 
+#endif
 
 #ifdef CONFIG_NET_DIVERT
 static inline int handle_diverter(struct sk_buff *skb)
@@ -1510,12 +1510,13 @@
 #endif /* CONFIG_NET_DIVERT */
 
 #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
-	if (skb->dev->br_port && br_handle_frame_hook) {
+	if (skb->dev->br_port) {
 		int ret;
 
 		ret = handle_bridge(skb, pt_prev);
-		if (br_handle_frame_hook(skb) == 0)
+		if (br_handle_frame_hook(skb) == 0) 
 			return ret;
+
 		pt_prev = NULL;
 	}
 #endif
@@ -2815,7 +2816,7 @@
 extern void dv_init(void);
 #endif /* CONFIG_NET_DIVERT */
 
-static decl_subsys(net,NULL);
+static decl_subsys(net,NULL,NULL);
 
 
 /*
diff -urN linux-2.5.67-bk3/net/ipv4/Kconfig linux-2.5.67-bk4/net/ipv4/Kconfig
--- linux-2.5.67-bk3/net/ipv4/Kconfig	2003-04-07 10:32:18.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/Kconfig	2003-04-12 04:33:22.000000000 -0700
@@ -362,5 +362,13 @@
 
 	  If unsure, say Y.
 
+config INET_IPCOMP
+	tristate "IP: IPComp transformation"
+	---help---
+	  Support for IP Paylod Compression (RFC3173), typically needed
+	  for IPsec.
+	  
+	  If unsure, say Y.
+
 source "net/ipv4/netfilter/Kconfig"
 
diff -urN linux-2.5.67-bk3/net/ipv4/Makefile linux-2.5.67-bk4/net/ipv4/Makefile
--- linux-2.5.67-bk3/net/ipv4/Makefile	2003-04-07 10:30:57.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/Makefile	2003-04-12 04:33:22.000000000 -0700
@@ -18,6 +18,7 @@
 obj-$(CONFIG_SYN_COOKIES) += syncookies.o
 obj-$(CONFIG_INET_AH) += ah.o
 obj-$(CONFIG_INET_ESP) += esp.o
+obj-$(CONFIG_INET_IPCOMP) += ipcomp.o
 obj-$(CONFIG_IP_PNP) += ipconfig.o
 obj-$(CONFIG_NETFILTER)	+= netfilter/
 
diff -urN linux-2.5.67-bk3/net/ipv4/igmp.c linux-2.5.67-bk4/net/ipv4/igmp.c
--- linux-2.5.67-bk3/net/ipv4/igmp.c	2003-04-07 10:32:55.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/igmp.c	2003-04-12 04:33:22.000000000 -0700
@@ -68,6 +68,8 @@
  *		Alan Cox:		Forget to enable FDDI support earlier.
  *		Alexey Kuznetsov:	Fixed leaving groups on device down.
  *		Alexey Kuznetsov:	Accordance to igmp-v2-06 draft.
+ *		David L Stevens:	IGMPv3 support, with help from
+ *					Vinay Kulkarni
  */
 
 
@@ -101,12 +103,10 @@
 
 #define IP_MAX_MEMBERSHIPS 20
 
-#ifdef CONFIG_IP_MULTICAST
-
-
 /* Parameter names and values are taken from igmp-v2-06 draft */
 
 #define IGMP_V1_Router_Present_Timeout		(400*HZ)
+#define IGMP_V2_Router_Present_Timeout		(400*HZ)
 #define IGMP_Unsolicited_Report_Interval	(10*HZ)
 #define IGMP_Query_Response_Interval		(10*HZ)
 #define IGMP_Unsolicited_Report_Count		2
@@ -121,9 +121,21 @@
  * contradict to specs provided this delay is small enough.
  */
 
-#define IGMP_V1_SEEN(in_dev) ((in_dev)->mr_v1_seen && (long)(jiffies - (in_dev)->mr_v1_seen) < 0)
+#define IGMP_V1_SEEN(in_dev) ((in_dev)->mr_v1_seen && \
+		time_before(jiffies, (in_dev)->mr_v1_seen))
+#define IGMP_V2_SEEN(in_dev) ((in_dev)->mr_v2_seen && \
+		time_before(jiffies, (in_dev)->mr_v2_seen))
 
+#ifdef CONFIG_MULTICAST
+static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im);
 #endif
+static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr);
+static void igmpv3_clear_delrec(struct in_device *in_dev);
+static int sf_setstate(struct ip_mc_list *pmc);
+static void sf_markstate(struct ip_mc_list *pmc);
+static void ip_mc_clear_src(struct ip_mc_list *pmc);
+int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
+	int sfcount, __u32 *psfsrc, int delta);
 
 static void ip_ma_put(struct ip_mc_list *im)
 {
@@ -160,6 +172,23 @@
 		atomic_inc(&im->refcnt);
 }
 
+static void igmp_gq_start_timer(struct in_device *in_dev)
+{
+	int tv = net_random() % in_dev->mr_maxdelay;
+
+	in_dev->mr_gq_running = 1;
+	if (!mod_timer(&in_dev->mr_gq_timer, jiffies+tv+2))
+		atomic_inc(&in_dev->refcnt);
+}
+
+static void igmp_ifc_start_timer(struct in_device *in_dev, int delay)
+{
+	int tv = net_random() % delay;
+
+	if (!mod_timer(&in_dev->mr_ifc_timer, jiffies+tv+2))
+		atomic_inc(&in_dev->refcnt);
+}
+
 static void igmp_mod_timer(struct ip_mc_list *im, int max_delay)
 {
 	spin_lock_bh(&im->lock);
@@ -184,20 +213,396 @@
 
 #define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)
 
-static int igmp_send_report(struct net_device *dev, u32 group, int type)
+
+static int is_in(struct ip_mc_list *pmc, struct ip_sf_list *psf, int type,
+	int gdeleted, int sdeleted)
+{
+	switch (type) {
+	case IGMPV3_MODE_IS_INCLUDE:
+	case IGMPV3_MODE_IS_EXCLUDE:
+		if (gdeleted || sdeleted)
+			return 0;
+		return !(pmc->gsquery && !psf->sf_gsresp);
+	case IGMPV3_CHANGE_TO_INCLUDE:
+		if (gdeleted || sdeleted)
+			return 0;
+		return psf->sf_count[MCAST_INCLUDE] != 0;
+	case IGMPV3_CHANGE_TO_EXCLUDE:
+		if (gdeleted || sdeleted)
+			return 0;
+		if (pmc->sfcount[MCAST_EXCLUDE] == 0 ||
+		    psf->sf_count[MCAST_INCLUDE])
+			return 0;
+		return pmc->sfcount[MCAST_EXCLUDE] ==
+			psf->sf_count[MCAST_EXCLUDE];
+	case IGMPV3_ALLOW_NEW_SOURCES:
+		if (gdeleted || !psf->sf_crcount)
+			return 0;
+		return (pmc->sfmode == MCAST_INCLUDE) ^ sdeleted;
+	case IGMPV3_BLOCK_OLD_SOURCES:
+		if (pmc->sfmode == MCAST_INCLUDE)
+			return gdeleted || (psf->sf_crcount && sdeleted);
+		return psf->sf_crcount && !gdeleted && !sdeleted;
+	}
+	return 0;
+}
+
+static int
+igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
+{
+	struct ip_sf_list *psf;
+	int scount = 0;
+
+	for (psf=pmc->sources; psf; psf=psf->sf_next) {
+		if (!is_in(pmc, psf, type, gdeleted, sdeleted))
+			continue;
+		scount++;
+	}
+	return scount;
+}
+
+static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
+{
+	struct sk_buff *skb;
+	struct rtable *rt;
+	struct iphdr *pip;
+	struct igmpv3_report *pig;
+
+	skb = alloc_skb(size + dev->hard_header_len + 15, GFP_ATOMIC);
+	if (skb == NULL)
+		return 0;
+
+	{
+		struct flowi fl = { .oif = dev->ifindex,
+				    .nl_u = { .ip4_u = {
+				    .daddr = IGMPV3_ALL_MCR } },
+				    .proto = IPPROTO_IGMP };
+		if (ip_route_output_key(&rt, &fl))
+			return 0;
+	}
+	if (rt->rt_src == 0) {
+		ip_rt_put(rt);
+		return 0;
+	}
+
+	skb->dst = &rt->u.dst;
+	skb->dev = dev;
+
+	skb_reserve(skb, (dev->hard_header_len+15)&~15);
+
+	skb->nh.iph = pip =(struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
+
+	pip->version  = 4;
+	pip->ihl      = (sizeof(struct iphdr)+4)>>2;
+	pip->tos      = 0xc0;
+	pip->frag_off = htons(IP_DF);
+	pip->ttl      = 1;
+	pip->daddr    = rt->rt_dst;
+	pip->saddr    = rt->rt_src;
+	pip->protocol = IPPROTO_IGMP;
+	pip->tot_len  = 0;	/* filled in later */
+	ip_select_ident(pip, &rt->u.dst, NULL);
+	((u8*)&pip[1])[0] = IPOPT_RA;
+	((u8*)&pip[1])[1] = 4;
+	((u8*)&pip[1])[2] = 0;
+	((u8*)&pip[1])[3] = 0;
+
+	pig =(struct igmpv3_report *)skb_put(skb, sizeof(*pig));
+	skb->h.igmph = (struct igmphdr *)pig;
+	pig->type = IGMPV3_HOST_MEMBERSHIP_REPORT;
+	pig->resv1 = 0;
+	pig->csum = 0;
+	pig->resv2 = 0;
+	pig->ngrec = 0;
+	return skb;
+}
+
+static int igmpv3_sendpack(struct sk_buff *skb)
+{
+	struct iphdr *pip = skb->nh.iph;
+	struct igmphdr *pig = skb->h.igmph;
+	int iplen, igmplen;
+
+	iplen = skb->tail - (unsigned char *)skb->nh.iph;
+	pip->tot_len = htons(iplen);
+	ip_send_check(pip);
+
+	igmplen = skb->tail - (unsigned char *)skb->h.igmph;
+	pig->csum = ip_compute_csum((void *)skb->h.igmph, igmplen);
+
+	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, skb->dev,
+		       dst_output);
+}
+
+static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
+{
+	return sizeof(struct igmpv3_grec) + 4*igmp_scount(pmc,type,gdel,sdel);
+}
+
+static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
+	int type, struct igmpv3_grec **ppgr)
+{
+	struct net_device *dev = pmc->interface->dev;
+	struct igmpv3_report *pih;
+	struct igmpv3_grec *pgr;
+
+	if (!skb)
+		skb = igmpv3_newpack(dev, dev->mtu);
+	if (!skb)
+		return 0;
+	pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec));
+	pgr->grec_type = type;
+	pgr->grec_auxwords = 0;
+	pgr->grec_nsrcs = 0;
+	pgr->grec_mca = pmc->multiaddr;
+	pih = (struct igmpv3_report *)skb->h.igmph;
+	pih->ngrec = htons(ntohs(pih->ngrec)+1);
+	*ppgr = pgr;
+	return skb;
+}
+
+#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \
+	skb_tailroom(skb)) : 0)
+
+static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
+	int type, int gdeleted, int sdeleted)
+{
+	struct net_device *dev = pmc->interface->dev;
+	struct igmpv3_report *pih;
+	struct igmpv3_grec *pgr = 0;
+	struct ip_sf_list *psf, *psf_next, *psf_prev, *psf_list;
+	int scount, first, isquery, truncate;
+
+	if (pmc->multiaddr == IGMP_ALL_HOSTS)
+		return skb;
+
+	isquery = type == IGMPV3_MODE_IS_INCLUDE ||
+		  type == IGMPV3_MODE_IS_EXCLUDE;
+	truncate = type == IGMPV3_MODE_IS_EXCLUDE ||
+		    type == IGMPV3_CHANGE_TO_EXCLUDE;
+
+	psf_list = sdeleted ? pmc->tomb : pmc->sources;
+
+	if (!psf_list) {
+		if (type == IGMPV3_ALLOW_NEW_SOURCES ||
+		    type == IGMPV3_BLOCK_OLD_SOURCES)
+			return skb;
+		if (pmc->crcount || isquery)
+			skb = add_grhead(skb, pmc, type, &pgr);
+		return skb;
+	}
+	pih = skb ? (struct igmpv3_report *)skb->h.igmph : 0;
+
+	/* EX and TO_EX get a fresh packet, if needed */
+	if (truncate) {
+		if (pih && pih->ngrec &&
+		    AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
+			if (skb)
+				igmpv3_sendpack(skb);
+			skb = igmpv3_newpack(dev, dev->mtu);
+		}
+	}
+	first = 1;
+	scount = 0;
+	psf_prev = 0;
+	for (psf=psf_list; psf; psf=psf_next) {
+		u32 *psrc;
+
+		psf_next = psf->sf_next;
+
+		if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
+			psf_prev = psf;
+			continue;
+		}
+
+		/* clear marks on query responses */
+		if (isquery)
+			psf->sf_gsresp = 0;
+
+		if (AVAILABLE(skb) < sizeof(u32) +
+		    first*sizeof(struct igmpv3_grec)) {
+			if (truncate && !first)
+				break;	 /* truncate these */
+			if (pgr)
+				pgr->grec_nsrcs = htons(scount);
+			if (skb)
+				igmpv3_sendpack(skb);
+			skb = igmpv3_newpack(dev, dev->mtu);
+			first = 1;
+			scount = 0;
+		}
+		if (first) {
+			skb = add_grhead(skb, pmc, type, &pgr);
+			first = 0;
+		}
+		psrc = (u32 *)skb_put(skb, sizeof(u32));
+		*psrc = psf->sf_inaddr;
+		scount++;
+		if ((type == IGMPV3_ALLOW_NEW_SOURCES ||
+		     type == IGMPV3_BLOCK_OLD_SOURCES) && psf->sf_crcount) {
+			psf->sf_crcount--;
+			if ((sdeleted || gdeleted) && psf->sf_crcount == 0) {
+				if (psf_prev)
+					psf_prev->sf_next = psf->sf_next;
+				else
+					pmc->tomb = psf->sf_next;
+				kfree(psf);
+				continue;
+			}
+		}
+		psf_prev = psf;
+	}
+	if (pgr)
+		pgr->grec_nsrcs = htons(scount);
+
+	if (isquery)
+		pmc->gsquery = 0;	/* clear query state on report */
+	return skb;
+}
+
+static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
+{
+	struct sk_buff *skb = 0;
+	int type;
+
+	if (!pmc) {
+		read_lock(&in_dev->lock);
+		for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
+			if (pmc->multiaddr == IGMP_ALL_HOSTS)
+				continue;
+			spin_lock_bh(&pmc->lock);
+			if (pmc->sfcount[MCAST_EXCLUDE])
+				type = IGMPV3_MODE_IS_EXCLUDE;
+			else
+				type = IGMPV3_MODE_IS_INCLUDE;
+			skb = add_grec(skb, pmc, type, 0, 0);
+			spin_unlock_bh(&pmc->lock);
+		}
+		read_unlock(&in_dev->lock);
+	} else {
+		spin_lock_bh(&pmc->lock);
+		if (pmc->sfcount[MCAST_EXCLUDE])
+			type = IGMPV3_MODE_IS_EXCLUDE;
+		else
+			type = IGMPV3_MODE_IS_INCLUDE;
+		skb = add_grec(skb, pmc, type, 0, 0);
+		spin_unlock_bh(&pmc->lock);
+	}
+	if (!skb)
+		return 0;
+	return igmpv3_sendpack(skb);
+}
+
+/*
+ * remove zero-count source records from a source filter list
+ */
+static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
+{
+	struct ip_sf_list *psf_prev, *psf_next, *psf;
+
+	psf_prev = 0;
+	for (psf=*ppsf; psf; psf = psf_next) {
+		psf_next = psf->sf_next;
+		if (psf->sf_crcount == 0) {
+			if (psf_prev)
+				psf_prev->sf_next = psf->sf_next;
+			else
+				*ppsf = psf->sf_next;
+			kfree(psf);
+		} else
+			psf_prev = psf;
+	}
+}
+
+static void igmpv3_send_cr(struct in_device *in_dev)
+{
+	struct ip_mc_list *pmc, *pmc_prev, *pmc_next;
+	struct sk_buff *skb = 0;
+	int type, dtype;
+
+	read_lock(&in_dev->lock);
+	write_lock_bh(&in_dev->mc_lock);
+
+	/* deleted MCA's */
+	pmc_prev = 0;
+	for (pmc=in_dev->mc_tomb; pmc; pmc=pmc_next) {
+		pmc_next = pmc->next;
+		if (pmc->sfmode == MCAST_INCLUDE) {
+			type = IGMPV3_BLOCK_OLD_SOURCES;
+			dtype = IGMPV3_BLOCK_OLD_SOURCES;
+			skb = add_grec(skb, pmc, type, 1, 0);
+			skb = add_grec(skb, pmc, dtype, 1, 1);
+		}
+		if (pmc->crcount) {
+			pmc->crcount--;
+			if (pmc->sfmode == MCAST_EXCLUDE) {
+				type = IGMPV3_CHANGE_TO_INCLUDE;
+				skb = add_grec(skb, pmc, type, 1, 0);
+			}
+			if (pmc->crcount == 0) {
+				igmpv3_clear_zeros(&pmc->tomb);
+				igmpv3_clear_zeros(&pmc->sources);
+			}
+		}
+		if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) {
+			if (pmc_prev)
+				pmc_prev->next = pmc_next;
+			else
+				in_dev->mc_tomb = pmc_next;
+			in_dev_put(pmc->interface);
+			kfree(pmc);
+		} else
+			pmc_prev = pmc;
+	}
+	write_unlock_bh(&in_dev->mc_lock);
+
+	/* change recs */
+	for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
+		spin_lock_bh(&pmc->lock);
+		if (pmc->sfcount[MCAST_EXCLUDE]) {
+			type = IGMPV3_BLOCK_OLD_SOURCES;
+			dtype = IGMPV3_ALLOW_NEW_SOURCES;
+		} else {
+			type = IGMPV3_ALLOW_NEW_SOURCES;
+			dtype = IGMPV3_BLOCK_OLD_SOURCES;
+		}
+		skb = add_grec(skb, pmc, type, 0, 0);
+		skb = add_grec(skb, pmc, dtype, 0, 1);	/* deleted sources */
+
+		/* filter mode changes */
+		if (pmc->crcount) {
+			pmc->crcount--;
+			if (pmc->sfmode == MCAST_EXCLUDE)
+				type = IGMPV3_CHANGE_TO_EXCLUDE;
+			else
+				type = IGMPV3_CHANGE_TO_INCLUDE;
+			skb = add_grec(skb, pmc, type, 0, 0);
+		}
+		spin_unlock_bh(&pmc->lock);
+	}
+	read_unlock(&in_dev->lock);
+	if (!skb)
+		return;
+	(void) igmpv3_sendpack(skb);
+}
+
+static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
+	int type)
 {
 	struct sk_buff *skb;
 	struct iphdr *iph;
 	struct igmphdr *ih;
 	struct rtable *rt;
+	struct net_device *dev = in_dev->dev;
+	u32	group = pmc ? pmc->multiaddr : 0;
 	u32	dst;
 
-	/* According to IGMPv2 specs, LEAVE messages are
-	 * sent to all-routers group.
-	 */
-	dst = group;
-	if (type == IGMP_HOST_LEAVE_MESSAGE)
+	if (type == IGMPV3_HOST_MEMBERSHIP_REPORT)
+		return igmpv3_send_report(in_dev, pmc);
+	else if (type == IGMP_HOST_LEAVE_MESSAGE)
 		dst = IGMP_ALL_ROUTER;
+	else
+		dst = group;
 
 	{
 		struct flowi fl = { .oif = dev->ifindex,
@@ -225,7 +630,7 @@
 
 	iph->version  = 4;
 	iph->ihl      = (sizeof(struct iphdr)+4)>>2;
-	iph->tos      = 0;
+	iph->tos      = 0xc0;
 	iph->frag_off = htons(IP_DF);
 	iph->ttl      = 1;
 	iph->daddr    = dst;
@@ -250,6 +655,34 @@
 		       dst_output);
 }
 
+static void igmp_gq_timer_expire(unsigned long data)
+{
+	struct in_device *in_dev = (struct in_device *)data;
+
+	in_dev->mr_gq_running = 0;
+	igmpv3_send_report(in_dev, 0);
+}
+
+static void igmp_ifc_timer_expire(unsigned long data)
+{
+	struct in_device *in_dev = (struct in_device *)data;
+
+	igmpv3_send_cr(in_dev);
+	if (in_dev->mr_ifc_count) {
+		in_dev->mr_ifc_count--;
+		igmp_ifc_start_timer(in_dev, IGMP_Unsolicited_Report_Interval);
+	}
+}
+
+static void igmp_ifc_event(struct in_device *in_dev)
+{
+	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev))
+		return;
+	in_dev->mr_ifc_count = in_dev->mr_qrv ? in_dev->mr_qrv : 
+		IGMP_Unsolicited_Report_Count;
+	igmp_ifc_start_timer(in_dev, 1);
+}
+
 
 static void igmp_timer_expire(unsigned long data)
 {
@@ -267,13 +700,33 @@
 	spin_unlock(&im->lock);
 
 	if (IGMP_V1_SEEN(in_dev))
-		igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
+		igmp_send_report(in_dev, im, IGMP_HOST_MEMBERSHIP_REPORT);
+	else if (IGMP_V2_SEEN(in_dev))
+		igmp_send_report(in_dev, im, IGMPV2_HOST_MEMBERSHIP_REPORT);
 	else
-		igmp_send_report(in_dev->dev, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
+		igmp_send_report(in_dev, im, IGMPV3_HOST_MEMBERSHIP_REPORT);
 
 	ip_ma_put(im);
 }
 
+static void igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __u32 *srcs)
+{
+	struct ip_sf_list *psf;
+	int i, scount;
+
+	scount = 0;
+	for (psf=pmc->sources; psf; psf=psf->sf_next) {
+		if (scount == nsrcs)
+			break;
+		for (i=0; i<nsrcs; i++)
+			if (srcs[i] == psf->sf_inaddr) {
+				psf->sf_gsresp = 1;
+				scount++;
+				break;
+			}
+	}
+}
+
 static void igmp_heard_report(struct in_device *in_dev, u32 group)
 {
 	struct ip_mc_list *im;
@@ -293,20 +746,46 @@
 	read_unlock(&in_dev->lock);
 }
 
-static void igmp_heard_query(struct in_device *in_dev, unsigned char max_resp_time,
-			     u32 group)
+static void igmp_heard_query(struct in_device *in_dev, struct igmphdr *ih,
+	int len)
 {
+	struct igmpv3_query *ih3 = (struct igmpv3_query *)ih;
 	struct ip_mc_list	*im;
+	u32			group = ih->group;
 	int			max_delay;
+	int			mark = 0;
 
-	max_delay = max_resp_time*(HZ/IGMP_TIMER_SCALE);
-
-	if (max_resp_time == 0) {
-		/* Alas, old v1 router presents here. */
 
-		max_delay = IGMP_Query_Response_Interval;
-		in_dev->mr_v1_seen = jiffies + IGMP_V1_Router_Present_Timeout;
-		group = 0;
+	if (len == 8) {
+		if (ih->code == 0) {
+			/* Alas, old v1 router presents here. */
+	
+			max_delay = IGMP_Query_Response_Interval;
+			in_dev->mr_v1_seen = jiffies +
+				IGMP_V1_Router_Present_Timeout;
+			group = 0;
+		} else {
+			/* v2 router present */
+			max_delay = ih->code*(HZ/IGMP_TIMER_SCALE);
+			in_dev->mr_v2_seen = jiffies +
+				IGMP_V2_Router_Present_Timeout;
+		}
+		igmpv3_clear_delrec(in_dev);
+	} else if (len < 12) {
+		return;	/* ignore bogus packet; freed by caller */
+	} else { /* v3 */
+		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+		in_dev->mr_maxdelay = max_delay;
+		if (ih3->qrv)
+			in_dev->mr_qrv = ih3->qrv;
+		if (!group) { /* general query */
+			if (ih3->nsrcs)
+				return;	/* no sources allowed */
+			igmp_gq_start_timer(in_dev);
+			return;
+		}
+		/* mark sources to include, if group & source-specific */
+		mark = ih3->nsrcs != 0;
 	}
 
 	/*
@@ -325,6 +804,14 @@
 			continue;
 		if (im->multiaddr == IGMP_ALL_HOSTS)
 			continue;
+		spin_lock_bh(&im->lock);
+		if (im->tm_running)
+			im->gsquery = im->gsquery && mark;
+		else
+			im->gsquery = mark;
+		if (im->gsquery)
+			igmp_marksources(im, ntohs(ih3->nsrcs), ih3->srcs);
+		spin_unlock_bh(&im->lock);
 		igmp_mod_timer(im, max_delay);
 	}
 	read_unlock(&in_dev->lock);
@@ -358,10 +845,11 @@
 
 	switch (ih->type) {
 	case IGMP_HOST_MEMBERSHIP_QUERY:
-		igmp_heard_query(in_dev, ih->code, ih->group);
+		igmp_heard_query(in_dev, ih, len);
 		break;
 	case IGMP_HOST_MEMBERSHIP_REPORT:
-	case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
+	case IGMPV2_HOST_MEMBERSHIP_REPORT:
+	case IGMPV3_HOST_MEMBERSHIP_REPORT:
 		/* Is it our report looped back? */
 		if (((struct rtable*)skb->dst)->fl.iif == 0)
 			break;
@@ -422,15 +910,105 @@
 		dev_mc_delete(dev,buf,dev->addr_len,0);
 }
 
+#ifdef CONFIG_IP_MULTICAST
+/*
+ * deleted ip_mc_list manipulation
+ */
+static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im)
+{
+	struct ip_mc_list *pmc;
+
+	/* this is an "ip_mc_list" for convenience; only the fields below
+	 * are actually used. In particular, the refcnt and users are not
+	 * used for management of the delete list. Using the same structure
+	 * for deleted items allows change reports to use common code with
+	 * non-deleted or query-response MCA's.
+	 */
+	pmc = (struct ip_mc_list *)kmalloc(sizeof(*pmc), GFP_KERNEL);
+	if (!pmc)
+		return;
+	memset(pmc, 0, sizeof(*pmc));
+	spin_lock_bh(&im->lock);
+	pmc->interface = im->interface;
+	in_dev_hold(in_dev);
+	pmc->multiaddr = im->multiaddr;
+	pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
+		IGMP_Unsolicited_Report_Count;
+	pmc->sfmode = im->sfmode;
+	if (pmc->sfmode == MCAST_INCLUDE) {
+		struct ip_sf_list *psf;
+
+		pmc->tomb = im->tomb;
+		pmc->sources = im->sources;
+		im->tomb = im->sources = 0;
+		for (psf=pmc->sources; psf; psf=psf->sf_next)
+			psf->sf_crcount = pmc->crcount;
+	}
+	spin_unlock_bh(&im->lock);
+
+	write_lock_bh(&in_dev->mc_lock);
+	pmc->next = in_dev->mc_tomb;
+	in_dev->mc_tomb = pmc;
+	write_unlock_bh(&in_dev->mc_lock);
+}
+#endif
+
+static void igmpv3_del_delrec(struct in_device *in_dev, __u32 multiaddr)
+{
+	struct ip_mc_list *pmc, *pmc_prev;
+	struct ip_sf_list *psf, *psf_next;
+
+	write_lock_bh(&in_dev->mc_lock);
+	pmc_prev = 0;
+	for (pmc=in_dev->mc_tomb; pmc; pmc=pmc->next) {
+		if (pmc->multiaddr == multiaddr)
+			break;
+		pmc_prev = pmc;
+	}
+	if (pmc) {
+		if (pmc_prev)
+			pmc_prev->next = pmc->next;
+		else
+			in_dev->mc_tomb = pmc->next;
+	}
+	write_unlock_bh(&in_dev->mc_lock);
+	if (pmc) {
+		for (psf=pmc->tomb; psf; psf=psf_next) {
+			psf_next = psf->sf_next;
+			kfree(psf);
+		}
+		in_dev_put(pmc->interface);
+		kfree(pmc);
+	}
+}
+
+static void igmpv3_clear_delrec(struct in_device *in_dev)
+{
+	struct ip_mc_list *pmc, *nextpmc;
+
+	write_lock_bh(&in_dev->mc_lock);
+	pmc = in_dev->mc_tomb;
+	in_dev->mc_tomb = 0;
+	write_unlock_bh(&in_dev->mc_lock);
+
+	for (; pmc; pmc = nextpmc) {
+		nextpmc = pmc->next;
+		ip_mc_clear_src(pmc);
+		in_dev_put(pmc->interface);
+		kfree(pmc);
+	}
+}
+
 static void igmp_group_dropped(struct ip_mc_list *im)
 {
+	struct in_device *in_dev = im->interface;
 #ifdef CONFIG_IP_MULTICAST
 	int reporter;
 #endif
 
 	if (im->loaded) {
 		im->loaded = 0;
-		ip_mc_filter_del(im->interface, im->multiaddr);
+		ip_mc_filter_del(in_dev, im->multiaddr);
 	}
 
 #ifdef CONFIG_IP_MULTICAST
@@ -440,25 +1018,46 @@
 	reporter = im->reporter;
 	igmp_stop_timer(im);
 
-	if (reporter && !IGMP_V1_SEEN(im->interface))
-		igmp_send_report(im->interface->dev, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
+	if (IGMP_V1_SEEN(in_dev))
+		goto done;
+	if (IGMP_V2_SEEN(in_dev)) {
+		if (reporter)
+			igmp_send_report(in_dev, im, IGMP_HOST_LEAVE_MESSAGE);
+		goto done;
+	}
+	/* IGMPv3 */
+	igmpv3_add_delrec(in_dev, im);
+
+	igmp_ifc_event(in_dev);
+done:
+	ip_mc_clear_src(im);
 #endif
 }
 
 static void igmp_group_added(struct ip_mc_list *im)
 {
+	struct in_device *in_dev = im->interface;
+
 	if (im->loaded == 0) {
 		im->loaded = 1;
-		ip_mc_filter_add(im->interface, im->multiaddr);
+		ip_mc_filter_add(in_dev, im->multiaddr);
 	}
 
 #ifdef CONFIG_IP_MULTICAST
 	if (im->multiaddr == IGMP_ALL_HOSTS)
 		return;
 
-	spin_lock_bh(&im->lock);
-	igmp_start_timer(im, IGMP_Initial_Report_Delay);
-	spin_unlock_bh(&im->lock);
+	if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) {
+		spin_lock_bh(&im->lock);
+		igmp_start_timer(im, IGMP_Initial_Report_Delay);
+		spin_unlock_bh(&im->lock);
+		return;
+	}
+	/* else, v3 */
+
+	im->crcount = in_dev->mr_qrv ? in_dev->mr_qrv :
+		IGMP_Unsolicited_Report_Count;
+	igmp_ifc_event(in_dev);
 #endif
 }
 
@@ -481,6 +1080,7 @@
 	for (im=in_dev->mc_list; im; im=im->next) {
 		if (im->multiaddr == addr) {
 			im->users++;
+			ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, 0, 0);
 			goto out;
 		}
 	}
@@ -493,6 +1093,13 @@
 	im->interface=in_dev;
 	in_dev_hold(in_dev);
 	im->multiaddr=addr;
+	/* initial mode is (EX, empty) */
+	im->sfmode = MCAST_EXCLUDE;
+	im->sfcount[MCAST_INCLUDE] = 0;
+	im->sfcount[MCAST_EXCLUDE] = 1;
+	im->sources = 0;
+	im->tomb = 0;
+	im->crcount = 0;
 	atomic_set(&im->refcnt, 1);
 	spin_lock_init(&im->lock);
 #ifdef  CONFIG_IP_MULTICAST
@@ -502,12 +1109,14 @@
 	im->timer.function=&igmp_timer_expire;
 	im->unsolicit_count = IGMP_Unsolicited_Report_Count;
 	im->reporter = 0;
+	im->gsquery = 0;
 #endif
 	im->loaded = 0;
 	write_lock_bh(&in_dev->lock);
 	im->next=in_dev->mc_list;
 	in_dev->mc_list=im;
 	write_unlock_bh(&in_dev->lock);
+	igmpv3_del_delrec(in_dev, im->multiaddr);
 	igmp_group_added(im);
 	if (in_dev->dev->flags & IFF_UP)
 		ip_rt_multicast_event(in_dev);
@@ -552,9 +1161,18 @@
 
 	ASSERT_RTNL();
 
+	in_dev->mr_ifc_count = 0;
+	if (del_timer(&in_dev->mr_ifc_timer))
+		atomic_dec(&in_dev->refcnt);
+	in_dev->mr_gq_running = 0;
+	if (del_timer(&in_dev->mr_gq_timer))
+		atomic_dec(&in_dev->refcnt);
+
 	for (i=in_dev->mc_list; i; i=i->next)
 		igmp_group_dropped(i);
 
+	igmpv3_clear_delrec(in_dev);
+
 	ip_mc_dec_group(in_dev, IGMP_ALL_HOSTS);
 }
 
@@ -566,6 +1184,20 @@
 
 	ASSERT_RTNL();
 
+#ifdef CONFIG_IP_MULTICAST
+	in_dev->mc_lock = RW_LOCK_UNLOCKED;
+	in_dev->mr_gq_running = 0;
+	init_timer(&in_dev->mr_gq_timer);
+	in_dev->mr_gq_timer.data=(unsigned long) in_dev;
+	in_dev->mr_gq_timer.function=&igmp_gq_timer_expire;
+	in_dev->mc_tomb = 0;
+	in_dev->mr_ifc_count = 0;
+	init_timer(&in_dev->mr_ifc_timer);
+	in_dev->mr_ifc_timer.data=(unsigned long) in_dev;
+	in_dev->mr_ifc_timer.function=&igmp_ifc_timer_expire;
+	in_dev->mr_qrv = IGMP_Unsolicited_Report_Count;
+#endif
+
 	ip_mc_inc_group(in_dev, IGMP_ALL_HOSTS);
 
 	for (i=in_dev->mc_list; i; i=i->next)
@@ -626,6 +1258,262 @@
  */
 int sysctl_igmp_max_memberships = IP_MAX_MEMBERSHIPS;
 
+
+static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
+	__u32 *psfsrc)
+{
+	struct ip_sf_list *psf, *psf_prev;
+	int rv = 0;
+
+	psf_prev = 0;
+	for (psf=pmc->sources; psf; psf=psf->sf_next) {
+		if (psf->sf_inaddr == *psfsrc)
+			break;
+		psf_prev = psf;
+	}
+	if (!psf || psf->sf_count[sfmode] == 0) {
+		/* source filter not found, or count wrong =>  bug */
+		return -ESRCH;
+	}
+	psf->sf_count[sfmode]--;
+	if (psf->sf_count[sfmode] == 0) {
+		ip_rt_multicast_event(pmc->interface);
+	}
+	if (!psf->sf_count[MCAST_INCLUDE] && !psf->sf_count[MCAST_EXCLUDE]) {
+		struct in_device *in_dev = pmc->interface;
+
+		/* no more filters for this source */
+		if (psf_prev)
+			psf_prev->sf_next = psf->sf_next;
+		else
+			pmc->sources = psf->sf_next;
+		if (psf->sf_oldin &&
+		    !IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) {
+			psf->sf_crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 
+				IGMP_Unsolicited_Report_Count;
+			psf->sf_next = pmc->tomb;
+			pmc->tomb = psf;
+			rv = 1;
+		} else
+			kfree(psf);
+	}
+	return rv;
+}
+
+#ifndef CONFIG_IP_MULTICAST
+#define igmp_ifc_event(x)	do { } while (0)
+#endif
+
+int ip_mc_del_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
+	int sfcount, __u32 *psfsrc, int delta)
+{
+	struct ip_mc_list *pmc;
+	int	changerec = 0;
+	int	i, err;
+
+	if (!in_dev)
+		return -ENODEV;
+	read_lock(&in_dev->lock);
+	for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
+		if (*pmca == pmc->multiaddr)
+			break;
+	}
+	if (!pmc) {
+		/* MCA not found?? bug */
+		read_unlock(&in_dev->lock);
+		return -ESRCH;
+	}
+	spin_lock_bh(&pmc->lock);
+	read_unlock(&in_dev->lock);
+	sf_markstate(pmc);
+	if (!delta) {
+		if (!pmc->sfcount[sfmode])
+			return -EINVAL;
+		pmc->sfcount[sfmode]--;
+	}
+	err = 0;
+	for (i=0; i<sfcount; i++) {
+		int rv = ip_mc_del1_src(pmc, sfmode, &psfsrc[i]);
+
+		changerec |= rv > 0;
+		if (!err && rv < 0)
+			err = rv;
+	}
+	if (pmc->sfmode == MCAST_EXCLUDE &&
+	    pmc->sfcount[MCAST_EXCLUDE] == 0 &&
+	    pmc->sfcount[MCAST_INCLUDE]) {
+		struct ip_sf_list *psf;
+
+		/* filter mode change */
+		pmc->sfmode = MCAST_INCLUDE;
+		pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 
+			IGMP_Unsolicited_Report_Count;
+		in_dev->mr_ifc_count = pmc->crcount;
+		for (psf=pmc->sources; psf; psf = psf->sf_next)
+			psf->sf_crcount = 0;
+		igmp_ifc_event(pmc->interface);
+	} else if (sf_setstate(pmc) || changerec) {
+		igmp_ifc_event(pmc->interface);
+	}
+	spin_unlock_bh(&pmc->lock);
+	return err;
+}
+
+/*
+ * Add multicast single-source filter to the interface list
+ */
+static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
+	__u32 *psfsrc, int delta)
+{
+	struct ip_sf_list *psf, *psf_prev;
+
+	psf_prev = 0;
+	for (psf=pmc->sources; psf; psf=psf->sf_next) {
+		if (psf->sf_inaddr == *psfsrc)
+			break;
+		psf_prev = psf;
+	}
+	if (!psf) {
+		psf = (struct ip_sf_list *)kmalloc(sizeof(*psf), GFP_ATOMIC);
+		if (!psf)
+			return -ENOBUFS;
+		memset(psf, 0, sizeof(*psf));
+		psf->sf_inaddr = *psfsrc;
+		if (psf_prev) {
+			psf_prev->sf_next = psf;
+		} else
+			pmc->sources = psf;
+	}
+	psf->sf_count[sfmode]++;
+	if (psf->sf_count[sfmode] == 1) {
+		ip_rt_multicast_event(pmc->interface);
+	}
+	return 0;
+}
+
+static void sf_markstate(struct ip_mc_list *pmc)
+{
+	struct ip_sf_list *psf;
+	int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
+
+	for (psf=pmc->sources; psf; psf=psf->sf_next)
+		if (pmc->sfcount[MCAST_EXCLUDE]) {
+			psf->sf_oldin = mca_xcount ==
+				psf->sf_count[MCAST_EXCLUDE] &&
+				!psf->sf_count[MCAST_INCLUDE];
+		} else
+			psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0;
+}
+
+static int sf_setstate(struct ip_mc_list *pmc)
+{
+	struct ip_sf_list *psf;
+	int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
+	int qrv = pmc->interface->mr_qrv;
+	int new_in, rv;
+
+	rv = 0;
+	for (psf=pmc->sources; psf; psf=psf->sf_next) {
+		if (pmc->sfcount[MCAST_EXCLUDE]) {
+			new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] &&
+				!psf->sf_count[MCAST_INCLUDE];
+		} else
+			new_in = psf->sf_count[MCAST_INCLUDE] != 0;
+		if (new_in != psf->sf_oldin) {
+			psf->sf_crcount = qrv;
+			rv++;
+		}
+	}
+	return rv;
+}
+
+/*
+ * Add multicast source filter list to the interface list
+ */
+int ip_mc_add_src(struct in_device *in_dev, __u32 *pmca, int sfmode,
+	int sfcount, __u32 *psfsrc, int delta)
+{
+	struct ip_mc_list *pmc;
+	int	isexclude;
+	int	i, err;
+
+	if (!in_dev)
+		return -ENODEV;
+	read_lock(&in_dev->lock);
+	for (pmc=in_dev->mc_list; pmc; pmc=pmc->next) {
+		if (*pmca == pmc->multiaddr)
+			break;
+	}
+	if (!pmc) {
+		/* MCA not found?? bug */
+		read_unlock(&in_dev->lock);
+		return -ESRCH;
+	}
+	spin_lock_bh(&pmc->lock);
+	read_unlock(&in_dev->lock);
+
+	sf_markstate(pmc);
+	isexclude = pmc->sfmode == MCAST_EXCLUDE;
+	if (!delta)
+		pmc->sfcount[sfmode]++;
+	err = 0;
+	for (i=0; i<sfcount; i++) {
+		err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i], delta);
+		if (err)
+			break;
+	}
+	if (err) {
+		int j;
+
+		pmc->sfcount[sfmode]--;
+		for (j=0; j<i; j++)
+			(void) ip_mc_del1_src(pmc, sfmode, &psfsrc[i]);
+	} else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
+		struct in_device *in_dev = pmc->interface;
+		struct ip_sf_list *psf;
+
+		/* filter mode change */
+		if (pmc->sfcount[MCAST_EXCLUDE])
+			pmc->sfmode = MCAST_EXCLUDE;
+		else if (pmc->sfcount[MCAST_INCLUDE])
+			pmc->sfmode = MCAST_INCLUDE;
+		/* else no filters; keep old mode for reports */
+
+		pmc->crcount = in_dev->mr_qrv ? in_dev->mr_qrv : 
+			IGMP_Unsolicited_Report_Count;
+		in_dev->mr_ifc_count = pmc->crcount;
+		for (psf=pmc->sources; psf; psf = psf->sf_next)
+			psf->sf_crcount = 0;
+		igmp_ifc_event(in_dev);
+	} else if (sf_setstate(pmc))
+		igmp_ifc_event(in_dev);
+	spin_unlock_bh(&pmc->lock);
+	return err;
+}
+
+static void ip_mc_clear_src(struct ip_mc_list *pmc)
+{
+	struct ip_sf_list *psf, *nextpsf;
+
+	for (psf=pmc->tomb; psf; psf=nextpsf) {
+		nextpsf = psf->sf_next;
+		kfree(psf);
+	}
+	pmc->tomb = 0;
+	for (psf=pmc->sources; psf; psf=nextpsf) {
+		nextpsf = psf->sf_next;
+		kfree(psf);
+	}
+	pmc->sources = 0;
+	pmc->sfmode = MCAST_EXCLUDE;
+	pmc->sfcount[MCAST_EXCLUDE] = 0;
+	pmc->sfcount[MCAST_EXCLUDE] = 1;
+}
+
+
+/*
+ * Join a multicast group
+ */
 int ip_mc_join_group(struct sock *sk , struct ip_mreqn *imr)
 {
 	int err;
@@ -674,6 +1562,8 @@
 	memcpy(&iml->multi, imr, sizeof(*imr));
 	iml->next = inet->mc_list;
 	iml->count = 1;
+	iml->sflist = NULL;
+	iml->sfmode = MCAST_EXCLUDE;
 	inet->mc_list = iml;
 	ip_mc_inc_group(in_dev, addr);
 	iml = NULL;
@@ -686,6 +1576,24 @@
 	return err;
 }
 
+int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
+	struct in_device *in_dev)
+{
+	int err;
+
+	if (iml->sflist == 0) {
+		/* any-source empty exclude case */
+		return ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
+			iml->sfmode, 0, 0, 0);
+	}
+	err = ip_mc_del_src(in_dev, &iml->multi.imr_multiaddr.s_addr,
+			iml->sfmode, iml->sflist->sl_count,
+			iml->sflist->sl_addr, 0);
+	sock_kfree_s(sk, iml->sflist, IP_SFLSIZE(iml->sflist->sl_max));
+	iml->sflist = 0;
+	return err;
+}
+
 /*
  *	Ask a socket to leave a group.
  */
@@ -701,14 +1609,19 @@
 		    iml->multi.imr_address.s_addr==imr->imr_address.s_addr &&
 		    (!imr->imr_ifindex || iml->multi.imr_ifindex==imr->imr_ifindex)) {
 			struct in_device *in_dev;
+
+			in_dev = inetdev_by_index(iml->multi.imr_ifindex);
+			if (in_dev)
+				(void) ip_mc_leave_src(sk, iml, in_dev);
 			if (--iml->count) {
 				rtnl_unlock();
+				if (in_dev)
+					in_dev_put(in_dev);
 				return 0;
 			}
 
 			*imlp = iml->next;
 
-			in_dev = inetdev_by_index(iml->multi.imr_ifindex);
 			if (in_dev) {
 				ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr);
 				in_dev_put(in_dev);
@@ -722,6 +1635,283 @@
 	return -EADDRNOTAVAIL;
 }
 
+int ip_mc_source(int add, int omode, struct sock *sk, struct
+	ip_mreq_source *mreqs)
+{
+	int err;
+	struct ip_mreqn	imr;
+	u32 addr = mreqs->imr_multiaddr;
+	struct ip_mc_socklist *pmc;
+	struct in_device *in_dev;
+	struct inet_opt *inet = inet_sk(sk);
+	struct ip_sf_socklist *psl;
+	int i, j, rv;
+
+	if (!MULTICAST(addr))
+		return -EINVAL;
+
+	rtnl_shlock();
+
+	imr.imr_multiaddr.s_addr = mreqs->imr_multiaddr;
+	imr.imr_address.s_addr = mreqs->imr_interface;
+	imr.imr_ifindex = 0;
+	in_dev = ip_mc_find_dev(&imr);
+
+	if (!in_dev) {
+		err = -ENODEV;
+		goto done;
+	}
+	err = -EADDRNOTAVAIL;
+
+	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
+		if (memcmp(&pmc->multi, mreqs, 2*sizeof(__u32)) == 0)
+			break;
+	}
+	if (!pmc)		/* must have a prior join */
+		goto done;
+	/* if a source filter was set, must be the same mode as before */
+	if (pmc->sflist) {
+		if (pmc->sfmode != omode)
+			goto done;
+	} else if (pmc->sfmode != omode) {
+		/* allow mode switches for empty-set filters */
+		ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, 
+			0, 0);
+		pmc->sfmode = omode;
+		ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, pmc->sfmode, 0, 
+			0, 0);
+	}
+
+	psl = pmc->sflist;
+	if (!add) {
+		if (!psl)
+			goto done;
+		rv = !0;
+		for (i=0; i<psl->sl_count; i++) {
+			rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+				sizeof(__u32));
+			if (rv >= 0)
+				break;
+		}
+		if (!rv)	/* source not found */
+			goto done;
+
+		/* update the interface filter */
+		ip_mc_del_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 
+			&mreqs->imr_sourceaddr, 1);
+
+		for (j=i+1; j<psl->sl_count; j++)
+			psl->sl_addr[j-1] = psl->sl_addr[j];
+		psl->sl_count--;
+		err = 0;
+		goto done;
+	}
+	/* else, add a new source to the filter */
+
+	if (!psl || psl->sl_count == psl->sl_max) {
+		struct ip_sf_socklist *newpsl;
+		int count = IP_SFBLOCK;
+
+		if (psl)
+			count += psl->sl_max;
+		newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk,
+			IP_SFLSIZE(count), GFP_KERNEL);
+		if (!newpsl) {
+			err = -ENOBUFS;
+			goto done;
+		}
+		newpsl->sl_max = count;
+		newpsl->sl_count = count - IP_SFBLOCK;
+		if (psl) {
+			for (i=0; i<psl->sl_count; i++)
+				newpsl->sl_addr[i] = psl->sl_addr[i];
+			sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max));
+		}
+		pmc->sflist = psl = newpsl;
+	}
+	rv = 1;	/* > 0 for insert logic below if sl_count is 0 */
+	for (i=0; i<psl->sl_count; i++) {
+		rv = memcmp(&psl->sl_addr, &mreqs->imr_multiaddr,
+			sizeof(__u32));
+		if (rv >= 0)
+			break;
+	}
+	if (rv == 0)		/* address already there is an error */
+		goto done;
+	for (j=psl->sl_count-1; j>=i; j--)
+		psl->sl_addr[j+1] = psl->sl_addr[j];
+	psl->sl_addr[i] = mreqs->imr_sourceaddr;
+	psl->sl_count++;
+	err = 0;
+	/* update the interface list */
+	ip_mc_add_src(in_dev, &mreqs->imr_multiaddr, omode, 1, 
+		&mreqs->imr_sourceaddr, 1);
+done:
+	rtnl_shunlock();
+	return err;
+}
+
+int ip_mc_msfilter(struct sock *sk, struct ip_msfilter *msf)
+{
+	int err;
+	struct ip_mreqn	imr;
+	u32 addr = msf->imsf_multiaddr;
+	struct ip_mc_socklist *pmc;
+	struct in_device *in_dev;
+	struct inet_opt *inet = inet_sk(sk);
+	struct ip_sf_socklist *newpsl, *psl;
+
+	if (!MULTICAST(addr))
+		return -EINVAL;
+	if (msf->imsf_fmode != MCAST_INCLUDE &&
+	    msf->imsf_fmode != MCAST_EXCLUDE)
+		return -EINVAL;
+
+	rtnl_shlock();
+
+	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
+	imr.imr_address.s_addr = msf->imsf_interface;
+	imr.imr_ifindex = 0;
+	in_dev = ip_mc_find_dev(&imr);
+
+	if (!in_dev) {
+		err = -ENODEV;
+		goto done;
+	}
+	err = -EADDRNOTAVAIL;
+
+	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
+		if (memcmp(&pmc->multi, &imr, sizeof(imr)) == 0)
+			break;
+	}
+	if (!pmc)		/* must have a prior join */
+		goto done;
+	if (msf->imsf_numsrc) {
+		newpsl = (struct ip_sf_socklist *)sock_kmalloc(sk,
+				IP_SFLSIZE(msf->imsf_numsrc), GFP_KERNEL);
+		if (!newpsl) {
+			err = -ENOBUFS;
+			goto done;
+		}
+		newpsl->sl_max = newpsl->sl_count = msf->imsf_numsrc;
+		memcpy(newpsl->sl_addr, msf->imsf_slist,
+			msf->imsf_numsrc * sizeof(msf->imsf_slist[0]));
+		err = ip_mc_add_src(in_dev, &msf->imsf_multiaddr,
+			msf->imsf_fmode, newpsl->sl_count, newpsl->sl_addr, 0);
+		if (err) {
+			sock_kfree_s(sk, newpsl, IP_SFLSIZE(newpsl->sl_max));
+			goto done;
+		}
+	} else
+		newpsl = 0;
+	psl = pmc->sflist;
+	if (psl) {
+		(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
+			psl->sl_count, psl->sl_addr, 0);
+		sock_kfree_s(sk, psl, IP_SFLSIZE(psl->sl_max));
+	} else
+		(void) ip_mc_del_src(in_dev, &msf->imsf_multiaddr, pmc->sfmode,
+			0, 0, 0);
+	pmc->sflist = newpsl;
+	pmc->sfmode = msf->imsf_fmode;
+done:
+	rtnl_shunlock();
+	return err;
+}
+
+int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf,
+	struct ip_msfilter *optval, int *optlen)
+{
+	int err, len, count, copycount;
+	struct ip_mreqn	imr;
+	u32 addr = msf->imsf_multiaddr;
+	struct ip_mc_socklist *pmc;
+	struct in_device *in_dev;
+	struct inet_opt *inet = inet_sk(sk);
+	struct ip_sf_socklist *psl;
+
+	if (!MULTICAST(addr))
+		return -EINVAL;
+	if (msf->imsf_fmode != MCAST_INCLUDE &&
+	    msf->imsf_fmode != MCAST_EXCLUDE)
+		return -EINVAL;
+
+	rtnl_shlock();
+
+	imr.imr_multiaddr.s_addr = msf->imsf_multiaddr;
+	imr.imr_address.s_addr = msf->imsf_interface;
+	imr.imr_ifindex = 0;
+	in_dev = ip_mc_find_dev(&imr);
+
+	if (!in_dev) {
+		err = -ENODEV;
+		goto done;
+	}
+	err = -EADDRNOTAVAIL;
+
+	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
+		if (memcmp(&pmc->multi, &imr, sizeof(imr)) == 0)
+			break;
+	}
+	if (!pmc)		/* must have a prior join */
+		goto done;
+	msf->imsf_fmode = pmc->sfmode;
+	psl = pmc->sflist;
+	rtnl_shunlock();
+	if (!psl) {
+		len = 0;
+		count = 0;
+	} else {
+		count = psl->sl_count;
+	}
+	copycount = count < msf->imsf_numsrc ? count : msf->imsf_numsrc;
+	len = copycount * sizeof(psl->sl_addr[0]);
+	msf->imsf_numsrc = count;
+	if (put_user(IP_MSFILTER_SIZE(copycount), optlen) ||
+	    copy_to_user((void *)optval, msf, IP_MSFILTER_SIZE(0))) {
+		return -EFAULT;
+	}
+	if (len &&
+	    copy_to_user((void *)&optval->imsf_slist[0], psl->sl_addr, len))
+		return -EFAULT;
+	return 0;
+done:
+	rtnl_shunlock();
+	return err;
+}
+
+/*
+ * check if a multicast source filter allows delivery for a given <src,dst,intf>
+ */
+int ip_mc_sf_allow(struct sock *sk, u32 loc_addr, u32 rmt_addr, int dif)
+{
+	struct inet_opt *inet = inet_sk(sk);
+	struct ip_mc_socklist *pmc;
+	struct ip_sf_socklist *psl;
+	int i;
+
+	for (pmc=inet->mc_list; pmc; pmc=pmc->next) {
+		if (pmc->multi.imr_multiaddr.s_addr == loc_addr &&
+		    pmc->multi.imr_ifindex == dif)
+			break;
+	}
+	if (!pmc)
+		return 0;
+	psl = pmc->sflist;
+	if (!psl)
+		return pmc->sfmode == MCAST_EXCLUDE;
+
+	for (i=0; i<psl->sl_count; i++) {
+		if (psl->sl_addr[i] == rmt_addr)
+			break;
+	}
+	if (pmc->sfmode == MCAST_INCLUDE && i < psl->sl_count)
+		return 1;
+	if (pmc->sfmode == MCAST_EXCLUDE && i >= psl->sl_count)
+		return 1;
+	return 0;
+}
+
 /*
  *	A socket is closing.
  */
@@ -740,6 +1930,7 @@
 		inet->mc_list = iml->next;
 
 		if ((in_dev = inetdev_by_index(iml->multi.imr_ifindex)) != NULL) {
+			(void) ip_mc_leave_src(sk, iml, in_dev);
 			ip_mc_dec_group(in_dev, iml->multi.imr_multiaddr.s_addr);
 			in_dev_put(in_dev);
 		}
@@ -749,19 +1940,33 @@
 	rtnl_unlock();
 }
 
-int ip_check_mc(struct in_device *in_dev, u32 mc_addr)
+int ip_check_mc(struct in_device *in_dev, u32 mc_addr, u32 src_addr, u16 proto)
 {
 	struct ip_mc_list *im;
+	struct ip_sf_list *psf;
+	int rv = 0;
 
 	read_lock(&in_dev->lock);
 	for (im=in_dev->mc_list; im; im=im->next) {
-		if (im->multiaddr == mc_addr) {
-			read_unlock(&in_dev->lock);
-			return 1;
+		if (im->multiaddr == mc_addr)
+			break;
+	}
+	if (im && proto == IPPROTO_IGMP) {
+		rv = 1;
+	} else if (im) {
+		for (psf=im->sources; psf; psf=psf->sf_next) {
+			if (psf->sf_inaddr == src_addr)
+				break;
 		}
+		if (psf)
+			rv = psf->sf_count[MCAST_INCLUDE] ||
+				psf->sf_count[MCAST_EXCLUDE] !=
+				im->sfcount[MCAST_EXCLUDE];
+		else
+			rv = im->sfcount[MCAST_EXCLUDE] != 0;
 	}
 	read_unlock(&in_dev->lock);
-	return 0;
+	return rv;
 }
 
 
@@ -822,5 +2027,101 @@
 		len=0;
 	return len;
 }
+
+int ip_mcf_procinfo(char *buffer, char **start, off_t offset, int length)
+{
+	off_t pos=0, begin=0;
+	int len=0;
+	int first = 1;
+	struct net_device *dev;
+
+	read_lock(&dev_base_lock);
+	for(dev=dev_base; dev; dev=dev->next) {
+		struct in_device *in_dev = in_dev_get(dev);
+		struct ip_mc_list *imc;
+
+		if (in_dev == NULL)
+			continue;
+
+		read_lock(&in_dev->lock);
+
+		for (imc=in_dev->mc_list; imc; imc=imc->next) {
+			struct ip_sf_list *psf;
+			unsigned long icount, xcount;
+
+			spin_lock_bh(&imc->lock);
+			icount = imc->sfcount[MCAST_INCLUDE];
+			xcount = imc->sfcount[MCAST_EXCLUDE];
+			for (psf=imc->sources; psf; psf=psf->sf_next) {
+				if (first) {
+					len += sprintf(buffer+len, "%3s %6s "
+						"%10s %10s %6s %6s\n", "Idx",
+						"Device", "MCA", "SRC", "INC",
+						"EXC");
+					first = 0;
+				}
+				len += sprintf(buffer+len, "%3d %6.6s 0x%08x "
+					"0x%08x %6lu %6lu\n", dev->ifindex,
+					dev->name, ntohl(imc->multiaddr),
+					ntohl(psf->sf_inaddr),
+					psf->sf_count[MCAST_INCLUDE],
+					psf->sf_count[MCAST_EXCLUDE]);
+				pos=begin+len;
+				if(pos<offset)
+				{
+					len=0;
+					begin=pos;
+				}
+				if(pos>offset+length) {
+					spin_unlock_bh(&imc->lock);
+					read_unlock(&in_dev->lock);
+					in_dev_put(in_dev);
+					goto done;
+				}
+				icount -= psf->sf_count[MCAST_INCLUDE];
+				xcount -= psf->sf_count[MCAST_EXCLUDE];
+			}
+			if (icount > 0 || xcount > 0) {
+				if (first) {
+					len += sprintf(buffer+len, "%3s %6s "
+						"%10s %10s %6s %6s\n", "Idx",
+						"Device", "MCA", "SRC", "INC",
+						"EXC");
+					first = 0;
+				}
+				len += sprintf(buffer+len, "%3d %6.6s 0x%08x "
+					"%10s %6lu %6lu\n", dev->ifindex,
+					dev->name, ntohl(imc->multiaddr),
+					"NONE", icount, xcount);
+				pos=begin+len;
+				if(pos<offset)
+				{
+					len=0;
+					begin=pos;
+				}
+				if(pos>offset+length) {
+					spin_unlock_bh(&imc->lock);
+					read_unlock(&in_dev->lock);
+					in_dev_put(in_dev);
+					goto done;
+				}
+			}
+			spin_unlock_bh(&imc->lock);
+		}
+		read_unlock(&in_dev->lock);
+		in_dev_put(in_dev);
+	}
+done:
+	read_unlock(&dev_base_lock);
+
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;
+	if(len<0)
+		len=0;
+	return len;
+}
+
 #endif
 
diff -urN linux-2.5.67-bk3/net/ipv4/ip_output.c linux-2.5.67-bk4/net/ipv4/ip_output.c
--- linux-2.5.67-bk3/net/ipv4/ip_output.c	2003-04-07 10:32:54.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/ip_output.c	2003-04-12 04:33:22.000000000 -0700
@@ -1312,5 +1312,6 @@
 
 #ifdef CONFIG_IP_MULTICAST
 	proc_net_create("igmp", 0, ip_mc_procinfo);
+	proc_net_create("mcfilter", 0, ip_mcf_procinfo);
 #endif
 }
diff -urN linux-2.5.67-bk3/net/ipv4/ip_sockglue.c linux-2.5.67-bk4/net/ipv4/ip_sockglue.c
--- linux-2.5.67-bk3/net/ipv4/ip_sockglue.c	2003-04-07 10:30:59.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/ip_sockglue.c	2003-04-12 04:33:22.000000000 -0700
@@ -610,9 +610,67 @@
 			}
 
 			if (optname == IP_ADD_MEMBERSHIP)
-				err = ip_mc_join_group(sk,&mreq);
+				err = ip_mc_join_group(sk, &mreq);
 			else
-				err = ip_mc_leave_group(sk,&mreq);
+				err = ip_mc_leave_group(sk, &mreq);
+			break;
+		}
+		case IP_MSFILTER:
+		{
+			struct ip_msfilter *msf;
+
+			if (optlen < IP_MSFILTER_SIZE(0))
+				goto e_inval;
+			msf = (struct ip_msfilter *)kmalloc(optlen, GFP_KERNEL);
+			if (msf == 0) {
+				err = -ENOBUFS;
+				break;
+			}
+			err = -EFAULT;
+			if (copy_from_user(msf, optval, optlen)) {
+				kfree(msf);
+				break;
+			}
+			err = ip_mc_msfilter(sk, msf);
+			kfree(msf);
+			break;
+		}
+		case IP_BLOCK_SOURCE:
+		case IP_UNBLOCK_SOURCE:
+		case IP_ADD_SOURCE_MEMBERSHIP:
+		case IP_DROP_SOURCE_MEMBERSHIP:
+		{
+			struct ip_mreq_source mreqs;
+			int omode, add;
+
+			if (optlen != sizeof(struct ip_mreq_source))
+				goto e_inval;
+			if (copy_from_user(&mreqs, optval, sizeof(mreqs))) {
+				err = -EFAULT;
+				break;
+			}
+			if (optname == IP_BLOCK_SOURCE) {
+				omode = MCAST_EXCLUDE;
+				add = 1;
+			} else if (optname == IP_UNBLOCK_SOURCE) {
+				omode = MCAST_EXCLUDE;
+				add = 0;
+			} else if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
+				struct ip_mreqn mreq;
+
+				mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
+				mreq.imr_address.s_addr = mreqs.imr_interface;
+				mreq.imr_ifindex = 0;
+				err = ip_mc_join_group(sk, &mreq);
+				if (err)
+					break;
+				omode = MCAST_INCLUDE;
+				add = 1;
+			} else /*IP_DROP_SOURCE_MEMBERSHIP */ {
+				omode = MCAST_INCLUDE;
+				add = 0;
+			}
+			err = ip_mc_source(add, omode, sk, &mreqs);
 			break;
 		}
 		case IP_ROUTER_ALERT:	
@@ -763,6 +821,20 @@
 				return -EFAULT;
 			return 0;
 		}
+		case IP_MSFILTER:
+		{
+			struct ip_msfilter msf;
+			int err;
+
+			if (len < IP_MSFILTER_SIZE(0))
+				return -EINVAL;
+			if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0)))
+				return -EFAULT;
+			err = ip_mc_msfget(sk, &msf,
+				(struct ip_msfilter *)optval, optlen);
+			release_sock(sk);
+			return err;
+		}
 		case IP_PKTOPTIONS:		
 		{
 			struct msghdr msg;
diff -urN linux-2.5.67-bk3/net/ipv4/ipcomp.c linux-2.5.67-bk4/net/ipv4/ipcomp.c
--- linux-2.5.67-bk3/net/ipv4/ipcomp.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.67-bk4/net/ipv4/ipcomp.c	2003-04-12 04:33:22.000000000 -0700
@@ -0,0 +1,366 @@
+/*
+ * IP Payload Compression Protocol (IPComp) - RFC3713.
+ *
+ * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option) 
+ * any later version.
+ *
+ * Todo:
+ *   - Tunable compression parameters.
+ *   - Compression stats.
+ *   - Adaptive compression.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/pfkeyv2.h>
+#include <net/ip.h>
+#include <net/xfrm.h>
+#include <net/icmp.h>
+#include <net/esp.h>
+
+#define IPCOMP_SCRATCH_SIZE	65400
+
+struct ipcomp_hdr {
+	u8 nexthdr;
+	u8 flags;
+	u16 cpi;
+};
+
+struct ipcomp_data {
+	u16 threshold;
+	u8 *scratch;
+	struct crypto_tfm *tfm;
+};
+
+static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
+{
+	int err, plen, dlen;
+	struct iphdr *iph;
+	struct ipcomp_data *ipcd = x->data;
+	u8 *start, *scratch = ipcd->scratch;
+	
+	plen = skb->len;
+	dlen = IPCOMP_SCRATCH_SIZE;
+	start = skb->data;
+
+	err = crypto_comp_decompress(ipcd->tfm, start, plen, scratch, &dlen);
+	if (err)
+		goto out;
+
+	if (dlen < (plen + sizeof(struct ipcomp_hdr))) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	err = pskb_expand_head(skb, 0, dlen - plen, GFP_ATOMIC);
+	if (err)
+		goto out;
+		
+	skb_put(skb, dlen - plen);
+	memcpy(skb->data, scratch, dlen);
+	iph = skb->nh.iph;
+	iph->tot_len = htons(dlen + iph->ihl * 4);
+out:	
+	return err;
+}
+
+static int ipcomp_input(struct xfrm_state *x,
+                        struct xfrm_decap_state *decap, struct sk_buff *skb)
+{
+	u8 nexthdr;
+	int err = 0;
+	struct iphdr *iph;
+	union {
+		struct iphdr	iph;
+		char 		buf[60];
+	} tmp_iph;
+
+
+	if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
+	    skb_linearize(skb, GFP_ATOMIC) != 0) {
+	    	err = -ENOMEM;
+	    	goto out;
+	}
+
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* Remove ipcomp header and decompress original payload */	
+	iph = skb->nh.iph;
+	memcpy(&tmp_iph, iph, iph->ihl * 4);
+	nexthdr = *(u8 *)skb->data;
+	skb_pull(skb, sizeof(struct ipcomp_hdr));
+	memcpy(skb->nh.raw, &tmp_iph, tmp_iph.iph.ihl * 4);
+	iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ipcomp_hdr));
+	iph->protocol = nexthdr;
+	skb->h.raw = skb->data;
+	err = ipcomp_decompress(x, skb);
+
+out:	
+	return err;
+}
+
+static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
+{
+	int err, plen, dlen, ihlen;
+	struct iphdr *iph = skb->nh.iph;
+	struct ipcomp_data *ipcd = x->data;
+	u8 *start, *scratch = ipcd->scratch;
+	
+	ihlen = iph->ihl * 4;
+	plen = skb->len - ihlen;
+	dlen = IPCOMP_SCRATCH_SIZE;
+	start = skb->data + ihlen;
+
+	err = crypto_comp_compress(ipcd->tfm, start, plen, scratch, &dlen);
+	if (err)
+		goto out;
+
+	if ((dlen + sizeof(struct ipcomp_hdr)) >= plen) {
+		err = -EMSGSIZE;
+		goto out;
+	}
+	
+	memcpy(start, scratch, dlen);
+	pskb_trim(skb, ihlen + dlen);
+	
+out:	
+	return err;
+}
+
+static void ipcomp_tunnel_encap(struct xfrm_state *x,
+                                struct sk_buff *skb, int compress)
+{
+	struct dst_entry *dst = skb->dst;
+	struct iphdr *iph, *top_iph;
+
+	iph = skb->nh.iph;
+	top_iph = (struct iphdr *)skb_push(skb, sizeof(struct iphdr));
+	top_iph->ihl = 5;
+	top_iph->version = 4;
+	top_iph->tos = iph->tos;
+	top_iph->tot_len = htons(skb->len);
+	if (!(iph->frag_off&htons(IP_DF)))
+		__ip_select_ident(top_iph, dst, 0);
+	top_iph->ttl = iph->ttl;
+	top_iph->protocol = compress ? IPPROTO_COMP : IPPROTO_IPIP;
+	top_iph->check = 0;
+	top_iph->saddr = x->props.saddr.a4;
+	top_iph->daddr = x->id.daddr.a4;
+	top_iph->frag_off = iph->frag_off&~htons(IP_MF|IP_OFFSET);
+	memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+	skb->nh.raw = skb->data;
+}
+
+static int ipcomp_output(struct sk_buff *skb)
+{
+	int err;
+	struct dst_entry *dst = skb->dst;
+	struct xfrm_state *x = dst->xfrm;
+	struct iphdr *iph, *top_iph;
+	struct ipcomp_hdr *ipch;
+	struct ipcomp_data *ipcd = x->data;
+	union {
+		struct iphdr	iph;
+		char 		buf[60];
+	} tmp_iph;
+
+	if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
+		err = -EINVAL;
+		goto error_nolock;
+	}
+
+	spin_lock_bh(&x->lock);
+	
+	if ((err = xfrm_state_check_expire(x)) != 0)
+		goto error;
+	if ((err = xfrm_state_check_space(x, skb)) != 0)
+		goto error;
+
+	/* Don't bother compressing */
+	if (skb->len < ipcd->threshold) {
+		if (x->props.mode) {
+			ipcomp_tunnel_encap(x, skb, 0);
+			iph = skb->nh.iph;
+			ip_send_check(iph);
+		}
+		goto out_ok;
+	}
+
+	if (x->props.mode) 
+		ipcomp_tunnel_encap(x, skb, 1);
+
+	if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
+	    skb_linearize(skb, GFP_ATOMIC) != 0) {
+	    	err = -ENOMEM;
+	    	goto error;
+	}
+	
+	err = ipcomp_compress(x, skb);
+	if (err) {
+		if (err == -EMSGSIZE)
+			goto out_ok;
+		goto error;
+	}
+
+	/* Install ipcomp header, convert into ipcomp datagram. */
+	iph = skb->nh.iph;
+	memcpy(&tmp_iph, iph, iph->ihl * 4);
+	top_iph = (struct iphdr *)skb_push(skb, sizeof(struct ipcomp_hdr));
+	memcpy(top_iph, &tmp_iph, iph->ihl * 4);
+	iph = top_iph;
+	iph->tot_len = htons(skb->len);
+	iph->protocol = IPPROTO_COMP;
+	iph->check = 0;
+	ipch = (struct ipcomp_hdr *)((char *)iph + iph->ihl * 4);
+	ipch->nexthdr = x->props.mode ? IPPROTO_IPIP : tmp_iph.iph.protocol;
+	ipch->flags = 0;
+	ipch->cpi = htons((u16 )ntohl(x->id.spi));
+	ip_send_check(iph);
+	skb->nh.raw = skb->data;
+
+out_ok:
+	x->curlft.bytes += skb->len;
+	x->curlft.packets++;
+	spin_unlock_bh(&x->lock);
+	
+	if ((skb->dst = dst_pop(dst)) == NULL) {
+		err = -EHOSTUNREACH;
+		goto error_nolock;
+	}
+	err = NET_XMIT_BYPASS;
+
+out_exit:
+	return err;
+error:
+	spin_unlock_bh(&x->lock);
+error_nolock:
+	kfree_skb(skb);
+	goto out_exit;
+}
+
+static void ipcomp4_err(struct sk_buff *skb, u32 info)
+{
+	u32 spi;
+	struct iphdr *iph = (struct iphdr *)skb->data;
+	struct ipcomp_hdr *ipch = (struct ipcomp_hdr *)(skb->data+(iph->ihl<<2));
+	struct xfrm_state *x;
+
+	if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
+	    skb->h.icmph->code != ICMP_FRAG_NEEDED)
+		return;
+
+	spi = ntohl(ntohs(ipch->cpi));
+	x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr,
+	                      spi, IPPROTO_COMP, AF_INET);
+	if (!x)
+		return;
+	printk(KERN_DEBUG "pmtu discvovery on SA IPCOMP/%08x/%u.%u.%u.%u\n",
+	       spi, NIPQUAD(iph->daddr));
+	xfrm_state_put(x);
+}
+
+static void ipcomp_free_data(struct ipcomp_data *ipcd)
+{
+	if (ipcd->tfm)
+		crypto_free_tfm(ipcd->tfm);
+	if (ipcd->scratch)
+		kfree(ipcd->scratch);	
+}
+
+static void ipcomp_destroy(struct xfrm_state *x)
+{
+	struct ipcomp_data *ipcd = x->data;
+	ipcomp_free_data(ipcd);
+	kfree(ipcd);
+}
+
+static int ipcomp_init_state(struct xfrm_state *x, void *args)
+{
+	int err = -ENOMEM;
+	struct ipcomp_data *ipcd;
+	struct xfrm_algo_desc *calg_desc;
+
+	ipcd = kmalloc(sizeof(*ipcd), GFP_KERNEL);
+	if (!ipcd)
+		goto error;
+
+	memset(ipcd, 0, sizeof(*ipcd));
+	x->props.header_len = sizeof(struct ipcomp_hdr);
+	if (x->props.mode)
+		x->props.header_len += sizeof(struct iphdr);
+	x->data = ipcd;
+
+	ipcd->scratch = kmalloc(IPCOMP_SCRATCH_SIZE, GFP_KERNEL);
+	if (!ipcd->scratch)
+		goto error;
+	
+	ipcd->tfm = crypto_alloc_tfm(x->calg->alg_name, 0);
+	if (!ipcd->tfm)
+		goto error;
+
+	calg_desc = xfrm_calg_get_byname(x->calg->alg_name);
+	BUG_ON(!calg_desc);
+	ipcd->threshold = calg_desc->uinfo.comp.threshold;
+	err = 0;
+out:
+	return err;
+
+error:
+	if (ipcd) {
+		ipcomp_free_data(ipcd);
+		kfree(ipcd);
+	}
+	goto out;
+}
+
+static struct xfrm_type ipcomp_type =
+{
+	.description	= "IPCOMP4",
+	.proto	     	= IPPROTO_COMP,
+	.init_state	= ipcomp_init_state,
+	.destructor	= ipcomp_destroy,
+	.input		= ipcomp_input,
+	.output		= ipcomp_output
+};
+
+static struct inet_protocol ipcomp4_protocol = {
+	.handler	=	xfrm4_rcv,
+	.err_handler	=	ipcomp4_err,
+	.no_policy	=	1,
+};
+
+static int __init ipcomp4_init(void)
+{
+	SET_MODULE_OWNER(&ipcomp_type);
+	if (xfrm_register_type(&ipcomp_type, AF_INET) < 0) {
+		printk(KERN_INFO "ipcomp init: can't add xfrm type\n");
+		return -EAGAIN;
+	}
+	if (inet_add_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0) {
+		printk(KERN_INFO "ipcomp init: can't add protocol\n");
+		xfrm_unregister_type(&ipcomp_type, AF_INET);
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+static void __exit ipcomp4_fini(void)
+{
+	if (inet_del_protocol(&ipcomp4_protocol, IPPROTO_COMP) < 0)
+		printk(KERN_INFO "ip ipcomp close: can't remove protocol\n");
+	if (xfrm_unregister_type(&ipcomp_type, AF_INET) < 0)
+		printk(KERN_INFO "ip ipcomp close: can't remove xfrm type\n");
+}
+
+module_init(ipcomp4_init);
+module_exit(ipcomp4_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3713");
+MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
+
diff -urN linux-2.5.67-bk3/net/ipv4/netfilter/ip_conntrack_core.c linux-2.5.67-bk4/net/ipv4/netfilter/ip_conntrack_core.c
--- linux-2.5.67-bk3/net/ipv4/netfilter/ip_conntrack_core.c	2003-04-12 04:33:18.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/netfilter/ip_conntrack_core.c	2003-04-12 04:33:22.000000000 -0700
@@ -273,6 +273,8 @@
 		 * the un-established ones only */
 		if (exp->sibling) {
 			DEBUGP("remove_expectations: skipping established %p of %p\n", exp->sibling, ct);
+			/* Indicate that this expectations parent is dead */
+			exp->expectant = NULL;
 			continue;
 		}
 
@@ -324,6 +326,9 @@
 		ip_conntrack_destroyed(ct);
 
 	WRITE_LOCK(&ip_conntrack_lock);
+	/* Delete us from our own list to prevent corruption later */
+	list_del(&ct->sibling_list);
+
 	/* Delete our master expectation */
 	if (ct->master) {
 		/* can't call __unexpect_related here,
diff -urN linux-2.5.67-bk3/net/ipv4/route.c linux-2.5.67-bk4/net/ipv4/route.c
--- linux-2.5.67-bk3/net/ipv4/route.c	2003-04-07 10:32:59.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/route.c	2003-04-12 04:33:22.000000000 -0700
@@ -1790,7 +1790,8 @@
 
 		read_lock(&inetdev_lock);
 		if ((in_dev = __in_dev_get(dev)) != NULL) {
-			int our = ip_check_mc(in_dev, daddr);
+			int our = ip_check_mc(in_dev, daddr, saddr,
+				skb->nh.iph->protocol);
 			if (our
 #ifdef CONFIG_IP_MROUTE
 			    || (!LOCAL_MCAST(daddr) && IN_DEV_MFORWARD(in_dev))
@@ -2020,7 +2021,7 @@
 		}
 	} else if (res.type == RTN_MULTICAST) {
 		flags |= RTCF_MULTICAST|RTCF_LOCAL;
-		if (!ip_check_mc(in_dev, oldflp->fl4_dst))
+		if (!ip_check_mc(in_dev, oldflp->fl4_dst, oldflp->fl4_src, oldflp->proto))
 			flags &= ~RTCF_LOCAL;
 		/* If multicast route do not exist use
 		   default one, but do not gateway in this case.
diff -urN linux-2.5.67-bk3/net/ipv4/udp.c linux-2.5.67-bk4/net/ipv4/udp.c
--- linux-2.5.67-bk3/net/ipv4/udp.c	2003-04-07 10:30:44.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/udp.c	2003-04-12 04:33:22.000000000 -0700
@@ -298,6 +298,8 @@
 		    ipv6_only_sock(s)					||
 		    (s->bound_dev_if && s->bound_dev_if != dif))
 			continue;
+		if (!ip_mc_sf_allow(sk, loc_addr, rmt_addr, dif))
+			continue;
 		break;
   	}
   	return s;
diff -urN linux-2.5.67-bk3/net/ipv4/xfrm4_input.c linux-2.5.67-bk4/net/ipv4/xfrm4_input.c
--- linux-2.5.67-bk3/net/ipv4/xfrm4_input.c	2003-04-07 10:30:42.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/xfrm4_input.c	2003-04-12 04:33:22.000000000 -0700
@@ -48,6 +48,9 @@
 		if (x->props.replay_window && xfrm_replay_check(x, seq))
 			goto drop_unlock;
 
+		if (xfrm_state_check_expire(x))
+			goto drop_unlock;
+
 		xfrm_vec[xfrm_nr].decap.decap_type = encap_type;
 		if (x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb))
 			goto drop_unlock;
diff -urN linux-2.5.67-bk3/net/ipv4/xfrm4_policy.c linux-2.5.67-bk4/net/ipv4/xfrm4_policy.c
--- linux-2.5.67-bk3/net/ipv4/xfrm4_policy.c	2003-04-07 10:32:18.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv4/xfrm4_policy.c	2003-04-12 04:33:22.000000000 -0700
@@ -201,6 +201,13 @@
 			}
 			break;
 
+		case IPPROTO_COMP:
+			if (pskb_may_pull(skb, xprth + 4 - skb->data)) {
+				u16 *ipcomp_hdr = (u16 *)xprth;
+
+				fl->uli_u.spi = ntohl(ntohs(ipcomp_hdr[1]));
+			}
+			break;
 		default:
 			fl->uli_u.spi = 0;
 			break;
diff -urN linux-2.5.67-bk3/net/ipv6/addrconf.c linux-2.5.67-bk4/net/ipv6/addrconf.c
--- linux-2.5.67-bk3/net/ipv6/addrconf.c	2003-04-07 10:32:50.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv6/addrconf.c	2003-04-12 04:33:22.000000000 -0700
@@ -1202,7 +1202,7 @@
 	if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))
 		rtmsg.rtmsg_flags |= RTF_NONEXTHOP;
 
-	ip6_route_add(&rtmsg);
+	ip6_route_add(&rtmsg, NULL);
 }
 
 /* Create "default" multicast route to the interface */
@@ -1219,7 +1219,7 @@
 	rtmsg.rtmsg_ifindex = dev->ifindex;
 	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
 	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
-	ip6_route_add(&rtmsg);
+	ip6_route_add(&rtmsg, NULL);
 }
 
 static void sit_route_add(struct net_device *dev)
@@ -1236,7 +1236,7 @@
 	rtmsg.rtmsg_flags	= RTF_UP|RTF_NONEXTHOP;
 	rtmsg.rtmsg_ifindex	= dev->ifindex;
 
-	ip6_route_add(&rtmsg);
+	ip6_route_add(&rtmsg, NULL);
 }
 
 static void addrconf_add_lroute(struct net_device *dev)
@@ -1328,7 +1328,7 @@
 	if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
 		if (rt->rt6i_flags&RTF_EXPIRES) {
 			if (pinfo->onlink == 0 || valid_lft == 0) {
-				ip6_del_rt(rt);
+				ip6_del_rt(rt, NULL);
 				rt = NULL;
 			} else {
 				rt->rt6i_expires = rt_expires;
@@ -1952,7 +1952,7 @@
 
 		rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex;
 
-		ip6_route_add(&rtmsg);
+		ip6_route_add(&rtmsg, NULL);
 	}
 
 out:
diff -urN linux-2.5.67-bk3/net/ipv6/ip6_fib.c linux-2.5.67-bk4/net/ipv6/ip6_fib.c
--- linux-2.5.67-bk3/net/ipv6/ip6_fib.c	2003-04-07 10:32:23.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv6/ip6_fib.c	2003-04-12 04:33:22.000000000 -0700
@@ -423,7 +423,8 @@
  *	Insert routing information in a node.
  */
 
-static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt)
+static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
+    struct nlmsghdr *nlh)
 {
 	struct rt6_info *iter = NULL;
 	struct rt6_info **ins;
@@ -480,7 +481,7 @@
 	*ins = rt;
 	rt->rt6i_node = fn;
 	atomic_inc(&rt->rt6i_ref);
-	inet6_rt_notify(RTM_NEWROUTE, rt);
+	inet6_rt_notify(RTM_NEWROUTE, rt, nlh);
 	rt6_stats.fib_rt_entries++;
 
 	if ((fn->fn_flags & RTN_RTINFO) == 0) {
@@ -504,7 +505,7 @@
  *	with source addr info in sub-trees
  */
 
-int fib6_add(struct fib6_node *root, struct rt6_info *rt)
+int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nlmsghdr *nlh)
 {
 	struct fib6_node *fn;
 	int err = -ENOMEM;
@@ -577,7 +578,7 @@
 	}
 #endif
 
-	err = fib6_add_rt2node(fn, rt);
+	err = fib6_add_rt2node(fn, rt, nlh);
 
 	if (err == 0) {
 		fib6_start_gc(rt);
@@ -885,7 +886,8 @@
 	}
 }
 
-static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp)
+static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
+    struct nlmsghdr *nlh)
 {
 	struct fib6_walker_t *w;
 	struct rt6_info *rt = *rtp;
@@ -940,11 +942,11 @@
 		if (atomic_read(&rt->rt6i_ref) != 1) BUG();
 	}
 
-	inet6_rt_notify(RTM_DELROUTE, rt);
+	inet6_rt_notify(RTM_DELROUTE, rt, nlh);
 	rt6_release(rt);
 }
 
-int fib6_del(struct rt6_info *rt)
+int fib6_del(struct rt6_info *rt, struct nlmsghdr *nlh)
 {
 	struct fib6_node *fn = rt->rt6i_node;
 	struct rt6_info **rtp;
@@ -969,7 +971,7 @@
 
 	for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->u.next) {
 		if (*rtp == rt) {
-			fib6_del_route(fn, rtp);
+			fib6_del_route(fn, rtp, nlh);
 			return 0;
 		}
 	}
@@ -1098,7 +1100,7 @@
 		res = c->func(rt, c->arg);
 		if (res < 0) {
 			w->leaf = rt;
-			res = fib6_del(rt);
+			res = fib6_del(rt, NULL);
 			if (res) {
 #if RT6_DEBUG >= 2
 				printk(KERN_DEBUG "fib6_clean_node: del failed: rt=%p@%p err=%d\n", rt, rt->rt6i_node, res);
diff -urN linux-2.5.67-bk3/net/ipv6/ndisc.c linux-2.5.67-bk4/net/ipv6/ndisc.c
--- linux-2.5.67-bk3/net/ipv6/ndisc.c	2003-04-07 10:31:48.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv6/ndisc.c	2003-04-12 04:33:22.000000000 -0700
@@ -961,7 +961,7 @@
 				struct rt6_info *rt;
 				rt = rt6_get_dflt_router(saddr, dev);
 				if (rt)
-					ip6_del_rt(rt);
+					ip6_del_rt(rt, NULL);
 			}
 		} else {
 			if (msg->icmph.icmp6_router)
@@ -1035,7 +1035,7 @@
 	rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
 
 	if (rt && lifetime == 0) {
-		ip6_del_rt(rt);
+		ip6_del_rt(rt, NULL);
 		rt = NULL;
 	}
 
diff -urN linux-2.5.67-bk3/net/ipv6/route.c linux-2.5.67-bk4/net/ipv6/route.c
--- linux-2.5.67-bk3/net/ipv6/route.c	2003-04-07 10:32:17.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv6/route.c	2003-04-12 04:33:22.000000000 -0700
@@ -318,12 +318,12 @@
    be destroyed.
  */
 
-static int rt6_ins(struct rt6_info *rt)
+static int rt6_ins(struct rt6_info *rt, struct nlmsghdr *nlh)
 {
 	int err;
 
 	write_lock_bh(&rt6_lock);
-	err = fib6_add(&ip6_routing_table, rt);
+	err = fib6_add(&ip6_routing_table, rt, nlh);
 	write_unlock_bh(&rt6_lock);
 
 	return err;
@@ -366,7 +366,7 @@
 
 		dst_hold(&rt->u.dst);
 
-		err = rt6_ins(rt);
+		err = rt6_ins(rt, NULL);
 		if (err == 0)
 			return rt;
 
@@ -522,7 +522,7 @@
 
 	if (rt) {
 		if (rt->rt6i_flags & RTF_CACHE)
-			ip6_del_rt(rt);
+			ip6_del_rt(rt, NULL);
 		else
 			dst_release(dst);
 	}
@@ -625,9 +625,10 @@
  *
  */
 
-int ip6_route_add(struct in6_rtmsg *rtmsg)
+int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh)
 {
 	int err;
+	struct rtmsg *r;
 	struct rt6_info *rt;
 	struct net_device *dev = NULL;
 	int addr_type;
@@ -648,6 +649,11 @@
 
 	rt->u.dst.obsolete = -1;
 	rt->rt6i_expires = rtmsg->rtmsg_info;
+	if (nlh && (r = NLMSG_DATA(nlh))) {
+		rt->rt6i_protocol = r->rtm_protocol;
+	} else {
+		rt->rt6i_protocol = RTPROT_BOOT;
+	}
 
 	addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
 
@@ -772,7 +778,7 @@
 	if (dst_metric(&rt->u.dst, RTAX_ADVMSS) > 65535-20)
 		rt->u.dst.metrics[RTAX_ADVMSS-1] = 65535;
 	rt->u.dst.dev = dev;
-	return rt6_ins(rt);
+	return rt6_ins(rt, nlh);
 
 out:
 	if (dev)
@@ -781,7 +787,7 @@
 	return err;
 }
 
-int ip6_del_rt(struct rt6_info *rt)
+int ip6_del_rt(struct rt6_info *rt, struct nlmsghdr *nlh)
 {
 	int err;
 
@@ -793,13 +799,13 @@
 
 	dst_release(&rt->u.dst);
 
-	err = fib6_del(rt);
+	err = fib6_del(rt, nlh);
 	write_unlock_bh(&rt6_lock);
 
 	return err;
 }
 
-static int ip6_route_del(struct in6_rtmsg *rtmsg)
+static int ip6_route_del(struct in6_rtmsg *rtmsg, struct nlmsghdr *nlh)
 {
 	struct fib6_node *fn;
 	struct rt6_info *rt;
@@ -826,7 +832,7 @@
 			dst_hold(&rt->u.dst);
 			read_unlock_bh(&rt6_lock);
 
-			return ip6_del_rt(rt);
+			return ip6_del_rt(rt, nlh);
 		}
 	}
 	read_unlock_bh(&rt6_lock);
@@ -928,11 +934,11 @@
 		nrt->u.dst.metrics[RTAX_ADVMSS-1] = 65535;
 	nrt->rt6i_hoplimit = ipv6_get_hoplimit(neigh->dev);
 
-	if (rt6_ins(nrt))
+	if (rt6_ins(nrt, NULL))
 		goto out;
 
 	if (rt->rt6i_flags&RTF_CACHE) {
-		ip6_del_rt(rt);
+		ip6_del_rt(rt, NULL);
 		return;
 	}
 
@@ -1018,7 +1024,7 @@
 		dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires);
 		nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES;
 		nrt->u.dst.metrics[RTAX_MTU-1] = pmtu;
-		rt6_ins(nrt);
+		rt6_ins(nrt, NULL);
 	}
 
 out:
@@ -1091,7 +1097,7 @@
 
 	rtmsg.rtmsg_ifindex = dev->ifindex;
 
-	ip6_route_add(&rtmsg);
+	ip6_route_add(&rtmsg, NULL);
 	return rt6_get_dflt_router(gwaddr, dev);
 }
 
@@ -1117,7 +1123,7 @@
 
 			read_unlock_bh(&rt6_lock);
 
-			ip6_del_rt(rt);
+			ip6_del_rt(rt, NULL);
 
 			goto restart;
 		}
@@ -1143,10 +1149,10 @@
 		rtnl_lock();
 		switch (cmd) {
 		case SIOCADDRT:
-			err = ip6_route_add(&rtmsg);
+			err = ip6_route_add(&rtmsg, NULL);
 			break;
 		case SIOCDELRT:
-			err = ip6_route_del(&rtmsg);
+			err = ip6_route_del(&rtmsg, NULL);
 			break;
 		default:
 			err = -EINVAL;
@@ -1203,7 +1209,7 @@
 
 	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
 	rt->rt6i_dst.plen = 128;
-	rt6_ins(rt);
+	rt6_ins(rt, NULL);
 
 	return 0;
 }
@@ -1220,7 +1226,7 @@
 	rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, 1);
 	if (rt) {
 		if (rt->rt6i_dst.plen == 128)
-			err = ip6_del_rt(rt);
+			err = ip6_del_rt(rt, NULL);
 		else
 			dst_release(&rt->u.dst);
 	}
@@ -1350,7 +1356,7 @@
 
 	if (inet6_rtm_to_rtmsg(r, arg, &rtmsg))
 		return -EINVAL;
-	return ip6_route_del(&rtmsg);
+	return ip6_route_del(&rtmsg, nlh);
 }
 
 int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
@@ -1360,7 +1366,7 @@
 
 	if (inet6_rtm_to_rtmsg(r, arg, &rtmsg))
 		return -EINVAL;
-	return ip6_route_add(&rtmsg);
+	return ip6_route_add(&rtmsg, nlh);
 }
 
 struct rt6_rtnl_dump_arg
@@ -1373,13 +1379,18 @@
 			 struct in6_addr *dst,
 			 struct in6_addr *src,
 			 int iif,
-			 int type, u32 pid, u32 seq)
+			 int type, u32 pid, u32 seq,
+			 struct nlmsghdr *in_nlh)
 {
 	struct rtmsg *rtm;
 	struct nlmsghdr  *nlh;
 	unsigned char	 *b = skb->tail;
 	struct rta_cacheinfo ci;
 
+	if (!pid && in_nlh) {
+		pid = in_nlh->nlmsg_pid;
+	}
+
 	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*rtm));
 	rtm = NLMSG_DATA(nlh);
 	rtm->rtm_family = AF_INET6;
@@ -1395,7 +1406,7 @@
 		rtm->rtm_type = RTN_UNICAST;
 	rtm->rtm_flags = 0;
 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
-	rtm->rtm_protocol = RTPROT_BOOT;
+	rtm->rtm_protocol = rt->rt6i_protocol;
 	if (rt->rt6i_flags&RTF_DYNAMIC)
 		rtm->rtm_protocol = RTPROT_REDIRECT;
 	else if (rt->rt6i_flags&(RTF_ADDRCONF|RTF_ALLONLINK))
@@ -1458,7 +1469,8 @@
 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
 
 	return rt6_fill_node(arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
-			     NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq);
+		     NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,
+		     NULL);
 }
 
 static int fib6_dump_node(struct fib6_walker_t *w)
@@ -1608,7 +1620,8 @@
 			    fl.nl_u.ip6_u.daddr,
 			    fl.nl_u.ip6_u.saddr,
 			    iif,
-			    RTM_NEWROUTE, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq);
+			    RTM_NEWROUTE, NETLINK_CB(in_skb).pid,
+			    nlh->nlmsg_seq, nlh);
 	if (err < 0) {
 		err = -EMSGSIZE;
 		goto out_free;
@@ -1624,7 +1637,7 @@
 	goto out;	
 }
 
-void inet6_rt_notify(int event, struct rt6_info *rt)
+void inet6_rt_notify(int event, struct rt6_info *rt, struct nlmsghdr *nlh)
 {
 	struct sk_buff *skb;
 	int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
@@ -1634,7 +1647,7 @@
 		netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, ENOBUFS);
 		return;
 	}
-	if (rt6_fill_node(skb, rt, NULL, NULL, 0, event, 0, 0) < 0) {
+	if (rt6_fill_node(skb, rt, NULL, NULL, 0, event, 0, 0, nlh) < 0) {
 		kfree_skb(skb);
 		netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, EINVAL);
 		return;
diff -urN linux-2.5.67-bk3/net/ipv6/xfrm6_input.c linux-2.5.67-bk4/net/ipv6/xfrm6_input.c
--- linux-2.5.67-bk3/net/ipv6/xfrm6_input.c	2003-04-07 10:33:02.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv6/xfrm6_input.c	2003-04-12 04:33:22.000000000 -0700
@@ -172,6 +172,9 @@
 		if (x->props.replay_window && xfrm_replay_check(x, seq))
 			goto drop_unlock;
 
+		if (xfrm_state_check_expire(x))
+			goto drop_unlock;
+
 		nexthdr = x->type->input(x, &(xfrm_vec[xfrm_nr].decap), skb);
 		if (nexthdr <= 0)
 			goto drop_unlock;
diff -urN linux-2.5.67-bk3/net/ipv6/xfrm6_policy.c linux-2.5.67-bk4/net/ipv6/xfrm6_policy.c
--- linux-2.5.67-bk3/net/ipv6/xfrm6_policy.c	2003-04-07 10:30:57.000000000 -0700
+++ linux-2.5.67-bk4/net/ipv6/xfrm6_policy.c	2003-04-12 04:33:22.000000000 -0700
@@ -203,6 +203,7 @@
 		/* XXX Why are there these headers? */
 		case IPPROTO_AH:
 		case IPPROTO_ESP:
+		case IPPROTO_COMP:
 		default:
 			fl->uli_u.spi = 0;
 			return;
diff -urN linux-2.5.67-bk3/net/netsyms.c linux-2.5.67-bk4/net/netsyms.c
--- linux-2.5.67-bk3/net/netsyms.c	2003-04-07 10:31:05.000000000 -0700
+++ linux-2.5.67-bk4/net/netsyms.c	2003-04-12 04:33:22.000000000 -0700
@@ -234,8 +234,8 @@
 
 #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
 EXPORT_SYMBOL(br_handle_frame_hook);
+EXPORT_SYMBOL(brioctl_set);
 #endif
-EXPORT_SYMBOL(br_ioctl_hook);
 
 #ifdef CONFIG_NET_DIVERT
 EXPORT_SYMBOL(alloc_divert_blk);
diff -urN linux-2.5.67-bk3/net/sched/sch_csz.c linux-2.5.67-bk4/net/sched/sch_csz.c
--- linux-2.5.67-bk3/net/sched/sch_csz.c	2003-04-07 10:31:45.000000000 -0700
+++ linux-2.5.67-bk4/net/sched/sch_csz.c	2003-04-12 04:33:22.000000000 -0700
@@ -749,6 +749,14 @@
 static void
 csz_destroy(struct Qdisc* sch)
 {
+	struct csz_sched_data *q = (struct csz_sched_data *)sch->data;
+	struct tcf_proto *tp;
+
+	while ((tp = q->filter_list) != NULL) {
+		q->filter_list = tp->next;
+		tp->ops->destroy(tp);
+	}
+
 	MOD_DEC_USE_COUNT;
 }
 
diff -urN linux-2.5.67-bk3/net/sched/sch_htb.c linux-2.5.67-bk4/net/sched/sch_htb.c
--- linux-2.5.67-bk3/net/sched/sch_htb.c	2003-04-07 10:31:52.000000000 -0700
+++ linux-2.5.67-bk4/net/sched/sch_htb.c	2003-04-12 04:33:22.000000000 -0700
@@ -102,7 +102,9 @@
 #define HTB_PASSQ q,
 #define HTB_ARGQ struct htb_sched *q,
 #define static
+#undef __inline__
 #define __inline__
+#undef inline
 #define inline
 #define HTB_CMAGIC 0xFEFAFEF1
 #define htb_safe_rb_erase(N,R) do { BUG_TRAP((N)->rb_color != -1); \
diff -urN linux-2.5.67-bk3/net/sched/sch_prio.c linux-2.5.67-bk4/net/sched/sch_prio.c
--- linux-2.5.67-bk3/net/sched/sch_prio.c	2003-04-07 10:31:05.000000000 -0700
+++ linux-2.5.67-bk4/net/sched/sch_prio.c	2003-04-12 04:33:22.000000000 -0700
@@ -158,6 +158,12 @@
 {
 	int prio;
 	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
+	struct tcf_proto *tp;
+
+	while ((tp = q->filter_list) != NULL) {
+		q->filter_list = tp->next;
+		tp->ops->destroy(tp);
+	}
 
 	for (prio=0; prio<q->bands; prio++) {
 		qdisc_destroy(q->queues[prio]);
diff -urN linux-2.5.67-bk3/net/socket.c linux-2.5.67-bk4/net/socket.c
--- linux-2.5.67-bk3/net/socket.c	2003-04-07 10:31:03.000000000 -0700
+++ linux-2.5.67-bk4/net/socket.c	2003-04-12 04:33:22.000000000 -0700
@@ -71,6 +71,7 @@
 #include <linux/wanrouter.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+#include <linux/if_bridge.h>
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <linux/cache.h>
@@ -712,7 +713,18 @@
 				 file, vector, count, tot_len);
 }
 
-int (*br_ioctl_hook)(unsigned long arg);
+
+static DECLARE_MUTEX(br_ioctl_mutex);
+static int (*br_ioctl_hook)(unsigned long arg) = NULL;
+
+void brioctl_set(int (*hook)(unsigned long))
+{
+	down(&br_ioctl_mutex);
+	br_ioctl_hook = hook;
+	up(&br_ioctl_mutex);
+}
+
+
 int (*vlan_ioctl_hook)(unsigned long arg);
 
 #ifdef CONFIG_DLCI
@@ -759,12 +771,16 @@
 		case SIOCGIFBR:
 		case SIOCSIFBR:
 			err = -ENOPKG;
+			
 #ifdef CONFIG_KMOD
 			if (!br_ioctl_hook)
 				request_module("bridge");
 #endif
-			if (br_ioctl_hook)
+
+			down(&br_ioctl_mutex);
+			if (br_ioctl_hook) 
 				err = br_ioctl_hook(arg);
+			up(&br_ioctl_mutex);
 			break;
 		case SIOCGIFVLAN:
 		case SIOCSIFVLAN:
diff -urN linux-2.5.67-bk3/sound/core/ioctl32/pcm32.c linux-2.5.67-bk4/sound/core/ioctl32/pcm32.c
--- linux-2.5.67-bk3/sound/core/ioctl32/pcm32.c	2003-04-12 04:33:18.000000000 -0700
+++ linux-2.5.67-bk4/sound/core/ioctl32/pcm32.c	2003-04-07 10:31:51.000000000 -0700
@@ -21,6 +21,7 @@
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/slab.h>
+#include <linux/compat.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include "ioctl32.h"
@@ -136,15 +137,10 @@
 	COPY(step);\
 }
 
-struct timeval32 {
-	s32 tv_sec;
-	s32 tv_usec;
-} __attribute__((packed));
-
 struct sndrv_pcm_status32 {
 	s32 state;
-	struct timeval32 trigger_tstamp;
-	struct timeval32 tstamp;
+	struct compat_timespec trigger_tstamp;
+	struct compat_timespec tstamp;
 	u32 appl_ptr;
 	u32 hw_ptr;
 	s32 delay;
@@ -159,9 +155,9 @@
 {\
 	COPY(state);\
 	COPY(trigger_tstamp.tv_sec);\
-	COPY(trigger_tstamp.tv_usec);\
+	COPY(trigger_tstamp.tv_nsec);\
 	COPY(tstamp.tv_sec);\
-	COPY(tstamp.tv_usec);\
+	COPY(tstamp.tv_nsec);\
 	COPY(appl_ptr);\
 	COPY(hw_ptr);\
 	COPY(delay);\
diff -urN linux-2.5.67-bk3/sound/core/ioctl32/rawmidi32.c linux-2.5.67-bk4/sound/core/ioctl32/rawmidi32.c
--- linux-2.5.67-bk3/sound/core/ioctl32/rawmidi32.c	2003-04-12 04:33:18.000000000 -0700
+++ linux-2.5.67-bk4/sound/core/ioctl32/rawmidi32.c	2003-04-07 10:31:20.000000000 -0700
@@ -21,6 +21,7 @@
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/fs.h>
+#include <linux/compat.h>
 #include <sound/core.h>
 #include <sound/rawmidi.h>
 #include <asm/uaccess.h>
@@ -42,14 +43,9 @@
 	COPY(no_active_sensing);\
 }
 
-struct timeval32 {
-	s32 tv_sec;
-	s32 tv_usec;
-} __attribute__((packed));
-
 struct sndrv_rawmidi_status32 {
 	s32 stream;
-	struct timeval32 tstamp;
+	struct compat_timespec tstamp;
 	u32 avail;
 	u32 xruns;
 	unsigned char reserved[16];
@@ -59,7 +55,7 @@
 {\
 	COPY(stream);\
 	COPY(tstamp.tv_sec);\
-	COPY(tstamp.tv_usec);\
+	COPY(tstamp.tv_nsec);\
 	COPY(avail);\
 	COPY(xruns);\
 }
diff -urN linux-2.5.67-bk3/sound/core/ioctl32/timer32.c linux-2.5.67-bk4/sound/core/ioctl32/timer32.c
--- linux-2.5.67-bk3/sound/core/ioctl32/timer32.c	2003-04-12 04:33:18.000000000 -0700
+++ linux-2.5.67-bk4/sound/core/ioctl32/timer32.c	2003-04-07 10:31:43.000000000 -0700
@@ -21,6 +21,7 @@
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/fs.h>
+#include <linux/compat.h>
 #include <sound/core.h>
 #include <sound/timer.h>
 #include <asm/uaccess.h>
@@ -31,7 +32,7 @@
 	s32 card;
 	unsigned char id[64];
 	unsigned char name[80];
-	u32 ticks;
+	u32 reserved0;
 	u32 resolution;
 	unsigned char reserved[64];
 };
@@ -42,17 +43,11 @@
 	COPY(card);\
 	memcpy(dst->id, src->id, sizeof(src->id));\
 	memcpy(dst->name, src->name, sizeof(src->name));\
-	COPY(ticks);\
 	COPY(resolution);\
 }
 
-struct timeval32 {
-	s32 tv_sec;
-	s32 tv_usec;
-};
-
 struct sndrv_timer_status32 {
-	struct timeval32 tstamp;
+	struct compat_timespec tstamp;
 	u32 resolution;
 	u32 lost;
 	u32 overrun;
@@ -63,7 +58,7 @@
 #define CVT_sndrv_timer_status()\
 {\
 	COPY(tstamp.tv_sec);\
-	COPY(tstamp.tv_usec);\
+	COPY(tstamp.tv_nsec);\
 	COPY(resolution);\
 	COPY(lost);\
 	COPY(overrun);\
diff -urN linux-2.5.67-bk3/sound/core/memalloc.c linux-2.5.67-bk4/sound/core/memalloc.c
--- linux-2.5.67-bk3/sound/core/memalloc.c	2003-04-12 04:33:18.000000000 -0700
+++ linux-2.5.67-bk4/sound/core/memalloc.c	2003-04-12 04:33:22.000000000 -0700
@@ -205,7 +205,7 @@
 #endif
 #ifdef CONFIG_SBUS
 	case SNDRV_DMA_TYPE_SBUS:
-		dmab->area = snd_malloc_pci_pages(dev->dev.sbus, size, &dmab->addr);
+		dmab->area = snd_malloc_sbus_pages(dev->dev.sbus, size, &dmab->addr);
 		break;
 #endif
 	default:
@@ -248,7 +248,7 @@
 #endif
 #ifdef CONFIG_SBUS
 	case SNDRV_DMA_TYPE_SBUS:
-		snd_free_sbus_pages(dev->dev.sbus, dmab->size, dmab->are, dmab->addr);
+		snd_free_sbus_pages(dev->dev.sbus, dmab->bytes, dmab->area, dmab->addr);
 		break;
 #endif
 	default:
