手提电脑开盖,关盖的驱动


author: hjjdebug

date: 2026年 05月 27日 星期三 14:11:15 CST

descrip: 手提电脑开盖,关盖的驱动


文章目录

  • 1.笔记本的合盖,开盖设备.
  • [2. 驱动代码](#2. 驱动代码)
  • [3. 去掉原有驱动,加载自己驱动是实验的关键.](#3. 去掉原有驱动,加载自己驱动是实验的关键.)
    • [3.1. 收到合盖事件不要休眠.](#3.1. 收到合盖事件不要休眠.)
    • [3.2. 解除原来的驱动绑定.](#3.2. 解除原来的驱动绑定.)
      • [3.2.1 先要找到盖子设备](#3.2.1 先要找到盖子设备)
      • [3.2.2 由这个设备找到驱动.](#3.2.2 由这个设备找到驱动.)
      • [3.2.3 解除这个驱动和设备的binding](#3.2.3 解除这个驱动和设备的binding)
  • 4.安装测试我们的驱动.

目的: 写一个实际设备的最简单的驱动.

1.笔记本的合盖,开盖设备.

据说这个盖子上有霍尔传感器,传感器信号连接到一个单片机上.

专门监视着这个开合信号. 一但有变,要通过ACPI 协议通知内核.

具体过程, EC(embeded control) ->(上报) pch(periperal controler hub) 南桥->

产生SCI中断 (system control interrupt), 一般是IRQ9

然后是中断响应及处理阶段, ACPI核心首先识别到是PNPC0D0 设备事件,遍历设备上

注册的所有Notify 回调链表.

驱动就在这个回调函数链上. 系统会通知你状态,就是这么简单. 你不用实时监测状态的变化.

就是说,驱动要完成的功能是,当盖子打开时,驱动能收到一条打开的消息.

盖子闭合时,驱动收到一条闭合的消息.

这不是传统意义上的驱动,获取或写入设备数据,而是监测设备数据变化.

  1. 首先,它要匹配上设备,让驱动独占这个设备,完成自己的probe 函数,这是驱动的典型行为.
  2. 注册一个回调函数,就能够收到状态改变信息.

可能硬件层面很复杂,甚至是其它类型, 但它应该符合ACPI协议,是ACPI设备,

盖子在ACPI 协议中的名称是"PNP0C0D"

我们也应该在标准中享受到便利之处,那就是忽略它的硬件复杂性,只面向系统接口编程

2. 驱动代码

下面看看代码,很简单.60行代码.

cpp 复制代码
$cat lid_driver.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/device.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Linux 5.4 Exact Match LID Driver");
MODULE_AUTHOR("DIY");

// 匹配 LID 标准设备, 是ACPI 协议,规定盖子的名称就是PNP0C0D
// 这就是标准的好处
static const struct acpi_device_id lid_acpi_ids[] = {
    {"PNP0C0D", 0},
    { /* 终止 */ }
};
MODULE_DEVICE_TABLE(acpi, lid_acpi_ids);

// 事件回调
static void lid_notify(acpi_handle handle, u32 event, void *priv)
{
    if (event == 0x80) {
        pr_info("[DIY LID DRV] >>> 盖子关闭\n");
    } else if (event == 0x81) {
        pr_info("[DIY LID DRV] >>> 盖子打开\n");
    }
}

// Probe 设备探测
static int lid_probe(struct acpi_device *adev)
{
    pr_info("[DIY LID DRV] ✅ 成功接管 PNP0C0D LID 设备!\n");

	//acpi 子系统会依次调用注册的回调函数
    acpi_install_notify_handler(adev->handle,
                               ACPI_DEVICE_NOTIFY,
                               lid_notify,
                               NULL);
    return 0;
}

// Remove 设备移除
static int lid_remove(struct acpi_device *adev)
{
    acpi_remove_notify_handler(adev->handle,
                               ACPI_DEVICE_NOTIFY,
                               lid_notify);
    pr_info("[DIY LID DRV] 设备解绑\n");
    return 0;
}


// 【完全严格对齐内核结构体顺序】
static struct acpi_driver my_lid_driver = {
    .name   = "my_custom_lid",
    .class  = "lid",
    .ids    = lid_acpi_ids,
    .flags  = 0,
	.ops    = {
		.add    = lid_probe, //probe 要放到ops 结构体中
		.remove = lid_remove,
	},
    .owner  = THIS_MODULE,
};

module_acpi_driver(my_lid_driver);

3. 去掉原有驱动,加载自己驱动是实验的关键.

原驱动收到合盖事件会使系统休眠, 先解决这个问题.

3.1. 收到合盖事件不要休眠.

经查, 这是systemd-logind 服务的行为.

$ systemctl status systemd-logind

● systemd-logind.service - Login Service

Loaded: loaded (/lib/systemd/system/systemd-logind.service; static; vendor preset: enabled)

Active: active (running) since Wed 2026-05-27 10:10:17 CST; 3h 21min ago

Docs: man:systemd-logind.service(8)

man:logind.conf(5)

https://www.freedesktop.org/wiki/Software/systemd/logind

https://www.freedesktop.org/wiki/Software/systemd/multiseat

Main PID: 4262 (systemd-logind)

Status: "Processing requests..."

CGroup: /system.slice/systemd-logind.service

└─4262 /lib/systemd/systemd-logind

5月 27 10:10:17 hjj-laptop systemd-logind[4262]: New seat seat0.

5月 27 10:10:17 hjj-laptop systemd-logind[4262]: Watching system buttons on /dev/input/event3 (Power Button)

5月 27 10:10:17 hjj-laptop systemd-logind[4262]: Watching system buttons on /dev/input/event2 (Power Button)

5月 27 10:10:17 hjj-laptop systemd-logind[4262]: Watching system buttons on /dev/input/event1 (Lid Switch)

5月 27 10:10:17 hjj-laptop systemd-logind[4262]: Watching system buttons on /dev/input/event0 (Sleep Button)

5月 27 10:10:17 hjj-laptop systemd-logind[4262]: Watching system buttons on /dev/input/event13 (SEMICO USB Keyboard)

5月 27 10:10:17 hjj-laptop systemd-logind[4262]: Watching system buttons on /dev/input/event6 (AT Translated Set 2 keyboard)

5月 27 10:10:17 hjj-laptop systemd[1]: Started Login Service.

5月 27 10:10:27 hjj-laptop systemd-logind[4262]: New session c1 of user gdm.

5月 27 10:20:16 hjj-laptop systemd-logind[4262]: New session 3 of user hjj.

它的服务脚本是/lib/systemd/system/systemd-logind.service, 执行文件是/lib/systemd/systemd-logind.

后者是一个elf 文件.功能:

监视着Power Button, Lid Switch, PS2 keyboard, USB keyboard 硬件.

我们不想关闭这个服务,因为影响太多, 但想改一下这个服务的行为,让合盖不要suspend, 而是ignore该信号.

打开/etc/systemd/logind.conf

#HandleLidSwitch=suspend

改为

HandleLidSwitch=ignore

意思是原来默认的行为是suspend, 现在显式指定为ignore

然后重启logind 服务:

$ sudo systemctl restart systemd-logind.service

再测关闭盖子,果然不再suspend了

3.2. 解除原来的驱动绑定.

3.2.1 先要找到盖子设备

$ find /sys/devices -name "PNP0C0D*"

输出: /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00

3.2.2 由这个设备找到驱动.

$ ls -l /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00

总用量 0

lrwxrwxrwx 1 root root 0 5月 27 13:49 driver -> .../.../.../.../bus/acpi/drivers/button/

-r--r--r-- 1 root root 4096 5月 27 10:21 hid

-r--r--r-- 1 root root 4096 5月 27 10:21 modalias

-r--r--r-- 1 root root 4096 5月 27 10:21 path

lrwxrwxrwx 1 root root 0 5月 27 10:21 physical_node -> .../.../.../platform/PNP0C0D:00

drwxr-xr-x 2 root root 0 5月 27 10:20 power

lrwxrwxrwx 1 root root 0 5月 27 10:09 subsystem -> .../.../.../.../bus/acpi

-rw-r--r-- 1 root root 4096 5月 27 10:09 uevent

drwxr-xr-x 3 root root 0 5月 27 10:09 wakeup

3.2.3 解除这个驱动和设备的binding

先进入到驱动目录.

$cd /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00

$cd .../.../.../.../bus/acpi/drivers/button/

hjj@hjj-laptop:/sys/bus/acpi/drivers/button$ ll

总用量 0

drwxr-xr-x 2 root root 0 5月 27 13:55 ./

drwxr-xr-x 9 root root 0 5月 27 13:55 .../

--w------- 1 root root 4096 5月 27 13:58 bind

lrwxrwxrwx 1 root root 0 5月 27 13:58 LNXPWRBN:00 -> .../.../.../.../devices/LNXSYSTM:00/LNXPWRBN:00/

lrwxrwxrwx 1 root root 0 5月 27 13:58 PNP0C0C:00 -> .../.../.../.../devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/

lrwxrwxrwx 1 root root 0 5月 27 13:58 PNP0C0D:00 -> .../.../.../.../devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/

lrwxrwxrwx 1 root root 0 5月 27 13:58 PNP0C0E:00 -> .../.../.../.../devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/

--w------- 1 root root 4096 5月 27 13:55 uevent

--w------- 1 root root 4096 5月 27 13:58 unbind

解除命令

$ echo PNP0C0D:00 |sudo tee unbind

于是,相关链接,/dev/input下事件均消失.

这种操作, 针对的是就算驱动编译到内核,也可以用该方法解除binding.

4.安装测试我们的驱动.

insmod lid_driver.ko

开盖,关盖可以查看打印信息.

相关推荐
百里杨3 个月前
ACPI电源按钮唤醒S3切换到S0流程(附流程图)
s3·acpi·s0
百里杨3 个月前
ACPI休眠按钮触发S0切换到S3流程(附流程图)
s3·acpi·s0
sitelist5 个月前
ACPI!device后ACPI!Name函数建立子节点对象
device·name·acpi