linux-old/net/atm/proc.c
<<
>>
Prefs
   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
  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        /* 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
 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",  /*  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
 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 * 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
 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; /* 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
 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.