diff options
-rw-r--r-- | Documentation/spi/spidev_test.c | 115 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 55 |
2 files changed, 138 insertions, 32 deletions
diff --git a/Documentation/spi/spidev_test.c b/Documentation/spi/spidev_test.c index 3a2f9d59edab..94f574b0fdb2 100644 --- a/Documentation/spi/spidev_test.c +++ b/Documentation/spi/spidev_test.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <unistd.h> | 15 | #include <unistd.h> |
16 | #include <stdio.h> | 16 | #include <stdio.h> |
17 | #include <stdlib.h> | 17 | #include <stdlib.h> |
18 | #include <string.h> | ||
18 | #include <getopt.h> | 19 | #include <getopt.h> |
19 | #include <fcntl.h> | 20 | #include <fcntl.h> |
20 | #include <sys/ioctl.h> | 21 | #include <sys/ioctl.h> |
@@ -34,24 +35,79 @@ static uint32_t mode; | |||
34 | static uint8_t bits = 8; | 35 | static uint8_t bits = 8; |
35 | static uint32_t speed = 500000; | 36 | static uint32_t speed = 500000; |
36 | static uint16_t delay; | 37 | static uint16_t delay; |
38 | static int verbose; | ||
37 | 39 | ||
38 | static void transfer(int fd) | 40 | uint8_t default_tx[] = { |
41 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
42 | 0x40, 0x00, 0x00, 0x00, 0x00, 0x95, | ||
43 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
44 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
45 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
46 | 0xF0, 0x0D, | ||
47 | }; | ||
48 | |||
49 | uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, }; | ||
50 | char *input_tx; | ||
51 | |||
52 | static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix) | ||
53 | { | ||
54 | int i = 0; | ||
55 | const unsigned char *address = src; | ||
56 | const unsigned char *line = address; | ||
57 | unsigned char c; | ||
58 | |||
59 | printf("%s | ", prefix); | ||
60 | while (length-- > 0) { | ||
61 | printf("%02X ", *address++); | ||
62 | if (!(++i % line_size) || (length == 0 && i % line_size)) { | ||
63 | if (length == 0) { | ||
64 | while (i++ % line_size) | ||
65 | printf("__ "); | ||
66 | } | ||
67 | printf(" | "); /* right close */ | ||
68 | while (line < address) { | ||
69 | c = *line++; | ||
70 | printf("%c", (c < 33 || c == 255) ? 0x2E : c); | ||
71 | } | ||
72 | printf("\n"); | ||
73 | if (length > 0) | ||
74 | printf("%s | ", prefix); | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * Unescape - process hexadecimal escape character | ||
81 | * converts shell input "\x23" -> 0x23 | ||
82 | */ | ||
83 | int unespcape(char *_dst, char *_src, size_t len) | ||
84 | { | ||
85 | int ret = 0; | ||
86 | char *src = _src; | ||
87 | char *dst = _dst; | ||
88 | unsigned int ch; | ||
89 | |||
90 | while (*src) { | ||
91 | if (*src == '\\' && *(src+1) == 'x') { | ||
92 | sscanf(src + 2, "%2x", &ch); | ||
93 | src += 4; | ||
94 | *dst++ = (unsigned char)ch; | ||
95 | } else { | ||
96 | *dst++ = *src++; | ||
97 | } | ||
98 | ret++; | ||
99 | } | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) | ||
39 | { | 104 | { |
40 | int ret; | 105 | int ret; |
41 | uint8_t tx[] = { | 106 | |
42 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
43 | 0x40, 0x00, 0x00, 0x00, 0x00, 0x95, | ||
44 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
45 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
46 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
47 | 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, | ||
48 | 0xF0, 0x0D, | ||
49 | }; | ||
50 | uint8_t rx[ARRAY_SIZE(tx)] = {0, }; | ||
51 | struct spi_ioc_transfer tr = { | 107 | struct spi_ioc_transfer tr = { |
52 | .tx_buf = (unsigned long)tx, | 108 | .tx_buf = (unsigned long)tx, |
53 | .rx_buf = (unsigned long)rx, | 109 | .rx_buf = (unsigned long)rx, |
54 | .len = ARRAY_SIZE(tx), | 110 | .len = len, |
55 | .delay_usecs = delay, | 111 | .delay_usecs = delay, |
56 | .speed_hz = speed, | 112 | .speed_hz = speed, |
57 | .bits_per_word = bits, | 113 | .bits_per_word = bits, |
@@ -76,12 +132,9 @@ static void transfer(int fd) | |||
76 | if (ret < 1) | 132 | if (ret < 1) |
77 | pabort("can't send spi message"); | 133 | pabort("can't send spi message"); |
78 | 134 | ||
79 | for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { | 135 | if (verbose) |
80 | if (!(ret % 6)) | 136 | hex_dump(tx, len, 32, "TX"); |
81 | puts(""); | 137 | hex_dump(rx, len, 32, "RX"); |
82 | printf("%.2X ", rx[ret]); | ||
83 | } | ||
84 | puts(""); | ||
85 | } | 138 | } |
86 | 139 | ||
87 | static void print_usage(const char *prog) | 140 | static void print_usage(const char *prog) |
@@ -97,6 +150,8 @@ static void print_usage(const char *prog) | |||
97 | " -L --lsb least significant bit first\n" | 150 | " -L --lsb least significant bit first\n" |
98 | " -C --cs-high chip select active high\n" | 151 | " -C --cs-high chip select active high\n" |
99 | " -3 --3wire SI/SO signals shared\n" | 152 | " -3 --3wire SI/SO signals shared\n" |
153 | " -v --verbose Verbose (show tx buffer)\n" | ||
154 | " -p Send data (e.g. \"1234\\xde\\xad\")\n" | ||
100 | " -N --no-cs no chip select\n" | 155 | " -N --no-cs no chip select\n" |
101 | " -R --ready slave pulls low to pause\n" | 156 | " -R --ready slave pulls low to pause\n" |
102 | " -2 --dual dual transfer\n" | 157 | " -2 --dual dual transfer\n" |
@@ -121,12 +176,13 @@ static void parse_opts(int argc, char *argv[]) | |||
121 | { "no-cs", 0, 0, 'N' }, | 176 | { "no-cs", 0, 0, 'N' }, |
122 | { "ready", 0, 0, 'R' }, | 177 | { "ready", 0, 0, 'R' }, |
123 | { "dual", 0, 0, '2' }, | 178 | { "dual", 0, 0, '2' }, |
179 | { "verbose", 0, 0, 'v' }, | ||
124 | { "quad", 0, 0, '4' }, | 180 | { "quad", 0, 0, '4' }, |
125 | { NULL, 0, 0, 0 }, | 181 | { NULL, 0, 0, 0 }, |
126 | }; | 182 | }; |
127 | int c; | 183 | int c; |
128 | 184 | ||
129 | c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24", lopts, NULL); | 185 | c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24p:v", lopts, NULL); |
130 | 186 | ||
131 | if (c == -1) | 187 | if (c == -1) |
132 | break; | 188 | break; |
@@ -165,9 +221,15 @@ static void parse_opts(int argc, char *argv[]) | |||
165 | case 'N': | 221 | case 'N': |
166 | mode |= SPI_NO_CS; | 222 | mode |= SPI_NO_CS; |
167 | break; | 223 | break; |
224 | case 'v': | ||
225 | verbose = 1; | ||
226 | break; | ||
168 | case 'R': | 227 | case 'R': |
169 | mode |= SPI_READY; | 228 | mode |= SPI_READY; |
170 | break; | 229 | break; |
230 | case 'p': | ||
231 | input_tx = optarg; | ||
232 | break; | ||
171 | case '2': | 233 | case '2': |
172 | mode |= SPI_TX_DUAL; | 234 | mode |= SPI_TX_DUAL; |
173 | break; | 235 | break; |
@@ -191,6 +253,9 @@ int main(int argc, char *argv[]) | |||
191 | { | 253 | { |
192 | int ret = 0; | 254 | int ret = 0; |
193 | int fd; | 255 | int fd; |
256 | uint8_t *tx; | ||
257 | uint8_t *rx; | ||
258 | int size; | ||
194 | 259 | ||
195 | parse_opts(argc, argv); | 260 | parse_opts(argc, argv); |
196 | 261 | ||
@@ -235,7 +300,17 @@ int main(int argc, char *argv[]) | |||
235 | printf("bits per word: %d\n", bits); | 300 | printf("bits per word: %d\n", bits); |
236 | printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); | 301 | printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); |
237 | 302 | ||
238 | transfer(fd); | 303 | if (input_tx) { |
304 | size = strlen(input_tx+1); | ||
305 | tx = malloc(size); | ||
306 | rx = malloc(size); | ||
307 | size = unespcape((char *)tx, input_tx, size); | ||
308 | transfer(fd, tx, rx, size); | ||
309 | free(rx); | ||
310 | free(tx); | ||
311 | } else { | ||
312 | transfer(fd, default_tx, default_rx, sizeof(default_tx)); | ||
313 | } | ||
239 | 314 | ||
240 | close(fd); | 315 | close(fd); |
241 | 316 | ||
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 4eb7a980e670..92c909eed6b5 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c | |||
@@ -223,7 +223,7 @@ static int spidev_message(struct spidev_data *spidev, | |||
223 | struct spi_transfer *k_xfers; | 223 | struct spi_transfer *k_xfers; |
224 | struct spi_transfer *k_tmp; | 224 | struct spi_transfer *k_tmp; |
225 | struct spi_ioc_transfer *u_tmp; | 225 | struct spi_ioc_transfer *u_tmp; |
226 | unsigned n, total; | 226 | unsigned n, total, tx_total, rx_total; |
227 | u8 *tx_buf, *rx_buf; | 227 | u8 *tx_buf, *rx_buf; |
228 | int status = -EFAULT; | 228 | int status = -EFAULT; |
229 | 229 | ||
@@ -239,33 +239,52 @@ static int spidev_message(struct spidev_data *spidev, | |||
239 | tx_buf = spidev->tx_buffer; | 239 | tx_buf = spidev->tx_buffer; |
240 | rx_buf = spidev->rx_buffer; | 240 | rx_buf = spidev->rx_buffer; |
241 | total = 0; | 241 | total = 0; |
242 | tx_total = 0; | ||
243 | rx_total = 0; | ||
242 | for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; | 244 | for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; |
243 | n; | 245 | n; |
244 | n--, k_tmp++, u_tmp++) { | 246 | n--, k_tmp++, u_tmp++) { |
245 | k_tmp->len = u_tmp->len; | 247 | k_tmp->len = u_tmp->len; |
246 | 248 | ||
247 | total += k_tmp->len; | 249 | total += k_tmp->len; |
248 | if (total > bufsiz) { | 250 | /* Since the function returns the total length of transfers |
251 | * on success, restrict the total to positive int values to | ||
252 | * avoid the return value looking like an error. Also check | ||
253 | * each transfer length to avoid arithmetic overflow. | ||
254 | */ | ||
255 | if (total > INT_MAX || k_tmp->len > INT_MAX) { | ||
249 | status = -EMSGSIZE; | 256 | status = -EMSGSIZE; |
250 | goto done; | 257 | goto done; |
251 | } | 258 | } |
252 | 259 | ||
253 | if (u_tmp->rx_buf) { | 260 | if (u_tmp->rx_buf) { |
261 | /* this transfer needs space in RX bounce buffer */ | ||
262 | rx_total += k_tmp->len; | ||
263 | if (rx_total > bufsiz) { | ||
264 | status = -EMSGSIZE; | ||
265 | goto done; | ||
266 | } | ||
254 | k_tmp->rx_buf = rx_buf; | 267 | k_tmp->rx_buf = rx_buf; |
255 | if (!access_ok(VERIFY_WRITE, (u8 __user *) | 268 | if (!access_ok(VERIFY_WRITE, (u8 __user *) |
256 | (uintptr_t) u_tmp->rx_buf, | 269 | (uintptr_t) u_tmp->rx_buf, |
257 | u_tmp->len)) | 270 | u_tmp->len)) |
258 | goto done; | 271 | goto done; |
272 | rx_buf += k_tmp->len; | ||
259 | } | 273 | } |
260 | if (u_tmp->tx_buf) { | 274 | if (u_tmp->tx_buf) { |
275 | /* this transfer needs space in TX bounce buffer */ | ||
276 | tx_total += k_tmp->len; | ||
277 | if (tx_total > bufsiz) { | ||
278 | status = -EMSGSIZE; | ||
279 | goto done; | ||
280 | } | ||
261 | k_tmp->tx_buf = tx_buf; | 281 | k_tmp->tx_buf = tx_buf; |
262 | if (copy_from_user(tx_buf, (const u8 __user *) | 282 | if (copy_from_user(tx_buf, (const u8 __user *) |
263 | (uintptr_t) u_tmp->tx_buf, | 283 | (uintptr_t) u_tmp->tx_buf, |
264 | u_tmp->len)) | 284 | u_tmp->len)) |
265 | goto done; | 285 | goto done; |
286 | tx_buf += k_tmp->len; | ||
266 | } | 287 | } |
267 | tx_buf += k_tmp->len; | ||
268 | rx_buf += k_tmp->len; | ||
269 | 288 | ||
270 | k_tmp->cs_change = !!u_tmp->cs_change; | 289 | k_tmp->cs_change = !!u_tmp->cs_change; |
271 | k_tmp->tx_nbits = u_tmp->tx_nbits; | 290 | k_tmp->tx_nbits = u_tmp->tx_nbits; |
@@ -303,8 +322,8 @@ static int spidev_message(struct spidev_data *spidev, | |||
303 | status = -EFAULT; | 322 | status = -EFAULT; |
304 | goto done; | 323 | goto done; |
305 | } | 324 | } |
325 | rx_buf += u_tmp->len; | ||
306 | } | 326 | } |
307 | rx_buf += u_tmp->len; | ||
308 | } | 327 | } |
309 | status = total; | 328 | status = total; |
310 | 329 | ||
@@ -684,6 +703,14 @@ static const struct file_operations spidev_fops = { | |||
684 | 703 | ||
685 | static struct class *spidev_class; | 704 | static struct class *spidev_class; |
686 | 705 | ||
706 | #ifdef CONFIG_OF | ||
707 | static const struct of_device_id spidev_dt_ids[] = { | ||
708 | { .compatible = "rohm,dh2228fv" }, | ||
709 | {}, | ||
710 | }; | ||
711 | MODULE_DEVICE_TABLE(of, spidev_dt_ids); | ||
712 | #endif | ||
713 | |||
687 | /*-------------------------------------------------------------------------*/ | 714 | /*-------------------------------------------------------------------------*/ |
688 | 715 | ||
689 | static int spidev_probe(struct spi_device *spi) | 716 | static int spidev_probe(struct spi_device *spi) |
@@ -692,6 +719,17 @@ static int spidev_probe(struct spi_device *spi) | |||
692 | int status; | 719 | int status; |
693 | unsigned long minor; | 720 | unsigned long minor; |
694 | 721 | ||
722 | /* | ||
723 | * spidev should never be referenced in DT without a specific | ||
724 | * compatbile string, it is a Linux implementation thing | ||
725 | * rather than a description of the hardware. | ||
726 | */ | ||
727 | if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { | ||
728 | dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); | ||
729 | WARN_ON(spi->dev.of_node && | ||
730 | !of_match_device(spidev_dt_ids, &spi->dev)); | ||
731 | } | ||
732 | |||
695 | /* Allocate driver data */ | 733 | /* Allocate driver data */ |
696 | spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); | 734 | spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); |
697 | if (!spidev) | 735 | if (!spidev) |
@@ -758,13 +796,6 @@ static int spidev_remove(struct spi_device *spi) | |||
758 | return 0; | 796 | return 0; |
759 | } | 797 | } |
760 | 798 | ||
761 | static const struct of_device_id spidev_dt_ids[] = { | ||
762 | { .compatible = "rohm,dh2228fv" }, | ||
763 | {}, | ||
764 | }; | ||
765 | |||
766 | MODULE_DEVICE_TABLE(of, spidev_dt_ids); | ||
767 | |||
768 | static struct spi_driver spidev_spi_driver = { | 799 | static struct spi_driver spidev_spi_driver = { |
769 | .driver = { | 800 | .driver = { |
770 | .name = "spidev", | 801 | .name = "spidev", |