kobject应用实例--在/sys下创建设备的属性节点

本文讲解如何利用内核提供的接口,在/sys下创建设备的属性节点,实现属性的读写接口。

1、主要数据结构

一、kobject --> 目录;kobj_type --> 属性文件
使用到的内核数据结构如下:

struct kobject {
    const char      *name;
    /*当前kobj的父节点,在文件系统中的表现就是父目录*/
    struct kobject      *parent;
    /*kobj属于的kset*/
    struct kset     *kset;
    /*kobj的类型描述,最主要的是其中的属性描述,包含其读写方式*/
    struct kobj_type    *ktype;
    /*当前kobj的引用,只有当引用为0时才能被删除*/
    struct kref     kref;
    ...
};
struct kobj_type {
	void (*release)(struct kobject *kobj); 释放kobject和其占用资源的函数
	const struct sysfs_ops *sysfs_ops;      操作下一个属性数组的方法
	struct attribute **default_attrs;        属性数组
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
};

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
};
 
struct attribute_group {
    const char        *name;
    umode_t            (*is_visible)(struct kobject *,
                          struct attribute *, int);
    struct attribute    **attrs;
};

struct kobj_attribute {
 struct attribute attr;
 ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
   char *buf);
 ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
    const char *buf, size_t count);
};
#define __ATTR(_name,_mode,_show,_store) { \
    .attr = {.name = __stringify(_name), .mode = _mode },   \
    .show   = _show,                    \
    .store  = _store,                   \
}

二、kobj_type:是用户空间的法宝
为kobject 对象构建多个属性文件(这些属性文件都是共用kobj_attr_show和kobj_attr_store读写接口)。这两个函数接口内部最终会根据函数参数struct attribute调用到我们自己实现的函数。
为每个属性文件设置具体操作接口(在上面两个统一的操作接口里面,会进一步去调用每个attribute属性文件具体的操作接口)。具体操作接口需要我们自己去实现。
虚拟文件系统vfs的inode对象与sysfs的kernfs_node对象的绑定过程(虚拟文件系统不能识别 kernfs_node)。

2、源码解析

编写一个内核模块,当模块加载后可以在 /sys 下创建一个叫 ssj_kobject 的目录,该目录下拥有两个属性 name、led,应用层可以通过读写属性文件修改名字,和控制 led 状态。
sys_demo.c 源码如下:

#include <linux/init.h>
#include <linux/module.h>           
#include <linux/kernel.h>
#include <linux/kthread.h>      
#include <linux/delay.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <linux/uaccess.h>

static char *name = NULL;
static int led_status = 0;
static struct kobject *my_obj;

#define LED_PIN  14


/**
 * @brief show 和 store函数的返回值为接收到的数据长度
 * 
 */
static ssize_t name_show(struct kobject* kodjs,struct kobj_attribute *attr,char *buf)
{
    if(name != NULL){
        return sprintf(buf,"my name is %s",name);
    } else {
        return sprintf(buf,"I don't have a name yet!\n");
    }   
}

static ssize_t name_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count)
{
    name = kmalloc(count,GFP_KERNEL);
    strncpy(name,buf,count);
    printk("have set name is %s",name);
    return count;
}

/**
 * @brief show 和 store函数的返回值为接收到的数据长度 
 * 
 */
static ssize_t led_show(struct kobject* kodjs,struct kobj_attribute *attr,char *buf)
{
    if(led_status==0){
      return  sprintf(buf,"led status is off\n");
    } else if(led_status==1){
      return  sprintf(buf,"led status is on\n");
    }   
}

static ssize_t led_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count)
{
     if(0 == memcmp(buf,"on",2))
    {
        gpio_set_value(LED_PIN,1);
        led_status = 1;
	printk("open led!\n");
    }
    else if(0 == memcmp(buf,"off",3))
    {
        gpio_set_value(LED_PIN,0);
        led_status = 0;
	printk("close led!");
    }
    return count;
}

static struct kobj_attribute name_attr = __ATTR(name,0660,name_show,name_store);
static struct kobj_attribute led_attr = __ATTR(led,0660,led_show,led_store);


static struct attribute *led_attrs[] = {
    &name_attr.attr,
    &led_attr.attr,
    NULL,
};

static struct attribute_group my_attr_group = {
    .name = "ssj_kobj",
    .attrs = led_attrs,
};

int create_kobject(void)
{
    my_obj = kobject_create_and_add("ssj_kobj",kernel_kobj->parent);
    return 0;
};

static void gpio_config(void)
{
    if(!gpio_is_valid(LED_PIN)){
        printk(KERN_ALERT "Error wrong gpio number\n");
        return ;
    }
    gpio_request(LED_PIN,"led_ctr");
    gpio_direction_output(LED_PIN,1);
    gpio_set_value(LED_PIN,1);
    led_status = 1;
}

static void gpio_deconfig(void)
{
    gpio_free(LED_PIN);
}

static int __init sysfs_ctrl_init(void){
    printk(KERN_INFO "ssj Kobject test!\n");
    gpio_config();
    create_kobject();
    sysfs_create_group(my_obj, &my_attr_group);
    return 0;
}

static void __exit sysfs_ctrl_exit(void){

    gpio_deconfig();
    kobject_put(my_obj);
    printk(KERN_INFO "Have removed mode!\n");
}

module_init(sysfs_ctrl_init);
module_exit(sysfs_ctrl_exit);

MODULE_LICENSE("GPL");              
MODULE_AUTHOR("SiShaojian");      
MODULE_DESCRIPTION("Kobject demo!");  
MODULE_VERSION("0.1");

关键代码解析:

创建 kobj_attribute 并初始化它们的名字和指定 show store 函数
static struct kobj_attribute name_attr = __ATTR(name,0660,name_show,name_store);
static struct kobj_attribute led_attr = __ATTR(led,0660,led_show,led_store);
创建及初始化 attribute_group 
static struct attribute *led_attrs[] = {
    &name_attr.attr,
    &led_attr.attr,
    NULL,
};

static struct attribute_group my_attr_group = {
    .name = "ssj_kobj",
    .attrs = led_attrs,
};
创建 kobject 起名为 ssj_kobj,在/sys 目录下产生 ssj_kobj目录
int create_kobject(void)
{
    my_obj = kobject_create_and_add("ssj_kobj",kernel_kobj->parent);
    return 0;
};
给 my_obj 添加属性文件,会在ssj_obj目录下出现 my_attr_group 中包含的属性
static int __init sysfs_ctrl_init(void){
    ...
    sysfs_create_group(my_obj, &my_attr_group);
    ...
}

3、将源码编译成模块

1、在内核源码的 /drivers 下创建 mode 目录。
2、修改/drivers 下的makefile,添加

obj-y				+= mode/

3、mode目录下有 sys_demo.c 和 Makefile 两个文件,Makefile文件的内容如下:

obj-m += sys_demo.o
# KERNEL_PATH 是内核源码的根目录
KERNEL_PATH := /home/ssj/zmj1/linux-kernel/
PWD := $(shell pwd)

all:
	make -C $(KERNEL_PATH) M=$(PWD) modules
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

设置好你的内核编译环境之后,在此目录下执行 make,将sys_mode.c 编译成 sys_demo.ko
如果你的内核是在外部构建的话,执行如下指令

make -C ./ O=../output

4、测试模块的功能

将模块从ubuntu发送到开发板:

scp sys_demo.ko root@191.168.0.66:/home/root

在开发板上加载模块
在这里插入图片描述
测试 led 属性
在这里插入图片描述
测试 name 属性
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值