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
add_stats(char * buf,const char * aal,const struct k_atm_aal_stats * stats)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
dev_info(const struct atm_dev * dev,char * buf)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
svc_addr(struct seq_file * seq,struct sockaddr_atmsvc * addr)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
atmarp_info(struct seq_file * seq,struct net_device * dev,struct atmarp_entry * entry,struct clip_vcc * clip_vcc)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
clip_seq_next_vcc(struct atmarp_entry * e,struct clip_vcc * curr)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
clip_seq_vcc_walk(struct clip_seq_state * state,struct atmarp_entry * e,loff_t * pos)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
clip_seq_sub_iter(struct neigh_seq_state * _state,struct neighbour * n,loff_t * pos)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
clip_seq_start(struct seq_file * seq,loff_t * pos)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
clip_seq_show(struct seq_file * seq,void * v)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
arp_seq_open(struct inode * inode,struct file * file)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 if (!clip_tbl_hook)
261 goto out;
262
263 state = kmalloc(sizeof(*state), GFP_KERNEL);
264 if (!state) {
265 rc = -ENOMEM;
266 goto out_kfree;
267 }
268 memset(state, 0, sizeof(*state));
269 state->ns.neigh_sub_iter = clip_seq_sub_iter;
270
271 rc = seq_open(file, &arp_seq_ops);
272 if (rc)
273 goto out_kfree;
274
275 seq = file->private_data;
276 seq->private = state;
277 out:
278 return rc;
279
280 out_kfree:
281 kfree(state);
282 goto out;
283 }
284
285 static struct file_operations arp_seq_fops = {
286 .open = arp_seq_open,
287 .read = seq_read,
288 .llseek = seq_lseek,
289 .release = seq_release_private,
290 .owner = THIS_MODULE,
291 };
292 #endif
293
294
pvc_info(struct atm_vcc * vcc,char * buf,int clip_info)295 static void pvc_info(struct atm_vcc *vcc, char *buf, int clip_info)
296 {
297 static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
298 static const char *aal_name[] = {
299 "---", "1", "2", "3/4", /* 0- 3 */
300 "???", "5", "???", "???", /* 4- 7 */
301 "???", "???", "???", "???", /* 8-11 */
302 "???", "0", "???", "???"}; /* 12-15 */
303 int off;
304
305 off = sprintf(buf,"%3d %3d %5d %-3s %7d %-5s %7d %-6s",
306 vcc->dev->number,vcc->vpi,vcc->vci,
307 vcc->qos.aal >= sizeof(aal_name)/sizeof(aal_name[0]) ? "err" :
308 aal_name[vcc->qos.aal],vcc->qos.rxtp.min_pcr,
309 class_name[vcc->qos.rxtp.traffic_class],vcc->qos.txtp.min_pcr,
310 class_name[vcc->qos.txtp.traffic_class]);
311 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
312 if (clip_info && (vcc->push == atm_clip_ops->clip_push)) {
313 struct clip_vcc *clip_vcc = CLIP_VCC(vcc);
314 struct net_device *dev;
315
316 dev = clip_vcc->entry ? clip_vcc->entry->neigh->dev : NULL;
317 off += sprintf(buf+off,"CLIP, Itf:%s, Encap:",
318 dev ? dev->name : "none?");
319 if (clip_vcc->encap)
320 off += sprintf(buf+off,"LLC/SNAP");
321 else
322 off += sprintf(buf+off,"None");
323 }
324 #endif
325 strcpy(buf+off,"\n");
326 }
327
328
vcc_state(struct atm_vcc * vcc)329 static const char *vcc_state(struct atm_vcc *vcc)
330 {
331 static const char *map[] = { ATM_VS2TXT_MAP };
332
333 return map[ATM_VF2VS(vcc->flags)];
334 }
335
336
vc_info(struct atm_vcc * vcc,char * buf)337 static void vc_info(struct atm_vcc *vcc,char *buf)
338 {
339 char *here;
340
341 here = buf+sprintf(buf,"%p ",vcc);
342 if (!vcc->dev) here += sprintf(here,"Unassigned ");
343 else here += sprintf(here,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
344 vcc->vci);
345 switch (vcc->sk->family) {
346 case AF_ATMPVC:
347 here += sprintf(here,"PVC");
348 break;
349 case AF_ATMSVC:
350 here += sprintf(here,"SVC");
351 break;
352 default:
353 here += sprintf(here,"%3d",vcc->sk->family);
354 }
355 here += sprintf(here," %04lx %5d %7d/%7d %7d/%7d\n",vcc->flags.bits,
356 vcc->reply,
357 atomic_read(&vcc->sk->wmem_alloc),vcc->sk->sndbuf,
358 atomic_read(&vcc->sk->rmem_alloc),vcc->sk->rcvbuf);
359 }
360
361
svc_info(struct atm_vcc * vcc,char * buf)362 static void svc_info(struct atm_vcc *vcc,char *buf)
363 {
364 char *here;
365 int i;
366
367 if (!vcc->dev)
368 sprintf(buf,sizeof(void *) == 4 ? "N/A@%p%10s" : "N/A@%p%2s",
369 vcc,"");
370 else sprintf(buf,"%3d %3d %5d ",vcc->dev->number,vcc->vpi,
371 vcc->vci);
372 here = strchr(buf,0);
373 here += sprintf(here,"%-10s ",vcc_state(vcc));
374 here += sprintf(here,"%s%s",vcc->remote.sas_addr.pub,
375 *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
376 if (*vcc->remote.sas_addr.prv)
377 for (i = 0; i < ATM_ESA_LEN; i++)
378 here += sprintf(here,"%02x",
379 vcc->remote.sas_addr.prv[i]);
380 strcat(here,"\n");
381 }
382
383
384 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
385
386 static char*
lec_arp_get_status_string(unsigned char status)387 lec_arp_get_status_string(unsigned char status)
388 {
389 switch(status) {
390 case ESI_UNKNOWN:
391 return "ESI_UNKNOWN ";
392 case ESI_ARP_PENDING:
393 return "ESI_ARP_PENDING ";
394 case ESI_VC_PENDING:
395 return "ESI_VC_PENDING ";
396 case ESI_FLUSH_PENDING:
397 return "ESI_FLUSH_PENDING ";
398 case ESI_FORWARD_DIRECT:
399 return "ESI_FORWARD_DIRECT";
400 default:
401 return "<Unknown> ";
402 }
403 }
404
405 static void
lec_info(struct lec_arp_table * entry,char * buf)406 lec_info(struct lec_arp_table *entry, char *buf)
407 {
408 int j, offset=0;
409
410 for(j=0;j<ETH_ALEN;j++) {
411 offset+=sprintf(buf+offset,"%2.2x",0xff&entry->mac_addr[j]);
412 }
413 offset+=sprintf(buf+offset, " ");
414 for(j=0;j<ATM_ESA_LEN;j++) {
415 offset+=sprintf(buf+offset,"%2.2x",0xff&entry->atm_addr[j]);
416 }
417 offset+=sprintf(buf+offset, " %s %4.4x",
418 lec_arp_get_status_string(entry->status),
419 entry->flags&0xffff);
420 if (entry->vcc) {
421 offset+=sprintf(buf+offset, "%3d %3d ", entry->vcc->vpi,
422 entry->vcc->vci);
423 } else
424 offset+=sprintf(buf+offset, " ");
425 if (entry->recv_vcc) {
426 offset+=sprintf(buf+offset, " %3d %3d",
427 entry->recv_vcc->vpi, entry->recv_vcc->vci);
428 }
429
430 sprintf(buf+offset,"\n");
431 }
432
433 #endif
434
atm_devices_info(loff_t pos,char * buf)435 static int atm_devices_info(loff_t pos,char *buf)
436 {
437 struct atm_dev *dev;
438 struct list_head *p;
439 int left;
440
441 if (!pos) {
442 return sprintf(buf,"Itf Type ESI/\"MAC\"addr "
443 "AAL(TX,err,RX,err,drop) ... [refcnt]\n");
444 }
445 left = pos-1;
446 spin_lock(&atm_dev_lock);
447 list_for_each(p, &atm_devs) {
448 dev = list_entry(p, struct atm_dev, dev_list);
449 if (left-- == 0) {
450 dev_info(dev,buf);
451 spin_unlock(&atm_dev_lock);
452 return strlen(buf);
453 }
454 }
455 spin_unlock(&atm_dev_lock);
456 return 0;
457 }
458
459 /*
460 * FIXME: it isn't safe to walk the VCC list without turning off interrupts.
461 * What is really needed is some lock on the devices. Ditto for ATMARP.
462 */
463
atm_pvc_info(loff_t pos,char * buf)464 static int atm_pvc_info(loff_t pos,char *buf)
465 {
466 struct sock *s;
467 struct atm_vcc *vcc;
468 int left, clip_info = 0;
469
470 if (!pos) {
471 return sprintf(buf,"Itf VPI VCI AAL RX(PCR,Class) "
472 "TX(PCR,Class)\n");
473 }
474 left = pos-1;
475 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
476 if (try_atm_clip_ops())
477 clip_info = 1;
478 #endif
479 read_lock(&vcc_sklist_lock);
480 for(s = vcc_sklist; s; s = s->next) {
481 vcc = s->protinfo.af_atm;
482 if (vcc->sk->family == PF_ATMPVC && vcc->dev && !left--) {
483 pvc_info(vcc,buf,clip_info);
484 read_unlock(&vcc_sklist_lock);
485 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
486 if (clip_info && atm_clip_ops->owner)
487 __MOD_DEC_USE_COUNT(atm_clip_ops->owner);
488 #endif
489 return strlen(buf);
490 }
491 }
492 read_unlock(&vcc_sklist_lock);
493 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
494 if (clip_info && atm_clip_ops->owner)
495 __MOD_DEC_USE_COUNT(atm_clip_ops->owner);
496 #endif
497 return 0;
498 }
499
500
atm_vc_info(loff_t pos,char * buf)501 static int atm_vc_info(loff_t pos,char *buf)
502 {
503 struct atm_vcc *vcc;
504 struct sock *s;
505 int left;
506
507 if (!pos)
508 return sprintf(buf,sizeof(void *) == 4 ? "%-8s%s" : "%-16s%s",
509 "Address"," Itf VPI VCI Fam Flags Reply Send buffer"
510 " Recv buffer\n");
511 left = pos-1;
512 read_lock(&vcc_sklist_lock);
513 for(s = vcc_sklist; s; s = s->next) {
514 vcc = s->protinfo.af_atm;
515 if (!left--) {
516 vc_info(vcc,buf);
517 read_unlock(&vcc_sklist_lock);
518 return strlen(buf);
519 }
520 }
521 read_unlock(&vcc_sklist_lock);
522
523 return 0;
524 }
525
526
atm_svc_info(loff_t pos,char * buf)527 static int atm_svc_info(loff_t pos,char *buf)
528 {
529 struct sock *s;
530 struct atm_vcc *vcc;
531 int left;
532
533 if (!pos)
534 return sprintf(buf,"Itf VPI VCI State Remote\n");
535 left = pos-1;
536 read_lock(&vcc_sklist_lock);
537 for(s = vcc_sklist; s; s = s->next) {
538 vcc = s->protinfo.af_atm;
539 if (vcc->sk->family == PF_ATMSVC && !left--) {
540 svc_info(vcc,buf);
541 read_unlock(&vcc_sklist_lock);
542 return strlen(buf);
543 }
544 }
545 read_unlock(&vcc_sklist_lock);
546
547 return 0;
548 }
549
550 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
atm_lec_info(loff_t pos,char * buf)551 static int atm_lec_info(loff_t pos,char *buf)
552 {
553 unsigned long flags;
554 struct lec_priv *priv;
555 struct lec_arp_table *entry;
556 int i, count, d, e;
557 struct net_device *dev;
558
559 if (!pos) {
560 return sprintf(buf,"Itf MAC ATM destination"
561 " Status Flags "
562 "VPI/VCI Recv VPI/VCI\n");
563 }
564 if (!try_atm_lane_ops())
565 return 0; /* the lane module is not there yet */
566
567 count = pos;
568 for(d = 0; d < MAX_LEC_ITF; d++) {
569 dev = atm_lane_ops->get_lec(d);
570 if (!dev || !(priv = (struct lec_priv *) dev->priv))
571 continue;
572 spin_lock_irqsave(&priv->lec_arp_lock, flags);
573 for(i = 0; i < LEC_ARP_TABLE_SIZE; i++) {
574 for(entry = priv->lec_arp_tables[i]; entry; entry = entry->next) {
575 if (--count)
576 continue;
577 e = sprintf(buf,"%s ", dev->name);
578 lec_info(entry, buf+e);
579 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
580 dev_put(dev);
581 if (atm_lane_ops->owner)
582 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
583 return strlen(buf);
584 }
585 }
586 for(entry = priv->lec_arp_empty_ones; entry; entry = entry->next) {
587 if (--count)
588 continue;
589 e = sprintf(buf,"%s ", dev->name);
590 lec_info(entry, buf+e);
591 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
592 dev_put(dev);
593 if (atm_lane_ops->owner)
594 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
595 return strlen(buf);
596 }
597 for(entry = priv->lec_no_forward; entry; entry=entry->next) {
598 if (--count)
599 continue;
600 e = sprintf(buf,"%s ", dev->name);
601 lec_info(entry, buf+e);
602 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
603 dev_put(dev);
604 if (atm_lane_ops->owner)
605 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
606 return strlen(buf);
607 }
608 for(entry = priv->mcast_fwds; entry; entry = entry->next) {
609 if (--count)
610 continue;
611 e = sprintf(buf,"%s ", dev->name);
612 lec_info(entry, buf+e);
613 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
614 dev_put(dev);
615 if (atm_lane_ops->owner)
616 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
617 return strlen(buf);
618 }
619 spin_unlock_irqrestore(&priv->lec_arp_lock, flags);
620 dev_put(dev);
621 }
622 if (atm_lane_ops->owner)
623 __MOD_DEC_USE_COUNT(atm_lane_ops->owner);
624 return 0;
625 }
626 #endif
627
628
proc_dev_atm_read(struct file * file,char * buf,size_t count,loff_t * pos)629 static ssize_t proc_dev_atm_read(struct file *file,char *buf,size_t count,
630 loff_t *pos)
631 {
632 struct atm_dev *dev;
633 unsigned long page;
634 int length;
635
636 if (count == 0) return 0;
637 page = get_free_page(GFP_KERNEL);
638 if (!page) return -ENOMEM;
639 dev = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
640 ->data;
641 if (!dev->ops->proc_read)
642 length = -EINVAL;
643 else {
644 length = dev->ops->proc_read(dev,pos,(char *) page);
645 if (length > count) length = -EINVAL;
646 }
647 if (length >= 0) {
648 if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
649 (*pos)++;
650 }
651 free_page(page);
652 return length;
653 }
654
655
proc_spec_atm_read(struct file * file,char * buf,size_t count,loff_t * pos)656 static ssize_t proc_spec_atm_read(struct file *file,char *buf,size_t count,
657 loff_t *pos)
658 {
659 unsigned long page;
660 int length;
661 int (*info)(loff_t,char *);
662 info = ((struct proc_dir_entry *) file->f_dentry->d_inode->u.generic_ip)
663 ->data;
664
665 if (count == 0) return 0;
666 page = get_free_page(GFP_KERNEL);
667 if (!page) return -ENOMEM;
668 length = (*info)(*pos,(char *) page);
669 if (length > count) length = -EINVAL;
670 if (length >= 0) {
671 if (copy_to_user(buf,(char *) page,length)) length = -EFAULT;
672 (*pos)++;
673 }
674 free_page(page);
675 return length;
676 }
677
678
679 struct proc_dir_entry *atm_proc_root;
680 EXPORT_SYMBOL(atm_proc_root);
681
682
atm_proc_dev_register(struct atm_dev * dev)683 int atm_proc_dev_register(struct atm_dev *dev)
684 {
685 int digits,num;
686 int error;
687
688 error = -ENOMEM;
689 digits = 0;
690 for (num = dev->number; num; num /= 10) digits++;
691 if (!digits) digits++;
692
693 dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_ATOMIC);
694 if (!dev->proc_name)
695 goto fail1;
696 sprintf(dev->proc_name,"%s:%d",dev->type, dev->number);
697
698 dev->proc_entry = create_proc_entry(dev->proc_name, 0, atm_proc_root);
699 if (!dev->proc_entry)
700 goto fail0;
701 dev->proc_entry->data = dev;
702 dev->proc_entry->proc_fops = &proc_dev_atm_operations;
703 dev->proc_entry->owner = THIS_MODULE;
704 return 0;
705 fail0:
706 kfree(dev->proc_name);
707 fail1:
708 return error;
709 }
710
711
atm_proc_dev_deregister(struct atm_dev * dev)712 void atm_proc_dev_deregister(struct atm_dev *dev)
713 {
714 remove_proc_entry(dev->proc_name, atm_proc_root);
715 kfree(dev->proc_name);
716 }
717
718
719 #define CREATE_ENTRY(name) \
720 name = create_proc_entry(#name,0,atm_proc_root); \
721 if (!name) goto cleanup; \
722 name->data = atm_##name##_info; \
723 name->proc_fops = &proc_spec_atm_operations; \
724 name->owner = THIS_MODULE
725
726 static struct proc_dir_entry *devices = NULL, *pvc = NULL,
727 *svc = NULL, *arp = NULL, *lec = NULL, *vc = NULL;
728
atm_proc_cleanup(void)729 static void atm_proc_cleanup(void)
730 {
731 if (devices)
732 remove_proc_entry("devices",atm_proc_root);
733 if (pvc)
734 remove_proc_entry("pvc",atm_proc_root);
735 if (svc)
736 remove_proc_entry("svc",atm_proc_root);
737 if (arp)
738 remove_proc_entry("arp",atm_proc_root);
739 if (lec)
740 remove_proc_entry("lec",atm_proc_root);
741 if (vc)
742 remove_proc_entry("vc",atm_proc_root);
743 remove_proc_entry("net/atm",NULL);
744 }
745
atm_proc_init(void)746 int atm_proc_init(void)
747 {
748 atm_proc_root = proc_mkdir("net/atm",NULL);
749 if (!atm_proc_root)
750 return -ENOMEM;
751 CREATE_ENTRY(devices);
752 CREATE_ENTRY(pvc);
753 CREATE_ENTRY(svc);
754 CREATE_ENTRY(vc);
755 #if defined(CONFIG_ATM_CLIP) || defined(CONFIG_ATM_CLIP_MODULE)
756 arp = create_proc_entry("arp", S_IRUGO, atm_proc_root);
757 if (!arp)
758 goto cleanup;
759 arp->proc_fops = &arp_seq_fops;
760 #endif
761 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
762 CREATE_ENTRY(lec);
763 #endif
764 return 0;
765
766 cleanup:
767 atm_proc_cleanup();
768 return -ENOMEM;
769 }
770
atm_proc_exit(void)771 void atm_proc_exit(void)
772 {
773 atm_proc_cleanup();
774 }
775