Linux - Accessing SPI Bus from User Space - Part II

In this post we will see how to send and receive SPI messages to the device from userspace.

You can open the SPI device by using the below function call.


int fd = 0;
fd = open("/dev/spidev2.0", O_RDWR);
if (fd < 0)
{
printf("SPI : can't open device");
return -1;
}
You can now set the configuration for the SPI using ioctl calls.

To set the SPI mode
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1)
{
printf("SPI : can't set spi mode");
return 1;
}
To get the SPI mode
ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
if (ret == -1)
{
printf("SPI : can't get spi mode");
return 1;
}
Other SPI Bus properties can be manipulated in the same way.

Now the send a message over SPI Bus, You will need to fill up the message structure and pass it down.
struct spi_ioc_transfer mesg[1] = { 0, };

mesg[0].tx_buf = (unsigned long)out_buffer;
mesg[0].len = num_out_bytes;

ret = ioctl(fd, SPI_IOC_MESSAGE(1), mesg);
To receive SPI message
struct spi_ioc_transfer mesg[1] = { 0, };

mesg[0].rx_buf = (unsigned long)out_buffer;
mesg[0].len = num_out_bytes;

ret = ioctl(fd, SPI_IOC_MESSAGE(1), mesg);
There may be requirements where a write has to be followed by a read without toggling the Chip Select signal. You can form 2 messages as below to handle such situations.
int spidev_if_write_read(unsigned int num_out_bytes,
unsigned char *out_buffer,
unsigned int num_in_bytes,
unsigned char *in_buffer)
{
struct spi_ioc_transfer mesg[2] = { 0, };
uint8_t num_tr = 0;
int ret;

if((out_buffer != NULL) && (num_out_bytes != 0))
{
mesg[0].tx_buf = (unsigned long)out_buffer;
mesg[0].rx_buf = (unsigned long)NULL;
mesg[0].len = num_out_bytes;
mesg[0].cs_change = 0;
num_tr++;
}

if((in_buffer != NULL) && (num_in_bytes != 0))
{
mesg[1].tx_buf = (unsigned long)NULL;
mesg[1].rx_buf = (unsigned long)in_buffer;
mesg[1].len = num_in_bytes;
num_tr++;
}

if(num_tr > 0)
{
ret = ioctl(fd, SPI_IOC_MESSAGE(num_tr), mesg);
if(ret == 1)
{
return 1;
}
}

return 0;
}
The variable cs_change=0 disables the change in CS signal state between the two transactions.

Linux - Accessing SPI Bus from User Space - Part I

There needs to be a way to communicate with your hardware to write a complex device driver. In this post we will see how we can use the SPI Bus to communicate with the device from the user space.

You`ll need to configure your kernel to enable the SPI communication from userspace.

Let's first see the basic Kernel Configs needed to enable SPI support.
CONFIG_SPI
CONFIG_SPI_MASTER
CONFIG_SPI_SPIDEV

The CONFIG_SPI_SPIDEV enables the Userspace SPI framework for us.

Most Processors comes with a built in SPI Bus Module for greater performance and ease of use. If your platform happens to have one with a matching linux device driver. Enable the appropriate config.

E.g) For OMAP24xx or OMAP34xx Platforms you`ll need to enable CONFIG_SPI_OMAP24XX to use the inbuilt McSPI module of the processor.

You will now need to configure your board file to export the SPI bus to Userspace.

Add this piece of code in your board*.c file.

/* Structure for configuing McSPI */
static struct spi_board_info board_spi3_board_info[] = {
{
.modalias = "spidev", /* Expose spi to userspace */
.max_speed_hz = 500000, /* SPI Speed. */
.bus_num = 3, /* McSPI Bus Number */
.chip_select = 0, /* ChipSelect for McSPI */
.mode = 0, /* SPI Mode */
},
};

and add register the SPI bus in the board_init function in the board*.c file.
/*Register SPI device*/
spi_register_board_info(board_spi3_board_info, ARRAY_SIZE(board_spi3_board_info));

Now, when you boot up your system. You should see your spi device in the filesystem at /dev path.
e.g) /dev/spidev2.0 ( 2 - SPI bus number, 0 - Chip Select)

We can use the standard open,read,write and close api's along with /dev/spidevx.y device to send and receive SPI messages to the device.

In the next post we`ll see how to use the /dev/spidevx.y to send and receive messages.