在Linux中,热插拔(热拔插)是指在系统运行过程中插入或拔出设备,而无需重启系统。为了支持热插拔,Linux驱动程序需要实现一些特定的机制和接口。以下是一些关键步骤和概念:
Linux内核提供了kobject和uevent机制来处理热插拔事件。驱动程序可以通过创建kobject并将其添加到sysfs文件系统中来注册热插拔事件监听器。
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/sysfs.h>
static struct kobject *my_kobject;
static int __init my_init(void) {
my_kobject = kobject_create_and_add("my_device", kernel_kobj);
if (!my_kobject)
return -ENOMEM;
if (sysfs_create_file(my_kobject, &attr.attr))
pr_debug("failed to create sysfs file\n");
return 0;
}
static void __exit my_exit(void) {
kobject_put(my_kobject);
}
module_init(my_init);
module_exit(my_exit);
当设备插入或拔出时,内核会生成uevent事件。驱动程序可以通过注册uevent处理函数来响应这些事件。
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#include <linux/uevent.h>
static struct kobject *my_kobject;
static int my_uevent(struct kobject *kobj, struct kobj_uevent_env *env) {
if (strcmp(kobj->name, "my_device") == 0) {
if (env->action == UE_EVENT_ADD)
pr_info("Device added\n");
else if (env->action == UE_EVENT_REMOVE)
pr_info("Device removed\n");
}
return 0;
}
static struct kset my_kset = {
.uevent = my_uevent,
};
static int __init my_init(void) {
my_kobject = kobject_create_and_add("my_device", &my_kset.kobj);
if (!my_kobject)
return -ENOMEM;
return 0;
}
static void __exit my_exit(void) {
kobject_put(my_kobject);
}
module_init(my_init);
module_exit(my_exit);
热插拔事件发生时,驱动程序可能需要动态地分配或释放资源。例如,插入设备时可能需要分配内存或打开设备文件,拔出设备时则需要释放这些资源。
static int my_open(struct inode *inodep, struct file *filep) {
// 打开设备文件
return 0;
}
static int my_release(struct inode *inodep, struct file *filep) {
// 关闭设备文件
return 0;
}
static struct file_operations fops = {
.open = my_open,
.release = my_release,
};
static int __init my_init(void) {
my_kobject = kobject_create_and_add("my_device", &my_kset.kobj);
if (!my_kobject)
return -ENOMEM;
// 注册字符设备
if (register_chrdev(0, "my_device", &fops) < 0) {
pr_err("Failed to register char device\n");
kobject_put(my_kobject);
return -ENOMEM;
}
return 0;
}
static void __exit my_exit(void) {
unregister_chrdev(0, "my_device");
kobject_put(my_kobject);
}
module_init(my_init);
module_exit(my_exit);
input子系统对于一些输入设备(如键盘、鼠标),可以使用input子系统来处理热插拔事件。
#include <linux/input.h>
#include <linux/module.h>
static struct input_dev *my_input_dev;
static int my_probe(struct platform_device *pdev) {
my_input_dev = input_allocate_device();
if (!my_input_dev)
return -ENOMEM;
my_input_dev->name = "My Input Device";
my_input_dev->id.bustype = BUS_USB;
my_input_dev->id.vendor = 0x1234;
my_input_dev->id.product = 0x5678;
input_set_capability(my_input_dev, EV_KEY, BTN_MOUSE);
input_set_capability(my_input_dev, EV_REL, REL_X);
input_set_capability(my_input_dev, EV_REL, REL_Y);
if (input_register_device(my_input_dev)) {
pr_err("Failed to register input device\n");
input_free_device(my_input_dev);
return -ENOMEM;
}
return 0;
}
static int my_remove(struct platform_device *pdev) {
input_unregister_device(my_input_dev);
input_free_device(my_input_dev);
return 0;
}
static struct of_device_id my_of_match[] = {
{ .compatible = "my,device", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_of_match);
static struct platform_driver my_driver = {
.probe = my_probe,
.remove = my_remove,
.driver = {
.name = "my_device",
.of_match_table = my_of_match,
},
};
module_platform_driver(my_driver);
支持热插拔的Linux驱动程序需要:
input)来处理特定类型的设备。通过这些步骤,驱动程序可以在设备插入或拔出时做出相应的响应,从而实现热插拔功能。