├── .cproject ├── .gitignore ├── .project ├── LICENSE ├── Makefile ├── README.Debian ├── README.md ├── include ├── access_ok_version.h ├── lddbus.h └── proc_ops_version.h ├── init ├── lddbus ├── Makefile └── lddbus.c ├── misc-modules ├── Makefile ├── complete.c ├── faulty.c ├── hello.c ├── hellop.c ├── jiq.c ├── jit.c ├── kdataalign.c ├── kdatasize.c ├── module_load ├── module_unload ├── seq.c ├── silly.c └── sleepy.c ├── misc-progs ├── Makefile ├── asynctest.c ├── dataalign.c ├── datasize.c ├── gdbline ├── inp.c ├── load50.c ├── mapcmp.c ├── mapper.c ├── nbtest.c ├── netifdebug.c ├── outp.c ├── polltest.c ├── setconsole.c ├── setlevel.c └── test │ ├── .gitignore │ └── nonblock.sh ├── pci ├── Makefile └── pci_skel.c ├── sbull ├── Makefile ├── sbull.c ├── sbull.h ├── sbull_load └── sbull_unload ├── scull-shared ├── scull-async.c └── scull-async.h ├── scull ├── Makefile ├── access.c ├── main.c ├── pipe.c ├── scull.h ├── scull.init ├── scull_load └── scull_unload ├── scullc ├── Makefile ├── main.c ├── mmap.c ├── scull-shared │ ├── scull-async.c │ └── scull-async.h ├── scullc.h ├── scullc_load └── scullc_unload ├── sculld ├── Makefile ├── main.c ├── mmap.c ├── scull-shared │ ├── scull-async.c │ └── scull-async.h ├── sculld.h ├── sculld_load └── sculld_unload ├── scullp ├── Makefile ├── main.c ├── mmap.c ├── scull-shared │ ├── scull-async.c │ └── scull-async.h ├── scullp.h ├── scullp_load └── scullp_unload ├── scullv ├── Makefile ├── main.c ├── mmap.c ├── scull-shared │ ├── scull-async.c │ └── scull-async.h ├── scullv.h ├── scullv_load └── scullv_unload ├── short ├── Makefile ├── short.c ├── short_load └── short_unload ├── shortprint ├── Makefile ├── shortprint.c ├── shortprint.h ├── shortprint_load └── shortprint_unload ├── simple ├── Makefile ├── simple.c ├── simple_load └── simple_unload ├── skull ├── Makefile ├── skull_clean.c └── skull_init.c ├── snull ├── Makefile ├── snull.c ├── snull.h ├── snull_load └── snull_unload ├── tty ├── Makefile ├── tiny_serial.c └── tiny_tty.c └── usb ├── Makefile └── usb-skeleton.c /.cproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 55 | 56 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 77 | 78 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vim 2 | *.sw[a-z] 3 | *.un~ 4 | Session.vim 5 | 6 | # make file 7 | .* 8 | !.gitignore 9 | *~ 10 | *.o 11 | core 12 | *.ko 13 | *.mod.c 14 | Module.symvers 15 | modules.order 16 | *.mod 17 | *.a 18 | 19 | # misc-progs 20 | nbtest 21 | load50 22 | mapcmp 23 | polltest 24 | mapper 25 | setlevel 26 | setconsole 27 | inp 28 | outp 29 | datasize 30 | dataalign 31 | netifdebug 32 | asynctest 33 | 34 | # Allows for Eclipse CDT lookups without checking in linux source 35 | linux_source_cdt 36 | !.project 37 | !.cproject 38 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ldd3 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 10 | clean,full,incremental, 11 | 12 | 13 | 14 | 15 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 16 | full,incremental, 17 | 18 | 19 | 20 | 21 | 22 | org.eclipse.cdt.core.cnature 23 | org.eclipse.cdt.core.ccnature 24 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 25 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 26 | 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | 3 | Unless otherwise stated, the source code distributed with this book 4 | can be redistributed in source or binary form so long as an 5 | acknowledgment appears in derived source files. The citation should 6 | list that the code comes from BOOK by AUTHOR, published by O'Reilly & 7 | Associates. This code is under copyright and cannot be included in 8 | any other book, publication, or educational product without permission 9 | from O'Reilly & Associates. No warranty is attached; we cannot take 10 | responsibility for errors or fitness for use. 11 | 12 | 13 | There are a few exception to this licence, however: a few sources 14 | herein are distributed according to the GNU General Public License, 15 | version 2. You'll find a copy of the license in 16 | /usr/src/linux/COPYING, and in other places in your filesystem. The 17 | affected source files are: 18 | 19 | pci/pci_skel.c 20 | tty/tiny_serial.c 21 | tty/tiny_tty.c 22 | usb/usb-skeleton.c 23 | 24 | The files in ./pci ./tty and ./usb inherit the GPL from the kernel 25 | sources, as most of their code comes straight from the kernel 26 | (usb-skeleton.c being part of the kernel source tree directly.) 27 | 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | SUBDIRS = misc-progs misc-modules \ 3 | skull scull scullc scullp lddbus sculld scullv shortprint simple tty \ 4 | pci usb\ 5 | sbull snull short 6 | 7 | all: subdirs 8 | 9 | subdirs: 10 | for n in $(SUBDIRS); do $(MAKE) -C $$n || exit 1; done 11 | 12 | clean: 13 | for n in $(SUBDIRS); do $(MAKE) -C $$n clean; done 14 | -------------------------------------------------------------------------------- /README.Debian: -------------------------------------------------------------------------------- 1 | Compiling in Debian 2 | ------------------- 3 | 4 | In order to build examples for current prebuilt kernel, you should install 5 | linux-headers package for you current kernel: 6 | 7 | $ sudo apt-get install linux-headers-$(uname -r) 8 | 9 | You will also need make tools: 10 | 11 | $ sudo apt-get install make 12 | 13 | Now just run make in example dir you want to build. 14 | 15 | NOTE: These examples were designed for latest linux kernel and you might not 16 | be able to build some of them for older prebuilt kernels from Debian-based 17 | distributives. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ldd3: Linux Device Drivers 3 examples updated to work with recent kernels 2 | 3 | # About 4 | ----- 5 | 6 | Linux Device Drivers 3 (http://lwn.net/Kernel/LDD3/) book is now a few years 7 | old and most of the example drivers do not compile in recent kernels. 8 | 9 | This project aims to keep LDD3 example drivers up-to-date with recent kernels. 10 | 11 | The original code can be found at: http://examples.oreilly.com/9780596005900/ 12 | 13 | # Compiling 14 | ---------- 15 | 16 | The example drivers should compile against latest Linus Torvalds kernel tree: 17 | * git://git.kernel.org/pub/scm/linux/kernel/git/sfr/linux-next.git 18 | 19 | To compile the drivers against a specific tree (for example Linus tree): 20 | ``` 21 | $ git clone git://github.com/martinezjavier/ldd3.git 22 | $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 23 | $ export KERNELDIR=/path/to/linux 24 | $ cd ldd3 25 | $ make 26 | ``` 27 | 28 | Bugs, comments or patches: See https://github.com/martinezjavier/ldd3/issues 29 | 30 | # Latest Tested Kernel Builds 31 | --------- 32 | The kernel builds below are the versions most recently tested/supported 33 | 34 | * Ubuntu 18.04 kernel as of July 2020: 5.4.0-42-generic 35 | * Ubuntu 20.04 kernel as of July 2021: 5.4.0-73-generic 36 | * Yocto poky warrior branch kernel for qemu aarch64 builds: 5.0.19 37 | * Yocto poky hardknott branch kernel for qemu aarch64 builds: 5.10.46 38 | * Buildroot 2019.05 kernel for qemu builds: 4.9.16 39 | * Buildroot 2021.02 kernel for qemu builds: 5.10 40 | * Alpine 3.13 kernel as of May 2021: 5.10.29-lts, see [here](https://github.com/ericwq/gccIDE/wiki/ldd3-project) for detail. 41 | 42 | 43 | # Eclipse Integration 44 | ---------4 45 | Eclipse CDT integration is provided by symlinking the correct linux source directory with the ./linux_source_cdt symlink. 46 | The .project and .cproject files were setup using instructions in [this link](https://wiki.eclipse.org/HowTo_use_the_CDT_to_navigate_Linux_kernel_source) 47 | and assuming a symlink is setup in the local project directory to point to relevant kernel headers 48 | 49 | This can be done on a system with kernel headers installed using: 50 | ``` 51 | ln -s /usr/src/linux-headers-`uname -r`/ linux_source_cdt 52 | ``` 53 | -------------------------------------------------------------------------------- /include/access_ok_version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @file access_ok_version.h 3 | * @date 10/13/2019 4 | * 5 | */ 6 | 7 | #include 8 | 9 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0) 10 | #define access_ok_wrapper(type,arg,cmd) \ 11 | access_ok(type, arg, cmd) 12 | #else 13 | #define access_ok_wrapper(type,arg,cmd) \ 14 | access_ok(arg, cmd) 15 | #endif 16 | -------------------------------------------------------------------------------- /include/lddbus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Definitions for the virtual LDD bus. 3 | * 4 | * $Id: lddbus.h,v 1.4 2004/08/20 18:49:44 corbet Exp $ 5 | */ 6 | 7 | //extern struct device ldd_bus; 8 | extern struct bus_type ldd_bus_type; 9 | 10 | 11 | /* 12 | * The LDD driver type. 13 | */ 14 | 15 | struct ldd_driver { 16 | char *version; 17 | struct module *module; 18 | struct device_driver driver; 19 | struct driver_attribute version_attr; 20 | }; 21 | 22 | #define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver); 23 | 24 | /* 25 | * A device type for things "plugged" into the LDD bus. 26 | */ 27 | 28 | struct ldd_device { 29 | char *name; 30 | struct ldd_driver *driver; 31 | struct device dev; 32 | }; 33 | 34 | #define to_ldd_device(dev) container_of(dev, struct ldd_device, dev); 35 | 36 | extern int register_ldd_device(struct ldd_device *); 37 | extern void unregister_ldd_device(struct ldd_device *); 38 | extern int register_ldd_driver(struct ldd_driver *); 39 | extern void unregister_ldd_driver(struct ldd_driver *); 40 | -------------------------------------------------------------------------------- /include/proc_ops_version.h: -------------------------------------------------------------------------------- 1 | #ifndef _PROC_OPS_VERSION_H 2 | #define _PROC_OPS_VERSION_H 3 | 4 | #include 5 | 6 | #ifdef CONFIG_COMPAT 7 | #define __add_proc_ops_compat_ioctl(pops, fops) \ 8 | (pops)->proc_compat_ioctl = (fops)->compat_ioctl 9 | #else 10 | #define __add_proc_ops_compat_ioctl(pops, fops) 11 | #endif 12 | 13 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) 14 | #define proc_ops_wrapper(fops, newname) (fops) 15 | #else 16 | #define proc_ops_wrapper(fops, newname) \ 17 | ({ \ 18 | static struct proc_ops newname; \ 19 | \ 20 | newname.proc_open = (fops)->open; \ 21 | newname.proc_read = (fops)->read; \ 22 | newname.proc_write = (fops)->write; \ 23 | newname.proc_release = (fops)->release; \ 24 | newname.proc_poll = (fops)->poll; \ 25 | newname.proc_ioctl = (fops)->unlocked_ioctl; \ 26 | newname.proc_mmap = (fops)->mmap; \ 27 | newname.proc_get_unmapped_area = (fops)->get_unmapped_area; \ 28 | newname.proc_lseek = (fops)->llseek; \ 29 | __add_proc_ops_compat_ioctl(&newname, fops); \ 30 | &newname; \ 31 | }) 32 | #endif 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /init: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | 4 | case "$1" in 5 | start) 6 | echo "Starting init script for Module Loading" 7 | start-stop-daemon -S -n init -a /bin/scull_load 8 | start-stop-daemon -S -n init -a /bin/module_load -- hello 9 | ;; 10 | stop) 11 | echo "Removing user modules" 12 | start-stop-daemon -K -n scull_load 13 | start-stop-daemon -K -n module_load 14 | start-stop-daemon -S -n init -a /bin/scull_unload 15 | start-stop-daemon -S -n init -a /bin/module_unload -- hello 16 | ;; 17 | *) 18 | echo "Usage: $0 {start|stop}" 19 | exit 1 20 | esac 21 | exit 0 22 | -------------------------------------------------------------------------------- /lddbus/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | # Add your debugging flag (or not) to CFLAGS 5 | ifeq ($(DEBUG),y) 6 | DEBFLAGS = -O -g # "-O" is needed to expand inlines 7 | else 8 | DEBFLAGS = -O2 9 | endif 10 | 11 | LDDINCDIR=$(PWD)/../include 12 | EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINCDIR) 13 | 14 | ifneq ($(KERNELRELEASE),) 15 | # call from kernel build system 16 | 17 | obj-m := lddbus.o 18 | 19 | else 20 | 21 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 22 | PWD := $(shell pwd) 23 | 24 | default: 25 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 26 | 27 | endif 28 | 29 | 30 | 31 | clean: 32 | rm -rf *.o *.ko *~ core .depend *.mod.c .*.cmd .tmp_versions .*.o.d *.mod modules.order *.symvers 33 | 34 | depend .depend dep: 35 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 36 | 37 | 38 | ifeq (.depend,$(wildcard .depend)) 39 | include .depend 40 | endif 41 | -------------------------------------------------------------------------------- /lddbus/lddbus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A virtual bus for LDD sample code devices to plug into. This 3 | * code is heavily borrowed from drivers/base/sys.c 4 | * 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | * 16 | */ 17 | /* $Id: lddbus.c,v 1.9 2004/09/26 08:12:27 gregkh Exp $ */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "lddbus.h" 26 | 27 | MODULE_AUTHOR("Jonathan Corbet"); 28 | MODULE_LICENSE("Dual BSD/GPL"); 29 | static char *Version = "$Revision: 1.9 $"; 30 | 31 | /* 32 | * Respond to udev events. 33 | */ 34 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)) 35 | static int ldd_uevent(struct device *dev, struct kobj_uevent_env *env) 36 | #else 37 | static int ldd_uevent(const struct device *dev, struct kobj_uevent_env *env) 38 | #endif 39 | { 40 | if (add_uevent_var(env, "LDDBUS_VERSION=%s", Version)) 41 | return -ENOMEM; 42 | 43 | return 0; 44 | } 45 | 46 | /* 47 | * Match LDD devices to drivers. Just do a simple name test. 48 | */ 49 | static int ldd_match(struct device *dev, struct device_driver *driver) 50 | { 51 | return !strncmp(dev_name(dev), driver->name, strlen(driver->name)); 52 | } 53 | 54 | 55 | /* 56 | * The LDD bus device. 57 | */ 58 | static void ldd_bus_release(struct device *dev) 59 | { 60 | printk(KERN_DEBUG "lddbus release\n"); 61 | } 62 | 63 | struct device ldd_bus = { 64 | .release = ldd_bus_release 65 | }; 66 | 67 | 68 | /* 69 | * And the bus type. 70 | */ 71 | struct bus_type ldd_bus_type = { 72 | .name = "ldd", 73 | .match = ldd_match, 74 | .uevent = ldd_uevent, 75 | }; 76 | 77 | /* 78 | * Export a simple attribute. 79 | */ 80 | /* Changed in kernel commit 75cff725d9566699a670a02b3cfd1c6e9e9ed53e 81 | * driver core: bus: mark the struct bus_type for sysfs callbacks as constant */ 82 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)) 83 | static ssize_t version_show(struct bus_type *bus, char *buf) 84 | #else 85 | static ssize_t version_show(const struct bus_type *bus, char *buf) 86 | #endif 87 | { 88 | return snprintf(buf, PAGE_SIZE, "%s\n", Version); 89 | } 90 | 91 | static BUS_ATTR_RO(version); 92 | 93 | 94 | 95 | /* 96 | * LDD devices. 97 | */ 98 | 99 | /* 100 | * For now, no references to LDDbus devices go out which are not 101 | * tracked via the module reference count, so we use a no-op 102 | * release function. 103 | */ 104 | static void ldd_dev_release(struct device *dev) 105 | { } 106 | 107 | int register_ldd_device(struct ldd_device *ldddev) 108 | { 109 | ldddev->dev.bus = &ldd_bus_type; 110 | ldddev->dev.parent = &ldd_bus; 111 | ldddev->dev.release = ldd_dev_release; 112 | dev_set_name(&ldddev->dev, ldddev->name); 113 | return device_register(&ldddev->dev); 114 | } 115 | EXPORT_SYMBOL(register_ldd_device); 116 | 117 | void unregister_ldd_device(struct ldd_device *ldddev) 118 | { 119 | device_unregister(&ldddev->dev); 120 | } 121 | EXPORT_SYMBOL(unregister_ldd_device); 122 | 123 | /* 124 | * Crude driver interface. 125 | */ 126 | 127 | 128 | static ssize_t show_version(struct device_driver *driver, char *buf) 129 | { 130 | struct ldd_driver *ldriver = to_ldd_driver(driver); 131 | 132 | sprintf(buf, "%s\n", ldriver->version); 133 | return strlen(buf); 134 | } 135 | 136 | 137 | int register_ldd_driver(struct ldd_driver *driver) 138 | { 139 | int ret; 140 | 141 | driver->driver.bus = &ldd_bus_type; 142 | ret = driver_register(&driver->driver); 143 | if (ret) 144 | return ret; 145 | driver->version_attr.attr.name = "version"; 146 | driver->version_attr.attr.mode = S_IRUGO; 147 | driver->version_attr.show = show_version; 148 | driver->version_attr.store = NULL; 149 | return driver_create_file(&driver->driver, &driver->version_attr); 150 | } 151 | 152 | void unregister_ldd_driver(struct ldd_driver *driver) 153 | { 154 | driver_unregister(&driver->driver); 155 | } 156 | EXPORT_SYMBOL(register_ldd_driver); 157 | EXPORT_SYMBOL(unregister_ldd_driver); 158 | 159 | 160 | 161 | static int __init ldd_bus_init(void) 162 | { 163 | int ret; 164 | 165 | ret = bus_register(&ldd_bus_type); 166 | if (ret) { 167 | printk(KERN_ERR "Unable to register ldd bus, failure was %d\n",ret); 168 | return ret; 169 | } 170 | if (bus_create_file(&ldd_bus_type, &bus_attr_version)) 171 | printk(KERN_ERR "Unable to create version attribute\n"); 172 | dev_set_name(&ldd_bus,"ldd0"); 173 | ret = device_register(&ldd_bus); 174 | if (ret) { 175 | printk(KERN_ERR "Unable to register ldd0, failure was %d\n",ret); 176 | goto out_fail; 177 | } 178 | return 0; 179 | 180 | out_fail: 181 | bus_unregister(&ldd_bus_type); 182 | return ret; 183 | } 184 | 185 | static void ldd_bus_exit(void) 186 | { 187 | device_unregister(&ldd_bus); 188 | bus_unregister(&ldd_bus_type); 189 | } 190 | 191 | module_init(ldd_bus_init); 192 | module_exit(ldd_bus_exit); 193 | -------------------------------------------------------------------------------- /misc-modules/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # To build modules outside of the kernel tree, we run "make" 3 | # in the kernel source tree; the Makefile these then includes this 4 | # Makefile once again. 5 | # This conditional selects whether we are being included from the 6 | # kernel Makefile or not. 7 | 8 | LDDINC=$(PWD)/../include 9 | EXTRA_CFLAGS += -I$(LDDINC) 10 | 11 | ifeq ($(KERNELRELEASE),) 12 | 13 | # Assume the source tree is where the running kernel was built 14 | # You should set KERNELDIR in the environment if it's elsewhere 15 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 16 | # The current directory is passed to sub-makes as argument 17 | PWD := $(shell pwd) 18 | 19 | modules: 20 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 21 | 22 | modules_install: 23 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install 24 | 25 | clean: 26 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers 27 | 28 | .PHONY: modules modules_install clean 29 | 30 | else 31 | # called from kernel build system: just declare what our modules are 32 | obj-m := hello.o hellop.o seq.o jiq.o sleepy.o complete.o \ 33 | silly.o faulty.o kdatasize.o kdataalign.o jit.o 34 | endif 35 | 36 | 37 | -------------------------------------------------------------------------------- /misc-modules/complete.c: -------------------------------------------------------------------------------- 1 | /* 2 | * complete.c -- the writers awake the readers 3 | * 4 | * Copyright (C) 2003 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2003 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: complete.c,v 1.2 2004/09/26 07:02:43 gregkh Exp $ 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include /* current and everything */ 22 | #include /* printk() */ 23 | #include /* everything... */ 24 | #include /* size_t */ 25 | #include 26 | 27 | MODULE_LICENSE("Dual BSD/GPL"); 28 | 29 | static int complete_major = 0; 30 | 31 | DECLARE_COMPLETION(comp); 32 | 33 | ssize_t complete_read (struct file *filp, char __user *buf, size_t count, loff_t *pos) 34 | { 35 | printk(KERN_DEBUG "process %i (%s) going to sleep\n", 36 | current->pid, current->comm); 37 | wait_for_completion(&comp); 38 | printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); 39 | return 0; /* EOF */ 40 | } 41 | 42 | ssize_t complete_write (struct file *filp, const char __user *buf, size_t count, 43 | loff_t *pos) 44 | { 45 | printk(KERN_DEBUG "process %i (%s) awakening the readers...\n", 46 | current->pid, current->comm); 47 | complete(&comp); 48 | return count; /* succeed, to avoid retrial */ 49 | } 50 | 51 | 52 | struct file_operations complete_fops = { 53 | .owner = THIS_MODULE, 54 | .read = complete_read, 55 | .write = complete_write, 56 | }; 57 | 58 | 59 | int complete_init(void) 60 | { 61 | int result; 62 | 63 | /* 64 | * Register your major, and accept a dynamic number 65 | */ 66 | result = register_chrdev(complete_major, "complete", &complete_fops); 67 | if (result < 0) 68 | return result; 69 | if (complete_major == 0) 70 | complete_major = result; /* dynamic */ 71 | return 0; 72 | } 73 | 74 | void complete_cleanup(void) 75 | { 76 | unregister_chrdev(complete_major, "complete"); 77 | } 78 | 79 | module_init(complete_init); 80 | module_exit(complete_cleanup); 81 | 82 | -------------------------------------------------------------------------------- /misc-modules/faulty.c: -------------------------------------------------------------------------------- 1 | /* 2 | * faulty.c -- a module which generates an oops when read 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: faulty.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $ 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include /* printk() */ 22 | #include /* everything... */ 23 | #include /* size_t */ 24 | #include 25 | 26 | MODULE_LICENSE("Dual BSD/GPL"); 27 | 28 | 29 | int faulty_major = 0; 30 | 31 | ssize_t faulty_read(struct file *filp, char __user *buf, 32 | size_t count, loff_t *pos) 33 | { 34 | int i; 35 | int ret; 36 | char stack_buf[4]; 37 | 38 | /* Let's try a buffer overflow */ 39 | for (i = 0; i < 20; i++) 40 | *(stack_buf + i) = 0xff; 41 | if (count > 4) 42 | count = 4; /* copy 4 bytes to the user */ 43 | ret = copy_to_user(buf, stack_buf, count); 44 | if (!ret) 45 | return count; 46 | return ret; 47 | } 48 | 49 | ssize_t faulty_write (struct file *filp, const char __user *buf, size_t count, 50 | loff_t *pos) 51 | { 52 | /* make a simple fault by dereferencing a NULL pointer */ 53 | *(int *)0 = 0; 54 | return 0; 55 | } 56 | 57 | 58 | 59 | struct file_operations faulty_fops = { 60 | .read = faulty_read, 61 | .write = faulty_write, 62 | .owner = THIS_MODULE 63 | }; 64 | 65 | 66 | int faulty_init(void) 67 | { 68 | int result; 69 | 70 | /* 71 | * Register your major, and accept a dynamic number 72 | */ 73 | result = register_chrdev(faulty_major, "faulty", &faulty_fops); 74 | if (result < 0) 75 | return result; 76 | if (faulty_major == 0) 77 | faulty_major = result; /* dynamic */ 78 | 79 | return 0; 80 | } 81 | 82 | void faulty_cleanup(void) 83 | { 84 | unregister_chrdev(faulty_major, "faulty"); 85 | } 86 | 87 | module_init(faulty_init); 88 | module_exit(faulty_cleanup); 89 | 90 | -------------------------------------------------------------------------------- /misc-modules/hello.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: hello.c,v 1.5 2004/10/26 03:32:21 corbet Exp $ 3 | */ 4 | #include 5 | #include 6 | MODULE_LICENSE("Dual BSD/GPL"); 7 | 8 | static int hello_init(void) 9 | { 10 | printk(KERN_ALERT "Hello, world\n"); 11 | return 0; 12 | } 13 | 14 | static void hello_exit(void) 15 | { 16 | printk(KERN_ALERT "Goodbye, cruel world\n"); 17 | } 18 | 19 | module_init(hello_init); 20 | module_exit(hello_exit); 21 | -------------------------------------------------------------------------------- /misc-modules/hellop.c: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: hellop.c,v 1.4 2004/09/26 07:02:43 gregkh Exp $ 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | MODULE_LICENSE("Dual BSD/GPL"); 9 | 10 | /* 11 | * These lines, although not shown in the book, 12 | * are needed to make hello.c run properly even when 13 | * your kernel has version support enabled 14 | */ 15 | 16 | 17 | /* 18 | * A couple of parameters that can be passed in: how many times we say 19 | * hello, and to whom. 20 | */ 21 | static char *whom = "world"; 22 | static int howmany = 1; 23 | module_param(howmany, int, S_IRUGO); 24 | module_param(whom, charp, S_IRUGO); 25 | 26 | static int hello_init(void) 27 | { 28 | int i; 29 | for (i = 0; i < howmany; i++) 30 | printk(KERN_ALERT "(%d) Hello, %s\n", i, whom); 31 | return 0; 32 | } 33 | 34 | static void hello_exit(void) 35 | { 36 | printk(KERN_ALERT "Goodbye, cruel world\n"); 37 | } 38 | 39 | module_init(hello_init); 40 | module_exit(hello_exit); 41 | -------------------------------------------------------------------------------- /misc-modules/jiq.c: -------------------------------------------------------------------------------- 1 | /* 2 | * jiq.c -- the just-in-queue module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * jiq.c,v 1.7 2004/09/26 07:02:43 gregkh Exp 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include /* everything... */ 25 | #include 26 | #include 27 | #include /* error codes */ 28 | #include 29 | #include 30 | #include /* tasklets */ 31 | 32 | #include "proc_ops_version.h" 33 | MODULE_LICENSE("Dual BSD/GPL"); 34 | 35 | /* 36 | * The delay for the delayed workqueue timer file. 37 | */ 38 | static long delay = 1; 39 | module_param(delay, long, 0); 40 | 41 | /* 42 | * This module is a silly one: it only embeds short code fragments 43 | * that show how enqueued tasks `feel' the environment 44 | */ 45 | 46 | #define LIMIT (PAGE_SIZE-128) /* don't print any more after this size */ 47 | 48 | /* 49 | * Print information about the current environment. This is called from 50 | * within the task queues. If the limit is reched, awake the reading 51 | * process. 52 | */ 53 | static DECLARE_WAIT_QUEUE_HEAD(jiq_wait); 54 | 55 | /* 56 | * Keep track of info we need between task queue runs. 57 | */ 58 | static struct clientdata { 59 | struct work_struct jiq_work; 60 | struct delayed_work jiq_delayed_work; 61 | struct timer_list jiq_timer; 62 | struct tasklet_struct jiq_tasklet; 63 | struct seq_file *m; 64 | int len; 65 | unsigned long jiffies; 66 | long delay; 67 | } jiq_data; 68 | 69 | #define SCHEDULER_QUEUE ((task_queue *) 1) 70 | 71 | static void jiq_print_tasklet(unsigned long); 72 | 73 | /* 74 | * Do the printing; return non-zero if the task should be rescheduled. 75 | */ 76 | static int jiq_print(struct clientdata *data) 77 | { 78 | int len = data->len; 79 | struct seq_file *m = data->m; 80 | unsigned long j = jiffies; 81 | 82 | if (len > LIMIT) { 83 | wake_up_interruptible(&jiq_wait); 84 | return 0; 85 | } 86 | 87 | if (len == 0) { 88 | seq_puts(m, " time delta preempt pid cpu command\n"); 89 | len = m->count; 90 | } else { 91 | len = 0; 92 | } 93 | 94 | /* intr_count is only exported since 1.3.5, 95 | but 1.99.4 is needed anyways */ 96 | seq_printf(m, "%9li %4li %3i %5i %3i %s\n", 97 | j, j - data->jiffies, 98 | preempt_count(), current->pid, smp_processor_id(), 99 | current->comm); 100 | len += m->count; 101 | 102 | data->len += len; 103 | data->jiffies = j; 104 | return 1; 105 | } 106 | 107 | 108 | /* 109 | * Call jiq_print from a work queue 110 | */ 111 | static void jiq_print_wq(struct work_struct *work) 112 | { 113 | struct clientdata *data = container_of(work, 114 | struct clientdata, jiq_work); 115 | 116 | if (!jiq_print(data)) 117 | return; 118 | 119 | schedule_work(&jiq_data.jiq_work); 120 | } 121 | 122 | static void jiq_print_wq_delayed(struct work_struct *work) 123 | { 124 | struct clientdata *data = container_of(work, struct clientdata, 125 | jiq_delayed_work.work); 126 | 127 | if (!jiq_print (data)) 128 | return; 129 | 130 | schedule_delayed_work(&jiq_data.jiq_delayed_work, data->delay); 131 | } 132 | 133 | 134 | static int jiq_read_wq_show(struct seq_file *m, void *v) 135 | { 136 | DEFINE_WAIT(wait); 137 | 138 | jiq_data.len = 0; /* nothing printed, yet */ 139 | jiq_data.m = m; /* print in this place */ 140 | jiq_data.jiffies = jiffies; /* initial time */ 141 | jiq_data.delay = 0; 142 | 143 | prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE); 144 | schedule_work(&jiq_data.jiq_work); 145 | schedule(); 146 | finish_wait(&jiq_wait, &wait); 147 | 148 | return 0; 149 | } 150 | 151 | static int jiq_read_wq_open(struct inode *inode, struct file *file) 152 | { 153 | return single_open(file, jiq_read_wq_show, NULL); 154 | } 155 | 156 | static const struct file_operations jiq_read_wq_fops = { 157 | .open = jiq_read_wq_open, 158 | .read = seq_read, 159 | .llseek = seq_lseek, 160 | .release = single_release, 161 | }; 162 | 163 | static int jiq_read_wq_delayed_show(struct seq_file *m, void *v) 164 | { 165 | DEFINE_WAIT(wait); 166 | 167 | jiq_data.len = 0; /* nothing printed, yet */ 168 | jiq_data.m = m; /* print in this place */ 169 | jiq_data.jiffies = jiffies; /* initial time */ 170 | jiq_data.delay = delay; 171 | 172 | prepare_to_wait(&jiq_wait, &wait, TASK_INTERRUPTIBLE); 173 | schedule_delayed_work(&jiq_data.jiq_delayed_work, delay); 174 | schedule(); 175 | finish_wait(&jiq_wait, &wait); 176 | 177 | return 0; 178 | } 179 | 180 | static int jiq_read_wq_delayed_open(struct inode *inode, struct file *file) 181 | { 182 | return single_open(file, jiq_read_wq_delayed_show, NULL); 183 | } 184 | 185 | static const struct file_operations jiq_read_wq_delayed_fops = { 186 | .open = jiq_read_wq_delayed_open, 187 | .read = seq_read, 188 | .llseek = seq_lseek, 189 | .release = single_release, 190 | }; 191 | 192 | /* 193 | * Call jiq_print from a tasklet 194 | */ 195 | static void jiq_print_tasklet(unsigned long ptr) 196 | { 197 | struct clientdata *data = (struct clientdata *)ptr; 198 | if (jiq_print(data)) 199 | tasklet_schedule(&data->jiq_tasklet); 200 | } 201 | 202 | static int jiq_read_tasklet_show(struct seq_file *m, void *v) 203 | { 204 | jiq_data.len = 0; /* nothing printed, yet */ 205 | jiq_data.m = m; /* print in this place */ 206 | jiq_data.jiffies = jiffies; /* initial time */ 207 | 208 | tasklet_schedule(&jiq_data.jiq_tasklet); 209 | wait_event_interruptible(jiq_wait, 0); /* sleep till completion */ 210 | 211 | return 0; 212 | } 213 | 214 | static int jiq_read_tasklet_open(struct inode *inode, struct file *file) 215 | { 216 | return single_open(file, jiq_read_tasklet_show, NULL); 217 | } 218 | 219 | static const struct file_operations jiq_read_tasklet_fops = { 220 | .open = jiq_read_tasklet_open, 221 | .read = seq_read, 222 | .llseek = seq_lseek, 223 | .release = single_release, 224 | }; 225 | 226 | /* 227 | * This one, instead, tests out the timers. 228 | */ 229 | 230 | static void jiq_timedout(struct timer_list *t) 231 | { 232 | struct clientdata *data = from_timer(data, t, jiq_timer); 233 | jiq_print(data); /* print a line */ 234 | wake_up_interruptible(&jiq_wait); /* awake the process */ 235 | } 236 | 237 | static int jiq_read_run_timer_show(struct seq_file *m, void *v) 238 | { 239 | jiq_data.len = 0; /* prepare the argument for jiq_print() */ 240 | jiq_data.m = m; 241 | jiq_data.jiffies = jiffies; 242 | 243 | timer_setup(&jiq_data.jiq_timer, jiq_timedout, 0); 244 | jiq_data.jiq_timer.expires = jiffies + HZ; /* one second */ 245 | 246 | jiq_print(&jiq_data); /* print and go to sleep */ 247 | add_timer(&jiq_data.jiq_timer); 248 | wait_event_interruptible(jiq_wait, 0); /* RACE */ 249 | del_timer_sync(&jiq_data.jiq_timer); /* in case a signal woke us up */ 250 | 251 | return 0; 252 | } 253 | 254 | static int jiq_read_run_timer_open(struct inode *inode, struct file *file) 255 | { 256 | return single_open(file, jiq_read_run_timer_show, NULL); 257 | } 258 | 259 | static const struct file_operations jiq_read_run_timer_fops = { 260 | .open = jiq_read_run_timer_open, 261 | .read = seq_read, 262 | .llseek = seq_lseek, 263 | .release = single_release, 264 | }; 265 | 266 | /* 267 | * the init/clean material 268 | */ 269 | 270 | static int jiq_init(void) 271 | { 272 | /* this line is in jiq_init() */ 273 | INIT_WORK(&jiq_data.jiq_work, jiq_print_wq); 274 | INIT_DELAYED_WORK(&jiq_data.jiq_delayed_work, jiq_print_wq_delayed); 275 | tasklet_init(&jiq_data.jiq_tasklet, jiq_print_tasklet, 276 | (unsigned long)&jiq_data); 277 | 278 | proc_create("jiqwq", 0, NULL, 279 | proc_ops_wrapper(&jiq_read_wq_fops, jiq_read_wq_pops)); 280 | proc_create("jiqwqdelay", 0, NULL, 281 | proc_ops_wrapper(&jiq_read_wq_delayed_fops, jiq_read_wq_delayed_pops)); 282 | proc_create("jitimer", 0, NULL, 283 | proc_ops_wrapper(&jiq_read_run_timer_fops, jiq_read_run_timer_pops)); 284 | proc_create("jiqtasklet", 0, NULL, 285 | proc_ops_wrapper(&jiq_read_tasklet_fops, jiq_read_tasklet_pops)); 286 | 287 | return 0; /* succeed */ 288 | } 289 | 290 | static void jiq_cleanup(void) 291 | { 292 | remove_proc_entry("jiqwq", NULL); 293 | remove_proc_entry("jiqwqdelay", NULL); 294 | remove_proc_entry("jitimer", NULL); 295 | remove_proc_entry("jiqtasklet", NULL); 296 | } 297 | 298 | 299 | module_init(jiq_init); 300 | module_exit(jiq_cleanup); 301 | -------------------------------------------------------------------------------- /misc-modules/jit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * jit.c -- the just-in-time module 3 | * 4 | * Copyright (C) 2001,2003 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001,2003 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * jit.c,v 1.16 2004/09/26 07:02:43 gregkh Exp 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "proc_ops_version.h" 37 | /* 38 | * This module is a silly one: it only embeds short code fragments 39 | * that show how time delays can be handled in the kernel. 40 | */ 41 | 42 | int delay = HZ; /* the default delay, expressed in jiffies */ 43 | 44 | module_param(delay, int, 0); 45 | 46 | MODULE_AUTHOR("Alessandro Rubini"); 47 | MODULE_LICENSE("Dual BSD/GPL"); 48 | 49 | /* use these as data pointers, to implement four files in one function */ 50 | enum jit_files { 51 | JIT_BUSY, 52 | JIT_SCHED, 53 | JIT_QUEUE, 54 | JIT_SCHEDTO 55 | }; 56 | 57 | /* 58 | * This function prints one line of data, after sleeping one second. 59 | * It can sleep in different ways, according to the data pointer 60 | */ 61 | int jit_fn_show(struct seq_file *m, void *v) 62 | { 63 | unsigned long j0, j1; /* jiffies */ 64 | wait_queue_head_t wait; 65 | long data = (long)m->private; 66 | 67 | init_waitqueue_head(&wait); 68 | j0 = jiffies; 69 | j1 = j0 + delay; 70 | 71 | switch (data) { 72 | case JIT_BUSY: 73 | while (time_before(jiffies, j1)) 74 | cpu_relax(); 75 | break; 76 | case JIT_SCHED: 77 | while (time_before(jiffies, j1)) 78 | schedule(); 79 | break; 80 | case JIT_QUEUE: 81 | wait_event_interruptible_timeout(wait, 0, delay); 82 | break; 83 | case JIT_SCHEDTO: 84 | set_current_state(TASK_INTERRUPTIBLE); 85 | schedule_timeout(delay); 86 | break; 87 | } 88 | j1 = jiffies; /* actual value after we delayed */ 89 | 90 | seq_printf(m, "%9li %9li\n", j0, j1); 91 | return 0; 92 | } 93 | 94 | static int jit_fn_open(struct inode *inode, struct file *file) 95 | { 96 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0) 97 | return single_open(file, jit_fn_show, PDE_DATA(inode)); 98 | #else 99 | return single_open(file, jit_fn_show, pde_data(inode)); 100 | #endif 101 | } 102 | 103 | static const struct file_operations jit_fn_fops = { 104 | .open = jit_fn_open, 105 | .read = seq_read, 106 | .llseek = seq_lseek, 107 | .release = single_release, 108 | }; 109 | 110 | /* 111 | * This file, on the other hand, returns the current time forever 112 | */ 113 | int jit_currentime_show(struct seq_file *m, void *v) 114 | { 115 | unsigned long j1; 116 | u64 j2; 117 | 118 | /* get them four */ 119 | j1 = jiffies; 120 | j2 = get_jiffies_64(); 121 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0) 122 | { 123 | struct timeval tv1; 124 | struct timespec tv2; 125 | do_gettimeofday(&tv1); 126 | tv2 = current_kernel_time(); 127 | /* print */ 128 | seq_printf(m, "0x%08lx 0x%016Lx %10i.%06i\n" 129 | "%40i.%09i\n", 130 | j1, j2, 131 | (int) tv1.tv_sec, (int) tv1.tv_usec, 132 | (int) tv2.tv_sec, (int) tv2.tv_nsec); 133 | } 134 | #else 135 | { 136 | struct timespec64 tv1; 137 | struct timespec64 tv2; 138 | ktime_get_real_ts64(&tv1); 139 | ktime_get_coarse_real_ts64(&tv2); 140 | seq_printf(m, "0x%08lx 0x%016Lx %10i.%09i\n" 141 | "%40i.%09i\n", 142 | j1, j2, 143 | (int) tv1.tv_sec, (int) tv1.tv_nsec, 144 | (int) tv2.tv_sec, (int) tv2.tv_nsec); 145 | 146 | } 147 | #endif 148 | 149 | return 0; 150 | } 151 | 152 | static int jit_currentime_open(struct inode *inode, struct file *file) 153 | { 154 | return single_open(file, jit_currentime_show, NULL); 155 | } 156 | 157 | static const struct file_operations jit_currentime_fops = { 158 | .open = jit_currentime_open, 159 | .read = seq_read, 160 | .llseek = seq_lseek, 161 | .release = single_release, 162 | }; 163 | 164 | /* 165 | * The timer example follows 166 | */ 167 | 168 | int tdelay = 10; 169 | module_param(tdelay, int, 0); 170 | 171 | /* This data structure used as "data" for the timer and tasklet functions */ 172 | struct jit_data { 173 | struct timer_list timer; 174 | struct tasklet_struct tlet; 175 | struct seq_file *m; 176 | int hi; /* tasklet or tasklet_hi */ 177 | wait_queue_head_t wait; 178 | unsigned long prevjiffies; 179 | int loops; 180 | }; 181 | #define JIT_ASYNC_LOOPS 5 182 | 183 | void jit_timer_fn(struct timer_list *t) 184 | { 185 | struct jit_data *data = from_timer(data, t, timer); 186 | unsigned long j = jiffies; 187 | seq_printf(data->m, "%9li %3li %i %6i %i %s\n", 188 | j, j - data->prevjiffies, in_interrupt() ? 1 : 0, 189 | current->pid, smp_processor_id(), current->comm); 190 | 191 | if (--data->loops) { 192 | data->timer.expires += tdelay; 193 | data->prevjiffies = j; 194 | add_timer(&data->timer); 195 | } else { 196 | wake_up_interruptible(&data->wait); 197 | } 198 | } 199 | 200 | /* the /proc function: allocate everything to allow concurrency */ 201 | int jit_timer_show(struct seq_file *m, void *v) 202 | { 203 | struct jit_data *data; 204 | unsigned long j = jiffies; 205 | 206 | data = kmalloc(sizeof(*data), GFP_KERNEL); 207 | if (!data) 208 | return -ENOMEM; 209 | 210 | init_waitqueue_head(&data->wait); 211 | 212 | /* write the first lines in the buffer */ 213 | seq_puts(m, " time delta inirq pid cpu command\n"); 214 | seq_printf(m, "%9li %3li %i %6i %i %s\n", 215 | j, 0L, in_interrupt() ? 1 : 0, 216 | current->pid, smp_processor_id(), current->comm); 217 | 218 | /* fill the data for our timer function */ 219 | data->prevjiffies = j; 220 | data->m = m; 221 | data->loops = JIT_ASYNC_LOOPS; 222 | 223 | /* register the timer */ 224 | timer_setup(&data->timer, jit_timer_fn, 0); 225 | data->timer.expires = j + tdelay; /* parameter */ 226 | add_timer(&data->timer); 227 | 228 | /* wait for the buffer to fill */ 229 | wait_event_interruptible(data->wait, !data->loops); 230 | if (signal_pending(current)) 231 | return -ERESTARTSYS; 232 | kfree(data); 233 | return 0; 234 | } 235 | 236 | static int jit_timer_open(struct inode *inode, struct file *file) 237 | { 238 | return single_open(file, jit_timer_show, NULL); 239 | } 240 | 241 | static const struct file_operations jit_timer_fops = { 242 | .open = jit_timer_open, 243 | .read = seq_read, 244 | .llseek = seq_lseek, 245 | .release = single_release, 246 | }; 247 | 248 | void jit_tasklet_fn(unsigned long arg) 249 | { 250 | struct jit_data *data = (struct jit_data *)arg; 251 | unsigned long j = jiffies; 252 | seq_printf(data->m, "%9li %3li %i %6i %i %s\n", 253 | j, j - data->prevjiffies, in_interrupt() ? 1 : 0, 254 | current->pid, smp_processor_id(), current->comm); 255 | 256 | if (--data->loops) { 257 | data->prevjiffies = j; 258 | if (data->hi) 259 | tasklet_hi_schedule(&data->tlet); 260 | else 261 | tasklet_schedule(&data->tlet); 262 | } else { 263 | wake_up_interruptible(&data->wait); 264 | } 265 | } 266 | 267 | /* the /proc function: allocate everything to allow concurrency */ 268 | int jit_tasklet_show(struct seq_file *m, void *v) 269 | { 270 | struct jit_data *data; 271 | unsigned long j = jiffies; 272 | long hi = (long)m->private; 273 | 274 | data = kmalloc(sizeof(*data), GFP_KERNEL); 275 | if (!data) 276 | return -ENOMEM; 277 | 278 | init_waitqueue_head(&data->wait); 279 | 280 | /* write the first lines in the buffer */ 281 | seq_puts(m, " time delta inirq pid cpu command\n"); 282 | seq_printf(m, "%9li %3li %i %6i %i %s\n", 283 | j, 0L, in_interrupt() ? 1 : 0, 284 | current->pid, smp_processor_id(), current->comm); 285 | 286 | /* fill the data for our tasklet function */ 287 | data->prevjiffies = j; 288 | data->m = m; 289 | data->loops = JIT_ASYNC_LOOPS; 290 | 291 | /* register the tasklet */ 292 | tasklet_init(&data->tlet, jit_tasklet_fn, (unsigned long)data); 293 | data->hi = hi; 294 | if (hi) 295 | tasklet_hi_schedule(&data->tlet); 296 | else 297 | tasklet_schedule(&data->tlet); 298 | 299 | /* wait for the buffer to fill */ 300 | wait_event_interruptible(data->wait, !data->loops); 301 | 302 | if (signal_pending(current)) 303 | return -ERESTARTSYS; 304 | kfree(data); 305 | return 0; 306 | } 307 | 308 | static int jit_tasklet_open(struct inode *inode, struct file *file) 309 | { 310 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0) 311 | return single_open(file, jit_tasklet_show, PDE_DATA(inode)); 312 | #else 313 | return single_open(file, jit_tasklet_show, pde_data(inode)); 314 | #endif 315 | } 316 | 317 | static const struct file_operations jit_tasklet_fops = { 318 | .open = jit_tasklet_open, 319 | .read = seq_read, 320 | .llseek = seq_lseek, 321 | .release = single_release, 322 | }; 323 | 324 | int __init jit_init(void) 325 | { 326 | proc_create("currentime", 0, NULL, 327 | proc_ops_wrapper(&jit_currentime_fops, jit_currentime_pops)); 328 | proc_create_data("jitbusy", 0, NULL, 329 | proc_ops_wrapper(&jit_fn_fops, jit_fn_pops), (void *)JIT_BUSY); 330 | proc_create_data("jitsched", 0, NULL, 331 | proc_ops_wrapper(&jit_fn_fops, jit_fn_pops), (void *)JIT_SCHED); 332 | proc_create_data("jitqueue", 0, NULL, 333 | proc_ops_wrapper(&jit_fn_fops, jit_fn_pops), (void *)JIT_QUEUE); 334 | proc_create_data("jitschedto", 0, NULL, 335 | proc_ops_wrapper(&jit_fn_fops, jit_fn_pops), (void *)JIT_SCHEDTO); 336 | 337 | proc_create("jitimer", 0, NULL, 338 | proc_ops_wrapper(&jit_timer_fops, jit_timer_pops)); 339 | proc_create("jitasklet", 0, NULL, 340 | proc_ops_wrapper(&jit_tasklet_fops, jit_tasklet_pops)); 341 | proc_create_data("jitasklethi", 0, NULL, 342 | proc_ops_wrapper(&jit_tasklet_fops, jit_tasklet_pops), (void *)1); 343 | 344 | return 0; /* success */ 345 | } 346 | 347 | void __exit jit_cleanup(void) 348 | { 349 | remove_proc_entry("currentime", NULL); 350 | remove_proc_entry("jitbusy", NULL); 351 | remove_proc_entry("jitsched", NULL); 352 | remove_proc_entry("jitqueue", NULL); 353 | remove_proc_entry("jitschedto", NULL); 354 | 355 | remove_proc_entry("jitimer", NULL); 356 | remove_proc_entry("jitasklet", NULL); 357 | remove_proc_entry("jitasklethi", NULL); 358 | } 359 | 360 | module_init(jit_init); 361 | module_exit(jit_cleanup); 362 | -------------------------------------------------------------------------------- /misc-modules/kdataalign.c: -------------------------------------------------------------------------------- 1 | /* 2 | * kdatasize.c -- print the size of common data items from kernel space 3 | * This runs with any Linux kernel (not any Unix, because of ) 4 | * 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | /* 24 | * Define several data structures, all of them start with a lone char 25 | * in order to present an unaligned offset for the next field 26 | */ 27 | struct c {char c; char t;} c; 28 | struct s {char c; short t;} s; 29 | struct i {char c; int t;} i; 30 | struct l {char c; long t;} l; 31 | struct ll {char c; long long t;} ll; 32 | struct p {char c; void * t;} p; 33 | struct u1b {char c; __u8 t;} u1b; 34 | struct u2b {char c; __u16 t;} u2b; 35 | struct u4b {char c; __u32 t;} u4b; 36 | struct u8b {char c; __u64 t;} u8b; 37 | 38 | static void data_cleanup(void) 39 | { 40 | /* never called */ 41 | } 42 | 43 | static int data_init(void) 44 | { 45 | /* print information and return an error */ 46 | printk("arch Align: char short int long ptr long-long " 47 | " u8 u16 u32 u64\n"); 48 | printk("%-12s %3i %3i %3i %3i %3i %3i " 49 | "%3i %3i %3i %3i\n", 50 | init_uts_ns.name.machine, 51 | /* note that gcc can subtract void * values, but it's not ansi */ 52 | (int)((void *)(&c.t) - (void *)&c), 53 | (int)((void *)(&s.t) - (void *)&s), 54 | (int)((void *)(&i.t) - (void *)&i), 55 | (int)((void *)(&l.t) - (void *)&l), 56 | (int)((void *)(&p.t) - (void *)&p), 57 | (int)((void *)(&ll.t) - (void *)&ll), 58 | (int)((void *)(&u1b.t) - (void *)&u1b), 59 | (int)((void *)(&u2b.t) - (void *)&u2b), 60 | (int)((void *)(&u4b.t) - (void *)&u4b), 61 | (int)((void *)(&u8b.t) - (void *)&u8b)); 62 | return -ENODEV; 63 | } 64 | 65 | module_init(data_init); 66 | module_exit(data_cleanup); 67 | 68 | MODULE_LICENSE("Dual BSD/GPL"); 69 | -------------------------------------------------------------------------------- /misc-modules/kdatasize.c: -------------------------------------------------------------------------------- 1 | /* 2 | * kdatasize.c -- print the size of common data items from kernel space 3 | * This runs with any Linux kernel (not any Unix, because of ) 4 | * 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | */ 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | static void data_cleanup(void) 25 | { 26 | /* never called */ 27 | } 28 | 29 | int data_init(void) 30 | { 31 | /* print information and return an error */ 32 | printk("arch Size: char short int long ptr long-long " 33 | " u8 u16 u32 u64\n"); 34 | printk("%-12s %3i %3i %3i %3i %3i %3i " 35 | "%3i %3i %3i %3i\n", 36 | init_uts_ns.name.machine, 37 | (int)sizeof(char), (int)sizeof(short), (int)sizeof(int), 38 | (int)sizeof(long), 39 | (int)sizeof(void *), (int)sizeof(long long), (int)sizeof(__u8), 40 | (int)sizeof(__u16), (int)sizeof(__u32), (int)sizeof(__u64)); 41 | return -ENODEV; 42 | } 43 | 44 | module_init(data_init); 45 | module_exit(data_cleanup); 46 | 47 | MODULE_LICENSE("Dual BSD/GPL"); 48 | -------------------------------------------------------------------------------- /misc-modules/module_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module=$1 3 | # Use the same name for the device as the name used for the module 4 | device=$1 5 | # Support read/write for owner and group, read only for everyone using 644 6 | mode="664" 7 | 8 | if [ $# -ne 1 ]; then 9 | echo "Wrong number of arguments" 10 | echo "usage: $0 module_name" 11 | echo "Will create a corresponding device /dev/module_name associated with module_name.ko" 12 | exit 1 13 | fi 14 | 15 | set -e 16 | # Group: since distributions do it differently, look for wheel or use staff 17 | # These are groups which correspond to system administrator accounts 18 | if grep -q '^staff:' /etc/group; then 19 | group="staff" 20 | else 21 | group="wheel" 22 | fi 23 | 24 | # Discard the module name before passing arguments to insmod 25 | shift 26 | 27 | echo "Load our module, exit on failure" 28 | insmod ./$module.ko $* || exit 1 29 | echo "Get the major number (allocated with allocate_chrdev_region) from /proc/devices" 30 | major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices) 31 | if [ ! -z ${major} ]; then 32 | echo "Remove any existing /dev node for /dev/${device}" 33 | rm -f /dev/${device} 34 | echo "Add a node for our device at /dev/${device} using mknod" 35 | mknod /dev/${device} c $major 0 36 | echo "Change group owner to ${group}" 37 | chgrp $group /dev/${device} 38 | echo "Change access mode to ${mode}" 39 | chmod $mode /dev/${device} 40 | else 41 | echo "No device found in /proc/devices for driver ${module} (this driver may not allocate a device)" 42 | fi 43 | -------------------------------------------------------------------------------- /misc-modules/module_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module=$1 3 | device=$1 4 | 5 | if [ $# -ne 1 ]; then 6 | echo "Wrong number of arguments" 7 | echo "usage: $0 module_name" 8 | echo "Will unload the module specified by module_name and remove assocaited device" 9 | exit 1 10 | fi 11 | 12 | # invoke rmmod with all arguments we got 13 | rmmod $module || exit 1 14 | 15 | # Remove stale nodes 16 | 17 | rm -f /dev/${device} 18 | -------------------------------------------------------------------------------- /misc-modules/seq.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple demonstration of the seq_file interface. 3 | * 4 | * seq.c,v 1.3 2004/09/26 07:02:43 gregkh Exp 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "proc_ops_version.h" 15 | 16 | MODULE_AUTHOR("Jonathan Corbet"); 17 | MODULE_LICENSE("Dual BSD/GPL"); 18 | 19 | 20 | 21 | /* 22 | * The sequence iterator functions. The position as seen by the 23 | * filesystem is just the count that we return. 24 | */ 25 | static void *ct_seq_start(struct seq_file *s, loff_t *pos) 26 | { 27 | loff_t *spos = kmalloc(sizeof(loff_t), GFP_KERNEL); 28 | if (!spos) 29 | return NULL; 30 | *spos = *pos; 31 | return spos; 32 | } 33 | 34 | static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) 35 | { 36 | loff_t *spos = (loff_t *) v; 37 | *pos = ++(*spos); 38 | return spos; 39 | } 40 | 41 | static void ct_seq_stop(struct seq_file *s, void *v) 42 | { 43 | kfree(v); 44 | } 45 | 46 | /* 47 | * The show function. 48 | */ 49 | static int ct_seq_show(struct seq_file *s, void *v) 50 | { 51 | loff_t *spos = (loff_t *) v; 52 | seq_printf(s, "%lld\n", *spos); 53 | return 0; 54 | } 55 | 56 | /* 57 | * Tie them all together into a set of seq_operations. 58 | */ 59 | static const struct seq_operations ct_seq_ops = { 60 | .start = ct_seq_start, 61 | .next = ct_seq_next, 62 | .stop = ct_seq_stop, 63 | .show = ct_seq_show 64 | }; 65 | 66 | 67 | /* 68 | * Time to set up the file operations for our /proc file. In this case, 69 | * all we need is an open function which sets up the sequence ops. 70 | */ 71 | 72 | static int ct_open(struct inode *inode, struct file *file) 73 | { 74 | return seq_open(file, &ct_seq_ops); 75 | }; 76 | 77 | /* 78 | * The file operations structure contains our open function along with 79 | * set of the canned seq_ ops. 80 | */ 81 | static const struct file_operations ct_file_ops = { 82 | .owner = THIS_MODULE, 83 | .open = ct_open, 84 | .read = seq_read, 85 | .llseek = seq_lseek, 86 | .release = seq_release 87 | }; 88 | 89 | 90 | /* 91 | * Module setup and teardown. 92 | */ 93 | 94 | static int ct_init(void) 95 | { 96 | struct proc_dir_entry *entry; 97 | 98 | entry = proc_create("sequence", 0, NULL, 99 | proc_ops_wrapper(&ct_file_ops, ct_file_pops)); 100 | if (!entry) 101 | return -ENOMEM; 102 | return 0; 103 | } 104 | 105 | static void ct_exit(void) 106 | { 107 | remove_proc_entry("sequence", NULL); 108 | } 109 | 110 | module_init(ct_init); 111 | module_exit(ct_exit); 112 | -------------------------------------------------------------------------------- /misc-modules/silly.c: -------------------------------------------------------------------------------- 1 | /* 2 | * silly.c -- Simple Tool for Unloading and Printing ISA Data 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: silly.c,v 1.3 2004/09/26 07:02:43 gregkh Exp $ 16 | */ 17 | 18 | /* =========================> BIG FAT WARNING: 19 | * This will only work on architectures with an ISA memory range. 20 | * It won't work on other computers. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include /* printk() */ 30 | #include /* everything... */ 31 | #include /* error codes */ 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | 40 | int silly_major = 0; 41 | module_param(silly_major, int, 0); 42 | MODULE_AUTHOR("Alessandro Rubini"); 43 | MODULE_LICENSE("Dual BSD/GPL"); 44 | 45 | /* 46 | * The devices access the 640k-1M memory. 47 | * minor 0 uses ioread8/iowrite8 48 | * minor 1 uses ioread16/iowrite16 49 | * minor 2 uses ioread32/iowrite32 50 | * minor 3 uses memcpy_fromio()/memcpy_toio() 51 | */ 52 | 53 | /* 54 | * Here's our address range, and a place to store the ioremap'd base. 55 | */ 56 | #define ISA_BASE 0xA0000 57 | #define ISA_MAX 0x100000 /* for general memory access */ 58 | 59 | #define VIDEO_MAX 0xC0000 /* for vga access */ 60 | #define VGA_BASE 0xb8000 61 | static void __iomem *io_base; 62 | 63 | 64 | 65 | int silly_open(struct inode *inode, struct file *filp) 66 | { 67 | return 0; 68 | } 69 | 70 | int silly_release(struct inode *inode, struct file *filp) 71 | { 72 | return 0; 73 | } 74 | 75 | enum silly_modes {M_8=0, M_16, M_32, M_memcpy}; 76 | 77 | ssize_t silly_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) 78 | { 79 | int retval; 80 | int mode = iminor(file_dentry(filp)->d_inode); 81 | void __iomem *add; 82 | unsigned long isa_addr = ISA_BASE + *f_pos; 83 | unsigned char *kbuf, *ptr; 84 | 85 | if (isa_addr + count > ISA_MAX) /* range: 0xA0000-0x100000 */ 86 | count = ISA_MAX - isa_addr; 87 | 88 | /* 89 | * too big an f_pos (caused by a malicious lseek()) 90 | * would result in a negative count 91 | */ 92 | if (count < 0) 93 | return 0; 94 | 95 | kbuf = kmalloc(count, GFP_KERNEL); 96 | if (!kbuf) 97 | return -ENOMEM; 98 | ptr = kbuf; 99 | retval = count; 100 | /* 101 | * Convert our address into our remapped area. 102 | */ 103 | add = (void __iomem *)(io_base + (isa_addr - ISA_BASE)); 104 | /* 105 | * kbuf is aligned, but the reads might not. In order not to 106 | * drive me mad with unaligned leading and trailing bytes, 107 | * I downgrade the `mode' if unaligned xfers are requested. 108 | */ 109 | 110 | if (mode == M_32 && ((isa_addr | count) & 3)) 111 | mode = M_16; 112 | if (mode == M_16 && ((isa_addr | count) & 1)) 113 | mode = M_8; 114 | 115 | switch(mode) { 116 | case M_32: 117 | while (count >= 4) { 118 | *(u32 *)ptr = ioread32(add); 119 | add += 4; 120 | count -= 4; 121 | ptr += 4; 122 | } 123 | break; 124 | 125 | case M_16: 126 | while (count >= 2) { 127 | *(u16 *)ptr = ioread16(add); 128 | add+=2; 129 | count-=2; 130 | ptr+=2; 131 | } 132 | break; 133 | 134 | case M_8: 135 | while (count) { 136 | *ptr = ioread8(add); 137 | add++; 138 | count--; 139 | ptr++; 140 | } 141 | break; 142 | 143 | case M_memcpy: 144 | memcpy_fromio(ptr, add, count); 145 | break; 146 | 147 | default: 148 | return -EINVAL; 149 | } 150 | if ((retval > 0) && copy_to_user(buf, kbuf, retval)) 151 | retval = -EFAULT; 152 | kfree(kbuf); 153 | *f_pos += retval; 154 | return retval; 155 | } 156 | 157 | 158 | ssize_t silly_write(struct file *filp, const char __user *buf, size_t count, 159 | loff_t *f_pos) 160 | { 161 | int retval; 162 | int mode = iminor(file_dentry(filp)->d_inode); 163 | unsigned long isa_addr = ISA_BASE + *f_pos; 164 | unsigned char *kbuf, *ptr; 165 | void __iomem *add; 166 | 167 | /* 168 | * Writing is dangerous. 169 | * Allow root-only, independently of device permissions 170 | */ 171 | if (!capable(CAP_SYS_RAWIO)) 172 | return -EPERM; 173 | 174 | if (isa_addr + count > ISA_MAX) /* range: 0xA0000-0x100000 */ 175 | count = ISA_MAX - isa_addr; 176 | 177 | /* 178 | * too big an f_pos (caused by a malicious lseek()) 179 | * results in a negative count 180 | */ 181 | if (count < 0) 182 | return 0; 183 | 184 | kbuf = kmalloc(count, GFP_KERNEL); 185 | if (!kbuf) 186 | return -ENOMEM; 187 | ptr = kbuf; 188 | retval=count; 189 | 190 | /* 191 | * kbuf is aligned, but the writes might not. In order not to 192 | * drive me mad with unaligned leading and trailing bytes, 193 | * I downgrade the `mode' if unaligned xfers are requested. 194 | */ 195 | 196 | if (mode == M_32 && ((isa_addr | count) & 3)) 197 | mode = M_16; 198 | if (mode == M_16 && ((isa_addr | count) & 1)) 199 | mode = M_8; 200 | 201 | if (copy_from_user(kbuf, buf, count)) { 202 | kfree(kbuf); 203 | return -EFAULT; 204 | } 205 | ptr = kbuf; 206 | 207 | /* 208 | * Switch over to our remapped address space. 209 | */ 210 | add = (void __iomem *)(io_base + (isa_addr - ISA_BASE)); 211 | 212 | switch(mode) { 213 | case M_32: 214 | while (count >= 4) { 215 | iowrite8(*(u32 *)ptr, add); 216 | add += 4; 217 | count -= 4; 218 | ptr += 4; 219 | } 220 | break; 221 | 222 | case M_16: 223 | while (count >= 2) { 224 | iowrite8(*(u16 *)ptr, add); 225 | add += 2; 226 | count -= 2; 227 | ptr += 2; 228 | } 229 | break; 230 | 231 | case M_8: 232 | while (count) { 233 | iowrite8(*ptr, add); 234 | add++; 235 | count--; 236 | ptr++; 237 | } 238 | break; 239 | 240 | case M_memcpy: 241 | memcpy_toio(add, ptr, count); 242 | break; 243 | 244 | default: 245 | return -EINVAL; 246 | } 247 | *f_pos += retval; 248 | kfree(kbuf); 249 | return retval; 250 | } 251 | 252 | 253 | unsigned int silly_poll(struct file *filp, poll_table *wait) 254 | { 255 | return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; 256 | } 257 | 258 | 259 | struct file_operations silly_fops = { 260 | .read = silly_read, 261 | .write = silly_write, 262 | .poll = silly_poll, 263 | .open = silly_open, 264 | .release = silly_release, 265 | .owner = THIS_MODULE 266 | }; 267 | 268 | int silly_init(void) 269 | { 270 | int result = register_chrdev(silly_major, "silly", &silly_fops); 271 | if (result < 0) { 272 | printk(KERN_INFO "silly: can't get major number\n"); 273 | return result; 274 | } 275 | if (silly_major == 0) 276 | silly_major = result; /* dynamic */ 277 | /* 278 | * Set up our I/O range. 279 | */ 280 | 281 | /* this line appears in silly_init */ 282 | io_base = ioremap(ISA_BASE, ISA_MAX - ISA_BASE); 283 | return 0; 284 | } 285 | 286 | void silly_cleanup(void) 287 | { 288 | iounmap(io_base); 289 | unregister_chrdev(silly_major, "silly"); 290 | } 291 | 292 | 293 | module_init(silly_init); 294 | module_exit(silly_cleanup); 295 | -------------------------------------------------------------------------------- /misc-modules/sleepy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sleepy.c -- the writers awake the readers 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: sleepy.c,v 1.7 2004/09/26 07:02:43 gregkh Exp $ 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include /* current and everything */ 22 | #include /* printk() */ 23 | #include /* everything... */ 24 | #include /* size_t */ 25 | #include 26 | 27 | MODULE_LICENSE("Dual BSD/GPL"); 28 | 29 | static int sleepy_major = 0; 30 | 31 | static DECLARE_WAIT_QUEUE_HEAD(wq); 32 | static int flag = 0; 33 | 34 | static ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos) 35 | { 36 | printk(KERN_DEBUG "process %i (%s) going to sleep\n", 37 | current->pid, current->comm); 38 | wait_event_interruptible(wq, flag != 0); 39 | flag = 0; 40 | printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); 41 | return 0; /* EOF */ 42 | } 43 | 44 | static ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count, 45 | loff_t *pos) 46 | { 47 | printk(KERN_DEBUG "process %i (%s) awakening the readers...\n", 48 | current->pid, current->comm); 49 | flag = 1; 50 | wake_up_interruptible(&wq); 51 | return count; /* succeed, to avoid retrial */ 52 | } 53 | 54 | 55 | struct file_operations sleepy_fops = { 56 | .owner = THIS_MODULE, 57 | .read = sleepy_read, 58 | .write = sleepy_write, 59 | }; 60 | 61 | 62 | static int sleepy_init(void) 63 | { 64 | int result; 65 | 66 | /* 67 | * Register your major, and accept a dynamic number 68 | */ 69 | result = register_chrdev(sleepy_major, "sleepy", &sleepy_fops); 70 | if (result < 0) 71 | return result; 72 | if (sleepy_major == 0) 73 | sleepy_major = result; /* dynamic */ 74 | return 0; 75 | } 76 | 77 | static void sleepy_cleanup(void) 78 | { 79 | unregister_chrdev(sleepy_major, "sleepy"); 80 | } 81 | 82 | module_init(sleepy_init); 83 | module_exit(sleepy_cleanup); 84 | 85 | -------------------------------------------------------------------------------- /misc-progs/Makefile: -------------------------------------------------------------------------------- 1 | 2 | FILES = asynctest nbtest load50 mapcmp polltest mapper setlevel setconsole inp outp \ 3 | datasize dataalign netifdebug 4 | 5 | CFLAGS = -O2 -fomit-frame-pointer -Wall 6 | 7 | all: $(FILES) 8 | 9 | clean: 10 | rm -f $(FILES) *~ core 11 | 12 | -------------------------------------------------------------------------------- /misc-progs/asynctest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * asynctest.c: use async notification to read stdin 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | int gotdata=0; 24 | void sighandler(int signo) 25 | { 26 | if (signo==SIGIO) 27 | gotdata++; 28 | return; 29 | } 30 | 31 | char buffer[4096]; 32 | 33 | int main(int argc, char **argv) 34 | { 35 | int count; 36 | struct sigaction action; 37 | 38 | memset(&action, 0, sizeof(action)); 39 | action.sa_handler = sighandler; 40 | action.sa_flags = 0; 41 | 42 | sigaction(SIGIO, &action, NULL); 43 | 44 | fcntl(STDIN_FILENO, F_SETOWN, getpid()); 45 | fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | FASYNC); 46 | 47 | while(1) { 48 | /* this only returns if a signal arrives */ 49 | sleep(86400); /* one day */ 50 | if (!gotdata) 51 | continue; 52 | count=read(0, buffer, 4096); 53 | /* buggy: if avail data is more than 4kbytes... */ 54 | count=write(1,buffer,count); 55 | gotdata=0; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /misc-progs/dataalign.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dataalign.c -- show alignment needs 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * This runs with any Linux kernel (not any Unix, because of ) 16 | */ 17 | #include 18 | #include 19 | #include 20 | 21 | /* 22 | * Define several data structures, all of them start with a lone char 23 | * in order to present an unaligned offset for the next field 24 | */ 25 | struct c {char c; char t;} c; 26 | struct s {char c; short t;} s; 27 | struct i {char c; int t;} i; 28 | struct l {char c; long t;} l; 29 | struct ll {char c; long long t;} ll; 30 | struct p {char c; void * t;} p; 31 | struct u1b {char c; __u8 t;} u1b; 32 | struct u2b {char c; __u16 t;} u2b; 33 | struct u4b {char c; __u32 t;} u4b; 34 | struct u8b {char c; __u64 t;} u8b; 35 | 36 | int main(int argc, char **argv) 37 | { 38 | struct utsname name; 39 | 40 | uname(&name); /* never fails :) */ 41 | printf("arch Align: char short int long ptr long-long " 42 | " u8 u16 u32 u64\n"); 43 | printf( "%-12s %3i %3i %3i %3i %3i %3i " 44 | "%3i %3i %3i %3i\n", 45 | name.machine, 46 | /* note that gcc can subtract void * values, but it's not ansi */ 47 | (int)((void *)(&c.t) - (void *)&c), 48 | (int)((void *)(&s.t) - (void *)&s), 49 | (int)((void *)(&i.t) - (void *)&i), 50 | (int)((void *)(&l.t) - (void *)&l), 51 | (int)((void *)(&p.t) - (void *)&p), 52 | (int)((void *)(&ll.t) - (void *)&ll), 53 | (int)((void *)(&u1b.t) - (void *)&u1b), 54 | (int)((void *)(&u2b.t) - (void *)&u2b), 55 | (int)((void *)(&u4b.t) - (void *)&u4b), 56 | (int)((void *)(&u8b.t) - (void *)&u8b)); 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /misc-progs/datasize.c: -------------------------------------------------------------------------------- 1 | /* 2 | * datasize.c -- print the size of common data items 3 | * This runs with any Linux kernel (not any Unix, because of ) 4 | * 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | */ 16 | #include 17 | #include 18 | #include 19 | 20 | int main(int argc, char **argv) 21 | { 22 | struct utsname name; 23 | 24 | uname(&name); /* never fails :) */ 25 | printf("arch Size: char short int long ptr long-long " 26 | " u8 u16 u32 u64\n"); 27 | printf( "%-12s %3i %3i %3i %3i %3i %3i " 28 | "%3i %3i %3i %3i\n", 29 | name.machine, 30 | (int)sizeof(char), (int)sizeof(short), (int)sizeof(int), 31 | (int)sizeof(long), 32 | (int)sizeof(void *), (int)sizeof(long long), (int)sizeof(__u8), 33 | (int)sizeof(__u16), (int)sizeof(__u32), (int)sizeof(__u64)); 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /misc-progs/gdbline: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # $Id: gdbline,v 1.1 2004/08/02 16:27:55 corbet Exp $ 4 | # 5 | # gdbline module image 6 | # 7 | # Outputs an add-symbol-file line suitable for pasting into gdb to examine 8 | # a loaded module. 9 | # 10 | cd /sys/module/$1/sections 11 | echo -n add-symbol-file $2 `/bin/cat .text` 12 | 13 | for section in .[a-z]* *; do 14 | if [ $section != ".text" ]; then 15 | echo " \\" 16 | echo -n " -s" $section `/bin/cat $section` 17 | fi 18 | done 19 | echo 20 | -------------------------------------------------------------------------------- /misc-progs/inp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * inp.c -- read all the ports specified in hex on the command line. 3 | * The program uses the faster ioperm/iopl calls on x86, /dev/port 4 | * on other platforms. The program acts as inb/inw/inl according 5 | * to its own name 6 | * 7 | * Copyright (C) 1998,2000,2001 Alessandro Rubini 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include /* linux-specific */ 34 | 35 | #ifdef __GLIBC__ 36 | # include 37 | #endif 38 | 39 | #define PORT_FILE "/dev/port" 40 | 41 | char *prgname; 42 | 43 | #ifdef __i386__ 44 | static int read_and_print_one(unsigned int port,int size) 45 | { 46 | static int iopldone = 0; 47 | 48 | if (port > 1024) { 49 | if (!iopldone && iopl(3)) { 50 | fprintf(stderr, "%s: iopl(): %s\n", prgname, strerror(errno)); 51 | return 1; 52 | } 53 | iopldone++; 54 | } else if (ioperm(port,size,1)) { 55 | fprintf(stderr, "%s: ioperm(%x): %s\n", prgname, 56 | port, strerror(errno)); 57 | return 1; 58 | } 59 | 60 | if (size == 4) 61 | printf("%04x: %08x\n", port, inl(port)); 62 | else if (size == 2) 63 | printf("%04x: %04x\n", port, inw(port)); 64 | else 65 | printf("%04x: %02x\n", port, inb(port)); 66 | return 0; 67 | } 68 | #else /* not i386 */ 69 | 70 | static int read_and_print_one(unsigned int port,int size) 71 | { 72 | static int fd = -1; 73 | unsigned char b; unsigned short w; unsigned int l; 74 | 75 | if (fd < 0) 76 | fd = open(PORT_FILE, O_RDONLY); 77 | if (fd < 0) { 78 | fprintf(stderr, "%s: %s: %s\n", prgname, PORT_FILE, strerror(errno)); 79 | return 1; 80 | } 81 | lseek(fd, port, SEEK_SET); 82 | 83 | if (size == 4) { 84 | read(fd, &l, 4); 85 | printf("%04x: 0x%08x\n", port, l); 86 | } else if (size == 2) { 87 | read(fd, &w, 2); 88 | printf("%04x: 0x%04x\n", port, w & 0xffff); 89 | } else { 90 | read(fd, &b, 1); 91 | printf("%04x: 0x%02x\n", port, b & 0xff); 92 | } 93 | return 0; 94 | } 95 | 96 | #endif /* i386 */ 97 | 98 | 99 | int main(int argc, char **argv) 100 | { 101 | unsigned int i, n, port, size, error = 0; 102 | 103 | prgname = argv[0]; 104 | /* find the data size */ 105 | switch (prgname[strlen(prgname)-1]) { 106 | case 'w': size = 2; break; 107 | case 'l': size = 4; break; 108 | case 'b': case 'p': default: 109 | size = 1; 110 | } 111 | 112 | setuid(0); /* if we're setuid, force it on */ 113 | for (i = 1; i < argc; i++) { 114 | if ( sscanf(argv[i], "%x%n", &port, &n) < 1 115 | || n != strlen(argv[i]) ) { 116 | fprintf(stderr, "%s: argument \"%s\" is not a hex number\n", 117 | argv[0], argv[i]); 118 | error++; continue; 119 | } 120 | if (port & (size-1)) { 121 | fprintf(stderr, "%s: argument \"%s\" is not properly aligned\n", 122 | argv[0], argv[i]); 123 | error++; continue; 124 | } 125 | error += read_and_print_one(port, size); 126 | } 127 | exit (error ? 1 : 0); 128 | } 129 | 130 | -------------------------------------------------------------------------------- /misc-progs/load50.c: -------------------------------------------------------------------------------- 1 | /* 2 | * load50.c -- a simple busy-looping tool. 3 | * Obviously, this runs with any kernel and any Unix 4 | * 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | int main(int argc, char **argv) 22 | { 23 | int i, load=50; 24 | 25 | if (argc==2) { 26 | load=atoi(argv[1]); 27 | } 28 | printf("Bringing load to %i\n",load); 29 | 30 | for (i=0; i 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | static char *mapdev (const char *, unsigned long, unsigned long); 26 | #define PAGE_SIZE 4096 27 | 28 | /* 29 | * memcmp dev1 dev2 offset pages 30 | */ 31 | int main (int argc, char **argv) 32 | { 33 | unsigned long offset, size, i; 34 | char *addr1, *addr2; 35 | /* 36 | * Sanity check. 37 | */ 38 | if (argc != 5) 39 | { 40 | fprintf (stderr, "Usage: mapcmp dev1 dev2 offset pages\n"); 41 | exit (1); 42 | } 43 | /* 44 | * Map the two devices. 45 | */ 46 | offset = strtoul (argv[3], NULL, 16); 47 | size = atoi (argv[4])*PAGE_SIZE; 48 | printf ("Offset is 0x%lx\n", offset); 49 | addr1 = mapdev (argv[1], offset, size); 50 | addr2 = mapdev (argv[2], offset, size); 51 | /* 52 | * Do the comparison. 53 | */ 54 | printf ("Comparing..."); 55 | fflush (stdout); 56 | for (i = 0; i < size; i++) 57 | if (*addr1++ != *addr2++) 58 | { 59 | printf ("areas differ at byte %ld\n", i); 60 | exit (0); 61 | } 62 | printf ("areas are identical.\n"); 63 | exit (0); 64 | } 65 | 66 | 67 | 68 | static char *mapdev (const char *dev, unsigned long offset, 69 | unsigned long size) 70 | { 71 | char *addr; 72 | int fd = open (dev, O_RDONLY); 73 | 74 | if (fd < 0) 75 | { 76 | perror (dev); 77 | exit (1); 78 | } 79 | addr = mmap (0, size, PROT_READ, MAP_PRIVATE, fd, offset); 80 | if (addr == MAP_FAILED) 81 | { 82 | perror (dev); 83 | exit (1); 84 | } 85 | printf ("Mapped %s (%lu @ %lx) at %p\n", dev, size, offset, addr); 86 | return (addr); 87 | } 88 | -------------------------------------------------------------------------------- /misc-progs/mapper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mapper.c -- simple file that mmap()s a file region and prints it 3 | * 4 | * Copyright (C) 1998,2000,2001 Alessandro Rubini 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | int main(int argc, char **argv) 30 | { 31 | char *fname; 32 | FILE *f; 33 | unsigned long offset, len; 34 | void *address; 35 | 36 | if (argc !=4 37 | || sscanf(argv[2],"%li", &offset) != 1 38 | || sscanf(argv[3],"%li", &len) != 1) { 39 | fprintf(stderr, "%s: Usage \"%s \"\n", argv[0], 40 | argv[0]); 41 | exit(1); 42 | } 43 | /* the offset might be big (e.g., PCI devices), but conversion trims it */ 44 | if (offset == INT_MAX) { 45 | if (argv[2][1]=='x') 46 | sscanf(argv[2]+2, "%lx", &offset); 47 | else 48 | sscanf(argv[2], "%lu", &offset); 49 | } 50 | 51 | fname=argv[1]; 52 | 53 | if (!(f=fopen(fname,"r"))) { 54 | fprintf(stderr, "%s: %s: %s\n", argv[0], fname, strerror(errno)); 55 | exit(1); 56 | } 57 | 58 | address=mmap(0, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fileno(f), offset); 59 | 60 | if (address == (void *)-1) { 61 | fprintf(stderr,"%s: mmap(): %s\n",argv[0],strerror(errno)); 62 | exit(1); 63 | } 64 | fclose(f); 65 | fprintf(stderr, "mapped \"%s\" from %lu (0x%08lx) to %lu (0x%08lx)\n", 66 | fname, offset, offset, offset+len, offset+len); 67 | 68 | fwrite(address, 1, len, stdout); 69 | return 0; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /misc-progs/nbtest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * nbtest.c: read and write in non-blocking mode 3 | * This should run with any Unix 4 | * 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | char buffer[4096]; 24 | 25 | int main(int argc, char **argv) 26 | { 27 | int delay = 1, n, m = 0; 28 | 29 | if (argc > 1) 30 | delay=atoi(argv[1]); 31 | fcntl(0, F_SETFL, fcntl(0,F_GETFL) | O_NONBLOCK); /* stdin */ 32 | fcntl(1, F_SETFL, fcntl(1,F_GETFL) | O_NONBLOCK); /* stdout */ 33 | 34 | while (1) { 35 | n = read(0, buffer, 4096); 36 | if (n >= 0) 37 | m = write(1, buffer, n); 38 | if ((n < 0 || m < 0) && (errno != EAGAIN)) 39 | break; 40 | sleep(delay); 41 | } 42 | perror(n < 0 ? "stdin" : "stdout"); 43 | exit(1); 44 | } 45 | -------------------------------------------------------------------------------- /misc-progs/netifdebug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * netifdebug.c -- change the IFF_DEBUG flag of an interface 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | int main(int argc, char **argv) 28 | { 29 | int action = -1, sock; 30 | struct ifreq req; 31 | char *actname; 32 | 33 | if (argc < 2) { 34 | fprintf(stderr,"%s: usage is \"%s []\"\n", 35 | argv[0],argv[0]); 36 | exit(1); 37 | } 38 | if (argc==2) 39 | actname="tell"; 40 | else 41 | actname=argv[2]; 42 | 43 | /* a silly raw socket just for ioctl()ling it */ 44 | sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 45 | if (sock < 0) { 46 | fprintf(stderr, "%s: socket(): %s\n", argv[0],strerror(errno)); 47 | exit(1); 48 | } 49 | 50 | /* retrieve flags */ 51 | strcpy(req.ifr_name, argv[1]); 52 | if ( ioctl(sock, SIOCGIFFLAGS, &req) < 0) { 53 | fprintf(stderr, " %s: ioctl(SIOCGIFFLAGS): %s\n", 54 | argv[0],strerror(errno)); 55 | exit(1); 56 | } 57 | 58 | if (!strcmp(actname,"on") 59 | || !strcmp(actname,"+") 60 | || !strcmp(actname,"1")) 61 | action = IFF_DEBUG; 62 | 63 | if (!strcmp(actname,"off") 64 | || !strcmp(actname,"-") 65 | || !strcmp(actname,"0")) 66 | action = 0; 67 | 68 | if (!strcmp(actname,"tell") 69 | || actname[0]=='t') { 70 | printf("%s: debug is %s\n", argv[1], 71 | req.ifr_flags & IFF_DEBUG ? "on" : "off"); 72 | exit(0); 73 | } 74 | 75 | req.ifr_flags &= ~IFF_DEBUG; 76 | req.ifr_flags |= action; 77 | 78 | if ( ioctl(sock, SIOCSIFFLAGS, &req) < 0) { 79 | fprintf(stderr, " %s: ioctl(SIOCSIFFLAGS): %s\n", 80 | argv[0],strerror(errno)); 81 | exit(1); 82 | } 83 | exit(0); 84 | } 85 | -------------------------------------------------------------------------------- /misc-progs/outp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * outp.c -- write all the ports specified in hex on the command line. 3 | * The program uses the faster ioperm/iopl calls on x86, /dev/port 4 | * on other platforms. The program acts as outb/outw/outl according 5 | * to its own name 6 | * 7 | * Copyright (C) 1998,2000,2001 Alessandro Rubini 8 | * 9 | * This program is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 2 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program; if not, write to the Free Software 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include /* linux-specific */ 34 | 35 | #ifdef __GLIBC__ 36 | # include 37 | #endif 38 | 39 | #define PORT_FILE "/dev/port" 40 | 41 | char *prgname; 42 | 43 | #ifdef __i386__ 44 | static int write_one(unsigned int port, unsigned int val, int size) 45 | { 46 | static int iopldone = 0; 47 | 48 | if (port > 1024) { 49 | if (!iopldone && iopl(3)) { 50 | fprintf(stderr, "%s: iopl(): %s\n", prgname, strerror(errno)); 51 | return 1; 52 | } 53 | iopldone++; 54 | } else if (ioperm(port,size,1)) { 55 | fprintf(stderr, "%s: ioperm(%x): %s\n", prgname, 56 | port, strerror(errno)); 57 | return 1; 58 | } 59 | 60 | if (size == 4) 61 | outl(val, port); 62 | else if (size == 2) 63 | outw(val&0xffff, port); 64 | else 65 | outb(val&0xff, port); 66 | return 0; 67 | } 68 | #else /* not i386 */ 69 | 70 | static int write_one(unsigned int port, unsigned int val, int size) 71 | { 72 | static int fd = -1; 73 | unsigned char b; unsigned short w; 74 | 75 | if (fd < 0) 76 | fd = open(PORT_FILE, O_WRONLY); 77 | if (fd < 0) { 78 | fprintf(stderr, "%s: %s: %s\n", prgname, PORT_FILE, strerror(errno)); 79 | return 1; 80 | } 81 | lseek(fd, port, SEEK_SET); 82 | 83 | if (size == 4) { 84 | write(fd, &val, 4); 85 | } else if (size == 2) { 86 | w = val; 87 | write(fd, &w, 2); 88 | } else { 89 | b = val; 90 | write(fd, &b, 1); 91 | } 92 | return 0; 93 | } 94 | 95 | #endif /* i386 */ 96 | 97 | int main(int argc, char **argv) 98 | { 99 | unsigned int i, n, port, val, size, error = 0; 100 | 101 | prgname = argv[0]; 102 | /* find the data size */ 103 | switch (prgname[strlen(prgname)-1]) { 104 | case 'w': size = 2; break; 105 | case 'l': size = 4; break; 106 | case 'b': case 'p': default: 107 | size = 1; 108 | } 109 | setuid(0); /* if we're setuid, force it on */ 110 | for (i=1;i (size == 1 ? 0xff : 0xffff)) { 129 | fprintf(stderr, "%s: argument \"%s\" out of range\n", 130 | argv[0], argv[i+1]); 131 | error++; continue; 132 | } 133 | error += write_one(port, val, size); 134 | } 135 | exit (error ? 1 : 0); 136 | } 137 | -------------------------------------------------------------------------------- /misc-progs/polltest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Test out reading with poll() 3 | * This should run with any Unix 4 | * 5 | * Copyright (C) 2003 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2003 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | * 16 | * $Id: polltest.c,v 1.1 2003/02/07 18:01:38 corbet Exp $ 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | char buffer[4096]; 27 | 28 | int main(int argc, char **argv) 29 | { 30 | struct pollfd pfd; 31 | int n; 32 | 33 | fcntl(0, F_SETFL, fcntl(0,F_GETFL) | O_NONBLOCK); /* stdin */ 34 | pfd.fd = 0; /* stdin */ 35 | pfd.events = POLLIN; 36 | 37 | while (1) { 38 | n=read(0, buffer, 4096); 39 | if (n >= 0) 40 | write(1, buffer, n); 41 | n = poll(&pfd, 1, -1); 42 | if (n < 0) 43 | break; 44 | } 45 | perror( n<0 ? "stdin" : "stdout"); 46 | exit(1); 47 | } 48 | -------------------------------------------------------------------------------- /misc-progs/setconsole.c: -------------------------------------------------------------------------------- 1 | /* 2 | * setconsole.c -- choose a console to receive kernel messages 3 | * 4 | * Copyright (C) 1998,2000,2001 Alessandro Rubini 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | int main(int argc, char **argv) 29 | { 30 | char bytes[2] = {11,0}; /* 11 is the TIOCLINUX cmd number */ 31 | 32 | if (argc==2) bytes[1] = atoi(argv[1]); /* the chosen console */ 33 | else { 34 | fprintf(stderr, "%s: need a single arg\n",argv[0]); exit(1); 35 | } 36 | if (ioctl(STDIN_FILENO, TIOCLINUX, bytes)<0) { /* use stdin */ 37 | fprintf(stderr,"%s: ioctl(stdin, TIOCLINUX): %s\n", 38 | argv[0], strerror(errno)); 39 | exit(1); 40 | } 41 | exit(0); 42 | } 43 | -------------------------------------------------------------------------------- /misc-progs/setlevel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * setlevel.c -- choose a console_loglevel for the kernel 3 | * 4 | * Copyright (C) 1998,2000,2001 Alessandro Rubini 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | /* #include */ /* conflicting on the alpha */ 26 | #include 27 | #include 28 | 29 | int main(int argc, char **argv) 30 | { 31 | int level; 32 | 33 | if (argc == 2) { 34 | level = atoi(argv[1]); /* the chosen console */ 35 | } else { 36 | fprintf(stderr, "%s: need a single arg\n", argv[0]); exit(1); 37 | } 38 | 39 | if (klogctl(8, NULL, level) < 0) { 40 | fprintf(stderr, "%s: syslog(setlevel): %s\n", 41 | argv[0], strerror(errno)); 42 | exit(1); 43 | } 44 | exit(0); 45 | } 46 | -------------------------------------------------------------------------------- /misc-progs/test/.gitignore: -------------------------------------------------------------------------------- 1 | infile 2 | outfile 3 | -------------------------------------------------------------------------------- /misc-progs/test/nonblock.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | infile=infile 4 | delay=1 5 | outfile=outfile 6 | strace= 7 | 8 | function printusage 9 | { 10 | echo "Usage: $0 [-i infile] [-d delay ] [-o outfile] [-s]" 11 | echo " Writes outfile in non-blocking mode with the content of infile as it is updated" 12 | echo " polling for changes to the file after delay seconds" 13 | echo " When -s is specified, strace is used to trace the command" 14 | echo " device default is ${device}" 15 | echo " infile default is ${infile}" 16 | echo " delay default is ${delay} seconds" 17 | echo " See Linux Device Drivers 3rd Edition chapter 6 section \"Testing the Scullpipe Driver\"" 18 | echo " For example, to test non blocking IO on /dev/scullpipe for both read and write, you can:" 19 | echo " Open one terminal window and enter command ./misc-progs/test/nonblock.sh -o /dev/scullpipe -s" 20 | echo " Open a second terminal window and enter command ./misc-progs/test/nonblock.sh -i /dev/scullpipe -s" 21 | echo " Open a 3rd terminal window and enter command tail -f ./misc-progs/test/outfile" 22 | echo " Open a 4th terminal window and ender command echo \"Hello World!\" >> ./misc-progs/test/infile" 23 | echo " * You should see continuous polling at the rate specified by the delay argument on each" 24 | echo " endpoint." 25 | echo " * You should see the output moved from infile to outfile as each echo command is sent" 26 | echo " * When building with DEBUG=y in the makefile, you should see debug messages in kern.log" 27 | echo " indicating the utility nbtest read/wrote bytes successfully" 28 | } 29 | 30 | while getopts "i:o:d:sh" opt; do 31 | case ${opt} in 32 | i ) 33 | infile=$OPTARG 34 | ;; 35 | d ) 36 | delay=$OPTARG 37 | ;; 38 | o ) 39 | outfile=$OPTARG 40 | ;; 41 | s ) 42 | strace=strace 43 | ;; 44 | h ) 45 | printusage 46 | exit 0 47 | ;; 48 | 49 | \? ) 50 | echo "Invalid option $OPTARG" 1>&2 51 | printusage 52 | exit 1 53 | ;; 54 | : ) 55 | echo "Invalid option $OPTARG requires an argument" 1>&2 56 | printusage 57 | exit 1 58 | ;; 59 | esac 60 | done 61 | 62 | set -e 63 | cd `dirname $0` 64 | touch ${infile} 65 | echo "Reading content of ${infile} through non blocking test to ${outfile}" 66 | echo "${strace} ../nbtest ${delay} > ${outfile} < ${infile}" 67 | ${strace} ../nbtest ${delay} > ${outfile} < ${infile} 68 | -------------------------------------------------------------------------------- /pci/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := pci_skel.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | PWD := $(shell pwd) 5 | 6 | all: 7 | $(MAKE) -C $(KERNELDIR) M=$(PWD) 8 | 9 | clean: 10 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers built-in.a 11 | 12 | -------------------------------------------------------------------------------- /pci/pci_skel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | static struct pci_device_id ids[] = { 8 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_3), }, 9 | { 0, } 10 | }; 11 | MODULE_DEVICE_TABLE(pci, ids); 12 | 13 | static unsigned char skel_get_revision(struct pci_dev *dev) 14 | { 15 | u8 revision; 16 | 17 | pci_read_config_byte(dev, PCI_REVISION_ID, &revision); 18 | return revision; 19 | } 20 | 21 | static int probe(struct pci_dev *dev, const struct pci_device_id *id) 22 | { 23 | /* Do probing type stuff here. 24 | * Like calling request_region(); 25 | */ 26 | if(pci_enable_device(dev)) { 27 | dev_err(&dev->dev, "can't enable PCI device\n"); 28 | return -ENODEV; 29 | } 30 | 31 | if (skel_get_revision(dev) == 0x42) 32 | return -ENODEV; 33 | 34 | 35 | return 0; 36 | } 37 | 38 | static void remove(struct pci_dev *dev) 39 | { 40 | /* clean up any allocated resources and stuff here. 41 | * like call release_region(); 42 | */ 43 | } 44 | 45 | static struct pci_driver pci_driver = { 46 | .name = "pci_skel", 47 | .id_table = ids, 48 | .probe = probe, 49 | .remove = remove, 50 | }; 51 | 52 | static int __init pci_skel_init(void) 53 | { 54 | return pci_register_driver(&pci_driver); 55 | } 56 | 57 | static void __exit pci_skel_exit(void) 58 | { 59 | pci_unregister_driver(&pci_driver); 60 | } 61 | 62 | MODULE_LICENSE("GPL"); 63 | 64 | module_init(pci_skel_init); 65 | module_exit(pci_skel_exit); 66 | -------------------------------------------------------------------------------- /sbull/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | 5 | # Add your debugging flag (or not) to CFLAGS 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSBULL_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | EXTRA_CFLAGS += $(DEBFLAGS) 13 | # for case RM_SIMPLE fall through 14 | EXTRA_CFLAGS += -I.. -Wno-implicit-fallthrough 15 | 16 | ifneq ($(KERNELRELEASE),) 17 | # call from kernel build system 18 | 19 | obj-m := sbull.o 20 | 21 | else 22 | 23 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 24 | PWD := $(shell pwd) 25 | 26 | default: 27 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 28 | 29 | endif 30 | 31 | 32 | 33 | clean: 34 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers 35 | 36 | depend .depend dep: 37 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 38 | 39 | 40 | ifeq (.depend,$(wildcard .depend)) 41 | include .depend 42 | endif 43 | -------------------------------------------------------------------------------- /sbull/sbull.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * sbull.h -- definitions for the char module 4 | * 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | * 16 | */ 17 | 18 | 19 | #include 20 | 21 | /* Multiqueue only works on 2.4 */ 22 | #ifdef SBULL_MULTIQUEUE 23 | # warning "Multiqueue only works on 2.4 kernels" 24 | #endif 25 | 26 | /* 27 | * Macros to help debugging 28 | */ 29 | 30 | #undef PDEBUG /* undef it, just in case */ 31 | #ifdef SBULL_DEBUG 32 | # ifdef __KERNEL__ 33 | /* This one if debugging is on, and kernel space */ 34 | # define PDEBUG(fmt, args...) printk( KERN_DEBUG "sbull: " fmt, ## args) 35 | # else 36 | /* This one for user space */ 37 | # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) 38 | # endif 39 | #else 40 | # define PDEBUG(fmt, args...) /* not debugging: nothing */ 41 | #endif 42 | 43 | #undef PDEBUGG 44 | #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ 45 | 46 | 47 | #define SBULL_MAJOR 0 /* dynamic major by default */ 48 | #define SBULL_DEVS 2 /* two disks */ 49 | #define SBULL_RAHEAD 2 /* two sectors */ 50 | #define SBULL_SIZE 2048 /* two megs each */ 51 | #define SBULL_BLKSIZE 1024 /* 1k blocks */ 52 | #define SBULL_HARDSECT 512 /* 2.2 and 2.4 can used different values */ 53 | 54 | #define SBULLR_MAJOR 0 /* Dynamic major for raw device */ 55 | /* 56 | * The sbull device is removable: if it is left closed for more than 57 | * half a minute, it is removed. Thus use a usage count and a 58 | * kernel timer 59 | */ 60 | 61 | typedef struct Sbull_Dev { 62 | int size; 63 | int usage; 64 | struct timer_list timer; 65 | spinlock_t lock; 66 | u8 *data; 67 | #ifdef SBULL_MULTIQUEUE 68 | request_queue_t *queue; 69 | int busy; 70 | #endif 71 | } Sbull_Dev; 72 | -------------------------------------------------------------------------------- /sbull/sbull_load: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function make_minors { 4 | let part=1 5 | while (($part < $minors)); do 6 | let minor=$part+$2 7 | mknod $1$part b $major $minor 8 | let part=$part+1 9 | done 10 | } 11 | 12 | 13 | # FIXME: This isn't handling minors (partitions) at all. 14 | module="sbull" 15 | device="sbull" 16 | mode="664" 17 | chardevice="sbullr" 18 | minors=16 19 | 20 | # Group: since distributions do it differently, look for wheel or use staff 21 | if grep '^staff:' /etc/group > /dev/null; then 22 | group="staff" 23 | else 24 | group="wheel" 25 | fi 26 | 27 | # invoke insmod with all arguments we got 28 | # and use a pathname, as newer modutils don't look in . by default 29 | insmod ./$module.ko $* || exit 1 30 | 31 | major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` 32 | 33 | # Remove stale nodes and replace them, then give gid and perms 34 | 35 | rm -f /dev/${device}[a-d]* /dev/${device} 36 | 37 | mknod /dev/${device}a b $major 0 38 | make_minors /dev/${device}a 0 39 | mknod /dev/${device}b b $major 16 40 | make_minors /dev/${device}b 16 41 | mknod /dev/${device}c b $major 32 42 | make_minors /dev/${device}c 32 43 | mknod /dev/${device}d b $major 48 44 | make_minors /dev/${device}d 48 45 | ln -sf ${device}a /dev/${device} 46 | chgrp $group /dev/${device}[a-d]* 47 | chmod $mode /dev/${device}[a-d]* 48 | -------------------------------------------------------------------------------- /sbull/sbull_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="sbull" 3 | device="sbull" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # Remove stale nodes 9 | rm -f /dev/${device}[a-d]* /dev/${device} 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /scull-shared/scull-async.c: -------------------------------------------------------------------------------- 1 | /* 2 | * scull-async.c 3 | * 4 | * Copyright (C) 2019 Dan Walkes 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include /* printk() */ 21 | #include /* kmalloc() */ 22 | #include /* everything... */ 23 | #include /* error codes */ 24 | #include /* size_t */ 25 | #include 26 | #include 27 | #include /* O_ACCMODE */ 28 | #include 29 | #include 30 | #include /* iov_iter* */ 31 | 32 | 33 | /* 34 | * A simple asynchronous I/O implementation. 35 | */ 36 | 37 | struct async_work { 38 | struct delayed_work work; 39 | struct kiocb *iocb; 40 | struct iov_iter *tofrom; 41 | }; 42 | 43 | /* 44 | * "Complete" an asynchronous operation. 45 | */ 46 | static void scull_do_deferred_op(struct work_struct *work) 47 | { 48 | struct async_work *stuff = container_of(work, struct async_work, work.work); 49 | if( iov_iter_rw(stuff->tofrom) == WRITE ) { 50 | generic_file_write_iter(stuff->iocb, stuff->tofrom); 51 | } else { 52 | generic_file_read_iter(stuff->iocb, stuff->tofrom); 53 | } 54 | kfree(stuff); 55 | } 56 | 57 | 58 | static int scull_defer_op(struct kiocb *iocb, struct iov_iter *tofrom) 59 | { 60 | struct async_work *stuff; 61 | int result; 62 | /* Otherwise defer the completion for a few milliseconds. */ 63 | stuff = kmalloc (sizeof (*stuff), GFP_KERNEL); 64 | if (stuff == NULL) 65 | return result; /* No memory, just complete now */ 66 | stuff->iocb = iocb; 67 | INIT_DELAYED_WORK(&stuff->work, scull_do_deferred_op); 68 | schedule_delayed_work(&stuff->work, HZ/100); 69 | return -EIOCBQUEUED; 70 | } 71 | 72 | 73 | ssize_t scull_read_iter(struct kiocb *iocb, struct iov_iter *to) 74 | { 75 | /* If this is a synchronous IOCB, we return our status now. */ 76 | if (is_sync_kiocb(iocb)) { 77 | return generic_file_read_iter(iocb,to); 78 | } 79 | return scull_defer_op(iocb, to); 80 | } 81 | 82 | ssize_t scull_write_iter(struct kiocb *iocb, struct iov_iter *from) 83 | { 84 | if (is_sync_kiocb(iocb)) { 85 | return generic_file_write_iter(iocb,from); 86 | } 87 | return scull_defer_op(iocb, from); 88 | } 89 | -------------------------------------------------------------------------------- /scull-shared/scull-async.h: -------------------------------------------------------------------------------- 1 | /* 2 | * scull-async.h 3 | * 4 | * Copyright (C) 2019 Dan Walkes 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | */ 16 | 17 | #ifndef SCULL_SHARED_SCULL_ASYNC_H_ 18 | #define SCULL_SHARED_SCULL_ASYNC_H_ 19 | 20 | 21 | ssize_t scull_write_iter(struct kiocb *iocb, struct iov_iter *from); 22 | ssize_t scull_read_iter(struct kiocb *iocb, struct iov_iter *to); 23 | 24 | 25 | #endif /* SCULL_SHARED_SCULL_ASYNC_H_ */ 26 | -------------------------------------------------------------------------------- /scull/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | 5 | # Add your debugging flag (or not) to CFLAGS 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | LDDINC=$(PWD)/../include 13 | 14 | EXTRA_CFLAGS += $(DEBFLAGS) 15 | EXTRA_CFLAGS += -I$(LDDINC) 16 | 17 | ifneq ($(KERNELRELEASE),) 18 | # call from kernel build system 19 | 20 | scull-objs := main.o pipe.o access.o 21 | 22 | obj-m := scull.o 23 | 24 | else 25 | 26 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 27 | PWD := $(shell pwd) 28 | 29 | modules: 30 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 31 | 32 | endif 33 | 34 | 35 | 36 | clean: 37 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers 38 | 39 | depend .depend dep: 40 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 41 | 42 | 43 | ifeq (.depend,$(wildcard .depend)) 44 | include .depend 45 | endif 46 | -------------------------------------------------------------------------------- /scull/scull.h: -------------------------------------------------------------------------------- 1 | /* 2 | * scull.h -- definitions for the char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: scull.h,v 1.15 2004/11/04 17:51:18 rubini Exp $ 16 | */ 17 | 18 | #ifndef _SCULL_H_ 19 | #define _SCULL_H_ 20 | 21 | #include /* needed for the _IOW etc stuff used later */ 22 | 23 | /* 24 | * Macros to help debugging 25 | */ 26 | 27 | #undef PDEBUG /* undef it, just in case */ 28 | #ifdef SCULL_DEBUG 29 | # ifdef __KERNEL__ 30 | /* This one if debugging is on, and kernel space */ 31 | # define PDEBUG(fmt, args...) printk( KERN_DEBUG "scull: " fmt, ## args) 32 | # else 33 | /* This one for user space */ 34 | # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) 35 | # endif 36 | #else 37 | # define PDEBUG(fmt, args...) /* not debugging: nothing */ 38 | #endif 39 | 40 | #undef PDEBUGG 41 | #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ 42 | 43 | #ifndef SCULL_MAJOR 44 | #define SCULL_MAJOR 0 /* dynamic major by default */ 45 | #endif 46 | 47 | #ifndef SCULL_NR_DEVS 48 | #define SCULL_NR_DEVS 4 /* scull0 through scull3 */ 49 | #endif 50 | 51 | #ifndef SCULL_P_NR_DEVS 52 | #define SCULL_P_NR_DEVS 4 /* scullpipe0 through scullpipe3 */ 53 | #endif 54 | 55 | /* 56 | * The bare device is a variable-length region of memory. 57 | * Use a linked list of indirect blocks. 58 | * 59 | * "scull_dev->data" points to an array of pointers, each 60 | * pointer refers to a memory area of SCULL_QUANTUM bytes. 61 | * 62 | * The array (quantum-set) is SCULL_QSET long. 63 | */ 64 | #ifndef SCULL_QUANTUM 65 | #define SCULL_QUANTUM 4000 66 | #endif 67 | 68 | #ifndef SCULL_QSET 69 | #define SCULL_QSET 1000 70 | #endif 71 | 72 | /* 73 | * The pipe device is a simple circular buffer. Here its default size 74 | */ 75 | #ifndef SCULL_P_BUFFER 76 | #define SCULL_P_BUFFER 4000 77 | #endif 78 | 79 | /* 80 | * Representation of scull quantum sets. 81 | */ 82 | struct scull_qset { 83 | void **data; 84 | struct scull_qset *next; 85 | }; 86 | 87 | struct scull_dev { 88 | struct scull_qset *data; /* Pointer to first quantum set */ 89 | int quantum; /* the current quantum size */ 90 | int qset; /* the current array size */ 91 | unsigned long size; /* amount of data stored here */ 92 | unsigned int access_key; /* used by sculluid and scullpriv */ 93 | struct mutex lock; /* mutual exclusion semaphore */ 94 | struct cdev cdev; /* Char device structure */ 95 | }; 96 | 97 | /* 98 | * Split minors in two parts 99 | */ 100 | #define TYPE(minor) (((minor) >> 4) & 0xf) /* high nibble */ 101 | #define NUM(minor) ((minor) & 0xf) /* low nibble */ 102 | 103 | 104 | /* 105 | * The different configurable parameters 106 | */ 107 | extern int scull_major; /* main.c */ 108 | extern int scull_nr_devs; 109 | extern int scull_quantum; 110 | extern int scull_qset; 111 | 112 | extern int scull_p_buffer; /* pipe.c */ 113 | 114 | 115 | /* 116 | * Prototypes for shared functions 117 | */ 118 | 119 | int scull_p_init(dev_t dev); 120 | void scull_p_cleanup(void); 121 | int scull_access_init(dev_t dev); 122 | void scull_access_cleanup(void); 123 | 124 | int scull_trim(struct scull_dev *dev); 125 | 126 | ssize_t scull_read(struct file *filp, char __user *buf, size_t count, 127 | loff_t *f_pos); 128 | ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, 129 | loff_t *f_pos); 130 | loff_t scull_llseek(struct file *filp, loff_t off, int whence); 131 | long scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); 132 | 133 | 134 | /* 135 | * Ioctl definitions 136 | */ 137 | 138 | /* Use 'k' as magic number */ 139 | #define SCULL_IOC_MAGIC 'k' 140 | /* Please use a different 8-bit number in your code */ 141 | 142 | #define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0) 143 | 144 | /* 145 | * S means "Set" through a ptr, 146 | * T means "Tell" directly with the argument value 147 | * G means "Get": reply by setting through a pointer 148 | * Q means "Query": response is on the return value 149 | * X means "eXchange": switch G and S atomically 150 | * H means "sHift": switch T and Q atomically 151 | */ 152 | #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) 153 | #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) 154 | #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) 155 | #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) 156 | #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int) 157 | #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int) 158 | #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) 159 | #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) 160 | #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int) 161 | #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int) 162 | #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) 163 | #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12) 164 | 165 | /* 166 | * The other entities only have "Tell" and "Query", because they're 167 | * not printed in the book, and there's no need to have all six. 168 | * (The previous stuff was only there to show different ways to do it. 169 | */ 170 | #define SCULL_P_IOCTSIZE _IO(SCULL_IOC_MAGIC, 13) 171 | #define SCULL_P_IOCQSIZE _IO(SCULL_IOC_MAGIC, 14) 172 | /* ... more to come */ 173 | 174 | #define SCULL_IOC_MAXNR 14 175 | 176 | #endif /* _SCULL_H_ */ 177 | -------------------------------------------------------------------------------- /scull/scull.init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Sample init script for the scull driver module 3 | 4 | DEVICE="scull" 5 | SECTION="misc" 6 | 7 | # The list of filenames and minor numbers: $PREFIX is prefixed to all names 8 | PREFIX="scull" 9 | FILES=" 0 0 1 1 2 2 3 3 priv 16 10 | pipe0 32 pipe1 33 pipe2 34 pipe3 35 11 | single 48 uid 64 wuid 80" 12 | 13 | INSMOD=/sbin/insmod; # use /sbin/modprobe if you prefer 14 | 15 | function device_specific_post_load () { 16 | true; # fill at will 17 | } 18 | function device_specific_pre_unload () { 19 | true; # fill at will 20 | } 21 | 22 | # Everything below this line should work unchanged for any char device. 23 | # Obviously, however, no options on the command line: either in 24 | # /etc/${DEVICE}.conf or /etc/modules.conf (if modprobe is used) 25 | 26 | # Optional configuration file: format is 27 | # owner 28 | # group 29 | # mode 30 | # options 31 | CFG=/etc/${DEVICE}.conf 32 | 33 | # kernel version, used to look for modules 34 | KERNEL=`uname -r` 35 | 36 | #FIXME: it looks like there is no misc section. Where should it be? 37 | MODDIR="/lib/modules/${KERNEL}/kernel/drivers/${SECTION}" 38 | if [ ! -d $MODDIR ]; then MODDIR="/lib/modules/${KERNEL}/${SECTION}"; fi 39 | 40 | # Root or die 41 | if [ "$(id -u)" != "0" ] 42 | then 43 | echo "You must be root to load or unload kernel modules" 44 | exit 1 45 | fi 46 | 47 | # Read configuration file 48 | if [ -r $CFG ]; then 49 | OWNER=`awk "\\$1==\"owner\" {print \\$2}" $CFG` 50 | GROUP=`awk "\\$1==\"group\" {print \\$2}" $CFG` 51 | MODE=`awk "\\$1==\"mode\" {print \\$2}" $CFG` 52 | # The options string may include extra blanks or only blanks 53 | OPTIONS=`sed -n '/^options / s/options //p' $CFG` 54 | fi 55 | 56 | 57 | # Create device files 58 | function create_files () { 59 | cd /dev 60 | local devlist="" 61 | local file 62 | while true; do 63 | if [ $# -lt 2 ]; then break; fi 64 | file="${DEVICE}$1" 65 | mknod $file c $MAJOR $2 66 | devlist="$devlist $file" 67 | shift 2 68 | done 69 | if [ -n "$OWNER" ]; then chown $OWNER $devlist; fi 70 | if [ -n "$GROUP" ]; then chgrp $GROUP $devlist; fi 71 | if [ -n "$MODE" ]; then chmod $MODE $devlist; fi 72 | } 73 | 74 | # Remove device files 75 | function remove_files () { 76 | cd /dev 77 | local devlist="" 78 | local file 79 | while true; do 80 | if [ $# -lt 2 ]; then break; fi 81 | file="${DEVICE}$1" 82 | devlist="$devlist $file" 83 | shift 2 84 | done 85 | rm -f $devlist 86 | } 87 | 88 | # Load and create files 89 | function load_device () { 90 | 91 | if [ -f $MODDIR/$DEVICE.ko ]; then 92 | devpath=$MODDIR/$DEVICE.ko 93 | else if [ -f ./$DEVICE.ko ]; then 94 | devpath=./$DEVICE.ko 95 | else 96 | devpath=$DEVICE; # let insmod/modprobe guess 97 | fi; fi 98 | if [ "$devpath" != "$DEVICE" ]; then 99 | echo -n " (loading file $devpath)" 100 | fi 101 | 102 | if $INSMOD $devpath $OPTIONS; then 103 | MAJOR=`awk "\\$2==\"$DEVICE\" {print \\$1}" /proc/devices` 104 | remove_files $FILES 105 | create_files $FILES 106 | device_specific_post_load 107 | else 108 | echo " FAILED!" 109 | fi 110 | } 111 | 112 | # Unload and remove files 113 | function unload_device () { 114 | device_specific_pre_unload 115 | /sbin/rmmod $DEVICE 116 | remove_files $FILES 117 | } 118 | 119 | 120 | case "$1" in 121 | start) 122 | echo -n "Loading $DEVICE" 123 | load_device 124 | echo "." 125 | ;; 126 | stop) 127 | echo -n "Unloading $DEVICE" 128 | unload_device 129 | echo "." 130 | ;; 131 | force-reload|restart) 132 | echo -n "Reloading $DEVICE" 133 | unload_device 134 | load_device 135 | echo "." 136 | ;; 137 | *) 138 | echo "Usage: $0 {start|stop|restart|force-reload}" 139 | exit 1 140 | esac 141 | 142 | exit 0 143 | -------------------------------------------------------------------------------- /scull/scull_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # $Id: scull_load,v 1.4 2004/11/03 06:19:49 rubini Exp $ 3 | module="scull" 4 | device="scull" 5 | mode="664" 6 | 7 | # Group: since distributions do it differently, look for wheel or use staff 8 | if grep -q '^staff:' /etc/group; then 9 | group="staff" 10 | else 11 | group="wheel" 12 | fi 13 | 14 | # invoke insmod with all arguments we got 15 | # and use a pathname, as insmod doesn't look in . by default 16 | insmod ./$module.ko $* || exit 1 17 | 18 | # retrieve major number 19 | major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices) 20 | 21 | # Remove stale nodes and replace them, then give gid and perms 22 | # Usually the script is shorter, it's scull that has several devices in it. 23 | 24 | rm -f /dev/${device}[0-3] 25 | mknod /dev/${device}0 c $major 0 26 | mknod /dev/${device}1 c $major 1 27 | mknod /dev/${device}2 c $major 2 28 | mknod /dev/${device}3 c $major 3 29 | ln -sf ${device}0 /dev/${device} 30 | chgrp $group /dev/${device}[0-3] 31 | chmod $mode /dev/${device}[0-3] 32 | 33 | rm -f /dev/${device}pipe[0-3] 34 | mknod /dev/${device}pipe0 c $major 4 35 | mknod /dev/${device}pipe1 c $major 5 36 | mknod /dev/${device}pipe2 c $major 6 37 | mknod /dev/${device}pipe3 c $major 7 38 | ln -sf ${device}pipe0 /dev/${device}pipe 39 | chgrp $group /dev/${device}pipe[0-3] 40 | chmod $mode /dev/${device}pipe[0-3] 41 | 42 | rm -f /dev/${device}single 43 | mknod /dev/${device}single c $major 8 44 | chgrp $group /dev/${device}single 45 | chmod $mode /dev/${device}single 46 | 47 | rm -f /dev/${device}uid 48 | mknod /dev/${device}uid c $major 9 49 | chgrp $group /dev/${device}uid 50 | chmod $mode /dev/${device}uid 51 | 52 | rm -f /dev/${device}wuid 53 | mknod /dev/${device}wuid c $major 10 54 | chgrp $group /dev/${device}wuid 55 | chmod $mode /dev/${device}wuid 56 | 57 | rm -f /dev/${device}priv 58 | mknod /dev/${device}priv c $major 11 59 | chgrp $group /dev/${device}priv 60 | chmod $mode /dev/${device}priv 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /scull/scull_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="scull" 3 | device="scull" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # Remove stale nodes 9 | 10 | rm -f /dev/${device} /dev/${device}[0-3] 11 | rm -f /dev/${device}priv 12 | rm -f /dev/${device}pipe /dev/${device}pipe[0-3] 13 | rm -f /dev/${device}single 14 | rm -f /dev/${device}uid 15 | rm -f /dev/${device}wuid 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /scullc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Comment/uncomment the following line to enable/disable debugging 3 | #DEBUG = y 4 | 5 | 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSCULLC_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | LDDINC=$(PWD)/../include 13 | EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINC) 14 | 15 | TARGET = scullc 16 | 17 | ifneq ($(KERNELRELEASE),) 18 | 19 | scullc-objs := main.o scull-shared/scull-async.o 20 | 21 | obj-m := scullc.o 22 | 23 | else 24 | 25 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 26 | PWD := $(shell pwd) 27 | 28 | modules: 29 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 30 | 31 | endif 32 | 33 | 34 | install: 35 | install -d $(INSTALLDIR) 36 | install -c $(TARGET).o $(INSTALLDIR) 37 | 38 | clean: 39 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers scull-shared/scull-async.o 40 | 41 | 42 | depend .depend dep: 43 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 44 | 45 | ifeq (.depend,$(wildcard .depend)) 46 | include .depend 47 | endif 48 | -------------------------------------------------------------------------------- /scullc/mmap.c: -------------------------------------------------------------------------------- 1 | /* -*- C -*- 2 | * mmap.c -- memory mapping for the scullc char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ 16 | */ 17 | 18 | #include 19 | 20 | #include /* everything */ 21 | #include /* error codes */ 22 | #include 23 | 24 | #include "scullc.h" /* local definitions */ 25 | 26 | 27 | /* 28 | * open and close: just keep track of how many times the device is 29 | * mapped, to avoid releasing it. 30 | */ 31 | 32 | void scullc_vma_open(struct vm_area_struct *vma) 33 | { 34 | struct scullc_dev *dev = vma->vm_private_data; 35 | 36 | dev->vmas++; 37 | } 38 | 39 | void scullc_vma_close(struct vm_area_struct *vma) 40 | { 41 | struct scullc_dev *dev = vma->vm_private_data; 42 | 43 | dev->vmas--; 44 | } 45 | 46 | /* 47 | * The nopage method: the core of the file. It retrieves the 48 | * page required from the scullc device and returns it to the 49 | * user. The count for the page must be incremented, because 50 | * it is automatically decremented at page unmap. 51 | * 52 | * For this reason, "order" must be zero. Otherwise, only the first 53 | * page has its count incremented, and the allocating module must 54 | * release it as a whole block. Therefore, it isn't possible to map 55 | * pages from a multipage block: when they are unmapped, their count 56 | * is individually decreased, and would drop to 0. 57 | */ 58 | 59 | struct page *scullc_vma_nopage(struct vm_area_struct *vma, 60 | unsigned long address, int *type) 61 | { 62 | unsigned long offset; 63 | struct scullc_dev *ptr, *dev = vma->vm_private_data; 64 | struct page *page = NOPAGE_SIGBUS; 65 | void *pageptr = NULL; /* default to "missing" */ 66 | 67 | mutex_lock(&dev->lock); 68 | offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); 69 | if (offset >= dev->size) goto out; /* out of range */ 70 | 71 | /* 72 | * Now retrieve the scullc device from the list,then the page. 73 | * If the device has holes, the process receives a SIGBUS when 74 | * accessing the hole. 75 | */ 76 | offset >>= PAGE_SHIFT; /* offset is a number of pages */ 77 | for (ptr = dev; ptr && offset >= dev->qset;) { 78 | ptr = ptr->next; 79 | offset -= dev->qset; 80 | } 81 | if (ptr && ptr->data) pageptr = ptr->data[offset]; 82 | if (!pageptr) goto out; /* hole or end-of-file */ 83 | 84 | /* got it, now increment the count */ 85 | get_page(page); 86 | if (type) 87 | *type = VM_FAULT_MINOR; 88 | out: 89 | mutex_unlock(&dev->lock); 90 | return page; 91 | } 92 | 93 | 94 | 95 | struct vm_operations_struct scullc_vm_ops = { 96 | .open = scullc_vma_open, 97 | .close = scullc_vma_close, 98 | .nopage = scullc_vma_nopage, 99 | }; 100 | 101 | 102 | int scullc_mmap(struct file *filp, struct vm_area_struct *vma) 103 | { 104 | struct inode *inode = filp->f_dentry->d_inode; 105 | 106 | /* refuse to map if order is not 0 */ 107 | if (scullc_devices[iminor(inode)].order) 108 | return -ENODEV; 109 | 110 | /* don't do anything here: "nopage" will set up page table entries */ 111 | vma->vm_ops = &scullc_vm_ops; 112 | vma->vm_private_data = filp->private_data; 113 | scullc_vma_open(vma); 114 | return 0; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /scullc/scull-shared/scull-async.c: -------------------------------------------------------------------------------- 1 | ../../scull-shared/scull-async.c -------------------------------------------------------------------------------- /scullc/scull-shared/scull-async.h: -------------------------------------------------------------------------------- 1 | ../../scull-shared/scull-async.h -------------------------------------------------------------------------------- /scullc/scullc.h: -------------------------------------------------------------------------------- 1 | /* -*- C -*- 2 | * scullc.h -- definitions for the scullc char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | /* 20 | * Macros to help debugging 21 | */ 22 | 23 | #undef PDEBUG /* undef it, just in case */ 24 | #ifdef SCULLC_DEBUG 25 | # ifdef __KERNEL__ 26 | /* This one if debugging is on, and kernel space */ 27 | # define PDEBUG(fmt, args...) printk( KERN_DEBUG "scullc: " fmt, ## args) 28 | # else 29 | /* This one for user space */ 30 | # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) 31 | # endif 32 | #else 33 | # define PDEBUG(fmt, args...) /* not debugging: nothing */ 34 | #endif 35 | 36 | #undef PDEBUGG 37 | #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ 38 | 39 | #define SCULLC_MAJOR 0 /* dynamic major by default */ 40 | 41 | #define SCULLC_DEVS 4 /* scullc0 through scullc3 */ 42 | 43 | /* 44 | * The bare device is a variable-length region of memory. 45 | * Use a linked list of indirect blocks. 46 | * 47 | * "scullc_dev->data" points to an array of pointers, each 48 | * pointer refers to a memory page. 49 | * 50 | * The array (quantum-set) is SCULLC_QSET long. 51 | */ 52 | #define SCULLC_QUANTUM 4000 /* use a quantum size like scull */ 53 | #define SCULLC_QSET 500 54 | 55 | struct scullc_dev { 56 | void **data; 57 | struct scullc_dev *next; /* next listitem */ 58 | int vmas; /* active mappings */ 59 | int quantum; /* the current allocation size */ 60 | int qset; /* the current array size */ 61 | size_t size; /* 32-bit will suffice */ 62 | struct mutex lock; /* Mutual exclusion */ 63 | struct cdev cdev; 64 | }; 65 | 66 | extern struct scullc_dev *scullc_devices; 67 | 68 | extern struct file_operations scullc_fops; 69 | 70 | /* 71 | * The different configurable parameters 72 | */ 73 | extern int scullc_major; /* main.c */ 74 | extern int scullc_devs; 75 | extern int scullc_order; 76 | extern int scullc_qset; 77 | 78 | /* 79 | * Prototypes for shared functions 80 | */ 81 | int scullc_trim(struct scullc_dev *dev); 82 | struct scullc_dev *scullc_follow(struct scullc_dev *dev, int n); 83 | 84 | 85 | #ifdef SCULLC_DEBUG 86 | # define SCULLC_USE_PROC 87 | #endif 88 | 89 | /* 90 | * Ioctl definitions 91 | */ 92 | 93 | /* Use 'K' as magic number */ 94 | #define SCULLC_IOC_MAGIC 'K' 95 | 96 | #define SCULLC_IOCRESET _IO(SCULLC_IOC_MAGIC, 0) 97 | 98 | /* 99 | * S means "Set" through a ptr, 100 | * T means "Tell" directly 101 | * G means "Get" (to a pointed var) 102 | * Q means "Query", response is on the return value 103 | * X means "eXchange": G and S atomically 104 | * H means "sHift": T and Q atomically 105 | */ 106 | #define SCULLC_IOCSQUANTUM _IOW(SCULLC_IOC_MAGIC, 1, int) 107 | #define SCULLC_IOCTQUANTUM _IO(SCULLC_IOC_MAGIC, 2) 108 | #define SCULLC_IOCGQUANTUM _IOR(SCULLC_IOC_MAGIC, 3, int) 109 | #define SCULLC_IOCQQUANTUM _IO(SCULLC_IOC_MAGIC, 4) 110 | #define SCULLC_IOCXQUANTUM _IOWR(SCULLC_IOC_MAGIC, 5, int) 111 | #define SCULLC_IOCHQUANTUM _IO(SCULLC_IOC_MAGIC, 6) 112 | #define SCULLC_IOCSQSET _IOW(SCULLC_IOC_MAGIC, 7, int) 113 | #define SCULLC_IOCTQSET _IO(SCULLC_IOC_MAGIC, 8) 114 | #define SCULLC_IOCGQSET _IOR(SCULLC_IOC_MAGIC, 9, int) 115 | #define SCULLC_IOCQQSET _IO(SCULLC_IOC_MAGIC, 10) 116 | #define SCULLC_IOCXQSET _IOWR(SCULLC_IOC_MAGIC,11, int) 117 | #define SCULLC_IOCHQSET _IO(SCULLC_IOC_MAGIC, 12) 118 | 119 | #define SCULLC_IOC_MAXNR 12 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /scullc/scullc_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="scullc" 3 | device="scullc" 4 | mode="664" 5 | 6 | # Group: since distributions do it differently, look for wheel or use staff 7 | if grep '^staff:' /etc/group > /dev/null; then 8 | group="staff" 9 | else 10 | group="wheel" 11 | fi 12 | 13 | # remove stale nodes 14 | rm -f /dev/${device}? 15 | 16 | # invoke insmod with all arguments we got 17 | # and use a pathname, as newer modutils don't look in . by default 18 | insmod ./$module.ko $* || exit 1 19 | 20 | major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` 21 | 22 | mknod /dev/${device}0 c $major 0 23 | mknod /dev/${device}1 c $major 1 24 | mknod /dev/${device}2 c $major 2 25 | mknod /dev/${device}3 c $major 3 26 | ln -sf ${device}0 /dev/${device} 27 | 28 | # give appropriate group/permissions 29 | chgrp $group /dev/${device}[0-3] 30 | chmod $mode /dev/${device}[0-3] 31 | -------------------------------------------------------------------------------- /scullc/scullc_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="scullc" 3 | device="scullc" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # remove nodes 9 | rm -f /dev/${device}[0-3] /dev/${device} 10 | 11 | exit 0 12 | -------------------------------------------------------------------------------- /sculld/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Comment/uncomment the following line to enable/disable debugging 3 | #DEBUG = y 4 | 5 | 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSCULLD_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | LDDINC=$(PWD)/../include 13 | EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINC) 14 | 15 | TARGET = sculld 16 | 17 | ifneq ($(KERNELRELEASE),) 18 | 19 | sculld-objs := main.o mmap.o scull-shared/scull-async.o 20 | 21 | obj-m := sculld.o 22 | 23 | else 24 | 25 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 26 | PWD := $(shell pwd) 27 | 28 | modules: 29 | echo "Copying Module.symvers to resolve lddbus exports" 30 | # cp $(PWD)/../lddbus/Module.symvers $(PWD) 31 | $(MAKE) -C $(KERNELDIR) M=$(PWD) KBUILD_EXTRA_SYMBOLS=$(PWD)/../lddbus/Module.symvers modules 32 | 33 | endif 34 | 35 | 36 | install: 37 | install -d $(INSTALLDIR) 38 | install -c $(TARGET).o $(INSTALLDIR) 39 | 40 | clean: 41 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers scull-shared/scull-async.o 42 | 43 | 44 | depend .depend dep: 45 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 46 | 47 | ifeq (.depend,$(wildcard .depend)) 48 | include .depend 49 | endif 50 | -------------------------------------------------------------------------------- /sculld/mmap.c: -------------------------------------------------------------------------------- 1 | /* -*- C -*- 2 | * mmap.c -- memory mapping for the sculld char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ 16 | */ 17 | 18 | #include 19 | #include 20 | #include /* everything */ 21 | #include /* error codes */ 22 | #include 23 | #include 24 | #include "sculld.h" /* local definitions */ 25 | 26 | 27 | /* 28 | * open and close: just keep track of how many times the device is 29 | * mapped, to avoid releasing it. 30 | */ 31 | 32 | void sculld_vma_open(struct vm_area_struct *vma) 33 | { 34 | struct sculld_dev *dev = vma->vm_private_data; 35 | 36 | dev->vmas++; 37 | } 38 | 39 | void sculld_vma_close(struct vm_area_struct *vma) 40 | { 41 | struct sculld_dev *dev = vma->vm_private_data; 42 | 43 | dev->vmas--; 44 | } 45 | 46 | /* 47 | * The nopage method: the core of the file. It retrieves the 48 | * page required from the sculld device and returns it to the 49 | * user. The count for the page must be incremented, because 50 | * it is automatically decremented at page unmap. 51 | * 52 | * For this reason, "order" must be zero. Otherwise, only the first 53 | * page has its count incremented, and the allocating module must 54 | * release it as a whole block. Therefore, it isn't possible to map 55 | * pages from a multipage block: when they are unmapped, their count 56 | * is individually decreased, and would drop to 0. 57 | */ 58 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0) 59 | typedef int vm_fault_t; 60 | #endif 61 | static vm_fault_t sculld_vma_nopage(struct vm_fault *vmf) 62 | { 63 | unsigned long offset; 64 | struct vm_area_struct *vma = vmf->vma; 65 | struct sculld_dev *ptr, *dev = vma->vm_private_data; 66 | struct page *page = NULL; 67 | void *pageptr = NULL; /* default to "missing" */ 68 | vm_fault_t retval = VM_FAULT_NOPAGE; 69 | 70 | mutex_lock(&dev->mutex); 71 | offset = (unsigned long)(vmf->address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); 72 | if (offset >= dev->size) goto out; /* out of range */ 73 | 74 | /* 75 | * Now retrieve the sculld device from the list,then the page. 76 | * If the device has holes, the process receives a SIGBUS when 77 | * accessing the hole. 78 | */ 79 | offset >>= PAGE_SHIFT; /* offset is a number of pages */ 80 | for (ptr = dev; ptr && offset >= dev->qset;) { 81 | ptr = ptr->next; 82 | offset -= dev->qset; 83 | } 84 | if (ptr && ptr->data) pageptr = ptr->data[offset]; 85 | if (!pageptr) goto out; /* hole or end-of-file */ 86 | 87 | /* got it, now increment the count */ 88 | get_page(page); 89 | vmf->page = page; 90 | retval = 0; 91 | 92 | out: 93 | mutex_unlock(&dev->mutex); 94 | return retval; 95 | } 96 | 97 | 98 | 99 | struct vm_operations_struct sculld_vm_ops = { 100 | .open = sculld_vma_open, 101 | .close = sculld_vma_close, 102 | .fault = sculld_vma_nopage, 103 | }; 104 | 105 | 106 | int sculld_mmap(struct file *filp, struct vm_area_struct *vma) 107 | { 108 | struct inode *inode = filp->f_path.dentry->d_inode; 109 | 110 | /* refuse to map if order is not 0 */ 111 | if (sculld_devices[iminor(inode)].order) 112 | return -ENODEV; 113 | 114 | /* don't do anything here: "nopage" will set up page table entries */ 115 | vma->vm_ops = &sculld_vm_ops; 116 | vma->vm_private_data = filp->private_data; 117 | sculld_vma_open(vma); 118 | return 0; 119 | } 120 | 121 | -------------------------------------------------------------------------------- /sculld/scull-shared/scull-async.c: -------------------------------------------------------------------------------- 1 | ../../scull-shared/scull-async.c -------------------------------------------------------------------------------- /sculld/scull-shared/scull-async.h: -------------------------------------------------------------------------------- 1 | ../../scull-shared/scull-async.h -------------------------------------------------------------------------------- /sculld/sculld.h: -------------------------------------------------------------------------------- 1 | /* -*- C -*- 2 | * sculld.h -- definitions for the sculld char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "../include/lddbus.h" 20 | 21 | /* 22 | * Macros to help debugging 23 | */ 24 | 25 | #undef PDEBUG /* undef it, just in case */ 26 | #ifdef SCULLD_DEBUG 27 | # ifdef __KERNEL__ 28 | /* This one if debugging is on, and kernel space */ 29 | # define PDEBUG(fmt, args...) printk( KERN_DEBUG "sculld: " fmt, ## args) 30 | # else 31 | /* This one for user space */ 32 | # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) 33 | # endif 34 | #else 35 | # define PDEBUG(fmt, args...) /* not debugging: nothing */ 36 | #endif 37 | 38 | #undef PDEBUGG 39 | #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ 40 | 41 | #define SCULLD_MAJOR 0 /* dynamic major by default */ 42 | 43 | #define SCULLD_DEVS 4 /* sculld0 through sculld3 */ 44 | 45 | /* 46 | * The bare device is a variable-length region of memory. 47 | * Use a linked list of indirect blocks. 48 | * 49 | * "sculld_dev->data" points to an array of pointers, each 50 | * pointer refers to a memory page. 51 | * 52 | * The array (quantum-set) is SCULLD_QSET long. 53 | */ 54 | #define SCULLD_ORDER 0 /* one page at a time */ 55 | #define SCULLD_QSET 500 56 | 57 | struct sculld_dev { 58 | void **data; 59 | struct sculld_dev *next; /* next listitem */ 60 | int vmas; /* active mappings */ 61 | int order; /* the current allocation order */ 62 | int qset; /* the current array size */ 63 | size_t size; /* 32-bit will suffice */ 64 | struct mutex mutex; /* Mutual exclusion */ 65 | struct cdev cdev; 66 | char devname[20]; 67 | struct ldd_device ldev; 68 | }; 69 | 70 | extern struct sculld_dev *sculld_devices; 71 | 72 | extern struct file_operations sculld_fops; 73 | 74 | /* 75 | * The different configurable parameters 76 | */ 77 | extern int sculld_major; /* main.c */ 78 | extern int sculld_devs; 79 | extern int sculld_order; 80 | extern int sculld_qset; 81 | 82 | /* 83 | * Prototypes for shared functions 84 | */ 85 | int sculld_trim(struct sculld_dev *dev); 86 | struct sculld_dev *sculld_follow(struct sculld_dev *dev, int n); 87 | 88 | 89 | #ifdef SCULLD_DEBUG 90 | # define SCULLD_USE_PROC 91 | #endif 92 | 93 | /* 94 | * Ioctl definitions 95 | */ 96 | 97 | /* Use 'K' as magic number */ 98 | #define SCULLD_IOC_MAGIC 'K' 99 | 100 | #define SCULLD_IOCRESET _IO(SCULLD_IOC_MAGIC, 0) 101 | 102 | /* 103 | * S means "Set" through a ptr, 104 | * T means "Tell" directly 105 | * G means "Get" (to a pointed var) 106 | * Q means "Query", response is on the return value 107 | * X means "eXchange": G and S atomically 108 | * H means "sHift": T and Q atomically 109 | */ 110 | #define SCULLD_IOCSORDER _IOW(SCULLD_IOC_MAGIC, 1, int) 111 | #define SCULLD_IOCTORDER _IO(SCULLD_IOC_MAGIC, 2) 112 | #define SCULLD_IOCGORDER _IOR(SCULLD_IOC_MAGIC, 3, int) 113 | #define SCULLD_IOCQORDER _IO(SCULLD_IOC_MAGIC, 4) 114 | #define SCULLD_IOCXORDER _IOWR(SCULLD_IOC_MAGIC, 5, int) 115 | #define SCULLD_IOCHORDER _IO(SCULLD_IOC_MAGIC, 6) 116 | #define SCULLD_IOCSQSET _IOW(SCULLD_IOC_MAGIC, 7, int) 117 | #define SCULLD_IOCTQSET _IO(SCULLD_IOC_MAGIC, 8) 118 | #define SCULLD_IOCGQSET _IOR(SCULLD_IOC_MAGIC, 9, int) 119 | #define SCULLD_IOCQQSET _IO(SCULLD_IOC_MAGIC, 10) 120 | #define SCULLD_IOCXQSET _IOWR(SCULLD_IOC_MAGIC,11, int) 121 | #define SCULLD_IOCHQSET _IO(SCULLD_IOC_MAGIC, 12) 122 | 123 | #define SCULLD_IOC_MAXNR 12 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /sculld/sculld_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="sculld" 3 | device="sculld" 4 | mode="664" 5 | 6 | # Group: since distributions do it differently, look for wheel or use staff 7 | if grep '^staff:' /etc/group > /dev/null; then 8 | group="staff" 9 | else 10 | group="wheel" 11 | fi 12 | 13 | # remove stale nodes 14 | rm -f /dev/${device}? 15 | 16 | # invoke insmod with all arguments we got 17 | # and use a pathname, as newer modutils don't look in . by default 18 | insmod ../lddbus/lddbus.ko $* || exit 1 19 | insmod ./$module.ko $* || exit 1 20 | 21 | major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` 22 | 23 | mknod /dev/${device}0 c $major 0 24 | mknod /dev/${device}1 c $major 1 25 | mknod /dev/${device}2 c $major 2 26 | mknod /dev/${device}3 c $major 3 27 | ln -sf ${device}0 /dev/${device} 28 | 29 | # give appropriate group/permissions 30 | chgrp $group /dev/${device}[0-3] 31 | chmod $mode /dev/${device}[0-3] 32 | -------------------------------------------------------------------------------- /sculld/sculld_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="sculld" 3 | device="sculld" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | rmmod lddbus $* || exit 1 8 | 9 | # remove nodes 10 | rm -f /dev/${device}[0-3] /dev/${device} 11 | 12 | exit 0 13 | -------------------------------------------------------------------------------- /scullp/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Comment/uncomment the following line to enable/disable debugging 3 | #DEBUG = y 4 | 5 | 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSCULLP_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | LDDINC=$(PWD)/../include 13 | EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINC) 14 | 15 | TARGET = scullp 16 | 17 | ifneq ($(KERNELRELEASE),) 18 | 19 | scullp-objs := main.o mmap.o scull-shared/scull-async.o 20 | 21 | obj-m := scullp.o 22 | 23 | else 24 | 25 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 26 | PWD := $(shell pwd) 27 | 28 | modules: 29 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 30 | 31 | endif 32 | 33 | 34 | install: 35 | install -d $(INSTALLDIR) 36 | install -c $(TARGET).o $(INSTALLDIR) 37 | 38 | clean: 39 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers scull-shared/scull-async.o 40 | 41 | 42 | depend .depend dep: 43 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 44 | 45 | ifeq (.depend,$(wildcard .depend)) 46 | include .depend 47 | endif 48 | -------------------------------------------------------------------------------- /scullp/mmap.c: -------------------------------------------------------------------------------- 1 | /* -*- C -*- 2 | * mmap.c -- memory mapping for the scullp char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ 16 | */ 17 | 18 | #include 19 | 20 | #include /* everything */ 21 | #include /* error codes */ 22 | #include 23 | #include 24 | #include 25 | #include "scullp.h" /* local definitions */ 26 | 27 | 28 | /* 29 | * open and close: just keep track of how many times the device is 30 | * mapped, to avoid releasing it. 31 | */ 32 | 33 | void scullp_vma_open(struct vm_area_struct *vma) 34 | { 35 | struct scullp_dev *dev = vma->vm_private_data; 36 | 37 | dev->vmas++; 38 | } 39 | 40 | void scullp_vma_close(struct vm_area_struct *vma) 41 | { 42 | struct scullp_dev *dev = vma->vm_private_data; 43 | 44 | dev->vmas--; 45 | } 46 | 47 | /* 48 | * The nopage method: the core of the file. It retrieves the 49 | * page required from the scullp device and returns it to the 50 | * user. The count for the page must be incremented, because 51 | * it is automatically decremented at page unmap. 52 | * 53 | * For this reason, "order" must be zero. Otherwise, only the first 54 | * page has its count incremented, and the allocating module must 55 | * release it as a whole block. Therefore, it isn't possible to map 56 | * pages from a multipage block: when they are unmapped, their count 57 | * is individually decreased, and would drop to 0. 58 | */ 59 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0) 60 | typedef int vm_fault_t; 61 | #endif 62 | static vm_fault_t scullp_vma_nopage(struct vm_fault *vmf) 63 | { 64 | unsigned long offset; 65 | struct vm_area_struct *vma = vmf->vma; 66 | struct scullp_dev *ptr, *dev = vma->vm_private_data; 67 | struct page *page = NULL; 68 | void *pageptr = NULL; /* default to "missing" */ 69 | vm_fault_t retval = VM_FAULT_NOPAGE; 70 | 71 | mutex_lock(&dev->mutex); 72 | offset = (unsigned long)(vmf->address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); 73 | if (offset >= dev->size) goto out; /* out of range */ 74 | 75 | /* 76 | * Now retrieve the scullp device from the list,then the page. 77 | * If the device has holes, the process receives a SIGBUS when 78 | * accessing the hole. 79 | */ 80 | offset >>= PAGE_SHIFT; /* offset is a number of pages */ 81 | for (ptr = dev; ptr && offset >= dev->qset;) { 82 | ptr = ptr->next; 83 | offset -= dev->qset; 84 | } 85 | if (ptr && ptr->data) pageptr = ptr->data[offset]; 86 | if (!pageptr) goto out; /* hole or end-of-file */ 87 | page = virt_to_page(pageptr); 88 | 89 | /* got it, now increment the count */ 90 | get_page(page); 91 | vmf->page = page; 92 | retval = 0; 93 | 94 | out: 95 | mutex_unlock(&dev->mutex); 96 | return retval; 97 | } 98 | 99 | 100 | 101 | struct vm_operations_struct scullp_vm_ops = { 102 | .open = scullp_vma_open, 103 | .close = scullp_vma_close, 104 | .fault = scullp_vma_nopage, 105 | }; 106 | 107 | 108 | int scullp_mmap(struct file *filp, struct vm_area_struct *vma) 109 | { 110 | struct inode *inode = filp->f_path.dentry->d_inode; 111 | 112 | /* refuse to map if order is not 0 */ 113 | if (scullp_devices[iminor(inode)].order) 114 | return -ENODEV; 115 | 116 | /* don't do anything here: "nopage" will set up page table entries */ 117 | vma->vm_ops = &scullp_vm_ops; 118 | vma->vm_private_data = filp->private_data; 119 | scullp_vma_open(vma); 120 | return 0; 121 | } 122 | 123 | -------------------------------------------------------------------------------- /scullp/scull-shared/scull-async.c: -------------------------------------------------------------------------------- 1 | ../../scull-shared/scull-async.c -------------------------------------------------------------------------------- /scullp/scull-shared/scull-async.h: -------------------------------------------------------------------------------- 1 | ../../scull-shared/scull-async.h -------------------------------------------------------------------------------- /scullp/scullp.h: -------------------------------------------------------------------------------- 1 | /* -*- C -*- 2 | * scullp.h -- definitions for the scullp char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | /* 21 | * Macros to help debugging 22 | */ 23 | 24 | #undef PDEBUG /* undef it, just in case */ 25 | #ifdef SCULLP_DEBUG 26 | # ifdef __KERNEL__ 27 | /* This one if debugging is on, and kernel space */ 28 | # define PDEBUG(fmt, args...) printk( KERN_DEBUG "scullp: " fmt, ## args) 29 | # else 30 | /* This one for user space */ 31 | # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) 32 | # endif 33 | #else 34 | # define PDEBUG(fmt, args...) /* not debugging: nothing */ 35 | #endif 36 | 37 | #undef PDEBUGG 38 | #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ 39 | 40 | #define SCULLP_MAJOR 0 /* dynamic major by default */ 41 | 42 | #define SCULLP_DEVS 4 /* scullp0 through scullp3 */ 43 | 44 | /* 45 | * The bare device is a variable-length region of memory. 46 | * Use a linked list of indirect blocks. 47 | * 48 | * "scullp_dev->data" points to an array of pointers, each 49 | * pointer refers to a memory page. 50 | * 51 | * The array (quantum-set) is SCULLP_QSET long. 52 | */ 53 | #define SCULLP_ORDER 0 /* one page at a time */ 54 | #define SCULLP_QSET 500 55 | 56 | struct scullp_dev { 57 | void **data; 58 | struct scullp_dev *next; /* next listitem */ 59 | int vmas; /* active mappings */ 60 | int order; /* the current allocation order */ 61 | int qset; /* the current array size */ 62 | size_t size; /* 32-bit will suffice */ 63 | struct mutex mutex; /* Mutual exclusion */ 64 | struct cdev cdev; 65 | }; 66 | 67 | extern struct scullp_dev *scullp_devices; 68 | 69 | extern struct file_operations scullp_fops; 70 | 71 | /* 72 | * The different configurable parameters 73 | */ 74 | extern int scullp_major; /* main.c */ 75 | extern int scullp_devs; 76 | extern int scullp_order; 77 | extern int scullp_qset; 78 | 79 | /* 80 | * Prototypes for shared functions 81 | */ 82 | int scullp_trim(struct scullp_dev *dev); 83 | struct scullp_dev *scullp_follow(struct scullp_dev *dev, int n); 84 | 85 | 86 | #ifdef SCULLP_DEBUG 87 | # define SCULLP_USE_PROC 88 | #endif 89 | 90 | /* 91 | * Ioctl definitions 92 | */ 93 | 94 | /* Use 'K' as magic number */ 95 | #define SCULLP_IOC_MAGIC 'K' 96 | 97 | #define SCULLP_IOCRESET _IO(SCULLP_IOC_MAGIC, 0) 98 | 99 | /* 100 | * S means "Set" through a ptr, 101 | * T means "Tell" directly 102 | * G means "Get" (to a pointed var) 103 | * Q means "Query", response is on the return value 104 | * X means "eXchange": G and S atomically 105 | * H means "sHift": T and Q atomically 106 | */ 107 | #define SCULLP_IOCSORDER _IOW(SCULLP_IOC_MAGIC, 1, int) 108 | #define SCULLP_IOCTORDER _IO(SCULLP_IOC_MAGIC, 2) 109 | #define SCULLP_IOCGORDER _IOR(SCULLP_IOC_MAGIC, 3, int) 110 | #define SCULLP_IOCQORDER _IO(SCULLP_IOC_MAGIC, 4) 111 | #define SCULLP_IOCXORDER _IOWR(SCULLP_IOC_MAGIC, 5, int) 112 | #define SCULLP_IOCHORDER _IO(SCULLP_IOC_MAGIC, 6) 113 | #define SCULLP_IOCSQSET _IOW(SCULLP_IOC_MAGIC, 7, int) 114 | #define SCULLP_IOCTQSET _IO(SCULLP_IOC_MAGIC, 8) 115 | #define SCULLP_IOCGQSET _IOR(SCULLP_IOC_MAGIC, 9, int) 116 | #define SCULLP_IOCQQSET _IO(SCULLP_IOC_MAGIC, 10) 117 | #define SCULLP_IOCXQSET _IOWR(SCULLP_IOC_MAGIC,11, int) 118 | #define SCULLP_IOCHQSET _IO(SCULLP_IOC_MAGIC, 12) 119 | 120 | #define SCULLP_IOC_MAXNR 12 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /scullp/scullp_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="scullp" 3 | device="scullp" 4 | mode="664" 5 | 6 | # Group: since distributions do it differently, look for wheel or use staff 7 | if grep '^staff:' /etc/group > /dev/null; then 8 | group="staff" 9 | else 10 | group="wheel" 11 | fi 12 | 13 | # remove stale nodes 14 | rm -f /dev/${device}? 15 | 16 | # invoke insmod with all arguments we got 17 | # and use a pathname, as newer modutils don't look in . by default 18 | insmod ./$module.ko $* || exit 1 19 | 20 | major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` 21 | 22 | mknod /dev/${device}0 c $major 0 23 | mknod /dev/${device}1 c $major 1 24 | mknod /dev/${device}2 c $major 2 25 | mknod /dev/${device}3 c $major 3 26 | ln -sf ${device}0 /dev/${device} 27 | 28 | # give appropriate group/permissions 29 | chgrp $group /dev/${device}[0-3] 30 | chmod $mode /dev/${device}[0-3] 31 | -------------------------------------------------------------------------------- /scullp/scullp_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="scullp" 3 | device="scullp" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # remove nodes 9 | rm -f /dev/${device}[0-3] /dev/${device} 10 | 11 | exit 0 12 | -------------------------------------------------------------------------------- /scullv/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Comment/uncomment the following line to enable/disable debugging 3 | #DEBUG = y 4 | 5 | 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSCULLV_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | LDDINC=$(PWD)/../include 13 | EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINC) 14 | 15 | TARGET = scullv 16 | 17 | ifneq ($(KERNELRELEASE),) 18 | 19 | scullv-objs := main.o mmap.o scull-shared/scull-async.o 20 | 21 | obj-m := scullv.o 22 | 23 | else 24 | 25 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 26 | PWD := $(shell pwd) 27 | 28 | modules: 29 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 30 | 31 | endif 32 | 33 | 34 | install: 35 | install -d $(INSTALLDIR) 36 | install -c $(TARGET).o $(INSTALLDIR) 37 | 38 | clean: 39 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers scull-shared/scull-async.o 40 | 41 | 42 | depend .depend dep: 43 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 44 | 45 | ifeq (.depend,$(wildcard .depend)) 46 | include .depend 47 | endif 48 | -------------------------------------------------------------------------------- /scullv/mmap.c: -------------------------------------------------------------------------------- 1 | /* -*- C -*- 2 | * mmap.c -- memory mapping for the scullv char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: _mmap.c.in,v 1.13 2004/10/18 18:07:36 corbet Exp $ 16 | */ 17 | 18 | #include 19 | 20 | #include /* everything */ 21 | #include /* error codes */ 22 | #include 23 | #include 24 | #include 25 | 26 | #include "scullv.h" /* local definitions */ 27 | 28 | 29 | /* 30 | * open and close: just keep track of how many times the device is 31 | * mapped, to avoid releasing it. 32 | */ 33 | 34 | void scullv_vma_open(struct vm_area_struct *vma) 35 | { 36 | struct scullv_dev *dev = vma->vm_private_data; 37 | 38 | dev->vmas++; 39 | } 40 | 41 | void scullv_vma_close(struct vm_area_struct *vma) 42 | { 43 | struct scullv_dev *dev = vma->vm_private_data; 44 | 45 | dev->vmas--; 46 | } 47 | 48 | /* 49 | * The nopage method: the core of the file. It retrieves the 50 | * page required from the scullv device and returns it to the 51 | * user. The count for the page must be incremented, because 52 | * it is automatically decremented at page unmap. 53 | * 54 | * For this reason, "order" must be zero. Otherwise, only the first 55 | * page has its count incremented, and the allocating module must 56 | * release it as a whole block. Therefore, it isn't possible to map 57 | * pages from a multipage block: when they are unmapped, their count 58 | * is individually decreased, and would drop to 0. 59 | */ 60 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0) 61 | typedef int vm_fault_t; 62 | #endif 63 | 64 | static vm_fault_t scullv_vma_nopage(struct vm_fault *vmf) 65 | { 66 | unsigned long offset; 67 | struct vm_area_struct *vma = vmf->vma; 68 | struct scullv_dev *ptr, *dev = vma->vm_private_data; 69 | struct page *page = NULL; 70 | void *pageptr = NULL; /* default to "missing" */ 71 | vm_fault_t retval = VM_FAULT_NOPAGE; 72 | 73 | mutex_lock(&dev->mutex); 74 | offset = (unsigned long)(vmf->address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); 75 | if (offset >= dev->size) goto out; /* out of range */ 76 | 77 | /* 78 | * Now retrieve the scullv device from the list,then the page. 79 | * If the device has holes, the process receives a SIGBUS when 80 | * accessing the hole. 81 | */ 82 | offset >>= PAGE_SHIFT; /* offset is a number of pages */ 83 | for (ptr = dev; ptr && offset >= dev->qset;) { 84 | ptr = ptr->next; 85 | offset -= dev->qset; 86 | } 87 | if (ptr && ptr->data) pageptr = ptr->data[offset]; 88 | if (!pageptr) goto out; /* hole or end-of-file */ 89 | 90 | /* 91 | * After scullv lookup, "page" is now the address of the page 92 | * needed by the current process. Since it's a vmalloc address, 93 | * turn it into a struct page. 94 | */ 95 | page = vmalloc_to_page(pageptr); 96 | 97 | /* got it, now increment the count */ 98 | get_page(page); 99 | vmf->page = page; 100 | retval = 0; 101 | 102 | out: 103 | mutex_unlock(&dev->mutex); 104 | return retval; 105 | } 106 | 107 | 108 | 109 | struct vm_operations_struct scullv_vm_ops = { 110 | .open = scullv_vma_open, 111 | .close = scullv_vma_close, 112 | .fault = scullv_vma_nopage, 113 | }; 114 | 115 | 116 | int scullv_mmap(struct file *filp, struct vm_area_struct *vma) 117 | { 118 | 119 | /* don't do anything here: "nopage" will set up page table entries */ 120 | vma->vm_ops = &scullv_vm_ops; 121 | vma->vm_private_data = filp->private_data; 122 | scullv_vma_open(vma); 123 | return 0; 124 | } 125 | 126 | -------------------------------------------------------------------------------- /scullv/scull-shared/scull-async.c: -------------------------------------------------------------------------------- 1 | ../../scull-shared/scull-async.c -------------------------------------------------------------------------------- /scullv/scull-shared/scull-async.h: -------------------------------------------------------------------------------- 1 | ../../scull-shared/scull-async.h -------------------------------------------------------------------------------- /scullv/scullv.h: -------------------------------------------------------------------------------- 1 | /* -*- C -*- 2 | * scullv.h -- definitions for the scullv char module 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | /* 21 | * Macros to help debugging 22 | */ 23 | 24 | #undef PDEBUG /* undef it, just in case */ 25 | #ifdef SCULLV_DEBUG 26 | # ifdef __KERNEL__ 27 | /* This one if debugging is on, and kernel space */ 28 | # define PDEBUG(fmt, args...) printk( KERN_DEBUG "scullv: " fmt, ## args) 29 | # else 30 | /* This one for user space */ 31 | # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) 32 | # endif 33 | #else 34 | # define PDEBUG(fmt, args...) /* not debugging: nothing */ 35 | #endif 36 | 37 | #undef PDEBUGG 38 | #define PDEBUGG(fmt, args...) /* nothing: it's a placeholder */ 39 | 40 | #define SCULLV_MAJOR 0 /* dynamic major by default */ 41 | 42 | #define SCULLV_DEVS 4 /* scullv0 through scullv3 */ 43 | 44 | /* 45 | * The bare device is a variable-length region of memory. 46 | * Use a linked list of indirect blocks. 47 | * 48 | * "scullv_dev->data" points to an array of pointers, each 49 | * pointer refers to a memory page. 50 | * 51 | * The array (quantum-set) is SCULLV_QSET long. 52 | */ 53 | #define SCULLV_ORDER 4 /* 16 pages at a time */ 54 | #define SCULLV_QSET 500 55 | 56 | struct scullv_dev { 57 | void **data; 58 | struct scullv_dev *next; /* next listitem */ 59 | int vmas; /* active mappings */ 60 | int order; /* the current allocation order */ 61 | int qset; /* the current array size */ 62 | size_t size; /* 32-bit will suffice */ 63 | struct mutex mutex; /* Mutual exclusion */ 64 | struct cdev cdev; 65 | }; 66 | 67 | extern struct scullv_dev *scullv_devices; 68 | 69 | extern struct file_operations scullv_fops; 70 | 71 | /* 72 | * The different configurable parameters 73 | */ 74 | extern int scullv_major; /* main.c */ 75 | extern int scullv_devs; 76 | extern int scullv_order; 77 | extern int scullv_qset; 78 | 79 | /* 80 | * Prototypes for shared functions 81 | */ 82 | int scullv_trim(struct scullv_dev *dev); 83 | struct scullv_dev *scullv_follow(struct scullv_dev *dev, int n); 84 | 85 | 86 | #ifdef SCULLV_DEBUG 87 | # define SCULLV_USE_PROC 88 | #endif 89 | 90 | /* 91 | * Ioctl definitions 92 | */ 93 | 94 | /* Use 'K' as magic number */ 95 | #define SCULLV_IOC_MAGIC 'K' 96 | 97 | #define SCULLV_IOCRESET _IO(SCULLV_IOC_MAGIC, 0) 98 | 99 | /* 100 | * S means "Set" through a ptr, 101 | * T means "Tell" directly 102 | * G means "Get" (to a pointed var) 103 | * Q means "Query", response is on the return value 104 | * X means "eXchange": G and S atomically 105 | * H means "sHift": T and Q atomically 106 | */ 107 | #define SCULLV_IOCSORDER _IOW(SCULLV_IOC_MAGIC, 1, int) 108 | #define SCULLV_IOCTORDER _IO(SCULLV_IOC_MAGIC, 2) 109 | #define SCULLV_IOCGORDER _IOR(SCULLV_IOC_MAGIC, 3, int) 110 | #define SCULLV_IOCQORDER _IO(SCULLV_IOC_MAGIC, 4) 111 | #define SCULLV_IOCXORDER _IOWR(SCULLV_IOC_MAGIC, 5, int) 112 | #define SCULLV_IOCHORDER _IO(SCULLV_IOC_MAGIC, 6) 113 | #define SCULLV_IOCSQSET _IOW(SCULLV_IOC_MAGIC, 7, int) 114 | #define SCULLV_IOCTQSET _IO(SCULLV_IOC_MAGIC, 8) 115 | #define SCULLV_IOCGQSET _IOR(SCULLV_IOC_MAGIC, 9, int) 116 | #define SCULLV_IOCQQSET _IO(SCULLV_IOC_MAGIC, 10) 117 | #define SCULLV_IOCXQSET _IOWR(SCULLV_IOC_MAGIC,11, int) 118 | #define SCULLV_IOCHQSET _IO(SCULLV_IOC_MAGIC, 12) 119 | 120 | #define SCULLV_IOC_MAXNR 12 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /scullv/scullv_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="scullv" 3 | device="scullv" 4 | mode="664" 5 | 6 | # Group: since distributions do it differently, look for wheel or use staff 7 | if grep '^staff:' /etc/group > /dev/null; then 8 | group="staff" 9 | else 10 | group="wheel" 11 | fi 12 | 13 | # remove stale nodes 14 | rm -f /dev/${device}? 15 | 16 | # invoke insmod with all arguments we got 17 | # and use a pathname, as newer modutils don't look in . by default 18 | insmod ./$module.ko $* || exit 1 19 | 20 | major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` 21 | 22 | mknod /dev/${device}0 c $major 0 23 | mknod /dev/${device}1 c $major 1 24 | mknod /dev/${device}2 c $major 2 25 | mknod /dev/${device}3 c $major 3 26 | ln -sf ${device}0 /dev/${device} 27 | 28 | # give appropriate group/permissions 29 | chgrp $group /dev/${device}[0-3] 30 | chmod $mode /dev/${device}[0-3] 31 | -------------------------------------------------------------------------------- /scullv/scullv_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="scullv" 3 | device="scullv" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # remove nodes 9 | rm -f /dev/${device}[0-3] /dev/${device} 10 | 11 | exit 0 12 | -------------------------------------------------------------------------------- /short/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | 5 | # Add your debugging flag (or not) to CFLAGS 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSHORT_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | EXTRA_CFLAGS += $(DEBFLAGS) 13 | EXTRA_CFLAGS += -I.. 14 | 15 | ifneq ($(KERNELRELEASE),) 16 | # call from kernel build system 17 | 18 | obj-m := short.o 19 | 20 | else 21 | 22 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 23 | PWD := $(shell pwd) 24 | 25 | default: 26 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 27 | 28 | endif 29 | 30 | 31 | clean: 32 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers 33 | 34 | depend .depend dep: 35 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 36 | 37 | 38 | ifeq (.depend,$(wildcard .depend)) 39 | include .depend 40 | endif 41 | -------------------------------------------------------------------------------- /short/short_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="short" 3 | device="short" 4 | mode="664" 5 | 6 | # Group: since distributions do it differently, look for wheel or use staff 7 | if grep '^staff:' /etc/group > /dev/null; then 8 | group="staff" 9 | else 10 | group="wheel" 11 | fi 12 | 13 | 14 | # invoke insmod with all arguments we got 15 | # and use a pathname, as newer modutils don't look in . by default 16 | insmod ./$module.ko $* || exit 1 17 | 18 | major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` 19 | 20 | # Create 8 entry points, as SHORT_NR_PORTS is 8 by default 21 | rm -f /dev/${device}[0-7] 22 | mknod /dev/${device}0 c $major 0 23 | mknod /dev/${device}1 c $major 1 24 | mknod /dev/${device}2 c $major 2 25 | mknod /dev/${device}3 c $major 3 26 | mknod /dev/${device}4 c $major 4 27 | mknod /dev/${device}5 c $major 5 28 | mknod /dev/${device}6 c $major 6 29 | mknod /dev/${device}7 c $major 7 30 | 31 | rm -f /dev/${device}[0-3][ps] 32 | mknod /dev/${device}0p c $major 16 33 | mknod /dev/${device}1p c $major 17 34 | mknod /dev/${device}2p c $major 18 35 | mknod /dev/${device}3p c $major 19 36 | mknod /dev/${device}4p c $major 20 37 | mknod /dev/${device}5p c $major 21 38 | mknod /dev/${device}6p c $major 22 39 | mknod /dev/${device}7p c $major 23 40 | 41 | mknod /dev/${device}0s c $major 32 42 | mknod /dev/${device}1s c $major 33 43 | mknod /dev/${device}2s c $major 34 44 | mknod /dev/${device}3s c $major 35 45 | mknod /dev/${device}4s c $major 36 46 | mknod /dev/${device}5s c $major 37 47 | mknod /dev/${device}6s c $major 38 48 | mknod /dev/${device}7s c $major 39 49 | 50 | rm -f /dev/${device}int /dev/${device}print 51 | mknod /dev/${device}int c $major 128 52 | mknod /dev/${device}print c $major 129 53 | 54 | chgrp $group /dev/${device}[0-7] /dev/${device}[0-7][ps] /dev/${device}int 55 | chmod $mode /dev/${device}[0-7] /dev/${device}[0-7][ps] /dev/${device}int 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /short/short_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="short" 3 | device="short" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # Remove stale nodes 9 | 10 | rm -f /dev/${device}[0-7] /dev/${device}[0-7][ps] \ 11 | /dev/${device}int /dev/${device}print 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /shortprint/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | EXTRA_CFLAGS += -O2 -I.. 5 | 6 | ifneq ($(KERNELRELEASE),) 7 | # call from kernel build system 8 | 9 | obj-m := shortprint.o 10 | 11 | else 12 | 13 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 14 | PWD := $(shell pwd) 15 | 16 | default: 17 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 18 | 19 | endif 20 | 21 | 22 | clean: 23 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers 24 | 25 | depend .depend dep: 26 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 27 | 28 | 29 | ifeq (.depend,$(wildcard .depend)) 30 | include .depend 31 | endif 32 | -------------------------------------------------------------------------------- /shortprint/shortprint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Useful info describing the parallel port device. 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | */ 16 | 17 | /* 18 | * Register offsets 19 | */ 20 | #define SP_DATA 0x00 21 | #define SP_STATUS 0x01 22 | #define SP_CONTROL 0x02 23 | #define SP_NPORTS 3 24 | 25 | /* 26 | * Status register bits. 27 | */ 28 | #define SP_SR_BUSY 0x80 29 | #define SP_SR_ACK 0x40 30 | #define SP_SR_PAPER 0x20 31 | #define SP_SR_ONLINE 0x10 32 | #define SP_SR_ERR 0x08 33 | 34 | /* 35 | * Control register. 36 | */ 37 | #define SP_CR_IRQ 0x10 38 | #define SP_CR_SELECT 0x08 39 | #define SP_CR_INIT 0x04 40 | #define SP_CR_AUTOLF 0x02 41 | #define SP_CR_STROBE 0x01 42 | 43 | /* 44 | * Minimum space before waking up a writer. 45 | */ 46 | #define SP_MIN_SPACE PAGE_SIZE/2 47 | -------------------------------------------------------------------------------- /shortprint/shortprint_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="shortprint" 3 | device="shortprint" 4 | mode="666" 5 | 6 | # Group: since distributions do it differently, look for wheel or use staff 7 | if grep '^staff:' /etc/group > /dev/null; then 8 | group="staff" 9 | else 10 | group="wheel" 11 | fi 12 | 13 | 14 | # invoke insmod with all arguments we got 15 | # and use a pathname, as newer modutils don't look in . by default 16 | insmod ./$module.ko $* || exit 1 17 | 18 | major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` 19 | 20 | # Create 8 entry points, as SHORT_NR_PORTS is 8 by default 21 | rm -f /dev/${device} 22 | mknod /dev/${device} c $major 0 23 | 24 | chgrp $group /dev/${device} 25 | chmod $mode /dev/${device} 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /shortprint/shortprint_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="shortprint" 3 | device="shortprint" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # Remove stale nodes 9 | rm -f /dev/${device} 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /simple/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | # Add your debugging flag (or not) to CFLAGS 5 | ifeq ($(DEBUG),y) 6 | DEBFLAGS = -O -g # "-O" is needed to expand inlines 7 | else 8 | DEBFLAGS = -O2 9 | endif 10 | 11 | EXTRA_CFLAGS += $(DEBFLAGS) -I$(LDDINCDIR) 12 | 13 | ifneq ($(KERNELRELEASE),) 14 | # call from kernel build system 15 | 16 | obj-m := simple.o 17 | 18 | else 19 | 20 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 21 | PWD := $(shell pwd) 22 | 23 | default: 24 | $(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINCDIR=$(PWD)/../include modules 25 | 26 | endif 27 | 28 | 29 | 30 | clean: 31 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers 32 | 33 | depend .depend dep: 34 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 35 | 36 | 37 | ifeq (.depend,$(wildcard .depend)) 38 | include .depend 39 | endif 40 | -------------------------------------------------------------------------------- /simple/simple.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple - REALLY simple memory mapping demonstration. 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * $Id: simple.c,v 1.12 2005/01/31 16:15:31 rubini Exp $ 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include /* printk() */ 23 | #include /* kmalloc() */ 24 | #include /* everything... */ 25 | #include /* error codes */ 26 | #include /* size_t */ 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | static int simple_major = 0; 36 | module_param(simple_major, int, 0); 37 | MODULE_AUTHOR("Jonathan Corbet"); 38 | MODULE_LICENSE("Dual BSD/GPL"); 39 | 40 | 41 | /* 42 | * Closing is just as simpler. 43 | */ 44 | static int simple_release(struct inode *inode, struct file *filp) 45 | { 46 | return 0; 47 | } 48 | 49 | 50 | 51 | /* 52 | * Common VMA ops. 53 | */ 54 | 55 | void simple_vma_open(struct vm_area_struct *vma) 56 | { 57 | printk(KERN_NOTICE "Simple VMA open, virt %lx, phys %lx\n", 58 | vma->vm_start, vma->vm_pgoff << PAGE_SHIFT); 59 | } 60 | 61 | void simple_vma_close(struct vm_area_struct *vma) 62 | { 63 | printk(KERN_NOTICE "Simple VMA close.\n"); 64 | } 65 | 66 | 67 | /* 68 | * The remap_pfn_range version of mmap. This one is heavily borrowed 69 | * from drivers/char/mem.c. 70 | */ 71 | 72 | static struct vm_operations_struct simple_remap_vm_ops = { 73 | .open = simple_vma_open, 74 | .close = simple_vma_close, 75 | }; 76 | 77 | static int simple_remap_mmap(struct file *filp, struct vm_area_struct *vma) 78 | { 79 | if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, 80 | vma->vm_end - vma->vm_start, 81 | vma->vm_page_prot)) 82 | return -EAGAIN; 83 | 84 | vma->vm_ops = &simple_remap_vm_ops; 85 | simple_vma_open(vma); 86 | return 0; 87 | } 88 | 89 | 90 | 91 | /* 92 | * The nopage version. 93 | */ 94 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0) 95 | typedef int vm_fault_t; 96 | #endif 97 | 98 | static vm_fault_t simple_vma_nopage(struct vm_fault *vmf) 99 | { 100 | struct page *pageptr; 101 | struct vm_area_struct *vma = vmf->vma; 102 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; 103 | unsigned long physaddr = (unsigned long) vmf->address - vma->vm_start + offset; 104 | unsigned long pageframe = physaddr >> PAGE_SHIFT; 105 | 106 | // Eventually remove these printks 107 | printk (KERN_NOTICE "---- Nopage, off %lx phys %lx\n", offset, physaddr); 108 | printk (KERN_NOTICE "VA is %p\n", __va (physaddr)); 109 | printk (KERN_NOTICE "Page at %p\n", virt_to_page (__va (physaddr))); 110 | if (!pfn_valid(pageframe)) 111 | return VM_FAULT_SIGBUS; 112 | pageptr = pfn_to_page(pageframe); 113 | printk (KERN_NOTICE "page->index = %ld mapping %p\n", pageptr->index, pageptr->mapping); 114 | printk (KERN_NOTICE "Page frame %ld\n", pageframe); 115 | get_page(pageptr); 116 | vmf->page = pageptr; 117 | 118 | return 0; 119 | } 120 | 121 | static struct vm_operations_struct simple_nopage_vm_ops = { 122 | .open = simple_vma_open, 123 | .close = simple_vma_close, 124 | .fault = simple_vma_nopage, 125 | }; 126 | 127 | static int simple_nopage_mmap(struct file *filp, struct vm_area_struct *vma) 128 | { 129 | vma->vm_ops = &simple_nopage_vm_ops; 130 | simple_vma_open(vma); 131 | return 0; 132 | } 133 | 134 | 135 | /* 136 | * Set up the cdev structure for a device. 137 | */ 138 | static void simple_setup_cdev(struct cdev *dev, int minor, 139 | struct file_operations *fops) 140 | { 141 | int err, devno = MKDEV(simple_major, minor); 142 | 143 | cdev_init(dev, fops); 144 | dev->owner = THIS_MODULE; 145 | err = cdev_add (dev, devno, 1); 146 | /* Fail gracefully if need be */ 147 | if (err) 148 | printk (KERN_NOTICE "Error %d adding simple%d", err, minor); 149 | } 150 | 151 | 152 | /* 153 | * Our various sub-devices. 154 | */ 155 | /* Device 0 uses remap_pfn_range */ 156 | static struct file_operations simple_remap_ops = { 157 | .owner = THIS_MODULE, 158 | .open = simple_open, 159 | .release = simple_release, 160 | .mmap = simple_remap_mmap, 161 | }; 162 | 163 | /* Device 1 uses nopage */ 164 | static struct file_operations simple_nopage_ops = { 165 | .owner = THIS_MODULE, 166 | .open = simple_open, 167 | .release = simple_release, 168 | .mmap = simple_nopage_mmap, 169 | }; 170 | 171 | #define MAX_SIMPLE_DEV 2 172 | 173 | #if 0 174 | static struct file_operations *simple_fops[MAX_SIMPLE_DEV] = { 175 | &simple_remap_ops, 176 | &simple_nopage_ops, 177 | }; 178 | #endif 179 | 180 | /* 181 | * We export two simple devices. There's no need for us to maintain any 182 | * special housekeeping info, so we just deal with raw cdevs. 183 | */ 184 | static struct cdev SimpleDevs[MAX_SIMPLE_DEV]; 185 | 186 | /* 187 | * Module housekeeping. 188 | */ 189 | static int simple_init(void) 190 | { 191 | int result; 192 | dev_t dev = MKDEV(simple_major, 0); 193 | 194 | /* Figure out our device number. */ 195 | if (simple_major) 196 | result = register_chrdev_region(dev, 2, "simple"); 197 | else { 198 | result = alloc_chrdev_region(&dev, 0, 2, "simple"); 199 | simple_major = MAJOR(dev); 200 | } 201 | if (result < 0) { 202 | printk(KERN_WARNING "simple: unable to get major %d\n", simple_major); 203 | return result; 204 | } 205 | if (simple_major == 0) 206 | simple_major = result; 207 | 208 | /* Now set up two cdevs. */ 209 | simple_setup_cdev(SimpleDevs, 0, &simple_remap_ops); 210 | simple_setup_cdev(SimpleDevs + 1, 1, &simple_nopage_ops); 211 | return 0; 212 | } 213 | 214 | 215 | static void simple_cleanup(void) 216 | { 217 | cdev_del(SimpleDevs); 218 | cdev_del(SimpleDevs + 1); 219 | unregister_chrdev_region(MKDEV(simple_major, 0), 2); 220 | } 221 | 222 | 223 | module_init(simple_init); 224 | module_exit(simple_cleanup); 225 | -------------------------------------------------------------------------------- /simple/simple_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="simple" 3 | device="simple" 4 | mode="664" 5 | 6 | # Group: since distributions do it differently, look for wheel or use staff 7 | if grep '^staff:' /etc/group > /dev/null; then 8 | group="staff" 9 | else 10 | group="wheel" 11 | fi 12 | 13 | # invoke insmod with all arguments we got 14 | # and use a pathname, as newer modutils don't look in . by default 15 | insmod ./$module.ko $* || exit 1 16 | 17 | major=`cat /proc/devices | awk "\\$2==\"$module\" {print \\$1}"` 18 | 19 | # Remove stale nodes and replace them, then give gid and perms 20 | # Usually the script is shorter, it's simple that has several devices in it. 21 | 22 | rm -f /dev/${device}[rn] 23 | mknod /dev/${device}r c $major 0 24 | mknod /dev/${device}n c $major 1 25 | chgrp $group /dev/${device}[rn] 26 | chmod $mode /dev/${device}[rn] 27 | -------------------------------------------------------------------------------- /simple/simple_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | module="simple" 3 | device="simple" 4 | 5 | # invoke rmmod with all arguments we got 6 | rmmod $module $* || exit 1 7 | 8 | # Remove stale nodes 9 | rm -f /dev/${device}[rn] 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /skull/Makefile: -------------------------------------------------------------------------------- 1 | foo: 2 | -------------------------------------------------------------------------------- /skull/skull_clean.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | void skull_release(unsigned int port, unsigned int range) 8 | { 9 | release_region(port,range); 10 | } 11 | 12 | void skull_cleanup(void) 13 | { 14 | /* should put real values here ... */ 15 | /* skull_release(0,0); */ 16 | } 17 | 18 | module_exit(skull_cleanup); 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /skull/skull_init.c: -------------------------------------------------------------------------------- 1 | /* 2 | * skull.c -- sample typeless module. 3 | * 4 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 5 | * Copyright (C) 2001 O'Reilly & Associates 6 | * 7 | * The source code in this file can be freely used, adapted, 8 | * and redistributed in source or binary form, so long as an 9 | * acknowledgment appears in derived source files. The citation 10 | * should list that the code comes from the book "Linux Device 11 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 12 | * by O'Reilly & Associates. No warranty is attached; 13 | * we cannot take responsibility for errors or fitness for use. 14 | * 15 | * BUGS: 16 | * -it only runs on intel platforms. 17 | * -readb() should be used (see short.c): skull doesn't work with 2.1 18 | * 19 | */ 20 | 21 | /* jc: cleaned up, but not yet run for anything */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include /* printk */ 29 | #include 30 | #include 31 | #include /* cli(), *_flags */ 32 | #include /* vremap (2.0) */ 33 | #include /* ioremap */ 34 | 35 | /* The region we look at. */ 36 | #define ISA_REGION_BEGIN 0xA0000 37 | #define ISA_REGION_END 0x100000 38 | #define STEP 2048 39 | 40 | /* have three symbols to export */ 41 | void skull_fn1(void){} 42 | static void skull_fn2(void){} 43 | int skull_variable; 44 | 45 | EXPORT_SYMBOL (skull_fn1); 46 | EXPORT_SYMBOL (skull_fn2); 47 | EXPORT_SYMBOL (skull_variable); 48 | 49 | 50 | /* perform hardware autodetection */ 51 | int skull_probe_hw(unsigned int port, unsigned int range) 52 | { 53 | /* do smart probing here */ 54 | return -1; /* not found :-) */ 55 | } 56 | 57 | /* perform hardware initalizazion */ 58 | int skull_init_board(unsigned int port) 59 | { 60 | /* do smart initalization here */ 61 | return 0; /* done :-) */ 62 | } 63 | 64 | /* detect the the device if the region is still free */ 65 | static int skull_detect(unsigned int port, unsigned int range) 66 | { 67 | int err; 68 | 69 | if ((err = check_region(port,range)) < 0) return err; /* busy */ 70 | if (skull_probe_hw(port,range) != 0) return -ENODEV; /* not found */ 71 | request_region(port,range,"skull"); /* "Can't fail" */ 72 | return 0; 73 | } 74 | 75 | /* 76 | * port ranges: the device can reside between 77 | * 0x280 and 0x300, in step of 0x10. It uses 0x10 ports. 78 | */ 79 | #define SKULL_PORT_FLOOR 0x280 80 | #define SKULL_PORT_CEIL 0x300 81 | #define SKULL_PORT_RANGE 0x010 82 | 83 | /* 84 | * the following function performs autodetection, unless a specific 85 | * value was assigned by insmod to "skull_port_base" 86 | */ 87 | 88 | static int skull_port_base=0; /* 0 forces autodetection */ 89 | module_param(skull_port_base, int, 0); 90 | 91 | static int skull_find_hw(void) /* returns the # of devices */ 92 | { 93 | /* base is either the load-time value or the first trial */ 94 | int base = skull_port_base ? skull_port_base 95 | : SKULL_PORT_FLOOR; 96 | int result = 0; 97 | 98 | /* loop one time if value assigned, try them all if autodetecting */ 99 | do { 100 | if (skull_detect(base, SKULL_PORT_RANGE) == 0) { 101 | skull_init_board(base); 102 | result++; 103 | } 104 | base += SKULL_PORT_RANGE; /* prepare for next trial */ 105 | } 106 | while (skull_port_base == 0 && base < SKULL_PORT_CEIL); 107 | 108 | return result; 109 | } 110 | 111 | 112 | int skull_init(void) 113 | { 114 | /* 115 | * Print the isa region map, in blocks of 2K bytes. 116 | * This is not the best code, as it prints too many lines, 117 | * but it deserves to remain short to be included in the book. 118 | * Note also that read() should be used instead of pointers. 119 | */ 120 | unsigned char oldval, newval; /* values read from memory */ 121 | unsigned long flags; /* used to hold system flags */ 122 | unsigned long add, i; 123 | void *base; 124 | 125 | /* Use ioremap to get a handle on our region */ 126 | base = ioremap(ISA_REGION_BEGIN, ISA_REGION_END - ISA_REGION_BEGIN); 127 | base -= ISA_REGION_BEGIN; /* Do the offset once */ 128 | 129 | /* probe all the memory hole in 2KB steps */ 130 | for (add = ISA_REGION_BEGIN; add < ISA_REGION_END; add += STEP) { 131 | /* 132 | * Check for an already allocated region. 133 | */ 134 | if (check_mem_region (add, 2048)) { 135 | printk(KERN_INFO "%lx: Allocated\n", add); 136 | continue; 137 | } 138 | /* 139 | * Read and write the beginning of the region and see what happens. 140 | */ 141 | save_flags(flags); 142 | cli(); 143 | oldval = readb (base + add); /* Read a byte */ 144 | writeb (oldval^0xff, base + add); 145 | mb(); 146 | newval = readb (base + add); 147 | writeb (oldval, base + add); 148 | restore_flags(flags); 149 | 150 | if ((oldval^newval) == 0xff) { /* we re-read our change: it's ram */ 151 | printk(KERN_INFO "%lx: RAM\n", add); 152 | continue; 153 | } 154 | if ((oldval^newval) != 0) { /* random bits changed: it's empty */ 155 | printk(KERN_INFO "%lx: empty\n", add); 156 | continue; 157 | } 158 | 159 | /* 160 | * Expansion rom (executed at boot time by the bios) 161 | * has a signature where the first byt is 0x55, the second 0xaa, 162 | * and the third byte indicates the size of such rom 163 | */ 164 | if ( (oldval == 0x55) && (readb (base + add + 1) == 0xaa)) { 165 | int size = 512 * readb (base + add + 2); 166 | printk(KERN_INFO "%lx: Expansion ROM, %i bytes\n", 167 | add, size); 168 | add += (size & ~2048) - 2048; /* skip it */ 169 | continue; 170 | } 171 | 172 | /* 173 | * If the tests above failed, we still don't know if it is ROM or 174 | * empty. Since empty memory can appear as 0x00, 0xff, or the low 175 | * address byte, we must probe multiple bytes: if at least one of 176 | * them is different from these three values, then this is rom 177 | * (though not boot rom). 178 | */ 179 | printk(KERN_INFO "%lx: ", add); 180 | for (i=0; i<5; i++) { 181 | unsigned long radd = add + 57*(i+1); /* a "random" value */ 182 | unsigned char val = readb (base + radd); 183 | if (val && val != 0xFF && val != ((unsigned long) radd&0xFF)) 184 | break; 185 | } 186 | printk("%s\n", i==5 ? "empty" : "ROM"); 187 | } 188 | 189 | /* 190 | * Find you hardware 191 | */ 192 | skull_find_hw(); 193 | 194 | /* 195 | * Always fail to load (or suceed). 196 | */ 197 | return 0; 198 | } 199 | 200 | module_init(skull_init); 201 | -------------------------------------------------------------------------------- /snull/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | 5 | # Add your debugging flag (or not) to CFLAGS 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSNULL_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | EXTRA_CFLAGS += $(DEBFLAGS) 13 | EXTRA_CFLAGS += -I.. 14 | 15 | ifneq ($(KERNELRELEASE),) 16 | # call from kernel build system 17 | 18 | obj-m := snull.o 19 | 20 | else 21 | 22 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 23 | PWD := $(shell pwd) 24 | 25 | default: 26 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 27 | 28 | endif 29 | 30 | 31 | 32 | clean: 33 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers 34 | 35 | depend .depend dep: 36 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 37 | 38 | 39 | ifeq (.depend,$(wildcard .depend)) 40 | include .depend 41 | endif 42 | -------------------------------------------------------------------------------- /snull/snull.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * snull.h -- definitions for the network module 4 | * 5 | * Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet 6 | * Copyright (C) 2001 O'Reilly & Associates 7 | * 8 | * The source code in this file can be freely used, adapted, 9 | * and redistributed in source or binary form, so long as an 10 | * acknowledgment appears in derived source files. The citation 11 | * should list that the code comes from the book "Linux Device 12 | * Drivers" by Alessandro Rubini and Jonathan Corbet, published 13 | * by O'Reilly & Associates. No warranty is attached; 14 | * we cannot take responsibility for errors or fitness for use. 15 | */ 16 | 17 | /* 18 | * Macros to help debugging 19 | */ 20 | 21 | #undef PDEBUG /* undef it, just in case */ 22 | #ifdef SNULL_DEBUG 23 | # ifdef __KERNEL__ 24 | /* This one if debugging is on, and kernel space */ 25 | # define PDEBUG(fmt, args...) printk( KERN_DEBUG "snull: " fmt, ## args) 26 | # else 27 | /* This one for user space */ 28 | # define PDEBUG(fmt, args...) fprintf(stderr, fmt, ## args) 29 | # endif 30 | #else 31 | # define PDEBUG(fmt, args...) /* not debugging: nothing */ 32 | #endif 33 | 34 | /* These are the flags in the statusword */ 35 | #define SNULL_RX_INTR 0x0001 36 | #define SNULL_TX_INTR 0x0002 37 | 38 | /* Default timeout period */ 39 | #define SNULL_TIMEOUT 5 /* In jiffies */ 40 | 41 | extern struct net_device *snull_devs[]; 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /snull/snull_load: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Use a pathname, as new modutils don't look in the current dir by default 4 | insmod ./snull.ko $* 5 | ifconfig sn0 local0 6 | ifconfig sn1 local1 7 | -------------------------------------------------------------------------------- /snull/snull_unload: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ifconfig sn0 down 4 | ifconfig sn1 down 5 | rmmod snull 6 | -------------------------------------------------------------------------------- /tty/Makefile: -------------------------------------------------------------------------------- 1 | # Comment/uncomment the following line to disable/enable debugging 2 | #DEBUG = y 3 | 4 | 5 | # Add your debugging flag (or not) to CFLAGS 6 | ifeq ($(DEBUG),y) 7 | DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines 8 | else 9 | DEBFLAGS = -O2 10 | endif 11 | 12 | EXTRA_CFLAGS += $(DEBFLAGS) 13 | EXTRA_CFLAGS += -I.. 14 | 15 | ifneq ($(KERNELRELEASE),) 16 | # call from kernel build system 17 | 18 | obj-m := tiny_tty.o tiny_serial.o 19 | 20 | else 21 | 22 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 23 | PWD := $(shell pwd) 24 | 25 | default: 26 | $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 27 | 28 | endif 29 | 30 | 31 | 32 | clean: 33 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers 34 | 35 | depend .depend dep: 36 | $(CC) $(EXTRA_CFLAGS) -M *.c > .depend 37 | 38 | 39 | ifeq (.depend,$(wildcard .depend)) 40 | include .depend 41 | endif 42 | -------------------------------------------------------------------------------- /tty/tiny_serial.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Tiny Serial driver 3 | * 4 | * Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com) 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, version 2 of the License. 9 | * 10 | * This driver shows how to create a minimal serial driver. It does not rely on 11 | * any backing hardware, but creates a timer that emulates data being received 12 | * from some kind of hardware. 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define DRIVER_AUTHOR "Greg Kroah-Hartman " 27 | #define DRIVER_DESC "Tiny serial driver" 28 | 29 | /* Module information */ 30 | MODULE_AUTHOR( DRIVER_AUTHOR ); 31 | MODULE_DESCRIPTION( DRIVER_DESC ); 32 | MODULE_LICENSE("GPL"); 33 | 34 | #define DELAY_TIME HZ * 2 /* 2 seconds per character */ 35 | #define TINY_DATA_CHARACTER 't' 36 | 37 | #define TINY_SERIAL_MAJOR 240 /* experimental range */ 38 | #define TINY_SERIAL_MINORS 1 /* only have one minor */ 39 | #define UART_NR 1 /* only use one port */ 40 | 41 | #define TINY_SERIAL_NAME "ttytiny" 42 | 43 | #define MY_NAME TINY_SERIAL_NAME 44 | 45 | static struct timer_list *timer; 46 | 47 | static void tiny_stop_tx(struct uart_port *port) 48 | { 49 | } 50 | 51 | static void tiny_stop_rx(struct uart_port *port) 52 | { 53 | } 54 | 55 | static void tiny_enable_ms(struct uart_port *port) 56 | { 57 | } 58 | 59 | static void tiny_tx_chars(struct uart_port *port) 60 | { 61 | struct circ_buf *xmit = &port->state->xmit; 62 | int count; 63 | 64 | if (port->x_char) { 65 | pr_debug("wrote %2x", port->x_char); 66 | port->icount.tx++; 67 | port->x_char = 0; 68 | return; 69 | } 70 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { 71 | tiny_stop_tx(port); 72 | return; 73 | } 74 | 75 | count = port->fifosize >> 1; 76 | do { 77 | pr_debug("wrote %2x", xmit->buf[xmit->tail]); 78 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); 79 | port->icount.tx++; 80 | if (uart_circ_empty(xmit)) 81 | break; 82 | } while (--count > 0); 83 | 84 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 85 | uart_write_wakeup(port); 86 | 87 | if (uart_circ_empty(xmit)) 88 | tiny_stop_tx(port); 89 | } 90 | 91 | static void tiny_start_tx(struct uart_port *port) 92 | { 93 | } 94 | 95 | static void tiny_timer(unsigned long data) 96 | { 97 | struct uart_port *port; 98 | struct tty_struct *tty; 99 | struct tty_port *tty_port; 100 | 101 | 102 | port = (struct uart_port *)data; 103 | if (!port) 104 | return; 105 | if (!port->state) 106 | return; 107 | tty = port->state->port.tty; 108 | if (!tty) 109 | return; 110 | 111 | tty_port = tty->port; 112 | 113 | /* add one character to the tty port */ 114 | /* this doesn't actually push the data through unless tty->low_latency is set */ 115 | tty_insert_flip_char(tty_port, TINY_DATA_CHARACTER, 0); 116 | 117 | tty_flip_buffer_push(tty_port); 118 | 119 | /* resubmit the timer again */ 120 | timer->expires = jiffies + DELAY_TIME; 121 | add_timer(timer); 122 | 123 | /* see if we have any data to transmit */ 124 | tiny_tx_chars(port); 125 | } 126 | 127 | static unsigned int tiny_tx_empty(struct uart_port *port) 128 | { 129 | return 0; 130 | } 131 | 132 | static unsigned int tiny_get_mctrl(struct uart_port *port) 133 | { 134 | return 0; 135 | } 136 | 137 | static void tiny_set_mctrl(struct uart_port *port, unsigned int mctrl) 138 | { 139 | } 140 | 141 | static void tiny_break_ctl(struct uart_port *port, int break_state) 142 | { 143 | } 144 | 145 | static void tiny_set_termios(struct uart_port *port, 146 | struct ktermios *new, struct ktermios *old) 147 | { 148 | int baud, quot, cflag = new->c_cflag; 149 | /* get the byte size */ 150 | switch (cflag & CSIZE) { 151 | case CS5: 152 | printk(KERN_DEBUG " - data bits = 5\n"); 153 | break; 154 | case CS6: 155 | printk(KERN_DEBUG " - data bits = 6\n"); 156 | break; 157 | case CS7: 158 | printk(KERN_DEBUG " - data bits = 7\n"); 159 | break; 160 | default: // CS8 161 | printk(KERN_DEBUG " - data bits = 8\n"); 162 | break; 163 | } 164 | 165 | /* determine the parity */ 166 | if (cflag & PARENB) 167 | if (cflag & PARODD) 168 | pr_debug(" - parity = odd\n"); 169 | else 170 | pr_debug(" - parity = even\n"); 171 | else 172 | pr_debug(" - parity = none\n"); 173 | 174 | /* figure out the stop bits requested */ 175 | if (cflag & CSTOPB) 176 | pr_debug(" - stop bits = 2\n"); 177 | else 178 | pr_debug(" - stop bits = 1\n"); 179 | 180 | /* figure out the flow control settings */ 181 | if (cflag & CRTSCTS) 182 | pr_debug(" - RTS/CTS is enabled\n"); 183 | else 184 | pr_debug(" - RTS/CTS is disabled\n"); 185 | 186 | /* Set baud rate */ 187 | baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16); 188 | quot = uart_get_divisor(port, baud); 189 | 190 | //UART_PUT_DIV_LO(port, (quot & 0xff)); 191 | //UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8)); 192 | } 193 | 194 | static int tiny_startup(struct uart_port *port) 195 | { 196 | /* this is the first time this port is opened */ 197 | /* do any hardware initialization needed here */ 198 | 199 | /* create our timer and submit it */ 200 | if (!timer) { 201 | timer = kmalloc(sizeof(*timer), GFP_KERNEL); 202 | if (!timer) 203 | return -ENOMEM; 204 | } 205 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0) 206 | timer->data = (unsigned long)port; 207 | timer->function = tiny_timer; 208 | #else 209 | timer_setup(timer, (void *) tiny_timer, (unsigned long) port); 210 | timer->function = (void *) tiny_timer; 211 | #endif 212 | timer->expires = jiffies + DELAY_TIME; 213 | add_timer(timer); 214 | return 0; 215 | } 216 | 217 | static void tiny_shutdown(struct uart_port *port) 218 | { 219 | /* The port is being closed by the last user. */ 220 | /* Do any hardware specific stuff here */ 221 | 222 | /* shut down our timer */ 223 | del_timer(timer); 224 | } 225 | 226 | static const char *tiny_type(struct uart_port *port) 227 | { 228 | return "tinytty"; 229 | } 230 | 231 | static void tiny_release_port(struct uart_port *port) 232 | { 233 | 234 | } 235 | 236 | static int tiny_request_port(struct uart_port *port) 237 | { 238 | return 0; 239 | } 240 | 241 | static void tiny_config_port(struct uart_port *port, int flags) 242 | { 243 | } 244 | 245 | static int tiny_verify_port(struct uart_port *port, struct serial_struct *ser) 246 | { 247 | return 0; 248 | } 249 | 250 | static struct uart_ops tiny_ops = { 251 | .tx_empty = tiny_tx_empty, 252 | .set_mctrl = tiny_set_mctrl, 253 | .get_mctrl = tiny_get_mctrl, 254 | .stop_tx = tiny_stop_tx, 255 | .start_tx = tiny_start_tx, 256 | .stop_rx = tiny_stop_rx, 257 | .enable_ms = tiny_enable_ms, 258 | .break_ctl = tiny_break_ctl, 259 | .startup = tiny_startup, 260 | .shutdown = tiny_shutdown, 261 | .set_termios = tiny_set_termios, 262 | .type = tiny_type, 263 | .release_port = tiny_release_port, 264 | .request_port = tiny_request_port, 265 | .config_port = tiny_config_port, 266 | .verify_port = tiny_verify_port, 267 | }; 268 | 269 | static struct uart_port tiny_port = { 270 | .ops = &tiny_ops, 271 | }; 272 | 273 | static struct uart_driver tiny_reg = { 274 | .owner = THIS_MODULE, 275 | .driver_name = TINY_SERIAL_NAME, 276 | .dev_name = TINY_SERIAL_NAME, 277 | .major = TINY_SERIAL_MAJOR, 278 | .minor = TINY_SERIAL_MINORS, 279 | .nr = UART_NR, 280 | }; 281 | 282 | static int __init tiny_init(void) 283 | { 284 | int result; 285 | 286 | printk(KERN_INFO "Tiny serial driver loaded\n"); 287 | 288 | result = uart_register_driver(&tiny_reg); 289 | if (result) 290 | return result; 291 | 292 | result = uart_add_one_port(&tiny_reg, &tiny_port); 293 | if (result) 294 | uart_unregister_driver(&tiny_reg); 295 | 296 | return result; 297 | } 298 | 299 | module_init(tiny_init); 300 | -------------------------------------------------------------------------------- /usb/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := usb-skeleton.o 2 | 3 | KERNELDIR ?= /lib/modules/$(shell uname -r)/build 4 | PWD := $(shell pwd) 5 | 6 | all: 7 | $(MAKE) -C $(KERNELDIR) M=$(PWD) 8 | 9 | clean: 10 | rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers built-in.a 11 | 12 | -------------------------------------------------------------------------------- /usb/usb-skeleton.c: -------------------------------------------------------------------------------- 1 | /* 2 | * USB Skeleton driver - 2.0 3 | * 4 | * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation, version 2. 9 | * 10 | * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c 11 | * but has been rewritten to be easy to read and use, as no locks are now 12 | * needed anymore. 13 | * 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | /* Define these values to match your devices */ 27 | #define USB_SKEL_VENDOR_ID 0xfff0 28 | #define USB_SKEL_PRODUCT_ID 0xfff0 29 | 30 | /* table of devices that work with this driver */ 31 | static struct usb_device_id skel_table [] = { 32 | { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, 33 | { } /* Terminating entry */ 34 | }; 35 | MODULE_DEVICE_TABLE (usb, skel_table); 36 | 37 | 38 | /* Get a minor range for your devices from the usb maintainer */ 39 | #define USB_SKEL_MINOR_BASE 192 40 | 41 | /* Structure to hold all of our device specific stuff */ 42 | struct usb_skel { 43 | struct usb_device * udev; /* the usb device for this device */ 44 | struct usb_interface * interface; /* the interface for this device */ 45 | unsigned char * bulk_in_buffer; /* the buffer to receive data */ 46 | size_t bulk_in_size; /* the size of the receive buffer */ 47 | __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ 48 | __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ 49 | struct kref kref; 50 | }; 51 | #define to_skel_dev(d) container_of(d, struct usb_skel, kref) 52 | 53 | static struct usb_driver skel_driver; 54 | 55 | static void skel_delete(struct kref *kref) 56 | { 57 | struct usb_skel *dev = to_skel_dev(kref); 58 | 59 | usb_put_dev(dev->udev); 60 | kfree (dev->bulk_in_buffer); 61 | kfree (dev); 62 | } 63 | 64 | static int skel_open(struct inode *inode, struct file *file) 65 | { 66 | struct usb_skel *dev; 67 | struct usb_interface *interface; 68 | int subminor; 69 | int retval = 0; 70 | 71 | subminor = iminor(inode); 72 | 73 | interface = usb_find_interface(&skel_driver, subminor); 74 | if (!interface) { 75 | pr_err("%s - error, can't find device for minor %d", 76 | __FUNCTION__, subminor); 77 | retval = -ENODEV; 78 | goto exit; 79 | } 80 | 81 | dev = usb_get_intfdata(interface); 82 | if (!dev) { 83 | retval = -ENODEV; 84 | goto exit; 85 | } 86 | 87 | /* increment our usage count for the device */ 88 | kref_get(&dev->kref); 89 | 90 | /* save our object in the file's private structure */ 91 | file->private_data = dev; 92 | 93 | exit: 94 | return retval; 95 | } 96 | 97 | static int skel_release(struct inode *inode, struct file *file) 98 | { 99 | struct usb_skel *dev; 100 | 101 | dev = (struct usb_skel *)file->private_data; 102 | if (dev == NULL) 103 | return -ENODEV; 104 | 105 | /* decrement the count on our device */ 106 | kref_put(&dev->kref, skel_delete); 107 | return 0; 108 | } 109 | 110 | static ssize_t skel_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 111 | { 112 | struct usb_skel *dev; 113 | int retval = 0; 114 | 115 | dev = (struct usb_skel *)file->private_data; 116 | 117 | /* do a blocking bulk read to get data from the device */ 118 | retval = usb_bulk_msg(dev->udev, 119 | usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), 120 | dev->bulk_in_buffer, 121 | min(dev->bulk_in_size, count), 122 | (int *) &count, HZ*10); 123 | 124 | /* if the read was successful, copy the data to userspace */ 125 | if (!retval) { 126 | if (copy_to_user(buffer, dev->bulk_in_buffer, count)) 127 | retval = -EFAULT; 128 | else 129 | retval = count; 130 | } 131 | 132 | return retval; 133 | } 134 | 135 | static void skel_write_bulk_callback(struct urb *urb) 136 | { 137 | struct usb_skel *dev = urb->context; 138 | 139 | /* sync/async unlink faults aren't errors */ 140 | if (urb->status && 141 | !(urb->status == -ENOENT || 142 | urb->status == -ECONNRESET || 143 | urb->status == -ESHUTDOWN)) { 144 | dev_dbg(&dev->interface->dev, 145 | "%s - nonzero write bulk status received: %d", 146 | __FUNCTION__, urb->status); 147 | } 148 | 149 | /* free up our allocated buffer */ 150 | usb_free_coherent(urb->dev, urb->transfer_buffer_length, 151 | urb->transfer_buffer, urb->transfer_dma); 152 | } 153 | 154 | static ssize_t skel_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos) 155 | { 156 | struct usb_skel *dev; 157 | int retval = 0; 158 | struct urb *urb = NULL; 159 | char *buf = NULL; 160 | 161 | dev = (struct usb_skel *)file->private_data; 162 | 163 | /* verify that we actually have some data to write */ 164 | if (count == 0) 165 | goto exit; 166 | 167 | /* create a urb, and a buffer for it, and copy the data to the urb */ 168 | urb = usb_alloc_urb(0, GFP_KERNEL); 169 | if (!urb) { 170 | retval = -ENOMEM; 171 | goto error; 172 | } 173 | 174 | buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); 175 | if (!buf) { 176 | retval = -ENOMEM; 177 | goto error; 178 | } 179 | if (copy_from_user(buf, user_buffer, count)) { 180 | retval = -EFAULT; 181 | goto error; 182 | } 183 | 184 | /* initialize the urb properly */ 185 | usb_fill_bulk_urb(urb, dev->udev, 186 | usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), 187 | buf, count, skel_write_bulk_callback, dev); 188 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 189 | 190 | /* send the data out the bulk port */ 191 | retval = usb_submit_urb(urb, GFP_KERNEL); 192 | if (retval) { 193 | pr_err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); 194 | goto error; 195 | } 196 | 197 | /* release our reference to this urb, the USB core will eventually free it entirely */ 198 | usb_free_urb(urb); 199 | 200 | exit: 201 | return count; 202 | 203 | error: 204 | usb_free_coherent(dev->udev, count, buf, urb->transfer_dma); 205 | usb_free_urb(urb); 206 | kfree(buf); 207 | return retval; 208 | } 209 | 210 | static struct file_operations skel_fops = { 211 | .owner = THIS_MODULE, 212 | .read = skel_read, 213 | .write = skel_write, 214 | .open = skel_open, 215 | .release = skel_release, 216 | }; 217 | 218 | /* 219 | * usb class driver info in order to get a minor number from the usb core, 220 | * and to have the device registered with devfs and the driver core 221 | */ 222 | static struct usb_class_driver skel_class = { 223 | .name = "usb/skel%d", 224 | .fops = &skel_fops, 225 | .minor_base = USB_SKEL_MINOR_BASE, 226 | }; 227 | 228 | static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id) 229 | { 230 | struct usb_skel *dev = NULL; 231 | struct usb_host_interface *iface_desc; 232 | struct usb_endpoint_descriptor *endpoint; 233 | size_t buffer_size; 234 | int i; 235 | int retval = -ENOMEM; 236 | 237 | /* allocate memory for our device state and initialize it */ 238 | dev = kzalloc(sizeof(struct usb_skel), GFP_KERNEL); 239 | if (!dev) { 240 | pr_err("Out of memory"); 241 | goto error; 242 | } 243 | kref_init(&dev->kref); 244 | 245 | dev->udev = usb_get_dev(interface_to_usbdev(interface)); 246 | dev->interface = interface; 247 | 248 | /* set up the endpoint information */ 249 | /* use only the first bulk-in and bulk-out endpoints */ 250 | iface_desc = interface->cur_altsetting; 251 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 252 | endpoint = &iface_desc->endpoint[i].desc; 253 | 254 | if (!dev->bulk_in_endpointAddr && 255 | (endpoint->bEndpointAddress & USB_DIR_IN) && 256 | ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) 257 | == USB_ENDPOINT_XFER_BULK)) { 258 | /* we found a bulk in endpoint */ 259 | buffer_size = endpoint->wMaxPacketSize; 260 | dev->bulk_in_size = buffer_size; 261 | dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; 262 | dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); 263 | if (!dev->bulk_in_buffer) { 264 | pr_err("Could not allocate bulk_in_buffer"); 265 | goto error; 266 | } 267 | } 268 | 269 | if (!dev->bulk_out_endpointAddr && 270 | !(endpoint->bEndpointAddress & USB_DIR_IN) && 271 | ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) 272 | == USB_ENDPOINT_XFER_BULK)) { 273 | /* we found a bulk out endpoint */ 274 | dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; 275 | } 276 | } 277 | if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { 278 | pr_err("Could not find both bulk-in and bulk-out endpoints"); 279 | goto error; 280 | } 281 | 282 | /* save our data pointer in this interface device */ 283 | usb_set_intfdata(interface, dev); 284 | 285 | /* we can register the device now, as it is ready */ 286 | retval = usb_register_dev(interface, &skel_class); 287 | if (retval) { 288 | /* something prevented us from registering this driver */ 289 | pr_err("Not able to get a minor for this device."); 290 | usb_set_intfdata(interface, NULL); 291 | goto error; 292 | } 293 | 294 | /* let the user know what node this device is now attached to */ 295 | dev_info(&interface->dev, "USB Skeleton device now attached to USBSkel-%d", interface->minor); 296 | return 0; 297 | 298 | error: 299 | if (dev) 300 | kref_put(&dev->kref, skel_delete); 301 | return retval; 302 | } 303 | 304 | static void skel_disconnect(struct usb_interface *interface) 305 | { 306 | struct usb_skel *dev; 307 | int minor = interface->minor; 308 | 309 | dev = usb_get_intfdata(interface); 310 | usb_set_intfdata(interface, NULL); 311 | 312 | /* give back our minor */ 313 | usb_deregister_dev(interface, &skel_class); 314 | 315 | /* decrement our usage count */ 316 | kref_put(&dev->kref, skel_delete); 317 | 318 | dev_info(&interface->dev, "USB Skeleton #%d now disconnected", minor); 319 | } 320 | 321 | static struct usb_driver skel_driver = { 322 | .name = "skeleton", 323 | .id_table = skel_table, 324 | .probe = skel_probe, 325 | .disconnect = skel_disconnect, 326 | }; 327 | 328 | static int __init usb_skel_init(void) 329 | { 330 | int result; 331 | 332 | /* register this driver with the USB subsystem */ 333 | result = usb_register(&skel_driver); 334 | if (result) 335 | pr_err("usb_register failed. Error number %d", result); 336 | 337 | return result; 338 | } 339 | 340 | static void __exit usb_skel_exit(void) 341 | { 342 | /* deregister this driver with the USB subsystem */ 343 | usb_deregister(&skel_driver); 344 | } 345 | 346 | module_init (usb_skel_init); 347 | module_exit (usb_skel_exit); 348 | 349 | MODULE_LICENSE("GPL"); 350 | --------------------------------------------------------------------------------