| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: wmi_acpi.c,v 1.10 2010/10/25 15:38:05 jruoho Exp $ */ | | 1 | /* $NetBSD: wmi_acpi.c,v 1.11 2010/10/28 15:55:04 jruoho Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2009, 2010 Jukka Ruohonen <jruohonen@iki.fi> | | 4 | * Copyright (c) 2009, 2010 Jukka Ruohonen <jruohonen@iki.fi> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * | | 10 | * |
11 | * 1. Redistributions of source code must retain the above copyright | | 11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. | | 12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright | | 13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the | | 14 | * notice, this list of conditions and the following disclaimer in the |
| @@ -17,71 +17,74 @@ | | | @@ -17,71 +17,74 @@ |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | | 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | | 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 | * SUCH DAMAGE. | | 27 | * SUCH DAMAGE. |
28 | */ | | 28 | */ |
29 | #include <sys/cdefs.h> | | 29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: wmi_acpi.c,v 1.10 2010/10/25 15:38:05 jruoho Exp $"); | | 30 | __KERNEL_RCSID(0, "$NetBSD: wmi_acpi.c,v 1.11 2010/10/28 15:55:04 jruoho Exp $"); |
31 | | | 31 | |
32 | #include <sys/param.h> | | 32 | #include <sys/param.h> |
33 | #include <sys/device.h> | | 33 | #include <sys/device.h> |
34 | #include <sys/endian.h> | | 34 | #include <sys/endian.h> |
35 | #include <sys/kmem.h> | | 35 | #include <sys/kmem.h> |
36 | #include <sys/systm.h> | | 36 | #include <sys/systm.h> |
37 | #include <sys/module.h> | | 37 | #include <sys/module.h> |
38 | | | 38 | |
39 | #include <dev/acpi/acpireg.h> | | 39 | #include <dev/acpi/acpireg.h> |
40 | #include <dev/acpi/acpivar.h> | | 40 | #include <dev/acpi/acpivar.h> |
| | | 41 | #include <dev/acpi/acpi_ecvar.h> |
41 | #include <dev/acpi/wmi/wmi_acpivar.h> | | 42 | #include <dev/acpi/wmi/wmi_acpivar.h> |
42 | | | 43 | |
43 | #define _COMPONENT ACPI_RESOURCE_COMPONENT | | 44 | #define _COMPONENT ACPI_RESOURCE_COMPONENT |
44 | ACPI_MODULE_NAME ("wmi_acpi") | | 45 | ACPI_MODULE_NAME ("wmi_acpi") |
45 | | | 46 | |
46 | /* | | 47 | /* |
47 | * This implements something called "Microsoft Windows Management | | 48 | * This implements something called "Microsoft Windows Management |
48 | * Instrumentation" (WMI). This subset of ACPI is desribed in: | | 49 | * Instrumentation" (WMI). This subset of ACPI is desribed in: |
49 | * | | 50 | * |
50 | * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx | | 51 | * http://www.microsoft.com/whdc/system/pnppwr/wmi/wmi-acpi.mspx |
51 | * | | 52 | * |
52 | * (Obtained on Thu Feb 12 18:21:44 EET 2009.) | | 53 | * (Obtained on Thu Feb 12 18:21:44 EET 2009.) |
53 | */ | | 54 | */ |
54 | | | 55 | |
55 | static int acpi_wmi_match(device_t, cfdata_t, void *); | | 56 | static int acpi_wmi_match(device_t, cfdata_t, void *); |
56 | static void acpi_wmi_attach(device_t, device_t, void *); | | 57 | static void acpi_wmi_attach(device_t, device_t, void *); |
57 | static int acpi_wmi_detach(device_t, int); | | 58 | static int acpi_wmi_detach(device_t, int); |
58 | static int acpi_wmi_rescan(device_t, const char *, const int *); | | 59 | static int acpi_wmi_rescan(device_t, const char *, const int *); |
59 | static void acpi_wmi_childdet(device_t, device_t); | | 60 | static void acpi_wmi_childdet(device_t, device_t); |
60 | static int acpi_wmi_print(void *, const char *); | | 61 | static int acpi_wmi_print(void *, const char *); |
61 | static bool acpi_wmi_init(struct acpi_wmi_softc *); | | 62 | static bool acpi_wmi_init(struct acpi_wmi_softc *); |
62 | static bool acpi_wmi_add(struct acpi_wmi_softc *, ACPI_OBJECT *); | | 63 | static void acpi_wmi_init_ec(struct acpi_wmi_softc *); |
63 | static void acpi_wmi_del(struct acpi_wmi_softc *); | | 64 | static bool acpi_wmi_add(struct acpi_wmi_softc *, ACPI_OBJECT *); |
64 | static void acpi_wmi_dump(struct acpi_wmi_softc *); | | 65 | static void acpi_wmi_del(struct acpi_wmi_softc *); |
65 | | | 66 | static void acpi_wmi_dump(struct acpi_wmi_softc *); |
66 | static ACPI_STATUS acpi_wmi_guid_get(struct acpi_wmi_softc *, | | 67 | static ACPI_STATUS acpi_wmi_guid_get(struct acpi_wmi_softc *, |
67 | const char *, struct wmi_t **); | | 68 | const char *, struct wmi_t **); |
68 | static void acpi_wmi_event_add(struct acpi_wmi_softc *); | | 69 | static void acpi_wmi_event_add(struct acpi_wmi_softc *); |
69 | static void acpi_wmi_event_del(struct acpi_wmi_softc *); | | 70 | static void acpi_wmi_event_del(struct acpi_wmi_softc *); |
70 | static void acpi_wmi_event_handler(ACPI_HANDLE, uint32_t, void *); | | 71 | static void acpi_wmi_event_handler(ACPI_HANDLE, uint32_t, void *); |
71 | static bool acpi_wmi_suspend(device_t, const pmf_qual_t *); | | 72 | static ACPI_STATUS acpi_wmi_ec_handler(uint32_t, ACPI_PHYSICAL_ADDRESS, |
72 | static bool acpi_wmi_resume(device_t, const pmf_qual_t *); | | 73 | uint32_t, ACPI_INTEGER *, void *, void *); |
73 | static ACPI_STATUS acpi_wmi_enable(ACPI_HANDLE, const char *, bool, bool); | | 74 | static bool acpi_wmi_suspend(device_t, const pmf_qual_t *); |
74 | static bool acpi_wmi_input(struct wmi_t *, uint8_t, uint8_t); | | 75 | static bool acpi_wmi_resume(device_t, const pmf_qual_t *); |
| | | 76 | static ACPI_STATUS acpi_wmi_enable(ACPI_HANDLE, const char *, bool, bool); |
| | | 77 | static bool acpi_wmi_input(struct wmi_t *, uint8_t, uint8_t); |
75 | | | 78 | |
76 | const char * const acpi_wmi_ids[] = { | | 79 | const char * const acpi_wmi_ids[] = { |
77 | "PNP0C14", | | 80 | "PNP0C14", |
78 | "pnp0c14", | | 81 | "pnp0c14", |
79 | NULL | | 82 | NULL |
80 | }; | | 83 | }; |
81 | | | 84 | |
82 | CFATTACH_DECL2_NEW(acpiwmi, sizeof(struct acpi_wmi_softc), | | 85 | CFATTACH_DECL2_NEW(acpiwmi, sizeof(struct acpi_wmi_softc), |
83 | acpi_wmi_match, acpi_wmi_attach, acpi_wmi_detach, NULL, | | 86 | acpi_wmi_match, acpi_wmi_attach, acpi_wmi_detach, NULL, |
84 | acpi_wmi_rescan, acpi_wmi_childdet); | | 87 | acpi_wmi_rescan, acpi_wmi_childdet); |
85 | | | 88 | |
86 | static int | | 89 | static int |
87 | acpi_wmi_match(device_t parent, cfdata_t match, void *aux) | | 90 | acpi_wmi_match(device_t parent, cfdata_t match, void *aux) |
| @@ -94,49 +97,56 @@ acpi_wmi_match(device_t parent, cfdata_t | | | @@ -94,49 +97,56 @@ acpi_wmi_match(device_t parent, cfdata_t |
94 | return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_wmi_ids); | | 97 | return acpi_match_hid(aa->aa_node->ad_devinfo, acpi_wmi_ids); |
95 | } | | 98 | } |
96 | | | 99 | |
97 | static void | | 100 | static void |
98 | acpi_wmi_attach(device_t parent, device_t self, void *aux) | | 101 | acpi_wmi_attach(device_t parent, device_t self, void *aux) |
99 | { | | 102 | { |
100 | struct acpi_wmi_softc *sc = device_private(self); | | 103 | struct acpi_wmi_softc *sc = device_private(self); |
101 | struct acpi_attach_args *aa = aux; | | 104 | struct acpi_attach_args *aa = aux; |
102 | | | 105 | |
103 | sc->sc_dev = self; | | 106 | sc->sc_dev = self; |
104 | sc->sc_node = aa->aa_node; | | 107 | sc->sc_node = aa->aa_node; |
105 | | | 108 | |
106 | sc->sc_child = NULL; | | 109 | sc->sc_child = NULL; |
| | | 110 | sc->sc_ecdev = NULL; |
107 | sc->sc_handler = NULL; | | 111 | sc->sc_handler = NULL; |
108 | | | 112 | |
109 | aprint_naive("\n"); | | 113 | aprint_naive("\n"); |
110 | aprint_normal(": ACPI WMI Interface\n"); | | 114 | aprint_normal(": ACPI WMI Interface\n"); |
111 | | | 115 | |
112 | if (acpi_wmi_init(sc) != true) | | 116 | if (acpi_wmi_init(sc) != true) |
113 | return; | | 117 | return; |
114 | | | 118 | |
115 | acpi_wmi_dump(sc); | | 119 | acpi_wmi_dump(sc); |
| | | 120 | acpi_wmi_init_ec(sc); |
116 | acpi_wmi_event_add(sc); | | 121 | acpi_wmi_event_add(sc); |
117 | | | | |
118 | acpi_wmi_rescan(self, NULL, NULL); | | 122 | acpi_wmi_rescan(self, NULL, NULL); |
119 | | | 123 | |
120 | (void)pmf_device_register(self, acpi_wmi_suspend, acpi_wmi_resume); | | 124 | (void)pmf_device_register(self, acpi_wmi_suspend, acpi_wmi_resume); |
121 | } | | 125 | } |
122 | | | 126 | |
123 | static int | | 127 | static int |
124 | acpi_wmi_detach(device_t self, int flags) | | 128 | acpi_wmi_detach(device_t self, int flags) |
125 | { | | 129 | { |
126 | struct acpi_wmi_softc *sc = device_private(self); | | 130 | struct acpi_wmi_softc *sc = device_private(self); |
127 | | | 131 | |
128 | acpi_wmi_event_del(sc); | | 132 | acpi_wmi_event_del(sc); |
129 | | | 133 | |
| | | 134 | if (sc->sc_ecdev != NULL) { |
| | | 135 | |
| | | 136 | (void)AcpiRemoveAddressSpaceHandler(sc->sc_node->ad_handle, |
| | | 137 | ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler); |
| | | 138 | } |
| | | 139 | |
130 | if (sc->sc_child != NULL) | | 140 | if (sc->sc_child != NULL) |
131 | (void)config_detach(sc->sc_child, flags); | | 141 | (void)config_detach(sc->sc_child, flags); |
132 | | | 142 | |
133 | acpi_wmi_del(sc); | | 143 | acpi_wmi_del(sc); |
134 | pmf_device_deregister(self); | | 144 | pmf_device_deregister(self); |
135 | | | 145 | |
136 | return 0; | | 146 | return 0; |
137 | } | | 147 | } |
138 | | | 148 | |
139 | static int | | 149 | static int |
140 | acpi_wmi_rescan(device_t self, const char *ifattr, const int *locators) | | 150 | acpi_wmi_rescan(device_t self, const char *ifattr, const int *locators) |
141 | { | | 151 | { |
142 | struct acpi_wmi_softc *sc = device_private(self); | | 152 | struct acpi_wmi_softc *sc = device_private(self); |
| @@ -281,26 +291,56 @@ acpi_wmi_dump(struct acpi_wmi_softc *sc) | | | @@ -281,26 +291,56 @@ acpi_wmi_dump(struct acpi_wmi_softc *sc) |
281 | wmi->guid.data1, wmi->guid.data2, wmi->guid.data3); | | 291 | wmi->guid.data1, wmi->guid.data2, wmi->guid.data3); |
282 | | | 292 | |
283 | aprint_debug("%02X%02X-%02X%02X%02X%02X%02X%02X} ", | | 293 | aprint_debug("%02X%02X-%02X%02X%02X%02X%02X%02X} ", |
284 | wmi->guid.data4[0], wmi->guid.data4[1], | | 294 | wmi->guid.data4[0], wmi->guid.data4[1], |
285 | wmi->guid.data4[2], wmi->guid.data4[3], | | 295 | wmi->guid.data4[2], wmi->guid.data4[3], |
286 | wmi->guid.data4[4], wmi->guid.data4[5], | | 296 | wmi->guid.data4[4], wmi->guid.data4[5], |
287 | wmi->guid.data4[6], wmi->guid.data4[7]); | | 297 | wmi->guid.data4[6], wmi->guid.data4[7]); |
288 | | | 298 | |
289 | aprint_debug("oid %04X count %02X flags %02X\n", | | 299 | aprint_debug("oid %04X count %02X flags %02X\n", |
290 | UGET16(wmi->guid.oid), wmi->guid.count, wmi->guid.flags); | | 300 | UGET16(wmi->guid.oid), wmi->guid.count, wmi->guid.flags); |
291 | } | | 301 | } |
292 | } | | 302 | } |
293 | | | 303 | |
| | | 304 | static void |
| | | 305 | acpi_wmi_init_ec(struct acpi_wmi_softc *sc) |
| | | 306 | { |
| | | 307 | ACPI_STATUS rv; |
| | | 308 | deviter_t i; |
| | | 309 | device_t d; |
| | | 310 | |
| | | 311 | d = deviter_first(&i, DEVITER_F_ROOT_FIRST); |
| | | 312 | |
| | | 313 | for (; d != NULL; d = deviter_next(&i)) { |
| | | 314 | |
| | | 315 | if (device_is_a(d, "acpiec") != false || |
| | | 316 | device_is_a(d, "acpiecdt") != false) { |
| | | 317 | sc->sc_ecdev = d; |
| | | 318 | break; |
| | | 319 | } |
| | | 320 | } |
| | | 321 | |
| | | 322 | deviter_release(&i); |
| | | 323 | |
| | | 324 | if (sc->sc_ecdev == NULL) |
| | | 325 | return; |
| | | 326 | |
| | | 327 | rv = AcpiInstallAddressSpaceHandler(sc->sc_node->ad_handle, |
| | | 328 | ACPI_ADR_SPACE_EC, acpi_wmi_ec_handler, NULL, sc); |
| | | 329 | |
| | | 330 | if (ACPI_FAILURE(rv)) |
| | | 331 | sc->sc_ecdev = NULL; |
| | | 332 | } |
| | | 333 | |
294 | static ACPI_STATUS | | 334 | static ACPI_STATUS |
295 | acpi_wmi_guid_get(struct acpi_wmi_softc *sc, | | 335 | acpi_wmi_guid_get(struct acpi_wmi_softc *sc, |
296 | const char *src, struct wmi_t **out) | | 336 | const char *src, struct wmi_t **out) |
297 | { | | 337 | { |
298 | struct wmi_t *wmi; | | 338 | struct wmi_t *wmi; |
299 | struct guid_t *guid; | | 339 | struct guid_t *guid; |
300 | char bin[16]; | | 340 | char bin[16]; |
301 | char hex[2]; | | 341 | char hex[2]; |
302 | const char *ptr; | | 342 | const char *ptr; |
303 | uint8_t i; | | 343 | uint8_t i; |
304 | | | 344 | |
305 | if (sc == NULL || src == NULL || strlen(src) != 36) | | 345 | if (sc == NULL || src == NULL || strlen(src) != 36) |
306 | return AE_BAD_PARAMETER; | | 346 | return AE_BAD_PARAMETER; |
| @@ -500,26 +540,58 @@ acpi_wmi_event_register(device_t self, A | | | @@ -500,26 +540,58 @@ acpi_wmi_event_register(device_t self, A |
500 | | | 540 | |
501 | sc->sc_handler = handler; | | 541 | sc->sc_handler = handler; |
502 | | | 542 | |
503 | return AE_OK; | | 543 | return AE_OK; |
504 | } | | 544 | } |
505 | | | 545 | |
506 | ACPI_STATUS | | 546 | ACPI_STATUS |
507 | acpi_wmi_event_deregister(device_t self) | | 547 | acpi_wmi_event_deregister(device_t self) |
508 | { | | 548 | { |
509 | return acpi_wmi_event_register(self, NULL); | | 549 | return acpi_wmi_event_register(self, NULL); |
510 | } | | 550 | } |
511 | | | 551 | |
512 | /* | | 552 | /* |
| | | 553 | * Handler for EC regions, which may be embedded in WMI. |
| | | 554 | */ |
| | | 555 | static ACPI_STATUS |
| | | 556 | acpi_wmi_ec_handler(uint32_t func, ACPI_PHYSICAL_ADDRESS addr, |
| | | 557 | uint32_t width, ACPI_INTEGER *val, void *setup, void *aux) |
| | | 558 | { |
| | | 559 | struct acpi_wmi_softc *sc = aux; |
| | | 560 | |
| | | 561 | if (aux == NULL || val == NULL) |
| | | 562 | return AE_BAD_PARAMETER; |
| | | 563 | |
| | | 564 | if (addr > 0xFF || width % 8 != 0) |
| | | 565 | return AE_BAD_ADDRESS; |
| | | 566 | |
| | | 567 | switch (func) { |
| | | 568 | |
| | | 569 | case ACPI_READ: |
| | | 570 | (void)acpiec_bus_read(sc->sc_ecdev, addr, val, width); |
| | | 571 | break; |
| | | 572 | |
| | | 573 | case ACPI_WRITE: |
| | | 574 | (void)acpiec_bus_write(sc->sc_ecdev, addr, *val, width); |
| | | 575 | break; |
| | | 576 | |
| | | 577 | default: |
| | | 578 | return AE_BAD_PARAMETER; |
| | | 579 | } |
| | | 580 | |
| | | 581 | return AE_OK; |
| | | 582 | } |
| | | 583 | |
| | | 584 | /* |
513 | * As there is no prior knowledge about the expensive | | 585 | * As there is no prior knowledge about the expensive |
514 | * events that cause "significant overhead", try to | | 586 | * events that cause "significant overhead", try to |
515 | * disable (enable) these before suspending (resuming). | | 587 | * disable (enable) these before suspending (resuming). |
516 | */ | | 588 | */ |
517 | static bool | | 589 | static bool |
518 | acpi_wmi_suspend(device_t self, const pmf_qual_t *qual) | | 590 | acpi_wmi_suspend(device_t self, const pmf_qual_t *qual) |
519 | { | | 591 | { |
520 | struct acpi_wmi_softc *sc = device_private(self); | | 592 | struct acpi_wmi_softc *sc = device_private(self); |
521 | | | 593 | |
522 | acpi_wmi_event_del(sc); | | 594 | acpi_wmi_event_del(sc); |
523 | | | 595 | |
524 | return true; | | 596 | return true; |
525 | } | | 597 | } |