From ab121f379a3cff458c90e6f480ba4bb68c8733dd Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 27 Nov 2010 07:49:17 +0100 Subject: Linux Device Drivers 3 examples --- misc-modules/Makefile | 32 +++++ misc-modules/complete.c | 81 +++++++++++++ misc-modules/faulty.c | 89 ++++++++++++++ misc-modules/hello.c | 20 ++++ misc-modules/hellop.c | 40 +++++++ misc-modules/jiq.c | 264 +++++++++++++++++++++++++++++++++++++++++ misc-modules/jit.c | 292 +++++++++++++++++++++++++++++++++++++++++++++ misc-modules/kdataalign.c | 69 +++++++++++ misc-modules/kdatasize.c | 48 ++++++++ misc-modules/seq.c | 109 +++++++++++++++++ misc-modules/silly.c | 294 ++++++++++++++++++++++++++++++++++++++++++++++ misc-modules/sleepy.c | 84 +++++++++++++ 12 files changed, 1422 insertions(+) create mode 100644 misc-modules/Makefile create mode 100644 misc-modules/complete.c create mode 100644 misc-modules/faulty.c create mode 100644 misc-modules/hello.c create mode 100644 misc-modules/hellop.c create mode 100644 misc-modules/jiq.c create mode 100644 misc-modules/jit.c create mode 100644 misc-modules/kdataalign.c create mode 100644 misc-modules/kdatasize.c create mode 100644 misc-modules/seq.c create mode 100644 misc-modules/silly.c create mode 100644 misc-modules/sleepy.c (limited to 'misc-modules') 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 +#include + +#include /* current and everything */ +#include /* printk() */ +#include /* everything... */ +#include /* size_t */ +#include + +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 +#include +#include + +#include /* printk() */ +#include /* everything... */ +#include /* size_t */ +#include + +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 +#include +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 +#include +#include + +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 +#include +#include +#include + +#include +#include +#include /* everything... */ +#include +#include /* error codes */ +#include +#include +#include /* 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +/* + * 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 ) + * + * 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 +#include +#include +#include +#include +#include + +/* + * 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 ) + * + * 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 +#include + +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include + + +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 +#include +#include +#include + +#include +#include /* printk() */ +#include /* everything... */ +#include /* error codes */ +#include +#include +#include +#include + +#include +#include + +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 +#include + +#include /* current and everything */ +#include /* printk() */ +#include /* everything... */ +#include /* size_t */ +#include + +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); + -- cgit v1.2.1-18-gbd029