1、概述
本文档介绍SATA和AHCI相关协议,以IMX6Q实验平台为基础,分析SylixOS中AHCI驱动框架的具体实现。
2、SATA简介
2.1 SATA硬盘
串口硬盘SATA(Serial ATA)与以往的并口硬盘PATA(Parallel ATA)相比,数据传输速度更加快捷,并支持热插拔;同时,SATA总线使用了嵌入式时钟频率信号,具备了比以往更强的纠错能力,能对传输指令进行检查,如果发现错误会自动矫正,提高了数据传输的可靠性。
目前SATA接口主要有SATA1.0、SATA2.0、SATA3.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协议模型
链路层的主要功能是通过控制原语的传递来控制信息帧的整个传输过程,保证帧信息能够正确的发送与接收,并能进行流量的控制,防止数据发送过快或接收过多。
传输层主要负责FIS帧信息结构的封装与解封。
应用层能够接收来自主机端的命令,根据命令的要求将自身的信息发送给主机端,或是接收来自主机端的以PIO或DMA方式传输的数据,同时写入闪存中,也能从闪存中以PIO或DMA的方式读出数据,传送给主机端。在应用层采用两个FIFO对数据进行缓冲,一个为读FIFO,一个为写FIFO。应用层能接收来自传输层的数据帧送入写FIFO中或将来自设备内部总线的数据保存在读FIFO中,然后通知传输层构造数据帧。
3、AHCI简介
3.1 AHCI系统结构
AHCI是SATA设备对应的协议标准(逻辑设备接口标准),在系统内存总线和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内部结构
4、AHCI驱动框架分析
在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控制器逻辑功能的具体实现。
创建AHCI控制器,并加入控制器管理链表进行管理。
初始化AHCI驱动,包括创建设备热插拔监测线程、控制器和驱动器内存结构的分配和初始化、命令序列和FIS信息帧的初始化、磁盘控制器和驱动器的初始化等。
设备热插拔监测线程可以监测设备接入、设备移除、设备异常等信息,并进行相关处理。
当监测到设备接入时,进行控制器和驱动器的初始化,然后创建一个块设备,块设备中提供了块设备的读写和控制等功能,为上层提供操作接口,最后加入设备管理链表进行管理。其具体实现流程如程序清单 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;
5、BSP中的具体实现
在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》