Commit 6dc1ec37 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://ldm.bkbits.net/linux-2.5-kobject

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 6e6e099b 808897cf
driverfs - The Device Driver Filesystem
Patrick Mochel <mochel@osdl.org>
2 August 2002
What it is:
~~~~~~~~~~~
driverfs is a ram-based filesystem. It was created by copying
ramfs/inode.c to driverfs/inode.c and doing a little search-and-replace.
driverfs is a means to export kernel data structures, their
attributes, and the linkages between them to userspace.
driverfs provides a unified interface for exporting attributes to
userspace. Currently, this interface is available only to device and
bus drivers.
Using driverfs
~~~~~~~~~~~~~~
driverfs is always compiled in. You can access it by doing something like:
mount -t driverfs driverfs /devices
Top Level Directory Layout
~~~~~~~~~~~~~~~~~~~~~~~~~~
The driverfs directory arrangement exposes the relationship of kernel
data structures.
The top level driverfs diretory looks like:
bus/
root/
root/ contains a filesystem representation of the device tree. It maps
directly to the internal kernel device tree, which is a hierarchy of
struct device.
bus/ contains flat directory layout of the various bus types in the
kernel. Each bus's directory contains two subdirectories:
devices/
drivers/
devices/ contains symlinks for each device discovered in the system
that point to the device's directory under root/.
drivers/ contains a directory for each device driver that is loaded
for devices on that particular bus (this assmumes that drivers do not
span multiple bus types).
More information can device-model specific features can be found in
Documentation/device-model/.
Directory Contents
~~~~~~~~~~~~~~~~~~
Each object that is represented in driverfs gets a directory, rather
than a file, to make it simple to export attributes of that object.
Attributes are exported via ASCII text files. The programming
interface is discussed below.
Instead of having monolithic files that are difficult to parse, all
files are intended to export one attribute. The name of the attribute
is the name of the file. The value of the attribute are the contents
of the file.
There should be few, if any, exceptions to this rule. You should not
violate it, for fear of public humilation.
The Two-Tier Model
~~~~~~~~~~~~~~~~~~
driverfs is a very simple, low-level interface. In order for kernel
objects to use it, there must be an intermediate layer in place for
each object type.
All calls in driverfs are intended to be as type-safe as possible.
In order to extend driverfs to support multiple data types, a layer of
abstraction was required. This intermediate layer converts between the
generic calls and data structures of the driverfs core to the
subsystem-specific objects and calls.
The Subsystem Interface
~~~~~~~~~~~~~~~~~~~~~~~
The subsystems bear the responsibility of implementing driverfs
extensions for the objects they control. Fortunately, it's intended to
be really easy to do so.
It's divided into three sections: directories, files, and operations.
Directories
~~~~~~~~~~~
struct driver_dir_entry {
char * name;
struct dentry * dentry;
mode_t mode;
struct driverfs_ops * ops;
};
int
driverfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *);
void
driverfs_remove_dir(struct driver_dir_entry * entry);
The directory structure should be statically allocated, and reside in
a subsystem-specific data structure:
struct device {
...
struct driver_dir_entry dir;
};
The subsystem is responsible for initializing the name, mode, and ops
fields of the directory entry. (More on struct driverfs_ops later)
Files
~~~~~
struct attribute {
char * name;
mode_t mode;
};
int
driverfs_create_file(struct attribute * attr, struct driver_dir_entry * parent);
void
driverfs_remove_file(struct driver_dir_entry *, const char * name);
The attribute structure is a simple, common token that the driverfs
core handles. It has little use on its own outside of the
core. Objects cannot use a plain struct attribute to export
attributes, since there are no callbacks for reading and writing data.
Therefore, the subsystem is required to define a data structure that
encapsulates the attribute structure, and provides type-safe callbacks
for reading and writing data.
An example looks like this:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off);
};
Note that there is a struct attribute embedded in the structure. In
order to relieve pain in declaring attributes, the subsystem should
also define a macro, like:
#define DEVICE_ATTR(_name,_mode,_show,_store) \
struct device_attribute dev_attr_##_name = { \
.attr = {.name = __stringify(_name) , .mode = _mode }, \
.show = _show, \
.store = _store, \
};
This hides the initialization of the embedded struct, and in general,
the internals of each structure. It yields a structure by the name of
dev_attr_<name>.
In order for objects to create files, the subsystem should create
wrapper functions, like this:
int device_create_file(struct device *device, struct device_attribute * entry);
void device_remove_file(struct device * dev, struct device_attribute * attr);
..and forward the call on to the driverfs functions.
Note that there is no unique information in the attribute structures,
so the same structure can be used to describe files of several
different object instances.
Operations
~~~~~~~~~~
struct driverfs_ops {
int (*open)(struct driver_dir_entry *);
int (*close)(struct driver_dir_entry *);
ssize_t (*show)(struct driver_dir_entry *, struct attribute *,char *, size_t, loff_t);
ssize_t (*store)(struct driver_dir_entry *,struct attribute *,const char *, size_t, loff_t);
};
Subsystems are required to implement this set of callbacks. Their
purpose is to translate the generic data structures into the specific
objects, and operate on them. This can be done by defining macros like
this:
#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr)
#define to_device(d) container_of(d, struct device, dir)
Since the directories are statically allocated in the object, you can
derive the pointer to the object that owns the file. Ditto for the
attribute structures.
Current Interfaces
~~~~~~~~~~~~~~~~~~
The following interface layers currently exist in driverfs:
- devices (include/linux/device.h)
----------------------------------
Structure:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device * dev, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct device * dev, const char * buf, size_t count, loff_t off);
};
Declaring:
DEVICE_ATTR(_name,_str,_mode,_show,_store);
Creation/Removal:
int device_create_file(struct device *device, struct device_attribute * entry);
void device_remove_file(struct device * dev, struct device_attribute * attr);
- bus drivers (include/linux/device.h)
--------------------------------------
Structure:
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct bus_type *, const char * buf, size_t count, loff_t off);
};
Declaring:
BUS_ATTR(_name,_mode,_show,_store)
Creation/Removal:
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
- device drivers (include/linux/device.h)
-----------------------------------------
Structure:
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *, char * buf, size_t count, loff_t off);
ssize_t (*store)(struct device_driver *, const char * buf, size_t count, loff_t off);
};
Declaring:
DRIVER_ATTR(_name,_mode,_show,_store)
Creation/Removal:
int driver_create_file(struct device_driver *, struct driver_attribute *);
void driver_remove_file(struct device_driver *, struct driver_attribute *);
Reading/Writing Data
~~~~~~~~~~~~~~~~~~~~
The callback functionality is similar to the way procfs works. When a
user performs a read(2) or write(2) on the file, it first calls a
driverfs function. This calls to the subsystem, which then calls to
the object's show() or store() function.
The buffer pointer, offset, and length should be passed to each
function. The downstream callback should fill the buffer and return
the number of bytes read/written.
What driverfs is not:
~~~~~~~~~~~~~~~~~~~~~
It is not a replacement for either devfs or procfs.
It does not handle device nodes, like devfs is intended to do. I think
this functionality is possible, but indeed think that integration of
the device nodes and control files should be done. Whether driverfs or
devfs, or something else, is the place to do it, I don't know.
It is not intended to be a replacement for all of the procfs
functionality. I think that many of the driver files should be moved
out of /proc (and maybe a few other things as well ;).
Limitations:
~~~~~~~~~~~~
The driverfs functions assume that at most a page is being either read
or written each time.
There is a race condition that is really, really hard to fix; if not
impossible. There exists a race between a driverfs file being opened
and the object that owns the file going away. During the driverfs
open() callback, the reference count for the owning object needs to be
incremented.
For drivers, we can put a struct module * owner in struct driver_dir_entry
and do try_inc_mod_count() when we open a file. However, this won't
work for devices, that aren't tied to a module. And, it is still not
guaranteed to solve the race.
I'm looking into fixing this, but it may not be doable without making
a separate filesystem instance for each object. It's fun stuff. Please
mail me with creative ideas that you know will work.
Possible bugs:
~~~~~~~~~~~~~~
It may not deal with offsets and/or seeks very well, especially if
they cross a page boundary.
......@@ -44,7 +44,7 @@
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/limits.h>
#include <linux/driverfs_fs.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <asm/edd.h>
#include <linux/device.h>
......@@ -63,20 +63,9 @@ MODULE_LICENSE("GPL");
#define left (count - (p - buf) - 1)
/*
* bios_dir may go away completely,
* and it definitely won't be at the root
* of driverfs forever.
*/
static struct driver_dir_entry bios_dir = {
.name = "bios",
.mode = (S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO),
};
struct edd_device {
char name[EDD_DEVICE_NAME_SIZE];
struct edd_info *info;
struct driver_dir_entry dir;
struct kobject kobj;
};
struct edd_attribute {
......@@ -112,13 +101,13 @@ edd_dev_set_info(struct edd_device *edev, struct edd_info *info)
}
#define to_edd_attr(_attr) container_of(_attr,struct edd_attribute,attr)
#define to_edd_device(_dir) container_of(_dir,struct edd_device,dir)
#define to_edd_device(obj) container_of(obj,struct edd_device,kobj)
static ssize_t
edd_attr_show(struct driver_dir_entry *dir, struct attribute *attr,
edd_attr_show(struct kobject * kobj, struct attribute *attr,
char *buf, size_t count, loff_t off)
{
struct edd_device *dev = to_edd_device(dir);
struct edd_device *dev = to_edd_device(kobj);
struct edd_attribute *edd_attr = to_edd_attr(attr);
ssize_t ret = 0;
......@@ -127,7 +116,7 @@ edd_attr_show(struct driver_dir_entry *dir, struct attribute *attr,
return ret;
}
static struct driverfs_ops edd_attr_ops = {
static struct sysfs_ops edd_attr_ops = {
.show = edd_attr_show,
};
......@@ -586,89 +575,26 @@ static EDD_DEVICE_ATTR(interface, 0444, edd_show_interface, edd_has_edd30);
static EDD_DEVICE_ATTR(host_bus, 0444, edd_show_host_bus, edd_has_edd30);
static struct edd_attribute * def_attrs[] = {
&edd_attr_raw_data,
&edd_attr_version,
&edd_attr_extensions,
&edd_attr_info_flags,
&edd_attr_sectors,
&edd_attr_default_cylinders,
&edd_attr_default_heads,
&edd_attr_default_sectors_per_track,
&edd_attr_interface,
&edd_attr_host_bus,
static struct attribute * def_attrs[] = {
&edd_attr_raw_data.attr,
&edd_attr_version.attr,
&edd_attr_extensions.attr,
&edd_attr_info_flags.attr,
&edd_attr_sectors.attr,
&edd_attr_default_cylinders.attr,
&edd_attr_default_heads.attr,
&edd_attr_default_sectors_per_track.attr,
&edd_attr_interface.attr,
&edd_attr_host_bus.attr,
NULL,
};
/* edd_get_devpath_length(), edd_fill_devpath(), and edd_device_link()
were taken from linux/drivers/base/fs/device.c. When these
or similar are exported to generic code, remove these.
*/
static int
edd_get_devpath_length(struct device *dev)
{
int length = 1;
struct device *parent = dev;
/* walk up the ancestors until we hit the root.
* Add 1 to strlen for leading '/' of each level.
*/
do {
length += strlen(parent->bus_id) + 1;
parent = parent->parent;
} while (parent);
return length;
}
static void
edd_fill_devpath(struct device *dev, char *path, int length)
{
struct device *parent;
--length;
for (parent = dev; parent; parent = parent->parent) {
int cur = strlen(parent->bus_id);
/* back up enough to print this bus id with '/' */
length -= cur;
strncpy(path + length, parent->bus_id, cur);
*(path + --length) = '/';
}
}
static int
edd_device_symlink(struct edd_device *edev, struct device *dev, char *name)
{
char *path;
int length;
int error = 0;
if (!dev->bus || !name)
return 0;
length = edd_get_devpath_length(dev);
/* now add the path from the edd_device directory
* It should be '../..' (one to get to the 'bios' directory,
* and one to get to the root of the fs.)
*/
length += strlen("../../root");
if (length > PATH_MAX)
return -ENAMETOOLONG;
if (!(path = kmalloc(length, GFP_KERNEL)))
return -ENOMEM;
memset(path, 0, length);
static struct subsystem edd_subsys = {
.kobj = { .name = "edd" },
.sysfs_ops = &edd_attr_ops,
.default_attrs = def_attrs,
};
/* our relative position */
strcpy(path, "../../root");
edd_fill_devpath(dev, path, length);
error = driverfs_create_symlink(&edev->dir, name, path);
kfree(path);
return error;
}
/**
* edd_dev_is_type() - is this EDD device a 'type' device?
......@@ -721,7 +647,7 @@ edd_create_symlink_to_pcidev(struct edd_device *edev)
struct pci_dev *pci_dev = edd_get_pci_dev(edev);
if (!pci_dev)
return 1;
return edd_device_symlink(edev, &pci_dev->dev, "pci_dev");
return sysfs_create_link(&edev->kobj,&pci_dev->dev.kobj,"pci_dev");
}
/**
......@@ -833,61 +759,16 @@ edd_create_symlink_to_scsidev(struct edd_device *edev)
return 1;
get_device(&sdev->sdev_driverfs_dev);
rc = edd_device_symlink(edev, &sdev->sdev_driverfs_dev, "disc");
rc = sysfs_create_link(&edev->kobj,&sdev->sdev_driverfs_dev.kobj, "disc");
put_device(&sdev->sdev_driverfs_dev);
return rc;
}
static inline int
edd_create_file(struct edd_device *edev, struct edd_attribute *attr)
{
return driverfs_create_file(&attr->attr, &edev->dir);
}
static inline void
edd_device_unregister(struct edd_device *edev)
{
driverfs_remove_dir(&edev->dir);
}
static int
edd_populate_dir(struct edd_device *edev)
{
struct edd_attribute *attr;
int i;
int error = 0;
for (i = 0; (attr=def_attrs[i]); i++) {
if (!attr->test || (attr->test && !attr->test(edev))) {
if ((error = edd_create_file(edev, attr))) {
break;
}
}
}
if (error)
return error;
edd_create_symlink_to_pcidev(edev);
edd_create_symlink_to_scsidev(edev);
return 0;
}
static int
edd_make_dir(struct edd_device *edev)
{
int error;
edev->dir.name = edev->name;
edev->dir.mode = (S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO);
edev->dir.ops = &edd_attr_ops;
error = driverfs_create_dir(&edev->dir, &bios_dir);
if (!error)
error = edd_populate_dir(edev);
return error;
kobject_unregister(&edev->kobj);
}
static int
......@@ -899,9 +780,15 @@ edd_device_register(struct edd_device *edev, int i)
return 1;
memset(edev, 0, sizeof (*edev));
edd_dev_set_info(edev, &edd[i]);
snprintf(edev->name, EDD_DEVICE_NAME_SIZE, "int13_dev%02x",
kobject_init(&edev->kobj);
snprintf(edev->kobj.name, EDD_DEVICE_NAME_SIZE, "int13_dev%02x",
edd[i].device);
error = edd_make_dir(edev);
edev->kobj.subsys = &edd_subsys;
error = kobject_register(&edev->kobj);
if (!error) {
edd_create_symlink_to_pcidev(edev);
edd_create_symlink_to_scsidev(edev);
}
return error;
}
......@@ -926,7 +813,7 @@ edd_init(void)
return 1;
}
rc = driverfs_create_dir(&bios_dir, NULL);
rc = firmware_register(&edd_subsys);
if (rc)
return rc;
......@@ -943,12 +830,9 @@ edd_init(void)
edd_devices[i] = edev;
}
if (rc) {
driverfs_remove_dir(&bios_dir);
return rc;
}
return 0;
if (rc)
firmware_unregister(&edd_subsys);
return rc;
}
static void __exit
......@@ -963,8 +847,7 @@ edd_exit(void)
kfree(edev);
}
}
driverfs_remove_dir(&bios_dir);
firmware_unregister(&edd_subsys);
}
late_initcall(edd_init);
......
......@@ -32,7 +32,7 @@ obj-$(CONFIG_ACPI_INTERPRETER) += osl.o utils.o \
#
# ACPI Bus and Device Drivers
#
obj-$(CONFIG_ACPI_BUS) += bus.o driverfs.o
obj-$(CONFIG_ACPI_BUS) += bus.o
obj-$(CONFIG_ACPI_AC) += ac.o
obj-$(CONFIG_ACPI_BATTERY) += battery.o
obj-$(CONFIG_ACPI_BUTTON) += button.o
......
......@@ -27,7 +27,7 @@
#define __ACPI_BUS_H__
#include <linux/version.h>
#include <linux/driverfs_fs.h>
#include <linux/kobject.h>
#include "include/acpi.h"
......@@ -255,7 +255,7 @@ struct acpi_device {
struct acpi_device_ops ops;
struct acpi_driver *driver;
void *driver_data;
struct driver_dir_entry driverfs_dir;
struct kobject kobj;
};
#define acpi_driver_data(d) ((d)->driver_data)
......@@ -274,6 +274,7 @@ struct acpi_bus_event {
u32 data;
};
extern struct subsystem acpi_subsys;
/*
* External Functions
......
......@@ -675,6 +675,9 @@ acpi_bus_init (void)
return_VALUE(-ENODEV);
}
struct subsystem acpi_subsys = {
.kobj = { .name = "acpi" },
};
static int __init acpi_init (void)
{
......@@ -693,6 +696,8 @@ static int __init acpi_init (void)
return -ENODEV;
}
firmware_register(&acpi_subsys);
result = acpi_bus_init();
if (!result) {
......
/*
* driverfs.c - ACPI bindings for driverfs.
*
* Copyright (c) 2002 Patrick Mochel
* Copyright (c) 2002 The Open Source Development Lab
*
*/
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/driverfs_fs.h>
#include "acpi_bus.h"
static struct driver_dir_entry acpi_dir = {
.name = "acpi",
.mode = (S_IRWXU | S_IRUGO | S_IXUGO),
};
/* driverfs ops for ACPI attribute files go here, when/if
* there are ACPI attribute files.
* For now, we just have directory creation and removal.
*/
void acpi_remove_dir(struct acpi_device * dev)
{
if (dev)
driverfs_remove_dir(&dev->driverfs_dir);
}
int acpi_create_dir(struct acpi_device * dev)
{
struct driver_dir_entry * parent;
parent = dev->parent ? &dev->parent->driverfs_dir : &acpi_dir;
dev->driverfs_dir.name = dev->pnp.bus_id;
dev->driverfs_dir.mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO);
return driverfs_create_dir(&dev->driverfs_dir,parent);
}
static int __init acpi_driverfs_init(void)
{
return driverfs_create_dir(&acpi_dir,NULL);
}
subsys_initcall(acpi_driverfs_init);
......@@ -25,20 +25,52 @@ extern struct acpi_device *acpi_root;
static LIST_HEAD(acpi_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;
static int
acpi_device_register (
struct acpi_device *device,
struct acpi_device *parent)
static void acpi_device_release(struct kobject * kobj)
{
return acpi_create_dir(device);
struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj);
kfree(dev);
}
static struct subsystem acpi_namespace_subsys = {
.kobj = { .name = "namespace" },
.parent = &acpi_subsys,
.release = acpi_device_release,
};
static void acpi_device_register(struct acpi_device * device, struct acpi_device * parent)
{
/*
* Linkage
* -------
* Link this device to its parent and siblings.
*/
INIT_LIST_HEAD(&device->children);
INIT_LIST_HEAD(&device->node);
INIT_LIST_HEAD(&device->g_list);
spin_lock(&acpi_device_lock);
if (device->parent) {
list_add_tail(&device->node, &device->parent->children);
list_add_tail(&device->g_list,&device->parent->g_list);
} else
list_add_tail(&device->g_list,&acpi_device_list);
spin_unlock(&acpi_device_lock);
kobject_init(&device->kobj);
strncpy(device->kobj.name,device->pnp.bus_id,KOBJ_NAME_LEN);
if (parent)
device->kobj.parent = &parent->kobj;
device->kobj.subsys = &acpi_namespace_subsys;
kobject_register(&device->kobj);
}
static int
acpi_device_unregister (
struct acpi_device *device)
struct acpi_device *device,
int type)
{
acpi_remove_dir(device);
kobject_unregister(&device->kobj);
return 0;
}
......@@ -443,16 +475,6 @@ acpi_bus_get_flags (
return_VALUE(0);
}
static int
acpi_bus_remove (
struct acpi_device *device,
int type)
{
acpi_device_unregister(device);
kfree(device);
return 0;
}
static void acpi_device_get_busid(struct acpi_device * device, acpi_handle handle, int type)
{
char bus_id[5] = {'?',0};
......@@ -621,28 +643,6 @@ void acpi_device_get_debug_info(struct acpi_device * device, acpi_handle handle,
#endif /*CONFIG_ACPI_DEBUG*/
}
static void acpi_device_attach(struct acpi_device * device, struct acpi_device * parent)
{
/*
* Linkage
* -------
* Link this device to its parent and siblings.
*/
INIT_LIST_HEAD(&device->children);
INIT_LIST_HEAD(&device->node);
INIT_LIST_HEAD(&device->g_list);
spin_lock(&acpi_device_lock);
if (device->parent) {
list_add_tail(&device->node, &device->parent->children);
list_add_tail(&device->g_list,&device->parent->g_list);
} else
list_add_tail(&device->g_list,&acpi_device_list);
spin_unlock(&acpi_device_lock);
acpi_device_register(device, parent);
}
static int
acpi_bus_add (
struct acpi_device **child,
......@@ -741,7 +741,7 @@ acpi_bus_add (
acpi_device_get_debug_info(device,handle,type);
acpi_device_attach(device,parent);
acpi_device_register(device,parent);
/*
* Bind _ADR-Based Devices
......@@ -919,6 +919,8 @@ static int __init acpi_scan_init(void)
if (acpi_disabled)
return_VALUE(0);
subsystem_register(&acpi_namespace_subsys);
/*
* Create the root device in the bus's device tree
*/
......@@ -935,7 +937,7 @@ static int __init acpi_scan_init(void)
result = acpi_bus_scan(acpi_root);
if (result)
acpi_bus_remove(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
Done:
return_VALUE(result);
......
......@@ -2,13 +2,14 @@
obj-y := core.o sys.o interface.o power.o bus.o \
driver.o class.o intf.o platform.o \
cpu.o
cpu.o firmware.o
obj-y += fs/
obj-$(CONFIG_HOTPLUG) += hotplug.o
export-objs := core.o power.o sys.o bus.o driver.o \
class.o intf.o platform.o cpu.o
class.o intf.o platform.o cpu.o \
firmware.o
include $(TOPDIR)/Rules.make
......@@ -10,44 +10,15 @@ extern struct list_head global_device_list;
extern spinlock_t device_lock;
extern struct semaphore device_sem;
extern struct device * get_device_locked(struct device *);
extern int bus_add_device(struct device * dev);
extern void bus_remove_device(struct device * dev);
extern int device_make_dir(struct device * dev);
extern void device_remove_dir(struct device * dev);
extern int bus_make_dir(struct bus_type * bus);
extern void bus_remove_dir(struct bus_type * bus);
extern int bus_add_driver(struct device_driver *);
extern void bus_remove_driver(struct device_driver *);
extern int driver_make_dir(struct device_driver * drv);
extern void driver_remove_dir(struct device_driver * drv);
extern int device_bus_link(struct device * dev);
extern void device_remove_symlink(struct driver_dir_entry * dir, const char * name);
extern int devclass_make_dir(struct device_class *);
extern void devclass_remove_dir(struct device_class *);
extern int devclass_drv_link(struct device_driver *);
extern void devclass_drv_unlink(struct device_driver *);
extern int devclass_dev_link(struct device_class *, struct device *);
extern void devclass_dev_unlink(struct device_class *, struct device *);
extern int devclass_add_device(struct device *);
extern void devclass_remove_device(struct device *);
extern int intf_make_dir(struct device_interface *);
extern void intf_remove_dir(struct device_interface *);
extern int intf_dev_link(struct intf_data *);
extern void intf_dev_unlink(struct intf_data *);
extern int interface_add(struct device_class *, struct device *);
extern void interface_remove(struct device_class *, struct device *);
......
......@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
#include "base.h"
static LIST_HEAD(bus_driver_list);
......@@ -19,6 +20,109 @@ static LIST_HEAD(bus_driver_list);
#define to_dev(node) container_of(node,struct device,bus_list)
#define to_drv(node) container_of(node,struct device_driver,bus_list)
#define to_bus_attr(_attr) container_of(_attr,struct bus_attribute,attr)
#define to_bus(obj) container_of(obj,struct bus_type,subsys.kobj)
/*
* sysfs bindings for drivers
*/
#define to_drv_attr(_attr) container_of(_attr,struct driver_attribute,attr)
#define to_driver(obj) container_of(obj, struct device_driver, kobj)
static ssize_t
drv_attr_show(struct kobject * kobj, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct driver_attribute * drv_attr = to_drv_attr(attr);
struct device_driver * drv = to_driver(kobj);
ssize_t ret = 0;
if (drv_attr->show)
ret = drv_attr->show(drv,buf,count,off);
return ret;
}
static ssize_t
drv_attr_store(struct kobject * kobj, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct driver_attribute * drv_attr = to_drv_attr(attr);
struct device_driver * drv = to_driver(kobj);
ssize_t ret = 0;
if (drv_attr->store)
ret = drv_attr->store(drv,buf,count,off);
return ret;
}
static struct sysfs_ops driver_sysfs_ops = {
.show = drv_attr_show,
.store = drv_attr_store,
};
/*
* sysfs bindings for drivers
*/
static ssize_t
bus_attr_show(struct kobject * kobj, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct bus_attribute * bus_attr = to_bus_attr(attr);
struct bus_type * bus = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->show)
ret = bus_attr->show(bus,buf,count,off);
return ret;
}
static ssize_t
bus_attr_store(struct kobject * kobj, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct bus_attribute * bus_attr = to_bus_attr(attr);
struct bus_type * bus = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->store)
ret = bus_attr->store(bus,buf,count,off);
return ret;
}
static struct sysfs_ops bus_sysfs_ops = {
.show = bus_attr_show,
.store = bus_attr_store,
};
int bus_create_file(struct bus_type * bus, struct bus_attribute * attr)
{
int error;
if (get_bus(bus)) {
error = sysfs_create_file(&bus->subsys.kobj,&attr->attr);
put_bus(bus);
} else
error = -EINVAL;
return error;
}
void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr)
{
if (get_bus(bus)) {
sysfs_remove_file(&bus->subsys.kobj,&attr->attr);
put_bus(bus);
}
}
struct subsystem bus_subsys = {
.kobj = { .name = "bus" },
.sysfs_ops = &bus_sysfs_ops,
};
/**
* bus_for_each_dev - walk list of devices and do something to each
* @bus: bus in question
......@@ -92,6 +196,7 @@ static void attach(struct device * dev)
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id,dev->driver->name);
list_add_tail(&dev->driver_list,&dev->driver->devices);
sysfs_create_link(&dev->driver->kobj,&dev->kobj,dev->kobj.name);
}
static int bus_match(struct device * dev, struct device_driver * drv)
......@@ -162,6 +267,7 @@ static int driver_attach(struct device_driver * drv)
static void detach(struct device * dev, struct device_driver * drv)
{
if (drv) {
sysfs_remove_link(&drv->kobj,dev->kobj.name);
list_del_init(&dev->driver_list);
devclass_remove_device(dev);
if (drv->remove)
......@@ -206,7 +312,7 @@ int bus_add_device(struct device * dev)
list_add_tail(&dev->bus_list,&dev->bus->devices);
device_attach(dev);
up_write(&dev->bus->rwsem);
device_bus_link(dev);
sysfs_create_link(&bus->devsubsys.kobj,&dev->kobj,dev->bus_id);
}
return 0;
}
......@@ -221,9 +327,9 @@ int bus_add_device(struct device * dev)
void bus_remove_device(struct device * dev)
{
if (dev->bus) {
sysfs_remove_link(&dev->bus->devsubsys.kobj,dev->bus_id);
down_write(&dev->bus->rwsem);
pr_debug("bus %s: remove device %s\n",dev->bus->name,dev->bus_id);
device_remove_symlink(&dev->bus->device_dir,dev->bus_id);
device_detach(dev);
list_del_init(&dev->bus_list);
up_write(&dev->bus->rwsem);
......@@ -240,7 +346,6 @@ int bus_add_driver(struct device_driver * drv)
list_add_tail(&drv->bus_list,&bus->drivers);
driver_attach(drv);
up_write(&bus->rwsem);
driver_make_dir(drv);
}
return 0;
}
......@@ -275,7 +380,6 @@ void put_bus(struct bus_type * bus)
list_del_init(&bus->node);
spin_unlock(&device_lock);
WARN_ON(bus->present);
bus_remove_dir(bus);
}
int bus_register(struct bus_type * bus)
......@@ -286,16 +390,25 @@ int bus_register(struct bus_type * bus)
atomic_set(&bus->refcount,2);
bus->present = 1;
strncpy(bus->subsys.kobj.name,bus->name,KOBJ_NAME_LEN);
bus->subsys.parent = &bus_subsys;
subsystem_register(&bus->subsys);
snprintf(bus->devsubsys.kobj.name,KOBJ_NAME_LEN,"devices");
bus->devsubsys.parent = &bus->subsys;
subsystem_register(&bus->devsubsys);
snprintf(bus->drvsubsys.kobj.name,KOBJ_NAME_LEN,"drivers");
bus->drvsubsys.parent = &bus->subsys;
bus->drvsubsys.sysfs_ops = &driver_sysfs_ops;
subsystem_register(&bus->drvsubsys);
spin_lock(&device_lock);
list_add_tail(&bus->node,&bus_driver_list);
spin_unlock(&device_lock);
pr_debug("bus type '%s' registered\n",bus->name);
/* give it some driverfs entities */
bus_make_dir(bus);
put_bus(bus);
return 0;
}
......@@ -306,9 +419,19 @@ void bus_unregister(struct bus_type * bus)
spin_unlock(&device_lock);
pr_debug("bus %s: unregistering\n",bus->name);
subsystem_unregister(&bus->drvsubsys);
subsystem_unregister(&bus->devsubsys);
subsystem_unregister(&bus->subsys);
put_bus(bus);
}
static int __init bus_subsys_init(void)
{
return subsystem_register(&bus_subsys);
}
core_initcall(bus_subsys_init);
EXPORT_SYMBOL(bus_for_each_dev);
EXPORT_SYMBOL(bus_for_each_drv);
EXPORT_SYMBOL(bus_add_device);
......@@ -317,3 +440,6 @@ EXPORT_SYMBOL(bus_register);
EXPORT_SYMBOL(bus_unregister);
EXPORT_SYMBOL(get_bus);
EXPORT_SYMBOL(put_bus);
EXPORT_SYMBOL(bus_create_file);
EXPORT_SYMBOL(bus_remove_file);
......@@ -4,10 +4,98 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include "base.h"
static LIST_HEAD(class_list);
#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr)
#define to_class(obj) container_of(obj,struct device_class,subsys.kobj)
static ssize_t
devclass_attr_show(struct kobject * kobj, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct devclass_attribute * class_attr = to_class_attr(attr);
struct device_class * dc = to_class(kobj);
ssize_t ret = 0;
if (class_attr->show)
ret = class_attr->show(dc,buf,count,off);
return ret;
}
static ssize_t
devclass_attr_store(struct kobject * kobj, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct devclass_attribute * class_attr = to_class_attr(attr);
struct device_class * dc = to_class(kobj);
ssize_t ret = 0;
if (class_attr->store)
ret = class_attr->store(dc,buf,count,off);
return ret;
}
static struct sysfs_ops class_sysfs_ops = {
show: devclass_attr_show,
store: devclass_attr_store,
};
static struct subsystem class_subsys = {
.kobj = { .name = "class", },
.sysfs_ops = &class_sysfs_ops,
};
static int devclass_dev_link(struct device_class * cls, struct device * dev)
{
char linkname[16];
snprintf(linkname,16,"%u",dev->class_num);
return sysfs_create_link(&cls->devsubsys.kobj,&dev->kobj,linkname);
}
static void devclass_dev_unlink(struct device_class * cls, struct device * dev)
{
char linkname[16];
snprintf(linkname,16,"%u",dev->class_num);
sysfs_remove_link(&cls->devsubsys.kobj,linkname);
}
static int devclass_drv_link(struct device_driver * drv)
{
char name[KOBJ_NAME_LEN * 3];
snprintf(name,KOBJ_NAME_LEN * 3,"%s:%s",drv->bus->name,drv->name);
return sysfs_create_link(&drv->devclass->drvsubsys.kobj,&drv->kobj,name);
}
static void devclass_drv_unlink(struct device_driver * drv)
{
char name[KOBJ_NAME_LEN * 3];
snprintf(name,KOBJ_NAME_LEN * 3,"%s:%s",drv->bus->name,drv->name);
return sysfs_remove_link(&drv->devclass->drvsubsys.kobj,name);
}
int devclass_create_file(struct device_class * cls, struct devclass_attribute * attr)
{
int error;
if (cls) {
error = sysfs_create_file(&cls->subsys.kobj,&attr->attr);
} else
error = -EINVAL;
return error;
}
void devclass_remove_file(struct device_class * cls, struct devclass_attribute * attr)
{
if (cls)
sysfs_remove_file(&cls->subsys.kobj,&attr->attr);
}
int devclass_add_driver(struct device_driver * drv)
{
struct device_class * cls = get_devclass(drv->devclass);
......@@ -136,7 +224,6 @@ void put_devclass(struct device_class * cls)
if (atomic_dec_and_lock(&cls->refcount,&device_lock)) {
list_del_init(&cls->node);
spin_unlock(&device_lock);
devclass_remove_dir(cls);
}
}
......@@ -150,10 +237,21 @@ int devclass_register(struct device_class * cls)
cls->present = 1;
pr_debug("device class '%s': registering\n",cls->name);
strncpy(cls->subsys.kobj.name,cls->name,KOBJ_NAME_LEN);
cls->subsys.parent = &class_subsys;
subsystem_register(&cls->subsys);
snprintf(cls->devsubsys.kobj.name,KOBJ_NAME_LEN,"devices");
cls->devsubsys.parent = &cls->subsys;
subsystem_register(&cls->devsubsys);
snprintf(cls->drvsubsys.kobj.name,KOBJ_NAME_LEN,"drivers");
cls->drvsubsys.parent = &cls->subsys;
subsystem_register(&cls->drvsubsys);
spin_lock(&device_lock);
list_add_tail(&cls->node,&class_list);
spin_unlock(&device_lock);
devclass_make_dir(cls);
put_devclass(cls);
return 0;
}
......@@ -164,9 +262,19 @@ void devclass_unregister(struct device_class * cls)
cls->present = 0;
spin_unlock(&device_lock);
pr_debug("device class '%s': unregistering\n",cls->name);
subsystem_unregister(&cls->drvsubsys);
subsystem_unregister(&cls->devsubsys);
subsystem_unregister(&cls->subsys);
put_devclass(cls);
}
static int __init class_subsys_init(void)
{
return subsystem_register(&class_subsys);
}
core_initcall(class_subsys_init);
EXPORT_SYMBOL(devclass_register);
EXPORT_SYMBOL(devclass_unregister);
EXPORT_SYMBOL(get_devclass);
......
......@@ -23,7 +23,74 @@ DECLARE_MUTEX(device_sem);
spinlock_t device_lock = SPIN_LOCK_UNLOCKED;
#define to_dev(node) container_of(node,struct device,driver_list)
#define to_dev(obj) container_of(obj,struct device,kobj)
/*
* sysfs bindings for devices.
*/
#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr)
extern struct attribute * dev_default_attrs[];
static ssize_t
dev_attr_show(struct kobject * kobj, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct device_attribute * dev_attr = to_dev_attr(attr);
struct device * dev = to_dev(kobj);
ssize_t ret = 0;
if (dev_attr->show)
ret = dev_attr->show(dev,buf,count,off);
return ret;
}
static ssize_t
dev_attr_store(struct kobject * kobj, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct device_attribute * dev_attr = to_dev_attr(attr);
struct device * dev = to_dev(kobj);
ssize_t ret = 0;
if (dev_attr->store)
ret = dev_attr->store(dev,buf,count,off);
return ret;
}
static struct sysfs_ops dev_sysfs_ops = {
.show = dev_attr_show,
.store = dev_attr_store,
};
struct subsystem device_subsys = {
.kobj = {
.name = "devices",
},
.sysfs_ops = &dev_sysfs_ops,
.default_attrs = dev_default_attrs,
};
int device_create_file(struct device * dev, struct device_attribute * attr)
{
int error = 0;
if (get_device(dev)) {
error = sysfs_create_file(&dev->kobj,&attr->attr);
put_device(dev);
}
return error;
}
void device_remove_file(struct device * dev, struct device_attribute * attr)
{
if (get_device(dev)) {
sysfs_remove_file(&dev->kobj,&attr->attr);
put_device(dev);
}
}
int device_add(struct device *dev)
{
......@@ -44,7 +111,11 @@ int device_add(struct device *dev)
pr_debug("DEV: registering device: ID = '%s', name = %s\n",
dev->bus_id, dev->name);
if ((error = device_make_dir(dev)))
strncpy(dev->kobj.name,dev->bus_id,KOBJ_NAME_LEN);
if (dev->parent)
dev->kobj.parent = &dev->parent->kobj;
dev->kobj.subsys = &device_subsys;
if ((error = kobject_register(&dev->kobj)))
goto register_done;
bus_add_device(dev);
......@@ -69,6 +140,7 @@ int device_add(struct device *dev)
void device_initialize(struct device *dev)
{
kobject_init(&dev->kobj);
INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->children);
INIT_LIST_HEAD(&dev->g_list);
......@@ -157,9 +229,6 @@ void device_del(struct device * dev)
bus_remove_device(dev);
/* remove the driverfs directory */
device_remove_dir(dev);
if (dev->release)
dev->release(dev);
......@@ -184,10 +253,21 @@ void device_unregister(struct device * dev)
pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n",
dev->bus_id,dev->name);
kobject_unregister(&dev->kobj);
put_device(dev);
}
static int __init device_subsys_init(void)
{
return subsystem_register(&device_subsys);
}
core_initcall(device_subsys_init);
EXPORT_SYMBOL(device_register);
EXPORT_SYMBOL(device_unregister);
EXPORT_SYMBOL(get_device);
EXPORT_SYMBOL(put_device);
EXPORT_SYMBOL(device_create_file);
EXPORT_SYMBOL(device_remove_file);
......@@ -12,6 +12,29 @@
#define to_dev(node) container_of(node,struct device,driver_list)
/*
* helpers for creating driver attributes in sysfs
*/
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr)
{
int error;
if (get_driver(drv)) {
error = sysfs_create_file(&drv->kobj,&attr->attr);
put_driver(drv);
} else
error = -EINVAL;
return error;
}
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr)
{
if (get_driver(drv)) {
sysfs_remove_file(&drv->kobj,&attr->attr);
put_driver(drv);
}
}
int driver_for_each_dev(struct device_driver * drv, void * data,
int (*callback)(struct device *, void * ))
{
......@@ -65,7 +88,6 @@ void put_driver(struct device_driver * drv)
return;
spin_unlock(&device_lock);
BUG_ON(drv->present);
bus_remove_driver(drv);
if (drv->release)
drv->release(drv);
put_bus(bus);
......@@ -84,6 +106,11 @@ int driver_register(struct device_driver * drv)
pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name);
kobject_init(&drv->kobj);
strncpy(drv->kobj.name,drv->name,KOBJ_NAME_LEN);
drv->kobj.subsys = &drv->bus->drvsubsys;
kobject_register(&drv->kobj);
get_bus(drv->bus);
atomic_set(&drv->refcount,2);
rwlock_init(&drv->lock);
......@@ -108,3 +135,6 @@ EXPORT_SYMBOL(driver_register);
EXPORT_SYMBOL(driver_unregister);
EXPORT_SYMBOL(get_driver);
EXPORT_SYMBOL(put_driver);
EXPORT_SYMBOL(driver_create_file);
EXPORT_SYMBOL(driver_remove_file);
/*
* firmware.c - firmware subsystem hoohaw.
*/
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/init.h>
static struct subsystem firmware_subsys = {
.kobj = { .name = "firmware" },
};
int firmware_register(struct subsystem * s)
{
s->parent = &firmware_subsys;
return subsystem_register(s);
}
void firmware_unregister(struct subsystem * s)
{
subsystem_unregister(s);
}
static int __init firmware_init(void)
{
return subsystem_register(&firmware_subsys);
}
core_initcall(firmware_init);
EXPORT_SYMBOL(firmware_register);
EXPORT_SYMBOL(firmware_unregister);
obj-y := device.o bus.o driver.o class.o intf.o
obj-y := device.o
export-objs := device.o bus.o driver.o class.o
export-objs := device.o
include $(TOPDIR)/Rules.make
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/stat.h>
#include "fs.h"
static struct driver_dir_entry bus_dir;
#define to_bus_attr(_attr) container_of(_attr,struct bus_attribute,attr)
#define to_bus(dir) container_of(dir,struct bus_type,dir)
/* driverfs ops for device attribute files */
static int
bus_attr_open(struct driver_dir_entry * dir)
{
struct bus_type * bus = to_bus(dir);
get_bus(bus);
return 0;
}
static int
bus_attr_close(struct driver_dir_entry * dir)
{
struct bus_type * bus = to_bus(dir);
put_bus(bus);
return 0;
}
static ssize_t
bus_attr_show(struct driver_dir_entry * dir, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct bus_attribute * bus_attr = to_bus_attr(attr);
struct bus_type * bus = to_bus(dir);
ssize_t ret = 0;
if (bus_attr->show)
ret = bus_attr->show(bus,buf,count,off);
return ret;
}
static ssize_t
bus_attr_store(struct driver_dir_entry * dir, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct bus_attribute * bus_attr = to_bus_attr(attr);
struct bus_type * bus = to_bus(dir);
ssize_t ret = 0;
if (bus_attr->store)
ret = bus_attr->store(bus,buf,count,off);
return ret;
}
static struct driverfs_ops bus_attr_ops = {
.open = bus_attr_open,
.close = bus_attr_close,
.show = bus_attr_show,
.store = bus_attr_store,
};
int bus_create_file(struct bus_type * bus, struct bus_attribute * attr)
{
int error;
if (get_bus(bus)) {
error = driverfs_create_file(&attr->attr,&bus->dir);
put_bus(bus);
} else
error = -EINVAL;
return error;
}
void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr)
{
if (get_bus(bus)) {
driverfs_remove_file(&bus->dir,attr->attr.name);
put_bus(bus);
}
}
int bus_make_dir(struct bus_type * bus)
{
int error;
bus->dir.name = bus->name;
bus->dir.ops = &bus_attr_ops;
error = device_create_dir(&bus->dir,&bus_dir);
if (!error) {
bus->device_dir.name = "devices";
device_create_dir(&bus->device_dir,&bus->dir);
bus->driver_dir.name = "drivers";
device_create_dir(&bus->driver_dir,&bus->dir);
}
return error;
}
void bus_remove_dir(struct bus_type * bus)
{
/* remove driverfs entries */
driverfs_remove_dir(&bus->driver_dir);
driverfs_remove_dir(&bus->device_dir);
driverfs_remove_dir(&bus->dir);
}
static struct driver_dir_entry bus_dir = {
.name = "bus",
.mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO),
};
static int __init bus_init(void)
{
/* make 'bus' driverfs directory */
return driverfs_create_dir(&bus_dir,NULL);
}
core_initcall(bus_init);
EXPORT_SYMBOL(bus_create_file);
EXPORT_SYMBOL(bus_remove_file);
/*
* class.c - driverfs bindings for device classes.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include "fs.h"
static struct driver_dir_entry class_dir;
#define to_class_attr(_attr) container_of(_attr,struct devclass_attribute,attr)
#define to_class(d) container_of(d,struct device_class,dir)
static ssize_t
devclass_attr_show(struct driver_dir_entry * dir, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct devclass_attribute * class_attr = to_class_attr(attr);
struct device_class * dc = to_class(dir);
ssize_t ret = 0;
if (class_attr->show)
ret = class_attr->show(dc,buf,count,off);
return ret;
}
static ssize_t
devclass_attr_store(struct driver_dir_entry * dir, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct devclass_attribute * class_attr = to_class_attr(attr);
struct device_class * dc = to_class(dir);
ssize_t ret = 0;
if (class_attr->store)
ret = class_attr->store(dc,buf,count,off);
return ret;
}
static struct driverfs_ops devclass_attr_ops = {
show: devclass_attr_show,
store: devclass_attr_store,
};
int devclass_create_file(struct device_class * dc, struct devclass_attribute * attr)
{
int error;
if (dc) {
error = driverfs_create_file(&attr->attr,&dc->dir);
} else
error = -EINVAL;
return error;
}
void devclass_remove_file(struct device_class * dc, struct devclass_attribute * attr)
{
if (dc)
driverfs_remove_file(&dc->dir,attr->attr.name);
}
/**
* devclass_dev_link - create symlink to device's directory
* @cls - device class we're a part of
* @dev - device we're linking to
*
* Create a symlink in the class's devices/ directory to @dev's
* directory in the physical hierarchy. The name is the device's
* class-enumerated value (struct device::class_num). We're
* creating:
* class/<class name>/devices/<link name> ->
* root/<path to device>/<device's dir>
* So, the link looks like:
* ../../../root/<path to device>/
*/
int devclass_dev_link(struct device_class * cls, struct device * dev)
{
char linkname[16];
char * path;
int length;
int error;
length = get_devpath_length(dev);
length += strlen("../../../root");
if (length > PATH_MAX)
return -ENAMETOOLONG;
if (!(path = kmalloc(length,GFP_KERNEL)))
return -ENOMEM;
memset(path,0,length);
strcpy(path,"../../../root");
fill_devpath(dev,path,length);
snprintf(linkname,16,"%u",dev->class_num);
error = driverfs_create_symlink(&cls->device_dir,linkname,path);
kfree(path);
return error;
}
void devclass_dev_unlink(struct device_class * cls, struct device * dev)
{
char linkname[16];
snprintf(linkname,16,"%u",dev->class_num);
driverfs_remove_file(&cls->device_dir,linkname);
}
/**
* devclass_drv_link - create symlink to driver's directory
* @drv: driver we're linking up
*
* Create a symlink in the class's drivers/ directory to @drv's
* directory (in the bus's directory). It's name is <bus>:<driver>
* to prevent naming conflicts.
*
* We're creating
* class/<class name>/drivers/<link name> ->
* bus/<bus name>/drivers/<driver name>/
* So, the link looks like:
* ../../../bus/<bus name>/drivers/<driver name>
*/
int devclass_drv_link(struct device_driver * drv)
{
char * name;
char * path;
int namelen;
int pathlen;
int error = 0;
namelen = strlen(drv->name) + strlen(drv->bus->name) + 2;
name = kmalloc(namelen,GFP_KERNEL);
if (!name)
return -ENOMEM;
snprintf(name,namelen,"%s:%s",drv->bus->name,drv->name);
pathlen = strlen("../../../bus/") +
strlen(drv->bus->name) +
strlen("/drivers/") +
strlen(drv->name) + 1;
if (!(path = kmalloc(pathlen,GFP_KERNEL))) {
error = -ENOMEM;
goto Done;
}
snprintf(path,pathlen,"%s%s%s%s",
"../../../bus/",
drv->bus->name,
"/drivers/",
drv->name);
error = driverfs_create_symlink(&drv->devclass->driver_dir,name,path);
Done:
kfree(name);
kfree(path);
return error;
}
void devclass_drv_unlink(struct device_driver * drv)
{
char * name;
int length;
length = strlen(drv->name) + strlen(drv->bus->name) + 2;
if ((name = kmalloc(length,GFP_KERNEL))) {
driverfs_remove_file(&drv->devclass->driver_dir,name);
kfree(name);
}
}
void devclass_remove_dir(struct device_class * dc)
{
driverfs_remove_dir(&dc->device_dir);
driverfs_remove_dir(&dc->driver_dir);
driverfs_remove_dir(&dc->dir);
}
int devclass_make_dir(struct device_class * dc)
{
int error;
dc->dir.name = dc->name;
dc->dir.ops = &devclass_attr_ops;
error = device_create_dir(&dc->dir,&class_dir);
if (!error) {
dc->driver_dir.name = "drivers";
error = device_create_dir(&dc->driver_dir,&dc->dir);
if (!error) {
dc->device_dir.name = "devices";
error = device_create_dir(&dc->device_dir,&dc->dir);
}
if (error)
driverfs_remove_dir(&dc->dir);
}
return error;
}
static struct driver_dir_entry class_dir = {
name: "class",
mode: (S_IRWXU | S_IRUGO | S_IXUGO),
};
static int __init devclass_driverfs_init(void)
{
return driverfs_create_dir(&class_dir,NULL);
}
core_initcall(devclass_driverfs_init);
EXPORT_SYMBOL(devclass_create_file);
EXPORT_SYMBOL(devclass_remove_file);
......@@ -16,112 +16,6 @@
#include <linux/stat.h>
#include <linux/limits.h>
static struct driver_dir_entry device_root_dir = {
.name = "root",
.mode = (S_IRWXU | S_IRUGO | S_IXUGO),
};
extern struct device_attribute * device_default_files[];
#define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr)
#define to_device(d) container_of(d, struct device, dir)
/* driverfs ops for device attribute files */
static int
dev_attr_open(struct driver_dir_entry * dir)
{
struct device * dev = to_device(dir);
get_device(dev);
return 0;
}
static int
dev_attr_close(struct driver_dir_entry * dir)
{
struct device * dev = to_device(dir);
put_device(dev);
return 0;
}
static ssize_t
dev_attr_show(struct driver_dir_entry * dir, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct device_attribute * dev_attr = to_dev_attr(attr);
struct device * dev = to_device(dir);
ssize_t ret = 0;
if (dev_attr->show)
ret = dev_attr->show(dev,buf,count,off);
return ret;
}
static ssize_t
dev_attr_store(struct driver_dir_entry * dir, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct device_attribute * dev_attr = to_dev_attr(attr);
struct device * dev = to_device(dir);
ssize_t ret = 0;
if (dev_attr->store)
ret = dev_attr->store(dev,buf,count,off);
return ret;
}
static struct driverfs_ops dev_attr_ops = {
.open = dev_attr_open,
.close = dev_attr_close,
.show = dev_attr_show,
.store = dev_attr_store,
};
/**
* device_create_file - create a driverfs file for a device
* @dev: device requesting file
* @entry: entry describing file
*
* Allocate space for file entry, copy descriptor, and create.
*/
int device_create_file(struct device * dev, struct device_attribute * entry)
{
int error = -EINVAL;
if (get_device(dev)) {
error = driverfs_create_file(&entry->attr,&dev->dir);
put_device(dev);
}
return error;
}
/**
* device_remove_file - remove a device's file by name
* @dev: device requesting removal
* @name: name of the file
*
*/
void device_remove_file(struct device * dev, struct device_attribute * attr)
{
if (dev) {
get_device(dev);
driverfs_remove_file(&dev->dir,attr->attr.name);
put_device(dev);
}
}
/**
* device_remove_dir - remove a device's directory
* @dev: device in question
*/
void device_remove_dir(struct device * dev)
{
if (dev)
driverfs_remove_dir(&dev->dir);
}
int get_devpath_length(struct device * dev)
{
int length = 1;
......@@ -153,92 +47,3 @@ void fill_devpath(struct device * dev, char * path, int length)
pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
}
int device_bus_link(struct device * dev)
{
char * path;
int length;
int error = 0;
if (!dev->bus)
return 0;
length = get_devpath_length(dev);
/* now add the path from the bus directory
* It should be '../../..' (one to get to the bus's directory,
* one to get to the 'bus' directory, and one to get to the root
* of the fs.)
*/
length += strlen("../../../root");
if (length > PATH_MAX)
return -ENAMETOOLONG;
if (!(path = kmalloc(length,GFP_KERNEL)))
return -ENOMEM;
memset(path,0,length);
/* our relative position */
strcpy(path,"../../../root");
fill_devpath(dev,path,length);
error = driverfs_create_symlink(&dev->bus->device_dir,dev->bus_id,path);
kfree(path);
return error;
}
void device_remove_symlink(struct driver_dir_entry * dir, const char * name)
{
driverfs_remove_file(dir,name);
}
int device_create_dir(struct driver_dir_entry * dir, struct driver_dir_entry * parent)
{
dir->mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO);
return driverfs_create_dir(dir,parent);
}
/**
* device_make_dir - create a driverfs directory
* @name: name of directory
* @parent: dentry for the parent directory
*
* Do the initial creation of the device's driverfs directory
* and populate it with the one default file.
*
* This is just a helper for device_register(), as we
* don't export this function. (Yes, that means we don't allow
* devices to create subdirectories).
*/
int device_make_dir(struct device * dev)
{
struct driver_dir_entry * parent;
struct device_attribute * entry;
int error;
int i;
parent = dev->parent ? &dev->parent->dir : &device_root_dir;
dev->dir.name = dev->bus_id;
dev->dir.ops = &dev_attr_ops;
if ((error = device_create_dir(&dev->dir,parent)))
return error;
for (i = 0; (entry = *(device_default_files + i)); i++) {
if ((error = device_create_file(dev,entry))) {
device_remove_dir(dev);
break;
}
}
return error;
}
static int device_driverfs_init(void)
{
return driverfs_create_dir(&device_root_dir,NULL);
}
core_initcall(device_driverfs_init);
EXPORT_SYMBOL(device_create_file);
EXPORT_SYMBOL(device_remove_file);
#include <linux/device.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/err.h>
#include "fs.h"
#define to_drv_attr(_attr) container_of(_attr,struct driver_attribute,attr)
#define to_drv(d) container_of(d, struct device_driver, dir)
/* driverfs ops for device attribute files */
static int
drv_attr_open(struct driver_dir_entry * dir)
{
struct device_driver * drv = to_drv(dir);
get_driver(drv);
return 0;
}
static int
drv_attr_close(struct driver_dir_entry * dir)
{
struct device_driver * drv = to_drv(dir);
put_driver(drv);
return 0;
}
static ssize_t
drv_attr_show(struct driver_dir_entry * dir, struct attribute * attr,
char * buf, size_t count, loff_t off)
{
struct driver_attribute * drv_attr = to_drv_attr(attr);
struct device_driver * drv = to_drv(dir);
ssize_t ret = 0;
if (drv_attr->show)
ret = drv_attr->show(drv,buf,count,off);
return ret;
}
static ssize_t
drv_attr_store(struct driver_dir_entry * dir, struct attribute * attr,
const char * buf, size_t count, loff_t off)
{
struct driver_attribute * drv_attr = to_drv_attr(attr);
struct device_driver * drv = to_drv(dir);
ssize_t ret = 0;
if (drv_attr->store)
ret = drv_attr->store(drv,buf,count,off);
return ret;
}
static struct driverfs_ops drv_attr_ops = {
.open = drv_attr_open,
.close = drv_attr_close,
.show = drv_attr_show,
.store = drv_attr_store,
};
int driver_create_file(struct device_driver * drv, struct driver_attribute * attr)
{
int error;
if (get_driver(drv)) {
error = driverfs_create_file(&attr->attr,&drv->dir);
put_driver(drv);
} else
error = -EINVAL;
return error;
}
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr)
{
if (get_driver(drv)) {
driverfs_remove_file(&drv->dir,attr->attr.name);
put_driver(drv);
}
}
/**
* driver_make_dir - create a driverfs directory for a driver
* @drv: driver in question
*/
int driver_make_dir(struct device_driver * drv)
{
drv->dir.name = drv->name;
drv->dir.ops = &drv_attr_ops;
return device_create_dir(&drv->dir,&drv->bus->driver_dir);
}
void driver_remove_dir(struct device_driver * drv)
{
driverfs_remove_dir(&drv->dir);
}
EXPORT_SYMBOL(driver_create_file);
EXPORT_SYMBOL(driver_remove_file);
extern int device_create_dir(struct driver_dir_entry * dir, struct driver_dir_entry * parent);
int get_devpath_length(struct device * dev);
void fill_devpath(struct device * dev, char * path, int length);
......
/*
* intf.c - driverfs glue for device interfaces
*/
#include <linux/device.h>
#include <linux/slab.h>
#include "fs.h"
/**
* intf_dev_link - symlink from interface's directory to device's directory
*
*/
int intf_dev_link(struct intf_data * data)
{
char linkname[16];
char * path;
int length;
int error;
length = get_devpath_length(data->dev);
length += strlen("../../../root");
if (length > PATH_MAX)
return -ENAMETOOLONG;
if (!(path = kmalloc(length,GFP_KERNEL)))
return -ENOMEM;
memset(path,0,length);
strcpy(path,"../../../root");
fill_devpath(data->dev,path,length);
snprintf(linkname,16,"%u",data->intf_num);
error = driverfs_create_symlink(&data->intf->dir,linkname,path);
kfree(path);
return error;
}
void intf_dev_unlink(struct intf_data * data)
{
char linkname[16];
snprintf(linkname,16,"%u",data->intf_num);
driverfs_remove_file(&data->intf->dir,linkname);
}
void intf_remove_dir(struct device_interface * intf)
{
driverfs_remove_dir(&intf->dir);
}
int intf_make_dir(struct device_interface * intf)
{
intf->dir.name = intf->name;
return device_create_dir(&intf->dir,&intf->devclass->dir);
}
......@@ -88,8 +88,8 @@ device_write_power(struct device * dev, const char * buf, size_t count, loff_t o
static DEVICE_ATTR(power,S_IWUSR | S_IRUGO,
device_read_power,device_write_power);
struct device_attribute * device_default_files[] = {
&dev_attr_name,
&dev_attr_power,
struct attribute * dev_default_attrs[] = {
&dev_attr_name.attr,
&dev_attr_power.attr,
NULL,
};
......@@ -11,6 +11,26 @@
#define to_intf(node) container_of(node,struct device_interface,node)
/**
* intf_dev_link - symlink from interface's directory to device's directory
*
*/
static int intf_dev_link(struct intf_data * data)
{
char linkname[16];
snprintf(linkname,16,"%u",data->intf_num);
return sysfs_create_link(&data->intf->kobj,&data->dev->kobj,linkname);
}
static void intf_dev_unlink(struct intf_data * data)
{
char linkname[16];
snprintf(linkname,16,"%u",data->intf_num);
sysfs_remove_link(&data->intf->kobj,linkname);
}
int interface_register(struct device_interface * intf)
{
struct device_class * cls = intf->devclass;
......@@ -18,7 +38,11 @@ int interface_register(struct device_interface * intf)
if (cls) {
pr_debug("register interface '%s' with class '%s\n",
intf->name,cls->name);
intf_make_dir(intf);
kobject_init(&intf->kobj);
strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN);
intf->kobj.subsys = &cls->subsys;
kobject_register(&intf->kobj);
spin_lock(&device_lock);
list_add_tail(&intf->node,&cls->intf_list);
spin_unlock(&device_lock);
......@@ -31,11 +55,10 @@ void interface_unregister(struct device_interface * intf)
{
pr_debug("unregistering interface '%s' from class '%s'\n",
intf->name,intf->devclass->name);
kobject_unregister(&intf->kobj);
spin_lock(&device_lock);
list_del_init(&intf->node);
spin_unlock(&device_lock);
intf_remove_dir(intf);
}
int interface_add(struct device_class * cls, struct device * dev)
......
......@@ -252,14 +252,6 @@ struct seq_operations partitions_op = {
extern int blk_dev_init(void);
struct device_class disk_devclass = {
.name = "disk",
};
static struct bus_type disk_bus = {
name: "block",
};
static struct gendisk *base_probe(dev_t dev, int *part, void *data)
{
char name[20];
......@@ -268,6 +260,8 @@ static struct gendisk *base_probe(dev_t dev, int *part, void *data)
return NULL;
}
struct subsystem block_subsys;
int __init device_init(void)
{
struct blk_probe *base = kmalloc(sizeof(struct blk_probe), GFP_KERNEL);
......@@ -280,23 +274,116 @@ int __init device_init(void)
for (i = 1; i < MAX_BLKDEV; i++)
probes[i] = base;
blk_dev_init();
devclass_register(&disk_devclass);
bus_register(&disk_bus);
subsystem_register(&block_subsys);
return 0;
}
__initcall(device_init);
subsys_initcall(device_init);
/*
* kobject & sysfs bindings for block devices
*/
#define to_disk(obj) container_of(obj,struct gendisk,kobj)
struct disk_attribute {
struct attribute attr;
ssize_t (*show)(struct gendisk *, char *, size_t, loff_t);
};
static ssize_t disk_attr_show(struct kobject * kobj, struct attribute * attr,
char * page, size_t count, loff_t off)
{
struct gendisk * disk = to_disk(kobj);
struct disk_attribute * disk_attr = container_of(attr,struct disk_attribute,attr);
ssize_t ret = 0;
if (disk_attr->show)
ret = disk_attr->show(disk,page,count,off);
return ret;
}
static struct sysfs_ops disk_sysfs_ops = {
.show = &disk_attr_show,
};
static ssize_t disk_dev_read(struct gendisk * disk,
char *page, size_t count, loff_t off)
{
dev_t base = MKDEV(disk->major, disk->first_minor);
return off ? 0 : sprintf(page, "%04x\n",base);
}
static ssize_t disk_range_read(struct gendisk * disk,
char *page, size_t count, loff_t off)
{
return off ? 0 : sprintf(page, "%d\n",disk->minors);
}
static ssize_t disk_size_read(struct gendisk * disk,
char *page, size_t count, loff_t off)
{
return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)get_capacity(disk));
}
static inline unsigned MSEC(unsigned x)
{
return x * 1000 / HZ;
}
static ssize_t disk_stat_read(struct gendisk * disk,
char *page, size_t count, loff_t off)
{
disk_round_stats(disk);
return off ? 0 : sprintf(page,
"%8u %8u %8llu %8u "
"%8u %8u %8llu %8u "
"%8u %8u %8u"
"\n",
disk->reads, disk->read_merges, (u64)disk->read_sectors,
MSEC(disk->read_ticks),
disk->writes, disk->write_merges, (u64)disk->write_sectors,
MSEC(disk->write_ticks),
disk->in_flight, MSEC(disk->io_ticks),
MSEC(disk->time_in_queue));
}
static struct disk_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
};
static struct disk_attribute disk_attr_range = {
.attr = {.name = "range", .mode = S_IRUGO },
.show = disk_range_read
};
static struct disk_attribute disk_attr_size = {
.attr = {.name = "size", .mode = S_IRUGO },
.show = disk_size_read
};
static struct disk_attribute disk_attr_stat = {
.attr = {.name = "stat", .mode = S_IRUGO },
.show = disk_stat_read
};
EXPORT_SYMBOL(disk_devclass);
static struct attribute * default_attrs[] = {
&disk_attr_dev.attr,
&disk_attr_range.attr,
&disk_attr_size.attr,
&disk_attr_stat.attr,
NULL,
};
static void disk_release(struct device *dev)
static void disk_release(struct kobject * kobj)
{
struct gendisk *disk = dev->driver_data;
struct gendisk *disk = to_disk(kobj);
kfree(disk->random);
kfree(disk->part);
kfree(disk);
}
struct subsystem block_subsys = {
.kobj = { .name = "block" },
.release = disk_release,
.sysfs_ops = &disk_sysfs_ops,
.default_attrs = default_attrs,
};
struct gendisk *alloc_disk(int minors)
{
struct gendisk *disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL);
......@@ -314,11 +401,9 @@ struct gendisk *alloc_disk(int minors)
disk->minors = minors;
while (minors >>= 1)
disk->minor_shift++;
kobject_init(&disk->kobj);
disk->kobj.subsys = &block_subsys;
INIT_LIST_HEAD(&disk->full_list);
disk->disk_dev.bus = &disk_bus;
disk->disk_dev.release = disk_release;
disk->disk_dev.driver_data = disk;
device_initialize(&disk->disk_dev);
}
rand_initialize_disk(disk);
return disk;
......@@ -332,14 +417,13 @@ struct gendisk *get_disk(struct gendisk *disk)
owner = disk->fops->owner;
if (owner && !try_inc_mod_count(owner))
return NULL;
atomic_inc(&disk->disk_dev.refcount);
return disk;
return to_disk(kobject_get(&disk->kobj));
}
void put_disk(struct gendisk *disk)
{
if (disk)
put_device(&disk->disk_dev);
kobject_put(&disk->kobj);
}
EXPORT_SYMBOL(alloc_disk);
......
......@@ -41,7 +41,7 @@ obj-$(CONFIG_QUOTACTL) += quota.o
obj-$(CONFIG_PROC_FS) += proc/
obj-y += partitions/
obj-y += driverfs/ sysfs/
obj-y += sysfs/
obj-y += devpts/
obj-$(CONFIG_PROFILING) += dcookies.o
......
#
# Makefile for the driverfs virtual filesystem.
#
export-objs := inode.o
obj-y := inode.o
include $(TOPDIR)/Rules.make
/*
* driverfs.c - The device driver file system
*
* Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* This is a simple, ram-based filesystem, which allows kernel
* callbacks for read/write of files.
*
* Please see Documentation/filesystems/driverfs.txt for more information.
*/
#include <linux/list.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/dcache.h>
#include <linux/namei.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/backing-dev.h>
#include <linux/driverfs_fs.h>
#include <asm/uaccess.h>
#undef DEBUG
#ifdef DEBUG
# define DBG(x...) printk(x)
#else
# define DBG(x...)
#endif
/* Random magic number */
#define DRIVERFS_MAGIC 0x42454552
static struct super_operations driverfs_ops;
static struct file_operations driverfs_file_operations;
static struct inode_operations driverfs_dir_inode_operations;
static struct address_space_operations driverfs_aops;
static struct vfsmount *driverfs_mount;
static spinlock_t mount_lock = SPIN_LOCK_UNLOCKED;
static int mount_count = 0;
static struct backing_dev_info driverfs_backing_dev_info = {
.ra_pages = 0, /* No readahead */
.memory_backed = 1, /* Does not contribute to dirty memory */
};
static int driverfs_readpage(struct file *file, struct page * page)
{
if (!PageUptodate(page)) {
void *kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr, 0, PAGE_CACHE_SIZE);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
SetPageUptodate(page);
}
unlock_page(page);
return 0;
}
static int driverfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
{
if (!PageUptodate(page)) {
void *kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr, 0, PAGE_CACHE_SIZE);
flush_dcache_page(page);
kunmap_atomic(kaddr, KM_USER0);
SetPageUptodate(page);
}
return 0;
}
static int driverfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
{
struct inode *inode = page->mapping->host;
loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
set_page_dirty(page);
if (pos > inode->i_size)
inode->i_size = pos;
return 0;
}
struct inode *driverfs_get_inode(struct super_block *sb, int mode, int dev)
{
struct inode *inode = new_inode(sb);
if (inode) {
inode->i_mode = mode;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_rdev = NODEV;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
inode->i_mapping->a_ops = &driverfs_aops;
inode->i_mapping->backing_dev_info = &driverfs_backing_dev_info;
switch (mode & S_IFMT) {
default:
init_special_inode(inode, mode, dev);
break;
case S_IFREG:
inode->i_fop = &driverfs_file_operations;
break;
case S_IFDIR:
inode->i_op = &driverfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inode->i_nlink++;
break;
case S_IFLNK:
inode->i_op = &page_symlink_inode_operations;
break;
}
}
return inode;
}
static int driverfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
{
struct inode *inode;
int error = -ENOSPC;
if (dentry->d_inode)
return -EEXIST;
/* only allow create if ->d_fsdata is not NULL (so we can assume it
* comes from the driverfs API below. */
inode = driverfs_get_inode(dir->i_sb, mode, dev);
if (inode) {
d_instantiate(dentry, inode);
error = 0;
}
return error;
}
static int driverfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
int res;
mode = (mode & (S_IRWXUGO|S_ISVTX)) | S_IFDIR;
res = driverfs_mknod(dir, dentry, mode, 0);
if (!res)
dir->i_nlink++;
return res;
}
static int driverfs_create(struct inode *dir, struct dentry *dentry, int mode)
{
int res;
mode = (mode & S_IALLUGO) | S_IFREG;
res = driverfs_mknod(dir, dentry, mode, 0);
return res;
}
static int driverfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
{
struct inode *inode;
int error = -ENOSPC;
if (dentry->d_inode)
return -EEXIST;
inode = driverfs_get_inode(dir->i_sb, S_IFLNK|S_IRWXUGO, 0);
if (inode) {
int l = strlen(symname)+1;
error = page_symlink(inode, symname, l);
if (!error) {
d_instantiate(dentry, inode);
dget(dentry);
} else
iput(inode);
}
return error;
}
static inline int driverfs_positive(struct dentry *dentry)
{
return (dentry->d_inode && !d_unhashed(dentry));
}
static int driverfs_empty(struct dentry *dentry)
{
struct list_head *list;
spin_lock(&dcache_lock);
list_for_each(list, &dentry->d_subdirs) {
struct dentry *de = list_entry(list, struct dentry, d_child);
if (driverfs_positive(de)) {
spin_unlock(&dcache_lock);
return 0;
}
}
spin_unlock(&dcache_lock);
return 1;
}
static int driverfs_unlink(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
down(&inode->i_sem);
dentry->d_inode->i_nlink--;
up(&inode->i_sem);
d_invalidate(dentry);
dput(dentry);
return 0;
}
/**
* driverfs_read_file - "read" data from a file.
* @file: file pointer
* @buf: buffer to fill
* @count: number of bytes to read
* @ppos: starting offset in file
*
* Userspace wants data from a file. It is up to the creator of the file to
* provide that data.
* There is a struct device_attribute embedded in file->private_data. We
* obtain that and check if the read callback is implemented. If so, we call
* it, passing the data field of the file entry.
* Said callback is responsible for filling the buffer and returning the number
* of bytes it put in it. We update @ppos correctly.
*/
static ssize_t
driverfs_read_file(struct file *file, char *buf, size_t count, loff_t *ppos)
{
struct attribute * attr = file->f_dentry->d_fsdata;
struct driver_dir_entry * dir;
unsigned char *page;
ssize_t retval = 0;
dir = file->f_dentry->d_parent->d_fsdata;
if (!dir->ops->show)
return 0;
if (count > PAGE_SIZE)
count = PAGE_SIZE;
page = (unsigned char*)__get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
while (count > 0) {
ssize_t len;
len = dir->ops->show(dir,attr,page,count,*ppos);
if (len <= 0) {
if (len < 0)
retval = len;
break;
} else if (len > count)
len = count;
if (copy_to_user(buf,page,len)) {
retval = -EFAULT;
break;
}
*ppos += len;
count -= len;
buf += len;
retval += len;
}
free_page((unsigned long)page);
return retval;
}
/**
* driverfs_write_file - "write" to a file
* @file: file pointer
* @buf: data to write
* @count: number of bytes
* @ppos: starting offset
*
* Similarly to driverfs_read_file, we act essentially as a bit pipe.
* We check for a "write" callback in file->private_data, and pass
* @buffer, @count, @ppos, and the file entry's data to the callback.
* The number of bytes written is returned, and we handle updating
* @ppos properly.
*/
static ssize_t
driverfs_write_file(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
struct attribute * attr = file->f_dentry->d_fsdata;
struct driver_dir_entry * dir;
ssize_t retval = 0;
char * page;
dir = file->f_dentry->d_parent->d_fsdata;
page = (char *)__get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
if (count >= PAGE_SIZE)
count = PAGE_SIZE - 1;
if (copy_from_user(page,buf,count))
goto done;
*(page + count) = '\0';
while (count > 0) {
ssize_t len;
len = dir->ops->store(dir,attr,page + retval,count,*ppos);
if (len <= 0) {
if (len < 0)
retval = len;
break;
}
retval += len;
count -= len;
*ppos += len;
buf += len;
}
done:
free_page((unsigned long)page);
return retval;
}
static loff_t
driverfs_file_lseek(struct file *file, loff_t offset, int orig)
{
loff_t retval = -EINVAL;
down(&file->f_dentry->d_inode->i_sem);
switch(orig) {
case 0:
if (offset > 0) {
file->f_pos = offset;
retval = file->f_pos;
}
break;
case 1:
if ((offset + file->f_pos) > 0) {
file->f_pos += offset;
retval = file->f_pos;
}
break;
default:
break;
}
up(&file->f_dentry->d_inode->i_sem);
return retval;
}
static int driverfs_open_file(struct inode * inode, struct file * filp)
{
struct driver_dir_entry * dir;
int error = 0;
dir = (struct driver_dir_entry *)filp->f_dentry->d_parent->d_fsdata;
if (dir) {
struct attribute * attr = filp->f_dentry->d_fsdata;
if (attr && dir->ops) {
if (dir->ops->open)
error = dir->ops->open(dir);
goto Done;
}
}
error = -EINVAL;
Done:
return error;
}
static int driverfs_release(struct inode * inode, struct file * filp)
{
struct driver_dir_entry * dir;
dir = (struct driver_dir_entry *)filp->f_dentry->d_parent->d_fsdata;
if (dir->ops->close)
dir->ops->close(dir);
return 0;
}
static struct file_operations driverfs_file_operations = {
.read = driverfs_read_file,
.write = driverfs_write_file,
.llseek = driverfs_file_lseek,
.open = driverfs_open_file,
.release = driverfs_release,
};
static struct inode_operations driverfs_dir_inode_operations = {
.lookup = simple_lookup,
};
static struct address_space_operations driverfs_aops = {
.readpage = driverfs_readpage,
.writepage = fail_writepage,
.prepare_write = driverfs_prepare_write,
.commit_write = driverfs_commit_write
};
static struct super_operations driverfs_ops = {
.statfs = simple_statfs,
.drop_inode = generic_delete_inode,
};
static int driverfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = DRIVERFS_MAGIC;
sb->s_op = &driverfs_ops;
inode = driverfs_get_inode(sb, S_IFDIR | 0755, 0);
if (!inode) {
DBG("%s: could not get inode!\n",__FUNCTION__);
return -ENOMEM;
}
root = d_alloc_root(inode);
if (!root) {
DBG("%s: could not get root dentry!\n",__FUNCTION__);
iput(inode);
return -ENOMEM;
}
sb->s_root = root;
return 0;
}
static struct super_block *driverfs_get_sb(struct file_system_type *fs_type,
int flags, char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, driverfs_fill_super);
}
static struct file_system_type driverfs_fs_type = {
.owner = THIS_MODULE,
.name = "driverfs",
.get_sb = driverfs_get_sb,
.kill_sb = kill_litter_super,
};
static int get_mount(void)
{
struct vfsmount * mnt;
spin_lock(&mount_lock);
if (driverfs_mount) {
mntget(driverfs_mount);
++mount_count;
spin_unlock(&mount_lock);
goto go_ahead;
}
spin_unlock(&mount_lock);
mnt = kern_mount(&driverfs_fs_type);
if (IS_ERR(mnt)) {
printk(KERN_ERR "driverfs: could not mount!\n");
return -ENODEV;
}
spin_lock(&mount_lock);
if (!driverfs_mount) {
driverfs_mount = mnt;
++mount_count;
spin_unlock(&mount_lock);
goto go_ahead;
}
mntget(driverfs_mount);
++mount_count;
spin_unlock(&mount_lock);
go_ahead:
DBG("driverfs: mount_count = %d\n",mount_count);
return 0;
}
static void put_mount(void)
{
struct vfsmount * mnt;
spin_lock(&mount_lock);
mnt = driverfs_mount;
--mount_count;
if (!mount_count)
driverfs_mount = NULL;
spin_unlock(&mount_lock);
mntput(mnt);
DBG("driverfs: mount_count = %d\n",mount_count);
}
static int __init driverfs_init(void)
{
return register_filesystem(&driverfs_fs_type);
}
core_initcall(driverfs_init);
static struct dentry * get_dentry(struct dentry * parent, const char * name)
{
struct qstr qstr;
qstr.name = name;
qstr.len = strlen(name);
qstr.hash = full_name_hash(name,qstr.len);
return lookup_hash(&qstr,parent);
}
/**
* driverfs_create_dir - create a directory in the filesystem
* @entry: directory entry
* @parent: parent directory entry
*/
int
driverfs_create_dir(struct driver_dir_entry * entry,
struct driver_dir_entry * parent)
{
struct dentry * dentry = NULL;
struct dentry * parent_dentry;
int error = 0;
if (!entry)
return -EINVAL;
get_mount();
parent_dentry = parent ? parent->dentry : NULL;
if (!parent_dentry)
if (driverfs_mount && driverfs_mount->mnt_sb)
parent_dentry = driverfs_mount->mnt_sb->s_root;
if (!parent_dentry) {
put_mount();
return -EFAULT;
}
down(&parent_dentry->d_inode->i_sem);
dentry = get_dentry(parent_dentry,entry->name);
if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *) entry;
entry->dentry = dentry;
error = driverfs_mkdir(parent_dentry->d_inode,dentry,entry->mode);
} else
error = PTR_ERR(dentry);
up(&parent_dentry->d_inode->i_sem);
if (error)
put_mount();
return error;
}
/**
* driverfs_create_file - create a file
* @entry: structure describing the file
* @parent: directory to create it in
*/
int
driverfs_create_file(struct attribute * entry,
struct driver_dir_entry * parent)
{
struct dentry * dentry;
int error = 0;
if (!entry || !parent)
return -EINVAL;
if (!parent->dentry) {
put_mount();
return -EINVAL;
}
down(&parent->dentry->d_inode->i_sem);
dentry = get_dentry(parent->dentry,entry->name);
if (!IS_ERR(dentry)) {
dentry->d_fsdata = (void *)entry;
error = driverfs_create(parent->dentry->d_inode,dentry,entry->mode);
} else
error = PTR_ERR(dentry);
up(&parent->dentry->d_inode->i_sem);
return error;
}
/**
* driverfs_create_symlink - make a symlink
* @parent: directory we're creating in
* @entry: entry describing link
* @target: place we're symlinking to
*
*/
int driverfs_create_symlink(struct driver_dir_entry * parent,
char * name, char * target)
{
struct dentry * dentry;
int error = 0;
if (!parent)
return -EINVAL;
if (!parent->dentry) {
put_mount();
return -EINVAL;
}
down(&parent->dentry->d_inode->i_sem);
dentry = get_dentry(parent->dentry,name);
if (!IS_ERR(dentry))
error = driverfs_symlink(parent->dentry->d_inode,dentry,target);
else
error = PTR_ERR(dentry);
up(&parent->dentry->d_inode->i_sem);
return error;
}
/**
* driverfs_remove_file - exported file removal
* @dir: directory the file supposedly resides in
* @name: name of the file
*
* Try and find the file in the dir's list.
* If it's there, call __remove_file() (above) for the dentry.
*/
void driverfs_remove_file(struct driver_dir_entry * dir, const char * name)
{
struct dentry * dentry;
if (!dir->dentry)
return;
down(&dir->dentry->d_inode->i_sem);
dentry = get_dentry(dir->dentry,name);
if (!IS_ERR(dentry)) {
/* make sure dentry is really there */
if (dentry->d_inode &&
(dentry->d_parent->d_inode == dir->dentry->d_inode)) {
driverfs_unlink(dir->dentry->d_inode,dentry);
}
}
up(&dir->dentry->d_inode->i_sem);
}
/**
* driverfs_remove_dir - exportable directory removal
* @dir: directory to remove
*
* To make sure we don't orphan anyone, first remove
* all the children in the list, then do clean up the directory.
*/
void driverfs_remove_dir(struct driver_dir_entry * dir)
{
struct list_head * node, * next;
struct dentry * dentry = dir->dentry;
struct dentry * parent;
if (!dentry)
goto done;
parent = dget(dentry->d_parent);
down(&parent->d_inode->i_sem);
down(&dentry->d_inode->i_sem);
list_for_each_safe(node,next,&dentry->d_subdirs) {
struct dentry * d = list_entry(node,struct dentry,d_child);
/* make sure dentry is still there */
if (d->d_inode)
driverfs_unlink(dentry->d_inode,d);
}
d_invalidate(dentry);
if (driverfs_empty(dentry)) {
dentry->d_inode->i_nlink -= 2;
dentry->d_inode->i_flags |= S_DEAD;
parent->d_inode->i_nlink--;
}
up(&dentry->d_inode->i_sem);
dput(dentry);
up(&parent->d_inode->i_sem);
dput(parent);
done:
put_mount();
}
EXPORT_SYMBOL(driverfs_create_file);
EXPORT_SYMBOL(driverfs_create_symlink);
EXPORT_SYMBOL(driverfs_create_dir);
EXPORT_SYMBOL(driverfs_remove_file);
EXPORT_SYMBOL(driverfs_remove_dir);
MODULE_LICENSE("GPL");
......@@ -277,231 +277,147 @@ static void devfs_remove_partitions(struct gendisk *dev)
#endif
}
static ssize_t part_dev_read(struct device *dev,
char *page, size_t count, loff_t off)
/*
* sysfs bindings for partitions
*/
struct part_attribute {
struct attribute attr;
ssize_t (*show)(struct hd_struct *,char *,size_t,loff_t);
};
static ssize_t part_attr_show(struct kobject * kobj, struct attribute * attr,
char * page, size_t count, loff_t off)
{
struct hd_struct * p = container_of(kobj,struct hd_struct,kobj);
struct part_attribute * part_attr = container_of(attr,struct part_attribute,attr);
ssize_t ret = 0;
if (part_attr->show)
part_attr->show(p,page,count,off);
return ret;
}
static struct sysfs_ops part_sysfs_ops = {
.show part_attr_show,
};
static ssize_t part_dev_read(struct hd_struct * p,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->parent->driver_data;
struct hd_struct *p = dev->driver_data;
struct gendisk *disk = container_of(p->kobj.parent,struct gendisk,kobj);
int part = p - disk->part + 1;
dev_t base = MKDEV(disk->major, disk->first_minor);
return off ? 0 : sprintf(page, "%04x\n",base + part);
}
static ssize_t part_start_read(struct device *dev,
char *page, size_t count, loff_t off)
static ssize_t part_start_read(struct hd_struct * p,
char *page, size_t count, loff_t off)
{
struct hd_struct *p = dev->driver_data;
return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)p->start_sect);
}
static ssize_t part_size_read(struct device *dev,
char *page, size_t count, loff_t off)
static ssize_t part_size_read(struct hd_struct * p,
char *page, size_t count, loff_t off)
{
struct hd_struct *p = dev->driver_data;
return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)p->nr_sects);
}
static ssize_t part_stat_read(struct device *dev,
char *page, size_t count, loff_t off)
static ssize_t part_stat_read(struct hd_struct * p,
char *page, size_t count, loff_t off)
{
struct hd_struct *p = dev->driver_data;
return off ? 0 : sprintf(page, "%8u %8llu %8u %8llu\n",
p->reads, (u64)p->read_sectors,
p->writes, (u64)p->write_sectors);
}
static struct device_attribute part_attr_dev = {
static struct part_attribute part_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = part_dev_read
};
static struct device_attribute part_attr_start = {
static struct part_attribute part_attr_start = {
.attr = {.name = "start", .mode = S_IRUGO },
.show = part_start_read
};
static struct device_attribute part_attr_size = {
static struct part_attribute part_attr_size = {
.attr = {.name = "size", .mode = S_IRUGO },
.show = part_size_read
};
static struct device_attribute part_attr_stat = {
static struct part_attribute part_attr_stat = {
.attr = {.name = "stat", .mode = S_IRUGO },
.show = part_stat_read
};
static struct attribute * default_attrs[] = {
&part_attr_dev.attr,
&part_attr_start.attr,
&part_attr_size.attr,
&part_attr_stat.attr,
NULL,
};
extern struct subsystem block_subsys;
static struct subsystem part_subsys = {
.kobj = { .name = "part" },
.parent = &block_subsys,
.default_attrs = default_attrs,
.sysfs_ops = &part_sysfs_ops,
};
static int __init part_subsys_init(void)
{
return subsystem_register(&part_subsys);
}
__initcall(part_subsys_init);
void delete_partition(struct gendisk *disk, int part)
{
struct hd_struct *p = disk->part + part - 1;
struct device *dev;
if (!p->nr_sects)
return;
p->start_sect = 0;
p->nr_sects = 0;
p->reads = p->writes = p->read_sectors = p->write_sectors = 0;
devfs_unregister(p->de);
dev = p->hd_driverfs_dev;
p->hd_driverfs_dev = NULL;
if (dev) {
device_remove_file(dev, &part_attr_stat);
device_remove_file(dev, &part_attr_size);
device_remove_file(dev, &part_attr_start);
device_remove_file(dev, &part_attr_dev);
device_unregister(dev);
}
}
static void part_release(struct device *dev)
{
kfree(dev);
kobject_unregister(&p->kobj);
}
void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len)
{
struct hd_struct *p = disk->part + part - 1;
struct device *parent = &disk->disk_dev;
struct device *dev;
p->start_sect = start;
p->nr_sects = len;
devfs_register_partition(disk, part);
dev = kmalloc(sizeof(struct device), GFP_KERNEL);
if (!dev)
return;
memset(dev, 0, sizeof(struct device));
dev->parent = parent;
sprintf(dev->bus_id, "p%d", part);
dev->release = part_release;
dev->driver_data = p;
device_register(dev);
device_create_file(dev, &part_attr_dev);
device_create_file(dev, &part_attr_start);
device_create_file(dev, &part_attr_size);
device_create_file(dev, &part_attr_stat);
p->hd_driverfs_dev = dev;
kobject_init(&p->kobj);
snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->disk_name,part);
p->kobj.parent = &disk->kobj;
p->kobj.subsys = &part_subsys;
kobject_register(&p->kobj);
}
static ssize_t disk_dev_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->driver_data;
dev_t base = MKDEV(disk->major, disk->first_minor);
return off ? 0 : sprintf(page, "%04x\n",base);
}
static ssize_t disk_range_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->driver_data;
return off ? 0 : sprintf(page, "%d\n",disk->minors);
}
static ssize_t disk_size_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->driver_data;
return off ? 0 : sprintf(page, "%llu\n",(unsigned long long)get_capacity(disk));
}
static inline unsigned MSEC(unsigned x)
static void disk_sysfs_symlinks(struct gendisk *disk)
{
return x * 1000 / HZ;
}
static ssize_t disk_stat_read(struct device *dev,
char *page, size_t count, loff_t off)
{
struct gendisk *disk = dev->driver_data;
disk_round_stats(disk);
return off ? 0 : sprintf(page,
"%8u %8u %8llu %8u "
"%8u %8u %8llu %8u "
"%8u %8u %8u"
"\n",
disk->reads, disk->read_merges, (u64)disk->read_sectors,
MSEC(disk->read_ticks),
disk->writes, disk->write_merges, (u64)disk->write_sectors,
MSEC(disk->write_ticks),
disk->in_flight, MSEC(disk->io_ticks),
MSEC(disk->time_in_queue));
}
static struct device_attribute disk_attr_dev = {
.attr = {.name = "dev", .mode = S_IRUGO },
.show = disk_dev_read
};
static struct device_attribute disk_attr_range = {
.attr = {.name = "range", .mode = S_IRUGO },
.show = disk_range_read
};
static struct device_attribute disk_attr_size = {
.attr = {.name = "size", .mode = S_IRUGO },
.show = disk_size_read
};
static struct device_attribute disk_attr_stat = {
.attr = {.name = "stat", .mode = S_IRUGO },
.show = disk_stat_read
};
static void disk_driverfs_symlinks(struct gendisk *disk)
{
struct device *target = disk->driverfs_dev;
struct device *dev = &disk->disk_dev;
struct device *p;
char *path;
char *s;
int length;
int depth;
if (!target)
return;
get_device(target);
length = get_devpath_length(target);
length += strlen("..");
if (length > PATH_MAX)
return;
if (!(path = kmalloc(length,GFP_KERNEL)))
return;
memset(path,0,length);
/* our relative position */
strcpy(path,"..");
fill_devpath(target, path, length);
driverfs_create_symlink(&dev->dir, "device", path);
kfree(path);
for (p = target, depth = 0; p; p = p->parent, depth++)
;
length = get_devpath_length(dev);
length += 3 * depth - 1;
if (length > PATH_MAX)
return;
if (!(path = kmalloc(length,GFP_KERNEL)))
return;
memset(path,0,length);
for (s = path; depth--; s += 3)
strcpy(s, "../");
fill_devpath(dev, path, length);
driverfs_create_symlink(&target->dir, "block", path);
kfree(path);
struct device *target = get_device(disk->driverfs_dev);
if (target) {
sysfs_create_link(&disk->kobj,&target->kobj,"device");
sysfs_create_link(&target->kobj,&disk->kobj,"block");
}
}
/* Not exported, helper to add_disk(). */
void register_disk(struct gendisk *disk)
{
struct device *dev = &disk->disk_dev;
struct parsed_partitions *state;
struct block_device *bdev;
char *s;
int j;
strcpy(dev->bus_id, disk->disk_name);
strncpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN);
/* ewww... some of these buggers have / in name... */
s = strchr(dev->bus_id, '/');
s = strchr(disk->kobj.name, '/');
if (s)
*s = '!';
device_add(dev);
device_create_file(dev, &disk_attr_dev);
device_create_file(dev, &disk_attr_range);
device_create_file(dev, &disk_attr_size);
device_create_file(dev, &disk_attr_stat);
disk_driverfs_symlinks(disk);
kobject_register(&disk->kobj);
disk_sysfs_symlinks(disk);
if (disk->flags & GENHD_FL_CD)
devfs_create_cdrom(disk);
......@@ -620,16 +536,12 @@ void del_gendisk(struct gendisk *disk)
disk->time_in_queue = 0;
disk->stamp = disk->stamp_idle = 0;
devfs_remove_partitions(disk);
device_remove_file(&disk->disk_dev, &disk_attr_dev);
device_remove_file(&disk->disk_dev, &disk_attr_range);
device_remove_file(&disk->disk_dev, &disk_attr_size);
device_remove_file(&disk->disk_dev, &disk_attr_stat);
driverfs_remove_file(&disk->disk_dev.dir, "device");
kobject_unregister(&disk->kobj);
sysfs_remove_link(&disk->kobj, "device");
if (disk->driverfs_dev) {
driverfs_remove_file(&disk->driverfs_dev->dir, "block");
sysfs_remove_link(&disk->driverfs_dev->kobj, "block");
put_device(disk->driverfs_dev);
}
device_del(&disk->disk_dev);
}
struct dev_name {
......@@ -680,3 +592,4 @@ char *partition_name(dev_t dev)
return dname->name;
}
......@@ -28,7 +28,7 @@
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/driverfs_fs.h>
#include <linux/kobject.h>
#define DEVICE_NAME_SIZE 80
#define DEVICE_ID_SIZE 32
......@@ -65,14 +65,13 @@ struct bus_type {
atomic_t refcount;
u32 present;
struct subsystem subsys;
struct subsystem drvsubsys;
struct subsystem devsubsys;
struct list_head node;
struct list_head devices;
struct list_head drivers;
struct driver_dir_entry dir;
struct driver_dir_entry device_dir;
struct driver_dir_entry driver_dir;
int (*match)(struct device * dev, struct device_driver * drv);
struct device * (*add) (struct device * parent, char * bus_id);
int (*hotplug) (struct device *dev, char **envp,
......@@ -119,12 +118,11 @@ struct device_driver {
atomic_t refcount;
u32 present;
struct kobject kobj;
struct list_head bus_list;
struct list_head class_list;
struct list_head devices;
struct driver_dir_entry dir;
int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
......@@ -177,14 +175,13 @@ struct device_class {
u32 devnum;
struct subsystem subsys;
struct subsystem devsubsys;
struct subsystem drvsubsys;
struct list_head node;
struct list_head drivers;
struct list_head intf_list;
struct driver_dir_entry dir;
struct driver_dir_entry driver_dir;
struct driver_dir_entry device_dir;
int (*add_device)(struct device *);
void (*remove_device)(struct device *);
int (*hotplug)(struct device *dev, char **envp,
......@@ -232,9 +229,9 @@ struct device_interface {
char * name;
struct device_class * devclass;
struct kobject kobj;
struct list_head node;
struct list_head devices;
struct driver_dir_entry dir;
u32 devnum;
......@@ -275,6 +272,7 @@ struct device {
struct list_head intf_list;
struct device * parent;
struct kobject kobj;
char name[DEVICE_NAME_SIZE]; /* descriptive ascii string */
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
......@@ -285,8 +283,6 @@ struct device {
* persists for the right amount of time */
struct bus_type * bus; /* type of bus device is on */
struct driver_dir_entry dir;
struct device_driver *driver; /* which driver has allocated this
device */
void *driver_data; /* data private to the driver */
......@@ -438,6 +434,11 @@ extern int device_suspend(u32 state, u32 level);
extern void device_resume(u32 level);
extern void device_shutdown(void);
/* drivrs/base/firmware.c */
extern int firmware_register(struct subsystem *);
extern void firmware_uregister(struct subsystem *);
/* debugging and troubleshooting/diagnostic helpers. */
#ifdef DEBUG
#define dev_dbg(dev, format, arg...) \
......
/*
* driverfs_fs.h - definitions for the device driver filesystem
*
* Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* This is a simple, ram-based filesystem, which allows kernel
* callbacks for read/write of files.
*
* Please see Documentation/filesystems/driverfs.txt for more information.
*/
#ifndef _DRIVER_FS_H_
#define _DRIVER_FS_H_
struct driver_dir_entry;
struct attribute;
struct driverfs_ops {
int (*open)(struct driver_dir_entry *);
int (*close)(struct driver_dir_entry *);
ssize_t (*show)(struct driver_dir_entry *, struct attribute *,char *, size_t, loff_t);
ssize_t (*store)(struct driver_dir_entry *,struct attribute *,const char *, size_t, loff_t);
};
struct driver_dir_entry {
char * name;
struct dentry * dentry;
mode_t mode;
struct driverfs_ops * ops;
};
struct attribute {
char * name;
mode_t mode;
};
extern int
driverfs_create_dir(struct driver_dir_entry *, struct driver_dir_entry *);
extern void
driverfs_remove_dir(struct driver_dir_entry * entry);
extern int
driverfs_create_file(struct attribute * attr,
struct driver_dir_entry * parent);
extern int
driverfs_create_symlink(struct driver_dir_entry * parent,
char * name, char * target);
extern void
driverfs_remove_file(struct driver_dir_entry *, const char * name);
#endif /* _DDFS_H_ */
......@@ -62,7 +62,7 @@ struct hd_struct {
sector_t start_sect;
sector_t nr_sects;
devfs_handle_t de; /* primary (master) devfs entry */
struct device *hd_driverfs_dev; /* support driverfs hiearchy */
struct kobject kobj;
unsigned reads, read_sectors, writes, write_sectors;
int policy;
};
......@@ -93,7 +93,7 @@ struct gendisk {
devfs_handle_t de; /* more of the same */
devfs_handle_t disk_de; /* piled higher and deeper */
struct device *driverfs_dev;
struct device disk_dev;
struct kobject kobj;
struct timer_rand_state *random;
int policy;
......@@ -130,8 +130,6 @@ static inline void set_capacity(struct gendisk *disk, sector_t size)
disk->capacity = size;
}
extern struct device_class disk_devclass;
#endif /* __KERNEL__ */
#ifdef CONFIG_SOLARIS_X86_PARTITION
......
......@@ -12,8 +12,10 @@
#include <linux/rwsem.h>
#include <asm/atomic.h>
#define KOBJ_NAME_LEN 16
struct kobject {
char name[16];
char name[KOBJ_NAME_LEN];
atomic_t refcount;
struct list_head entry;
struct kobject * parent;
......
......@@ -9,20 +9,18 @@
#ifndef _SYSFS_H_
#define _SYSFS_H_
struct driver_dir_entry;
struct attribute;
struct kobject;
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *, size_t, loff_t);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t, loff_t);
};
struct attribute {
char * name;
mode_t mode;
};
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *, size_t, loff_t);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t, loff_t);
};
extern int
sysfs_create_dir(struct kobject *);
......
......@@ -101,7 +101,7 @@ static __init dev_t try_name(char *name, int part)
/* read device number from .../dev */
sprintf(path, "/sys/bus/block/devices/%s/dev", name);
sprintf(path, "/sys/block/%s/dev", name);
fd = open(path, 0, 0);
if (fd < 0)
goto fail;
......@@ -119,7 +119,7 @@ static __init dev_t try_name(char *name, int part)
return res;
/* otherwise read range from .../range */
sprintf(path, "/sys/bus/block/devices/%s/range", name);
sprintf(path, "/sys/block/%s/range", name);
fd = open(path, 0, 0);
if (fd < 0)
goto fail;
......@@ -166,7 +166,7 @@ __init dev_t name_to_dev_t(char *name)
int part;
sys_mkdir("/sys", 0700);
if (sys_mount("driverfs", "/sys", "driverfs", 0, NULL) < 0)
if (sys_mount("sysfs", "/sys", "sysfs", 0, NULL) < 0)
goto out;
if (strncmp(name, "/dev/", 5) != 0) {
......
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