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

全部博文(24)

文章存档

2010年(21)

2009年(3)

我的朋友

分类: LINUX

2010-03-11 15:11:44

一,本文继《设备驱动模型之总线浅析》之后,结合一个简单的实例对device这一块作一些分析。(基于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>

extern struct device test_bus;
extern struct bus_type test_bus_type;
#define INFO_SIZE 512
static char device_info[INFO_SIZE]="this is a test device";
static void test_dev_release(struct device *dev)
{
    printk(KERN_INFO "test device release\n");
}
struct device test_dev = {
    .bus = &test_bus_type,
    .bus_id = "test_dev",
    .parent = &test_bus,
    .release = test_dev_release,
};

static ssize_t test_dev_show(struct device *dev,
    struct device_attribute *attr, char *buf)
{
    return snprintf(buf, INFO_SIZE, "%s\n", device_info);
}
static ssize_t test_dev_store(struct device *dev,
    struct device_attribute *attr, const char *buf, size_t count)
{
    memset(device_info, 0, INFO_SIZE);
    if(count>INFO_SIZE){
        strncpy(device_info,buf,INFO_SIZE-1);
        return INFO_SIZE-1;
    }else{
        strncpy(device_info,buf,count);
        return count;
    }
}

static DEVICE_ATTR(dev_info, S_IWUGO | S_IRUGO | S_IWUSR, test_dev_show, test_dev_store);

static int __init test_device_init(void)
{
    int ret = 0;
    
    /*注册设备*/
    ret = device_register(&test_dev);
    if (ret){
        printk(KERN_WARNING "test_dev device_register error: %d\n", ret);
        return ret;
    }
    /*创建属性文件*/    
     if(device_create_file(&test_dev, &dev_attr_dev_info))
            printk(KERN_INFO "cannot create device attribute!\n");
        return ret;
}
static void __exit test_device_exit(void)
{
    device_unregister(&test_dev);
}
module_init(test_device_init);
module_exit(test_device_exit);
MODULE_AUTHOR("J.H.Luo");
MODULE_LICENSE("Dual BSD/GPL");

1,在本例中,会用到上一个实例中的总线和总线设备。


2,设备device的定义:

 

struct device {
    struct klist        klist_children;
    struct klist_node    knode_parent;    /* node in sibling list */
    struct klist_node    knode_driver;
    struct klist_node    knode_bus;
    struct device        *parent; //父设备,一般指向总线设备。


    struct kobject kobj; //内嵌了一个kobject

    char    bus_id[BUS_ID_SIZE];    /* 设备名称,position on parent bus */
    const char        *init_name; /* initial name of the device */
    struct device_type    *type;
    unsigned        uevent_suppress:1;

    struct semaphore    sem;    /* semaphore to synchronize calls to
                     * its driver.
                     */


    struct bus_type    *bus;        /* 所属的总线 type of bus device is on */
    struct device_driver *driver;    /* 与此设备相匹配的驱动which driver has allocated this
                     device */

    void        *driver_data;    /* data private to the driver */
    void        *platform_data;    /* Platform specific data, device
                     core doesn't touch it */

    struct dev_pm_info    power;

#ifdef CONFIG_NUMA
    int        numa_node;    /* NUMA node this device is close to */
#endif
    u64        *dma_mask;    /* dma mask (if dma'able device) */
    u64        coherent_dma_mask;/* Like dma_mask, but for
                     alloc_coherent mappings as
                     not all hardware supports
                     64 bit addresses for consistent
                     allocations such descriptors. */


    struct device_dma_parameters *dma_parms;

    struct list_head    dma_pools;    /* dma pools (if dma'ble) */
#ifdef CONFIG_DPM
        struct constraints *constraints;
#endif
    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem
                     override */

    /* arch specific additions */
    struct dev_archdata    archdata;

    spinlock_t        devres_lock;
    struct list_head    devres_head;

    struct klist_node    knode_class;
    struct class        *class;
    dev_t            devt;    /* dev_t, creates the sysfs "dev" */
    struct attribute_group    **groups;    /* optional groups */

    void    (*release)(struct device *dev);
};


3device的注册过程:

 

int device_register(struct device *dev)
{
    device_initialize(dev);
    return device_add(dev);
}

device_initialize()主要是完成一些初始化工作。

void device_initialize(struct device *dev)
{
/*指定kobject的kset*/
    dev->kobj.kset = devices_kset;
/*初始化kobject的ktype*/
    kobject_init(&dev->kobj, &device_ktype);
    klist_init(&dev->klist_children, klist_children_get,
         klist_children_put);
    INIT_LIST_HEAD(&dev->dma_pools);
    init_MUTEX(&dev->sem);
    spin_lock_init(&dev->devres_lock);
    INIT_LIST_HEAD(&dev->devres_head);
    device_init_wakeup(dev, 0);
    device_pm_init(dev);
    set_dev_node(dev, -1);
}


真正注册device是由device_add()来完成:

int device_add(struct device *dev)
{
    struct device *parent = NULL;
    struct class_interface *class_intf;
    int error = -EINVAL;
    /*引用计数加1*/
    dev = get_device(dev);
    if (!dev)
        goto done;

    /* Temporarily support init_name if it is set.
     * It will override bus_id for now */

    if (dev->init_name)
        dev_set_name(dev, "%s", dev->init_name);

    if (!strlen(dev->bus_id))
        goto done;

    pr_debug("device: '%s': %s\n", dev->bus_id, __func__);
/*父设备的引用计数加1,并设置kobject的parent*/
    parent = get_device(dev->parent);
    setup_parent(dev, parent);

    /* use parent numa_node */
    if (parent)
        set_dev_node(dev, dev_to_node(parent));

    /* first, register with generic layer. */
/*将内嵌的kobject加入到层次结构中,并且建立sysfs entry.*/
    error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
    if (error)
        goto Error;

    /* notify platform of device entry */
    if (platform_notify)
        platform_notify(dev);

    /* notify clients of device entry (new way) */
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                     BUS_NOTIFY_ADD_DEVICE, dev);
/*建立属性为uevent_attr的属性文件*/
    error = device_create_file(dev, &uevent_attr);
    if (error)
        goto attrError;
/*如果device中指定了设备号,则建立属性为devt_attr的属性文件*/
    if (MAJOR(dev->devt)) {
        error = device_create_file(dev, &devt_attr);
        if (error)
            goto ueventattrError;

        error = device_create_sys_dev_entry(dev);
        if (error)
            goto devtattrError;
    }

    error = device_add_class_symlinks(dev);
    if (error)
        goto SymlinkError;
    error = device_add_attrs(dev);
    if (error)
        goto AttrsError;
/* bus_add_device()会在对应总线代表目录的device目录下创建一个到device的链接*/
    error = bus_add_device(dev);
    if (error)
        goto BusError;
    error = dpm_sysfs_add(dev);
    if (error)
        goto DPMError;
    device_pm_add(dev);

/*设置环境变量,产生一个add事件;
kobject_uevent_env()会调用call_usermodehelper(argv[0], argv,        env->envp, UMH_WAIT_EXEC); 引起热拔插事件用户空间脚本执行。*/

    kobject_uevent(&dev->kobj, KOBJ_ADD);
/* bus_attach_device()是匹配设备与驱动,跟下去可以知道:如果dev->driver已经存在,最终会调用device_bind_driver()进行绑定,否则遍历dev->bus上drivers列表,调用dev->bus.match(dev,drv)来看是否有一个驱动与该dev匹配。如果匹配则绑定。*/
    bus_attach_device(dev);
    if (parent)
        klist_add_tail(&dev->knode_parent, &parent->klist_children);
//如果设备属于某个类,则建立类的sysfs符号链接

    if (dev->class) {
        mutex_lock(&dev->class->p->class_mutex);
        /* tie the class to the device */
        klist_add_tail(&dev->knode_class,
             &dev->class->p->class_devices);

        /* notify any interfaces that the device is here */
        list_for_each_entry(class_intf,
                 &dev->class->p->class_interfaces, node)
            if (class_intf->add_dev)
                class_intf->add_dev(dev, class_intf);
        mutex_unlock(&dev->class->p->class_mutex);
    }
done:
    put_device(dev);
    return error;
 DPMError:
    bus_remove_device(dev);
 BusError:
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                     BUS_NOTIFY_DEL_DEVICE, dev);
    device_remove_attrs(dev);
 AttrsError:
    device_remove_class_symlinks(dev);
 SymlinkError:
    if (MAJOR(dev->devt))
        device_remove_sys_dev_entry(dev);
 devtattrError:
    if (MAJOR(dev->devt))
        device_remove_file(dev, &devt_attr);
 ueventattrError:
    device_remove_file(dev, &uevent_attr);
 attrError:
    kobject_uevent(&dev->kobj, KOBJ_REMOVE);
    kobject_del(&dev->kobj);
 Error:
    cleanup_device_parent(dev);
    if (parent)
        put_device(parent);
    goto done;
}

 

三,最后来测试一下这个实例:

/nand2 # insmod bus_test.ko
/nand2 # insmod device_test.ko
/nand2 # cd /sys/bus/test_bus/devices/
/sys/bus/test_bus/devices # ls -l
lrwxrwxrwx 1 0 0 0 Jan 1 00:00 test_dev -> ../../../devices/test_bus0/test_dev
/sys/bus/test_bus/devices # cd /sys/devices/test_bus0/
/sys/devices/test_bus0 # ls -l
drwxr-xr-x 2 0 0 0 Jan 1 00:01 power
drwxr-xr-x 3 0 0 0 Jan 1 00:00 test_dev
-rw-r--r-- 1 0 0 4096 Jan 1 00:01 uevent
/sys/devices/test_bus0 # cd test_dev/
/sys/devices/test_bus0/test_dev # ls -l
lrwxrwxrwx 1 0 0 0 Jan 1 00:01 bus -> ../../../bus/test_bus
-rw-rw-rw- 1 0 0 4096 Jan 1 00:01 dev_info
drwxr-xr-x 2 0 0 0 Jan 1 00:01 power
lrwxrwxrwx 1 0 0 0 Jan 1 00:01 subsystem -> ../../../bus/test_bus
-rw-r--r-- 1 0 0 4096 Jan 1 00:01 uevent
/sys/devices/test_bus0/test_dev # cat dev_info
this is a test device
/sys/devices/test_bus0/test_dev # echo hey,guys > dev_info
/sys/devices/test_bus0/test_dev # cat dev_info
hey,guys


 

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

本文乃原创!

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

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

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