mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-12-29 01:37:42 +02:00
make keyboard work a bit
This commit is contained in:
parent
283b97955d
commit
561535234d
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
kernel
|
||||
kernel.raw
|
||||
kernel.raw.gz
|
||||
iris
|
||||
iris.raw
|
||||
iris.raw.gz
|
||||
uimage
|
||||
*.o
|
||||
*.cc
|
||||
|
6
Makefile
6
Makefile
@ -23,8 +23,8 @@ LD = $(CROSS)ld
|
||||
OBJCOPY = $(CROSS)objcopy
|
||||
|
||||
headers = kernel.hh iris.h $(arch_headers)
|
||||
kernel_sources = panic.cc data.cc alloc.cc memory.cc invoke.cc schedule.cc $(arch_kernel_sources)
|
||||
BUILT_SOURCES = $(kernel_sources) $(boot_sources)
|
||||
iris_sources = panic.cc data.cc alloc.cc memory.cc invoke.cc schedule.cc $(arch_iris_sources)
|
||||
BUILT_SOURCES = $(iris_sources) $(boot_sources)
|
||||
|
||||
# Include arch-specific rules.
|
||||
include Makefile.arch
|
||||
@ -47,6 +47,6 @@ PYPP = /usr/bin/pypp
|
||||
#$(OBJCOPY) -S $(OBJCOPYFLAGS) $@
|
||||
|
||||
clean:
|
||||
rm -f *.o boot-programs/*.o kernel kernel.raw kernel.raw.gz $(BUILT_SOURCES) $(ARCH_CLEAN_FILES)
|
||||
rm -f *.o boot-programs/*.o $(BUILT_SOURCES) $(ARCH_CLEAN_FILES)
|
||||
|
||||
.PHONY: clean
|
||||
|
37
README.build
Normal file
37
README.build
Normal file
@ -0,0 +1,37 @@
|
||||
Building Iris.
|
||||
|
||||
For building, you will need:
|
||||
- A compiler to create mips binaries. Unless you run on a mips platform, which
|
||||
is unlikely, that means a cross-compiler.
|
||||
- The pythonic preprocessor, pypp.
|
||||
- Python
|
||||
- mkimage, from the Debian package uboot-mkimage.
|
||||
|
||||
The last two can simply be installed using your favorite package manager. For
|
||||
the cross compiler, please follow the instructions from
|
||||
report/cross-compiler.tex.
|
||||
|
||||
Pypp can be downloaded using
|
||||
svn co http://a82-93-13-222.adsl.xs4all.nl/svn/trunk/pypp
|
||||
|
||||
To build, it needs libshevek, which can be downloaded with
|
||||
svn co http://a82-93-13-222.adsl.xs4all.nl/svn/trunk/libshevek
|
||||
|
||||
For both of them, dpkg-buildpackage -uc -us in their directory creates a Debian
|
||||
package which can be installed. Install libshevek*deb before building pypp.
|
||||
If you are not using Debian, use
|
||||
autoreconf -f -i -s
|
||||
./configure --prefix=/usr
|
||||
make
|
||||
make install
|
||||
|
||||
When all is installed, "make" should be enough to create "uimage". To use it,
|
||||
make the first partition of an SD card (it really must be SD; MMC does not
|
||||
work) smaller than 32 MB; I use 16 MB myself. Format it as FAT and copy uimage
|
||||
to it. Then insert it in the Trendtac (it fits upside down) and boot with
|
||||
Fn+left Ctrl+left Shift pressed. When caps and scroll lock are flashing, Iris
|
||||
is booting (the flashing lights are from uboot, not from Iris, but they
|
||||
indicate that the SD image is used). Then you can release the keys.
|
||||
|
||||
If anything doesn't work, or you have other comments, please send a message to
|
||||
Bas Wijnen <wijnen@debian.org>. Thanks.
|
@ -19,9 +19,146 @@
|
||||
#ifndef __IRIS_DEVICES_HH
|
||||
#define __IRIS_DEVICES_HH
|
||||
|
||||
#include "iris.h"
|
||||
|
||||
// This shouldn't really be here. But where should it be?
|
||||
// Requests made by initial threads to tell init about themselves.
|
||||
enum init_requests:
|
||||
INIT_SET_GPIO_0
|
||||
INIT_SET_GPIO_1
|
||||
INIT_SET_LCD
|
||||
|
||||
|
||||
// Keyboard interface.
|
||||
// Startup: kbd_set_cb
|
||||
// Set the event callback. Currently pressed keys emit a key press event to the new callback immediately, plus a ~0 to signal the end of such events.
|
||||
static __inline__ void keyboard_set_cb (Capability kbd_set_cb, Capability cb):
|
||||
invoke_10 (kbd_set_cb, cb)
|
||||
// At event: the callback is called with a keycode. One bit defines if it's a press or release event.
|
||||
#define KEYBOARD_RELEASE (1 << 31)
|
||||
|
||||
|
||||
|
||||
// Display interface.
|
||||
// Startup: disp_set_eof_cb, disp_create_fb, disp_use_fb, disp_info
|
||||
|
||||
// Register an end-of-frame callback.
|
||||
// At end of frame, the callback is invoked and forgotten. It must be reregistered to keep a stream of events.
|
||||
static __inline__ void display_set_eof_cb (Capability disp_set_eof_cb, Capability cb):
|
||||
invoke_10 (disp_set_eof_cb, cb)
|
||||
|
||||
// Create a framebuffer for the display. When not in use, it can be freed by the user.
|
||||
// The pages must be cappages holding Page capabilities. They are filled by the display.
|
||||
// The passed numbers must be 0 or match a mode that the device can use.
|
||||
// The returned number is the physical address of the framebuffer. It can be used with display_use_framebuffer.
|
||||
static __inline__ unsigned display_create_framebuffer (Capability disp_create_fb, Capability page0, Capability page1 = 0, Capability page2 = 0, unsigned w = 0, unsigned h = 0, unsigned mode = 0):
|
||||
return call_n33 (disp_create_fb, page0, page1, page2, w, h, mode)
|
||||
|
||||
// Use a framebuffer. The address must have been returned from display_create_framebuffer.
|
||||
// w, h and mode must match the values given at creation time.
|
||||
// unuse_cb is called the next time this operation is requested.
|
||||
static __inline__ void display_use_framebuffer (Capability disp_use_fb, unsigned addr, Capability unuse_cb, unsigned w = 0, unsigned h = 0, unsigned mode = 0):
|
||||
invoke_11 (disp_use_fb, unuse_cb, addr)
|
||||
|
||||
// Get information about the display.
|
||||
static __inline__ void display_get_info (Capability disp_info):
|
||||
// TODO: Interface is to be designed.
|
||||
|
||||
|
||||
|
||||
// File system interface.
|
||||
// This may not be a server, so there need not be a startup phase. Instead, capabilities can implement certain interfaces: directory, file, stream, seekable file, mappable file. Normal files implement at least stream or seekable file. Directories implement directory.
|
||||
|
||||
enum File_request:
|
||||
FILE_INFO
|
||||
FILE_CLOSE
|
||||
FILE_COPY_HANDLE
|
||||
DIRECTORY_GET_SIZE
|
||||
DIRECTORY_GET_NAME
|
||||
DIRECTORY_GET_FILE
|
||||
DIRECTORY_GET_FILE_INFO
|
||||
DIRECTORY_CREATE_FILE
|
||||
DIRECTORY_DELETE_FILE
|
||||
FILE_STREAM_READ
|
||||
FILE_STREAM_WRITE
|
||||
FILE_SEEKABLE_READ
|
||||
FILE_SEEKABLE_WRITE
|
||||
FILE_SEEKABLE_TRUNCATE
|
||||
|
||||
// File interface.
|
||||
// Get information about the file.
|
||||
static __inline__ unsigned long long file_get_info (Capability file, unsigned type):
|
||||
return call_l02 (file, FILE_INFO, type)
|
||||
// Close a file. If this is a directory, it implicitly closes all files opened from it.
|
||||
static __inline__ void file_close (Capability file):
|
||||
invoke_01 (file, FILE_CLOSE)
|
||||
// Copy a file handle. This can be useful for closing all children at once. The new handle itself is a child of the original handle.
|
||||
static __inline__ Capability file_copy_handle (Capability file):
|
||||
return call_c01 (file, FILE_COPY_HANDLE)
|
||||
|
||||
// Directory interface.
|
||||
// Get the number of entries in this directory.
|
||||
static __inline__ unsigned long long directory_get_size (Capability dir):
|
||||
return call_l01 (dir, DIRECTORY_GET_SIZE)
|
||||
// Get the filename. The return value is the size of the string, the page is filled with the string itself.
|
||||
static __inline__ unsigned directory_get_name (Capability dir, unsigned long long idx, Capability page):
|
||||
return call_n13 (dir, page, DIRECTORY_GET_NAME, idx & 0xffffffff, idx >> 32)
|
||||
// Get the file.
|
||||
static __inline__ Capability directory_get_file (Capability dir, unsigned long long idx, Capability page):
|
||||
return call_c03 (dir, DIRECTORY_GET_FILE, idx & 0xffffffff, idx >> 32)
|
||||
// Get file info. This returns the same information as file_get_info, without opening the file.
|
||||
static __inline__ unsigned long long directory_get_file_info (Capability dir, unsigned long long idx, unsigned type):
|
||||
return call_l04 (dir, DIRECTORY_GET_FILE_INFO, idx & 0xffffffff, idx >> 32, type)
|
||||
// Create a new file. After this, any index may map to a different file.
|
||||
static __inline__ Capability directory_create_file (Capability dir, unsigned long long idx, Capability page):
|
||||
return call_c03 (dir, DIRECTORY_CREATE_FILE, idx & 0xffffffff, idx >> 32)
|
||||
// Delete a file. After this, any index may map to a different file.
|
||||
static __inline__ Capability directory_delete_file (Capability dir, unsigned long long idx, Capability page):
|
||||
return call_c03 (dir, DIRECTORY_DELETE_FILE, idx & 0xffffffff, idx >> 32)
|
||||
|
||||
// Stream interface.
|
||||
// Try to read size bytes. Returns the number of bytes successfully read. It cannot be more than PAGE_SIZE.
|
||||
static __inline__ unsigned file_stream_read (Capability file, Capability page, unsigned size):
|
||||
return call_n12 (file, page, FILE_STREAM_READ, size)
|
||||
// Try to write size bytes. Returns the number of bytes successfully written. It cannot be more than PAGE_SIZE.
|
||||
static __inline__ unsigned file_stream_write (Capability file, Capability page, unsigned size):
|
||||
return call_n12 (file, page, FILE_STREAM_WRITE, size)
|
||||
|
||||
// Seekable file interface.
|
||||
// Try to read size bytes from position idx. Returns the number of bytes successfully read. It cannot be more than PAGE_SIZE.
|
||||
static __inline__ unsigned file_seekable_read (Capability file, Capability page, unsigned long long idx, unsigned size):
|
||||
return call_n14 (file, page, FILE_SEEKABLE_READ, idx & 0xffffffff, idx >> 32, size)
|
||||
// Try to write size bytes at position idx; the file is extended with zeroes if the write is past the end. Returns the number of bytes successfully written. It cannot be more than PAGE_SIZE.
|
||||
static __inline__ unsigned file_seekable_write (Capability file, Capability page, unsigned long long idx, unsigned size):
|
||||
return call_n14 (file, page, FILE_SEEKABLE_WRITE, idx & 0xffffffff, idx >> 32, size)
|
||||
// Truncate file to size idx. The file is extended with zeroes if it gets longer.
|
||||
static __inline__ void file_seekable_truncate (Capability file, unsigned long long idx):
|
||||
call_n03 (file, FILE_SEEKABLE_TRUNCATE, idx & 0xffffffff, idx >> 32)
|
||||
|
||||
// Mappable file interface.
|
||||
// TODO: to be designed.
|
||||
|
||||
|
||||
|
||||
// Block device interface.
|
||||
// Startup: blk_get_size, blk_get_file.
|
||||
|
||||
// Get block size.
|
||||
static __inline__ unsigned block_get_size (Capability blk_get_size):
|
||||
return call_n00 (blk_get_size)
|
||||
|
||||
// Get file capability. Returns a seekable mappable non-stream file.
|
||||
static __inline__ Capability block_get_file (Capability blk_get_file):
|
||||
call_c00 (blk_get_file)
|
||||
|
||||
|
||||
|
||||
// TODO.
|
||||
// Sound interface.
|
||||
// Usb interfaces (port, device).
|
||||
// Pointer interface. (Only movement; buttons follow keyboard interface.)
|
||||
// Network interface.
|
||||
// Camera interface.
|
||||
// Terminal interfaces.
|
||||
|
||||
#endif
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "arch.hh"
|
||||
|
||||
// Interval between polls for keyboard (when keys are pressed) and battery/power (always) events
|
||||
#define ALARM_INTERVAL (HZ / 1)
|
||||
#define ALARM_INTERVAL (HZ / 20)
|
||||
|
||||
// GPIO pins for the devices (port.pin)
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
// 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.
|
||||
// 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
|
||||
@ -90,17 +90,22 @@ enum battery_type:
|
||||
|
||||
class Keyboard:
|
||||
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 = (col << 3) | row
|
||||
if data & (1 << row):
|
||||
code |= 0x10000
|
||||
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 ():
|
||||
kdebug ("keyboard scan\n")
|
||||
// Disable interrupts during scan.
|
||||
GPIO_GPIER (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK
|
||||
// All columns are input.
|
||||
@ -108,31 +113,34 @@ class Keyboard:
|
||||
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
|
||||
unsigned data
|
||||
// Set scanning to false before first parse.
|
||||
scanning = false
|
||||
for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col:
|
||||
// clear pin
|
||||
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
|
||||
// output
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
|
||||
// Generate events of previous column. Do that now, so there is a short delay for the data to stabilize.
|
||||
if col != 0:
|
||||
parse (col - 1, data)
|
||||
else:
|
||||
// Add a short delay for stabilization.
|
||||
parse (0, keys[0])
|
||||
data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK
|
||||
for unsigned i = 0; i < 100000; ++i:
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
|
||||
unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK
|
||||
parse (col, data)
|
||||
// set pin
|
||||
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat | (1 << cols[col])
|
||||
// input
|
||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir
|
||||
parse (GPIO_KBD_NUM_COLS - 1, data)
|
||||
// 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
|
||||
// clear pending interrupts.
|
||||
// Set interrupts on change.
|
||||
unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT)
|
||||
for unsigned i = 0; i < 8; ++i:
|
||||
GPIO_GPFR (GPIO_KBD_ROW_PORT) |= 1 << i
|
||||
if data & (1 << i):
|
||||
gpio_irq_fall (GPIO_KBD_ROW_PORT, i)
|
||||
else:
|
||||
gpio_irq_rise (GPIO_KBD_ROW_PORT, i)
|
||||
// Clear pending interrupts.
|
||||
GPIO_GPFR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
|
||||
// Reenable interrupts.
|
||||
GPIO_GPIER (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
|
||||
Keyboard ():
|
||||
@ -143,9 +151,6 @@ class Keyboard:
|
||||
// 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
|
||||
// Detect interrupts on falling edge.
|
||||
for unsigned i = 0; i < GPIO_KBD_NUM_ROWS; ++i:
|
||||
gpio_irq_fall (GPIO_KBD_ROW_PORT, i)
|
||||
// Initialize matrix.
|
||||
for unsigned i = 0; i < GPIO_KBD_NUM_COLS; ++i:
|
||||
keys[i] = 0xff
|
||||
@ -174,6 +179,8 @@ class Touchpad:
|
||||
if (state ^ old_state) & (1 << GPIO_TP_RIGHT):
|
||||
event (TOUCHPAD_EVENT, 0x10001)
|
||||
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)
|
||||
@ -313,17 +320,13 @@ int main ():
|
||||
switch msg.protected_data:
|
||||
case ~0:
|
||||
// Alarm.
|
||||
kdebug ("alarm\n")
|
||||
// Periodically scan several devices.
|
||||
kbd.scan ()
|
||||
if kbd.is_scanning ():
|
||||
kbd.scan ()
|
||||
power.poll ()
|
||||
receiver_set_alarm (__my_receiver, ALARM_INTERVAL)
|
||||
break
|
||||
case IRQ_GPIO0:
|
||||
kdebug ("gpio interrupt\n")
|
||||
//unsigned irq = GPIO_GPFR (0)
|
||||
// Ack all.
|
||||
GPIO_GPFR (0) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT) | GPIO_KBD_ROW_MASK
|
||||
// Always scan keyboard and touchpad on any interrupt.
|
||||
kbd.scan ()
|
||||
tp.check_events ()
|
||||
|
166
iris.h
166
iris.h
@ -117,8 +117,8 @@ static const char *exception_name[NUM_EXCEPTION_CODES] = {
|
||||
#define CAP_THREAD_MAKE_PRIV 6
|
||||
#define CAP_THREAD_GET_TOP_MEMORY 7
|
||||
#define CAP_THREAD_REGISTER_INTERRUPT 8
|
||||
#define CAP_THREAD_ALL_RIGHTS 0x0f
|
||||
#define CAP_THREAD_ALL_PRIV_RIGHTS (CAP_THREAD_ALL_RIGHTS | (1 << CAP_THREAD_REGISTER_INTERRUPT) | (1 << CAP_THREAD_GET_TOP_MEMORY) | (1 << CAP_THREAD_MAKE_PRIV) | (1 << CAP_THREAD_ALLOC_PHYSICAL) | (1 << CAP_THREAD_PHYSICAL_ADDRESS))
|
||||
#define CAP_THREAD_ALL_RIGHTS 0x07
|
||||
#define CAP_THREAD_ALL_PRIV_RIGHTS (CAP_THREAD_ALL_RIGHTS | (1 << CAP_THREAD_REGISTER_INTERRUPT) | (1 << CAP_THREAD_GET_TOP_MEMORY) | (1 << CAP_THREAD_MAKE_PRIV) | (1 << CAP_THREAD_ALLOC_PHYSICAL) | (1 << CAP_THREAD_PHYSICAL_ADDRESS) | (1 << CAP_THREAD_ALLOC_RANGE))
|
||||
|
||||
/* These get/set_info are not arch-specific. */
|
||||
#define CAP_THREAD_INFO_PC ~0
|
||||
@ -455,6 +455,36 @@ static void call_00 (Capability c)
|
||||
call (__my_call, &msg);
|
||||
}
|
||||
|
||||
static unsigned call_n00 (Capability c)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = 0;
|
||||
msg.data[1] = 0;
|
||||
msg.data[2] = 0;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return msg.data[0];
|
||||
}
|
||||
|
||||
static Capability call_c00 (Capability c)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = 0;
|
||||
msg.data[1] = 0;
|
||||
msg.data[2] = 0;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return msg.cap[0];
|
||||
}
|
||||
|
||||
static Capability call_c01 (Capability c, unsigned d)
|
||||
{
|
||||
Message msg;
|
||||
@ -470,6 +500,21 @@ static Capability call_c01 (Capability c, unsigned d)
|
||||
return msg.cap[0];
|
||||
}
|
||||
|
||||
static unsigned long long call_l01 (Capability c, unsigned d)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d;
|
||||
msg.data[1] = 0;
|
||||
msg.data[2] = 0;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return (unsigned long long)msg.data[0] | ((unsigned long long)msg.data[1] << 32);
|
||||
}
|
||||
|
||||
static Capability call_c02 (Capability c, unsigned d0, unsigned d1)
|
||||
{
|
||||
Message msg;
|
||||
@ -485,6 +530,51 @@ static Capability call_c02 (Capability c, unsigned d0, unsigned d1)
|
||||
return msg.cap[0];
|
||||
}
|
||||
|
||||
static unsigned long long call_l02 (Capability c, unsigned d0, unsigned d1)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = 0;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return (unsigned long long)msg.data[0] | ((unsigned long long)msg.data[1] << 32);
|
||||
}
|
||||
|
||||
static Capability call_c03 (Capability c, unsigned d0, unsigned d1, unsigned d2)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = d2;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return msg.cap[0];
|
||||
}
|
||||
|
||||
static unsigned long long call_l04 (Capability c, unsigned d0, unsigned d1, unsigned d2, unsigned d3)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = d2;
|
||||
msg.data[3] = d3;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return (unsigned long long)msg.data[0] | ((unsigned long long)msg.data[1] << 32);
|
||||
}
|
||||
|
||||
static Capability call_c12 (Capability c, Capability c1, unsigned d0, unsigned d1)
|
||||
{
|
||||
Message msg;
|
||||
@ -545,6 +635,21 @@ static unsigned call_n12 (Capability c, Capability c1, unsigned d0, unsigned d1)
|
||||
return msg.data[0];
|
||||
}
|
||||
|
||||
static unsigned call_n14 (Capability c, Capability c1, unsigned d0, unsigned d1, unsigned d2, unsigned d3)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.cap[1] = c1;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = d2;
|
||||
msg.data[3] = d3;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return msg.data[0];
|
||||
}
|
||||
|
||||
static unsigned call_n02 (Capability c, unsigned d0, unsigned d1)
|
||||
{
|
||||
Message msg;
|
||||
@ -560,6 +665,22 @@ static unsigned call_n02 (Capability c, unsigned d0, unsigned d1)
|
||||
return msg.data[0];
|
||||
}
|
||||
|
||||
static Capability call_p02 (Capability c, unsigned d0, unsigned d1, unsigned *base_return)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = 0;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
*base_return = msg.data[0];
|
||||
return msg.cap[0];
|
||||
}
|
||||
|
||||
static unsigned call_n03 (Capability c, unsigned d0, unsigned d1, unsigned d2)
|
||||
{
|
||||
Message msg;
|
||||
@ -575,6 +696,22 @@ static unsigned call_n03 (Capability c, unsigned d0, unsigned d1, unsigned d2)
|
||||
return msg.data[0];
|
||||
}
|
||||
|
||||
static unsigned call_n04 (Capability c, unsigned d0, unsigned d1, unsigned d2, unsigned d3)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = d2;
|
||||
msg.data[3] = d3;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return msg.data[0];
|
||||
}
|
||||
|
||||
static unsigned call_n13 (Capability c, Capability c1, unsigned d0, unsigned d1, unsigned d2)
|
||||
{
|
||||
Message msg;
|
||||
@ -590,38 +727,21 @@ static unsigned call_n13 (Capability c, Capability c1, unsigned d0, unsigned d1,
|
||||
return msg.data[0];
|
||||
}
|
||||
|
||||
static unsigned call_n04 (Capability c, unsigned d0, unsigned d1, unsigned d2, unsigned d3)
|
||||
static unsigned call_n33 (Capability c, Capability c1, Capability c2, Capability c3, unsigned d0, unsigned d1, unsigned d2)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.cap[1] = c1;
|
||||
msg.cap[2] = c2;
|
||||
msg.cap[3] = c3;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = d2;
|
||||
msg.data[3] = d3;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
return msg.data[0];
|
||||
}
|
||||
|
||||
static Capability call_p02 (Capability c, unsigned d0, unsigned d1, unsigned *base_return)
|
||||
{
|
||||
Message msg;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = 0;
|
||||
msg.data[3] = 0;
|
||||
msg.cap[1] = 0;
|
||||
msg.cap[2] = 0;
|
||||
msg.cap[3] = 0;
|
||||
call (__my_call, &msg);
|
||||
*base_return = msg.data[0];
|
||||
return msg.cap[0];
|
||||
}
|
||||
|
||||
static Capability degrade (Capability src, unsigned mask)
|
||||
{
|
||||
return call_c02 (src, CAP_DEGRADE, mask);
|
||||
|
@ -24,7 +24,7 @@ OBJDUMP = $(CROSS)objdump
|
||||
junk = mdebug.abi32 reginfo comment pdr
|
||||
OBJCOPYFLAGS = $(addprefix --remove-section=.,$(junk))
|
||||
|
||||
arch_kernel_sources = mips/interrupts.cc mips/arch.cc
|
||||
arch_iris_sources = mips/interrupts.cc mips/arch.cc
|
||||
boot_sources = mips/init.cc
|
||||
arch_headers = mips/arch.hh mips/jz4730.hh
|
||||
boot_threads = init gpio lcd
|
||||
@ -41,14 +41,14 @@ boot-programs/charset.data: boot-programs/charset
|
||||
$< > $@
|
||||
|
||||
# Transform ':' into ';' so vim doesn't think there are errors.
|
||||
uimage: kernel.raw.gz Makefile mips/Makefile.arch
|
||||
mkimage -A MIPS -O Linux -C gzip -a $(load) -e 0xa$(shell /bin/sh -c '$(OBJDUMP) -t kernel | grep __start$$ | cut -b2-8') -n "Iris" -d $< $@ | sed -e 's/:/;/g'
|
||||
uimage: iris.raw.gz Makefile mips/Makefile.arch
|
||||
mkimage -A MIPS -O Linux -C gzip -a $(load) -e 0xa$(shell /bin/sh -c '$(OBJDUMP) -t iris | grep __start$$ | cut -b2-8') -n "Iris" -d $< $@ | sed -e 's/:/;/g'
|
||||
|
||||
%.o:%.S Makefile mips/Makefile.arch mips/arch.hh
|
||||
$(CC) $(CPPFLAGS) $(TARGET_FLAGS) -DKERNEL_STACK_SIZE=0x2000 -c $< -o $@
|
||||
|
||||
# entry.o must be the first file. boot.o must be the first of the init objects (which can be freed after loading).
|
||||
kernel: mips/entry.o $(subst .cc,.o,$(kernel_sources)) mips/boot.o $(subst .cc,.o,$(boot_sources))
|
||||
iris: mips/entry.o $(subst .cc,.o,$(iris_sources)) mips/boot.o $(subst .cc,.o,$(boot_sources))
|
||||
$(LD) --omagic -Ttext $(load) $^ -o $@
|
||||
|
||||
%.raw: %
|
||||
@ -57,4 +57,4 @@ kernel: mips/entry.o $(subst .cc,.o,$(kernel_sources)) mips/boot.o $(subst .cc,.
|
||||
%.gz: %
|
||||
gzip < $< > $@
|
||||
|
||||
ARCH_CLEAN_FILES = uimage kernel kernel.raw kernel.raw.gz $(boot_threads) mips/*.o boot-programs/charset.data
|
||||
ARCH_CLEAN_FILES = uimage $(boot_threads) mips/*.o boot-programs/charset.data iris iris.raw iris.raw.gz
|
||||
|
@ -269,16 +269,37 @@ registers and possibly other information which is different per Thread.
|
||||
\item Let Iris schedule the next process. This is not thread-specific.
|
||||
\item Get the top Memory object. This is not thread-specific. Most Threads
|
||||
are not allowed to perform this operation. It is given to the initial Threads.
|
||||
They can pass it on to Threads that need it (mostly device drivers).
|
||||
They can pass it on to Threads that need it (if any).
|
||||
\item In the same category, register a Receiver for an interrupt. Upon
|
||||
registration, the interrupt is enabled. When the interrupt arrives, the
|
||||
registered Receiver gets a message from Iris and the interrupt is disabled
|
||||
again. After the Thread has handled the interrupt, it must reregister it in
|
||||
order to enable it again.
|
||||
\item Allocate a range of contiguous physical memory. This is only relevant
|
||||
for device drivers whose device will directly access the storage, such as the
|
||||
display driver. The result of this call is that the memory is counted as used
|
||||
by the Thread, and it is reserved, but it is not returned. Instead, the
|
||||
address of physical memory is returned, and the pages need to be retrieved with
|
||||
the next operation. This capability is not present in normally created
|
||||
threads.
|
||||
\item Allocate a page of physical memory. This is used in combination with the
|
||||
previous operation to reserve a block of physical memory, and by device drivers
|
||||
to map I/O memory into their address space. There is a flag indicating whether
|
||||
this memory should be freed (ranges) or not (I/O). Users of this operation are
|
||||
trusted to handle it properly; no checks are done to ensure that no kernel
|
||||
memory is leaked, or that the allocated memory isn't used by other threads or
|
||||
the kernel. Of course, this capability is not present in normally created
|
||||
threads.
|
||||
\item Get the physical address of a page. Only device drivers need to know the
|
||||
physical address of their pages, so this operation is not available on normal
|
||||
threads.
|
||||
\item And similarly, allow these priviledged operations (or some of them) in an
|
||||
other thread. This is a property of the caller, because the target thread
|
||||
normally doesn't have the permission to do this (otherwise the call would not
|
||||
be needed). The result of this operation is a new Thread capability with all specified rights set. Normally this is inserted in a priviledged process's address space during setup, before it is run (instead of the capability which is obtained during Thread creation).
|
||||
be needed). The result of this operation is a new Thread capability with all
|
||||
specified rights set. Normally this is inserted in a priviledged process's
|
||||
address space during setup, before it is run (instead of the capability which
|
||||
is obtained during Thread creation).
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Page and Cappage}
|
||||
@ -317,4 +338,14 @@ Operations or capability objects:
|
||||
\item Get a copy of the capability.
|
||||
\end{itemize}
|
||||
|
||||
\section{Interface classes}
|
||||
Around Iris is a system of some programs to create the operating system. These
|
||||
include the device drivers. While Iris itself needs no specific interfaces
|
||||
from them, some interface classes are defined, which are used by the default
|
||||
environment. By defining classes, it is possible to let a program use any
|
||||
device of that type without needing changes to its code.
|
||||
|
||||
These definitions are in the source. A copy of the information here would only
|
||||
lead to it getting outdated.
|
||||
|
||||
\end{document}
|
||||
|
@ -578,4 +578,70 @@ Because this didn't feel good, I decided to implement the timer interrupt
|
||||
first. I copied some code for it from Linux and found, as I feared, that it
|
||||
didn't give any interrupts. I suppose the os timer isn't running either.
|
||||
|
||||
However, it wasn't as bad. I simply had a bug in my timer code; the OS timer
|
||||
does give interrupts. Checks to see the random register also showed that while
|
||||
it doesn't run as required by the mips specification, it does auto-increment as
|
||||
part of the \textit{tlbwr} instruction, which is for practical purposes just as
|
||||
good (but, I suppose, costs less power).
|
||||
|
||||
So the only clock that isn't working is the cpu counter. This means that the
|
||||
operating system timer must be used for timed interrupts, and that works fine.
|
||||
|
||||
After rereading the Linux driver for the display, I also found several things I
|
||||
had done wrong, and after fixing them it did indeed work. The display
|
||||
controller reads its data from physical memory, which means that the entire
|
||||
framebuffer needs to be a continuous part of physical memory. I had to create
|
||||
a new kernel call for this. Like the other priviledged calls, I added it to
|
||||
the Thread capability.
|
||||
|
||||
\section{Debugging made easier}
|
||||
So far, all debugging had to be done using blinking LEDs. This is slow and
|
||||
annoying. With a working display, that was no longer needed. I added a simple
|
||||
($6\cross8$) character set, and implemented a way to let the kernel send
|
||||
messages which are printed on the screen. Then I changed the response to a
|
||||
\textit{break} opcode to result in sending a character to the lcd as well. Now
|
||||
I have a textual log output, which is much better than blinking LEDs.
|
||||
|
||||
Shortly after this, I encountered a bug in the kernel allocation routines.
|
||||
This still needed to be debugged with blinking LEDs, because allocation was
|
||||
done as part of sending messages to the display driver. This was annoying, but
|
||||
now that's done as well and the text log can be used.
|
||||
|
||||
\section{Keyboard}
|
||||
The keyboard driver works mostly as I expected it to. I added interrupts on
|
||||
any change, so that it is quite normal that key changes are detected by
|
||||
interrupt, which is faster than waiting for a scan. I would like to use
|
||||
level-triggered interrupts, instead of edge-triggered. However, at the moment
|
||||
that doesn't seem to work. I don't really care for now, although this may lead
|
||||
to missed keys, so it is something to fix eventually.
|
||||
|
||||
\section{What is a terminal?}
|
||||
Now's the time to think about more high-level features. One feature I want to
|
||||
have is that a user has a session manager. This session manager can have
|
||||
access to the terminal. And it can lose it. And get it again. It gets access
|
||||
when the user logs in, and loses it when the user logs out. Having access
|
||||
means being able to use the hardware (display, keyboard, sound, etc.). The
|
||||
problem is mostly with losing the display. Getting access to the display
|
||||
happens by mapping the pages into memory. The user can then share these pages,
|
||||
and it will be impossible to revoke the access.
|
||||
|
||||
But there is a simple solution. The session manager itself is part of the
|
||||
system. It is trusted by the kernel, and will behave. It will do anything the
|
||||
user asks, as long as the system allows it. Each session manager can have its
|
||||
own frame buffer. This is even a good idea: it means that user programs will
|
||||
not have to handle things differently depending on whether the framebuffer is
|
||||
available: it is always there, it may just not be visible for the user. Then
|
||||
the rest of the problem is for the user to solve. The user may mess up their
|
||||
own display if they want. It will not harm the main control display (used by
|
||||
session managers and the login manager), or displays of other users.
|
||||
|
||||
\section{Defining interfaces}
|
||||
The next programs to write are a \textit{block device} (the mmc and sd card
|
||||
driver, or else a ramdisk), a file system, the session manager, a keyboard
|
||||
translator (interpreting modifier keys and implementing key repeat) and some
|
||||
sort of shell. These are more device \textit{class} interfaces than simply
|
||||
interfaces for these specific devices. So it's good to design an interface for
|
||||
the class, which allows other devices of the same class to use the same
|
||||
interface. Then higher level programs can use both devices without noticing.
|
||||
|
||||
\end{document}
|
||||
|
Loading…
Reference in New Issue
Block a user