This article is a continuation of the Series on Linux Device Drivers and carries the discussion on Linux device drivers and their implementation. The aim of this series is to provide easy and practical examples that anyone can understand. This is the Tasklets in Linux kernel (Dynamic Method) – Linux Device Driver Tutorial Part 21.
Prerequisites
This is the continuation of Interrupts in the Linux Kernel. So I’d suggest you to know some ideas about Linux Interrupts. You can find some useful tutorials about Interrupts and Bottom Halves below.
- Interrupts in Linux Kernel
- Interrupts Example Program
- Workqueue Example – Static Method
- Workqueue Example – Dynamic Method
- Workqueue Example – Own Workqueue
- Tasklet Example – Static Method
Tasklets in Linux Driver
Introduction
In our Previous Tutorial, we have seen the Tasklet using Static Method. In that method, we had initialized the tasklet statically. But in this tutorial, we are going to initialize the tasklet using dynamically. So except the creation of the tasklet, everything will be the same as the previous tutorial. Please refer to the previous tutorial for Scheduling, Enable, Disable, Kill the Tasklet.
Dynamically Creation of Tasklet
tasklet_init
This function used to Initialize the tasklet dynamically.
Where,
|
Example
/* Tasklet by Dynamic Method */ struct tasklet_struct *tasklet; /* Init the tasklet bt Dynamic Method */ tasklet = kmalloc(sizeof(struct tasklet_struct),GFP_KERNEL); if(tasklet == NULL) { printk(KERN_INFO "etx_device: cannot allocate Memory"); } tasklet_init(tasklet,tasklet_fn,0);
Now we will see how the function is working in the background. When I call the function like above, it assigns the parameter to the passed tasklet structure. It will be looks like below.
tasklet->func = tasklet_fn; //function tasklet->data = 0; //data arg tasklet->state = TASKLET_STATE_SCHED; //Tasklet state is scheduled atomic_set(&tasklet->count, 0); //taskelet enabled
NOTE: Please refer to the previous tutorial for the rest of the functions like Scheduling, Enable, Disable, Kill the Tasklet.
Tasklets in Linux Programming
Driver Source Code
In that source code, When we read the device file (/dev/etx_device
), interrupt will hit (To understand interrupts in Linux go to this tutorial). Whenever an interrupt hits, I’m scheduling the task to the tasklet. I’m not going to do any job in both interrupt handler and tasklet function (only print) since it is a tutorial post. But in a real tasklet, this function can be used to carry out any operations that need to be scheduled.
NOTE: In this source code many unwanted functions will be there (which is not related to the Tasklet). Because I’m just maintaining the source code throughout these Device driver series.
[Get the source code from the GitHub]
/****************************************************************************//** * \file driver.c * * \details Simple linux driver (Tasklet Dynamic method) * * \author EmbeTronicX * * *******************************************************************************/ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/kdev_t.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include<linux/slab.h> //kmalloc() #include<linux/uaccess.h> //copy_to/from_user() #include<linux/sysfs.h> #include<linux/kobject.h> #include <linux/interrupt.h> #include <asm/io.h> #include <linux/err.h> #define IRQ_NO 11 void tasklet_fn(unsigned long); /* Tasklet by Dynamic Method */ struct tasklet_struct *tasklet = NULL; /*Tasklet Function*/ void tasklet_fn(unsigned long arg) { printk(KERN_INFO "Executing Tasklet Function : arg = %ld\n", arg); } //Interrupt handler for IRQ 11. static irqreturn_t irq_handler(int irq,void *dev_id) { printk(KERN_INFO "Shared IRQ: Interrupt Occurred"); /*Scheduling Task to Tasklet*/ tasklet_schedule(tasklet); return IRQ_HANDLED; } volatile int etx_value = 0; dev_t dev = 0; static struct class *dev_class; static struct cdev etx_cdev; struct kobject *kobj_ref; static int __init etx_driver_init(void); static void __exit etx_driver_exit(void); /*************** Driver Functions **********************/ static int etx_open(struct inode *inode, struct file *file); static int etx_release(struct inode *inode, struct file *file); static ssize_t etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off); static ssize_t etx_write(struct file *filp, const char *buf, size_t len, loff_t * off); /*************** Sysfs Functions **********************/ static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count); struct kobj_attribute etx_attr = __ATTR(etx_value, 0660, sysfs_show, sysfs_store); /* ** File operation sturcture */ static struct file_operations fops = { .owner = THIS_MODULE, .read = etx_read, .write = etx_write, .open = etx_open, .release = etx_release, }; /* ** This function will be called when we read the sysfs file */ static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { printk(KERN_INFO "Sysfs - Read!!!\n"); return sprintf(buf, "%d", etx_value); } /* ** This function will be called when we write the sysfsfs file */ static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count) { printk(KERN_INFO "Sysfs - Write!!!\n"); sscanf(buf,"%d",&etx_value); return count; } /* ** This function will be called when we open the Device file */ static int etx_open(struct inode *inode, struct file *file) { printk(KERN_INFO "Device File Opened...!!!\n"); return 0; } /* ** This function will be called when we close the Device file */ static int etx_release(struct inode *inode, struct file *file) { printk(KERN_INFO "Device File Closed...!!!\n"); return 0; } /* ** This function will be called when we read the Device file */ static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off) { printk(KERN_INFO "Read function\n"); asm("int $0x3B"); // Corresponding to irq 11 return 0; } /* ** This function will be called when we write the Device file */ static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off) { printk(KERN_INFO "Write Function\n"); return len; } /* ** Module Init function */ static int __init etx_driver_init(void) { /*Allocating Major number*/ if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){ printk(KERN_INFO "Cannot allocate major number\n"); return -1; } printk(KERN_INFO "Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev)); /*Creating cdev structure*/ cdev_init(&etx_cdev,&fops); /*Adding character device to the system*/ if((cdev_add(&etx_cdev,dev,1)) < 0){ printk(KERN_INFO "Cannot add the device to the system\n"); goto r_class; } /*Creating struct class*/ if(IS_ERR(dev_class = class_create(THIS_MODULE,"etx_class"))){ printk(KERN_INFO "Cannot create the struct class\n"); goto r_class; } /*Creating device*/ if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"etx_device"))){ printk(KERN_INFO "Cannot create the Device 1\n"); goto r_device; } /*Creating a directory in /sys/kernel/ */ kobj_ref = kobject_create_and_add("etx_sysfs",kernel_kobj); /*Creating sysfs file for etx_value*/ if(sysfs_create_file(kobj_ref,&etx_attr.attr)){ printk(KERN_INFO"Cannot create sysfs file......\n"); goto r_sysfs; } if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "etx_device", (void *)(irq_handler))) { printk(KERN_INFO "etx_device: cannot register IRQ "); goto irq; } /* Init the tasklet bt Dynamic Method */ tasklet = kmalloc(sizeof(struct tasklet_struct),GFP_KERNEL); if(tasklet == NULL) { printk(KERN_INFO "etx_device: cannot allocate Memory"); goto irq; } tasklet_init(tasklet,tasklet_fn,0); printk(KERN_INFO "Device Driver Insert...Done!!!\n"); return 0; irq: free_irq(IRQ_NO,(void *)(irq_handler)); r_sysfs: kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &etx_attr.attr); r_device: class_destroy(dev_class); r_class: unregister_chrdev_region(dev,1); cdev_del(&etx_cdev); return -1; } /* ** Module exit function */ static void __exit etx_driver_exit(void) { /* Kill the Tasklet */ tasklet_kill(tasklet); if(tasklet != NULL) { kfree(tasklet); } free_irq(IRQ_NO,(void *)(irq_handler)); kobject_put(kobj_ref); sysfs_remove_file(kernel_kobj, &etx_attr.attr); device_destroy(dev_class,dev); class_destroy(dev_class); cdev_del(&etx_cdev); unregister_chrdev_region(dev, 1); printk(KERN_INFO "Device Driver Remove...Done!!!\n"); } module_init(etx_driver_init); module_exit(etx_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("EmbeTronicX <[email protected]>"); MODULE_DESCRIPTION("A simple device driver - Tasklet Dynamic"); MODULE_VERSION("1.16");
MakeFile
obj-m += driver.o KDIR = /lib/modules/$(shell uname -r)/build all: make -C $(KDIR) M=$(shell pwd) modules clean: make -C $(KDIR) M=$(shell pwd) clean
Building and Testing Driver
- Build the driver by using Makefile (
sudo make
) - Load the driver using
sudo insmod driver.ko
- To trigger the interrupt read device file (
sudo cat /dev/etx_device
) - Now see the Dmesg (
dmesg
)
linux@embetronicx-VirtualBox: dmesg [12372.451624] Major = 246 Minor = 0 [12372.456927] Device Driver Insert...Done!!! [12375.112089] Device File Opened...!!! [12375.112109] Read function [12375.112134] Shared IRQ: Interrupt Occurred [12375.112139] Executing Tasklet Function : arg = 0 [12375.112147] Device File Closed...!!! [12377.954952] Device Driver Remove...Done!!!
- We can able to see the print “Shared IRQ: Interrupt Occurred“ and “Executing Tasklet Function: arg = 0“
- Unload the module using
sudo rmmod driver
In our next tutorial, we will discuss Mutex in the Linux device driver. You can read the softirq tutorial here.
Please find the other Linux device driver tutorials here.
You can also read the below tutorials.

Embedded Software | Firmware | Linux Devic Deriver | RTOS
Hi, I’m SLR. I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!