
#include <linux/module.h>
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* everything... */
#include <linux/errno.h>    /* error codes */
#include <linux/types.h>    /* size_t */
#include <linux/init.h>     /* module_init */
#include <linux/mm.h>     /* module_init */
#include <asm/page.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>	/* for get_user and put_user */
#include <asm/io.h>
#include "../../../api.h"

#define IOCTL_KTAPI_DRV_IO_BLOCK 100


#define DRIVER_AUTHOR "Ole Larsen"
#define DRIVER_DESC   "KTAPI Driver"

int major = 0;

static int ktapi_driver_open( struct inode* inode, struct file* filp );

static int ktapi_driver_release( struct inode* inode, struct file* filp );


/*
static int ktapi_driver_ioctl (struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long long arg);
*/
static int ktapi_driver_ioctl (struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg);

static int ktapi_driver_mmap(struct file * file, struct vm_area_struct * vma);


struct file_operations ktapi_driver_fops =
{

	open: ktapi_driver_open,
	release: ktapi_driver_release,
	mmap: ktapi_driver_mmap,
	ioctl:   ktapi_driver_ioctl,
                 // nothing more, fill with NULLs
};


/*
struct file_operations ktapi_driver_fops = {
	.mmap = ktapi_driver_mmap,
	.ioctl = ktapi_driver_ioctl,
	.open = ktapi_driver_open,
	.release = ktapi_driver_release,	// a.k.a. close 
};
*/
static int ktapi_driver_opened = 0;        /* True iff the device is open */


unsigned int OS_Execute_IO_Block(_API_IO_Struct * IO_Block,unsigned int Size)
{
unsigned int Index1=0;
unsigned int Index2=0;
unsigned int Data[0x20];  
unsigned int uiLoop;


	while((IO_Block->Command&0xff)!=_API_iob_END)
	{

		Index1=(IO_Block->Command>>8)&0x1f;
		Index2=(IO_Block->Command>>16)&0x1f;

		switch(IO_Block->Command & 0xff)
		{
		case  _API_iob_ADD:
			Data[Index1]+=IO_Block->Data;
			break;
		case  _API_iob_AND:
			Data[Index1]&=IO_Block->Data;
			break;
		case _API_iob_OR:
			Data[Index1]|=IO_Block->Data;
			break;
		case  _API_iob_XOR:
			Data[Index1]^=IO_Block->Data;
			break;
		case  _API_iob_SHL:
			Data[Index1]<<=IO_Block->Data;
			break;
		case  _API_iob_SHR:
			Data[Index1]>>=IO_Block->Data;
			break;
		case  _API_iob_Set_Data:
			Data[Index1]=IO_Block->Data;
			break;
		case  _API_iob_Copy:
			Data[Index1]=Data[Index2];
			break;
		case  _API_iob_IO_Read_Byte:
			*(unsigned char *)&Data[Index1]=inb(Data[Index2]);
			break;
		case  _API_iob_IO_Read_Word:
			*(unsigned short *)&Data[Index1]=inw(Data[Index2]);
			break;
		case  _API_iob_IO_Read_Dword:
			Data[Index1]=inl(Data[Index2]);
			break;
		case  _API_iob_IO_Write_Byte:
			outb(Data[Index1],Data[Index2]);
			break;
		case  _API_iob_IO_Write_Word:
			outw(Data[Index1],Data[Index2]);
			break;
		case  _API_iob_IO_Write_Dword:
			outl(Data[Index1],Data[Index2]);
			break;
		case _API_iob_IO_Delay:
			for(uiLoop=0;uiLoop<IO_Block->Data;uiLoop++)
				inb(Data[Index1]);
			break;			

		}
		IO_Block++;
	}

	return Data[Index1];
}




static int ktapi_driver_ioctl (struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{
    
    
    switch(cmd)
    {
    case IOCTL_KTAPI_DRV_IO_BLOCK:
    	
    	return OS_Execute_IO_Block((_API_IO_Struct * )arg,0);
   	
    }

	return 0;

}


static int ktapi_driver_mmap(struct file * file, struct vm_area_struct * vma)
{
	size_t size = vma->vm_end - vma->vm_start;
 
	// Remap-pfn-range will mark the range VM_IO and VM_RESERVED 
	if (remap_pfn_range(vma,vma->vm_start,vma->vm_pgoff,size,vma->vm_page_prot))
	{
		return -EAGAIN;
	}
	return 0;
}



static int ktapi_driver_open( struct inode* inode, struct file* filp )
{
	
	/* only one of these devices, so whinge if bad (non-0) minor number */
	
	if( MINOR( inode->i_rdev ) != 0 )
		return -ENODEV;
	
	/* The device can be opened only once */
	if( ktapi_driver_opened )
		return -EBUSY;
	
	ktapi_driver_opened = 1;
	
	
	return 0;
}

static int ktapi_driver_release( struct inode* inode, struct file* filp )
{

	ktapi_driver_opened = 0;

	return 0; 
}


static int __init name_of_initialization_routine(void)
{

   int res;

  /* Register *no* symbol table explicitly so that no functions other than
     those in the table will be externally visible - this can't possibly
     fail, so don't bother to check... */
  //register_symtab( NULL );

  if( ( res = register_chrdev( major, "ktapi", &ktapi_driver_fops ) ) < 0 )
  {
    printk( KERN_INFO "ktapi_driver: can\'t get major number\n" );
    return res;
  }
  if( major == 0 )
    major = res;


	return 0;
}
static void __exit name_of_cleanup_routine(void)
{
   
	unregister_chrdev( major, "ktapi" );

}
module_init(name_of_initialization_routine);
module_exit(name_of_cleanup_routine);

MODULE_LICENSE("GPL");

MODULE_AUTHOR(DRIVER_AUTHOR);	/* Who wrote this module? */
MODULE_DESCRIPTION(DRIVER_DESC);	/* What does this module do */

MODULE_SUPPORTED_DEVICE("ktapi");

