/* * procmod.c: example of using /proc for debug output, using proc_read() * Copyright (C) 2002-2003 Randy Dunlap */ /****************************************************************** # 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., 675 Mass Ave, Cambridge, MA 02139, USA. ******************************************************************/ #define MODULE #include #include #include #include #include /* * This module can be used for printing debug info in /proc/numbers . * /proc/numbers is currently read-only. * It could be made to accept written data also. * * This procfs module exhibits a well-known syndrome: * For each instance of 'cat /proc/numbers', numbers_read() is * called 2 times, and is incremented 2 times. * This is a side-effect of how apps and/or libc read a file * until read() returns 0, meaning EOF. * * version 0.2: add "dump_resource_trees()"; */ #define VERSION "0.2" #define MODNAME "numbers-debug" extern struct proc_dir_entry proc_root; static struct proc_dir_entry *numdir; static int number_current; #define RESOURCES 1 #ifdef RESOURCES #include extern struct resource ioport_resource, iomem_resource; struct resource * do_next_resource (struct resource *res, int *level) { struct resource *par; if (res->child) { ++*level; return res->child; } if (res->sibling) return res->sibling; par = res->parent; --*level; while (par) { if (par->sibling) return par->sibling; par = par->parent; --*level; } return NULL; } #define MAX_LEVEL 5 void dump_resource_trees (void) { struct resource *res; int level; char *fmt; level = 0; fmt = " IO: %08lx-%08lx : %s (PSC: %p, %p, %p)\n"; res = ioport_resource.child; if (res->end < 0x10000) fmt = " IO: %04lx-%04lx : %s (PSC: %p, %p, %p)\n"; while (res) { if (level > MAX_LEVEL) level = MAX_LEVEL; printk (fmt + 2 * MAX_LEVEL - 2 * level, res->start, res->end, res->name ?: "", res->parent, res->sibling, res->child); res = do_next_resource(res, &level); } level = 0; fmt = " IO: %08lx-%08lx : %s (PSC: %p, %p, %p)\n"; res = iomem_resource.child; if (res->end < 0x10000) fmt = " IO: %04lx-%04lx : %s (PSC: %p, %p, %p)\n"; while (res) { if (level > MAX_LEVEL) level = MAX_LEVEL; printk (fmt + 2 * MAX_LEVEL - 2 * level, res->start, res->end, res->name ?: "(nil)", res->parent, res->sibling, res->child); res = do_next_resource(res, &level); } } #else static inline void dump_resource_trees (void) {} #endif static int numbers_read (char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len = 0; len = sprintf(buf, "module_name: %s\n", MODNAME); len += sprintf(buf + len, "version: %s\n", VERSION); len += sprintf(buf + len, "number_current: %d\n", ++number_current); ///printk (KERN_INFO "number_current now = %d\n", number_current); *eof = 1; ///// NOTE: next line is for temporary testing: dump_resource_trees(); return len; } /* * In this example, the function doesn't write the * passed data. It is only used to reset the counter. */ static int numbers_write (struct file *file, const char *buf, unsigned long count, void *data) { number_current = 0; return count < sizeof (number_current) ? count : sizeof (number_current); } static int __init numbers_init (void) { numdir = create_proc_entry ("numbers", 0644, &proc_root); if (numdir) { numdir->owner = THIS_MODULE; numdir->uid = 0; numdir->read_proc = numbers_read; numdir->write_proc = numbers_write; } else printk (KERN_INFO MODNAME ": numdir create error\n"); return 0; } static void __exit numbers_exit (void) { if (numdir) remove_proc_entry ("numbers", NULL); } module_init (numbers_init); module_exit (numbers_exit); MODULE_LICENSE ("GPL"); MODULE_DESCRIPTION ("procfs-debug-example");