File: /usr/src/linux/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/version.h>
41 #include <linux/types.h>
42 #include <linux/sched.h>
43 #include <linux/netdevice.h>
44 #include <linux/proc_fs.h>
45 #include <linux/if_arp.h>
46 #include <linux/inetdevice.h>
47 #include <linux/pkt_sched.h>
48 #include <linux/init.h>
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 fr->keepa_timer.expires = jiffies + HZ;
218 fr->keepa_timer.function = fr_keepalive_timerfun;
219 fr->keepa_timer.data = (unsigned long)dev;
220 ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
221 dev->flags &= ~IFF_RUNNING;
222 comx_status(dev, ch->line_status);
223 if(ch->line_status & LINE_UP) {
224 add_timer(&fr->keepa_timer);
225 }
226 }
227 }
228
229 static void fr_rx(struct net_device *dev, struct sk_buff *skb)
230 {
231 struct comx_channel *ch = dev->priv;
232 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
233 struct net_device *sdev = dev;
234 struct comx_channel *sch;
235 struct fr_data *sfr;
236 u16 dlci;
237 u8 nlpid;
238
239 if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
240 kfree_skb(skb);
241 return;
242 }
243
244 /* Itt majd ki kell talalni, melyik slave kapja a csomagot */
245 dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
246 if ((nlpid = skb->data[3]) == 0) { // Optional padding
247 nlpid = skb->data[4];
248 skb_pull(skb, 1);
249 }
250 skb_pull(skb, 4); /* DLCI and header throw away */
251
252 if (ch->debug_flags & DEBUG_COMX_DLCI) {
253 comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n",
254 dlci, nlpid);
255 comx_debug_skb(dev, skb, "Contents");
256 }
257
258 /* Megkeressuk, kihez tartozik */
259 for (; dir ; dir = dir->next) {
260 if(!S_ISDIR(dir->mode)) {
261 continue;
262 }
263 if ((sdev = dir->data) && (sch = sdev->priv) &&
264 (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
265 (sfr->master == dev) && (sfr->dlci == dlci)) {
266 skb->dev = sdev;
267 if (ch->debug_flags & DEBUG_COMX_DLCI) {
268 comx_debug(dev, "Passing it to %s\n",sdev->name);
269 }
270 if (dev != sdev) {
271 sch->stats.rx_packets++;
272 sch->stats.rx_bytes += skb->len;
273 }
274 break;
275 }
276 }
277 switch(nlpid) {
278 case NLPID_IP:
279 skb->protocol = htons(ETH_P_IP);
280 skb->mac.raw = skb->data;
281 comx_rx(sdev, skb);
282 break;
283 case NLPID_Q933_LMI:
284 fr_rx_lmi(dev, skb, dlci, nlpid);
285 default:
286 kfree_skb(skb);
287 break;
288 }
289 }
290
291 static int fr_tx(struct net_device *dev)
292 {
293 struct comx_channel *ch = dev->priv;
294 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
295 struct net_device *sdev;
296 struct comx_channel *sch;
297 struct fr_data *sfr;
298 int cnt = 1;
299
300 /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel,
301 es annal a slave-nel aki eppen kuldott.
302 Egy helyen akkor all, ha a master kuldott.
303 Ez megint jo lesz majd, ha utemezni akarunk */
304
305 /* This should be fixed, the slave tbusy should be set when
306 the masters queue is full and reset when not */
307
308 for (; dir ; dir = dir->next) {
309 if(!S_ISDIR(dir->mode)) {
310 continue;
311 }
312 if ((sdev = dir->data) && (sch = sdev->priv) &&
313 (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
314 (sfr->master == dev) && (netif_queue_stopped(sdev))) {
315 netif_wake_queue(sdev);
316 cnt++;
317 }
318 }
319
320 netif_wake_queue(dev);
321 return 0;
322 }
323
324 static void fr_status(struct net_device *dev, unsigned short status)
325 {
326 struct comx_channel *ch = dev->priv;
327 struct fr_data *fr = ch->LINE_privdata;
328 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
329 struct net_device *sdev;
330 struct comx_channel *sch;
331 struct fr_data *sfr;
332
333 if (status & LINE_UP) {
334 if (!fr->keepa_freq) {
335 status |= PROTO_UP;
336 }
337 } else {
338 status &= ~(PROTO_UP | PROTO_LOOP);
339 }
340
341 if (dev == fr->master && fr->keepa_freq) {
342 if (status & LINE_UP) {
343 fr->keepa_timer.expires = jiffies + HZ;
344 add_timer(&fr->keepa_timer);
345 fr->keepalivecnt = MAXALIVECNT + 1;
346 fr->keeploopcnt = 0;
347 } else {
348 del_timer(&fr->keepa_timer);
349 }
350 }
351
352 /* Itt a status valtozast vegig kell vinni az osszes slave-n */
353 for (; dir ; dir = dir->next) {
354 if(!S_ISDIR(dir->mode)) {
355 continue;
356 }
357
358 if ((sdev = dir->data) && (sch = sdev->priv) &&
359 (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) &&
360 (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
361 if(status & LINE_UP) {
362 netif_wake_queue(sdev);
363 }
364 comx_status(sdev, status);
365 if(status & (PROTO_UP | PROTO_LOOP)) {
366 dev->flags |= IFF_RUNNING;
367 } else {
368 dev->flags &= ~IFF_RUNNING;
369 }
370 }
371 }
372 }
373
374 static int fr_open(struct net_device *dev)
375 {
376 struct comx_channel *ch = dev->priv;
377 struct fr_data *fr = ch->LINE_privdata;
378 struct proc_dir_entry *comxdir = ch->procdir;
379 struct comx_channel *mch;
380
381 if (!(ch->init_status & HW_OPEN)) {
382 return -ENODEV;
383 }
384
385 if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
386 (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
387 printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
388 return -EINVAL;
389 }
390
391 if (!fr->master) {
392 return -ENODEV;
393 }
394 mch = fr->master->priv;
395 if (fr->master != dev && (!(mch->init_status & LINE_OPEN)
396 || (mch->protocol != &fr_master_protocol))) {
397 printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
398 "unable to open %s\n", fr->master->name, dev->name);
399 return -ENODEV;
400 }
401
402 ch->init_status |= LINE_OPEN;
403 ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
404 dev->flags &= ~IFF_RUNNING;
405
406 if (fr->master == dev) {
407 if (fr->keepa_freq) {
408 fr->keepa_timer.function = fr_keepalive_timerfun;
409 fr->keepa_timer.data = (unsigned long)dev;
410 add_timer(&fr->keepa_timer);
411 } else {
412 if (ch->line_status & LINE_UP) {
413 ch->line_status |= PROTO_UP;
414 dev->flags |= IFF_RUNNING;
415 }
416 }
417 } else {
418 ch->line_status = mch->line_status;
419 if(fr->master->flags & IFF_RUNNING) {
420 dev->flags |= IFF_RUNNING;
421 }
422 }
423
424 for (; comxdir ; comxdir = comxdir->next) {
425 if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
426 strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
427 strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
428 comxdir->mode = S_IFREG | 0444;
429 }
430 }
431 // comx_status(dev, ch->line_status);
432 return 0;
433 }
434
435 static int fr_close(struct net_device *dev)
436 {
437 struct comx_channel *ch = dev->priv;
438 struct fr_data *fr = ch->LINE_privdata;
439 struct proc_dir_entry *comxdir = ch->procdir;
440
441 if (fr->master == dev) { // Ha master
442 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
443 struct net_device *sdev = dev;
444 struct comx_channel *sch;
445 struct fr_data *sfr;
446
447 if (!(ch->init_status & HW_OPEN)) {
448 return -ENODEV;
449 }
450
451 if (fr->keepa_freq) {
452 del_timer(&fr->keepa_timer);
453 }
454
455 for (; dir ; dir = dir->next) {
456 if(!S_ISDIR(dir->mode)) {
457 continue;
458 }
459 if ((sdev = dir->data) && (sch = sdev->priv) &&
460 (sdev->type == ARPHRD_DLCI) &&
461 (sfr = sch->LINE_privdata) &&
462 (sfr->master == dev) &&
463 (sch->init_status & LINE_OPEN)) {
464 dev_close(sdev);
465 }
466 }
467 }
468
469 ch->init_status &= ~LINE_OPEN;
470 ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
471 dev->flags &= ~IFF_RUNNING;
472
473 for (; comxdir ; comxdir = comxdir->next) {
474 if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
475 strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
476 strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
477 comxdir->mode = S_IFREG | 0444;
478 }
479 }
480
481 return 0;
482 }
483
484 static int fr_xmit(struct sk_buff *skb, struct net_device *dev)
485 {
486 struct comx_channel *ch = dev->priv;
487 struct comx_channel *sch, *mch;
488 struct fr_data *fr = ch->LINE_privdata;
489 struct fr_data *sfr;
490 struct net_device *sdev;
491 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
492
493 if (!fr->master) {
494 printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
495 return 0;
496 }
497
498 mch = fr->master->priv;
499
500 /* Ennek majd a slave utemezeskor lesz igazan jelentosege */
501 if (ch->debug_flags & DEBUG_COMX_DLCI) {
502 comx_debug_skb(dev, skb, "Sending frame");
503 }
504
505 if (dev != fr->master) {
506 struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
507 if (!newskb)
508 return -ENOMEM;
509 newskb->dev=fr->master;
510 dev_queue_xmit(newskb);
511 ch->stats.tx_bytes += skb->len;
512 ch->stats.tx_packets++;
513 dev_kfree_skb(skb);
514 } else {
515 netif_stop_queue(dev);
516 for (; dir ; dir = dir->next) {
517 if(!S_ISDIR(dir->mode)) {
518 continue;
519 }
520 if ((sdev = dir->data) && (sch = sdev->priv) &&
521 (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
522 (sfr->master == dev) && (netif_queue_stopped(sdev))) {
523 netif_stop_queue(sdev);
524 }
525 }
526
527 switch(mch->HW_send_packet(dev, skb)) {
528 case FRAME_QUEUED:
529 netif_wake_queue(dev);
530 break;
531 case FRAME_ACCEPTED:
532 case FRAME_DROPPED:
533 break;
534 case FRAME_ERROR:
535 printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
536 dev->name, skb->len);
537 break;
538 }
539 }
540 return 0;
541 }
542
543 static int fr_header(struct sk_buff *skb, struct net_device *dev,
544 unsigned short type, void *daddr, void *saddr, unsigned len)
545 {
546 struct comx_channel *ch = dev->priv;
547 struct fr_data *fr = ch->LINE_privdata;
548
549 skb_push(skb, dev->hard_header_len);
550 /* Put in DLCI */
551 skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
552 skb->data[1] = (fr->dlci & 15) << 4 | 1; // EA bit 1
553 skb->data[2] = FRAD_UI;
554 skb->data[3] = NLPID_IP;
555
556 return dev->hard_header_len;
557 }
558
559 static int fr_statistics(struct net_device *dev, char *page)
560 {
561 struct comx_channel *ch = dev->priv;
562 struct fr_data *fr = ch->LINE_privdata;
563 int len = 0;
564
565 if (fr->master == dev) {
566 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
567 struct net_device *sdev;
568 struct comx_channel *sch;
569 struct fr_data *sfr;
570 int slaves = 0;
571
572 len += sprintf(page + len,
573 "This is a Frame Relay master device\nSlaves: ");
574 for (; dir ; dir = dir->next) {
575 if(!S_ISDIR(dir->mode)) {
576 continue;
577 }
578 if ((sdev = dir->data) && (sch = sdev->priv) &&
579 (sdev->type == ARPHRD_DLCI) &&
580 (sfr = sch->LINE_privdata) &&
581 (sfr->master == dev) && (sdev != dev)) {
582 slaves++;
583 len += sprintf(page + len, "%s ", sdev->name);
584 }
585 }
586 len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
587 if (fr->keepa_freq) {
588 len += sprintf(page + len, "Line keepalive (value %d) "
589 "status %s [%d]\n", fr->keepa_freq,
590 ch->line_status & PROTO_LOOP ? "LOOP" :
591 ch->line_status & PROTO_UP ? "UP" : "DOWN",
592 fr->keepalivecnt);
593 } else {
594 len += sprintf(page + len, "Line keepalive protocol "
595 "is not set\n");
596 }
597 } else { // if slave
598 len += sprintf(page + len,
599 "This is a Frame Relay slave device, master: %s\n",
600 fr->master ? fr->master->name : "(not set)");
601 }
602 return len;
603 }
604
605 static int fr_read_proc(char *page, char **start, off_t off, int count,
606 int *eof, void *data)
607 {
608 struct proc_dir_entry *file = (struct proc_dir_entry *)data;
609 struct net_device *dev = file->parent->data;
610 struct comx_channel *ch = dev->priv;
611 struct fr_data *fr = NULL;
612 int len = 0;
613
614 if (ch) {
615 fr = ch->LINE_privdata;
616 }
617
618 if (strcmp(file->name, FILENAME_DLCI) == 0) {
619 len = sprintf(page, "%04d\n", fr->dlci);
620 } else if (strcmp(file->name, FILENAME_MASTER) == 0) {
621 len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
622 "(none)");
623 } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
624 len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq)
625 : sprintf(page, "off\n");
626 } else {
627 printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
628 return -EBADF;
629 }
630
631 if (off >= len) {
632 *eof = 1;
633 return 0;
634 }
635
636 *start = page + off;
637 if (count >= len - off) *eof = 1;
638 return min_t(int, count, len - off);
639 }
640
641 static int fr_write_proc(struct file *file, const char *buffer,
642 u_long count, void *data)
643 {
644 struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
645 struct net_device *dev = entry->parent->data;
646 struct comx_channel *ch = dev->priv;
647 struct fr_data *fr = NULL;
648 char *page;
649
650 if (ch) {
651 fr = ch->LINE_privdata;
652 }
653
654 if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
655 return -ENOMEM;
656 }
657
658 copy_from_user(page, buffer, count);
659 if (*(page + count - 1) == '\n') {
660 *(page + count - 1) = 0;
661 }
662
663 if (strcmp(entry->name, FILENAME_DLCI) == 0) {
664 u16 dlci_new = simple_strtoul(page, NULL, 10);
665
666 if (dlci_new > 1023) {
667 printk(KERN_ERR "Invalid DLCI value\n");
668 }
669 else fr->dlci = dlci_new;
670 } else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
671 struct net_device *new_master = dev_get_by_name(page);
672
673 if (new_master && new_master->type == ARPHRD_FRAD) {
674 struct comx_channel *sch = new_master->priv;
675 struct fr_data *sfr = sch->LINE_privdata;
676
677 if (sfr && sfr->master == new_master) {
678 if(fr->master)
679 dev_put(fr->master);
680 fr->master = new_master;
681 /* Megorokli a master statuszat */
682 ch->line_status = sch->line_status;
683 }
684 }
685 } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
686 int keepa_new = -1;
687
688 if (strcmp(page, KEEPALIVE_OFF) == 0) {
689 keepa_new = 0;
690 } else {
691 keepa_new = simple_strtoul(page, NULL, 10);
692 }
693
694 if (keepa_new < 0 || keepa_new > 100) {
695 printk(KERN_ERR "invalid keepalive\n");
696 } else {
697 if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
698 fr_set_keepalive(dev, 0);
699 }
700 if (keepa_new) {
701 fr_set_keepalive(dev, keepa_new);
702 }
703 }
704 } else {
705 printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n",
706 entry->name);
707 count = -EBADF;
708 }
709
710 free_page((unsigned long)page);
711 return count;
712 }
713
714 static int fr_exit(struct net_device *dev)
715 {
716 struct comx_channel *ch = dev->priv;
717 struct fr_data *fr = ch->LINE_privdata;
718 struct net_device *sdev = dev;
719 struct comx_channel *sch;
720 struct fr_data *sfr;
721 struct proc_dir_entry *dir = ch->procdir->parent->subdir;
722
723 /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
724 if (fr->master && fr->master == dev) {
725 for (; dir ; dir = dir->next) {
726 if(!S_ISDIR(dir->mode)) {
727 continue;
728 }
729 if ((sdev = dir->data) && (sch = sdev->priv) &&
730 (sdev->type == ARPHRD_DLCI) &&
731 (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
732 dev_close(sdev);
733 sfr->master = NULL;
734 }
735 }
736 }
737 dev->flags = 0;
738 dev->type = 0;
739 dev->mtu = 0;
740 dev->hard_header_len = 0;
741
742 ch->LINE_rx = NULL;
743 ch->LINE_tx = NULL;
744 ch->LINE_status = NULL;
745 ch->LINE_open = NULL;
746 ch->LINE_close = NULL;
747 ch->LINE_xmit = NULL;
748 ch->LINE_header = NULL;
749 ch->LINE_rebuild_header = NULL;
750 ch->LINE_statistics = NULL;
751
752 ch->LINE_status = 0;
753
754 if (fr->master != dev) { // if not master, remove dlci
755 if(fr->master)
756 dev_put(fr->master);
757 remove_proc_entry(FILENAME_DLCI, ch->procdir);
758 remove_proc_entry(FILENAME_MASTER, ch->procdir);
759 } else {
760 if (fr->keepa_freq) {
761 fr_set_keepalive(dev, 0);
762 }
763 remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
764 remove_proc_entry(FILENAME_DLCI, ch->procdir);
765 }
766
767 kfree(fr);
768 ch->LINE_privdata = NULL;
769
770 MOD_DEC_USE_COUNT;
771 return 0;
772 }
773
774 static int fr_master_init(struct net_device *dev)
775 {
776 struct comx_channel *ch = dev->priv;
777 struct fr_data *fr;
778 struct proc_dir_entry *new_file;
779
780 if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
781 GFP_KERNEL)) == NULL) {
782 return -ENOMEM;
783 }
784 memset(fr, 0, sizeof(struct fr_data));
785 fr->master = dev; // this means master
786 fr->dlci = 0; // let's say default
787
788 dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
789 dev->type = ARPHRD_FRAD;
790 dev->mtu = 1500;
791 dev->hard_header_len = 4;
792 dev->addr_len = 0;
793
794 ch->LINE_rx = fr_rx;
795 ch->LINE_tx = fr_tx;
796 ch->LINE_status = fr_status;
797 ch->LINE_open = fr_open;
798 ch->LINE_close = fr_close;
799 ch->LINE_xmit = fr_xmit;
800 ch->LINE_header = fr_header;
801 ch->LINE_rebuild_header = NULL;
802 ch->LINE_statistics = fr_statistics;
803
804 if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
805 ch->procdir)) == NULL) {
806 goto cleanup_LINE_privdata;
807 }
808 new_file->data = (void *)new_file;
809 new_file->read_proc = &fr_read_proc;
810 new_file->write_proc = &fr_write_proc;
811 new_file->size = 5;
812 new_file->nlink = 1;
813
814 if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644,
815 ch->procdir)) == NULL) {
816 goto cleanup_filename_dlci;
817 }
818 new_file->data = (void *)new_file;
819 new_file->read_proc = &fr_read_proc;
820 new_file->write_proc = &fr_write_proc;
821 new_file->size = 4;
822 new_file->nlink = 1;
823
824 fr_set_keepalive(dev, 0);
825
826 MOD_INC_USE_COUNT;
827 return 0;
828 cleanup_filename_dlci:
829 remove_proc_entry(FILENAME_DLCI, ch->procdir);
830 cleanup_LINE_privdata:
831 kfree(fr);
832 return -EIO;
833 }
834
835 static int fr_slave_init(struct net_device *dev)
836 {
837 struct comx_channel *ch = dev->priv;
838 struct fr_data *fr;
839 struct proc_dir_entry *new_file;
840
841 if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data),
842 GFP_KERNEL)) == NULL) {
843 return -ENOMEM;
844 }
845 memset(fr, 0, sizeof(struct fr_data));
846
847 dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
848 dev->type = ARPHRD_DLCI;
849 dev->mtu = 1500;
850 dev->hard_header_len = 4;
851 dev->addr_len = 0;
852
853 ch->LINE_rx = fr_rx;
854 ch->LINE_tx = fr_tx;
855 ch->LINE_status = fr_status;
856 ch->LINE_open = fr_open;
857 ch->LINE_close = fr_close;
858 ch->LINE_xmit = fr_xmit;
859 ch->LINE_header = fr_header;
860 ch->LINE_rebuild_header = NULL;
861 ch->LINE_statistics = fr_statistics;
862
863 if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644,
864 ch->procdir)) == NULL) {
865 goto cleanup_LINE_privdata;
866 }
867
868 new_file->data = (void *)new_file;
869 new_file->read_proc = &fr_read_proc;
870 new_file->write_proc = &fr_write_proc;
871 new_file->size = 5;
872 new_file->nlink = 1;
873
874 if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644,
875 ch->procdir)) == NULL) {
876 goto cleanup_filename_dlci;
877 }
878 new_file->data = (void *)new_file;
879 new_file->read_proc = &fr_read_proc;
880 new_file->write_proc = &fr_write_proc;
881 new_file->size = 10;
882 new_file->nlink = 1;
883 MOD_INC_USE_COUNT;
884 return 0;
885 cleanup_filename_dlci:
886 remove_proc_entry(FILENAME_DLCI, ch->procdir);
887 cleanup_LINE_privdata:
888 kfree(fr);
889 return -EIO;
890 }
891
892 static int dlci_open(struct net_device *dev)
893 {
894 struct comx_channel *ch = dev->priv;
895
896 ch->init_status |= HW_OPEN;
897
898 MOD_INC_USE_COUNT;
899 return 0;
900 }
901
902 static int dlci_close(struct net_device *dev)
903 {
904 struct comx_channel *ch = dev->priv;
905
906 ch->init_status &= ~HW_OPEN;
907
908 MOD_DEC_USE_COUNT;
909 return 0;
910 }
911
912 static int dlci_txe(struct net_device *dev)
913 {
914 struct comx_channel *ch = dev->priv;
915 struct fr_data *fr = ch->LINE_privdata;
916
917 if (!fr->master) {
918 return 0;
919 }
920
921 ch = fr->master->priv;
922 fr = ch->LINE_privdata;
923 return ch->HW_txe(fr->master);
924 }
925
926 static int dlci_statistics(struct net_device *dev, char *page)
927 {
928 return 0;
929 }
930
931 static int dlci_init(struct net_device *dev)
932 {
933 struct comx_channel *ch = dev->priv;
934
935 ch->HW_open = dlci_open;
936 ch->HW_close = dlci_close;
937 ch->HW_txe = dlci_txe;
938 ch->HW_statistics = dlci_statistics;
939
940 /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
941
942 MOD_INC_USE_COUNT;
943 return 0;
944 }
945
946 static int dlci_exit(struct net_device *dev)
947 {
948 struct comx_channel *ch = dev->priv;
949
950 ch->HW_open = NULL;
951 ch->HW_close = NULL;
952 ch->HW_txe = NULL;
953 ch->HW_statistics = NULL;
954
955 MOD_DEC_USE_COUNT;
956 return 0;
957 }
958
959 static int dlci_dump(struct net_device *dev)
960 {
961 printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
962 return -1;
963 }
964
965 static struct comx_protocol fr_master_protocol = {
966 name: "frad",
967 version: VERSION,
968 encap_type: ARPHRD_FRAD,
969 line_init: fr_master_init,
970 line_exit: fr_exit,
971 };
972
973 static struct comx_protocol fr_slave_protocol = {
974 name: "ietf-ip",
975 version: VERSION,
976 encap_type: ARPHRD_DLCI,
977 line_init: fr_slave_init,
978 line_exit: fr_exit,
979 };
980
981 static struct comx_hardware fr_dlci = {
982 name: "dlci",
983 version: VERSION,
984 hw_init: dlci_init,
985 hw_exit: dlci_exit,
986 hw_dump: dlci_dump,
987 };
988
989 #ifdef MODULE
990 #define comx_proto_fr_init init_module
991 #endif
992
993 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 #ifdef MODULE
1007 void cleanup_module(void)
1008 {
1009 comx_unregister_hardware(fr_dlci.name);
1010 comx_unregister_protocol(fr_master_protocol.name);
1011 comx_unregister_protocol(fr_slave_protocol.name);
1012 }
1013 #endif /* MODULE */
1014
1015