aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.15/950-0517-bcm2835_smi_dev-Fix-handling-of-word-odd-lengths.patch
blob: b0055dfcf8d8fc3539806a5801d5c1315a0b475a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
From 0922abd923ec50f6cc41ded24485bd22c719c7bd Mon Sep 17 00:00:00 2001
From: madimario <madimario@users.noreply.github.com>
Date: Tue, 28 Sep 2021 04:20:06 -0400
Subject: [PATCH] bcm2835_smi_dev: Fix handling of word-odd lengths

The read and write functions did not use the correct pointer offset
when dealing with an odd number of bytes after a DMA transfer. Also,
only handle the remaining odd bytes if the DMA transfer completed
successfully.

Submitted-by: @madimario (GitHub)
Signed-off-by: Phil Elwell <phil@raspberrypi.com>
---
 drivers/char/broadcom/bcm2835_smi_dev.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

--- a/drivers/char/broadcom/bcm2835_smi_dev.c
+++ b/drivers/char/broadcom/bcm2835_smi_dev.c
@@ -191,6 +191,7 @@ bcm2835_read_file(struct file *f, char _
 		  size_t count, loff_t *offs)
 {
 	int odd_bytes;
+	size_t count_check;
 
 	dev_dbg(inst->dev, "User reading %zu bytes from SMI.", count);
 	/* We don't want to DMA a number of bytes % 4 != 0 (32 bit FIFO) */
@@ -199,6 +200,7 @@ bcm2835_read_file(struct file *f, char _
 	else
 		odd_bytes = count;
 	count -= odd_bytes;
+	count_check = count;
 	if (count) {
 		struct bcm2835_smi_bounce_info *bounce;
 
@@ -209,15 +211,16 @@ bcm2835_read_file(struct file *f, char _
 			count = dma_bounce_user(DMA_DEV_TO_MEM, user_ptr,
 				count, bounce);
 	}
-	if (odd_bytes) {
+	if (odd_bytes && (count == count_check)) {
 		/* Read from FIFO directly if not using DMA */
 		uint8_t buf[DMA_THRESHOLD_BYTES];
+		unsigned long bytes_not_transferred;
 
 		bcm2835_smi_read_buf(smi_inst, buf, odd_bytes);
-		if (copy_to_user(user_ptr, buf, odd_bytes))
+		bytes_not_transferred = copy_to_user(user_ptr + count, buf, odd_bytes);
+		if (bytes_not_transferred)
 			dev_err(inst->dev, "copy_to_user() failed.");
-		count += odd_bytes;
-
+		count += odd_bytes - bytes_not_transferred;
 	}
 	return count;
 }
@@ -227,6 +230,7 @@ bcm2835_write_file(struct file *f, const
 		   size_t count, loff_t *offs)
 {
 	int odd_bytes;
+	size_t count_check;
 
 	dev_dbg(inst->dev, "User writing %zu bytes to SMI.", count);
 	if (count > DMA_THRESHOLD_BYTES)
@@ -234,6 +238,7 @@ bcm2835_write_file(struct file *f, const
 	else
 		odd_bytes = count;
 	count -= odd_bytes;
+	count_check = count;
 	if (count) {
 		struct bcm2835_smi_bounce_info *bounce;
 
@@ -245,14 +250,16 @@ bcm2835_write_file(struct file *f, const
 				(char __user *)user_ptr,
 				count, bounce);
 	}
-	if (odd_bytes) {
+	if (odd_bytes && (count == count_check)) {
 		uint8_t buf[DMA_THRESHOLD_BYTES];
+		unsigned long bytes_not_transferred;
 
-		if (copy_from_user(buf, user_ptr, odd_bytes))
+		bytes_not_transferred = copy_from_user(buf, user_ptr + count, odd_bytes);
+		if (bytes_not_transferred)
 			dev_err(inst->dev, "copy_from_user() failed.");
 		else
 			bcm2835_smi_write_buf(smi_inst, buf, odd_bytes);
-		count += odd_bytes;
+		count += odd_bytes - bytes_not_transferred;
 	}
 	return count;
 }