Linux字符設備驅動
阿新 • • 發佈:2017-06-08
case 結構 完全 .net 實例 som main node ont
本文詳細介紹字符設備驅動,使用linux-4.8.2版本代碼。
1.綜述:從註冊到open、read/write
- 申請設備號;
- 註冊cdev到cdev_map:cdev_init和cdev_add;
- 創建設備節點:mknod(手動)或者udev(自動,借助熱插拔和sysfs,推薦);
- 用戶空間通過路徑open到內核空間對應的設備節點,內核空間中(VFS、filp和cdev_map):chrdev_open根據設備節點中的設備號(此時該節點的cdev還是NULL),找到cdev實例,並賦值到設備節點的成員cdev變量上,最後把cdev的ops賦值到filp->f_op,並調用filp->f_op->open函數;
- 用戶空間通過fd,read到內核空間,sys_read調用vfs_read最終調用 file->f_op->read;
備註:
-
-
- 由3創建出來的設備節點是不完全初始化的設備節點,至少包含設備號,不包含cdev實例;
- 在5處,根據fd而不是文件路徑,找到struct file而不是struct inode去獲取open已經掛接好的cdev的read;
-
2.sys_open代碼
[todo]
3.chrdev_open代碼
1 static int chrdev_open(struct inode *inode, struct file *filp)2 { 3 const struct file_operations *fops; 4 struct cdev *p; 5 struct cdev *new = NULL; 6 int ret = 0; 7 8 spin_lock(&cdev_lock); 9 p = inode->i_cdev; 10 if (!p) {//第一次打開時進入 11 struct kobject *kobj; 12 int idx; 13 spin_unlock(&cdev_lock);14 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);//在cdev_map中找到kobj 15 if (!kobj) 16 return -ENXIO; 17 new = container_of(kobj, struct cdev, kobj);//找到cdev 18 spin_lock(&cdev_lock); 19 /* Check i_cdev again in case somebody beat us to it while 20 we dropped the lock. */ 21 p = inode->i_cdev; 22 if (!p) { 23 inode->i_cdev = p = new; 24 list_add(&inode->i_devices, &p->list); 25 new = NULL; 26 } else if (!cdev_get(p)) 27 ret = -ENXIO; 28 } else if (!cdev_get(p)) 29 ret = -ENXIO; 30 spin_unlock(&cdev_lock); 31 cdev_put(new); 32 if (ret) 33 return ret; 34 35 ret = -ENXIO; 36 fops = fops_get(p->ops); 37 if (!fops) 38 goto out_cdev_put; 39 40 replace_fops(filp, fops);//綁定驅動的fops到filp上 41 if (filp->f_op->open) { 42 ret = filp->f_op->open(inode, filp);//調用驅動的open 43 if (ret) 44 goto out_cdev_put; 45 } 46 47 return 0; 48 49 out_cdev_put: 50 cdev_put(p); 51 return ret; 52 }
4.sys_read代碼
5.vfs_read代碼
[todo]
6.cdev_add調用kobj_map
字符設備結構體cdev的添加步驟:
cdev初始化:cdev_init,該函數將file_operations與cdev對應起來;
向kernel添加:cdev_add,該函數將主設備號與cdev結構體對應起來,由此主設備號對應驅動程序;(設備節點對應的是文件兩碼事,ALSA字符設備)
當對open設備節點時,首先通過節點找到主設備號,然後再kernel中搜索與主設備號相對應的字符設備cdev,然後動過cdev中file_operations結構體定義的open方法(這個open是需要自己實現的);
1 int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, 2 struct module *module, kobj_probe_t *probe, 3 int (*lock)(dev_t, void *), void *data) 4 { 5 unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1; 6 unsigned index = MAJOR(dev);//取得主設備號 7 unsigned i; 8 struct probe *p; 9 10 if (n > 255) 11 n = 255; 12 13 p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL); 14 if (p == NULL) 15 return -ENOMEM; 16 17 for (i = 0; i < n; i++, p++) { 18 p->owner = module; 19 p->get = probe; 20 p->lock = lock; 21 p->dev = dev; 22 p->range = range; 23 p->data = data; 24 } 25 mutex_lock(domain->lock); 26 for (i = 0, p -= n; i < n; i++, p++, index++) { 27 struct probe **s = &domain->probes[index % 255]; 28 while (*s && (*s)->range < range) 29 s = &(*s)->next; 30 p->next = *s; 31 *s = p; 32 } 33 mutex_unlock(domain->lock); 34 return 0; 35 }
參考:
1.Multiconflictism的《Linux設備管理(二)_從cdev_add說起》,http://www.cnblogs.com/xiaojiang1025/p/6196198.html
2.cuijiyue的《主設備號--驅動模塊與設備節點聯系的紐帶》http://blog.csdn.net/cuijiyue/article/details/42066425
2017-06-08
Linux字符設備驅動