event.c 5.8 KB
Newer Older
L
Linus Torvalds 已提交
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>
L
Linus Torvalds 已提交
16 17

#define _COMPONENT		ACPI_SYSTEM_COMPONENT
18
ACPI_MODULE_NAME("event");
L
Linus Torvalds 已提交
19 20 21

/* Global vars for handling event proc entry */
static DEFINE_SPINLOCK(acpi_system_event_lock);
L
Len Brown 已提交
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;
L
Linus Torvalds 已提交
25

L
Len Brown 已提交
26
static int acpi_system_open_event(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
27
{
P
Pavel Machek 已提交
28
	spin_lock_irq(&acpi_system_event_lock);
L
Linus Torvalds 已提交
29

P
Pavel Machek 已提交
30
	if (event_is_open)
L
Linus Torvalds 已提交
31 32 33 34
		goto out_busy;

	event_is_open = 1;

P
Pavel Machek 已提交
35
	spin_unlock_irq(&acpi_system_event_lock);
L
Linus Torvalds 已提交
36 37
	return 0;

L
Len Brown 已提交
38
      out_busy:
P
Pavel Machek 已提交
39
	spin_unlock_irq(&acpi_system_event_lock);
L
Linus Torvalds 已提交
40 41 42 43
	return -EBUSY;
}

static ssize_t
L
Len Brown 已提交
44 45
acpi_system_read_event(struct file *file, char __user * buffer, size_t count,
		       loff_t * ppos)
L
Linus Torvalds 已提交
46
{
L
Len Brown 已提交
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;
L
Linus Torvalds 已提交
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;
L
Linus Torvalds 已提交
59 60

		result = acpi_bus_receive_event(&event);
61
		if (result)
62
			return result;
L
Linus Torvalds 已提交
63

L
Len Brown 已提交
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);
L
Linus Torvalds 已提交
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;
L
Linus Torvalds 已提交
79 80 81 82 83

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

84
	return count;
L
Linus Torvalds 已提交
85 86
}

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

L
Len Brown 已提交
95
static unsigned int acpi_system_poll_event(struct file *file, poll_table * wait)
L
Linus Torvalds 已提交
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 = {
L
Len Brown 已提交
104 105 106 107
	.open = acpi_system_open_event,
	.read = acpi_system_read_event,
	.release = acpi_system_close_event,
	.poll = acpi_system_poll_event,
L
Linus Torvalds 已提交
108 109
};

110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
#ifdef CONFIG_NET
unsigned int acpi_event_seqnum;
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

L
Linus Torvalds 已提交
243 244
static int __init acpi_event_init(void)
{
L
Len Brown 已提交
245
	struct proc_dir_entry *entry;
L
Linus Torvalds 已提交
246 247 248
	int error = 0;

	if (acpi_disabled)
249
		return 0;
L
Linus Torvalds 已提交
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");

L
Linus Torvalds 已提交
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;
L
Linus Torvalds 已提交
265 266
}

267
fs_initcall(acpi_event_init);