2005-04-17 02:20:36 +04:00
/*
* This program 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 .
*
* Copyright ( C ) Jonathan Naylor G4KLX ( g4klx @ g4klx . demon . co . uk )
* Copyright ( C ) Joerg Reuter DL1BKE ( jreuter @ yaina . de )
*/
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/in.h>
# include <linux/kernel.h>
# include <linux/timer.h>
# include <linux/string.h>
# include <linux/sockios.h>
# include <linux/spinlock.h>
# include <linux/net.h>
# include <net/ax25.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
# include <linux/skbuff.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>
void ax25_ds_nr_error_recovery ( ax25_cb * ax25 )
{
ax25_ds_establish_data_link ( ax25 ) ;
}
/*
* dl1bke 960114 : transmit I frames on DAMA poll
*/
void ax25_ds_enquiry_response ( ax25_cb * ax25 )
{
ax25_cb * ax25o ;
struct hlist_node * node ;
/* Please note that neither DK4EG<45> s nor DG2FEF<45> s
* DAMA spec mention the following behaviour as seen
* with TheFirmware :
*
* DB0ACH - > DL1BKE < RR C P R0 > [ DAMA ]
* DL1BKE - > DB0ACH < I NR = 0 NS = 0 >
* DL1BKE - 7 - > DB0PRA - 6 DB0ACH < I C S3 R5 >
* DL1BKE - > DB0ACH < RR R F R0 >
*
* The Flexnet DAMA Master implementation apparently
* insists on the " proper " AX .25 behaviour :
*
* DB0ACH - > DL1BKE < RR C P R0 > [ DAMA ]
* DL1BKE - > DB0ACH < RR R F R0 >
* DL1BKE - > DB0ACH < I NR = 0 NS = 0 >
* DL1BKE - 7 - > DB0PRA - 6 DB0ACH < I C S3 R5 >
*
* Flexnet refuses to send us * any * I frame if we send
* a REJ in case AX25_COND_REJECT is set . It is superfluous in
* this mode anyway ( a RR or RNR invokes the retransmission ) .
* Is this a Flexnet bug ?
*/
ax25_std_enquiry_response ( ax25 ) ;
if ( ! ( ax25 - > condition & AX25_COND_PEER_RX_BUSY ) ) {
ax25_requeue_frames ( ax25 ) ;
ax25_kick ( ax25 ) ;
}
if ( ax25 - > state = = AX25_STATE_1 | | ax25 - > state = = AX25_STATE_2 | | skb_peek ( & ax25 - > ack_queue ) ! = NULL )
ax25_ds_t1_timeout ( ax25 ) ;
else
ax25 - > n2count = 0 ;
ax25_start_t3timer ( ax25 ) ;
ax25_ds_set_timer ( ax25 - > ax25_dev ) ;
2006-07-13 00:25:23 +04:00
spin_lock ( & ax25_list_lock ) ;
2005-04-17 02:20:36 +04:00
ax25_for_each ( ax25o , node , & ax25_list ) {
if ( ax25o = = ax25 )
continue ;
if ( ax25o - > ax25_dev ! = ax25 - > ax25_dev )
continue ;
if ( ax25o - > state = = AX25_STATE_1 | | ax25o - > state = = AX25_STATE_2 ) {
ax25_ds_t1_timeout ( ax25o ) ;
continue ;
}
if ( ! ( ax25o - > condition & AX25_COND_PEER_RX_BUSY ) & & ax25o - > state = = AX25_STATE_3 ) {
ax25_requeue_frames ( ax25o ) ;
ax25_kick ( ax25o ) ;
}
if ( ax25o - > state = = AX25_STATE_1 | | ax25o - > state = = AX25_STATE_2 | | skb_peek ( & ax25o - > ack_queue ) ! = NULL )
ax25_ds_t1_timeout ( ax25o ) ;
/* do not start T3 for listening sockets (tnx DD8NE) */
if ( ax25o - > state ! = AX25_STATE_0 )
ax25_start_t3timer ( ax25o ) ;
}
2006-07-13 00:25:23 +04:00
spin_unlock ( & ax25_list_lock ) ;
2005-04-17 02:20:36 +04:00
}
void ax25_ds_establish_data_link ( ax25_cb * ax25 )
{
ax25 - > condition & = AX25_COND_DAMA_MODE ;
ax25 - > n2count = 0 ;
ax25_calculate_t1 ( ax25 ) ;
ax25_start_t1timer ( ax25 ) ;
ax25_stop_t2timer ( ax25 ) ;
ax25_start_t3timer ( ax25 ) ;
}
/*
* : : : FIXME : : :
* This is a kludge . Not all drivers recognize kiss commands .
* We need a driver level request to switch duplex mode , that does
* either SCC changing , PI config or KISS as required . Currently
* this request isn ' t reliable .
*/
static void ax25_kiss_cmd ( ax25_dev * ax25_dev , unsigned char cmd , unsigned char param )
{
struct sk_buff * skb ;
unsigned char * p ;
if ( ax25_dev - > dev = = NULL )
return ;
if ( ( skb = alloc_skb ( 2 , GFP_ATOMIC ) ) = = NULL )
return ;
2007-04-11 07:45:18 +04:00
skb_reset_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
p = skb_put ( skb , 2 ) ;
* p + + = cmd ;
* p + + = param ;
2005-04-25 05:53:06 +04:00
skb - > protocol = ax25_type_trans ( skb , ax25_dev - > dev ) ;
2005-04-17 02:20:36 +04:00
dev_queue_xmit ( skb ) ;
}
/*
* A nasty problem arises if we count the number of DAMA connections
* wrong , especially when connections on the device already existed
* and our network node ( or the sysop ) decides to turn on DAMA Master
* mode . We thus flag the ' real ' slave connections with
* ax25 - > dama_slave = 1 and look on every disconnect if still slave
* connections exist .
*/
static int ax25_check_dama_slave ( ax25_dev * ax25_dev )
{
ax25_cb * ax25 ;
int res = 0 ;
struct hlist_node * node ;
2006-07-13 00:25:23 +04:00
spin_lock ( & ax25_list_lock ) ;
2005-04-17 02:20:36 +04:00
ax25_for_each ( ax25 , node , & ax25_list )
if ( ax25 - > ax25_dev = = ax25_dev & & ( ax25 - > condition & AX25_COND_DAMA_MODE ) & & ax25 - > state > AX25_STATE_1 ) {
res = 1 ;
break ;
}
2006-07-13 00:25:23 +04:00
spin_unlock ( & ax25_list_lock ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
static void ax25_dev_dama_on ( ax25_dev * ax25_dev )
{
if ( ax25_dev = = NULL )
return ;
if ( ax25_dev - > dama . slave = = 0 )
ax25_kiss_cmd ( ax25_dev , 5 , 1 ) ;
ax25_dev - > dama . slave = 1 ;
ax25_ds_set_timer ( ax25_dev ) ;
}
void ax25_dev_dama_off ( ax25_dev * ax25_dev )
{
if ( ax25_dev = = NULL )
return ;
if ( ax25_dev - > dama . slave & & ! ax25_check_dama_slave ( ax25_dev ) ) {
ax25_kiss_cmd ( ax25_dev , 5 , 0 ) ;
ax25_dev - > dama . slave = 0 ;
ax25_ds_del_timer ( ax25_dev ) ;
}
}
void ax25_dama_on ( ax25_cb * ax25 )
{
ax25_dev_dama_on ( ax25 - > ax25_dev ) ;
ax25 - > condition | = AX25_COND_DAMA_MODE ;
}
void ax25_dama_off ( ax25_cb * ax25 )
{
ax25 - > condition & = ~ AX25_COND_DAMA_MODE ;
ax25_dev_dama_off ( ax25 - > ax25_dev ) ;
}