2021.10.11

  • Post author:
  • Post category:其他



作业:在sysfs中建立一个属性,可以通过shell更改属性,可查询

参考:

Linux内核宏DEVICE_ATTR使用 – Cqlismy – 博客园 (cnblogs.com)

思路:通过驱动建立属性?查询资料得知在include/linux.h/device.h中有添加非默认属性的接口:device_attribute。在linux驱动程序编写中使用DEVICE_ATTR宏,可以定义一个struct device_attribute设备属性,再使用sysfs的API函数就可以在设备目录下创建出属性文件,当我们在驱动程序中实现了show和store函数后就可以使用cat和echo命令对创建出来的设备属性文件进行读写从而达到控制设备的功能。

结论:在驱动中实现show和store功能就可以实现属性更改和查询

需掌握的知识点:struct attribute结构体(在linux/device.h中有定义)

struct device_attribute(在linux/device.h中有定义)

宏DEVICE_ATTR的实现(在linux/device.h和linux/sysfs.h中有定义)

模块加载函数和模块卸载函数

make之后的操作


know1:struct attribute结构体

struct attribute {


const char        *name;

umode_t            mode;

#ifdef CONFIG_DEBUG_LOCK_ALLOC

bool            ignore_lockdep:1;

struct lock_class_key    *key;

struct lock_class_key    skey;

#endif

};

name:属性名称 mode:属性的读写权限


know2:struct device_attribute结构体

/* interface for exporting device attributes */

struct device_attribute {


struct attribute    attr;

ssize_t (*show)(struct device *dev, struct device_attribute *attr,

char *buf);

ssize_t (*store)(struct device *dev, struct device_attribute *attr,

const char *buf, size_t count);

};

show:读取设备属性文件 store:写设备的属性文件

*驱动中实现后可以用cat和echo读写


know3:DEVICE_ATTR

#define DEVICE_ATTR(_name,_mode,_show,_store)        \

struct device_attribute dev_attribute dev_attr_##_name = __ATTR(_name,_mode,_show,_store)

宏的功能:定义一个struct device_attribute结构体变量dev_attr_name并对里面的成员进行初始化,包括struct attribute结构体里面的name和mode成员变量、实现属性文件读写的show和store函数赋值。

*代码中的”\”表示换行,无实际意义


know4:应用实例

static ssize_t  mydevice_show(struct device *dev,struct device_attribute *attr,char *buf){

return sprintf(buf,”%s\n”,mybuf);

}

static ssize_t mydevice_store(struct device *dev,struct devie_attribute *attr,const char *buf,size_t count){

sprintf(mybuf,”%s”,buf);

return count;

}

static DEVICE_ATTR(mydevice,0644,mydevice_show,mydevice_store);

可以看出,要使用DEVICE_ATTR宏,先对_show和_store函数进行定义,再通过宏初始化。

上述代码定义了一个mydevice的属性文件,这个属性文件的读写权限为:拥有者可以读写,所属组和其他人只能读。cat和echo命令会调用到上面写的show和store函数。

*权限:0644:用户具有读写权限,组和其他用户具有只读权限

0755:用户具有读写执行权限,组和其他用户具有只读权限

一般,目录0755,文件0644权限

*ssize_t表示有符号整形,在32位机器上等同int,在64位机器上等同long int;size_t表示无符号整形,unsigned long/unsigned int

*const:定义一个在整个作用域值都不能被改变的变量


know5:加载模块

static int __init mydevice_init(void){

int ret;

struct device *mydevice;

major = register_chrdev(0,”mydevice”,&myfops);

if(major<0){

ret = major;

return ret;

}

myclass = class_create(THIS_MODULE,”myclass”);

if(IS_ERR(myclass)){

ret = -EBUSY;

goto fail;

}

mydevice = device_create(myclass,NULL,MKDEV(major,0),NULL,”mydevice”);

if(IS_ERR(mydevice)){

class_destory(myclass);

ret = -EBUSY;

goto fail;

}

ret = sysfs_create_file(&mydevice->kobj,&dev_attr_mydevice.attr);

if(ret<0)

return ret;

return 0;

fail:

unregister_chrdev(major,”mydevice”);

return ret;

}

register_chrdev()完成对主设备号的动态申请,且注册设备名称为mydevice,再用class_create()和device_create()在sysfs中动态创建出设备所属的类myclass和设备mydevice并对返回结果进行了错误检测,最后使用sysfs的API函数在sys中创建出设备的属性文件,完成模块加载。

*register_chrdev()是注册设备驱动程序的内核函数,需要的参数:

major,主设备号,当设置为0时内核会动态分配一个设备号

baseminor:次设备号,在一定范围内从0开始

count:次设备号的范围

name:设备名称

fops:文件系统的接口指针

在上述代码中主设备号是动态分配的,没有使用次设备号。因此baseminor和count都没有出现。当major为0时且正常注册后,返回分配的主设备号;注册失败则返回-EBUSY。当major不为0时,若指定的major值已有注册的设备,返回-EBUSY;注册成功则返回0.

*卸载模块使用device_destory(myclass,MKDEV(major,0)),class_destory(myclass),unregister_chrdev(major,”mydevice”)

*MKDEV宏:MKDEV(MAJOR,MINOR)获取设备在设备表中的位置,major为主设备号,minor为次设备号,为0表示不能使用

*__函数代表内核级函数


know6:模块编译运行

将文件保存为test_cha.c

编写makefile,后在命令行:

make

sudo insmod test_cha.ko

sudo dmesg

cd /sys/device/virtual/myclass/mydevice/

ls

可以看到其中有我们创建的属性文件mydevice。

*sys/device/virtual中存放的是我们创建的东西

用cat和echo进行测试。

cat mydevice

echo “success”>mydevice

这里如果使用su进入root用户模式后可以直接这么操作,但如果没有进入该模式,只用sudo是无法解决权限不足的情况的,需要使用sudo bash -c ‘echo “success”>mydevice’,就不会报错了。

这之后再用cat获取mydevice的值,就可以看到改变了。



版权声明:本文为qq_43416724原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。