Commit ab86e576 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6

* 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
parents 7ea61767 2b2af54a
......@@ -25,6 +25,10 @@
<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>
......@@ -41,6 +45,13 @@ GPL version 2.
</abstract>
<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>
......@@ -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>
......
......@@ -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:
......
......@@ -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
......
......@@ -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
};
......
......@@ -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
......
......@@ -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
......
......@@ -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
......@@ -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 @@ int bus_add_device(struct device *dev)
}
/**
* 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);
}
}
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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);
/*
* 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;
}
File moved
......@@ -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;
......
......@@ -20,6 +20,7 @@
void __init driver_init(void)
{
/* These are the core pieces */
devtmpfs_init();
devices_init();
buses_init();
classes_init();
......
......@@ -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);
......
......@@ -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
};
......
......@@ -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);
......
......@@ -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;
}
......
......@@ -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,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment