I'm maintaining some userspace code that talks to a FPGA via SPI. Right now it's polling to see if there's data to act on, which I'm not thrilled about. The (heavily-simplified) structure of the comm thread looks like this:
int spi_fd;
void do_transfer(char *buf, int len)
{
struct spi_ioc_transfer xfer;
memset(xfer, 0, sizeof(xfer));
ioctl_tell_some_fpga_register_heads_up();
xfer[0].len = len;
xfer[0].tx_buf = NULL;
xfer[0].rx_buf = buf;
ioctl(spi_fd, SPI_IOC_MESSAGE(1), xfer);
ioctl_tell_some_fpga_register_were_done();
}
void *comm_thread(void arg)
{
uint8_t config = SPI_MODE_3;
__u32 speed = 4000000;
char buffer[5120];
spi_fd = open("/dev/spidev1.0", O_RDWR);
ioctl(spi_fd, SPI_IOC_WR_MODE, &config);
ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
while(1) {
sleep(2); //ugh
if(ioctl_read_some_fpga_register_that_says_if_theres_data())
{
do_transfer(buffer, some_small_number_of_bytes());
do_stuff(buffer); //you get the picture
}
}
}
I'd really prefer an event-based solution over poll-and-sleep. The first thing that came to mind was doing a select() on the spidev file descriptor instead of checking some register every X seconds, something like
fd_set myset;
while(1) {
FD_ZERO(&myset);
FD_SET(spi_fd, &myset);
select(spi_fd + 1, &myset, NULL, NULL, NULL);
do_transfer(buffer, some_small_number_of_bytes());
do_stuff(buffer);
}
Thing is I can't find any examples of people handling SPI like that, and I'm wondering if maybe there's a good reason for it. Can /dev/spidev be used this way? Will it do something goofy like always/never being "ready to read"? Can it be made to behave the way I want? Is it hardware dependent? I'm not averse to a little kernel driver hacking if it's necessary, but I'm not really sure if/where I need to be looking.
/dev/spidev wasn't showing up because files were missing in /lib/modules. I cross compiled the rt kernel and when I was installing it on the target I forgot to copy the /lib/modules/4.9.80-rt62-v7+ and /lib/firmware. After copying the contents of those directories onto the taget and rebooting /dev/spidev exists!
1 Enable SPI on the Raspberry Pi In your Pi’s terminal, run sudo raspi-config Go to Advanced Options > SPI Choose “Yes” for both questions then select Finish to ... 2 Install spidev Spidev is a python module that allows us to interface with the Pi’s SPI bus. ... 3 Python script
Under most circumstances, we should avoid the use of file descriptors 0, 1 and 2, since these file descriptors are assigned to standard in, standard out and standard error. In the example shown, each time we read from infile, our position in the file will be incremented by one line.
First, you can assign a file descriptor to an input file with this syntax: where 3 is the file descriptor we have chosen to use and file is the name of the file. Under most circumstances, we should avoid the use of file descriptors 0, 1 and 2, since these file descriptors are assigned to standard in, standard out and standard error.
Can I select() on a /dev/spidev file descriptor?
No.
The spidev documentation states
At this time there is no async I/O support; everything is purely synchronous.
More importantly the spidev driver does not support a poll file operation. The select() syscall requires the device driver to support a poll fops.
670 static const struct file_operations spidev_fops = {
671 .owner = THIS_MODULE,
672 /* REVISIT switch to aio primitives, so that userspace
673 * gets more complete API coverage. It'll simplify things
674 * too, except for the locking.
675 */
676 .write = spidev_write,
677 .read = spidev_read,
678 .unlocked_ioctl = spidev_ioctl,
679 .compat_ioctl = spidev_compat_ioctl,
680 .open = spidev_open,
681 .release = spidev_release,
682 .llseek = no_llseek,
683 };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With