1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-06-24 16:15:27 +03:00

make keyboard work a bit

This commit is contained in:
Bas Wijnen 2009-07-27 20:03:58 +02:00
parent 283b97955d
commit 561535234d
9 changed files with 454 additions and 60 deletions

6
.gitignore vendored
View File

@ -1,6 +1,6 @@
kernel
kernel.raw
kernel.raw.gz
iris
iris.raw
iris.raw.gz
uimage
*.o
*.cc

View File

@ -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
View 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.

View File

@ -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

View File

@ -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
View File

@ -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);

View File

@ -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

View File

@ -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}

View File

@ -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}