diff spandsp-0.0.3/spandsp-0.0.3/src/v42.c @ 5:f762bf195c4b

import spandsp-0.0.3
author Peter Meerwald <pmeerw@cosy.sbg.ac.at>
date Fri, 25 Jun 2010 16:00:21 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spandsp-0.0.3/spandsp-0.0.3/src/v42.c	Fri Jun 25 16:00:21 2010 +0200
@@ -0,0 +1,1443 @@
+/*
+ * 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 General Public License version 2, 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.30 2006/12/01 18:00:48 steveu Exp $
+ */
+
+/* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. */
+
+/*! \file */
+
+#ifdef 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"
+
+#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                       750
+/* Acknowledgement timer - 1 second between SABME's */
+#define T_401                       1000
+/* Replay delay timer (optional) */
+#define T_402                       1000
+/* Inactivity timer (optional). No default - use 10 seconds with no packets */
+#define T_403                       10000
+/* 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);
+
+void lapm_reset(lapm_state_t *s);
+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]\n", (void *) s);
+        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 a [%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 a is %d [%p]\n", s->t401_timer, (void *) s);
+        if (s->t401_timer >= 0)
+        {
+fprintf(stderr, "Deleting T401 a [%p]\n", (void *) s);
+            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\n");
+            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 a [%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 --------------------------------------------------------*/
+
+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 --------------------------------------------------------*/
+
+int lapm_tx(lapm_state_t *s, const void *buf, int len)
+{
+    return queue_write(&s->tx_queue, buf, len);
+}
+/*- End of function --------------------------------------------------------*/
+
+int lapm_release(lapm_state_t *s)
+{
+    s->state = LAPM_RELEASE;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+int lapm_loopback(lapm_state_t *s, int enable)
+{
+    s->state = LAPM_TEST;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+int lapm_break(lapm_state_t *s, int enable)
+{
+    s->state = LAPM_SIGNAL;
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+int lapm_tx_iframe(lapm_state_t *s, const void *buf, int len, int cr)
+{
+    lapm_frame_queue_t *f;
+
+    if ((f = malloc(sizeof(lapm_frame_queue_t) + len + 4)) == NULL)
+    {
+        span_log(&s->logging, SPAN_LOG_FLOW, "Out of memory\n");
+        return -1;
+    }
+    /*endif*/
+
+    if (s->peer_is_originator)
+        lapm_init_header(f->frame, cr);
+    else
+        lapm_init_header(f->frame, !cr);
+    /*endif*/
+    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\n");
+        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 --------------------------------------------------------*/
+
+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]\n", (void *) s);
+        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 --------------------------------------------------------*/
+
+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]\n", (void *) s);
+        span_schedule_del(&s->sched, s->t401_timer);
+        s->t401_timer = -1;
+    }
+    /*endif*/
+    if (s->t403_timer >= 0)
+    {
+fprintf(stderr, "Deleting T403 e\n");
+        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 --------------------------------------------------------*/
+
+void lapm_receive(void *user_data, int ok, 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)
+    {
+        /* Special conditions */
+        switch (len)
+        {
+        case PUTBIT_TRAINING_FAILED:
+            span_log(&s->logging, SPAN_LOG_DEBUG, "Training failed\n");
+            break;
+        case PUTBIT_TRAINING_SUCCEEDED:
+            span_log(&s->logging, SPAN_LOG_DEBUG, "Training succeeded\n");
+            break;
+        case PUTBIT_CARRIER_UP:
+            span_log(&s->logging, SPAN_LOG_DEBUG, "Carrier up\n");
+            break;
+        case PUTBIT_CARRIER_DOWN:
+            span_log(&s->logging, SPAN_LOG_DEBUG, "Carrier down\n");
+            break;
+        case PUTBIT_FRAMING_OK:
+            span_log(&s->logging, SPAN_LOG_DEBUG, "Framing OK\n");
+            break;
+        case PUTBIT_ABORT:
+            span_log(&s->logging, SPAN_LOG_DEBUG, "Abort\n");
+            break;
+        default:
+            span_log(&s->logging, SPAN_LOG_DEBUG, "Eh!\n");
+            break;
+        }
+        /*endswitch*/
+        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]\n", (void *) s);
+                        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\n");
+                        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 --------------------------------------------------------*/
+
+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, "LAPM");
+    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! */
+fprintf(stderr, "LAPM restart T401 a [%p]\n", (void *) s);
+    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 --------------------------------------------------------*/
+
+static void lapm_init(lapm_state_t *s)
+{
+    lapm_restart(s);
+}
+/*- End of function --------------------------------------------------------*/
+
+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 */
+        switch (new_bit)
+        {
+        case PUTBIT_CARRIER_UP:
+            break;
+        case PUTBIT_CARRIER_DOWN:
+        case PUTBIT_TRAINING_SUCCEEDED:
+        case PUTBIT_TRAINING_FAILED:
+            break;
+        default:
+            span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected special 'bit' code %d\n", new_bit);
+            break;
+        }
+        /*endswitch*/
+        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->caller  &&  s->rxstream == 0x145)
+        {
+            s->rx_negotiation_step++;
+        }
+        else if (!s->caller  &&  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->caller  &&  s->rxstream == 0x185)
+        {
+            s->rx_negotiation_step++;
+        }
+        else if (s->caller  &&  s->rxstream == 0x001)
+        {
+            s->rx_negotiation_step++;
+        }
+        else if (!s->caller  &&  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->caller)
+                {
+                    if (s->t400_timer >= 0)
+                    {
+fprintf(stderr, "Deleting T400 h\n");
+                        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->caller)
+    {
+        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\n");
+                        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 --------------------------------------------------------*/
+
+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 --------------------------------------------------------*/
+
+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 --------------------------------------------------------*/
+
+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 --------------------------------------------------------*/
+
+void v42_restart(v42_state_t *s)
+{
+    span_schedule_init(&s->lapm.sched);
+
+    s->lapm.we_are_originator = s->caller;
+    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 --------------------------------------------------------*/
+
+v42_state_t *v42_init(v42_state_t *s, int caller, int detect, v42_frame_handler_t frame_handler, void *user_data)
+{
+    if (frame_handler == NULL)
+        return NULL;
+    /*endif*/
+    memset(s, 0, sizeof(*s));
+    s->caller = caller;
+    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);
+    if (queue_create(&(s->lapm.tx_queue), 16384, 0) < 0)
+        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 --------------------------------------------------------*/
+
+int v42_release(v42_state_t *s)
+{
+    free(s);
+    return 0;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.