18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * LAPB release 002 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This code REQUIRES 2.1.15 or higher/ NET3.038 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * History 88c2ecf20Sopenharmony_ci * LAPB 001 Jonathan Naylor Started Coding 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/socket.h> 168c2ecf20Sopenharmony_ci#include <linux/in.h> 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/timer.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/sockios.h> 218c2ecf20Sopenharmony_ci#include <linux/net.h> 228c2ecf20Sopenharmony_ci#include <linux/inet.h> 238c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <net/sock.h> 268c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 278c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 288c2ecf20Sopenharmony_ci#include <linux/mm.h> 298c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 308c2ecf20Sopenharmony_ci#include <net/lapb.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * This routine purges all the queues of frames. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_civoid lapb_clear_queues(struct lapb_cb *lapb) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci skb_queue_purge(&lapb->write_queue); 388c2ecf20Sopenharmony_ci skb_queue_purge(&lapb->ack_queue); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* 428c2ecf20Sopenharmony_ci * This routine purges the input queue of those frames that have been 438c2ecf20Sopenharmony_ci * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the 448c2ecf20Sopenharmony_ci * SDL diagram. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_civoid lapb_frames_acked(struct lapb_cb *lapb, unsigned short nr) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct sk_buff *skb; 498c2ecf20Sopenharmony_ci int modulus; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * Remove all the ack-ed frames from the ack queue. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci if (lapb->va != nr) 578c2ecf20Sopenharmony_ci while (skb_peek(&lapb->ack_queue) && lapb->va != nr) { 588c2ecf20Sopenharmony_ci skb = skb_dequeue(&lapb->ack_queue); 598c2ecf20Sopenharmony_ci kfree_skb(skb); 608c2ecf20Sopenharmony_ci lapb->va = (lapb->va + 1) % modulus; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_civoid lapb_requeue_frames(struct lapb_cb *lapb) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct sk_buff *skb, *skb_prev = NULL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* 698c2ecf20Sopenharmony_ci * Requeue all the un-ack-ed frames on the output queue to be picked 708c2ecf20Sopenharmony_ci * up by lapb_kick called from the timer. This arrangement handles the 718c2ecf20Sopenharmony_ci * possibility of an empty output queue. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&lapb->ack_queue)) != NULL) { 748c2ecf20Sopenharmony_ci if (!skb_prev) 758c2ecf20Sopenharmony_ci skb_queue_head(&lapb->write_queue, skb); 768c2ecf20Sopenharmony_ci else 778c2ecf20Sopenharmony_ci skb_append(skb_prev, skb, &lapb->write_queue); 788c2ecf20Sopenharmony_ci skb_prev = skb; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* 838c2ecf20Sopenharmony_ci * Validate that the value of nr is between va and vs. Return true or 848c2ecf20Sopenharmony_ci * false for testing. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ciint lapb_validate_nr(struct lapb_cb *lapb, unsigned short nr) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci unsigned short vc = lapb->va; 898c2ecf20Sopenharmony_ci int modulus; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci while (vc != lapb->vs) { 948c2ecf20Sopenharmony_ci if (nr == vc) 958c2ecf20Sopenharmony_ci return 1; 968c2ecf20Sopenharmony_ci vc = (vc + 1) % modulus; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return nr == lapb->vs; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci * This routine is the centralised routine for parsing the control 1048c2ecf20Sopenharmony_ci * information for the different frame formats. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ciint lapb_decode(struct lapb_cb *lapb, struct sk_buff *skb, 1078c2ecf20Sopenharmony_ci struct lapb_frame *frame) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci frame->type = LAPB_ILLEGAL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci lapb_dbg(2, "(%p) S%d RX %3ph\n", lapb->dev, lapb->state, skb->data); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* We always need to look at 2 bytes, sometimes we need 1148c2ecf20Sopenharmony_ci * to look at 3 and those cases are handled below. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, 2)) 1178c2ecf20Sopenharmony_ci return -1; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (lapb->mode & LAPB_MLP) { 1208c2ecf20Sopenharmony_ci if (lapb->mode & LAPB_DCE) { 1218c2ecf20Sopenharmony_ci if (skb->data[0] == LAPB_ADDR_D) 1228c2ecf20Sopenharmony_ci frame->cr = LAPB_COMMAND; 1238c2ecf20Sopenharmony_ci if (skb->data[0] == LAPB_ADDR_C) 1248c2ecf20Sopenharmony_ci frame->cr = LAPB_RESPONSE; 1258c2ecf20Sopenharmony_ci } else { 1268c2ecf20Sopenharmony_ci if (skb->data[0] == LAPB_ADDR_C) 1278c2ecf20Sopenharmony_ci frame->cr = LAPB_COMMAND; 1288c2ecf20Sopenharmony_ci if (skb->data[0] == LAPB_ADDR_D) 1298c2ecf20Sopenharmony_ci frame->cr = LAPB_RESPONSE; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } else { 1328c2ecf20Sopenharmony_ci if (lapb->mode & LAPB_DCE) { 1338c2ecf20Sopenharmony_ci if (skb->data[0] == LAPB_ADDR_B) 1348c2ecf20Sopenharmony_ci frame->cr = LAPB_COMMAND; 1358c2ecf20Sopenharmony_ci if (skb->data[0] == LAPB_ADDR_A) 1368c2ecf20Sopenharmony_ci frame->cr = LAPB_RESPONSE; 1378c2ecf20Sopenharmony_ci } else { 1388c2ecf20Sopenharmony_ci if (skb->data[0] == LAPB_ADDR_A) 1398c2ecf20Sopenharmony_ci frame->cr = LAPB_COMMAND; 1408c2ecf20Sopenharmony_ci if (skb->data[0] == LAPB_ADDR_B) 1418c2ecf20Sopenharmony_ci frame->cr = LAPB_RESPONSE; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci skb_pull(skb, 1); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (lapb->mode & LAPB_EXTENDED) { 1488c2ecf20Sopenharmony_ci if (!(skb->data[0] & LAPB_S)) { 1498c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, 2)) 1508c2ecf20Sopenharmony_ci return -1; 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * I frame - carries NR/NS/PF 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci frame->type = LAPB_I; 1558c2ecf20Sopenharmony_ci frame->ns = (skb->data[0] >> 1) & 0x7F; 1568c2ecf20Sopenharmony_ci frame->nr = (skb->data[1] >> 1) & 0x7F; 1578c2ecf20Sopenharmony_ci frame->pf = skb->data[1] & LAPB_EPF; 1588c2ecf20Sopenharmony_ci frame->control[0] = skb->data[0]; 1598c2ecf20Sopenharmony_ci frame->control[1] = skb->data[1]; 1608c2ecf20Sopenharmony_ci skb_pull(skb, 2); 1618c2ecf20Sopenharmony_ci } else if ((skb->data[0] & LAPB_U) == 1) { 1628c2ecf20Sopenharmony_ci if (!pskb_may_pull(skb, 2)) 1638c2ecf20Sopenharmony_ci return -1; 1648c2ecf20Sopenharmony_ci /* 1658c2ecf20Sopenharmony_ci * S frame - take out PF/NR 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci frame->type = skb->data[0] & 0x0F; 1688c2ecf20Sopenharmony_ci frame->nr = (skb->data[1] >> 1) & 0x7F; 1698c2ecf20Sopenharmony_ci frame->pf = skb->data[1] & LAPB_EPF; 1708c2ecf20Sopenharmony_ci frame->control[0] = skb->data[0]; 1718c2ecf20Sopenharmony_ci frame->control[1] = skb->data[1]; 1728c2ecf20Sopenharmony_ci skb_pull(skb, 2); 1738c2ecf20Sopenharmony_ci } else if ((skb->data[0] & LAPB_U) == 3) { 1748c2ecf20Sopenharmony_ci /* 1758c2ecf20Sopenharmony_ci * U frame - take out PF 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci frame->type = skb->data[0] & ~LAPB_SPF; 1788c2ecf20Sopenharmony_ci frame->pf = skb->data[0] & LAPB_SPF; 1798c2ecf20Sopenharmony_ci frame->control[0] = skb->data[0]; 1808c2ecf20Sopenharmony_ci frame->control[1] = 0x00; 1818c2ecf20Sopenharmony_ci skb_pull(skb, 1); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } else { 1848c2ecf20Sopenharmony_ci if (!(skb->data[0] & LAPB_S)) { 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * I frame - carries NR/NS/PF 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci frame->type = LAPB_I; 1898c2ecf20Sopenharmony_ci frame->ns = (skb->data[0] >> 1) & 0x07; 1908c2ecf20Sopenharmony_ci frame->nr = (skb->data[0] >> 5) & 0x07; 1918c2ecf20Sopenharmony_ci frame->pf = skb->data[0] & LAPB_SPF; 1928c2ecf20Sopenharmony_ci } else if ((skb->data[0] & LAPB_U) == 1) { 1938c2ecf20Sopenharmony_ci /* 1948c2ecf20Sopenharmony_ci * S frame - take out PF/NR 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci frame->type = skb->data[0] & 0x0F; 1978c2ecf20Sopenharmony_ci frame->nr = (skb->data[0] >> 5) & 0x07; 1988c2ecf20Sopenharmony_ci frame->pf = skb->data[0] & LAPB_SPF; 1998c2ecf20Sopenharmony_ci } else if ((skb->data[0] & LAPB_U) == 3) { 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * U frame - take out PF 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci frame->type = skb->data[0] & ~LAPB_SPF; 2048c2ecf20Sopenharmony_ci frame->pf = skb->data[0] & LAPB_SPF; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci frame->control[0] = skb->data[0]; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci skb_pull(skb, 1); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * This routine is called when the HDLC layer internally generates a 2178c2ecf20Sopenharmony_ci * command or response for the remote machine ( eg. RR, UA etc. ). 2188c2ecf20Sopenharmony_ci * Only supervisory or unnumbered frames are processed, FRMRs are handled 2198c2ecf20Sopenharmony_ci * by lapb_transmit_frmr below. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_civoid lapb_send_control(struct lapb_cb *lapb, int frametype, 2228c2ecf20Sopenharmony_ci int poll_bit, int type) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct sk_buff *skb; 2258c2ecf20Sopenharmony_ci unsigned char *dptr; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if ((skb = alloc_skb(LAPB_HEADER_LEN + 3, GFP_ATOMIC)) == NULL) 2288c2ecf20Sopenharmony_ci return; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci skb_reserve(skb, LAPB_HEADER_LEN + 1); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (lapb->mode & LAPB_EXTENDED) { 2338c2ecf20Sopenharmony_ci if ((frametype & LAPB_U) == LAPB_U) { 2348c2ecf20Sopenharmony_ci dptr = skb_put(skb, 1); 2358c2ecf20Sopenharmony_ci *dptr = frametype; 2368c2ecf20Sopenharmony_ci *dptr |= poll_bit ? LAPB_SPF : 0; 2378c2ecf20Sopenharmony_ci } else { 2388c2ecf20Sopenharmony_ci dptr = skb_put(skb, 2); 2398c2ecf20Sopenharmony_ci dptr[0] = frametype; 2408c2ecf20Sopenharmony_ci dptr[1] = (lapb->vr << 1); 2418c2ecf20Sopenharmony_ci dptr[1] |= poll_bit ? LAPB_EPF : 0; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci dptr = skb_put(skb, 1); 2458c2ecf20Sopenharmony_ci *dptr = frametype; 2468c2ecf20Sopenharmony_ci *dptr |= poll_bit ? LAPB_SPF : 0; 2478c2ecf20Sopenharmony_ci if ((frametype & LAPB_U) == LAPB_S) /* S frames carry NR */ 2488c2ecf20Sopenharmony_ci *dptr |= (lapb->vr << 5); 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci lapb_transmit_buffer(lapb, skb, type); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* 2558c2ecf20Sopenharmony_ci * This routine generates FRMRs based on information previously stored in 2568c2ecf20Sopenharmony_ci * the LAPB control block. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_civoid lapb_transmit_frmr(struct lapb_cb *lapb) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct sk_buff *skb; 2618c2ecf20Sopenharmony_ci unsigned char *dptr; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if ((skb = alloc_skb(LAPB_HEADER_LEN + 7, GFP_ATOMIC)) == NULL) 2648c2ecf20Sopenharmony_ci return; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci skb_reserve(skb, LAPB_HEADER_LEN + 1); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (lapb->mode & LAPB_EXTENDED) { 2698c2ecf20Sopenharmony_ci dptr = skb_put(skb, 6); 2708c2ecf20Sopenharmony_ci *dptr++ = LAPB_FRMR; 2718c2ecf20Sopenharmony_ci *dptr++ = lapb->frmr_data.control[0]; 2728c2ecf20Sopenharmony_ci *dptr++ = lapb->frmr_data.control[1]; 2738c2ecf20Sopenharmony_ci *dptr++ = (lapb->vs << 1) & 0xFE; 2748c2ecf20Sopenharmony_ci *dptr = (lapb->vr << 1) & 0xFE; 2758c2ecf20Sopenharmony_ci if (lapb->frmr_data.cr == LAPB_RESPONSE) 2768c2ecf20Sopenharmony_ci *dptr |= 0x01; 2778c2ecf20Sopenharmony_ci dptr++; 2788c2ecf20Sopenharmony_ci *dptr++ = lapb->frmr_type; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci lapb_dbg(1, "(%p) S%d TX FRMR %5ph\n", 2818c2ecf20Sopenharmony_ci lapb->dev, lapb->state, 2828c2ecf20Sopenharmony_ci &skb->data[1]); 2838c2ecf20Sopenharmony_ci } else { 2848c2ecf20Sopenharmony_ci dptr = skb_put(skb, 4); 2858c2ecf20Sopenharmony_ci *dptr++ = LAPB_FRMR; 2868c2ecf20Sopenharmony_ci *dptr++ = lapb->frmr_data.control[0]; 2878c2ecf20Sopenharmony_ci *dptr = (lapb->vs << 1) & 0x0E; 2888c2ecf20Sopenharmony_ci *dptr |= (lapb->vr << 5) & 0xE0; 2898c2ecf20Sopenharmony_ci if (lapb->frmr_data.cr == LAPB_RESPONSE) 2908c2ecf20Sopenharmony_ci *dptr |= 0x10; 2918c2ecf20Sopenharmony_ci dptr++; 2928c2ecf20Sopenharmony_ci *dptr++ = lapb->frmr_type; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci lapb_dbg(1, "(%p) S%d TX FRMR %3ph\n", 2958c2ecf20Sopenharmony_ci lapb->dev, lapb->state, &skb->data[1]); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci lapb_transmit_buffer(lapb, skb, LAPB_RESPONSE); 2998c2ecf20Sopenharmony_ci} 300