diff options
author | Zhang Rui <rui.zhang@intel.com> | 2007-06-18 23:40:03 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-07-03 14:50:58 -0400 |
commit | 864bdfb912e372670b5b2541dac9d273a4a7722a (patch) | |
tree | 6f3bd48b2ea92130a2f2b8add2c393cb01151ac7 /drivers/acpi/event.c | |
parent | 872aad45d6174570dd2e1defc3efee50f2cfcc72 (diff) |
ACPI: Export events via generic netlink
Upon ACPI events, send an "acpi_event" via Generic Netlink.
This is in addition to /proc/acpi/event, which remains intact for now.
Thanks to Jamal for his great help.
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/event.c')
-rw-r--r-- | drivers/acpi/event.c | 166 |
1 files changed, 159 insertions, 7 deletions
diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index 3b23562e6f92..98627b02f547 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c | |||
@@ -11,6 +11,8 @@ | |||
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <linux/poll.h> | 12 | #include <linux/poll.h> |
13 | #include <acpi/acpi_drivers.h> | 13 | #include <acpi/acpi_drivers.h> |
14 | #include <net/netlink.h> | ||
15 | #include <net/genetlink.h> | ||
14 | 16 | ||
15 | #define _COMPONENT ACPI_SYSTEM_COMPONENT | 17 | #define _COMPONENT ACPI_SYSTEM_COMPONENT |
16 | ACPI_MODULE_NAME("event"); | 18 | ACPI_MODULE_NAME("event"); |
@@ -48,7 +50,6 @@ acpi_system_read_event(struct file *file, char __user * buffer, size_t count, | |||
48 | static int chars_remaining = 0; | 50 | static int chars_remaining = 0; |
49 | static char *ptr; | 51 | static char *ptr; |
50 | 52 | ||
51 | |||
52 | if (!chars_remaining) { | 53 | if (!chars_remaining) { |
53 | memset(&event, 0, sizeof(struct acpi_bus_event)); | 54 | memset(&event, 0, sizeof(struct acpi_bus_event)); |
54 | 55 | ||
@@ -106,23 +107,174 @@ static const struct file_operations acpi_system_event_ops = { | |||
106 | .poll = acpi_system_poll_event, | 107 | .poll = acpi_system_poll_event, |
107 | }; | 108 | }; |
108 | 109 | ||
110 | #ifdef CONFIG_NET | ||
111 | unsigned int acpi_event_seqnum; | ||
112 | struct acpi_genl_event { | ||
113 | acpi_device_class device_class; | ||
114 | char bus_id[15]; | ||
115 | u32 type; | ||
116 | u32 data; | ||
117 | }; | ||
118 | |||
119 | /* attributes of acpi_genl_family */ | ||
120 | enum { | ||
121 | ACPI_GENL_ATTR_UNSPEC, | ||
122 | ACPI_GENL_ATTR_EVENT, /* ACPI event info needed by user space */ | ||
123 | __ACPI_GENL_ATTR_MAX, | ||
124 | }; | ||
125 | #define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1) | ||
126 | |||
127 | /* commands supported by the acpi_genl_family */ | ||
128 | enum { | ||
129 | ACPI_GENL_CMD_UNSPEC, | ||
130 | ACPI_GENL_CMD_EVENT, /* kernel->user notifications for ACPI events */ | ||
131 | __ACPI_GENL_CMD_MAX, | ||
132 | }; | ||
133 | #define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1) | ||
134 | |||
135 | #define ACPI_GENL_NAME "acpi_event" | ||
136 | #define ACPI_GENL_VERSION 0x01 | ||
137 | |||
138 | static struct genl_family acpi_event_genl_family = { | ||
139 | .id = GENL_ID_GENERATE, | ||
140 | .name = ACPI_GENL_NAME, | ||
141 | .version = ACPI_GENL_VERSION, | ||
142 | .maxattr = ACPI_GENL_ATTR_MAX, | ||
143 | }; | ||
144 | |||
145 | /* .doit: standard command callback */ | ||
146 | static int acpi_genl_cmd_event(struct sk_buff *skb, struct genl_info *info) | ||
147 | { | ||
148 | struct acpi_genl_event *event = info->userhdr; | ||
149 | |||
150 | if (!event) | ||
151 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "ACPI event: NULL\n")); | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static struct genl_ops acpi_event_genl_ops = { | ||
157 | .cmd = ACPI_GENL_CMD_EVENT, | ||
158 | .doit = acpi_genl_cmd_event, | ||
159 | }; | ||
160 | |||
161 | int acpi_bus_generate_genetlink_event(struct acpi_device *device, | ||
162 | u8 type, int data) | ||
163 | { | ||
164 | struct sk_buff *skb; | ||
165 | struct nlattr *attr; | ||
166 | struct acpi_genl_event *event; | ||
167 | void *msg_header; | ||
168 | int size; | ||
169 | int result; | ||
170 | |||
171 | /* allocate memory */ | ||
172 | size = nla_total_size(sizeof(struct acpi_genl_event)) + | ||
173 | nla_total_size(0); | ||
174 | |||
175 | skb = genlmsg_new(size, GFP_ATOMIC); | ||
176 | if (!skb) | ||
177 | return -ENOMEM; | ||
178 | |||
179 | /* add the genetlink message header */ | ||
180 | msg_header = genlmsg_put(skb, 0, acpi_event_seqnum++, | ||
181 | &acpi_event_genl_family, 0, | ||
182 | ACPI_GENL_CMD_EVENT); | ||
183 | if (!msg_header) { | ||
184 | nlmsg_free(skb); | ||
185 | return -ENOMEM; | ||
186 | } | ||
187 | |||
188 | /* fill the data */ | ||
189 | attr = | ||
190 | nla_reserve(skb, ACPI_GENL_ATTR_EVENT, | ||
191 | sizeof(struct acpi_genl_event)); | ||
192 | if (!attr) { | ||
193 | nlmsg_free(skb); | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | |||
197 | event = nla_data(attr); | ||
198 | if (!event) { | ||
199 | nlmsg_free(skb); | ||
200 | return -EINVAL; | ||
201 | } | ||
202 | |||
203 | memset(event, 0, sizeof(struct acpi_genl_event)); | ||
204 | |||
205 | strcpy(event->device_class, device->pnp.device_class); | ||
206 | strcpy(event->bus_id, device->dev.bus_id); | ||
207 | event->type = type; | ||
208 | event->data = data; | ||
209 | |||
210 | /* send multicast genetlink message */ | ||
211 | result = genlmsg_end(skb, msg_header); | ||
212 | if (result < 0) { | ||
213 | nlmsg_free(skb); | ||
214 | return result; | ||
215 | } | ||
216 | |||
217 | result = | ||
218 | genlmsg_multicast(skb, 0, acpi_event_genl_family.id, GFP_ATOMIC); | ||
219 | if (result) | ||
220 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
221 | "Failed to send a Genetlink message!\n")); | ||
222 | return 0; | ||
223 | } | ||
224 | EXPORT_SYMBOL(acpi_bus_generate_genetlink_event); | ||
225 | |||
226 | static int acpi_event_genetlink_init(void) | ||
227 | { | ||
228 | int result; | ||
229 | |||
230 | result = genl_register_family(&acpi_event_genl_family); | ||
231 | if (result) | ||
232 | return result; | ||
233 | |||
234 | result = | ||
235 | genl_register_ops(&acpi_event_genl_family, &acpi_event_genl_ops); | ||
236 | if (result) | ||
237 | genl_unregister_family(&acpi_event_genl_family); | ||
238 | |||
239 | return result; | ||
240 | } | ||
241 | |||
242 | #else | ||
243 | int acpi_bus_generate_genetlink_event(struct acpi_device *device, u8 type, | ||
244 | int data) | ||
245 | { | ||
246 | return 0; | ||
247 | } | ||
248 | EXPORT_SYMBOL(acpi_bus_generate_genetlink_event); | ||
249 | |||
250 | static int acpi_event_genetlink_init(void) | ||
251 | { | ||
252 | return -ENODEV; | ||
253 | } | ||
254 | #endif | ||
255 | |||
109 | static int __init acpi_event_init(void) | 256 | static int __init acpi_event_init(void) |
110 | { | 257 | { |
111 | struct proc_dir_entry *entry; | 258 | struct proc_dir_entry *entry; |
112 | int error = 0; | 259 | int error = 0; |
113 | 260 | ||
114 | |||
115 | if (acpi_disabled) | 261 | if (acpi_disabled) |
116 | return 0; | 262 | return 0; |
117 | 263 | ||
264 | /* create genetlink for acpi event */ | ||
265 | error = acpi_event_genetlink_init(); | ||
266 | if (error) | ||
267 | printk(KERN_WARNING PREFIX | ||
268 | "Failed to create genetlink family for ACPI event\n"); | ||
269 | |||
118 | /* 'event' [R] */ | 270 | /* 'event' [R] */ |
119 | entry = create_proc_entry("event", S_IRUSR, acpi_root_dir); | 271 | entry = create_proc_entry("event", S_IRUSR, acpi_root_dir); |
120 | if (entry) | 272 | if (entry) |
121 | entry->proc_fops = &acpi_system_event_ops; | 273 | entry->proc_fops = &acpi_system_event_ops; |
122 | else { | 274 | else |
123 | error = -ENODEV; | 275 | return -ENODEV; |
124 | } | 276 | |
125 | return error; | 277 | return 0; |
126 | } | 278 | } |
127 | 279 | ||
128 | subsys_initcall(acpi_event_init); | 280 | fs_initcall(acpi_event_init); |