创建字符设备驱动

文章介绍了在Linux系统中如何操作字符设备驱动,包括在设备树中增加platform_device,编写字符设备驱动代码,修改设备权限以及处理SELinux权限问题。具体步骤包括设备树配置,驱动代码实现如open、read、write函数,以及如何处理SELinux的denied权限错误,通过定义file_contexts和device.te文件来授予shell对设备的访问权限。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

主要做如下:
1.设备树增加对应的platform_device
2.创建字符设备驱动
3.修改字符设备dev/Test_A的权限
4.创建dev/Test_A的SELinux权限,修改shell(subject主体)对dev/Test_A(object客体)的SELinux权限


1.设备树

设备树xxx.dts

	odm: odm {
		compatible = "simple-bus";
		/* reserved for overlay by odm */
	};

设备树自定义c_xxx.dts

#define ROOT_NODE &odm

ROOT_NODE {
	...
	zzh_test_a: zzh_test_a {
        compatible = "mediatek,zzh_test_a";
	};
	...
}

2.字符设备驱动代码

#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

#define NODE_NAME "Test_A"

typedef struct { 

	dev_t devt;							/* Device ID */ 		
	struct cdev cdev;					/* cdev */ 
	struct class *class;				
	struct device *device;				
	int major;							/* Major device id */ 
	int minor;							/* Minor device id */ 

	unsigned int device_count;			
	char test_store_data[128];			/* store data(r/w) */

} dev_info;


static int test_a_open(struct inode *inode, struct file *file)
{   
	dev_info *m_dev_info;
	int ret = 0;

	printk("%s enter\n", __func__);

	//通过结构体成员变量地址inode->i_cdev,找到结构体dev_info
	m_dev_info = container_of(inode->i_cdev, dev_info, cdev);
	if (IS_ERR(m_dev_info)) {
		ret = PTR_ERR(m_dev_info);
		printk(KERN_ERR, " %s pointer err, err code %d\n", __func__, ret);
	} else {
		file->private_data = m_dev_info;
	}
	
	return ret;
}
 
 
static ssize_t test_a_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	int ret = 0;
	dev_info *m_dev_info = file->private_data;
	
	if (IS_ERR(m_dev_info)) {
		ret = PTR_ERR(m_dev_info);
		printk(KERN_ERR, " %s pointer err, err code %d\n", __func__, ret);
	} else {
		//使用cat进行调试,建议使用simple_read_from_buffer
		return simple_read_from_buffer(buf, count, ppos, m_dev_info->test_store_data, strlen(m_dev_info->test_store_data));
	}

	return -1;
}

 
static ssize_t test_a_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
	int i = 0;
	int ret = 0;
	dev_info *m_dev_info;
	m_dev_info = (dev_info *) file->private_data;

	printk("%s enter\n", __func__);

	if (IS_ERR(m_dev_info)) {
		ret = PTR_ERR(m_dev_info);
		printk(KERN_ERR, " %s pointer err, err code %d\n", __func__, ret);
		return 0;
	}

	memset(m_dev_info->test_store_data, 0x00, sizeof(m_dev_info->test_store_data));
	if (copy_from_user(&m_dev_info->test_store_data, buf, sizeof(m_dev_info->test_store_data)))
		return -EFAULT;

	for (i = 0; i < count; i++) {
		printk("%s write_value[%d] = %x \n", __func__, i, m_dev_info->test_store_data[i]);
	}

	return count;
}
 

static const struct file_operations test_a_fops = {
	.owner = THIS_MODULE,
	.open = test_a_open,
	.read = test_a_read,
	.write = test_a_write
};

static dev_info *create_test_a_cdev(struct platform_device *pdev)
{
	int ret = 0;
	dev_info *device_info;

	//开辟内存
	device_info = devm_kzalloc(&pdev->dev, sizeof(dev_info), GFP_KERNEL);
	device_info->device_count = 1;

	//1.没有定义设备号,动态分配设备号
	ret = alloc_chrdev_region(&device_info->devt, 0, device_info->device_count, NODE_NAME);
	if (ret) {
		printk("%s failed to allocate char device region, error code %d\n", __func__, ret);
		goto alloc_chrdev_fial;
	}

	//获取字符设备主设备,从设备号
	device_info->major = MAJOR(device_info->devt);
	device_info->minor = MINOR(device_info->devt);
 
	//2.初始化字符设备
	cdev_init(&device_info->cdev, &test_a_fops);

	//3.添加字符设备
	ret = cdev_add(&device_info->cdev, device_info->devt, device_info->device_count);
	if (ret) {
		printk("%s cdev_add failed\n", __func__);
		goto cdev_add_fial;
	}

	//创建class
	device_info->class = class_create(THIS_MODULE, NODE_NAME); 
	if (IS_ERR(device_info->class)) { 
		ret = PTR_ERR(device_info->class);
		goto class_create_fail;
	}
	
	//创建节点
	device_info->device = device_create(device_info->class, &pdev->dev, device_info->devt, NULL, NODE_NAME); 
	if (IS_ERR(device_info->device)) { 
		ret = PTR_ERR(device_info->device);
		goto device_create_fail;
	}

	return device_info;

device_create_fail:
	class_destroy(device_info->class);

class_create_fail:
	cdev_del(&device_info->cdev);

cdev_add_fial:
	unregister_chrdev_region(device_info->devt, device_info->device_count);

alloc_chrdev_fial:
 
	return NULL;

}


static int test_a_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const struct of_device_id *match_score;
	dev_info *device_info;
	int ret = 0;

	//配对分数:==0无对应设备,>0有对应设备
	match_score = of_match_device(dev->driver->of_match_table, dev);
	if (IS_ERR(match_score)) {
		ret = PTR_ERR(match_score);
		printk("%s device match failed!!! error code\n", __func__, ret);
		return ret;
	}

	//创建字符设备
	device_info = create_test_a_cdev(pdev);

	//把device_info设置为这个驱动的私有数据 
	if (device_info != NULL) {
		printk("%s create char device succeed!!! \n", __func__);
		platform_set_drvdata(pdev, device_info);
	} else {
		printk("%s create char device fail!!! \n", __func__);
	}

	return 0;
}


static int test_a_remove(struct platform_device *pdev)
{
	dev_info *device_info;
	int ret = 0;

	printk(KERN_INFO "++ %s: platform driver removed!\n", __func__);
 
	//获取这个驱动私有数据
	device_info = platform_get_drvdata(pdev);
	if (IS_ERR(device_info)) {
		ret = PTR_ERR(device_info);
		printk(KERN_ERR " %s remove error!\n", __func__);
		return ret; 
	} 

	device_destroy(device_info->class, device_info->devt); 
	class_destroy(device_info->class); 
	cdev_del(&device_info->cdev); 
	unregister_chrdev_region(device_info->devt, device_info->device_count); 
	platform_set_drvdata(pdev, NULL);

 	return 0;
}

static const struct of_device_id mt_test_a_match_table[] = {
	{.compatible = "mediatek,zzh_test_a",},
	{},
};


static struct platform_driver test_a_driver = {
	.driver		= {
		.name	= "zzh_test_a",
		.of_match_table = mt_test_a_match_table,
		.owner	= THIS_MODULE,
	},
    .probe		= test_a_probe,
	.remove		= test_a_remove,
};


static int __init test_a_init(void)
{
	int ret = 0;

	printk("%s\n", __FUNCTION__); 

	ret = platform_driver_register(&test_a_driver);

	if (ret) {
        printk("failed to register test_a_device\n");
	}

	return 0;
} 


static void __exit test_a_exit(void)
{
    platform_driver_unregister(&test_a_driver);
}


late_initcall(test_a_init);
module_exit(test_a_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZZH");

3.修改字符设备dev/Test_A的权限

init.rc

on init
	...
	chmod 0666 dev/Test_A
	...

4.修改SELinux权限

出现问题:

avc: denied { write } for name="Test_A" dev="tmpfs" ino=3963 scontext=u:r:shell:s0 tcontext=u:object_r:device:s0 tclass=chr_file permissive=0

分析问题:
scontext=u:r: shell:s0 中shell对tcontext=u:object_r:device:s0中device没有权限,Test_A是chr_file类型,permissive=0宽容模式没打开

解决问题:
1.定义object
/mediatek/sepolicy/bsp/non_plat/file_contexts

...
#emdoor char device
/dev/Test_A u:object_r:test_a_device:s0
...

2.定义object类型
/mediatek/sepolicy/bsp/non_plat/device.te

...
type test_a_device, dev_type;
...

3.让shell可以访问dev/Test_A

...
allow shell test_a_device:chr_file { open read write };
...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值