Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
808897cf
Commit
808897cf
authored
Oct 30, 2002
by
Patrick Mochel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
driverfs: die die die
parent
60211581
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1 addition
and
1119 deletions
+1
-1119
Documentation/filesystems/driverfs.txt
Documentation/filesystems/driverfs.txt
+0
-336
fs/Makefile
fs/Makefile
+1
-1
fs/driverfs/Makefile
fs/driverfs/Makefile
+0
-9
fs/driverfs/inode.c
fs/driverfs/inode.c
+0
-708
include/linux/driverfs_fs.h
include/linux/driverfs_fs.h
+0
-65
No files found.
Documentation/filesystems/driverfs.txt
deleted
100644 → 0
View file @
60211581
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.
fs/Makefile
View file @
808897cf
...
@@ -37,7 +37,7 @@ obj-$(CONFIG_QUOTACTL) += quota.o
...
@@ -37,7 +37,7 @@ obj-$(CONFIG_QUOTACTL) += quota.o
obj-$(CONFIG_PROC_FS)
+=
proc/
obj-$(CONFIG_PROC_FS)
+=
proc/
obj-y
+=
partitions/
obj-y
+=
partitions/
obj-y
+=
driverfs/
sysfs/
obj-y
+=
sysfs/
obj-y
+=
devpts/
obj-y
+=
devpts/
obj-$(CONFIG_PROFILING)
+=
dcookies.o
obj-$(CONFIG_PROFILING)
+=
dcookies.o
...
...
fs/driverfs/Makefile
deleted
100644 → 0
View file @
60211581
#
# Makefile for the driverfs virtual filesystem.
#
export-objs
:=
inode.o
obj-y
:=
inode.o
include
$(TOPDIR)/Rules.make
fs/driverfs/inode.c
deleted
100644 → 0
View file @
60211581
/*
* 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"
);
include/linux/driverfs_fs.h
deleted
100644 → 0
View file @
60211581
/*
* 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_
#include <linux/sysfs.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
;
};
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_ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment