atmos/kernel/usb_txn.rs
1//! L1: USB トランザクション完了判定(純粋ロジック)。
2//!
3//! ## なぜ純粋モジュールにするか
4//! `usb_transfer` は HCINT のビットを見て「成功/再試行/失敗」を決めるが、
5//! この判断を誤ると
6//! - NAK を失敗と誤判定 → 正常な「今はデータ無し」で転送を諦める
7//! - 失敗を成功と誤判定 → 送っていないデータを送信済み扱いし、
8//! データトグルとシーケンス番号がずれる(**静かに壊れる**)
9//! - STALL を再試行 → 永久ループ
10//! のいずれかになる。受信側 (`usb_rx_state.rs`) で同種の欠陥
11//! (NAK 誤分類でチャネル永久停止)を実際に踏んでいるので、
12//! 送信側も同じ形で保証する。
13//!
14//! ## 典拠
15//! USB 2.0 §8.5(Transaction packet sequences)、
16//! DWC2 の HCINT ビット定義。
17
18/// HCINT のビット位置。
19pub const XFER_COMPL: u32 = 1 << 0;
20pub const CH_HALTED: u32 = 1 << 1;
21pub const STALL: u32 = 1 << 3;
22pub const NAK: u32 = 1 << 4;
23pub const ACK: u32 = 1 << 5;
24pub const NYET: u32 = 1 << 6;
25pub const XACT_ERR: u32 = 1 << 7;
26pub const BBL_ERR: u32 = 1 << 8;
27pub const FRM_OVRUN: u32 = 1 << 9;
28pub const DATA_TGL_ERR: u32 = 1 << 10;
29
30/// トランザクションの判定結果。
31#[derive(Debug, PartialEq, Eq, Clone, Copy)]
32pub enum TxnOutcome {
33 /// 転送完了。データを確定してよい。
34 Complete,
35 /// デバイスが一時的に受け付けられない。**同じトグルで再試行する**。
36 Retry,
37 /// エンドポイント停止。再試行せず、上位で CLEAR_FEATURE が必要。
38 Stalled,
39 /// 回復不能なエラー。**成功扱いにしてはいけない**
40 /// (送信済みとみなすとトグルとシーケンス番号がずれる)。
41 Failed,
42 /// まだ何も起きていない。ポーリングを続ける。
43 Pending,
44}
45
46/// HCINT からトランザクションの結末を判定する。
47///
48/// 判定順序が重要:
49/// 1. 転送完了が立っていれば成功(他のビットが残っていても優先)
50/// 2. STALL は再試行しない
51/// 3. NAK / NYET は再試行(エラーではない)
52/// 4. 各種エラービットは失敗
53/// 5. どれも無ければ保留
54pub fn classify(hcint: u32) -> TxnOutcome {
55 if (hcint & XFER_COMPL) != 0 {
56 return TxnOutcome::Complete;
57 }
58 if (hcint & STALL) != 0 {
59 return TxnOutcome::Stalled;
60 }
61 if (hcint & (NAK | NYET)) != 0 {
62 return TxnOutcome::Retry;
63 }
64 if (hcint & (XACT_ERR | BBL_ERR | FRM_OVRUN | DATA_TGL_ERR)) != 0 {
65 return TxnOutcome::Failed;
66 }
67 TxnOutcome::Pending
68}
69
70/// 転送済みバイト数を求める。`requested` は要求長、`remaining` は HCTSIZ の残り。
71///
72/// 残りが要求長を超えるのは異常なので、黙って巨大な値を返さず `None`。
73pub fn transferred_len(requested: u32, remaining: u32) -> Option<u32> {
74 if remaining > requested {
75 return None;
76 }
77 Some(requested - remaining)
78}
79
80/// この結末でデータトグルを進めてよいか。
81///
82/// **完了時のみ進める。** Retry/Failed で進めると以後のパケットが
83/// すべてデバイスに捨てられる(トグル不一致)。
84pub fn should_advance_toggle(outcome: TxnOutcome) -> bool {
85 matches!(outcome, TxnOutcome::Complete)
86}
87
88/// この結末で再試行してよいか。
89pub fn should_retry(outcome: TxnOutcome) -> bool {
90 matches!(outcome, TxnOutcome::Retry | TxnOutcome::Pending)
91}