diff options
Diffstat (limited to 'net/ax25/ax25_timer.c')
-rw-r--r-- | net/ax25/ax25_timer.c | 569 |
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; } |