首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

基於 OHCI 的 USB 主機開發:批量端點寫入資料(Mass Storage 協議)

     編輯:關於C語言

本文記錄 OHCI 主機控制器驅動中,針對 USB 大容量儲存裝置(U 盤)的批量寫入實作。與讀取類似,差別在於資料方向與 CBW/CSW 封包順序。

協議封包結構

  • OUT 端點 (ED[0]):兩個 TD
    1. CBW (Command Block Wrapper,31 位元組)
    2. DATA (實際寫入資料,512 位元組)
  • IN 端點 (ED[1]):一個 TD
    1. CSW (Command Status Wrapper,13 位元組)

本函數僅處理 Mass Storage 協議層,不涉及 UFI/SCSI 指令內容。

核心函數:bulkWrite


/**
 * 從批量 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;
}

關鍵點解析

  1. Data Toggle 管理:每次傳輸前呼叫 usbGetEdToggle() 讀取 ED 的 ToggleCarry,確保協議層面的同步(CBW/DATA/CSW 交替)。
  2. TD 鏈表終結:每條端點的最後一個 TD 將 NextTD=0、BufferLength=0、DataToggle=3,標記鏈表結束。
  3. 方向常數:TD_OUT (主機→裝置)、TD_IN (裝置→主機),對應 PID OUT/IN。
  4. BufferRounding=1:允許緩衝區長度非 MaxPacketSize 整數倍,硬體自動處理短封包。
  5. 同步等待:usbRunCmd(USB_CMD_TYPE_BULK_WRITE, 3) 封裝了中斷等待與狀態檢查,參數 3 代表預期完成的 TD 數(CBW+DATA+結束)。

常見除錯提示

  • CSW 回傳失敗:檢查 IN 端點 ED 的 ToggleCarry 是否與裝置同步,或 CBW 中的 dCBWDataTransferLength 與實際 DATA 長度是否一致。
  • 傳輸逾時:確認 ohciBulkXfer 已正確將 ED 掛入 HC 的 Bulk List,且帧列表啟用。
  • 資料損壞:檢查 pData 緩衝區對齊(OHCI 要求 32-bit 對齊)及快取一致性(DMA 前需 Clean/Invalidate)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved