Chinaunix首页 | 论坛 | 博客
  • 博客访问: 107078
  • 博文数量: 26
  • 博客积分: 1898
  • 博客等级: 上尉
  • 技术积分: 450
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-22 23:41
文章分类

全部博文(26)

文章存档

2011年(15)

2010年(4)

2009年(7)

分类: LINUX

2010-05-13 13:43:00

如何在驱动中添加kobject以及在其下添加单个或多个文件(sysfs file)

How to add a kojbect and sysfs file in Linux driver

驱动程序常常需要和用户有一定的交互,
或者说用户常常要去通过某些接口去查看当前设备的某些信息,或者对驱动做一些配置,
分别对应着程序里面的read和write操作。
Linux内核提供了很多方式,除了常见的proc文件系统之外,还有个sysfs。
其中每个sysfs中的文件夹都对应着内核中的一个kobject。
每个sysfs文件下面,可能会有单个或多个文件,供用户read或write,
其一般通过cat sysfs_file_name和echo XXX > sysfs_file_name实现。

下面将要介绍的就是,向一个驱动中添加一个kobject和在其下添加单个或多个sysfs file的大概套路。
由于水平有限,难免有误,看到问题的还请指教:green-waste(At)163.com

1.在Probe函数中添加函数去添加kobject
  1. static int easypoint_probe(struct i2c_client *client,
  2.       const struct i2c_device_id *id)
  3. {
  4. ...
  5. err = kobject_init_and_add?&epdata->kobj, &ktype_easypoint, &idev->dev.kobj,
  6.        "easypoint");
  7. ...
  8. }
复制代码


说明:
(1)easypoint_probe是我的某个驱动的probe函数。
(2)kobject_init_and_add的原型为:
  1. int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
  2.     struct kobject *parent, const char *fmt, ...);
复制代码
此处只需要调用一次kobject_init_and_add,
即可实现添加kobj以及创建对应的sysfs中的文件夹
及其由传入的ktype中的default_attrs决定的那些属性文件(sysfs file)。
(3)传入的&idev->dev.kobj是表示parent object,如果此处没有,那么也可以直接写成NULL,我这里是有parent的obj的,
即前面已经有的一个input device的obj。

2.实现对应的ktype和ktype中的操作接口sysfs_ops

下面接着说关于参数中的ktype对应的变量以及其他相关信息:
  1. static ssize_t show(struct kobject *kobj, struct attribute *attr ,char *buf)
  2. {
  3. struct easypoint_data *epdata = to_epdata(kobj);
  4. struct easypoint_attr *ep_attr = to_attr(attr);
  5. ssize_t ret = -EINVAL;

  6. if (ep_attr->show)
  7.    ret = ep_attr->show(epdata, buf);
  8. else
  9.    ret = -EIO;

  10. return ret;
  11. }

  12. static ssize_t store(struct kobject *kobj, struct attribute *attr,
  13.        const char *buf, size_t count)
  14. {
  15. struct easypoint_data *epdata = to_epdata(kobj);
  16. struct easypoint_attr *ep_attr = to_attr(attr);
  17. ssize_t ret = -EINVAL;

  18. if (ep_attr->store)
  19.    ret = ep_attr->store(epdata, buf, count);
  20. else
  21.    ret = -EIO;

  22. return ret;
  23. }

  24. static struct attribute *ep_default_attrs[] = {
  25. &easypoint_calibrate.attr,
  26. NULL
  27. };

  28. static void easypoint_sysfs_release(struct kobject *kobj)
  29. {

  30. }

  31. static struct sysfs_ops ep_sysfs_ops = {
  32. .show = show,
  33. .store = store,
  34. };

  35. static struct kobj_type ktype_easypoint = {
  36. .sysfs_ops = &ep_sysfs_ops,
  37. .default_attrs = ep_default_attrs,
  38. .release = easypoint_sysfs_release,
  39. };
复制代码
说明:
(1)
  1. struct kobj_type {
  2. void (*release)(struct kobject *kobj);
  3. struct sysfs_ops *sysfs_ops;
  4. struct attribute **default_attrs;
  5. };
复制代码

中的release函数,此处好像不需要做什么事情,所以留空。
(2)sysfs_ops的定义:
  1. struct sysfs_ops {
  2. ssize_t (*show)(struct kobject *, struct attribute *,char *);
  3. ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
  4. };
复制代码

所以,上面定义了一个ep_sysfs_ops,然后挂载了一个show和store,其函数意思很明显,
就是相当于对此文件操作的read和write时候,分别调用show和store函数。
(3)其中的show和store函数中,也没什么特殊的,只是调用对应的attr的show和store函数。
(4)其中show和store函数中的to_epdata和to_attr分别是:
  1. #define to_epdata(k) container_of(k,struct easypoint_data, kobj)
  2. #define to_attr(a) container_of(a,struct easypoint_attr, attr)
复制代码
即利用常用的container_of,来实现通过结构体成员kobj来得到我们自己的数据结构指针。

3.定义对应的单个或多个sysfs file对应的attr
下面说说关于上面的那个default_attrs对应的ep_default_attrs:
  1. static struct attribute *ep_default_attrs[] = {
  2. &easypoint_calibrate.attr,
  3. NULL
  4. };
复制代码

其实很简单,就是一个attr的指针数组,一个个挂到default_attr中。
如果要实现多个sysfs的file,即多个attr,即类似于
&easypoint_calibrate.attr,
一样,去加上别的即可,型如:
&easypoint_calibrate.attr,
&XXXXXX.attr,
其中easypoint_calibrate相关内容,见如下定义的:
  1. struct easypoint_attr {
  2. struct attribute attr;
  3. ssize_t (*show)(struct easypoint_data *, char *);
  4. ssize_t (*store)(struct easypoint_data *, const char *, size_t count);
  5. };
  6. #define define_one_rw(_name) \
  7. static struct easypoint_attr _name = \
  8. __ATTR(_name, 0644, show_##_name, store_##_name)
  9. define_one_rw(easypoint_calibrate);
复制代码

去用define_one_rw定义了一个读写的attr,其中__ATTR是sysfs.h中提供的一个宏方便我们初始化attr的变量:
  1. /**
  2. * Use these macros to make defining attributes easier. See include/linux/device.h
  3. * for examples..
  4. */

  5. #define __ATTR(_name,_mode,_show,_store) { \
  6. .attr = {.name = __stringify(_name), .mode = _mode }, \
  7. .show = _show,      \
  8. .store = _store,      \
  9. }
复制代码

说明:
(1)上面的
  1. #define define_one_rw(_name) \
  2. static struct easypoint_attr _name = \
  3. __ATTR(_name, 0644, show_##_name, store_##_name)
  4. define_one_rw(easypoint_calibrate);
复制代码
可以用
  1. static struct easypoint_attr easypoint_calibrate = \
  2. __ATTR(_name, 0644, show_easypoint_calibrate, store_easypoint_calibrate)
复制代码

来代替,其中##可以用于字符串连接。
(2)__ATTR中__stringify,用于将输入内容变成字符串,否则单独的宏定义,比如你传入的easypoint_calibrate,会无法被识别的。
据我的理解,如果想要定义一个attr,即一个sysfs中一个file,其套路一般是:
先定义一个自己的attr相关的结构体变量,比如上面的struct easypoint_attr,
其中的show和store中的第一个参数(此处的struct easypoint_data *)都是自己的那个数据结构体指针,后面的参数都是一样的。
然后再去定义一个这样的变量,此处的是static struct easypoint_attr _name,这样,
就定义了对应的一个attr,然后再去实现上面的show和store函数,此处即为show_easypoint_calibrate和store_easypoint_calibrate。

4.实现对应的那些attr的show和store函数,实现你所需要的功能

下面针对上面说明,给出我此处的实现例子:
  1. /**
  2. * show_easypoint_calibrate - show calibrate info
  3. */
  4. static ssize_t show_easypoint_calibrate(struct easypoint_data *epdata,
  5.      char *buf)
  6. {
  7. int ret = 0;

  8. ret = sprintf(buf, "Calibrated:\t%3s\n", epdata->calibrated ? "Yes" : "No");
  9. ret += sprintf(buf + ret, "Calibrating:\t%3s\n", epdata->calibrating ? "Yes" : "No");
  10. ret += sprintf(buf + ret, "xn:%2d,xp:%2d,yn:%2d,yp:%2d\n",
  11.    epdata->xn_max_allow,
  12.    epdata->xp_max_allow,
  13.    epdata->yn_max_allow,
  14.    epdata->yp_max_allow);

  15. return ret;
  16. }
复制代码
说明:
show函数中的,一般都是打印一些信息,注意第二行的
  1. ret += sprintf(buf + ret, "Calibrating:\t%3s\n", epdata->calibrating ? "Yes" : "No");
复制代码

   中的buf + ret表示接着上面一行接着输出。
其中sprintf返回值为已经格式化打印输出的字符数。

  1. /**
  2. * store_easypoint_calibrate - process the calibrate command and do action
  3. */
  4. static ssize_t store_easypoint_calibrate(struct easypoint_data *epdata,
  5.       const char *buf, size_t count)
  6. {
  7. unsigned int ret = -EINVAL;
  8. char str_cmd[10];
  9. int x_sum, y_sum;
  10. char x_offset, y_offset;
  11. int retry_time, i;

  12. ret = sscanf (buf, "%10s", str_cmd);
  13. if (ret != 1)
  14.    return -EINVAL;

  15. if (0 == strnicmp(str_cmd, "start", 10)) {
  16.    /* start calibrate */
  17. ...
  18. }
  19. else if (0 == strnicmp(str_cmd, "stop", 10)) {
  20.    /* stop calibrate */
  21. ...
  22. }
  23. else
  24.    return -EINVAL;

  25. return count;

  26. fail:
  27. return ret;
  28. }
复制代码
说明:
(1)store函数一般都是接受外界输入,然后对判断输入的内容去执行对应的操作。
比如此处的这里运行的输入为start和stop,分别去做对应的事情。
(2)另外要说明的是,对于sysfs中的文件,如果要其显示内容,即执行show函数,要用cat命令,比如此处的:
  1. # cat /sys/class/input/input2/easypoint/easypoint_calibrate
  2. Calibrated:      No
  3. Calibrating:     No
  4. xn:26,xp:32,yn:29,yp:30
复制代码

而对于输入内容,让store函数执行,要用到echo,而不是cat,比如:
  1. # cd /sys/class/input/input2/easypoint/
  2. # ls
  3. easypoint_calibrate
  4. # echo start > easypoint_calibrate
  5. ....
  6. # echo stop > easypoint_calibrate
  7. ...
复制代码
阅读(1246) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~