Messages in this thread | | | Date | Fri, 26 Mar 1999 14:28:01 -0600 | From | root of all evil <> |
| |
I submit the following for your approval:
(I would like to apologize ahead of time if this has already been done or rejected)
Purpose: To dynamically create device inodes when the modules load without needing a special script.
Background: I have been writing a device driver for a specialty board we use here at my workplace. I am writing it as a module. Because of the possibility of a conflict with a MAJOR number, I want have the module ask for the next available MAJOR number. This causes problems because I have to provide a script which creates the device inode files in /dev.
Solution: I have modified the /proc file system to support block and character device inode entries. The module now has two routines to instantiate a driver inode into the /proc/dev/ directory structure:
int create_proc_dev_entry(const char *name,mode_t mode,kdev_t dev);
given the device name, ie "myboard0X", the mode of operation, S_IFCHR|0666, and the device info, MKDEV(my_major_number,my_minor_number), the routine will add the device to /proc/dev and return 1.
int remove_proc_dev_entry(const char *name);
this routine will remove the device inode from the /proc/dev directory.
Comments: The owner of the device file is root. Once installed, there is no way to change the permissions on the file.
There is a current limitition that the file must not belong to a subdirectory of the /proc/dev/ directory. The routine does not allow you to add a device "device/part".
How It Works:
The "/proc/dev" directory is created when the proc filesystem is initialized. The proc_dev variable holds the entry for the subdirectory "dev".
The user calls the create_proc_dev_entry from the init_module routine. This routine adds the device name to the directory and stores the major and minor numbers in the size element of the proc_dir_entry (as device inodes don't need the size). The inode operation is set to blkdev_inode_operations, or chrdev_inode_operations, depending on the device type.
When the inode entry is looked up, a check is done on the mode to see if it is a block or char device, if so, sets the inode dev and rdev entry to the size, and sets the size to 0.
My Questions: I tried compiling the kernel with version information included, but it would not calculate the checksum on the two new routines I wrote. A cat of /proc/ksyms gave a create_proc_dev_entry_R__ver_create_proc_dev_entry, and the module would not load. What am I missing?
The Modifications (7 minor changes total):
The following modifications were done to the Linux 2.2.3 kernel tree, specifically in the linux/fs/proc subdirectory:
Change 1: (to root.c) changed line:
struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus;
to read:
struct proc_dir_entry *proc_dev, *proc_net, *proc_scsi, *proc_bus;
Change 2: (to root.c) added line:
proc_dev = create_proc_entry("dev",S_IFDIR,0);
Change 3: (to procfs_syms.c) added lines:
EXPORT_SYMBOL(create_proc_dev_entry); EXPORT_SYMBOL(remove_proc_dev_entry);
Change 4: (to /usr/include/linux/proc_fs.h) added lines: int create_proc_dev_entry(const char *name, mode_t mode, kdev_t dev); void remove_proc_dev_entry(const char *name);
( and their corresponding NULL functions if !CONFIG_PROC_FS )
Change 5: (to inode.c) replaced, in function proc_get_inode, the line:
inode->i_size = de->size;
with:
if(S_ISCHR(de->mode) || S_ISBLK(de->mode)) { inode->i_rdev=de->size; inode->i_dev=de->size; inode->i_size=0; } else if(de->size) inode->i_size=de->size
Change 6: (to Makefile) added proc_dev.o to list of object files
Change 7: (added a file proc_dev.c)
==============START of FILE========================================= /* * proc/fs/proc_dev.c --- special routines for the proc/dev * * This file contains proc/dev routines for handling * insertion and removal of device inodes in /proc/dev/ * * This file is modified from generic.c * * changes Copyright (C) 1999 Kevin Johnson */
#include <asm/uaccess.h>
#include <linux/errno.h> #include <linux/sched.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <asm/bitops.h>
extern struct proc_dir_entry *proc_dev; extern struct inode_operations chrdev_inode_operations; extern struct inode_operations blkdev_inode_operations;
int create_proc_dev_entry(const char *name, mode_t mode,dev_t dev) { struct proc_dir_entry *ent = NULL; const char *fn = name; int len;
/* * No subdirectories allowed (yet) */
if(strchr(fn,'/')) return 0;
/* * make sure its a block or character device */
if (!S_ISCHR(mode) && !S_ISBLK(mode)) return;
len = strlen(fn);
ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL); if (!ent) return 0;
memset(ent, 0, sizeof(struct proc_dir_entry)); memcpy(((char *) ent) + sizeof(*ent), fn, len + 1);
ent->name = ((char *) ent) + sizeof(*ent); ent->namelen = len; ent->size=dev; ent->mode=mode; ent->nlink = 1; if(S_ISCHR(mode)) { ent->ops=&chrdev_inode_operations; } else { ent->ops=&blkdev_inode_operations; } proc_register(proc_dev, ent); return 1; }
extern void free_proc_entry(struct proc_dir_entry *);
/* * Remove a /proc/dev entry and free it if it's not currently in use. * If it is in use, we set the 'deleted' flag. */
void remove_proc_dev_entry(const char *name) { struct proc_dir_entry *de; const char *fn = name; int len;
/* * No subdirectories allowed (yet) */
if(strchr(fn,'/')) goto out;
len = strlen(fn);
for (de = proc_dev->subdir; de ; de = de->next) { if (proc_match(len, fn, de)) break; }
if (de) { proc_unregister(proc_dev, de->low_ino); de->nlink = 0; de->deleted = 1; if (!de->count) free_proc_entry(de); else { printk("remove_proc_dev_entry: %s/%s busy, count=%d\n", proc_dev->name, de->name, de->count); } } out: return; }
==================================END of FILE================================
Kevin Johnson, kevin@www.dsabl.sill.army.mil - or - kevin@rli.net
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/
| |