/*
 * 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.
 *
 */

#define __NO_VERSION__
#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);
void bcopy(void *pSource, void *pDest, dword dwLength);
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)
{
	dia_load_t *pDivaLoad;
	dia_start_t *pDivaStart;
	dia_config_t *pDivaConfig;
	dia_log_t *pDivaLog;
	byte *pUserCards, card_i;
	word wCardNum;
	mem_block_t *mem_block;

	switch (command)
	{
		case DIA_IOCTL_CONFIG:
			pDivaConfig = (dia_config_t *) arg;
			
			if (!verify_area(VERIFY_READ, pDivaConfig, sizeof(dia_config_t)))
			{
				DivasCardConfig(pDivaConfig);
			}
			else
			{
				printk(KERN_WARNING "Divas: Unable to complete CONFIG ioctl (verify area failed)\n");
				return -1;
			}
				return 0;

		case DIA_IOCTL_DETECT:
			pUserCards = (byte *) arg;

			if (!verify_area(VERIFY_WRITE, pUserCards, 20))
			{
				put_user(DivasCardNext, pUserCards++);

				for (card_i=1; card_i < 20; card_i++)
				{
					put_user((byte) DivasCards[card_i - 1].cfg.card_type, pUserCards++);
				}
			}
			else
			{
				printk(KERN_WARNING "Divas: Unable to complete DETECT ioctl (verify area failed)\n");
				return -1;
			}
			return 0;

		case DIA_IOCTL_START:
			pDivaStart = (dia_start_t *) arg;
			
			if (!verify_area(VERIFY_READ, pDivaStart, sizeof(dia_start_t)))
			{
				return DivasCardStart(pDivaStart->card_id);
			}
			else
			{
				printk(KERN_WARNING "Divas: Unable to complete START ioctl (verify area failed)\n");
				return -1;
			}


		case DIA_IOCTL_FLAVOUR:
			return 0;

		case DIA_IOCTL_LOAD:
			pDivaLoad = (dia_load_t *) arg;
			if (!verify_area(VERIFY_READ, pDivaLoad->code,pDivaLoad->length))
			{
				if (DivasCardLoad(pDivaLoad))
				{
					printk(KERN_WARNING "Divas: Error loading DIVA Server adapter\n");
					return -EINVAL;
				}
			}
			else
			{
				printk(KERN_WARNING "Divas: Error in LOAD parameters (verify failed)\n");
				return -EINVAL;
			}
			return 0;

		case DIA_IOCTL_LOG:
			pDivaLog = (dia_log_t *) arg;
			
			if (!verify_area(VERIFY_READ, pDivaLog, sizeof(dia_log_t)))
			{
				DivasLog(pDivaLog);
			}
			else
			{
				printk(KERN_WARNING "Divas: Unable to complete LOG ioctl (verify area failed)\n");
				return -1;
			}
			return 0;

		case DIA_IOCTL_XLOG_REQ:
			
			if (!verify_area(VERIFY_READ, (void *)arg, sizeof(word)))
			{
				wCardNum = * (word *) arg;
				DivasXlogReq(wCardNum);
			}
			else
			{
				printk(KERN_WARNING "Divas: Unable to complete XLOG_REQ ioctl (verify area failed)\n");
				return -1;
			}
			return 0;

		case DIA_IOCTL_GET_NUM:
			
			if (!verify_area(VERIFY_WRITE, (void *)arg, sizeof(int)))
			{
				* (int *) arg = DivasCardNext;
			}
			else
			{
				printk(KERN_WARNING "Divas: Unable to complete GET_NUM ioctl (verify area failed)\n");
				return -1;
			}
			return 0;

		case DIA_IOCTL_GET_LIST:
			DPRINTF(("divas: DIA_IOCTL_GET_LIST"));
			
			if (!verify_area(VERIFY_WRITE, (void *)arg, sizeof(dia_card_list_t)))
			{
				DivasGetList((dia_card_list_t *)arg);
			}
			else
			{
				printk(KERN_WARNING "Divas: Unable to complete GET_LIST ioctl (verify area failed)\n");
				return -1;
			}
			return 0;

		case DIA_IOCTL_GET_MEM:
			mem_block = (mem_block_t *) arg;
			
			if (!verify_area(VERIFY_WRITE, mem_block, sizeof(mem_block_t)))
			{
				DivasGetMem(mem_block);
			}
			else
			{
				printk(KERN_WARNING "Divas: Unable to complete GET_MEM ioctl (verify area failed)\n");
				return -1;
			}
			return 0;

		case DIA_IOCTL_UNLOCK:
			UnlockDivas();
			return 0;

		default:
			printk(KERN_WARNING "Divas: Unknown IOCTL Received by DIVA Server Driver(%d)\n", command);
			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 0;
	}

	pHeadItem = (klog_t *) DivasLogFifoRead();

	if (pHeadItem)
	{
		bcopy(pHeadItem, pClientLogBuffer, 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;
	}
}
