|
| 1 | +#include <linux/delay.h> |
| 2 | +#include <linux/spi/spi.h> |
| 3 | +#include <linux/spi/spidev.h> |
| 4 | + |
| 5 | +#define SPI_SYNC 0x5AU |
| 6 | +#define SPI_HACK 0x79U |
| 7 | +#define SPI_DACK 0x85U |
| 8 | +#define SPI_NACK 0x1FU |
| 9 | +#define SPI_CHECKSUM_START 0xABU |
| 10 | + |
| 11 | +struct __attribute__((packed)) spi_header { |
| 12 | + u8 sync; |
| 13 | + u8 endpoint; |
| 14 | + uint16_t tx_len; |
| 15 | + uint16_t max_rx_len; |
| 16 | +}; |
| 17 | + |
| 18 | +struct spi_panda_transfer { |
| 19 | + __u64 rx_buf; |
| 20 | + __u64 tx_buf; |
| 21 | + __u32 tx_length; |
| 22 | + __u32 rx_length_max; |
| 23 | + __u32 timeout; |
| 24 | + __u8 endpoint; |
| 25 | + __u8 expect_disconnect; |
| 26 | +}; |
| 27 | + |
| 28 | +static u8 panda_calc_checksum(u8 *buf, u16 length) { |
| 29 | + int i; |
| 30 | + u8 checksum = SPI_CHECKSUM_START; |
| 31 | + for (i = 0U; i < length; i++) { |
| 32 | + checksum ^= buf[i]; |
| 33 | + } |
| 34 | + return checksum; |
| 35 | +} |
| 36 | + |
| 37 | +static long panda_wait_for_ack(struct spidev_data *spidev, u8 ack_val, u8 length) { |
| 38 | + int i; |
| 39 | + int ret; |
| 40 | + for (i = 0; i < 1000; i++) { |
| 41 | + ret = spidev_sync_read(spidev, length); |
| 42 | + if (ret < 0) { |
| 43 | + return ret; |
| 44 | + } |
| 45 | + |
| 46 | + if (spidev->rx_buffer[0] == ack_val) { |
| 47 | + return 0; |
| 48 | + } else if (spidev->rx_buffer[0] == SPI_NACK) { |
| 49 | + return -2; |
| 50 | + } |
| 51 | + if (i > 20) usleep_range(10, 20); |
| 52 | + } |
| 53 | + return -1; |
| 54 | +} |
| 55 | + |
| 56 | +static long panda_transfer_raw(struct spidev_data *spidev, struct spi_device *spi, unsigned long arg) { |
| 57 | + u16 rx_len; |
| 58 | + long retval = -1; |
| 59 | + struct spi_header header; |
| 60 | + struct spi_panda_transfer pt; |
| 61 | + |
| 62 | + struct spi_transfer t = { |
| 63 | + .len = 0, |
| 64 | + .tx_buf = spidev->tx_buffer, |
| 65 | + .rx_buf = spidev->rx_buffer, |
| 66 | + .speed_hz = spidev->spi->max_speed_hz, |
| 67 | + }; |
| 68 | + |
| 69 | + struct spi_message m; |
| 70 | + spi_message_init(&m); |
| 71 | + spi_message_add_tail(&t, &m); |
| 72 | + |
| 73 | + // read struct from user |
| 74 | + if (!access_ok(VERIFY_WRITE, arg, sizeof(pt))) { |
| 75 | + return -1; |
| 76 | + } |
| 77 | + if (copy_from_user(&pt, (void __user *)arg, sizeof(pt))) { |
| 78 | + return -1; |
| 79 | + } |
| 80 | + dev_dbg(&spi->dev, "ep: %d, tx len: %d\n", pt.endpoint, pt.tx_length); |
| 81 | + |
| 82 | + // send header |
| 83 | + header.sync = 0x5a; |
| 84 | + header.endpoint = pt.endpoint; |
| 85 | + header.tx_len = pt.tx_length; |
| 86 | + header.max_rx_len = pt.rx_length_max; |
| 87 | + memcpy(spidev->tx_buffer, &header, sizeof(header)); |
| 88 | + spidev->tx_buffer[sizeof(header)] = panda_calc_checksum(spidev->tx_buffer, sizeof(header)); |
| 89 | + |
| 90 | + t.len = sizeof(header) + 1; |
| 91 | + retval = spidev_sync(spidev, &m); |
| 92 | + if (retval < 0) { |
| 93 | + dev_dbg(&spi->dev, "spi xfer failed %ld\n", retval); |
| 94 | + return retval; |
| 95 | + } |
| 96 | + |
| 97 | + // wait for ACK |
| 98 | + retval = panda_wait_for_ack(spidev, SPI_HACK, 1); |
| 99 | + if (retval < 0) { |
| 100 | + dev_dbg(&spi->dev, "no header ack %ld\n", retval); |
| 101 | + return retval; |
| 102 | + } |
| 103 | + |
| 104 | + // send data |
| 105 | + dev_dbg(&spi->dev, "sending data\n"); |
| 106 | + retval = copy_from_user(spidev->tx_buffer, (const u8 __user *)(uintptr_t)pt.tx_buf, pt.tx_length); |
| 107 | + spidev->tx_buffer[pt.tx_length] = panda_calc_checksum(spidev->tx_buffer, pt.tx_length); |
| 108 | + t.len = pt.tx_length + 1; |
| 109 | + retval = spidev_sync(spidev, &m); |
| 110 | + |
| 111 | + if (pt.expect_disconnect) { |
| 112 | + return 0; |
| 113 | + } |
| 114 | + |
| 115 | + // wait for ACK |
| 116 | + retval = panda_wait_for_ack(spidev, SPI_DACK, 3); |
| 117 | + if (retval < 0) { |
| 118 | + dev_dbg(&spi->dev, "no data ack\n"); |
| 119 | + return retval; |
| 120 | + } |
| 121 | + |
| 122 | + // get response |
| 123 | + t.rx_buf = spidev->rx_buffer + 3; |
| 124 | + rx_len = (spidev->rx_buffer[2] << 8) | (spidev->rx_buffer[1]); |
| 125 | + dev_dbg(&spi->dev, "rx len %u\n", rx_len); |
| 126 | + if (rx_len > pt.rx_length_max) { |
| 127 | + dev_dbg(&spi->dev, "RX len greater than max\n"); |
| 128 | + return -1; |
| 129 | + } |
| 130 | + |
| 131 | + // do the read |
| 132 | + t.len = rx_len + 1; |
| 133 | + retval = spidev_sync(spidev, &m); |
| 134 | + if (retval < 0) { |
| 135 | + dev_dbg(&spi->dev, "spi xfer failed %ld\n", retval); |
| 136 | + return retval; |
| 137 | + } |
| 138 | + if (panda_calc_checksum(spidev->rx_buffer, 3 + rx_len + 1) != 0) { |
| 139 | + dev_dbg(&spi->dev, "bad checksum\n"); |
| 140 | + return -1; |
| 141 | + } |
| 142 | + |
| 143 | + retval = copy_to_user((u8 __user *)(uintptr_t)pt.rx_buf, spidev->rx_buffer + 3, rx_len); |
| 144 | + |
| 145 | + return rx_len; |
| 146 | +} |
| 147 | + |
| 148 | +static long panda_transfer(struct spidev_data *spidev, struct spi_device *spi, unsigned long arg) { |
| 149 | + int i; |
| 150 | + int ret; |
| 151 | + dev_dbg(&spi->dev, "=== XFER start ===\n"); |
| 152 | + for (i = 0; i < 20; i++) { |
| 153 | + ret = panda_transfer_raw(spidev, spi, arg); |
| 154 | + if (ret >= 0) { |
| 155 | + break; |
| 156 | + } |
| 157 | + } |
| 158 | + dev_dbg(&spi->dev, "took %d tries\n", i+1); |
| 159 | + return ret; |
| 160 | +} |
0 commit comments