/*
 * procdiv64.c:  example of using /proc for div64 output, using proc_read()
 *   with the addition of a div64() function (strictly an example)
 * 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/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/kernel_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:
* remove_proc_entry for "numbers64", not "numbers";
*/

#define VERSION		"0.2"
#define MODNAME		"div64-example"

//////////////////////////////////////////////////////////////////////
/* div64.c */

#include <linux/types.h>

/*
 * Function copied/adapted/optimized from:
 *
 *  nemesis.sourceforge.net/browse/lib/static/intmath/ix86/intmath.c.html
 *
 * Copyright 1994, University of Cambridge Computer Laboratory
 * All Rights Reserved.
 *
 * TODO: When running on a 64-bit CPU platform, this should no longer be
 * TODO: necessary.
 *
 * Function modified to return both quotient and remainder
 * instead of one of them at a time.
 *
 * returns integer quotient (x/y);
 * if (rem != NULL) returns remainder part of (x/y) in *rem;
 * where remainder part of (x/y) == x % y == x MOD y;
 *
 * version 0.2:  add 2.5 external module support + Makefile;
 */

s64 divrem64(s64 x, s64 y, s64 *rem)
{
	u64 a = (x < 0) ? -x : x;
	u64 b = (y < 0) ? -y : y;
	u64 res = 0, d = 1;

	if (b > 0) while (b < a) b <<= 1, d <<= 1;

	do {
		if (a >= b) a -= b, res += d;
		b >>= 1;
		d >>= 1;
	} while (d);

	if (rem)
		*rem = ((x & (1ll<<63)) == 0) ? a : -(s64)a;
	return (((x ^ y) & (1ll<<63)) == 0) ? res : -(s64)res;
}
//////////////////////////////////////////////////////////////////////

extern struct proc_dir_entry proc_root;
static struct proc_dir_entry *numdir;
static int number_current;
/*
 * kstat.
	unsigned int pgpgin, pgpgout;
	unsigned int pswpin, pswpout;
	unsigned int context_swtch;
 */
/////extern int nr_running, nr_threads, last_pid;
extern unsigned long volatile jiffies;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)

#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40)
static int numbers_read (char *buf, char **start, off_t offset, int count,
	int *eof, void *data)
{
	int len = 0;
	__u64 ctxtsw = kstat.context_swtch;
	__u64 pages = kstat.pgpgin + kstat.pgpgout + kstat.pswpin + kstat.pswpout;
	__u64 jifs = jiffies;
	__u64 t1, t2;

	len = sprintf(buf, "module_name: %s\n", MODNAME);
	len += sprintf(buf + len, "version: %s\n", VERSION);

	/* paging * 1000 / ctxtsw ::= */

	t1 = divrem64(pages * 1000, ctxtsw, &t2);
	len += sprintf(buf + len, "pages, ctxtsw, t1, t2:  %lld, %lld, %lld, %lld\n",
			pages, ctxtsw, t1, t2);

	/* paging * 1000 / jifs ::= */

	t1 = divrem64(pages * 1000, jifs, &t2);
	len += sprintf(buf + len, "pages, jifs, t1, t2:  %lld, %lld, %lld, %lld\n",
			pages, jifs, t1, t2);

	/* ctxtsw * 1000 / jifs ::= */
	t1 = divrem64(ctxtsw * 1000, jifs, &t2);
	len += sprintf(buf + len, "ctxtsw, jifs, t1, t2:  %lld, %lld, %lld, %lld\n",
			ctxtsw, jifs, t1, t2);

	*eof = 1;
	return len;
}
#else	// for 2.5.40+
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/swap.h>

#define KB(x)	((x) << (PAGE_SHIFT - 10))

static int numbers_read (char *buf, char **start, off_t offset, int count,
	int *eof, void *data)
{
	int len = 0;
	__u64 jifs = jiffies;
	__u64 t1, t2;
	/// ctxtsw and pages aren't good for >= 2.5.40, so use different data;
	/// for 2.5.40+, maybe use /proc/cpuinfo.cpuMHz: <cpu_khz>; Y
	/// or something from /proc/meminfo (si_meminfo(&si));
	/// or something else from /proc/stat (not ctxtsw;
	///    btime := <xtime.tv_sec> - (ulong) <jifs>);
	struct sysinfo si;
	__u64 btime = xtime.tv_sec - jifs;

	si_meminfo (&si);
	// convert si to KB
	si.totalram = KB(si.totalram);		// MemTotal:
	si.freeram = KB(si.freeram);		// MemFree:
	si.bufferram = KB(si.bufferram);	// Buffers:
	si.totalhigh = KB(si.totalhigh);	// HighTotal:
	si.freehigh = KB(si.freehigh);		// HighFree:
	//KB(si.totalram - si.totalhigh); // LowTotal:
	//KB(si.freeram - si.freehigh);   // LowFree:

	len = sprintf(buf, "module_name: %s\n", MODNAME);
	len += sprintf(buf + len, "version: %s\n", VERSION);

	// do some div64() examples: //
	/* cpu_khz / 1000 ::= cpu MHz */

	t1 = divrem64(cpu_khz, 1000, &t2);
	len += sprintf(buf + len, "cpu_khz; cpu_MHz := t1, t2:  %lld, %lld.%lld\n",
			(__u64)cpu_khz, t1, t2);

	/* btime / xtime.tv_sec ::= */

	t1 = divrem64(btime, xtime.tv_sec, &t2);
	len += sprintf(buf + len, "btime / xtime.tv_sec := t1, t2:  %lld, %lld, %lld, %lld\n",
			btime, (__u64)xtime.tv_sec, t1, t2);

	/* btime / jifs ::= */
	t1 = divrem64(btime, jifs, &t2);
	len += sprintf(buf + len, "btime / jifs := t1, t2:  %lld, %lld, %lld, %lld\n",
			btime, jifs, t1, t2);

	*eof = 1;
	return len;
}
#endif

/*
 * 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 ("numbers64", 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 ("numbers64", NULL);
}

module_init (numbers_init);
module_exit (numbers_exit);
#ifdef MODULE_LICENSE
MODULE_LICENSE ("GPL");
#endif
MODULE_DESCRIPTION ("procfs-div64-example");
