/******************************************************************************/
/*                                                                            */
/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 - 2003 Broadcom  */
/* Corporation.                                                               */
/* All rights reserved.                                                       */
/*                                                                            */
/* 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, located in the file LICENSE.                 */
/*                                                                            */
/* /proc file system handling code.                                           */
/*                                                                            */
/******************************************************************************/

#include "mm.h"
#ifdef BCM_PROC_FS

#define NICINFO_PROC_DIR "nicinfo"

static struct proc_dir_entry *bcm5700_procfs_dir;

extern char bcm5700_driver[], bcm5700_version[];

extern uint64_t bcm5700_crc_count(PUM_DEVICE_BLOCK pUmDevice);
extern uint64_t bcm5700_rx_err_count(PUM_DEVICE_BLOCK pUmDevice);

static char *na_str = "n/a";
static char *pause_str = "pause ";
static char *asym_pause_str = "asym_pause ";
static char *on_str = "on";
static char *off_str = "off";
static char *up_str = "up";
static char *down_str = "down";

static struct proc_dir_entry *
proc_getdir(char *name, struct proc_dir_entry *proc_dir)
{
	struct proc_dir_entry *pde = proc_dir;

	lock_kernel();
	for (pde=pde->subdir; pde; pde = pde->next) {
		if (pde->namelen && (strcmp(name, pde->name) == 0)) {
			/* directory exists */
			break;
		}
	}
	if (pde == (struct proc_dir_entry *) 0)
	{
		/* create the directory */
#if (LINUX_VERSION_CODE > 0x20300)
		pde = proc_mkdir(name, proc_dir);
#else
		pde = create_proc_entry(name, S_IFDIR, proc_dir);
#endif
		if (pde == (struct proc_dir_entry *) 0) {
			unlock_kernel();
			return (pde);
		}
	}
	unlock_kernel();
	return (pde);
}

int
bcm5700_proc_create(void)
{
	bcm5700_procfs_dir = proc_getdir(NICINFO_PROC_DIR, proc_net);

	if (bcm5700_procfs_dir == (struct proc_dir_entry *) 0) {
		printk(KERN_DEBUG "Could not create procfs nicinfo directory %s\n", NICINFO_PROC_DIR);
		return -1;
	}
	return 0;
}

void
b57_get_speed_adv(PUM_DEVICE_BLOCK pUmDevice, char *str)
{
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;

	if (pDevice->DisableAutoNeg == TRUE) {
		strcpy(str, na_str);
		return;
	}
	if (pDevice->TbiFlags & ENABLE_TBI_FLAG) {
		strcpy(str, "1000full");
		return;
	}
	str[0] = 0;
	if (pDevice->advertising & PHY_AN_AD_10BASET_HALF) {
		strcat(str, "10half ");
	}
	if (pDevice->advertising & PHY_AN_AD_10BASET_FULL) {
		strcat(str, "10full ");
	}
	if (pDevice->advertising & PHY_AN_AD_100BASETX_HALF) {
		strcat(str, "100half ");
	}
	if (pDevice->advertising & PHY_AN_AD_100BASETX_FULL) {
		strcat(str, "100full ");
	}
	if (pDevice->advertising1000 & BCM540X_AN_AD_1000BASET_HALF) {
		strcat(str, "1000half ");
	}
	if (pDevice->advertising1000 & BCM540X_AN_AD_1000BASET_FULL) {
		strcat(str, "1000full ");
	}
}

void
b57_get_fc_adv(PUM_DEVICE_BLOCK pUmDevice, char *str)
{
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;

	if (pDevice->DisableAutoNeg == TRUE) {
		strcpy(str, na_str);
		return;
	}
	str[0] = 0;
	if (pDevice->TbiFlags & ENABLE_TBI_FLAG) {
		if(pDevice->DisableAutoNeg == FALSE ||
			pDevice->RequestedLineSpeed == LM_LINE_SPEED_AUTO) {
			if (pDevice->FlowControlCap &
				LM_FLOW_CONTROL_RECEIVE_PAUSE) {

				strcpy(str, pause_str);
				if (!(pDevice->FlowControlCap &
					LM_FLOW_CONTROL_TRANSMIT_PAUSE)) {

					strcpy(str, asym_pause_str);
				}
			}
			else if (pDevice->FlowControlCap &
				LM_FLOW_CONTROL_TRANSMIT_PAUSE) {

				strcpy(str, asym_pause_str);
			}
		}
		return;
	}
	if (pDevice->advertising & PHY_AN_AD_PAUSE_CAPABLE) {
		strcat(str, pause_str);
	}
	if (pDevice->advertising & PHY_AN_AD_ASYM_PAUSE) {
		strcat(str, asym_pause_str);
	}
}

int
bcm5700_read_pfs(char *page, char **start, off_t off, int count,
	int *eof, void *data)
{
	struct net_device *dev = (struct net_device *) data;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;
	PT3_STATS_BLOCK pStats = (PT3_STATS_BLOCK) pDevice->pStatsBlkVirt;
	int len = 0;
	unsigned long rx_mac_errors, rx_crc_errors, rx_align_errors;
	unsigned long rx_runt_errors, rx_frag_errors, rx_long_errors;
	unsigned long rx_overrun_errors, rx_jabber_errors;
	char str[64];

	if (pUmDevice->opened == 0)
		pStats = 0;

	len += sprintf(page+len, "Description\t\t\t%s\n", pUmDevice->name);
	len += sprintf(page+len, "Driver_Name\t\t\t%s\n", bcm5700_driver);
	len += sprintf(page+len, "Driver_Version\t\t\t%s\n", bcm5700_version);
	len += sprintf(page+len, "Bootcode_Version\t\t%s\n", pDevice->BootCodeVer);
	len += sprintf(page+len, "PCI_Vendor\t\t\t0x%04x\n", pDevice->PciVendorId);
	len += sprintf(page+len, "PCI_Device_ID\t\t\t0x%04x\n",
		pDevice->PciDeviceId);
	len += sprintf(page+len, "PCI_Subsystem_Vendor\t\t0x%04x\n",
		pDevice->SubsystemVendorId);
	len += sprintf(page+len, "PCI_Subsystem_ID\t\t0x%04x\n",
		pDevice->SubsystemId);
	len += sprintf(page+len, "PCI_Revision_ID\t\t\t0x%02x\n",
		pDevice->PciRevId);
	len += sprintf(page+len, "PCI_Slot\t\t\t%d\n",
		PCI_SLOT(pUmDevice->pdev->devfn));
	if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) {
		len += sprintf(page+len, "PCI_Function\t\t\t%d\n",
			pDevice->FunctNum);
	}
	len += sprintf(page+len, "PCI_Bus\t\t\t\t%d\n",
		pUmDevice->pdev->bus->number);

	len += sprintf(page+len, "PCI_Bus_Speed\t\t\t%s\n",
		pDevice->BusSpeedStr);

	len += sprintf(page+len, "Memory\t\t\t\t0x%lx\n", pUmDevice->dev->base_addr);
	len += sprintf(page+len, "IRQ\t\t\t\t%d\n", dev->irq);
	len += sprintf(page+len, "System_Device_Name\t\t%s\n", dev->name);
	len += sprintf(page+len, "Current_HWaddr\t\t\t%02x:%02x:%02x:%02x:%02x:%02x\n",
		dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
		dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
	len += sprintf(page+len,
		"Permanent_HWaddr\t\t%02x:%02x:%02x:%02x:%02x:%02x\n",
		pDevice->NodeAddress[0], pDevice->NodeAddress[1],
		pDevice->NodeAddress[2], pDevice->NodeAddress[3],
		pDevice->NodeAddress[4], pDevice->NodeAddress[5]);
	len += sprintf(page+len, "Part_Number\t\t\t%s\n\n", pDevice->PartNo);

	len += sprintf(page+len, "Link\t\t\t\t%s\n", 
		(pUmDevice->opened == 0) ? "unknown" :
    		((pDevice->LinkStatus == LM_STATUS_LINK_ACTIVE) ? up_str :
		down_str));
	len += sprintf(page+len, "Auto_Negotiate\t\t\t%s\n", 
    		(pDevice->DisableAutoNeg == TRUE) ? off_str : on_str);
	b57_get_speed_adv(pUmDevice, str);
	len += sprintf(page+len, "Speed_Advertisement\t\t%s\n", str);
	b57_get_fc_adv(pUmDevice, str);
	len += sprintf(page+len, "Flow_Control_Advertisement\t%s\n", str);
	len += sprintf(page+len, "Speed\t\t\t\t%s\n", 
    		((pDevice->LinkStatus == LM_STATUS_LINK_DOWN) ||
		(pUmDevice->opened == 0)) ? na_str :
    		((pDevice->LineSpeed == LM_LINE_SPEED_1000MBPS) ? "1000" :
    		((pDevice->LineSpeed == LM_LINE_SPEED_100MBPS) ? "100" :
    		(pDevice->LineSpeed == LM_LINE_SPEED_10MBPS) ? "10" : na_str)));
	len += sprintf(page+len, "Duplex\t\t\t\t%s\n", 
    		((pDevice->LinkStatus == LM_STATUS_LINK_DOWN) ||
		(pUmDevice->opened == 0)) ? na_str :
		((pDevice->DuplexMode == LM_DUPLEX_MODE_FULL) ? "full" :
			"half"));
	len += sprintf(page+len, "Flow_Control\t\t\t%s\n", 
    		((pDevice->LinkStatus == LM_STATUS_LINK_DOWN) ||
		(pUmDevice->opened == 0)) ? na_str :
		((pDevice->FlowControl == LM_FLOW_CONTROL_NONE) ? off_str :
		(((pDevice->FlowControl & LM_FLOW_CONTROL_RX_TX_PAUSE) ==
			LM_FLOW_CONTROL_RX_TX_PAUSE) ? "receive/transmit" :
		(pDevice->FlowControl & LM_FLOW_CONTROL_RECEIVE_PAUSE) ?
			"receive" : "transmit")));
	len += sprintf(page+len, "State\t\t\t\t%s\n", 
		(pUmDevice->suspended ? "suspended" :
    		((dev->flags & IFF_UP) ? up_str : down_str)));
	len += sprintf(page+len, "MTU_Size\t\t\t%d\n\n", dev->mtu);
	len += sprintf(page+len, "Rx_Packets\t\t\t%lu\n", 
			((pStats == 0) ? 0 :
			MM_GETSTATS(pStats->ifHCInUcastPkts) +
			MM_GETSTATS(pStats->ifHCInMulticastPkts) +
			MM_GETSTATS(pStats->ifHCInBroadcastPkts)));
#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
	if (dev->mtu > 1500) {
		len += sprintf(page+len, "Rx_Jumbo_Packets\t\t%lu\n", 
			((pStats == 0) ? 0 :
			MM_GETSTATS(
				pStats->etherStatsPkts1523Octetsto2047Octets) +
			MM_GETSTATS(
				pStats->etherStatsPkts2048Octetsto4095Octets) +
			MM_GETSTATS(
				pStats->etherStatsPkts4096Octetsto8191Octets) +
			MM_GETSTATS(
				pStats->etherStatsPkts8192Octetsto9022Octets)));
	}
#endif
	len += sprintf(page+len, "Tx_Packets\t\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->ifHCOutUcastPkts) +
		MM_GETSTATS(pStats->ifHCOutMulticastPkts) +
		MM_GETSTATS(pStats->ifHCOutBroadcastPkts)));
#ifdef BCM_TSO
	len += sprintf(page+len, "TSO_Large_Packets\t\t%lu\n",
		pUmDevice->tso_pkt_count);
#endif
	len += sprintf(page+len, "Rx_Bytes\t\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->ifHCInOctets)));
	len += sprintf(page+len, "Tx_Bytes\t\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->ifHCOutOctets)));
	if (pStats == 0) {
		rx_crc_errors = 0;
		rx_align_errors = 0;
		rx_runt_errors = 0;
		rx_frag_errors = 0;
		rx_long_errors = 0;
		rx_overrun_errors = 0;
		rx_jabber_errors = 0;
	}
	else {
		rx_crc_errors = (unsigned long) bcm5700_crc_count(pUmDevice);
		rx_align_errors = MM_GETSTATS(pStats->dot3StatsAlignmentErrors);
		rx_runt_errors = MM_GETSTATS(pStats->etherStatsUndersizePkts);
		rx_frag_errors = MM_GETSTATS(pStats->etherStatsFragments);
		rx_long_errors = MM_GETSTATS(pStats->dot3StatsFramesTooLong);
		rx_overrun_errors = MM_GETSTATS(pStats->nicNoMoreRxBDs);
		rx_jabber_errors = MM_GETSTATS(pStats->etherStatsJabbers);
	}
	rx_mac_errors = (unsigned long) bcm5700_rx_err_count(pUmDevice);
	len += sprintf(page+len, "Rx_Errors\t\t\t%lu\n",
		((pStats == 0) ? 0 :
		rx_mac_errors + rx_overrun_errors + pUmDevice->rx_misc_errors));
	len += sprintf(page+len, "Tx_Errors\t\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->ifOutErrors)));
	len += sprintf(page+len, "\nTx_Carrier_Errors\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->dot3StatsCarrierSenseErrors)));
	len += sprintf(page+len, "Tx_Abort_Excess_Coll\t\t%lu\n",
		((pStats == 0) ? 0 :
    		MM_GETSTATS(pStats->dot3StatsExcessiveCollisions)));
	len += sprintf(page+len, "Tx_Abort_Late_Coll\t\t%lu\n",
		((pStats == 0) ? 0 :
    		MM_GETSTATS(pStats->dot3StatsLateCollisions)));
	len += sprintf(page+len, "Tx_Deferred_Ok\t\t\t%lu\n",
		((pStats == 0) ? 0 :
    		MM_GETSTATS(pStats->dot3StatsDeferredTransmissions)));
	len += sprintf(page+len, "Tx_Single_Coll_Ok\t\t%lu\n",
		((pStats == 0) ? 0 :
    		MM_GETSTATS(pStats->dot3StatsSingleCollisionFrames)));
	len += sprintf(page+len, "Tx_Multi_Coll_Ok\t\t%lu\n",
		((pStats == 0) ? 0 :
    		MM_GETSTATS(pStats->dot3StatsMultipleCollisionFrames)));
	len += sprintf(page+len, "Tx_Total_Coll_Ok\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->etherStatsCollisions)));
	len += sprintf(page+len, "Tx_XON_Pause_Frames\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->outXonSent)));
	len += sprintf(page+len, "Tx_XOFF_Pause_Frames\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->outXoffSent)));
	len += sprintf(page+len, "\nRx_CRC_Errors\t\t\t%lu\n", rx_crc_errors);
	len += sprintf(page+len, "Rx_Short_Fragment_Errors\t%lu\n",
		rx_frag_errors);
	len += sprintf(page+len, "Rx_Short_Length_Errors\t\t%lu\n",
		rx_runt_errors);
	len += sprintf(page+len, "Rx_Long_Length_Errors\t\t%lu\n",
		rx_long_errors);
	len += sprintf(page+len, "Rx_Align_Errors\t\t\t%lu\n",
		rx_align_errors);
	len += sprintf(page+len, "Rx_Overrun_Errors\t\t%lu\n",
		rx_overrun_errors);
	len += sprintf(page+len, "Rx_XON_Pause_Frames\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->xonPauseFramesReceived)));
	len += sprintf(page+len, "Rx_XOFF_Pause_Frames\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->xoffPauseFramesReceived)));
	len += sprintf(page+len, "\nTx_MAC_Errors\t\t\t%lu\n",
		((pStats == 0) ? 0 :
		MM_GETSTATS(pStats->dot3StatsInternalMacTransmitErrors)));
	len += sprintf(page+len, "Rx_MAC_Errors\t\t\t%lu\n\n",
		rx_mac_errors);

	len += sprintf(page+len, "Tx_Checksum\t\t\t%s\n",
		((pDevice->TaskToOffload & LM_TASK_OFFLOAD_TX_TCP_CHECKSUM) ?
		on_str : off_str));
	len += sprintf(page+len, "Rx_Checksum\t\t\t%s\n",
		((pDevice->TaskToOffload & LM_TASK_OFFLOAD_RX_TCP_CHECKSUM) ?
		on_str : off_str));
	len += sprintf(page+len, "Scatter_Gather\t\t\t%s\n",
#if (LINUX_VERSION_CODE >= 0x20400)
		((dev->features & NETIF_F_SG) ? on_str : off_str));
#else
		off_str);
#endif
#ifdef BCM_TSO
	len += sprintf(page+len, "TSO\t\t\t\t%s\n",
		((dev->features & NETIF_F_TSO) ? on_str : off_str));
#endif
	len += sprintf(page+len, "VLAN\t\t\t\t%s\n\n",
		((pDevice->RxMode & RX_MODE_KEEP_VLAN_TAG) ? off_str : on_str));

#ifdef BCM_NIC_SEND_BD
	len += sprintf(page+len, "NIC_Tx_BDs\t\t\t%s\n",
		(pDevice->Flags & NIC_SEND_BD_FLAG) ? on_str : off_str);
#endif
	len += sprintf(page+len, "Tx_Desc_Count\t\t\t%u\n",
		pDevice->TxPacketDescCnt);
	len += sprintf(page+len, "Rx_Desc_Count\t\t\t%u\n",
		pDevice->RxStdDescCnt);
#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
	len += sprintf(page+len, "Rx_Jumbo_Desc_Count\t\t%u\n",
		pDevice->RxJumboDescCnt);
#endif
#ifdef BCM_INT_COAL
	len += sprintf(page+len, "Adaptive_Coalescing\t\t%s\n",
		(pUmDevice->adaptive_coalesce ? on_str : off_str));
	len += sprintf(page+len, "Rx_Coalescing_Ticks\t\t%u\n",
		pUmDevice->rx_curr_coalesce_ticks);
	len += sprintf(page+len, "Rx_Coalesced_Frames\t\t%u\n",
		pUmDevice->rx_curr_coalesce_frames);
	len += sprintf(page+len, "Tx_Coalescing_Ticks\t\t%u\n",
		pDevice->TxCoalescingTicks);
	len += sprintf(page+len, "Tx_Coalesced_Frames\t\t%u\n",
		pUmDevice->tx_curr_coalesce_frames);
	len += sprintf(page+len, "Stats_Coalescing_Ticks\t\t%u\n",
		pDevice->StatsCoalescingTicks);
#endif
#ifdef BCM_WOL
	len += sprintf(page+len, "Wake_On_LAN\t\t\t%s\n",
        	((pDevice->WakeUpMode & LM_WAKE_UP_MODE_MAGIC_PACKET) ?
		on_str : off_str));
#endif
#if TIGON3_DEBUG
	len += sprintf(page+len, "\nDmaReadWriteCtrl\t\t%x\n",
		pDevice->DmaReadWriteCtrl);
	len += sprintf(page+len, "\nTx_Zero_Copy_Packets\t\t%u\n",
		pUmDevice->tx_zc_count);
	len += sprintf(page+len, "Tx_Chksum_Packets\t\t%u\n",
		pUmDevice->tx_chksum_count);
	len += sprintf(page+len, "Tx_Highmem_Fragments\t\t%u\n",
		pUmDevice->tx_himem_count);
	len += sprintf(page+len, "Rx_Good_Chksum_Packets\t\t%u\n",
		pUmDevice->rx_good_chksum_count);
	len += sprintf(page+len, "Rx_Bad_Chksum_Packets\t\t%u\n",
		pUmDevice->rx_bad_chksum_count);
	if (!(pDevice->TbiFlags & ENABLE_TBI_FLAG)) {
		LM_UINT32 value32;
		unsigned long flags;

		BCM5700_PHY_LOCK(pUmDevice, flags);
        	LM_ReadPhy(pDevice, 0, &value32);
		len += sprintf(page+len, "\nPhy_Register_0x00\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 1, &value32);
		len += sprintf(page+len, "Phy_Register_0x01\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 2, &value32);
		len += sprintf(page+len, "Phy_Register_0x02\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 3, &value32);
		len += sprintf(page+len, "Phy_Register_0x03\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 4, &value32);
		len += sprintf(page+len, "Phy_Register_0x04\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 5, &value32);
		len += sprintf(page+len, "Phy_Register_0x05\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 9, &value32);
		len += sprintf(page+len, "Phy_Register_0x09\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 0xa, &value32);
		len += sprintf(page+len, "Phy_Register_0x0A\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 0xf, &value32);
		len += sprintf(page+len, "Phy_Register_0x0F\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 0x10, &value32);
		len += sprintf(page+len, "Phy_Register_0x10\t\t0x%x\n", value32);
        	LM_ReadPhy(pDevice, 0x19, &value32);
		len += sprintf(page+len, "Phy_Register_0x19\t\t0x%x\n", value32);
        	LM_WritePhy(pDevice, 0x18, 0x0007);
        	LM_ReadPhy(pDevice, 0x18, &value32);
		len += sprintf(page+len, "Phy_Register_0x18_00\t\t0x%x\n", value32);
		BCM5700_PHY_UNLOCK(pUmDevice, flags);
	}
#endif

	*eof = 1;
	return len;
}

int
bcm5700_proc_create_dev(struct net_device *dev)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;

	if (!bcm5700_procfs_dir)
		return -1;

	sprintf(pUmDevice->pfs_name, "%s.info", dev->name);
	pUmDevice->pfs_entry = create_proc_entry(pUmDevice->pfs_name,
		S_IFREG, bcm5700_procfs_dir);
	if (pUmDevice->pfs_entry == 0)
		return -1;
	pUmDevice->pfs_entry->read_proc = bcm5700_read_pfs;
	pUmDevice->pfs_entry->data = dev;
	return 0;
}
int
bcm5700_proc_remove_dev(struct net_device *dev)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;

	remove_proc_entry(pUmDevice->pfs_name, bcm5700_procfs_dir);
	return 0;
}

#endif
