之前楚昭,獲取按鍵值的方式都是應(yīng)用程序主動(dòng)去查詢冈涧,無(wú)論是 poll 機(jī)制還是阻塞的 read茂附,那么,另外一種思路就是讓驅(qū)動(dòng)程序來(lái)通知應(yīng)用程序督弓,而不讓他自己去查詢营曼,那么這樣做的好處就是不會(huì)干擾到應(yīng)用程序的工作,只需要等具體的事情發(fā)生了再去處理即可愚隧。
- 應(yīng)用程序從硬件獲取信息主要有三種方式:
- 死循環(huán)read:耗費(fèi)資源嚴(yán)重蒂阱,無(wú)意義
- read + wait_event_interruptible
- poll + read
關(guān)于異步通知,會(huì)用到信號(hào)的概念狂塘,可以參考我之前的文章录煤,來(lái)看如何發(fā)送和接收信號(hào)的。
->《Linux 信號(hào)(signal)》
那么發(fā)送和接收信號(hào)荞胡,有幾個(gè)要素必須成立妈踊,在這里簡(jiǎn)單歸納:
- 接受者注冊(cè)信號(hào)處理函數(shù) (等于signal 或 sigaction 函數(shù))
- 接收者需要告訴自己的 PID (等于 ps 命令來(lái)獲取 pid)
- 發(fā)送者需要知道接受者是誰(shuí)(kill 命令中輸入的 pid )
- 發(fā)送者要發(fā)送信號(hào)(kill 命令發(fā)送信號(hào)或者 kill 函數(shù)、sigqueue 函數(shù)發(fā)送信號(hào))
了解了以上幾個(gè)要點(diǎn)泪漂,現(xiàn)在可以理一下具體的思路了廊营。
目標(biāo):按下按鍵時(shí),驅(qū)動(dòng)程序通過(guò)發(fā)送信號(hào)的方式通知應(yīng)用程序狀態(tài)發(fā)生了變化窖梁,可以去讀取相應(yīng)的數(shù)據(jù)了赘风。
那么應(yīng)用程序是接收者,驅(qū)動(dòng)程序是發(fā)送者纵刘,他們分別都需要做哪些工作呢邀窃?
- 應(yīng)用程序作為 接收者 需要:
將自己的 PID 告訴驅(qū)動(dòng)程序,并且注冊(cè)某種信號(hào)的捕捉函數(shù) - 驅(qū)動(dòng)程序作為 發(fā)送者 需要:
將信號(hào)發(fā)送到應(yīng)用程序(PID),發(fā)送信號(hào)
根據(jù)信號(hào)的開(kāi)發(fā)經(jīng)驗(yàn)瞬捕,我們知道對(duì)于信號(hào)的發(fā)送函數(shù)無(wú)論是 kill 還是 sigqueue 函數(shù)都是實(shí)際都是綜合發(fā)送者的兩個(gè)要素的鞍历,通過(guò)這個(gè)函數(shù)既可以告訴內(nèi)核接收者的 pid,調(diào)用的同時(shí)也發(fā)送了具體的信號(hào)肪虎。那么驅(qū)動(dòng)程序是不是也是通過(guò)這樣的方式來(lái)發(fā)送信號(hào)呢劣砍?
發(fā)送的幾個(gè)要素肯定都要完成,但是其中問(wèn)題就出現(xiàn)在扇救,發(fā)送者需要知道接受者是誰(shuí)刑枝,往往驅(qū)動(dòng)程序是先于應(yīng)用程序完成開(kāi)發(fā)的,那么迅腔,在獲取應(yīng)用程序的 PID 環(huán)節(jié)肯定需要一套應(yīng)用程序向驅(qū)動(dòng)程序注冊(cè)的機(jī)制装畅。
驅(qū)動(dòng)程序發(fā)送信號(hào)的具體流程如下:
- 定義一個(gè)全局靜態(tài)變量指針
static struct fasync_struct *xxx_async_queue
- 在
struct file_operations
結(jié)構(gòu)體中,注冊(cè)fasync
成員沧烈,他的函數(shù)指針類型為:int (*fasync) (int, struct file *, int);
掠兄,定義該函數(shù) - 在 fasync 函數(shù)中,使用
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
函數(shù)對(duì)全局靜態(tài)的變量進(jìn)行初始化锌雀,以上的步驟就是為了方便應(yīng)用程序向驅(qū)動(dòng)程序注冊(cè) PID 的 - 使用函數(shù)
void kill_fasync(struct fasync_struct **fp, int sig, int band)
在需要發(fā)送信號(hào)的地方進(jìn)行調(diào)用蚂夕,調(diào)用時(shí),指定了發(fā)送信號(hào)的類型腋逆,一般驅(qū)動(dòng)程序常用的信號(hào)類型是 SIGIO婿牍,bind 是 POLL_IN 代表,應(yīng)用程序可以讀取數(shù)據(jù)了惩歉。
那么應(yīng)用程序牍汹,想要接受驅(qū)動(dòng)程序發(fā)送的信號(hào),需要做以下的工作:
-
signal(SIGIO, signal_fun);
:注冊(cè)新號(hào)捕捉函數(shù) -
fcntl(fd, F_SETOWN, getpid());
:設(shè)置文件描述符 fd 接收進(jìn)程或者進(jìn)程組接收 SIGIO 或 SIGURG 信號(hào)柬泽。 -
oflags = fcntl(fd, F_GETFL);
//獲取文件描述符的 flag -
fcntl(fd, F_SETFL, oflags | FASYNC);
:將文件描述符設(shè)置為FASYNC模式
ps:發(fā)送和接收信號(hào)驅(qū)動(dòng)程序和應(yīng)用程序的流程已完成了行施,那么系統(tǒng)幫我們完成了一件事璧榄,就是讓驅(qū)動(dòng)程序知道應(yīng)該把信號(hào)發(fā)送到哪個(gè)PID,實(shí)際這個(gè)階段是內(nèi)核通過(guò)設(shè)置結(jié)構(gòu)體中的變量來(lái)完成的:fasync_struct->fa_file->f_owner->pid,這一步內(nèi)核完成了形娇,無(wú)需驅(qū)動(dòng)程序來(lái)完成了表制,只需要調(diào)用fasync_helper函數(shù)初始化即可撩炊。
那么放仗,Show me the code!
驅(qū)動(dòng)程序:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/signal.h>
#define EINT_PIN_COUNT 4
static const char* dev_name = "fasync_eint";
static volatile unsigned int major = 0;
static struct class* fasync_class;
static struct class_device* fasync_class_device;
struct pin_desc
{
unsigned int pin;
unsigned int value;
};
//%kernel%\include\asm-arm\arch\irqs.h
//#define IRQ_EINT0 S3C2410_IRQ(0) /* 16 */
//中斷號(hào)數(shù)組
static const int eints[EINT_PIN_COUNT] =
{
IRQ_EINT0,
IRQ_EINT2,
IRQ_EINT11,
IRQ_EINT19
};
static struct pin_desc pins[4] =
{
{S3C2410_GPF0, 0x1},
{S3C2410_GPF2, 0x2},
{S3C2410_GPG3, 0x3},
{S3C2410_GPG11, 0x4},
};
unsigned int status = 0;
unsigned char value = 0;
static struct fasync_struct * fs;
static irqreturn_t irq_handler(int irq, void *dev_id)
{
struct pin_desc* desc = (struct pin_desc*) dev_id;
status = s3c2410_gpio_getpin(desc->pin);
if(status)
value = desc->value | 0x80;
else
value = desc->value;
//void kill_fasync(struct fasync_struct **fp, int sig, int band)
kill_fasync(&fs, SIGIO, POLL_IN);
return 0;
}
static ssize_t fasync_read (struct file *file, char __user *buff, size_t size, loff_t *ppos)
{
copy_to_user(buff, &value, 1);
return 0;
}
static int fasync_open (struct inode *inode, struct file *file)
{
int i;
for(i = 0; i < EINT_PIN_COUNT; ++i){
request_irq(eints[i], irq_handler, IRQT_BOTHEDGE, dev_name, &pins[i]);
}
printk("interrupt register\n");
return 0;
}
int fasync_fasync (int fd, struct file * filp, int on)
{
//int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
fasync_helper(fd, filp, on, &fs);
return 0;
}
static int fasync_release (struct inode *inode, struct file *file)
{
//void free_irq(unsigned int irq, void *dev_id)
int i = 0;
for(;i < EINT_PIN_COUNT; ++i){
free_irq(eints[i], &pins[i]);
}
printk("button released\n");
return 0;
}
struct file_operations fasync_fops =
{
.owner = THIS_MODULE,
.open = fasync_open,
.read = fasync_read,
.fasync = fasync_fasync,
.release = fasync_release,
};
static int __init fasync_init(void)
{
major = register_chrdev(major, dev_name, &fasync_fops);
fasync_class = class_create(THIS_MODULE, dev_name);
fasync_class_device = class_device_create(fasync_class, NULL, MKDEV(major, 0), NULL, dev_name);
printk("init\n");
return 0;
}
static void __exit fasync_exit(void)
{
unregister_chrdev(major, dev_name);
class_device_unregister(fasync_class_device);
class_destroy(fasync_class);
printk("exit\n");
}
module_init(fasync_init);
module_exit(fasync_exit);
MODULE_AUTHOR("Ethan Lee <4128127@qq.com>");
MODULE_LICENSE("GPL");
應(yīng)用程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
void signal_fun(int signum);
int fd;
int main()
{
fd = open("/dev/fasync_eint", O_RDWR);
int oflags;
if(fd < 0)
{
printf("open error\n");
return -1;
}
signal(SIGIO, signal_fun);//注冊(cè)捕捉函數(shù)
fcntl(fd, F_SETOWN, getpid());//設(shè)置文件描述符 fd 接收進(jìn)程或者進(jìn)程組接收 SIGIO 或 SIGURG 信號(hào)包警。
oflags = fcntl(fd, F_GETFL);//獲取 flag
fcntl(fd, F_SETFL, oflags | FASYNC);//原來(lái) flag或上 FASYNC撵摆,并設(shè)置 flag
//將 fd 設(shè)置為 FASYNC 方式打開(kāi)
//該設(shè)置,會(huì)觸發(fā)驅(qū)動(dòng)程序中`file_operations`中注冊(cè)的 fasync 函數(shù)害晦。
//驅(qū)動(dòng)程序中特铝,該函數(shù)調(diào)用了 fasync_helper 函數(shù)來(lái)初始化了 fasync_struct 結(jié)構(gòu)體。
//該結(jié)構(gòu)體中維護(hù)了要接收信號(hào)的進(jìn)程 PID(fasync_struct->fa_file->f_owner->pid)
while(1)
{
sleep(1000);
}
return 0;
}
void signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}
那么現(xiàn)在基本上完成了字符設(shè)備的驅(qū)動(dòng)程序的編寫(xiě),還存在一些小問(wèn)題鲫剿,在下次來(lái)解決吧鳄逾。