一、为什么有 Linux 背景的工程师需要这篇
前面的文章已经把 Linux MMC 子系统从头到尾走了一遍——从 mmc_host 的 struct sdhci_host 到 mmc_card 的 ext_csd[512],从 _mmc_suspend 的三层防线到 mmc_blk_part_switch 的分区切换。如果你已经消化了那些内容,你对 MMC 子系统的理解已经有了一个完整的参考系。
现在进入 Windows 侧,这篇不会从零开始讲 SD 协议,而是以 Linux MMC 三层模型为对照,逐层映射到 Windows 侧,让你用已有的知识锚点快速建立 Windows SD 栈的认知。
二、三层架构对比:Linux vs Windows
两个 OS 都把 SD 栈分成三层,但划分的边界和职责不同:

核心差异
Linux 的 mmc_core 是内核内的一个中间层模块,代码公开,你可以直接修改 mmc_ops.c 或 block.c。Windows 的 sdbus.sys 是微软提供的闭源总线驱动,你无法修改它的协议状态机,只能通过它暴露的接口(WDF request、IOCTL、回调)与之交互。
Linux 的 port/host 驱动是一个内核模块(sdhci.ko),你写 struct sdhci_ops 填充回调即可。Windows 的 sdhc.sys 也是微软提供的端口驱动,你在它之下写 miniport 或直接替换整个 port driver。
三、sdhc.sys:端口的寄存器操作者
sdhc.sys 在 Windows SD 栈中的角色等价于 Linux 的 sdhci.c——它负责 SD Host Controller 标准寄存器(偏移 0x00–0xFE)的读写、中断处理和 DMA 传输。
寄存器映射
SD Host Controller Spec 定义的标准寄存器布局,sdhc.sys 每一组都有对应操作:
| 偏移范围 |
寄存器组 |
sdhc.sys 操作 |
| 0x00–0x0D |
SD Command / Argument / Response |
发命令前写 Argument,然后写 Command 寄存器触发 |
| 0x0E–0x0F |
保留 |
— |
| 0x10–0x1B |
Present State / Host Control / Clock Control |
读 Card Inserted 位、写时钟分频 |
| 0x1C–0x1F |
Timeout Control |
设置 Data Timeout 计数器(基于 SD Clock) |
| 0x20–0x23 |
Software Reset |
写 bit0 (CMD Line Reset) 或 bit1 (DAT Line Reset) |
| 0x24–0x2D |
Normal/Error Interrupt Status |
中断产生后读状态位,写 1 清除 |
| 0x2E–0x3F |
Capabilities / MAX Current |
只读:64-bit DMA 支持、电压范围 |
在 Linux 侧你通过 sdhci_readw(host, SDHCI_INT_STATUS) 读中断状态——在 Windows 侧 sdhc.sys 替你做了完全一样的寄存器操作,然后把中断事件通过 WDF interrupt 对象向上层报告。
中断处理对比
// Linux: sdhci.c 的中断处理(你写 sdhci_ops 回调即可)
static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
struct sdhci_host *host = dev_id;
u16 mask = sdhci_readw(host, SDHCI_INT_STATUS);
if (mask & SDHCI_INT_CMD_COMPLETE)
sdhci_finish_command(host);
if (mask & SDHCI_INT_TRANS_COMPLETE)
sdhci_finish_data(host);
return IRQ_HANDLED;
}
// Windows: sdhc.sys 的等价逻辑(你在 miniport 层不可见)
// sdhc.sys 内部 ISR:
BOOLEAN SdhcIsr(PKINTERRUPT Interrupt, PVOID Context) {
UCHAR status = READ_REGISTER_UCHAR(regs + SDHC_NORMAL_INT_STATUS);
if (status & TRANSFER_COMPLETE) {
WRITE_REGISTER_UCHAR(regs + SDHC_NORMAL_INT_STATUS, TRANSFER_COMPLETE);
// 通过 WDFREQUEST 向上层 sdbus.sys 报告完成
WdfRequestComplete(/* 对应的 request */, STATUS_SUCCESS);
}
return TRUE;
}
关键差异
Linux 的 sdhci_irq 是你的代码,你可以加打印、改逻辑。Windows 的 SdhcIsr 是微软的,你只能通过 WDFREQUEST 的 completion 拿到结果。调试时 Linux 用 printk 看中断状态,Windows 用 !wdfkd.wdfinterrupts 看中断计数。
四、sdbus.sys:协议状态机的拥有者
sdbus.sys 是 Windows SD 栈的"大脑"。它的职责等价于 Linux mmc_core 中 mmc.c + mmc_ops.c + sd_ops.c 的总和——管理卡的生命周期:插入检测 → 初始化 → 枚举功能 → 数据传输 → 移除。
初始化状态机
从卡插入到就绪,sdbus.sys 的协议状态机走以下状态(与 SD Spec §A2 状态图一致):

与 Linux 对比:
- Linux 的开源代码 mmc_rescan → mmc_init_card → mmc_sd_init_card 走完全相同的序列,你可以加 dev_info 看每一步
- Windows 的 sdbus.sys 走同样的序列,但你是旁观者——你只能在 miniport 层通过 sdhc.sys 看到寄存器读写,看不到 sdbus.sys 的状态跃迁
SDIO vs SD Memory 的分叉
这是 sdbus.sys 最关键的一个判断点。SD 卡和 SDIO 卡在物理上共用相同的接口,但初始化路径在 CMD5/CMD8 之后分叉:
SD Memory 类设备(包括 eMMC):sdbus.sys 加载 sdstor.sys,呈现为磁盘
SDIO 类设备(WiFi/Bluetooth):sdbus.sys 加载厂商提供的 SDIO function driver,不经过 sdstor.sys
Linux 侧这个分叉在 mmc_rescan → mmc_attach_sd / mmc_attach_sdio 中完成。Windows 侧 sdbus.sys 内部做同样的决策,但你的 SDIO function driver 看不到这个过程——它只在被加载时拿到一个已经初始化好的 Function Context。
五、sdstor.sys:从 SD 卡到磁盘
sdstor.sys 等价于 Linux 的 mmc_block.c——它把 SD Memory 卡呈现为 Windows 的磁盘设备(\Device\HarddiskX),让文件系统可以 mount。
数据路径:
NTFS.sys
→ IRP_MJ_READ/WRITE
→ disk.sys(磁盘类驱动)
→ sdstor.sys(SD 存储功能驱动)
→ WDFREQUEST → sdbus.sys(协议封装为 SD 命令)
→ WDFREQUEST → sdhc.sys(SD 命令编码为寄存器写)
→ SD Host Controller 寄存器
Linux 侧等价路径:
ext4
→ submit_bio
→ mmc_queue → mmc_blk_issue_rq
→ mmc_start_request
→ sdhci_send_command → sdhci_writew(reg, SDHCI_COMMAND)
六、调试手段对比
| 调试需求 |
Linux 手段 |
Windows 手段 |
| 看当前卡状态 |
cat /sys/kernel/debug/mmc0/ios |
!wdfkd.wdfdevicequeues + 寄存器 dump |
| 看 EXT_CSD |
mmc extcsd read /dev/mmcblk0 |
WinDbg 手动读 EXT_CSD 偏移(通过 IOCTL 或 HAL MMIO) |
| 看中断计数 |
cat /proc/interrupts \| grep mmc |
!wdfkd.wdfinterrupts |
| 看 DMA 状态 |
cat /sys/kernel/debug/mmc0/dma |
WDF Verifier + !wdfkd.wdfdma |
| 看命令超时 |
dmesg \| grep "mmc0: timeout" |
!wdfkd.wdfrequest 找 pending 的 request |
七、工程总结
Windows SD 栈的层次映射到 Linux 是正交的
Linux 的核心价值在 mmc_core(可修改),Windows 的核心价值在 sdbus.sys(不可修改但可靠)。写 Windows SD 驱动的心态不是"我把协议实现一遍",而是"我在微软的协议实现之上加硬件适配"。