| /* |
| * Copyright (c) 2015 Cedric Hnyda <[email protected]> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it would be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /* |
| * Create a virtual device, activate auto-repeat and |
| * and check that auto repeat is working |
| */ |
| |
| #include <linux/input.h> |
| #include <linux/uinput.h> |
| #include <linux/kd.h> |
| |
| #include "test.h" |
| #include "safe_macros.h" |
| #include "lapi/fcntl.h" |
| #include "input_helper.h" |
| |
| static void setup(void); |
| static void send_events(void); |
| static int check_events(void); |
| static void cleanup(void); |
| |
| static int fd; |
| static int fd2; |
| struct input_event events[64]; |
| static int num_events; |
| static int ev_iter; |
| |
| char *TCID = "input06"; |
| |
| int main(int ac, char **av) |
| { |
| int lc; |
| int pid; |
| |
| tst_parse_opts(ac, av, NULL, NULL); |
| |
| setup(); |
| |
| for (lc = 0; TEST_LOOPING(lc); ++lc) { |
| pid = tst_fork(); |
| |
| switch (pid) { |
| case 0: |
| send_events(); |
| exit(0); |
| case -1: |
| tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); |
| default: |
| if (!check_events()) |
| tst_resm(TFAIL, |
| "Wrong data received in eventX"); |
| else |
| tst_resm(TPASS, "Data received in eventX"); |
| break; |
| } |
| |
| SAFE_WAITPID(NULL, pid, NULL, 0); |
| } |
| |
| cleanup(); |
| tst_exit(); |
| } |
| |
| static void setup(void) |
| { |
| tst_require_root(); |
| |
| fd = open_uinput(); |
| |
| SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_KEY); |
| SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_REP); |
| SAFE_IOCTL(NULL, fd, UI_SET_KEYBIT, KEY_X); |
| |
| create_device(fd); |
| |
| fd2 = open_device(); |
| SAFE_IOCTL(NULL, fd2, EVIOCGRAB, 1); |
| } |
| |
| static void send_events(void) |
| { |
| send_event(fd, EV_KEY, KEY_X, 1); |
| send_event(fd, EV_SYN, 0, 0); |
| |
| /* |
| * Sleep long enough to keep the key pressed for some time |
| * (auto-repeat). Default kernel delay to start auto-repeat is 250ms |
| * and the period is 33ms. So, we wait for a generous 500ms to make |
| * sure we get the auto-repeated keys |
| */ |
| usleep(500000); |
| |
| send_event(fd, EV_KEY, KEY_X, 0); |
| send_event(fd, EV_SYN, 0, 0); |
| } |
| |
| static int check_event(struct input_event *iev, int event, int code, int value) |
| { |
| return iev->type == event && iev->code == code && iev->value == value; |
| } |
| |
| static void read_events(void) |
| { |
| int rd = read(fd2, events, sizeof(events)); |
| if (rd < 0) |
| tst_brkm(TBROK | TERRNO, cleanup, "read() failed"); |
| |
| if (rd == 0) |
| tst_brkm(TBROK, cleanup, "Failed to read events"); |
| |
| if (rd % sizeof(struct input_event) != 0) { |
| tst_brkm(TBROK, cleanup, "read size %i not multiple of %zu", |
| rd, sizeof(struct input_event)); |
| } |
| |
| ev_iter = 0; |
| num_events = rd / sizeof(struct input_event); |
| } |
| |
| static int have_events(void) |
| { |
| return num_events && ev_iter < num_events; |
| } |
| |
| static struct input_event *next_event(void) |
| { |
| if (!have_events()) |
| read_events(); |
| |
| return &events[ev_iter++]; |
| } |
| |
| static int parse_autorepeat_config(struct input_event *iev) |
| { |
| if (!check_event_code(iev, EV_REP, REP_DELAY)) { |
| tst_resm(TFAIL, |
| "Didn't get EV_REP configuration with code REP_DELAY"); |
| return 0; |
| } |
| |
| if (!check_event_code(next_event(), EV_REP, REP_PERIOD)) { |
| tst_resm(TFAIL, |
| "Didn't get EV_REP configuration with code REP_PERIOD"); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int parse_key(struct input_event *iev) |
| { |
| int autorep_count = 0; |
| |
| if (!check_event(iev, EV_KEY, KEY_X, 1) || !check_sync_event(next_event())) { |
| tst_resm(TFAIL, "Didn't get expected key press for KEY_X"); |
| return 0; |
| } |
| |
| iev = next_event(); |
| while (check_event(iev, EV_KEY, KEY_X, 2) && check_sync_event(next_event())) { |
| autorep_count++; |
| iev = next_event(); |
| } |
| |
| /* make sure we have atleast one auto-repeated key event */ |
| if (!autorep_count) { |
| tst_resm(TFAIL, |
| "Didn't get autorepeat events for the key - KEY_X"); |
| return 0; |
| } |
| |
| if (!check_event(iev, EV_KEY, KEY_X, 0) || !check_sync_event(next_event())) { |
| tst_resm(TFAIL, |
| "Didn't get expected key release for KEY_X"); |
| return 0; |
| } |
| |
| tst_resm(TINFO, |
| "Received %d repititions for KEY_X", autorep_count); |
| |
| return 1; |
| } |
| |
| static int check_events(void) |
| { |
| struct input_event *iev; |
| int ret = 0; |
| int rep_config_done = 0; |
| int rep_keys_done = 0; |
| |
| read_events(); |
| |
| while (have_events()) { |
| iev = next_event(); |
| switch (iev->type) { |
| case EV_REP: |
| ret = parse_autorepeat_config(iev); |
| rep_config_done = 1; |
| break; |
| case EV_KEY: |
| ret = parse_key(iev); |
| rep_keys_done = 1; |
| break; |
| default: |
| tst_resm(TFAIL, |
| "Unexpected event type '0x%04x' received", |
| iev->type); |
| ret = 0; |
| break; |
| } |
| |
| if (!ret || (rep_config_done && rep_keys_done)) |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static void cleanup(void) |
| { |
| if (fd2 > 0 && close(fd2)) |
| tst_resm(TWARN | TERRNO, "close(fd2) failed"); |
| |
| destroy_device(fd); |
| } |