file-fops-inode-dev_t之间一些关系

本文详细解析了Linux设备驱动的加载过程,从device_create到device_register的实现细节,再到系统如何通过文件名和路径找到相应的inode及fops,最终完成设备操作函数的注册。


本文为转载,仅作自己学习使用。


各种驱动形式本质还是把fops注册到inode中。

device_create->device_create_vargs->dev_set_drvdata(dev, drvdata)把fops设置到了dev->p->driver_data中         

 device_register->device_add->devtmpfs_create_node->vfs_mknod这里应该就是终点了


注:前半部分把fops函数数组放到了dev->device_private->driver_data中,后半部分vfs_mknod(nd.path.dentry->d_inode,dentry, mode, dev->devt);

[1]. 建立了设备号与inode名称的映射关系,这样通过文件名可以找到设备号,通过设备号就能找到dev结构,

[2]. 通过dev->device_private->driver_data就能解析出fops,从而给系统调用open时建立file operation


linux平台驱动其实不是真正的“驱动”它只不过做点初始化硬件的事情(在probe函数里)真正操作设备的函数在device结构里。

这里体现了C++类的影子

 

3.简单点说系统调用open会建立一个file结构体,并且通过文件名和路径找到inode结构,并提取i_fopfops

 

 

4.至于提取的过程

 

</pre><pre name="code" class="html">static int chrdev_open(struct inode *inode, struct file *filp)
{
 struct cdev *p;
 struct cdev *new = NULL;
 int ret = 0;
 spin_lock(&cdev_lock);
 p = inode->i_cdev;
 if (!p) {
  struct kobject *kobj;
  int idx;
  spin_unlock(&cdev_lock);
  kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
  if (!kobj)
   return -ENXIO;
  new = container_of(kobj, struct cdev, kobj);
  spin_lock(&cdev_lock);
  /* Check i_cdev again in case somebody beat us to it while
     we dropped the lock. */
  p = inode->i_cdev;
  if (!p) {
   inode->i_cdev = p = new;
   list_add(&inode->i_devices, &p->list);
   new = NULL;
  } else if (!cdev_get(p))
   ret = -ENXIO;
 } else if (!cdev_get(p))
  ret = -ENXIO;
 spin_unlock(&cdev_lock);
 cdev_put(new);
 if (ret)
  return ret;
 ret = -ENXIO;
 filp->f_op = fops_get(p->ops);
 if (!filp->f_op)
  goto out_cdev_put;
 if (filp->f_op->open) {
  ret = filp->f_op->open(inode,filp);
  if (ret)
   goto out_cdev_put;
 }
 return 0;
 out_cdev_put:
 cdev_put(p);
 return ret;
}



 注:貌似只有 open(!/dev/testchar!, O_RDWR) 打开才是这样的,因为/dev目录下都是字符的驱动,是不是使用cdev的都不在sysfs内呢?

 

在linux设备模型浅析之设备篇中有段描述:device_add定义在drivers/base/core.c中

int device_add(struct device *dev)

{

................

if (MAJOR(dev->devt)) {
  error = device_create_file(dev, &devt_attr); //如果存在设备号则添加dev_t属性,这样udev就能读取设备号属性从而在/dev/目录下创建设备节点,这样kobj和cdev也关联了
  if (error)
   goto ueventattrError;

 

注:所以我一直追求的目标貌似在这里,是udev把device里包含的fops关联到cedv里,然后chrdev_open就顺理成章了!!

 

 

补充点内容:device结构有个device_private用来放一些不想对外开放的东西,其中还有个driver_data。所以是这样的device->p->driver_data

一般情况下这里放的是file_operations但是也未必,对于platform来说有2个函数void *dev_get_drvdata(const struct device *dev)

和void dev_set_drvdata(struct device *dev, void *data)

注册的时候set,至于以后怎么用就不一定了,比如LED的驱动,使用get函数又取出数据,放在了attr里导出到用户空间使用

#include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/uaccess.h> #include <linux/ctype.h> #include <linux/mutex.h> // 添加互斥锁支持 #define DEVICE_NAME "my_char_dev" #define MAX_BUF_LEN 1024 /* 模块参数 */ static int cap = 0; module_param(cap, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(cap, "Convert to uppercase if set to 1 (default=0)"); /* 共享数据结构 */ static struct { char buffer[MAX_BUF_LEN]; // 数据缓冲区 size_t data_len; // 有效数据长度 struct mutex lock; // 互斥锁 } dev_data; static dev_t dev_num; static struct cdev my_cdev; static struct class *my_class; static struct device *my_device; /* 设备打开函数 */ static int device_open(struct inode *inode, struct file *file) { return 0; } /* 设备释放函数 */ static int device_release(struct inode *inode, struct file *file) { return 0; } /* 设备写入函数 */ static ssize_t device_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { size_t len = (count > MAX_BUF_LEN - 1) ? MAX_BUF_LEN - 1 : count; // 加锁保护共享数据 mutex_lock(&dev_data.lock); /* 从用户空间复制数据 */ if (copy_from_user(dev_data.buffer, buf, len)) { mutex_unlock(&dev_data.lock); return -EFAULT; } dev_data.buffer[len] = '\0'; // 确保字符串终止 dev_data.data_len = len; // 记录有效长度 /* 根据cap参数进行大小写转换 */ if (cap) { for (int i = 0; dev_data.buffer[i]; i++) { dev_data.buffer[i] = toupper(dev_data.buffer[i]); } } printk(KERN_INFO "my_char_dev: Received: %s\n", dev_data.buffer); mutex_unlock(&dev_data.lock); // 解锁 return len; } /* 新增:设备读取函数 */ static ssize_t device_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { ssize_t retval = 0; // 加锁保护共享数据 mutex_lock(&dev_data.lock); if (*f_pos >= dev_data.data_len) { mutex_unlock(&dev_data.lock); return 0; // 文件结尾 } // 计算剩余可读数据量 size_t remaining = dev_data.data_len - *f_pos; size_t to_copy = (count < remaining) ? count : remaining; // 复制数据到用户空间 if (copy_to_user(buf, dev_data.buffer + *f_pos, to_copy)) { mutex_unlock(&dev_data.lock); return -EFAULT; } *f_pos += to_copy; // 更新文件位置 retval = to_copy; mutex_unlock(&dev_data.lock); // 解锁 return retval; } /* 定义设备支持的操作 */ static const struct file_operations fops = { .owner = THIS_MODULE, .open = device_open, .release = device_release, .write = device_write, .read = device_read, // 添加read支持 }; /* 模块初始化函数 */ static int __init char_dev_init(void) { /* 初始化共享数据 */ mutex_init(&dev_data.lock); // 初始化互斥锁 dev_data.data_len = 0; dev_data.buffer[0] = '\0'; /* 动态分配设备号 */ if (alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME) < 0) return -1; /* 初始化字符设备 */ cdev_init(&my_cdev, &fops); if (cdev_add(&my_cdev, dev_num, 1) < 0) goto err_cdev; /* 创建设备类 */ my_class = class_create(THIS_MODULE, "my_char_class"); if (IS_ERR(my_class)) goto err_class; /* 创建设备节点 */ my_device = device_create(my_class, NULL, dev_num, NULL, DEVICE_NAME); if (IS_ERR(my_device)) goto err_device; /* 设置设备权限(所有用户可读写) */ device_create_file(my_device, &dev_attr_cap); // 可选:添加cap属性文件 printk(KERN_INFO "my_char_dev: Module loaded, cap=%d\n", cap); return 0; /* 错误处理 */ err_device: class_destroy(my_class); err_class: cdev_del(&my_cdev); err_cdev: unregister_chrdev_region(dev_num, 1); return -1; } /* 模块退出函数 */ static void __exit char_dev_exit(void) { device_destroy(my_class, dev_num); class_destroy(my_class); cdev_del(&my_cdev); unregister_chrdev_region(dev_num, 1); mutex_destroy(&dev_data.lock); // 销毁互斥锁 printk(KERN_INFO "my_char_dev: Module unloaded\n"); } module_init(char_dev_init); module_exit(char_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Advanced char device driver with case conversion"); 为这段代码声明、定义的变量、函数添加注释
08-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值