1. 程式人生 > >Linux字符設備驅動

Linux字符設備驅動

case 結構 完全 .net 實例 som main node ont

本文詳細介紹字符設備驅動,使用linux-4.8.2版本代碼。

1.綜述:從註冊到open、read/write

  1. 申請設備號;
  2. 註冊cdev到cdev_map:cdev_init和cdev_add;
  3. 創建設備節點:mknod(手動)或者udev(自動,借助熱插拔和sysfs,推薦);
  4. 用戶空間通過路徑open到內核空間對應的設備節點,內核空間中(VFS、filp和cdev_map):chrdev_open根據設備節點中的設備號(此時該節點的cdev還是NULL),找到cdev實例,並賦值到設備節點的成員cdev變量上,最後把cdev的ops賦值到filp->f_op,並調用filp->f_op->open函數;
  5. 用戶空間通過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字符設備驅動