1 /* $NetBSD: atk0110.c,v 1.4 2010/02/11 06:54:57 cnst Exp $ */
2 /* $OpenBSD: atk0110.c,v 1.1 2009/07/23 01:38:16 cnst Exp $ */
3
4 /*
5 * Copyright (c) 2009, 2010 Constantine A. Murenin <cnst++@FreeBSD.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <sys/cdefs.h>
21 __FBSDID("$FreeBSD: releng/8.4/sys/dev/acpi_support/atk0110.c 210476 2010-07-25 17:48:16Z rpaulo $");
22
23 #include <machine/_inttypes.h>
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/kernel.h>
27 #include <sys/bus.h>
28 #include <sys/module.h>
29 #include <sys/malloc.h>
30 #include <sys/sysctl.h>
31
32 #include <contrib/dev/acpica/include/acpi.h>
33 #include <dev/acpica/acpivar.h>
34
35 /*
36 * ASUSTeK AI Booster (ACPI ASOC ATK0110).
37 *
38 * This code was originally written for OpenBSD after the techniques
39 * described in the Linux's asus_atk0110.c and FreeBSD's Takanori Watanabe's
40 * acpi_aiboost.c were verified to be accurate on the actual hardware kindly
41 * provided by Sam Fourman Jr. It was subsequently ported from OpenBSD to
42 * DragonFly BSD, to NetBSD's sysmon_envsys(9) and to FreeBSD's sysctl(9).
43 *
44 * -- Constantine A. Murenin <http://cnst.su/>
45 */
46
47 #define _COMPONENT ACPI_OEM
48 ACPI_MODULE_NAME("aibs");
49 ACPI_SERIAL_DECL(aibs, "aibs");
50
51 #define AIBS_MORE_SENSORS
52 #define AIBS_VERBOSE
53
54 enum aibs_type {
55 AIBS_VOLT,
56 AIBS_TEMP,
57 AIBS_FAN
58 };
59
60 struct aibs_sensor {
61 ACPI_INTEGER v;
62 ACPI_INTEGER i;
63 ACPI_INTEGER l;
64 ACPI_INTEGER h;
65 enum aibs_type t;
66 };
67
68 struct aibs_softc {
69 struct device *sc_dev;
70 ACPI_HANDLE sc_ah;
71
72 struct aibs_sensor *sc_asens_volt;
73 struct aibs_sensor *sc_asens_temp;
74 struct aibs_sensor *sc_asens_fan;
75 };
76
77 static int aibs_probe(device_t);
78 static int aibs_attach(device_t);
79 static int aibs_detach(device_t);
80 static int aibs_sysctl(SYSCTL_HANDLER_ARGS);
81
82 static void aibs_attach_sif(struct aibs_softc *, enum aibs_type);
83
84 static device_method_t aibs_methods[] = {
85 DEVMETHOD(device_probe, aibs_probe),
86 DEVMETHOD(device_attach, aibs_attach),
87 DEVMETHOD(device_detach, aibs_detach),
88 { NULL, NULL }
89 };
90
91 static driver_t aibs_driver = {
92 "aibs",
93 aibs_methods,
94 sizeof(struct aibs_softc)
95 };
96
97 static devclass_t aibs_devclass;
98
99 DRIVER_MODULE(aibs, acpi, aibs_driver, aibs_devclass, NULL, NULL);
100
101
102 static char* aibs_hids[] = {
103 "ATK0110",
104 NULL
105 };
106
107 static int
108 aibs_probe(device_t dev)
109 {
110 if (acpi_disabled("aibs") ||
111 ACPI_ID_PROBE(device_get_parent(dev), dev, aibs_hids) == NULL)
112 return ENXIO;
113
114 device_set_desc(dev, "ASUSTeK AI Booster (ACPI ASOC ATK0110)");
115 return 0;
116 }
117
118 static int
119 aibs_attach(device_t dev)
120 {
121 struct aibs_softc *sc = device_get_softc(dev);
122
123 sc->sc_dev = dev;
124 sc->sc_ah = acpi_get_handle(dev);
125
126 aibs_attach_sif(sc, AIBS_VOLT);
127 aibs_attach_sif(sc, AIBS_TEMP);
128 aibs_attach_sif(sc, AIBS_FAN);
129
130 return 0;
131 }
132
133 static void
134 aibs_attach_sif(struct aibs_softc *sc, enum aibs_type st)
135 {
136 ACPI_STATUS s;
137 ACPI_BUFFER b;
138 ACPI_OBJECT *bp, *o;
139 int i, n;
140 const char *node;
141 char name[] = "?SIF";
142 struct aibs_sensor *as;
143 struct sysctl_oid *so;
144
145 switch (st) {
146 case AIBS_VOLT:
147 node = "volt";
148 name[0] = 'V';
149 break;
150 case AIBS_TEMP:
151 node = "temp";
152 name[0] = 'T';
153 break;
154 case AIBS_FAN:
155 node = "fan";
156 name[0] = 'F';
157 break;
158 default:
159 return;
160 }
161
162 b.Length = ACPI_ALLOCATE_BUFFER;
163 s = AcpiEvaluateObjectTyped(sc->sc_ah, name, NULL, &b,
164 ACPI_TYPE_PACKAGE);
165 if (ACPI_FAILURE(s)) {
166 device_printf(sc->sc_dev, "%s not found\n", name);
167 return;
168 }
169
170 bp = b.Pointer;
171 o = bp->Package.Elements;
172 if (o[0].Type != ACPI_TYPE_INTEGER) {
173 device_printf(sc->sc_dev, "%s[0]: invalid type\n", name);
174 AcpiOsFree(b.Pointer);
175 return;
176 }
177
178 n = o[0].Integer.Value;
179 if (bp->Package.Count - 1 < n) {
180 device_printf(sc->sc_dev, "%s: invalid package\n", name);
181 AcpiOsFree(b.Pointer);
182 return;
183 } else if (bp->Package.Count - 1 > n) {
184 int on = n;
185
186 #ifdef AIBS_MORE_SENSORS
187 n = bp->Package.Count - 1;
188 #endif
189 device_printf(sc->sc_dev, "%s: malformed package: %i/%i"
190 ", assume %i\n", name, on, bp->Package.Count - 1, n);
191 }
192 if (n < 1) {
193 device_printf(sc->sc_dev, "%s: no members in the package\n",
194 name);
195 AcpiOsFree(b.Pointer);
196 return;
197 }
198
199 as = malloc(sizeof(*as) * n, M_DEVBUF, M_NOWAIT | M_ZERO);
200 if (as == NULL) {
201 device_printf(sc->sc_dev, "%s: malloc fail\n", name);
202 AcpiOsFree(b.Pointer);
203 return;
204 }
205 switch (st) {
206 case AIBS_VOLT:
207 sc->sc_asens_volt = as;
208 break;
209 case AIBS_TEMP:
210 sc->sc_asens_temp = as;
211 break;
212 case AIBS_FAN:
213 sc->sc_asens_fan = as;
214 break;
215 }
216
217 /* sysctl subtree for sensors of this type */
218 so = SYSCTL_ADD_NODE(device_get_sysctl_ctx(sc->sc_dev),
219 SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)), st,
220 node, CTLFLAG_RD, NULL, NULL);
221
222 for (i = 0, o++; i < n; i++, o++) {
223 ACPI_OBJECT *oi;
224 char si[3];
225 const char *desc;
226
227 /* acpica5 automatically evaluates the referenced package */
228 if (o[0].Type != ACPI_TYPE_PACKAGE) {
229 device_printf(sc->sc_dev,
230 "%s: %i: not a package: %i type\n",
231 name, i, o[0].Type);
232 continue;
233 }
234 oi = o[0].Package.Elements;
235 if (o[0].Package.Count != 5 ||
236 oi[0].Type != ACPI_TYPE_INTEGER ||
237 oi[1].Type != ACPI_TYPE_STRING ||
238 oi[2].Type != ACPI_TYPE_INTEGER ||
239 oi[3].Type != ACPI_TYPE_INTEGER ||
240 oi[4].Type != ACPI_TYPE_INTEGER) {
241 device_printf(sc->sc_dev,
242 "%s: %i: invalid package\n",
243 name, i);
244 continue;
245 }
246 as[i].i = oi[0].Integer.Value;
247 desc = oi[1].String.Pointer;
248 as[i].l = oi[2].Integer.Value;
249 as[i].h = oi[3].Integer.Value;
250 as[i].t = st;
251 #ifdef AIBS_VERBOSE
252 device_printf(sc->sc_dev, "%c%i: "
253 "0x%08"PRIx64" %20s %5"PRIi64" / %5"PRIi64" "
254 "0x%"PRIx64"\n",
255 name[0], i,
256 as[i].i, desc, (int64_t)as[i].l, (int64_t)as[i].h,
257 oi[4].Integer.Value);
258 #endif
259 snprintf(si, sizeof(si), "%i", i);
260 SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->sc_dev),
261 SYSCTL_CHILDREN(so), i, si, CTLTYPE_OPAQUE | CTLFLAG_RD,
262 sc, st, aibs_sysctl, st == AIBS_TEMP ? "IK" : "I", desc);
263 }
264
265 AcpiOsFree(b.Pointer);
266 }
267
268 static int
269 aibs_detach(device_t dev)
270 {
271 struct aibs_softc *sc = device_get_softc(dev);
272
273 if (sc->sc_asens_volt != NULL)
274 free(sc->sc_asens_volt, M_DEVBUF);
275 if (sc->sc_asens_temp != NULL)
276 free(sc->sc_asens_temp, M_DEVBUF);
277 if (sc->sc_asens_fan != NULL)
278 free(sc->sc_asens_fan, M_DEVBUF);
279 return 0;
280 }
281
282 #ifdef AIBS_VERBOSE
283 #define ddevice_printf(x...) device_printf(x)
284 #else
285 #define ddevice_printf(x...)
286 #endif
287
288 static int
289 aibs_sysctl(SYSCTL_HANDLER_ARGS)
290 {
291 struct aibs_softc *sc = arg1;
292 enum aibs_type st = arg2;
293 int i = oidp->oid_number;
294 ACPI_STATUS rs;
295 ACPI_OBJECT p, *bp;
296 ACPI_OBJECT_LIST mp;
297 ACPI_BUFFER b;
298 char *name;
299 struct aibs_sensor *as;
300 ACPI_INTEGER v, l, h;
301 int so[3];
302
303 switch (st) {
304 case AIBS_VOLT:
305 name = "RVLT";
306 as = sc->sc_asens_volt;
307 break;
308 case AIBS_TEMP:
309 name = "RTMP";
310 as = sc->sc_asens_temp;
311 break;
312 case AIBS_FAN:
313 name = "RFAN";
314 as = sc->sc_asens_fan;
315 break;
316 default:
317 return ENOENT;
318 }
319 if (as == NULL)
320 return ENOENT;
321 l = as[i].l;
322 h = as[i].h;
323 p.Type = ACPI_TYPE_INTEGER;
324 p.Integer.Value = as[i].i;
325 mp.Count = 1;
326 mp.Pointer = &p;
327 b.Length = ACPI_ALLOCATE_BUFFER;
328 ACPI_SERIAL_BEGIN(aibs);
329 rs = AcpiEvaluateObjectTyped(sc->sc_ah, name, &mp, &b,
330 ACPI_TYPE_INTEGER);
331 if (ACPI_FAILURE(rs)) {
332 ddevice_printf(sc->sc_dev,
333 "%s: %i: evaluation failed\n",
334 name, i);
335 ACPI_SERIAL_END(aibs);
336 return EIO;
337 }
338 bp = b.Pointer;
339 v = bp->Integer.Value;
340 AcpiOsFree(b.Pointer);
341 ACPI_SERIAL_END(aibs);
342
343 switch (st) {
344 case AIBS_VOLT:
345 break;
346 case AIBS_TEMP:
347 v += 2732;
348 l += 2732;
349 h += 2732;
350 break;
351 case AIBS_FAN:
352 break;
353 }
354 so[0] = v;
355 so[1] = l;
356 so[2] = h;
357 return sysctl_handle_opaque(oidp, &so, sizeof(so), req);
358 }
Cache object: a52478468727ab8bbce3513e429aad68
|