diff options
author | nwhitehorn <nwhitehorn@FreeBSD.org> | 2011-10-16 21:01:42 +0000 |
---|---|---|
committer | nwhitehorn <nwhitehorn@FreeBSD.org> | 2011-10-16 21:01:42 +0000 |
commit | 2cd40fe2b68029b4e3872631ccfea41808316a14 (patch) | |
tree | e65d2a846c963441e5e1987ba540b0559eac3585 /sys/dev/adb | |
parent | 1cfa7840f9aabdfd296ee5486a4b3ac60b2a09a0 (diff) | |
download | FreeBSD-src-2cd40fe2b68029b4e3872631ccfea41808316a14.zip FreeBSD-src-2cd40fe2b68029b4e3872631ccfea41808316a14.tar.gz |
Add support for special keys (volume/brightness/eject) on Apple laptops with
ADB keyboards.
Submitted by: Justin Hibbits <jrh29 at alumni dot cwru dot edu>
MFC after: 9.0-RELEASE
Diffstat (limited to 'sys/dev/adb')
-rw-r--r-- | sys/dev/adb/adb_buttons.c | 165 | ||||
-rw-r--r-- | sys/dev/adb/adb_kbd.c | 72 |
2 files changed, 236 insertions, 1 deletions
diff --git a/sys/dev/adb/adb_buttons.c b/sys/dev/adb/adb_buttons.c new file mode 100644 index 0000000..92cdda1 --- /dev/null +++ b/sys/dev/adb/adb_buttons.c @@ -0,0 +1,165 @@ +/*- + * Copyright (c) 2011, Justin Hibbits. + * Copyright (c) 2002, Miodrag Vallat. + * Copyright (C) 1999 Tsubai Masanari. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * OpenBSD: abtn.c,v 1.12 2009/01/10 18:00:59 robert Exp + * NetBSD: abtn.c,v 1.1 1999/07/12 17:48:26 tsubai Exp + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/kernel.h> +#include <sys/bus.h> + +#include <machine/bus.h> + +#include <dev/ofw/openfirm.h> +#include <machine/ofw_machdep.h> + +#include <dev/adb/adb.h> + +#define ABTN_HANDLER_ID 31 + +struct abtn_softc { + device_t sc_dev; + + int handler_id; +}; + +static int abtn_probe(device_t dev); +static int abtn_attach(device_t dev); +static u_int abtn_receive_packet(device_t dev, u_char status, + u_char command, u_char reg, int len, u_char *data); + +static device_method_t abtn_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, abtn_probe), + DEVMETHOD(device_attach, abtn_attach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* ADB interface */ + DEVMETHOD(adb_receive_packet, abtn_receive_packet), + + { 0, 0 } +}; + +static driver_t abtn_driver = { + "abtn", + abtn_methods, + sizeof(struct abtn_softc), +}; + +static devclass_t abtn_devclass; + +DRIVER_MODULE(abtn, adb, abtn_driver, abtn_devclass, 0, 0); + +static int +abtn_probe(device_t dev) +{ + uint8_t type; + + type = adb_get_device_type(dev); + + if (type != ADB_DEVICE_MISC) + return (ENXIO); + + device_set_desc(dev, "ADB Brightness/Volume/Eject Buttons"); + return (0); +} + +static int +abtn_attach(device_t dev) +{ + struct abtn_softc *sc; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->handler_id = adb_get_device_handler(dev); + + return 0; +} + +static u_int +abtn_receive_packet(device_t dev, u_char status, + u_char command, u_char reg, int len, u_char *data) +{ + u_int cmd; + + cmd = data[0]; + + switch (cmd) { + case 0x0a: /* decrease brightness */ + if (devctl_process_running()) + devctl_notify("PMU", "keys", "brightness", + "notify=down"); + break; + + case 0x09: /* increase brightness */ + if (devctl_process_running()) + devctl_notify("PMU", "keys", "brightness", "notify=up"); + break; + + case 0x08: /* mute */ + case 0x01: /* mute, AV hardware */ + if (devctl_process_running()) + devctl_notify("PMU", "keys", "mute", NULL); + break; + case 0x07: /* decrease volume */ + case 0x02: /* decrease volume, AV hardware */ + if (devctl_process_running()) + devctl_notify("PMU", "keys", "volume", "notify=down"); + break; + case 0x06: /* increase volume */ + case 0x03: /* increase volume, AV hardware */ + if (devctl_process_running()) + devctl_notify("PMU", "keys", "volume", "notify=up"); + break; + case 0x0c: /* mirror display key */ + /* Need callback to do something with this */ + break; + case 0x0b: /* eject tray */ + if (devctl_process_running()) + devctl_notify("PMU", "keys", "eject", NULL); + case 0x7f: /* numlock */ + /* Need callback to do something with this */ + break; + + default: +#ifdef DEBUG + if ((cmd & ~0x7f) == 0) + device_printf(dev, "unknown ADB button 0x%x\n", cmd); +#endif + break; + } + return 0; +} + diff --git a/sys/dev/adb/adb_kbd.c b/sys/dev/adb/adb_kbd.c index e66b5b2..e69c4db 100644 --- a/sys/dev/adb/adb_kbd.c +++ b/sys/dev/adb/adb_kbd.c @@ -35,12 +35,15 @@ #include <sys/condvar.h> #include <sys/callout.h> #include <sys/kernel.h> +#include <sys/sysctl.h> #include <machine/bus.h> #include "opt_kbd.h" #include <dev/kbd/kbdreg.h> #include <dev/kbd/kbdtables.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> #include <vm/vm.h> #include <vm/pmap.h> @@ -55,6 +58,7 @@ static int adb_kbd_probe(device_t dev); static int adb_kbd_attach(device_t dev); static int adb_kbd_detach(device_t dev); static void akbd_repeat(void *xsc); +static int adb_fn_keys(SYSCTL_HANDLER_ARGS); static u_int adb_kbd_receive_packet(device_t dev, u_char status, u_char command, u_char reg, int len, u_char *data); @@ -282,6 +286,8 @@ adb_kbd_attach(device_t dev) { struct adb_kbd_softc *sc; keyboard_switch_t *sw; + uint32_t fkeys; + phandle_t handle; sw = kbd_get_switch(KBD_DRIVER_NAME); if (sw == NULL) { @@ -333,6 +339,40 @@ adb_kbd_attach(device_t dev) adb_set_autopoll(dev,1); + handle = OF_finddevice("mac-io/via-pmu/adb/keyboard"); + if (handle != -1 && OF_getprop(handle, "AAPL,has-embedded-fn-keys", + &fkeys, sizeof(fkeys)) != -1) { + static const char *key_names[] = {"F1", "F2", "F3", "F4", "F5", + "F6", "F7", "F8", "F9", "F10", "F11", "F12"}; + struct sysctl_ctx_list *ctx; + struct sysctl_oid *tree; + int i; + + if (bootverbose) + device_printf(dev, "Keyboard has embedded Fn keys\n"); + + for (i = 0; i < 12; i++) { + uint32_t keyval; + char buf[3]; + if (OF_getprop(handle, key_names[i], &keyval, + sizeof(keyval)) < 0) + continue; + buf[0] = 1; + buf[1] = i+1; + buf[2] = keyval; + adb_write_register(dev, 0, 3, buf); + } + adb_write_register(dev, 1, 2, &(uint16_t){0}); + + ctx = device_get_sysctl_ctx(dev); + tree = device_get_sysctl_tree(dev); + + SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "fn_keys_function_as_primary", CTLTYPE_INT | CTLFLAG_RW, sc, + 0, adb_fn_keys, "I", + "Set the Fn keys to be their F-key type as default"); + } + return (0); } @@ -383,6 +423,13 @@ adb_kbd_receive_packet(device_t dev, u_char status, return (0); mtx_lock(&sc->sc_mutex); + /* 0x7f is always the power button */ + if (data[0] == 0x7f && devctl_process_running()) { + devctl_notify("PMU", "Button", "pressed", NULL); + return (0); + } else if (data[0] == 0xff) { + return (0); /* Ignore power button release. */ + } if ((data[0] & 0x7f) == 57 && sc->buffers < 7) { /* Fake the down/up cycle for caps lock */ sc->buffer[sc->buffers++] = data[0] & 0x7f; @@ -390,7 +437,6 @@ adb_kbd_receive_packet(device_t dev, u_char status, } else { sc->buffer[sc->buffers++] = data[0]; } - if (sc->buffer[sc->buffers-1] < 0xff) sc->last_press = sc->buffer[sc->buffers-1]; @@ -814,5 +860,29 @@ akbd_modevent(module_t mod, int type, void *data) return (0); } +static int +adb_fn_keys(SYSCTL_HANDLER_ARGS) +{ + struct adb_kbd_softc *sc = arg1; + int error; + uint16_t is_fn_enabled; + unsigned int is_fn_enabled_sysctl; + + adb_read_register(sc->sc_dev, 1, &is_fn_enabled); + is_fn_enabled &= 1; + is_fn_enabled_sysctl = is_fn_enabled; + error = sysctl_handle_int(oidp, &is_fn_enabled_sysctl, 0, req); + + if (error || !req->newptr) + return (error); + + is_fn_enabled = is_fn_enabled_sysctl; + if (is_fn_enabled != 1 && is_fn_enabled != 0) + return (EINVAL); + + adb_write_register(sc->sc_dev, 1, 2, &is_fn_enabled); + return (0); +} + DEV_MODULE(akbd, akbd_modevent, NULL); |