生活随笔
收集整理的這篇文章主要介紹了
linux设备模型之Class
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
版權聲明:歡迎轉載,轉載請注明出處 http://blog.csdn.net/lizuobin2/
? ? 參考:http://www.wowotech.net/device_model/class.html
? ? 剛開始寫字符設備驅動程序的時候,老師教我們自動創建設備節點,“要先創建類,在類下面創建設備,類名字不重要“。
? ? ? ? firstdrv_class = class_create(THIS_MODULE, "firstdrv");
? ? ? ? firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
? ? 于是乎,這兩行代碼被糊里糊涂的復制粘貼了好多次,差點成為一種習慣~ ? ? ? ?
? ? 前面分析設備總線驅動模型的時候,我們知道,將一個設備調用 device_add 函數注冊到內核中去的時候,如果指定了設備號,那么用戶空間的 mdev 會根據 sysfs 文件系統中的設備信息去自動創建設備節點。我們看到前面第二行代碼里有一個?device_create ,參數里還有設備號 ,八九不離十,里邊也間接調用了device_add ,不信一會分析代碼。
? ? 類是一個設備的高層視圖,它抽象出了低層的實現細節,大概意思就是抽象出了一個通用的接口吧。常見的類設備有 Input 、tty 、usb 、rtc?等等。
? ? class 就好比 bus ,我們在設備總線驅動模型中創建設備時,要指定它所屬的 Bus ,那么在創建類設備的時候也需要指定它所從屬的類,class 也離不開 Kobject ,因此如果你了解總線設備驅動模型,你就會發現,其實真的都是差不多的東西。
struct?class?{?? ????const?char??????*name;?? ????struct?module???????*owner;?? ????struct?class_attribute??????*class_attrs;?? ????struct?device_attribute?????*dev_attrs;?? ????struct?kobject??????????*dev_kobj;?? ????int?(*dev_uevent)(struct?device?*dev,?struct?kobj_uevent_env?*env);?? ????char?*(*devnode)(struct?device?*dev,?mode_t?*mode);?? ????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);?? ????const?struct?dev_pm_ops?*pm;?? ????struct?class_private?*p;?? };??
? ? name,class的名稱,會在“/sys/class/”目錄下體現。
? ??class_atrrs,該class的默認attribute,會在class注冊到內核時,自動在“/sys/class/xxx_class”下創建對應的attribute文件。
? ??dev_attrs,該class下每個設備的attribute,會在設備注冊到內核時,自動在該設備的sysfs目錄下創建對應的attribute文件。
? ??dev_bin_attrs,類似dev_attrs,只不過是二進制類型attribute。
? ??dev_kobj,表示該class下的設備在/sys/dev/下的目錄,現在一般有char和block兩個,如果dev_kobj為NULL,則默認選擇char。
? ??dev_uevent,當該class下有設備發生變化時,會調用class的uevent回調函數。
? ??class_release,用于release自身的回調函數。
? ??dev_release,用于release class內設備的回調函數。在device_release接口中,會依次檢查Device、Device Type以及Device所在的class,是否注冊release接口,如果有則調用相應的release接口release設備指針。
struct?class_private?{?? ????struct?kset?class_subsys;?? ????struct?klist?class_devices;?? ????struct?list_head?class_interfaces;?? ????struct?kset?class_dirs;?? ????struct?mutex?class_mutex;?? ????struct?class?*class;?? };?? ? ? struct class_interface是這樣的一個結構:它允許class driver在class下有設備添加或移除的時候,調用預先設置好的回調函數(add_dev和remove_dev)。那調用它們做什么呢?想做什么都行(例如修改設備的名稱),由具體的class driver實現。
? ? 該結構的定義如下:
struct?class_interface?{?? ????struct?list_head????????node;?? ????struct?class????????????*class;?? ?? ????int?(*add_dev)??????????(struct?device?*,?struct?class_interface?*);?? ????void?(*remove_dev)??????(struct?device?*,?struct?class_interface?*);?? };?? ? ? 下面,我們來看 Class 的注冊過程,前面我們提到,class->name 會出現在/sys/class 目錄下,那么這個目錄是哪里來的,代碼一看便知。
int?__init?classes_init(void)?? {?? ????class_kset?=?kset_create_and_add("class",?NULL,?NULL);?? ????if?(!class_kset)?? ????????return?-ENOMEM;?? ????return?0;?? }?? ? ? 下面,我們來看一下一個Class 的注冊過程
#define?class_create(owner,?name)???????\?? ({??????????????????????\?? ????static?struct?lock_class_key?__key;?\?? ????__class_create(owner,?name,?&__key);????\?? })?? struct?class?*__class_create(struct?module?*owner,?const?char?*name,?? ?????????????????struct?lock_class_key?*key)?? {?? ????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,?key);?? ????if?(retval)?? ????????goto?error;?? ?? ????return?cls;?? ?? error:?? ????kfree(cls);?? ????return?ERR_PTR(retval);?? }?? ? ? 在 class_create 函數中,只是簡單構造了一個class結構體,設置了名字以及所屬的模塊,然后調用 class_register?
int?__class_register(struct?class?*cls,?struct?lock_class_key?*key)?? {?? ????struct?class_private?*cp;?? ????int?error;?? ?? ????pr_debug("device?class?'%s':?registering\n",?cls->name);?? ?? ????cp?=?kzalloc(sizeof(*cp),?GFP_KERNEL);?? ????if?(!cp)?? ????????return?-ENOMEM;?? ????klist_init(&cp->class_devices,?klist_class_dev_get,?klist_class_dev_put);?? ????INIT_LIST_HEAD(&cp->class_interfaces);?? ????kset_init(&cp->class_dirs);?? ????__mutex_init(&cp->class_mutex,?"struct?class?mutex",?key);?? ????error?=?kobject_set_name(&cp->class_subsys.kobj,?"%s",?cls->name);?? ????if?(error)?{?? ????????kfree(cp);?? ????????return?error;?? ????}?? ?? ?????? ????if?(!cls->dev_kobj)?? ????????cls->dev_kobj?=?sysfs_dev_char_kobj;?? ?? #if?defined(CONFIG_SYSFS_DEPRECATED)?&&?defined(CONFIG_BLOCK)?? ?????? ????if?(cls?!=?&block_class)?? ????????cp->class_subsys.kobj.kset?=?class_kset;?? #else?? ????cp->class_subsys.kobj.kset?=?class_kset;?? #endif?? ????cp->class_subsys.kobj.ktype?=?&class_ktype;?? ????cp->class?=?cls;?? ????cls->p?=?cp;?? ?? ????error?=?kset_register(&cp->class_subsys);?? ????if?(error)?{?? ????????kfree(cp);?? ????????return?error;?? ????}?? ????error?=?add_class_attrs(class_get(cls));?? ????class_put(cls);?? ????return?error;?? }?? ? ? 代碼第15行,將 cp->class_subsys.kobj 的 name 設置為cls->name
? ? 代碼第28行,將cp->class_subsys.kobj.kest 設置為class_kest
? ? 代碼第36行,將cp->class_subsys 注冊進內核,沒有設置 cp->class_subsys.kobj.parent ,內核會將cp->class_subsys.kobj.kset.kobj 設置成它的Parent ,這也就是為什么說 class->name ?會出現在 /sys/class 目錄下的原因。
? ? 下面,來看向 class 注冊 device 的過程
struct?device?*device_create(struct?class?*class,?struct?device?*parent,?? ?????????????????dev_t?devt,?void?*drvdata,?const?char?*fmt,?...)?? {?? ????va_list?vargs;?? ????struct?device?*dev;?? ?? ????va_start(vargs,?fmt);?? ????dev?=?device_create_vargs(class,?parent,?devt,?drvdata,?fmt,?vargs);?? ????va_end(vargs);?? ????return?dev;?? }?? struct?device?*device_create_vargs(struct?class?*class,?struct?device?*parent,?? ???????????????????dev_t?devt,?void?*drvdata,?const?char?*fmt,?? ???????????????????va_list?args)?? {?? ????struct?device?*dev?=?NULL;?? ????int?retval?=?-ENODEV;?? ?? ????if?(class?==?NULL?||?IS_ERR(class))?? ????????goto?error;?? ?? ????dev?=?kzalloc(sizeof(*dev),?GFP_KERNEL);?? ????if?(!dev)?{?? ????????retval?=?-ENOMEM;?? ????????goto?error;?? ????}?? ?? ????dev->devt?=?devt;?? ????dev->class?=?class;?? ????dev->parent?=?parent;?? ????dev->release?=?device_create_release;?? ????dev_set_drvdata(dev,?drvdata);?? ?? ????retval?=?kobject_set_name_vargs(&dev->kobj,?fmt,?args);?? ????if?(retval)?? ????????goto?error;?? ?? ????retval?=?device_register(dev);?? ????if?(retval)?? ????????goto?error;?? ?? ????return?dev;?? ?? error:?? ????put_device(dev);?? ????return?ERR_PTR(retval);?? }?? ? ? 上邊代碼也沒有什么好分析的,與我們分析設備總線驅動模型時分析 device 時有一點不一樣的就是這里設置的是 dev->class 而不是 dev->bus ,同時這里為 dev設置了設備號 devt ,因此,在sysfs中會創建 dev 屬性文件,mdev 就會自動為我們創建設備節點了。
int?device_register(struct?device?*dev)?? {?? ????device_initialize(dev);?? ????return?device_add(dev);?? }?? int?device_add(struct?device?*dev)?? {?? ????struct?device?*parent?=?NULL;?? ????struct?class_interface?*class_intf;?? ????int?error?=?-EINVAL;?? ?? ????dev?=?get_device(dev);?? ????if?(!dev)?? ????????goto?done;?? ?? ????if?(!dev->p)?{?? ????????error?=?device_private_init(dev);?? ????????if?(error)?? ????????????goto?done;?? ????}?? ?? ????? ? ? ? ?? ????if?(dev->init_name)?{?? ????????dev_set_name(dev,?"%s",?dev->init_name);?? ????????dev->init_name?=?NULL;?? ????}?? ?? ????if?(!dev_name(dev))?? ????????goto?name_error;?? ?? ????pr_debug("device:?'%s':?%s\n",?dev_name(dev),?__func__);?? ?? ????parent?=?get_device(dev->parent);?? ????setup_parent(dev,?parent);?? ?? ?????? ????if?(parent)?? ????????set_dev_node(dev,?dev_to_node(parent));?? ?? ?????? ?????? ????error?=?kobject_add(&dev->kobj,?dev->kobj.parent,?NULL);?? ????if?(error)?? ????????goto?Error;?? ?? ?????? ????if?(platform_notify)?? ????????platform_notify(dev);?? ?? ????error?=?device_create_file(dev,?&uevent_attr);?? ????if?(error)?? ????????goto?attrError;?? ?? ????if?(MAJOR(dev->devt))?{?? ????????error?=?device_create_file(dev,?&devt_attr);?? ????????if?(error)?? ????????????goto?ueventattrError;?? ?? ????????error?=?device_create_sys_dev_entry(dev);?? ????????if?(error)?? ????????????goto?devtattrError;?? ?? ????????devtmpfs_create_node(dev);?? ????}?? ?? ????error?=?device_add_class_symlinks(dev);?? ????if?(error)?? ????????goto?SymlinkError;?? ????error?=?device_add_attrs(dev);?? ????if?(error)?? ????????goto?AttrsError;?? ????error?=?bus_add_device(dev);?? ????if?(error)?? ????????goto?BusError;?? ????error?=?dpm_sysfs_add(dev);?? ????if?(error)?? ????????goto?DPMError;?? ????device_pm_add(dev);?? ?? ????? ? ?? ????if?(dev->bus)?? ????????blocking_notifier_call_chain(&dev->bus->p->bus_notifier,?? ?????????????????????????BUS_NOTIFY_ADD_DEVICE,?dev);?? ?? ????kobject_uevent(&dev->kobj,?KOBJ_ADD);?? ????bus_probe_device(dev);?? ????if?(parent)?? ????????klist_add_tail(&dev->p->knode_parent,?? ???????????????????&parent->p->klist_children);?? ?? ????if?(dev->class)?{?? ????????mutex_lock(&dev->class->p->class_mutex);?? ?????????? ????????klist_add_tail(&dev->knode_class,?? ???????????????????&dev->class->p->class_devices);?? ?? ?????????? ????????list_for_each_entry(class_intf,?? ????????????????????&dev->class->p->class_interfaces,?node)?? ????????????if?(class_intf->add_dev)?? ????????????????class_intf->add_dev(dev,?class_intf);?? ????????mutex_unlock(&dev->class->p->class_mutex);?? ????}?? done:?? ????put_device(dev);?? ????return?error;?? ?DPMError:?? ????bus_remove_device(dev);?? ?BusError:?? ????device_remove_attrs(dev);?? ?AttrsError:?? ????device_remove_class_symlinks(dev);?? ?SymlinkError:?? ????if?(MAJOR(dev->devt))?? ????????device_remove_sys_dev_entry(dev);?? ?devtattrError:?? ????if?(MAJOR(dev->devt))?? ????????device_remove_file(dev,?&devt_attr);?? ?ueventattrError:?? ????device_remove_file(dev,?&uevent_attr);?? ?attrError:?? ????kobject_uevent(&dev->kobj,?KOBJ_REMOVE);?? ????kobject_del(&dev->kobj);?? ?Error:?? ????cleanup_device_parent(dev);?? ????if?(parent)?? ????????put_device(parent);?? name_error:?? ????kfree(dev->p);?? ????dev->p?=?NULL;?? ????goto?done;?? }?? ? ? 代碼第41行,將 dev->kobj 注冊進內核,會在/sys/devices 目錄下創建目錄。
? ? 代碼第53-63行,是創建屬性文件dev 的過程,也就是這一步,讓mdev能夠自動為我們創建設備節點。
? ? 代碼第65行,創建 /sys/class 到 /sys/device/xx/dev->name的符號鏈接,這個跟設備總線驅動模型中創建/sys/bus 到?/sys/device/xx/dev->name的符號鏈接是一樣一樣的。
? ? 代碼第92-103行,將 dev 加入 class的設備鏈表,并調用?class_interfaces 鏈表中的每一個?class_intf 結構,調用里面的?add_dev 函數。
? ? 分析到這,class 好像并沒有干什么實質性的事情。后面到input、tty、rtc在具體分析吧。
總結
以上是生活随笔為你收集整理的linux设备模型之Class的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。