本文記錄 OHCI 主機控制器驅動中,針對 USB 大容量儲存裝置(U 盤)的批量寫入實作。與讀取類似,差別在於資料方向與 CBW/CSW 封包順序。
本函數僅處理 Mass Storage 協議層,不涉及 UFI/SCSI 指令內容。
/**
* 從批量 OUT 端點發送 CBW + DATA,從批量 IN 端點接收 CSW
* @param pCbw CBW 緩衝區指標 (31 bytes)
* @param pData 資料緩衝區指標 (512 bytes)
* @param pCsw CSW 接收緩衝區指標 (13 bytes)
* @return 0 成功,非 0 失敗
*/
short bulkWrite(unsigned char *pCbw, unsigned char *pData, unsigned char *pCsw)
{
BYTE toggle;
/* ========== 批量 OUT 端點 (ED[0]) ========== */
toggle = usbGetEdToggle(bulkEd[0]); // 取得下一個 data toggle
AT91F_CreateEd(
(unsigned int)&bulkEd[0], // ED 位址
64, // MaxPacketSize
0, 0, 0, // TDFormat, Skip, Speed
ED_OUT, // Direction: OUT
usbDevInfo.bulkOutPort, // Endpoint Number
usbDevInfo.addr, // Device Address
(unsigned int)&genTd[2], // TDQueueTail
(unsigned int)&genTd[0], // TDQueueHead
toggle, // ToggleCarry
(unsigned int)&bulkEd[1] // NextED
);
/* TD[0]:發送 CBW */
AT91F_CreateGenTd(
(unsigned int)&genTd[0], // TD 位址
0, // DataToggle (從 ED 繼承)
TD_DELAY_INT, // DelayInterrupt
TD_OUT, // Direction: OUT
1, // BufferRounding
(unsigned int)pCbw, // CurrentBufferPointer
(unsigned int)&genTd[1], // NextTD
31 // BufferLength (CBW 大小)
);
/* TD[1]:發送 DATA */
AT91F_CreateGenTd(
(unsigned int)&genTd[1],
0,
TD_DELAY_INT,
TD_OUT,
1,
(unsigned int)pData,
(unsigned int)&genTd[2],
512 // BufferLength (資料大小)
);
/* TD[2]:終結 TD (零長度) */
AT91F_CreateGenTd(
(unsigned int)&genTd[2],
3, // DataToggle = 3 (保留/結束標記)
TD_DELAY_INT,
TD_OUT,
1,
0x0, // Null buffer
(unsigned int)0, // NextTD = NULL
0x0 // Length = 0
);
/* ========== 批量 IN 端點 (ED[1]) ========== */
toggle = usbGetEdToggle(bulkEd[1]);
AT91F_CreateEd(
(unsigned int)&bulkEd[1],
64, 0, 0, 0,
ED_IN, // Direction: IN
usbDevInfo.bulkInPort,
usbDevInfo.addr,
(unsigned int)&genTd[4], // TDQueueTail
(unsigned int)&genTd[3], // TDQueueHead
toggle,
(unsigned int)0 // NextED = NULL (鏈表結束)
);
/* TD[3]:接收 CSW */
AT91F_CreateGenTd(
(unsigned int)&genTd[3],
0,
TD_DELAY_INT,
TD_IN, // Direction: IN
1,
(unsigned int)pCsw,
(unsigned int)&genTd[4],
13 // CSW 大小
);
/* TD[4]:終結 TD */
AT91F_CreateGenTd(
(unsigned int)&genTd[4],
3,
TD_DELAY_INT,
TD_IN,
1,
0x0,
(unsigned int)0,
0x0
);
/* ========== 觸發傳輸 ========== */
ohciBulkXfer(&bulkEd[0]); // 啟動 OHCI 批量傳輸
usbRunCmd(USB_CMD_TYPE_BULK_WRITE, 3); // 等待完成 / 處理中斷
return 0;
}
usbGetEdToggle() 讀取 ED 的 ToggleCarry,確保協議層面的同步(CBW/DATA/CSW 交替)。NextTD=0、BufferLength=0、DataToggle=3,標記鏈表結束。TD_OUT (主機→裝置)、TD_IN (裝置→主機),對應 PID OUT/IN。usbRunCmd(USB_CMD_TYPE_BULK_WRITE, 3) 封裝了中斷等待與狀態檢查,參數 3 代表預期完成的 TD 數(CBW+DATA+結束)。ToggleCarry 是否與裝置同步,或 CBW 中的 dCBWDataTransferLength 與實際 DATA 長度是否一致。ohciBulkXfer 已正確將 ED 掛入 HC 的 Bulk List,且帧列表啟用。pData 緩衝區對齊(OHCI 要求 32-bit 對齊)及快取一致性(DMA 前需 Clean/Invalidate)。