mirror of
https://github.com/adulau/aha.git
synced 2024-12-27 11:16:11 +00:00
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
This commit is contained in:
commit
ab86e5765d
59 changed files with 1287 additions and 239 deletions
|
@ -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 "8086 10f5" > /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 > /sys/bus/pci/drivers/e1000e/unbind
|
||||
echo -n 0000:00:19.0 > /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 -> ../../../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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
int uiofd;
|
||||
int configfd;
|
||||
int err;
|
||||
int i;
|
||||
unsigned icount;
|
||||
unsigned char command_high;
|
||||
|
||||
uiofd = open("/dev/uio0", O_RDONLY);
|
||||
if (uiofd < 0) {
|
||||
perror("uio open:");
|
||||
return errno;
|
||||
}
|
||||
configfd = open("/sys/class/uio/uio0/device/config", O_RDWR);
|
||||
if (uiofd < 0) {
|
||||
perror("config open:");
|
||||
return errno;
|
||||
}
|
||||
|
||||
/* Read and cache command value */
|
||||
err = pread(configfd, &command_high, 1, 5);
|
||||
if (err != 1) {
|
||||
perror("command config read:");
|
||||
return errno;
|
||||
}
|
||||
command_high &= ~0x4;
|
||||
|
||||
for(i = 0;; ++i) {
|
||||
/* Print out a message, for debugging. */
|
||||
if (i == 0)
|
||||
fprintf(stderr, "Started uio test driver.\n");
|
||||
else
|
||||
fprintf(stderr, "Interrupts: %d\n", icount);
|
||||
|
||||
/****************************************/
|
||||
/* Here we got an interrupt from the
|
||||
device. Do something to it. */
|
||||
/****************************************/
|
||||
|
||||
/* Re-enable interrupts. */
|
||||
err = pwrite(configfd, &command_high, 1, 5);
|
||||
if (err != 1) {
|
||||
perror("config write:");
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wait for next interrupt. */
|
||||
err = read(uiofd, &icount, 4);
|
||||
if (err != 4) {
|
||||
perror("uio read:");
|
||||
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 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
367
drivers/base/devtmpfs.c
Normal file
367
drivers/base/devtmpfs.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
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
|
||||
if (filp->f_op && filp->f_op->open)
|
||||
ret = filp->f_op->open(inode, filp);
|
||||
|
||||
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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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 void ilo_locked_reset(struct ilo_hwinfo *hw)
|
||||
static inline void ilo_enable_interrupts(struct ilo_hwinfo *hw)
|
||||
{
|
||||
iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]);
|
||||
}
|
||||
|
||||
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>");
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
207
drivers/uio/uio_pci_generic.c
Normal file
207
drivers/uio/uio_pci_generic.c
Normal file
|
@ -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);
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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] */
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -415,7 +415,7 @@ void __init prepare_namespace(void)
|
|||
|
||||
mount_root();
|
||||
out:
|
||||
devtmpfs_mount("dev");
|
||||
sys_mount(".", "/", NULL, MS_MOVE, NULL);
|
||||
sys_chroot(".");
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue