Commit 9e330bf4 authored by David S. Miller's avatar David S. Miller

Merge branch 'ethtool-master-slave'

Oleksij Rempel says:

====================
provide support for PHY master/slave configuration

changes v6:
- use NL_SET_ERR_MSG_ATTR in ethnl_update_linkmodes
- add sanity checks in the ioctl interface
- use bool for ethnl_validate_master_slave_cfg()

changes v5:
- set MASTER_SLAVE_CFG_UNSUPPORTED as default value
- send a netlink error message on validation error
- more code fixes

changes v4:
- rename port_mode to master_slave
- move validation code to net/ethtool/linkmodes.c
- add UNSUPPORTED state and avoid sending unsupported fields
- more formatting and naming fixes
- tja11xx: support only force mode
- tja11xx: mark state as unsupported

changes v3:
- provide separate field for config and state.
- make state rejected on set
- add validation

changes v2:
- change names. Use MASTER_PREFERRED instead of MULTIPORT
- configure master/slave only on request. Default configuration can be
  provided by PHY or eeprom
- status and configuration to the user space.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3d59a583 b883e47b
...@@ -392,14 +392,16 @@ Request contents: ...@@ -392,14 +392,16 @@ Request contents:
Kernel response contents: Kernel response contents:
==================================== ====== ========================== ========================================== ====== ==========================
``ETHTOOL_A_LINKMODES_HEADER`` nested reply header ``ETHTOOL_A_LINKMODES_HEADER`` nested reply header
``ETHTOOL_A_LINKMODES_AUTONEG`` u8 autonegotiation status ``ETHTOOL_A_LINKMODES_AUTONEG`` u8 autonegotiation status
``ETHTOOL_A_LINKMODES_OURS`` bitset advertised link modes ``ETHTOOL_A_LINKMODES_OURS`` bitset advertised link modes
``ETHTOOL_A_LINKMODES_PEER`` bitset partner link modes ``ETHTOOL_A_LINKMODES_PEER`` bitset partner link modes
``ETHTOOL_A_LINKMODES_SPEED`` u32 link speed (Mb/s) ``ETHTOOL_A_LINKMODES_SPEED`` u32 link speed (Mb/s)
``ETHTOOL_A_LINKMODES_DUPLEX`` u8 duplex mode ``ETHTOOL_A_LINKMODES_DUPLEX`` u8 duplex mode
==================================== ====== ========================== ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG`` u8 Master/slave port mode
``ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE`` u8 Master/slave port state
========================================== ====== ==========================
For ``ETHTOOL_A_LINKMODES_OURS``, value represents advertised modes and mask For ``ETHTOOL_A_LINKMODES_OURS``, value represents advertised modes and mask
represents supported modes. ``ETHTOOL_A_LINKMODES_PEER`` in the reply is a bit represents supported modes. ``ETHTOOL_A_LINKMODES_PEER`` in the reply is a bit
...@@ -414,14 +416,15 @@ LINKMODES_SET ...@@ -414,14 +416,15 @@ LINKMODES_SET
Request contents: Request contents:
==================================== ====== ========================== ========================================== ====== ==========================
``ETHTOOL_A_LINKMODES_HEADER`` nested request header ``ETHTOOL_A_LINKMODES_HEADER`` nested request header
``ETHTOOL_A_LINKMODES_AUTONEG`` u8 autonegotiation status ``ETHTOOL_A_LINKMODES_AUTONEG`` u8 autonegotiation status
``ETHTOOL_A_LINKMODES_OURS`` bitset advertised link modes ``ETHTOOL_A_LINKMODES_OURS`` bitset advertised link modes
``ETHTOOL_A_LINKMODES_PEER`` bitset partner link modes ``ETHTOOL_A_LINKMODES_PEER`` bitset partner link modes
``ETHTOOL_A_LINKMODES_SPEED`` u32 link speed (Mb/s) ``ETHTOOL_A_LINKMODES_SPEED`` u32 link speed (Mb/s)
``ETHTOOL_A_LINKMODES_DUPLEX`` u8 duplex mode ``ETHTOOL_A_LINKMODES_DUPLEX`` u8 duplex mode
==================================== ====== ========================== ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG`` u8 Master/slave port mode
========================================== ====== ==========================
``ETHTOOL_A_LINKMODES_OURS`` bit set allows setting advertised link modes. If ``ETHTOOL_A_LINKMODES_OURS`` bit set allows setting advertised link modes. If
autonegotiation is on (either set now or kept from before), advertised modes autonegotiation is on (either set now or kept from before), advertised modes
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#define MII_ECTRL_WAKE_REQUEST BIT(0) #define MII_ECTRL_WAKE_REQUEST BIT(0)
#define MII_CFG1 18 #define MII_CFG1 18
#define MII_CFG1_MASTER_SLAVE BIT(15)
#define MII_CFG1_AUTO_OP BIT(14) #define MII_CFG1_AUTO_OP BIT(14)
#define MII_CFG1_SLEEP_CONFIRM BIT(6) #define MII_CFG1_SLEEP_CONFIRM BIT(6)
#define MII_CFG1_LED_MODE_MASK GENMASK(5, 4) #define MII_CFG1_LED_MODE_MASK GENMASK(5, 4)
...@@ -167,6 +168,32 @@ static int tja11xx_soft_reset(struct phy_device *phydev) ...@@ -167,6 +168,32 @@ static int tja11xx_soft_reset(struct phy_device *phydev)
return genphy_soft_reset(phydev); return genphy_soft_reset(phydev);
} }
static int tja11xx_config_aneg(struct phy_device *phydev)
{
u16 ctl = 0;
int ret;
switch (phydev->master_slave_set) {
case MASTER_SLAVE_CFG_MASTER_FORCE:
ctl |= MII_CFG1_MASTER_SLAVE;
break;
case MASTER_SLAVE_CFG_SLAVE_FORCE:
break;
case MASTER_SLAVE_CFG_UNKNOWN:
case MASTER_SLAVE_CFG_UNSUPPORTED:
return 0;
default:
phydev_warn(phydev, "Unsupported Master/Slave mode\n");
return -ENOTSUPP;
}
ret = phy_modify_changed(phydev, MII_CFG1, MII_CFG1_MASTER_SLAVE, ctl);
if (ret < 0)
return ret;
return __genphy_config_aneg(phydev, ret);
}
static int tja11xx_config_init(struct phy_device *phydev) static int tja11xx_config_init(struct phy_device *phydev)
{ {
int ret; int ret;
...@@ -224,10 +251,22 @@ static int tja11xx_read_status(struct phy_device *phydev) ...@@ -224,10 +251,22 @@ static int tja11xx_read_status(struct phy_device *phydev)
{ {
int ret; int ret;
phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
ret = genphy_update_link(phydev); ret = genphy_update_link(phydev);
if (ret) if (ret)
return ret; return ret;
ret = phy_read(phydev, MII_CFG1);
if (ret < 0)
return ret;
if (ret & MII_CFG1_MASTER_SLAVE)
phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
else
phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
if (phydev->link) { if (phydev->link) {
ret = phy_read(phydev, MII_COMMSTAT); ret = phy_read(phydev, MII_COMMSTAT);
if (ret < 0) if (ret < 0)
...@@ -504,6 +543,7 @@ static struct phy_driver tja11xx_driver[] = { ...@@ -504,6 +543,7 @@ static struct phy_driver tja11xx_driver[] = {
.features = PHY_BASIC_T1_FEATURES, .features = PHY_BASIC_T1_FEATURES,
.probe = tja11xx_probe, .probe = tja11xx_probe,
.soft_reset = tja11xx_soft_reset, .soft_reset = tja11xx_soft_reset,
.config_aneg = tja11xx_config_aneg,
.config_init = tja11xx_config_init, .config_init = tja11xx_config_init,
.read_status = tja11xx_read_status, .read_status = tja11xx_read_status,
.suspend = genphy_suspend, .suspend = genphy_suspend,
...@@ -519,6 +559,7 @@ static struct phy_driver tja11xx_driver[] = { ...@@ -519,6 +559,7 @@ static struct phy_driver tja11xx_driver[] = {
.features = PHY_BASIC_T1_FEATURES, .features = PHY_BASIC_T1_FEATURES,
.probe = tja11xx_probe, .probe = tja11xx_probe,
.soft_reset = tja11xx_soft_reset, .soft_reset = tja11xx_soft_reset,
.config_aneg = tja11xx_config_aneg,
.config_init = tja11xx_config_init, .config_init = tja11xx_config_init,
.read_status = tja11xx_read_status, .read_status = tja11xx_read_status,
.suspend = genphy_suspend, .suspend = genphy_suspend,
...@@ -533,6 +574,7 @@ static struct phy_driver tja11xx_driver[] = { ...@@ -533,6 +574,7 @@ static struct phy_driver tja11xx_driver[] = {
.features = PHY_BASIC_T1_FEATURES, .features = PHY_BASIC_T1_FEATURES,
.probe = tja1102_p0_probe, .probe = tja1102_p0_probe,
.soft_reset = tja11xx_soft_reset, .soft_reset = tja11xx_soft_reset,
.config_aneg = tja11xx_config_aneg,
.config_init = tja11xx_config_init, .config_init = tja11xx_config_init,
.read_status = tja11xx_read_status, .read_status = tja11xx_read_status,
.match_phy_device = tja1102_p0_match_phy_device, .match_phy_device = tja1102_p0_match_phy_device,
...@@ -551,6 +593,7 @@ static struct phy_driver tja11xx_driver[] = { ...@@ -551,6 +593,7 @@ static struct phy_driver tja11xx_driver[] = {
.features = PHY_BASIC_T1_FEATURES, .features = PHY_BASIC_T1_FEATURES,
/* currently no probe for Port 1 is need */ /* currently no probe for Port 1 is need */
.soft_reset = tja11xx_soft_reset, .soft_reset = tja11xx_soft_reset,
.config_aneg = tja11xx_config_aneg,
.config_init = tja11xx_config_init, .config_init = tja11xx_config_init,
.read_status = tja11xx_read_status, .read_status = tja11xx_read_status,
.match_phy_device = tja1102_p1_match_phy_device, .match_phy_device = tja1102_p1_match_phy_device,
......
...@@ -295,7 +295,7 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev, ...@@ -295,7 +295,7 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
phydev->advertising, autoneg == AUTONEG_ENABLE); phydev->advertising, autoneg == AUTONEG_ENABLE);
phydev->duplex = duplex; phydev->duplex = duplex;
phydev->master_slave_set = cmd->base.master_slave_cfg;
phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl; phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl;
/* Restart the PHY */ /* Restart the PHY */
...@@ -314,6 +314,8 @@ void phy_ethtool_ksettings_get(struct phy_device *phydev, ...@@ -314,6 +314,8 @@ void phy_ethtool_ksettings_get(struct phy_device *phydev,
cmd->base.speed = phydev->speed; cmd->base.speed = phydev->speed;
cmd->base.duplex = phydev->duplex; cmd->base.duplex = phydev->duplex;
cmd->base.master_slave_cfg = phydev->master_slave_get;
cmd->base.master_slave_state = phydev->master_slave_state;
if (phydev->interface == PHY_INTERFACE_MODE_MOCA) if (phydev->interface == PHY_INTERFACE_MODE_MOCA)
cmd->base.port = PORT_BNC; cmd->base.port = PORT_BNC;
else else
......
...@@ -1913,6 +1913,90 @@ int genphy_setup_forced(struct phy_device *phydev) ...@@ -1913,6 +1913,90 @@ int genphy_setup_forced(struct phy_device *phydev)
} }
EXPORT_SYMBOL(genphy_setup_forced); EXPORT_SYMBOL(genphy_setup_forced);
static int genphy_setup_master_slave(struct phy_device *phydev)
{
u16 ctl = 0;
if (!phydev->is_gigabit_capable)
return 0;
switch (phydev->master_slave_set) {
case MASTER_SLAVE_CFG_MASTER_PREFERRED:
ctl |= CTL1000_PREFER_MASTER;
break;
case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
break;
case MASTER_SLAVE_CFG_MASTER_FORCE:
ctl |= CTL1000_AS_MASTER;
/* fallthrough */
case MASTER_SLAVE_CFG_SLAVE_FORCE:
ctl |= CTL1000_ENABLE_MASTER;
break;
case MASTER_SLAVE_CFG_UNKNOWN:
case MASTER_SLAVE_CFG_UNSUPPORTED:
return 0;
default:
phydev_warn(phydev, "Unsupported Master/Slave mode\n");
return -EOPNOTSUPP;
}
return phy_modify_changed(phydev, MII_CTRL1000,
(CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
CTL1000_PREFER_MASTER), ctl);
}
static int genphy_read_master_slave(struct phy_device *phydev)
{
int cfg, state;
u16 val;
if (!phydev->is_gigabit_capable) {
phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
return 0;
}
phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
val = phy_read(phydev, MII_CTRL1000);
if (val < 0)
return val;
if (val & CTL1000_ENABLE_MASTER) {
if (val & CTL1000_AS_MASTER)
cfg = MASTER_SLAVE_CFG_MASTER_FORCE;
else
cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
} else {
if (val & CTL1000_PREFER_MASTER)
cfg = MASTER_SLAVE_CFG_MASTER_PREFERRED;
else
cfg = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
}
val = phy_read(phydev, MII_STAT1000);
if (val < 0)
return val;
if (val & LPA_1000MSFAIL) {
state = MASTER_SLAVE_STATE_ERR;
} else if (phydev->link) {
/* this bits are valid only for active link */
if (val & LPA_1000MSRES)
state = MASTER_SLAVE_STATE_MASTER;
else
state = MASTER_SLAVE_STATE_SLAVE;
} else {
state = MASTER_SLAVE_STATE_UNKNOWN;
}
phydev->master_slave_get = cfg;
phydev->master_slave_state = state;
return 0;
}
/** /**
* genphy_restart_aneg - Enable and Restart Autonegotiation * genphy_restart_aneg - Enable and Restart Autonegotiation
* @phydev: target phy_device struct * @phydev: target phy_device struct
...@@ -1971,6 +2055,12 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed) ...@@ -1971,6 +2055,12 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed)
if (genphy_config_eee_advert(phydev)) if (genphy_config_eee_advert(phydev))
changed = true; changed = true;
err = genphy_setup_master_slave(phydev);
if (err < 0)
return err;
else if (err)
changed = true;
if (AUTONEG_ENABLE != phydev->autoneg) if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev); return genphy_setup_forced(phydev);
...@@ -2205,6 +2295,10 @@ int genphy_read_status(struct phy_device *phydev) ...@@ -2205,6 +2295,10 @@ int genphy_read_status(struct phy_device *phydev)
phydev->pause = 0; phydev->pause = 0;
phydev->asym_pause = 0; phydev->asym_pause = 0;
err = genphy_read_master_slave(phydev);
if (err < 0)
return err;
err = genphy_read_lpa(phydev); err = genphy_read_lpa(phydev);
if (err < 0) if (err < 0)
return err; return err;
......
...@@ -477,6 +477,9 @@ struct phy_device { ...@@ -477,6 +477,9 @@ struct phy_device {
int duplex; int duplex;
int pause; int pause;
int asym_pause; int asym_pause;
u8 master_slave_get;
u8 master_slave_set;
u8 master_slave_state;
/* Union of PHY and Attached devices' supported link modes */ /* Union of PHY and Attached devices' supported link modes */
/* See ethtool.h for more info */ /* See ethtool.h for more info */
......
...@@ -1666,6 +1666,18 @@ static inline int ethtool_validate_duplex(__u8 duplex) ...@@ -1666,6 +1666,18 @@ static inline int ethtool_validate_duplex(__u8 duplex)
return 0; return 0;
} }
#define MASTER_SLAVE_CFG_UNSUPPORTED 0
#define MASTER_SLAVE_CFG_UNKNOWN 1
#define MASTER_SLAVE_CFG_MASTER_PREFERRED 2
#define MASTER_SLAVE_CFG_SLAVE_PREFERRED 3
#define MASTER_SLAVE_CFG_MASTER_FORCE 4
#define MASTER_SLAVE_CFG_SLAVE_FORCE 5
#define MASTER_SLAVE_STATE_UNSUPPORTED 0
#define MASTER_SLAVE_STATE_UNKNOWN 1
#define MASTER_SLAVE_STATE_MASTER 2
#define MASTER_SLAVE_STATE_SLAVE 3
#define MASTER_SLAVE_STATE_ERR 4
/* Which connector port. */ /* Which connector port. */
#define PORT_TP 0x00 #define PORT_TP 0x00
#define PORT_AUI 0x01 #define PORT_AUI 0x01
...@@ -1904,7 +1916,9 @@ struct ethtool_link_settings { ...@@ -1904,7 +1916,9 @@ struct ethtool_link_settings {
__u8 eth_tp_mdix_ctrl; __u8 eth_tp_mdix_ctrl;
__s8 link_mode_masks_nwords; __s8 link_mode_masks_nwords;
__u8 transceiver; __u8 transceiver;
__u8 reserved1[3]; __u8 master_slave_cfg;
__u8 master_slave_state;
__u8 reserved1[1];
__u32 reserved[7]; __u32 reserved[7];
__u32 link_mode_masks[0]; __u32 link_mode_masks[0];
/* layout of link_mode_masks fields: /* layout of link_mode_masks fields:
......
...@@ -216,6 +216,8 @@ enum { ...@@ -216,6 +216,8 @@ enum {
ETHTOOL_A_LINKMODES_PEER, /* bitset */ ETHTOOL_A_LINKMODES_PEER, /* bitset */
ETHTOOL_A_LINKMODES_SPEED, /* u32 */ ETHTOOL_A_LINKMODES_SPEED, /* u32 */
ETHTOOL_A_LINKMODES_DUPLEX, /* u8 */ ETHTOOL_A_LINKMODES_DUPLEX, /* u8 */
ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG, /* u8 */
ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE, /* u8 */
/* add new constants above here */ /* add new constants above here */
__ETHTOOL_A_LINKMODES_CNT, __ETHTOOL_A_LINKMODES_CNT,
......
...@@ -151,11 +151,13 @@ ...@@ -151,11 +151,13 @@
/* 1000BASE-T Control register */ /* 1000BASE-T Control register */
#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ #define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */
#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ #define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */
#define CTL1000_PREFER_MASTER 0x0400 /* prefer to operate as master */
#define CTL1000_AS_MASTER 0x0800 #define CTL1000_AS_MASTER 0x0800
#define CTL1000_ENABLE_MASTER 0x1000 #define CTL1000_ENABLE_MASTER 0x1000
/* 1000BASE-T Status register */ /* 1000BASE-T Status register */
#define LPA_1000MSFAIL 0x8000 /* Master/Slave resolution failure */ #define LPA_1000MSFAIL 0x8000 /* Master/Slave resolution failure */
#define LPA_1000MSRES 0x4000 /* Master/Slave resolution status */
#define LPA_1000LOCALRXOK 0x2000 /* Link partner local receiver status */ #define LPA_1000LOCALRXOK 0x2000 /* Link partner local receiver status */
#define LPA_1000REMRXOK 0x1000 /* Link partner remote receiver status */ #define LPA_1000REMRXOK 0x1000 /* Link partner remote receiver status */
#define LPA_1000FULL 0x0800 /* Link partner 1000BASE-T full duplex */ #define LPA_1000FULL 0x0800 /* Link partner 1000BASE-T full duplex */
......
...@@ -552,6 +552,8 @@ static int ethtool_get_link_ksettings(struct net_device *dev, ...@@ -552,6 +552,8 @@ static int ethtool_get_link_ksettings(struct net_device *dev,
link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS; link_ksettings.base.cmd = ETHTOOL_GLINKSETTINGS;
link_ksettings.base.link_mode_masks_nwords link_ksettings.base.link_mode_masks_nwords
= __ETHTOOL_LINK_MODE_MASK_NU32; = __ETHTOOL_LINK_MODE_MASK_NU32;
link_ksettings.base.master_slave_cfg = MASTER_SLAVE_CFG_UNSUPPORTED;
link_ksettings.base.master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
return store_link_ksettings_for_user(useraddr, &link_ksettings); return store_link_ksettings_for_user(useraddr, &link_ksettings);
} }
...@@ -589,6 +591,10 @@ static int ethtool_set_link_ksettings(struct net_device *dev, ...@@ -589,6 +591,10 @@ static int ethtool_set_link_ksettings(struct net_device *dev,
!= link_ksettings.base.link_mode_masks_nwords) != link_ksettings.base.link_mode_masks_nwords)
return -EINVAL; return -EINVAL;
if (link_ksettings.base.master_slave_cfg ||
link_ksettings.base.master_slave_state)
return -EINVAL;
err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings); err = dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
if (err >= 0) { if (err >= 0) {
ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL); ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
......
...@@ -27,6 +27,8 @@ linkmodes_get_policy[ETHTOOL_A_LINKMODES_MAX + 1] = { ...@@ -27,6 +27,8 @@ linkmodes_get_policy[ETHTOOL_A_LINKMODES_MAX + 1] = {
[ETHTOOL_A_LINKMODES_PEER] = { .type = NLA_REJECT }, [ETHTOOL_A_LINKMODES_PEER] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_REJECT }, [ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_REJECT }, [ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE] = { .type = NLA_REJECT },
}; };
static int linkmodes_prepare_data(const struct ethnl_req_info *req_base, static int linkmodes_prepare_data(const struct ethnl_req_info *req_base,
...@@ -63,6 +65,7 @@ static int linkmodes_reply_size(const struct ethnl_req_info *req_base, ...@@ -63,6 +65,7 @@ static int linkmodes_reply_size(const struct ethnl_req_info *req_base,
{ {
const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base); const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
const struct ethtool_link_ksettings *ksettings = &data->ksettings; const struct ethtool_link_ksettings *ksettings = &data->ksettings;
const struct ethtool_link_settings *lsettings = &ksettings->base;
bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
int len, ret; int len, ret;
...@@ -86,6 +89,12 @@ static int linkmodes_reply_size(const struct ethnl_req_info *req_base, ...@@ -86,6 +89,12 @@ static int linkmodes_reply_size(const struct ethnl_req_info *req_base,
len += ret; len += ret;
} }
if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED)
len += nla_total_size(sizeof(u8));
if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED)
len += nla_total_size(sizeof(u8));
return len; return len;
} }
...@@ -122,6 +131,16 @@ static int linkmodes_fill_reply(struct sk_buff *skb, ...@@ -122,6 +131,16 @@ static int linkmodes_fill_reply(struct sk_buff *skb,
nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex)) nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex))
return -EMSGSIZE; return -EMSGSIZE;
if (lsettings->master_slave_cfg != MASTER_SLAVE_CFG_UNSUPPORTED &&
nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,
lsettings->master_slave_cfg))
return -EMSGSIZE;
if (lsettings->master_slave_state != MASTER_SLAVE_STATE_UNSUPPORTED &&
nla_put_u8(skb, ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE,
lsettings->master_slave_state))
return -EMSGSIZE;
return 0; return 0;
} }
...@@ -249,6 +268,8 @@ linkmodes_set_policy[ETHTOOL_A_LINKMODES_MAX + 1] = { ...@@ -249,6 +268,8 @@ linkmodes_set_policy[ETHTOOL_A_LINKMODES_MAX + 1] = {
[ETHTOOL_A_LINKMODES_PEER] = { .type = NLA_REJECT }, [ETHTOOL_A_LINKMODES_PEER] = { .type = NLA_REJECT },
[ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_U32 }, [ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_U32 },
[ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_U8 }, [ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_U8 },
[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG] = { .type = NLA_U8 },
[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE] = { .type = NLA_REJECT },
}; };
/* Set advertised link modes to all supported modes matching requested speed /* Set advertised link modes to all supported modes matching requested speed
...@@ -287,14 +308,45 @@ static bool ethnl_auto_linkmodes(struct ethtool_link_ksettings *ksettings, ...@@ -287,14 +308,45 @@ static bool ethnl_auto_linkmodes(struct ethtool_link_ksettings *ksettings,
__ETHTOOL_LINK_MODE_MASK_NBITS); __ETHTOOL_LINK_MODE_MASK_NBITS);
} }
static bool ethnl_validate_master_slave_cfg(u8 cfg)
{
switch (cfg) {
case MASTER_SLAVE_CFG_MASTER_PREFERRED:
case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
case MASTER_SLAVE_CFG_MASTER_FORCE:
case MASTER_SLAVE_CFG_SLAVE_FORCE:
return true;
}
return false;
}
static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb, static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb,
struct ethtool_link_ksettings *ksettings, struct ethtool_link_ksettings *ksettings,
bool *mod) bool *mod)
{ {
struct ethtool_link_settings *lsettings = &ksettings->base; struct ethtool_link_settings *lsettings = &ksettings->base;
bool req_speed, req_duplex; bool req_speed, req_duplex;
const struct nlattr *master_slave_cfg;
int ret; int ret;
master_slave_cfg = tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG];
if (master_slave_cfg) {
u8 cfg = nla_get_u8(master_slave_cfg);
if (lsettings->master_slave_cfg == MASTER_SLAVE_CFG_UNSUPPORTED) {
NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg,
"master/slave configuration not supported by device");
return -EOPNOTSUPP;
}
if (!ethnl_validate_master_slave_cfg(cfg)) {
NL_SET_ERR_MSG_ATTR(info->extack, master_slave_cfg,
"master/slave value is invalid");
return -EOPNOTSUPP;
}
}
*mod = false; *mod = false;
req_speed = tb[ETHTOOL_A_LINKMODES_SPEED]; req_speed = tb[ETHTOOL_A_LINKMODES_SPEED];
req_duplex = tb[ETHTOOL_A_LINKMODES_DUPLEX]; req_duplex = tb[ETHTOOL_A_LINKMODES_DUPLEX];
...@@ -311,6 +363,7 @@ static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb, ...@@ -311,6 +363,7 @@ static int ethnl_update_linkmodes(struct genl_info *info, struct nlattr **tb,
mod); mod);
ethnl_update_u8(&lsettings->duplex, tb[ETHTOOL_A_LINKMODES_DUPLEX], ethnl_update_u8(&lsettings->duplex, tb[ETHTOOL_A_LINKMODES_DUPLEX],
mod); mod);
ethnl_update_u8(&lsettings->master_slave_cfg, master_slave_cfg, mod);
if (!tb[ETHTOOL_A_LINKMODES_OURS] && lsettings->autoneg && if (!tb[ETHTOOL_A_LINKMODES_OURS] && lsettings->autoneg &&
(req_speed || req_duplex) && (req_speed || req_duplex) &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment