1 /* $OpenBSD: acpidock.c,v 1.46 2022/08/10 16:58:16 patrick Exp $ */ 2 /* 3 * Copyright (c) 2006,2007 Michael Knudsen <mk@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/systm.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 #include <sys/sensors.h> 23 24 #include <machine/bus.h> 25 26 #include <dev/acpi/acpivar.h> 27 #include <dev/acpi/acpidev.h> 28 #include <dev/acpi/amltypes.h> 29 #include <dev/acpi/dsdt.h> 30 31 struct aml_nodelist { 32 struct aml_node *node; 33 TAILQ_ENTRY(aml_nodelist) entries; 34 }; 35 36 int acpidock_match(struct device *, void *, void *); 37 void acpidock_attach(struct device *, struct device *, void *); 38 39 const struct cfattach acpidock_ca = { 40 sizeof(struct acpidock_softc), acpidock_match, acpidock_attach 41 }; 42 43 struct cfdriver acpidock_cd = { 44 NULL, "acpidock", DV_DULL 45 }; 46 47 int acpidock_docklock(struct acpidock_softc *, int); 48 int acpidock_dockctl(struct acpidock_softc *, int); 49 int acpidock_eject(struct acpidock_softc *, struct aml_node *); 50 int acpidock_notify(struct aml_node *, int, void *); 51 int acpidock_status(struct acpidock_softc *); 52 int acpidock_walkchildren(struct aml_node *, void *); 53 54 int acpidock_foundejd(struct aml_node *, void *); 55 56 int 57 acpidock_match(struct device *parent, void *match, void *aux) 58 { 59 struct acpi_attach_args *aaa = aux; 60 struct cfdata *cf = match; 61 62 /* sanity */ 63 if (aaa->aaa_name == NULL || 64 strcmp(aaa->aaa_name, cf->cf_driver->cd_name) != 0 || 65 aaa->aaa_table != NULL) 66 return (0); 67 68 return (1); 69 } 70 71 void 72 acpidock_attach(struct device *parent, struct device *self, void *aux) 73 { 74 struct acpidock_softc *sc = (struct acpidock_softc *)self; 75 struct acpi_attach_args *aa = aux; 76 77 sc->sc_acpi = (struct acpi_softc *)parent; 78 sc->sc_devnode = aa->aaa_node; 79 80 printf(": %s", sc->sc_devnode->name); 81 82 acpidock_status(sc); 83 if (sc->sc_docked == ACPIDOCK_STATUS_DOCKED) { 84 acpidock_docklock(sc, 1); 85 acpidock_dockctl(sc, 1); 86 } 87 88 acpidock_status(sc); 89 printf("%s docked (%d)\n", 90 sc->sc_docked == ACPIDOCK_STATUS_DOCKED ? "" : " not", 91 sc->sc_sta); 92 93 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 94 sizeof(sc->sc_sensdev.xname)); 95 if (sc->sc_docked) 96 strlcpy(sc->sc_sens.desc, "docked", 97 sizeof(sc->sc_sens.desc)); 98 else 99 strlcpy(sc->sc_sens.desc, "not docked", 100 sizeof(sc->sc_sens.desc)); 101 102 sc->sc_sens.type = SENSOR_INDICATOR; 103 sc->sc_sens.value = sc->sc_docked == ACPIDOCK_STATUS_DOCKED; 104 sc->sc_sens.status = sc->sc_docked ? SENSOR_S_OK : SENSOR_S_UNKNOWN; 105 sensor_attach(&sc->sc_sensdev, &sc->sc_sens); 106 sensordev_install(&sc->sc_sensdev); 107 108 TAILQ_INIT(&sc->sc_deps_h); 109 aml_find_node(sc->sc_acpi->sc_root, "_EJD", acpidock_foundejd, sc); 110 111 aml_register_notify(sc->sc_devnode, aa->aaa_dev, 112 acpidock_notify, sc, ACPIDEV_NOPOLL); 113 } 114 115 int 116 acpidock_status(struct acpidock_softc *sc) 117 { 118 int64_t sta; 119 int rv; 120 121 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, 122 &sta) != 0) { 123 sta = 0; 124 rv = 0; 125 } else 126 rv = 1; 127 128 sc->sc_sta = sta; 129 sc->sc_docked = sc->sc_sta & STA_PRESENT; 130 131 return (rv); 132 } 133 134 int 135 acpidock_docklock(struct acpidock_softc *sc, int lock) 136 { 137 struct aml_value cmd; 138 struct aml_value res; 139 int rv; 140 141 memset(&cmd, 0, sizeof cmd); 142 cmd.v_integer = lock; 143 cmd.type = AML_OBJTYPE_INTEGER; 144 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_LCK", 1, &cmd, 145 &res) != 0) { 146 dnprintf(20, "%s: _LCK %d failed\n", DEVNAME(sc), lock); 147 rv = 0; 148 } else { 149 dnprintf(20, "%s: _LCK %d successful\n", DEVNAME(sc), lock); 150 rv = 1; 151 } 152 153 aml_freevalue(&res); 154 155 return (rv); 156 } 157 158 int 159 acpidock_dockctl(struct acpidock_softc *sc, int dock) 160 { 161 struct aml_value cmd; 162 struct aml_value res; 163 int rv; 164 165 memset(&cmd, 0, sizeof cmd); 166 cmd.v_integer = dock; 167 cmd.type = AML_OBJTYPE_INTEGER; 168 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_DCK", 1, &cmd, 169 &res) != 0) { 170 dnprintf(15, "%s: _DCK %d failed\n", DEVNAME(sc), dock); 171 rv = 0; 172 } else { 173 dnprintf(15, "%s: _DCK %d successful\n", DEVNAME(sc), dock); 174 rv = 1; 175 } 176 177 aml_freevalue(&res); 178 179 return (rv); 180 } 181 182 int 183 acpidock_eject(struct acpidock_softc *sc, struct aml_node *node) 184 { 185 struct aml_value cmd; 186 struct aml_value res; 187 int rv; 188 189 if (node != sc->sc_devnode) 190 aml_notify(node, 3); 191 192 memset(&cmd, 0, sizeof cmd); 193 cmd.v_integer = 1; 194 cmd.type = AML_OBJTYPE_INTEGER; 195 if (aml_evalname(sc->sc_acpi, node, "_EJ0", 1, &cmd, 196 &res) != 0) { 197 dnprintf(15, "%s: _EJ0 failed\n", DEVNAME(sc)); 198 rv = 0; 199 } else { 200 dnprintf(15, "%s: _EJ0 successful\n", DEVNAME(sc)); 201 rv = 1; 202 } 203 204 aml_freevalue(&res); 205 206 return (rv); 207 } 208 209 int 210 acpidock_notify(struct aml_node *node, int notify_type, void *arg) 211 { 212 struct acpidock_softc *sc = arg; 213 struct aml_nodelist *n; 214 215 dnprintf(5, "%s: acpidock_notify: notify %d\n", DEVNAME(sc), 216 notify_type); 217 218 switch (notify_type) { 219 case ACPIDOCK_EVENT_INSERT: 220 acpidock_docklock(sc, 1); 221 acpidock_dockctl(sc, 1); 222 223 TAILQ_FOREACH_REVERSE(n, &sc->sc_deps_h, aml_nodelisth, entries) 224 aml_notify(n->node, 0x00); 225 break; 226 227 case ACPIDOCK_EVENT_EJECT: 228 case ACPIDOCK_EVENT_DEVCHECK: 229 /* ACPI Spec says eject button press generates 230 * a Notify(Device, 1); */ 231 TAILQ_FOREACH(n, &sc->sc_deps_h, entries) 232 acpidock_eject(sc, n->node); 233 acpidock_dockctl(sc, 0); 234 acpidock_docklock(sc, 0); 235 236 /* now actually undock */ 237 acpidock_eject(sc, sc->sc_devnode); 238 break; 239 } 240 241 acpidock_status(sc); 242 sc->sc_sens.value = sc->sc_docked == ACPIDOCK_STATUS_DOCKED; 243 sc->sc_sens.status = sc->sc_docked ? SENSOR_S_OK : SENSOR_S_UNKNOWN; 244 if (sc->sc_docked) 245 strlcpy(sc->sc_sens.desc, "docked", 246 sizeof(sc->sc_sens.desc)); 247 else 248 strlcpy(sc->sc_sens.desc, "not docked", 249 sizeof(sc->sc_sens.desc)); 250 251 printf("%s: %s\n", 252 DEVNAME(sc), sc->sc_docked == ACPIDOCK_STATUS_DOCKED ? 253 "docked" : "undocked"); 254 255 return (0); 256 } 257 258 int 259 acpidock_walkchildren(struct aml_node *node, void *arg) 260 { 261 struct acpidock_softc *sc = arg; 262 struct aml_nodelist *n; 263 264 if (node && node->value && node->value->type == AML_OBJTYPE_DEVICE) { 265 n = malloc(sizeof *n, M_DEVBUF, M_WAITOK | M_ZERO); 266 n->node = node; 267 dnprintf(10,"%s depends on", aml_nodename(node)); 268 dnprintf(10,"%s\n", aml_nodename(sc->sc_devnode)); 269 TAILQ_INSERT_TAIL(&sc->sc_deps_h, n, entries); 270 } 271 272 return (0); 273 } 274 275 int 276 acpidock_foundejd(struct aml_node *node, void *arg) 277 { 278 struct acpidock_softc *sc = (struct acpidock_softc *)arg; 279 struct aml_value res; 280 struct aml_node *dock; 281 282 dnprintf(15, "%s: %s", DEVNAME(sc), node->name); 283 284 if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1) 285 printf(": error\n"); 286 else { 287 dock = aml_searchname(sc->sc_acpi->sc_root, res.v_string); 288 289 if (dock == sc->sc_devnode) 290 /* Add all children devices of Device containing _EJD */ 291 aml_walknodes(node->parent, AML_WALK_POST, 292 acpidock_walkchildren, sc); 293 aml_freevalue(&res); 294 } 295 296 return (0); 297 } 298