#include #include #include #include #include #include #include #include #include "keymap.h" /* * defines whether to access the gpios so other parts of the kernel module can * be tested on systems w/o the prerequisite GPIOs */ #define USE_GPIO 0 /* * Unsigned ints containing the BCM pin numbers for the pins used, __initdata * helps with the loading and unloading of data in memory */ static unsigned int column_pins[10] = {10, 24, 23, 22, 27, 18, 17, 15, 14, 4}; static unsigned int row_pins[3] = {3, 6, 5}; /* * GPIO labels */ static char * clabels[10] = {"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9",}; static char * rlabels[3] = {"R0", "R1", "R2"}; /* * task struct for setting up the thread */ static struct task_struct *update_task; /* * input.h input device */ struct input_dev * skey_dev; /* * Name and ID */ struct input_id skey_id; char * skey_name = "Switch Keyboard for the Omnicom"; /* *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 const struct sched_param PARAM = { .sched_priority = 45}; 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) { i++; i = i % 500; 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; int column_index = 0; while (column_index < 10) { //sets the column pin high for reading gpio_set_value(column_pins[column_index], 1); //reads with all the rows row_index = 0; while (row_index < 3) { if (gpio_get_value(row_pins[row_index])) { printk(KERN_INFO "skey: key pressed at column %d, row %d\n", column_index, row_index); } row_index++; } //sets the row pin low gpio_set_value(column_pins[column_index], 0); column_index++; } } //sleep 2000us= 2ms = 0.002 seconds usleep_range(2000, 2000); //for when the thread has to stop if (kthread_should_stop()) { break; } } return 0; } /* * This function is called when the module is loaded * __init functions similar to __initdata */ static int __init skey_init (void) { printk(KERN_INFO "skey: module initiating...\n"); if (USE_GPIO) { int i = 0; //claims the column pins while (i < 10) { //checks to see if the gpios are valid if (!gpio_is_valid(column_pins[i])) { printk(KERN_ALERT "skey: column_pins[%d], BCM %d request invalid\n", i, column_pins[i]); return -EINVAL; } //requests access of the GPIO if (gpio_request_one(column_pins[i], GPIOF_OUT_INIT_LOW ,clabels[i])) { printk(KERN_ALERT "skey: column_pins[%d], BCM %d request failed\n", i, column_pins[i]); return -EINVAL; } if (gpio_direction_output(column_pins[i], 0)) { printk(KERN_ALERT "skey: column_pins[%d], BCM %d, set output failed\n",i, column_pins[i]); } i++; } //ditto for row pins int j = 0; while (j < 3) { //checks to see if the gpios are valid if (!gpio_is_valid(row_pins[j])) { printk(KERN_ALERT "skey: row_pins[%d], BCM %d request invalid\n", j, row_pins[j]); return -EINVAL; } //requests access of the GPIO if (gpio_request_one(row_pins[j], GPIOF_DIR_IN ,rlabels[j])) { printk(KERN_ALERT "skey: row_pins[%d], BCM %d request failed\n", j, row_pins[j]); return -EINVAL; } if (gpio_direction_input(row_pins[j])) { printk(KERN_ALERT "skey: row_pins[%d], BCM %d, set output failed\n",j, row_pins[j]); } j++; } } else { printk(KERN_ALERT "skey: currently in test mode with gpio use disabled\n"); } /* * sets up input.h device */ skey_dev = input_allocate_device(); if (!skey_dev) { printk(KERN_ALERT "skey: unable to allocate input device\n"); return -ENOMEM; } // declares the event code and typesthe device emmits set_bit(EV_KEY, skey_dev->evbit); set_bit(EV_REL, skey_dev->evbit); set_bit(KEY_CAPSLOCK, skey_dev->keybit); /* * skey_dev->name and skey_dev->id are metadata that are required for X11 * and libinput to use skey_dev */ //sets the name of skey_dev skey_dev->name = skey_name; //sets ids skey_id.bustype = BUS_HOST; skey_id.vendor = 13398; skey_id.product = 13398; skey_id.version = 1; skey_dev->id = skey_id; // registers the device if (input_register_device(skey_dev)) { input_free_device(skey_dev); return -EINVAL; } //sets up update thread printk("skey: setting up update thread...\n"); update_task = kthread_run(skey_update_thread, NULL, "skey_update_thread"); printk(KERN_INFO "skey: module finished initiating\n"); return 0; } /* * This function is called when the module is unloaded * */ static void __exit skey_exit (void) { printk(KERN_INFO "skey: module exiting...\n"); if (USE_GPIO) { //frees column and row pins int i = 0; while (i < 10) { gpio_free(column_pins[i]); i++; } int j = 0; while (j < 3) { gpio_free(row_pins[j]); j++; } } else { printk(KERN_ALERT "skey: currently in test mode with gpio use disabled\n"); } //deletes the thread kthread_stop(update_task); //frees input device input_free_device(skey_dev); printk(KERN_INFO "skey: module exited\n"); return; } /* * Registers the init and exit functions */ module_init(skey_init); module_exit(skey_exit); /* * registers module metadata */ MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Haoran S. Diao <1339802534.kk@gmail.com>"); MODULE_DESCRIPTION("Driver for the omnicom switch keyboard"); /* *this sets the name of device files used by this module, this is used *when the kernel makes devices automatically */ MODULE_SUPPORTED_DEVICE("skeydev");