xref: /openbmc/linux/drivers/acpi/event.c (revision ad319565)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * event.c - exporting ACPI events via procfs
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
61da177e4SLinus Torvalds  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
10*ad319565SHanjun Guo #define pr_fmt(fmt) "ACPI: " fmt
11*ad319565SHanjun Guo 
121da177e4SLinus Torvalds #include <linux/spinlock.h>
13214f2c90SPaul Gortmaker #include <linux/export.h>
141da177e4SLinus Torvalds #include <linux/proc_fs.h>
151da177e4SLinus Torvalds #include <linux/init.h>
161da177e4SLinus Torvalds #include <linux/poll.h>
175a0e3ad6STejun Heo #include <linux/gfp.h>
188b48463fSLv Zheng #include <linux/acpi.h>
19864bdfb9SZhang Rui #include <net/netlink.h>
20864bdfb9SZhang Rui #include <net/genetlink.h>
211da177e4SLinus Torvalds 
22a192a958SLen Brown #include "internal.h"
23a192a958SLen Brown 
249ee85241SZhang Rui /* ACPI notifier chain */
25c8e773faSAdrian Bunk static BLOCKING_NOTIFIER_HEAD(acpi_chain_head);
269ee85241SZhang Rui 
acpi_notifier_call_chain(struct acpi_device * dev,u32 type,u32 data)279ee85241SZhang Rui int acpi_notifier_call_chain(struct acpi_device *dev, u32 type, u32 data)
289ee85241SZhang Rui {
299ee85241SZhang Rui 	struct acpi_bus_event event;
309ee85241SZhang Rui 
319ee85241SZhang Rui 	strcpy(event.device_class, dev->pnp.device_class);
329ee85241SZhang Rui 	strcpy(event.bus_id, dev->pnp.bus_id);
339ee85241SZhang Rui 	event.type = type;
349ee85241SZhang Rui 	event.data = data;
359ee85241SZhang Rui 	return (blocking_notifier_call_chain(&acpi_chain_head, 0, (void *)&event)
369ee85241SZhang Rui 			== NOTIFY_BAD) ? -EINVAL : 0;
379ee85241SZhang Rui }
389ee85241SZhang Rui EXPORT_SYMBOL(acpi_notifier_call_chain);
399ee85241SZhang Rui 
register_acpi_notifier(struct notifier_block * nb)409ee85241SZhang Rui int register_acpi_notifier(struct notifier_block *nb)
419ee85241SZhang Rui {
429ee85241SZhang Rui 	return blocking_notifier_chain_register(&acpi_chain_head, nb);
439ee85241SZhang Rui }
449ee85241SZhang Rui EXPORT_SYMBOL(register_acpi_notifier);
459ee85241SZhang Rui 
unregister_acpi_notifier(struct notifier_block * nb)469ee85241SZhang Rui int unregister_acpi_notifier(struct notifier_block *nb)
479ee85241SZhang Rui {
489ee85241SZhang Rui 	return blocking_notifier_chain_unregister(&acpi_chain_head, nb);
499ee85241SZhang Rui }
509ee85241SZhang Rui EXPORT_SYMBOL(unregister_acpi_notifier);
519ee85241SZhang Rui 
52864bdfb9SZhang Rui #ifdef CONFIG_NET
53e13d8747SAdrian Bunk static unsigned int acpi_event_seqnum;
54864bdfb9SZhang Rui struct acpi_genl_event {
55864bdfb9SZhang Rui 	acpi_device_class device_class;
56864bdfb9SZhang Rui 	char bus_id[15];
57864bdfb9SZhang Rui 	u32 type;
58864bdfb9SZhang Rui 	u32 data;
59864bdfb9SZhang Rui };
60864bdfb9SZhang Rui 
61864bdfb9SZhang Rui /* attributes of acpi_genl_family */
62864bdfb9SZhang Rui enum {
63864bdfb9SZhang Rui 	ACPI_GENL_ATTR_UNSPEC,
64864bdfb9SZhang Rui 	ACPI_GENL_ATTR_EVENT,	/* ACPI event info needed by user space */
65864bdfb9SZhang Rui 	__ACPI_GENL_ATTR_MAX,
66864bdfb9SZhang Rui };
67864bdfb9SZhang Rui #define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1)
68864bdfb9SZhang Rui 
69864bdfb9SZhang Rui /* commands supported by the acpi_genl_family */
70864bdfb9SZhang Rui enum {
71864bdfb9SZhang Rui 	ACPI_GENL_CMD_UNSPEC,
72864bdfb9SZhang Rui 	ACPI_GENL_CMD_EVENT,	/* kernel->user notifications for ACPI events */
73864bdfb9SZhang Rui 	__ACPI_GENL_CMD_MAX,
74864bdfb9SZhang Rui };
75864bdfb9SZhang Rui #define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1)
76864bdfb9SZhang Rui 
779c977a45SZhang Rui #define ACPI_GENL_FAMILY_NAME		"acpi_event"
78864bdfb9SZhang Rui #define ACPI_GENL_VERSION		0x01
799c977a45SZhang Rui #define ACPI_GENL_MCAST_GROUP_NAME 	"acpi_mc_group"
80864bdfb9SZhang Rui 
812a94fe48SJohannes Berg static const struct genl_multicast_group acpi_event_mcgrps[] = {
822a94fe48SJohannes Berg 	{ .name = ACPI_GENL_MCAST_GROUP_NAME, },
832a94fe48SJohannes Berg };
842a94fe48SJohannes Berg 
8556989f6dSJohannes Berg static struct genl_family acpi_event_genl_family __ro_after_init = {
86489111e5SJohannes Berg 	.module = THIS_MODULE,
879c977a45SZhang Rui 	.name = ACPI_GENL_FAMILY_NAME,
88864bdfb9SZhang Rui 	.version = ACPI_GENL_VERSION,
89864bdfb9SZhang Rui 	.maxattr = ACPI_GENL_ATTR_MAX,
902a94fe48SJohannes Berg 	.mcgrps = acpi_event_mcgrps,
912a94fe48SJohannes Berg 	.n_mcgrps = ARRAY_SIZE(acpi_event_mcgrps),
92864bdfb9SZhang Rui };
93864bdfb9SZhang Rui 
acpi_bus_generate_netlink_event(const char * device_class,const char * bus_id,u8 type,int data)94962ce8caSZhang Rui int acpi_bus_generate_netlink_event(const char *device_class,
95962ce8caSZhang Rui 				      const char *bus_id,
96864bdfb9SZhang Rui 				      u8 type, int data)
97864bdfb9SZhang Rui {
98864bdfb9SZhang Rui 	struct sk_buff *skb;
99864bdfb9SZhang Rui 	struct nlattr *attr;
100864bdfb9SZhang Rui 	struct acpi_genl_event *event;
101864bdfb9SZhang Rui 	void *msg_header;
102864bdfb9SZhang Rui 	int size;
103864bdfb9SZhang Rui 
104864bdfb9SZhang Rui 	/* allocate memory */
105864bdfb9SZhang Rui 	size = nla_total_size(sizeof(struct acpi_genl_event)) +
106864bdfb9SZhang Rui 	    nla_total_size(0);
107864bdfb9SZhang Rui 
108864bdfb9SZhang Rui 	skb = genlmsg_new(size, GFP_ATOMIC);
109864bdfb9SZhang Rui 	if (!skb)
110864bdfb9SZhang Rui 		return -ENOMEM;
111864bdfb9SZhang Rui 
112864bdfb9SZhang Rui 	/* add the genetlink message header */
113864bdfb9SZhang Rui 	msg_header = genlmsg_put(skb, 0, acpi_event_seqnum++,
114864bdfb9SZhang Rui 				 &acpi_event_genl_family, 0,
115864bdfb9SZhang Rui 				 ACPI_GENL_CMD_EVENT);
116864bdfb9SZhang Rui 	if (!msg_header) {
117864bdfb9SZhang Rui 		nlmsg_free(skb);
118864bdfb9SZhang Rui 		return -ENOMEM;
119864bdfb9SZhang Rui 	}
120864bdfb9SZhang Rui 
121864bdfb9SZhang Rui 	/* fill the data */
122864bdfb9SZhang Rui 	attr =
123864bdfb9SZhang Rui 	    nla_reserve(skb, ACPI_GENL_ATTR_EVENT,
124864bdfb9SZhang Rui 			sizeof(struct acpi_genl_event));
125864bdfb9SZhang Rui 	if (!attr) {
126864bdfb9SZhang Rui 		nlmsg_free(skb);
127864bdfb9SZhang Rui 		return -EINVAL;
128864bdfb9SZhang Rui 	}
129864bdfb9SZhang Rui 
130864bdfb9SZhang Rui 	event = nla_data(attr);
131864bdfb9SZhang Rui 	memset(event, 0, sizeof(struct acpi_genl_event));
132864bdfb9SZhang Rui 
133c7d5f21eSGustavo A. R. Silva 	strscpy(event->device_class, device_class, sizeof(event->device_class));
134c7d5f21eSGustavo A. R. Silva 	strscpy(event->bus_id, bus_id, sizeof(event->bus_id));
135864bdfb9SZhang Rui 	event->type = type;
136864bdfb9SZhang Rui 	event->data = data;
137864bdfb9SZhang Rui 
138864bdfb9SZhang Rui 	/* send multicast genetlink message */
139053c095aSJohannes Berg 	genlmsg_end(skb, msg_header);
140864bdfb9SZhang Rui 
1412a94fe48SJohannes Berg 	genlmsg_multicast(&acpi_event_genl_family, skb, 0, 0, GFP_ATOMIC);
142864bdfb9SZhang Rui 	return 0;
143864bdfb9SZhang Rui }
144864bdfb9SZhang Rui 
145962ce8caSZhang Rui EXPORT_SYMBOL(acpi_bus_generate_netlink_event);
146962ce8caSZhang Rui 
acpi_event_genetlink_init(void)14756989f6dSJohannes Berg static int __init acpi_event_genetlink_init(void)
148864bdfb9SZhang Rui {
1492a94fe48SJohannes Berg 	return genl_register_family(&acpi_event_genl_family);
150864bdfb9SZhang Rui }
151864bdfb9SZhang Rui 
152864bdfb9SZhang Rui #else
acpi_bus_generate_netlink_event(const char * device_class,const char * bus_id,u8 type,int data)1533e069ee0SLen Brown int acpi_bus_generate_netlink_event(const char *device_class,
1543e069ee0SLen Brown 				      const char *bus_id,
1553e069ee0SLen Brown 				      u8 type, int data)
156864bdfb9SZhang Rui {
157864bdfb9SZhang Rui 	return 0;
158864bdfb9SZhang Rui }
159864bdfb9SZhang Rui 
16066baf327SHenrique de Moraes Holschuh EXPORT_SYMBOL(acpi_bus_generate_netlink_event);
161962ce8caSZhang Rui 
acpi_event_genetlink_init(void)162864bdfb9SZhang Rui static int acpi_event_genetlink_init(void)
163864bdfb9SZhang Rui {
164864bdfb9SZhang Rui 	return -ENODEV;
165864bdfb9SZhang Rui }
166864bdfb9SZhang Rui #endif
167864bdfb9SZhang Rui 
acpi_event_init(void)1681da177e4SLinus Torvalds static int __init acpi_event_init(void)
1691da177e4SLinus Torvalds {
1701da177e4SLinus Torvalds 	int error;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	if (acpi_disabled)
173d550d98dSPatrick Mochel 		return 0;
1741da177e4SLinus Torvalds 
175864bdfb9SZhang Rui 	/* create genetlink for acpi event */
176864bdfb9SZhang Rui 	error = acpi_event_genetlink_init();
177864bdfb9SZhang Rui 	if (error)
178*ad319565SHanjun Guo 		pr_warn("Failed to create genetlink family for ACPI event\n");
179*ad319565SHanjun Guo 
180864bdfb9SZhang Rui 	return 0;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds 
183864bdfb9SZhang Rui fs_initcall(acpi_event_init);
184