一,概念
IO是Input stream输入/Output stream输出的缩写,对于驱动程序来说的IO就是用户程序对设备资源的访问和操作。在IO 操作中几种状态分别是阻塞,非阻塞、同步和异步。
1, 阻塞就是指某个操作如果不满足执行的条件,它会一直处于等徃状态直至条件满足。
2,非阻塞是指某个操作如果不满足执行的条件,它会等待并返回未执行的结果。
3,同步指多个操作同时收发时,返些操作需要排队逐个执行。
4,异步指多个操作同时发生时,返些操作可以一起执行。
对于驱动来说IO操作一般可以理解为对外设的读写。一个完整的IO操作有两个阶段: 第一阶段:查看外设数据是否就绪; 第二阶段:数据就绪,读写外设数据。
阻塞 IO、非阻塞 IO: 当应用程序发出了IO请求。如果目标外设或数据没有准备好,对于阻塞 IO 来说,就会在 read 方法一直等待,直到数据准备好才会返回。而非阻塞 IO 会直接返回数据未准备好,应用程序再去处理NG的情况,重新读取或是其它。可见阻塞IO和非阻塞IO体现在IO操作的第一阶段。
同步 IO、异步 IO: 同步IO和异步IO实际上是针对应用程序和内核的交互来说的,应用程序发出 IO请求后, 如果数据没有就绪,需要应用程序不断的去轮询,直到准备就绪再执行第二阶段。对于异步 IO, 应用程序发出IO请求以后,第一阶段和第二阶段全都交由内核完成,驱动程序也属于内核的一部分。
二,阻塞IO:阻塞IO实际上是同步阻塞IO。
linux 的阻塞式访问中,应用程序调用read()函数从设备中获取数据时,如果设备或者数据没有准备好,就会迕入休眠让出CPU资源,准备好时就会唤醒并返回数据给应用程序。内核提供了等徃队列机制来实现返里的休眠唤醒工作。
1,等待待队列也就是进程组成的队列,linux 在系统执行会根据不同的状态把进程分成不同的队列,等待队列就是其中之一。 在驱动中使用等待队列步骤:
2,阻塞IO驱动
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME "bio_led" #define DEVID_COUNT 1 #define DRIVE_COUNT 1 #define MAJOR_U #define MINOR_U 0 struct alinx_char_dev { dev_t devid; //设备号 struct cdev cdev; //字符设备 struct class *class; //类 struct device *device; //设备 struct device_node *nd; //设备树的设备节点 int alinx_key_gpio; //gpio号 atomic_t key_sts; //记录按键状态, 为1时被按下 unsigned int irq; //中断号 struct timer_list timer; //定时器 wait_queue_head_t wait_q_h; //等待队列头 }; static struct alinx_char_dev alinx_char = { .cdev = { .owner = THIS_MODULE, }, }; static irqreturn_t key_handler(int irq, void *dev) { mod_timer(&alinx_char.timer, jiffies + msecs_to_jiffies(50)); return IRQ_RETVAL(IRQ_HANDLED); } void timer_function(struct timer_list *timer) { unsigned char value; value = gpio_get_value(alinx_char.alinx_key_gpio); if(value == 0) { atomic_set(&alinx_char.key_sts, 1); wake_up_interruptible(&alinx_char.wait_q_h); } else { } } static int char_drv_open(struct inode *inode_p, struct file *file_p) { printk("gpio_test module openn"); return 0; } static ssize_t char_drv_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p) { unsigned int keysts = 0; int ret; keysts = atomic_read(&alinx_char.key_sts); if(!keysts) { DECLARE_WAITQUEUE(queue_mem, current); add_wait_queue(&alinx_char.wait_q_h, &queue_mem); __set_current_state(TASK_INTERRUPTIBLE); schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&alinx_char.wait_q_h, &queue_mem); if(signal_pending(current)) { return -ERESTARTSYS; } else { } } else { } keysts = atomic_read(&alinx_char.key_sts); ret = copy_to_user(buf, &keysts, sizeof(keysts)); atomic_set(&alinx_char.key_sts, 0); return 0; } static int char_drv_release(struct inode *inode_p, struct file *file_p) { printk("gpio_test module releasen"); return 0; } static struct file_operations ax_char_fops = { .owner = THIS_MODULE, .open = char_drv_open, .read = char_drv_read, .release = char_drv_release, }; static int __init char_drv_init(void) { u32 ret = 0; atomic_set(&alinx_char.key_sts, 0); alinx_char.nd = of_find_node_by_path("/alinxkey"); if(alinx_char.nd == NULL) { printk("alinx_char node not findrn"); return -EINVAL; } else { printk("alinx_char node findrn"); } alinx_char.alinx_key_gpio = of_get_named_gpio(alinx_char.nd, "alinxkey-gpios", 0); if(alinx_char.alinx_key_gpio < 0) { printk("can not get alinxkey-gpios"); return -EINVAL; } printk("alinxkey-gpio num = %drn", alinx_char.alinx_key_gpio); ret = gpio_request(alinx_char.alinx_key_gpio, "alinxkey"); if(ret != 0) { printk("can not request gpiorn"); return -EINVAL; } ret = gpio_direction_input(alinx_char.alinx_key_gpio); if(ret < 0) { printk("can not set gpiorn"); return -EINVAL; } alinx_char.irq = gpio_to_irq(alinx_char.alinx_key_gpio); ret = request_irq(alinx_char.irq, key_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "alinxkey", NULL); if(ret < 0) { printk("irq %d request failedrn", alinx_char.irq); return -EFAULT; } timer_setup(&alinx_char.timer, timer_function, NULL); init_waitqueue_head(&alinx_char.wait_q_h); alloc_chrdev_region(&alinx_char.devid, MINOR_U, DEVID_COUNT, DEVICE_NAME); cdev_init(&alinx_char.cdev, &ax_char_fops); cdev_add(&alinx_char.cdev, alinx_char.devid, DRIVE_COUNT); alinx_char.class = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(alinx_char.class)) { return PTR_ERR(alinx_char.class); } alinx_char.device = device_create(alinx_char.class, NULL, alinx_char.devid, NULL, DEVICE_NAME); if (IS_ERR(alinx_char.device)) { return PTR_ERR(alinx_char.device); } return 0; } static void __exit char_drv_exit(void) { gpio_free(alinx_char.alinx_key_gpio); free_irq(alinx_char.irq, NULL); del_timer_sync(&alinx_char.timer); cdev_del(&alinx_char.cdev); unregister_chrdev_region(alinx_char.devid, DEVID_COUNT); device_destroy(alinx_char.class, alinx_char.devid); class_destroy(alinx_char.class); printk("timer_led_dev_exit_okn"); } module_init(char_drv_init); module_exit(char_drv_exit); MODULE_AUTHOR("subomb"); MODULE_ALIAS("alinx char"); MODULE_DESCRIPTION("BIO LED driver"); MODULE_VERSION("v1.0"); MODULE_LICENSE("GPL");
3,测试
#include#include #include #include int main(int argc, char *argv[]) { int fd, fd_l ,ret; char *filename, led_value = 0; unsigned int key_value; if(argc != 2) { printf("Error Usagern"); return -1; } filename = argv[1]; fd = open(filename, O_RDWR); if(fd < 0) { printf("file %s open failedrn", argv[1]); return -1; } while(1) { ret = read(fd, &key_value, sizeof(key_value)); if(ret < 0) { printf("read failedrn"); break; } if(1 == key_value) { printf("ps_key1 pressrn"); led_value = !led_value; fd_l = open("/dev/gpio_leds", O_RDWR); if(fd_l < 0) { printf("file /dev/gpio_leds open failedrn"); break; } ret = write(fd_l, &led_value, sizeof(led_value)); if(ret < 0) { printf("write failedrn"); break; } ret = close(fd_l); if(ret < 0) { printf("file /dev/gpio_leds close failedrn"); break; } } } ret = close(fd); if(ret < 0) { printf("file %s close failedrn", argv[1]); return -1; } return 0; }
三,非阻塞IO:非阻塞 IO(NIO),也就是同步非阻塞 IO。
IO操作的两个阶段先查询再读写,而非阻塞IO在查询阶段的处理和阻塞 IO不同。应用程序需要迕行 IO 操 作前,先发起查询,驱劢程序根据数据情况返回查询结果,如果返回查询结果NG,应用程序就不执行读写操作了。如果应用程序非要读写的话,就继续去查询,直到驱动程序返回数据准备完成,才会做下一步的读写操作。
非阻塞IO的处理方式是轮询。linux 中提供了应用程序的轮询机制和相应的驱动程序系统调用。
1,应用程序中的三种轮询方法:select、poll、epoll
(1)select
struct timeval
{
long tv_sec;
long tv_usec;
}
返个参数的值有三种情况
1,如果传入NULL,不传入时间结构,
就是一个阻塞凼数,直到某个文件描述符収生发化才会返回。
2,如果把秒和微妙都设为 0,就是一个非阻塞凼数,会立刻返回,
文件无发化返回0,有发化返回正值。
3,如果值大于0,则意为超时时间, select若在timeout时间内没有检测到文件描述符发化,
则会直接返回0,有发化直接回正值。
使用举例:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
int main(int argc, char *argv[])
{
int ret, fd, fd_l;
fd_set readfds;
struct timeval timeout;
char *filename, led_value = 0;
unsigned int key_value;
if(argc != 2)
{
printf("Error Usagern");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR | O_NONBLOCK);
if(fd < 0)
{
printf("can not open file %srn", filename);
return -1;
}
while(1)
{
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
timeout.tv_sec = 1;
timeout.tv_usec = 500000;
ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
switch (ret)
{
case 0:
{
break;
}
case -1:
{
break;
}
default:
{
if(FD_ISSET(fd, &readfds))
{
ret = read(fd, &key_value, sizeof(key_value));
if(ret < 0)
{
printf("read failedrn");
break;
}
printf("key_value = %drn", key_value);
if(1 == key_value)
{
printf("ps_key1 pressrn");
led_value = !led_value;
fd_l = open("/dev/gpio_leds", O_RDWR);
if(fd_l < 0)
{
printf("file /dev/gpio_leds open failedrn");
break;
}
ret = write(fd_l, &led_value, sizeof(led_value));
if(ret < 0)
{
printf("write failedrn");
break;
}
ret = close(fd_l);
if(ret < 0)
{
printf("file /dev/gpio_leds close failedrn");
break;
}
}
}
break;
}
}
}
close(fd);
return ret;
}
(2)poll:原型int poll (struct pollfd *fds, unsigned int nfds, int timeout);
poll 本质上和sellect 没有区别,poll它的最大连接数没有限制。参数说明:
nfds:poll 监规的文件句柄数量,也就是fds的数组长度。
imeout:超时时间,单位为毫秒。
fds:struct pollfd 结构体是文件句柄和事件的组合,定义:
struct pollfd
{
int fd;
//fd 是文件句柄,events 是对于这个文件需要监规的事件类型,
revents 是内核返回的事件类型。事件类型有:
POLLIN //有数据可诺
POLLPRI //有紧急数据可诺
POLLOUT //数据可冐
POLLERR //挃定文件描述符収生错诨
POLLHUP //挃定文件描述符挂起
POLLNVAL //无效请求
POLLRDNORM //有数据可诺
short events;
short revents;
};
(3),epoll:可以理解为 event poll,设计用于大并发时的IO查询,常用于网络编程。
2,驱动程序中的poll函数:
应用程序中调用 select、poll、epoll 时,系统调用就会执行驱动程序中file_operations的poll函数。也就是需要实现的驱动函数,函数原型:
unsigned int (*poll) (struct file *filp, struct poll_table_struct *wait);
返回值和struct pollfd结构体中事件相同。
filp:应用程序传递过来的值,应用程序 open 以后获得的目标文件句柄。
wait:应用程序传递过来的值,代表应用程序线程。我们需要在poll函数中调用poll_wait将应用程序添线程wait添加到poll_table等待队列中,poll_wait函数原型如下:
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p);
3,驱动程序
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME "nio_led" #define DEVID_COUNT 1 #define DRIVE_COUNT 1 #define MAJOR_U #define MINOR_U 0 struct alinx_char_dev { dev_t devid; //设备号 struct cdev cdev; //字符设备 struct class *class; //类 struct device *device; //设备 struct device_node *nd; //设备树的设备节点 int alinx_key_gpio; //gpio号 atomic_t key_sts; //记录按键状态, 为1时被按下 unsigned int irq; //中断号 struct timer_list timer; //定时器 wait_queue_head_t wait_q_h; //等待队列头 }; static struct alinx_char_dev alinx_char = { .cdev = { .owner = THIS_MODULE, }, }; static irqreturn_t key_handler(int irq, void *dev) { mod_timer(&alinx_char.timer, jiffies + msecs_to_jiffies(50)); return IRQ_RETVAL(IRQ_HANDLED); } void timer_function(struct timer_list *timer) { unsigned char value; value = gpio_get_value(alinx_char.alinx_key_gpio); if(value == 0) { atomic_set(&alinx_char.key_sts, 1); wake_up_interruptible(&alinx_char.wait_q_h); } else { } } static int char_drv_open(struct inode *inode_p, struct file *file_p) { printk("gpio_test module openn"); return 0; } static ssize_t char_drv_read(struct file *file_p, char __user *buf, size_t len, loff_t *loff_t_p) { unsigned int keysts = 0; int ret; keysts = atomic_read(&alinx_char.key_sts); if(file_p->f_flags & O_NONBLOCK) { } else if(!keysts) { DECLARE_WAITQUEUE(queue_mem, current); add_wait_queue(&alinx_char.wait_q_h, &queue_mem); __set_current_state(TASK_INTERRUPTIBLE); schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&alinx_char.wait_q_h, &queue_mem); if(signal_pending(current)) { return -ERESTARTSYS; } else { } } else { } keysts = atomic_read(&alinx_char.key_sts); ret = copy_to_user(buf, &keysts, sizeof(keysts)); atomic_set(&alinx_char.key_sts, 0); return 0; } unsigned int char_drv_poll(struct file *filp, struct poll_table_struct *wait) { unsigned int ret = 0; poll_wait(filp, &alinx_char.wait_q_h, wait); if(atomic_read(&alinx_char.key_sts)) { ret = POLLIN; } else { } return ret; } static int char_drv_release(struct inode *inode_p, struct file *file_p) { printk("gpio_test module releasen"); return 0; } static struct file_operations ax_char_fops = { .owner = THIS_MODULE, .open = char_drv_open, .read = char_drv_read, .poll = char_drv_poll, .release = char_drv_release, }; static int __init char_drv_init(void) { u32 ret = 0; atomic_set(&alinx_char.key_sts, 0); alinx_char.nd = of_find_node_by_path("/alinxkey"); if(alinx_char.nd == NULL) { printk("alinx_char node not findrn"); return -EINVAL; } else { printk("alinx_char node findrn"); } alinx_char.alinx_key_gpio = of_get_named_gpio(alinx_char.nd, "alinxkey-gpios", 0); if(alinx_char.alinx_key_gpio < 0) { printk("can not get alinxkey-gpios"); return -EINVAL; } printk("alinxkey-gpio num = %drn", alinx_char.alinx_key_gpio); ret = gpio_request(alinx_char.alinx_key_gpio, "alinxkey"); if(ret != 0) { printk("can not request gpiorn"); return -EINVAL; } ret = gpio_direction_input(alinx_char.alinx_key_gpio); if(ret < 0) { printk("can not set gpiorn"); return -EINVAL; } alinx_char.irq = gpio_to_irq(alinx_char.alinx_key_gpio); ret = request_irq(alinx_char.irq,key_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "alinxkey", NULL); if(ret < 0) { printk("irq %d request failedrn", alinx_char.irq); return -EFAULT; } timer_setup(&alinx_char.timer, timer_function, NULL); init_waitqueue_head(&alinx_char.wait_q_h); alloc_chrdev_region(&alinx_char.devid, MINOR_U, DEVID_COUNT, DEVICE_NAME); cdev_init(&alinx_char.cdev, &ax_char_fops); cdev_add(&alinx_char.cdev, alinx_char.devid, DRIVE_COUNT); alinx_char.class = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(alinx_char.class)) { return PTR_ERR(alinx_char.class); } alinx_char.device = device_create(alinx_char.class, NULL, alinx_char.devid, NULL, DEVICE_NAME); if (IS_ERR(alinx_char.device)) { return PTR_ERR(alinx_char.device); } return 0; } static void __exit char_drv_exit(void) { gpio_free(alinx_char.alinx_key_gpio); free_irq(alinx_char.irq, NULL); del_timer_sync(&alinx_char.timer); cdev_del(&alinx_char.cdev); unregister_chrdev_region(alinx_char.devid, DEVID_COUNT); device_destroy(alinx_char.class, alinx_char.devid); class_destroy(alinx_char.class); printk("timer_led_dev_exit_okn"); } module_init(char_drv_init); module_exit(char_drv_exit); MODULE_AUTHOR("subomb"); MODULE_ALIAS("alinx char"); MODULE_DESCRIPTION("NIO LED driver"); MODULE_VERSION("v1.0"); MODULE_LICENSE("GPL");



