wince下DM9000A网卡驱动移植及学习总结---4
来源:互联网 发布:linux查看某个进程 编辑:程序博客网 时间:2024/04/29 15:50
6. CQueue(队列)类分析
与CQueue类紧密相关的是队列header结构体:PCQUEUE_GEN_HEADER,原型如下:
typedefstruct _CQUEUE_GEN_HEADER
{
struct _CQUEUE_GEN_HEADER *pNext;
U32 uFlags;
PVOID pPacket;
U16 nReserved;
U16 nLength;
} CQUEUE_GEN_HEADER, *PCQUEUE_GEN_HEADER;
这个类有两个私有的成员变量,定义如下:
CQUEUE_GEN_HEADER *m_pHead,*m_pTail;
这个类完成的是将上面的结构体变量XXX虚拟的放入一个FIFO,先入先出的一个队列中,m_pTail是FIFO的尾指针,m_pHead是FIFO的头指针。
入队函数Enqueue完成的功能是入队,每有一个XXX进入队列,让上一个入队的XXX的pNext指向当前入队的这个XXX,表示它的下一个XXX是新进来的这个XXX,最后将FIFO的尾指针修改,这样建立这个虚拟FIFO的链接关系。出队函数Dequeue相反,将FIFO的头指针输出,然后修改头指针。(XXX意为FIFO中存放的一个结构体变量)
GetHead函数是获得FIFO的头指针,IsQueueEmpty函数是判断FIFO是否为空,Size函数返回FIFO中XXX的个数。
7. CSpinlock类分析:
CSpinlock类构造函数调用NDIS库函数NdisAllocateSpinLock,初始化一个NDIS_SPIN_LOCK类型的变量,目的:为了同步访问驱动中的共享资源而不涉及中断。
类成员函数Lock调用NDIS库函数NdisAcquireSpinLock,完成的功能是spin lock,这样调用者可以访问共享资源。
类成员函数Release调NDIS库函数NdisReleaseSpinLock,完成的功能与上面相反,释放lock。
8. CMutex类分析:
构造函数调用内核函数InitializeCriticalSection,初始化一个临界对象,Lock函数调内核函数EnterCriticalSection进入临界状态,TryLock函数调用内核TryEnterCriticalSection也是进入临界状态但是会很块返回而不管是否进入时,Release函数调内核函数LeaveCriticalSection离开临界状态。
9. C_Exception类
这个类是调NDIS库打印一些调制信息。
10. 网卡send分析
NDIS驱动会调用MiniportSend函数进行发数操作,而MiniportSend函数将调用函数DriverSend函数。MiniportSend函数原型如下:
NDIS_STATUS MiniportSend(NDIS_HANDLE MiniportAdapterContext, PNDIS_PACKET Packet, UINT Flags)
参数说明:
u MiniportAdapterContext:一个指向网卡结构的句柄,该网卡结构在MiniportInitialize函数中被创建。
u Packet:指向一个包描述符,该包描述符用于描述被发送的数据包
u Flags:发送标记
注:后面两个参数通过DriverSend函数继续传递。
下面我们详细看看发送。
首先我们需要注意在几个hpp文件中定义的几个CQueue类成员变量:
l Driver类中定义的CQueue类成员变量:m_TQueue
l deivce类中定义的CQueue类成员变量:m_TQStandby和m_TQWaiting
l Dm9isa类中定义的CQueue类成员变量:m_RQueue和m_RQStandby
Dm9isa类中的m_RQueue和m_RQStandby是在接收时使用,后面再讲,我们先看与send相关的前3个。
先来回顾以下EDriverInitialize中的如下语句:
/* init tx buffers */
U32 m,uaddr;
if(!(uaddr = (U32)malloc(sizeof(DATA_BLOCK)* (m=m_pLower->m_szConfigures[CID_TXBUFFER_NUMBER]*2)))) //m=32(0x20)*2=64
THROW((ERR_STRING("Insufficient memory")));
for(;m--;uaddr+=sizeof(DATA_BLOCK))
m_TQueue.Enqueue((PCQUEUE_GEN_HEADER)uaddr);
这是初始化tx buffers,读注册表认为上述m = 64,每一个DATA_BLOCK是由header+buffer[0x5f0]组成,共malloc了64个这样的DATA_BLOCK,之后的for是所有的DATA_BLOCK的header做m_TQueue类实例的入队处理。
注意:入队出队的FIFO中存的都是header而不是整个DATA_BLOCK。
DATA_BLOCK的结构如下图:
接下来我们看函数DriverSend。
这个函数首先执行的代码如下:
if(!(pobj = m_TQueue.Dequeue())){….}
即m_TQueue类实例的出队操作,返回值是FIFO中的一个XXX,由于m_TQueue类实例在EDriverInitialize时已经初始化过了,所以这里肯定存在,if判断语句条件不成立,所以不进入。继续DriverSend函数:
PU8 pcurr = (PU8)CQueueGetUserPointer(pobj);
CQueueGetUserPointer函数原型如下:
#define CQueueGetUserPointer(ptr) \
((PVOID)((U32)(ptr) + sizeof(CQUEUE_GEN_HEADER)))
所以其实CQueueGetUserPointer函数完成的功能就是将指针指向DATA_BLOCK的buffer[]。
继续看DriverSend函数:
NdisQueryPacket(pPacket, &uPhysicalBufferCount, &uBufferCount,
&pndisFirstBuffer, &uTotalPacketLength);
NdisQueryPacket是NDIS库函数,这里用于获得MiniportSend传入的参数Packet的信息,接下来的一个
for()
{…}
作用是将Packet的内容拷贝到DATA_BLOCK的buffer中。最后几行代码如下:
pobj->pPacket = (PVOID)pPacket;
pobj->uFlags = uFlags;
pobj->nLength = uTotalPacketLength;
m_pLower->DeviceSend(pobj);
是将Packet的信息存到DATA_BLOCK的header里面,然后将这个header做为参数调用函数DeviceSend。
总的来说,DriverSend就是对MiniportSend传入的参数Packet的信息进行分析,然后重新拷贝到DATA_BLOCK中,最后调用DeviceSend函数完成真正的功能。
下面我们看函数DeviceSend。这个函数在子类中实现,首先执行代码如下:
if(pObject) m_TQStandby.Enqueue(pObject);
即将传入的header入m_TQStandby类实例的FIFO。m_TQStandby没有初始化,所以FIFO中只有这一个header,接下来执行:
m_nTxPendings++;
这个是等待发送的包的计数,发送完成后执行m_nTxPendings--操作。接下来执行:
m_TQWaiting.Enqueue(pcurr=m_TQStandby.Dequeue());
即m_TQStandby出队,而m_TQWaiting入队,所以这里m_TQStandby类实例其实是个缓冲的作用,可以认为是不存在的,header直接传到m_TQWaiting类实例。接下来执行如下:
/* fill data */
DeviceWriteString((PU8)CQueueGetUserPointer(pcurr),pcurr->nLength);
DeviceWritePort(DM9_TXLENH,HIGH_BYTE(pcurr->nLength));
DeviceWritePort(DM9_TXLENL,LOW_BYTE(pcurr->nLength));
// TXCR<0>, issue TX request
DeviceWritePort(DM9_TXCR, MAKE_MASK(0));
这几句是往DM9000A的发数缓冲写入要发数的数据,往发数的长度寄存器写入这些数据的长度,最后往DM9000A的发送控制寄存器(TCR)写发送请求。
发送完成后,会产生一个中断,执行前面说讲过的中断操作,判断是包1还是包2发完成,最后执行:
DeviceSendCompleted(m_TQWaiting.Dequeue());
即m_TQWaiting的出队操作(完成后m_TQWaiting将继续保持为空,等待下一个send,然后从m_TQStandby出队时获得一个header将其入队,如此反复…),和m_TQueue的入队操作(send最开始时执行出队,其实这个FIFO中保存的header只是保证在内存中开辟了64*sizeof(DATA_BLOCK)大小的区域,出队后对出队的header填入相关信息,进行发送,结束后重新进行入队操作,保证了FIFO的size,而内存中存的数据已经没有意义,下一次使用这个header时(即FIFO转一圈后这个header再次出队时)会覆盖现在的内容,所以这里只是一个简单的入队操作。
上述就是整个网卡发数的操作,总的来说,就是:上层会将发数的数据放在packed参数里面,然后驱动将分析这个Packet,最后将其送到DM9000A的TX SRAM,将长度也放入相应寄存器,最后执行发送请求。不过复杂的是里面有几级缓冲,将上层给的数据一级一级的放到缓冲里,最后发数。上面“6”中所讲的CQueue类其实目的就是将这些缓冲做成一个虚拟的FIFO,先入先出,保持顺序。
11. 结束。
这段时间一直学习网卡,所以做了详细的总结,以供自己日后回顾复习和他人学习。
- wince下DM9000A网卡驱动移植及学习总结---4
- wince下DM9000A网卡驱动移植及学习总结---1
- wince下DM9000A网卡驱动移植及学习总结---2
- wince下DM9000A网卡驱动移植及学习总结---3
- 2440下vxWorks DM9000A网卡驱动移植
- wince下Dm9000A驱动调试总结
- wince下Dm9000A驱动调试总结----转自巴乔
- wince下Dm9000A驱动调试总结----转自巴乔 .
- 友坚U-boot-1.1.6学习及移植dm9000a驱动
- dm9000a网卡驱动
- DM9000A网卡模块调试总结
- dm9000网卡驱动移植总结
- 2410平台上dm9000a网卡驱动分析
- DM9000A网卡驱动框架源码分析
- 网络驱动程序 各个函数详解及图解 DM9000A网卡驱动框架源码分析
- S3C2416 +linux驱动移植之DM9000A
- S3C2416 +linux驱动移植之DM9000A
- WINCE下RTL8201CL网卡驱动探索
- C++单线程TCP扫描器
- 文件权限
- Android7种Dialog对话框
- 一次插入多条数据
- JFreeChart生成2D折线图
- wince下DM9000A网卡驱动移植及学习总结---4
- 【Java】SequenceInputStream类合并文件的综合举例分析
- 一维数组及子数组最大和问题Java实现
- 一个很好的调试函数
- MyEclipse10 中的两种FreeMarker插件的安装与配置
- go property snippet
- Linux iptables lay7配置(下)
- ASP.NET PAGE 事件执行顺序
- 画板社交工具开发分享——HTML5 canvas控件、PHP、社交分享学习(三)