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