Skip to content

Instantly share code, notes, and snippets.

@paranlee
Last active June 11, 2022 06:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paranlee/15f816a874818dfb247a6309a0ccc5c1 to your computer and use it in GitHub Desktop.
Save paranlee/15f816a874818dfb247a6309a0ccc5c1 to your computer and use it in GitHub Desktop.
Run Workqueue Linux kernel module program on rpi4 ARM64 Linux v5.15.x.
/***************************************************************************//**
* \file driver.c
*
* \details Simple Linux device driver (Global Workqueue - 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/workqueue.h> // Required for workqueues
#define IRQ_NO 60
/* Work structure */
static struct work_struct workqueue;
void workqueue_fn(struct work_struct *work);
/*Workqueue Function*/
void workqueue_fn(struct work_struct *work)
{
pr_info("Executing Workqueue Function\n");
}
//Interrupt handler for IRQ_NO.
static irqreturn_t irq_handler(int irq,void *dev_id) {
pr_info("Shared IRQ: Interrupt Occurred");
/*Allocating work to queue*/
schedule_work(&workqueue);
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;
/*
** Function Prototypes
*/
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)
{
pr_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)
{
pr_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)
{
pr_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)
{
pr_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)
{
pr_info("Read function\n");
generic_handle_irq(IRQ_NO);
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)
{
pr_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){
pr_info("Cannot allocate major number\n");
return -1;
}
pr_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){
pr_info("Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
/*if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
pr_info("Cannot create the struct class\n");
goto r_class;
}*/
/*Creating device*/
/*if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
pr_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)){
pr_err("Cannot create sysfs file......\n");
goto r_sysfs;
}*/
if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED, "etx_device", (void *)(irq_handler))) {
pr_info("my_device: cannot register IRQ ");
goto irq;
}
/*Creating work by Dynamic Method */
INIT_WORK(&workqueue,workqueue_fn);
pr_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)
{
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);
pr_info("Device Driver Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("Simple Linux device driver (Global Workqueue - Dynamic method)");
MODULE_VERSION("1.11");
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

Run Workqueue

make
sudo insmod driver.ko
lsmod # check loaded
cat /dev/etx_Dev
sudo rmmod driver.ko
dmesg
[13086.377471] Major = 509 Minor = 0
[13086.377551] Device Driver Insert...Done!!!
[13130.175567] Device File Opened...!!!
[13130.175622] Read function
[13130.175633] Shared IRQ: Interrupt Occurred
[13130.175676] Executing Workqueue Function
[13130.175723] Device File Closed...!!!
[13149.723135] Device Driver Remove...Done!!!

embetronicx : workqueue-in-linux-dynamic-creation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment