我可以在 /dev/spidev 文件描述符上 select() 吗?
Can I select() on a /dev/spidev file descriptor?
我正在维护一些通过 SPI 与 FPGA 通信的用户空间代码。现在它正在轮询以查看是否有数据可以采取行动,我对此并不感到兴奋。通信线程的(高度简化的)结构如下所示:
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
}
}
}
我真的更喜欢基于事件的解决方案而不是轮询和睡眠。我想到的第一件事是在 spidev 文件描述符上执行 select() 而不是每隔 X 秒检查一些寄存器,比如
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);
}
问题是我找不到任何人这样处理 SPI 的例子,我想知道是否有充分的理由这样做。 /dev/spidev可以这么用吗? 它会做一些像 always/never 变成 "ready to read" 这样的傻事吗?可以让按照我想要的方式行事吗?它依赖于硬件吗?如果有必要,我不反对对内核驱动程序进行一些黑客攻击,但我不太确定 if/where 我需要查看。
Can I select() on a /dev/spidev file descriptor?
没有
spidev documentation 表示
At this time there is no async I/O support; everything is purely synchronous.
更重要的是 spidev driver 不支持轮询文件操作。 select() 系统调用要求设备驱动程序支持轮询 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 };
甚至在这可能是一个 Linux SPI 驱动程序问题之前,您必须了解如何从 FPGA 获取状态信息。
除非 FPGA 正在执行驱动中断或注意线之类的操作,否则 SPI 主机(可能连接到 CPU)将不得不执行 SPI 操作以轮询 FPGA。因此,除非或直到您在内核 space 中有定期执行该轮询的代码,否则驱动程序中没有可用信息供用户 space 有意义地 select() 使用。
如果你从 FPGA 得到一个注意信号回到处理器(这取决于是否有其他东西正在共享它,可能就像不按顺序驱动 MISO 一样简单)那么你可能会监控它作为一个在内核 SPI 驱动程序中中断,或单独使用用户 space 中断接口,您可以在该接口上 select().
如果不是,您将不得不权衡通过 SPI 将状态轮询移至自定义内核驱动程序与将其留在用户 space 中的权衡。
我正在维护一些通过 SPI 与 FPGA 通信的用户空间代码。现在它正在轮询以查看是否有数据可以采取行动,我对此并不感到兴奋。通信线程的(高度简化的)结构如下所示:
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
}
}
}
我真的更喜欢基于事件的解决方案而不是轮询和睡眠。我想到的第一件事是在 spidev 文件描述符上执行 select() 而不是每隔 X 秒检查一些寄存器,比如
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);
}
问题是我找不到任何人这样处理 SPI 的例子,我想知道是否有充分的理由这样做。 /dev/spidev可以这么用吗? 它会做一些像 always/never 变成 "ready to read" 这样的傻事吗?可以让按照我想要的方式行事吗?它依赖于硬件吗?如果有必要,我不反对对内核驱动程序进行一些黑客攻击,但我不太确定 if/where 我需要查看。
Can I select() on a /dev/spidev file descriptor?
没有
spidev documentation 表示
At this time there is no async I/O support; everything is purely synchronous.
更重要的是 spidev driver 不支持轮询文件操作。 select() 系统调用要求设备驱动程序支持轮询 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 };
甚至在这可能是一个 Linux SPI 驱动程序问题之前,您必须了解如何从 FPGA 获取状态信息。
除非 FPGA 正在执行驱动中断或注意线之类的操作,否则 SPI 主机(可能连接到 CPU)将不得不执行 SPI 操作以轮询 FPGA。因此,除非或直到您在内核 space 中有定期执行该轮询的代码,否则驱动程序中没有可用信息供用户 space 有意义地 select() 使用。
如果你从 FPGA 得到一个注意信号回到处理器(这取决于是否有其他东西正在共享它,可能就像不按顺序驱动 MISO 一样简单)那么你可能会监控它作为一个在内核 SPI 驱动程序中中断,或单独使用用户 space 中断接口,您可以在该接口上 select().
如果不是,您将不得不权衡通过 SPI 将状态轮询移至自定义内核驱动程序与将其留在用户 space 中的权衡。