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