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