Linux字符设备驱动之异步通知

嵌入式技术

1372人已加入

描述

poll机制可实现有数据的时候就去读,没有数据的时候,如果超过规定一个时间,就表示超时时间。poll机制需要应用程序主动去读,而异步通知并不需要,一旦设备就绪,则主动通知应用程序,应用程序不需要主动查询设备状态,类似于中断的概念,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来 等待信号的到达。

在linux中,异步通知是使用信号来实现的,而在linux,大概有30种信号,比如大家熟悉的ctrl+c的SIGINT信号,进程能够忽略或者捕获除过SIGSTOP和SIGKILL的全部信号,当信号背捕获以后,有相应的函数来处理它。

实现异步通知的四个要素:

一、应用程序要实现:注册信号处理函数,使用signal函数;

二、谁来发?驱动来发;

三、发给谁?发给应用程序,但应用程序必须告诉驱动PID;

四、怎么发?驱动程序使用kill_fasync函数;

问:应该在驱动的哪里调用kill_fasync函数?

答:kill_fasync函数的作用是,当有数据时去通知应用程序,理所当然的应该在用户终端处理函数里调用。

问:file_operations需要添加什么函数指针成员吗?

答:要的,需要添加fasync函数指针,要实现这个函数指针,幸运的是,这个函数仅仅调用了fasync_helper函数,而且这个函数是内核帮我们实现好了,驱动工程师不用修改,fasync_helper函数的作用是初始化/释放fasync_struct

详细请参考驱动源码

#include #include #include #include #include #include #include #include #include #include //class_create#include //S3C2410_GPF1//#include #include //#include #include //wait_event_interruptible#include //poll#include /* 定义并初始化等待队列头 */static DECLARE_WAIT_QUEUE_HEAD(button_waitq);static struct class *fifthdrv_class;static struct device *fifthdrv_device;static struct pin_desc { unsigned int pin; unsigned int key_val;};static struct pin_desc pins_desc[4] = { {S3C2410_GPF1, 0x01}, {S3C2410_GPF4, 0x02}, {S3C2410_GPF2, 0x03}, {S3C2410_GPF0, 0x04},};static int ev_press = 0;/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 *//* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */static unsigned char key_val;int major;static struct fasync_struct *button_fasync;/* 用户中断处理函数 */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) { /* 松开 */ key_val = 0x80 | (pindesc->key_val); } else { /* 按下 */ key_val = pindesc->key_val; } ev_press = 1;/* 表示中断已经发生 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ /* 用kill_fasync函数告诉应用程序,有数据可读了 * button_fasync结构体里包含了发给谁(PID指定) * SIGIO表示要发送的信号类型 * POLL_IN表示发送的原因(有数据可读了) */ kill_fasync(&button_fasync, SIGIO, POLL_IN); return IRQ_HANDLED;}static int fifth_drv_open(struct inode * inode, struct file * filp){ /* K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0 * 配置GPF1、GPF4、GPF2、GPF0为相应的外部中断引脚 * IRQT_BOTHEDGE应该改为IRQ_TYPE_EDGE_BOTH */ request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1",&pins_desc[0]); request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2",&pins_desc[1]); request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3",&pins_desc[2]); request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4",&pins_desc[3]); return 0;}static ssize_t fifth_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos){ if (size != 1) return -EINVAL; /* 当没有按键按下时,休眠。 * 即ev_press = 0; * 当有按键按下时,发生中断,在中断处理函数会唤醒 * 即ev_press = 1; * 唤醒后,接着继续将数据通过copy_to_user函数传递给应用程序 */ wait_event_interruptible(button_waitq, ev_press); copy_to_user(user, &key_val, 1); /* 将ev_press清零 */ ev_press = 0; return 1;}static int fifth_drv_close(struct inode *inode, struct file *file){ free_irq(IRQ_EINT1,&pins_desc[0]); free_irq(IRQ_EINT4,&pins_desc[1]); free_irq(IRQ_EINT2,&pins_desc[2]); free_irq(IRQ_EINT0,&pins_desc[3]); return 0;}static unsigned int fifth_drv_poll(struct file *file, poll_table *wait){ unsigned int mask = 0; /* 该函数,只是将进程挂在button_waitq队列上,而不是立即休眠 */ poll_wait(file, &button_waitq, wait); /* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0 * 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1 */ if(ev_press) { mask |= POLLIN | POLLRDNORM; /* 表示有数据可读 */ } /* 如果有按键按下时,mask |= POLLIN | POLLRDNORM,否则mask = 0 */ return mask;}/* 当应用程序调用了fcntl(fd, F_SETFL, Oflags | FASYNC); * 则最终会调用驱动的fasync函数,在这里则是fifth_drv_fasync * fifth_drv_fasync最终又会调用到驱动的fasync_helper函数 * fasync_helper函数的作用是初始化/释放fasync_struct */static int fifth_drv_fasync(int fd, struct file *filp, int on){ return fasync_helper(fd, filp, on, &button_fasync);}/* File operations struct for character device */static const struct file_operations fifth_drv_fops = { .owner = THIS_MODULE, .open = fifth_drv_open, .read = fifth_drv_read, .release = fifth_drv_close, .poll = fifth_drv_poll, .fasync = fifth_drv_fasync,};/* 驱动入口函数 */static int fifth_drv_init(void){ /* 主设备号设置为0表示由系统自动分配主设备号 */ major = register_chrdev(0, "fifth_drv", &fifth_drv_fops); /* 创建fifthdrv类 */ fifthdrv_class = class_create(THIS_MODULE, "fifthdrv"); /* 在fifthdrv类下创建buttons设备,供应用程序打开设备*/ fifthdrv_device = device_create(fifthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); return 0;}/* 驱动出口函数 */static void fifth_drv_exit(void){ unregister_chrdev(major, "fifth_drv"); device_unregister(fifthdrv_device); //卸载类下的设备 class_destroy(fifthdrv_class); //卸载类}module_init(fifth_drv_init); //用于修饰入口函数module_exit(fifth_drv_exit); //用于修饰出口函数MODULE_AUTHOR("LWJ");MODULE_DESCRIPTION("Just for Demon");MODULE_LICENSE("GPL"); //遵循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

应用测试程序源码

#include #include #include #include #include //sleep#include #include #include int fd;void mysignal_fun(int signum){ unsigned char key_val; read(fd, &key_val, 1); printf("key_val = 0x%x ", key_val);}int main(int argc ,char *argv[]){ int flag; signal(SIGIO, mysignal_fun); fd = open("/dev/buttons", O_RDWR); if (fd < 0) { printf("open error "); } /* F_SETOWN: Set the process ID * 告诉内核,发给谁 */ fcntl(fd, F_SETOWN, getpid()); /* F_GETFL :Read the file status flags * 读出当前文件的状态 */ flag = fcntl(fd, F_GETFL); /* F_SETFL: Set the file status flags to the value specified by arg * int fcntl(int fd, int cmd, long arg); * 修改当前文件的状态,添加异步通知功能 */ fcntl(fd, F_SETFL, flag | FASYNC); while(1) { /* 为了测试,主函数里,什么也不做 */ sleep(1000); } 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

当无按键按下时,应用测试程序一直在sleep,当有按键按下时,signal会被调用,最终会调用mysignal_fun,在此函数里read(fd, &key_val, 1);会去读出按键值,这样一来,应用程序就相当于不用主动去读数据了,每当驱动里有数据时,就会告诉应用程序有数据了,此时read函数才会被调用。

总结

为了使设备支持异步通知机制,驱动程序中涉及以下3项工作:

支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。  不过此项工作已由内核完成,设备驱动无须处理。

支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。驱动中应该实现fasync()函数。

在设备资源可获得时,调用kill_fasync()函数激发相应的信号

应用程序:

fcntl(fd, F_SETOWN, getpid()); // 告诉内核,发给谁Oflags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, Oflags | FASYNC); // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_s
 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分