异步通知机制=====》》fasync

应用层中启用异步通知机制

其实在应用层启用异步通知只三个步骤:
1)signal(SIGIO, sig_handler);
调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。
2)fcntl(fd, F_SET_OWNER, getpid());
指定一个进程作为文件的“属主(filp->owner)”,这样内核才知道信号要发给哪个进程。
3)f_flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, f_flags | FASYNC);
在设备文件中添加FASYNC标志,驱动中就会调用将要实现的test_fasync函数。
三个步骤执行后,一旦有信号产生,相应的进程就会收到。

驱动中需要实现的异步通知

3.1 异步通知内核实现

实现异步通知,内核需要知道几个东西:哪个文件(filp),什么信号(SIGIIO),发给哪个进程(pid),收到信号后做什么(sig_handler)。这些都由上述前两个步骤完成了,而这前两个步骤内核帮忙实现了,所以,我们只需要实现第三个步骤的一个简单的传参。

3.2 fasync_struct结构体

要实现传参,我们需要把一个结构体struct fasync_struct添加到内核的异步队列中,这个结构体用来存放对应设备文件的信息(如fd, filp)并交给内核来管理。一但收到信号,内核就会在这个所谓的异步队列头找到相应的文件(fd),并在filp->owner中找到对应的进程PID,并且调用对应的sig_handler了。
struct fasync_struct {
int magic;
int fa_fd;
struct fasync_struct *fa_next; /* singly linked list */
struct file *fa_file;
};

3.3 内核中我们的工作

上面说了前两个步骤会由内核完成,所以我们只要做两件事情:
1)定义结构体fasync_struct。
struct fasync_struct *async_queue;
2)实现test_fasync,把函数fasync_helper将fd,filp和定义的结构体传给内核。
int test_fasync (int fd, struct file *filp, int mode)
{
struct _test_t *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}
函数fasync_helper的定义为:
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
前面的三个参数其实就是teat_fasync的三个参数,所以只要我们定义好的fasync_struct结构体也传进去就可以了。

3.4 其余的工作

另外还有两件事:
3)当设备可写时,调用函数kill_fasync发送信号SIGIO给内核。
if (dev->async_queue){
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
讲解一下这个函数:
void kill_fasync(struct fasync_struct **fp, int sig, int band)
sig就是我们要发送的信号。
band(带宽),一般都是使用POLL_IN,表示设备可读,如果设备可写,使用POLL_OUT
4)当设备关闭时,需要将fasync_struct从异步队列中删除:
test_fasync(-1, filp, 0);
删除也是调用test_fasync,不过改了一下参数而已。




参考代码:
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sched.h>


#define DEVICE_NAME "adc_device"  
#define ms           30


typedef struct adc_str{
    void __iomem *adc_base;
    struct clk *adc_clk;
    volatile int is_adc_read;
    int adc_data;
    struct fasync_struct *async_queue;
    struct timer_list my_timer;
}adc_str;

adc_str adc_entity ={.is_adc_read = 1};

static void start_adc(void);

void my_timer_func(unsigned long expires)
{
    //while(readl(adc_entity.adc_base + S3C2410_ADCCON)&0x01);
    while(!(readl(adc_entity.adc_base + S3C2410_ADCCON) & 0x8000));
    
    adc_entity.adc_data = readl(adc_entity.adc_base + S3C2410_ADCDAT0) & 0x3ff;

    printk("adc_data = %d\n",adc_entity.adc_data);

    kill_fasync(&adc_entity.async_queue, SIGIO, POLL_IN);
        
    //printk("enter timer intterupt\n");
    mod_timer(&adc_entity.my_timer,jiffies + ms);
}


static irqreturn_t adc_irq_handle(int irq, void *dev_id)
{
   
    if(adc_entity.is_adc_read)
    {
       
        adc_entity.adc_data = readl(adc_entity.adc_base + S3C2410_ADCDAT0) & 0x3ff;

        adc_entity.is_adc_read = 0;
        //wake_up_interruptible(&adc_waitq); 读完以后通知应用程序读取数据
    }

    kill_fasync(&adc_entity.async_queue, SIGIO, POLL_IN);
    printk("enter interrupt,adc data = %d\n",adc_entity.adc_data);
    
    start_adc();

    return IRQ_HANDLED;
}

int adc_conf_init()
{
    
   
    adc_entity.adc_clk = clk_get(NULL, "adc");
    if (!adc_entity.adc_clk)
    {
       
        printk(KERN_ERR "failed to find adc clock source\n");
        return -ENOENT;
    }

   
    clk_enable(adc_entity.adc_clk);

   
    adc_entity.adc_base = ioremap(S3C2410_PA_ADC, 0x20);
    if (adc_entity.adc_base == NULL)
    {
       
        printk(KERN_ERR "Failed to remap register block\n");
        iounmap(adc_entity.adc_base);
    }


    //tmp = (1 << 14) | (255 << 6) | (2 << 3);
    //writel(tmp, adc_entity.adc_base + S3C2410_ADCCON);
    
    return 0;
}

int adc_int_init()
{
    int ret;

   
    ret = request_irq(IRQ_ADC, adc_irq_handle, IRQF_SHARED, DEVICE_NAME, (void *)1);
    if (ret)
    {
       
        printk(KERN_ERR "IRQ%d error %d\n", IRQ_ADC, ret);
        return -EINVAL;
    }

    return 0;
}

static void start_adc(void)
{
    unsigned int tmp;

    tmp = (1 << 14) | (255 << 6) | (2 << 3);
    writel(tmp, adc_entity.adc_base + S3C2410_ADCCON);

    tmp = readl(adc_entity.adc_base + S3C2410_ADCCON);
    tmp = tmp | (1 << 0);                
    writel(tmp, adc_entity.adc_base + S3C2410_ADCCON);
}

static int adc_fasync(int fd, struct file *filp, int mode)
{
    printk("touch fasync\n");
    return fasync_helper(fd,filp,mode,&adc_entity.async_queue);//struct fasync_struct **
}


int adc_release(struct inode *inode, struct file *filp)
{
#if adc_int
    free_irq(IRQ_ADC, (void *)1);    
#else
    del_timer(&adc_entity.my_timer);
    adc_fasync(-1,filp,0);
#endif    
    return 0;
}

static int adc_open(struct inode *inode, struct file *file)
{
    printk("adc_open \n");

    adc_conf_init();
#if adc_int
    adc_int_init();
    printk("#if adc_int\n");
    start_adc();
#else
    start_adc();
    init_timer(&adc_entity.my_timer);
    adc_entity.my_timer.function = my_timer_func;
    add_timer(&adc_entity.my_timer);
#endif   

    return 0;
}


static ssize_t adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{

   
    adc_entity.is_adc_read = 1;

   
    copy_to_user(buffer, (char *)&adc_entity.adc_data, sizeof(adc_entity.adc_data));

    adc_entity.adc_data = 0;

    return sizeof(adc_entity.adc_data);
}



ssize_t adc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{   
    return 1;
}


struct file_operations adc_fops = {
    .owner = THIS_MODULE,
    .read  = adc_read,
   .write = adc_write,
   //.ioctl = adc_ioctl,
    .open = adc_open,
    .release = adc_release,
    .fasync   = adc_fasync,
};

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &adc_fops,
};

int adc_init(void)
{
    int ret;  
    
    printk("adc init NOW\n");

    ret = misc_register(&misc);
   
    if (ret < 0)
    {
        printk(DEVICE_NAME " can't register\n");
      
        return ret;
    }

    writel(0,S3C2410_GPBCON);  
    writel((1<<10)| (1<<12) | (1<<14) | (1<<16)| (1<<18) | (1 << 20),S3C2410_GPBCON);
   
    writel(0,S3C2410_GPBUP);
    writel(0,S3C2410_GPBDAT);
 
    gpio_set_value(S3C2410_GPB5,1);
    gpio_set_value(S3C2410_GPB7,0);
   
    printk("S3C2410_GPB5:%d\t S3C2410_GPB7:%d\n",gpio_get_value(S3C2410_GPB5),gpio_get_value(S3C2410_GPB7));

    printk (DEVICE_NAME" initialized success\n");

    return ret;      
}

void adc_exit(void)
{      

    writel(0,S3C2410_GPBDAT);

    gpio_set_value(S3C2410_GPB5,0);
    gpio_set_value(S3C2410_GPB7,1);
   
    printk("S3C2410_GPB5:%d\t S3C2410_GPB7:%d\n",gpio_get_value(S3C2410_GPB5),gpio_get_value(S3C2410_GPB7));
   
    printk("adc exit\n");
   
    misc_deregister(&misc);
}

module_init(adc_init);
module_exit(adc_exit);
MODULE_AUTHOR("ysj");
MODULE_LICENSE("Dual BSD/GPL");

adc_test.c
#include <stdio.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

static int adc_data = 0;
static int fd;
void sig_handler()
{
   unsigned int i;
   unsigned int ret;
   unsigned int val;
   ret = read(fd,&adc_data, sizeof(int));
   
   if (ret < 0)
   {
     printf("read err!\n");
     return 0;
   }

   printf("application adc number = %d\n",adc_data);
 
   adc_data = 0;
}

int main(int argc, char **argv)
{  
    int ret;

    int f_flags;
    fd = open("/dev/adc_device", O_RDWR);        
   if (fd < 0) {
        printf("Can't open /dev/adc_device\n");
        perror("/dev/adc_device:");
        return -1;
    }

    signal(SIGIO, sig_handler); //指定的信号SIGIO
    fcntl(fd, F_SETOWN, getpid());
    f_flags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, f_flags | FASYNC);
    printf("adc_device is = %d\n",fd);
 
    while (1) {
        sleep(10);
printf("after 1000\n");
    }
    close(fd);
    return 0;    
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值