xref: /openbsd/sys/dev/acpi/acpidock.c (revision 09467b48)
1 /* $OpenBSD: acpidock.c,v 1.44 2015/03/14 03:38:46 jsg 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 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 	extern struct aml_node	aml_root;
77 
78 	sc->sc_acpi = (struct acpi_softc *)parent;
79 	sc->sc_devnode = aa->aaa_node;
80 
81 	printf(": %s", sc->sc_devnode->name);
82 
83 	acpidock_status(sc);
84 	if (sc->sc_docked == ACPIDOCK_STATUS_DOCKED) {
85 		acpidock_docklock(sc, 1);
86 		acpidock_dockctl(sc, 1);
87 	}
88 
89 	acpidock_status(sc);
90 	printf("%s docked (%d)\n",
91 	    sc->sc_docked == ACPIDOCK_STATUS_DOCKED ? "" : " not",
92 	    sc->sc_sta);
93 
94 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
95 	    sizeof(sc->sc_sensdev.xname));
96 	if (sc->sc_docked)
97 		strlcpy(sc->sc_sens.desc, "docked",
98 		    sizeof(sc->sc_sens.desc));
99 	else
100 		strlcpy(sc->sc_sens.desc, "not docked",
101 		    sizeof(sc->sc_sens.desc));
102 
103 	sc->sc_sens.type = SENSOR_INDICATOR;
104 	sc->sc_sens.value = sc->sc_docked == ACPIDOCK_STATUS_DOCKED;
105 	sc->sc_sens.status = sc->sc_docked ? SENSOR_S_OK : SENSOR_S_UNKNOWN;
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, "_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 	case ACPIDOCK_EVENT_DEVCHECK:
230 		/* ACPI Spec says eject button press generates
231 		 * a Notify(Device, 1); */
232 		TAILQ_FOREACH(n, &sc->sc_deps_h, entries)
233 			acpidock_eject(sc, n->node);
234 		acpidock_dockctl(sc, 0);
235 		acpidock_docklock(sc, 0);
236 
237 		/* now actually undock */
238 		acpidock_eject(sc, sc->sc_devnode);
239 		break;
240 	}
241 
242 	acpidock_status(sc);
243 	sc->sc_sens.value = sc->sc_docked == ACPIDOCK_STATUS_DOCKED;
244 	sc->sc_sens.status = sc->sc_docked ? SENSOR_S_OK : SENSOR_S_UNKNOWN;
245 	if (sc->sc_docked)
246 		strlcpy(sc->sc_sens.desc, "docked",
247 		    sizeof(sc->sc_sens.desc));
248 	else
249 		strlcpy(sc->sc_sens.desc, "not docked",
250 		    sizeof(sc->sc_sens.desc));
251 
252 	printf("%s: %s\n",
253 	    DEVNAME(sc), sc->sc_docked == ACPIDOCK_STATUS_DOCKED ?
254 	    "docked" : "undocked");
255 
256 	return (0);
257 }
258 
259 int
260 acpidock_walkchildren(struct aml_node *node, void *arg)
261 {
262 	struct acpidock_softc	*sc = arg;
263 	struct aml_nodelist	*n;
264 
265 	if (node && node->value && node->value->type == AML_OBJTYPE_DEVICE) {
266 		n = malloc(sizeof *n, M_DEVBUF, M_WAITOK | M_ZERO);
267 		n->node = node;
268 		dnprintf(10,"%s depends on", aml_nodename(node));
269 		dnprintf(10,"%s\n", aml_nodename(sc->sc_devnode));
270 		TAILQ_INSERT_TAIL(&sc->sc_deps_h, n, entries);
271 	}
272 
273 	return (0);
274 }
275 
276 int
277 acpidock_foundejd(struct aml_node *node, void *arg)
278 {
279 	struct acpidock_softc	*sc = (struct acpidock_softc *)arg;
280 	struct aml_value	res;
281 	struct aml_node		*dock;
282 	extern struct aml_node	aml_root;
283 
284 	dnprintf(15, "%s: %s", DEVNAME(sc), node->name);
285 
286 	if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1)
287 		printf(": error\n");
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