Mercurial > hg > audiostuff
diff spandsp-0.0.6pre17/src/v42.c @ 4:26cd8f1ef0b1
import spandsp-0.0.6pre17
author | Peter Meerwald <pmeerw@cosy.sbg.ac.at> |
---|---|
date | Fri, 25 Jun 2010 15:50:58 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spandsp-0.0.6pre17/src/v42.c Fri Jun 25 15:50:58 2010 +0200 @@ -0,0 +1,1432 @@ +/* + * SpanDSP - a series of DSP components for telephony + * + * v42.c + * + * Written by Steve Underwood <steveu@coppice.org> + * + * Copyright (C) 2004 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. + * + * $Id: v42.c,v 1.51 2009/11/04 15:52:06 steveu Exp $ + */ + +/* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. */ + +/*! \file */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include <errno.h> + +#include "spandsp/telephony.h" +#include "spandsp/logging.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 T_WAIT_MIN 2000 +#define T_WAIT_MAX 10000 +/* Detection phase timer */ +#define T_400 750000 +/* Acknowledgement timer - 1 second between SABME's */ +#define T_401 1000000 +/* Replay delay timer (optional) */ +#define T_402 1000000 +/* 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 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); + +SPAN_DECLARE(void) lapm_reset(lapm_state_t *s); +SPAN_DECLARE(void) lapm_restart(lapm_state_t *s); + +static void lapm_link_down(lapm_state_t *s); + +static __inline__ void lapm_init_header(uint8_t *frame, int command) +{ + /* 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; +} +/*- End of function --------------------------------------------------------*/ + +static int lapm_tx_frame(lapm_state_t *s, uint8_t *frame, int len) +{ + 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; +} +/*- End of function --------------------------------------------------------*/ + +static void t400_expired(span_sched_state_t *ss, void *user_data) +{ + 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*/ +} +/*- End of function --------------------------------------------------------*/ + +static void lapm_send_ua(lapm_state_t *s, int pfbit) +{ + uint8_t frame[3]; + + 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); +} +/*- End of function --------------------------------------------------------*/ + +static void lapm_send_sabme(span_sched_state_t *ss, void *user_data) +{ + lapm_state_t *s; + uint8_t frame[3]; + + 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) + { + /* 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; + } + /*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); +} +/*- End of function --------------------------------------------------------*/ + +static int lapm_ack_packet(lapm_state_t *s, int num) +{ + lapm_frame_queue_t *f; + lapm_frame_queue_t *prev; + + for (prev = NULL, f = s->txqueue; f; prev = f, f = f->next) + { + if ((f->frame[1] >> 1) == num) + { + /* 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; + } + /*endif*/ + } + /*endfor*/ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +static void lapm_ack_rx(lapm_state_t *s, int ack) +{ + int i; + int cnt; + + /* 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"); + return; + } + /*endif*/ + + /* 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 + { + 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); + } + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +static void lapm_reject(lapm_state_t *s) +{ + 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); +} +/*- End of function --------------------------------------------------------*/ + +static void lapm_rr(lapm_state_t *s, int pfbit) +{ + 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); +} +/*- End of function --------------------------------------------------------*/ + +static void t401_expired(span_sched_state_t *ss, void *user_data) +{ + 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) + { + /* 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) + { + /* 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); + } + /*endif*/ + } + else + { + span_log(&s->logging, SPAN_LOG_FLOW, "Timer T_401 expired. Nothing to send...\n"); + } + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(const char *) lapm_status_to_str(int status) +{ + 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 "???"; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) lapm_tx(lapm_state_t *s, const void *buf, int len) +{ + return queue_write(s->tx_queue, buf, len); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) lapm_release(lapm_state_t *s) +{ + s->state = LAPM_RELEASE; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) lapm_loopback(lapm_state_t *s, int enable) +{ + s->state = LAPM_TEST; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) lapm_break(lapm_state_t *s, int enable) +{ + s->state = LAPM_SIGNAL; + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) lapm_tx_iframe(lapm_state_t *s, const void *buf, int len, int cr) +{ + lapm_frame_queue_t *f; + + if ((f = malloc(sizeof(*f) + len + 4)) == NULL) + { + span_log(&s->logging, SPAN_LOG_FLOW, "Out of memory\n"); + return -1; + } + /*endif*/ + + 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; +} +/*- End of function --------------------------------------------------------*/ + +static void t403_expired(span_sched_state_t *ss, void *user_data) +{ + 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); +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) lapm_dump(lapm_state_t *s, const uint8_t *frame, int len, int showraw, int txrx) +{ + 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*/ +} +/*- End of function --------------------------------------------------------*/ + +static void lapm_link_up(lapm_state_t *s) +{ + uint8_t buf[1024]; + int len; + + 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)) + { + if ((len = queue_read(s->tx_queue, buf, s->n401)) > 0) + lapm_tx_iframe(s, buf, len, TRUE); + /*endif*/ + } + /*endif*/ + if (s->t401_timer >= 0) + { +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; + } + /*endif*/ + /* Start the T403 timer */ +fprintf(stderr, "Setting T403 g\n"); + s->t403_timer = span_schedule_event(&s->sched, T_403, t403_expired, s); +} +/*- End of function --------------------------------------------------------*/ + +static void lapm_link_down(lapm_state_t *s) +{ + lapm_reset(s); + + if (s->status_callback) + s->status_callback(s->status_callback_user_data, s->state); + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) lapm_reset(lapm_state_t *s) +{ + 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) + { +fprintf(stderr, "Deleting T403 e %d\n", s->t403_timer); + span_schedule_del(&s->sched, s->t403_timer); + s->t403_timer = -1; + } + /*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; ) + { + p = f; + f = f->next; + free(p); + } + /*endfor*/ + s->txqueue = NULL; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE_NONSTD(void) lapm_receive(void *user_data, const uint8_t *frame, int len, int ok) +{ + 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) + { + /* Special conditions */ + span_log(&s->logging, SPAN_LOG_DEBUG, "V.42 rx status is %s (%d)\n", signal_status_to_str(len), len); + return; + } + /*endif*/ + + 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) + return; + /*endif*/ + octet++; + switch (frame[octet] & LAPM_FRAMETYPE_MASK) + { + 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) + { + span_log(&s->logging, SPAN_LOG_FLOW, "!! Received short I-frame (expected 4, got %d)\n", len); + break; + } + /*endif*/ + /* Make sure this is a valid packet */ + if ((frame[1] >> 1) == s->next_expected_frame) + { + /* 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*/ + } + else + { + 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*/ + } + /*endif*/ + break; + case LAPM_FRAMETYPE_S: + if (s->state != LAPM_DATA) + { + span_log(&s->logging, SPAN_LOG_FLOW, "!! Got S-frame while link down\n"); + break; + } + /*endif*/ + if (len < 4) + { + span_log(&s->logging, SPAN_LOG_FLOW, "!! Received short S-frame (expected 4, got %d)\n", len); + break; + } + /*endif*/ + s_field = frame[octet] & 0xEC; + switch (s_field) + { + case 0x00: + /* RR (receive ready) */ + s->busy = FALSE; + /* Acknowledge frames as necessary */ + lapm_ack_rx(s, frame[2] >> 1); + if ((frame[2] & 0x01)) + { + /* 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; + } + /*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)) + { + /* If it has the poll bit set, send an appropriate supervisory response */ + lapm_rr(s, 1); + } + /*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) + { + 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*/ + } + /*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) + { + span_log(&s->logging, SPAN_LOG_FLOW, "!! Received too short unnumbered frame\n"); + break; + } + /*endif*/ + m_field = frame[octet] & 0xEC; + switch (m_field) + { + case 0x00: + /* UI (unnumbered information) */ + switch (frame[++octet] & 0x7F) + { + 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"); + break; + default: + /* Unknown */ + span_log(&s->logging, SPAN_LOG_FLOW, "Unknown UI type\n"); + 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; + } + /*endswitch*/ +} +/*- End of function --------------------------------------------------------*/ + +static void lapm_hdlc_underflow(void *user_data) +{ + lapm_state_t *s; + uint8_t buf[1024]; + int len; + + s = (lapm_state_t *) user_data; + span_log(&s->logging, SPAN_LOG_FLOW, "HDLC underflow\n"); + if (s->state == LAPM_DATA) + { + if (!queue_empty(s->tx_queue)) + { + if ((len = queue_read(s->tx_queue, buf, s->n401)) > 0) + lapm_tx_iframe(s, buf, len, TRUE); + /*endif*/ + } + /*endif*/ + } + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) lapm_restart(lapm_state_t *s) +{ +#if 0 + if (s->state != LAPM_RELEASE) + { + span_log(&s->logging, SPAN_LOG_FLOW, "!! lapm_restart: Not in 'Link Connection Released' state\n"); + 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); +} +/*- End of function --------------------------------------------------------*/ + +#if 0 +static void lapm_init(lapm_state_t *s) +{ + lapm_restart(s); +} +/*- 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"; + /* V.42 OK E , 8-16 ones, C, 8-16 ones */ + //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"; + + /* There may be no negotiation, so we need to process this data through the + HDLC receiver as well */ + if (new_bit < 0) + { + /* Special conditions */ + span_log(&s->logging, SPAN_LOG_DEBUG, "V.42 rx status is %s (%d)\n", signal_status_to_str(new_bit), new_bit); + return; + } + /*endif*/ + new_bit &= 1; + s->rxstream = (s->rxstream << 1) | new_bit; + switch (s->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; + break; + case 1: + /* Look for the first character */ + if (++s->rxbits < 9) + break; + /*endif*/ + s->rxstream &= 0x3FF; + if (s->calling_party && s->rxstream == 0x145) + { + s->rx_negotiation_step++; + } + else if (!s->calling_party && s->rxstream == 0x111) + { + s->rx_negotiation_step++; + } + else + { + s->rx_negotiation_step = 0; + } + /*endif*/ + s->rxbits = 0; + s->rxstream = ~0; + break; + case 2: + /* Look for 8 to 16 ones */ + s->rxbits++; + if (new_bit) + break; + /*endif*/ + if (s->rxbits >= 8 && s->rxbits <= 16) + s->rx_negotiation_step++; + else + s->rx_negotiation_step = 0; + /*endif*/ + s->rxbits = 0; + s->rxstream = ~1; + break; + case 3: + /* Look for the second character */ + if (++s->rxbits < 9) + break; + /*endif*/ + s->rxstream &= 0x3FF; + if (s->calling_party && s->rxstream == 0x185) + { + s->rx_negotiation_step++; + } + else if (s->calling_party && s->rxstream == 0x001) + { + s->rx_negotiation_step++; + } + else if (!s->calling_party && s->rxstream == 0x113) + { + s->rx_negotiation_step++; + } + else + { + s->rx_negotiation_step = 0; + } + /*endif*/ + s->rxbits = 0; + s->rxstream = ~0; + break; + case 4: + /* Look for 8 to 16 ones */ + s->rxbits++; + if (new_bit) + break; + /*endif*/ + if (s->rxbits >= 8 && s->rxbits <= 16) + { + if (++s->rxoks >= 2) + { + /* HIT */ + s->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*/ + } + else + { + s->odp_seen = TRUE; + } + /*endif*/ + break; + } + /*endif*/ + s->rx_negotiation_step = 1; + s->rxbits = 0; + s->rxstream = ~1; + } + else + { + s->rx_negotiation_step = 0; + s->rxbits = 0; + s->rxstream = ~0; + } + /*endif*/ + break; + case 5: + /* Parked */ + break; + } + /*endswitch*/ +} +/*- End of function --------------------------------------------------------*/ + +static int v42_support_negotiation_tx_bit(v42_state_t *s) +{ + int bit; + + if (s->calling_party) + { + if (s->txbits <= 0) + { + s->txstream = 0x3FE22; + s->txbits = 36; + } + else if (s->txbits == 18) + { + s->txstream = 0x3FF22; + } + /*endif*/ + bit = s->txstream & 1; + s->txstream >>= 1; + s->txbits--; + } + else + { + if (s->odp_seen && s->txadps < 10) + { + if (s->txbits <= 0) + { + if (++s->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; + } + else + { + s->txstream = 0x3FE8A; + s->txbits = 36; + } + /*endif*/ + } + else if (s->txbits == 18) + { + s->txstream = 0x3FE86; + } + /*endif*/ + bit = s->txstream & 1; + s->txstream >>= 1; + s->txbits--; + } + else + { + bit = 1; + } + /*endif*/ + } + /*endif*/ + return bit; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) v42_rx_bit(void *user_data, int bit) +{ + v42_state_t *s; + + s = (v42_state_t *) user_data; + if (s->lapm.state == LAPM_DETECT) + negotiation_rx_bit(s, bit); + else + hdlc_rx_put_bit(&s->lapm.hdlc_rx, bit); + /*endif*/ +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) v42_tx_bit(void *user_data) +{ + v42_state_t *s; + int bit; + + s = (v42_state_t *) user_data; + if (s->lapm.state == LAPM_DETECT) + bit = v42_support_negotiation_tx_bit(s); + else + bit = hdlc_tx_get_bit(&s->lapm.hdlc_tx); + /*endif*/ + return bit; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) v42_set_status_callback(v42_state_t *s, v42_status_func_t callback, void *user_data) +{ + s->lapm.status_callback = callback; + s->lapm.status_callback_user_data = user_data; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(void) v42_restart(v42_state_t *s) +{ + span_schedule_init(&s->lapm.sched); + + 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); + s->lapm.state = LAPM_DETECT; + } + else + { + s->lapm.state = LAPM_ESTABLISH; + } + /*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) +{ + int alloced; + + if (frame_handler == NULL) + return NULL; + /*endif*/ + alloced = FALSE; + if (s == NULL) + { + if ((s = (v42_state_t *) malloc(sizeof(*s))) == 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; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) v42_release(v42_state_t *s) +{ + return 0; +} +/*- End of function --------------------------------------------------------*/ + +SPAN_DECLARE(int) v42_free(v42_state_t *s) +{ + free(s); + return 0; +} +/*- End of function --------------------------------------------------------*/ +/*- End of file ------------------------------------------------------------*/