大概步驟:
一、 注冊(cè)設(shè)備號(hào)
注冊(cè)函數(shù):
? ?regsiter_chrdev_region()
? ?alloc_chrdev_region() 或 ? ? ? ? ? ? ? ? ? ? ? ? ? ? 查看#cat /proc/devices
??? ? register_chrdev()
? ? ? ? 注銷函數(shù):
? ? ? ? ? ? unregist_chrdev_region() 或
? ? ? ? ? ? unregister_chrdev()
? ??
二、初始化cdev并添加到系統(tǒng)
初始化cdev
? ? ? ? ? ? 靜態(tài)初始化 cdev_init() 或
? ? ? ? ? ? 動(dòng)態(tài)初始化 cdev_alloc()
? ? ? ? 添加到系統(tǒng)函數(shù)
? ? ? ? ? ? cdev_add()
? ? ? ? 從系統(tǒng)刪除函數(shù)
? ? ? ? ? ? cdev_del()
? ? ? ? ? ??
三、創(chuàng)建設(shè)備節(jié)點(diǎn)
創(chuàng)建類
? ? ? ? ? ? class_create() ? ? ? ? ?將放于/sysfs ? ? ? ? ? ? ? ? ? ?查看#ls /sys/class
? ? ? ? 刪除類
? ? ? ? ? ? class_destroy()
? ? ? ??
? ? ? ? 創(chuàng)建節(jié)點(diǎn)
? ? ? ? ? ? device_create() 或 class_device_create() ?將存放于/dev ?查看#ls /dev
? ? ? ? 刪除節(jié)點(diǎn)
? ? ? ? ? ? device_destroy() 或 class_device_destroy()
? ??
/***************************************************************************************************
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?第一步:注冊(cè)設(shè)備號(hào)
***************************************************************************************************/
Linux內(nèi)核中所有已分配的字符設(shè)備編號(hào)都記錄在一個(gè)名為 chrdevs 散列表里。
? ? 該散列表中的每一個(gè)元素是一個(gè) char_device_struct 結(jié)構(gòu),它的定義如下:
? ? static struct char_device_struct
? ? {
? ? ? ? struct char_device_struct *next; ? ?// 指向散列沖突鏈表中的下一個(gè)元素的指針
? ? ? ? unsigned ? ?int major; ? ? ? ? ? ? ?// 主設(shè)備號(hào)
? ? ? ? unsigned ? ?int baseminor; ? ? ? ? ?// 起始次設(shè)備號(hào)
? ? ? ? int minorct; ? ? ? ? ? ? ? ? ? ? ? ?// 設(shè)備編號(hào)的范圍大小
? ? ? ? char ? ?name[64]; ? ? ? ? ? ? ? ? ? // 處理該設(shè)備編號(hào)范圍內(nèi)的設(shè)備驅(qū)動(dòng)的名稱
? ? ? ? struct file_operations *fops; ? ? ? // 沒有使用
? ? ? ? struct cdev *cdev; ? ? ? ? ? ? ? ? ?// 指向字符設(shè)備驅(qū)動(dòng)程序描述符的指針
? ? }*chrdevs[CHRDEV_MAJOR_HASH_SIZE];
? ? 1 每一個(gè)主設(shè)備有一個(gè)會(huì)分配一個(gè)此結(jié)構(gòu),可以有多個(gè)次設(shè)備號(hào)。次設(shè)備是依次遞增的。
? ? 2 內(nèi)核提供了5個(gè)函數(shù)來來管理字符設(shè)備編號(hào)。
? ??
? ? ? ? ? ? register_chrdev_region() ? ? ? ?指定初始值
? ? ? ? ? ? alloc_chrdev_region() ? ? ? ? ? 動(dòng)態(tài)分配
? ? ? ? ? ? register_chrdev() ? ? ? ? ? ? ? 指定設(shè)備號(hào)
? ? ? ? 他們都會(huì)調(diào)用 __register_chrdev_region() 來注冊(cè)一組設(shè)備編號(hào)范圍(一個(gè)char_device_struct結(jié)構(gòu)),我們使用其中一個(gè)即可。
? ? ? ? ? ??
? ? ? ? ? ? unregist_chrdev_region() ? ? ? ?釋放都用此函數(shù)
? ? ? ? ? ? unregister_chrdev() ? ? ? ? ? ? 都調(diào)用了 __unregister_chrdev_region() 來注銷設(shè)備
? ? ? ? 注冊(cè):
? ? ? ? ? ? register_chrdev_region(dev_t first,unsigned int count,char *name)
? ? ? ? ? ? ? ? first :要分配的設(shè)備編號(hào)范圍的初始值(次設(shè)備號(hào)常設(shè)為0);
? ? ? ? ? ? ? ? count :連續(xù)編號(hào)范圍.
? ? ? ? ? ? ? ? Name ?:編號(hào)相關(guān)聯(lián)的設(shè)備名稱. (/proc/devices);
? ? ? ? ? ? int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
? ? ? ? ? ? ? ? *dev ? ? ? ?:存放返回的設(shè)備號(hào)
? ? ? ? ? ? ? ? firstminor ?:第一個(gè)次設(shè)備號(hào)的號(hào)數(shù),常為0;
? ? ? ? ? ? int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
? ? ? ? ? ? ? ? major :要注冊(cè)的設(shè)備號(hào), 若為0則自動(dòng)分配一個(gè)
? ? ? ? ? ? ? ? name ?:設(shè)備名
? ? ? ? ? ? ? ? *fops :以后再聊
? ? ? ? 釋放:
? ? ? ? ? ? void unregister_chrdev(unsigned int major, const char *name);
? ? ? ? ? ? void unregister_chrdev_region(dev_t from, unsigned count);
? ? 3 示例:略
? ? 4 參考:感謝原著 (有此6個(gè)函數(shù)的源碼及解說)。
? ? ? ? http://blog.csdn.net/iLetLet/article/details/6180314
/***************************************************************************************************
? ? ? ? ? ? ? ? ? ? ? ? ? ? 第二步:初始化 cdev 并添加到系統(tǒng)
***************************************************************************************************/
1.內(nèi)核中每個(gè)字符設(shè)備都對(duì)應(yīng)一個(gè) cdev 結(jié)構(gòu)的變量,定義如下:
? ? linux-2.6.22/include/linux/cdev.h
? ? struct cdev?
? ? {
? ? ? ? struct kobject kobj; ? ? ? ? ? ? ? ?//每個(gè) cdev 都是一個(gè) kobject
? ? ? ? struct module *owner; ? ? ? ? ? ? ? //指向?qū)崿F(xiàn)驅(qū)動(dòng)的模塊
? ? ? ? const struct file_operations *ops; ?//操縱這個(gè)字符設(shè)備文件的方法
? ? ? ? struct list_head list; ? ? ? ? ? ? ?//與 cdev 對(duì)應(yīng)的字符設(shè)備文件的 inode->i_devices 的鏈表頭
? ? ? ? dev_t dev; ? ? ? ? ? ? ? ? ? ? ? ? ?//起始設(shè)備編號(hào)
? ? ? ? unsigned int count; ? ? ? ? ? ? ? ? //設(shè)備范圍號(hào)大小
? ? };
2. 初始化cdev :有兩種定義初始化方式:
? ??
? ? 方式1:靜態(tài)內(nèi)存定義初始化:
? ? ? ? struct cdev my_cdev;
? ? ? ? cdev_init(&my_cdev, &fops);
? ? ? ? my_cdev.owner = THIS_MODULE;
? ? ? ??
? ? 方式2:動(dòng)態(tài)內(nèi)存定義初始化:
? ? ? ? struct cdev *my_cdev = cdev_alloc();
? ? ? ? my_cdev->ops = &fops;
? ? ? ? my_cdev->owner = THIS_MODULE;
? ? 下面是2函數(shù)的具體代碼:
? ? ? ? struct cdev *cdev_alloc(void) ? ? ? //它主要完成了空間的申請(qǐng)和簡單的初始化操作;
? ? ? ? {
? ? ? ? ? ? struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
? ? ? ? ? ? if (p)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? INIT_LIST_HEAD(&p->list);
? ? ? ? ? ? ? ? kobject_init(&p->kobj, &ktype_cdev_dynamic);
? ? ? ? ? ? }
? ? ? ? ? ? return p;
? ? ? ? }
? ??
? ? ? ? void cdev_init(struct cdev *cdev, const struct file_operations *fops)
? ? ? ? { ??
? ? ? ? ? ? memset(cdev, 0, sizeof *cdev); ?//主要是對(duì)空間起到一個(gè)清零作用并較之cdev_alloc多了一個(gè)ops的賦值操作
? ? ? ? ? ? INIT_LIST_HEAD(&cdev->list);
? ? ? ? ? ? kobject_init(&cdev->kobj, &ktype_cdev_default);
? ? ? ? ? ? cdev->ops = fops;
? ? ? ? }
3. 添加cdev到系統(tǒng)
? ? 為此可以調(diào)用 cdev_add() 函數(shù)。傳入cdev結(jié)構(gòu)的指針,起始設(shè)備編號(hào),以及設(shè)備編號(hào)范圍。
? ? int cdev_add(struct cdev *p, dev_t dev, unsigned count)
? ? {
? ? ? ? p->dev = dev;
? ? ? ? p->count = count;
? ? ? ? return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
? ? }
? ? 釋放時(shí)使用 cdev_del()函數(shù)來釋放cdev占用的內(nèi)存。
? ? void cdev_del(struct cdev *p)
? ? {
? ? ? ? cdev_unmap(p->dev, p->count); ? //釋放 cdev_map 散列表中的對(duì)象
? ? ? ? kobject_put(&p->kobj); ? ? ? ? ?//釋放 cdev 結(jié)構(gòu)本身。
? ? }
4.關(guān)于kobject_init() kobj_map()
? ? 內(nèi)核中所有都字符設(shè)備都會(huì)記錄在一個(gè) kobj_map 結(jié)構(gòu)的 cdev_map 變量中。
? ? 這個(gè)結(jié)構(gòu)的變量中包含一個(gè)散列表用來快速存取所有的對(duì)象。
? ? kobj_map() 函數(shù)就是用來把字符設(shè)備編號(hào)和 cdev 結(jié)構(gòu)變量一起保存到 cdev_map 這個(gè)散列表里。
? ? 當(dāng)后續(xù)要打開一個(gè)字符設(shè)備文件時(shí),通過調(diào)用 kobj_lookup() ?函數(shù),根據(jù)設(shè)備編號(hào)就可以找到 cdev 結(jié)構(gòu)變量,從而取出其中的 ops 字段。
/***************************************************************************************************
? ? ? ? ? ? ? ? ? ? ? ? ? ? 第三步:創(chuàng)建設(shè)備節(jié)點(diǎn)
***************************************************************************************************/
? ? 方法一:利用mknod命令手動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn)。
? ? 方法二:實(shí)際上Linux內(nèi)核為我們提供了一組函數(shù),可以在模塊加載的時(shí)候在/dev目錄下創(chuàng)建相應(yīng)設(shè)備節(jié)點(diǎn),在卸載時(shí)可刪除該節(jié)點(diǎn)。
? ? 原理:
? ? ? ? 1 內(nèi)核中定義了struct class結(jié)構(gòu)體,它對(duì)應(yīng)一個(gè)類。
? ? ? ? 2 先調(diào)用class_create()函數(shù),可以用它來創(chuàng)建一個(gè)類,這個(gè)類將存放于sysfs下面.
? ? ? ? 3 再調(diào)用device_create()函數(shù),從而在/dev目錄下創(chuàng)建相應(yīng)的設(shè)備節(jié)點(diǎn)。
? ? ? ? 4 卸載模塊對(duì)應(yīng)的函數(shù)是 device_destroy 和 class_destroy()
? ? ? ? 注:2.6 以后的版本使用device_create(),之前的版本使用的class_device_create()。
? ? 詳解:
? ? ? ? 1:class結(jié)構(gòu):
? ? ? ? ? ? include/linux/device.h
? ? ? ? ? ? struct class
? ? ? ? ? ? {
? ? ? ? ? ? ? ? const ? char ? ? ? ?*name;
? ? ? ? ? ? ? ? struct module ? ? ? *owner;
? ? ? ? ? ? ? ? struct kset ? ? ? ? subsys;
? ? ? ? ? ? ? ? struct list_head ? ?devices;
? ? ? ? ? ? ? ? struct list_head ? ?interfaces;
? ? ? ? ? ? ? ? struct kset ? ? ? ? class_dirs;
? ? ? ? ? ? ? ? struct semaphore sem; ? ? ? /* locks ? ?children, devices, interfaces */
? ? ? ? ? ? ? ? struct class_attribute ?*class_attrs;
? ? ? ? ? ? ? ? struct device_attribute *dev_attrs;
? ??
? ? ? ? ? ? ? ? int ? ?(*dev_uevent) ? (struct device *dev,struct kobj_uevent_env *env);
? ? ? ? ? ? ? ? void ? (*class_release)(struct class *class);
? ? ? ? ? ? ? ? void ? (*dev_release) ?(struct device ? *dev);
? ? ? ? ? ? ? ? int ? ?(*suspend) ? ? ?(struct ?device *dev, pm_message_t state);
? ? ? ? ? ? ? ? int ? ?(*resume) ? ? ? (struct device *dev);
? ? ? ? ? ? };
? ? ? ? 2:class_create()?
? ? ? ? ? ? class_create()在/drivers/base/class.c中實(shí)現(xiàn):
? ? ? ? ? ? struct class ? ?*class_create(struct module *owner, // ?指定類的所有者是哪個(gè)模塊
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *name) ? ? // ?指定類名
? ? ? ? ? ? {
? ? ? ? ? ? ? ? struct class *cls;
? ? ? ? ? ? ? ? int retval;
? ? ? ? ? ? ? ? cls = ? kzalloc(sizeof(*cls), GFP_KERNEL);
? ? ? ? ? ? ? ? if (!cls)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? retval = ? ?-ENOMEM;
? ? ? ? ? ? ? ? ? ? goto ? ?error;
? ? ? ? ? ? ? ? }
? ? ? ??
? ? ? ? ? ? ? ? cls->name ? = name;
? ? ? ? ? ? ? ? cls->owner = owner;
? ? ? ? ? ? ? ? cls->class_release = class_create_release;
? ? ? ??
? ? ? ? ? ? ? ? retval = class_register(cls);
? ? ? ? ? ? ? ? if (retval)
? ? ? ? ? ? ? ? ? ?goto error;
? ? ? ? ? ? ? ? return cls;
? ? ? ? ? ? ? ? error:
? ? ? ? ? ? ? ? ? ? kfree(cls);
? ? ? ? ? ? ? ? ? ? return ERR_PTR(retval);
? ? ? ? ? ? }
? ? ? ? ? ??
? ? ? ? 3:device_create()函數(shù)在/drivers/base/core.c中實(shí)現(xiàn):
? ? ? ? ? ? struct device *device_create(struct class *class, ? //指定所要?jiǎng)?chuàng)建的設(shè)備所從屬的類
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct devicev *parent, //這個(gè)設(shè)備的父設(shè)備,如果沒有就指定為NULL
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dev_t devt, ? ? ? ? ? ? //設(shè)備號(hào)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char *fmt, ? ? ? ?//設(shè)備名稱
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ...) ? ? ? ? ? ? ? ? ? ?//從設(shè)備號(hào)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? va_list vargs;
? ? ? ? ? ? ? ? struct ?device *dev;
? ? ? ? ? ? ? ? va_start(vargs, fmt);
? ? ? ? ? ? ? ? dev = device_create_vargs(class, parent, devt, ?NULL, fmt, vargs);
? ? ? ? ? ? ? ? va_end(vargs);
? ? ? ? ? ? ? ? return ?dev;
? ? ? ? ? ? }
?
評(píng)論
查看更多