6

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.

laughingcoyote
  • 259
  • 2
  • 9
  • 1
    `select()` should work. Data is ready to read as soon as there is a single byte ready in the kernel's buffer. However, I cannot guarantee that the author of the device driver didn't cut any corners. – fuz Apr 14 '16 at 14:32
  • If the driver is sound then `select()` should work. While you're mindful of these issues would be a good time write a suitable test -- even if everything works on the device you're now targeting, you will be thankful for the test if you later try to build for a device or driver on which it fails. – John Bollinger Apr 14 '16 at 14:52
  • 1
    *"I'd really prefer an event-based solution"* -- If the SPI driver is forcing you to poll because it doesn't use interrupts, then there's no magical routine that will transform the situation. Using **select()** (which may not work with the ***user-space*** SPI driver) would only move the polling out of your code, and hide in behind a libc call. If you want event-driven I/O, then you have to use/write a driver that generates & services interrupts. – sawdust Apr 14 '16 at 18:05
  • What is `ioctl_read_some_fpga_register_that_says_if_theres_data()`? It sounds like that's the issue, not SPI. How would `select` even help you? It's not SPI that tells whether there's data to read or not but some FPGA register. Does that FPGA register support `select`? That's what you're waiting for, not SPI. – David Schwartz Apr 14 '16 at 18:32
  • Thanks. I'm still getting familiar with theory behind SPI interfaces. I've spent a little while reading the [appropriate TI technical document](http://www.ti.com/lit/pdf/sprufh1) for my SPI device (and a few other resources) and I'm increasingly certain that I either need to keep polling or see if my FPGA has some interrupt I can enable & service when there's data to process. Just to be sure - the Receive Interrupt in 2.7.1.2 is *not* what I'm looking for, right? That would only fire after I'd already lit up the bus, right? – laughingcoyote Apr 14 '16 at 20:07
  • I can't be sure with just what you've shown, but probably so. Try using a blocking `read` and see if it works. – David Schwartz Apr 15 '16 at 07:37
  • 1
    Be sure you understand the Linux driver model for SPI. See [spi summary](https://www.kernel.org/doc/Documentation/spi/spi-summary). The TI doc would be for the SPI master controller; its interrupts are not want you want. The spidev driver is a user-space SPI protocol driver, i.e. a driver for the SPI slave device, your FPGA.. If your FPGA can generate an interrupt, then you'd probably connect it to a GPIO to trigger an interrupt.. – sawdust Apr 15 '16 at 08:06

2 Answers2

2

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 };
sawdust
  • 16,103
  • 3
  • 40
  • 50
  • 1
    If it has neither async I/O support nor any way to block until the device is ready to provide data, that's pretty sad. :( – David Schwartz Apr 14 '16 at 18:29
  • @DavidSchwartz -- The spidev driver was not intended to be a production-quality driver. It's a user-space kludge to allow rapid development and testing of a protocol driver in the SPI driver model. If the SPI slave device needs attention, there's typically a HW kludge such as a GPIO interrupt. – sawdust Apr 14 '16 at 18:39
1

Even before this can be a Linux SPI driver question, you have to look at how status information can be obtained from the FPGA.

Unless the FPGA is doing something like driving an interrupt or attention line, the SPI master (presumably attached to the CPU) is going to have to do an SPI operation to poll the FPGA. So unless or until you have code in kernel space which periodically does that polling, there's no information available within the driver for userspace to meaningfully select() upon.

If you get an attention signal from the FPGA back to the processor (which depending on if anything else is sharing it, could be as simple as driving MISO out-of-turn) then you could presumably monitor that as an interrupt either in a kernel SPI driver, or by separately using a userspace interrupt interface on which you can select().

If not, you'll have to evaluate the tradeoffs of moving the polling of status via SPI into a custom kernel driver, vs. leaving it in userspace.

Chris Stratton
  • 39,853
  • 6
  • 84
  • 117