/*
 * 86Box    A hypervisor and IBM PC system emulator that specializes in
 *          running old operating systems and software designed for IBM
 *          PC systems and compatibles from 1981 through fairly recent
 *          system designs based on the PCI bus.
 *
 *          This file is part of the 86Box distribution.
 *
 *          Definitions for character devices.
 *
 *
 *
 * Authors: RichardG, <richardg867@gmail.com>
 *
 *          Copyright 2025-2026 RichardG.
 */
#ifndef EMU_CHAR_H
#define EMU_CHAR_H

#ifdef _WIN32
#    define CHAR_FD_VALID(fd) ((fd) && ((fd) != INVALID_HANDLE_VALUE))
#else
#    define CHAR_FD_VALID(fd) ((fd) >= 0)
#endif
#define CHAR_RECONNECT_MS 500

enum { /* port hotunpluggability tracking */
       CHAR_PORT_DETACHED = 0,
       CHAR_PORT_HOTUNPLUG,
       CHAR_PORT_NOHOTUNPLUG
};

enum { /* device flags */
       CHAR_LPT_USESTROBE = 0x1, /* only issue SPP write when STROBE is asserted */
       CHAR_LPT_NIBBLE    = 0x2, /* use nibble/4-bit/LapLink system for bidirectional communication over SPP */
       CHAR_LPT_PTI       = 0x4  /* emulate Parallel Technologies DirectParallel FAST (Windows DCC) active cable */
};

enum { /* port types */
       CHAR_PORT_COM = 0x1,
       CHAR_PORT_LPT = 0x2
};

enum { /* serial parity types */
       CHAR_COM_PARITY_ODD   = 0x1,
       CHAR_COM_PARITY_EVEN  = 0x3,
       CHAR_COM_PARITY_MARK  = 0x5,
       CHAR_COM_PARITY_SPACE = 0x7
};

enum { /* device control */
       /* serial */
       CHAR_COM_DTR = 0x1,
       CHAR_COM_RTS = 0x2,
       CHAR_COM_BREAK = 0x40,

       /* parallel */
       CHAR_LPT_STROBE   = 0x100,
       CHAR_LPT_AUTOFEED = 0x200,
       CHAR_LPT_RESET    = 0x400,
       CHAR_LPT_PSELECT  = 0x800,
       CHAR_LPT_EPP_ADDR = 0x40000000, /* if set before read/write: data is EPP address */
       CHAR_LPT_EPP_DATA = 0x80000000, /* if set before read/write: data is EPP data */
       CHAR_LPT_EPP      = CHAR_LPT_EPP_ADDR | CHAR_LPT_EPP_DATA
};

#define CHAR_RAW_CONTROL(x) ((x) ^ (CHAR_LPT_STROBE | CHAR_LPT_AUTOFEED | CHAR_LPT_PSELECT))

enum { /* device status */
       /* serial */
       CHAR_COM_CTS = 0x10,
       CHAR_COM_DSR = 0x20,
       CHAR_COM_RI  = 0x40,
       CHAR_COM_DCD = 0x80,

       /* parallel */
       CHAR_LPT_ERROR    = 0x800,
       CHAR_LPT_SELECT   = 0x1000,
       CHAR_LPT_PAPEROUT = 0x2000,
       CHAR_LPT_ACK      = 0x4000,
       CHAR_LPT_BUSY     = 0x8000,

       /* global status */
       CHAR_RX_DISCONNECTED = 0x40000000,
       CHAR_TX_DISCONNECTED = 0x80000000,
       CHAR_DISCONNECTED    = CHAR_RX_DISCONNECTED | CHAR_TX_DISCONNECTED
};

#define CHAR_RAW_STATUS(x) ((x) ^ CHAR_LPT_BUSY)

enum { /* char_pipe modes (for config migration) */
       CHAR_PIPE_MODE_AUTO = 0,
       CHAR_PIPE_MODE_SERVER,
       CHAR_PIPE_MODE_CLIENT,
       CHAR_PIPE_MODE_MAX
};

enum { /* char_stdio modes (for config migration) */
       CHAR_STDIO_MODE_STDIO = 0,
       CHAR_STDIO_MODE_PTY,
       CHAR_STDIO_MODE_TERM,
       CHAR_STDIO_MODE_CMD,
       CHAR_STDIO_MODE_MAX
};

typedef struct _char_device_ {
    uint32_t flags;
    void    *priv;

    size_t (*read)(uint8_t *buf, size_t len, void *priv);
    size_t (*write)(uint8_t *buf, size_t len, void *priv);
    uint32_t (*status)(void *priv);
    void (*control)(uint32_t flags, void *priv);
    void (*port_config)(void *priv);
} char_device_t;

typedef struct {
    char_device_t chardev;
    char          name[32];

    void *priv;
    void (*update_status)(void *priv);

    uint8_t attached;
    uint8_t type;
    union {
        struct {
            uint32_t baud;
            uint8_t  data_bits;
            uint8_t  parity;
            uint8_t  stop_bits;
        } com;
        struct {
            uint8_t dummy;
        } lpt;
    };
} char_port_t;

extern const int       char_get_from_internal_name(const char *internal_name, int bus);
extern const device_t *char_get_device(const int id);

extern void        *char_init(char_port_t *port, const device_t *device, int instance);
extern char_port_t *char_attach(uint32_t flags,
                                size_t (*read)(uint8_t *buf, size_t len, void *priv),
                                size_t (*write)(uint8_t *buf, size_t len, void *priv),
                                uint32_t (*status)(void *priv),
                                void (*control)(uint32_t flags, void *priv),
                                void (*port_config)(void *priv),
                                void *priv);
extern void         char_update_status(char_port_t *port);
extern void        *char_log_open(char_port_t *port, char *dev_name);

extern const device_t char_serial_passthrough_com_device;
extern const device_t char_pipe_com_device;
extern const device_t char_pipe_lpt_device;
extern const device_t char_file_com_device;
extern const device_t char_file_lpt_device;
extern const device_t char_stdio_com_device;
extern const device_t char_loopback_com_device;
extern const device_t char_loopback_lpt_device;

#endif /*EMU_CHAR_H*/
