d6a57195592a0bb41206d33766cf4ba184a1c5d5
[linux-2.4.git] / proc.c
1 /* net/atm/proc.c - ATM /proc interface */
2
3 /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
4
5 /*
6  * The mechanism used here isn't designed for speed but rather for convenience
7  * of implementation. We only return one entry per read system call, so we can
8  * be reasonably sure not to overrun the page and race conditions may lead to
9  * the addition or omission of some lines but never to any corruption of a
10  * line's internal structure.
11  *
12  * Making the whole thing slightly more efficient is left as an exercise to the
13  * reader. (Suggestions: wrapper which loops to get several entries per system
14  * call; or make --left slightly more clever to avoid O(n^2) characteristics.)
15  * I find it fast enough on my unloaded 266 MHz Pentium 2 :-)
16  */
17
18
19 #include <linux/config.h>
20 #include <linux/module.h> /* for EXPORT_SYMBOL */
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> /* for __init */
35 #include <asm/uaccess.h>
36 #include <asm/atomic.h>
37 #include <asm/param.h> /* for HZ */
38 #include "resources.h"
39 #include "common.h" /* atm_proc_init prototype */
40 #include "signaling.h" /* to get sigd - ugly too */
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
52 static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
53     loff_t *pos);
54 static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
55     loff_t *pos);
56
57 static struct file_operations proc_dev_atm_operations = {
58         read:           proc_dev_atm_read,
59 };
60
61 static struct file_operations proc_spec_atm_operations = {
62         read:           proc_spec_atm_read,
63 };
64
65 static 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
75 static 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
95 static 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
124 static 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
172 struct clip_seq_state {
173         /* This member must be first. */
174         struct neigh_seq_state ns;
175
176         /* Local to clip specific iteration. */
177         struct clip_vcc *vcc;
178 };
179
180 static 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
198 static 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
217 static 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
225 static 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
230 static 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
247 static 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
254 static 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;
274 out:
275         return rc;
276
277 out_kfree:
278         kfree(state);
279         goto out;
280 }
281
282 static 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
292 static 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",  /*  0- 3 */
297                 "???",  "5",    "???",  "???",  /*  4- 7 */
298                 "???",  "???",  "???",  "???",  /*  8-11 */
299                 "???",  "0",    "???",  "???"}; /* 12-15 */
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
326 static 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
334 static 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
359 static 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
383 static char*
384 lec_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
402 static void 
403 lec_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
432 static 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  * FIXME: it isn't safe to walk the VCC list without turning off interrupts.
458  * What is really needed is some lock on the devices. Ditto for ATMARP.
459  */
460
461 static 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
498 static 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
524 static 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)
548 static 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; /* the lane module is not there yet */
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
626 static 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
653 static 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
676 struct proc_dir_entry *atm_proc_root;
677 EXPORT_SYMBOL(atm_proc_root);
678
679
680 int 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;
702 fail0:
703         kfree(dev->proc_name);
704 fail1:
705         return error;
706 }
707
708
709 void 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
723 static struct proc_dir_entry *devices = NULL, *pvc = NULL,
724                 *svc = NULL, *arp = NULL, *lec = NULL, *vc = NULL;
725
726 static 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
743 int 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
763 cleanup:
764         atm_proc_cleanup();
765         return -ENOMEM;
766 }
767
768 void atm_proc_exit(void)
769 {
770         atm_proc_cleanup();
771 }