summaryrefslogtreecommitdiffstats
path: root/skull/skull_init.c
diff options
context:
space:
mode:
authorJavier Martinez Canillas <martinez.javier@gmail.com>2010-11-27 07:49:17 +0100
committerJavier Martinez Canillas <martinez.javier@gmail.com>2010-11-27 07:49:17 +0100
commitab121f379a3cff458c90e6f480ba4bb68c8733dd (patch)
treea9851af109ee83646d108bc247d03b131461b764 /skull/skull_init.c
downloadldd3-ab121f379a3cff458c90e6f480ba4bb68c8733dd.tar.gz
Linux Device Drivers 3 examples
Diffstat (limited to 'skull/skull_init.c')
-rw-r--r--skull/skull_init.c200
1 files changed, 200 insertions, 0 deletions
diff --git a/skull/skull_init.c b/skull/skull_init.c
new file mode 100644
index 0000000..aaa06e2
--- /dev/null
+++ b/skull/skull_init.c
@@ -0,0 +1,200 @@
+/*
+ * 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);