Comprehensive Guide to spidev Driver ioctl Commands: Beyond SPI_IOC_MESSAGE

Comprehensive Guide to spidev Driver ioctl Commands: Beyond SPI_IOC_MESSAGE

The Linux spidev driver provides a versatile ioctl interface for configuring SPI devices and controlling data transfers. While SPI_IOC_MESSAGE handles data transactions, other ioctl commands manage critical settings like SPI mode, clock speed, and hardware-specific behaviors. This article explores all ioctl commands supported by spidev, detailing their user-space usage, kernel-space handling, and impact on hardware.


1. Overview of spidev ioctl Commands

The spidev driver supports the following ioctl commands, grouped by functionality:

Article content

2. General ioctl Dispatch Mechanism

Kernel-Space Workflow

  • System Call Entry:

User-space calls ioctl(fd, cmd, arg), which triggers a transition to kernel mode.

The kernel’s VFS layer maps the fd to the spidev driver’s file_operations structure.

The spidev_ioctl function is invoked (drivers/spi/spidev.c).

  • Command Validation: The cmd is decoded using IOCTYPE(cmd) to verify it targets the SPI subsystem (SPI_IOC_MAGIC).
  • Parameter Handling:

Read Commands (e.g., SPI_IOC_RD_MODE): Copy data from kernel to user space.

Write Commands (e.g., SPI_IOC_WR_MODE): Copy data from user to kernel space.

  • Hardware Configuration:

The driver updates the struct spi_device’s settings (e.g., spi->mode, spi->max_speed_hz). Changes are propagated to the SPI controller driver (e.g., spi-bcm2835).


3. Detailed Command Breakdown

3.1 SPI Mode Configuration (SPI_IOC_RD_MODE, SPI_IOC_WR_MODE)

Purpose: Set CPOL (clock polarity) and CPHA (clock phase). User-Space Example:

uint8_t mode;
ioctl(fd, SPI_IOC_RD_MODE, &mode);  // Read current mode
mode |= SPI_MODE_3;                 // Set CPOL=1, CPHA=1
ioctl(fd, SPI_IOC_WR_MODE, &mode);  // Apply new mode        

Kernel Handling:

  • spidev_ioctl calls spi_set_mode() to update spi->mode.
  • The SPI controller driver applies the mode during transfers.

Note: SPI_IOC_RD_MODE32/SPI_IOC_WR_MODE32 handle 32-bit mode flags for exotic controllers.


3.2 Bits per Word (SPI_IOC_RD_BITS_PER_WORD, SPI_IOC_WR_BITS_PER_WORD)

Purpose: Set the number of bits per SPI word (e.g., 8, 16). User-Space Example:

uint8_t bits = 16;
ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);        

Kernel Handling:

  • Updates spi->bits_per_word.
  • The controller driver splits transfers into chunks of bits size.

Error Case: Returns -EINVAL if the controller doesn’t support the requested bits.


3.3 Clock Speed (SPI_IOC_RD_MAX_SPEED_HZ, SPI_IOC_WR_MAX_SPEED_HZ)

Purpose: Set the SPI clock frequency (Hz). User-Space Example:

uint32_t speed = 1000000; // 1 MHz
ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);        

Kernel Handling:

  • Updates spi->max_speed_hz.
  • The controller driver scales the clock divider to match the speed.

Note: Actual speed may differ if the controller can’t match the exact value.


3.4 Data Order (SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST)

Purpose: Transmit data LSB-first (default: MSB-first). User-Space Example:

uint8_t lsb = 1;
ioctl(fd, SPI_IOC_WR_LSB_FIRST, &lsb);        

Kernel Handling:

  • Sets spi->lsb_first.
  • The controller reverses bit order during transfers.


3.5 Loopback Mode (SPI_IOC_RD_LOOP, SPI_IOC_WR_LOOP)

Purpose: Echo transmitted data to the receive buffer (for debugging). User-Space Example:

uint8_t loop = 1;
ioctl(fd, SPI_IOC_WR_LOOP, &loop);        

Kernel Handling:

  • Enables SPI_LOOP in spi->mode.
  • The controller internally connects TX to RX.


3.6 Chip Select Configuration

  • SPI_IOC_RD_CS_HIGH/SPI_IOC_WR_CS_HIGH: Set CS active high (default: active low).
  • SPI_IOC_RD_NO_CS/SPI_IOC_WR_NO_CS: Disable automatic CS control (manual GPIO toggling).

User-Space Example:

uint8_t cs_high = 1;
ioctl(fd, SPI_IOC_WR_CS_HIGH, &cs_high);        

3.7 3-Wire Mode (SPI_IOC_RD_3WIRE, SPI_IOC_WR_3WIRE)

Purpose: Enable half-duplex mode with a shared MISO/MOSI line. Kernel Handling:

  • Sets spi->mode to SPI_3WIRE.
  • The controller driver reconfigures the data line.


3.8 Number of Bits (SPI_IOC_RD_NBITS, SPI_IOC_WR_NBITS)

Purpose: Set non-byte-aligned transfer sizes (e.g., 12 bits). User-Space Example:

uint8_t nbits = 12;
ioctl(fd, SPI_IOC_WR_NBITS, &nbits);        

Note: Rarely used; requires controller support.


4. Error Handling and Common Issues

Article content

5. Example: Full SPI Configuration

User-Space Code:

int fd = open("/dev/spidev0.0", O_RDWR);

// Set mode to SPI_MODE_3 (CPOL=1, CPHA=1)
uint8_t mode = SPI_MODE_3;
ioctl(fd, SPI_IOC_WR_MODE, &mode);

// Set 8 bits per word
uint8_t bits = 8;
ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);

// Set clock speed to 1 MHz
uint32_t speed = 1000000;
ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

// Perform transfer
struct spi_ioc_transfer tr = { ... };
ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

        

Kernel Flow:

  1. spidev_ioctl updates spi_device settings.
  2. During SPI_IOC_MESSAGE, the controller applies the configured mode, speed, and bits.


6. Conclusion

The spidev driver’s ioctl interface provides fine-grained control over SPI hardware, enabling developers to optimize performance and compatibility. By understanding how each command configures the kernel’s SPI subsystem and interacts with controller drivers, you can debug complex issues and tailor SPI behavior to your application’s needs. Whether adjusting clock speeds for sensor compatibility or enabling loopback for testing, these commands form the backbone of Linux’s SPI user-space support.

To view or add a comment, sign in

More articles by David Zhu

Insights from the community

Others also viewed

Explore topics