模块名称:buttons_drv.c
开发环境:Ubuntu 16.04
编译环境:Linux 2.6.22.6
最后修改:2019.03.07
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static unsigned char key_val ;
static struct class *buttons_drv_class ;
static struct class_device *buttons_drv_dev ;
static volatile unsigned long *gpfcon ;
static volatile unsigned long *gpfdat ;
static volatile unsigned long *gpgcon ;
static volatile unsigned long *gpgdat ;
/* 等待队列:
* 当没有按键按下时,如果有应用程序调用buttons_drv_read()
* 它将休眠
*/
static DECLARE_WAIT_QUEUE_HEAD(button_wait);
/* 中断标记事件
* 中断服务程序将它置1
* buttons_drv_read()将它清除
*/
static volatile int ev_press = 0 ;
struct pin_desc {
unsigned char pin ;
unsigned char key_val ;
} ;
/* 按键描述结构体 引脚,按键值 */
static struct pin_desc pins_desc[4] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
} ;
/* 按键中断处理函数 */
static irqreturn_t buttons_irq_handle(int irq, void *dev_id)
{
struct pin_desc *my_pin_desc = (struct pin_desc *)dev_id ;
unsigned int pin_val = 0 ;
/* 获取引脚电平值 */
pin_val = s3c2410_gpio_getpin(my_pin_desc->pin) ;
if(!pin_val)
{
key_val = my_pin_desc->key_val ;
}
else
{
key_val = my_pin_desc->key_val | 0x80 ;
}
ev_press = 1 ;
wake_up_interruptible(&button_wait) ;
return IRQ_HANDLED ;
}
/* 当应用程序open时,函数将向内核申请中断 */
static int buttons_drv_open(struct inode * inode, struct file * file)
{
/* 向内核请求外部中断 */
request_irq(IRQ_EINT0, buttons_irq_handle, IRQ_TYPE_EDGE_BOTH, "S2", &pins_desc[0]);
request_irq(IRQ_EINT2, buttons_irq_handle, IRQ_TYPE_EDGE_BOTH, "S3", &pins_desc[1]);
request_irq(IRQ_EINT11, buttons_irq_handle, IRQ_TYPE_EDGE_BOTH, "S4", &pins_desc[2]);
request_irq(IRQ_EINT19, buttons_irq_handle, IRQ_TYPE_EDGE_BOTH, "S5", &pins_desc[3]);
return 0 ;
}
/* 当应用程序read时,函数将按键值传到用户空间 */
static ssize_t buttons_drv_read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
{
if(bytes != 1)
return -EINVAL ;
wait_event_interruptible(button_wait, ev_press) ;
copy_to_user(userbuf, &key_val, 1) ;
ev_press = 0 ;
return 1 ;
}
static int buttons_drv_close(struct inode * inode, struct file * file)
{
/* 释放中断 */
free_irq(IRQ_EINT0, &pins_desc[0]) ;
free_irq(IRQ_EINT2, &pins_desc[1]) ;
free_irq(IRQ_EINT11, &pins_desc[2]) ;
free_irq(IRQ_EINT19, &pins_desc[3]) ;
return 0 ;
}
/* 驱动架构核心结构体 - file_operations */
static struct file_operations buttons_fops = {
.owner = THIS_MODULE,
.open = buttons_drv_open,
.read = buttons_drv_read,
.release = buttons_drv_close,
} ;
static int major = 0 ; //主设备号
/* 驱动入口函数 */
static int buttons_drv_init(void)
{
/* 注册字符设备 */
major = register_chrdev(0, "buttons_drv", &buttons_fops) ;
/* 自动创建设备节点 udev机制 */
buttons_drv_class = class_create(THIS_MODULE, "buttons_drv") ;
/* 创建设备节点 */
buttons_drv_dev = class_device_create(buttons_drv_class, NULL, MKDEV(major, 0), NULL, "buttons") ;
#if 0 //使用中断机制不需要映射虚拟地址
/* 虚拟地址映射 */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16) ;
gpfdat = gpfcon + 1 ;
gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16) ;
gpgdat = gpgcon + 1 ;
#endif
return 0 ;
}
/* 驱动出口函数 */
static void buttons_drv_exit(void)
{
/* 卸载字符设备 */
unregister_chrdev(major, "buttons_drv") ;
/* 删除设备节点 */
class_device_unregister(buttons_drv_dev) ;
/* 销毁节点类 */
class_destroy(buttons_drv_class) ;
#if 0
/* 解除虚拟地址映射 */
iounmap(gpfcon) ;
iounmap(gpgcon) ;
#endif
return ;
}
/* 驱动入口:加载 */
module_init(buttons_drv_init) ;
/* 驱动出口:卸载 */
module_exit(buttons_drv_exit) ;
/* 许可 */
MODULE_LICENSE("GPL") ;
模块名称:buttons_test.c
开发环境:Ubuntu 16.04
编译环境:Linux 2.6.22.6
最后修改:2019.03.07
#include
#include
#include
#include
#include
int main(int argv, char **argc)
{
int fd ;
unsigned char key_val ;
/* 以读写方式打开设备 */
fd = open("/dev/buttons", O_RDWR) ;
if(fd < 0)
{
printf("can't open!\n") ;
//return -1 ;
}
while(1)
{
/* 从驱动读取按键值到key_val */
read(fd, &key_val, 1) ;
/* 串口打印键值 */
printf("key_val = 0x%x\n", key_val) ;
}
}