[PATCH v8 05/13] mfd: nct6694: Transfer data packets via a dedicated buffer
From: a0282524688
Date: Wed Oct 07 2026 - 05:33:44 EST
From: Ming Yu <a0282524688@xxxxxxxxx>
The caller's buffer is passed straight to usb_bulk_msg(). Sub-device
drivers embed those buffers in their private data, so they are neither
cacheline aligned nor exclusively owned by the transfer, and mapping
them for DMA can corrupt adjacent fields on non-coherent architectures.
Transfer the data packets through a buffer owned by the transport, and
reject commands exceeding the maximum data length.
Fixes: 51dad33ede63 ("mfd: Add core driver for Nuvoton NCT6694")
Signed-off-by: Ming Yu <a0282524688@xxxxxxxxx>
---
Changes in v8:
- Moved before the refactoring so it applies to the original driver.
- Renamed xfer_buf to data_buf, len to data_len and
NCT6694_MAX_PACKET_SIZE to NCT6694_MAX_DATA_LEN.
Changes in v7:
- New patch.
drivers/mfd/nct6694.c | 37 +++++++++++++++++++++++++++----------
include/linux/mfd/nct6694.h | 4 ++++
2 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/drivers/mfd/nct6694.c b/drivers/mfd/nct6694.c
index 4f5a5b855de2..f9ae8476fcae 100644
--- a/drivers/mfd/nct6694.c
+++ b/drivers/mfd/nct6694.c
@@ -98,8 +98,12 @@ int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *c
{
union nct6694_usb_msg *msg = nct6694->usb_msg;
struct usb_device *udev = nct6694->udev;
+ u16 data_len = le16_to_cpu(cmd_hd->len);
int tx_len, rx_len, ret;
+ if (data_len > NCT6694_MAX_DATA_LEN)
+ return -EINVAL;
+
guard(mutex)(&nct6694->access_lock);
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
@@ -124,17 +128,19 @@ int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *c
}
/* Receive data packet from USB device */
- ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
- le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), nct6694->data_buf,
+ data_len, &rx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
- if (rx_len != le16_to_cpu(cmd_hd->len)) {
+ if (rx_len != data_len) {
dev_err(nct6694->dev, "Expected received length %d, but got %d\n",
- le16_to_cpu(cmd_hd->len), rx_len);
+ data_len, rx_len);
return -EIO;
}
+ memcpy(buf, nct6694->data_buf, data_len);
+
return nct6694_response_err_handling(nct6694, msg->response_header.sts);
}
EXPORT_SYMBOL_GPL(nct6694_read_msg);
@@ -154,12 +160,17 @@ int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *
{
union nct6694_usb_msg *msg = nct6694->usb_msg;
struct usb_device *udev = nct6694->udev;
+ u16 data_len = le16_to_cpu(cmd_hd->len);
int tx_len, rx_len, ret;
+ if (data_len > NCT6694_MAX_DATA_LEN)
+ return -EINVAL;
+
guard(mutex)(&nct6694->access_lock);
memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));
msg->cmd_header.hctrl = NCT6694_HCTRL_SET;
+ memcpy(nct6694->data_buf, buf, data_len);
/* Send command packet to USB device */
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,
@@ -168,8 +179,8 @@ int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *
return ret;
/* Send data packet to USB device */
- ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), buf,
- le16_to_cpu(cmd_hd->len), &tx_len, NCT6694_URB_TIMEOUT);
+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), nct6694->data_buf,
+ data_len, &tx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
@@ -186,17 +197,19 @@ int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *
}
/* Receive data packet from USB device */
- ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,
- le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), nct6694->data_buf,
+ data_len, &rx_len, NCT6694_URB_TIMEOUT);
if (ret)
return ret;
- if (rx_len != le16_to_cpu(cmd_hd->len)) {
+ if (rx_len != data_len) {
dev_err(nct6694->dev, "Expected transmitted length %d, but got %d\n",
- le16_to_cpu(cmd_hd->len), rx_len);
+ data_len, rx_len);
return -EIO;
}
+ memcpy(buf, nct6694->data_buf, data_len);
+
return nct6694_response_err_handling(nct6694, msg->response_header.sts);
}
EXPORT_SYMBOL_GPL(nct6694_write_msg);
@@ -315,6 +328,10 @@ static int nct6694_usb_probe(struct usb_interface *iface,
if (!nct6694->usb_msg)
return -ENOMEM;
+ nct6694->data_buf = devm_kzalloc(dev, NCT6694_MAX_DATA_LEN, GFP_KERNEL);
+ if (!nct6694->data_buf)
+ return -ENOMEM;
+
nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL);
if (!nct6694->int_buffer)
return -ENOMEM;
diff --git a/include/linux/mfd/nct6694.h b/include/linux/mfd/nct6694.h
index a5ad7be47bf9..69e4652cf16c 100644
--- a/include/linux/mfd/nct6694.h
+++ b/include/linux/mfd/nct6694.h
@@ -19,6 +19,9 @@
#define NCT6694_URB_TIMEOUT 1000
+/* Maximum data packet length the firmware accepts in a single command */
+#define NCT6694_MAX_DATA_LEN 0x3F0
+
enum nct6694_irq_id {
NCT6694_IRQ_GPIO0 = 0,
NCT6694_IRQ_GPIO1,
@@ -92,6 +95,7 @@ struct nct6694 {
struct urb *int_in_urb;
struct usb_device *udev;
union nct6694_usb_msg *usb_msg;
+ void *data_buf;
__le32 *int_buffer;
unsigned int irq_enable;
};
--
2.34.1