4

実装したい USB プロトコルがありますが、それを行う最善の方法について少し迷っています。

USB プロトコルでは、次のようにデータと肯定応答パケットをやり取りします。

Device: data
Host: ACK
Host: reply
Device: ACK

ただし、次のようにパケットが非同期に着信する場合があります。

Device: data #1
Device: data #2
Host: ACK #1
...

USB のすべての詳細を抽象化し、プログラムが実際のデータを処理するだけで、パケット ヘッダーやパケットの確認などを気にする必要がない API が必要です。理想的にはwrite_to_device、デバイスがパケットを確認するまでブロックする機能read_from_device、パケットが受信されるまでブロックするis_data_available機能、およびキューにデータがあるかどうかをすぐに返す機能が存在します。

USB イベントを処理する別のスレッドを実行することを考えています。このスレッドは、すべてのデータのカプセル化と確認応答を処理します。

パケットが着信すると、処理スレッドは ACK パケットを送信し、生データを抽出してパイプに書き込みます。関数(read_from_deviceメイン スレッドから呼び出される) は、このパイプから単純に読み取り、データが存在するまで自然にブロックします。しかし、このスキームを使用すると、is_data_available関数を実装するクリーンな方法が得られなくなります。パイプにデータがあるかどうかを読み取らずに確認する方法はありません。

このようなもの:

[ Main thread    ][ Processing thread   ]
| Read from pipe ||                     |
|                || USB packet comes in |
|                || Send ACK packet     |
|                || Extract data        |
|                || Write data to pipe  |
| Read succeeds  ||                     |
| Return data    ||                     |

本当の問題はwrite_to_device関数を実装することです。

[ Main thread                ][ Processing thread      ]
| Somehow signal write       ||                        |
| Wait for write to complete ||                        |
|                            || Send the data          |
|                            || Wait for ACK packet    |
|                            || Somehow signal that write completed
| Return                     ||                        |

パケットを送信し、確認パケットを待ってから戻る方法をきれいに実装するにはどうすればよいですか?

4

2 に答える 2

1

libusb多かれ少なかれ、あなたが説明したことはすべてすでに行っています。libusb_interrupt_transfer各 USB エンドポイントは、 (または)で書き込むことができるデータグラム ソケットと見なすことができますlibusb_control_transfer。これらの関数では、入力または出力として機能する配列を渡します。謝辞などを送る必要はありません。入力または出力の方向は、エンドポイントの構成によって異なります。

また、転送を開始していくつかのファイル記述子をメインまたはループに追加し、I/O が完了すると最終的にコールバックを取得する非同期 APIもあります。selectpoll

于 2013-01-05T15:31:23.537 に答える
1

カスタムパイプクラスまたは構造などを作成することをお勧めします。そのために、書き込みメソッドを定義します。このメソッドは、セマフォがトリガーされるまでの待機も保持します。Linux を使用している場合は、sem_wait(セマフォ関数ファミリからsem_*) を見たいものです。

書き込み関数は、FIFO にデータを書き込み、セマフォにフラグが立てられるのを待ちます。しかし、書き込みスレッドは、送信したいパイプを介してすべてのデータが到着したことをどのように知るのでしょうか? スレッドがブロックして読み取る必要がある場合、ここで問題が発生する可能性があります。

したがって、メイン スレッドから処理スレッドへのパイプ内で microformat を使用し、書き込むバイト数を定義する整数サイズを送信することをお勧めします。処理スレッドは、そのバイト数を読み取り、それをデバイスに転送し、すべてのデータにフラグが立てられるとすぐにセマフォにフラグを立てます。関数はwriteセマフォを待機するため、処理スレッドが終了するまで非ビジー ブロッキングになります。

これは、カスタム パイプ構造体とアウトライン化された書き込み関数を作成する方法です。

#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>

typedef struct {
    int write_to_pipe, read_from_pipe;
    sem_t *write_sem;
} MyPipe;

MyPipe *pipe_new() {
    int fds[2];
    if (pipe(fds)) {
        // handle error here
        return NULL;
    }

    sem_t *sem = NULL;
    if (sem_init(sem, 0, 0)) {
        // handle error here
        close(fds[0]);
        close(fds[1]);
        return NULL;
    }

    MyPipe *result = malloc(sizeof(MyPipe));
    result->write_to_pipe = fds[1];
    result->read_from_pipe = fds[0];
    result->write_sem = sem;
    return result;
}

void pipe_write(MyPipe *pipe, const unsigned char *buf, const int size) {
    write(pipe->write_to_pipe, &size, sizeof(int));
    write(pipe->write_to_pipe, buf, size);
    sem_wait(pipe->write_sem);
}

処理スレッドはMyPipeインスタンスを認識し、read_from_pipe必要なときにいつでも読み取ります。最初にメイン スレッドがパイプに書き込んだバイト数を読み取り、その後、すべてのバイトを任意のチャンクで読み取ります。すべてのデータがデバイスに送信され、デバイスによって ACK が返された後sem_post、セマフォが返されるので、それpipe_writeが返されます。

必要に応じて、別のセマフォを追加pipe_writeして、実際に利用可能なデータがある場合にのみ処理スレッドがデータを読み取るようにすることができます。

免責事項:コードはテストしていません。コンパイルできることだけを確認しました。を使用してビルドする必要-pthreadsem_*あります。

于 2013-01-05T11:19:11.773 に答える