linux/drivers/isdn/i4l/isdn_x25iface.c
<<
>>
Prefs
   1/* $Id: isdn_x25iface.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
   2 *
   3 * Linux ISDN subsystem, X.25 related functions
   4 *
   5 * This software may be used and distributed according to the terms
   6 * of the GNU General Public License, incorporated herein by reference.
   7 *
   8 * stuff needed to support the Linux X.25 PLP code on top of devices that
   9 * can provide a lab_b service using the concap_proto mechanism.
  10 * This module supports a network interface which provides lapb_sematics
  11 * -- as defined in Documentation/networking/x25-iface.txt -- to
  12 * the upper layer and assumes that the lower layer provides a reliable
  13 * data link service by means of the concap_device_ops callbacks.
  14 *
  15 * Only protocol specific stuff goes here. Device specific stuff
  16 * goes to another -- device related -- concap_proto support source file.
  17 *
  18 */
  19
  20/* #include <linux/isdn.h> */
  21#include <linux/netdevice.h>
  22#include <linux/concap.h>
  23#include <linux/wanrouter.h>
  24#include <net/x25device.h>
  25#include "isdn_x25iface.h"
  26
  27/* for debugging messages not to cause an oops when device pointer is NULL*/
  28#define MY_DEVNAME(dev)  ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" )
  29
  30
  31typedef struct isdn_x25iface_proto_data {
  32        int magic;
  33        enum wan_states state;
  34        /* Private stuff, not to be accessed via proto_data. We provide the
  35           other storage for the concap_proto instance here as well,
  36           enabling us to allocate both with just one kmalloc(): */ 
  37        struct concap_proto priv;
  38} ix25_pdata_t;
  39
  40
  41
  42/* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */
  43static void isdn_x25iface_proto_del( struct concap_proto * );
  44static int isdn_x25iface_proto_close( struct concap_proto * );
  45static int isdn_x25iface_proto_restart( struct concap_proto *,
  46                                        struct net_device *,
  47                                        struct concap_device_ops *);
  48static int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * );
  49static int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * );
  50static int isdn_x25iface_connect_ind( struct concap_proto * );
  51static int isdn_x25iface_disconn_ind( struct concap_proto * );
  52
  53
  54static struct concap_proto_ops ix25_pops = {
  55        &isdn_x25iface_proto_new,
  56        &isdn_x25iface_proto_del,
  57        &isdn_x25iface_proto_restart,
  58        &isdn_x25iface_proto_close,
  59        &isdn_x25iface_xmit,
  60        &isdn_x25iface_receive,
  61        &isdn_x25iface_connect_ind,
  62        &isdn_x25iface_disconn_ind
  63};
  64
  65/* error message helper function */
  66static void illegal_state_warn( unsigned state, unsigned char firstbyte) 
  67{
  68        printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in"
  69                "current state %d\n",firstbyte, state );
  70}
  71
  72/* check protocol data field for consistency */
  73static int pdata_is_bad( ix25_pdata_t * pda ){
  74
  75        if( pda  &&  pda -> magic == ISDN_X25IFACE_MAGIC ) return 0;
  76        printk( KERN_WARNING
  77                "isdn_x25iface_xxx: illegal pointer to proto data\n" );
  78        return 1;
  79}
  80
  81/* create a new x25 interface protocol instance
  82 */
  83struct concap_proto * isdn_x25iface_proto_new(void)
  84{
  85        ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL);
  86        IX25DEBUG("isdn_x25iface_proto_new\n");
  87        if( tmp ){
  88                tmp -> magic = ISDN_X25IFACE_MAGIC;
  89                tmp -> state = WAN_UNCONFIGURED;
  90                /* private data space used to hold the concap_proto data.
  91                   Only to be accessed via the returned pointer */
  92                spin_lock_init(&tmp->priv.lock);
  93                tmp -> priv.dops       = NULL;
  94                tmp -> priv.net_dev    = NULL;
  95                tmp -> priv.pops       = &ix25_pops;
  96                tmp -> priv.flags      = 0;
  97                tmp -> priv.proto_data = tmp;
  98                return( &(tmp -> priv) );
  99        }
 100        return NULL;
 101};
 102
 103/* close the x25iface encapsulation protocol 
 104 */
 105static int isdn_x25iface_proto_close(struct concap_proto *cprot){
 106
 107        ix25_pdata_t *tmp;
 108        int ret = 0;
 109        ulong flags;
 110
 111        if( ! cprot ){
 112                printk( KERN_ERR "isdn_x25iface_proto_close: "
 113                        "invalid concap_proto pointer\n" );
 114                return -1;
 115        }
 116        IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) );
 117        spin_lock_irqsave(&cprot->lock, flags);
 118        cprot -> dops    = NULL;
 119        cprot -> net_dev = NULL;
 120        tmp = cprot -> proto_data;
 121        if( pdata_is_bad( tmp ) ){
 122                ret = -1;
 123        } else {
 124                tmp -> state = WAN_UNCONFIGURED;
 125        }
 126        spin_unlock_irqrestore(&cprot->lock, flags);
 127        return ret;
 128}
 129
 130/* Delete the x25iface encapsulation protocol instance
 131 */
 132static void isdn_x25iface_proto_del(struct concap_proto *cprot){
 133
 134        ix25_pdata_t * tmp;
 135 
 136        IX25DEBUG( "isdn_x25iface_proto_del \n" );
 137        if( ! cprot ){
 138                printk( KERN_ERR "isdn_x25iface_proto_del: "
 139                        "concap_proto pointer is NULL\n" );
 140                return;
 141        }
 142        tmp = cprot -> proto_data;
 143        if( tmp == NULL ){ 
 144                printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent "
 145                        "proto_data pointer (maybe already deleted?)\n"); 
 146                return;
 147        }
 148        /* close if the protocol is still open */
 149        if( cprot -> dops ) isdn_x25iface_proto_close(cprot);
 150        /* freeing the storage should be sufficient now. But some additional
 151           settings might help to catch wild pointer bugs */
 152        tmp -> magic = 0;
 153        cprot -> proto_data = NULL;
 154
 155        kfree( tmp );
 156        return;
 157}
 158
 159/* (re-)initialize the data structures for x25iface encapsulation
 160 */
 161static int isdn_x25iface_proto_restart(struct concap_proto *cprot,
 162                                        struct net_device *ndev,
 163                                        struct concap_device_ops *dops)
 164{
 165        ix25_pdata_t * pda = cprot -> proto_data ;
 166        ulong flags;
 167
 168        IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) );
 169
 170        if ( pdata_is_bad( pda ) ) return -1;
 171
 172        if( !( dops  && dops -> data_req && dops -> connect_req 
 173               && dops -> disconn_req )  ){
 174                printk( KERN_WARNING "isdn_x25iface_restart: required dops"
 175                        " missing\n" );
 176                isdn_x25iface_proto_close(cprot);
 177                return -1;
 178        }
 179        spin_lock_irqsave(&cprot->lock, flags);
 180        cprot -> net_dev = ndev;
 181        cprot -> pops = &ix25_pops;
 182        cprot -> dops = dops;
 183        pda -> state = WAN_DISCONNECTED;
 184        spin_unlock_irqrestore(&cprot->lock, flags);
 185        return 0;
 186}
 187
 188/* deliver a dl_data frame received from i4l HL driver to the network layer 
 189 */
 190static int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb)
 191{
 192        IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) );
 193        if ( ( (ix25_pdata_t*) (cprot->proto_data) ) 
 194             -> state == WAN_CONNECTED ){
 195                if( skb_push(skb, 1)){
 196                        skb -> data[0]=0x00;
 197                        skb->protocol = x25_type_trans(skb, cprot->net_dev);
 198                        netif_rx(skb);
 199                        return 0;
 200                }
 201        }
 202        printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) );
 203        dev_kfree_skb(skb);
 204        return -1;
 205}
 206
 207/* a connection set up is indicated by lower layer 
 208 */
 209static int isdn_x25iface_connect_ind(struct concap_proto *cprot)
 210{
 211        struct sk_buff * skb;
 212        enum wan_states *state_p 
 213          = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
 214        IX25DEBUG( "isdn_x25iface_connect_ind %s \n"
 215                   , MY_DEVNAME(cprot->net_dev) );
 216        if( *state_p == WAN_UNCONFIGURED ){ 
 217                printk(KERN_WARNING 
 218                       "isdn_x25iface_connect_ind while unconfigured %s\n"
 219                       , MY_DEVNAME(cprot->net_dev) );
 220                return -1;
 221        }
 222        *state_p = WAN_CONNECTED;
 223
 224        skb = dev_alloc_skb(1);
 225        if( skb ){
 226                *( skb_put(skb, 1) ) = 0x01;
 227                skb->protocol = x25_type_trans(skb, cprot->net_dev);
 228                netif_rx(skb);
 229                return 0;
 230        } else {
 231                printk(KERN_WARNING "isdn_x25iface_connect_ind: "
 232                       " out of memory -- disconnecting\n");
 233                cprot -> dops -> disconn_req(cprot);
 234                return -1;
 235        }
 236}
 237        
 238/* a disconnect is indicated by lower layer 
 239 */
 240static int isdn_x25iface_disconn_ind(struct concap_proto *cprot)
 241{
 242        struct sk_buff *skb;
 243        enum wan_states *state_p 
 244          = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
 245        IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) );
 246        if( *state_p == WAN_UNCONFIGURED ){ 
 247                printk(KERN_WARNING 
 248                       "isdn_x25iface_disconn_ind while unconfigured\n");
 249                return -1;
 250        }
 251        if(! cprot -> net_dev) return -1;
 252        *state_p = WAN_DISCONNECTED;
 253        skb = dev_alloc_skb(1);
 254        if( skb ){
 255                *( skb_put(skb, 1) ) = 0x02;
 256                skb->protocol = x25_type_trans(skb, cprot->net_dev);
 257                netif_rx(skb);
 258                return 0;
 259        } else {
 260                printk(KERN_WARNING "isdn_x25iface_disconn_ind:"
 261                       " out of memory\n");
 262                return -1;
 263        }
 264}
 265
 266/* process a frame handed over to us from linux network layer. First byte
 267   semantics as defined in Documentation/networking/x25-iface.txt
 268   */
 269static int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb)
 270{
 271        unsigned char firstbyte = skb->data[0];
 272        enum wan_states *state = &((ix25_pdata_t*)cprot->proto_data)->state;
 273        int ret = 0;
 274        IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state );
 275        switch ( firstbyte ){
 276        case 0x00: /* dl_data request */
 277                if( *state == WAN_CONNECTED ){
 278                        skb_pull(skb, 1);
 279                        cprot -> net_dev -> trans_start = jiffies;
 280                        ret = ( cprot -> dops -> data_req(cprot, skb) );
 281                        /* prepare for future retransmissions */
 282                        if( ret ) skb_push(skb,1);
 283                        return ret;
 284                }
 285                illegal_state_warn( *state, firstbyte ); 
 286                break;
 287        case 0x01: /* dl_connect request */
 288                if( *state == WAN_DISCONNECTED ){
 289                        *state = WAN_CONNECTING;
 290                        ret = cprot -> dops -> connect_req(cprot);
 291                        if(ret){
 292                                /* reset state and notify upper layer about
 293                                 * immidiatly failed attempts */
 294                                isdn_x25iface_disconn_ind(cprot);
 295                        }
 296                } else {
 297                        illegal_state_warn( *state, firstbyte );
 298                }
 299                break;
 300        case 0x02: /* dl_disconnect request */
 301                switch ( *state ){
 302                case WAN_DISCONNECTED: 
 303                        /* Should not happen. However, give upper layer a
 304                           chance to recover from inconstistency  but don't
 305                           trust the lower layer sending the disconn_confirm
 306                           when already disconnected */
 307                        printk(KERN_WARNING "isdn_x25iface_xmit: disconnect "
 308                               " requested while disconnected\n" );
 309                        isdn_x25iface_disconn_ind(cprot);
 310                        break; /* prevent infinite loops */
 311                case WAN_CONNECTING:
 312                case WAN_CONNECTED:
 313                        *state = WAN_DISCONNECTED;
 314                        cprot -> dops -> disconn_req(cprot);
 315                        break;
 316                default:
 317                        illegal_state_warn( *state, firstbyte );
 318                }
 319                break;
 320        case 0x03: /* changing lapb parameters requested */
 321                printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb"
 322                       " options not yet supported\n");
 323                break;
 324        default:
 325                printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal"
 326                       " first byte %x ignored:\n", firstbyte);
 327        }
 328        dev_kfree_skb(skb);
 329        return 0;
 330}
 331