• linux驱动程序之poll机制


    使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询是否可对设备进行无阻塞的访问.
    select() poll() epoll()系统调用最终会使设备驱动中的poll()函数被执行.
    POLL机制
    达到的效果:

    应用程序读取数据时, 查看是否有数据, 没有数据休眠一段时间(休眠期间若有数据, 那么会唤醒线程), 再查询, 这个循环超时后返回

    关键函数poll_wait
    头文件 linux/poll.h

    static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
    {
    	if (p && p->_qproc && wait_address)
    		p->_qproc(filp, wait_address, p);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    poll_wait()并不会引发阻塞, 其工作是把当前进程添加到wait参数指向的等待列表(poll_table)中, 实际作用是让唤醒参数queue对应的等待队列可以唤醒因select()而睡眠的进程.

    驱动中的poll()实现
    设备驱动中的poll()函数原型是:

    unsigned int (*poll)(struct file *filp, struct poll_table* wait);
    
    • 1

    编写驱动
    使用poll机制时,驱动程序的核心就是提供对应的drv_poll函数。
    在drv_poll函数中要做2件事:
    ① 把当前线程挂入队列wq:poll_wait
    APP调用一次poll,可能导致drv_poll被调用2次,但是我们并不需要把当前线程挂入队列2次。
    可以使用内核的函数poll_wait把线程挂入队列,如果线程已经在队列里了,它就不会再次挂入。
    ② 返回设备状态:
    APP调用poll函数时,有可能是查询“有没有数据可以读”:POLLIN,也有可能是查询“你有没有空间给我写数据”:POLLOUT。
    所以drv_poll要返回自己的当前状态:(POLLIN | POLLRDNORM) 或 (POLLOUT | POLLWRNORM)。
    POLLRDNORM等同于POLLIN,为了兼容某些APP把它们一起返回。
    POLLWRNORM等同于POLLOUT ,为了兼容某些APP把它们一起返回。

    #include 
    #include 
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    struct gpio_key{
    	int gpio;
    	struct gpio_desc *gpiod;
    	int flag;
    	int irq;
    } ;
    
    static struct gpio_key *gpio_keys_array_100ask;
    
    /* 主设备号                                                                 */
    static int major = 0;
    static struct class *gpio_key_class;
    
    /* 环形缓冲区 */
    #define BUF_LEN 128
    static int g_keys[BUF_LEN];
    static int r, w;
    
    #define NEXT_POS(x) ((x+1) % BUF_LEN)
    
    static int is_key_buf_empty(void)
    {
    	return (r == w);
    }
    
    static int is_key_buf_full(void)
    {
    	return (r == NEXT_POS(w));
    }
    
    static void put_key(int key)
    {
    	if (!is_key_buf_full())
    	{
    		g_keys[w] = key;
    		w = NEXT_POS(w);
    	}
    }
    
    static int get_key(void)
    {
    	int key = 0;
    	if (!is_key_buf_empty())
    	{
    		key = g_keys[r];
    		r = NEXT_POS(r);
    	}
    	return key;
    }
    
    
    static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
    
    /* 实现对应的open/read/write等函数,填入file_operations结构体                   */
    static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
    {
    	//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    	int err;
    	int key;
    	
    	wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
    	key = get_key();
    	err = copy_to_user(buf, &key, 4);
    	
    	return 4;
    }
    
    static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
    {
    	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    	poll_wait(fp, &gpio_key_wait, wait); //查询一次后 进入休眠
    	return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
    }
    
    
    /* 定义自己的file_operations结构体                                              */
    static struct file_operations gpio_key_drv = {
    	.owner	 = THIS_MODULE,
    	.read    = gpio_key_drv_read,
    	.poll    = gpio_key_drv_poll,
    };
    
    
    static irqreturn_t gpio_key_isr(int irq, void *dev_id)
    {
    	struct gpio_key *gpio_key = dev_id;
    	int val;
    	int key;
    	
    	val = gpiod_get_value(gpio_key->gpiod);
    	
    
    	printk("key %d %d\n", gpio_key->gpio, val);
    	key = (gpio_key->gpio << 8) | val;
    	put_key(key);
    	wake_up_interruptible(&gpio_key_wait); //唤醒, 会使得poll跳出poll_wait
    	
    	return IRQ_HANDLED;
    }
    
    /* 1. 从platform_device获得GPIO
     * 2. gpio=>irq
     * 3. request_irq
     */
    static int gpio_key_probe(struct platform_device *pdev)
    {
    	int err;
    	struct device_node *node = pdev->dev.of_node;
    	int count;
    	int i;
    	enum of_gpio_flags flag;
    		
    	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    
    	count = of_gpio_count(node);
    	if (!count)
    	{
    		printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
    		return -1;
    	}
    
    	gpio_keys_array = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
    	for (i = 0; i < count; i++)
    	{
    		gpio_keys_array[i].gpio = of_get_gpio_flags(node, i, &flag);
    		if (gpio_keys_array[i].gpio < 0)
    		{
    			printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
    			return -1;
    		}
    		gpio_keys_array[i].gpiod = gpio_to_desc(gpio_keys_array[i].gpio);
    		gpio_keys_array[i].flag = flag & OF_GPIO_ACTIVE_LOW;
    		gpio_keys_array[i].irq  = gpio_to_irq(gpio_keys_array[i].gpio);
    	}
    
    	for (i = 0; i < count; i++)
    	{
    		err = request_irq(gpio_keys_array[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "gpio_keys", &gpio_keys_array[i]);
    	}
    
    	/* 注册file_operations 	*/
    	major = register_chrdev(0, "gpio_keys", &gpio_key_drv);  /* /dev/gpio_key */
    
    	gpio_key_class = class_create(THIS_MODULE, "gpio_keys_class");
    	if (IS_ERR(gpio_key_class)) {
    		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    		unregister_chrdev(major, "gpio_keys");
    		return PTR_ERR(gpio_key_class);
    	}
    
    	device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, "gpio_keys"); /* /dev/gpio_keys */
            
        return 0;
        
    }
    
    static int gpio_key_remove(struct platform_device *pdev)
    {
    	//int err;
    	struct device_node *node = pdev->dev.of_node;
    	int count;
    	int i;
    
    	device_destroy(gpio_key_class, MKDEV(major, 0));
    	class_destroy(gpio_key_class);
    	unregister_chrdev(major, "gpio_keys");
    
    	count = of_gpio_count(node);
    	for (i = 0; i < count; i++)
    	{
    		free_irq(gpio_keys_array[i].irq, &gpio_keys_array[i]);
    	}
    	kfree(gpio_keys_array);
        return 0;
    }
    
    
    static const struct of_device_id gpio_keys[] = {
        { .compatible = "jzy,gpio_key" },
        { },
    };
    
    /* 1. 定义platform_driver */
    static struct platform_driver gpio_keys_driver = {
        .probe      = gpio_key_probe,
        .remove     = gpio_key_remove,
        .driver     = {
            .name   = "gpio_keys",
            .of_match_table = gpio_keys,
        },
    };
    
    /* 2. 在入口函数注册platform_driver */
    static int __init gpio_key_init(void)
    {
        int err;
        
    	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    	
        err = platform_driver_register(&gpio_keys_driver); 
    	
    	return err;
    }
    
    /* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
     *     卸载platform_driver
     */
    static void __exit gpio_key_exit(void)
    {
    	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    
        platform_driver_unregister(&gpio_keys_driver);
    }
    
    
    /* 7. 其他完善:提供设备信息,自动创建设备节点                                     */
    
    module_init(gpio_key_init);
    module_exit(gpio_key_exit);
    
    MODULE_LICENSE("GPL");
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250

    编写应用程序
    POLLIN 有数据可读
    POLLRDNORM 等同于POLLIN
    POLLRDBAND Priority band data can be read,有优先级较较高的“band data”可读
    Linux系统中很少使用这个事件
    POLLPRI 高优先级数据可读
    POLLOUT 可以写数据
    POLLWRNORM 等同于POLLOUT
    POLLWRBAND Priority data may be written
    POLLERR 发生了错误
    POLLHUP 挂起
    POLLNVAL 无效的请求,一般是fd未open

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main(int argc, char **argv)
    {
    	int fd;
    	int val;
    	struct pollfd fds[1];
    	int timeout_ms = 5000;
    	int ret;
    	
    	/* 1. 判断参数 */
    	if (argc != 2) 
    	{
    		printf("Usage: %s \n", argv[0]);
    		return -1;
    	}
    
    	/* 2. 打开文件 */
    	fd = open(argv[1], O_RDWR);
    	if (fd == -1)
    	{
    		printf("can not open file %s\n", argv[1]);
    		return -1;
    	}
    
    	fds[0].fd = fd;
    	fds[0].events = POLLIN;
    	
    
    	while (1)
    	{
    		/* 3. 读文件 */
    		ret = poll(fds, 1, timeout_ms);
    		if ((ret == 1) && (fds[0].revents & POLLIN))
    		{
    			read(fd, &val, 4);
    			printf("get button : 0x%x\n", val);
    		}
    		else
    		{
    			printf("timeout\n");
    		}
    	}
    	
    	close(fd);
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
  • 相关阅读:
    java 企业工程管理系统软件源码+Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis
    C++ 函数模板
    Wireshark把DDoS照原形
    基于椭圆动态限制和免疫机理的路径规划算法
    DS:二叉树的链式存储及遍历
    新手小白学JAVA Lambda表达式
    maven的进阶学习
    The timestamp difference between admin and executor exceeds the limit.解决办法
    车辆检验和测试服务市场现状研究分析
    猿创征文|uniapp tabbar自定义风格
  • 原文地址:https://blog.csdn.net/qq_40684669/article/details/127760079