mirror of
git://projects.qi-hardware.com/iris.git
synced 2025-01-01 17:42:05 +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
|
iris
|
||||||
kernel.raw
|
iris.raw
|
||||||
kernel.raw.gz
|
iris.raw.gz
|
||||||
uimage
|
uimage
|
||||||
*.o
|
*.o
|
||||||
*.cc
|
*.cc
|
||||||
|
6
Makefile
6
Makefile
@ -23,8 +23,8 @@ LD = $(CROSS)ld
|
|||||||
OBJCOPY = $(CROSS)objcopy
|
OBJCOPY = $(CROSS)objcopy
|
||||||
|
|
||||||
headers = kernel.hh iris.h $(arch_headers)
|
headers = kernel.hh iris.h $(arch_headers)
|
||||||
kernel_sources = panic.cc data.cc alloc.cc memory.cc invoke.cc schedule.cc $(arch_kernel_sources)
|
iris_sources = panic.cc data.cc alloc.cc memory.cc invoke.cc schedule.cc $(arch_iris_sources)
|
||||||
BUILT_SOURCES = $(kernel_sources) $(boot_sources)
|
BUILT_SOURCES = $(iris_sources) $(boot_sources)
|
||||||
|
|
||||||
# Include arch-specific rules.
|
# Include arch-specific rules.
|
||||||
include Makefile.arch
|
include Makefile.arch
|
||||||
@ -47,6 +47,6 @@ PYPP = /usr/bin/pypp
|
|||||||
#$(OBJCOPY) -S $(OBJCOPYFLAGS) $@
|
#$(OBJCOPY) -S $(OBJCOPYFLAGS) $@
|
||||||
|
|
||||||
clean:
|
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
|
.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
|
#ifndef __IRIS_DEVICES_HH
|
||||||
#define __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:
|
enum init_requests:
|
||||||
INIT_SET_GPIO_0
|
INIT_SET_GPIO_0
|
||||||
INIT_SET_GPIO_1
|
INIT_SET_GPIO_1
|
||||||
INIT_SET_LCD
|
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
|
#endif
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include "arch.hh"
|
#include "arch.hh"
|
||||||
|
|
||||||
// Interval between polls for keyboard (when keys are pressed) and battery/power (always) events
|
// 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)
|
// 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
|
// 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
|
// 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.
|
// 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
|
// touchpad buttons
|
||||||
// Left: 0.16
|
// Left: 0.16
|
||||||
@ -90,17 +90,22 @@ enum battery_type:
|
|||||||
|
|
||||||
class Keyboard:
|
class Keyboard:
|
||||||
unsigned keys[GPIO_KBD_NUM_COLS]
|
unsigned keys[GPIO_KBD_NUM_COLS]
|
||||||
|
bool scanning
|
||||||
void parse (unsigned col, unsigned data):
|
void parse (unsigned col, unsigned data):
|
||||||
for unsigned row = 0; row < GPIO_KBD_NUM_ROWS; ++row:
|
for unsigned row = 0; row < GPIO_KBD_NUM_ROWS; ++row:
|
||||||
if (data ^ keys[col]) & (1 << row):
|
if (data ^ keys[col]) & (1 << row):
|
||||||
unsigned code = (col << 3) | row
|
unsigned code = (col << 3) | row
|
||||||
if data & (1 << row):
|
if data & (1 << row):
|
||||||
code |= 0x10000
|
code |= KEYBOARD_RELEASE
|
||||||
event (KEYBOARD_EVENT, code)
|
event (KEYBOARD_EVENT, code)
|
||||||
keys[col] = data
|
keys[col] = data
|
||||||
|
// If any keys are pressed, scanning is required.
|
||||||
|
if data != GPIO_KBD_ROW_MASK:
|
||||||
|
scanning = true
|
||||||
public:
|
public:
|
||||||
|
bool is_scanning ():
|
||||||
|
return scanning
|
||||||
void scan ():
|
void scan ():
|
||||||
kdebug ("keyboard scan\n")
|
|
||||||
// Disable interrupts during scan.
|
// Disable interrupts during scan.
|
||||||
GPIO_GPIER (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK
|
GPIO_GPIER (GPIO_KBD_ROW_PORT) &= ~GPIO_KBD_ROW_MASK
|
||||||
// All columns are input.
|
// 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 }
|
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 dir = GPIO_GPDIR (GPIO_KBD_COL_PORT) & ~GPIO_KBD_COL_MASK
|
||||||
unsigned dat = GPIO_GPDR (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:
|
for unsigned col = 0; col < GPIO_KBD_NUM_COLS; ++col:
|
||||||
// clear pin
|
// clear pin
|
||||||
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
|
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
|
||||||
// output
|
// output
|
||||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
|
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.
|
for unsigned i = 0; i < 100000; ++i:
|
||||||
if col != 0:
|
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | (1 << cols[col])
|
||||||
parse (col - 1, data)
|
unsigned data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK
|
||||||
else:
|
parse (col, data)
|
||||||
// Add a short delay for stabilization.
|
|
||||||
parse (0, keys[0])
|
|
||||||
data = GPIO_GPDR (GPIO_KBD_ROW_PORT) & GPIO_KBD_ROW_MASK
|
|
||||||
// set pin
|
// set pin
|
||||||
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat | (1 << cols[col])
|
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat | (1 << cols[col])
|
||||||
// input
|
// input
|
||||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir
|
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir
|
||||||
parse (GPIO_KBD_NUM_COLS - 1, data)
|
|
||||||
// set all to 0.
|
// set all to 0.
|
||||||
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
|
GPIO_GPDR (GPIO_KBD_COL_PORT) = dat
|
||||||
// set all to output.
|
// set all to output.
|
||||||
GPIO_GPDIR (GPIO_KBD_COL_PORT) = dir | GPIO_KBD_COL_MASK
|
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:
|
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.
|
// Reenable interrupts.
|
||||||
GPIO_GPIER (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
|
GPIO_GPIER (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
|
||||||
Keyboard ():
|
Keyboard ():
|
||||||
@ -143,9 +151,6 @@ class Keyboard:
|
|||||||
// Set all rows to input and enable the pull-ups.
|
// Set all rows to input and enable the pull-ups.
|
||||||
GPIO_GPPUR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
|
GPIO_GPPUR (GPIO_KBD_ROW_PORT) |= GPIO_KBD_ROW_MASK
|
||||||
GPIO_GPDIR (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.
|
// Initialize matrix.
|
||||||
for unsigned i = 0; i < GPIO_KBD_NUM_COLS; ++i:
|
for unsigned i = 0; i < GPIO_KBD_NUM_COLS; ++i:
|
||||||
keys[i] = 0xff
|
keys[i] = 0xff
|
||||||
@ -174,6 +179,8 @@ class Touchpad:
|
|||||||
if (state ^ old_state) & (1 << GPIO_TP_RIGHT):
|
if (state ^ old_state) & (1 << GPIO_TP_RIGHT):
|
||||||
event (TOUCHPAD_EVENT, 0x10001)
|
event (TOUCHPAD_EVENT, 0x10001)
|
||||||
old_state = state
|
old_state = state
|
||||||
|
// Ack interrupts.
|
||||||
|
GPIO_GPFR (GPIO_TP_LEFT_PORT) = (1 << GPIO_TP_LEFT) | (1 << GPIO_TP_RIGHT)
|
||||||
Touchpad ():
|
Touchpad ():
|
||||||
// Set pins to input with pull-ups.
|
// Set pins to input with pull-ups.
|
||||||
gpio_as_input (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT)
|
gpio_as_input (GPIO_TP_LEFT_PORT, GPIO_TP_LEFT)
|
||||||
@ -313,17 +320,13 @@ int main ():
|
|||||||
switch msg.protected_data:
|
switch msg.protected_data:
|
||||||
case ~0:
|
case ~0:
|
||||||
// Alarm.
|
// Alarm.
|
||||||
kdebug ("alarm\n")
|
|
||||||
// Periodically scan several devices.
|
// Periodically scan several devices.
|
||||||
|
if kbd.is_scanning ():
|
||||||
kbd.scan ()
|
kbd.scan ()
|
||||||
power.poll ()
|
power.poll ()
|
||||||
receiver_set_alarm (__my_receiver, ALARM_INTERVAL)
|
receiver_set_alarm (__my_receiver, ALARM_INTERVAL)
|
||||||
break
|
break
|
||||||
case IRQ_GPIO0:
|
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.
|
// Always scan keyboard and touchpad on any interrupt.
|
||||||
kbd.scan ()
|
kbd.scan ()
|
||||||
tp.check_events ()
|
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_MAKE_PRIV 6
|
||||||
#define CAP_THREAD_GET_TOP_MEMORY 7
|
#define CAP_THREAD_GET_TOP_MEMORY 7
|
||||||
#define CAP_THREAD_REGISTER_INTERRUPT 8
|
#define CAP_THREAD_REGISTER_INTERRUPT 8
|
||||||
#define CAP_THREAD_ALL_RIGHTS 0x0f
|
#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))
|
#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. */
|
/* These get/set_info are not arch-specific. */
|
||||||
#define CAP_THREAD_INFO_PC ~0
|
#define CAP_THREAD_INFO_PC ~0
|
||||||
@ -455,6 +455,36 @@ static void call_00 (Capability c)
|
|||||||
call (__my_call, &msg);
|
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)
|
static Capability call_c01 (Capability c, unsigned d)
|
||||||
{
|
{
|
||||||
Message msg;
|
Message msg;
|
||||||
@ -470,6 +500,21 @@ static Capability call_c01 (Capability c, unsigned d)
|
|||||||
return msg.cap[0];
|
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)
|
static Capability call_c02 (Capability c, unsigned d0, unsigned d1)
|
||||||
{
|
{
|
||||||
Message msg;
|
Message msg;
|
||||||
@ -485,6 +530,51 @@ static Capability call_c02 (Capability c, unsigned d0, unsigned d1)
|
|||||||
return msg.cap[0];
|
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)
|
static Capability call_c12 (Capability c, Capability c1, unsigned d0, unsigned d1)
|
||||||
{
|
{
|
||||||
Message msg;
|
Message msg;
|
||||||
@ -545,6 +635,21 @@ static unsigned call_n12 (Capability c, Capability c1, unsigned d0, unsigned d1)
|
|||||||
return msg.data[0];
|
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)
|
static unsigned call_n02 (Capability c, unsigned d0, unsigned d1)
|
||||||
{
|
{
|
||||||
Message msg;
|
Message msg;
|
||||||
@ -560,6 +665,22 @@ static unsigned call_n02 (Capability c, unsigned d0, unsigned d1)
|
|||||||
return msg.data[0];
|
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)
|
static unsigned call_n03 (Capability c, unsigned d0, unsigned d1, unsigned d2)
|
||||||
{
|
{
|
||||||
Message msg;
|
Message msg;
|
||||||
@ -575,6 +696,22 @@ static unsigned call_n03 (Capability c, unsigned d0, unsigned d1, unsigned d2)
|
|||||||
return msg.data[0];
|
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)
|
static unsigned call_n13 (Capability c, Capability c1, unsigned d0, unsigned d1, unsigned d2)
|
||||||
{
|
{
|
||||||
Message msg;
|
Message msg;
|
||||||
@ -590,38 +727,21 @@ static unsigned call_n13 (Capability c, Capability c1, unsigned d0, unsigned d1,
|
|||||||
return msg.data[0];
|
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;
|
Message msg;
|
||||||
msg.cap[0] = c;
|
msg.cap[0] = c;
|
||||||
|
msg.cap[1] = c1;
|
||||||
|
msg.cap[2] = c2;
|
||||||
|
msg.cap[3] = c3;
|
||||||
msg.data[0] = d0;
|
msg.data[0] = d0;
|
||||||
msg.data[1] = d1;
|
msg.data[1] = d1;
|
||||||
msg.data[2] = d2;
|
msg.data[2] = d2;
|
||||||
msg.data[3] = d3;
|
|
||||||
msg.data[3] = 0;
|
msg.data[3] = 0;
|
||||||
msg.cap[1] = 0;
|
|
||||||
msg.cap[2] = 0;
|
|
||||||
msg.cap[3] = 0;
|
|
||||||
call (__my_call, &msg);
|
call (__my_call, &msg);
|
||||||
return msg.data[0];
|
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)
|
static Capability degrade (Capability src, unsigned mask)
|
||||||
{
|
{
|
||||||
return call_c02 (src, CAP_DEGRADE, mask);
|
return call_c02 (src, CAP_DEGRADE, mask);
|
||||||
|
@ -24,7 +24,7 @@ OBJDUMP = $(CROSS)objdump
|
|||||||
junk = mdebug.abi32 reginfo comment pdr
|
junk = mdebug.abi32 reginfo comment pdr
|
||||||
OBJCOPYFLAGS = $(addprefix --remove-section=.,$(junk))
|
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
|
boot_sources = mips/init.cc
|
||||||
arch_headers = mips/arch.hh mips/jz4730.hh
|
arch_headers = mips/arch.hh mips/jz4730.hh
|
||||||
boot_threads = init gpio lcd
|
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.
|
# Transform ':' into ';' so vim doesn't think there are errors.
|
||||||
uimage: kernel.raw.gz Makefile mips/Makefile.arch
|
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 kernel | grep __start$$ | cut -b2-8') -n "Iris" -d $< $@ | sed -e 's/:/;/g'
|
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
|
%.o:%.S Makefile mips/Makefile.arch mips/arch.hh
|
||||||
$(CC) $(CPPFLAGS) $(TARGET_FLAGS) -DKERNEL_STACK_SIZE=0x2000 -c $< -o $@
|
$(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).
|
# 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 $@
|
$(LD) --omagic -Ttext $(load) $^ -o $@
|
||||||
|
|
||||||
%.raw: %
|
%.raw: %
|
||||||
@ -57,4 +57,4 @@ kernel: mips/entry.o $(subst .cc,.o,$(kernel_sources)) mips/boot.o $(subst .cc,.
|
|||||||
%.gz: %
|
%.gz: %
|
||||||
gzip < $< > $@
|
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 Let Iris schedule the next process. This is not thread-specific.
|
||||||
\item Get the top Memory object. This is not thread-specific. Most Threads
|
\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.
|
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
|
\item In the same category, register a Receiver for an interrupt. Upon
|
||||||
registration, the interrupt is enabled. When the interrupt arrives, the
|
registration, the interrupt is enabled. When the interrupt arrives, the
|
||||||
registered Receiver gets a message from Iris and the interrupt is disabled
|
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
|
again. After the Thread has handled the interrupt, it must reregister it in
|
||||||
order to enable it again.
|
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
|
\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
|
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
|
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}
|
\end{itemize}
|
||||||
|
|
||||||
\subsection{Page and Cappage}
|
\subsection{Page and Cappage}
|
||||||
@ -317,4 +338,14 @@ Operations or capability objects:
|
|||||||
\item Get a copy of the capability.
|
\item Get a copy of the capability.
|
||||||
\end{itemize}
|
\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}
|
\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
|
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.
|
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}
|
\end{document}
|
||||||
|
Loading…
Reference in New Issue
Block a user