6

シリアル ポートでデバイスをエミュレートするプログラムを書きたいと思います。これを達成するために疑似端末を使用しようとしています。マスターを制御する 1 つの異なるプロセスが必要です。このプロセスは、シリアル デバイス エミュレータとして機能します。別のプロセス (kermit など) がスレーブ端末を使用してマスターと通信できるようにします。明確なプロセス要件のため、フォークは使用していません。インターネット上のほぼすべての疑似端末の例は、マスター/スレーブに fork() を使用することを示しています。

私はそれを一方向に動かしています。つまり、スレーブプロセスがスレーブ疑似端末にデータを書き込むことができ、マスターはマスター疑似端末からそれを読み取ることができます。

問題は別の方向にあります。マスターにデータを書き込み、スレーブにデータを読み取ることができません。

動作しない双方向コードと動作する単方向コードの両方を示します。

非稼働双方向マスター:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char* argv[])
{
  // get the master fd
  int masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if(masterfd < 0)
  {
    perror("getpt");
    exit(1);
  }

  // grant access to the slave
  if(grantpt(masterfd) < 0)
  {
    perror("grantpt");
    exit(1);
  }

  // unlock the slave
  if(unlockpt(masterfd) < 0)
  {
    perror("unlockpt");
    exit(1);
  }

  // get the path to the slave
  char slavepath[64];
  if(ptsname_r(masterfd, slavepath, sizeof(slavepath)) < 0)
  {
    perror("ptsname_r");
    exit(1);
  }

  printf("Using %s\n", slavepath);

  char bufout = 'D';
  char bufin;

  int c;
  while(1)
  {
    printf("reading\n");
    c = read(masterfd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    if(bufout == 'D') bufout = 'E';
    else if(bufout == 'E') bufout = 'D';
    printf("writing %c\n", bufout);
    c = write(masterfd, &bufout, 1);
    printf("wrote %i bytes\n", c);
    if(c == -1) break;

    sleep(1);
  }

  close(masterfd);

  return 0;
}

非稼働双方向スレーブ:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
  int fd = open("/dev/pts/15", O_RDWR | O_NOCTTY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }

  char bufout = 'A';
  char bufin;

  int c;
  while(1)
  {
    if(bufout == 'A') bufout = 'B';
    else if(bufout == 'B') bufout = 'A';
    printf("writing %c\n", bufout);
    c = write(fd, &bufout, 1);
    if(c == -1) break;

    printf("reading\n");
    c = read(fd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    sleep(1);
  }

  close(fd);
}

非稼働マスターの出力: (受信した最初の文字はスレーブからのもので、残りはマスターによって書き込まれた文字であることに注意してください。つまり、マスターはマスター pts に書き込んだのと同じ文字を読み取っていて、スレーブが何を無視しているのかを無視しています。最初の文字を除いて書いています。)

Using /dev/pts/15
reading
read 1 bytes: B     [<--- FROM THE SLAVE]
writing E
wrote 1 bytes
reading
read 1 bytes: E     [<--- REST FROM THE MASTER]
writing D
wrote 1 bytes
reading
read 1 bytes: D
writing E
wrote 1 bytes
reading
read 1 bytes: E
writing D
wrote 1 bytes
reading
read 1 bytes: D
^C

非稼働スレーブの出力: (マスターが書き込んでいるものを受け取りません。)

writing B
reading
^C

作業中の単方向マスター:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char* argv[])
{
  // get the master fd
  int masterfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
  if(masterfd < 0)
  {
    perror("getpt");
    exit(1);
  }

  // grant access to the slave
  if(grantpt(masterfd) < 0)
  {
    perror("grantpt");
    exit(1);
  }

  // unlock the slave
  if(unlockpt(masterfd) < 0)
  {
    perror("unlockpt");
    exit(1);
  }

  // get the path to the slave
  char slavepath[64];
  if(ptsname_r(masterfd, slavepath, sizeof(slavepath)) < 0)
  {
    perror("ptsname_r");
    exit(1);
  }

  printf("Using %s\n", slavepath);

  char bufout = 'D';
  char bufin;

  int c;
  while(1)
  {
    printf("reading\n");
    c = read(masterfd, &bufin, 1);
    printf("read %i bytes: %c\n", c, bufin);
    if(c == -1) break;

    sleep(1);
  }

  close(masterfd);

  return 0;
}

単方向スレーブの作業:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
  int fd = open("/dev/pts/15", O_RDWR | O_NOCTTY);
  if(fd < 0)
  {
    perror("open");
    exit(1);
  }

  char bufout = 'A';
  char bufin;

  int c;
  while(1)
  {
    if(bufout == 'A') bufout = 'B';
    else if(bufout == 'B') bufout = 'A';
    printf("writing %c\n", bufout);
    c = write(fd, &bufout, 1);
    if(c == -1) break;

    sleep(1);
  }

  close(fd);
}

作業マスターの出力: (スレーブが正常に書き込む内容を読み取ります。)

Using /dev/pts/15
reading
read 1 bytes: B
reading
read 1 bytes: A
reading
read 1 bytes: B
reading
read 1 bytes: A
reading
read 1 bytes: B
^C

作業スレーブの出力:

writing B
writing A
writing B
writing A
writing B
^C
4

1 に答える 1

7

あなたの最初の例は、スレーブプログラムのようなscreen、または代わりに端末エミュレーターを使用すると機能することに気付きました。picocom

その違いは、さまざまな線の「料理」の設定から来ていると思います。pts作成時のデフォルトの状態は次のとおりです。

$ stty -F /dev/pts/2 
speed 38400 baud; line = 0;
-brkint -imaxbel

で開いた後、次のようになりますscreen

$ stty -F /dev/pts/2 
speed 9600 baud; line = 0;
kill = ^H; min = 100; time = 2;
-icrnl -imaxbel
-opost -onlcr
-isig -icanon -echo

次にpts、読み取り/書き込み操作の前に、スレーブ プログラムで raw モードに設定してみました。Raw モードは、調理を可能な限り無効にするライン設定の組み合わせです。

struct termios ts;

if(tcgetattr(fd, &ts))
{
  perror("tcgetattr");
  exit(1);
}

cfmakeraw(&ts);
tcsetattr (fd, TCSANOW, &ts);

それが機能するように見えます。

主人:

$ ./pts_master 
Using /dev/pts/2
reading
read 1 bytes: B
writing E
wrote 1 bytes
reading
read 1 bytes: A
writing D
wrote 1 bytes
reading
read 1 bytes: B
writing E
wrote 1 bytes
...

スレーブ:

$ ./pts_slave 
writing B
reading
read 1 bytes: E
writing A
reading
read 1 bytes: D
writing B
...
于 2014-05-04T20:49:49.497 に答える