1 | /* $NetBSD: wmi_hp.c,v 1.9 2015/04/23 23:23:00 pgoyette Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Jukka Ruohonen. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 | * SUCH DAMAGE. |
31 | */ |
32 | |
33 | /*- |
34 | * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de> |
35 | * All rights reserved. |
36 | * |
37 | * Redistribution and use in source and binary forms, with or without |
38 | * modification, are permitted provided that the following conditions |
39 | * are met: |
40 | * 1. Redistributions of source code must retain the above copyright |
41 | * notice, this list of conditions and the following disclaimer. |
42 | * 2. Redistributions in binary form must reproduce the above copyright |
43 | * notice, this list of conditions and the following disclaimer in the |
44 | * documentation and/or other materials provided with the distribution. |
45 | * |
46 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
47 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
48 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
49 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
50 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
51 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
52 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
53 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
54 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
55 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
56 | * SUCH DAMAGE. |
57 | */ |
58 | |
59 | #include <sys/cdefs.h> |
60 | __KERNEL_RCSID(0, "$NetBSD: wmi_hp.c,v 1.9 2015/04/23 23:23:00 pgoyette Exp $" ); |
61 | |
62 | #include <sys/param.h> |
63 | #include <sys/device.h> |
64 | #include <sys/kmem.h> |
65 | #include <sys/module.h> |
66 | |
67 | #include <dev/acpi/acpireg.h> |
68 | #include <dev/acpi/acpivar.h> |
69 | #include <dev/acpi/wmi/wmi_acpivar.h> |
70 | |
71 | #include <dev/sysmon/sysmonvar.h> |
72 | |
73 | #include <sys/sysctl.h> |
74 | |
75 | /* |
76 | * HP CMI whitepaper: |
77 | * http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf |
78 | */ |
79 | |
80 | #define _COMPONENT ACPI_RESOURCE_COMPONENT |
81 | ACPI_MODULE_NAME ("wmi_hp" ) |
82 | |
83 | #define WMI_HP_METHOD_ARG_READ 0x01 |
84 | #define WMI_HP_METHOD_ARG_WRITE 0x02 |
85 | #define WMI_HP_METHOD_ARG_WRITE_SIZE 0x04 |
86 | #define WMI_HP_METHOD_ARG_MAGIC 0x55434553 |
87 | #define WMI_HP_METHOD_ARG_SIZE 0x05 * sizeof(uint32_t) |
88 | |
89 | #define WMI_HP_METHOD_CMD_DISPLAY 0x01 |
90 | #define WMI_HP_METHOD_CMD_HDDTEMP 0x02 |
91 | #define WMI_HP_METHOD_CMD_ALS 0x03 |
92 | #define WMI_HP_METHOD_CMD_DOCK 0x04 |
93 | #define WMI_HP_METHOD_CMD_SWITCH 0x05 |
94 | #define WMI_HP_METHOD_CMD_HOTKEY 0x0C |
95 | |
96 | #define WMI_HP_EVENT_DOCK 0x01 |
97 | #define WMI_HP_EVENT_HOTKEY 0x04 |
98 | #define WMI_HP_EVENT_SWITCH 0x05 |
99 | /* WMI_HP_EVENT_UNKNOWN 0xXX */ |
100 | |
101 | #define WMI_HP_HOTKEY_BRIGHTNESS_UP 0x02 |
102 | #define WMI_HP_HOTKEY_BRIGHTNESS_DOWN 0x03 |
103 | #define WMI_HP_HOTKEY_PROG1 0x20e6 |
104 | #define WMI_HP_HOTKEY_MEDIA1 0x20e8 |
105 | #define WMI_HP_HOTKEY_MEDIA2 0x2142 |
106 | #define WMI_HP_HOTKEY_INFO 0x213b |
107 | #define WMI_HP_HOTKEY_DIRECTION 0x2169 |
108 | #define WMI_HP_HOTKEY_HELP 0x231b |
109 | /* WMI_HP_HOTKEY_UNKNOWN 0xXX */ |
110 | |
111 | #define WMI_HP_SWITCH_WLAN 0x01 |
112 | #define WMI_HP_SWITCH_BT 0x02 |
113 | #define WMI_HP_SWITCH_WWAN 0x04 |
114 | |
115 | #define WMI_HP_SWITCH_ARG_WLAN_OFF 0x100 |
116 | #define WMI_HP_SWITCH_ARG_WLAN_ON 0x101 |
117 | #define WMI_HP_SWITCH_ARG_BT_OFF 0x200 |
118 | #define WMI_HP_SWITCH_ARG_BT_ON 0x202 |
119 | #define WMI_HP_SWITCH_ARG_WWAN_OFF 0x400 |
120 | #define WMI_HP_SWITCH_ARG_WWAN_ON 0x404 |
121 | |
122 | #define WMI_HP_SWITCH_MASK_WLAN_ONAIR __BIT(8) |
123 | #define WMI_HP_SWITCH_MASK_WLAN_ENABLED __BIT(9) |
124 | #define WMI_HP_SWITCH_MASK_WLAN_RADIO __BIT(11) |
125 | #define WMI_HP_SWITCH_MASK_BT_ONAIR __BIT(16) |
126 | #define WMI_HP_SWITCH_MASK_BT_ENABLED __BIT(17) |
127 | #define WMI_HP_SWITCH_MASK_BT_RADIO __BIT(19) |
128 | #define WMI_HP_SWITCH_MASK_WWAN_ONAIR __BIT(24) |
129 | #define WMI_HP_SWITCH_MASK_WWAN_ENABLED __BIT(25) |
130 | #define WMI_HP_SWITCH_MASK_WWAN_RADIO __BIT(27) |
131 | |
132 | #define WMI_HP_GUID_EVENT "95F24279-4D7B-4334-9387-ACCDC67EF61C" |
133 | #define WMI_HP_GUID_METHOD "5FB7F034-2C63-45E9-BE91-3D44E2C707E4" |
134 | #define WMI_HP_GUID_CMI "2D114B49-2DFB-4130-B8FE-4A3C09E75133" |
135 | |
136 | #define WMI_HP_SENSOR_WLAN 0 |
137 | #define WMI_HP_SENSOR_BT 1 |
138 | #define WMI_HP_SENSOR_WWAN 2 |
139 | #define WMI_HP_SENSOR_HDDTEMP 3 |
140 | #define WMI_HP_SENSOR_DISPLAY 4 |
141 | #define WMI_HP_SENSOR_DOCK 5 |
142 | #define WMI_HP_SENSOR_COUNT 6 |
143 | #define WMI_HP_SENSOR_SIZE WMI_HP_SENSOR_COUNT * sizeof(envsys_data_t) |
144 | |
145 | #define ACPI_HP_CMI_PATHS 0x01 |
146 | #define ACPI_HP_CMI_ENUMS 0x02 |
147 | #define ACPI_HP_CMI_FLAGS 0x04 |
148 | #define ACPI_HP_CMI_MAX_INSTANCE 0x08 |
149 | |
150 | struct wmi_hp_softc { |
151 | device_t sc_dev; |
152 | device_t sc_parent; |
153 | struct sysmon_envsys *sc_sme; |
154 | envsys_data_t *sc_sensor; |
155 | uint32_t *sc_arg; |
156 | uint32_t sc_val; |
157 | }; |
158 | |
159 | static int wmi_hp_match(device_t, cfdata_t, void *); |
160 | static void wmi_hp_attach(device_t, device_t, void *); |
161 | static int wmi_hp_detach(device_t, int); |
162 | static bool wmi_hp_suspend(device_t, const pmf_qual_t *); |
163 | static bool wmi_hp_resume(device_t, const pmf_qual_t *); |
164 | static void wmi_hp_notify_handler(ACPI_HANDLE, uint32_t, void *); |
165 | static void wmi_hp_hotkey(void *); |
166 | static bool wmi_hp_method(struct wmi_hp_softc *); |
167 | static bool wmi_hp_method_read(struct wmi_hp_softc *, uint8_t); |
168 | static bool wmi_hp_method_write(struct wmi_hp_softc *, uint8_t, uint32_t); |
169 | |
170 | static void wmi_hp_sensor_init(struct wmi_hp_softc *); |
171 | static void wmi_hp_sensor_switch_update(void *); |
172 | static void wmi_hp_sensor_refresh(struct sysmon_envsys *, envsys_data_t *); |
173 | |
174 | static void sysctl_wmi_hp_setup(struct wmi_hp_softc *); |
175 | static int sysctl_wmi_hp_set_als(SYSCTLFN_PROTO); |
176 | static struct sysctllog *wmihp_sysctllog = NULL; |
177 | static int wmihp_als = 0; |
178 | static struct wmi_hp_softc *wmi_hp_sc = NULL; /* XXX */ |
179 | |
180 | CFATTACH_DECL_NEW(wmihp, sizeof(struct wmi_hp_softc), |
181 | wmi_hp_match, wmi_hp_attach, wmi_hp_detach, NULL); |
182 | |
183 | static int |
184 | wmi_hp_match(device_t parent, cfdata_t match, void *aux) |
185 | { |
186 | return acpi_wmi_guid_match(parent, WMI_HP_GUID_METHOD); |
187 | } |
188 | |
189 | static void |
190 | wmi_hp_attach(device_t parent, device_t self, void *aux) |
191 | { |
192 | struct wmi_hp_softc *sc = device_private(self); |
193 | ACPI_STATUS rv = AE_ERROR; |
194 | |
195 | sc->sc_dev = self; |
196 | sc->sc_parent = parent; |
197 | |
198 | sc->sc_sme = NULL; |
199 | sc->sc_sensor = NULL; |
200 | |
201 | sc->sc_arg = kmem_alloc(WMI_HP_METHOD_ARG_SIZE, KM_SLEEP); |
202 | |
203 | if (sc->sc_arg == NULL) |
204 | return; |
205 | |
206 | aprint_naive("\n" ); |
207 | aprint_normal(": HP WMI mappings\n" ); |
208 | |
209 | (void)pmf_device_register(sc->sc_dev, wmi_hp_suspend, wmi_hp_resume); |
210 | |
211 | if (acpi_wmi_guid_match(parent, WMI_HP_GUID_EVENT) != 0) |
212 | rv = acpi_wmi_event_register(parent, wmi_hp_notify_handler); |
213 | |
214 | if (ACPI_FAILURE(rv)) |
215 | return; |
216 | |
217 | sc->sc_sensor = kmem_alloc(WMI_HP_SENSOR_SIZE, KM_SLEEP); |
218 | |
219 | if (sc->sc_sensor == NULL) |
220 | return; |
221 | |
222 | wmi_hp_sc = sc; /* XXX Can I pass sc as a cookie to sysctl? */ |
223 | wmi_hp_sensor_init(sc); |
224 | sysctl_wmi_hp_setup(sc); |
225 | } |
226 | |
227 | static int |
228 | wmi_hp_detach(device_t self, int flags) |
229 | { |
230 | struct wmi_hp_softc *sc = device_private(self); |
231 | device_t parent = sc->sc_parent; |
232 | |
233 | (void)acpi_wmi_event_deregister(parent); |
234 | |
235 | if (sc->sc_sme != NULL) |
236 | sysmon_envsys_unregister(sc->sc_sme); |
237 | |
238 | if (sc->sc_sensor != NULL) |
239 | kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); |
240 | |
241 | if (sc->sc_arg != NULL) |
242 | kmem_free(sc->sc_arg, WMI_HP_METHOD_ARG_SIZE); |
243 | |
244 | pmf_device_deregister(self); |
245 | |
246 | if (wmihp_sysctllog != NULL) |
247 | sysctl_teardown(&wmihp_sysctllog); |
248 | wmihp_sysctllog = NULL; |
249 | wmi_hp_sc = NULL; |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | static bool |
255 | wmi_hp_suspend(device_t self, const pmf_qual_t *qual) |
256 | { |
257 | struct wmi_hp_softc *sc = device_private(self); |
258 | device_t parent = sc->sc_parent; |
259 | |
260 | if (sc->sc_sensor != NULL) |
261 | (void)acpi_wmi_event_deregister(parent); |
262 | |
263 | return true; |
264 | } |
265 | |
266 | static bool |
267 | wmi_hp_resume(device_t self, const pmf_qual_t *qual) |
268 | { |
269 | struct wmi_hp_softc *sc = device_private(self); |
270 | device_t parent = sc->sc_parent; |
271 | |
272 | if (sc->sc_sensor != NULL) |
273 | (void)acpi_wmi_event_register(parent, wmi_hp_notify_handler); |
274 | |
275 | return true; |
276 | } |
277 | |
278 | static void |
279 | wmi_hp_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) |
280 | { |
281 | static const int handler = OSL_NOTIFY_HANDLER; |
282 | struct wmi_hp_softc *sc; |
283 | device_t self = aux; |
284 | ACPI_OBJECT *obj; |
285 | ACPI_BUFFER buf; |
286 | ACPI_STATUS rv; |
287 | uint32_t val; |
288 | |
289 | buf.Pointer = NULL; |
290 | |
291 | sc = device_private(self); |
292 | rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf); |
293 | |
294 | if (ACPI_FAILURE(rv)) |
295 | goto out; |
296 | |
297 | obj = buf.Pointer; |
298 | |
299 | if (obj->Type != ACPI_TYPE_BUFFER) { |
300 | rv = AE_TYPE; |
301 | goto out; |
302 | } |
303 | |
304 | if (obj->Buffer.Length != 8) { |
305 | rv = AE_LIMIT; |
306 | goto out; |
307 | } |
308 | |
309 | val = *((uint8_t *)obj->Buffer.Pointer); |
310 | |
311 | if (val == 0x00) { |
312 | rv = AE_BAD_DATA; |
313 | goto out; |
314 | } |
315 | |
316 | switch (val) { |
317 | |
318 | case WMI_HP_EVENT_SWITCH: |
319 | rv = AcpiOsExecute(handler, wmi_hp_sensor_switch_update, self); |
320 | break; |
321 | |
322 | case WMI_HP_EVENT_HOTKEY: |
323 | rv = AcpiOsExecute(handler, wmi_hp_hotkey, self); |
324 | break; |
325 | |
326 | case WMI_HP_EVENT_DOCK: /* FALLTHROUGH */ |
327 | |
328 | default: |
329 | aprint_debug_dev(sc->sc_dev, "unknown event 0x%02X\n" , evt); |
330 | break; |
331 | } |
332 | |
333 | out: |
334 | if (buf.Pointer != NULL) |
335 | ACPI_FREE(buf.Pointer); |
336 | |
337 | if (ACPI_FAILURE(rv)) |
338 | aprint_error_dev(sc->sc_dev, "failed to get data for " |
339 | "event 0x%02X: %s\n" , evt, AcpiFormatException(rv)); |
340 | } |
341 | |
342 | static void |
343 | wmi_hp_hotkey(void *aux) |
344 | { |
345 | struct wmi_hp_softc *sc; |
346 | device_t self = aux; |
347 | |
348 | sc = device_private(self); |
349 | |
350 | if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HOTKEY) != true) |
351 | return; |
352 | |
353 | switch (sc->sc_val) { |
354 | |
355 | case WMI_HP_HOTKEY_BRIGHTNESS_UP: |
356 | pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); |
357 | break; |
358 | |
359 | case WMI_HP_HOTKEY_BRIGHTNESS_DOWN: |
360 | pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); |
361 | break; |
362 | |
363 | case WMI_HP_HOTKEY_PROG1: |
364 | aprint_debug_dev(self, "PROG1 hotkey pressed\n" ); |
365 | break; |
366 | case WMI_HP_HOTKEY_MEDIA1: |
367 | aprint_debug_dev(self, "MEDIA1 hotkey pressed\n" ); |
368 | break; |
369 | case WMI_HP_HOTKEY_MEDIA2: |
370 | aprint_debug_dev(self, "MEDIA2 hotkey pressed\n" ); |
371 | break; |
372 | case WMI_HP_HOTKEY_INFO: |
373 | aprint_debug_dev(self, "INFO hotkey pressed\n" ); |
374 | break; |
375 | case WMI_HP_HOTKEY_DIRECTION: |
376 | aprint_debug_dev(self, "DIRECTION hotkey pressed\n" ); |
377 | break; |
378 | case WMI_HP_HOTKEY_HELP: |
379 | aprint_debug_dev(self, "HELP hotkey pressed\n" ); |
380 | break; |
381 | default: |
382 | aprint_debug_dev(self, "unknown hotkey 0x%02x\n" , sc->sc_val); |
383 | break; |
384 | } |
385 | } |
386 | |
387 | static bool |
388 | wmi_hp_method(struct wmi_hp_softc *sc) |
389 | { |
390 | ACPI_BUFFER ibuf, obuf; |
391 | ACPI_STATUS rv = AE_OK; |
392 | ACPI_OBJECT *obj; |
393 | uint32_t cmd, *val; |
394 | |
395 | cmd = sc->sc_arg[2]; |
396 | |
397 | KDASSERT(cmd != 0); |
398 | KDASSERT(sc->sc_arg[0] == WMI_HP_METHOD_ARG_MAGIC); |
399 | |
400 | obuf.Pointer = NULL; |
401 | ibuf.Pointer = sc->sc_arg; |
402 | ibuf.Length = WMI_HP_METHOD_ARG_SIZE; |
403 | |
404 | rv = acpi_wmi_method(sc->sc_parent, |
405 | WMI_HP_GUID_METHOD, 0, 3, &ibuf, &obuf); |
406 | |
407 | if (ACPI_FAILURE(rv)) |
408 | goto out; |
409 | |
410 | obj = obuf.Pointer; |
411 | |
412 | if (obj->Type != ACPI_TYPE_BUFFER) { |
413 | rv = AE_TYPE; |
414 | goto out; |
415 | } |
416 | |
417 | /* |
418 | * val[0] unknown |
419 | * val[1] error code |
420 | * val[2] return value |
421 | */ |
422 | val = (uint32_t *)obj->Buffer.Pointer; |
423 | |
424 | sc->sc_val = val[2]; |
425 | |
426 | switch (val[1]) { |
427 | case 0: /* Ok. */ |
428 | break; |
429 | case 2: /* wrong signature */ |
430 | rv = AE_ERROR; |
431 | aprint_debug_dev(sc->sc_dev, "wrong signature " |
432 | "(cmd = 0x%02X): %s\n" , cmd, AcpiFormatException(rv)); |
433 | break; |
434 | case 3: /* unknown command */ |
435 | rv = AE_ERROR; |
436 | aprint_debug_dev(sc->sc_dev, "unknown command " |
437 | "(cmd = 0x%02X): %s\n" , cmd, AcpiFormatException(rv)); |
438 | break; |
439 | case 4: /* unknown command type */ |
440 | rv = AE_ERROR; |
441 | aprint_debug_dev(sc->sc_dev, "unknown command type " |
442 | "(cmd = 0x%02X): %s\n" , cmd, AcpiFormatException(rv)); |
443 | break; |
444 | case 5: /* invalid parameters */ |
445 | rv = AE_ERROR; |
446 | aprint_debug_dev(sc->sc_dev, "invalid parameters " |
447 | "(cmd = 0x%02X): %s\n" , cmd, AcpiFormatException(rv)); |
448 | break; |
449 | default: /* unknown error */ |
450 | rv = AE_ERROR; |
451 | aprint_debug_dev(sc->sc_dev, "unknown error " |
452 | "(cmd = 0x%02X): %s\n" , cmd, AcpiFormatException(rv)); |
453 | break; |
454 | } |
455 | out: |
456 | if (obuf.Pointer != NULL) |
457 | ACPI_FREE(obuf.Pointer); |
458 | |
459 | if (ACPI_FAILURE(rv)) { |
460 | aprint_debug_dev(sc->sc_dev, "failed to evaluate method " |
461 | "(cmd = 0x%02X): %s\n" , cmd, AcpiFormatException(rv)); |
462 | return false; |
463 | } |
464 | |
465 | return true; |
466 | } |
467 | |
468 | static bool |
469 | wmi_hp_method_read(struct wmi_hp_softc *sc, uint8_t cmd) |
470 | { |
471 | |
472 | sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; |
473 | sc->sc_arg[1] = WMI_HP_METHOD_ARG_READ; |
474 | sc->sc_arg[2] = cmd; |
475 | sc->sc_arg[3] = 0; |
476 | sc->sc_arg[4] = 0; |
477 | |
478 | return wmi_hp_method(sc); |
479 | } |
480 | |
481 | static bool |
482 | wmi_hp_method_write(struct wmi_hp_softc *sc, uint8_t cmd, uint32_t val) |
483 | { |
484 | |
485 | sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC; |
486 | sc->sc_arg[1] = WMI_HP_METHOD_ARG_WRITE; |
487 | sc->sc_arg[2] = cmd; |
488 | sc->sc_arg[3] = WMI_HP_METHOD_ARG_WRITE_SIZE; |
489 | sc->sc_arg[4] = val; |
490 | |
491 | return wmi_hp_method(sc); |
492 | } |
493 | |
494 | |
495 | static void |
496 | wmi_hp_switch_init(struct wmi_hp_softc *sc) |
497 | { |
498 | int i, sensor[3]; |
499 | |
500 | const char desc[][ENVSYS_DESCLEN] = { |
501 | "wireless" , "bluetooth" , "mobile" |
502 | }; |
503 | |
504 | if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) |
505 | return; |
506 | |
507 | sensor[0] = WMI_HP_SWITCH_WLAN; |
508 | sensor[1] = WMI_HP_SWITCH_BT; |
509 | sensor[2] = WMI_HP_SWITCH_WWAN; |
510 | |
511 | CTASSERT(WMI_HP_SENSOR_WLAN == 0); |
512 | CTASSERT(WMI_HP_SENSOR_BT == 1); |
513 | CTASSERT(WMI_HP_SENSOR_WWAN == 2); |
514 | |
515 | for (i = 0; i < 3; i++) { |
516 | |
517 | if ((sc->sc_val & sensor[i]) == 0) |
518 | continue; |
519 | |
520 | (void)strlcpy(sc->sc_sensor[i].desc, desc[i], ENVSYS_DESCLEN); |
521 | |
522 | sc->sc_sensor[i].state = ENVSYS_SINVALID; |
523 | sc->sc_sensor[i].units = ENVSYS_INDICATOR; |
524 | |
525 | if (sysmon_envsys_sensor_attach(sc->sc_sme, |
526 | &sc->sc_sensor[i]) != 0) |
527 | break; |
528 | } |
529 | } |
530 | |
531 | static void |
532 | wmi_hp_sensor_init(struct wmi_hp_softc *sc) |
533 | { |
534 | int sensor; |
535 | |
536 | KDASSERT(sc->sc_sme == NULL); |
537 | KDASSERT(sc->sc_sensor != NULL); |
538 | |
539 | (void)memset(sc->sc_sensor, 0, WMI_HP_SENSOR_SIZE); |
540 | |
541 | sc->sc_sme = sysmon_envsys_create(); |
542 | |
543 | wmi_hp_switch_init(sc); |
544 | |
545 | if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HDDTEMP) == true) { |
546 | sensor = WMI_HP_SENSOR_HDDTEMP; |
547 | (void)strlcpy(sc->sc_sensor[sensor].desc, "hddtemp" , |
548 | ENVSYS_DESCLEN); |
549 | sc->sc_sensor[sensor].state = ENVSYS_SVALID; |
550 | sc->sc_sensor[sensor].units = ENVSYS_STEMP; |
551 | sc->sc_sensor[sensor].value_cur = |
552 | sc->sc_val * 1000000 + 273150000; |
553 | |
554 | sysmon_envsys_sensor_attach(sc->sc_sme, |
555 | &sc->sc_sensor[sensor]); |
556 | } |
557 | |
558 | if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_DISPLAY) == true) { |
559 | sensor = WMI_HP_SENSOR_DISPLAY; |
560 | (void)strlcpy(sc->sc_sensor[sensor].desc, "display" , |
561 | ENVSYS_DESCLEN); |
562 | sc->sc_sensor[sensor].state = ENVSYS_SVALID; |
563 | sc->sc_sensor[sensor].units = ENVSYS_INDICATOR; |
564 | sc->sc_sensor[sensor].value_cur = sc->sc_val; |
565 | |
566 | sysmon_envsys_sensor_attach(sc->sc_sme, |
567 | &sc->sc_sensor[sensor]); |
568 | } |
569 | |
570 | if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_DOCK) == true) { |
571 | sensor = WMI_HP_SENSOR_DOCK; |
572 | (void)strlcpy(sc->sc_sensor[sensor].desc, "docking station" , |
573 | ENVSYS_DESCLEN); |
574 | sc->sc_sensor[sensor].state = ENVSYS_SVALID; |
575 | sc->sc_sensor[sensor].units = ENVSYS_INDICATOR; |
576 | sc->sc_sensor[sensor].value_cur = sc->sc_val; |
577 | |
578 | sysmon_envsys_sensor_attach(sc->sc_sme, |
579 | &sc->sc_sensor[sensor]); |
580 | } |
581 | |
582 | sc->sc_sme->sme_cookie = sc; |
583 | sc->sc_sme->sme_refresh = wmi_hp_sensor_refresh; |
584 | sc->sc_sme->sme_name = device_xname(sc->sc_dev); |
585 | |
586 | if (sysmon_envsys_register(sc->sc_sme) != 0) |
587 | goto fail; |
588 | |
589 | wmi_hp_sensor_switch_update(sc->sc_dev); |
590 | |
591 | return; |
592 | |
593 | fail: |
594 | aprint_debug_dev(sc->sc_dev, "failed to initialize sysmon\n" ); |
595 | |
596 | sysmon_envsys_destroy(sc->sc_sme); |
597 | kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE); |
598 | |
599 | sc->sc_sme = NULL; |
600 | sc->sc_sensor = NULL; |
601 | } |
602 | |
603 | static void |
604 | wmi_hp_sensor_switch_update(void *aux) |
605 | { |
606 | struct wmi_hp_softc *sc; |
607 | device_t self = aux; |
608 | |
609 | sc = device_private(self); |
610 | |
611 | if (sc->sc_sme == NULL || sc->sc_sensor == NULL) |
612 | return; |
613 | |
614 | if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) { |
615 | sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SINVALID; |
616 | sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SINVALID; |
617 | sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SINVALID; |
618 | return; |
619 | } |
620 | |
621 | if ((sc->sc_val & WMI_HP_SWITCH_WLAN) != 0) { |
622 | sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 0; |
623 | |
624 | if ((sc->sc_val & WMI_HP_SWITCH_MASK_WLAN_ONAIR) != 0) |
625 | sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 1; |
626 | |
627 | sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SVALID; |
628 | } |
629 | |
630 | if ((sc->sc_val & WMI_HP_SWITCH_BT) != 0) { |
631 | sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 0; |
632 | |
633 | if ((sc->sc_val & WMI_HP_SWITCH_MASK_BT_ONAIR) != 0) |
634 | sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 1; |
635 | |
636 | sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SVALID; |
637 | } |
638 | |
639 | if ((sc->sc_val & WMI_HP_SWITCH_WWAN) != 0) { |
640 | sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 0; |
641 | |
642 | if ((sc->sc_val & WMI_HP_SWITCH_MASK_WWAN_ONAIR) != 0) |
643 | sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 1; |
644 | |
645 | sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SVALID; |
646 | } |
647 | } |
648 | |
649 | static void |
650 | wmi_hp_sensor_read(struct wmi_hp_softc *sc, envsys_data_t *sensor, int cmd) |
651 | { |
652 | if (wmi_hp_method_read(sc, cmd) == true) { |
653 | sensor->state = ENVSYS_SVALID; |
654 | sensor->value_cur = sc->sc_val; |
655 | } else { |
656 | sensor->state = ENVSYS_SINVALID; |
657 | } |
658 | } |
659 | |
660 | static void |
661 | wmi_hp_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) |
662 | { |
663 | struct wmi_hp_softc *sc = sme->sme_cookie; |
664 | envsys_data_t *sensor; |
665 | |
666 | sensor = &sc->sc_sensor[WMI_HP_SENSOR_HDDTEMP]; |
667 | wmi_hp_sensor_read(sc, sensor, WMI_HP_METHOD_CMD_HDDTEMP); |
668 | if (sensor->state == ENVSYS_SVALID) { |
669 | sensor->value_cur = sensor->value_cur * 1000000 + |
670 | 273150000; |
671 | } |
672 | |
673 | wmi_hp_sensor_read(sc, &sc->sc_sensor[WMI_HP_SENSOR_DISPLAY], |
674 | WMI_HP_METHOD_CMD_DISPLAY); |
675 | |
676 | wmi_hp_sensor_read(sc, &sc->sc_sensor[WMI_HP_SENSOR_DOCK], |
677 | WMI_HP_METHOD_CMD_DOCK); |
678 | } |
679 | |
680 | MODULE(MODULE_CLASS_DRIVER, wmihp, "acpiwmi,sysmon_envsys" ); |
681 | |
682 | #ifdef _MODULE |
683 | #include "ioconf.c" |
684 | #endif |
685 | |
686 | static int |
687 | wmihp_modcmd(modcmd_t cmd, void *aux) |
688 | { |
689 | int rv = 0; |
690 | |
691 | switch (cmd) { |
692 | |
693 | case MODULE_CMD_INIT: |
694 | |
695 | #ifdef _MODULE |
696 | rv = config_init_component(cfdriver_ioconf_wmihp, |
697 | cfattach_ioconf_wmihp, cfdata_ioconf_wmihp); |
698 | #endif |
699 | break; |
700 | |
701 | case MODULE_CMD_FINI: |
702 | |
703 | #ifdef _MODULE |
704 | rv = config_fini_component(cfdriver_ioconf_wmihp, |
705 | cfattach_ioconf_wmihp, cfdata_ioconf_wmihp); |
706 | #endif |
707 | break; |
708 | |
709 | default: |
710 | rv = ENOTTY; |
711 | } |
712 | |
713 | return rv; |
714 | } |
715 | |
716 | static int |
717 | sysctl_wmi_hp_set_als(SYSCTLFN_ARGS) |
718 | { |
719 | struct sysctlnode node; |
720 | int err; |
721 | int als = wmihp_als; |
722 | struct wmi_hp_softc *sc = wmi_hp_sc; |
723 | |
724 | node = *rnode; |
725 | node.sysctl_data = &als; |
726 | |
727 | err = sysctl_lookup(SYSCTLFN_CALL(&node)); |
728 | |
729 | if (err != 0 || newp == NULL) |
730 | return err;; |
731 | |
732 | if (als < 0 || als > 1) |
733 | return EINVAL; |
734 | |
735 | if (wmi_hp_method_write(sc, WMI_HP_METHOD_CMD_ALS, als) == true) { |
736 | wmihp_als = als; |
737 | return 0; |
738 | } |
739 | |
740 | return EIO; |
741 | } |
742 | |
743 | static void |
744 | sysctl_wmi_hp_setup(struct wmi_hp_softc *sc) |
745 | { |
746 | const struct sysctlnode *rnode; |
747 | int err; |
748 | |
749 | err = sysctl_createv(&wmihp_sysctllog, 0, NULL, &rnode, |
750 | CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi" , NULL, |
751 | NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); |
752 | |
753 | if (err != 0) |
754 | return; |
755 | |
756 | err = sysctl_createv(&wmihp_sysctllog, 0, &rnode, &rnode, |
757 | 0, CTLTYPE_NODE, "wmi" , SYSCTL_DESCR("ACPI HP WMI" ), |
758 | NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); |
759 | |
760 | if (err != 0) |
761 | return; |
762 | |
763 | if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_ALS) == true) { |
764 | (void)sysctl_createv(NULL, 0, &rnode, NULL, |
765 | CTLFLAG_READWRITE, CTLTYPE_BOOL, "als" , |
766 | SYSCTL_DESCR("Ambient Light Sensor" ), |
767 | sysctl_wmi_hp_set_als, 0, NULL, 0, CTL_CREATE, CTL_EOL); |
768 | } |
769 | } |
770 | |