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