最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

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

工作和技术 crifan 2663浏览 0评论

如何在驱动中添加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
static int easypoint_probe(struct i2c_client *client,
      const struct i2c_device_id *id)
{

err = kobject_init_and_add?&epdata->kobj, &ktype_easypoint, &idev->dev.kobj,
       "easypoint");

}

说明:
(1)easypoint_probe是我的某个驱动的probe函数。
(2)kobject_init_and_add的原型为:
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
    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对应的变量以及其他相关信息:
static ssize_t show(struct kobject *kobj, struct attribute *attr ,char *buf)
{
struct easypoint_data *epdata = to_epdata(kobj);
struct easypoint_attr *ep_attr = to_attr(attr);
ssize_t ret = -EINVAL;

if (ep_attr->show)
   ret = ep_attr->show(epdata, buf);
else
   ret = -EIO;

return ret;
}

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

if (ep_attr->store)
   ret = ep_attr->store(epdata, buf, count);
else
   ret = -EIO;

return ret;
}

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

static void easypoint_sysfs_release(struct kobject *kobj)
{

}

static struct sysfs_ops ep_sysfs_ops = {
.show = show,
.store = store,
};

static struct kobj_type ktype_easypoint = {
.sysfs_ops = &ep_sysfs_ops,
.default_attrs = ep_default_attrs,
.release = easypoint_sysfs_release,
};
说明:
(1)
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};

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

所以,上面定义了一个ep_sysfs_ops,然后挂载了一个show和store,其函数意思很明显,
就是相当于对此文件操作的read和write时候,分别调用show和store函数。
(3)其中的show和store函数中,也没什么特殊的,只是调用对应的attr的show和store函数。
(4)其中show和store函数中的to_epdata和to_attr分别是:
#define to_epdata(k) container_of(k,struct easypoint_data, kobj)
#define to_attr(a) container_of(a,struct easypoint_attr, attr)

即利用常用的container_of,来实现通过结构体成员kobj来得到我们自己的数据结构指针。

3.定义对应的单个或多个sysfs file对应的attr
下面说说关于上面的那个default_attrs对应的ep_default_attrs:

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

其实很简单,就是一个attr的指针数组,一个个挂到default_attr中。
如果要实现多个sysfs的file,即多个attr,即类似于
&easypoint_calibrate.attr,
一样,去加上别的即可,型如:
&easypoint_calibrate.attr,
&XXXXXX.attr,

其中easypoint_calibrate相关内容,见如下定义的:

struct easypoint_attr {
struct attribute attr;
ssize_t (*show)(struct easypoint_data *, char *);
ssize_t (*store)(struct easypoint_data *, const char *, size_t count);
};

#define define_one_rw(_name)
static struct easypoint_attr _name =
__ATTR(_name, 0644, show_##_name, store_##_name)

define_one_rw(easypoint_calibrate);

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

#define __ATTR(_name,_mode,_show,_store) {
.attr = {.name = __stringify(_name), .mode = _mode },
.show = _show,     
.store = _store,     
}
说明:
(1)上面的
#define define_one_rw(_name)
static struct easypoint_attr _name =
__ATTR(_name, 0644, show_##_name, store_##_name)
define_one_rw(easypoint_calibrate);

可以用
static struct easypoint_attr easypoint_calibrate =
__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函数,实现你所需要的功能

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

ret = sprintf(buf, "Calibrated:t%3sn", epdata->calibrated ? "Yes" : "No");
ret += sprintf(buf + ret, "Calibrating:t%3sn", epdata->calibrating ? "Yes" : "No");
ret += sprintf(buf + ret, "xn:%2d,xp:%2d,yn:%2d,yp:%2dn",
   epdata->xn_max_allow,
   epdata->xp_max_allow,
   epdata->yn_max_allow,
   epdata->yp_max_allow);

return ret;
}
说明:
show函数中的,一般都是打印一些信息,注意第二行的
ret += sprintf(buf + ret, "Calibrating:t%3sn", epdata->calibrating ? "Yes" : "No");
   中的buf + ret表示接着上面一行接着输出。
其中sprintf返回值为已经格式化打印输出的字符数。

/**
* store_easypoint_calibrate – process the calibrate command and do action
*/
static ssize_t store_easypoint_calibrate(struct easypoint_data *epdata,
      const char *buf, size_t count)
{
unsigned int ret = -EINVAL;
char str_cmd[10];
int x_sum, y_sum;
char x_offset, y_offset;
int retry_time, i;

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

if (0 == strnicmp(str_cmd, "start", 10)) {
   /* start calibrate */

}
else if (0 == strnicmp(str_cmd, "stop", 10)) {
   /* stop calibrate */

}
else
   return -EINVAL;

return count;

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

而对于输入内容,让store函数执行,要用到echo,而不是cat,比如:

# cd /sys/class/input/input2/easypoint/
# ls
easypoint_calibrate
# echo start > easypoint_calibrate
….
# echo stop > easypoint_calibrate

转载请注明:在路上 » 如何在驱动中添加kobject以及在其下添加单个或多个文件(sysfs file)

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
79 queries in 0.149 seconds, using 22.11MB memory