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