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