Linux设备模型<五>实例二winter_bus

一大清早那是历经各种艰难。。这电脑。。什么连不上网啊。虚拟机连不上网。。各种无语。开发板都能ping通ubuntu主机了,但是主机就是打不开网页,真的鸭梨好大。挂载个网络文件系统,下载内核镜像 几KB的东西出现各种的TTTTTTTTTTTTTTTTTTTTTTTTTT。看来睡了一个晚上这电脑有点不乖了。不听话了

经过昨晚好几个小时的纠结终于把这个总线、设备、驱动搞清

还是按照总线、设备、驱动的递归来写这个代码,这样思路要清晰一点。下面一步一步解析这个winter_bus.c

1)找到入口、找到出口

static int __init winter_bus_init(void)
{
    int ret;
        /*注册总线*/
    ret = bus_register(&winter_bus_type);
    if (ret)
        return ret;
    /*创建一个包含版本号的属性文件/sys/bus/version*/        
    if (bus_create_file(&winter_bus_type, &bus_attr_version))
        printk(KERN_NOTICE "Fail to create version attribute!\n");
        
    /*注册总线设备*/
    ret = device_register(&winter_bus_device);
    if (ret)
        printk(KERN_NOTICE "Fail to register device:winter_bus!\n");
    return ret;
}

static void __exit winter_bus_exit(void)
{        //注销设备
        device_unregister(&winter_bus_device);
      //注销一条总线
        bus_unregister(&winter_bus_type);
}

module_init(winter_bus_init);
module_exit(winter_bus_exit);

MODULE_AUTHOR("Eilian.Lau");
MODULE_LICENSE("Dual BSD/GPL");


2)从下往上一步一步实现winter_bus_init()中的各个参数及结构

首先看注册总线设备,其实总线也是设备呢。

/*注册总线设备*/
    ret = device_register(&winter_bus_device);
    if (ret)
        printk(KERN_NOTICE "Fail to register device:winter_bus!\n");
    return ret;

/***************************************************************************************/

winter_bus_device结构的实现:

struct device winter_bus_device = {
    .init_name   = "winter_dev",/**这个是在sys/device/    你看呢那些设备的链接归根到底都的链接到这个目录下的**/
    .release  = winter_bus_release
};

static void winter_bus_release(struct device *dev)
{
    printk(KERN_DEBUG "winter bus release\n");
}

昨晚做实验的时候这个总线设备忘记注册了结果添加设备的时候那是使劲的都插不进去设备啊。。。。。

3)总线的注册

 int ret;
        /*注册总线*/
    ret = bus_register(&winter_bus_type);
    if (ret)
        return ret;
    /*创建一个包含版本号的属性文件/sys/bus/version*/        
    if (bus_create_file(&winter_bus_type, &bus_attr_version))
        printk(KERN_NOTICE "Fail to create version attribute!\n");

先来研究研究这个属性文件的创建需要的参数是些什么咚咚

当在编译的时候系统会调用下面这个宏去创建和初始化一个bus_attribute结构 

该宏声明了一个结构,它将bus_attr_作为给定name的前缀来创建总线的真正名称

所以说下面这个宏它的返回值就是bus_attribute bus_attr_version了

/*创建和初始化bus_attribute结构 BUS_ATTR(_name, _mode, _show, _store)*/
/*return struct bus_attribute bus_attr_version*/
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

/**当用户读文件属性的时候调用该函数**/
static ssize_t show_bus_version(struct bus_type *bus,char *buf){
    return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

由于创建属性文件的时候还需要传递一个winter_bus_type结构指针

struct bus_type winter_bus_type = {
    .name = "winter_bus",            /*总线的名称就是何种总线*/
    .match = winter_match,
};
   
重点来看看match 它的实现如下:

static int winter_match(struct device *dev, struct device_driver *driver)
{
    return !strncmp(dev->init_name, driver->name, strlen(driver->name));
}

当用户向该总线注册设备或者驱动的时候会调用该函数。它通过设备和驱动程序的名字去比较,看相应的驱动有没有对应可用的设备

3)总线的注册:先看看总线注册的源代码


int bus_register(struct bus_type *bus)
{
    int retval;
    struct bus_type_private *priv;
   /*************分配 bus_type_private结构***/
    priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    priv->bus = bus;/****这个那就是给私有成员赋值了。。将bus赋值给bus_type_private结构中的成员bus这不刚好,详情请看下面那两个结构挨着看哈。。**/
    bus->p = priv;

    BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
    /**操作subsys为子系统中的kobject设置名字*/

    /**还是看一下这个bus_type_private类里面都包含了那些成员吧,我把这些结构理解成类,用面向对象的思想去看源代码这样我更容易懂一点**/
    retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
    if (retval)
        goto out;
    /**下面这些操作就是在设置bus_type_private 结构体中成员变量的一些值啊 什么的**********/
    priv->subsys.kobj.kset = bus_kset; //设置总线子系统kobject的所属集合,其实对应sys/bus/ 目录
    priv->subsys.kobj.ktype = &bus_ktype; //kobj_type类型变量,和sysfs有关
    priv->drivers_autoprobe = 1;  //这个变量如果置位,则会执行一个函数,在驱动注册会看到它的运用

    retval = kset_register(&priv->subsys);         //程序执行到这里即注册总线子系统将会在/sys/bus/目录下产生一个总线目录
    if (retval)
        goto out;
   /**创建属性文件**/
    retval = bus_create_file(bus, &bus_attr_uevent);
    if (retval)
        goto bus_uevent_fail;

    priv->devices_kset = kset_create_and_add("devices", NULL,
                         &priv->subsys.kobj);
    if (!priv->devices_kset) {
        retval = -ENOMEM;
        goto bus_devices_fail;
    }
    /**操作总线设备了
    priv->drivers_kset = kset_create_and_add("drivers", NULL,
                         &priv->subsys.kobj);
    if (!priv->drivers_kset) {
        retval = -ENOMEM;
        goto bus_drivers_fail;
    }
    //初始化klist_devices
    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

    //初始化klist_drivers
    klist_init(&priv->klist_drivers, NULL, NULL);

    retval = add_probe_files(bus);
    if (retval)
        goto bus_probe_files_fail;

    retval = bus_add_attrs(bus);
    if (retval)
        goto bus_attrs_fail;

    pr_debug("bus: '%s': registered\n", bus->name);
    return 0;

bus_attrs_fail:
    remove_probe_files(bus);
bus_probe_files_fail:
    kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
    kset_unregister(bus->p->devices_kset);
bus_devices_fail:
    bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
    kset_unregister(&bus->p->subsys);
    kfree(bus->p);
out:
    bus->p = NULL;
    return retval;
}

先看一下下面这些东西再返回去看这个注册过程吧要不真有点迷茫的。。。。。

struct bus_type {
    const char        *name;
    struct bus_attribute    *bus_attrs;
    struct device_attribute    *dev_attrs;
    struct driver_attribute    *drv_attrs;

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    const struct dev_pm_ops *pm;

    struct bus_type_private *p;
};

/**********

struct bus_type_private {
    struct kset subsys;
    struct kset *drivers_kset;
    struct kset *devices_kset;
    struct klist klist_devices;
    struct klist klist_drivers;
    struct blocking_notifier_head bus_notifier;
    unsigned int drivers_autoprobe:1;
    struct bus_type *bus;
};

/****************让面这些东西或许读起来相当的痛苦,但是你知道的成长总是需要过程的,这些代码一次看不懂没事的,多看几次就行,其实现在我也不是很懂。。。**/

bus_type结构定义如下:
struct bus_type winter_bus_type = {
    .name = "winter_bus",
    .match = winter_match,
};  
  

如果你有一条总线要注册,那么注册后,会在sys/bus/目录下产生你的总线目录名,并且会在你的总线目录下在产生两个关于设备和驱动的目录,显然,它们记录了这条总线上所有的设备和驱动。

原文的整体代码如下:

#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

static char *Version = "$Revision: 1.0 $";

static int winter_match(struct device *dev, struct device_driver *driver)
{
    return !strncmp(dev->init_name, driver->name, strlen(driver->name));
}

static void winter_bus_release(struct device *dev)
{
    printk(KERN_DEBUG "winter bus release\n");
}

struct bus_type winter_bus_type = {
    .name = "winter_bus",
    .match = winter_match,
};    

struct device winter_bus_device = {
    .init_name   = "winter_dev",/**这个是在sys/device/下**/
    .release  = winter_bus_release
};

EXPORT_SYMBOL(winter_bus_device);
EXPORT_SYMBOL(winter_bus_type);

/**当用户读文件属性的时候调用该函数**/
static ssize_t show_bus_version(struct bus_type *bus,char *buf){
    return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

/*创建和初始化bus_attribute结构 BUS_ATTR(_name, _mode, _show, _store)*/
/*return struct bus_attribute bus_attr_version*/
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

static int __init winter_bus_init(void)
{
    int ret;
        /*注册总线*/
    ret = bus_register(&winter_bus_type);
    if (ret)
        return ret;
    /*创建一个包含版本号的属性文件/sys/bus/version*/        
    if (bus_create_file(&winter_bus_type, &bus_attr_version))
        printk(KERN_NOTICE "Fail to create version attribute!\n");
        
    /*注册总线设备*/
    ret = device_register(&winter_bus_device);
    if (ret)
        printk(KERN_NOTICE "Fail to register device:winter_bus!\n");
    return ret;
}

static void __exit winter_bus_exit(void)
{        //注销设备
        device_unregister(&winter_bus_device);
      //注销一条总线
        bus_unregister(&winter_bus_type);
}

module_init(winter_bus_init);
module_exit(winter_bus_exit);

MODULE_AUTHOR("Eilian.Lau");
MODULE_LICENSE("Dual BSD/GPL");





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值