ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RT-Thread ATA/AHCI 主机抽象层(`ahci.h`)驱动接入指南:`rt_ahci_host` / `rt_ahci_ops` 与寄存器化注册流程

RT-Thread ATA/AHCI 主机抽象层(`ahci.h`)驱动接入指南:`rt_ahci_host` / `rt_ahci_ops` 与寄存器化注册流程 操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载本篇技术指南围绕 RT-Thread 设备驱动框架中的 ATA/AHCI 主机抽象层展开系统讲解struct rt_ahci_host、struct rt_ahci_port、struct rt_ahci_ops三个核心结构体的字段语义、属主关系与回调时序并结合 ahci.c 的源码实现还原rt_ahci_host_register的完整内部流程。阅读完成后你将掌握如何为自定义 SoC 平台填充主机结构体、实现五个可选回调、规避常见的 DMA/中断集成陷阱并理解 AHCI 控制器如何通过 SCSI 子系统向块设备层提供读写能力。一、AHCI 主机抽象层概览AHCIAdvanced Host Controller Interface是 SATA 控制器的标准硬件接口。RT-Thread 在components/drivers/ata/下提供了一套与具体平台无关的 AHCI 主机抽象层其核心定义位于 drivers/ahci.h实现位于 ata/ahci.c。这套抽象层的设计模式是**调用方填结构体 核心注册**平台驱动如 PCI 控制器或 Synopsys DWC 控制器在 probe 阶段构造struct rt_ahci_host填写必要的硬件信息IRQ、寄存器基址、回调表等然后调用rt_ahci_host_register交给核心代码完成 HBA 复位、端口枚举、链路检测、DMA 缓冲布局、中断安装与 SCSI 主机注册之后 AHCI 控制器便通过rt_scsi_host接入统一的 SCSI 子系统进而对上层表现为可用的块设备。从 ata/Kconfig 可以看出该组件的启用条件menuconfig RT_USING_ATA bool Using Advanced Technology Attachment (ATA) device drivers depends on RT_USING_DM depends on RT_USING_BLK depends on RT_USING_DMA config RT_ATA_AHCI bool Advanced Host Controller Interface (AHCI) depends on RT_USING_ATA depends on RT_USING_SCSI default y对应地ata/SConscript 会根据配置将ahci.c及可选的ahci-dw.cRT_ATA_AHCI_DW、ahci-pci.cRT_ATA_AHCI_PCI编入构建。也就是说AHCI 抽象层同时服务两类宿主PCI 总线上的标准 AHCI 控制器以及挂接在 SoC 平台总线上的 DWCDesignWareAHCI 控制器。二、struct rt_ahci_host调用方在注册前必须填充的结构体struct rt_ahci_host是接入入口。它的定义drivers/ahci.h如下struct rt_ahci_host { struct rt_scsi_host parent; int irq; void *regs; rt_size_t ports_nr; rt_uint32_t ports_map; struct rt_ahci_port ports[32]; rt_uint32_t cap; rt_uint32_t max_blocks; const struct rt_ahci_ops *ops; };各字段的属主Caller 平台驱动填写Core 核心代码回填Core ops 核心与回调协同维护与含义如下表MemberOwnerMeaningparentCaller内嵌的struct rt_scsi_hostparent.dev必须非 NULL核心会用它调用rt_dma_alloc_coherent等 DMA 接口并获取设备名。irqCaller全局主机中断号核心以struct rt_ahci_host *为参数安装 ISR。regsCallerHBAAHCI 1.0寄存器块的虚拟基地址。ports_nrCore由CAP.NP字段计算得到(cap RT_AHCI_CAP_NP) 1不要依赖初始值。ports_mapCoreDT 可覆盖从PIPorts Implemented寄存器读取若设备树节点带有ports-implemented属性则以该属性为准。ports[]Core ops每个端口的运行时状态详见下文struct rt_ahci_port。capCore从RT_AHCI_HBA_CAP读取经掩码SG 路径用于判断是否支持64 位 DMART_AHCI_CAP_64等。max_blocksCaller可为 0每次 SCSI 拆分传输的最大块数0会在注册时被替换为0x80128 块。opsCaller必填每个回调都是可选的NULL 使用核心默认行为但host_init/port_init/port_dma_init的失败会导致跳过该端口或仅记录 DMA 错误日志。几点关键约束host-parent.dev非空是硬性要求。在 ahci.c 中注册函数的第一步就是校验host、host-parent.dev、host-ops三者任一为空直接返回-RT_EINVAL。max_blocks为 0 时会在注册入口被改写为0x80即默认按 128 块512 字节扇区下约 64 KiB为单位拆分长传输。ports数组固定为 32 项对应 AHCI 规范支持的最大端口数ports_nr由核心从CAP.NP推导调用方不需要也不能自行猜测。三、struct rt_ahci_port对 ops 可见的每端口状态struct rt_ahci_port描述单个 SATA 端口的运行时状态定义在 drivers/ahci.hstruct rt_ahci_port { void *regs; void *dma; rt_ubase_t dma_handle; struct rt_ahci_cmd_hdr *cmd_slot; struct rt_ahci_sg *cmd_tbl_sg; void *cmd_tbl; rt_ubase_t cmd_tbl_dma; void *rx_fis; rt_uint32_t int_enabled; rt_size_t block_size; rt_uint16_t *ataid; rt_bool_t link; struct rt_completion done; };字段语义与属主MemberOwnerMeaningregsCorehost-regs 0x100 port_index * 0x80即每端口寄存器块基址在port_init调用前就已有效。dma、dma_handle、cmd_slot、rx_fis、cmd_tbl、cmd_tbl_dma、cmd_tbl_sgCore当link RT_TRUE后核心会分配一块RT_AHCI_DMA_SIZE的 coherent 内存并按固定布局摆放命令槽、RX FIS 与命令表/SG 区同时把CLB/CLBU、FB/FBU寄存器编程到位。int_enabledCore写入PORT_INTE的RT_AHCI_PORT_INTE_*中断使能掩码在 DMA 引擎启动后写入。block_sizeCore由 Identify/Inquiry 结果与SIG/ATAPI 判定得出为512磁盘或2048光驱。ataidCoreIdentify 数据缓冲区配合rt_ahci_ata_id_*系列内联助手使用。linkCore链路建立时为RT_TRUEISR 只在link为真时才调用port_isr与rt_completion_done。doneCorert_completion_init初始化的完成量命令路径在写PORT_CI后调用rt_completion_wait(port-done, ...)等待port_isr必须与done的语义保持一致。重要提醒在port_init/port_dma_init中你可以访问port-regs及其它尚未被核心填充的硬件状态但不要假设dma在port_init内已存在——DMA 内存只在链路成功建立之后才会分配。这条约束直接影响回调编写方式详见第五节常见集成陷阱。四、struct rt_ahci_ops五个可选回调及其在注册流程中的调用顺序struct rt_ahci_ops定义了平台驱动可以介入的所有环节定义在 drivers/ahci.hstruct rt_ahci_ops { rt_err_t (*host_init)(struct rt_ahci_host *host); rt_err_t (*port_init)(struct rt_ahci_host *host, struct rt_ahci_port *port); rt_err_t (*port_link_up)(struct rt_ahci_host *host, struct rt_ahci_port *port); rt_err_t (*port_dma_init)(struct rt_ahci_host *host, struct rt_ahci_port *port); rt_err_t (*port_isr)(struct rt_ahci_host *host, struct rt_ahci_port *port, rt_uint32_t isr); };回调触发时机与职责CallbackWhenRolehost_initHBA 软复位之后、GHC.AHCI_EN置位、CAP读取/掩码、PI写入之后端口枚举之前。可选。HBA 级别初始化时钟、PHY、厂商寄存器返回非RT_EOK会使rt_ahci_host_register整体失败。port_initPORT_CMD中的LIST_ON/FIS_ON/START/FIS_RX已清除、端口已静默之后置SPIN_UP之前。可选。端口复位、SCTL、非标准SIG处理此时port-regs有效。失败则continue跳过该端口。port_link_upport_init之后核心已置SPIN_UP。可选。若为NULL核心轮询SSTS.DET等待PHYRDY自定义实现必须在链路可用时返回RT_EOK。port_dma_initCLB/FB与 DMA 缓冲区编程完毕之后、核心写PORT_CMDACTIVE、FIS_RX、POWER_ON、SPIN_UP、START之前。可选。额外的 SoC 级 DMA/端口设置出错仅记录日志核心仍会照常断言PORT_CMD。port_isr全局 ISR 中HBA_INTS对应位被置位、port-link为真、PORT_INTS已读入isr时。可选。SoC 特有的中断处理核心总会执行rt_completion_done(port-done)并清除PORT_INTS。这些回调在 rt_ahci_host_register 中的实际落点如下HBA 复位ahci.cL558-L580写GHC.RESET最多等待 5 次 × 200ms 直到硬件自清零使能 AHCI 并读取端口信息L585-L600置GHC.AHCI_EN读CAP并按SPM | SSS | SIS掩码后回写、重读随后PI写0xf在此之后调用host_initL594失败即跳转_fail端口枚举循环L605-L732对ports_map中置位的每个端口——分配port-regsL618host-regs 0x100 i * 0x80→ 使端口停止清 LIST_ON/FIS_ON/FIS_RX/STARTL623-L633→ 调用port_initL635失败LOG_E后continue→ 置SPIN_UPL642→ 调用port_link_up或核心轮询SSTS.DETL648-L668→ 清SERR、等待TFD非忙、处理 COMINIT 重试、清挂起中断DMA 内存分配与引擎启动L736-L827对link为真的端口分配RT_AHCI_DMA_SIZEcoherent 内存布局命令槽偏移RT_AHCI_CMD_SLOT_SIZE 224、RX FIS256 字节、命令表RT_AHCI_CMD_TBL_HDR之后接 SG 区写CLB/CLBU、FB/FBU→ 调用port_dma_initL783出错仅记录→ 写PORT_CMD断言 ACTIVE/FIS_RX/POWER_ON/SPIN_UP/START → 等待 spin-up 完成20s 超时→ 写PORT_INTE中断使能掩码并rt_completion_init安装中断L829-L833设备名格式为ahci-%srt_dm_dev_get_name(host-parent.dev)rt_hw_interrupt_install(host-irq, ahci_isr, host, dev_name)后rt_hw_interrupt_umask注册 SCSI 主机L835-L845设置scsi-max_lun max(max_lun, 1)、scsi-max_id ports_nr、scsi-ops ahci_scsi_ops最后rt_scsi_host_register。全局 ISRahci_isrL509-L539的逻辑与port_isr的协作关系是遍历HBA_INTS中置位的端口位 → 读取PORT_INTS→ 若port-link为真先调用host-ops-port_isr(host, port, isr)可选随后无条件rt_completion_done(port-done)→ 清PORT_INTS→ 清HBA_INTS。这解释了文档中反复强调的覆盖port_isr时必须保持与rt_completion_done语义一致否则命令会挂死。回调与命令路径的底层联动rt_ahci_host_register完成后读写命令由 SCSI 传输回调ahci_scsi_transferahci.c驱动其内部会构造 Register H2D FISRT_AHCI_FIS_TYPE_REG_H2Dfis[2]为READ_EXT/WRITE_EXT/IDENTIFY/FLUSH/DSM等 ATA 命令码按max_blocks拆分 LBA 区间通过ahci_fill_sgL37-L72构建 PRDT/SG 表——每个 SG 项最大RT_ACHI_PRDT_BYTES_MAX4 MiB总数不能超过RT_AHCI_MAX_SG56并且当addr_hi非零而host-cap无RT_AHCI_CAP_64时直接失败这正是文档64 位 DMA构建 PRDT 时必须遵循RT_AHCI_CAP_64的代码出处ahci_fill_cmd_slot填命令槽后写PORT_CI触发执行然后rt_completion_wait(port-done, 10s)等待 ISR 置完成量读操作完成后执行rt_hw_cpu_dcache_ops(RT_HW_CACHE_INVALIDATE, ...)写操作前执行 FLUSH保证 cache 与 DMA 一致性。五、常见集成陷阱文档明确列出的四条陷阱每一处都能在源码中找到对应证据port_init与 DMA 的关系port-dma在port_init阶段尚未分配——DMA 缓冲区要到链路建立成功第二阶段循环L751-L752才通过rt_dma_alloc_coherent分配。因此在port_init里只能碰port-regs和 PHY 级寄存器任何依赖dma/cmd_tbl的代码都会踩空指针。port_dma_init出错只会打日志核心在 L783-L786 记录Init port[%d] DMA error后仍然会写PORT_CMD使能引擎。如果 SoC 缺少该回调的额外设置就无法运行应改在更早的port_link_up中返回失败让核心跳过该端口而不是带着错误继续启动。port_isr的完成量语义核心 ISR 在link为真时总会调用rt_completion_done(port-done)L532。若平台实现覆盖了port_isr必须保持与命令路径rt_completion_wait的配合否则命令会一直等待直到 10s 超时返回错误。64 位 DMA 能力一致性SG 地址高 32 位非零时必须确认host-cap包含RT_AHCI_CAP_64L59-L62否则填充 SG 表直接返回失败同时命令槽的tbl_addr_hi与CLBU/FBU寄存器的编程L34、L777、L781也依赖同样的 DMA 地址宽度假设。六、注册与注销 APIAPIRolert_ahci_host_register(host)校验host、host-parent.dev、host-ops执行 HBA 复位、端口枚举、链路检测、DMA 布局、rt_scsi_host_register与中断安装。失败路径统一rt_hw_interrupt_maskrt_pic_detach_irq。rt_ahci_host_unregister(host)先注销 SCSI 主机再屏蔽中断、rt_pic_detach_irq释放各端口ataid与 per-port DMA清除PORT_CMD引擎位最后清GHC.AHCI_EN | GHC.IRQ_EN。两者的声明位于 drivers/ahci.h实现位于 ahci.c。注销时各端口依次释放ataidrt_free与 DMArt_dma_free_coherentrt_ahci_host_unregister只要求host非空。七、平台示例PCI 与 DWC 两种接入形态仓库内已有两个真实接入样例可作为填结构体 注册模式的参照。PCI 总线 AHCIahci-pci.c默认使用 BAR 5#define AHCI_REG_BAR 5映射寄存器Cavium 控制器通过 quirk 指定 BAR 0。通用pci_ahci_ops只实现了host_initL34-L46对 JMicron 设备写配置寄存器0x41 0xa1规避已知问题Intel quirkL53-L67则在host_init中操作配置空间0x92切换相关模式。一个回调都不需要的空 ops 也能工作——dwc_ahci_ops就是{}。probe 流程L74-L140分配pci_ahci_host→ 设ahci-parent.dev→rt_pci_iomap→ 选 ops → 使能 MSI 或解屏蔽 INTx →rt_pci_set_master→rt_ahci_host_registerremove 流程对称调用rt_ahci_host_unregister。SoC 平台 DWC AHCIahci-dw.c通过设备树 compatiblesnps,dwc-ahci匹配L155-L158使用rt_dm_dev_iomap映射寄存器、rt_dm_dev_get_irq取中断。时钟与 PHY 在 probe 中由平台框架管理rt_clk_get_arrayrt_phye_get_by_name获取sata-phyrt_clk_array_prepare_enable/rt_phye_init/rt_phye_power_on上电再以const struct rt_ahci_ops dwc_ahci_ops {}空回调注册——说明链路检测、DMA 布局等一切默认行为都可由核心完成。若 SoC 需要额外的端口级 PHY 或 SATA 控制设置按第五节时序把逻辑放进host_init/port_init/port_link_up即可无需改动核心代码。八、与其他存储抽象层的关联AHCI 抽象层处于存储设备驱动栈的中间位置向下封装 AHCI 寄存器与 SATA 协议FIS、PRDT、命令槽向上对接 SCSI 子系统struct rt_scsi_host的transfer回调实现于 ahci.c最终由 SCSI 层把READ10/16、WRITE10/16、SYNCHRONIZE_CACHE、WRITE_SAME等 SCSI 命令翻译为 ATA 命令下发。其上层结构struct rt_scsi_hostmax_id、max_lun、ops等定义于 drivers/scsi.hSCSI 命令结构与 opcode 常量也在同一头文件中。官方文档指出本页所采用的填结构体 ops 注册顺序文档模式与其他存储/传输抽象层一致可对照阅读 ufs.md、nvme.md、scsi.md第 1 节、disk.md 与 dma.md第 1 节以快速掌握同类驱动接入方法。九、深入阅读指引全部寄存器定义、FIS 类型、ATA 命令码与rt_ahci_ata_id_*内联助手components/drivers/include/drivers/ahci.h核心实现注册/注销、ISR、SG 填充、SCSI 命令翻译components/drivers/ata/ahci.c平台接入样例components/drivers/ata/ahci-pci.c、components/drivers/ata/ahci-dw.c配置项components/drivers/ata/Kconfig、components/drivers/ata/SConscriptSCSI 上层接口components/drivers/include/drivers/scsi.h赞分享操作系统嵌入式物联网嵌入式OSRTOS【免费下载链接】rt-threadRT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/项目地址https://gitcode.com/gh_mirrors/rt/rt-thread点击查看免费下载相关推荐大麦自动抢票开源工具实战教程从配置到开票瞬间的双端全自动大麦自动抢票开源工具实战教程从配置到开票瞬间的双端全自动 开票 3 秒页面直接变缺货登记刷新也没用。这款开源抢票工具「大麦自动抢票」用 PythonGUI 自动化RPART-Thread CherryUSB 中 OHCI 主机控制器驱动的移植与配置指南RT Thread CherryUSB 中 OHCI 主机控制器驱动的移植与配置指南 导读 本文以 components/drivers/usb/cherryu操作系统嵌入式物联网嵌入式OSRTOSRT-Thread FAL 闪存抽象层 API 完全指南从分区管理到设备挂载的实战手册RT Thread FAL 闪存抽象层 API 完全指南从分区管理到设备挂载的实战手册 FALFlash Abstraction Layer闪存抽象层是操作系统嵌入式物联网嵌入式OSRTOS上一篇Apex Legends反后坐力系统基于像素识别与模式匹配的实时补偿方案下一篇如何高效获取A股市场数据Python通达信数据接口mootdx完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表