/*
 * dev_ioc.h - IOC (Block Device Controler) generic device API definition.
 * 
 * Author  Alain Greiner    (2016,2017,2018,2019)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH
 *
 * 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
 */

#ifndef _DEV_IOC_H
#define _DEV_IOC_H

#include <kernel_config.h>
#include <hal_kernel_types.h>

/****  Forward declarations  ****/

struct chdev_s;

/*****************************************************************************************
 *     Generic Block Device Controler definition
 *
 * This device provide access to an external mass storage peripheral such as a
 * magnetic hard disk or a SD card, that can store blocks of data in a linear array
 * of sectors indexed by a simple lba (logic block address).
 *
 * It supports four command types:
 * - READ       : move blocks from device to memory, with a descheduling policy.
 * - WRITE      : move blocks from memory to device, with a descheduling policy.
 * - SYNC_READ  : move blocks from device to memory, with a busy waiting policy.
 * - SYNC_WRITE : move blocks from memory to device, with a busy waiting policy.
 *
 * For the he READ or WRITE operations, the client thread is descheduled, and the work
 * is done by the server thread associated to the IOC device:
 * The client thread calls the dev_ioc_move_data() kernel functions that (i) registers
 * the command in the client thread descriptor, (ii) registers the client thread 
 * in the IOC device waiting queue, and (iii) blocks on the THREAD_BLOCKED_IO condition
 * and deschedules.
 * The server thread attached to the IOC device descriptor handles the commands
 * registered in the waiting queue, calling the IOC driver function.
 * Most IOC device implementations have a DMA capability, but some implementations,
 * such as the RDK (Ram Disk) implementation does not use DMA.
 * When the server thread completes an I/O operation, it reactivates the client thread.
 *
 * The SYNC_READ and SYNC_WRITE operations are used by the kernel in the initialisation
 * phase, to update the FAT (both the FAT mapper and the FAT on IOC device), or to update
 * a directory on IOC device when a new file is created. 
 * These synchronous operations do not not use the IOC device waiting queue, 
 * the server thread, and the IOC IRQ. The client thread does not deschedules:
 * it registers the command in the thread descriptor, calls directly the IOC driver,
 * and uses a busy-waiting policy to poll the IOC device status.
 *****************************************************************************************/

/******************************************************************************************
 * This defines the (implementation independant) extension for the generic IOC device.
 *****************************************************************************************/

typedef struct ioc_extend_s
{
    uint32_t    size;      /*! number of bytes in a block                               */
    uint32_t    count;     /*! total number of blocks in physical device                */
}
ioc_extend_t;

/******************************************************************************************
 * This enum defines the various implementations of the generic IOC peripheral.
 * It must be kept consistent with the define in arch_info.h file.
 *****************************************************************************************/

typedef enum
{
    IMPL_IOC_BDV =   0,     
    IMPL_IOC_HBA =   1,  
    IMPL_IOC_SDC =   2,
    IMPL_IOC_SPI =   3,
    IMPL_IOC_RDK =   4,
}
ioc_impl_t;

/******************************************************************************************
 * This defines the (implementation independant)  command passed to the driver.
 *****************************************************************************************/

typedef enum
{
    IOC_READ       = 0,
    IOC_WRITE      = 1,
    IOC_SYNC_READ  = 2,
    IOC_SYNC_WRITE = 3,
}
cmd_type_t;

typedef struct ioc_command_s
{
    xptr_t      dev_xp;     /*! extended pointer on IOC device descriptor                */
    uint32_t    type;       /*! command type above                                       */
    uint32_t    lba;        /*! first block index                                        */
    uint32_t    count;      /*! number of blocks                                         */
    xptr_t      buf_xp;     /*! extended pointer on memory buffer                        */
    uint32_t    error;      /*! operation status (0 if success)                          */
}
ioc_command_t;

/******************************************************************************************
 * This function returns a printable string for a IOC command type.
 ******************************************************************************************
 * @ cmd  : command type.
 * @ return pointer on string.
 *****************************************************************************************/
char * dev_ioc_cmd_str( cmd_type_t cmd );

/******************************************************************************************
 * This function completes the IOC chdev descriptor initialisation,
 * namely the link with the implementation specific driver.
 * The func, impl, channel, is_rx, base fields have been previously initialised.
 * It calls the specific driver initialisation function, to initialise the hardware
 * device and the specific data structures when required.
 * It creates the associated server thread and allocates a WTI from local ICU.
 * It must de executed by a local thread.
 ******************************************************************************************
 * @ chdev     : local pointer on IOC chdev descriptor.
 *****************************************************************************************/
void dev_ioc_init( struct chdev_s * chdev );

/******************************************************************************************
 * This blocking function moves <count> contiguous blocks of data between the block device
 * starting from block defined by the <lba> argument and a kernel memory buffer, defined
 * by the <buffer_xp> argument. The transfer direction and mode are defined by the 
 * <cmd_type> argument. The request is always registered in the calling thread descriptor.
 * - In synchronous mode, the calling thread is not descheduled, and directly calls the
 *   IOC driver, polling the IOC status to detect transfer completion.
 * - In asynchronous mode, the calling thread blocks and deschedules, and the IOC driver
 *   is called by the server thread associated to the IOC device.
 ******************************************************************************************
 * @ cmd_type  : IOC_READ / IOC_WRITE / IOC_SYNC_READ / IOC_SYN_WRITE.
 * @ buffer_xp : extended pointer on kernel buffer in memory (must be block aligned).
 * @ lba       : first block index on device.
 * @ count     : number of blocks to transfer.
 * @ returns 0 if success / returns -1 if error.
 *****************************************************************************************/
error_t dev_ioc_move_data( uint32_t  cmd_type,
                           xptr_t    buffer_xp,
                           uint32_t  lba,
                           uint32_t  count );

#endif	/* _DEV_IOC_H */
