xref: /openbsd-src/sys/dev/acpi/acpidock.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /* $OpenBSD: acpidock.c,v 1.42 2010/07/27 01:21:19 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 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, "_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 	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
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
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 	extern struct aml_node	aml_root;
282 
283 	dnprintf(15, "%s: %s", DEVNAME(sc), node->name);
284 
285 	if (aml_evalnode(sc->sc_acpi, node, 0, NULL, &res) == -1)
286 		printf(": error\n");
287 	else {
288 		dock = aml_searchname(&aml_root, res.v_string);
289 
290 		if (dock == sc->sc_devnode)
291 			/* Add all children devices of Device containing _EJD */
292 			aml_walknodes(node->parent, AML_WALK_POST,
293 			    acpidock_walkchildren, sc);
294 		aml_freevalue(&res);
295 	}
296 
297 	return (0);
298 }
299