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