#include <../source/TAL/objbase.h>
#include <../source/vfs/iobase.h>
#include <../source/vfs/fbase.h>
#include <../source/vfs/mbase.h>

/*
 * Implementation of memory-mapped, read-only I/O port
 * TODO: ioCreateSectionEx()
 */

#define	MAPPED_SECTION(obj)		((io_section_t*)(obj))

V_LOCAL u32_t V_CALL
section_read(void *object,void *buf,u32_t len)
{
	u32_t avail=MAPPED_SECTION(object)->size-MAPPED_SECTION(object)->ptr;
	if(avail<len)
		len=avail;
	memCopy(buf,MAPPED_SECTION(object)->base+MAPPED_SECTION(object)->ptr,len);
	MAPPED_SECTION(object)->ptr+=len;
	return len;
}

V_LOCAL u32_t V_CALL
section_seek(void *object,s32_t offset,int whence)
{
	switch(whence)
	{
		case SEEK_SET:
			/* this cast also handles incorrect negative offsets */
			if((u32_t)offset > MAPPED_SECTION(object)->size)
				return INVALID_IO_OFFSET;
			MAPPED_SECTION(object)->ptr=(u32_t)offset;
			break;
		case SEEK_CUR:
			if(offset<0)
			{
				if((u32_t)(-offset) > MAPPED_SECTION(object)->ptr)
					return INVALID_IO_OFFSET;
			}
			else
			{
				if((u32_t)(offset) > 
					(MAPPED_SECTION(object)->size-MAPPED_SECTION(object)->ptr) )
					return INVALID_IO_OFFSET;
			}
			MAPPED_SECTION(object)->ptr=MAPPED_SECTION(object)->ptr + offset;
			break;
		case SEEK_END:
			if((u32_t)(-offset) > MAPPED_SECTION(object)->size)
				return INVALID_IO_OFFSET;
			MAPPED_SECTION(object)->ptr=MAPPED_SECTION(object)->size + offset;
			break;
		default:
			return INVALID_IO_OFFSET;
	}
	return MAPPED_SECTION(object)->ptr;
}

V_LOCAL u32_t V_CALL
section_tell(void *object)
{
	return MAPPED_SECTION(object)->ptr;
}

V_LOCAL	int V_CALL
section_ioctl(void *object,int code,void *parameters)
{
	switch(code)
    {
    	case pctl_get_type:
        	if(!parameters)
            	return -1;
           	*((u32_t*)parameters)=PCTL_BLOCK_DEVICE | PCTL_SUPPORT_MMAP;
            return 0;
		case pctl_map_ispace:
        	if(!parameters)
            	return -1;
           	*((void**)parameters)=MAPPED_SECTION(object)->base;
			return 0;
	}
	return -1;
}

V_LOCAL u32_t V_CALL
section_destroy(vhandle_t handle)
{
	io_section_p self=(io_section_p)handle;
    if(--self->vtbl.base.ref_count)
    	return self->vtbl.base.ref_count;
	memFreeObject(self);
	return 0;
}

#undef	MAPPED_SECTION

/* Factory function */
io_section_p V_CALL
ioAllocSection(u32_t real_port_size,void *mmap_base,u32_t mmap_size)
{
	io_section_t *port;
	if(real_port_size < sizeof(io_section_t))
	{
		/* Descendant can't be less in size than ancestor */
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"ioAllocSection(): "
			"section must be at least %u bytes, got %u.\n",
			sizeof(io_section_t),real_port_size);
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	port=(io_section_t*)ioAllocPort(real_port_size);
    port->vtbl.base.destroy=section_destroy;
	port->vtbl.read=section_read;
	port->vtbl.seek=section_seek;
	port->vtbl.tell=section_tell;
	port->vtbl.ioctl=section_ioctl;
	port->base=(u8_t*)mmap_base;
	port->size=mmap_size;
	port->ptr=0;
	return port;
}
