summaryrefslogtreecommitdiff
path: root/skey.c
blob: e252cec199560507bfccd6ea9d4c633a47a6fb81 (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
#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>
/*
 * 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"};
/*
 * variables for setting up the thread
 */

static struct task_struct *update_task;
/*
 *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;
	while (1) {
		i++;
		i = i % 500;
		if (!i) {
			printk(KERN_INFO "skey: update thread printed to kernel log every 1s\n");
		}
		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 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);
	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");