[aoe] Rename AoeDriverIrpSysCtl_ to AoeIrpSysCtl
[people/sha0/winvblock.git] / src / aoe / driver.c
1 /**
2  * Copyright (C) 2009-2011, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  * Copyright 2006-2008, V.
4  * For WinAoE contact information, see http://winaoe.org/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
8  * WinVBlock is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * WinVBlock is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 /**
23  * @file
24  *
25  * AoE specifics.
26  */
27
28 #include <stdio.h>
29 #include <ntddk.h>
30 #include <scsi.h>
31
32 #include "portable.h"
33 #include "winvblock.h"
34 #include "wv_stdlib.h"
35 #include "wv_string.h"
36 #include "irp.h"
37 #include "driver.h"
38 #include "bus.h"
39 #include "device.h"
40 #include "disk.h"
41 #include "mount.h"
42 #include "aoe.h"
43 #include "registry.h"
44 #include "protocol.h"
45 #include "debug.h"
46
47 #define AOEPROTOCOLVER 1
48
49 extern NTSTATUS STDCALL ZwWaitForSingleObject(
50     IN HANDLE Handle,
51     IN BOOLEAN Alertable,
52     IN PLARGE_INTEGER Timeout OPTIONAL
53   );
54
55 /* From aoe/bus.c */
56 extern WVL_S_BUS_T AoeBusMain;
57 extern BOOLEAN AoeBusCreate(IN PDRIVER_OBJECT);
58 extern VOID AoeBusFree(void);
59 extern NTSTATUS STDCALL AoeBusDevCtl(IN PIRP, IN ULONG POINTER_ALIGNMENT);
60 extern NTSTATUS STDCALL AoeBusAttachFdo(
61     IN PDRIVER_OBJECT,
62     IN PDEVICE_OBJECT
63   );
64 extern const WV_S_DRIVER_DUMMY_IDS * AoeBusDummyIds;
65 extern BOOLEAN STDCALL AoeBusAddDev(IN OUT WV_SP_DEV_T);
66 /* From aoe/registry.c */
67 extern BOOLEAN STDCALL AoeRegSetup(OUT PNTSTATUS);
68
69 /* Forward declarations. */
70 struct AOE_DISK_;
71 static VOID STDCALL AoeThread_(IN PVOID);
72 static VOID AoeProcessAbft_(void);
73 static VOID STDCALL AoeUnload_(IN PDRIVER_OBJECT);
74 static struct AOE_DISK_ * AoeDiskCreate_(void);
75 static WV_F_DEV_FREE AoeDiskFree_;
76 static WV_F_DISK_IO AoeDiskIo_;
77 static WV_F_DISK_MAX_XFER_LEN AoeDiskMaxXferLen_;
78 static WV_F_DISK_INIT AoeDiskInit_;
79 static WV_F_DISK_CLOSE AoeDiskClose_;
80 static driver__dispatch_func AoeDriverIrpNotSupported_;
81 static __drv_dispatchType(IRP_MJ_POWER) DRIVER_DISPATCH AoeIrpPower;
82 static
83   __drv_dispatchType(IRP_MJ_CREATE)
84   __drv_dispatchType(IRP_MJ_CLOSE)
85   DRIVER_DISPATCH AoeIrpCreateClose;
86 static __drv_dispatchType(IRP_MJ_SYSTEM_CONTROL)
87   DRIVER_DISPATCH AoeIrpSysCtl;
88 static driver__dispatch_func AoeDriverIrpDevCtl_;
89 static driver__dispatch_func AoeDriverIrpScsi_;
90 static driver__dispatch_func AoeDriverIrpPnp_;
91 static VOID STDCALL AoeDriverUnload_(IN PDRIVER_OBJECT);
92
93 /** Tag types. */
94 typedef enum AOE_TAG_TYPE_ {
95     AoeTagTypeIo_,
96     AoeTagTypeSearchDrive_,
97     AoeTagTypes_
98   } AOE_E_TAG_TYPE_, * AOE_EP_TAG_TYPE_;
99
100 #ifdef _MSC_VER
101 #  pragma pack(1)
102 #endif
103 /** AoE packet. */
104 struct AOE_PACKET_ {
105     UCHAR ReservedFlag:2;
106     UCHAR ErrorFlag:1;
107     UCHAR ResponseFlag:1;
108     UCHAR Ver:4;
109     UCHAR Error;
110     UINT16 Major;
111     UCHAR Minor;
112     UCHAR Command;
113     UINT32 Tag;
114
115     UCHAR WriteAFlag:1;
116     UCHAR AsyncAFlag:1;
117     UCHAR Reserved1AFlag:2;
118     UCHAR DeviceHeadAFlag:1;
119     UCHAR Reserved2AFlag:1;
120     UCHAR ExtendedAFlag:1;
121     UCHAR Reserved3AFlag:1;
122     union {
123         UCHAR Err;
124         UCHAR Feature;
125       };
126     UCHAR Count;
127     union {
128         UCHAR Cmd;
129         UCHAR Status;
130       };
131
132     UCHAR Lba0;
133     UCHAR Lba1;
134     UCHAR Lba2;
135     UCHAR Lba3;
136     UCHAR Lba4;
137     UCHAR Lba5;
138     UINT16 Reserved;
139
140     UCHAR Data[];
141   } __attribute__((__packed__));
142 typedef struct AOE_PACKET_ AOE_S_PACKET_, * AOE_SP_PACKET_;
143 #ifdef _MSC_VER
144 #  pragma pack()
145 #endif
146
147 /** An I/O request. */
148 typedef struct AOE_IO_REQ_ {
149     WV_E_DISK_IO_MODE Mode;
150     UINT32 SectorCount;
151     PUCHAR Buffer;
152     PIRP Irp;
153     UINT32 TagCount;
154     UINT32 TotalTags;
155   } AOE_S_IO_REQ_, * AOE_SP_IO_REQ_;
156
157 /** A work item "tag". */
158 typedef struct AOE_WORK_TAG_ {
159     AOE_E_TAG_TYPE_ type;
160     WV_SP_DEV_T device;
161     AOE_SP_IO_REQ_ request_ptr;
162     UINT32 Id;
163     AOE_SP_PACKET_ packet_data;
164     UINT32 PacketSize;
165     LARGE_INTEGER FirstSendTime;
166     LARGE_INTEGER SendTime;
167     UINT32 BufferOffset;
168     UINT32 SectorCount;
169     struct AOE_WORK_TAG_ * next;
170     struct AOE_WORK_TAG_ * previous;
171   } AOE_S_WORK_TAG_, * AOE_SP_WORK_TAG_;
172
173 /** A disk search. */
174 typedef struct AOE_DISK_SEARCH_ {
175     WV_SP_DEV_T device;
176     AOE_SP_WORK_TAG_ tag;
177     struct AOE_DISK_SEARCH_ * next;
178   } AOE_S_DISK_SEARCH_, * AOE_SP_DISK_SEARCH_;
179
180 typedef enum AOE_SEARCH_STATE_ {
181     AoeSearchStateSearchNic_,
182     AoeSearchStateGetSize_,
183     AoeSearchStateGettingSize_,
184     AoeSearchStateGetGeometry_,
185     AoeSearchStateGettingGeometry_,
186     AoeSearchStateGetMaxSectsPerPacket_,
187     AoeSearchStateGettingMaxSectsPerPacket_,
188     AoeSearchStateDone_,
189     AoeSearchStates
190   } AOE_E_SEARCH_STATE_, * AOE_EP_SEARCH_STATE_;
191
192 /** The AoE disk type. */
193 typedef struct AOE_DISK_ {
194     WV_SP_DISK_T disk;
195     UINT32 MTU;
196     UCHAR ClientMac[6];
197     UCHAR ServerMac[6];
198     UINT32 Major;
199     UINT32 Minor;
200     UINT32 MaxSectorsPerPacket;
201     UINT32 Timeout;
202     AOE_E_SEARCH_STATE_ search_state;
203     WV_FP_DEV_FREE prev_free;
204     LIST_ENTRY tracking;
205   } AOE_S_DISK_, * AOE_SP_DISK_;
206
207 typedef struct AOE_TARGET_LIST_ {
208     AOE_S_MOUNT_TARGET Target;
209     struct AOE_TARGET_LIST_ * next;
210   } AOE_S_TARGET_LIST_, * AOE_SP_TARGET_LIST_;
211
212 /** Private globals. */
213 static AOE_SP_TARGET_LIST_ AoeTargetList_ = NULL;
214 static KSPIN_LOCK AoeTargetListLock_;
215 static BOOLEAN AoeStop_ = FALSE;
216 static KSPIN_LOCK AoeLock_;
217 static KEVENT AoeSignal_;
218 static AOE_SP_WORK_TAG_ AoeTagListFirst_ = NULL;
219 static AOE_SP_WORK_TAG_ AoeTagListLast_ = NULL;
220 static AOE_SP_WORK_TAG_ AoeProbeTag_ = NULL;
221 static AOE_SP_DISK_SEARCH_ AoeDiskSearchList_ = NULL;
222 static LONG AoePendingTags_ = 0;
223 static HANDLE AoeThreadHandle_;
224 static BOOLEAN AoeStarted_ = FALSE;
225 static LIST_ENTRY AoeDiskList_;
226 static KSPIN_LOCK AoeDiskListLock_;
227
228 /* Yield a pointer to the AoE disk. */
229 static AOE_SP_DISK_ AoeDiskFromDev_(WV_SP_DEV_T dev_ptr) {
230     return disk__get_ptr(dev_ptr)->ext;
231   }
232
233 /**
234  * Start AoE operations.
235  *
236  * @ret Status          Return status code.
237  */
238 NTSTATUS STDCALL DriverEntry(
239     IN PDRIVER_OBJECT DriverObject,
240     IN PUNICODE_STRING RegistryPath
241   ) {
242     NTSTATUS Status;
243     OBJECT_ATTRIBUTES ObjectAttributes;
244     PVOID ThreadObject;
245     WVL_SP_BUS_T bus_ptr;
246     int i;
247
248     DBG("Entry\n");
249
250     if (AoeStarted_)
251       return STATUS_SUCCESS;
252     /* Initialize the global list of AoE disks. */
253     InitializeListHead(&AoeDiskList_);
254     KeInitializeSpinLock(&AoeDiskListLock_);
255     /* Setup the Registry. */
256     if (!NT_SUCCESS(AoeRegSetup(&Status))) {
257         DBG("Could not update Registry!\n");
258         return Status;
259       } else {
260         DBG("Registry updated\n");
261       }
262     /* Start up the protocol. */
263     if (!NT_SUCCESS(Status = Protocol_Start())) {
264         DBG("Protocol startup failure!\n");
265         return Status;
266       }
267     /* Allocate and zero-fill the global probe tag. */
268     AoeProbeTag_ = wv_mallocz(sizeof *AoeProbeTag_);
269     if (AoeProbeTag_ == NULL) {
270         DBG("Couldn't allocate probe tag; bye!\n");
271         return STATUS_INSUFFICIENT_RESOURCES;
272       }
273
274     /* Set up the probe tag's AoE packet reference. */
275     AoeProbeTag_->PacketSize = sizeof (AOE_S_PACKET_);
276     /* Allocate and zero-fill the probe tag's packet reference. */
277     AoeProbeTag_->packet_data = wv_mallocz(AoeProbeTag_->PacketSize);
278     if (AoeProbeTag_->packet_data == NULL) {
279         DBG("Couldn't allocate AoeProbeTag_->packet_data\n");
280         wv_free(AoeProbeTag_);
281         return STATUS_INSUFFICIENT_RESOURCES;
282       }
283     AoeProbeTag_->SendTime.QuadPart = 0LL;
284
285     /* Initialize the probe tag's AoE packet. */
286     AoeProbeTag_->packet_data->Ver = AOEPROTOCOLVER;
287     AoeProbeTag_->packet_data->Major =
288       htons((UINT16) -1);
289     AoeProbeTag_->packet_data->Minor = (UCHAR) -1;
290     AoeProbeTag_->packet_data->Cmd = 0xec;           /* IDENTIFY DEVICE */
291     AoeProbeTag_->packet_data->Count = 1;
292
293     /* Initialize global target-list spinlock. */
294     KeInitializeSpinLock(&AoeTargetListLock_);
295
296     /* Initialize global spin-lock and global thread signal event. */
297     KeInitializeSpinLock(&AoeLock_);
298     KeInitializeEvent(&AoeSignal_, SynchronizationEvent, FALSE);
299
300     /* Initialize object attributes for thread. */
301     InitializeObjectAttributes(
302         &ObjectAttributes,
303         NULL,
304         OBJ_KERNEL_HANDLE,
305         NULL,
306         NULL
307       );
308
309     /* Create global thread. */
310     if (!NT_SUCCESS(Status = PsCreateSystemThread(
311         &AoeThreadHandle_,
312         THREAD_ALL_ACCESS,
313         &ObjectAttributes,
314         NULL,
315         NULL,
316         AoeThread_,
317         NULL
318       )))
319       return WvlError("PsCreateSystemThread", Status);
320
321     if (!NT_SUCCESS(Status = ObReferenceObjectByHandle(
322         AoeThreadHandle_,
323         THREAD_ALL_ACCESS,
324         NULL,
325         KernelMode,
326         &ThreadObject,
327         NULL
328       ))) {
329         ZwClose(AoeThreadHandle_);
330         WvlError("ObReferenceObjectByHandle", Status);
331         AoeStop_ = TRUE;
332         KeSetEvent(&AoeSignal_, 0, FALSE);
333       }
334
335     for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
336       DriverObject->MajorFunction[i] = AoeDriverIrpNotSupported_;
337     DriverObject->MajorFunction[IRP_MJ_PNP] = AoeDriverIrpPnp_;
338     DriverObject->MajorFunction[IRP_MJ_POWER] = AoeIrpPower;
339     DriverObject->MajorFunction[IRP_MJ_CREATE] = AoeIrpCreateClose;
340     DriverObject->MajorFunction[IRP_MJ_CLOSE] = AoeIrpCreateClose;
341     DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = AoeIrpSysCtl;
342     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AoeDriverIrpDevCtl_;
343     DriverObject->MajorFunction[IRP_MJ_SCSI] = AoeDriverIrpScsi_;
344     /* Set the driver Unload callback. */
345     DriverObject->DriverUnload = AoeUnload_;
346     /* Set the driver AddDevice callback. */
347     DriverObject->DriverExtension->AddDevice = AoeBusAttachFdo;
348     AoeStarted_ = TRUE;
349     if (!AoeBusCreate(DriverObject)) {
350         DBG("Unable to create AoE bus!\n");
351         AoeUnload_(DriverObject);
352         return STATUS_INSUFFICIENT_RESOURCES;
353       }
354     AoeProcessAbft_();
355     DBG("Exit\n");
356     return Status;
357   }
358
359 /**
360  * Stop AoE operations.
361  */
362 static VOID STDCALL AoeUnload_(IN PDRIVER_OBJECT DriverObject) {
363     NTSTATUS Status;
364     AOE_SP_DISK_SEARCH_ disk_searcher, previous_disk_searcher;
365     AOE_SP_WORK_TAG_ tag;
366     KIRQL Irql, Irql2;
367     AOE_SP_TARGET_LIST_ Walker, Next;
368
369     DBG("Entry\n");
370     /* If we're not already started, there's nothing to do. */
371     if (!AoeStarted_)
372       return;
373     /* Destroy the AoE bus. */
374     AoeBusFree();
375     /* Stop the AoE protocol. */
376     Protocol_Stop();
377     /* If we're not already shutting down, signal the event. */
378     if (!AoeStop_) {
379         AoeStop_ = TRUE;
380         KeSetEvent(&AoeSignal_, 0, FALSE);
381         /* Wait until the event has been signalled. */
382         if (!NT_SUCCESS(Status = ZwWaitForSingleObject(
383             AoeThreadHandle_,
384             FALSE,
385             NULL
386           )))
387           WvlError("AoE_Stop ZwWaitForSingleObject", Status);
388         ZwClose(AoeThreadHandle_);
389       }
390
391     /* Free the target list. */
392     KeAcquireSpinLock(&AoeTargetListLock_, &Irql2);
393     Walker = AoeTargetList_;
394     while (Walker != NULL) {
395         Next = Walker->next;
396         wv_free(Walker);
397         Walker = Next;
398       }
399     KeReleaseSpinLock(&AoeTargetListLock_, Irql2);
400
401     /* Wait until we have the global spin-lock. */
402     KeAcquireSpinLock(&AoeLock_, &Irql);
403
404     /* Free disk searches in the global disk search list. */
405     disk_searcher = AoeDiskSearchList_;
406     while (disk_searcher != NULL) {
407         KeSetEvent(
408             &(disk__get_ptr(disk_searcher->device)->SearchEvent),
409             0,
410             FALSE
411           );
412         previous_disk_searcher = disk_searcher;
413         disk_searcher = disk_searcher->next;
414         wv_free(previous_disk_searcher);
415       }
416
417     /* Cancel and free all tags in the global tag list. */
418     tag = AoeTagListFirst_;
419     while (tag != NULL) {
420         if (tag->request_ptr != NULL && --tag->request_ptr->TagCount == 0) {
421             tag->request_ptr->Irp->IoStatus.Information = 0;
422             tag->request_ptr->Irp->IoStatus.Status = STATUS_CANCELLED;
423             IoCompleteRequest(tag->request_ptr->Irp, IO_NO_INCREMENT);
424             wv_free(tag->request_ptr);
425           }
426         if (tag->next == NULL) {
427             wv_free(tag->packet_data);
428             wv_free(tag);
429             tag = NULL;
430           } else {
431             tag = tag->next;
432             wv_free(tag->previous->packet_data);
433             wv_free(tag->previous);
434           }
435       }
436     AoeTagListFirst_ = NULL;
437     AoeTagListLast_ = NULL;
438
439     /* Free the global probe tag and its AoE packet. */
440     wv_free(AoeProbeTag_->packet_data);
441     wv_free(AoeProbeTag_);
442
443     /* Release the global spin-lock. */
444     KeReleaseSpinLock(&AoeLock_, Irql);
445     AoeBusFree();
446     AoeStarted_ = FALSE;
447     DBG("Exit\n");
448   }
449
450 /**
451  * Search for disk parameters.
452  *
453  * @v disk_ptr          The disk to initialize (for AoE, match).
454  * @ret BOOLEAN         See below.
455  *
456  * Returns TRUE if the disk could be matched, FALSE otherwise.
457  */
458 static BOOLEAN STDCALL AoeDiskInit_(IN WV_SP_DISK_T disk_ptr) {
459     AOE_SP_DISK_SEARCH_
460       disk_searcher, disk_search_walker, previous_disk_searcher;
461     LARGE_INTEGER Timeout, CurrentTime;
462     AOE_SP_WORK_TAG_ tag, tag_walker;
463     KIRQL Irql, InnerIrql;
464     LARGE_INTEGER MaxSectorsPerPacketSendTime;
465     UINT32 MTU;
466     AOE_SP_DISK_ aoe_disk_ptr;
467
468     aoe_disk_ptr = AoeDiskFromDev_(disk_ptr->Dev);
469     /*
470      * Allocate our disk search 
471      */
472     if ((disk_searcher = wv_malloc(sizeof *disk_searcher)) == NULL) {
473         DBG ( "Couldn't allocate for disk_searcher; bye!\n" );
474         return FALSE;
475       }
476
477     /*
478      * Initialize the disk search 
479      */
480     disk_searcher->device = disk_ptr->Dev;
481     disk_searcher->next = NULL;
482     aoe_disk_ptr->search_state = AoeSearchStateSearchNic_;
483     KeResetEvent ( &disk_ptr->SearchEvent );
484
485     /*
486      * Wait until we have the global spin-lock 
487      */
488     KeAcquireSpinLock ( &AoeLock_, &Irql );
489
490     /*
491      * Add our disk search to the global list of disk searches 
492      */
493     if ( AoeDiskSearchList_ == NULL )
494       {
495         AoeDiskSearchList_ = disk_searcher;
496       }
497     else
498       {
499         disk_search_walker = AoeDiskSearchList_;
500         while ( disk_search_walker->next )
501     disk_search_walker = disk_search_walker->next;
502         disk_search_walker->next = disk_searcher;
503       }
504
505     /*
506      * Release the global spin-lock 
507      */
508     KeReleaseSpinLock ( &AoeLock_, Irql );
509
510     /*
511      * We go through all the states until the disk is ready for use 
512      */
513     while ( TRUE )
514       {
515         /*
516          * Wait for our device's extension's search to be signalled 
517          */
518         /*
519          * TODO: Make the below value a #defined constant 
520          */
521         /*
522          * 500.000 * 100ns = 50.000.000 ns = 50ms 
523          */
524         Timeout.QuadPart = -500000LL;
525         KeWaitForSingleObject ( &disk_ptr->SearchEvent, Executive, KernelMode,
526               FALSE, &Timeout );
527         if ( AoeStop_ )
528     {
529       DBG ( "AoE is shutting down; bye!\n" );
530       return FALSE;
531     }
532
533         /*
534          * Wait until we have the device extension's spin-lock 
535          */
536         KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
537
538         if (aoe_disk_ptr->search_state == AoeSearchStateSearchNic_)
539     {
540       if ( !Protocol_SearchNIC ( aoe_disk_ptr->ClientMac ) )
541         {
542           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
543           continue;
544         }
545       else
546         {
547           /*
548            * We found the adapter to use, get MTU next 
549            */
550           aoe_disk_ptr->MTU = Protocol_GetMTU ( aoe_disk_ptr->ClientMac );
551           aoe_disk_ptr->search_state = AoeSearchStateGetSize_;
552         }
553     }
554
555         if (aoe_disk_ptr->search_state == AoeSearchStateGettingSize_)
556     {
557       /*
558        * Still getting the disk's size 
559        */
560       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
561       continue;
562     }
563         if (aoe_disk_ptr->search_state == AoeSearchStateGettingGeometry_)
564     {
565       /*
566        * Still getting the disk's geometry 
567        */
568       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
569       continue;
570     }
571         if (aoe_disk_ptr->search_state ==
572           AoeSearchStateGettingMaxSectsPerPacket_)
573     {
574       KeQuerySystemTime ( &CurrentTime );
575       /*
576        * TODO: Make the below value a #defined constant 
577        */
578       /*
579        * 2.500.000 * 100ns = 250.000.000 ns = 250ms 
580        */
581       if ( CurrentTime.QuadPart >
582            MaxSectorsPerPacketSendTime.QuadPart + 2500000LL )
583         {
584           DBG ( "No reply after 250ms for MaxSectorsPerPacket %d, "
585           "giving up\n", aoe_disk_ptr->MaxSectorsPerPacket );
586           aoe_disk_ptr->MaxSectorsPerPacket--;
587           aoe_disk_ptr->search_state = AoeSearchStateDone_;
588         }
589       else
590         {
591           /*
592            * Still getting the maximum sectors per packet count 
593            */
594           KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
595           continue;
596         }
597     }
598
599         if (aoe_disk_ptr->search_state == AoeSearchStateDone_)
600     {
601       /*
602        * We've finished the disk search; perform clean-up 
603        */
604       KeAcquireSpinLock ( &AoeLock_, &InnerIrql );
605
606       /*
607        * Tag clean-up: Find out if our tag is in the global tag list 
608        */
609       tag_walker = AoeTagListFirst_;
610       while ( tag_walker != NULL && tag_walker != tag )
611         tag_walker = tag_walker->next;
612       if ( tag_walker != NULL )
613         {
614           /*
615            * We found it.  If it's at the beginning of the list, adjust
616            * the list to point the the next tag
617            */
618           if ( tag->previous == NULL )
619       AoeTagListFirst_ = tag->next;
620           else
621       /*
622        * Remove our tag from the list 
623        */
624       tag->previous->next = tag->next;
625           /*
626            * If we 're at the end of the list, adjust the list's end to
627            * point to the penultimate tag
628            */
629           if ( tag->next == NULL )
630       AoeTagListLast_ = tag->previous;
631           else
632       /*
633        * Remove our tag from the list 
634        */
635       tag->next->previous = tag->previous;
636           AoePendingTags_--;
637           if ( AoePendingTags_ < 0 )
638       DBG ( "AoePendingTags_ < 0!!\n" );
639           /*
640            * Free our tag and its AoE packet 
641            */
642           wv_free(tag->packet_data);
643           wv_free(tag);
644         }
645
646       /*
647        * Disk search clean-up 
648        */
649       if ( AoeDiskSearchList_ == NULL )
650         {
651           DBG ( "AoeDiskSearchList_ == NULL!!\n" );
652         }
653       else
654         {
655           /*
656            * Find our disk search in the global list of disk searches 
657            */
658           disk_search_walker = AoeDiskSearchList_;
659           while (
660               disk_search_walker &&
661               disk_search_walker->device != disk_ptr->Dev
662             ) {
663         previous_disk_searcher = disk_search_walker;
664         disk_search_walker = disk_search_walker->next;
665       }
666           if ( disk_search_walker )
667       {
668         /*
669          * We found our disk search.  If it's the first one in
670          * the list, adjust the list and remove it
671          */
672         if ( disk_search_walker == AoeDiskSearchList_ )
673           AoeDiskSearchList_ = disk_search_walker->next;
674         else
675           /*
676            * Just remove it 
677            */
678           previous_disk_searcher->next = disk_search_walker->next;
679         /*
680          * Free our disk search 
681          */
682         wv_free(disk_search_walker);
683       }
684           else
685       {
686         DBG ( "Disk not found in AoeDiskSearchList_!!\n" );
687       }
688         }
689
690       /*
691        * Release global and device extension spin-locks 
692        */
693       KeReleaseSpinLock ( &AoeLock_, InnerIrql );
694       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
695
696       DBG ( "Disk size: %I64uM cylinders: %I64u heads: %u "
697       "sectors: %u sectors per packet: %u\n",
698       disk_ptr->LBADiskSize / 2048, disk_ptr->Cylinders,
699       disk_ptr->Heads, disk_ptr->Sectors,
700       aoe_disk_ptr->MaxSectorsPerPacket );
701       return TRUE;
702     }
703
704         #if 0
705         if ( aoe_disk_ptr->search_state == AoeSearchStateDone_)
706         #endif
707         /*
708          * Establish our tag 
709          */
710         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
711       DBG ( "Couldn't allocate tag\n" );
712       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
713       /*
714        * Maybe next time around 
715        */
716       continue;
717     }
718         tag->type = AoeTagTypeSearchDrive_;
719         tag->device = disk_ptr->Dev;
720
721         /*
722          * Establish our tag's AoE packet 
723          */
724         tag->PacketSize = sizeof (AOE_S_PACKET_);
725         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
726       DBG ( "Couldn't allocate tag->packet_data\n" );
727       wv_free(tag);
728       tag = NULL;
729       KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
730       /*
731        * Maybe next time around 
732        */
733       continue;
734     }
735         tag->packet_data->Ver = AOEPROTOCOLVER;
736         tag->packet_data->Major =
737     htons ( ( UINT16 ) aoe_disk_ptr->Major );
738         tag->packet_data->Minor = ( UCHAR ) aoe_disk_ptr->Minor;
739         tag->packet_data->ExtendedAFlag = TRUE;
740
741         /*
742          * Initialize the packet appropriately based on our current phase 
743          */
744         switch ( aoe_disk_ptr->search_state )
745     {
746       case AoeSearchStateGetSize_:
747         /*
748          * TODO: Make the below value into a #defined constant 
749          */
750         tag->packet_data->Cmd = 0xec;  /* IDENTIFY DEVICE */
751         tag->packet_data->Count = 1;
752         aoe_disk_ptr->search_state = AoeSearchStateGettingSize_;
753         break;
754       case AoeSearchStateGetGeometry_:
755         /*
756          * TODO: Make the below value into a #defined constant 
757          */
758         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
759         tag->packet_data->Count = 1;
760         aoe_disk_ptr->search_state = AoeSearchStateGettingGeometry_;
761         break;
762       case AoeSearchStateGetMaxSectsPerPacket_:
763         /*
764          * TODO: Make the below value into a #defined constant 
765          */
766         tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
767         tag->packet_data->Count =
768           ( UCHAR ) ( ++aoe_disk_ptr->MaxSectorsPerPacket );
769         KeQuerySystemTime ( &MaxSectorsPerPacketSendTime );
770         aoe_disk_ptr->search_state =
771           AoeSearchStateGettingMaxSectsPerPacket_;
772         /*
773          * TODO: Make the below value into a #defined constant 
774          */
775         aoe_disk_ptr->Timeout = 200000;
776         break;
777       default:
778         DBG ( "Undefined search_state!!\n" );
779         wv_free(tag->packet_data);
780         wv_free(tag);
781         /*
782          * TODO: Do we need to nullify tag here? 
783          */
784         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
785         continue;
786         break;
787     }
788
789         /*
790          * Enqueue our tag 
791          */
792         tag->next = NULL;
793         KeAcquireSpinLock ( &AoeLock_, &InnerIrql );
794         if ( AoeTagListFirst_ == NULL )
795     {
796       AoeTagListFirst_ = tag;
797       tag->previous = NULL;
798     }
799         else
800     {
801       AoeTagListLast_->next = tag;
802       tag->previous = AoeTagListLast_;
803     }
804         AoeTagListLast_ = tag;
805         KeReleaseSpinLock ( &AoeLock_, InnerIrql );
806         KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
807       }
808   }
809
810 static NTSTATUS STDCALL AoeDiskIo_(
811     IN WV_SP_DEV_T dev_ptr,
812     IN WV_E_DISK_IO_MODE mode,
813     IN LONGLONG start_sector,
814     IN UINT32 sector_count,
815     IN PUCHAR buffer,
816     IN PIRP irp
817   ) {
818     AOE_SP_IO_REQ_ request_ptr;
819     AOE_SP_WORK_TAG_ tag, new_tag_list = NULL, previous_tag = NULL;
820     KIRQL Irql;
821     UINT32 i;
822     PHYSICAL_ADDRESS PhysicalAddress;
823     PUCHAR PhysicalMemory;
824     WV_SP_DISK_T disk_ptr;
825     AOE_SP_DISK_ aoe_disk_ptr;
826
827     /*
828      * Establish pointers to the disk and AoE disk
829      */
830     disk_ptr = disk__get_ptr ( dev_ptr );
831     aoe_disk_ptr = AoeDiskFromDev_(dev_ptr);
832
833     if ( AoeStop_ )
834       {
835         /*
836          * Shutting down AoE; we can't service this request 
837          */
838         irp->IoStatus.Information = 0;
839         irp->IoStatus.Status = STATUS_CANCELLED;
840         IoCompleteRequest ( irp, IO_NO_INCREMENT );
841         return STATUS_CANCELLED;
842       }
843
844     if ( sector_count < 1 )
845       {
846         /*
847          * A silly request 
848          */
849         DBG ( "sector_count < 1; cancelling\n" );
850         irp->IoStatus.Information = 0;
851         irp->IoStatus.Status = STATUS_CANCELLED;
852         IoCompleteRequest ( irp, IO_NO_INCREMENT );
853         return STATUS_CANCELLED;
854       }
855
856     /*
857      * Allocate and zero-fill our request 
858      */
859     if ((request_ptr = wv_mallocz(sizeof *request_ptr)) == NULL) {
860         DBG ( "Couldn't allocate for reques_ptr; bye!\n" );
861         irp->IoStatus.Information = 0;
862         irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
863         IoCompleteRequest ( irp, IO_NO_INCREMENT );
864         return STATUS_INSUFFICIENT_RESOURCES;
865       }
866
867     /*
868      * Initialize the request 
869      */
870     request_ptr->Mode = mode;
871     request_ptr->SectorCount = sector_count;
872     request_ptr->Buffer = buffer;
873     request_ptr->Irp = irp;
874     request_ptr->TagCount = 0;
875
876     /*
877      * Split the requested sectors into packets in tags
878      */
879     for ( i = 0; i < sector_count; i += aoe_disk_ptr->MaxSectorsPerPacket )
880       {
881         /*
882          * Allocate each tag 
883          */
884         if ((tag = wv_mallocz(sizeof *tag)) == NULL) {
885       DBG ( "Couldn't allocate tag; bye!\n" );
886       /*
887        * We failed while allocating tags; free the ones we built 
888        */
889       tag = new_tag_list;
890       while ( tag != NULL )
891         {
892           previous_tag = tag;
893           tag = tag->next;
894           wv_free(previous_tag->packet_data);
895           wv_free(previous_tag);
896         }
897       wv_free(request_ptr);
898       irp->IoStatus.Information = 0;
899       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
900       IoCompleteRequest ( irp, IO_NO_INCREMENT );
901       return STATUS_INSUFFICIENT_RESOURCES;
902     }
903
904         /*
905          * Initialize each tag 
906          */
907         tag->type = AoeTagTypeIo_;
908         tag->request_ptr = request_ptr;
909         tag->device = dev_ptr;
910         request_ptr->TagCount++;
911         tag->Id = 0;
912         tag->BufferOffset = i * disk_ptr->SectorSize;
913         tag->SectorCount =
914     ( ( sector_count - i ) <
915       aoe_disk_ptr->MaxSectorsPerPacket ? sector_count -
916       i : aoe_disk_ptr->MaxSectorsPerPacket );
917
918         /*
919          * Allocate and initialize each tag's AoE packet 
920          */
921         tag->PacketSize = sizeof (AOE_S_PACKET_);
922         if (mode == WvDiskIoModeWrite)
923     tag->PacketSize += tag->SectorCount * disk_ptr->SectorSize;
924         if ((tag->packet_data = wv_mallocz(tag->PacketSize)) == NULL) {
925       DBG ( "Couldn't allocate tag->packet_data; bye!\n" );
926       /*
927        * We failed while allocating an AoE packet; free
928        * the tags we built
929        */
930       wv_free(tag);
931       tag = new_tag_list;
932       while ( tag != NULL )
933         {
934           previous_tag = tag;
935           tag = tag->next;
936           wv_free(previous_tag->packet_data);
937           wv_free(previous_tag);
938         }
939       wv_free(request_ptr);
940       irp->IoStatus.Information = 0;
941       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
942       IoCompleteRequest ( irp, IO_NO_INCREMENT );
943       return STATUS_INSUFFICIENT_RESOURCES;
944     }
945         tag->packet_data->Ver = AOEPROTOCOLVER;
946         tag->packet_data->Major =
947     htons ( ( UINT16 ) aoe_disk_ptr->Major );
948         tag->packet_data->Minor = ( UCHAR ) aoe_disk_ptr->Minor;
949         tag->packet_data->Tag = 0;
950         tag->packet_data->Command = 0;
951         tag->packet_data->ExtendedAFlag = TRUE;
952         if (mode == WvDiskIoModeRead)
953     {
954       tag->packet_data->Cmd = 0x24;  /* READ SECTOR */
955     }
956         else
957     {
958       tag->packet_data->Cmd = 0x34;  /* WRITE SECTOR */
959       tag->packet_data->WriteAFlag = 1;
960     }
961         tag->packet_data->Count = ( UCHAR ) tag->SectorCount;
962         tag->packet_data->Lba0 =
963     ( UCHAR ) ( ( ( start_sector + i ) >> 0 ) & 255 );
964         tag->packet_data->Lba1 =
965     ( UCHAR ) ( ( ( start_sector + i ) >> 8 ) & 255 );
966         tag->packet_data->Lba2 =
967     ( UCHAR ) ( ( ( start_sector + i ) >> 16 ) & 255 );
968         tag->packet_data->Lba3 =
969     ( UCHAR ) ( ( ( start_sector + i ) >> 24 ) & 255 );
970         tag->packet_data->Lba4 =
971     ( UCHAR ) ( ( ( start_sector + i ) >> 32 ) & 255 );
972         tag->packet_data->Lba5 =
973     ( UCHAR ) ( ( ( start_sector + i ) >> 40 ) & 255 );
974
975         /* For a write request, copy from the buffer into the AoE packet. */
976         if (mode == WvDiskIoModeWrite)
977     RtlCopyMemory ( tag->packet_data->Data, &buffer[tag->BufferOffset],
978         tag->SectorCount * disk_ptr->SectorSize );
979
980         /*
981          * Add this tag to the request's tag list 
982          */
983         tag->previous = previous_tag;
984         tag->next = NULL;
985         if ( new_tag_list == NULL )
986     {
987       new_tag_list = tag;
988     }
989         else
990     {
991       previous_tag->next = tag;
992     }
993         previous_tag = tag;
994       }
995     /*
996      * Split the requested sectors into packets in tags
997      */
998     request_ptr->TotalTags = request_ptr->TagCount;
999
1000     /*
1001      * Wait until we have the global spin-lock 
1002      */
1003     KeAcquireSpinLock ( &AoeLock_, &Irql );
1004
1005     /*
1006      * Enqueue our request's tag list to the global tag list 
1007      */
1008     if ( AoeTagListLast_ == NULL )
1009       {
1010         AoeTagListFirst_ = new_tag_list;
1011       }
1012     else
1013       {
1014         AoeTagListLast_->next = new_tag_list;
1015         new_tag_list->previous = AoeTagListLast_;
1016       }
1017     /*
1018      * Adjust the global list to reflect our last tag 
1019      */
1020     AoeTagListLast_ = tag;
1021
1022     irp->IoStatus.Information = 0;
1023     irp->IoStatus.Status = STATUS_PENDING;
1024     IoMarkIrpPending ( irp );
1025
1026     KeReleaseSpinLock ( &AoeLock_, Irql );
1027     KeSetEvent ( &AoeSignal_, 0, FALSE );
1028     return STATUS_PENDING;
1029   }
1030
1031 static VOID STDCALL add_target(
1032     IN PUCHAR ClientMac,
1033     IN PUCHAR ServerMac,
1034     UINT16 Major,
1035     UCHAR Minor,
1036     LONGLONG LBASize
1037   )
1038   {
1039     AOE_SP_TARGET_LIST_ Walker, Last;
1040     KIRQL Irql;
1041
1042     KeAcquireSpinLock ( &AoeTargetListLock_, &Irql );
1043     Walker = Last = AoeTargetList_;
1044     while ( Walker != NULL )
1045       {
1046         if (wv_memcmpeq(&Walker->Target.ClientMac, ClientMac, 6) &&
1047           wv_memcmpeq(&Walker->Target.ServerMac, ServerMac, 6) &&
1048           Walker->Target.Major == Major
1049           && Walker->Target.Minor == Minor) {
1050       if ( Walker->Target.LBASize != LBASize )
1051         {
1052           DBG ( "LBASize changed for e%d.%d " "(%I64u->%I64u)\n", Major,
1053           Minor, Walker->Target.LBASize, LBASize );
1054           Walker->Target.LBASize = LBASize;
1055         }
1056       KeQuerySystemTime ( &Walker->Target.ProbeTime );
1057       KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1058       return;
1059     }
1060         Last = Walker;
1061         Walker = Walker->next;
1062       }
1063
1064     if ((Walker = wv_malloc(sizeof *Walker)) == NULL) {
1065         DBG("wv_malloc Walker\n");
1066         KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1067         return;
1068       }
1069     Walker->next = NULL;
1070     RtlCopyMemory ( Walker->Target.ClientMac, ClientMac, 6 );
1071     RtlCopyMemory ( Walker->Target.ServerMac, ServerMac, 6 );
1072     Walker->Target.Major = Major;
1073     Walker->Target.Minor = Minor;
1074     Walker->Target.LBASize = LBASize;
1075     KeQuerySystemTime ( &Walker->Target.ProbeTime );
1076
1077     if ( Last == NULL )
1078       {
1079         AoeTargetList_ = Walker;
1080       }
1081     else
1082       {
1083         Last->next = Walker;
1084       }
1085     KeReleaseSpinLock ( &AoeTargetListLock_, Irql );
1086   }
1087
1088 /**
1089  * Process an AoE reply.
1090  *
1091  * @v SourceMac         The AoE server's MAC address.
1092  * @v DestinationMac    The AoE client's MAC address.
1093  * @v Data              The AoE packet.
1094  * @v DataSize          The AoE packet's size.
1095  */
1096 NTSTATUS STDCALL aoe__reply(
1097     IN PUCHAR SourceMac,
1098     IN PUCHAR DestinationMac,
1099     IN PUCHAR Data,
1100     IN UINT32 DataSize
1101   )
1102   {
1103     AOE_SP_PACKET_ reply = (AOE_SP_PACKET_) Data;
1104     LONGLONG LBASize;
1105     AOE_SP_WORK_TAG_ tag;
1106     KIRQL Irql;
1107     BOOLEAN Found = FALSE;
1108     LARGE_INTEGER CurrentTime;
1109     WV_SP_DISK_T disk_ptr;
1110     AOE_SP_DISK_ aoe_disk_ptr;
1111
1112     /*
1113      * Discard non-responses 
1114      */
1115     if ( !reply->ResponseFlag )
1116       return STATUS_SUCCESS;
1117
1118     /*
1119      * If the response matches our probe, add the AoE disk device 
1120      */
1121     if ( AoeProbeTag_->Id == reply->Tag )
1122       {
1123         RtlCopyMemory ( &LBASize, &reply->Data[200], sizeof ( LONGLONG ) );
1124         add_target ( DestinationMac, SourceMac, ntohs ( reply->Major ),
1125          reply->Minor, LBASize );
1126         return STATUS_SUCCESS;
1127       }
1128
1129     /*
1130      * Wait until we have the global spin-lock 
1131      */
1132     KeAcquireSpinLock ( &AoeLock_, &Irql );
1133
1134     /*
1135      * Search for request tag 
1136      */
1137     if ( AoeTagListFirst_ == NULL )
1138       {
1139         KeReleaseSpinLock ( &AoeLock_, Irql );
1140         return STATUS_SUCCESS;
1141       }
1142     tag = AoeTagListFirst_;
1143     while ( tag != NULL )
1144       {
1145         if ( ( tag->Id == reply->Tag )
1146        && ( tag->packet_data->Major == reply->Major )
1147        && ( tag->packet_data->Minor == reply->Minor ) )
1148     {
1149       Found = TRUE;
1150       break;
1151     }
1152         tag = tag->next;
1153       }
1154     if ( !Found )
1155       {
1156         KeReleaseSpinLock ( &AoeLock_, Irql );
1157         return STATUS_SUCCESS;
1158       }
1159     else
1160       {
1161         /*
1162          * Remove the tag from the global tag list 
1163          */
1164         if ( tag->previous == NULL )
1165     AoeTagListFirst_ = tag->next;
1166         else
1167     tag->previous->next = tag->next;
1168         if ( tag->next == NULL )
1169     AoeTagListLast_ = tag->previous;
1170         else
1171     tag->next->previous = tag->previous;
1172         AoePendingTags_--;
1173         if ( AoePendingTags_ < 0 )
1174     DBG ( "AoePendingTags_ < 0!!\n" );
1175         KeSetEvent ( &AoeSignal_, 0, FALSE );
1176       }
1177     KeReleaseSpinLock ( &AoeLock_, Irql );
1178
1179     /*
1180      * Establish pointers to the disk device and AoE disk
1181      */
1182     disk_ptr = disk__get_ptr ( tag->device );
1183     aoe_disk_ptr = AoeDiskFromDev_(tag->device);
1184
1185     /*
1186      * If our tag was a discovery request, note the server 
1187      */
1188     if (wv_memcmpeq(aoe_disk_ptr->ServerMac, "\xff\xff\xff\xff\xff\xff", 6)) {
1189         RtlCopyMemory ( aoe_disk_ptr->ServerMac, SourceMac, 6 );
1190         DBG ( "Major: %d minor: %d found on server "
1191         "%02x:%02x:%02x:%02x:%02x:%02x\n", aoe_disk_ptr->Major,
1192         aoe_disk_ptr->Minor, SourceMac[0], SourceMac[1], SourceMac[2],
1193         SourceMac[3], SourceMac[4], SourceMac[5] );
1194       }
1195
1196     KeQuerySystemTime ( &CurrentTime );
1197     aoe_disk_ptr->Timeout -=
1198       ( UINT32 ) ( ( aoe_disk_ptr->Timeout -
1199               ( CurrentTime.QuadPart -
1200           tag->FirstSendTime.QuadPart ) ) / 1024 );
1201     /*
1202      * TODO: Replace the values below with #defined constants 
1203      */
1204     if ( aoe_disk_ptr->Timeout > 100000000 )
1205       aoe_disk_ptr->Timeout = 100000000;
1206
1207     switch ( tag->type )
1208       {
1209         case AoeTagTypeSearchDrive_:
1210     KeAcquireSpinLock ( &disk_ptr->SpinLock, &Irql );
1211     switch ( aoe_disk_ptr->search_state )
1212       {
1213         case AoeSearchStateGettingSize_:
1214           /*
1215            * The reply tells us the disk size
1216            */
1217           RtlCopyMemory ( &disk_ptr->LBADiskSize, &reply->Data[200],
1218               sizeof ( LONGLONG ) );
1219           /*
1220            * Next we are concerned with the disk geometry
1221            */
1222           aoe_disk_ptr->search_state = AoeSearchStateGetGeometry_;
1223           break;
1224         case AoeSearchStateGettingGeometry_:
1225           /*
1226            * FIXME: use real values from partition table.
1227            * We used to truncate a fractional end cylinder, but
1228            * now leave it be in the hopes everyone uses LBA
1229            */
1230           disk_ptr->SectorSize = 512;
1231           disk_ptr->Heads = 255;
1232           disk_ptr->Sectors = 63;
1233           disk_ptr->Cylinders =
1234       disk_ptr->LBADiskSize / ( disk_ptr->Heads *
1235               disk_ptr->Sectors );
1236           /*
1237            * Next we are concerned with the maximum sectors per packet
1238            */
1239           aoe_disk_ptr->search_state =
1240             AoeSearchStateGetMaxSectsPerPacket_;
1241           break;
1242         case AoeSearchStateGettingMaxSectsPerPacket_:
1243           DataSize -= sizeof (AOE_S_PACKET_);
1244           if ( DataSize <
1245          ( aoe_disk_ptr->MaxSectorsPerPacket *
1246            disk_ptr->SectorSize ) )
1247       {
1248         DBG ( "Packet size too low while getting "
1249         "MaxSectorsPerPacket (tried %d, got size of %d)\n",
1250         aoe_disk_ptr->MaxSectorsPerPacket, DataSize );
1251         aoe_disk_ptr->MaxSectorsPerPacket--;
1252         aoe_disk_ptr->search_state = AoeSearchStateDone_;
1253       }
1254           else if ( aoe_disk_ptr->MTU <
1255         ( sizeof (AOE_S_PACKET_) +
1256           ( ( aoe_disk_ptr->MaxSectorsPerPacket +
1257               1 ) * disk_ptr->SectorSize ) ) )
1258       {
1259         DBG ( "Got MaxSectorsPerPacket %d at size of %d. "
1260         "MTU of %d reached\n",
1261         aoe_disk_ptr->MaxSectorsPerPacket, DataSize,
1262         aoe_disk_ptr->MTU );
1263         aoe_disk_ptr->search_state = AoeSearchStateDone_;
1264       }
1265           else
1266       {
1267         DBG ( "Got MaxSectorsPerPacket %d at size of %d, "
1268         "trying next...\n", aoe_disk_ptr->MaxSectorsPerPacket,
1269         DataSize );
1270         aoe_disk_ptr->search_state =
1271           AoeSearchStateGetMaxSectsPerPacket_;
1272       }
1273           break;
1274         default:
1275           DBG ( "Undefined search_state!\n" );
1276           break;
1277       }
1278     KeReleaseSpinLock ( &disk_ptr->SpinLock, Irql );
1279     KeSetEvent ( &disk_ptr->SearchEvent, 0, FALSE );
1280     break;
1281         case AoeTagTypeIo_:
1282     /* If the reply is in response to a read request, get our data! */
1283     if (tag->request_ptr->Mode == WvDiskIoModeRead)
1284       RtlCopyMemory ( &tag->request_ptr->Buffer[tag->BufferOffset],
1285           reply->Data,
1286           tag->SectorCount * disk_ptr->SectorSize );
1287     /*
1288      * If this is the last reply expected for the read request,
1289      * complete the IRP and free the request
1290      */
1291     if ( InterlockedDecrement ( &tag->request_ptr->TagCount ) == 0 )
1292       {
1293         WvlIrpComplete(
1294             tag->request_ptr->Irp,
1295             tag->request_ptr->SectorCount * disk_ptr->SectorSize,
1296             STATUS_SUCCESS
1297           );
1298         wv_free(tag->request_ptr);
1299       }
1300     break;
1301         default:
1302     DBG ( "Unknown tag type!!\n" );
1303     break;
1304       }
1305
1306     KeSetEvent ( &AoeSignal_, 0, FALSE );
1307     wv_free(tag->packet_data);
1308     wv_free(tag);
1309     return STATUS_SUCCESS;
1310   }
1311
1312 VOID aoe__reset_probe(void)
1313   {
1314     AoeProbeTag_->SendTime.QuadPart = 0LL;
1315   }
1316
1317 static VOID STDCALL AoeThread_(IN PVOID StartContext)
1318   {
1319     NTSTATUS status;
1320     LARGE_INTEGER Timeout, CurrentTime, ProbeTime, ReportTime;
1321     UINT32 NextTagId = 1;
1322     AOE_SP_WORK_TAG_ tag;
1323     KIRQL Irql;
1324     UINT32 Sends = 0;
1325     UINT32 Resends = 0;
1326     UINT32 ResendFails = 0;
1327     UINT32 Fails = 0;
1328     UINT32 RequestTimeout = 0;
1329     WV_SP_DISK_T disk_ptr;
1330     AOE_SP_DISK_ aoe_disk_ptr;
1331
1332     DBG ( "Entry\n" );
1333
1334     /* Create the PDO for the sub-bus on the WinVBlock bus. */
1335     status = WvDriverAddDummy(
1336         AoeBusDummyIds,
1337         FILE_DEVICE_CONTROLLER,
1338         FILE_DEVICE_SECURE_OPEN
1339       );
1340     if (!NT_SUCCESS(status)) {
1341         DBG("Couldn't add AoE bus to WinVBlock bus!\n");
1342         status = PsTerminateSystemThread(status);
1343         return;
1344       }
1345
1346     ReportTime.QuadPart = 0LL;
1347     ProbeTime.QuadPart = 0LL;
1348
1349     while ( TRUE )
1350       {
1351         /*
1352          * TODO: Make the below value a #defined constant 
1353          */
1354         /*
1355          * 100.000 * 100ns = 10.000.000 ns = 10ms
1356          */
1357         Timeout.QuadPart = -100000LL;
1358         KeWaitForSingleObject ( &AoeSignal_, Executive,
1359               KernelMode, FALSE, &Timeout );
1360         KeResetEvent ( &AoeSignal_ );
1361         if ( AoeStop_ )
1362     {
1363       DBG ( "Stopping...\n" );
1364       WvlBusCancelWorkItems(&AoeBusMain);
1365       PsTerminateSystemThread ( STATUS_SUCCESS );
1366     }
1367         WvlBusProcessWorkItems(&AoeBusMain);
1368
1369         KeQuerySystemTime ( &CurrentTime );
1370         /*
1371          * TODO: Make the below value a #defined constant 
1372          */
1373         if ( CurrentTime.QuadPart > ( ReportTime.QuadPart + 10000000LL ) )
1374     {
1375       DBG ( "Sends: %d  Resends: %d  ResendFails: %d  Fails: %d  "
1376       "AoePendingTags_: %d  RequestTimeout: %d\n", Sends,
1377       Resends, ResendFails, Fails, AoePendingTags_,
1378       RequestTimeout );
1379       Sends = 0;
1380       Resends = 0;
1381       ResendFails = 0;
1382       Fails = 0;
1383       KeQuerySystemTime ( &ReportTime );
1384     }
1385
1386         /*
1387          * TODO: Make the below value a #defined constant 
1388          */
1389         if ( CurrentTime.QuadPart >
1390        ( AoeProbeTag_->SendTime.QuadPart + 100000000LL ) )
1391     {
1392       AoeProbeTag_->Id = NextTagId++;
1393       if ( NextTagId == 0 )
1394         NextTagId++;
1395       AoeProbeTag_->packet_data->Tag = AoeProbeTag_->Id;
1396       Protocol_Send ( "\xff\xff\xff\xff\xff\xff",
1397           "\xff\xff\xff\xff\xff\xff",
1398           ( PUCHAR ) AoeProbeTag_->
1399           packet_data, AoeProbeTag_->PacketSize,
1400           NULL );
1401       KeQuerySystemTime ( &AoeProbeTag_->SendTime );
1402     }
1403
1404         KeAcquireSpinLock ( &AoeLock_, &Irql );
1405         if ( AoeTagListFirst_ == NULL )
1406     {
1407       KeReleaseSpinLock ( &AoeLock_, Irql );
1408       continue;
1409     }
1410         tag = AoeTagListFirst_;
1411         while ( tag != NULL )
1412     {
1413       /*
1414        * Establish pointers to the disk and AoE disk
1415        */
1416       disk_ptr = disk__get_ptr ( tag->device );
1417       aoe_disk_ptr = AoeDiskFromDev_(tag->device);
1418
1419       RequestTimeout = aoe_disk_ptr->Timeout;
1420       if ( tag->Id == 0 )
1421         {
1422           if ( AoePendingTags_ <= 64 )
1423       {
1424         /*
1425          * if ( AoePendingTags_ <= 102400 ) { 
1426          */
1427         if ( AoePendingTags_ < 0 )
1428           DBG ( "AoePendingTags_ < 0!!\n" );
1429         tag->Id = NextTagId++;
1430         if ( NextTagId == 0 )
1431           NextTagId++;
1432         tag->packet_data->Tag = tag->Id;
1433         if ( Protocol_Send
1434              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1435          ( PUCHAR ) tag->packet_data,
1436          tag->PacketSize, tag ) )
1437           {
1438             KeQuerySystemTime ( &tag->FirstSendTime );
1439             KeQuerySystemTime ( &tag->SendTime );
1440             AoePendingTags_++;
1441             Sends++;
1442           }
1443         else
1444           {
1445             Fails++;
1446             tag->Id = 0;
1447             break;
1448           }
1449       }
1450         }
1451       else
1452         {
1453           KeQuerySystemTime ( &CurrentTime );
1454           if ( CurrentTime.QuadPart >
1455          ( tag->SendTime.QuadPart +
1456            ( LONGLONG ) ( aoe_disk_ptr->Timeout * 2 ) ) )
1457       {
1458         if ( Protocol_Send
1459              ( aoe_disk_ptr->ClientMac, aoe_disk_ptr->ServerMac,
1460          ( PUCHAR ) tag->packet_data,
1461          tag->PacketSize, tag ) )
1462           {
1463             KeQuerySystemTime ( &tag->SendTime );
1464             aoe_disk_ptr->Timeout += aoe_disk_ptr->Timeout / 1000;
1465             if ( aoe_disk_ptr->Timeout > 100000000 )
1466         aoe_disk_ptr->Timeout = 100000000;
1467             Resends++;
1468           }
1469         else
1470           {
1471             ResendFails++;
1472             break;
1473           }
1474       }
1475         }
1476       tag = tag->next;
1477       if ( tag == AoeTagListFirst_ )
1478         {
1479           DBG ( "Taglist Cyclic!!\n" );
1480           break;
1481         }
1482     }
1483         KeReleaseSpinLock ( &AoeLock_, Irql );
1484       }
1485     DBG ( "Exit\n" );
1486   }
1487
1488 static UINT32 AoeDiskMaxXferLen_(IN WV_SP_DISK_T disk_ptr) {
1489     AOE_SP_DISK_ aoe_disk_ptr = AoeDiskFromDev_(disk_ptr->Dev);
1490
1491     return disk_ptr->SectorSize * aoe_disk_ptr->MaxSectorsPerPacket;
1492   }
1493
1494 static UINT32 STDCALL query_id(
1495     IN WV_SP_DEV_T dev,
1496     IN BUS_QUERY_ID_TYPE query_type,
1497     IN OUT WCHAR (*buf)[512]
1498   ) {
1499     AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev);
1500
1501     switch (query_type) {
1502         case BusQueryDeviceID:
1503           return swprintf(*buf, WVL_M_WLIT L"\\AoEHardDisk") + 1;
1504
1505         case BusQueryInstanceID:
1506           return swprintf(
1507               *buf,
1508               L"AoE_at_Shelf_%d.Slot_%d",
1509               aoe_disk->Major,
1510               aoe_disk->Minor
1511             ) + 1;
1512
1513         case BusQueryHardwareIDs: {
1514             UINT32 tmp;
1515
1516             tmp = swprintf(*buf, WVL_M_WLIT L"\\AoEHardDisk") + 1;
1517             tmp += swprintf(*buf + tmp, L"GenDisk") + 4;
1518             return tmp;
1519           }
1520
1521         case BusQueryCompatibleIDs:
1522           return swprintf(*buf, L"GenDisk") + 4;
1523
1524         default:
1525           return 0;
1526       }
1527   }
1528
1529 #ifdef _MSC_VER
1530 #  pragma pack(1)
1531 #endif
1532 struct AOE_ABFT {
1533     UINT32 Signature;  /* 0x54464261 (aBFT) */
1534     UINT32 Length;
1535     UCHAR Revision;
1536     UCHAR Checksum;
1537     UCHAR OEMID[6];
1538     UCHAR OEMTableID[8];
1539     UCHAR Reserved1[12];
1540     UINT16 Major;
1541     UCHAR Minor;
1542     UCHAR Reserved2;
1543     UCHAR ClientMac[6];
1544   } __attribute__((__packed__));
1545 typedef struct AOE_ABFT AOE_S_ABFT, * AOE_SP_ABFT;
1546 #ifdef _MSC_VER
1547 #  pragma pack()
1548 #endif
1549
1550 static VOID STDCALL AoeDiskClose_(IN WV_SP_DISK_T disk_ptr) {
1551     return;
1552   }
1553
1554 static VOID AoeProcessAbft_(void) {
1555     PHYSICAL_ADDRESS PhysicalAddress;
1556     PUCHAR PhysicalMemory;
1557     UINT32 Offset, Checksum, i;
1558     BOOLEAN FoundAbft = FALSE;
1559     AOE_S_ABFT AoEBootRecord;
1560     AOE_SP_DISK_ aoe_disk;
1561
1562     /* Find aBFT. */
1563     PhysicalAddress.QuadPart = 0LL;
1564     PhysicalMemory = MmMapIoSpace(PhysicalAddress, 0xa0000, MmNonCached);
1565     if (!PhysicalMemory) {
1566         DBG("Could not map low memory\n");
1567         goto err_map_mem;
1568       }
1569     for (Offset = 0; Offset < 0xa0000; Offset += 0x10) {
1570         if (!(
1571             ((AOE_SP_ABFT) (PhysicalMemory + Offset))->Signature ==
1572             0x54464261
1573           ))
1574           continue;
1575         Checksum = 0;
1576         for (
1577             i = 0;
1578             i < ((AOE_SP_ABFT) (PhysicalMemory + Offset))->Length;
1579             i++
1580           )
1581           Checksum += PhysicalMemory[Offset + i];
1582         if (Checksum & 0xff)
1583           continue;
1584         if (((AOE_SP_ABFT) (PhysicalMemory + Offset))->Revision != 1) {
1585             DBG(
1586                 "Found aBFT with mismatched revision v%d at "
1587                   "segment 0x%4x. want v1.\n",
1588                 ((abft_ptr) (PhysicalMemory + Offset))->Revision,
1589                 (Offset / 0x10)
1590               );
1591             continue;
1592           }
1593         DBG("Found aBFT at segment: 0x%04x\n", (Offset / 0x10));
1594         RtlCopyMemory(
1595             &AoEBootRecord,
1596             PhysicalMemory + Offset,
1597             sizeof AoEBootRecord
1598           );
1599         FoundAbft = TRUE;
1600         break;
1601       }
1602     MmUnmapIoSpace(PhysicalMemory, 0xa0000);
1603
1604     #ifdef RIS
1605     FoundAbft = TRUE;
1606     RtlCopyMemory(AoEBootRecord.ClientMac, "\x00\x0c\x29\x34\x69\x34", 6);
1607     AoEBootRecord.Major = 0;
1608     AoEBootRecord.Minor = 10;
1609     #endif
1610
1611     if (!FoundAbft)
1612       goto out_no_abft;
1613     aoe_disk = AoeDiskCreate_();
1614     if(aoe_disk == NULL) {
1615         DBG("Could not create AoE disk from aBFT!\n");
1616         return;
1617       }
1618     DBG(
1619         "Attaching AoE disk from client NIC "
1620           "%02x:%02x:%02x:%02x:%02x:%02x to major: %d minor: %d\n",
1621         AoEBootRecord.ClientMac[0],
1622         AoEBootRecord.ClientMac[1],
1623         AoEBootRecord.ClientMac[2],
1624         AoEBootRecord.ClientMac[3],
1625         AoEBootRecord.ClientMac[4],
1626         AoEBootRecord.ClientMac[5],
1627         AoEBootRecord.Major,
1628         AoEBootRecord.Minor
1629       );
1630     RtlCopyMemory(aoe_disk->ClientMac, AoEBootRecord.ClientMac, 6);
1631     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
1632     aoe_disk->Major = AoEBootRecord.Major;
1633     aoe_disk->Minor = AoEBootRecord.Minor;
1634     aoe_disk->MaxSectorsPerPacket = 1;
1635     aoe_disk->Timeout = 200000;          /* 20 ms. */
1636     aoe_disk->disk->Dev->Boot = TRUE;
1637     aoe_disk->disk->Media = WvDiskMediaTypeHard;
1638     AoeBusAddDev(aoe_disk->disk->Dev);
1639     return;
1640
1641     out_no_abft:
1642     DBG("No aBFT found\n");
1643
1644     err_map_mem:
1645
1646     return;
1647   }
1648
1649 NTSTATUS STDCALL AoeBusDevCtlScan(IN PIRP irp) {
1650     KIRQL irql;
1651     UINT32 count;
1652     AOE_SP_TARGET_LIST_ target_walker;
1653     AOE_SP_MOUNT_TARGETS targets;
1654     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
1655
1656     DBG("Got IOCTL_AOE_SCAN...\n");
1657     KeAcquireSpinLock(&AoeTargetListLock_, &irql);
1658
1659     count = 0;
1660     target_walker = AoeTargetList_;
1661     while (target_walker != NULL) {
1662         count++;
1663         target_walker = target_walker->next;
1664       }
1665
1666     targets = wv_malloc(sizeof *targets + (count * sizeof targets->Target[0]));
1667     if (targets == NULL) {
1668         DBG("wv_malloc targets\n");
1669         return WvlIrpComplete(
1670             irp,
1671             0,
1672             STATUS_INSUFFICIENT_RESOURCES
1673           );
1674       }
1675     irp->IoStatus.Information =
1676       sizeof (AOE_S_MOUNT_TARGETS) + (count * sizeof (AOE_S_MOUNT_TARGET));
1677     targets->Count = count;
1678
1679     count = 0;
1680     target_walker = AoeTargetList_;
1681     while (target_walker != NULL) {
1682         RtlCopyMemory(
1683             &targets->Target[count],
1684             &target_walker->Target,
1685             sizeof (AOE_S_MOUNT_TARGET)
1686           );
1687         count++;
1688         target_walker = target_walker->next;
1689       }
1690     RtlCopyMemory(
1691         irp->AssociatedIrp.SystemBuffer,
1692         targets,
1693         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
1694           (sizeof (AOE_S_MOUNT_TARGETS) + (count * sizeof (AOE_S_MOUNT_TARGET))) ?
1695           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
1696           (sizeof (AOE_S_MOUNT_TARGETS) + (count * sizeof (AOE_S_MOUNT_TARGET)))
1697         )
1698       );
1699     wv_free(targets);
1700
1701     KeReleaseSpinLock(&AoeTargetListLock_, irql);
1702     return WvlIrpComplete(irp, irp->IoStatus.Information, STATUS_SUCCESS);
1703   }
1704
1705 NTSTATUS STDCALL AoeBusDevCtlShow(IN PIRP irp) {
1706     UINT32 count;
1707     WVL_SP_BUS_NODE walker;
1708     AOE_SP_MOUNT_DISKS disks;
1709     wv_size_t size;
1710     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
1711
1712     if (!(io_stack_loc->Control & SL_PENDING_RETURNED)) {
1713         NTSTATUS status;
1714
1715         /* Enqueue the IRP. */
1716         status = WvlBusEnqueueIrp(&AoeBusMain, irp);
1717         if (status != STATUS_PENDING)
1718           /* Problem. */
1719           return WvlIrpComplete(irp, 0, status);
1720         /* Ok. */
1721         return status;
1722       }
1723     /* If we get here, we should be called by WvlBusProcessWorkItems() */
1724     DBG("Got IOCTL_AOE_SHOW...\n");
1725
1726     count = WvlBusGetNodeCount(&AoeBusMain);
1727     size = sizeof *disks + (count * sizeof disks->Disk[0]);
1728
1729     disks = wv_malloc(size);
1730     if (disks == NULL) {
1731         DBG("wv_malloc disks\n");
1732         return WvlIrpComplete(
1733             irp,
1734             0,
1735             STATUS_INSUFFICIENT_RESOURCES
1736           );
1737       }
1738     irp->IoStatus.Information = size;
1739     disks->Count = count;
1740
1741     count = 0;
1742     walker = NULL;
1743     /* For each node on the bus... */
1744     while (walker = WvlBusGetNextNode(&AoeBusMain, walker)) {
1745         WV_SP_DEV_T dev = WvDevFromDevObj(WvlBusGetNodePdo(walker));
1746         WV_SP_DISK_T disk = disk__get_ptr(dev);
1747         AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev);
1748
1749         disks->Disk[count].Disk = dev->DevNum;
1750         RtlCopyMemory(
1751             &disks->Disk[count].ClientMac,
1752             &aoe_disk->ClientMac,
1753             6
1754           );
1755         RtlCopyMemory(
1756             &disks->Disk[count].ServerMac,
1757             &aoe_disk->ServerMac,
1758             6
1759           );
1760         disks->Disk[count].Major = aoe_disk->Major;
1761         disks->Disk[count].Minor = aoe_disk->Minor;
1762         disks->Disk[count].LBASize = disk->LBADiskSize;
1763         count++;
1764       }
1765     RtlCopyMemory(
1766         irp->AssociatedIrp.SystemBuffer,
1767         disks,
1768         (io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength <
1769           (sizeof (AOE_S_MOUNT_DISKS) + (count * sizeof (AOE_S_MOUNT_DISK))) ?
1770           io_stack_loc->Parameters.DeviceIoControl.OutputBufferLength :
1771           (sizeof (AOE_S_MOUNT_DISKS) + (count * sizeof (AOE_S_MOUNT_DISK)))
1772         )
1773       );
1774     wv_free(disks);
1775     return WvlIrpComplete(irp, irp->IoStatus.Information, STATUS_SUCCESS);
1776   }
1777
1778 NTSTATUS STDCALL AoeBusDevCtlMount(IN PIRP irp) {
1779     PUCHAR buffer = irp->AssociatedIrp.SystemBuffer;
1780     AOE_SP_DISK_ aoe_disk;
1781
1782     DBG(
1783         "Got IOCTL_AOE_MOUNT for client: %02x:%02x:%02x:%02x:%02x:%02x "
1784           "Major:%d Minor:%d\n",
1785         buffer[0],
1786         buffer[1],
1787         buffer[2],
1788         buffer[3],
1789         buffer[4],
1790         buffer[5],
1791         *(PUINT16) (buffer + 6),
1792         (UCHAR) buffer[8]
1793       );
1794     aoe_disk = AoeDiskCreate_();
1795     if (aoe_disk == NULL) {
1796         DBG("Could not create AoE disk!\n");
1797         return WvlIrpComplete(
1798             irp,
1799             0,
1800             STATUS_INSUFFICIENT_RESOURCES
1801           );
1802       }
1803     RtlCopyMemory(aoe_disk->ClientMac, buffer, 6);
1804     RtlFillMemory(aoe_disk->ServerMac, 6, 0xff);
1805     aoe_disk->Major = *(PUINT16) (buffer + 6);
1806     aoe_disk->Minor = (UCHAR) buffer[8];
1807     aoe_disk->MaxSectorsPerPacket = 1;
1808     aoe_disk->Timeout = 200000;             /* 20 ms. */
1809     aoe_disk->disk->Dev->Boot = FALSE;
1810     aoe_disk->disk->Media = WvDiskMediaTypeHard;
1811     AoeBusAddDev(aoe_disk->disk->Dev);
1812
1813     return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
1814   }
1815
1816 /**
1817  * Create a new AoE disk.
1818  *
1819  * @ret aoe_disk        The address of a new AoE disk, or NULL for failure.
1820  *
1821  * This function should not be confused with a PDO creation routine, which is
1822  * actually implemented for each device type.  This routine will allocate an
1823  * AOE_S_DISK_, track it in a global list, as well as populate the disk
1824  * with default values.
1825  */
1826 static AOE_SP_DISK_ AoeDiskCreate_(void) {
1827     WV_SP_DISK_T disk;
1828     AOE_SP_DISK_ aoe_disk;
1829
1830     /* Try to create a disk. */
1831     disk = disk__create();
1832     if (disk == NULL)
1833       goto err_nodisk;
1834     /*
1835      * AoE disk devices might be used for booting and should
1836      * not be allocated from a paged memory pool.
1837      */
1838     aoe_disk = wv_mallocz(sizeof *aoe_disk);
1839     if (aoe_disk == NULL)
1840       goto err_noaoedisk;
1841     /* Track the new AoE disk in our global list. */
1842     ExInterlockedInsertTailList(
1843         &AoeDiskList_,
1844         &aoe_disk->tracking,
1845         &AoeDiskListLock_
1846       );
1847     /* Populate non-zero device defaults. */
1848     aoe_disk->disk = disk;
1849     aoe_disk->prev_free = disk->Dev->Ops.Free;
1850     disk->Dev->Ops.Free = AoeDiskFree_;
1851     disk->Dev->Ops.PnpId = query_id;
1852     disk->disk_ops.Io = AoeDiskIo_;
1853     disk->disk_ops.MaxXferLen = AoeDiskMaxXferLen_;
1854     disk->disk_ops.Init = AoeDiskInit_;
1855     disk->disk_ops.Close = AoeDiskClose_;
1856     disk->ext = aoe_disk;
1857
1858     return aoe_disk;
1859
1860     err_noaoedisk:
1861
1862     WvDevFree(disk->Dev);
1863     err_nodisk:
1864
1865     return NULL;
1866   }
1867
1868 /**
1869  * Default AoE disk deletion operation.
1870  *
1871  * @v dev               Points to the AoE disk device to delete.
1872  */
1873 static VOID STDCALL AoeDiskFree_(IN WV_SP_DEV_T dev) {
1874     AOE_SP_DISK_ aoe_disk = AoeDiskFromDev_(dev);
1875     /* Free the "inherited class". */
1876     aoe_disk->prev_free(dev);
1877     /*
1878      * Track the AoE disk deletion in our global list.  Unfortunately,
1879      * for now we have faith that an AoE disk won't be deleted twice and
1880      * result in a race condition.  Something to keep in mind...
1881      */
1882     ExInterlockedRemoveHeadList(
1883         aoe_disk->tracking.Blink,
1884         &AoeDiskListLock_
1885       );
1886
1887     wv_free(aoe_disk);
1888   }
1889
1890 static NTSTATUS STDCALL AoeDriverIrpNotSupported_(
1891     IN PDEVICE_OBJECT dev_obj,
1892     IN PIRP irp
1893   ) {
1894     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
1895   }
1896
1897 /* Handle a power IRP. */
1898 static NTSTATUS AoeIrpPower(
1899     IN PDEVICE_OBJECT dev_obj,
1900     IN PIRP irp
1901   ) {
1902     WV_SP_DEV_T dev;
1903
1904     #ifdef DEBUGIRPS
1905     Debug_IrpStart(dev_obj, irp);
1906     #endif
1907     /* Check for a bus IRP. */
1908     if (dev_obj == AoeBusMain.Fdo)
1909       return WvlBusPower(&AoeBusMain, irp);
1910     /* WvDevFromDevObj() checks for a NULL dev_obj */
1911     dev = WvDevFromDevObj(dev_obj);
1912     /* Check that the device exists. */
1913     if (!dev || dev->State == WvDevStateDeleted) {
1914         /* Even if it doesn't, a power IRP is important! */
1915         PoStartNextPowerIrp(irp);
1916         return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
1917       }
1918     /* Call the particular device's power handler. */
1919     if (dev->IrpMj && dev->IrpMj->Power)
1920       return dev->IrpMj->Power(dev, irp);
1921     /* Otherwise, we don't support the IRP. */
1922     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
1923   }
1924
1925 /* Handle an IRP_MJ_CREATE or IRP_MJ_CLOSE IRP. */
1926 static NTSTATUS AoeIrpCreateClose(
1927     IN PDEVICE_OBJECT dev_obj,
1928     IN PIRP irp
1929   ) {
1930     WV_SP_DEV_T dev;
1931
1932     #ifdef DEBUGIRPS
1933     Debug_IrpStart(dev_obj, irp);
1934     #endif
1935     /* Check for a bus IRP. */
1936     if (dev_obj == AoeBusMain.Fdo)
1937       return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
1938     /* WvDevFromDevObj() checks for a NULL dev_obj */
1939     dev = WvDevFromDevObj(dev_obj);
1940     /* Check that the device exists. */
1941     if (!dev || dev->State == WvDevStateDeleted)
1942       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
1943     /* Always succeed with nothing to do. */
1944     return WvlIrpComplete(irp, 0, STATUS_SUCCESS);
1945   }
1946
1947 /* Handle an IRP_MJ_SYSTEM_CONTROL IRP. */
1948 static NTSTATUS AoeIrpSysCtl(
1949     IN PDEVICE_OBJECT dev_obj,
1950     IN PIRP irp
1951   ) {
1952     WV_SP_DEV_T dev;
1953
1954     #ifdef DEBUGIRPS
1955     Debug_IrpStart(dev_obj, irp);
1956     #endif
1957     /* Check for a bus IRP. */
1958     if (dev_obj == AoeBusMain.Fdo)
1959       return WvlBusSysCtl(&AoeBusMain, irp);
1960     /* WvDevFromDevObj() checks for a NULL dev_obj */
1961     dev = WvDevFromDevObj(dev_obj);
1962     /* Check that the device exists. */
1963     if (!dev || dev->State == WvDevStateDeleted)
1964       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
1965     /* Call the particular device's power handler. */
1966     if (dev->IrpMj && dev->IrpMj->SysCtl)
1967       return dev->IrpMj->SysCtl(dev, irp);
1968     /* Otherwise, we don't support the IRP. */
1969     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
1970   }
1971
1972 /* Handle an IRP_MJ_DEVICE_CONTROL IRP. */
1973 static NTSTATUS AoeDriverIrpDevCtl_(
1974     IN PDEVICE_OBJECT dev_obj,
1975     IN PIRP irp
1976   ) {
1977     WV_SP_DEV_T dev;
1978     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
1979
1980     #ifdef DEBUGIRPS
1981     Debug_IrpStart(dev_obj, irp);
1982     #endif
1983     /* Check for a bus IRP. */
1984     if (dev_obj == AoeBusMain.Fdo) {
1985         return AoeBusDevCtl(
1986             irp,
1987             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
1988           );
1989       }
1990     /* WvDevFromDevObj() checks for a NULL dev_obj */
1991     dev = WvDevFromDevObj(dev_obj);
1992     /* Check that the device exists. */
1993     if (!dev || dev->State == WvDevStateDeleted)
1994       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
1995     /* Call the particular device's power handler. */
1996     if (dev->IrpMj && dev->IrpMj->DevCtl) {
1997         return dev->IrpMj->DevCtl(
1998             dev,
1999             irp,
2000             io_stack_loc->Parameters.DeviceIoControl.IoControlCode
2001           );
2002       }
2003     /* Otherwise, we don't support the IRP. */
2004     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
2005   }
2006
2007 /* Handle an IRP_MJ_SCSI IRP. */
2008 static NTSTATUS AoeDriverIrpScsi_(
2009     IN PDEVICE_OBJECT dev_obj,
2010     IN PIRP irp
2011   ) {
2012     WV_SP_DEV_T dev;
2013     PIO_STACK_LOCATION io_stack_loc;
2014
2015     #ifdef DEBUGIRPS
2016     Debug_IrpStart(dev_obj, irp);
2017     #endif
2018     /* Check for a bus IRP. */
2019     if (dev_obj == AoeBusMain.Fdo)
2020       return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
2021     /* WvDevFromDevObj() checks for a NULL dev_obj */
2022     dev = WvDevFromDevObj(dev_obj);
2023     io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2024     /* Check that the device exists. */
2025     if (!dev || dev->State == WvDevStateDeleted)
2026       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
2027     /* Call the particular device's power handler. */
2028     if (dev->IrpMj && dev->IrpMj->Scsi) {
2029         return dev->IrpMj->Scsi(
2030             dev,
2031             irp,
2032             io_stack_loc->Parameters.Scsi.Srb->Function
2033           );
2034       }
2035     /* Otherwise, we don't support the IRP. */
2036     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
2037   }
2038
2039 /* Handle an IRP_MJ_PNP IRP. */
2040 static NTSTATUS AoeDriverIrpPnp_(
2041     IN PDEVICE_OBJECT dev_obj,
2042     IN PIRP irp
2043   ) {
2044     WV_SP_DEV_T dev;
2045     PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
2046
2047     #ifdef DEBUGIRPS
2048     Debug_IrpStart(dev_obj, irp);
2049     #endif
2050     /* Check for a bus IRP. */
2051     if (dev_obj == AoeBusMain.Fdo)
2052       return WvlBusPnpIrp(&AoeBusMain, irp, io_stack_loc->MinorFunction);
2053     /* WvDevFromDevObj() checks for a NULL dev_obj */
2054     dev = WvDevFromDevObj(dev_obj);
2055     /* Check that the device exists. */
2056     if (!dev || dev->State == WvDevStateDeleted)
2057       return WvlIrpComplete(irp, 0, STATUS_NO_SUCH_DEVICE);
2058     /* Call the particular device's power handler. */
2059     if (dev->IrpMj && dev->IrpMj->Pnp) {
2060         return dev->IrpMj->Pnp(
2061             dev,
2062             irp,
2063             io_stack_loc->MinorFunction
2064           );
2065       }
2066     /* Otherwise, we don't support the IRP. */
2067     return WvlIrpComplete(irp, 0, STATUS_NOT_SUPPORTED);
2068   }