/* Copyright (C) 2018 RDA Technologies Limited and/or its affiliates("RDA").
 * All rights reserved.
 *
 * This software is supplied "AS IS" without any warranties.
 * RDA assumes no responsibility or liability for the use of the software,
 * conveys no license or title under any patent, copyright, or mask work
 * right to the product. RDA reserves the right to make changes in the
 * software without notification.  RDA also make no representation or
 * warranty that such application will be suitable for the specified use
 * without further testing or modification.
 */

#ifndef _DRV_DEBUG_PORT_H_
#define _DRV_DEBUG_PORT_H_

#include "osi_api.h"
#include "drv_names.h"
#include <sys/queue.h>

OSI_EXTERN_C_BEGIN

/**
 * \brief forward declaration
 */
struct drvDebugPort;

/**
 * \brief debug port operations
 *
 * It is by design that there are no delete API. All debug port instances
 * doesn't support delete.
 */
typedef struct
{
    /** refer to \p drvDebugPortSendPacket */
    bool (*send_packet)(struct drvDebugPort *d, const void *data, unsigned size);
    /** refer to \p drvDebugPortRead */
    int (*read)(struct drvDebugPort *d, void *data, unsigned size);
    /** refer to \p drvDebugPortSetRxCallback  */
    void (*set_rx_callback)(struct drvDebugPort *d, osiCallback_t cb, void *param);
    /** refer to \p drvDebugPortSetTraceEnable */
    void (*set_trace_enable)(struct drvDebugPort *d, bool enable);
    /** refer to \p drvDebugPortWorkEnable */
    void (*work_enable)(struct drvDebugPort *d, bool enable);
} drvDebugPortOps_t;

/**
 * \brief debug port protocol
 */
typedef enum
{
    DRV_DEBUG_PROTOCOL_HOST = 1,         ///< host protocol, trace and command
    DRV_DEBUG_PROTOCOL_DIAG,             ///< diag protocol, trace and command
    DRV_DEBUG_PROTOCOL_MODEM_LOG = 0x10, ///< modem log
    DRV_DEBUG_PROTOCOL_MOS_LOG,          ///< mos log
} drvDebugPortProtocol_t;

/**
 * \brief debug port working mode
 */
typedef union {
    struct
    {
        uint8_t protocol;      ///< refer to \p drvDebugPortProtocol_t
        bool trace_enable : 1; ///< whether trace is enabled
        bool cmd_enable : 1;   ///< whether command is enabled
        bool bs_enable : 1;    ///< whether to work under blue screen
        bool bs_only : 1;      ///< whether to work under blue screen only
    };
    uint32_t mode; ///< combined mode word
} drvDebugPortMode_t;

/**
 * \brief debug port status
 */
typedef union {
    struct
    {
        bool usb_enabled : 1;     ///< for usb debug port only, whether is enabled
        bool usb_host_opened : 1; ///< for usb debug port only, whether host has opened
    };
    uint32_t status; ///< combined status word
} drvDebugPortStatus_t;

/**
 * \brief debug port base data struct
 *
 * This is base of debug port. That means the operation pointer must be
 * the first member, and private data can follow.
 */
typedef struct drvDebugPort
{
    /** operations */
    const drvDebugPortOps_t *ops;
    /** debug port device name */
    unsigned name;
    /** baud rate for uart */
    unsigned baud;
    /** debug port mode */
    drvDebugPortMode_t mode;
    /** debug port status */
    drvDebugPortStatus_t status;
    /** iterator for the global debug port list */
    /**/ SLIST_ENTRY(drvDebugPort) iter;
} drvDebugPort_t;

/**
 * \brief send packet through debug port
 *
 * The packet can be host protocol or diag protocol, depends on the debug
 * mode at creation.
 *
 * Usually, it is called by the host command processing or diag command
 * processing.
 *
 * When host protocol is used, the packet should be a complete host packet.
 * \p data should be 4 bytes aligned, and there are no alignment requirement
 * for \p size.
 *
 * When diag protocol is used, the packet should be a complete hdlc encoded
 * packet, with leading and trailing sync byte. \p data should be 4 bytes
 * aligned, and there are no erquirement for \p size.
 *
 * When trace is enabled in the debug port, the packet will be mixed with
 * trace data.
 *
 * \param d debug port instance, must be valid
 * \param data packet data
 * \param size packet size
 * \return
 *      - true on success
 *      - false on error, invalid parameters or send timeout
 */
bool drvDebugPortSendPacket(drvDebugPort_t *d, const void *data, unsigned size);

/**
 * \brief set rx callback
 *
 * \p cb will be called when there are rx data arrived. Most likely, it
 * will be called in ISR. So, the recommended callback is to enqueue a
 * work for the real processing.
 *
 * \param d debug port instance, must be valid
 * \param cb rx arrive callback
 * \param param callback context
 */
void drvDebugPortSetRxCallback(drvDebugPort_t *d, osiCallback_t cb, void *param);

/**
 * \brief set trace enable
 *
 * It may be called for the registered debug port at \p osiTraceSetEnable.
 *
 * \param d debug port instance, must be valid
 * \param enable trace enable
 */
void drvDebugPortSetTraceEnable(drvDebugPort_t *d, bool enable);

/**
 * \brief work enable
 *
 * It may be called for the registered debugHost port at \p osiTraceSetPushEnable.
 *
 * \param d debug port instance, must be valid
 * \param enable debughost to tx log from ifc to fifo output
 */
void drvDebugPortWorkEnable(drvDebugPort_t *d, bool enable);

/**
 * \brief read from debug port
 *
 * \p cb will be called when there are rx data arrived. Most likely, it
 * will be called in ISR. So, the recommended callback is to enqueue a
 * work for the real processing.
 *
 * \param d debug port instance, must be valid
 * \param data memory for read
 * \param size maximum read size
 * \return
 *      - read size, 0 for no rx data available
 *      - -1 on error
 */
int drvDebugPortRead(drvDebugPort_t *d, void *data, unsigned size);

/**
 * \brief get debug port device name
 *
 * \param d debug port instance, must be valid
 * \return device name
 */
static inline unsigned drvDebugPortGetName(drvDebugPort_t *d) { return d->name; }

/**
 * \brief get debug port mode
 *
 * \param d debug port instance, must be valid
 * \return debug port mode
 */
static inline drvDebugPortMode_t drvDebugPortGetMode(drvDebugPort_t *d) { return d->mode; }

/**
 * \brief whether the debug port is on usb serial
 *
 * \param d debug port instance, must be valid
 * \return true if debug port on usb
 */
static inline bool drvDebugPortIsUsb(drvDebugPort_t *d) { return DRV_NAME_IS_USRL(d->name); }

/**
 * \brief whether trace is enabled on the debug port
 *
 * \param d debug port instance, must be valid
 * \return true if trace is enabled
 */
static inline bool drvDebugPortIsTraceEnabled(drvDebugPort_t *d) { return d->mode.trace_enable; }

/**
 * \brief whether the usb serial debug port is opened by host
 *
 * \param d debug port instance, must be valid
 * \return true if opened by host
 */
static inline bool drvDebugPortIsUsbHostOpened(drvDebugPort_t *d) { return d->status.usb_host_opened; }

/**
 * \brief register the debug port to global list
 *
 * \param d debug port instance, must be valid
 */
void drvDebugPortRegister(drvDebugPort_t *d);

/**
 * \brief find debug port by deivce name
 *
 * \param name device name
 * \return
 *      - debug port instance
 *      - NULL if there are no debug port on the device
 */
drvDebugPort_t *drvDebugPortFindByName(uint32_t name);

/**
 * \brief create debug port on debughost
 *
 * Usually, there is only one debughost in system. So, it is not needed
 * to specify the device name. Also, the configuration of debughost is
 * fixed.
 *
 * \param mode debugging mode
 * \return
 *      - debughost instance
 */
drvDebugPort_t *drvDhostCreate(drvDebugPortMode_t mode);

/**
 * \brief create debug port on uart
 *
 * \param name uart device name
 * \param mode debugging mode
 * \param baud baud rate
 * \return
 *      - debug uart instance
 */
drvDebugPort_t *drvDebugUartPortCreate(unsigned name, drvDebugPortMode_t mode, unsigned baud);

/**
 * \brief create debug port on usb serial
 *
 * \param name usb serial device name
 * \param mode debugging mode
 * \return
 *      - debug uart instance
 */
drvDebugPort_t *drvDebugUserialPortCreate(unsigned name, drvDebugPortMode_t mode);

/**
 * \brief restore debughost configuration
 *
 * It is for debug purpose only.
 */
void drvDhostRestoreConfig(void);

OSI_EXTERN_C_END
#endif
