/*
 * System dependent file for Ultrix 4.2A
 */

/* 
 * Ricardo E Gonzalez, Stanford University <recardog@chroma.stanford.edu>
 *      based on code by
 * Salvador P. Abreu, U.Nova de Lisboa, PORTUGAL <spa@fctunl.rccn.pt>
 *      based on code by
 * Chris Siebenmann, University of Toronto <cks@sys.toronto.edu>
 */

/* LINTLIBRARY */
#include <sys/param.h>
#include <sys/dk.h>
#include <machine/cpu.h>
#include <sys/cpudata.h>
#include <nlist.h>
#include <stdio.h>
#include <errno.h>

#define kmseek(N) if (lseek(kmem, (long) (N), 0) != (long) (N)) perror ("lseek kmem");
#define KMEM 	"/dev/kmem"

extern char *xmalloc(/* int nbytes */);

extern int open(), read();
extern long lseek();

extern char *kernelSymbols;

struct cpudata *cpu_data[MAXCPU];
long **cptime_old = NULL;

int	kmem;			/* file descriptor of /dev/kmem. */
struct nlist	nl[] = {
#define X_ACTIVECPU 0
	{ "_cpu_avail" },	/* number of active CPUs */
#define X_CPUDATA 1
	{ "_cpudata" },		/* cpudata[] array */
#define X_LOWCPU 2
	{ "_lowcpu" },		/* lowest cpu number */
#define X_HIGHCPU 3
	{ "_highcpu" },		/* highest cpu number */
	{ 0 },
};

int activecpu;
int lowcpu = 0;
int highcpu = 0;
static unsigned long cpudata_offset = 0;

/* Called by -version */
void
version()
{
	printf("Ultrix: maxcpu=%d, maxdisk=0\n", MAXCPU);
}

/* Called at the beginning to inquire how many bars are needed. */
int
num_bars()
{
    if ((kmem = open("/dev/kmem", 0)) < 0) {
	perror("/dev/kmem");
	exit(1);
    }
    (void) nlist(kernelSymbols?kernelSymbols:"/vmunix", nl);

/*
    printf("ACTIVE : %x\n", nl[X_ACTIVECPU].n_value);
    printf("CPUDATA: %x\n", nl[X_CPUDATA].n_value);
    fflush(stdout);
*/

    (void) getkval(nl[X_LOWCPU].n_value, (int *)(&lowcpu), sizeof(lowcpu),
	nl[X_LOWCPU].n_name);
    (void) getkval(nl[X_HIGHCPU].n_value, (int *)(&highcpu), sizeof(highcpu),
	nl[X_HIGHCPU].n_name);
    activecpu = highcpu - lowcpu + 1;
    cpudata_offset = nl[X_CPUDATA].n_value;

/*
    printf("CPUS   : %d\n", activecpu);
    fflush(stdout);
    return (activecpu);
*/

}

/*
 * Indicates how many levels each bar has.  For most machines, each bar will
 * have the same stuff.  But one can, for instance, display memory use on one
 * bar, processor levels on others, etc.
 */
void
bar_items(nbars, items)
int nbars;
int items[];    /* nbars items in this */
{
    int i;

    for(i = 0; i < nbars; i++)
        items[i] = CPUSTATES;
}


/* Called after num_bars to ask for the bar names */
/* ARGSUSED */
char **
label_bars(nbars)
{
    static char **names;
    static char hname[MAXHOSTNAMELEN];
    int i;

    names = (char **) malloc (nbars * sizeof (char *));
    for (i=0; i<nbars; i++) {
	names[i] = (char *) malloc (strlen("cpuxxx")+1);
	(void) sprintf (names[i], "cpu%d", i);
    }
    return names;
}

/* 
 *  Called after the bars are created to perform any machine dependent
 *  initializations.
 */
/* ARGSUSED */
void init_bars(nbars)
    int nbars;
{
    int ncpu, i;
    register struct cpudata cpu;

    (void) getkval(cpudata_offset, cpu_data, sizeof(cpu_data), "_cpudata");
	
    cptime_old = (long **) malloc (nbars * sizeof (long *));
    for (ncpu=lowcpu; ncpu<=highcpu; ++ncpu) {
	if (cpu_data[ncpu] != NULL) {
	    (void) getkval(cpu_data[ncpu], &cpu, sizeof(cpu), "???");
	    cptime_old[ncpu] = (long *) malloc (CPUSTATES * sizeof (long));
	    if (cpu.cpu_state & CPU_RUN) {
		for (i = 0; i < CPUSTATES; i ++) {
		    cptime_old[ncpu][i] = cpu.cpu_cptime[i];
		}
	    }
	}
    }
}

/* 
 *  This procedure gets called every interval to compute and display the
 *  bars. It should call draw_bar() with the bar number, the array of
 *  integer values to display in the bar, and the number of values in
 *  the array.
 */
#define delta(ncpu, cpustate) \
    ((int) (cpu.cpu_cptime[(cpustate)] - cptime_old[ncpu][(cpustate)]))

/* ARGSUSED */
void
display_bars(nbars)
{
    int	states[CPUSTATES];
    int	nstates;
    int	i, ncpu;
    int status;
    register struct cpudata cpu;
    extern void draw_bar(	/*int bar_num, int *states, int num_states*/);
	

    (void) getkval(cpudata_offset, cpu_data, sizeof(cpu_data), "_cpudata");
    for (ncpu = lowcpu; ncpu <= highcpu; ++ncpu) {
	if (cpu_data[ncpu] != NULL) {
	    (void) getkval(cpu_data[ncpu], &cpu, sizeof(cpu), "???");
	    if (cpu.cpu_state & CPU_RUN) {
		nstates = 0;
		states[nstates++] = delta(ncpu, CP_IDLE);
		states[nstates++] = delta(ncpu, CP_USER);
		states[nstates++] = delta(ncpu, CP_NICE);
		states[nstates++] = delta(ncpu, CP_SYS);

		draw_bar(ncpu, states, nstates);

		for (i = 0; i < CPUSTATES; i ++) {
		    cptime_old[ncpu][i] = cpu.cpu_cptime[i];
/*		    printf ("%d\t", cptime_old[ncpu][i]); */
		}
/*		printf ("\n"); */
	    }
	}
    }
}


/*
 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
 *      "offset" is the byte offset into the kernel for the desired value,
 *      "ptr" points to a buffer into which the value is retrieved,
 *      "size" is the size of the buffer (and the object to retrieve),
 *      "refstr" is a reference string used when printing error meessages,
 *          if "refstr" starts with a '!', then a failure on read will not
 *          be fatal (this may seem like a silly way to do things, but I
 *          really didn't want the overhead of another argument).
 *  
 */

getkval(offset, ptr, size, refstr)

unsigned long offset;
int *ptr;
int size;
char *refstr;

{
    if (lseek(kmem, (long)offset, SEEK_SET) == -1) {
        if (*refstr == '!')
            refstr++;
        (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, 
                       refstr, strerror(errno));
        exit(23);
    }
    if (read(kmem, (char *) ptr, size) == -1) {
        if (*refstr == '!') 
            return(0);
        else {
            (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, 
                           refstr, strerror(errno));
            exit(23);
        }
    }
    return(1);
}


syntax highlighted by Code2HTML, v. 0.9.1