/******************************************************************************
 *
 * Name:	skgiproc.c
 * Project:	SysKonnect SK-9Dxx Gigabit Ethernet
 * Version:	$Revision: 1.6 $
 * Date:    $Date: 2002/02/18 10:14:42 $
 * Purpose:	Functions to display statistics data
 *
 ******************************************************************************/

/******************************************************************************
 *
 *	(C)Copyright 2001 SysKonnect GmbH.
 *
 *	This program 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.
 *
 *	Created 19-May-2001
 *	Author: Mirko Lindner (mlindner@syskonnect.de)
 *
 *	The information in this file is provided "AS IS" without warranty.
 *
 ******************************************************************************/

/******************************************************************************
 *
 * History:
 *
 *	$Log: skgiproc.c,v $
 *	Revision 1.6  2002/02/18 10:14:42  mlindner
 *	fix: Speed output
 *	
 *	Revision 1.5  2001/10/22 15:54:59  rschmidt
 *	Changes for Kernel 2.4.x
 *	
 *	Revision 1.4  2001/08/14 14:11:54  rschmidt
 *	Added line speed info and counter for Rx errors.
 *	Editorial changes.
 *	
 *	Revision 1.3  2001/06/05 15:18:41  rassmann
 *	Changed file header.
 *	
 *	Revision 1.2  2001/06/05 15:13:49  rassmann
 *	Kernel 2.4 support.
 *	
 *	Revision 1.1  2001/06/05 08:28:23  rassmann
 *	First public version.
 *	
 *
 ******************************************************************************/

#include "h/skdrv1st.h"

#include <linux/proc_fs.h>
#include "h/skdrv2nd.h"


#define ZEROPAD		1		/* pad with zero */
#define SIGN		2		/* unsigned/signed long */
#define PLUS		4		/* show plus */
#define SPACE		8		/* space if plus */
#define LEFT		16		/* left justified */
#define SPECIALX	32		/* 0x */
#define LARGE		64

extern SK_AC			*pACList;

extern uint power(
uint base,
uint exp);

extern char * SkNumber(
	char * str,
	long long num,
	int base,
	int size,
	int precision,
	int type);

/*****************************************************************************
 *
 * 	proc_read - print "summaries" entry
 *
 * Description:
 *  This function fills the proc entry with statistic data about
 *  the ethernet device.
 *
 *
 * Returns: buffer with statistic data
 *	
 */
int proc_read(char *buffer,
char **buffer_location,
off_t offset,
int buffer_length,
int *eof,
void *data)
{
	int len;
	SK_AC			*pAC;
	char 			test_buf[100];
	unsigned int	Flags;		
	unsigned int	Size;
	NET_DEV			*dev;	

	SK_PNMI_STRUCT_DATA 	*pPnmiStruct;
	SK_PNMI_STAT			*pPnmiStat;
	struct proc_dir_entry	*file;

	len = 0;
	file = (struct proc_dir_entry *)data;
	pAC = pACList;

	while (pAC != NULL) {		
		dev = pACList->dev;
		pPnmiStruct = &pACList->PnmiStruct;

		spin_lock_irqsave(&pAC->SlowPathLock, Flags);
		Size = SK_PNMI_STRUCT_SIZE;
		SkPnmiGetStruct(pAC, pAC->IoBase, pPnmiStruct, &Size);
		spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);

		if (strcmp(pAC->dev->name, file->name) == 0) {
			pPnmiStat = &pPnmiStruct->Stat[0];
			len = sprintf(buffer,
				"\nDetailed statistic for device %s\n",
				pAC->dev->name);
			len += sprintf(buffer + len,
				"==================================\n");

			len += sprintf(buffer + len,
				"\nBoard statistics\n\n");
			len += sprintf(buffer + len,
				"Bus speed (MHz)           %d\n",
             			 pPnmiStruct->BusSpeed);
			len += sprintf(buffer + len,
				"Bus width (Bit)           %d\n",
				pPnmiStruct->BusWidth);
			len += sprintf(buffer + len,
				"Link speed (Mbps)         %u\n",
				power(10, pAC->Hw.LineSpeed));
			/*	power(10, pPnmiStruct->EthSpeed));*/

			/* Receive statistics */

			len += sprintf(buffer + len,
				"\nReceive statistics\n\n");
			len += sprintf(buffer + len,
				"Received bytes            %s\n",
				SkNumber(test_buf,
					pPnmiStat->ifHCInOctets,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"Received packets          %s\n",
				SkNumber(test_buf,
					pPnmiStat->COSIfHCInPkts,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"Received errors           %s\n",
				SkNumber(test_buf,
					pPnmiStat->ifInErrors,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"Received errors types\n");
			len += sprintf(buffer + len,
				"   length errors          %s\n",
				SkNumber(test_buf,
					pPnmiStat->etherStatsUndersizePkts,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"   over errors            %s\n",
				SkNumber(test_buf,
					pPnmiStat->nicNoMoreRxBDs,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"   crc errors             %s\n",
				SkNumber(test_buf,
					pPnmiStat->dot3StatsFCSErrors,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"   frame errors           %s\n",
				SkNumber(test_buf,
					pPnmiStat->dot3StatsAlignmentErrors,
					10,0,-1,0));				
			len += sprintf(buffer + len,
				"   fifo errors            %s\n",
				SkNumber(test_buf,
					pPnmiStat->nicNoMoreRxBDs,
					10,0,-1,0));				
			len += sprintf(buffer + len,
				"   missed errors          none\n");
				
				/* Transmit statistics */

			len += sprintf(buffer + len,
				"\nTransmit statistics\n\n");
			len += sprintf(buffer + len,
				"Transmit bytes            %s\n",
				SkNumber(test_buf,
					pPnmiStat->ifHCOutOctets,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"Transmit packets          %s\n",
				SkNumber(test_buf,
					pPnmiStat->COSIfHCOutPkts,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"Transmit errors           %s\n",
				SkNumber(test_buf,
					pPnmiStat->dot3StatsSingleCollisionFrames,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"Transmit dropped          none\n");
			len += sprintf(buffer + len,
				"Transmit collisions       %s\n",
				SkNumber(test_buf,
					pPnmiStat->dot3StatsSingleCollisionFrames,
					10,0,-1,0));
			len += sprintf(buffer + len,
				"Transmited errors types\n");
			len += sprintf(buffer + len,
				"   aborted errors         none\n");
			len += sprintf(buffer + len,
				"   carrier errors         %s\n",
				SkNumber(test_buf,
					pPnmiStat->dot3StatsCarrierSenseErrors,
					10, 0, -1, 0));
			len += sprintf(buffer + len,
				"   fifo errors            %s\n",
				SkNumber(test_buf,
					pPnmiStat->dot3StatsInternalMacTransmitErrors,
					10, 0, -1, 0));
			len += sprintf(buffer + len,
				"   heartbeat errors       %s\n",
				SkNumber(test_buf,
					pPnmiStat->dot3StatsCarrierSenseErrors,
					10, 0, -1, 0));
			len += sprintf(buffer + len,
				"   window errors          none\n");

		}
		pAC = pAC->pNext;
	}


	if (offset >= len) {
		*eof = 1;
		return 0;
	}

	*buffer_location = buffer + offset;
	if (buffer_length >= len - offset) {
		*eof = 1;
	}

	return (min(buffer_length, len - (int)offset));
}





/*****************************************************************************
 *
 * SkDoDiv - convert 64bit number
 *
 * Description:
 *	This function "converts" a long long number.
 *
 * Returns:
 *	remainder of division
 */
static long SkDoDiv (long long Dividend, int Divisor, long long *pErg)
{
 long   	Rest;
 long long 	Ergebnis;
 long   	Akku;


 Akku  = Dividend >> 32;

 Ergebnis = ((long long) (Akku / Divisor)) << 32;
 Rest = Akku % Divisor ;

 Akku = Rest << 16;
 Akku |= ((Dividend & 0xFFFF0000) >> 16);


 Ergebnis += ((long long) (Akku / Divisor)) << 16;
 Rest = Akku % Divisor ;

 Akku = Rest << 16;
 Akku |= (Dividend & 0xFFFF);

 Ergebnis += (Akku / Divisor);
 Rest = Akku % Divisor ;

 *pErg = Ergebnis;
 return (Rest);
}


#if 0
#define do_div(n,base) ({ \
long long __res; \
__res = ((unsigned long long) n) % (unsigned) base; \
n = ((unsigned long long) n) / (unsigned) base; \
__res; })

#endif


/*****************************************************************************
 *
 * SkNumber - Print results
 *
 * Description:
 *	This function converts a long long number into a string.
 *
 * Returns:
 *	number as string
 */
char *SkNumber(
char		*str,
long long	num,
int			base,
int			size,
int			precision,
int			type)
{
	char c,sign,tmp[66], *strorg = str;
	const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
	int i;

	if (type & LARGE) {
		digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	}
	if (type & LEFT) {
		type &= ~ZEROPAD;
	}
	if (base < 2 || base > 36) {
		return 0;
	}

	c = (type & ZEROPAD) ? '0' : ' ';
	sign = 0;
	if (type & SIGN) {
		if (num < 0) {
			sign = '-';
			num = -num;
			size--;
		}
		else if (type & PLUS) {
			sign = '+';
			size--;
		}
		else if (type & SPACE) {
			sign = ' ';
			size--;
		}
	}
	if (type & SPECIALX) {
		if (base == 16) {
			size -= 2;
		}
		else if (base == 8) {
			size--;
		}
	}
	i = 0;
	if (num == 0) {
		tmp[i++]='0';
	}
	else while (num != 0) {
		tmp[i++] = digits[SkDoDiv(num,base, &num)];
	}

	if (i > precision) {
		precision = i;
	}
	size -= precision;
	if (!(type&(ZEROPAD+LEFT))) {
		while (size-- > 0) {
			*str++ = ' ';
		}
	}
	if (sign)	{
		*str++ = sign;
	}
	if (type & SPECIALX) {
		if (base == 8) {
			*str++ = '0';
		}
		else if (base == 16) {
			*str++ = '0';
			*str++ = digits[33];
		}
	}
	if (!(type & LEFT)) {
		while (size-- > 0) {
			*str++ = c;
		}
	}
	while (i < precision--) {
		*str++ = '0';
	}
	while (i-- > 0) {
		*str++ = tmp[i];
	}
	while (size-- > 0) {
		*str++ = ' ';
	}
	
	str[0] = '\0';
	
	return (strorg);
}


