总线、设备、驱动模型通用API及数据结构学习
通用api
1. bus_register
用于在 Linux 内核设备模型中注册一个总线类型。
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL); //第二个参数表示在内核空间中分配内存
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);//初始化priv结构体中的通知链,用于在内核中传递特定事件的通知
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);//设置内核对象的名称,格式为传入的总线名称
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset; //kset是内核中组织和管理kobject的集合,这里将总线的 kobject 加入到 bus_kset 集合中。
priv->subsys.kobj.ktype = &bus_ktype;//ktype定义了kobject的行为和属性
priv->drivers_autoprobe = 1;//内核自动探测并加载适合该设备的驱动
retval = kset_register(&priv->subsys);注册kset
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent);//创建属性文件,用于用户空间和内核空间之间传递总线信息
if (retval)
goto bus_uevent_fail;
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);//创建并添加一个名为devices的kset,并于并将其与 priv->subsys.kobj 关联。这个 kset 用于组织连接到该总线上的设备。
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);//创建并添加一个名为driver的kset,并于并将其与 priv->subsys.kobj 关联。这个 kset 用于组织连接到该总线上的驱动。
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces); //初始化链表头
__mutex_init(&priv->mutex, "subsys mutex", key); //初始化结构体中的互斥锁,锁名为subsys mutex,关联到key锁类键(用于锁调试和分析)
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);//该初始化的链表用于管理总线上的设备
klist_init(&priv->klist_drivers, NULL, NULL);//该初始化的链表用于管理总线上的驱动
retval = add_probe_files(bus);//在 /sys/bus 下创建drivers_probe(手动触发匹配)和drivers_autoprobe(自动匹配开关)文件
if (retval)
goto bus_probe_files_fail;
retval = bus_add_groups(bus, bus->bus_groups);//把总线的自定义属性组,批量创建到 /sys/bus/xxx/ 目录下(xxx时总线名称)
if (retval)
goto bus_groups_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_groups_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
主要作用:
kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
为 struct subsys_private 结构体分配内存。
kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
设置总线名及相关属性。
kset_register(&priv->subsys);
将总线相关的kset注册进内核设备模型中。
kset_create_and_add();
添加名为device和driver的kset用于组织连接到总线上的设备和驱动。
__mutex_init();klist_init();klist_init();
初始化锁和链表用于管理总线上的驱动与设备
subsys_private.bus_notifier 通知链,用于在内核中传递特定事件的通知。
subsys_private.subsys.kobj.kset kset是内核中组织和管理kobject的集合
subsys_private.subsys.kobj.ktype ktype定义了kobject的行为和属性
subsys_private.drivers_autoprobe = 1 表示内核自动探测并加载适合该设备的驱动
2. class_register
2.1 函数功能
用于在 Linux 内核中注册一个设备类。主要负责分配和初始化与设备类相关的数据结构,设置设备类的属性,并将其注册到内核设备模型中。
#define class_register(class) \
({ \
static struct lock_class_key __key; \
__class_register(class, &__key); \
})
int __class_register(struct class *cls, struct lock_class_key *key)
{
struct subsys_private *cp;
int error;
pr_debug("device class '%s': registering\n", cls->name);
cp = kzalloc(sizeof(*cp), GFP_KERNEL);//给subsys_private结构体分配内存空间
if (!cp)
return -ENOMEM;
klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
INIT_LIST_HEAD(&cp->interfaces);
kset_init(&cp->glue_dirs);
__mutex_init(&cp->mutex, "subsys mutex", key);
error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
if (error) {
kfree(cp);
return error;
}
/* set the default /sys/dev directory for devices of this class */
if (!cls->dev_kobj)
cls->dev_kobj = sysfs_dev_char_kobj;//设为默认设备对象表示字符设备在sysfs文件系统中的节点
#if defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (!sysfs_deprecated || cls != &block_class)
cp->subsys.kobj.kset = class_kset;
#else
cp->subsys.kobj.kset = class_kset;
#endif
cp->subsys.kobj.ktype = &class_ktype;
cp->class = cls;
cls->p = cp;
error = kset_register(&cp->subsys);
if (error) {
kfree(cp);
return error;
}
error = add_class_attrs(class_get(cls));
class_put(cls);
return error;
}
kzalloc(sizeof(*cp), GFP_KERNEL);
给subsys_private结构体分配内存空间。klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
初始化klist_devices内核链表,用于管理属于该设备类的设备,klist_class_dev_get和klist_class_dev_put函数分别用于增加和减少设备的引用计数。
kset_init(&cp->glue_dirs);
初始化结构体中的glue_dirs。kset_register(&cp->subsys);
subsys表示定义该子系统的keyset结构体,这里表示注册这个结构体。add_class_attrs(class_get(cls));
为设备类添加属性。属性是设备类在 sysfs 文件系统中暴露给用户空间的接口,通过这些属性,用户空间可以获取设备类的相关信息或对设备类进行某些操作。
__mutex_init(&cp->mutex, "subsys mutex", key);的key参数作用?
函数原型:
void
__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
key 是指向 struct lock_class_key 的指针。在程序运行过程中,每个 lock_class_key 实例在内存中都有唯一的地址。当我们为不同的锁(如不同子系统的互斥锁)分别关联不同的 lock_class_key 实例时,这些锁实际上就与不同的内存地址建立了联系。可以通过比较这些地址来区分不同的锁。

在宏封装 class_register 中,当不同驱动调用该宏时,由于宏内部定义了 static struct lock_class_key __key,编译器会为每个 class_register 宏调用中的 __key 单独分配内存空间。这些分配的内存地址是唯一的,即使在不同驱动场景下,各个生成的 __key 实例都具有独立的地址。
通过这种方式,为设备类注册过程中涉及的锁操作(如在 __class_register 函数内 cp->mutex 互斥锁的操作)分配了不同的标识地址,内核锁调试机制利用这些不同地址来区分不同设备类注册相关的锁,进而对其进行跟踪和调试。
3. device_add
第一部分

第二部分

第三部分


devtmpfs 是 “访问通道”,让应用能打开设备读写;sysfs 是 “管理面板”,让用户态能查询、配置、监控设备与内核模型。二者缺一不可,分别承担 I/O 访问与设备模型管理的核心职责。
第四部分

static int add_device(SLMP_INFO *info)
{
info->next_device = NULL;//初始化链表指针
info->line = synclinkmp_device_count;//设置新设备的行号
sprintf(info->device_name,"ttySLM%dp%d",info->adapter_num,info->port_num);//根据适配器编号和端口编号生成设备名称
if (info->line < MAX_DEVICES) {
if (maxframe[info->line])
info->max_frame_size = maxframe[info->line];//设备最大帧大小,其值是固定的
}
synclinkmp_device_count++;//更新设备计数
if ( !synclinkmp_device_list )
synclinkmp_device_list = info;//将设备添加到链表中,表示系统中新增加了一个设备
else {
SLMP_INFO *current_dev = synclinkmp_device_list;
while( current_dev->next_device )
current_dev = current_dev->next_device;
current_dev->next_device = info;
}
if ( info->max_frame_size < 4096 )
info->max_frame_size = 4096;
else if ( info->max_frame_size > 65535 )
info->max_frame_size = 65535; //调整最大帧大小
printk( "SyncLink MultiPort %s: "
"Mem=(%08x %08X %08x %08X) IRQ=%d MaxFrameSize=%u\n",
info->device_name,
info->phys_sca_base,
info->phys_memory_base,
info->phys_statctrl_base,
info->phys_lcr_base,
info->irq_level,
info->max_frame_size );
#if SYNCLINK_GENERIC_HDLC
return hdlcdev_init(info);//执行HDLC初始化操作
#else
return 0;
#endif
}
add_device 函数的主要作用是向设备列表中添加一个新的设备信息,并对设备的一些属性进行初始化和调整,同时根据条件可能会调用 hdlcdev_init 函数进行进一步的设备初始化操作。
3.1 什么是HDLC(High - Level Data Link Control,高级数据链路控制)?
是一种面向比特的数据链路层协议,用于在网络节点之间可靠地传输数据帧。它提供了一种标准的方法来封装数据、检测和纠正传输错误,并管理数据链路的流量。
hdlcdev_init();
static int hdlcdev_init(struct mgsl_struct *info)
{
int rc;
struct net_device *dev;
hdlc_device *hdlc;
/* allocate and initialize network and HDLC layer objects */
dev = alloc_hdlcdev(info);//为hdlc设备分配内存并初始化
if (!dev) {
printk(KERN_ERR "%s:hdlc device allocation failure\n",__FILE__);
return -ENOMEM;
}
/* 网络层设置 */
dev->base_addr = info->io_base;
dev->irq = info->irq_level;
dev->dma = info->dma_level;
/* 设置网络层操作回调函数为hdlcdev_ops。定义了网络层对HDLC设备的操作方式 */
dev->netdev_ops = &hdlcdev_ops;
dev->watchdog_timeo = 10 * HZ;
dev->tx_queue_len = 50;
hdlc = dev_to_hdlc(dev);//net_device中获取对应的hdlc_device结构体指针hdlc
hdlc->attach = hdlcdev_attach;
hdlc->xmit = hdlcdev_xmit;
rc = register_hdlc_device(dev);//注册初始化好的设备到系统中
if (rc) {
printk(KERN_WARNING "%s:unable to register hdlc device\n",__FILE__);
free_netdev(dev);//注册失败释放之前分配的网络设备资源
return rc;
}
info->netdev = dev;
return 0;
}
4. driver_remove_groups/driver_add_groups
driver_remove_groups();
->sysfs_remove_group();

driver_add_groups();
->sysfs_create_group();
->internal_create_group();//在指定的kobject下创建一个属性组(attribute group), 也就 是在sysf文件系统中生成对应的目录和属性文件

5. bus_remove_driver
这只是个入口,在这个入口里会调用你自己实现的platform_driver.remove ,达到将驱动从总线上注销的目的,不同的总线调用不同的实现。
6. driver_register

数据结构
1.bus_type
某一类总线的全局行为模板,对于其中的回调函数,总线层只做统一调度、校验、上锁、转发,操作寄存器控制硬件的永远是设备对应的驱动。

name:总线名称, /sys/bus/bus_name 。
dev_name:总线下设备默认前缀名。
dev_root:该总线在sysfs里的设备挂载根目录。
dev_attrs:/* use dev_groups instead */
bus_groups:总线本身的默认sysfs属性文件组。
dev_groups:该总线下所有设备默认自带的属性文件。
drv_groups:该总线下所有驱动默认自带的属性文件。
match:设备与驱动匹配的唯一原则,为新设备寻找驱动。
uevent:向用户态发送热插拔事件,实现自动创建节点、加载规则。
probe:匹配成功后总线级的探测初始化,最终还是调用驱动的probe。
remove:设备卸载、驱动移除时的清理回调,与probe操作相反。
shutdown:总线级的系统关机、设备断电时的收尾工作,内核会按顺序对总线上的所有设备调用这个回调函数,再去调用不同驱动对应的shutdown。相较于remove,shutdown不销毁对象。
online:控制设备逻辑上电。
offline:控制设备逻辑离线,用于热插拔、故障隔离。与online一样获取dev的私有数据操作寄存器。
suspend:系统休眠时总线级电源回调。
resume:系统唤醒时总线级电源回调,最终还是会调用驱动中的resume。
pm:(PowerMangment)完整的扩展电源操作集合参考 Documentation/power/runtime_pm.txt。
iommu_ops:iommu_ops 是总线级别的 DMA 地址映射操。
p:子系统私有内核内部数据。
lock_key:锁类,给总线锁分配唯一的类别标识,lock_key只是给lockdep(LockDepend)(内核锁检测工具)看的。lockdep会根据锁类追踪内核中所有锁的上锁解锁顺序,用于内核锁调试、死锁检测。
2.platform_device

name:设备名称,驱动程序根据这个名称来判断是否支持该设备。
id:设备id,用于在同一类型的多个设备中唯一标识一个设备。
id_auto:指示设备id是否自动分配。
dev:继承自Linux内核的struct device,使得平台设备能够融入内核的通用设备管理框架。
num_resources:表示设备所拥有的资源数量。
resource:指向一个 struct resource 结构体数组,每个数组元素描述了设备的一种资源。
id_entry:指向一个平台设备 ID 结构体,用于更精确的驱动匹配。
driver_override:强制指定驱动名称进行匹配。
mfd_cell:MFD 是一种包含多个功能模块的设备,通过这个指针可以将平台设备与 MFD 中的特定单元相关联,便于管理和驱动 MFD 设备中的各个功能模块。
archdata:这个结构体提供了一个地方来存放特定硬件的设备数据。
3.platform_driver

probe:驱动与设备匹配成功后调用该函数。
remove:与probe内容相反。
shutdown:系统关机时执行该函数用于保存设备状态信息、关闭设备电源,不同于remove的销毁设备。
suspend:系统睡眠或设备挂起时调用,state表示挂起状态。bus_type中的suspend最终会调用这里的suspend具体实现。这里是对应不同设备的驱动。
resume:从suspend状态恢复时调用。
driver:通过他将平台驱动融入到Linux内核中的struct device_driver,使得平台设备驱动能融入到内核统一的设备驱动管理框架中。与platform_device.dev作用相同。
id_table:指向平台设备的id表,除了设备名称匹配以外进行更精确的匹配。
prevent_deferred_probe:用于控制是否阻止延迟探测。在某些情况下,设备可能依赖于其他设备或资源的初始化完成后才能进行探测。如果 prevent_deferred_probe 设置为 true,则驱动程序不会进行延迟探测,而是立即尝试探测设备;如果设置为 false,则允许内核在适当的时候进行延迟探测。
4.file_operations
面向应用的接口。

owner:指向拥有该文件操作集合的模块。通过他内核能确保在模块卸载的时候没有正在操作这些文件操作的文件。防止出现悬空指针的错误。
llseek:用于设置文件当前的读写位置,struct file 是文件的内核表示,loff_t 参数指定要设置的偏移量值,int 参数指定偏移量的参考点(例如,从文件开头、当前位置或文件末尾开始偏移)。返回值为新的偏移量,如果操作失败则返回负值。
read:从文件中读取数据到用户空间缓冲区。struct file 表示要读取的文件,char __user * 是用户空间缓冲区的指针,size_t 是要读取的字节数,loff_t * 指向当前文件偏移量。函数返回实际读取的字节数,如果遇到错误则返回负值。
write:将用户空间缓冲区的数据写入文件。参数与read类似。
read_iter:使用struct kiob和struct iov_iter结构体更好的处理分散在多个内存区域的数据读取。
write_iter:使用两个结构体进行写入操作。
iterate:用于遍历目录文件,将目录中的条目填充到dir_context结构体中,如果成功遍历完目录,返回 0;如果遇到错误或提前结束,返回非零值。
iterate_shared:用于多个进程共享目录遍历的情况下,确保遍历的一致性。
poll:用于检测文件状态,例如可读、可写或有异常发生。
unlocked_ioctl:执行设备特定的控制操作。unsigned int 是命令码,指示要执行的具体操作,unsigned long 是操作的参数。这个函数在非锁环境下执行,用于处理不涉及自旋锁等锁机制的控制操作。返回值表示操作的结果,0 表示成功,非零表示失败。
compat_ioctl:与 unlocked_ioctl 类似,但用于处理与用户空间的兼容性问题,特别是在 32 位和 64 位系统之间的交互。一些旧的用户空间程序可能使用不同的 ioctl 命令编码,compat_ioctl 函数可以处理这些兼容性情况。
mmap:将文件映射到用户空间的虚拟内存区域,struct vm_area_struct描述了要映射的虚拟内存区域的属性。
open:打开文件时调用,在此函数中可以执行打开文件的初始化操作,struct file 是文件的内核表示保存文件的打开状态信息比如偏移量、读写模式,跟着打开的文件描述符,关闭就销毁。struct inode保存文件静态元数据,硬盘上有就存在。
flush:在文件关闭前调用,用于文件相关的缓存数据,确保数据已被写入持久存储。fl_owner_t id 用于表示文件的拥有者。返回 0 表示成功,非零表示失败。
release:文件所有引用都被释放(文件关闭)时调用,进行清理操作,如释放文件打开时分配的资源、关闭设备等。返回 0 表示成功,非零表示失败。
fsync:用于将文件的缓存数据同步到持久存储。loff_t 参数指定要同步的文件范围(如果为 0 则表示整个文件),int datasync 标志指示是否只同步数据(而不包括元数据)。返回 0 表示成功,非零表示失败。
fasync:用于处理异步 I/O 通知。当文件的异步 I/O 模式发生变化时调用,例如从非异步模式切换到异步模式,或者反之。参数用于指示操作类型和文件状态。返回 0 表示成功,非零表示失败。
lock:用于对文件进行加锁操作,以实现文件的并发访问控制。int 参数指定锁的类型(如读锁、写锁),struct file_lock * 包含锁的具体信息。返回 0 表示成功,非零表示失败。
sendpage:用于将文件的内容发送到一个页面(struct page),常用于网络设备驱动中,将文件数据发送到网络。返回发送的字节数或错误码。
get_unmapped_area:用于在用户空间的虚拟地址空间中找到一块未映射的区域,通常用于内存映射操作。参数用于指定搜索的范围和对齐要求等。返回找到的未映射区域的起始地址,如果找不到合适的区域则返回负值。
check_flags:检查并可能修改文件的标志(如 O_RDONLY、O_WRONLY 等)。int 参数是传入的文件标志,函数返回修改后的标志或错误码。
flock:与 lock 类似,用于文件锁操作,但使用 flock 风格的锁。int 参数指定锁的类型,struct file_lock * 包含锁的信息。返回 0 表示成功,非零表示失败。
splice_write:将管道中的数据写入文件。struct pipe_inode_info 表示管道,struct file 是目标文件,loff_t * 是文件偏移量,size_t 是要写入的字节数,unsigned int 包含一些操作标志。返回写入的字节数或错误码。
splice_read:从文件中读取数据并写入管道。参数含义与 splice_write 类似,只是数据流动方向相反。返回读取的字节数或错误码。
setlease:用于设置文件的租赁(lease),这在一些文件系统中用于处理文件锁和文件访问的时间限制等问题。long 参数指定租赁的类型和期限。返回 0 表示成功,非零表示失败。
fallocate:用于预分配文件的空间。int mode 表示分配模式(如直接分配、间接分配等),loff_t offset 和 loff_t len 分别指定分配的起始偏移量和长度。返回 0 表示成功,非零表示失败。
show_fdinfo:用于在 /proc 文件系统中显示文件描述符的信息。struct seq_file 用于格式化输出信息,struct file * 是要显示信息的文件。
mmap_capabilities:在不支持 MMU(内存管理单元)的系统中,用于描述文件的内存映射能力。返回一个掩码,指示文件支持的内存映射特性。
copy_file_range:在两个文件之间直接复制数据,而不需要通过用户空间缓冲区。struct file * 分别是源文件和目标文件,loff_t 参数指定源文件和目标文件的偏移量,size_t 是要复制的字节数,unsigned int 包含操作标志。返回复制的字节数或错误码。
clone_file_range:用于克隆文件范围,返回 0 表示成功,非零表示失败。
dedupe_file_range:用于对文件范围进行去重操作,在一些支持数据去重的文件系统中使用。返回去重操作的结果(如去重的字节数)或错误码。
5.key_set

list:内核标准的双向链表头,用来挂载、管理该kest下属的所有kobject内核对象。
list_lock:保护上面的链表并发访问的自旋锁。
kobj:kset自身内嵌的内核基础对象,让kset本身也可以像kobject一样作为一个独立的节点接入sysfs目录树。是该 kset 内嵌的 kobject(递归结构)。
uevent_ops:热插拔事件回调函数集合。是该 kset 对应的 uevent 操作集。每当某个 kobject 发生相关事件时便会调用这些操作。
kset是组织相关的kobject的集合。
kset 如何实现管理多个 kobject ?

通过 kobjetc.entry 这个成员将相关的 kobject 插入到管理其的kset的list中。同时kobject.parent会指向kset.kobj,使得kset.kobj成为管理所有kobject的父对象。
6.subsys_private
管理总线子系统私有数据的结构体。

subsys:这是一个内核对象集合(kset),作为整个总线子系统在sysfs文件系统中的根节点。
devices_kset:
interfaces:这是一个链表头,用于链接与该总线相关的接口。例如,当总线上的设备添加或移除时,可以遍历这个链表来通知相关接口执行相应的操作。
mutex:用于保护对结构体中其他成员并发访问的互斥锁。
drivers_kset:指向一个kset,及 sys/bus/ 下的的dribers目录。
klist_devices:内核链表,用于维护总线上设备的列表。
klist_drivers:内核链表,用于维护总线上驱动的列表。
bus_notifier:阻塞通知链表头,当总线上有重要的事发生如设备添加、移除驱动程序时,可以通过这个通知链表向注册的通知链进行广播。设备驱动程序之类的内核组件注册在这个通知链上。
drivers_autoprobe:1:这是一个标志位(使用位域表示),用于指示是否自动探测总线上的设备并尝试加载与之匹配的驱动程序。如果设置为 1,当有新设备连接到总线上时,内核会自动触发设备探测过程,寻找合适的驱动程序并进行加载;如果设置为 0,则需要手动进行设备探测或采取其他方式加载驱动程序。
bus:指向所属的总线类型结构体。实现该私有结构体与对应总线类型结构体的关联。
glue_dirs:这是一个特殊的 kset,用于在 sysfs 文件系统中创建一些用于关联和组织设备、驱动程序等的中间目录,起到 “胶水” 的作用,帮助在 sysfs 中构建更清晰和合理的层次结构,方便用户空间程序对设备和驱动程序的访问和管理。
class:总线子系统与设备类进行关联。
7.class

name //用户空间通过这个类名称来识别和操作该类设备,例如“block”和“char”表示块设备和字符设备。
owner; //指向拥有该设备的模块
class_attrs;//类属性是指这个大类的属性,不是特定属于某个设备,例如定义该类设备的通用版本信息可通过 /sys/class/<class_name>/<attr_name> 来访问
dev_groups;//设备的属性组,会在 class/<device_name>/<attr_name> 找到
dev_kobj;//指向一个内核对象,作为该类所有设备的父对象。
dev_uevent//当设备发生热插拔等事件时,内核会调用这个函数来生成 uevent 事件。函数的参数 dev 指向发生事件的设备,env 用于设置事件相关的环境变量。通过这个函数,可以向用户空间发送设备状态变化的通知。
devnode//用于生成设备节点的名称和权限。创建设备节点是调用该函数,返回的字符串是该设备的名称。
class_release//设备类释放时调用该函数执行清理操作比如是否设备类占用的资源。
dev_release//属于该类的设备被释放时调用该函数。
suspend//处理设备的挂起操作,当系统进入睡眠状态或设备需要挂起以节省电源时dev指向挂起设备,state表示挂起状态。
resume//系统从睡眠状态恢复或设备要结束挂起状态调用该函数。
shutdown//用于处理系统关机时设备的相关操作。当系统关机时,内核会调用这个函数来执行设备的关机清理工作,例如保存设备的状态信息等。
ns_type//指向一组命名空间操作函数。命名空间用于在 sysfs 文件系统中对设备进行逻辑分组和隔离,这在一些复杂的系统中有助于更好地组织和管理设备。
namespace//用于获取设备所属的命名空间
pm;/指向一组电源管理操作函数。例如前面提到的 suspend 和 resume 函数。
p;//指向一个私有子系统数据结构体。这个结构体通常包含了设备类的一些私有数据和内部管理信息,内核内部使用它来管理设备类的一些状态和操作。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)