mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-12-28 11:59:53 +02:00
524 lines
18 KiB
Plaintext
524 lines
18 KiB
Plaintext
#pypp 0
|
|
// Iris: micro-kernel for a capability-based operating system.
|
|
// boot-programs/devices.hhp: interfaces for core devices.
|
|
// Copyright 2009 Bas Wijnen <wijnen@debian.org>
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef __IRIS_DEVICES_HH
|
|
#define __IRIS_DEVICES_HH
|
|
|
|
#include "iris.hh"
|
|
|
|
namespace Iris:
|
|
// Caplist interface.
|
|
template <typename _T> //
|
|
struct Caplist : public Caps:
|
|
Caplist (Caps c = Cap ()) : Caps (c):
|
|
Caplist <_T> create (unsigned size, Memory mem = my_memory):
|
|
return Caplist <_T> (mem.create_caps (size))
|
|
void set (unsigned idx, _T value):
|
|
return Caps::set (idx, value)
|
|
_T get (unsigned idx):
|
|
return _T (Caps::get (idx))
|
|
|
|
struct _Locker_base:
|
|
enum request:
|
|
LOCK_RO = 0x2001
|
|
UNLOCK_RO
|
|
SET_CHANGE_CB
|
|
NUM
|
|
template <typename _T> //
|
|
struct Locker : public _Locker_base, public _T:
|
|
// Lock the object for reading. Multiple read locks can exist simultaneously, but no write lock can be present.
|
|
void lock_ro ():
|
|
_T::call (CAP_MASTER_DIRECT | LOCK_RO)
|
|
// Release a read-only lock.
|
|
void unlock_ro ():
|
|
_T::call (CAP_MASTER_DIRECT | UNLOCK_RO)
|
|
// Register a callback when the backing store is changed.
|
|
// The cb is invoked with data[0] set to the position of the first change and data[1] set to the length of the changed part.
|
|
// The accuracy of this is not guaranteed. Servers which only want to provide an event should set data[0] == 0, data[1] == ~0.
|
|
// The change invoke must happen before this function's reply is sent.
|
|
void set_change_cb (Listitem cb):
|
|
_T::ocall (cb, CAP_MASTER_DIRECT | SET_CHANGE_CB)
|
|
Locker ():
|
|
Locker (Cap c) : _T (c):
|
|
|
|
struct _WLocker_base:
|
|
enum request:
|
|
LOCK = _Locker_base::NUM
|
|
UNLOCK
|
|
NUM
|
|
template <typename _T> //
|
|
struct WLocker : public _WLocker_base, public _T:
|
|
// Lock the object. Write operations can only be done when the object is locked.
|
|
void lock ():
|
|
_T::call (CAP_MASTER_DIRECT | LOCK)
|
|
// Unlock the object. Write operations can only be done when the object is locked.
|
|
void unlock ():
|
|
_T::call (CAP_MASTER_DIRECT | UNLOCK)
|
|
WLocker ():
|
|
WLocker (Cap c) : _T (c):
|
|
|
|
/// A short block of data with a size and content. Any character can be stored in it (including '\0').
|
|
struct _String : public Cap:
|
|
_String (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
GET_SIZE = _WLocker_base::NUM
|
|
GET_CHARS
|
|
ID
|
|
/// Get the size of the string.
|
|
Num get_size ():
|
|
return call (CAP_MASTER_DIRECT | GET_SIZE)
|
|
/// Get exactly 16 characters. There is no alignment requirement.
|
|
char *get_chars (Num idx, char buffer[16]):
|
|
call (CAP_MASTER_DIRECT | GET_CHARS, idx)
|
|
unsigned *b = (unsigned *)buffer
|
|
b[0] = recv.data[0].l
|
|
b[1] = recv.data[0].h
|
|
b[2] = recv.data[1].l
|
|
b[3] = recv.data[1].h
|
|
return buffer
|
|
typedef Locker <_String> String
|
|
|
|
/// A block of data with a size and content. Any character can be stored in it (including '\0').
|
|
struct _Block : public Cap:
|
|
_Block (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
GET_SIZE = _String::ID
|
|
GET_ALIGN_BITS
|
|
GET_BLOCK
|
|
ID
|
|
/// Get the size of the block.
|
|
Num get_size ():
|
|
return call (CAP_MASTER_DIRECT | GET_SIZE)
|
|
/// Get the number of bits that page accesses must be aligned to. Cannot be more than PAGE_BITS.
|
|
unsigned get_align_bits ():
|
|
return call (CAP_MASTER_DIRECT | GET_ALIGN_BITS).l
|
|
/// Helper function for get_block.
|
|
static Page _create_paying_page ():
|
|
Page ret = my_memory.create_page ()
|
|
ret.set_flags (Page::PAYING)
|
|
return ret
|
|
/// Get a block from the block; place it at offset on page. This need not be implemented for blocks smaller than PAGE_SIZE. All arguments must be aligned. If size is PAGE_SIZE, the caller may lose the frame in the transaction.
|
|
Cap get_block (Num idx, unsigned size = PAGE_SIZE, unsigned offset = 0, Page ret = _create_paying_page ()):
|
|
ocall (ret, Iris::Num (CAP_MASTER_DIRECT | GET_BLOCK, size << 16 | offset), idx)
|
|
return ret
|
|
typedef Locker <_Block> Block
|
|
|
|
/// A writable String.
|
|
struct _WString : public String:
|
|
_WString (Cap c = Cap ()) : String (c):
|
|
enum request:
|
|
TRUNCATE = _Block::ID
|
|
SET_CHARS
|
|
ID
|
|
/// Set the size of the string. Strings may have a limit to this setting.
|
|
void truncate (Num size):
|
|
call (CAP_MASTER_DIRECT | TRUNCATE, size)
|
|
/// Set exactly 4 characters. If the size is shorter than idx + 4, the extra characters are ignored.
|
|
void set_chars (Num idx, char buffer[4]):
|
|
call (Num (CAP_MASTER_DIRECT | SET_CHARS, *(unsigned *)buffer), idx)
|
|
typedef WLocker <_WString> WString
|
|
|
|
/// A writable Block.
|
|
struct _WBlock : public Block:
|
|
_WBlock (Cap c = Cap ()) : Block (c):
|
|
enum request:
|
|
TRUNCATE = _WString::TRUNCATE
|
|
SET_BLOCK = _WString::ID
|
|
ID
|
|
/// Set the size of the block. This setting may have a limited range, or not be supported at all.
|
|
void truncate (Num size):
|
|
call (CAP_MASTER_DIRECT | TRUNCATE, size)
|
|
/// Overwrite a block from the block with data at offset on the page. All arguments must be aligned. If size is PAGE_SIZE, the caller may lose the frame in the transaction. Only the specified part of the page is used for overwriting data.
|
|
void set_block (Num idx, Page page, unsigned size = PAGE_SIZE, unsigned offset = 0):
|
|
ocall (page, Iris::Num (CAP_MASTER_DIRECT | SET_BLOCK, size << 16 | offset), idx)
|
|
typedef WLocker <_WBlock> WBlock
|
|
|
|
// This interface allows another kernel to be booted by Iris.
|
|
struct Boot : public Cap:
|
|
Boot (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
BOOT = _WBlock::ID
|
|
ID
|
|
// Boot a new kernel.
|
|
void boot (String code, unsigned load, unsigned entry):
|
|
ocall (code, CAP_MASTER_DIRECT | BOOT, Iris::Num (load, entry))
|
|
|
|
// Every process which wants to be switchable through a terminal must implement this interface.
|
|
struct Device : public Cap:
|
|
Device (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
RESET = Boot::ID
|
|
ID
|
|
// Reset the device. This is called by the terminal while switching owners.
|
|
void reset ():
|
|
call (CAP_MASTER_DIRECT | RESET)
|
|
|
|
struct Event : public Device:
|
|
Event (Cap c = Cap ()) : Device (c):
|
|
enum request:
|
|
SET_CB = Device::ID
|
|
ID
|
|
// Set the event callback. Pending event emit to the new callback immediately.
|
|
void set_cb (Cap cb):
|
|
ocall (cb, CAP_MASTER_DIRECT | SET_CB)
|
|
|
|
struct Elfrun : public Cap:
|
|
Elfrun (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
RUN_BLOCK = Event::ID
|
|
RUN_CAPS
|
|
ID
|
|
enum arg_pos:
|
|
PARENT_MEMORY
|
|
DATA
|
|
PARENT
|
|
Caps run_block (Memory parent_memory, Block data, Cap parent, unsigned num_slots = 8, unsigned num_caps = 32):
|
|
Caps caps = my_memory.create_caps (3)
|
|
caps.set (PARENT_MEMORY, parent_memory)
|
|
caps.set (DATA, data)
|
|
caps.set (PARENT, parent)
|
|
iocall (caps.copy (), CAP_MASTER_DIRECT | RUN_BLOCK, Num (num_slots, num_caps))
|
|
Caps ret = get_arg ()
|
|
my_memory.destroy (caps)
|
|
free_cap (caps)
|
|
return ret
|
|
Caps run_caps (Memory parent_memory, Caps data, Cap parent, unsigned pages, unsigned num_slots = 8, unsigned num_caps = 32):
|
|
Caps caps = my_memory.create_caps (3)
|
|
caps.set (PARENT_MEMORY, parent_memory)
|
|
caps.set (DATA, data)
|
|
caps.set (PARENT, parent)
|
|
iocall (caps.copy (), Num (CAP_MASTER_DIRECT | RUN_CAPS, pages), Num (num_slots, num_caps))
|
|
Caps ret = get_arg ()
|
|
my_memory.destroy (caps)
|
|
free_cap (caps)
|
|
return ret
|
|
|
|
// Interface for talking to the parent process.
|
|
struct Parent : public Cap:
|
|
Parent (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
GET_CAPABILITY = Elfrun::ID
|
|
PROVIDE_CAPABILITY
|
|
WAIT
|
|
GET_MEMORY
|
|
PROVIDE_MEMORY
|
|
INIT_DONE
|
|
EXIT
|
|
ID
|
|
// Get a handle.
|
|
template <typename _T> _T get_capability (unsigned num = 0):
|
|
icall (Num (CAP_MASTER_DIRECT | GET_CAPABILITY, num), _T::ID)
|
|
return get_arg ()
|
|
// Provide a device handle.
|
|
template <typename _T> void provide_capability (Cap cap, unsigned num = 0):
|
|
ocall (cap, Num (CAP_MASTER_DIRECT | PROVIDE_CAPABILITY, num), _T::ID)
|
|
// Wait until a device is used by the caller again.
|
|
template <typename _T> void wait (unsigned num = 0):
|
|
call (Num (CAP_MASTER_DIRECT | WAIT, num), _T::ID)
|
|
// Get memory paid for by another thread, which cannot be inspected or changed by that thread. The memory can be inspected and changed by the user (owning both threads). The call will fail when the threads are not owned by the same user.
|
|
Memory get_memory (Cap target):
|
|
iocall (target, CAP_MASTER_DIRECT | GET_MEMORY)
|
|
return get_arg ()
|
|
// Get a handle that another thread can use to call get_memory on. The actual limit on the created memory is floor(limit, thread address space limit).
|
|
Cap provide_memory (unsigned limit):
|
|
icall (CAP_MASTER_DIRECT | PROVIDE_MEMORY, limit)
|
|
return get_arg ()
|
|
// Signal the parent that the initialisation phase is over.
|
|
void init_done (Num stage = 0):
|
|
call (CAP_MASTER_DIRECT | INIT_DONE, stage)
|
|
// Exit the program. The parent does not reply, but kills the process.
|
|
void exit (Num code):
|
|
call (CAP_MASTER_DIRECT | EXIT, code)
|
|
|
|
// Keyboard interface.
|
|
struct Keyboard : public Event:
|
|
Keyboard (Cap c = Cap ()) : Event (c):
|
|
enum request:
|
|
GET_NUM_KEYS = Parent::ID
|
|
GET_KEYS
|
|
ID
|
|
// At event: the callback is called with a keycode. One bit defines if it's a press or release event.
|
|
enum constant:
|
|
RELEASE = 1 << 31
|
|
// Get the number of keys on the keyboard.
|
|
unsigned get_num_keys ():
|
|
return call (CAP_MASTER_DIRECT | GET_NUM_KEYS).l
|
|
// Get the keycodes for the keys. The reply sends 4 key codes (32 bit each).
|
|
void get_keys (unsigned first):
|
|
call (CAP_MASTER_DIRECT | GET_KEYS, first)
|
|
|
|
// Buzzer interface.
|
|
struct Buzzer : public Device:
|
|
Buzzer (Cap c = Cap ()) : Device (c):
|
|
enum request:
|
|
BEEP = Keyboard::ID
|
|
STOP
|
|
ID
|
|
// Emit a beep of specified frequency, time and volume. Volume may not be supported. If an other beep is in progress, it is aborted.
|
|
void beep (unsigned freq, unsigned ms, unsigned volume, Cap cb = Cap ()):
|
|
ocall (cb, Num (CAP_MASTER_DIRECT | BEEP, volume), Num (freq, ms))
|
|
// Abort current beep, if any.
|
|
void stop ():
|
|
call (CAP_MASTER_DIRECT | STOP)
|
|
|
|
// Display interface.
|
|
struct Display : public Device:
|
|
Display (Cap c = Cap ()) : Device (c):
|
|
enum request:
|
|
SET_EOF_CB = Buzzer::ID
|
|
MAP_FB
|
|
UNMAP_FB
|
|
GET_INFO
|
|
ID
|
|
// 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.
|
|
void set_eof_cb (Cap cb):
|
|
ocall (cb, CAP_MASTER_DIRECT | SET_EOF_CB)
|
|
// Map the framebuffer into memory.
|
|
Caps map_fb (unsigned address, Memory mem = my_memory, bool use = true):
|
|
iocall (mem, Num (CAP_MASTER_DIRECT | MAP_FB, use ? 1 : 0), address)
|
|
return get_arg ()
|
|
void unmap_fb (Caps caps):
|
|
ocall (caps, CAP_MASTER_DIRECT | UNMAP_FB)
|
|
// Get information about the display.
|
|
void get_info ():
|
|
// TODO: Interface is to be designed.
|
|
panic (0, "using undefined interface Display::get_info ()")
|
|
|
|
// Font interface.
|
|
// This can be used to turn a Display into a tty
|
|
struct Font : public Cap:
|
|
Font (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
SET_DISPLAY = Display::ID
|
|
LOAD_FONT
|
|
SETUP
|
|
AT
|
|
WRITE
|
|
GET_POS
|
|
GET_SIZE
|
|
ID
|
|
// Connect this font to new Display.
|
|
void set_display (Display d):
|
|
ocall (d, CAP_MASTER_DIRECT | SET_DISPLAY)
|
|
// Load new glyphs.
|
|
void load_font (String font):
|
|
ocall (font, CAP_MASTER_DIRECT | LOAD_FONT)
|
|
// Set some things up, like colours.
|
|
void setup ():
|
|
// TODO: Interface is to be designed.
|
|
panic (0, "using undefined interface Font::setup ()")
|
|
// Set new cursor position.
|
|
void at (int x, int y, bool delta_x = false, bool delta_y = false):
|
|
call (Num (CAP_MASTER_DIRECT | AT, delta_x + 2 * delta_y), Num (x, y))
|
|
// Write one glyph to the display.
|
|
void write (char c):
|
|
call (CAP_MASTER_DIRECT | WRITE, c)
|
|
// Write some glyphs to the display.
|
|
void write (char const *text):
|
|
for char const *t = text; *t; ++t:
|
|
invoke (CAP_MASTER_DIRECT | WRITE, *t)
|
|
// Write some glyphs to the display.
|
|
void write (char const *text, unsigned size):
|
|
for unsigned t = 0; t < size; ++t:
|
|
invoke (CAP_MASTER_DIRECT | WRITE, text[t])
|
|
void get_pos (unsigned &x, unsigned &y):
|
|
call (CAP_MASTER_DIRECT | GET_POS)
|
|
x = recv.data[0].l
|
|
y = recv.data[1].l
|
|
// Determine the size of a character.
|
|
void get_size (unsigned c, unsigned &width, unsigned &height, unsigned &baseline):
|
|
Num ret = call (CAP_MASTER_DIRECT | GET_SIZE, c)
|
|
width = recv.data[1].l
|
|
height = ret.h
|
|
baseline = ret.l
|
|
void write (unsigned num, unsigned base = 10):
|
|
char const *encode = "0123456789abcdef"
|
|
unsigned digits = 1
|
|
unsigned power = 1
|
|
while power <= num / base:
|
|
power *= base
|
|
++digits
|
|
for unsigned i = 0; i < digits; ++i:
|
|
unsigned d = num / power
|
|
write (encode[d])
|
|
num -= d * power
|
|
power /= base
|
|
#ifndef __NATIVE__
|
|
void printf (char const *f, ...):
|
|
unsigned *last = (unsigned *)&f
|
|
while *f:
|
|
if *f == '\r':
|
|
at (0, 0, false, true)
|
|
else if *f == '\n':
|
|
at (0, 16, false, true)
|
|
else if *f == '%':
|
|
++f
|
|
switch *f:
|
|
case '%':
|
|
write ('%')
|
|
break
|
|
case 'd':
|
|
++last
|
|
write (*last)
|
|
break
|
|
case 'x':
|
|
++last
|
|
write (*last, 0x10)
|
|
break
|
|
case 's':
|
|
++last
|
|
write ((char *)*last)
|
|
break
|
|
case 'c':
|
|
++last
|
|
write ((char)*last)
|
|
break
|
|
default:
|
|
panic (*f, "invalid code character in dbg format string")
|
|
else:
|
|
write (*f)
|
|
++f
|
|
#endif
|
|
|
|
// Numerical setting, such as a display backlight.
|
|
struct Setting : public Device:
|
|
Setting (Cap c = Cap ()) : Device (c):
|
|
enum request:
|
|
SET = Font::ID
|
|
GET_RANGE
|
|
ID
|
|
// Set a new value.
|
|
void set (unsigned value):
|
|
call (CAP_MASTER_DIRECT | SET, value)
|
|
// Get the maximum value for this setting. Using a higher value with SET gives undefined results.
|
|
unsigned get_range ():
|
|
return call (CAP_MASTER_DIRECT | GET_RANGE).l
|
|
|
|
// File system interface.
|
|
// filesystem-related interfaces: directory, stream, seekable.
|
|
// Normal files implement stream and/or seekable. Directories implement directory.
|
|
// Seekable is not a class, it is identical to [W]String.
|
|
|
|
// Directory interface.
|
|
struct _Directory : public Cap:
|
|
_Directory (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
GET_SIZE = Setting::ID
|
|
GET_NAME
|
|
GET_FILE_RO
|
|
GET_FILE_INFO
|
|
ID
|
|
// Get the number of entries in this directory.
|
|
Num get_size ():
|
|
return call (CAP_MASTER_DIRECT | GET_SIZE)
|
|
// Get the filename.
|
|
String get_name (Num idx):
|
|
icall (CAP_MASTER_DIRECT | GET_NAME, idx)
|
|
return get_arg ()
|
|
// Get the file.
|
|
Cap get_file_ro (Num idx):
|
|
icall (CAP_MASTER_DIRECT | GET_FILE_RO, idx)
|
|
return get_arg ()
|
|
// Get file info. TODO: define possible types.
|
|
Num get_file_info (Num idx, unsigned type):
|
|
return icall (Num (CAP_MASTER_DIRECT | GET_FILE_INFO, type), idx)
|
|
typedef Locker <_Directory> Directory
|
|
|
|
struct _WDirectory : public Directory:
|
|
_WDirectory (Cap c = Cap ()) : Directory (c):
|
|
enum request:
|
|
GET_FILE = _Directory::ID
|
|
CREATE_FILE
|
|
DELETE_FILE
|
|
ID
|
|
// Get the file.
|
|
Cap get_file (Num idx):
|
|
icall (CAP_MASTER_DIRECT | GET_FILE, idx)
|
|
return get_arg ()
|
|
// Create a new file. After this, any index may map to a different file.
|
|
Cap create_file (String name):
|
|
icall (CAP_MASTER_DIRECT | CREATE_FILE)
|
|
return get_arg ()
|
|
// Delete a file. After this, any index may map to a different file.
|
|
void delete_file (Num idx):
|
|
call (CAP_MASTER_DIRECT | DELETE_FILE, idx)
|
|
typedef WLocker <_WDirectory> WDirectory
|
|
|
|
// Stream interface.
|
|
struct Stream : public Cap:
|
|
Stream (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
READ = _WDirectory::ID
|
|
WRITE
|
|
ID
|
|
// Try to read size bytes. Returns the number of bytes successfully read.
|
|
Num read (Num size, bool block):
|
|
return icall (Num (CAP_MASTER_DIRECT | READ, block), size)
|
|
// Try to write size bytes. Returns the number of bytes successfully written.
|
|
Num write (String s, Num size):
|
|
return ocall (s, CAP_MASTER_DIRECT | WRITE, size)
|
|
|
|
struct UI : public Cap:
|
|
UI (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
GET_STATE = Stream::ID
|
|
EVENT
|
|
EXIT
|
|
ID
|
|
enum constant:
|
|
INPUT = 1 << 31
|
|
void get_state (Cap cap):
|
|
ocall (cap, CAP_MASTER_DIRECT | GET_STATE)
|
|
void event (unsigned code, Iris::Num value = 0):
|
|
call (Num (CAP_MASTER_DIRECT | EVENT, code), value)
|
|
void exit ():
|
|
call (CAP_MASTER_DIRECT | EXIT)
|
|
|
|
struct RTC : public Cap:
|
|
RTC (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
SETUP = UI::ID
|
|
GET_TIME
|
|
SET_TIME
|
|
GET_ALARM
|
|
UNSET_ALARM
|
|
SET_ALARM
|
|
ID
|
|
void setup (unsigned hz, unsigned adjc):
|
|
call (CAP_MASTER_DIRECT | SETUP, Num (hz, adjc))
|
|
unsigned get_time ():
|
|
return call (CAP_MASTER_DIRECT | GET_TIME).l
|
|
unsigned set_time (unsigned time):
|
|
return call (CAP_MASTER_DIRECT | SET_TIME, time).l
|
|
unsigned get_alarm ():
|
|
return call (CAP_MASTER_DIRECT | GET_ALARM).l
|
|
unsigned unset_alarm ():
|
|
return call (CAP_MASTER_DIRECT | UNSET_ALARM).l
|
|
unsigned set_alarm (unsigned time, Cap cb):
|
|
return ocall (cb, CAP_MASTER_DIRECT | SET_ALARM, time).l
|
|
|
|
// TODO.
|
|
// Sound interface.
|
|
// Usb interfaces (port, device).
|
|
// Pointer interface. (Only movement; buttons follow keyboard interface.)
|
|
// Network interface.
|
|
// Camera interface.
|
|
|
|
#endif
|