一个简单的示例程序。创建一个/sys的接口,可以读写,每次回读都是上次写入的内容。每次读写都会触发一次event。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kthread.h>
static struct cdev test_cdev;
static dev_t test_devno;
static struct class *test_class;
struct device *test_device;
struct test_thread {
wait_queue_head_t wqueue;
unsigned long flags;
struct task_struct *tsk;
unsigned long timeout;
}test_thread;
#define FROM_SHOW 0
#define FROM_STORE 1
static const struct file_operations test_fops =
{
.owner = THIS_MODULE,
};
static int test_fun(void *arg)
{
struct test_thread *thread = arg;
allow_signal(SIGKILL);
while (!kthread_should_stop()) {
/* We need to wait INTERRUPTIBLE so that
* we don't add to the load-average.
* That means we need to be sure no signals are
* pending
*/
if (signal_pending(current))
flush_signals(current);
wait_event_interruptible_timeout
(thread->wqueue,
test_bit(FROM_SHOW, &thread->flags)
|| test_bit(FROM_STORE, &thread->flags)
|| kthread_should_stop(),
thread->timeout);
printk("thread %s is waken up by %s\n",
thread->tsk->comm,
test_bit(FROM_SHOW, &thread->flags) ? "show" :
test_bit(FROM_STORE, &thread->flags) ? "store" : "kill");
thread->flags = 0;
}
return 0;
}
#define TEST_LEN 4096
static char test_buf[TEST_LEN];
static ssize_t test_show(struct device *ddev,
struct device_attribute *attr, char *buf)
{
int len;
struct test_thread *thread;
thread = dev_get_drvdata(ddev);
len = strlen(test_buf) + 1;
memcpy(buf, test_buf, len);
set_bit(FROM_SHOW, &thread->flags);
wake_up(&thread->wqueue);
return len;
}
static ssize_t test_store(struct device *ddev,
struct device_attribute *attr, const char *buf, size_t count)
{
size_t len;
struct test_thread *thread;
thread = dev_get_drvdata(ddev);
if (count < TEST_LEN - 1)
len = count;
else
len = TEST_LEN - 1;
memcpy(test_buf, buf, len);
test_buf[len] = 0;
set_bit(FROM_STORE, &thread->flags);
wake_up(&thread->wqueue);
return count;
}
static DEVICE_ATTR(test1, 0644, test_show, test_store);
static int test_init(void)
{
int ret;
ret = alloc_chrdev_region(&test_devno, 0, 255, "test");
if (ret) {
printk(KERN_INFO "alloc_chrdev_region failed, ret=%d\n", ret);
return ret;
}
cdev_init(&test_cdev, &test_fops);
test_cdev.owner = THIS_MODULE;
ret = cdev_add(&test_cdev, test_devno, 1);
if (ret) {
printk(KERN_INFO "cdev_add failed, ret=%d\n", ret);
goto free_devno;
}
test_class = class_create(THIS_MODULE, "test_class");
if (IS_ERR(test_class)) {
ret = PTR_ERR(test_class);
printk(KERN_INFO "class_create failed, ret=%d\n", ret);
goto free_cdev;
}
test_device = device_create(test_class, NULL, test_devno, NULL, "test");
if (IS_ERR(test_device)) {
ret = PTR_ERR(test_device);
printk(KERN_INFO "device_create failed, ret=%d\n", ret);
goto free_class;
}
ret = device_create_file(test_device, &dev_attr_test1);
if (ret) {
printk(KERN_INFO "device_create_file failed, ret=%d\n", ret);
goto free_device;
}
init_waitqueue_head(&test_thread.wqueue);
test_thread.timeout = MAX_SCHEDULE_TIMEOUT;
test_thread.flags = 0;
dev_set_drvdata(test_device, &test_thread);
test_thread.tsk = kthread_run(test_fun, &test_thread, "test_thread");
if (IS_ERR(test_thread.tsk)) {
ret = PTR_ERR(test_thread.tsk);
printk(KERN_INFO "kthread_run failed, ret=%d\n", ret);
goto free_file;
}
return 0;
free_file:
device_remove_file(test_device, &dev_attr_test1);
free_device:
device_destroy(test_class, test_devno);
free_class:
class_destroy(test_class);
free_cdev:
cdev_del(&test_cdev);
free_devno:
unregister_chrdev_region(test_devno, 255);
return ret;
}
void test_exit(void)
{
kthread_stop(test_thread.tsk);
device_remove_file(test_device, &dev_attr_test1);
device_destroy(test_class, test_devno);
class_destroy(test_class);
cdev_del(&test_cdev);
unregister_chrdev_region(test_devno, 255);
}
MODULE_LICENSE("GPL");
module_init (test_init);
module_exit (test_exit);
没有评论:
发表评论