异步通信主要又两种方式:信号和netlink。下面例子主要是讲述驱动通过中断方式异步通知用户程序:
1、驱动
/*
*
* dsp p6a interrupt
* author: helb
* date: 2018-08-08
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_NAME "p6a"
#define P6A_IOC_MAGIC 'A'
#define P6A_IOCDTBSIZE _IO(P6A_IOC_MAGIC, 0)
#define P6A_IOCGETDTB _IO(P6A_IOC_MAGIC, 1)
#define DSP_P6A_PHYS_ADDR 0xfb007400
#define IOREMAP_SIZE 1024
struct irq_dev {
char *name;
unsigned int irq;
void *reg_base;
};
static struct fasync_struct *p6a_async;
struct irq_dev p6airq= {"p6airq", 9};
static int p6a_open(struct inode *inode, struct file *file)
{
printk("Do nothing
");
return 0;
}
static int p6a_release(struct inode *inode, struct file *file)
{
printk("Do nothing
");
return 0;
}
static ssize_t p6a_write(struct file *file, const char __user *data, size_t len, loff_t *ppos)
{
printk("Do nothing
");
return 0;
}
static long p6a_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
printk("Do nothing
");
return 0;
}
static int p6a_fasync(int fd, struct file *filp, int on)
{
printk("p6a fasync
");
return fasync_helper(fd, filp, on, &p6a_async);
}
static irqreturn_t p6a_interrupt(int irq, void *dev)
{
u32 reg;
reg = readl(p6airq.reg_base + 0x50);
reg &= ~(1<<0);
writel(reg, p6airq.reg_base + 0x50);
kill_fasync(&p6a_async, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
/*
* Kernel Interfaces
*/
static struct file_operations p6a_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = p6a_write,
.unlocked_ioctl = p6a_unlocked_ioctl,
.open = p6a_open,
.release = p6a_release,
.fasync = p6a_fasync,
};
static struct miscdevice p6a_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DRIVER_NAME,
.fops = &p6a_fops,
};
static int __init p6a_init(void)
{
int ret = 0;
ret = misc_register(&p6a_miscdev);
if(ret) {
printk (KERN_ERR "cannot register miscdev (err=%d)
", ret);
return ret;
}
p6airq.reg_base = ioremap(DSP_P6A_PHYS_ADDR, IOREMAP_SIZE);
if(p6airq.reg_base == NULL){
printk("p6a ioremap:%#x failed
", DSP_P6A_PHYS_ADDR);
goto error;
}
ret = request_irq(p6airq.irq, p6a_interrupt, IRQF_SHARED, p6airq.name, (void *)&p6airq);
if(ret < 0){
printk("p6a request_irq failed
");
goto error;
}
return 0;
error:
misc_deregister(&p6a_miscdev);
return -1;
}
static void __exit p6a_exit(void)
{
iounmap(p6airq.reg_base);
free_irq(p6airq.irq, (void *)&p6airq);
misc_deregister(&p6a_miscdev);
}
module_init(p6a_init);
module_exit(p6a_exit);
MODULE_AUTHOR("Byavs");
MODULE_DESCRIPTION("Byavs p6a irq event Device Driver");
MODULE_LICENSE("GPL");
2、用户测试用例
#include
#include
#include
#include
#define DEV_NAME "/dev/p6a"
void sig_handler(int sig)
{
if(sig == SIGIO){
printf("Receive io signal from kernel!
");
}
}
int main(void)
{
int fd;
signal(SIGIO, sig_handler);
fd = open(DEV_NAME, O_RDWR);
if(fd < 0){
printf("open %s failed
", DEV_NAME);
return -1;
}
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);
printf("waiting p6a interrupt
");
while(1){
sleep(1);
}
return 0;
}