Development of key-driven instances on 2440

Source: Internet
Author: User
I. Development Environment • Master: virtualbox -- fedora 9 • Development Board: mini2440 -- 128 MB nand, friendly: kernel: 2.6.32.2 • Compiler: arm-linux-gcc-4.4.3

The key program uses a lot of knowledge about the driver. There are interruptions, blocking, waiting queues, and Linux Device Driver models.
The process of using interrupt handling is as follows:

  1. Register with the kernel for interruption
  2. Implement interrupt processing functions.

The interrupted installation function is:

Int request_irq (unsigned int IRQ, irqreturn_t (* Handler) (INT, void *, struct pt_regs *),
  1. Unsigned long flags, const char * dev_name, void * dev_id );

Released functions:

Void free_irq (unsigned int IRQ, void * dev_id );

Here we mainly apply for the interrupt signal line, which is a very valuable resource. When the dev_id parameter in request_irq is used to share the interrupt signal line, because the device that needs to interrupt processing must have more than the interrupt signal line, once a certain device triggers the interrupt, the kernel needs to find the interrupted Device on this interrupt line. Because the interrupt number is the same, dev_id is used to identify that dev_id is unique. Pay attention to releasing valuable resources after use. From the program below, we can see that the registration interruption is open. Why not register the module function? If it is in the registration function, unless the module is uninstalled, in this way, the driver will always occupy the interrupt number, and may not need it, which wastes valuable resources.
The following is a prototype of the interrupt processing function:

Irqreturn_t (* Handler) (INT, void *, struct pt_regs *);

The first parameter is IRQ, the second is dev_id, and the third is struct.
Pt_reg * regs, rarely used, stores the processor context snapshot before the processor enters the interrupted code. Note that the returned value is irq_handled if the handler finds that the device does need to be processed. Otherwise, the returned value is irq_none.

The blocking mechanism is used for reading. That is to say, if the user program cannot obtain data, it will be blocked. At this time, the process enters the sleep state and is moved from the running queue to the waiting queue by the CPU scheduler. In the Linux kernel, blocking is implemented by waiting for the queue. In Linux, a waiting queue passes through a "waiting queue header (wait
Queue head. The waiting queue header is a struct type of wait_queue_head_t, which is defined in <Linux/Wait. H>.

You can use the following method to statically define and initialize a waiting queue:

Declare_wait_queue_head (name );

Or use a dynamic method:

Wait_queue_head_t my_queue; init_waitqueue_head (& my_queue );

The sleep mode in the Linux kernel is to use the wait_event macro, as shown below:

Wait_event (queue, condition );
  1. Wait_event_interruptible (queue, condition );
  2. Wait_event_timeout (queue, condition, timeout );
  3. Wait_event_interruptible_timeout (queue, condition, timeout );

The queue is the waiting queue header, and the condition is the waiting condition. If the condition is 0, it is blocked. Otherwise, it is not blocked. The difference between the wait_event_interruptible macro and the wait_event macro is that wait_event_interruptible can be interrupted by signals. When a process sleep, it will expect a condition to become true in the future; when a process is awakened, it must detect again that the conditions it waits for are true.
Functions used to wake up the waiting queue:

[Void wake_up (wait_queue_head_t * Queue );
  1. Void wake_up_interruptible (wait_queue_head_t * Queue );

Wake_up will wake up all processes waiting for a given queue. Wake_up_interruptible only wakes the processes that can interrupt sleep.

II. Implementation Step 1. Hardware Principle Analysis the principle diagram shows that the external interruptions used by each button are eint8, eint11, eint13, eint14, eint15,

Eint19, the corresponding Io ports are gpg0, gpg3, gpg5, gpg6, gpg7, and gpg11. According to the interface circuit of the buttons, when the buttons are pressed

When you press the next button to connect, the original vdd33v high level of the disconnection is pulled low, thus triggering the interruption.

2. Write a key driver suitable for mini2440. My2440_buttons.c1. Specify the master device number as 232 (in fact, this device number should be written as a dynamic allocation. Here is just an exercise to specify a fixed master device number for simplicity) # include <Linux/kernel. h>

# Include <Linux/module. h> # include <Linux/init. h> # include <Linux/Fs. h> # include <Linux/errno. h> # include <Mach/gpio. h> # include <Mach/hardware. h> # include <Linux/poll. h> // the header file for interruption # include <Linux/interrupt. h> # include <Linux/IRQ. h> # include <ASM/IRQ. h> # include <Linux/list. h> # include <Linux/stddef. h> # include <Linux/spinlock. h> # include <ASM/system. h> # include <ASM/current. h> # include <Linux/sched. h> # Define device_name "my2440_buttons" // decice name # define device_major 231 // major device number # define key_down 0 // press the key # define key_up 1 // press the key to raise # define key_uncertain 2 // press the key uncertain # define key_count 6 // press the six keys static volatile int key_status [key_count]; // record the status of the six buttons static struct timer_list key_timers [key_count]; // the timer for 6 keys to shake # define key_timer_delay1 (Hz/50) // press the button to shake down for 20 milliseconds # define key_timer_delay2 (Hz/10) // press the button to lift the jitter. The latency is 100 milliseconds. Static volatile int ev_press = 0; // press the button to generate an identifier, which is used to determine whether data is readable when reading the device, otherwise, the process sleeps static declare_wait_queue_head (button_waitq); // wait for the definition of the queue and initialize // The description structure of the key: struct button_irq_desc {int IRQ; // IRQ Number int pin; // The IO pin int pin_setting; // Io configure char * Name; // button name}; // define 6 button resource arraystatic struct button_irq_desc buttons_irq [] ={{ irq_eint8, s3c2410_gpg (0), S Second, "key0" },{ irq_eint11, s3c2410_gpg (3), second, "key1" },{ irq_eint13, s3c2410_gpg (5), second, "key2" },{ irq_eint14, s3c2410_gpg (6), s3c2410_gpg6_eint14, "key3" },{ irq_eint15, s3c2410_gpg (7), kernel, "key4" },{ irq_eint19, s3c2410_gpg (11), kernel, "key5" },}; // interrupt handle functionstatic irqreturn_t buttons_interrupt (Int irq, void * dev_id) {// obtain the index of the current key resource int key = (INT) dev_id; // If (key_status [Key] = key_up) is interrupted only when the status of the current key is determined to be lifted {// set the status of the current key to key_status [Key] = key_uncertain; // set the delay of the timer when the current button is pressed down and start the timer key_timers [Key]. expires = jiffies + keys; add_timer (& key_timers [Key]);} return irq_retval (irq_handled);} // timer deal with functionstatic void buttons_timer (unsigned long Arg) {// retrieve the index int of the current key resource Key = ARG; // obtain the level value on the current key pin to determine whether the key is pressed or lifted int up = s3c2410_gpio_getpin (buttons_irq [Key]. Pin); If (! Up) // low level, press {If (key_status [Key] = key_uncertain) {// to identify the current key status as pressing key_status [Key] = key_down; // identify that the current button has been pressed and wake up waiting for the device to read ev_press = 1; wake_up_interruptible (& button_waitq );} // set the delay of the timer when the current button is lifted and the timer key_timers [Key] is started. expires = jiffies + key_timer_delay2; add_timer (& key_timers [Key]);} else // high level, press the button to raise {// identify the current button status to raise key_status [Key] = key_up;} static int buttons_read (struct file * file, char _ Use R * Buf, size_t count, loff_t * OFFP) {unsigned long ret; If (! Ev_press) // The identifier is generated when the key is pressed. 0 does not occur. {If (file-> f_flags & o_nonblock) {// if the application uses non-blocking reading, return-eagain;} else {// read in blocking mode and the key is not pressed, let the waiting queue enter sleep wait_event_interruptible (button_waitq, ev_press) ;}// 1 is generated by pressing the key, and the mark is cleared as 0. Prepare to use ev_press = 0 for the next judgment; // copy the key status data in the kernel to the user space for the application to use ret = copy_to_user (BUF, (void *) key_status, min (sizeof (key_status), count )); return ret? -Efault: min (sizeof (key_status), count);} // polling in the driver, used for polling in applications to query whether devices can be accessed static int buttons_poll (struct file * file, struct poll_table_struct * Wait) {unsigned int mask = 0; // Add a waiting queue to the waiting queue table (poll_table) poll_wait (file, & button_waitq, wait); If (ev_press) {// The ID data can obtain the mask | = Pollin | pollrdnorm; // set some flag spaces. Select to use} return mask;} static int buttons_close (struct inode * inode, struct file * file) {Int I; // release 6 timers and interrupts for (I = 0; I <key_count; I ++) {del_timer (& key_timers [I]); disable_irq (buttons_irq [I]. IRQ); free_irq (buttons_irq [I]. IRQ, (void *) I);} return 0;} static int buttons_open (struct inode * inode, struct file * file) {int I; int ret; for (I = 0; I <key_count; I ++) {// set six Io ports to the interrupt trigger mode: s3c2410_gpio_cfgpin (buttons_irq [I]. pin, buttons_irq [I]. pin_setting); // set the drop edge of the interrupt to set_irq_type (Tons_irq [I]. IRQ, irq_type_edge_falling); // request for interruption (type: Fast interruption. Will all external interruptions be blocked when service interruption occurs ?) Ret = request_irq (buttons_irq [I]. IRQ, buttons_interrupt, ir1__disabled, buttons_irq [I]. name, (void *) I); If (RET) {break;} // The status of the six buttons is raised to key_status [I] = key_up; // initialize and set six deshake timers setup_timer (& key_timers [I], buttons_timer, I);} If (RET) {// process I --; for (; I> = 0; I --) {// release the registered disable_irq (buttons_irq [I]. IRQ); free_irq (buttons_irq [I]. IRQ, (void *) I);} return-ebusy;} return 0;} St Atic int _ init button_init (void) {int ret; // register char device ret = register_chrdev (device_major, device_name, & button_fops); If (Ret <0) {printk (device_name "register faild! \ N "); return ret;} return 0;} static void _ exit button_exit (void) {unregister_chrdev (device_major, device_name);} struct file_operations button_fops = {. owner = this_module ,. open = buttons_open ,. release = buttons_close ,. read = buttons_read ,. poll = buttons_poll,}; module_init (button_init); module_exit (button_exit );

Module_license ("GPL ");

2) driver test program # include <stdio. h>

# Include <stdlib. h> # include <error. h> int main (INT argc, char ** argv) {int FD; int key_status [6]; // open device block FD = open ("/dev/my2440_buttons ", 0); If (FD <0) {printf ("Open buttons devices faild! \ N "); exit (1) ;}while (1) {int I; int ret; fd_set RDS; fd_zero (& RDs); fd_set (FD, & RDs ); // The application performs Round Robin to check whether devices can be accessed. ret = select (FD + 1, & RDS, null); If (Ret <0) {printf ("read buttons device faild! \ N "); exit (1) ;}if (ret = 0) {printf (" read buttons device timeout! \ N ");} else if (fd_isset (FD, & RDs) {// Read Device ret = read (FD, key_status, sizeof (key_status); If (Ret! = Sizeof (key_status) {If (errno! = Eagain) {printf ("read button device faild! \ N ") ;}continue;} else {for (I = 0; I <6; I ++) {// The status of the buttons in the driver, 0 indicates that the key is pressed if (key_status [I] = 0) {printf ("Key % d down \ n", I + 1 );}}}} close (FD); Return 0;

}

Iii. Summary This instance is very useful, and it uses many things that will be used in the future, such as timer and interrupt configuration and use .. If you use this part of content in the future, you can directly use it for reference.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.