diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2005-10-03 17:01:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2005-10-03 17:01:37 -0400 |
commit | a232f76732e11c91c2215d3a43cf9ebc7f939939 (patch) | |
tree | e23578fb673d037193178b4a52b38ea5d42164c2 /drivers/net/cassini.c | |
parent | 325ed8239309cb29f10ea58c5a668058ead11479 (diff) |
[CASSINI]: Convert to ethtool_ops
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/cassini.c')
-rw-r--r-- | drivers/net/cassini.c | 502 |
1 files changed, 214 insertions, 288 deletions
diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 45831fb377a0..2e617424d3fb 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c | |||
@@ -4423,18 +4423,14 @@ static struct { | |||
4423 | #define CAS_REG_LEN (sizeof(ethtool_register_table)/sizeof(int)) | 4423 | #define CAS_REG_LEN (sizeof(ethtool_register_table)/sizeof(int)) |
4424 | #define CAS_MAX_REGS (sizeof (u32)*CAS_REG_LEN) | 4424 | #define CAS_MAX_REGS (sizeof (u32)*CAS_REG_LEN) |
4425 | 4425 | ||
4426 | static u8 *cas_get_regs(struct cas *cp) | 4426 | static void cas_read_regs(struct cas *cp, u8 *ptr, int len) |
4427 | { | 4427 | { |
4428 | u8 *ptr = kmalloc(CAS_MAX_REGS, GFP_KERNEL); | ||
4429 | u8 *p; | 4428 | u8 *p; |
4430 | int i; | 4429 | int i; |
4431 | unsigned long flags; | 4430 | unsigned long flags; |
4432 | 4431 | ||
4433 | if (!ptr) | ||
4434 | return NULL; | ||
4435 | |||
4436 | spin_lock_irqsave(&cp->lock, flags); | 4432 | spin_lock_irqsave(&cp->lock, flags); |
4437 | for (i = 0, p = ptr; i < CAS_REG_LEN ; i ++, p += sizeof(u32)) { | 4433 | for (i = 0, p = ptr; i < len ; i ++, p += sizeof(u32)) { |
4438 | u16 hval; | 4434 | u16 hval; |
4439 | u32 val; | 4435 | u32 val; |
4440 | if (ethtool_register_table[i].offsets < 0) { | 4436 | if (ethtool_register_table[i].offsets < 0) { |
@@ -4447,8 +4443,6 @@ static u8 *cas_get_regs(struct cas *cp) | |||
4447 | memcpy(p, (u8 *)&val, sizeof(u32)); | 4443 | memcpy(p, (u8 *)&val, sizeof(u32)); |
4448 | } | 4444 | } |
4449 | spin_unlock_irqrestore(&cp->lock, flags); | 4445 | spin_unlock_irqrestore(&cp->lock, flags); |
4450 | |||
4451 | return ptr; | ||
4452 | } | 4446 | } |
4453 | 4447 | ||
4454 | static struct net_device_stats *cas_get_stats(struct net_device *dev) | 4448 | static struct net_device_stats *cas_get_stats(struct net_device *dev) |
@@ -4561,316 +4555,251 @@ static void cas_set_multicast(struct net_device *dev) | |||
4561 | spin_unlock_irqrestore(&cp->lock, flags); | 4555 | spin_unlock_irqrestore(&cp->lock, flags); |
4562 | } | 4556 | } |
4563 | 4557 | ||
4564 | /* Eventually add support for changing the advertisement | 4558 | static void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) |
4565 | * on autoneg. | 4559 | { |
4566 | */ | 4560 | struct cas *cp = netdev_priv(dev); |
4567 | static int cas_ethtool_ioctl(struct net_device *dev, void __user *ep_user) | 4561 | strncpy(info->driver, DRV_MODULE_NAME, ETHTOOL_BUSINFO_LEN); |
4562 | strncpy(info->version, DRV_MODULE_VERSION, ETHTOOL_BUSINFO_LEN); | ||
4563 | info->fw_version[0] = '\0'; | ||
4564 | strncpy(info->bus_info, pci_name(cp->pdev), ETHTOOL_BUSINFO_LEN); | ||
4565 | info->regdump_len = cp->casreg_len < CAS_MAX_REGS ? | ||
4566 | cp->casreg_len : CAS_MAX_REGS; | ||
4567 | info->n_stats = CAS_NUM_STAT_KEYS; | ||
4568 | } | ||
4569 | |||
4570 | static int cas_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) | ||
4568 | { | 4571 | { |
4569 | struct cas *cp = netdev_priv(dev); | 4572 | struct cas *cp = netdev_priv(dev); |
4570 | u16 bmcr; | 4573 | u16 bmcr; |
4571 | int full_duplex, speed, pause; | 4574 | int full_duplex, speed, pause; |
4572 | struct ethtool_cmd ecmd; | ||
4573 | unsigned long flags; | 4575 | unsigned long flags; |
4574 | enum link_state linkstate = link_up; | 4576 | enum link_state linkstate = link_up; |
4575 | 4577 | ||
4576 | if (copy_from_user(&ecmd, ep_user, sizeof(ecmd))) | 4578 | cmd->advertising = 0; |
4577 | return -EFAULT; | 4579 | cmd->supported = SUPPORTED_Autoneg; |
4578 | 4580 | if (cp->cas_flags & CAS_FLAG_1000MB_CAP) { | |
4579 | switch(ecmd.cmd) { | 4581 | cmd->supported |= SUPPORTED_1000baseT_Full; |
4580 | case ETHTOOL_GDRVINFO: { | 4582 | cmd->advertising |= ADVERTISED_1000baseT_Full; |
4581 | struct ethtool_drvinfo info = { .cmd = ETHTOOL_GDRVINFO }; | ||
4582 | |||
4583 | strncpy(info.driver, DRV_MODULE_NAME, | ||
4584 | ETHTOOL_BUSINFO_LEN); | ||
4585 | strncpy(info.version, DRV_MODULE_VERSION, | ||
4586 | ETHTOOL_BUSINFO_LEN); | ||
4587 | info.fw_version[0] = '\0'; | ||
4588 | strncpy(info.bus_info, pci_name(cp->pdev), | ||
4589 | ETHTOOL_BUSINFO_LEN); | ||
4590 | info.regdump_len = cp->casreg_len < CAS_MAX_REGS ? | ||
4591 | cp->casreg_len : CAS_MAX_REGS; | ||
4592 | info.n_stats = CAS_NUM_STAT_KEYS; | ||
4593 | if (copy_to_user(ep_user, &info, sizeof(info))) | ||
4594 | return -EFAULT; | ||
4595 | |||
4596 | return 0; | ||
4597 | } | 4583 | } |
4598 | 4584 | ||
4599 | case ETHTOOL_GSET: | 4585 | /* Record PHY settings if HW is on. */ |
4600 | ecmd.advertising = 0; | 4586 | spin_lock_irqsave(&cp->lock, flags); |
4601 | ecmd.supported = SUPPORTED_Autoneg; | 4587 | bmcr = 0; |
4602 | if (cp->cas_flags & CAS_FLAG_1000MB_CAP) { | 4588 | linkstate = cp->lstate; |
4603 | ecmd.supported |= SUPPORTED_1000baseT_Full; | 4589 | if (CAS_PHY_MII(cp->phy_type)) { |
4604 | ecmd.advertising |= ADVERTISED_1000baseT_Full; | 4590 | cmd->port = PORT_MII; |
4591 | cmd->transceiver = (cp->cas_flags & CAS_FLAG_SATURN) ? | ||
4592 | XCVR_INTERNAL : XCVR_EXTERNAL; | ||
4593 | cmd->phy_address = cp->phy_addr; | ||
4594 | cmd->advertising |= ADVERTISED_TP | ADVERTISED_MII | | ||
4595 | ADVERTISED_10baseT_Half | | ||
4596 | ADVERTISED_10baseT_Full | | ||
4597 | ADVERTISED_100baseT_Half | | ||
4598 | ADVERTISED_100baseT_Full; | ||
4599 | |||
4600 | cmd->supported |= | ||
4601 | (SUPPORTED_10baseT_Half | | ||
4602 | SUPPORTED_10baseT_Full | | ||
4603 | SUPPORTED_100baseT_Half | | ||
4604 | SUPPORTED_100baseT_Full | | ||
4605 | SUPPORTED_TP | SUPPORTED_MII); | ||
4606 | |||
4607 | if (cp->hw_running) { | ||
4608 | cas_mif_poll(cp, 0); | ||
4609 | bmcr = cas_phy_read(cp, MII_BMCR); | ||
4610 | cas_read_mii_link_mode(cp, &full_duplex, | ||
4611 | &speed, &pause); | ||
4612 | cas_mif_poll(cp, 1); | ||
4605 | } | 4613 | } |
4606 | 4614 | ||
4607 | /* Record PHY settings if HW is on. */ | 4615 | } else { |
4608 | spin_lock_irqsave(&cp->lock, flags); | 4616 | cmd->port = PORT_FIBRE; |
4609 | bmcr = 0; | 4617 | cmd->transceiver = XCVR_INTERNAL; |
4610 | linkstate = cp->lstate; | 4618 | cmd->phy_address = 0; |
4611 | if (CAS_PHY_MII(cp->phy_type)) { | 4619 | cmd->supported |= SUPPORTED_FIBRE; |
4612 | ecmd.port = PORT_MII; | 4620 | cmd->advertising |= ADVERTISED_FIBRE; |
4613 | ecmd.transceiver = (cp->cas_flags & CAS_FLAG_SATURN) ? | 4621 | |
4614 | XCVR_INTERNAL : XCVR_EXTERNAL; | 4622 | if (cp->hw_running) { |
4615 | ecmd.phy_address = cp->phy_addr; | 4623 | /* pcs uses the same bits as mii */ |
4616 | ecmd.advertising |= ADVERTISED_TP | ADVERTISED_MII | | 4624 | bmcr = readl(cp->regs + REG_PCS_MII_CTRL); |
4617 | ADVERTISED_10baseT_Half | | 4625 | cas_read_pcs_link_mode(cp, &full_duplex, |
4618 | ADVERTISED_10baseT_Full | | 4626 | &speed, &pause); |
4619 | ADVERTISED_100baseT_Half | | ||
4620 | ADVERTISED_100baseT_Full; | ||
4621 | |||
4622 | ecmd.supported |= | ||
4623 | (SUPPORTED_10baseT_Half | | ||
4624 | SUPPORTED_10baseT_Full | | ||
4625 | SUPPORTED_100baseT_Half | | ||
4626 | SUPPORTED_100baseT_Full | | ||
4627 | SUPPORTED_TP | SUPPORTED_MII); | ||
4628 | |||
4629 | if (cp->hw_running) { | ||
4630 | cas_mif_poll(cp, 0); | ||
4631 | bmcr = cas_phy_read(cp, MII_BMCR); | ||
4632 | cas_read_mii_link_mode(cp, &full_duplex, | ||
4633 | &speed, &pause); | ||
4634 | cas_mif_poll(cp, 1); | ||
4635 | } | ||
4636 | |||
4637 | } else { | ||
4638 | ecmd.port = PORT_FIBRE; | ||
4639 | ecmd.transceiver = XCVR_INTERNAL; | ||
4640 | ecmd.phy_address = 0; | ||
4641 | ecmd.supported |= SUPPORTED_FIBRE; | ||
4642 | ecmd.advertising |= ADVERTISED_FIBRE; | ||
4643 | |||
4644 | if (cp->hw_running) { | ||
4645 | /* pcs uses the same bits as mii */ | ||
4646 | bmcr = readl(cp->regs + REG_PCS_MII_CTRL); | ||
4647 | cas_read_pcs_link_mode(cp, &full_duplex, | ||
4648 | &speed, &pause); | ||
4649 | } | ||
4650 | } | 4627 | } |
4651 | spin_unlock_irqrestore(&cp->lock, flags); | 4628 | } |
4629 | spin_unlock_irqrestore(&cp->lock, flags); | ||
4652 | 4630 | ||
4653 | if (bmcr & BMCR_ANENABLE) { | 4631 | if (bmcr & BMCR_ANENABLE) { |
4654 | ecmd.advertising |= ADVERTISED_Autoneg; | 4632 | cmd->advertising |= ADVERTISED_Autoneg; |
4655 | ecmd.autoneg = AUTONEG_ENABLE; | 4633 | cmd->autoneg = AUTONEG_ENABLE; |
4656 | ecmd.speed = ((speed == 10) ? | 4634 | cmd->speed = ((speed == 10) ? |
4657 | SPEED_10 : | 4635 | SPEED_10 : |
4658 | ((speed == 1000) ? | 4636 | ((speed == 1000) ? |
4659 | SPEED_1000 : SPEED_100)); | 4637 | SPEED_1000 : SPEED_100)); |
4660 | ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; | 4638 | cmd->duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; |
4639 | } else { | ||
4640 | cmd->autoneg = AUTONEG_DISABLE; | ||
4641 | cmd->speed = | ||
4642 | (bmcr & CAS_BMCR_SPEED1000) ? | ||
4643 | SPEED_1000 : | ||
4644 | ((bmcr & BMCR_SPEED100) ? SPEED_100: | ||
4645 | SPEED_10); | ||
4646 | cmd->duplex = | ||
4647 | (bmcr & BMCR_FULLDPLX) ? | ||
4648 | DUPLEX_FULL : DUPLEX_HALF; | ||
4649 | } | ||
4650 | if (linkstate != link_up) { | ||
4651 | /* Force these to "unknown" if the link is not up and | ||
4652 | * autonogotiation in enabled. We can set the link | ||
4653 | * speed to 0, but not cmd->duplex, | ||
4654 | * because its legal values are 0 and 1. Ethtool will | ||
4655 | * print the value reported in parentheses after the | ||
4656 | * word "Unknown" for unrecognized values. | ||
4657 | * | ||
4658 | * If in forced mode, we report the speed and duplex | ||
4659 | * settings that we configured. | ||
4660 | */ | ||
4661 | if (cp->link_cntl & BMCR_ANENABLE) { | ||
4662 | cmd->speed = 0; | ||
4663 | cmd->duplex = 0xff; | ||
4661 | } else { | 4664 | } else { |
4662 | ecmd.autoneg = AUTONEG_DISABLE; | 4665 | cmd->speed = SPEED_10; |
4663 | ecmd.speed = | 4666 | if (cp->link_cntl & BMCR_SPEED100) { |
4664 | (bmcr & CAS_BMCR_SPEED1000) ? | 4667 | cmd->speed = SPEED_100; |
4665 | SPEED_1000 : | 4668 | } else if (cp->link_cntl & CAS_BMCR_SPEED1000) { |
4666 | ((bmcr & BMCR_SPEED100) ? SPEED_100: | 4669 | cmd->speed = SPEED_1000; |
4667 | SPEED_10); | ||
4668 | ecmd.duplex = | ||
4669 | (bmcr & BMCR_FULLDPLX) ? | ||
4670 | DUPLEX_FULL : DUPLEX_HALF; | ||
4671 | } | ||
4672 | if (linkstate != link_up) { | ||
4673 | /* Force these to "unknown" if the link is not up and | ||
4674 | * autonogotiation in enabled. We can set the link | ||
4675 | * speed to 0, but not ecmd.duplex, | ||
4676 | * because its legal values are 0 and 1. Ethtool will | ||
4677 | * print the value reported in parentheses after the | ||
4678 | * word "Unknown" for unrecognized values. | ||
4679 | * | ||
4680 | * If in forced mode, we report the speed and duplex | ||
4681 | * settings that we configured. | ||
4682 | */ | ||
4683 | if (cp->link_cntl & BMCR_ANENABLE) { | ||
4684 | ecmd.speed = 0; | ||
4685 | ecmd.duplex = 0xff; | ||
4686 | } else { | ||
4687 | ecmd.speed = SPEED_10; | ||
4688 | if (cp->link_cntl & BMCR_SPEED100) { | ||
4689 | ecmd.speed = SPEED_100; | ||
4690 | } else if (cp->link_cntl & CAS_BMCR_SPEED1000) { | ||
4691 | ecmd.speed = SPEED_1000; | ||
4692 | } | ||
4693 | ecmd.duplex = (cp->link_cntl & BMCR_FULLDPLX)? | ||
4694 | DUPLEX_FULL : DUPLEX_HALF; | ||
4695 | } | 4670 | } |
4671 | cmd->duplex = (cp->link_cntl & BMCR_FULLDPLX)? | ||
4672 | DUPLEX_FULL : DUPLEX_HALF; | ||
4696 | } | 4673 | } |
4697 | if (copy_to_user(ep_user, &ecmd, sizeof(ecmd))) | 4674 | } |
4698 | return -EFAULT; | 4675 | return 0; |
4699 | return 0; | 4676 | } |
4700 | 4677 | ||
4701 | case ETHTOOL_SSET: | 4678 | static int cas_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) |
4702 | if (!capable(CAP_NET_ADMIN)) | 4679 | { |
4703 | return -EPERM; | 4680 | struct cas *cp = netdev_priv(dev); |
4681 | unsigned long flags; | ||
4704 | 4682 | ||
4705 | /* Verify the settings we care about. */ | 4683 | /* Verify the settings we care about. */ |
4706 | if (ecmd.autoneg != AUTONEG_ENABLE && | 4684 | if (cmd->autoneg != AUTONEG_ENABLE && |
4707 | ecmd.autoneg != AUTONEG_DISABLE) | 4685 | cmd->autoneg != AUTONEG_DISABLE) |
4708 | return -EINVAL; | 4686 | return -EINVAL; |
4709 | 4687 | ||
4710 | if (ecmd.autoneg == AUTONEG_DISABLE && | 4688 | if (cmd->autoneg == AUTONEG_DISABLE && |
4711 | ((ecmd.speed != SPEED_1000 && | 4689 | ((cmd->speed != SPEED_1000 && |
4712 | ecmd.speed != SPEED_100 && | 4690 | cmd->speed != SPEED_100 && |
4713 | ecmd.speed != SPEED_10) || | 4691 | cmd->speed != SPEED_10) || |
4714 | (ecmd.duplex != DUPLEX_HALF && | 4692 | (cmd->duplex != DUPLEX_HALF && |
4715 | ecmd.duplex != DUPLEX_FULL))) | 4693 | cmd->duplex != DUPLEX_FULL))) |
4716 | return -EINVAL; | 4694 | return -EINVAL; |
4717 | 4695 | ||
4718 | /* Apply settings and restart link process. */ | 4696 | /* Apply settings and restart link process. */ |
4719 | spin_lock_irqsave(&cp->lock, flags); | 4697 | spin_lock_irqsave(&cp->lock, flags); |
4720 | cas_begin_auto_negotiation(cp, &ecmd); | 4698 | cas_begin_auto_negotiation(cp, cmd); |
4721 | spin_unlock_irqrestore(&cp->lock, flags); | 4699 | spin_unlock_irqrestore(&cp->lock, flags); |
4722 | return 0; | 4700 | return 0; |
4701 | } | ||
4723 | 4702 | ||
4724 | case ETHTOOL_NWAY_RST: | 4703 | static int cas_nway_reset(struct net_device *dev) |
4725 | if ((cp->link_cntl & BMCR_ANENABLE) == 0) | 4704 | { |
4726 | return -EINVAL; | 4705 | struct cas *cp = netdev_priv(dev); |
4706 | unsigned long flags; | ||
4727 | 4707 | ||
4728 | /* Restart link process. */ | 4708 | if ((cp->link_cntl & BMCR_ANENABLE) == 0) |
4729 | spin_lock_irqsave(&cp->lock, flags); | 4709 | return -EINVAL; |
4730 | cas_begin_auto_negotiation(cp, NULL); | ||
4731 | spin_unlock_irqrestore(&cp->lock, flags); | ||
4732 | 4710 | ||
4733 | return 0; | 4711 | /* Restart link process. */ |
4712 | spin_lock_irqsave(&cp->lock, flags); | ||
4713 | cas_begin_auto_negotiation(cp, NULL); | ||
4714 | spin_unlock_irqrestore(&cp->lock, flags); | ||
4734 | 4715 | ||
4735 | case ETHTOOL_GWOL: | 4716 | return 0; |
4736 | case ETHTOOL_SWOL: | 4717 | } |
4737 | break; /* doesn't exist */ | ||
4738 | 4718 | ||
4739 | /* get link status */ | 4719 | static u32 cas_get_link(struct net_device *dev) |
4740 | case ETHTOOL_GLINK: { | 4720 | { |
4741 | struct ethtool_value edata = { .cmd = ETHTOOL_GLINK }; | 4721 | struct cas *cp = netdev_priv(dev); |
4722 | return cp->lstate == link_up; | ||
4723 | } | ||
4742 | 4724 | ||
4743 | edata.data = (cp->lstate == link_up); | 4725 | static u32 cas_get_msglevel(struct net_device *dev) |
4744 | if (copy_to_user(ep_user, &edata, sizeof(edata))) | 4726 | { |
4745 | return -EFAULT; | 4727 | struct cas *cp = netdev_priv(dev); |
4746 | return 0; | 4728 | return cp->msg_enable; |
4747 | } | 4729 | } |
4748 | 4730 | ||
4749 | /* get message-level */ | 4731 | static void cas_set_msglevel(struct net_device *dev, u32 value) |
4750 | case ETHTOOL_GMSGLVL: { | 4732 | { |
4751 | struct ethtool_value edata = { .cmd = ETHTOOL_GMSGLVL }; | 4733 | struct cas *cp = netdev_priv(dev); |
4734 | cp->msg_enable = value; | ||
4735 | } | ||
4752 | 4736 | ||
4753 | edata.data = cp->msg_enable; | 4737 | static int cas_get_regs_len(struct net_device *dev) |
4754 | if (copy_to_user(ep_user, &edata, sizeof(edata))) | 4738 | { |
4755 | return -EFAULT; | 4739 | struct cas *cp = netdev_priv(dev); |
4756 | return 0; | 4740 | return cp->casreg_len < CAS_MAX_REGS ? cp->casreg_len: CAS_MAX_REGS; |
4757 | } | 4741 | } |
4758 | 4742 | ||
4759 | /* set message-level */ | 4743 | static void cas_get_regs(struct net_device *dev, struct ethtool_regs *regs, |
4760 | case ETHTOOL_SMSGLVL: { | 4744 | void *p) |
4761 | struct ethtool_value edata; | 4745 | { |
4746 | struct cas *cp = netdev_priv(dev); | ||
4747 | regs->version = 0; | ||
4748 | /* cas_read_regs handles locks (cp->lock). */ | ||
4749 | cas_read_regs(cp, p, regs->len / sizeof(u32)); | ||
4750 | } | ||
4762 | 4751 | ||
4763 | if (!capable(CAP_NET_ADMIN)) { | 4752 | static int cas_get_stats_count(struct net_device *dev) |
4764 | return (-EPERM); | 4753 | { |
4765 | } | 4754 | return CAS_NUM_STAT_KEYS; |
4766 | if (copy_from_user(&edata, ep_user, sizeof(edata))) | 4755 | } |
4767 | return -EFAULT; | ||
4768 | cp->msg_enable = edata.data; | ||
4769 | return 0; | ||
4770 | } | ||
4771 | 4756 | ||
4772 | case ETHTOOL_GREGS: { | 4757 | static void cas_get_strings(struct net_device *dev, u32 stringset, u8 *data) |
4773 | struct ethtool_regs edata; | 4758 | { |
4774 | u8 *ptr; | 4759 | memcpy(data, ðtool_cassini_statnames, |
4775 | int len = cp->casreg_len < CAS_MAX_REGS ? | 4760 | CAS_NUM_STAT_KEYS * ETH_GSTRING_LEN); |
4776 | cp->casreg_len: CAS_MAX_REGS; | 4761 | } |
4777 | |||
4778 | if (copy_from_user(&edata, ep_user, sizeof (edata))) | ||
4779 | return -EFAULT; | ||
4780 | |||
4781 | if (edata.len > len) | ||
4782 | edata.len = len; | ||
4783 | edata.version = 0; | ||
4784 | if (copy_to_user (ep_user, &edata, sizeof(edata))) | ||
4785 | return -EFAULT; | ||
4786 | |||
4787 | /* cas_get_regs handles locks (cp->lock). */ | ||
4788 | ptr = cas_get_regs(cp); | ||
4789 | if (ptr == NULL) | ||
4790 | return -ENOMEM; | ||
4791 | if (copy_to_user(ep_user + sizeof (edata), ptr, edata.len)) | ||
4792 | return -EFAULT; | ||
4793 | |||
4794 | kfree(ptr); | ||
4795 | return (0); | ||
4796 | } | ||
4797 | case ETHTOOL_GSTRINGS: { | ||
4798 | struct ethtool_gstrings edata; | ||
4799 | int len; | ||
4800 | |||
4801 | if (copy_from_user(&edata, ep_user, sizeof(edata))) | ||
4802 | return -EFAULT; | ||
4803 | |||
4804 | len = edata.len; | ||
4805 | switch(edata.string_set) { | ||
4806 | case ETH_SS_STATS: | ||
4807 | edata.len = (len < CAS_NUM_STAT_KEYS) ? | ||
4808 | len : CAS_NUM_STAT_KEYS; | ||
4809 | if (copy_to_user(ep_user, &edata, sizeof(edata))) | ||
4810 | return -EFAULT; | ||
4811 | |||
4812 | if (copy_to_user(ep_user + sizeof(edata), | ||
4813 | ðtool_cassini_statnames, | ||
4814 | (edata.len * ETH_GSTRING_LEN))) | ||
4815 | return -EFAULT; | ||
4816 | return 0; | ||
4817 | default: | ||
4818 | return -EINVAL; | ||
4819 | } | ||
4820 | } | ||
4821 | case ETHTOOL_GSTATS: { | ||
4822 | int i = 0; | ||
4823 | u64 *tmp; | ||
4824 | struct ethtool_stats edata; | ||
4825 | struct net_device_stats *stats; | ||
4826 | int len; | ||
4827 | |||
4828 | if (copy_from_user(&edata, ep_user, sizeof(edata))) | ||
4829 | return -EFAULT; | ||
4830 | |||
4831 | len = edata.n_stats; | ||
4832 | stats = cas_get_stats(cp->dev); | ||
4833 | edata.cmd = ETHTOOL_GSTATS; | ||
4834 | edata.n_stats = (len < CAS_NUM_STAT_KEYS) ? | ||
4835 | len : CAS_NUM_STAT_KEYS; | ||
4836 | if (copy_to_user(ep_user, &edata, sizeof (edata))) | ||
4837 | return -EFAULT; | ||
4838 | |||
4839 | tmp = kmalloc(sizeof(u64)*CAS_NUM_STAT_KEYS, GFP_KERNEL); | ||
4840 | if (tmp) { | ||
4841 | tmp[i++] = stats->collisions; | ||
4842 | tmp[i++] = stats->rx_bytes; | ||
4843 | tmp[i++] = stats->rx_crc_errors; | ||
4844 | tmp[i++] = stats->rx_dropped; | ||
4845 | tmp[i++] = stats->rx_errors; | ||
4846 | tmp[i++] = stats->rx_fifo_errors; | ||
4847 | tmp[i++] = stats->rx_frame_errors; | ||
4848 | tmp[i++] = stats->rx_length_errors; | ||
4849 | tmp[i++] = stats->rx_over_errors; | ||
4850 | tmp[i++] = stats->rx_packets; | ||
4851 | tmp[i++] = stats->tx_aborted_errors; | ||
4852 | tmp[i++] = stats->tx_bytes; | ||
4853 | tmp[i++] = stats->tx_dropped; | ||
4854 | tmp[i++] = stats->tx_errors; | ||
4855 | tmp[i++] = stats->tx_fifo_errors; | ||
4856 | tmp[i++] = stats->tx_packets; | ||
4857 | BUG_ON(i != CAS_NUM_STAT_KEYS); | ||
4858 | |||
4859 | i = copy_to_user(ep_user + sizeof(edata), | ||
4860 | tmp, sizeof(u64)*edata.n_stats); | ||
4861 | kfree(tmp); | ||
4862 | } else { | ||
4863 | return -ENOMEM; | ||
4864 | } | ||
4865 | if (i) | ||
4866 | return -EFAULT; | ||
4867 | return 0; | ||
4868 | } | ||
4869 | } | ||
4870 | 4762 | ||
4871 | return -EOPNOTSUPP; | 4763 | static void cas_get_ethtool_stats(struct net_device *dev, |
4764 | struct ethtool_stats *estats, u64 *data) | ||
4765 | { | ||
4766 | struct cas *cp = netdev_priv(dev); | ||
4767 | struct net_device_stats *stats = cas_get_stats(cp->dev); | ||
4768 | int i = 0; | ||
4769 | data[i++] = stats->collisions; | ||
4770 | data[i++] = stats->rx_bytes; | ||
4771 | data[i++] = stats->rx_crc_errors; | ||
4772 | data[i++] = stats->rx_dropped; | ||
4773 | data[i++] = stats->rx_errors; | ||
4774 | data[i++] = stats->rx_fifo_errors; | ||
4775 | data[i++] = stats->rx_frame_errors; | ||
4776 | data[i++] = stats->rx_length_errors; | ||
4777 | data[i++] = stats->rx_over_errors; | ||
4778 | data[i++] = stats->rx_packets; | ||
4779 | data[i++] = stats->tx_aborted_errors; | ||
4780 | data[i++] = stats->tx_bytes; | ||
4781 | data[i++] = stats->tx_dropped; | ||
4782 | data[i++] = stats->tx_errors; | ||
4783 | data[i++] = stats->tx_fifo_errors; | ||
4784 | data[i++] = stats->tx_packets; | ||
4785 | BUG_ON(i != CAS_NUM_STAT_KEYS); | ||
4872 | } | 4786 | } |
4873 | 4787 | ||
4788 | static struct ethtool_ops cas_ethtool_ops = { | ||
4789 | .get_drvinfo = cas_get_drvinfo, | ||
4790 | .get_settings = cas_get_settings, | ||
4791 | .set_settings = cas_set_settings, | ||
4792 | .nway_reset = cas_nway_reset, | ||
4793 | .get_link = cas_get_link, | ||
4794 | .get_msglevel = cas_get_msglevel, | ||
4795 | .set_msglevel = cas_set_msglevel, | ||
4796 | .get_regs_len = cas_get_regs_len, | ||
4797 | .get_regs = cas_get_regs, | ||
4798 | .get_stats_count = cas_get_stats_count, | ||
4799 | .get_strings = cas_get_strings, | ||
4800 | .get_ethtool_stats = cas_get_ethtool_stats, | ||
4801 | }; | ||
4802 | |||
4874 | static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 4803 | static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
4875 | { | 4804 | { |
4876 | struct cas *cp = netdev_priv(dev); | 4805 | struct cas *cp = netdev_priv(dev); |
@@ -4883,10 +4812,6 @@ static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
4883 | */ | 4812 | */ |
4884 | down(&cp->pm_sem); | 4813 | down(&cp->pm_sem); |
4885 | switch (cmd) { | 4814 | switch (cmd) { |
4886 | case SIOCETHTOOL: | ||
4887 | rc = cas_ethtool_ioctl(dev, ifr->ifr_data); | ||
4888 | break; | ||
4889 | |||
4890 | case SIOCGMIIPHY: /* Get address of MII PHY in use. */ | 4815 | case SIOCGMIIPHY: /* Get address of MII PHY in use. */ |
4891 | data->phy_id = cp->phy_addr; | 4816 | data->phy_id = cp->phy_addr; |
4892 | /* Fallthrough... */ | 4817 | /* Fallthrough... */ |
@@ -5112,6 +5037,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, | |||
5112 | dev->get_stats = cas_get_stats; | 5037 | dev->get_stats = cas_get_stats; |
5113 | dev->set_multicast_list = cas_set_multicast; | 5038 | dev->set_multicast_list = cas_set_multicast; |
5114 | dev->do_ioctl = cas_ioctl; | 5039 | dev->do_ioctl = cas_ioctl; |
5040 | dev->ethtool_ops = &cas_ethtool_ops; | ||
5115 | dev->tx_timeout = cas_tx_timeout; | 5041 | dev->tx_timeout = cas_tx_timeout; |
5116 | dev->watchdog_timeo = CAS_TX_TIMEOUT; | 5042 | dev->watchdog_timeo = CAS_TX_TIMEOUT; |
5117 | dev->change_mtu = cas_change_mtu; | 5043 | dev->change_mtu = cas_change_mtu; |