代码拉取完成,页面将自动刷新
#include <linux/compat.h>
#include <linux/delay.h>
#include <linux/file.h>
#include <linux/idr.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/completion.h>
#include <linux/kthread.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/eventpoll.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
struct test_dev {
const char *name;
const char *drv_name;
const char *api_ver;
int status;
struct device *pdev;
u32 dev_id;
unsigned long dev_state;
struct cdev cdev;
struct device dev;
void *priv;
};
static struct class *test_class;
static DEFINE_IDR(test_idr);
static dev_t test_devt;
#define TEST_DEV_FROM_DEV_ATTR(dev) container_of(dev, struct test_dev, dev)
static ssize_t id_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct test_dev *testdev = TEST_DEV_FROM_DEV_ATTR(dev);
return sprintf(buf, "%d\n", testdev->dev_id);
}
static DEVICE_ATTR_RO(id);
static ssize_t api_ver_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct test_dev *testdev = TEST_DEV_FROM_DEV_ATTR(dev);
return sprintf(buf, "%s\n", testdev->api_ver);
}
static DEVICE_ATTR_RO(api_ver);
static ssize_t dev_state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
// struct test_dev *testdev = TEST_DEV_FROM_DEV_ATTR(dev);
struct test_dev *testdev = dev_get_drvdata(dev);
return sprintf(buf, "dev_state %lu\n", testdev->dev_state);
}
static ssize_t dev_state_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
// struct test_dev *testdev = TEST_DEV_FROM_DEV_ATTR(dev);
struct test_dev *testdev = dev_get_drvdata(dev);
unsigned long val = 0;
pr_info("dev_state store buf: %s, count %lu\r\n", buf, count);
if (kstrtoul(buf, 0, &val) < 0)
return -EINVAL;
testdev->dev_state = val;
return count;
}
static DEVICE_ATTR_RW(dev_state);
static struct attribute *test_dev_attrs[] = {
&dev_attr_id.attr,
&dev_attr_dev_state.attr,
&dev_attr_api_ver.attr,
NULL,
};
static const struct attribute_group test_dev_attr_group = {
.name = "attrs",
.attrs = test_dev_attrs,
};
static const struct attribute_group *test_dev_attr_groups[] = {
&test_dev_attr_group, NULL
};
static void tasklet_work(struct tasklet_struct *task)
{
pr_info("call %s task %p\n", __func__, task);
}
DECLARE_TASKLET(aaa, tasklet_work);
static void test_work_handler(struct work_struct *w)
{
pr_info("call %s work %p\n", __func__, w);
}
struct work_struct bbb;
static int test_fops_open(struct inode *inode, struct file *filep)
{
pr_info("call %s\r\n", __func__);
schedule_work(&bbb);
tasklet_schedule(&aaa);
return 0;
}
static int test_fops_release(struct inode *inode, struct file *filep)
{
pr_info("call %s\r\n", __func__);
return 0;
}
static long test_fops_unl_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg)
{
pr_info("call %s\r\n", __func__);
return 0;
}
#ifdef CONFIG_COMPAT
static long test_fops_compat_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg)
{
pr_info("call %s\r\n", __func__);
arg = (unsigned long)compat_ptr(arg);
return test_fops_unl_ioctl(filep, cmd, arg);
}
#endif
static int test_fops_mmap(struct file *filep, struct vm_area_struct *vma)
{
pr_info("call %s\r\n", __func__);
return -1;
}
static const struct file_operations testdev_fops = {
.owner = THIS_MODULE,
.open = test_fops_open,
.release = test_fops_release,
.unlocked_ioctl = test_fops_unl_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = test_fops_compat_ioctl,
#endif
.mmap = test_fops_mmap,
// .poll = testdev_fops_poll,
};
static void test_dev_release(struct device *dev)
{
}
static int test_create_chrdev(struct test_dev *testdev)
{
int ret;
ret = idr_alloc(&test_idr, testdev, 0, 0, GFP_KERNEL);
if (ret < 0)
return ret;
cdev_init(&testdev->cdev, &testdev_fops);
testdev->dev_id = ret;
testdev->cdev.owner = THIS_MODULE;
device_initialize(&testdev->dev);
testdev->dev.devt = MKDEV(MAJOR(test_devt), testdev->dev_id);
testdev->dev.class = test_class;
testdev->dev.groups = test_dev_attr_groups;
// testdev->dev.parent = testdev->pdev;
testdev->dev.release = test_dev_release;
// dev_set_name(&testdev->dev, "%s-%d", testdev->name, testdev->dev_id);
// dev_set_name(&testdev->dev, "%s", testdev->name);
dev_set_drvdata(&testdev->dev, testdev);
ret = cdev_device_add(&testdev->cdev, &testdev->dev);
if (ret)
goto err_with_idr;
// device_create_file(&testdev->dev, &dev_attr_dev_state);
dev_dbg(&testdev->dev, "create testdev minior=%d\n", testdev->dev_id);
return 0;
err_with_idr:
idr_remove(&test_idr, testdev->dev_id);
return ret;
}
static void test_destroy_chrdev(struct test_dev *testdev)
{
// device_remove_file(&testdev->dev, &dev_attr_dev_state);
cdev_device_del(&testdev->cdev, &testdev->dev);
put_device(&testdev->dev);
memset(&testdev->dev, 0, sizeof(struct device));
idr_remove(&test_idr, testdev->dev_id);
}
struct test_dev *global_testdev;
static int __init chrdev_init(void)
{
int ret;
INIT_WORK(&bbb, test_work_handler);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)
test_class = class_create("test");
#else
test_class = class_create(THIS_MODULE, "test");
#endif
if (IS_ERR(test_class)) {
ret = PTR_ERR(test_class);
goto err;
}
pr_debug("call %s\r\n", __func__);
pr_info("call %s\r\n", __func__);
ret = alloc_chrdev_region(&test_devt, 0, MINORMASK, "mychar");
if (ret)
goto err_with_class;
global_testdev = kmalloc(sizeof(struct test_dev), GFP_KERNEL);
if (!global_testdev) {
goto err_with_class;
}
global_testdev->name = "testdev";
global_testdev->drv_name = "testdev_drv";
global_testdev->api_ver = "0.0.1";
ret = test_create_chrdev(global_testdev);
if (ret) {
kfree(global_testdev);
global_testdev = NULL;
goto err_with_class;
}
pr_info("Init with major number:%d\n", MAJOR(test_devt));
return 0;
err_with_class:
class_destroy(test_class);
err:
return ret;
}
static void __exit chrdev_exit(void)
{
pr_info("call %s\r\n", __func__);
pr_debug("call %s\r\n", __func__);
if (global_testdev) {
test_destroy_chrdev(global_testdev);
kfree(global_testdev);
}
unregister_chrdev_region(test_devt, MINORMASK);
class_destroy(test_class);
idr_destroy(&test_idr);
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_DESCRIPTION("Module named chrdev.");
MODULE_LICENSE("GPL");
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。