mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-11-17 06:47:11 +02:00
367 lines
11 KiB
COBOL
367 lines
11 KiB
COBOL
#pypp 0
|
|
// Iris: micro-kernel for a capability-based operating system.
|
|
// boot-programs/nanonote-gpio.ccp: gpio devices on the nanonote.
|
|
// 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"
|
|
|
|
//#define QI
|
|
#define SCAN_INTERVAL HZ / 50
|
|
|
|
class DevKbd:
|
|
static unsigned const NUM_COLS = 8
|
|
static unsigned const NUM_ROWS = 8
|
|
static unsigned const COLS_PORT = 2
|
|
static unsigned const ROWS_PORT = 3
|
|
static unsigned const ALL_COLS = 0x0003fc00
|
|
static unsigned const ALL_ROWS = 0x05fc0000
|
|
static unsigned const COLS[NUM_COLS]
|
|
static unsigned const ROWS[NUM_ROWS]
|
|
static unsigned const encode[NUM_ROWS][NUM_COLS]
|
|
enum Keys:
|
|
#ifdef QI
|
|
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
|
|
TAB, BACKSLASH, QUOTE, COMMA, PERIOD, SLASH, EQUAL, SPACE
|
|
ESCAPE, ENTER, BACKSPACE
|
|
UP, DOWN, LEFT, RIGHT
|
|
CAPS, ARROW, QI, CTRL, VOLUP, VOLDOWN, SHIFT, ALT, FN
|
|
#else
|
|
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
|
|
CAPS, TAB, ENTER, ESCAPE, MP3, SPACE, BACKSPACE, EQUALS
|
|
UP, DOWN, LEFT, RIGHT
|
|
VOLUP, VOLDOWN
|
|
FN, SHIFT, CTRL, ALT
|
|
#endif
|
|
NUM_KEYS
|
|
static char const *const names[NUM_KEYS]
|
|
unsigned state[NUM_COLS]
|
|
Kernel::Cap event
|
|
bool is_active
|
|
bool is_scanning
|
|
public:
|
|
unsigned size ():
|
|
return NUM_KEYS
|
|
unsigned get_name_size (unsigned n):
|
|
if n >= NUM_KEYS:
|
|
return 0
|
|
unsigned ret = 0
|
|
for char const *p = names[n]; *p; ++p:
|
|
++ret
|
|
return ret
|
|
void send_name (unsigned n, Kernel::Cap c, Kernel::Num offset):
|
|
if n >= NUM_KEYS:
|
|
c.invoke (0, 0)
|
|
return
|
|
unsigned data[4]
|
|
char *d = (char *)data
|
|
if offset.value () < get_name_size (n):
|
|
unsigned o = offset.l & ~3;
|
|
unsigned p
|
|
for p = 0; p < 16 && names[n][p + o]; ++p:
|
|
*d++ = names[n][p + o]
|
|
for ; p < 16; ++p:
|
|
*d++ = 0
|
|
c.invoke (Kernel::Num (data[0], data[1]), Kernel::Num (data[2], data[3]))
|
|
bool scanning ():
|
|
return is_scanning
|
|
void inactive ():
|
|
if is_active:
|
|
Kernel::free_cap (event)
|
|
is_active = false
|
|
void check (unsigned col, unsigned rowdata):
|
|
for unsigned r = 0; r < NUM_ROWS; ++r:
|
|
if !((rowdata ^ state[col]) & (1 << ROWS[r])):
|
|
continue
|
|
unsigned code = encode[r][col]
|
|
if rowdata & (1 << ROWS[r]):
|
|
code |= Keyboard::RELEASE
|
|
if is_active:
|
|
event.invoke (code)
|
|
state[col] = rowdata
|
|
void delay ():
|
|
for unsigned i = 0; i < 10000; ++i:
|
|
gpio_set (0, 0)
|
|
void scan ():
|
|
unsigned r
|
|
gpio_mask_irq (ROWS_PORT, ALL_ROWS)
|
|
is_scanning = false
|
|
if !is_active:
|
|
return
|
|
for unsigned c = 0; c < NUM_COLS; ++c:
|
|
gpio_as_input (COLS_PORT, ALL_COLS & ~(1 << COLS[c]))
|
|
gpio_as_output (COLS_PORT, 1 << COLS[c])
|
|
if c > 0:
|
|
check (c - 1, r)
|
|
delay ()
|
|
else:
|
|
check (0, state[0])
|
|
delay ()
|
|
r = gpio_get_port (ROWS_PORT) & ALL_ROWS
|
|
if r != ALL_ROWS:
|
|
is_scanning = true
|
|
gpio_as_output (COLS_PORT, ALL_COLS)
|
|
check (NUM_COLS - 1, r)
|
|
delay ()
|
|
r = gpio_get_port (ROWS_PORT) & ALL_ROWS
|
|
unsigned high = 0, low = 0
|
|
for unsigned i = 0; i < NUM_ROWS; ++i:
|
|
if r & (1 << ROWS[i]):
|
|
low |= 1 << ROWS[i]
|
|
else:
|
|
high |= 1 << ROWS[i]
|
|
gpio_as_interrupt (ROWS_PORT, high, true, true)
|
|
gpio_as_interrupt (ROWS_PORT, low, false, true)
|
|
gpio_unmask_irq (ROWS_PORT, ALL_ROWS)
|
|
void active (Kernel::Cap cb):
|
|
inactive ()
|
|
event = cb
|
|
is_active = true
|
|
for unsigned c = 0; c < NUM_COLS; ++c:
|
|
state[c] = ALL_ROWS
|
|
scan ()
|
|
DevKbd ():
|
|
is_active = false
|
|
gpio_as_gpio (COLS_PORT, ALL_COLS)
|
|
gpio_as_gpio (ROWS_PORT, ALL_ROWS)
|
|
gpio_clear (COLS_PORT, ALL_COLS)
|
|
gpio_as_output (COLS_PORT, ALL_COLS)
|
|
gpio_as_input (ROWS_PORT, ALL_ROWS)
|
|
gpio_enable_pull (ROWS_PORT, ALL_ROWS)
|
|
for unsigned i = 0; i < NUM_COLS; ++i:
|
|
state[i] = ALL_ROWS
|
|
scan ()
|
|
|
|
unsigned const DevKbd::COLS[NUM_COLS] = { 10, 11, 12, 13, 14, 15, 16, 17 }
|
|
unsigned const DevKbd::ROWS[NUM_ROWS] = { 18, 19, 20, 21, 22, 23, 24, 26 }
|
|
unsigned const DevKbd::encode[NUM_ROWS][NUM_COLS] = {
|
|
#ifdef QI
|
|
{ F1, F2, F3, F4, F5, F6, F7, ~0 },
|
|
{ Q, W, E, R, T, Y, U, I },
|
|
{ A, S, D, F, G, H, J, K },
|
|
{ ESCAPE, Z, X, C, V, B, N, M },
|
|
{ TAB, CAPS, BACKSLASH, QUOTE, COMMA, PERIOD, SLASH, UP },
|
|
{ O, L, EQUAL, ARROW, SPACE, QI, CTRL, LEFT },
|
|
{ F8, P, BACKSPACE, ENTER, VOLUP, VOLDOWN, DOWN, RIGHT },
|
|
{ SHIFT, ALT, FN, ~0, ~0, ~0, ~0, ~0 }
|
|
#else
|
|
{ ESCAPE, TAB, F1, F2, F3, F4, MP3, ~0 },
|
|
{ N1, N2, N3, N4, N5, N6, N7, N8 },
|
|
{ Q, W, E, R, T, Y, U, I },
|
|
{ A, S, D, F, G, H, J, K },
|
|
{ Z, X, C, V, B, N, M, UP },
|
|
{ N9, O, L, ALT, CAPS, SPACE, EQUALS, LEFT },
|
|
{ BACKSPACE, N0, P, ENTER, VOLUP, VOLDOWN, DOWN, RIGHT },
|
|
{ FN, SHIFT, CTRL, ~0, ~0, ~0, ~0, ~0 }
|
|
#endif
|
|
}
|
|
char const *const DevKbd::names[NUM_KEYS] = {
|
|
#ifdef QI
|
|
"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",
|
|
"tab", "\\", "'", ",", ".", "/", "=", "space",
|
|
"escape", "enter", "backspace",
|
|
"up", "down", "left", "right",
|
|
"caps lock", "arrow", "qi", "left control", "volume up", "volume down", "left shift", "left alt", "fn"
|
|
#else
|
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
|
"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",
|
|
"caps lock", "tab", "enter", "escape", "mp3", "space", "backspace", "=",
|
|
"up", "down", "left", "right",
|
|
"volume up", "volume down",
|
|
"fn", "left shift", "left control", "left alt"
|
|
#endif
|
|
}
|
|
|
|
class PowerButton:
|
|
bool state, started
|
|
Kernel::Cap cb
|
|
public:
|
|
void scan ():
|
|
gpio_mask_irq (3, 1 << 29)
|
|
bool s = gpio_get_port (3) & (1 << 29)
|
|
if s != state:
|
|
state = s
|
|
cb.invoke (state ? Keyboard::RELEASE : 0)
|
|
gpio_as_interrupt (3, 1 << 29, !state, true)
|
|
gpio_unmask_irq (3, 1 << 29)
|
|
PowerButton ():
|
|
gpio_as_gpio (3, 29)
|
|
state = true
|
|
started = false
|
|
void set_cb (Kernel::Cap c):
|
|
if started:
|
|
Kernel::free_cap (cb)
|
|
else:
|
|
started = true
|
|
cb = c
|
|
state = true
|
|
scan ()
|
|
|
|
enum codes:
|
|
KBD_DEV = 32
|
|
KBD_LIST
|
|
PWR
|
|
|
|
Kernel::Num start ():
|
|
map_gpio ()
|
|
map_tcu ()
|
|
|
|
DevKbd kbd
|
|
PowerButton pwr
|
|
|
|
Device dev = Kernel::my_receiver.create_capability (KBD_DEV)
|
|
Keyboard pw = Kernel::my_receiver.create_capability (PWR)
|
|
Kernel::my_parent.ocall (dev.copy (), Kernel::Num (Keyboard::ID, 0))
|
|
Kernel::my_parent.ocall (pw.copy (), Kernel::Num (Keyboard::ID, 1))
|
|
Kernel::free_cap (dev)
|
|
Kernel::free_cap (pw)
|
|
if kbd.scanning ():
|
|
Kernel::my_receiver.set_alarm (SCAN_INTERVAL)
|
|
unsigned user (~0)
|
|
unsigned next_user (0)
|
|
List <String> list = List <String> (Kernel::my_receiver.create_capability (KBD_LIST))
|
|
Kernel::register_interrupt (IRQ_GPIO3)
|
|
while true:
|
|
Kernel::wait ()
|
|
switch Kernel::recv.protected_data.h:
|
|
case ~0:
|
|
// Alarm.
|
|
kbd.scan ()
|
|
if kbd.scanning ():
|
|
Kernel::my_receiver.set_alarm (SCAN_INTERVAL)
|
|
break
|
|
case 0:
|
|
switch Kernel::recv.protected_data.l:
|
|
case IRQ_GPIO3:
|
|
// Interrupt.
|
|
pwr.scan ()
|
|
kbd.scan ()
|
|
if kbd.scanning ():
|
|
Kernel::my_receiver.set_alarm (SCAN_INTERVAL)
|
|
Kernel::register_interrupt (IRQ_GPIO3)
|
|
break
|
|
case PWR:
|
|
// Power button request.
|
|
switch Kernel::recv.data[0].l:
|
|
case Keyboard::SET_CB:
|
|
pwr.set_cb (Kernel::get_arg ())
|
|
Kernel::recv.reply.invoke ()
|
|
break
|
|
default:
|
|
kdebug ("power button invalid request\n")
|
|
break
|
|
break
|
|
case KBD_DEV:
|
|
// Keyboard device control request.
|
|
switch Kernel::recv.data[0].l:
|
|
case Device::CREATE_USER:
|
|
kdebug ("create\n")
|
|
Kernel::Cap reply = Kernel::get_reply ()
|
|
Keyboard cap = Kernel::my_receiver.create_capability (Kernel::Num (next_user++, KBD_DEV))
|
|
reply.invoke (0, 0, cap.copy ())
|
|
Kernel::free_cap (cap)
|
|
Kernel::free_cap (reply)
|
|
break
|
|
case Device::DESTROY_USER:
|
|
kdebug ("destroy\n")
|
|
Kernel::recv.reply.invoke ()
|
|
break
|
|
case Device::UNUSE:
|
|
kdebug ("unuse\n")
|
|
kbd.inactive ()
|
|
Kernel::recv.reply.invoke ()
|
|
break
|
|
case Device::USE:
|
|
kdebug ("use\n")
|
|
Kernel::Cap reply = Kernel::get_reply ()
|
|
user = Kernel::my_receiver.get_protected (Kernel::recv.arg).l
|
|
reply.invoke ()
|
|
Kernel::free_cap (reply)
|
|
break
|
|
default:
|
|
kdebug ("other dev:")
|
|
kdebug_num (Kernel::recv.data[0].l)
|
|
kdebug ("\n")
|
|
break
|
|
break
|
|
case KBD_LIST:
|
|
// Keyboard name lookup.
|
|
switch Kernel::recv.data[0].l:
|
|
case Kernel::Caps::GET:
|
|
Kernel::Cap reply = Kernel::get_reply ()
|
|
unsigned num = Kernel::recv.data[1].l
|
|
Kernel::Cap name = Kernel::my_receiver.create_capability (Kernel::Num (num, KBD_LIST))
|
|
reply.invoke (kbd.get_name_size (num), 0, name.copy ())
|
|
Kernel::free_cap (name)
|
|
Kernel::free_cap (reply)
|
|
break
|
|
case Kernel::Caps::GET_SIZE:
|
|
Kernel::recv.reply.invoke (kbd.size ())
|
|
break
|
|
default:
|
|
kdebug ("invalid list operation\n")
|
|
break
|
|
break
|
|
default:
|
|
break
|
|
break
|
|
case KBD_DEV:
|
|
// Keyboard device user request.
|
|
if Kernel::recv.protected_data.l != user:
|
|
kdebug ("invalid user requesting\n")
|
|
Kernel::recv.reply.invoke ()
|
|
break
|
|
switch Kernel::recv.data[0].l:
|
|
case Keyboard::SET_CB:
|
|
kdebug ("set cb\n")
|
|
kbd.active (Kernel::get_arg ())
|
|
Kernel::recv.reply.invoke ()
|
|
break
|
|
case Keyboard::GET_KEYS:
|
|
kdebug ("get keys\n")
|
|
Kernel::recv.reply.invoke (0, 0, list)
|
|
break
|
|
default:
|
|
kdebug ("other\n")
|
|
break
|
|
break
|
|
case KBD_LIST:
|
|
switch Kernel::recv.data[0].l:
|
|
case String::GET_SIZE:
|
|
Kernel::recv.reply.invoke (kbd.get_name_size (Kernel::recv.protected_data.l))
|
|
break
|
|
case String::GET_CHARS:
|
|
kbd.send_name (Kernel::recv.protected_data.l, Kernel::recv.reply, Kernel::recv.data[1])
|
|
break
|
|
default:
|
|
kdebug ("invalid string operation\n")
|
|
break
|
|
break
|
|
default:
|
|
kdebug ("unknown num: ")
|
|
kdebug_num (Kernel::recv.protected_data.h)
|
|
kdebug ("\n")
|