1 /* $NetBSD: wmi_hp.c,v 1.11 2018/06/06 01:49:08 maya 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.11 2018/06/06 01:49:08 maya 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
wmi_hp_match(device_t parent,cfdata_t match,void * aux)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
wmi_hp_attach(device_t parent,device_t self,void * aux)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 aprint_naive("\n");
203 aprint_normal(": HP WMI mappings\n");
204
205 (void)pmf_device_register(sc->sc_dev, wmi_hp_suspend, wmi_hp_resume);
206
207 if (acpi_wmi_guid_match(parent, WMI_HP_GUID_EVENT) != 0)
208 rv = acpi_wmi_event_register(parent, wmi_hp_notify_handler);
209
210 if (ACPI_FAILURE(rv))
211 return;
212
213 sc->sc_sensor = kmem_alloc(WMI_HP_SENSOR_SIZE, KM_SLEEP);
214
215 wmi_hp_sc = sc; /* XXX Can I pass sc as a cookie to sysctl? */
216 wmi_hp_sensor_init(sc);
217 sysctl_wmi_hp_setup(sc);
218 }
219
220 static int
wmi_hp_detach(device_t self,int flags)221 wmi_hp_detach(device_t self, int flags)
222 {
223 struct wmi_hp_softc *sc = device_private(self);
224 device_t parent = sc->sc_parent;
225
226 (void)acpi_wmi_event_deregister(parent);
227
228 if (sc->sc_sme != NULL)
229 sysmon_envsys_unregister(sc->sc_sme);
230
231 if (sc->sc_sensor != NULL)
232 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE);
233
234 if (sc->sc_arg != NULL)
235 kmem_free(sc->sc_arg, WMI_HP_METHOD_ARG_SIZE);
236
237 pmf_device_deregister(self);
238
239 if (wmihp_sysctllog != NULL)
240 sysctl_teardown(&wmihp_sysctllog);
241 wmihp_sysctllog = NULL;
242 wmi_hp_sc = NULL;
243
244 return 0;
245 }
246
247 static bool
wmi_hp_suspend(device_t self,const pmf_qual_t * qual)248 wmi_hp_suspend(device_t self, const pmf_qual_t *qual)
249 {
250 struct wmi_hp_softc *sc = device_private(self);
251 device_t parent = sc->sc_parent;
252
253 if (sc->sc_sensor != NULL)
254 (void)acpi_wmi_event_deregister(parent);
255
256 return true;
257 }
258
259 static bool
wmi_hp_resume(device_t self,const pmf_qual_t * qual)260 wmi_hp_resume(device_t self, const pmf_qual_t *qual)
261 {
262 struct wmi_hp_softc *sc = device_private(self);
263 device_t parent = sc->sc_parent;
264
265 if (sc->sc_sensor != NULL)
266 (void)acpi_wmi_event_register(parent, wmi_hp_notify_handler);
267
268 return true;
269 }
270
271 static void
wmi_hp_notify_handler(ACPI_HANDLE hdl,uint32_t evt,void * aux)272 wmi_hp_notify_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux)
273 {
274 static const int handler = OSL_NOTIFY_HANDLER;
275 struct wmi_hp_softc *sc;
276 device_t self = aux;
277 ACPI_OBJECT *obj;
278 ACPI_BUFFER buf;
279 ACPI_STATUS rv;
280 uint32_t val;
281
282 buf.Pointer = NULL;
283
284 sc = device_private(self);
285 rv = acpi_wmi_event_get(sc->sc_parent, evt, &buf);
286
287 if (ACPI_FAILURE(rv))
288 goto out;
289
290 obj = buf.Pointer;
291
292 if (obj->Type != ACPI_TYPE_BUFFER) {
293 rv = AE_TYPE;
294 goto out;
295 }
296
297 if (obj->Buffer.Length != 8) {
298 rv = AE_LIMIT;
299 goto out;
300 }
301
302 val = *((uint8_t *)obj->Buffer.Pointer);
303
304 if (val == 0x00) {
305 rv = AE_BAD_DATA;
306 goto out;
307 }
308
309 switch (val) {
310
311 case WMI_HP_EVENT_SWITCH:
312 rv = AcpiOsExecute(handler, wmi_hp_sensor_switch_update, self);
313 break;
314
315 case WMI_HP_EVENT_HOTKEY:
316 rv = AcpiOsExecute(handler, wmi_hp_hotkey, self);
317 break;
318
319 case WMI_HP_EVENT_DOCK: /* FALLTHROUGH */
320
321 default:
322 aprint_debug_dev(sc->sc_dev, "unknown event 0x%02X\n", evt);
323 break;
324 }
325
326 out:
327 if (buf.Pointer != NULL)
328 ACPI_FREE(buf.Pointer);
329
330 if (ACPI_FAILURE(rv))
331 aprint_error_dev(sc->sc_dev, "failed to get data for "
332 "event 0x%02X: %s\n", evt, AcpiFormatException(rv));
333 }
334
335 static void
wmi_hp_hotkey(void * aux)336 wmi_hp_hotkey(void *aux)
337 {
338 struct wmi_hp_softc *sc;
339 device_t self = aux;
340
341 sc = device_private(self);
342
343 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HOTKEY) != true)
344 return;
345
346 switch (sc->sc_val) {
347
348 case WMI_HP_HOTKEY_BRIGHTNESS_UP:
349 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
350 break;
351
352 case WMI_HP_HOTKEY_BRIGHTNESS_DOWN:
353 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
354 break;
355
356 case WMI_HP_HOTKEY_PROG1:
357 aprint_debug_dev(self, "PROG1 hotkey pressed\n");
358 break;
359 case WMI_HP_HOTKEY_MEDIA1:
360 aprint_debug_dev(self, "MEDIA1 hotkey pressed\n");
361 break;
362 case WMI_HP_HOTKEY_MEDIA2:
363 aprint_debug_dev(self, "MEDIA2 hotkey pressed\n");
364 break;
365 case WMI_HP_HOTKEY_INFO:
366 aprint_debug_dev(self, "INFO hotkey pressed\n");
367 break;
368 case WMI_HP_HOTKEY_DIRECTION:
369 aprint_debug_dev(self, "DIRECTION hotkey pressed\n");
370 break;
371 case WMI_HP_HOTKEY_HELP:
372 aprint_debug_dev(self, "HELP hotkey pressed\n");
373 break;
374 default:
375 aprint_debug_dev(self, "unknown hotkey 0x%02x\n", sc->sc_val);
376 break;
377 }
378 }
379
380 static bool
wmi_hp_method(struct wmi_hp_softc * sc)381 wmi_hp_method(struct wmi_hp_softc *sc)
382 {
383 ACPI_BUFFER ibuf, obuf;
384 ACPI_STATUS rv = AE_OK;
385 ACPI_OBJECT *obj;
386 uint32_t cmd, *val;
387
388 cmd = sc->sc_arg[2];
389
390 KDASSERT(cmd != 0);
391 KDASSERT(sc->sc_arg[0] == WMI_HP_METHOD_ARG_MAGIC);
392
393 obuf.Pointer = NULL;
394 ibuf.Pointer = sc->sc_arg;
395 ibuf.Length = WMI_HP_METHOD_ARG_SIZE;
396
397 rv = acpi_wmi_method(sc->sc_parent,
398 WMI_HP_GUID_METHOD, 0, 3, &ibuf, &obuf);
399
400 if (ACPI_FAILURE(rv))
401 goto out;
402
403 obj = obuf.Pointer;
404
405 if (obj->Type != ACPI_TYPE_BUFFER) {
406 rv = AE_TYPE;
407 goto out;
408 }
409
410 /*
411 * val[0] unknown
412 * val[1] error code
413 * val[2] return value
414 */
415 val = (uint32_t *)obj->Buffer.Pointer;
416
417 sc->sc_val = val[2];
418
419 switch (val[1]) {
420 case 0: /* Ok. */
421 break;
422 case 2: /* wrong signature */
423 rv = AE_ERROR;
424 aprint_debug_dev(sc->sc_dev, "wrong signature "
425 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv));
426 break;
427 case 3: /* unknown command */
428 rv = AE_ERROR;
429 aprint_debug_dev(sc->sc_dev, "unknown command "
430 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv));
431 break;
432 case 4: /* unknown command type */
433 rv = AE_ERROR;
434 aprint_debug_dev(sc->sc_dev, "unknown command type "
435 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv));
436 break;
437 case 5: /* invalid parameters */
438 rv = AE_ERROR;
439 aprint_debug_dev(sc->sc_dev, "invalid parameters "
440 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv));
441 break;
442 default: /* unknown error */
443 rv = AE_ERROR;
444 aprint_debug_dev(sc->sc_dev, "unknown error "
445 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv));
446 break;
447 }
448 out:
449 if (obuf.Pointer != NULL)
450 ACPI_FREE(obuf.Pointer);
451
452 if (ACPI_FAILURE(rv)) {
453 aprint_debug_dev(sc->sc_dev, "failed to evaluate method "
454 "(cmd = 0x%02X): %s\n", cmd, AcpiFormatException(rv));
455 return false;
456 }
457
458 return true;
459 }
460
461 static bool
wmi_hp_method_read(struct wmi_hp_softc * sc,uint8_t cmd)462 wmi_hp_method_read(struct wmi_hp_softc *sc, uint8_t cmd)
463 {
464
465 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC;
466 sc->sc_arg[1] = WMI_HP_METHOD_ARG_READ;
467 sc->sc_arg[2] = cmd;
468 sc->sc_arg[3] = 0;
469 sc->sc_arg[4] = 0;
470
471 return wmi_hp_method(sc);
472 }
473
474 static bool
wmi_hp_method_write(struct wmi_hp_softc * sc,uint8_t cmd,uint32_t val)475 wmi_hp_method_write(struct wmi_hp_softc *sc, uint8_t cmd, uint32_t val)
476 {
477
478 sc->sc_arg[0] = WMI_HP_METHOD_ARG_MAGIC;
479 sc->sc_arg[1] = WMI_HP_METHOD_ARG_WRITE;
480 sc->sc_arg[2] = cmd;
481 sc->sc_arg[3] = WMI_HP_METHOD_ARG_WRITE_SIZE;
482 sc->sc_arg[4] = val;
483
484 return wmi_hp_method(sc);
485 }
486
487
488 static void
wmi_hp_switch_init(struct wmi_hp_softc * sc)489 wmi_hp_switch_init(struct wmi_hp_softc *sc)
490 {
491 int i, sensor[3];
492
493 const char desc[][ENVSYS_DESCLEN] = {
494 "wireless", "bluetooth", "mobile"
495 };
496
497 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true)
498 return;
499
500 sensor[0] = WMI_HP_SWITCH_WLAN;
501 sensor[1] = WMI_HP_SWITCH_BT;
502 sensor[2] = WMI_HP_SWITCH_WWAN;
503
504 CTASSERT(WMI_HP_SENSOR_WLAN == 0);
505 CTASSERT(WMI_HP_SENSOR_BT == 1);
506 CTASSERT(WMI_HP_SENSOR_WWAN == 2);
507
508 for (i = 0; i < 3; i++) {
509
510 if ((sc->sc_val & sensor[i]) == 0)
511 continue;
512
513 (void)strlcpy(sc->sc_sensor[i].desc, desc[i], ENVSYS_DESCLEN);
514
515 sc->sc_sensor[i].state = ENVSYS_SINVALID;
516 sc->sc_sensor[i].units = ENVSYS_INDICATOR;
517
518 if (sysmon_envsys_sensor_attach(sc->sc_sme,
519 &sc->sc_sensor[i]) != 0)
520 break;
521 }
522 }
523
524 static void
wmi_hp_sensor_init(struct wmi_hp_softc * sc)525 wmi_hp_sensor_init(struct wmi_hp_softc *sc)
526 {
527 int sensor;
528
529 KDASSERT(sc->sc_sme == NULL);
530 KDASSERT(sc->sc_sensor != NULL);
531
532 (void)memset(sc->sc_sensor, 0, WMI_HP_SENSOR_SIZE);
533
534 sc->sc_sme = sysmon_envsys_create();
535
536 wmi_hp_switch_init(sc);
537
538 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_HDDTEMP) == true) {
539 sensor = WMI_HP_SENSOR_HDDTEMP;
540 (void)strlcpy(sc->sc_sensor[sensor].desc, "hddtemp",
541 ENVSYS_DESCLEN);
542 sc->sc_sensor[sensor].state = ENVSYS_SVALID;
543 sc->sc_sensor[sensor].units = ENVSYS_STEMP;
544 sc->sc_sensor[sensor].value_cur =
545 sc->sc_val * 1000000 + 273150000;
546
547 sysmon_envsys_sensor_attach(sc->sc_sme,
548 &sc->sc_sensor[sensor]);
549 }
550
551 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_DISPLAY) == true) {
552 sensor = WMI_HP_SENSOR_DISPLAY;
553 (void)strlcpy(sc->sc_sensor[sensor].desc, "display",
554 ENVSYS_DESCLEN);
555 sc->sc_sensor[sensor].state = ENVSYS_SVALID;
556 sc->sc_sensor[sensor].units = ENVSYS_INDICATOR;
557 sc->sc_sensor[sensor].value_cur = sc->sc_val;
558
559 sysmon_envsys_sensor_attach(sc->sc_sme,
560 &sc->sc_sensor[sensor]);
561 }
562
563 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_DOCK) == true) {
564 sensor = WMI_HP_SENSOR_DOCK;
565 (void)strlcpy(sc->sc_sensor[sensor].desc, "docking station",
566 ENVSYS_DESCLEN);
567 sc->sc_sensor[sensor].state = ENVSYS_SVALID;
568 sc->sc_sensor[sensor].units = ENVSYS_INDICATOR;
569 sc->sc_sensor[sensor].value_cur = sc->sc_val;
570
571 sysmon_envsys_sensor_attach(sc->sc_sme,
572 &sc->sc_sensor[sensor]);
573 }
574
575 sc->sc_sme->sme_cookie = sc;
576 sc->sc_sme->sme_refresh = wmi_hp_sensor_refresh;
577 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
578
579 if (sysmon_envsys_register(sc->sc_sme) != 0)
580 goto fail;
581
582 wmi_hp_sensor_switch_update(sc->sc_dev);
583
584 return;
585
586 fail:
587 aprint_debug_dev(sc->sc_dev, "failed to initialize sysmon\n");
588
589 sysmon_envsys_destroy(sc->sc_sme);
590 kmem_free(sc->sc_sensor, WMI_HP_SENSOR_SIZE);
591
592 sc->sc_sme = NULL;
593 sc->sc_sensor = NULL;
594 }
595
596 static void
wmi_hp_sensor_switch_update(void * aux)597 wmi_hp_sensor_switch_update(void *aux)
598 {
599 struct wmi_hp_softc *sc;
600 device_t self = aux;
601
602 sc = device_private(self);
603
604 if (sc->sc_sme == NULL || sc->sc_sensor == NULL)
605 return;
606
607 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_SWITCH) != true) {
608 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SINVALID;
609 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SINVALID;
610 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SINVALID;
611 return;
612 }
613
614 if ((sc->sc_val & WMI_HP_SWITCH_WLAN) != 0) {
615 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 0;
616
617 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WLAN_ONAIR) != 0)
618 sc->sc_sensor[WMI_HP_SENSOR_WLAN].value_cur = 1;
619
620 sc->sc_sensor[WMI_HP_SENSOR_WLAN].state = ENVSYS_SVALID;
621 }
622
623 if ((sc->sc_val & WMI_HP_SWITCH_BT) != 0) {
624 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 0;
625
626 if ((sc->sc_val & WMI_HP_SWITCH_MASK_BT_ONAIR) != 0)
627 sc->sc_sensor[WMI_HP_SENSOR_BT].value_cur = 1;
628
629 sc->sc_sensor[WMI_HP_SENSOR_BT].state = ENVSYS_SVALID;
630 }
631
632 if ((sc->sc_val & WMI_HP_SWITCH_WWAN) != 0) {
633 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 0;
634
635 if ((sc->sc_val & WMI_HP_SWITCH_MASK_WWAN_ONAIR) != 0)
636 sc->sc_sensor[WMI_HP_SENSOR_WWAN].value_cur = 1;
637
638 sc->sc_sensor[WMI_HP_SENSOR_WWAN].state = ENVSYS_SVALID;
639 }
640 }
641
642 static void
wmi_hp_sensor_read(struct wmi_hp_softc * sc,envsys_data_t * sensor,int cmd)643 wmi_hp_sensor_read(struct wmi_hp_softc *sc, envsys_data_t *sensor, int cmd)
644 {
645 if (wmi_hp_method_read(sc, cmd) == true) {
646 sensor->state = ENVSYS_SVALID;
647 sensor->value_cur = sc->sc_val;
648 } else {
649 sensor->state = ENVSYS_SINVALID;
650 }
651 }
652
653 static void
wmi_hp_sensor_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)654 wmi_hp_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
655 {
656 struct wmi_hp_softc *sc = sme->sme_cookie;
657 envsys_data_t *sensor;
658
659 sensor = &sc->sc_sensor[WMI_HP_SENSOR_HDDTEMP];
660 wmi_hp_sensor_read(sc, sensor, WMI_HP_METHOD_CMD_HDDTEMP);
661 if (sensor->state == ENVSYS_SVALID) {
662 sensor->value_cur = sensor->value_cur * 1000000 +
663 273150000;
664 }
665
666 wmi_hp_sensor_read(sc, &sc->sc_sensor[WMI_HP_SENSOR_DISPLAY],
667 WMI_HP_METHOD_CMD_DISPLAY);
668
669 wmi_hp_sensor_read(sc, &sc->sc_sensor[WMI_HP_SENSOR_DOCK],
670 WMI_HP_METHOD_CMD_DOCK);
671 }
672
673 MODULE(MODULE_CLASS_DRIVER, wmihp, "acpiwmi,sysmon_envsys");
674
675 #ifdef _MODULE
676 #include "ioconf.c"
677 #endif
678
679 static int
wmihp_modcmd(modcmd_t cmd,void * aux)680 wmihp_modcmd(modcmd_t cmd, void *aux)
681 {
682 int rv = 0;
683
684 switch (cmd) {
685
686 case MODULE_CMD_INIT:
687
688 #ifdef _MODULE
689 rv = config_init_component(cfdriver_ioconf_wmihp,
690 cfattach_ioconf_wmihp, cfdata_ioconf_wmihp);
691 #endif
692 break;
693
694 case MODULE_CMD_FINI:
695
696 #ifdef _MODULE
697 rv = config_fini_component(cfdriver_ioconf_wmihp,
698 cfattach_ioconf_wmihp, cfdata_ioconf_wmihp);
699 #endif
700 break;
701
702 default:
703 rv = ENOTTY;
704 }
705
706 return rv;
707 }
708
709 static int
sysctl_wmi_hp_set_als(SYSCTLFN_ARGS)710 sysctl_wmi_hp_set_als(SYSCTLFN_ARGS)
711 {
712 struct sysctlnode node;
713 int err;
714 int als = wmihp_als;
715 struct wmi_hp_softc *sc = wmi_hp_sc;
716
717 node = *rnode;
718 node.sysctl_data = &als;
719
720 err = sysctl_lookup(SYSCTLFN_CALL(&node));
721
722 if (err != 0 || newp == NULL)
723 return err;
724
725 if (als < 0 || als > 1)
726 return EINVAL;
727
728 if (wmi_hp_method_write(sc, WMI_HP_METHOD_CMD_ALS, als) == true) {
729 wmihp_als = als;
730 return 0;
731 }
732
733 return EIO;
734 }
735
736 static void
sysctl_wmi_hp_setup(struct wmi_hp_softc * sc)737 sysctl_wmi_hp_setup(struct wmi_hp_softc *sc)
738 {
739 const struct sysctlnode *rnode;
740 int err;
741
742 err = sysctl_createv(&wmihp_sysctllog, 0, NULL, &rnode,
743 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", NULL,
744 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
745
746 if (err != 0)
747 return;
748
749 err = sysctl_createv(&wmihp_sysctllog, 0, &rnode, &rnode,
750 0, CTLTYPE_NODE, "wmi", SYSCTL_DESCR("ACPI HP WMI"),
751 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
752
753 if (err != 0)
754 return;
755
756 if (wmi_hp_method_read(sc, WMI_HP_METHOD_CMD_ALS) == true) {
757 (void)sysctl_createv(NULL, 0, &rnode, NULL,
758 CTLFLAG_READWRITE, CTLTYPE_BOOL, "als",
759 SYSCTL_DESCR("Ambient Light Sensor"),
760 sysctl_wmi_hp_set_als, 0, NULL, 0, CTL_CREATE, CTL_EOL);
761 }
762 }
763