If you want to apply the program to a specific project, we recommend that you Replace "_ udelay (50000)" in the interrupt handler "button_irq ()" with a kernel timer.
Key schematic
// -------------------------------------------- Driver -----------------------------------------------------
// # Include <Linux/config. h>
# Include <Linux/module. h>
# Include <Linux/version. h>
# Include <Linux/kernel. h>
# Include <Linux/init. h>
# Include <Linux/fs. h>
# Include <Linux/interrupt. h>
# Include <Linux/time. h>
# Include <Linux/spinlock. h>
# Include <Linux/IRQ. h>
# Include <ASM/hardware. h>
# Include <ASM/delay. h>
# Include <ASM/uaccess. h>
# Include <ASM-arm/arch-s3c2410/regs-gpio.h>
# Include <ASM/IO. h>
# Include <ASM/uaccess. h>
# Include <ASM-arm/arch-s3c2410/irqs. h>
# Include <ASM-arm/IRQ. h>
# Include <Linux/module. h>
# Include <Linux/init. h>
# Include <Linux/fs. h>
# Include <Linux/cdev. h>
# Include <Linux/IOCTL. h>
# Include <ASM/uaccess. h>
# Include <ASM/hardware. h>
# Include <ASM/ARCH/regs-gpio.h>
# Define device_name "button"
# Define max_key_count 32
# Define extint0 * (volatile unsigned int *) s3c2410_extint0
# Define extint1 * (volatile unsigned int *) s3c2410_extint1
# Define extint2 * (volatile unsigned int *) s3c2410_extint2
Module_license ("GPL"); // The module should specify the license used by the code
Typedef struct
{
Unsigned long Jiffy [max_key_count]; // key time. If the key is read, the ammonium key earlier than 5 seconds is invalid.
Unsigned char Buf [max_key_count]; // Key Buffer
Unsigned int head, tail; // Key Buffer header and tail
} Key_buffer;
Static key_buffer g_keybuffer; // Keyboard Buffer
Static spinlock_t buffer_lock; // buffer lock
Static int button_major = 255; // define device major add by Yoyo
Static void * gpecon;
Static void * gpedat;
Static void * gpfcon;
Static void * gpfdat;
Static void * gpgcon;
Static void * gpgdat;
/*
* Function: Get the current number of milliseconds (starting from system startup)
* Entry:
*/
Static unsigned long gettickcount (void)
{
Struct timeval currtick;
Unsigned long ulret;
Do_gettimeofday (& currtick );
Ulret = currtick. TV _sec;
Ulret * = 1000;
Ulret + = (currtick. TV _usec + 500)/1000;
Return ulret;
}
/*
* Function: Initialize the keyboard buffer.
* Entry:
*/
Static void init_keybuffer (void)
{
Int I;
Spin_lock_irq (& buffer_lock); // obtain a spin lock that will not be interrupted.
G_keybuffer.head = 0;
G_keybuffer.tail = 0;
For (I = 0; I <max_key_count; I ++)
{
G_keybuffer.buf [I] = 0;
G_keybuffer.jiffy [I] = 0;
}
Spin_unlock_irq (& buffer_lock); // release the spin lock
}
/*
* Function: delete expired key values (5 seconds ago)
* Entry:
*/
Static void remove_timeoutkey (void)
{
Unsigned long ultick;
Spin_lock_irq (& buffer_lock); // obtain a spin lock that will not be interrupted.
While (g_keybuffer.head! = G_keybuffer.tail)
{
Ultick = gettickcount ()-g_keybuffer.jiffy [g_keybuffer.head];
If (ultick <5000) // 5 seconds
Break;
G_keybuffer.buf [g_keybuffer.head] = 0;
G_keybuffer.jiffy [g_keybuffer.head] = 0;
G_keybuffer.head ++;
G_keybuffer.head & = (max_key_count-1 );
}
Spin_unlock_irq (& buffer_lock); // release the spin lock
}
/*
* Function: Initialize gpio and set interrupt 0, 2, 11, and 19 to drop edge interrupt.
* Entry:
*/
Static void init_gpio (void)
{
// Set low position for gpe13 11
Writel (readl (gpecon) | (3 <26) | (3 <22 )))&(~ (1 <27) | (1 <23), gpecon); // set gpe13, 11 to output
Writel (readl (gpedat) & 0xffffd7ff, gpedat); // gpe13, 11 output is 0
// Set gpg6 and 2 to a low position
Writel (readl (gpgcon) | 0x3030) & 0 xffffdfdf, gpgcon); // set gpg6, 2 to output
Writel (readl (gpgdat) & 0 xffffffbb, gpgdat); // gpg6, 2 outputs 0
Writel (readl (gpfcon) | 0x33) & 0 xffffffee, gpfcon); // gpf2, 0 is set to interrupted
Writel (readl (gpgcon) | (3 <22) | (3 <6 ))&(~ (1 <22) | (1 <6), gpgcon); // gpg11, 3 is set to interrupted
Set_irq_type (irq_eint0, irqt_falling );
// Printk ("dddddddddddd = % x \ n", extint0 );
Extint0 = (extint0 &(~ 0x07) + 0x02;
Set_irq_type (irq_eint2, irqt_falling );
Extint0 = (extint0 &(~ (0x07 <8) + (0x02 <8 );
Set_irq_type (irq_eint11, irqt_falling );
Extint1 = (extint1 &(~ (0x07 <12) + (0x02 <12 );
Set_irq_type (irq_eint19, irqt_falling );
Extint2 = (extint2 &(~ (0x07 <12) + (0x02 <12 );
}
/*
* Function: Activation interrupted
* Entry:
*/
Static _ inline void enable_irqs (void)
{
Enable_irq (irq_eint0 );
Enable_irq (irq_eint2 );
Enable_irq (irq_eint11 );
Enable_irq (irq_eint19 );
}
/*
* Function: block interruptions
* Entry:
*/
Static _ inline void disable_irqs (void)
{
Disable_irq (irq_eint0 );
Disable_irq (irq_eint2 );
Disable_irq (irq_eint11 );
Disable_irq (irq_eint19 );
}
/*
* Function: after interruption, scan the ammonium key
* Entry:
* Return value: key code (1-16). 0xff indicates an error.
*/
Static _ inline unsigned char button_scan (int irq)
{
Long lgpf, lgpg;
Writel (readl (gpfcon) | 0x33) & 0 xffffffcc, gpfcon); // gpf2, 0 Input
Writel (readl (gpgcon) | (3 <22) | (3 <6 ))&(~ (3 <22) | (3 <6), gpgcon); // gpg11, 3 Input
// Scan the keyboard without using the IRQ.
// Set the G2 low position, G6, E11, and E13 High.
Writel (readl (gpgdat) | (1 <6 ))&(~ (1 <2), gpgdat );
Writel (readl (gpedat) | (1 <11) | (1 <13), gpedat );
// Obtain the values of gpf0, gpf2, gpg3, and gpg11.
Lgpf = readl (gpfdat );
Lgpg = readl (gpgdat );
// Judgment button
If (lgpf & (1 <0) = 0) return 16;
Else if (lgpf & (1 <2) = 0) return 15;
Else if (lgpg & (1 <3) = 0) return 14;
Else if (lgpg & (1 <11) = 0) Return 13;
// Set G6 low position, G2, E11, and E13 High Position
Writel (readl (gpgdat) | (1 <2 ))&(~ (1 <6), gpgdat );
Lgpf = readl (gpfdat );
Lgpg = readl (gpgdat );
If (lgpf & (1 <0) = 0) return 11;
Else if (lgpf & (1 <2) = 0) return 8;
Else if (lgpg & (1 <3) = 0) return 5;
Else if (lgpg & (1 <11) = 0) return 2;
// Set the E11 low, G2, G6, and E13 high
Writel (readl (gpgdat) | (1 <6) | (1 <2), gpgdat );
Writel (readl (gpedat) | (1 <13 ))&(~ (1 <11), gpedat );
Lgpf = readl (gpfdat );
Lgpg = readl (gpgdat );
If (lgpf & (1 <0) = 0) return 10;
Else if (lgpf & (1 <2) = 0) return 7;
Else if (lgpg & (1 <3) = 0) return 4;
Else if (lgpg & (1 <11) = 0) return 1;
// Set the E13 low, G2, G6, and E11 high
// Writel (readl (gpgdat) | (1 <6) | (1 <2), gpgdat );
Writel (readl (gpedat) | (1 <11 ))&(~ (1 <13), gpedat );
Lgpf = readl (gpfdat );
Lgpg = readl (gpgdat );
If (lgpf & (1 <0) = 0) return 12;
Else if (lgpf & (1 <2) = 0) return 9;
Else if (lgpg & (1 <3) = 0) return 6;
Else if (lgpg & (1 <11) = 0) return 3;
Return 0xff;
}
/*
* Function: interrupt function,
* Entry: IRQ interrupt number
*
*/
Static irqreturn_t button_irq (int irq, void * dev_id, struct pt_regs * regs)
{
Unsigned char uckey;
Disable_irqs ();
Printk ("in IRQ \ n ");
// The latency is 50 milliseconds, shielding the key glitch
_ Udelay (50000 );
Uckey = button_scan (IRQ );
If (uckey> = 1) & (uckey <= 16 ))
{
// If the buffer is full, do not add
If (g_keybuffer.head + 1) & (max_key_count-1 ))! = G_keybuffer.tail)
{
Spin_lock_irq (& buffer_lock );
G_keybuffer.buf [g_keybuffer.tail] = uckey;
G_keybuffer.jiffy [g_keybuffer.tail] = gettickcount ();
G_keybuffer.tail ++;
G_keybuffer.tail & = (max_key_count-1 );
Spin_unlock_irq (& buffer_lock );
}
}
Init_gpio ();
Enable_irqs ();
// Printk ("in IRQ! % X \ n ", extint0 );
Return irq_handled; // the return value of the 2.6 kernel is generally this macro.
}
/*
* Function: Application interrupted
* Entry:
*
*/
Static int request_irqs ()
{
Int ret;
Ret = request_irq (irq_eint0, button_irq, sa_interrupt, device_name, null );
If (Ret <0)
Return ret;
Ret = request_irq (irq_eint2, button_irq, sa_interrupt, device_name, null );
If (Ret> = 0)
{
Ret = request_irq (irq_eint11, button_irq, sa_interrupt, device_name, null );
If (Ret> = 0)
{
Ret = request_irq (irq_eint19, button_irq, sa_interrupt, device_name, null );
If (Ret> = 0)
Return ret;
Free_irq (irq_eint11, button_irq );
}
Free_irq (irq_eint2, button_irq );
}
Free_irq (irq_eint0, button_irq );
Return ret;
}
/*
* Function: Release interrupted
* Entry:
*
*/
Static _ inline void free_irqs ()
{
Free_irq (irq_eint0, null); // button_irq );
Free_irq (irq_eint2, null); // button_irq );
Free_irq (irq_eint11, null); // button_irq );
Free_irq (irq_eint19, null); // button_irq );
}
/*
* Function: open the file and start to interrupt
* Entry:
*
*/
Static int button_open (struct inode * inode, struct file * filp)
{
Int ret = nonseekable_open (inode, filp );
If (Ret> = 0)
{
Init_keybuffer ();
Enable_irqs ();
}
Return ret;
}
/*
* Function: Disable files and block interruptions.
* Entry:
*
*/
Static int button_release (struct inode * inode, struct file * filp)
{
Disable_irqs ();
Return 0;
}
/*
* Function: Read the keyboard
* Entry:
*
*/
Static ssize_t button_read (struct file * filp, char * buffer, size_t count, loff_t * PPOs)
{
Ssize_t ret = 0;
Remove_timeoutkey ();
Spin_lock_irq (& buffer_lock );
While (g_keybuffer.head! = G_keybuffer.tail) & (size_t) RET) <count ))
{
Buffer [RET] = (char) (g_keybuffer.buf [g_keybuffer.head]);
G_keybuffer.buf [g_keybuffer.head] = 0;
G_keybuffer.jiffy [g_keybuffer.head] = 0;
G_keybuffer.head ++;
G_keybuffer.head & = (max_key_count-1 );
RET ++;
}
Spin_unlock_irq (& buffer_lock );
Return ret;
}
/*
* Function: clears the keyboard buffer.
* Entry:
*
*/
Static int button_ioctl (struct inode * inode, struct file * file, unsigned int cmd, unsigned long Arg)
{
Init_keybuffer ();
Return 1;
}
/*
* Initialize and add the structure struct cdev to the system.
*/
Static void led_setup_cdev (struct cdev * Dev, int minor, struct file_operations * FoPs)
{
Int err;
Int devno = mkdev (button_major, minor );
Cdev_init (Dev, fops); // initialize the struct cdev
Dev-> owner = this_module;
Dev-> Ops = fops; // assign an initial value to the ops members in the struct. Here is the specific implementation function for device operations.
Err = cdev_add (Dev, devno, 1); // Add struct cdev to the system.
If (ERR)
Printk (kern_info "error % d adding button % d \ n", err, minor );
}
/*
* Defines a file_operations struct to implement the specific operations on the device.
*/
Static struct file_operations button_fops =
{
. Owner = this_module,
. IOCTL = button_ioctl,
. Open = button_open,
. Read = button_read,
. Release = button_release,
};
Static struct cdev simpledevs; // Add By Yoyo
/*
* Function: Initialize the driver.
* Entry:
*
*/
Static int button_init (void)
{
Int ret;
Int result; // Add By Yoyo
Gpecon = ioremap (0x56000040, 0x04); // obtain the virtual address of the corresponding IO port, the same below
Gpedat = ioremap (0x56000044, 0x04 );
Gpfcon = ioremap (0x56000050, 0x04 );
Gpfdat = ioremap (0x56000054, 0x04 );
Gpgcon = ioremap (0x56000060, 0x04 );
Gpgdat = ioremap (0x56000064, 0x04 );
Init_gpio ();
Ret = request_irqs ();
If (Ret <0) return ret;
Disable_irqs ();
// Add By Yoyo
Dev_t Dev = mkdev (button_major, 0); // define the master and secondary device numbers to a dev_t data type struct.
If (button_major)
Result = register_chrdev_region (Dev, 1, "button"); // register a device statically. Specify the device number and get a device name. Run CAT/proc/device to view the information.
Else
{
Result = alloc_chrdev_region (& Dev, "button"); // if the master device number is occupied, the system provides a master device number to the driver.
Button_major = major (Dev); // obtain the master device ID.
}
If (result <0)
{
Printk (kern_warning "button: Unable to get Major % d \ n", button_major );
Return result;
}
If (button_major = 0)
Button_major = result; // If the static allocation fails. Assign a device number that is not dynamically assigned to the device driver.
Printk (kern_info "button register OK !!!!!!!!!! \ N ");
Led_setup_cdev (& simpledevs, 0, & button_fops); // initialize and add the struct cdev to the system.
// Return 0;
Printk ("button initialized. \ n ");
Return 0;
}
/*
* Function: Release the driver.
* Entry:
*
*/
Static void _ exit button_exit (void)
{
Disable_irqs ();
Free_irqs ();
Iounmap (gpecon );
Iounmap (gpedat );
Iounmap (gpfcon );
Iounmap (gpfdat );
Iounmap (gpgcon );
Iounmap (gpgdat );
Cdev_del (& simpledevs); // Delete the struct cdev
Printk ("button_major = % d \ n", button_major );
Unregister_chrdev_region (mkdev (button_major, 0), 1); // uninstall the resources occupied by the device driver
Printk ("button device uninstalled \ n ");
}
Module_init (button_init); // entry for device driver initialization
Module_exit (button_exit); // The entry for uninstalling the device driver
// --------------------------------------------- Test program ----------------------------------------------------
# Include <sys/STAT. h>
# Include <fcntl. h>
# Include <stdio. h>
# Include <sys/time. h>
# Include <sys/types. h>
# Include <unistd. h>
Main ()
{
Int FD;
Char key = 0;
FD = open ("/dev/button_scan", o_rdwr); // open the device
If (FD =-1)
{
Printf ("open device button errr! \ N ");
Return 0;
}
IOCTL (FD,); // clears the keyboard buffer. The following two parameters are meaningless,
While (key! = 16)
{
If (read (FD, & Key, 1)> 0) // read the keyboard to obtain the corresponding key value.
{
Printf ("********************* key value = % d *********** ****************** \ n ", key );
}
}
Close (FD); // close the device
Return 0;
}
// ----------------------------------------------- Makefile ----------------------------------------------------
Ifeq ($ (kernelrelease ),)
# Kerneldir? =/Your/target/source/directory/
Kerneldir? =/Home/imsure/farsight/linux-2.6.22.6 # This path is changed to the path of your own kernel source tree
PWD: = $ (shell PWD)
Modules:
$ (Make)-C $ (kerneldir) M = $ (PWD) Modules
Modules_install:
$ (Make)-C $ (kerneldir) M = $ (PWD) modules_install
Clean:
Rm-RF *. O *~ Core. depend. *. CMD *. Ko *. Mod. C. tmp_versions
. Phony: modules modules_install clean
Else
OBJ-M: = button_scan.o
Endif
Http://hi.baidu.com/pchwuhan/blog/item/79ef8b0af3bcb4b92eddd4c1.html