linux/drivers/staging/isdn/hysdn/hysdn_proclog.c
<<
>>
Prefs
   1/* $Id: hysdn_proclog.c,v 1.9.6.3 2001/09/23 22:24:54 kai Exp $
   2 *
   3 * Linux driver for HYSDN cards, /proc/net filesystem log functions.
   4 *
   5 * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
   6 * Copyright 1999 by Werner Cornelius (werner@titro.de)
   7 *
   8 * This software may be used and distributed according to the terms
   9 * of the GNU General Public License, incorporated herein by reference.
  10 *
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/poll.h>
  15#include <linux/proc_fs.h>
  16#include <linux/sched.h>
  17#include <linux/slab.h>
  18#include <linux/mutex.h>
  19#include <linux/kernel.h>
  20
  21#include "hysdn_defs.h"
  22
  23/* the proc subdir for the interface is defined in the procconf module */
  24extern struct proc_dir_entry *hysdn_proc_entry;
  25
  26static DEFINE_MUTEX(hysdn_log_mutex);
  27static void put_log_buffer(hysdn_card *card, char *cp);
  28
  29/*************************************************/
  30/* structure keeping ascii log for device output */
  31/*************************************************/
  32struct log_data {
  33        struct log_data *next;
  34        unsigned long usage_cnt;/* number of files still to work */
  35        void *proc_ctrl;        /* pointer to own control procdata structure */
  36        char log_start[2];      /* log string start (final len aligned by size) */
  37};
  38
  39/**********************************************/
  40/* structure holding proc entrys for one card */
  41/**********************************************/
  42struct procdata {
  43        struct proc_dir_entry *log;     /* log entry */
  44        char log_name[15];      /* log filename */
  45        struct log_data *log_head, *log_tail;   /* head and tail for queue */
  46        int if_used;            /* open count for interface */
  47        unsigned char logtmp[LOG_MAX_LINELEN];
  48        wait_queue_head_t rd_queue;
  49};
  50
  51
  52/**********************************************/
  53/* log function for cards error log interface */
  54/**********************************************/
  55void
  56hysdn_card_errlog(hysdn_card *card, tErrLogEntry *logp, int maxsize)
  57{
  58        char buf[ERRLOG_TEXT_SIZE + 40];
  59
  60        sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
  61        put_log_buffer(card, buf);      /* output the string */
  62}                               /* hysdn_card_errlog */
  63
  64/***************************************************/
  65/* Log function using format specifiers for output */
  66/***************************************************/
  67void
  68hysdn_addlog(hysdn_card *card, char *fmt, ...)
  69{
  70        struct procdata *pd = card->proclog;
  71        char *cp;
  72        va_list args;
  73
  74        if (!pd)
  75                return;         /* log structure non existent */
  76
  77        cp = pd->logtmp;
  78        cp += sprintf(cp, "HYSDN: card %d ", card->myid);
  79
  80        va_start(args, fmt);
  81        cp += vsprintf(cp, fmt, args);
  82        va_end(args);
  83        *cp++ = '\n';
  84        *cp = 0;
  85
  86        if (card->debug_flags & DEB_OUT_SYSLOG)
  87                printk(KERN_INFO "%s", pd->logtmp);
  88        else
  89                put_log_buffer(card, pd->logtmp);
  90
  91}                               /* hysdn_addlog */
  92
  93/********************************************/
  94/* put an log buffer into the log queue.    */
  95/* This buffer will be kept until all files */
  96/* opened for read got the contents.        */
  97/* Flushes buffers not longer in use.       */
  98/********************************************/
  99static void
 100put_log_buffer(hysdn_card *card, char *cp)
 101{
 102        struct log_data *ib;
 103        struct procdata *pd = card->proclog;
 104        unsigned long flags;
 105
 106        if (!pd)
 107                return;
 108        if (!cp)
 109                return;
 110        if (!*cp)
 111                return;
 112        if (pd->if_used <= 0)
 113                return;         /* no open file for read */
 114
 115        if (!(ib = kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
 116                return; /* no memory */
 117        strcpy(ib->log_start, cp);      /* set output string */
 118        ib->next = NULL;
 119        ib->proc_ctrl = pd;     /* point to own control structure */
 120        spin_lock_irqsave(&card->hysdn_lock, flags);
 121        ib->usage_cnt = pd->if_used;
 122        if (!pd->log_head)
 123                pd->log_head = ib;      /* new head */
 124        else
 125                pd->log_tail->next = ib;        /* follows existing messages */
 126        pd->log_tail = ib;      /* new tail */
 127
 128        /* delete old entrys */
 129        while (pd->log_head->next) {
 130                if ((pd->log_head->usage_cnt <= 0) &&
 131                    (pd->log_head->next->usage_cnt <= 0)) {
 132                        ib = pd->log_head;
 133                        pd->log_head = pd->log_head->next;
 134                        kfree(ib);
 135                } else {
 136                        break;
 137                }
 138        }               /* pd->log_head->next */
 139
 140        spin_unlock_irqrestore(&card->hysdn_lock, flags);
 141
 142        wake_up_interruptible(&(pd->rd_queue));         /* announce new entry */
 143}                               /* put_log_buffer */
 144
 145
 146/******************************/
 147/* file operations and tables */
 148/******************************/
 149
 150/****************************************/
 151/* write log file -> set log level bits */
 152/****************************************/
 153static ssize_t
 154hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
 155{
 156        int rc;
 157        hysdn_card *card = file->private_data;
 158
 159        rc = kstrtoul_from_user(buf, count, 0, &card->debug_flags);
 160        if (rc < 0)
 161                return rc;
 162        hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
 163        return (count);
 164}                               /* hysdn_log_write */
 165
 166/******************/
 167/* read log file */
 168/******************/
 169static ssize_t
 170hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t *off)
 171{
 172        struct log_data *inf;
 173        int len;
 174        hysdn_card *card = PDE_DATA(file_inode(file));
 175
 176        if (!(inf = *((struct log_data **) file->private_data))) {
 177                struct procdata *pd = card->proclog;
 178                if (file->f_flags & O_NONBLOCK)
 179                        return (-EAGAIN);
 180
 181                wait_event_interruptible(pd->rd_queue, (inf =
 182                                *((struct log_data **) file->private_data)));
 183        }
 184        if (!inf)
 185                return (0);
 186
 187        inf->usage_cnt--;       /* new usage count */
 188        file->private_data = &inf->next;        /* next structure */
 189        if ((len = strlen(inf->log_start)) <= count) {
 190                if (copy_to_user(buf, inf->log_start, len))
 191                        return -EFAULT;
 192                *off += len;
 193                return (len);
 194        }
 195        return (0);
 196}                               /* hysdn_log_read */
 197
 198/******************/
 199/* open log file */
 200/******************/
 201static int
 202hysdn_log_open(struct inode *ino, struct file *filep)
 203{
 204        hysdn_card *card = PDE_DATA(ino);
 205
 206        mutex_lock(&hysdn_log_mutex);
 207        if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
 208                /* write only access -> write log level only */
 209                filep->private_data = card;     /* remember our own card */
 210        } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
 211                struct procdata *pd = card->proclog;
 212                unsigned long flags;
 213
 214                /* read access -> log/debug read */
 215                spin_lock_irqsave(&card->hysdn_lock, flags);
 216                pd->if_used++;
 217                if (pd->log_head)
 218                        filep->private_data = &pd->log_tail->next;
 219                else
 220                        filep->private_data = &pd->log_head;
 221                spin_unlock_irqrestore(&card->hysdn_lock, flags);
 222        } else {                /* simultaneous read/write access forbidden ! */
 223                mutex_unlock(&hysdn_log_mutex);
 224                return (-EPERM);        /* no permission this time */
 225        }
 226        mutex_unlock(&hysdn_log_mutex);
 227        return nonseekable_open(ino, filep);
 228}                               /* hysdn_log_open */
 229
 230/*******************************************************************************/
 231/* close a cardlog file. If the file has been opened for exclusive write it is */
 232/* assumed as pof data input and the pof loader is noticed about.              */
 233/* Otherwise file is handled as log output. In this case the interface usage   */
 234/* count is decremented and all buffers are noticed of closing. If this file   */
 235/* was the last one to be closed, all buffers are freed.                       */
 236/*******************************************************************************/
 237static int
 238hysdn_log_close(struct inode *ino, struct file *filep)
 239{
 240        struct log_data *inf;
 241        struct procdata *pd;
 242        hysdn_card *card;
 243        int retval = 0;
 244
 245        mutex_lock(&hysdn_log_mutex);
 246        if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
 247                /* write only access -> write debug level written */
 248                retval = 0;     /* success */
 249        } else {
 250                /* read access -> log/debug read, mark one further file as closed */
 251
 252                inf = *((struct log_data **) filep->private_data);      /* get first log entry */
 253                if (inf)
 254                        pd = (struct procdata *) inf->proc_ctrl;        /* still entries there */
 255                else {
 256                        /* no info available -> search card */
 257                        card = PDE_DATA(file_inode(filep));
 258                        pd = card->proclog;     /* pointer to procfs log */
 259                }
 260                if (pd)
 261                        pd->if_used--;  /* decrement interface usage count by one */
 262
 263                while (inf) {
 264                        inf->usage_cnt--;       /* decrement usage count for buffers */
 265                        inf = inf->next;
 266                }
 267
 268                if (pd)
 269                        if (pd->if_used <= 0)   /* delete buffers if last file closed */
 270                                while (pd->log_head) {
 271                                        inf = pd->log_head;
 272                                        pd->log_head = pd->log_head->next;
 273                                        kfree(inf);
 274                                }
 275        }                       /* read access */
 276        mutex_unlock(&hysdn_log_mutex);
 277
 278        return (retval);
 279}                               /* hysdn_log_close */
 280
 281/*************************************************/
 282/* select/poll routine to be able using select() */
 283/*************************************************/
 284static __poll_t
 285hysdn_log_poll(struct file *file, poll_table *wait)
 286{
 287        __poll_t mask = 0;
 288        hysdn_card *card = PDE_DATA(file_inode(file));
 289        struct procdata *pd = card->proclog;
 290
 291        if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
 292                return (mask);  /* no polling for write supported */
 293
 294        poll_wait(file, &(pd->rd_queue), wait);
 295
 296        if (*((struct log_data **) file->private_data))
 297                mask |= EPOLLIN | EPOLLRDNORM;
 298
 299        return mask;
 300}                               /* hysdn_log_poll */
 301
 302/**************************************************/
 303/* table for log filesystem functions defined above. */
 304/**************************************************/
 305static const struct file_operations log_fops =
 306{
 307        .owner          = THIS_MODULE,
 308        .llseek         = no_llseek,
 309        .read           = hysdn_log_read,
 310        .write          = hysdn_log_write,
 311        .poll           = hysdn_log_poll,
 312        .open           = hysdn_log_open,
 313        .release        = hysdn_log_close,
 314};
 315
 316
 317/***********************************************************************************/
 318/* hysdn_proclog_init is called when the module is loaded after creating the cards */
 319/* conf files.                                                                     */
 320/***********************************************************************************/
 321int
 322hysdn_proclog_init(hysdn_card *card)
 323{
 324        struct procdata *pd;
 325
 326        /* create a cardlog proc entry */
 327
 328        if ((pd = kzalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
 329                sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
 330                pd->log = proc_create_data(pd->log_name,
 331                                      S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry,
 332                                      &log_fops, card);
 333
 334                init_waitqueue_head(&(pd->rd_queue));
 335
 336                card->proclog = (void *) pd;    /* remember procfs structure */
 337        }
 338        return (0);
 339}                               /* hysdn_proclog_init */
 340
 341/************************************************************************************/
 342/* hysdn_proclog_release is called when the module is unloaded and before the cards */
 343/* conf file is released                                                            */
 344/* The module counter is assumed to be 0 !                                          */
 345/************************************************************************************/
 346void
 347hysdn_proclog_release(hysdn_card *card)
 348{
 349        struct procdata *pd;
 350
 351        if ((pd = (struct procdata *) card->proclog) != NULL) {
 352                if (pd->log)
 353                        remove_proc_entry(pd->log_name, hysdn_proc_entry);
 354                kfree(pd);      /* release memory */
 355                card->proclog = NULL;
 356        }
 357}                               /* hysdn_proclog_release */
 358