uio.c 中的初始化

uio.c 是uio的核心文件。其入口函数如下,可以看到uio也是作为一个module被kernel初始化的.
static int __init uio_init(void)
{
    return init_uio_class();
}
module_init(uio_init)
在init_uio_class中只要做了两件事情,一件是注册字符设备,初始化字符设备的major number,从前面的分析可以知道每个uio driver的kernel part都是一个字符设备。 第二件是初始化uio这个class,这样就可以看到/sys/class/uio
static int init_uio_class(void)
{
    int ret;

    /* This is the first time in here, set everything up properly */
    ret = uio_major_init();
    if (ret)
        goto exit;

    ret = class_register(&uio_class);
    if (ret) {
        printk(KERN_ERR "class_register failed for uio\n");
        goto err_class_register;
    }
    return 0;

err_class_register:
    uio_major_cleanup();
exit:
    return ret;
}
第二件很简单只要是调用class_register 完成的,我们看看第一件事情是如何注册字符设备的。
static int uio_major_init(void)
{
    static const char name[] = "uio";
    struct cdev *cdev = NULL;
    dev_t uio_dev = 0;
    int result;

    result = alloc_chrdev_region(&uio_dev, 0, UIO_MAX_DEVICES, name);
    if (result)
        goto out;

    result = -ENOMEM;
    cdev = cdev_alloc();
    if (!cdev)
        goto out_unregister;

    cdev->owner = THIS_MODULE;
    cdev->ops = &uio_fops;
    kobject_set_name(&cdev->kobj, "%s", name);

    result = cdev_add(cdev, uio_dev, UIO_MAX_DEVICES);
    if (result)
        goto out_put;

    uio_major = MAJOR(uio_dev);
    uio_cdev = cdev;
    return 0;
out_put:
    kobject_put(&cdev->kobj);
out_unregister:
    unregister_chrdev_region(uio_dev, UIO_MAX_DEVICES);
out:
    return result;
}
从uio_major_init 中可以看到就是调用字符设备的三步走方法,alloc_chrdev_region/cdev_alloc/cdev_add。最后从    uio_major = MAJOR(uio_dev);中得到major number。我们重点看看这个字符设备的ops。
static const struct file_operations uio_fops = {
    .owner        = THIS_MODULE,
    .open        = uio_open,
    .release    = uio_release,
    .read        = uio_read,
    .write        = uio_write,
    .mmap        = uio_mmap,
    .poll        = uio_poll,
    .fasync        = uio_fasync,
    .llseek        = noop_llseek,
};
当我们在用户态通过open函数打开/dev/uioX 的时候会调用uio_fops的open函数uio_open,在uio_open 中
static int uio_open(struct inode *inode, struct file *filep)
{
    struct uio_device *idev;
    struct uio_listener *listener;
    int ret = 0;
// 首先更加minor number找到uio_device *idev。且这个minor number 是通过iminor 从inode->i_rdev中获得的
//static inline unsigned iminor(const struct inode *inode)
//{
//    return MINOR(inode->i_rdev);
//}


    mutex_lock(&minor_lock);
    idev = idr_find(&uio_idr, iminor(inode));
    mutex_unlock(&minor_lock);
    if (!idev) {
        ret = -ENODEV;
        goto out;
    }

    if (!try_module_get(idev->owner)) {
        ret = -ENODEV;
        goto out;
    }
//申请一个    struct uio_listener *listener; 结构图

    listener = kmalloc(sizeof(*listener), GFP_KERNEL);
    if (!listener) {
        ret = -ENOMEM;
        goto err_alloc_listener;
    }
//给listener赋值
    listener->dev = idev;
    listener->event_count = atomic_read(&idev->event);
    filep->private_data = listener;
//通过idev->info->open 调用具体uio driver 中的uio_info 中的open函数,例如前文中讲过的uioinfo->open = uio_dmem_genirq_open;

    if (idev->info->open) {
        ret = idev->info->open(idev->info, inode);
        if (ret)
            goto err_infoopen;
    }
    return 0;

err_infoopen:
    kfree(listener);

err_alloc_listener:
    module_put(idev->owner);

out:
    return ret;
}
当在用户态调用read和write的时候同样会调用uio_fops的read/writer函数,这两个函数主要调用copy_to_user和copy_from_user。
同样当用户态调用mmap的时候会调用uio_fops的uio_mmap
static int uio_mmap(struct file *filep, struct vm_area_struct *vma)
{
    struct uio_listener *listener = filep->private_data;
    struct uio_device *idev = listener->dev;
    int mi;
    unsigned long requested_pages, actual_pages;
    int ret = 0;

    if (vma->vm_end < vma->vm_start)
        return -EINVAL;

    vma->vm_private_data = idev;
//根据vma找到在uio_info中的index,这个index的范围是0~5
    mi = uio_find_mem_index(vma);
    if (mi < 0)
        return -EINVAL;
//从vma中得到需要映射的page即requested_pages,对其后得到实际需要的pages actual_pages。
    requested_pages = vma_pages(vma);
    actual_pages = ((idev->info->mem[mi].addr & ~PAGE_MASK)
            + idev->info->mem[mi].size + PAGE_SIZE -1) >> PAGE_SHIFT;
    if (requested_pages > actual_pages)
        return -EINVAL;

    if (idev->info->mmap) {
        ret = idev->info->mmap(idev->info, vma);
        return ret;
    }
// 根据uio driver映射的type 调用不同的函数,从下面可以知道映射的memtype可以分为两类UIO_MEM_PHYS和UIO_MEM_LOGICAL/UIO_MEM_VIRTUAL。如果是UIO_MEM_PHYS 表示user space拿到的是物理地址,如果是UIO_MEM_LOGICAL/UIO_MEM_VIRTUAL的话表示user space拿到的是虚拟地址.
    switch (idev->info->mem[mi].memtype) {
        case UIO_MEM_PHYS:
            return uio_mmap_physical(vma);
        case UIO_MEM_LOGICAL:
        case UIO_MEM_VIRTUAL:
            return uio_mmap_logical(vma);
        default:
            return -EINVAL;
    }
}
在uio_mmap_physical和uio_mmap_logical 中最终要的就是设置vma->vm_ops = &uio_physical_vm_ops/    vma->vm_ops = &uio_logical_vm_ops;。这个vm_ops 的用法后面再分析.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值