This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / net / wan / comx-proto-lapb.c
1 /*
2  * LAPB protocol module for the COMX driver 
3  * for Linux kernel 2.2.X
4  *
5  * Original author: Tivadar Szemethy <tiv@itc.hu>
6  * Maintainer: Gergely Madarasz <gorgo@itc.hu>
7  *
8  * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version
13  * 2 of the License, or (at your option) any later version.
14  *
15  * Version 0.80 (99/06/14):
16  *              - cleaned up the source code a bit
17  *              - ported back to kernel, now works as non-module
18  *
19  * Changed      (00/10/29, Henner Eisen):
20  *              - comx_rx() / comxlapb_data_indication() return status.
21  * 
22  */
23
24 #define VERSION "0.80"
25
26 #include <linux/module.h>
27 #include <linux/types.h>
28 #include <linux/netdevice.h>
29 #include <linux/proc_fs.h>
30 #include <linux/if_arp.h>
31 #include <linux/inetdevice.h>
32 #include <asm/uaccess.h>
33 #include <linux/lapb.h>
34 #include <linux/init.h>
35
36 #include        "comx.h"
37 #include        "comxhw.h"
38
39 static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
40         int size, struct proc_dir_entry *dir);
41
42 static void comxlapb_rx(struct net_device *dev, struct sk_buff *skb) 
43 {
44         if (!dev || !dev->priv) {
45                 dev_kfree_skb(skb);
46         } else {
47                 lapb_data_received(dev, skb);
48         }
49 }
50
51 static int comxlapb_tx(struct net_device *dev) 
52 {
53         netif_wake_queue(dev);
54         return 0;
55 }
56
57 static int comxlapb_header(struct sk_buff *skb, struct net_device *dev, 
58         unsigned short type, void *daddr, void *saddr, unsigned len) 
59 {
60         return dev->hard_header_len;  
61 }
62
63 static void comxlapb_status(struct net_device *dev, unsigned short status)
64 {
65         struct comx_channel *ch;
66
67         if (!dev || !(ch = dev->priv)) {
68                 return;
69         }
70         if (status & LINE_UP) {
71                 netif_wake_queue(dev);
72         }
73         comx_status(dev, status);
74 }
75
76 static int comxlapb_open(struct net_device *dev)
77 {
78         struct comx_channel *ch = dev->priv;
79         int err = 0;
80
81         if (!(ch->init_status & HW_OPEN)) {
82                 return -ENODEV;
83         }
84
85         err = lapb_connect_request(dev);
86
87         if (ch->debug_flags & DEBUG_COMX_LAPB) {
88                 comx_debug(dev, "%s: lapb opened, error code: %d\n", 
89                         dev->name, err);
90         }
91
92         if (!err) {
93                 ch->init_status |= LINE_OPEN;
94                 MOD_INC_USE_COUNT;
95         }
96         return err;
97 }
98
99 static int comxlapb_close(struct net_device *dev)
100 {
101         struct comx_channel *ch = dev->priv;
102
103         if (!(ch->init_status & HW_OPEN)) {
104                 return -ENODEV;
105         }
106
107         if (ch->debug_flags & DEBUG_COMX_LAPB) {
108                 comx_debug(dev, "%s: lapb closed\n", dev->name);
109         }
110
111         lapb_disconnect_request(dev);
112
113         ch->init_status &= ~LINE_OPEN;
114         ch->line_status &= ~PROTO_UP;
115         MOD_DEC_USE_COUNT;
116         return 0;
117 }
118
119 static int comxlapb_xmit(struct sk_buff *skb, struct net_device *dev)
120 {
121         struct comx_channel *ch = dev->priv;
122         struct sk_buff *skb2;
123
124         if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) {
125                 return -ENODEV;
126         }
127
128         if (dev->type == ARPHRD_X25) { // first byte tells what to do 
129                 switch(skb->data[0]) {
130                         case 0x00:      
131                                 break;  // transmit
132                         case 0x01:      
133                                 lapb_connect_request(dev);
134                                 kfree_skb(skb);
135                                 return 0;
136                         case 0x02:      
137                                 lapb_disconnect_request(dev);
138                         default:
139                                 kfree_skb(skb);
140                                 return 0;
141                 }
142                 skb_pull(skb,1);
143         }
144
145         netif_stop_queue(dev);
146         
147         if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
148                 lapb_data_request(dev, skb2);
149         }
150
151         return FRAME_ACCEPTED;
152 }
153
154 static int comxlapb_statistics(struct net_device *dev, char *page) 
155 {
156         struct lapb_parms_struct parms;
157         int len = 0;
158
159         len += sprintf(page + len, "Line status: ");
160         if (lapb_getparms(dev, &parms) != LAPB_OK) {
161                 len += sprintf(page + len, "not initialized\n");
162                 return len;
163         }
164         len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, "
165                 "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE", 
166                 parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD",
167                 parms.t1timer, parms.t1, parms.t2timer, parms.t2, 
168                 parms.n2count, parms.n2, parms.window);
169
170         return len;
171 }
172
173 static int comxlapb_read_proc(char *page, char **start, off_t off, int count,
174         int *eof, void *data)
175 {
176         struct proc_dir_entry *file = (struct proc_dir_entry *)data;
177         struct net_device *dev = file->parent->data;
178         struct lapb_parms_struct parms;
179         int len = 0;
180
181         if (lapb_getparms(dev, &parms)) {
182                 return -ENODEV;
183         }
184
185         if (strcmp(file->name, FILENAME_T1) == 0) {
186                 len += sprintf(page + len, "%02u / %02u\n", 
187                         parms.t1timer, parms.t1);
188         } else if (strcmp(file->name, FILENAME_T2) == 0) {
189                 len += sprintf(page + len, "%02u / %02u\n", 
190                         parms.t2timer, parms.t2);
191         } else if (strcmp(file->name, FILENAME_N2) == 0) {
192                 len += sprintf(page + len, "%02u / %02u\n", 
193                         parms.n2count, parms.n2);
194         } else if (strcmp(file->name, FILENAME_WINDOW) == 0) {
195                 len += sprintf(page + len, "%u\n", parms.window);
196         } else if (strcmp(file->name, FILENAME_MODE) == 0) {
197                 len += sprintf(page + len, "%s, %s\n", 
198                         parms.mode & LAPB_DCE ? "DCE" : "DTE",
199                         parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD");
200         } else {
201                 printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name);
202                 return -EBADF;
203         }
204
205         if (off >= len) {
206                 *eof = 1;
207                 return 0;
208         }
209
210         *start = page + off;
211         if (count >= len - off) {
212                 *eof = 1;
213         }
214         return min_t(int, count, len - off);
215 }
216
217 static int comxlapb_write_proc(struct file *file, const char *buffer, 
218         u_long count, void *data)
219 {
220         struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
221         struct net_device *dev = entry->parent->data;
222         struct lapb_parms_struct parms;
223         unsigned long parm;
224         char *page;
225
226         if (lapb_getparms(dev, &parms)) {
227                 return -ENODEV;
228         }
229
230         if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
231                 return -ENOMEM;
232         }
233
234         if (copy_from_user(page, buffer, count)) {
235                 free_page((unsigned long)page);
236                 return -EFAULT;
237         }
238         if (*(page + count - 1) == '\n') {
239                 *(page + count - 1) = 0;
240         }
241
242         if (strcmp(entry->name, FILENAME_T1) == 0) {
243                 parm=simple_strtoul(page,NULL,10);
244                 if (parm > 0 && parm < 100) {
245                         parms.t1=parm;
246                         lapb_setparms(dev, &parms);
247                 }
248         } else if (strcmp(entry->name, FILENAME_T2) == 0) {
249                 parm=simple_strtoul(page, NULL, 10);
250                 if (parm > 0 && parm < 100) {
251                         parms.t2=parm;
252                         lapb_setparms(dev, &parms);
253                 }
254         } else if (strcmp(entry->name, FILENAME_N2) == 0) {
255                 parm=simple_strtoul(page, NULL, 10);
256                 if (parm > 0 && parm < 100) {
257                         parms.n2=parm;
258                         lapb_setparms(dev, &parms);
259                 }
260         } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
261                 parms.window = simple_strtoul(page, NULL, 10);
262                 lapb_setparms(dev, &parms);
263         } else if (strcmp(entry->name, FILENAME_MODE) == 0) {
264                 if (comx_strcasecmp(page, "dte") == 0) {
265                         parms.mode &= ~(LAPB_DCE | LAPB_DTE); 
266                         parms.mode |= LAPB_DTE;
267                 } else if (comx_strcasecmp(page, "dce") == 0) {
268                         parms.mode &= ~(LAPB_DTE | LAPB_DCE); 
269                         parms.mode |= LAPB_DCE;
270                 } else if (comx_strcasecmp(page, "std") == 0 || 
271                     comx_strcasecmp(page, "standard") == 0) {
272                         parms.mode &= ~LAPB_EXTENDED; 
273                         parms.mode |= LAPB_STANDARD;
274                 } else if (comx_strcasecmp(page, "ext") == 0 || 
275                     comx_strcasecmp(page, "extended") == 0) {
276                         parms.mode &= ~LAPB_STANDARD; 
277                         parms.mode |= LAPB_EXTENDED;
278                 }
279                 lapb_setparms(dev, &parms);
280         } else {
281                 printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n", 
282                         entry->name);
283                 return -EBADF;
284         }
285
286         free_page((unsigned long)page);
287         return count;
288 }
289
290 static void comxlapb_connected(struct net_device *dev, int reason)
291 {
292         struct comx_channel *ch = dev->priv; 
293         struct proc_dir_entry *comxdir = ch->procdir->subdir;
294
295         if (ch->debug_flags & DEBUG_COMX_LAPB) {
296                 comx_debug(ch->dev, "%s: lapb connected, reason: %d\n", 
297                         ch->dev->name, reason);
298         }
299
300         if (ch->dev->type == ARPHRD_X25) {
301                 unsigned char *p;
302                 struct sk_buff *skb;
303
304                 if ((skb = dev_alloc_skb(1)) == NULL) {
305                         printk(KERN_ERR "comxlapb: out of memory!\n");
306                         return;
307                 }
308                 p = skb_put(skb,1);
309                 *p = 0x01;              // link established
310                 skb->dev = ch->dev;
311                 skb->protocol = htons(ETH_P_X25);
312                 skb->mac.raw = skb->data;
313                 skb->pkt_type = PACKET_HOST;
314
315                 netif_rx(skb);
316                 ch->dev->last_rx = jiffies;
317         }
318
319         for (; comxdir; comxdir = comxdir->next) {
320                 if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
321                         comxdir->mode = S_IFREG | 0444;
322                 }
323         }
324
325
326         ch->line_status |= PROTO_UP;
327         comx_status(ch->dev, ch->line_status);
328 }
329
330 static void comxlapb_disconnected(struct net_device *dev, int reason)
331 {
332         struct comx_channel *ch = dev->priv; 
333         struct proc_dir_entry *comxdir = ch->procdir->subdir;
334
335         if (ch->debug_flags & DEBUG_COMX_LAPB) {
336                 comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n", 
337                         ch->dev->name, reason);
338         }
339
340         if (ch->dev->type == ARPHRD_X25) {
341                 unsigned char *p;
342                 struct sk_buff *skb;
343
344                 if ((skb = dev_alloc_skb(1)) == NULL) {
345                         printk(KERN_ERR "comxlapb: out of memory!\n");
346                         return;
347                 }
348                 p = skb_put(skb,1);
349                 *p = 0x02;              // link disconnected
350                 skb->dev = ch->dev;
351                 skb->protocol = htons(ETH_P_X25);
352                 skb->mac.raw = skb->data;
353                 skb->pkt_type = PACKET_HOST;
354
355                 netif_rx(skb);
356                 ch->dev->last_rx = jiffies;
357         }
358
359         for (; comxdir; comxdir = comxdir->next) {
360                 if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
361                         comxdir->mode = S_IFREG | 0644;
362                 }
363         }
364         
365         ch->line_status &= ~PROTO_UP;
366         comx_status(ch->dev, ch->line_status);
367 }
368
369 static int comxlapb_data_indication(struct net_device *dev, struct sk_buff *skb)
370 {
371         struct comx_channel *ch = dev->priv; 
372
373         if (ch->dev->type == ARPHRD_X25) {
374                 skb_push(skb, 1);
375
376                 if (skb_cow(skb, 1))
377                         return NET_RX_DROP;
378
379                 skb->data[0] = 0;       // indicate data for X25
380                 skb->protocol = htons(ETH_P_X25);
381         } else {
382                 skb->protocol = htons(ETH_P_IP);
383         }
384
385         skb->dev = ch->dev;
386         skb->mac.raw = skb->data;
387         return comx_rx(ch->dev, skb);
388 }
389
390 static void comxlapb_data_transmit(struct net_device *dev, struct sk_buff *skb)
391 {
392         struct comx_channel *ch = dev->priv; 
393
394         if (ch->HW_send_packet) {
395                 ch->HW_send_packet(ch->dev, skb);
396         }
397 }
398
399 static int comxlapb_exit(struct net_device *dev) 
400 {
401         struct comx_channel *ch = dev->priv;
402
403         dev->flags              = 0;
404         dev->type               = 0;
405         dev->mtu                = 0;
406         dev->hard_header_len    = 0;
407
408         ch->LINE_rx     = NULL;
409         ch->LINE_tx     = NULL;
410         ch->LINE_status = NULL;
411         ch->LINE_open   = NULL;
412         ch->LINE_close  = NULL;
413         ch->LINE_xmit   = NULL;
414         ch->LINE_header = NULL;
415         ch->LINE_statistics = NULL;
416
417         if (ch->debug_flags & DEBUG_COMX_LAPB) {
418                 comx_debug(dev, "%s: unregistering lapb\n", dev->name);
419         }
420         lapb_unregister(dev);
421
422         remove_proc_entry(FILENAME_T1, ch->procdir);
423         remove_proc_entry(FILENAME_T2, ch->procdir);
424         remove_proc_entry(FILENAME_N2, ch->procdir);
425         remove_proc_entry(FILENAME_MODE, ch->procdir);
426         remove_proc_entry(FILENAME_WINDOW, ch->procdir);
427
428         MOD_DEC_USE_COUNT;
429         return 0;
430 }
431
432 static int comxlapb_init(struct net_device *dev)
433 {
434         struct comx_channel *ch = dev->priv;
435         struct lapb_register_struct lapbreg;
436
437         dev->mtu                = 1500;
438         dev->hard_header_len    = 4;
439         dev->addr_len           = 0;
440
441         ch->LINE_rx     = comxlapb_rx;
442         ch->LINE_tx     = comxlapb_tx;
443         ch->LINE_status = comxlapb_status;
444         ch->LINE_open   = comxlapb_open;
445         ch->LINE_close  = comxlapb_close;
446         ch->LINE_xmit   = comxlapb_xmit;
447         ch->LINE_header = comxlapb_header;
448         ch->LINE_statistics = comxlapb_statistics;
449
450         lapbreg.connect_confirmation = comxlapb_connected;
451         lapbreg.connect_indication = comxlapb_connected;
452         lapbreg.disconnect_confirmation = comxlapb_disconnected;
453         lapbreg.disconnect_indication = comxlapb_disconnected;
454         lapbreg.data_indication = comxlapb_data_indication;
455         lapbreg.data_transmit = comxlapb_data_transmit;
456         if (lapb_register(dev, &lapbreg)) {
457                 return -ENOMEM;
458         }
459         if (ch->debug_flags & DEBUG_COMX_LAPB) {
460                 comx_debug(dev, "%s: lapb registered\n", dev->name);
461         }
462
463         if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) {
464                 return -ENOMEM;
465         }
466         if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) {
467                 return -ENOMEM;
468         }
469         if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) {
470                 return -ENOMEM;
471         }
472         if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) {
473                 return -ENOMEM;
474         }
475         if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) {
476                 return -ENOMEM;
477         }
478
479         MOD_INC_USE_COUNT;
480         return 0;
481 }
482
483 static int comxlapb_init_lapb(struct net_device *dev) 
484 {
485         dev->flags      = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
486         dev->type       = ARPHRD_LAPB;
487
488         return(comxlapb_init(dev));
489 }
490
491 static int comxlapb_init_x25(struct net_device *dev)
492 {
493         dev->flags              = IFF_NOARP;
494         dev->type               = ARPHRD_X25;
495
496         return(comxlapb_init(dev));
497 }
498
499 static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
500         int size, struct proc_dir_entry *dir)
501 {
502         struct proc_dir_entry *new_file;
503
504         if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
505                 new_file->data = (void *)new_file;
506                 new_file->read_proc = &comxlapb_read_proc;
507                 new_file->write_proc = &comxlapb_write_proc;
508                 new_file->size = size;
509                 new_file->nlink = 1;
510         }
511         return(new_file);
512 }
513
514 static struct comx_protocol comxlapb_protocol = {
515         "lapb", 
516         VERSION,
517         ARPHRD_LAPB, 
518         comxlapb_init_lapb, 
519         comxlapb_exit, 
520         NULL 
521 };
522
523 static struct comx_protocol comx25_protocol = {
524         "x25", 
525         VERSION,
526         ARPHRD_X25, 
527         comxlapb_init_x25, 
528         comxlapb_exit, 
529         NULL 
530 };
531
532 static int __init comx_proto_lapb_init(void)
533 {
534         int ret;
535
536         if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
537                 return ret;
538         }
539         return comx_register_protocol(&comx25_protocol);
540 }
541
542 static void __exit comx_proto_lapb_exit(void)
543 {
544         comx_unregister_protocol(comxlapb_protocol.name);
545         comx_unregister_protocol(comx25_protocol.name);
546 }
547
548 module_init(comx_proto_lapb_init);
549 module_exit(comx_proto_lapb_exit);
550
551 MODULE_LICENSE("GPL");