/*
 * Copyright (C) 2000-2002 ASANO Masahiro
 */

#include <unistd.h>
#include "crash.h"

#include <linux/timer.h>

/* BEGIN "linux/kernel/timer.c" */

#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)

struct timer_vec {
	int index;
	struct list_head vec[TVN_SIZE];
};

struct timer_vec_root {
	int index;
	struct list_head vec[TVR_SIZE];
};

/* END "linux/kernel/timer.c" */

PRIVATE addr_t timer_vec();
const commandtable_t command_timer_vec =
	{"timer_vec", timer_vec, "", "print timer_vec table (tv1-tv5)"};

addr_t tv_addr[6];
addr_t jiffies_addr;

void
prhead_timer_vec()
{
	 mprintf("SLOT: "SPTR"  EXPIRES  FUNCTION (ARGUMENT)\n", "ADDRESS");
}

void
print_timer_vec(tvn, addr)
	int tvn;
	addr_t addr;
{
	int i;
	struct timer_vec_root tv;
	struct timer_list tl;
	addr_t vec;
	const char *p;

	/* sizeof(timer_vec) < sizeof(timer_vec_root) */
	memread(addr, sizeof(struct timer_vec_root), &tv, "timer_vec");

	mprintf("\ntv%x  (%lx)  index: %d\n", tvn, addr, tv.index);
	prhead_timer_vec();
	for (i = 0; i < ((tvn == 1)? TVR_SIZE: TVN_SIZE); i++) {
		for (vec = (addr_t)tv.vec[i].next; vec; vec = (addr_t)tl.list.next) {
			if (vec >= addr && vec < addr + sizeof(struct timer_vec_root))
				break;
			memread(vec, sizeof(struct timer_list), &tl, "timer_list");
			if (tl.list.next == tv.vec[i].next)
				break;
			mprintf("%4d: " FPTR, i, vec);
			mprintf(" %8lx", tl.expires);
			p = getsymstr((addr_t)tl.function);
			if (p) {
				mprintf("  %s (%lx)\n", p, tl.data);
			} else {
				mprintf("  %lx (%lx)\n", tl.function, tl.data);
			}
		}
	}
}

PRIVATE addr_t
timer_vec()
{
	int i, c;
	char buf[32];
	unsigned long jiffies;

	while ((c = getopt(argcnt, args, "")) != EOF) {
		switch (c) {
		default:
			THROW(usage);
		}
	}

	if (optind != argcnt)
		THROW(usage);

	for (i = 1; i <= 5; i++) {
		if (tv_addr[i] == 0) {
			sprintf(buf, "tv%x", i);
			if ((tv_addr[i] = searchaddr_bysym(buf)) == 0) {
				THROW(buf);
			}
		}
	}

	GETADDR(jiffies);
	memread(jiffies_addr, sizeof(jiffies), &jiffies, "jiffies");
	mprintf("jiffies = %lx\n", jiffies);

	for (i = 1; i <= 5; i++) {
		print_timer_vec(i, tv_addr[i]);
	}
	return 0;
}
