はじめに

USBデバイスなんだけど、デバイスマネージャを開くとCOMポートとして認識されているデバイスって結構あると思います。 COMポートとして認識されているってことはそのポートをオープンすれば自由自在にそのデバイスを操れるはず! が、COMポートでやり取りされているプロトコルが何なのか分からない...どうしよう... という場合に何度か遭遇していました。

例えば

などの製品を使えば確かにシリアル通信は見ることができるのですが、これはあくまでもRS232Cをインタフェースとして持つデバイス用であって、USBをCOMポートに見せるようなFT232Rを使ったようなデバイスには対応することができません。

そこでいろいろ調べていたところcom0comという仮想COMポートドライバを使えばできることが分かりました。

com0comのインストール

Windows 7とWindows 8だと少し前準備が必要です。

Windows 7の場合はドライバのデジタル署名制限を無効化する必要があります。 やり方は簡単でコマンドプロンプトから

bcdedit /set TESTSIGNING ON

を実行するだけです。 詳細は

をご覧ください。

Windows 8はもう一歩厄介で、セキュアブートの解除からしなければならないようです。 詳細は

をご覧ください。

この辺の設定が終わったら、

からcom0comをダウンロードしてきてインストールします。 途中いろいろ聞かれますが、「インストールします」を選んでください。

最後にGUIで

  • CNCA0
  • CNCB0

の名前をCOM11、COM12とかに変更してapplyボタンを押したらインストールは終了です。

com0com_gui.jpg

例えば僕の環境では、↓で分かるようにCOM11、COM12、COM30、COM31がcom0comで作られています。 このCOM11とCOM12、COM30とCOM31がそれぞれ仮想的に繋がっています。

device_manager.jpg

com0comを使ってシリアル通信をsniff (モニタリング)する

シリアル通信をsniffするためのプログラムが少し長いですが、以下になります。 cygwinで動きます。

#include "sdlab.h"

int init_serial(char *device)
{
  int i, fd;
  FILE *f;
  struct termios tty;
  char *c, buf[256];

  if((fd = open(device, O_RDWR|O_NOCTTY)) == -1){
    fprintf(stdout, "can't open %s.\n",device);
    exit(EXIT_FAILURE);
  }

  memset(&tty, 0, sizeof(tty));
  tty.c_cflag = CS8|CLOCAL|CREAD;
  tty.c_cc[VMIN] = 1;
  tty.c_cc[VTIME] = 0;
  cfsetospeed(&tty, B115200);
  cfsetispeed(&tty, B115200);
  tcflush(fd, TCIFLUSH);
  tcsetattr(fd, TCSANOW, &tty);

  return fd;
}

void list_serials()
{
  struct stat st;
  char filename[256];
  int ret;

  int num_devices = 0;

  for(int i = 0; i < 50; i++){
    sprintf(filename, "/dev/ttyS%d", i);
    ret = stat(filename, &st);
    if(ret == 0){
      printf("found %s\n",filename);
    }

    num_devices++;
  }

  if(num_devices == 0){
    printf("serial port not found\n");
    exit(-1);
  }
}

int main(int argc, char **argv)
{
  if(argc != 3){
    printf("usage: %s\n", argv[0]);
    list_serials();
    exit(-1);
  }

  int fd_dev = init_serial(argv[1]);
  int fd_app = init_serial(argv[2]);

  while(1){
    fd_set fdset;
    uint8_t buf[1024];

    FD_ZERO(&fdset);
    FD_SET(fd_dev, &fdset);
    FD_SET(fd_app, &fdset);

    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    int ret = select(fd_app + 1, &fdset, NULL, NULL, &tv);

    if(FD_ISSET(fd_dev, &fdset)){
      int ret = read(fd_dev, buf, sizeof(buf) - 1);
      buf[ret] = 0;
      write(fd_app, buf, ret);
      printf("from dev to app: %s\n", buf);
    }

    if(FD_ISSET(fd_app, &fdset)){
      int ret = read(fd_app, buf, sizeof(buf) - 1);
      buf[ret] = 0;
      write(fd_dev, buf, ret);
      printf("from app to dev: %s\n", buf);
    }
  }

  close(fd_dev);
  close(fd_app);
}

やってることは単純で、引数の一番目と二番目に指定されたcomポートをブリッジしているだけです。

動かし方

g++ -o comsniffer comsniffer.c

でコンパイルして、COM3 (/dev/ttyS2)が実際のシリアルポートで、例えば↓のような構成にした場合

  • アプリケーションのCOMポートの設定をCOM12 (/dev/ttyS11)にする
  • ./comsniffer /dev/ttyS2 /dev/ttyS10 を実行

で行けるはずです。

com0com_ex1.jpg

添付ファイル: filecom0com_ex1.jpg 4213件 [詳細] filedevice_manager.jpg 4456件 [詳細] filecom0com_gui.jpg 3726件 [詳細]

  添付編集
Last-modified: 2015-06-11 (木) 04:21:57 (3452d)