背景与挑战
在嵌入式相机项目中,我们面临一个独特的技术挑战:客户要求在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; os_struct os; cfg_item_t *cfg; tag_queue_t tag_queue; dma_trans_api_t dma_api; pm_state_t pm_state; soft_irq_t soft_irq; scsi_mng_t scsi; srb_ext_t *p_srb_ext; } 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; request_t req;
sg_list_t srb_sg_list[MAX_SGL_RANGE]; u32 srb_sg_len;
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
| 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
| typedef struct {
void *os_alloc_buffer(...); void os_free_buffer(...);
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
| 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_enable_device(pdev); pci_set_master(pdev); pdx->host.pci_dev.membase = ioremap( pci_resource_start(pdev, 0), pci_resource_len(pdev, 0) );
bht_id_table[] = { { .vendor = 0x1217, .device = 0x9860 }, { .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
| 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); 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框架,我们实现了:
- 快速交付:无需等待内核主线支持,3周内完成Linux版本开发
- 代码复用:95%以上的业务逻辑代码跨平台复用
- 灵活部署:Out-of-tree模块方式,支持客户定制内核
- 完整功能:UHS-II协议、热插拔、电源管理、Tagged Queue全部支持
这种架构设计模式对于需要快速响应客户需求、同时保持代码跨平台一致性的嵌入式项目具有重要参考价值。