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

2016年(20)

2015年(56)

分类: 嵌入式

2015-05-25 20:34:43

P { margin-bottom: 0.21cm; }

fifth_drv字符驱动之异步通知

15年5月5月8日月8日8日日11:17:14

驱动程序代码:

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 *fifth_drv_class;

15 static struct class_device *fifth_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 static struct fasync_struct *button_async;

27

28 struct pin_desc{

29 unsigned int pin;

30 unsigned int key_val;

31 };

32

33 static unsigned char key_val;

34

35 static struct pin_desc pins_desc[4] = {

36 {S3C2410_GPF0, 0X01},

37 {S3C2410_GPF2, 0X02},

38 {S3C2410_GPG3, 0X03},

39 {S3C2410_GPG11, 0X04},

40 };

41

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

43 {

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

45 unsigned int pinval;

46

47 pinval = s3c2410_gpio_getpin(pindesc->pin);

48

49 if(pinval)

50 {

51 key_val = 0x80 | pindesc->key_val;

52 }

53 else

54 {

55 key_val = pindesc->key_val;

56 }

57

58 ev_press = 1;

59 wake_up_interruptible(&button_waitq);

60

61 kill_fasync(&button_async, SIGIO, POLL_IN);

62

63 return IRQ_RETVAL(IRQ_HANDLED);

64 }

65

66 static int fifth_drv_open(struct inode *inode, struct file *file){

67

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

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

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

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

72 return 0;

73 }

74

75 ssize_t fifth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){

76

77 if(size != 1)

78 return -EINVAL;

79

80 wait_event_interruptible(button_waitq, ev_press);

81

82 copy_to_user(buf, &key_val, 1);

83

84 ev_press = 0;

85

86 return 1;

87 }

88

89 int fifth_drv_close(struct inode *inode, struct file *file)

90 {

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

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

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

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

95 }

96

97 static unsigned fifth_drv_poll (struct file *file, poll_table *wait)

98 {

99 unsigned int mask = 0;

100 poll_wait(file, &button_waitq, wait);

101

102 if (ev_press)

103 mask |= POLLIN | POLLRDNORM;

104

105 return mask;

106 }

107

108 static int fifth_drv_fasync(int fd, struct file *filp, int on)

109 {

110 printk("dirve : fifth_drv_fasync\n");

111 return fasync_helper(fd, filp, on, &button_async);

112 }

113

114 static struct file_operations fifth_drv_fops = {

115 .owner = THIS_MODULE,

116 .open = fifth_drv_open,

117 .read = fifth_drv_read,

118 .release = fifth_drv_close,

119 .poll = fifth_drv_poll,

120 .fasync = fifth_drv_fasync,

121 };

122

123 int major;

124 static int __init fifth_drv_init(void){

125 major = register_chrdev(0, "fifth_drv", &fifth_drv_fops);

126

127 fifth_drv_class = class_create(THIS_MODULE, "fifth_drv");

128 fifth_drv_class_dev = class_device_create(fifth_drv_class, NULL, MKDEV(major, 0), NULL, "buttons");

129

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

131 gpfdat = gpfcon + 1;

132

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

134 gpgdat = gpgcon + 1;

135 return 0;

136 }

137

138 static void __exit fifth_drv_exit(void){

139 unregister_chrdev(major, "fifth_drv");

140

141 class_device_unregister(fifth_drv_class_dev);

142 class_destroy(fifth_drv_class);

143

144 iounmap(gpfcon);

145 iounmap(gpgcon);

146

147 return 0;

148 }

149

150 module_init(fifth_drv_init);

151 module_exit(fifth_drv_exit);

152

153 MODULE_LICENSE("GPL");

测试程序:

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7 #include

8 #include

9 #include

10

11 int fd;

12

13 void my_signal_fun(int signum)

14 {

15 unsigned char key_val;

16 read(fd, &key_val, 1);

17 printf("key_val: 0x%x\n", key_val);

18 }

19

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

21 {

22 unsigned char key_val;

23 int ret;

24 int Oflags;

25

26 signal(SIGIO, my_signal_fun);

27

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

29

30 if (fd < 0)

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

32

33 fcntl(fd, F_SETOWN, getpid());

34 Oflags = fcntl(fd, F_GETFL);

35 fcntl(fd, F_SETFL, Oflags | FASYNC);

36

37 while (1)

38 {

39 sleep(1000);

40 }

41 return 0;

42 }


通过使用异步通知,应用程序可以在数据可用时接受到一个信号,而不需要不停地使用轮询来关注数据。那么,怎么发信号?需要(1)应用程序注册信号处理函数(2)谁发?驱动发(3)发给谁?发送给应用程序,但是驱动怎么知道发送给哪一个应用程序,这时候需要应用程序告诉驱动它的PID。(4)怎么发?使用内核提供的函数kill_fasync()


从应用程序的角度考虑:

首先,指定一个进程作为文件的“属主(owner)”,即把进程的PID告诉驱动,让内核知道该通知哪一个进程,这时候需要用到fcntl函数调用F_SETOWN命令实现,在电脑上man fcntl可以查看fcntl函数的相关信息。

第二:为了真正启用异步通知机制,应用程序必须在设备中设置FASYNC标志,这时候需要用到fcntl函数调用F_SETFL命令实现。


fcntl(fd, F_SETOWN, getpid()); //把设备的PID 号告诉驱动

Oflags = fcntl(fd, F_GETFL); //读出原来的oflags

fcntl(fd, F_SETFL, Oflags | FASYNC); //修改这个oflags

其中 Oflagsfile结构体中的一个参数。


第三:在应用程序中注册信号函数,signalSIGIO,处理函数),可以用man signal查看函数的使用方法。

signal(SIGIO, my_signal_fun);

其中 my_signal_fun为信号处理函数,如下所示:

void my_signal_fun(int signum)

{

unsigned char key_val;

read(fd, &key_val, 1);

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

}


刚开始的时候,写了一个signal函数测试一下,使用kill -10 PID 来发送信号,结果如下所示:



从驱动程序的角度考虑:

1)先定义 struct fasync_struct这个结构体,其中包含进程PID,但是没有初始化。

2)当一个文件的FASYNC 标志被修改时,调用fasync_helper函数去初始化 fasync_struct这个结构体,初始化以后,中断函数就可以使用kill_fasync了。

在驱动程序中如下方式调用fasync_helper函数:


static int fifth_drv_fasync(int fd, struct file *filp, int on)

{

printk("dirve : fifth_drv_fasync\n");

return fasync_helper(fd, filp, on, &button_async);

}


3)数据到达时,可以使用kill_fasync通知注册的进程,我们的目标是在按下按键的时候,驱动去通知应用,所以,这个通知函数就在中断程序static irqreturn_t buttons_irq(int irq, void *dev_id)中,

kill_fasync(&button_async, SIGIO, POLL_IN); 。函数原型如下所示,它的第一个参数是定义的结构体,第二个参数是要发送的信号,一般是SIGIO表示I/O口有数据可供读写,第三个参数是带宽,这个参数几乎总是 POLL_IN


驱动程序要调用的两个函数原型如下:

int fasync_helper (int fd, struct file *filp, int mode, struct fasync_struct ** fa);

void kill_fasync (struct fasync_struct ** fa, int sig, int band);


总结一下这个驱动程序跟测试程序的流程:

1)首先安装驱动程序,insmod fifth_drv.ko

2)然后,执行测试程序,这时候,测试程序注册了信号函数,通知驱动程序它的PID,设置了O_flags,同时,驱动程序监测到测试程序的FASYNC标志被修改,就调用.fasync = fifth_drv_fasync,这个函数,打印出来“dirve : fifth_drv_fasync ”这句话,并且调用fasync_helper函数去初始化 fasync_struct这个结构体。

3)按下按键, 发生中断,就会执行buttons_irq函数,在 buttons_irq函数中,驱动通过执行kill_fasync(&button_async, SIGIO, POLL_IN);发送给应用程序。

4)应用程序接收到 SIGIO信号,调用 my_signal_fun函数,在 my_signal_fun函数中去读取按键值,并且打印出来,我们就看到屏幕上输出按键值信息。

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