/*
 * Copyright (C) Eicon Technology Corporation, 2000.
 *
 * Eicon File Revision :    1.12  
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 *
 */

#include <linux/module.h>

#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/slab.h>

#undef N_DATA

#include "adapter.h"
#include "divas.h"
#include "divalog.h"

extern int DivasCardNext;
void UxPause(long ms);
int DivasGetMem(mem_block_t *);

#define DIA_IOCTL_UNLOCK 12
void UnlockDivas(void);

int do_ioctl(struct inode *pDivasInode, struct file *pDivasFile, 
			 unsigned int command, unsigned long arg)
{
	byte *pUserCards, card_i;
	word wCardNum;

	switch (command)
	{
		case DIA_IOCTL_CONFIG:
		{
			dia_config_t DivaConfig;
			if (copy_from_user(&DivaConfig, (void *)arg, sizeof(dia_config_t)))
				return -EFAULT;
			DivasCardConfig(&DivaConfig);
			return 0;
		}

		case DIA_IOCTL_DETECT:
			pUserCards = (byte *) arg;

			if (!verify_area(VERIFY_WRITE, pUserCards, 20))
			{
				if(__put_user(DivasCardNext, pUserCards++))
					return -EFAULT;

				for (card_i=1; card_i < 20; card_i++)
				{
					if(__put_user((byte) DivasCards[card_i - 1].cfg.card_type, pUserCards++))
						return -EFAULT;
				}
			}
			else return -EFAULT;

			return 0;

		case DIA_IOCTL_START:
		{
			dia_start_t DivaStart;
			if (copy_from_user(&DivaStart, (void *)arg, sizeof(dia_start_t)))
				return -EFAULT;
			return DivasCardStart(DivaStart.card_id);
		}

		case DIA_IOCTL_FLAVOUR:
			return 0;

		case DIA_IOCTL_LOAD:
		{
			dia_load_t DivaLoad;
			if(copy_from_user(&DivaLoad, (void *)arg, sizeof(dia_load_t)))
				return -EFAULT;
			if (!verify_area(VERIFY_READ, DivaLoad.code,DivaLoad.length))
			{
				if (DivasCardLoad(&DivaLoad))
				{
					printk(KERN_WARNING "Divas: Error loading DIVA Server adapter\n");
					return -EINVAL;
				}
				return 0;
			}
			return -EFAULT;
		}
		case DIA_IOCTL_LOG:
		{
			dia_log_t DivaLog;
			if (copy_from_user(&DivaLog, (void *) arg, sizeof(dia_log_t)))
				return -EFAULT;
			DivasLog(&DivaLog);
			return 0;
		}

		case DIA_IOCTL_XLOG_REQ:
			if(get_user(wCardNum, (word *) arg))
				return -EFAULT;
			DivasXlogReq(wCardNum);
			return 0;

		case DIA_IOCTL_GET_NUM:
			if(put_user(DivasCardNext, (int *)arg))
				return -EFAULT;
			return 0;

		case DIA_IOCTL_GET_LIST:
		{
			dia_card_list_t cards;
			DPRINTF(("divas: DIA_IOCTL_GET_LIST"));
			DivasGetList(&cards);
			if(copy_to_user((void *)arg, &cards, sizeof(cards)))
				return -EFAULT;
			return 0;
		}
		case DIA_IOCTL_GET_MEM:
		{
			mem_block_t mem_block;
			if (copy_from_user(&mem_block, (void *)arg, sizeof(mem_block_t)))
				return -EFAULT;
			DivasGetMem(&mem_block);
			return 0;
		}

		case DIA_IOCTL_UNLOCK:
			UnlockDivas();
			return 0;

		default:
			return -EINVAL;
	}
	return -EINVAL;
}

unsigned int do_poll(struct file *pFile, struct poll_table_struct *pPollTable)
{
	word wMask = 0;

	if (!DivasLogFifoEmpty())
		wMask |= POLLIN | POLLRDNORM;
	return wMask;
}

ssize_t do_read(struct file *pFile, char *pUserBuffer, size_t BufferSize, loff_t *pOffset)
{
	klog_t *pClientLogBuffer = (klog_t *) pUserBuffer;
	klog_t *pHeadItem;

	if (BufferSize < sizeof(klog_t))
	{
		printk(KERN_WARNING "Divas: Divalog buffer specifed a size that is too small (%d - %d required)\n",
			BufferSize, sizeof(klog_t));
		return -EIO;
	}

	pHeadItem = (klog_t *) DivasLogFifoRead();

	if (pHeadItem)
	{
		memcpy(pClientLogBuffer, pHeadItem, sizeof(klog_t));
		kfree(pHeadItem);
		return sizeof(klog_t);
	}

	return 0;
}
static int private_usage_count;

int do_open(struct inode *pInode, struct file *pFile)
{
	MOD_INC_USE_COUNT;
#ifdef MODULE
	private_usage_count++;
#endif
	return 0;
}

int do_release(struct inode *pInode, struct file *pFile)
{
	MOD_DEC_USE_COUNT;
#ifdef MODULE
	private_usage_count--;
#endif
	return 0;
}

void UnlockDivas(void)
{
	while (private_usage_count > 0)
	{
		private_usage_count--;
		MOD_DEC_USE_COUNT;
	}
}
