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
81ACPI_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
150struct 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
159static int wmi_hp_match(device_t, cfdata_t, void *);
160static void wmi_hp_attach(device_t, device_t, void *);
161static int wmi_hp_detach(device_t, int);
162static bool wmi_hp_suspend(device_t, const pmf_qual_t *);
163static bool wmi_hp_resume(device_t, const pmf_qual_t *);
164static void wmi_hp_notify_handler(ACPI_HANDLE, uint32_t, void *);
165static void wmi_hp_hotkey(void *);
166static bool wmi_hp_method(struct wmi_hp_softc *);
167static bool wmi_hp_method_read(struct wmi_hp_softc *, uint8_t);
168static bool wmi_hp_method_write(struct wmi_hp_softc *, uint8_t, uint32_t);
169
170static void wmi_hp_sensor_init(struct wmi_hp_softc *);
171static void wmi_hp_sensor_switch_update(void *);
172static void wmi_hp_sensor_refresh(struct sysmon_envsys *, envsys_data_t *);
173
174static void sysctl_wmi_hp_setup(struct wmi_hp_softc *);
175static int sysctl_wmi_hp_set_als(SYSCTLFN_PROTO);
176static struct sysctllog *wmihp_sysctllog = NULL;
177static int wmihp_als = 0;
178static struct wmi_hp_softc *wmi_hp_sc = NULL; /* XXX */
179
180CFATTACH_DECL_NEW(wmihp, sizeof(struct wmi_hp_softc),
181 wmi_hp_match, wmi_hp_attach, wmi_hp_detach, NULL);
182
183static int
184wmi_hp_match(device_t parent, cfdata_t match, void *aux)
185{
186 return acpi_wmi_guid_match(parent, WMI_HP_GUID_METHOD);
187}
188
189static void
190wmi_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
227static int
228wmi_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
254static bool
255wmi_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
266static bool
267wmi_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
278static void
279wmi_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
333out:
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
342static void
343wmi_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
387static bool
388wmi_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 }
455out:
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
468static bool
469wmi_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
481static bool
482wmi_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
495static void
496wmi_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
531static void
532wmi_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
593fail:
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
603static void
604wmi_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
649static void
650wmi_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
660static void
661wmi_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
680MODULE(MODULE_CLASS_DRIVER, wmihp, "acpiwmi,sysmon_envsys");
681
682#ifdef _MODULE
683#include "ioconf.c"
684#endif
685
686static int
687wmihp_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
716static int
717sysctl_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
743static void
744sysctl_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