Kernel driver series-interrupt and timer

1. Interruption

1 Overview:

  The interrupt handling of Linux is divided into the top half and the bottom half. The top half completes the least urgent functions as possible, and often simply completes the work of "registering interrupts".

  That is to hang the bottom half of the processing program to the bottom half of the device's processing queue. However, it is not rigid to think that the interrupt handling in the linux device driver must be divided

  For two halves, if the work to be processed by interruption is very small, it can be completely completed in the top half. View the /proc/interrupts file to get the system

  Statistics of interruptions.

2 Interrupt programming:

  1 Application and release interrupt

    (1) Apply for irq

      int request_irq (unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, 

                void *dev_id)

      irq is the interrupt number to be applied for, handler is the interrupt processing function registered with the system, irq_flags is the attribute of interrupt processing, and the trigger of the interrupt can be specified

      Mode machine processing mode. dev_id will be used when interrupt sharing, generally set to the structure of this device or NULL.

    (2) Release irq

      void free_irq (unsigned int irq, void *dev_id); The parameter definition is the same as request_irq().

   2 Enable and shield interrupt

     (1) 屏蔽:void disable_irq (int irq);

         void disable_irq_nosync (ing irq);//return immediately

         void enable_irq (int irq);

         #define local_irq_save (flags)//Block all of this cpu

         void local_irq_disable (void) //Shield all interrupts of this cpu

     (2) Recovery interruption

      #define local_irq_restore (flags)

      void local_irq_enable (void);

  3 The bottom half of the mechanism-the implementation mechanism mainly includes tasklet, work queue and soft interrupt.

    (1) tasklet

      void my_tasklet_func (unsigned long);

      DECLARE_TASKLET (my_tasklet, my_tasklet_func, data);

      /*Define a tasklet structure my_tasklet, which is associated with the my_tasklet_func(data) function*/

      tasklet_schedule (&my_tasklet);

      /*Make the system schedule the function registered by tasklet at an appropriate time*/

    (2) Work queue

      struct work_struct my_wq;

      void my_wq_func (unsigned long);

      INIT_WORK (&my_wq, (void(*)(void *))my_wq_func, NULL);

      / * Initialize the work queue and bind it to the processing function * /

      schedule_work (&my_wq); /*Schedule work queue execution*/

    (3) Soft interrupt (and usually said soft interrupt (interrupt caused by software instruction), such as arm's swi is a completely different concept)

      In the linux kernel, a softirq_action structure is used to characterize a soft interrupt. This structure contains a soft interrupt handling function pointer and passed to the

      The parameters of the function. Use the open_softirq() function to register the processing function corresponding to the soft interrupt, and the raise_softirq() function can trigger a

      A soft interrupt.

      Soft interrupt and tasklet running and soft interrupt contexts are still a kind of atomic context, while work queues are running and process contexts. therefore,

      Sleep is not allowed in soft interrupt and tasklet processing functions, while sleep is allowed in work queue processing functions.

      local_bh_disable() and local_bh_enable() are functions in the kernel to disable and enable soft interrupts and the bottom half of the tasklet mechanism.

3 Example-s3c2410 real clock interrupt

  Refer to the linux module, in the /drivers/rtc/rtc-s3c.c and /drivers/rtc/interface.c files.

Second, the clock

1 Overview:

  The timer in the software sense is finally realized by the hardware timer. The kernel detects the release of the timer after the clock interrupt occurs, and the timer processing function after the expiration

  Will be executed as the bottom half of the soft interrupt. In driver programming, a set of functions and data structures can be used to complete timer triggering or certain periodic tasks.

2 Data structures and functions used

  (1) An instance of the timer_list structure corresponds to a timer, which is defined as follows:

    struct timer_list {

      struct list_head entry, /*定时器列表*/

      unsigned long expires, /*定时器到期时间*/

      void (*function) (unsigned long), /*定时器处理函数*/

      unsigned long data,/*作为参数被传入定时器处理函数*/

      struct timer_base_s *base,

      ...

    };

    Instantiate struct timer_list my_timer;

   (2) Initialize the timer

    void init_timer (struct timer_list *timer);

    TIMER_INITIALIZER (_function, _expires, _data)

    DEFINE_TIMER (_name, _function, _expires, _data)

    setup_timer ();

   (3) Add timer

    void add_timer (struct timer_list *timer);

   (4) Delete timer

    int del_timer (struct timer_list * timer);

   (5) Modify the expire of the timer

    int mod_timer (struct timer_list *timer, unsigned long expires);

   (6) For periodic tasks, the linux kernel also provides a delayed_work mechanism to complete, which is essentially implemented with work queues and timers.

3 Example-second character device

second_drv.c

 

  1 #include <linux/module.h>
  2 #include <linux/types.h>
  3 #include <linux/fs.h>
  4 #include <linux/errno.h>
  5 #include <linux/mm.h>
  6 #include <linux/sched.h>
  7 #include <linux/init.h>
  8 #include <linux/cdev.h>
  9 #include <asm/io.h>
 10 #include <asm/system.h>
 11 #include <asm/uaccess.h>
 12 #include <linux/slab.h>
 13 
 14 #define SECOND_MAJOR 248
 15 
 16 static int second_major = SECOND_MAJOR;
 17 
 18 struct second_dev {
 19     struct cdev cdev;
 20     atomic_t counter;
 21     struct timer_list s_timer;
 22 };
 23 
 24 struct second_dev *second_devp;
 25 static void second_timer_handle (unsigned long arg)
 26 {
 27     mod_timer (&second_devp->s_timer, jiffies + HZ);
 28     atomic_inc (&second_devp->counter);
 29     printk (KERN_NOTICE "current jiffies is %ld\n", jiffies);
 30 }
 31 int second_open (struct inode *inode, struct file *filp)
 32 {
 33     init_timer (&second_devp->s_timer);
 34     second_devp->s_timer.function = &second_timer_handle;
 35     second_devp->s_timer.expires = jiffies + HZ;
 36     add_timer (&second_devp->s_timer);
 37     atomic_set (&second_devp->counter, 0);
 38     return 0;
 39 }
 40 int second_release (struct inode *inode, struct file *filp)
 41 {
 42     del_timer (&second_devp->s_timer);
 43     return 0;
 44 }
 45 static ssize_t second_read (struct file *filp, char __user *buf,
 46         size_t count, loff_t *ppos)
 47 {
 48     int counter;
 49     counter = atomic_read (&second_devp->counter);
 50     if (put_user (counter, (int *)buf))
 51         return -EFAULT;
 52     else
 53         return sizeof (unsigned int);
 54 }
 55 static const struct file_operations second_fops = {
 56     .owner = THIS_MODULE,
 57     .open = second_open,
 58     .release = second_release,
 59     .read = second_read,
 60 };
 61 static void second_setup_cdev (struct second_dev *dev, int index)
 62 {
 63     int err, devno = MKDEV (second_major, index);
 64     cdev_init (&dev->cdev, &second_fops);
 65     dev->cdev.owner = THIS_MODULE;
 66     err = cdev_add (&dev->cdev, devno, 1);
 67     if (err)
 68         printk (KERN_NOTICE "Error %d adding CDEV %d", err, index);
 69 }
 70 int second_init (void)
 71 {
 72     int ret;
 73     dev_t devno = MKDEV (second_major, 0);
 74     if (second_major)
 75         ret = register_chrdev_region (devno, 1, "second");
 76     else {
 77         return alloc_chrdev_region (&devno, 0, 1, "second");
 78         second_major = MAJOR (devno);
 79     }
 80     if (ret < 0)
 81         return ret;
 82     second_devp = kmalloc (sizeof (struct second_dev), GFP_KERNEL);
 83     if (!second_devp) {
 84         ret = -ENOMEM;
 85         goto fail_malloc;
 86     }
 87     memset (second_devp, 0, sizeof (struct second_dev));
 88     second_setup_cdev (second_devp, 0);
 89     return 0;
 90 fail_malloc:
 91     unregister_chrdev_region (devno, 1);
 92     return ret;
 93 }
 94 void second_exit (void)
 95 {
 96     cdev_del (&second_devp->cdev);
 97     kfree (second_devp);
 98     unregister_chrdev_region (MKDEV (second_major, 0), 1);
 99 }
100 MODULE_AUTHOR ("Ljia-----Ljia");
101 MODULE_LICENSE ("Dual BSD/GPL");
102 module_param (second_major, int, S_IRUGO);
103 module_init (second_init);
104 module_exit (second_exit);

 

second_test.c

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

int main (void)
{
    int fd;
    int counter = 0;
    int old_counter = 0;

    fd = open ("/dev/second", O_RDONLY);
    if (fd != -1) {
        while (1) {
            read (fd, &counter, sizeof (unsigned int));
            if (counter != old_counter) {
                printf ("seconds after open /dev/second: %d\n", 
                        counter);
                old_counter = counter;
            }
        }
    } else {
        printf ("Device open failure\n");
    }
    return 0;
}

 

Guess you like

Origin blog.csdn.net/daocaokafei/article/details/114805857