/**
 * @file oprofile.h
 * Main driver code
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author John Levon
 * @author Philippe Elie
 */

#ifndef OPROFILE_H
#define OPROFILE_H

#include <linux/version.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/sysctl.h>
#include <linux/smp_lock.h>

#include <asm/uaccess.h>

#include "compat.h"

#include "op_config_24.h"
#include "op_hw_config.h"
#include "op_interface.h"
#include "op_cpu_type.h"

#undef min
#undef max

#define streq(a, b) (!strcmp((a), (b)))

/* per-cpu dynamic data */
struct _oprof_data {
	/* eviction buffer */
	struct op_sample * buffer;
	/* nr. in buffer */
	uint buf_size;
	/* we try to wakeup when nextbuf >= buf_watermark */
	uint buf_watermark;
	/* next in buffer (atomic) */
	uint nextbuf;
	/* number of IRQs for this CPU */
	uint nr_irq;
	/* buffer overflow cumulated size */
	uint nr_buffer_overflow;
	/* reset counter values */
	uint ctr_count[OP_MAX_COUNTERS];
};

/* reflect /proc/sys/dev/oprofile/#counter files */
struct oprof_counter {
	int count;
	int enabled;
	int event;
	int kernel;
	int user;
	int unit_mask;
};

/* reflect /proc/sys/dev/oprofile files */
struct oprof_sysctl {
	/* nr. in eviction buffser */
	int buf_size;
	/* sysctl dump */
	int dump;
	/* dump and stop */
	int dump_stop;
	/* nr. in note buffer */
	int note_size;
	/* nr. interrupts occured */
	int nr_interrupts;
	/* the cpu core type: CPU_PPRO, CPU_PII ... */
	int cpu_type;
	/* nr note buffer overflow */
	int nr_note_buffer_overflow;
	/* nr buffer overflow */
	int nr_buffer_overflow;
	/* counter setup */
	struct oprof_counter ctr[OP_MAX_COUNTERS];
};

/**
 * A interrupt handler must implement these routines.
 * When an interrupt arrives, it must eventually call
 * op_do_profile().
 */
struct op_int_operations {
	/* initialise the interrupt handler on module load.
	 * On failure deinit handler is not called so all resources
	 * allocated by init() must be freed before returning an error code
	 * (or 0 on success)
	 */
	int (*init)(void);
	/* deinitialise on module unload */
	void (*deinit)(void);
	/* add any handler-specific sysctls at the position given by @next. Return 0 on success */
	int (*add_sysctls)(ctl_table * next);
	/* remove handler-specific sysctls */
	void (*remove_sysctls)(ctl_table * next);
	/* check given profiling parameters are correct. Return 0 on success */
	int (*check_params)(void);
	/* setup the handler from profiling parameters. Return 0 on success */
	int (*setup)(void);
	/* start profiling on all CPUs */
	void (*start)(void);
	/* stop profiling on all CPUs */
	void (*stop)(void);
	/* start profiling on the given CPU */
	void (*start_cpu)(uint);
	/* stop profiling on the given CPU */
	void (*stop_cpu)(uint);
};

/* maximum depth of dname trees - this is just a page */
#define DNAME_STACK_MAX 1024

/* oprof_start() copy here the sysctl settable parameters */
extern struct oprof_sysctl sysctl;

int oprof_init(void);
void oprof_exit(void);
unsigned long is_map_ready(void);
int oprof_hash_map_open(void);
int oprof_hash_map_release(void);
int oprof_hash_map_mmap(struct file * file, struct vm_area_struct * vma);
int oprof_map_open(void);
int oprof_map_release(void);
int oprof_init_hashmap(void);
void oprof_free_hashmap(void);

/* used by interrupt handlers if the underlined harware doesn't support
 * performance counter */
extern struct op_int_operations op_rtc_ops;

void op_do_profile(uint cpu, long eip, long irq_enabled, int ctr);
extern struct _oprof_data oprof_data[NR_CPUS];
extern struct oprof_sysctl sysctl_parms;
extern int lproc_dointvec(ctl_table * table, int write, struct file * filp, void * buffer, size_t * lenp);

/* functionality provided by the architecture dependent file */
/* must return OP_RTC if the hardware doesn't support something like
 * perf counter */
op_cpu get_cpu_type(void);
/* return an interface pointer, this function is called only if get_cpu_type
 * doesn't return OP_RTC */
struct op_int_operations const * op_int_interface(void);
/* intercept the needed syscall */
void op_intercept_syscalls(void);
void op_restore_syscalls(void);
void op_save_syscalls(void);

#endif /* OPROFILE_H */