Loading Documentation/spi/spidev_test.c +95 −20 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <fcntl.h> #include <sys/ioctl.h> Loading @@ -34,24 +35,79 @@ static uint32_t mode; static uint8_t bits = 8; static uint32_t speed = 500000; static uint16_t delay; static int verbose; static void transfer(int fd) { int ret; uint8_t tx[] = { uint8_t default_tx[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x95, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D, }; uint8_t rx[ARRAY_SIZE(tx)] = {0, }; uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, }; char *input_tx; static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix) { int i = 0; const unsigned char *address = src; const unsigned char *line = address; unsigned char c; printf("%s | ", prefix); while (length-- > 0) { printf("%02X ", *address++); if (!(++i % line_size) || (length == 0 && i % line_size)) { if (length == 0) { while (i++ % line_size) printf("__ "); } printf(" | "); /* right close */ while (line < address) { c = *line++; printf("%c", (c < 33 || c == 255) ? 0x2E : c); } printf("\n"); if (length > 0) printf("%s | ", prefix); } } } /* * Unescape - process hexadecimal escape character * converts shell input "\x23" -> 0x23 */ int unespcape(char *_dst, char *_src, size_t len) { int ret = 0; char *src = _src; char *dst = _dst; unsigned int ch; while (*src) { if (*src == '\\' && *(src+1) == 'x') { sscanf(src + 2, "%2x", &ch); src += 4; *dst++ = (unsigned char)ch; } else { *dst++ = *src++; } ret++; } return ret; } static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) { int ret; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)tx, .rx_buf = (unsigned long)rx, .len = ARRAY_SIZE(tx), .len = len, .delay_usecs = delay, .speed_hz = speed, .bits_per_word = bits, Loading @@ -76,12 +132,9 @@ static void transfer(int fd) if (ret < 1) pabort("can't send spi message"); for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { if (!(ret % 6)) puts(""); printf("%.2X ", rx[ret]); } puts(""); if (verbose) hex_dump(tx, len, 32, "TX"); hex_dump(rx, len, 32, "RX"); } static void print_usage(const char *prog) Loading @@ -97,6 +150,8 @@ static void print_usage(const char *prog) " -L --lsb least significant bit first\n" " -C --cs-high chip select active high\n" " -3 --3wire SI/SO signals shared\n" " -v --verbose Verbose (show tx buffer)\n" " -p Send data (e.g. \"1234\\xde\\xad\")\n" " -N --no-cs no chip select\n" " -R --ready slave pulls low to pause\n" " -2 --dual dual transfer\n" Loading @@ -121,12 +176,13 @@ static void parse_opts(int argc, char *argv[]) { "no-cs", 0, 0, 'N' }, { "ready", 0, 0, 'R' }, { "dual", 0, 0, '2' }, { "verbose", 0, 0, 'v' }, { "quad", 0, 0, '4' }, { NULL, 0, 0, 0 }, }; int c; c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24", lopts, NULL); c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24p:v", lopts, NULL); if (c == -1) break; Loading Loading @@ -165,9 +221,15 @@ static void parse_opts(int argc, char *argv[]) case 'N': mode |= SPI_NO_CS; break; case 'v': verbose = 1; break; case 'R': mode |= SPI_READY; break; case 'p': input_tx = optarg; break; case '2': mode |= SPI_TX_DUAL; break; Loading @@ -191,6 +253,9 @@ int main(int argc, char *argv[]) { int ret = 0; int fd; uint8_t *tx; uint8_t *rx; int size; parse_opts(argc, argv); Loading Loading @@ -235,7 +300,17 @@ int main(int argc, char *argv[]) printf("bits per word: %d\n", bits); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); transfer(fd); if (input_tx) { size = strlen(input_tx+1); tx = malloc(size); rx = malloc(size); size = unespcape((char *)tx, input_tx, size); transfer(fd, tx, rx, size); free(rx); free(tx); } else { transfer(fd, default_tx, default_rx, sizeof(default_tx)); } close(fd); Loading drivers/spi/spidev.c +43 −12 Original line number Diff line number Diff line Loading @@ -223,7 +223,7 @@ static int spidev_message(struct spidev_data *spidev, struct spi_transfer *k_xfers; struct spi_transfer *k_tmp; struct spi_ioc_transfer *u_tmp; unsigned n, total; unsigned n, total, tx_total, rx_total; u8 *tx_buf, *rx_buf; int status = -EFAULT; Loading @@ -239,33 +239,52 @@ static int spidev_message(struct spidev_data *spidev, tx_buf = spidev->tx_buffer; rx_buf = spidev->rx_buffer; total = 0; tx_total = 0; rx_total = 0; for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; n; n--, k_tmp++, u_tmp++) { k_tmp->len = u_tmp->len; total += k_tmp->len; if (total > bufsiz) { /* Since the function returns the total length of transfers * on success, restrict the total to positive int values to * avoid the return value looking like an error. Also check * each transfer length to avoid arithmetic overflow. */ if (total > INT_MAX || k_tmp->len > INT_MAX) { status = -EMSGSIZE; goto done; } if (u_tmp->rx_buf) { /* this transfer needs space in RX bounce buffer */ rx_total += k_tmp->len; if (rx_total > bufsiz) { status = -EMSGSIZE; goto done; } k_tmp->rx_buf = rx_buf; if (!access_ok(VERIFY_WRITE, (u8 __user *) (uintptr_t) u_tmp->rx_buf, u_tmp->len)) goto done; rx_buf += k_tmp->len; } if (u_tmp->tx_buf) { /* this transfer needs space in TX bounce buffer */ tx_total += k_tmp->len; if (tx_total > bufsiz) { status = -EMSGSIZE; goto done; } k_tmp->tx_buf = tx_buf; if (copy_from_user(tx_buf, (const u8 __user *) (uintptr_t) u_tmp->tx_buf, u_tmp->len)) goto done; } tx_buf += k_tmp->len; rx_buf += k_tmp->len; } k_tmp->cs_change = !!u_tmp->cs_change; k_tmp->tx_nbits = u_tmp->tx_nbits; Loading Loading @@ -303,9 +322,9 @@ static int spidev_message(struct spidev_data *spidev, status = -EFAULT; goto done; } } rx_buf += u_tmp->len; } } status = total; done: Loading Loading @@ -684,6 +703,14 @@ static const struct file_operations spidev_fops = { static struct class *spidev_class; #ifdef CONFIG_OF static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); #endif /*-------------------------------------------------------------------------*/ static int spidev_probe(struct spi_device *spi) Loading @@ -692,6 +719,17 @@ static int spidev_probe(struct spi_device *spi) int status; unsigned long minor; /* * spidev should never be referenced in DT without a specific * compatbile string, it is a Linux implementation thing * rather than a description of the hardware. */ if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); WARN_ON(spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)); } /* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) Loading Loading @@ -758,13 +796,6 @@ static int spidev_remove(struct spi_device *spi) return 0; } static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev", Loading Loading
Documentation/spi/spidev_test.c +95 −20 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <fcntl.h> #include <sys/ioctl.h> Loading @@ -34,24 +35,79 @@ static uint32_t mode; static uint8_t bits = 8; static uint32_t speed = 500000; static uint16_t delay; static int verbose; static void transfer(int fd) { int ret; uint8_t tx[] = { uint8_t default_tx[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x95, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD, 0xF0, 0x0D, }; uint8_t rx[ARRAY_SIZE(tx)] = {0, }; uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, }; char *input_tx; static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix) { int i = 0; const unsigned char *address = src; const unsigned char *line = address; unsigned char c; printf("%s | ", prefix); while (length-- > 0) { printf("%02X ", *address++); if (!(++i % line_size) || (length == 0 && i % line_size)) { if (length == 0) { while (i++ % line_size) printf("__ "); } printf(" | "); /* right close */ while (line < address) { c = *line++; printf("%c", (c < 33 || c == 255) ? 0x2E : c); } printf("\n"); if (length > 0) printf("%s | ", prefix); } } } /* * Unescape - process hexadecimal escape character * converts shell input "\x23" -> 0x23 */ int unespcape(char *_dst, char *_src, size_t len) { int ret = 0; char *src = _src; char *dst = _dst; unsigned int ch; while (*src) { if (*src == '\\' && *(src+1) == 'x') { sscanf(src + 2, "%2x", &ch); src += 4; *dst++ = (unsigned char)ch; } else { *dst++ = *src++; } ret++; } return ret; } static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) { int ret; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)tx, .rx_buf = (unsigned long)rx, .len = ARRAY_SIZE(tx), .len = len, .delay_usecs = delay, .speed_hz = speed, .bits_per_word = bits, Loading @@ -76,12 +132,9 @@ static void transfer(int fd) if (ret < 1) pabort("can't send spi message"); for (ret = 0; ret < ARRAY_SIZE(tx); ret++) { if (!(ret % 6)) puts(""); printf("%.2X ", rx[ret]); } puts(""); if (verbose) hex_dump(tx, len, 32, "TX"); hex_dump(rx, len, 32, "RX"); } static void print_usage(const char *prog) Loading @@ -97,6 +150,8 @@ static void print_usage(const char *prog) " -L --lsb least significant bit first\n" " -C --cs-high chip select active high\n" " -3 --3wire SI/SO signals shared\n" " -v --verbose Verbose (show tx buffer)\n" " -p Send data (e.g. \"1234\\xde\\xad\")\n" " -N --no-cs no chip select\n" " -R --ready slave pulls low to pause\n" " -2 --dual dual transfer\n" Loading @@ -121,12 +176,13 @@ static void parse_opts(int argc, char *argv[]) { "no-cs", 0, 0, 'N' }, { "ready", 0, 0, 'R' }, { "dual", 0, 0, '2' }, { "verbose", 0, 0, 'v' }, { "quad", 0, 0, '4' }, { NULL, 0, 0, 0 }, }; int c; c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24", lopts, NULL); c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24p:v", lopts, NULL); if (c == -1) break; Loading Loading @@ -165,9 +221,15 @@ static void parse_opts(int argc, char *argv[]) case 'N': mode |= SPI_NO_CS; break; case 'v': verbose = 1; break; case 'R': mode |= SPI_READY; break; case 'p': input_tx = optarg; break; case '2': mode |= SPI_TX_DUAL; break; Loading @@ -191,6 +253,9 @@ int main(int argc, char *argv[]) { int ret = 0; int fd; uint8_t *tx; uint8_t *rx; int size; parse_opts(argc, argv); Loading Loading @@ -235,7 +300,17 @@ int main(int argc, char *argv[]) printf("bits per word: %d\n", bits); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); transfer(fd); if (input_tx) { size = strlen(input_tx+1); tx = malloc(size); rx = malloc(size); size = unespcape((char *)tx, input_tx, size); transfer(fd, tx, rx, size); free(rx); free(tx); } else { transfer(fd, default_tx, default_rx, sizeof(default_tx)); } close(fd); Loading
drivers/spi/spidev.c +43 −12 Original line number Diff line number Diff line Loading @@ -223,7 +223,7 @@ static int spidev_message(struct spidev_data *spidev, struct spi_transfer *k_xfers; struct spi_transfer *k_tmp; struct spi_ioc_transfer *u_tmp; unsigned n, total; unsigned n, total, tx_total, rx_total; u8 *tx_buf, *rx_buf; int status = -EFAULT; Loading @@ -239,33 +239,52 @@ static int spidev_message(struct spidev_data *spidev, tx_buf = spidev->tx_buffer; rx_buf = spidev->rx_buffer; total = 0; tx_total = 0; rx_total = 0; for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; n; n--, k_tmp++, u_tmp++) { k_tmp->len = u_tmp->len; total += k_tmp->len; if (total > bufsiz) { /* Since the function returns the total length of transfers * on success, restrict the total to positive int values to * avoid the return value looking like an error. Also check * each transfer length to avoid arithmetic overflow. */ if (total > INT_MAX || k_tmp->len > INT_MAX) { status = -EMSGSIZE; goto done; } if (u_tmp->rx_buf) { /* this transfer needs space in RX bounce buffer */ rx_total += k_tmp->len; if (rx_total > bufsiz) { status = -EMSGSIZE; goto done; } k_tmp->rx_buf = rx_buf; if (!access_ok(VERIFY_WRITE, (u8 __user *) (uintptr_t) u_tmp->rx_buf, u_tmp->len)) goto done; rx_buf += k_tmp->len; } if (u_tmp->tx_buf) { /* this transfer needs space in TX bounce buffer */ tx_total += k_tmp->len; if (tx_total > bufsiz) { status = -EMSGSIZE; goto done; } k_tmp->tx_buf = tx_buf; if (copy_from_user(tx_buf, (const u8 __user *) (uintptr_t) u_tmp->tx_buf, u_tmp->len)) goto done; } tx_buf += k_tmp->len; rx_buf += k_tmp->len; } k_tmp->cs_change = !!u_tmp->cs_change; k_tmp->tx_nbits = u_tmp->tx_nbits; Loading Loading @@ -303,9 +322,9 @@ static int spidev_message(struct spidev_data *spidev, status = -EFAULT; goto done; } } rx_buf += u_tmp->len; } } status = total; done: Loading Loading @@ -684,6 +703,14 @@ static const struct file_operations spidev_fops = { static struct class *spidev_class; #ifdef CONFIG_OF static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); #endif /*-------------------------------------------------------------------------*/ static int spidev_probe(struct spi_device *spi) Loading @@ -692,6 +719,17 @@ static int spidev_probe(struct spi_device *spi) int status; unsigned long minor; /* * spidev should never be referenced in DT without a specific * compatbile string, it is a Linux implementation thing * rather than a description of the hardware. */ if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); WARN_ON(spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)); } /* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) Loading Loading @@ -758,13 +796,6 @@ static int spidev_remove(struct spi_device *spi) return 0; } static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev", Loading