diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2015-06-24 04:23:46 +0200 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2015-06-24 10:03:18 +0200 |
commit | e5067d7cd967cb17067de24a162306b79f432b20 (patch) | |
tree | 541f101762df32a5742bec354009986a96d8e564 /net/ax25/ax25_lapb.c | |
parent | 86a981e836404006efc35881ebf3d5ae36925e82 (diff) |
Import newax25-2.4.3.patch.1.bz2HEADnewax25-2.4.3-1
And cleanup the *.orig and *.rej files and whitespace errors that are part
of the original patch.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'net/ax25/ax25_lapb.c')
-rw-r--r-- | net/ax25/ax25_lapb.c | 789 |
1 files changed, 789 insertions, 0 deletions
diff --git a/net/ax25/ax25_lapb.c b/net/ax25/ax25_lapb.c new file mode 100644 index 000000000..849eeb44d --- /dev/null +++ b/net/ax25/ax25_lapb.c @@ -0,0 +1,789 @@ +/* + * ax25_lapb.c: NEW-AX.25 state machine + * + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF) + * + * Comment: Most of this code is based on the SDL diagrams published in the + * ARRL Computer Networking Conference papers. The diagrams have mi + * in them, but are mostly correct. Before you modify the code coul + * read the SDL diagrams as the code is not obvious and probably ve + * easy to break; + * Rewritten from scratch by Matthias in 1998. + * + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/socket.h> +#include <linux/time.h> +#include <net/ax25.h> +#include <net/ax25dev.h> +#include <net/tcp.h> + +#include "ax25_ddi.h" +#include "ax25_vj.h" +#include "ax25_route.h" +#include "ax25_in.h" +#include "ax25_lapb.h" +#include "ax25_core.h" +#include "ax25_subr.h" +#include "ax25_timer.h" + +/* + * Calculate the Round Trip Time + */ +static void ax25_calculate_rtt(ax25_cb *ax25, unsigned long rtt_raw) +{ + switch (ax25->backoff) { + case 0: + ax25->rtt = rtt_raw; + break; + + case 1: + case 2: + ax25->rtt = (9 * ax25->rtt + rtt_raw) / 10; + break; + } +} + +/* + * This routine purges the input queue of those frames that have been + * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the + * SDL diagram. + */ +static void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) +{ + struct sk_buff *skb; + /* + * Remove all the ack-ed frames from ack_queue. + */ + while (ax25->va != nr && (skb = skb_dequeue(&ax25->ack_queue)) != NULL) { + ax25->va = (ax25->va + 1) & ax25->seqmask; + kfree_skb(skb); + + if (ax25->vs_rtt == ax25->va) { + ax25_calculate_rtt(ax25, jiffies - ax25->rtt_timestamp + 1); + ax25->vs_rtt = -1; + } + } + if (ax25->condition & AX25_COND_SETUP) + ax25_clr_cond(ax25, AX25_COND_SETUP); +} + +/* + * This routine decides whether to restart T1 or not on an incoming + * frame. If the frame acks all outstanding frames, T1 is + * stopped, T3 is started. Else if it acks at least one + * outstanding frame T1 is restarted. + */ +static void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) +{ + write_lock(&ax25->timer_lock); + if (ax25->va != nr) { + /* at least one frame acked */ + ax25_frames_acked(ax25, nr); + if ((ax25->vs != nr) && !(DAMA_STATE(ax25) & DAMA_SLAVE)) + ax25->wrt_timer = ax25->t1; + } + if (ax25->vs == ax25->va) ax25->wrt_timer = ax25->t3; + write_unlock(&ax25->timer_lock); +} + +/* + * Due to the changed retransmission handling frames are not + * requeued from the ack_queue into the write queue. All + * retransmission stuff is done in ax25_dev_timer() now. This + * function is for clarity only, it will either be removed + * completely or turn into an inline function. + */ +static void ax25_requeue_frames(ax25_cb *ax25) +{ + /* + * reset the send sequence counter. + */ + ax25->vs = ax25->va; +} + +/* + * Validate that the value of nr is between va and vs. Return true or + * false for testing. + */ +static int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) +{ + unsigned short ds; + unsigned short dr; + + ds = ax25->vs_max - ax25->va; + ds &= ax25->seqmask; + + dr = nr - ax25->va; + dr &= ax25->seqmask; + + return (dr <= ds); +} + +/* + * link reset handling. This is a rather complex routine, as it + * has to cope with a tricky situation in the protocol flow. + */ +static int ax25_reset_link(ax25_cb* ax25, struct sk_buff* skb) +{ + ax25_cb* peer = ax25->peer; + + write_lock(&ax25->timer_lock); + ax25->wrt_timer = 0; + ax25->ack_timer = 0; + ax25->idletimer = ax25->idle; + ax25->n2count = 0; + write_unlock(&ax25->timer_lock); + + /* + * peer just didn't get our UA, handle gracefully. + */ + if (ax25->condition & AX25_COND_SETUP) { + ax25_requeue_frames(ax25); + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + return 0; + } + + /* + * Ok, this is a *real* reset. If we have an attached socket, + * disconnect it and produce a new, accept-ready one. + */ + if (ax25->sk != NULL) { + struct sock* make; + struct sock* sk; + ax25_cb* tmp = ax25_find_listener(&ax25->addr.src, 0, ax25->device); + + printk(KERN_DEBUG "ax25_lapb.c: resetting socket\n"); + + if (!tmp || !(sk = tmp->sk) + || sk->ack_backlog == sk->max_ack_backlog + || (make = ax25_make_new(sk, ax25->device)) == NULL) + { + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25->state = AX25_STATE_0; + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + return 0; + } + + /* set the destination address */ + make->protinfo.ax25->addr = ax25->addr; + + /* + * ax25_make_new() has produced a new ax25_cb, attached to + * a struct sock, we just throw away the old one the regular + * way and insert the new one into the appropriate device queue + * + * It should be enough to disconnect() the old control block, + * as this will wake up the application which then in turn + * does a close() on the respective socket descriptor. + */ + ax25_remove_cb(ax25); + ax25_disconnect(ax25, ECONNRESET); + ax25_close_socket(ax25->sk, ECONNRESET); + + /* + * done with the old one, now for the new one + */ + write_lock(&ax25->timer_lock); + ax25 = make->protinfo.ax25; + ax25->state = AX25_STATE_3; + ax25->wrt_timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->vs_rtt = -1; + write_unlock(&ax25->timer_lock); + + make->pair = sk; + skb->sk = make; + skb_queue_head(&sk->receive_queue, skb); + sk->ack_backlog++; + ax25_dev_insert_cb(ax25); + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + sk->data_ready(sk, skb->len); + return 1; + } + + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->vl = 0; + + /* + * check if we have a peer connection to notify + */ + if (peer) { + + printk(KERN_DEBUG "ax25_lapb.c: resetting digipeated connection, state:%d peer state:%d\n", + ax25->state, peer->state); + + ax25_clear_queues(ax25); + + write_lock(&ax25->timer_lock); + + ax25->killtimer = 30 * AX25_SLOWHZ; + ax25->wrt_timer = 0; + ax25->ack_timer = 0; + ax25->state = AX25_STATE_0; + ax25->condition = AX25_COND_SETUP; + + write_unlock(&ax25->timer_lock); + + ax25_clear_queues(peer); + + write_lock(&peer->timer_lock); + + peer->wrt_timer = 0; + peer->ack_timer = 0; + peer->vs = 0; + peer->va = 0; + peer->vr = 0; + peer->vl = 0; + peer->condition = AX25_COND_SETUP; + peer->state = AX25_STATE_1; + + write_unlock(&peer->timer_lock); + + if (peer->seqmask == AX25_SEQMASK) + ax25_tx_command(peer, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(peer, AX25_SABME, AX25_POLLON); + return 0; + } + + printk(KERN_DEBUG "ax25_lapb.c: resetting protocol uplink\n"); + + if (ax25->slcomp != NULL) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25->wrt_timer = ax25->t3; + ax25->condition = AX25_COND_SETUP; + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + + return 0; +} + +/* + * State machine for state 0, Disconnected State. + * we need this now for the hop-2-hop acknowledgement + */ +static int ax25_state0_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (peer) { + if (peer->condition & AX25_COND_SETUP) { + if (peer->seqmask == AX25_SEQMASK) + ax25_tx_command(peer, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(peer, AX25_SABME, AX25_POLLON); + ax25->killtimer = 30 * AX25_SLOWHZ; + } else { + struct net_device *dev; + if ((dev = ax25_rt_set_addr(&peer->addr, &pkt->addr, ax25->device, peer->device)) == NULL) + ax25_tx_command(ax25, AX25_DM, AX25_POLLON); + else { + ax25_fillin_cb(peer, dev); + ax25_reset_link(ax25, skb); + } + } + } else if (!ax25->sk) + ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + default: + if (pkt->pf && pkt->cmdrsp == AX25_COMMAND) + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + break; + } + return 0; /* we never queue */ +} + + +/* + * State machine for state 1, Awaiting Connection State. + * The handling of the timer(s) is in file ax25_timer.c. + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + if (peer || ax25->sk == NULL) { + if (ax25->seqmask != AX25_SEQMASK) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, pkt->pf); + if (peer) { + peer->state = AX25_STATE_3; + peer->vs_rtt = -1; + ax25_tx_response(peer, AX25_UA, pkt->pf); + } + } + break; + + case AX25_SABME: + if (ax25->seqmask == AX25_ESEQMASK && (peer || ax25->sk == NULL)) { + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, pkt->pf); + if (peer) { + peer->state = AX25_STATE_3; + peer->vs_rtt = -1; + ax25_tx_response(peer, AX25_UA, pkt->pf); + } + } + break; + + case AX25_DISC: + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + case AX25_UA: + if (pkt->pf) { + write_lock(&ax25->timer_lock); + ax25->state = AX25_STATE_3; + ax25->wrt_timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->killtimer = 0; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->vl = 0; + ax25->vs_rtt = -1; + ax25->n2count = 0; + ax25->tx_cmd = 0; + ax25->tx_rsp = 0; + write_unlock(&ax25->timer_lock); + + if (peer != NULL) { + write_lock(&peer->timer_lock); + peer->wrt_timer = peer->t3; + peer->idletimer = 0; + peer->killtimer = 0; + peer->vs = 0; + peer->va = 0; + peer->vr = 0; + peer->vl = 0; + peer->vs_rtt = -1; + peer->n2count = 0; + peer->state = AX25_STATE_3; + ax25_clr_cond(peer, AX25_COND_STATE_CHANGE); + ax25_tx_response(peer, AX25_UA, pkt->pf); + write_unlock(&peer->timer_lock); + } else if (ax25->sk != NULL) { + ax25->sk->state = TCP_ESTABLISHED; + /* For WAIT_SABM connections we will produce an accept ready socket here */ + ax25->sk->state_change(ax25->sk); + } + + ax25_kick(ax25); + } + break; + + case AX25_DM: + if (peer != NULL) { + ax25_set_cond(peer, AX25_COND_STATE_CHANGE); + ax25_disconnect(peer, 0); + ax25_tx_response(peer, AX25_DM, pkt->pf); + } else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNREFUSED); + ax25_disconnect(ax25, ECONNREFUSED); + break; + + default: + break; + } + + return 0; +} + +/* + * State machine for state 2, Awaiting Release State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + int queued = 0; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + case AX25_DM: + case AX25_UA: + if (pkt->pf) { + if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_disconnect(ax25, 0); + ax25->killtimer=0; + ax25->tx_cmd=0; + } + break; + + case AX25_I: + case AX25_REJ: + case AX25_RNR: + case AX25_RR: + if (pkt->pf && pkt->cmdrsp == AX25_COMMAND) + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + break; + + default: + break; + } + return queued; +} + +/* + * State machine for state 3, Connected State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + int queued = 0; + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_UA, pkt->pf); + break; + + case AX25_DM: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNRESET); + ax25_disconnect(ax25, ECONNRESET); + break; + + case AX25_RR: + case AX25_RNR: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_check_iframes_acked(ax25, pkt->nr); + + if (pkt->frametype == AX25_RR) + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + else + ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY); + + if (pkt->pf) { + if (pkt->cmdrsp == AX25_COMMAND) { + ax25_enquiry_response(ax25); + } + if (ax25->vs == ax25->va) { + ax25->n2count=0; + } else { + ax25_requeue_frames(ax25); + if (DAMA_STATE(ax25) & DAMA_SLAVE) { + /* + * in DAMA mode we use n2count in state 3 + * to track the number of DAMA polls + * ack'ing only part of our I frames. This + * is neccessary to prevent hangs with buggy + * masters. + */ + if (++ax25->n2count == ax25->n2) { + ax25->vs_rtt = -1; + ax25->n2count = 1; + ax25_start_t1(ax25); + ax25->state = AX25_STATE_4; + ax25_transmit_enquiry(ax25); + } + } + } + } + + ax25_kick(ax25); + break; + + case AX25_REJ: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + ax25_check_iframes_acked(ax25, pkt->nr); + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + ax25_requeue_frames(ax25); + ax25_kick(ax25); + AX25_PTR(ax25->device)->rx_rejects++; + break; + + case AX25_I: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_check_iframes_acked(ax25, pkt->nr); + + if (pkt->ns == ax25->vr) { + if ((queued = ax25_rx_iframe(ax25, skb)) != 0) { + ax25_reseq_update(ax25, skb, pkt->ns); + ax25->vr = (ax25->vr+1) & ax25->seqmask; + ax25_reseq_out(ax25); + ax25_clr_cond(ax25, AX25_COND_REJECT); + } + + if (pkt->pf) { + ax25_enquiry_response(ax25); +#ifdef notdef + } else if (((pkt->ns+2) & ax25->seqmask) == ax25->vl) { + /* + * optimization (fast ack): + * If our peer's TX window is sent out + * completely we ack immediately without + * waiting for T2. + */ + ax25_timeout_response(ax25); + } else { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } +#else + } else ax25_timeout_response(ax25); +#endif + } else { + /* frame is not in sequence */ + queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf); + if (ax25->condition & AX25_COND_REJECT) { + if (pkt->pf) { + ax25_enquiry_response(ax25); + } else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + ax25->ack_timer = 0; + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); + ax25_set_cond(ax25, AX25_COND_REJECT); + ax25_tx_response(ax25, AX25_REJ, pkt->pf); + } + } + ax25_kick(ax25); + break; + + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_nr_error_recovery(ax25); + break; + + default: + break; + } + + return queued; +} + +/* + * State machine for state 4, Timer Recovery State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + int queued = 0; + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_UA, pkt->pf); + break; + + case AX25_DM: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNRESET); + ax25_disconnect(ax25, ECONNRESET); + break; + + case AX25_RR: + case AX25_RNR: + case AX25_REJ: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_frames_acked(ax25, pkt->nr); + + if (pkt->frametype == AX25_RR) + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + else if (pkt->frametype == AX25_REJ) { + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + AX25_PTR(ax25->device)->rx_rejects++; + } else + ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY); + + if (pkt->pf) { + if (pkt->cmdrsp == AX25_RESPONSE) { + write_lock(&ax25->timer_lock); + if (ax25->vs == ax25->va) { + ax25->n2count = 0; + ax25->wrt_timer = 0; + ax25->state = AX25_STATE_3; + ax25->tx_cmd = 0; + ax25->wrt_timer = ax25->t3; + } + write_unlock(&ax25->timer_lock); + ax25_requeue_frames(ax25); + } else { + if (DAMA_STATE(ax25) & DAMA_SLAVE) + ax25_requeue_frames(ax25); + ax25_enquiry_response(ax25); + } + } + ax25_kick(ax25); + break; + + case AX25_I: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + ax25_frames_acked(ax25, pkt->nr); + write_lock(&ax25->timer_lock); + + if (pkt->ns == ax25->vr) { + /* frame is in sequence */ + if ((queued = ax25_rx_iframe(ax25, skb)) != 0) { + ax25_reseq_update(ax25, skb, pkt->ns); + ax25->vr = (ax25->vr+1) & ax25->seqmask; + ax25_reseq_out(ax25); + ax25_clr_cond(ax25, AX25_COND_REJECT); + } + if (pkt->pf) { + ax25_enquiry_response(ax25); + } else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + /* frame is not in sequence */ + queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf); + if (ax25->condition & AX25_COND_REJECT) { + if (pkt->pf) + ax25_enquiry_response(ax25); + else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + ax25->ack_timer = 0; + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); + ax25_set_cond(ax25, AX25_COND_REJECT); + ax25_tx_response(ax25, AX25_REJ, pkt->pf); + } + } + + write_unlock(&ax25->timer_lock); + break; + + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_nr_error_recovery(ax25); + break; + + default: + break; + } + + return queued; +} + +static int ax25_state_nop(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + printk(KERN_DEBUG "ax25_state_nop()\n"); + return 0; +} + +ax25_statefunc_t ax25_lapb_table[] = +{ + ax25_state0_machine, + ax25_state1_machine, + ax25_state2_machine, + ax25_state3_machine, + ax25_state4_machine, + ax25_state_nop +}; |