| @@ -1,805 +1,877 @@ | | | @@ -1,805 +1,877 @@ |
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 |
15 | * documentation and/or other materials provided with the distribution. | | 15 | * documentation and/or other materials provided with the distribution. |
16 | * | | 16 | * |
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) |
88 | { | | 91 | { |
89 | struct acpi_attach_args *aa = aux; | | 92 | struct acpi_attach_args *aa = aux; |
90 | | | 93 | |
91 | if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) | | 94 | if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) |
92 | return 0; | | 95 | return 0; |
93 | | | 96 | |
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); |
143 | | | 153 | |
144 | if (ifattr_match(ifattr, "acpiwmibus") && sc->sc_child == NULL) | | 154 | if (ifattr_match(ifattr, "acpiwmibus") && sc->sc_child == NULL) |
145 | sc->sc_child = config_found_ia(self, "acpiwmibus", | | 155 | sc->sc_child = config_found_ia(self, "acpiwmibus", |
146 | NULL, acpi_wmi_print); | | 156 | NULL, acpi_wmi_print); |
147 | | | 157 | |
148 | return 0; | | 158 | return 0; |
149 | } | | 159 | } |
150 | | | 160 | |
151 | static void | | 161 | static void |
152 | acpi_wmi_childdet(device_t self, device_t child) | | 162 | acpi_wmi_childdet(device_t self, device_t child) |
153 | { | | 163 | { |
154 | struct acpi_wmi_softc *sc = device_private(self); | | 164 | struct acpi_wmi_softc *sc = device_private(self); |
155 | | | 165 | |
156 | if (sc->sc_child == child) | | 166 | if (sc->sc_child == child) |
157 | sc->sc_child = NULL; | | 167 | sc->sc_child = NULL; |
158 | } | | 168 | } |
159 | | | 169 | |
160 | static int | | 170 | static int |
161 | acpi_wmi_print(void *aux, const char *pnp) | | 171 | acpi_wmi_print(void *aux, const char *pnp) |
162 | { | | 172 | { |
163 | | | 173 | |
164 | if (pnp != NULL) | | 174 | if (pnp != NULL) |
165 | aprint_normal("acpiwmibus at %s", pnp); | | 175 | aprint_normal("acpiwmibus at %s", pnp); |
166 | | | 176 | |
167 | return UNCONF; | | 177 | return UNCONF; |
168 | } | | 178 | } |
169 | | | 179 | |
170 | static bool | | 180 | static bool |
171 | acpi_wmi_init(struct acpi_wmi_softc *sc) | | 181 | acpi_wmi_init(struct acpi_wmi_softc *sc) |
172 | { | | 182 | { |
173 | ACPI_OBJECT *obj; | | 183 | ACPI_OBJECT *obj; |
174 | ACPI_BUFFER buf; | | 184 | ACPI_BUFFER buf; |
175 | ACPI_STATUS rv; | | 185 | ACPI_STATUS rv; |
176 | uint32_t len; | | 186 | uint32_t len; |
177 | | | 187 | |
178 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "_WDG", &buf); | | 188 | rv = acpi_eval_struct(sc->sc_node->ad_handle, "_WDG", &buf); |
179 | | | 189 | |
180 | if (ACPI_FAILURE(rv)) | | 190 | if (ACPI_FAILURE(rv)) |
181 | goto fail; | | 191 | goto fail; |
182 | | | 192 | |
183 | obj = buf.Pointer; | | 193 | obj = buf.Pointer; |
184 | | | 194 | |
185 | if (obj->Type != ACPI_TYPE_BUFFER) { | | 195 | if (obj->Type != ACPI_TYPE_BUFFER) { |
186 | rv = AE_TYPE; | | 196 | rv = AE_TYPE; |
187 | goto fail; | | 197 | goto fail; |
188 | } | | 198 | } |
189 | | | 199 | |
190 | len = obj->Buffer.Length; | | 200 | len = obj->Buffer.Length; |
191 | | | 201 | |
192 | if (len != obj->Package.Count) { | | 202 | if (len != obj->Package.Count) { |
193 | rv = AE_BAD_VALUE; | | 203 | rv = AE_BAD_VALUE; |
194 | goto fail; | | 204 | goto fail; |
195 | } | | 205 | } |
196 | | | 206 | |
197 | CTASSERT(sizeof(struct guid_t) == 20); | | 207 | CTASSERT(sizeof(struct guid_t) == 20); |
198 | | | 208 | |
199 | if (len < sizeof(struct guid_t) || | | 209 | if (len < sizeof(struct guid_t) || |
200 | len % sizeof(struct guid_t) != 0) { | | 210 | len % sizeof(struct guid_t) != 0) { |
201 | rv = AE_BAD_DATA; | | 211 | rv = AE_BAD_DATA; |
202 | goto fail; | | 212 | goto fail; |
203 | } | | 213 | } |
204 | | | 214 | |
205 | return acpi_wmi_add(sc, obj); | | 215 | return acpi_wmi_add(sc, obj); |
206 | | | 216 | |
207 | fail: | | 217 | fail: |
208 | aprint_error_dev(sc->sc_dev, "failed to evaluate _WDG: %s\n", | | 218 | aprint_error_dev(sc->sc_dev, "failed to evaluate _WDG: %s\n", |
209 | AcpiFormatException(rv)); | | 219 | AcpiFormatException(rv)); |
210 | | | 220 | |
211 | if (buf.Pointer != NULL) | | 221 | if (buf.Pointer != NULL) |
212 | ACPI_FREE(buf.Pointer); | | 222 | ACPI_FREE(buf.Pointer); |
213 | | | 223 | |
214 | return false; | | 224 | return false; |
215 | } | | 225 | } |
216 | | | 226 | |
217 | static bool | | 227 | static bool |
218 | acpi_wmi_add(struct acpi_wmi_softc *sc, ACPI_OBJECT *obj) | | 228 | acpi_wmi_add(struct acpi_wmi_softc *sc, ACPI_OBJECT *obj) |
219 | { | | 229 | { |
220 | struct wmi_t *wmi; | | 230 | struct wmi_t *wmi; |
221 | size_t i, n, offset, siz; | | 231 | size_t i, n, offset, siz; |
222 | | | 232 | |
223 | siz = sizeof(struct guid_t); | | 233 | siz = sizeof(struct guid_t); |
224 | n = obj->Buffer.Length / siz; | | 234 | n = obj->Buffer.Length / siz; |
225 | | | 235 | |
226 | SIMPLEQ_INIT(&sc->wmi_head); | | 236 | SIMPLEQ_INIT(&sc->wmi_head); |
227 | | | 237 | |
228 | for (i = offset = 0; i < n; ++i) { | | 238 | for (i = offset = 0; i < n; ++i) { |
229 | | | 239 | |
230 | if ((wmi = kmem_zalloc(sizeof(*wmi), KM_NOSLEEP)) == NULL) | | 240 | if ((wmi = kmem_zalloc(sizeof(*wmi), KM_NOSLEEP)) == NULL) |
231 | goto fail; | | 241 | goto fail; |
232 | | | 242 | |
233 | (void)memcpy(&wmi->guid, obj->Buffer.Pointer + offset, siz); | | 243 | (void)memcpy(&wmi->guid, obj->Buffer.Pointer + offset, siz); |
234 | | | 244 | |
235 | wmi->eevent = false; | | 245 | wmi->eevent = false; |
236 | offset = offset + siz; | | 246 | offset = offset + siz; |
237 | | | 247 | |
238 | SIMPLEQ_INSERT_TAIL(&sc->wmi_head, wmi, wmi_link); | | 248 | SIMPLEQ_INSERT_TAIL(&sc->wmi_head, wmi, wmi_link); |
239 | } | | 249 | } |
240 | | | 250 | |
241 | ACPI_FREE(obj); | | 251 | ACPI_FREE(obj); |
242 | | | 252 | |
243 | return true; | | 253 | return true; |
244 | | | 254 | |
245 | fail: | | 255 | fail: |
246 | ACPI_FREE(obj); | | 256 | ACPI_FREE(obj); |
247 | acpi_wmi_del(sc); | | 257 | acpi_wmi_del(sc); |
248 | | | 258 | |
249 | return false; | | 259 | return false; |
250 | } | | 260 | } |
251 | | | 261 | |
252 | static void | | 262 | static void |
253 | acpi_wmi_del(struct acpi_wmi_softc *sc) | | 263 | acpi_wmi_del(struct acpi_wmi_softc *sc) |
254 | { | | 264 | { |
255 | struct wmi_t *wmi; | | 265 | struct wmi_t *wmi; |
256 | | | 266 | |
257 | if (SIMPLEQ_EMPTY(&sc->wmi_head) != 0) | | 267 | if (SIMPLEQ_EMPTY(&sc->wmi_head) != 0) |
258 | return; | | 268 | return; |
259 | | | 269 | |
260 | while (SIMPLEQ_FIRST(&sc->wmi_head) != NULL) { | | 270 | while (SIMPLEQ_FIRST(&sc->wmi_head) != NULL) { |
261 | | | 271 | |
262 | wmi = SIMPLEQ_FIRST(&sc->wmi_head); | | 272 | wmi = SIMPLEQ_FIRST(&sc->wmi_head); |
263 | SIMPLEQ_REMOVE_HEAD(&sc->wmi_head, wmi_link); | | 273 | SIMPLEQ_REMOVE_HEAD(&sc->wmi_head, wmi_link); |
264 | | | 274 | |
265 | KASSERT(wmi != NULL); | | 275 | KASSERT(wmi != NULL); |
266 | | | 276 | |
267 | kmem_free(wmi, sizeof(*wmi)); | | 277 | kmem_free(wmi, sizeof(*wmi)); |
268 | } | | 278 | } |
269 | } | | 279 | } |
270 | | | 280 | |
271 | static void | | 281 | static void |
272 | acpi_wmi_dump(struct acpi_wmi_softc *sc) | | 282 | acpi_wmi_dump(struct acpi_wmi_softc *sc) |
273 | { | | 283 | { |
274 | struct wmi_t *wmi; | | 284 | struct wmi_t *wmi; |
275 | | | 285 | |
276 | KASSERT(SIMPLEQ_EMPTY(&sc->wmi_head) == 0); | | 286 | KASSERT(SIMPLEQ_EMPTY(&sc->wmi_head) == 0); |
277 | | | 287 | |
278 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { | | 288 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { |
279 | | | 289 | |
280 | aprint_debug_dev(sc->sc_dev, "{%08X-%04X-%04X-", | | 290 | aprint_debug_dev(sc->sc_dev, "{%08X-%04X-%04X-", |
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; |
307 | | | 347 | |
308 | for (ptr = src, i = 0; i < 16; i++) { | | 348 | for (ptr = src, i = 0; i < 16; i++) { |
309 | | | 349 | |
310 | if (*ptr == '-') | | 350 | if (*ptr == '-') |
311 | ptr++; | | 351 | ptr++; |
312 | | | 352 | |
313 | (void)memcpy(hex, ptr, 2); | | 353 | (void)memcpy(hex, ptr, 2); |
314 | | | 354 | |
315 | if (HEXCHAR(hex[0]) == 0 || HEXCHAR(hex[1]) == 0) | | 355 | if (HEXCHAR(hex[0]) == 0 || HEXCHAR(hex[1]) == 0) |
316 | return AE_BAD_HEX_CONSTANT; | | 356 | return AE_BAD_HEX_CONSTANT; |
317 | | | 357 | |
318 | bin[i] = strtoul(hex, NULL, 16) & 0xFF; | | 358 | bin[i] = strtoul(hex, NULL, 16) & 0xFF; |
319 | | | 359 | |
320 | ptr++; | | 360 | ptr++; |
321 | ptr++; | | 361 | ptr++; |
322 | } | | 362 | } |
323 | | | 363 | |
324 | guid = (struct guid_t *)bin; | | 364 | guid = (struct guid_t *)bin; |
325 | guid->data1 = be32toh(guid->data1); | | 365 | guid->data1 = be32toh(guid->data1); |
326 | guid->data2 = be16toh(guid->data2); | | 366 | guid->data2 = be16toh(guid->data2); |
327 | guid->data3 = be16toh(guid->data3); | | 367 | guid->data3 = be16toh(guid->data3); |
328 | | | 368 | |
329 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { | | 369 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { |
330 | | | 370 | |
331 | if (GUIDCMP(guid, &wmi->guid) != 0) { | | 371 | if (GUIDCMP(guid, &wmi->guid) != 0) { |
332 | | | 372 | |
333 | if (out != NULL) | | 373 | if (out != NULL) |
334 | *out = wmi; | | 374 | *out = wmi; |
335 | | | 375 | |
336 | return AE_OK; | | 376 | return AE_OK; |
337 | } | | 377 | } |
338 | } | | 378 | } |
339 | | | 379 | |
340 | return AE_NOT_FOUND; | | 380 | return AE_NOT_FOUND; |
341 | } | | 381 | } |
342 | | | 382 | |
343 | /* | | 383 | /* |
344 | * Checks if a GUID is present. Child devices | | 384 | * Checks if a GUID is present. Child devices |
345 | * can use this in their autoconf(9) routines. | | 385 | * can use this in their autoconf(9) routines. |
346 | */ | | 386 | */ |
347 | int | | 387 | int |
348 | acpi_wmi_guid_match(device_t self, const char *guid) | | 388 | acpi_wmi_guid_match(device_t self, const char *guid) |
349 | { | | 389 | { |
350 | struct acpi_wmi_softc *sc = device_private(self); | | 390 | struct acpi_wmi_softc *sc = device_private(self); |
351 | ACPI_STATUS rv; | | 391 | ACPI_STATUS rv; |
352 | | | 392 | |
353 | rv = acpi_wmi_guid_get(sc, guid, NULL); | | 393 | rv = acpi_wmi_guid_get(sc, guid, NULL); |
354 | | | 394 | |
355 | if (ACPI_SUCCESS(rv)) | | 395 | if (ACPI_SUCCESS(rv)) |
356 | return 1; | | 396 | return 1; |
357 | | | 397 | |
358 | return 0; | | 398 | return 0; |
359 | } | | 399 | } |
360 | | | 400 | |
361 | /* | | 401 | /* |
362 | * Adds internal event handler. | | 402 | * Adds internal event handler. |
363 | */ | | 403 | */ |
364 | static void | | 404 | static void |
365 | acpi_wmi_event_add(struct acpi_wmi_softc *sc) | | 405 | acpi_wmi_event_add(struct acpi_wmi_softc *sc) |
366 | { | | 406 | { |
367 | struct wmi_t *wmi; | | 407 | struct wmi_t *wmi; |
368 | ACPI_STATUS rv; | | 408 | ACPI_STATUS rv; |
369 | | | 409 | |
370 | if (acpi_register_notify(sc->sc_node, acpi_wmi_event_handler) != true) | | 410 | if (acpi_register_notify(sc->sc_node, acpi_wmi_event_handler) != true) |
371 | return; | | 411 | return; |
372 | | | 412 | |
373 | /* | | 413 | /* |
374 | * Enable possible expensive events. | | 414 | * Enable possible expensive events. |
375 | */ | | 415 | */ |
376 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { | | 416 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { |
377 | | | 417 | |
378 | if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0 && | | 418 | if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0 && |
379 | (wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0) { | | 419 | (wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0) { |
380 | | | 420 | |
381 | rv = acpi_wmi_enable(sc->sc_node->ad_handle, | | 421 | rv = acpi_wmi_enable(sc->sc_node->ad_handle, |
382 | wmi->guid.oid, false, true); | | 422 | wmi->guid.oid, false, true); |
383 | | | 423 | |
384 | if (ACPI_SUCCESS(rv)) { | | 424 | if (ACPI_SUCCESS(rv)) { |
385 | wmi->eevent = true; | | 425 | wmi->eevent = true; |
386 | continue; | | 426 | continue; |
387 | } | | 427 | } |
388 | | | 428 | |
389 | aprint_debug_dev(sc->sc_dev, "failed to enable " | | 429 | aprint_debug_dev(sc->sc_dev, "failed to enable " |
390 | "expensive WExx: %s\n", AcpiFormatException(rv)); | | 430 | "expensive WExx: %s\n", AcpiFormatException(rv)); |
391 | } | | 431 | } |
392 | } | | 432 | } |
393 | } | | 433 | } |
394 | | | 434 | |
395 | /* | | 435 | /* |
396 | * Removes the internal event handler. | | 436 | * Removes the internal event handler. |
397 | */ | | 437 | */ |
398 | static void | | 438 | static void |
399 | acpi_wmi_event_del(struct acpi_wmi_softc *sc) | | 439 | acpi_wmi_event_del(struct acpi_wmi_softc *sc) |
400 | { | | 440 | { |
401 | struct wmi_t *wmi; | | 441 | struct wmi_t *wmi; |
402 | ACPI_STATUS rv; | | 442 | ACPI_STATUS rv; |
403 | | | 443 | |
404 | acpi_deregister_notify(sc->sc_node); | | 444 | acpi_deregister_notify(sc->sc_node); |
405 | | | 445 | |
406 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { | | 446 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { |
407 | | | 447 | |
408 | if (wmi->eevent != true) | | 448 | if (wmi->eevent != true) |
409 | continue; | | 449 | continue; |
410 | | | 450 | |
411 | KASSERT((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0); | | 451 | KASSERT((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) != 0); |
412 | KASSERT((wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0); | | 452 | KASSERT((wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0); |
413 | | | 453 | |
414 | rv = acpi_wmi_enable(sc->sc_node->ad_handle, | | 454 | rv = acpi_wmi_enable(sc->sc_node->ad_handle, |
415 | wmi->guid.oid, false, false); | | 455 | wmi->guid.oid, false, false); |
416 | | | 456 | |
417 | if (ACPI_SUCCESS(rv)) { | | 457 | if (ACPI_SUCCESS(rv)) { |
418 | wmi->eevent = false; | | 458 | wmi->eevent = false; |
419 | continue; | | 459 | continue; |
420 | } | | 460 | } |
421 | | | 461 | |
422 | aprint_debug_dev(sc->sc_dev, "failed to disable " | | 462 | aprint_debug_dev(sc->sc_dev, "failed to disable " |
423 | "expensive WExx: %s\n", AcpiFormatException(rv)); | | 463 | "expensive WExx: %s\n", AcpiFormatException(rv)); |
424 | } | | 464 | } |
425 | } | | 465 | } |
426 | | | 466 | |
427 | /* | | 467 | /* |
428 | * Returns extra information possibly associated with an event. | | 468 | * Returns extra information possibly associated with an event. |
429 | */ | | 469 | */ |
430 | ACPI_STATUS | | 470 | ACPI_STATUS |
431 | acpi_wmi_event_get(device_t self, uint32_t event, ACPI_BUFFER *obuf) | | 471 | acpi_wmi_event_get(device_t self, uint32_t event, ACPI_BUFFER *obuf) |
432 | { | | 472 | { |
433 | struct acpi_wmi_softc *sc = device_private(self); | | 473 | struct acpi_wmi_softc *sc = device_private(self); |
434 | struct wmi_t *wmi; | | 474 | struct wmi_t *wmi; |
435 | ACPI_OBJECT_LIST arg; | | 475 | ACPI_OBJECT_LIST arg; |
436 | ACPI_OBJECT obj; | | 476 | ACPI_OBJECT obj; |
437 | ACPI_HANDLE hdl; | | 477 | ACPI_HANDLE hdl; |
438 | | | 478 | |
439 | if (sc == NULL || obuf == NULL) | | 479 | if (sc == NULL || obuf == NULL) |
440 | return AE_BAD_PARAMETER; | | 480 | return AE_BAD_PARAMETER; |
441 | | | 481 | |
442 | if (sc->sc_handler == NULL) | | 482 | if (sc->sc_handler == NULL) |
443 | return AE_ABORT_METHOD; | | 483 | return AE_ABORT_METHOD; |
444 | | | 484 | |
445 | hdl = sc->sc_node->ad_handle; | | 485 | hdl = sc->sc_node->ad_handle; |
446 | | | 486 | |
447 | obj.Type = ACPI_TYPE_INTEGER; | | 487 | obj.Type = ACPI_TYPE_INTEGER; |
448 | obj.Integer.Value = event; | | 488 | obj.Integer.Value = event; |
449 | | | 489 | |
450 | arg.Count = 0x01; | | 490 | arg.Count = 0x01; |
451 | arg.Pointer = &obj; | | 491 | arg.Pointer = &obj; |
452 | | | 492 | |
453 | obuf->Pointer = NULL; | | 493 | obuf->Pointer = NULL; |
454 | obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; | | 494 | obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; |
455 | | | 495 | |
456 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { | | 496 | SIMPLEQ_FOREACH(wmi, &sc->wmi_head, wmi_link) { |
457 | | | 497 | |
458 | if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) == 0) | | 498 | if ((wmi->guid.flags & ACPI_WMI_FLAG_EVENT) == 0) |
459 | continue; | | 499 | continue; |
460 | | | 500 | |
461 | if (wmi->guid.nid != event) | | 501 | if (wmi->guid.nid != event) |
462 | continue; | | 502 | continue; |
463 | | | 503 | |
464 | return AcpiEvaluateObject(hdl, "_WED", &arg, obuf); | | 504 | return AcpiEvaluateObject(hdl, "_WED", &arg, obuf); |
465 | } | | 505 | } |
466 | | | 506 | |
467 | return AE_NOT_FOUND; | | 507 | return AE_NOT_FOUND; |
468 | } | | 508 | } |
469 | | | 509 | |
470 | /* | | 510 | /* |
471 | * Forwards events to the external handler through the internal one. | | 511 | * Forwards events to the external handler through the internal one. |
472 | */ | | 512 | */ |
473 | static void | | 513 | static void |
474 | acpi_wmi_event_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) | | 514 | acpi_wmi_event_handler(ACPI_HANDLE hdl, uint32_t evt, void *aux) |
475 | { | | 515 | { |
476 | struct acpi_wmi_softc *sc; | | 516 | struct acpi_wmi_softc *sc; |
477 | device_t self = aux; | | 517 | device_t self = aux; |
478 | | | 518 | |
479 | sc = device_private(self); | | 519 | sc = device_private(self); |
480 | | | 520 | |
481 | if (sc->sc_child == NULL) | | 521 | if (sc->sc_child == NULL) |
482 | return; | | 522 | return; |
483 | | | 523 | |
484 | if (sc->sc_handler == NULL) | | 524 | if (sc->sc_handler == NULL) |
485 | return; | | 525 | return; |
486 | | | 526 | |
487 | (*sc->sc_handler)(NULL, evt, sc->sc_child); | | 527 | (*sc->sc_handler)(NULL, evt, sc->sc_child); |
488 | } | | 528 | } |
489 | | | 529 | |
490 | ACPI_STATUS | | 530 | ACPI_STATUS |
491 | acpi_wmi_event_register(device_t self, ACPI_NOTIFY_HANDLER handler) | | 531 | acpi_wmi_event_register(device_t self, ACPI_NOTIFY_HANDLER handler) |
492 | { | | 532 | { |
493 | struct acpi_wmi_softc *sc = device_private(self); | | 533 | struct acpi_wmi_softc *sc = device_private(self); |
494 | | | 534 | |
495 | if (sc == NULL) | | 535 | if (sc == NULL) |
496 | return AE_BAD_PARAMETER; | | 536 | return AE_BAD_PARAMETER; |
497 | | | 537 | |
498 | if (handler != NULL && sc->sc_handler != NULL) | | 538 | if (handler != NULL && sc->sc_handler != NULL) |
499 | return AE_ALREADY_EXISTS; | | 539 | return AE_ALREADY_EXISTS; |
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 | } |
526 | | | 598 | |
527 | static bool | | 599 | static bool |
528 | acpi_wmi_resume(device_t self, const pmf_qual_t *qual) | | 600 | acpi_wmi_resume(device_t self, const pmf_qual_t *qual) |
529 | { | | 601 | { |
530 | struct acpi_wmi_softc *sc = device_private(self); | | 602 | struct acpi_wmi_softc *sc = device_private(self); |
531 | | | 603 | |
532 | acpi_wmi_event_add(sc); | | 604 | acpi_wmi_event_add(sc); |
533 | | | 605 | |
534 | return true; | | 606 | return true; |
535 | } | | 607 | } |
536 | | | 608 | |
537 | /* | | 609 | /* |
538 | * Enables or disables data collection (WCxx) or an event (WExx). | | 610 | * Enables or disables data collection (WCxx) or an event (WExx). |
539 | */ | | 611 | */ |
540 | static ACPI_STATUS | | 612 | static ACPI_STATUS |
541 | acpi_wmi_enable(ACPI_HANDLE hdl, const char *oid, bool data, bool flag) | | 613 | acpi_wmi_enable(ACPI_HANDLE hdl, const char *oid, bool data, bool flag) |
542 | { | | 614 | { |
543 | char path[5]; | | 615 | char path[5]; |
544 | const char *str; | | 616 | const char *str; |
545 | | | 617 | |
546 | str = (data != false) ? "WC" : "WE"; | | 618 | str = (data != false) ? "WC" : "WE"; |
547 | | | 619 | |
548 | (void)strlcpy(path, str, sizeof(path)); | | 620 | (void)strlcpy(path, str, sizeof(path)); |
549 | (void)strlcat(path, oid, sizeof(path)); | | 621 | (void)strlcat(path, oid, sizeof(path)); |
550 | | | 622 | |
551 | return acpi_eval_set_integer(hdl, path, (flag != false) ? 0x01 : 0x00); | | 623 | return acpi_eval_set_integer(hdl, path, (flag != false) ? 0x01 : 0x00); |
552 | } | | 624 | } |
553 | | | 625 | |
554 | static bool | | 626 | static bool |
555 | acpi_wmi_input(struct wmi_t *wmi, uint8_t flag, uint8_t idx) | | 627 | acpi_wmi_input(struct wmi_t *wmi, uint8_t flag, uint8_t idx) |
556 | { | | 628 | { |
557 | | | 629 | |
558 | if ((wmi->guid.flags & flag) == 0) | | 630 | if ((wmi->guid.flags & flag) == 0) |
559 | return false; | | 631 | return false; |
560 | | | 632 | |
561 | if (wmi->guid.count == 0x00) | | 633 | if (wmi->guid.count == 0x00) |
562 | return false; | | 634 | return false; |
563 | | | 635 | |
564 | if (wmi->guid.count < idx) | | 636 | if (wmi->guid.count < idx) |
565 | return false; | | 637 | return false; |
566 | | | 638 | |
567 | return true; | | 639 | return true; |
568 | } | | 640 | } |
569 | | | 641 | |
570 | /* | | 642 | /* |
571 | * Makes a WMI data block query (WQxx). The corresponding control | | 643 | * Makes a WMI data block query (WQxx). The corresponding control |
572 | * method for data collection will be invoked if it is available. | | 644 | * method for data collection will be invoked if it is available. |
573 | */ | | 645 | */ |
574 | ACPI_STATUS | | 646 | ACPI_STATUS |
575 | acpi_wmi_data_query(device_t self, const char *guid, | | 647 | acpi_wmi_data_query(device_t self, const char *guid, |
576 | uint8_t idx, ACPI_BUFFER *obuf) | | 648 | uint8_t idx, ACPI_BUFFER *obuf) |
577 | { | | 649 | { |
578 | struct acpi_wmi_softc *sc = device_private(self); | | 650 | struct acpi_wmi_softc *sc = device_private(self); |
579 | struct wmi_t *wmi; | | 651 | struct wmi_t *wmi; |
580 | char path[5] = "WQ"; | | 652 | char path[5] = "WQ"; |
581 | ACPI_OBJECT_LIST arg; | | 653 | ACPI_OBJECT_LIST arg; |
582 | ACPI_STATUS rv, rvxx; | | 654 | ACPI_STATUS rv, rvxx; |
583 | ACPI_OBJECT obj; | | 655 | ACPI_OBJECT obj; |
584 | | | 656 | |
585 | rvxx = AE_SUPPORT; | | 657 | rvxx = AE_SUPPORT; |
586 | | | 658 | |
587 | if (obuf == NULL) | | 659 | if (obuf == NULL) |
588 | return AE_BAD_PARAMETER; | | 660 | return AE_BAD_PARAMETER; |
589 | | | 661 | |
590 | rv = acpi_wmi_guid_get(sc, guid, &wmi); | | 662 | rv = acpi_wmi_guid_get(sc, guid, &wmi); |
591 | | | 663 | |
592 | if (ACPI_FAILURE(rv)) | | 664 | if (ACPI_FAILURE(rv)) |
593 | return rv; | | 665 | return rv; |
594 | | | 666 | |
595 | if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true) | | 667 | if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true) |
596 | return AE_BAD_DATA; | | 668 | return AE_BAD_DATA; |
597 | | | 669 | |
598 | (void)strlcat(path, wmi->guid.oid, sizeof(path)); | | 670 | (void)strlcat(path, wmi->guid.oid, sizeof(path)); |
599 | | | 671 | |
600 | obj.Type = ACPI_TYPE_INTEGER; | | 672 | obj.Type = ACPI_TYPE_INTEGER; |
601 | obj.Integer.Value = idx; | | 673 | obj.Integer.Value = idx; |
602 | | | 674 | |
603 | arg.Count = 0x01; | | 675 | arg.Count = 0x01; |
604 | arg.Pointer = &obj; | | 676 | arg.Pointer = &obj; |
605 | | | 677 | |
606 | obuf->Pointer = NULL; | | 678 | obuf->Pointer = NULL; |
607 | obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; | | 679 | obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; |
608 | | | 680 | |
609 | /* | | 681 | /* |
610 | * If the expensive flag is set, we should enable | | 682 | * If the expensive flag is set, we should enable |
611 | * data collection before evaluating the WQxx buffer. | | 683 | * data collection before evaluating the WQxx buffer. |
612 | */ | | 684 | */ |
613 | if ((wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0) { | | 685 | if ((wmi->guid.flags & ACPI_WMI_FLAG_EXPENSIVE) != 0) { |
614 | | | 686 | |
615 | rvxx = acpi_wmi_enable(sc->sc_node->ad_handle, | | 687 | rvxx = acpi_wmi_enable(sc->sc_node->ad_handle, |
616 | wmi->guid.oid, true, true); | | 688 | wmi->guid.oid, true, true); |
617 | } | | 689 | } |
618 | | | 690 | |
619 | rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf); | | 691 | rv = AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf); |
620 | | | 692 | |
621 | /* No longer needed. */ | | 693 | /* No longer needed. */ |
622 | if (ACPI_SUCCESS(rvxx)) { | | 694 | if (ACPI_SUCCESS(rvxx)) { |
623 | | | 695 | |
624 | (void)acpi_wmi_enable(sc->sc_node->ad_handle, | | 696 | (void)acpi_wmi_enable(sc->sc_node->ad_handle, |
625 | wmi->guid.oid, true, false); | | 697 | wmi->guid.oid, true, false); |
626 | } | | 698 | } |
627 | | | 699 | |
628 | #ifdef DIAGNOSTIC | | 700 | #ifdef DIAGNOSTIC |
629 | /* | | 701 | /* |
630 | * XXX: It appears that quite a few laptops have WQxx | | 702 | * XXX: It appears that quite a few laptops have WQxx |
631 | * methods that are declared as expensive, but lack the | | 703 | * methods that are declared as expensive, but lack the |
632 | * corresponding WCxx control method. | | 704 | * corresponding WCxx control method. |
633 | * | | 705 | * |
634 | * -- Acer Aspire One is one example <jruohonen@iki.fi>. | | 706 | * -- Acer Aspire One is one example <jruohonen@iki.fi>. |
635 | */ | | 707 | */ |
636 | if (ACPI_FAILURE(rvxx) && rvxx != AE_SUPPORT) | | 708 | if (ACPI_FAILURE(rvxx) && rvxx != AE_SUPPORT) |
637 | aprint_error_dev(sc->sc_dev, "failed to evaluate WCxx " | | 709 | aprint_error_dev(sc->sc_dev, "failed to evaluate WCxx " |
638 | "for %s: %s\n", path, AcpiFormatException(rvxx)); | | 710 | "for %s: %s\n", path, AcpiFormatException(rvxx)); |
639 | #endif | | 711 | #endif |
640 | return rv; | | 712 | return rv; |
641 | } | | 713 | } |
642 | | | 714 | |
643 | /* | | 715 | /* |
644 | * Writes to a data block (WSxx). | | 716 | * Writes to a data block (WSxx). |
645 | */ | | 717 | */ |
646 | ACPI_STATUS | | 718 | ACPI_STATUS |
647 | acpi_wmi_data_write(device_t self, const char *guid, | | 719 | acpi_wmi_data_write(device_t self, const char *guid, |
648 | uint8_t idx, ACPI_BUFFER *ibuf) | | 720 | uint8_t idx, ACPI_BUFFER *ibuf) |
649 | { | | 721 | { |
650 | struct acpi_wmi_softc *sc = device_private(self); | | 722 | struct acpi_wmi_softc *sc = device_private(self); |
651 | struct wmi_t *wmi; | | 723 | struct wmi_t *wmi; |
652 | ACPI_OBJECT_LIST arg; | | 724 | ACPI_OBJECT_LIST arg; |
653 | ACPI_OBJECT obj[2]; | | 725 | ACPI_OBJECT obj[2]; |
654 | char path[5] = "WS"; | | 726 | char path[5] = "WS"; |
655 | ACPI_STATUS rv; | | 727 | ACPI_STATUS rv; |
656 | | | 728 | |
657 | if (ibuf == NULL) | | 729 | if (ibuf == NULL) |
658 | return AE_BAD_PARAMETER; | | 730 | return AE_BAD_PARAMETER; |
659 | | | 731 | |
660 | rv = acpi_wmi_guid_get(sc, guid, &wmi); | | 732 | rv = acpi_wmi_guid_get(sc, guid, &wmi); |
661 | | | 733 | |
662 | if (ACPI_FAILURE(rv)) | | 734 | if (ACPI_FAILURE(rv)) |
663 | return rv; | | 735 | return rv; |
664 | | | 736 | |
665 | if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true) | | 737 | if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_DATA, idx) != true) |
666 | return AE_BAD_DATA; | | 738 | return AE_BAD_DATA; |
667 | | | 739 | |
668 | (void)strlcat(path, wmi->guid.oid, sizeof(path)); | | 740 | (void)strlcat(path, wmi->guid.oid, sizeof(path)); |
669 | | | 741 | |
670 | obj[0].Integer.Value = idx; | | 742 | obj[0].Integer.Value = idx; |
671 | obj[0].Type = ACPI_TYPE_INTEGER; | | 743 | obj[0].Type = ACPI_TYPE_INTEGER; |
672 | | | 744 | |
673 | obj[1].Buffer.Length = ibuf->Length; | | 745 | obj[1].Buffer.Length = ibuf->Length; |
674 | obj[1].Buffer.Pointer = ibuf->Pointer; | | 746 | obj[1].Buffer.Pointer = ibuf->Pointer; |
675 | | | 747 | |
676 | obj[1].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ? | | 748 | obj[1].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ? |
677 | ACPI_TYPE_STRING : ACPI_TYPE_BUFFER; | | 749 | ACPI_TYPE_STRING : ACPI_TYPE_BUFFER; |
678 | | | 750 | |
679 | arg.Count = 0x02; | | 751 | arg.Count = 0x02; |
680 | arg.Pointer = obj; | | 752 | arg.Pointer = obj; |
681 | | | 753 | |
682 | return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, NULL); | | 754 | return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, NULL); |
683 | } | | 755 | } |
684 | | | 756 | |
685 | /* | | 757 | /* |
686 | * Executes a method (WMxx). | | 758 | * Executes a method (WMxx). |
687 | */ | | 759 | */ |
688 | ACPI_STATUS | | 760 | ACPI_STATUS |
689 | acpi_wmi_method(device_t self, const char *guid, uint8_t idx, | | 761 | acpi_wmi_method(device_t self, const char *guid, uint8_t idx, |
690 | uint32_t mid, ACPI_BUFFER *ibuf, ACPI_BUFFER *obuf) | | 762 | uint32_t mid, ACPI_BUFFER *ibuf, ACPI_BUFFER *obuf) |
691 | { | | 763 | { |
692 | struct acpi_wmi_softc *sc = device_private(self); | | 764 | struct acpi_wmi_softc *sc = device_private(self); |
693 | struct wmi_t *wmi; | | 765 | struct wmi_t *wmi; |
694 | ACPI_OBJECT_LIST arg; | | 766 | ACPI_OBJECT_LIST arg; |
695 | ACPI_OBJECT obj[3]; | | 767 | ACPI_OBJECT obj[3]; |
696 | char path[5] = "WM"; | | 768 | char path[5] = "WM"; |
697 | ACPI_STATUS rv; | | 769 | ACPI_STATUS rv; |
698 | | | 770 | |
699 | if (ibuf == NULL || obuf == NULL) | | 771 | if (ibuf == NULL || obuf == NULL) |
700 | return AE_BAD_PARAMETER; | | 772 | return AE_BAD_PARAMETER; |
701 | | | 773 | |
702 | rv = acpi_wmi_guid_get(sc, guid, &wmi); | | 774 | rv = acpi_wmi_guid_get(sc, guid, &wmi); |
703 | | | 775 | |
704 | if (ACPI_FAILURE(rv)) | | 776 | if (ACPI_FAILURE(rv)) |
705 | return rv; | | 777 | return rv; |
706 | | | 778 | |
707 | if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_METHOD, idx) != true) | | 779 | if (acpi_wmi_input(wmi, ACPI_WMI_FLAG_METHOD, idx) != true) |
708 | return AE_BAD_DATA; | | 780 | return AE_BAD_DATA; |
709 | | | 781 | |
710 | (void)strlcat(path, wmi->guid.oid, sizeof(path)); | | 782 | (void)strlcat(path, wmi->guid.oid, sizeof(path)); |
711 | | | 783 | |
712 | obj[0].Integer.Value = idx; | | 784 | obj[0].Integer.Value = idx; |
713 | obj[1].Integer.Value = mid; | | 785 | obj[1].Integer.Value = mid; |
714 | obj[0].Type = obj[1].Type = ACPI_TYPE_INTEGER; | | 786 | obj[0].Type = obj[1].Type = ACPI_TYPE_INTEGER; |
715 | | | 787 | |
716 | obj[2].Buffer.Length = ibuf->Length; | | 788 | obj[2].Buffer.Length = ibuf->Length; |
717 | obj[2].Buffer.Pointer = ibuf->Pointer; | | 789 | obj[2].Buffer.Pointer = ibuf->Pointer; |
718 | | | 790 | |
719 | obj[2].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ? | | 791 | obj[2].Type = ((wmi->guid.flags & ACPI_WMI_FLAG_STRING) != 0) ? |
720 | ACPI_TYPE_STRING : ACPI_TYPE_BUFFER; | | 792 | ACPI_TYPE_STRING : ACPI_TYPE_BUFFER; |
721 | | | 793 | |
722 | arg.Count = 0x03; | | 794 | arg.Count = 0x03; |
723 | arg.Pointer = obj; | | 795 | arg.Pointer = obj; |
724 | | | 796 | |
725 | obuf->Pointer = NULL; | | 797 | obuf->Pointer = NULL; |
726 | obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; | | 798 | obuf->Length = ACPI_ALLOCATE_LOCAL_BUFFER; |
727 | | | 799 | |
728 | return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf); | | 800 | return AcpiEvaluateObject(sc->sc_node->ad_handle, path, &arg, obuf); |
729 | } | | 801 | } |
730 | | | 802 | |
731 | #ifdef _MODULE | | 803 | #ifdef _MODULE |
732 | | | 804 | |
733 | MODULE(MODULE_CLASS_DRIVER, acpiwmi, NULL); | | 805 | MODULE(MODULE_CLASS_DRIVER, acpiwmi, NULL); |
734 | CFDRIVER_DECL(acpiwmi, DV_DULL, NULL); | | 806 | CFDRIVER_DECL(acpiwmi, DV_DULL, NULL); |
735 | | | 807 | |
736 | static int acpiwmiloc[] = { -1 }; | | 808 | static int acpiwmiloc[] = { -1 }; |
737 | extern struct cfattach acpiwmi_ca; | | 809 | extern struct cfattach acpiwmi_ca; |
738 | | | 810 | |
739 | static struct cfparent acpiparent = { | | 811 | static struct cfparent acpiparent = { |
740 | "acpinodebus", NULL, DVUNIT_ANY | | 812 | "acpinodebus", NULL, DVUNIT_ANY |
741 | }; | | 813 | }; |
742 | | | 814 | |
743 | static struct cfdata acpiwmi_cfdata[] = { | | 815 | static struct cfdata acpiwmi_cfdata[] = { |
744 | { | | 816 | { |
745 | .cf_name = "acpiwmi", | | 817 | .cf_name = "acpiwmi", |
746 | .cf_atname = "acpiwmi", | | 818 | .cf_atname = "acpiwmi", |
747 | .cf_unit = 0, | | 819 | .cf_unit = 0, |
748 | .cf_fstate = FSTATE_STAR, | | 820 | .cf_fstate = FSTATE_STAR, |
749 | .cf_loc = acpiwmiloc, | | 821 | .cf_loc = acpiwmiloc, |
750 | .cf_flags = 0, | | 822 | .cf_flags = 0, |
751 | .cf_pspec = &acpiparent, | | 823 | .cf_pspec = &acpiparent, |
752 | }, | | 824 | }, |
753 | | | 825 | |
754 | { NULL, NULL, 0, 0, NULL, 0, NULL } | | 826 | { NULL, NULL, 0, 0, NULL, 0, NULL } |
755 | }; | | 827 | }; |
756 | | | 828 | |
757 | static int | | 829 | static int |
758 | acpiwmi_modcmd(modcmd_t cmd, void *opaque) | | 830 | acpiwmi_modcmd(modcmd_t cmd, void *opaque) |
759 | { | | 831 | { |
760 | int err; | | 832 | int err; |
761 | | | 833 | |
762 | switch (cmd) { | | 834 | switch (cmd) { |
763 | | | 835 | |
764 | case MODULE_CMD_INIT: | | 836 | case MODULE_CMD_INIT: |
765 | | | 837 | |
766 | err = config_cfdriver_attach(&acpiwmi_cd); | | 838 | err = config_cfdriver_attach(&acpiwmi_cd); |
767 | | | 839 | |
768 | if (err != 0) | | 840 | if (err != 0) |
769 | return err; | | 841 | return err; |
770 | | | 842 | |
771 | err = config_cfattach_attach("acpiwmi", &acpiwmi_ca); | | 843 | err = config_cfattach_attach("acpiwmi", &acpiwmi_ca); |
772 | | | 844 | |
773 | if (err != 0) { | | 845 | if (err != 0) { |
774 | config_cfdriver_detach(&acpiwmi_cd); | | 846 | config_cfdriver_detach(&acpiwmi_cd); |
775 | return err; | | 847 | return err; |
776 | } | | 848 | } |
777 | | | 849 | |
778 | err = config_cfdata_attach(acpiwmi_cfdata, 1); | | 850 | err = config_cfdata_attach(acpiwmi_cfdata, 1); |
779 | | | 851 | |
780 | if (err != 0) { | | 852 | if (err != 0) { |
781 | config_cfattach_detach("acpiwmi", &acpiwmi_ca); | | 853 | config_cfattach_detach("acpiwmi", &acpiwmi_ca); |
782 | config_cfdriver_detach(&acpiwmi_cd); | | 854 | config_cfdriver_detach(&acpiwmi_cd); |
783 | return err; | | 855 | return err; |
784 | } | | 856 | } |
785 | | | 857 | |
786 | return 0; | | 858 | return 0; |
787 | | | 859 | |
788 | case MODULE_CMD_FINI: | | 860 | case MODULE_CMD_FINI: |
789 | | | 861 | |
790 | err = config_cfdata_detach(acpiwmi_cfdata); | | 862 | err = config_cfdata_detach(acpiwmi_cfdata); |
791 | | | 863 | |
792 | if (err != 0) | | 864 | if (err != 0) |
793 | return err; | | 865 | return err; |
794 | | | 866 | |
795 | config_cfattach_detach("acpiwmi", &acpiwmi_ca); | | 867 | config_cfattach_detach("acpiwmi", &acpiwmi_ca); |
796 | config_cfdriver_detach(&acpiwmi_cd); | | 868 | config_cfdriver_detach(&acpiwmi_cd); |
797 | | | 869 | |
798 | return 0; | | 870 | return 0; |
799 | | | 871 | |
800 | default: | | 872 | default: |
801 | return ENOTTY; | | 873 | return ENOTTY; |
802 | } | | 874 | } |
803 | } | | 875 | } |
804 | | | 876 | |
805 | #endif /* _MODULE */ | | 877 | #endif /* _MODULE */ |