mirror of
git://projects.qi-hardware.com/iris.git
synced 2025-04-21 12:27:27 +03:00
new directory organization
This commit is contained in:
323
userspace/trendtac/trendtac-gpio.ccp
Normal file
323
userspace/trendtac/trendtac-gpio.ccp
Normal file
@@ -0,0 +1,323 @@
|
||||
#pypp 0
|
||||
// Iris: micro-kernel for a capability-based operating system.
|
||||
// boot-programs/gpio.ccp: GPIO driver, controlling all devices without special hardware.
|
||||
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "devices.hh"
|
||||
#define ARCH
|
||||
#include "arch.hh"
|
||||
|
||||
// Interval between polls for keyboard events (when keys are pressed)
|
||||
#define ALARM_INTERVAL (HZ / 50)
|
||||
|
||||
// GPIO pins for the devices (port.pin)
|
||||
|
||||
// keyboard
|
||||
// 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
|
||||
// Rows = 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7
|
||||
// For some reason, it only works if the rows are input and the columns are output.
|
||||
// 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.
|
||||
|
||||
// touchpad buttons
|
||||
// Left: 0.16
|
||||
// Right: 0.13
|
||||
// interrupts: yes, for any change.
|
||||
|
||||
// Lock leds
|
||||
// Num lock: 2.22
|
||||
// Caps lock: 0.27
|
||||
// Scroll lock: 0.9
|
||||
// interrupts: no, output only.
|
||||
|
||||
// interrupt summary
|
||||
// Port 0: pin 0, 1, 2, 3, 4, 5, 6, 7: keyboard; 13, 16: touchpad
|
||||
// Port 1: None.
|
||||
// Port 2: None.
|
||||
// Port 3: None.
|
||||
|
||||
enum event_type:
|
||||
KEYBOARD_EVENT
|
||||
TOUCHPAD_EVENT
|
||||
NUM_EVENTS
|
||||
|
||||
static Iris::Cap events[NUM_EVENTS]
|
||||
|
||||
static void event (event_type type, unsigned data):
|
||||
events[type].invoke (data)
|
||||
|
||||
static void set_cb (event_type type):
|
||||
Iris::free_cap (events[type])
|
||||
events[type] = Iris::get_arg ()
|
||||
|
||||
class DevKeyboard:
|
||||
static unsigned const encode[GPIO_KBD_NUM_COLS][GPIO_KBD_NUM_ROWS]
|
||||
unsigned keys[GPIO_KBD_NUM_COLS]
|
||||
bool scanning
|
||||
void parse (unsigned col, unsigned data):
|
||||
for unsigned row = 0; row < GPIO_KBD_NUM_ROWS; ++row:
|
||||
if (data ^ keys[col]) & (1 << row):
|
||||
unsigned code = encode[col][row]
|
||||
if data & (1 << row):
|
||||
code |= Keyboard::RELEASE
|
||||
event (KEYBOARD_EVENT, code)
|
||||
keys[col] = data
|
||||
// If any keys are pressed, scanning is required.
|
||||
if data != GPIO_KBD_ROW_MASK:
|
||||
scanning = true
|
||||
public:
|
||||
bool is_scanning ():
|
||||
return scanning
|
||||
void scan ():
|
||||
// Disable interrupts during scan.
|
||||
GPIO_GPIER (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK
|
||||
// All columns are input.
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) &= ~GPIO_KBD_COL_MASK
|
||||
int const cols[GPIO_KBD_NUM_COLS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 29 }
|
||||
unsigned dir = GPIO_GPDIR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK
|
||||
unsigned dat = GPIO_GPDR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK
|
||||
// Set scanning to false before first parse.
|
||||
scanning = false
|
||||
// Clear all pins. This is only required once, because it's done at the end of the loop as well.
|
||||
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
|
||||
for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col:
|
||||
// Set pin to output.
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
|
||||
// Delay for stabalization.
|
||||
for unsigned i = 0; i < 100; ++i:
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
|
||||
// Read the result.
|
||||
unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK
|
||||
// Generate events.
|
||||
parse (col, data)
|
||||
// Set pin to get rid of capacitance effects.
|
||||
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat | (1 << cols[col])
|
||||
// Delay to make the above trick work.
|
||||
for unsigned i = 0; i < 100; ++i:
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
|
||||
// Pin is set to input at the start of the loop.
|
||||
// set all to 0.
|
||||
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
|
||||
// set all to output.
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | GPIO_KBD_COL_MASK
|
||||
// Delay for stabalization.
|
||||
for unsigned i = 0; i < 100; ++i:
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | GPIO_KBD_COL_MASK
|
||||
// Set interrupts.
|
||||
unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT)
|
||||
for unsigned i = 0; i < 8; ++i:
|
||||
if data & (1 << i):
|
||||
gpio_irq_low (GPIO_KBD_ROW_PORT, i)
|
||||
else:
|
||||
gpio_irq_high (GPIO_KBD_ROW_PORT, i)
|
||||
// Reenable interrupts.
|
||||
GPIO_GPIER (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
|
||||
DevKeyboard ():
|
||||
// Set all columns to output without pull-ups when set as input.
|
||||
GPIO_GPPUR (GPIO_KBD_COL_PORT) &= ~GPIO_KBD_COL_MASK
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) |= GPIO_KBD_COL_MASK
|
||||
|
||||
// Set all rows to input and enable the pull-ups.
|
||||
GPIO_GPPUR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
|
||||
GPIO_GPDIR (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK
|
||||
// Initialize things in the same way as when a new callback is set up.
|
||||
send_initial ()
|
||||
void send_initial ():
|
||||
for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col:
|
||||
keys[col] = 0xff
|
||||
scan ()
|
||||
|
||||
enum Keys:
|
||||
N0, N1, N2, N3, N4, N5, N6, N7, N8, N9
|
||||
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
|
||||
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10
|
||||
SPACE, TAB, ENTER, LBRACE, RBRACE, COMMA, PERIOD, MINUS, EQUAL, SLASH, BACKSLASH, SEMICOLON, EXTRA, BACKQUOTE, QUOTE
|
||||
UP, DOWN, LEFT, RIGHT
|
||||
ESC, INSERT, DELETE, BACKSPACE, PAUSE, FN, ZZZ, MENU, SYSRQ
|
||||
LSHIFT, RSHIFT, CTRL, ALT
|
||||
CAPS, NUM
|
||||
NONE = ~Keyboard::RELEASE
|
||||
|
||||
unsigned const DevKeyboard::encode[GPIO_KBD_NUM_COLS][GPIO_KBD_NUM_ROWS] = {
|
||||
{ PAUSE, NONE, NONE, NONE, NONE, NONE, CTRL, F5 },
|
||||
{ Q, TAB, A, ESC, Z, NONE, BACKQUOTE, N1 },
|
||||
{ W, CAPS, S, EXTRA, X, NONE, NONE, N2 },
|
||||
{ E, F3, D, F4, C, NONE, NONE, N3 },
|
||||
{ R, T, F, G, V, B, N5, N4 },
|
||||
{ U, Y, J, H, M, N, N6, N7 },
|
||||
{ I, RBRACE, K, F6, COMMA, NONE, EQUAL, N8 },
|
||||
{ O, F7, L, NONE, PERIOD, MENU, F8, N9 },
|
||||
{ NONE, NONE, NONE, SPACE, NUM, NONE, DELETE, NONE },
|
||||
{ NONE, BACKSPACE, NONE, NONE, ENTER, NONE, F9, NONE },
|
||||
{ NONE, NONE, NONE, ALT, NONE, NONE, NONE, SYSRQ },
|
||||
{ P, LBRACE, SEMICOLON, QUOTE, BACKSLASH, SLASH, MINUS, N0 },
|
||||
{ NONE, ZZZ, NONE, NONE, NONE, NONE, NONE, F10 },
|
||||
{ NONE, NONE, NONE, NONE, NONE, NONE, F2, NONE },
|
||||
{ NONE, NONE, NONE, NONE, NONE, NONE, INSERT, NONE },
|
||||
{ NONE, NONE, UP, DOWN, LEFT, RIGHT, NONE, NONE },
|
||||
{ NONE, LSHIFT, RSHIFT, NONE, NONE, NONE, F1, FN }}
|
||||
|
||||
class Touchpad:
|
||||
unsigned old_state
|
||||
public:
|
||||
void check_events ():
|
||||
unsigned state = GPIO_GPDR (GPIO_TP_LEFT_PORT)
|
||||
if state & (1 << GPIO_TP_LEFT):
|
||||
gpio_irq_low (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT)
|
||||
if (state ^ old_state) & (1 << GPIO_TP_LEFT):
|
||||
event (TOUCHPAD_EVENT, 0)
|
||||
else:
|
||||
gpio_irq_high (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT)
|
||||
if (state ^ old_state) & (1 << GPIO_TP_LEFT):
|
||||
event (TOUCHPAD_EVENT, 0 | Keyboard::RELEASE)
|
||||
if state & (1 << GPIO_TP_RIGHT):
|
||||
gpio_irq_low (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT)
|
||||
if (state ^ old_state) & (1 << GPIO_TP_RIGHT):
|
||||
event (TOUCHPAD_EVENT, 1)
|
||||
else:
|
||||
gpio_irq_high (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT)
|
||||
if (state ^ old_state) & (1 << GPIO_TP_RIGHT):
|
||||
event (TOUCHPAD_EVENT, 1 | Keyboard::RELEASE)
|
||||
old_state = state
|
||||
// Ack interrupts.
|
||||
//GPIO_GPFR (GPIO_TP_LEFT_PORT) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT)
|
||||
Touchpad ():
|
||||
// Set pins to input with pull-ups.
|
||||
gpio_as_input (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT)
|
||||
gpio_as_input (GPIO_TP_RIGHT_PORT, GPIO_TP_RIGHT)
|
||||
GPIO_GPPUR (0) |= (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT)
|
||||
// See if they are already pressed. Also set up interrupts. This is done like when a new callback is registered.
|
||||
send_initial ()
|
||||
void send_initial ():
|
||||
old_state = 0
|
||||
check_events ()
|
||||
|
||||
class Lockleds:
|
||||
// Note that num lock is in port 2. The others are in port 0.
|
||||
public:
|
||||
Lockleds ():
|
||||
gpio_as_output (GPIO_NUM_PORT, GPIO_NUM)
|
||||
gpio_as_output (GPIO_CAPS_PORT, GPIO_CAPS)
|
||||
gpio_as_output (GPIO_SCROLL_PORT, GPIO_SCROLL)
|
||||
GPIO_GPDR (GPIO_NUM_PORT) |= 1 << GPIO_NUM
|
||||
GPIO_GPDR (GPIO_CAPS_PORT) |= 1 << GPIO_CAPS
|
||||
GPIO_GPDR (GPIO_SCROLL_PORT) |= 1 << GPIO_SCROLL
|
||||
void set (unsigned state):
|
||||
if state & 4:
|
||||
GPIO_GPDR (GPIO_NUM_PORT) &= ~(1 << GPIO_NUM)
|
||||
else:
|
||||
GPIO_GPDR (GPIO_NUM_PORT) |= 1 << GPIO_NUM
|
||||
if state & 2:
|
||||
GPIO_GPDR (GPIO_CAPS_PORT) &= ~(1 << GPIO_CAPS)
|
||||
else:
|
||||
GPIO_GPDR (GPIO_CAPS_PORT) |= 1 << GPIO_CAPS
|
||||
if state & 1:
|
||||
GPIO_GPDR (GPIO_SCROLL_PORT) &= ~(1 << GPIO_SCROLL)
|
||||
else:
|
||||
GPIO_GPDR (GPIO_SCROLL_PORT) |= 1 << GPIO_SCROLL
|
||||
|
||||
// Not really a gpio device, but it's so small, and uses gpio, so I include it here to avoid ipc.
|
||||
class Pwm:
|
||||
public:
|
||||
Pwm ():
|
||||
GPIO_GPDIR (GPIO_PWM_ENABLE_PORT) |= 1 << GPIO_PWM_ENABLE
|
||||
PWM_PER (0) = 300
|
||||
void set_backlight (unsigned level):
|
||||
if level > 300:
|
||||
level = 300
|
||||
PWM_DUT (0) = level
|
||||
if level:
|
||||
PWM_CTR (0) = 0x80
|
||||
GPIO_GPDR (GPIO_PWM_ENABLE_PORT) |= 1 << GPIO_PWM_ENABLE
|
||||
else:
|
||||
PWM_CTR (0) = 0x00
|
||||
GPIO_GPDR (GPIO_PWM_ENABLE_PORT) &= ~(1 << GPIO_PWM_ENABLE)
|
||||
// TODO: make it really work as a pwm instead of a switch; check if pwm1 is connected to anything.
|
||||
|
||||
enum codes:
|
||||
KEYBOARD = 1
|
||||
TOUCHPAD
|
||||
LOCKLEDS
|
||||
PWM
|
||||
|
||||
Iris::Num start ():
|
||||
map_gpio ()
|
||||
map_pwm0 ()
|
||||
|
||||
for unsigned i = 0; i < NUM_EVENTS; ++i:
|
||||
events[i] = Iris::alloc_cap ()
|
||||
|
||||
DevKeyboard kbd
|
||||
Touchpad tp
|
||||
Lockleds leds
|
||||
Pwm pwm
|
||||
|
||||
Iris::Cap c = Iris::my_receiver.create_capability (KEYBOARD)
|
||||
Iris::my_parent.provide_capability <Keyboard> (c.copy (), 0)
|
||||
Iris::free_cap (c)
|
||||
c = Iris::my_receiver.create_capability (TOUCHPAD)
|
||||
Iris::my_parent.provide_capability <Keyboard> (c.copy (), 1)
|
||||
Iris::free_cap (c)
|
||||
Iris::my_parent.init_done ()
|
||||
|
||||
if kbd.is_scanning ():
|
||||
Iris::my_receiver.set_alarm (ALARM_INTERVAL)
|
||||
|
||||
// Enable interrupts. All are in port 0.
|
||||
GPIO_GPIER (GPIO_KBD_ROW_PORT) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) | GPIO_KBD_ROW_MASK
|
||||
Iris::register_interrupt (IRQ_GPIO0)
|
||||
|
||||
while true:
|
||||
Iris::schedule ()
|
||||
Iris::wait ()
|
||||
switch Iris::recv.protected_data.l:
|
||||
case ~0:
|
||||
// Alarm.
|
||||
kbd.scan ()
|
||||
if kbd.is_scanning ():
|
||||
Iris::my_receiver.set_alarm (ALARM_INTERVAL)
|
||||
break
|
||||
case 0:
|
||||
// Always scan keyboard and touchpad on any interrupt.
|
||||
kbd.scan ()
|
||||
tp.check_events ()
|
||||
// Reregister the interrupt.
|
||||
Iris::register_interrupt (IRQ_GPIO0)
|
||||
break
|
||||
case KEYBOARD:
|
||||
set_cb (KEYBOARD_EVENT)
|
||||
Iris::recv.reply.invoke ()
|
||||
kbd.send_initial ()
|
||||
event (KEYBOARD_EVENT, ~0)
|
||||
break
|
||||
case TOUCHPAD:
|
||||
set_cb (TOUCHPAD_EVENT)
|
||||
Iris::recv.reply.invoke ()
|
||||
tp.send_initial ()
|
||||
event (TOUCHPAD_EVENT, ~0)
|
||||
break
|
||||
case LOCKLEDS:
|
||||
leds.set (Iris::recv.data[0].l)
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
case PWM:
|
||||
pwm.set_backlight (Iris::recv.data[0].l)
|
||||
Iris::recv.reply.invoke ()
|
||||
break
|
||||
default:
|
||||
kdebug ("invalid gpio operation ")
|
||||
kdebug_num (Iris::recv.protected_data.l)
|
||||
kdebug ("\n")
|
||||
break
|
||||
Reference in New Issue
Block a user