1.前言很多朋友在调试驱动的时候都会遇到这样的场景:修改一个参数,然后调用内核中的一个函数。例如拉高/低一个gpio的值,修改一个寄存器的值等等。如果每个参数都通过字符设备的ioctl接口传递,要加上相应的cmd会比较麻烦。研究内核的计算机高手怎么能容忍这种事情,所以设计了宏DRIVER_ATTR来完美解决这个需求。接下来,一口君将通过一个简单的例子来说明如何使用DRIVER_ATTR。2.DRIVER_ATTR定义宏定义文件如下:include/linux/device.hstructdriver_attribute{structattributeattr;ssize_t(*show)(structdevice_driver*driver,char*buf);ssize_t(*store)(structdevice_driver*driver,constchar*buf,size_tcount);};#defineDRIVER_ATTR(_name,_mode,_show,_store)\structdriver_attribute驱动_attr_##_name=__ATTR(_name,_mode,_show,_store)__ATTR在文件中定义include/linux/sysfs.h#define__ATTR(_name,_mode,_show,_store){\.attr={.name=__stringify(_name),.mode=_mode},\.show=_show,\.store=_store,\}说明_name:Name,是sysfs中会生成的文件名。_mode:以上文件的访问权限与普通文件相同,UGO格式,最高权限为0644,否则会报错。_show:显示函数,cat文件时调用此函数。_store:写函数,回显内容发送到文件时调用该函数。3.使用steps定义写操作回调函数:staticssize_tpeng_test_store(structdevice_driver*driver,constchar*buf,size_tcount){//检查参数if(NULL==buf||count>255||count==0||strnchr(buf,count,0x20))返回-1;printk("buf:%scount:%d\n",buf,count);returncount;}声明函数与文件结点有静态关系DRIVER_ATTR(peng,0644,NULL,peng_test_store);创建文件节点:ret=driver_create_file(&(hello_driver.driver),&driver_attr_peng);if(ret<0){dev_err(&pdev->dev,"无法创建sysfs文件\n");ret=-ENOENT;}这些名称之间的关系如下:4.源码本中的实验代码分为device和driver两个模块,分别定义结构体platform_device和platform_driver注册到平台总线上。完整源代码如下:device.c#include#include#include#includestaticvoidhello_release(structdevice*dev){return;}staticstructplatform_devicehello_device={.name="duang",.id=-1,.dev.release=hello_release,};staticinthello_init(void){printk("hello_init\n");返回platform_device_register(&hello_device);}staticvoidhello_exit(void){printk("hello_exit\n");platform_device_unregister(&hello_device);return;}MODULE_LICENSE("GPL");module_init(hello_init);module_exit(hello_exit);driver.c#include#include#include#include#include#include#include#include#includestaticinthello_probe(structplatform_device*pdev);staticinthello_remove(structplatform_device*pdev);staticssize_tpeng_test_store(structdevice_driver*driver,constchar*buf,size_tcount){if(NULL==buf||count>255||count==0||strnhr(buf,count,0x20))返回-1;printk("buf:%scount:%d\n",buf,count);返回计数;}staticDRIVER_ATTR(peng,0644,NULL,peng_test_store);staticstructplatform_driverhello_driver={.probe=hello_probe,.driver.name="duang",.remove=hello_remove,};structresource*res;staticinthello_probe(structplatform_device*pdev){intret;printk("匹配成功\n");ret=driver_create_file(&(hello_driver.driver),&driver_attr_peng);if(ret<0){dev_err(&pdev->dev,"无法创建sysfs文件\n");ret=-ENOENT;}return0;}staticinthello_remove(structplatform_device*pdev){printk("hello_remove\n");返回0;}staticinthello_init(void){printk("hello_init\n");返回platform_driver_register(&hello_driver);}staticvoidhello_exit(void){printk("hello_exit\n");platform_driver_unregister(&hello_driver);返回;}MODULE_LICENSE("GPL");module_init(hello_init);module_exit(hello_exit);Makefileifneq($(KERNELRELEASE),)obj-m:=device.odriver.oelseKDIR:=/lib/modules/$(shelluname-r)/build#KDIR:=/home/peng/linux-3.14PWD:=$(shellpwd)all:make-C$(KDIR)M=$(PWD)modulesclean:rm-f*.ko*.o*.mod.o*.symvers*.cmd*.mod.c*.orderendif5。编译运行第一步:编译第二步:加载模块驱动第三步:查看生成的文件节点:第四步:通过以下命令向节点输入一个数字(需要管理员权限):echo1>pengFrom结果,我们可以通过向文件pengpeng_test_store()写入一个字符来实现调用函数,将字符1传递给参数buf,将字符个数传递给count其中目录duang是由结构体变量hello_driver给出:staticstructplatform_driverhello_driver={.driver.name="duang",};6.一次注册许多节??点需要调用结构体drivers\input\touchscreen\ads7846.cstaticssize_tads7846_pen_down_show(structdevice*dev,structdevice_attribute*attr,char*buf){structads7846*ts=dev_get_drvdata(dev);返回sprintf(buf,"%u\n",ts->pendown);}staticDEVICE_ATTR(pen_down,S_IRUGO,ads7846_pen_down_show,NULL);staticssize_tads7846_disable_show(structdevice*dev,structdevice_attribute*attr,char*buf){结构ads7846*ts=dev_get_drvdata(dev);返回sprintf(buf,"%u\n",ts->disabled);}staticssize_tads7846_disable_store(structdevice*dev,structdevice_attribute*attr,constchar*buf,size_tcount){structads7846*ts=dev_get_drvdata(dev);无符号整数我;内部错误;错误=kstrtouint(buf,10,&i);如果(错误)返回错误;如果(i)ads7846_disable(ts);否则ads7846_enable(ts);返回计数;}staticDEVICE_ATTR(禁用,0664,ads7846_disable_show,ads7846_disable_store);staticstructattribute*ads784x_attributes[]={&dev_attr_pen_down.attr,&dev_attr_disable.attr,NULL,};staticstructattribute_groupads784x_attr_group={.attrs=ads784x_attributes,};serr=s->dev.kobj,&ads784x_attr_group);7.补充当然,_ATTR不是独生子,他还有一系列的姐妹__ATTR_RO宏只读方法,__ATTR_NULL等。比如对设备使用DEVICE_ATTR,对驱动使用DRIVER_ATTR,对设备使用BUS_ATTR公共汽车。类别(class)使用CLASS_ATTR。不要忘记,这些宏可以在以后调试驱动程序时使用。