From 1966fcd11290656185a428ac41e5205c3f29d9e9 Mon Sep 17 00:00:00 2001 From: knolax <1339802534.kk@gmail.com> Date: Fri, 29 Dec 2017 15:42:27 -0500 Subject: added full functionality to the module. it now sends the correct keypresses to the input subsystem. As a reminder, i'm using git to upload code to the production system so this code hasn't been tested yet. --- skey.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 233 insertions(+), 17 deletions(-) diff --git a/skey.c b/skey.c index 917bf6c..55c86b4 100644 --- a/skey.c +++ b/skey.c @@ -50,33 +50,250 @@ static struct task_struct *update_task; */ struct input_dev * skey_dev; /* - * Name and ID + * Name and ID, for use with input.h */ struct input_id skey_id; char * skey_name = "Switch Keyboard for the Omnicom"; /* + * Array to hold the keystate for the board + * a char is assumed to be atleast 8 bits, the bit KEYSTATE_PHYS holds the previous physical + * states for detecting rising and falling edges whereas the other ones hold the previous states + * of the 3 keymaps when stickymode is turned on. + */ +char keystate[3][10]= {0}; +#define KEYSTATE_PHYS 0x1 +#define KEYSTATE_ALPHA 0x2 +#define KEYSTATE_NUMBER 0x4 +#define KEYSTATE_SPECIAL 0x8 +/* + * current keymapping,\ + * 0 - ALPHA + * 1 - NUM + * 2 - SPECIAL + * 3 - SPECIAL with mouse + */ +int current_keymap = 0; +/* + * Stickymode + */ +int stickymode = 0; +//number of pixels mousemove buttons do +#define MOUSE_DIST 5 +//same but for scrollwheel +#define WHEEL_DIST 5 +/* + * clears the state of stickymode keystates, sends a key up event if it was previously pressed + * + */ +void clearkeystate(void) { + int row_index = 0; + int column_index = 0; + int map_index = 0; //range of 0 to 2, which are KEYSTATE_ALPHA to KEYSTATE_SPECIAL + int keystate_mask; // the actual map used + while (map_index < 3) { + //gets mask from map_index + switch(map_index) { + case 0: + keystate_mask = KEYSTATE_ALPHA; + break; + case 1: + keystate_mask = KEYSTATE_NUMBER; + break; + case 2: + keystate_mask = KEYSTATE_SPECIAL; + break; + } + row_index = 0; + while (row_index < 3) { + column_index = 0; + while (column_index < 10) { + //sends a button released event if it is not sticky mode + if (keystate[row_index][column_index] & keystate_mask) { + //reports key up + input_report_key(skey_dev, skey_keymap[map_index][row_index][column_index], 0); + input_sync(skey_dev); + //sets the bit to low. + keystate[row_index][column_index] = (~keystate_mask) & keystate[row_index][column_index]; + } + column_index++; + } + row_index++; + } + map_index++; + } + return; +} +/* + * The keystates are processed as following, the current read value is compared with the + * previous one stored in KEYSTATE_PHYS to detect a rising or falling edge. for regular keys, + * the corresponding keycode from skey_keymap is sent, with the current_keymap. if stickymode + * is on, then it toggles the value stored in KEYSTATE_ALPHA, *NUMBER or *SPECIAL depending on + * current_keymap. special keys that change the keymap and the mouse position are only triggered + * on rising level, when current_keymap is 3, or special map with mouse, the KEYSTATE_SPECIAL + * mask is used. + */ +/* + * This function is called by skey_update_thread every time it reads the keyboard, and + * implements the above. returns -EINVAL if the column and row are out of bouns, but that's + * not going to happen. + */ +int processkey(int row, int column, int state) { + //sanity checks + if ((column < 10) && (column >= 0) && (row < 3) && (row >= 0)) { + } else { + /*if this happens, whatever is calling this function is not expected to handle + * this + */ + printk(KERN_ALERT "skey: keypress data sent to processkey() was invalid\n"); + return -EINVAL; + } + //finds the KEYSTATE_* bitmask to use with the current keymapping + int keystate_bitmask; + switch (current_keymap) { + case 0: + keystate_bitmask = KEYSTATE_ALPHA; + break; + case 1: + keystate_bitmask = KEYSTATE_NUMBER; + break; + case 2: + keystate_bitmask = KEYSTATE_SPECIAL; + break; + case 3: + keystate_bitmask = KEYSTATE_SPECIAL; + break; + //if current_keymap is out of range [0,3] + default: + return -EINVAL; + break; + } + + //falling edge + if ((KEYSTATE_PHYS & keystate[row][column]) && !state) { + //after the previous keystate is checked, it is updated + if (state) { + keystate[row][column] = KEYSTATE_PHYS | keystate[row][column]; + } else { + keystate[row][column] = (~KEYSTATE_PHYS) & keystate[row][column]; + } + + if (!stickymode) { + //reports key up + input_report_key(skey_dev, skey_keymap[current_keymap][row][column], 0); + input_sync(skey_dev); + + } + //rising edge + }else if (!(KEYSTATE_PHYS & keystate[row][column]) && state) { + //after the previous keystate is checked, it is updated + if (state) { + keystate[row][column] = KEYSTATE_PHYS | keystate[row][column]; + } else { + keystate[row][column] = (~KEYSTATE_PHYS) & keystate[row][column]; + } + /* + *handles keys that have special behavior, and to which stickykey do not apply + * if sticky keys don't apply then only the falling edge applies + */ + switch (skey_keymap[current_keymap][row][column]) { + case KEY_ALPHAMAP: + current_keymap = 0; + return 0; + break; + case KEY_NUMBERMAP: + current_keymap = 1; + return 0; + break; + case KEY_SPECIALMAP: + current_keymap = 2; + return 0; + break; + case KEY_STICKYMAP: + stickymode = !stickymode; + if (!stickymode) { + clearkeystate(); + } + return 0; + break; + case KEY_MS_2ND: + if (current_keymap == 2) { + current_keymap = 3; + } else if (current_keymap == 3) { + current_keymap = 2; + } + return 0; + break; + case MS_UP: + input_event(skey_dev, EV_REL, REL_Y, +MOUSE_DIST); + input_sync(skey_dev); + return 0; + break; + case MS_DOWN: + input_event(skey_dev, EV_REL, REL_Y, -MOUSE_DIST); + input_sync(skey_dev); + return 0; + break; + case MS_LEFT: + input_event(skey_dev, EV_REL, REL_X, -MOUSE_DIST); + input_sync(skey_dev); + return 0; + break; + case MS_RIGHT: + input_event(skey_dev, EV_REL, REL_X, +MOUSE_DIST); + input_sync(skey_dev); + return 0; + break; + case MS_SCRL_UP: + input_event(skey_dev, EV_REL, REL_WHEEL, +WHEEL_DIST); + input_sync(skey_dev); + return 0; + break; + case MS_SCRL_DOWN: + input_event(skey_dev, EV_REL, REL_WHEEL, -WHEEL_DIST); + input_sync(skey_dev); + return 0; + break; + } + //if it's stickymode, toggle the state stored and send corresponding vent + if (stickymode) { + //it was high before, so now it's being toggled off + if (keystate[row][column] & keystate_bitmask) { + input_report_key(skey_dev, skey_keymap[current_keymap][row][column], 0); + input_sync(skey_dev); + + keystate[row][column] = (~keystate_bitmask) & keystate[row][column]; + //it was low before, so now it's being toggled on + } else { + input_report_key(skey_dev, skey_keymap[current_keymap][row][column], 1); + input_sync(skey_dev); + keystate[row][column] = keystate_bitmask | keystate[row][column]; + + } + //otherwise just send a key down event + } else { + input_report_key(skey_dev, skey_keymap[current_keymap][row][column], 1); + input_sync(skey_dev); + } + } + return 0; +} + +/* *This function is called every timer-period to actually read the keyboard */ int skey_update_thread (void *data) { printk(KERN_INFO "skey: starting update thread\n"); //thread stuff + //thread priority const struct sched_param PARAM = { .sched_priority = 45}; + //sets the thread to be kinda realtime sched_setscheduler(current, SCHED_FIFO, &PARAM); printk(KERN_INFO "skey: started update thread\n"); if (!USE_GPIO) { printk(KERN_ALERT "skey: currently in test mode with gpio use disabled\n"); } - //a counter for testing prints - int i = 0; - int j = 0; + while (1) { - /*if (!i) { - printk(KERN_INFO "skey: update thread printed to kernel log every 1s\n"); - //reports the a button pressed - input_report_key(skey_dev,KEY_CAPSLOCK,j); - input_sync(skey_dev); - j = !j; - }*/ if (USE_GPIO) { //reads the keyboard int row_index = 0; @@ -90,9 +307,9 @@ int skey_update_thread (void *data) { column_index = 0; while (column_index < 10) { if (gpio_get_value_cansleep(column_pins[column_index])) { - printk(KERN_INFO "skey: key pressed at column %d, row %d\n", column_index, row_index); + processkey(row_index, column_index, 1); } else { - //printk(KERN_INFO "skey: key not pressed at column %d, row %d\n", column_index, row_index); + processkey(row_index, column_index, 0); } column_index++; } @@ -102,10 +319,9 @@ int skey_update_thread (void *data) { row_index++; } } - //sleep 2000us= 2ms = 0.002 seconds - //usleep_range(2000, 2000); - //sleeps = 1000000us = 1000ms = 1s - usleep_range(1000000,1000000); + //sleep 500us= .5ms = 0.0005 seconds + // 1 / 0.0005 = 2000 hz fastest, but it's really 1/0.00052 + usleep_range(500,500); //for when the thread has to stop if (kthread_should_stop()) { break; -- cgit v1.1