1/* $Id: hysdn_proclog.c,v 1.1.1.1 2008/10/15 03:26:34 james26_jang 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 */
24extern struct proc_dir_entry *hysdn_proc_entry;
25
26/*************************************************/
27/* structure keeping ascii log for device output */
28/*************************************************/
29struct 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/**********************************************/
39struct 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/**********************************************/
53void
54hysdn_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/***************************************************/
65void
66hysdn_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/********************************************/
97void
98put_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/****************************************/
152static ssize_t
153hysdn_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/******************/
205static ssize_t
206hysdn_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
214	if (!*((struct log_data **) file->private_data)) {
215		if (file->f_flags & O_NONBLOCK)
216			return (-EAGAIN);
217
218		/* sorry, but we need to search the card */
219		ino = file->f_dentry->d_inode->i_ino & 0xFFFF;	/* low-ino */
220		card = card_root;
221		while (card) {
222			pd = card->proclog;
223			if (pd->log->low_ino == ino)
224				break;
225			card = card->next;	/* search next entry */
226		}
227		if (card)
228			interruptible_sleep_on(&(pd->rd_queue));
229		else
230			return (-EAGAIN);
231
232	}
233	if (!(inf = *((struct log_data **) file->private_data)))
234		return (0);
235
236	inf->usage_cnt--;	/* new usage count */
237	(struct log_data **) file->private_data = &inf->next;	/* next structure */
238	if ((len = strlen(inf->log_start)) <= count) {
239		if (copy_to_user(buf, inf->log_start, len))
240			return -EFAULT;
241		file->f_pos += len;
242		return (len);
243	}
244	return (0);
245}				/* hysdn_log_read */
246
247/******************/
248/* open log file */
249/******************/
250static int
251hysdn_log_open(struct inode *ino, struct file *filep)
252{
253	hysdn_card *card;
254	struct procdata *pd = NULL;
255	ulong flags;
256
257	lock_kernel();
258	card = card_root;
259	while (card) {
260		pd = card->proclog;
261		if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
262			break;
263		card = card->next;	/* search next entry */
264	}
265	if (!card) {
266		unlock_kernel();
267		return (-ENODEV);	/* device is unknown/invalid */
268	}
269	filep->private_data = card;	/* remember our own card */
270
271	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
272		/* write only access -> write log level only */
273	} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
274
275		/* read access -> log/debug read */
276		save_flags(flags);
277		cli();
278		pd->if_used++;
279		if (pd->log_head)
280			(struct log_data **) filep->private_data = &(pd->log_tail->next);
281		else
282			(struct log_data **) filep->private_data = &(pd->log_head);
283		restore_flags(flags);
284	} else {		/* simultaneous read/write access forbidden ! */
285		unlock_kernel();
286		return (-EPERM);	/* no permission this time */
287	}
288	unlock_kernel();
289	return (0);
290}				/* hysdn_log_open */
291
292/*******************************************************************************/
293/* close a cardlog file. If the file has been opened for exclusive write it is */
294/* assumed as pof data input and the pof loader is noticed about.              */
295/* Otherwise file is handled as log output. In this case the interface usage   */
296/* count is decremented and all buffers are noticed of closing. If this file   */
297/* was the last one to be closed, all buffers are freed.                       */
298/*******************************************************************************/
299static int
300hysdn_log_close(struct inode *ino, struct file *filep)
301{
302	struct log_data *inf;
303	struct procdata *pd;
304	hysdn_card *card;
305	int flags, retval = 0;
306
307
308	lock_kernel();
309	if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
310		/* write only access -> write debug level written */
311		retval = 0;	/* success */
312	} else {
313		/* read access -> log/debug read, mark one further file as closed */
314
315		pd = NULL;
316		save_flags(flags);
317		cli();
318		inf = *((struct log_data **) filep->private_data);	/* get first log entry */
319		if (inf)
320			pd = (struct procdata *) inf->proc_ctrl;	/* still entries there */
321		else {
322			/* no info available -> search card */
323			card = card_root;
324			while (card) {
325				pd = card->proclog;
326				if (pd->log->low_ino == (ino->i_ino & 0xFFFF))
327					break;
328				card = card->next;	/* search next entry */
329			}
330			if (card)
331				pd = card->proclog;	/* pointer to procfs log */
332		}
333		if (pd)
334			pd->if_used--;	/* decrement interface usage count by one */
335
336		while (inf) {
337			inf->usage_cnt--;	/* decrement usage count for buffers */
338			inf = inf->next;
339		}
340		restore_flags(flags);
341
342		if (pd)
343			if (pd->if_used <= 0)	/* delete buffers if last file closed */
344				while (pd->log_head) {
345					inf = pd->log_head;
346					pd->log_head = pd->log_head->next;
347					kfree(inf);
348				}
349	}			/* read access */
350	unlock_kernel();
351
352	return (retval);
353}				/* hysdn_log_close */
354
355/*************************************************/
356/* select/poll routine to be able using select() */
357/*************************************************/
358static unsigned int
359hysdn_log_poll(struct file *file, poll_table * wait)
360{
361	unsigned int mask = 0;
362	word ino;
363	hysdn_card *card;
364	struct procdata *pd = NULL;
365
366	if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
367		return (mask);	/* no polling for write supported */
368
369	/* we need to search the card */
370	ino = file->f_dentry->d_inode->i_ino & 0xFFFF;	/* low-ino */
371	card = card_root;
372	while (card) {
373		pd = card->proclog;
374		if (pd->log->low_ino == ino)
375			break;
376		card = card->next;	/* search next entry */
377	}
378	if (!card)
379		return (mask);	/* card not found */
380
381	poll_wait(file, &(pd->rd_queue), wait);
382
383	if (*((struct log_data **) file->private_data))
384		mask |= POLLIN | POLLRDNORM;
385
386	return mask;
387}				/* hysdn_log_poll */
388
389/**************************************************/
390/* table for log filesystem functions defined above. */
391/**************************************************/
392static struct file_operations log_fops =
393{
394	llseek:         no_llseek,
395	read:           hysdn_log_read,
396	write:          hysdn_log_write,
397	poll:           hysdn_log_poll,
398	open:           hysdn_log_open,
399	release:        hysdn_log_close,
400};
401
402
403/***********************************************************************************/
404/* hysdn_proclog_init is called when the module is loaded after creating the cards */
405/* conf files.                                                                     */
406/***********************************************************************************/
407int
408hysdn_proclog_init(hysdn_card * card)
409{
410	struct procdata *pd;
411
412	/* create a cardlog proc entry */
413
414	if ((pd = (struct procdata *) kmalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
415		memset(pd, 0, sizeof(struct procdata));
416		sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
417		if ((pd->log = create_proc_entry(pd->log_name, S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry)) != NULL) {
418		        pd->log->proc_fops = &log_fops;
419		        pd->log->owner = THIS_MODULE;
420		}
421
422		init_waitqueue_head(&(pd->rd_queue));
423
424		card->proclog = (void *) pd;	/* remember procfs structure */
425	}
426	return (0);
427}				/* hysdn_proclog_init */
428
429/************************************************************************************/
430/* hysdn_proclog_release is called when the module is unloaded and before the cards */
431/* conf file is released                                                            */
432/* The module counter is assumed to be 0 !                                          */
433/************************************************************************************/
434void
435hysdn_proclog_release(hysdn_card * card)
436{
437	struct procdata *pd;
438
439	if ((pd = (struct procdata *) card->proclog) != NULL) {
440		if (pd->log)
441			remove_proc_entry(pd->log_name, hysdn_proc_entry);
442		kfree(pd);	/* release memory */
443		card->proclog = NULL;
444	}
445}				/* hysdn_proclog_release */
446