Skip to content

Commit 70c7763

Browse files
adeebshihadehComma Device
and
Comma Device
authored
SPI kernel driver (commaai#1497)
* kernel driver * fix checksum check * cleanup --------- Co-authored-by: Comma Device <[email protected]>
1 parent a9a3227 commit 70c7763

File tree

9 files changed

+1192
-1
lines changed

9 files changed

+1192
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
*.tmp
12
*.pyc
23
.*.swp
34
.*.swo

drivers/spi/.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
spidev.c
2+
*.ko
3+
*.cmd
4+
*.mod
5+
*.symvers
6+
*.order
7+
*.mod.c

drivers/spi/Makefile

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
obj-m += spidev_panda.o
2+
3+
KDIR := /lib/modules/$(shell uname -r)/build
4+
PWD := $(shell pwd)
5+
6+
# GCC9 bug, apply kernel patch instead?
7+
# https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0b999ae3614d09d97a1575936bcee884f912b10e
8+
ccflags-y := -Wno-missing-attributes
9+
10+
default:
11+
$(MAKE) -C $(KDIR) M=$(PWD) modules
12+
13+
clean:
14+
$(MAKE) -C $(KDIR) M=$(PWD) clean

drivers/spi/load.sh

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
set -e
3+
4+
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
5+
cd $DIR
6+
7+
make -j8
8+
9+
sudo su -c "echo spi0.0 > /sys/bus/spi/drivers/spidev/unbind" || true
10+
11+
sudo dmesg -C
12+
13+
#sudo rmmod -f spidev_panda
14+
sudo rmmod spidev_panda || true
15+
sudo insmod spidev_panda.ko
16+
17+
sudo su -c "echo 'file $DIR/spidev_panda.c +p' > /sys/kernel/debug/dynamic_debug/control"
18+
sudo su -c "echo 'file $DIR/spi_panda.h +p' > /sys/kernel/debug/dynamic_debug/control"
19+
20+
sudo lsmod
21+
22+
echo "loaded"
23+
ls -la /dev/spi*
24+
sudo chmod 666 /dev/spi*
25+
ipython -c "from panda import Panda; print(Panda.list())"
26+
KERN=1 ipython -c "from panda import Panda; print(Panda.list())"
27+
dmesg

drivers/spi/patch

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
53c53,54
2+
< #define SPIDEV_MAJOR 153 /* assigned */
3+
---
4+
> int SPIDEV_MAJOR = 0;
5+
> //#define SPIDEV_MAJOR 153 /* assigned */
6+
354a356,358
7+
>
8+
> #include "spi_panda.h"
9+
>
10+
413,414c417,419
11+
< retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
12+
< (__u8 __user *)arg);
13+
---
14+
> retval = panda_transfer(spidev, spi, arg);
15+
> //retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
16+
> // (__u8 __user *)arg);
17+
697,698d701
18+
< { .compatible = "rohm,dh2228fv" },
19+
< { .compatible = "lineartechnology,ltc2488" },
20+
831c834
21+
< .name = "spidev",
22+
---
23+
> .name = "spidev_panda",
24+
856c859
25+
< status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
26+
---
27+
> status = register_chrdev(0, "spi", &spidev_fops);
28+
860c863,865
29+
< spidev_class = class_create(THIS_MODULE, "spidev");
30+
---
31+
> SPIDEV_MAJOR = status;
32+
>
33+
> spidev_class = class_create(THIS_MODULE, "spidev_panda");

drivers/spi/pull-src.sh

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/bash
2+
set -e
3+
4+
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
5+
cd $DIR
6+
7+
rm -f spidev.c
8+
wget https://raw.githubusercontent.com/commaai/agnos-kernel-sdm845/master/drivers/spi/spidev.c
9+
10+
# diff spidev.c spidev_panda.c > patch
11+
# git diff --no-index spidev.c spidev_panda.c
12+
patch -o spidev_panda.c spidev.c -i patch

drivers/spi/spi_panda.h

+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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

Comments
 (0)