linkmode.c (3418B)
1// SPDX-License-Identifier: GPL-2.0+ 2#include <linux/linkmode.h> 3 4/** 5 * linkmode_resolve_pause - resolve the allowable pause modes 6 * @local_adv: local advertisement in ethtool format 7 * @partner_adv: partner advertisement in ethtool format 8 * @tx_pause: pointer to bool to indicate whether transmit pause should be 9 * enabled. 10 * @rx_pause: pointer to bool to indicate whether receive pause should be 11 * enabled. 12 * 13 * Flow control is resolved according to our and the link partners 14 * advertisements using the following drawn from the 802.3 specs: 15 * Local device Link partner 16 * Pause AsymDir Pause AsymDir Result 17 * 0 X 0 X Disabled 18 * 0 1 1 0 Disabled 19 * 0 1 1 1 TX 20 * 1 0 0 X Disabled 21 * 1 X 1 X TX+RX 22 * 1 1 0 1 RX 23 */ 24void linkmode_resolve_pause(const unsigned long *local_adv, 25 const unsigned long *partner_adv, 26 bool *tx_pause, bool *rx_pause) 27{ 28 __ETHTOOL_DECLARE_LINK_MODE_MASK(m); 29 30 linkmode_and(m, local_adv, partner_adv); 31 if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, m)) { 32 *tx_pause = true; 33 *rx_pause = true; 34 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, m)) { 35 *tx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, 36 partner_adv); 37 *rx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, 38 local_adv); 39 } else { 40 *tx_pause = false; 41 *rx_pause = false; 42 } 43} 44EXPORT_SYMBOL_GPL(linkmode_resolve_pause); 45 46/** 47 * linkmode_set_pause - set the pause mode advertisement 48 * @advertisement: advertisement in ethtool format 49 * @tx: boolean from ethtool struct ethtool_pauseparam tx_pause member 50 * @rx: boolean from ethtool struct ethtool_pauseparam rx_pause member 51 * 52 * Configure the advertised Pause and Asym_Pause bits according to the 53 * capabilities of provided in @tx and @rx. 54 * 55 * We convert as follows: 56 * tx rx Pause AsymDir 57 * 0 0 0 0 58 * 0 1 1 1 59 * 1 0 0 1 60 * 1 1 1 0 61 * 62 * Note: this translation from ethtool tx/rx notation to the advertisement 63 * is actually very problematical. Here are some examples: 64 * 65 * For tx=0 rx=1, meaning transmit is unsupported, receive is supported: 66 * 67 * Local device Link partner 68 * Pause AsymDir Pause AsymDir Result 69 * 1 1 1 0 TX + RX - but we have no TX support. 70 * 1 1 0 1 Only this gives RX only 71 * 72 * For tx=1 rx=1, meaning we have the capability to transmit and receive 73 * pause frames: 74 * 75 * Local device Link partner 76 * Pause AsymDir Pause AsymDir Result 77 * 1 0 0 1 Disabled - but since we do support tx and rx, 78 * this should resolve to RX only. 79 * 80 * Hence, asking for: 81 * rx=1 tx=0 gives Pause+AsymDir advertisement, but we may end up 82 * resolving to tx+rx pause or only rx pause depending on 83 * the partners advertisement. 84 * rx=0 tx=1 gives AsymDir only, which will only give tx pause if 85 * the partners advertisement allows it. 86 * rx=1 tx=1 gives Pause only, which will only allow tx+rx pause 87 * if the other end also advertises Pause. 88 */ 89void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx) 90{ 91 linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertisement, rx); 92 linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertisement, 93 rx ^ tx); 94} 95EXPORT_SYMBOL_GPL(linkmode_set_pause);