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", |
