1、文 件 系 统 驱 动 编 程 基 础 篇 之 7端 口 读 写 关 键 字 : 文 件 系 统 驱 动 编 程 ,端 口 读 写 作 者 : wskjuf 更 新 : 2008-10-06 22:14:58 浏 览 : 10976文 件 系 统 驱 动 编 程 基 础 篇 之 七 端 口 读 写 一 、 前 略 本 系 列 文 章 为 业 余 编 程 爱 好 者 而 写 , 仅 仅 作 为 初 学 者 的 一 个 借 鉴 , 真 正 的 精 华 存 在 于 参 考 资 料 *中 。知 识 的 积 累 将 经 历 从 薄 到 厚 , 再 从 厚 到 薄 的 反 复 过 程 , 为 了 打 下
2、牢 固 的 基 础 , 请 读 者 务 必 在 阅 读本 文 的 基 础 上 花 费 必 要 的 时 间 完 成 参 考 资 料 。笔 者 的 实 践 环 境 为 :硬 件 : P35 Motherboard asm mov al, 0x10out 0x70, alin al, 0x71mov result, alprintf(“%X“, result);return 0;运 行 后 发 现 程 序 弹 出 了 异 常 , 无 法 执 行 out 指 令 , 给 出 的 提 示 为 :这 是 早 已 预 料 的 结 果 。 笔 者 搜 索 了 一 下 , 找 到 了 Direct Port I
3、O and Windows NT , 欣 欣然 之 下 禁 不 住 想 让 大 家 知 道 原 来 “不 需 要 ”写 驱 动 也 可 以 访 问 端 口 了 。 它 究 竟 是 何 方 神 圣 ? 原 来这 是 一 种 称 之 为 iopm 的 方 法 , 它 涉 及 了 CPU 硬 件 理 论 里 关 于 TS 段 的 基 础 知 识 :通 过 修 改 I/O 映 射 表 , 用 户 模 式 下 的 进 程 就 具 有 了 存 取 端 口 的 权 限 。 问 题 是 如 何 修 改 I/O映 射 表 ? 阅 读 了 porttalk 和 winio 的 源 代 码 后 , 发 现 所 谓
4、的 iopm 还 是 需 要 通 过 驱 动 代 码 修 改映 射 表 , 此 外 你 还 需 要 修 改 当 前 进 程 的 映 射 表 偏 移 地 址 。 当 我 们 费 力 的 写 好 近 千 行 代 码 , 换 来 的好 处 就 是 真 的 可 以 在 用 户 模 式 下 以 较 快 的 速 度 存 取 端 口 了 。 欣 欣 然 的 你 不 妨 用 文 初 给 出 的 测 试 程序 来 检 查 一 下 成 果 :如 果 把 驱 动 编 程 想 得 这 么 的 简 单 , 那 你 就 有 些 过 于 乐 观 了 。 现 在 尝 试 另 一 个 端 口 , 比 如 串 口 的0x3f8,
5、 看 看 有 什 么 奇 怪 的 事 情 发 生 ? 这 次 读 出 了 0xff, 那 么 0x3f9 呢 , 还 是 0xff, 一 直 读完 串 口 的 所 有 端 口 , 结 果 都 是 0xff。 假 如 我 们 首 先 用 CreateFile 来 打 开 串 口 , 怎 么 读 出 的 值不 再 是 0xff 了 , 如 果 关 闭 了 串 口 再 来 读 一 读 , 结 果 又 是 0xff 了 。 Google 或 baidu 后 , 发 现串 口 原 理 的 资 料 很 少 , 换 个 思 路 来 查 , 查 询 串 口 的 主 要 芯 片 从 8250 一 直 搜 索 到
6、16550A, 这 下 果 然 搜 出 了 有 价 值 的 资 料 , 如 Interfacing the Serial RS232 Port , 串 行 输入 输 出 接 口 , 常 用 的 输 入 输 出 接 口 芯 片 , UART 内 部 寄 存 器 对 应 端 口 及 用 途 等 。但 这 些 资 料 只 说 明 了 一 个 问 题 很 久 很 久 以 前 , 在 DOS 下 是 如 何 发 送 串 口 指 令 的 却还 是 没 有 解 决 Windows 下 的 问 题 。让 我 们 再 换 一 下 思 路 。 从 DevView 里 看 到 串 口 驱 动 的 基 本 情 况 ,
7、 原 来 串 口 的 PDO 是ACPI。阅 读 Advanced Configuration and Power Interface Specification , 可 知 ACPI 是 微 软 和 几 个厂 家 制 定 的 协 议 , 不 由 得 想 起 使 用 WINPE 启 动 的 时 候 选 择 了 ACPI, 结 果 机 子 一 次 次 的 重 启 ,本 杂 牌 机 无 论 从 外 观 还 是 认 证 标 签 上 看 极 象 兼 容 ACPI, 但 假 货 毕 竟 是 假 货 。 折 腾 了 半 天 后 ,因 我 们 的 目 的 不 在 于 如 何 写 ACPI 驱 动 , 故 这
8、 个 问 题 的 答 案 即 使 和 它 有 关 , 也 请 暂 时 绕 过 ( 实践 会 证 明 , 很 多 问 题 都 是 冤 家 路 窄 , 避 是 避 不 过 的 _。 对 ACPI 调 用 IoCallDriver 后 , 激活 了 串 口 ) 。 三 、 串 口 代 码 之 实 践 本 篇 最 终 目 的 是 阅 读 并 调 试 串 口 程 序 , 因 笔 者 使 用 的 DDK 版 本 是 3790 的 , 源 代 码 位 在WINDDK3790srckernelserial 下 。 首 先 我 们 要 做 的 是 完 善 串 口 的 调 试 输 出 信 息 函 数 , 修 改
9、位于 log.c 下 的 SerialDbgPrintEx 如 下 : ULONGSerialDbgPrintEx(IN ULONG Level, PCHAR Format, .) / 级 别 , 格 式 , 不 定 的 参 数va_list arglist;ULONG rval;ULONG Mask = 0;ULONG cb;UCHAR bufferSERIAL_DBGPRINT_BUFSIZE;RTL_QUERY_REGISTRY_TABLE paramTable4;ULONG zero = 0;if (KeGetCurrentIrql() = PASSIVE_LEVEL)RtlZeroM
10、emory (mTable0, sizeof(paramTable);paramTable0.Flags = RTL_QUERY_REGISTRY_DIRECT;paramTable0.Name = L“SerialDebugLevelBegin“;paramTable0.EntryContext = / 保 存 查 询 结 果paramTable0.DefaultType = REG_DWORD;paramTable0.DefaultData = paramTable0.DefaultLength = sizeof(ULONG);paramTable1.Flags = RTL_QUERY_R
11、EGISTRY_DIRECT;paramTable1.Name = L“SerialDebugLevelEnd“;paramTable1.EntryContext = / 保 存 查 询 结 果paramTable1.DefaultType = REG_DWORD;paramTable1.DefaultData = paramTable1.DefaultLength = sizeof(ULONG);paramTable2.Flags = RTL_QUERY_REGISTRY_DIRECT;paramTable2.Name = L“SerialDebugLevelCloseHigh“;param
12、Table2.EntryContext = / 保 存 查 询 结 果paramTable2.DefaultType = REG_DWORD;paramTable2.DefaultData = paramTable2.DefaultLength = sizeof(ULONG);RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,SerialGlobals.RegistryPath.Buffer, / HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesSeria
13、lmTable0, / 以 全 0 的 结 尾 表 结 束 , 这 样 一 次 就 可 以 查 询 多 个 值 了NULL,NULL);Mask = Level;else if (SerialDebugLevelCloseHigh = 1) / 不 显 示 高 irql 信 息return STATUS_SUCCESS;if (Mask SerialDebugLevelEnd) return STATUS_SUCCESS;va_start(arglist, Format); / arglist 越 过 了 格 式 串 , 指 向 了 参 数cb = _vsnprintf(buffer, siz
14、eof(buffer), Format, arglist);if (cb = -1) / 串 长 于 bufferbuffersizeof(buffer) - 2 = n;DbgPrint(“IRQL %d, SERIAL: %s“, KeGetCurrentIrql(), buffer);/rval = vDbgPrintEx(DPFLTR_SERIAL_ID, Level, Format, arglist);va_end(arglist);rval = STATUS_SUCCESS;return rval;往 注 册 表 里 添 加SerialDebugLevelBegin, Serial
15、DebugLevelEnd, SerialDebugLevelCloseHigh 三 键 , 我 们 可 以更 灵 活 的 动 态 控 制 调 试 信 息 。 编 译 好 Checked 模 式 的 串 口 驱 动 程 序 后 , 接 下 来 需 要 替 换 掉 系 统 自 带 的 串 口 驱 动WINDOWSsystem32drivers serial.sys( 大 小 为 59KB) 。 希 望 你 首 先 按 照 自 己 的 思 路 来 尝 试替 换 , 如 果 失 败 了 再 来 尝 试 如 下 的 方 法 : 1. 关 闭 系 统 还 原 2. 改 名 WINDOWSsystem32
16、dllcacheserial.sys3. 将 WINDOWSsystem32CatRoot 目 录 改 名 为 其 他 名 字 4. 将 你 编 译 好 的 checked 模 式 的 串 口 驱 动 程 序 覆 盖WINDOWSsystem32driversserial.sys5. 在 硬 件 管 理 器 里 删 除 掉 串 口 驱 动 , 再 重 新 扫 描 硬 件 变 更 , 重 新 安 装 驱 动 时 可 能 出 现“是 否 替 换 ”的 提 示 , 选 择 否 , 直 到 你 发 现 驱 动 目 录 下 的 串 口 驱 动 程 序 确 实 是 你 自 己 编译 过 的 程 序 6.
17、运 行 Dbgview 后 在 硬 件 管 理 器 里 停 止 串 口 驱 动 , 再 启 动 驱 动 , 此 时 可 以 看 见 在Dbgview 里 看 见 调 试 信 息 ( 前 提 是 注 册 表 已 设 好 上 述 三 键 的 值 ) 为 了 方 便 阅 读 代 码 , 简 单 说 明 一 下 串 口 寄 存 器 。 端 口 0x3f8-0x3fe 用 于 微 机 上 COM1 串 行口 , 0x2f8-0x2fe 对 应 COM2 端 口 。 DLAB(Divisor Latch Access Bit)是 除 数 锁 存 访 问 位 , 是指 线 路 控 制 寄 存 器 的 最 高
18、 位 7。表 UART 内 部 寄 存 器 对 应 端 口 及 用 途序 号 端 口 读 /写 条 件 用 途0 0x3f8 (0x2f8)写 DLAB=0 发 送 保 持 寄 存 器 。 含 有 将 发 送 的 字 符 。 TRANSMIT_HOLDING_REGISTER读 DLAB=0 接 收 缓 存 寄 存 器 。 含 有 收 到 的 字 符 。 RECEIVE_BUFFER_REGISTER读 /写 DLAB=1 波 特 率 因 子 低 字 节 ( LSB) 。 DIVISOR_LATCH_LSB1 0x3f9 (0x2f9)读 /写 DLAB=1 波 特 率 因 子 高 字 节 (
19、 MSB) 。 DIVISOR_LATCH_MSB读 /写 DLAB=0 中 断 允 许 寄 存 器 。 INTERRUPT_ENABLE_REGISTER位 7-4 全 0 保 留 不 用 ;位 3=1 modem 状 态 中 断 允 许 ;位 2=1 接 收 器 线 路 状 态 中 断 允 许 ;位 1=1 发 送 保 持 寄 存 器 空 中 断 允 许 ;位 0=1 已 接 收 到 数 据 中 断 允 许 。2 0x3fa (0x2fa)读 中 断 标 识 寄 存 器 。 中 断 处 理 程 序 用 以 判 断 此 次 中 断 是 4 种 中 的 那 一 种 。INTERRUPT_IDE
20、NT_REGISTER位 7-3 全 0( 8250 不 用 , 16550: 7-6=11 允 许 fifo, =00 不 允 许 fifo) ;位 2-1 确 定 中 断 的 优 先 级 ;= 11 接 收 状 态 有 错 中 断 , 优 先 级 最 高 ;= 10 已 接 收 到 数 据 中 断 , 优 先 级 第 2;= 01 发 送 保 持 寄 存 器 空 中 断 , 优 先 级 第 3;= 00 modem 状 态 改 变 中 断 , 优 先 级 第 4。位 0=0 有 待 处 理 中 断 ; =1 无 中 断 。3 0x3fb (0x2fb)写 线 路 控 制 寄 存 器 。 L
21、INE_CONTROL_REGISTER位 7=1 除 数 锁 存 访 问 位 (DLAB)。 =0 访 问 发 送 保 持 寄 存 器 、 接 收 缓 存 寄 存 器 器 或中 断 允 许 寄 存 器 ;位 6=1 强 迫 SOUT 送 出 空 闲 状 态 ; 0 禁 止 间 断 ;保 持 奇 偶 位 位 5=1 偶 校 验 时 , 校 验 位 =0 奇 校 验 时 , 校 验 位 =1; 0 无 数位 4, 3=11 偶 校 验 ; =01 奇 校 验 ; x0 不 加 校 验 位位 2=1 ( 5 位 数 据 位 时 ) 1.5 位 停 止 位 , ( 6, 7, 8 位 数 据 位 时
22、 ) 2 位 停 止 位 ;=0 1 位 停 止 位 ;位 1-0 数 据 位 长 度 : = 00 5 位 数 据 位 ; = 01 6 位 数 据 位 ; = 10 7 位 数 据 位 ; = 11 8 位 数 据 位 。4 0x3fc (0x2fc)写 modem 控 制 寄 存 器 。 MODEM_CONTROL_REGISTER位 7-5 全 0 保 留 ;位 4=1 芯 片 处 于 循 环 反 馈 诊 断 操 作 模 式 ; ( 自 测 试 循 环 ) SERIAL_MCR_LOOP位 3=1 辅 助 用 户 指 定 输 出 2, 允 许 INTRPT 到 系 统 ; SERIAL
23、_MCR_OUT2位 2=1 辅 助 用 户 指 定 输 出 1, PC 机 未 用 ; SERIAL_MCR_OUT1位 1=1 使 请 求 发 送 RTS 有 效 ; SERIAL_MCR_RTS位 0=1 使 数 据 终 端 就 绪 DTR 有 效 。 SERIAL_MCR_DTR5 0x3fd (0x2fd)读 线 路 状 态 寄 存 器 。 LINE_STATUS_REGISTER位 7=0 保 留 ;位 6=1 发 送 移 位 寄 存 器 为 空 ;位 5=1 发 送 保 持 寄 存 器 为 空 , 可 以 取 字 符 发 送 ;位 4=1 接 收 到 满 足 间 断 条 件 的
24、位 序 列 ;位 3=1 帧 格 式 错 误 ;位 2=1 奇 偶 校 验 错 误 ;位 1=1 超 越 覆 盖 错 误 ;位 0=1 接 收 器 数 据 准 备 好 , 系 统 可 读 取 。6 0x3fe (0x2fe)读 modem 状 态 寄 存 器 。 表 示 信 号 发 生 变 化 。 MODEM_STATUS_REGISTER位 7=1 载 波 检 测 (CD)有 效 ;位 6=1 响 铃 指 示 (RI)有 效 ;位 5=1 数 据 设 备 就 绪 (DSR)有 效 ;位 4=1 清 除 发 送 ( CTS) 有 效 ;位 3=1 检 测 到 载 波 ;位 2=1 检 测 到
25、响 铃 信 号 边 沿 ;位 1=1 数 据 设 备 就 绪 (DSR);位 0=1 清 除 发 送 (CTS)。#define RECEIVE_BUFFER_REGISTER? (ULONG)(0x00)*SERIAL_REGISTER_STRIDE)/ 本 文 转 自 C+Builder 研 究 - http:/ TRANSMIT_HOLDING_REGISTER? (ULONG)(0x00)*SERIAL_REGISTER_STRIDE)#define INTERRUPT_ENABLE_REGISTER? (ULONG)(0x01)*SERIAL_REGISTER_STRIDE)#def
26、ine INTERRUPT_IDENT_REGISTER? (ULONG)(0x02)*SERIAL_REGISTER_STRIDE)#define FIFO_CONTROL_REGISTER? (ULONG)(0x02)*SERIAL_REGISTER_STRIDE)#define LINE_CONTROL_REGISTER? (ULONG)(0x03)*SERIAL_REGISTER_STRIDE)#define MODEM_CONTROL_REGISTER? (ULONG)(0x04)*SERIAL_REGISTER_STRIDE)#define LINE_STATUS_REGISTER
27、? (ULONG)(0x05)*SERIAL_REGISTER_STRIDE)#define MODEM_STATUS_REGISTER? (ULONG)(0x06)*SERIAL_REGISTER_STRIDE)前 几 章 中 涉 及 的 代 码 量 不 过 千 行 左 右 , 此 次 阅 读 的 个 别 子 例 程 代 码 量 已 达 到 上 千 行 , 所 以 要 特 别注 意 阅 读 的 技 巧 。 首 先 大 略 了 解 各 文 件 的 基 本 功 能 , 从 DriverEntry 起 , 分 步 阅 读 各 个 派 遣例 程 , 适 时 记 录 流 程 中 涉 及 的 关 键 函 数 , 至 少 读 懂 全 部 代 码 的 45 并 攻 克 本 文 提 出 的 问 题 。 如 果 你 已 经 开 始 厌 烦 没 完 没 了 的 书 写 重 复 的 代 码 , 那 么 你 就 应 该 有 所 理 解 为 何 微 软 在KMDF 模 型 下 提 供 现 成 的 PNP 和 电 源 管 理 代 码 的 原 因 了 。