# 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` 才會是真的執行的地方。