event.c 5.78 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
/*
 * event.c - exporting ACPI events via procfs
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *
 */

#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <acpi/acpi_drivers.h>
14
15
#include <net/netlink.h>
#include <net/genetlink.h>
Linus Torvalds's avatar
Linus Torvalds committed
16
17

#define _COMPONENT		ACPI_SYSTEM_COMPONENT
18
ACPI_MODULE_NAME("event");
Linus Torvalds's avatar
Linus Torvalds committed
19
20
21

/* Global vars for handling event proc entry */
static DEFINE_SPINLOCK(acpi_system_event_lock);
Len Brown's avatar
Len Brown committed
22
23
24
int event_is_open = 0;
extern struct list_head acpi_bus_event_list;
extern wait_queue_head_t acpi_bus_event_queue;
Linus Torvalds's avatar
Linus Torvalds committed
25

Len Brown's avatar
Len Brown committed
26
static int acpi_system_open_event(struct inode *inode, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
27
{
Pavel Machek's avatar
Pavel Machek committed
28
	spin_lock_irq(&acpi_system_event_lock);
Linus Torvalds's avatar
Linus Torvalds committed
29

Pavel Machek's avatar
Pavel Machek committed
30
	if (event_is_open)
Linus Torvalds's avatar
Linus Torvalds committed
31
32
33
34
		goto out_busy;

	event_is_open = 1;

Pavel Machek's avatar
Pavel Machek committed
35
	spin_unlock_irq(&acpi_system_event_lock);
Linus Torvalds's avatar
Linus Torvalds committed
36
37
	return 0;

Len Brown's avatar
Len Brown committed
38
      out_busy:
Pavel Machek's avatar
Pavel Machek committed
39
	spin_unlock_irq(&acpi_system_event_lock);
Linus Torvalds's avatar
Linus Torvalds committed
40
41
42
43
	return -EBUSY;
}

static ssize_t
Len Brown's avatar
Len Brown committed
44
45
acpi_system_read_event(struct file *file, char __user * buffer, size_t count,
		       loff_t * ppos)
Linus Torvalds's avatar
Linus Torvalds committed
46
{
Len Brown's avatar
Len Brown committed
47
48
49
50
51
	int result = 0;
	struct acpi_bus_event event;
	static char str[ACPI_MAX_STRING];
	static int chars_remaining = 0;
	static char *ptr;
Linus Torvalds's avatar
Linus Torvalds committed
52
53
54
55
56
57

	if (!chars_remaining) {
		memset(&event, 0, sizeof(struct acpi_bus_event));

		if ((file->f_flags & O_NONBLOCK)
		    && (list_empty(&acpi_bus_event_list)))
58
			return -EAGAIN;
Linus Torvalds's avatar
Linus Torvalds committed
59
60

		result = acpi_bus_receive_event(&event);
61
		if (result)
62
			return result;
Linus Torvalds's avatar
Linus Torvalds committed
63

Len Brown's avatar
Len Brown committed
64
65
66
67
68
69
		chars_remaining = sprintf(str, "%s %s %08x %08x\n",
					  event.device_class ? event.
					  device_class : "<unknown>",
					  event.bus_id ? event.
					  bus_id : "<unknown>", event.type,
					  event.data);
Linus Torvalds's avatar
Linus Torvalds committed
70
71
72
73
74
75
76
77
		ptr = str;
	}

	if (chars_remaining < count) {
		count = chars_remaining;
	}

	if (copy_to_user(buffer, ptr, count))
78
		return -EFAULT;
Linus Torvalds's avatar
Linus Torvalds committed
79
80
81
82
83

	*ppos += count;
	chars_remaining -= count;
	ptr += count;

84
	return count;
Linus Torvalds's avatar
Linus Torvalds committed
85
86
}

Len Brown's avatar
Len Brown committed
87
static int acpi_system_close_event(struct inode *inode, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
88
{
Len Brown's avatar
Len Brown committed
89
	spin_lock_irq(&acpi_system_event_lock);
Linus Torvalds's avatar
Linus Torvalds committed
90
	event_is_open = 0;
Len Brown's avatar
Len Brown committed
91
	spin_unlock_irq(&acpi_system_event_lock);
Linus Torvalds's avatar
Linus Torvalds committed
92
93
94
	return 0;
}

Len Brown's avatar
Len Brown committed
95
static unsigned int acpi_system_poll_event(struct file *file, poll_table * wait)
Linus Torvalds's avatar
Linus Torvalds committed
96
97
98
99
100
101
102
{
	poll_wait(file, &acpi_bus_event_queue, wait);
	if (!list_empty(&acpi_bus_event_list))
		return POLLIN | POLLRDNORM;
	return 0;
}

103
static const struct file_operations acpi_system_event_ops = {
Len Brown's avatar
Len Brown committed
104
105
106
107
	.open = acpi_system_open_event,
	.read = acpi_system_read_event,
	.release = acpi_system_close_event,
	.poll = acpi_system_poll_event,
Linus Torvalds's avatar
Linus Torvalds committed
108
109
};

110
#ifdef CONFIG_NET
Adrian Bunk's avatar
Adrian Bunk committed
111
static unsigned int acpi_event_seqnum;
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
struct acpi_genl_event {
	acpi_device_class device_class;
	char bus_id[15];
	u32 type;
	u32 data;
};

/* attributes of acpi_genl_family */
enum {
	ACPI_GENL_ATTR_UNSPEC,
	ACPI_GENL_ATTR_EVENT,	/* ACPI event info needed by user space */
	__ACPI_GENL_ATTR_MAX,
};
#define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1)

/* commands supported by the acpi_genl_family */
enum {
	ACPI_GENL_CMD_UNSPEC,
	ACPI_GENL_CMD_EVENT,	/* kernel->user notifications for ACPI events */
	__ACPI_GENL_CMD_MAX,
};
#define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1)

135
136
137
#define ACPI_GENL_FAMILY_NAME		"acpi_event"
#define ACPI_GENL_VERSION		0x01
#define ACPI_GENL_MCAST_GROUP_NAME 	"acpi_mc_group"
138
139
140

static struct genl_family acpi_event_genl_family = {
	.id = GENL_ID_GENERATE,
141
	.name = ACPI_GENL_FAMILY_NAME,
142
143
144
145
	.version = ACPI_GENL_VERSION,
	.maxattr = ACPI_GENL_ATTR_MAX,
};

146
147
static struct genl_multicast_group acpi_event_mcgrp = {
	.name = ACPI_GENL_MCAST_GROUP_NAME,
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
};

int acpi_bus_generate_genetlink_event(struct acpi_device *device,
				      u8 type, int data)
{
	struct sk_buff *skb;
	struct nlattr *attr;
	struct acpi_genl_event *event;
	void *msg_header;
	int size;
	int result;

	/* allocate memory */
	size = nla_total_size(sizeof(struct acpi_genl_event)) +
	    nla_total_size(0);

	skb = genlmsg_new(size, GFP_ATOMIC);
	if (!skb)
		return -ENOMEM;

	/* add the genetlink message header */
	msg_header = genlmsg_put(skb, 0, acpi_event_seqnum++,
				 &acpi_event_genl_family, 0,
				 ACPI_GENL_CMD_EVENT);
	if (!msg_header) {
		nlmsg_free(skb);
		return -ENOMEM;
	}

	/* fill the data */
	attr =
	    nla_reserve(skb, ACPI_GENL_ATTR_EVENT,
			sizeof(struct acpi_genl_event));
	if (!attr) {
		nlmsg_free(skb);
		return -EINVAL;
	}

	event = nla_data(attr);
	if (!event) {
		nlmsg_free(skb);
		return -EINVAL;
	}

	memset(event, 0, sizeof(struct acpi_genl_event));

	strcpy(event->device_class, device->pnp.device_class);
	strcpy(event->bus_id, device->dev.bus_id);
	event->type = type;
	event->data = data;

	/* send multicast genetlink message */
	result = genlmsg_end(skb, msg_header);
	if (result < 0) {
		nlmsg_free(skb);
		return result;
	}

	result =
207
	    genlmsg_multicast(skb, 0, acpi_event_mcgrp.id, GFP_ATOMIC);
208
209
210
211
212
213
214
215
216
217
218
219
220
221
	if (result)
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Failed to send a Genetlink message!\n"));
	return 0;
}

static int acpi_event_genetlink_init(void)
{
	int result;

	result = genl_register_family(&acpi_event_genl_family);
	if (result)
		return result;

222
223
	result = genl_register_mc_group(&acpi_event_genl_family,
					&acpi_event_mcgrp);
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
	if (result)
		genl_unregister_family(&acpi_event_genl_family);

	return result;
}

#else
int acpi_bus_generate_genetlink_event(struct acpi_device *device, u8 type,
				      int data)
{
	return 0;
}

static int acpi_event_genetlink_init(void)
{
	return -ENODEV;
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
243
244
static int __init acpi_event_init(void)
{
Len Brown's avatar
Len Brown committed
245
	struct proc_dir_entry *entry;
Linus Torvalds's avatar
Linus Torvalds committed
246
247
248
	int error = 0;

	if (acpi_disabled)
249
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
250

251
252
253
254
255
256
	/* create genetlink for acpi event */
	error = acpi_event_genetlink_init();
	if (error)
		printk(KERN_WARNING PREFIX
		       "Failed to create genetlink family for ACPI event\n");

Linus Torvalds's avatar
Linus Torvalds committed
257
258
259
260
	/* 'event' [R] */
	entry = create_proc_entry("event", S_IRUSR, acpi_root_dir);
	if (entry)
		entry->proc_fops = &acpi_system_event_ops;
261
262
263
264
	else
		return -ENODEV;

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
265
266
}

267
fs_initcall(acpi_event_init);