1. 程式人生 > >SylixOS中AHCI驅動框架分析

SylixOS中AHCI驅動框架分析

1、概述

本文件介紹SATAAHCI相關協議,以IMX6Q實驗平臺為基礎,分析SylixOSAHCI驅動框架的具體實現。

2SATA簡介

2.1 SATA硬碟

串列埠硬碟SATASerial ATA)與以往的並口硬碟PATAParallel ATA)相比,資料傳輸速度更加快捷,並支援熱插拔;同時,SATA匯流排使用了嵌入式時鐘頻率訊號,具備了比以往更強的糾錯能力,能對傳輸指令進行檢查,如果發現錯誤會自動矯正,提高了資料傳輸的可靠性。

目前SATA介面主要有SATA1.0SATA2.0SATA3.0這三個版本,三種主流規範的頻寬與傳輸速度的對比如 2.1所示。

2.1 SATA

傳輸速度對比

2.2 拓撲結構

SATA的拓撲結構是點對點式的,主機可以通過多個連結支援多個裝置,每個裝置百分百佔用匯流排頻寬,並且一個裝置的連結出錯不會影響其他裝置的連結,其連結方式如圖 2.2所示。

2.2 SATA拓撲結構

2.3 介面結構

SATA介面使用4根電纜傳輸資料,其結構圖如圖 2.3所示。Tx+Tx-表示輸出差分資料線,對應的,Rx+Rx-表示輸入差分資料線。

2.3 SATA介面結構

2.4 協議模型

SATA協議借鑑TCP/IP模型,分為四個層次來實現,包括物理層、鏈路層、傳輸層、應用層,其體系結構如圖 2.4所示。

2.4 SATA協議模型

  1. 鏈路層的主要功能是通過控制原語的傳遞來控制資訊幀的整個傳輸過程,保證幀資訊能夠正確的傳送與接收,並能進行流量的控制,防止資料傳送過快或接收過多。
  2. 傳輸層主要負責FIS幀資訊結構的封裝與解封。
  3. 應用層能夠接收來自主機端的命令,根據命令的要求將自身的資訊傳送給主機端,或是接收來自主機端的以PIODMA方式傳輸的資料,同時寫入快閃記憶體中,也能從快閃記憶體中以PIODMA的方式讀出資料,傳送給主機端。在應用層採用兩個FIFO對資料進行緩衝,一個為讀FIFO,一個為寫FIFO。應用層能接收來自傳輸層的資料幀送入寫FIFO中或將來自裝置內部匯流排的資料儲存在讀FIFO中,然後通知傳輸層構造資料幀。

3AHCI簡介

3.1 AHCI系統結構

AHCISATA裝置對應的協議標準(邏輯裝置介面標準),在系統記憶體匯流排和SATA裝置內部邏輯間扮演通用介面的角色,我們可以將其視為SATA的一種優化驅動,其本質就是SATA協議模型各層次的具體實現。它以AHCI HBA(Host Bus Adapter)的形式呈現,其系統結構如圖 3.1所示。

3.1 AHCI系統結構

3.2 AHCI HBA結構

每個HBA上最多可以支援32個埠,HBA的內部邏輯結構如圖 3.2所示,圖中從左往右,可以按照SATA協議四層模型劃分。

3.2 HBA內部結構

4AHCI驅動框架分析

IMX6Q實驗平臺上,HBA是整合在晶片組內部,並且僅使用了埠0

SylixOS整合開發環境base工程libsylixos/SylixOS/system/device/ahci路徑下,提供了ahci驅動框架,其結構如圖 4.1所示。

4.1 AHCI驅動框架

4.1 AHCI驅動管理

ahcidrv.c中,主要實現瞭如下函式:

  • API_AhciDrvInit

該函式主要負責AHCI驅動註冊的初始化工作,如:初始化驅動數量統計的全域性變數、初始化驅動管理連結串列等,同時給shell命令"ahcidrv"關聯操作函式。該函式需要在進行驅動註冊前呼叫。

  • API_AhciDrvRegister

該函式主要負責註冊指定型別的驅動,通過建立驅動控制塊,將bsp層實現的AHCI相關驅動鏈入管理連結串列,以便AHCI控制器呼叫。

  • API_AhciDrvHandleGet

該函式為AHCI控制器提供呼叫AHCI驅動控制代碼的介面。

  • __tshellAhciDrvCmd

shell命令"ahcidrv"的關聯函式實現,提供AHCI裝置驅動列表的顯示功能。

4.2 AHCI裝置管理

ahcidev.c中,主要實現瞭如下函式:

  • API_AhciDevInit

該函式主要負責AHCI裝置管理的初始化,如:初始化裝置數量統計的全域性變數、初始化裝置管理連結串列等,同時給shell命令"ahcidev"關聯操作函式。該函式在進行驅動註冊前被呼叫。

  • API_AhciDevCountGet

該函式儲存當前裝置連結串列中存在的裝置數量。

  • API_AhciDevHandleGet

該函式提供了根據控制器控制代碼和驅動器編號獲取AHCI裝置控制代碼的介面,在建立裝置前可以判斷當前裝置連結串列中是否已存在同類型的裝置。

  • API_AhciDevAdd

該函式用於向AHCI裝置管理連結串列中新增一個裝置。

  • API_AhciDevDelete

該函式用於從AHCI裝置管理連結串列中刪除一個裝置。

  • API_AhciDevIoctl

該函式提供AHCI裝置控制介面,用於獲取/設定裝置的cache回寫功能

  • __tshellAhciDevCmd

Shell命令"ahcidev"的關聯函式實現,提供AHCI裝置列表顯示和cache回寫設定功能。

4.3 AHCI控制器管理

ahciCtrl.c中,主要實現瞭如下函式:

  • API_AhciCtrlInit

該函式主要負責AHCI控制器管理初始化,如:初始化控制器數量統計的全域性變數、初始化控制器管理連結串列等,同時給shell命令"ahcictrl"關聯操作函式。該函式在進行驅動註冊前被呼叫。

  • API_AhciCtrlCountGet

該函式用來獲取當前AHCI控制器總數。

  • API_AhciCtrlIndexGet

該函式用來給新建立的控制器分配索引號。

  • API_AhciCtrlHandleGetFromIndex

該函式可通過索引號獲取AHCI控制器的控制代碼。

  • API_AhciCtrlHandleGetFromName

該函式可通過控制器名字獲取AHCI控制器的控制代碼。

  • API_AhciCtrlHandleGetFromPciArg

該函式可通過PCI裝置控制代碼來獲取AHCI控制器的控制代碼。

  • API_AhciCtrlAdd

該函式用於向AHCI控制器連結串列中新增一個新的控制器。

  • API_AhciCtrlDelete

該函式用於向AHCI控制器連結串列中刪除一個控制器。

  • __tshellAhciCtrlCmd

Shell命令"ahcictrl"的關聯函式實現,提供AHCI控制器列表顯示功能。

4.4 AHCI驅動庫

ahciLib.c中,提供了一些AHCI驅動器和控制器的相關操作庫函式,如驅動器扇區資訊、工作模式、中斷處理、控制器復位、控制器資訊列印等功能。

4. 5AHCI電源管理

ahciPm.c中,提供了使能/禁能裝置電源管理功能,用於塊裝置中的電源控制管理。

4.6 AHCI驅動實現

ahci.c中,是AHCI控制器邏輯功能的具體實現。

  1. 建立AHCI控制器,並加入控制器管理連結串列進行管理。
  2. 初始化AHCI驅動,包括建立裝置熱插拔監測執行緒、控制器和驅動器記憶體結構的分配和初始化、命令序列和FIS資訊幀的初始化、磁碟控制器和驅動器的初始化等。
  3. 裝置熱插拔監測執行緒可以監測裝置接入、裝置移除、裝置異常等資訊,並進行相關處理。
    1. 當監測到裝置接入時,進行控制器和驅動器的初始化,然後建立一個塊裝置,塊裝置中提供了塊裝置的讀寫和控制等功能,為上層提供操作介面,最後加入裝置管理連結串列進行管理。其具體實現流程如程式清單 4.1所示。

程式清單 4.1

case  AHCI_MSG_ATTACH:                                                 /* 裝置接入                     */
    AHCI_LOG(AHCI_LOG_PRT,  "recv  attach  msg  ctrl %d drive %d.\r\n",
             hCtrl->AHCICTRL_uiIndex,  iDrive);
	if (hDrive->AHCIDRIVE_ucState != AHCI_DEV_NONE) {
		AHCI_LOG(AHCI_LOG_PRT,  "ctrl  %d  drive %d  state  none.\r\n",
		         hCtrl->AHCICTRL_uiIndex,  iDrive);
		continue;
	}

	hDrive->AHCIDRIVE_ucState = AHCI_DEV_INIT;
	AHCI_LOG(AHCI_LOG_PRT,  "init  ctrl %d  drive %d.\r\n", hCtrl->AHCICTRL_uiIndex,  iDrive);
     
     /*
      * 判斷當前驅動器是否處於忙狀態,因為機械硬碟從上電到正常工作需要一段時間
      */
	iRet = __ahciDriveNoBusyWait(hDrive);
	if (iRet != ERROR_NONE) {
		break;
	}

	iRetry = 0;
     /*
      * 磁碟控制器初始化
      */
	iRet  = __ahciDiskCtrlInit(hCtrl,  iDrive);
	while (iRet != ERROR_NONE) {
		AHCI_LOG(AHCI_LOG_ERR,  "ctrl  init  err  ctrl %d  drive  %d  retry %d.\r\n",
			    hCtrl->AHCICTRL_uiIndex,  iDrive,  iRetry);
		iRetry += 1;
		if (iRetry >= AHCI_RETRY_NUM) {
			break;
		}
		iRet = __ahciDiskCtrlInit(hCtrl, iDrive);
	}
	iRetry = 0;
     /*
      * 磁碟驅動器初始化
      */
	iRet  = __ahciDiskDriveInit(hCtrl,  iDrive);
	while (iRet != ERROR_NONE) {
		AHCI_LOG(AHCI_LOG_ERR,  "drive  init  err  ctrl %d  drive %d  retry %d.\r\n",
			    hCtrl->AHCICTRL_uiIndex, iDrive,  iRetry);
		iRetry += 1;
		if (iRetry >= AHCI_RETRY_NUM) {
			break;
		}
		iRet = __ahciDiskDriveInit(hCtrl,  iDrive);
	}

     /*
      * 塊裝置建立,根據裝置型別提供讀寫和控制介面
      */
	hBlkDev = __ahciBlkDevCreate(hCtrl,  iDrive,  hDrive->AHCIDRIVE_ulStartSector,  0);
	if (!hBlkDev) {
		AHCI_LOG(AHCI_LOG_ERR,  "create  blk  dev  error %s.\r\n",  hDrive->AHCIDRIVE_cDevName);
		break;
	}

     /*
      * 加入塊裝置管理連結串列
      */
	API_AhciDevAdd(hCtrl,  iDrive);
	break;

2.當監測到裝置移除時,進行塊裝置的移除操作,具體實現如程式清單 4.2所示。

程式清單 4.2

case  AHCI_MSG_REMOVE:                                                 /* 裝置移除                     */
	AHCI_LOG(AHCI_LOG_PRT,  "remove  ctrl %d  drive %d.\r\n",  hCtrl->AHCICTRL_uiIndex,  iDrive);
	if (hDrive->AHCIDRIVE_hDev != LW_NULL) {
		__ahciBlkDevRemove(hCtrl,  iDrive);
	}
	break;

5BSP中的具體實現

IMX6Q實驗平臺上,BSP中具體實現如程式清單 5.1所示。主要進行註冊驅動前的初始化、驅動函式的實現、驅動函式的註冊、AHCI控制器的建立。

程式清單 5.1

/********************************************************************************************************
** 函式名稱: imxAhciDevInit
** 功能描述: IMX 型別控制器驅動相關初始化
** 輸 入  : NONE
** 輸 出  : ERROR or OK
** 全域性變數:
** 呼叫模組:
                                           API 函式
********************************************************************************************************/
LW_API INT  imxAhciDevInit (VOID)
{
    AHCI_DRV_CB         tDrvReg;
    AHCI_DRV_HANDLE     hDrvReg = &tDrvReg;

    /*
     * AHCI驅動註冊初始化,在驅動註冊前進行
     */
    API_AhciDrvInit();
    lib_strlcpy(&hDrvReg->AHCIDRV_cDrvName[0], AHCI_IMX_DRV_NAME, AHCI_DRV_NAME_MAX);

    /*
     * 驅動函式具體實現
     */
    hDrvReg->AHCIDRV_uiDrvVer                   = AHCI_IMX_DRV_VER_NUM;
    hDrvReg->AHCIDRV_pfuncOptCtrl               = imxAhciCtrlOpt;
    hDrvReg->AHCIDRV_pfuncVendorDriveInfoShow   = imxAhciVendorDriveInfoShow;
    hDrvReg->AHCIDRV_pfuncVendorDriveRegNameGet = imxAhciVendorDriveRegNameGet;
    hDrvReg->AHCIDRV_pfuncVendorDriveInit       = imxAhciVendorDriveInit;
    hDrvReg->AHCIDRV_pfuncVendorCtrlInfoShow    = imxAhciVendorCtrlInfoShow;
    hDrvReg->AHCIDRV_pfuncVendorCtrlRegNameGet  = imxAhciVendorCtrlRegNameGet;
    hDrvReg->AHCIDRV_pfuncVendorCtrlTypeNameGet = imxAhciVendorCtrlTypeNameGet;
    hDrvReg->AHCIDRV_pfuncVendorCtrlIntEnable   = imxAhciVendorCtrlIntEnable;
    hDrvReg->AHCIDRV_pfuncVendorCtrlIntConnect  = imxAhciVendorCtrlIntConnect;
    hDrvReg->AHCIDRV_pfuncVendorCtrlInit        = imxAhciVendorCtrlInit;
    hDrvReg->AHCIDRV_pfuncVendorCtrlReadyWork   = imxAhciVendorCtrlReadyWork;
    hDrvReg->AHCIDRV_pfuncVendorPlatformInit    = imxAhciVendorPlatformInit;
    hDrvReg->AHCIDRV_pfuncVendorDrvReadyWork    = imxAhciVendorDrvReadyWork;

    /*
     * 註冊驅動函式
     */
    API_AhciDrvRegister(hDrvReg);

    /*
     * 建立AHCI控制器
     */
    API_AhciCtrlCreate(AHCI_IMX_DRV_NAME, 0, LW_NULL);

    return  (ERROR_NONE);
}

這樣,建立完AHCI控制器後,在AHCI控制器管理驅動中進行一系列初始化,建立熱插拔監測執行緒進行裝置狀態監測,根據裝置狀態進行塊裝置的相關處理。

注:本文件著重介紹AHCI驅動的框架結構,關於AHCI協議中命令槽、命令序列、FIS資訊幀等細節的相關處理,還待後續深入學習整理。

6、參考資料

1、《serial ATA Revision 3.0

2、《serial-ata-ahci-spec-rev1-3-1