Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6586713
  • 博文数量: 227
  • 博客积分: 10047
  • 博客等级: 上将
  • 技术积分: 6678
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-11 10:33
个人简介

网上的蜘蛛

文章分类

全部博文(227)

文章存档

2010年(19)

2009年(29)

2008年(179)

分类: 系统运维

2008-05-12 23:52:55

VirtualWiFi的驱动实现在物理层(Mac)以及协议层(Ip protocol)之间。如图:
图中就是VirtualWiFi的层次结构图了。VirtualWiFi加载了或者说虚拟了两种可用的无线网卡。而且这两张网卡在IP层看来,都是激活可用的。虽然在任意的某一个时刻只有一张是可用的,因为我们的实现方法是靠高速的切换。所以图中显示的此刻network2才是激活的。而VirtualWiFi driver又可以分为两层,Miniport driver(MD)绑定了上一层的网络协议 和protocol driver(PD)则绑定了下层的网卡的为端口驱动(miniport driever).这样做的好处就是每张网卡都有不同的IP地址,但是他们分享一张网卡的MAC地址。
现在简单介绍这两个层次的作用。MD负责监控所有虚拟网卡的状态,包括SSID,网络模式,同时负责对底层驱动的信息查询和设置。PD则管理这些网卡的状态,比如交换时间,停留时间,什么时候换成数据,什么时候发送缓冲区数据等等。VirtualWiFi Driver实现在kernel层,所以NDIS提供了主要的软件组件接口。而VWService实现在user层。主要考虑便于程序的开发。但是,如何在user层以及kernel层进行通讯呢?即VWService如何控制driver进行状态的改变呢?这个任务主要有DeviceIoControl()这个系统函数来完成,它传递了一种I/O控制码(IOCTLCODE)用来帮助鞋套VWService和底层驱动的通讯问题。关于DeviceIoContrl()可以参考
这里只做简单介绍哦。

DeviceIoControl函数向指定的设备驱动发送一个控制码,驱动程序通过这个控制码来完成特定的工作。该函数原型如下:

BOOL DeviceIoControl(
    HANDLE hDevice,
    DWORD dwIoControlCode,
    LPVOID lpInBuffer,
    DWORD nInBufferSize,
    LPVOID lpOutBuffer,
    DWORD nOutBufferSize,
    LPDWORD lpBytesReturned,
    LPOVERLAPPED lpOverlapped
);

参数说明

hDevice

要进行操作的设备句柄。

dwIoControlCode

要进行操作的控制码。驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层,irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。

lpInBuffer

由用户层发送的缓冲区数据。在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。

传输类型 位置
METHOD_IN_DIRECT irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER irpStack->Parameters.DeviceIoControl.Type3InputBuffer

nInBufferSize

由用户层发送的缓冲区大小。在驱动层,这个值是irpStack->Parameters.DeviceIoControl.InputBufferLength

lpOutBuffer

由用户层指定,用于接收驱动层返回数据的缓冲区。在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。

传输类型 位置
METHOD_IN_DIRECT irp->MdlAddress
METHOD_OUT_DIRECT irp->MdlAddress
METHOD_BUFFERED irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER irp->UserBuffer

nOutBufferSize

由用户层指定,用于接收驱动层返回数据的缓冲区大小。在驱动层,这个值是irpStack->Parameters.DeviceIoControl.OutputBufferLength

lpBytesReturned

由用户层指定,用于接收驱动层实际返回数据大小。在驱动层,这个值是irp->IoStatus->Information

lpOverlapped

用于异步操作。

举个例子:VWService想通知driver发送缓冲区的数据。

一个简单的流程图:

首先,创建一个用户自定义的IOCTL:

// Send the buffered packets - used by both the Server and Service
#define IOCTL_SEND_BUFFERED_PACKETS CTRL_CODE( 0x839, METHOD_BUFFERED, FILE_READ_ACCESS)

第二:由VWService调度执行IOCTL.

// Send the packets buffered on the current SSID

retVal = ioctlSet(IOCTL_SEND_BUFFERED_PACKETS, adapterNum, sizeof(adapterNum));

这里调用了自定义函数ioctlSet()传递了之前定义的IOCTL.

第三:由ioctlSet这个函数传递信息到底层驱动,当然主要还要靠DeviceIoCtrol()来完成。

VOID ioctlSet(DWORD dwIOControlCode,
LPVOID lpInBuffer,
DWORD lpInBufferSize)
{
(Omitted)
b = DeviceIoControl(
hDriver, // handle to a device, file, or directory

dwIOControlCode, // control code of operation to perform

lpInBuffer, //lpInBuffer, pointer to buffer to supply input data

lpInBufferSize, //nInBufferSize, in bytes, of input buffer

NULL, //lpOutBuffer, pointer to buffer to receive output data

0, //nOutBufferSize, in bytes, of output buffer

&bytesreturned, // pointer to variable to receive byte count

NULL // pointer to structure for asynchronous

);
(Omitted)
CloseHandle(hDriver); // Close the driver

}
}

第四,底层驱动调度执行IOCTL对应的功能。I/O manager管理所有的IRP储存在IO_STACK_LOCATION堆栈里面。通过IoGetCurrentIrpStackLocation可以拿到需要的device control code.

1 NTSTATUS
2 PtDispatch()
3 {
4 (Omitted)
5 irpStack = IoGetCurrentIrpStackLocation(Irp);
6 DBGPRINT(MUX_LOUD, ("==>PtDispatch %d\n", irpStack->MajorFunction));
7
8 switch (irpStack->MajorFunction)
9 {
10 case IRP_MJ_DEVICE_CONTROL: {
11
12 buffer = Irp->AssociatedIrp.SystemBuffer;
13 inlen = irpStack->Parameters.DeviceIoControl.InputBufferLength;
14. outlen = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
15. saveControlFlags = irpStack->Control;
16
17 switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
18 case IOCTL_SEND_BUFFERED_PACKETS:
19 memcpy(tempBuffer, buffer, inlen);
20 // get adapter from ioctl buffer

21 adapterNum = atol(tempBuffer);
22
23 // get the corresponding pAdapt data structure

24 for(p = AdapterList.Flink;
p != &AdapterList;
p = p->Flink)
25 {
26 i++;
27 if(i<adapterNum) {
38. continue;
29 }
30 pAdapt = CONTAINING_RECORD(p, ADAPT, Link);
31 FoundAdapter = TRUE;
32 break;
33 }
34 // Send the packets for the corresponding adapter

35 if(FoundAdapter)
36 PtIoctlSendBufferedPackets(pAdapt);
37 FoundAdapter = FALSE;
38 i = 0;
39 DBGPRINT(MUX_LOUD, ("The card sent the buffered packets\n"));
40 Irp->IoStatus.Information = 0;
41 break;
}

第五:调用PtIoctlSendBufferedPackets(pAdapt)执行具体的数据发送。

1 // Called by an Ioctl to Send the Buffered Packets

2 VOID PtIoctlSendBufferedPackets(
IN PADAPT pAdapt
)
3{
4 pAdapt->isSSIDActive[pAdapt->CurrentActiveConnection] = TRUE;
5
6 // Send all the buffered packets

7 while(!IsQueueEmpty(&pAdapt->SendWaitQueue[pAdapt->CurrentActiveConnection]))
8 {
9 PQUEUE_ENTRY pEntry = RemoveHeadQueue(&pAdapt->SendWaitQueue[pAdapt->CurrentActiveConnection]);
10 ASSERT(pEntry);
11 DBGPRINT(MUX_LOUD, ("Dequeueing packet for SSID %d\n", pAdapt->CurrentActiveConnection));
12
13 // decrement the number of buffered packets

14 pAdapt->nWaitSend[pAdapt->CurrentActiveConnection]--;
15 MyPacket = CONTAINING_RECORD(pEntry, NDIS_PACKET, MiniportReserved);
16
17 pSendReserved = MUX_RSVD_FROM_SEND_PACKET(MyPacket);
18 pVElan = pSendReserved->pVElan;
19
20 MUX_INCR_PENDING_SENDS(pVElan);
21
22 NdisSend(&Status,
pAdapt->BindingHandle,
MyPacket);
23 if (Status != NDIS_STATUS_PENDING)
24 {

25 PtSendComplete((NDIS_HANDLE)pAdapt,
MyPacket,
Status);
26

这样就完成了发送缓冲区数据的全部过程。其他的I/O都类似这样的。不多讲了。
-----------------------------------
阅读(3055) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~