diff --git a/include/nim/audiooutclient.nim b/include/nim/audiooutclient.nim new file mode 100644 index 0000000..93e46a3 --- /dev/null +++ b/include/nim/audiooutclient.nim @@ -0,0 +1,151 @@ +# +# \brief Audio_out client +# \author Emery Hemingway +# \date 2017-10-15 +# + +# +# Copyright (C) 2017 Genode Labs GmbH +# +# This file is part of the Genode OS framework, which is distributed +# under the terms of the GNU Affero General Public License version 3. +# + +import genode + +const + Header = "" + PacketPeriod* = 512 + PacketQueueSize* = 256 + +type + Packet* = ptr PacketObj + PacketObj {.importcpp: "Audio_out::Packet", header: Header, pure.} = object + + PeriodBuffer* = ptr array[PacketPeriod, cfloat] + StereoPacket* = tuple + left, right: Packet + +proc content(pkt: Packet | PacketObj): PeriodBuffer {.importcpp.} +proc played(pkt: Packet | PacketObj): bool {.importcpp.} +proc valid(pkt: Packet | PacketObj): bool {.importcpp.} +proc invalidate(pkt: Packet | PacketObj) {.importcpp.} +proc mark_as_played(pkt: Packet | PacketObj) {.importcpp.} + +proc buffer*(pkt: Packet): PeriodBuffer = cast[PeriodBuffer](pkt.content()) + +proc `[]`*(pkt: Packet; i: int): float32 {.importcpp: "#->content()[#] ".} + +proc `[]=`*(pkt: Packet; i: int; x: float32) {.importcpp: "#->content()[#] = #".} + +proc `[]=`*(pkt: Packet; i: int; x: int16) {.importcpp: "#->content()[#] = # / 32768.0".} + +type + Stream = ptr StreamObj + StreamObj {.importcpp: "Audio_out::Stream", header: Header, pure.} = object + +proc pos(s: Stream): cuint {.importcpp.} +proc tail(s: Stream): cuint {.importcpp.} +proc queued(s: Stream): cuint {.importcpp.} +proc next(s: Stream): Packet {.importcpp.} +proc next(s: Stream; p: Packet): Packet {.importcpp.} +proc packet_position(s: Stream): Packet {.importcpp.} +proc packet_position(s: Stream; p: Packet): cuint {.importcpp.} +proc empty(s: Stream): bool {.importcpp.} +proc full(s: Stream): bool {.importcpp.} +proc get(s: Stream; pos: cuint): Packet {.importcpp.} +proc alloc(s: Stream): Packet {.importcpp.} +proc reset(s: Stream) {.importcpp.} +proc invalidate_all(s: Stream) {.importcpp.} +proc pos(s: Stream; p: cuint) {.importcpp.} +proc increment_position(s: Stream; p: cuint) {.importcpp.} + +type + ConnectionBase {. + importcpp: "Audio_out::Connection", header: Header.} = object + Connection = Constructible[ConnectionBase] + + AudioOutClient* = ref AudioOutClientObj + AudioOutClientObj = object + left, right: Connection + +proc construct(conn: Connection; label: cstring) {.tags: [RpcEffect], + importcpp: "#.construct(*genodeEnv, @, false, false)".} + +proc destruct(conn: Connection) {.tags: [RpcEffect], + importcpp: "#.destruct()".} + +proc stream(conn: Connection): Stream {.importcpp: "#->stream()".} + +proc start(conn: Connection) {.tags: [RpcEffect], importcpp: "#->start()".} +proc stop(conn: Connection) {.tags: [RpcEffect], importcpp: "#->stop()".} + +proc progress_sigh(conn: Connection; sig: SignalContextCapability) {.tags: [RpcEffect], + importcpp: "#->progress_sigh(#)".} + +proc alloc_sigh(conn: Connection; sig: SignalContextCapability) {.tags: [RpcEffect], + importcpp: "#->alloc_sigh(#)".} + +proc submit(conn: Connection; pkt: Packet) {.tags: [RpcEffect], + importcpp: "#->submit(#)".} + +proc newAudioOutClient*(): AudioOutClient = + new result + result.left.construct("left") + result.right.construct("right") + +proc close*(dac: AudioOutClient) {.tags: [RpcEffect].} = + destruct dac.left + destruct dac.right + +proc start*(dac: AudioOutClient) = + start dac.left + start dac.right + +proc stop*(dac: AudioOutClient) = + stop dac.left + stop dac.right + +proc setAllocSigh*(dac: AudioOutClient; cap: SignalContextCapability) = + ## Install a signal handler to detect when the packet buffer ceases to be full. + dac.left.alloc_sigh cap + +proc setProgressSigh*(dac: AudioOutClient; cap: SignalContextCapability) = + ## Install a signal handler to detect when a packet had been played. + dac.left.progress_sigh cap + +proc queued*(dac: AudioOutClient): uint = + ## Return the number of packets queued. + dac.left.stream.queued + +proc full*(dac: AudioOutClient): bool = + ## Check if the packet stream is full. + dac.left.stream.full + +iterator tailSubmission*(dac: AudioOutClient): Packet = + ## Return the packets and the end of the buffer then submit. + doAssert(not dac.full, "cannot submit packets, stream is full") + let + lp = dac.left.stream.alloc() + rp = dac.right.stream.alloc() + yield lp + yield rp + dac.left.submit lp + dac.left.submit rp + +proc next*(dac: AudioOutClient): StereoPacket = + ## Return the successive packets from the current playback position. + (dac.left.stream.next, dac.right.stream.next) + +proc next*(dac: AudioOutClient, pkts: StereoPacket): StereoPacket = + ## Return the successive packets from the packet buffers. + (dac.left.stream.next(pkts.left), dac.right.stream.next(pkts.right)) + +proc alloc*(dac: AudioOutClient): StereoPacket = + ## Return the first pair of unused packets. + (dac.left.stream.alloc(), dac.right.stream.alloc()) + +proc submit*(dac: AudioOutClient, pkts: StereoPacket) = + ## Mark a pair of packets as active and wake the servers. + dac.left.submit pkts.left + dac.right.submit pkts.right diff --git a/include/nim/genode.nim b/include/nim/genode.nim new file mode 100644 index 0000000..4b28dc0 --- /dev/null +++ b/include/nim/genode.nim @@ -0,0 +1,185 @@ +# +# \brief Base Genode support for Nim +# \author Emery Hemingway +# \date 2017-10-15 +# + +# +# Copyright (C) 2017 Genode Labs GmbH +# +# This file is part of the Genode OS framework, which is distributed +# under the terms of the GNU Affero General Public License version 3. +# + +when not defined(genode) or defined(nimdoc): + {.error: "Genode only module".} + +type RpcEffect* = object of RootEffect + ## Effect describing a synchronous client-side RPC. + +# +# C++ utilities +# + +type Constructible* {. + importcpp: "Genode::Constructible", header: "", final, pure.} [T] = object + +template defineConstructible*(Final, Base: untyped) = + type Final* = Constructible[Base] + proc construct*(obj: Final) {.importcpp: "#.construct()".} + proc construct*(obj: ref Final) {.importcpp: "(*#).construct()".} + proc destruct*(obj: Final) {.importcpp: "#.destruct()".} + proc destruct*(obj: ref Final) {.importcpp: "(*#).destruct()".} + +# +# Signals +# + +const SignalsH = "nim/signals.h" + +type + SignalContextCapability* {. + importcpp: "Genode::Signal_context_capability", + header: "", final, pure.} = object + ## Capability to an asynchronous signal context. + + SignalDispatcherCpp {.importcpp, header: SignalsH, final, pure.} = object + cap {.importcpp.}: SignalContextCapability + + SignalDispatcher* = ref object + cpp: SignalDispatcherCpp + handler*: proc() {.closure.} + +proc init(cpp: SignalDispatcherCpp; sh: SignalDispatcher) + {.importcpp: "#.init(&genodeEnv->ep(), #)".} + +proc deinit(sd: SignalDispatcherCpp) {.importcpp.} + +proc newSignalDispatcher*(): SignalDispatcher = + new result + init result.cpp, result + +proc dissolve*(sig: SignalDispatcher) = + ## Dissolve signal dispatcher from entrypoint. + deinit sig.cpp + sig.handler = nil + +proc cap*(sig: SignalDispatcher): SignalContextCapability = sig.cpp.cap + ## Signal context capability. Can be delegated to external components. + +proc nimHandleSignal(p: pointer) {.exportc.} = + ## C symbol invoked by entrypoint during signal dispatch. + let dispatch = cast[SignalDispatcher](p) + if not dispatch.handler.isNil: + dispatch.handler() + +# +# Sessions +# + +type SessionCapability* {. + importcpp: "Genode::Session_capability", + header: "", final, pure.} = object + ## Capability to a session. + +# +# Dataspaces +# + +import streams + +type + DataspaceCapability* {. + importcpp: "Genode::Dataspace_capability", header: "dataspace/capability.h".} = object + +proc isValid*(cap: DataspaceCapability): bool {. + importcpp: "#.valid()", tags: [RpcEffect].} + +proc size*(cap: DataspaceCapability): int {. + importcpp: "Genode::Dataspace_client(@).size()", header: "dataspace/client.h", + tags: [RpcEffect].} + +proc allocDataspace*(size: int): DataspaceCapability {. + importcpp: "genodeEnv->pd().alloc(#)", tags: [RpcEffect].} + +proc freeDataspace*(cap: DataspaceCapability) {. + importcpp: "genodeEnv->pd().free(#)", tags: [RpcEffect].} + +proc attach*(cap: DataspaceCapability): ByteAddress {. + importcpp: "genodeEnv->rm().attach(@)", tags: [RpcEffect].} + +proc detach*(p: ByteAddress) {. + importcpp: "genodeEnv->rm().detach(@)", tags: [RpcEffect].} + +type + DataspaceStream* = ref DataspaceStreamObj + ## a stream that provides safe access to dataspace content + DataspaceStreamObj* = object of StreamObj + cap: DataspaceCapability + base: ByteAddress + size: int + pos: int + +proc size*(ds: DataspaceStream): int = ds.size + +proc dsClose(s: Stream) = + var s = DataspaceStream(s) + if s.base != 0: + detach s.base + s.base = 0 + s.size = 0 + s.pos = 0 + +proc dsAtEnd(s: Stream): bool = + var s = DataspaceStream(s) + result = s.pos <= s.size + +proc dsSetPosition(s: Stream, pos: int) = + var s = DataspaceStream(s) + s.pos = clamp(pos, 0, s.size) + +proc dsGetPosition(s: Stream): int = + result = DataspaceStream(s).pos + +proc dsPeekData(s: Stream, buffer: pointer, bufLen: int): int = + var s = DataspaceStream(s) + result = min(bufLen, s.size - s.pos) + if result > 0: + copyMem(buffer, cast[pointer](cast[int](s.base) + s.pos), result) + +proc dsReadData(s: Stream, buffer: pointer, bufLen: int): int = + var s = DataspaceStream(s) + result = min(bufLen, s.size - s.pos) + if result > 0: + copyMem(buffer, cast[pointer](cast[int](s.base) + s.pos), result) + inc(s.pos, result) + +proc dsWriteData(s: Stream, buffer: pointer, bufLen: int) = + var s = DataspaceStream(s) + let count = clamp(bufLen, 0, s.size - s.pos) + copyMem(cast[pointer](cast[int](s.base) + s.pos), buffer, count) + inc(s.pos, count) + +proc newDataspaceStream*(cap: DataspaceCapability): DataspaceStream = + result = DataspaceStream( + closeImpl: dsClose, + atEndImpl: dsAtEnd, + setPositionImpl: dsSetPosition, + getPositionImpl: dsGetPosition, + readDataImpl: dsReadData, + peekDataImpl: dsPeekData, + writeDataImpl: dsWriteData) + if cap.isValid: + result.cap = cap + result.base = attach cap + result.size = cap.size + +proc update*(ds: DataspaceStream; cap: DataspaceCapability) = + ds.pos = 0 + if cap.isValid: + ds.cap = cap + ds.base = attach cap + ds.size = cap.size + else: + ds.base = 0 + ds.size = 0 diff --git a/include/nim/input.nim.cfg b/include/nim/input.nim.cfg new file mode 100644 index 0000000..86b9fff --- /dev/null +++ b/include/nim/input.nim.cfg @@ -0,0 +1 @@ +-d:private=public diff --git a/include/nim/inputclient.nim b/include/nim/inputclient.nim new file mode 100644 index 0000000..424f709 --- /dev/null +++ b/include/nim/inputclient.nim @@ -0,0 +1,44 @@ +# +# \brief Input client +# \author Emery Hemingway +# \date 2017-10-15 +# + +# +# Copyright (C) 2017 Genode Labs GmbH +# +# This file is part of the Genode OS framework, which is distributed +# under the terms of the GNU Affero General Public License version 3. +# + +import genode +include inputevent + +type + InputClient* = ptr InputClientObj + InputClientObj {.importcpp: "Input::Session_client", header: "".} = object + ## Normally a Connection class is imported, but the Nitpicker connection + ## contains an Input session in the form of the Rpc_client base class. + +proc dataspace*(input: InputClient): DataspaceCapability {.tags: [RpcEffect], importcpp.} + +proc pending*(input: InputClient): bool {.tags: [RpcEffect], importcpp.} + +proc flush*(input: InputClient): int {.tags: [RpcEffect], importcpp.} + +proc sigh*(input: InputClient; cap: SignalContextCapability) {.tags: [RpcEffect], importcpp.} + +type EventBuffer {.unchecked.} = array[0, Event] + ## Type to represent an unbounded Event array. + +proc eventBuffer*(input: InputClient): ptr EventBuffer {. + importcpp: "#->_event_ds.local_addr()".} + ## Requires breaking the C++ private/public rules. + +iterator events*(input: InputClient): Event = + ## Flush and iterate over input events. + let + buf = input.eventBuffer + n = flush input + for i in 0..mode()".} + +proc mode*(np: NitpickerClient): Mode = mode np.conn + ## Return physical screen mode. + +proc mode_sigh(conn: Connection; cap: SignalContextCapability) {. + importcpp: "#->mode_sigh(#)".} + +proc modeSigh*(np: NitpickerClient; cap: SignalContextCapability) = + ## Register signal handler to be notified about mode changes. + np.conn.modeSigh cap + +proc input(conn: Connection): InputClient {. + importcpp: "#->input()".} + +proc input*(np: NitpickerClient): InputClient = + ## Return an object representing the Input sub-session. + result = np.conn.input() \ No newline at end of file diff --git a/include/nim/reportclient.nim b/include/nim/reportclient.nim new file mode 100644 index 0000000..2871a7c --- /dev/null +++ b/include/nim/reportclient.nim @@ -0,0 +1,50 @@ +# +# \brief Report client +# \author Emery Hemingway +# \date 2017-10-15 +# + +# +# Copyright (C) 2017 Genode Labs GmbH +# +# This file is part of the Genode OS framework, which is distributed +# under the terms of the GNU Affero General Public License version 3. +# + +import genode, streams + +const ReportH = "" + +type + ConnectionBase {. + importcpp: "Report::Connection", header: ReportH.} = object + Connection = Constructible[ConnectionBase] + + ReportClient* = ref ReportClientObj + ReportClientObj = object + conn: Connection + stream*: DataspaceStream + +proc construct(c: Connection, label: cstring, bufferSize: csize) {. + importcpp: "#.construct(*genodeEnv, @)", tags: [RpcEffect].} + +proc destruct(c: Connection) {.tags: [RpcEffect], + importcpp: "#.destruct()".} + +proc dataspace(c: Connection): DataspaceCapability {.tags: [RpcEffect], + importcpp: "#->dataspace()".} + +proc submit(c: Connection, n: csize) {.tags: [RpcEffect], + importcpp: "#->submit(#)".} + +proc newReportClient*(label: string, bufferSize = 4096): ReportClient= + new result + construct result.conn, label, bufferSize + result.stream = newDataspaceStream(result.conn.dataspace) + +proc submit*(report: ReportClient) = + report.conn.submit(report.stream.size) + +proc close*(report: ReportClient) = + close report.stream + destruct report.conn diff --git a/include/nim/romclient.nim b/include/nim/romclient.nim new file mode 100644 index 0000000..78eba2f --- /dev/null +++ b/include/nim/romclient.nim @@ -0,0 +1,90 @@ +# +# \brief ROM client +# \author Emery Hemingway +# \date 2017-10-15 +# + +# +# Copyright (C) 2017 Genode Labs GmbH +# +# This file is part of the Genode OS framework, which is distributed +# under the terms of the GNU Affero General Public License version 3. +# + +import genode, streams, xmlparser, xmltree + +const RomH = "" + +type + ConnectionBase {. + importcpp: "Genode::Rom_connection", header: RomH.} = object + Connection = Constructible[ConnectionBase] + + RomClient* = ref RomClientObj + RomClientObj = object + conn: Connection + dispatcher: SignalDispatcher + stream*: DataspaceStream + +proc construct(c: Connection, label: cstring) {. + importcpp: "#.construct(*genodeEnv, @)", tags: [RpcEffect].} + +proc destruct(c: Connection) {.tags: [RpcEffect], + importcpp: "#.destruct()".} + +proc dataspace(c: Connection): DataspaceCapability {.tags: [RpcEffect], + importcpp: "#->dataspace()".} + ## Return the current dataspace capability from the ROM server. + +proc update(c: Connection) {.tags: [RpcEffect], + importcpp: "#->update()".} + +proc sigh(c: Connection; cap: SignalContextCapability) {.tags: [RpcEffect], + importcpp: "#->sigh(#)".} + +proc newRomClient*(label: string): RomClient = + ## Open a new ROM connection. + new result + construct result.conn, label + result.stream = newDataspaceStream(result.conn.dataspace) + +proc close*(rom: RomClient) = + close rom.stream + destruct rom.conn + if not rom.dispatcher.isNil: + dissolve rom.dispatcher + +proc dataspace*(rom: RomClient): DataspaceCapability = rom.conn.dataspace + ## Return the ROM dataspace. Use the stream object member whenever possible. + +proc update*(rom: RomClient) = + ## Update the ROM dataspace. + update rom.conn + rom.stream.update rom.conn.dataspace + +proc sigh*(rom: RomClient; cap: SignalContextCapability) = + ## Register a capability to a signal handler at the ROM server. + doAssert(rom.dispatcher.isNil, "ROM handler already set with `handler=`") + rom.conn.sigh cap + +proc `handler=`*(rom: RomClient; cb: proc() {.closure.}) = + ## Set the signal handling procedure for a ROM client. + ## A nil procedure will disable signal handling. + if cb.isNil: + if not rom.dispatcher.isNil: + rom.conn.sigh(SignalContextCapability()) + dissolve rom.dispatcher + rom.dispatcher = nil + else: + if rom.dispatcher.isNil: + rom.dispatcher = newSignalDispatcher() + rom.conn.sigh rom.dispatcher.cap + rom.dispatcher.handler = cb + +proc xml*(rom: RomClient): XmlNode = + ## Parse ROM content as XML. + try: + rom.stream.setPosition 0 + result = parseXml rom.stream + except XmlError: + result = <>empty() diff --git a/include/nim/signals.h b/include/nim/signals.h new file mode 100644 index 0000000..9800661 --- /dev/null +++ b/include/nim/signals.h @@ -0,0 +1,59 @@ +/* + * \brief Genode signal support for Nim + * \author Emery Hemingway + * \date 2015-10-15 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + + +#ifndef _GENODE_CPP__SIGNALS_H_ +#define _GENODE_CPP__SIGNALS_H_ + +#include +#include +#include +#include + +extern "C" void nimHandleSignal(void *arg); + +struct SignalDispatcherCpp +{ + struct _Dispatcher : Genode::Signal_dispatcher_base + { + void *arg; + + void dispatch(unsigned num) override + { + Libc::with_libc([&] () { + nimHandleSignal(arg); }); + } + + _Dispatcher(void *arg) : arg(arg) { + Genode::Signal_context::_level = Genode::Signal_context::Level::App; } + }; + + Genode::Constructible<_Dispatcher> _dispatcher; + Genode::Entrypoint *entrypoint; + Genode::Signal_context_capability cap; + + void init(Genode::Entrypoint *ep, void *arg) + { + _dispatcher.construct(arg); + entrypoint = ep; + cap = entrypoint->manage(*_dispatcher); + } + + void deinit() + { + entrypoint->dissolve(*_dispatcher); + _dispatcher.destruct(); + } +}; + +#endif