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