***************************************************************************** * MAKING A MODULE I/O DRIVER * ***************************************************************************** We shall use as example the same I/O driver for the /dev/lp_led device that was implemented statically inside the kernel (see patch-2.4.16kh9). ***************************************************************************** STEP 0: set the proper EXTRAVERSION value in Makefile ***************************************************************************** replace: EXTRAVERSION = with: EXTRAVERSION = kh10 ***************************************************************************** STEP 1: modify the linux/arch/i386/config.in file ***************************************************************************** add the following lines in the 'Kernel hacking' main menu: # patch to add a new I/O driver for the lp_led device tristate 'I/O driver for lp_led' CONFIG_LP_LED ***************************************************************************** STEP 2: Add a new kernel hacking configuration option ***************************************************************************** add in linux/Documentation/Configure.help the following lines right after CONFIG_MAGIC_SYSRQ: Support for lp_led device CONFIG_LP_LED If you say Y here, you will have an I/O driver capable of handling the lp_led device connected to the parallel port. **************************************************************************** STEP 3: create a new directory linux/new_iodrivers that will contain the new lp_led.o object file of the I/O driver routines ***************************************************************************** mkdir new_iodrivers ***************************************************************************** STEP 4: modify the main linux/Makefile ***************************************************************************** a) replace CORE_FILES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o new_syscalls/new_syscalls.o with: CORE_FILES =kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o new_syscalls/new_syscalls.o new_iodrivers/new_iodrivers.o b) replace: SUBDIRS =kernel drivers mm fs net ipc lib with: SUBDIRS =kernel drivers mm fs net ipc lib new_iodrivers ***************************************************************************** STEP 5: Reserve a major number for the new lp_led I/O device ***************************************************************************** Finding a stable free major number is not easy. Do not use high numbers such as 254 or similar because they may be used before our device is registered, when performing dynamic assignment of free major numbers. We use 42 described as follows in the linux/Documentation/devices.txt file: 42 Demo/sample use This number is intended for use in sample code, as well as a general "example" device number. It should never be used for a device driver that is being distributed; either obtain an official number or use the local/experimental range. The sudden addition or removal of a driver with this number should not cause ill effects to the system (bugs excepted.) We thus add the following lines in the include/linux/major.h file: #ifdef CONFIG_LP_LED #define LP_LED_MAJOR 42 #endif ***************************************************************************** STEP 6: Define a new lp_led char device file with major number 42 in the /dev directory ***************************************************************************** issue as superuser the following command: mknod /dev/lp_led c 42 0 ***************************************************************************** STEP 7: Create the Makefile for the linux/new_iodrivers directory ***************************************************************************** # We only get in/to here if CONFIG_LP_LED = 'y' or 'm' O_TARGET := new_iodrivers.o obj-m := obj-y := ifeq ($(CONFIG_LP_LED),y) obj-y += lp_led.o else ifeq ($(CONFIG_LP_LED),m) obj-m += lp_led.o endif endif include $(TOPDIR)/Rules.make ***************************************************************************** STEP 8: Add the lp_led.c file in the new_IOdrivers directory ***************************************************************************** /* lp_led.c */ /* */ /* This file includes all the routines needed to drive the */ /* lp_led I/O device */ #include /* THIS_MODULE, EXPORT_NO_SYMBOLS */ #include /* LP_LED_MAJOR */ #include /* inb(), outb() */ /* Addresses of the parallel port */ #define PData 0x378 #define PInput 0x379 #define PControl 0x37a static int cc; static int lp_led_open(struct inode *inode, struct file *file) { outb(0x00, PData); /* switch off the data byte (green leds) */ outb(0x04, PControl); /* switch on the power on bit (yellow led) */ return 0; } static int lp_led_close(struct inode *inode, struct file *file) { outb(0xff, PData); /* switch on the data byte (green leds) */ return 0; } static ssize_t lp_led_read(struct file *file, char *buf, size_t count, loff_t *ppos) { if (count == 1) { *buf = inb(PInput); cc = 1; } else { printk("KERNEL-NOTICE: when reading from lp_led count must be set to 1\n"); cc = -1; } return cc; } static ssize_t lp_led_write(struct file *file, char *buf, size_t count, loff_t *ppos) { if (count == 1) { outb((*buf & 0xff), PData); cc = 1; } else cc = -1; return cc; } /* this struct must appear in the code AFTER the definition of the file operations */ static struct file_operations lp_led_fops = { owner: THIS_MODULE, read: lp_led_read, write: lp_led_write, open: lp_led_open, release: lp_led_close, }; int lp_led_init(void) { cc = register_chrdev(LP_LED_MAJOR, "lp_led_card", &lp_led_fops); if (cc < 0) printk("KERNEL-NOTICE: lp_led init error %d\n", cc); return cc; } #ifdef MODULE int init_module(void) { EXPORT_NO_SYMBOLS; printk("KERNEL-NOTICE: loading the lp_led module\n"); cc = lp_led_init(); return cc; } int cleanup_module(void) { printk("KERNEL-NOTICE: unloading the lp_led module\n"); cc = unregister_chrdev(LP_LED_MAJOR, "lp_led_card"); if (cc < 0) printk("KERNEL-NOTICE: lp_led init error %d\n", cc); return cc; } #endif ***************************************************************************** STEP 9: Test the I/O driver module: ***************************************************************************** a) run make menuconfig and set to 'm' the CONFIG_LP_LED kernel hackers option b) run make dep, recompile the kernel and copy the arch/i386/boot/bzImage in /boot/vmlinuz-2.4.16kh10 or on a floppy c) run make modules and make modules_install d) run lilo and reboot the system and select linux-2.4.16kh10 as the system to boot e) login as root and insert the module: insmod lp_led.o f) test whether it has been loaded correctly by typing: lsmod lp_led.o g) run the Tlp_led test program shown in Making_an_I/Odriver.txt file