/* * procseq.c: example of using /proc for debug output, using seq_file. * 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 #include #include /* * This module can be used for printing debug info in /proc/numseq . * /proc/numseq is currently read-only. * It could be made to accept written data also. * * The seq_file interface for /procfs was introduced in Linux 2.4.15. * You won't find it in kernels before that version. * * Using seq_file for reading /procfs files has advantages over other * (prior) proc_read methods. Some of the advantages are: * - kernel API is cleaner * (it is defined in linux/include/linux/seq_file.h) * - don't have to be concerned about output buffer overflow * or truncation * - * * version 0.2: add MODULE_PARM(inputstr); for testing. * version 0.3: add a little 2.5 external module support + Makefile; * version 0.4: add usage/testing of single_open() seq_files; */ #define VERSION "0.4" #define MODNAME "numseq-debug" #define NUMS_SIZE 5 extern struct proc_dir_entry proc_root; static struct proc_dir_entry *numdir; static struct proc_dir_entry *sgldir; static int numbers_current[NUMS_SIZE]; static char *inputstr; // NULL ptr if not set /* * numseq seq_operations */ static void *ns_start(struct seq_file *seq, loff_t *pos) { //loff_t k = *pos; // return 0 => nothing to show; _stop will be called return *pos < NUMS_SIZE ? &numbers_current[*pos] : 0; } static void *ns_next(struct seq_file *seq, void *v, loff_t *pos) { ++*pos; return *pos < NUMS_SIZE ? &numbers_current[*pos] : 0; } /* free, release mem, locks, etc., as needed */ static void ns_stop(struct seq_file *seq, void *v) { seq_puts (seq, "=====\n"); } /* * For a fixed-size output (amount of data), this could * all be done at one call of ns_show(), but this exampe * uses the iterator method to show the general purpose * usefullness of seq_file operations. */ static int ns_show(struct seq_file *seq, void *v) { int *valp = v; //int val = *valp; // current value of item int ix = valp - numbers_current; // get array index if (valp == (int *)&numbers_current) { // or ix == 0 /* or check for beginning of a data struct or array or list */ seq_printf(seq, "module_name: %s\n", MODNAME); seq_printf(seq, "version: %s\n", VERSION); } /* could seq_puts() or seq_printf() any number of items here */ /* give numbers_current[] some flavor by incrementing * them differently, then show the new value */ numbers_current[ix] += ix + 1; /* for each value, show numbers_current[ix] */ seq_printf(seq, "[%d] = %d\n", ix, numbers_current[ix]); return 0; } struct seq_operations ns_ops = { start: ns_start, next: ns_next, stop: ns_stop, show: ns_show }; /* * numseq file_operations */ /* * In this example, the function doesn't write the * passed data. It is only used to reset the counters. */ static int numseq_write (struct file *file, const char *buf, size_t count, loff_t *off) { memset (numbers_current, 0, sizeof (numbers_current)); return count < sizeof (numbers_current) ? count : sizeof (numbers_current); } static int numseq_open(struct inode *inode, struct file *file) { return seq_open(file, &ns_ops); } static struct file_operations ns_fops = { open: numseq_open, read: seq_read, write: numseq_write, llseek: seq_lseek, release: seq_release, }; static int sgl_show (struct seq_file *seq, void *v) { int ix; seq_printf (seq, "all %d numbers:\n", NUMS_SIZE); for (ix = 0; ix < NUMS_SIZE; ix++) seq_printf (seq, "[%d] = %d\n", ix, numbers_current [ix]); seq_printf (seq, "fini;\n"); return 0; } static int sglseq_open(struct inode *inode, struct file *file) { return single_open(file, sgl_show, NULL); } static struct file_operations ss_fops = { open: sglseq_open, read: seq_read, llseek: seq_lseek, release: single_release, }; static int __init parse_input (char *input) { int level, next, other, count; // format types: o x=X i* d* u [* = signed] printk ("scanning input string:{%s}:\n", input); level = next = other = count = 0; count = sscanf (input, "%o %i %i", &level, &next, &other); printk (" scanned: %d: %%o level = 0%o = 0x%x, next = %d, other = %d\n", count, level, level, next, other); level = next = other = count = 0; count = sscanf (input, "%x %i %i", &level, &next, &other); printk (" scanned: %d: %%x level = %d = 0x%x, next = %d, other = %d\n", count, level, level, next, other); level = next = other = count = 0; count = sscanf (input, "%i %i %i", &level, &next, &other); printk (" scanned: %d: %%i level = %d = 0x%x, next = %d, other = %d\n", count, level, level, next, other); level = next = other = count = 0; count = sscanf (input, "%d %i %i", &level, &next, &other); printk (" scanned: %d: %%d level = %d = 0x%x, next = %d, other = %d\n", count, level, level, next, other); level = next = other = count = 0; count = sscanf (input, "%u %i %i", &level, &next, &other); printk (" scanned: %d: %%u level = %d = 0x%x, next = %d, other = %d\n", count, level, level, next, other); return 0; } static int __init numseq_init (void) { numdir = create_proc_entry ("numseq", 0644, &proc_root); if (numdir) { numdir->owner = THIS_MODULE; numdir->uid = 0; numdir->proc_fops = &ns_fops; } else printk (KERN_INFO MODNAME ": numdir create error\n"); sgldir = create_proc_entry ("sglseq", 0644, &proc_root); if (sgldir) { sgldir->owner = THIS_MODULE; sgldir->uid = 0; sgldir->proc_fops = &ss_fops; } else printk (KERN_INFO MODNAME ": sgldir create error\n"); if (inputstr) parse_input (inputstr); return 0; } static void __exit numseq_exit (void) { if (numdir) remove_proc_entry ("numseq", NULL); if (sgldir) remove_proc_entry ("sglseq", NULL); } module_init (numseq_init); module_exit (numseq_exit); MODULE_LICENSE ("GPL"); MODULE_DESCRIPTION ("procfseq-debug-example"); MODULE_PARM(inputstr, "s"); MODULE_PARM_DESC(inputstr, "some test input string");