使用DEVICE_ATTR添加device节点
前言
在驱动调试中,有些特定情况需要增加设备节点来协助分析问题。
例如:在调试I2C芯片时,经常需要去实时读写寄存器。通常情况可以在代码运行到某个地方时增加对应的读写函数,但难免需要编译并且烧录固件,如果重复此操作会严重影响任务周期,此时可以增加读写节点方便操作。
记录一下自己增加的节点代码以及自己的个人理解。
一、读写节点是做什么的?
在 /sys 中生成节点,可用于应用层从驱动中获取数据,也可以直接将数据写入驱动中
二、操作步骤
1.DEVICE_ATTR()宏定义
DEVICE_ATTR的功能就是定义一个device_attribute结构体对象。
LINUX中定义位于include/linux/device.h,内容如下:
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
_name:你的节点名字,在sys路径下可以直接搜索这个东西
_mode:这个节点的权限
400 :拥有者能够读,其他任何人不能进行任何操作;
644 :拥有者都能够读,但只有拥有者可以编辑;
660 :拥有者和组用户都可读和写,其他人不能进行任何操作;
664 :所有人都可读,但只有拥有者和组用户可编辑;
700 :拥有者能够读、写和执行,其他用户不能任何操作;
744 :所有人都能读,但只有拥有者才能编辑和执行;
755 :所有人都能读和执行,但只有拥有者才能编辑;
777 :所有人都能读、写和执行(该设置通常不是好想法)。
当然也可以用S_IWUSR(用户可写),S_IRUSR(用户可读)等宏代替。
_show: 当cat命令作用于该文件时调用该函数
_store: 当echo数据到该文件时调用该函数
实际用例:
static DEVICE_ATTR(chipID, S_IRUGO, sensor_chipID_show, NULL);//
static DEVICE_ATTR(regRead, S_IRUGO | S_IWUSR, sensor_regRead_show, sensor_regRead_store);
static struct device_attribute *sensor_attr_list[] = {
&dev_attr_chipID,
&dev_attr_regRead,
};
然后在应用层就能通过cat和echo命令来对sys创建出来的文件进行读写驱动设备,实现交互.
2.device_create_file
device_create_file利用创建好的device_attribute 在device下创建文件。
这个方法只能一次创建一个属性文件
实际用例:
//struct device *sensor_dev;
int sensor_platform_probe(struct platform_device *pdev) {
int i = 0;
int ret = 0;
//sensor_dev= &pdev->dev;
//如果只有一个节点可以直接这么使用,
//device_create_file(&pdev->dev, &dev_attr_version.attr);
//多个节点可以这么使用
for (i = 0; i < ARRAY_SIZE(sensor_attr_list); i++) {
ret = device_create_file(&pdev->dev, sensor_attr_list[i]);
if(ret)
dev_err(&pdev->dev, "sensor>>>%s: call device_create_file fail\n", __func__);
}
return 0;
}
这个pdev->dev就是该device。可以在sys/对应的device下搜索节点名字
3.读写函数
函数增加需要用节点实现的功能即可,如下示例为I2C的读写,仅供参考
示例:
static ssize_t sensor_regWrite_store(struct device *cd, struct device_attribute *attr, const char *buf, size_t count)
{
int num=2;
uint16_t senosr_regWrite_addr = 0x00000;
int32_t sensor_regWrite_value = 0x00;
if ((!count )|| (count > 48)){
pr_err("sensor>>>%s: wrong count\n", __func__);
return -EINVAL;
}
if (num != sscanf(buf, "0x%04x 0x%02x\n", &senosr_regWrite_addr , &sensor_regWrite_value )){
pr_err("sensor>>>%s: num = %d !=2,senosr_regWrite_addr= 0x%04x, sensor_regWrite_value =0x%02x\n", __func__, num, senosr_regWrite_addr, sensor_regWrite_value );
return -1;
}
down(&sensor_lock);
sensor_WriteByte8_4CH(I2C_SLVAE_ADDR, sensor_regWrite_addr, sensor_regWrite_value);
up(&sensor_lock);
pr_err("sensor>>>%s: sensor_regWrite_addr=0x%04x, sensor_regWrite_value=0x%02x\n", __func__, sensor_regWrite_addr, sensor_regWrite_value);
return count;
}
static ssize_t sensor_regRead_show(struct device* cd,struct device_attribute *attr, char* buf)
{
ssize_t len = 0;
down(&sensor_lock);
len += snprintf(buf+len, PAGE_SIZE-len, "0x%04x 0x%02x\n", sensor_regRead_addr, sensor_regRead_value);
pr_err("sensor>>>%s: sensor_regRead_addr=0x%04x, sensor_regRead_value=0x%02x\n", __func__, sensor_regRead_addr, sensor_regRead_value);
up(&sensor_lock);
return len;
}
类似的还有DRIVER_ATTR,BUS_ATTR,CLASS_ATTR。
它们的区别就是,DEVICE_ATTR对应的文件在/sys/devices/目录中对应的device下面。而其他几个分别在driver,bus,class中对应的目录下。