summaryrefslogtreecommitdiffstats
path: root/net/ax25/ax25_timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ax25/ax25_timer.c')
-rw-r--r--net/ax25/ax25_timer.c569
1 files changed, 384 insertions, 185 deletions
diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c
index 01e7596f3..fdc871bc1 100644
--- a/net/ax25/ax25_timer.c
+++ b/net/ax25/ax25_timer.c
@@ -1,256 +1,455 @@
/*
- * AX.25 release 037
+ * ax25_timer.c: timer subroutines for NEW-AX.25
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KLX
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * This module:
- * 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.
+ * Comment:
*
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
- * sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug
- * AX.25 033 Jonathan(G4KLX) Modularisation functions.
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into separate files.
- * Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with
- * standard AX.25 mode.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Tomi(OH2BNS) Fixed heartbeat expiry (check ax25_dev).
+ * 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/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
#include <linux/netdevice.h>
-#include <linux/skbuff.h>
+#include <linux/tcp.h>
#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-static void ax25_heartbeat_expiry(unsigned long);
-static void ax25_t1timer_expiry(unsigned long);
-static void ax25_t2timer_expiry(unsigned long);
-static void ax25_t3timer_expiry(unsigned long);
-static void ax25_idletimer_expiry(unsigned long);
-
-void ax25_start_heartbeat(ax25_cb *ax25)
-{
- del_timer(&ax25->timer);
+#include <net/ax25dev.h>
+#include <net/ax25.h>
- ax25->timer.data = (unsigned long)ax25;
- ax25->timer.function = &ax25_heartbeat_expiry;
- ax25->timer.expires = jiffies + 5 * HZ;
+#include "ax25_core.h"
+#include "ax25_ddi.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_timer.h"
- add_timer(&ax25->timer);
-}
+static void ax25_wrt_timeout(ax25_cb *);
-void ax25_start_t1timer(ax25_cb *ax25)
+/*
+ * AX.25 TIMER
+ *
+ * This routine is called every 100ms. Decrement timer by this
+ * amount - if expired then process the event.
+ */
+void ax25_timer(ax25_cb *ax25)
{
- del_timer(&ax25->t1timer);
+ int wrt_timeout;
+
+ switch (ax25->state) {
+ case AX25_LISTEN:
+ /*
+ * never kill listening sockets. let the be moved to
+ * AX25_STATE_0 first. ax25_release() does this.
+ */
+ return;
+
+ case AX25_STATE_0:
+ /*
+ * don't kill if a frame signalling a state change is
+ * still pending
+ */
+ if (ax25->condition & AX25_COND_STATE_CHANGE)
+ break;
+
+ if (ax25->sk) {
+ /*
+ * ax25_release() sets ax25->sk = NULL, releasing the
+ * connection between the socket and the underlying control
+ * structure, we handle that further down
+ */
+
+ /* almost dead, notify socket */
+ if (!ax25->sk->dead)
+ ax25_close_socket(ax25->sk, 0);
+ break;
+ }
+
+ /*
+ * wait a certain time before destroying the control block
+ */
+ if (ax25->killtimer > 0 && --ax25->killtimer > 0)
+ break;
+
+ /*
+ * if a peer exists in STATE [12], disconnect it. this handles
+ * "connection timed out" for digipeated connections.
+ *
+ * else if no peer exists, destroy this control block.
+ */
+ if (ax25->peer) {
+ if (ax25->peer->state < AX25_STATE_3)
+ ax25_destroy_cb(ax25->peer);
+ else
+ break;
+ }
+
+ ax25_destroy_cb(ax25);
+ return;
+
+ case AX25_STATE_3:
+ write_lock(&ax25->timer_lock);
+ if (!ax25->peer && !ax25->sk && ax25->idletimer > 0 && --ax25->idletimer == 0)
+ ax25_set_cond(ax25, AX25_COND_RELEASE);
+ write_unlock(&ax25->timer_lock);
+ /* fall through */
+ case AX25_STATE_4:
+ write_lock(&ax25->timer_lock);
+ if (ax25->condition & AX25_COND_ACK_PENDING) {
+ if (ax25->ack_timer > 0)
+ ax25->ack_timer--;
+ else
+ ax25_timeout_response(ax25);
+ }
+
+ /*
+ * Our peer connection has seen a DISC or DM and we're about to change to
+ * state 2. We don't DISC until we've delivered all queued data
+ */
+ if (ax25->condition & AX25_COND_RELEASE) {
+ if (!skb_queue_len(&ax25->write_queue) && !skb_queue_len(&ax25->ack_queue)) {
+ ax25->n2count = 1;
+ ax25->ack_timer = 0;
+ ax25_start_t1(ax25);
+ ax25_clr_cond(ax25, AX25_COND_RELEASE);
+ ax25->state = AX25_STATE_2;
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+ }
+
+ /*
+ * Check the state of the receive buffer. This is part of the flow control
+ * and should be done in ax25_rcvmsg();
+ */
+ } else if (ax25->sk && skb_queue_len(&ax25->rcv_queue)) {
+ struct sk_buff *skb;
+
+ while ((skb = skb_peek(&ax25->rcv_queue)) != NULL) {
+ if (ax25->sk->shutdown & RCV_SHUTDOWN)
+ break;
+ if (atomic_read(&ax25->sk->rmem_alloc) + skb->truesize < ax25->sk->rcvbuf) {
+ skb_dequeue(&ax25->rcv_queue);
+ sock_queue_rcv_skb(ax25->sk, skb);
+ } else
+ break;
+ }
+ if (skb == NULL) {
+ ax25_clr_cond(ax25, AX25_COND_OWN_RX_BUSY);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25->state = AX25_STATE_4;
+ ax25_transmit_enquiry(ax25);
+ }
+ }
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ default: /* state 1/2 */
+ break;
+ }
+
+ /* ax25_wrt_timout() must be called unlocked as ax25_disconnect()
+ * sets ax25->timer_lock
+ */
+
+ write_lock(&ax25->timer_lock);
+ wrt_timeout = (ax25->wrt_timer > 0 && --ax25->wrt_timer == 0);
+ write_unlock(&ax25->timer_lock);
+
+ if (wrt_timeout)
+ ax25_wrt_timeout(ax25);
- ax25->t1timer.data = (unsigned long)ax25;
- ax25->t1timer.function = &ax25_t1timer_expiry;
- ax25->t1timer.expires = jiffies + ax25->t1;
- add_timer(&ax25->t1timer);
}
-void ax25_start_t2timer(ax25_cb *ax25)
+static void ax25_wrt_timeout(ax25_cb *ax25)
{
- del_timer(&ax25->t2timer);
+ ax25_cb *peer = ax25->peer;
+ switch (ax25->state) {
+ case AX25_STATE_1:
+ if (ax25->n2count == ax25->n2) {
+ if (ax25->seqmask == AX25_SEQMASK) {
+ ax25_disconnect(ax25, ETIMEDOUT);
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, ETIMEDOUT);
+ else if (peer) {
+ ax25_disconnect(peer, 0);
+ }
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ ax25->n2count = 0;
+ ax25_start_t1(ax25);
+ ax25_tx_command(ax25, AX25_SABM, AX25_POLLON);
+ }
+ } else {
+ ax25->n2count++;
+ 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);
+ }
+ break;
+
+ case AX25_STATE_2:
+ if (ax25->n2count == ax25->n2) {
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_disconnect(ax25, 0);
+ } else {
+ ax25->n2count++;
+ ax25_start_t1(ax25);
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+ }
+ break;
+
+ case AX25_STATE_3:
+ ax25->vs_rtt = -1;
+ ax25->n2count = 1;
+ ax25_start_t1(ax25);
+ ax25->state = AX25_STATE_4;
+
+ /*
+ * We are angry now.
+ * Use CSMA. If master responds to poll, circuit will be saved
+ * and dama_mode will be re-enabled automagically in ax25_in.c
+ */
+ ax25_dev_set_dama(ax25->device, 0);
+ ax25_transmit_enquiry(ax25);
+ break;
+
+ case AX25_STATE_4:
+ if (ax25->n2count == ax25->n2) {
+ if (peer) {
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ ax25->killtimer = 90 * AX25_SLOWHZ;
+ } else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ETIMEDOUT);
+ ax25_disconnect(ax25, ETIMEDOUT);
+ } else {
+ ax25->n2count++;
+ ax25_transmit_enquiry(ax25);
+ }
+ break;
+ }
+}
- ax25->t2timer.data = (unsigned long)ax25;
- ax25->t2timer.function = &ax25_t2timer_expiry;
- ax25->t2timer.expires = jiffies + ax25->t2;
- add_timer(&ax25->t2timer);
-}
+/* -------------------------------------------------------------------- */
+
+/************************************************************************/
+/* Module support functions follow. */
+/************************************************************************/
+
+static struct protocol_struct {
+ struct protocol_struct *next;
+ unsigned int pid;
+ int (*func)(struct sk_buff *, ax25_cb *);
+} *protocol_list = NULL;
+
+static struct linkfail_struct {
+ struct linkfail_struct *next;
+ void (*func)(ax25_cb *, int);
+} *linkfail_list = NULL;
-void ax25_start_t3timer(ax25_cb *ax25)
+static struct listen_struct {
+ struct listen_struct *next;
+ ax25_address callsign;
+ struct net_device *dev;
+} *listen_list = NULL;
+
+int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
{
- del_timer(&ax25->t3timer);
+ struct protocol_struct *protocol;
+ unsigned long flags;
- if (ax25->t3 > 0) {
- ax25->t3timer.data = (unsigned long)ax25;
- ax25->t3timer.function = &ax25_t3timer_expiry;
- ax25->t3timer.expires = jiffies + ax25->t3;
+ if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
+ return 0;
- add_timer(&ax25->t3timer);
- }
+ if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ protocol->pid = pid;
+ protocol->func = func;
+
+ save_flags(flags);
+ cli();
+
+ protocol->next = protocol_list;
+ protocol_list = protocol;
+
+ restore_flags(flags);
+
+ return 1;
}
-void ax25_start_idletimer(ax25_cb *ax25)
+void ax25_protocol_release(unsigned int pid)
{
- del_timer(&ax25->idletimer);
+ struct protocol_struct *s, *protocol = protocol_list;
+ unsigned long flags;
+
+ if (protocol == NULL)
+ return;
- if (ax25->idle > 0) {
- ax25->idletimer.data = (unsigned long)ax25;
- ax25->idletimer.function = &ax25_idletimer_expiry;
- ax25->idletimer.expires = jiffies + ax25->idle;
+ save_flags(flags);
+ cli();
- add_timer(&ax25->idletimer);
+ if (protocol->pid == pid) {
+ protocol_list = protocol->next;
+ restore_flags(flags);
+ kfree(protocol);
+ return;
}
-}
-void ax25_stop_heartbeat(ax25_cb *ax25)
-{
- del_timer(&ax25->timer);
-}
+ while (protocol != NULL && protocol->next != NULL) {
+ if (protocol->next->pid == pid) {
+ s = protocol->next;
+ protocol->next = protocol->next->next;
+ restore_flags(flags);
+ kfree(s);
+ return;
+ }
-void ax25_stop_t1timer(ax25_cb *ax25)
-{
- del_timer(&ax25->t1timer);
-}
+ protocol = protocol->next;
+ }
-void ax25_stop_t2timer(ax25_cb *ax25)
-{
- del_timer(&ax25->t2timer);
+ restore_flags(flags);
}
-void ax25_stop_t3timer(ax25_cb *ax25)
+int ax25_linkfail_register(void (*func)(ax25_cb *, int))
{
- del_timer(&ax25->t3timer);
-}
+ struct linkfail_struct *linkfail;
+ unsigned long flags;
-void ax25_stop_idletimer(ax25_cb *ax25)
-{
- del_timer(&ax25->idletimer);
+ if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ linkfail->func = func;
+
+ save_flags(flags);
+ cli();
+
+ linkfail->next = linkfail_list;
+ linkfail_list = linkfail;
+
+ restore_flags(flags);
+
+ return 1;
}
-int ax25_t1timer_running(ax25_cb *ax25)
+void ax25_linkfail_release(void (*func)(ax25_cb *, int))
{
- return timer_pending(&ax25->t1timer);
+ struct linkfail_struct *s, *linkfail = linkfail_list;
+ unsigned long flags;
+
+ if (linkfail == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+
+ if (linkfail->func == func) {
+ linkfail_list = linkfail->next;
+ restore_flags(flags);
+ kfree(linkfail);
+ return;
+ }
+
+ while (linkfail != NULL && linkfail->next != NULL) {
+ if (linkfail->next->func == func) {
+ s = linkfail->next;
+ linkfail->next = linkfail->next->next;
+ restore_flags(flags);
+ kfree(s);
+ return;
+ }
+
+ linkfail = linkfail->next;
+ }
+
+ restore_flags(flags);
}
-unsigned long ax25_display_timer(struct timer_list *timer)
+static char empty_addr[AX25_ADDR_LEN] = {0, 0, 0, 0, 0, 0, 0};
+
+int ax25_listen_register(ax25_address *callsign, struct net_device *dev)
{
- if (!timer_pending(timer))
+ ax25_cb *ax25;
+
+ ax25_addr_t addr;
+
+ addr.dcount = 0;
+ addr.src = *callsign;
+ memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN);
+
+ if (ax25_find_cb(&addr, dev) != NULL)
+ return 0;
+
+ if ((ax25 = ax25_create_cb()) == NULL)
return 0;
- return timer->expires - jiffies;
+ ax25->addr.src = *callsign;
+
+ ax25_fillin_cb(ax25, dev);
+ ax25_insert_cb(ax25);
+
+ return 1;
}
-static void ax25_heartbeat_expiry(unsigned long param)
+void ax25_listen_release(ax25_address *callsign, struct net_device *dev)
{
- ax25_cb *ax25 = (ax25_cb *)param;
- int proto = AX25_PROTO_STD_SIMPLEX;
+ ax25_cb *ax25;
- if (ax25->ax25_dev)
- proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL];
+ ax25_addr_t addr;
- switch (proto) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_heartbeat_expiry(ax25);
- break;
+ addr.dcount = 0;
+ addr.src = *callsign;
+ memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN);
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_heartbeat_expiry(ax25);
- else
- ax25_std_heartbeat_expiry(ax25);
- break;
-#endif
- }
+ if ((ax25 = ax25_find_cb(&addr, dev)) != NULL)
+ ax25_destroy_cb(ax25);
}
-static void ax25_t1timer_expiry(unsigned long param)
+int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct protocol_struct *protocol;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t1timer_expiry(ax25);
- break;
+ for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+ if (protocol->pid == pid)
+ return protocol->func;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (!ax25->ax25_dev->dama.slave)
- ax25_std_t1timer_expiry(ax25);
- break;
-#endif
- }
+ return NULL;
}
-static void ax25_t2timer_expiry(unsigned long param)
+int ax25_listen_mine(ax25_address *callsign, struct net_device *dev)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct listen_struct *listen;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t2timer_expiry(ax25);
- break;
+ for (listen = listen_list; listen != NULL; listen = listen->next)
+ if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
+ return 1;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (!ax25->ax25_dev->dama.slave)
- ax25_std_t2timer_expiry(ax25);
- break;
-#endif
- }
+ return 0;
}
-static void ax25_t3timer_expiry(unsigned long param)
+void ax25_link_failed(ax25_cb *ax25, int reason)
{
- ax25_cb *ax25 = (ax25_cb *)param;
-
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t3timer_expiry(ax25);
- break;
+ struct linkfail_struct *linkfail;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_t3timer_expiry(ax25);
- else
- ax25_std_t3timer_expiry(ax25);
- break;
-#endif
- }
+ for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
+ (linkfail->func)(ax25, reason);
}
-static void ax25_idletimer_expiry(unsigned long param)
+int ax25_protocol_is_registered(unsigned int pid)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct protocol_struct *protocol;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_idletimer_expiry(ax25);
- break;
+ for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+ if (protocol->pid == pid)
+ return 1;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_idletimer_expiry(ax25);
- else
- ax25_std_idletimer_expiry(ax25);
- break;
-#endif
- }
+ return 0;
}