Linux で termios フレームワークを使用して、UART (usbserial) 経由で非接触型スマート カード リーダーに接続しようとしています。コードは PC で正常に動作しますが、クロスコンパイルして ARM9 ターゲットで試してみると、デバイスを開き、デバイスにコマンドを書き込むことさえできますが、読み取りコマンドは無期限にブロックされます。コード スニペットは次のとおりです。
int mifare_rdr_init(struct mifare_1K * ptr, char *rdr_devnode)
{
bzero(ptr, sizeof(struct mifare_1K)); // zero the entire structure
// open serial device
int fd = open(rdr_devnode, O_RDWR|O_NOCTTY );
if (fd == -1) {
perror("Failed to open serial device ");
return 1;
}
ptr->serialfd = fd; // save file descriptor
ptr->serialdev.c_iflag = 0; // no i/p flags
ptr->serialdev.c_oflag = 0; // o/p flags
ptr->serialdev.c_cflag = ( CS8 | CREAD | B38400 ); // 8 bits, receive enable, baud for rdr
ptr->serialdev.c_lflag = ( ICANON ); // CANONICAL mode, means read till newline char '\n'.
// control chars
// commented below line as suggested by A.H below, since it's not needed in CANONICAL mode
// ptr->serialdev.c_cc[VMIN] = 1; // read unblocks only after at least one received char.
// flush all i/o garbage data if present
tcflush(ptr->serialfd,TCIOFLUSH);
int ret = 0;
// apply settings
ret = tcsetattr(ptr->serialfd,TCSANOW,&ptr->serialdev);
if (ret == -1) {
perror("tcsetattr() failed ");
return 2;
}
return 0;
}
int get_mifare_rdr_version(struct mifare_1K *ptr, char *data)
{
// flush all i/o garbage data if present
tcflush(ptr->serialfd,TCIOFLUSH);
int chars_written = write(ptr->serialfd,"$1V\n",4);
if( chars_written < 0 ) {
perror("Failed to write serial device ");
return 1;
}
printf("cmd sent, read version...\n"); // this prints, so I know cmd sent...
int chars_read = read(ptr->serialfd,ptr->data_buf,14);
if( chars_read < 0 ) {
perror("Failed to read serial device ");
return 2;
}
// copy data to user buffer
printf("reading done.\n"); // this doesn't print...
return 0;
}
mifare_1K 構造体には、シリアル デバイスのファイル記述子、termios 構造体、および使用しているさまざまなバッファーが含まれています。私が言及したデバイスは、usb-to-serial (モジュール: ftdi_sio) デバイスです。termios の Canonical モードで 38400@8-N-1 に設定されています。
リーダーからの応答が '\n' で終わるため正規モードです。したがって、'\n' が受信されるまでデバイスを読み取るため、正規モードでより適切に処理されます (間違っている場合は修正してください)。
最初に init() fn を呼び出し、次に get_rdr_version() を呼び出します。「cmd sent, read version...」という文字列が出力されるので、書き込み可能であることがわかりますが、「reading done.」という文字列は出力されません。その後。
もう 1 つの問題は、カード リーダーを取り外して、そのポートを別の PC の gtkterm (シリアル ポート ターミナル プログラム) に接続すると、その gtkterm で「$1V\n」が表示されないことです??!! . 次に、少しRnDした後、リーダーがリーダーに接続されているシステムを再起動すると、他のGtktermでそのコマンド「$ 1V\n」しか取得できないことがわかりました。再起動せずに再試行すると、そのコマンドはそのGktermに表示されません...手がかりですが、まだ理解していません。
cmdがデバイスファイルに書き込まれているが、実際のデバイスには排出されていないようなものですか? これをチェックする方法はありますか?
私はこのfrでしばらく立ち往生しているので、どんな助けも深く感謝しています....thnks。
アップデート :
OK、以下に示すようにコードを少し変更することで機能するようになりました。
// open serial device
int fd = open(rdr_devnode, O_RDWR|O_NOCTTY|O_NDELAY ); // O_NDELAY ignores the status of DCD line, all read/write calls after this will be non-blocking
fcntl(fd,F_SETFL,0); // restore read/write blocking behavior
if (fd == -1) {
perror("Failed to open serial device ");
return 1;
}
これは、init() 関数でポートを開くコードの変更されたセクションです。2 つの変更:
1) O_NDELAY が open() 呼び出しのフラグに追加されます。これは、データ キャリア検出 (DCD) ラインを無視して、相手側が接続され、通信の準備ができているかどうかを確認します。これはもともと必要のない MODEM に使用されていたもので、実際には usbserial を使用しているため、まったく持っていません。ただし、このフラグはさらに read() および write() をノンブロッキングとして呼び出します。注意点として、termios 構造体の cflag に CLOCAL を追加することでこれが解決されると思っていましたが、試してみましたが機能しませんでした。
2) fcntl(fd,F_SETFL,0) は、以降の read() および write() 呼び出しのブロッキング動作を復元します。
このコンボは私にとって完璧に機能しています。これを回答として投稿しない唯一の理由は、同じハードウェアであるため、この変更なしで PC で動作した理由をまだ理解していないためです。実際、 minicomを使用して ARM9 TARGET のスマート カード リーダーからデータを読み取ることはできましたが、私のプログラムではできませんでした。FT232BL のドキュメントをチェックして、デフォルトで DCD のステータスがどのようなものかを確認します。
とにかく、POSIXオペレーティングシステムのシリアルプログラミングガイドでこの情報を見つけました。説明誰か??? もちろん、答えが分かり次第追記します。
乾杯 :)