| /* | |
| * procfs_example.c: an example proc interface | |
| * | |
| * Copyright (C) 2001, Erik Mouw (J.A.K.Mouw@its.tudelft.nl) | |
| * | |
| * This file accompanies the procfs-guide in the Linux kernel | |
| * source. Its main use is to demonstrate the concepts and | |
| * functions described in the guide. | |
| * | |
| * This software has been developed while working on the LART | |
| * computing board (http://www.lart.tudelft.nl/), which is | |
| * sponsored by the Mobile Multi-media Communications | |
| * (http://www.mmc.tudelft.nl/) and Ubiquitous Communications | |
| * (http://www.ubicom.tudelft.nl/) projects. | |
| * | |
| * The author can be reached at: | |
| * | |
| * Erik Mouw | |
| * Information and Communication Theory Group | |
| * Faculty of Information Technology and Systems | |
| * Delft University of Technology | |
| * P.O. Box 5031 | |
| * 2600 GA Delft | |
| * The Netherlands | |
| * | |
| * | |
| * 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 | |
| * | |
| */ | |
| #include <linux/module.h> | |
| #include <linux/kernel.h> | |
| #include <linux/init.h> | |
| #include <linux/proc_fs.h> | |
| #include <linux/jiffies.h> | |
| #include <asm/uaccess.h> | |
| #define MODULE_VERS "1.0" | |
| #define MODULE_NAME "procfs_example" | |
| #define FOOBAR_LEN 8 | |
| struct fb_data_t { | |
| char name[FOOBAR_LEN + 1]; | |
| char value[FOOBAR_LEN + 1]; | |
| }; | |
| static struct proc_dir_entry *example_dir, *foo_file, | |
| *bar_file, *jiffies_file, *symlink; | |
| struct fb_data_t foo_data, bar_data; | |
| static int proc_read_jiffies(char *page, char **start, | |
| off_t off, int count, | |
| int *eof, void *data) | |
| { | |
| int len; | |
| len = sprintf(page, "jiffies = %ld\n", | |
| jiffies); | |
| return len; | |
| } | |
| static int proc_read_foobar(char *page, char **start, | |
| off_t off, int count, | |
| int *eof, void *data) | |
| { | |
| int len; | |
| struct fb_data_t *fb_data = (struct fb_data_t *)data; | |
| /* DON'T DO THAT - buffer overruns are bad */ | |
| len = sprintf(page, "%s = '%s'\n", | |
| fb_data->name, fb_data->value); | |
| return len; | |
| } | |
| static int proc_write_foobar(struct file *file, | |
| const char *buffer, | |
| unsigned long count, | |
| void *data) | |
| { | |
| int len; | |
| struct fb_data_t *fb_data = (struct fb_data_t *)data; | |
| if(count > FOOBAR_LEN) | |
| len = FOOBAR_LEN; | |
| else | |
| len = count; | |
| if(copy_from_user(fb_data->value, buffer, len)) | |
| return -EFAULT; | |
| fb_data->value[len] = '\0'; | |
| return len; | |
| } | |
| static int __init init_procfs_example(void) | |
| { | |
| int rv = 0; | |
| /* create directory */ | |
| example_dir = proc_mkdir(MODULE_NAME, NULL); | |
| if(example_dir == NULL) { | |
| rv = -ENOMEM; | |
| goto out; | |
| } | |
| example_dir->owner = THIS_MODULE; | |
| /* create jiffies using convenience function */ | |
| jiffies_file = create_proc_read_entry("jiffies", | |
| 0444, example_dir, | |
| proc_read_jiffies, | |
| NULL); | |
| if(jiffies_file == NULL) { | |
| rv = -ENOMEM; | |
| goto no_jiffies; | |
| } | |
| jiffies_file->owner = THIS_MODULE; | |
| /* create foo and bar files using same callback | |
| * functions | |
| */ | |
| foo_file = create_proc_entry("foo", 0644, example_dir); | |
| if(foo_file == NULL) { | |
| rv = -ENOMEM; | |
| goto no_foo; | |
| } | |
| strcpy(foo_data.name, "foo"); | |
| strcpy(foo_data.value, "foo"); | |
| foo_file->data = &foo_data; | |
| foo_file->read_proc = proc_read_foobar; | |
| foo_file->write_proc = proc_write_foobar; | |
| foo_file->owner = THIS_MODULE; | |
| bar_file = create_proc_entry("bar", 0644, example_dir); | |
| if(bar_file == NULL) { | |
| rv = -ENOMEM; | |
| goto no_bar; | |
| } | |
| strcpy(bar_data.name, "bar"); | |
| strcpy(bar_data.value, "bar"); | |
| bar_file->data = &bar_data; | |
| bar_file->read_proc = proc_read_foobar; | |
| bar_file->write_proc = proc_write_foobar; | |
| bar_file->owner = THIS_MODULE; | |
| /* create symlink */ | |
| symlink = proc_symlink("jiffies_too", example_dir, | |
| "jiffies"); | |
| if(symlink == NULL) { | |
| rv = -ENOMEM; | |
| goto no_symlink; | |
| } | |
| symlink->owner = THIS_MODULE; | |
| /* everything OK */ | |
| printk(KERN_INFO "%s %s initialised\n", | |
| MODULE_NAME, MODULE_VERS); | |
| return 0; | |
| no_symlink: | |
| remove_proc_entry("tty", example_dir); | |
| no_tty: | |
| remove_proc_entry("bar", example_dir); | |
| no_bar: | |
| remove_proc_entry("foo", example_dir); | |
| no_foo: | |
| remove_proc_entry("jiffies", example_dir); | |
| no_jiffies: | |
| remove_proc_entry(MODULE_NAME, NULL); | |
| out: | |
| return rv; | |
| } | |
| static void __exit cleanup_procfs_example(void) | |
| { | |
| remove_proc_entry("jiffies_too", example_dir); | |
| remove_proc_entry("tty", example_dir); | |
| remove_proc_entry("bar", example_dir); | |
| remove_proc_entry("foo", example_dir); | |
| remove_proc_entry("jiffies", example_dir); | |
| remove_proc_entry(MODULE_NAME, NULL); | |
| printk(KERN_INFO "%s %s removed\n", | |
| MODULE_NAME, MODULE_VERS); | |
| } | |
| module_init(init_procfs_example); | |
| module_exit(cleanup_procfs_example); | |
| MODULE_AUTHOR("Erik Mouw"); | |
| MODULE_DESCRIPTION("procfs examples"); |