driverobject->driverextension->adddevice = usb2com_pnpadddevice;
(1) iocreatedevice系統api的原理為:
ntkernelapi
ntstatus
iocreatedevice(
in pdriver_object driverobject,
in ulong deviceextensionsize,
in punicode_string devicename optional,
in device_type devicetype,
in ulong devicecharacteristics,
in boolean reserved,
out pdevice_object *deviceobject
);
在之前真實的usb驅動中我們是這樣建立的:
ntstatus = iocreatedevice(
driverobject, // our driver object
sizeof(device_extension), // extension size for us
null, // name for this device
file_device_unknown,
file_autogenerated_device_name, // device characteristics
false, // not exclusive
&deviceobject); // our device object
就是第三個參數為null, 第四個參數為file_device_unknown,意味着我們驅動想附加的裝置是空的,且未知。
由于我們是建立虛拟序列槽驅動,是以調用iocreatedevice建立時在指定序列槽裝置的名字,且指定裝置類型。
ntstatus = iocreatedevice(driverobject, sizeof(device_extension),
&deviceobjname, file_device_serial_port,
file_device_secure_open, true, deviceobject);
(2)為自定義的擴充裝置中的裝置名字段指定裝置名
// deviceextension->devicename為unicode_string類型
rtlzeromemory(&deviceextension->devicename, sizeof(unicode_string));
deviceextension->devicename.maximumlength = deviceobjname.length + sizeof(wchar);
// buffer重新配置設定
deviceextension->devicename.buffer = usb2com_exallocatepool(nonpagedpool, deviceobjname.length + sizeof(wchar));
rtlzeromemory(deviceextension->devicename.buffer,
deviceobjname.length+sizeof(wchar));
rtlappendunicodestringtostring(&deviceextension->devicename, &deviceobjname);
(3)初始化事件、序列槽控件對象、關鍵代碼段、自旋鎖、讀寫隊列連結清單。
// this event is triggered when there is no pending io of any kind and device is removed
keinitializeevent(&deviceextension->removeevent, notificationevent, false);
// this event is triggered when self-requested power irps complete
keinitializeevent(&deviceextension->selfrequestedpowerirpevent, notificationevent, false);
// this event is triggered when there is no pending io (pending io count == 1 )
keinitializeevent(&deviceextension->nopendingioevent, notificationevent, false);
// spinlock used to protect inc/dec iocount logic
keinitializespinlock (&deviceextension->iocountspinlock);
deviceextension->baudrate = 19200;
/* set line control */
deviceextension->seriallinecontrol.stopbits = stop_bit_1;
deviceextension->seriallinecontrol.parity = no_parity;
deviceextension->seriallinecontrol.wordlength = 8;
deviceextension->specialchars.xonchar = serial_def_xon;
deviceextension->specialchars.xoffchar = serial_def_xoff;
deviceextension->handflow.controlhandshake = serial_dtr_control;
deviceextension->handflow.flowreplace = serial_rts_control;
deviceextension->handflow.xofflimit = 300;
deviceextension->handflow.xonlimit = 100;
initializecircularbuffer(&deviceextension->inputbuffer, 512);
initializecircularbuffer(&deviceextension->outputbuffer, 512);
keinitializespinlock(&deviceextension->inputbufferlock);
keinitializespinlock(&deviceextension->outputbufferlock);
initializelisthead(&deviceextension->readqueue);
keinitializespinlock(&deviceextension->readqueuespinlock);
initializelisthead(&deviceextension->writequeue);
keinitializespinlock(&deviceextension->writequeuespinlock);
initializelisthead(&deviceextension->purgequeue);
keinitializespinlock(&deviceextension->purgequeuespinlock);
設定do_power_pagable的目的是在suspend期間不接收一個irp_mn_stop_device,
在resume時不接收一個irp_mn_start_device消息。
// we support direct io for read/write
//
deviceobject->flags |= do_direct_io;
//set this flag causes the driver to not receive a irp_mn_stop_device
//during suspend and also not get an irp_mn_start_device during resume.
//this is neccesary because during the start device call,
// the getdescriptors() call will be failed by the usb stack.
deviceobject->flags |= do_power_pagable;
deviceextension->topofstackdeviceobject =
ioattachdevicetodevicestack(deviceobject, physicaldeviceobject);
status = ioregisterdeviceinterface(pdevext->physicaldeviceobject, (lpguid)&guid_class_comport,
null, &pdevext->deviceclasssymbolicname);
(1)建立irp來産生一個發往fdo的内部查詢請求;
irp = ioallocateirp(lowerdeviceobject->stacksize, false);
(2)設定irp要發往的裝置棧location(是更低層的裝置,在這裡就是它附加下的usb)的資訊,
eg: majorfunction、minorfunction、parameters.devicecapabilities.capabilities
nextstack = iogetnextirpstacklocation(irp);
nextstack->majorfunction= irp_mj_pnp;
nextstack->minorfunction= irp_mn_query_capabilities;
在以上代碼中的iogetnextirpstacklocation是一個宏,它的定義如下:
#define iogetnextirpstacklocation( irp ) (\
(irp)->tail.overlay.currentstacklocation - 1 )
從以上宏可以看出:irp結構體中存有目前的棧location currentstackloctation,而我們的irp要發往的棧location的獲得方法就是原有棧的位址 - 1。
(3)設定irp的完成例程;(在完成全程中就是把事件激活,這樣kewaitforsingleobject就能走下來)
(4)把irp發送下去,并等待完成;
ntstatus = iocalldriver(lowerdeviceobject,
irp);
usb2com_kdprint( dbglvl_medium,(" usb2com_querycapabilities() ntstatus from iocalldriver to pci = 0x%x\n", ntstatus));
if (ntstatus == status_pending) {
// wait for irp to complete
kewaitforsingleobject(
&event,
suspended,
kernelmode,
false,
null);
當完成全程傳回時, nextstack->parameters.devicecapabilities.capabilities = devicecapabilities;指針就存着我們的性能資訊。
直接調用系統api:
usbd_getusbdiversion(&versioninformation);
driverobject->majorfunction[irp_mj_pnp] = usb2com_processpnpirp;
在usb2com_processpnpirp裡case了以下幾個消息:
irp_mn_start_device 、irp_mn_query_stop_device、irp_mn_cancel_stop_device、irp_mn_stop_device、
irp_mn_query_remove_device、irp_mn_cancel_remove_device、irp_mn_surprise_removal、irp_mn_remove_device
irp_mn_start_device、
irp_mn_stop_device 、irp_mn_eject和irp_mn_surprise_removal
是以一比較,覺得usb2com考慮得更全面。
(1)irp_mn_start_device
(2)irp_mn_query_stop_device
(3)irp_mn_cancel_stop_device
(4)irp_mn_stop_device、
(5)irp_mn_query_remove_device、
(6)irp_mn_cancel_remove_device、
(7)irp_mn_surprise_removal、
(8)irp_mn_remove_device
driverobject->majorfunction[irp_mj_create] = usb2com_create;
如果不能接收一個新的io請求,那麼直接傳回。
if ( !usb2com_canacceptiorequests( deviceobject ) ) {
ntstatus = status_delete_pending;
usb2com_kdprint( dbglvl_default,("aborting usb2com_create\n"));
goto done;
}
在以下的條件不能接收一個新的io(判斷的标志是我們自己标記的):
1) 裝置已經被移除了,
2) 從來沒有被啟動過,,
3) 已經停止了,
4) 有一個移除的請求還沒處理,
5) 有一個停止的請求還沒處理。
//flag set when processing irp_mn_remove_device
if ( !deviceextension->deviceremoved &&
// device must be started( enabled )
deviceextension->devicestarted &&
// flag set when driver has answered success to irp_mn_query_remove_device
!deviceextension->removedevicerequested &&
// flag set when driver has answered success to irp_mn_query_stop_device
!deviceextension->stopdevicerequested ){
fcan = true;
}
startreadinturb(
deviceobject,
&interface->pipes[0]
);
(1)如果判斷現在不能接收io請求,那麼就記錄在extension中的irp置為完成狀态,然後傳回;
(2)配置設定irp、urb空間,并存到extension->readinturbs數組中,再把pipe句柄、buffer、傳送标志填入到urb結構體成員;
irp = ioallocateirp(stacksize, false);
if(irp == null)
{
return status_insufficient_resources;
}
urb = usb2com_exallocatepool(nonpagedpool,
sizeof(struct _urb_bulk_or_interrupt_transfer));
if(urb == null)
iofreeirp(irp);
return status_insufficient_resources;
deviceextension->readinturbs[i].irp = irp;
deviceextension->readinturbs[i].urb = urb;
deviceextension->readinturbs[i].deviceobject = deviceobject;
deviceextension->readinturbs[i].pipeinfo = pipeinfo;
initinturb(urb,
pipeinfo->pipehandle,
deviceextension->readinturbs[i].transferbuffer,
sizeof(deviceextension->readinturbs[i].transferbuffer),
true);
initinturb為自己封裝的函數,很簡單,就是把資料填入到urb結構體中:
void
initinturb(
in purb urb,
in usbd_pipe_handle pipehandle,
in puchar transferbuffer,
in ulong length,
in boolean read
)
{
ushort siz = sizeof(struct _urb_bulk_or_interrupt_transfer);
if (urb) {
rtlzeromemory(urb, siz);
urb->urbbulkorinterrupttransfer.hdr.length = (ushort) siz;
urb->urbbulkorinterrupttransfer.hdr.function =
urb_function_bulk_or_interrupt_transfer;
urb->urbbulkorinterrupttransfer.pipehandle = pipehandle;
urb->urbbulkorinterrupttransfer.transferflags =
read ? usbd_transfer_direction_in : 0;
// short packet is not treated as an error.
urb->urbbulkorinterrupttransfer.transferflags |=
usbd_short_transfer_ok;
//
// not using linked urb's
urb->urbbulkorinterrupttransfer.urblink = null;
urb->urbbulkorinterrupttransfer.transferbuffermdl = null;
urb->urbbulkorinterrupttransfer.transferbuffer = transferbuffer;
urb->urbbulkorinterrupttransfer.transferbufferlength = length;
}
(3)初始化irp的棧location及它的完成例程readinturbcomplete,并把目前readinturbs的記憶體作為完成例程的context;
(4)完成例程readinturbcomplete的處理;
a、判斷傳回執行後傳回的irp、urb的狀态,如果為未連接配接或取消那麼就釋放irp及urb記憶體,并完成把extension中的irp置為完成狀态,然後直接傳回;否則往下執行;
b、把irp之前綁定的buffer資料拷到inputbuffer中(現在的buffer就是我們的結果資料);
keacquirespinlock(&deviceextension->inputbufferlock, &oldirql);
pushcircularbufferentry(
&deviceextension->inputbuffer,
&pinturbs->transferbuffer[1],
pinturbs->transferbuffer[0]);
kereleasespinlock(&deviceextension->inputbufferlock, oldirql);
pushcircularbufferentry為自己封裝的函數:
把data記憶體中的len個資料拷到pbuffer中
pushcircularbufferentry(
in pcircular_buffer pbuffer,
in puchar data,
in ulong len)
ulong nextposition;
dbgprint("serial: pushcircularbufferentry(data %p, len %d)\n", data, len);
assert(pbuffer);
assert(pbuffer->length);
if ((data == null) || (len == 0))
return status_invalid_parameter;
do{
nextposition = (pbuffer->writeposition + 1) % pbuffer->length;
if (nextposition == pbuffer->readposition)
return status_buffer_too_small;
pbuffer->buffer[pbuffer->writeposition] = *data++;
pbuffer->datalen++;
pbuffer->writeposition = nextposition;
}while(--len);
return status_success;
c、如果extension中有等待的irp,且等待的事件中有serial_ev_rxchar,那麼通過serialcompletecurrentwait完成等待序列槽的等待irp。
c、1 把目前irp的取消完成例程置為null,根據包是否已經被取消标志pirp->cancel 及之前的irp取消例程是否被執行過了,那麼調用serialcancelcurrentwait來取消。
注意serialcancelcurrentwait中很重要的是要通過調用ioreleasecancelspinlock來釋放系統的删除自旋鎖。
serialcancelcurrentwait( pdevice_object deviceobject, pirp pirp )
pio_stack_location irpstack;
pdevice_extension deviceextension;
dbgprint("serialcancelcurrentwait enter irp = %p\n",pirp);
assert(pirp);
irpstack = iogetcurrentirpstacklocation(pirp);
deviceextension = irpstack->deviceobject->deviceextension;
deviceextension->currentwaitirp = null;
/*
*all cancel routines must follow these guidelines:
* 1. call ioreleasecancelspinlock to release the system's cancel spin lock
* 2. ...
*/
ioreleasecancelspinlock(pirp->cancelirql);
pirp->iostatus.status = status_cancelled;
pirp->iostatus.information = 0;
iocompleterequest(pirp, io_no_increment);
dbgprint("serialcancelcurrentwait exit\n");
c、2 如果沒有被取消,那麼把目前我們的事件傳回給應用層,并完成irp。
deviceextension->currentwaitirp = null;
deviceextension->historymask &= ~events;
ioreleasecancelspinlock(oldirql);
pirp->iostatus.information = sizeof(ulong);
pirp->iostatus.status = ntstatus;
*((ulong *)pirp->associatedirp.systembuffer) = events;
iocompleterequest (pirp,io_no_increment);
d、完成目前的讀irp;
d、1 如果有目前讀的irp,那麼把第(4)b、裡得到的結果資料彈出到目前的讀irp中(通過目前讀的irp中的mdladdress位址通路到記憶體)。
if(deviceextension->currentreadirp)
ulong havelen;
boolean returnwhatspresent = false;
if(deviceextension->serialtimeouts.readintervaltimeout &&
( deviceextension->serialtimeouts.readtotaltimeoutmultiplier == 0) &&
( deviceextension->serialtimeouts.readtotaltimeoutconstant == 0)
{
returnwhatspresent = true;
iobuffer = mmgetsystemaddressformdlsafe(deviceextension->currentreadirp->mdladdress,normalpagepriority );
iolength = mmgetmdlbytecount(deviceextension->currentreadirp->mdladdress);
keacquirespinlock(&deviceextension->inputbufferlock, &oldirql);
havelen = circularbufferdatalen(&deviceextension->inputbuffer);
if( (iolength <= havelen) || (returnwhatspresent && (havelen > 0)))
iolength = (iolength < havelen) ? iolength : havelen;
dbgprint("complete currentreadirp iolength = %d\n",iolength);
ntstatus = popcircularbufferentry(&deviceextension->inputbuffer,iobuffer,iolength);
kereleasespinlock(&deviceextension->inputbufferlock, oldirql);
deviceextension->currentreadirp->iostatus.information = iolength;
deviceextension->currentreadirp->iostatus.status = ntstatus;
iocompleterequest(deviceextension->currentreadirp,io_no_increment);
deviceextension->currentreadirp = dequeuereadirp(deviceextension);
else
以上代碼中mmgetsystemaddressformdlsafe
// 函數說明:
// 此函數傳回mdl映射的位址,如果此mdl還沒有被映射,那麼它将會被映射
// 參數:
// memorydescriptorlist - 指向mdl的指針
// priority - 指向一個标志,該标記表明它是如何的重要,以至于這個請求在低的可用pte條件下也成功了
// 傳回值:
// 傳回映射頁的基位址,這個基位址和mdl的虛拟位址有同樣的偏移位址.
// 與mmgetsystemaddressformdl不同,在失敗時它會傳回null,而不是bugchecking the system.
// 版本說明:
// 此宏在wdm 1.0中不同用,在wdm1.0的驅動中實作此功能是通過提供同步并set/reset mdl_mapping_can_fail bit.
#define mmgetsystemaddressformdlsafe(mdl, priority) \
(((mdl)->mdlflags & (mdl_mapped_to_system_va | \
mdl_source_is_nonpaged_pool)) ? \
((mdl)->mappedsystemva) : \
(mmmaplockedpagesspecifycache((mdl), \
kernelmode, \
mmcached, \
null, \
false, \
(priority))))
第一個參數是deviceextension->currentreadirp->mdladdress,我們從wdm.h可以看到它的定義:
// 定義一個指向這個i/o請求的記憶體描述符(mdl)的指針,
// 此域僅在i/o是“direct i/o"時被用
pmdl mdladdress;
d、2 判斷是否能接收一個新的io請求
如果不能接收一個新的io請求或者pipe[0]關閉了,那麼直接傳回。
d、3 循環發送irp,并置完成例程為readinturbcomplete。(這樣,就又回到了(4)),何時結束呢?
結束條件:直至裝置不能接收一個新的io請求或未連接配接、取消。
是以處理目前讀irp是一直進行的。
preparewriteinturb(
in pdevice_object deviceobject,
in pusbd_pipe_information pipeinfo
a、配置設定寫的irb 與 urb空間。
b、儲存pipe[1]位址為deviceextension->writeinturb.pipeinfo存在extension中。
應用程式的名字從目前irp中的fileobject得到。
ourpipeinfo = usb2com_pipewithname( deviceobject, &fileobject->filename );
for (i=0; i<interface->numberofpipes; i++) {
pipeinfo = &interface->pipes[i]; // pusbd_pipe_information pipeinfo;
if ( ourpipeinfo == &deviceextension->pipeinfo[i] ) {
//
// found a match
usb2com_kdprint( dbglvl_default,("open pipe %d\n", i));
fileobject->fscontext = pipeinfo;
ourpipeinfo->fpipeopened = true; // set flag for opened
ntstatus = status_success;
deviceextension->openpipecount++;
// try to power up device if its not in d0
actstat = usb2com_selfsuspendoractivate( deviceobject, false );
break;