From 46f77ab436a73361e7d37f69bb77e6753b407a2f Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Tue, 6 Nov 2012 21:15:01 -0800 Subject: Imported Upstream version 2.7.3 --- src/apple.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 src/apple.c (limited to 'src/apple.c') diff --git a/src/apple.c b/src/apple.c new file mode 100644 index 0000000..8e00a84 --- /dev/null +++ b/src/apple.c @@ -0,0 +1,312 @@ +/* + * Copyright © 2011 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without + * fee, provided that the above copyright notice appear in all copies + * and that both that copyright notice and this permission notice + * appear in supporting documentation, and that the name of Red Hat + * not be used in advertising or publicity pertaining to distribution + * of the software without specific, written prior permission. Red + * Hat makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Authors: + * Peter Hutterer (peter.hutterer@redhat.com) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Apple-specific controls. + * + * On Apple keyboards, the multimedia function keys are overlaid with the F + * keys. F1 is also BrightnessDown, F10 is Mute, etc. The kernel provides a + * tweak to enable/disable this. + * + * /sys/module/hid_apple/parameters/fnmode + * 0 .. keyboard sends Fx keys, fn is disabled + * 1 .. keyboard sends multimedia keys, fn sends Fx keys + * 2 .. keyboard sends Fx keys, fn sends multimedia keys + * + * We only handle 1 and 2, don't care about 0. If fnmode is found to be on + * 0, we force it to 2 instead. + */ + +/* In this file: fkeymode refers to the evdev-specific enums and parameters, + * fnmode refers to the fnmode parameter exposed by the kernel. fnmode is + * apple-specific */ +#define FNMODE_PATH "/sys/module/hid_apple/parameters/fnmode" + +/* Taken from the kernel */ +#define USB_VENDOR_ID_APPLE 0x05ac +#define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI 0x021d +#define USB_DEVICE_ID_APPLE_ALU_MINI_ISO 0x021e +#define USB_DEVICE_ID_APPLE_ALU_MINI_JIS 0x021f +#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220 +#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221 +#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222 +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239 +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b + +static int EvdevAppleGetProperty (DeviceIntPtr dev, Atom property); +static int EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom, + XIPropertyValuePtr val, BOOL checkonly); + +static Atom prop_fkeymode; +static Bool fnmode_readonly; /* set if we can only read fnmode */ + +struct product_table +{ + unsigned int vendor; + unsigned int product; +} apple_keyboard_table[] = { + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS}, + { 0, 0} +}; + +/** + * @return TRUE if the device matches a product in the given product table, + * FALSE otherwise + */ +static Bool product_check(const struct product_table *t, int vendor, int product) +{ + while (t->vendor) + { + if (vendor == t->vendor && product == t->product) + return TRUE; + t++; + } + + return FALSE; +} + +/** + * @return 0 on success, -1 otherwise (check errno) + */ +static int +set_fnmode(enum fkeymode fkeymode) +{ + int fd; + char mode; + int bytes_written; + + if (fkeymode == FKEYMODE_UNKNOWN) + { + errno = EINVAL; /* silly you */ + return -1; + } + + fd = open(FNMODE_PATH, O_WRONLY); + if (fd < 0) + return -1; + + mode = (fkeymode == FKEYMODE_FKEYS) ? '2' : '1'; + + bytes_written = write(fd, &mode, 1); + close(fd); + + return (bytes_written == 1) ? 0 : -1; +} + +/** + * Get the current value of fnmode. If fnmode is found to be on 0, we set it + * to 2 in the process. Yes, quite daring, I know. I live on the edge. + * + * @return The current setting of fnmode or FKEYMODE_UNKNOWN on error (check + * errno) + */ +static enum fkeymode +get_fnmode(void) +{ + int fd; + char retvalue; + + fd = open(FNMODE_PATH, O_RDWR); + if (fd < 0 && errno == EACCES) + { + fnmode_readonly = TRUE; + fd = open(FNMODE_PATH, O_RDONLY); + } + + if (fd < 0) + goto err; + + if (read(fd, &retvalue, 1) != 1) + goto err; + + if (retvalue != '0' && retvalue != '1' && retvalue != '2') + { + xf86Msg(X_ERROR, "Invalid fnmode value: %c\n", retvalue); + errno = EINVAL; + goto err; + } + + close(fd); + + /* we don't want 0, switch to 2 */ + if (retvalue == '0') + { + if (fnmode_readonly) + xf86Msg(X_WARNING, "fnmode is disabled and read-only. Fn key will" + "not toggle to multimedia keys.\n"); + else + set_fnmode(FKEYMODE_FKEYS); + } + + + return retvalue == '1' ? FKEYMODE_MMKEYS : FKEYMODE_FKEYS; + +err: + if (fd >= 0) + close(fd); + return FKEYMODE_UNKNOWN; +} + +/** + * Set the property value to fkeymode. If the property doesn't exist, + * initialize it. + */ +static void set_fkeymode_property(InputInfoPtr pInfo, enum fkeymode fkeymode) +{ + DeviceIntPtr dev = pInfo->dev; + BOOL init = FALSE; + char data; + + switch(fkeymode) + { + case FKEYMODE_FKEYS: data = 0; break; + case FKEYMODE_MMKEYS: data = 1; break; + case FKEYMODE_UNKNOWN: + xf86IDrvMsg(pInfo, X_ERROR, "Failed to get fnmode (%s)\n", strerror(errno)); + return; + } + + if (!prop_fkeymode) { + init = TRUE; + prop_fkeymode = MakeAtom(EVDEV_PROP_FUNCTION_KEYS, strlen(EVDEV_PROP_FUNCTION_KEYS), TRUE); + } + + /* Don't send an event if we're initializing the property */ + XIChangeDeviceProperty(dev, prop_fkeymode, XA_INTEGER, 8, + PropModeReplace, 1, &data, !init); + + if (init) + { + XISetDevicePropertyDeletable(dev, prop_fkeymode, FALSE); + XIRegisterPropertyHandler(dev, EvdevAppleSetProperty, EvdevAppleGetProperty, NULL); + } +} + + +/** + * Called when a client reads the property state. + * Update with current kernel state, it may have changed behind our back. + */ +static int +EvdevAppleGetProperty (DeviceIntPtr dev, Atom property) +{ + if (property == prop_fkeymode) + { + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + enum fkeymode fkeymode; + + fkeymode = get_fnmode(); + if (fkeymode != pEvdev->fkeymode) { + /* set internal copy first, so we don't write to the file in + * SetProperty handler */ + pEvdev->fkeymode = fkeymode; + set_fkeymode_property(pInfo, fkeymode); + } + } + return Success; +} + +static int +EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom, + XIPropertyValuePtr val, BOOL checkonly) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + + if (atom == prop_fkeymode) + { + CARD8 v = *(CARD8*)val->data; + + if (val->format != 8 || val->type != XA_INTEGER) + return BadMatch; + + if (fnmode_readonly) + return BadAccess; + + if (v > 1) + return BadValue; + + if (!checkonly) + { + if ((!v && pEvdev->fkeymode != FKEYMODE_FKEYS) || + (v && pEvdev->fkeymode != FKEYMODE_MMKEYS)) + { + pEvdev->fkeymode = v ? FKEYMODE_MMKEYS : FKEYMODE_FKEYS; + set_fnmode(pEvdev->fkeymode); + } + } + } + + return Success; +} + +void +EvdevAppleInitProperty(DeviceIntPtr dev) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + EvdevPtr pEvdev = pInfo->private; + enum fkeymode fkeymode; + + if (!product_check(apple_keyboard_table, + pEvdev->id_vendor, pEvdev->id_product)) + return; + + fkeymode = get_fnmode(); + pEvdev->fkeymode = fkeymode; + set_fkeymode_property(pInfo, fkeymode); +} -- cgit v1.2.3