1 /* $OpenBSD: acpibtn.c,v 1.54 2024/09/21 19:06:06 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/signalvar.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/malloc.h> 23 24 #include <machine/bus.h> 25 #include <machine/apmvar.h> 26 27 #include <dev/acpi/acpireg.h> 28 #include <dev/acpi/acpivar.h> 29 #include <dev/acpi/acpidev.h> 30 #include <dev/acpi/amltypes.h> 31 #include <dev/acpi/dsdt.h> 32 33 #include <sys/sensors.h> 34 35 int acpibtn_match(struct device *, void *, void *); 36 void acpibtn_attach(struct device *, struct device *, void *); 37 int acpibtn_notify(struct aml_node *, int, void *); 38 int acpibtn_activate(struct device *, int); 39 40 struct acpibtn_softc { 41 struct device sc_dev; 42 43 bus_space_tag_t sc_iot; 44 bus_space_handle_t sc_ioh; 45 46 struct acpi_softc *sc_acpi; 47 struct aml_node *sc_devnode; 48 49 struct ksensor sc_sens; 50 struct ksensordev sc_sensdev; 51 52 int sc_btn_type; 53 #define ACPIBTN_UNKNOWN 0 54 #define ACPIBTN_LID 1 55 #define ACPIBTN_POWER 2 56 #define ACPIBTN_SLEEP 3 57 }; 58 59 int acpibtn_setpsw(struct acpibtn_softc *, int); 60 61 struct acpi_lid { 62 struct acpibtn_softc *abl_softc; 63 SLIST_ENTRY(acpi_lid) abl_link; 64 }; 65 SLIST_HEAD(acpi_lid_head, acpi_lid) acpibtn_lids = 66 SLIST_HEAD_INITIALIZER(acpibtn_lids); 67 68 const struct cfattach acpibtn_ca = { 69 sizeof(struct acpibtn_softc), acpibtn_match, acpibtn_attach, NULL, 70 acpibtn_activate 71 }; 72 73 struct cfdriver acpibtn_cd = { 74 NULL, "acpibtn", DV_DULL 75 }; 76 77 const char *acpibtn_hids[] = { 78 ACPI_DEV_LD, 79 ACPI_DEV_PBD, 80 ACPI_DEV_SBD, 81 NULL 82 }; 83 84 /* 85 * acpibtn_numopenlids 86 * 87 * Return the number of _LID devices that are in the "open" state. 88 * Used to determine if we should go back to sleep/hibernate if we 89 * woke up with the all the lids still closed for some reason. If 90 * the machine has no lids, returns -1. 91 */ 92 int 93 acpibtn_numopenlids(void) 94 { 95 struct acpi_lid *lid; 96 int64_t val; 97 int ct = 0; 98 99 /* If we have no lids ... */ 100 if (SLIST_EMPTY(&acpibtn_lids)) 101 return (-1); 102 103 /* 104 * Determine how many lids are open. Assumes _LID evals to 105 * non-0 or 0, for on / off (which is what the spec says). 106 */ 107 SLIST_FOREACH(lid, &acpibtn_lids, abl_link) 108 if (!aml_evalinteger(lid->abl_softc->sc_acpi, 109 lid->abl_softc->sc_devnode, "_LID", 0, NULL, &val) && 110 val != 0) 111 ct++; 112 return (ct); 113 } 114 115 int 116 acpibtn_setpsw(struct acpibtn_softc *sc, int psw) 117 { 118 struct aml_value val; 119 120 bzero(&val, sizeof val); 121 val.type = AML_OBJTYPE_INTEGER; 122 val.v_integer = psw; 123 val.length = 1; 124 125 return (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSW", 1, &val, 126 NULL)); 127 } 128 129 void 130 acpibtn_disable_psw(void) 131 { 132 struct acpi_lid *lid; 133 134 /* disable _LID for wakeup */ 135 SLIST_FOREACH(lid, &acpibtn_lids, abl_link) 136 acpibtn_setpsw(lid->abl_softc, 0); 137 } 138 139 void 140 acpibtn_enable_psw(void) 141 { 142 struct acpi_lid *lid; 143 144 /* enable _LID for wakeup */ 145 SLIST_FOREACH(lid, &acpibtn_lids, abl_link) 146 acpibtn_setpsw(lid->abl_softc, 1); 147 } 148 149 int 150 acpibtn_match(struct device *parent, void *match, void *aux) 151 { 152 struct acpi_attach_args *aa = aux; 153 struct cfdata *cf = match; 154 155 /* sanity */ 156 return (acpi_matchhids(aa, acpibtn_hids, cf->cf_driver->cd_name)); 157 } 158 159 void 160 acpibtn_attach(struct device *parent, struct device *self, void *aux) 161 { 162 struct acpibtn_softc *sc = (struct acpibtn_softc *)self; 163 struct acpi_attach_args *aa = aux; 164 struct acpi_lid *lid; 165 int64_t lid_open = 1; 166 int64_t st; 167 168 sc->sc_acpi = (struct acpi_softc *)parent; 169 sc->sc_devnode = aa->aaa_node; 170 171 printf(": %s", sc->sc_devnode->name); 172 173 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st)) 174 st = STA_PRESENT | STA_ENABLED | STA_DEV_OK; 175 if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) != 176 (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) 177 return; 178 179 if (!strcmp(aa->aaa_dev, ACPI_DEV_LD)) { 180 sc->sc_btn_type = ACPIBTN_LID; 181 182 /* Set PSW (if present) to disable wake on this LID */ 183 (void)acpibtn_setpsw(sc, 0); 184 lid = malloc(sizeof(*lid), M_DEVBUF, M_WAITOK | M_ZERO); 185 lid->abl_softc = sc; 186 SLIST_INSERT_HEAD(&acpibtn_lids, lid, abl_link); 187 } else if (!strcmp(aa->aaa_dev, ACPI_DEV_PBD)) 188 sc->sc_btn_type = ACPIBTN_POWER; 189 else if (!strcmp(aa->aaa_dev, ACPI_DEV_SBD)) 190 sc->sc_btn_type = ACPIBTN_SLEEP; 191 192 if (sc->sc_btn_type == ACPIBTN_LID) { 193 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 194 sizeof(sc->sc_sensdev.xname)); 195 strlcpy(sc->sc_sens.desc, "lid open", 196 sizeof(sc->sc_sens.desc)); 197 sc->sc_sens.type = SENSOR_INDICATOR; 198 sensor_attach(&sc->sc_sensdev, &sc->sc_sens); 199 sensordev_install(&sc->sc_sensdev); 200 201 aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 202 "_LID", 0, NULL, &lid_open); 203 sc->sc_sens.value = lid_open; 204 } 205 206 /* Enable any related GPEs for wake. */ 207 if (acpi_toggle_wakedev(sc->sc_acpi, sc->sc_devnode, 1) == 0) { 208 #ifdef SUSPEND 209 device_register_wakeup(self); 210 printf("(wakeup)"); 211 #endif /* SUSPEND */ 212 } 213 214 printf("\n"); 215 aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify, 216 sc, ACPIDEV_NOPOLL | ACPIDEV_WAKEUP); 217 } 218 219 int 220 acpibtn_notify(struct aml_node *node, int notify_type, void *arg) 221 { 222 struct acpibtn_softc *sc = arg; 223 #ifndef SMALL_KERNEL 224 extern int lid_action; 225 extern int pwr_action; 226 int64_t lid; 227 #endif 228 229 dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type, 230 sc->sc_devnode->name); 231 232 /* Ignore button events if we're resuming. */ 233 if (acpi_resuming(sc->sc_acpi)) 234 return (0); 235 236 switch (sc->sc_btn_type) { 237 case ACPIBTN_LID: 238 #ifndef SMALL_KERNEL 239 /* 240 * Notification of 0x80 for lid opens or closes. We 241 * need to check the current status by calling the 242 * _LID method. 0 means the lid is closed and we 243 * should go to sleep. 244 */ 245 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 246 "_LID", 0, NULL, &lid)) 247 return (0); 248 if (sc->sc_sens.value == lid) 249 break; 250 sc->sc_sens.value = lid; 251 252 if (lid != 0) 253 break; 254 255 switch (lid_action) { 256 case 1: 257 case -1: 258 goto sleep; 259 #ifdef HIBERNATE 260 case 2: 261 /* Request to go to sleep */ 262 if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ)) 263 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 264 sc->sc_acpi, SLEEP_HIBERNATE); 265 break; 266 #endif 267 default: 268 break; 269 } 270 #endif /* SMALL_KERNEL */ 271 break; 272 case ACPIBTN_SLEEP: 273 #ifndef SMALL_KERNEL 274 switch (notify_type) { 275 case 0x02: 276 /* "something" has been taken care of by the system */ 277 break; 278 case 0x80: 279 sleep: 280 /* Request to go to sleep */ 281 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) 282 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 283 sc->sc_acpi, SLEEP_SUSPEND); 284 break; 285 } 286 #endif /* SMALL_KERNEL */ 287 break; 288 case ACPIBTN_POWER: 289 if (notify_type == 0x80) { 290 switch (pwr_action) { 291 case 0: 292 break; 293 case 1: 294 acpi_addtask(sc->sc_acpi, acpi_powerdown_task, 295 sc->sc_acpi, 0); 296 break; 297 #ifndef SMALL_KERNEL 298 case 2: 299 goto sleep; 300 #endif 301 } 302 } 303 break; 304 default: 305 printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc), 306 sc->sc_btn_type); 307 break; 308 } 309 310 return (0); 311 } 312 313 int 314 acpibtn_activate(struct device *self, int act) 315 { 316 struct acpibtn_softc *sc = (struct acpibtn_softc *)self; 317 int64_t lid_open = 1; 318 319 switch (act) { 320 case DVACT_WAKEUP: 321 switch (sc->sc_btn_type) { 322 case ACPIBTN_LID: 323 aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 324 "_LID", 0, NULL, &lid_open); 325 sc->sc_sens.value = lid_open; 326 break; 327 } 328 break; 329 } 330 return (0); 331 } 332