18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * event.c - exporting ACPI events via procfs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 118c2ecf20Sopenharmony_ci#include <linux/export.h> 128c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/poll.h> 158c2ecf20Sopenharmony_ci#include <linux/gfp.h> 168c2ecf20Sopenharmony_ci#include <linux/acpi.h> 178c2ecf20Sopenharmony_ci#include <net/netlink.h> 188c2ecf20Sopenharmony_ci#include <net/genetlink.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "internal.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* ACPI notifier chain */ 238c2ecf20Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(acpi_chain_head); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciint acpi_notifier_call_chain(struct acpi_device *dev, u32 type, u32 data) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct acpi_bus_event event; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci strcpy(event.device_class, dev->pnp.device_class); 308c2ecf20Sopenharmony_ci strcpy(event.bus_id, dev->pnp.bus_id); 318c2ecf20Sopenharmony_ci event.type = type; 328c2ecf20Sopenharmony_ci event.data = data; 338c2ecf20Sopenharmony_ci return (blocking_notifier_call_chain(&acpi_chain_head, 0, (void *)&event) 348c2ecf20Sopenharmony_ci == NOTIFY_BAD) ? -EINVAL : 0; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_notifier_call_chain); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciint register_acpi_notifier(struct notifier_block *nb) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return blocking_notifier_chain_register(&acpi_chain_head, nb); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_acpi_notifier); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciint unregister_acpi_notifier(struct notifier_block *nb) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return blocking_notifier_chain_unregister(&acpi_chain_head, nb); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_acpi_notifier); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#ifdef CONFIG_NET 518c2ecf20Sopenharmony_cistatic unsigned int acpi_event_seqnum; 528c2ecf20Sopenharmony_cistruct acpi_genl_event { 538c2ecf20Sopenharmony_ci acpi_device_class device_class; 548c2ecf20Sopenharmony_ci char bus_id[15]; 558c2ecf20Sopenharmony_ci u32 type; 568c2ecf20Sopenharmony_ci u32 data; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* attributes of acpi_genl_family */ 608c2ecf20Sopenharmony_cienum { 618c2ecf20Sopenharmony_ci ACPI_GENL_ATTR_UNSPEC, 628c2ecf20Sopenharmony_ci ACPI_GENL_ATTR_EVENT, /* ACPI event info needed by user space */ 638c2ecf20Sopenharmony_ci __ACPI_GENL_ATTR_MAX, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci#define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* commands supported by the acpi_genl_family */ 688c2ecf20Sopenharmony_cienum { 698c2ecf20Sopenharmony_ci ACPI_GENL_CMD_UNSPEC, 708c2ecf20Sopenharmony_ci ACPI_GENL_CMD_EVENT, /* kernel->user notifications for ACPI events */ 718c2ecf20Sopenharmony_ci __ACPI_GENL_CMD_MAX, 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci#define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define ACPI_GENL_FAMILY_NAME "acpi_event" 768c2ecf20Sopenharmony_ci#define ACPI_GENL_VERSION 0x01 778c2ecf20Sopenharmony_ci#define ACPI_GENL_MCAST_GROUP_NAME "acpi_mc_group" 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const struct genl_multicast_group acpi_event_mcgrps[] = { 808c2ecf20Sopenharmony_ci { .name = ACPI_GENL_MCAST_GROUP_NAME, }, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic struct genl_family acpi_event_genl_family __ro_after_init = { 848c2ecf20Sopenharmony_ci .module = THIS_MODULE, 858c2ecf20Sopenharmony_ci .name = ACPI_GENL_FAMILY_NAME, 868c2ecf20Sopenharmony_ci .version = ACPI_GENL_VERSION, 878c2ecf20Sopenharmony_ci .maxattr = ACPI_GENL_ATTR_MAX, 888c2ecf20Sopenharmony_ci .mcgrps = acpi_event_mcgrps, 898c2ecf20Sopenharmony_ci .n_mcgrps = ARRAY_SIZE(acpi_event_mcgrps), 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciint acpi_bus_generate_netlink_event(const char *device_class, 938c2ecf20Sopenharmony_ci const char *bus_id, 948c2ecf20Sopenharmony_ci u8 type, int data) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct sk_buff *skb; 978c2ecf20Sopenharmony_ci struct nlattr *attr; 988c2ecf20Sopenharmony_ci struct acpi_genl_event *event; 998c2ecf20Sopenharmony_ci void *msg_header; 1008c2ecf20Sopenharmony_ci int size; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* allocate memory */ 1038c2ecf20Sopenharmony_ci size = nla_total_size(sizeof(struct acpi_genl_event)) + 1048c2ecf20Sopenharmony_ci nla_total_size(0); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci skb = genlmsg_new(size, GFP_ATOMIC); 1078c2ecf20Sopenharmony_ci if (!skb) 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* add the genetlink message header */ 1118c2ecf20Sopenharmony_ci msg_header = genlmsg_put(skb, 0, acpi_event_seqnum++, 1128c2ecf20Sopenharmony_ci &acpi_event_genl_family, 0, 1138c2ecf20Sopenharmony_ci ACPI_GENL_CMD_EVENT); 1148c2ecf20Sopenharmony_ci if (!msg_header) { 1158c2ecf20Sopenharmony_ci nlmsg_free(skb); 1168c2ecf20Sopenharmony_ci return -ENOMEM; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* fill the data */ 1208c2ecf20Sopenharmony_ci attr = 1218c2ecf20Sopenharmony_ci nla_reserve(skb, ACPI_GENL_ATTR_EVENT, 1228c2ecf20Sopenharmony_ci sizeof(struct acpi_genl_event)); 1238c2ecf20Sopenharmony_ci if (!attr) { 1248c2ecf20Sopenharmony_ci nlmsg_free(skb); 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci event = nla_data(attr); 1298c2ecf20Sopenharmony_ci memset(event, 0, sizeof(struct acpi_genl_event)); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci strscpy(event->device_class, device_class, sizeof(event->device_class)); 1328c2ecf20Sopenharmony_ci strscpy(event->bus_id, bus_id, sizeof(event->bus_id)); 1338c2ecf20Sopenharmony_ci event->type = type; 1348c2ecf20Sopenharmony_ci event->data = data; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* send multicast genetlink message */ 1378c2ecf20Sopenharmony_ci genlmsg_end(skb, msg_header); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci genlmsg_multicast(&acpi_event_genl_family, skb, 0, 0, GFP_ATOMIC); 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_bus_generate_netlink_event); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int __init acpi_event_genetlink_init(void) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return genl_register_family(&acpi_event_genl_family); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci#else 1518c2ecf20Sopenharmony_ciint acpi_bus_generate_netlink_event(const char *device_class, 1528c2ecf20Sopenharmony_ci const char *bus_id, 1538c2ecf20Sopenharmony_ci u8 type, int data) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_bus_generate_netlink_event); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int acpi_event_genetlink_init(void) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci return -ENODEV; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci#endif 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int __init acpi_event_init(void) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci int error = 0; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (acpi_disabled) 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* create genetlink for acpi event */ 1748c2ecf20Sopenharmony_ci error = acpi_event_genetlink_init(); 1758c2ecf20Sopenharmony_ci if (error) 1768c2ecf20Sopenharmony_ci printk(KERN_WARNING PREFIX 1778c2ecf20Sopenharmony_ci "Failed to create genetlink family for ACPI event\n"); 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cifs_initcall(acpi_event_init); 182