1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <linux/config.h>
20#include <linux/module.h>
21#include <linux/string.h>
22#include <linux/types.h>
23#include <linux/mm.h>
24#include <linux/fs.h>
25#include <linux/stat.h>
26#include <linux/proc_fs.h>
27#include <linux/errno.h>
28#include <linux/atm.h>
29#include <linux/atmdev.h>
30#include <linux/netdevice.h>
31#include <linux/atmclip.h>
32#include <linux/atmarp.h>
33#include <linux/if_arp.h>
34#include <linux/init.h>
35#include <asm/uaccess.h>
36#include <asm/atomic.h>
37#include <asm/param.h>
38#include "resources.h"
39#include "common.h"
40#include "signaling.h"
41
42#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
43#include <net/atmclip.h>
44#include "ipcommon.h"
45#endif
46
47#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
48#include "lec.h"
49#include "lec_arpc.h"
50#endif
51
52static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
53 loff_t *pos);
54static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
55 loff_t *pos);
56
57static struct file_operations proc_dev_atm_operations = {
58 read: proc_dev_atm_read,
59};
60
61static struct file_operations proc_spec_atm_operations = {
62 read: proc_spec_atm_read,
63};
64
65static void add_stats(char *buf,const char *aal,
66 const struct k_atm_aal_stats *stats)
67{
68 sprintf(strchr(buf,0),"%s ( %d %d %d %d %d )",aal,
69 atomic_read(&stats->tx),atomic_read(&stats->tx_err),
70 atomic_read(&stats->rx),atomic_read(&stats->rx_err),
71 atomic_read(&stats->rx_drop));
72}
73
74
75static void dev_info(const struct atm_dev *dev,char *buf)
76{
77 int off,i;
78
79 off = sprintf(buf,"%3d %-8s",dev->number,dev->type);
80 for (i = 0; i < ESI_LEN; i++)
81 off += sprintf(buf+off,"%02x",dev->esi[i]);
82 strcat(buf," ");
83 add_stats(buf,"0",&dev->stats.aal0);
84 strcat(buf," ");
85 add_stats(buf,"5",&dev->stats.aal5);
86 sprintf(strchr(buf,0), "\t[%d]", atomic_read(&dev->refcnt));
87 strcat(buf,"\n");
88}
89
90
91#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
92
93#define SEQ_NO_VCC_TOKEN ((void *) 2)
94
95static void svc_addr(struct seq_file *seq, struct sockaddr_atmsvc *addr)
96{
97 static int code[] = { 1,2,10,6,1,0 };
98 static int e164[] = { 1,8,4,6,1,0 };
99
100 if (*addr->sas_addr.pub) {
101 seq_printf(seq, "%s", addr->sas_addr.pub);
102 if (*addr->sas_addr.prv)
103 seq_putc(seq, '+');
104 } else if (!*addr->sas_addr.prv) {
105 seq_printf(seq, "%s", "(none)");
106 return;
107 }
108 if (*addr->sas_addr.prv) {
109 unsigned char *prv = addr->sas_addr.prv;
110 int *fields;
111 int i, j;
112
113 fields = *prv == ATM_AFI_E164 ? e164 : code;
114 for (i = 0; fields[i]; i++) {
115 for (j = fields[i]; j; j--)
116 seq_printf(seq, "%02X", *prv++);
117 if (fields[i+1])
118 seq_putc(seq, '.');
119 }
120 }
121}
122
123
124static void atmarp_info(struct seq_file *seq, struct net_device *dev,struct
125 atmarp_entry *entry, struct clip_vcc *clip_vcc) {
126 unsigned long exp;
127 char buf[17];
128 int svc, llc, off;
129
130 svc = ((clip_vcc == SEQ_NO_VCC_TOKEN) ||
131 (clip_vcc->vcc->sk->family == AF_ATMSVC));
132
133 llc = ((clip_vcc == SEQ_NO_VCC_TOKEN) ||
134 (clip_vcc->encap));
135
136 if (clip_vcc == SEQ_NO_VCC_TOKEN)
137 exp = entry->neigh->used;
138 else
139 exp = clip_vcc->last_use;
140
141 exp = (jiffies - exp) / HZ;
142
143 seq_printf(seq, "%-6s%-4s%-4s%5ld ",
144 dev->name,
145 svc ? "SVC" : "PVC",
146 llc ? "LLC" : "NULL",
147 exp);
148
149 off = snprintf(buf, sizeof(buf)-1, "%d.%d.%d.%d", NIPQUAD(entry->ip));
150 while (off < 16)
151 buf[off++] = ' ';
152 buf[off] = '\0';
153 seq_printf(seq, "%s", buf);
154
155 if (clip_vcc == SEQ_NO_VCC_TOKEN) {
156 if (time_before(jiffies, entry->expires))
157 seq_printf(seq, "(resolving)\n");
158 else
159 seq_printf(seq, "(expired, ref %d)\n",
160 atomic_read(&entry->neigh->refcnt));
161 } else if (!svc) {
162 seq_printf(seq, "%d.%d.%d\n",
163 clip_vcc->vcc->dev->number,
164 clip_vcc->vcc->vpi,
165 clip_vcc->vcc->vci);
166 } else {
167 svc_addr(seq, &clip_vcc->vcc->remote);
168 seq_putc(seq, '\n');
169 }
170}
171
172struct clip_seq_state {
173
174 struct neigh_seq_state ns;
175
176
177 struct clip_vcc *vcc;
178};
179
180static struct clip_vcc *clip_seq_next_vcc(struct atmarp_entry *e,
181 struct clip_vcc *curr)
182{
183 if (!curr) {
184 curr = e->vccs;
185 if (!curr)
186 return SEQ_NO_VCC_TOKEN;
187 return curr;
188 }
189
190 if (curr == SEQ_NO_VCC_TOKEN)
191 return NULL;
192
193 curr = curr->next;
194
195 return curr;
196}
197
198static void *clip_seq_vcc_walk(struct clip_seq_state *state,
199 struct atmarp_entry *e, loff_t *pos)
200{
201 struct clip_vcc *vcc = state->vcc;
202
203 vcc = clip_seq_next_vcc(e, vcc);
204 if (vcc && pos != NULL) {
205 while (*pos) {
206 vcc = clip_seq_next_vcc(e, vcc);
207 if (!vcc)
208 break;
209 --(*pos);
210 }
211 }
212 state->vcc = vcc;
213
214 return vcc;
215}
216
217static void *clip_seq_sub_iter(struct neigh_seq_state *_state,
218 struct neighbour *n, loff_t *pos)
219{
220 struct clip_seq_state *state = (struct clip_seq_state *) _state;
221
222 return clip_seq_vcc_walk(state, NEIGH2ENTRY(n), pos);
223}
224
225static void *clip_seq_start(struct seq_file *seq, loff_t *pos)
226{
227 return neigh_seq_start(seq, pos, clip_tbl_hook, NEIGH_SEQ_NEIGH_ONLY);
228}
229
230static int clip_seq_show(struct seq_file *seq, void *v)
231{
232 static char atm_arp_banner[] =
233 "IPitf TypeEncp Idle IP address ATM address\n";
234
235 if (v == SEQ_START_TOKEN) {
236 seq_puts(seq, atm_arp_banner);
237 } else {
238 struct clip_seq_state *state = seq->private;
239 struct neighbour *n = v;
240 struct clip_vcc *vcc = state->vcc;
241
242 atmarp_info(seq, n->dev, NEIGH2ENTRY(n), vcc);
243 }
244 return 0;
245}
246
247static struct seq_operations arp_seq_ops = {
248 .start = clip_seq_start,
249 .next = neigh_seq_next,
250 .stop = neigh_seq_stop,
251 .show = clip_seq_show,
252};
253
254static int arp_seq_open(struct inode *inode, struct file *file)
255{
256 struct clip_seq_state *state;
257 struct seq_file *seq;
258 int rc = -EAGAIN;
259
260 state = kmalloc(sizeof(*state), GFP_KERNEL);
261 if (!state) {
262 rc = -ENOMEM;
263 goto out_kfree;
264 }
265 memset(state, 0, sizeof(*state));
266 state->ns.neigh_sub_iter = clip_seq_sub_iter;
267
268 rc = seq_open(file, &arp_seq_ops);
269 if (rc)
270 goto out_kfree;
271
272 seq = file->private_data;
273 seq->private = state;
274out:
275 return rc;
276
277out_kfree:
278 kfree(state);
279 goto out;
280}
281
282static struct file_operations arp_seq_fops = {
283 .open = arp_seq_open,
284 .read = seq_read,
285 .llseek = seq_lseek,
286 .release = seq_release_private,
287 .owner = THIS_MODULE,
288};
289#endif
290
291
292static void pvc_info(struct atm_vcc *vcc, char *buf, int clip_info)
293{
294 static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
295 static const char *aal_name[] = {
296 "---", "1", "2", "3/4",
297 "???", "5", "???", "???",
298 "???", "???", "???", "???",
299 "???", "0", "???", "???"};
300 int off;
301
302 off = sprintf(buf,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
303 vcc->dev->number,vcc->vpi,vcc->vci,
304 vcc->qos.aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" :
305 aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr,
306 class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
307 class_name[vcc->qos.txtp.traffic_class]);
308#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
309 if (clip_info && (vcc->push == atm_clip_ops->clip_push)) {
310 struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
311 struct net_device *dev;
312
313 dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL;
314 off += sprintf(buf+off,"CLIP, Itf:%s, Encap:",
315 dev ? dev->name : "none?");
316 if (clip_vcc->encap)
317 off += sprintf(buf+off,"LLC/SNAP");
318 else
319 off += sprintf(buf+off,"None");
320 }
321#endif
322 strcpy(buf+off,"\n");
323}
324
325
326static const char *vcc_state(struct atm_vcc *vcc)
327{
328 static const char *map[] = { ATM_VS2TXT_MAP };
329
330 return map[ATM_VF2VS(vcc->flags)];
331}
332
333
334static void vc_info(struct atm_vcc *vcc,char *buf)
335{
336 char *here;
337
338 here = buf+sprintf(buf,"%p ",vcc);
339 if (!vcc->dev) here += sprintf(here,"Unassigned ");
340 else here += sprintf(here,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
341 vcc->vci);
342 switch (vcc->sk->family) {
343 case AF_ATMPVC:
344 here += sprintf(here,"PVC");
345 break;
346 case AF_ATMSVC:
347 here += sprintf(here,"SVC");
348 break;
349 default:
350 here += sprintf(here,"%3d",vcc->sk->family);
351 }
352 here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
353 vcc->reply,
354 atomic_read(&vcc->sk->wmem_alloc),vcc->sk->sndbuf,
355 atomic_read(&vcc->sk->rmem_alloc),vcc->sk->rcvbuf);
356}
357
358
359static void svc_info(struct atm_vcc *vcc,char *buf)
360{
361 char *here;
362 int i;
363
364 if (!vcc->dev)
365 sprintf(buf,sizeof(void *) == 4 ? "N/A@%p%10s" : "N/A@%p%2s",
366 vcc,"");
367 else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
368 vcc->vci);
369 here = strchr(buf,0);
370 here += sprintf(here,"%-10s ",vcc_state(vcc));
371 here += sprintf(here,"%s%s",vcc->remote.sas_addr.pub,
372 *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
373 if (*vcc->remote.sas_addr.prv)
374 for (i = 0; i < ATM_ESA_LEN; i++)
375 here += sprintf(here,"%02x",
376 vcc->remote.sas_addr.prv[i]);
377 strcat(here,"\n");
378}
379
380
381#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
382
383static char*
384lec_arp_get_status_string(unsigned char status)
385{
386 switch(status) {
387 case ESI_UNKNOWN:
388 return "ESI_UNKNOWN ";
389 case ESI_ARP_PENDING:
390 return "ESI_ARP_PENDING ";
391 case ESI_VC_PENDING:
392 return "ESI_VC_PENDING ";
393 case ESI_FLUSH_PENDING:
394 return "ESI_FLUSH_PENDING ";
395 case ESI_FORWARD_DIRECT:
396 return "ESI_FORWARD_DIRECT";
397 default:
398 return "<Unknown> ";
399 }
400}
401
402static void
403lec_info(struct lec_arp_table *entry, char *buf)
404{
405 int j, offset=0;
406
407 for(j=0;j<ETH_ALEN;j++) {
408 offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
409 }
410 offset+=sprintf(buf+offset, " ");
411 for(j=0;j<ATM_ESA_LEN;j++) {
412 offset+=sprintf(buf+offset,"%2.2x",0xff&entry->atm_addr[j]);
413 }
414 offset+=sprintf(buf+offset, " %s %4.4x",
415 lec_arp_get_status_string(entry->status),
416 entry->flags&0xffff);
417 if (entry->vcc) {
418 offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi,
419 entry->vcc->vci);
420 } else
421 offset+=sprintf(buf+offset, " ");
422 if (entry->recv_vcc) {
423 offset+=sprintf(buf+offset, " %3d %3d",
424 entry->recv_vcc->vpi, entry->recv_vcc->vci);
425 }
426
427 sprintf(buf+offset,"\n");
428}
429
430#endif
431
432static int atm_devices_info(loff_t pos,char *buf)
433{
434 struct atm_dev *dev;
435 struct list_head *p;
436 int left;
437
438 if (!pos) {
439 return sprintf(buf,"Itf Type ESI/\"MAC\"addr "
440 "AAL(TX,err,RX,err,drop) ... [refcnt]\n");
441 }
442 left = pos-1;
443 spin_lock(&atm_dev_lock);
444 list_for_each(p, &atm_devs) {
445 dev = list_entry(p, struct atm_dev, dev_list);
446 if (left-- == 0) {
447 dev_info(dev,buf);
448 spin_unlock(&atm_dev_lock);
449 return strlen(buf);
450 }
451 }
452 spin_unlock(&atm_dev_lock);
453 return 0;
454}
455
456
457
458
459
460
461static int atm_pvc_info(loff_t pos,char *buf)
462{
463 struct sock *s;
464 struct atm_vcc *vcc;
465 int left, clip_info = 0;
466
467 if (!pos) {
468 return sprintf(buf,"Itf VPI VCI AAL RX(PCR,Class) "
469 "TX(PCR,Class)\n");
470 }
471 left = pos-1;
472#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
473 if (try_atm_clip_ops())
474 clip_info = 1;
475#endif
476 read_lock(&vcc_sklist_lock);
477 for(s = vcc_sklist; s; s = s->next) {
478 vcc = s->protinfo.af_atm;
479 if (vcc->sk->family == PF_ATMPVC && vcc->dev && !left--) {
480 pvc_info(vcc,buf,clip_info);
481 read_unlock(&vcc_sklist_lock);
482#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
483 if (clip_info && atm_clip_ops->owner)
484 __MOD_DEC_USE_COUNT(atm_clip_ops->owner);
485#endif
486 return strlen(buf);
487 }
488 }
489 read_unlock(&vcc_sklist_lock);
490#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
491 if (clip_info && atm_clip_ops->owner)
492 __MOD_DEC_USE_COUNT(atm_clip_ops->owner);
493#endif
494 return 0;
495}
496
497
498static int atm_vc_info(loff_t pos,char *buf)
499{
500 struct atm_vcc *vcc;
501 struct sock *s;
502 int left;
503
504 if (!pos)
505 return sprintf(buf,sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
506 "Address"," Itf VPI VCI Fam Flags Reply Send buffer"
507 " Recv buffer\n");
508 left = pos-1;
509 read_lock(&vcc_sklist_lock);
510 for(s = vcc_sklist; s; s = s->next) {
511 vcc = s->protinfo.af_atm;
512 if (!left--) {
513 vc_info(vcc,buf);
514 read_unlock(&vcc_sklist_lock);
515 return strlen(buf);
516 }
517 }
518 read_unlock(&vcc_sklist_lock);
519
520 return 0;
521}
522
523
524static int atm_svc_info(loff_t pos,char *buf)
525{
526 struct sock *s;
527 struct atm_vcc *vcc;
528 int left;
529
530 if (!pos)
531 return sprintf(buf,"Itf VPI VCI State Remote\n");
532 left = pos-1;
533 read_lock(&vcc_sklist_lock);
534 for(s = vcc_sklist; s; s = s->next) {
535 vcc = s->protinfo.af_atm;
536 if (vcc->sk->family == PF_ATMSVC && !left--) {
537 svc_info(vcc,buf);
538 read_unlock(&vcc_sklist_lock);
539 return strlen(buf);
540 }
541 }
542 read_unlock(&vcc_sklist_lock);
543
544 return 0;
545}
546
547#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
548static int atm_lec_info(loff_t pos,char *buf)
549{
550 unsigned long flags;
551 struct lec_priv *priv;
552 struct lec_arp_table *entry;
553 int i, count, d, e;
554 struct net_device *dev;
555
556 if (!pos) {
557 return sprintf(buf,"Itf MAC ATM destination"
558 " Status Flags "
559 "VPI/VCI Recv VPI/VCI\n");
560 }
561 if (!try_atm_lane_ops())
562 return 0;
563
564 count = pos;
565 for(d = 0; d < MAX_LEC_ITF; d++) {
566 dev = atm_lane_ops->get_lec(d);
567 if (!dev || !(priv = (struct lec_priv *) dev->priv))
568 continue;
569 spin_lock_irqsave(&priv->lec_arp_lock, flags);
570 for(i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
571 for(entry = priv->lec_arp_tables[i]; entry; entry = entry->next) {
572 if (--count)
573 continue;
574 e = sprintf(buf,"%s ", dev->name);
575 lec_info(entry, buf+e);
576 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
577 dev_put(dev);
578 if (atm_lane_ops->owner)
579 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
580 return strlen(buf);
581 }
582 }
583 for(entry = priv->lec_arp_empty_ones; entry; entry = entry->next) {
584 if (--count)
585 continue;
586 e = sprintf(buf,"%s ", dev->name);
587 lec_info(entry, buf+e);
588 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
589 dev_put(dev);
590 if (atm_lane_ops->owner)
591 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
592 return strlen(buf);
593 }
594 for(entry = priv->lec_no_forward; entry; entry=entry->next) {
595 if (--count)
596 continue;
597 e = sprintf(buf,"%s ", dev->name);
598 lec_info(entry, buf+e);
599 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
600 dev_put(dev);
601 if (atm_lane_ops->owner)
602 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
603 return strlen(buf);
604 }
605 for(entry = priv->mcast_fwds; entry; entry = entry->next) {
606 if (--count)
607 continue;
608 e = sprintf(buf,"%s ", dev->name);
609 lec_info(entry, buf+e);
610 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
611 dev_put(dev);
612 if (atm_lane_ops->owner)
613 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
614 return strlen(buf);
615 }
616 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
617 dev_put(dev);
618 }
619 if (atm_lane_ops->owner)
620 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
621 return 0;
622}
623#endif
624
625
626static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
627 loff_t *pos)
628{
629 struct atm_dev *dev;
630 unsigned long page;
631 int length;
632
633 if (count == 0) return 0;
634 page = get_free_page(GFP_KERNEL);
635 if (!page) return -ENOMEM;
636 dev = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
637 ->data;
638 if (!dev->ops->proc_read)
639 length = -EINVAL;
640 else {
641 length = dev->ops->proc_read(dev,pos,(char *) page);
642 if (length > count) length = -EINVAL;
643 }
644 if (length >= 0) {
645 if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
646 (*pos)++;
647 }
648 free_page(page);
649 return length;
650}
651
652
653static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
654 loff_t *pos)
655{
656 unsigned long page;
657 int length;
658 int (*info)(loff_t,char *);
659 info = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
660 ->data;
661
662 if (count == 0) return 0;
663 page = get_free_page(GFP_KERNEL);
664 if (!page) return -ENOMEM;
665 length = (*info)(*pos,(char *) page);
666 if (length > count) length = -EINVAL;
667 if (length >= 0) {
668 if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
669 (*pos)++;
670 }
671 free_page(page);
672 return length;
673}
674
675
676struct proc_dir_entry *atm_proc_root;
677EXPORT_SYMBOL(atm_proc_root);
678
679
680int atm_proc_dev_register(struct atm_dev *dev)
681{
682 int digits,num;
683 int error;
684
685 error = -ENOMEM;
686 digits = 0;
687 for (num = dev->number; num; num /= 10) digits++;
688 if (!digits) digits++;
689
690 dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_ATOMIC);
691 if (!dev->proc_name)
692 goto fail1;
693 sprintf(dev->proc_name,"%s:%d",dev->type, dev->number);
694
695 dev->proc_entry = create_proc_entry(dev->proc_name, 0, atm_proc_root);
696 if (!dev->proc_entry)
697 goto fail0;
698 dev->proc_entry->data = dev;
699 dev->proc_entry->proc_fops = &proc_dev_atm_operations;
700 dev->proc_entry->owner = THIS_MODULE;
701 return 0;
702fail0:
703 kfree(dev->proc_name);
704fail1:
705 return error;
706}
707
708
709void atm_proc_dev_deregister(struct atm_dev *dev)
710{
711 remove_proc_entry(dev->proc_name, atm_proc_root);
712 kfree(dev->proc_name);
713}
714
715
716#define CREATE_ENTRY(name) \
717 name = create_proc_entry(#name,0,atm_proc_root); \
718 if (!name) goto cleanup; \
719 name->data = atm_##name##_info; \
720 name->proc_fops = &proc_spec_atm_operations; \
721 name->owner = THIS_MODULE
722
723static struct proc_dir_entry *devices = NULL, *pvc = NULL,
724 *svc = NULL, *arp = NULL, *lec = NULL, *vc = NULL;
725
726static void atm_proc_cleanup(void)
727{
728 if (devices)
729 remove_proc_entry("devices",atm_proc_root);
730 if (pvc)
731 remove_proc_entry("pvc",atm_proc_root);
732 if (svc)
733 remove_proc_entry("svc",atm_proc_root);
734 if (arp)
735 remove_proc_entry("arp",atm_proc_root);
736 if (lec)
737 remove_proc_entry("lec",atm_proc_root);
738 if (vc)
739 remove_proc_entry("vc",atm_proc_root);
740 remove_proc_entry("net/atm",NULL);
741}
742
743int atm_proc_init(void)
744{
745 atm_proc_root = proc_mkdir("net/atm",NULL);
746 if (!atm_proc_root)
747 return -ENOMEM;
748 CREATE_ENTRY(devices);
749 CREATE_ENTRY(pvc);
750 CREATE_ENTRY(svc);
751 CREATE_ENTRY(vc);
752#if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
753 arp = create_proc_entry("arp", S_IRUGO, atm_proc_root);
754 if (!arp)
755 goto cleanup;
756 arp->proc_fops = &arp_seq_fops;
757#endif
758#if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
759 CREATE_ENTRY(lec);
760#endif
761 return 0;
762
763cleanup:
764 atm_proc_cleanup();
765 return -ENOMEM;
766}
767
768void atm_proc_exit(void)
769{
770 atm_proc_cleanup();
771}
772