// vi:set ft=cpp: -*- Mode: C++ -*- /** * \file * IPC stream */ /* * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>, * Alexander Warg <warg@os.inf.tu-dresden.de>, * Torsten Frenzel <frenzel@os.inf.tu-dresden.de> * economic rights: Technische Universität Dresden (Germany) * * This file is part of TUD:OS and distributed under the terms of the * GNU General Public License 2. * Please see the COPYING-GPL-2 file for details. * * As a special exception, you may use this file as part of a free software * library without restriction. Specifically, if other files instantiate * templates or use macros or inline functions from this file, or you compile * this file and link it with other files to produce an executable, this * file does not by itself cause the resulting executable to be covered by * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. */ #pragma once #include <l4/sys/ipc.h> #include <l4/sys/capability> #include <l4/sys/cxx/ipc_types> #include <l4/sys/cxx/ipc_varg> #include <l4/cxx/type_traits> #include <l4/cxx/minmax> namespace L4 { namespace Ipc { class Ostream; class Istream; namespace Internal { /** * Abstraction for inserting an array into an Ipc::Ostream. * \internal * * An object of Buf_cp_out can be used to insert an array of arbitrary values, * that can be inserted into an Ipc::Ostream individually. * The array is therefore copied to the message buffer, in contrast to * data handled with Msg_out_buffer or Msg_io_buffer. * * On insertion into the Ipc::Ostream exactly the given number of elements * of type T are copied to the message buffer, this means the source buffer * is no longer referenced after insertion into the stream. * * The method buf_cp_out() should be used to create instances of Buf_cp_out. * * The counterpart is either Buf_cp_in (buf_cp_in()) or Buf_in (buf_in()). */ template< typename T > class Buf_cp_out { public: /** * Create a buffer object for the given array. * * \param v The pointer to the array with size elements of type T. * \param size The number of elements in the array. */ Buf_cp_out(T const *v, unsigned long size) : _v(v), _s(size) {} /** * Get the number of elements in the array. * * \return The number of elements in the array. * * \note This function is usually used by the Ipc::Ostream itself. */ unsigned long size() const { return _s; } /** * Get the pointer to the array. * * \return Pointer to the array. * * \note This function is usually used by the Ipc::Ostream itself. */ T const *buf() const { return _v; } private: friend class Ostream; T const *_v; unsigned long _s; }; } /** * Insert an array into an Ipc::Ostream. * * \param v Pointer to the array that shall be inserted into an * Ipc::Ostream. * \param size Number of elements in the array. * * This function inserts an array (e.g. a string) into an Ipc::Ostream. * The data is copied to the stream. On insertion into the Ipc::Ostream * exactly the given number of elements of type T are copied to the message * buffer, this means the source buffer is no longer referenced after * insertion into the stream. * * \see The counterpart is either buf_cp_in() or buf_in(). */ template< typename T > Internal::Buf_cp_out<T> buf_cp_out(T const *v, unsigned long size) { return Internal::Buf_cp_out<T>(v, size); } namespace Internal { /** * Abstraction for extracting array from an Ipc::Istream. * \internal * * An instance of Buf_cp_in can be used to extract an array from * an Ipc::Istream. This is the counterpart to the Buf_cp_out abstraction. * The data from the received message is thereby copied to the given buffer * and size is set to the number of elements found in the stream. * To avoid the copy operation Buf_in may be used instead. * * \see buf_cp_in(), Buf_in, buf_in(), Buf_cp_out, and buf_cp_out(). */ template< typename T > class Buf_cp_in { public: /** * Create a buffer for extracting an array from an Ipc::Istream. * * \param v The buffer for array (copy in). * \param[in,out] size Input: the number of elements the array can take at * most <br> * Output: the number of elements found in the stream. */ Buf_cp_in(T *v, unsigned long &size) : _v(v), _s(&size) {} unsigned long &size() const { return *_s; } T *buf() const { return _v; } private: friend class Istream; T *_v; unsigned long *_s; }; } /** * Extract an array from an Ipc::Istream. * * \param v Pointer to the array that shall receive the values from * the Ipc::Istream. * \param[in,out] size Input: the number of elements the array can take at * most <br> * Output: the number of elements found in the stream. * * buf_cp_in() can be used to extract an array from an Ipc::Istream. This is * the counterpart buf_cp_out(). The data from the received message is * thereby copied to the given buffer and size is set to the number of * elements found in the stream. To avoid the copy operation buf_in() may be * used instead. * * \see buf_in() and buf_cp_out(). */ template< typename T > Internal::Buf_cp_in<T> buf_cp_in(T *v, unsigned long &size) { return Internal::Buf_cp_in<T>(v, size); } /** * Abstraction for extracting a zero-terminated string from an Ipc::Istream. * * An instance of Str_cp_in can be used to extract a zero-terminated string * an Ipc::Istream. The data from the received message is thereby copied to the * given buffer and size is set to the number of characters found in the * stream. The string is zero terminated in any circumstances. When the given * buffer is smaller than the received string the last byte in the buffer will * be the zero terminator. In the case the received string is shorter than the * given buffer the zero termination will be placed behind the received data. * This provides a zero-terminated result even in cases where the sender did * not provide proper termination or in cases of too small receiver buffers. * * \see str_cp_in(). */ template< typename T > class Str_cp_in { public: /** * Create a buffer for extracting an array from an Ipc::Istream. * * \param v The buffer for string. * \param[in,out] size Input: The number of bytes available in `v` <br> * Output: The number of bytes received (including the * terminator). */ Str_cp_in(T *v, unsigned long &size) : _v(v), _s(&size) {} unsigned long &size() const { return *_s; } T *buf() const { return _v; } private: friend class Istream; T *_v; unsigned long *_s; }; /** * Create a Str_cp_in for the given values. * * \param v Pointer to the array that shall receive the values from * the Ipc::Istream. * \param[in,out] size Input: the number of elements the array can take at * most <br> * Output: the number of elements found in the stream. * * This function makes it more convenient to extract arrays from an * Ipc::Istream (\see Str_cp_in.) */ template< typename T > Str_cp_in<T> str_cp_in(T *v, unsigned long &size) { return Str_cp_in<T>(v, size); } /** * Pointer to an element of type T in an Ipc::Istream. * * This wrapper can be used to extract an element of type T from an * Ipc::Istream, whereas the data is not copied out, but a pointer into * the message buffer itself is returned. With is mechanism it is possible * to avoid an extra copy of large data structures from a received IPC * message, instead the returned pointer gives direct access to the data * in the message. * * See msg_ptr(). */ template< typename T > class Msg_ptr { private: T **_p; public: /** * Create a Msg_ptr object that set pointer p to point into the message * buffer. * * \param p The pointer that is adjusted to point into the message buffer. */ explicit Msg_ptr(T *&p) : _p(&p) {} void set(T *p) const { *_p = p; } }; /** * Create an Msg_ptr to adjust the given pointer. * * This function makes it more convenient to extract pointers to data in the * message buffer itself from an Ipc::Istream. This may be used to avoid copy * out of large data structures. (See Msg_ptr.) */ template< typename T > Msg_ptr<T> msg_ptr(T *&p) { return Msg_ptr<T>(p); } namespace Internal { /** * Abstraction to extract an array from an Ipc::Istream. * \internal * * This wrapper provides a possibility to extract an array from an * Ipc::Istream, without extra copy overhead. In contrast to Buf_cp_in * the data is not copied to a buffer, but a pointer to the array is returned. * * The mechanism is comparable to that of Msg_ptr, however it handles arrays * inserted with Buf_cp_out. * * See buf_in(), Buf_cp_out, buf_cp_out(), Buf_cp_in, and buf_cp_in(). */ template< typename T > class Buf_in { public: /** * Create a Buf_in to adjust a pointer to the array and the size of the array. * * \param v The pointer to adjust to the first element of the array. * \param[out] size The number of elements found in the stream. */ Buf_in(T *&v, unsigned long &size) : _v(&v), _s(&size) {} void set_size(unsigned long s) const { *_s = s; } T *&buf() const { return *_v; } private: friend class Istream; T **_v; unsigned long *_s; }; } /** * Return a pointer to stream array data. * * \param[out] v Pointer to the array within the Ipc::Istream. * \param[out] size The number of elements found in the stream. * * This routine provdes a possibility to extract an array from an * Ipc::Istream, without extra copy overhead. In contrast to buf_cp_in() * the data is not copied to a buffer, but a pointer to the array is returned. * The user must make sure the UTCB is not used for other purposes while the * returned pointer is still in use. * * The mechanism is comparable to that of Msg_ptr, however it handles arrays * inserted with buf_cp_out(). * * \see buf_cp_in() and buf_cp_out(). */ template< typename T > Internal::Buf_in<T> buf_in(T *&v, unsigned long &size) { return Internal::Buf_in<T>(v, size); } namespace Utcb_stream_check { static bool check_utcb_data_offset(unsigned sz) { return sz > sizeof(l4_umword_t) * L4_UTCB_GENERIC_DATA_SIZE; } } /** * Input stream for IPC unmarshalling. * * Ipc::Istream is part of the dynamic IPC marshalling infrastructure, as well * as Ipc::Ostream and Ipc::Iostream. * * Ipc::Istream is an input stream supporting extraction of values from an * IPC message buffer. A received IPC message can be unmarshalled using the * usual extraction operator (>>). * * There exist some special wrapper classes to extract arrays (see * Ipc_buf_cp_in and Ipc_buf_in) and indirect strings (see Msg_in_buffer and * Msg_io_buffer). */ class Istream { public: /** * Create an input stream for the given message buffer. * * The given message buffer is used for IPC operations wait()/receive() * and received data can be extracted using the >> operator afterwards. * In the case of indirect message parts a buffer of type Msg_in_buffer * must be inserted into the stream before the IPC operation and contains * received data afterwards. * * \param utcb The message buffer to receive IPC messages. */ Istream(l4_utcb_t *utcb) : _tag(), _utcb(utcb), _current_msg(reinterpret_cast<char*>(l4_utcb_mr_u(utcb)->mr)), _pos(0), _current_buf(0) {} /** * Reset the stream to empty, and ready for receive()/wait(). * The stream is reset to the same state as on its creation. */ void reset() { _pos = 0; _current_buf = 0; _current_msg = reinterpret_cast<char*>(l4_utcb_mr_u(_utcb)->mr); } /** * Check whether a value of type T can be obtained from the stream. */ template< typename T > bool has_more(unsigned long count = 1) { auto const max_bytes = L4_UTCB_GENERIC_DATA_SIZE * sizeof(l4_umword_t); unsigned apos = cxx::Type_traits<T>::align(_pos); return (count <= max_bytes / sizeof(T)) && (apos + (sizeof(T) * count) <= _tag.words() * sizeof(l4_umword_t)); } /** * \name Get/Put Functions. */ //@{ /** * Copy out an array of type `T` with `size` elements. * * \param buf Pointer to a buffer for size elements of type T. * \param elems Number of elements of type T to copy out. * * \return The number of elements copied out. * * See \ref Istream::operator>>() */ template< typename T > unsigned long get(T *buf, unsigned long elems) { if (L4_UNLIKELY(!has_more<T>(elems))) return 0; unsigned long size = elems * sizeof(T); _pos = cxx::Type_traits<T>::align(_pos); __builtin_memcpy(buf, _current_msg + _pos, size); _pos += size; return elems; } /** * Skip size elements of type T in the stream. * * \param elems Number of elements to skip. */ template< typename T > void skip(unsigned long elems) { if (L4_UNLIKELY(!has_more<T>(elems))) return; unsigned long size = elems * sizeof(T); _pos = cxx::Type_traits<T>::align(_pos); _pos += size; } /** * Read one size elements of type T from the stream and return a pointer. * * \param buf A Msg_ptr that is actually set to point to the element in the * stream. * \param elems Number of elements to extract (default is 1). * * \return The number of elements extracted. * * In contrast to a normal get, this version does actually not copy the data * but returns a pointer to the data. * * See \ref Istream::operator>>() */ template< typename T > unsigned long get(Msg_ptr<T> const &buf, unsigned long elems = 1) { if (L4_UNLIKELY(!has_more<T>(elems))) return 0; unsigned long size = elems * sizeof(T); _pos = cxx::Type_traits<T>::align(_pos); buf.set(reinterpret_cast<T*>(_current_msg + _pos)); _pos += size; return elems; } /** * Extract a single element of type T from the stream. * * \param[out] v The element. * * \retval true An element was successfully extracted. * \retval false An element could not be extracted. * * See \ref Istream::operator>>() */ template< typename T > bool get(T &v) { if (L4_UNLIKELY(!has_more<T>())) { v = T(); return false; } _pos = cxx::Type_traits<T>::align(_pos); v = *(reinterpret_cast<T*>(_current_msg + _pos)); _pos += sizeof(T); return true; } bool get(Ipc::Varg *va) { Ipc::Varg::Tag t; if (!has_more<Ipc::Varg::Tag>()) { va->tag(0); return 0; } get(t); va->tag(t); char const *d; get(msg_ptr(d), va->length()); va->data(d); return 1; } /** * Get the message tag of a received IPC. * * \return The L4 message tag for the received IPC. * * This is in particular useful for handling page faults or exceptions. * * See \ref Istream::operator>>() */ l4_msgtag_t tag() const { return _tag; } /** * Get the message tag of a received IPC. * * \return A reference to the L4 message tag for the received IPC. * * This is in particular useful for handling page faults or exceptions. * * See \ref Istream::operator>>() */ l4_msgtag_t &tag() { return _tag; } //@} /** * \internal * Put a receive item into the stream's buffer registers. */ inline bool put(Buf_item const &); /** * \internal * Put a small receive item into the stream's buffer registers. */ inline bool put(Small_buf const &); /** * \name IPC operations. */ //@{ /** * Wait for an incoming message from any sender. * * \param[out] src Contains the sender after a successful IPC operation. * * \return Syscall return tag. * * This wait is actually known as 'open wait'. */ inline l4_msgtag_t wait(l4_umword_t *src) { return wait(src, L4_IPC_NEVER); } /** * Wait for an incoming message from any sender. * * \param[out] src Contains the sender after a successful IPC operation. * \param timeout Timeout used for IPC. * * \return The IPC result tag (l4_msgtag_t). * * This wait is actually known as 'open wait'. */ inline l4_msgtag_t wait(l4_umword_t *src, l4_timeout_t timeout); /** * Wait for a message from the specified sender. * * \param src The sender id to receive from. * * \return The IPC result tag (l4_msgtag_t). * * This is commonly known as 'closed wait'. */ inline l4_msgtag_t receive(l4_cap_idx_t src) { return receive(src, L4_IPC_NEVER); } inline l4_msgtag_t receive(l4_cap_idx_t src, l4_timeout_t timeout); //@} /** * Return utcb pointer. */ inline l4_utcb_t *utcb() const { return _utcb; } protected: l4_msgtag_t _tag; l4_utcb_t *_utcb; char *_current_msg; unsigned _pos; unsigned char _current_buf; }; class Istream_copy : public Istream { private: l4_msg_regs_t _mrs; public: Istream_copy(Istream const &o) : Istream(o), _mrs(*l4_utcb_mr_u(o.utcb())) { // do some reverse mr to utcb trickery _utcb = (l4_utcb_t *)((l4_addr_t)&_mrs - (l4_addr_t)l4_utcb_mr_u((l4_utcb_t *)0)); _current_msg = reinterpret_cast<char*>(l4_utcb_mr_u(_utcb)->mr); } }; /** * Output stream for IPC marshalling. * * Ipc::Ostream is part of the dynamic IPC marshalling infrastructure, as well * as Ipc::Istream and Ipc::Iostream. * * Ipc::Ostream is an output stream supporting insertion of values into an * IPC message buffer. A IPC message can be marshalled using the * usual insertion operator <<, see \link ipc_stream IPC stream operators * \endlink. * * There exist some special wrapper classes to insert arrays (see * Ipc::Buf_cp_out) and indirect strings (see Msg_out_buffer and * Msg_io_buffer). */ class Ostream { public: /** * Create an IPC output stream using the given message buffer `utcb`. */ Ostream(l4_utcb_t *utcb) : _tag(), _utcb(utcb), _current_msg(reinterpret_cast<char *>(l4_utcb_mr_u(_utcb)->mr)), _pos(0), _current_item(0) {} /** * Reset the stream to empty, same state as a newly created stream. */ void reset() { _pos = 0; _current_item = 0; _current_msg = reinterpret_cast<char*>(l4_utcb_mr_u(_utcb)->mr); } /** * \name Get/Put functions. * * These functions are basically used to implement the insertion operators * (<<) and should not be called directly. */ //@{ /** * Put an array with `size` elements of type `T` into the stream. * * \param buf A pointer to the array to insert into the buffer. * \param size The number of elements in the array. */ template< typename T > bool put(T *buf, unsigned long size) { size *= sizeof(T); _pos = cxx::Type_traits<T>::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + size)) return false; __builtin_memcpy(_current_msg + _pos, buf, size); _pos += size; return true; } /** * Insert an element of type `T` into the stream. * * \param v The element to insert. */ template< typename T > bool put(T const &v) { _pos = cxx::Type_traits<T>::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + sizeof(T))) return false; *(reinterpret_cast<T*>(_current_msg + _pos)) = v; _pos += sizeof(T); return true; } int put(Varg const &va) { put(va.tag()); put(va.data(), va.length()); return 0; } template< typename T > int put(Varg_t<T> const &va) { return put(static_cast<Varg const &>(va)); } /** * Extract the L4 message tag from the stream. * * \return The extracted L4 message tag. */ l4_msgtag_t tag() const { return _tag; } /** * Extract a reference to the L4 message tag from the stream. * * \return A reference to the L4 message tag. */ l4_msgtag_t &tag() { return _tag; } //@} /** * \internal * Put a send item into the stream's message buffer. */ inline bool put_snd_item(Snd_item const &); /** * \name IPC operations. */ //@{ /** * Send the message via IPC to the given receiver. * * \param dst The destination for the message. * \param proto Protocol to use. * \param flags Flags to use. * * \return The syscall return tag. */ inline l4_msgtag_t send(l4_cap_idx_t dst, long proto = 0, unsigned flags = 0); //@} /** * Return utcb pointer. */ inline l4_utcb_t *utcb() const { return _utcb; } #if 0 /** * Get the currently used bytes in the stream. */ unsigned long tell() const { unsigned w = (_pos + sizeof(l4_umword_t)-1) / sizeof(l4_umword_t); w -= _current_item * 2; _tag = l4_msgtag(0, w, _current_item, 0); } #endif public: l4_msgtag_t prepare_ipc(long proto = 0, unsigned flags = 0) { unsigned w = (_pos + sizeof(l4_umword_t) - 1) / sizeof(l4_umword_t); w -= _current_item * 2; return l4_msgtag(proto, w, _current_item, flags); } // XXX: this is a hack for <l4/sys/cxx/ipc_server> adaption void set_ipc_params(l4_msgtag_t tag) { _pos = (tag.words() + tag.items() * 2) * sizeof(l4_umword_t); _current_item = tag.items(); } protected: l4_msgtag_t _tag; l4_utcb_t *_utcb; char *_current_msg; unsigned _pos; unsigned char _current_item; }; /** * Input/Output stream for IPC [un]marshalling. * * The Ipc::Iostream is part of the AW Env IPC framework as well as * Ipc::Istream and Ipc::Ostream. * In particular an Ipc::Iostream is a combination of an Ipc::Istream and an * Ipc::Ostream. It can use either a single message buffer for receiving and * sending messages or a pair of a receive and a send buffer. The stream also * supports combined IPC operations such as call() and reply_and_wait(), which * can be used to implement RPC functionality. */ class Iostream : public Istream, public Ostream { public: /** * Create an IPC IO stream with a single message buffer. * * \param utcb The message buffer used as backing store. * * The created IO stream uses the same message buffer for sending and * receiving IPC messages. */ explicit Iostream(l4_utcb_t *utcb) : Istream(utcb), Ostream(utcb) {} // disambiguate those functions l4_msgtag_t tag() const { return Istream::tag(); } l4_msgtag_t &tag() { return Istream::tag(); } l4_utcb_t *utcb() const { return Istream::utcb(); } /** * Reset the stream to its initial state. * * Input as well as the output stream are reset. */ void reset() { Istream::reset(); Ostream::reset(); } /** * \name Get/Put functions. * * These functions are basically used to implement the insertion operators * (<<) and should not be called directly. */ //@{ using Istream::get; using Istream::put; using Ostream::put; //@} /** * \name IPC operations. */ //@{ /** * Do an IPC call using the message in the output stream and receive the * reply in the input stream. * * \param dst The destination to call. * \param timeout The IPC timeout for the call. * \param proto The protocol value to use in the message tag. * * \return The result tag of the IPC operation. * * This is a combined IPC operation consisting of a send and a receive * to/from the given destination `dst`. * * A call is usually used by clients for RPCs to a server. */ inline l4_msgtag_t call(l4_cap_idx_t dst, l4_timeout_t timeout, long proto = 0); inline l4_msgtag_t call(l4_cap_idx_t dst, long proto = 0); /** * Do an IPC reply and wait. * * \param[in,out] src_dst Input: the destination for the send operation. <br> * Output: the source of the received message. * \param proto Protocol to use. * * \return The result tag of the IPC operation. * * This is a combined IPC operation consisting of a send operation and * an open wait for any message. * * A reply and wait is usually used by servers that reply to a client * and wait for the next request by any other client. */ inline l4_msgtag_t reply_and_wait(l4_umword_t *src_dst, long proto = 0) { return reply_and_wait(src_dst, L4_IPC_SEND_TIMEOUT_0, proto); } inline l4_msgtag_t send_and_wait(l4_cap_idx_t dest, l4_umword_t *src, long proto = 0) { return send_and_wait(dest, src, L4_IPC_SEND_TIMEOUT_0, proto); } /** * Do an IPC reply and wait. * * \param[in,out] src_dst Input: the destination for the send operation. <br> * Output: the source of the received message. * \param timeout Timeout used for IPC. * \param proto Protocol to use. * * \return The result tag of the IPC operation. * * This is a combined IPC operation consisting of a send operation and * an open wait for any message. * * A reply and wait is usually used by servers that reply to a client * and wait for the next request by any other client. */ inline l4_msgtag_t reply_and_wait(l4_umword_t *src_dst, l4_timeout_t timeout, long proto = 0); inline l4_msgtag_t send_and_wait(l4_cap_idx_t dest, l4_umword_t *src, l4_timeout_t timeout, long proto = 0); inline l4_msgtag_t reply(l4_timeout_t timeout, long proto = 0); inline l4_msgtag_t reply(long proto = 0) { return reply(L4_IPC_SEND_TIMEOUT_0, proto); } //@} }; inline bool Ostream::put_snd_item(Snd_item const &v) { typedef Snd_item T; _pos = cxx::Type_traits<Snd_item>::align(_pos); if (Utcb_stream_check::check_utcb_data_offset(_pos + sizeof(T))) return false; *(reinterpret_cast<T*>(_current_msg + _pos)) = v; _pos += sizeof(T); ++_current_item; return true; } inline bool Istream::put(Buf_item const &item) { if (_current_buf >= L4_UTCB_GENERIC_BUFFERS_SIZE - 3) return false; l4_utcb_br_u(_utcb)->bdr &= ~L4_BDR_OFFSET_MASK; reinterpret_cast<Buf_item&>(l4_utcb_br_u(_utcb)->br[_current_buf]) = item; _current_buf += 2; return true; } inline bool Istream::put(Small_buf const &item) { if (_current_buf >= L4_UTCB_GENERIC_BUFFERS_SIZE - 2) return false; l4_utcb_br_u(_utcb)->bdr &= ~L4_BDR_OFFSET_MASK; reinterpret_cast<Small_buf&>(l4_utcb_br_u(_utcb)->br[_current_buf]) = item; _current_buf += 1; return true; } inline l4_msgtag_t Ostream::send(l4_cap_idx_t dst, long proto, unsigned flags) { l4_msgtag_t tag = prepare_ipc(proto, L4_MSGTAG_FLAGS & flags); return l4_ipc_send(dst, _utcb, tag, L4_IPC_NEVER); } inline l4_msgtag_t Iostream::call(l4_cap_idx_t dst, l4_timeout_t timeout, long label) { l4_msgtag_t tag = prepare_ipc(label); tag = l4_ipc_call(dst, Ostream::_utcb, tag, timeout); Istream::tag() = tag; Istream::_pos = 0; return tag; } inline l4_msgtag_t Iostream::call(l4_cap_idx_t dst, long label) { return call(dst, L4_IPC_NEVER, label); } inline l4_msgtag_t Iostream::reply_and_wait(l4_umword_t *src_dst, l4_timeout_t timeout, long proto) { l4_msgtag_t tag = prepare_ipc(proto); tag = l4_ipc_reply_and_wait(Ostream::_utcb, tag, src_dst, timeout); Istream::tag() = tag; Istream::_pos = 0; return tag; } inline l4_msgtag_t Iostream::send_and_wait(l4_cap_idx_t dest, l4_umword_t *src, l4_timeout_t timeout, long proto) { l4_msgtag_t tag = prepare_ipc(proto); tag = l4_ipc_send_and_wait(dest, Ostream::_utcb, tag, src, timeout); Istream::tag() = tag; Istream::_pos = 0; return tag; } inline l4_msgtag_t Iostream::reply(l4_timeout_t timeout, long proto) { l4_msgtag_t tag = prepare_ipc(proto); tag = l4_ipc_send(L4_INVALID_CAP | L4_SYSF_REPLY, Ostream::_utcb, tag, timeout); Istream::tag() = tag; Istream::_pos = 0; return tag; } inline l4_msgtag_t Istream::wait(l4_umword_t *src, l4_timeout_t timeout) { l4_msgtag_t res; res = l4_ipc_wait(_utcb, src, timeout); tag() = res; _pos = 0; return res; } inline l4_msgtag_t Istream::receive(l4_cap_idx_t src, l4_timeout_t timeout) { l4_msgtag_t res; res = l4_ipc_receive(src, _utcb, timeout); tag() = res; _pos = 0; return res; } } // namespace Ipc } // namespace L4 /** * Extract one element of type `T` from the stream `s`. * * \param s The stream to extract from. * \param[out] v Extracted value. * * \return The stream `s`. */ inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, bool &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, long int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, long long int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned long int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned long long int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, short int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned short int &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, char &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, unsigned char &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, signed char &v) { s.get(v); return s; } inline L4::Ipc::Istream &operator << (L4::Ipc::Istream &s, L4::Ipc::Buf_item const &v) { s.put(v); return s; } inline L4::Ipc::Istream &operator << (L4::Ipc::Istream &s, L4::Ipc::Small_buf const &v) { s.put(v); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Snd_item &v) { l4_umword_t b, d; s >> b >> d; v = L4::Ipc::Snd_item(b, d); return s; } inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Varg &v) { s.get(&v); return s; } /** * Extract the L4 message tag from the stream `s`. * * \param s The stream to extract from. * \param[out] v The extracted tag. * * \return The stream `s`. */ inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, l4_msgtag_t &v) { v = s.tag(); return s; } /** * Extract an array of `T` elements from the stream `s`. * * \param s The stream to extract from. * \param[out] v Pointer to the extracted array (ipc_buf_in()). * * \return The stream `s`. * * This operator actually does not copy out the data in the array, but * returns a pointer into the message buffer itself. This means that the * data is only valid as long as there is no new data inserted into the stream. * * \note If array does not fit into transmitted words size will be set to zero. * Client has to implement check against zero. * * See Ipc::Buf_in, Ipc::Buf_cp_in, and Ipc::Buf_cp_out. */ template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Internal::Buf_in<T> const &v) { unsigned long si; if (s.get(si) && s.has_more<T>(si)) v.set_size(s.get(L4::Ipc::Msg_ptr<T>(v.buf()), si)); else v.set_size(0); return s; } /** * Extract an element of type `T` from the stream `s`. * * \param s The stream to extract from. * \param[out] v Pointer to the extracted element. * * \return The stream `s`. * * This operator actually does not copy out the data, but * returns a pointer into the message buffer itself. This means that the * data is only valid as long as there is no new data inserted into the stream. * * See Msg_ptr. */ template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Msg_ptr<T> const &v) { s.get(v); return s; } /** * Extract an array of `T` elements from the stream `s`. * * \param s The stream to extract from. * \param[out] v Buffer description to copy the array to (Ipc::Buf_cp_out()). * * \return The stream `s`. * * This operator does a copy out of the data into the given buffer. * * See Ipc::Buf_in, Ipc::Buf_cp_in, and Ipc::Buf_cp_out. */ template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Internal::Buf_cp_in<T> const &v) { unsigned long sz; s.get(sz); v.size() = s.get(v.buf(), cxx::min(v.size(), sz)); return s; } /** * Extract a zero-terminated string from the stream. * * \param s The stream to extract from. * \param[out] v Buffer description to copy the array to (Ipc::Str_cp_out()). * * \return The stream `s`. * * This operator does a copy out of the data into the given buffer. */ template< typename T > inline L4::Ipc::Istream &operator >> (L4::Ipc::Istream &s, L4::Ipc::Str_cp_in<T> const &v) { unsigned long sz; s.get(sz); unsigned long rsz = s.get(v.buf(), cxx::min(v.size(), sz)); if (rsz < v.size() && v.buf()[rsz - 1]) ++rsz; // add the zero termination behind the received data if (rsz != 0) v.buf()[rsz - 1] = 0; v.size() = rsz; return s; } /** * Insert an element to type `T` into the stream `s`. * * \param s The stream to insert the element `v`. * \param v The element to insert. * * \return The stream `s`. */ inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, bool v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, long int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, long long int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned long int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned long long int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, short int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned short int v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, char v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, unsigned char v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, signed char v) { s.put(v); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Snd_item const &v) { s.put_snd_item(v); return s; } template< typename T > inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Cap<T> const &v) { s << L4::Ipc::Snd_fpage(v.fpage()); return s; } inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Varg const &v) { s.put(v); return s; } template< typename T > inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Varg_t<T> const &v) { s.put(v); return s; } /** * Insert the L4 message tag into the stream `s`. * * \param s The stream to insert the tag `v`. * \param v The L4 message tag to insert. * * \return The stream `s`. * * \note Only one message tag can be inserted into a stream. Multiple * insertions simply overwrite previous insertions. */ inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, l4_msgtag_t const &v) { s.tag() = v; return s; } /** * Insert an array with elements of type `T` into the stream `s`. * * \param s The stream to insert the array `v`. * \param v The array to insert (see Ipc::Buf_cp_out()). * * \return The stream `s`. */ template< typename T > inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, L4::Ipc::Internal::Buf_cp_out<T> const &v) { s.put(v.size()); s.put(v.buf(), v.size()); return s; } /** * Insert a zero terminated character string into the stream `s`. * * \param s The stream to insert the string `v`. * \param v The string to insert. * * \return The stream `s`. * * This operator produces basically the same content as the array insertion, * however the length of the array is calculated using `strlen(v) + 1` * The string is copied into the message including the trailing zero. */ inline L4::Ipc::Ostream &operator << (L4::Ipc::Ostream &s, char const *v) { unsigned long l = __builtin_strlen(v) + 1; s.put(l); s.put(v, l); return s; } namespace L4 { namespace Ipc { /** * Read a value out of a stream. * * \param s An Istream. * * \return The value of type `T`. * * The stream position is progressed accordingly. */ template< typename T > inline T read(Istream &s) { T t; s >> t; return t; } } // namespace Ipc } // namespace L4