/*
  Copyright 2005 Denis Vlasenko

  This file is part of prism54usb.

  prism54usb is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  prism54usb is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with prism54usb; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

#ifndef _HAVE_ISLSL_LOG_H_
#define _HAVE_ISLSL_LOG_H_

#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>

#define ISL_DEBUG 2

/* Madwifi does define printf as a macro. Aren't they using gcc with
 * format attribute too ? */
#undef printf

/* FIXME: relayfs support is needed but don't know how to check for it */
#if (ISL_DEBUG > 0) && defined CONFIG_DEBUG_FS && (defined CONFIG_RELAYFS_FS || defined CONFIG_RELAYFS_FS_MODULE)
#define ISL_DEBUG_RELAY 1
#else
#define ISL_DEBUG_RELAY 0
#endif

/***********************************************************************
** Logging: guidelines
**
** - Use printk() for messages which are to be always logged.
**   Supply either 'islsm:' or '<devname>:' prefix so that user
**   can figure out who's speaking among other kernel chatter.
**   islsm: is for general issues (e.g. "islsm: no firmware image!")
**   while <devname>: is related to a particular device
**   (think about multi-card setup). Double check that message
**   is not confusing to the average user.
**
** - use printk KERN_xxx level only if message is not a WARNING
**   but is INFO, ERR etc. In general, we mostly not using these
**
** - Use islog() for messages which may be omitted (and they
**   _will_ be omitted in non-debug builds). Note that
**   message levels may be disabled at compile-time selectively,
**   thus select them wisely. Example: L_DEBUG is the lowest
**   (most likely to be compiled out) -> use for less important stuff.
**
** - Do not print important stuff with islog(), or else people
**   will never build non-debug driver.
*/

enum {
	/* generic debug */
	L_DEBUG = (ISL_DEBUG > 1) * 0x0001,
	/* function tracing */
	L_FUNC = (ISL_DEBUG > 0) * 0x0002,
	/* IRQ data, use with care */
	L_IRQ = (ISL_DEBUG > 0) * 0x0004,
	/* UART data */
	L_DATA =  (ISL_DEBUG > 1) * 0x0008,
	/* device IOCTLs */
	L_IOCTL = (ISL_DEBUG > 0) * 0x0010,
	/* raw protocol islsm frames
	   input (R) and output (T) */
	L_BUFR = (ISL_DEBUG > 1) * 0x0020,
	L_BUFT = (ISL_DEBUG > 1) * 0x0040,
	/* firmware & PDA parsing infos */
	L_FW = (ISL_DEBUG > 0) * 0x0080,
	L_PDA = (ISL_DEBUG > 0) * 0x0100,
	/* allocator debug */
	L_ALLOC = (ISL_DEBUG > 1) * 0x0200,
	/* frame interpretation, input, output */
	L_SM_INPUT  = (ISL_DEBUG > 0) * 0x0400,
	L_SM_OUTPUT = (ISL_DEBUG > 0) * 0x0800,
	L_ANY = 0xffff,
};

#if ISL_DEBUG
extern unsigned int     isl_debug;
#else
enum { isl_debug = 0 };
#endif

#if ISL_DEBUG_RELAY > 0         /* relay-specific functions */
extern unsigned int     isl_relay;
#else
enum { isl_relay = 0 };
#endif

#if ISL_DEBUG > 1

void                    isl_fn_enter(const char *funcname);
void                    isl_fn_exit(const char *funcname);
void                    isl_fn_exit_v(const char *funcname, int v);

#define FN_ENTER \
	do { \
		if (unlikely(isl_debug & L_FUNC)) { \
			isl_fn_enter(__func__); \
		} \
	} while (0)

#define FN_EXIT1(v) \
	do { \
		if (unlikely(isl_debug & L_FUNC)) { \
			isl_fn_exit_v(__func__, v); \
		} \
	} while (0)
#define FN_EXIT0 \
	do { \
		if (unlikely(isl_debug & L_FUNC)) { \
			isl_fn_exit(__func__); \
		} \
	} while (0)

#else

#define FN_ENTER
#define FN_EXIT1(v)
#define FN_EXIT0

#endif				/* ISL_DEBUG > 1 */

#if ISL_DEBUG

#if ISL_DEBUG_RELAY > 0         /* relay-specific functions */

void islsm_log_cleanup(void);
int  islsm_log_init(void);
void isl_dump_bytes(const void *data, int num);
void islog_bytes_relay(const int chan, const void *data, int num);
int  islog_relay(const int chan, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3)));

#define islog_dump_bytes(chan, args...) islog_bytes_relay(chan, args)
#define islog_printk(chan, args...) islog_relay(chan, args)

#else                           /* non-relayfs version */

static inline int islsm_log_init(void) { return 0; }

static inline void islsm_log_cleanup(void) { }

void isl_dump_bytes(const void *data, int num);

#define islog_dump_bytes(chan, args...) isl_dump_bytes(args)
#define islog_printk(chan, args...) printk(args)

#endif                          /* ISL_RELAY_DEBUG > 0 */

/* common relay and non-relay definitions */

#define islog(chan, args...) \
	do { \
		if (isl_debug & (chan)) \
			islog_printk((chan), args); \
	} while (0)

#define islog_bytes(chan, data, num) \
	do { \
		if (isl_debug & (chan)) \
			islog_dump_bytes((chan), data, num); \
	} while (0)

#else				/* Non-debug build: */

/*
 * Defining the function eases the islog usage as it does not lead to
 * "unused variable" errors in non-debug builds
 */

static inline int islsm_log_init(void) { return 0; }

static inline void islsm_log_cleanup(void) { }

static inline int islog(const int chan, const char *s, ...)
	__attribute__ ((format (printf, 2, 3)));
static inline int islog(const int chan, const char *s, ...) { return 0; }

static inline void isl_dump_bytes(const void *data, const int num) { }

static inline void islog_bytes(const int chan, const void *data, int num) { }

#endif				/* ISL_DEBUG */

void                    isl_print_mac(const char *head, const u8 *mac,
				      const char *tail);

/* Optimized out to nothing in non-debug build */
static inline void
islog_mac(int level, const char *head, const u8 *mac, const char *tail)
{
	if (isl_debug & level) {
		isl_print_mac(head, mac, tail);
	}
}

/* Useful to get __FILE__ without full leading path */
static inline const char *sanitize_str(const char *s)
{
	const char             *t = strrchr(s, '/');
	if (t)
		return t + 1;
	return s;
}

/* raw output data debug */
static inline void
islsm_txdata_debug(const unsigned int queue,
		   const void *_data, const int len)
{
	unsigned long int time = jiffies;
	islog(L_BUFT, "[OUT +%-6ld] %02x\n", time, queue);
	islog_bytes(L_BUFT, _data, len);
}

/* raw input data debug */
static inline void
islsm_rxdata_debug(const unsigned int queue,
		   const void *_data, const int len)
{
	unsigned long time = jiffies;
	islog(L_BUFR, "[IN +%-6ld] %02x\n", time, queue);
	islog_bytes(L_BUFR, _data, len);
}

#endif				/* _HAVE_ISLSL_LOG_H_ */
