【Linux驱动开发】Linux SDIO 底层原理与实现细节详解
Linux SDIO 底层原理与实现细节详解
1. SDIO 协议概述
SDIO (Secure Digital Input Output) 是一种基于 SD 卡接口的扩展协议,它允许在 SD 卡物理接口上连接各种外设设备。SDIO 不仅支持存储设备,还支持 WiFi、蓝牙、GPS、摄像头等多种功能设备。
1.1 SDIO 协议特点
- 兼容性: 后向兼容 SD 存储卡协议
- 多功能: 支持多种外设类型
- 热插拔: 支持设备的热插拔操作
- 低功耗: 适用于移动设备
- 高速传输: 支持高速数据传输模式
2. SDIO 硬件接口
2.1 物理接口
SDIO 使用 9-pin 接口,与标准 SD 卡接口完全兼容:
引脚定义:
┌─────┬─────┬─────┐
│ 1 │ 2 │ 3 │
│ CD/DAT3 │ CMD │ VSS │
├─────┼─────┼─────┤
│ 4 │ 5 │ 6 │
│ VDD │ CLK │ VSS │
├─────┼─────┼─────┤
│ 7 │ 8 │ 9 │
│ DAT0 │ DAT1 │ DAT2 │
└─────┴─────┴─────┘
2.2 信号线功能
- CMD: 命令/响应线,双向传输
- CLK: 时钟信号,由主机提供
- DAT0-DAT3: 数据线,支持 1-bit 或 4-bit 模式
- VDD/VSS: 电源和地线
- CD: 卡检测信号
3. SDIO 协议架构
3.1 协议层次结构
┌─────────────────────────────────────┐
│ 应用层 (Application) │
├─────────────────────────────────────┤
│ SDIO 功能层 (Function) │
├─────────────────────────────────────┤
│ SDIO 核心层 (SDIO Core) │
├─────────────────────────────────────┤
│ SD 物理层 (Physical) │
└─────────────────────────────────────┘
3.2 功能编号 (Function Number)
SDIO 设备可以包含多个功能:
- Function 0: 卡信息功能 (CIA - Card Information Area)
- Function 1-7: 具体设备功能
- Function 8-255: 保留
3.3 寄存器空间
SDIO 设备寄存器空间映射:
0x00-0x0FFF: Function 0 (CIA) - 卡基本信息
0x1000-0x1FFF: Function 1 寄存器空间
0x2000-0x2FFF: Function 2 寄存器空间
...
0x7000-0x7FFF: Function 7 寄存器空间
4. Linux SDIO 子系统架构
4.1 子系统组件
Linux SDIO 子系统主要包含以下组件:
┌─────────────────────────────────────┐
│ SDIO 设备驱动层 │
│ (WiFi, Bluetooth, GPS, etc.) │
├─────────────────────────────────────┤
│ SDIO 核心层 │
│ (mmc/sdio/core.c) │
├─────────────────────────────────────┤
│ MMC/SD 主机控制器驱动 │
│ (mmc/host/*.c) │
├─────────────────────────────────────┤
│ 硬件平台层 │
│ (SoC SDIO 控制器) │
└─────────────────────────────────────┘
4.2 核心数据结构
4.2.1 struct sdio_func
struct sdio_func {struct mmc_card *card; /* 关联的 MMC 卡 */struct sdio_func_tuple *tuples; /* 功能元数据 */unsigned int num; /* 功能编号 (1-7) */unsigned char class; /* 设备类别 */u16 vendor; /* 厂商 ID */u16 device; /* 设备 ID */unsigned int max_blksize; /* 最大块大小 */unsigned int cur_blksize; /* 当前块大小 */struct device dev; /* 设备模型 */struct sdio_func *next; /* 下一个功能 *//* 中断处理 */sdio_irq_handler_t *irq_handler;void *irq_data;/* 电源管理 */unsigned int enable_timeout;
};
4.2.2 struct mmc_card
struct mmc_card {struct mmc_host *host; /* 主机控制器 */struct device dev; /* 设备模型 */unsigned int rca; /* 相对地址 */unsigned int type; /* 卡类型 (MMC/SD/SDIO) */unsigned int state; /* 卡状态 *//* SDIO 特有字段 */unsigned int sdio_funcs; /* SDIO 功能数量 */struct sdio_func *sdio_func[SDIO_MAX_FUNCS];/* 卡信息 */char *name; /* 卡名称 */struct cis *cis; /* 卡信息结构 */
};
5. SDIO 初始化流程
5.1 卡检测与识别
/* 1. 卡插入检测 */
mmc_detect_change(host, 0);/* 2. 发送 CMD5 (IO_SEND_OP_COND) */
mmc_send_io_op_cond(host, ocr, &rocr);/* 3. 获取卡相对地址 */
mmc_send_relative_addr(card, &card->rca);/* 4. 选择卡 */
mmc_select_card(card);/* 5. 读取 CIS (Card Information Structure) */
sdio_read_cis(card);/* 6. 初始化各个功能 */
for (func = 1; func <= card->sdio_funcs; func++) {sdio_init_func(card, func);
}
5.2 CIS (Card Information Structure) 解析
CIS 包含设备的关键信息:
struct cis_tuple {u8 code; /* Tuple 代码 */u8 size; /* 数据大小 */u8 data[0]; /* 数据内容 */
};
主要 Tuple 类型:
- 0x20: 功能扩展 Tuple
- 0x21: 功能信息 Tuple
- 0x22: 功能扩展信息 Tuple
- 0x80: 厂商特定 Tuple
6. SDIO 数据传输机制
6.1 传输模式
SDIO 支持两种数据传输模式:
6.1.1 字节模式 (Byte Mode)
/* 读取单个寄存器 */
u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret);/* 写入单个寄存器 */
void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret);
6.1.2 块模式 (Block Mode)
/* 读取数据块 */
int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, int count);/* 写入数据块 */
int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src, int count);
6.2 DMA 传输
Linux SDIO 支持 DMA 传输以提高性能:
struct mmc_data {unsigned int timeout_ns; /* 超时时间 */unsigned int timeout_clks; /* 时钟周期超时 */unsigned int blksz; /* 块大小 */unsigned int blocks; /* 块数量 */unsigned int error; /* 错误码 */unsigned int flags; /* 标志位 *//* 数据缓冲区 */struct scatterlist *sg; /* 散列表 */unsigned int sg_len; /* 散列表长度 *//* DMA 相关 */int dma_ch; /* DMA 通道 */unsigned int dma_dir; /* DMA 方向 */
};
7. SDIO 中断处理
7.1 中断类型
SDIO 支持两种中断模式:
7.1.1 功能中断 (Function Interrupt)
每个功能可以独立产生中断:
/* 注册中断处理函数 */
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);/* 释放中断 */
int sdio_release_irq(struct sdio_func *func);
7.1.2 卡中断 (Card Interrupt)
整个卡产生的中断:
/* 在主机控制器中处理卡中断 */
static irqreturn_t sdio_irq(int irq, void *dev_id)
{struct mmc_host *host = dev_id;/* 读取中断状态 */status = sdio_readb(func, SDIO_CCCR_INTx, &err);/* 处理各个功能的中断 */for (i = 1; i <= 7; i++) {if (status & (1 << i)) {/* 调用功能的中断处理函数 */if (card->sdio_func[i]->irq_handler) {card->sdio_func[i]->irq_handler(card->sdio_func[i]);}}}return IRQ_HANDLED;
}
7.2 中断使能寄存器
/* CCCR (Card Common Control Registers) 中的中断控制 */
#define SDIO_CCCR_IENx 0x04 /* 中断使能寄存器 */
#define SDIO_CCCR_INTx 0x05 /* 中断状态寄存器 */
#define SDIO_CCCR_ABORT 0x06 /* 中止控制寄存器 */
8. 电源管理
8.1 电源状态
SDIO 定义了三种电源状态:
- Active: 全功率运行
- Idle: 低功耗状态,保持上下文
- Standby: 最低功耗,丢失上下文
8.2 电源控制寄存器
/* 电源控制相关寄存器 */
#define SDIO_CCCR_BUS_IF_CTRL 0x07 /* 总线接口控制 */
#define SDIO_CCCR_CARD_CAPS 0x08 /* 卡能力 */
#define SDIO_CCCR_CIS_PTR 0x09 /* CIS 指针 */
#define SDIO_CCCR_BUS_SUSP 0x0C /* 总线挂起控制 */
#define SDIO_CCCR_FUNC_SEL 0x0D /* 功能选择 */
#define SDIO_CCCR_EXEC_FLAGS 0x0E /* 执行标志 */
#define SDIO_CCCR_READY_FLAGS 0x0F /* 就绪标志 */
8.3 Linux 电源管理实现
static int sdio_suspend(struct device *dev)
{struct sdio_func *func = dev_to_sdio_func(dev);struct sdio_driver *drv = to_sdio_driver(dev->driver);/* 调用驱动的挂起函数 */if (drv->suspend) {ret = drv->suspend(func);if (ret)return ret;}/* 禁用功能 */sdio_disable_func(func);return 0;
}static int sdio_resume(struct device *dev)
{struct sdio_func *func = dev_to_sdio_func(dev);struct sdio_driver *drv = to_sdio_driver(dev->driver);/* 启用功能 */ret = sdio_enable_func(func);if (ret)return ret;/* 调用驱动的恢复函数 */if (drv->resume) {ret = drv->resume(func);if (ret)goto disable_func;}return 0;
}
9. SDIO 驱动开发
9.1 驱动结构
struct sdio_driver {char *name; /* 驱动名称 */const struct sdio_device_id *id_table; /* 支持的设备 ID */int (*probe)(struct sdio_func *); /* 探测函数 */void (*remove)(struct sdio_func *); /* 移除函数 *//* 电源管理 */int (*suspend)(struct sdio_func *);int (*resume)(struct sdio_func *);struct device_driver drv;
};
9.2 设备 ID 匹配
static const struct sdio_device_id my_sdio_ids[] = {{ SDIO_DEVICE(SDIO_VENDOR_ID_XXX, SDIO_DEVICE_ID_XXX) },{ /* 结束标记 */ }
};
MODULE_DEVICE_TABLE(sdio, my_sdio_ids);
9.3 驱动注册
static struct sdio_driver my_sdio_driver = {.name = "my_sdio_device",.id_table = my_sdio_ids,.probe = my_sdio_probe,.remove = my_sdio_remove,.suspend = my_sdio_suspend,.resume = my_sdio_resume,
};static int __init my_sdio_init(void)
{return sdio_register_driver(&my_sdio_driver);
}static void __exit my_sdio_exit(void)
{sdio_unregister_driver(&my_sdio_driver);
}module_init(my_sdio_init);
module_exit(my_sdio_exit);
10. 调试技术
10.1 调试工具
# 查看 SDIO 设备信息
ls /sys/bus/sdio/devices/# 查看驱动信息
ls /sys/bus/sdio/drivers/# 查看详细的 SDIO 信息
cat /sys/kernel/debug/mmc*/ios
cat /sys/kernel/debug/mmc*/clock
cat /sys/kernel/debug/mmc*/stats
10.2 内核调试选项
# 启用 MMC/SDIO 调试信息
CONFIG_MMC_DEBUG=y
CONFIG_MMC_VERBOSE_PROBE=y# 查看内核日志
dmesg | grep -i sdio
dmesg | grep -i mmc
10.3 常见问题排查
10.3.1 设备识别失败
# 检查硬件连接
# 验证电压和时钟信号
# 检查设备树配置
10.3.2 数据传输错误
# 检查 DMA 配置
# 验证时钟频率设置
# 检查数据线连接
10.3.3 中断问题
# 检查中断引脚配置
# 验证中断处理函数
# 检查中断使能寄存器
11. 性能优化
11.1 传输优化
- 使用 DMA 传输: 减少 CPU 占用
- 增加块大小: 提高传输效率
- 使用高速模式: 支持 50MHz 以上时钟
11.2 中断优化
- 中断合并: 减少中断频率
- 轮询模式: 在高负载时使用轮询
- 中断亲和性: 绑定到特定 CPU
11.3 电源优化
- 动态时钟: 根据负载调整时钟频率
- 智能休眠: 空闲时进入低功耗模式
- 批量操作: 合并多个操作为一次传输
12. 安全考虑
12.1 数据安全
- 加密传输: 对敏感数据进行加密
- 访问控制: 实现基于功能的访问控制
- 安全启动: 验证设备固件完整性
12.2 系统安全
- 权限控制: 限制 SDIO 设备访问权限
- 输入验证: 验证所有用户输入
- 错误处理: 安全的错误处理机制
13. 总结
Linux SDIO 子系统提供了完整的 SDIO 设备支持框架,包括设备识别、数据传输、中断处理和电源管理等功能。理解其底层原理和实现细节对于开发高性能、高可靠性的 SDIO 设备驱动至关重要。
通过深入了解 SDIO 协议、Linux 内核实现机制以及调试技术,开发者可以更好地利用 SDIO 接口连接各种外设,构建功能丰富的嵌入式系统。
参考资料
- SDIO 规范: SDIO Specification Version 3.0
- Linux 内核源码: drivers/mmc/core/
- 内核文档: Documentation/mmc/
- SD 协会: www.sdcard.org
- Linux MMC 项目: git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc.git
本文档基于 Linux 5.x 内核版本编写,具体实现可能因内核版本而异。
