Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
Linus Torvalds [Wed, 16 Sep 2009 15:27:10 +0000 (08:27 -0700)]
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6:
  Driver Core: devtmpfs - kernel-maintained tmpfs-based /dev
  debugfs: Modify default debugfs directory for debugging pktcdvd.
  debugfs: Modified default dir of debugfs for debugging UHCI.
  debugfs: Change debugfs directory of IWMC3200
  debugfs: Change debuhgfs directory of trace-events-sample.h
  debugfs: Fix mount directory of debugfs by default in events.txt
  hpilo: add poll f_op
  hpilo: add interrupt handler
  hpilo: staging for interrupt handling
  driver core: platform_device_add_data(): use kmemdup()
  Driver core: Add support for compatibility classes
  uio: add generic driver for PCI 2.3 devices
  driver-core: move dma-coherent.c from kernel to driver/base
  mem_class: fix bug
  mem_class: use minor as index instead of searching the array
  driver model: constify attribute groups
  UIO: remove 'default n' from Kconfig
  Driver core: Add accessor for device platform data
  Driver core: move dev_get/set_drvdata to drivers/base/dd.c
  Driver core: add new device to bus's list before probing

59 files changed:
Documentation/DocBook/uio-howto.tmpl
Documentation/trace/events.txt
MAINTAINERS
block/genhd.c
drivers/base/Kconfig
drivers/base/Makefile
drivers/base/base.h
drivers/base/bus.c
drivers/base/class.c
drivers/base/core.c
drivers/base/dd.c
drivers/base/devtmpfs.c [new file with mode: 0644]
drivers/base/dma-coherent.c [moved from kernel/dma-coherent.c with 100% similarity]
drivers/base/driver.c
drivers/base/init.c
drivers/base/platform.c
drivers/block/cciss.c
drivers/block/pktcdvd.c
drivers/char/mem.c
drivers/firewire/core-device.c
drivers/firmware/dmi-id.c
drivers/infiniband/hw/ehca/ehca_main.c
drivers/infiniband/hw/ipath/ipath_kernel.h
drivers/infiniband/hw/ipath/ipath_sysfs.c
drivers/input/input.c
drivers/misc/enclosure.c
drivers/misc/hpilo.c
drivers/misc/hpilo.h
drivers/mmc/core/mmc.c
drivers/mmc/core/sd.c
drivers/mtd/mtdcore.c
drivers/net/wireless/iwmc3200wifi/Kconfig
drivers/s390/cio/css.c
drivers/s390/cio/device.c
drivers/s390/net/netiucv.c
drivers/scsi/scsi_priv.h
drivers/scsi/scsi_sysfs.c
drivers/uio/Kconfig
drivers/uio/Makefile
drivers/uio/uio_pci_generic.c [new file with mode: 0644]
drivers/usb/core/endpoint.c
drivers/usb/core/sysfs.c
drivers/usb/core/usb.h
drivers/usb/host/uhci-hcd.c
drivers/uwb/lc-dev.c
fs/partitions/check.c
include/linux/attribute_container.h
include/linux/device.h
include/linux/netdevice.h
include/linux/pci_regs.h
include/linux/shmem_fs.h
include/linux/transport_class.h
init/do_mounts.c
init/main.c
kernel/Makefile
mm/shmem.c
net/bluetooth/hci_sysfs.c
net/core/net-sysfs.c
samples/trace_events/trace-events-sample.h

index 8f6e3b2..4d4ce0e 100644 (file)
        <year>2006-2008</year>
        <holder>Hans-J├╝rgen Koch.</holder>
 </copyright>
+<copyright>
+       <year>2009</year>
+       <holder>Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)</holder>
+</copyright>
 
 <legalnotice>
 <para>
@@ -42,6 +46,13 @@ GPL version 2.
 
 <revhistory>
        <revision>
+       <revnumber>0.9</revnumber>
+       <date>2009-07-16</date>
+       <authorinitials>mst</authorinitials>
+       <revremark>Added generic pci driver
+               </revremark>
+       </revision>
+       <revision>
        <revnumber>0.8</revnumber>
        <date>2008-12-24</date>
        <authorinitials>hjk</authorinitials>
@@ -809,6 +820,158 @@ framework to set up sysfs files for this region. Simply leave it alone.
 
 </chapter>
 
+<chapter id="uio_pci_generic" xreflabel="Using Generic driver for PCI cards">
+<?dbhtml filename="uio_pci_generic.html"?>
+<title>Generic PCI UIO driver</title>
+       <para>
+       The generic driver is a kernel module named uio_pci_generic.
+       It can work with any device compliant to PCI 2.3 (circa 2002) and
+       any compliant PCI Express device. Using this, you only need to
+        write the userspace driver, removing the need to write
+        a hardware-specific kernel module.
+       </para>
+
+<sect1 id="uio_pci_generic_binding">
+<title>Making the driver recognize the device</title>
+       <para>
+Since the driver does not declare any device ids, it will not get loaded
+automatically and will not automatically bind to any devices, you must load it
+and allocate id to the driver yourself. For example:
+       <programlisting>
+ modprobe uio_pci_generic
+ echo &quot;8086 10f5&quot; &gt; /sys/bus/pci/drivers/uio_pci_generic/new_id
+       </programlisting>
+       </para>
+       <para>
+If there already is a hardware specific kernel driver for your device, the
+generic driver still won't bind to it, in this case if you want to use the
+generic driver (why would you?) you'll have to manually unbind the hardware
+specific driver and bind the generic driver, like this:
+       <programlisting>
+    echo -n 0000:00:19.0 &gt; /sys/bus/pci/drivers/e1000e/unbind
+    echo -n 0000:00:19.0 &gt; /sys/bus/pci/drivers/uio_pci_generic/bind
+       </programlisting>
+       </para>
+       <para>
+You can verify that the device has been bound to the driver
+by looking for it in sysfs, for example like the following:
+       <programlisting>
+    ls -l /sys/bus/pci/devices/0000:00:19.0/driver
+       </programlisting>
+Which if successful should print
+       <programlisting>
+  .../0000:00:19.0/driver -&gt; ../../../bus/pci/drivers/uio_pci_generic
+       </programlisting>
+Note that the generic driver will not bind to old PCI 2.2 devices.
+If binding the device failed, run the following command:
+       <programlisting>
+  dmesg
+       </programlisting>
+and look in the output for failure reasons
+       </para>
+</sect1>
+
+<sect1 id="uio_pci_generic_internals">
+<title>Things to know about uio_pci_generic</title>
+       <para>
+Interrupts are handled using the Interrupt Disable bit in the PCI command
+register and Interrupt Status bit in the PCI status register.  All devices
+compliant to PCI 2.3 (circa 2002) and all compliant PCI Express devices should
+support these bits.  uio_pci_generic detects this support, and won't bind to
+devices which do not support the Interrupt Disable Bit in the command register.
+       </para>
+       <para>
+On each interrupt, uio_pci_generic sets the Interrupt Disable bit.
+This prevents the device from generating further interrupts
+until the bit is cleared. The userspace driver should clear this
+bit before blocking and waiting for more interrupts.
+       </para>
+</sect1>
+<sect1 id="uio_pci_generic_userspace">
+<title>Writing userspace driver using uio_pci_generic</title>
+       <para>
+Userspace driver can use pci sysfs interface, or the
+libpci libray that wraps it, to talk to the device and to
+re-enable interrupts by writing to the command register.
+       </para>
+</sect1>
+<sect1 id="uio_pci_generic_example">
+<title>Example code using uio_pci_generic</title>
+       <para>
+Here is some sample userspace driver code using uio_pci_generic:
+<programlisting>
+#include &lt;stdlib.h&gt;
+#include &lt;stdio.h&gt;
+#include &lt;unistd.h&gt;
+#include &lt;sys/types.h&gt;
+#include &lt;sys/stat.h&gt;
+#include &lt;fcntl.h&gt;
+#include &lt;errno.h&gt;
+
+int main()
+{
+       int uiofd;
+       int configfd;
+       int err;
+       int i;
+       unsigned icount;
+       unsigned char command_high;
+
+       uiofd = open(&quot;/dev/uio0&quot;, O_RDONLY);
+       if (uiofd &lt; 0) {
+               perror(&quot;uio open:&quot;);
+               return errno;
+       }
+       configfd = open(&quot;/sys/class/uio/uio0/device/config&quot;, O_RDWR);
+       if (uiofd &lt; 0) {
+               perror(&quot;config open:&quot;);
+               return errno;
+       }
+
+       /* Read and cache command value */
+       err = pread(configfd, &amp;command_high, 1, 5);
+       if (err != 1) {
+               perror(&quot;command config read:&quot;);
+               return errno;
+       }
+       command_high &amp;= ~0x4;
+
+       for(i = 0;; ++i) {
+               /* Print out a message, for debugging. */
+               if (i == 0)
+                       fprintf(stderr, &quot;Started uio test driver.\n&quot;);
+               else
+                       fprintf(stderr, &quot;Interrupts: %d\n&quot;, icount);
+
+               /****************************************/
+               /* Here we got an interrupt from the
+                  device. Do something to it. */
+               /****************************************/
+
+               /* Re-enable interrupts. */
+               err = pwrite(configfd, &amp;command_high, 1, 5);
+               if (err != 1) {
+                       perror(&quot;config write:&quot;);
+                       break;
+               }
+
+               /* Wait for next interrupt. */
+               err = read(uiofd, &amp;icount, 4);
+               if (err != 4) {
+                       perror(&quot;uio read:&quot;);
+                       break;
+               }
+
+       }
+       return errno;
+}
+
+</programlisting>
+       </para>
+</sect1>
+
+</chapter>
+
 <appendix id="app1">
 <title>Further information</title>
 <itemizedlist>
index 2bcc8d4..90e8b33 100644 (file)
@@ -22,12 +22,12 @@ tracing information should be printed.
 ---------------------------------
 
 The events which are available for tracing can be found in the file
-/debug/tracing/available_events.
+/sys/kernel/debug/tracing/available_events.
 
 To enable a particular event, such as 'sched_wakeup', simply echo it
-to /debug/tracing/set_event. For example:
+to /sys/kernel/debug/tracing/set_event. For example:
 
-       # echo sched_wakeup >> /debug/tracing/set_event
+       # echo sched_wakeup >> /sys/kernel/debug/tracing/set_event
 
 [ Note: '>>' is necessary, otherwise it will firstly disable
   all the events. ]
@@ -35,15 +35,15 @@ to /debug/tracing/set_event. For example:
 To disable an event, echo the event name to the set_event file prefixed
 with an exclamation point:
 
-       # echo '!sched_wakeup' >> /debug/tracing/set_event
+       # echo '!sched_wakeup' >> /sys/kernel/debug/tracing/set_event
 
 To disable all events, echo an empty line to the set_event file:
 
-       # echo > /debug/tracing/set_event
+       # echo > /sys/kernel/debug/tracing/set_event
 
 To enable all events, echo '*:*' or '*:' to the set_event file:
 
-       # echo *:* > /debug/tracing/set_event
+       # echo *:* > /sys/kernel/debug/tracing/set_event
 
 The events are organized into subsystems, such as ext4, irq, sched,
 etc., and a full event name looks like this: <subsystem>:<event>.  The
@@ -52,29 +52,29 @@ file.  All of the events in a subsystem can be specified via the syntax
 "<subsystem>:*"; for example, to enable all irq events, you can use the
 command:
 
-       # echo 'irq:*' > /debug/tracing/set_event
+       # echo 'irq:*' > /sys/kernel/debug/tracing/set_event
 
 2.2 Via the 'enable' toggle
 ---------------------------
 
-The events available are also listed in /debug/tracing/events/ hierarchy
+The events available are also listed in /sys/kernel/debug/tracing/events/ hierarchy
 of directories.
 
 To enable event 'sched_wakeup':
 
-       # echo 1 > /debug/tracing/events/sched/sched_wakeup/enable
+       # echo 1 > /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
 
 To disable it:
 
-       # echo 0 > /debug/tracing/events/sched/sched_wakeup/enable
+       # echo 0 > /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
 
 To enable all events in sched subsystem:
 
-       # echo 1 > /debug/tracing/events/sched/enable
+       # echo 1 > /sys/kernel/debug/tracing/events/sched/enable
 
 To eanble all events:
 
-       # echo 1 > /debug/tracing/events/enable
+       # echo 1 > /sys/kernel/debug/tracing/events/enable
 
 When reading one of these enable files, there are four results:
 
index 64b9e44..9117b65 100644 (file)
@@ -2218,6 +2218,13 @@ T:       git git://git.kernel.org/pub/scm/linux/kernel/git/arnd/asm-generic.git
 S:     Maintained
 F:     include/asm-generic
 
+GENERIC UIO DRIVER FOR PCI DEVICES
+M:     Michael S. Tsirkin <mst@redhat.com>
+L:     kvm@vger.kernel.org
+L:     linux-kernel@vger.kernel.org
+S:     Supported
+F:     drivers/uio/uio_pci_generic.c
+
 GFS2 FILE SYSTEM
 M:     Steven Whitehouse <swhiteho@redhat.com>
 L:     cluster-devel@redhat.com
index 5b76bf5..2ad91dd 100644 (file)
@@ -903,7 +903,7 @@ static struct attribute_group disk_attr_group = {
        .attrs = disk_attrs,
 };
 
-static struct attribute_group *disk_attr_groups[] = {
+static const struct attribute_group *disk_attr_groups[] = {
        &disk_attr_group,
        NULL
 };
index 8f006f9..ee37727 100644 (file)
@@ -8,6 +8,31 @@ config UEVENT_HELPER_PATH
          Path to uevent helper program forked by the kernel for
          every uevent.
 
+config DEVTMPFS
+       bool "Create a kernel maintained /dev tmpfs (EXPERIMENTAL)"
+       depends on HOTPLUG && SHMEM && TMPFS
+       help
+         This creates a tmpfs filesystem, and mounts it at bootup
+         and mounts it at /dev. The kernel driver core creates device
+         nodes for all registered devices in that filesystem. All device
+         nodes are owned by root and have the default mode of 0600.
+         Userspace can add and delete the nodes as needed. This is
+         intended to simplify bootup, and make it possible to delay
+         the initial coldplug at bootup done by udev in userspace.
+         It should also provide a simpler way for rescue systems
+         to bring up a kernel with dynamic major/minor numbers.
+         Meaningful symlinks, permissions and device ownership must
+         still be handled by userspace.
+         If unsure, say N here.
+
+config DEVTMPFS_MOUNT
+       bool "Automount devtmpfs at /dev"
+       depends on DEVTMPFS
+       help
+         This will mount devtmpfs at /dev if the kernel mounts the root
+         filesystem. It will not affect initramfs based mounting.
+         If unsure, say N here.
+
 config STANDALONE
        bool "Select only drivers that don't need compile-time external firmware" if EXPERIMENTAL
        default y
index b5b8ba5..c12c7f2 100644 (file)
@@ -4,8 +4,10 @@ obj-y                  := core.o sys.o bus.o dd.o \
                           driver.o class.o platform.o \
                           cpu.o firmware.o init.o map.o devres.o \
                           attribute_container.o transport_class.o
+obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
 obj-y                  += power/
 obj-$(CONFIG_HAS_DMA)  += dma-mapping.o
+obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
 obj-$(CONFIG_ISA)      += isa.o
 obj-$(CONFIG_FW_LOADER)        += firmware_class.o
 obj-$(CONFIG_NUMA)     += node.o
index b528145..2ca7f5b 100644 (file)
@@ -70,6 +70,8 @@ struct class_private {
  * @knode_parent - node in sibling list
  * @knode_driver - node in driver list
  * @knode_bus - node in bus list
+ * @driver_data - private pointer for driver specific info.  Will turn into a
+ * list soon.
  * @device - pointer back to the struct class that this structure is
  * associated with.
  *
@@ -80,6 +82,7 @@ struct device_private {
        struct klist_node knode_parent;
        struct klist_node knode_driver;
        struct klist_node knode_bus;
+       void *driver_data;
        struct device *device;
 };
 #define to_device_private_parent(obj)  \
@@ -89,6 +92,8 @@ struct device_private {
 #define to_device_private_bus(obj)     \
        container_of(obj, struct device_private, knode_bus)
 
+extern int device_private_init(struct device *dev);
+
 /* initialisation functions */
 extern int devices_init(void);
 extern int buses_init(void);
@@ -104,7 +109,7 @@ extern int system_bus_init(void);
 extern int cpu_dev_init(void);
 
 extern int bus_add_device(struct device *dev);
-extern void bus_attach_device(struct device *dev);
+extern void bus_probe_device(struct device *dev);
 extern void bus_remove_device(struct device *dev);
 
 extern int bus_add_driver(struct device_driver *drv);
@@ -134,3 +139,9 @@ static inline void module_add_driver(struct module *mod,
                                     struct device_driver *drv) { }
 static inline void module_remove_driver(struct device_driver *drv) { }
 #endif
+
+#ifdef CONFIG_DEVTMPFS
+extern int devtmpfs_init(void);
+#else
+static inline int devtmpfs_init(void) { return 0; }
+#endif
index 4b04a15..973bf2a 100644 (file)
@@ -459,8 +459,9 @@ static inline void remove_deprecated_bus_links(struct device *dev) { }
  * bus_add_device - add device to bus
  * @dev: device being added
  *
+ * - Add device's bus attributes.
+ * - Create links to device's bus.
  * - Add the device to its bus's list of devices.
- * - Create link to device's bus.
  */
 int bus_add_device(struct device *dev)
 {
@@ -483,6 +484,7 @@ int bus_add_device(struct device *dev)
                error = make_deprecated_bus_links(dev);
                if (error)
                        goto out_deprecated;
+               klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
        }
        return 0;
 
@@ -498,24 +500,19 @@ out_put:
 }
 
 /**
- * bus_attach_device - add device to bus
- * @dev: device tried to attach to a driver
+ * bus_probe_device - probe drivers for a new device
+ * @dev: device to probe
  *
- * - Add device to bus's list of devices.
- * - Try to attach to driver.
+ * - Automatically probe for a driver if the bus allows it.
  */
-void bus_attach_device(struct device *dev)
+void bus_probe_device(struct device *dev)
 {
        struct bus_type *bus = dev->bus;
-       int ret = 0;
+       int ret;
 
-       if (bus) {
-               if (bus->p->drivers_autoprobe)
-                       ret = device_attach(dev);
+       if (bus && bus->p->drivers_autoprobe) {
+               ret = device_attach(dev);
                WARN_ON(ret < 0);
-               if (ret >= 0)
-                       klist_add_tail(&dev->p->knode_bus,
-                                      &bus->p->klist_devices);
        }
 }
 
index eb85e43..161746d 100644 (file)
@@ -488,6 +488,93 @@ void class_interface_unregister(struct class_interface *class_intf)
        class_put(parent);
 }
 
+struct class_compat {
+       struct kobject *kobj;
+};
+
+/**
+ * class_compat_register - register a compatibility class
+ * @name: the name of the class
+ *
+ * Compatibility class are meant as a temporary user-space compatibility
+ * workaround when converting a family of class devices to a bus devices.
+ */
+struct class_compat *class_compat_register(const char *name)
+{
+       struct class_compat *cls;
+
+       cls = kmalloc(sizeof(struct class_compat), GFP_KERNEL);
+       if (!cls)
+               return NULL;
+       cls->kobj = kobject_create_and_add(name, &class_kset->kobj);
+       if (!cls->kobj) {
+               kfree(cls);
+               return NULL;
+       }
+       return cls;
+}
+EXPORT_SYMBOL_GPL(class_compat_register);
+
+/**
+ * class_compat_unregister - unregister a compatibility class
+ * @cls: the class to unregister
+ */
+void class_compat_unregister(struct class_compat *cls)
+{
+       kobject_put(cls->kobj);
+       kfree(cls);
+}
+EXPORT_SYMBOL_GPL(class_compat_unregister);
+
+/**
+ * class_compat_create_link - create a compatibility class device link to
+ *                           a bus device
+ * @cls: the compatibility class
+ * @dev: the target bus device
+ * @device_link: an optional device to which a "device" link should be created
+ */
+int class_compat_create_link(struct class_compat *cls, struct device *dev,
+                            struct device *device_link)
+{
+       int error;
+
+       error = sysfs_create_link(cls->kobj, &dev->kobj, dev_name(dev));
+       if (error)
+               return error;
+
+       /*
+        * Optionally add a "device" link (typically to the parent), as a
+        * class device would have one and we want to provide as much
+        * backwards compatibility as possible.
+        */
+       if (device_link) {
+               error = sysfs_create_link(&dev->kobj, &device_link->kobj,
+                                         "device");
+               if (error)
+                       sysfs_remove_link(cls->kobj, dev_name(dev));
+       }
+
+       return error;
+}
+EXPORT_SYMBOL_GPL(class_compat_create_link);
+
+/**
+ * class_compat_remove_link - remove a compatibility class device link to
+ *                           a bus device
+ * @cls: the compatibility class
+ * @dev: the target bus device
+ * @device_link: an optional device to which a "device" link was previously
+ *              created
+ */
+void class_compat_remove_link(struct class_compat *cls, struct device *dev,
+                             struct device *device_link)
+{
+       if (device_link)
+               sysfs_remove_link(&dev->kobj, "device");
+       sysfs_remove_link(cls->kobj, dev_name(dev));
+}
+EXPORT_SYMBOL_GPL(class_compat_remove_link);
+
 int __init classes_init(void)
 {
        class_kset = kset_create_and_add("class", NULL, NULL);
index 7ecb193..390e664 100644 (file)
@@ -341,7 +341,7 @@ static void device_remove_attributes(struct device *dev,
 }
 
 static int device_add_groups(struct device *dev,
-                            struct attribute_group **groups)
+                            const struct attribute_group **groups)
 {
        int error = 0;
        int i;
@@ -361,7 +361,7 @@ static int device_add_groups(struct device *dev,
 }
 
 static void device_remove_groups(struct device *dev,
-                                struct attribute_group **groups)
+                                const struct attribute_group **groups)
 {
        int i;
 
@@ -843,6 +843,17 @@ static void device_remove_sys_dev_entry(struct device *dev)
        }
 }
 
+int device_private_init(struct device *dev)
+{
+       dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
+       if (!dev->p)
+               return -ENOMEM;
+       dev->p->device = dev;
+       klist_init(&dev->p->klist_children, klist_children_get,
+                  klist_children_put);
+       return 0;
+}
+
 /**
  * device_add - add device to device hierarchy.
  * @dev: device.
@@ -868,14 +879,11 @@ int device_add(struct device *dev)
        if (!dev)
                goto done;
 
-       dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);
        if (!dev->p) {
-               error = -ENOMEM;
-               goto done;
+               error = device_private_init(dev);
+               if (error)
+                       goto done;
        }
-       dev->p->device = dev;
-       klist_init(&dev->p->klist_children, klist_children_get,
-                  klist_children_put);
 
        /*
         * for statically allocated devices, which should all be converted
@@ -921,6 +929,8 @@ int device_add(struct device *dev)
                error = device_create_sys_dev_entry(dev);
                if (error)
                        goto devtattrError;
+
+               devtmpfs_create_node(dev);
        }
 
        error = device_add_class_symlinks(dev);
@@ -945,7 +955,7 @@ int device_add(struct device *dev)
                                             BUS_NOTIFY_ADD_DEVICE, dev);
 
        kobject_uevent(&dev->kobj, KOBJ_ADD);
-       bus_attach_device(dev);
+       bus_probe_device(dev);
        if (parent)
                klist_add_tail(&dev->p->knode_parent,
                               &parent->p->klist_children);
@@ -1067,6 +1077,7 @@ void device_del(struct device *dev)
        if (parent)
                klist_del(&dev->p->knode_parent);
        if (MAJOR(dev->devt)) {
+               devtmpfs_delete_node(dev);
                device_remove_sys_dev_entry(dev);
                device_remove_file(dev, &devt_attr);
        }
index 7b34b3a..979d159 100644 (file)
@@ -11,8 +11,8 @@
  *
  * Copyright (c) 2002-5 Patrick Mochel
  * Copyright (c) 2002-3 Open Source Development Labs
- * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
- * Copyright (c) 2007 Novell Inc.
+ * Copyright (c) 2007-2009 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2007-2009 Novell Inc.
  *
  * This file is released under the GPLv2
  */
@@ -391,3 +391,30 @@ void driver_detach(struct device_driver *drv)
                put_device(dev);
        }
 }
+
+/*
+ * These exports can't be _GPL due to .h files using this within them, and it
+ * might break something that was previously working...
+ */
+void *dev_get_drvdata(const struct device *dev)
+{
+       if (dev && dev->p)
+               return dev->p->driver_data;
+       return NULL;
+}
+EXPORT_SYMBOL(dev_get_drvdata);
+
+void dev_set_drvdata(struct device *dev, void *data)
+{
+       int error;
+
+       if (!dev)
+               return;
+       if (!dev->p) {
+               error = device_private_init(dev);
+               if (error)
+                       return;
+       }
+       dev->p->driver_data = data;
+}
+EXPORT_SYMBOL(dev_set_drvdata);
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
new file mode 100644 (file)
index 0000000..fd488ad
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * devtmpfs - kernel-maintained tmpfs-based /dev
+ *
+ * Copyright (C) 2009, Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * During bootup, before any driver core device is registered,
+ * devtmpfs, a tmpfs-based filesystem is created. Every driver-core
+ * device which requests a device node, will add a node in this
+ * filesystem. The node is named after the the name of the device,
+ * or the susbsytem can provide a custom name. All devices are
+ * owned by root and have a mode of 0600.
+ */
+
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/mount.h>
+#include <linux/device.h>
+#include <linux/genhd.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
+#include <linux/shmem_fs.h>
+#include <linux/cred.h>
+#include <linux/init_task.h>
+
+static struct vfsmount *dev_mnt;
+
+#if defined CONFIG_DEVTMPFS_MOUNT
+static int dev_mount = 1;
+#else
+static int dev_mount;
+#endif
+
+static int __init mount_param(char *str)
+{
+       dev_mount = simple_strtoul(str, NULL, 0);
+       return 1;
+}
+__setup("devtmpfs.mount=", mount_param);
+
+static int dev_get_sb(struct file_system_type *fs_type, int flags,
+                     const char *dev_name, void *data, struct vfsmount *mnt)
+{
+       return get_sb_single(fs_type, flags, data, shmem_fill_super, mnt);
+}
+
+static struct file_system_type dev_fs_type = {
+       .name = "devtmpfs",
+       .get_sb = dev_get_sb,
+       .kill_sb = kill_litter_super,
+};
+
+#ifdef CONFIG_BLOCK
+static inline int is_blockdev(struct device *dev)
+{
+       return dev->class == &block_class;
+}
+#else
+static inline int is_blockdev(struct device *dev) { return 0; }
+#endif
+
+static int dev_mkdir(const char *name, mode_t mode)
+{
+       struct nameidata nd;
+       struct dentry *dentry;
+       int err;
+
+       err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
+                             name, LOOKUP_PARENT, &nd);
+       if (err)
+               return err;
+
+       dentry = lookup_create(&nd, 1);
+       if (!IS_ERR(dentry)) {
+               err = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+               dput(dentry);
+       } else {
+               err = PTR_ERR(dentry);
+       }
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+
+       path_put(&nd.path);
+       return err;
+}
+
+static int create_path(const char *nodepath)
+{
+       char *path;
+       struct nameidata nd;
+       int err = 0;
+
+       path = kstrdup(nodepath, GFP_KERNEL);
+       if (!path)
+               return -ENOMEM;
+
+       err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
+                             path, LOOKUP_PARENT, &nd);
+       if (err == 0) {
+               struct dentry *dentry;
+
+               /* create directory right away */
+               dentry = lookup_create(&nd, 1);
+               if (!IS_ERR(dentry)) {
+                       err = vfs_mkdir(nd.path.dentry->d_inode,
+                                       dentry, 0755);
+                       dput(dentry);
+               }
+               mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+
+               path_put(&nd.path);
+       } else if (err == -ENOENT) {
+               char *s;
+
+               /* parent directories do not exist, create them */
+               s = path;
+               while (1) {
+                       s = strchr(s, '/');
+                       if (!s)
+                               break;
+                       s[0] = '\0';
+                       err = dev_mkdir(path, 0755);
+                       if (err && err != -EEXIST)
+                               break;
+                       s[0] = '/';
+                       s++;
+               }
+       }
+
+       kfree(path);
+       return err;
+}
+
+int devtmpfs_create_node(struct device *dev)
+{
+       const char *tmp = NULL;
+       const char *nodename;
+       const struct cred *curr_cred;
+       mode_t mode;
+       struct nameidata nd;
+       struct dentry *dentry;
+       int err;
+
+       if (!dev_mnt)
+               return 0;
+
+       nodename = device_get_nodename(dev, &tmp);
+       if (!nodename)
+               return -ENOMEM;
+
+       if (is_blockdev(dev))
+               mode = S_IFBLK|0600;
+       else
+               mode = S_IFCHR|0600;
+
+       curr_cred = override_creds(&init_cred);
+       err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
+                             nodename, LOOKUP_PARENT, &nd);
+       if (err == -ENOENT) {
+               /* create missing parent directories */
+               create_path(nodename);
+               err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
+                                     nodename, LOOKUP_PARENT, &nd);
+               if (err)
+                       goto out;
+       }
+
+       dentry = lookup_create(&nd, 0);
+       if (!IS_ERR(dentry)) {
+               err = vfs_mknod(nd.path.dentry->d_inode,
+                               dentry, mode, dev->devt);
+               /* mark as kernel created inode */
+               if (!err)
+                       dentry->d_inode->i_private = &dev_mnt;
+               dput(dentry);
+       } else {
+               err = PTR_ERR(dentry);
+       }
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+
+       path_put(&nd.path);
+out:
+       kfree(tmp);
+       revert_creds(curr_cred);
+       return err;
+}
+
+static int dev_rmdir(const char *name)
+{
+       struct nameidata nd;
+       struct dentry *dentry;
+       int err;
+
+       err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
+                             name, LOOKUP_PARENT, &nd);
+       if (err)
+               return err;
+
+       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
+       if (!IS_ERR(dentry)) {
+               if (dentry->d_inode)
+                       err = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+               else
+                       err = -ENOENT;
+               dput(dentry);
+       } else {
+               err = PTR_ERR(dentry);
+       }
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+
+       path_put(&nd.path);
+       return err;
+}
+
+static int delete_path(const char *nodepath)
+{
+       const char *path;
+       int err = 0;
+
+       path = kstrdup(nodepath, GFP_KERNEL);
+       if (!path)
+               return -ENOMEM;
+
+       while (1) {
+               char *base;
+
+               base = strrchr(path, '/');
+               if (!base)
+                       break;
+               base[0] = '\0';
+               err = dev_rmdir(path);
+               if (err)
+                       break;
+       }
+
+       kfree(path);
+       return err;
+}
+
+static int dev_mynode(struct device *dev, struct inode *inode, struct kstat *stat)
+{
+       /* did we create it */
+       if (inode->i_private != &dev_mnt)
+               return 0;
+
+       /* does the dev_t match */
+       if (is_blockdev(dev)) {
+               if (!S_ISBLK(stat->mode))
+                       return 0;
+       } else {
+               if (!S_ISCHR(stat->mode))
+                       return 0;
+       }
+       if (stat->rdev != dev->devt)
+               return 0;
+
+       /* ours */
+       return 1;
+}
+
+int devtmpfs_delete_node(struct device *dev)
+{
+       const char *tmp = NULL;
+       const char *nodename;
+       const struct cred *curr_cred;
+       struct nameidata nd;
+       struct dentry *dentry;
+       struct kstat stat;
+       int deleted = 1;
+       int err;
+
+       if (!dev_mnt)
+               return 0;
+
+       nodename = device_get_nodename(dev, &tmp);
+       if (!nodename)
+               return -ENOMEM;
+
+       curr_cred = override_creds(&init_cred);
+       err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt,
+                             nodename, LOOKUP_PARENT, &nd);
+       if (err)
+               goto out;
+
+       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       dentry = lookup_one_len(nd.last.name, nd.path.dentry, nd.last.len);
+       if (!IS_ERR(dentry)) {
+               if (dentry->d_inode) {
+                       err = vfs_getattr(nd.path.mnt, dentry, &stat);
+                       if (!err && dev_mynode(dev, dentry->d_inode, &stat)) {
+                               err = vfs_unlink(nd.path.dentry->d_inode,
+                                                dentry);
+                               if (!err || err == -ENOENT)
+                                       deleted = 1;
+                       }
+               } else {
+                       err = -ENOENT;
+               }
+               dput(dentry);
+       } else {
+               err = PTR_ERR(dentry);
+       }
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+
+       path_put(&nd.path);
+       if (deleted && strchr(nodename, '/'))
+               delete_path(nodename);
+out:
+       kfree(tmp);
+       revert_creds(curr_cred);
+       return err;
+}
+
+/*
+ * If configured, or requested by the commandline, devtmpfs will be
+ * auto-mounted after the kernel mounted the root filesystem.
+ */
+int devtmpfs_mount(const char *mountpoint)
+{
+       struct path path;
+       int err;
+
+       if (!dev_mount)
+               return 0;
+
+       if (!dev_mnt)
+               return 0;
+
+       err = kern_path(mountpoint, LOOKUP_FOLLOW, &path);
+       if (err)
+               return err;
+       err = do_add_mount(dev_mnt, &path, 0, NULL);
+       if (err)
+               printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
+       else
+               printk(KERN_INFO "devtmpfs: mounted\n");
+       path_put(&path);
+       return err;
+}
+
+/*
+ * Create devtmpfs instance, driver-core devices will add their device
+ * nodes here.
+ */
+int __init devtmpfs_init(void)
+{
+       int err;
+       struct vfsmount *mnt;
+
+       err = register_filesystem(&dev_fs_type);
+       if (err) {
+               printk(KERN_ERR "devtmpfs: unable to register devtmpfs "
+                      "type %i\n", err);
+               return err;
+       }
+
+       mnt = kern_mount(&dev_fs_type);
+       if (IS_ERR(mnt)) {
+               err = PTR_ERR(mnt);
+               printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err);
+               unregister_filesystem(&dev_fs_type);
+               return err;
+       }
+       dev_mnt = mnt;
+
+       printk(KERN_INFO "devtmpfs: initialized\n");
+       return 0;
+}
index 8ae0f63..ed2ebd3 100644 (file)
@@ -181,7 +181,7 @@ void put_driver(struct device_driver *drv)
 EXPORT_SYMBOL_GPL(put_driver);
 
 static int driver_add_groups(struct device_driver *drv,
-                            struct attribute_group **groups)
+                            const struct attribute_group **groups)
 {
        int error = 0;
        int i;
@@ -201,7 +201,7 @@ static int driver_add_groups(struct device_driver *drv,
 }
 
 static void driver_remove_groups(struct device_driver *drv,
-                                struct attribute_group **groups)
+                                const struct attribute_group **groups)
 {
        int i;
 
index 7bd9b6a..c8a934e 100644 (file)
@@ -20,6 +20,7 @@
 void __init driver_init(void)
 {
        /* These are the core pieces */
+       devtmpfs_init();
        devices_init();
        buses_init();
        classes_init();
index 0f7d434..ed156a1 100644 (file)
@@ -10,6 +10,7 @@
  * information.
  */
 
+#include <linux/string.h>
 #include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/init.h>
@@ -213,14 +214,13 @@ EXPORT_SYMBOL_GPL(platform_device_add_resources);
 int platform_device_add_data(struct platform_device *pdev, const void *data,
                             size_t size)
 {
-       void *d;
+       void *d = kmemdup(data, size, GFP_KERNEL);
 
-       d = kmalloc(size, GFP_KERNEL);
        if (d) {
-               memcpy(d, data, size);
                pdev->dev.platform_data = d;
+               return 0;
        }
-       return d ? 0 : -ENOMEM;
+       return -ENOMEM;
 }
 EXPORT_SYMBOL_GPL(platform_device_add_data);
 
index 0589dfb..d8372b4 100644 (file)
@@ -572,7 +572,7 @@ static struct attribute_group cciss_dev_attr_group = {
        .attrs = cciss_dev_attrs,
 };
 
-static struct attribute_group *cciss_dev_attr_groups[] = {
+static const struct attribute_group *cciss_dev_attr_groups[] = {
        &cciss_dev_attr_group,
        NULL
 };
index 99a506f..95f11cd 100644 (file)
@@ -92,7 +92,7 @@ static struct mutex ctl_mutex;        /* Serialize open/close/setup/teardown */
 static mempool_t *psd_pool;
 
 static struct class    *class_pktcdvd = NULL;    /* /sys/class/pktcdvd */
-static struct dentry   *pkt_debugfs_root = NULL; /* /debug/pktcdvd */
+static struct dentry   *pkt_debugfs_root = NULL; /* /sys/kernel/debug/pktcdvd */
 
 /* forward declaration */
 static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev);
index 645237b..0491cdf 100644 (file)
@@ -864,71 +864,67 @@ static const struct file_operations kmsg_fops = {
        .write =        kmsg_write,
 };
 
-static const struct {
-       unsigned int            minor;
-       char                    *name;
-       umode_t                 mode;
-       const struct file_operations    *fops;
-       struct backing_dev_info *dev_info;
-} devlist[] = { /* list of minor devices */
-       {1, "mem",     S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops,
-               &directly_mappable_cdev_bdi},
+static const struct memdev {
+       const char *name;
+       const struct file_operations *fops;
+       struct backing_dev_info *dev_info;
+} devlist[] = {
+       [ 1] = { "mem", &mem_fops, &directly_mappable_cdev_bdi },
 #ifdef CONFIG_DEVKMEM
-       {2, "kmem",    S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops,
-               &directly_mappable_cdev_bdi},
+       [ 2] = { "kmem", &kmem_fops, &directly_mappable_cdev_bdi },
 #endif
-       {3, "null",    S_IRUGO | S_IWUGO,           &null_fops, NULL},
+       [ 3] = {"null", &null_fops, NULL },
 #ifdef CONFIG_DEVPORT
-       {4, "port",    S_IRUSR | S_IWUSR | S_IRGRP, &port_fops, NULL},
+       [ 4] = { "port", &port_fops, NULL },
 #endif
-       {5, "zero",    S_IRUGO | S_IWUGO,           &zero_fops, &zero_bdi},
-       {7, "full",    S_IRUGO | S_IWUGO,           &full_fops, NULL},
-       {8, "random",  S_IRUGO | S_IWUSR,           &random_fops, NULL},
-       {9, "urandom", S_IRUGO | S_IWUSR,           &urandom_fops, NULL},
-       {11,"kmsg",    S_IRUGO | S_IWUSR,           &kmsg_fops, NULL},
+       [ 5] = { "zero", &zero_fops, &zero_bdi },
+       [ 7] = { "full", &full_fops, NULL },
+       [ 8] = { "random", &random_fops, NULL },
+       [ 9] = { "urandom", &urandom_fops, NULL },
+       [11] = { "kmsg", &kmsg_fops, NULL },
 #ifdef CONFIG_CRASH_DUMP
-       {12,"oldmem",    S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops, NULL},
+       [12] = { "oldmem", &oldmem_fops, NULL },
 #endif
 };
 
 static int memory_open(struct inode *inode, struct file *filp)
 {
-       int ret = 0;
-       int i;
+       int minor;
+       const struct memdev *dev;
+       int ret = -ENXIO;
 
        lock_kernel();
 
-       for (i = 0; i < ARRAY_SIZE(devlist); i++) {
-               if (devlist[i].minor == iminor(inode)) {
-                       filp->f_op = devlist[i].fops;
-                       if (devlist[i].dev_info) {
-                               filp->f_mapping->backing_dev_info =
-                                       devlist[i].dev_info;
-                       }
+       minor = iminor(inode);
+       if (minor >= ARRAY_SIZE(devlist))
+               goto out;
 
-                       break;
-               }
-       }
+       dev = &devlist[minor];
+       if (!dev->fops)
+               goto out;
 
-       if (i == ARRAY_SIZE(devlist))
-               ret = -ENXIO;
-       else
-               if (filp->f_op && filp->f_op->open)
-                       ret = filp->f_op->open(inode, filp);
+       filp->f_op = dev->fops;
+       if (dev->dev_info)
+               filp->f_mapping->backing_dev_info = dev->dev_info;
 
+       if (dev->fops->open)
+               ret = dev->fops->open(inode, filp);
+       else
+               ret = 0;
+out:
        unlock_kernel();
        return ret;
 }
 
 static const struct file_operations memory_fops = {
-       .open           = memory_open,  /* just a selector for the real open */
+       .open           = memory_open,
 };
 
 static struct class *mem_class;
 
 static int __init chr_dev_init(void)
 {
-       int i;
+       int minor;
        int err;
 
        err = bdi_init(&zero_bdi);
@@ -939,10 +935,12 @@ static int __init chr_dev_init(void)
                printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 
        mem_class = class_create(THIS_MODULE, "mem");
-       for (i = 0; i < ARRAY_SIZE(devlist); i++)
-               device_create(mem_class, NULL,
-                             MKDEV(MEM_MAJOR, devlist[i].minor), NULL,
-                             devlist[i].name);
+       for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
+               if (!devlist[minor].name)
+                       continue;
+               device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
+                             NULL, devlist[minor].name);
+       }
 
        return 0;
 }
index 97e656a..9d0dfcb 100644 (file)
@@ -312,7 +312,7 @@ static void init_fw_attribute_group(struct device *dev,
        group->groups[0] = &group->group;
        group->groups[1] = NULL;
        group->group.attrs = group->attrs;
-       dev->groups = group->groups;
+       dev->groups = (const struct attribute_group **) group->groups;
 }
 
 static ssize_t modalias_show(struct device *dev,
index 5a76d05..dbdf6fa 100644 (file)
@@ -139,7 +139,7 @@ static struct attribute_group sys_dmi_attribute_group = {
        .attrs = sys_dmi_attributes,
 };
 
-static struct attribute_group* sys_dmi_attribute_groups[] = {
+static const struct attribute_group* sys_dmi_attribute_groups[] = {
        &sys_dmi_attribute_group,
        NULL
 };
index 5b635aa..fb2d83c 100644 (file)
@@ -623,7 +623,7 @@ static struct attribute_group ehca_drv_attr_grp = {
        .attrs = ehca_drv_attrs
 };
 
-static struct attribute_group *ehca_drv_attr_groups[] = {
+static const struct attribute_group *ehca_drv_attr_groups[] = {
        &ehca_drv_attr_grp,
        NULL,
 };
index 6ba4861..b3d7efc 100644 (file)
@@ -1286,7 +1286,7 @@ struct device_driver;
 
 extern const char ib_ipath_version[];
 
-extern struct attribute_group *ipath_driver_attr_groups[];
+extern const struct attribute_group *ipath_driver_attr_groups[];
 
 int ipath_device_create_group(struct device *, struct ipath_devdata *);
 void ipath_device_remove_group(struct device *, struct ipath_devdata *);
index a6c8efb..b8cb2f1 100644 (file)
@@ -1069,7 +1069,7 @@ static ssize_t show_tempsense(struct device *dev,
        return ret;
 }
 
-struct attribute_group *ipath_driver_attr_groups[] = {
+const struct attribute_group *ipath_driver_attr_groups[] = {
        &driver_attr_group,
        NULL,
 };
index 7c237e6..851791d 100644 (file)
@@ -1144,7 +1144,7 @@ static struct attribute_group input_dev_caps_attr_group = {
        .attrs  = input_dev_caps_attrs,
 };
 
-static struct attribute_group *input_dev_attr_groups[] = {
+static const struct attribute_group *input_dev_attr_groups[] = {
        &input_dev_attr_group,
        &input_dev_id_attr_group,
        &input_dev_caps_attr_group,
index 7b03930..e9eae4a 100644 (file)
@@ -238,7 +238,7 @@ static void enclosure_component_release(struct device *dev)
        put_device(dev->parent);
 }
 
-static struct attribute_group *enclosure_groups[];
+static const struct attribute_group *enclosure_groups[];
 
 /**
  * enclosure_component_register - add a particular component to an enclosure
@@ -536,7 +536,7 @@ static struct attribute_group enclosure_group = {
        .attrs = enclosure_component_attrs,
 };
 
-static struct attribute_group *enclosure_groups[] = {
+static const struct attribute_group *enclosure_groups[] = {
        &enclosure_group,
        NULL
 };
index 880ccf3..1ad27c6 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/pci.h>
+#include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/file.h>
@@ -21,6 +22,8 @@
 #include <linux/delay.h>
 #include <linux/uaccess.h>
 #include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
 #include "hpilo.h"
 
 static struct class *ilo_class;
@@ -61,9 +64,10 @@ static inline int desc_mem_sz(int nr_entry)
 static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry)
 {
        struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
+       unsigned long flags;
        int ret = 0;
 
-       spin_lock(&hw->fifo_lock);
+       spin_lock_irqsave(&hw->fifo_lock, flags);
        if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask]
              & ENTRY_MASK_O)) {
                fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |=
@@ -71,7 +75,7 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry)
                fifo_q->tail += 1;
                ret = 1;
        }
-       spin_unlock(&hw->fifo_lock);
+       spin_unlock_irqrestore(&hw->fifo_lock, flags);
 
        return ret;
 }
@@ -79,10 +83,11 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry)
 static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
 {
        struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
+       unsigned long flags;
        int ret = 0;
        u64 c;
 
-       spin_lock(&hw->fifo_lock);
+       spin_lock_irqsave(&hw->fifo_lock, flags);
        c = fifo_q->fifobar[fifo_q->head & fifo_q->imask];
        if (c & ENTRY_MASK_C) {
                if (entry)
@@ -93,7 +98,23 @@ static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry)
                fifo_q->head += 1;
                ret = 1;
        }
-       spin_unlock(&hw->fifo_lock);
+       spin_unlock_irqrestore(&hw->fifo_lock, flags);
+
+       return ret;
+}
+
+static int fifo_check_recv(struct ilo_hwinfo *hw, char *fifobar)
+{
+       struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar);
+       unsigned long flags;
+       int ret = 0;
+       u64 c;
+
+       spin_lock_irqsave(&hw->fifo_lock, flags);
+       c = fifo_q->fifobar[fifo_q->head & fifo_q->imask];
+       if (c & ENTRY_MASK_C)
+               ret = 1;
+       spin_unlock_irqrestore(&hw->fifo_lock, flags);
 
        return ret;
 }
@@ -142,6 +163,13 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb,
        return ret;
 }
 
+static int ilo_pkt_recv(struct ilo_hwinfo *hw, struct ccb *ccb)
+{
+       char *fifobar = ccb->ccb_u3.recv_fifobar;
+
+       return fifo_check_recv(hw, fifobar);
+}
+
 static inline void doorbell_set(struct ccb *ccb)
 {
        iowrite8(1, ccb->ccb_u5.db_base);
@@ -151,6 +179,7 @@ static inline void doorbell_clr(struct ccb *ccb)
 {
        iowrite8(2, ccb->ccb_u5.db_base);
 }
+
 static inline int ctrl_set(int l2sz, int idxmask, int desclim)
 {
        int active = 0, go = 1;
@@ -160,6 +189,7 @@ static inline int ctrl_set(int l2sz, int idxmask, int desclim)
               active << CTRL_BITPOS_A |
               go << CTRL_BITPOS_G;
 }
+
 static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz)
 {
        /* for simplicity, use the same parameters for send and recv ctrls */
@@ -192,13 +222,10 @@ static void fifo_setup(void *base_addr, int nr_entry)
 
 static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data)
 {
-       struct ccb *driver_ccb;
-       struct ccb __iomem *device_ccb;
+       struct ccb *driver_ccb = &data->driver_ccb;
+       struct ccb __iomem *device_ccb = data->mapped_ccb;
        int retries;
 
-       driver_ccb = &data->driver_ccb;
-       device_ccb = data->mapped_ccb;
-
        /* complicated dance to tell the hw we are stopping */
        doorbell_clr(driver_ccb);
        iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G),
@@ -225,26 +252,22 @@ static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data)
        pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa);
 }
 
-static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
+static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
 {
        char *dma_va, *dma_pa;
-       int pkt_id, pkt_sz, i, error;
        struct ccb *driver_ccb, *ilo_ccb;
-       struct pci_dev *pdev;
 
        driver_ccb = &data->driver_ccb;
        ilo_ccb = &data->ilo_ccb;
-       pdev = hw->ilo_dev;
 
        data->dma_size = 2 * fifo_sz(NR_QENTRY) +
                         2 * desc_mem_sz(NR_QENTRY) +
                         ILO_START_ALIGN + ILO_CACHE_SZ;
 
-       error = -ENOMEM;
-       data->dma_va = pci_alloc_consistent(pdev, data->dma_size,
+       data->dma_va = pci_alloc_consistent(hw->ilo_dev, data->dma_size,
                                            &data->dma_pa);
        if (!data->dma_va)
-               goto out;
+               return -ENOMEM;
 
        dma_va = (char *)data->dma_va;
        dma_pa = (char *)data->dma_pa;
@@ -290,10 +313,18 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
        driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE);
        ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */
 
+       return 0;
+}
+
+static void ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
+{
+       int pkt_id, pkt_sz;
+       struct ccb *driver_ccb = &data->driver_ccb;
+
        /* copy the ccb with physical addrs to device memory */
        data->mapped_ccb = (struct ccb __iomem *)
                                (hw->ram_vaddr + (slot * ILOHW_CCB_SZ));
-       memcpy_toio(data->mapped_ccb, ilo_ccb, sizeof(struct ccb));
+       memcpy_toio(data->mapped_ccb, &data->ilo_ccb, sizeof(struct ccb));
 
        /* put packets on the send and receive queues */
        pkt_sz = 0;
@@ -306,7 +337,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
        for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++)
                ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz);
 
+       /* the ccb is ready to use */
        doorbell_clr(driver_ccb);
+}
+
+static int ilo_ccb_verify(struct ilo_hwinfo *hw, struct ccb_data *data)
+{
+       int pkt_id, i;
+       struct ccb *driver_ccb = &data->driver_ccb;
 
        /* make sure iLO is really handling requests */
        for (i = MAX_WAIT; i > 0; i--) {
@@ -315,20 +353,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot)
                udelay(WAIT_TIME);
        }
 
-       if (i) {
-               ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0);
-               doorbell_set(driver_ccb);
-       } else {
-               dev_err(&pdev->dev, "Open could not dequeue a packet\n");
-               error = -EBUSY;
-               goto free;
+       if (i == 0) {
+               dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n");
+               return -EBUSY;
        }
 
+       ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0);
+       doorbell_set(driver_ccb);
        return 0;
-free:
-       ilo_ccb_close(pdev, data);
-out:
-       return error;
 }
 
 static inline int is_channel_reset(struct ccb *ccb)
@@ -343,19 +375,45 @@ static inline void set_channel_reset(struct ccb *ccb)
        FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1;
 }
 
+static inline int get_device_outbound(struct ilo_hwinfo *hw)
+{
+       return ioread32(&hw->mmio_vaddr[DB_OUT]);
+}
+
+static inline int is_db_reset(int db_out)
+{
+       return db_out & (1 << DB_RESET);
+}
+
 static inline int is_device_reset(struct ilo_hwinfo *hw)
 {
        /* check for global reset condition */
-       return ioread32(&hw->mmio_vaddr[DB_OUT]) & (1 << DB_RESET);
+       return is_db_reset(get_device_outbound(hw));
+}
+
+static inline void clear_pending_db(struct ilo_hwinfo *hw, int clr)
+{
+       iowrite32(clr, &hw->mmio_vaddr[DB_OUT]);
 }
 
 static inline void clear_device(struct ilo_hwinfo *hw)
 {
        /* clear the device (reset bits, pending channel entries) */
-       iowrite32(-1, &hw->mmio_vaddr[DB_OUT]);
+       clear_pending_db(hw, -1);
+}
+
+static inline void ilo_enable_interrupts(struct ilo_hwinfo *hw)
+{
+       iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]);
 }
 
-static void ilo_locked_reset(struct ilo_hwinfo *hw)
+static inline void ilo_disable_interrupts(struct ilo_hwinfo *hw)
+{
+       iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) & ~1,
+                &hw->mmio_vaddr[DB_IRQ]);
+}
+
+static void ilo_set_reset(struct ilo_hwinfo *hw)
 {
        int slot;
 
@@ -368,40 +426,22 @@ static void ilo_locked_reset(struct ilo_hwinfo *hw)
                        continue;
                set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb);
        }
-
-       clear_device(hw);
-}
-
-static void ilo_reset(struct ilo_hwinfo *hw)
-{
-       spin_lock(&hw->alloc_lock);
-
-       /* reset might have been handled after lock was taken */
-       if (is_device_reset(hw))
-               ilo_locked_reset(hw);
-
-       spin_unlock(&hw->alloc_lock);
 }
 
 static ssize_t ilo_read(struct file *fp, char __user *buf,
                        size_t len, loff_t *off)
 {
        int err, found, cnt, pkt_id, pkt_len;
-       struct ccb_data *data;
-       struct ccb *driver_ccb;
-       struct ilo_hwinfo *hw;
+       struct ccb_data *data = fp->private_data;
+       struct ccb *driver_ccb = &data->driver_ccb;
+       struct ilo_hwinfo *hw = data->ilo_hw;
        void *pkt;
 
-       data = fp->private_data;
-       driver_ccb = &data->driver_ccb;
-       hw = data->ilo_hw;
-
-       if (is_device_reset(hw) || is_channel_reset(driver_ccb)) {
+       if (is_channel_reset(driver_ccb)) {
                /*
                 * If the device has been reset, applications
                 * need to close and reopen all ccbs.
                 */
-               ilo_reset(hw);
                return -ENODEV;
        }
 
@@ -442,23 +482,13 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf,
                         size_t len, loff_t *off)
 {
        int err, pkt_id, pkt_len;
-       struct ccb_data *data;
-       struct ccb *driver_ccb;
-       struct ilo_hwinfo *hw;
+       struct ccb_data *data = fp->private_data;
+       struct ccb *driver_ccb = &data->driver_ccb;
+       struct ilo_hwinfo *hw = data->ilo_hw;
        void *pkt;
 
-       data = fp->private_data;
-       driver_ccb = &data->driver_ccb;
-       hw = data->ilo_hw;
-
-       if (is_device_reset(hw) || is_channel_reset(driver_ccb)) {
-               /*
-                * If the device has been reset, applications
-                * need to close and reopen all ccbs.
-                */
-               ilo_reset(hw);
+       if (is_channel_reset(driver_ccb))
                return -ENODEV;
-       }
 
        /* get a packet to send the user command */
        if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt))
@@ -480,32 +510,48 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf,
        return err ? -EFAULT : len;
 }
 
+static unsigned int ilo_poll(struct file *fp, poll_table *wait)
+{
+       struct ccb_data *data = fp->private_data;
+       struct ccb *driver_ccb = &data->driver_ccb;
+
+       poll_wait(fp, &data->ccb_waitq, wait);
+
+       if (is_channel_reset(driver_ccb))
+               return POLLERR;
+       else if (ilo_pkt_recv(data->ilo_hw, driver_ccb))
+               return POLLIN | POLLRDNORM;
+
+       return 0;
+}
+
 static int ilo_close(struct inode *ip, struct file *fp)
 {
        int slot;
        struct ccb_data *data;
        struct ilo_hwinfo *hw;
+       unsigned long flags;
 
        slot = iminor(ip) % MAX_CCB;
        hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);
 
-       spin_lock(&hw->alloc_lock);
-
-       if (is_device_reset(hw))
-               ilo_locked_reset(hw);
+       spin_lock(&hw->open_lock);
 
        if (hw->ccb_alloc[slot]->ccb_cnt == 1) {
 
                data = fp->private_data;
 
+               spin_lock_irqsave(&hw->alloc_lock, flags);
+               hw->ccb_alloc[slot] = NULL;
+               spin_unlock_irqrestore(&hw->alloc_lock, flags);
+
                ilo_ccb_close(hw->ilo_dev, data);
 
                kfree(data);
-               hw->ccb_alloc[slot] = NULL;
        } else
                hw->ccb_alloc[slot]->ccb_cnt--;
 
-       spin_unlock(&hw->alloc_lock);
+       spin_unlock(&hw->open_lock);
 
        return 0;
 }
@@ -515,6 +561,7 @@ static int ilo_open(struct inode *ip, struct file *fp)
        int slot, error;
        struct ccb_data *data;
        struct ilo_hwinfo *hw;
+       unsigned long flags;
 
        slot = iminor(ip) % MAX_CCB;
        hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev);
@@ -524,22 +571,42 @@ static int ilo_open(struct inode *ip, struct file *fp)
        if (!data)
                return -ENOMEM;
 
-       spin_lock(&hw->alloc_lock);
-
-       if (is_device_reset(hw))
-               ilo_locked_reset(hw);
+       spin_lock(&hw->open_lock);
 
        /* each fd private_data holds sw/hw view of ccb */
        if (hw->ccb_alloc[slot] == NULL) {
                /* create a channel control block for this minor */
-               error = ilo_ccb_open(hw, data, slot);
-               if (!error) {
-                       hw->ccb_alloc[slot] = data;
-                       hw->ccb_alloc[slot]->ccb_cnt = 1;
-                       hw->ccb_alloc[slot]->ccb_excl = fp->f_flags & O_EXCL;
-                       hw->ccb_alloc[slot]->ilo_hw = hw;
-               } else
+               error = ilo_ccb_setup(hw, data, slot);
+               if (error) {
                        kfree(data);
+                       goto out;
+               }
+
+               data->ccb_cnt = 1;
+               data->ccb_excl = fp->f_flags & O_EXCL;
+               data->ilo_hw = hw;
+               init_waitqueue_head(&data->ccb_waitq);
+
+               /* write the ccb to hw */
+               spin_lock_irqsave(&hw->alloc_lock, flags);
+               ilo_ccb_open(hw, data, slot);
+               hw->ccb_alloc[slot] = data;
+               spin_unlock_irqrestore(&hw->alloc_lock, flags);
+
+               /* make sure the channel is functional */
+               error = ilo_ccb_verify(hw, data);
+               if (error) {
+
+                       spin_lock_irqsave(&hw->alloc_lock, flags);
+                       hw->ccb_alloc[slot] = NULL;
+                       spin_unlock_irqrestore(&hw->alloc_lock, flags);
+
+                       ilo_ccb_close(hw->ilo_dev, data);
+
+                       kfree(data);
+                       goto out;
+               }
+
        } else {
                kfree(data);
                if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) {
@@ -554,7 +621,8 @@ static int ilo_open(struct inode *ip, struct file *fp)
                        error = 0;
                }
        }
-       spin_unlock(&hw->alloc_lock);
+out:
+       spin_unlock(&hw->open_lock);
 
        if (!error)
                fp->private_data = hw->ccb_alloc[slot];
@@ -566,10 +634,46 @@ static const struct file_operations ilo_fops = {
        .owner          = THIS_MODULE,
        .read           = ilo_read,
        .write          = ilo_write,
+       .poll           = ilo_poll,
        .open           = ilo_open,
        .release        = ilo_close,
 };
 
+static irqreturn_t ilo_isr(int irq, void *data)
+{
+       struct ilo_hwinfo *hw = data;
+       int pending, i;
+
+       spin_lock(&hw->alloc_lock);
+
+       /* check for ccbs which have data */
+       pending = get_device_outbound(hw);
+       if (!pending) {
+               spin_unlock(&hw->alloc_lock);
+               return IRQ_NONE;
+       }
+
+       if (is_db_reset(pending)) {
+               /* wake up all ccbs if the device was reset */
+               pending = -1;
+               ilo_set_reset(hw);
+       }
+
+       for (i = 0; i < MAX_CCB; i++) {
+               if (!hw->ccb_alloc[i])
+                       continue;
+               if (pending & (1 << i))
+                       wake_up_interruptible(&hw->ccb_alloc[i]->ccb_waitq);
+       }
+
+       /* clear the device of the channels that have been handled */
+       clear_pending_db(hw, pending);
+
+       spin_unlock(&hw->alloc_lock);
+
+       return IRQ_HANDLED;
+}
+
 static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw)
 {
        pci_iounmap(pdev, hw->db_vaddr);
@@ -623,6 +727,8 @@ static void ilo_remove(struct pci_dev *pdev)
                device_destroy(ilo_class, MKDEV(ilo_major, i));
 
        cdev_del(&ilo_hw->cdev);
+       ilo_disable_interrupts(ilo_hw);
+       free_irq(pdev->irq, ilo_hw);
        ilo_unmap_device(pdev, ilo_hw);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
@@ -658,6 +764,7 @@ static int __devinit ilo_probe(struct pci_dev *pdev,
        ilo_hw->ilo_dev = pdev;
        spin_lock_init(&ilo_hw->alloc_lock);
        spin_lock_init(&ilo_hw->fifo_lock);
+       spin_lock_init(&ilo_hw->open_lock);
 
        error = pci_enable_device(pdev);
        if (error)
@@ -676,13 +783,19 @@ static int __devinit ilo_probe(struct pci_dev *pdev,
        pci_set_drvdata(pdev, ilo_hw);
        clear_device(ilo_hw);
 
+       error = request_irq(pdev->irq, ilo_isr, IRQF_SHARED, "hpilo", ilo_hw);
+       if (error)
+               goto unmap;
+
+       ilo_enable_interrupts(ilo_hw);
+
        cdev_init(&ilo_hw->cdev, &ilo_fops);
        ilo_hw->cdev.owner = THIS_MODULE;
        start = devnum * MAX_CCB;
        error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB);
        if (error) {
                dev_err(&pdev->dev, "Could not add cdev\n");
-               goto unmap;
+               goto remove_isr;
        }
 
        for (minor = 0 ; minor < MAX_CCB; minor++) {
@@ -695,6 +808,9 @@ static int __devinit ilo_probe(struct pci_dev *pdev,
        }
 
        return 0;
+remove_isr:
+       ilo_disable_interrupts(ilo_hw);
+       free_irq(pdev->irq, ilo_hw);
 unmap:
        ilo_unmap_device(pdev, ilo_hw);
 free_regions:
@@ -759,7 +875,7 @@ static void __exit ilo_exit(void)
        class_destroy(ilo_class);
 }
 
-MODULE_VERSION("1.1");
+MODULE_VERSION("1.2");
 MODULE_ALIAS(ILO_NAME);
 MODULE_DESCRIPTION(ILO_NAME);
 MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>");
index 03a14c8..3857605 100644 (file)
@@ -46,11 +46,14 @@ struct ilo_hwinfo {
 
        spinlock_t alloc_lock;
        spinlock_t fifo_lock;
+       spinlock_t open_lock;
 
        struct cdev cdev;
 };
 
-/* offset from mmio_vaddr */
+/* offset from mmio_vaddr for enabling doorbell interrupts */
+#define DB_IRQ         0xB2
+/* offset from mmio_vaddr for outbound communications */
 #define DB_OUT         0xD4
 /* DB_OUT reset bit */
 #define DB_RESET       26
@@ -131,6 +134,9 @@ struct ccb_data {
        /* pointer to hardware device info */
        struct ilo_hwinfo *ilo_hw;
 
+       /* queue for this ccb to wait for recv data */
+       wait_queue_head_t ccb_waitq;
+
        /* usage count, to allow for shared ccb's */
        int         ccb_cnt;
 
index 06084db..2fb9d5f 100644 (file)
@@ -276,7 +276,7 @@ static struct attribute_group mmc_std_attr_group = {
        .attrs = mmc_std_attrs,
 };
 
-static struct attribute_group *mmc_attr_groups[] = {
+static const struct attribute_group *mmc_attr_groups[] = {
        &mmc_std_attr_group,
        NULL,
 };
index cd81c39..7ad646f 100644 (file)
@@ -314,7 +314,7 @@ static struct attribute_group sd_std_attr_group = {
        .attrs = sd_std_attrs,
 };
 
-static struct attribute_group *sd_attr_groups[] = {
+static const struct attribute_group *sd_attr_groups[] = {
        &sd_std_attr_group,
        NULL,
 };
index 00ebf7a..69007a6 100644 (file)
@@ -217,7 +217,7 @@ struct attribute_group mtd_group = {
        .attrs          = mtd_attrs,
 };
 
-struct attribute_group *mtd_groups[] = {
+const struct attribute_group *mtd_groups[] = {
        &mtd_group,
        NULL,
 };
index c62da43..c25a043 100644 (file)
@@ -24,8 +24,8 @@ config IWM_DEBUG
          To see the list of debug modules and levels, see iwm/debug.h
 
          For example, if you want the full MLME debug output:
-         echo 0xff > /debug/iwm/phyN/debug/mlme
+         echo 0xff > /sys/kernel/debug/iwm/phyN/debug/mlme
 
          Or, if you want the full debug, for all modules:
-         echo 0xff > /debug/iwm/phyN/debug/level
-         echo 0xff > /debug/iwm/phyN/debug/modules
+         echo 0xff > /sys/kernel/debug/iwm/phyN/debug/level
+         echo 0xff > /sys/kernel/debug/iwm/phyN/debug/modules
index e995123..393c73c 100644 (file)
@@ -266,7 +266,7 @@ static struct attribute_group subch_attr_group = {
        .attrs = subch_attrs,
 };
 
-static struct attribute_group *default_subch_attr_groups[] = {
+static const struct attribute_group *default_subch_attr_groups[] = {
        &subch_attr_group,
        NULL,
 };
index 0f95405..6527f3f 100644 (file)
@@ -656,7 +656,7 @@ static struct attribute_group ccwdev_attr_group = {
        .attrs = ccwdev_attrs,
 };
 
-static struct attribute_group *ccwdev_attr_groups[] = {
+static const struct attribute_group *ccwdev_attr_groups[] = {
        &ccwdev_attr_group,
        NULL,
 };
index 9215fbb..a4b2c57 100644 (file)
@@ -2159,7 +2159,7 @@ static struct attribute_group netiucv_drv_attr_group = {
        .attrs = netiucv_drv_attrs,
 };
 
-static struct attribute_group *netiucv_drv_attr_groups[] = {
+static const struct attribute_group *netiucv_drv_attr_groups[] = {
        &netiucv_drv_attr_group,
        NULL,
 };
index 021e503..1fbf7c7 100644 (file)
@@ -132,7 +132,7 @@ extern struct scsi_transport_template blank_transport_template;
 extern void __scsi_remove_device(struct scsi_device *);
 
 extern struct bus_type scsi_bus_type;
-extern struct attribute_group *scsi_sysfs_shost_attr_groups[];
+extern const struct attribute_group *scsi_sysfs_shost_attr_groups[];
 
 /* scsi_netlink.c */
 #ifdef CONFIG_SCSI_NETLINK
index 91482f2..fde5453 100644 (file)
@@ -275,7 +275,7 @@ struct attribute_group scsi_shost_attr_group = {
        .attrs =        scsi_sysfs_shost_attrs,
 };
 
-struct attribute_group *scsi_sysfs_shost_attr_groups[] = {
+const struct attribute_group *scsi_sysfs_shost_attr_groups[] = {
        &scsi_shost_attr_group,
        NULL
 };
@@ -745,7 +745,7 @@ static struct attribute_group scsi_sdev_attr_group = {
        .attrs =        scsi_sdev_attrs,
 };
 
-static struct attribute_group *scsi_sdev_attr_groups[] = {
+static const struct attribute_group *scsi_sdev_attr_groups[] = {
        &scsi_sdev_attr_group,
        NULL
 };
index 7f86534..8aa1955 100644 (file)
@@ -1,7 +1,6 @@
 menuconfig UIO
        tristate "Userspace I/O drivers"
        depends on !S390
-       default n
        help
          Enable this to allow the userspace driver core code to be
          built.  This code allows userspace programs easy access to
@@ -16,7 +15,6 @@ if UIO
 config UIO_CIF
        tristate "generic Hilscher CIF Card driver"
        depends on PCI
-       default n
        help
          Driver for Hilscher CIF DeviceNet and Profibus cards.  This
          driver requires a userspace component that handles all of the
@@ -48,7 +46,6 @@ config UIO_PDRV_GENIRQ
 
 config UIO_SMX
        tristate "SMX cryptengine UIO interface"
-       default n
        help
          Userspace IO interface to the Cryptography engine found on the
          Nias Digital SMX boards.  These will be available from Q4 2008
@@ -61,7 +58,6 @@ config UIO_SMX
 config UIO_AEC
        tristate "AEC video timestamp device"
        depends on PCI
-       default n
        help
 
          UIO driver for the Adrienne Electronics Corporation PCI time
@@ -78,7 +74,6 @@ config UIO_AEC
 
 config UIO_SERCOS3
        tristate "Automata Sercos III PCI card driver"
-       default n
        help
          Userspace I/O interface for the Sercos III PCI card from
          Automata GmbH. The userspace part of this driver will be
@@ -89,4 +84,14 @@ config UIO_SERCOS3
 
          If you compile this as a module, it will be called uio_sercos3.
 
+config UIO_PCI_GENERIC
+       tristate "Generic driver for PCI 2.3 and PCI Express cards"
+       depends on PCI
+       default n
+       help
+         Generic driver that you can bind, dynamically, to any
+         PCI 2.3 compliant and PCI Express card. It is useful,
+         primarily, for virtualization scenarios.
+         If you compile this as a module, it will be called uio_pci_generic.
+
 endif
index 5c2586d..73b2e75 100644 (file)
@@ -5,3 +5,4 @@ obj-$(CONFIG_UIO_PDRV_GENIRQ)   += uio_pdrv_genirq.o
 obj-$(CONFIG_UIO_SMX)  += uio_smx.o
 obj-$(CONFIG_UIO_AEC)  += uio_aec.o
 obj-$(CONFIG_UIO_SERCOS3)      += uio_sercos3.o
+obj-$(CONFIG_UIO_PCI_GENERIC)  += uio_pci_generic.o
diff --git a/drivers/uio/uio_pci_generic.c b/drivers/uio/uio_pci_generic.c
new file mode 100644 (file)
index 0000000..313da35
--- /dev/null
@@ -0,0 +1,207 @@
+/* uio_pci_generic - generic UIO driver for PCI 2.3 devices
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ *
+ * Since the driver does not declare any device ids, you must allocate
+ * id and bind the device to the driver yourself.  For example:
+ *
+ * # echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id
+ * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind
+ * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind
+ * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver
+ * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic
+ *
+ * Driver won't bind to devices which do not support the Interrupt Disable Bit
+ * in the command register. All devices compliant to PCI 2.3 (circa 2002) and
+ * all compliant PCI Express devices should support this bit.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/uio_driver.h>
+#include <linux/spinlock.h>
+
+#define DRIVER_VERSION "0.01.0"
+#define DRIVER_AUTHOR  "Michael S. Tsirkin <mst@redhat.com>"
+#define DRIVER_DESC    "Generic UIO driver for PCI 2.3 devices"
+
+struct uio_pci_generic_dev {
+       struct uio_info info;
+       struct pci_dev *pdev;
+       spinlock_t lock; /* guards command register accesses */
+};
+
+static inline struct uio_pci_generic_dev *
+to_uio_pci_generic_dev(struct uio_info *info)
+{
+       return container_of(info, struct uio_pci_generic_dev, info);
+}
+
+/* Interrupt handler. Read/modify/write the command register to disable
+ * the interrupt. */
+static irqreturn_t irqhandler(int irq, struct uio_info *info)
+{
+       struct uio_pci_generic_dev *gdev = to_uio_pci_generic_dev(info);
+       struct pci_dev *pdev = gdev->pdev;
+       irqreturn_t ret = IRQ_NONE;
+       u32 cmd_status_dword;
+       u16 origcmd, newcmd, status;
+
+       /* We do a single dword read to retrieve both command and status.
+        * Document assumptions that make this possible. */
+       BUILD_BUG_ON(PCI_COMMAND % 4);
+       BUILD_BUG_ON(PCI_COMMAND + 2 != PCI_STATUS);
+
+       spin_lock_irq(&gdev->lock);
+       pci_block_user_cfg_access(pdev);
+
+       /* Read both command and status registers in a single 32-bit operation.
+        * Note: we could cache the value for command and move the status read
+        * out of the lock if there was a way to get notified of user changes
+        * to command register through sysfs. Should be good for shared irqs. */
+       pci_read_config_dword(pdev, PCI_COMMAND, &cmd_status_dword);
+       origcmd = cmd_status_dword;
+       status = cmd_status_dword >> 16;
+
+       /* Check interrupt status register to see whether our device
+        * triggered the interrupt. */
+       if (!(status & PCI_STATUS_INTERRUPT))
+               goto done;
+
+       /* We triggered the interrupt, disable it. */
+       newcmd = origcmd | PCI_COMMAND_INTX_DISABLE;
+       if (newcmd != origcmd)
+               pci_write_config_word(pdev, PCI_COMMAND, newcmd);
+
+       /* UIO core will signal the user process. */
+       ret = IRQ_HANDLED;
+done:
+
+       pci_unblock_user_cfg_access(pdev);
+       spin_unlock_irq(&gdev->lock);
+       return ret;
+}
+
+/* Verify that the device supports Interrupt Disable bit in command register,
+ * per PCI 2.3, by flipping this bit and reading it back: this bit was readonly
+ * in PCI 2.2. */
+static int __devinit verify_pci_2_3(struct pci_dev *pdev)
+{
+       u16 orig, new;
+       int err = 0;
+
+       pci_block_user_cfg_access(pdev);
+       pci_read_config_word(pdev, PCI_COMMAND, &orig);
+       pci_write_config_word(pdev, PCI_COMMAND,
+                             orig ^ PCI_COMMAND_INTX_DISABLE);
+       pci_read_config_word(pdev, PCI_COMMAND, &new);
+       /* There's no way to protect against
+        * hardware bugs or detect them reliably, but as long as we know
+        * what the value should be, let's go ahead and check it. */
+       if ((new ^ orig) & ~PCI_COMMAND_INTX_DISABLE) {
+               err = -EBUSY;
+               dev_err(&pdev->dev, "Command changed from 0x%x to 0x%x: "
+                       "driver or HW bug?\n", orig, new);
+               goto err;
+       }
+       if (!((new ^ orig) & PCI_COMMAND_INTX_DISABLE)) {
+               dev_warn(&pdev->dev, "Device does not support "
+                        "disabling interrupts: unable to bind.\n");
+               err = -ENODEV;
+               goto err;
+       }
+       /* Now restore the original value. */
+       pci_write_config_word(pdev, PCI_COMMAND, orig);
+err:
+       pci_unblock_user_cfg_access(pdev);
+       return err;
+}
+
+static int __devinit probe(struct pci_dev *pdev,
+                          const struct pci_device_id *id)
+{
+       struct uio_pci_generic_dev *gdev;
+       int err;
+
+       if (!pdev->irq) {
+               dev_warn(&pdev->dev, "No IRQ assigned to device: "
+                        "no support for interrupts?\n");
+               return -ENODEV;
+       }
+
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "%s: pci_enable_device failed: %d\n",
+                       __func__, err);
+               return err;
+       }
+
+       err = verify_pci_2_3(pdev);
+       if (err)
+               goto err_verify;
+
+       gdev = kzalloc(sizeof(struct uio_pci_generic_dev), GFP_KERNEL);
+       if (!gdev) {
+               err = -ENOMEM;
+               goto err_alloc;
+       }
+
+       gdev->info.name = "uio_pci_generic";
+       gdev->info.version = DRIVER_VERSION;
+       gdev->info.irq = pdev->irq;
+       gdev->info.irq_flags = IRQF_SHARED;
+       gdev->info.handler = irqhandler;
+       gdev->pdev = pdev;
+       spin_lock_init(&gdev->lock);
+
+       if (uio_register_device(&pdev->dev, &gdev->info))
+               goto err_register;
+       pci_set_drvdata(pdev, gdev);
+
+       return 0;
+err_register:
+       kfree(gdev);
+err_alloc:
+err_verify:
+       pci_disable_device(pdev);
+       return err;
+}
+
+static void remove(struct pci_dev *pdev)
+{
+       struct uio_pci_generic_dev *gdev = pci_get_drvdata(pdev);
+
+       uio_unregister_device(&gdev->info);
+       pci_disable_device(pdev);
+       kfree(gdev);
+}
+
+static struct pci_driver driver = {
+       .name = "uio_pci_generic",
+       .id_table = NULL, /* only dynamic id's */
+       .probe = probe,
+       .remove = remove,
+};
+
+static int __init init(void)
+{
+       pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+       return pci_register_driver(&driver);
+}
+
+static void __exit cleanup(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+module_init(init);
+module_exit(cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
index bc39fc4..fdfaa78 100644 (file)
@@ -154,7 +154,7 @@ static struct attribute *ep_dev_attrs[] = {
 static struct attribute_group ep_dev_attr_grp = {
        .attrs = ep_dev_attrs,
 };
-static struct attribute_group *ep_dev_groups[] = {
+static const struct attribute_group *ep_dev_groups[] = {
        &ep_dev_attr_grp,
        NULL
 };
index b5c72e4..7ec3041 100644 (file)
@@ -573,7 +573,7 @@ static struct attribute_group dev_string_attr_grp = {
        .is_visible =   dev_string_attrs_are_visible,
 };
 
-struct attribute_group *usb_device_groups[] = {
+const struct attribute_group *usb_device_groups[] = {
        &dev_attr_grp,
        &dev_string_attr_grp,
        NULL
@@ -799,7 +799,7 @@ static struct attribute_group intf_assoc_attr_grp = {
        .is_visible =   intf_assoc_attrs_are_visible,
 };
 
-struct attribute_group *usb_interface_groups[] = {
+const struct attribute_group *usb_interface_groups[] = {
        &intf_attr_grp,
        &intf_assoc_attr_grp,
        NULL
index e2a8cfa..c0e0ae2 100644 (file)
@@ -152,8 +152,8 @@ static inline int is_active(const struct usb_interface *f)
 extern const char *usbcore_name;
 
 /* sysfs stuff */
-extern struct attribute_group *usb_device_groups[];
-extern struct attribute_group *usb_interface_groups[];
+extern const struct attribute_group *usb_device_groups[];
+extern const struct attribute_group *usb_interface_groups[];
 
 /* usbfs stuff */
 extern struct mutex usbfs_mutex;
index 274751b..5cd0e48 100644 (file)
@@ -67,7 +67,7 @@ MODULE_PARM_DESC(ignore_oc, "ignore hardware overcurrent indications");
  * debug = 0, no debugging messages
  * debug = 1, dump failed URBs except for stalls
  * debug = 2, dump all failed URBs (including stalls)
- *            show all queues in /debug/uhci/[pci_addr]
+ *            show all queues in /sys/kernel/debug/uhci/[pci_addr]
  * debug = 3, show all TDs in URBs when dumping
  */
 #ifdef DEBUG
index e9fe1bb..1097e81 100644 (file)
@@ -255,7 +255,7 @@ static struct attribute_group dev_attr_group = {
        .attrs = dev_attrs,
 };
 
-static struct attribute_group *groups[] = {
+static const struct attribute_group *groups[] = {
        &dev_attr_group,
        NULL,
 };
index 619ba99..fbeaddf 100644 (file)
@@ -312,7 +312,7 @@ static struct attribute_group part_attr_group = {
        .attrs = part_attrs,
 };
 
-static struct attribute_group *part_attr_groups[] = {
+static const struct attribute_group *part_attr_groups[] = {
        &part_attr_group,
 #ifdef CONFIG_BLK_DEV_IO_TRACE
        &blk_trace_attr_group,
index 794ad74..c3ab814 100644 (file)
@@ -17,7 +17,7 @@ struct attribute_container {
        struct list_head        node;
        struct klist            containers;
        struct class            *class;
-       struct attribute_group  *grp;
+       const struct attribute_group *grp;
        struct device_attribute **attrs;
        int (*match)(struct attribute_container *, struct device *);
 #define        ATTRIBUTE_CONTAINER_NO_CLASSDEVS        0x01
index a286429..847b763 100644 (file)
@@ -2,7 +2,8 @@
  * device.h - generic, centralized driver model
  *
  * Copyright (c) 2001-2003 Patrick Mochel <mochel@osdl.org>
- * Copyright (c) 2004-2007 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2004-2009 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2008-2009 Novell Inc.
  *
  * This file is released under the GPLv2
  *
@@ -130,7 +131,7 @@ struct device_driver {
        void (*shutdown) (struct device *dev);
        int (*suspend) (struct device *dev, pm_message_t state);
        int (*resume) (struct device *dev);
-       struct attribute_group **groups;
+       const struct attribute_group **groups;
 
        const struct dev_pm_ops *pm;
 
@@ -224,6 +225,14 @@ extern void class_unregister(struct class *class);
        __class_register(class, &__key);        \
 })
 
+struct class_compat;
+struct class_compat *class_compat_register(const char *name);
+void class_compat_unregister(struct class_compat *cls);
+int class_compat_create_link(struct class_compat *cls, struct device *dev,
+                            struct device *device_link);
+void class_compat_remove_link(struct class_compat *cls, struct device *dev,
+                             struct device *device_link);
+
 extern void class_dev_iter_init(struct class_dev_iter *iter,
                                struct class *class,
                                struct device *start,
@@ -287,7 +296,7 @@ extern void class_destroy(struct class *cls);
  */
 struct device_type {
        const char *name;
-       struct attribute_group **groups;
+       const struct attribute_group **groups;
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
        char *(*nodename)(struct device *dev);
        void (*release)(struct device *dev);
@@ -381,7 +390,6 @@ struct device {
        struct bus_type *bus;           /* type of bus device is on */
        struct device_driver *driver;   /* which driver has allocated this
                                           device */
-       void            *driver_data;   /* data private to the driver */
        void            *platform_data; /* Platform specific data, device
                                           core doesn't touch it */
        struct dev_pm_info      power;
@@ -412,7 +420,7 @@ struct device {
 
        struct klist_node       knode_class;
        struct class            *class;
-       struct attribute_group  **groups;       /* optional groups */
+       const struct attribute_group **groups;  /* optional groups */
 
        void    (*release)(struct device *dev);
 };
@@ -447,16 +455,6 @@ static inline void set_dev_node(struct device *dev, int node)
 }
 #endif
 
-static inline void *dev_get_drvdata(const struct device *dev)
-{
-       return dev->driver_data;
-}
-
-static inline void dev_set_drvdata(struct device *dev, void *data)
-{
-       dev->driver_data = data;
-}
-
 static inline unsigned int dev_get_uevent_suppress(const struct device *dev)
 {
        return dev->kobj.uevent_suppress;
@@ -490,6 +488,8 @@ extern int device_rename(struct device *dev, char *new_name);
 extern int device_move(struct device *dev, struct device *new_parent,
                       enum dpm_order dpm_order);
 extern const char *device_get_nodename(struct device *dev, const char **tmp);
+extern void *dev_get_drvdata(const struct device *dev);
+extern void dev_set_drvdata(struct device *dev, void *data);
 
 /*
  * Root device objects for grouping under /sys/devices
@@ -502,6 +502,11 @@ static inline struct device *root_device_register(const char *name)
 }
 extern void root_device_unregister(struct device *root);
 
+static inline void *dev_get_platdata(const struct device *dev)
+{
+       return dev->platform_data;
+}
+
 /*
  * Manual binding of a device to driver. See drivers/base/bus.c
  * for information on use.
@@ -547,6 +552,16 @@ extern void put_device(struct device *dev);
 
 extern void wait_for_device_probe(void);
 
+#ifdef CONFIG_DEVTMPFS
+extern int devtmpfs_create_node(struct device *dev);
+extern int devtmpfs_delete_node(struct device *dev);
+extern int devtmpfs_mount(const char *mountpoint);
+#else
+static inline int devtmpfs_create_node(struct device *dev) { return 0; }
+static inline int devtmpfs_delete_node(struct device *dev) { return 0; }
+static inline int devtmpfs_mount(const char *mountpoint) { return 0; }
+#endif
+
 /* drivers/base/power/shutdown.c */
 extern void device_shutdown(void);
 
index 65ee192..a9aa4b5 100644 (file)
@@ -895,7 +895,7 @@ struct net_device
        /* class/net/name entry */
        struct device           dev;
        /* space for optional statistics and wireless sysfs groups */
-       struct attribute_group  *sysfs_groups[3];
+       const struct attribute_group *sysfs_groups[3];
 
        /* rtnetlink link ops */
        const struct rtnl_link_ops *rtnl_link_ops;
index fcaee42..dd0bed4 100644 (file)
@@ -42,6 +42,7 @@
 #define  PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */
 
 #define PCI_STATUS             0x06    /* 16 bits */
+#define  PCI_STATUS_INTERRUPT  0x08    /* Interrupt status */
 #define  PCI_STATUS_CAP_LIST   0x10    /* Support Capability List */
 #define  PCI_STATUS_66MHZ      0x20    /* Support 66 Mhz PCI 2.1 bus */
 #define  PCI_STATUS_UDF                0x40    /* Support User Definable Features [obsolete] */
index 6d3f2f4..deee7af 100644 (file)
@@ -38,6 +38,9 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
        return container_of(inode, struct shmem_inode_info, vfs_inode);
 }
 
+extern int init_tmpfs(void);
+extern int shmem_fill_super(struct super_block *sb, void *data, int silent);
+
 #ifdef CONFIG_TMPFS_POSIX_ACL
 int shmem_check_acl(struct inode *, int);
 int shmem_acl_init(struct inode *, struct inode *);
index eaec1ea..9ae8da3 100644 (file)
@@ -55,7 +55,7 @@ struct anon_transport_class cls = {                           \
 
 struct transport_container {
        struct attribute_container ac;
-       struct attribute_group *statistics;
+       const struct attribute_group *statistics;
 };
 
 #define attribute_container_to_transport_container(x) \
index 093f659..bb008d0 100644 (file)
@@ -415,7 +415,7 @@ void __init prepare_namespace(void)
 
        mount_root();
 out:
+       devtmpfs_mount("dev");
        sys_mount(".", "/", NULL, MS_MOVE, NULL);
        sys_chroot(".");
 }
-
index 63904bb..34971be 100644 (file)
@@ -68,6 +68,7 @@
 #include <linux/async.h>
 #include <linux/kmemcheck.h>
 #include <linux/kmemtrace.h>
+#include <linux/shmem_fs.h>
 #include <trace/boot.h>
 
 #include <asm/io.h>
@@ -785,6 +786,7 @@ static void __init do_basic_setup(void)
        init_workqueues();
        cpuset_init_smp();
        usermodehelper_init();
+       init_tmpfs();
        driver_init();
        init_irq_proc();
        do_ctors();
index 961379c..3d9c7e2 100644 (file)
@@ -90,7 +90,6 @@ obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
 obj-$(CONFIG_MARKERS) += marker.o
 obj-$(CONFIG_TRACEPOINTS) += tracepoint.o
 obj-$(CONFIG_LATENCYTOP) += latencytop.o
-obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
 obj-$(CONFIG_FUNCTION_TRACER) += trace/
 obj-$(CONFIG_TRACING) += trace/
 obj-$(CONFIG_X86_DS) += trace/
index 5a0b3d4..bd20f8b 100644 (file)
@@ -2298,8 +2298,7 @@ static void shmem_put_super(struct super_block *sb)
        sb->s_fs_info = NULL;
 }
 
-static int shmem_fill_super(struct super_block *sb,
-                           void *data, int silent)
+int shmem_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct inode *inode;
        struct dentry *root;
@@ -2519,7 +2518,7 @@ static struct file_system_type tmpfs_fs_type = {
        .kill_sb        = kill_litter_super,
 };
 
-static int __init init_tmpfs(void)
+int __init init_tmpfs(void)
 {
        int error;
 
@@ -2576,7 +2575,7 @@ static struct file_system_type tmpfs_fs_type = {
        .kill_sb        = kill_litter_super,
 };
 
-static int __init init_tmpfs(void)
+int __init init_tmpfs(void)
 {
        BUG_ON(register_filesystem(&tmpfs_fs_type) != 0);
 
@@ -2687,5 +2686,3 @@ int shmem_zero_setup(struct vm_area_struct *vma)
        vma->vm_ops = &shmem_vm_ops;
        return 0;
 }
-
-module_init(init_tmpfs)
index 95f7a7a..7f939ce 100644 (file)
@@ -68,7 +68,7 @@ static struct attribute_group bt_link_group = {
        .attrs = bt_link_attrs,
 };
 
-static struct attribute_group *bt_link_groups[] = {
+static const struct attribute_group *bt_link_groups[] = {
        &bt_link_group,
        NULL
 };
@@ -392,7 +392,7 @@ static struct attribute_group bt_host_group = {
        .attrs = bt_host_attrs,
 };
 
-static struct attribute_group *bt_host_groups[] = {
+static const struct attribute_group *bt_host_groups[] = {
        &bt_host_group,
        NULL
 };
index ad91e9e..7d4c575 100644 (file)
@@ -493,7 +493,7 @@ void netdev_unregister_kobject(struct net_device * net)
 int netdev_register_kobject(struct net_device *net)
 {
        struct device *dev = &(net->dev);
-       struct attribute_group **groups = net->sysfs_groups;
+       const struct attribute_group **groups = net->sysfs_groups;
 
        dev->class = &net_class;
        dev->platform_data = net;
index f24ae37..6af3732 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * If TRACE_SYSTEM is defined, that will be the directory created
- * in the ftrace directory under /debugfs/tracing/events/<system>
+ * in the ftrace directory under /sys/kernel/debug/tracing/events/<system>
  *
  * The define_trace.h below will also look for a file name of
  * TRACE_SYSTEM.h where TRACE_SYSTEM is what is defined here.