Root/
| 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 = 1 |
| 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_capability <Keyboard> (c.copy (), 0) |
| 269 | Iris::free_cap (c) |
| 270 | c = Iris::my_receiver.create_capability (TOUCHPAD) |
| 271 | Iris::my_parent.provide_capability <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 0: |
| 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
