#include "r1000.h"

extern int R1000_READ_GMII_REG(unsigned long ioaddr, int RegAddr);
extern int R1000_WRITE_GMII_REG(unsigned long ioaddr, int RegAddr, int value);
extern int r1000_set_medium(struct net_device *netdev,u16 speed,u8 duplex,u8 autoneg);

static int ethtool_get_settings(struct net_device *netdev,struct ethtool_cmd *ecmd){
	struct r1000_private *priv = (struct r1000_private *)(netdev->priv);
	unsigned long ioaddr = priv->ioaddr;
	unsigned int bmcr = R1000_READ_GMII_REG(ioaddr,PHY_STAT_REG);
	unsigned int bmsr = R1000_READ_GMII_REG(ioaddr,PHY_AUTO_NEGO_REG);
	unsigned int gbcr = R1000_READ_GMII_REG(ioaddr,PHY_1000_CTRL_REG);

	ecmd->supported = (SUPPORTED_10baseT_Half|
		SUPPORTED_10baseT_Full|
		SUPPORTED_100baseT_Half|
		SUPPORTED_100baseT_Full|
		SUPPORTED_1000baseT_Full|
		SUPPORTED_Autoneg|
		SUPPORTED_TP);

	ecmd->advertising = ADVERTISED_TP;

	if(bmsr&ADVERTISE_10HALF)
		ecmd->advertising |= ADVERTISED_10baseT_Half;
	if(bmsr&ADVERTISE_10FULL)
		ecmd->advertising |= ADVERTISED_10baseT_Full;
	if(bmsr&ADVERTISE_100HALF)
		ecmd->advertising |= ADVERTISED_100baseT_Half;
	if(bmsr&ADVERTISE_100FULL)
		ecmd->advertising |= ADVERTISED_100baseT_Full;
	if(gbcr&PHY_Cap_1000_Full)
		ecmd->advertising |= ADVERTISED_1000baseT_Full;
	if(bmcr&PHY_Enable_Auto_Nego)
		ecmd->advertising |= ADVERTISED_Autoneg;

	ecmd->port = PORT_TP;

	if(priv->mcfg == MCFG_METHOD_1)
		ecmd->transceiver = XCVR_EXTERNAL;
	else
		ecmd->transceiver = XCVR_INTERNAL;

	if(RTL_R8(PHYstatus)&LinkStatus){
		if(RTL_R8(PHYstatus)&_1000Mbps)
			ecmd->speed = SPEED_1000;
		else if(RTL_R8(PHYstatus)&_100Mbps)
			ecmd->speed = SPEED_100;
		else if(RTL_R8(PHYstatus)&_10Mbps)
			ecmd->speed = SPEED_10;
		
		if(RTL_R8(PHYstatus)&FullDup)
			ecmd->duplex = DUPLEX_FULL;
		else
			ecmd->duplex = DUPLEX_HALF;
		
	}else{
		ecmd->speed = -1;
		ecmd->duplex = -1;
	}
	ecmd->autoneg = AUTONEG_ENABLE;
	return 0;
}

static int ethtool_set_settings(struct net_device *netdev,struct ethtool_cmd *ecmd){
	return r1000_set_medium(netdev,ecmd->speed,ecmd->duplex,ecmd->autoneg);
}

static void ethtool_get_drvinfo(struct net_device *netdev,struct ethtool_drvinfo *drvinfo){
	struct r1000_private *priv = netdev_priv(netdev);

	strncpy(drvinfo->driver, MODULENAME,32);
	strncpy(drvinfo->version, R1000_VERSION,32);
	strncpy(drvinfo->fw_version, "N/A", 32);
	strncpy(drvinfo->bus_info, pci_name(priv->pci_dev), 32);
}

static int ethtool_nway_reset(struct net_device *netdev){
//	struct r1000_private *priv = netdev_priv(netdev);
	
	if(netif_running(netdev)){
	}
		
	return 0;
}

uint32_t ethtool_get_link(struct net_device *netdev){
	return netif_carrier_ok(netdev) ? 1 : 0;
}

int ethtool_ioctl(struct ifreq *ifr){
	struct net_device *netdev=__dev_get_by_name(ifr->ifr_name);
	void *useraddr=(void *)ifr->ifr_data;
	uint32_t ethcmd;

	if(!capable(CAP_NET_ADMIN))
		return -EPERM;

	if(!netdev || !netif_device_present(netdev))
		return -ENODEV;
	if(copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
		return -EFAULT;
	
	switch (ethcmd){
	case ETHTOOL_GSET:
		return ethtool_get_settings(netdev,useraddr);
	case ETHTOOL_SSET:
		return ethtool_set_settings(netdev,useraddr);
	case ETHTOOL_GDRVINFO:
		ethtool_get_drvinfo(netdev,useraddr);
	case ETHTOOL_NWAY_RST:
		return ethtool_nway_reset(netdev);
	case ETHTOOL_GLINK:
		return ethtool_get_link(netdev);
	default:
		return -EOPNOTSUPP;
	}
	return -EOPNOTSUPP;
}

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
struct ethtool_ops r1000_ethtool_ops = {
	.get_settings	= ethtool_get_settings,
	.set_settings	= ethtool_set_settings,
	.get_drvinfo	= ethtool_get_drvinfo,
	.nway_reset	= ethtool_nway_reset,
	.get_link	= ethtool_get_link,
};
#endif
