設備驅動模型 - kset

目錄

一. kset簡介

1.1 定義

1.2  熱插拔事件

1.2.1 消息通知的兩種方式

1.2.2 操作接口

二. kobject與kset的關係

2.1 繼承關係

2.2 層級關係

2.3 具體繼承關係的體現:

三. 內核接口

四. kset實例

4.1 Makefile

4.2 實例1

4.3 實例2


注:
內核版本:3.18.20

一. kset簡介

1.1 定義

    kset是具有相同類型的kobject的集合,在sysfs中體現成一個目錄,在內核中用kset數據結構表示,定義爲:
 

/*1. kset定義*/
/**
 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
 *
 * A kset defines a group of kobjects.  They can be individually
 * different "types" but overall these kobjects all want to be grouped
 * together and operated on in the same manner.  ksets are used to
 * define the attribute callbacks and other common events that happen to
 * a kobject.
 *
 * @list: the list of all kobjects for this kset
 * @list_lock: a lock for iterating over the kobjects
 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
 * @uevent_ops: the set of uevent operations for this kset.  These are
 * called whenever a kobject has something happen to it so that the kset
 * can add new environment variables, or filter out the uevents if so
 * desired.
 */
struct kset {
    struct list_head list;                     /*kobject容器,即連接該kset中所有kobject的鏈表頭*/
    spinlock_t list_lock;                      /*鏈表自旋鎖*/
    struct kobject kobj;                       /*內嵌的kobject,即kset繼承與kobject*/
    const struct kset_uevent_ops *uevent_ops;  /*處理熱插拔事件的操作集合*/
};


/*2.uevent操作集合:消息回調函數:*/
struct kset_uevent_ops {
    int (* const filter)(struct kset *kset, struct kobject *kobj);
    const char *(* const name)(struct kset *kset, struct kobject *kobj);
    int (* const uevent)(struct kset *kset, struct kobject *kobj,
              struct kobj_uevent_env *env);
};
/*注:
問題一:這三個函數什麼時候調用?
答:當該kset所管理的kobject和kset狀態發生變化時(如被加入,移動),這三個函數將被調用。
(例:kobject_uevent調用)

問題二:這三個函數的功能是什麼?
答:filter:決定是否將事件傳遞到用戶空間。如果 filter返回 0,將不傳遞事件。(例: uevent_filter)
    name:用於將字符串傳遞給用戶空間的熱插拔處理程序。
    uevent:將用戶空間需要的參數添加到環境變量中。(例:dev_uevent)
*/

/*3.內核對象的動作類型*/
/*
 * The actions here must match the index to the string array
 * in lib/kobject_uevent.c
 *
 * Do not add new actions here without checking with the driver-core
 * maintainers. Action strings are not meant to express subsystem
 * or device specific properties. In most cases you want to send a
 * kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event
 * specific variables added to the event environment.
 */
enum kobject_action {
    KOBJ_ADD,
    KOBJ_REMOVE,
    KOBJ_CHANGE,
    KOBJ_MOVE,
    KOBJ_ONLINE,
    KOBJ_OFFLINE,
    KOBJ_MAX
};

1.2  熱插拔事件

      在Linux系統中,當系統配置發生變化時,如:添加kset到系統;移動kobject, 一個通知會從內核空間發送到用戶空間,這就是熱插拔事件。熱插拔事件會導致用戶空間中相應的處理程序(如udev,mdev)被調用, 這些處理程序會通過加載驅動程序, 創建設備節點等來響應熱插拔事件。

1.2.1 消息通知的兩種方式

  1. udev --netlink (udev 通常用於pc機的消息通知)
  2. mdev --call usermodehelper (mdev 通常用於嵌入式設備的消息通知)

1.2.2 操作接口

int kobject_uevent(struct kobject *kobj, enum kobject_action action);

二. kobject與kset的關係

2.1 繼承關係

  1. 結構體看:kset包含有一個kobject成員對象。
  2. 接口函數看:初始化,註冊,註銷,都會先處理kobject相關工作。
  3. kset繼承了kobject的name、kref、sd等。

2.2 層級關係

  1.  kset可以作爲kobject的父目錄。
  2. kset作爲具有共同特徵的kobject的容器。

2.3 具體繼承關係的體現:

static inline struct kset *to_kset(struct kobject *kobj)
{
    return kobj ? container_of(kobj, struct kset, kobj) : NULL;
}

三. 內核接口

/*1. 初始化kset*/
extern void kset_init(struct kset *kset);

/*2. 向內核註冊一個kset*/
extern int __must_check kset_register(struct kset *kset);

/*3. 註銷一個kset*/
extern void kset_unregister(struct kset *kset);

/*4. 創建並添加一個kset*/
extern struct kset * __must_check kset_create_and_add(const char *name,
                        const struct kset_uevent_ops *u,
                        struct kobject *parent_kobj);

/*5. 根據kset中的kobject結構獲取kset結構地址*/
static inline struct kset *to_kset(struct kobject *kobj)

/*6. kset引用計數加一*/
static inline struct kset *kset_get(struct kset *k)

/*7. kset引用計數減一*/
static inline void kset_put(struct kset *k)

/*8. 查找kset容器中所有的kobject*/
extern struct kobject *kset_find_obj(struct kset *, const char *);                    

 


四. kset實例

4.1 Makefile

ifneq ($(KERNELRELEASE),)
    obj-m := kset_test.o
else
    PWD  := $(shell pwd)
KDIR := /lib/modules/$(shell uname -r)/build

all:
    $(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
    rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.order Module.symvers
endif

4.2 實例1

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/kobject.h>


/*from class subsystem*/

static struct kset *devices_set = NULL;
static struct kobject *kobj_dev = NULL;


static void kobj_dev_release(struct kobject *kobj)
{
    printk("kobject: '%s' (%p): %s\n", kobject_name(kobj), kobj, __func__);
    kfree(kobj);
}
static struct kobj_type devices_ktype = {
    .release = kobj_dev_release,
};



static int devices_uevent_filter(struct kset *kset, struct kobject *kobj)
{
    printk("UEVENT: filter kobj %s. \n", kobj->name);
    return 1;
}
static const char *devices_uevent_name(struct kset *kset, struct kobject *kobj)
{
    static char buf[20];
    printk("UEVENT: name kobj %s. \n", kobj->name);
    sprintf(buf, "%s", "kset_test");
    return buf;
}
static int devices_uevent(struct kset *kset, struct kobject *kobj,
    struct kobj_uevent_env *env)
{
    int i = 0;
    printk("UEVENT: uevent kobj %s. \n", kobj->name);
    
    while (i < env->envp_idx) {
        printk("%s. \n", env->envp[i]);
        i++;
    }
    return 0;
}
static const struct kset_uevent_ops devices_uevent_ops = {
    .filter = devices_uevent_filter,
    .name = devices_uevent_name,
    .uevent = devices_uevent,
};


static int kset_test_init(void)
{
    int retval = 0;
    
    devices_set = kset_create_and_add("devices_set", &devices_uevent_ops, NULL);
    if (!devices_set)
        return -ENOMEM;
    
    kobj_dev = kzalloc(sizeof(struct kobject), GFP_KERNEL);
    if (!kobj_dev) {
        retval = -ENOMEM;
        goto alloc_fail;
    }
    
    /*初始化kobject相關屬性*/
    kobj_dev->kset = devices_set;
    kobject_init(kobj_dev, &devices_ktype);
    
    retval = kobject_add(kobj_dev, NULL, "%s_dev", "1");
    if (retval)
        goto out;
    
    /*這裏會調用kset的 filter、name、uevent回調函數*/
    kobject_uevent(kobj_dev, KOBJ_ADD);
    
    printk("kset_test init. \n");
    return 0;
    
out:
    kobject_put(kobj_dev);
    
alloc_fail:
    kset_unregister(devices_set);
    return retval;
}

static void kset_test_exit(void)
{
    kobject_del(kobj_dev);
    kobject_put(kobj_dev);
    kset_unregister(devices_set);
    printk("kset test exit. \n");
}


module_init(kset_test_init);
module_exit(kset_test_exit);
MODULE_AUTHOR("vector");
MODULE_LICENSE("Dual BSD/GPL");

4.3 實例2

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/kobject.h>

/*from platform subsys, bus type
ql-ol-kernel\drivers\base*/

static struct kset *bus_set = NULL;
static struct kset *subsys = NULL;
static struct kset *devices = NULL;
static struct kset *drivers = NULL;


static void kset_release(struct kobject *kobj)
{
    printk("subsys kobj: '%s' (%p): %s\n", kobject_name(kobj), kobj, __func__);
    kfree(kobj);
}
/*用於分發到各個具體的attr屬性文件中的show,store函數中*/
static ssize_t kset_ops_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    struct kobj_attribute *my_attr;
    ssize_t ret = -EIO;
    my_attr = container_of(attr, struct kobj_attribute, attr);
    if (my_attr->show)
        ret = my_attr->show(kobj, my_attr, buf);

    return ret;
}
static ssize_t kset_ops_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
    struct kobj_attribute *my_attr;
    ssize_t ret = -EIO;
    my_attr = container_of(attr, struct kobj_attribute, attr);
    if (my_attr->store)
        ret = my_attr->store(kobj, my_attr, buf, count);

    return ret;
}
static const struct sysfs_ops kset_sysfs_ops = {
    .show = kset_ops_show,
    .store = kset_ops_store,
};
static struct kobj_type kset_ktype = {
    .sysfs_ops = &kset_sysfs_ops,
    .release = kset_release,
};



static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{
    printk("UEVENT: filter kobj %s. \n", kobj->name);
    return 1;
}
static const char *bus_uevent_name(struct kset *kset, struct kobject *kobj)
{
    static char buf[20];
    printk("UEVENT: name kobj %s. \n", kobj->name);
    sprintf(buf, "%s", "kset_test");
    return buf;
}
static int bus_uevent(struct kset *kset, struct kobject *kobj,
    struct kobj_uevent_env *env)
{
    int i = 0;
    printk("UEVENT: uevent kobj %s. \n", kobj->name);
    
    while (i < env->envp_idx) {
        printk("%s. \n", env->envp[i]);
        i++;
    }
    return 0;
}
static const struct kset_uevent_ops bus_uevent_ops = {
    .filter = bus_uevent_filter,
    .name = bus_uevent_name,
    .uevent = bus_uevent,
};


static int kset_create_file(struct kset *kset, struct kobj_attribute *kobj_attr)
{
    int error;
    error = sysfs_create_file(&kset->kobj, &kobj_attr->attr);
    printk("uevent file: %d", error);
    return 0;
}
static int kset_remove_file(struct kset *kset, struct kobj_attribute *kobj_attr)
{
    sysfs_remove_file(&kset->kobj, &kobj_attr->attr);
    return 0;
}


static ssize_t uevent_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    printk("%s :in\n", __FUNCTION__);
    strcpy(buf, "uevent show");
    return strlen("uevent show");
}
static ssize_t uevent_store(struct kobject *kobj, struct kobj_attribute *attr,
    const char *buf, size_t count)
{
    printk("%s :in, %s!\n", __FUNCTION__, buf);
    return count;
}
static struct kobj_attribute kset_attr_uevent =
    __ATTR(uevent, 0644, uevent_show, uevent_store);
    

    
    
static int kset_test_init(void)
{
    int retval = 0;
    
    
    bus_set = kset_create_and_add("bus_set", &bus_uevent_ops, NULL);
    if (!bus_set)
        return -ENOMEM;
    
    
    /*該kset沒有直接向用戶發送uevent事件的能力,但是可發給它所屬的kset,
    然後由上級kset發event給用戶。*/
    subsys = kzalloc(sizeof(struct kset), GFP_KERNEL);
    if (!subsys) {
        retval = -ENOMEM;
        goto alloc_fail;
    }
    retval = kobject_set_name(&subsys->kobj, "%s", "platform");
    if (retval)
        goto out;
    subsys->kobj.ktype = &kset_ktype;
    subsys->kobj.kset = bus_set;
    /*uevent--1*/
    retval = kset_register(subsys);
    if (retval)
        goto out;
    
    retval = kset_create_file(subsys, &kset_attr_uevent);
    if (retval)
        goto files_fail;
    
    
    /*uevent--2*/
    devices = kset_create_and_add("devices", NULL, &subsys->kobj);
    if (!devices) {
        retval = -ENOMEM;
        goto devices_fail;
    }
    
    /*uevent--3*/
    drivers = kset_create_and_add("drivers", NULL, &subsys->kobj);
    if (!drivers) {
        retval = -ENOMEM;
        goto drivers_fail;
    }
    
    
    /*uevent--4*/
    /*這裏會調用kset的 filter、name、uevent回調函數*/
    kobject_uevent(&drivers->kobj, KOBJ_ADD);
    
    printk("kset_test init. \n");
    return 0;

drivers_fail:
    kset_unregister(devices);
    
devices_fail:
    kset_remove_file(subsys, &kset_attr_uevent);

files_fail:
    kset_unregister(subsys);
    
out:
    kfree(subsys);
    
alloc_fail:
    kset_unregister(bus_set);
    return retval;
}

static void kset_test_exit(void)
{
    kset_unregister(drivers);
    kset_unregister(devices);
    kset_remove_file(subsys, &kset_attr_uevent);
    kset_unregister(subsys);
    kset_unregister(bus_set);
    printk("kset test exit. \n");
}


module_init(kset_test_init);
module_exit(kset_test_exit);
MODULE_AUTHOR("vector");
MODULE_LICENSE("Dual BSD/GPL");
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>

struct sockaddr_nl {
    unsigned short nl_family; /*AF_NETLINK*/
    unsigned short nl_pad; /*zero*/
    unsigned int nl_pid; /*port ID*/
    unsigned int nl_groups; /*multicast groups mask*/
};

static int init_hotplug_sock(void)
{
    struct sockaddr_nl snl;
    const int buffer_size = 16 * 1024 *1024;
    int retval;
    
    memset(&snl, 0x00, sizeof(struct sockaddr_nl));
    snl.nl_family = AF_NETLINK;
    snl.nl_pid = getpid();
    snl.nl_groups = 1;
    
    int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, 15);
    if (-1 == hotplug_sock) {
        printf("error getting socket: %s\n", strerror(errno));
        return -1;
    }
    
    /*set receive buffer size*/
    setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffer_size, sizeof(buffer_size));
    
    
    retval = bind(hotplug_sock, (struct sockaddr_nl *)&snl, sizeof(struct sockaddr_nl));
    if (retval < 0) {
        printf("bind failed: %s\n", strerror(errno));
        close(hotplug_sock);
        hotplug_sock = -1;
        return -1;
    }
    
    return hotplug_sock;
}


#define UEVENT_BUFFER_SIZE 2048

int main(int argc, char *argv[])
{
    int hotplug_sock = init_hotplug_sock();
    
    while (1) {
        char buf[UEVENT_BUFFER_SIZE * 2] = {0};
        recv(hotplug_sock, &buf, sizeof(buf), 0);
        printf("vec:%s\n", buf);        
    }
    
    return 0;
    
}

 

發佈了97 篇原創文章 · 獲贊 52 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章