/*
 * \brief  Emulation of Linux kernel interfaces
 * \author Norman Feske
 * \author Sebastian Sumpf
 * \date   2012-01-29
 */

/*
 * Copyright (C) 2012-2017 Genode Labs GmbH
 *
 * This file is distributed under the terms of the GNU General Public License
 * version 2.
 */

/* Genode includes */
#include <base/allocator_avl.h>
#include <dataspace/client.h>
#include <region_map/client.h>
#include <timer_session/connection.h>
#include <util/string.h>

/* Local includes */
#include "signal.h"
#include "lx_emul.h"

#include <lx_kit/backend_alloc.h>
#include <lx_kit/irq.h>
#include <lx_kit/scheduler.h>
#include <lx_kit/work.h>


#include <lx_emul/impl/slab.h>

namespace Genode {
	class Slab_backend_alloc;
	class Slab_alloc;
}


unsigned long jiffies;
void backtrace() { }


void pci_dev_put(struct pci_dev *pci_dev)
{
	Genode::destroy(&Lx_kit::env().heap(), pci_dev);
}

/***********************
 ** Atomic operations **
 ***********************/

/*
 * Actually not atomic, for now
 */

unsigned int atomic_read(atomic_t *p) { return p->v; }

void atomic_inc(atomic_t *v) { v->v++;  }
void atomic_dec(atomic_t *v) { v->v--; }

void atomic_add(int i, atomic_t *v) { v->v += i; }
void atomic_sub(int i, atomic_t *v) { v->v -= i; }

void atomic_set(atomic_t *p, unsigned int v) { p->v = v; }


/*************************************
 ** Memory allocation, linux/slab.h **
 *************************************/

void *dma_malloc(size_t size)
{
	return Lx::Malloc::dma().alloc_large(size);
}


void dma_free(void *ptr)
{
	Lx::Malloc::dma().free_large(ptr);
}


/*********************
 ** linux/vmalloc.h **
 *********************/

void *vzalloc(unsigned long size)
{
	size_t *addr;
	try { addr = (size_t *)Lx::Malloc::mem().alloc_large(size); }
	catch (...) { return 0; }

	memset(addr, 0, size);

	return addr;
}


void vfree(void *addr)
{
	if (!addr) return;
	Lx::Malloc::mem().free_large(addr);
}


/******************
 ** linux/kref.h **
 ******************/

void kref_init(struct kref *kref)
{ 
	lx_log(DEBUG_KREF,"%s ref: %p", __func__, kref);
	kref->refcount.v = 1; 
}


void kref_get(struct kref *kref)
{
	kref->refcount.v++;
	lx_log(DEBUG_KREF, "%s ref: %p c: %d", __func__, kref, kref->refcount.v);
}


int  kref_put(struct kref *kref, void (*release) (struct kref *kref))
{
	lx_log(DEBUG_KREF, "%s: ref: %p c: %d", __func__, kref, kref->refcount.v);

	if (!--kref->refcount.v) {
		release(kref);
		return 1;
	}
	return 0;
}


/*********************
 ** linux/uaccess.h **
 *********************/

size_t copy_to_user(void *dst, void const *src, size_t len)
{
	if (dst && src && len)
		memcpy(dst, src, len);
	return 0;
}


size_t copy_from_user(void *dst, void const *src, size_t len)
{
	if (dst && src && len)
		memcpy(dst, src, len);
	return 0;
}


bool access_ok(int access, void *addr, size_t size) { return 1; }


/********************
 ** linux/string.h **
 ********************/

void *_memcpy(void *d, const void *s, size_t n)
{
	return Genode::memcpy(d, s, n);
}


inline void *memset(void *s, int c, size_t n) 
{
	return Genode::memset(s, c, n);
}


int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
	Genode::String_console sc(buf, size);
	sc.vprintf(fmt, args);

	return sc.len();
}


int snprintf(char *buf, size_t size, const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	Genode::String_console sc(buf, size);
	sc.vprintf(fmt, args);
	va_end(args);

	return sc.len();
}


int scnprintf(char *buf, size_t size, const char *fmt, ...)
{
	va_list args;

	va_start(args, fmt);
	Genode::String_console sc(buf, size);
	sc.vprintf(fmt, args);
	va_end(args);

	return sc.len();
}

int    strcmp(const char *s1, const char *s2)
{
	printk("%s:%d from %p\n", __func__, __LINE__, __builtin_return_address(0));
	return Genode::strcmp(s1, s2);
}
size_t strlen(const char *s) { return Genode::strlen(s); }


size_t strlcat(char *dest, const char *src, size_t count)
{
	size_t dsize = strlen(dest);
	size_t len = strlen(src);
	size_t res = dsize + len;

	/* This would be a bug */
	BUG_ON(dsize >= count);

	dest += dsize;
	count -= dsize;
	if (len >= count)
		len = count-1;
	memcpy(dest, src, len);
	dest[len] = 0;
	return res;
}


size_t strlcpy(char *dest, const char *src, size_t size)
{
	size_t ret = strlen(src);

	if (size) {
		size_t len = (ret >= size) ? size - 1 : ret;
		Genode::memcpy(dest, src, len);
		dest[len] = '\0';
	}
	return ret;
}


void *memscan(void *addr, int c, size_t size)
{
	unsigned char* p = (unsigned char *)addr;

	for (size_t s = 0; s < size; s++, p++)
		if (*p == c)
			break;

	return (void *)p;
}


/*****************
 ** linux/gfp.h **
 *****************/

unsigned long get_zeroed_page(gfp_t gfp_mask)
{
	return (unsigned long)kzalloc(PAGE_SIZE, 0);
}


/******************
 ** linux/log2.h **
 ******************/

int ilog2(u32 n) { return Genode::log2(n); }


/********************
 ** linux/slab.h   **
 ********************/


void *kmem_cache_zalloc(struct kmem_cache *cache, gfp_t flags)
{
	void *ret;
	ret = kmem_cache_alloc(cache, flags);
	memset(ret, 0, cache->size());

	return ret;
}


/**********************
 ** asm-generic/io.h **
 **********************/

void *phys_to_virt(unsigned long address)
{
	return (void *)Lx::Malloc::dma().virt_addr(address);
}


/********************
 ** linux/device.h **
 ********************/

/**
 * Simple driver management class
 */
class Driver : public Genode::List<Driver>::Element
{
	private:

		struct device_driver *_drv; /* Linux driver */

	public:

		Driver(struct device_driver *drv) : _drv(drv)
		{
			list()->insert(this);
		}

		/**
		 * List of all currently registered drivers
		 */
		static Genode::List<Driver> *list()
		{
			static Genode::List<Driver> _list;
			return &_list;
		}

		/**
		 * Match device and drivers
		 */
		bool match(struct device *dev)
		{
			/*
			 *  Don't try if buses don't match, since drivers often use 'container_of'
			 *  which might cast the device to non-matching type
			 */
			if (_drv->bus != dev->bus)
				return false;

			bool ret = _drv->bus->match ? _drv->bus->match(dev, _drv) : true;
			lx_log(DEBUG_DRIVER, "MATCH: %s ret: %u match: %p %p",
			            _drv->name, ret,  _drv->bus->match, _drv->probe);
			return ret;
		}

		/**
		 * Probe device with driver
		 */
		int probe(struct device *dev)
		{
			dev->driver = _drv;

			if (dev->bus->probe)
				return dev->bus->probe(dev);
			else if (_drv->probe)
				return _drv->probe(dev);

			return 0;
		}
};


int driver_register(struct device_driver *drv)
{
	lx_log(DEBUG_DRIVER, "%s at %p", drv->name, drv);
	new (Lx::Malloc::mem()) Driver(drv);
	return 0;
}


int device_add(struct device *dev)
{
	if (dev->driver)
		return 0;

	/* foreach driver match and probe device */
	for (Driver *driver = Driver::list()->first(); driver; driver = driver->next())
		if (driver->match(dev)) {
			int ret = driver->probe(dev);
			lx_log(DEBUG_DRIVER, "Probe return %d", ret);

			if (!ret)
				return 0;
		}

	return 0;
}


void device_del(struct device *dev)
{
	if (dev->driver && dev->driver->remove)
		dev->driver->remove(dev);

	if (dev->bus && dev->bus->remove)
		dev->bus->remove(dev);
}


int device_register(struct device *dev)
{
	return device_add(dev);
}


void device_unregister(struct device *dev)
{
	device_del(dev);
	put_device(dev);
}


int device_is_registered(struct device *dev)
{
	return 1;
}


void device_release_driver(struct device *dev)
{
	/* is usb_unbind_interface(dev); */
	if (dev->driver->remove)
		dev->driver->remove(dev);

	dev->driver = nullptr;
}


void put_device(struct device *dev)
{
	if (dev->ref) {
		dev->ref--;
		return;
	}

	if (dev->release)
		dev->release(dev);
	else if (dev->type && dev->type->release)
		dev->type->release(dev);
}


struct device *get_device(struct device *dev)
{
	dev->ref++;

	return dev;
}


void *dev_get_drvdata(const struct device *dev)
{
	return dev->driver_data;
}


int dev_set_drvdata(struct device *dev, void *data)
{
	dev->driver_data = data; return 0;
}


const char *dev_name(const struct device *dev) { return dev->name; }


/*******************************
 ** asm-generic/bitops/find.h **
 *******************************/

unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
                            unsigned long offset)
{
	unsigned long i  = offset / BITS_PER_LONG;
	offset -= (i * BITS_PER_LONG);

	for (; offset < size; offset++)
		if (addr[i] & (1UL << offset))
			return offset + (i * BITS_PER_LONG);

	return size;
}


long find_next_zero_bit_le(const void *addr,
                           unsigned long size, unsigned long offset)
{
	static unsigned cnt = 0;
	unsigned long max_size = sizeof(long) * 8;
	if (offset >= max_size) {
		Genode::warning("Offset greater max size");
		return offset + size;
	}

	for (; offset < max_size; offset++)
		if (!(*(unsigned long*)addr & (1L << offset)))
			return offset;

	return offset + size;
}


void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
{
	return kzalloc(size, gfp);
}


void *dev_get_platdata(const struct device *dev)
{
	return (void *)dev->platform_data;
}


/*******************************
 ** linux/byteorder/generic.h **
 *******************************/

u16 get_unaligned_le16(const void *p)
{
	const struct __una_u16 *ptr = (const struct __una_u16 *)p;
	return ptr->x;
}


void put_unaligned_le16(u16 val, void *p)
{
	struct __una_u16 *ptr = (struct __una_u16 *)p;
	ptr->x = val;
}

u32 get_unaligned_le32(const void *p)
{
	const struct __una_u32 *ptr = (const struct __una_u32 *)p;
	return ptr->x;
}


void put_unaligned_le32(u32 val, void *p)
{
	struct __una_u32 *ptr = (struct __una_u32 *)p;
	ptr->x = val;
}


u64 get_unaligned_le64(const void *p)
{
	const struct __una_u64 *ptr = (const struct __una_u64 *)p;
	return ptr->x;
}


void put_unaligned_le64(u64 val, void *p)
{
	struct __una_u64 *ptr = (struct __una_u64 *)p;
	ptr->x = val;
}


/**********************************
 ** linux/bitops.h, asm/bitops.h **
 **********************************/

int fls(int x)
{
	if (!x)
		return 0;

	for (int i = 31; i >= 0; i--)
		if (x & (1 << i))
			return i + 1;

	return 0;
}


/*******************
 ** linux/delay.h **
 *******************/

#include <lx_emul/impl/delay.h>


/*********
 ** DMA **
 *********/

struct dma_pool
{
	size_t size;
	int    align;
};

struct dma_pool *dma_pool_create(const char *name, struct device *d, size_t size,
                                 size_t align, size_t alloc)
{
	lx_log(DEBUG_DMA, "size: %zx align:%zx %p", size, align, __builtin_return_address((0)));

	if (align & (align - 1))
		return 0;

	dma_pool *pool = new(Lx::Malloc::mem()) dma_pool;
	pool->align    = Genode::log2((int)align);
	pool->size     = size;
	return pool;
}


void  dma_pool_destroy(struct dma_pool *d)
{
	lx_log(DEBUG_DMA, "close");
	destroy(Lx::Malloc::mem(), d);
}


void *dma_pool_alloc(struct dma_pool *d, gfp_t f, dma_addr_t *dma)
{
	void *addr;
	addr = dma_alloc_coherent(0, d->size, dma, 0);

	lx_log(DEBUG_DMA, "addr: %p size %zx align %x phys: %lx pool %p",
	            addr, d->size, d->align, *dma, d);
	return addr;
}


void  dma_pool_free(struct dma_pool *d, void *vaddr, dma_addr_t a)
{
	lx_log(DEBUG_DMA, "free: addr %p, size: %zx", vaddr, d->size);
	Lx::Malloc::dma().free(vaddr);
}


void *dma_alloc_coherent(struct device *, size_t size, dma_addr_t *dma, gfp_t)
{
	void *addr = Lx::Malloc::dma().alloc(size, PAGE_SHIFT, dma);

	if (!addr)
		return 0;

	lx_log(DEBUG_DMA, "DMA pool alloc addr: %p size %zx align: %d, phys: %lx",
	            addr, size, PAGE_SHIFT, *dma);
	return addr;
}


void dma_free_coherent(struct device *, size_t size, void *vaddr, dma_addr_t)
{
	lx_log(DEBUG_DMA, "free: addr %p, size: %zx", vaddr, size);
	Lx::Malloc::dma().free(vaddr);
}


/*************************
 ** linux/dma-mapping.h **
 *************************/

dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
                                size_t size,
                                enum dma_data_direction dir,
                                struct dma_attrs *attrs)
{
	dma_addr_t phys = (dma_addr_t)Lx::Malloc::dma().phys_addr(ptr);

	if (phys == ~0UL)
		Genode::error("translation virt->phys ", ptr, "->", Genode::Hex(phys), "failed, return ip ",
		     __builtin_return_address(0));

	lx_log(DEBUG_DMA, "virt: %p phys: %lx", ptr, phys);
	return phys;
}


dma_addr_t dma_map_page(struct device *dev, struct page *page,
                        size_t offset, size_t size,
                        enum dma_data_direction dir)
{
	lx_log(DEBUG_DMA, "virt: %p phys: %lx offs: %zx", page->virt, page->phys, offset);
	return page->phys + offset;
}


int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
                     int nents, enum dma_data_direction dir,
                     struct dma_attrs *attrs) { return nents; }


/*********************
 ** linux/kthread.h **
 *********************/

struct task_struct *kthread_run(int (*fn)(void *), void *arg, const char *n, ...)
{
	/*
	 * This is just called for delayed device scanning (see
	 * 'drivers/usb/storage/usb.c')
	 */
	lx_log(DEBUG_THREAD, "Run %s", n);

	new (Lx::Malloc::mem()) Lx::Task((void (*)(void *))fn, arg, n,
	                                 Lx::Task::PRIORITY_2,
	                                 Lx::scheduler());
	return 0;
}


/*************************
 ** linux/scatterlist.h **
 *************************/

struct scatterlist *sg_next(struct scatterlist *sg)
{
	if (sg->last)
		return 0;

	return sg++;
}


struct page *sg_page(struct scatterlist *sg)
{
	if (!sg)
		return 0;
	
	return (page *)sg->page_link;
}


void *sg_virt(struct scatterlist *sg)
{
	if (!sg || !sg->page_link)
		return 0;

	struct page *page = (struct page *)sg->page_link;
	return (void *)((unsigned long)page->virt + sg->offset);
}


/********************
 ** linux/ioport.h **
 ********************/

struct resource * devm_request_mem_region(struct device *dev, resource_size_t start,
                                          resource_size_t n, const char *name)
{
	struct resource *r = (struct resource *)kzalloc(sizeof(struct resource), GFP_KERNEL);
	r->start = start;
	r->end   = start + n - 1;
	r->name  = name;

	return r;
}


/*****************
 ** linux/smp.h **
 *****************/

int smp_call_function_single(int cpu, smp_call_func_t func, void *info,
                             int wait) { func(info); return 0; }


/****************
 ** Networking **
 ****************/


/*************************
 ** linux/etherdevice.h **
 *************************/

struct net_device *alloc_etherdev(int sizeof_priv)
{
	net_device *dev = new (Lx::Malloc::mem()) net_device();

	dev->mtu      = 1500;
	dev->hard_header_len = 0;
	dev->priv     = kzalloc(sizeof_priv, 0);
	dev->dev_addr = dev->_dev_addr;
	memset(dev->_dev_addr, 0, sizeof(dev->_dev_addr));

	return dev;
}


int is_valid_ether_addr(const u8 *addr)
{
	/* is multicast */
	if ((addr[0] & 0x1))
		return 0;

	/* zero */
	if (!(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]))
		return 0;

	return 1;
}


/*****************
 ** linux/mii.h **
 *****************/

/**
 * Restart NWay (autonegotiation) for this interface
 */
int mii_nway_restart (struct mii_if_info *mii)
{
	int bmcr;
	int r = -EINVAL;
	enum { 
		BMCR_ANENABLE  = 0x1000, /* enable auto negotiation */
		BMCR_ANRESTART = 0x200,  /* auto negotation restart */
	};

	/* if autoneg is off, it's an error */
	bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);

	if (bmcr & BMCR_ANENABLE) {
		printk("Reanable\n");
		bmcr |= BMCR_ANRESTART;
		mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
		r = 0;
	}

	return r;
}


int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
{
	ecmd->duplex = DUPLEX_FULL;
	return 0;
}


u8 mii_resolve_flowctrl_fdx(u16 lcladv, u16 rmtadv)
{
	u8 cap = 0;

	if (lcladv & rmtadv & ADVERTISE_PAUSE_CAP) {
		cap = FLOW_CTRL_TX | FLOW_CTRL_RX;
	} else if (lcladv & rmtadv & ADVERTISE_PAUSE_ASYM) {
		if (lcladv & ADVERTISE_PAUSE_CAP)
			cap = FLOW_CTRL_RX;
		else if (rmtadv & ADVERTISE_PAUSE_CAP)
			cap = FLOW_CTRL_TX;
	}

	 return cap;
}

int mii_link_ok (struct mii_if_info *mii)
{
	/* first, a dummy read, needed to latch some MII phys */
	mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
	        return 1;
	return 0;
}


unsigned int mii_check_media (struct mii_if_info *mii,
                              unsigned int ok_to_print,
                              unsigned int init_media)
{
	if (mii_link_ok(mii))
		netif_carrier_on(mii->dev);
	else
		netif_carrier_off(mii->dev);
	return 0;
}


/******************
 ** linux/log2.h **
 ******************/


int rounddown_pow_of_two(u32 n)
{
	return 1U << Genode::log2(n);
}


/*****************
 ** linux/nls.h **
 *****************/

int utf16s_to_utf8s(const wchar_t *pwcs, int len,
                    enum utf16_endian endian, u8 *s, int maxlen)
{
	/*
	 * We do not convert to char, we simply copy the UTF16 plane 0 values
	 */
	u16 *out = (u16 *)s;
	u16 *in  = (u16 *)pwcs;
	int length = min(len, maxlen / 2);
	for (int i = 0; i < length; i++)
		out[i] = in[i];

	return 2 * length;
}

/**********************
 ** linux/notifier.h **
 **********************/

int raw_notifier_chain_register(struct raw_notifier_head *nh,
                                struct notifier_block *n)
{
	struct notifier_block *nl = nh->head;
	struct notifier_block *pr = 0;
	while (nl) {
		if (n->priority > nl->priority)
			break;
		pr = nl;
		nl = nl->next;
	}

	n->next = nl;
	if (pr)
		pr->next = n;
	else
		nh->head = n;

	return 0;
}


int raw_notifier_call_chain(struct raw_notifier_head *nh,
                            unsigned long val, void *v)
{
	int ret = NOTIFY_DONE;
	struct notifier_block *nb = nh->head;

	while (nb) {

		ret = nb->notifier_call(nb, val, v);
		if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK)
			break;

		nb = nb->next;
	}

	return ret;
}


int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
                                     struct notifier_block *n)
{
	return raw_notifier_chain_register((struct raw_notifier_head *)nh, n);
}


int blocking_notifier_call_chain(struct blocking_notifier_head *nh,
                                 unsigned long val, void *v)
{
	return raw_notifier_call_chain((struct raw_notifier_head *)nh, val, v);
}


/*******************
 ** linux/timer.h **
 *******************/

#include <lx_emul/impl/timer.h>
#include <lx_emul/impl/sched.h>

signed long schedule_timeout_uninterruptible(signed long timeout)
{
	lx_log(DEBUG_COMPLETION, "%ld\n", timeout);
	schedule_timeout(timeout);
	return 0;
}


/************************
 ** linux/completion.h **
 ************************/

#include <lx_emul/impl/completion.h>


static void _completion_timeout(unsigned long t)
{
	Lx::Task *task = (Lx::Task *)t;
	task->unblock();
}


long __wait_completion(struct completion *work, unsigned long timeout)
{
	timer_list t;
	unsigned long j = timeout ? jiffies + timeout : 0;

	if (timeout) {
		setup_timer(&t, _completion_timeout,
		            (unsigned long)Lx::scheduler().current());
		mod_timer(&t, j);
	}

	while (!work->done) {

		if (j && j <= jiffies)
			return 0;

		Lx::Task *task = Lx::scheduler().current();
		work->task = (void *)task;
		task->block_and_schedule();
	}

	if (timeout)
		del_timer(&t);

	work->done = 0;

	return j ? j - jiffies : 1;
}


/***********************
 ** linux/workqueue.h **
 ***********************/

#include <lx_emul/impl/work.h>


void tasklet_init(struct tasklet_struct *t, void (*f)(unsigned long), unsigned long d)
{
	t->func    = f;
	t->data    = d;
}


void tasklet_schedule(struct tasklet_struct *tasklet)
{
	Lx::Work *lx_work = (Lx::Work *)tasklet_wq->task;
	lx_work->schedule_tasklet(tasklet);
	lx_work->unblock();
}


void tasklet_hi_schedule(struct tasklet_struct *tasklet)
{
	tasklet_schedule(tasklet);
}


struct workqueue_struct *create_singlethread_workqueue(char const *name)
{
	workqueue_struct *wq = (workqueue_struct *)kzalloc(sizeof(workqueue_struct), 0);
	Lx::Work *work = Lx::Work::alloc_work_queue(&Lx::Malloc::mem(), name);
	wq->task       = (void *)work;

	return wq;
}


struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags,
                                         int max_active, ...)
{
	return create_singlethread_workqueue(fmt);
}


/******************
 ** linux/wait.h **
 ******************/

#include <lx_emul/impl/wait.h>

/*******************
 ** lib/hexdump.c **
 *******************/

#include <linux/ctype.h>

/**
 * hex_to_bin - convert a hex digit to its real value
 * @ch: ascii character represents hex digit
 *
 * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
 * input.
 */
int hex_to_bin(char ch)
{
	if ((ch >= '0') && (ch <= '9'))
		return ch - '0';
	ch = tolower(ch);
	if ((ch >= 'a') && (ch <= 'f'))
		return ch - 'a' + 10;
	return -1;
}

/**
 * hex2bin - convert an ascii hexadecimal string to its binary representation
 * @dst: binary result
 * @src: ascii hexadecimal string
 * @count: result length
 *
 * Return 0 on success, -1 in case of bad input.
 */
int hex2bin(u8 *dst, const char *src, size_t count)
{
	while (count--) {
		int hi = hex_to_bin(*src++);
		int lo = hex_to_bin(*src++);

		if ((hi < 0) || (lo < 0))
			return -1;

		*dst++ = (hi << 4) | lo;
	}
	return 0;
}


/*******************
 ** linux/timer.h **
 *******************/

extern "C" void init_timer(struct timer_list *) { }

struct callback_timer {
	void (*function)(unsigned long);
	unsigned long data;
};

/*
 * For compatibility with 4.4.3 drivers, the argument of this callback function
 * is the 'data' member of the 'timer_list' object, which normally points to
 * the 'timer_list' object itself when initialized with 'timer_setup()', but
 * here it was overridden in 'setup_timer()' to point to the 'callback_timer'
 * object instead.
 */
static void timer_callback(struct timer_list *t)
{
	struct callback_timer * tc = (struct callback_timer *)t;
	tc->function(tc->data);
}

extern "C" void setup_timer(struct timer_list *timer, void (*function)(unsigned long),
                            unsigned long data)
{
	callback_timer * tc = new (Lx::Malloc::mem()) callback_timer;
	tc->function = function;
	tc->data     = data;

	timer_setup(timer, timer_callback, 0u);
	timer->data = (unsigned long)tc;
}
