/*1* kbd.c2*/34/*-5* SPDX-License-Identifier: BSD-2-Clause6*7* Copyright (c) 2006 Maksim Yevmenkin <[email protected]>8* All rights reserved.9*10* Redistribution and use in source and binary forms, with or without11* modification, are permitted provided that the following conditions12* are met:13* 1. Redistributions of source code must retain the above copyright14* notice, this list of conditions and the following disclaimer.15* 2. Redistributions in binary form must reproduce the above copyright16* notice, this list of conditions and the following disclaimer in the17* documentation and/or other materials provided with the distribution.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND20* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE21* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE22* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE23* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL24* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS25* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT27* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY28* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF29* SUCH DAMAGE.30*31* $Id: kbd.c,v 1.4 2006/09/07 21:06:53 max Exp $32*/3334#include <sys/consio.h>35#include <sys/ioctl.h>36#include <sys/kbio.h>37#include <sys/param.h>38#include <sys/queue.h>39#include <sys/wait.h>40#include <assert.h>41#define L2CAP_SOCKET_CHECKED42#include <bluetooth.h>43#include <dev/usb/usb.h>44#include <dev/usb/usbhid.h>45#include <dev/vkbd/vkbd_var.h>46#include <errno.h>47#include <fcntl.h>48#include <limits.h>49#include <stdarg.h>50#include <stdio.h>51#include <stdlib.h>52#include <string.h>53#include <syslog.h>54#include <unistd.h>55#include <usbhid.h>56#include "bthid_config.h"57#include "bthidd.h"58#include "btuinput.h"59#include "kbd.h"6061static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd);62static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob);63static void uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd);6465/*66* HID code to PS/2 set 1 code translation table.67*68* http://www.microsoft.com/whdc/device/input/Scancode.mspx69*70* The table only contains "make" (key pressed) codes.71* The "break" (key released) code is generated as "make" | 0x8072*/7374#define E0PREFIX (1U << 31)75#define NOBREAK (1 << 30)76#define CODEMASK (~(E0PREFIX|NOBREAK))7778static int32_t const x[] =79{80/*==================================================*/81/* Name HID code Make Break*/82/*==================================================*/83/* No Event 00 */ -1, /* None */84/* Overrun Error 01 */ NOBREAK|0xFF, /* None */85/* POST Fail 02 */ NOBREAK|0xFC, /* None */86/* ErrorUndefined 03 */ -1, /* Unassigned */87/* a A 04 */ 0x1E, /* 9E */88/* b B 05 */ 0x30, /* B0 */89/* c C 06 */ 0x2E, /* AE */90/* d D 07 */ 0x20, /* A0 */91/* e E 08 */ 0x12, /* 92 */92/* f F 09 */ 0x21, /* A1 */93/* g G 0A */ 0x22, /* A2 */94/* h H 0B */ 0x23, /* A3 */95/* i I 0C */ 0x17, /* 97 */96/* j J 0D */ 0x24, /* A4 */97/* k K 0E */ 0x25, /* A5 */98/* l L 0F */ 0x26, /* A6 */99/* m M 10 */ 0x32, /* B2 */100/* n N 11 */ 0x31, /* B1 */101/* o O 12 */ 0x18, /* 98 */102/* p P 13 */ 0x19, /* 99 */103/* q Q 14 */ 0x10, /* 90 */104/* r R 15 */ 0x13, /* 93 */105/* s S 16 */ 0x1F, /* 9F */106/* t T 17 */ 0x14, /* 94 */107/* u U 18 */ 0x16, /* 96 */108/* v V 19 */ 0x2F, /* AF */109/* w W 1A */ 0x11, /* 91 */110/* x X 1B */ 0x2D, /* AD */111/* y Y 1C */ 0x15, /* 95 */112/* z Z 1D */ 0x2C, /* AC */113/* 1 ! 1E */ 0x02, /* 82 */114/* 2 @ 1F */ 0x03, /* 83 */115/* 3 # 20 */ 0x04, /* 84 */116/* 4 $ 21 */ 0x05, /* 85 */117/* 5 % 22 */ 0x06, /* 86 */118/* 6 ^ 23 */ 0x07, /* 87 */119/* 7 & 24 */ 0x08, /* 88 */120/* 8 * 25 */ 0x09, /* 89 */121/* 9 ( 26 */ 0x0A, /* 8A */122/* 0 ) 27 */ 0x0B, /* 8B */123/* Return 28 */ 0x1C, /* 9C */124/* Escape 29 */ 0x01, /* 81 */125/* Backspace 2A */ 0x0E, /* 8E */126/* Tab 2B */ 0x0F, /* 8F */127/* Space 2C */ 0x39, /* B9 */128/* - _ 2D */ 0x0C, /* 8C */129/* = + 2E */ 0x0D, /* 8D */130/* [ { 2F */ 0x1A, /* 9A */131/* ] } 30 */ 0x1B, /* 9B */132/* \ | 31 */ 0x2B, /* AB */133/* Europe 1 32 */ 0x2B, /* AB */134/* ; : 33 */ 0x27, /* A7 */135/* " ' 34 */ 0x28, /* A8 */136/* ` ~ 35 */ 0x29, /* A9 */137/* comma < 36 */ 0x33, /* B3 */138/* . > 37 */ 0x34, /* B4 */139/* / ? 38 */ 0x35, /* B5 */140/* Caps Lock 39 */ 0x3A, /* BA */141/* F1 3A */ 0x3B, /* BB */142/* F2 3B */ 0x3C, /* BC */143/* F3 3C */ 0x3D, /* BD */144/* F4 3D */ 0x3E, /* BE */145/* F5 3E */ 0x3F, /* BF */146/* F6 3F */ 0x40, /* C0 */147/* F7 40 */ 0x41, /* C1 */148/* F8 41 */ 0x42, /* C2 */149/* F9 42 */ 0x43, /* C3 */150/* F10 43 */ 0x44, /* C4 */151/* F11 44 */ 0x57, /* D7 */152/* F12 45 */ 0x58, /* D8 */153/* Print Screen 46 */ E0PREFIX|0x37, /* E0 B7 */154/* Scroll Lock 47 */ 0x46, /* C6 */155#if 0156/* Break (Ctrl-Pause) 48 */ E0 46 E0 C6, /* None */157/* Pause 48 */ E1 1D 45 E1 9D C5, /* None */158#else159/* Break (Ctrl-Pause)/Pause 48 */ NOBREAK /* Special case */, /* None */160#endif161/* Insert 49 */ E0PREFIX|0x52, /* E0 D2 */162/* Home 4A */ E0PREFIX|0x47, /* E0 C7 */163/* Page Up 4B */ E0PREFIX|0x49, /* E0 C9 */164/* Delete 4C */ E0PREFIX|0x53, /* E0 D3 */165/* End 4D */ E0PREFIX|0x4F, /* E0 CF */166/* Page Down 4E */ E0PREFIX|0x51, /* E0 D1 */167/* Right Arrow 4F */ E0PREFIX|0x4D, /* E0 CD */168/* Left Arrow 50 */ E0PREFIX|0x4B, /* E0 CB */169/* Down Arrow 51 */ E0PREFIX|0x50, /* E0 D0 */170/* Up Arrow 52 */ E0PREFIX|0x48, /* E0 C8 */171/* Num Lock 53 */ 0x45, /* C5 */172/* Keypad / 54 */ E0PREFIX|0x35, /* E0 B5 */173/* Keypad * 55 */ 0x37, /* B7 */174/* Keypad - 56 */ 0x4A, /* CA */175/* Keypad + 57 */ 0x4E, /* CE */176/* Keypad Enter 58 */ E0PREFIX|0x1C, /* E0 9C */177/* Keypad 1 End 59 */ 0x4F, /* CF */178/* Keypad 2 Down 5A */ 0x50, /* D0 */179/* Keypad 3 PageDn 5B */ 0x51, /* D1 */180/* Keypad 4 Left 5C */ 0x4B, /* CB */181/* Keypad 5 5D */ 0x4C, /* CC */182/* Keypad 6 Right 5E */ 0x4D, /* CD */183/* Keypad 7 Home 5F */ 0x47, /* C7 */184/* Keypad 8 Up 60 */ 0x48, /* C8 */185/* Keypad 9 PageUp 61 */ 0x49, /* C9 */186/* Keypad 0 Insert 62 */ 0x52, /* D2 */187/* Keypad . Delete 63 */ 0x53, /* D3 */188/* Europe 2 64 */ 0x56, /* D6 */189/* App 65 */ E0PREFIX|0x5D, /* E0 DD */190/* Keyboard Power 66 */ E0PREFIX|0x5E, /* E0 DE */191/* Keypad = 67 */ 0x59, /* D9 */192/* F13 68 */ 0x64, /* E4 */193/* F14 69 */ 0x65, /* E5 */194/* F15 6A */ 0x66, /* E6 */195/* F16 6B */ 0x67, /* E7 */196/* F17 6C */ 0x68, /* E8 */197/* F18 6D */ 0x69, /* E9 */198/* F19 6E */ 0x6A, /* EA */199/* F20 6F */ 0x6B, /* EB */200/* F21 70 */ 0x6C, /* EC */201/* F22 71 */ 0x6D, /* ED */202/* F23 72 */ 0x6E, /* EE */203/* F24 73 */ 0x76, /* F6 */204/* Keyboard Execute 74 */ -1, /* Unassigned */205/* Keyboard Help 75 */ -1, /* Unassigned */206/* Keyboard Menu 76 */ -1, /* Unassigned */207/* Keyboard Select 77 */ -1, /* Unassigned */208/* Keyboard Stop 78 */ -1, /* Unassigned */209/* Keyboard Again 79 */ -1, /* Unassigned */210/* Keyboard Undo 7A */ -1, /* Unassigned */211/* Keyboard Cut 7B */ -1, /* Unassigned */212/* Keyboard Copy 7C */ -1, /* Unassigned */213/* Keyboard Paste 7D */ -1, /* Unassigned */214/* Keyboard Find 7E */ -1, /* Unassigned */215/* Keyboard Mute 7F */ -1, /* Unassigned */216/* Keyboard Volume Up 80 */ -1, /* Unassigned */217/* Keyboard Volume Dn 81 */ -1, /* Unassigned */218/* Keyboard Locking Caps Lock 82 */ -1, /* Unassigned */219/* Keyboard Locking Num Lock 83 */ -1, /* Unassigned */220/* Keyboard Locking Scroll Lock 84 */ -1, /* Unassigned */221/* Keypad comma 85 */ 0x7E, /* FE */222/* Keyboard Equal Sign 86 */ -1, /* Unassigned */223/* Keyboard Int'l 1 87 */ 0x73, /* F3 */224/* Keyboard Int'l 2 88 */ 0x70, /* F0 */225/* Keyboard Int'l 2 89 */ 0x7D, /* FD */226/* Keyboard Int'l 4 8A */ 0x79, /* F9 */227/* Keyboard Int'l 5 8B */ 0x7B, /* FB */228/* Keyboard Int'l 6 8C */ 0x5C, /* DC */229/* Keyboard Int'l 7 8D */ -1, /* Unassigned */230/* Keyboard Int'l 8 8E */ -1, /* Unassigned */231/* Keyboard Int'l 9 8F */ -1, /* Unassigned */232/* Keyboard Lang 1 90 */ 0x71, /* Kana */233/* Keyboard Lang 2 91 */ 0x72, /* Eisu */234/* Keyboard Lang 3 92 */ 0x78, /* F8 */235/* Keyboard Lang 4 93 */ 0x77, /* F7 */236/* Keyboard Lang 5 94 */ 0x76, /* F6 */237/* Keyboard Lang 6 95 */ -1, /* Unassigned */238/* Keyboard Lang 7 96 */ -1, /* Unassigned */239/* Keyboard Lang 8 97 */ -1, /* Unassigned */240/* Keyboard Lang 9 98 */ -1, /* Unassigned */241/* Keyboard Alternate Erase 99 */ -1, /* Unassigned */242/* Keyboard SysReq/Attention 9A */ -1, /* Unassigned */243/* Keyboard Cancel 9B */ -1, /* Unassigned */244/* Keyboard Clear 9C */ -1, /* Unassigned */245/* Keyboard Prior 9D */ -1, /* Unassigned */246/* Keyboard Return 9E */ -1, /* Unassigned */247/* Keyboard Separator 9F */ -1, /* Unassigned */248/* Keyboard Out A0 */ -1, /* Unassigned */249/* Keyboard Oper A1 */ -1, /* Unassigned */250/* Keyboard Clear/Again A2 */ -1, /* Unassigned */251/* Keyboard CrSel/Props A3 */ -1, /* Unassigned */252/* Keyboard ExSel A4 */ -1, /* Unassigned */253/* Reserved A5 */ -1, /* Reserved */254/* Reserved A6 */ -1, /* Reserved */255/* Reserved A7 */ -1, /* Reserved */256/* Reserved A8 */ -1, /* Reserved */257/* Reserved A9 */ -1, /* Reserved */258/* Reserved AA */ -1, /* Reserved */259/* Reserved AB */ -1, /* Reserved */260/* Reserved AC */ -1, /* Reserved */261/* Reserved AD */ -1, /* Reserved */262/* Reserved AE */ -1, /* Reserved */263/* Reserved AF */ -1, /* Reserved */264/* Reserved B0 */ -1, /* Reserved */265/* Reserved B1 */ -1, /* Reserved */266/* Reserved B2 */ -1, /* Reserved */267/* Reserved B3 */ -1, /* Reserved */268/* Reserved B4 */ -1, /* Reserved */269/* Reserved B5 */ -1, /* Reserved */270/* Reserved B6 */ -1, /* Reserved */271/* Reserved B7 */ -1, /* Reserved */272/* Reserved B8 */ -1, /* Reserved */273/* Reserved B9 */ -1, /* Reserved */274/* Reserved BA */ -1, /* Reserved */275/* Reserved BB */ -1, /* Reserved */276/* Reserved BC */ -1, /* Reserved */277/* Reserved BD */ -1, /* Reserved */278/* Reserved BE */ -1, /* Reserved */279/* Reserved BF */ -1, /* Reserved */280/* Reserved C0 */ -1, /* Reserved */281/* Reserved C1 */ -1, /* Reserved */282/* Reserved C2 */ -1, /* Reserved */283/* Reserved C3 */ -1, /* Reserved */284/* Reserved C4 */ -1, /* Reserved */285/* Reserved C5 */ -1, /* Reserved */286/* Reserved C6 */ -1, /* Reserved */287/* Reserved C7 */ -1, /* Reserved */288/* Reserved C8 */ -1, /* Reserved */289/* Reserved C9 */ -1, /* Reserved */290/* Reserved CA */ -1, /* Reserved */291/* Reserved CB */ -1, /* Reserved */292/* Reserved CC */ -1, /* Reserved */293/* Reserved CD */ -1, /* Reserved */294/* Reserved CE */ -1, /* Reserved */295/* Reserved CF */ -1, /* Reserved */296/* Reserved D0 */ -1, /* Reserved */297/* Reserved D1 */ -1, /* Reserved */298/* Reserved D2 */ -1, /* Reserved */299/* Reserved D3 */ -1, /* Reserved */300/* Reserved D4 */ -1, /* Reserved */301/* Reserved D5 */ -1, /* Reserved */302/* Reserved D6 */ -1, /* Reserved */303/* Reserved D7 */ -1, /* Reserved */304/* Reserved D8 */ -1, /* Reserved */305/* Reserved D9 */ -1, /* Reserved */306/* Reserved DA */ -1, /* Reserved */307/* Reserved DB */ -1, /* Reserved */308/* Reserved DC */ -1, /* Reserved */309/* Reserved DD */ -1, /* Reserved */310/* Reserved DE */ -1, /* Reserved */311/* Reserved DF */ -1, /* Reserved */312/* Left Control E0 */ 0x1D, /* 9D */313/* Left Shift E1 */ 0x2A, /* AA */314/* Left Alt E2 */ 0x38, /* B8 */315/* Left GUI E3 */ E0PREFIX|0x5B, /* E0 DB */316/* Right Control E4 */ E0PREFIX|0x1D, /* E0 9D */317/* Right Shift E5 */ 0x36, /* B6 */318/* Right Alt E6 */ E0PREFIX|0x38, /* E0 B8 */319/* Right GUI E7 */ E0PREFIX|0x5C /* E0 DC */320};321322#define xsize (int32_t)nitems(x)323324/*325* Get a max HID keycode (aligned)326*/327328int32_t329kbd_maxkey(void)330{331return (xsize);332}333334/*335* Process keys336*/337338int32_t339kbd_process_keys(bthid_session_p s)340{341bitstr_t bit_decl(diff, xsize);342int32_t f1, f2, i;343344assert(s != NULL);345assert(s->srv != NULL);346347/* Check if the new keys have been pressed */348bit_ffs(s->keys1, xsize, &f1);349350/* Check if old keys still pressed */351bit_ffs(s->keys2, xsize, &f2);352353if (f1 == -1) {354/* no new key pressed */355if (f2 != -1) {356/* release old keys */357kbd_write(s->keys2, f2, 0, s->vkbd);358uinput_kbd_write(s->keys2, f2, 0, s->ukbd);359memset(s->keys2, 0, bitstr_size(xsize));360}361362return (0);363}364365if (f2 == -1) {366/* no old keys, but new keys pressed */367assert(f1 != -1);368369memcpy(s->keys2, s->keys1, bitstr_size(xsize));370kbd_write(s->keys1, f1, 1, s->vkbd);371uinput_kbd_write(s->keys1, f1, 1, s->ukbd);372memset(s->keys1, 0, bitstr_size(xsize));373374return (0);375}376377/* new keys got pressed, old keys got released */378memset(diff, 0, bitstr_size(xsize));379380for (i = f2; i < xsize; i ++) {381if (bit_test(s->keys2, i)) {382if (!bit_test(s->keys1, i)) {383bit_clear(s->keys2, i);384bit_set(diff, i);385}386}387}388389for (i = f1; i < xsize; i++) {390if (bit_test(s->keys1, i)) {391if (!bit_test(s->keys2, i))392bit_set(s->keys2, i);393else394bit_clear(s->keys1, i);395}396}397398bit_ffs(diff, xsize, &f2);399if (f2 > 0) {400kbd_write(diff, f2, 0, s->vkbd);401uinput_kbd_write(diff, f2, 0, s->ukbd);402}403404bit_ffs(s->keys1, xsize, &f1);405if (f1 > 0) {406kbd_write(s->keys1, f1, 1, s->vkbd);407uinput_kbd_write(s->keys1, f1, 1, s->ukbd);408memset(s->keys1, 0, bitstr_size(xsize));409}410411return (0);412}413414/*415* Translate given keymap and write keyscodes416*/417void418uinput_kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd)419{420int32_t i;421422if (fd >= 0) {423for (i = fb; i < xsize; i++) {424if (bit_test(m, i))425uinput_rep_key(fd, i, make);426}427}428}429430/*431* Translate given keymap and write keyscodes432*/433434static void435kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd)436{437int32_t i, *b, *eob, n, buf[64];438439b = buf;440eob = b + nitems(buf);441i = fb;442443while (i < xsize) {444if (bit_test(m, i)) {445n = kbd_xlate(i, make, b, eob);446if (n == -1) {447write(fd, buf, (b - buf) * sizeof(buf[0]));448b = buf;449continue;450}451452b += n;453}454455i ++;456}457458if (b != buf)459write(fd, buf, (b - buf) * sizeof(buf[0]));460}461462/*463* Translate HID code into PS/2 code and put codes into buffer b.464* Returns the number of codes put in b. Return -1 if buffer has not465* enough space.466*/467468#undef PUT469#define PUT(c, n, b, eob) \470do { \471if ((b) >= (eob)) \472return (-1); \473*(b) = (c); \474(b) ++; \475(n) ++; \476} while (0)477478static int32_t479kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob)480{481int32_t c, n;482483n = 0;484485if (code >= xsize)486return (0); /* HID code is not in the table */487488/* Handle special case - Pause/Break */489if (code == 0x48) {490if (!make)491return (0); /* No break code */492493#if 0494XXX FIXME495if (ctrl_is_pressed) {496/* Break (Ctrl-Pause) */497PUT(0xe0, n, b, eob);498PUT(0x46, n, b, eob);499PUT(0xe0, n, b, eob);500PUT(0xc6, n, b, eob);501} else {502/* Pause */503PUT(0xe1, n, b, eob);504PUT(0x1d, n, b, eob);505PUT(0x45, n, b, eob);506PUT(0xe1, n, b, eob);507PUT(0x9d, n, b, eob);508PUT(0xc5, n, b, eob);509}510#endif511512return (n);513}514515if ((c = x[code]) == -1)516return (0); /* HID code translation is not defined */517518if (make) {519if (c & E0PREFIX)520PUT(0xe0, n, b, eob);521522PUT((c & CODEMASK), n, b, eob);523} else if (!(c & NOBREAK)) {524if (c & E0PREFIX)525PUT(0xe0, n, b, eob);526527PUT((0x80|(c & CODEMASK)), n, b, eob);528}529530return (n);531}532533/*534* Process status change from vkbd(4)535*/536537int32_t538kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len)539{540vkbd_status_t st;541uint8_t found, report_id;542hid_device_p hid_device;543hid_data_t d;544hid_item_t h;545uint8_t leds_mask = 0;546547assert(s != NULL);548assert(len == sizeof(vkbd_status_t));549550memcpy(&st, data, sizeof(st));551found = 0;552report_id = NO_REPORT_ID;553554hid_device = get_hid_device(&s->bdaddr);555assert(hid_device != NULL);556557data[0] = 0xa2; /* DATA output (HID output report) */558data[1] = 0x00;559data[2] = 0x00;560561for (d = hid_start_parse(hid_device->desc, 1 << hid_output, -1);562hid_get_item(d, &h) > 0; ) {563if (HID_PAGE(h.usage) == HUP_LEDS) {564found++;565566if (report_id == NO_REPORT_ID)567report_id = h.report_ID;568else if (h.report_ID != report_id)569syslog(LOG_WARNING, "Output HID report IDs " \570"for %s do not match: %d vs. %d. " \571"Please report",572bt_ntoa(&s->bdaddr, NULL),573h.report_ID, report_id);574575switch(HID_USAGE(h.usage)) {576case 0x01: /* Num Lock LED */577if (st.leds & LED_NUM)578hid_set_data(&data[1], &h, 1);579leds_mask |= LED_NUM;580break;581582case 0x02: /* Caps Lock LED */583if (st.leds & LED_CAP)584hid_set_data(&data[1], &h, 1);585leds_mask |= LED_CAP;586break;587588case 0x03: /* Scroll Lock LED */589if (st.leds & LED_SCR)590hid_set_data(&data[1], &h, 1);591leds_mask |= LED_SCR;592break;593594/* XXX add other LEDs ? */595}596}597}598hid_end_parse(d);599600if (report_id != NO_REPORT_ID) {601data[2] = data[1];602data[1] = report_id;603}604605if (found)606write(s->intr, data, (report_id != NO_REPORT_ID) ? 3 : 2);607608if (found && s->srv->uinput && hid_device->keyboard)609uinput_rep_leds(s->ukbd, st.leds, leds_mask);610611return (0);612}613614615616