#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");
评论