Path: blob/main/usr.sbin/bluetooth/bthidd/btuinput.c
103352 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2015-2017 Vladimir Kondratyev <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/ioctl.h>30#include <sys/kbio.h>31#include <sys/sysctl.h>3233#include <dev/evdev/input.h>34#include <dev/evdev/uinput.h>35#include <dev/usb/usb.h>36#include <dev/usb/usbhid.h>3738#include <assert.h>39#define L2CAP_SOCKET_CHECKED40#include <bluetooth.h>41#include <errno.h>42#include <fcntl.h>43#include <stdio.h>44#include <string.h>45#include <syslog.h>46#include <time.h>47#include <unistd.h>48#include <usbhid.h>4950#include "bthid_config.h"51#include "bthidd.h"52#include "btuinput.h"5354static int16_t const mbuttons[8] = {55BTN_LEFT,56BTN_MIDDLE,57BTN_RIGHT,58BTN_SIDE,59BTN_EXTRA,60BTN_FORWARD,61BTN_BACK,62BTN_TASK63};6465static uint16_t const led_codes[3] = {66LED_CAPSL, /* CLKED */67LED_NUML, /* NLKED */68LED_SCROLLL, /* SLKED */69};7071#define NONE KEY_RESERVED7273static uint16_t const keymap[0x100] = {74/* 0x00 - 0x27 */75NONE, NONE, NONE, NONE, KEY_A, KEY_B, KEY_C, KEY_D,76KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L,77KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,78KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2,79KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,80/* 0x28 - 0x3f */81KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB,82KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE,83KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON,84KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT,85KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2,86KEY_F3, KEY_F4, KEY_F5, KEY_F6,87/* 0x40 - 0x5f */88KEY_F7, KEY_F8, KEY_F9, KEY_F10,89KEY_F11, KEY_F12, KEY_SYSRQ, KEY_SCROLLLOCK,90KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP,91KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT,92KEY_LEFT, KEY_DOWN, KEY_UP, KEY_NUMLOCK,93KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS,94KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3,95KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7,96/* 0x60 - 0x7f */97KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT,98KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL,99KEY_F13, KEY_F14, KEY_F15, KEY_F16,100KEY_F17, KEY_F18, KEY_F19, KEY_F20,101KEY_F21, KEY_F22, KEY_F23, KEY_F24,102KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT,103KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT,104KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE,105/* 0x80 - 0x9f */106KEY_VOLUMEUP, KEY_VOLUMEDOWN, NONE, NONE,107NONE, KEY_KPCOMMA, NONE, KEY_RO,108KEY_KATAKANAHIRAGANA, KEY_YEN,KEY_HENKAN, KEY_MUHENKAN,109KEY_KPJPCOMMA, NONE, NONE, NONE,110KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA,111KEY_ZENKAKUHANKAKU, NONE, NONE, NONE,112NONE, NONE, NONE, NONE,113NONE, NONE, NONE, NONE,114/* 0xa0 - 0xbf */115NONE, NONE, NONE, NONE,116NONE, NONE, NONE, NONE,117NONE, NONE, NONE, NONE,118NONE, NONE, NONE, NONE,119NONE, NONE, NONE, NONE,120NONE, NONE, NONE, NONE,121NONE, NONE, NONE, NONE,122NONE, NONE, NONE, NONE,123/* 0xc0 - 0xdf */124NONE, NONE, NONE, NONE,125NONE, NONE, NONE, NONE,126NONE, NONE, NONE, NONE,127NONE, NONE, NONE, NONE,128NONE, NONE, NONE, NONE,129NONE, NONE, NONE, NONE,130NONE, NONE, NONE, NONE,131NONE, NONE, NONE, NONE,132/* 0xe0 - 0xff */133KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA,134KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA,135KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,KEY_NEXTSONG,136KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,137KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP,138KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT,139KEY_SLEEP, KEY_COFFEE, KEY_REFRESH, KEY_CALC,140NONE, NONE, NONE, NONE,141};142143/* Consumer page usage mapping */144static uint16_t const consmap[0x300] = {145[0x030] = KEY_POWER,146[0x031] = KEY_RESTART,147[0x032] = KEY_SLEEP,148[0x034] = KEY_SLEEP,149[0x035] = KEY_KBDILLUMTOGGLE,150[0x036] = BTN_MISC,151[0x040] = KEY_MENU,152[0x041] = KEY_SELECT,153[0x042] = KEY_UP,154[0x043] = KEY_DOWN,155[0x044] = KEY_LEFT,156[0x045] = KEY_RIGHT,157[0x046] = KEY_ESC,158[0x047] = KEY_KPPLUS,159[0x048] = KEY_KPMINUS,160[0x060] = KEY_INFO,161[0x061] = KEY_SUBTITLE,162[0x063] = KEY_VCR,163[0x065] = KEY_CAMERA,164[0x069] = KEY_RED,165[0x06a] = KEY_GREEN,166[0x06b] = KEY_BLUE,167[0x06c] = KEY_YELLOW,168[0x06d] = KEY_ZOOM,169[0x06f] = KEY_BRIGHTNESSUP,170[0x070] = KEY_BRIGHTNESSDOWN,171[0x072] = KEY_BRIGHTNESS_TOGGLE,172[0x073] = KEY_BRIGHTNESS_MIN,173[0x074] = KEY_BRIGHTNESS_MAX,174[0x075] = KEY_BRIGHTNESS_AUTO,175[0x082] = KEY_VIDEO_NEXT,176[0x083] = KEY_LAST,177[0x084] = KEY_ENTER,178[0x088] = KEY_PC,179[0x089] = KEY_TV,180[0x08a] = KEY_WWW,181[0x08b] = KEY_DVD,182[0x08c] = KEY_PHONE,183[0x08d] = KEY_PROGRAM,184[0x08e] = KEY_VIDEOPHONE,185[0x08f] = KEY_GAMES,186[0x090] = KEY_MEMO,187[0x091] = KEY_CD,188[0x092] = KEY_VCR,189[0x093] = KEY_TUNER,190[0x094] = KEY_EXIT,191[0x095] = KEY_HELP,192[0x096] = KEY_TAPE,193[0x097] = KEY_TV2,194[0x098] = KEY_SAT,195[0x09a] = KEY_PVR,196[0x09c] = KEY_CHANNELUP,197[0x09d] = KEY_CHANNELDOWN,198[0x0a0] = KEY_VCR2,199[0x0b0] = KEY_PLAY,200[0x0b1] = KEY_PAUSE,201[0x0b2] = KEY_RECORD,202[0x0b3] = KEY_FASTFORWARD,203[0x0b4] = KEY_REWIND,204[0x0b5] = KEY_NEXTSONG,205[0x0b6] = KEY_PREVIOUSSONG,206[0x0b7] = KEY_STOPCD,207[0x0b8] = KEY_EJECTCD,208[0x0bc] = KEY_MEDIA_REPEAT,209[0x0b9] = KEY_SHUFFLE,210[0x0bf] = KEY_SLOW,211[0x0cd] = KEY_PLAYPAUSE,212[0x0cf] = KEY_VOICECOMMAND,213[0x0e2] = KEY_MUTE,214[0x0e5] = KEY_BASSBOOST,215[0x0e9] = KEY_VOLUMEUP,216[0x0ea] = KEY_VOLUMEDOWN,217[0x0f5] = KEY_SLOW,218[0x181] = KEY_BUTTONCONFIG,219[0x182] = KEY_BOOKMARKS,220[0x183] = KEY_CONFIG,221[0x184] = KEY_WORDPROCESSOR,222[0x185] = KEY_EDITOR,223[0x186] = KEY_SPREADSHEET,224[0x187] = KEY_GRAPHICSEDITOR,225[0x188] = KEY_PRESENTATION,226[0x189] = KEY_DATABASE,227[0x18a] = KEY_MAIL,228[0x18b] = KEY_NEWS,229[0x18c] = KEY_VOICEMAIL,230[0x18d] = KEY_ADDRESSBOOK,231[0x18e] = KEY_CALENDAR,232[0x18f] = KEY_TASKMANAGER,233[0x190] = KEY_JOURNAL,234[0x191] = KEY_FINANCE,235[0x192] = KEY_CALC,236[0x193] = KEY_PLAYER,237[0x194] = KEY_FILE,238[0x196] = KEY_WWW,239[0x199] = KEY_CHAT,240[0x19c] = KEY_LOGOFF,241[0x19e] = KEY_COFFEE,242[0x19f] = KEY_CONTROLPANEL,243[0x1a2] = KEY_APPSELECT,244[0x1a3] = KEY_NEXT,245[0x1a4] = KEY_PREVIOUS,246[0x1a6] = KEY_HELP,247[0x1a7] = KEY_DOCUMENTS,248[0x1ab] = KEY_SPELLCHECK,249[0x1ae] = KEY_KEYBOARD,250[0x1b1] = KEY_SCREENSAVER,251[0x1b4] = KEY_FILE,252[0x1b6] = KEY_IMAGES,253[0x1b7] = KEY_AUDIO,254[0x1b8] = KEY_VIDEO,255[0x1bc] = KEY_MESSENGER,256[0x1bd] = KEY_INFO,257[0x201] = KEY_NEW,258[0x202] = KEY_OPEN,259[0x203] = KEY_CLOSE,260[0x204] = KEY_EXIT,261[0x207] = KEY_SAVE,262[0x208] = KEY_PRINT,263[0x209] = KEY_PROPS,264[0x21a] = KEY_UNDO,265[0x21b] = KEY_COPY,266[0x21c] = KEY_CUT,267[0x21d] = KEY_PASTE,268[0x21f] = KEY_FIND,269[0x221] = KEY_SEARCH,270[0x222] = KEY_GOTO,271[0x223] = KEY_HOMEPAGE,272[0x224] = KEY_BACK,273[0x225] = KEY_FORWARD,274[0x226] = KEY_STOP,275[0x227] = KEY_REFRESH,276[0x22a] = KEY_BOOKMARKS,277[0x22d] = KEY_ZOOMIN,278[0x22e] = KEY_ZOOMOUT,279[0x22f] = KEY_ZOOMRESET,280[0x233] = KEY_SCROLLUP,281[0x234] = KEY_SCROLLDOWN,282[0x23d] = KEY_EDIT,283[0x25f] = KEY_CANCEL,284[0x269] = KEY_INSERT,285[0x26a] = KEY_DELETE,286[0x279] = KEY_REDO,287[0x289] = KEY_REPLY,288[0x28b] = KEY_FORWARDMAIL,289[0x28c] = KEY_SEND,290[0x2c7] = KEY_KBDINPUTASSIST_PREV,291[0x2c8] = KEY_KBDINPUTASSIST_NEXT,292[0x2c9] = KEY_KBDINPUTASSIST_PREVGROUP,293[0x2ca] = KEY_KBDINPUTASSIST_NEXTGROUP,294[0x2cb] = KEY_KBDINPUTASSIST_ACCEPT,295[0x2cc] = KEY_KBDINPUTASSIST_CANCEL,296};297298static int32_t299uinput_open_common(hid_device_p const p, bdaddr_p local, const uint8_t *name)300{301struct uinput_setup uisetup;302uint8_t phys[UINPUT_MAX_NAME_SIZE];303uint8_t uniq[UINPUT_MAX_NAME_SIZE];304int32_t fd;305306/* Take local and remote bdaddr */307bt_ntoa(local, phys);308bt_ntoa(&p->bdaddr, uniq);309310/* Take device name from bthidd.conf. Fallback to generic name. */311if (p->name != NULL)312name = p->name;313314/* Set device name and bus/vendor information */315memset(&uisetup, 0, sizeof(uisetup));316snprintf(uisetup.name, UINPUT_MAX_NAME_SIZE,317"%s, bdaddr %s", name, uniq);318uisetup.id.bustype = BUS_BLUETOOTH;319uisetup.id.vendor = p->vendor_id;320uisetup.id.product = p->product_id;321uisetup.id.version = p->version;322323fd = open("/dev/uinput", O_RDWR | O_NONBLOCK);324325if (ioctl(fd, UI_SET_PHYS, phys) < 0 ||326ioctl(fd, UI_SET_BSDUNIQ, uniq) < 0 ||327ioctl(fd, UI_DEV_SETUP, &uisetup) < 0)328return (-1);329330return (fd);331}332333/*334* Setup uinput device as 8button mouse with wheel(s)335* TODO: bring in more feature detection code from ums336*/337int32_t338uinput_open_mouse(hid_device_p const p, bdaddr_p local)339{340size_t i;341int32_t fd;342343assert(p != NULL);344345if ((fd = uinput_open_common(p, local, "Bluetooth Mouse")) < 0)346goto bail_out;347348/* Advertise events and axes */349if (ioctl(fd, UI_SET_EVBIT, EV_SYN) < 0 ||350ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 ||351ioctl(fd, UI_SET_EVBIT, EV_REL) < 0 ||352ioctl(fd, UI_SET_RELBIT, REL_X) < 0 ||353ioctl(fd, UI_SET_RELBIT, REL_Y) < 0 ||354(p->has_wheel && ioctl(fd, UI_SET_RELBIT, REL_WHEEL) < 0) ||355(p->has_hwheel && ioctl(fd, UI_SET_RELBIT, REL_HWHEEL) < 0) ||356ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_POINTER) < 0)357goto bail_out;358359/* Advertise mouse buttons */360for (i = 0; i < nitems(mbuttons); i++)361if (ioctl(fd, UI_SET_KEYBIT, mbuttons[i]) < 0)362goto bail_out;363364if (ioctl(fd, UI_DEV_CREATE) >= 0)365return (fd); /* SUCCESS */366367bail_out:368if (fd >= 0)369close(fd);370return (-1);371}372373/*374* Setup uinput keyboard375*/376int32_t377uinput_open_keyboard(hid_device_p const p, bdaddr_p local)378{379size_t i;380int32_t fd;381382assert(p != NULL);383384if ((fd = uinput_open_common(p, local, "Bluetooth Keyboard")) < 0)385goto bail_out;386387/* Advertise key events and LEDs */388if (ioctl(fd, UI_SET_EVBIT, EV_KEY) < 0 ||389ioctl(fd, UI_SET_EVBIT, EV_LED) < 0 ||390ioctl(fd, UI_SET_EVBIT, EV_SYN) < 0 ||391ioctl(fd, UI_SET_EVBIT, EV_REP) < 0 ||392ioctl(fd, UI_SET_LEDBIT, LED_CAPSL) < 0 ||393ioctl(fd, UI_SET_LEDBIT, LED_NUML) < 0 ||394ioctl(fd, UI_SET_LEDBIT, LED_SCROLLL))395goto bail_out;396397/* Advertise keycodes */398for (i = 0; i < nitems(keymap); i++)399if (keymap[i] != NONE &&400ioctl(fd, UI_SET_KEYBIT, keymap[i]) < 0)401goto bail_out;402403/* Advertise consumer page keys if any */404if (p->has_cons) {405for (i = 0; i < nitems(consmap); i++) {406if (consmap[i] != NONE &&407ioctl(fd, UI_SET_KEYBIT, consmap[i]) < 0)408goto bail_out;409}410}411412if (ioctl(fd, UI_DEV_CREATE) >= 0)413return (fd); /* SUCCESS */414415bail_out:416if (fd >= 0)417close(fd);418return (-1);419}420421/* from sys/dev/evdev/evdev.h */422#define EVDEV_RCPT_HW_MOUSE (1<<2)423#define EVDEV_RCPT_HW_KBD (1<<3)424425#define MASK_POLL_INTERVAL 5 /* seconds */426#define MASK_SYSCTL "kern.evdev.rcpt_mask"427428static int32_t429uinput_get_rcpt_mask(void)430{431static struct timespec last = { 0, 0 };432struct timespec now;433static int32_t mask = 0;434size_t len;435time_t elapsed;436437if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == -1)438return mask;439440elapsed = now.tv_sec - last.tv_sec;441if (now.tv_nsec < last.tv_nsec)442elapsed--;443444if (elapsed >= MASK_POLL_INTERVAL) {445len = sizeof(mask);446if (sysctlbyname(MASK_SYSCTL, &mask, &len, NULL, 0) < 0) {447if (errno == ENOENT)448/* kernel is compiled w/o EVDEV_SUPPORT */449mask = EVDEV_RCPT_HW_MOUSE | EVDEV_RCPT_HW_KBD;450else451mask = 0;452}453last = now;454}455return mask;456}457458static int32_t459uinput_write_event(int32_t fd, uint16_t type, uint16_t code, int32_t value)460{461struct input_event ie;462463assert(fd >= 0);464465memset(&ie, 0, sizeof(ie));466ie.type = type;467ie.code = code;468ie.value = value;469return (write(fd, &ie, sizeof(ie)));470}471472int32_t473uinput_rep_mouse(int32_t fd, int32_t x, int32_t y, int32_t z, int32_t t,474int32_t buttons, int32_t obuttons)475{476size_t i;477int32_t rcpt_mask, mask;478479assert(fd >= 0);480481rcpt_mask = uinput_get_rcpt_mask();482if (!(rcpt_mask & EVDEV_RCPT_HW_MOUSE))483return (0);484485if ((x != 0 && uinput_write_event(fd, EV_REL, REL_X, x) < 0) ||486(y != 0 && uinput_write_event(fd, EV_REL, REL_Y, y) < 0) ||487(z != 0 && uinput_write_event(fd, EV_REL, REL_WHEEL, -z) < 0) ||488(t != 0 && uinput_write_event(fd, EV_REL, REL_HWHEEL, t) < 0))489return (-1);490491for (i = 0; i < nitems(mbuttons); i++) {492mask = 1 << i;493if ((buttons & mask) == (obuttons & mask))494continue;495if (uinput_write_event(fd, EV_KEY, mbuttons[i],496(buttons & mask) != 0) < 0)497return (-1);498}499500if (uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) < 0)501return (-1);502503return (0);504}505506/*507* Translate and report keyboard page key events508*/509int32_t510uinput_rep_key(int32_t fd, int32_t key, int32_t make)511{512int32_t rcpt_mask;513514assert(fd >= 0);515516rcpt_mask = uinput_get_rcpt_mask();517if (!(rcpt_mask & EVDEV_RCPT_HW_KBD))518return (0);519520if (key >= 0 && key < (int32_t)nitems(keymap) &&521keymap[key] != NONE) {522if (uinput_write_event(fd, EV_KEY, keymap[key], make) > 0 &&523uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) > 0)524return (0);525}526return (-1);527}528529/*530* Translate and report consumer page key events531*/532int32_t533uinput_rep_cons(int32_t fd, int32_t key, int32_t make)534{535int32_t rcpt_mask;536537assert(fd >= 0);538539rcpt_mask = uinput_get_rcpt_mask();540if (!(rcpt_mask & EVDEV_RCPT_HW_KBD))541return (0);542543if (key >= 0 && key < (int32_t)nitems(consmap) &&544consmap[key] != NONE) {545if (uinput_write_event(fd, EV_KEY, consmap[key], make) > 0 &&546uinput_write_event(fd, EV_SYN, SYN_REPORT, 0) > 0)547return (0);548}549return (-1);550}551552/*553* Translate and report LED events554*/555int32_t556uinput_rep_leds(int32_t fd, int state, int mask)557{558size_t i;559int32_t rcpt_mask;560561assert(fd >= 0);562563rcpt_mask = uinput_get_rcpt_mask();564if (!(rcpt_mask & EVDEV_RCPT_HW_KBD))565return (0);566567for (i = 0; i < nitems(led_codes); i++) {568if (mask & (1 << i) &&569uinput_write_event(fd, EV_LED, led_codes[i],570state & (1 << i) ? 1 : 0) < 0)571return (-1);572}573574return (0);575}576577/*578* Process status change from evdev579*/580int32_t581uinput_kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len)582{583struct input_event ie;584int32_t leds, oleds;585size_t i;586587assert(s != NULL);588assert(s->vkbd >= 0);589assert(len == sizeof(struct input_event));590591memcpy(&ie, data, sizeof(ie));592switch (ie.type) {593case EV_LED:594ioctl(s->vkbd, KDGETLED, &oleds);595leds = oleds;596for (i = 0; i < nitems(led_codes); i++) {597if (led_codes[i] == ie.code) {598if (ie.value)599leds |= 1 << i;600else601leds &= ~(1 << i);602if (leds != oleds)603ioctl(s->vkbd, KDSETLED, leds);604break;605}606}607break;608case EV_REP:609/* FALLTHROUGH. Repeats are handled by evdev subsystem */610default:611break;612}613614return (0);615}616617618