// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
 *               Alexander Warg <warg@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/capability>
#include <l4/sys/irq>
#include <l4/sys/cxx/ipc_iface>
#include <l4/sys/cxx/ipc_array>
#include <l4/re/dataspace>
#include <l4/re/event.h>

namespace L4Re {

/**
 * \defgroup api_l4re_event Event API
 * \ingroup api_l4re
 * Event API.
 *
 * On top of a shared L4Re::Dataspace (and optionally using L4::Triggerable),
 * the event API implements asynchronous event transmission from an event
 * provider (server) to an event receiver (client). Events are put into an
 * Event_buffer_t residing on the shared L4Re::Dataspace.
 *
 * This interface is not usually used directly. Instead use
 * L4Re::Util::Event_t for clients. An example server portion is implemented
 * in L4Re::Util::Event_svr.
 */

typedef l4re_event_stream_id_t Event_stream_id;
typedef l4re_event_absinfo_t Event_absinfo;

class L4_EXPORT Event_stream_bitmap_h
{
protected:
  static unsigned __get_idx(unsigned idx)
  { return idx / (sizeof(unsigned long)*8); }

  static unsigned long __get_mask(unsigned idx)
  { return 1ul << (idx % (sizeof(unsigned long)*8)); }

  static bool __get_bit(unsigned long const *bm, unsigned max, unsigned idx)
  {
    if (idx <= max)
      return bm[__get_idx(idx)] & __get_mask(idx);
    return false;
  }

  static void __set_bit(unsigned long *bm, unsigned max, unsigned idx, bool v)
  {
    if (idx > max)
      return;

    if (v)
      bm[__get_idx(idx)] |= __get_mask(idx);
    else
      bm[__get_idx(idx)] &= ~__get_mask(idx);
  }
};

class L4_EXPORT Event_stream_info
: public l4re_event_stream_info_t,
  private Event_stream_bitmap_h
{
public:
  bool get_propbit(unsigned idx) const
  { return __get_bit(propbits, L4RE_EVENT_PROP_MAX, idx); }

  void set_propbit(unsigned idx, bool v)
  { __set_bit(propbits, L4RE_EVENT_PROP_MAX, idx, v); }

  bool get_evbit(unsigned idx) const
  { return __get_bit(evbits, L4RE_EVENT_EV_MAX, idx); }

  void set_evbit(unsigned idx, bool v)
  { __set_bit(evbits, L4RE_EVENT_EV_MAX, idx, v); }

  bool get_keybit(unsigned idx) const
  { return __get_bit(keybits, L4RE_EVENT_KEY_MAX, idx); }

  void set_keybit(unsigned idx, bool v)
  { __set_bit(keybits, L4RE_EVENT_KEY_MAX, idx, v); }

  bool get_relbit(unsigned idx) const
  { return __get_bit(relbits, L4RE_EVENT_REL_MAX, idx); }

  void set_relbit(unsigned idx, bool v)
  { __set_bit(relbits, L4RE_EVENT_REL_MAX, idx, v); }

  bool get_absbit(unsigned idx) const
  { return __get_bit(absbits, L4RE_EVENT_ABS_MAX, idx); }

  void set_absbit(unsigned idx, bool v)
  { __set_bit(absbits, L4RE_EVENT_ABS_MAX, idx, v); }

  bool get_swbit(unsigned idx) const
  { return __get_bit(swbits, L4RE_EVENT_SW_MAX, idx); }

  void set_swbit(unsigned idx, bool v)
  { __set_bit(swbits, L4RE_EVENT_SW_MAX, idx, v); }
};

class L4_EXPORT Event_stream_state
: public l4re_event_stream_state_t,
  private Event_stream_bitmap_h
{
public:
  bool get_keybit(unsigned idx) const
  { return __get_bit(keybits, L4RE_EVENT_KEY_MAX, idx); }

  void set_keybit(unsigned idx, bool v)
  { __set_bit(keybits, L4RE_EVENT_KEY_MAX, idx, v); }

  bool get_swbit(unsigned idx) const
  { return __get_bit(swbits, L4RE_EVENT_SW_MAX, idx); }

  void set_swbit(unsigned idx, bool v)
  { __set_bit(swbits, L4RE_EVENT_SW_MAX, idx, v); }
};

/**
 * \brief Event class.
 * \ingroup api_l4re_event
 *
 * \see \link api_l4re_event L4Re Event API \endlink
 */
class L4_EXPORT Event :
  public L4::Kobject_t<Event, L4::Icu, L4RE_PROTO_EVENT>
{
public:
  /**
   * \brief Get event signal buffer.
   *
   * \retval ds Event buffer.
   *
   * \return 0 on success, negative error code otherwise.
   */
  L4_RPC(long, get_buffer, (L4::Ipc::Out<L4::Cap<Dataspace> > ds));
  L4_RPC(long, get_num_streams, ());
  L4_RPC(long, get_stream_info, (int idx, Event_stream_info *info));
  L4_RPC(long, get_stream_info_for_id, (l4_umword_t stream_id, Event_stream_info *info));
  L4_RPC_NF(long, get_axis_info, (l4_umword_t stream_id,
                                  L4::Ipc::Array<unsigned const, unsigned long> axes,
                                  L4::Ipc::Array<Event_absinfo, unsigned long> &info));

  long get_axis_info(l4_umword_t stream_id, unsigned naxes,
                     unsigned const *axis, Event_absinfo *info) const noexcept
  {
    L4::Ipc::Array<Event_absinfo, unsigned long> i(naxes, info);
    return get_axis_info_t::call(c(), stream_id,
        L4::Ipc::Array<unsigned const, unsigned long>(naxes, axis), i);
  }

  L4_RPC(long, get_stream_state_for_id, (l4_umword_t stream_id,
                                         Event_stream_state *state));

  typedef L4::Typeid::Rpcs<
    get_buffer_t,
    get_num_streams_t,
    get_stream_info_t,
    get_stream_info_for_id_t,
    get_axis_info_t,
    get_stream_state_for_id_t
  > Rpcs;
};

struct L4_EXPORT Default_event_payload
{
  unsigned short type;    /**< Type of event */
  unsigned short code;    /**< Code of event */
  int value;              /**< Value of event */
  l4_umword_t stream_id;  /**< Stream ID */
};


/**
 * \brief Event buffer class.
 * \ingroup api_l4re_event
 */
template< typename PAYLOAD = Default_event_payload >
class L4_EXPORT Event_buffer_t
{
public:

  /**
   * \brief Event structure used in buffer.
   */
  struct Event
  {
    long long time;         /**< Event time stamp */
    PAYLOAD payload;

    /**
     * \brief Free the entry.
     */
    void free() noexcept { l4_mb(); time = 0; }
  };

private:
  Event *_current;
  Event *_begin;
  Event const *_end;

  void inc() noexcept
  {
    ++_current;
    if (_current == _end)
      _current = _begin;
  }

public:

  Event_buffer_t() : _current(0), _begin(0), _end(0) {}

  void reset()
  {
    for (Event *i = _begin; i != _end; ++i)
      i->time = 0;
    _current = _begin;
  }

  /**
   * \brief Initialize event buffer.
   *
   * \param buffer   Pointer to buffer.
   * \param size     Size of buffer in bytes.
   */
  Event_buffer_t(void *buffer, l4_addr_t size)
    : _current((Event*)buffer), _begin(_current),
      _end(_begin + size / sizeof(Event))
  { reset(); }

  /**
   * \brief Next event in buffer.
   *
   * \return 0 if no event available, event otherwise.
   */
  Event *next() noexcept
  {
    Event *c = _current;
    if (c->time)
      {
        inc();
        return c;
      }
    return 0;
  }

  /**
   * \brief Put event into buffer at current position.
   *
   * \param ev   Event to put into the buffer.
   * \return false if buffer is full and entry could not be added.
   */
  bool put(Event const &ev) noexcept
  {
    Event *c = _current;
    if (c->time)
      return false;

    inc();
    c->payload = ev.payload;
    l4_wmb();
    c->time = ev.time;
    return true;
  }
};

typedef Event_buffer_t<Default_event_payload> Event_buffer;

}