1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/proc_fs.h> |
3 | #include <linux/seq_file.h> |
4 | #include <linux/export.h> |
5 | #include <linux/suspend.h> |
6 | #include <linux/bcd.h> |
7 | #include <linux/acpi.h> |
8 | #include <linux/uaccess.h> |
9 | |
10 | #include "sleep.h" |
11 | #include "internal.h" |
12 | |
13 | /* |
14 | * this file provides support for: |
15 | * /proc/acpi/wakeup |
16 | */ |
17 | |
18 | static int |
19 | acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) |
20 | { |
21 | struct acpi_device *dev, *tmp; |
22 | |
23 | seq_printf(m: seq, fmt: "Device\tS-state\t Status Sysfs node\n" ); |
24 | |
25 | mutex_lock(&acpi_device_lock); |
26 | list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, |
27 | wakeup_list) { |
28 | struct acpi_device_physical_node *entry; |
29 | |
30 | if (!dev->wakeup.flags.valid) |
31 | continue; |
32 | |
33 | seq_printf(m: seq, fmt: "%s\t S%d\t" , |
34 | dev->pnp.bus_id, |
35 | (u32) dev->wakeup.sleep_state); |
36 | |
37 | mutex_lock(&dev->physical_node_lock); |
38 | |
39 | if (!dev->physical_node_count) { |
40 | seq_printf(m: seq, fmt: "%c%-8s\n" , |
41 | dev->wakeup.flags.valid ? '*' : ' ', |
42 | device_may_wakeup(dev: &dev->dev) ? |
43 | "enabled" : "disabled" ); |
44 | } else { |
45 | struct device *ldev; |
46 | list_for_each_entry(entry, &dev->physical_node_list, |
47 | node) { |
48 | ldev = get_device(dev: entry->dev); |
49 | if (!ldev) |
50 | continue; |
51 | |
52 | if (&entry->node != |
53 | dev->physical_node_list.next) |
54 | seq_printf(m: seq, fmt: "\t\t" ); |
55 | |
56 | seq_printf(m: seq, fmt: "%c%-8s %s:%s\n" , |
57 | dev->wakeup.flags.valid ? '*' : ' ', |
58 | (device_may_wakeup(dev: &dev->dev) || |
59 | device_may_wakeup(dev: ldev)) ? |
60 | "enabled" : "disabled" , |
61 | ldev->bus ? ldev->bus->name : |
62 | "no-bus" , dev_name(dev: ldev)); |
63 | put_device(dev: ldev); |
64 | } |
65 | } |
66 | |
67 | mutex_unlock(lock: &dev->physical_node_lock); |
68 | } |
69 | mutex_unlock(lock: &acpi_device_lock); |
70 | return 0; |
71 | } |
72 | |
73 | static void physical_device_enable_wakeup(struct acpi_device *adev) |
74 | { |
75 | struct acpi_device_physical_node *entry; |
76 | |
77 | mutex_lock(&adev->physical_node_lock); |
78 | |
79 | list_for_each_entry(entry, |
80 | &adev->physical_node_list, node) |
81 | if (entry->dev && device_can_wakeup(dev: entry->dev)) { |
82 | bool enable = !device_may_wakeup(dev: entry->dev); |
83 | device_set_wakeup_enable(dev: entry->dev, enable); |
84 | } |
85 | |
86 | mutex_unlock(lock: &adev->physical_node_lock); |
87 | } |
88 | |
89 | static ssize_t |
90 | acpi_system_write_wakeup_device(struct file *file, |
91 | const char __user * buffer, |
92 | size_t count, loff_t * ppos) |
93 | { |
94 | struct acpi_device *dev, *tmp; |
95 | char strbuf[5]; |
96 | char str[5] = "" ; |
97 | |
98 | if (count > 4) |
99 | count = 4; |
100 | |
101 | if (copy_from_user(to: strbuf, from: buffer, n: count)) |
102 | return -EFAULT; |
103 | strbuf[count] = '\0'; |
104 | sscanf(strbuf, "%s" , str); |
105 | |
106 | mutex_lock(&acpi_device_lock); |
107 | list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, |
108 | wakeup_list) { |
109 | if (!dev->wakeup.flags.valid) |
110 | continue; |
111 | |
112 | if (!strncmp(dev->pnp.bus_id, str, 4)) { |
113 | if (device_can_wakeup(dev: &dev->dev)) { |
114 | bool enable = !device_may_wakeup(dev: &dev->dev); |
115 | device_set_wakeup_enable(dev: &dev->dev, enable); |
116 | } else { |
117 | physical_device_enable_wakeup(adev: dev); |
118 | } |
119 | break; |
120 | } |
121 | } |
122 | mutex_unlock(lock: &acpi_device_lock); |
123 | return count; |
124 | } |
125 | |
126 | static int |
127 | acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file) |
128 | { |
129 | return single_open(file, acpi_system_wakeup_device_seq_show, |
130 | pde_data(inode)); |
131 | } |
132 | |
133 | static const struct proc_ops acpi_system_wakeup_device_proc_ops = { |
134 | .proc_open = acpi_system_wakeup_device_open_fs, |
135 | .proc_read = seq_read, |
136 | .proc_write = acpi_system_write_wakeup_device, |
137 | .proc_lseek = seq_lseek, |
138 | .proc_release = single_release, |
139 | }; |
140 | |
141 | void __init acpi_sleep_proc_init(void) |
142 | { |
143 | /* 'wakeup device' [R/W] */ |
144 | proc_create(name: "wakeup" , S_IFREG | S_IRUGO | S_IWUSR, |
145 | parent: acpi_root_dir, proc_ops: &acpi_system_wakeup_device_proc_ops); |
146 | } |
147 | |