acpi_ibm.c revision 138825
1/*-
2 * Copyright (c) 2004 Takanori Watanabe
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: head/sys/dev/acpi_support/acpi_ibm.c 138825 2004-12-13 23:31:46Z njl $
27 */
28
29#include "opt_acpi.h"
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/bus.h>
33#include <machine/cpufunc.h>
34#include "acpi.h"
35#include "acpi_if.h"
36#include <sys/module.h>
37#include <dev/acpica/acpivar.h>
38#include <sys/sysctl.h>
39#include <machine/clock.h>
40
41#define _COMPONENT	ACPI_OEM
42ACPI_MODULE_NAME("IBM")
43
44#define IBM_RTC_MISCKEY 0x65
45#define IBM_RTC_BRIGHTNESS 0x6c
46#define   IBM_RTC_MASK_BRI 0x7
47#define   IBM_RTC_MASK_BRKEY 0x40
48#define IBM_RTC_KEYLIGHT 0x66
49#define   IBM_RTC_MASK_KEYLIGHT 0x10
50#define IBM_RTC_VOLUME 0x6e
51#define   IBM_RTC_MASK_VOL 0xf
52#define   IBM_RTC_MASK_MUTE 0x40
53#define   IBM_RTC_MASK_VOLKEY 0x80
54
55#define IBM_NAME_GET_WIRELESS "GBDC"
56#define IBM_NAME_SET_WIRELESS "SBDC"
57#define IBM_NAME_INTERFACE_VERSION "MHKV"
58#define IBM_NAME_AVAIL_MASK "MHKA"
59#define IBM_NAME_CURRENT_MASK "MHKN"
60#define IBM_NAME_MODIFY_MASK "MHKM"
61#define IBM_NAME_GET_EVENT "MHKP"
62#define IBM_NAME_ENABLE "MHKC"
63#if 0
64/* TPX31 Specific? */
65#define IBM_UCMS_VOLDN 0x0
66#define IBM_UCMS_VOLUP 0x1
67#define IBM_UCMS_MUTE 0x2
68#define IBM_UCMS_BRIUP 0x4
69#define IBM_UCMS_BRIDN 0x5
70#define IBM_UCMS_KEYLIGHT 0xe
71#endif
72
73struct acpi_ibm_softc {
74	unsigned int	ibm_version;
75	unsigned int	ibm_availmask;
76	unsigned int	ibm_initialmask;
77	int		ibm_enable;
78	int		device_flag;
79#define IBM_MHKN_AVAIL 1
80#define IBM_MHKM_AVAIL 2
81	struct sysctl_oid *oid_bluetooth;
82	struct sysctl_oid *oid_wlan;
83};
84
85static int	acpi_ibm_probe(device_t dev);
86static int	acpi_ibm_attach(device_t dev);
87static int	acpi_ibm_detach(device_t dev);
88static void
89acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify,
90			void *context);
91static int	sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS);
92static int	sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS);
93static int	sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS);
94static int	sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS);
95static int	sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS);
96static int	sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS);
97static int	sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS);
98static int	sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS);
99static int	acpi_ibm_enable_mask(device_t dev, int val);
100
101static device_method_t acpi_ibm_methods[] = {
102	/* Device interface */
103	DEVMETHOD(device_probe, acpi_ibm_probe),
104	DEVMETHOD(device_attach, acpi_ibm_attach),
105	DEVMETHOD(device_detach, acpi_ibm_detach),
106
107	{0, 0}
108};
109
110static driver_t	acpi_ibm_driver = {
111	"acpi_ibm",
112	acpi_ibm_methods,
113	sizeof(struct acpi_ibm_softc),
114};
115
116static devclass_t acpi_ibm_devclass;
117
118DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass,
119	      0, 0);
120MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
121static char    *ibm_id[] = {"IBM0068", NULL};
122
123static int
124acpi_ibm_probe(device_t dev)
125{
126	struct acpi_ibm_softc *sc;
127	int		ret = ENXIO;
128
129	sc = device_get_softc(dev);
130
131	if (ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_id)) {
132		device_set_desc(dev, "IBM ThinkPad Button");
133		ret = 0;
134	}
135	return (ret);
136}
137
138static int
139acpi_ibm_call_two_method(device_t dev, char *name, int val1, int val2)
140{
141	ACPI_OBJECT	arg [2];
142	ACPI_OBJECT_LIST args = {.Count = 2,.Pointer = arg};
143	arg[0].Type = ACPI_TYPE_INTEGER;
144	arg[0].Integer.Value = val1;
145	arg[1].Type = ACPI_TYPE_INTEGER;
146	arg[1].Integer.Value = val2;
147	return AcpiEvaluateObject(acpi_get_handle(dev), name, &args, NULL);
148}
149
150static int
151acpi_ibm_attach(device_t dev)
152{
153	struct acpi_ibm_softc *sc;
154	ACPI_STATUS	status;
155	ACPI_HANDLE	h;
156	int		dummy;
157	struct sysctl_oid *oid;
158	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
159
160	sc = device_get_softc(dev);
161	sc->device_flag = 0;
162	if (ACPI_FAILURE
163	    (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_INTERFACE_VERSION, &sc->ibm_version))) {
164		sc->ibm_version = 0;
165	}
166	device_printf(dev, "Version %x\n", sc->ibm_version);
167	if (ACPI_FAILURE
168	    (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_AVAIL_MASK, &sc->ibm_availmask)))
169		sc->ibm_availmask = 0xffffffff;
170
171	if (ACPI_FAILURE
172	    (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &sc->ibm_initialmask)))
173		sc->ibm_initialmask = 0xffffffff;
174	else
175		sc->device_flag |= IBM_MHKN_AVAIL;
176
177	if (ACPI_SUCCESS(status = AcpiGetHandle(acpi_get_handle(dev), IBM_NAME_MODIFY_MASK, &h)))
178		sc->device_flag |= IBM_MHKM_AVAIL;
179	else
180		printf("%s\n", AcpiFormatException(status));
181
182	device_printf(dev, "Available Mask %x\n", sc->ibm_availmask);
183	device_printf(dev, "Initial Mask %x\n", sc->ibm_initialmask);
184	/* Install Specific Handler */
185	status = AcpiInstallNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler, dev);
186	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
187			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
188			OID_AUTO, "key_mask", CTLTYPE_INT | CTLFLAG_RW,
189			dev, 0,
190			sysctl_acpi_ibm_mask_handler, "I", "Hot key mask");
191	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
192		       SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
193		       OID_AUTO, "version", CTLFLAG_RD,
194		       &sc->ibm_version, 0, "Interface version");
195	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
196		       SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
197		       OID_AUTO, "avail_mask", CTLFLAG_RD,
198		       &sc->ibm_availmask, 0, "Available Key mask");
199	if (ACPI_FAILURE(acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, 1)))
200		goto fail;
201	sc->ibm_enable = 1;
202	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
203			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
204			OID_AUTO, "enable", CTLTYPE_INT | CTLFLAG_RW,
205			dev, 0,
206		     sysctl_acpi_ibm_enable_handler, "I", "Hot key enable");
207	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
208			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
209			OID_AUTO, "misckey", CTLTYPE_INT | CTLFLAG_RD,
210			dev, 0,
211	       sysctl_acpi_ibm_misckey_handler, "I", "Key Status: Poll me");
212	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
213			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
214			OID_AUTO, "brightness", CTLTYPE_INT | CTLFLAG_RD,
215			dev, 0,
216		     sysctl_acpi_ibm_brightness_handler, "I", "Brightness");
217
218	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
219			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
220			OID_AUTO, "volume", CTLTYPE_INT | CTLFLAG_RD,
221			dev, 0,
222			sysctl_acpi_ibm_volume_handler, "I", "Volume");
223
224	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
225			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
226			OID_AUTO, "mute", CTLTYPE_INT | CTLFLAG_RD,
227			dev, 0,
228			sysctl_acpi_ibm_mute_handler, "I", "Muting");
229
230
231	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
232			SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
233			OID_AUTO, "keylight", CTLTYPE_INT | CTLFLAG_RD,
234			dev, 0,
235			sysctl_acpi_ibm_keylight_handler, "I", "Key Light");
236	if (ACPI_SUCCESS(acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_WIRELESS, &dummy))) {
237		oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
238			       SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
239		    OID_AUTO, "bluetooth", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
240		 sysctl_acpi_ibm_wireless_handler, "I", "Bluetooth Enable");
241		sc->oid_bluetooth = oid;
242		oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
243			       SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
244			 OID_AUTO, "wlan", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
245		      sysctl_acpi_ibm_wireless_handler, "I", "WLAN Enable");
246		sc->oid_wlan = oid;
247	}
248	return_VALUE(0);
249fail:
250	device_printf(dev, "FAILED\n");
251	AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler);
252	return_VALUE(EINVAL);
253}
254
255static int
256acpi_ibm_detach(device_t dev)
257{
258	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
259
260	struct acpi_ibm_softc *sc = device_get_softc(dev);
261	acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, 0);
262	acpi_ibm_enable_mask(dev, sc->ibm_initialmask);
263
264	AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler);
265	return_VALUE(0);
266}
267#if 0
268static int
269acpi_ibm_suspend(device_t dev)
270{
271	struct acpi_ibm_softc *sc = device_get_softc(dev);
272	return_VALUE(0);
273}
274
275static int
276acpi_ibm_resume(device_t dev)
277{
278	return (0);
279}
280#endif
281static void
282acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify,
283			void *context)
284{
285	int		mhkp      , arg, type;
286	device_t	dev = context;
287	struct acpi_ibm_softc *sc = device_get_softc(dev);
288
289	printf("IBM:NOTIFY:%x\n", notify);
290	if (notify != 0x80) {
291		printf("Unknown notify\n");
292	}
293	for (;;) {
294
295		acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_EVENT, &mhkp);
296
297		if (mhkp == 0) {
298			break;
299		}
300		printf("notify:%x\n", mhkp);
301
302		type = (mhkp >> 12) & 0xf;
303		arg = mhkp & 0xfff;
304		switch (type) {
305		case 1:
306			if (!(sc->ibm_availmask & (1 << (arg - 1)))) {
307				printf("Unknown key %d\n", arg);
308				break;
309			}
310			acpi_UserNotify("IBM", h, (arg & 0xff));
311			break;
312		default:
313			break;
314		}
315	}
316}
317
318static int
319acpi_ibm_enable_mask(device_t dev, int val)
320{
321	int		i;
322	struct acpi_ibm_softc *sc = device_get_softc(dev);
323
324	if (!(sc->device_flag | IBM_MHKM_AVAIL)) {
325		return -1;
326	}
327	for (i = 0; i < 32; i++) {
328		acpi_ibm_call_two_method(dev, IBM_NAME_MODIFY_MASK, i + 1, 1);
329		if (!((1 << i) & val))
330			acpi_ibm_call_two_method(dev, IBM_NAME_MODIFY_MASK, i + 1, 0);
331	}
332	return 0;
333}
334
335static int
336sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS)
337{
338	device_t	dev = arg1;
339	int		val = 0xffffffff;
340	int		error = 0;
341
342	struct acpi_ibm_softc *sc = device_get_softc(dev);
343
344	if (sc->device_flag & IBM_MHKN_AVAIL)
345		acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &val);
346
347	error = sysctl_handle_int(oidp, &val, 0, req);
348
349	if (error || !req->newptr)
350		return error;
351
352	val &= sc->ibm_availmask;
353	val |= sc->ibm_initialmask;
354
355	acpi_ibm_enable_mask(dev, val);
356
357	return 0;
358}
359
360static int
361sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS)
362{
363	int		val       , error;
364	val = rtcin(IBM_RTC_MISCKEY);
365	error = sysctl_handle_int(oidp, &val, 0, req);
366	if (error || !req->newptr)
367		return error;
368	return 0;
369}
370
371
372static int
373sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS)
374{
375	int		val       , error;
376	val = rtcin(IBM_RTC_BRIGHTNESS);
377	val &= IBM_RTC_MASK_BRI;
378	error = sysctl_handle_int(oidp, &val, 0, req);
379	if (error || !req->newptr)
380		return error;
381	return 0;
382}
383
384static int
385sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS)
386{
387	int		val       , error;
388	val = rtcin(IBM_RTC_VOLUME);
389	val = ((val & IBM_RTC_MASK_MUTE) == IBM_RTC_MASK_MUTE);
390
391	error = sysctl_handle_int(oidp, &val, 0, req);
392	if (error || !req->newptr)
393		return error;
394	return 0;
395}
396
397static int
398sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS)
399{
400	int		val       , error;
401	val = ((rtcin(IBM_RTC_KEYLIGHT) & IBM_RTC_MASK_KEYLIGHT)
402	       == IBM_RTC_MASK_KEYLIGHT);
403
404	error = sysctl_handle_int(oidp, &val, 0, req);
405	if (error || !req->newptr)
406		return error;
407	return 0;
408}
409
410static int
411sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS)
412{
413	int		val       , error;
414	val = rtcin(IBM_RTC_VOLUME);
415	val &= IBM_RTC_MASK_VOL;
416	error = sysctl_handle_int(oidp, &val, 0, req);
417	if (error || !req->newptr)
418		return error;
419	return 0;
420}
421
422static int
423sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS)
424{
425	device_t	dev = arg1;
426	struct acpi_ibm_softc *sc = device_get_softc(dev);
427	int		error = 0;
428
429	error = sysctl_handle_int(oidp, &sc->ibm_enable, 0, req);
430
431	if (error || !req->newptr)
432		return error;
433
434	if (sc->ibm_enable)
435		sc->ibm_enable = 1;
436	else
437		sc->ibm_enable = 0;
438	acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, sc->ibm_enable);
439
440	return 0;
441}
442
443static int
444sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS)
445{
446	device_t	dev = arg1;
447	struct acpi_ibm_softc *sc = device_get_softc(dev);
448	int		error = 0,	val, oldval, mask;
449	if (sc->oid_bluetooth == oidp) {
450		mask = 2;
451	} else if (sc->oid_wlan == oidp) {
452		mask = 4;
453	} else {
454		printf("WARNING: wrong handler invoked\n");
455		return ENOENT;
456	}
457
458	acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_WIRELESS, &oldval);
459	val = !((oldval & mask) == 0);
460	error = sysctl_handle_int(oidp, &val, 0, req);
461
462	if (error || !req->newptr)
463		return error;
464	oldval &= (~mask);
465	if (val)
466		oldval |= mask;
467	acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_SET_WIRELESS, oldval);
468	return 0;
469
470
471}
472