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