memo: broccoli
use of
async fn in public traits is discouraged as auto trait bounds cannot be specified
async fnを公開したときの警告について。
async関数を持ったtraitのMock化、公開を含めるとごちゃごちゃしてきた
#[cfg_attr(test, async_mock)]
#[cfg_attr(test, async_trait)]
#[trait_variant::make(Send + Sync)]
pub trait Driver {
//...
bulk転送
ざっくり全容見るなら仕様書ではないが良さげ
USB Mass storage class Bulk-only transportについて
- ざっくり Command Transport, Data Transport, Status Transportの3ステージになっている
- 転送には受信、送信のEndpointを1つずつ持つ
- Command TransportではCBW(CommandTransportWrapper)をHost->Deviceに送り、次のData Transportの情報(データの方向含む)、コマンドの内容を取得する
- Data TransportではCBWにあった方向(Host->Device or Device->Host)に指定されたデータ数転送。Device->Hostの場合、指定数に満たないケースもあるが、これはStatus Transport時に報告
- Status Transport: CSW (Command Status Wrapper)をDevice->Hostに転送。コマンドの成否、DataResidue(転送数に満たなかった分の値)、CBWのtagをつけて送る
この他にコントロール転送で Mass storage Reset, Get Max LUNもサポートする必要がある様子 (Bulk onlyなのはtypなデータ転送本体だけ)
embassy-rs/examples/rp/src/bin
scsiのコマンド、先のrenesas資料には最低限サポート例があったがもっとあるやつ
ここなども。直近やったことほぼそのまままとまっている
SCSI Transparent Command Seであれば、フルセット実装しなくてよいみたい。
また、Request Senseがエラーレポートを行っていて、非サポートコマンドもここで把握しているので重要とのこと
Request sense返却値
とりあえず疎通まではOK
しらべているとちょくちょくSeagate,Oracle,IBMの資料につくが、網羅性高そう
tinyusb mscでramdiskにデフォルト値いれて最初からREADME出している例。やるかは決めてない
USB FSのBulk転送時のpacket size。無視して送ろうとするとembassy-usb側でEndpointError返してくる
認識されるようになった
rstest + tokio::test を使ったunit test
Compiling broccoli-core v0.3.0 (E:\repos\broccoli\broccoli-core)
error[E0433]: failed to resolve: use of undeclared crate or module `async_std`
--> src\storage\handler_ramdisk.rs:149:5
|
149 | #[rstest]
| ^^^^^^^^^ use of undeclared crate or module `async_std`
|
= note: this error originates in the attribute macro `rstest` (in Nightly builds, run with -Z macro-backtrace for more info)
の怒られがあるが、rstest,async-std (featuresにattributesを追加) で回避できた
[dev-dependencies]
tokio = { version = "1.38.0", features = ["full"] }
async-mock = "0.1.3"
fake = "2.9.2"
mockall = "0.12.1"
rstest = "0.22.0"
async-std = { version = "1.13.0", features = ["attributes"] }
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
type StorageRequestTag = u32;
const LOGICAL_BLOCK_SIZE: usize = 512;
const TOTAL_DATA_SIZE: usize = 1024;
#[rstest]
#[tokio::test]
#[case(
StorageRequest::setup(0x00),
StorageResponse::report_setup_success(0x00, TOTAL_DATA_SIZE / LOGICAL_BLOCK_SIZE)
)]
#[case(
StorageRequest::read(0x01, 0),
StorageResponse::read(0x01, [0; 512])
)]
#[case(
StorageRequest::write(0x02, 0, [0; 512]),
StorageResponse::write(0x02)
)]
#[case(
StorageRequest::read(0x03, 0),
StorageResponse::read(0x03, [0; 512])
)]
#[case(StorageRequest::flush(0x04), StorageResponse::flush(0x04))]
async fn test_check_id_tag(
#[case] req: StorageRequest<StorageRequestTag, LOGICAL_BLOCK_SIZE>,
#[case] expected_resp: StorageResponse<StorageRequestTag, LOGICAL_BLOCK_SIZE>,
) {
let mut handler = RamDiskHandler::<LOGICAL_BLOCK_SIZE, TOTAL_DATA_SIZE>::new();
let resp = handler.request(req).await;
assert_eq!(resp, expected_resp);
}
}
memo
結局ペリフェラルに依存しないものを外部クレートに切り出す方針なら、arm buildするappには単体テストでテストしたいものはペリフェラル依存のものが多く残るはず
モックでペリフェラルに投げつけるところまでは見れるが、そもそも投げ方を勘違いしているケースなど検証としては片手落ち。qemuでテストできるものが多くない、またペリフェラルを模倣した何らかのプラジェクトがないのであれば、実機向けのテストビルドをして実機上で流した方が良いかもしれない