162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) 562306a36Sopenharmony_ci * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) 662306a36Sopenharmony_ci * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/socket.h> 1162306a36Sopenharmony_ci#include <linux/in.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/timer.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/sockios.h> 1762306a36Sopenharmony_ci#include <linux/spinlock.h> 1862306a36Sopenharmony_ci#include <linux/net.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <net/ax25.h> 2162306a36Sopenharmony_ci#include <linux/inet.h> 2262306a36Sopenharmony_ci#include <linux/netdevice.h> 2362306a36Sopenharmony_ci#include <linux/skbuff.h> 2462306a36Sopenharmony_ci#include <net/sock.h> 2562306a36Sopenharmony_ci#include <linux/uaccess.h> 2662306a36Sopenharmony_ci#include <linux/fcntl.h> 2762306a36Sopenharmony_ci#include <linux/mm.h> 2862306a36Sopenharmony_ci#include <linux/interrupt.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ax25_frag_lock); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, const ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci ax25_dev *ax25_dev; 3562306a36Sopenharmony_ci ax25_cb *ax25; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* 3862306a36Sopenharmony_ci * Take the default packet length for the device if zero is 3962306a36Sopenharmony_ci * specified. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci if (paclen == 0) { 4262306a36Sopenharmony_ci if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) 4362306a36Sopenharmony_ci return NULL; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci paclen = ax25_dev->values[AX25_VALUES_PACLEN]; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* 4962306a36Sopenharmony_ci * Look for an existing connection. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) { 5262306a36Sopenharmony_ci ax25_output(ax25, paclen, skb); 5362306a36Sopenharmony_ci return ax25; /* It already existed */ 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) 5762306a36Sopenharmony_ci return NULL; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if ((ax25 = ax25_create_cb()) == NULL) 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci ax25_fillin_cb(ax25, ax25_dev); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci ax25->source_addr = *src; 6562306a36Sopenharmony_ci ax25->dest_addr = *dest; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (digi != NULL) { 6862306a36Sopenharmony_ci ax25->digipeat = kmemdup(digi, sizeof(*digi), GFP_ATOMIC); 6962306a36Sopenharmony_ci if (ax25->digipeat == NULL) { 7062306a36Sopenharmony_ci ax25_cb_put(ax25); 7162306a36Sopenharmony_ci return NULL; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { 7662306a36Sopenharmony_ci case AX25_PROTO_STD_SIMPLEX: 7762306a36Sopenharmony_ci case AX25_PROTO_STD_DUPLEX: 7862306a36Sopenharmony_ci ax25_std_establish_data_link(ax25); 7962306a36Sopenharmony_ci break; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#ifdef CONFIG_AX25_DAMA_SLAVE 8262306a36Sopenharmony_ci case AX25_PROTO_DAMA_SLAVE: 8362306a36Sopenharmony_ci if (ax25_dev->dama.slave) 8462306a36Sopenharmony_ci ax25_ds_establish_data_link(ax25); 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci ax25_std_establish_data_link(ax25); 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci#endif 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * There is one ref for the state machine; a caller needs 9362306a36Sopenharmony_ci * one more to put it back, just like with the existing one. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci ax25_cb_hold(ax25); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci ax25_cb_add(ax25); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ax25->state = AX25_STATE_1; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ax25_start_heartbeat(ax25); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ax25_output(ax25, paclen, skb); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return ax25; /* We had to create it */ 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciEXPORT_SYMBOL(ax25_send_frame); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * All outgoing AX.25 I frames pass via this routine. Therefore this is 11262306a36Sopenharmony_ci * where the fragmentation of frames takes place. If fragment is set to 11362306a36Sopenharmony_ci * zero then we are not allowed to do fragmentation, even if the frame 11462306a36Sopenharmony_ci * is too large. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_civoid ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct sk_buff *skbn; 11962306a36Sopenharmony_ci unsigned char *p; 12062306a36Sopenharmony_ci int frontlen, len, fragno, ka9qfrag, first = 1; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (paclen < 16) { 12362306a36Sopenharmony_ci WARN_ON_ONCE(1); 12462306a36Sopenharmony_ci kfree_skb(skb); 12562306a36Sopenharmony_ci return; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if ((skb->len - 1) > paclen) { 12962306a36Sopenharmony_ci if (*skb->data == AX25_P_TEXT) { 13062306a36Sopenharmony_ci skb_pull(skb, 1); /* skip PID */ 13162306a36Sopenharmony_ci ka9qfrag = 0; 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci paclen -= 2; /* Allow for fragment control info */ 13462306a36Sopenharmony_ci ka9qfrag = 1; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci fragno = skb->len / paclen; 13862306a36Sopenharmony_ci if (skb->len % paclen == 0) fragno--; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci frontlen = skb_headroom(skb); /* Address space + CTRL */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci while (skb->len > 0) { 14362306a36Sopenharmony_ci spin_lock_bh(&ax25_frag_lock); 14462306a36Sopenharmony_ci if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) { 14562306a36Sopenharmony_ci spin_unlock_bh(&ax25_frag_lock); 14662306a36Sopenharmony_ci printk(KERN_CRIT "AX.25: ax25_output - out of memory\n"); 14762306a36Sopenharmony_ci return; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (skb->sk != NULL) 15162306a36Sopenharmony_ci skb_set_owner_w(skbn, skb->sk); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci spin_unlock_bh(&ax25_frag_lock); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci len = (paclen > skb->len) ? skb->len : paclen; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (ka9qfrag == 1) { 15862306a36Sopenharmony_ci skb_reserve(skbn, frontlen + 2); 15962306a36Sopenharmony_ci skb_set_network_header(skbn, 16062306a36Sopenharmony_ci skb_network_offset(skb)); 16162306a36Sopenharmony_ci skb_copy_from_linear_data(skb, skb_put(skbn, len), len); 16262306a36Sopenharmony_ci p = skb_push(skbn, 2); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci *p++ = AX25_P_SEGMENT; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci *p = fragno--; 16762306a36Sopenharmony_ci if (first) { 16862306a36Sopenharmony_ci *p |= AX25_SEG_FIRST; 16962306a36Sopenharmony_ci first = 0; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } else { 17262306a36Sopenharmony_ci skb_reserve(skbn, frontlen + 1); 17362306a36Sopenharmony_ci skb_set_network_header(skbn, 17462306a36Sopenharmony_ci skb_network_offset(skb)); 17562306a36Sopenharmony_ci skb_copy_from_linear_data(skb, skb_put(skbn, len), len); 17662306a36Sopenharmony_ci p = skb_push(skbn, 1); 17762306a36Sopenharmony_ci *p = AX25_P_TEXT; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci skb_pull(skb, len); 18162306a36Sopenharmony_ci skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */ 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci kfree_skb(skb); 18562306a36Sopenharmony_ci } else { 18662306a36Sopenharmony_ci skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */ 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { 19062306a36Sopenharmony_ci case AX25_PROTO_STD_SIMPLEX: 19162306a36Sopenharmony_ci case AX25_PROTO_STD_DUPLEX: 19262306a36Sopenharmony_ci ax25_kick(ax25); 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci#ifdef CONFIG_AX25_DAMA_SLAVE 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * A DAMA slave is _required_ to work as normal AX.25L2V2 19862306a36Sopenharmony_ci * if no DAMA master is available. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci case AX25_PROTO_DAMA_SLAVE: 20162306a36Sopenharmony_ci if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25); 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci#endif 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * This procedure is passed a buffer descriptor for an iframe. It builds 20962306a36Sopenharmony_ci * the rest of the control part of the frame and then writes it out. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci unsigned char *frame; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (skb == NULL) 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci skb_reset_network_header(skb); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (ax25->modulus == AX25_MODULUS) { 22162306a36Sopenharmony_ci frame = skb_push(skb, 1); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci *frame = AX25_I; 22462306a36Sopenharmony_ci *frame |= (poll_bit) ? AX25_PF : 0; 22562306a36Sopenharmony_ci *frame |= (ax25->vr << 5); 22662306a36Sopenharmony_ci *frame |= (ax25->vs << 1); 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci frame = skb_push(skb, 2); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci frame[0] = AX25_I; 23162306a36Sopenharmony_ci frame[0] |= (ax25->vs << 1); 23262306a36Sopenharmony_ci frame[1] = (poll_bit) ? AX25_EPF : 0; 23362306a36Sopenharmony_ci frame[1] |= (ax25->vr << 1); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ax25_start_idletimer(ax25); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ax25_transmit_buffer(ax25, skb, AX25_COMMAND); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_civoid ax25_kick(ax25_cb *ax25) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct sk_buff *skb, *skbn; 24462306a36Sopenharmony_ci int last = 1; 24562306a36Sopenharmony_ci unsigned short start, end, next; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) 24862306a36Sopenharmony_ci return; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (ax25->condition & AX25_COND_PEER_RX_BUSY) 25162306a36Sopenharmony_ci return; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (skb_peek(&ax25->write_queue) == NULL) 25462306a36Sopenharmony_ci return; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; 25762306a36Sopenharmony_ci end = (ax25->va + ax25->window) % ax25->modulus; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (start == end) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* 26362306a36Sopenharmony_ci * Transmit data until either we're out of data to send or 26462306a36Sopenharmony_ci * the window is full. Send a poll on the final I frame if 26562306a36Sopenharmony_ci * the window is filled. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * Dequeue the frame and copy it. 27062306a36Sopenharmony_ci * Check for race with ax25_clear_queues(). 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci skb = skb_dequeue(&ax25->write_queue); 27362306a36Sopenharmony_ci if (!skb) 27462306a36Sopenharmony_ci return; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci ax25->vs = start; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci do { 27962306a36Sopenharmony_ci if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { 28062306a36Sopenharmony_ci skb_queue_head(&ax25->write_queue, skb); 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (skb->sk != NULL) 28562306a36Sopenharmony_ci skb_set_owner_w(skbn, skb->sk); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci next = (ax25->vs + 1) % ax25->modulus; 28862306a36Sopenharmony_ci last = (next == end); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Transmit the frame copy. 29262306a36Sopenharmony_ci * bke 960114: do not set the Poll bit on the last frame 29362306a36Sopenharmony_ci * in DAMA mode. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { 29662306a36Sopenharmony_ci case AX25_PROTO_STD_SIMPLEX: 29762306a36Sopenharmony_ci case AX25_PROTO_STD_DUPLEX: 29862306a36Sopenharmony_ci ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci#ifdef CONFIG_AX25_DAMA_SLAVE 30262306a36Sopenharmony_ci case AX25_PROTO_DAMA_SLAVE: 30362306a36Sopenharmony_ci ax25_send_iframe(ax25, skbn, AX25_POLLOFF); 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci#endif 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ax25->vs = next; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* 31162306a36Sopenharmony_ci * Requeue the original data frame. 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci skb_queue_tail(&ax25->ack_queue, skb); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ax25->condition &= ~AX25_COND_ACK_PENDING; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (!ax25_t1timer_running(ax25)) { 32062306a36Sopenharmony_ci ax25_stop_t3timer(ax25); 32162306a36Sopenharmony_ci ax25_calculate_t1(ax25); 32262306a36Sopenharmony_ci ax25_start_t1timer(ax25); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_civoid ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci unsigned char *ptr; 32962306a36Sopenharmony_ci int headroom; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (ax25->ax25_dev == NULL) { 33262306a36Sopenharmony_ci ax25_disconnect(ax25, ENETUNREACH); 33362306a36Sopenharmony_ci return; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci headroom = ax25_addr_size(ax25->digipeat); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (unlikely(skb_headroom(skb) < headroom)) { 33962306a36Sopenharmony_ci skb = skb_expand_head(skb, headroom); 34062306a36Sopenharmony_ci if (!skb) { 34162306a36Sopenharmony_ci printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n"); 34262306a36Sopenharmony_ci return; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ptr = skb_push(skb, headroom); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci ax25_queue_xmit(skb, ax25->ax25_dev->dev); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/* 35462306a36Sopenharmony_ci * A small shim to dev_queue_xmit to add the KISS control byte, and do 35562306a36Sopenharmony_ci * any packet forwarding in operation. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_civoid ax25_queue_xmit(struct sk_buff *skb, struct net_device *dev) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci unsigned char *ptr; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci skb->protocol = ax25_type_trans(skb, ax25_fwd_dev(dev)); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ptr = skb_push(skb, 1); 36462306a36Sopenharmony_ci *ptr = 0x00; /* KISS */ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci dev_queue_xmit(skb); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciint ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci if (ax25->vs == nr) { 37262306a36Sopenharmony_ci ax25_frames_acked(ax25, nr); 37362306a36Sopenharmony_ci ax25_calculate_rtt(ax25); 37462306a36Sopenharmony_ci ax25_stop_t1timer(ax25); 37562306a36Sopenharmony_ci ax25_start_t3timer(ax25); 37662306a36Sopenharmony_ci return 1; 37762306a36Sopenharmony_ci } else { 37862306a36Sopenharmony_ci if (ax25->va != nr) { 37962306a36Sopenharmony_ci ax25_frames_acked(ax25, nr); 38062306a36Sopenharmony_ci ax25_calculate_t1(ax25); 38162306a36Sopenharmony_ci ax25_start_t1timer(ax25); 38262306a36Sopenharmony_ci return 1; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 387