linux/drivers/staging/dgrp/dgrp_specproc.c
<<
>>
Prefs
   1/*
   2 *
   3 * Copyright 1999 Digi International (www.digi.com)
   4 *     James Puzzo  <jamesp at digi dot com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2, or (at your option)
   9 * any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the
  13 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  14 * PURPOSE.  See the GNU General Public License for more details.
  15 *
  16 */
  17
  18/*
  19 *
  20 *  Filename:
  21 *
  22 *     dgrp_specproc.c
  23 *
  24 *  Description:
  25 *
  26 *     Handle the "config" proc entry for the linux realport device driver
  27 *     and provide slots for the "net" and "mon" devices
  28 *
  29 *  Author:
  30 *
  31 *     James A. Puzzo
  32 *
  33 */
  34
  35#include <linux/module.h>
  36#include <linux/tty.h>
  37#include <linux/sched.h>
  38#include <linux/cred.h>
  39#include <linux/proc_fs.h>
  40#include <linux/slab.h>
  41#include <linux/ctype.h>
  42#include <linux/seq_file.h>
  43#include <linux/uaccess.h>
  44#include <linux/vmalloc.h>
  45
  46#include "dgrp_common.h"
  47
  48static struct proc_dir_entry *dgrp_proc_dir_entry;
  49
  50static int dgrp_add_id(long id);
  51static int dgrp_remove_nd(struct nd_struct *nd);
  52static struct proc_dir_entry *add_proc_file(struct nd_struct *node,
  53                                 struct proc_dir_entry *root,
  54                                 const struct file_operations *fops);
  55
  56/* File operation declarations */
  57static int parse_write_config(char *);
  58
  59static ssize_t dgrp_config_proc_write(struct file *file,
  60                                      const char __user *buffer,
  61                                      size_t count, loff_t *pos);
  62
  63static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file);
  64static int dgrp_info_proc_open(struct inode *inode, struct file *file);
  65static int dgrp_config_proc_open(struct inode *inode, struct file *file);
  66
  67static const struct file_operations config_proc_file_ops = {
  68        .owner   = THIS_MODULE,
  69        .open    = dgrp_config_proc_open,
  70        .read    = seq_read,
  71        .llseek  = seq_lseek,
  72        .release = seq_release,
  73        .write   = dgrp_config_proc_write,
  74};
  75
  76static const struct file_operations info_proc_file_ops = {
  77        .owner   = THIS_MODULE,
  78        .open    = dgrp_info_proc_open,
  79        .read    = seq_read,
  80        .llseek  = seq_lseek,
  81        .release = single_release,
  82};
  83
  84static const struct file_operations nodeinfo_proc_file_ops = {
  85        .owner   = THIS_MODULE,
  86        .open    = dgrp_nodeinfo_proc_open,
  87        .read    = seq_read,
  88        .llseek  = seq_lseek,
  89        .release = seq_release,
  90};
  91
  92static struct proc_dir_entry *net_entry_pointer;
  93static struct proc_dir_entry *mon_entry_pointer;
  94static struct proc_dir_entry *dpa_entry_pointer;
  95static struct proc_dir_entry *ports_entry_pointer;
  96
  97static void remove_files(struct nd_struct *nd)
  98{
  99        char buf[3];
 100        ID_TO_CHAR(nd->nd_ID, buf);
 101        dgrp_remove_node_class_sysfs_files(nd);
 102        if (nd->nd_net_de)
 103                remove_proc_entry(buf, net_entry_pointer);
 104        if (nd->nd_mon_de)
 105                remove_proc_entry(buf, mon_entry_pointer);
 106        if (nd->nd_dpa_de)
 107                remove_proc_entry(buf, dpa_entry_pointer);
 108        if (nd->nd_ports_de)
 109                remove_proc_entry(buf, ports_entry_pointer);
 110}
 111
 112void dgrp_unregister_proc(void)
 113{
 114        net_entry_pointer = NULL;
 115        mon_entry_pointer = NULL;
 116        dpa_entry_pointer = NULL;
 117        ports_entry_pointer = NULL;
 118
 119        if (dgrp_proc_dir_entry) {
 120                struct nd_struct *nd;
 121                list_for_each_entry(nd, &nd_struct_list, list)
 122                        remove_files(nd);
 123                remove_proc_entry("dgrp/config", NULL);
 124                remove_proc_entry("dgrp/info", NULL);
 125                remove_proc_entry("dgrp/nodeinfo", NULL);
 126                remove_proc_entry("dgrp/net", NULL);
 127                remove_proc_entry("dgrp/mon", NULL);
 128                remove_proc_entry("dgrp/dpa", NULL);
 129                remove_proc_entry("dgrp/ports", NULL);
 130                remove_proc_entry("dgrp", NULL);
 131                dgrp_proc_dir_entry = NULL;
 132        }
 133}
 134
 135void dgrp_register_proc(void)
 136{
 137        /*
 138         *      Register /proc/dgrp
 139         */
 140        dgrp_proc_dir_entry = proc_mkdir("dgrp", NULL);
 141        if (!dgrp_proc_dir_entry)
 142                return;
 143        proc_create("dgrp/config", 0644, NULL, &config_proc_file_ops);
 144        proc_create("dgrp/info", 0644, NULL, &info_proc_file_ops);
 145        proc_create("dgrp/nodeinfo", 0644, NULL, &nodeinfo_proc_file_ops);
 146        net_entry_pointer = proc_mkdir_mode("dgrp/net", 0500, NULL);
 147        mon_entry_pointer = proc_mkdir_mode("dgrp/mon", 0500, NULL);
 148        dpa_entry_pointer = proc_mkdir_mode("dgrp/dpa", 0500, NULL);
 149        ports_entry_pointer = proc_mkdir_mode("dgrp/ports", 0500, NULL);
 150}
 151
 152static void *dgrp_config_proc_start(struct seq_file *m, loff_t *pos)
 153{
 154        return seq_list_start_head(&nd_struct_list, *pos);
 155}
 156
 157static void *dgrp_config_proc_next(struct seq_file *p, void *v, loff_t *pos)
 158{
 159        return seq_list_next(v, &nd_struct_list, pos);
 160}
 161
 162static void dgrp_config_proc_stop(struct seq_file *m, void *v)
 163{
 164}
 165
 166static int dgrp_config_proc_show(struct seq_file *m, void *v)
 167{
 168        struct nd_struct *nd;
 169        char tmp_id[4];
 170
 171        if (v == &nd_struct_list) {
 172                seq_puts(m, "#-----------------------------------------------------------------------------\n");
 173                seq_puts(m, "#                        Avail\n");
 174                seq_puts(m, "# ID  Major  State       Ports\n");
 175                return 0;
 176        }
 177
 178        nd = list_entry(v, struct nd_struct, list);
 179
 180        ID_TO_CHAR(nd->nd_ID, tmp_id);
 181
 182        seq_printf(m, "  %-2.2s  %-5ld  %-10.10s  %-5d\n",
 183                   tmp_id,
 184                   nd->nd_major,
 185                   ND_STATE_STR(nd->nd_state),
 186                   nd->nd_chan_count);
 187
 188        return 0;
 189}
 190
 191static const struct seq_operations proc_config_ops = {
 192        .start = dgrp_config_proc_start,
 193        .next  = dgrp_config_proc_next,
 194        .stop  = dgrp_config_proc_stop,
 195        .show  = dgrp_config_proc_show,
 196};
 197
 198static int dgrp_config_proc_open(struct inode *inode, struct file *file)
 199{
 200        return seq_open(file, &proc_config_ops);
 201}
 202
 203
 204/*
 205 *  When writing configuration information, each "record" (i.e. each
 206 *  write) is treated as an independent request.  See the "parse"
 207 *  description for more details.
 208 */
 209static ssize_t dgrp_config_proc_write(struct file *file,
 210                                      const char __user *buffer,
 211                                      size_t count, loff_t *pos)
 212{
 213        ssize_t retval;
 214        char *inbuf, *sp;
 215        char *line, *ldelim;
 216
 217        if (count > 32768)
 218                return -EINVAL;
 219
 220        inbuf = sp = vzalloc(count + 1);
 221        if (!inbuf)
 222                return -ENOMEM;
 223
 224        if (copy_from_user(inbuf, buffer, count)) {
 225                retval = -EFAULT;
 226                goto done;
 227        }
 228
 229        inbuf[count] = 0;
 230
 231        ldelim = "\n";
 232
 233        line = strpbrk(sp, ldelim);
 234        while (line) {
 235                *line = 0;
 236                retval = parse_write_config(sp);
 237                if (retval)
 238                        goto done;
 239
 240                sp = line + 1;
 241                line = strpbrk(sp, ldelim);
 242        }
 243
 244        retval = count;
 245done:
 246        vfree(inbuf);
 247        return retval;
 248}
 249
 250/*
 251 *  ------------------------------------------------------------------------
 252 *
 253 *  The following are the functions to parse input
 254 *
 255 *  ------------------------------------------------------------------------
 256 */
 257static inline char *skip_past_ws(const char *str)
 258{
 259        while ((*str) && !isspace(*str))
 260                ++str;
 261
 262        return skip_spaces(str);
 263}
 264
 265static int parse_id(char **c, char *cID)
 266{
 267        int tmp = **c;
 268
 269        if (isalnum(tmp) || (tmp == '_'))
 270                cID[0] = tmp;
 271        else
 272                return -EINVAL;
 273
 274        (*c)++; tmp = **c;
 275
 276        if (isalnum(tmp) || (tmp == '_')) {
 277                cID[1] = tmp;
 278                (*c)++;
 279        } else
 280                cID[1] = 0;
 281
 282        return 0;
 283}
 284
 285static int parse_add_config(char *buf)
 286{
 287        char *c = buf;
 288        int  retval;
 289        char cID[2];
 290        long ID;
 291
 292        c = skip_past_ws(c);
 293
 294        retval = parse_id(&c, cID);
 295        if (retval < 0)
 296                return retval;
 297
 298        ID = CHAR_TO_ID(cID);
 299
 300        c = skip_past_ws(c);
 301
 302        return dgrp_add_id(ID);
 303}
 304
 305static int parse_del_config(char *buf)
 306{
 307        char *c = buf;
 308        int  retval;
 309        struct nd_struct *nd;
 310        char cID[2];
 311        long ID;
 312        long major;
 313
 314        c = skip_past_ws(c);
 315
 316        retval = parse_id(&c, cID);
 317        if (retval < 0)
 318                return retval;
 319
 320        ID = CHAR_TO_ID(cID);
 321
 322        c = skip_past_ws(c);
 323
 324        retval = kstrtol(c, 10, &major);
 325        if (retval)
 326                return retval;
 327
 328        nd = nd_struct_get(major);
 329        if (!nd)
 330                return -EINVAL;
 331
 332        if ((nd->nd_major != major) || (nd->nd_ID != ID))
 333                return -EINVAL;
 334
 335        return dgrp_remove_nd(nd);
 336}
 337
 338static int parse_chg_config(char *buf)
 339{
 340        return -EINVAL;
 341}
 342
 343/*
 344 *  The passed character buffer represents a single configuration request.
 345 *  If the first character is a "+", it is parsed as a request to add a
 346 *     PortServer
 347 *  If the first character is a "-", it is parsed as a request to delete a
 348 *     PortServer
 349 *  If the first character is a "*", it is parsed as a request to change a
 350 *     PortServer
 351 *  Any other character (including whitespace) causes the record to be
 352 *     ignored.
 353 */
 354static int parse_write_config(char *buf)
 355{
 356        int retval;
 357
 358        switch (buf[0]) {
 359        case '+':
 360                retval = parse_add_config(buf);
 361                break;
 362        case '-':
 363                retval = parse_del_config(buf);
 364                break;
 365        case '*':
 366                retval = parse_chg_config(buf);
 367                break;
 368        default:
 369                retval = -EINVAL;
 370        }
 371
 372        return retval;
 373}
 374
 375static int dgrp_info_proc_show(struct seq_file *m, void *v)
 376{
 377        seq_printf(m, "version: %s\n", DIGI_VERSION);
 378        seq_puts(m, "register_with_sysfs: 1\n");
 379        seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
 380                   dgrp_poll_tick, dgrp_poll_tick);
 381
 382        return 0;
 383}
 384
 385static int dgrp_info_proc_open(struct inode *inode, struct file *file)
 386{
 387        return single_open(file, dgrp_info_proc_show, NULL);
 388}
 389
 390
 391static void *dgrp_nodeinfo_start(struct seq_file *m, loff_t *pos)
 392{
 393        return seq_list_start_head(&nd_struct_list, *pos);
 394}
 395
 396static void *dgrp_nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
 397{
 398        return seq_list_next(v, &nd_struct_list, pos);
 399}
 400
 401static void dgrp_nodeinfo_stop(struct seq_file *m, void *v)
 402{
 403}
 404
 405static int dgrp_nodeinfo_show(struct seq_file *m, void *v)
 406{
 407        struct nd_struct *nd;
 408        char hwver[8];
 409        char swver[8];
 410        char tmp_id[4];
 411
 412        if (v == &nd_struct_list) {
 413                seq_puts(m, "#-----------------------------------------------------------------------------\n");
 414                seq_puts(m, "#                 HW       HW   SW\n");
 415                seq_puts(m, "# ID  State       Version  ID   Version  Description\n");
 416                return 0;
 417        }
 418
 419        nd = list_entry(v, struct nd_struct, list);
 420
 421        ID_TO_CHAR(nd->nd_ID, tmp_id);
 422
 423        if (nd->nd_state == NS_READY) {
 424                sprintf(hwver, "%d.%d", (nd->nd_hw_ver >> 8) & 0xff,
 425                        nd->nd_hw_ver & 0xff);
 426                sprintf(swver, "%d.%d", (nd->nd_sw_ver >> 8) & 0xff,
 427                        nd->nd_sw_ver & 0xff);
 428                seq_printf(m, "  %-2.2s  %-10.10s  %-7.7s  %-3d  %-7.7s  %-35.35s\n",
 429                           tmp_id,
 430                           ND_STATE_STR(nd->nd_state),
 431                           hwver,
 432                           nd->nd_hw_id,
 433                           swver,
 434                           nd->nd_ps_desc);
 435
 436        } else {
 437                seq_printf(m, "  %-2.2s  %-10.10s\n",
 438                           tmp_id,
 439                           ND_STATE_STR(nd->nd_state));
 440        }
 441
 442        return 0;
 443}
 444
 445
 446static const struct seq_operations nodeinfo_ops = {
 447        .start = dgrp_nodeinfo_start,
 448        .next  = dgrp_nodeinfo_next,
 449        .stop  = dgrp_nodeinfo_stop,
 450        .show  = dgrp_nodeinfo_show,
 451};
 452
 453static int dgrp_nodeinfo_proc_open(struct inode *inode, struct file *file)
 454{
 455        return seq_open(file, &nodeinfo_ops);
 456}
 457
 458/**
 459 * dgrp_add_id() -- creates new nd struct and adds it to list
 460 * @id: id of device to add
 461 */
 462static int dgrp_add_id(long id)
 463{
 464        struct nd_struct *nd;
 465        int ret;
 466        int i;
 467
 468        nd = kzalloc(sizeof(struct nd_struct), GFP_KERNEL);
 469        if (!nd)
 470                return -ENOMEM;
 471
 472        nd->nd_major = 0;
 473        nd->nd_ID = id;
 474
 475        spin_lock_init(&nd->nd_lock);
 476
 477        init_waitqueue_head(&nd->nd_tx_waitq);
 478        init_waitqueue_head(&nd->nd_mon_wqueue);
 479        init_waitqueue_head(&nd->nd_dpa_wqueue);
 480        sema_init(&nd->nd_mon_semaphore, 1);
 481        sema_init(&nd->nd_net_semaphore, 1);
 482        spin_lock_init(&nd->nd_dpa_lock);
 483        nd->nd_state = NS_CLOSED;
 484        for (i = 0; i < SEQ_MAX; i++)
 485                init_waitqueue_head(&nd->nd_seq_wque[i]);
 486
 487        /* setup the structures to get the major number */
 488        ret = dgrp_tty_init(nd);
 489        if (ret)
 490                goto error_out;
 491
 492        nd->nd_major = nd->nd_serial_ttdriver->major;
 493
 494        ret = nd_struct_add(nd);
 495        if (ret)
 496                goto error_out;
 497
 498        dgrp_create_node_class_sysfs_files(nd);
 499        nd->nd_net_de = add_proc_file(nd, net_entry_pointer, &dgrp_net_ops);
 500        nd->nd_mon_de = add_proc_file(nd, mon_entry_pointer, &dgrp_mon_ops);
 501        nd->nd_dpa_de = add_proc_file(nd, dpa_entry_pointer, &dgrp_dpa_ops);
 502        nd->nd_ports_de = add_proc_file(nd, ports_entry_pointer,
 503                                        &dgrp_ports_ops);
 504        return 0;
 505
 506        /* FIXME this guy should free the tty driver stored in nd and destroy
 507         * all channel ports */
 508error_out:
 509        kfree(nd);
 510        return ret;
 511
 512}
 513
 514static int dgrp_remove_nd(struct nd_struct *nd)
 515{
 516        int ret;
 517
 518        /* Check to see if the selected structure is in use */
 519        if (nd->nd_tty_ref_cnt)
 520                return -EBUSY;
 521
 522        remove_files(nd);
 523
 524        dgrp_tty_uninit(nd);
 525
 526        ret = nd_struct_del(nd);
 527        if (ret)
 528                return ret;
 529
 530        kfree(nd);
 531        return 0;
 532}
 533
 534static struct proc_dir_entry *add_proc_file(struct nd_struct *node,
 535                                 struct proc_dir_entry *root,
 536                                 const struct file_operations *fops)
 537{
 538        char buf[3];
 539        ID_TO_CHAR(node->nd_ID, buf);
 540        return proc_create_data(buf, 0600, root, fops, node);
 541}
 542