summaryrefslogtreecommitdiff
path: root/skey.c
diff options
context:
space:
mode:
Diffstat (limited to 'skey.c')
-rw-r--r--skey.c260
1 files changed, 239 insertions, 21 deletions
diff --git a/skey.c b/skey.c
index 917bf6c..bc94680 100644
--- a/skey.c
+++ b/skey.c
@@ -50,33 +50,251 @@ 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) {
+ //the bitmask to use for the current keymap mode
+ int keystate_bitmask;
+ //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
+ 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};
+ printk(KERN_INFO "skey: starting update thread\n");
+ //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 +308,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 +320,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;
@@ -120,7 +337,9 @@ int skey_update_thread (void *data) {
static int __init skey_init (void) {
printk(KERN_INFO "skey: module initiating...\n");
if (USE_GPIO) {
+ //indexes for row and column pin init loops
int i = 0;
+ int j = 0;
//claims the column pins
while (i < 10) {
//checks to see if the gpios are valid
@@ -138,8 +357,7 @@ static int __init skey_init (void) {
}
i++;
}
- //ditto for row pins
- int j = 0;
+ //ditto for row pins
while (j < 3) {
//checks to see if the gpios are valid
if (!gpio_is_valid(row_pins[j])) {
@@ -208,11 +426,11 @@ static void __exit skey_exit (void) {
if (USE_GPIO) {
//frees column and row pins
int i = 0;
+ int j = 0;
while (i < 10) {
gpio_free(column_pins[i]);
i++;
}
- int j = 0;
while (j < 3) {
gpio_free(row_pins[j]);
j++;