Chinaunix首页 | 论坛 | 博客
  • 博客访问: 301009
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

2015-05-25 20:32:39

P { margin-bottom: 0.21cm; }

forth_drv字符驱动笔记poll机制分析

15年5月5月7日月7日7日日16:28:19

驱动程序代码如下:

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 #include

8 #include

9 #include

10 #include

11 #include

12 #include

13

14 static struct class *forth_drv_class;

15 static struct class_device *forth_drv_class_dev;

16

17 volatile unsigned long *gpfcon;

18 volatile unsigned long *gpfdat;

19

20 volatile unsigned long *gpgcon;

21 volatile unsigned long *gpgdat;

22

23 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

24 static volatile int ev_press = 0;

25

26 struct pin_desc{

27 unsigned int pin;

28 unsigned int key_val;

29 };

30

31 static unsigned char key_val;

32

33 static struct pin_desc pins_desc[4] = {

34 {S3C2410_GPF0, 0X01},

35 {S3C2410_GPF2, 0X02},

36 {S3C2410_GPG3, 0X03},

37 {S3C2410_GPG11, 0X04},

38 };

39

40 static irqreturn_t buttons_irq(int irq, void *dev_id)

41 {

42 struct pin_desc * pindesc = (struct pin_desc *)dev_id;

43 unsigned int pinval;

44

45 pinval = s3c2410_gpio_getpin(pindesc->pin);

46

47 if(pinval)

48 {

49 key_val = 0x80 | pindesc->key_val;

50 }

51 else

52 {

53 key_val = pindesc->key_val;

54 }

55

56 ev_press = 1;

57 wake_up_interruptible(&button_waitq);

58

59 return IRQ_RETVAL(IRQ_HANDLED);

60 }

61

62 static int forth_drv_open(struct inode *inode, struct file *file){

63

64 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);

65 request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);

66 request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);

67 request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);

68 return 0;

69 }

70

71 ssize_t forth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){

72

73 if(size != 1)

74 return -EINVAL;

75

76 wait_event_interruptible(button_waitq, ev_press);

77

78 copy_to_user(buf, &key_val, 1);

79

80 ev_press = 0;

81

82 return 1;

83 }

84

85 int forth_drv_close(struct inode *inode, struct file *file)

86 {

87 free_irq(IRQ_EINT0, &pins_desc[0]);

88 free_irq(IRQ_EINT2, &pins_desc[1]);

89 free_irq(IRQ_EINT11, &pins_desc[2]);

90 free_irq(IRQ_EINT19, &pins_desc[3]);

91 }

92

93 static unsigned forth_drv_poll (struct file *file, poll_table *wait)

94 {

95 unsigned int mask = 0;

96 poll_wait(file, &button_waitq, wait); //将进程挂到等待队列里面,程序这时候不会立即休眠。

97

98 if (ev_press) //检查有没有按键按下。

99 mask |= POLLIN | POLLRDNORM; /*如果有按键按下的话,把位掩码置为 POLLIN | POLLRDNORM*/

100

101 return mask; /*返回mask,如果有按键按下的话,mask=POLLIN | POLLRDNORM,如果没有按键按下的话,mask=0,这时候do_sys_poll继续执行,就会让进程进入休眠。*/

102 }

103

104 static struct file_operations forth_drv_fops = {

105 .owner = THIS_MODULE,

106 .open = forth_drv_open,

107 .read = forth_drv_read,

108 .release = forth_drv_close,

109 .poll = forth_drv_poll,

110 };

111

112 int major;

113 static int __init forth_drv_init(void){

114 major = register_chrdev(0, "forth_drv", &forth_drv_fops);

115

116 forth_drv_class = class_create(THIS_MODULE, "forth_drv");

117 forth_drv_class_dev = class_device_create(forth_drv_class, NULL, MKDEV(major, 0), NULL, "buttons");

118

119 gpfcon = (unsigned long *)ioremap(0x56000050, 16);

120 gpfdat = gpfcon + 1;

121

122 gpgcon = (unsigned long *)ioremap(0x56000060, 16);

123 gpgdat = gpgcon + 1;

124 return 0;

125 }

126

127 static void __exit forth_drv_exit(void){

128 unregister_chrdev(major, "forth_drv");

129

130 class_device_unregister(forth_drv_class_dev);

131 class_destroy(forth_drv_class);

132

133 iounmap(gpfcon);

134 iounmap(gpgcon);

135

136 return 0;

137 }

138

139 module_init(forth_drv_init);

140 module_exit(forth_drv_exit);

141

142 MODULE_LICENSE("GPL");

测试程序

1 #include

2 #include

3 #include

4 #include

5 #include

6

7 int main (int argc, char **argv)

8 {

9 int fd;

10 unsigned char key_val;

11 int ret;

12

13 struct pollfd fds[1];

14

15 fd = open("/dev/buttons",O_RDWR);

16

17 if (fd < 0)

18 printf("cannot open!\n");

19

20 fds[0].fd = fd;

21 fds[0].events = POLLIN;

22 while (1)

23 {

24 ret = poll(fds, 1, 5000);

25 if (ret == 0)

26 {

27 printf("time out!\n");

28 }

29 else

30 {

31 read(fd, &key_val, 1);

32 printf("key_val = 0x%x\n", key_val);

33 }

34 }

35 return 0;

36 }


poll机制的作用主要是判断接下来的I/O操作会不会发生阻塞。poll方法的原型是

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


poll方法分两步处理:

1.在一个或多个可指示poll状态变化的等待队列上调用poll_wait,如果当前没有文件描述符可用来执行I/O,则内核将使进程在传递到该系统调用的所有文件描述符对应的等待队列上等待。

2.返回一个用来描述操作是否可以立即无阻塞执行的位掩码。


对于第一个任务,需要用poll_wait函数向poll传递第二个参数 poll_table *wait,向这个 poll_table结构体里面添加一个等待队列。poll_wait函数的原型是:

void poll_wait (struct file *, wait_queue_head_t *, poll_table *);


对于第二个任务,返回哪个操作可以立即执行的位掩码,几个标志用来指示可能的操作:


 标志

 含义

 POLLIN

 如果设备无阻塞的读,就返回该值

 POLLRDNORM

 通常的数据已经准备好,可以读了,就返回该值。通常的做法是会返回(POLLLIN|POLLRDNORA

 POLLRDBAND

 如果可以从设备读出带外数据,就返回该值,它只可在linux内核的某些网络代码中使用,通常不用在设备驱动程序中

 POLLPRI

 如果可以无阻塞的读取高优先级(带外)数据,就返回该值,返回该值会导致select报告文件发生异常,以为select八带外数据当作异常处理

 POLLHUP

 当读设备的进程到达文件尾时,驱动程序必须返回该值,依照select的功能描述,调用select的进程被告知进程时可读的。

 POLLERR

 如果设备发生错误,就返回该值。

 POLLOUT

 如果设备可以无阻塞地些,就返回该值

 POLLWRNORM

 设备已经准备好,可以写了,就返回该值。通常地做法是(POLLOUT|POLLNORM

 POLLWRBAND

 POLLRDBAND类似


描述比较抽象,下面一条一条分析forth_drv_poll 函数:

static unsigned forth_drv_poll (struct file *file, poll_table *wait)

{

unsigned int mask = 0;

poll_wait(file, &button_waitq, wait);


if (ev_press)

mask |= POLLIN | POLLRDNORM;


return mask;

}


首先声明forth_drv_poll 函数,注意它的第二个参数,是poll_table结构,然后调用 poll_wait函数,向这个poll_table结构中添加等待队列。


void poll_wait (struct file *, wait_queue_head_t *, poll_table *);这个函数中三个参数,第一个参数是file,第二个参数是咱们前面声明的static DECLARE_WAIT_QUEUE_HEAD(button_waitq); 第三个参数是static unsigned forth_drv_poll (struct file *file, poll_table *wait) 中的wait


if (ev_press)

mask |= POLLIN | POLLRDNORM;

如果有按键按下的话,就把位掩码置为POLLIN | POLLRDNORM,意思是应用程序可以直接读取而不用阻塞。


如果没有按键按下的话,就返回 mask = 0

这个位掩码 mask = 0有什么用呢?可以看韦东山写的poll机制分析,如果mask = 0do_sys_poll函数就会使进程休眠一段时间。休眠的时间由测试程序指定。


下面分析测试程序:

测试程序只需要调用poll函数即可,具体的poll函数的使用方法,可以通过man poll来查看,下面截取一部分:

SYNOPSIS

#include


int poll(struct pollfd *fds, nfds_t nfds, int timeout);

DESCRIPTION

poll() performs a similar task to select(2): it waits for one of a set

of file descriptors to become ready to perform I/O.


The set of file descriptors to be monitored is specified in the fds

argument, which is an array of structures of the following form:


struct pollfd {

int fd; /* file descriptor */

short events; /* requested events */

short revents; /* returned events */

};


The caller should specify the number of items in the fds array in nfds.


The field fd contains a file descriptor for an open file.


The field events is an input parameter, a bit mask specifying the

events the application is interested in.


The field revents is an output parameter, filled by the kernel with the

events that actually occurred. The bits returned in revents can

include any of those specified in events, or one of the values POLLERR,

POLLHUP, or POLLNVAL. (These three bits are meaningless in the events

field, and will be set in the revents field whenever the corresponding

condition is true.)

If none of the events requested (and no error) has occurred for any of

the file descriptors, then poll() blocks until one of the events

occurs.


The timeout argument specifies an upper limit on the time for which

poll() will block, in milliseconds. Specifying a negative value in

timeout means an infinite timeout.

RETURN VALUE

On success, a positive number is returned; this is the number of struc‐

tures which have nonzero revents fields (in other words, those descrip‐

tors with events or errors reported). A value of 0 indicates that the

call timed out and no file descriptors were ready. On error, -1 is

returned, and errno is set appropriately.


可以看出poll函数的原型为int poll(struct pollfd *fds, nfds_t nfds, int timeout);

它有三个参数,第一个参数是要打开的设备fds,它是一个结构体,

struct pollfd {

int fd; /* file descriptor */

short events; /* requested events */

short revents; /* returned events */

};

所以要构建这个结构体,咱们只需要打开一个设备,所以用 struct pollfd fds[1]; 来创建这个结构体,然后用fds[0].fd = fd; 来指向我们打开的设备,用 fds[0].events = POLLIN; 来指定位掩码。

第二个参数是要打开的设备个数,第三个参数是指定系统的超时时间。


poll函数有返回值,如果为0的话,表示超时,为-1的话表示出错。


可以看到结果,如果5s没有按键的话,直接打印出来time out!如果按下按键的话,直接打印出来键值。


下面这写出scullpipepoll实现,仔细体会一下:

static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
{
struct scull_pipe *dev = filp->private_data;
unsigned int mask = 0;


/*
* The buffer is circular; it is considered full
* if "wp" is right behind "rp" and empty if the
* two are equal.
*/

/*缓冲区是环形的,也就是说,如果wprp之后,则表明缓冲区已满, *而如果他们两个相等,则表明是空的。*/


down(&dev->sem);
poll_wait(filp, &dev->inq, wait);

/*将输入挂到poll_table 队列中*/
poll_wait(filp, &dev->outq, wait);

/*将输出挂到poll_table 队列中*/
if (dev->rp != dev->wp)
mask |= POLLIN | POLLRDNORM; /*
可读取 */
if (spacefree(dev))
mask |= POLLOUT | POLLWRNORM; /*
可写入 */
up(&dev->sem);


return mask;
}

阅读(1373) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~