Blokující close pro sériový port

masi

Blokující close pro sériový port
« kdy: 10. 01. 2013, 11:21:08 »
Zdravim vsechny,
nedavno jsem narazil na problem, se kterym jsem se nikdy nesetkal a nevim si s nim rady. Ani po dukladne konzultaci strycka googla nejsem o moc moudrejsi.

Pisu aplikaci, ktera mimo jine generuje a posila nejaka data na seriovy port. Resp. na virtualni seriovy port gadget ethernet - zatizeni /dev/ttyGSx. Pouzivam klasicke termios funkce a neblokuji file descriptory a vse chodi dobe. Az na jeden problem.

Muj program tedy periodicky generuje data a posila je ven. Vzhledem k tomu, ze se jedna o seriovy port (i kdyz virtualni), neni zadna informace, zda si je druhy konec vybira (zarizeni /dev/ttyGSx je vytvoreno vzdycky bez ohledu na to, zda je USB pripojeno a/nebo nakonfigurovano).

Takze po nejake dobe dojde k naplneni interniho fifo. Funkce "write" wrati chybu would block. V poradku. Potiz ale nastane, kdyz chci takovy file descriptor s naplnenym fifem zavrit (funkci close). V tomto okamziku se funkce close zablokuje! Ne do nekonenecna, ale asi tak na 10 sekund (odhad).

Nic jsem k tomuto problemu nenasel, zkousel jsem vystup flushnout pomoci "tcflush(uart->fd, TCIOFLUSH)" a nic. Pokud je naplnene fifo, close se proste zablokuje.

Nesetkal se nekdo s podobnym problemem?

Diky

Masi


« Poslední změna: 10. 01. 2013, 11:25:57 od Petr Krčmář »


Rax

Re:Blokující close pro sériový port
« Odpověď #1 kdy: 10. 01. 2013, 11:46:06 »
To je standardní chování těchto udělátek na virtuální sériové porty, nedodělky a bugy jsou na denním pořádku. Buď se jim přizpůsobíš nebo si napíšeš/upravíš svůj vlastní driver.
Já už jsem zažil i že funkce close na virtuální sériový port se občas dala ukončit jenom tlačítkem RESET.

masi

Re:Blokující close pro sériový port
« Odpověď #2 kdy: 10. 01. 2013, 12:09:09 »
Dik za odpoved, ale obavam, ze to tim neni. Toto neni zadne udelatko. S hardwarem to vubec nesouvisi. Gadget serial je standardni driver kernelu, kdy na usb typu device(!!!) emuluje zarizeni typu ACM. Nejedna se o zadny fyzicky prevodnik. Navic je to soucasti kernelu uz peknych par let.

A mam pocit ze jsem neco takoveho uz videl na opravdovem seriovem portu, a ze stejneho efektu dosahnes takto (overim si):

1. otevres normalni(!) seriovy port /dev/ttySx
2. nastavis rizeni toku na hardware (treba RTS/CTS)
3. pri odpojenem(!) zarizeni zapises nejaka data. protoze neni pripojene zarizeni a rizeni toku nedovoli data odeslat, tak ta zustanou ve fifu
4. das close a ten se zablokuje, protoze fifo neni prazdne.

Podle toto musi mit nejake standardni reseni, ale nikde jsem se nedocetl jake...

Re:Blokující close pro sériový port
« Odpověď #3 kdy: 10. 01. 2013, 16:11:50 »
Navic je to soucasti kernelu uz peknych par let.
To není důvod, proč by v něm neměla být chyba :)

A mam pocit ze jsem neco takoveho uz videl na opravdovem seriovem portu, a ze stejneho efektu dosahnes takto (overim si):

1. otevres normalni(!) seriovy port /dev/ttySx
2. nastavis rizeni toku na hardware (treba RTS/CTS)
3. pri odpojenem(!) zarizeni zapises nejaka data. protoze neni pripojene zarizeni a rizeni toku nedovoli data odeslat, tak ta zustanou ve fifu
4. das close a ten se zablokuje, protoze fifo neni prazdne.
Tahle by to asi nešlo - při nonblock a odpojeném zařízení vrátí write EAGAIN. Při blokující zase zůstane na write viset.

Takhle to teda aspoň funguje na MacOSu, ověřeno.

Re:Blokující close pro sériový port
« Odpověď #4 kdy: 10. 01. 2013, 16:17:52 »
Můžeme zkusit, jak se chová očesaný pokus:

Kód: [Vybrat]
#include <termios.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
   int fd;
   int res;
   struct termios options;

   if (argc!=2) {
      printf("Usage: %s <serial device>\n",argv[0]);
      exit(1);
   }

   printf("Opening device %s\n",argv[1]);
   fd = open(argv[1], O_RDWR | O_NOCTTY );
   if (fd<0) {
      perror("Error opening device");
      exit(1);
   }

   if(tcgetattr(fd, &options)!=0) {
      perror("Error getting port options");
      exit(1);
   }
   // set baudrate
   cfsetispeed(&options, B19200);
   cfsetospeed(&options, B19200);
   // set HW control - RTS/CTS
   options.c_cflag |= CRTSCTS;
   if (tcsetattr(fd, TCSANOW, &options)!=0) {
      perror("Error setting port options");
      exit(1);
   }

   if(res=write(fd, "XXXXXXXXXXXXXXXX", 16)<0){
      perror("Error writing to port");
   } else printf("%d of 16B written.\n",res);

   if(close(fd)!=0) {
      perror("Error closing port");
   }

   printf("FD closed.\n");
}


Ed

Re:Blokující close pro sériový port
« Odpověď #5 kdy: 10. 01. 2013, 17:28:00 »
Na neco trochu podobneho jsem nedavno narazil (tam se nekdy blokoval open()) a na vine byl nejaky modem-manager proces spousteny z NetworkManageru. Treba vam to pomuze.

masi

Re:Blokující close pro sériový port
« Odpověď #6 kdy: 10. 01. 2013, 22:37:22 »
Navic je to soucasti kernelu uz peknych par let.
To není důvod, proč by v něm neměla být chyba :)
No uz to tak vypada, viz. dale...


A mam pocit ze jsem neco takoveho uz videl na opravdovem seriovem portu, a ze stejneho efektu dosahnes takto (overim si):

1. otevres normalni(!) seriovy port /dev/ttySx
2. nastavis rizeni toku na hardware (treba RTS/CTS)
3. pri odpojenem(!) zarizeni zapises nejaka data. protoze neni pripojene zarizeni a rizeni toku nedovoli data odeslat, tak ta zustanou ve fifu
4. das close a ten se zablokuje, protoze fifo neni prazdne.
Tahle by to asi nešlo - při nonblock a odpojeném zařízení vrátí write EAGAIN. Při blokující zase zůstane na write viset.
Nevrati. Ne hned, dokud se nezaplni nejake fifo v kernelu. Odhadem tak 4096 znaku (nepocital jsem) se tam vleze, nez ten EAGAIN vrati. Do te doby vraci success.


masi

Re:Blokující close pro sériový port
« Odpověď #7 kdy: 10. 01. 2013, 22:46:44 »
Můžeme zkusit, jak se chová očesaný pokus:

Diky. Podobny jsem si uz napsal. Vysledek je, ze kdyz takto otevru /dev/ttyS0 na PC, close se pak skutecne zablokuje. Pokud ovsem pred zavrenim zavolam tcflush(fd, TCOFLUSH), nasledny close skonci okamzite, jak bych ocekaval.

Na OMAPu kdyz stejny program pouziji na /dev/ttyGS0 (coz je zarizeni "gadget serial" - usb device nakonfigurovany jako virtualni seriovy port a kompatibilni s CDC ACM. Dokonce i Windows na to standardni driver), tak zavolani toho tcflushe nema zadny efekt a close se pak na 15s zablokuje.

Takze asi opravdu bude chyba v tom driveru g_serial, ktery ignoruje to vyprazdneni fronty.

Diky vsem za odpovedi.

Re:Blokující close pro sériový port
« Odpověď #8 kdy: 10. 01. 2013, 22:51:48 »
Nevrati. Ne hned, dokud se nezaplni nejake fifo v kernelu. Odhadem tak 4096 znaku (nepocital jsem) se tam vleze, nez ten EAGAIN vrati. Do te doby vraci success.
Na MacOSu (s opensource driverem pro PL2303) se to takhle nechová. Třeba to bufferování jde nějak vypnout. Každopádně ale jestli neprázdný buffer blokuje close, tak to nejspíš bude fakt chyba v implementaci driveru.