## 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 @@ USB Host-Side API Model - Host-side drivers for USB devices talk to the "usbcore" APIs. + 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 general purpose drivers, exposed through @@ -289,6 +290,684 @@ !Edrivers/usb/core/buffer.c + + The USB Filesystem (usbfs) + + This chapter presents the Linux usbfs. + 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 + libusb + for C/C++, and + jUSB for Java. + + + Unfinished + 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. + + + + Configure usbfs into Linux kernels by enabling the + USB filesystem option (CONFIG_USB_DEVICEFS), + and you get basic support for user mode USB device drivers. + Until relatively recently it was often (confusingly) called + usbdevfs although it wasn't solving what + devfs 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. + + + + What files are in "usbfs"? + + Conventionally mounted at + /proc/bus/usb, usbfs + features include: + + /proc/bus/usb/devices + ... 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. + + /proc/bus/usb/BBB/DDD + ... 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.) + + + + + 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. + Don't even think of saving these in application + configuration files. + Stable identifiers are available, for user mode applications + that want to use them. HID and networking devices expose + these IDs. + + + + + + Mounting and Access Control + + 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 + (/proc/bus/BBB/DDD) although anyone may read + the devices + or drivers files. + I/O requests to the device also need the CAP_SYS_RAWIO capability, + + + 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 devmode=0666 option) + so the driver can start whenever it's needed. + + + The mount options for usbfs, usable in /etc/fstab or + in command line invocations of mount, are: + + + + busgid=NNNNN + Controls the GID used for the + /proc/bus/usb/BBB + directories. (Default: 0) + busmode=MMM + Controls the file mode used for the + /proc/bus/usb/BBB + directories. (Default: 0555) + + busuid=NNNNN + Controls the UID used for the + /proc/bus/usb/BBB + directories. (Default: 0) + + devgid=NNNNN + Controls the GID used for the + /proc/bus/usb/BBB/DDD + files. (Default: 0) + devmode=MMM + Controls the file mode used for the + /proc/bus/usb/BBB/DDD + files. (Default: 0644) + devuid=NNNNN + Controls the UID used for the + /proc/bus/usb/BBB/DDD + files. (Default: 0) + + listgid=NNNNN + Controls the GID used for the + /proc/bus/usb/devices and drivers files. + (Default: 0) + listmode=MMM + Controls the file mode used for the + /proc/bus/usb/devices and drivers files. + (Default: 0444) + listuid=NNNNN + Controls the UID used for the + /proc/bus/usb/devices and drivers files. + (Default: 0) + + + + + Note that many Linux distributions hard-wire the mount options + for usbfs in their init scripts, such as + /etc/rc.d/rc.sysinit, + rather than making it easy to set this per-system + policy in /etc/fstab. + + + + + + /proc/bus/usb/devices + + 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 + Documentation/usb/proc_usb_info.txt + file in your Linux kernel sources. + + + 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. + + + + + + /proc/bus/usb/BBB/DDD + + Use these files in one of these basic ways: + + + They can be read, + 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. + + + Perform USB operations using + ioctl() 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 half duplex protocols, + but otherwise you'd use asynchronous i/o requests. + + + + + + + Life Cycle of User Mode Drivers + + 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 + /sbin/hotplug 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 read() 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. + + + 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. + + + 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.) + + + 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. + Bulk transfers are easiest to use, + but only their sibling interrupt transfers + work with low speed devices. + Both interrupt and isochronous 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. + + + 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. + + + + + The ioctl() Requests + + To use these ioctls, you need to include the following + headers in your userspace program: +#include <linux/usb.h> +#include <linux/usbdevice_fs.h> +#include <asm/byteorder.h> + The standard USB device model requests, from "Chapter 9" of + the USB 2.0 specification, are automatically included from + the <linux/usb_ch9.h> header. + + + 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 + Documentation/usb/error-codes.txt + in your kernel sources.) + + + 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 + configuration that affects things + like power consumption and basic functionality. + The endpoints are part of USB interfaces, + which may have altsettings + 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. + + + + + Management/Status Requests + + 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. + + + + + USBDEVFS_CLAIMINTERFACE + 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). + + 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. + + This claim will be released by a RELEASEINTERFACE ioctl, + or by closing the file descriptor. + File modification time is not updated by this request. + + + USBDEVFS_CONNECTINFO + Says whether the device is lowspeed. + The ioctl parameter points to a structure like this: +struct usbdevfs_connectinfo { + unsigned int devnum; + unsigned char slow; +}; + File modification time is not updated by this request. + + You can't tell whether a "not slow" + device is connected at high speed (480 MBit/sec) + or just full speed (12 MBit/sec). + You should know the devnum value already, + it's the DDD value of the device file name. + + + USBDEVFS_GETDRIVER + Returns the name of the kernel driver + bound to a given interface (a string). Parameter + is a pointer to this structure, which is modified: +struct usbdevfs_getdriver { + unsigned int interface; + char driver[USBDEVFS_MAXDRIVERNAME + 1]; +}; + File modification time is not updated by this request. + + + USBDEVFS_IOCTL + Passes a request from userspace through + to a kernel driver that has an ioctl entry in the + struct usb_driver it registered. +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, &wrapper); +} + File modification time is not updated by this request. + + 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. + + + USBDEVFS_RELEASEINTERFACE + 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. + + 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. + + + USBDEVFS_RESETEP + 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. + + Avoid using this request. + It should probably be removed. + 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. + + + + + + + + Synchronous I/O Support + + 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. + + + + + USBDEVFS_BULK + Issues a bulk read or write request to the + device. + The ioctl parameter is a pointer to this structure: +struct usbdevfs_bulktransfer { + unsigned int ep; + unsigned int len; + unsigned int timeout; /* in milliseconds */ + void *data; +}; + 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. + FIXME say how read length is returned, + and how short reads are handled.. + + + USBDEVFS_CLEAR_HALT + 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. + + Use this on bulk or interrupt endpoints which have + stalled, returning -EPIPE 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. + + + USBDEVFS_CONTROL + Issues a control request to the device. + The ioctl parameter points to a structure like this: +struct usbdevfs_ctrltransfer { + __u8 bRequestType; + __u8 bRequest; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + __u32 timeout; /* in milliseconds */ + void *data; +}; + + 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 + <linux/usb.h>). + 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). + + 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.) + Also there's no way to say it's + not OK to get a short read back from the device. + + + USBDEVFS_RESET + 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. + + Avoid using this call + until some usbcore bugs get fixed, + since it does not fully synchronize device, interface, + and driver (not just usbfs) state. + + + USBDEVFS_SETINTERFACE + Sets the alternate setting for an + interface. The ioctl parameter is a pointer to a + structure like this: +struct usbdevfs_setinterface { + unsigned int interface; + unsigned int altsetting; +}; + File modification time is not updated by this request. + + 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.) + + + USBDEVFS_SETCONFIGURATION + Issues the + usb_set_configuration 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. + + Avoid using this call + until some usbcore bugs get fixed, + since it does not fully synchronize device, interface, + and driver (not just usbfs) state. + + + + + + + Asynchronous I/O Support + + 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. + + + 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). + + + Each request can specify a realtime signal number + (between SIGRTMIN and SIGRTMAX, inclusive) to request a + signal be sent when the request completes. + + + 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. + + +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[]; +}; + + 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. + + + + + USBDEVFS_DISCARDURB + + TBS + File modification time is not updated by this request. + + + + USBDEVFS_DISCSIGNAL + + TBS + File modification time is not updated by this request. + + + + USBDEVFS_REAPURB + + TBS + File modification time is not updated by this request. + + + + USBDEVFS_REAPURBNDELAY + + TBS + File modification time is not updated by this request. + + + + USBDEVFS_SUBMITURB + + TBS + + + + + + + + + + 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 #include -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 @@ . 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 . + + 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 , + Philip Edelbrock , Kyösti Mälkki , + Mark D. Studebaker + + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 and Philip Edelbrock "); +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 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 + + Original work by + + Copyright (c) 2000 Philip Edelbrock + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 "); +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; ichannels[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; ichan_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 -#include /* needed for MODULE_PARM */ +#include +#include #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 #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 + #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 + * Copyright (C) 2000 Andreas E. Bombe + * 2001-2003 Ben Collins * * 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 #include #include -#ifdef CONFIG_PROC_FS -#include -#endif +#include #include #include @@ -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 + #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 * . Reworked code for initiating bus resets * (long, short, with or without hold-off) + * + * Nandu Santhi + * . Added support for nVidia nForce2 onboard Firewire chipset + * */ #include @@ -90,6 +94,7 @@ #include #include #include +#include #include #include #include @@ -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 "; + "$Rev: 858 $ Ben Collins "; /* 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 #include #include +#include #include #include #include @@ -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 or remove-single-device , where: - * = host (starting at zero for first SCSI adapter) - * = bus (normally zero) - * = target (starting at zero for first SBP-2 device) - * = 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 #include #include +#include #include #include #include @@ -319,14 +298,14 @@ #include "sbp2.h" static char version[] __devinitdata = - "$Rev: 797 $ James Goodwin "; + "$Rev: 846 $ James Goodwin "; /* * 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 */ @@ -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 * 2001-06-16: Bryce Nesbitt * 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, ®s->intrstatus); writel (OHCI_INTR_MIE, ®s->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 . 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 " #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 @@ -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 + +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 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 + */ +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 " +#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 #include +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;idev.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; isf_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 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; isfcount[sfmode]--; + for (j=0; jsfcount[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; isl_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; jsl_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; isl_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; isl_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 + */ +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; isl_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(posoffset+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(posoffset+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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); + 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; priobands; 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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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: