18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 38c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 48c2ecf20Sopenharmony_ci#include <linux/export.h> 58c2ecf20Sopenharmony_ci#include <linux/suspend.h> 68c2ecf20Sopenharmony_ci#include <linux/bcd.h> 78c2ecf20Sopenharmony_ci#include <linux/acpi.h> 88c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "sleep.h" 118c2ecf20Sopenharmony_ci#include "internal.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * this file provides support for: 158c2ecf20Sopenharmony_ci * /proc/acpi/wakeup 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic int 198c2ecf20Sopenharmony_ciacpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci struct acpi_device *dev, *tmp; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci seq_printf(seq, "Device\tS-state\t Status Sysfs node\n"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci mutex_lock(&acpi_device_lock); 268c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, 278c2ecf20Sopenharmony_ci wakeup_list) { 288c2ecf20Sopenharmony_ci struct acpi_device_physical_node *entry; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (!dev->wakeup.flags.valid) 318c2ecf20Sopenharmony_ci continue; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci seq_printf(seq, "%s\t S%d\t", 348c2ecf20Sopenharmony_ci dev->pnp.bus_id, 358c2ecf20Sopenharmony_ci (u32) dev->wakeup.sleep_state); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci mutex_lock(&dev->physical_node_lock); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (!dev->physical_node_count) { 408c2ecf20Sopenharmony_ci seq_printf(seq, "%c%-8s\n", 418c2ecf20Sopenharmony_ci dev->wakeup.flags.valid ? '*' : ' ', 428c2ecf20Sopenharmony_ci device_may_wakeup(&dev->dev) ? 438c2ecf20Sopenharmony_ci "enabled" : "disabled"); 448c2ecf20Sopenharmony_ci } else { 458c2ecf20Sopenharmony_ci struct device *ldev; 468c2ecf20Sopenharmony_ci list_for_each_entry(entry, &dev->physical_node_list, 478c2ecf20Sopenharmony_ci node) { 488c2ecf20Sopenharmony_ci ldev = get_device(entry->dev); 498c2ecf20Sopenharmony_ci if (!ldev) 508c2ecf20Sopenharmony_ci continue; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (&entry->node != 538c2ecf20Sopenharmony_ci dev->physical_node_list.next) 548c2ecf20Sopenharmony_ci seq_printf(seq, "\t\t"); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci seq_printf(seq, "%c%-8s %s:%s\n", 578c2ecf20Sopenharmony_ci dev->wakeup.flags.valid ? '*' : ' ', 588c2ecf20Sopenharmony_ci (device_may_wakeup(&dev->dev) || 598c2ecf20Sopenharmony_ci device_may_wakeup(ldev)) ? 608c2ecf20Sopenharmony_ci "enabled" : "disabled", 618c2ecf20Sopenharmony_ci ldev->bus ? ldev->bus->name : 628c2ecf20Sopenharmony_ci "no-bus", dev_name(ldev)); 638c2ecf20Sopenharmony_ci put_device(ldev); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci mutex_unlock(&dev->physical_node_lock); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci mutex_unlock(&acpi_device_lock); 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void physical_device_enable_wakeup(struct acpi_device *adev) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct acpi_device_physical_node *entry; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci mutex_lock(&adev->physical_node_lock); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci list_for_each_entry(entry, 808c2ecf20Sopenharmony_ci &adev->physical_node_list, node) 818c2ecf20Sopenharmony_ci if (entry->dev && device_can_wakeup(entry->dev)) { 828c2ecf20Sopenharmony_ci bool enable = !device_may_wakeup(entry->dev); 838c2ecf20Sopenharmony_ci device_set_wakeup_enable(entry->dev, enable); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci mutex_unlock(&adev->physical_node_lock); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic ssize_t 908c2ecf20Sopenharmony_ciacpi_system_write_wakeup_device(struct file *file, 918c2ecf20Sopenharmony_ci const char __user * buffer, 928c2ecf20Sopenharmony_ci size_t count, loff_t * ppos) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct acpi_device *dev, *tmp; 958c2ecf20Sopenharmony_ci char strbuf[5]; 968c2ecf20Sopenharmony_ci char str[5] = ""; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (count > 4) 998c2ecf20Sopenharmony_ci count = 4; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (copy_from_user(strbuf, buffer, count)) 1028c2ecf20Sopenharmony_ci return -EFAULT; 1038c2ecf20Sopenharmony_ci strbuf[count] = '\0'; 1048c2ecf20Sopenharmony_ci sscanf(strbuf, "%s", str); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci mutex_lock(&acpi_device_lock); 1078c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, 1088c2ecf20Sopenharmony_ci wakeup_list) { 1098c2ecf20Sopenharmony_ci if (!dev->wakeup.flags.valid) 1108c2ecf20Sopenharmony_ci continue; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (!strncmp(dev->pnp.bus_id, str, 4)) { 1138c2ecf20Sopenharmony_ci if (device_can_wakeup(&dev->dev)) { 1148c2ecf20Sopenharmony_ci bool enable = !device_may_wakeup(&dev->dev); 1158c2ecf20Sopenharmony_ci device_set_wakeup_enable(&dev->dev, enable); 1168c2ecf20Sopenharmony_ci } else { 1178c2ecf20Sopenharmony_ci physical_device_enable_wakeup(dev); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci mutex_unlock(&acpi_device_lock); 1238c2ecf20Sopenharmony_ci return count; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int 1278c2ecf20Sopenharmony_ciacpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci return single_open(file, acpi_system_wakeup_device_seq_show, 1308c2ecf20Sopenharmony_ci PDE_DATA(inode)); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic const struct proc_ops acpi_system_wakeup_device_proc_ops = { 1348c2ecf20Sopenharmony_ci .proc_open = acpi_system_wakeup_device_open_fs, 1358c2ecf20Sopenharmony_ci .proc_read = seq_read, 1368c2ecf20Sopenharmony_ci .proc_write = acpi_system_write_wakeup_device, 1378c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 1388c2ecf20Sopenharmony_ci .proc_release = single_release, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_civoid __init acpi_sleep_proc_init(void) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci /* 'wakeup device' [R/W] */ 1448c2ecf20Sopenharmony_ci proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR, 1458c2ecf20Sopenharmony_ci acpi_root_dir, &acpi_system_wakeup_device_proc_ops); 1468c2ecf20Sopenharmony_ci} 147