// -*- mode: c++; c-basic-offset: 4 -*-
#ifndef ANYDEVICE_HH
#define ANYDEVICE_HH
#include <click/element.hh>
#include <click/task.hh>

#include <click/cxxprotect.h>
CLICK_CXX_PROTECT
#include <linux/netdevice.h>
CLICK_CXX_UNPROTECT
#include <click/cxxunprotect.h>

// #define CLICK_DEVICE_CYCLES 1
// #define CLICK_DEVICE_PRFCTR 1
// #define CLICK_DEVICE_THESIS_STATS 1
// #define _DEV_OVRN_STATS_ 1
#define CLICK_CYCLE_COMPENSATION 0

#ifdef HAVE_STRIDE_SCHED
# define CLICK_DEVICE_ADJUST_TICKETS 1
#endif

#if CLICK_DEVICE_PRFCTR && __i386__

#define CLICK_DEVICE_STATS 1
#define SET_STATS(p0mark, p1mark, time_mark) \
  { \
    unsigned high; \
    rdpmc(0, p0mark, high); \
    rdpmc(1, p1mark, high); \
    time_mark = click_get_cycles(); \
  }
#define GET_STATS_RESET(p0mark, p1mark, time_mark, pctr0, pctr1, tctr) \
  { \
    unsigned high; \
    unsigned low01, low11; \
    tctr += click_get_cycles() - time_mark - CLICK_CYCLE_COMPENSATION; \
    rdpmc(0, low01, high); \
    rdpmc(1, low11, high); \
    pctr0 += (low01 >= p0mark) ? low01-p0mark : (UINT_MAX-p0mark+low01); \
    pctr1 += (low11 >= p1mark) ? low11-p1mark : (UINT_MAX-p1mark+low11); \
    rdpmc(0, p0mark, high); \
    rdpmc(1, p1mark, high); \
    time_mark = click_get_cycles(); \
  }

#elif CLICK_DEVICE_CYCLES

#define CLICK_DEVICE_STATS 1
#define SET_STATS(p0mark, p1mark, time_mark) \
  { \
    time_mark = click_get_cycles(); \
  }
#define GET_STATS_RESET(p0mark, p1mark, time_mark, pctr0, pctr1, tctr) \
  { \
    uint64_t __now = click_get_cycles(); \
    tctr += __now - time_mark - CLICK_CYCLE_COMPENSATION; \
    time_mark = __now; \
  }

#else

#define GET_STATS_RESET(a,b,c,d,e,f)	/* nothing */
#define SET_STATS(a,b,c)		/* nothing */

#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 0)
typedef struct enet_statistics net_device_stats;
#define dev_hold(dev)		/* nada */
#define dev_put(dev)		/* nada */
#endif

class AnyDeviceMap;

class AnyDevice : public Element { public:

    enum { CONFIGURE_PHASE_FROMHOST = CONFIGURE_PHASE_DEFAULT - 2,
	   CONFIGURE_PHASE_TODEVICE = CONFIGURE_PHASE_DEFAULT - 1,
	   CONFIGURE_PHASE_POLLDEVICE = CONFIGURE_PHASE_DEFAULT };

    AnyDevice();
    ~AnyDevice();

    const String &devname() const	{ return _devname; }
    net_device *device() const		{ return _dev; }
    int ifindex() const			{ return _dev ? _dev->ifindex : -1; }

    bool promisc() const		{ return _promisc; }
    void set_promisc()			{ _promisc = true; }

    int find_device(bool, AnyDeviceMap *, ErrorHandler *);
    void set_device(net_device *, AnyDeviceMap *);
    void clear_device(AnyDeviceMap *);
    void adjust_tickets(int work);

  protected:

    String _devname;
    net_device *_dev;

    bool _promisc : 1;
    bool _in_map : 1;
    AnyDevice *_next;

    friend class AnyDeviceMap;

};

class AnyTaskDevice : public AnyDevice { public:

    AnyTaskDevice();

    void adjust_tickets(int work);

  protected:

    Task _task;
    int _max_tickets;
    int _idles;

};


inline void
AnyTaskDevice::adjust_tickets(int work)
{
#if CLICK_DEVICE_ADJUST_TICKETS
    int tix = _task.tickets();

    // simple additive increase damped multiplicative decrease scheme
    if (work > 2) {
	tix += work;
	if (tix > _max_tickets)
	    tix = _max_tickets;
	_idles = 0;
    } else if (work == 0) {
	_idles++;
	if (_idles >= 64) {
	    if (tix > 64)
		tix -= (tix >> 5);
	    else
		tix -= 2;
	    if (tix < 1)
		tix = 1;
	    _idles = 0;
	}
    }

    _task.set_tickets(tix);
#endif
}

class AnyDeviceMap { public:

    void initialize();
    AnyDevice *lookup(net_device *, AnyDevice *);
    AnyDevice *lookup_unknown(net_device *, AnyDevice *);
    void lookup_all(net_device *, bool known, Vector<AnyDevice *> &);
    void insert(AnyDevice *);
    void remove(AnyDevice *);
    void move_to_front(AnyDevice *);

  private:

    enum { MAP_SIZE = 64 };
    AnyDevice *_unknown_map;
    AnyDevice *_map[MAP_SIZE];

};

inline AnyDevice *
AnyDeviceMap::lookup(net_device *dev, AnyDevice *last)
{
    if (!dev)
	return 0;
    AnyDevice *d = (last ? last->_next : _map[dev->ifindex % MAP_SIZE]);
    while (d && d->device() != dev)
	d = d->_next;
    return d;
}

net_device *dev_get_by_ether_address(const String &, Element *);

#endif


syntax highlighted by Code2HTML, v. 0.9.1