1. 程式人生 > >Linux核心中斷引入使用者空間(非同步通知機制)

Linux核心中斷引入使用者空間(非同步通知機制)

當linux核心空間發生中斷後怎麼使使用者空間的應用程式執行相應的函式呢,當晶片有資料到來時核心會產生一箇中斷,但是怎樣通知應用程式來取資料,以前這個問題一直困擾我很長時間,後來發現linux中有非同步通知機制,在使用者程式中用signal註冊一個響應SIGIO訊號的回撥函式,然後在驅動程式中向該程序發出SIGIO訊號便完成該功能,下面是該功能具體實施方法:

1.在驅動中定義一個static struct fasync_struct *async;

2.在fasync系統呼叫中註冊fasync_helper(fd, filp, mode, &async);

3.在中斷服務程式(頂半部、底半部都可以)發出訊號kill_fasync(&async, SIGIO, POLL_IN);

4.在使用者應用程式中用signal註冊一個響應SIGIO的回撥函式signal(SIGIO, sig_handler);

5.通過fcntl(fd, F_SETOWN, getpid())將將程序pid傳入核心

6.通過fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC)設定非同步通知

 

驅動部分程式碼:

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <asm-generic/siginfo.h>
#include <linux/init.h>
#include <asm/signal.h>
#include <linux/timer.h>
#include <asm/uaccess.h>
 
#define DEVICE_NAME "mybeep"
 
volatile unsigned long *GPBCON;
volatile unsigned long *GPBDAT;
volatile unsigned long *GPBUP;
void beep_start(void);
void beep_stop(void);
int  beep_irq_register(void);
unsigned int flag=1;
 
static struct fasync_struct *async; //宣告fasync_struct
struct key_irq_desc {
	unsigned int irq;
	int pin;
	int pin_setting;
	int number;
	char *name;
};
 
static int beep_fasync(int fd, struct file *filp, int mode)
{
	printk("application  fasync!\n");
	return fasync_helper(fd, filp, mode, &async);         //註冊上層呼叫程序的資訊,上層呼叫fcntl設定FASYNC會呼叫這個系統呼叫
}
 
static struct key_irq_desc key_irqs [] = {
	{IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 0, "KEY1"},
};
 
static irqreturn_t key_interrupt(int irq, void *dev_id)
{
	kill_fasync(&async, SIGIO, POLL_IN);  //向開啟裝置檔案的程序發出SIGIO訊號
	return (IRQ_HANDLED);
}
 
void beep_gpiob_init(void)
{
	*GPBCON&=~((1<<0)|(1<<1));
	*GPBCON|=(1<<0);
	*GPBUP&=~(1<<0);
}
 
void beep_start(void)
{
	*GPBDAT|=(1<<0);
}
 
void beep_stop(void)
{
	*GPBDAT&=~(1<<0);
}
 
int beep_open(struct inode *inode, struct file *filp)
{
	if(beep_irq_register() != 0)
	{
		printk("Request irq error!\n");
	}
	printk(KERN_ALERT "application  open!\n");
	return 0;
}
 
ssize_t beep_read(struct file *file, char __user *buff, size_t count, loff_t *offp)
{
	printk("application  read!\n");
	return 0;
}
 
ssize_t beep_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)
{
	printk("application  write!\n");
	return 0;
}
 
static int beep_release(struct inode *inode, struct file *file)
{
	disable_irq(key_irqs[0].irq);
	free_irq(key_irqs[0].irq, (void *)&key_irqs[0]);
	printk("application  close!\n");
	return beep_fasync(-1, file, 0);
}
 
static int beep_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	switch(cmd)
	{
	case 0:
		beep_start();
		break;
	case 1:
		beep_stop();
		break;
	default:
		break;
	}
	return 0;
}
 
static struct file_operations beep_ops = {
	.owner = THIS_MODULE,
	.open = beep_open,
	.release = beep_release,
	.ioctl = beep_ioctl,
	.read = beep_read,
	.write = beep_write,
	.fasync = beep_fasync,
};
 
static struct miscdevice beep_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &beep_ops,
};
 
int beep_irq_register(void)
{
	int err;
	err = request_irq(key_irqs[0].irq, key_interrupt, 0, key_irqs[0].name, (void *)&key_irqs[0]);
	set_irq_type(key_irqs[0].irq, IRQ_TYPE_EDGE_RISING);
	if(err)
	{
		disable_irq(key_irqs[0].irq);
		free_irq(key_irqs[0].irq, (void *)&key_irqs[0]);
		return -EBUSY;
	}
	return 0;
}
 
static int __init beep_init(void)
{
	int ret;
	ret=misc_register(&beep_misc);
	if(ret <0)
	{
		printk("register miscdevice error code:%d\n",ret);
		return ret;
	}
	printk("beep device create!\n");
	GPBCON=(volatile unsigned long *)ioremap(0x56000010,12);
	GPBDAT=GPBCON+1;
	GPBUP=GPBCON+2;
	beep_gpiob_init();
	return 0;
}
 
static void __exit beep_exit(void)
{
	iounmap(GPBCON);
	misc_deregister(&beep_misc);
	printk("beep device delete!\n");
}
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kingdragonfly");
module_init(beep_init);
module_exit(beep_exit);

使用者應用程式程式碼:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
 
void sig_handler(int sig)
{
	if(sig == SIGIO)
	{
		printf("Receive io signal from kernel!\n");
	}
}
 
int main(void)
{
	int fd;
	signal(SIGIO, sig_handler);
	fd = open("/dev/mybeep",O_RDWR);
	fcntl(fd, F_SETOWN, getpid());
	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | FASYNC);
	printf("waiting key interrupt:\n");
	while(1)
	{
	}
}


當核心裡發生中斷時在中斷服務程式中發出SIGIO訊號從而自動呼叫相應的回撥函式,在回撥函式中可以進行相應處理。