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/hdlc.h>
  14#include <linux/if_arp.h>
  15#include <linux/inetdevice.h>
  16#include <linux/init.h>
  17#include <linux/kernel.h>
  18#include <linux/lapb.h>
  19#include <linux/module.h>
  20#include <linux/pkt_sched.h>
  21#include <linux/poll.h>
  22#include <linux/rtnetlink.h>
  23#include <linux/skbuff.h>
  24#include <linux/slab.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                printk(KERN_ERR "%s: out of memory\n", dev->name);
  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, 1);
  53}
  54
  55
  56
  57static void x25_disconnected(struct net_device *dev, int reason)
  58{
  59        x25_connect_disconnect(dev, reason, 2);
  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 = 0;
  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 0:         /* 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 1:
 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                                printk(KERN_ERR "%s: LAPB connect request "
 110                                       "failed, error code = %i\n",
 111                                       dev->name, result);
 112                }
 113                break;
 114
 115        case 2:
 116                if ((result = lapb_disconnect_request(dev)) != LAPB_OK) {
 117                        if (result == LAPB_NOTCONNECTED)
 118                                /* Send disconnect confirm. msg to level 3 */
 119                                x25_disconnected(dev, 0);
 120                        else
 121                                printk(KERN_ERR "%s: LAPB disconnect request "
 122                                       "failed, error code = %i\n",
 123                                       dev->name, result);
 124                }
 125                break;
 126
 127        default:                /* to be defined */
 128                break;
 129        }
 130
 131        dev_kfree_skb(skb);
 132        return NETDEV_TX_OK;
 133}
 134
 135
 136
 137static int x25_open(struct net_device *dev)
 138{
 139        struct lapb_register_struct cb;
 140        int result;
 141
 142        cb.connect_confirmation = x25_connected;
 143        cb.connect_indication = x25_connected;
 144        cb.disconnect_confirmation = x25_disconnected;
 145        cb.disconnect_indication = x25_disconnected;
 146        cb.data_indication = x25_data_indication;
 147        cb.data_transmit = x25_data_transmit;
 148
 149        result = lapb_register(dev, &cb);
 150        if (result != LAPB_OK)
 151                return result;
 152        return 0;
 153}
 154
 155
 156
 157static void x25_close(struct net_device *dev)
 158{
 159        lapb_unregister(dev);
 160}
 161
 162
 163
 164static int x25_rx(struct sk_buff *skb)
 165{
 166        struct net_device *dev = skb->dev;
 167
 168        if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
 169                dev->stats.rx_dropped++;
 170                return NET_RX_DROP;
 171        }
 172
 173        if (lapb_data_received(dev, skb) == LAPB_OK)
 174                return NET_RX_SUCCESS;
 175
 176        dev->stats.rx_errors++;
 177        dev_kfree_skb_any(skb);
 178        return NET_RX_DROP;
 179}
 180
 181
 182static struct hdlc_proto proto = {
 183        .open           = x25_open,
 184        .close          = x25_close,
 185        .ioctl          = x25_ioctl,
 186        .netif_rx       = x25_rx,
 187        .xmit           = x25_xmit,
 188        .module         = THIS_MODULE,
 189};
 190
 191
 192static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
 193{
 194        hdlc_device *hdlc = dev_to_hdlc(dev);
 195        int result;
 196
 197        switch (ifr->ifr_settings.type) {
 198        case IF_GET_PROTO:
 199                if (dev_to_hdlc(dev)->proto != &proto)
 200                        return -EINVAL;
 201                ifr->ifr_settings.type = IF_PROTO_X25;
 202                return 0; /* return protocol only, no settable parameters */
 203
 204        case IF_PROTO_X25:
 205                if(!capable(CAP_NET_ADMIN))
 206                        return -EPERM;
 207
 208                if(dev->flags & IFF_UP)
 209                        return -EBUSY;
 210
 211                result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
 212                if (result)
 213                        return result;
 214
 215                if ((result = attach_hdlc_protocol(dev, &proto, 0)))
 216                        return result;
 217                dev->type = ARPHRD_X25;
 218                netif_dormant_off(dev);
 219                return 0;
 220        }
 221
 222        return -EINVAL;
 223}
 224
 225
 226static int __init mod_init(void)
 227{
 228        register_hdlc_protocol(&proto);
 229        return 0;
 230}
 231
 232
 233
 234static void __exit mod_exit(void)
 235{
 236        unregister_hdlc_protocol(&proto);
 237}
 238
 239
 240module_init(mod_init);
 241module_exit(mod_exit);
 242
 243MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
 244MODULE_DESCRIPTION("X.25 protocol support for generic HDLC");
 245MODULE_LICENSE("GPL v2");
 246