2 * Copyright © 2011 Red Hat, Inc.
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of Red Hat
9 * not be used in advertising or publicity pertaining to distribution
10 * of the software without specific, written prior permission. Red
11 * Hat makes no representations about the suitability of this software
12 * for any purpose. It is provided "as is" without express or implied
15 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Peter Hutterer (peter.hutterer@redhat.com)
32 #include <evdev-properties.h>
34 #include <sys/types.h>
41 #include <xf86Xinput.h>
42 #include <X11/Xatom.h>
44 /* Apple-specific controls.
46 * On Apple keyboards, the multimedia function keys are overlaid with the F
47 * keys. F1 is also BrightnessDown, F10 is Mute, etc. The kernel provides a
48 * tweak to enable/disable this.
50 * /sys/module/hid_apple/parameters/fnmode
51 * 0 .. keyboard sends Fx keys, fn is disabled
52 * 1 .. keyboard sends multimedia keys, fn sends Fx keys
53 * 2 .. keyboard sends Fx keys, fn sends multimedia keys
55 * We only handle 1 and 2, don't care about 0. If fnmode is found to be on
56 * 0, we force it to 2 instead.
59 /* In this file: fkeymode refers to the evdev-specific enums and parameters,
60 * fnmode refers to the fnmode parameter exposed by the kernel. fnmode is
62 #define FNMODE_PATH "/sys/module/hid_apple/parameters/fnmode"
64 /* Taken from the kernel */
65 #define USB_VENDOR_ID_APPLE 0x05ac
66 #define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI 0x021d
67 #define USB_DEVICE_ID_APPLE_ALU_MINI_ISO 0x021e
68 #define USB_DEVICE_ID_APPLE_ALU_MINI_JIS 0x021f
69 #define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220
70 #define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221
71 #define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222
72 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c
73 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d
74 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e
75 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI 0x0239
76 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO 0x023a
77 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
79 static int EvdevAppleGetProperty (DeviceIntPtr dev, Atom property);
80 static int EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom,
81 XIPropertyValuePtr val, BOOL checkonly);
83 static Atom prop_fkeymode;
84 static Bool fnmode_readonly; /* set if we can only read fnmode */
90 } apple_keyboard_table[] = {
91 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI},
92 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO},
93 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS},
94 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI},
95 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO},
96 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS},
97 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI},
98 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO},
99 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS},
100 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI},
101 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO},
102 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS},
107 * @return TRUE if the device matches a product in the given product table,
110 static Bool product_check(const struct product_table *t, int vendor, int product)
114 if (vendor == t->vendor && product == t->product)
123 * @return 0 on success, -1 otherwise (check errno)
126 set_fnmode(enum fkeymode fkeymode)
132 if (fkeymode == FKEYMODE_UNKNOWN)
134 errno = EINVAL; /* silly you */
138 fd = open(FNMODE_PATH, O_WRONLY);
142 mode = (fkeymode == FKEYMODE_FKEYS) ? '2' : '1';
144 bytes_written = write(fd, &mode, 1);
147 return (bytes_written == 1) ? 0 : -1;
151 * Get the current value of fnmode. If fnmode is found to be on 0, we set it
152 * to 2 in the process. Yes, quite daring, I know. I live on the edge.
154 * @return The current setting of fnmode or FKEYMODE_UNKNOWN on error (check
163 fd = open(FNMODE_PATH, O_RDWR);
164 if (fd < 0 && errno == EACCES)
166 fnmode_readonly = TRUE;
167 fd = open(FNMODE_PATH, O_RDONLY);
173 if (read(fd, &retvalue, 1) != 1)
176 if (retvalue != '0' && retvalue != '1' && retvalue != '2')
178 xf86Msg(X_ERROR, "Invalid fnmode value: %c\n", retvalue);
185 /* we don't want 0, switch to 2 */
189 xf86Msg(X_WARNING, "fnmode is disabled and read-only. Fn key will"
190 "not toggle to multimedia keys.\n");
192 set_fnmode(FKEYMODE_FKEYS);
196 return retvalue == '1' ? FKEYMODE_MMKEYS : FKEYMODE_FKEYS;
201 return FKEYMODE_UNKNOWN;
205 * Set the property value to fkeymode. If the property doesn't exist,
208 static void set_fkeymode_property(InputInfoPtr pInfo, enum fkeymode fkeymode)
210 DeviceIntPtr dev = pInfo->dev;
216 case FKEYMODE_FKEYS: data = 0; break;
217 case FKEYMODE_MMKEYS: data = 1; break;
218 case FKEYMODE_UNKNOWN:
219 xf86IDrvMsg(pInfo, X_ERROR, "Failed to get fnmode (%s)\n", strerror(errno));
223 if (!prop_fkeymode) {
225 prop_fkeymode = MakeAtom(EVDEV_PROP_FUNCTION_KEYS, strlen(EVDEV_PROP_FUNCTION_KEYS), TRUE);
228 /* Don't send an event if we're initializing the property */
229 XIChangeDeviceProperty(dev, prop_fkeymode, XA_INTEGER, 8,
230 PropModeReplace, 1, &data, !init);
234 XISetDevicePropertyDeletable(dev, prop_fkeymode, FALSE);
235 XIRegisterPropertyHandler(dev, EvdevAppleSetProperty, EvdevAppleGetProperty, NULL);
241 * Called when a client reads the property state.
242 * Update with current kernel state, it may have changed behind our back.
245 EvdevAppleGetProperty (DeviceIntPtr dev, Atom property)
247 if (property == prop_fkeymode)
249 InputInfoPtr pInfo = dev->public.devicePrivate;
250 EvdevPtr pEvdev = pInfo->private;
251 enum fkeymode fkeymode;
253 fkeymode = get_fnmode();
254 if (fkeymode != pEvdev->fkeymode) {
255 /* set internal copy first, so we don't write to the file in
256 * SetProperty handler */
257 pEvdev->fkeymode = fkeymode;
258 set_fkeymode_property(pInfo, fkeymode);
265 EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom,
266 XIPropertyValuePtr val, BOOL checkonly)
268 InputInfoPtr pInfo = dev->public.devicePrivate;
269 EvdevPtr pEvdev = pInfo->private;
271 if (atom == prop_fkeymode)
273 CARD8 v = *(CARD8*)val->data;
275 if (val->format != 8 || val->type != XA_INTEGER)
286 if ((!v && pEvdev->fkeymode != FKEYMODE_FKEYS) ||
287 (v && pEvdev->fkeymode != FKEYMODE_MMKEYS))
289 pEvdev->fkeymode = v ? FKEYMODE_MMKEYS : FKEYMODE_FKEYS;
290 set_fnmode(pEvdev->fkeymode);
299 EvdevAppleInitProperty(DeviceIntPtr dev)
301 InputInfoPtr pInfo = dev->public.devicePrivate;
302 EvdevPtr pEvdev = pInfo->private;
303 enum fkeymode fkeymode;
305 if (!product_check(apple_keyboard_table,
306 pEvdev->id_vendor, pEvdev->id_product))
309 fkeymode = get_fnmode();
310 pEvdev->fkeymode = fkeymode;
311 set_fkeymode_property(pInfo, fkeymode);