1、概述

本文档介绍SATAAHCI相关协议,以IMX6Q实验平台为基础,分析SylixOSAHCI驱动框架的具体实现。

2SATA简介

2.1 SATA硬盘

串口硬盘SATASerial ATA)与以往的并口硬盘PATAParallel ATA)相比,数据传输速度更加快捷,并支持热插拔;同时,SATA总线使用了嵌入式时钟频率信号,具备了比以往更强的纠错能力,能对传输指令进行检查,如果发现错误会自动矫正,提高了数据传输的可靠性。

目前SATA接口主要有SATA1.0SATA2.0SATA3.0这三个版本,三种主流规范的带宽与传输速度的对比如 2.1所示。

SylixOS中AHCI驱动框架分析_SylixOS

2.1 SATA传输速度对比

2.2 拓扑结构

SATA的拓扑结构是点对点式的,主机可以通过多个链接支持多个设备,每个设备百分百占用总线带宽,并且一个设备的链接出错不会影响其他设备的链接,其链接方式如图 2.2所示。

SylixOS中AHCI驱动框架分析_SylixOS_02

2.2 SATA拓扑结构

2.3 接口结构

SATA接口使用4根电缆传输数据,其结构图如图 2.3所示。Tx+Tx-表示输出差分数据线,对应的,Rx+Rx-表示输入差分数据线。

SylixOS中AHCI驱动框架分析_ AHCI_03

2.3 SATA接口结构

2.4 协议模型

SATA协议借鉴TCP/IP模型,分为四个层次来实现,包括物理层、链路层、传输层、应用层,其体系结构如图 2.4所示。

SylixOS中AHCI驱动框架分析_SylixOS_04

2.4 SATA协议模型

  1. 物理层采用全双工串行传输方式,主要功能是进行信号的串并及并串转化。

  2. 链路层的主要功能是通过控制原语的传递来控制信息帧的整个传输过程,保证帧信息能够正确的发送与接收,并能进行流量的控制,防止数据发送过快或接收过多。

  3. 传输层主要负责FIS帧信息结构的封装与解封。

  4. 应用层能够接收来自主机端的命令,根据命令的要求将自身的信息发送给主机端,或是接收来自主机端的以PIODMA方式传输的数据,同时写入闪存中,也能从闪存中以PIODMA的方式读出数据,传送给主机端。在应用层采用两个FIFO对数据进行缓冲,一个为读FIFO,一个为写FIFO。应用层能接收来自传输层的数据帧送入写FIFO中或将来自设备内部总线的数据保存在读FIFO中,然后通知传输层构造数据帧。

3AHCI简介

3.1 AHCI系统结构

AHCISATA设备对应的协议标准(逻辑设备接口标准),在系统内存总线和SATA设备内部逻辑间扮演通用接口的角色,我们可以将其视为SATA的一种优化驱动,其本质就是SATA协议模型各层次的具体实现。它以AHCI HBA(Host Bus Adapter)的形式呈现,其系统结构如图 3.1所示。

SylixOS中AHCI驱动框架分析_SylixOS_05

3.1 AHCI系统结构

3.2 AHCI HBA结构

每个HBA上最多可以支持32个端口,HBA的内部逻辑结构如图 3.2所示,图中从左往右,可以按照SATA协议四层模型划分。

SylixOS中AHCI驱动框架分析_SylixOS_06

3.2 HBA内部结构

 

4AHCI驱动框架分析

IMX6Q实验平台上,HBA是集成在芯片组内部,并且仅使用了端口0

SylixOS集成开发环境base工程libsylixos/SylixOS/system/device/ahci路径下,提供了ahci驱动框架,其结构如图 4.1所示。

SylixOS中AHCI驱动框架分析_ AHCI_07

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;

                        b.当监测到设备移除时,进行块设备的移除操作,具体实现如程序清单 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