/*
 * dev_fbf.c - FBF (Frame Buffer) generic device API implementation.
 * 
 * Author  Alain Greiner    (2016,2017,2018,2019)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MK
 *
 * ALMOS-MKH.is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH.is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-kernel; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <kernel_config.h>
#include <hal_kernel_types.h>
#include <hal_gpt.h>
#include <hal_drivers.h>
#include <thread.h>
#include <printk.h>
#include <string.h>
#include <chdev.h>
#include <dev_fbf.h>

/////////////////////////////////////////////////////////////////////////////////////////
// Extern global variables
/////////////////////////////////////////////////////////////////////////////////////////

extern chdev_directory_t  chdev_dir;         // allocated in kernel_init.c

///////////////////////////////////////////
char * dev_fbf_cmd_str( uint32_t cmd_type )
{
    if     ( cmd_type == FBF_READ       )  return "READ";
    else if( cmd_type == FBF_WRITE      )  return "WRITE";
    else if( cmd_type == FBF_GET_CONFIG )  return "GET_CONFIG";
    else                                   return "undefined";
}

////////////////////////////////////
void dev_fbf_init( chdev_t  * fbf )
{
    // set chdev name
    strcpy( fbf->name, "fbf" );

    // call driver init function
    hal_drivers_fbf_init( fbf );

}  // end dev_fbf_init()

//////////////////////////////////////////
void dev_fbf_get_config( uint32_t * width,
                         uint32_t * height,
                         uint32_t * type )
{
    // get extended pointer on FBF chdev descriptor
    xptr_t  dev_xp = chdev_dir.fbf[0];

    assert( (dev_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );

    // get FBF chdev cluster and local pointer
    cxy_t     dev_cxy = GET_CXY( dev_xp );
    chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp );

    // return values
    *width  = hal_remote_l32( XPTR( dev_cxy , &dev_ptr->ext.fbf.width ) );
    *height = hal_remote_l32( XPTR( dev_cxy , &dev_ptr->ext.fbf.height ) );
    *type   = hal_remote_l32( XPTR( dev_cxy , &dev_ptr->ext.fbf.subsampling ) );

}  // end dev_fbf_get_config()

/////////////////////////////////////////////////////
error_t dev_fbf_move_data( uint32_t   cmd_type,
                           void     * user_buffer,
                           uint32_t   length,
                           uint32_t   offset )
{
    // get pointer on calling thread
    thread_t * this = CURRENT_THREAD;

#if DEBUG_DEV_FBF
uint32_t   cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_FBF < cycle )
printk("\n[%s] thread[%x,%x] : %s / buffer %x / length %d / offset %x / cycle %d\n",
__FUNCTION__ , this->process->pid, this->trdid,  
dev_fbf_cmd_str(cmd_type), user_buffer, length, offset, cycle );
#endif

    // get pointers on FBF chdev
    xptr_t      fbf_xp  = chdev_dir.fbf[0];
    cxy_t       fbf_cxy = GET_CXY( fbf_xp );
    chdev_t   * fbf_ptr = GET_PTR( fbf_xp );

// check fbf_xp definition
assert( (fbf_xp != XPTR_NULL) , "undefined FBF chdev descriptor" );

    // get frame buffer width and height
    uint32_t width  = hal_remote_l32 ( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.width ) );
    uint32_t height = hal_remote_l32 ( XPTR( fbf_cxy , &fbf_ptr->ext.fbf.height ) );

// check offset and length versus FBF size
assert( ((offset + length) <= (width * height)) ,
"offset %d / length %d / width %d / height %d\n", offset, length, width, height ); 

    // register command in calling thread descriptor
    this->fbf_cmd.dev_xp    = fbf_xp;
    this->fbf_cmd.type      = cmd_type;
    this->fbf_cmd.buffer    = user_buffer;
    this->fbf_cmd.offset    = offset;
    this->fbf_cmd.length    = length;

    // get driver command function
    dev_cmd_t * cmd = (dev_cmd_t *)hal_remote_lpt( XPTR( fbf_cxy , &fbf_ptr->cmd ) );

    // call driver 
    cmd( XPTR( local_cxy , this ) );

    error_t error = this->fbf_cmd.error;

#if DEBUG_DEV_FBF
cycle = (uint32_t)hal_get_cycles();
if( DEBUG_DEV_FBF < cycle )
printk("\n[%s] thread[%x,%x] completes %s / error = %d / cycle %d\n",
__FUNCTION__ , this->process->pid, this->trdid,  
dev_fbf_cmd_str(cmd_type), error , cycle );
#endif

    // return I/O operation status
    return error;

}  // end dev_fbf_move_data()
