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