Merge /spare/repo/linux-2.6/
[linux-2.6] / drivers / isdn / hysdn / hysdn_proclog.c
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/version.h>
15 #include <linux/poll.h>
16 #include <linux/proc_fs.h>
17 #include <linux/pci.h>
18 #include <linux/smp_lock.h>
19
20 #include "hysdn_defs.h"
21
22 /* the proc subdir for the interface is defined in the procconf module */
23 extern struct proc_dir_entry *hysdn_proc_entry;
24
25 static void put_log_buffer(hysdn_card * card, char *cp);
26
27 /*************************************************/
28 /* structure keeping ascii log for device output */
29 /*************************************************/
30 struct log_data {
31         struct log_data *next;
32         ulong usage_cnt;        /* number of files still to work */
33         void *proc_ctrl;        /* pointer to own control procdata structure */
34         char log_start[2];      /* log string start (final len aligned by size) */
35 };
36
37 /**********************************************/
38 /* structure holding proc entrys for one card */
39 /**********************************************/
40 struct procdata {
41         struct proc_dir_entry *log;     /* log entry */
42         char log_name[15];      /* log filename */
43         struct log_data *log_head, *log_tail;   /* head and tail for queue */
44         int if_used;            /* open count for interface */
45         int volatile del_lock;  /* lock for delete operations */
46         uchar logtmp[LOG_MAX_LINELEN];
47         wait_queue_head_t rd_queue;
48 };
49
50
51 /**********************************************/
52 /* log function for cards error log interface */
53 /**********************************************/
54 void
55 hysdn_card_errlog(hysdn_card * card, tErrLogEntry * logp, int maxsize)
56 {
57         char buf[ERRLOG_TEXT_SIZE + 40];
58
59         sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
60         put_log_buffer(card, buf);      /* output the string */
61 }                               /* hysdn_card_errlog */
62
63 /***************************************************/
64 /* Log function using format specifiers for output */
65 /***************************************************/
66 void
67 hysdn_addlog(hysdn_card * card, char *fmt,...)
68 {
69         struct procdata *pd = card->proclog;
70         char *cp;
71         va_list args;
72
73         if (!pd)
74                 return;         /* log structure non existent */
75
76         cp = pd->logtmp;
77         cp += sprintf(cp, "HYSDN: card %d ", card->myid);
78
79         va_start(args, fmt);
80         cp += vsprintf(cp, fmt, args);
81         va_end(args);
82         *cp++ = '\n';
83         *cp = 0;
84
85         if (card->debug_flags & DEB_OUT_SYSLOG)
86                 printk(KERN_INFO "%s", pd->logtmp);
87         else
88                 put_log_buffer(card, pd->logtmp);
89
90 }                               /* hysdn_addlog */
91
92 /********************************************/
93 /* put an log buffer into the log queue.    */
94 /* This buffer will be kept until all files */
95 /* opened for read got the contents.        */
96 /* Flushes buffers not longer in use.       */
97 /********************************************/
98 static void
99 put_log_buffer(hysdn_card * card, char *cp)
100 {
101         struct log_data *ib;
102         struct procdata *pd = card->proclog;
103         int i;
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 = (struct log_data *) 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         save_flags(flags);
121         cli();
122         ib->usage_cnt = pd->if_used;
123         if (!pd->log_head)
124                 pd->log_head = ib;      /* new head */
125         else
126                 pd->log_tail->next = ib;        /* follows existing messages */
127         pd->log_tail = ib;      /* new tail */
128         i = pd->del_lock++;     /* get lock state */
129         restore_flags(flags);
130
131         /* delete old entrys */
132         if (!i)
133                 while (pd->log_head->next) {
134                         if ((pd->log_head->usage_cnt <= 0) &&
135                             (pd->log_head->next->usage_cnt <= 0)) {
136                                 ib = pd->log_head;
137                                 pd->log_head = pd->log_head->next;
138                                 kfree(ib);
139                         } else
140                                 break;
141                 }               /* pd->log_head->next */
142         pd->del_lock--;         /* release lock level */
143         wake_up_interruptible(&(pd->rd_queue));         /* announce new entry */
144 }                               /* put_log_buffer */
145
146
147 /******************************/
148 /* file operations and tables */
149 /******************************/
150
151 /****************************************/
152 /* write log file -> set log level bits */
153 /****************************************/
154 static ssize_t
155 hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
156 {
157         ulong u = 0;
158         int found = 0;
159         uchar *cp, valbuf[128];
160         long base = 10;
161         hysdn_card *card = (hysdn_card *) file->private_data;
162
163         if (count > (sizeof(valbuf) - 1))
164                 count = sizeof(valbuf) - 1;     /* limit length */
165         if (copy_from_user(valbuf, buf, count))
166                 return (-EFAULT);       /* copy failed */
167
168         valbuf[count] = 0;      /* terminating 0 */
169         cp = valbuf;
170         if ((count > 2) && (valbuf[0] == '0') && (valbuf[1] == 'x')) {
171                 cp += 2;        /* pointer after hex modifier */
172                 base = 16;
173         }
174         /* scan the input for debug flags */
175         while (*cp) {
176                 if ((*cp >= '0') && (*cp <= '9')) {
177                         found = 1;
178                         u *= base;      /* adjust to next digit */
179                         u += *cp++ - '0';
180                         continue;
181                 }
182                 if (base != 16)
183                         break;  /* end of number */
184
185                 if ((*cp >= 'a') && (*cp <= 'f')) {
186                         found = 1;
187                         u *= base;      /* adjust to next digit */
188                         u += *cp++ - 'a' + 10;
189                         continue;
190                 }
191                 break;          /* terminated */
192         }
193
194         if (found) {
195                 card->debug_flags = u;  /* remember debug flags */
196                 hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
197         }
198         return (count);
199 }                               /* hysdn_log_write */
200
201 /******************/
202 /* read log file */
203 /******************/
204 static ssize_t
205 hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t * off)
206 {
207         struct log_data *inf;
208         int len;
209         struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
210         struct procdata *pd = NULL;
211         hysdn_card *card;
212
213         if (!*((struct log_data **) file->private_data)) {
214                 if (file->f_flags & O_NONBLOCK)
215                         return (-EAGAIN);
216
217                 /* sorry, but we need to search the card */
218                 card = card_root;
219                 while (card) {
220                         pd = card->proclog;
221                         if (pd->log == pde)
222                                 break;
223                         card = card->next;      /* search next entry */
224                 }
225                 if (card)
226                         interruptible_sleep_on(&(pd->rd_queue));
227                 else
228                         return (-EAGAIN);
229
230         }
231         if (!(inf = *((struct log_data **) file->private_data)))
232                 return (0);
233
234         inf->usage_cnt--;       /* new usage count */
235         file->private_data = &inf->next;        /* next structure */
236         if ((len = strlen(inf->log_start)) <= count) {
237                 if (copy_to_user(buf, inf->log_start, len))
238                         return -EFAULT;
239                 *off += len;
240                 return (len);
241         }
242         return (0);
243 }                               /* hysdn_log_read */
244
245 /******************/
246 /* open log file */
247 /******************/
248 static int
249 hysdn_log_open(struct inode *ino, struct file *filep)
250 {
251         hysdn_card *card;
252         struct procdata *pd = NULL;
253         ulong flags;
254
255         lock_kernel();
256         card = card_root;
257         while (card) {
258                 pd = card->proclog;
259                 if (pd->log == PDE(ino))
260                         break;
261                 card = card->next;      /* search next entry */
262         }
263         if (!card) {
264                 unlock_kernel();
265                 return (-ENODEV);       /* device is unknown/invalid */
266         }
267         filep->private_data = card;     /* remember our own card */
268
269         if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
270                 /* write only access -> write log level only */
271         } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
272
273                 /* read access -> log/debug read */
274                 save_flags(flags);
275                 cli();
276                 pd->if_used++;
277                 if (pd->log_head)
278                         filep->private_data = &pd->log_tail->next;
279                 else
280                         filep->private_data = &pd->log_head;
281                 restore_flags(flags);
282         } else {                /* simultaneous read/write access forbidden ! */
283                 unlock_kernel();
284                 return (-EPERM);        /* no permission this time */
285         }
286         unlock_kernel();
287         return nonseekable_open(ino, filep);
288 }                               /* hysdn_log_open */
289
290 /*******************************************************************************/
291 /* close a cardlog file. If the file has been opened for exclusive write it is */
292 /* assumed as pof data input and the pof loader is noticed about.              */
293 /* Otherwise file is handled as log output. In this case the interface usage   */
294 /* count is decremented and all buffers are noticed of closing. If this file   */
295 /* was the last one to be closed, all buffers are freed.                       */
296 /*******************************************************************************/
297 static int
298 hysdn_log_close(struct inode *ino, struct file *filep)
299 {
300         struct log_data *inf;
301         struct procdata *pd;
302         hysdn_card *card;
303         int retval = 0;
304         unsigned long flags;
305
306
307         lock_kernel();
308         if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
309                 /* write only access -> write debug level written */
310                 retval = 0;     /* success */
311         } else {
312                 /* read access -> log/debug read, mark one further file as closed */
313
314                 pd = NULL;
315                 save_flags(flags);
316                 cli();
317                 inf = *((struct log_data **) filep->private_data);      /* get first log entry */
318                 if (inf)
319                         pd = (struct procdata *) inf->proc_ctrl;        /* still entries there */
320                 else {
321                         /* no info available -> search card */
322                         card = card_root;
323                         while (card) {
324                                 pd = card->proclog;
325                                 if (pd->log == PDE(ino))
326                                         break;
327                                 card = card->next;      /* search next entry */
328                         }
329                         if (card)
330                                 pd = card->proclog;     /* pointer to procfs log */
331                 }
332                 if (pd)
333                         pd->if_used--;  /* decrement interface usage count by one */
334
335                 while (inf) {
336                         inf->usage_cnt--;       /* decrement usage count for buffers */
337                         inf = inf->next;
338                 }
339                 restore_flags(flags);
340
341                 if (pd)
342                         if (pd->if_used <= 0)   /* delete buffers if last file closed */
343                                 while (pd->log_head) {
344                                         inf = pd->log_head;
345                                         pd->log_head = pd->log_head->next;
346                                         kfree(inf);
347                                 }
348         }                       /* read access */
349         unlock_kernel();
350
351         return (retval);
352 }                               /* hysdn_log_close */
353
354 /*************************************************/
355 /* select/poll routine to be able using select() */
356 /*************************************************/
357 static unsigned int
358 hysdn_log_poll(struct file *file, poll_table * wait)
359 {
360         unsigned int mask = 0;
361         struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode);
362         hysdn_card *card;
363         struct procdata *pd = NULL;
364
365         if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
366                 return (mask);  /* no polling for write supported */
367
368         /* we need to search the card */
369         card = card_root;
370         while (card) {
371                 pd = card->proclog;
372                 if (pd->log == pde)
373                         break;
374                 card = card->next;      /* search next entry */
375         }
376         if (!card)
377                 return (mask);  /* card not found */
378
379         poll_wait(file, &(pd->rd_queue), wait);
380
381         if (*((struct log_data **) file->private_data))
382                 mask |= POLLIN | POLLRDNORM;
383
384         return mask;
385 }                               /* hysdn_log_poll */
386
387 /**************************************************/
388 /* table for log filesystem functions defined above. */
389 /**************************************************/
390 static struct file_operations log_fops =
391 {
392         .llseek         = no_llseek,
393         .read           = hysdn_log_read,
394         .write          = hysdn_log_write,
395         .poll           = hysdn_log_poll,
396         .open           = hysdn_log_open,
397         .release        = hysdn_log_close,                                        
398 };
399
400
401 /***********************************************************************************/
402 /* hysdn_proclog_init is called when the module is loaded after creating the cards */
403 /* conf files.                                                                     */
404 /***********************************************************************************/
405 int
406 hysdn_proclog_init(hysdn_card * card)
407 {
408         struct procdata *pd;
409
410         /* create a cardlog proc entry */
411
412         if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
413                 memset(pd, 0, sizeof(struct procdata));
414                 sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
415                 if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL) {
416                         pd->log->proc_fops = &log_fops; 
417                         pd->log->owner = THIS_MODULE;
418                 }
419
420                 init_waitqueue_head(&(pd->rd_queue));
421
422                 card->proclog = (void *) pd;    /* remember procfs structure */
423         }
424         return (0);
425 }                               /* hysdn_proclog_init */
426
427 /************************************************************************************/
428 /* hysdn_proclog_release is called when the module is unloaded and before the cards */
429 /* conf file is released                                                            */
430 /* The module counter is assumed to be 0 !                                          */
431 /************************************************************************************/
432 void
433 hysdn_proclog_release(hysdn_card * card)
434 {
435         struct procdata *pd;
436
437         if ((pd = (struct procdata *) card->proclog) != NULL) {
438                 if (pd->log)
439                         remove_proc_entry(pd->log_name, hysdn_proc_entry);
440                 kfree(pd);      /* release memory */
441                 card->proclog = NULL;
442         }
443 }                               /* hysdn_proclog_release */