162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) 562306a36Sopenharmony_ci * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/errno.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/socket.h> 1062306a36Sopenharmony_ci#include <linux/in.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/timer.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/sockios.h> 1562306a36Sopenharmony_ci#include <linux/spinlock.h> 1662306a36Sopenharmony_ci#include <linux/net.h> 1762306a36Sopenharmony_ci#include <linux/gfp.h> 1862306a36Sopenharmony_ci#include <net/ax25.h> 1962306a36Sopenharmony_ci#include <linux/inet.h> 2062306a36Sopenharmony_ci#include <linux/netdevice.h> 2162306a36Sopenharmony_ci#include <linux/skbuff.h> 2262306a36Sopenharmony_ci#include <net/sock.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <linux/fcntl.h> 2562306a36Sopenharmony_ci#include <linux/mm.h> 2662306a36Sopenharmony_ci#include <linux/interrupt.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_civoid ax25_ds_nr_error_recovery(ax25_cb *ax25) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci ax25_ds_establish_data_link(ax25); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * dl1bke 960114: transmit I frames on DAMA poll 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_civoid ax25_ds_enquiry_response(ax25_cb *ax25) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci ax25_cb *ax25o; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* Please note that neither DK4EG's nor DG2FEF's 4162306a36Sopenharmony_ci * DAMA spec mention the following behaviour as seen 4262306a36Sopenharmony_ci * with TheFirmware: 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * DB0ACH->DL1BKE <RR C P R0> [DAMA] 4562306a36Sopenharmony_ci * DL1BKE->DB0ACH <I NR=0 NS=0> 4662306a36Sopenharmony_ci * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5> 4762306a36Sopenharmony_ci * DL1BKE->DB0ACH <RR R F R0> 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * The Flexnet DAMA Master implementation apparently 5062306a36Sopenharmony_ci * insists on the "proper" AX.25 behaviour: 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * DB0ACH->DL1BKE <RR C P R0> [DAMA] 5362306a36Sopenharmony_ci * DL1BKE->DB0ACH <RR R F R0> 5462306a36Sopenharmony_ci * DL1BKE->DB0ACH <I NR=0 NS=0> 5562306a36Sopenharmony_ci * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5> 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * Flexnet refuses to send us *any* I frame if we send 5862306a36Sopenharmony_ci * a REJ in case AX25_COND_REJECT is set. It is superfluous in 5962306a36Sopenharmony_ci * this mode anyway (a RR or RNR invokes the retransmission). 6062306a36Sopenharmony_ci * Is this a Flexnet bug? 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ax25_std_enquiry_response(ax25); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) { 6662306a36Sopenharmony_ci ax25_requeue_frames(ax25); 6762306a36Sopenharmony_ci ax25_kick(ax25); 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL) 7162306a36Sopenharmony_ci ax25_ds_t1_timeout(ax25); 7262306a36Sopenharmony_ci else 7362306a36Sopenharmony_ci ax25->n2count = 0; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ax25_start_t3timer(ax25); 7662306a36Sopenharmony_ci ax25_ds_set_timer(ax25->ax25_dev); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci spin_lock(&ax25_list_lock); 7962306a36Sopenharmony_ci ax25_for_each(ax25o, &ax25_list) { 8062306a36Sopenharmony_ci if (ax25o == ax25) 8162306a36Sopenharmony_ci continue; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (ax25o->ax25_dev != ax25->ax25_dev) 8462306a36Sopenharmony_ci continue; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) { 8762306a36Sopenharmony_ci ax25_ds_t1_timeout(ax25o); 8862306a36Sopenharmony_ci continue; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) { 9262306a36Sopenharmony_ci ax25_requeue_frames(ax25o); 9362306a36Sopenharmony_ci ax25_kick(ax25o); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL) 9762306a36Sopenharmony_ci ax25_ds_t1_timeout(ax25o); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* do not start T3 for listening sockets (tnx DD8NE) */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (ax25o->state != AX25_STATE_0) 10262306a36Sopenharmony_ci ax25_start_t3timer(ax25o); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci spin_unlock(&ax25_list_lock); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_civoid ax25_ds_establish_data_link(ax25_cb *ax25) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci ax25->condition &= AX25_COND_DAMA_MODE; 11062306a36Sopenharmony_ci ax25->n2count = 0; 11162306a36Sopenharmony_ci ax25_calculate_t1(ax25); 11262306a36Sopenharmony_ci ax25_start_t1timer(ax25); 11362306a36Sopenharmony_ci ax25_stop_t2timer(ax25); 11462306a36Sopenharmony_ci ax25_start_t3timer(ax25); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * :::FIXME::: 11962306a36Sopenharmony_ci * This is a kludge. Not all drivers recognize kiss commands. 12062306a36Sopenharmony_ci * We need a driver level request to switch duplex mode, that does 12162306a36Sopenharmony_ci * either SCC changing, PI config or KISS as required. Currently 12262306a36Sopenharmony_ci * this request isn't reliable. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistatic void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct sk_buff *skb; 12762306a36Sopenharmony_ci unsigned char *p; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (ax25_dev->dev == NULL) 13062306a36Sopenharmony_ci return; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL) 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci skb_reset_network_header(skb); 13662306a36Sopenharmony_ci p = skb_put(skb, 2); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci *p++ = cmd; 13962306a36Sopenharmony_ci *p++ = param; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci skb->protocol = ax25_type_trans(skb, ax25_dev->dev); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci dev_queue_xmit(skb); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * A nasty problem arises if we count the number of DAMA connections 14862306a36Sopenharmony_ci * wrong, especially when connections on the device already existed 14962306a36Sopenharmony_ci * and our network node (or the sysop) decides to turn on DAMA Master 15062306a36Sopenharmony_ci * mode. We thus flag the 'real' slave connections with 15162306a36Sopenharmony_ci * ax25->dama_slave=1 and look on every disconnect if still slave 15262306a36Sopenharmony_ci * connections exist. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic int ax25_check_dama_slave(ax25_dev *ax25_dev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci ax25_cb *ax25; 15762306a36Sopenharmony_ci int res = 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci spin_lock(&ax25_list_lock); 16062306a36Sopenharmony_ci ax25_for_each(ax25, &ax25_list) 16162306a36Sopenharmony_ci if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) { 16262306a36Sopenharmony_ci res = 1; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci spin_unlock(&ax25_list_lock); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return res; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void ax25_dev_dama_on(ax25_dev *ax25_dev) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci if (ax25_dev == NULL) 17362306a36Sopenharmony_ci return; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (ax25_dev->dama.slave == 0) 17662306a36Sopenharmony_ci ax25_kiss_cmd(ax25_dev, 5, 1); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ax25_dev->dama.slave = 1; 17962306a36Sopenharmony_ci ax25_ds_set_timer(ax25_dev); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_civoid ax25_dev_dama_off(ax25_dev *ax25_dev) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci if (ax25_dev == NULL) 18562306a36Sopenharmony_ci return; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) { 18862306a36Sopenharmony_ci ax25_kiss_cmd(ax25_dev, 5, 0); 18962306a36Sopenharmony_ci ax25_dev->dama.slave = 0; 19062306a36Sopenharmony_ci ax25_ds_del_timer(ax25_dev); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_civoid ax25_dama_on(ax25_cb *ax25) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci ax25_dev_dama_on(ax25->ax25_dev); 19762306a36Sopenharmony_ci ax25->condition |= AX25_COND_DAMA_MODE; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_civoid ax25_dama_off(ax25_cb *ax25) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci ax25->condition &= ~AX25_COND_DAMA_MODE; 20362306a36Sopenharmony_ci ax25_dev_dama_off(ax25->ax25_dev); 20462306a36Sopenharmony_ci} 205