Add uinput daemon that creates an input device for the keyboard
This commit is contained in:
parent
b9b02a5e73
commit
a2ff7e6e98
3
inputd/build.sh
Executable file
3
inputd/build.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
gcc -o ppkbd main.c || exit 1
|
284
inputd/kmap.h
Normal file
284
inputd/kmap.h
Normal file
@ -0,0 +1,284 @@
|
||||
static const uint8_t el_phys_map[256] = {
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0xff, 0xff, 0xff, 0xff,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0xff, 0xff, 0xff, 0xff,
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x3a, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
||||
0x49, 0x4a, 0x4b, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x51, 0xff, 0xff, 0x54, 0xff, 0x56, 0xff,
|
||||
0x58, 0x57, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0x52, 0x53, 0xff, 0x55, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
static const int used_keys[] = {
|
||||
KEY_ESC,
|
||||
KEY_1,
|
||||
KEY_LEFTSHIFT,
|
||||
KEY_BACKSLASH,
|
||||
KEY_F1,
|
||||
KEY_2,
|
||||
KEY_F2,
|
||||
KEY_3,
|
||||
KEY_DOLLAR,
|
||||
KEY_F3,
|
||||
KEY_4,
|
||||
KEY_EURO,
|
||||
KEY_F4,
|
||||
KEY_5,
|
||||
KEY_GRAVE,
|
||||
KEY_F5,
|
||||
KEY_6,
|
||||
KEY_F6,
|
||||
KEY_7,
|
||||
KEY_MINUS,
|
||||
KEY_F7,
|
||||
KEY_8,
|
||||
KEY_EQUAL,
|
||||
KEY_F8,
|
||||
KEY_9,
|
||||
KEY_F9,
|
||||
KEY_BACKSPACE,
|
||||
KEY_DELETE,
|
||||
KEY_TAB,
|
||||
KEY_Q,
|
||||
KEY_W,
|
||||
KEY_E,
|
||||
KEY_R,
|
||||
KEY_T,
|
||||
KEY_Y,
|
||||
KEY_U,
|
||||
KEY_I,
|
||||
KEY_O,
|
||||
KEY_P,
|
||||
KEY_ENTER,
|
||||
KEY_LEFTMETA,
|
||||
KEY_SYSRQ,
|
||||
KEY_A,
|
||||
KEY_S,
|
||||
KEY_D,
|
||||
KEY_F,
|
||||
KEY_G,
|
||||
KEY_H,
|
||||
KEY_J,
|
||||
KEY_K,
|
||||
KEY_L,
|
||||
KEY_SEMICOLON,
|
||||
KEY_INSERT,
|
||||
KEY_Z,
|
||||
KEY_X,
|
||||
KEY_C,
|
||||
KEY_V,
|
||||
KEY_B,
|
||||
KEY_N,
|
||||
KEY_M,
|
||||
KEY_COMMA,
|
||||
KEY_HOME,
|
||||
KEY_DOT,
|
||||
KEY_UP,
|
||||
KEY_SLASH,
|
||||
KEY_END,
|
||||
KEY_LEFTCTRL,
|
||||
KEY_FN,
|
||||
KEY_LEFTALT,
|
||||
KEY_SPACE,
|
||||
KEY_RIGHTALT,
|
||||
KEY_APOSTROPHE,
|
||||
KEY_LEFT,
|
||||
KEY_LEFTBRACE,
|
||||
KEY_DOWN,
|
||||
KEY_RIGHTBRACE,
|
||||
KEY_RIGHT,
|
||||
};
|
||||
|
||||
static const char* key_names[] = {
|
||||
[KEY_ESC] = "ESC",
|
||||
[KEY_1] = "1",
|
||||
[KEY_LEFTSHIFT] = "LEFTSHIFT",
|
||||
[KEY_BACKSLASH] = "BACKSLASH",
|
||||
[KEY_F1] = "F1",
|
||||
[KEY_2] = "2",
|
||||
[KEY_F2] = "F2",
|
||||
[KEY_3] = "3",
|
||||
[KEY_DOLLAR] = "DOLLAR",
|
||||
[KEY_F3] = "F3",
|
||||
[KEY_4] = "4",
|
||||
[KEY_EURO] = "EURO",
|
||||
[KEY_F4] = "F4",
|
||||
[KEY_5] = "5",
|
||||
[KEY_GRAVE] = "GRAVE",
|
||||
[KEY_F5] = "F5",
|
||||
[KEY_6] = "6",
|
||||
[KEY_F6] = "F6",
|
||||
[KEY_7] = "7",
|
||||
[KEY_MINUS] = "MINUS",
|
||||
[KEY_F7] = "F7",
|
||||
[KEY_8] = "8",
|
||||
[KEY_EQUAL] = "EQUAL",
|
||||
[KEY_F8] = "F8",
|
||||
[KEY_9] = "9",
|
||||
[KEY_F9] = "F9",
|
||||
[KEY_BACKSPACE] = "BACKSPACE",
|
||||
[KEY_DELETE] = "DELETE",
|
||||
[KEY_TAB] = "TAB",
|
||||
[KEY_Q] = "Q",
|
||||
[KEY_W] = "W",
|
||||
[KEY_E] = "E",
|
||||
[KEY_R] = "R",
|
||||
[KEY_T] = "T",
|
||||
[KEY_Y] = "Y",
|
||||
[KEY_U] = "U",
|
||||
[KEY_I] = "I",
|
||||
[KEY_O] = "O",
|
||||
[KEY_P] = "P",
|
||||
[KEY_ENTER] = "ENTER",
|
||||
[KEY_LEFTMETA] = "LEFTMETA",
|
||||
[KEY_SYSRQ] = "SYSRQ",
|
||||
[KEY_A] = "A",
|
||||
[KEY_S] = "S",
|
||||
[KEY_D] = "D",
|
||||
[KEY_F] = "F",
|
||||
[KEY_G] = "G",
|
||||
[KEY_H] = "H",
|
||||
[KEY_J] = "J",
|
||||
[KEY_K] = "K",
|
||||
[KEY_L] = "L",
|
||||
[KEY_SEMICOLON] = "SEMICOLON",
|
||||
[KEY_INSERT] = "INSERT",
|
||||
[KEY_Z] = "Z",
|
||||
[KEY_X] = "X",
|
||||
[KEY_C] = "C",
|
||||
[KEY_V] = "V",
|
||||
[KEY_B] = "B",
|
||||
[KEY_N] = "N",
|
||||
[KEY_M] = "M",
|
||||
[KEY_COMMA] = "COMMA",
|
||||
[KEY_HOME] = "HOME",
|
||||
[KEY_DOT] = "DOT",
|
||||
[KEY_UP] = "UP",
|
||||
[KEY_SLASH] = "SLASH",
|
||||
[KEY_END] = "END",
|
||||
[KEY_LEFTCTRL] = "LEFTCTRL",
|
||||
[KEY_FN] = "FN",
|
||||
[KEY_LEFTALT] = "LEFTALT",
|
||||
[KEY_SPACE] = "SPACE",
|
||||
[KEY_RIGHTALT] = "RIGHTALT",
|
||||
[KEY_APOSTROPHE] = "APOSTROPHE",
|
||||
[KEY_LEFT] = "LEFT",
|
||||
[KEY_LEFTBRACE] = "LEFTBRACE",
|
||||
[KEY_DOWN] = "DOWN",
|
||||
[KEY_RIGHTBRACE] = "RIGHTBRACE",
|
||||
[KEY_RIGHT] = "RIGHT",
|
||||
};
|
||||
|
||||
static const int keymap_base[256][2] = {
|
||||
[0x11] = { KEY_ESC },
|
||||
[0x12] = { KEY_1 },
|
||||
[0x13] = { KEY_2 },
|
||||
[0x14] = { KEY_3 },
|
||||
[0x15] = { KEY_4 },
|
||||
[0x16] = { KEY_5 },
|
||||
[0x17] = { KEY_6 },
|
||||
[0x18] = { KEY_7 },
|
||||
[0x19] = { KEY_8 },
|
||||
[0x1a] = { KEY_9 },
|
||||
[0x1c] = { KEY_BACKSPACE },
|
||||
[0x21] = { KEY_TAB },
|
||||
[0x22] = { KEY_Q },
|
||||
[0x23] = { KEY_W },
|
||||
[0x24] = { KEY_E },
|
||||
[0x25] = { KEY_R },
|
||||
[0x26] = { KEY_T },
|
||||
[0x27] = { KEY_Y },
|
||||
[0x28] = { KEY_U },
|
||||
[0x29] = { KEY_I },
|
||||
[0x2a] = { KEY_O },
|
||||
[0x2b] = { KEY_P },
|
||||
[0x2c] = { KEY_ENTER },
|
||||
[0x31] = { KEY_LEFTMETA },
|
||||
[0x32] = { KEY_A },
|
||||
[0x33] = { KEY_S },
|
||||
[0x34] = { KEY_D },
|
||||
[0x35] = { KEY_F },
|
||||
[0x36] = { KEY_G },
|
||||
[0x37] = { KEY_H },
|
||||
[0x38] = { KEY_J },
|
||||
[0x39] = { KEY_K },
|
||||
[0x3a] = { KEY_L },
|
||||
[0x3b] = { KEY_SEMICOLON },
|
||||
[0x41] = { KEY_LEFTSHIFT },
|
||||
[0x42] = { KEY_Z },
|
||||
[0x43] = { KEY_X },
|
||||
[0x44] = { KEY_C },
|
||||
[0x45] = { KEY_V },
|
||||
[0x46] = { KEY_B },
|
||||
[0x47] = { KEY_N },
|
||||
[0x48] = { KEY_M },
|
||||
[0x49] = { KEY_COMMA },
|
||||
[0x4a] = { KEY_DOT },
|
||||
[0x4b] = { KEY_SLASH },
|
||||
[0x51] = { KEY_LEFTCTRL },
|
||||
[0x52] = { KEY_FN },
|
||||
[0x53] = { KEY_LEFTALT },
|
||||
[0x54] = { KEY_SPACE },
|
||||
[0x55] = { KEY_RIGHTALT },
|
||||
[0x56] = { KEY_APOSTROPHE },
|
||||
[0x57] = { KEY_LEFTBRACE },
|
||||
[0x58] = { KEY_RIGHTBRACE },
|
||||
};
|
||||
|
||||
static const int keymap_fn[256][2] = {
|
||||
[0x12] = { KEY_LEFTSHIFT, KEY_BACKSLASH },
|
||||
[0x13] = { KEY_BACKSLASH },
|
||||
[0x14] = { KEY_DOLLAR },
|
||||
[0x15] = { KEY_EURO },
|
||||
[0x16] = { KEY_LEFTSHIFT, KEY_GRAVE },
|
||||
[0x17] = { KEY_GRAVE },
|
||||
[0x18] = { KEY_MINUS },
|
||||
[0x19] = { KEY_EQUAL },
|
||||
[0x1a] = { KEY_LEFTSHIFT, KEY_MINUS },
|
||||
[0x1c] = { KEY_DELETE },
|
||||
[0x31] = { KEY_LEFTSHIFT, KEY_SYSRQ },
|
||||
[0x3b] = { KEY_INSERT },
|
||||
[0x49] = { KEY_HOME },
|
||||
[0x4a] = { KEY_UP },
|
||||
[0x4b] = { KEY_END },
|
||||
[0x56] = { KEY_LEFT },
|
||||
[0x57] = { KEY_DOWN },
|
||||
[0x58] = { KEY_RIGHT },
|
||||
};
|
||||
|
||||
static const int keymap_pine[256][2] = {
|
||||
[0x12] = { KEY_F1 },
|
||||
[0x13] = { KEY_F2 },
|
||||
[0x14] = { KEY_F3 },
|
||||
[0x15] = { KEY_F4 },
|
||||
[0x16] = { KEY_F5 },
|
||||
[0x17] = { KEY_F6 },
|
||||
[0x18] = { KEY_F7 },
|
||||
[0x19] = { KEY_F8 },
|
||||
[0x1a] = { KEY_F9 },
|
||||
};
|
||||
|
498
inputd/main.c
Normal file
498
inputd/main.c
Normal file
@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Pinephone keyboard userspace input device daemon.
|
||||
*
|
||||
* Copyright (C) 2021 Ondřej Jirman <megi@xff.cz>
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// {{{ includes
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-dev.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/uinput.h>
|
||||
|
||||
#define DEBUG 0
|
||||
#define MEGI_PROTO_BUG 1
|
||||
|
||||
#if DEBUG
|
||||
#define debug(args...) printf(args)
|
||||
#else
|
||||
#define debug(args...)
|
||||
#endif
|
||||
|
||||
// }}}
|
||||
// {{{ utils
|
||||
|
||||
static void syscall_error(int is_err, const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!is_err)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "ERROR: ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, ": %s\n", strerror(errno));
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void error(const char* fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
fprintf(stderr, "ERROR: ");
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bool read_file(const char* path, char* buf, size_t size)
|
||||
{
|
||||
int fd;
|
||||
ssize_t ret;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
ret = read(fd, buf, size);
|
||||
close(fd);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
if (ret < size) {
|
||||
buf[ret] = 0;
|
||||
return true;
|
||||
} else {
|
||||
buf[size - 1] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#define KB_ADDR 0x15
|
||||
|
||||
int read_kb(int fd, uint8_t data[16])
|
||||
{
|
||||
int ret;
|
||||
struct i2c_msg msgs[] = {
|
||||
{ KB_ADDR, I2C_M_RD, 16, data },
|
||||
};
|
||||
|
||||
struct i2c_rdwr_ioctl_data msg = {
|
||||
.msgs = msgs,
|
||||
.nmsgs = sizeof(msgs) / sizeof(msgs[0])
|
||||
};
|
||||
|
||||
ret = ioctl(fd, I2C_RDWR, &msg);
|
||||
//syscall_error(ret < 0, "I2C_RDWR failed");
|
||||
|
||||
return ret == 1 ? 0 : -1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int write_kb(int fd, uint8_t data[16])
|
||||
{
|
||||
int ret;
|
||||
struct i2c_msg msgs[] = {
|
||||
{ KB_ADDR, 0, 16, data },
|
||||
};
|
||||
|
||||
struct i2c_rdwr_ioctl_data msg = {
|
||||
.msgs = msgs,
|
||||
.nmsgs = sizeof(msgs) / sizeof(msgs[0])
|
||||
};
|
||||
|
||||
ret = ioctl(fd, I2C_RDWR, &msg);
|
||||
//syscall_error(ret < 0, "I2C_RDWR failed");
|
||||
|
||||
return ret == 1 ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int gpiochip_open(void)
|
||||
{
|
||||
int ret;
|
||||
char path[256], buf[1024];
|
||||
int fd = -1;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
snprintf(path, sizeof path, "/sys/bus/gpio/devices/gpiochip%d/uevent", i);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
continue;
|
||||
|
||||
if (!strstr(buf, "OF_FULLNAME=/soc/pinctrl@1f02c00"))
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof path, "/dev/gpiochip%d", i);
|
||||
|
||||
int fd = open(path, O_RDWR);
|
||||
syscall_error(fd < 0, "open(%s) failed");
|
||||
|
||||
//ret = ioctl(fd, I2C_SLAVE, addr);
|
||||
//syscall_error(ret < 0, "I2C_SLAVE failed");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
error("Can't find POGO I2C adapter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int setup_gpio(void)
|
||||
{
|
||||
int ret;
|
||||
struct gpio_v2_line_request req = {
|
||||
.num_lines = 1,
|
||||
.offsets[0] = 12,
|
||||
.config.flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_BIAS_PULL_UP | /*GPIO_V2_LINE_FLAG_ACTIVE_HIGH |*/ GPIO_V2_LINE_FLAG_EDGE_FALLING,
|
||||
.consumer = "ppkbd",
|
||||
};
|
||||
|
||||
int fd = gpiochip_open();
|
||||
|
||||
ret = ioctl(fd, GPIO_V2_GET_LINE_IOCTL, &req);
|
||||
syscall_error(ret < 0, "GPIO_V2_GET_LINE_IOCTL failed");
|
||||
|
||||
close(fd);
|
||||
|
||||
return req.fd;
|
||||
}
|
||||
|
||||
static int get_int_value(int lfd)
|
||||
{
|
||||
int ret;
|
||||
struct gpio_v2_line_values vals = {
|
||||
.mask = 1,
|
||||
};
|
||||
|
||||
ret = ioctl(lfd, GPIO_V2_LINE_GET_VALUES_IOCTL, &vals);
|
||||
syscall_error(ret < 0, "GPIO_V2_GET_LINE_IOCTL failed");
|
||||
|
||||
return vals.bits & 0x1;
|
||||
}
|
||||
|
||||
static int pogo_i2c_open(void)
|
||||
{
|
||||
int ret;
|
||||
char path[256], buf[1024];
|
||||
int fd = -1;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
snprintf(path, sizeof path, "/sys/class/i2c-adapter/i2c-%d/uevent", i);
|
||||
if (!read_file(path, buf, sizeof buf))
|
||||
continue;
|
||||
|
||||
if (!strstr(buf, "OF_FULLNAME=/soc/i2c@1c2b400"))
|
||||
continue;
|
||||
|
||||
snprintf(path, sizeof path, "/dev/i2c-%d", i);
|
||||
|
||||
int fd = open(path, O_RDWR);
|
||||
syscall_error(fd < 0, "open(%s) failed");
|
||||
|
||||
//ret = ioctl(fd, I2C_SLAVE, addr);
|
||||
//syscall_error(ret < 0, "I2C_SLAVE failed");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
error("Can't find POGO I2C adapter");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#include "kmap.h"
|
||||
|
||||
int open_uinput_dev(void)
|
||||
{
|
||||
int fd, ret;
|
||||
|
||||
fd = open("/dev/uinput", O_WRONLY/* | O_NONBLOCK*/);
|
||||
syscall_error(fd < 0, "open(/dev/uinput) failed");
|
||||
|
||||
struct uinput_setup setup = {
|
||||
.name = "ppkbd",
|
||||
.id = {
|
||||
.bustype = BUS_USB,
|
||||
.vendor = 0x1234,
|
||||
.product = 0x5678,
|
||||
},
|
||||
};
|
||||
|
||||
ret = ioctl(fd, UI_SET_EVBIT, EV_KEY);
|
||||
syscall_error(ret < 0, "UI_SET_EVBIT failed");
|
||||
|
||||
for (int i = 0; i < sizeof(used_keys) / sizeof(used_keys[0]); i++) {
|
||||
ret = ioctl(fd, UI_SET_KEYBIT, used_keys[i]);
|
||||
syscall_error(ret < 0, "UI_SET_KEYBIT failed");
|
||||
}
|
||||
|
||||
ret = ioctl(fd, UI_DEV_SETUP, &setup);
|
||||
syscall_error(ret < 0, "UI_DEV_SETUP failed");
|
||||
|
||||
ret = ioctl(fd, UI_DEV_CREATE);
|
||||
syscall_error(ret < 0, "UI_DEV_CREATE failed");
|
||||
|
||||
//ioctl(fd, UI_DEV_DESTROY);
|
||||
//close(fd);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
void emit_ev(int fd, int type, int code, int val)
|
||||
{
|
||||
struct input_event ev = {
|
||||
.type = type,
|
||||
.code = code,
|
||||
.value = val,
|
||||
};
|
||||
|
||||
ssize_t ret = write(fd, &ev, sizeof ev);
|
||||
syscall_error(ret < 0, "write event failed");
|
||||
}
|
||||
|
||||
void print_bitmap(uint8_t* map)
|
||||
{
|
||||
printf("\033[H");
|
||||
for (int r = 0; r < 6; r++) {
|
||||
if (r == 0) {
|
||||
printf(" C");
|
||||
for (int c = 0; c < 12; c++)
|
||||
printf("%-3d", c + 1);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("R%d", r + 1);
|
||||
for (int c = 0; c < 12; c++)
|
||||
printf(" %s", map[c] & (1u << r) ? "X" : ".");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int get_index(int* keys, int len, int key)
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (keys[i] == key)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int compact(int* keys, int len)
|
||||
{
|
||||
int ckeys[len];
|
||||
int ci = 0;
|
||||
|
||||
memset(ckeys, 0, len * sizeof(int));
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
if (keys[i])
|
||||
ckeys[ci++] = keys[i];
|
||||
|
||||
memcpy(keys, ckeys, sizeof ckeys);
|
||||
return ci;
|
||||
}
|
||||
|
||||
static int uinput_fd = -1;
|
||||
static int pressed_keys[128]; // contains currently pressed phys_idxs in press order
|
||||
static int pressed_count;
|
||||
|
||||
void on_press(uint8_t phys_idx)
|
||||
{
|
||||
int key = keymap_base[phys_idx][0];
|
||||
// printf("press %02hhx %s\n", phys_idx, key ? key_names[key] : "");
|
||||
|
||||
if (key == KEY_FN || key == KEY_LEFTMETA) {
|
||||
return;
|
||||
}
|
||||
|
||||
//XXX: make sure fn/pine is the last presssed key prior to this one?
|
||||
int fn_idx = get_index(pressed_keys, pressed_count, 0x52);
|
||||
int pine_idx = get_index(pressed_keys, pressed_count, 0x31);
|
||||
|
||||
const int* keys = keymap_base[phys_idx];
|
||||
if (fn_idx >= 0) {
|
||||
keys = keymap_fn[phys_idx];
|
||||
} else if (pine_idx >= 0) {
|
||||
keys = keymap_pine[phys_idx];
|
||||
}
|
||||
|
||||
if (keys[0]) {
|
||||
emit_ev(uinput_fd, EV_KEY, keys[0], 1);
|
||||
emit_ev(uinput_fd, EV_SYN, SYN_REPORT, 0);
|
||||
}
|
||||
|
||||
if (keys[1]) {
|
||||
emit_ev(uinput_fd, EV_KEY, keys[1], 1);
|
||||
emit_ev(uinput_fd, EV_SYN, SYN_REPORT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void on_release(uint8_t phys_idx)
|
||||
{
|
||||
int key = keymap_base[phys_idx][0];
|
||||
// printf("release %02hhx %s\n", phys_idx, key ? key_names[key] : "");
|
||||
|
||||
if (key == KEY_FN || key == KEY_LEFTMETA) {
|
||||
return;
|
||||
}
|
||||
|
||||
int fn_idx = get_index(pressed_keys, pressed_count, 0x52);
|
||||
int pine_idx = get_index(pressed_keys, pressed_count, 0x31);
|
||||
|
||||
const int* keys = keymap_base[phys_idx];
|
||||
if (fn_idx >= 0) {
|
||||
keys = keymap_fn[phys_idx];
|
||||
} else if (pine_idx >= 0) {
|
||||
keys = keymap_pine[phys_idx];
|
||||
}
|
||||
|
||||
if (keys[0]) {
|
||||
emit_ev(uinput_fd, EV_KEY, keys[0], 0);
|
||||
emit_ev(uinput_fd, EV_SYN, SYN_REPORT, 0);
|
||||
}
|
||||
|
||||
if (keys[1]) {
|
||||
emit_ev(uinput_fd, EV_KEY, keys[1], 0);
|
||||
emit_ev(uinput_fd, EV_SYN, SYN_REPORT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void update_keys(uint8_t* map)
|
||||
{
|
||||
// physical indices of pressed keys reported over I2C
|
||||
int keys[128];
|
||||
int n_keys = 0;
|
||||
|
||||
for (int c = 0; c < 12; c++) {
|
||||
for (int r = 0; r < 6; r++) {
|
||||
if (map[c] != 0xff && map[c] & (1u << r)) {
|
||||
uint8_t el_idx = (r << 4) | c;
|
||||
uint8_t phys_idx = el_phys_map[el_idx];
|
||||
if (phys_idx != 0xff && n_keys < 128) {
|
||||
#if MEGI_PROTO_BUG
|
||||
int key = keymap_base[phys_idx][0];
|
||||
if (key == KEY_LEFTCTRL || key == KEY_Z) // ignore these keys on my keyboard
|
||||
continue;
|
||||
#endif
|
||||
|
||||
keys[n_keys++] = phys_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// which pressed keys are no longer pressed?
|
||||
for (int j = 0; j < pressed_count; j++) {
|
||||
int key = pressed_keys[j];
|
||||
|
||||
int idx = get_index(keys, n_keys, key);
|
||||
if (idx < 0) {
|
||||
pressed_keys[j] = 0;
|
||||
pressed_count = compact(pressed_keys, 128);
|
||||
on_release(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// which new keys are pressed?
|
||||
for (int i = 0; i < n_keys; i++) {
|
||||
int key = keys[i];
|
||||
|
||||
// if the key was not pressed, handle a new press event
|
||||
int pressed_idx = get_index(pressed_keys, pressed_count, key);
|
||||
if (pressed_idx < 0 && pressed_count < 128) {
|
||||
pressed_keys[pressed_count++] = key;
|
||||
on_press(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t time_abs(void)
|
||||
{
|
||||
struct timespec tmp;
|
||||
int ret;
|
||||
|
||||
ret = clock_gettime(CLOCK_MONOTONIC, &tmp);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
return tmp.tv_sec * 1000000000ull + tmp.tv_nsec;
|
||||
}
|
||||
|
||||
int main(int ac, char* av[])
|
||||
{
|
||||
int fd, ret;
|
||||
|
||||
fd = pogo_i2c_open();
|
||||
uinput_fd = open_uinput_dev();
|
||||
|
||||
int lfd = setup_gpio();
|
||||
|
||||
struct pollfd fds[2] = {
|
||||
{ .fd = lfd, .events = POLLIN, },
|
||||
};
|
||||
|
||||
debug("\033[2J");
|
||||
|
||||
while (1) {
|
||||
ret = poll(fds, 1, 10000);
|
||||
syscall_error(ret < 0, "poll failed");
|
||||
|
||||
if (fds[0].revents & POLLIN) {
|
||||
struct gpio_v2_line_event ev;
|
||||
ssize_t len = read(lfd, &ev, sizeof ev);
|
||||
syscall_error(len != sizeof ev, "Invalid event size");
|
||||
|
||||
// read keyboard data
|
||||
uint8_t buf[16];
|
||||
ret = read_kb(fd, buf);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
#if DEBUG
|
||||
print_bitmap(buf + 4);
|
||||
#endif
|
||||
update_keys(buf + 4);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,6 +1,25 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Pinephone keyboard userspace input device daemon.
|
||||
*
|
||||
* Copyright (C) 2021 Ondřej Jirman <megi@xff.cz>
|
||||
*
|
||||
* 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
$pmap = file_get_contents("physical-map.txt");
|
||||
$kmap = file_get_contents("factory-keymap.txt");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user