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