Initialize Tizen 2.3
[adaptation/xorg/driver/xserver-xorg-input-evdev.git] / src / apple.c
1 /*
2  * Copyright © 2011 Red Hat, Inc.
3  *
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
13  * warranty.
14  *
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.
22  *
23  * Authors:
24  *      Peter Hutterer (peter.hutterer@redhat.com)
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <evdev.h>
32 #include <evdev-properties.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38
39 #include <exevents.h>
40 #include <xf86.h>
41 #include <xf86Xinput.h>
42 #include <X11/Xatom.h>
43
44 /* Apple-specific controls.
45  *
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.
49  *
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
54  *
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.
57  */
58
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
61  * apple-specific */
62 #define FNMODE_PATH "/sys/module/hid_apple/parameters/fnmode"
63
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
78
79 static int EvdevAppleGetProperty (DeviceIntPtr dev, Atom property);
80 static int EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom,
81                       XIPropertyValuePtr val, BOOL checkonly);
82
83 static Atom prop_fkeymode;
84 static Bool fnmode_readonly; /* set if we can only read fnmode */
85
86 struct product_table
87 {
88     unsigned int vendor;
89     unsigned int product;
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},
103     { 0, 0}
104 };
105
106 /**
107  * @return TRUE if the device matches a product in the given product table,
108  *         FALSE otherwise
109  */
110 static Bool product_check(const struct product_table *t, int vendor, int product)
111 {
112     while (t->vendor)
113     {
114         if (vendor == t->vendor && product == t->product)
115             return TRUE;
116         t++;
117     }
118
119     return FALSE;
120 }
121
122 /**
123  * @return 0 on success, -1 otherwise (check errno)
124  */
125 static int
126 set_fnmode(enum fkeymode fkeymode)
127 {
128     int fd;
129     char mode;
130     int bytes_written;
131
132     if (fkeymode == FKEYMODE_UNKNOWN)
133     {
134         errno = EINVAL; /* silly you */
135         return -1;
136     }
137
138     fd = open(FNMODE_PATH, O_WRONLY);
139     if (fd < 0)
140         return -1;
141
142     mode = (fkeymode == FKEYMODE_FKEYS) ? '2' : '1';
143
144     bytes_written = write(fd, &mode, 1);
145     close(fd);
146
147     return (bytes_written == 1) ? 0 : -1;
148 }
149
150 /**
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.
153  *
154  * @return The current setting of fnmode or FKEYMODE_UNKNOWN on error (check
155  * errno)
156  */
157 static enum fkeymode
158 get_fnmode(void)
159 {
160     int fd;
161     char retvalue;
162
163     fd = open(FNMODE_PATH, O_RDWR);
164     if (fd < 0 && errno == EACCES)
165     {
166         fnmode_readonly = TRUE;
167         fd = open(FNMODE_PATH, O_RDONLY);
168     }
169
170     if (fd < 0)
171         goto err;
172
173     if (read(fd, &retvalue, 1) != 1)
174         goto err;
175
176     if (retvalue != '0' && retvalue != '1' && retvalue != '2')
177     {
178         xf86Msg(X_ERROR, "Invalid fnmode value: %c\n", retvalue);
179         errno = EINVAL;
180         goto err;
181     }
182
183     close(fd);
184
185     /* we don't want 0, switch to 2 */
186     if (retvalue == '0')
187     {
188         if (fnmode_readonly)
189             xf86Msg(X_WARNING, "fnmode is disabled and read-only. Fn key will"
190                     "not toggle to multimedia keys.\n");
191         else
192             set_fnmode(FKEYMODE_FKEYS);
193     }
194
195
196     return retvalue == '1' ? FKEYMODE_MMKEYS : FKEYMODE_FKEYS;
197
198 err:
199     if (fd >= 0)
200         close(fd);
201     return FKEYMODE_UNKNOWN;
202 }
203
204 /**
205  * Set the property value to fkeymode. If the property doesn't exist,
206  * initialize it.
207  */
208 static void set_fkeymode_property(InputInfoPtr pInfo, enum fkeymode fkeymode)
209 {
210     DeviceIntPtr dev = pInfo->dev;
211     BOOL init = FALSE;
212     char data;
213
214     switch(fkeymode)
215     {
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));
220             return;
221     }
222
223     if (!prop_fkeymode) {
224         init = TRUE;
225         prop_fkeymode = MakeAtom(EVDEV_PROP_FUNCTION_KEYS, strlen(EVDEV_PROP_FUNCTION_KEYS), TRUE);
226     }
227
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);
231
232     if (init)
233     {
234         XISetDevicePropertyDeletable(dev, prop_fkeymode, FALSE);
235         XIRegisterPropertyHandler(dev, EvdevAppleSetProperty, EvdevAppleGetProperty, NULL);
236     }
237 }
238
239
240 /**
241  * Called when a client reads the property state.
242  * Update with current kernel state, it may have changed behind our back.
243  */
244 static int
245 EvdevAppleGetProperty (DeviceIntPtr dev, Atom property)
246 {
247     if (property == prop_fkeymode)
248     {
249         InputInfoPtr pInfo  = dev->public.devicePrivate;
250         EvdevPtr     pEvdev = pInfo->private;
251         enum fkeymode fkeymode;
252
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);
259         }
260     }
261     return Success;
262 }
263
264 static int
265 EvdevAppleSetProperty(DeviceIntPtr dev, Atom atom,
266                       XIPropertyValuePtr val, BOOL checkonly)
267 {
268     InputInfoPtr pInfo  = dev->public.devicePrivate;
269     EvdevPtr pEvdev = pInfo->private;
270
271     if (atom == prop_fkeymode)
272     {
273         CARD8 v = *(CARD8*)val->data;
274
275         if (val->format != 8 || val->type != XA_INTEGER)
276             return BadMatch;
277
278         if (fnmode_readonly)
279             return BadAccess;
280
281         if (v > 1)
282             return BadValue;
283
284         if (!checkonly)
285         {
286             if ((!v && pEvdev->fkeymode != FKEYMODE_FKEYS) ||
287                 (v && pEvdev->fkeymode != FKEYMODE_MMKEYS))
288             {
289                 pEvdev->fkeymode = v ? FKEYMODE_MMKEYS : FKEYMODE_FKEYS;
290                 set_fnmode(pEvdev->fkeymode);
291             }
292         }
293     }
294
295     return Success;
296 }
297
298 void
299 EvdevAppleInitProperty(DeviceIntPtr dev)
300 {
301     InputInfoPtr pInfo  = dev->public.devicePrivate;
302     EvdevPtr     pEvdev = pInfo->private;
303     enum fkeymode fkeymode;
304
305     if (!product_check(apple_keyboard_table,
306                        pEvdev->id_vendor, pEvdev->id_product))
307         return;
308
309     fkeymode = get_fnmode();
310     pEvdev->fkeymode = fkeymode;
311     set_fkeymode_property(pInfo, fkeymode);
312 }