1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <stdint.h>
21#include <dirent.h>
22#include <fcntl.h>
23#include <sys/ioctl.h>
24#include <sys/inotify.h>
25#include <sys/limits.h>
26#include <sys/poll.h>
27#include <linux/input.h>
28#include <errno.h>
29#include <cutils/log.h>
30
31static struct pollfd* ufds;
32static char** device_names;
33static int nfds;
34
35static int open_device(const char* device) {
36  int version;
37  int fd;
38  struct pollfd* new_ufds;
39  char** new_device_names;
40  char name[80];
41  char location[80];
42  char idstr[80];
43  struct input_id id;
44
45  fd = open(device, O_RDWR);
46  if (fd < 0) {
47    return -1;
48  }
49
50  if (ioctl(fd, EVIOCGVERSION, &version)) {
51    return -1;
52  }
53  if (ioctl(fd, EVIOCGID, &id)) {
54    return -1;
55  }
56  name[sizeof(name) - 1] = '\0';
57  location[sizeof(location) - 1] = '\0';
58  idstr[sizeof(idstr) - 1] = '\0';
59  if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
60    name[0] = '\0';
61  }
62  if (ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
63    location[0] = '\0';
64  }
65  if (ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
66    idstr[0] = '\0';
67  }
68
69  new_ufds = reinterpret_cast<pollfd*>(realloc(ufds, sizeof(ufds[0]) * (nfds + 1)));
70  if (new_ufds == NULL) {
71    fprintf(stderr, "out of memory\n");
72    return -1;
73  }
74  ufds = new_ufds;
75  new_device_names = reinterpret_cast<char**>(realloc(
76      device_names, sizeof(device_names[0]) * (nfds + 1)));
77  if (new_device_names == NULL) {
78    fprintf(stderr, "out of memory\n");
79    return -1;
80  }
81  device_names = new_device_names;
82  ufds[nfds].fd = fd;
83  ufds[nfds].events = POLLIN;
84  device_names[nfds] = strdup(device);
85  nfds++;
86
87  return 0;
88}
89
90int close_device(const char* device) {
91  int i;
92  for (i = 1; i < nfds; i++) {
93    if (strcmp(device_names[i], device) == 0) {
94      int count = nfds - i - 1;
95      free(device_names[i]);
96      memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
97      memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
98      nfds--;
99      return 0;
100    }
101  }
102  return -1;
103}
104
105static int read_notify(const char* dirname, int nfd) {
106  int res;
107  char devname[PATH_MAX];
108  char* filename;
109  char event_buf[512];
110  int event_size;
111  int event_pos = 0;
112  struct inotify_event *event;
113
114  res = read(nfd, event_buf, sizeof(event_buf));
115  if (res < (int)sizeof(*event)) {
116    if (errno == EINTR)
117      return 0;
118    fprintf(stderr, "could not get event, %s\n", strerror(errno));
119    return 1;
120  }
121
122  strcpy(devname, dirname);
123  filename = devname + strlen(devname);
124  *filename++ = '/';
125
126  while (res >= (int)sizeof(*event)) {
127    event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
128    if (event->len) {
129      strcpy(filename, event->name);
130      if (event->mask & IN_CREATE) {
131        open_device(devname);
132      } else {
133        close_device(devname);
134      }
135    }
136    event_size = sizeof(*event) + event->len;
137    res -= event_size;
138    event_pos += event_size;
139  }
140  return 0;
141}
142
143static int scan_dir(const char* dirname) {
144  char devname[PATH_MAX];
145  char* filename;
146  DIR* dir;
147  struct dirent* de;
148  dir = opendir(dirname);
149  if (dir == NULL)
150    return -1;
151  strcpy(devname, dirname);
152  filename = devname + strlen(devname);
153  *filename++ = '/';
154  while ((de = readdir(dir))) {
155    if ((de->d_name[0] == '.' && de->d_name[1] == '\0') ||
156        (de->d_name[1] == '.' && de->d_name[2] == '\0'))
157      continue;
158    strcpy(filename, de->d_name);
159    open_device(devname);
160  }
161  closedir(dir);
162  return 0;
163}
164
165int init_getevent() {
166  int res;
167  const char* device_path = "/dev/input";
168
169  nfds = 1;
170  ufds = reinterpret_cast<pollfd*>(calloc(1, sizeof(ufds[0])));
171  ufds[0].fd = inotify_init();
172  ufds[0].events = POLLIN;
173
174  res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
175  if (res < 0) {
176    return 1;
177  }
178  res = scan_dir(device_path);
179  if (res < 0) {
180    return 1;
181  }
182  return 0;
183}
184
185void uninit_getevent() {
186  int i;
187  for (i = 0; i < nfds; i++) {
188    close(ufds[i].fd);
189  }
190  free(ufds);
191  ufds = 0;
192  nfds = 0;
193}
194
195int get_event(struct input_event* event, int timeout) {
196  int res;
197  int i;
198  int pollres;
199  const char* device_path = "/dev/input";
200  while (1) {
201    pollres = poll(ufds, nfds, timeout);
202    if (pollres == 0) {
203      return 1;
204    }
205    if (ufds[0].revents & POLLIN) {
206      read_notify(device_path, ufds[0].fd);
207    }
208    for (i = 1; i < nfds; i++) {
209      if (ufds[i].revents) {
210        if (ufds[i].revents & POLLIN) {
211          res = read(ufds[i].fd, event, sizeof(*event));
212          if (res < static_cast<int>(sizeof(event))) {
213            fprintf(stderr, "could not get event\n");
214            return -1;
215          }
216          return 0;
217        }
218      }
219    }
220  }
221  return 0;
222}
223