xref: /openbsd-src/sys/dev/acpi/acpidock.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $OpenBSD: acpidock.c,v 1.32 2009/03/13 18:53:50 jordan 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 
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 	} else {
88 		acpidock_dockctl(sc, 0);
89 		acpidock_docklock(sc, 0);
90 	}
91 
92 	acpidock_status(sc);
93 	printf(":%s docked (%d)\n",
94 	    sc->sc_docked == ACPIDOCK_STATUS_DOCKED ? "" : " not",
95 	    sc->sc_sta);
96 
97 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
98 	    sizeof(sc->sc_sensdev.xname));
99 	if (sc->sc_docked)
100 		strlcpy(sc->sc_sens.desc, "docked",
101 		    sizeof(sc->sc_sens.desc));
102 	else
103 		strlcpy(sc->sc_sens.desc, "not docked",
104 		    sizeof(sc->sc_sens.desc));
105 
106 	sc->sc_sens.type = SENSOR_INDICATOR;
107 	sc->sc_sens.value = sc->sc_docked == ACPIDOCK_STATUS_DOCKED;
108 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
109 	sensordev_install(&sc->sc_sensdev);
110 
111 	TAILQ_INIT(&sc->sc_deps_h);
112 	aml_find_node(aml_root.child, "_EJD", acpidock_foundejd, sc);
113 
114 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
115 	    acpidock_notify, sc, ACPIDEV_NOPOLL);
116 }
117 
118 int
119 acpidock_status(struct acpidock_softc *sc)
120 {
121 	int64_t			sta;
122 	int			rv;
123 
124 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL,
125 	    &sta) != 0) {
126 		sta = 0;
127 		rv = 0;
128 	}
129 	else
130 		rv = 1;
131 
132 	sc->sc_sta = sta;
133 
134 	sc->sc_docked = sc->sc_sta & STA_PRESENT;
135 
136 	return (rv);
137 }
138 
139 int
140 acpidock_docklock(struct acpidock_softc *sc, int lock)
141 {
142 	struct aml_value	cmd;
143 	struct aml_value	res;
144 	int rv;
145 
146 	memset(&cmd, 0, sizeof cmd);
147 	cmd.v_integer = lock;
148 	cmd.type = AML_OBJTYPE_INTEGER;
149 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_LCK", 1, &cmd,
150 	    &res) != 0) {
151 		dnprintf(20, "%s: _LCK %d failed\n", DEVNAME(sc), lock);
152 
153 		rv = 0;
154 	} else {
155 		dnprintf(20, "%s: _LCK %d successful\n", DEVNAME(sc), lock);
156 
157 		rv = 1;
158 	}
159 
160 	aml_freevalue(&res);
161 
162 	return rv;
163 }
164 
165 int
166 acpidock_dockctl(struct acpidock_softc *sc, int dock)
167 {
168 	struct aml_value	cmd;
169 	struct aml_value	res;
170 	int rv;
171 
172 	memset(&cmd, 0, sizeof cmd);
173 	cmd.v_integer = dock;
174 	cmd.type = AML_OBJTYPE_INTEGER;
175 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_DCK", 1, &cmd,
176 	    &res) != 0) {
177 		/* XXX */
178 		dnprintf(15, "%s: _DCK %d failed\n", DEVNAME(sc), dock);
179 
180 		rv = 0;
181 	} else {
182 		dnprintf(15, "%s: _DCK %d successful\n", DEVNAME(sc), dock);
183 
184 		rv = 1;
185 	}
186 
187 	aml_freevalue(&res);
188 
189 	return rv;
190 }
191 
192 int
193 acpidock_eject(struct acpidock_softc *sc, struct aml_node *node)
194 {
195 	struct aml_value	cmd;
196 	struct aml_value	res;
197 	int rv;
198 
199 	memset(&cmd, 0, sizeof cmd);
200 	cmd.v_integer = 1;
201 	cmd.type = AML_OBJTYPE_INTEGER;
202 	if (aml_evalname(sc->sc_acpi, node, "_EJ0", 1, &cmd,
203 	    &res) != 0) {
204 		/* XXX */
205 		dnprintf(15, "%s: _EJ0 failed\n", DEVNAME(sc));
206 
207 		rv = 0;
208 	} else {
209 		dnprintf(15, "%s: _EJ0 successful\n", DEVNAME(sc));
210 
211 		rv = 1;
212 	}
213 
214 	aml_freevalue(&res);
215 
216 	return rv;
217 }
218 
219 int
220 acpidock_notify(struct aml_node *node, int notify_type, void *arg)
221 {
222 	struct acpidock_softc	*sc = arg;
223 
224 	dnprintf(5, "%s: acpidock_notify: notify %d\n", DEVNAME(sc),
225 	    notify_type);
226 
227 	switch (notify_type) {
228 	case ACPIDOCK_EVENT_INSERT:
229 		printf("%s: dock", DEVNAME(sc));
230 		acpidock_docklock(sc, 1);
231 		acpidock_dockctl(sc, 1);
232 
233 		break;
234 	case ACPIDOCK_EVENT_EJECT: {
235 		struct aml_nodelist *n;
236 
237 		TAILQ_FOREACH(n, &sc->sc_deps_h, entries)
238 			acpidock_eject(sc, n->node);
239 
240 		acpidock_dockctl(sc, 0);
241 		acpidock_docklock(sc, 0);
242 
243 		/* now actually undock */
244 		acpidock_eject(sc, sc->sc_devnode);
245 
246 		printf("%s: undock", DEVNAME(sc));
247 
248 		break;
249 		}
250 	}
251 
252 	acpidock_status(sc);
253 	sc->sc_sens.value = sc->sc_docked == ACPIDOCK_STATUS_DOCKED;
254 	if (sc->sc_docked)
255 		strlcpy(sc->sc_sens.desc, "docked",
256 		    sizeof(sc->sc_sens.desc));
257 	else
258 		strlcpy(sc->sc_sens.desc, "not docked",
259 		    sizeof(sc->sc_sens.desc));
260 
261 	printf(": status %s\n",
262 	    sc->sc_docked == ACPIDOCK_STATUS_DOCKED ? "docked" : "undocked");
263 
264 	return (0);
265 }
266 
267 int
268 acpidock_foundejd(struct aml_node *node, void *arg)
269 {
270 	struct acpidock_softc *sc = (struct acpidock_softc *)arg;
271 	struct aml_value res;
272 
273 	dnprintf(15, "%s: %s", DEVNAME(sc), node->name);
274 
275 	if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1) {
276 		printf(": error\n");
277 	} else {
278 		struct aml_nodelist *n;
279 
280 		/* XXX debug */
281 		dnprintf(10, "%s: %s depends on %s\n", DEVNAME(sc),
282 		    node->name, res.v_string);
283 
284 		/* XXX more than one dock? */
285 		n = malloc(sizeof(struct aml_nodelist), M_DEVBUF, M_WAITOK);
286 		n->node = node;
287 
288 		TAILQ_INSERT_TAIL(&sc->sc_deps_h, n, entries);
289 	}
290 
291 	aml_freevalue(&res);
292 
293 	return 0;
294 }
295