Linux key driver example

Source: Internet
Author: User

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

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.