summaryrefslogtreecommitdiffstats
path: root/misc-modules
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 /misc-modules
downloadldd3-ab121f379a3cff458c90e6f480ba4bb68c8733dd.tar.gz
Linux Device Drivers 3 examples
Diffstat (limited to 'misc-modules')
-rw-r--r--misc-modules/Makefile32
-rw-r--r--misc-modules/complete.c81
-rw-r--r--misc-modules/faulty.c89
-rw-r--r--misc-modules/hello.c20
-rw-r--r--misc-modules/hellop.c40
-rw-r--r--misc-modules/jiq.c264
-rw-r--r--misc-modules/jit.c292
-rw-r--r--misc-modules/kdataalign.c69
-rw-r--r--misc-modules/kdatasize.c48
-rw-r--r--misc-modules/seq.c109
-rw-r--r--misc-modules/silly.c294
-rw-r--r--misc-modules/sleepy.c84
12 files changed, 1422 insertions, 0 deletions
diff --git a/misc-modules/Makefile b/misc-modules/Makefile
new file mode 100644
index 0000000..d04d2d6
--- /dev/null
+++ b/misc-modules/Makefile
@@ -0,0 +1,32 @@
+
+# To build modules outside of the kernel tree, we run "make"
+# in the kernel source tree; the Makefile these then includes this
+# Makefile once again.
+# This conditional selects whether we are being included from the
+# kernel Makefile or not.
+ifeq ($(KERNELRELEASE),)
+
+ # Assume the source tree is where the running kernel was built
+ # You should set KERNELDIR in the environment if it's elsewhere
+ KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+ # The current directory is passed to sub-makes as argument
+ PWD := $(shell pwd)
+
+modules:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
+
+modules_install:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
+
+.PHONY: modules modules_install clean
+
+else
+ # called from kernel build system: just declare what our modules are
+ obj-m := hello.o hellop.o seq.o jit.o jiq.o sleepy.o complete.o \
+ silly.o faulty.o kdatasize.o kdataalign.o
+endif
+
+
diff --git a/misc-modules/complete.c b/misc-modules/complete.c
new file mode 100644
index 0000000..700923f
--- /dev/null
+++ b/misc-modules/complete.c
@@ -0,0 +1,81 @@
+/*
+ * complete.c -- the writers awake the readers
+ *
+ * Copyright (C) 2003 Alessandro Rubini and Jonathan Corbet
+ * Copyright (C) 2003 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.
+ *
+ * $Id: complete.c,v 1.2 2004/09/26 07:02:43 gregkh Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/sched.h> /* current and everything */
+#include <linux/kernel.h> /* printk() */
+#include <linux/fs.h> /* everything... */
+#include <linux/types.h> /* size_t */
+#include <linux/completion.h>
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+static int complete_major = 0;
+
+DECLARE_COMPLETION(comp);
+
+ssize_t complete_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+ printk(KERN_DEBUG "process %i (%s) going to sleep\n",
+ current->pid, current->comm);
+ wait_for_completion(&comp);
+ printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
+ return 0; /* EOF */
+}
+
+ssize_t complete_write (struct file *filp, const char __user *buf, size_t count,
+ loff_t *pos)
+{
+ printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
+ current->pid, current->comm);
+ complete(&comp);
+ return count; /* succeed, to avoid retrial */
+}
+
+
+struct file_operations complete_fops = {
+ .owner = THIS_MODULE,
+ .read = complete_read,
+ .write = complete_write,
+};
+
+
+int complete_init(void)
+{
+ int result;
+
+ /*
+ * Register your major, and accept a dynamic number
+ */
+ result = register_chrdev(complete_major, "complete", &complete_fops);
+ if (result < 0)
+ return result;
+ if (complete_major == 0)
+ complete_major = result; /* dynamic */
+ return 0;
+}
+
+void complete_cleanup(void)
+{
+ unregister_chrdev(complete_major, "complete");
+}
+
+module_init(complete_init);
+module_exit(complete_cleanup);
+
diff --git a/misc-modules/faulty.c b/misc-modules/faulty.c
new file mode 100644
index 0000000..48c1850
--- /dev/null
+++ b/misc-modules/faulty.c
@@ -0,0 +1,89 @@
+/*
+ * faulty.c -- a module which generates an oops when read
+ *
+ * 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.
+ *
+ * $Id: faulty.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h> /* printk() */
+#include <linux/fs.h> /* everything... */
+#include <linux/types.h> /* size_t */
+#include <asm/uaccess.h>
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+int faulty_major = 0;
+
+ssize_t faulty_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ int ret;
+ char stack_buf[4];
+
+ /* Let's try a buffer overflow */
+ memset(stack_buf, 0xff, 20);
+ if (count > 4)
+ count = 4; /* copy 4 bytes to the user */
+ ret = copy_to_user(buf, stack_buf, count);
+ if (!ret)
+ return count;
+ return ret;
+}
+
+ssize_t faulty_write (struct file *filp, const char __user *buf, size_t count,
+ loff_t *pos)
+{
+ /* make a simple fault by dereferencing a NULL pointer */
+ *(int *)0 = 0;
+ return 0;
+}
+
+
+
+struct file_operations faulty_fops = {
+ .read = faulty_read,
+ .write = faulty_write,
+ .owner = THIS_MODULE
+};
+
+
+int faulty_init(void)
+{
+ int result;
+
+ /*
+ * Register your major, and accept a dynamic number
+ */
+ result = register_chrdev(faulty_major, "faulty", &faulty_fops);
+ if (result < 0)
+ return result;
+ if (faulty_major == 0)
+ faulty_major = result; /* dynamic */
+
+ return 0;
+}
+
+void faulty_cleanup(void)
+{
+ unregister_chrdev(faulty_major, "faulty");
+}
+
+module_init(faulty_init);
+module_exit(faulty_cleanup);
+
diff --git a/misc-modules/hello.c b/misc-modules/hello.c
new file mode 100644
index 0000000..85cd6d0
--- /dev/null
+++ b/misc-modules/hello.c
@@ -0,0 +1,20 @@
+/*
+ * $Id: hello.c,v 1.5 2004/10/26 03:32:21 corbet Exp $
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+MODULE_LICENSE("Dual BSD/GPL");
+
+static int hello_init(void)
+{
+ printk(KERN_ALERT "Hello, world\n");
+ return 0;
+}
+
+static void hello_exit(void)
+{
+ printk(KERN_ALERT "Goodbye, cruel world\n");
+}
+
+module_init(hello_init);
+module_exit(hello_exit);
diff --git a/misc-modules/hellop.c b/misc-modules/hellop.c
new file mode 100644
index 0000000..88eaa67
--- /dev/null
+++ b/misc-modules/hellop.c
@@ -0,0 +1,40 @@
+/*
+ * $Id: hellop.c,v 1.4 2004/09/26 07:02:43 gregkh Exp $
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * These lines, although not shown in the book,
+ * are needed to make hello.c run properly even when
+ * your kernel has version support enabled
+ */
+
+
+/*
+ * A couple of parameters that can be passed in: how many times we say
+ * hello, and to whom.
+ */
+static char *whom = "world";
+static int howmany = 1;
+module_param(howmany, int, S_IRUGO);
+module_param(whom, charp, S_IRUGO);
+
+static int hello_init(void)
+{
+ int i;
+ for (i = 0; i < howmany; i++)
+ printk(KERN_ALERT "(%d) Hello, %s\n", i, whom);
+ return 0;
+}
+
+static void hello_exit(void)
+{
+ printk(KERN_ALERT "Goodbye, cruel world\n");
+}
+
+module_init(hello_init);
+module_exit(hello_exit);
diff --git a/misc-modules/jiq.c b/misc-modules/jiq.c
new file mode 100644
index 0000000..dc7da28
--- /dev/null
+++ b/misc-modules/jiq.c
@@ -0,0 +1,264 @@
+/*
+ * jiq.c -- the just-in-queue 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.
+ *
+ * $Id: jiq.c,v 1.7 2004/09/26 07:02:43 gregkh Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/fs.h> /* everything... */
+#include <linux/proc_fs.h>
+#include <linux/errno.h> /* error codes */
+#include <linux/workqueue.h>
+#include <linux/preempt.h>
+#include <linux/interrupt.h> /* tasklets */
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * The delay for the delayed workqueue timer file.
+ */
+static long delay = 1;
+module_param(delay, long, 0);
+
+
+/*
+ * This module is a silly one: it only embeds short code fragments
+ * that show how enqueued tasks `feel' the environment
+ */
+
+#define LIMIT (PAGE_SIZE-128) /* don't print any more after this size */
+
+/*
+ * Print information about the current environment. This is called from
+ * within the task queues. If the limit is reched, awake the reading
+ * process.
+ */
+static DECLARE_WAIT_QUEUE_HEAD (jiq_wait);
+
+
+static struct work_struct jiq_work;
+
+
+
+/*
+ * Keep track of info we need between task queue runs.
+ */
+static struct clientdata {
+ int len;
+ char *buf;
+ unsigned long jiffies;
+ long delay;
+} jiq_data;
+
+#define SCHEDULER_QUEUE ((task_queue *) 1)
+
+
+
+static void jiq_print_tasklet(unsigned long);
+static DECLARE_TASKLET(jiq_tasklet, jiq_print_tasklet, (unsigned long)&jiq_data);
+
+
+/*
+ * Do the printing; return non-zero if the task should be rescheduled.
+ */
+static int jiq_print(void *ptr)
+{
+ struct clientdata *data = ptr;
+ int len = data->len;
+ char *buf = data->buf;
+ unsigned long j = jiffies;
+
+ if (len > LIMIT) {
+ wake_up_interruptible(&jiq_wait);
+ return 0;
+ }
+
+ if (len == 0)
+ len = sprintf(buf," time delta preempt pid cpu command\n");
+ else
+ len =0;
+
+ /* intr_count is only exported since 1.3.5, but 1.99.4 is needed anyways */
+ len += sprintf(buf+len, "%9li %4li %3i %5i %3i %s\n",
+ j, j - data->jiffies,
+ preempt_count(), current->pid, smp_processor_id(),
+ current->comm);
+
+ data->len += len;
+ data->buf += len;
+ data->jiffies = j;
+ return 1;
+}
+
+
+/*
+ * Call jiq_print from a work queue
+ */
+static void jiq_print_wq(void *ptr)
+{
+ struct clientdata *data = (struct clientdata *) ptr;
+
+ if (! jiq_print (ptr))
+ return;
+
+ if (data->delay)
+ schedule_delayed_work(&jiq_work, data->delay);
+ else
+ schedule_work(&jiq_work);
+}
+
+
+
+static int jiq_read_wq(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ DEFINE_WAIT(wait);
+
+ jiq_data.len = 0; /* nothing printed, yet */
+ jiq_data.buf = buf; /* print in this place */
+ jiq_data.jiffies = jiffies; /* initial time */
+ jiq_data.delay = 0;
+
+ prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);
+ schedule_work(&jiq_work);
+ schedule();
+ finish_wait(&jiq_wait, &wait);
+
+ *eof = 1;
+ return jiq_data.len;
+}
+
+
+static int jiq_read_wq_delayed(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ DEFINE_WAIT(wait);
+
+ jiq_data.len = 0; /* nothing printed, yet */
+ jiq_data.buf = buf; /* print in this place */
+ jiq_data.jiffies = jiffies; /* initial time */
+ jiq_data.delay = delay;
+
+ prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE);
+ schedule_delayed_work(&jiq_work, delay);
+ schedule();
+ finish_wait(&jiq_wait, &wait);
+
+ *eof = 1;
+ return jiq_data.len;
+}
+
+
+
+
+/*
+ * Call jiq_print from a tasklet
+ */
+static void jiq_print_tasklet(unsigned long ptr)
+{
+ if (jiq_print ((void *) ptr))
+ tasklet_schedule (&jiq_tasklet);
+}
+
+
+
+static int jiq_read_tasklet(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ jiq_data.len = 0; /* nothing printed, yet */
+ jiq_data.buf = buf; /* print in this place */
+ jiq_data.jiffies = jiffies; /* initial time */
+
+ tasklet_schedule(&jiq_tasklet);
+ interruptible_sleep_on(&jiq_wait); /* sleep till completion */
+
+ *eof = 1;
+ return jiq_data.len;
+}
+
+
+
+
+/*
+ * This one, instead, tests out the timers.
+ */
+
+static struct timer_list jiq_timer;
+
+static void jiq_timedout(unsigned long ptr)
+{
+ jiq_print((void *)ptr); /* print a line */
+ wake_up_interruptible(&jiq_wait); /* awake the process */
+}
+
+
+static int jiq_read_run_timer(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+
+ jiq_data.len = 0; /* prepare the argument for jiq_print() */
+ jiq_data.buf = buf;
+ jiq_data.jiffies = jiffies;
+
+ init_timer(&jiq_timer); /* init the timer structure */
+ jiq_timer.function = jiq_timedout;
+ jiq_timer.data = (unsigned long)&jiq_data;
+ jiq_timer.expires = jiffies + HZ; /* one second */
+
+ jiq_print(&jiq_data); /* print and go to sleep */
+ add_timer(&jiq_timer);
+ interruptible_sleep_on(&jiq_wait); /* RACE */
+ del_timer_sync(&jiq_timer); /* in case a signal woke us up */
+
+ *eof = 1;
+ return jiq_data.len;
+}
+
+
+
+/*
+ * the init/clean material
+ */
+
+static int jiq_init(void)
+{
+
+ /* this line is in jiq_init() */
+ INIT_WORK(&jiq_work, jiq_print_wq, &jiq_data);
+
+ create_proc_read_entry("jiqwq", 0, NULL, jiq_read_wq, NULL);
+ create_proc_read_entry("jiqwqdelay", 0, NULL, jiq_read_wq_delayed, NULL);
+ create_proc_read_entry("jitimer", 0, NULL, jiq_read_run_timer, NULL);
+ create_proc_read_entry("jiqtasklet", 0, NULL, jiq_read_tasklet, NULL);
+
+ return 0; /* succeed */
+}
+
+static void jiq_cleanup(void)
+{
+ remove_proc_entry("jiqwq", NULL);
+ remove_proc_entry("jiqwqdelay", NULL);
+ remove_proc_entry("jitimer", NULL);
+ remove_proc_entry("jiqtasklet", NULL);
+}
+
+
+module_init(jiq_init);
+module_exit(jiq_cleanup);
diff --git a/misc-modules/jit.c b/misc-modules/jit.c
new file mode 100644
index 0000000..62978b0
--- /dev/null
+++ b/misc-modules/jit.c
@@ -0,0 +1,292 @@
+/*
+ * jit.c -- the just-in-time module
+ *
+ * Copyright (C) 2001,2003 Alessandro Rubini and Jonathan Corbet
+ * Copyright (C) 2001,2003 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.
+ *
+ * $Id: jit.c,v 1.16 2004/09/26 07:02:43 gregkh Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+
+#include <asm/hardirq.h>
+/*
+ * This module is a silly one: it only embeds short code fragments
+ * that show how time delays can be handled in the kernel.
+ */
+
+int delay = HZ; /* the default delay, expressed in jiffies */
+
+module_param(delay, int, 0);
+
+MODULE_AUTHOR("Alessandro Rubini");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/* use these as data pointers, to implement four files in one function */
+enum jit_files {
+ JIT_BUSY,
+ JIT_SCHED,
+ JIT_QUEUE,
+ JIT_SCHEDTO
+};
+
+/*
+ * This function prints one line of data, after sleeping one second.
+ * It can sleep in different ways, according to the data pointer
+ */
+int jit_fn(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ unsigned long j0, j1; /* jiffies */
+ wait_queue_head_t wait;
+
+ init_waitqueue_head (&wait);
+ j0 = jiffies;
+ j1 = j0 + delay;
+
+ switch((long)data) {
+ case JIT_BUSY:
+ while (time_before(jiffies, j1))
+ cpu_relax();
+ break;
+ case JIT_SCHED:
+ while (time_before(jiffies, j1)) {
+ schedule();
+ }
+ break;
+ case JIT_QUEUE:
+ wait_event_interruptible_timeout(wait, 0, delay);
+ break;
+ case JIT_SCHEDTO:
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout (delay);
+ break;
+ }
+ j1 = jiffies; /* actual value after we delayed */
+
+ len = sprintf(buf, "%9li %9li\n", j0, j1);
+ *start = buf;
+ return len;
+}
+
+/*
+ * This file, on the other hand, returns the current time forever
+ */
+int jit_currentime(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ struct timeval tv1;
+ struct timespec tv2;
+ unsigned long j1;
+ u64 j2;
+
+ /* get them four */
+ j1 = jiffies;
+ j2 = get_jiffies_64();
+ do_gettimeofday(&tv1);
+ tv2 = current_kernel_time();
+
+ /* print */
+ len=0;
+ len += sprintf(buf,"0x%08lx 0x%016Lx %10i.%06i\n"
+ "%40i.%09i\n",
+ j1, j2,
+ (int) tv1.tv_sec, (int) tv1.tv_usec,
+ (int) tv2.tv_sec, (int) tv2.tv_nsec);
+ *start = buf;
+ return len;
+}
+
+/*
+ * The timer example follows
+ */
+
+int tdelay = 10;
+module_param(tdelay, int, 0);
+
+/* This data structure used as "data" for the timer and tasklet functions */
+struct jit_data {
+ struct timer_list timer;
+ struct tasklet_struct tlet;
+ int hi; /* tasklet or tasklet_hi */
+ wait_queue_head_t wait;
+ unsigned long prevjiffies;
+ unsigned char *buf;
+ int loops;
+};
+#define JIT_ASYNC_LOOPS 5
+
+void jit_timer_fn(unsigned long arg)
+{
+ struct jit_data *data = (struct jit_data *)arg;
+ unsigned long j = jiffies;
+ data->buf += sprintf(data->buf, "%9li %3li %i %6i %i %s\n",
+ j, j - data->prevjiffies, in_interrupt() ? 1 : 0,
+ current->pid, smp_processor_id(), current->comm);
+
+ if (--data->loops) {
+ data->timer.expires += tdelay;
+ data->prevjiffies = j;
+ add_timer(&data->timer);
+ } else {
+ wake_up_interruptible(&data->wait);
+ }
+}
+
+/* the /proc function: allocate everything to allow concurrency */
+int jit_timer(char *buf, char **start, off_t offset,
+ int len, int *eof, void *unused_data)
+{
+ struct jit_data *data;
+ char *buf2 = buf;
+ unsigned long j = jiffies;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ init_timer(&data->timer);
+ init_waitqueue_head (&data->wait);
+
+ /* write the first lines in the buffer */
+ buf2 += sprintf(buf2, " time delta inirq pid cpu command\n");
+ buf2 += sprintf(buf2, "%9li %3li %i %6i %i %s\n",
+ j, 0L, in_interrupt() ? 1 : 0,
+ current->pid, smp_processor_id(), current->comm);
+
+ /* fill the data for our timer function */
+ data->prevjiffies = j;
+ data->buf = buf2;
+ data->loops = JIT_ASYNC_LOOPS;
+
+ /* register the timer */
+ data->timer.data = (unsigned long)data;
+ data->timer.function = jit_timer_fn;
+ data->timer.expires = j + tdelay; /* parameter */
+ add_timer(&data->timer);
+
+ /* wait for the buffer to fill */
+ wait_event_interruptible(data->wait, !data->loops);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ buf2 = data->buf;
+ kfree(data);
+ *eof = 1;
+ return buf2 - buf;
+}
+
+void jit_tasklet_fn(unsigned long arg)
+{
+ struct jit_data *data = (struct jit_data *)arg;
+ unsigned long j = jiffies;
+ data->buf += sprintf(data->buf, "%9li %3li %i %6i %i %s\n",
+ j, j - data->prevjiffies, in_interrupt() ? 1 : 0,
+ current->pid, smp_processor_id(), current->comm);
+
+ if (--data->loops) {
+ data->prevjiffies = j;
+ if (data->hi)
+ tasklet_hi_schedule(&data->tlet);
+ else
+ tasklet_schedule(&data->tlet);
+ } else {
+ wake_up_interruptible(&data->wait);
+ }
+}
+
+/* the /proc function: allocate everything to allow concurrency */
+int jit_tasklet(char *buf, char **start, off_t offset,
+ int len, int *eof, void *arg)
+{
+ struct jit_data *data;
+ char *buf2 = buf;
+ unsigned long j = jiffies;
+ long hi = (long)arg;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ init_waitqueue_head (&data->wait);
+
+ /* write the first lines in the buffer */
+ buf2 += sprintf(buf2, " time delta inirq pid cpu command\n");
+ buf2 += sprintf(buf2, "%9li %3li %i %6i %i %s\n",
+ j, 0L, in_interrupt() ? 1 : 0,
+ current->pid, smp_processor_id(), current->comm);
+
+ /* fill the data for our tasklet function */
+ data->prevjiffies = j;
+ data->buf = buf2;
+ data->loops = JIT_ASYNC_LOOPS;
+
+ /* register the tasklet */
+ tasklet_init(&data->tlet, jit_tasklet_fn, (unsigned long)data);
+ data->hi = hi;
+ if (hi)
+ tasklet_hi_schedule(&data->tlet);
+ else
+ tasklet_schedule(&data->tlet);
+
+ /* wait for the buffer to fill */
+ wait_event_interruptible(data->wait, !data->loops);
+
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ buf2 = data->buf;
+ kfree(data);
+ *eof = 1;
+ return buf2 - buf;
+}
+
+
+
+int __init jit_init(void)
+{
+ create_proc_read_entry("currentime", 0, NULL, jit_currentime, NULL);
+ create_proc_read_entry("jitbusy", 0, NULL, jit_fn, (void *)JIT_BUSY);
+ create_proc_read_entry("jitsched",0, NULL, jit_fn, (void *)JIT_SCHED);
+ create_proc_read_entry("jitqueue",0, NULL, jit_fn, (void *)JIT_QUEUE);
+ create_proc_read_entry("jitschedto", 0, NULL, jit_fn, (void *)JIT_SCHEDTO);
+
+ create_proc_read_entry("jitimer", 0, NULL, jit_timer, NULL);
+ create_proc_read_entry("jitasklet", 0, NULL, jit_tasklet, NULL);
+ create_proc_read_entry("jitasklethi", 0, NULL, jit_tasklet, (void *)1);
+
+ return 0; /* success */
+}
+
+void __exit jit_cleanup(void)
+{
+ remove_proc_entry("currentime", NULL);
+ remove_proc_entry("jitbusy", NULL);
+ remove_proc_entry("jitsched", NULL);
+ remove_proc_entry("jitqueue", NULL);
+ remove_proc_entry("jitschedto", NULL);
+
+ remove_proc_entry("jitimer", NULL);
+ remove_proc_entry("jitasklet", NULL);
+ remove_proc_entry("jitasklethi", NULL);
+}
+
+module_init(jit_init);
+module_exit(jit_cleanup);
diff --git a/misc-modules/kdataalign.c b/misc-modules/kdataalign.c
new file mode 100644
index 0000000..92d8ace
--- /dev/null
+++ b/misc-modules/kdataalign.c
@@ -0,0 +1,69 @@
+/*
+ * kdatasize.c -- print the size of common data items from kernel space
+ * This runs with any Linux kernel (not any Unix, because of <linux/types.h>)
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+
+/*
+ * Define several data structures, all of them start with a lone char
+ * in order to present an unaligned offset for the next field
+ */
+struct c {char c; char t;} c;
+struct s {char c; short t;} s;
+struct i {char c; int t;} i;
+struct l {char c; long t;} l;
+struct ll {char c; long long t;} ll;
+struct p {char c; void * t;} p;
+struct u1b {char c; __u8 t;} u1b;
+struct u2b {char c; __u16 t;} u2b;
+struct u4b {char c; __u32 t;} u4b;
+struct u8b {char c; __u64 t;} u8b;
+
+static void data_cleanup(void)
+{
+ /* never called */
+}
+
+static int data_init(void)
+{
+ /* print information and return an error */
+ printk("arch Align: char short int long ptr long-long "
+ " u8 u16 u32 u64\n");
+ printk("%-12s %3i %3i %3i %3i %3i %3i "
+ "%3i %3i %3i %3i\n",
+ system_utsname.machine,
+ /* note that gcc can subtract void * values, but it's not ansi */
+ (int)((void *)(&c.t) - (void *)&c),
+ (int)((void *)(&s.t) - (void *)&s),
+ (int)((void *)(&i.t) - (void *)&i),
+ (int)((void *)(&l.t) - (void *)&l),
+ (int)((void *)(&p.t) - (void *)&p),
+ (int)((void *)(&ll.t) - (void *)&ll),
+ (int)((void *)(&u1b.t) - (void *)&u1b),
+ (int)((void *)(&u2b.t) - (void *)&u2b),
+ (int)((void *)(&u4b.t) - (void *)&u4b),
+ (int)((void *)(&u8b.t) - (void *)&u8b));
+ return -ENODEV;
+}
+
+module_init(data_init);
+module_exit(data_cleanup);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/misc-modules/kdatasize.c b/misc-modules/kdatasize.c
new file mode 100644
index 0000000..4ac8895
--- /dev/null
+++ b/misc-modules/kdatasize.c
@@ -0,0 +1,48 @@
+/*
+ * kdatasize.c -- print the size of common data items from kernel space
+ * This runs with any Linux kernel (not any Unix, because of <linux/types.h>)
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+
+static void data_cleanup(void)
+{
+ /* never called */
+}
+
+int data_init(void)
+{
+ /* print information and return an error */
+ printk("arch Size: char short int long ptr long-long "
+ " u8 u16 u32 u64\n");
+ printk("%-12s %3i %3i %3i %3i %3i %3i "
+ "%3i %3i %3i %3i\n",
+ system_utsname.machine,
+ (int)sizeof(char), (int)sizeof(short), (int)sizeof(int),
+ (int)sizeof(long),
+ (int)sizeof(void *), (int)sizeof(long long), (int)sizeof(__u8),
+ (int)sizeof(__u16), (int)sizeof(__u32), (int)sizeof(__u64));
+ return -ENODEV;
+}
+
+module_init(data_init);
+module_exit(data_cleanup);
+
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/misc-modules/seq.c b/misc-modules/seq.c
new file mode 100644
index 0000000..59026a5
--- /dev/null
+++ b/misc-modules/seq.c
@@ -0,0 +1,109 @@
+/*
+ * Simple demonstration of the seq_file interface.
+ *
+ * $Id: seq.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+
+MODULE_AUTHOR("Jonathan Corbet");
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+
+/*
+ * The sequence iterator functions. The position as seen by the
+ * filesystem is just the count that we return.
+ */
+static void *ct_seq_start(struct seq_file *s, loff_t *pos)
+{
+ loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL);
+ if (!spos)
+ return NULL;
+ *spos = *pos;
+ return spos;
+}
+
+static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ loff_t *spos = (loff_t *) v;
+ *pos = ++(*spos);
+ return spos;
+}
+
+static void ct_seq_stop(struct seq_file *s, void *v)
+{
+ kfree (v);
+}
+
+/*
+ * The show function.
+ */
+static int ct_seq_show(struct seq_file *s, void *v)
+{
+ loff_t *spos = (loff_t *) v;
+ seq_printf(s, "%Ld\n", *spos);
+ return 0;
+}
+
+/*
+ * Tie them all together into a set of seq_operations.
+ */
+static struct seq_operations ct_seq_ops = {
+ .start = ct_seq_start,
+ .next = ct_seq_next,
+ .stop = ct_seq_stop,
+ .show = ct_seq_show
+};
+
+
+/*
+ * Time to set up the file operations for our /proc file. In this case,
+ * all we need is an open function which sets up the sequence ops.
+ */
+
+static int ct_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ct_seq_ops);
+};
+
+/*
+ * The file operations structure contains our open function along with
+ * set of the canned seq_ ops.
+ */
+static struct file_operations ct_file_ops = {
+ .owner = THIS_MODULE,
+ .open = ct_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release
+};
+
+
+/*
+ * Module setup and teardown.
+ */
+
+static int ct_init(void)
+{
+ struct proc_dir_entry *entry;
+
+ entry = create_proc_entry("sequence", 0, NULL);
+ if (entry)
+ entry->proc_fops = &ct_file_ops;
+ return 0;
+}
+
+static void ct_exit(void)
+{
+ remove_proc_entry("sequence", NULL);
+}
+
+module_init(ct_init);
+module_exit(ct_exit);
diff --git a/misc-modules/silly.c b/misc-modules/silly.c
new file mode 100644
index 0000000..3b1f893
--- /dev/null
+++ b/misc-modules/silly.c
@@ -0,0 +1,294 @@
+/*
+ * silly.c -- Simple Tool for Unloading and Printing ISA Data
+ *
+ * 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.
+ *
+ * $Id: silly.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $
+ */
+
+/* =========================> BIG FAT WARNING:
+ * This will only work on architectures with an ISA memory range.
+ * It won't work on other computers.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/fs.h> /* everything... */
+#include <linux/errno.h> /* error codes */
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/poll.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+int silly_major = 0;
+module_param(silly_major, int, 0);
+MODULE_AUTHOR("Alessandro Rubini");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * The devices access the 640k-1M memory.
+ * minor 0 uses ioread8/iowrite8
+ * minor 1 uses ioread16/iowrite16
+ * minor 2 uses ioread32/iowrite32
+ * minor 3 uses memcpy_fromio()/memcpy_toio()
+ */
+
+/*
+ * Here's our address range, and a place to store the ioremap'd base.
+ */
+#define ISA_BASE 0xA0000
+#define ISA_MAX 0x100000 /* for general memory access */
+
+#define VIDEO_MAX 0xC0000 /* for vga access */
+#define VGA_BASE 0xb8000
+static void __iomem *io_base;
+
+
+
+int silly_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+int silly_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+enum silly_modes {M_8=0, M_16, M_32, M_memcpy};
+
+ssize_t silly_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+ int retval;
+ int mode = iminor(filp->f_dentry->d_inode);
+ void __iomem *add;
+ unsigned long isa_addr = ISA_BASE + *f_pos;
+ unsigned char *kbuf, *ptr;
+
+ if (isa_addr + count > ISA_MAX) /* range: 0xA0000-0x100000 */
+ count = ISA_MAX - isa_addr;
+
+ /*
+ * too big an f_pos (caused by a malicious lseek())
+ * would result in a negative count
+ */
+ if (count < 0)
+ return 0;
+
+ kbuf = kmalloc(count, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+ ptr = kbuf;
+ retval = count;
+ /*
+ * Convert our address into our remapped area.
+ */
+ add = (void __iomem *)(io_base + (isa_addr - ISA_BASE));
+ /*
+ * kbuf is aligned, but the reads might not. In order not to
+ * drive me mad with unaligned leading and trailing bytes,
+ * I downgrade the `mode' if unaligned xfers are requested.
+ */
+
+ if (mode == M_32 && ((isa_addr | count) & 3))
+ mode = M_16;
+ if (mode == M_16 && ((isa_addr | count) & 1))
+ mode = M_8;
+
+ switch(mode) {
+ case M_32:
+ while (count >= 4) {
+ *(u32 *)ptr = ioread32(add);
+ add += 4;
+ count -= 4;
+ ptr += 4;
+ }
+ break;
+
+ case M_16:
+ while (count >= 2) {
+ *(u16 *)ptr = ioread16(add);
+ add+=2;
+ count-=2;
+ ptr+=2;
+ }
+ break;
+
+ case M_8:
+ while (count) {
+ *ptr = ioread8(add);
+ add++;
+ count--;
+ ptr++;
+ }
+ break;
+
+ case M_memcpy:
+ memcpy_fromio(ptr, add, count);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ if ((retval > 0) && copy_to_user(buf, kbuf, retval))
+ retval = -EFAULT;
+ kfree(kbuf);
+ *f_pos += retval;
+ return retval;
+}
+
+
+ssize_t silly_write(struct file *filp, const char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ int retval;
+ int mode = iminor(filp->f_dentry->d_inode);
+ unsigned long isa_addr = ISA_BASE + *f_pos;
+ unsigned char *kbuf, *ptr;
+ void __iomem *add;
+
+ /*
+ * Writing is dangerous.
+ * Allow root-only, independently of device permissions
+ */
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ if (isa_addr + count > ISA_MAX) /* range: 0xA0000-0x100000 */
+ count = ISA_MAX - isa_addr;
+
+ /*
+ * too big an f_pos (caused by a malicious lseek())
+ * results in a negative count
+ */
+ if (count < 0)
+ return 0;
+
+ kbuf = kmalloc(count, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+ ptr = kbuf;
+ retval=count;
+
+ /*
+ * kbuf is aligned, but the writes might not. In order not to
+ * drive me mad with unaligned leading and trailing bytes,
+ * I downgrade the `mode' if unaligned xfers are requested.
+ */
+
+ if (mode == M_32 && ((isa_addr | count) & 3))
+ mode = M_16;
+ if (mode == M_16 && ((isa_addr | count) & 1))
+ mode = M_8;
+
+ if (copy_from_user(kbuf, buf, count)) {
+ kfree(kbuf);
+ return -EFAULT;
+ }
+ ptr = kbuf;
+
+ /*
+ * Switch over to our remapped address space.
+ */
+ add = (void __iomem *)(io_base + (isa_addr - ISA_BASE));
+
+ switch(mode) {
+ case M_32:
+ while (count >= 4) {
+ iowrite8(*(u32 *)ptr, add);
+ add += 4;
+ count -= 4;
+ ptr += 4;
+ }
+ break;
+
+ case M_16:
+ while (count >= 2) {
+ iowrite8(*(u16 *)ptr, add);
+ add += 2;
+ count -= 2;
+ ptr += 2;
+ }
+ break;
+
+ case M_8:
+ while (count) {
+ iowrite8(*ptr, add);
+ add++;
+ count--;
+ ptr++;
+ }
+ break;
+
+ case M_memcpy:
+ memcpy_toio(add, ptr, count);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ *f_pos += retval;
+ kfree(kbuf);
+ return retval;
+}
+
+
+unsigned int silly_poll(struct file *filp, poll_table *wait)
+{
+ return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
+}
+
+
+struct file_operations silly_fops = {
+ .read = silly_read,
+ .write = silly_write,
+ .poll = silly_poll,
+ .open = silly_open,
+ .release = silly_release,
+ .owner = THIS_MODULE
+};
+
+int silly_init(void)
+{
+ int result = register_chrdev(silly_major, "silly", &silly_fops);
+ if (result < 0) {
+ printk(KERN_INFO "silly: can't get major number\n");
+ return result;
+ }
+ if (silly_major == 0)
+ silly_major = result; /* dynamic */
+ /*
+ * Set up our I/O range.
+ */
+
+ /* this line appears in silly_init */
+ io_base = ioremap(ISA_BASE, ISA_MAX - ISA_BASE);
+ return 0;
+}
+
+void silly_cleanup(void)
+{
+ iounmap(io_base);
+ unregister_chrdev(silly_major, "silly");
+}
+
+
+module_init(silly_init);
+module_exit(silly_cleanup);
diff --git a/misc-modules/sleepy.c b/misc-modules/sleepy.c
new file mode 100644
index 0000000..33c7fc3
--- /dev/null
+++ b/misc-modules/sleepy.c
@@ -0,0 +1,84 @@
+/*
+ * sleepy.c -- the writers awake the readers
+ *
+ * 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.
+ *
+ * $Id: sleepy.c,v 1.7 2004/09/26 07:02:43 gregkh Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/sched.h> /* current and everything */
+#include <linux/kernel.h> /* printk() */
+#include <linux/fs.h> /* everything... */
+#include <linux/types.h> /* size_t */
+#include <linux/wait.h>
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+static int sleepy_major = 0;
+
+static DECLARE_WAIT_QUEUE_HEAD(wq);
+static int flag = 0;
+
+ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+ printk(KERN_DEBUG "process %i (%s) going to sleep\n",
+ current->pid, current->comm);
+ wait_event_interruptible(wq, flag != 0);
+ flag = 0;
+ printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
+ return 0; /* EOF */
+}
+
+ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,
+ loff_t *pos)
+{
+ printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
+ current->pid, current->comm);
+ flag = 1;
+ wake_up_interruptible(&wq);
+ return count; /* succeed, to avoid retrial */
+}
+
+
+struct file_operations sleepy_fops = {
+ .owner = THIS_MODULE,
+ .read = sleepy_read,
+ .write = sleepy_write,
+};
+
+
+int sleepy_init(void)
+{
+ int result;
+
+ /*
+ * Register your major, and accept a dynamic number
+ */
+ result = register_chrdev(sleepy_major, "sleepy", &sleepy_fops);
+ if (result < 0)
+ return result;
+ if (sleepy_major == 0)
+ sleepy_major = result; /* dynamic */
+ return 0;
+}
+
+void sleepy_cleanup(void)
+{
+ unregister_chrdev(sleepy_major, "sleepy");
+}
+
+module_init(sleepy_init);
+module_exit(sleepy_cleanup);
+