/*
* @file hv_drv_UsbPortingOsDisk.h
* @brief Header file of usb adapting to os about disk.
*
* @author HiView SoC Software Team
* @version 1.0.0
* @date 2022-06-15
*/

#ifndef __HV_DRV_USB_PORTING_OS_DISK_H
#define __HV_DRV_USB_PORTING_OS_DISK_H

#include "hv_drv_UsbPortingOsTypes.h"
#include "hv_drv_UsbPortingOsDevtab.h"
#include "hv_drv_UsbPortingOsDiskIo.h"

// ---------------------------------------------------------------------------

typedef struct os_disk_partition_t os_disk_partition_t;
typedef struct os_disk_info_t os_disk_info_t;
typedef struct os_disk_identify_t os_disk_identify_t;

typedef struct disk_channel    disk_channel;
typedef struct disk_controller disk_controller;
typedef struct disk_funs       disk_funs;

// ---------------------------------------------------------------------------

// Pointers into upper-level driver
typedef struct {

    // Initialize the disk
    boolean (*disk_init)(struct os_devtab_entry *tab);

    // Disk device has been connected
    os_errno (*disk_connected)(struct os_devtab_entry *tab,
                               os_disk_identify_t     *ident);

    // Disk device has been disconnected
    os_errno (*disk_disconnected)(struct disk_channel *chan);

    // Lookup disk device
    os_errno (*disk_lookup)(struct os_devtab_entry **tab,
                            struct os_devtab_entry  *sub_tab,
                            const char               *name);

    // Asynchronous block transfer done
    void (*disk_transfer_done)(struct disk_channel *chan, os_errno res);

} disk_callbacks_t;

#define DISK_CALLBACKS(_l,                      \
                       _init,                   \
                       _connected,              \
                       _disconnected,           \
                       _lookup,                 \
                       _transfer_done)          \
disk_callbacks_t _l = {                         \
    _init,                                      \
    _connected,                                 \
    _disconnected,                              \
    _lookup,                                    \
    _transfer_done                              \
};

extern disk_callbacks_t os_io_disk_callbacks;

// ---------------------------------------------------------------------------
// Private data which describes a disk controller

struct disk_controller {
    void               *priv;          // Private data
    volatile os_errno  result;         // Last operation result
    boolean            init;           // Initialized?
};


#define DISK_CONTROLLER(_l, _priv)             \
    static disk_controller _l = { &_priv, 0, false};

// ---------------------------------------------------------------------------
// Private data which describes this channel

struct disk_channel {
    disk_funs               *funs;
    disk_callbacks_t        *callbacks;
    void                    *dev_priv;    // device private data
    disk_controller         *controller;  // pointer to controller
    os_disk_info_t          *info;        // disk info
    os_disk_partition_t     *partition;   // partition data
    struct os_devtab_entry  *pdevs_dev;   // partition devs devtab ents
    disk_channel            *pdevs_chan;  // partition devs disk chans
    boolean                 mbr_support;  // true if disk has MBR
    boolean                 valid;        // true if device valid
    boolean                 init;         // true if initialized
    os_ucount16             mounts;       // count of number of mounts
    u32 byte_per_sector;
};

// Initialization macro for disk channel
#define DISK_CHANNEL(_l,                                              \
                     _funs,                                           \
                     _dev_priv,                                       \
                     _controller,                                     \
                     _mbr_supp,                                       \
                     _max_part_num)                                   \
static struct os_devtab_entry _l##_part_dev[_max_part_num];           \
static disk_channel            _l##_part_chan[_max_part_num];         \
static os_disk_partition_t    _l##_part_tab[_max_part_num];           \
static os_disk_info_t         _l##_disk_info = {                      \
    _l##_part_tab,                                                    \
    _max_part_num                                                     \
};                                                                    \
disk_channel _l = {                                                   \
    &(_funs),                                                         \
    NULL,                                                             \
    &(_dev_priv),                                                     \
    &(_controller),                                                   \
    &(_l##_disk_info),                                                \
    NULL,                                                             \
    _l##_part_dev,                                                    \
    _l##_part_chan,                                                   \
    _mbr_supp,                                                        \
    false,                                                            \
    false,                                                            \
    0,                                                                \
    2048                                                              \
};

// Initialization macro for disk channel allocated elsewhere.
#define DISK_CHANNEL_INIT(_dc,                           \
                          _funs,                         \
                          _dev_priv,                     \
                          _controller,                   \
                          _disk_info,                    \
                          _part_dev,                     \
                          _part_chan,                    \
                          _part_tab,                     \
                          _mbr_supp,                     \
                          _max_part_num)                 \
    OS_MACRO_START                                       \
    HV_MEMSET((_disk_info), 0, sizeof(os_disk_info_t));     \
    (_dc).funs = &(_funs);                               \
    (_dc).callbacks = NULL;                              \
    (_dc).dev_priv = (_dev_priv);                        \
    (_dc).controller = &(_controller);                   \
    (_dc).info = &(_disk_info);                          \
    (_dc).info->partitions = (_part_tab);                \
    (_dc).pdevs_dev = (_part_dev);                       \
    (_dc).pdevs_chan = (_part_chan);                     \
    (_dc).partition = NULL;                              \
    (_dc).mbr_support = (_mbr_supp);                     \
    (_dc).valid = false;                                 \
    (_dc).init = false;                                  \
    (_dc).mounts = 0;                                    \
    (_dc).info->partitions_num = (_max_part_num);        \
    OS_MACRO_END

// ---------------------------------------------------------------------------
// Low level interface functions

struct disk_funs {

    // Read block data into buf
    os_errno (*read)(disk_channel *priv,
                     void         *buf,
                     u32    len,
                     u32    block_num);

    // Write block data from buf
    os_errno (*write)(disk_channel *priv,
                      const void   *buf,
                      u32    len,
                      u32    block_num);

    // Get hardware configuration
    os_errno (*get_config)(disk_channel *priv,
                           u32    key,
                           const void   *xbuf,
                           u32   *len);

    // Set hardware configuration
    os_errno (*set_config)(disk_channel *priv,
                           u32    key,
                           const void   *xbuf,
                           u32   *len);
};

#define DISK_FUNS(_l,_read,_write,_get_config,_set_config)           \
static disk_funs _l = {                                              \
  _read,                                                             \
  _write,                                                            \
  _get_config,                                                       \
  _set_config                                                        \
};

struct disk_channel* get_scsi_disk_channel(void);

#endif 
