/*
 *
 *    Copyright (c) 2002, Smart Link Ltd.
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without
 *    modification, are permitted provided that the following conditions
 *    are met:
 *
 *        1. Redistributions of source code must retain the above copyright
 *           notice, this list of conditions and the following disclaimer.
 *        2. Redistributions in binary form must reproduce the above
 *           copyright notice, this list of conditions and the following
 *           disclaimer in the documentation and/or other materials provided
 *           with the distribution.
 *        3. Neither the name of the Smart Link Ltd. nor the names of its
 *           contributors may be used to endorse or promote products derived
 *           from this software without specific prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 *
 *    modem.h - modem definitions
 *
 *    Author: sashak@smlink.com
 */

#ifndef __MODEM_H__
#define __MODEM_H__

#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <termios.h>

#include <modem_defs.h>
#include <modem_homolog.h>
#include <modem_dp.h>
#include <modem_timer.h>


/* modem modes */
enum MODEM_MODE {
	MODEM_MODE_DATA = 0,
	MODEM_MODE_FAX  = 1,
	MODEM_MODE_VOICE = 8
};


/* modem status */
#define STATUS_OK         0x0
#define STATUS_NOCARRIER  0x0002 // 3
#define STATUS_ERROR      0x0004 // 4
#define STATUS_NODIALTONE 0x0010 // 6
#define STATUS_BUSY       0x0020 // 7
#define STATUS_NOANSWER   0x0040 // 8
#define STATUS_DP_LINK    0x0100 // 13
#define STATUS_DP_ERROR   0x0200 // 14
#define STATUS_PACK_LINK  0x0400 // 15
#define STATUS_PACK_ERROR 0x0800 // 16
#define STATUS_EC_LINK    0x1000 // 17
#define STATUS_EC_RELEASE 0x2000 // 18
#define STATUS_EC_ERROR   0x4000 // 19


/*
 *    parameters
 *
 */

#define MODEM_FORMAT MFMT_S16_LE
#define MODEM_RATE   9600 /* 8000 */
#define MODEM_FRAG   (MODEM_RATE/200)


#define MODEM_MIN_RATE 300
#define MODEM_MAX_RATE 56000

#define MODEM_DEFAULT_COUNTRY_CODE 0xb5 /* USA */

#define MODEM_CONFIG_CID   1
#define MODEM_CONFIG_VOICE 1
#define MODEM_CONFIG_FAX   1
#define MODEM_CONFIG_FAX_CLASS1 1

#define MODEM_CONFIG_RING_DETECTOR 1

/*
 *    type definitions
 *
 */


/* modem driver struct */
struct modem_driver {
	const char *name;
        int (*start)(struct modem *m);
        int (*stop )(struct modem *m);
	int (*ioctl)(struct modem *m,unsigned cmd, unsigned long arg);
};

/* modem data pumps driver struct */
struct dp_driver {
	const enum DP_ID id;
	const char *name;
	const char *code;
	struct dp_operations *op;
};


/*
 *
 *    compressor definitions
 *
 */

#define COMP_MAX_CODEWORDS 2048  /* dictionary size   */
#define COMP_MAX_STRING    34    /* max string length */

/* encoder and decoder states */
struct comp_state {
	const char *name;
	enum COMP_MODE {TRANSPARENT,COMPRESSED} mode;
	int escape;        /* escape state: decoder only */
	u16 update_at;     /* update dict. at node  */
	u16 next_free;     /* next free dict entry */
	u16 last_matched;  /* last matched dict entry */
	u16 last_added;    /* last added dict entry */
	u16 cw_size;       /* cw size */
	u16 threshold;     /* cw size threshold */
	u8  escape_char;   /* escape char value */
	int max_string;    /* max string length */
	int dict_size;     /* dictionary size */
	struct dict {
		u8  ch;
		u16 parent;
		u16 child;
		u16 next;
	} *dict;           /* dictionary */
	/* fixme: union may be used? */
	int bit_len;
	u32 bit_data;
	int flushed_len;   /* string length already sent */
	int str_len;       /* string length */
	u8  str_data[COMP_MAX_STRING];
	/* test statistics info : encoder only */
	u16 cmp_last; /* compressed bits per last TEST_SLICE chars */
	u32 cmp_bits; /* total compressed bits */
	u32 raw_bits; /* total non-compressed bits */
	/* temporary */
	int  olen;
	int  ohead;
	char obuf[1024];
};


extern int  modem_comp_init(struct modem *m);
extern int  modem_comp_config(struct modem *m, int dict_size, int max_str);
extern void modem_comp_exit(struct modem *m);

extern int  modem_comp_encode(struct modem *m, u8 ch, char *buf, int n);
extern int  modem_comp_decode(struct modem *m, u8 ch, char *buf, int n);
extern int  modem_comp_flush_encoder(struct modem *m, char *buf, int n);
extern int  modem_comp_flush_decoder(struct modem *m, char *buf, int n);

/*
 *
 *    HDLC definitions
 *
 */

typedef struct hdlc_frame {
	struct hdlc_frame *next;
	int count;
	int size;
	//u16 addr;
	//u16 ctrl;
	u16 fcs;
	u8 *buf;
} frame_t;



/*
 *    LAPM definitions
 *
 */

#define LAPM_MAX_WIN_SIZE    15  /* max supported winsize  */
#define LAPM_MAX_INFO_SIZE  128  /* max supported infosize */

#define LAPM_INFO_FRAMES    LAPM_MAX_WIN_SIZE + 1 /* number of info frames */
#define LAPM_CTRL_FRAMES      8  /* number of ctrl frames */

/* lapm state */
struct lapm_state {
        /* modem link */
        struct modem *modem;
        int state;
        /* sub-states: may be optimized */
        int config;
        /* those DATA sub-states may be optimized */
        int reject;
        int busy;
        int peer_busy;
        u8  cmd_addr;
        u8  rsp_addr;
        u8 vs;
        u8 va;
        u8 vr;
        int rtx_count; /* retransmission counter () */
        unsigned int tx_win_size;
        unsigned int rx_win_size;
        unsigned int tx_info_size;
        unsigned int rx_info_size;
        frame_t *sent_info;
        frame_t *tx_info;
        frame_t *info_list;
        frame_t *tx_ctrl;
        frame_t *ctrl_list;
        /* temporary: internal buffs */
        /* rx buf (used when enter busy state) */
        int rx_count;
        int rx_head;
        u8  rx_buf[LAPM_MAX_INFO_SIZE];
        /* temporary: frames bufs */
        struct {
                frame_t frame;
                u8 ctrl[4];
                u8 info[LAPM_MAX_INFO_SIZE];
        } info_buf[LAPM_INFO_FRAMES];
        struct {
                frame_t frame;
                u8 ctrl[4];
                u8 info[LAPM_MAX_INFO_SIZE];
        } ctrl_buf[LAPM_CTRL_FRAMES];
        /* temporary: debug counters: remove it */
        int info_count; // debug
        int tx_count;   // debug
        int sent_count; // debug
        int ctrl_count; // debug
};


/* modem struct */
struct modem {
	const char *name;
	int pty; /* pty descriptor */
	const char *dev_name, *pty_name; /* and names */
	struct termios termios;
	/* device driver */
	struct modem_driver driver;
	void *dev_data;
        /* modem states */
        unsigned modem_info;
        unsigned state;
	unsigned started;
	unsigned command;
	unsigned result_code;
        unsigned hook;
        unsigned caller;
	unsigned rx_rate;
	unsigned tx_rate;
	enum MODEM_MODE mode; /* data,fax,voice */
        /* modem events (ring,escape,waiting for connect,etc) */
	unsigned event;
	unsigned new_event;
        struct timer event_timer;
	/* modem parameters */
	unsigned min_rate;
	unsigned max_rate;
        unsigned char sregs[256];   /* s-registers */
        const struct homolog_set *homolog; /* homologation parameters */
        /* at processor */
        unsigned int at_count;
        char at_line[64];
        char at_cmd[64];
	/* ring detector */
	unsigned int ring_count;
	unsigned long ring_first;
	unsigned long ring_last;
#ifdef MODEM_CONFIG_RING_DETECTOR
	void *rd_obj;
#endif
	/* cid */
#ifdef MODEM_CONFIG_CID
	unsigned cid_requested;
	void *cid;
#endif
        /* dialer */
        char dial_string[32];
        /* escape counter */
        unsigned escape_count;
	unsigned long last_esc_check;
	/* xmit queue */
	struct xmit_queue {
		int count;
		unsigned int head;
		unsigned int tail;
		unsigned int size;
		unsigned char *buf;
	} xmit;
	/* run time configuration */
	struct modem_config {
		/* ec params */
		u8  ec;
		u8  ec_detector;
		u8  ec_tx_win_size;
		u8  ec_rx_win_size;
		u16 ec_tx_info_size;
		u16 ec_rx_info_size;
		/* compressor params */
		u8  comp;
		int comp_dict_size;
		int comp_max_string;
	} cfg;
        /* modem packer */
        int (*get_chars)(struct modem *m, char *buf, int n);
        int (*put_chars)(struct modem *m, const char *buf, int n);
	//int tx_window;
	//int rx_window;
	int bit_timer;
	void (*bit_timer_func)(struct modem *m);
	void (*packer_process)(struct modem *m, int bits);
	struct bits_state {
		int bit;
		u32 data;
	} pack,unpack;
        int (*get_bits)(struct modem *m, int nbits, u8 *buf, int n);
        int (*put_bits)(struct modem *m, int nbits, u8 *buf, int n);
	union packer_union {
		struct async_state {
			struct packer *packer;
			int data_bits;
			int stop_bits;
			int parity;
			int char_size;
		} async;
		struct detect_state {
			int tx_count;
			u8 tx_pattern[2];
			int rx_count;
			u8 rx_pattern[2];
			int ec_enable;
		} detector;
		struct hdlc_state {
			struct hdlc_frame *tx_frame;
			struct hdlc_frame *rx_frame;
			int rx_error;
			int rx_ones,tx_ones;
			/* framer interface */
			struct hdlc_frame *(*get_tx_frame)(void *framer);
			void (*tx_complete)(void *framer, frame_t *fr);
			void (*rx_complete)(void *framer, frame_t *fr);
			void *framer;
			/* temporary: internal bufs */
			struct hdlc_frame _rx_frame;
			u8 _rx_buf[512];
		} hdlc;
	} packer;
	/* error corrector */
	union {
		struct lapm_state lapm;
	} ec;
	/* compressor */
	struct comp_struct {
		// fixme: redo interface
		int head,count;
		char buf[256];
		struct comp_state encoder;
		struct comp_state decoder;
	} comp;
        /* modem data pump interface */
        unsigned srate;
        unsigned format;
        unsigned frag;
        unsigned dp_requested;
        unsigned automode_requested;
	int update_delay;
	void (*process)(struct modem *m,void *in,void *out,int cnt);
        /* process counter and sample timer */
        unsigned long count;
        unsigned long sample_timer;
	void (*sample_timer_func)(struct modem *);
        /* data pump */
        struct dp_operations **dp_ops;
        struct dp *dp;
	void *dp_runtime;
	/* dc evaluator and cleaner */
	void *dcr;
	/* dsp info data */
	struct dsp_info dsp_info;
	/* voice info */
#ifdef MODEM_CONFIG_VOICE
	void *voice_obj;
	struct voice_info voice_info;
#endif
#ifdef MODEM_CONFIG_FAX
	void *fax_obj;
#endif
};



/*
 *    prototypes
 *
 */

/* device driver --> modem */
extern struct modem *modem_create(struct modem_driver *drv, const char *dev_name);
extern void modem_delete(struct modem *m);
extern int modem_write(struct modem *m, const char *buf, int n);
extern void modem_hangup(struct modem *m);
extern void modem_update_termios(struct modem *m, struct termios *tios);
extern void modem_error  (struct modem *m);
extern void modem_ring   (struct modem *m);
extern void modem_event  (struct modem *m);
extern void modem_process(struct modem *m,void *in,void *out,int cnt);

/* packers && EC */ // FIXME: improve interface
extern void modem_async_start(struct modem *m);
extern int  modem_async_get_bits(struct modem *m,int nbits,u8 *buf,int cnt);
extern int  modem_async_put_bits(struct modem *m,int nbits,u8 *buf,int cnt);
extern void modem_detector_start(struct modem *m);
extern int  modem_detector_get_bits(struct modem *m,int nbits,u8 *buf,int cnt);
extern int  modem_detector_put_bits(struct modem *m,int nbits,u8 *buf,int cnt);
extern void modem_hdlc_start(struct modem *m);
extern int  modem_hdlc_get_bits(struct modem *m,int nbits,u8 *buf,int cnt);
extern int  modem_hdlc_put_bits(struct modem *m,int nbits,u8 *buf,int cnt);

extern int  modem_ec_init(struct modem *m);
extern void modem_ec_exit(struct modem *m);
extern void modem_ec_start(struct modem *m);
extern void modem_ec_stop(struct modem *m);

/* status & config */
extern void modem_update_status(struct modem *m, unsigned status);
extern void modem_update_config(struct modem *m, struct modem_config *cfg);

/* modem parameters access macros */
#define CRLF_CHARS(m)     ((char *)((m)->sregs+SREG_CR_CHAR))
#define MODEM_DP(m)       ((m)->sregs[SREG_DP])
#define MODEM_AUTOMODE(m) ((m)->sregs[SREG_AUTOMODE])

#endif /* __MODEM_H__ */

