xref: /freebsd/sys/dev/acpica/acpi_dock.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /*-
2  * Copyright (c) 2005-2006 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include "opt_acpi.h"
30 #include <sys/param.h>
31 #include <sys/bus.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 
35 #include <contrib/dev/acpica/acpi.h>
36 #include <contrib/dev/acpica/acnamesp.h>
37 #include <dev/acpica/acpivar.h>
38 #include <dev/acpica/acpiio.h>
39 
40 /* Hooks for the ACPI CA debugging infrastructure */
41 #define _COMPONENT	ACPI_DOCK
42 ACPI_MODULE_NAME("DOCK")
43 
44 /* For Docking status */
45 #define ACPI_DOCK_STATUS_UNKNOWN	-1
46 #define ACPI_DOCK_STATUS_UNDOCKED	0
47 #define ACPI_DOCK_STATUS_DOCKED		1
48 
49 #define ACPI_DOCK_UNLOCK		0 /* Allow device to be ejected */
50 #define ACPI_DOCK_LOCK			1 /* Prevent dev from being removed */
51 
52 #define ACPI_DOCK_ISOLATE		0 /* Isolate from dock connector */
53 #define ACPI_DOCK_CONNECT		1 /* Connect to dock */
54 
55 struct acpi_dock_softc {
56 	int		_sta;
57 	int		_bdn;
58 	int		_uid;
59 	int		status;
60 	struct sysctl_ctx_list	*sysctl_ctx;
61 	struct sysctl_oid	*sysctl_tree;
62 };
63 
64 ACPI_SERIAL_DECL(dock, "ACPI Docking Station");
65 
66 /*
67  * Utility functions
68  */
69 
70 static void
71 acpi_dock_get_info(device_t dev)
72 {
73 	struct acpi_dock_softc *sc;
74 	ACPI_HANDLE	h;
75 
76 	sc = device_get_softc(dev);
77 	h = acpi_get_handle(dev);
78 
79 	if (ACPI_FAILURE(acpi_GetInteger(h, "_STA", &sc->_sta)))
80 		sc->_sta = ACPI_DOCK_STATUS_UNKNOWN;
81 	if (ACPI_FAILURE(acpi_GetInteger(h, "_BDN", &sc->_bdn)))
82 		sc->_bdn = ACPI_DOCK_STATUS_UNKNOWN;
83 	if (ACPI_FAILURE(acpi_GetInteger(h, "_UID", &sc->_uid)))
84 		sc->_uid = ACPI_DOCK_STATUS_UNKNOWN;
85 	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
86 		    "_STA: %04x, _BDN: %04x, _UID: %04x\n", sc->_sta,
87 		    sc->_bdn, sc->_uid);
88 }
89 
90 static int
91 acpi_dock_execute_dck(device_t dev, int dock)
92 {
93 	ACPI_HANDLE	h;
94 	ACPI_OBJECT	argobj;
95 	ACPI_OBJECT_LIST args;
96 	ACPI_BUFFER	buf;
97 	ACPI_OBJECT	retobj;
98 	ACPI_STATUS	status;
99 
100 	h = acpi_get_handle(dev);
101 
102 	argobj.Type = ACPI_TYPE_INTEGER;
103 	argobj.Integer.Value = dock;
104 	args.Count = 1;
105 	args.Pointer = &argobj;
106 	buf.Pointer = &retobj;
107 	buf.Length = sizeof(retobj);
108 	status = AcpiEvaluateObject(h, "_DCK", &args, &buf);
109 
110 	/*
111 	 * When _DCK is called with 0, OSPM will ignore the return value.
112 	 */
113 	if (dock == ACPI_DOCK_ISOLATE)
114 		return (0);
115 
116 	/* If _DCK returned 1, the request succeeded. */
117 	if (ACPI_SUCCESS(status) && retobj.Type == ACPI_TYPE_INTEGER &&
118 	    retobj.Integer.Value == 1)
119 		return (0);
120 
121 	return (-1);
122 }
123 
124 /* Lock devices while docked to prevent surprise removal. */
125 static void
126 acpi_dock_execute_lck(device_t dev, int lock)
127 {
128 	ACPI_HANDLE	h;
129 
130 	h = acpi_get_handle(dev);
131 	acpi_SetInteger(h, "_LCK", lock);
132 }
133 
134 /* Eject a device (i.e., motorized). */
135 static int
136 acpi_dock_execute_ejx(device_t dev, int eject, int state)
137 {
138 	ACPI_HANDLE	h;
139 	ACPI_STATUS	status;
140 	char		ejx[5];
141 
142 	h = acpi_get_handle(dev);
143 	snprintf(ejx, sizeof(ejx), "_EJ%d", state);
144 	status = acpi_SetInteger(h, ejx, eject);
145 	if (ACPI_SUCCESS(status))
146 		return (0);
147 
148 	return (-1);
149 }
150 
151 /* Find dependent devices.  When their parent is removed, so are they. */
152 static int
153 acpi_dock_is_ejd_device(ACPI_HANDLE dock_handle, ACPI_HANDLE handle)
154 {
155 	int		ret;
156 	ACPI_STATUS	ret_status;
157 	ACPI_BUFFER	ejd_buffer;
158 	ACPI_OBJECT	*obj;
159 
160 	ret = 0;
161 
162 	ejd_buffer.Pointer = NULL;
163 	ejd_buffer.Length = ACPI_ALLOCATE_BUFFER;
164 	ret_status = AcpiEvaluateObject(handle, "_EJD", NULL, &ejd_buffer);
165 	if (ACPI_FAILURE(ret_status))
166 		goto out;
167 
168 	obj = (ACPI_OBJECT *)ejd_buffer.Pointer;
169 	if (dock_handle == acpi_GetReference(NULL, obj))
170 		ret = 1;
171 
172 out:
173 	if (ejd_buffer.Pointer != NULL)
174 		AcpiOsFree(ejd_buffer.Pointer);
175 
176 	return (ret);
177 }
178 
179 /*
180  * Docking functions
181  */
182 
183 static void
184 acpi_dock_attach_later(void *context)
185 {
186 	device_t	dev;
187 
188 	dev = (device_t)context;
189 
190 	if (!device_is_enabled(dev))
191 		device_enable(dev);
192 
193 	mtx_lock(&Giant);
194 	device_probe_and_attach(dev);
195 	mtx_unlock(&Giant);
196 }
197 
198 static ACPI_STATUS
199 acpi_dock_insert_child(ACPI_HANDLE handle, UINT32 level, void *context,
200     void **status)
201 {
202 	device_t	dock_dev, dev;
203 	ACPI_HANDLE	dock_handle;
204 
205 	dock_dev = (device_t)context;
206 	dock_handle = acpi_get_handle(dock_dev);
207 
208 	if (!acpi_dock_is_ejd_device(dock_handle, handle))
209 		goto out;
210 
211 	ACPI_VPRINT(dock_dev, acpi_device_get_parent_softc(dock_dev),
212 		    "inserting device for %s\n", acpi_name(handle));
213 
214 #if 0
215 	/*
216 	 * If the system boot up w/o Docking, the devices under the dock
217 	 * still un-initialized, also control methods such as _INI, _STA
218 	 * are not executed.
219 	 * Normal devices are initialized at booting by calling
220 	 * AcpiInitializeObjects(), however the devices under the dock
221 	 * need to be initialized here on the scheme of ACPICA.
222 	 */
223 	ACPI_INIT_WALK_INFO	Info;
224 
225 	AcpiNsWalkNamespace(ACPI_TYPE_ANY, handle,
226 	    100, TRUE, AcpiNsInitOneDevice, &Info, NULL);
227 #endif
228 
229 	dev = acpi_get_device(handle);
230 	if (dev == NULL) {
231 		device_printf(dock_dev, "error: %s has no associated device\n",
232 		    acpi_name(handle));
233 		goto out;
234 	}
235 
236 	AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_dock_attach_later, dev);
237 
238 out:
239 	return (AE_OK);
240 }
241 
242 static void
243 acpi_dock_insert_children(device_t dev)
244 {
245 	ACPI_STATUS	status;
246 	ACPI_HANDLE	sb_handle;
247 
248 	status = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle);
249 	if (ACPI_SUCCESS(status)) {
250 		AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle,
251 		    100, acpi_dock_insert_child, dev, NULL);
252 	}
253 }
254 
255 static void
256 acpi_dock_insert(device_t dev)
257 {
258 	struct acpi_dock_softc *sc;
259 	ACPI_HANDLE	h;
260 
261 	ACPI_SERIAL_ASSERT(dock);
262 
263 	sc = device_get_softc(dev);
264 	h = acpi_get_handle(dev);
265 
266 	if (sc->status == ACPI_DOCK_STATUS_UNDOCKED ||
267 	    sc->status == ACPI_DOCK_STATUS_UNKNOWN) {
268 		acpi_dock_execute_lck(dev, ACPI_DOCK_LOCK);
269 		if (acpi_dock_execute_dck(dev, ACPI_DOCK_CONNECT) != 0) {
270 			device_printf(dev, "_DCK failed\n");
271 			return;
272 		}
273 
274 		if (!cold)
275 			acpi_dock_insert_children(dev);
276 		sc->status = ACPI_DOCK_STATUS_DOCKED;
277 	}
278 }
279 
280 /*
281  * Undock
282  */
283 
284 static ACPI_STATUS
285 acpi_dock_eject_child(ACPI_HANDLE handle, UINT32 level, void *context,
286     void **status)
287 {
288 	device_t	dock_dev, dev;
289 	ACPI_HANDLE	dock_handle;
290 
291 	dock_dev = *(device_t *)context;
292 	dock_handle = acpi_get_handle(dock_dev);
293 
294 	if (!acpi_dock_is_ejd_device(dock_handle, handle))
295 		goto out;
296 
297 	ACPI_VPRINT(dock_dev, acpi_device_get_parent_softc(dock_dev),
298 	    "ejecting device for %s\n", acpi_name(handle));
299 
300 	dev = acpi_get_device(handle);
301 	if (dev != NULL && device_is_attached(dev)) {
302 		mtx_lock(&Giant);
303 		device_detach(dev);
304 		mtx_unlock(&Giant);
305 	}
306 
307 	acpi_SetInteger(handle, "_EJ0", 0);
308 out:
309 	return (AE_OK);
310 }
311 
312 static void
313 acpi_dock_eject_children(device_t dev)
314 {
315 	ACPI_HANDLE	sb_handle;
316 	ACPI_STATUS	status;
317 
318 	status = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sb_handle);
319 	if (ACPI_SUCCESS(status)) {
320 		AcpiWalkNamespace(ACPI_TYPE_DEVICE, sb_handle,
321 		    100, acpi_dock_eject_child, &dev, NULL);
322 	}
323 }
324 
325 static void
326 acpi_dock_removal(device_t dev)
327 {
328 	struct acpi_dock_softc *sc;
329 
330 	ACPI_SERIAL_ASSERT(dock);
331 
332 	sc = device_get_softc(dev);
333 	if (sc->status == ACPI_DOCK_STATUS_DOCKED ||
334 	    sc->status == ACPI_DOCK_STATUS_UNKNOWN) {
335 		acpi_dock_eject_children(dev);
336 		if (acpi_dock_execute_dck(dev, ACPI_DOCK_ISOLATE) != 0)
337 			return;
338 
339 		acpi_dock_execute_lck(dev, ACPI_DOCK_UNLOCK);
340 
341 		if (acpi_dock_execute_ejx(dev, 1, 0) != 0) {
342 			device_printf(dev, "_EJ0 failed\n");
343 			return;
344 		}
345 
346 		sc->status = ACPI_DOCK_STATUS_UNDOCKED;
347 	}
348 
349 	acpi_dock_get_info(dev);
350 	if (sc->_sta != 0)
351 		device_printf(dev, "mechanical failure (%#x).\n", sc->_sta);
352 }
353 
354 /*
355  * Device/Bus check
356  */
357 
358 static void
359 acpi_dock_device_check(device_t dev)
360 {
361 	struct acpi_dock_softc *sc;
362 
363 	ACPI_SERIAL_ASSERT(dock);
364 
365 	sc = device_get_softc(dev);
366 	acpi_dock_get_info(dev);
367 
368 	/*
369 	 * If the _STA method indicates 'present' and 'functioning', the
370 	 * system is docked.  If _STA does not exist for this device, it
371 	 * is always present.
372 	 */
373 	if (sc->_sta == ACPI_DOCK_STATUS_UNKNOWN ||
374 	    ACPI_DEVICE_PRESENT(sc->_sta))
375 		acpi_dock_insert(dev);
376 	else if (sc->_sta == 0)
377 		acpi_dock_removal(dev);
378 }
379 
380 /*
381  * Notify Handler
382  */
383 
384 static void
385 acpi_dock_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context)
386 {
387 	device_t	dev;
388 
389 	dev = (device_t) context;
390 	ACPI_VPRINT(dev, acpi_device_get_parent_softc(dev),
391 		    "got notification %#x\n", notify);
392 
393 	ACPI_SERIAL_BEGIN(dock);
394 	switch (notify) {
395 	case ACPI_NOTIFY_BUS_CHECK:
396 	case ACPI_NOTIFY_DEVICE_CHECK:
397 		acpi_dock_device_check(dev);
398 		break;
399 	case ACPI_NOTIFY_EJECT_REQUEST:
400 		acpi_dock_removal(dev);
401 		break;
402 	default:
403 		device_printf(dev, "unknown notify %#x\n", notify);
404 		break;
405 	}
406 	ACPI_SERIAL_END(dock);
407 }
408 
409 static int
410 acpi_dock_status_sysctl(SYSCTL_HANDLER_ARGS)
411 {
412 	struct acpi_dock_softc *sc;
413 	device_t	dev;
414 	int		status, err;
415 
416 	err = 0;
417 	dev = (device_t)arg1;
418 
419 	sc = device_get_softc(dev);
420 	status = sc->status;
421 
422 	ACPI_SERIAL_BEGIN(dock);
423 	err = sysctl_handle_int(oidp, &status, 0, req);
424 	if (err != 0 || req->newptr == NULL)
425 		goto out;
426 
427 	if (status != ACPI_DOCK_STATUS_UNDOCKED &&
428 	    status != ACPI_DOCK_STATUS_DOCKED) {
429 		err = EINVAL;
430 		goto out;
431 	}
432 
433 	if (status == sc->status)
434 		goto out;
435 
436 	switch (status) {
437 	case ACPI_DOCK_STATUS_UNDOCKED:
438 		acpi_dock_removal(dev);
439 		break;
440 	case ACPI_DOCK_STATUS_DOCKED:
441 		acpi_dock_device_check(dev);
442 		break;
443 	default:
444 		err = EINVAL;
445 		break;
446 	}
447 out:
448 	ACPI_SERIAL_END(dock);
449 	return (err);
450 }
451 
452 static int
453 acpi_dock_probe(device_t dev)
454 {
455 	ACPI_HANDLE	h, tmp;
456 
457 	h = acpi_get_handle(dev);
458 	if (acpi_disabled("dock") ||
459 	    ACPI_FAILURE(AcpiGetHandle(h, "_DCK", &tmp)))
460 		return (ENXIO);
461 
462 	device_set_desc(dev, "ACPI Docking Station");
463 
464 	/*
465 	 * XXX Somewhere else in the kernel panics on "sysctl kern" if we
466 	 * return a negative value here (reprobe ok).
467 	 */
468 	return (0);
469 }
470 
471 static int
472 acpi_dock_attach(device_t dev)
473 {
474 	struct acpi_dock_softc *sc;
475 	ACPI_HANDLE	h;
476 
477 	sc = device_get_softc(dev);
478 	h = acpi_get_handle(dev);
479 	if (sc == NULL || h == NULL)
480 		return (ENXIO);
481 
482 	sc->status = ACPI_DOCK_STATUS_UNKNOWN;
483 
484 	AcpiEvaluateObject(h, "_INI", NULL, NULL);
485 
486 	ACPI_SERIAL_BEGIN(dock);
487 
488 	acpi_dock_device_check(dev);
489 
490 	/* Get the sysctl tree */
491 	sc->sysctl_ctx = device_get_sysctl_ctx(dev);
492 	sc->sysctl_tree = device_get_sysctl_tree(dev);
493 
494 	SYSCTL_ADD_INT(sc->sysctl_ctx,
495 		SYSCTL_CHILDREN(sc->sysctl_tree),
496 		OID_AUTO, "_sta", CTLFLAG_RD,
497 		&sc->_sta, 0, "Dock _STA");
498 	SYSCTL_ADD_INT(sc->sysctl_ctx,
499 		SYSCTL_CHILDREN(sc->sysctl_tree),
500 		OID_AUTO, "_bdn", CTLFLAG_RD,
501 		&sc->_bdn, 0, "Dock _BDN");
502 	SYSCTL_ADD_INT(sc->sysctl_ctx,
503 		SYSCTL_CHILDREN(sc->sysctl_tree),
504 		OID_AUTO, "_uid", CTLFLAG_RD,
505 		&sc->_uid, 0, "Dock _UID");
506 	SYSCTL_ADD_PROC(sc->sysctl_ctx,
507 		SYSCTL_CHILDREN(sc->sysctl_tree),
508 		OID_AUTO, "status",
509 		CTLTYPE_INT|CTLFLAG_RW, dev, 0,
510 		acpi_dock_status_sysctl, "I",
511 		"Dock/Undock operation");
512 
513 	ACPI_SERIAL_END(dock);
514 
515 	AcpiInstallNotifyHandler(h, ACPI_ALL_NOTIFY,
516 				 acpi_dock_notify_handler, dev);
517 
518 	return (0);
519 }
520 
521 static device_method_t acpi_dock_methods[] = {
522 	/* Device interface */
523 	DEVMETHOD(device_probe, acpi_dock_probe),
524 	DEVMETHOD(device_attach, acpi_dock_attach),
525 
526 	{0, 0}
527 };
528 
529 static driver_t	acpi_dock_driver = {
530 	"acpi_dock",
531 	acpi_dock_methods,
532 	sizeof(struct acpi_dock_softc),
533 };
534 
535 static devclass_t acpi_dock_devclass;
536 
537 DRIVER_MODULE(acpi_dock, acpi, acpi_dock_driver, acpi_dock_devclass, 0, 0);
538 MODULE_DEPEND(acpi_dock, acpi, 1, 1, 1);
539 
540