提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
主要做如下:
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 };
...