diff options
author | David Decotigny <decot@google.com> | 2011-04-27 14:32:43 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-04-29 17:03:03 -0400 |
commit | 14ad2513ed5b709e566a853f4b515d91c5d83311 (patch) | |
tree | 19ce2e6deefaa0725533612df85c51b99d2513cb /drivers/net/e1000 | |
parent | fbef7139a8b89a7f49ba1410593ed894b4c8b017 (diff) |
net/igb/e1000/e1000e: more robust ethtool duplex/speed configuration
This makes sure that one cannot request a 99Mbps full-duplex and get a
100Mbps half-duplex configuration in return due to the way the
speed/duplex parameters are handled internally.
Tested: e1000 works
Signed-off-by: David Decotigny <decot@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/e1000')
-rw-r--r-- | drivers/net/e1000/e1000.h | 2 | ||||
-rw-r--r-- | drivers/net/e1000/e1000_ethtool.c | 2 | ||||
-rw-r--r-- | drivers/net/e1000/e1000_main.c | 42 |
3 files changed, 26 insertions, 20 deletions
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index a881dd0093bd..b1b23ddd4eed 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h | |||
@@ -349,7 +349,7 @@ extern int e1000_up(struct e1000_adapter *adapter); | |||
349 | extern void e1000_down(struct e1000_adapter *adapter); | 349 | extern void e1000_down(struct e1000_adapter *adapter); |
350 | extern void e1000_reinit_locked(struct e1000_adapter *adapter); | 350 | extern void e1000_reinit_locked(struct e1000_adapter *adapter); |
351 | extern void e1000_reset(struct e1000_adapter *adapter); | 351 | extern void e1000_reset(struct e1000_adapter *adapter); |
352 | extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx); | 352 | extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx); |
353 | extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); | 353 | extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); |
354 | extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); | 354 | extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); |
355 | extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter); | 355 | extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter); |
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 127fef4fce49..4fa727ce8374 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c | |||
@@ -199,7 +199,7 @@ static int e1000_set_settings(struct net_device *netdev, | |||
199 | ecmd->advertising = hw->autoneg_advertised; | 199 | ecmd->advertising = hw->autoneg_advertised; |
200 | } else { | 200 | } else { |
201 | u32 speed = ethtool_cmd_speed(ecmd); | 201 | u32 speed = ethtool_cmd_speed(ecmd); |
202 | if (e1000_set_spd_dplx(adapter, speed + ecmd->duplex)) { | 202 | if (e1000_set_spd_dplx(adapter, speed, ecmd->duplex)) { |
203 | clear_bit(__E1000_RESETTING, &adapter->flags); | 203 | clear_bit(__E1000_RESETTING, &adapter->flags); |
204 | return -EINVAL; | 204 | return -EINVAL; |
205 | } | 205 | } |
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 477e066a1cf0..c18cb8e883dd 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c | |||
@@ -96,7 +96,6 @@ int e1000_up(struct e1000_adapter *adapter); | |||
96 | void e1000_down(struct e1000_adapter *adapter); | 96 | void e1000_down(struct e1000_adapter *adapter); |
97 | void e1000_reinit_locked(struct e1000_adapter *adapter); | 97 | void e1000_reinit_locked(struct e1000_adapter *adapter); |
98 | void e1000_reset(struct e1000_adapter *adapter); | 98 | void e1000_reset(struct e1000_adapter *adapter); |
99 | int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx); | ||
100 | int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); | 99 | int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); |
101 | int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); | 100 | int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); |
102 | void e1000_free_all_tx_resources(struct e1000_adapter *adapter); | 101 | void e1000_free_all_tx_resources(struct e1000_adapter *adapter); |
@@ -4385,7 +4384,6 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, | |||
4385 | struct mii_ioctl_data *data = if_mii(ifr); | 4384 | struct mii_ioctl_data *data = if_mii(ifr); |
4386 | int retval; | 4385 | int retval; |
4387 | u16 mii_reg; | 4386 | u16 mii_reg; |
4388 | u16 spddplx; | ||
4389 | unsigned long flags; | 4387 | unsigned long flags; |
4390 | 4388 | ||
4391 | if (hw->media_type != e1000_media_type_copper) | 4389 | if (hw->media_type != e1000_media_type_copper) |
@@ -4424,17 +4422,18 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, | |||
4424 | hw->autoneg = 1; | 4422 | hw->autoneg = 1; |
4425 | hw->autoneg_advertised = 0x2F; | 4423 | hw->autoneg_advertised = 0x2F; |
4426 | } else { | 4424 | } else { |
4425 | u32 speed; | ||
4427 | if (mii_reg & 0x40) | 4426 | if (mii_reg & 0x40) |
4428 | spddplx = SPEED_1000; | 4427 | speed = SPEED_1000; |
4429 | else if (mii_reg & 0x2000) | 4428 | else if (mii_reg & 0x2000) |
4430 | spddplx = SPEED_100; | 4429 | speed = SPEED_100; |
4431 | else | 4430 | else |
4432 | spddplx = SPEED_10; | 4431 | speed = SPEED_10; |
4433 | spddplx += (mii_reg & 0x100) | 4432 | retval = e1000_set_spd_dplx( |
4434 | ? DUPLEX_FULL : | 4433 | adapter, speed, |
4435 | DUPLEX_HALF; | 4434 | ((mii_reg & 0x100) |
4436 | retval = e1000_set_spd_dplx(adapter, | 4435 | ? DUPLEX_FULL : |
4437 | spddplx); | 4436 | DUPLEX_HALF)); |
4438 | if (retval) | 4437 | if (retval) |
4439 | return retval; | 4438 | return retval; |
4440 | } | 4439 | } |
@@ -4596,20 +4595,24 @@ static void e1000_restore_vlan(struct e1000_adapter *adapter) | |||
4596 | } | 4595 | } |
4597 | } | 4596 | } |
4598 | 4597 | ||
4599 | int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) | 4598 | int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx) |
4600 | { | 4599 | { |
4601 | struct e1000_hw *hw = &adapter->hw; | 4600 | struct e1000_hw *hw = &adapter->hw; |
4602 | 4601 | ||
4603 | hw->autoneg = 0; | 4602 | hw->autoneg = 0; |
4604 | 4603 | ||
4604 | /* Make sure dplx is at most 1 bit and lsb of speed is not set | ||
4605 | * for the switch() below to work */ | ||
4606 | if ((spd & 1) || (dplx & ~1)) | ||
4607 | goto err_inval; | ||
4608 | |||
4605 | /* Fiber NICs only allow 1000 gbps Full duplex */ | 4609 | /* Fiber NICs only allow 1000 gbps Full duplex */ |
4606 | if ((hw->media_type == e1000_media_type_fiber) && | 4610 | if ((hw->media_type == e1000_media_type_fiber) && |
4607 | spddplx != (SPEED_1000 + DUPLEX_FULL)) { | 4611 | spd != SPEED_1000 && |
4608 | e_err(probe, "Unsupported Speed/Duplex configuration\n"); | 4612 | dplx != DUPLEX_FULL) |
4609 | return -EINVAL; | 4613 | goto err_inval; |
4610 | } | ||
4611 | 4614 | ||
4612 | switch (spddplx) { | 4615 | switch (spd + dplx) { |
4613 | case SPEED_10 + DUPLEX_HALF: | 4616 | case SPEED_10 + DUPLEX_HALF: |
4614 | hw->forced_speed_duplex = e1000_10_half; | 4617 | hw->forced_speed_duplex = e1000_10_half; |
4615 | break; | 4618 | break; |
@@ -4628,10 +4631,13 @@ int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) | |||
4628 | break; | 4631 | break; |
4629 | case SPEED_1000 + DUPLEX_HALF: /* not supported */ | 4632 | case SPEED_1000 + DUPLEX_HALF: /* not supported */ |
4630 | default: | 4633 | default: |
4631 | e_err(probe, "Unsupported Speed/Duplex configuration\n"); | 4634 | goto err_inval; |
4632 | return -EINVAL; | ||
4633 | } | 4635 | } |
4634 | return 0; | 4636 | return 0; |
4637 | |||
4638 | err_inval: | ||
4639 | e_err(probe, "Unsupported Speed/Duplex configuration\n"); | ||
4640 | return -EINVAL; | ||
4635 | } | 4641 | } |
4636 | 4642 | ||
4637 | static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) | 4643 | static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) |