多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > [置頂] 圖解linux char驅動

[置頂] 圖解linux char驅動

來源:程序員人生   發布時間:2015-01-06 09:04:05 閱讀次數:3308次

                                                                             圖解Linux char 驅動

 主題:                                                                                                                                                                               
   1.字符驅動模型。
   2.字符裝備的裝備號。
   3.文件系統中對字符裝備的訪問。

   1:字符裝備框架
 
        寫的驅動程序只是1個程序,
        然后將底層硬件驅動抽象出來用1個結構體來表示cdev。
        那末我們需要做的就是從用戶空間寫的open、write、read等系統調用來操作硬件,
        通過系統調用進入內核,而內核的實現就是利用了1切皆文件的思想,
       就是上層直接操作文件但實質底層就是在操作硬件,
     這個轉換就是VFS。
   
  2:大概的進程:
    大概進程就是說open(“/dev/first_drv”,..)
    會進入內核->創建1個file結構體->該結構體指向->inode
    ->通過主裝備號查找到cdev結構(即是驅動)->找到cdev指向的操作函數指針->找到.open->自己的open函數。
   
  3:下面是相干的詳細說明底層如何實現:
  相干數據結構:

25 struct cdev { 26 struct kobject kobj; 27 struct module *owner; 28 const struct file_operations *ops; 29 struct list_head list; 30 dev_t dev; 31 unsigned int count; 32 }; 33 34 struct kobj_map { 35 36 struct probe { 37 38 struct probe *next; 39 dev_t dev; 40 unsigned long range; 41 struct module *owner; 42 kobj_probe_t *get; 43 int (*lock)(dev_t, void *); 44 void *data; 45 } *probes[255]; 46 struct mutex *lock; 47 }; 48 49 static struct char_device_struct { 50 51 struct char_device_struct *next; 52 unsigned int major; 53 unsigned int baseminor; 54 int minorct; 55 char name[64]; 56 struct file_operations *fops; 57 struct cdev *cdev; /* will die */ 58 } *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; 59 60 #define CHRDEV_MAJOR_HASH_SIZE 255


 1.字符裝備驅動模型。
    每個字符設驅動有1個cdev結構提表示。
    再裝備驅動模型那個(device driver model)中。使用(kobject mapping domain)
    來記錄字符裝備文件驅動。這是由struct kobj_map結構體來表示。
    它內嵌了255個struct probe指針數組。kobj_map由全局變量cdev_map援用。
    static struct kobj_map *cdev_map; 這個全局變量的定義在char_dev.c中。

70 /------------- 71 | cdev_map | 72 -------------/ 73 | 74 | /------> probe /------> probe 75 | | +---------+ | +---------+ /------- 76 | | | *next |----/ | *next |----->| NULL | 77 --->kobj_map | +---------+ +---------+ -------/ 78 +-------------------+ | | dev | | dev | 79 | *probes[255] | | +---------+ +---------+ 80 | +-----------+ | | | range | | range | 81 | | *probe | | | +---------+ +---------+ 82 | +-----------+ | | | *owner | | *owner | 83 | | *probe |---------/ +---------+ +---------+ 84 | +-----------+ | | *get | | *get | 85 | | .... | | +---------+ +---------+ 86 | +-----------+ | | *lock | | *lock | 87 | | *probe | | +---------+ +---------+ 88 | +-----------+ | | *data |------ | *data |------ 89 | | *probe |--------- +---------+ | +---------+ | 90 | +-----------+ | | | | 91 +------------------- | | | 92 | | | 93 | | | 94 | /------- | | 95 ----->| NULL | | | 96 -------/ | | 97 | | 98 cdev<-----------/ cdev<----/ 99 +---------+ +---------+ 100 | *kobj | | *kobj | 101 +---------+ +---------+ 102 | *owner | | *owner | 103 +---------+ +---------+ 104 | *ops | | *ops | 105 +---------+ +---------+ 106 | *list | | *list | 107 +---------+ +---------+ 108 | dev | | dev | 109 +---------+ +---------+ 110 | *count | | *count | 111 +---------+ +---------+


1:cdev_add()函數詳解。

1.

  1般會使用kzalloc(size,GFP_KERNEL)給cdev分配1塊空間,然后初始化好,ops對應的結構體。  

2.
  再使用cdev_init把ops函數操作集和cdev綁定起來。
 
3.
   使用cdev_add() 用來將cdev對象添加到驅動模型中,其主要是通過kobj_map()來實現的.
kobj_map() 會創建1個probe對象,然后將其插入cdev_map中的某1項中,并關聯probe->data 指向 cdev

 4.

   struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index)
根據裝備號,在cdev_map中查找其cdev對象內嵌的kobject. (probe->data->kobj),返回的是cdev的kobject

int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, 133 struct module *module, kobj_probe_t *probe, 134 int (*lock)(dev_t, void *), void *data) 135 { 136 137 unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1; 138 unsigned index = MAJOR(dev); 139 unsigned i; 140 struct probe *p; 141 142 if (n > 255) 143 n = 255; 144 //為prob結構體分配空間。 145 p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL); 146 if (p == NULL) 147 return -ENOMEM; 148 149 //給結構體賦值。 150 for (i = 0; i < n; i++, p++) { 151 p->owner = module; 152 p->get = probe; 153 p->lock = lock; 154 p->dev = dev; 155 p->range = range; 156 p->data = data; 157 } 158 mutex_lock(domain->lock); 159 160 //把分配的結構體probe寄存到該hash表中。結構如上。 161 for (i = 0, p -= n; i < n; i++, p++, index++) { 162 struct probe **s = &domain->probes[index % 255]; 163 while (*s && (*s)->range < range) 164 s = &(*s)->next; 165 p->next = *s; 166 *s = p; 167 } 168 mutex_unlock(domain->lock); 169 return 0; 170 }



 2:字符裝備的裝備號。


174 0 1 2 253 254 175 +--------+--------+--------+--------+--------+-------- 176 cdevs | | | | .... | | | 177 +--------+--------+--------+--------+--------+-------- 178 | | | | 179 | | | | 180 | | | | 181 /------- | | | | 182 | NULL |<--/ | | | 183 -------/ | | | /------- 184 | | ------>| NULL | 185 | | -------/ 186 /-----------------/ /------- 187 | | NULL | 188 | -------/ 189 | 190 ------>char_devices_struct /----> char_devices_struct 191 +----------------+ | +----------------+ /------- 192 | *next |--------/ | *next |------>| NULL | 193 +----------------+ +----------------+ -------/ 194 | major | | major | 195 +----------------+ +----------------+ 196 | vaseminor | | vaseminor | 197 +----------------+ +----------------+ 198 | *name[64] | | *name[64] | 199 +----------------+ +----------------+ 200 | *fops | | *fops | 201 +----------------+ +----------------+ 202 | *cdev | | *cdev | 203 +----------------+ +----------------+

 字符裝備的主,次裝備號的分配:

 1.
    全局數組 chrdevs 包括了255(CHRDEV_MAJOR_HASH_SIZE 的值)個struct char_device_struct的元素.
 每個對應1個相應的主裝備號.

2.
  分配了1個裝備號,就會創建1個 struct char_device_struct 的對象,
并將其添加到chrdevs中.這樣,通過chrdevs數組,我們就能夠知道分配了哪些裝備號.

 相干函數:
 1.  register_chrdev_region( ) 分配指定的裝備號范圍
  2.alloc_chrdev_region( ) 動態分配裝備范圍他們都主要是通過調用函數__register_chrdev_region() 來實現的
      要注意,這兩個函數僅僅是注冊裝備號! 如果要和cdev關聯起來,還要調用cdev_add()

   register_chrdev( ) 申請指定的裝備號,并且將其注冊到字符裝備驅動模型中.
  它所做的事情為:
   1. 注冊裝備號, 通過調用 __register_chrdev_region() 來實現
   2. 分配1個cdev, 通過調用 cdev_init()來實現
   3. 將cdev添加到驅動模型中,這1步將裝備號和驅動關聯了起來.通過調用 cdev_add() 來實現
   4. 將第1步中創建的 struct char_device_struct 對象的 cdev 指向第2步中分配的cdev. 由于register_chrdev()是老的接口,
       這1步在新的接口中其實不需要.

236 /-------------------------------------->cdev<------------------------------------ 237 | +---------+ | 238 | | *kobj | | 239 | +---------+ | 240 | | *owner | | 241 | +---------+ | 242 | | *ops | | 243 | +---------+ | 244 | /----------------------------->| *list |<--------------------------- | 245 | | +---------+ | | 246 | | | dev | | | 247 | | +---------+ | | 248 | | | *count | | | 249 | | +---------+ | | 250 | | | | 251 | | inode inode | | 252 | | +---------+ +---------+ | | 253 | | | i_rdev | | i_rdev | | | 254 | | +---------+ +---------+ | | 255 | ----------|i_devices|<----------.........--------->|i_devices|<------/ | 256 | +---------+ +---------+ | 257 --------------- |*i_cdev | |*i_cdev |------------/ 258 +---------+ +---------+ 259 |i_cindex | |i_cindex | 260 +---------+ +---------+ 261 | ..... | | ..... | 262 +---------+ +---------+

 

系統調用open打開1個字符裝備的時候, 通過1系列調用,終究會履行到 chrdev_open.
    (終究是通過調用到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 這1系列的調用進程,本文暫不討論)

    int chrdev_open(struct inode * inode, struct file * filp)

     chrdev_open()所做的事情可以概括以下:
   1. 根據裝備號(inode->i_rdev), 在字符裝備驅動模型中查找對應的驅動程序,
        這通過kobj_lookup() 來實現, kobj_lookup()會返回對應驅動程序cdev的kobject.   
    2. 設置inode->i_cdev , 指向找到的cdev  
    3. 將inode添加到cdev->list的鏈表中.
    4. 使用cdev的ops 設置file對象的f_op
    5. 如果ops中定義了open方法,則調用該open方法
    6. 返回.
  履行完chrdev_open()以后,file對象的f_op指向cdev的ops,因此以后對裝備進行的read, write等操作,就會履行cdev的相應操作.




285 static int chrdev_open(struct inode *inode, struct file *filp) 286 { 287 288 struct cdev *p; 289 struct cdev *new = NULL; 290 int ret = 0; 291 292 spin_lock(&cdev_lock); 293 p = inode->i_cdev; 294 if (!p) { 295 struct kobject *kobj; 296 int idx; 297 spin_unlock(&cdev_lock); 298 299 // 1. 根據裝備號(inode->i_rdev), 在字符裝備驅動模型中查找對應的驅動程序, 300 //這通過kobj_lookup() 來實現, kobj_lookup()會返回對應驅動程序cdev的kobject. 301 302 kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); 303 if (!kobj) 304 return -ENXIO; 305 new = container_of(kobj, struct cdev, kobj); 306 spin_lock(&cdev_lock); 307 /* Check i_cdev again in case somebody beat us to it while 308 we dropped the lock. */ 309 310 //2. 設置inode->i_cdev , 指向找到的cdev. 311 p = inode->i_cdev; 312 if (!p) { 313 inode->i_cdev = p = new; 314 315 //3. 將inode添加到cdev->list的鏈表中. 316 list_add(&inode->i_devices, &p->list); 317 318 new = NULL; 319 } else if (!cdev_get(p)) 320 ret = -ENXIO; 321 } else if (!cdev_get(p)) 322 ret = -ENXIO; 323 spin_unlock(&cdev_lock); 324 cdev_put(new); 325 if (ret) 326 return ret; 327 328 ret = -ENXIO; 329 330 //4. 使用cdev的ops 設置file對象的f_op 331 filp->f_op = fops_get(p->ops); 332 if (!filp->f_op) 333 goto out_cdev_put; 334 335 if (filp->f_op->open) { 336 5. 如果ops中定義了open方法,則調用該open方法 337 ret = filp->f_op->open(inode, filp); 338 if (ret) 339 goto out_cdev_put; 340 } 341 342 return 0; }


 
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 国产主播福利 | 国内小情侣一二三区在线视频 | 欧美国产成人精品一区二区三区 | 极品福利视频 | 日本不卡视频一区二区 | 宇都宫紫苑乳在线观看 | 三级视频在线看 | 国产精品一区二区三区高清在线 | yellow日本| 国产性色视频 | 噜噜噜噜私人影院老湿在线观看 | 国产美女久久久亚洲 | 久久天天躁狠狠躁夜夜 | 欧美啊啊 | 久久综合一区二区三区 | 最新亚洲人成网站在线影院 | 日本欧美高清 | 可以看黄的网址 | 欧美精欧美乱码一二三四区 | 一区二区三区四区无限乱码 | 免费看www网站入口 免费看w片的网站在线看 | 2018生活片性色生活片 | a4yy私人毛片| 激情影院在线视频永久观看 | 在线视频中文 | 猫咪www免费人成网站 | 欧美精品黑人性xxxx | 国产成人影院在线观看 | 看性过程三级视频在线观看 | 日韩一区二区三区四区 | 在线精品小视频 | 欧美性色视频 | 午夜免费啪在线观看视频网站 | 日本高清在线看 | 在线人成精品免费视频 | 一本大道香蕉久在线不卡视频 | 国产精品欧美在线不卡 | 最色网址| 国产精品自拍在线观看 | 亚洲天堂中文字幕在线观看 | 日本www在线 |