/*
 * szedata 2 test module
 *
 * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
 *
 * Licensed under GPLv2
 */

#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/timer.h>

#include "szedata2k.h"

#define SZET_BLOCK_COUNT	(150*1024*1024/SZET_BLOCK_SIZE)
#define SZET_BLOCK_SIZE		PAGE_SIZE

static void szetest_timer(unsigned long unused);
static void szetest_work(struct work_struct *work);

static DEFINE_TIMER(timer, szetest_timer, 0, 0);
static DECLARE_DELAYED_WORK(work, szetest_work);
static struct szedata2 *sd, *sd1;
static struct file *filp;
static fl_owner_t fl_owner;

static void szetest_timer(unsigned long unused)
{
	static unsigned int shown;

	if (shown++ < 5)
		printk(KERN_DEBUG "timer fired\n");

	mod_timer(&timer, jiffies + HZ);
}

static void szetest_work(struct work_struct *work)
{
	struct file *filp1;

	filp1 = open_pathname(AT_FDCWD, "/dev/szedataII3", O_RDWR, 0);
	if (!IS_ERR(filp1)) {
		printk(KERN_WARNING "szedata2: unregistered open didn't "
				"fail\n");
		filp_close(filp1, current->files);
	}
	printk(KERN_DEBUG "closing\n");
	filp_close(filp, fl_owner);
}

static int szetest_open(struct szedata2 *sd)
{
	return 0;
}

static void szetest_close(struct szedata2 *sd)
{
}

/*
 * INIT test
 */

static int szetest_test_init(void)
{
	struct szedata2 *sda[8];
	struct pci_dev *pdev;
	unsigned int a, b;
	int ret;
#ifdef SZETEST_FAULT
	/* some fault injection */
	if (!szedata2_register(NULL) || !szedata2_register(ERR_PTR(-EBUSY)))
		printk(KERN_WARNING "szedata2_register bogus didn't fail\n");
	szedata2_destroy(NULL);
	szedata2_destroy(ERR_PTR(-EBUSY));
#endif
	/* real device with private size 1 */
	/* expects one persistent pci device/bridge */
	pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
	pci_set_dma_mask
	sd = alloc_szedata2(1, &pdev->dev);
	pci_dev_put(pdev);
	if (IS_ERR(sd)) {
		printk(KERN_ERR "alloc_szedata2 A failed\n");
		ret = PTR_ERR(sd);
		goto err_dest;
	}
	/* owner not set test */
	if (!szedata2_register(sd))
		printk(KERN_WARNING "szedata2_register A didn't fail\n");
	sd->owner = THIS_MODULE;
	sd->open = szetest_open;
	sd->close = szetest_close;

	ret = szedata2_alloc_dmaspace(sd, SZE2_MMIO_RX, 4,
			SZET_BLOCK_COUNT, PAGE_ALIGN(SZET_BLOCK_SIZE));
	if (ret) {
		printk(KERN_ERR "szedata2_alloc_dmaspace failed\n");
		goto err_dest;
	}

	ret = szedata2_register(sd);
	if (ret) {
		printk(KERN_ERR "szedata2_register A failed\n");
		goto err_dest;
	}

	/* too much devices test */
	for (a = 0; a < ARRAY_SIZE(sda); a++) {
		sda[a] = alloc_szedata2(0, NULL);
		if (IS_ERR(sda[a])) {
			printk(KERN_ERR "alloc_szedata2 B failed\n");
			ret = PTR_ERR(sda[a]);
			goto err_dest;
		}
		sda[a]->owner = THIS_MODULE;
		ret = szedata2_register(sda[a]);
		if (ret && a + 1 < ARRAY_SIZE(sda)) {
			printk(KERN_ERR "szedata2_register B failed\n");
			for (b = a + 1; b > 0; b--)
				szedata2_destroy(sda[b - 1]);
			goto err_dest;
		} else if (!ret && a + 1 == ARRAY_SIZE(sda))
			printk(KERN_WARNING "szedata2_register B didn't "
					"fail\n");
		printk(KERN_DEBUG "%u\n", a);
	}
	sd1 = sda[ARRAY_SIZE(sda) - 2]; /* last successful */
	sda[ARRAY_SIZE(sda) - 2] = NULL; /* don't free this */

	msleep(1000); /* wait for udev */
	filp = open_pathname(AT_FDCWD, "/dev/szedataII3", O_RDWR, 0);
	if (IS_ERR(filp))
		printk(KERN_ERR "can't open szedata node\n");
	else {
		fl_owner = current->files;
		schedule_delayed_work(&work, HZ); /* close it in a sec */
	}

	for (a = 0; a < ARRAY_SIZE(sda); a++) {
		printk(KERN_DEBUG "destroying: %u\n", a);
		szedata2_destroy(sda[a]);
	}

	/* register twice test */
	if (!szedata2_register(sd1))
		printk(KERN_WARNING "szedata2_register C didn't fail\n");

	if (!sd->private || sd1->private)
		printk(KERN_WARNING "szedata2 privates: %p %p\n", sd->private,
				sd1->private);

	return 0;
err_dest:
	szedata2_destroy(sd);
	szedata2_destroy(sd1);
	return ret;
}

static void szetest_kill_init(void)
{
	szedata2_destroy(sd);
	szedata2_destroy(sd1);
}

/*
 * RX test
 */

static int szetest_test_rx(void)
{
	mod_timer(&timer, jiffies + HZ);
	return 0;
}

static void szetest_kill_rx(void)
{
	del_timer_sync(&timer);
}

static int szetest_init(void)
{
	int ret;

	ret = szetest_test_init(); /* fills sd, sd1 */
	if (ret)
		goto err;

	ret = szetest_test_rx();
	if (ret)
		goto err;

	return 0;
err:
	return ret;
}

static void szetest_exit(void)
{
	szetest_kill_init();
	szetest_kill_rx();
}

module_init(szetest_init);
module_exit(szetest_exit);

MODULE_LICENSE("GPL");