This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / net / wan / comx-proto-fr.c
1 /*
2  * Frame-relay protocol module for the COMX driver 
3  * for Linux 2.2.X
4  *
5  * Original author: Tivadar Szemethy <tiv@itc.hu>
6  * Maintainer: Gergely Madarasz <gorgo@itc.hu>
7  *
8  * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
9  * 
10  * Contributors:
11  * Arnaldo Carvalho de Melo <acme@conectiva.com.br> (0.73)
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version
16  * 2 of the License, or (at your option) any later version.
17  *
18  * Version 0.70 (99/06/14):
19  *              - cleaned up the source code a bit
20  *              - ported back to kernel, now works as builtin code 
21  *
22  * Version 0.71 (99/06/25):
23  *              - use skb priorities and queues for sending keepalive
24  *              - use device queues for slave->master data transmit
25  *              - set IFF_RUNNING only line protocol up
26  *              - fixes on slave device flags
27  * 
28  * Version 0.72 (99/07/09):
29  *              - handle slave tbusy with master tbusy (should be fixed)
30  *              - fix the keepalive timer addition/deletion
31  *
32  * Version 0.73 (00/08/15)
33  *              - resource release on failure at fr_master_init and
34  *                fr_slave_init                   
35  */
36
37 #define VERSION "0.73"
38
39 #include <linux/module.h>
40 #include <linux/types.h>
41 #include <linux/jiffies.h>
42 #include <linux/netdevice.h>
43 #include <linux/proc_fs.h>
44 #include <linux/if_arp.h>
45 #include <linux/inetdevice.h>
46 #include <linux/pkt_sched.h>
47 #include <linux/init.h>
48
49 #include <asm/uaccess.h>
50
51 #include "comx.h"
52 #include "comxhw.h"
53
54 MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");
55 MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers"
56         "for Linux kernel 2.4.X");
57 MODULE_LICENSE("GPL");
58
59 #define FRAD_UI         0x03
60 #define NLPID_IP        0xcc
61 #define NLPID_Q933_LMI  0x08
62 #define NLPID_CISCO_LMI 0x09    
63 #define Q933_ENQ        0x75
64 #define Q933_LINESTAT   0x51
65 #define Q933_COUNTERS   0x53
66
67 #define MAXALIVECNT     3               /* No. of failures */
68
69 struct fr_data {
70         u16     dlci;
71         struct  net_device *master;
72         char    keepa_pend;
73         char    keepa_freq;
74         char    keepalivecnt, keeploopcnt;
75         struct  timer_list keepa_timer;
76         u8      local_cnt, remote_cnt;
77 };
78
79 static struct comx_protocol fr_master_protocol;
80 static struct comx_protocol fr_slave_protocol;
81 static struct comx_hardware fr_dlci;
82
83 static void fr_keepalive_send(struct net_device *dev) 
84 {
85         struct comx_channel *ch = dev->priv;
86         struct fr_data *fr = ch->LINE_privdata;
87         struct sk_buff *skb;
88         u8 *fr_packet;
89         
90         skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC);
91         
92         if(skb==NULL)
93                 return;
94                
95         skb_reserve(skb, dev->hard_header_len);
96         
97         fr_packet=(u8*)skb_put(skb, 13);
98                  
99         fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2;
100         fr_packet[1] = (fr->dlci & 15) << 4 | 1;        // EA bit 1
101         fr_packet[2] = FRAD_UI;
102         fr_packet[3] = NLPID_Q933_LMI;
103         fr_packet[4] = 0;
104         fr_packet[5] = Q933_ENQ;
105         fr_packet[6] = Q933_LINESTAT;
106         fr_packet[7] = 0x01;
107         fr_packet[8] = 0x01;
108         fr_packet[9] = Q933_COUNTERS;
109         fr_packet[10] = 0x02;
110         fr_packet[11] = ++fr->local_cnt;
111         fr_packet[12] = fr->remote_cnt;
112
113         skb->dev = dev;
114         skb->priority = TC_PRIO_CONTROL;
115         dev_queue_xmit(skb);
116 }
117
118 static void fr_keepalive_timerfun(unsigned long d) 
119 {
120         struct net_device *dev = (struct net_device *)d;
121         struct comx_channel *ch = dev->priv;
122         struct fr_data *fr = ch->LINE_privdata;
123         struct proc_dir_entry *dir = ch->procdir->parent->subdir;
124         struct comx_channel *sch;
125         struct fr_data *sfr;
126         struct net_device *sdev;
127
128         if (ch->init_status & LINE_OPEN) {
129                 if (fr->keepalivecnt == MAXALIVECNT) {
130                         comx_status(dev, ch->line_status & ~PROTO_UP);
131                         dev->flags &= ~IFF_RUNNING;
132                         for (; dir ; dir = dir->next) {
133                                 if(!S_ISDIR(dir->mode)) {
134                                     continue;
135                                 }
136         
137                                 if ((sdev = dir->data) && (sch = sdev->priv) && 
138                                     (sdev->type == ARPHRD_DLCI) && 
139                                     (sfr = sch->LINE_privdata) 
140                                     && (sfr->master == dev) && 
141                                     (sdev->flags & IFF_UP)) {
142                                         sdev->flags &= ~IFF_RUNNING;
143                                         comx_status(sdev, 
144                                                 sch->line_status & ~PROTO_UP);
145                                 }
146                         }
147                 }
148                 if (fr->keepalivecnt <= MAXALIVECNT) {
149                         ++fr->keepalivecnt;
150                 }
151                 fr_keepalive_send(dev);
152         }
153         mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);
154 }
155
156 static void fr_rx_lmi(struct net_device *dev, struct sk_buff *skb, 
157         u16 dlci, u8 nlpid) 
158 {
159         struct comx_channel *ch = dev->priv;
160         struct fr_data *fr = ch->LINE_privdata;
161         struct proc_dir_entry *dir = ch->procdir->parent->subdir;
162         struct comx_channel *sch;
163         struct fr_data *sfr;
164         struct net_device *sdev;
165
166         if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) {
167                 return;
168         }
169
170         fr->remote_cnt = skb->data[7];
171         if (skb->data[8] == fr->local_cnt) { // keepalive UP!
172                 fr->keepalivecnt = 0;
173                 if ((ch->line_status & LINE_UP) && 
174                     !(ch->line_status & PROTO_UP)) {
175                         comx_status(dev, ch->line_status |= PROTO_UP);
176                         dev->flags |= IFF_RUNNING;
177                         for (; dir ; dir = dir->next) {
178                                 if(!S_ISDIR(dir->mode)) {
179                                     continue;
180                                 }
181         
182                                 if ((sdev = dir->data) && (sch = sdev->priv) && 
183                                     (sdev->type == ARPHRD_DLCI) && 
184                                     (sfr = sch->LINE_privdata) 
185                                     && (sfr->master == dev) && 
186                                     (sdev->flags & IFF_UP)) {
187                                         sdev->flags |= IFF_RUNNING;
188                                         comx_status(sdev, 
189                                                 sch->line_status | PROTO_UP);
190                                 }
191                         }
192                 }
193         }
194 }
195
196 static void fr_set_keepalive(struct net_device *dev, int keepa) 
197 {
198         struct comx_channel *ch = dev->priv;
199         struct fr_data *fr = ch->LINE_privdata;
200
201         if (!keepa && fr->keepa_freq) { // switch off
202                 fr->keepa_freq = 0;
203                 if (ch->line_status & LINE_UP) {
204                         comx_status(dev, ch->line_status | PROTO_UP);
205                         dev->flags |= IFF_RUNNING;
206                         del_timer(&fr->keepa_timer);
207                 }
208                 return;
209         }
210
211         if (keepa) { // bekapcs
212                 if(fr->keepa_freq && (ch->line_status & LINE_UP)) {
213                         del_timer(&fr->keepa_timer);
214                 }
215                 fr->keepa_freq = keepa;
216                 fr->local_cnt = fr->remote_cnt = 0;
217                 init_timer(&fr->keepa_timer);
218                 fr->keepa_timer.expires = jiffies + HZ;
219                 fr->keepa_timer.function = fr_keepalive_timerfun;
220                 fr->keepa_timer.data = (unsigned long)dev;
221                 ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
222                 dev->flags &= ~IFF_RUNNING;
223                 comx_status(dev, ch->line_status);
224                 if(ch->line_status & LINE_UP) {
225                         add_timer(&fr->keepa_timer);
226                 }
227         }
228 }
229
230 static void fr_rx(struct net_device *dev, struct sk_buff *skb) 
231 {
232         struct comx_channel *ch = dev->priv;
233         struct proc_dir_entry *dir = ch->procdir->parent->subdir;
234         struct net_device *sdev = dev;
235         struct comx_channel *sch;
236         struct fr_data *sfr;
237         u16 dlci;
238         u8 nlpid;
239
240         if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
241                 kfree_skb(skb);
242                 return;
243         }
244
245         /* Itt majd ki kell talalni, melyik slave kapja a csomagot */
246         dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
247         if ((nlpid = skb->data[3]) == 0) { // Optional padding 
248                 nlpid = skb->data[4];
249                 skb_pull(skb, 1);
250         }
251         skb_pull(skb, 4);       /* DLCI and header throw away */
252
253         if (ch->debug_flags & DEBUG_COMX_DLCI) {
254                 comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n", 
255                         dlci, nlpid);
256                 comx_debug_skb(dev, skb, "Contents");
257         }
258
259         /* Megkeressuk, kihez tartozik */
260         for (; dir ; dir = dir->next) {
261                 if(!S_ISDIR(dir->mode)) {
262                         continue;
263                 }
264                 if ((sdev = dir->data) && (sch = sdev->priv) && 
265                     (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
266                     (sfr->master == dev) && (sfr->dlci == dlci)) {
267                         skb->dev = sdev;        
268                         if (ch->debug_flags & DEBUG_COMX_DLCI) {
269                                 comx_debug(dev, "Passing it to %s\n",sdev->name);
270                         }
271                         if (dev != sdev) {
272                                 sch->stats.rx_packets++;
273                                 sch->stats.rx_bytes += skb->len;
274                         }
275                         break;
276                 }
277         }
278         switch(nlpid) {
279                 case NLPID_IP:
280                         skb->protocol = htons(ETH_P_IP);
281                         skb->mac.raw = skb->data;
282                         comx_rx(sdev, skb);
283                         break;
284                 case NLPID_Q933_LMI:
285                         fr_rx_lmi(dev, skb, dlci, nlpid);
286                 default:
287                         kfree_skb(skb);
288                         break;
289         }
290 }
291
292 static int fr_tx(struct net_device *dev) 
293 {
294         struct comx_channel *ch = dev->priv;
295         struct proc_dir_entry *dir = ch->procdir->parent->subdir;
296         struct net_device *sdev;
297         struct comx_channel *sch;
298         struct fr_data *sfr;
299         int cnt = 1;
300
301         /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel, 
302            es annal a slave-nel aki eppen kuldott.
303            Egy helyen akkor all, ha a master kuldott.
304            Ez megint jo lesz majd, ha utemezni akarunk */
305            
306         /* This should be fixed, the slave tbusy should be set when 
307            the masters queue is full and reset when not */
308
309         for (; dir ; dir = dir->next) {
310                 if(!S_ISDIR(dir->mode)) {
311                     continue;
312                 }
313                 if ((sdev = dir->data) && (sch = sdev->priv) && 
314                     (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
315                     (sfr->master == dev) && (netif_queue_stopped(sdev))) {
316                         netif_wake_queue(sdev);
317                         cnt++;
318                 }
319         }
320
321         netif_wake_queue(dev);
322         return 0;
323 }
324
325 static void fr_status(struct net_device *dev, unsigned short status)
326 {
327         struct comx_channel *ch = dev->priv;
328         struct fr_data *fr = ch->LINE_privdata;
329         struct proc_dir_entry *dir = ch->procdir->parent->subdir;
330         struct net_device *sdev;
331         struct comx_channel *sch;
332         struct fr_data *sfr;
333
334         if (status & LINE_UP) {
335                 if (!fr->keepa_freq) {
336                         status |= PROTO_UP;
337                 }
338         } else {
339                 status &= ~(PROTO_UP | PROTO_LOOP);
340         }
341
342         if (dev == fr->master && fr->keepa_freq) {
343                 if (status & LINE_UP) {
344                         fr->keepa_timer.expires = jiffies + HZ;
345                         add_timer(&fr->keepa_timer);
346                         fr->keepalivecnt = MAXALIVECNT + 1;
347                         fr->keeploopcnt = 0;
348                 } else {
349                         del_timer(&fr->keepa_timer);
350                 }
351         }
352                 
353         /* Itt a status valtozast vegig kell vinni az osszes slave-n */
354         for (; dir ; dir = dir->next) {
355                 if(!S_ISDIR(dir->mode)) {
356                     continue;
357                 }
358         
359                 if ((sdev = dir->data) && (sch = sdev->priv) && 
360                     (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) && 
361                     (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
362                         if(status & LINE_UP) {
363                                 netif_wake_queue(sdev);
364                         }
365                         comx_status(sdev, status);
366                         if(status & (PROTO_UP | PROTO_LOOP)) {
367                                 dev->flags |= IFF_RUNNING;
368                         } else {
369                                 dev->flags &= ~IFF_RUNNING;
370                         }
371                 }
372         }
373 }
374
375 static int fr_open(struct net_device *dev)
376 {
377         struct comx_channel *ch = dev->priv;
378         struct fr_data *fr = ch->LINE_privdata;
379         struct proc_dir_entry *comxdir = ch->procdir;
380         struct comx_channel *mch;
381
382         if (!(ch->init_status & HW_OPEN)) {
383                 return -ENODEV;
384         }
385
386         if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
387             (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
388                 printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
389                 return -EINVAL;
390         }
391
392         if (!fr->master) {
393                 return -ENODEV;
394         }
395         mch = fr->master->priv;
396         if (fr->master != dev && (!(mch->init_status & LINE_OPEN) 
397            || (mch->protocol != &fr_master_protocol))) {
398                 printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
399                         "unable to open %s\n", fr->master->name, dev->name);
400                 return -ENODEV;
401         }
402
403         ch->init_status |= LINE_OPEN;
404         ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
405         dev->flags &= ~IFF_RUNNING;
406
407         if (fr->master == dev) {
408                 if (fr->keepa_freq) {
409                         fr->keepa_timer.function = fr_keepalive_timerfun;
410                         fr->keepa_timer.data = (unsigned long)dev;
411                         add_timer(&fr->keepa_timer);
412                 } else {
413                         if (ch->line_status & LINE_UP) {
414                                 ch->line_status |= PROTO_UP;
415                                 dev->flags |= IFF_RUNNING;
416                         }
417                 }
418         } else {
419                 ch->line_status = mch->line_status;
420                 if(fr->master->flags & IFF_RUNNING) {
421                         dev->flags |= IFF_RUNNING;
422                 }
423         }
424
425         for (; comxdir ; comxdir = comxdir->next) {
426                 if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
427                    strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
428                    strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
429                         comxdir->mode = S_IFREG | 0444;
430                 }
431         }
432 //      comx_status(dev, ch->line_status);
433         return 0;
434 }
435
436 static int fr_close(struct net_device *dev)
437 {
438         struct comx_channel *ch = dev->priv;
439         struct fr_data *fr = ch->LINE_privdata;
440         struct proc_dir_entry *comxdir = ch->procdir;
441
442         if (fr->master == dev) { // Ha master 
443                 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
444                 struct net_device *sdev = dev;
445                 struct comx_channel *sch;
446                 struct fr_data *sfr;
447
448                 if (!(ch->init_status & HW_OPEN)) {
449                         return -ENODEV;
450                 }
451
452                 if (fr->keepa_freq) {
453                         del_timer(&fr->keepa_timer);
454                 }
455                 
456                 for (; dir ; dir = dir->next) {
457                         if(!S_ISDIR(dir->mode)) {
458                                 continue;
459                         }
460                         if ((sdev = dir->data) && (sch = sdev->priv) && 
461                             (sdev->type == ARPHRD_DLCI) && 
462                             (sfr = sch->LINE_privdata) &&
463                             (sfr->master == dev) && 
464                             (sch->init_status & LINE_OPEN)) {
465                                 dev_close(sdev);
466                         }
467                 }
468         }
469
470         ch->init_status &= ~LINE_OPEN;
471         ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
472         dev->flags &= ~IFF_RUNNING;
473
474         for (; comxdir ; comxdir = comxdir->next) {
475                 if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
476                     strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
477                     strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
478                         comxdir->mode = S_IFREG | 0444;
479                 }
480         }
481
482         return 0;
483 }
484
485 static int fr_xmit(struct sk_buff *skb, struct net_device *dev)
486 {
487         struct comx_channel *ch = dev->priv;
488         struct comx_channel *sch, *mch;
489         struct fr_data *fr = ch->LINE_privdata;
490         struct fr_data *sfr;
491         struct net_device *sdev;
492         struct proc_dir_entry *dir = ch->procdir->parent->subdir;
493
494         if (!fr->master) {
495                 printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
496                 return 0;
497         }
498
499         mch = fr->master->priv;
500
501         /* Ennek majd a slave utemezeskor lesz igazan jelentosege */
502         if (ch->debug_flags & DEBUG_COMX_DLCI) {
503                 comx_debug_skb(dev, skb, "Sending frame");
504         }
505
506         if (dev != fr->master) {
507                 struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
508                 if (!newskb)
509                         return -ENOMEM;
510                 newskb->dev=fr->master;
511                 dev_queue_xmit(newskb);
512                 ch->stats.tx_bytes += skb->len;
513                 ch->stats.tx_packets++;
514                 dev_kfree_skb(skb);
515         } else {
516                 netif_stop_queue(dev);
517                 for (; dir ; dir = dir->next) {
518                         if(!S_ISDIR(dir->mode)) {
519                             continue;
520                         }
521                         if ((sdev = dir->data) && (sch = sdev->priv) && 
522                             (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
523                             (sfr->master == dev) && (netif_queue_stopped(sdev))) {
524                                 netif_stop_queue(sdev);
525                         }
526                 }
527                         
528                 switch(mch->HW_send_packet(dev, skb)) {
529                         case FRAME_QUEUED:
530                                 netif_wake_queue(dev);
531                                 break;
532                         case FRAME_ACCEPTED:
533                         case FRAME_DROPPED:
534                                 break;
535                         case FRAME_ERROR:
536                                 printk(KERN_ERR "%s: Transmit frame error (len %d)\n", 
537                                         dev->name, skb->len);
538                                 break;
539                 }
540         }
541         return 0;
542 }
543
544 static int fr_header(struct sk_buff *skb, struct net_device *dev, 
545         unsigned short type, void *daddr, void *saddr, unsigned len) 
546 {
547         struct comx_channel *ch = dev->priv;
548         struct fr_data *fr = ch->LINE_privdata;
549
550         skb_push(skb, dev->hard_header_len);      
551         /* Put in DLCI */
552         skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
553         skb->data[1] = (fr->dlci & 15) << 4 | 1;        // EA bit 1
554         skb->data[2] = FRAD_UI;
555         skb->data[3] = NLPID_IP;
556
557         return dev->hard_header_len;  
558 }
559
560 static int fr_statistics(struct net_device *dev, char *page) 
561 {
562         struct comx_channel *ch = dev->priv;
563         struct fr_data *fr = ch->LINE_privdata;
564         int len = 0;
565
566         if (fr->master == dev) {
567                 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
568                 struct net_device *sdev;
569                 struct comx_channel *sch;
570                 struct fr_data *sfr;
571                 int slaves = 0;
572
573                 len += sprintf(page + len, 
574                         "This is a Frame Relay master device\nSlaves: ");
575                 for (; dir ; dir = dir->next) {
576                         if(!S_ISDIR(dir->mode)) {
577                                 continue;
578                         }
579                         if ((sdev = dir->data) && (sch = sdev->priv) && 
580                             (sdev->type == ARPHRD_DLCI) &&
581                             (sfr = sch->LINE_privdata) && 
582                             (sfr->master == dev) && (sdev != dev)) {
583                                 slaves++;
584                                 len += sprintf(page + len, "%s ", sdev->name);
585                         }
586                 }
587                 len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
588                 if (fr->keepa_freq) {
589                         len += sprintf(page + len, "Line keepalive (value %d) "
590                                 "status %s [%d]\n", fr->keepa_freq, 
591                                 ch->line_status & PROTO_LOOP ? "LOOP" :
592                                 ch->line_status & PROTO_UP ? "UP" : "DOWN", 
593                                 fr->keepalivecnt);
594                 } else {
595                         len += sprintf(page + len, "Line keepalive protocol "
596                                 "is not set\n");
597                 }
598         } else {                // if slave
599                 len += sprintf(page + len, 
600                         "This is a Frame Relay slave device, master: %s\n",
601                         fr->master ? fr->master->name : "(not set)");
602         }
603         return len;
604 }
605
606 static int fr_read_proc(char *page, char **start, off_t off, int count,
607         int *eof, void *data)
608 {
609         struct proc_dir_entry *file = (struct proc_dir_entry *)data;
610         struct net_device *dev = file->parent->data;
611         struct comx_channel *ch = dev->priv;
612         struct fr_data *fr = NULL;
613         int len = 0;
614
615         if (ch) {
616                 fr = ch->LINE_privdata;
617         }
618
619         if (strcmp(file->name, FILENAME_DLCI) == 0) {
620                 len = sprintf(page, "%04d\n", fr->dlci);
621         } else if (strcmp(file->name, FILENAME_MASTER) == 0) {
622                 len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
623                         "(none)");
624         } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
625                 len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq) 
626                         : sprintf(page, "off\n");
627         } else {
628                 printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
629                 return -EBADF;
630         }
631
632         if (off >= len) {
633                 *eof = 1;
634                 return 0;
635         }
636
637         *start = page + off;
638         if (count >= len - off) *eof = 1;
639         return min_t(int, count, len - off);
640 }
641
642 static int fr_write_proc(struct file *file, const char *buffer, 
643         u_long count, void *data)
644 {
645         struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
646         struct net_device *dev = entry->parent->data;
647         struct comx_channel *ch = dev->priv;
648         struct fr_data *fr = NULL; 
649         char *page;
650
651         if (ch) {
652                 fr = ch->LINE_privdata;
653         }
654
655         if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
656                 return -ENOMEM;
657         }
658
659         if (copy_from_user(page, buffer, count)) {
660                 free_page((unsigned long)page);
661                 return -EFAULT;
662         }
663         if (*(page + count - 1) == '\n') {
664                 *(page + count - 1) = 0;
665         }
666
667         if (strcmp(entry->name, FILENAME_DLCI) == 0) {
668                 u16 dlci_new = simple_strtoul(page, NULL, 10);
669
670                 if (dlci_new > 1023) {
671                         printk(KERN_ERR "Invalid DLCI value\n");
672                 }
673                 else fr->dlci = dlci_new;
674         } else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
675                 struct net_device *new_master = dev_get_by_name(page);
676
677                 if (new_master && new_master->type == ARPHRD_FRAD) {
678                         struct comx_channel *sch = new_master->priv;
679                         struct fr_data *sfr = sch->LINE_privdata;
680
681                         if (sfr && sfr->master == new_master) {
682                                 if(fr->master)
683                                         dev_put(fr->master);
684                                 fr->master = new_master;
685                                 /* Megorokli a master statuszat */
686                                 ch->line_status = sch->line_status;
687                         }
688                 }
689         } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
690                 int keepa_new = -1;
691
692                 if (strcmp(page, KEEPALIVE_OFF) == 0) {
693                         keepa_new = 0;
694                 } else {
695                         keepa_new = simple_strtoul(page, NULL, 10);
696                 }
697
698                 if (keepa_new < 0 || keepa_new > 100) {
699                         printk(KERN_ERR "invalid keepalive\n");
700                 } else {
701                         if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
702                                 fr_set_keepalive(dev, 0);
703                         }
704                         if (keepa_new) {
705                                 fr_set_keepalive(dev, keepa_new);
706                         }
707                 }
708         } else {
709                 printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n", 
710                         entry->name);
711                 count = -EBADF;
712         }
713
714         free_page((unsigned long)page);
715         return count;
716 }
717
718 static int fr_exit(struct net_device *dev) 
719 {
720         struct comx_channel *ch = dev->priv;
721         struct fr_data *fr = ch->LINE_privdata;
722         struct net_device *sdev = dev;
723         struct comx_channel *sch;
724         struct fr_data *sfr;
725         struct proc_dir_entry *dir = ch->procdir->parent->subdir;
726
727         /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
728         if (fr->master && fr->master == dev) {
729                 for (; dir ; dir = dir->next) {
730                         if(!S_ISDIR(dir->mode)) {
731                                 continue;
732                         }
733                         if ((sdev = dir->data) && (sch = sdev->priv) && 
734                             (sdev->type == ARPHRD_DLCI) && 
735                             (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
736                                 dev_close(sdev);
737                                 sfr->master = NULL;
738                         }
739                 }
740         }
741         dev->flags              = 0;
742         dev->type               = 0;
743         dev->mtu                = 0;
744         dev->hard_header_len    = 0;
745
746         ch->LINE_rx     = NULL;
747         ch->LINE_tx     = NULL;
748         ch->LINE_status = NULL;
749         ch->LINE_open   = NULL;
750         ch->LINE_close  = NULL;
751         ch->LINE_xmit   = NULL;
752         ch->LINE_header = NULL;
753         ch->LINE_rebuild_header = NULL;
754         ch->LINE_statistics = NULL;
755
756         ch->LINE_status = 0;
757
758         if (fr->master != dev) { // if not master, remove dlci
759                 if(fr->master)
760                         dev_put(fr->master);
761                 remove_proc_entry(FILENAME_DLCI, ch->procdir);
762                 remove_proc_entry(FILENAME_MASTER, ch->procdir);
763         } else {
764                 if (fr->keepa_freq) {
765                         fr_set_keepalive(dev, 0);
766                 }
767                 remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
768                 remove_proc_entry(FILENAME_DLCI, ch->procdir);
769         }
770
771         kfree(fr);
772         ch->LINE_privdata = NULL;
773
774         MOD_DEC_USE_COUNT;
775         return 0;
776 }
777
778 static int fr_master_init(struct net_device *dev)
779 {
780         struct comx_channel *ch = dev->priv;
781         struct fr_data *fr;
782         struct proc_dir_entry *new_file;
783
784         if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), 
785             GFP_KERNEL)) == NULL) {
786                 return -ENOMEM;
787         }
788         memset(fr, 0, sizeof(struct fr_data));
789         fr->master = dev;       // this means master
790         fr->dlci = 0;           // let's say default
791
792         dev->flags      = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
793         dev->type       = ARPHRD_FRAD;
794         dev->mtu        = 1500;
795         dev->hard_header_len    = 4;            
796         dev->addr_len   = 0;
797
798         ch->LINE_rx     = fr_rx;
799         ch->LINE_tx     = fr_tx;
800         ch->LINE_status = fr_status;
801         ch->LINE_open   = fr_open;
802         ch->LINE_close  = fr_close;
803         ch->LINE_xmit   = fr_xmit;
804         ch->LINE_header = fr_header;
805         ch->LINE_rebuild_header = NULL;
806         ch->LINE_statistics = fr_statistics;
807
808         if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, 
809             ch->procdir)) == NULL) {
810                 goto cleanup_LINE_privdata;
811         }
812         new_file->data = (void *)new_file;
813         new_file->read_proc = &fr_read_proc;
814         new_file->write_proc = &fr_write_proc;
815         new_file->size = 5;
816         new_file->nlink = 1;
817
818         if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644, 
819             ch->procdir)) == NULL) {
820                 goto cleanup_filename_dlci;
821         }
822         new_file->data = (void *)new_file;
823         new_file->read_proc = &fr_read_proc;
824         new_file->write_proc = &fr_write_proc;
825         new_file->size = 4;
826         new_file->nlink = 1;
827
828         fr_set_keepalive(dev, 0);
829
830         MOD_INC_USE_COUNT;
831         return 0;
832 cleanup_filename_dlci:
833          remove_proc_entry(FILENAME_DLCI, ch->procdir);
834 cleanup_LINE_privdata:
835         kfree(fr);
836         return -EIO;
837 }
838
839 static int fr_slave_init(struct net_device *dev)
840 {
841         struct comx_channel *ch = dev->priv;
842         struct fr_data *fr;
843         struct proc_dir_entry *new_file;
844
845         if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), 
846             GFP_KERNEL)) == NULL) {
847                 return -ENOMEM;
848         }
849         memset(fr, 0, sizeof(struct fr_data));
850
851         dev->flags      = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
852         dev->type       = ARPHRD_DLCI;
853         dev->mtu        = 1500;
854         dev->hard_header_len    = 4;            
855         dev->addr_len   = 0;
856
857         ch->LINE_rx     = fr_rx;
858         ch->LINE_tx     = fr_tx;
859         ch->LINE_status = fr_status;
860         ch->LINE_open   = fr_open;
861         ch->LINE_close  = fr_close;
862         ch->LINE_xmit   = fr_xmit;
863         ch->LINE_header = fr_header;
864         ch->LINE_rebuild_header = NULL;
865         ch->LINE_statistics = fr_statistics;
866
867         if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, 
868             ch->procdir)) == NULL) {
869                 goto cleanup_LINE_privdata;
870         }
871         
872         new_file->data = (void *)new_file;
873         new_file->read_proc = &fr_read_proc;
874         new_file->write_proc = &fr_write_proc;
875         new_file->size = 5;
876         new_file->nlink = 1;
877
878         if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644, 
879             ch->procdir)) == NULL) {
880                 goto cleanup_filename_dlci;
881         }
882         new_file->data = (void *)new_file;
883         new_file->read_proc = &fr_read_proc;
884         new_file->write_proc = &fr_write_proc;
885         new_file->size = 10;
886         new_file->nlink = 1;
887         MOD_INC_USE_COUNT;
888         return 0;
889 cleanup_filename_dlci:
890          remove_proc_entry(FILENAME_DLCI, ch->procdir);
891 cleanup_LINE_privdata:
892         kfree(fr);
893         return -EIO;
894 }
895
896 static int dlci_open(struct net_device *dev)
897 {
898         struct comx_channel *ch = dev->priv;
899
900         ch->init_status |= HW_OPEN;
901
902         MOD_INC_USE_COUNT;
903         return 0;
904 }
905
906 static int dlci_close(struct net_device *dev)
907 {
908         struct comx_channel *ch = dev->priv;
909
910         ch->init_status &= ~HW_OPEN;
911
912         MOD_DEC_USE_COUNT;
913         return 0;
914 }
915
916 static int dlci_txe(struct net_device *dev)
917 {
918         struct comx_channel *ch = dev->priv;
919         struct fr_data *fr = ch->LINE_privdata;
920
921         if (!fr->master) {
922                 return 0;
923         }
924
925         ch = fr->master->priv;
926         fr = ch->LINE_privdata;
927         return ch->HW_txe(fr->master);
928 }
929
930 static int dlci_statistics(struct net_device *dev, char *page) 
931 {
932         return 0;
933 }
934
935 static int dlci_init(struct net_device *dev)
936 {
937         struct comx_channel *ch = dev->priv;
938
939         ch->HW_open = dlci_open;
940         ch->HW_close = dlci_close;
941         ch->HW_txe = dlci_txe;
942         ch->HW_statistics = dlci_statistics;
943
944         /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
945
946         MOD_INC_USE_COUNT;
947         return 0;
948 }
949
950 static int dlci_exit(struct net_device *dev)
951 {
952         struct comx_channel *ch = dev->priv;
953
954         ch->HW_open = NULL;
955         ch->HW_close = NULL;
956         ch->HW_txe = NULL;
957         ch->HW_statistics = NULL;
958
959         MOD_DEC_USE_COUNT;
960         return 0;
961 }
962
963 static int dlci_dump(struct net_device *dev)
964 {
965         printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
966         return -1;
967 }
968
969 static struct comx_protocol fr_master_protocol = {
970         .name           = "frad", 
971         .version        = VERSION,
972         .encap_type     = ARPHRD_FRAD, 
973         .line_init      = fr_master_init, 
974         .line_exit      = fr_exit, 
975 };
976
977 static struct comx_protocol fr_slave_protocol = {
978         .name           = "ietf-ip", 
979         .version        = VERSION,
980         .encap_type     = ARPHRD_DLCI, 
981         .line_init      = fr_slave_init, 
982         .line_exit      = fr_exit, 
983 };
984
985 static struct comx_hardware fr_dlci = { 
986         .name           = "dlci", 
987         .version        = VERSION,
988         .hw_init        = dlci_init, 
989         .hw_exit        = dlci_exit, 
990         .hw_dump        = dlci_dump, 
991 };
992
993 static int __init comx_proto_fr_init(void)
994 {
995         int ret; 
996
997         if ((ret = comx_register_hardware(&fr_dlci))) {
998                 return ret;
999         }
1000         if ((ret = comx_register_protocol(&fr_master_protocol))) {
1001                 return ret;
1002         }
1003         return comx_register_protocol(&fr_slave_protocol);
1004 }
1005
1006 static void __exit comx_proto_fr_exit(void)
1007 {
1008         comx_unregister_hardware(fr_dlci.name);
1009         comx_unregister_protocol(fr_master_protocol.name);
1010         comx_unregister_protocol(fr_slave_protocol.name);
1011 }
1012
1013 module_init(comx_proto_fr_init);
1014 module_exit(comx_proto_fr_exit);