Address: http://www.cnblogs.com/nick123/archive/2010/03/26/1696966.html
The following are drivers:
/* The driver runs on the tq2440 Development Board and kernel version 2.6.31.6 */
# 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/Io. h> # include <Linux/interrupt. h> # include <ASM/uaccess. h> # include <Mach/regs-clock.h> # include <plat/regs-adc.h> # include <Mach/hardware. h> # include <Linux/platform_device.h> # include <Linux/CD Ev. h> # include <Linux/miscdevice. h> # define device_name "tq2440-buttons" # define dev_count 1 # define button_major 0 # define button_minor 0 # define min (a, B) (a)> (B )? (B) :( A)/* waiting queue: * when no key is pressed, if a process calls the tq2440_buttons_read function *, it will sleep */static declare_wait_queue_head (button_waitq ); /* indicates the interrupt event. The interrupt service sets it to 1, and tq2440_buttons_read clears it to 0 */static volatile int ev_press = 0;/* indicates the number of times the key is pressed (to be precise, is the number of interruptions) */static volatile int press_cnt [] = {0, 0, 0}; static struct class * button_class; struct button_irqs_desc {int IRQ; // interrupt No. Unsigned long flags; // interrupt flag, used to define the trigger method of the interrupt char * Name; // interrupt name};/* used to specify the button The External Interrupt pin and the interrupt trigger method. The name is */static struct button_irqs_desc button_irqs [] ={{ irq_eint0, middle, "key1"}, // K1 {irq_eint1, middle, "key2"}, // K2 {irq_eint2, irq_type_edge_falling, "key3"}, // K3 {irq_eint4, keys, "key4"}, // K4}; dev_t dev_num; static struct cdev * buttons_cdev_p; // cdev struct pointer/* uninstall driver */static void _ exit tq2440_buttons_exit (void) {cdev_del (buttons_cdev_p); // deregister Cdev kfree (buttons_cdev_p); // release the memory of the device struct (dev_num, dev_count); // release the device number} static irqreturn_t buttons_interrupt (int irq, void * dev_id) {volatile int * press_cnt = (volatile int *) dev_id; * press_cnt = * press_cnt + 1;/* Add 1 */ev_press = 1 to the key counter; /* indicates that */wake_up_interruptible (& button_waitq) is interrupted;/* wake up the sleeping process */printk ("IRQ: % d \ n", IRQ ); return irq_retval (irq_handled);}/* The application executes open ("/dev/butto NS ",...) during system calling, the tq2440_buttons_open function will be called *, which is used to register the interrupt handler for four buttons */static int tq2440_buttons_open (struct inode * inode, struct file * file) {int I; int err; for (I = 0; I <sizeof (button_irqs)/sizeof (button_irqs [0]); I ++) {// register the interrupt handler err = request_irq (button_irqs [I]. IRQ, buttons_interrupt, button_irqs [I]. flags, button_irqs [I]. name, (void *) & press_cnt [I]); If (ERR) break;} If (ERR) {// if an error occurs, release the registered interrupt I --; for (; I> = 0; I --) Free_irq (button_irqs [I]. IRQ, (void *) & press_cnt [I]); Return-ebusy;} return 0;}/* The application executes close (...) on the device file/dev/buttons (...) * The tq2440_buttons_close function */static int tq2440_buttons_close (struct inode * inode, struct file * file) {int I; for (I = 0; I <sizeof (button_irqs) /sizeof (button_irqs [0]); I ++) {// release the registered interrupt free_irq (button_irqs [I]. IRQ, (void *) & press_cnt [I]);} return 0;}/* The application executes read (...) on the device file/dev/buttons (...) * The tq2440_buttons_read function */static int tq2440_buttons_read (struct file * filp, char _ User * buff, size_t count, loff_t * OFFP) {unsigned long err; /* If ev_press is equal to 0, sleep */wait_event_interruptible (button_waitq, ev_press);/* when it is executed, ev_press must be equal to 1 and clear it 0 */ev_press = 0; /* copy the key state to the user, and ask 0 */err = copy_to_user (buff, (const void *) press_cnt, min (sizeof (press_cnt), count )); memset (void *) press_cnt, 0, sizeof (Press _ CNT); Return err? -Efault: 0;}/* this structure is the core of the character driver program * the open, read, write and other functions * will eventually call the corresponding function in this structure */static struct file_operations tq2440_buttons_fops = {. owner = this_module,/* This is a macro that points to the _ this_module variable automatically created during compilation */. open = tq2440_buttons_open ,. release = tq2440_buttons_close ,. read = tq2440_buttons_read,};/* initialize and register cdev */static void buttons_cdev_setup (void) {int err; cdev_init (buttons_cdev_p, & tq2440_buttons_fops ); Buttons_cdev_p-> owner = this_module; buttons_cdev_p-> Ops = & role; err = cdev_add (buttons_cdev_p, dev_num, 1); If (is_err (& ERR )) printk (kern_notice "error % d adding buttons", err);} static int _ init tq2440_buttons_init (void) {int ret; /* register the character device driver * parameter as the main device number, device name, And file_operations struct *. In this way, the main device number is associated with the specific file_operations struct, * When you operate the device file whose main device number is button_major, the related member functions in * tq2440_buttons_fops will be called, button_maj Or can be set to 0, * indicates that the kernel automatically allocates the master device Number */If (button_major) // manually allocates the device number {dev_num = mkdev (button_major, button_minor ); ret = register_chrdev_region (dev_num, dev_count, device_name);} else {// dynamically allocates the device number ret = alloc_chrdev_region (& dev_num, button_minor, dev_count, device_name );} if (Ret <0) {printk (device_name "can't register major number \ n"); return ret ;} /* dynamically apply the memory of the cdev struct */buttons_cdev_p = kmalloc (sizeof (struct cdev _ Kernel); If (! Buttons_cdev_p) // application failed {ret =-enomem; goto fial_malloc;} memset (buttons_cdev_p, 0, sizeof (struct cdev); buttons_cdev_setup (); // register a class, enable mdev to create a device node button_class = class_create (this_module, device_name) under the "/dev/" Directory; If (is_err (button_class) {printk ("error: failed to creat button_class \ n "); Return-1 ;}// create a device node named device_name device_create (button_class, null, dev_num, null, device_name ); printk (device_name "initialized \ n"); Return 0; fial_malloc: unregister_chrdev_region (dev_num, 1 );} /* initialize and uninstall the driver in these two lines */module_init (tq2440_buttons_init); module_exit (tq2440_buttons_exit)
The following is the test procedure:
# Include <stdio. h> # include <stdlib. h> # include <unistd. h> # include <sys/IOCTL. h> int main (INT argc, char ** argv) {int I; int ret; int FD; int press_cnt [4]; FD = open ("/dev/tq2440-buttons ", 0); If (FD <0) {printf ("can't open/dev/tq2440-buttons \ n"); Return-1 ;}// this is an infinite loop, the process may sleep in the READ function. When a key is pressed, // It returns while (1) {ret = read (FD, press_cnt, sizeof (press_cnt )); if (Ret <0) {printf ("read Err! \ N "); continue;} // if the number of times pressed is not 0, print it out for (I = 0; I <sizeof (press_cnt) /sizeof (press_cnt [0]); I ++) {If (press_cnt [I]) printf ("Key % d has been pressed % d times \ n ", I + 1, press_cnt [I]) ;}// while}
Go to the dev directory of the root file system and find that the device has been successfully allocated and the file node is automatically created.
[Nick @ tq2440/dev] # ls-l TQ *
CrW-RW ---- 1 0 0 232, 0 Jan 1 tq2440-adc
CrW-RW ---- 1 0 0 254, 0 Jan 1 tq2440-buttons
CrW-RW ---- 1 0 0 231, 0 Jan 1 tq2440-leds
CrW-RW ---- 1 0 0 204, 64 Jan 1 tq2440_serial0
CrW-RW ---- 1 0 0 204, 65 Jan 1 00:00 tq2440_serial1
CrW-RW ---- 1 0 0 204, 66 Jan 1 00:00 tq2440_serial
After downloading the kernel, run the test program.
[Nick @ tq2440/APP] #./buttons_test &
Check that the interrupt has been registered successfully.
[Nick @ tq2440/proc] # Cat interrupts
Cpu0
16: 0 s3c-ext0 key1
17: 0 s3c-ext0 key2
18: 0 s3c-ext0 key3
30: 27496 s3c S3C2410 timer tick
S3c s3c2410-lcd 32: 0
S3c s3c2440-i2c 43: 0
48: 0 s3c-ext key4
51: 2587 s3c-ext eth0
70: 156 s3c-uart0 s3c2440-uart
71: 1839 s3c-uart0 s3c2440-uart
83: 0-s3c2410-wdt
Err: 0
The first column indicates the interrupt number.
The second column indicates the number of times the interrupt occurred.
The third column indicates the name of the interrupted hardware access structure "struct irq_chip * chip", which is specified when initializing the interrupt Architecture
The fourth column indicates the interrupt name.
Press the button and the output is as follows:
IRQ: 48
Key4 has been pressed 1 times
IRQ: 16
Key1 has been pressed 1 times
IRQ: 18
Key3 has been pressed 1 times
IRQ: 48
Key4 has been pressed 1 times
IRQ: 16
Key1 has been pressed 1 times
IRQ: 17
Key2 has been pressed 1 times