Interrupt-based key driver instance
1. circuit diagram:
2. Driver
2.1 driver source code int_key \ completion \ driver \ int_key_drv.c
# Include <Linux/module. h> # include <Linux/kernel. h> # include <Linux/Fs. h> # include <Linux/init. h> # include <Linux/delay. h> # include <Linux/poll. h> # include <Linux/IRQ. h> # include <ASM/IRQ. h> # include <Linux/Wait. h> # include <Linux/sched. h> # include <Linux/interrupt. h> # include <ASM/uaccess. h> # include <Linux/gpio. h> # include <Mach/regs-gpio.h> # include <Mach/hardware. h >#include <Linux/platform_device.h> # include <Linux/cdev. h> # include <Linux/miscdevice. h> # define using_taskletstatic int key_major = 0; struct key_irq_desc {// defines the struct unsigned int IRQ; /* The IRQ Number corresponding to the key. This value has been defined in the kernel header file */INT pin;/* The Pin corresponding to */INT pin_setting;/* The reason why the PIN is set, the following uses the external interrupt mode */INT number;/* consistent with the following table in the array */char * Name;/* name */}; /* specifies the External Interrupt pin used by the buttons and the interrupt trigger method. The value is */static struct key_irq_desc key_irqs [] ={{ irq_eint8, s3c2410_gpg (0), s3c2410_gpg0_eint8, 0 Ey1 "},/* K1 */{irq_eint11, s3c2410_gpg (3), s3c2410_gpg3_eint11, 1," key2 "},/* K2 */{irq_eint13, s3c2410_gpg (5 ), keys, 2, "key3"},/* K3 */{irq_eint14, s3c2410_gpg (6), s3c2410_gpg6_eint14, 3, "key4"},/* K3 */{irq_eint15, s3c2410_gpg (7), s3c2410_gpg7_eint15, 4, "key5"},/* K3 */{irq_eint19, s3c2410_gpg (11), s3c2410_gpg11_eint19, 5, "key6 "}, /* K4 */};/* Number of times the button is pressed (accurately, the number of times the button is interrupted) */Static volatile int key_values [] = {0, 0, 0, 0, 0};/* waiting queue: * when no buttons are pressed, if a process calls the key_read function, * It will sleep */static declare_wait_queue_head (key_waitq); // initialize a wait queue header key_waitq/* interrupt event flag, interrupt the service program to set it to 1, key_read clear it 0 */static volatile int ev_press = 0;/* mark whether the key is pressed */# ifdef using_taskletstatic struct tasklet_struct key_tasklet; /* define a task queue */static void key_do_tasklet (unsigned long);/* function declaration * // declare_tasklet (Key _ Tasklet, key_do_tasklet, 0); static void key_do_tasklet (unsigned long data)/* lower half function */{printk ("key_do_tasklet \ n ");} # endifstatic irqreturn_t key_interrupt (int irq, void * dev_id) {struct key_irq_desc * key_irqs = (struct failed *) dev_id; int up = encrypt (key_irqs-> pin ); // ARCH/ARM/plat-s3c24xx/gpio. c/* read key status press or raise */printk ("<1> up = % d \ n", up); If (up) key_values [key_irqs-> Number] = (key_irqs-> Number + 1) + 0x80; elsekey_values [key_irqs-> Number] + = 1; ev_press = 1;/* indicates that the interrupt occurred */wake_up_interruptible (& key_waitq ); /* wake up the sleep process */# ifdef using_tasklettasklet_schedule (& key_tasklet); # endif return irq_retval (irq_handled );} /* The application executes open (...) on the device file/dev/key (...) * The key_open function */static int key_open (struct inode * inode, struct file * file) {int I; int err; for (I = 0; I <sizeof (key_irqs)/sizeof (key_ir QS [0]); I ++) {// registers the interrupt handler function s3c2410_gpio_cfgpin (key_irqs [I]. pin, key_irqs [I]. pin_setting); err = request_irq (key_irqs [I]. IRQ, key_interrupt, 0,/* apply for interrupted sharing? */Key_irqs [I]. name, (void *) & key_irqs [I]);/* (void *) & key_irqs [I] transfer the struct address */set_irq_type (key_irqs [I]. IRQ, irq_type_edge_falling); // <Linux/IRQ. h> set the trigger mode. Here, set the trigger mode to descent edge. // irq_type_edge_rising, irq_type_edge_both, irq_type_edge_high, // irq_type_edge_low if (ERR) break) {// release the registered interrupt I --; For (; I> = 0; I --) {disable_irq (key_irqs [I]. IRQ); free_irq (key_irqs [I]. IRQ, (void *) & key_irqs [I]); // (void *) & Key_irqs [I] forced conversion type} return-ebusy;} return 0;}/* The application executes close (...) on the device file/dev/key (...) * The key_close function */static int key_close (struct inode * inode, struct file * file) {int I; for (I = 0; I <sizeof (key_irqs) /sizeof (key_irqs [0]); I ++) {// release the registered disable_irq (key_irqs [I]. IRQ);/* forbidden interrupt */free_irq (key_irqs [I]. IRQ, (void *) & key_irqs [I]);/* release interrupt Number */} return 0 ;} /* The application executes read (...) on the device file/dev/key (...) * Will call key_r EAD function */static int key_read (struct file * filp, char _ User * buff, size_t count, loff_t * OFFP) {unsigned long err; If (! Ev_press) {If (filp-> f_flags & o_nonblock)/* non-blocking */Return-eagain; else/* If ev_press is equal to 0, sleep until key_waitq is awakened, and ev_press is true */wait_event_interruptible (key_waitq, ev_press);/* is blocked */}/* when it is executed here, ev_press is equal to 1, clear it 0 */ev_press = 0;/* copy the key state to the user, and clear 0 */err = copy_to_user (buff, (const void *) key_values, min (sizeof (key_values), count); memset (void *) key_values, 0, sizeof (key_values); Return err? -Efault: min (sizeof (key_values), count );} /*************************************** * *********** when a user program calls the select function, this function is called * If key data exists, the Select function immediately returns * if no key data exists, this function uses poll_wait to wait *********************************** * *************/static unsigned int key_poll (struct file * file, struct poll_table_struct * Wait)/* implement non-blocking round robin */{unsigned int mask = 0; poll_wait (file, & key_waitq, wait ); /* Add key_waitq to the waiting queue */If (ev_press) mask | = Pollin | pollrdnorm; return mask ;} /* this structure is the core of the character device driver * Open, read, write, and other functions called when the application operates the device file, * the corresponding function */static struct file_operations key_fops = {will be called eventually {. owner = this_module,/* This is a macro that points to the _ this_module variable automatically created during compilation */. open = key_open ,. release = key_close ,. read = key_read ,. poll = key_poll,};/** set up the cdev structure for a device. */static void key_setup_cdev (struct cdev * Dev, int minor, struct file_operations * FoPs) {int err, devno = mkdev (key_major, minor); cdev_init (Dev, fops ); dev-> owner = this_module; Dev-> Ops = fops; err = cdev_add (Dev, devno, 1);/* fail gracefully if need be */If (ERR) printk (kern_notice "error % d adding key % d", err, minor);}/** we export one key device. there's no need for us to maintain any * Special housekeeping info, so we just deal with raw cdev. */static struct cdev key_cdev;/** this function is called when you execute the "insmod key_drv.ko" command */static int _ init userkey_init (void) // static int key_init (void) {int result; dev_t Dev = mkdev (key_major, 0); char dev_name [] = "key";/* figure out our device number. */If (key_major) Result = register_chrdev_region (Dev, 1, dev_name); else {result = alloc_chrdev_region (& Dev, 0, 1, dev_name ); key_major = major (Dev);} If (result <0) {printk (kern_warning "key: Unable to get Major % d \ n", key_major); return result ;} if (key_major = 0) key_major = result; # ifdef using_tasklettasklet_init (& key_tasklet, key_do_tasklet, 0); # endif/* now set up cdev. */key_setup_cdev (& key_cdev, 0, & key_fops); printk ("key device installed, with major % d \ n", key_major); printk ("the device name is: /dev/% s \ n ", dev_name); Return 0 ;} /** call this function when executing the rmod key_drv command */static void _ exit userkey_exit (void) // static void key_exit (void) {cdev_del (& key_cdev ); unregister_chrdev_region (mkdev (key_major, 0), 1); # ifdef using_tasklettasklet_kill (& key_tasklet); # endif printk ("key device uninstalled \ n ");} /* specify the initialization and uninstallation functions of the driver */module_init (userkey_init); module_exit (userkey_exit);/* describes some information about the driver, not required */module_author ("Hanson he"); // The driver author module_description ("S3C2410/S3C2440 key driver "); // some descriptive information module_license ("dual BSD/GPL"); // The following protocol
2.2makefile
ifeq ($(KERNELRELEASE),)#KERNELDIR ?= /your/target/source/directory/KERNELDIR ?=/home/student/linux-2.6.32.2PWD := $(shell pwd)modules:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesmodules_install:$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_installclean:rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions.PHONY: modules modules_install cleanelse obj-m := int_key_drv.oendif
3. 1 user program
3.1 User source code int_key \ completion \ User \ key_test.c
# Include <stdio. h> # include <stdlib. h> # include <unistd. h> # include <sys/IOCTL. h> # include <sys/types. h> # include <sys/STAT. h> # include <fcntl. h> # include <sys/select. h> # include <sys/time. h> # include <errno. h> int main (void) {int I; int key_fd; int key_value [] = {0, 0, 0, 0 }; /* Open the keyboard Device File */key_fd = open ("/dev/Key", 0); If (key_fd <0) {perror ("open device key "); exit (1) ;}for (;) {fd_set RDS;/* fd_set data structure, which is actually a long class * // * Can each array element be associated with an open file sentence? // * (Whether it is a socket handle, or another file or named pipe or device handle) establish a connection * // * the programmer completes the connection establishment. When select () is called, * // * the content of fd_set is modified by the kernel according to the IO status, so as to notify the select () process * // * which socket or file has a readable or writable event. */Int ret; fd_zero (& RDs);/* zeroes the RDS so that the set does not contain any key_fd */fd_set (key_fd, & RDs ); // place the key_fd in the read collection/* place the key_fd In the RDS collection ** // use the System Call select to check whether data can be read from the/dev/key device */ret = select (key_fd + 1, & RDS, null);/* exit the program when an error occurs in Reading */If (Ret <0) {perror ("select"); exit (1 );} if (ret = 0) {printf ("timeout. \ n ");}/* can read data */else if (fd_isset (key_fd, & RDs) {/* after the select () function is called, use fd_isset to check whether the status of key_fd changes in the rdst set. Return an integer * // *. If the FD status changes, the system returns true. Otherwise, Returns false (0) * // * to start reading the data sent by the keyboard driver. Note that the key_value and the keyboard driver are of the same type. */int ret = read (key_fd, key_value, sizeof key_value); If (Ret! = Sizeof key_value) {If (errno! = Eagain) perror ("read key \ n"); continue;} else {/* print key value */for (I = 0; I <6; I ++) printf ("K % d % s, key value = 0x % 02x \ n", \ I + 1, (key_value [I] & 0x80 )? "Released": \ key_value [I]? "Pressed down": "", key_value [I]); key_value [I] = 0 ;}}/* close the device file handle */close (key_fd ); return 0 ;}
3.2makefile
KERNELDIR ?=/opt/linux-2.6.34/includeall: key_test key_test : key_test.c#arm-linux-gcc -I$(KERNELDIR) -s -Wl,-warn-common --static -o $@ $^arm-linux-gcc -I$(KERNELDIR) -o $@ $^clean :rm key_test
4. Pay attention to the skills used above.
Printf ("K % d % s, key value = 0x % 02x \ n ",\
I + 1, (key_value [I] & 0x80 )? "Released": key_value [I]? "Pressed down": "", key_value [I]);
Similar to: # define min (A, B) (a) <= (B )? (A) (B ))
Statement: This article is not original and organized from application embedding.