Root/
Source at commit 12bfb32 created 13 years 10 months ago. By Bas Wijnen, make things work with unfinished new startup procedure | |
---|---|
1 | #pypp 0 |
2 | // Iris: micro-kernel for a capability-based operating system. |
3 | // boot-programs/gpio.ccp: GPIO driver, controlling all devices without special hardware. |
4 | // Copyright 2009 Bas Wijnen <wijnen@debian.org> |
5 | // |
6 | // This program is free software: you can redistribute it and/or modify |
7 | // it under the terms of the GNU General Public License as published by |
8 | // the Free Software Foundation, either version 3 of the License, or |
9 | // (at your option) any later version. |
10 | // |
11 | // This program is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | // |
16 | // You should have received a copy of the GNU General Public License |
17 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | |
19 | #include "devices.hh" |
20 | #define ARCH |
21 | #include "arch.hh" |
22 | |
23 | // Interval between polls for keyboard events (when keys are pressed) |
24 | #define ALARM_INTERVAL (HZ / 50) |
25 | |
26 | // GPIO pins for the devices (port.pin) |
27 | |
28 | // keyboard |
29 | // Cols = 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14, 3.15, 3.29 |
30 | // Rows = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7 |
31 | // For some reason, it only works if the rows are input and the columns are output. |
32 | // interrupts: yes, with all columns set to output 0, the first key press can be detected as an interrupt; some other events also trigger interrupts. |
33 | |
34 | // touchpad buttons |
35 | // Left: 0.16 |
36 | // Right: 0.13 |
37 | // interrupts: yes, for any change. |
38 | |
39 | // Lock leds |
40 | // Num lock: 2.22 |
41 | // Caps lock: 0.27 |
42 | // Scroll lock: 0.9 |
43 | // interrupts: no, output only. |
44 | |
45 | // interrupt summary |
46 | // Port 0: pin 0, 1, 2, 3, 4, 5, 6, 7: keyboard; 13, 16: touchpad |
47 | // Port 1: None. |
48 | // Port 2: None. |
49 | // Port 3: None. |
50 | |
51 | enum event_type: |
52 | KEYBOARD_EVENT |
53 | TOUCHPAD_EVENT |
54 | NUM_EVENTS |
55 | |
56 | static Iris::Cap events[NUM_EVENTS] |
57 | |
58 | static void event (event_type type, unsigned data): |
59 | events[type].invoke (data) |
60 | |
61 | static void set_cb (event_type type): |
62 | Iris::free_cap (events[type]) |
63 | events[type] = Iris::get_arg () |
64 | |
65 | class DevKeyboard: |
66 | static unsigned const encode[GPIO_KBD_NUM_COLS][GPIO_KBD_NUM_ROWS] |
67 | unsigned keys[GPIO_KBD_NUM_COLS] |
68 | bool scanning |
69 | void parse (unsigned col, unsigned data): |
70 | for unsigned row = 0; row < GPIO_KBD_NUM_ROWS; ++row: |
71 | if (data ^ keys[col]) & (1 << row): |
72 | unsigned code = encode[col][row] |
73 | if data & (1 << row): |
74 | code |= Keyboard::RELEASE |
75 | event (KEYBOARD_EVENT, code) |
76 | keys[col] = data |
77 | // If any keys are pressed, scanning is required. |
78 | if data != GPIO_KBD_ROW_MASK: |
79 | scanning = true |
80 | public: |
81 | bool is_scanning (): |
82 | return scanning |
83 | void scan (): |
84 | // Disable interrupts during scan. |
85 | GPIO_GPIER (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK |
86 | // All columns are input. |
87 | GPIO_GPDIR (GPIO_KBD_COL_PORT) &= ~GPIO_KBD_COL_MASK |
88 | int const cols[GPIO_KBD_NUM_COLS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 29 } |
89 | unsigned dir = GPIO_GPDIR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK |
90 | unsigned dat = GPIO_GPDR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK |
91 | // Set scanning to false before first parse. |
92 | scanning = false |
93 | // Clear all pins. This is only required once, because it's done at the end of the loop as well. |
94 | GPIO_GPDR (GPIO_KBD_COL_PORT) = dat |
95 | for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col: |
96 | // Set pin to output. |
97 | GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col]) |
98 | // Delay for stabalization. |
99 | for unsigned i = 0; i < 100; ++i: |
100 | GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col]) |
101 | // Read the result. |
102 | unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK |
103 | // Generate events. |
104 | parse (col, data) |
105 | // Set pin to get rid of capacitance effects. |
106 | GPIO_GPDR (GPIO_KBD_COL_PORT) = dat | (1 << cols[col]) |
107 | // Delay to make the above trick work. |
108 | for unsigned i = 0; i < 100; ++i: |
109 | GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col]) |
110 | // Pin is set to input at the start of the loop. |
111 | // set all to 0. |
112 | GPIO_GPDR (GPIO_KBD_COL_PORT) = dat |
113 | // set all to output. |
114 | GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | GPIO_KBD_COL_MASK |
115 | // Delay for stabalization. |
116 | for unsigned i = 0; i < 100; ++i: |
117 | GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | GPIO_KBD_COL_MASK |
118 | // Set interrupts. |
119 | unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT) |
120 | for unsigned i = 0; i < 8; ++i: |
121 | if data & (1 << i): |
122 | gpio_irq_low (GPIO_KBD_ROW_PORT, i) |
123 | else: |
124 | gpio_irq_high (GPIO_KBD_ROW_PORT, i) |
125 | // Reenable interrupts. |
126 | GPIO_GPIER (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK |
127 | DevKeyboard (): |
128 | // Set all columns to output without pull-ups when set as input. |
129 | GPIO_GPPUR (GPIO_KBD_COL_PORT) &= ~GPIO_KBD_COL_MASK |
130 | GPIO_GPDIR (GPIO_KBD_COL_PORT) |= GPIO_KBD_COL_MASK |
131 | |
132 | // Set all rows to input and enable the pull-ups. |
133 | GPIO_GPPUR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK |
134 | GPIO_GPDIR (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK |
135 | // Initialize things in the same way as when a new callback is set up. |
136 | send_initial () |
137 | void send_initial (): |
138 | for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col: |
139 | keys[col] = 0xff |
140 | scan () |
141 | |
142 | enum Keys: |
143 | N0, N1, N2, N3, N4, N5, N6, N7, N8, N9 |
144 | A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z |
145 | F1, F2, F3, F4, F5, F6, F7, F8, F9, F10 |
146 | SPACE, TAB, ENTER, LBRACE, RBRACE, COMMA, PERIOD, MINUS, EQUAL, SLASH, BACKSLASH, SEMICOLON, EXTRA, BACKQUOTE, QUOTE |
147 | UP, DOWN, LEFT, RIGHT |
148 | ESC, INSERT, DELETE, BACKSPACE, PAUSE, FN, ZZZ, MENU, SYSRQ |
149 | LSHIFT, RSHIFT, CTRL, ALT |
150 | CAPS, NUM |
151 | NONE = ~Keyboard::RELEASE |
152 | |
153 | unsigned const DevKeyboard::encode[GPIO_KBD_NUM_COLS][GPIO_KBD_NUM_ROWS] = { |
154 | { PAUSE, NONE, NONE, NONE, NONE, NONE, CTRL, F5 }, |
155 | { Q, TAB, A, ESC, Z, NONE, BACKQUOTE, N1 }, |
156 | { W, CAPS, S, EXTRA, X, NONE, NONE, N2 }, |
157 | { E, F3, D, F4, C, NONE, NONE, N3 }, |
158 | { R, T, F, G, V, B, N5, N4 }, |
159 | { U, Y, J, H, M, N, N6, N7 }, |
160 | { I, RBRACE, K, F6, COMMA, NONE, EQUAL, N8 }, |
161 | { O, F7, L, NONE, PERIOD, MENU, F8, N9 }, |
162 | { NONE, NONE, NONE, SPACE, NUM, NONE, DELETE, NONE }, |
163 | { NONE, BACKSPACE, NONE, NONE, ENTER, NONE, F9, NONE }, |
164 | { NONE, NONE, NONE, ALT, NONE, NONE, NONE, SYSRQ }, |
165 | { P, LBRACE, SEMICOLON, QUOTE, BACKSLASH, SLASH, MINUS, N0 }, |
166 | { NONE, ZZZ, NONE, NONE, NONE, NONE, NONE, F10 }, |
167 | { NONE, NONE, NONE, NONE, NONE, NONE, F2, NONE }, |
168 | { NONE, NONE, NONE, NONE, NONE, NONE, INSERT, NONE }, |
169 | { NONE, NONE, UP, DOWN, LEFT, RIGHT, NONE, NONE }, |
170 | { NONE, LSHIFT, RSHIFT, NONE, NONE, NONE, F1, FN }} |
171 | |
172 | class Touchpad: |
173 | unsigned old_state |
174 | public: |
175 | void check_events (): |
176 | unsigned state = GPIO_GPDR (GPIO_TP_LEFT_PORT) |
177 | if state & (1 << GPIO_TP_LEFT): |
178 | gpio_irq_low (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) |
179 | if (state ^ old_state) & (1 << GPIO_TP_LEFT): |
180 | event (TOUCHPAD_EVENT, 0) |
181 | else: |
182 | gpio_irq_high (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) |
183 | if (state ^ old_state) & (1 << GPIO_TP_LEFT): |
184 | event (TOUCHPAD_EVENT, 0 | Keyboard::RELEASE) |
185 | if state & (1 << GPIO_TP_RIGHT): |
186 | gpio_irq_low (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) |
187 | if (state ^ old_state) & (1 << GPIO_TP_RIGHT): |
188 | event (TOUCHPAD_EVENT, 1) |
189 | else: |
190 | gpio_irq_high (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) |
191 | if (state ^ old_state) & (1 << GPIO_TP_RIGHT): |
192 | event (TOUCHPAD_EVENT, 1 | Keyboard::RELEASE) |
193 | old_state = state |
194 | // Ack interrupts. |
195 | //GPIO_GPFR (GPIO_TP_LEFT_PORT) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) |
196 | Touchpad (): |
197 | // Set pins to input with pull-ups. |
198 | gpio_as_input (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT) |
199 | gpio_as_input (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT) |
200 | GPIO_GPPUR (0) |= (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) |
201 | // See if they are already pressed. Also set up interrupts. This is done like when a new callback is registered. |
202 | send_initial () |
203 | void send_initial (): |
204 | old_state = 0 |
205 | check_events () |
206 | |
207 | class Lockleds: |
208 | // Note that num lock is in port 2. The others are in port 0. |
209 | public: |
210 | Lockleds (): |
211 | gpio_as_output (GPIO_NUM_PORT, GPIO_NUM) |
212 | gpio_as_output (GPIO_CAPS_PORT, GPIO_CAPS) |
213 | gpio_as_output (GPIO_SCROLL_PORT, GPIO_SCROLL) |
214 | GPIO_GPDR (GPIO_NUM_PORT) |= 1 << GPIO_NUM |
215 | GPIO_GPDR (GPIO_CAPS_PORT) |= 1 << GPIO_CAPS |
216 | GPIO_GPDR (GPIO_SCROLL_PORT) |= 1 << GPIO_SCROLL |
217 | void set (unsigned state): |
218 | if state & 4: |
219 | GPIO_GPDR (GPIO_NUM_PORT) &= ~(1 << GPIO_NUM) |
220 | else: |
221 | GPIO_GPDR (GPIO_NUM_PORT) |= 1 << GPIO_NUM |
222 | if state & 2: |
223 | GPIO_GPDR (GPIO_CAPS_PORT) &= ~(1 << GPIO_CAPS) |
224 | else: |
225 | GPIO_GPDR (GPIO_CAPS_PORT) |= 1 << GPIO_CAPS |
226 | if state & 1: |
227 | GPIO_GPDR (GPIO_SCROLL_PORT) &= ~(1 << GPIO_SCROLL) |
228 | else: |
229 | GPIO_GPDR (GPIO_SCROLL_PORT) |= 1 << GPIO_SCROLL |
230 | |
231 | // Not really a gpio device, but it's so small, and uses gpio, so I include it here to avoid ipc. |
232 | class Pwm: |
233 | public: |
234 | Pwm (): |
235 | GPIO_GPDIR (GPIO_PWM_ENABLE_PORT) |= 1 << GPIO_PWM_ENABLE |
236 | PWM_PER (0) = 300 |
237 | void set_backlight (unsigned level): |
238 | if level > 300: |
239 | level = 300 |
240 | PWM_DUT (0) = level |
241 | if level: |
242 | PWM_CTR (0) = 0x80 |
243 | GPIO_GPDR (GPIO_PWM_ENABLE_PORT) |= 1 << GPIO_PWM_ENABLE |
244 | else: |
245 | PWM_CTR (0) = 0x00 |
246 | GPIO_GPDR (GPIO_PWM_ENABLE_PORT) &= ~(1 << GPIO_PWM_ENABLE) |
247 | // TODO: make it really work as a pwm instead of a switch; check if pwm1 is connected to anything. |
248 | |
249 | enum codes: |
250 | KEYBOARD = 32 |
251 | TOUCHPAD |
252 | LOCKLEDS |
253 | PWM |
254 | |
255 | Iris::Num start (): |
256 | map_gpio () |
257 | map_pwm0 () |
258 | |
259 | for unsigned i = 0; i < NUM_EVENTS; ++i: |
260 | events[i] = Iris::alloc_cap () |
261 | |
262 | DevKeyboard kbd |
263 | Touchpad tp |
264 | Lockleds leds |
265 | Pwm pwm |
266 | |
267 | Iris::Cap c = Iris::my_receiver.create_capability (KEYBOARD) |
268 | Iris::my_parent.provide_device <Keyboard> (c.copy (), 0) |
269 | Iris::free_cap (c) |
270 | c = Iris::my_receiver.create_capability (TOUCHPAD) |
271 | Iris::my_parent.provide_device <Keyboard> (c.copy (), 1) |
272 | Iris::free_cap (c) |
273 | Iris::my_parent.init_done () |
274 | |
275 | if kbd.is_scanning (): |
276 | Iris::my_receiver.set_alarm (ALARM_INTERVAL) |
277 | |
278 | // Enable interrupts. All are in port 0. |
279 | GPIO_GPIER (GPIO_KBD_ROW_PORT) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) | GPIO_KBD_ROW_MASK |
280 | Iris::register_interrupt (IRQ_GPIO0) |
281 | |
282 | while true: |
283 | Iris::schedule () |
284 | Iris::wait () |
285 | switch Iris::recv.protected_data.l: |
286 | case ~0: |
287 | // Alarm. |
288 | kbd.scan () |
289 | if kbd.is_scanning (): |
290 | Iris::my_receiver.set_alarm (ALARM_INTERVAL) |
291 | break |
292 | case IRQ_GPIO0: |
293 | // Always scan keyboard and touchpad on any interrupt. |
294 | kbd.scan () |
295 | tp.check_events () |
296 | // Reregister the interrupt. |
297 | Iris::register_interrupt (IRQ_GPIO0) |
298 | break |
299 | case KEYBOARD: |
300 | set_cb (KEYBOARD_EVENT) |
301 | Iris::recv.reply.invoke () |
302 | kbd.send_initial () |
303 | event (KEYBOARD_EVENT, ~0) |
304 | break |
305 | case TOUCHPAD: |
306 | set_cb (TOUCHPAD_EVENT) |
307 | Iris::recv.reply.invoke () |
308 | tp.send_initial () |
309 | event (TOUCHPAD_EVENT, ~0) |
310 | break |
311 | case LOCKLEDS: |
312 | leds.set (Iris::recv.data[0].l) |
313 | Iris::recv.reply.invoke () |
314 | break |
315 | case PWM: |
316 | pwm.set_backlight (Iris::recv.data[0].l) |
317 | Iris::recv.reply.invoke () |
318 | break |
319 | default: |
320 | kdebug ("invalid gpio operation ") |
321 | kdebug_num (Iris::recv.protected_data.l) |
322 | kdebug ("\n") |
323 | break |
324 |
Branches:
master