mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 02:36:02 +00:00
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: 5950c7c0a6
("net: dsa: qca8k: add support for mgmt read/write in Ethernet packet")
Tested-by: Pawel Dembicki <paweldembicki@gmail.com>
Tested-by: Lech Perczak <lech.perczak@gmail.com>
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Reviewed-by: Lech Perczak <lech.perczak@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
777ecaabd6
commit
a2550d3ce5
@ -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_priv *priv = ds->priv;
|
||||
struct qca_mgmt_ethhdr *mgmt_ethhdr;
|
||||
u32 command;
|
||||
u8 len, cmd;
|
||||
int i;
|
||||
|
||||
mgmt_ethhdr = (struct qca_mgmt_ethhdr *)skb_mac_header(skb);
|
||||
mgmt_eth_data = &priv->mgmt_eth_data;
|
||||
|
||||
cmd = FIELD_GET(QCA_HDR_MGMT_CMD, mgmt_ethhdr->command);
|
||||
len = FIELD_GET(QCA_HDR_MGMT_LENGTH, mgmt_ethhdr->command);
|
||||
command = get_unaligned_le32(&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 */
|
||||
if (mgmt_ethhdr->seq == mgmt_eth_data->seq)
|
||||
if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
|
||||
mgmt_eth_data->ack = true;
|
||||
|
||||
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.
|
||||
* The read/write function will extract the requested data.
|
||||
*/
|
||||
if (len > QCA_HDR_MGMT_DATA1_LEN)
|
||||
memcpy(mgmt_eth_data->data + 1, skb->data,
|
||||
QCA_HDR_MGMT_DATA2_LEN);
|
||||
if (len > QCA_HDR_MGMT_DATA1_LEN) {
|
||||
__le32 *data2 = (__le32 *)skb->data;
|
||||
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);
|
||||
@ -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;
|
||||
unsigned int real_len;
|
||||
struct sk_buff *skb;
|
||||
u32 *data2;
|
||||
__le32 *data2;
|
||||
u32 command;
|
||||
u16 hdr;
|
||||
int i;
|
||||
|
||||
skb = dev_alloc_skb(QCA_HDR_MGMT_PKT_LEN);
|
||||
if (!skb)
|
||||
@ -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_CONTROL, QCA_HDR_XMIT_TYPE_RW_REG);
|
||||
|
||||
mgmt_ethhdr->command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
|
||||
mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
|
||||
mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
|
||||
mgmt_ethhdr->command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
|
||||
command = FIELD_PREP(QCA_HDR_MGMT_ADDR, reg);
|
||||
command |= FIELD_PREP(QCA_HDR_MGMT_LENGTH, real_len);
|
||||
command |= FIELD_PREP(QCA_HDR_MGMT_CMD, cmd);
|
||||
command |= FIELD_PREP(QCA_HDR_MGMT_CHECK_CODE,
|
||||
QCA_HDR_MGMT_CHECK_CODE_VAL);
|
||||
|
||||
put_unaligned_le32(command, &mgmt_ethhdr->command);
|
||||
|
||||
if (cmd == MDIO_WRITE)
|
||||
mgmt_ethhdr->mdio_data = *val;
|
||||
put_unaligned_le32(*val, &mgmt_ethhdr->mdio_data);
|
||||
|
||||
mgmt_ethhdr->hdr = htons(hdr);
|
||||
|
||||
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)
|
||||
memcpy(data2, val + 1, len - QCA_HDR_MGMT_DATA1_LEN);
|
||||
if (cmd == MDIO_WRITE && 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;
|
||||
}
|
||||
@ -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)
|
||||
{
|
||||
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->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)
|
||||
|
@ -61,9 +61,9 @@ struct sk_buff;
|
||||
|
||||
/* Special struct emulating a Ethernet header */
|
||||
struct qca_mgmt_ethhdr {
|
||||
u32 command; /* command bit 31:0 */
|
||||
u32 seq; /* seq 63:32 */
|
||||
u32 mdio_data; /* first 4byte mdio */
|
||||
__le32 command; /* command bit 31:0 */
|
||||
__le32 seq; /* seq 63:32 */
|
||||
__le32 mdio_data; /* first 4byte mdio */
|
||||
__be16 hdr; /* qca hdr */
|
||||
} __packed;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user