root/ldd3-samples-1.0.0/skull/skull_init.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. skull_fn1
  2. skull_fn2
  3. skull_probe_hw
  4. skull_init_board
  5. skull_detect
  6. skull_find_hw
  7. skull_init

/*
 * skull.c -- sample typeless module.
 *
 * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
 * Copyright (C) 2001 O'Reilly & Associates
 *
 * The source code in this file can be freely used, adapted,
 * and redistributed in source or binary form, so long as an
 * acknowledgment appears in derived source files.  The citation
 * should list that the code comes from the book "Linux Device
 * Drivers" by Alessandro Rubini and Jonathan Corbet, published
 * by O'Reilly & Associates.   No warranty is attached;
 * we cannot take responsibility for errors or fitness for use.
 *
 * BUGS:
 *   -it only runs on intel platforms.
 *   -readb() should be used (see short.c): skull doesn't work with 2.1
 *
 */

/* jc: cleaned up, but not yet run for anything */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>

#include <linux/kernel.h> /* printk */
#include <linux/ioport.h>
#include <linux/errno.h>
#include <asm/system.h> /* cli(), *_flags */
#include <linux/mm.h> /* vremap (2.0) */
#include <asm/io.h> /* ioremap */

/* The region we look at. */
#define ISA_REGION_BEGIN 0xA0000
#define ISA_REGION_END   0x100000
#define STEP 2048

/* have three symbols to export */
       void skull_fn1(void){}
static void skull_fn2(void){}
       int  skull_variable;

EXPORT_SYMBOL (skull_fn1);
EXPORT_SYMBOL (skull_fn2);
EXPORT_SYMBOL (skull_variable);


/* perform hardware autodetection */
int skull_probe_hw(unsigned int port, unsigned int range)
{
   /* do smart probing here */
   return -1; /* not found  :-) */
}

/* perform hardware initalizazion */
int skull_init_board(unsigned int port)
{
  /* do smart initalization here */
  return 0; /* done :-) */
}

/* detect the the device if the region is still free */
static int skull_detect(unsigned int port, unsigned int range)
{
    int err;

    if ((err = check_region(port,range)) < 0) return err; /* busy */
    if (skull_probe_hw(port,range) != 0) return -ENODEV;  /* not found */
    request_region(port,range,"skull");                   /* "Can't fail" */
    return 0;
}

/*
 * port ranges: the device can reside between
 * 0x280 and 0x300, in step of 0x10. It uses 0x10 ports.
 */
#define SKULL_PORT_FLOOR 0x280
#define SKULL_PORT_CEIL  0x300
#define SKULL_PORT_RANGE  0x010

/*
 * the following function performs autodetection, unless a specific
 * value was assigned by insmod to "skull_port_base"
 */

static int skull_port_base=0; /* 0 forces autodetection */
module_param(skull_port_base, int, 0);

static int skull_find_hw(void) /* returns the # of devices */
{
    /* base is either the load-time value or the first trial */
    int base = skull_port_base ? skull_port_base 
                             : SKULL_PORT_FLOOR; 
    int result = 0;

    /* loop one time if value assigned, try them all if autodetecting */
    do {
        if (skull_detect(base, SKULL_PORT_RANGE) == 0) {
            skull_init_board(base);
            result++;
        }
        base += SKULL_PORT_RANGE; /* prepare for next trial */
    }
    while (skull_port_base == 0 && base < SKULL_PORT_CEIL);

    return result;
}


int skull_init(void)
{
    /*
     * Print the isa region map, in blocks of 2K bytes.
     * This is not the best code, as it prints too many lines,
     * but it deserves to remain short to be included in the book.
     * Note also that read() should be used instead of pointers.
     */
    unsigned char oldval, newval; /* values read from memory   */
    unsigned long flags;          /* used to hold system flags */
    unsigned long add, i;
    void *base;
    
    /* Use ioremap to get a handle on our region */
    base = ioremap(ISA_REGION_BEGIN, ISA_REGION_END - ISA_REGION_BEGIN);
    base -= ISA_REGION_BEGIN;  /* Do the offset once */
    
    /* probe all the memory hole in 2KB steps */
    for (add = ISA_REGION_BEGIN; add < ISA_REGION_END; add += STEP) {
        /*
         * Check for an already allocated region.
         */
        if (check_mem_region (add, 2048)) {
                printk(KERN_INFO "%lx: Allocated\n", add);
                continue;
        }
        /*
         * Read and write the beginning of the region and see what happens.
         */
        save_flags(flags); 
        cli();
        oldval = readb (base + add);  /* Read a byte */
        writeb (oldval^0xff, base + add);
        mb();
        newval = readb (base + add);
        writeb (oldval, base + add);
        restore_flags(flags);

        if ((oldval^newval) == 0xff) {  /* we re-read our change: it's ram */
            printk(KERN_INFO "%lx: RAM\n", add);
            continue;
        }
        if ((oldval^newval) != 0) {  /* random bits changed: it's empty */
            printk(KERN_INFO "%lx: empty\n", add);
            continue;
        }
        
        /*
         * Expansion rom (executed at boot time by the bios)
         * has a signature where the first byt is 0x55, the second 0xaa,
         * and the third byte indicates the size of such rom
         */
        if ( (oldval == 0x55) && (readb (base + add + 1) == 0xaa)) {
            int size = 512 * readb (base + add + 2);
            printk(KERN_INFO "%lx: Expansion ROM, %i bytes\n",
                   add, size);
            add += (size & ~2048) - 2048; /* skip it */
            continue;
        }
        
        /*
         * If the tests above failed, we still don't know if it is ROM or
         * empty. Since empty memory can appear as 0x00, 0xff, or the low
         * address byte, we must probe multiple bytes: if at least one of
         * them is different from these three values, then this is rom
         * (though not boot rom).
         */
        printk(KERN_INFO "%lx: ", add);
        for (i=0; i<5; i++) {
            unsigned long radd = add + 57*(i+1);  /* a "random" value */
            unsigned char val = readb (base + radd);
            if (val && val != 0xFF && val != ((unsigned long) radd&0xFF))
                break;
        }    
        printk("%s\n", i==5 ? "empty" : "ROM");
    }

    /*
     * Find you hardware 
     */
    skull_find_hw();

    /*
     * Always fail to load (or suceed).
     */
    return 0;
}

module_init(skull_init);

/* [<][>][^][v][top][bottom][index][help] */