return "Poor signal quality";
case SIG_STATUS_MODEM_RETRAIN_OCCURRED:
return "Modem retrain occurred";
+ case SIG_STATUS_LINK_CONNECTED:
+ return "Link connected";
+ case SIG_STATUS_LINK_DISCONNECTED:
+ return "Link disconnected";
+ case SIG_STATUS_LINK_ERROR:
+ return "Link error";
}
return "???";
}
/* This is a simple set of direct digital synthesis (DDS) functions to generate sine
waves. This version uses a 128 entry sin/cos table to cover one quadrant. */
-static const int16_t sine_table[DDS_STEPS] =
+static const int16_t sine_table[DDS_STEPS + 1] =
{
201,
603,
32753,
32762,
32767,
+ 32767
};
SPAN_DECLARE(int32_t) dds_phase_rate(float frequency)
phase >>= DDS_SHIFT;
step = phase & (DDS_STEPS - 1);
if ((phase & DDS_STEPS))
- step = (DDS_STEPS - 1) - step;
+ step = DDS_STEPS - step;
amp = sine_table[step];
if ((phase & (2*DDS_STEPS)))
amp = -amp;
{
int16_t amp;
- amp = (int16_t) (((int32_t) dds_lookup(*phase_acc + phase)*(int32_t) scale) >> 15);
+ amp = (int16_t) (((int32_t) dds_lookup(*phase_acc + phase)*scale) >> 15);
*phase_acc += phase_rate;
return amp;
}
{
complexi_t amp;
- amp = complex_seti(((int32_t) dds_lookup(*phase_acc + phase + (1 << 30))*(int32_t) scale) >> 15,
- ((int32_t) dds_lookup(*phase_acc + phase)*(int32_t) scale) >> 15);
+ amp = complex_seti(((int32_t) dds_lookup(*phase_acc + phase + (1 << 30))*scale) >> 15,
+ ((int32_t) dds_lookup(*phase_acc + phase)*scale) >> 15);
*phase_acc += phase_rate;
return amp;
}
{
complexi16_t amp;
- amp = complex_seti16((int16_t) (((int32_t) dds_lookup(*phase_acc + phase + (1 << 30))*(int32_t) scale) >> 15),
- (int16_t) (((int32_t) dds_lookup(*phase_acc + phase)*(int32_t) scale) >> 15));
+ amp = complex_seti16((int16_t) (((int32_t) dds_lookup(*phase_acc + phase + (1 << 30))*scale) >> 15),
+ (int16_t) (((int32_t) dds_lookup(*phase_acc + phase)*scale) >> 15));
*phase_acc += phase_rate;
return amp;
}
{
complexi32_t amp;
- amp = complex_seti32(((int32_t) dds_lookup(*phase_acc + phase + (1 << 30))*(int32_t) scale) >> 15,
- ((int32_t) dds_lookup(*phase_acc + phase)*(int32_t) scale) >> 15);
+ amp = complex_seti32(((int32_t) dds_lookup(*phase_acc + phase + (1 << 30))*scale) >> 15,
+ ((int32_t) dds_lookup(*phase_acc + phase)*scale) >> 15);
*phase_acc += phase_rate;
return amp;
}
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) fsk_tx_set_modem_status_handler(fsk_tx_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) fsk_tx_set_modem_status_handler(fsk_tx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) fsk_rx_set_modem_status_handler(fsk_rx_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) fsk_rx_set_modem_status_handler(fsk_rx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
/* 4.2.1 Downscaling of the input signal */
SO = (amp[k] >> 1) & ~3;
- assert(SO >= -0x4000); // downscaled by
- assert(SO <= 0x3FFC); // previous routine.
+ /* This is supposed to have been downscaled by previous routine. */
+ assert(SO >= -0x4000);
+ assert(SO <= 0x3FFC);
/* 4.2.2 Offset compensation */
/* This part implements a high-pass filter and requires extended
arithmetic precision for the recursive part of this filter.
The input of this procedure is the array so[0...159] and the
- output the array sof[ 0...159 ].
+ output the array sof[0...159].
*/
/* Compute the non-recursive part */
s1 = SO - z1;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) hdlc_rx_set_status_handler(hdlc_rx_state_t *s, modem_rx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) hdlc_rx_set_status_handler(hdlc_rx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
int input_length;
double c1;
double c2;
- double int_part;
int x;
#if defined(SPANDSP_USE_FIXED_POINT)
int frac_row;
int frac_col;
#else
+ double int_part;
double frac_row;
double frac_col;
#endif
if (n == 0 || x == 1)
return 1;
if (x != -1)
- return (x == 0) ? 1/x : 0;
+ return (x != 0) ? 1/x : 0;
n = -n;
}
u = n;
float old_weight;
float new_weight;
float gain;
- //int16_t *orig_amp;
int orig_len;
- //orig_amp = amp;
orig_len = len;
if (s->missing_samples == 0)
{
if (s->reading <= 0)
return -96.329f + DBM0_MAX_POWER;
/* This is based on A-law, but u-law is only 0.03dB different, so don't worry. */
- return log10f((float) s->reading/(32767.0f*32767.0f))*10.0f + DBM0_MAX_POWER;
+ return 10.0f*log10f((float) s->reading/(32767.0f*32767.0f) + 1.0e-10f) + DBM0_MAX_POWER;
}
/*- End of function --------------------------------------------------------*/
{
if (s->reading <= 0)
return -96.329f;
- return log10f((float) s->reading/(32767.0f*32767.0f))*10.0f;
+ return 10.0f*log10f((float) s->reading/(32767.0f*32767.0f) + 1.0e-10f);
}
/*- End of function --------------------------------------------------------*/
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) silence_gen_status_handler(silence_gen_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) silence_gen_status_handler(silence_gen_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
float abs_y;
float angle;
- if (x == 0.0f || y == 0.0f)
- return 0;
+ if (y == 0.0f)
+ {
+ if (x < 0.0f)
+ return 0x80000000;
+ return 0x00000000;
+ }
+ if (x == 0.0f)
+ {
+ if (y < 0.0f)
+ return 0xc0000000;
+ return 0x40000000;
+ }
abs_y = fabsf(y);
float fx;
float fy;
- if (x == 0.0f || y == 0.0f)
- return 0;
+ if (y == 0.0f)
+ {
+ if (x < 0.0f)
+ return 3.1415926f;
+ return 0.0f;
+ }
+ if (x == 0.0f)
+ {
+ if (y < 0.0f)
+ return 3.1415926f*1.5f;
+ return 3.1415926f*0.5f;
+ }
fx = fabsf(x);
fy = fabsf(y);
/* Deal with the octants */
/*! \brief Notification that a modem has detected signal quality degradation. */
SIG_STATUS_POOR_SIGNAL_QUALITY = -12,
/*! \brief Notification that a modem retrain has occurred. */
- SIG_STATUS_MODEM_RETRAIN_OCCURRED = -13
+ SIG_STATUS_MODEM_RETRAIN_OCCURRED = -13,
+ /*! \brief The link protocol (e.g. V.42) has connected. */
+ SIG_STATUS_LINK_CONNECTED = -14,
+ /*! \brief The link protocol (e.g. V.42) has disconnected. */
+ SIG_STATUS_LINK_DISCONNECTED = -15,
+ /*! \brief An error has occurred in the link protocol (e.g. V.42). */
+ SIG_STATUS_LINK_ERROR = -16
};
/*! Message put function for data pumps */
/*! Bit get function for data pumps */
typedef int (*get_bit_func_t)(void *user_data);
-/*! Completion callback function for tx data pumps */
-typedef void (*modem_tx_status_func_t)(void *user_data, int status);
-
-/*! Completion callback function for rx data pumps */
-typedef void (*modem_rx_status_func_t)(void *user_data, int status);
+/*! Status change callback function for data pumps */
+typedef void (*modem_status_func_t)(void *user_data, int status);
enum
{
SPAN_DECLARE(uint64_t) bit_reverse_8bytes(uint64_t data);
#endif
-/*! \brief Bit reverse each bytes in a buffer.
+/*! \brief Bit reverse each byte in a buffer.
\param to The buffer to place the reversed data in.
\param from The buffer containing the data to be reversed.
\param len The length of the data in the buffer. */
}
/*- End of function --------------------------------------------------------*/
+static __inline__ int32_t poweri16(const complexi16_t *x)
+{
+ return (int32_t) x->re*x->re + (int32_t) x->im*x->im;
+}
+/*- End of function --------------------------------------------------------*/
+
static __inline__ float powerf(const complexf_t *x)
{
return x->re*x->re + x->im*x->im;
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) fsk_tx_set_modem_status_handler(fsk_tx_state_t *s, modem_tx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) fsk_tx_set_modem_status_handler(fsk_tx_state_t *s, modem_status_func_t handler, void *user_data);
/*! Generate a block of FSK modem audio samples.
\brief Generate a block of FSK modem audio samples.
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) fsk_rx_set_modem_status_handler(fsk_rx_state_t *s, modem_rx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) fsk_rx_set_modem_status_handler(fsk_rx_state_t *s, modem_status_func_t handler, void *user_data);
#if defined(__cplusplus)
}
\param handler The callback routine used to report status changes.
\param user_data An opaque parameter for the callback routine.
*/
-SPAN_DECLARE(void) hdlc_rx_set_status_handler(hdlc_rx_state_t *s, modem_rx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) hdlc_rx_set_status_handler(hdlc_rx_state_t *s, modem_status_func_t handler, void *user_data);
/*! Release an HDLC receiver context.
\brief Release an HDLC receiver context.
void *get_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_tx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
void *put_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_tx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
/*! \brief An opaque parameter passed to the frame callback routine. */
void *frame_user_data;
/*! \brief The callback routine called to report status changes. */
- modem_rx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief An opaque parameter passed to the status callback routine. */
void *status_user_data;
/*! \brief TRUE if bad frames are to be reported. */
struct silence_gen_state_s
{
/*! \brief The callback function used to report status changes. */
- modem_tx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
void *put_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_rx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
void *get_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_tx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
/*! \brief A user specified opaque pointer passed to the put_bit callback routine. */
void *put_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_rx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
void *put_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_rx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
void *get_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_tx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
void *put_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_rx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
void *get_bit_user_data;
/*! \brief The callback function used to report modem status changes. */
- modem_tx_status_func_t status_handler;
+ modem_status_func_t status_handler;
/*! \brief A user specified opaque pointer passed to the status function. */
void *status_user_data;
int rrc_filter_step;
/*! \brief The register for the data scrambler. */
- unsigned int scramble_reg;
+ uint32_t scramble_reg;
/*! \brief The register for the training scrambler. */
uint8_t training_scramble_reg;
/*! \brief TRUE if transmitting the training sequence, or shutting down transmission.
*
* Written by Steve Underwood <steveu@coppice.org>
*
- * Copyright (C) 2003 Steve Underwood
+ * Copyright (C) 2003, 2011 Steve Underwood
*
* All rights reserved.
*
#if !defined(_SPANDSP_PRIVATE_V42_H_)
#define _SPANDSP_PRIVATE_V42_H_
+/*! Max retries (9.2.2) */
+#define V42_DEFAULT_N_400 5
+/*! Default for max octets in an information field (9.2.3) */
+#define V42_DEFAULT_N_401 128
+/*! Maximum supported value for max octets in an information field */
+#define V42_MAX_N_401 128
+/*! Default window size (k) (9.2.4) */
+#define V42_DEFAULT_WINDOW_SIZE_K 15
+/*! Maximum supported window size (k) */
+#define V42_MAX_WINDOW_SIZE_K 15
+
+/*! The number of info frames to allocate */
+#define V42_INFO_FRAMES (V42_MAX_WINDOW_SIZE_K + 1)
+/*! The number of control frames to allocate */
+#define V42_CTRL_FRAMES 8
+
+typedef struct
+{
+ /* V.42 LAP.M parameters */
+ uint8_t v42_tx_window_size_k;
+ uint8_t v42_rx_window_size_k;
+ uint16_t v42_tx_n401;
+ uint16_t v42_rx_n401;
+
+ /* V.42bis compressor parameters */
+ uint8_t comp;
+ int comp_dict_size;
+ int comp_max_string;
+} v42_config_parameters_t;
+
+typedef struct frame_s
+{
+ int len;
+ uint8_t buf[4 + V42_MAX_N_401];
+} v42_frame_t;
+
/*!
LAP-M descriptor. This defines the working state for a single instance of LAP-M.
*/
-struct lapm_state_s
+typedef struct
{
- int handle;
+ get_msg_func_t iframe_get;
+ void *iframe_get_user_data;
+
+ put_msg_func_t iframe_put;
+ void *iframe_put_user_data;
+
+ modem_status_func_t status_handler;
+ void *status_user_data;
+
hdlc_rx_state_t hdlc_rx;
hdlc_tx_state_t hdlc_tx;
-
- v42_frame_handler_t iframe_receive;
- void *iframe_receive_user_data;
- v42_status_func_t status_callback;
- void *status_callback_user_data;
+ /*! Negotiated values for the window and maximum info sizes */
+ uint8_t tx_window_size_k;
+ uint8_t rx_window_size_k;
+ uint16_t tx_n401;
+ uint16_t rx_n401;
+ uint8_t cmd_addr;
+ uint8_t rsp_addr;
+ uint8_t vs;
+ uint8_t va;
+ uint8_t vr;
int state;
- int tx_waiting;
- int debug;
- /*! TRUE if originator. FALSE if answerer */
- int we_are_originator;
- /*! Remote network type (unknown, answerer. originator) */
- int peer_is_originator;
- /*! Next N(S) for transmission */
- int next_tx_frame;
- /*! The last of our frames which the peer acknowledged */
- int last_frame_peer_acknowledged;
- /*! Next N(R) for reception */
- int next_expected_frame;
- /*! The last of the peer's frames which we acknowledged */
- int last_frame_we_acknowledged;
- /*! TRUE if we sent an I or S frame with the F-bit set */
- int solicit_f_bit;
- /*! Retransmission count */
- int retransmissions;
- /*! TRUE if peer is busy */
- int busy;
-
- /*! Acknowledgement timer */
- int t401_timer;
- /*! Reply delay timer - optional */
- int t402_timer;
- /*! Inactivity timer - optional */
- int t403_timer;
- /*! Maximum number of octets in an information field */
- int n401;
- /*! Window size */
- int window_size_k;
-
- lapm_frame_queue_t *txqueue;
- lapm_frame_queue_t *tx_next;
- lapm_frame_queue_t *tx_last;
- queue_state_t *tx_queue;
-
- span_sched_state_t sched;
- /*! \brief Error and flow logging control */
- logging_state_t logging;
-};
+ int configuring;
+ int local_busy;
+ int far_busy;
+ int rejected;
+ int retry_count;
-/*!
- V.42 descriptor. This defines the working state for a single instance of V.42.
-*/
-struct v42_state_s
-{
- /*! TRUE if we are the calling party, otherwise FALSE */
- int calling_party;
- /*! TRUE if we should detect whether the far end is V.42 capable. FALSE if we go
- directly to protocol establishment */
- int detect;
+ /* The control frame buffer, and its pointers */
+ int ctrl_put;
+ int ctrl_get;
+ v42_frame_t ctrl_buf[V42_CTRL_FRAMES];
+
+ /* The info frame buffer, and its pointers */
+ int info_put;
+ int info_get;
+ int info_acked;
+ v42_frame_t info_buf[V42_INFO_FRAMES];
+
+ void (*packer_process)(v42_state_t *m, int bits);
+} lapm_state_t;
+/*! V.42 support negotiation parameters */
+typedef struct
+{
/*! Stage in negotiating V.42 support */
int rx_negotiation_step;
int rxbits;
int txbits;
int txstream;
int txadps;
- /*! The LAP.M context */
+} v42_negotiation_t;
+
+/*!
+ V.42 descriptor. This defines the working state for a single
+ instance of a V.42 error corrector.
+*/
+struct v42_state_s
+{
+ /*! TRUE if we are the calling party, otherwise FALSE. */
+ int calling_party;
+ /*! TRUE if we should detect whether the far end is V.42 capable. FALSE if we go
+ directly to protocol establishment. */
+ int detect;
+
+ /*! The bit rate, used to time events */
+ int tx_bit_rate;
+
+ v42_config_parameters_t config;
+ v42_negotiation_t neg;
lapm_state_t lapm;
- /*! V.42 support detection timer */
- int t400_timer;
+ int bit_timer;
+ void (*bit_timer_func)(v42_state_t *m);
+
/*! \brief Error and flow logging control */
logging_state_t logging;
};
/*!
V.42bis dictionary node.
+ Note that 0 is not a valid node to point to (0 is always a control code), so 0 is used
+ as a "no such value" marker in this structure.
*/
typedef struct
{
- /*! \brief The prior code for each defined code. */
- uint16_t parent_code;
- /*! \brief The number of leaf nodes this node has */
- int16_t leaves;
- /*! \brief This leaf octet for each defined code. */
+ /*! \brief The value of the octet represented by the current dictionary node */
uint8_t node_octet;
- /*! \brief Bit map of the children which exist */
- uint32_t children[8];
+ /*! \brief The parent of this node */
+ uint16_t parent;
+ /*! \brief The first child of this node */
+ uint16_t child;
+ /*! \brief The next node at the same depth */
+ uint16_t next;
} v42bis_dict_node_t;
/*!
- V.42bis compression. This defines the working state for a single instance
- of V.42bis compression.
+ V.42bis compression or decompression. This defines the working state for a single instance
+ of V.42bis compression or decompression.
*/
typedef struct
{
+ /*! \brief Compression enabled. */
+ int v42bis_parm_p0;
/*! \brief Compression mode. */
int compression_mode;
- /*! \brief Callback function to handle received frames. */
- v42bis_frame_handler_t handler;
- /*! \brief An opaque pointer passed in calls to frame_handler. */
+ /*! \brief Callback function to handle output data. */
+ put_msg_func_t handler;
+ /*! \brief An opaque pointer passed in calls to the data handler. */
void *user_data;
- /*! \brief The maximum frame length allowed */
- int max_len;
+ /*! \brief The maximum amount to be passed to the data handler. */
+ int max_output_len;
- uint32_t string_code;
- uint32_t latest_code;
- int string_length;
- uint32_t output_bit_buffer;
- int output_bit_count;
- int output_octet_count;
- uint8_t output_buf[1024];
- v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS];
/*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */
int transparent;
- int change_transparency;
- /*! \brief IIR filter state, used in assessing compressibility. */
- int compressibility_filter;
- int compressibility_persistence;
-
/*! \brief Next empty dictionary entry */
- uint32_t v42bis_parm_c1;
+ uint16_t v42bis_parm_c1;
/*! \brief Current codeword size */
- int v42bis_parm_c2;
+ uint16_t v42bis_parm_c2;
/*! \brief Threshold for codeword size change */
- uint32_t v42bis_parm_c3;
+ uint16_t v42bis_parm_c3;
+ /*! \brief The current update point in the dictionary */
+ uint16_t update_at;
+ /*! \brief The last entry matched in the dictionary */
+ uint16_t last_matched;
+ /*! \brief The last entry added to the dictionary */
+ uint16_t last_added;
+ /*! \brief Total number of codewords in the dictionary */
+ int v42bis_parm_n2;
+ /*! \brief Maximum permitted string length */
+ int v42bis_parm_n7;
+ /*! \brief The dictionary */
+ v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS];
- /*! \brief Mark that this is the first octet/code to be processed */
- int first;
- uint8_t escape_code;
-} v42bis_compress_state_t;
+ /*! \brief The octet string in progress */
+ uint8_t string[V42BIS_MAX_STRING_SIZE];
+ /*! \brief The current length of the octet string in progress */
+ int string_length;
+ /*! \brief The amount of the octet string in progress which has already
+ been flushed out of the buffer */
+ int flushed_length;
-/*!
- V.42bis decompression. This defines the working state for a single instance
- of V.42bis decompression.
-*/
-typedef struct
-{
- /*! \brief Callback function to handle decompressed data. */
- v42bis_data_handler_t handler;
- /*! \brief An opaque pointer passed in calls to data_handler. */
- void *user_data;
- /*! \brief The maximum decompressed data block length allowed */
- int max_len;
+ /*! \brief Compression performance metric */
+ uint16_t compression_performance;
- uint32_t old_code;
- uint32_t last_old_code;
- uint32_t input_bit_buffer;
- int input_bit_count;
- int octet;
- int last_length;
- int output_octet_count;
- uint8_t output_buf[1024];
- v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS];
- /*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */
- int transparent;
+ /*! \brief Outgoing bit buffer (compression), or incoming bit buffer (decompression) */
+ uint32_t bit_buffer;
+ /*! \brief Outgoing bit count (compression), or incoming bit count (decompression) */
+ int bit_count;
- int last_extra_octet;
+ /*! \brief The output composition buffer */
+ uint8_t output_buf[V42BIS_MAX_OUTPUT_LENGTH];
+ /*! \brief The length of the contents of the output composition buffer */
+ int output_octet_count;
- /*! \brief Next empty dictionary entry */
- uint32_t v42bis_parm_c1;
- /*! \brief Current codeword size */
- int v42bis_parm_c2;
- /*! \brief Threshold for codeword size change */
- uint32_t v42bis_parm_c3;
-
- /*! \brief Mark that this is the first octet/code to be processed */
- int first;
+ /*! \brief The current value of the escape code */
uint8_t escape_code;
+ /*! \brief TRUE if we just hit an escape code, and are waiting for the following octet */
int escaped;
-} v42bis_decompress_state_t;
+} v42bis_comp_state_t;
/*!
V.42bis compression/decompression descriptor. This defines the working state for a
*/
struct v42bis_state_s
{
- /*! \brief V.42bis data compression directions. */
- int v42bis_parm_p0;
-
/*! \brief Compression state. */
- v42bis_compress_state_t compress;
+ v42bis_comp_state_t compress;
/*! \brief Decompression state. */
- v42bis_decompress_state_t decompress;
-
- /*! \brief Maximum codeword size (bits) */
- int v42bis_parm_n1;
- /*! \brief Total number of codewords */
- uint32_t v42bis_parm_n2;
- /*! \brief Maximum string length */
- int v42bis_parm_n7;
+ v42bis_comp_state_t decompress;
+
+ /*! \brief Error and flow logging control */
+ logging_state_t logging;
};
#endif
\param s The silence generator context.
\param handler The callback routine used to report status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) silence_gen_status_handler(silence_gen_state_t *s, modem_tx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) silence_gen_status_handler(silence_gen_state_t *s, modem_status_func_t handler, void *user_data);
/*! Initialise a timed silence generator context.
\brief Initialise a timed silence generator context.
T30_ERR_RX_GOTDCS, /*! DCS received while waiting for DTC */
T30_ERR_RX_INVALCMD, /*! Unexpected command after page received */
T30_ERR_RX_NOCARRIER, /*! Carrier lost during fax receive */
- T30_ERR_RX_NOEOL, /*! Timed out while waiting for EOL (end Of line) */
+ T30_ERR_RX_NOEOL, /*! Timed out while waiting for EOL (end of line) */
T30_ERR_RX_NOFAX, /*! Timed out while waiting for first line */
T30_ERR_RX_T2EXPDCN, /*! Timer T2 expired while waiting for DCN */
T30_ERR_RX_T2EXPD, /*! Timer T2 expired while waiting for phase D */
#define TRUE (!FALSE)
#endif
+/* Fixed point constant macros */
+#define FP_Q_9_7(x) ((int16_t) (128.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_8_8(x) ((int16_t) (256.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_7_9(x) ((int16_t) (512.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_6_10(x) ((int16_t) (1024.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_5_11(x) ((int16_t) (2048.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_4_12(x) ((int16_t) (4096.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_3_13(x) ((int16_t) (8192.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_2_14(x) ((int16_t) (16384.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_1_15(x) ((int16_t) (32768.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+
+#define FP_Q_9_23(x) ((int32_t) (65536.0*128.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_8_24(x) ((int32_t) (65536.0*256.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_7_25(x) ((int32_t) (65536.0*512.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_6_26(x) ((int32_t) (65536.0*1024.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_5_27(x) ((int32_t) (65536.0*2048.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_4_28(x) ((int32_t) (65536.0*4096.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_3_29(x) ((int32_t) (65536.0*8192.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_2_30(x) ((int32_t) (65536.0*16384.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+#define FP_Q_1_31(x) ((int32_t) (65536.0*32768.0*x + ((x >= 0.0) ? 0.5 : -0.5)))
+
#if defined(__cplusplus)
/* C++ doesn't seem to have sane rounding functions/macros yet */
#if !defined(WIN32)
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) v17_rx_set_modem_status_handler(v17_rx_state_t *s, modem_rx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) v17_rx_set_modem_status_handler(v17_rx_state_t *s, modem_status_func_t handler, void *user_data);
/*! Process a block of received V.17 modem audio samples.
\brief Process a block of received V.17 modem audio samples.
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) v17_tx_set_modem_status_handler(v17_tx_state_t *s, modem_tx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) v17_tx_set_modem_status_handler(v17_tx_state_t *s, modem_status_func_t handler, void *user_data);
/*! Generate a block of V.17 modem audio samples.
\brief Generate a block of V.17 modem audio samples.
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) v22bis_set_modem_status_handler(v22bis_state_t *s, modem_rx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) v22bis_set_modem_status_handler(v22bis_state_t *s, modem_status_func_t handler, void *user_data);
#if defined(__cplusplus)
}
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) v27ter_rx_set_modem_status_handler(v27ter_rx_state_t *s, modem_rx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) v27ter_rx_set_modem_status_handler(v27ter_rx_state_t *s, modem_status_func_t handler, void *user_data);
/*! Process a block of received V.27ter modem audio samples.
\brief Process a block of received V.27ter modem audio samples.
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) v27ter_tx_set_modem_status_handler(v27ter_tx_state_t *s, modem_tx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) v27ter_tx_set_modem_status_handler(v27ter_tx_state_t *s, modem_status_func_t handler, void *user_data);
/*! Generate a block of V.27ter modem audio samples.
\brief Generate a block of V.27ter modem audio samples.
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) v29_rx_set_modem_status_handler(v29_rx_state_t *s, modem_rx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) v29_rx_set_modem_status_handler(v29_rx_state_t *s, modem_status_func_t handler, void *user_data);
/*! Process a block of received V.29 modem audio samples.
\brief Process a block of received V.29 modem audio samples.
\param s The modem context.
\param handler The callback routine used to report modem status changes.
\param user_data An opaque pointer. */
-SPAN_DECLARE(void) v29_tx_set_modem_status_handler(v29_tx_state_t *s, modem_tx_status_func_t handler, void *user_data);
+SPAN_DECLARE(void) v29_tx_set_modem_status_handler(v29_tx_state_t *s, modem_status_func_t handler, void *user_data);
/*! Generate a block of V.29 modem audio samples.
\brief Generate a block of V.29 modem audio samples.
*
* Written by Steve Underwood <steveu@coppice.org>
*
- * Copyright (C) 2003 Steve Underwood
+ * Copyright (C) 2003, 2011 Steve Underwood
*
* All rights reserved.
*
#if !defined(_SPANDSP_V42_H_)
#define _SPANDSP_V42_H_
-enum
-{
- LAPM_DETECT = 0,
- LAPM_ESTABLISH = 1,
- LAPM_DATA = 2,
- LAPM_RELEASE = 3,
- LAPM_SIGNAL = 4,
- LAPM_SETPARM = 5,
- LAPM_TEST = 6,
- LAPM_UNSUPPORTED = 7
-};
-
-typedef void (*v42_status_func_t)(void *user_data, int status);
-typedef void (*v42_frame_handler_t)(void *user_data, const uint8_t *pkt, int len);
-
-typedef struct lapm_frame_queue_s
-{
- struct lapm_frame_queue_s *next;
- int len;
- uint8_t frame[];
-} lapm_frame_queue_t;
-
-/*!
- LAP-M descriptor. This defines the working state for a single instance of LAP-M.
-*/
-typedef struct lapm_state_s lapm_state_t;
-
-/*!
- V.42 descriptor. This defines the working state for a single instance of V.42.
-*/
typedef struct v42_state_s v42_state_t;
-/*! Log the raw HDLC frames */
-#define LAPM_DEBUG_LAPM_RAW (1 << 0)
-/*! Log the interpreted frames */
-#define LAPM_DEBUG_LAPM_DUMP (1 << 1)
-/*! Log state machine changes */
-#define LAPM_DEBUG_LAPM_STATE (1 << 2)
-
#if defined(__cplusplus)
extern "C"
{
SPAN_DECLARE(const char *) lapm_status_to_str(int status);
-/*! Dump LAP.M frames in a raw and/or decoded forms
- \param frame The frame itself
- \param len The length of the frame, in octets
- \param showraw TRUE if the raw octets should be dumped
- \param txrx TRUE if tx, FALSE if rx. Used to highlight the packet's direction.
-*/
-SPAN_DECLARE(void) lapm_dump(lapm_state_t *s, const uint8_t *frame, int len, int showraw, int txrx);
+SPAN_DECLARE_NONSTD(void) lapm_receive(void *user_data, const uint8_t *frame, int len, int ok);
-/*! Accept an HDLC packet
-*/
-SPAN_DECLARE_NONSTD(void) lapm_receive(void *user_data, const uint8_t *buf, int len, int ok);
+SPAN_DECLARE(void) v42_start(v42_state_t *s);
-/*! Transmit a LAP.M frame
-*/
-SPAN_DECLARE(int) lapm_tx(lapm_state_t *s, const void *buf, int len);
+SPAN_DECLARE(void) v42_stop(v42_state_t *s);
-/*! Transmit a LAP.M information frame
-*/
-SPAN_DECLARE(int) lapm_tx_iframe(lapm_state_t *s, const void *buf, int len, int cr);
-
-/*! Send a break over a LAP.M connection
-*/
-SPAN_DECLARE(int) lapm_break(lapm_state_t *s, int enable);
-
-/*! Initiate an orderly release of a LAP.M connection
-*/
-SPAN_DECLARE(int) lapm_release(lapm_state_t *s);
-
-/*! Enable or disable loopback of a LAP.M connection
+/*! Set the busy status of the local end of a V.42 context.
+ \param s The V.42 context.
+ \param busy The new local end busy status.
+ \return The previous local end busy status.
*/
-SPAN_DECLARE(int) lapm_loopback(lapm_state_t *s, int enable);
+SPAN_DECLARE(int) v42_set_local_busy_status(v42_state_t *s, int busy);
-/*! Assign or remove a callback routine used to deal with V.42 status changes.
+/*! Get the busy status of the far end of a V.42 context.
+ \param s The V.42 context.
+ \return The far end busy status.
*/
-SPAN_DECLARE(void) v42_set_status_callback(v42_state_t *s, v42_status_func_t callback, void *user_data);
+SPAN_DECLARE(int) v42_get_far_busy_status(v42_state_t *s);
-/*! Process a newly received bit for a V.42 context.
-*/
SPAN_DECLARE(void) v42_rx_bit(void *user_data, int bit);
-/*! Get the next transmit bit for a V.42 context.
-*/
SPAN_DECLARE(int) v42_tx_bit(void *user_data);
+SPAN_DECLARE(void) v42_set_status_callback(v42_state_t *s, modem_status_func_t callback, void *user_data);
+
/*! Initialise a V.42 context.
\param s The V.42 context.
\param calling_party TRUE if caller mode, else answerer mode.
- \param frame_handler A callback function to handle received frames of data.
- \param user_data An opaque pointer passed to the frame handler routine.
+ \param detect TRUE to perform the V.42 detection, else go straight into LAP.M
+ \param iframe_get A callback function to handle received frames of data.
+ \param iframe_put A callback function to get frames of data for transmission.
+ \param user_data An opaque pointer passed to the frame handler routines.
\return ???
*/
-SPAN_DECLARE(v42_state_t *) v42_init(v42_state_t *s, int calling_party, int detect, v42_frame_handler_t frame_handler, void *user_data);
+SPAN_DECLARE(v42_state_t *) v42_init(v42_state_t *s,
+ int calling_party,
+ int detect,
+ get_msg_func_t iframe_get,
+ put_msg_func_t iframe_put,
+ void *user_data);
/*! Restart a V.42 context.
\param s The V.42 context.
/*! Release a V.42 context.
\param s The V.42 context.
\return 0 if OK */
-SPAN_DECLARE(int) v42_release(v42_state_t *s);
+SPAN_DECLARE(void) v42_release(v42_state_t *s);
/*! Free a V.42 context.
\param s The V.42 context.
\return 0 if OK */
-SPAN_DECLARE(int) v42_free(v42_state_t *s);
+SPAN_DECLARE(void) v42_free(v42_state_t *s);
#if defined(__cplusplus)
}
*
* Written by Steve Underwood <steveu@coppice.org>
*
- * Copyright (C) 2005 Steve Underwood
+ * Copyright (C) 2005, 2011 Steve Underwood
*
* All rights reserved.
*
#define V42BIS_MIN_DICTIONARY_SIZE 512
#define V42BIS_MAX_BITS 12
#define V42BIS_MAX_CODEWORDS 4096 /* 2^V42BIS_MAX_BITS */
-#define V42BIS_TABLE_SIZE 5021 /* This should be a prime >(2^V42BIS_MAX_BITS) */
+#define V42BIS_MAX_OUTPUT_LENGTH 1024
enum
{
V42BIS_COMPRESSION_MODE_NEVER
};
-typedef void (*v42bis_frame_handler_t)(void *user_data, const uint8_t *pkt, int len);
-typedef void (*v42bis_data_handler_t)(void *user_data, const uint8_t *buf, int len);
-
/*!
V.42bis compression/decompression descriptor. This defines the working state for a
single instance of V.42bis compress/decompression.
\param buf The data to be compressed.
\param len The length of the data buffer.
\return 0 */
-SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t *buf, int len);
+SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t buf[], int len);
/*! Flush out any data remaining in a compression buffer.
\param s The V.42bis context.
\param buf The data to be decompressed.
\param len The length of the data buffer.
\return 0 */
-SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t *buf, int len);
+SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t buf[], int len);
/*! Flush out any data remaining in the decompression buffer.
\param s The V.42bis context.
\param negotiated_p0 The negotiated P0 parameter, from the V.42bis spec.
\param negotiated_p1 The negotiated P1 parameter, from the V.42bis spec.
\param negotiated_p2 The negotiated P2 parameter, from the V.42bis spec.
- \param frame_handler Frame callback handler.
- \param frame_user_data An opaque pointer passed to the frame callback handler.
- \param max_frame_len The maximum length that should be passed to the frame handler.
- \param data_handler data callback handler.
- \param data_user_data An opaque pointer passed to the data callback handler.
- \param max_data_len The maximum length that should be passed to the data handler.
+ \param encode_handler Encode callback handler.
+ \param encode_user_data An opaque pointer passed to the encode callback handler.
+ \param max_encode_len The maximum length that should be passed to the encode handler.
+ \param decode_handler Decode callback handler.
+ \param decode_user_data An opaque pointer passed to the decode callback handler.
+ \param max_decode_len The maximum length that should be passed to the decode handler.
\return The V.42bis context. */
SPAN_DECLARE(v42bis_state_t *) v42bis_init(v42bis_state_t *s,
int negotiated_p0,
int negotiated_p1,
int negotiated_p2,
- v42bis_frame_handler_t frame_handler,
- void *frame_user_data,
- int max_frame_len,
- v42bis_data_handler_t data_handler,
- void *data_user_data,
- int max_data_len);
+ put_msg_func_t encode_handler,
+ void *encode_user_data,
+ int max_encode_len,
+ put_msg_func_t decode_handler,
+ void *decode_user_data,
+ int max_decode_len);
/*! Release a V.42bis context.
\param s The V.42bis context.
static int increment_overflow(int *number, int delta)
{
- int number0;
+ int last_number;
- number0 = *number;
+ last_number = *number;
*number += delta;
- return (*number < number0) != (delta < 0);
+ return (*number < last_number) != (delta < 0);
}
/*- End of function --------------------------------------------------------*/
int y;
int hit;
int i;
+ int newy;
+ time_t tdelta;
+ int idelta;
+ int leapdays;
corr = 0;
hit = 0;
rem = *timep - tdays*SECS_PER_DAY;
while (tdays < 0 || tdays >= year_lengths[isleap(y)])
{
- int newy;
- time_t tdelta;
- int idelta;
- int leapdays;
-
tdelta = tdays / DAYS_PER_LEAP_YEAR;
idelta = tdelta;
if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) v17_rx_set_modem_status_handler(v17_rx_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) v17_rx_set_modem_status_handler(v17_rx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) v17_tx_set_modem_status_handler(v17_tx_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) v17_tx_set_modem_status_handler(v17_tx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) v22bis_set_modem_status_handler(v22bis_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) v22bis_set_modem_status_handler(v22bis_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) v27ter_rx_set_modem_status_handler(v27ter_rx_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) v27ter_rx_set_modem_status_handler(v27ter_rx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) v27ter_tx_set_modem_status_handler(v27ter_tx_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) v27ter_tx_set_modem_status_handler(v27ter_tx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) v29_rx_set_modem_status_handler(v29_rx_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) v29_rx_set_modem_status_handler(v29_rx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) v29_tx_set_modem_status_handler(v29_tx_state_t *s, modem_tx_status_func_t handler, void *user_data)
+SPAN_DECLARE(void) v29_tx_set_modem_status_handler(v29_tx_state_t *s, modem_status_func_t handler, void *user_data)
{
s->status_handler = handler;
s->status_user_data = user_data;
*
* Written by Steve Underwood <steveu@coppice.org>
*
- * Copyright (C) 2004 Steve Underwood
+ * Copyright (C) 2004, 2011 Steve Underwood
*
* All rights reserved.
*
#include <inttypes.h>
#include <string.h>
#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <assert.h>
#include "spandsp/telephony.h"
#include "spandsp/logging.h"
+#include "spandsp/bit_operations.h"
#include "spandsp/async.h"
#include "spandsp/hdlc.h"
-#include "spandsp/schedule.h"
-#include "spandsp/queue.h"
#include "spandsp/v42.h"
#include "spandsp/private/logging.h"
-#include "spandsp/private/schedule.h"
#include "spandsp/private/hdlc.h"
#include "spandsp/private/v42.h"
-#if !defined(FALSE)
-#define FALSE 0
-#endif
-#if !defined(TRUE)
-#define TRUE (!FALSE)
-#endif
-
-#define LAPM_FRAMETYPE_MASK 0x03
-
-#define LAPM_FRAMETYPE_I 0x00
-#define LAPM_FRAMETYPE_I_ALT 0x02
-#define LAPM_FRAMETYPE_S 0x01
-#define LAPM_FRAMETYPE_U 0x03
-
-/* Timer values */
+#define FALSE 0
+#define TRUE (!FALSE)
-#define T_WAIT_MIN 2000
-#define T_WAIT_MAX 10000
/* Detection phase timer */
-#define T_400 750000
+#define T_400 750
/* Acknowledgement timer - 1 second between SABME's */
-#define T_401 1000000
+#define T_401 1000
/* Replay delay timer (optional) */
-#define T_402 1000000
+#define T_402 1000
/* Inactivity timer (optional). No default - use 10 seconds with no packets */
-#define T_403 10000000
-/* Max retries */
-#define N_400 3
-/* Max octets in an information field */
-#define N_401 128
+#define T_403 10000
-#define LAPM_DLCI_DTE_TO_DTE 0
-#define LAPM_DLCI_LAYER2_MANAGEMENT 63
+#define LAPM_DLCI_DTE_TO_DTE 0
+#define LAPM_DLCI_LAYER2_MANAGEMENT 63
-static void t401_expired(span_sched_state_t *s, void *user_data);
-static void t403_expired(span_sched_state_t *s, void *user_data);
+#define elements(a) (sizeof(a)/sizeof((a)[0]))
-SPAN_DECLARE(void) lapm_reset(lapm_state_t *s);
-SPAN_DECLARE(void) lapm_restart(lapm_state_t *s);
+/* LAPM definitions */
+
+#define LAPM_FRAMETYPE_MASK 0x03
+
+enum
+{
+ LAPM_FRAMETYPE_I = 0x00,
+ LAPM_FRAMETYPE_I_ALT = 0x02,
+ LAPM_FRAMETYPE_S = 0x01,
+ LAPM_FRAMETYPE_U = 0x03
+};
+
+/* Supervisory headers */
+enum
+{
+ LAPM_S_RR = 0x00, /* cr */
+ LAPM_S_RNR = 0x04, /* cr */
+ LAPM_S_REJ = 0x08, /* cr */
+ LAPM_S_SREJ = 0x0C /* cr */
+};
-static void lapm_link_down(lapm_state_t *s);
+#define LAPM_S_PF 0x01
-static __inline__ void lapm_init_header(uint8_t *frame, int command)
+/* Unnumbered headers */
+enum
+{
+ LAPM_U_UI = 0x00, /* cr */
+ LAPM_U_DM = 0x0C, /* r */
+ LAPM_U_DISC = 0x40, /* c */
+ LAPM_U_UA = 0x60, /* r */
+ LAPM_U_SABME = 0x6C, /* c */
+ LAPM_U_FRMR = 0x84, /* r */
+ LAPM_U_XID = 0xAC, /* cr */
+ LAPM_U_TEST = 0xE0 /* c */
+};
+
+#define LAPM_U_PF 0x10
+
+/* XID sub-field definitions */
+#define FI_GENERAL 0x82
+#define GI_PARAM_NEGOTIATION 0x80
+#define GI_PRIVATE_NEGOTIATION 0xF0
+#define GI_USER_DATA 0xFF
+
+/* Param negotiation (Table 11a/V.42) */
+enum
+{
+ PI_HDLC_OPTIONAL_FUNCTIONS = 0x03,
+ PI_TX_INFO_MAXSIZE = 0x05,
+ PI_RX_INFO_MAXSIZE = 0x06,
+ PI_TX_WINDOW_SIZE = 0x07,
+ PI_RX_WINDOW_SIZE = 0x08
+};
+
+/* Private param negotiation (Table 11b/V.42) */
+enum
+{
+ PI_PARAMETER_SET_ID = 0x00,
+ PI_V42BIS_COMPRESSION_REQUEST = 0x01,
+ PI_V42BIS_NUM_CODEWORDS = 0x02,
+ PI_V42BIS_MAX_STRING_LENGTH = 0x03
+};
+
+#define LAPM_DLCI_DTE_TO_DTE 0
+#define LAPM_DLCI_LAYER2_MANAGEMENT 63
+
+/* Type definitions */
+enum
+{
+ LAPM_DETECT = 0,
+ LAPM_IDLE = 1,
+ LAPM_ESTABLISH = 2,
+ LAPM_DATA = 3,
+ LAPM_RELEASE = 4,
+ LAPM_SIGNAL = 5,
+ LAPM_SETPARM = 6,
+ LAPM_TEST = 7,
+ LAPM_V42_UNSUPPORTED = 8
+};
+
+/* Prototypes */
+static int lapm_connect(v42_state_t *ss);
+static int lapm_disconnect(v42_state_t *s);
+static void reset_lapm(v42_state_t *s);
+static void lapm_hdlc_underflow(void *user_data);
+
+static int lapm_config(v42_state_t *ss);
+
+SPAN_DECLARE(const char *) lapm_status_to_str(int status)
{
- /* Data link connection identifier (0) */
- /* Command/response (0 if answerer, 1 if originator) */
- /* Extended address (1) */
- frame[0] = (LAPM_DLCI_DTE_TO_DTE << 2) | ((command) ? 0x02 : 0x00) | 0x01;
+ switch (status)
+ {
+ case LAPM_DETECT:
+ return "LAPM_DETECT";
+ case LAPM_IDLE:
+ return "LAPM_IDLE";
+ case LAPM_ESTABLISH:
+ return "LAPM_ESTABLISH";
+ case LAPM_DATA:
+ return "LAPM_DATA";
+ case LAPM_RELEASE:
+ return "LAPM_RELEASE";
+ case LAPM_SIGNAL:
+ return "LAPM_SIGNAL";
+ case LAPM_SETPARM:
+ return "LAPM_SETPARM";
+ case LAPM_TEST:
+ return "LAPM_TEST";
+ case LAPM_V42_UNSUPPORTED:
+ return "LAPM_V42_UNSUPPORTED";
+ }
+ /*endswitch*/
+ return "???";
}
/*- End of function --------------------------------------------------------*/
-static int lapm_tx_frame(lapm_state_t *s, uint8_t *frame, int len)
+static void report_rx_status_change(v42_state_t *s, int status)
{
- if ((s->debug & LAPM_DEBUG_LAPM_DUMP))
- lapm_dump(s, frame, len, s->debug & LAPM_DEBUG_LAPM_RAW, TRUE);
- /*endif*/
- hdlc_tx_frame(&s->hdlc_tx, frame, len);
- return 0;
+ if (s->lapm.status_handler)
+ s->lapm.status_handler(s->lapm.status_user_data, status);
+ else if (s->lapm.iframe_put)
+ s->lapm.iframe_put(s->lapm.iframe_put_user_data, NULL, status);
}
/*- End of function --------------------------------------------------------*/
-static void t400_expired(span_sched_state_t *ss, void *user_data)
+static inline uint32_t pack_value(const uint8_t *buf, int len)
{
- v42_state_t *s;
-
- /* Give up trying to detect a V.42 capable peer. */
- s = (v42_state_t *) user_data;
- s->t400_timer = -1;
- s->lapm.state = LAPM_UNSUPPORTED;
- if (s->lapm.status_callback)
- s->lapm.status_callback(s->lapm.status_callback_user_data, s->lapm.state);
- /*endif*/
+ uint32_t val;
+
+ val = 0;
+ while (len--)
+ {
+ val <<= 8;
+ val |= *buf++;
+ }
+ return val;
}
/*- End of function --------------------------------------------------------*/
-static void lapm_send_ua(lapm_state_t *s, int pfbit)
+static inline v42_frame_t *get_next_free_ctrl_frame(lapm_state_t *s)
{
- uint8_t frame[3];
+ v42_frame_t *f;
+ int ctrl_put_next;
- lapm_init_header(frame, !s->we_are_originator);
- frame[1] = (uint8_t) (0x63 | (pfbit << 4));
- frame[2] = 0;
- span_log(&s->logging, SPAN_LOG_FLOW, "Sending unnumbered acknowledgement\n");
- lapm_tx_frame(s, frame, 3);
+ if ((ctrl_put_next = s->ctrl_put + 1) >= V42_CTRL_FRAMES)
+ ctrl_put_next = 0;
+ if (ctrl_put_next == s->ctrl_get)
+ return NULL;
+ f = &s->ctrl_buf[s->ctrl_put];
+ s->ctrl_put = ctrl_put_next;
+ return f;
}
/*- End of function --------------------------------------------------------*/
-static void lapm_send_sabme(span_sched_state_t *ss, void *user_data)
+static int tx_unnumbered_frame(lapm_state_t *s, uint8_t addr, uint8_t ctrl, uint8_t *info, int len)
{
- lapm_state_t *s;
- uint8_t frame[3];
+ v42_frame_t *f;
+ uint8_t *buf;
- s = (lapm_state_t *) user_data;
- if (s->t401_timer >= 0)
- {
-fprintf(stderr, "Deleting T401 q [%p] %d\n", (void *) s, s->t401_timer);
- span_schedule_del(&s->sched, s->t401_timer);
- s->t401_timer = -1;
- }
- /*endif*/
- if (++s->retransmissions > N_400)
+ if ((f = get_next_free_ctrl_frame(s)) == NULL)
+ return -1;
+ buf = f->buf;
+ buf[0] = addr;
+ buf[1] = LAPM_FRAMETYPE_U | ctrl;
+ f->len = 2;
+ if (info && len)
{
- /* 8.3.2.2 Too many retries */
- s->state = LAPM_RELEASE;
- if (s->status_callback)
- s->status_callback(s->status_callback_user_data, s->state);
- /*endif*/
- return;
+ memcpy(buf + f->len, info, len);
+ f->len += len;
}
- /*endif*/
-fprintf(stderr, "Setting T401 a1 [%p]\n", (void *) s);
- s->t401_timer = span_schedule_event(&s->sched, T_401, lapm_send_sabme, s);
- lapm_init_header(frame, s->we_are_originator);
- frame[1] = 0x7F;
- frame[2] = 0;
- span_log(&s->logging, SPAN_LOG_FLOW, "Sending SABME (set asynchronous balanced mode extended)\n");
- lapm_tx_frame(s, frame, 3);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int tx_supervisory_frame(lapm_state_t *s, uint8_t addr, uint8_t ctrl, uint8_t pf_mask)
+{
+ v42_frame_t *f;
+ uint8_t *buf;
+
+ if ((f = get_next_free_ctrl_frame(s)) == NULL)
+ return -1;
+ buf = f->buf;
+ buf[0] = addr;
+ buf[1] = LAPM_FRAMETYPE_S | ctrl;
+ buf[2] = (s->vr << 1) | pf_mask;
+ f->len = 3;
+ return 0;
}
/*- End of function --------------------------------------------------------*/
-static int lapm_ack_packet(lapm_state_t *s, int num)
+static __inline__ int set_param(int param, int value, int def)
{
- lapm_frame_queue_t *f;
- lapm_frame_queue_t *prev;
+ if ((value < def && param >= def) || (value >= def && param < def))
+ return def;
+ if ((value < def && param < value) || (value >= def && param > value))
+ return value;
+ return param;
+}
+/*- End of function --------------------------------------------------------*/
- for (prev = NULL, f = s->txqueue; f; prev = f, f = f->next)
+static int receive_xid(v42_state_t *ss, const uint8_t *frame, int len)
+{
+ lapm_state_t *s;
+ v42_config_parameters_t config;
+ const uint8_t *buf;
+ uint8_t group_id;
+ uint16_t group_len;
+ uint32_t param_val;
+ uint8_t param_id;
+ uint8_t param_len;
+
+ s = &ss->lapm;
+ if (frame[2] != FI_GENERAL)
+ return -1;
+ memset(&config, 0, sizeof(config));
+ /* Skip the header octets */
+ frame += 3;
+ len -= 3;
+ while (len > 0)
{
- if ((f->frame[1] >> 1) == num)
+ group_id = frame[0];
+ group_len = frame[1];
+ group_len = (group_len << 8) | frame[2];
+ frame += 3;
+ len -= (3 + group_len);
+ if (len < 0)
+ break;
+ buf = frame;
+ frame += group_len;
+ switch (group_id)
{
- /* Cancel each packet, as necessary */
- if (prev)
- prev->next = f->next;
- else
- s->txqueue = f->next;
- /*endif*/
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "-- ACKing packet %d. New txqueue is %d (-1 means empty)\n",
- (f->frame[1] >> 1),
- (s->txqueue) ? (s->txqueue->frame[1] >> 1) : -1);
- s->last_frame_peer_acknowledged = num;
- free(f);
- /* Reset retransmission count if we actually acked something */
- s->retransmissions = 0;
- return 1;
+ case GI_PARAM_NEGOTIATION:
+ while (group_len > 0)
+ {
+ param_id = buf[0];
+ param_len = buf[1];
+ buf += 2;
+ group_len -= (2 + param_len);
+ if (group_len < 0)
+ break;
+ switch (param_id)
+ {
+ case PI_HDLC_OPTIONAL_FUNCTIONS:
+ param_val = pack_value(buf, param_len);
+ break;
+ case PI_TX_INFO_MAXSIZE:
+ param_val = pack_value(buf, param_len);
+ param_val >>= 3;
+ config.v42_tx_n401 =
+ s->tx_n401 = set_param(s->tx_n401, param_val, ss->config.v42_tx_n401);
+ break;
+ case PI_RX_INFO_MAXSIZE:
+ param_val = pack_value(buf, param_len);
+ param_val >>= 3;
+ config.v42_rx_n401 =
+ s->rx_n401 = set_param(s->rx_n401, param_val, ss->config.v42_rx_n401);
+ break;
+ case PI_TX_WINDOW_SIZE:
+ param_val = pack_value(buf, param_len);
+ config.v42_tx_window_size_k =
+ s->tx_window_size_k = set_param(s->tx_window_size_k, param_val, ss->config.v42_tx_window_size_k);
+ break;
+ case PI_RX_WINDOW_SIZE:
+ param_val = pack_value(buf, param_len);
+ config.v42_rx_window_size_k =
+ s->rx_window_size_k = set_param(s->rx_window_size_k, param_val, ss->config.v42_rx_window_size_k);
+ break;
+ default:
+ break;
+ }
+ buf += param_len;
+ }
+ break;
+ case GI_PRIVATE_NEGOTIATION:
+ while (group_len > 0)
+ {
+ param_id = buf[0];
+ param_len = buf[1];
+ buf += 2;
+ group_len -= (2 + param_len);
+ if (group_len < 0)
+ break;
+ switch (param_id)
+ {
+ case PI_PARAMETER_SET_ID:
+ /* This might be worth monitoring, but it doesn't serve mnuch other purpose */
+ break;
+ case PI_V42BIS_COMPRESSION_REQUEST:
+ config.comp = pack_value(buf, param_len);
+ break;
+ case PI_V42BIS_NUM_CODEWORDS:
+ config.comp_dict_size = pack_value(buf, param_len);
+ break;
+ case PI_V42BIS_MAX_STRING_LENGTH:
+ config.comp_max_string = pack_value(buf, param_len);
+ break;
+ default:
+ break;
+ }
+ buf += param_len;
+ }
+ break;
+ default:
+ break;
}
- /*endif*/
}
- /*endfor*/
+ //v42_update_config(ss, &config);
return 0;
}
/*- End of function --------------------------------------------------------*/
-static void lapm_ack_rx(lapm_state_t *s, int ack)
+static void transmit_xid(v42_state_t *ss, uint8_t addr)
{
- int i;
- int cnt;
+ lapm_state_t *s;
+ uint8_t *buf;
+ int len;
+ int group_len;
+ uint32_t param_val;
+ v42_frame_t *f;
- /* This might not be acking anything new */
- if (s->last_frame_peer_acknowledged == ack)
- return;
- /*endif*/
- /* It should be acking something that is actually outstanding */
- if ((s->last_frame_peer_acknowledged < s->next_tx_frame && (ack < s->last_frame_peer_acknowledged || ack > s->next_tx_frame))
- ||
- (s->last_frame_peer_acknowledged > s->next_tx_frame && (ack > s->last_frame_peer_acknowledged || ack < s->next_tx_frame)))
- {
- /* ACK was outside our window --- ignore */
- span_log(&s->logging, SPAN_LOG_FLOW, "ACK received outside window, ignoring\n");
+ s = &ss->lapm;
+ if ((f = get_next_free_ctrl_frame(s)) == NULL)
return;
- }
- /*endif*/
+
+ buf = f->buf;
+ len = 0;
+
+ /* Figure 11/V.42 */
+ *buf++ = addr;
+ *buf++ = LAPM_U_XID | LAPM_FRAMETYPE_U;
+ /* Format identifier subfield */
+ *buf++ = FI_GENERAL;
+ len += 3;
+
+ /* Parameter negotiation group */
+ group_len = 20;
+ *buf++ = GI_PARAM_NEGOTIATION;
+ *buf++ = (group_len >> 8) & 0xFF;
+ *buf++ = group_len & 0xFF;
+ len += 3;
+
+ /* For conformance with the encoding rules in ISO/IEC 8885, the transmitter of an XID command frame shall
+ set bit positions 2, 4, 8, 9, 12 and 16 to 1. (Table 11a/V.42)
+ Optional bits are:
+ 3 Selective retransmission procedure (SREJ frame) single I frame request
+ 14 Loop-back test procedure (TEST frame)
+ 17
+ Extended FCS procedure (32-bit FCS)
+ 24 Selective retransmission procedure (SREJ frame) multiple I frame request with span list
+ capability. */
+ *buf++ = PI_HDLC_OPTIONAL_FUNCTIONS;
+ *buf++ = 4;
+ *buf++ = 0x8A; /* Bits 2, 4, and 8 set */
+ *buf++ = 0x89; /* Bits 9, 12, and 16 set */
+ *buf++ = 0x00;
+ *buf++ = 0x00;
+
+ /* Send the maximum as a number of bits, rather than octets */
+ param_val = ss->config.v42_tx_n401 << 3;
+ *buf++ = PI_TX_INFO_MAXSIZE;
+ *buf++ = 2;
+ *buf++ = (param_val >> 8) & 0xFF;
+ *buf++ = (param_val & 0xFF);
+
+ /* Send the maximum as a number of bits, rather than octets */
+ param_val = ss->config.v42_rx_n401 << 3;
+ *buf++ = PI_RX_INFO_MAXSIZE;
+ *buf++ = 2;
+ *buf++ = (param_val >> 8) & 0xFF;
+ *buf++ = (param_val & 0xFF);
- /* Cancel each packet, as necessary */
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "-- ACKing all packets from %d to (but not including) %d\n",
- s->last_frame_peer_acknowledged,
- ack);
- for (cnt = 0, i = s->last_frame_peer_acknowledged; i != ack; i = (i + 1) & 0x7F)
- cnt += lapm_ack_packet(s, i);
- /*endfor*/
- s->last_frame_peer_acknowledged = ack;
- if (s->txqueue == NULL)
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Since there was nothing left, stopping timer T_401\n");
- /* Something was ACK'd. Stop timer T_401. */
-fprintf(stderr, "T401 a2 is %d [%p]\n", s->t401_timer, (void *) s);
- if (s->t401_timer >= 0)
- {
-fprintf(stderr, "Deleting T401 a3 [%p] %d\n", (void *) s, s->t401_timer);
- span_schedule_del(&s->sched, s->t401_timer);
- s->t401_timer = -1;
- }
- /*endif*/
- }
- /*endif*/
- if (s->t403_timer >= 0)
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Stopping timer T_403, since we got an ACK\n");
- if (s->t403_timer >= 0)
- {
-fprintf(stderr, "Deleting T403 b %d\n", s->t403_timer);
- span_schedule_del(&s->sched, s->t403_timer);
- s->t403_timer = -1;
- }
- /*endif*/
- }
- /*endif*/
- if (s->txqueue)
- {
- /* Something left to transmit. Start timer T_401 again if it is stopped */
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "-- Something left to transmit (%d). Restarting timer T_401\n",
- s->txqueue->frame[1] >> 1);
- if (s->t401_timer < 0)
- {
-fprintf(stderr, "Setting T401 b [%p]\n", (void *) s);
- s->t401_timer = span_schedule_event(&s->sched, T_401, t401_expired, s);
- }
- /*endif*/
- }
- else
+ *buf++ = PI_TX_WINDOW_SIZE;
+ *buf++ = 1;
+ *buf++ = ss->config.v42_tx_window_size_k;
+
+ *buf++ = PI_RX_WINDOW_SIZE;
+ *buf++ = 1;
+ *buf++ = ss->config.v42_rx_window_size_k;
+
+ len += group_len;
+
+ if (ss->config.comp)
{
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Nothing left, starting timer T_403\n");
- /* Nothing to transmit. Start timer T_403. */
-fprintf(stderr, "Setting T403 c\n");
- s->t403_timer = span_schedule_event(&s->sched, T_403, t403_expired, s);
+ /* Private parameter negotiation group */
+ group_len = 15;
+ *buf++ = GI_PRIVATE_NEGOTIATION;
+ *buf++ = (group_len >> 8) & 0xFF;
+ *buf++ = group_len & 0xFF;
+ len += 3;
+
+ /* Private parameter for V.42 (ASCII for V42). V.42 says ".42", but V.42bis says "V42",
+ and that seems to be what should be used. */
+ *buf++ = PI_PARAMETER_SET_ID;
+ *buf++ = 3;
+ *buf++ = 'V';
+ *buf++ = '4';
+ *buf++ = '2';
+
+ /* V.42bis P0
+ 00 Compression in neither direction (default);
+ 01 Negotiation initiator-responder direction only;
+ 10 Negotiation responder-initiator direction only;
+ 11 Both directions. */
+ *buf++ = PI_V42BIS_COMPRESSION_REQUEST;
+ *buf++ = 1;
+ *buf++ = ss->config.comp;
+
+ /* V.42bis P1 */
+ param_val = ss->config.comp_dict_size;
+ *buf++ = PI_V42BIS_NUM_CODEWORDS;
+ *buf++ = 2;
+ *buf++ = (param_val >> 8) & 0xFF;
+ *buf++ = param_val & 0xFF;
+
+ /* V.42bis P2 */
+ *buf++ = PI_V42BIS_MAX_STRING_LENGTH;
+ *buf++ = 1;
+ *buf++ = ss->config.comp_max_string;
+
+ len += group_len;
}
- /*endif*/
+
+ f->len = len;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int ms_to_bits(v42_state_t *s, int time)
+{
+ return ((time*s->tx_bit_rate)/1000);
}
/*- End of function --------------------------------------------------------*/
-static void lapm_reject(lapm_state_t *s)
+static void t400_expired(v42_state_t *ss)
{
- uint8_t frame[4];
-
- lapm_init_header(frame, !s->we_are_originator);
- frame[1] = (uint8_t) (0x00 | 0x08 | LAPM_FRAMETYPE_S);
- /* Where to start retransmission */
- frame[2] = (uint8_t) ((s->next_expected_frame << 1) | 0x01);
- span_log(&s->logging, SPAN_LOG_FLOW, "Sending REJ (reject (%d)\n", s->next_expected_frame);
- lapm_tx_frame(s, frame, 4);
+ /* Give up trying to detect a V.42 capable peer. */
+ ss->bit_timer = 0;
+ ss->lapm.state = LAPM_V42_UNSUPPORTED;
+ report_rx_status_change(ss, ss->lapm.state);
}
/*- End of function --------------------------------------------------------*/
-static void lapm_rr(lapm_state_t *s, int pfbit)
+static __inline__ void t400_start(v42_state_t *s)
{
- uint8_t frame[4];
-
- lapm_init_header(frame, !s->we_are_originator);
- frame[1] = (uint8_t) (0x00 | 0x00 | LAPM_FRAMETYPE_S);
- frame[2] = (uint8_t) ((s->next_expected_frame << 1) | pfbit);
- /* Note that we have already ACKed this */
- s->last_frame_we_acknowledged = s->next_expected_frame;
- span_log(&s->logging, SPAN_LOG_FLOW, "Sending RR (receiver ready) (%d)\n", s->next_expected_frame);
- lapm_tx_frame(s, frame, 4);
+ s->bit_timer = ms_to_bits(s, T_400);
+ s->bit_timer_func = t400_expired;
}
/*- End of function --------------------------------------------------------*/
-static void t401_expired(span_sched_state_t *ss, void *user_data)
+static __inline__ void t400_stop(v42_state_t *s)
+{
+ s->bit_timer = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void t401_expired(v42_state_t *ss)
{
lapm_state_t *s;
-
- s = (lapm_state_t *) user_data;
-fprintf(stderr, "Expiring T401 a4 [%p]\n", (void *) s);
- s->t401_timer = -1;
- if (s->txqueue)
+
+ span_log(&ss->logging, SPAN_LOG_FLOW, "T.401 expired\n");
+ s = &ss->lapm;
+ if (s->retry_count > V42_DEFAULT_N_400)
{
- /* Retransmit first packet in the queue, setting the poll bit */
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Timer T_401 expired, What to do...\n");
- /* Update N(R), and set the poll bit */
- s->txqueue->frame[2] = (uint8_t)((s->next_expected_frame << 1) | 0x01);
- s->last_frame_we_acknowledged = s->next_expected_frame;
- s->solicit_f_bit = TRUE;
- if (++s->retransmissions <= N_400)
+ s->retry_count = 0;
+ switch (s->state)
{
- /* Reschedule timer T401 */
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Retransmitting %d bytes\n", s->txqueue->len);
- lapm_tx_frame(s, s->txqueue->frame, s->txqueue->len);
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Scheduling retransmission (%d)\n", s->retransmissions);
-fprintf(stderr, "Setting T401 d [%p]\n", (void *) s);
- s->t401_timer = span_schedule_event(&s->sched, T_401, t401_expired, s);
- }
- else
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Timeout occured\n");
- s->state = LAPM_RELEASE;
- if (s->status_callback)
- s->status_callback(s->status_callback_user_data, s->state);
- lapm_link_down(s);
- lapm_restart(s);
+ case LAPM_ESTABLISH:
+ case LAPM_RELEASE:
+ s->state = LAPM_IDLE;
+ report_rx_status_change(ss, SIG_STATUS_LINK_DISCONNECTED);
+ break;
+ case LAPM_DATA:
+ lapm_disconnect(ss);
+ break;
}
- /*endif*/
+ return ;
+ }
+ s->retry_count++;
+ if (s->configuring)
+ {
+ transmit_xid(ss, s->cmd_addr);
}
else
{
- span_log(&s->logging, SPAN_LOG_FLOW, "Timer T_401 expired. Nothing to send...\n");
+ switch (s->state)
+ {
+ case LAPM_ESTABLISH:
+ tx_unnumbered_frame(s, s->cmd_addr, LAPM_U_SABME | LAPM_U_PF, NULL, 0);
+ break;
+ case LAPM_RELEASE:
+ tx_unnumbered_frame(s, s->cmd_addr, LAPM_U_DISC | LAPM_U_PF, NULL, 0);
+ break;
+ case LAPM_DATA:
+ tx_supervisory_frame(s, s->cmd_addr, (s->local_busy) ? LAPM_S_RNR : LAPM_S_RR, 1);
+ break;
+ }
}
- /*endif*/
+ ss->bit_timer = ms_to_bits(ss, T_401);
+ ss->bit_timer_func = t401_expired;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(const char *) lapm_status_to_str(int status)
+static __inline__ void t401_start(v42_state_t *s)
{
- switch (status)
- {
- case LAPM_DETECT:
- return "LAPM_DETECT";
- case LAPM_ESTABLISH:
- return "LAPM_ESTABLISH";
- case LAPM_DATA:
- return "LAPM_DATA";
- case LAPM_RELEASE:
- return "LAPM_RELEASE";
- case LAPM_SIGNAL:
- return "LAPM_SIGNAL";
- case LAPM_SETPARM:
- return "LAPM_SETPARM";
- case LAPM_TEST:
- return "LAPM_TEST";
- case LAPM_UNSUPPORTED:
- return "LAPM_UNSUPPORTED";
- }
- /*endswitch*/
- return "???";
+ s->bit_timer = ms_to_bits(s, T_401);
+ s->bit_timer_func = t401_expired;
+ s->lapm.retry_count = 0;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) lapm_tx(lapm_state_t *s, const void *buf, int len)
+static __inline__ void t401_stop(v42_state_t *s)
{
- return queue_write(s->tx_queue, buf, len);
+ s->bit_timer = 0;
+ s->lapm.retry_count = 0;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) lapm_release(lapm_state_t *s)
+static void t403_expired(v42_state_t *ss)
{
- s->state = LAPM_RELEASE;
- return 0;
+ lapm_state_t *s;
+
+ span_log(&ss->logging, SPAN_LOG_FLOW, "T.403 expired\n");
+ if (ss->lapm.state != LAPM_DATA)
+ return;
+ s = &ss->lapm;
+ tx_supervisory_frame(s, s->cmd_addr, (ss->lapm.local_busy) ? LAPM_S_RNR : LAPM_S_RR, 1);
+ t401_start(ss);
+ ss->lapm.retry_count = 1;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) lapm_loopback(lapm_state_t *s, int enable)
+static __inline__ void t401_stop_t403_start(v42_state_t *s)
{
- s->state = LAPM_TEST;
- return 0;
+ s->bit_timer = ms_to_bits(s, T_403);
+ s->bit_timer_func = t403_expired;
+ s->lapm.retry_count = 0;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) lapm_break(lapm_state_t *s, int enable)
+static void initiate_negotiation_expired(v42_state_t *s)
{
- s->state = LAPM_SIGNAL;
- return 0;
+ /* Timer service routine */
+ span_log(&s->logging, SPAN_LOG_FLOW, "Start negotiation\n");
+ lapm_config(s);
+ lapm_hdlc_underflow(s);
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) lapm_tx_iframe(lapm_state_t *s, const void *buf, int len, int cr)
+static int tx_information_frame(v42_state_t *ss)
{
- lapm_frame_queue_t *f;
-
- if ((f = malloc(sizeof(*f) + len + 4)) == NULL)
+ lapm_state_t *s;
+ v42_frame_t *f;
+ uint8_t *buf;
+ int n;
+ int info_put_next;
+
+ s = &ss->lapm;
+ if (s->far_busy || ((s->vs - s->va) & 0x7F) >= s->tx_window_size_k)
+ return FALSE;
+ if (s->info_get != s->info_put)
+ return TRUE;
+ if ((info_put_next = s->info_put + 1) >= V42_INFO_FRAMES)
+ info_put_next = 0;
+ if (info_put_next == s->info_get || info_put_next == s->info_acked)
+ return FALSE;
+ f = &s->info_buf[s->info_put];
+ buf = f->buf;
+ if (s->iframe_get == NULL)
+ return FALSE;
+ n = s->iframe_get(s->iframe_get_user_data, buf + 3, s->tx_n401);
+ if (n < 0)
{
- span_log(&s->logging, SPAN_LOG_FLOW, "Out of memory\n");
- return -1;
+ /* Error */
+ report_rx_status_change(ss, SIG_STATUS_LINK_ERROR);
+ return FALSE;
}
- /*endif*/
+ if (n == 0)
+ return FALSE;
- lapm_init_header(f->frame, (s->peer_is_originator) ? cr : !cr);
- f->next = NULL;
- f->len = len + 4;
- f->frame[1] = (uint8_t) (s->next_tx_frame << 1);
- f->frame[2] = (uint8_t) (s->next_expected_frame << 1);
- memcpy(f->frame + 3, buf, len);
- s->next_tx_frame = (s->next_tx_frame + 1) & 0x7F;
- s->last_frame_we_acknowledged = s->next_expected_frame;
- /* Clear poll bit */
- f->frame[2] &= ~0x01;
- if (s->tx_last)
- s->tx_last->next = f;
- else
- s->txqueue = f;
- /*endif*/
- s->tx_last = f;
- /* Immediately transmit unless we're in a recovery state */
- if (s->retransmissions == 0)
- lapm_tx_frame(s, f->frame, f->len);
- /*endif*/
- if (s->t403_timer >= 0)
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "Stopping T_403 timer\n");
-fprintf(stderr, "Deleting T403 c %d\n", s->t403_timer);
- span_schedule_del(&s->sched, s->t403_timer);
- s->t403_timer = -1;
- }
- /*endif*/
- if (s->t401_timer < 0)
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "Starting timer T_401\n");
- s->t401_timer = span_schedule_event(&s->sched, T_401, t401_expired, s);
-fprintf(stderr, "Setting T401 e %d [%p]\n", s->t401_timer, (void *) s);
- }
- else
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "Timer T_401 already running (%d)\n", s->t401_timer);
- }
- /*endif*/
- return 0;
+ f->len = n + 3;
+ s->info_put = info_put_next;
+ return TRUE;
}
/*- End of function --------------------------------------------------------*/
-static void t403_expired(span_sched_state_t *ss, void *user_data)
+static void tx_information_rr_rnr_response(v42_state_t *ss, const uint8_t *frame, int len)
{
lapm_state_t *s;
- s = (lapm_state_t *) user_data;
- span_log(&s->logging, SPAN_LOG_FLOW, "Timer T_403 expired. Sending RR and scheduling T_403 again\n");
- s->t403_timer = -1;
- s->retransmissions = 0;
- /* Solicit an F-bit in the other end's RR */
- s->solicit_f_bit = TRUE;
- lapm_rr(s, 1);
- /* Restart ourselves */
-fprintf(stderr, "Setting T403 f\n");
- s->t401_timer = span_schedule_event(&s->sched, T_401, t401_expired, s);
+ s = &ss->lapm;
+ /* Respond with information frame, RR, or RNR, as appropriate */
+ /* p = 1 may be used for status checking */
+ if ((frame[2] & 0x1) || !tx_information_frame(ss))
+ tx_supervisory_frame(s, frame[0], (s->local_busy) ? LAPM_S_RNR : LAPM_S_RR, 1);
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) lapm_dump(lapm_state_t *s, const uint8_t *frame, int len, int showraw, int txrx)
+static int reject_info(lapm_state_t *s)
{
- const char *type;
- char direction_tag[2];
-
- direction_tag[0] = txrx ? '>' : '<';
- direction_tag[1] = '\0';
- if (showraw)
- span_log_buf(&s->logging, SPAN_LOG_FLOW, direction_tag, frame, len);
- /*endif*/
-
- switch ((frame[1] & LAPM_FRAMETYPE_MASK))
- {
- case LAPM_FRAMETYPE_I:
- case LAPM_FRAMETYPE_I_ALT:
- span_log(&s->logging, SPAN_LOG_FLOW, "%c Information frame:\n", direction_tag[0]);
- break;
- case LAPM_FRAMETYPE_S:
- span_log(&s->logging, SPAN_LOG_FLOW, "%c Supervisory frame:\n", direction_tag[0]);
- break;
- case LAPM_FRAMETYPE_U:
- span_log(&s->logging, SPAN_LOG_FLOW, "%c Unnumbered frame:\n", direction_tag[0]);
- break;
- }
- /*endswitch*/
-
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c DLCI: %2d C/R: %d EA: %d\n",
- direction_tag[0],
- (frame[0] >> 2),
- (frame[0] & 0x02) ? 1 : 0,
- (frame[0] & 0x01),
- direction_tag[0]);
- switch ((frame[1] & LAPM_FRAMETYPE_MASK))
- {
- case LAPM_FRAMETYPE_I:
- case LAPM_FRAMETYPE_I_ALT:
- /* Information frame */
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c N(S): %03d\n",
- direction_tag[0],
- (frame[1] >> 1));
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c N(R): %03d P: %d\n",
- direction_tag[0],
- (frame[2] >> 1),
- (frame[2] & 0x01));
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c %d bytes of data\n",
- direction_tag[0],
- len - 4);
- break;
- case LAPM_FRAMETYPE_S:
- /* Supervisory frame */
- switch (frame[1] & 0x0C)
- {
- case 0x00:
- type = "RR (receive ready)";
- break;
- case 0x04:
- type = "RNR (receive not ready)";
- break;
- case 0x08:
- type = "REJ (reject)";
- break;
- case 0x0C:
- type = "SREJ (selective reject)";
- break;
- default:
- type = "???";
- break;
- }
- /*endswitch*/
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c S: %03d [ %s ]\n",
- direction_tag[0],
- frame[1],
- type);
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c N(R): %03d P/F: %d\n",
- direction_tag[0],
- frame[2] >> 1,
- frame[2] & 0x01);
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c %d bytes of data\n",
- direction_tag[0],
- len - 4);
- break;
- case LAPM_FRAMETYPE_U:
- /* Unnumbered frame */
- switch (frame[1] & 0xEC)
- {
- case 0x00:
- type = "UI (unnumbered information)";
- break;
- case 0x0C:
- type = "DM (disconnect mode)";
- break;
- case 0x40:
- type = "DISC (disconnect)";
- break;
- case 0x60:
- type = "UA (unnumbered acknowledgement)";
- break;
- case 0x6C:
- type = "SABME (set asynchronous balanced mode extended)";
- break;
- case 0x84:
- type = "FRMR (frame reject)";
- break;
- case 0xAC:
- type = "XID (exchange identification)";
- break;
- case 0xE0:
- type = "TEST (test)";
- break;
- default:
- type = "???";
- break;
- }
- /*endswitch*/
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c M: %03d [ %s ] P/F: %d\n",
- direction_tag[0],
- frame[1],
- type,
- (frame[1] >> 4) & 1);
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "%c %d bytes of data\n",
- direction_tag[0],
- len - 3);
- break;
- }
- /*endswitch*/
+ uint8_t n;
+
+ /* Reject all non-acked frames */
+ if (s->state != LAPM_DATA)
+ return 0;
+ n = (s->vs - s->va) & 0x7F;
+ s->vs = s->va;
+ s->info_get = s->info_acked;
+ return n;
}
/*- End of function --------------------------------------------------------*/
-static void lapm_link_up(lapm_state_t *s)
+static int ack_info(v42_state_t *ss, uint8_t nr)
{
- uint8_t buf[1024];
- int len;
+ lapm_state_t *s;
+ int n;
- lapm_reset(s);
- /* Go into connection established state */
- s->state = LAPM_DATA;
- if (s->status_callback)
- s->status_callback(s->status_callback_user_data, s->state);
- /*endif*/
- if (!queue_empty(s->tx_queue))
+ s = &ss->lapm;
+ /* Check that NR is valid - i.e. VA <= NR <= VS && VS-VA <= k */
+ if (!((((nr - s->va) & 0x7F) + ((s->vs - nr) & 0x7F)) <= s->tx_window_size_k
+ &&
+ ((s->vs - s->va) & 0x7F) <= s->tx_window_size_k))
{
- if ((len = queue_read(s->tx_queue, buf, s->n401)) > 0)
- lapm_tx_iframe(s, buf, len, TRUE);
- /*endif*/
+ lapm_disconnect(ss);
+ return -1;
}
- /*endif*/
- if (s->t401_timer >= 0)
+ n = 0;
+ while (s->va != nr && s->info_acked != s->info_get)
{
-fprintf(stderr, "Deleting T401 x [%p] %d\n", (void *) s, s->t401_timer);
- span_schedule_del(&s->sched, s->t401_timer);
- s->t401_timer = -1;
+ if (++s->info_acked >= V42_INFO_FRAMES)
+ s->info_acked = 0;
+ s->va = (s->va + 1) & 0x7F;
+ n++;
}
- /*endif*/
- /* Start the T403 timer */
-fprintf(stderr, "Setting T403 g\n");
- s->t403_timer = span_schedule_event(&s->sched, T_403, t403_expired, s);
+ if (n > 0 && s->retry_count == 0)
+ {
+ t401_stop_t403_start(ss);
+ /* 8.4.8 */
+ if (((s->vs - s->va) & 0x7F))
+ t401_start(ss);
+ }
+ return n;
}
/*- End of function --------------------------------------------------------*/
-static void lapm_link_down(lapm_state_t *s)
+static int valid_data_state(v42_state_t *ss)
{
- lapm_reset(s);
+ lapm_state_t *s;
- if (s->status_callback)
- s->status_callback(s->status_callback_user_data, s->state);
- /*endif*/
+ s = &ss->lapm;
+ switch (s->state)
+ {
+ case LAPM_DETECT:
+ case LAPM_IDLE:
+ break;
+ case LAPM_ESTABLISH:
+ reset_lapm(ss);
+ s->state = LAPM_DATA;
+ report_rx_status_change(ss, SIG_STATUS_LINK_CONNECTED);
+ return 1;
+ case LAPM_DATA:
+ return 1;
+ case LAPM_RELEASE:
+ reset_lapm(ss);
+ s->state = LAPM_IDLE;
+ report_rx_status_change(ss, SIG_STATUS_LINK_DISCONNECTED);
+ break;
+ case LAPM_SIGNAL:
+ case LAPM_SETPARM:
+ case LAPM_TEST:
+ case LAPM_V42_UNSUPPORTED:
+ break;
+ }
+ return 0;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) lapm_reset(lapm_state_t *s)
+static void receive_information_frame(v42_state_t *ss, const uint8_t *frame, int len)
{
- lapm_frame_queue_t *f;
- lapm_frame_queue_t *p;
-
- /* Having received a SABME, we need to reset our entire state */
- s->next_tx_frame = 0;
- s->last_frame_peer_acknowledged = 0;
- s->next_expected_frame = 0;
- s->last_frame_we_acknowledged = 0;
- s->window_size_k = 15;
- s->n401 = 128;
- if (s->t401_timer >= 0)
- {
-fprintf(stderr, "Deleting T401 d [%p] %d\n", (void *) s, s->t401_timer);
- span_schedule_del(&s->sched, s->t401_timer);
- s->t401_timer = -1;
- }
- /*endif*/
- if (s->t403_timer >= 0)
+ lapm_state_t *s;
+ int ret;
+ int n;
+
+ s = &ss->lapm;
+ if (!valid_data_state(ss))
+ return;
+ if (len > s->rx_n401 + 3)
+ return;
+ ret = 0;
+ /* Ack I frames: NR - 1 */
+ n = ack_info(ss, frame[2] >> 1);
+ if (s->local_busy)
{
-fprintf(stderr, "Deleting T403 e %d\n", s->t403_timer);
- span_schedule_del(&s->sched, s->t403_timer);
- s->t403_timer = -1;
+ /* 8.4.7 */
+ if ((frame[2] & 0x1))
+ tx_supervisory_frame(s, s->rsp_addr, LAPM_S_RNR, 1);
+ return;
}
- /*endif*/
- s->busy = FALSE;
- s->solicit_f_bit = FALSE;
- s->state = LAPM_RELEASE;
- s->retransmissions = 0;
- /* Discard anything waiting to go out */
- for (f = s->txqueue; f; )
+ /* NS sequence error */
+ if ((frame[1] >> 1) != s->vr)
{
- p = f;
- f = f->next;
- free(p);
+ if (!s->rejected)
+ {
+ tx_supervisory_frame(s, s->rsp_addr, LAPM_S_REJ, (frame[2] & 0x1));
+ s->rejected = TRUE;
+ }
+ return;
}
- /*endfor*/
- s->txqueue = NULL;
+ s->rejected = FALSE;
+
+ s->iframe_put(s->iframe_put_user_data, frame + 3, len - 3);
+ /* Increment vr */
+ s->vr = (s->vr + 1) & 0x7F;
+ tx_information_rr_rnr_response(ss, frame, len);
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE_NONSTD(void) lapm_receive(void *user_data, const uint8_t *frame, int len, int ok)
+static void rx_supervisory_cmd_frame(v42_state_t *ss, const uint8_t *frame, int len)
{
lapm_state_t *s;
- lapm_frame_queue_t *f;
- int sendnow;
- int octet;
- int s_field;
- int m_field;
-
-fprintf(stderr, "LAPM receive %d %d\n", ok, len);
- if (!ok || len == 0)
- return;
- /*endif*/
- s = (lapm_state_t *) user_data;
- if (len < 0)
+ int n;
+
+ s = &ss->lapm;
+ /* If l->local_busy each RR,RNR,REJ with p=1 should be replied by RNR with f=1 (8.4.7) */
+ switch (frame[1] & 0x0C)
{
- /* Special conditions */
- span_log(&s->logging, SPAN_LOG_DEBUG, "V.42 rx status is %s (%d)\n", signal_status_to_str(len), len);
+ case LAPM_S_RR:
+ s->far_busy = FALSE;
+ n = ack_info(ss, frame[2] >> 1);
+ /* If p = 1 may be used for status checking? */
+ tx_information_rr_rnr_response(ss, frame, len);
+ break;
+ case LAPM_S_RNR:
+ s->far_busy = TRUE;
+ n = ack_info(ss, frame[2] >> 1);
+ /* If p = 1 may be used for status checking? */
+ if ((frame[2] & 0x1))
+ tx_supervisory_frame(s, s->rsp_addr, (s->local_busy) ? LAPM_S_RNR : LAPM_S_RR, 1);
+ break;
+ case LAPM_S_REJ:
+ s->far_busy = FALSE;
+ n = ack_info(ss, frame[2] >> 1);
+ if (s->retry_count == 0)
+ {
+ t401_stop_t403_start(ss);
+ reject_info(s);
+ }
+ tx_information_rr_rnr_response(ss, frame, len);
+ break;
+ case LAPM_S_SREJ:
+ /* TODO: */
+ return;
+ default:
return;
}
- /*endif*/
+}
+/*- End of function --------------------------------------------------------*/
- if ((s->debug & LAPM_DEBUG_LAPM_DUMP))
- lapm_dump(s, frame, len, s->debug & LAPM_DEBUG_LAPM_RAW, FALSE);
- /*endif*/
- octet = 0;
- /* We do not expect extended addresses */
- if ((frame[octet] & 0x01) == 0)
- return;
- /*endif*/
- /* Check for DLCIs we do not recognise */
- if ((frame[octet] >> 2) != LAPM_DLCI_DTE_TO_DTE)
+static void rx_supervisory_rsp_frame(v42_state_t *ss, const uint8_t *frame, int len)
+{
+ lapm_state_t *s;
+ int n;
+
+ s = &ss->lapm;
+ if (s->retry_count == 0 && (frame[2] & 0x1))
return;
- /*endif*/
- octet++;
- switch (frame[octet] & LAPM_FRAMETYPE_MASK)
+ /* Ack I frames <= NR - 1 */
+ switch (frame[1] & 0x0C)
{
- case LAPM_FRAMETYPE_I:
- case LAPM_FRAMETYPE_I_ALT:
- if (s->state != LAPM_DATA)
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "!! Got an I-frame while link state is %d\n", s->state);
- break;
- }
- /*endif*/
- /* Information frame */
- if (len < 4)
+ case LAPM_S_RR:
+ s->far_busy = FALSE;
+ n = ack_info(ss, frame[2] >> 1);
+ if (s->retry_count && (frame[2] & 0x1))
{
- span_log(&s->logging, SPAN_LOG_FLOW, "!! Received short I-frame (expected 4, got %d)\n", len);
- break;
+ reject_info(s);
+ t401_stop_t403_start(ss);
}
- /*endif*/
- /* Make sure this is a valid packet */
- if ((frame[1] >> 1) == s->next_expected_frame)
+ break;
+ case LAPM_S_RNR:
+ s->far_busy = TRUE;
+ n = ack_info(ss, frame[2] >> 1);
+ if (s->retry_count && (frame[2] & 0x1))
{
- /* Increment next expected I-frame */
- s->next_expected_frame = (s->next_expected_frame + 1) & 0x7F;
- /* Handle their ACK */
- lapm_ack_rx(s, frame[2] >> 1);
- if ((frame[2] & 0x01))
- {
- /* If the Poll/Final bit is set, send the RR immediately */
- lapm_rr(s, 1);
- }
- /*endif*/
- s->iframe_receive(s->iframe_receive_user_data, frame + 3, len - 4);
- /* Send an RR if one wasn't sent already */
- if (s->last_frame_we_acknowledged != s->next_expected_frame)
- lapm_rr(s, 0);
- /*endif*/
+ reject_info(s);
+ t401_stop_t403_start(ss);
}
- else
+ if (s->retry_count == 0)
+ t401_start(ss);
+ break;
+ case LAPM_S_REJ:
+ s->far_busy = FALSE;
+ n = ack_info(ss, frame[2] >> 1);
+ if (s->retry_count == 0 || (frame[2] & 0x1))
{
- if (((s->next_expected_frame - (frame[1] >> 1)) & 127) < s->window_size_k)
- {
- /* It's within our window -- send back an RR */
- lapm_rr(s, 0);
- }
- else
- {
- lapm_reject(s);
- }
- /*endif*/
+ reject_info(s);
+ t401_stop_t403_start(ss);
}
- /*endif*/
break;
- case LAPM_FRAMETYPE_S:
- if (s->state != LAPM_DATA)
+ case LAPM_S_SREJ:
+ /* TODO: */
+ return;
+ default:
+ return;
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int rx_unnumbered_cmd_frame(v42_state_t *ss, const uint8_t *frame, int len)
+{
+ lapm_state_t *s;
+
+ s = &ss->lapm;
+ switch (frame[1] & 0xEC)
+ {
+ case LAPM_U_SABME:
+ /* Discard un-acked I frames. Reset vs, vr, and va. Clear exceptions */
+ reset_lapm(ss);
+ /* Going to connected state */
+ s->state = LAPM_DATA;
+ /* Respond UA (or DM on error) */
+ // fixme: why may be error and LAPM_U_DM ??
+ tx_unnumbered_frame(s, s->rsp_addr, LAPM_U_UA | (frame[1] & 0x10), NULL, 0);
+ t401_stop_t403_start(ss);
+ report_rx_status_change(ss, SIG_STATUS_LINK_CONNECTED);
+ break;
+ case LAPM_U_UI:
+ /* Break signal */
+ /* TODO: */
+ break;
+ case LAPM_U_DISC:
+ /* Respond UA (or DM) */
+ if (s->state == LAPM_IDLE)
{
- span_log(&s->logging, SPAN_LOG_FLOW, "!! Got S-frame while link down\n");
- break;
+ tx_unnumbered_frame(s, s->rsp_addr, LAPM_U_DM | LAPM_U_PF, NULL, 0);
}
- /*endif*/
- if (len < 4)
+ else
{
- span_log(&s->logging, SPAN_LOG_FLOW, "!! Received short S-frame (expected 4, got %d)\n", len);
- break;
+ /* Going to disconnected state, discard unacked I frames, reset all. */
+ s->state = LAPM_IDLE;
+ reset_lapm(ss);
+ tx_unnumbered_frame(s, s->rsp_addr, LAPM_U_UA | (frame[1] & 0x10), NULL, 0);
+ t401_stop(ss);
+ /* TODO: notify CF */
+ report_rx_status_change(ss, SIG_STATUS_LINK_DISCONNECTED);
}
- /*endif*/
- s_field = frame[octet] & 0xEC;
- switch (s_field)
+ break;
+ case LAPM_U_XID:
+ /* Exchange general ID info */
+ receive_xid(ss, frame, len);
+ transmit_xid(ss, s->rsp_addr);
+ break;
+ case LAPM_U_TEST:
+ /* TODO: */
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int rx_unnumbered_rsp_frame(v42_state_t *ss, const uint8_t *frame, int len)
+{
+ lapm_state_t *s;
+
+ s = &ss->lapm;
+ switch (frame[1] & 0xEC)
+ {
+ case LAPM_U_DM:
+ switch (s->state)
{
- case 0x00:
- /* RR (receive ready) */
- s->busy = FALSE;
- /* Acknowledge frames as necessary */
- lapm_ack_rx(s, frame[2] >> 1);
- if ((frame[2] & 0x01))
+ case LAPM_IDLE:
+ if (!(frame[1] & 0x10))
{
- /* If P/F is one, respond with an RR with the P/F bit set */
- if (s->solicit_f_bit)
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Got RR response to our frame\n");
- }
- else
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Unsolicited RR with P/F bit, responding\n");
- lapm_rr(s, 1);
- }
- /*endif*/
- s->solicit_f_bit = FALSE;
+ /* TODO: notify CF */
+ report_rx_status_change(ss, SIG_STATUS_LINK_CONNECTED);
}
- /*endif*/
break;
- case 0x04:
- /* RNR (receive not ready) */
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Got receiver not ready\n");
- s->busy = TRUE;
- break;
- case 0x08:
- /* REJ (reject) */
- /* Just retransmit */
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Got reject requesting packet %d... Retransmitting.\n", frame[2] >> 1);
- if ((frame[2] & 0x01))
+ case LAPM_ESTABLISH:
+ case LAPM_RELEASE:
+ if ((frame[1] & 0x10))
{
- /* If it has the poll bit set, send an appropriate supervisory response */
- lapm_rr(s, 1);
+ s->state = LAPM_IDLE;
+ reset_lapm(ss);
+ t401_stop(ss);
+ /* TODO: notify CF */
+ report_rx_status_change(ss, SIG_STATUS_LINK_DISCONNECTED);
}
- /*endif*/
- sendnow = FALSE;
- /* Resend the appropriate I-frame */
- for (f = s->txqueue; f; f = f->next)
- {
- if (sendnow || (f->frame[1] >> 1) == (frame[2] >> 1))
- {
- /* Matches the request, or follows in our window */
- sendnow = TRUE;
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "!! Got reject for frame %d, retransmitting frame %d now, updating n_r!\n",
- frame[2] >> 1,
- f->frame[1] >> 1);
- f->frame[2] = (uint8_t) (s->next_expected_frame << 1);
- lapm_tx_frame(s, f->frame, f->len);
- }
- /*endif*/
- }
- /*endfor*/
- if (!sendnow)
+ break;
+ case LAPM_DATA:
+ if (s->retry_count || !(frame[1] & 0x10))
{
- if (s->txqueue)
- {
- /* This should never happen */
- if ((frame[2] & 0x01) == 0 || (frame[2] >> 1))
- {
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "!! Got reject for frame %d, but we only have others!\n",
- frame[2] >> 1);
- }
- /*endif*/
- }
- else
- {
- /* Hrm, we have nothing to send, but have been REJ'd. Reset last_frame_peer_acknowledged, next_tx_frame, etc */
- span_log(&s->logging, SPAN_LOG_FLOW, "!! Got reject for frame %d, but we have nothing -- resetting!\n", frame[2] >> 1);
- s->last_frame_peer_acknowledged =
- s->next_tx_frame = frame[2] >> 1;
- /* Reset t401 timer if it was somehow going */
- if (s->t401_timer >= 0)
- {
-fprintf(stderr, "Deleting T401 f [%p] %d\n", (void *) s, s->t401_timer);
- span_schedule_del(&s->sched, s->t401_timer);
- s->t401_timer = -1;
- }
- /*endif*/
- /* Reset and restart t403 timer */
- if (s->t403_timer >= 0)
- {
-fprintf(stderr, "Deleting T403 g %d\n", s->t403_timer);
- span_schedule_del(&s->sched, s->t403_timer);
- s->t403_timer = -1;
- }
- /*endif*/
-fprintf(stderr, "Setting T403 h\n");
- s->t403_timer = span_schedule_event(&s->sched, T_403, t403_expired, s);
- }
- /*endif*/
+ s->state = LAPM_IDLE;
+ reset_lapm(ss);
+ /* TODO: notify CF */
+ report_rx_status_change(ss, SIG_STATUS_LINK_DISCONNECTED);
}
- /*endif*/
- break;
- case 0x0C:
- /* SREJ (selective reject) */
break;
default:
- span_log(&s->logging,
- SPAN_LOG_FLOW,
- "!! XXX Unknown Supervisory frame sd=0x%02x,pf=%02xnr=%02x vs=%02x, va=%02x XXX\n",
- s_field,
- frame[2] & 0x01,
- frame[2] >> 1,
- s->next_tx_frame,
- s->last_frame_peer_acknowledged);
break;
}
- /*endswitch*/
break;
- case LAPM_FRAMETYPE_U:
- if (len < 3)
+ case LAPM_U_UI:
+ /* TODO: */
+ break;
+ case LAPM_U_UA:
+ switch (s->state)
{
- span_log(&s->logging, SPAN_LOG_FLOW, "!! Received too short unnumbered frame\n");
+ case LAPM_ESTABLISH:
+ s->state = LAPM_DATA;
+ reset_lapm(ss);
+ t401_stop_t403_start(ss);
+ report_rx_status_change(ss, SIG_STATUS_LINK_CONNECTED);
+ break;
+ case LAPM_RELEASE:
+ s->state = LAPM_IDLE;
+ reset_lapm(ss);
+ t401_stop(ss);
+ report_rx_status_change(ss, SIG_STATUS_LINK_DISCONNECTED);
+ break;
+ default:
+ /* Unsolicited UA */
+ /* TODO: */
break;
}
- /*endif*/
- m_field = frame[octet] & 0xEC;
- switch (m_field)
+ /* Clear all exceptions, busy states (self and peer) */
+ /* Reset vars */
+ break;
+ case LAPM_U_FRMR:
+ /* Non-recoverable error */
+ /* TODO: */
+ break;
+ case LAPM_U_XID:
+ if (s->configuring)
{
- case 0x00:
- /* UI (unnumbered information) */
- switch (frame[++octet] & 0x7F)
+ receive_xid(ss, frame, len);
+ s->configuring = FALSE;
+ t401_stop(ss);
+ switch (s->state)
{
- case 0x40:
- /* BRK */
- span_log(&s->logging, SPAN_LOG_FLOW, "BRK - option %d, length %d\n", (frame[octet] >> 5), frame[octet + 1]);
- octet += 2;
- break;
- case 0x60:
- /* BRKACK */
- span_log(&s->logging, SPAN_LOG_FLOW, "BRKACK\n");
+ case LAPM_IDLE:
+ lapm_connect(ss);
break;
- default:
- /* Unknown */
- span_log(&s->logging, SPAN_LOG_FLOW, "Unknown UI type\n");
+ case LAPM_DATA:
+ s->local_busy = FALSE;
+ tx_supervisory_frame(s, s->cmd_addr, LAPM_S_RR, 0);
break;
}
- /*endswitch*/
- break;
- case 0x0C:
- /* DM (disconnect mode) */
- if ((frame[octet] & 0x10))
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Got Unconnected Mode from peer.\n");
- /* Disconnected mode, try again */
- lapm_link_down(s);
- lapm_restart(s);
- }
- else
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "-- DM (disconnect mode) requesting SABME, starting.\n");
- /* Requesting that we start */
- lapm_restart(s);
- }
- /*endif*/
- break;
- case 0x40:
- /* DISC (disconnect) */
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Got DISC (disconnect) from peer.\n");
- /* Acknowledge */
- lapm_send_ua(s, (frame[octet] & 0x10));
- lapm_link_down(s);
- break;
- case 0x60:
- /* UA (unnumbered acknowledgement) */
- if (s->state == LAPM_ESTABLISH)
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Got UA (unnumbered acknowledgement) from %s peer. Link up.\n", (frame[0] & 0x02) ? "xxx" : "yyy");
- lapm_link_up(s);
- }
- else
- {
- span_log(&s->logging, SPAN_LOG_FLOW, "!! Got a UA (unnumbered acknowledgement) in state %d\n", s->state);
- }
- /*endif*/
- break;
- case 0x6C:
- /* SABME (set asynchronous balanced mode extended) */
- span_log(&s->logging, SPAN_LOG_FLOW, "-- Got SABME (set asynchronous balanced mode extended) from %s peer.\n", (frame[0] & 0x02) ? "yyy" : "xxx");
- if ((frame[0] & 0x02))
- {
- s->peer_is_originator = TRUE;
- if (s->we_are_originator)
- {
- /* We can't both be originators */
- span_log(&s->logging, SPAN_LOG_FLOW, "We think we are the originator, but they think so too.");
- break;
- }
- /*endif*/
- }
- else
- {
- s->peer_is_originator = FALSE;
- if (!s->we_are_originator)
- {
- /* We can't both be answerers */
- span_log(&s->logging, SPAN_LOG_FLOW, "We think we are the answerer, but they think so too.\n");
- break;
- }
- /*endif*/
- }
- /*endif*/
- /* Send unnumbered acknowledgement */
- lapm_send_ua(s, (frame[octet] & 0x10));
- lapm_link_up(s);
- break;
- case 0x84:
- /* FRMR (frame reject) */
- span_log(&s->logging, SPAN_LOG_FLOW, "!! FRMR (frame reject).\n");
- break;
- case 0xAC:
- /* XID (exchange identification) */
- span_log(&s->logging, SPAN_LOG_FLOW, "!! XID (exchange identification) frames not supported\n");
- break;
- case 0xE0:
- /* TEST (test) */
- span_log(&s->logging, SPAN_LOG_FLOW, "!! TEST (test) frames not supported\n");
- break;
- default:
- span_log(&s->logging, SPAN_LOG_FLOW, "!! Don't know what to do with M=%X u-frames\n", m_field);
- break;
}
- /*endswitch*/
+ break;
+ default:
break;
}
- /*endswitch*/
+ return 0;
}
/*- End of function --------------------------------------------------------*/
static void lapm_hdlc_underflow(void *user_data)
{
lapm_state_t *s;
- uint8_t buf[1024];
- int len;
+ v42_state_t *ss;
+ v42_frame_t *f;
- s = (lapm_state_t *) user_data;
- span_log(&s->logging, SPAN_LOG_FLOW, "HDLC underflow\n");
- if (s->state == LAPM_DATA)
+ ss = (v42_state_t *) user_data;
+ s = &ss->lapm;
+ if (s->ctrl_get != s->ctrl_put)
+ {
+ /* Send control frame */
+ f = &s->ctrl_buf[s->ctrl_get];
+ if (++s->ctrl_get >= V42_CTRL_FRAMES)
+ s->ctrl_get = 0;
+ }
+ else
{
- if (!queue_empty(s->tx_queue))
+ if (s->far_busy || s->configuring || s->state != LAPM_DATA)
{
- if ((len = queue_read(s->tx_queue, buf, s->n401)) > 0)
- lapm_tx_iframe(s, buf, len, TRUE);
- /*endif*/
+ hdlc_tx_flags(&s->hdlc_tx, 10);
+ return;
}
- /*endif*/
+ if (s->info_get == s->info_put && !tx_information_frame(ss))
+ {
+ hdlc_tx_flags(&s->hdlc_tx, 10);
+ return;
+ }
+ /* Send info frame */
+ f = &s->info_buf[s->info_get];
+ if (++s->info_get >= V42_INFO_FRAMES)
+ s->info_get = 0;
+
+ f->buf[0] = s->cmd_addr;
+ f->buf[1] = s->vs << 1;
+ f->buf[2] = s->vr << 1;
+ s->vs = (s->vs + 1) & 0x7F;
+ if (ss->bit_timer == 0)
+ t401_start(ss);
}
- /*endif*/
+ hdlc_tx_frame(&s->hdlc_tx, f->buf, f->len);
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) lapm_restart(lapm_state_t *s)
+SPAN_DECLARE_NONSTD(void) lapm_receive(void *user_data, const uint8_t *frame, int len, int ok)
{
-#if 0
- if (s->state != LAPM_RELEASE)
+ lapm_state_t *s;
+ v42_state_t *ss;
+
+ ss = (v42_state_t *) user_data;
+ s = &ss->lapm;
+ if (len < 0)
{
- span_log(&s->logging, SPAN_LOG_FLOW, "!! lapm_restart: Not in 'Link Connection Released' state\n");
+ span_log(&ss->logging, SPAN_LOG_DEBUG, "V.42 rx status is %s (%d)\n", signal_status_to_str(len), len);
return;
}
- /*endif*/
-#endif
- span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
- span_log_set_protocol(&s->logging, "LAP.M");
- hdlc_tx_init(&s->hdlc_tx, FALSE, 1, TRUE, lapm_hdlc_underflow, s);
- hdlc_rx_init(&s->hdlc_rx, FALSE, FALSE, 1, lapm_receive, s);
- /* TODO: This is a bodge! */
- s->t401_timer = -1;
- s->t402_timer = -1;
- s->t403_timer = -1;
- lapm_reset(s);
- /* TODO: Maybe we should implement T_WAIT? */
- lapm_send_sabme(NULL, s);
+ if (!ok)
+ return;
+
+ switch ((frame[1] & LAPM_FRAMETYPE_MASK))
+ {
+ case LAPM_FRAMETYPE_I:
+ case LAPM_FRAMETYPE_I_ALT:
+ receive_information_frame(ss, frame, len);
+ break;
+ case LAPM_FRAMETYPE_S:
+ if (!valid_data_state(ss))
+ return;
+ if (frame[0] == s->rsp_addr)
+ rx_supervisory_cmd_frame(ss, frame, len);
+ else
+ rx_supervisory_rsp_frame(ss, frame, len);
+ break;
+ case LAPM_FRAMETYPE_U:
+ if (frame[0] == s->rsp_addr)
+ rx_unnumbered_cmd_frame(ss, frame, len);
+ else
+ rx_unnumbered_rsp_frame(ss, frame, len);
+ break;
+ default:
+ break;
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static int lapm_connect(v42_state_t *ss)
+{
+ lapm_state_t *s;
+
+ s = &ss->lapm;
+ if (s->state != LAPM_IDLE)
+ return -1;
+
+ /* Negotiate params */
+ //transmit_xid(s, s->cmd_addr);
+
+ reset_lapm(ss);
+ /* Connect */
+ s->state = LAPM_ESTABLISH;
+ tx_unnumbered_frame(s, s->cmd_addr, LAPM_U_SABME | LAPM_U_PF, NULL, 0);
+ /* Start T401 (and not T403) */
+ t401_start(ss);
+ return 0;
}
/*- End of function --------------------------------------------------------*/
-#if 0
-static void lapm_init(lapm_state_t *s)
+static int lapm_disconnect(v42_state_t *ss)
{
- lapm_restart(s);
+ lapm_state_t *s;
+
+ s = &ss->lapm;
+ s->state = LAPM_RELEASE;
+ tx_unnumbered_frame(s, s->cmd_addr, LAPM_U_DISC | LAPM_U_PF, NULL, 0);
+ t401_start(ss);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int lapm_config(v42_state_t *ss)
+{
+ lapm_state_t *s;
+
+ s = &ss->lapm;
+ s->configuring = TRUE;
+ if (s->state == LAPM_DATA)
+ {
+ s->local_busy = TRUE;
+ tx_supervisory_frame(s, s->cmd_addr, LAPM_S_RNR, 1);
+ }
+ transmit_xid(ss, s->cmd_addr);
+ t401_start(ss);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void reset_lapm(v42_state_t *ss)
+{
+ lapm_state_t *s;
+
+ s = &ss->lapm;
+ /* Reset the LAP.M state */
+ s->local_busy = FALSE;
+ s->far_busy = FALSE;
+ s->vs = 0;
+ s->va = 0;
+ s->vr = 0;
+ /* Discard any info frames still queued for transmission */
+ s->info_put = 0;
+ s->info_acked = 0;
+ s->info_get = 0;
+ /* Discard any control frames */
+ s->ctrl_put = 0;
+ s->ctrl_get = 0;
+
+ s->tx_window_size_k = ss->config.v42_tx_window_size_k;
+ s->rx_window_size_k = ss->config.v42_rx_window_size_k;
+ s->tx_n401 = ss->config.v42_tx_n401;
+ s->rx_n401 = ss->config.v42_rx_n401;
+}
+/*- End of function --------------------------------------------------------*/
+
+void v42_stop(v42_state_t *ss)
+{
+ lapm_state_t *s;
+
+ s = &ss->lapm;
+ ss->bit_timer = 0;
+ s->packer_process = NULL;
+ lapm_disconnect(ss);
+}
+/*- End of function --------------------------------------------------------*/
+
+static void restart_lapm(v42_state_t *s)
+{
+ if (s->calling_party)
+ {
+ s->bit_timer = 48*8;
+ s->bit_timer_func = initiate_negotiation_expired;
+ }
+ else
+ {
+ lapm_hdlc_underflow(s);
+ }
+ s->lapm.packer_process = NULL;
+ s->lapm.state = LAPM_IDLE;
}
/*- End of function --------------------------------------------------------*/
-#endif
static void negotiation_rx_bit(v42_state_t *s, int new_bit)
{
/* DC1 with even parity, 8-16 ones, DC1 with odd parity, 8-16 ones */
- //uint8_t odp = "0100010001 11111111 0100010011 11111111";
+ /* uint8_t odp = "0100010001 11111111 0100010011 11111111"; */
/* V.42 OK E , 8-16 ones, C, 8-16 ones */
- //uint8_t adp_v42 = "0101000101 11111111 0110000101 11111111";
+ /* uint8_t adp_v42 = "0101000101 11111111 0110000101 11111111"; */
/* V.42 disabled E, 8-16 ones, NULL, 8-16 ones */
- //uint8_t adp_nov42 = "0101000101 11111111 0000000001 11111111";
+ /* uint8_t adp_nov42 = "0101000101 11111111 0000000001 11111111"; */
/* There may be no negotiation, so we need to process this data through the
HDLC receiver as well */
}
/*endif*/
new_bit &= 1;
- s->rxstream = (s->rxstream << 1) | new_bit;
- switch (s->rx_negotiation_step)
+ s->neg.rxstream = (s->neg.rxstream << 1) | new_bit;
+ switch (s->neg.rx_negotiation_step)
{
case 0:
/* Look for some ones */
if (new_bit)
break;
/*endif*/
- s->rx_negotiation_step = 1;
- s->rxbits = 0;
- s->rxstream = ~1;
- s->rxoks = 0;
+ s->neg.rx_negotiation_step = 1;
+ s->neg.rxbits = 0;
+ s->neg.rxstream = ~1;
+ s->neg.rxoks = 0;
break;
case 1:
/* Look for the first character */
- if (++s->rxbits < 9)
+ if (++s->neg.rxbits < 9)
break;
/*endif*/
- s->rxstream &= 0x3FF;
- if (s->calling_party && s->rxstream == 0x145)
+ s->neg.rxstream &= 0x3FF;
+ if (s->calling_party && s->neg.rxstream == 0x145)
{
- s->rx_negotiation_step++;
+ s->neg.rx_negotiation_step++;
}
- else if (!s->calling_party && s->rxstream == 0x111)
+ else if (!s->calling_party && s->neg.rxstream == 0x111)
{
- s->rx_negotiation_step++;
+ s->neg.rx_negotiation_step++;
}
else
{
- s->rx_negotiation_step = 0;
+ s->neg.rx_negotiation_step = 0;
}
/*endif*/
- s->rxbits = 0;
- s->rxstream = ~0;
+ s->neg.rxbits = 0;
+ s->neg.rxstream = ~0;
break;
case 2:
/* Look for 8 to 16 ones */
- s->rxbits++;
+ s->neg.rxbits++;
if (new_bit)
break;
/*endif*/
- if (s->rxbits >= 8 && s->rxbits <= 16)
- s->rx_negotiation_step++;
+ if (s->neg.rxbits >= 8 && s->neg.rxbits <= 16)
+ s->neg.rx_negotiation_step++;
else
- s->rx_negotiation_step = 0;
+ s->neg.rx_negotiation_step = 0;
/*endif*/
- s->rxbits = 0;
- s->rxstream = ~1;
+ s->neg.rxbits = 0;
+ s->neg.rxstream = ~1;
break;
case 3:
/* Look for the second character */
- if (++s->rxbits < 9)
+ if (++s->neg.rxbits < 9)
break;
/*endif*/
- s->rxstream &= 0x3FF;
- if (s->calling_party && s->rxstream == 0x185)
+ s->neg.rxstream &= 0x3FF;
+ if (s->calling_party && s->neg.rxstream == 0x185)
{
- s->rx_negotiation_step++;
+ s->neg.rx_negotiation_step++;
}
- else if (s->calling_party && s->rxstream == 0x001)
+ else if (s->calling_party && s->neg.rxstream == 0x001)
{
- s->rx_negotiation_step++;
+ s->neg.rx_negotiation_step++;
}
- else if (!s->calling_party && s->rxstream == 0x113)
+ else if (!s->calling_party && s->neg.rxstream == 0x113)
{
- s->rx_negotiation_step++;
+ s->neg.rx_negotiation_step++;
}
else
{
- s->rx_negotiation_step = 0;
+ s->neg.rx_negotiation_step = 0;
}
/*endif*/
- s->rxbits = 0;
- s->rxstream = ~0;
+ s->neg.rxbits = 0;
+ s->neg.rxstream = ~0;
break;
case 4:
/* Look for 8 to 16 ones */
- s->rxbits++;
+ s->neg.rxbits++;
if (new_bit)
break;
/*endif*/
- if (s->rxbits >= 8 && s->rxbits <= 16)
+ if (s->neg.rxbits >= 8 && s->neg.rxbits <= 16)
{
- if (++s->rxoks >= 2)
+ if (++s->neg.rxoks >= 2)
{
- /* HIT */
- s->rx_negotiation_step++;
+ /* HIT - we have found the "V.42 supported" pattern. */
+ s->neg.rx_negotiation_step++;
if (s->calling_party)
{
- if (s->t400_timer >= 0)
- {
-fprintf(stderr, "Deleting T400 h %d\n", s->t400_timer);
- span_schedule_del(&s->lapm.sched, s->t400_timer);
- s->t400_timer = -1;
- }
- /*endif*/
- s->lapm.state = LAPM_ESTABLISH;
- if (s->lapm.status_callback)
- s->lapm.status_callback(s->lapm.status_callback_user_data, s->lapm.state);
- /*endif*/
+ t400_stop(s);
+ s->lapm.state = LAPM_IDLE;
+ report_rx_status_change(s, s->lapm.state);
+ restart_lapm(s);
}
else
{
- s->odp_seen = TRUE;
+ s->neg.odp_seen = TRUE;
}
/*endif*/
break;
}
/*endif*/
- s->rx_negotiation_step = 1;
- s->rxbits = 0;
- s->rxstream = ~1;
+ s->neg.rx_negotiation_step = 1;
+ s->neg.rxbits = 0;
+ s->neg.rxstream = ~1;
}
else
{
- s->rx_negotiation_step = 0;
- s->rxbits = 0;
- s->rxstream = ~0;
+ s->neg.rx_negotiation_step = 0;
+ s->neg.rxbits = 0;
+ s->neg.rxstream = ~0;
}
/*endif*/
break;
if (s->calling_party)
{
- if (s->txbits <= 0)
+ if (s->neg.txbits <= 0)
{
- s->txstream = 0x3FE22;
- s->txbits = 36;
+ s->neg.txstream = 0x3FE22;
+ s->neg.txbits = 36;
}
- else if (s->txbits == 18)
+ else if (s->neg.txbits == 18)
{
- s->txstream = 0x3FF22;
+ s->neg.txstream = 0x3FF22;
}
/*endif*/
- bit = s->txstream & 1;
- s->txstream >>= 1;
- s->txbits--;
+ bit = s->neg.txstream & 1;
+ s->neg.txstream >>= 1;
+ s->neg.txbits--;
}
else
{
- if (s->odp_seen && s->txadps < 10)
+ if (s->neg.odp_seen && s->neg.txadps < 10)
{
- if (s->txbits <= 0)
+ if (s->neg.txbits <= 0)
{
- if (++s->txadps >= 10)
+ if (++s->neg.txadps >= 10)
{
- if (s->t400_timer >= 0)
- {
-fprintf(stderr, "Deleting T400 i %d\n", s->t400_timer);
- span_schedule_del(&s->lapm.sched, s->t400_timer);
- s->t400_timer = -1;
- }
- /*endif*/
- s->lapm.state = LAPM_ESTABLISH;
- if (s->lapm.status_callback)
- s->lapm.status_callback(s->lapm.status_callback_user_data, s->lapm.state);
- /*endif*/
- s->txstream = 1;
+ t400_stop(s);
+ s->lapm.state = LAPM_IDLE;
+ report_rx_status_change(s, s->lapm.state);
+ s->neg.txstream = 1;
+ restart_lapm(s);
}
else
{
- s->txstream = 0x3FE8A;
- s->txbits = 36;
+ s->neg.txstream = 0x3FE8A;
+ s->neg.txbits = 36;
}
/*endif*/
}
- else if (s->txbits == 18)
+ else if (s->neg.txbits == 18)
{
- s->txstream = 0x3FE86;
+ s->neg.txstream = 0x3FE86;
}
/*endif*/
- bit = s->txstream & 1;
- s->txstream >>= 1;
- s->txbits--;
+ bit = s->neg.txstream & 1;
+ s->neg.txstream >>= 1;
+ s->neg.txbits--;
}
else
{
int bit;
s = (v42_state_t *) user_data;
+ if (s->bit_timer && (--s->bit_timer) <= 0)
+ {
+ s->bit_timer = 0;
+ s->bit_timer_func(s);
+ }
if (s->lapm.state == LAPM_DETECT)
bit = v42_support_negotiation_tx_bit(s);
else
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(void) v42_set_status_callback(v42_state_t *s, v42_status_func_t callback, void *user_data)
+SPAN_DECLARE(int) v42_set_local_busy_status(v42_state_t *s, int busy)
+{
+ int previous_busy;
+
+ previous_busy = s->lapm.local_busy;
+ s->lapm.local_busy = busy;
+ return previous_busy;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(int) v42_get_far_busy_status(v42_state_t *s)
+{
+ return s->lapm.far_busy;
+}
+/*- End of function --------------------------------------------------------*/
+
+SPAN_DECLARE(void) v42_set_status_callback(v42_state_t *s, modem_status_func_t status_handler, void *user_data)
{
- s->lapm.status_callback = callback;
- s->lapm.status_callback_user_data = user_data;
+ s->lapm.status_handler = status_handler;
+ s->lapm.status_user_data = user_data;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) v42_restart(v42_state_t *s)
{
- span_schedule_init(&s->lapm.sched);
+ hdlc_tx_init(&s->lapm.hdlc_tx, FALSE, 1, TRUE, lapm_hdlc_underflow, s);
+ hdlc_rx_init(&s->lapm.hdlc_rx, FALSE, FALSE, 1, lapm_receive, s);
- s->lapm.we_are_originator = s->calling_party;
- lapm_restart(&s->lapm);
if (s->detect)
{
- s->txstream = ~0;
- s->txbits = 0;
- s->rxstream = ~0;
- s->rxbits = 0;
- s->rxoks = 0;
- s->txadps = 0;
- s->rx_negotiation_step = 0;
- s->odp_seen = FALSE;
-fprintf(stderr, "Setting T400 i\n");
- s->t400_timer = span_schedule_event(&s->lapm.sched, T_400, t400_expired, s);
+ /* We need to do the V.42 support detection sequence */
+ s->neg.txstream = ~0;
+ s->neg.txbits = 0;
+ s->neg.rxstream = ~0;
+ s->neg.rxbits = 0;
+ s->neg.rxoks = 0;
+ s->neg.txadps = 0;
+ s->neg.rx_negotiation_step = 0;
+ s->neg.odp_seen = FALSE;
+ t400_start(s);
s->lapm.state = LAPM_DETECT;
}
else
{
- s->lapm.state = LAPM_ESTABLISH;
+ /* Go directly to LAP.M mode */
+ s->lapm.state = LAPM_IDLE;
+ restart_lapm(s);
}
/*endif*/
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(v42_state_t *) v42_init(v42_state_t *s, int calling_party, int detect, v42_frame_handler_t frame_handler, void *user_data)
+SPAN_DECLARE(v42_state_t *) v42_init(v42_state_t *ss,
+ int calling_party,
+ int detect,
+ get_msg_func_t iframe_get,
+ put_msg_func_t iframe_put,
+ void *user_data)
{
- int alloced;
-
- if (frame_handler == NULL)
- return NULL;
- /*endif*/
- alloced = FALSE;
- if (s == NULL)
+ lapm_state_t *s;
+
+ if (ss == NULL)
{
- if ((s = (v42_state_t *) malloc(sizeof(*s))) == NULL)
+ if ((ss = (v42_state_t *) malloc(sizeof(*ss))) == NULL)
return NULL;
- alloced = TRUE;
- }
- memset(s, 0, sizeof(*s));
- s->calling_party = calling_party;
- s->detect = detect;
- s->lapm.iframe_receive = frame_handler;
- s->lapm.iframe_receive_user_data = user_data;
- s->lapm.debug |= (LAPM_DEBUG_LAPM_RAW | LAPM_DEBUG_LAPM_DUMP | LAPM_DEBUG_LAPM_STATE);
- s->lapm.t401_timer =
- s->lapm.t402_timer =
- s->lapm.t403_timer = -1;
-
- if ((s->lapm.tx_queue = queue_init(NULL, 16384, 0)) == NULL)
- {
- if (alloced)
- free(s);
- return NULL;
}
- /*endif*/
- span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
- span_log_set_protocol(&s->logging, "V.42");
- v42_restart(s);
- return s;
+ memset(ss, 0, sizeof(*ss));
+
+ s = &ss->lapm;
+ ss->calling_party = calling_party;
+ ss->detect = detect;
+ s->iframe_get = iframe_get;
+ s->iframe_get_user_data = user_data;
+ s->iframe_put = iframe_put;
+ s->iframe_put_user_data = user_data;
+
+ s->state = (ss->detect) ? LAPM_DETECT : LAPM_IDLE;
+ s->local_busy = FALSE;
+ s->far_busy = FALSE;
+
+ /* The address octet is:
+ Data link connection identifier (0)
+ Command/response (0 if answerer, 1 if originator)
+ Extended address (1) */
+ s->cmd_addr = (LAPM_DLCI_DTE_TO_DTE << 2) | ((ss->calling_party) ? 0x02 : 0x00) | 0x01;
+ s->rsp_addr = (LAPM_DLCI_DTE_TO_DTE << 2) | ((ss->calling_party) ? 0x00 : 0x02) | 0x01;
+
+ /* Set default values for the LAP.M parameters. These can be modified later. */
+ ss->config.v42_tx_window_size_k = V42_DEFAULT_WINDOW_SIZE_K;
+ ss->config.v42_rx_window_size_k = V42_DEFAULT_WINDOW_SIZE_K;
+ ss->config.v42_tx_n401 = V42_DEFAULT_N_401;
+ ss->config.v42_rx_n401 = V42_DEFAULT_N_401;
+
+ /* TODO: This should be part of the V.42bis startup */
+ ss->config.comp = 1;
+ ss->config.comp_dict_size = 512;
+ ss->config.comp_max_string = 6;
+
+ ss->tx_bit_rate = 28800;
+
+ reset_lapm(ss);
+
+ span_log_init(&ss->logging, SPAN_LOG_NONE, NULL);
+ span_log_set_protocol(&ss->logging, "V.42");
+ return ss;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) v42_release(v42_state_t *s)
+SPAN_DECLARE(void) v42_release(v42_state_t *s)
{
- return 0;
+ reset_lapm(s);
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) v42_free(v42_state_t *s)
+SPAN_DECLARE(void) v42_free(v42_state_t *s)
{
+ v42_release(s);
free(s);
- return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/
*
* Written by Steve Underwood <steveu@coppice.org>
*
- * Copyright (C) 2005 Steve Underwood
+ * Copyright (C) 2005, 2011 Steve Underwood
*
* All rights reserved.
*
#include "spandsp/telephony.h"
#include "spandsp/logging.h"
#include "spandsp/bit_operations.h"
+#include "spandsp/async.h"
#include "spandsp/v42bis.h"
#include "spandsp/private/logging.h"
#include "spandsp/private/v42bis.h"
/* Fixed parameters from the spec. */
-#define V42BIS_N3 8 /* Character size (bits) */
-#define V42BIS_N4 256 /* Number of characters in the alphabet */
-#define V42BIS_N5 (V42BIS_N4 + V42BIS_N6) /* Index number of first dictionary entry used to store a string */
-#define V42BIS_N6 3 /* Number of control codewords */
-
+/* Character size (bits) */
+#define V42BIS_N3 8
+/* Number of characters in the alphabet */
+#define V42BIS_N4 256
+/* Index number of first dictionary entry used to store a string */
+#define V42BIS_N5 (V42BIS_N4 + V42BIS_N6)
+/* Number of control codewords */
+#define V42BIS_N6 3
/* V.42bis/9.2 */
-#define V42BIS_ESC_STEP 51
+#define V42BIS_ESC_STEP 51
+
+/* Compreeibility monitoring parameters for assessing automated switches between
+ transparent and compressed mode */
+#define COMPRESSIBILITY_MONITOR (256*V42BIS_N3)
+#define COMPRESSIBILITY_MONITOR_HYSTERESIS 11
/* Control code words in compressed mode */
enum
V42BIS_RESET = 2 /* Force reinitialisation */
};
-static __inline__ void push_compressed_raw_octet(v42bis_compress_state_t *ss, int octet)
+static __inline__ void push_octet(v42bis_comp_state_t *s, int octet)
+{
+ s->output_buf[s->output_octet_count++] = (uint8_t) octet;
+ if (s->output_octet_count >= s->max_output_len)
+ {
+ s->handler(s->user_data, s->output_buf, s->output_octet_count);
+ s->output_octet_count = 0;
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len)
+{
+ int i;
+ int chunk;
+
+ i = 0;
+ while ((s->output_octet_count + len - i) >= s->max_output_len)
+ {
+ chunk = s->max_output_len - s->output_octet_count;
+ memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
+ s->handler(s->user_data, s->output_buf, s->max_output_len);
+ s->output_octet_count = 0;
+ i += chunk;
+ }
+ chunk = len - i;
+ if (chunk > 0)
+ {
+ memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
+ s->output_octet_count += chunk;
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code)
{
- ss->output_buf[ss->output_octet_count++] = (uint8_t) octet;
- if (ss->output_octet_count >= ss->max_len)
+ s->bit_buffer |= code << s->bit_count;
+ s->bit_count += s->v42bis_parm_c2;
+ while (s->bit_count >= 8)
{
- ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
- ss->output_octet_count = 0;
+ push_octet(s, s->bit_buffer & 0xFF);
+ s->bit_buffer >>= 8;
+ s->bit_count -= 8;
}
}
/*- End of function --------------------------------------------------------*/
-static __inline__ void push_compressed_code(v42bis_compress_state_t *ss, int code)
+static __inline__ void push_octet_alignment(v42bis_comp_state_t *s)
{
- ss->output_bit_buffer |= code << (32 - ss->v42bis_parm_c2 - ss->output_bit_count);
- ss->output_bit_count += ss->v42bis_parm_c2;
- while (ss->output_bit_count >= 8)
+ if ((s->bit_count & 7))
{
- push_compressed_raw_octet(ss, ss->output_bit_buffer >> 24);
- ss->output_bit_buffer <<= 8;
- ss->output_bit_count -= 8;
+ s->bit_count += (8 - (s->bit_count & 7));
+ while (s->bit_count >= 8)
+ {
+ push_octet(s, s->bit_buffer & 0xFF);
+ s->bit_buffer >>= 8;
+ s->bit_count -= 8;
+ }
}
}
/*- End of function --------------------------------------------------------*/
-static __inline__ void push_compressed_octet(v42bis_compress_state_t *ss, int code)
+static __inline__ void flush_octets(v42bis_comp_state_t *s)
{
- ss->output_bit_buffer |= code << (32 - 8 - ss->output_bit_count);
- ss->output_bit_count += 8;
- while (ss->output_bit_count >= 8)
+ if (s->output_octet_count > 0)
{
- push_compressed_raw_octet(ss, ss->output_bit_buffer >> 24);
- ss->output_bit_buffer <<= 8;
- ss->output_bit_count -= 8;
+ s->handler(s->user_data, s->output_buf, s->output_octet_count);
+ s->output_octet_count = 0;
}
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t *buf, int len)
+static void dictionary_init(v42bis_comp_state_t *s)
{
- int ptr;
int i;
- uint32_t octet;
- uint32_t code;
- v42bis_compress_state_t *ss;
- ss = &s->compress;
- if ((s->v42bis_parm_p0 & 2) == 0)
+ memset(s->dict, 0, sizeof(s->dict));
+ for (i = 0; i < V42BIS_N4; i++)
+ s->dict[i + V42BIS_N6].node_octet = i;
+ s->v42bis_parm_c1 = V42BIS_N5;
+ s->v42bis_parm_c2 = V42BIS_N3 + 1;
+ s->v42bis_parm_c3 = V42BIS_N4 << 1;
+ s->last_matched = 0;
+ s->update_at = 0;
+ s->last_added = 0;
+ s->bit_buffer = 0;
+ s->bit_count = 0;
+ s->flushed_length = 0;
+ s->string_length = 0;
+ s->escape_code = 0;
+ s->transparent = TRUE;
+ s->escaped = FALSE;
+ s->compression_performance = COMPRESSIBILITY_MONITOR;
+}
+/*- End of function --------------------------------------------------------*/
+
+static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
+{
+ uint16_t e;
+
+ if (at == 0)
+ return octet + V42BIS_N6;
+ e = s->dict[at].child;
+ while (e)
{
- /* Compression is off - just push the incoming data out */
- for (i = 0; i < len - ss->max_len; i += ss->max_len)
- ss->handler(ss->user_data, buf + i, ss->max_len);
- if (i < len)
- ss->handler(ss->user_data, buf + i, len - i);
- return 0;
+ if (s->dict[e].node_octet == octet)
+ return e;
+ e = s->dict[e].next;
+ }
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
+{
+ uint16_t newx;
+ uint16_t next;
+ uint16_t e;
+
+ newx = s->v42bis_parm_c1;
+ s->dict[newx].node_octet = octet;
+ s->dict[newx].parent = at;
+ s->dict[newx].child = 0;
+ s->dict[newx].next = s->dict[at].child;
+ s->dict[at].child = newx;
+ next = newx;
+ /* 6.5 Recovering a dictionary entry to use next */
+ do
+ {
+ /* 6.5(a) and (b) */
+ if (++next == s->v42bis_parm_n2)
+ next = V42BIS_N5;
}
- ptr = 0;
- if (ss->first && len > 0)
+ while (s->dict[next].child);
+ /* 6.5(c) We need to reuse a leaf node */
+ if (s->dict[next].parent)
{
- octet = buf[ptr++];
- ss->string_code = octet + V42BIS_N6;
- if (octet == ss->escape_code)
+ /* 6.5(d) Detach the leaf node from its parent, and re-use it */
+ e = s->dict[next].parent;
+ if (s->dict[e].child == next)
{
- push_compressed_octet(ss, ss->escape_code);
- ss->escape_code += V42BIS_ESC_STEP;
- push_compressed_octet(ss, V42BIS_EID);
+ s->dict[e].child = s->dict[next].next;
}
else
{
- push_compressed_octet(ss, octet);
+ e = s->dict[e].child;
+ while (s->dict[e].next != next)
+ e = s->dict[e].next;
+ s->dict[e].next = s->dict[next].next;
}
- ss->first = FALSE;
}
- while (ptr < len)
+ s->v42bis_parm_c1 = next;
+ return newx;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void send_string(v42bis_comp_state_t *s)
+{
+ push_octets(s, s->string, s->string_length);
+ s->string_length = 0;
+ s->flushed_length = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code)
+{
+ int i;
+ uint16_t p;
+
+ /* Work out the length */
+ for (i = 0, p = code; p; i++)
+ p = s->dict[p].parent;
+ s->string_length += i;
+ /* Now expand the known length of string */
+ i = s->string_length - 1;
+ for (p = code; p; )
+ {
+ s->string[i--] = s->dict[p].node_octet;
+ p = s->dict[p].parent;
+ }
+}
+/*- End of function --------------------------------------------------------*/
+
+static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code)
+{
+ int i;
+
+ /* Update compressibility metric */
+ /* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */
+ s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR);
+ if (s->transparent)
{
- octet = buf[ptr++];
- if ((ss->dict[ss->string_code].children[octet >> 5] & (1 << (octet & 0x1F))))
+ for (i = 0; i < s->string_length; i++)
{
- /* The leaf exists. Now find it in the table. */
- /* TODO: This is a brute force scan for a match. We need something better. */
- for (code = 0; code < ss->v42bis_parm_c3; code++)
+ push_octet(s, s->string[i]);
+ if (s->string[i] == s->escape_code)
{
- if (ss->dict[code].parent_code == ss->string_code && ss->dict[code].node_octet == octet)
- break;
+ push_octet(s, V42BIS_EID);
+ s->escape_code += V42BIS_ESC_STEP;
}
}
- else
+ }
+ else
+ {
+ /* Allow for any escape octets in the string */
+ for (i = 0; i < s->string_length; i++)
{
- /* The leaf does not exist. */
- code = s->v42bis_parm_n2;
+ if (s->string[i] == s->escape_code)
+ s->escape_code += V42BIS_ESC_STEP;
}
- /* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry
- created by the last invocation of the string matching procedure, then the
- next character shall be read and appended to the string and this step
- repeated. */
- if (code < ss->v42bis_parm_c3 && code != ss->latest_code)
+ /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */
+ while (code >= s->v42bis_parm_c3)
{
- /* The string was found */
- ss->string_code = code;
- ss->string_length++;
+ /* We need to increase the codeword size */
+ /* 7.4(a) */
+ push_compressed_code(s, V42BIS_STEPUP);
+ /* 7.4(b) */
+ s->v42bis_parm_c2++;
+ /* 7.4(c) */
+ s->v42bis_parm_c3 <<= 1;
+ /* 7.4(d) this might need to be repeated, so we loop */
}
- else
+ /* 7.5 Transfer - output the last state of the string */
+ push_compressed_code(s, code);
+ }
+ s->string_length = 0;
+ s->flushed_length = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void go_compressed(v42bis_state_t *ss)
+{
+ v42bis_comp_state_t *s;
+
+ s = &ss->compress;
+ if (!s->transparent)
+ return;
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n");
+ /* Switch out of transparent now, between codes. We need to send the octet which did not
+ match, just before switching. */
+ if (s->last_matched)
+ {
+ s->update_at = s->last_matched;
+ send_encoded_data(s, s->last_matched);
+ s->last_matched = 0;
+ }
+ push_octet(s, s->escape_code);
+ push_octet(s, V42BIS_ECM);
+ s->bit_buffer = 0;
+ s->transparent = FALSE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void go_transparent(v42bis_state_t *ss)
+{
+ v42bis_comp_state_t *s;
+
+ s = &ss->compress;
+ if (s->transparent)
+ return;
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n");
+ /* Switch into transparent now, between codes, and the unmatched octet should
+ go out in transparent mode, just below */
+ if (s->last_matched)
+ {
+ s->update_at = s->last_matched;
+ send_encoded_data(s, s->last_matched);
+ s->last_matched = 0;
+ }
+ s->last_added = 0;
+ push_compressed_code(s, V42BIS_ETM);
+ push_octet_alignment(s);
+ s->transparent = TRUE;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void monitor_for_mode_change(v42bis_state_t *ss)
+{
+ v42bis_comp_state_t *s;
+
+ s = &ss->compress;
+ switch (s->compression_mode)
+ {
+ case V42BIS_COMPRESSION_MODE_DYNAMIC:
+ /* 7.8 Data compressibility test */
+ if (s->transparent)
{
- /* The string is not in the table. */
- if (!ss->transparent)
- {
- /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */
- while (ss->v42bis_parm_c1 >= ss->v42bis_parm_c3 && ss->v42bis_parm_c3 <= s->v42bis_parm_n2)
- {
- /* We need to increase the codeword size */
- /* 7.4(a) */
- push_compressed_code(ss, V42BIS_STEPUP);
- /* 7.4(b) */
- ss->v42bis_parm_c2++;
- /* 7.4(c) */
- ss->v42bis_parm_c3 <<= 1;
- /* 7.4(d) this might need to be repeated, so we loop */
- }
- /* 7.5 Transfer - output the last state of the string */
- push_compressed_code(ss, ss->string_code);
- }
- /* 7.6 Dictionary updating */
- /* 6.4 Add the string to the dictionary */
- /* 6.4(b) The string is not in the table. */
- if (code != ss->latest_code && ss->string_length < s->v42bis_parm_n7)
- {
- ss->latest_code = ss->v42bis_parm_c1;
- /* 6.4(a) The length of the string is in range for adding to the dictionary */
- /* If the last code was a leaf, it no longer is */
- ss->dict[ss->string_code].leaves++;
- ss->dict[ss->string_code].children[octet >> 5] |= (1 << (octet & 0x1F));
- /* The new one is definitely a leaf */
- ss->dict[ss->v42bis_parm_c1].parent_code = (uint16_t) ss->string_code;
- ss->dict[ss->v42bis_parm_c1].leaves = 0;
- ss->dict[ss->v42bis_parm_c1].node_octet = (uint8_t) octet;
- /* 7.7 Node recovery */
- /* 6.5 Recovering a dictionary entry to use next */
- for (;;)
- {
- /* 6.5(a) and (b) */
- if ((int) (++ss->v42bis_parm_c1) >= s->v42bis_parm_n2)
- ss->v42bis_parm_c1 = V42BIS_N5;
- /* 6.5(c) We need to reuse a leaf node */
- if (ss->dict[ss->v42bis_parm_c1].leaves)
- continue;
- if (ss->dict[ss->v42bis_parm_c1].parent_code == 0xFFFF)
- break;
- /* 6.5(d) Detach the leaf node from its parent, and re-use it */
- /* Possibly make the parent a leaf node again */
- ss->dict[ss->dict[ss->v42bis_parm_c1].parent_code].leaves--;
- ss->dict[ss->dict[ss->v42bis_parm_c1].parent_code].children[ss->dict[ss->v42bis_parm_c1].node_octet >> 5] &= ~(1 << (ss->dict[ss->v42bis_parm_c1].node_octet & 0x1F));
- ss->dict[ss->v42bis_parm_c1].parent_code = 0xFFFF;
- break;
- }
- }
- else
+ if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS)
{
- ss->latest_code = 0xFFFFFFFF;
+ /* 7.8.1 Transition to compressed mode */
+ go_compressed(ss);
}
- /* 7.8 Data compressibility test */
- /* Filter on the balance of what went into the compressor, and what came out */
- ss->compressibility_filter += ((((8*ss->string_length - ss->v42bis_parm_c2) << 20) - ss->compressibility_filter) >> 10);
- if (ss->compression_mode == V42BIS_COMPRESSION_MODE_DYNAMIC)
- {
- /* Work out if it is appropriate to change between transparent and
- compressed mode. */
- if (ss->transparent)
- {
- if (ss->compressibility_filter > 0)
- {
- if (++ss->compressibility_persistence > 1000)
- {
- /* Schedule a switch to compressed mode */
- ss->change_transparency = -1;
- ss->compressibility_persistence = 0;
- }
- }
- else
- {
- ss->compressibility_persistence = 0;
- }
- }
- else
- {
- if (ss->compressibility_filter < 0)
- {
- if (++ss->compressibility_persistence > 1000)
- {
- /* Schedule a switch to transparent mode */
- ss->change_transparency = 1;
- ss->compressibility_persistence = 0;
- }
- }
- else
- {
- ss->compressibility_persistence = 0;
- }
- }
- }
- if (ss->change_transparency)
- {
- if (ss->change_transparency < 0)
- {
- if (ss->transparent)
- {
- printf("Going compressed\n");
- /* 7.8.1 Transition to compressed mode */
- /* Switch out of transparent now, between codes. We need to send the octet which did not
- match, just before switching. */
- if (octet == ss->escape_code)
- {
- push_compressed_octet(ss, ss->escape_code);
- ss->escape_code += V42BIS_ESC_STEP;
- push_compressed_octet(ss, V42BIS_EID);
- }
- else
- {
- push_compressed_octet(ss, octet);
- }
- push_compressed_octet(ss, ss->escape_code);
- ss->escape_code += V42BIS_ESC_STEP;
- push_compressed_octet(ss, V42BIS_ECM);
- ss->transparent = FALSE;
- }
- }
- else
- {
- if (!ss->transparent)
- {
- printf("Going transparent\n");
- /* 7.8.2 Transition to transparent mode */
- /* Switch into transparent now, between codes, and the unmatched octet should
- go out in transparent mode, just below */
- push_compressed_code(ss, V42BIS_ETM);
- ss->transparent = TRUE;
- }
- }
- ss->change_transparency = 0;
- }
- /* 7.8.3 Reset function - TODO */
- ss->string_code = octet + V42BIS_N6;
- ss->string_length = 1;
}
- if (ss->transparent)
+ else
{
- if (octet == ss->escape_code)
- {
- push_compressed_octet(ss, ss->escape_code);
- ss->escape_code += V42BIS_ESC_STEP;
- push_compressed_octet(ss, V42BIS_EID);
- }
- else
+ if (s->compression_performance > COMPRESSIBILITY_MONITOR)
{
- push_compressed_octet(ss, octet);
+ /* 7.8.2 Transition to transparent mode */
+ go_transparent(ss);
}
}
+ /* 7.8.3 Reset function - TODO */
+ break;
+ case V42BIS_COMPRESSION_MODE_ALWAYS:
+ if (s->transparent)
+ go_compressed(ss);
+ break;
+ case V42BIS_COMPRESSION_MODE_NEVER:
+ if (!s->transparent)
+ go_transparent(ss);
+ break;
}
+}
+/*- End of function --------------------------------------------------------*/
+
+static int v42bis_comp_init(v42bis_comp_state_t *s,
+ int p1,
+ int p2,
+ put_msg_func_t handler,
+ void *user_data,
+ int max_output_len)
+{
+ memset(s, 0, sizeof(*s));
+ s->v42bis_parm_n2 = p1;
+ s->v42bis_parm_n7 = p2;
+ s->handler = handler;
+ s->user_data = user_data;
+ s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH) ? max_output_len : V42BIS_MAX_OUTPUT_LENGTH;
+ s->output_octet_count = 0;
+ dictionary_init(s);
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int comp_exit(v42bis_comp_state_t *s)
+{
+ s->v42bis_parm_n2 = 0;
return 0;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s)
+SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len)
{
- v42bis_compress_state_t *ss;
+ v42bis_comp_state_t *s;
+ int i;
+ uint16_t code;
- ss = &s->compress;
- if (!ss->transparent)
+ s = &ss->compress;
+ if (!s->v42bis_parm_p0)
{
- /* Output the last state of the string */
- push_compressed_code(ss, ss->string_code);
- /* TODO: We use a positive FLUSH at all times. It is really needed, if the
- previous step resulted in no leftover bits. */
- push_compressed_code(ss, V42BIS_FLUSH);
- }
- while (ss->output_bit_count > 0)
- {
- push_compressed_raw_octet(ss, ss->output_bit_buffer >> 24);
- ss->output_bit_buffer <<= 8;
- ss->output_bit_count -= 8;
+ /* Compression is off - just push the incoming data out */
+ push_octets(s, buf, len);
+ return 0;
}
- /* Now push out anything remaining. */
- if (ss->output_octet_count > 0)
+ for (i = 0; i < len; )
{
- ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
- ss->output_octet_count = 0;
+ /* 6.4 Add the string to the dictionary */
+ if (s->update_at)
+ {
+ if (match_octet(s, s->update_at, buf[i]) == 0)
+ s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]);
+ s->update_at = 0;
+ }
+ /* Match string */
+ while (i < len)
+ {
+ code = match_octet(s, s->last_matched, buf[i]);
+ if (code == 0)
+ {
+ s->update_at = s->last_matched;
+ send_encoded_data(s, s->last_matched);
+ s->last_matched = 0;
+ break;
+ }
+ if (code == s->last_added)
+ {
+ s->last_added = 0;
+ send_encoded_data(s, s->last_matched);
+ s->last_matched = 0;
+ break;
+ }
+ s->last_matched = code;
+ /* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry
+ created by the last invocation of the string matching procedure, then the
+ next character shall be read and appended to the string and this step
+ repeated. */
+ s->string[s->string_length++] = buf[i++];
+ /* 6.4(a) The string must not exceed N7 in length */
+ if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
+ {
+ send_encoded_data(s, s->last_matched);
+ s->last_matched = 0;
+ break;
+ }
+ }
+ monitor_for_mode_change(ss);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
-#if 0
-SPAN_DECLARE(int) v42bis_compress_dump(v42bis_state_t *s)
+SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss)
{
- int i;
+ v42bis_comp_state_t *s;
+ int len;
- for (i = 0; i < V42BIS_MAX_CODEWORDS; i++)
+ s = &ss->compress;
+ if (s->update_at)
+ return 0;
+ if (s->last_matched)
{
- if (s->compress.dict[i].parent_code != 0xFFFF)
- {
- printf("Entry %4x, prior %4x, leaves %d, octet %2x\n", i, s->compress.dict[i].parent_code, s->compress.dict[i].leaves, s->compress.dict[i].node_octet);
- }
+ len = s->string_length;
+ send_encoded_data(s, s->last_matched);
+ s->flushed_length += len;
}
+ if (!s->transparent)
+ {
+ s->update_at = s->last_matched;
+ s->last_matched = 0;
+ s->flushed_length = 0;
+ push_compressed_code(s, V42BIS_FLUSH);
+ push_octet_alignment(s);
+ }
+ flush_octets(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
-#endif
-SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t *buf, int len)
+SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len)
{
- int ptr;
+ v42bis_comp_state_t *s;
int i;
- int this_length;
- uint8_t *string;
- uint32_t code;
- uint32_t new_code;
- int code_len;
- v42bis_decompress_state_t *ss;
- uint8_t decode_buf[V42BIS_MAX_STRING_SIZE];
-
- ss = &s->decompress;
- if ((s->v42bis_parm_p0 & 1) == 0)
+ int j;
+ int yyy;
+ uint16_t code;
+ uint16_t p;
+ uint8_t ch;
+ uint8_t in;
+
+ s = &ss->decompress;
+ if (!s->v42bis_parm_p0)
{
/* Compression is off - just push the incoming data out */
- for (i = 0; i < len - ss->max_len; i += ss->max_len)
- ss->handler(ss->user_data, buf + i, ss->max_len);
- if (i < len)
- ss->handler(ss->user_data, buf + i, len - i);
+ push_octets(s, buf, len);
return 0;
}
- ptr = 0;
- code_len = (ss->transparent) ? 8 : ss->v42bis_parm_c2;
- for (;;)
+ for (i = 0; i < len; )
{
- /* Fill up the bit buffer. */
- while (ss->input_bit_count < (32 - 8) && ptr < len)
+ if (s->transparent)
{
- ss->input_bit_count += 8;
- ss->input_bit_buffer |= (uint32_t) buf[ptr++] << (32 - ss->input_bit_count);
- }
- if (ss->input_bit_count < code_len)
- break;
- new_code = ss->input_bit_buffer >> (32 - code_len);
- ss->input_bit_count -= code_len;
- ss->input_bit_buffer <<= code_len;
- if (ss->transparent)
- {
- code = new_code;
- if (ss->escaped)
+ in = buf[i];
+ if (s->escaped)
{
- ss->escaped = FALSE;
- switch (code)
+ /* Command */
+ s->escaped = FALSE;
+ switch (in)
{
case V42BIS_ECM:
- printf("Hit V42BIS_ECM\n");
- ss->transparent = FALSE;
- code_len = ss->v42bis_parm_c2;
- break;
+ /* Enter compressed mode */
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n");
+ send_string(s);
+ s->transparent = FALSE;
+ s->update_at = s->last_matched;
+ s->last_matched = 0;
+ i++;
+ continue;
case V42BIS_EID:
- printf("Hit V42BIS_EID\n");
- ss->output_buf[ss->output_octet_count++] = ss->escape_code;
- ss->escape_code += V42BIS_ESC_STEP;
- if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7)
- {
- ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
- ss->output_octet_count = 0;
- }
+ /* Escape symbol */
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n");
+ in = s->escape_code;
+ s->escape_code += V42BIS_ESC_STEP;
break;
case V42BIS_RESET:
- printf("Hit V42BIS_RESET\n");
- break;
+ /* Reset dictionary */
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n");
+ /* TODO: */
+ send_string(s);
+ dictionary_init(s);
+ i++;
+ continue;
default:
- printf("Hit V42BIS_???? - %" PRIu32 "\n", code);
- break;
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in);
+ return -1;
}
}
- else if (code == ss->escape_code)
+ else if (in == s->escape_code)
{
- ss->escaped = TRUE;
+ s->escaped = TRUE;
+ i++;
+ continue;
}
- else
+
+ yyy = TRUE;
+ for (j = 0; j < 2 && yyy; j++)
{
- ss->output_buf[ss->output_octet_count++] = (uint8_t) code;
- if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7)
+ if (s->update_at)
+ {
+ if (match_octet(s, s->update_at, in) == 0)
+ s->last_added = add_octet_to_dictionary(s, s->update_at, in);
+ s->update_at = 0;
+ }
+
+ code = match_octet(s, s->last_matched, in);
+ if (code == 0)
+ {
+ s->update_at = s->last_matched;
+ send_string(s);
+ s->last_matched = 0;
+ }
+ else if (code == s->last_added)
+ {
+ s->last_added = 0;
+ send_string(s);
+ s->last_matched = 0;
+ }
+ else
{
- ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
- ss->output_octet_count = 0;
+ s->last_matched = code;
+ s->string[s->string_length++] = in;
+ if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
+ {
+ send_string(s);
+ s->last_matched = 0;
+ }
+ i++;
+ yyy = FALSE;
}
}
}
else
{
- if (new_code < V42BIS_N6)
+ /* Get code from input */
+ while (s->bit_count < s->v42bis_parm_c2 && i < len)
+ {
+ s->bit_buffer |= buf[i++] << s->bit_count;
+ s->bit_count += 8;
+ }
+ if (s->bit_count < s->v42bis_parm_c2)
+ continue;
+ code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1);
+ s->bit_buffer >>= s->v42bis_parm_c2;
+ s->bit_count -= s->v42bis_parm_c2;
+
+ if (code < V42BIS_N6)
{
/* We have a control code. */
- switch (new_code)
+ switch (code)
{
case V42BIS_ETM:
- printf("Hit V42BIS_ETM\n");
- ss->transparent = TRUE;
- code_len = 8;
+ /* Enter transparent mode */
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n");
+ s->bit_count = 0;
+ s->transparent = TRUE;
+ s->last_matched = 0;
+ s->last_added = 0;
break;
case V42BIS_FLUSH:
- printf("Hit V42BIS_FLUSH\n");
- v42bis_decompress_flush(s);
+ /* Flush signal */
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n");
+ s->bit_count = 0;
break;
case V42BIS_STEPUP:
- /* We need to increase the codeword size */
- printf("Hit V42BIS_STEPUP\n");
- if (ss->v42bis_parm_c3 >= s->v42bis_parm_n2)
- {
- /* Invalid condition */
+ /* Increase code word size */
+ span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n");
+ s->v42bis_parm_c2++;
+ s->v42bis_parm_c3 <<= 1;
+ if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3))
return -1;
- }
- code_len = ++ss->v42bis_parm_c2;
- ss->v42bis_parm_c3 <<= 1;
break;
}
continue;
}
- if (ss->first)
+ /* Regular codeword */
+ if (code == s->v42bis_parm_c1)
+ return -1;
+ expand_codeword_to_string(s, code);
+ if (s->update_at)
{
- ss->first = FALSE;
- ss->octet = new_code - V42BIS_N6;
- ss->output_buf[0] = (uint8_t) ss->octet;
- ss->output_octet_count = 1;
- if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7)
+ ch = s->string[0];
+ if ((p = match_octet(s, s->update_at, ch)) == 0)
{
- ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
- ss->output_octet_count = 0;
+ s->last_added = add_octet_to_dictionary(s, s->update_at, ch);
+ if (code == s->v42bis_parm_c1)
+ return -1;
}
- ss->old_code = new_code;
- continue;
- }
- /* Start at the end of the buffer, and decode backwards */
- string = &decode_buf[V42BIS_MAX_STRING_SIZE - 1];
- /* Check the received code is valid. It can't be too big, as we pulled only the expected number
- of bits from the input stream. It could, however, be unknown. */
- if (ss->dict[new_code].parent_code == 0xFFFF)
- return -1;
- /* Otherwise we do a straight decode of the new code. */
- code = new_code;
- /* Trace back through the octets which form the string, and output them. */
- while (code >= V42BIS_N5)
- {
-if (code > 4095) {printf("Code is 0x%" PRIu32 "\n", code); exit(2);}
- *string-- = ss->dict[code].node_octet;
- code = ss->dict[code].parent_code;
- }
- *string = (uint8_t) (code - V42BIS_N6);
- ss->octet = code - V42BIS_N6;
- /* Output the decoded string. */
- this_length = V42BIS_MAX_STRING_SIZE - (int) (string - decode_buf);
- for (i = 0; i < this_length; i++)
- {
- if (string[i] == ss->escape_code)
- ss->escape_code += V42BIS_ESC_STEP;
- }
- memcpy(ss->output_buf + ss->output_octet_count, string, this_length);
- ss->output_octet_count += this_length;
- if (ss->output_octet_count >= ss->max_len - s->v42bis_parm_n7)
- {
- ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
- ss->output_octet_count = 0;
- }
- /* 6.4 Add the string to the dictionary */
- if (ss->last_length < s->v42bis_parm_n7)
- {
- /* 6.4(a) The string does not exceed N7 in length */
- if (ss->last_old_code != ss->old_code
- ||
- ss->last_extra_octet != *string)
+ else if (p == s->last_added)
{
- /* 6.4(b) The string is not in the table. */
- ss->dict[ss->old_code].leaves++;
- /* The new one is definitely a leaf */
- ss->dict[ss->v42bis_parm_c1].parent_code = (uint16_t) ss->old_code;
- ss->dict[ss->v42bis_parm_c1].leaves = 0;
- ss->dict[ss->v42bis_parm_c1].node_octet = (uint8_t) ss->octet;
- /* 6.5 Recovering a dictionary entry to use next */
- for (;;)
- {
- /* 6.5(a) and (b) */
- if (++ss->v42bis_parm_c1 >= s->v42bis_parm_n2)
- ss->v42bis_parm_c1 = V42BIS_N5;
- /* 6.5(c) We need to reuse a leaf node */
- if (ss->dict[ss->v42bis_parm_c1].leaves)
- continue;
- /* 6.5(d) This is a leaf node, so re-use it */
- /* Possibly make the parent a leaf node again */
- if (ss->dict[ss->v42bis_parm_c1].parent_code != 0xFFFF)
- ss->dict[ss->dict[ss->v42bis_parm_c1].parent_code].leaves--;
- ss->dict[ss->v42bis_parm_c1].parent_code = 0xFFFF;
- break;
- }
+ s->last_added = 0;
}
}
- /* Record the addition to the dictionary, so we can check for repeat attempts
- at the next code - see II.4.3 */
- ss->last_old_code = ss->old_code;
- ss->last_extra_octet = *string;
-
- ss->old_code = new_code;
- ss->last_length = this_length;
+ s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7) ? 0 : code;
+ /* Allow for any escapes which may be in this string */
+ for (j = 0; j < s->string_length; j++)
+ {
+ if (s->string[j] == s->escape_code)
+ s->escape_code += V42BIS_ESC_STEP;
+ }
+ send_string(s);
}
}
return 0;
}
/*- End of function --------------------------------------------------------*/
-SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s)
-{
- v42bis_decompress_state_t *ss;
-
- ss = &s->decompress;
- /* Push out anything remaining. */
- if (ss->output_octet_count > 0)
- {
- ss->handler(ss->user_data, ss->output_buf, ss->output_octet_count);
- ss->output_octet_count = 0;
- }
- return 0;
-}
-/*- End of function --------------------------------------------------------*/
-
-#if 0
-SPAN_DECLARE(int) v42bis_decompress_dump(v42bis_state_t *s)
+SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss)
{
- int i;
+ v42bis_comp_state_t *s;
+ int len;
- for (i = 0; i < V42BIS_MAX_CODEWORDS; i++)
- {
- if (s->decompress.dict[i].parent_code != 0xFFFF)
- {
- printf("Entry %4x, prior %4x, leaves %d, octet %2x\n", i, s->decompress.dict[i].parent_code, s->decompress.dict[i].leaves, s->decompress.dict[i].node_octet);
- }
- }
+ s = &ss->decompress;
+ len = s->string_length;
+ send_string(s);
+ s->flushed_length += len;
+ flush_octets(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
-#endif
SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode)
{
s->compress.compression_mode = mode;
- switch (mode)
- {
- case V42BIS_COMPRESSION_MODE_ALWAYS:
- s->compress.change_transparency = -1;
- break;
- case V42BIS_COMPRESSION_MODE_NEVER:
- s->compress.change_transparency = 1;
- break;
- }
}
/*- End of function --------------------------------------------------------*/
int negotiated_p0,
int negotiated_p1,
int negotiated_p2,
- v42bis_frame_handler_t frame_handler,
- void *frame_user_data,
- int max_frame_len,
- v42bis_data_handler_t data_handler,
- void *data_user_data,
- int max_data_len)
+ put_msg_func_t encode_handler,
+ void *encode_user_data,
+ int max_encode_len,
+ put_msg_func_t decode_handler,
+ void *decode_user_data,
+ int max_decode_len)
{
- int i;
+ int ret;
if (negotiated_p1 < V42BIS_MIN_DICTIONARY_SIZE || negotiated_p1 > 65535)
return NULL;
return NULL;
}
memset(s, 0, sizeof(*s));
+ span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
+ span_log_set_protocol(&s->logging, "V.42bis");
- s->compress.handler = frame_handler;
- s->compress.user_data = frame_user_data;
- s->compress.max_len = (max_frame_len < 1024) ? max_frame_len : 1024;
-
- s->decompress.handler = data_handler;
- s->decompress.user_data = data_user_data;
- s->decompress.max_len = (max_data_len < 1024) ? max_data_len : 1024;
-
- s->v42bis_parm_p0 = negotiated_p0; /* default is both ways off */
-
- s->v42bis_parm_n1 = top_bit(negotiated_p1 - 1) + 1;
- s->v42bis_parm_n2 = negotiated_p1;
- s->v42bis_parm_n7 = negotiated_p2;
-
- /* 6.5 */
- s->compress.v42bis_parm_c1 =
- s->decompress.v42bis_parm_c1 = V42BIS_N5;
-
- s->compress.v42bis_parm_c2 =
- s->decompress.v42bis_parm_c2 = V42BIS_N3 + 1;
-
- s->compress.v42bis_parm_c3 =
- s->decompress.v42bis_parm_c3 = 2*V42BIS_N4;
-
- s->compress.first =
- s->decompress.first = TRUE;
- for (i = 0; i < V42BIS_MAX_CODEWORDS; i++)
+ if ((ret = v42bis_comp_init(&s->compress, negotiated_p1, negotiated_p2, encode_handler, encode_user_data, max_encode_len)))
+ return NULL;
+ if ((ret = v42bis_comp_init(&s->decompress, negotiated_p1, negotiated_p2, decode_handler, decode_user_data, max_decode_len)))
{
- s->compress.dict[i].parent_code =
- s->decompress.dict[i].parent_code = 0xFFFF;
- s->compress.dict[i].leaves =
- s->decompress.dict[i].leaves = 0;
+ comp_exit(&s->compress);
+ return NULL;
}
- /* Point the root nodes for decompression to themselves. It doesn't matter much what
- they are set to, as long as they are considered "known" codes. */
- for (i = 0; i < V42BIS_N5; i++)
- s->decompress.dict[i].parent_code = (uint16_t) i;
- s->compress.string_code = 0xFFFFFFFF;
- s->compress.latest_code = 0xFFFFFFFF;
- s->compress.transparent = TRUE;
- s->compress.first = TRUE;
-
- s->decompress.last_old_code = 0xFFFFFFFF;
- s->decompress.last_extra_octet = -1;
- s->decompress.transparent = TRUE;
- s->compress.first = TRUE;
-
- s->compress.compression_mode = V42BIS_COMPRESSION_MODE_DYNAMIC;
+ s->compress.v42bis_parm_p0 = negotiated_p0 & 2;
+ s->decompress.v42bis_parm_p0 = negotiated_p0 & 1;
return s;
}
SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s)
{
- free(s);
+ comp_exit(&s->compress);
+ comp_exit(&s->decompress);
return 0;
}
/*- End of function --------------------------------------------------------*/
noinst_DATA = sound_c1_8k.wav sound_c3_8k.wav
-EXTRA_DIST = regression_tests.sh \
+EXTRA_DIST = fax_tests.sh \
+ regression_tests.sh \
tsb85_extra_tests.sh \
- v42bis_tests.sh \
- fax_tests.sh \
tsb85_tests.sh \
+ v42bis_tests.sh \
msvc/adsi_tests.vcproj \
msvc/complex_tests.vcproj \
msvc/complex_vector_float_tests.vcproj \
super_tone_rx_tests \
super_tone_tx_tests \
swept_tone_tests \
- t4_tests \
t31_tests \
- t38_decode \
t38_core_tests \
+ t38_decode \
t38_gateway_tests \
t38_gateway_to_terminal_tests \
t38_non_ecm_buffer_tests \
t38_terminal_tests \
t38_terminal_to_gateway_tests \
+ t4_tests \
time_scale_tests \
timezone_tests \
tone_detect_tests \
v8_tests \
vector_float_tests \
vector_int_tests \
- testadsi \
- testfax \
tsb85_tests
noinst_HEADERS = echo_monitor.h \
vector_int_tests_SOURCES = vector_int_tests.c
vector_int_tests_LDADD = $(LIBDIR) -lspandsp
-testadsi_SOURCES = testadsi.c
-testadsi_LDADD = $(LIBDIR) -lspandsp
-
-testfax_SOURCES = testfax.c
-testfax_LDADD = $(LIBDIR) -lspandsp
-
# We need to create the CSS files for echo cancellation tests.
sound_c1_8k.wav sound_c3_8k.wav: make_g168_css$(EXEEXT)
char tag[20];
i = (int) (intptr_t) user_data;
- snprintf(tag, sizeof(tag), "%c: Phase B:", i);
+ snprintf(tag, sizeof(tag), "%c: Phase B", i);
printf("%c: Phase B handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result));
log_rx_parameters(s, tag);
return T30_ERR_OK;
char tag[20];
i = (int) (intptr_t) user_data;
- snprintf(tag, sizeof(tag), "%c: Phase D:", i);
+ snprintf(tag, sizeof(tag), "%c: Phase D", i);
printf("%c: Phase D handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result));
log_transfer_statistics(s, tag);
log_tx_parameters(s, tag);
char tag[20];
i = (intptr_t) user_data;
- snprintf(tag, sizeof(tag), "%c: Phase E:", i);
+ snprintf(tag, sizeof(tag), "%c: Phase E", i);
printf("%c: Phase E handler on channel %c - (%d) %s\n", i, i, result, t30_completion_code_to_str(result));
log_transfer_statistics(s, tag);
log_tx_parameters(s, tag);
}
}
+ encoded_fd = -1;
+ inhandle = NULL;
+ oki_enc_state = NULL;
if (encoded_file_name)
{
if ((encoded_fd = open(encoded_file_name, O_RDONLY)) < 0)
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
+
+#if defined(HAVE_PCAP_H)
#include <pcap.h>
+#endif
#include <netinet/in.h>
#include <netinet/udp.h>
-#if defined(__HPUX) || defined(__CYGWIN) || defined(__FreeBSD__)
+#if defined(__HPUX) || defined(__CYGWIN__) || defined(__FreeBSD__)
#include <netinet/in_systm.h>
#endif
#include <netinet/ip.h>
-#ifndef __CYGWIN
+#if !defined(__CYGWIN__)
#include <netinet/ip6.h>
#endif
#include <string.h>
#include "spandsp.h"
#include "pcap_parse.h"
-#if defined(__HPUX) || defined(__DARWIN) || defined(__CYGWIN) || defined(__FreeBSD__)
+#if defined(__HPUX) || defined(__DARWIN) || defined(__CYGWIN__) || defined(__FreeBSD__)
struct iphdr
{
{
char ether_dst[6];
char ether_src[6];
- u_int16_t ether_type; /* we only need the type, so we can determine, if the next header is IPv4 or IPv6 */
+ u_int16_t ether_type;
} ether_hdr;
+typedef struct _null_hdr
+{
+ uint32_t pf_type;
+} null_hdr;
+
+#if !defined(__CYGWIN__)
typedef struct _ipv6_hdr
{
char dontcare[6];
u_int8_t nxt_header; /* we only need the next header, so we can determine, if the next header is UDP or not */
char dontcare2[33];
} ipv6_hdr;
+#endif
char errbuf[PCAP_ERRBUF_SIZE];
int total_pkts;
uint32_t pktlen;
ether_hdr *ethhdr;
+ null_hdr *nullhdr;
struct iphdr *iphdr;
+#if !defined(__CYGWIN__)
ipv6_hdr *ip6hdr;
+#endif
struct udphdr *udphdr;
+ int datalink;
+ int packet_type;
total_pkts = 0;
if ((pcap = pcap_open_offline(file, errbuf)) == NULL)
fprintf(stderr, "Can't open PCAP file '%s'\n", file);
return -1;
}
+ datalink = pcap_datalink(pcap);
+ /* DLT_EN10MB seems to apply to all forms of ethernet, not just the 10MB kind. */
+ if (datalink != DLT_EN10MB && datalink != DLT_NULL)
+ {
+ fprintf(stderr, "Unsupported data link type %d\n", datalink);
+ return -1;
+ }
+
+ printf("Datalink type %d\n", datalink);
pkthdr = NULL;
pktdata = NULL;
#if defined(HAVE_PCAP_NEXT_EX)
while ((pktdata = (uint8_t *) pcap_next(pcap, pkthdr)) != NULL)
{
#endif
- ethhdr = (ether_hdr *) pktdata;
- if (ntohs(ethhdr->ether_type) != 0x0800 /* IPv4 */
- &&
- ntohs(ethhdr->ether_type) != 0x86dd) /* IPv6 */
+ if (datalink == DLT_EN10MB)
+ {
+ ethhdr = (ether_hdr *) pktdata;
+ packet_type = ntohs(ethhdr->ether_type);
+#if !defined(__CYGWIN__)
+ if (packet_type != 0x0800 /* IPv4 */
+ &&
+ packet_type != 0x86DD) /* IPv6 */
+#else
+ if (packet_type != 0x0800) /* IPv4 */
+#endif
+ {
+ continue;
+ }
+ iphdr = (struct iphdr *) ((uint8_t *) ethhdr + sizeof(*ethhdr));
+ }
+ else if (datalink == DLT_NULL)
+ {
+ nullhdr = (null_hdr *) pktdata;
+ if (nullhdr->pf_type != PF_INET && nullhdr->pf_type != PF_INET6)
+ continue;
+ iphdr = (struct iphdr *) ((uint8_t *) nullhdr + sizeof(*nullhdr));
+ }
+ else
{
continue;
}
- iphdr = (struct iphdr *) ((uint8_t *) ethhdr + sizeof(*ethhdr));
+#if 0
+ {
+ int i;
+ printf("--- %d -", pkthdr->caplen);
+ for (i = 0; i < pkthdr->caplen; i++)
+ printf(" %02x", pktdata[i]);
+ printf("\n");
+ }
+#endif
+#if !defined(__CYGWIN__)
if (iphdr && iphdr->version == 6)
{
/* ipv6 */
udphdr = (struct udphdr *) ((uint8_t *) ip6hdr + sizeof(*ip6hdr));
}
else
+#endif
{
/* ipv4 */
if (iphdr->protocol != IPPROTO_UDP)
continue;
-#if defined(__DARWIN) || defined(__CYGWIN) || defined(__FreeBSD__)
+#if defined(__DARWIN) || defined(__CYGWIN__) || defined(__FreeBSD__)
udphdr = (struct udphdr *) ((uint8_t *) iphdr + (iphdr->ihl << 2) + 4);
pktlen = (uint32_t) ntohs(udphdr->uh_ulen);
#elif defined ( __HPUX)
pktlen = (uint32_t) ntohs(udphdr->len);
#endif
}
+
timing_update_handler(user_data, &pkthdr->ts);
+
if (src_addr && ntohl(iphdr->saddr) != src_addr)
continue;
+#if defined(__DARWIN) || defined(__CYGWIN__) || defined(__FreeBSD__)
+ if (src_port && ntohs(udphdr->uh_sport) != src_port)
+#else
if (src_port && ntohs(udphdr->source) != src_port)
+#endif
continue;
if (dest_addr && ntohl(iphdr->daddr) != dest_addr)
continue;
+#if defined(__DARWIN) || defined(__CYGWIN__) || defined(__FreeBSD__)
+ if (dest_port && ntohs(udphdr->uh_dport) != dest_port)
+#else
if (dest_port && ntohs(udphdr->dest) != dest_port)
+#endif
continue;
+ if (pkthdr->len != pkthdr->caplen)
+ {
+ fprintf(stderr, "Truncated packet - total len = %d, captured len = %d\n", pkthdr->len, pkthdr->caplen);
+ exit(2);
+ }
body = (const uint8_t *) udphdr;
- body += sizeof(udphdr);
- body_len = pktlen - sizeof(udphdr);
+ body += sizeof(struct udphdr);
+ body_len = pktlen - sizeof(struct udphdr);
packet_handler(user_data, body, body_len);
total_pkts++;
}
- fprintf(stderr, "In pcap %s, npkts %d\n", file, total_pkts);
+ fprintf(stderr, "In pcap %s there were %d accepted packets\n", file, total_pkts);
pcap_close(pcap);
return 0;
#fi
#echo v32bis_tests completed OK
-#./v42_tests >$STDOUT_DEST 2>$STDERR_DEST
-#RETVAL=$?
-#if [ $RETVAL != 0 ]
-#then
-# echo v42_tests failed!
-# exit $RETVAL
-#fi
-#echo v42_tests completed OK
-echo v42_tests not enabled
+./v42_tests >$STDOUT_DEST 2>$STDERR_DEST
+RETVAL=$?
+if [ $RETVAL != 0 ]
+then
+ echo v42_tests failed!
+ exit $RETVAL
+fi
+echo v42_tests completed OK
-#./v42bis_tests.sh >/dev/null
-#RETVAL=$?
-#if [ $RETVAL != 0 ]
-#then
-# echo v42bis_tests failed!
-# exit $RETVAL
-#fi
-#echo v42bis_tests completed OK
-echo v42bis_tests not enabled
+./v42bis_tests.sh >/dev/null
+RETVAL=$?
+if [ $RETVAL != 0 ]
+then
+ echo v42bis_tests failed!
+ exit $RETVAL
+fi
+echo v42bis_tests completed OK
./v8_tests >$STDOUT_DEST 2>$STDERR_DEST
RETVAL=$?
char tag[20];
i = (int) (intptr_t) user_data;
- snprintf(tag, sizeof(tag), "%c: Phase B:", i);
+ snprintf(tag, sizeof(tag), "%c: Phase B", i);
printf("%c: Phase B handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result));
log_rx_parameters(s, tag);
return T30_ERR_OK;
char tag[20];
i = (int) (intptr_t) user_data;
- snprintf(tag, sizeof(tag), "%c: Phase D:", i);
+ snprintf(tag, sizeof(tag), "%c: Phase D", i);
printf("%c: Phase D handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result));
log_transfer_statistics(s, tag);
log_tx_parameters(s, tag);
char tag[20];
i = (intptr_t) user_data;
- snprintf(tag, sizeof(tag), "%c: Phase E:", i);
+ snprintf(tag, sizeof(tag), "%c: Phase E", i);
printf("Phase E handler on channel %c\n", i);
log_transfer_statistics(s, tag);
log_tx_parameters(s, tag);
/*
* SpanDSP - a series of DSP components for telephony
*
- * pcap-parse.c
+ * t38_decode.c
*
* Written by Steve Underwood <steveu@coppice.org>
*
#define OUTPUT_FILE_NAME "t38pcap.tif"
t38_terminal_state_t *t38_state;
+struct timeval now;
static int phase_b_handler(t30_state_t *s, void *user_data, int result)
{
t38_core_state_t *t38_core;
logging_state_t *logging;
int samples;
+ int partial;
static int64_t current = 0;
int64_t when;
int64_t diff;
+ memcpy(&now, ts, sizeof(now));
+
when = ts->tv_sec*1000000LL + ts->tv_usec;
if (current == 0)
current = when;
diff = when - current;
samples = diff/125LL;
- if (samples > 0)
+ while (samples > 0)
{
+ partial = (samples > 160) ? 160 : samples;
+ //fprintf(stderr, "Update time by %d samples\n", partial);
logging = t38_terminal_get_logging_state(t38_state);
- span_log_bump_samples(logging, samples);
+ span_log_bump_samples(logging, partial);
t38_core = t38_terminal_get_t38_core_state(t38_state);
logging = t38_core_get_logging_state(t38_core);
- span_log_bump_samples(logging, samples);
+ span_log_bump_samples(logging, partial);
t30 = t38_terminal_get_t30_state(t38_state);
logging = t30_get_logging_state(t30);
- span_log_bump_samples(logging, samples);
+ span_log_bump_samples(logging, partial);
- t38_terminal_send_timeout(t38_state, samples);
+ t38_terminal_send_timeout(t38_state, partial);
current = when;
+ samples -= partial;
}
return 0;
}
use_ecm = FALSE;
t38_version = 1;
+ options = 0;
input_file_name = INPUT_FILE_NAME;
fill_removal = FALSE;
use_tep = FALSE;
src_port = 0;
dest_addr = 0;
dest_port = 0;
- while ((opt = getopt(argc, argv, "D:d:eFi:m:S:s:tv:")) != -1)
+ while ((opt = getopt(argc, argv, "D:d:eFi:m:oS:s:tv:")) != -1)
{
switch (opt)
{
case 'm':
supported_modems = atoi(optarg);
break;
+ case 'o':
+ options = atoi(optarg);
+ break;
case 'S':
src_addr = atoi(optarg);
break;
if (pcap_scan_pkts(input_file_name, src_addr, src_port, dest_addr, dest_port, timing_update, process_packet, NULL))
exit(2);
+ /* Push the time along, to flush out any remaining activity from the application. */
+ now.tv_sec += 60;
+ timing_update(NULL, &now);
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/
logging = t38_core_get_logging_state(t38_core);
span_log_bump_samples(logging, SAMPLES_PER_CHUNK);
logging = &t38_state_a->audio.modems.v17_rx.logging;
- span_log_bump_samples(logging, t30_len_a);
+ span_log_bump_samples(logging, SAMPLES_PER_CHUNK);
logging = t38_terminal_get_logging_state(t38_state_b);
span_log_bump_samples(logging, SAMPLES_PER_CHUNK);
-/*\r
- * SpanDSP - a series of DSP components for telephony\r
- *\r
- * timezone_tests.c - Timezone handling for time interpretation\r
- *\r
- * Written by Steve Underwood <steveu@coppice.org>\r
- *\r
- * Copyright (C) 2010 Steve Underwood\r
- *\r
- * All rights reserved.\r
- *\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU Lesser General Public License version 2.1,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
- */\r
-\r
-/*! \page timezone_tests_page Timezone handling tests\r
-\section timezone_tests_page_sec_1 What does it do?\r
-*/\r
-\r
-#if defined(HAVE_CONFIG_H)\r
-#include "config.h"\r
-#endif\r
-\r
-#include <stdlib.h>\r
-#include <inttypes.h>\r
-#include <stdio.h>\r
-#include <string.h>\r
-#include <time.h>\r
-\r
-//#if defined(WITH_SPANDSP_INTERNALS)\r
-#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES\r
-//#endif\r
-\r
-#include "spandsp.h"\r
-\r
-#ifndef FALSE\r
-#define FALSE 0\r
-#endif\r
-\r
-#ifndef TRUE\r
-#define TRUE (!FALSE)\r
-#endif\r
-\r
-int main(int argc, char *argv[])\r
-{\r
- struct tm tms;\r
- struct tm *tmp = &tms;\r
- time_t ltime;\r
- tz_t *tz;\r
-\r
- /* Get the current time */\r
- ltime = time(NULL);\r
-\r
- /* Compute the local current time now for several localities, based on Posix tz strings */\r
-\r
- tz = tz_init(NULL, "GMT0GMT0,M10.5.0,M3.5.0");\r
- tz_localtime(tz, tmp, ltime);\r
- printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
- printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
-\r
- tz_init(tz, "CST-8CST-8,M10.5.0,M3.5.0");\r
- tz_localtime(tz, tmp, ltime);\r
- printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
- printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
-\r
- tz_init(tz, "AEST-10AEDT-11,M10.5.0,M3.5.0");\r
- tz_localtime(tz, tmp, ltime);\r
- printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);\r
- printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));\r
-\r
- tz_free(tz);\r
-\r
- return 0;\r
-}\r
-/*- End of function --------------------------------------------------------*/\r
-/*- End of file ------------------------------------------------------------*/\r
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * timezone_tests.c - Timezone handling for time interpretation
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2010 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \page timezone_tests_page Timezone handling tests
+\section timezone_tests_page_sec_1 What does it do?
+*/
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
+
+#include "spandsp.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE (!FALSE)
+#endif
+
+int main(int argc, char *argv[])
+{
+ struct tm tms;
+ struct tm *tmp = &tms;
+ time_t ltime;
+ tz_t *tz;
+
+ /* Get the current time */
+ ltime = time(NULL);
+
+ /* Compute the local current time now for several localities, based on Posix tz strings */
+
+ tz = tz_init(NULL, "GMT0GMT0,M10.5.0,M3.5.0");
+ tz_localtime(tz, tmp, ltime);
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));
+
+ tz_init(tz, "CST-8CST-8,M10.5.0,M3.5.0");
+ tz_localtime(tz, tmp, ltime);
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));
+
+ tz_init(tz, "AEST-10AEDT-11,M10.5.0,M3.5.0");
+ tz_localtime(tz, tmp, ltime);
+ printf("Local time is %02d:%02d:%02d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ printf("Time zone is %s\n", tz_tzname(tz, tmp->tm_isdst));
+
+ tz_free(tz);
+
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
char tag[20];
i = (intptr_t) user_data;
- snprintf(tag, sizeof(tag), "%c: Phase D:", i);
+ snprintf(tag, sizeof(tag), "%c: Phase D", i);
printf("%c: Phase D handler on channel %c - (0x%X) %s\n", i, i, result, t30_frametype(result));
log_transfer_statistics(s, tag);
char tag[20];
i = (intptr_t) user_data;
- snprintf(tag, sizeof(tag), "%c: Phase E:", i);
+ snprintf(tag, sizeof(tag), "%c: Phase E", i);
printf("%c: Phase E handler on channel %c - (%d) %s\n", i, i, result, t30_completion_code_to_str(result));
log_transfer_statistics(s, tag);
log_tx_parameters(s, tag);
#include <sndfile.h>
#include <signal.h>
#if defined(HAVE_FENV_H)
+#define __USE_GNU
#include <fenv.h>
#endif
#include <sndfile.h>
#include <signal.h>
#if defined(HAVE_FENV_H)
+#define __USE_GNU
#include <fenv.h>
#endif
#include <sndfile.h>
#include <signal.h>
#if defined(HAVE_FENV_H)
+#define __USE_GNU
#include <fenv.h>
#endif
*
* Written by Steve Underwood <steveu@coppice.org>
*
- * Copyright (C) 2004 Steve Underwood
+ * Copyright (C) 2004, 2011 Steve Underwood
*
* All rights reserved.
*
*/
#if defined(HAVE_CONFIG_H)
-#include <config.h>
+#include "config.h"
#endif
#include <stdlib.h>
+#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
v42_state_t caller;
v42_state_t answerer;
+int variable_length;
int rx_next[3] = {0};
int tx_next[3] = {0};
static void v42_status(void *user_data, int status)
{
int x;
-
+
x = (intptr_t) user_data;
- printf("%d: Status is '%s' (%d)\n", x, lapm_status_to_str(status), status);
- //if (status == LAPM_DATA)
- // lapm_tx_iframe((x == 1) ? &caller.lapm : &answerer.lapm, "ABCDEFGHIJ", 10, 1);
+ if (status < 0)
+ printf("%d: Status is '%s' (%d)\n", x, signal_status_to_str(status), status);
+ else
+ printf("%d: Status is '%s' (%d)\n", x, lapm_status_to_str(status), status);
}
+/*- End of function --------------------------------------------------------*/
-static void v42_frames(void *user_data, const uint8_t *msg, int len)
+static int v42_get_frames(void *user_data, uint8_t *msg, int len)
{
int i;
+ int j;
+ int k;
int x;
-
+
+ if (len < 0)
+ {
+ v42_status(user_data, len);
+ return 0;
+ }
+ x = (intptr_t) user_data;
+ if (variable_length)
+ {
+ j = make_mask32(len);
+ do
+ k = j & rand();
+ while (k > len);
+ }
+ else
+ {
+ k = len;
+ }
+ for (i = 0; i < k; i++)
+ msg[i] = tx_next[x]++;
+ return k;
+}
+/*- End of function --------------------------------------------------------*/
+
+static void v42_put_frames(void *user_data, const uint8_t *msg, int len)
+{
+ int i;
+ int x;
+
+ if (len < 0)
+ {
+ v42_status(user_data, len);
+ return;
+ }
x = (intptr_t) user_data;
for (i = 0; i < len; i++)
{
if (msg[i] != (rx_next[x] & 0xFF))
+ {
printf("%d: Mismatch 0x%02X 0x%02X\n", x, msg[i], rx_next[x] & 0xFF);
+ exit(2);
+ }
rx_next[x]++;
}
printf("%d: Got frame len %d\n", x, len);
}
+/*- End of function --------------------------------------------------------*/
int main(int argc, char *argv[])
{
int i;
int bit;
- uint8_t buf[1024];
+ int insert_caller_bit_errors;
+ int insert_answerer_bit_errors;
+ int opt;
+
+ insert_caller_bit_errors = FALSE;
+ insert_answerer_bit_errors = FALSE;
+ variable_length = FALSE;
+ while ((opt = getopt(argc, argv, "bv")) != -1)
+ {
+ switch (opt)
+ {
+ case 'b':
+ insert_caller_bit_errors = 11000;
+ insert_answerer_bit_errors = 10000;
+ break;
+ case 'v':
+ variable_length = TRUE;
+ break;
+ default:
+ //usage();
+ exit(2);
+ break;
+ }
+ }
- v42_init(&caller, TRUE, TRUE, v42_frames, (void *) 1);
- v42_init(&answerer, FALSE, TRUE, v42_frames, (void *) 2);
+ v42_init(&caller, TRUE, TRUE, v42_get_frames, v42_put_frames, (void *) 1);
+ v42_init(&answerer, FALSE, TRUE, v42_get_frames, v42_put_frames, (void *) 2);
v42_set_status_callback(&caller, v42_status, (void *) 1);
v42_set_status_callback(&answerer, v42_status, (void *) 2);
- span_log_set_level(&caller.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_DEBUG);
+ v42_restart(&caller);
+ v42_restart(&answerer);
+
+ span_log_set_level(&caller.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_DEBUG);
span_log_set_tag(&caller.logging, "caller");
- span_log_set_level(&caller.lapm.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_DEBUG);
- span_log_set_tag(&caller.lapm.logging, "caller");
- span_log_set_level(&caller.lapm.sched.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_DEBUG);
- span_log_set_tag(&caller.lapm.sched.logging, "caller");
- span_log_set_level(&answerer.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_DEBUG);
+ span_log_set_level(&answerer.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_SHOW_TAG | SPAN_LOG_DEBUG);
span_log_set_tag(&answerer.logging, "answerer");
- span_log_set_level(&answerer.lapm.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_DEBUG);
- span_log_set_tag(&answerer.lapm.logging, "answerer");
- span_log_set_level(&answerer.lapm.sched.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_DEBUG);
- span_log_set_tag(&answerer.lapm.sched.logging, "answerer");
- for (i = 0; i < 100000; i++)
+
+ for (i = 0; i < 1000000; i++)
{
bit = v42_tx_bit(&caller);
+ if (insert_caller_bit_errors && i%insert_caller_bit_errors == 0)
+ bit ^= 1;
v42_rx_bit(&answerer, bit);
bit = v42_tx_bit(&answerer);
- //if (i%10000 == 0)
- // bit ^= 1;
+ if (insert_answerer_bit_errors && i%insert_answerer_bit_errors == 0)
+ bit ^= 1;
v42_rx_bit(&caller, bit);
- span_schedule_update(&caller.lapm.sched, 4);
- span_schedule_update(&answerer.lapm.sched, 4);
- buf[0] = tx_next[1];
- if (lapm_tx(&caller.lapm, buf, 1) == 1)
- tx_next[1]++;
- buf[0] = tx_next[2];
- if (lapm_tx(&answerer.lapm, buf, 1) == 1)
- tx_next[2]++;
}
return 0;
}
*/
#if defined(HAVE_CONFIG_H)
-#include <config.h>
+#include "config.h"
#endif
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
-#include "spandsp.h"
+//#if defined(WITH_SPANDSP_INTERNALS)
+#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES
+//#endif
-#include "spandsp/private/v42bis.h"
+#include "spandsp.h"
#define COMPRESSED_FILE_NAME "v42bis_tests.v42bis"
-#define OUTPUT_FILE_NAME "v42bis_tests.out"
+#define DECOMPRESSED_FILE_NAME "v42bis_tests.out"
int in_octets_to_date = 0;
int out_octets_to_date = 0;
int out_fd;
int do_compression;
int do_decompression;
+ int stutter_compression;
+ int stutter_time;
+ int seg;
+ int opt;
time_t now;
+ const char *argv0;
+ const char *original_file;
+ const char *compressed_file;
+ const char *decompressed_file;
- do_compression = TRUE;
- do_decompression = TRUE;
- if (argc < 2)
+ argv0 = argv[0];
+ do_compression = FALSE;
+ do_decompression = FALSE;
+ stutter_compression = FALSE;
+ while ((opt = getopt(argc, argv, "cds")) != -1)
+ {
+ switch (opt)
+ {
+ case 'c':
+ do_compression = TRUE;
+ break;
+ case 'd':
+ do_decompression = TRUE;
+ break;
+ case 's':
+ stutter_compression = TRUE;
+ break;
+ default:
+ //usage();
+ exit(2);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
{
- fprintf(stderr, "Usage: %s <file>\n", argv[0]);
+ fprintf(stderr, "Usage: %s [-c] [-d] [-s] <in-file> [<out-file>]\n", argv0);
exit(2);
}
if (do_compression)
{
- if ((in_fd = open(argv[1], O_RDONLY)) < 0)
+ original_file = argv[0];
+ compressed_file = COMPRESSED_FILE_NAME;
+ }
+ else
+ {
+ original_file = NULL;
+ compressed_file = argv[0];
+ }
+ decompressed_file = (argc > 1) ? argv[1] : DECOMPRESSED_FILE_NAME;
+ if (do_compression)
+ {
+ stutter_time = rand() & 0x3FF;
+ if ((in_fd = open(argv[0], O_RDONLY)) < 0)
{
- fprintf(stderr, "Error opening file '%s'.\n", argv[1]);
+ fprintf(stderr, "Error opening file '%s'.\n", original_file);
exit(2);
}
- if ((v42bis_fd = open(COMPRESSED_FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
+ if ((v42bis_fd = open(compressed_file, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
{
- fprintf(stderr, "Error opening file '%s'.\n", COMPRESSED_FILE_NAME);
+ fprintf(stderr, "Error opening file '%s'.\n", compressed_file);
exit(2);
}
time(&now);
v42bis_init(&state_a, 3, 512, 6, frame_handler, (void *) (intptr_t) v42bis_fd, 512, data_handler, NULL, 512);
- v42bis_compression_control(&state_a, V42BIS_COMPRESSION_MODE_ALWAYS);
+ span_log_set_level(&state_a.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+ span_log_set_tag(&state_a.logging, "XXX");
+ //v42bis_compression_control(&state_a, V42BIS_COMPRESSION_MODE_ALWAYS);
in_octets_to_date = 0;
out_octets_to_date = 0;
while ((len = read(in_fd, buf, 1024)) > 0)
{
- if (v42bis_compress(&state_a, buf, len))
+ seg = 0;
+ if (stutter_compression)
+ {
+ while ((len - seg) >= stutter_time)
+ {
+ if (v42bis_compress(&state_a, buf + seg, stutter_time))
+ {
+ fprintf(stderr, "Bad return code from compression\n");
+ exit(2);
+ }
+ v42bis_compress_flush(&state_a);
+ seg += stutter_time;
+ stutter_time = rand() & 0x3FF;
+ }
+ }
+ if (v42bis_compress(&state_a, buf + seg, len - seg))
{
fprintf(stderr, "Bad return code from compression\n");
exit(2);
if (do_decompression)
{
/* Now open the files for the decompression. */
- if ((v42bis_fd = open(COMPRESSED_FILE_NAME, O_RDONLY)) < 0)
+ if ((v42bis_fd = open(compressed_file, O_RDONLY)) < 0)
{
- fprintf(stderr, "Error opening file '%s'.\n", COMPRESSED_FILE_NAME);
+ fprintf(stderr, "Error opening file '%s'.\n", compressed_file);
exit(2);
}
- if ((out_fd = open(OUTPUT_FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
+ if ((out_fd = open(decompressed_file, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
{
- fprintf(stderr, "Error opening file '%s'.\n", OUTPUT_FILE_NAME);
+ fprintf(stderr, "Error opening file '%s'.\n", decompressed_file);
exit(2);
}
time(&now);
v42bis_init(&state_b, 3, 512, 6, frame_handler, (void *) (intptr_t) v42bis_fd, 512, data_handler, (void *) (intptr_t) out_fd, 512);
+ span_log_set_level(&state_b.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
+ span_log_set_tag(&state_b.logging, "XXX");
in_octets_to_date = 0;
out_octets_to_date = 0;
while ((len = read(v42bis_fd, buf, 1024)) > 0)
BASE=../test-data/itu/v56ter
-./v42bis_tests ${BASE}/1.TST
+./v42bis_tests -c -d ${BASE}/1.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/1X04.TST
+./v42bis_tests -c -d ${BASE}/1X04.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/1X30.TST
+./v42bis_tests -c -d ${BASE}/1X30.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/2.TST
+./v42bis_tests -c -d ${BASE}/2.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/2X10.TST
+./v42bis_tests -c -d ${BASE}/2X10.TST
RETVAL=$?
+
if [ $RETVAL != 0 ]
then
exit $RETVAL
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/3.TST
+./v42bis_tests -c -d ${BASE}/3.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/3X06.TST
+./v42bis_tests -c -d ${BASE}/3X06.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/4.TST
+./v42bis_tests -c -d ${BASE}/4.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/4X04.TST
+./v42bis_tests -c -d ${BASE}/4X04.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/5.TST
+./v42bis_tests -c -d ${BASE}/5.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
then
exit $RETVAL
fi
-./v42bis_tests ${BASE}/5X16.TST
+./v42bis_tests -c -d ${BASE}/5X16.TST
RETVAL=$?
if [ $RETVAL != 0 ]
then
cd gsm0610
fi
-if [ $1x = --no-exe-runx ]
+if [ $1x == --no-exe-runx ]
then
# Run the .exe files, which should be here
./FR_A.EXE
rm -rf ACTION
rm -rf unpacked
-if [ $1x = --no-exex ]
+if [ $1x == --no-exex ]
then
# We need to prepare the .exe files to be run separately
rm -rf *.INP
version='1.0'>
<xsl:import href="http://docbook.sourceforge.net/release/xsl/current/xhtml/chunk.xsl"/>
<xsl:param name="html.stylesheet">css.css</xsl:param>
-</xsl:stylesheet>
\ No newline at end of file
+</xsl:stylesheet>