Poll for linux drivers and poll for linux drivers
POLL operations
1. POLL execution process:
Poll is a system call. Its kernel entry function is sys_poll. sys_poll calls do_sys_poll directly without any processing. The execution process of do_sys_poll can be divided into three parts:
1. Copy the input pollfd array to the kernel space. Because the copy operation is related to the array length, this is an O (n) operation, in do_sys_poll, the code in this step includes the part starting from the function to calling do_poll.
2. query the status of the device corresponding to each file descriptor. If the device is not ready, add an item to the device's waiting queue and continue querying the status of the next device. If no device is ready after all the devices are queried, the current process needs to be suspended until the device is ready or times out. The pending operation is performed by calling schedule_timeout. After the device is ready, the process is notified to continue running. Then, all devices are traversed again to find the ready device. This step traverses all devices twice, and the time complexity is O (n), which does not include the waiting time. The related code is in the do_poll function.
3. Transmit the obtained data to the user space and perform the following operations, such as releasing the memory and detaching the waiting queue, the time complexity of operations such as copying data to a user space and detaching a waiting queue is also O (n). The specific code includes the part that ends after do_poll is called in the do_sys_poll function.
2. Code Writing 1. Add poll to the file_operations struct.
static struct file_operations button_sdv_fops ={.owner = THIS_MODULE,.open = button_dev_open,.read = button_dev_read,.release = button_dev_close,.poll= button_dev_poll,};
2. Complete the button_dev_poll function.
Static unsigned int button_dev_poll (struct file * file, poll_table * wait) {unsigned int mask = 0; poll_wait (file, & button_wait_q, wait); // attaches the current process to the queue, if (ev_press) mask | = POLLIN | POLLRDNORM; return mask ;}
Ev_press indicates the interruption event in the previous blog. If the interruption occurs, the mask value is told to the kernel. Description of the mask value:
Constant |
Description |
POLLIN |
Normal or priority with data readable |
POLLRDNORM |
Common Data readable |
POLLRDBAND |
Data Reading with priority |
POLLPRI |
High-priority data readable |
POLLOUT |
Common Data writable |
POLLWRNORM |
Common Data writable |
POLLWRBAND |
Data with writable priority |
POLLERR |
Error occurred |
POLLHUP |
Suspended |
POLLNVAL |
The description is not an open file. |
3. Compile the test program
Ret = poll (fds, 1, 3000 );
Poll function prototype:
Int poll (struct pollfd * fds, nfds_t nfds, int timeout );
The pollfd struct is:
struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ };
Fd is the device to be queried, and events is the expected event. Here we set it to: POLLIN
Fds defines an array to store all devices that require poll. Poll queries these devices at the same time.
Nfds indicates the number of queried files.
Timeout indicates the time-out period.
Complete driver code:
# Include <linux/init. h> # include <linux/module. h> # include <linux/kernel. h> # include <linux/fs. h> # include <asm/io. h> # include <linux/cdev. h> # include <linux/device. h> # include <linux/irq. h> # include <asm/uaccess. h> # include <asm/irq. h> # include <mach/regs-gpio.h> # include <mach/irqs. h> // This in/opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach path # include <linux/interrupt. h> # include <linux/poll. h> MOD ULE_LICENSE ("Dual BSD/GPL"); static struct class * buttondrv_class; static struct class_devices * buttondrv_class_dev;/**/static interrupt (button_wait_q);/* indicates the interrupt event, interrupt the service program and set it to 1. The read function sets it to 0 */static volatile int ev_press = 0; volatile unsigned long * gpfcon = NULL; volatile unsigned long * gpfdat = NULL; static unsigned keyval; struct pin_desc {unsigned int pin; unsigned int key_value ;};/* press 0x01 0x02 0x03 0x04 * // * When the button is released: 0x81 0x82 0x83 0x84 */struct pin_desc pins_desc [4] = {S3C2410_GPF1, 0x01 },{ S3C2410_GPF4, 0x02 },{ S3C2410_GPF2, 0x03 },{ S3C2410_GPF0, 0x04 },}; /** determine the key value */static irqreturn_t buttons_irq (int irq, void * dev_id) {struct pin_desc * pindesc = (struct pin_desc *) dev_id; unsigned int pinval; pinval = s3c2410_gpio_getpin (pindesc-> pin); if (pinval) // release {keyval = 0x80 | pindesc-> key_value;} else {k Eyval = pindesc-> key_value;} ev_press = 1; // wake_up_interruptible (& button_wait_q); printk ("button is pressed: % d \ n", irq ); return IRQ_HANDLED;} static int button_dev_open (struct inode * inode, struct file * file) {// configure the pin GPF0, 1, 2, and 4 for the input pin request_irq (IRQ_EINT1, buttons_irq, keys, "key1", & pins_desc [0]); request_irq (IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "key2", & pins_desc [1]); request_irq (IRQ_EINT2, buttons_irq, keys, "key3", & pins_desc [2]); request_irq (IRQ_EINT0, buttons_irq, keys, "key4", & pins_desc [3]); return 0 ;} ssize_t button_dev_read (struct file * file, char _ user * buf, size_t size, loff_t * ppos) {if (size! = 1) {return-EINVAL;}/* if no button action occurs, sleep */wait_event_interruptible (button_wait_q, ev_press);/* If a button action occurs, directly return */copy_to_user (buf, & keyval, 1); ev_press = 0; return 0;} int button_dev_close (struct inode * inode, struct file * file) {free_irq (IRQ_EINT1, & pins_desc [0]); free_irq (IRQ_EINT4, & pins_desc [1]); free_irq (records, & pins_desc [2]); free_irq (records, & pins_desc [3]); return 0;} static unsigned int button_dev_poll (struct file * file, poll_table * wait) {unsigned int mask = 0; poll_wait (file, & button_wait_q, wait ); // attaches the current process to the queue and does not immediately sleep if (ev_press) mask | = POLLIN | POLLRDNORM; return mask;} static struct file_operations button_sdv_fops = {. owner = THIS_MODULE ,. open = button_dev_open ,. read = button_dev_read ,. release = button_dev_close ,. poll = callback,}; int major; static int button_dev_init (void) // entry function {major = callback (0, "button_drv", & button_sdv_fops); buttondrv_class = class_create (THIS_MODULE, "button_drv"); if (IS_ERR (buttondrv_class) return response (buttondrv_class); response = device_create (buttondrv_class, NULL, MKDEV (major, 0), NULL, "wq_button "); if (unlikely (IS_ERR (buttondrv_class_dev) return PTR_ERR (buttondrv_class_dev);/* ing physical address */gpfcon = (volatile unsigned long *) ioremap (0x56000050, 16 ); gpfdat = gpfcon + 1; return 0;} static void button_dev_exit (void) {encrypt (major, "button_drv"); device_unregister (batch); class_destroy (buttondrv_class ); iounmap (gpfcon);} module_init (button_dev_init); module_exit (button_dev_exit );
Complete code of the test program:
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <poll.h>int main(int argc, char **argv){int ret=0;int cnt=0;int fd;unsigned char key_val;struct pollfd fds[1];fd = open("/dev/wq_button",O_RDWR);if(fd<0){printf("can't open \n");}fds[0].fd = fd;fds[0].events = POLLIN;while(1){ret = poll(fds, 1, 3000);if(ret == 0){printf("time out \n");}else{read(fd,&key_val,1);printf("key_val = 0x%x\n",key_val);}}return 0;}