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$"); 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 device_t 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 MODULE_DEPEND(aibs, acpi, 1, 1, 1); 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 (uint64_t)as[i].i, desc, (int64_t)as[i].l, 257 (int64_t)as[i].h, (uint64_t)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_INT | 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 } 359