linux/drivers/net/wan/hdlc_x25.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Generic HDLC support routines for Linux
   4 * X.25 support
   5 *
   6 * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
   7 */
   8
   9#include <linux/errno.h>
  10#include <linux/gfp.h>
  11#include <linux/hdlc.h>
  12#include <linux/if_arp.h>
  13#include <linux/inetdevice.h>
  14#include <linux/init.h>
  15#include <linux/kernel.h>
  16#include <linux/lapb.h>
  17#include <linux/module.h>
  18#include <linux/pkt_sched.h>
  19#include <linux/poll.h>
  20#include <linux/rtnetlink.h>
  21#include <linux/skbuff.h>
  22#include <net/x25device.h>
  23
  24static int x25_ioctl(struct net_device *dev, struct ifreq *ifr);
  25
  26/* These functions are callbacks called by LAPB layer */
  27
  28static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
  29{
  30        struct sk_buff *skb;
  31        unsigned char *ptr;
  32
  33        if ((skb = dev_alloc_skb(1)) == NULL) {
  34                netdev_err(dev, "out of memory\n");
  35                return;
  36        }
  37
  38        ptr = skb_put(skb, 1);
  39        *ptr = code;
  40
  41        skb->protocol = x25_type_trans(skb, dev);
  42        netif_rx(skb);
  43}
  44
  45
  46
  47static void x25_connected(struct net_device *dev, int reason)
  48{
  49        x25_connect_disconnect(dev, reason, X25_IFACE_CONNECT);
  50}
  51
  52
  53
  54static void x25_disconnected(struct net_device *dev, int reason)
  55{
  56        x25_connect_disconnect(dev, reason, X25_IFACE_DISCONNECT);
  57}
  58
  59
  60
  61static int x25_data_indication(struct net_device *dev, struct sk_buff *skb)
  62{
  63        unsigned char *ptr;
  64
  65        skb_push(skb, 1);
  66
  67        if (skb_cow(skb, 1))
  68                return NET_RX_DROP;
  69
  70        ptr  = skb->data;
  71        *ptr = X25_IFACE_DATA;
  72
  73        skb->protocol = x25_type_trans(skb, dev);
  74        return netif_rx(skb);
  75}
  76
  77
  78
  79static void x25_data_transmit(struct net_device *dev, struct sk_buff *skb)
  80{
  81        hdlc_device *hdlc = dev_to_hdlc(dev);
  82        hdlc->xmit(skb, dev); /* Ignore return value :-( */
  83}
  84
  85
  86
  87static netdev_tx_t x25_xmit(struct sk_buff *skb, struct net_device *dev)
  88{
  89        int result;
  90
  91
  92        /* X.25 to LAPB */
  93        switch (skb->data[0]) {
  94        case X25_IFACE_DATA:    /* Data to be transmitted */
  95                skb_pull(skb, 1);
  96                if ((result = lapb_data_request(dev, skb)) != LAPB_OK)
  97                        dev_kfree_skb(skb);
  98                return NETDEV_TX_OK;
  99
 100        case X25_IFACE_CONNECT:
 101                if ((result = lapb_connect_request(dev))!= LAPB_OK) {
 102                        if (result == LAPB_CONNECTED)
 103                                /* Send connect confirm. msg to level 3 */
 104                                x25_connected(dev, 0);
 105                        else
 106                                netdev_err(dev, "LAPB connect request failed, error code = %i\n",
 107                                           result);
 108                }
 109                break;
 110
 111        case X25_IFACE_DISCONNECT:
 112                if ((result = lapb_disconnect_request(dev)) != LAPB_OK) {
 113                        if (result == LAPB_NOTCONNECTED)
 114                                /* Send disconnect confirm. msg to level 3 */
 115                                x25_disconnected(dev, 0);
 116                        else
 117                                netdev_err(dev, "LAPB disconnect request failed, error code = %i\n",
 118                                           result);
 119                }
 120                break;
 121
 122        default:                /* to be defined */
 123                break;
 124        }
 125
 126        dev_kfree_skb(skb);
 127        return NETDEV_TX_OK;
 128}
 129
 130
 131
 132static int x25_open(struct net_device *dev)
 133{
 134        int result;
 135        static const struct lapb_register_struct cb = {
 136                .connect_confirmation = x25_connected,
 137                .connect_indication = x25_connected,
 138                .disconnect_confirmation = x25_disconnected,
 139                .disconnect_indication = x25_disconnected,
 140                .data_indication = x25_data_indication,
 141                .data_transmit = x25_data_transmit,
 142        };
 143
 144        result = lapb_register(dev, &cb);
 145        if (result != LAPB_OK)
 146                return result;
 147        return 0;
 148}
 149
 150
 151
 152static void x25_close(struct net_device *dev)
 153{
 154        lapb_unregister(dev);
 155}
 156
 157
 158
 159static int x25_rx(struct sk_buff *skb)
 160{
 161        struct net_device *dev = skb->dev;
 162
 163        if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
 164                dev->stats.rx_dropped++;
 165                return NET_RX_DROP;
 166        }
 167
 168        if (lapb_data_received(dev, skb) == LAPB_OK)
 169                return NET_RX_SUCCESS;
 170
 171        dev->stats.rx_errors++;
 172        dev_kfree_skb_any(skb);
 173        return NET_RX_DROP;
 174}
 175
 176
 177static struct hdlc_proto proto = {
 178        .open           = x25_open,
 179        .close          = x25_close,
 180        .ioctl          = x25_ioctl,
 181        .netif_rx       = x25_rx,
 182        .xmit           = x25_xmit,
 183        .module         = THIS_MODULE,
 184};
 185
 186
 187static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
 188{
 189        hdlc_device *hdlc = dev_to_hdlc(dev);
 190        int result;
 191
 192        switch (ifr->ifr_settings.type) {
 193        case IF_GET_PROTO:
 194                if (dev_to_hdlc(dev)->proto != &proto)
 195                        return -EINVAL;
 196                ifr->ifr_settings.type = IF_PROTO_X25;
 197                return 0; /* return protocol only, no settable parameters */
 198
 199        case IF_PROTO_X25:
 200                if (!capable(CAP_NET_ADMIN))
 201                        return -EPERM;
 202
 203                if (dev->flags & IFF_UP)
 204                        return -EBUSY;
 205
 206                result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
 207                if (result)
 208                        return result;
 209
 210                if ((result = attach_hdlc_protocol(dev, &proto, 0)))
 211                        return result;
 212                dev->type = ARPHRD_X25;
 213                call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev);
 214                netif_dormant_off(dev);
 215                return 0;
 216        }
 217
 218        return -EINVAL;
 219}
 220
 221
 222static int __init mod_init(void)
 223{
 224        register_hdlc_protocol(&proto);
 225        return 0;
 226}
 227
 228
 229
 230static void __exit mod_exit(void)
 231{
 232        unregister_hdlc_protocol(&proto);
 233}
 234
 235
 236module_init(mod_init);
 237module_exit(mod_exit);
 238
 239MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
 240MODULE_DESCRIPTION("X.25 protocol support for generic HDLC");
 241MODULE_LICENSE("GPL v2");
 242