Linux驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数
1. 前言
很多朋友在調(diào)試驅(qū)動(dòng)的時(shí)候,都會(huì)遇到這樣一個(gè)場(chǎng)景:修改一個(gè)參數(shù),然后調(diào)用某個(gè)內(nèi)核中的函數(shù)。
比如將某個(gè)gpio的值拉高/拉低,修改某個(gè)寄存器的值等等。
如果每一個(gè)參數(shù)都通過字符設(shè)備的ioctl接口,增加對(duì)應(yīng)的cmd,會(huì)比較麻煩,
研究?jī)?nèi)核的計(jì)算機(jī)大牛門怎么會(huì)容忍這種事發(fā)生,
于是設(shè)計(jì)出了DRIVER_ATTR這個(gè)宏,完美解決這個(gè)需求。
下面一口君通過一個(gè)簡(jiǎn)單的實(shí)例,給大家講解如何使用DRIVER_ATTR。
2. DRIVER_ATTR定義
該宏定義的文件如下:include/linux/device.h
struct?driver_attribute?{struct?attribute?attr;ssize_t?(*show)(struct?device_driver?*driver,?char?*buf);ssize_t?(*store)(struct?device_driver?*driver,?const?char?*buf,size_t?count); };#define?DRIVER_ATTR(_name,?_mode,?_show,?_store)?\struct?driver_attribute?driver_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:名稱,也就是將在sys fs中生成的文件名稱。_mode:上述文件的訪問權(quán)限,與普通文件相同,UGO的格式,最高權(quán)限0644,否則會(huì)報(bào)錯(cuò)。_show:顯示函數(shù),cat該文件時(shí),此函數(shù)被調(diào)用。_store:寫函數(shù),echo內(nèi)容到該文件時(shí),此函數(shù)被調(diào)用。3. 使用步驟
定義一個(gè)寫操作的回調(diào)函數(shù):
static?ssize_t?peng_test_store(struct?device_driver?*driver,const?char?*buf,?size_t?count) { //對(duì)參數(shù)進(jìn)行檢查if(NULL?==?buf?||?count?>255?||?count?==?0?||?strnchr(buf,?count,?0x20))return?-1;printk("buf:%s?count:%d\n",buf,count);return?count; }聲明該函數(shù)與文件節(jié)點(diǎn)關(guān)系
static?DRIVER_ATTR(peng,?0644,?NULL,?peng_test_store);創(chuàng)建文件節(jié)點(diǎn):
ret?=?driver_create_file(&(hello_driver.driver),?&driver_attr_peng);if?(ret?<?0){dev_err(&pdev->dev,?"could?not?create?sysfs?files\n");ret?=?-ENOENT;}這幾個(gè)名字之間關(guān)系如下:
4. 源碼
本實(shí)驗(yàn)代碼分為兩個(gè)模塊 device、driver, 分別定義結(jié)構(gòu)體platform_device、platform_driver并注冊(cè)到platform總線。
完整源碼如下:
device.c
#include?<linux/init.h> #include?<linux/module.h> #include?<linux/platform_device.h> #include?<linux/ioport.h> static?void?hello_release(struct?device?*dev) {return; } static?struct?platform_device?hello_device?=? {.name?=?"duang",.id?=?-1,.dev.release?=?hello_release, }; static?int?hello_init(void) {printk("hello_init?\n");return?platform_device_register(&hello_device);} static?void?hello_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?<linux/init.h> #include?<linux/module.h> #include?<linux/kdev_t.h> #include?<linux/fs.h> #include?<linux/cdev.h> #include?<linux/device.h> #include?<asm/io.h> #include?<linux/platform_device.h> #include?<linux/ioport.h>static?int?hello_probe(struct?platform_device?*pdev); static??int?hello_remove(struct?platform_device?*pdev);static?ssize_t?peng_test_store(struct?device_driver?*driver,const?char?*buf,?size_t?count) {if(NULL?==?buf?||?count?>255?||?count?==?0?||?strnchr(buf,?count,?0x20))return?-1;printk("buf:%s?count:%d\n",buf,count);return?count; } static?DRIVER_ATTR(peng,?0644,?NULL,?peng_test_store);static?struct?platform_driver?hello_driver?= {.probe?=?hello_probe,.driver.name?=?"duang",.remove?=?hello_remove,?? };struct?resource?*res; static?int?hello_probe(struct?platform_device?*pdev) {int?ret;printk("match?ok?\n");ret?=?driver_create_file(&(hello_driver.driver),?&driver_attr_peng);if?(ret?<?0){dev_err(&pdev->dev,?"could?not?create?sysfs?files\n");ret?=?-ENOENT;}return?0; } static??int?hello_remove(struct?platform_device?*pdev) {printk("hello_remove?\n");return?0; }static?int?hello_init(void) {printk("hello_init?\n");return?platform_driver_register(&hello_driver); } static?void?hello_exit(void) {printk("hello_exit?\n");platform_driver_unregister(&hello_driver);return; } MODULE_LICENSE("GPL"); module_init(hello_init); module_exit(hello_exit);Makefile
ifneq?($(KERNELRELEASE),) obj-m:=device.o?driver.o else KDIR?:=/lib/modules/$(shell?uname?-r)/build #KDIR?:=/home/peng/linux-3.14 PWD??:=$(shell?pwd) all:make?-C?$(KDIR)?M=$(PWD)?modules clean:rm?-f?*.ko?*.o?*.mod.o?*.symvers?*.cmd??*.mod.c?*.order endif5. 編譯運(yùn)行
第一步:編譯
第二步:加載模塊驅(qū)動(dòng)第三步:查看生成的文件節(jié)點(diǎn):
第四步:通過下面命令向節(jié)點(diǎn)輸入一個(gè)數(shù)字(要管理員權(quán)限):
echo?1?>?peng由結(jié)果可知,我們通過向文件peng寫入一個(gè)字符,實(shí)現(xiàn)了調(diào)用函數(shù)peng_test_store(),并且字符1傳遞給了參數(shù)buf,字符個(gè)數(shù)傳遞給了count。
其中目錄duang是由結(jié)構(gòu)體變量hello_driver 給出:
static?struct?platform_driver?hello_driver?= {.driver.name?=?"duang", };6. 一次注冊(cè)多個(gè)節(jié)點(diǎn)
需要借助結(jié)構(gòu)體
struct?attribute以及函數(shù)
/***?sysfs_create_group?-?given?a?directory?kobject,?create?an?attribute?group*?@kobj:?The?kobject?to?create?the?group?on*?@grp:?The?attribute?group?to?create**?This?function?creates?a?group?for?the?first?time.??It?will?explicitly*?warn?and?error?if?any?of?the?attribute?files?being?created?already?exist.**?Returns?0?on?success?or?error.*/ int?sysfs_create_group(struct?kobject?*kobj,const?struct?attribute_group?*grp)此處就不驗(yàn)證了,直接從內(nèi)核找個(gè)例子給大家學(xué)習(xí)下吧
drivers\input\touchscreen\ads7846.cstatic?ssize_t?ads7846_pen_down_show(struct?device?*dev,struct?device_attribute?*attr,?char?*buf) {struct?ads7846?*ts?=?dev_get_drvdata(dev);return?sprintf(buf,?"%u\n",?ts->pendown); }static?DEVICE_ATTR(pen_down,?S_IRUGO,?ads7846_pen_down_show,?NULL);static?ssize_t?ads7846_disable_show(struct?device?*dev,struct?device_attribute?*attr,?char?*buf) {struct?ads7846?*ts?=?dev_get_drvdata(dev);return?sprintf(buf,?"%u\n",?ts->disabled); }static?ssize_t?ads7846_disable_store(struct?device?*dev,struct?device_attribute?*attr,const?char?*buf,?size_t?count) {struct?ads7846?*ts?=?dev_get_drvdata(dev);unsigned?int?i;int?err;err?=?kstrtouint(buf,?10,?&i);if?(err)return?err;if?(i)ads7846_disable(ts);elseads7846_enable(ts);return?count; } static?DEVICE_ATTR(disable,?0664,?ads7846_disable_show,?ads7846_disable_store);static?struct?attribute?*ads784x_attributes[]?=?{&dev_attr_pen_down.attr,&dev_attr_disable.attr,NULL, };static?struct?attribute_group?ads784x_attr_group?=?{.attrs?=?ads784x_attributes, };err?=?sysfs_create_group(&mydevice->dev.kobj,?&ads784x_attr_group);7. 補(bǔ)充
當(dāng)然_ATTR不是獨(dú)生子女,他還有一系列的姊妹__ATTR_RO宏只有讀方法,__ATTR_NULL等等
如對(duì)設(shè)備的使用????????DEVICE_ATTR??? 對(duì)驅(qū)動(dòng)使用???????????????DRIVER_ATTR 對(duì)總線使用???????????????BUS_ATTR? 對(duì)類別?(class)?使用??CLASS_ATTR好了,大家后面在調(diào)試驅(qū)動(dòng)的時(shí)候別忘了有這些宏可以使用。
end
總結(jié)
以上是生活随笔為你收集整理的Linux驱动小技巧 | 利用DRIVER_ATTR实现调用内核函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS手势全屏滑动返回
- 下一篇: 从单片机步入Linux之运行地址与加载地