Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu
[sfrench/cifs-2.6.git] / drivers / isdn / i4l / isdn_x25iface.c
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
31 typedef 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); */
43 static void isdn_x25iface_proto_del( struct concap_proto * );
44 static int isdn_x25iface_proto_close( struct concap_proto * );
45 static int isdn_x25iface_proto_restart( struct concap_proto *,
46                                         struct net_device *,
47                                         struct concap_device_ops *);
48 static int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * );
49 static int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * );
50 static int isdn_x25iface_connect_ind( struct concap_proto * );
51 static int isdn_x25iface_disconn_ind( struct concap_proto * );
52
53
54 static 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 */
66 static 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 */
73 static 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  */
83 struct 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  */
105 static 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  */
132 static 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  */
161 static 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  */
190 static 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  */
209 static 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  */
240 static 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    */
269 static 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 }