1 /*-
2 * Copyright (c) 2009 Michael Gmelin <freebsd@grem.de>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 /*
29 * Driver for extra ACPI-controlled features found on HP laptops
30 * that use a WMI enabled BIOS (e.g. HP Compaq 8510p and 6510p).
31 * Allows to control and read status of integrated hardware and read
32 * BIOS settings through CMI.
33 * Inspired by the hp-wmi driver, which implements a subset of these
34 * features (hotkeys) on Linux.
35 *
36 * HP CMI whitepaper:
37 * http://h20331.www2.hp.com/Hpsub/downloads/cmi_whitepaper.pdf
38 * wmi-hp for Linux:
39 * http://www.kernel.org
40 * WMI and ACPI:
41 * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx
42 */
43
44 #include "opt_acpi.h"
45 #include <sys/param.h>
46 #include <sys/conf.h>
47 #include <sys/uio.h>
48 #include <sys/proc.h>
49 #include <sys/kernel.h>
50 #include <sys/bus.h>
51 #include <sys/sbuf.h>
52 #include <sys/module.h>
53 #include <sys/sysctl.h>
54
55 #include <contrib/dev/acpica/include/acpi.h>
56 #include <contrib/dev/acpica/include/accommon.h>
57 #include <dev/acpica/acpivar.h>
58 #include "acpi_wmi_if.h"
59
60 #define _COMPONENT ACPI_OEM
61 ACPI_MODULE_NAME("HP")
62
63 #define ACPI_HP_WMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
64 #define ACPI_HP_WMI_BIOS_GUID "5FB7F034-2C63-45E9-BE91-3D44E2C707E4"
65 #define ACPI_HP_WMI_CMI_GUID "2D114B49-2DFB-4130-B8FE-4A3C09E75133"
66
67 #define ACPI_HP_WMI_DISPLAY_COMMAND 0x1
68 #define ACPI_HP_WMI_HDDTEMP_COMMAND 0x2
69 #define ACPI_HP_WMI_ALS_COMMAND 0x3
70 #define ACPI_HP_WMI_DOCK_COMMAND 0x4
71 #define ACPI_HP_WMI_WIRELESS_COMMAND 0x5
72 #define ACPI_HP_WMI_BIOS_COMMAND 0x9
73 #define ACPI_HP_WMI_FEATURE_COMMAND 0xb
74 #define ACPI_HP_WMI_HOTKEY_COMMAND 0xc
75 #define ACPI_HP_WMI_FEATURE2_COMMAND 0xd
76 #define ACPI_HP_WMI_WIRELESS2_COMMAND 0x1b
77 #define ACPI_HP_WMI_POSTCODEERROR_COMMAND 0x2a
78
79 #define ACPI_HP_METHOD_WLAN_ENABLED 1
80 #define ACPI_HP_METHOD_WLAN_RADIO 2
81 #define ACPI_HP_METHOD_WLAN_ON_AIR 3
82 #define ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON 4
83 #define ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF 5
84 #define ACPI_HP_METHOD_BLUETOOTH_ENABLED 6
85 #define ACPI_HP_METHOD_BLUETOOTH_RADIO 7
86 #define ACPI_HP_METHOD_BLUETOOTH_ON_AIR 8
87 #define ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON 9
88 #define ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF 10
89 #define ACPI_HP_METHOD_WWAN_ENABLED 11
90 #define ACPI_HP_METHOD_WWAN_RADIO 12
91 #define ACPI_HP_METHOD_WWAN_ON_AIR 13
92 #define ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON 14
93 #define ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF 15
94 #define ACPI_HP_METHOD_ALS 16
95 #define ACPI_HP_METHOD_DISPLAY 17
96 #define ACPI_HP_METHOD_HDDTEMP 18
97 #define ACPI_HP_METHOD_DOCK 19
98 #define ACPI_HP_METHOD_CMI_DETAIL 20
99 #define ACPI_HP_METHOD_VERBOSE 21
100
101 #define HP_MASK_WWAN_ON_AIR 0x1000000
102 #define HP_MASK_BLUETOOTH_ON_AIR 0x10000
103 #define HP_MASK_WLAN_ON_AIR 0x100
104 #define HP_MASK_WWAN_RADIO 0x8000000
105 #define HP_MASK_BLUETOOTH_RADIO 0x80000
106 #define HP_MASK_WLAN_RADIO 0x800
107 #define HP_MASK_WWAN_ENABLED 0x2000000
108 #define HP_MASK_BLUETOOTH_ENABLED 0x20000
109 #define HP_MASK_WLAN_ENABLED 0x200
110
111 #define ACPI_HP_EVENT_DOCK 0x01
112 #define ACPI_HP_EVENT_PARK_HDD 0x02
113 #define ACPI_HP_EVENT_SMART_ADAPTER 0x03
114 #define ACPI_HP_EVENT_BEZEL_BUTTON 0x04
115 #define ACPI_HP_EVENT_WIRELESS 0x05
116 #define ACPI_HP_EVENT_CPU_BATTERY_THROTTLE 0x06
117 #define ACPI_HP_EVENT_LOCK_SWITCH 0x07
118 #define ACPI_HP_EVENT_LID_SWITCH 0x08
119 #define ACPI_HP_EVENT_SCREEN_ROTATION 0x09
120 #define ACPI_HP_EVENT_COOLSENSE_SYSTEM_MOBILE 0x0A
121 #define ACPI_HP_EVENT_COOLSENSE_SYSTEM_HOT 0x0B
122 #define ACPI_HP_EVENT_PROXIMITY_SENSOR 0x0C
123 #define ACPI_HP_EVENT_BACKLIT_KB_BRIGHTNESS 0x0D
124 #define ACPI_HP_EVENT_PEAKSHIFT_PERIOD 0x0F
125 #define ACPI_HP_EVENT_BATTERY_CHARGE_PERIOD 0x10
126
127 #define ACPI_HP_CMI_DETAIL_PATHS 0x01
128 #define ACPI_HP_CMI_DETAIL_ENUMS 0x02
129 #define ACPI_HP_CMI_DETAIL_FLAGS 0x04
130 #define ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE 0x08
131
132 #define ACPI_HP_WMI_RET_WRONG_SIGNATURE 0x02
133 #define ACPI_HP_WMI_RET_UNKNOWN_COMMAND 0x03
134 #define ACPI_HP_WMI_RET_UNKNOWN_CMDTYPE 0x04
135 #define ACPI_HP_WMI_RET_INVALID_PARAMETERS 0x05
136
137 struct acpi_hp_inst_seq_pair {
138 UINT32 sequence; /* sequence number as suggested by cmi bios */
139 UINT8 instance; /* object instance on guid */
140 };
141
142 struct acpi_hp_softc {
143 device_t dev;
144 device_t wmi_dev;
145 int has_notify; /* notification GUID found */
146 int has_cmi; /* CMI GUID found */
147 int has_wireless; /* Wireless command found */
148 int cmi_detail; /* CMI detail level
149 (set by sysctl) */
150 int verbose; /* add debug output */
151 int wlan_enable_if_radio_on; /* set by sysctl */
152 int wlan_disable_if_radio_off; /* set by sysctl */
153 int bluetooth_enable_if_radio_on; /* set by sysctl */
154 int bluetooth_disable_if_radio_off; /* set by sysctl */
155 int wwan_enable_if_radio_on; /* set by sysctl */
156 int wwan_disable_if_radio_off; /* set by sysctl */
157 int was_wlan_on_air; /* last known WLAN
158 on air status */
159 int was_bluetooth_on_air; /* last known BT
160 on air status */
161 int was_wwan_on_air; /* last known WWAN
162 on air status */
163 struct sysctl_ctx_list *sysctl_ctx;
164 struct sysctl_oid *sysctl_tree;
165 struct cdev *hpcmi_dev_t; /* hpcmi device handle */
166 struct sbuf hpcmi_sbuf; /* /dev/hpcmi output sbuf */
167 pid_t hpcmi_open_pid; /* pid operating on
168 /dev/hpcmi */
169 int hpcmi_bufptr; /* current pointer position
170 in /dev/hpcmi output buffer
171 */
172 int cmi_order_size; /* size of cmi_order list */
173 struct acpi_hp_inst_seq_pair cmi_order[128]; /* list of CMI
174 instances ordered by BIOS suggested sequence */
175 };
176
177 static struct {
178 char *name;
179 int method;
180 char *description;
181 int flag_rdonly;
182 } acpi_hp_sysctls[] = {
183 {
184 .name = "wlan_enabled",
185 .method = ACPI_HP_METHOD_WLAN_ENABLED,
186 .description = "Enable/Disable WLAN (WiFi)",
187 },
188 {
189 .name = "wlan_radio",
190 .method = ACPI_HP_METHOD_WLAN_RADIO,
191 .description = "WLAN radio status",
192 .flag_rdonly = 1
193 },
194 {
195 .name = "wlan_on_air",
196 .method = ACPI_HP_METHOD_WLAN_ON_AIR,
197 .description = "WLAN radio ready to use (enabled and radio)",
198 .flag_rdonly = 1
199 },
200 {
201 .name = "wlan_enable_if_radio_on",
202 .method = ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON,
203 .description = "Enable WLAN if radio is turned on",
204 },
205 {
206 .name = "wlan_disable_if_radio_off",
207 .method = ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF,
208 .description = "Disable WLAN if radio is turned off",
209 },
210 {
211 .name = "bt_enabled",
212 .method = ACPI_HP_METHOD_BLUETOOTH_ENABLED,
213 .description = "Enable/Disable Bluetooth",
214 },
215 {
216 .name = "bt_radio",
217 .method = ACPI_HP_METHOD_BLUETOOTH_RADIO,
218 .description = "Bluetooth radio status",
219 .flag_rdonly = 1
220 },
221 {
222 .name = "bt_on_air",
223 .method = ACPI_HP_METHOD_BLUETOOTH_ON_AIR,
224 .description = "Bluetooth radio ready to use"
225 " (enabled and radio)",
226 .flag_rdonly = 1
227 },
228 {
229 .name = "bt_enable_if_radio_on",
230 .method = ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON,
231 .description = "Enable bluetooth if radio is turned on",
232 },
233 {
234 .name = "bt_disable_if_radio_off",
235 .method = ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF,
236 .description = "Disable bluetooth if radio is turned off",
237 },
238 {
239 .name = "wwan_enabled",
240 .method = ACPI_HP_METHOD_WWAN_ENABLED,
241 .description = "Enable/Disable WWAN (UMTS)",
242 },
243 {
244 .name = "wwan_radio",
245 .method = ACPI_HP_METHOD_WWAN_RADIO,
246 .description = "WWAN radio status",
247 .flag_rdonly = 1
248 },
249 {
250 .name = "wwan_on_air",
251 .method = ACPI_HP_METHOD_WWAN_ON_AIR,
252 .description = "WWAN radio ready to use (enabled and radio)",
253 .flag_rdonly = 1
254 },
255 {
256 .name = "wwan_enable_if_radio_on",
257 .method = ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON,
258 .description = "Enable WWAN if radio is turned on",
259 },
260 {
261 .name = "wwan_disable_if_radio_off",
262 .method = ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF,
263 .description = "Disable WWAN if radio is turned off",
264 },
265 {
266 .name = "als_enabled",
267 .method = ACPI_HP_METHOD_ALS,
268 .description = "Enable/Disable ALS (Ambient light sensor)",
269 },
270 {
271 .name = "display",
272 .method = ACPI_HP_METHOD_DISPLAY,
273 .description = "Display status",
274 .flag_rdonly = 1
275 },
276 {
277 .name = "hdd_temperature",
278 .method = ACPI_HP_METHOD_HDDTEMP,
279 .description = "HDD temperature",
280 .flag_rdonly = 1
281 },
282 {
283 .name = "is_docked",
284 .method = ACPI_HP_METHOD_DOCK,
285 .description = "Docking station status",
286 .flag_rdonly = 1
287 },
288 {
289 .name = "cmi_detail",
290 .method = ACPI_HP_METHOD_CMI_DETAIL,
291 .description = "Details shown in CMI output "
292 "(cat /dev/hpcmi)",
293 },
294 {
295 .name = "verbose",
296 .method = ACPI_HP_METHOD_VERBOSE,
297 .description = "Verbosity level",
298 },
299 { NULL, 0, NULL, 0 }
300 };
301
302 ACPI_SERIAL_DECL(hp, "HP ACPI-WMI Mapping");
303
304 static void acpi_hp_identify(driver_t *driver, device_t parent);
305 static int acpi_hp_probe(device_t dev);
306 static int acpi_hp_attach(device_t dev);
307 static int acpi_hp_detach(device_t dev);
308
309 static void acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc* sc);
310 static int acpi_hp_sysctl(SYSCTL_HANDLER_ARGS);
311 static int acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method,
312 int arg, int oldarg);
313 static int acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method);
314 static int acpi_hp_exec_wmi_command(device_t wmi_dev, int command,
315 int is_write, int val, int *retval);
316 static void acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context);
317 static int acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid,
318 UINT8 instance, char* outbuf, size_t outsize,
319 UINT32* sequence, int detail);
320 static void acpi_hp_hex_decode(char* buffer);
321
322 static d_open_t acpi_hp_hpcmi_open;
323 static d_close_t acpi_hp_hpcmi_close;
324 static d_read_t acpi_hp_hpcmi_read;
325
326 /* handler /dev/hpcmi device */
327 static struct cdevsw hpcmi_cdevsw = {
328 .d_version = D_VERSION,
329 .d_open = acpi_hp_hpcmi_open,
330 .d_close = acpi_hp_hpcmi_close,
331 .d_read = acpi_hp_hpcmi_read,
332 .d_name = "hpcmi",
333 };
334
335 static device_method_t acpi_hp_methods[] = {
336 DEVMETHOD(device_identify, acpi_hp_identify),
337 DEVMETHOD(device_probe, acpi_hp_probe),
338 DEVMETHOD(device_attach, acpi_hp_attach),
339 DEVMETHOD(device_detach, acpi_hp_detach),
340
341 DEVMETHOD_END
342 };
343
344 static driver_t acpi_hp_driver = {
345 "acpi_hp",
346 acpi_hp_methods,
347 sizeof(struct acpi_hp_softc),
348 };
349
350 DRIVER_MODULE(acpi_hp, acpi_wmi, acpi_hp_driver, 0, 0);
351 MODULE_DEPEND(acpi_hp, acpi_wmi, 1, 1, 1);
352 MODULE_DEPEND(acpi_hp, acpi, 1, 1, 1);
353
354 static void
acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc * sc)355 acpi_hp_evaluate_auto_on_off(struct acpi_hp_softc *sc)
356 {
357 int res;
358 int wireless;
359 int new_wlan_status;
360 int new_bluetooth_status;
361 int new_wwan_status;
362
363 res = acpi_hp_exec_wmi_command(sc->wmi_dev,
364 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &wireless);
365 if (res != 0) {
366 device_printf(sc->wmi_dev, "Wireless command error %x\n", res);
367 return;
368 }
369 new_wlan_status = -1;
370 new_bluetooth_status = -1;
371 new_wwan_status = -1;
372
373 if (sc->verbose)
374 device_printf(sc->wmi_dev, "Wireless status is %x\n", wireless);
375 if (sc->wlan_disable_if_radio_off && !(wireless & HP_MASK_WLAN_RADIO)
376 && (wireless & HP_MASK_WLAN_ENABLED)) {
377 acpi_hp_exec_wmi_command(sc->wmi_dev,
378 ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x100, NULL);
379 new_wlan_status = 0;
380 }
381 else if (sc->wlan_enable_if_radio_on && (wireless & HP_MASK_WLAN_RADIO)
382 && !(wireless & HP_MASK_WLAN_ENABLED)) {
383 acpi_hp_exec_wmi_command(sc->wmi_dev,
384 ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x101, NULL);
385 new_wlan_status = 1;
386 }
387 if (sc->bluetooth_disable_if_radio_off &&
388 !(wireless & HP_MASK_BLUETOOTH_RADIO) &&
389 (wireless & HP_MASK_BLUETOOTH_ENABLED)) {
390 acpi_hp_exec_wmi_command(sc->wmi_dev,
391 ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x200, NULL);
392 new_bluetooth_status = 0;
393 }
394 else if (sc->bluetooth_enable_if_radio_on &&
395 (wireless & HP_MASK_BLUETOOTH_RADIO) &&
396 !(wireless & HP_MASK_BLUETOOTH_ENABLED)) {
397 acpi_hp_exec_wmi_command(sc->wmi_dev,
398 ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x202, NULL);
399 new_bluetooth_status = 1;
400 }
401 if (sc->wwan_disable_if_radio_off &&
402 !(wireless & HP_MASK_WWAN_RADIO) &&
403 (wireless & HP_MASK_WWAN_ENABLED)) {
404 acpi_hp_exec_wmi_command(sc->wmi_dev,
405 ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x400, NULL);
406 new_wwan_status = 0;
407 }
408 else if (sc->wwan_enable_if_radio_on &&
409 (wireless & HP_MASK_WWAN_RADIO) &&
410 !(wireless & HP_MASK_WWAN_ENABLED)) {
411 acpi_hp_exec_wmi_command(sc->wmi_dev,
412 ACPI_HP_WMI_WIRELESS_COMMAND, 1, 0x404, NULL);
413 new_wwan_status = 1;
414 }
415
416 if (new_wlan_status == -1) {
417 new_wlan_status = (wireless & HP_MASK_WLAN_ON_AIR);
418 if ((new_wlan_status?1:0) != sc->was_wlan_on_air) {
419 sc->was_wlan_on_air = sc->was_wlan_on_air?0:1;
420 if (sc->verbose)
421 device_printf(sc->wmi_dev,
422 "WLAN on air changed to %i "
423 "(new_wlan_status is %i)\n",
424 sc->was_wlan_on_air, new_wlan_status);
425 acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
426 0xc0+sc->was_wlan_on_air);
427 }
428 }
429 if (new_bluetooth_status == -1) {
430 new_bluetooth_status = (wireless & HP_MASK_BLUETOOTH_ON_AIR);
431 if ((new_bluetooth_status?1:0) != sc->was_bluetooth_on_air) {
432 sc->was_bluetooth_on_air = sc->was_bluetooth_on_air?
433 0:1;
434 if (sc->verbose)
435 device_printf(sc->wmi_dev,
436 "BLUETOOTH on air changed"
437 " to %i (new_bluetooth_status is %i)\n",
438 sc->was_bluetooth_on_air,
439 new_bluetooth_status);
440 acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
441 0xd0+sc->was_bluetooth_on_air);
442 }
443 }
444 if (new_wwan_status == -1) {
445 new_wwan_status = (wireless & HP_MASK_WWAN_ON_AIR);
446 if ((new_wwan_status?1:0) != sc->was_wwan_on_air) {
447 sc->was_wwan_on_air = sc->was_wwan_on_air?0:1;
448 if (sc->verbose)
449 device_printf(sc->wmi_dev,
450 "WWAN on air changed to %i"
451 " (new_wwan_status is %i)\n",
452 sc->was_wwan_on_air, new_wwan_status);
453 acpi_UserNotify("HP", ACPI_ROOT_OBJECT,
454 0xe0+sc->was_wwan_on_air);
455 }
456 }
457 }
458
459 static void
acpi_hp_identify(driver_t * driver,device_t parent)460 acpi_hp_identify(driver_t *driver, device_t parent)
461 {
462
463 /* Don't do anything if driver is disabled. */
464 if (acpi_disabled("hp"))
465 return;
466
467 /* Add only a single device instance. */
468 if (device_find_child(parent, "acpi_hp", -1) != NULL)
469 return;
470
471 /* Check BIOS GUID to see whether system is compatible. */
472 if (!ACPI_WMI_PROVIDES_GUID_STRING(parent,
473 ACPI_HP_WMI_BIOS_GUID))
474 return;
475
476 if (BUS_ADD_CHILD(parent, 0, "acpi_hp", -1) == NULL)
477 device_printf(parent, "add acpi_hp child failed\n");
478 }
479
480 static int
acpi_hp_probe(device_t dev)481 acpi_hp_probe(device_t dev)
482 {
483
484 device_set_desc(dev, "HP ACPI-WMI Mapping");
485 return (0);
486 }
487
488 static int
acpi_hp_attach(device_t dev)489 acpi_hp_attach(device_t dev)
490 {
491 struct acpi_hp_softc *sc;
492 int arg;
493
494 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
495
496 sc = device_get_softc(dev);
497 sc->dev = dev;
498 sc->has_notify = 0;
499 sc->has_cmi = 0;
500 sc->bluetooth_enable_if_radio_on = 0;
501 sc->bluetooth_disable_if_radio_off = 0;
502 sc->wlan_enable_if_radio_on = 0;
503 sc->wlan_disable_if_radio_off = 0;
504 sc->wlan_enable_if_radio_on = 0;
505 sc->wlan_disable_if_radio_off = 0;
506 sc->was_wlan_on_air = 0;
507 sc->was_bluetooth_on_air = 0;
508 sc->was_wwan_on_air = 0;
509 sc->cmi_detail = 0;
510 sc->cmi_order_size = -1;
511 sc->verbose = bootverbose;
512 memset(sc->cmi_order, 0, sizeof(sc->cmi_order));
513
514 sc->wmi_dev = device_get_parent(dev);
515 if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
516 ACPI_HP_WMI_BIOS_GUID)) {
517 device_printf(dev,
518 "WMI device does not provide the HP BIOS GUID\n");
519 return (EINVAL);
520 }
521 if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
522 ACPI_HP_WMI_EVENT_GUID)) {
523 device_printf(dev,
524 "HP event GUID detected, installing event handler\n");
525 if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
526 ACPI_HP_WMI_EVENT_GUID, acpi_hp_notify, dev)) {
527 device_printf(dev,
528 "Could not install notification handler!\n");
529 }
530 else {
531 sc->has_notify = 1;
532 }
533 }
534 if ((sc->has_cmi =
535 ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev, ACPI_HP_WMI_CMI_GUID)
536 )) {
537 device_printf(dev, "HP CMI GUID detected\n");
538 }
539
540 if (sc->has_cmi) {
541 sc->hpcmi_dev_t = make_dev(&hpcmi_cdevsw, 0, UID_ROOT,
542 GID_WHEEL, 0644, "hpcmi");
543 sc->hpcmi_dev_t->si_drv1 = sc;
544 sc->hpcmi_open_pid = 0;
545 sc->hpcmi_bufptr = -1;
546 }
547
548 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
549 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, NULL) == 0)
550 sc->has_wireless = 1;
551
552 ACPI_SERIAL_BEGIN(hp);
553
554 sc->sysctl_ctx = device_get_sysctl_ctx(dev);
555 sc->sysctl_tree = device_get_sysctl_tree(dev);
556 for (int i = 0; acpi_hp_sysctls[i].name != NULL; ++i) {
557 arg = 0;
558 if (((!sc->has_notify || !sc->has_wireless) &&
559 (acpi_hp_sysctls[i].method ==
560 ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON ||
561 acpi_hp_sysctls[i].method ==
562 ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF ||
563 acpi_hp_sysctls[i].method ==
564 ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON ||
565 acpi_hp_sysctls[i].method ==
566 ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF ||
567 acpi_hp_sysctls[i].method ==
568 ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON ||
569 acpi_hp_sysctls[i].method ==
570 ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF)) ||
571 (arg = acpi_hp_sysctl_get(sc,
572 acpi_hp_sysctls[i].method)) < 0) {
573 continue;
574 }
575 if (acpi_hp_sysctls[i].method == ACPI_HP_METHOD_WLAN_ON_AIR) {
576 sc->was_wlan_on_air = arg;
577 }
578 else if (acpi_hp_sysctls[i].method ==
579 ACPI_HP_METHOD_BLUETOOTH_ON_AIR) {
580 sc->was_bluetooth_on_air = arg;
581 }
582 else if (acpi_hp_sysctls[i].method ==
583 ACPI_HP_METHOD_WWAN_ON_AIR) {
584 sc->was_wwan_on_air = arg;
585 }
586
587 if (acpi_hp_sysctls[i].flag_rdonly != 0) {
588 SYSCTL_ADD_PROC(sc->sysctl_ctx,
589 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
590 acpi_hp_sysctls[i].name,
591 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE,
592 sc, i, acpi_hp_sysctl, "I",
593 acpi_hp_sysctls[i].description);
594 } else {
595 SYSCTL_ADD_PROC(sc->sysctl_ctx,
596 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
597 acpi_hp_sysctls[i].name,
598 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
599 sc, i, acpi_hp_sysctl, "I",
600 acpi_hp_sysctls[i].description);
601 }
602 }
603 ACPI_SERIAL_END(hp);
604
605 return (0);
606 }
607
608 static int
acpi_hp_detach(device_t dev)609 acpi_hp_detach(device_t dev)
610 {
611 struct acpi_hp_softc *sc;
612
613 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
614 sc = device_get_softc(dev);
615 if (sc->has_cmi && sc->hpcmi_open_pid != 0)
616 return (EBUSY);
617
618 if (sc->has_notify) {
619 ACPI_WMI_REMOVE_EVENT_HANDLER(sc->wmi_dev,
620 ACPI_HP_WMI_EVENT_GUID);
621 }
622
623 if (sc->has_cmi) {
624 if (sc->hpcmi_bufptr != -1) {
625 sbuf_delete(&sc->hpcmi_sbuf);
626 sc->hpcmi_bufptr = -1;
627 }
628 sc->hpcmi_open_pid = 0;
629 destroy_dev(sc->hpcmi_dev_t);
630 }
631
632 return (0);
633 }
634
635 static int
acpi_hp_sysctl(SYSCTL_HANDLER_ARGS)636 acpi_hp_sysctl(SYSCTL_HANDLER_ARGS)
637 {
638 struct acpi_hp_softc *sc;
639 int arg;
640 int oldarg;
641 int error = 0;
642 int function;
643 int method;
644
645 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
646
647 sc = (struct acpi_hp_softc *)oidp->oid_arg1;
648 function = oidp->oid_arg2;
649 method = acpi_hp_sysctls[function].method;
650
651 ACPI_SERIAL_BEGIN(hp);
652 arg = acpi_hp_sysctl_get(sc, method);
653 oldarg = arg;
654 error = sysctl_handle_int(oidp, &arg, 0, req);
655 if (!error && req->newptr != NULL) {
656 error = acpi_hp_sysctl_set(sc, method, arg, oldarg);
657 }
658 ACPI_SERIAL_END(hp);
659
660 return (error);
661 }
662
663 static int
acpi_hp_sysctl_get(struct acpi_hp_softc * sc,int method)664 acpi_hp_sysctl_get(struct acpi_hp_softc *sc, int method)
665 {
666 int val = 0;
667
668 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
669 ACPI_SERIAL_ASSERT(hp);
670
671 switch (method) {
672 case ACPI_HP_METHOD_WLAN_ENABLED:
673 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
674 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
675 return (-EINVAL);
676 val = ((val & HP_MASK_WLAN_ENABLED) != 0);
677 break;
678 case ACPI_HP_METHOD_WLAN_RADIO:
679 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
680 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
681 return (-EINVAL);
682 val = ((val & HP_MASK_WLAN_RADIO) != 0);
683 break;
684 case ACPI_HP_METHOD_WLAN_ON_AIR:
685 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
686 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
687 return (-EINVAL);
688 val = ((val & HP_MASK_WLAN_ON_AIR) != 0);
689 break;
690 case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
691 val = sc->wlan_enable_if_radio_on;
692 break;
693 case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
694 val = sc->wlan_disable_if_radio_off;
695 break;
696 case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
697 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
698 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
699 return (-EINVAL);
700 val = ((val & HP_MASK_BLUETOOTH_ENABLED) != 0);
701 break;
702 case ACPI_HP_METHOD_BLUETOOTH_RADIO:
703 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
704 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
705 return (-EINVAL);
706 val = ((val & HP_MASK_BLUETOOTH_RADIO) != 0);
707 break;
708 case ACPI_HP_METHOD_BLUETOOTH_ON_AIR:
709 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
710 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
711 return (-EINVAL);
712 val = ((val & HP_MASK_BLUETOOTH_ON_AIR) != 0);
713 break;
714 case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
715 val = sc->bluetooth_enable_if_radio_on;
716 break;
717 case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
718 val = sc->bluetooth_disable_if_radio_off;
719 break;
720 case ACPI_HP_METHOD_WWAN_ENABLED:
721 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
722 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
723 return (-EINVAL);
724 val = ((val & HP_MASK_WWAN_ENABLED) != 0);
725 break;
726 case ACPI_HP_METHOD_WWAN_RADIO:
727 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
728 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
729 return (-EINVAL);
730 val = ((val & HP_MASK_WWAN_RADIO) != 0);
731 break;
732 case ACPI_HP_METHOD_WWAN_ON_AIR:
733 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
734 ACPI_HP_WMI_WIRELESS_COMMAND, 0, 0, &val))
735 return (-EINVAL);
736 val = ((val & HP_MASK_WWAN_ON_AIR) != 0);
737 break;
738 case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
739 val = sc->wwan_enable_if_radio_on;
740 break;
741 case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
742 val = sc->wwan_disable_if_radio_off;
743 break;
744 case ACPI_HP_METHOD_ALS:
745 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
746 ACPI_HP_WMI_ALS_COMMAND, 0, 0, &val))
747 return (-EINVAL);
748 break;
749 case ACPI_HP_METHOD_DISPLAY:
750 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
751 ACPI_HP_WMI_DISPLAY_COMMAND, 0, 0, &val))
752 return (-EINVAL);
753 break;
754 case ACPI_HP_METHOD_HDDTEMP:
755 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
756 ACPI_HP_WMI_HDDTEMP_COMMAND, 0, 0, &val))
757 return (-EINVAL);
758 break;
759 case ACPI_HP_METHOD_DOCK:
760 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
761 ACPI_HP_WMI_DOCK_COMMAND, 0, 0, &val))
762 return (-EINVAL);
763 break;
764 case ACPI_HP_METHOD_CMI_DETAIL:
765 val = sc->cmi_detail;
766 break;
767 case ACPI_HP_METHOD_VERBOSE:
768 val = sc->verbose;
769 break;
770 }
771
772 return (val);
773 }
774
775 static int
acpi_hp_sysctl_set(struct acpi_hp_softc * sc,int method,int arg,int oldarg)776 acpi_hp_sysctl_set(struct acpi_hp_softc *sc, int method, int arg, int oldarg)
777 {
778 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
779 ACPI_SERIAL_ASSERT(hp);
780
781 if (method != ACPI_HP_METHOD_CMI_DETAIL &&
782 method != ACPI_HP_METHOD_VERBOSE)
783 arg = arg?1:0;
784
785 if (arg != oldarg) {
786 switch (method) {
787 case ACPI_HP_METHOD_WLAN_ENABLED:
788 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
789 ACPI_HP_WMI_WIRELESS_COMMAND, 1,
790 arg?0x101:0x100, NULL))
791 return (-EINVAL);
792 break;
793 case ACPI_HP_METHOD_WLAN_ENABLE_IF_RADIO_ON:
794 sc->wlan_enable_if_radio_on = arg;
795 acpi_hp_evaluate_auto_on_off(sc);
796 break;
797 case ACPI_HP_METHOD_WLAN_DISABLE_IF_RADIO_OFF:
798 sc->wlan_disable_if_radio_off = arg;
799 acpi_hp_evaluate_auto_on_off(sc);
800 break;
801 case ACPI_HP_METHOD_BLUETOOTH_ENABLED:
802 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
803 ACPI_HP_WMI_WIRELESS_COMMAND, 1,
804 arg?0x202:0x200, NULL))
805 return (-EINVAL);
806 break;
807 case ACPI_HP_METHOD_BLUETOOTH_ENABLE_IF_RADIO_ON:
808 sc->bluetooth_enable_if_radio_on = arg;
809 acpi_hp_evaluate_auto_on_off(sc);
810 break;
811 case ACPI_HP_METHOD_BLUETOOTH_DISABLE_IF_RADIO_OFF:
812 sc->bluetooth_disable_if_radio_off = arg?1:0;
813 acpi_hp_evaluate_auto_on_off(sc);
814 break;
815 case ACPI_HP_METHOD_WWAN_ENABLED:
816 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
817 ACPI_HP_WMI_WIRELESS_COMMAND, 1,
818 arg?0x404:0x400, NULL))
819 return (-EINVAL);
820 break;
821 case ACPI_HP_METHOD_WWAN_ENABLE_IF_RADIO_ON:
822 sc->wwan_enable_if_radio_on = arg?1:0;
823 acpi_hp_evaluate_auto_on_off(sc);
824 break;
825 case ACPI_HP_METHOD_WWAN_DISABLE_IF_RADIO_OFF:
826 sc->wwan_disable_if_radio_off = arg?1:0;
827 acpi_hp_evaluate_auto_on_off(sc);
828 break;
829 case ACPI_HP_METHOD_ALS:
830 if (acpi_hp_exec_wmi_command(sc->wmi_dev,
831 ACPI_HP_WMI_ALS_COMMAND, 1, arg?1:0, NULL))
832 return (-EINVAL);
833 break;
834 case ACPI_HP_METHOD_CMI_DETAIL:
835 sc->cmi_detail = arg;
836 if ((arg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) !=
837 (oldarg & ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE)) {
838 sc->cmi_order_size = -1;
839 }
840 break;
841 case ACPI_HP_METHOD_VERBOSE:
842 sc->verbose = arg;
843 break;
844 }
845 }
846
847 return (0);
848 }
849
850 static __inline void
acpi_hp_free_buffer(ACPI_BUFFER * buf)851 acpi_hp_free_buffer(ACPI_BUFFER* buf) {
852 if (buf && buf->Pointer) {
853 AcpiOsFree(buf->Pointer);
854 }
855 }
856
857 static void
acpi_hp_notify(ACPI_HANDLE h,UINT32 notify,void * context)858 acpi_hp_notify(ACPI_HANDLE h, UINT32 notify, void *context)
859 {
860 device_t dev = context;
861 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
862
863 struct acpi_hp_softc *sc = device_get_softc(dev);
864 ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
865 ACPI_OBJECT *obj;
866 ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
867 obj = (ACPI_OBJECT*) response.Pointer;
868 if (obj && obj->Type == ACPI_TYPE_BUFFER && obj->Buffer.Length == 8) {
869 switch (*((UINT8 *) obj->Buffer.Pointer)) {
870 case ACPI_HP_EVENT_WIRELESS:
871 acpi_hp_evaluate_auto_on_off(sc);
872 break;
873 default:
874 if (sc->verbose) {
875 device_printf(sc->dev, "Event %02x\n",
876 *((UINT8 *) obj->Buffer.Pointer));
877 }
878 break;
879 }
880 }
881 acpi_hp_free_buffer(&response);
882 }
883
884 static int
acpi_hp_exec_wmi_command(device_t wmi_dev,int command,int is_write,int val,int * retval)885 acpi_hp_exec_wmi_command(device_t wmi_dev, int command, int is_write,
886 int val, int *retval)
887 {
888 UINT32 params[4+32] = { 0x55434553, is_write ? 2 : 1,
889 command, 4, val};
890 UINT32* result;
891 ACPI_OBJECT *obj;
892 ACPI_BUFFER in = { sizeof(params), ¶ms };
893 ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL };
894 int res;
895
896 if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev, ACPI_HP_WMI_BIOS_GUID,
897 0, 0x3, &in, &out))) {
898 acpi_hp_free_buffer(&out);
899 return (-EINVAL);
900 }
901 obj = out.Pointer;
902 if (!obj || obj->Type != ACPI_TYPE_BUFFER) {
903 acpi_hp_free_buffer(&out);
904 return (-EINVAL);
905 }
906 result = (UINT32*) obj->Buffer.Pointer;
907 res = result[1];
908 if (res == 0 && retval != NULL)
909 *retval = result[2];
910 acpi_hp_free_buffer(&out);
911
912 return (res);
913 }
914
915 static __inline char*
acpi_hp_get_string_from_object(ACPI_OBJECT * obj,char * dst,size_t size)916 acpi_hp_get_string_from_object(ACPI_OBJECT* obj, char* dst, size_t size) {
917 int length;
918
919 dst[0] = 0;
920 if (obj->Type == ACPI_TYPE_STRING) {
921 length = obj->String.Length+1;
922 if (length > size) {
923 length = size - 1;
924 }
925 strlcpy(dst, obj->String.Pointer, length);
926 acpi_hp_hex_decode(dst);
927 }
928
929 return (dst);
930 }
931
932 /*
933 * Read BIOS Setting block in instance "instance".
934 * The block returned is ACPI_TYPE_PACKAGE which should contain the following
935 * elements:
936 * Index Meaning
937 * 0 Setting Name [string]
938 * 1 Value (comma separated, asterisk marks the current value) [string]
939 * 2 Path within the bios hierarchy [string]
940 * 3 IsReadOnly [int]
941 * 4 DisplayInUI [int]
942 * 5 RequiresPhysicalPresence [int]
943 * 6 Sequence for ordering within the bios settings (absolute) [int]
944 * 7 Length of prerequisites array [int]
945 * 8..8+[7] PrerequisiteN [string]
946 * 9+[7] Current value (in case of enum) [string] / Array length [int]
947 * 10+[7] Enum length [int] / Array values
948 * 11+[7]ff Enum value at index x [string]
949 */
950 static int
acpi_hp_get_cmi_block(device_t wmi_dev,const char * guid,UINT8 instance,char * outbuf,size_t outsize,UINT32 * sequence,int detail)951 acpi_hp_get_cmi_block(device_t wmi_dev, const char* guid, UINT8 instance,
952 char* outbuf, size_t outsize, UINT32* sequence, int detail)
953 {
954 ACPI_OBJECT *obj;
955 ACPI_BUFFER out = { ACPI_ALLOCATE_BUFFER, NULL };
956 int i;
957 int outlen;
958 int has_enums = 0;
959 int valuebase = 0;
960 char string_buffer[255];
961 int enumbase;
962
963 outlen = 0;
964 outbuf[0] = 0;
965 if (ACPI_FAILURE(ACPI_WMI_GET_BLOCK(wmi_dev, guid, instance, &out))) {
966 acpi_hp_free_buffer(&out);
967 return (-EINVAL);
968 }
969 obj = out.Pointer;
970 if (!obj || obj->Type != ACPI_TYPE_PACKAGE) {
971 acpi_hp_free_buffer(&out);
972 return (-EINVAL);
973 }
974
975 /* Check if first 6 bytes matches our expectations. */
976 if (obj->Package.Count < 8 ||
977 obj->Package.Elements[0].Type != ACPI_TYPE_STRING ||
978 obj->Package.Elements[1].Type != ACPI_TYPE_STRING ||
979 obj->Package.Elements[2].Type != ACPI_TYPE_STRING ||
980 obj->Package.Elements[3].Type != ACPI_TYPE_INTEGER ||
981 obj->Package.Elements[4].Type != ACPI_TYPE_INTEGER ||
982 obj->Package.Elements[5].Type != ACPI_TYPE_INTEGER ||
983 obj->Package.Elements[6].Type != ACPI_TYPE_INTEGER ||
984 obj->Package.Elements[7].Type != ACPI_TYPE_INTEGER) {
985 acpi_hp_free_buffer(&out);
986 return (-EINVAL);
987 }
988
989 /* Skip prerequisites and optionally array. */
990 valuebase = 8 + obj->Package.Elements[7].Integer.Value;
991 if (obj->Package.Count <= valuebase) {
992 acpi_hp_free_buffer(&out);
993 return (-EINVAL);
994 }
995 if (obj->Package.Elements[valuebase].Type == ACPI_TYPE_INTEGER)
996 valuebase += 1 + obj->Package.Elements[valuebase].Integer.Value;
997
998 /* Check if we have value and enum. */
999 if (obj->Package.Count <= valuebase + 1 ||
1000 obj->Package.Elements[valuebase].Type != ACPI_TYPE_STRING ||
1001 obj->Package.Elements[valuebase+1].Type != ACPI_TYPE_INTEGER) {
1002 acpi_hp_free_buffer(&out);
1003 return (-EINVAL);
1004 }
1005 enumbase = valuebase + 1;
1006 if (obj->Package.Count <= valuebase +
1007 obj->Package.Elements[enumbase].Integer.Value) {
1008 acpi_hp_free_buffer(&out);
1009 return (-EINVAL);
1010 }
1011
1012 if (detail & ACPI_HP_CMI_DETAIL_PATHS) {
1013 strlcat(outbuf, acpi_hp_get_string_from_object(
1014 &obj->Package.Elements[2],
1015 string_buffer, sizeof(string_buffer)), outsize);
1016 outlen += 48;
1017 while (strlen(outbuf) < outlen)
1018 strlcat(outbuf, " ", outsize);
1019 }
1020 strlcat(outbuf, acpi_hp_get_string_from_object(
1021 &obj->Package.Elements[0],
1022 string_buffer, sizeof(string_buffer)), outsize);
1023 outlen += 43;
1024 while (strlen(outbuf) < outlen)
1025 strlcat(outbuf, " ", outsize);
1026 strlcat(outbuf, acpi_hp_get_string_from_object(
1027 &obj->Package.Elements[valuebase],
1028 string_buffer, sizeof(string_buffer)), outsize);
1029 outlen += 21;
1030 while (strlen(outbuf) < outlen)
1031 strlcat(outbuf, " ", outsize);
1032 for (i = 0; i < strlen(outbuf); ++i)
1033 if (outbuf[i] == '\\')
1034 outbuf[i] = '/';
1035 if (detail & ACPI_HP_CMI_DETAIL_ENUMS) {
1036 for (i = enumbase + 1; i < enumbase + 1 +
1037 obj->Package.Elements[enumbase].Integer.Value; ++i) {
1038 acpi_hp_get_string_from_object(
1039 &obj->Package.Elements[i],
1040 string_buffer, sizeof(string_buffer));
1041 if (strlen(string_buffer) > 1 ||
1042 (strlen(string_buffer) == 1 &&
1043 string_buffer[0] != ' ')) {
1044 if (has_enums)
1045 strlcat(outbuf, "/", outsize);
1046 else
1047 strlcat(outbuf, " (", outsize);
1048 strlcat(outbuf, string_buffer, outsize);
1049 has_enums = 1;
1050 }
1051 }
1052 }
1053 if (has_enums)
1054 strlcat(outbuf, ")", outsize);
1055 if (detail & ACPI_HP_CMI_DETAIL_FLAGS) {
1056 strlcat(outbuf, obj->Package.Elements[3].Integer.Value ?
1057 " [ReadOnly]" : "", outsize);
1058 strlcat(outbuf, obj->Package.Elements[4].Integer.Value ?
1059 "" : " [NOUI]", outsize);
1060 strlcat(outbuf, obj->Package.Elements[5].Integer.Value ?
1061 " [RPP]" : "", outsize);
1062 }
1063 *sequence = (UINT32) obj->Package.Elements[6].Integer.Value;
1064 acpi_hp_free_buffer(&out);
1065
1066 return (0);
1067 }
1068
1069 /*
1070 * Convert given two digit hex string (hexin) to an UINT8 referenced
1071 * by byteout.
1072 * Return != 0 if the was a problem (invalid input)
1073 */
acpi_hp_hex_to_int(const UINT8 * hexin,UINT8 * byteout)1074 static __inline int acpi_hp_hex_to_int(const UINT8 *hexin, UINT8 *byteout)
1075 {
1076 unsigned int hi;
1077 unsigned int lo;
1078
1079 hi = hexin[0];
1080 lo = hexin[1];
1081 if ('0' <= hi && hi <= '9')
1082 hi -= '0';
1083 else if ('A' <= hi && hi <= 'F')
1084 hi -= ('A' - 10);
1085 else if ('a' <= hi && hi <= 'f')
1086 hi -= ('a' - 10);
1087 else
1088 return (1);
1089 if ('0' <= lo && lo <= '9')
1090 lo -= '0';
1091 else if ('A' <= lo && lo <= 'F')
1092 lo -= ('A' - 10);
1093 else if ('a' <= lo && lo <= 'f')
1094 lo -= ('a' - 10);
1095 else
1096 return (1);
1097 *byteout = (hi << 4) + lo;
1098
1099 return (0);
1100 }
1101
1102 static void
acpi_hp_hex_decode(char * buffer)1103 acpi_hp_hex_decode(char* buffer)
1104 {
1105 int i;
1106 int length = strlen(buffer);
1107 UINT8 *uin;
1108 UINT8 uout;
1109
1110 if (rounddown((int)length, 2) == length || length < 10)
1111 return;
1112
1113 for (i = 0; i<length; ++i) {
1114 if (!((i+1)%3)) {
1115 if (buffer[i] != ' ')
1116 return;
1117 }
1118 else
1119 if (!((buffer[i] >= '0' && buffer[i] <= '9') ||
1120 (buffer[i] >= 'A' && buffer[i] <= 'F')))
1121 return;
1122 }
1123
1124 for (i = 0; i<length; i += 3) {
1125 uin = &buffer[i];
1126 uout = 0;
1127 acpi_hp_hex_to_int(uin, &uout);
1128 buffer[i/3] = (char) uout;
1129 }
1130 buffer[(length+1)/3] = 0;
1131 }
1132
1133 /*
1134 * open hpcmi device
1135 */
1136 static int
acpi_hp_hpcmi_open(struct cdev * dev,int flags,int mode,struct thread * td)1137 acpi_hp_hpcmi_open(struct cdev* dev, int flags, int mode, struct thread *td)
1138 {
1139 struct acpi_hp_softc *sc;
1140 int ret;
1141
1142 if (dev == NULL || dev->si_drv1 == NULL)
1143 return (EBADF);
1144 sc = dev->si_drv1;
1145
1146 ACPI_SERIAL_BEGIN(hp);
1147 if (sc->hpcmi_open_pid != 0) {
1148 ret = EBUSY;
1149 }
1150 else {
1151 if (sbuf_new(&sc->hpcmi_sbuf, NULL, 4096, SBUF_AUTOEXTEND)
1152 == NULL) {
1153 ret = ENXIO;
1154 } else {
1155 sc->hpcmi_open_pid = td->td_proc->p_pid;
1156 sc->hpcmi_bufptr = 0;
1157 ret = 0;
1158 }
1159 }
1160 ACPI_SERIAL_END(hp);
1161
1162 return (ret);
1163 }
1164
1165 /*
1166 * close hpcmi device
1167 */
1168 static int
acpi_hp_hpcmi_close(struct cdev * dev,int flags,int mode,struct thread * td)1169 acpi_hp_hpcmi_close(struct cdev* dev, int flags, int mode, struct thread *td)
1170 {
1171 struct acpi_hp_softc *sc;
1172 int ret;
1173
1174 if (dev == NULL || dev->si_drv1 == NULL)
1175 return (EBADF);
1176 sc = dev->si_drv1;
1177
1178 ACPI_SERIAL_BEGIN(hp);
1179 if (sc->hpcmi_open_pid == 0) {
1180 ret = EBADF;
1181 }
1182 else {
1183 if (sc->hpcmi_bufptr != -1) {
1184 sbuf_delete(&sc->hpcmi_sbuf);
1185 sc->hpcmi_bufptr = -1;
1186 }
1187 sc->hpcmi_open_pid = 0;
1188 ret = 0;
1189 }
1190 ACPI_SERIAL_END(hp);
1191
1192 return (ret);
1193 }
1194
1195 /*
1196 * Read from hpcmi bios information
1197 */
1198 static int
acpi_hp_hpcmi_read(struct cdev * dev,struct uio * buf,int flag)1199 acpi_hp_hpcmi_read(struct cdev *dev, struct uio *buf, int flag)
1200 {
1201 struct acpi_hp_softc *sc;
1202 int pos, i, l, ret;
1203 UINT8 instance;
1204 UINT8 maxInstance;
1205 UINT32 sequence;
1206 char line[1025];
1207
1208 if (dev == NULL || dev->si_drv1 == NULL)
1209 return (EBADF);
1210 sc = dev->si_drv1;
1211
1212 ACPI_SERIAL_BEGIN(hp);
1213 if (sc->hpcmi_open_pid != buf->uio_td->td_proc->p_pid
1214 || sc->hpcmi_bufptr == -1) {
1215 ret = EBADF;
1216 }
1217 else {
1218 if (!sbuf_done(&sc->hpcmi_sbuf)) {
1219 if (sc->cmi_order_size < 0) {
1220 maxInstance = sc->has_cmi;
1221 if (!(sc->cmi_detail &
1222 ACPI_HP_CMI_DETAIL_SHOW_MAX_INSTANCE) &&
1223 maxInstance > 0) {
1224 maxInstance--;
1225 }
1226 sc->cmi_order_size = 0;
1227 for (instance = 0; instance < maxInstance;
1228 ++instance) {
1229 if (acpi_hp_get_cmi_block(sc->wmi_dev,
1230 ACPI_HP_WMI_CMI_GUID, instance,
1231 line, sizeof(line), &sequence,
1232 sc->cmi_detail)) {
1233 instance = maxInstance;
1234 }
1235 else {
1236 pos = sc->cmi_order_size;
1237 for (i=0;
1238 i<sc->cmi_order_size && i<127;
1239 ++i) {
1240 if (sc->cmi_order[i].sequence > sequence) {
1241 pos = i;
1242 break;
1243 }
1244 }
1245 for (i=sc->cmi_order_size;
1246 i>pos;
1247 --i) {
1248 sc->cmi_order[i].sequence =
1249 sc->cmi_order[i-1].sequence;
1250 sc->cmi_order[i].instance =
1251 sc->cmi_order[i-1].instance;
1252 }
1253 sc->cmi_order[pos].sequence =
1254 sequence;
1255 sc->cmi_order[pos].instance =
1256 instance;
1257 sc->cmi_order_size++;
1258 }
1259 }
1260 }
1261 for (i=0; i<sc->cmi_order_size; ++i) {
1262 if (!acpi_hp_get_cmi_block(sc->wmi_dev,
1263 ACPI_HP_WMI_CMI_GUID,
1264 sc->cmi_order[i].instance, line, sizeof(line),
1265 &sequence, sc->cmi_detail)) {
1266 sbuf_printf(&sc->hpcmi_sbuf, "%s\n", line);
1267 }
1268 }
1269 sbuf_finish(&sc->hpcmi_sbuf);
1270 }
1271 if (sbuf_len(&sc->hpcmi_sbuf) <= 0) {
1272 sbuf_delete(&sc->hpcmi_sbuf);
1273 sc->hpcmi_bufptr = -1;
1274 sc->hpcmi_open_pid = 0;
1275 ret = ENOMEM;
1276 } else {
1277 l = min(buf->uio_resid, sbuf_len(&sc->hpcmi_sbuf) -
1278 sc->hpcmi_bufptr);
1279 ret = (l > 0)?uiomove(sbuf_data(&sc->hpcmi_sbuf) +
1280 sc->hpcmi_bufptr, l, buf) : 0;
1281 sc->hpcmi_bufptr += l;
1282 }
1283 }
1284 ACPI_SERIAL_END(hp);
1285
1286 return (ret);
1287 }
1288