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