Path: blob/master/drivers/isdn/i4l/isdn_x25iface.c
15112 views
/* $Id: isdn_x25iface.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $1*2* Linux ISDN subsystem, X.25 related functions3*4* This software may be used and distributed according to the terms5* of the GNU General Public License, incorporated herein by reference.6*7* stuff needed to support the Linux X.25 PLP code on top of devices that8* can provide a lab_b service using the concap_proto mechanism.9* This module supports a network interface which provides lapb_sematics10* -- as defined in Documentation/networking/x25-iface.txt -- to11* the upper layer and assumes that the lower layer provides a reliable12* data link service by means of the concap_device_ops callbacks.13*14* Only protocol specific stuff goes here. Device specific stuff15* goes to another -- device related -- concap_proto support source file.16*17*/1819/* #include <linux/isdn.h> */20#include <linux/netdevice.h>21#include <linux/concap.h>22#include <linux/slab.h>23#include <linux/wanrouter.h>24#include <net/x25device.h>25#include "isdn_x25iface.h"2627/* for debugging messages not to cause an oops when device pointer is NULL*/28#define MY_DEVNAME(dev) ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" )293031typedef struct isdn_x25iface_proto_data {32int magic;33enum wan_states state;34/* Private stuff, not to be accessed via proto_data. We provide the35other storage for the concap_proto instance here as well,36enabling us to allocate both with just one kmalloc(): */37struct concap_proto priv;38} ix25_pdata_t;39404142/* 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 *,46struct net_device *,47struct 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 * );525354static 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_ind63};6465/* error message helper function */66static void illegal_state_warn( unsigned state, unsigned char firstbyte)67{68printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in"69"current state %d\n",firstbyte, state );70}7172/* check protocol data field for consistency */73static int pdata_is_bad( ix25_pdata_t * pda ){7475if( pda && pda -> magic == ISDN_X25IFACE_MAGIC ) return 0;76printk( KERN_WARNING77"isdn_x25iface_xxx: illegal pointer to proto data\n" );78return 1;79}8081/* create a new x25 interface protocol instance82*/83struct concap_proto * isdn_x25iface_proto_new(void)84{85ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL);86IX25DEBUG("isdn_x25iface_proto_new\n");87if( tmp ){88tmp -> magic = ISDN_X25IFACE_MAGIC;89tmp -> state = WAN_UNCONFIGURED;90/* private data space used to hold the concap_proto data.91Only to be accessed via the returned pointer */92spin_lock_init(&tmp->priv.lock);93tmp -> priv.dops = NULL;94tmp -> priv.net_dev = NULL;95tmp -> priv.pops = &ix25_pops;96tmp -> priv.flags = 0;97tmp -> priv.proto_data = tmp;98return( &(tmp -> priv) );99}100return NULL;101};102103/* close the x25iface encapsulation protocol104*/105static int isdn_x25iface_proto_close(struct concap_proto *cprot){106107ix25_pdata_t *tmp;108int ret = 0;109ulong flags;110111if( ! cprot ){112printk( KERN_ERR "isdn_x25iface_proto_close: "113"invalid concap_proto pointer\n" );114return -1;115}116IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) );117spin_lock_irqsave(&cprot->lock, flags);118cprot -> dops = NULL;119cprot -> net_dev = NULL;120tmp = cprot -> proto_data;121if( pdata_is_bad( tmp ) ){122ret = -1;123} else {124tmp -> state = WAN_UNCONFIGURED;125}126spin_unlock_irqrestore(&cprot->lock, flags);127return ret;128}129130/* Delete the x25iface encapsulation protocol instance131*/132static void isdn_x25iface_proto_del(struct concap_proto *cprot){133134ix25_pdata_t * tmp;135136IX25DEBUG( "isdn_x25iface_proto_del \n" );137if( ! cprot ){138printk( KERN_ERR "isdn_x25iface_proto_del: "139"concap_proto pointer is NULL\n" );140return;141}142tmp = cprot -> proto_data;143if( tmp == NULL ){144printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent "145"proto_data pointer (maybe already deleted?)\n");146return;147}148/* close if the protocol is still open */149if( cprot -> dops ) isdn_x25iface_proto_close(cprot);150/* freeing the storage should be sufficient now. But some additional151settings might help to catch wild pointer bugs */152tmp -> magic = 0;153cprot -> proto_data = NULL;154155kfree( tmp );156return;157}158159/* (re-)initialize the data structures for x25iface encapsulation160*/161static int isdn_x25iface_proto_restart(struct concap_proto *cprot,162struct net_device *ndev,163struct concap_device_ops *dops)164{165ix25_pdata_t * pda = cprot -> proto_data ;166ulong flags;167168IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) );169170if ( pdata_is_bad( pda ) ) return -1;171172if( !( dops && dops -> data_req && dops -> connect_req173&& dops -> disconn_req ) ){174printk( KERN_WARNING "isdn_x25iface_restart: required dops"175" missing\n" );176isdn_x25iface_proto_close(cprot);177return -1;178}179spin_lock_irqsave(&cprot->lock, flags);180cprot -> net_dev = ndev;181cprot -> pops = &ix25_pops;182cprot -> dops = dops;183pda -> state = WAN_DISCONNECTED;184spin_unlock_irqrestore(&cprot->lock, flags);185return 0;186}187188/* deliver a dl_data frame received from i4l HL driver to the network layer189*/190static int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb)191{192IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) );193if ( ( (ix25_pdata_t*) (cprot->proto_data) )194-> state == WAN_CONNECTED ){195if( skb_push(skb, 1)){196skb->data[0] = X25_IFACE_DATA;197skb->protocol = x25_type_trans(skb, cprot->net_dev);198netif_rx(skb);199return 0;200}201}202printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) );203dev_kfree_skb(skb);204return -1;205}206207/* a connection set up is indicated by lower layer208*/209static int isdn_x25iface_connect_ind(struct concap_proto *cprot)210{211struct sk_buff * skb;212enum wan_states *state_p213= &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);214IX25DEBUG( "isdn_x25iface_connect_ind %s \n"215, MY_DEVNAME(cprot->net_dev) );216if( *state_p == WAN_UNCONFIGURED ){217printk(KERN_WARNING218"isdn_x25iface_connect_ind while unconfigured %s\n"219, MY_DEVNAME(cprot->net_dev) );220return -1;221}222*state_p = WAN_CONNECTED;223224skb = dev_alloc_skb(1);225if( skb ){226*(skb_put(skb, 1)) = X25_IFACE_CONNECT;227skb->protocol = x25_type_trans(skb, cprot->net_dev);228netif_rx(skb);229return 0;230} else {231printk(KERN_WARNING "isdn_x25iface_connect_ind: "232" out of memory -- disconnecting\n");233cprot -> dops -> disconn_req(cprot);234return -1;235}236}237238/* a disconnect is indicated by lower layer239*/240static int isdn_x25iface_disconn_ind(struct concap_proto *cprot)241{242struct sk_buff *skb;243enum wan_states *state_p244= &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);245IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) );246if( *state_p == WAN_UNCONFIGURED ){247printk(KERN_WARNING248"isdn_x25iface_disconn_ind while unconfigured\n");249return -1;250}251if(! cprot -> net_dev) return -1;252*state_p = WAN_DISCONNECTED;253skb = dev_alloc_skb(1);254if( skb ){255*(skb_put(skb, 1)) = X25_IFACE_DISCONNECT;256skb->protocol = x25_type_trans(skb, cprot->net_dev);257netif_rx(skb);258return 0;259} else {260printk(KERN_WARNING "isdn_x25iface_disconn_ind:"261" out of memory\n");262return -1;263}264}265266/* process a frame handed over to us from linux network layer. First byte267semantics as defined in Documentation/networking/x25-iface.txt268*/269static int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb)270{271unsigned char firstbyte = skb->data[0];272enum wan_states *state = &((ix25_pdata_t*)cprot->proto_data)->state;273int ret = 0;274IX25DEBUG("isdn_x25iface_xmit: %s first=%x state=%d\n",275MY_DEVNAME(cprot->net_dev), firstbyte, *state);276switch ( firstbyte ){277case X25_IFACE_DATA:278if( *state == WAN_CONNECTED ){279skb_pull(skb, 1);280cprot -> net_dev -> trans_start = jiffies;281ret = ( cprot -> dops -> data_req(cprot, skb) );282/* prepare for future retransmissions */283if( ret ) skb_push(skb,1);284return ret;285}286illegal_state_warn( *state, firstbyte );287break;288case X25_IFACE_CONNECT:289if( *state == WAN_DISCONNECTED ){290*state = WAN_CONNECTING;291ret = cprot -> dops -> connect_req(cprot);292if(ret){293/* reset state and notify upper layer about294* immidiatly failed attempts */295isdn_x25iface_disconn_ind(cprot);296}297} else {298illegal_state_warn( *state, firstbyte );299}300break;301case X25_IFACE_DISCONNECT:302switch ( *state ){303case WAN_DISCONNECTED:304/* Should not happen. However, give upper layer a305chance to recover from inconstistency but don't306trust the lower layer sending the disconn_confirm307when already disconnected */308printk(KERN_WARNING "isdn_x25iface_xmit: disconnect "309" requested while disconnected\n" );310isdn_x25iface_disconn_ind(cprot);311break; /* prevent infinite loops */312case WAN_CONNECTING:313case WAN_CONNECTED:314*state = WAN_DISCONNECTED;315cprot -> dops -> disconn_req(cprot);316break;317default:318illegal_state_warn( *state, firstbyte );319}320break;321case X25_IFACE_PARAMS:322printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb"323" options not yet supported\n");324break;325default:326printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal"327" first byte %x ignored:\n", firstbyte);328}329dev_kfree_skb(skb);330return 0;331}332333334