Deleted Added
sdiff udiff text old ( 144623 ) new ( 150003 )
full compact
1/*-
2 * Copyright (c) 2002 Sean Bullington <seanATstalker.org>
3 * 2003-2005 Anish Mistry <amistry@am-productions.biz>
4 * 2004 Mark Santcroos <marks@ripe.net>
5 * All Rights Reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_fujitsu.c 144623 2005-04-04 10:21:20Z philip $");
32
33#include "opt_acpi.h"
34#include <sys/param.h>
35#include <sys/kernel.h>
36#include <sys/bus.h>
37#include <sys/module.h>
38#include <sys/sysctl.h>
39
40#include "acpi.h"
41#include <dev/acpica/acpivar.h>
42
43/* Hooks for the ACPI CA debugging infrastructure */
44#define _COMPONENT ACPI_OEM
45ACPI_MODULE_NAME("Fujitsu")
46
47/* Change and update bits for the buttons */
48#define MOUSE_UPDATED_BIT 0x80000000
49#define VOLUME_MUTE_BIT 0x40000000
50#define VOLUME_CHANGE_BIT 0x80000000
51#define BRIGHTNESS_CHANGE_BIT 0x80000000
52
53/* Values of settings */
54#define GENERAL_SETTING_BITS 0x0fffffff
55#define MOUSE_SETTING_BITS GENERAL_SETTING_BITS
56#define VOLUME_SETTING_BITS GENERAL_SETTING_BITS
57#define BRIGHTNESS_SETTING_BITS GENERAL_SETTING_BITS
58
59/* Possible state changes */
60#define VOLUME_CHANGED 1
61#define BRIGHT_CHANGED 2
62#define MOUSE_CHANGED 3
63
64/* sysctl values */
65#define FN_MUTE 0
66#define FN_POINTER_ENABLE 1
67#define FN_LCD_BRIGHTNESS 2
68#define FN_VOLUME 3
69
70/* Methods */
71#define METHOD_GBLL 1
72#define METHOD_GMOU 2
73#define METHOD_GVOL 3
74#define METHOD_MUTE 4
75
76/* Notify event */
77#define ACPI_NOTIFY_STATUS_CHANGED 0x80
78
79/*
80 * Holds a control method name and its associated integer value.
81 * Only used for no-argument control methods which return a value.
82 */
83struct int_nameval {
84 char *name;
85 int value;
86};
87
88/*
89 * Driver extension for the FUJITSU ACPI driver.
90 */
91struct acpi_fujitsu_softc {
92 device_t dev;
93 ACPI_HANDLE handle;
94
95 /* Control methods */
96 struct int_nameval _sta, /* unused */
97 gbll, /* brightness */
98 ghks, /* unused */
99 gmou, /* mouse */
100 gsif, /* unused */
101 gvol, /* volume */
102 rbll, /* unused */
103 rvol; /* unused */
104
105 /* State variables */
106 uint8_t bIsMuted; /* Is volume muted */
107 uint8_t bIntPtrEnabled; /* Is internal ptr enabled */
108 uint32_t lastValChanged; /* The last value updated */
109
110 /* sysctl tree */
111 struct sysctl_ctx_list sysctl_ctx;
112 struct sysctl_oid *sysctl_tree;
113};
114
115/* Driver entry point forward declarations. */
116static int acpi_fujitsu_probe(device_t dev);
117static int acpi_fujitsu_attach(device_t dev);
118static int acpi_fujitsu_detach(device_t dev);
119static int acpi_fujitsu_suspend(device_t dev);
120static int acpi_fujitsu_resume(device_t dev);
121
122static void acpi_fujitsu_notify_status_changed(void *arg);
123static void acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context);
124static int acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS);
125
126/* Utility function declarations */
127static uint8_t acpi_fujitsu_update(struct acpi_fujitsu_softc *sc);
128static uint8_t acpi_fujitsu_init(struct acpi_fujitsu_softc *sc);
129
130/* Driver/Module specific structure definitions. */
131static device_method_t acpi_fujitsu_methods[] = {
132 /* Device interface */
133 DEVMETHOD(device_probe, acpi_fujitsu_probe),
134 DEVMETHOD(device_attach, acpi_fujitsu_attach),
135 DEVMETHOD(device_detach, acpi_fujitsu_detach),
136 DEVMETHOD(device_suspend, acpi_fujitsu_suspend),
137 DEVMETHOD(device_resume, acpi_fujitsu_resume),
138 {0, 0}
139};
140
141static driver_t acpi_fujitsu_driver = {
142 "acpi_fujitsu",
143 acpi_fujitsu_methods,
144 sizeof(struct acpi_fujitsu_softc),
145};
146
147/* Prototype for function buttons for getting/setting a value. */
148static int acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method);
149static int acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value);
150
151static char *fujitsu_ids[] = { "FUJ02B1", NULL };
152
153ACPI_SERIAL_DECL(fujitsu, "Fujitsu Function Buttons");
154
155/* sysctl names and function calls */
156static struct {
157 char *name;
158 int method;
159 char *description;
160} sysctl_table[] = {
161 {
162 .name = "mute",
163 .method = METHOD_MUTE,
164 .description = "Speakers/headphones mute status"
165 },
166 {
167 .name = "pointer_enable",
168 .method = METHOD_GMOU,
169 .description = "Enable and disable the internal pointer"
170 },
171 {
172 .name = "lcd_brightness",
173 .method = METHOD_GBLL,
174 .description = "Brightness level of the LCD panel"
175 },
176 {
177 .name = "volume",
178 .method = METHOD_GVOL,
179 .description = "Speakers/headphones volume level"
180 },
181
182 { NULL, 0, NULL }
183};
184
185static devclass_t acpi_fujitsu_devclass;
186DRIVER_MODULE(acpi_fujitsu, acpi, acpi_fujitsu_driver,
187 acpi_fujitsu_devclass, 0, 0);
188MODULE_DEPEND(acpi_fujitsu, acpi, 1, 1, 1);
189MODULE_VERSION(acpi_fujitsu, 1);
190
191static int
192acpi_fujitsu_probe(device_t dev)
193{
194
195 if (acpi_disabled("fujitsu") ||
196 ACPI_ID_PROBE(device_get_parent(dev), dev, fujitsu_ids) == NULL ||
197 device_get_unit(dev) != 0)
198 return (ENXIO);
199
200 device_set_desc(dev, "Fujitsu Function Buttons");
201
202 return (0);
203}
204
205static int
206acpi_fujitsu_attach(device_t dev)
207{
208 struct acpi_fujitsu_softc *sc;
209
210 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
211
212 sc = device_get_softc(dev);
213 sc->dev = dev;
214 sc->handle = acpi_get_handle(dev);
215
216 /* Install notification handler */
217 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
218 acpi_fujitsu_notify_handler, sc);
219
220 /* Snag our default values for the buttons / button states. */
221 ACPI_SERIAL_BEGIN(fujitsu);
222 if (!acpi_fujitsu_init(sc))
223 device_printf(dev, "Couldn't initialize button states!\n");
224 ACPI_SERIAL_END(fujitsu);
225
226 return (0);
227}
228
229/*
230 * Called when the system is being suspended, simply
231 * set an event to be signalled when we wake up.
232 */
233static int
234acpi_fujitsu_suspend(device_t dev)
235{
236
237 return (0);
238}
239
240static int
241acpi_fujitsu_resume(device_t dev)
242{
243 struct acpi_fujitsu_softc *sc;
244 ACPI_STATUS status;
245
246 sc = device_get_softc(dev);
247
248 /*
249 * The pointer needs to be re-enabled for
250 * some revisions of the P series (2120).
251 */
252 ACPI_SERIAL_BEGIN(fujitsu);
253
254 status = acpi_SetInteger(sc->handle, "SMOU", 1);
255 if (ACPI_FAILURE(status))
256 device_printf(sc->dev, "Couldn't enable pointer\n");
257
258 ACPI_SERIAL_END(fujitsu);
259
260 return (0);
261}
262
263static void
264acpi_fujitsu_notify_status_changed(void *arg)
265{
266 struct acpi_fujitsu_softc *sc;
267
268 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
269
270 sc = (struct acpi_fujitsu_softc *)arg;
271
272 /*
273 * Since our notify function is called, we know something has
274 * happened. So the only reason for acpi_fujitsu_update to fail
275 * is if we can't find what has changed or an error occurs.
276 */
277 ACPI_SERIAL_BEGIN(fujitsu);
278 acpi_fujitsu_update(sc);
279 ACPI_SERIAL_END(fujitsu);
280}
281
282
283static void
284acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context)
285{
286 struct acpi_fujitsu_softc *sc;
287
288 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
289
290 sc = (struct acpi_fujitsu_softc *)context;
291
292 switch (notify) {
293 case ACPI_NOTIFY_STATUS_CHANGED:
294 AcpiOsQueueForExecution(OSD_PRIORITY_LO,
295 acpi_fujitsu_notify_status_changed, sc);
296 break;
297 default:
298 /* unknown notification value */
299 break;
300 }
301}
302
303static int
304acpi_fujitsu_detach(device_t dev)
305{
306 struct acpi_fujitsu_softc *sc;
307
308 sc = device_get_softc(dev);
309 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
310 acpi_fujitsu_notify_handler);
311
312 sysctl_ctx_free(&sc->sysctl_ctx);
313
314 return (0);
315}
316
317/*
318 * Initializes the names of the ACPI control methods and grabs
319 * the current state of all of the ACPI buttons into the softc.
320 */
321static uint8_t
322acpi_fujitsu_init(struct acpi_fujitsu_softc *sc)
323{
324 struct acpi_softc *acpi_sc;
325 int i;
326
327 ACPI_SERIAL_ASSERT(fujitsu);
328
329 /* Setup all of the names for each control method */
330 sc->_sta.name = "_STA";
331 sc->gbll.name = "GBLL";
332 sc->ghks.name = "GHKS";
333 sc->gmou.name = "GMOU";
334 sc->gsif.name = "GSIF";
335 sc->gvol.name = "GVOL";
336 sc->rbll.name = "RBLL";
337 sc->rvol.name = "RVOL";
338
339 /* Build the sysctl tree */
340 acpi_sc = acpi_device_get_parent_softc(sc->dev);
341 sysctl_ctx_init(&sc->sysctl_ctx);
342 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
343 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
344 OID_AUTO, "fujitsu", CTLFLAG_RD, 0, "");
345
346 for (i = 0; sysctl_table[i].name != NULL; i++) {
347 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
348 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
349 sysctl_table[i].name,
350 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
351 sc, i, acpi_fujitsu_sysctl, "I",
352 sysctl_table[i].description);
353 }
354
355 /* Set the buttons to their initial states */
356 if (!acpi_fujitsu_update(sc)) {
357 device_printf(sc->dev, "Couldn't init button states\n");
358 return (FALSE);
359 }
360
361 return (TRUE);
362}
363
364static int
365acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS)
366{
367 struct acpi_fujitsu_softc *sc;
368 int method;
369 int arg;
370 int function_num, error = 0;
371
372 sc = (struct acpi_fujitsu_softc *)oidp->oid_arg1;
373 function_num = oidp->oid_arg2;
374 method = sysctl_table[function_num].method;
375
376 ACPI_SERIAL_BEGIN(fujitsu);
377
378 /* Get the current value */
379 arg = acpi_fujitsu_method_get(sc, method);
380 error = sysctl_handle_int(oidp, &arg, 0, req);
381
382 if (error != 0 || req->newptr == NULL)
383 goto out;
384
385 /* Update the value */
386 error = acpi_fujitsu_method_set(sc, method, arg);
387
388out:
389 ACPI_SERIAL_END(fujitsu);
390 return (error);
391}
392
393static int
394acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method)
395{
396 struct int_nameval nv;
397 ACPI_STATUS status;
398
399 ACPI_SERIAL_ASSERT(fujitsu);
400
401 switch (method) {
402 case METHOD_GBLL:
403 nv = sc->gbll;
404 break;
405 case METHOD_GMOU:
406 nv = sc->gmou;
407 break;
408 case METHOD_GVOL:
409 case METHOD_MUTE:
410 nv = sc->gvol;
411 break;
412 default:
413 return (FALSE);
414 }
415
416 status = acpi_GetInteger(sc->handle, nv.name, &nv.value);
417 if (ACPI_FAILURE(status)) {
418 device_printf(sc->dev, "Couldn't query method\n");
419 return (FALSE);
420 }
421
422 if (method == METHOD_MUTE) {
423 sc->bIsMuted = (uint8_t)((nv.value & VOLUME_MUTE_BIT) != 0);
424 return (sc->bIsMuted);
425 }
426
427 nv.value &= GENERAL_SETTING_BITS;
428 return (nv.value);
429}
430
431static int
432acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value)
433{
434 struct int_nameval nv;
435 ACPI_STATUS status;
436 char *control;
437 int changed;
438
439 ACPI_SERIAL_ASSERT(fujitsu);
440
441 switch (method) {
442 case METHOD_GBLL:
443 changed = BRIGHT_CHANGED;
444 control = "SBLL";
445 nv = sc->gbll;
446 break;
447 case METHOD_GMOU:
448 changed = MOUSE_CHANGED;
449 control = "SMOU";
450 nv = sc->gmou;
451 break;
452 case METHOD_GVOL:
453 case METHOD_MUTE:
454 changed = VOLUME_CHANGED;
455 control = "SVOL";
456 nv = sc->gvol;
457 break;
458 default:
459 return (EINVAL);
460 }
461
462 if (method == METHOD_MUTE) {
463 if (value == 1)
464 value = nv.value | VOLUME_MUTE_BIT;
465 else if (value == 0)
466 value = nv.value & ~VOLUME_MUTE_BIT;
467 else
468 return (EINVAL);
469 }
470
471 status = acpi_SetInteger(sc->handle, control, value);
472 if (ACPI_FAILURE(status)) {
473 device_printf(sc->dev, "Couldn't update %s\n", control);
474 return (EINVAL);
475 }
476
477 sc->lastValChanged = changed;
478 return (0);
479}
480
481/*
482 * Query each of the ACPI control methods that contain information we're
483 * interested in. We check the return values from the control methods and
484 * adjust any state variables if they should be adjusted.
485 */
486static uint8_t
487acpi_fujitsu_update(struct acpi_fujitsu_softc *sc)
488{
489 struct acpi_softc *acpi_sc;
490
491 acpi_sc = acpi_device_get_parent_softc(sc->dev);
492
493 ACPI_SERIAL_ASSERT(fujitsu);
494
495 /* System Volume Level */
496 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
497 sc->gvol.name, &(sc->gvol.value)))) {
498 device_printf(sc->dev, "Couldn't query volume level\n");
499 return (FALSE);
500 }
501
502 if (sc->gvol.value & VOLUME_CHANGE_BIT) {
503 sc->bIsMuted =
504 (uint8_t)((sc->gvol.value & VOLUME_MUTE_BIT) != 0);
505
506 /* Clear the modification bit */
507 sc->gvol.value &= VOLUME_SETTING_BITS;
508
509 if (sc->bIsMuted) {
510 acpi_UserNotify("FUJITSU", sc->handle, FN_MUTE);
511 ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now mute\n");
512 } else
513 ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now %d\n",
514 sc->gvol.value);
515
516 acpi_UserNotify("FUJITSU", sc->handle, FN_VOLUME);
517
518 sc->lastValChanged = VOLUME_CHANGED;
519 }
520
521 /* Internal mouse pointer (eraserhead) */
522 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
523 sc->gmou.name, &(sc->gmou.value)))) {
524 device_printf(sc->dev, "Couldn't query pointer state\n");
525 return (FALSE);
526 }
527
528 if (sc->gmou.value & MOUSE_UPDATED_BIT) {
529 sc->bIntPtrEnabled = (uint8_t)(sc->gmou.value & 0x1);
530
531 /* Clear the modification bit */
532 sc->gmou.value &= MOUSE_SETTING_BITS;
533
534 acpi_UserNotify("FUJITSU", sc->handle, FN_POINTER_ENABLE);
535
536 ACPI_VPRINT(sc->dev, acpi_sc, "Internal pointer is now %s\n",
537 (sc->bIntPtrEnabled) ? "enabled" : "disabled");
538
539 sc->lastValChanged = MOUSE_CHANGED;
540 }
541
542 /* Screen Brightness Level */
543 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
544 sc->gbll.name, &(sc->gbll.value)))) {
545 device_printf(sc->dev, "Couldn't query brightness level\n");
546 return (FALSE);
547 }
548
549 if (sc->gbll.value & BRIGHTNESS_CHANGE_BIT) {
550 /* No state to record here. */
551
552 /* Clear the modification bit */
553 sc->gbll.value &= BRIGHTNESS_SETTING_BITS;
554
555 acpi_UserNotify("FUJITSU", sc->handle, FN_LCD_BRIGHTNESS);
556
557 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness level is now %d\n",
558 sc->gbll.value);
559
560 sc->lastValChanged = BRIGHT_CHANGED;
561 }
562
563 return (TRUE);
564}