/* * ax25_subr.c: Subroutines for NEW-AX.25 state machine * * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KL * Alan Cox (GW4PTS), Joerg (DL1BKE), et al * * 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; * * 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 #include #include #include #include #include #include "ax25_ddi.h" #include "ax25_in.h" #include "ax25_subr.h" #include "ax25_core.h" #include "ax25_vj.h" #include "ax25_timer.h" /* * This routine purges all the queues of frames. */ void ax25_clear_queues(ax25_cb *ax25) { int i; skb_queue_purge(&ax25->frag_queue); skb_queue_purge(&ax25->rcv_queue); skb_queue_purge(&ax25->write_queue); skb_queue_purge(&ax25->ack_queue); for (i = 0; i <= AX25_SEQMASK; i++) { struct sk_buff *skb; if ((skb = ax25->reseq[i].skb) != NULL) { ax25->reseq[i].skb = NULL; ax25->reseq[i].csum = 0; kfree_skb(skb); } } } /* * this is new */ void ax25_tx_command(ax25_cb *ax25, int frametype, int poll_bit) { if (poll_bit) frametype |= 0x100; ax25->tx_cmd = frametype; ax25_kick(ax25); } void ax25_tx_response(ax25_cb *ax25, int frametype, int poll_bit) { if (poll_bit) frametype |= 0x100; ax25->tx_rsp = frametype; ax25_kick(ax25); } /* * Send a 'DM' to an unknown connection attempt, or an invalid caller. * * Note: src here is the sender, thus it's the target of the DM */ void ax25_return_dm(struct net_device *dev, ax25_pktinfo *pkt) { struct sk_buff *skb; unsigned char *dptr; int sizeof_addr; ax25_addr_t addr; sizeof_addr = ax25_sizeof_addr(&pkt->addr); if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + sizeof_addr + 1, GFP_ATOMIC)) == NULL) return; /* Next SABM will get DM'd */ skb_reserve(skb, AX25_BPQ_HEADER_LEN + sizeof_addr); ax25_invert_addr(&pkt->addr, &addr); *skb_put(skb, 1) = AX25_DM|AX25_PF; /* * Do the address ourselves */ skb->nh.raw = skb->data; dptr = skb_push(skb, sizeof_addr); dptr += ax25_build_addr(dptr, &addr, AX25_RESPONSE, AX25_SEQMASK); ax25_send_unproto(skb, dev); } /* * Exponential backoff for AX.25 */ unsigned short ax25_calculate_t1(ax25_cb *ax25) { int n; int t = 1; switch (ax25->backoff) { case 0: break; case 1: t += 2 * ax25->n2count; break; case 2: t <<= (ax25->n2count < 8 ? ax25->n2count : 8); break; } n = (t * ax25->rtt); if (n > AX25_T1CLAMPHI) return AX25_T1CLAMPHI; if (n < AX25_T1CLAMPLO) return AX25_T1CLAMPLO; return n; } void ax25_close_socket(struct sock *sk, int reason) { sk->err = reason; sk->shutdown = SHUTDOWN_MASK; sk->state = TCP_CLOSE; sk->state_change(sk); sk->dead = 1; } void ax25_disconnect(ax25_cb *ax25, int reason) { ax25_clear_queues(ax25); ax25_link_failed(ax25, reason); write_lock(&ax25->timer_lock); ax25->wrt_timer = 0; ax25->ack_timer = 0; ax25->idletimer = 0; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; ax25->vl = 0; ax25->killtimer = 90 * AX25_SLOWHZ; ax25->state = AX25_STATE_0; write_unlock(&ax25->timer_lock); if (ax25->peer && ax25->peer->state > AX25_STATE_2) ax25_set_cond(ax25->peer, AX25_COND_RELEASE); if (ax25->slcomp) { axhc_free(ax25->slcomp); ax25->slcomp = NULL; } } void ax25_nr_error_recovery(ax25_cb *ax25) { if (ax25->peer != NULL) ax25->peer->condition |= AX25_COND_RELEASE; ax25_clear_queues(ax25); ax25_start_t1(ax25); ax25->state = AX25_STATE_2; ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); } void ax25_establish_data_link(ax25_cb *ax25) { ax25->timer_lock= RW_LOCK_UNLOCKED; ax25->state = AX25_STATE_1; ax25->n2count = 0; ax25->ack_timer = 0; ax25->vs = 0; ax25->va = 0; ax25->vr = 0; ax25->vl = 0; ax25_set_cond(ax25, AX25_COND_SETUP); ax25_start_t1(ax25); if (ax25->seqmask == AX25_SEQMASK) ax25_tx_command(ax25, AX25_SABM, AX25_POLLON); else ax25_tx_command(ax25, AX25_SABME, AX25_POLLON); } void ax25_transmit_enquiry(ax25_cb *ax25) { int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; ax25_tx_command(ax25, ft, AX25_POLLON); ax25_start_t1(ax25); ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); if (DAMA_STATE(ax25) & DAMA_SLAVE) AX25_PTR(ax25->device)->dama_polled = 1; } void ax25_enquiry_response(ax25_cb *ax25) { int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; ax25_tx_response(ax25, ft, AX25_POLLON); ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); } void ax25_timeout_response(ax25_cb *ax25) { int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; ax25_tx_response(ax25, ft, AX25_POLLOFF); ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); }