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
acpidock_match(struct device * parent,void * match,void * aux)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
acpidock_attach(struct device * parent,struct device * self,void * aux)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
acpidock_status(struct acpidock_softc * sc)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
acpidock_docklock(struct acpidock_softc * sc,int lock)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
acpidock_dockctl(struct acpidock_softc * sc,int dock)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
acpidock_eject(struct acpidock_softc * sc,struct aml_node * node)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
acpidock_notify(struct aml_node * node,int notify_type,void * arg)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
acpidock_walkchildren(struct aml_node * node,void * arg)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
acpidock_foundejd(struct aml_node * node,void * arg)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