Chinaunix首页 | 论坛 | 博客
  • 博客访问: 110449
  • 博文数量: 24
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 270
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-30 18:17
文章分类

全部博文(24)

文章存档

2010年(21)

2009年(3)

我的朋友

分类: LINUX

2010-03-05 17:35:59

一,理解设备驱动模型对linux驱动的编写是大有裨益的,下面会结合一个简单的实例对总线这一块作一些分析。(基于TCC8900 + linux-2.6.28

 

二,实例代码如下:

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

#define COMMENT_SIZE 1024
static char comment[COMMENT_SIZE] = "this bus is for test";
static int bus_match(struct device *dev,struct device_driver * driver)
{
    return !strncmp(dev->bus_id,driver->name, strlen(driver->name));
}
static void test_bus_release(struct device * dev)
{
    printk(KERN_INFO "test bus release\n");
}
struct device test_bus ={
    .bus_id = "test_bus0",
    .release = test_bus_release
};
struct bus_type test_bus_type = {
    /*这个名字会出现在/sys/bus/目录下*/
    .name = "test_bus",
    .match = bus_match,
};
static ssize_t show_bus_comment(struct bus_type * bus, char * buf)
{
    return snprintf(buf, COMMENT_SIZE, "%s\n", comment);
}
static ssize_t store_bus_comment(struct bus_type *bus,
             const char *buf, size_t count)
{
    memset(comment, 0, COMMENT_SIZE);
    if(count>COMMENT_SIZE){
        strncpy(comment,buf,COMMENT_SIZE-1);
        return COMMENT_SIZE-1;
    }else{
        strncpy(comment,buf,count);
        return count;
    }
}
static BUS_ATTR(comment, S_IWUSR | S_IRUGO, show_bus_comment, store_bus_comment);

static int __init test_bus_init(void)
{
    int ret;
   /*注册总线设备*/
    ret = device_register(&test_bus);
    if (ret){
     printk(KERN_WARNING "test_bus device_register error: %d\n", ret);
     return ret;
    }
    /*注册总线*/
    ret=bus_register(&test_bus_type);
    if(ret){
        printk(KERN_WARNING "bus_register error: %d\n", ret);
        device_unregister(&test_bus);
        return ret;
    }
    /*创建属性文件*/
    if(bus_create_file(&test_bus_type, &bus_attr_comment))
        printk(KERN_INFO "cannot create comment attribute!\n");
    return ret;
}
static void __exit test_bus_exit(void)
{
    bus_unregister(&test_bus_type);
    device_unregister(&test_bus);
}

EXPORT_SYMBOL(test_bus);
EXPORT_SYMBOL(test_bus_type);
module_init(test_bus_init);
module_exit(test_bus_exit);
MODULE_AUTHOR("J.H.Luo");
MODULE_LICENSE("Dual BSD/GPL");


1,bus_type结构体

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 (*suspend_late)(struct device *dev, pm_message_t state);
    int (*resume_early)(struct device *dev);
    int (*resume)(struct device *dev);

    struct pm_ext_ops *pm;
/*下面这个结构体可以说是一个容器,内嵌了sysfs文件系统最基本的结构体kobject*/
    struct bus_type_private *p;
};

 

在本例中,我们只定义了bus_typenamematch成员,其中match函数只是简单地匹配devicedevice_driverID是不是一样。如果一样则返回非零,说明匹配成功。

 

2

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;
/*我中有你,你中有我的思想在linux内核中随处可见*/
    priv->bus = bus;
    bus->p = priv;

    BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

  /*把总线的名称设置为kobject的名称,这就是为什么我们在/sys/bus/下可以看到一个目录项跟新注册的总线名称一样的原因*/
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
    if (retval)
        goto out;
  /*初始化kset,并注册它*/
    priv->subsys.kobj.kset = bus_kset;
    priv->subsys.kobj.ktype = &bus_ktype;
    priv->drivers_autoprobe = 1;

    retval = kset_register(&priv->subsys);
    if (retval)
        goto out;
 /*创建属性文件uevent ,支持热插拔之用*/
    retval = bus_create_file(bus, &bus_attr_uevent);
    if (retval)
        goto bus_uevent_fail;
/*在每条bus总线下创建两个目录项devices和drivers
以存放挂在这条总线上的设备和驱动*/

    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, klist跟引用计数有关系,
klist_devices_get和klist_devices_put分别是
引用计数增加和减小的操作函数*/

    klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
    klist_init(&priv->klist_drivers, NULL, NULL);
/*创建两个属性:drivers_probe,drivers_autoprobe*/
    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:
    return retval;
}

 

整个总线的注册过程大部分工作都是在处理bus_type_private这个结构体,最终的结果是在/sys/bus/下产生一个新的总线分支。

 

3,在本例中,用bus_create_file()创建一个名为comment的属性,bus_attr_comment由宏BUS_ATTR定义,读写这个属性时分别会调用这个属性的函数:show_bus_commentstore_bus_comment

其实,由上面注册bus的过程可知,我们也可以直接把这个属性bus_attr_comment放在bus_typebus_attrs的成员里,这样bus_register的时候它自会为我们创建这个属性。

驱动代码稍作修改即可,如下:

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

#define COMMENT_SIZE 1024
static char comment[COMMENT_SIZE] = "this bus is for test";

static void test_bus_release(struct device * dev)
{
    printk(KERN_INFO "test bus release\n");
}
struct device test_bus ={
    .bus_id = "test_bus0",
    .release = test_bus_release
};

static int bus_match(struct device *dev,struct device_driver * driver)
{
    return !strncmp(dev->bus_id,driver->name, strlen(driver->name));
}
static ssize_t show_bus_comment(struct bus_type * bus, char * buf)
{
    return snprintf(buf, COMMENT_SIZE, "%s\n", comment);
}
static ssize_t store_bus_comment(struct bus_type *bus,
             const char *buf, size_t count)
{
    memset(comment, 0, COMMENT_SIZE);
    if(count>COMMENT_SIZE){
        strncpy(comment,buf,COMMENT_SIZE-1);
        return COMMENT_SIZE-1;
    }else{
        strncpy(comment,buf,count);
        return count;
    }
}
static BUS_ATTR(comment, S_IWUSR | S_IRUGO, show_bus_comment, store_bus_comment);

struct bus_type test_bus_type = {
    /*这个名字会出现在/sys/bus/目录下*/
    .name = "test_bus",
    .match = bus_match,
    .bus_attrs = &bus_attr_comment,
};

static int __init test_bus_init(void)
{
    int ret;
   /*注册总线设备*/
    ret = device_register(&test_bus);
    if (ret){
     printk(KERN_WARNING "test_bus device_register error: %d\n", ret);
     return ret;
    }
    /*注册总线*/
    ret=bus_register(&test_bus_type);
    if(ret){
        printk(KERN_WARNING "bus_register error: %d\n", ret);
        device_unregister(&test_bus);
        return ret;
    }
    return ret;
}
static void __exit test_bus_exit(void)
{
    bus_unregister(&test_bus_type);
    device_unregister(&test_bus);
}

EXPORT_SYMBOL(test_bus);
EXPORT_SYMBOL(test_bus_type);
module_init(test_bus_init);
module_exit(test_bus_exit);
MODULE_AUTHOR("J.H.Luo");
MODULE_LICENSE("Dual BSD/GPL");


三,最后给出这个实例的测试过程:

/nand2 # insmod bus_test.ko
/nand2 # cd /sys/bus/test_bus/
/sys/bus/test_bus # ls
comment drivers drivers_probe
devices drivers_autoprobe uevent
/sys/bus/test_bus # cat comment
this bus is for test
/sys/bus/test_bus # cat > comment
/sys/bus/test_bus # echo hey,guys > comment
/sys/bus/test_bus # cat comment
hey,guys


------------------------------------------

本文乃原创!

转载请注明出处:http://sparklecliz.cublog.cn/

------------------------------------------



阅读(1919) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~