/*
 * procmod.c:  example of using /proc for debug output, using proc_read()
 * Copyright (C) 2002-2003 Randy Dunlap <rddunlap@ieee.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., 675 Mass Ave, Cambridge, MA 02139, USA.
******************************************************************/

#define MODULE

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>

/*
* 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 <number_current> 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 <linux/ioport.h>
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 ?: "<BAD>",
				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 <write> function doesn't write the
 * passed data.  It is only used to reset the <number> 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");
