linux 内核设备管理模型sysfs(进阶篇)
《linux 内核设备管理模型sysfs(入门篇)》讲述了内核设备管理模型sysfs的基础管理单元kobject和kset,但是在实际过程中很少有驱动工程师有机会直接操作上述结构,linux在上述结构的基础上又进一步进行了高级抽象:总线bus、驱动driver、设备device,这三个重要的抽象。
一般在处理器中 要与外设进行打交到就要通过各自总线与外界设备链接起来,经常用到的总结比如pci、usb、I2c等结构,各自总线被按照逻辑抽象到bus结构中,在sys/bus中可以看到各种常用的总线:
就好比一个usb鼠标,通过usb总线与CPU进行链接,与抽象到的bus类型为usb进行打交到,但是只有bus驱动是不够的,因为每个总线上可以接各种不同类型的设备,比如usb上即可以链接usb 鼠标还可以链接键盘、U盘等,每种类型的设备都需要各种的驱动,所以linux将相同类型的设备驱动抽象成driver,比如鼠标就有属于自己专门的driver,键盘有自己的专门driver。
如下图所示 为笔者usb总线driver下面的driver:
但是bus总线和driver驱动还是不不够,因为一个总线上有可能接多个相同的设备,比如一台电脑接两个鼠标或者键盘,那么每个鼠标都需要有一个自己的device抽象,用来与另外一个鼠标进行区别,这个称之为device, device相当于书driver的实例化,两个鼠标分别被实例化为mouse1 和mouse 2两个device,以达到两个鼠标互不干扰的目的。
下图为笔者 usb device设备,每个设备都是通过软件来链接到device目录,和/sys/devices内的目录进行链接,实际上/sys/devices为实际上所有设备所出的目录位置。
总线BUS
linux设备管理模型在kset更高一个层级,将每个总线抽象成bus_type结构,在linux 5.4.56版本中,其主要结构如下(定义位于include\linux\device.h文件中):
struct bus_type {const char *name;const char *dev_name;struct device *dev_root;const struct attribute_group **bus_groups;const struct attribute_group **dev_groups;const struct attribute_group **drv_groups;int (*match)(struct device *dev, struct device_driver *drv);int (*uevent)(struct device *dev, struct kobj_uevent_env *env);int (*probe)(struct device *dev);int (*remove)(struct device *dev);void (*shutdown)(struct device *dev);int (*online)(struct device *dev);int (*offline)(struct device *dev);int (*suspend)(struct device *dev, pm_message_t state);int (*resume)(struct device *dev);int (*num_vf)(struct device *dev);int (*dma_configure)(struct device *dev);const struct dev_pm_ops *pm;const struct iommu_ops *iommu_ops;struct subsys_private *p;struct lock_class_key lock_key;bool need_parent_lock;
};
- bus_type结构是所有位于sys/bus一级目录的结构抽象,整个bus实现位于drivers\base\bus.c文件中,几个比较重要的-结构:
- name: 该bus名称,比如usb等
- dev_name: 用于在该子系统内的device name,和devic id结合在一起用于命名该bus中多个device .
- dev_root: 被用作默认device的parent
- bus_groups:默认bus属性。
- dev_groups: 在该bus上的device默认属性。
- drv_groups: 在该bus上的driver默认属性
- match:bus中发现一个新的device是,调用match 是否与某个device或者driver匹配
- uevent: 添加删除等时间
- probe: 当一个新的device或者driver加入到bus中,用于探测该设备是否存在
- remove: 移除设备处理
- shutdwn: 关闭 一个device
- online: 设备上线时会调用该回调
- offline: 设备下线
- suspend: 设备被挂起 处于sleep模式
- resume: 从sleep中进行唤醒
- struct subsys_private *p:每个bus的私有数据,里面包含了该bus下所有的device和driver, 主要数据如下:
struct subsys_private {struct kset subsys;struct kset *devices_kset;struct list_head interfaces;struct mutex mutex;struct kset *drivers_kset;struct klist klist_devices;struct klist klist_drivers;struct blocking_notifier_head bus_notifier;unsigned int drivers_autoprobe:1;struct bus_type *bus;struct kset glue_dirs;struct class *class;
};
- struct kset subsys:为该bus下所有的总线的集合,
- struct klist klist_devices: 该bus下所有device集合通过链表串联起来
- struct klist klist_drivers:该bus下所有driver集合
- struct bus_type:类似与kobjece中的kobj_type,相同类型bus。
BUS API
linux kernel bus模块提供了一些基本的接口供驱动开发人员使用:
API | 作用及说明 |
nt bus_register(struct bus_type *bus) | 注册一个bus |
void bus_unregister(struct bus_type *bus) | 去注册一个bus |
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) | 为该bus 创建一个bus属性 |
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) | 为该bus中移除掉一个bus属性 |
struct klist *bus_get_device_klist(struct bus_type *bus) | 获取到该bus ket |
struct klist *bus_get_device_klist(struct bus_type *bus) | 获取该bus中所有的device |
struct device *bus_find_device(struct bus_type *bus, struct device *start, const void *data, int (*match)(struct device *dev, const void *data)) | 查找该bus中某个device |
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb) | 注册该bus中notdifer |
int bus_unregister_notifier(struct bus_type *bus, struct notifier_block *nb) | 去注册该bus中notdifer |
int bus_rescan_devices(struct bus_type *bus) | 重新对该bus中的设备进行扫描 |
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *)) | 遍历该bus内的driver |
创建Mybus用例
通过Mybus用例来更加了解bus的工作原理以及总线驱动的一些更深层次的理解,创建一个名为虚拟的Mybus 总线,其创建的总线与usb、pci等总线并行,调用bus_register进行注册,用例示范代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static struct bus_type my_bus_type= {.name = "mybus",.match = my_match,.uevent = my_uevent,
};static int __init bus_test_init(void)
{int err;err = bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus test init failed\n");return err;}return 0;
}static void __exit bus_test_exit(void)
{bus_unregister(&my_bus_type);
}module_init(bus_test_init);
module_exit(bus_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for bus test");
MODULE_VERSION("V1.0");
用例中创建一个static struct bus_type结构my_bus_type,然后调用bus_register进行注册,创建一个新的总线名为mybus,运行结果如下:
可以看到如上图所示创建一个名为mybus的总线与其他总线并列,进一步查看mybus目录,发现默认会创建如下结构:
其中devices和drivers类型分别为kset,对应的是struct subsys_private *p结构中的struct klist klist_devices和 struct klist klist_drivers,可以分别查看该总线上的所有的device和drivers。
bus_attribute
bus_attribute是基于attribute的一个简单封装,提供show和store 回调, 类似一与kobj_attribute结构,定义如下:
struct bus_attribute {struct attribute attr;ssize_t (*show)(struct bus_type *bus, char *buf);ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
创建bus的属性有两种方法 一种是通过bus_type在创建是指定bus_groups 创建默认attribute,另外一种是通过bus_create_file创建非默认。
默认bus_attribute创建用例
使用默认attribute创建mybus 时 指定两个属性分别为para1和para2 , 用例代码如下:
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static ssize_t para1_show(struct bus_type *bus, char *buf)
{return snprintf(buf, PAGE_SIZE, "para1 test\n");
}BUS_ATTR_RO(para1);
static ssize_t para2_show(struct bus_type *bus, char *buf)
{return snprintf(buf, PAGE_SIZE, "para1 test\n");
}BUS_ATTR_RO(para2);static struct attribute *mybus_para_attrs[]={&bus_attr_para1.attr,&bus_attr_para2.attr,NULL,
};
ATTRIBUTE_GROUPS(mybus_para);static struct bus_type my_bus_type= {.name = "mybus",.match = my_match,.uevent = my_uevent,.bus_groups = mybus_para_groups
};
static int __init bus_test_init(void)
{int err;err = bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus register failed\n");return err;}return 0;
}static void __exit bus_test_exit(void)
{bus_unregister(&my_bus_type);
}module_init(bus_test_init);
module_exit(bus_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for bus test");
MODULE_VERSION("V1.0");
运行结果如下:
创建非默认bus_attribute
使用bus_create_file()创建mybus非默认bus_attribute 名字分别为descr1和descr2,代码示例如下:
include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static struct bus_type my_bus_type= {.name = "mybus",.match = my_match,.uevent = my_uevent,
};static ssize_t descr1_show(struct bus_type *bus, char *buf)
{return snprintf(buf, PAGE_SIZE, "mybus descr 1\n");
}BUS_ATTR_RO(descr1);
static ssize_t descr2_show(struct bus_type *bus, char *buf)
{return snprintf(buf, PAGE_SIZE, "mybus descr 2\n");
}BUS_ATTR_RO(descr2);
static int __init bus_test_init(void)
{int err;err = bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus register failed\n");return err;}err = bus_create_file(&my_bus_type, &bus_attr_descr1);if (err){printk(KERN_INFO "bus creat descr1 failed\n");return err;}err = bus_create_file(&my_bus_type, &bus_attr_descr2);if (err){printk(KERN_INFO "bus creat descr2 failed\n");return err;}return 0;
}static void __exit bus_test_exit(void)
{bus_remove_file(&my_bus_type, &bus_attr_descr1);bus_remove_file(&my_bus_type, &bus_attr_descr2);bus_unregister(&my_bus_type);
}
module_init(bus_test_init);
module_exit(bus_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for bus test");
MODULE_VERSION("V1.0");
运行结果如下:
驱动driver
从一个CPU角度来将,CPU与各种外设都是通过各种总线进行打交道,一个设备可能使用到多种总线类型,仅仅用于bus结构还不足以支撑整个系统运转,在一个总线上会有各种各样的设备,就需要不同类型的驱动,对于linux 内核驱动而言,一个设备的驱动一定会归属于某个总线,没有一个驱动是没有相关总线的,内核将总线上的驱动抽象为device_driver 结构,在5.4.56内核版本种该结构如下:
struct device_driver {const char *name;struct bus_type *bus;struct module *owner;const char *mod_name; /* used for built-in modules */bool suppress_bind_attrs; /* disables bind/unbind via sysfs */enum probe_type probe_type;const struct of_device_id *of_match_table;const struct acpi_device_id *acpi_match_table;int (*probe) (struct device *dev);int (*remove) (struct device *dev);void (*shutdown) (struct device *dev);int (*suspend) (struct device *dev, pm_message_t state);int (*resume) (struct device *dev);const struct attribute_group **groups;const struct attribute_group **dev_groups;const struct dev_pm_ops *pm;void (*coredump) (struct device *dev);struct driver_private *p;
};
- name: driver name
- bus: 该驱动归属的bus总线
- owner:驱动所属的module.
- mod_name: 用于在 built-in module模块名
- suppress_bind_attrs: 是否需要通过sysfs文件系统进行绑定
- probe: 用于探测设备是否存在。
- remove:设备移除
- shutdown:关闭 设备
- suspend:挂起设备使之处于sleep状态
- resume: 从sleep状态种唤醒
- groups:默认属性
- dev_groups:一旦一个设备实例化 将会将该属性绑定到该设备种
- struct driver_private *p:私有数据,数据类型如下:
struct driver_private {struct kobject kobj;struct klist klist_devices;struct klist_node knode_bus;struct module_kobject *mkobj;struct device_driver *driver;
};
- struct kobject kobj: 因为同一种设备类型只有一个驱动即可 故device_driver数据结构为基于kobject进行抽象即可
- struct klist klist_devices:该驱动有多少个具体的device实例化
- struct device_driver *driver: 该私有数据对应的para device_driver,主要是为了根据kobj 通过contain_of查找到driver_private,然后查找到所属的device_driver结构
device_driver API
在内核驱动模块种drivers\base\driver.c提供了对驱动使用的API,主要列表如下:
API | 作用及说明 |
int driver_register(struct device_driver *drv) | 注册一个driver |
void driver_unregister(struct device_driver *drv) | 去注册一个driver |
int driver_create_file(struct device_driver *drv, const struct driver_attribute *attr) | 为该driver创建属性文件 |
void driver_remove_file(struct device_driver *drv, const struct driver_attribute *attr) | 删除该driver属性文件 |
struct device *driver_find_device(struct device_driver *drv, struct device *start, const void *data, int (*match)(struct device *dev, const void *data)) | 在该driver内查找某个device |
int driver_for_each_device(struct device_driver *drv, struct device *start, void *data, int (*fn)(struct device *, void *)) | 遍历该driver内的所有device |
struct device_driver *driver_find(const char *name, struct bus_type *bus) | 在bus内根据name 查找driver |
device_driver用例
使用driver相关接口 在my_bus上注册一个mydriver驱动,用例代码如下:
include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static struct bus_type my_bus_type= {.name = "mybus",.match = my_match,.uevent = my_uevent,
};static struct device_driver my_driver ={.name ="my_driver",.bus = &my_bus_type,
};static int __init driver_test_init(void)
{int err;err = bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus test init failed\n");return err;}err = driver_register(&my_driver);if (err){printk(KERN_INFO "my driver register failed\n");return err;}return 0;
}static void __exit device_test_exit(void)
{driver_unregister(&my_driver);bus_unregister(&my_bus_type);
}module_init(driver_test_init);
module_exit(device_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for device test");
MODULE_VERSION("V1.0");
代码中my_driver一定要指明所在的bus,device_driver结构在sys中没有专门的目录,只能在所属的bus中查看,如果是一个单独的ko则可以在/sys/module中进行查看:
设备Deivce
device是一个驱动driver的实例化,指向具体的一个设备,是一个基于kobject的struct device结构,主要结构如下:
struct device {struct kobject kobj;struct device *parent;struct device_private *p;const char *init_name; /* initial name of the device */const struct device_type *type;struct bus_type *bus; /* type of bus device is on */struct device_driver *driver; /* which driver has allocated thisdevice */void *platform_data; /* Platform specific data, devicecore doesn't touch it */void *driver_data; /* Driver data, set and get withdev_set_drvdata/dev_get_drvdata */
#ifdef CONFIG_PROVE_LOCKINGstruct mutex lockdep_mutex;
#endifstruct mutex mutex; /* mutex to synchronize calls to* its driver.*/struct dev_links_info links;struct dev_pm_info power;struct dev_pm_domain *pm_domain;#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAINstruct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRLstruct dev_pin_info *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQstruct list_head msi_list;
#endifconst struct dma_map_ops *dma_ops;u64 *dma_mask; /* dma mask (if dma'able device) */u64 coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations such descriptors. */u64 bus_dma_mask; /* upstream dma_mask constraint */unsigned long dma_pfn_offset;struct device_dma_parameters *dma_parms;struct list_head dma_pools; /* dma pools (if dma'ble) */#ifdef CONFIG_DMA_DECLARE_COHERENTstruct dma_coherent_mem *dma_mem; /* internal for coherent memoverride */
#endif
#ifdef CONFIG_DMA_CMAstruct cma *cma_area; /* contiguous memory area for dmaallocations */
#endif/* arch specific additions */struct dev_archdata archdata;struct device_node *of_node; /* associated device tree node */struct fwnode_handle *fwnode; /* firmware device node */#ifdef CONFIG_NUMAint numa_node; /* NUMA node this device is close to */
#endifdev_t devt; /* dev_t, creates the sysfs "dev" */u32 id; /* device instance */spinlock_t devres_lock;struct list_head devres_head;struct class *class;const struct attribute_group **groups; /* optional groups */void (*release)(struct device *dev);struct iommu_group *iommu_group;struct iommu_fwspec *iommu_fwspec;struct iommu_param *iommu_param;bool offline_disabled:1;bool offline:1;bool of_node_reused:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)bool dma_coherent:1;
#endif
};
主要结构成员:
- struct kobject kobj:内嵌的kobject结构。
- struct device *parent:指向父设备,有时为 bus or host controllor控制器,如果为NULL则为处于top设备。
- const char *init_name:该设备名称。
- struct bus_type *bus:该设备所属于的bus总线
- struct device_private *p:该设备的私有数据成员,主要数据内容如下:
struct device_private {struct klist klist_children;struct klist_node knode_parent;struct klist_node knode_driver;struct klist_node knode_bus;struct klist_node knode_class;struct list_head deferred_probe;struct device_driver *async_driver;struct device *device;u8 dead:1;
};
- knode_bus:所属bus。
- knode_class:所属设备类型。
- struct klist klist_children:如果该设备有子设备 ,则含有所有的子设备。
- struct klist_node knode_parent:所属的父设备。
device API
device主要实现位于drivers\base\core.c中:
API | 主要作用说明 |
int device_register(struct device *dev) | 注册一个设备 |
void device_unregister(struct device *dev) | 去注册一个设备 |
void device_initialize(struct device *dev) | 初始化一个设备 |
int device_add(struct device *dev) | 添加一个设备 |
void device_del(struct device *dev) | 删除一个设备 |
int device_add_groups(struct device *dev, const struct attribute_group **groups) | 添加设备属性组 |
void device_remove_groups(struct device *dev, const struct attribute_group **groups) | 移除一个设备组 |
struct device *device_create_with_groups(struct class *class, struct device *parent, dev_t devt, void *drvdata, const struct attribute_group **groups, const char *fmt, ...) |
创建一个设备 并使用groups初始化 |
void device_destroy(struct class *class, dev_t devt) | destory一个设备 |
struct device *device_create_vargs(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, va_list args) |
创建一个设备 |
创建MyDevice用例
使用上述API 创建一个Mydeice用例:
include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static struct device my_device ={.init_name ="my_device",
};static int __init device_test_init(void)
{int err;err = device_register(&my_device);if (err){printk(KERN_INFO "my device register failed\n");return err;}return 0;
}static void __exit device_test_exit(void)
{device_unregister(&my_device);
}module_init(device_test_init);
module_exit(device_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for device test");
MODULE_VERSION("V1.0");
创建所有的device都位于sys/devices目录
MyBus、MyDriver、MyDeivce
bus_type是CPU对外总线类型bus的抽象,CPU一个外设必然是通过各种总线打交道,一般而言总线级别的驱动实现基于bus_type,例如pci在此基础上进行了封装pci驱动头文件位于\include\linux\pci.h文件中该文件为pci对外提供的pci驱动接口,具体实现每个芯片略有差异。总线之外外挂各种类型的设备,需要在总线之上实现各种类型设备的驱动,一般会在 device_driver层进行封装实现。 device为各种类型设备的实例化相当于device_driver的实例化。
在上述用例中使用MyBus来模拟一个bus类型的总线,MyDriver为基于MyBus上的一个设备驱动,MyDevice为MyDriver驱动的一个具体的设备,这样可以更加理解linux驱动模型,整体结构如下:
对于linux 驱动模型 一般而言总线驱动实现需要提供register,attribute等接口以便当一个新的设备类型时,方便基于该总线进行新设备的driver驱动开发,创建mybus层 其数据结构为my_bus_type。在mybus上的驱动封装成struct my_driver,再次基础上实现mydriver驱动实现,myDevice基于device 进行了封装my_device。整个linux 驱动要实现是模型思路就是如此。
用例代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static int my_match(struct device * dev, struct device_driver * driver)
{printk(KERN_INFO "my bus match dev name %s, driver name %s\n",dev_name(dev), driver->name);return !strncmp(dev_name(dev),driver->name, strlen(driver->name));
}static int my_uevent(struct device *dev, struct kobj_uevent_env *env)
{printk(KERN_INFO "my bus uevent dev name %s\n",dev_name(dev));add_uevent_var(env, "DEV_NAME=%s", dev_name(dev));return 0;
}static struct bus_type my_bus_type= {.name = "mybus",.match = my_match,.uevent = my_uevent,
};static struct device_driver my_driver ={.name ="my_driver",.bus = &my_bus_type,
};static struct device my_device ={.init_name ="my_device",.bus =&my_bus_type,.driver =&my_driver};static int __init driver_test_init(void)
{int err;err = bus_register(&my_bus_type);if (err){printk(KERN_INFO "bus test init failed\n");return err;}err = driver_register(&my_driver);if (err){printk(KERN_INFO "my driver register failed\n");return err;}err = device_register(&my_device);if (err){printk(KERN_INFO "my device register failed\n");return err;}return 0;
}
static void __exit device_test_exit(void)
{device_unregister(&my_device);driver_unregister(&my_driver);bus_unregister(&my_bus_type);
}module_init(driver_test_init);
module_exit(device_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for device test");
MODULE_VERSION("V1.0");
运行模块记载完成之后,在sys/mybus的组织拓扑结构如下:
mydevice设备是一个通过虚拟的mybus总线结构打交道,故mybus/devices目录下软链接到my_device形式,同时mydevice是基于my_driver的实例化,故再my_driver目录下同样会软链接到device下。
总结
总线,驱动和设备是linux设备模型中比较关键的模型,虽然在如今大多数系统中不需要驱动工作者去真正创建一个总线,但是了解该模型能够更好理解linux中一个驱动如何工作的,不仅要知其然,还要知其所以然。以上主要是通过一个个具体实例来了解整个模型,如果还要具体了解可以学习《linux 设备驱动程序》这本书。
类class
类是设备模型中的最后一个概念,是基于device的高层抽象,将设备的底层实现细节进行抽象。通常而言对于SCSI磁盘或者ATA磁盘等设备而言,在类的层次而言都是磁盘而已,类将多种设备类型中相同的功能进行抽象,可以减少代码重复率,然后设备再基于此类进行实例化。如果说bus是总线的抽象,那么类就是设备的相同功能的抽象。类允许用户空间使用设备所提供的功能,而不关心设备是如何连接的以及它是如何工作的。
几乎所有的类都显示在/sys/class目录中。举例子说明,不管是网络接口的类型是什么,所有的网络接口都集中在/sys/class /net中。输入设备 都可以在/sys/class/input下找到。
设备驱动模型中将类都抽象成strut class结构:
struct class {const char *name;struct module *owner;const struct attribute_group **class_groups;const struct attribute_group **dev_groups;struct kobject *dev_kobj;int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);char *(*devnode)(struct device *dev, umode_t *mode);void (*class_release)(struct class *class);void (*dev_release)(struct device *dev);int (*shutdown_pre)(struct device *dev);const struct kobj_ns_type_operations *ns_type;const void *(*namespace)(struct device *dev);void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);const struct dev_pm_ops *pm;struct subsys_private *p;
};
- name:为该类名称
- owner:为该类所属module
- class_groups:为该类默认属性
- dev_groups:为对应的设备属性
- dev_kobj:代表该class的kobj
- 一些回调:相应的回调动作
- subsys_private *p:对应类类的私有结构,与bus_type一样。
class用例
类使用的一个简单用例:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static struct class my_class = {.name = "myclass",
};static int __init class_test_init(void)
{int err;err = class_register(&my_class);if (err){printk(KERN_INFO "class test init failed\n");return err;}return 0;
}static void __exit class_test_exit(void)
{class_unregister(&my_class);
}module_init(class_test_init);
module_exit(class_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for class test");
MODULE_VERSION("V1.0");
运行结果/sys/class目录产生一个新类:
创建基于Myclass的device用例
创建一个基于Myclass的Mydevice用例:
include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/device.h>
MODULE_LICENSE("GPL v2");static struct class my_class = {.name = "myclass",
};static struct device my_device ={.init_name ="my_device",.class =&my_class,
};static int __init class_test_init(void)
{int err;err = class_register(&my_class);if (err){printk(KERN_INFO "class test init failed\n");return err;}err = device_register(&my_device);if (err){printk(KERN_INFO "my device register failed\n");return err;}return 0;
}static void __exit class_test_exit(void)
{device_unregister(&my_device);class_unregister(&my_class);
}module_init(class_test_init);
module_exit(class_test_exit);
MODULE_AUTHOR("hzk");
MODULE_DESCRIPTION("A simple moudule for class test");
MODULE_VERSION("V1.0");
运行结果如下:
linux 内核设备管理模型sysfs(进阶篇)相关推荐
- linux 内核设备管理模型sysfs(入门篇)
sysfs是linux 内核从2.6中开始引入的设备管理模型,该模型通过一个虚拟的文件系统sysfs 将整个硬件的层级模型显现和组织起来,并将其采用矩阵式管理方式,还是支持热插拔等功能,该设备模型自从 ...
- linux 内核模型,The Linux Kernel Device Model - Overview -- Linux 内核设备模型概述
--------------------------------------------------------------------------------------------------- ...
- 【Linux内核系列】百分百干货篇丨不要错过,5个方面分析内核架构,让你对内核不再陌生
5个方面分析内核架构,让你对内核不再陌生 1. 内存管理 mm_struct 2. 进程管理 task_struct 3. 网络协议栈 sk_buff 4. 文件系统 super_block 5. 设 ...
- linux 内核驱动模型,linux设备驱动模型架构分析 一
linux设备驱动模型架构分析 一 发布时间:2018-07-04 15:14, 浏览次数:584 , 标签: linux 概述 LDD3中说:"Linux内核需要一个对系统结构的一般性描述 ...
- 和菜鸟一起学linux内核源码之启动篇
又是一个周末,日子过得比较散,虽然期间也有不断地看书学习,总觉得有点小盲目.想想毕业也快要1年了,从事嵌入式linux的研发工作也1年多了.这1年多的从实习到正式工作到现在的自己,进步有,也很大,但是 ...
- linux内核基本模型,Linux设备模型(1)_基本概念
Linux设备模型(1)_基本概念 作者:wowo 发布于:2014-2-27 17:01 分类:统一设备模型 1. 前言 在"Linux内核的整体架构"中,蜗蜗有提到,由于Lin ...
- 学习linux内核-- 内存,看一篇就够了(多图)
内存模块 1 linux内存总体布局:内存分成用户态和内核态 4G进程地址空间解析 内核地址空间 进程地址空间 2 地址转换和页表 2.1 地址转换 虚拟内存是指程序使用的逻辑地址.每个进程4G.所有 ...
- linux内核设备管理典型算法,linux内核物理存储空间管理有哪些常用算法
Linux进程调度 kill命令 当需要中断一个前台进程的时候,通常是使用< Ctrl+c >组合键:但是对于一个后台进程恐怕就不是一个组合键所能解决的了,这时就必须求助于kill命令.该 ...
- Linux基础之bash脚本进阶篇-循环语句(for,while,until)
20160909 补充break与continue的区别 什么是循环语句.死循环? 循环语句:将一段代码重复执行0.1或多次. 到底要重复运行多少次?以及我们如何设定循环语句的重复次数? 为了解决上面 ...
最新文章
- anago 围棋_跳进我的碗里—关于中惒围棋加盟那些事
- [Spark][Flume]Flume 启动例子
- Node.js用6行代码1个JS文件搭建一个HTTP静态服务器
- zend studio mysql 配置_php 在Zend Framework中配置数据库参数
- 在普通java类里获取Spring管理的bean
- android seekbar 圆角,android - 最小或最大时,圆角android seekbar的ui奇怪行为 - 堆栈内存溢出...
- Eclipse/NSight: methond could not resolved
- 使用iPhone系统设置开发者,进行弱网测试
- JAVA300集速学堂高淇个人笔记P1-P7如何学习JAVA300集计算机语言的发展历史多种编程语言的介绍JAVA三大版本的含义:
- win10html网页运行空白,win10系统Ie浏览器无法打开HTML格式的网页文件的处理秒方...
- Wannacry 勒索病毒有预设的解密口令“WNcry@2ol7”么?
- java http心跳_MQTT协议笔记之连接和心跳
- 正则表达式-注册表验证
- linux 静态编译多媒体框架,Go编译32位GNU静态链接库的方法
- MySQL Study之--Mysql数据库备份工具(mysqldump)
- <四>关于flv格式解析
- knewton适应性学习白皮书(2)
- c#读取CSV格式文件
- python画图小游戏课程设计
- N-Case 律师事务所管理系统功能介绍