2016-04-14 8 views
6

Ich behalte einige Userspace-Code, der über SPI mit einem FPGA spricht. Im Augenblick wird abgefragt, ob es Daten zum Handeln gibt, über die ich nicht begeistert bin. Die (stark vereinfacht) Struktur des comm Thread sieht wie folgt aus:Kann ich() auf einem Dateideskriptor/dev/spidev auswählen?

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 
     } 
    } 
} 

Ich würde wirklich lieber eine ereignisbasierte Lösung über Umfrage-und-Schlaf. Das erste, was in den Sinn kam war dabei ein select() auf der spidev Dateideskriptors statt einige Register alle X Sekunden überprüft, so etwas wie

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); 
} 

Sache ist, kann ich keine Beispiele von Menschen, wie Umgang mit SPI finden und ich frage mich, ob es vielleicht einen guten Grund dafür gibt. Kann/dev/spidev auf diese Weise verwendet werden? Wird es etwas tun, doof wie immer/nie "Read Read"? Kann es sein gemacht so zu verhalten, wie ich will? Ist es Hardware abhängig? Ich bin nicht abgeneigt, wenn ein kleiner Kernel-Treiber hackt, wenn es notwendig ist, aber ich bin mir nicht sicher, ob/wohin ich schauen muss.

+1

'select()' sollte funktionieren. Die Daten können gelesen werden, sobald ein einzelnes Byte im Puffer des Kernels bereitsteht. Ich kann jedoch nicht garantieren, dass der Autor des Gerätetreibers keine Ecken und Kanten abgeschnitten hat. – fuz

+0

Wenn der Treiber Sound ist, sollte 'select()' funktionieren. Während Sie sich dieser Probleme bewusst sind, sollten Sie einen geeigneten Test schreiben. Selbst wenn alles auf dem Gerät funktioniert, auf das Sie jetzt zielen, werden Sie für den Test dankbar sein, wenn Sie später versuchen, ein Gerät oder einen Treiber zu erstellen woran es scheitert. –

+1

* "Ich würde wirklich eine ereignisbasierte Lösung bevorzugen" * - Wenn der SPI-Treiber Sie zum Abfragen zwingt, weil er keine Interrupts verwendet, dann gibt es keine magische Routine, die die Situation verändern wird.Die Verwendung von ** select() ** (was möglicherweise nicht mit dem *** user-space *** SPI-Treiber funktioniert) würde nur die Abfrage Ihres Codes verschieben und sich hinter einem libc-Aufruf verbergen. Wenn Sie ereignisgesteuerte E/A verwenden möchten, müssen Sie einen Treiber verwenden/schreiben, der Interrupts für Dienste generiert. – sawdust

Antwort

2

Kann ich() auf einem Dateideskriptor/dev/spidev auswählen?

Nr
Die spidev documentation Staaten

At this time there is no async I/O support; everything is purely synchronous. 

Noch wichtiger ist die spidev driver keine Umfrage Dateioperation unterstützen. Der select() syscall erfordert, dass der Gerätetreiber einen Poll-Fops unterstützt.

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 }; 
+1

Wenn es weder Unterstützung für asynchrone E/A noch irgendeine Möglichkeit zum Blockieren hat, bis das Gerät bereit ist, Daten bereitzustellen, ist das ziemlich traurig. :( –

+0

@DavidSchwartz - Der spidev-Treiber sollte kein Treiber für Produktionsqualität sein. Es ist ein User-Space-Kluding, um eine schnelle Entwicklung und das Testen eines Protokolltreibers im SPI-Treibermodell zu ermöglichen. Wenn das SPI-Slave-Gerät Aufmerksamkeit benötigt , gibt es typischerweise einen HW-Klumpen wie einen GPIO-Interrupt. – sawdust

1

Schon vorher eine Linux SPI-Treiber Frage sein kann, muss man schauen, wie Statusinformationen aus dem FPGA erhalten werden.

Wenn der FPGA keine Unterbrechung oder Aufmerksamkeitsleitung ansteuert, muss der SPI-Master (vermutlich an die CPU angeschlossen) eine SPI-Operation durchführen, um den FPGA abzufragen. Also, es sei denn, oder bis Sie Code im Kernel-Space haben, der periodisch diese Abfrage durchführt, gibt es keine Information innerhalb des Treibers für den Benutzerbereich, um sinngemäß() auszuwählen.

Wenn Sie ein Aufmerksamkeitssignal vom FPGA zurück zum Prozessor erhalten (was je nachdem, ob etwas anderes es teilt, könnte so einfach sein wie MISO außer Betrieb zu fahren), dann könnten Sie das vermutlich als Interrupt überwachen Entweder in einem Kernel-SPI-Treiber oder durch separate Verwendung einer Userspace-Interrupt-Schnittstelle, auf der Sie() auswählen können.

Wenn nicht, müssen Sie die Kompromisse auswerten, indem Sie den Abruf des Status über SPI in einen benutzerdefinierten Kernel-Treiber verschieben, anstatt ihn im Benutzerbereich zu belassen.