设备驱动(九)

中断处理分为上下半部
上半部:完成紧急的,不耗时的操作(ISR)
下半部:不紧急,耗时的操作,一般使用内核延时机制执行下半部
内核延时机制
内核延时机制:内核在某个时间点调用执行指定的函数
内核延时机制实现方式:tasklet/work queue/软中断(软中断不推荐)
内核调度优先级内优先级
中断 > tasklet > work queue
tasklet用法
  1. struct tasklet_struct tlet;                 //定义tasklet
  2. void handler (unsigned long data);   //定义执行函数
  3. tasklet_init(&tlet, handler, args);     //初始化tasklet
  4. tasklet_schedule(&tlet);                  //调度tasklet
work queue用法
  1. struct work_struct my_wq;     //定义结构体
  2. woid do_my_work(struct work_struct *);     //定义执行函数
  3. INIT_WORK(&my_wq, do_my_work);     //初始化工作队列
  4. schedule_work(&my_wq);     //调度工作队列
内核确保在一个jiffy内调度执行函数,在下一个时钟中断来之前调度
jiffy
jiffy是内核时钟中断的间隔
jiffies 是内核的全局变量,在驱动中可以直接访问 该值记录内核时钟中断产生的个数
tasklet与jiffies比较
  tasklet work queue
是否可睡眠 不允许睡眠 可睡眠
是否响应中断 响应中断 响应中断
响应时间 一个jiffy内 执行时间不确定(由内核调度)

tasklet参考代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/interrupt.h>

MODULE_LICENSE ("GPL");

int hello_major = 250;
int hello_minor = 0;
int number_of_devices = 1;

struct hello_device
{
     char data[128];
     int len;
     wait_queue_head_t rq, wq;
     struct semaphore sem;
     struct cdev cdev;
     struct tasklet_struct tlet;
} hello_device;

static int hello_open (struct inode *inode, struct file *filp)
{
     filp->private_data = container_of(inode->i_cdev, struct hello_device, cdev);
     printk (KERN_INFO "Hey! device opened\n");

     return 0;
}

static int hello_release (struct inode *inode, struct file *filp)
{
     printk (KERN_INFO "Hmmm... device closed\n");

     return 0;
}

ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
{
     ssize_t result = 0;
     struct hello_device *dev = filp->private_data;

     down(&dev->sem);
     while (hello_device.len == 0)
     {
          up(&dev->sem);
          if (filp->f_flags & O_NONBLOCK)
               return -EAGAIN;
          if (wait_event_interruptible(dev->rq, (dev->len != 0))) return -ERESTARTSYS;
          down(&dev->sem);
     }

     if (count > dev->len) count = dev->len;
     if (copy_to_user (buff, dev->data, count))
     {
          result = -EFAULT;
     }
     else
     {
          printk (KERN_INFO "wrote %d bytes\n", count);
          dev->len -= count;
          result = count;
          memcpy(dev->data, dev->data+count, dev->len);
     }
     up(&dev->sem);
     wake_up(&dev->wq);

     return result;
}

ssize_t hello_write (struct file *filp, const char  *buf, size_t count, loff_t *f_pos)
{
     ssize_t ret = 0;
     struct hello_device *dev = filp->private_data;

     if (count > 128) return -ENOMEM;
     down(&dev->sem);
     while (dev->len == 128)
     {
          up(&dev->sem);
          if (filp->f_flags & O_NONBLOCK)
               return -EAGAIN;
          if (wait_event_interruptible(dev->wq, (dev->len != 128))) return -ERESTARTSYS;
          down(&dev->sem);
     }

     if (count > (128 - dev->len)) count = 128 - dev->len;
     if (copy_from_user (dev->data+dev->len, buf, count)) {
          ret = -EFAULT;
     }
     else {
          dev->len += count;
          ret = count;
     }

     tasklet_schedule(&dev->tlet);
     printk("in write jiffies=%ld\n",jiffies);
    
     up(&dev->sem);
     wake_up(&dev->rq);

     return ret;
}


struct file_operations hello_fops = {
     .owner = THIS_MODULE,
     .open  = hello_open,
     .release = hello_release,
     .read  = hello_read,
     .write = hello_write
};

static void char_reg_setup_cdev (void)
{
     int error;
     dev_t devno;

     devno = MKDEV (hello_major, hello_minor);
     cdev_init (&hello_device.cdev, &hello_fops);
     hello_device.cdev.owner = THIS_MODULE;
     error = cdev_add (&hello_device.cdev, devno , 1);
     if (error)
          printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
}

void jit_tasklet_fn(unsigned long arg)
{
     printk("in jit_tasklet_fn  jiffies=%ld\n",jiffies);
}

static int __init hello_2_init (void)
{
     int result;
     dev_t devno;

     devno = MKDEV (hello_major, hello_minor);
     result = register_chrdev_region (devno, number_of_devices, "hello");

     if (result < 0) {
          printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
          return result;
     }

     char_reg_setup_cdev ();
     init_waitqueue_head(&hello_device.rq);
     init_waitqueue_head(&hello_device.wq);
     sema_init(&hello_device.sem, 1);
     memset(hello_device.data, 0, 128);
     hello_device.len = 0;

     tasklet_init(&hello_device.tlet, jit_tasklet_fn, (unsigned long)&hello_device);

     printk (KERN_INFO "char device registered\n");

     return 0;
}

static void __exit hello_2_exit (void)
{
     dev_t devno = MKDEV (hello_major, hello_minor);

     cdev_del (&hello_device.cdev);

     unregister_chrdev_region (devno, number_of_devices);
}

module_init (hello_2_init);
module_exit (hello_2_exit);
work queue参考代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/interrupt.h>

MODULE_LICENSE ("GPL");

int hello_major = 250;
int hello_minor = 0;
int number_of_devices = 1;

struct hello_device
{
     char data[128];
     int len;
     wait_queue_head_t rq, wq;
     struct semaphore sem;
     struct cdev cdev;
     //struct tasklet_struct tlet;
     struct work_struct my_wq;
} hello_device;

static int hello_open (struct inode *inode, struct file *filp)
{
     filp->private_data = container_of(inode->i_cdev, struct hello_device, cdev);
     printk (KERN_INFO "Hey! device opened\n");

     return 0;
}

static int hello_release (struct inode *inode, struct file *filp)
{
     printk (KERN_INFO "Hmmm... device closed\n");

     return 0;
}

ssize_t hello_read (struct file *filp, char *buff, size_t count, loff_t *offp)
{
     ssize_t result = 0;
     struct hello_device *dev = filp->private_data;

     down(&dev->sem);
     while (hello_device.len == 0)
     {
          up(&dev->sem);
          if (filp->f_flags & O_NONBLOCK)
               return -EAGAIN;
          if (wait_event_interruptible(dev->rq, (dev->len != 0))) return -ERESTARTSYS;
          down(&dev->sem);
     }

     if (count > dev->len) count = dev->len;
     if (copy_to_user (buff, dev->data, count))
     {
          result = -EFAULT;
     }
     else
     {
          printk (KERN_INFO "wrote %d bytes\n", count);
          dev->len -= count;
          result = count;
          memcpy(dev->data, dev->data+count, dev->len);
     }
     up(&dev->sem);
     wake_up(&dev->wq);

     return result;
}

ssize_t hello_write (struct file *filp, const char  *buf, size_t count, loff_t *f_pos)
{
     ssize_t ret = 0;
     struct hello_device *dev = filp->private_data;

     if (count > 128) return -ENOMEM;
     down(&dev->sem);
     while (dev->len == 128)
     {
          up(&dev->sem);
          if (filp->f_flags & O_NONBLOCK)
               return -EAGAIN;
          if (wait_event_interruptible(dev->wq, (dev->len != 128))) return -ERESTARTSYS;
          down(&dev->sem);
     }

     if (count > (128 - dev->len)) count = 128 - dev->len;
     if (copy_from_user (dev->data+dev->len, buf, count)) {
          ret = -EFAULT;
     }
     else {
          dev->len += count;
          ret = count;
     }

     //tasklet_schedule(&dev->tlet);
     schedule_work(&dev->my_wq);
     printk("in write jiffies=%ld\n",jiffies);
    
     up(&dev->sem);
     wake_up(&dev->rq);

     return ret;
}


struct file_operations hello_fops = {
     .owner = THIS_MODULE,
     .open  = hello_open,
     .release = hello_release,
     .read  = hello_read,
     .write = hello_write
};

static void char_reg_setup_cdev (void)
{
     int error;
     dev_t devno;

     devno = MKDEV (hello_major, hello_minor);
     cdev_init (&hello_device.cdev, &hello_fops);
     hello_device.cdev.owner = THIS_MODULE;
     error = cdev_add (&hello_device.cdev, devno , 1);
     if (error)
          printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);
}

void my_do_work(struct work_struct *mywork)
{
     printk("in my_do_work  jiffies=%ld\n",jiffies);
}

static int __init hello_2_init (void)
{
     int result;
     dev_t devno;

     devno = MKDEV (hello_major, hello_minor);
     result = register_chrdev_region (devno, number_of_devices, "hello");

     if (result < 0) {
          printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
          return result;
     }

     char_reg_setup_cdev ();
     init_waitqueue_head(&hello_device.rq);
     init_waitqueue_head(&hello_device.wq);
     sema_init(&hello_device.sem, 1);
     memset(hello_device.data, 0, 128);
     hello_device.len = 0;

     //tasklet_init(&hello_device.tlet, jit_tasklet_fn, (unsigned long)&hello_device);
     INIT_WORK(&hello_device.my_wq, my_do_work);

     printk (KERN_INFO "char device registered\n");

     return 0;
}

static void __exit hello_2_exit (void)
{
     dev_t devno = MKDEV (hello_major, hello_minor);

     cdev_del (&hello_device.cdev);

     unregister_chrdev_region (devno, number_of_devices);
}

module_init (hello_2_init);
module_exit (hello_2_exit);

你可能感兴趣的:(linux,设备驱动)