Commit a2550d3c authored by Christian Marangi's avatar Christian Marangi Committed by David S. Miller

net: dsa: qca8k: fix inband mgmt for big-endian systems

The header and the data of the skb for the inband mgmt requires
to be in little-endian. This is problematic for big-endian system
as the mgmt header is written in the cpu byte order.

Fix this by converting each value for the mgmt header and data to
little-endian, and convert to cpu byte order the mgmt header and
data sent by the switch.

Fixes: 5950c7c0 ("net: dsa: qca8k: add support for mgmt read/write in Ethernet packet")
Tested-by: default avatarPawel Dembicki <paweldembicki@gmail.com>
Tested-by: default avatarLech Perczak <lech.perczak@gmail.com>
Signed-off-by: default avatarChristian Marangi <ansuelsmth@gmail.com>
Reviewed-by: default avatarLech Perczak <lech.perczak@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 777ecaab
...@@ -137,27 +137,42 @@ static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb) ...@@ -137,27 +137,42 @@ static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
struct qca8k_mgmt_eth_data *mgmt_eth_data; struct qca8k_mgmt_eth_data *mgmt_eth_data;
struct qca8k_priv *priv = ds->priv; struct qca8k_priv *priv = ds->priv;
struct qca_mgmt_ethhdr *mgmt_ethhdr; struct qca_mgmt_ethhdr *mgmt_ethhdr;
u32 command;
u8 len, cmd; u8 len, cmd;
int i;
mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb); mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
mgmt_eth_data = &priv->mgmt_eth_data; mgmt_eth_data = &priv->mgmt_eth_data;
cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command); command = get_unaligned_le32(&mgmt_ethhdr->command);
len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command); cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command);
len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command);
/* Make sure the seq match the requested packet */ /* Make sure the seq match the requested packet */
if (mgmt_ethhdr->seq == mgmt_eth_data->seq) if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
mgmt_eth_data->ack = true; mgmt_eth_data->ack = true;
if (cmd == MDIO_READ) { if (cmd == MDIO_READ) {
mgmt_eth_data->data[0] = mgmt_ethhdr->mdio_data; u32 *val = mgmt_eth_data->data;
*val = get_unaligned_le32(&mgmt_ethhdr->mdio_data);
/* Get the rest of the 12 byte of data. /* Get the rest of the 12 byte of data.
* The read/write function will extract the requested data. * The read/write function will extract the requested data.
*/ */
if (len > QCA_HDR_MGMT_DATA1_LEN) if (len > QCA_HDR_MGMT_DATA1_LEN) {
memcpy(mgmt_eth_data->data + 1, skb->data, __le32 *data2 = (__le32 *)skb->data;
QCA_HDR_MGMT_DATA2_LEN); int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
len - QCA_HDR_MGMT_DATA1_LEN);
val++;
for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
*val = get_unaligned_le32(data2);
val++;
data2++;
}
}
} }
complete(&mgmt_eth_data->rw_done); complete(&mgmt_eth_data->rw_done);
...@@ -169,8 +184,10 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 * ...@@ -169,8 +184,10 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
struct qca_mgmt_ethhdr *mgmt_ethhdr; struct qca_mgmt_ethhdr *mgmt_ethhdr;
unsigned int real_len; unsigned int real_len;
struct sk_buff *skb; struct sk_buff *skb;
u32 *data2; __le32 *data2;
u32 command;
u16 hdr; u16 hdr;
int i;
skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN); skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
if (!skb) if (!skb)
...@@ -199,20 +216,32 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 * ...@@ -199,20 +216,32 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0)); hdr |= FIELD_PREP(QCA_HDR_XMIT_DP_BIT, BIT(0));
hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG); hdr |= FIELD_PREP(QCA_HDR_XMIT_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg); command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len); command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd); command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE, command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
QCA_HDR_MGMT_CHECK_CODE_VAL); QCA_HDR_MGMT_CHECK_CODE_VAL);
put_unaligned_le32(command, &mgmt_ethhdr->command);
if (cmd == MDIO_WRITE) if (cmd == MDIO_WRITE)
mgmt_ethhdr->mdio_data = *val; put_unaligned_le32(*val, &mgmt_ethhdr->mdio_data);
mgmt_ethhdr->hdr = htons(hdr); mgmt_ethhdr->hdr = htons(hdr);
data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN); data2 = skb_put_zero(skb, QCA_HDR_MGMT_DATA2_LEN + QCA_HDR_MGMT_PADDING_LEN);
if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) if (cmd == MDIO_WRITE && len > QCA_HDR_MGMT_DATA1_LEN) {
memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN); int data_len = min_t(int, QCA_HDR_MGMT_DATA2_LEN,
len - QCA_HDR_MGMT_DATA1_LEN);
val++;
for (i = sizeof(u32); i <= data_len; i += sizeof(u32)) {
put_unaligned_le32(*val, data2);
data2++;
val++;
}
}
return skb; return skb;
} }
...@@ -220,9 +249,11 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 * ...@@ -220,9 +249,11 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num) static void qca8k_mdio_header_fill_seq_num(struct sk_buff *skb, u32 seq_num)
{ {
struct qca_mgmt_ethhdr *mgmt_ethhdr; struct qca_mgmt_ethhdr *mgmt_ethhdr;
u32 seq;
seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num);
mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data; mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb->data;
mgmt_ethhdr->seq = FIELD_PREP(QCA_HDR_MGMT_SEQ_NUM, seq_num); put_unaligned_le32(seq, &mgmt_ethhdr->seq);
} }
static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len) static int qca8k_read_eth(struct qca8k_priv *priv, u32 reg, u32 *val, int len)
......
...@@ -61,9 +61,9 @@ struct sk_buff; ...@@ -61,9 +61,9 @@ struct sk_buff;
/* Special struct emulating a Ethernet header */ /* Special struct emulating a Ethernet header */
struct qca_mgmt_ethhdr { struct qca_mgmt_ethhdr {
u32 command; /* command bit 31:0 */ __le32 command; /* command bit 31:0 */
u32 seq; /* seq 63:32 */ __le32 seq; /* seq 63:32 */
u32 mdio_data; /* first 4byte mdio */ __le32 mdio_data; /* first 4byte mdio */
__be16 hdr; /* qca hdr */ __be16 hdr; /* qca hdr */
} __packed; } __packed;
......
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