]>
Commit | Line | Data |
---|---|---|
663996b3 MS |
1 | /* |
2 | * accelerometer - exports device orientation through property | |
3 | * | |
4 | * When an "change" event is received on an accelerometer, | |
5 | * open its device node, and from the value, as well as the previous | |
6 | * value of the property, calculate the device's new orientation, | |
7 | * and export it as ID_INPUT_ACCELEROMETER_ORIENTATION. | |
8 | * | |
9 | * Possible values are: | |
10 | * undefined | |
11 | * * normal | |
12 | * * bottom-up | |
13 | * * left-up | |
14 | * * right-up | |
15 | * | |
16 | * The property will be persistent across sessions, and the new | |
17 | * orientations can be deducted from the previous one (it allows | |
18 | * for a threshold for switching between opposite ends of the | |
19 | * orientation). | |
20 | * | |
21 | * Copyright (C) 2011 Red Hat, Inc. | |
22 | * Author: | |
23 | * Bastien Nocera <hadess@hadess.net> | |
24 | * | |
25 | * orientation_calc() from the sensorfw package | |
26 | * Copyright (C) 2009-2010 Nokia Corporation | |
27 | * Authors: | |
28 | * Üstün Ergenoglu <ext-ustun.ergenoglu@nokia.com> | |
29 | * Timo Rongas <ext-timo.2.rongas@nokia.com> | |
30 | * Lihan Guo <lihan.guo@digia.com> | |
31 | * | |
32 | * This program is free software; you can redistribute it and/or modify | |
33 | * it under the terms of the GNU General Public License as published by | |
34 | * the Free Software Foundation; either version 2 of the License, or | |
35 | * (at your option) any later version. | |
36 | * | |
37 | * This program is distributed in the hope that it will be useful, | |
38 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
39 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
40 | * GNU General Public License for more details. | |
41 | * | |
42 | * You should have received a copy of the GNU General Public License along | |
43 | * with this program; if not, write to the Free Software Foundation, Inc., | |
44 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
45 | */ | |
46 | ||
47 | #include <stdio.h> | |
48 | #include <string.h> | |
49 | #include <stdbool.h> | |
50 | #include <math.h> | |
51 | #include <sys/types.h> | |
52 | #include <sys/stat.h> | |
53 | #include <fcntl.h> | |
54 | #include <stdlib.h> | |
55 | #include <unistd.h> | |
56 | #include <getopt.h> | |
57 | #include <limits.h> | |
58 | #include <linux/limits.h> | |
59 | #include <linux/input.h> | |
60 | ||
61 | #include "libudev.h" | |
62 | #include "libudev-private.h" | |
63 | ||
64 | /* we must use this kernel-compatible implementation */ | |
65 | #define BITS_PER_LONG (sizeof(unsigned long) * 8) | |
66 | #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) | |
67 | #define OFF(x) ((x)%BITS_PER_LONG) | |
68 | #define BIT(x) (1UL<<OFF(x)) | |
69 | #define LONG(x) ((x)/BITS_PER_LONG) | |
70 | #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) | |
71 | ||
60f067b4 | 72 | _printf_(6,0) |
663996b3 MS |
73 | static void log_fn(struct udev *udev, int priority, |
74 | const char *file, int line, const char *fn, | |
75 | const char *format, va_list args) | |
76 | { | |
5eef597e | 77 | log_metav(priority, file, line, fn, format, args); |
663996b3 MS |
78 | } |
79 | ||
80 | typedef enum { | |
81 | ORIENTATION_UNDEFINED, | |
82 | ORIENTATION_NORMAL, | |
83 | ORIENTATION_BOTTOM_UP, | |
84 | ORIENTATION_LEFT_UP, | |
85 | ORIENTATION_RIGHT_UP | |
86 | } OrientationUp; | |
87 | ||
88 | static const char *orientations[] = { | |
89 | "undefined", | |
90 | "normal", | |
91 | "bottom-up", | |
92 | "left-up", | |
93 | "right-up", | |
94 | NULL | |
95 | }; | |
96 | ||
97 | #define ORIENTATION_UP_UP ORIENTATION_NORMAL | |
98 | ||
99 | #define DEFAULT_THRESHOLD 250 | |
100 | #define RADIANS_TO_DEGREES 180.0/M_PI | |
101 | #define SAME_AXIS_LIMIT 5 | |
102 | ||
103 | #define THRESHOLD_LANDSCAPE 25 | |
104 | #define THRESHOLD_PORTRAIT 20 | |
105 | ||
106 | static const char * | |
107 | orientation_to_string (OrientationUp o) | |
108 | { | |
109 | return orientations[o]; | |
110 | } | |
111 | ||
112 | static OrientationUp | |
113 | string_to_orientation (const char *orientation) | |
114 | { | |
115 | int i; | |
116 | ||
117 | if (orientation == NULL) | |
118 | return ORIENTATION_UNDEFINED; | |
119 | for (i = 0; orientations[i] != NULL; i++) { | |
120 | if (streq (orientation, orientations[i])) | |
121 | return i; | |
122 | } | |
123 | return ORIENTATION_UNDEFINED; | |
124 | } | |
125 | ||
126 | static OrientationUp | |
127 | orientation_calc (OrientationUp prev, | |
128 | int x, int y, int z) | |
129 | { | |
130 | int rotation; | |
131 | OrientationUp ret = prev; | |
132 | ||
133 | /* Portrait check */ | |
134 | rotation = round(atan((double) x / sqrt(y * y + z * z)) * RADIANS_TO_DEGREES); | |
135 | ||
136 | if (abs(rotation) > THRESHOLD_PORTRAIT) { | |
137 | ret = (rotation < 0) ? ORIENTATION_LEFT_UP : ORIENTATION_RIGHT_UP; | |
138 | ||
139 | /* Some threshold to switching between portrait modes */ | |
140 | if (prev == ORIENTATION_LEFT_UP || prev == ORIENTATION_RIGHT_UP) { | |
141 | if (abs(rotation) < SAME_AXIS_LIMIT) { | |
142 | ret = prev; | |
143 | } | |
144 | } | |
145 | ||
146 | } else { | |
147 | /* Landscape check */ | |
148 | rotation = round(atan((double) y / sqrt(x * x + z * z)) * RADIANS_TO_DEGREES); | |
149 | ||
150 | if (abs(rotation) > THRESHOLD_LANDSCAPE) { | |
151 | ret = (rotation < 0) ? ORIENTATION_BOTTOM_UP : ORIENTATION_NORMAL; | |
152 | ||
153 | /* Some threshold to switching between landscape modes */ | |
154 | if (prev == ORIENTATION_BOTTOM_UP || prev == ORIENTATION_NORMAL) { | |
155 | if (abs(rotation) < SAME_AXIS_LIMIT) { | |
156 | ret = prev; | |
157 | } | |
158 | } | |
159 | } | |
160 | } | |
161 | ||
162 | return ret; | |
163 | } | |
164 | ||
165 | static OrientationUp | |
166 | get_prev_orientation(struct udev_device *dev) | |
167 | { | |
168 | const char *value; | |
169 | ||
170 | value = udev_device_get_property_value(dev, "ID_INPUT_ACCELEROMETER_ORIENTATION"); | |
171 | if (value == NULL) | |
172 | return ORIENTATION_UNDEFINED; | |
173 | return string_to_orientation(value); | |
174 | } | |
175 | ||
5eef597e | 176 | #define READ_AXIS(axis, var) { memzero(&abs_info, sizeof(abs_info)); r = ioctl(fd, EVIOCGABS(axis), &abs_info); if (r < 0) return; var = abs_info.value; } |
663996b3 MS |
177 | |
178 | /* accelerometers */ | |
179 | static void test_orientation(struct udev *udev, | |
180 | struct udev_device *dev, | |
181 | const char *devpath) | |
182 | { | |
183 | OrientationUp old, new; | |
184 | _cleanup_close_ int fd = -1; | |
5eef597e | 185 | struct input_absinfo abs_info; |
663996b3 | 186 | int x = 0, y = 0, z = 0; |
5eef597e | 187 | int r; |
663996b3 MS |
188 | char text[64]; |
189 | ||
190 | old = get_prev_orientation(dev); | |
191 | ||
60f067b4 | 192 | fd = open(devpath, O_RDONLY|O_CLOEXEC); |
663996b3 MS |
193 | if (fd < 0) |
194 | return; | |
195 | ||
5eef597e MP |
196 | READ_AXIS(ABS_X, x); |
197 | READ_AXIS(ABS_Y, y); | |
198 | READ_AXIS(ABS_Z, z); | |
663996b3 | 199 | |
663996b3 MS |
200 | new = orientation_calc(old, x, y, z); |
201 | snprintf(text, sizeof(text), | |
202 | "ID_INPUT_ACCELEROMETER_ORIENTATION=%s", orientation_to_string(new)); | |
203 | puts(text); | |
204 | } | |
205 | ||
206 | static void help(void) | |
207 | { | |
208 | printf("Usage: accelerometer [options] <device path>\n" | |
209 | " --debug debug to stderr\n" | |
210 | " --help print this help text\n\n"); | |
211 | } | |
212 | ||
213 | int main (int argc, char** argv) | |
214 | { | |
215 | struct udev *udev; | |
216 | struct udev_device *dev; | |
217 | ||
218 | static const struct option options[] = { | |
219 | { "debug", no_argument, NULL, 'd' }, | |
220 | { "help", no_argument, NULL, 'h' }, | |
221 | {} | |
222 | }; | |
223 | ||
224 | char devpath[PATH_MAX]; | |
225 | char *devnode; | |
226 | struct udev_enumerate *enumerate; | |
227 | struct udev_list_entry *list_entry; | |
228 | ||
5eef597e MP |
229 | log_parse_environment(); |
230 | log_open(); | |
231 | ||
663996b3 MS |
232 | udev = udev_new(); |
233 | if (udev == NULL) | |
234 | return 1; | |
235 | ||
663996b3 MS |
236 | udev_set_log_fn(udev, log_fn); |
237 | ||
238 | /* CLI argument parsing */ | |
239 | while (1) { | |
240 | int option; | |
241 | ||
242 | option = getopt_long(argc, argv, "dxh", options, NULL); | |
243 | if (option == -1) | |
244 | break; | |
245 | ||
246 | switch (option) { | |
247 | case 'd': | |
5eef597e | 248 | log_set_target(LOG_TARGET_CONSOLE); |
663996b3 MS |
249 | log_set_max_level(LOG_DEBUG); |
250 | udev_set_log_priority(udev, LOG_DEBUG); | |
5eef597e | 251 | log_open(); |
663996b3 MS |
252 | break; |
253 | case 'h': | |
254 | help(); | |
255 | exit(0); | |
256 | default: | |
257 | exit(1); | |
258 | } | |
259 | } | |
260 | ||
261 | if (argv[optind] == NULL) { | |
262 | help(); | |
263 | exit(1); | |
264 | } | |
265 | ||
266 | /* get the device */ | |
267 | snprintf(devpath, sizeof(devpath), "/sys/%s", argv[optind]); | |
268 | dev = udev_device_new_from_syspath(udev, devpath); | |
269 | if (dev == NULL) { | |
270 | fprintf(stderr, "unable to access '%s'\n", devpath); | |
271 | return 1; | |
272 | } | |
273 | ||
274 | /* Get the children devices and find the devnode */ | |
275 | devnode = NULL; | |
276 | enumerate = udev_enumerate_new(udev); | |
277 | udev_enumerate_add_match_parent(enumerate, dev); | |
278 | udev_enumerate_scan_devices(enumerate); | |
279 | udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(enumerate)) { | |
280 | struct udev_device *device; | |
281 | const char *node; | |
282 | ||
283 | device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate), | |
284 | udev_list_entry_get_name(list_entry)); | |
285 | if (device == NULL) | |
286 | continue; | |
287 | /* Already found it */ | |
288 | if (devnode != NULL) { | |
289 | udev_device_unref(device); | |
290 | continue; | |
291 | } | |
292 | ||
293 | node = udev_device_get_devnode(device); | |
294 | if (node == NULL) { | |
295 | udev_device_unref(device); | |
296 | continue; | |
297 | } | |
298 | /* Use the event sub-device */ | |
299 | if (strstr(node, "/event") == NULL) { | |
300 | udev_device_unref(device); | |
301 | continue; | |
302 | } | |
303 | ||
304 | devnode = strdup(node); | |
305 | udev_device_unref(device); | |
306 | } | |
307 | ||
308 | if (devnode == NULL) { | |
309 | fprintf(stderr, "unable to get device node for '%s'\n", devpath); | |
310 | return 0; | |
311 | } | |
312 | ||
60f067b4 | 313 | log_debug("opening accelerometer device %s", devnode); |
663996b3 MS |
314 | test_orientation(udev, dev, devnode); |
315 | free(devnode); | |
316 | log_close(); | |
317 | return 0; | |
318 | } |