# SPI in Linux ###### tags: `Linux kernel` {%hackmd theme-dark %} ## [Access spi in user space](https://www.kernel.org/doc/html/latest/spi/spidev.html) [第三方的 library](https://github.com/azorg/spi) ```c int spi_init(spi_t *self, const char *device, int mode, int bits, int speed) { self->fd = open(device, O_RDWR); if (mode) { self->mode = (__u8) mode; ioctl(self->fd, SPI_IOC_WR_MODE, &self->mode); } ioctl(self->fd, SPI_IOC_RD_MODE, &self->mode); ioctl(self->fd, SPI_IOC_RD_LSB_FIRST, &self->lsb); if (bits) { self->bits = (__u8) bits; ioctl(self->fd, SPI_IOC_WR_BITS_PER_WORD, &self->bits); } ioctl(self->fd, SPI_IOC_RD_BITS_PER_WORD, &self->bits); if (speed) { self->speed = (__u32) speed; ioctl(self->fd, SPI_IOC_WR_MAX_SPEED_HZ, &self->speed); } ioctl(self->fd, SPI_IOC_RD_MAX_SPEED_HZ, &self->speed); return SPI_ERR_NONE; } int spi_exchange(spi_t *self, char *rx_buf, const char *tx_buf, int len) { self->xfer.tx_buf = (__u64) tx_buf; self->xfer.rx_buf = (__u64) rx_buf; self->xfer.len = (__u32) len; ioctl(self->fd, SPI_IOC_MESSAGE(1), &self->xfer); return SPI_ERR_NONE; } ``` ## Trace code in kernel https://elixir.bootlin.com/linux/latest/source/drivers/spi/spidev.c#L345 設定最終都是呼叫到 `spi_setup`。 ```c // spidev_ioctl case SPI_IOC_WR_MAX_SPEED_HZ: { get_user(tmp, (__u32 __user *)arg); save = spi->max_speed_hz; spi->max_speed_hz = tmp; retval = spi_setup(spi); pi->max_speed_hz = save; break; } int spi_setup(struct spi_device *spi) { if (spi->controller->setup) { status = spi->controller->setup(spi); } return status; } ``` `spi_controller` 裡面有需要各平台執行的 function pointer。`setup` 最後會呼叫到 controller 的 `setup`,是由 driver 註冊的。 ```c struct spi_device { struct device dev; struct spi_controller *controller; struct spi_controller *master; /* Compatibility layer */ u32 max_speed_hz; // ... }; struct spi_controller { struct device dev; /* Setup mode and clock, etc (spi driver may call many times). * * IMPORTANT: this may be called when transfers to another * device are active. DO NOT UPDATE SHARED REGISTERS in ways * which could break those transfers. */ int (*setup)(struct spi_device *spi); /* Bidirectional bulk transfers * * + The transfer() method may not sleep; its main role is * just to add the message to the queue. * + For now there's no remove-from-queue operation, or * any other request management * + To a given spi_device, message queueing is pure fifo * * + The controller's main job is to process its message queue, * selecting a chip (for masters), then transferring data * + If there are multiple spi_device children, the i/o queue * arbitration algorithm is unspecified (round robin, fifo, * priority, reservations, preemption, etc) * * + Chipselect stays active during the entire message * (unless modified by spi_transfer.cs_change != 0). * + The message transfers use clock and SPI mode parameters * previously established by setup() for this device */ int (*transfer)(struct spi_device *spi, struct spi_message *mesg); }; ``` 以 RP4 為例,要在樹莓派 fork 的 linux 版本中才找的到對應的 driver https://github.com/raspberrypi/linux/blob/rpi-3.6.y/drivers/spi/spi-bcm2708.c ```c tatic int __devinit bcm2708_spi_probe(struct platform_device *pdev) { INIT_WORK(&bs->work, bcm2708_work); // ... master->setup = bcm2708_spi_setup; master->transfer = bcm2708_spi_transfer; } static int bcm2708_spi_transfer(struct spi_device *spi, struct spi_message *msg) { queue_work(bs->workq, &bs->work); } ``` 可以看到 `setup` 是直接執行,而 `transfer` 則是交給 scheduler 去安排,`bcm2708_work` 才會是真的執行的地方。