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

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

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

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

V_LOCAL	s32_t V_CALL
section_ioctl(void *object,u32_t code,void *parameters)
{
	io_section_p self = (io_section_p)object;
	switch(code)
    {
		case pctl_get_name:
			if(self->section_name && parameters)
			{
				strCopy((char*)parameters,self->section_name,_MAX_PATH);
				return 0;
			}
			return -1;
    	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) = self->base;
			return 0;
		case pctl_get_size:
        	if(!parameters)
            	return -1;
           	*((u32_t*)parameters) = self->size;
			return 0;
		case pctl_is_eof:
        	if(!parameters)
            	return -1;
           	*((u32_t*)parameters) = (self->size == self->ptr) ? 1 : 0;
			return 0;
		default:
			break;
	}
	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;
}

io_section_p V_CALL
ioAllocSection(u32_t real_port_size,void *mmap_base,u32_t mmap_size)
{
	return ioAllocNamedSection(real_port_size,mmap_base,mmap_size,NULL);
}

/* Factory function */
io_section_p V_CALL
ioAllocNamedSection(u32_t real_port_size,void *mmap_base,u32_t mmap_size,
	char *section_name)
{
	u32_t name_len;
	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;
	}
	if(section_name)
		name_len = INT_ALIGN(strlen(section_name)+1);
	else
		name_len = 0;
	port=(io_section_t*)ioAllocPort(real_port_size+name_len);
    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;
	if(section_name)
	{
		/* Bottom-up allocate space for name */
		port->section_name = (char*)((u8_t*)port + real_port_size);
		strCopy(port->section_name,section_name,name_len);
	}
	else
		port->section_name = NULL;
	return port;
}
