#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>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/err.h>
#include <linux/smp.h>
volatile int my_value = 0;
struct my_node
{
struct list_head list;
int data;
};
static LIST_HEAD(my_list);
static struct workqueue_struct *own_workqueue;
static void static_wq_fn(struct work_struct *work)
{
struct my_node *node = NULL;
pr_info("Static workqueue function called on CPU[%d]\n", smp_processor_id());
node = kmalloc(sizeof(struct my_node), GFP_KERNEL);
node->data = my_value;
INIT_LIST_HEAD(&node->list);
list_add_tail(&node->list, &my_list);
}
static DECLARE_WORK(static_work, static_wq_fn);
static void dynanic_wq_fn(struct work_struct *work)
{
pr_info("Dynamic workqueue function called on CPU[%d]\n", smp_processor_id());
}
static struct work_struct dynamic_work;
#define IRQ_NO 63
static irqreturn_t irq_handler(int irq, void *dev_id)
{
pr_info("Shared IRQ[%d]: Interrupt Occurred\n", irq);
queue_work(own_workqueue, &static_work);
queue_work_on(3, own_workqueue, &dynamic_work);
return IRQ_HANDLED;
}
static char thread_data1[] = "my_thread1";
static char thread_data2[] = "my_thread2";
static struct task_struct *my_thread1 = NULL;
static struct task_struct *my_thread2 = NULL;
static int thread_fn(void *data)
{
char *name = (char *)data;
int i = 0;
while (!kthread_should_stop())
{
pr_info("In My Thread Function [%s] %d\n", name, i++);
msleep(1000);
}
return 0;
}
dev_t dev = 0;
static struct class *dev_class;
static struct cdev my_cdev;
struct kobject *kobj_ref;
static ssize_t sysfs_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d", my_value);
}
static ssize_t sysfs_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%d", &my_value);
return count;
}
struct kobj_attribute my_attr = __ATTR(my_value, 0660, sysfs_show, sysfs_store);
static ssize_t my_read(struct file *filp,
char __user *buf, size_t len, loff_t *off)
{
struct my_node *node;
int i = 0;
list_for_each_entry(node, &my_list, list)
{
pr_info("Node[%d] { data = %d }\n", i++, node->data);
}
pr_info("Total Nodes = %d\n", i);
return 0;
}
static ssize_t my_write(struct file *filp,
const char __user *buf, size_t len, loff_t *off)
{
char __buf[10] = {0};
if (copy_from_user(__buf, buf, len) == 0)
{
pr_info("Write: %s", __buf);
sscanf(__buf, "%d", &my_value);
}
generic_handle_irq(IRQ_NO);
return len;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
};
static int __init my_driver_init(void)
{
if ((alloc_chrdev_region(&dev, 0, 1, "my_dev")) < 0)
return -1;
cdev_init(&my_cdev, &fops);
my_cdev.owner = THIS_MODULE;
my_cdev.ops = &fops;
if ((cdev_add(&my_cdev, dev, 1)) < 0)
goto r_class;
if (IS_ERR(dev_class = class_create(THIS_MODULE, "my_class")))
goto r_class;
if (IS_ERR(device_create(dev_class, NULL, dev, NULL, "my_device")))
goto r_device;
kobj_ref = kobject_create_and_add("my_sysfs", kernel_kobj);
if (sysfs_create_file(kobj_ref, &my_attr.attr))
goto r_sysfs;
if (request_irq(IRQ_NO, irq_handler, IRQF_SHARED,
"my_device", (void *)(irq_handler)))
goto irq;
INIT_WORK(&dynamic_work, dynanic_wq_fn);
own_workqueue = create_workqueue("own_wq");
if ((my_thread1 = kthread_create(thread_fn, &thread_data1, "My Thread 1")))
wake_up_process(my_thread1);
else
goto r_device;
if (!(my_thread2 = kthread_run(thread_fn, &thread_data2, "My Thread 2")))
goto r_device;
return 0;
irq:
free_irq(IRQ_NO, (void *)(irq_handler));
r_sysfs:
kobject_put(kobj_ref);
sysfs_remove_file(kernel_kobj, &my_attr.attr);
r_device:
device_destroy(dev_class, dev);
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev, 1);
cdev_del(&my_cdev);
return -1;
}
static void __exit my_driver_exit(void)
{
struct my_node *cur, *next;
if (my_thread1)
kthread_stop(my_thread1);
if (my_thread2)
kthread_stop(my_thread2);
list_for_each_entry_safe(cur, next, &my_list, list)
{
list_del(&cur->list);
kfree(cur);
}
destroy_workqueue(own_workqueue);
free_irq(IRQ_NO, (void *)(irq_handler));
kobject_put(kobj_ref);
sysfs_remove_file(kernel_kobj, &my_attr.attr);
device_destroy(dev_class, dev);
class_destroy(dev_class);
cdev_del(&my_cdev);
unregister_chrdev_region(dev, 1);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("feifei <feifei@gmail.com>");
MODULE_DESCRIPTION("A simple device driver");
MODULE_VERSION("1.16");
评论