summaryrefslogtreecommitdiff
path: root/skey.c
blob: 427f3e46a0077a1d4d6d5e771fae1a4b67ccb1dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/sched.h>
#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 __initdata unsigned int column_pins[10] = {10, 24, 23, 22, 27, 18, 17, 15, 14, 4};
static __initdata unsigned int row_pins[3] = {3, 6, 5};
/*
 * GPIO labels
 */
static __initdata char * clabels[10] = {"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9",};
static __initdata 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;

/*
 *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);
	// 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");