7. linux 驱动异步编程
1. 相关接口和结构体
接口:
//初始化接口
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);
结构体:
//文件操作结构体
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
//.read = hello_read,
//.write = hello_write,
.fasync = hello_fasync,
.release = hello_release,
// .unlocked_ioctl = hello_ioctl,
};
需要编写接口 int hello_fasync(int fd, struct file *flip, int mode)
和int hello_release(struct inode *pnode, struct file *pfile)
并注册到上述结构体中。
2. 驱动代码示例
使用定时器定时发送SIGIO信号给应用层
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#define HELLO_CNT 1
//主设备号为0,表示动态分配设备号
dev_t dev = 0;
static int major = 0;
static int minor = 0;
static struct cdev *hello_cdev[HELLO_CNT];
static struct class *hello_class = NULL;
static struct class_device * hello_class_dev[HELLO_CNT];
static struct timer_list tm;
static struct fasync_struct *async = NULL;
int hello_open(struct inode * pnode, struct file * pfile)
{
printk("open file..\n");
int num = MINOR(pnode->i_rdev);
if(num >= HELLO_CNT)
{
return -ENODEV;
}
pfile->private_data = hello_cdev[num];
return 0;
}
int hello_fasync(int fd, struct file *flip, int mode)
{
printk("set fasync..");
return fasync_helper(fd, flip, mode, &async);
}
int hello_release(struct inode *pnode, struct file *pfile)
{
printk("release file ..\n");
return hello_fasync(-1, filp, 0);
}
//文件操作结构体
static const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
//.read = hello_read,
//.write = hello_write,
.fasync = hello_fasync,
.release = hello_release,
// .unlocked_ioctl = hello_ioctl,
};
static void setup_cdev(int index)
{
int err, devno = MKDEV(major, index);
cdev_init(hello_cdev[index], &fops);
hello_cdev[index]->owner = THIS_MODULE;
hello_cdev[index]->ops = &fops;
err = cdev_add(hello_cdev[index], devno, 1);
if(err)
{
printk(KERN_NOTICE "Error %d adding hello%d", err, index);
}
}
void call_back(unsigned long val)
{
printk("timer is out!!\n");
//使用信号通知上层应用
if (async)
{
kill_fasync(&async, SIGIO, POLL_IN);
printk("send SIGIO to app!!\n");
}
//重新注册定时器
tm.expires = jiffies + 2 * HZ;
add_timer(&tm);
}
static int __init hello_init(void)
{
//申请设备号,动态or静态
int ret = 0;
if(major)
{
//为字符设备静态申请第一个设备号
dev = MKDEV(major, minor);
ret = register_chrdev_region(dev, HELLO_CNT, "hello");
}
else
{
//为字符设备动态申请一个设备号
ret = alloc_chrdev_region(&dev, minor, HELLO_CNT, "hello");
major = MAJOR(dev);
}
//构造cdev设备对象
int i = 0;
for(i = 0; i < HELLO_CNT; ++i)
{
hello_cdev[i] = cdev_alloc();
}
//初始化设备对象
for(minor = 0; minor < HELLO_CNT; ++minor)
{
setup_cdev(minor);
}
hello_class = class_create(THIS_MODULE, "hello");
for(minor = 0; minor < HELLO_CNT; ++minor)
{
hello_class_dev[minor] = device_create(hello_class, NULL, MKDEV(major, minor), NULL, "hello%d",minor);
}
//初始化定时器
init_timer(&tm);
tm.function = call_back;
tm.expires = jiffies + 2 * HZ;
add_timer(&tm);
return 0;
}
static void __exit hello_exit(void)
{
for(minor = 0; minor < HELLO_CNT; ++minor)
{
device_destroy(hello_class, MKDEV(major, minor));
}
class_destroy(hello_class);
//从内核注销cdev设备对象
cdev_del(hello_cdev);
//回收设备号
unregister_chrdev_region(dev, HELLO_CNT);
//注销定时器
del_timer(&tm);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
3. 应用层测试代码
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#define DEV_NAME ("/dev/hello0")
void signal_handle(int num)
{
printf("Get SIGIO Signal from kernel!\n");
printf("I need to do something!!\n");
}
int main(int argc, char **argv)
{
int fd = -1;
int flags = -1;
//注册信号捕获函数
signal(SIGIO, signal_handle);
fd = open(DEV_NAME, O_RDWR);
if(fd < 0)
{
printf("can't find dev:%s\n", DEV_NAME);
return -1;
}
//将驱动发出的异步信号绑定到本进程
fcntl(fd, F_SETOWN, getpid());
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);
while(1)
{
sleep(1);
}
return 0;
}