lddbus-sculld之设备模型

lddbus.c:这里包括了设备模型包的一些定义。

static int __init ldd_bus_init(void)
{
    int ret;

    ret = bus_register(&ldd_bus_type);        //注册总线,在/sys/bus/下会创建文件夹,其名字为ldd_bus_type.name,即ldd

    if (ret)
        return ret;
    if (bus_create_file(&ldd_bus_type, &bus_attr_version))    //在sys/bus/ldd/下创建文件version,它用来显示版本号

        printk(KERN_NOTICE "Unable to create version attribute\n");
    ret = device_register(&ldd_bus);        //注册设备

    if (ret)
        printk(KERN_NOTICE "Unable to register ldd0\n");
    return ret;
}

总线 ldd_bus_type定义:

/*
 * Respond to hotplug events.
 */

static int ldd_hotplug(struct device *dev, char **envp, int num_envp,
        char *buffer, int buffer_size)
{
    envp[0] = buffer;
    if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s",
             Version) >= buffer_size)
        return -ENOMEM;
    envp[1] = NULL;
    return 0;
}

/*
 * Match LDD devices to drivers. Just do a simple name test.
 */

static int ldd_match(struct device *dev, struct device_driver *driver)
{
    return !strncmp(dev->bus_id, driver->name, strlen(driver->name));       //这里比较的长度为什么是driver->name的长度,而不是dev->bus_id的长度,是因为,一般一个驱动可以驱动多个设备,而一个设备只能有一个驱动,比如这里的驱动名为sculld,而设备有sculld0,sculld1等。
}

/*
 * And the bus type.
 */

struct bus_type ldd_bus_type = {
    .name = "ldd",
    .match = ldd_match,
    .hotplug = ldd_hotplug,
};

总线的一些属性bus_attr_version定义:

/*
 * Export a simple attribute.
 */

static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);

设备 ldd_bus定义:

/*
 * The LDD bus device.
 */

static void ldd_bus_release(struct device *dev)
{
    printk(KERN_DEBUG "lddbus release\n");
}
    
struct device ldd_bus = {
    .bus_id = "ldd0",
    .release = ldd_bus_release
};

现在我们要利用上面的模型包了,分析驱动sculld驱动

int sculld_init(void)
{
    int result, i;
    dev_t dev = MKDEV(sculld_major, 0);
    
    /*
     * Register your major, and accept a dynamic number.
     */

    if (sculld_major)
        result = register_chrdev_region(dev, sculld_devs, "sculld");
    else {
        result = alloc_chrdev_region(&dev, 0, sculld_devs, "sculld");
        sculld_major = MAJOR(dev);
    }
    if (result < 0)
        return result;

    /*
     * Register with the driver core.
     */

    register_ldd_driver(&sculld_driver);
    
    /*
     * allocate the devices -- we can't have them static, as the number
     * can be specified at load time
     */

    sculld_devices = kmalloc(sculld_devs*sizeof (struct sculld_dev), GFP_KERNEL);
    if (!sculld_devices) {
        result = -ENOMEM;
        goto fail_malloc;
    }
    memset(sculld_devices, 0, sculld_devs*sizeof (struct sculld_dev));
    for (i = 0; i < sculld_devs; i++) {
        sculld_devices[i].order = sculld_order;
        sculld_devices[i].qset = sculld_qset;
        sema_init (&sculld_devices[i].sem, 1);
        sculld_setup_cdev(sculld_devices + i, i);
        sculld_register_dev(sculld_devices + i, i);
    }


#ifdef SCULLD_USE_PROC /* only when available */
    create_proc_read_entry("sculldmem", 0, NULL, sculld_read_procmem, NULL);
#endif
    return 0; /* succeed */

  fail_malloc:
    unregister_chrdev_region(dev, sculld_devs);
    return result;
}

sculld_driver定义:

/*
 * The LDD driver type.
 */

struct ldd_driver {
    char *version;
    struct module *module;
    struct device_driver driver;
    struct driver_attribute version_attr;
};

 

/* Device model stuff */

static struct ldd_driver sculld_driver = {
    .version = "$Revision: 1.21 $",
    .module = THIS_MODULE,
    .driver = {
        .name = "sculld",
    },
};

register_ldd_driver(&sculld_driver)注册驱动,看一下它的定义:

int register_ldd_driver(struct ldd_driver *driver)
{
    int ret;
    
    driver->driver.bus = &ldd_bus_type;      //驱动的总线是ldd_bus_type
    ret = driver_register(&driver->driver);  //向系统注册驱动
    if (ret)
        return ret;
    driver->version_attr.attr.name = "version";    //属性文件名
    driver->version_attr.attr.owner = driver->module; //属性属主
    driver->version_attr.attr.mode = S_IRUGO;      //属性文件的读写权限
    driver->version_attr.show = show_version;      //属性文件读
    driver->version_attr.store = NULL;
    return driver_create_file(&driver->driver, &driver->version_attr);  //在这个设备的文件夹下,创建一个属性文件,由driver->version_attr描述
}

从上面的分析可知,这个函数功能:1,确定使用的总线;2,注册驱动,3,创建属性文件,方便用户查询一些信息。
现在继续往下分析:sculld_setup_cdev(sculld_devices + i, i);

static void sculld_setup_cdev(struct sculld_dev *dev, int index)
{
    int err, devno = MKDEV(sculld_major, index);
    
    cdev_init(&dev->cdev, &sculld_fops);  //初始化字符设备,这个字符设备的操作方法就是sculld_fops,比如应用层open打开这个文件时就会调用这个结构体的open函数。
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &sculld_fops;
    err = cdev_add (&dev->cdev, devno, 1);  //字符设备和设备的设备号相关,并向系统注册这个设备。至于设备名无所谓的,对于内核而言它认的是设备号,所以在创建设备节点的时候,可以任意取名,但是设备号必须一致。
    /* Fail gracefully if need be */
    if (err)
        printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}

通过上面的分析,我们就明白了其实它就是像系统注册一个字符设备。
接着分析:sculld_register_dev(sculld_devices + i, i);

struct ldd_device {
    char *name;
    struct ldd_driver *driver;
    struct device dev;
};

struct sculld_dev {
    void **data;
    struct sculld_dev *next; /* next listitem */
    int vmas; /* active mappings */
    int order; /* the current allocation order */
    int qset; /* the current array size */
    size_t size; /* 32-bit will suffice */
    struct semaphore sem; /* Mutual exclusion */
    struct cdev cdev;
    char devname[20];
    struct ldd_device ldev;
};

static ssize_t sculld_show_dev(struct device *ddev, char *buf)
{
    struct sculld_dev *dev = ddev->driver_data;

    return print_dev_t(buf, dev->cdev.dev);
}

static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);

static void sculld_register_dev(struct sculld_dev *dev, int index)
{
    sprintf(dev->devname, "sculld%d", index);
    dev->ldev.name = dev->devname;
    dev->ldev.driver = &sculld_driver;
    dev->ldev.dev.driver_data = dev;
    register_ldd_device(&dev->ldev);
    device_create_file(&dev->ldev.dev, &dev_attr_dev);
}

可以看到上面这个函数前面部分主要是对dev->ldev成员赋值。然后通过调用register_ldd_device(&dev->ldev);进行设备注册,这是我猜的,但答案应该是对的,看源代码:

int register_ldd_device(struct ldd_device *ldddev)
{
    ldddev->dev.bus = &ldd_bus_type;   //设备的总线
    ldddev->dev.parent = &ldd_bus;     //父设备是ldd_bus
    ldddev->dev.release = ldd_dev_release;
    strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE);  //设备名赋值
    return device_register(&ldddev->dev);  //向系统注册这个设备
}

设备的注册,必须是struct device,所以上面我们可以看到,对设备的ldd_device里面的dev成员进行了一些初始化。注意,这里使用的总线也是ldd_bus_type。其实,device_register函数向系统注册设备,它会查询挂在这个设备的总线上有哪些驱动device_driver,通过回调总线驱动的match函数进行匹配,一般在match函数中会比较device和device_driver的名字是否相同,如果相同,那么就说明匹配。也就是说设备和驱动挂接上了,这时会回调总线驱动的probe函数,最终会device_driver的probe函数,进行驱动加载。 (由于ldd3驱动使用的内核比较久了,所以,上面说的有点出入,但整体可以这么理解)
device_create_file(&dev->ldev.dev, &dev_attr_dev);在设备文件夹下,创建一个设备属性文件。dev_attr_dev定义,在这个设备属性文件中显示了设备的设备号,udev就可以通过它来创建设备节点。:

static ssize_t sculld_show_dev(struct device *ddev, char *buf)
{
    struct sculld_dev *dev = ddev->driver_data;

    return print_dev_t(buf, dev->cdev.dev);
}

static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);

设备模型,这里可以发现一个是通过总线串起设备和设备驱动,设备里面又包含了子父设备的关系。在sys文件系统就构成了相应的目录树结构
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值