linux驱动程序开发-第九节:应用层阻塞归功于底层驱动采用中断机制
作者:互联网
驱动采用:中断+等待队列实现阻塞。
设置一个等待的条件,条件满足,进程就继续向下执行;条件不满足,进程就阻塞在等待队列上。当条件满足后,中断会唤醒等待队列中的进程,进程再继续向下执行。
视频讲解及详细资料链接:链接:https://pan.baidu.com/s/13xxNzgLur468qeivBw8FUg
提取码:osb1
程序案列:一个按键对应一个led,按下亮,中断实现底层驱动ioctl()阻塞。
1.key_drv.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <cfg_type.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <mach/devices.h>
#include <linux/sched.h>
#define GEC6818_K2_STA _IOR('K', 1, unsigned long)
#define GEC6818_K3_STA _IOR('K', 2, unsigned long)
#define GEC6818_K4_STA _IOR('K', 3, unsigned long)
#define GEC6818_K6_STA _IOR('K', 4, unsigned long)
#define GEC6818_KALL_STA _IOR('K', 5, unsigned long)
static unsigned int key_val=0;
static int key_press_flag=0;
static wait_queue_head_t gec6818_key_wq;
irqreturn_t keys_irq_handler(int irq,void *dev)
{
if(irq == IRQ_GPIO_A_START +28)
{
key_val|=gpio_get_value(PAD_GPIO_A+28)? 0: 1<<0;
}
if(irq == IRQ_GPIO_B_START +9)
{
key_val|=gpio_get_value(PAD_GPIO_B+9)? 0: 1<<1;
}
if(irq == IRQ_GPIO_B_START +30)
{
key_val|=gpio_get_value(PAD_GPIO_B+30)? 0: 1<<2;
}
if(irq == IRQ_GPIO_B_START +31)
{
key_val|=gpio_get_value(PAD_GPIO_B+31)? 0: 1<<3;
}
//设置等待条件为真
key_press_flag = 1;
//唤醒等待队列中的进程
wake_up(&gec6818_key_wq);
//printk("keys_irq_handler\n");
return IRQ_HANDLED;//告诉内核,中断已经处理完毕
}
static int gec6818_key_open (struct inode * inode, struct file *file)
{
printk("gec6818_key_open \n");
return 0;
}
static int gec6818_key_release (struct inode * inode, struct file *file)
{
printk("gec6818_key_release \n");
return 0;
}
static long gec6818_key_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{
int rt=0;
switch(cmd)
{
case GEC6818_KALL_STA:
{
//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
wait_event_interruptible(gec6818_key_wq,key_press_flag);
key_press_flag=0;
}break;
case GEC6818_K2_STA:
{
//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
wait_event_interruptible(gec6818_key_wq,key_press_flag);
key_press_flag=0;
}break;
case GEC6818_K3_STA:
{
//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
wait_event_interruptible(gec6818_key_wq,key_press_flag);
key_press_flag=0;
}break;
case GEC6818_K4_STA:
{
//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
wait_event_interruptible(gec6818_key_wq,key_press_flag);
key_press_flag=0;
}break;
case GEC6818_K6_STA:
{
//访问等待队列头,并且去判断key_press_flag是否为真,若是假的话继续阻塞等待
wait_event_interruptible(gec6818_key_wq,key_press_flag);
key_press_flag=0;
}break;
default:
printk("key ENOIOCTLCMD\n");
return -ENOIOCTLCMD;
}
rt = copy_to_user((void *)args,&key_val,4);
key_val=0;
if(rt != 0)
return -EFAULT;
return 0;
}
static const struct file_operations gec6818_key_fops = {
.owner = THIS_MODULE,
.open = gec6818_key_open,
.release = gec6818_key_release,
.unlocked_ioctl = gec6818_key_ioctl,
};
static struct miscdevice gec6818_key_miscdev = {
.minor = MISC_DYNAMIC_MINOR, //MISC_DYNAMIC_MINOR,动态分配次设备号
.name = "wgh_gec6818_keys_irq", //设备名称,/dev/gec6818_keys
.fops = &gec6818_key_fops, //文件操作集
};
//入口函数
static int __init gec6818_key_init(void)
{
int rt=0;
//混杂设备的注册
rt = misc_register(&gec6818_key_miscdev);
if (rt)
{
printk("misc_register fail\n");
return rt;
}
rt=request_irq( gpio_to_irq(PAD_GPIO_A+28),
keys_irq_handler,
IRQF_TRIGGER_FALLING,
"gpioA_28",
NULL);
if(rt)
{
printk("request_irq fail\n");
goto request_irq_fail_1;
}
rt=request_irq( gpio_to_irq(PAD_GPIO_B+31),
keys_irq_handler,
IRQF_TRIGGER_FALLING,
"gpioB_31",
NULL);
if(rt)
{
printk("request_irq fail\n");
goto request_irq_fail_2;
}
rt=request_irq( gpio_to_irq(PAD_GPIO_B+9),
keys_irq_handler,
IRQF_TRIGGER_FALLING,
"gpioB_9",
NULL);
if(rt)
{
printk("request_irq fail\n");
goto request_irq_fail_3;
}
rt=request_irq( gpio_to_irq(PAD_GPIO_B+30),
keys_irq_handler,
IRQF_TRIGGER_FALLING,
"gpioB_30",
NULL);
if(rt)
{
printk("request_irq fail\n");
goto request_irq_fail_4;
}
//初始化等待队列头
init_waitqueue_head(&gec6818_key_wq);
printk("gec6818 key init\n");
return 0;
request_irq_fail_4:
free_irq(IRQ_GPIO_B_START+30,NULL);
request_irq_fail_3:
free_irq(IRQ_GPIO_B_START+31,NULL);
request_irq_fail_2:
free_irq(IRQ_GPIO_A_START+28,NULL);
request_irq_fail_1:
misc_deregister(&gec6818_key_miscdev);
return rt;
}
//出口函数
static void __exit gec6818_key_exit(void)
{
free_irq(IRQ_GPIO_A_START+28,NULL);
free_irq(IRQ_GPIO_B_START+9,NULL);
free_irq(IRQ_GPIO_B_START+30,NULL);
free_irq(IRQ_GPIO_B_START+31,NULL);
misc_deregister(&gec6818_key_miscdev);
printk("gec6818 key exit\n");
}
//驱动程序的入口:insmod led_drv.ko调用module_init,module_init又会去调用gec6818_key_init。
module_init(gec6818_key_init);
//驱动程序的出口:rmsmod led_drv调用module_exit,module_exit又会去调用gec6818_key_exit。
module_exit(gec6818_key_exit)
//模块描述
MODULE_AUTHOR("stephenwen88@163.com"); //作者信息
MODULE_DESCRIPTION("gec6818 led driver"); //模块功能说明
MODULE_LICENSE("GPL");
2.led_drv.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#include <linux/miscdevice.h>
#include <linux/ioctl.h>
#define GEC6818_LED_ON _IOW('L', 1, unsigned long)
#define GEC6818_LED_OFF _IOW('L', 2, unsigned long)
struct led_gpio_info{
unsigned int num;
char name[20];
};
static const struct led_gpio_info led_gpio_info_tbl[4]={
{
.num = PAD_GPIO_E+13,
.name = "gpioe13",
},
{
.num = PAD_GPIO_C+7,
.name = "gpioc7",
},
{
.num = PAD_GPIO_C+8,
.name = "gpioc8",
},
{
.num = PAD_GPIO_C+17,
.name = "gpioc17",
},
};
static int gec6818_led_open (struct inode * inode, struct file *file)
{
int i=0;
//配置GPIOE13、GPIOC17、GPIOC7、GPIOC8为输出模式
for(i=0; i<4; i++)
gpio_direction_output(led_gpio_info_tbl[i].num,1);
printk("gec6818_led_open \n");
return 0;
}
static int gec6818_led_release (struct inode * inode, struct file *file)
{
printk("gec6818_led_release \n");
return 0;
}
static long gec6818_led_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
{
//获取当前要控制的LED灯
unsigned long led_pos = args - 7;
switch(cmd)
{
case GEC6818_LED_ON:
gpio_set_value(led_gpio_info_tbl[led_pos].num,0);
break;
case GEC6818_LED_OFF:
gpio_set_value(led_gpio_info_tbl[led_pos].num,1);
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static ssize_t gec6818_led_read (struct file *file, char __user *buf, size_t len, loff_t * offs)
{
return 0;
}
static const struct file_operations gec6818_led_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = gec6818_led_ioctl,
.open = gec6818_led_open,
.release = gec6818_led_release,
.read = gec6818_led_read,
};
static struct miscdevice gec6818_led_miscdev = {
.minor = MISC_DYNAMIC_MINOR, //MISC_DYNAMIC_MINOR,动态分配次设备号
.name = "wgh_gec6818_leds_riq", //设备名称,/dev/gec6818_leds
.fops = &gec6818_led_fops, //文件操作集
};
//入口函数
static int __init gec6818_led_init(void)
{
int rt=0;
int i=0;
//混杂设备的注册
rt = misc_register(&gec6818_led_miscdev);
if (rt)
{
printk("misc_register fail\n");
return rt;
}
//这招是损招,先释放gpio占用的资源
for(i=0; i<4; i++)
gpio_free(led_gpio_info_tbl[i].num);
//申请GPIOE13、GPIOC7、GPIOC8、GPIOC17
for(i=0; i<4; i++)
{
rt=gpio_request(led_gpio_info_tbl[i].num,
led_gpio_info_tbl[i].name);
if(rt < 0)
{
printk("gpio_request:%s fail\n",led_gpio_info_tbl[i].name);
goto gpio_request_fail;
}
}
printk("gec6818 led init\n");
return 0;
gpio_request_fail:
for(i=0; i<4; i++)
gpio_free(led_gpio_info_tbl[i].num);
misc_deregister(&gec6818_led_miscdev);
return rt;
}
//出口函数
static void __exit gec6818_led_exit(void)
{
int i;
for(i=0; i<4; i++)
gpio_free(led_gpio_info_tbl[i].num);
misc_deregister(&gec6818_led_miscdev);
printk("gec6818 led exit\n");
}
//驱动程序的入口:insmod led_drv.ko调用module_init,module_init又会去调用gec6818_led_init。
module_init(gec6818_led_init);
//驱动程序的出口:rmsmod led_drv调用module_exit,module_exit又会去调用gec6818_led_exit。
module_exit(gec6818_led_exit)
//模块描述
MODULE_AUTHOR("stephenwen88@163.com"); //作者信息
MODULE_DESCRIPTION("gec6818 led driver"); //模块功能说明
MODULE_LICENSE("GPL"); //许可证:驱动遵循GPL协议
3.test.c
#include <stdio.h>
/*
一个按键对于一个led,按下亮,再按灭,中断实现底层驱动ioctl()阻塞。
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#define GEC6818_LED_ON _IOW('L', 1, unsigned long)
#define GEC6818_LED_OFF _IOW('L', 2, unsigned long)
#define GEC6818_K2_STA _IOR('K', 1, unsigned long)
#define GEC6818_K3_STA _IOR('K', 2, unsigned long)
#define GEC6818_K4_STA _IOR('K', 3, unsigned long)
#define GEC6818_K6_STA _IOR('K', 4, unsigned long)
#define GEC6818_KALL_STA _IOR('K', 5, unsigned long)
int main(int argc, char **argv)
{
int led_fd=-1;
int key_fd=-1;
int key_val=0;
int rt=0;
//打开gec6818_leds设备
led_fd = open("/dev/wgh_gec6818_leds_riq",O_RDWR);
if(led_fd < 0)
{
perror("open /dev/wgh_gec6818_leds_riq:");
return led_fd;
}
//打开gec6818_keys设备
key_fd = open("/dev/wgh_gec6818_keys_irq",O_RDWR);
if(key_fd < 0)
{
perror("open /dev/wgh_gec6818_keys_irq:");
return key_fd;
}
while(1)
{
//读取按键的状态
rt = ioctl(key_fd,GEC6818_KALL_STA,&key_val);
if(rt == 0)
{
if(key_val&(0x1<<0))
ioctl(led_fd,GEC6818_LED_ON,7);
else
ioctl(led_fd,GEC6818_LED_OFF,7);
if(key_val&(0x1<<1))
ioctl(led_fd,GEC6818_LED_ON,8);
else
ioctl(led_fd,GEC6818_LED_OFF,8);
if(key_val&(0x1<<2))
ioctl(led_fd,GEC6818_LED_ON,9);
else
ioctl(led_fd,GEC6818_LED_OFF,9);
if(key_val&(0x1<<3))
ioctl(led_fd,GEC6818_LED_ON,10);
else
ioctl(led_fd,GEC6818_LED_OFF,10);
}
printf("read key end\n");
}
close(led_fd);
close(key_fd);
return 0;
}
标签:驱动程序,第九节,unsigned,long,GEC6818,key,linux,include,define 来源: https://blog.csdn.net/wghkemo123/article/details/86498198