| /****************************************************************************** | 
 |  * | 
 |  * Name:    skproc.c | 
 |  * Project:	GEnesis, PCI Gigabit Ethernet Adapter | 
 |  * Version:	$Revision: 1.4 $ | 
 |  * Date:    $Date: 2003/02/25 14:16:37 $ | 
 |  * Purpose:	Funktions to display statictic data | 
 |  * | 
 |  ******************************************************************************/ | 
 |  | 
 | /****************************************************************************** | 
 |  * | 
 |  *	(C)Copyright 1998-2003 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 22-Nov-2000 | 
 |  *	Author: Mirko Lindner (mlindner@syskonnect.de) | 
 |  * | 
 |  *	The information in this file is provided "AS IS" without warranty. | 
 |  * | 
 |  ******************************************************************************/ | 
 | /****************************************************************************** | 
 |  * | 
 |  * History: | 
 |  * | 
 |  *	$Log: skproc.c,v $ | 
 |  *	Revision 1.4  2003/02/25 14:16:37  mlindner | 
 |  *	Fix: Copyright statement | 
 |  * | 
 |  *	Revision 1.3  2002/10/02 12:59:51  mlindner | 
 |  *	Add: Support for Yukon | 
 |  *	Add: Speed check and setup | 
 |  *	Add: Merge source for kernel 2.2.x and 2.4.x | 
 |  *	Add: Read sensor names directly from VPD | 
 |  *	Fix: Volt values | 
 |  * | 
 |  *	Revision 1.2.2.7  2002/01/14 12:45:15  mlindner | 
 |  *	Fix: Editorial changes | 
 |  * | 
 |  *	Revision 1.2.2.6  2001/12/06 15:26:07  mlindner | 
 |  *	Fix: Return value of proc_read | 
 |  * | 
 |  *	Revision 1.2.2.5  2001/12/06 09:57:39  mlindner | 
 |  *	New ProcFs entries | 
 |  * | 
 |  *	Revision 1.2.2.4  2001/09/05 12:16:02  mlindner | 
 |  *	Add: New ProcFs entries | 
 |  *	Fix: Counter Errors (Jumbo == to long errors) | 
 |  *	Fix: Kernel error compilation | 
 |  *	Fix: too short counters | 
 |  * | 
 |  *	Revision 1.2.2.3  2001/06/25 07:26:26  mlindner | 
 |  *	Add: More error messages | 
 |  * | 
 |  *	Revision 1.2.2.2  2001/03/15 12:50:13  mlindner | 
 |  *	fix: ProcFS owner protection | 
 |  * | 
 |  *	Revision 1.2.2.1  2001/03/12 16:43:48  mlindner | 
 |  *	chg: 2.4 requirements for procfs | 
 |  * | 
 |  *	Revision 1.1  2001/01/22 14:15:31  mlindner | 
 |  *	added ProcFs functionality | 
 |  *	Dual Net functionality integrated | 
 |  *	Rlmt networks added | 
 |  * | 
 |  * | 
 |  ******************************************************************************/ | 
 |  | 
 | #include <config.h> | 
 |  | 
 | #ifdef CONFIG_SK98 | 
 |  | 
 | #include <linux/proc_fs.h> | 
 |  | 
 | #include "h/skdrv1st.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 struct net_device 	*SkGeRootDev; | 
 |  | 
 | 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 = 0; | 
 | 	int t; | 
 | 	int i; | 
 | 	DEV_NET					*pNet; | 
 | 	SK_AC					*pAC; | 
 | 	char 					test_buf[100]; | 
 | 	char					sens_msg[50]; | 
 | 	unsigned long			Flags; | 
 | 	unsigned int			Size; | 
 | 	struct SK_NET_DEVICE 		*next; | 
 | 	struct SK_NET_DEVICE 		*SkgeProcDev = SkGeRootDev; | 
 |  | 
 | 	SK_PNMI_STRUCT_DATA 	*pPnmiStruct; | 
 | 	SK_PNMI_STAT		*pPnmiStat; | 
 | 	struct proc_dir_entry *file = (struct proc_dir_entry*) data; | 
 |  | 
 | 	while (SkgeProcDev) { | 
 | 		pNet = (DEV_NET*) SkgeProcDev->priv; | 
 | 		pAC = pNet->pAC; | 
 | 		next = pAC->Next; | 
 | 		pPnmiStruct = &pAC->PnmiStruct; | 
 | 		/* NetIndex in GetStruct is now required, zero is only dummy */ | 
 |  | 
 | 		for (t=pAC->GIni.GIMacsFound; t > 0; t--) { | 
 | 			if ((pAC->GIni.GIMacsFound == 2) && pAC->RlmtNets == 1) | 
 | 				t--; | 
 |  | 
 | 			spin_lock_irqsave(&pAC->SlowPathLock, Flags); | 
 | 			Size = SK_PNMI_STRUCT_SIZE; | 
 | 			SkPnmiGetStruct(pAC, pAC->IoBase, | 
 | 				pPnmiStruct, &Size, t-1); | 
 | 			spin_unlock_irqrestore(&pAC->SlowPathLock, Flags); | 
 |  | 
 | 			if (strcmp(pAC->dev[t-1]->name, file->name) == 0) { | 
 | 				pPnmiStat = &pPnmiStruct->Stat[0]; | 
 | 				len = sprintf(buffer, | 
 | 					"\nDetailed statistic for device %s\n", | 
 | 					pAC->dev[t-1]->name); | 
 | 				len += sprintf(buffer + len, | 
 | 					"=======================================\n"); | 
 |  | 
 | 				/* Board statistics */ | 
 | 				len += sprintf(buffer + len, | 
 | 					"\nBoard statistics\n\n"); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Active Port                    %c\n", | 
 | 					'A' + pAC->Rlmt.Net[t-1].Port[pAC->Rlmt. | 
 | 					Net[t-1].PrefPort]->PortNumber); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Preferred Port                 %c\n", | 
 | 					'A' + pAC->Rlmt.Net[t-1].Port[pAC->Rlmt. | 
 | 					Net[t-1].PrefPort]->PortNumber); | 
 |  | 
 | 				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, | 
 | 					"Hardware revision              v%d.%d\n", | 
 | 					(pAC->GIni.GIPciHwRev >> 4) & 0x0F, | 
 | 					pAC->GIni.GIPciHwRev & 0x0F); | 
 |  | 
 | 				/* Print sensor informations */ | 
 | 				for (i=0; i < pAC->I2c.MaxSens; i ++) { | 
 | 					/* Check type */ | 
 | 					switch (pAC->I2c.SenTable[i].SenType) { | 
 | 					case 1: | 
 | 						strcpy(sens_msg, pAC->I2c.SenTable[i].SenDesc); | 
 | 						strcat(sens_msg, " (C)"); | 
 | 						len += sprintf(buffer + len, | 
 | 							"%-25s      %d.%02d\n", | 
 | 							sens_msg, | 
 | 							pAC->I2c.SenTable[i].SenValue / 10, | 
 | 							pAC->I2c.SenTable[i].SenValue % 10); | 
 |  | 
 | 						strcpy(sens_msg, pAC->I2c.SenTable[i].SenDesc); | 
 | 						strcat(sens_msg, " (F)"); | 
 | 						len += sprintf(buffer + len, | 
 | 							"%-25s      %d.%02d\n", | 
 | 							sens_msg, | 
 | 							((((pAC->I2c.SenTable[i].SenValue) | 
 | 							*10)*9)/5 + 3200)/100, | 
 | 							((((pAC->I2c.SenTable[i].SenValue) | 
 | 							*10)*9)/5 + 3200) % 10); | 
 | 						break; | 
 | 					case 2: | 
 | 						strcpy(sens_msg, pAC->I2c.SenTable[i].SenDesc); | 
 | 						strcat(sens_msg, " (V)"); | 
 | 						len += sprintf(buffer + len, | 
 | 							"%-25s      %d.%03d\n", | 
 | 							sens_msg, | 
 | 							pAC->I2c.SenTable[i].SenValue / 1000, | 
 | 							pAC->I2c.SenTable[i].SenValue % 1000); | 
 | 						break; | 
 | 					case 3: | 
 | 						strcpy(sens_msg, pAC->I2c.SenTable[i].SenDesc); | 
 | 						strcat(sens_msg, " (rpm)"); | 
 | 						len += sprintf(buffer + len, | 
 | 							"%-25s      %d\n", | 
 | 							sens_msg, | 
 | 							pAC->I2c.SenTable[i].SenValue); | 
 | 						break; | 
 | 					default: | 
 | 						break; | 
 | 					} | 
 | 				} | 
 |  | 
 | 				/*Receive statistics */ | 
 | 				len += sprintf(buffer + len, | 
 | 				"\nReceive statistics\n\n"); | 
 |  | 
 | 				len += sprintf(buffer + len, | 
 | 					"Received bytes                 %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxOctetsOkCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Received packets               %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxOkCts, | 
 | 					10,0,-1,0)); | 
 | #if 0 | 
 | 				if (pAC->GIni.GP[0].PhyType == SK_PHY_XMAC && | 
 | 					pAC->HWRevision < 12) { | 
 | 					pPnmiStruct->InErrorsCts = pPnmiStruct->InErrorsCts - | 
 | 						pPnmiStat->StatRxShortsCts; | 
 | 					pPnmiStat->StatRxShortsCts = 0; | 
 | 				} | 
 | #endif | 
 | 				if (pNet->Mtu > 1500) | 
 | 					pPnmiStruct->InErrorsCts = pPnmiStruct->InErrorsCts - | 
 | 						pPnmiStat->StatRxTooLongCts; | 
 |  | 
 | 				len += sprintf(buffer + len, | 
 | 					"Receive errors                 %s\n", | 
 | 					SkNumber(test_buf, pPnmiStruct->InErrorsCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Receive drops                  %s\n", | 
 | 					SkNumber(test_buf, pPnmiStruct->RxNoBufCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Received multicast             %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxMulticastOkCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Receive error types\n"); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   length                      %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxRuntCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   buffer overflow             %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxFifoOverflowCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   bad crc                     %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxFcsCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   framing                     %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxFramingCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   missed frames               %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxMissedCts, | 
 | 					10, 0, -1, 0)); | 
 |  | 
 | 				if (pNet->Mtu > 1500) | 
 | 					pPnmiStat->StatRxTooLongCts = 0; | 
 |  | 
 | 				len += sprintf(buffer + len, | 
 | 					"   too long                    %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxTooLongCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   carrier extension           %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxCextCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   too short                   %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxShortsCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   symbol                      %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxSymbolCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   LLC MAC size                %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxIRLengthCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   carrier event               %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxCarrierCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   jabber                      %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatRxJabberCts, | 
 | 					10, 0, -1, 0)); | 
 |  | 
 |  | 
 | 				/*Transmit statistics */ | 
 | 				len += sprintf(buffer + len, | 
 | 				"\nTransmit statistics\n\n"); | 
 |  | 
 | 				len += sprintf(buffer + len, | 
 | 					"Transmited bytes               %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatTxOctetsOkCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Transmited packets             %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatTxOkCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Transmit errors                %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatTxSingleCollisionCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Transmit dropped               %s\n", | 
 | 					SkNumber(test_buf, pPnmiStruct->TxNoBufCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Transmit collisions            %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatTxSingleCollisionCts, | 
 | 					10,0,-1,0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"Transmit errors types\n"); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   excessive collision         %ld\n", | 
 | 					pAC->stats.tx_aborted_errors); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   carrier                     %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatTxCarrierCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   fifo underrun               %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatTxFifoUnderrunCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   heartbeat                   %s\n", | 
 | 					SkNumber(test_buf, pPnmiStat->StatTxCarrierCts, | 
 | 					10, 0, -1, 0)); | 
 | 				len += sprintf(buffer + len, | 
 | 					"   window                      %ld\n", | 
 | 					pAC->stats.tx_window_errors); | 
 |  | 
 | 			} | 
 | 		} | 
 | 		SkgeProcDev = next; | 
 | 	} | 
 | 	if (offset >= len) { | 
 | 		*eof = 1; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	*buffer_location = buffer + offset; | 
 | 	if (buffer_length >= len - offset) { | 
 | 		*eof = 1; | 
 | 	} | 
 | 	return (min_t(int, buffer_length, len - 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; | 
 | } | 
 |  | 
 | #endif /* CONFIG_SK98 */ |