xref: /openbsd/sys/dev/acpi/acpidock.c (revision 9108e412)
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