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

#include "def.h"
#include "mpercpu.h"

#define __KERNEL__
#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)

typedef struct tvec_s {
	struct list_head vec[TVN_SIZE];
} tvec_t;

typedef struct tvec_root_s {
	struct list_head vec[TVR_SIZE];
} tvec_root_t;

struct tvec_t_base_s {
	spinlock_t lock;
	unsigned long timer_jiffies;
	struct timer_list *running_timer;
	tvec_root_t tv1;
	tvec_t tv2;
	tvec_t tv3;
	tvec_t tv4;
	tvec_t tv5;
};

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

IMPORT_PER_CPU(tvec_bases);
extern unsigned long __per_cpu_offset[32];

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

PRIVATE void
print_timer_vec(tvn, addr, index)
	int tvn;
	addr_t addr;
	unsigned long index;
{
	int i;
	struct tvec_root_s tv;
	struct timer_list tl;
	addr_t vec;
	const char *p;
	int vec_size;

	vec_size = (tvn == 1)?	sizeof(struct tvec_root_s):
				sizeof(struct tvec_s);
	memread(addr, vec_size, &tv, "timer_vec");

	mprintf("\ntv%x  (%lx)  index: %ld\n", tvn, addr, 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.entry.next) {
			if (vec >= addr && vec < addr + vec_size)
				break;
			memread(vec, sizeof(struct timer_list), &tl, "timer_list");
			mprintf("%4d: " FPTR " ", i, vec);
			if (tl.entry.next == tv.vec[i].next || (addr_t)tl.entry.next == vec) {
				mprintf("?\n");
				break;
			}
			mprintf("%8lx", tl.expires);
			p = getsymstr_func((addr_t)tl.function);
			if (p) {
				mprintf("  %s (%lx)\n", p, tl.data);
			} else {
				mprintf("  " FPTR " (%lx)\n", tl.function, tl.data);
			}
		}
	}
}

addr_t
print_timer(jiffies, cpu)
	unsigned long jiffies;
	int cpu;
{
	/*NR_CPUS*/
	{
		unsigned long timerj;
		memread(PER_CPU_ADDR(tvec_bases, cpu) + OFFSET(struct tvec_t_base_s, timer_jiffies), sizeof(timerj), &timerj, "timer_jiffies");
		print_timer_vec(1, PER_CPU_ADDR(tvec_bases, cpu) + OFFSET(struct tvec_t_base_s, tv1), timerj & TVR_MASK);
		timerj >>= TVR_BITS;
		print_timer_vec(2, PER_CPU_ADDR(tvec_bases, cpu) + OFFSET(struct tvec_t_base_s, tv2), timerj & TVN_MASK);
		timerj >>= TVN_BITS;
		print_timer_vec(3, PER_CPU_ADDR(tvec_bases, cpu) + OFFSET(struct tvec_t_base_s, tv3), timerj & TVN_MASK);
		timerj >>= TVN_BITS;
		print_timer_vec(4, PER_CPU_ADDR(tvec_bases, cpu) + OFFSET(struct tvec_t_base_s, tv4), timerj & TVN_MASK);
		timerj >>= TVN_BITS;
		print_timer_vec(5, PER_CPU_ADDR(tvec_bases, cpu) + OFFSET(struct tvec_t_base_s, tv5), timerj & TVN_MASK);
	}
	return 0;
}
