linux-3.4.58 UIO笔记

UIO初始化

 

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;
}

static void uio_major_cleanup(void)
{
 unregister_chrdev_region(MKDEV(uio_major, 0), UIO_MAX_DEVICES);
 cdev_del(uio_cdev);
}

 

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;
}

 

注册设备

int __uio_register_device(struct module *owner,
     struct device *parent,
     struct uio_info *info)
{
 struct uio_device *idev;
 int ret = 0;

 if (!parent || !info || !info->name || !info->version)
  return -EINVAL;

 info->uio_dev = NULL;
       根据uio_info创建uio_device结构
 idev = kzalloc(sizeof(*idev), GFP_KERNEL);
 if (!idev) {
  ret = -ENOMEM;
  goto err_kzalloc;
 }
       设置所有的模块, 使用时需要增加引用计数 确保模块不会被卸载
 idev->owner = owner;
 idev->info = info;
 init_waitqueue_head(&idev->wait);
 atomic_set(&idev->event, 0);
从IDR中分配次设备号, 并将ui_device加入IDR
 ret = uio_get_minor(idev);
 if (ret)
  goto err_get_minor;
      根据次设备号 创建设备文件一般是uio0 uio1 uio2
 idev->dev = device_create(&uio_class, parent,
      MKDEV(uio_major, idev->minor), idev,
      "uio%d", idev->minor);
 if (IS_ERR(idev->dev)) {
  printk(KERN_ERR "UIO: device register failed\n");
  ret = PTR_ERR(idev->dev);
  goto err_device_create;
 }
设置sysfs属性,主要是IO内存和IO端口的属性文件
 ret = uio_dev_add_attributes(idev);
 if (ret)
  goto err_uio_dev_add_attributes;

 info->uio_dev = idev;
申请设备对应的中断, 处理函数主要做唤醒
 if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
  ret = request_irq(info->irq, uio_interrupt,
      info->irq_flags, info->name, idev);
  if (ret)
   goto err_request_irq;
 }

 return 0;

err_request_irq:
 uio_dev_del_attributes(idev);
err_uio_dev_add_attributes:
 device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
err_device_create:
 uio_free_minor(idev);
err_get_minor:
 kfree(idev);
err_kzalloc:
 return ret;
}
EXPORT_SYMBOL_GPL(__uio_register_device);

/**
 * uio_unregister_device - unregister a industrial IO device
 * @info: UIO device capabilities
 *
 */
void uio_unregister_device(struct uio_info *info)
{
 struct uio_device *idev;

 if (!info || !info->uio_dev)
  return;

 idev = info->uio_dev;

 uio_free_minor(idev);

 if (info->irq && (info->irq != UIO_IRQ_CUSTOM))
  free_irq(info->irq, idev);

 uio_dev_del_attributes(idev);

 device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
 kfree(idev);

 return;
}

 

UIO FOPS

static int uio_open(struct inode *inode, struct file *filep)
{
 struct uio_device *idev;
 struct uio_listener *listener;
 int ret = 0;

 mutex_lock(&minor_lock);

根据Inode中的次设备号 在IDR中查找到文件对应的 uio_device
 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;
 }

 

 listener = kmalloc(sizeof(*listener), GFP_KERNEL);
 if (!listener) {
  ret = -ENOMEM;
  goto err_alloc_listener;
 }

 listener->dev = idev;
 listener->event_count = atomic_read(&idev->event);

通过private_data指针实现私有数据隐藏
 filep->private_data = listener;

设备注册时是否指定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;
}

 

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;

 mi = uio_find_mem_index(vma);
 if (mi < 0)
  return -EINVAL;

 requested_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
 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;
 }

 switch (idev->info->mem[mi].memtype) {
  case UIO_MEM_PHYS:

mmap映射有两个方式:

直接映射所有页面
   return uio_mmap_physical(vma);
  case UIO_MEM_LOGICAL:
  case UIO_MEM_VIRTUAL:

并不直接映射,而是设置vma中的 vm_ops接口, 在应用层访问内存 引发缺页异常是映射,缺页异常引发时 缺页异常处理程序调用vm_ops 中的fault函数指针指向的uio_vma_fault函数,该函数中查找确实的页并返回
   return uio_mmap_logical(vma);
  default:
   return -EINVAL;
 }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值