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
|
#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 1
/*
* 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");
}
while (1) {
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");
|