UHS-II SD卡Linux SCSI驱动架构设计

背景与挑战

在嵌入式相机项目中,我们面临一个独特的技术挑战:客户要求在Linux环境下支持UHS-II SD卡,而Linux内核主线(Kernel Mainline)的MMC框架尚未提供完整的UHS-II SD 4.0支持。

传统方案是将驱动提交到Linux内核主线,但这需要漫长的代码审核周期,且客户的项目周期不允许等待。在此背景下,我们采用了跨平台架构设计:在Windows环境下已经成熟运行的Storport Miniport驱动基础上,将核心业务逻辑移植到Linux SCSI框架,以Out-of-tree独立模块的方式交付。

整体架构设计

驱动采用分层架构,实现OS无关的业务逻辑与OS特定的适配层分离:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌─────────────────────────────────────────────────────────────┐
│ Linux SCSI 子系统 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ linux_scsi.c (SCSI命令处理层) │ │
│ │ • queuecommand 入口函数 │ │
│ │ • INQUIRY/READ_CAPACITY/RW 等命令处理 │ │
│ │ • SRB扩展管理 (srb_ext_t) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ linux_api.c (Linux OS适配层) │ │
│ │ • 线程/定时器/等待队列 │ │
│ │ • DMA内存分配与映射 │ │
│ │ • PCI中断处理 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 平台无关业务逻辑层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ main/ │ │ host/ │ │ card/ │ │
│ │ • 配置管理 │ │ • 主机控制器│ │ • SD/UHS-II协议 │ │
│ │ • 请求管理 │ │ • DMA传输 │ │ • 卡初始化 │ │
│ │ • 电源管理 │ │ • 命令处理 │ │ • 热插拔检测 │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ tagqueue/ (Tagged Command Queue) │ │
│ │ • 命令队列调度 • 多命令并行 │ │
│ │ • ADMA2/ADMA3/SDMA • 命令合并优化 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

核心数据结构

设备扩展结构 (bht_dev_ext_t)

设备扩展是整个驱动的核心数据结构,承载所有子模块的状态:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct bht_dev_ext {
sd_host_t host; // 主机控制器状态
sd_card_t card; // SD卡状态
os_struct os; // Linux OS相关资源
cfg_item_t *cfg; // 动态配置信息
tag_queue_t tag_queue; // 命令队列
dma_trans_api_t dma_api; // DMA传输API
pm_state_t pm_state; // 电源管理状态
soft_irq_t soft_irq; // 软中断
scsi_mng_t scsi; // SCSI层状态
srb_ext_t *p_srb_ext; // 当前SRB扩展指针
} bht_dev_ext_t;

SRB扩展结构 (srb_ext_t)

SRB (SCSI Request Block) 扩展是连接Linux SCSI层与业务逻辑的桥梁:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct srb_ext_t {
scsi_srb *psrb; // 指向Linux SCSI命令
request_t req; // 业务层请求结构

// DMA相关
sg_list_t srb_sg_list[MAX_SGL_RANGE]; // Scatter-Gather列表
u32 srb_sg_len; // SG列表长度

// 命令队列链
struct srb_ext_t *prev;
struct srb_ext_t *next;
} srb_ext_t;

Windows到Linux的关键移植点

1. SCSI命令处理框架

Windows使用Storport框架的HwScsiStartIo作为命令入口,Linux则使用queuecommand回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Linux SCSI命令入口
static int bht_scsi_queuecommand_lck(struct scsi_cmnd *srb)
{
bht_dev_ext_t *pdx = (bht_dev_ext_t *)
(srb->device->host->hostdata[0]);

switch (srb->cmnd[0]) {
case INQUIRY:
return bht_scsi_exec_inquiry(pdx, srb, &dev_busy);
case READ_10:
case WRITE_10:
return bht_scsi_exec_rw(pdx, srb, bWrite, &dev_busy);
// ... 其他命令
}
}

2. 内存管理适配

Windows和Linux的内存分配接口不同,通过抽象层统一:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Linux适配层
typedef struct {
// Windows: ExAllocatePool / ExFreePool
// Linux: kmalloc / kfree, vmalloc

void *os_alloc_buffer(...);
void os_free_buffer(...);

// Windows: MmAllocateContiguousMemory / MmFreeContiguousMemory
// Linux: dma_alloc_coherent / dma_free_coherent
bool os_alloc_dma_buffer(...);
void os_free_dma_buffer(...);
} os_struct;

3. 中断处理

1
2
3
4
5
6
7
8
9
10
11
12
13
// Linux中断处理
static irqreturn_t bht_sd_irq(int irq, void *dev_id)
{
bht_dev_ext_t *pdx = (bht_dev_ext_t *)dev_id;

if (pdx->host.pci_dev.use_msi)
irqret |= IRQ_HANDLED;
else {
if (ret) // 调用业务层中断处理
irqret |= IRQ_HANDLED;
}
return irqret;
}

4. PCI设备探测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static int bht_sd_probe(struct pci_dev *pdev,
const struct pci_device_id *pid)
{
bht_sd_slot_t *slot = bht_slot_getfree();
bht_dev_ext_t *pdx = &slot->data;

// 初始化PCI BAR空间
pci_enable_device(pdev);
pci_set_master(pdev);
pdx->host.pci_dev.membase = ioremap(
pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0)
);

// 注册PCI设备ID
bht_id_table[] = {
{ .vendor = 0x1217, .device = 0x9860 }, // GG8 Chip
{ .vendor = 0x1217, .device = 0x9861 },
// ...
{0, 0} // 终止标志
};
}

Out-of-tree模块编译

驱动以独立内核模块形式编译,不依赖内核树:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Makefile
KERNEL_SOURCE = /lib/modules/`uname -r`/build

obj-m := bht-sd.o
bht-sd-objs := \
linux_os/linux_base.o \
linux_os/linux_api.o \
linux_os/linux_scsi.o \
card/cardcommon.o card/cardinterface.o card/mmc.o \
card/sd.o card/uhs2.o \
host/host.o host/hostven.o host/cmdhandler.o \
host/irqhandler.o host/transhandler.o \
main/cfgmng.o main/thread.o main/reqmng.o \
tagqueue/tagqueue.o tagqueue/tqadma2.o \
tagqueue/tqadma3.o tagqueue/tqsdma.o \
util/debug.o

default:
$(MAKE) -C $(KERNEL_SOURCE) M=`pwd` modules

电源管理与热插拔

驱动支持完整的电源管理和卡热插拔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 运行时电源管理
static int bht_sd_runtime_suspend(struct device *dev)
{
bht_dev_ext_t *pdx = ...;
pdx->pm_state.rtd3_entered = TRUE;
req_enter_d3(pdx); // 进入D3状态
return 0;
}

// 卡热插拔检测线程
static void thread_main(void *param)
{
bht_dev_ext_t *pdx = param;

while (!pdx->os.thread_terminate_stop) {
os_wait_event(&pdx->os.event, EVENT_CARD_CHG);

if (card_detect_change(&pdx->card)) {
// 重新初始化卡
req_chk_card_info(pdx, srb_ext);
}
}
}

支持的芯片平台

芯片系列 Device ID 特性
GG8 0x9860-0x9863 DDR200支持
SeaEagle2 0x8720-0x8723 HS400支持
SandStorm2 0x8750, 0x8751 UHS-I/II
SDS2 0x8420, 0x8421 多模支持

总结

通过将Windows Storport框架下的业务逻辑完整移植到Linux SCSI框架,我们实现了:

  1. 快速交付:无需等待内核主线支持,3周内完成Linux版本开发
  2. 代码复用:95%以上的业务逻辑代码跨平台复用
  3. 灵活部署:Out-of-tree模块方式,支持客户定制内核
  4. 完整功能:UHS-II协议、热插拔、电源管理、Tagged Queue全部支持

这种架构设计模式对于需要快速响应客户需求、同时保持代码跨平台一致性的嵌入式项目具有重要参考价值。