Windows XP下usbport.sys驱动内部实现解析(三)
2010-01-22 18:16
(接上一篇)
_USBPORT_MapTransfer是个比较复杂的函数了,他涉及到transfer的切割、sgList结构的填写,少安毋躁。。。哈哈
struct SG_LIST
{
ULONG Flags;
PVOID CurrentVa;
PVOID MappedSysAddress;
ULONG EntriesCount;
SG_ENTRY Entries[1]; // var size
};
struct SG_ENTRY
{
ULARGE_INTEGER PhysicalAddr;
PVOID VirtualAddr;
ULONG Length;
ULONG Offset;
ULONG field_14; // unknown
};
_USBPORT_MapTransfer(pFdo,pIrp,pMapRegisterBase,pTransfer)
{
pFdoExt->DoMapping = FALSE
// 1. build sgList
ULONG ulMappedLength = pUrb->TransferBufferLength
do
{
IoMapTransfer(...&ulMappedLength..);
add physical:virtual to sgList
ulMappedLength = pUrb->TransferBufferLength - ulMappedLength
} while(ulMappedLength != 0);
// 2. split transfer
if this is an iso transfer
init some field for iso transfer only
LIST_ENTRY listHeader;
call _USBPORT_SplitTransfer(..pTransfer,&listHeader);
while(listHeader is not empty)
{
get transfer from list header
insert to endpoint\'s ActiveTransfer
}
// 3. endpoint worker
call _USBPORT_CoreEndpointWorker
if return != 0
call _USBPORT_InvalidateEndpoint
}
看到这里我想你也能大致CoreEndpointWorker跟InvalidateEndpoint的大致作用,很明显是用来处理active transfer list的。先不管他,来看split:
USBPORT_SplitTransfer(pFdoExt,pEndpoint,pTransfer,pListHead)
{
init pTransfer->ChildTransferListHead
if(pTransfer->TransferBufferLength <= pEndpoint->MaxTransferLength)
insert pTransfer to pListHead
else
{
ASSERT(pTransfer->Type == interrupt || pTransfer->Type == bulk)
call _USBPORT_SplitBulkInterruptTransfer
}
}
很简单的逻辑:只有在transfer length 大于endpoint的最大传输大小,并且是interrupt或者bulk的时候,才split。最多传输长度并不是MaxPacketSize,这个长度要大得多。
_USBPORT_SplitBulkInterruptTransfer(pFdo,pEndpoint,pTransfer,pListHead)
{
ULONG ulChildrenCount = pEndpoint->MaxTransferLen / pTransfer->TransferBufferLength
ExAllocatePool(sizeof(TRANSFER) * ulChildrenCount)
copy from pTransfer to all the new allocated structs
reinit all the children transfer\'s list entry
ULONG ulLeftTransferLen = pTransfer->TransferBufferLength
pChildTransfer = FirstChildTransfer
pOrgSgList = &pTransfer->sgList
curSgEntryIndex = 0
curOffsetInEntry = 0
OffsetInTotalBuffer = 0
while(leftTransferLen != 0)
{
call _USBPORT_MakeSplitTransfer( pFdo, pOrgSgList, pChildTransfer, MaxTransferLength, MaxPacketLength, &curSgEntryIndex, &curOffsetInEntry, leftTransferLength, OffsetInTotalBuffer )
leftTransferLen = return value
link pChildTransfer to pListHead
OffsetInTotalBuffer += pChildTransfer->TransferBufferLength
pChildTransfer = NextChildTransfer
}
}
这个过程有些复杂。简单的讲,这个函数首先计算需要多少个children transfer,然后分配这么多个结构,然后把原来结构的内容copy到新的每个结构里面,但是那些list必须要重新初始化成正确的值,不能指向原来的transfer。然后调用一个辅助函数切割原来transfer的sgList,每个小的transfer的大小最多是 pEndpoint->MaxTransfer 这么大。
sgList是由一个一个的sgEntry组成的,这些sgEntry的长度却未必就是pEndpoint->MaxTransfer 这么大,所以必须由两变量来表示当前处理的是哪个entry,以及当前处理到了这个entry的哪个offset。也就是上面 curSgEntryIndex 和 curOffsetInEntry 的作用,USBPORT_MakeSplitTransfer辅助函数会修改这两个值。
最后注意,原来那个被切割的transfer并没有进入pListHeader,当然也就没有进入Endpoint的ActiveTransfer。
USBPORT_MakeSplitTransfer( pFdo, pOrgSgList, pChildTransfer, MaxTransferLen, MaxPacketLen, pCurEntryIndex, pCurOffsetInEntry, LeftTransferLen, OffsetInTotalBuffer)
{
ASSERT(MaxTransferLen % MaxPacketLen == 0)
length = MaxTransferLen
left = pOrgSgList->sgEntry[*pCurEntryIndex].Length - *pCurOffsetInEntry
pChildTransfer->sgList.Count = 0
i = 0
while(length > left)
{
pChildTransfer->sgList.Count ++
pChildTransfer->sgList.sgEntry[ i ]. Length = left
pChildTransfer->sgList.sgEntry[ i ].VirtualAddr = pOrgSgList->sgEntry[*pCurEntryIndex].VirtualAddr + *pCurOffsetInEntry
pChildTransfer->sgList.sgEntry[ i ].PhysicalAddr = pOrgSgList->sgEntry[*pCurEntryIndex].PhysicalAddr + *pCurOffsetInEntry
i ++
*pCurEntryIndex ++
*pCurOffsetInEntry = 0
LeftTransferLen -= left
length -= left
left = pOrgSgList->sgEntry[*pCurEntryIndex].Length
}
if(length)
{
pChildTransfer->sgList.Count ++
pChildTransfer->sgList.sgEntry[ i ].Length = length
pChildTransfer->sgList.sgEntry[ i ].VirtualAddr = pOrgSgList->sgEntry[*pCurEntryIndex].VirtualAddr + *pCurOffsetInEntry;
pChildTransfer->sgList.sgEntry[ i ].PhysicalAddr = pOrgSgList->sgEntry[*pCurEntryIndex].PhysicalAddr + *pCurOffsetInEntry;
*pCurOffsetInEntry += length
if(*pCurOffsetInEntry == pOrgSgList->sgEntry[*pCurEntryIndex].Length)
{
*pCurOffsetInEntry = 0
*pCurEntryIndex ++
}
}
return LeftTransferLen - length
}
逻辑很简单,按照MaxTransferLen划分切割就行了。
接下来就是要真正处理active transfer的部分了:
_USBPORT_CoreEndpointWorker(pEndpoint,bRecursion)
{
if(!bRecursion)
{
if(pEndpoint->Busy)
return 1;
else
pEndpoint->Busy = 1
}
call _USBPORT_GetEndpointState(pEndpoint)
state = return value
if(state == closed)
{
pEndpoint->Busy = 0
return 0;
}
call _USBPORT_PollEndpoint(pEndpoint)
call _USBPORT_GetEndpointState(pEndpoint)
state = return value
if(state == removed)
{
pEndpoint->CurrentState = close
pEndpoint->NextState = close
insert the endpoint to fdo\'s ClosedEndpointListHead
pEndpoint->Busy = 0
return 0
}
if(pEndpoint->ActiveTransfer is empty &&
pEndpoint->PendingTransfer is empty &&
pEndpoint->CancelTransfer is empty)
{
pEndpoint->Busy = 0
call _USBPORT_FlushAbortList(pEndpoint)
return 0
}
call _USBPORT_GetEndpointState(pEndpoint)
state = return value
if(state != pEndpoint->NextState)
{
pEndpoint->Busy = 0
return 1
}
call pEndpoint->WorkRoutine(pEndpoint)
call _USBPORT_FlushAbortList(pEndpoint)
pEndpoint->Busy = 0
return 0;
}
眼见函数就要分析完了,这个家伙马上又引出来若干个函数。。哈哈
其中_USBPORT_FlushAbortList函数在讲到cancel的时候再说,他其实是一个无关紧要的函数。
而_USBPORT_PollEndpoint函数则是调用miniport对应的Poll函数,在这个函数里面,miniport driver总是检查自己的transfer有没有完成了的,如果有就告诉usbport自己完成了某个transfer。
至于_USBPORT_GetEndpointState更是简单,如果pEndpoint->CurrentState == pEndpoint->NextState,则返回CurrentState,否则返回一个特殊的值 ENDPOINT_STATE_TRANSITION,这个值从来不会被设置到CurrentState 跟 NextState成员上面。
至于这个state的用法这里先大致的说说,state表征了endpoint的当前状态,能够设置的值有active, pause, remove, close,后两种是在特别的情况下才设置的。当你关闭一个endpoint的时候 (select configuration却填一个0的config desc),endpoint进入remove状态,上面已经看到他马上就转换成了close状态而进入了fdo的closed enpoint list,作lazy close。而active状态就是正常状态,在这个状态下,endpoint把自己的active
list里面的transfer一一提交给miniport driver。而pause的状态是为了处理cancel跟abort的情况的。如果active list里面的transfer有某个被标记成了canceled或者aborted.endpoint就从active状态进入pause状态。进入 pause状态以后一一完成那些标记成canceled和aborted的transfer,直到全部都处理完了,就转入active状态。
那上面这些操作都是由谁来完成的呢?当然不用说也是pEndpoint->WorkRoutine要作的事情了。
当然,状态的转换并不是这么简单的事情。一个最主要的原因是要同步endpoint的FrameNumber32Bit成员到当前usb host controller的Frame Number,这个主要是用于Iso传输的,所以状态转换的过程是个迂回的。
endpoint 首先进入fdo的state change list,然后fdo让miniport driver在下一个start of frame的时候引发一个中断,在isr的dpc里面同步endpoint的Frame Number,然后把endpoint从state change list里面取出来,这样endpoint才算是进入了新的状态,也就是让endpoint的CurrentState跟NextState相同。
然后要注意的是,这个函数如果返回值是1的话,紧接着调用的就是_USBPORT_InvalidateEndpoint函数。这个函数告诉一个辅助线程 (通过设置一个Event),调用一次_USBPORT_CoreEndpointWorker。miniport driver也会在比如某个传输完成的时候调用这个函数来排队CoreEndpointWorker的调用,使得usbport能继续向其提交新的传输,而不是停下来。
上面这段话是什么意思呢?看这个流程:进入pending -> flush pending -> 进入map ->flush map->MapTransfer进入 active->CoreWorker 提交一些但不是全部的transfer给硬件。
这里就层层返回了..并没有连续的提交。那要提交下一个怎么办呢?必须要再次进入CoreWorker才可以,这就是通过InvalidateEndpoint函数来完成的。
Invalidate 这个函数把你要Invalidate的Endpoint收集到一个list里面链接到fdo的AttendListHead上,然后唤醒work thread。work thread从fdo的AttendListHead上一一取出每个endpoint,然后调用CoreWorker,如果返回还是1,则保存到另外一个 list上面。当第一遍循环完了,再把那些还是返回1的endpoint重新链接到fdo的AttendListHead上面,再次排队下一个 CoreWorker调用,也就是再次设置Event,而不是直接循环再调用CoreWorker。
希望能明白这种排队poll调用的机制。记住一点:transfer的处理是要通过调用CoreWorker才会传递给miniport然后传递给硬件的,InvalidateEndpoint正是引发一次CoreWorker调用的关键。
相关推荐
Windows 2000专用USB驱动完整安装包(含INF配置与微端口驱动)
简介:本资源为面向Windows 2000操作系统的USB设备兼容性解决方案,包含经验证的主机控制器驱动、通用USB驱动模型及设备级微端口驱动。核心文件win2000_usb.inf提供标准化安装指引,支持硬件ID识别与自动驱动部署;压缩包集成可执行安装程序、PUDN来源说明文档及完整驱动签名/配置信息,适用于解决Win2000下USB设备无法识别、枚举失败或功能异常等典型问题,助力老旧系统延续USB外设支持能力。
usb杂谈之获取设备信息——举例鼠标urb
首先梳理一下usb设备的检测过程: 当usb设备接到port时,控制器会监测到该port电平状态发生改变,继而产生中断 port状态中断函数:handle_port_status 中断函数做了两件事: 第一件处理当前中断 第二件继续轮询该port状态第一件事:使用tasklet中断下半部处理,然后使用工作队列创建出设备 else if (high_prio_bh) // 同步传输或中断传输 tasklet_hi_schedule(
QF9700 USB网卡驱动程序完整安装与配置指南
QF9700 USB网卡驱动是专为基于QF9700芯片组设计的USB接口网络适配器提供的核心软件支持,旨在实现设备与操作系统的无缝通信。该驱动不仅承担硬件识别、资源分配和数据传输调度等基础功能,还通过标准化接口向上层网络协议栈提供稳定的数据链路服务。作为连接物理网络与操作系统之间的桥梁,QF9700驱动在即插即用(Plug and Play)、电源管理(如Suspend/Resume)以及热拔插响应机制中发挥关键作用。
nt!KiReadyThread函数分析和紧随其后的nt!KiProcessDeferredReadyList函数
nt!KiReadyThread函数分析和紧随其后的nt!KiProcessDeferredReadyList函数
HASP4加密锁Windows驱动包(含DOS下hdd32.exe调试工具与HTML版安装指南)
HASP4硬件加密锁配套驱动安装程序,支持主流Windows系统(XP至Win10/11 32位与64位),主安装程序HASP4_driver_setup自动完成驱动文件部署、注册表配置及服务注册,实现即插即用式硬件认证。包内附带hdd32.exe——专为纯DOS环境设计的32位HASP4设备识别与状态检测工具,适用于老旧工控系统、驱动兼容性验证或无GUI场景下的底层调试;readme.html提供完整操作指引,涵盖安装流程、系统要求(如需管理员权限、禁用驱动签名强制)、常见故障代码释义(如设备未识别、服务
Scratch-陶陶摘苹果
Scratch版-陶陶摘苹果([蓝桥杯][算法]) 题目要求: 陶陶家的院子里有一棵苹果树,每到秋天树上就会结出10个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个30厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。 现在已知10个苹果到地面的高度,以及陶陶把手伸直的时候能够达到的最大高度,请帮陶陶算一下她能够摘到的苹果的数目。假设她碰到苹果,苹果就会掉下来。 用scratch怎么完成呢? 首先加入角色题目介绍 接下来用询问框来录入苹果的高度并且记录到列表里 数据初始化 .
Windows XP下usbport.sys驱动内部实现解析(四)
http://hi.baidu.com/jlgwxq/blog/item/e54543100b5cb7c5c2fd7860.html Windows XP下usbport.sys驱动内部实现解析(四) 2010-01-22 18:16 (接上一篇) 然后来看看实际上的work routine,先看通用的这个。root hub的是一个特殊的函数。 USBPORT_DmaEndpoi
Aider的Repo Map功能
要把整个项目的所有文件添加到 Repo Map 中,需要调整 Aider 的行为,让它扫描整个代码库并将所有文件纳入 Repo Map。在使用 Aider 时,Repo Map 默认只包含当前会话中明确指定的文件(通过命令行传入的。通过命令行将整个项目的文件列表传递给 Aider,使其初始化时包含所有文件。,让 Aider 在初始化时自动加载整个项目的文件到。如果你的项目是 Git 仓库,Aider 可以通过。在 Aider 的交互模式中,手动添加整个项目文件。,从而生成包含所有文件的 Repo Map。
Windows操作系统的USB驱动架构
Winows操作系统下关于USB的驱动可分为: Windows2000 在Windows2000时代,USB设备基本是USB1.0.USB1.1和少量的USB2.0设备。 所以USB驱动架构构为: Windows2000 USB控制器 usbd.sys - Universal Serial Bus Driver 这是USB控制器的类驱动程序,配合2个端口驱动程序openhci.sys和uhcd.sys,这2个端口驱动分别对应于usb1.0和usb.1.1 Windows2000 USB集线器-HUB US
Jetson AGX Orin到手必看:DP线选错黑屏、SSD不识别?这篇避坑指南帮你一次搞定
本文详细介绍了Jetson AGX Orin开发套件的硬件避坑指南,涵盖开箱检查、DP线选择、SSD兼容性等关键问题。通过实测数据和实用技巧,帮助开发者避免黑屏、存储不识别等常见问题,提升开发效率。特别推荐符合VESA认证的DP 1.4a线缆和兼容性优秀的SSD型号,确保设备稳定运行。
DC-DC拓扑:从电感位置一眼识别Buck、Boost与Buck-Boost
本文详细解析了DC-DC拓扑中电感位置对识别Buck、Boost与Buck-Boost电路的关键作用。通过对比不同拓扑的电感连接特征,提供快速识别方法和设计要点,帮助工程师避免常见错误并优化电路性能。特别强调电感位置对输出质量和效率的影响,适合电源设计人员参考。
Windows XP下usbport.sys驱动内部实现解析(一)
http://hi.baidu.com/jlgwxq/blog/item/a3bcc30356a3cc057aec2c64.html Windows XP下usbport.sys驱动内部实现解析(一) 2010-01-22 18:10 上一篇讲了USB驱动栈整体结构,说明了usbport.sys的重要作用,现在就具体分析usbport.sys的内部实现细节。 首先再重复一下:
Matlab Copula函数实战:从金融风控到数据建模的5个应用场景
本文深入探讨了Matlab Copula函数在金融风控与数据建模中的五大实战应用场景,包括投资组合风险价值计算、信用违约相关性建模、跨市场套利策略优化等。通过具体代码示例和数据分析,展示了Copula模型在捕捉复杂依赖关系方面的优势,为金融风险管理和量化分析提供实用工具。
Windows XP下usbport.sys驱动内部实现解析(二)
http://hi.baidu.com/jlgwxq/blog/item/e0f223312c0aa548ac4b5f67.html Windows XP下usbport.sys驱动内部实现解析(二) 2010-01-22 18:15 (接上一篇) 4. 处理USB请求(URB) 在进入下一个主题之前我总结几个事实让大家注意: 1. 对于每个usb总线上的设备,usbpo
若依前后端分离版实战:MQTT集成保姆级教程(含MQTTX测试工具使用)
本文详细介绍了在若依前后端分离架构中集成MQTT协议的完整流程,包括环境搭建、核心配置、安全实践及性能优化。通过MQTTX测试工具的使用和实战代码示例,帮助开发者快速实现物联网场景下的实时通信功能,提升系统稳定性和效率。
eNSP常见故障处理
eNSP无法启动设备一直停在#井号的问题的解决方案?
USB 驱动程序堆栈体系结构
USB 驱动程序堆栈体系结构 本主题提供了通用串行总线 (USB) 驱动程序堆栈体系结构的概述。 下图显示了 Windows 8 USB 驱动程序堆栈体系结构框图。该图分别显示了 USB 2.0 和 USB 3.0 的 USB 驱动程序堆栈。当设备附加到 xHCI 控制器时,Windows 会加载 USB 3.0 驱动程序堆栈。USB 3.0 堆栈是 Windows 8 中的新功能。 当
海思3516DV300智能算法实现
海思3516DV300智能算法实现
windbg 常用命令详解
一、 1、 !address eax 查看对应内存页的属性 2、 vertarget 显示当前进程的大致信息 3 !peb 显示process Environment Block 4、 lmvm 可以查看任意一个dll的详细信息 例如:我们查看cyusb.sys的信息 5.reload /!sym 加载符号文件 6、 l
efm32 入门.pdf
本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。
怀旧版电脑GBC模拟器
再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。
工厂设备接MES为什么慢:协议识别与点表配置自动化的技术拆解
工业现场设备接 MES 的周期主要消耗在协议识别与点表配置。本文拆解这条链路的四层结构,给出点表映射的数据模型、两种部署架构的对比,以及可直接落地的三项验收口径与校验代码。适合工控集成、MES 实施、设备联网方向的工程师。
文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况
主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。
list
的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。迭代到该位置,在这段位置上迭代需要线性的时间开销;最大的缺陷是不支持任意位置的随机访问,比如:要访问。还需要一些额外的空间,以保存每个节点的相关联信息。个元素,必须从已知的位置。
C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片
基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正
向 excel、word、pdf 三种文档中占位符插入数据
【代码】向 excel、word、pdf 三种文档中占位符插入数据。
C++ STL 反向迭代器:从源码框架到适配器实现
在 C++ STL 中,反向迭代器(reverse_iterator)是一个很典型的设计案例:它本身并不直接持有容器数据,而是通过封装一个正向迭代器,把「前进」变成「后退」,从而让同一个容器既能正序遍历,也能逆序遍历。本文从 SGI-STL 3.0 的源码框架出发,梳理反向迭代器的设计思路,再手写一个通用的迭代器适配器,并在vector和list中完成适配与测试。在 SGI-STL 3.0 中,反向迭代器的核心源码位于。反向迭代器是一个适配器,各个容器再基于它适配出自己的反向迭代器类型。下面截取list。
[C++]:7.STL:list
好久不见,firdawn小课堂又和大家见面啦。今天我们要学习的内容是list容器,list也是一种常用的数据结构。下面是本章的思维导图,那么让我们开始学习吧!自定义的list,由三个结构构成。分别是链表的最小组成单位,也就是链表节点。以及迭代器类和list类其实要特别说明的是, List的迭代器,它不是单纯的指针,因为我们如果对指针直接进行解引用操作的话,我们得到的是这个节点,而不是节点里面存储的值并且我们对这个指针加加,得到的也不是下一个节点的地址,而是所在节点的下一个单位的地址。
RT-Thread 5.4.0 完整版架构剖析(下):IPC、时钟、内存、设备与组件
从 IPC 全量、时钟与定时器、内存管理,到设备框架、组件层、软件包机制、移植裁剪与速查表,补齐 RT-Thread 5.4.0 完整版的下半张地图。
CMake 多目录工程实战:静态库与动态库的完整构建示例
本文通过一个完整CMake项目示例,展示了如何构建静态库(math)与动态库(sort),并分别在test1、test2中链接使用。项目结构清晰,顶层CMakeLists.txt统一管理变量,各子目录独立配置编译规则。通过add_library生成库,link_directories和link_libraries实现链接,最终完成构建与验证。重点区分了静态库(打包进可执行文件)与动态库(运行时加载)的差异,适用于实际工程化开发与面试准备。
255



被折叠的 条评论
为什么被折叠?



