/*
 * (c) 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.
 */

#include <l4/sys/capability>
#include <l4/sys/typeinfo_svr>
#include <l4/re/util/br_manager>
#include <l4/re/dataspace>

#include <cstdio>
#include <getopt.h>
#include <cstdlib>

#include "fb.h"

static L4Re::Util::Registry_server<L4Re::Util::Br_manager_hooks> server;

void
Phys_fb::setup_ds(char const *name)
{
  server.registry()->register_obj(this, name);
  _fb_ds = L4::Cap<L4Re::Dataspace>(obj_cap().cap());
  _ds_start = _vidmem_start;
  _ds_size = _vidmem_size;
  _rw_flags = L4Re::Dataspace::F::RW;
  _cache_flags = L4::Ipc::Snd_fpage::Buffered;
}

int
Phys_fb::map_hook(L4Re::Dataspace::Offset /* offs */, unsigned /* order */,
                  L4Re::Dataspace::Flags /* flags */,
                  L4Re::Dataspace::Map_addr * /* base */,
                  unsigned * /* send_order */)
{
  // map everything at once, a framebuffer will usually used fully
  if (_map_done)
    return 0;

  int err;
  L4::Cap<L4Re::Dataspace> ds;
  unsigned long sz = 1;
  L4Re::Rm::Offset off;
  L4Re::Rm::Flags fl;
  l4_addr_t a = _vidmem_start;

  if ((err = L4Re::Env::env()->rm()->find(&a, &sz, &off, &fl, &ds)) < 0)
    {
      printf("Failed to query video memory: %d\n", err);
      return err;
    }

  if ((err = ds->map_region(off, L4Re::Dataspace::F::RW,
                            _vidmem_start, _vidmem_end)) < 0)
    {
      printf("Failed to map video memory: %d\n", err);
      return err;
    }

  _map_done = 1;
  return 0;
}

Prog_args::Prog_args(int argc, char *argv[])
 : vbemode(~0), config_str(0), do_virtual(false), width(0), height(0)
{
  while (1)
    {
      struct option opts[] = {
            { "vbemode", required_argument, 0, 'm' },
            { "config", required_argument, 0, 'c' },
            { "virtual", no_argument, 0, 'V' },
            { "width", required_argument, 0, 'W' },
            { "height", required_argument, 0, 'H' },
            { 0, 0, 0, 0 },
      };

      int c = getopt_long(argc, argv, "m:c:V", opts, NULL);
      if (c == -1)
        break;

      switch (c)
        {
        case 'm':
          vbemode = strtol(optarg, 0, 0);
          break;
        case 'c':
          config_str = optarg;
          break;
        case 'V':
          do_virtual = true;
          break;
        case 'W':
          width = strtoul(optarg, 0, 0);
          break;
        case 'H':
          height = strtoul(optarg, 0, 0);
          break;
        default:
          printf("Unknown option '%c'\n", c);
          break;
        }
    }
}

int main(int argc, char *argv[])
{
  Prog_args args(argc, argv);
  Phys_fb *fb;

  if (args.do_virtual)
    fb = Phys_fb::get_virtual();
  else
    fb = Phys_fb::probe();

  if (!fb->setup_drv(&args, server.registry()))
    {
      printf("Failed to setup framebuffer\n");
      return 1;
    }

  fb->setup_ds("fb");

  if (!fb->running())
    {
      printf("Failed to initialize framebuffer\n");
      return 1;
    }

  if (!fb->obj_cap().is_valid())
    {
      printf("Failed to connect\n");
      return 1;
    }

  printf("Starting server loop\n");
  server.loop();

  return 0;
}
